mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-28 01:28:59 +00:00
347 lines
9.2 KiB
Diff
347 lines
9.2 KiB
Diff
|
From 546bac0479e51024027f8c8820f912573643b101 Mon Sep 17 00:00:00 2001
|
||
|
From: Eric Anholt <eric@anholt.net>
|
||
|
Date: Wed, 18 Jan 2017 07:31:57 +1100
|
||
|
Subject: [PATCH] clk: bcm2835: Add leaf clock measurement support, disabled by
|
||
|
default
|
||
|
|
||
|
This proved incredibly useful during debugging of the DSI driver, to
|
||
|
see if our clocks were running at rate we requested. Let's leave it
|
||
|
here for the next person interacting with clocks on the platform (and
|
||
|
so that hopefully we can just hook it up to debugfs some day).
|
||
|
|
||
|
Signed-off-by: Eric Anholt <eric@anholt.net>
|
||
|
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||
|
(cherry picked from commit 3f9195811d8d829556c4cd88d3f9e56a80d5ba60)
|
||
|
---
|
||
|
drivers/clk/bcm/clk-bcm2835.c | 144 ++++++++++++++++++++++++++++++++++--------
|
||
|
1 file changed, 119 insertions(+), 25 deletions(-)
|
||
|
|
||
|
--- a/drivers/clk/bcm/clk-bcm2835.c
|
||
|
+++ b/drivers/clk/bcm/clk-bcm2835.c
|
||
|
@@ -39,6 +39,7 @@
|
||
|
#include <linux/clk.h>
|
||
|
#include <linux/clk/bcm2835.h>
|
||
|
#include <linux/debugfs.h>
|
||
|
+#include <linux/delay.h>
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/of.h>
|
||
|
#include <linux/platform_device.h>
|
||
|
@@ -98,7 +99,8 @@
|
||
|
#define CM_SMIDIV 0x0b4
|
||
|
/* no definition for 0x0b8 and 0x0bc */
|
||
|
#define CM_TCNTCTL 0x0c0
|
||
|
-#define CM_TCNTDIV 0x0c4
|
||
|
+# define CM_TCNT_SRC1_SHIFT 12
|
||
|
+#define CM_TCNTCNT 0x0c4
|
||
|
#define CM_TECCTL 0x0c8
|
||
|
#define CM_TECDIV 0x0cc
|
||
|
#define CM_TD0CTL 0x0d0
|
||
|
@@ -338,6 +340,61 @@ static inline u32 cprman_read(struct bcm
|
||
|
return readl(cprman->regs + reg);
|
||
|
}
|
||
|
|
||
|
+/* Does a cycle of measuring a clock through the TCNT clock, which may
|
||
|
+ * source from many other clocks in the system.
|
||
|
+ */
|
||
|
+static unsigned long bcm2835_measure_tcnt_mux(struct bcm2835_cprman *cprman,
|
||
|
+ u32 tcnt_mux)
|
||
|
+{
|
||
|
+ u32 osccount = 19200; /* 1ms */
|
||
|
+ u32 count;
|
||
|
+ ktime_t timeout;
|
||
|
+
|
||
|
+ spin_lock(&cprman->regs_lock);
|
||
|
+
|
||
|
+ cprman_write(cprman, CM_TCNTCTL, CM_KILL);
|
||
|
+
|
||
|
+ cprman_write(cprman, CM_TCNTCTL,
|
||
|
+ (tcnt_mux & CM_SRC_MASK) |
|
||
|
+ (tcnt_mux >> CM_SRC_BITS) << CM_TCNT_SRC1_SHIFT);
|
||
|
+
|
||
|
+ cprman_write(cprman, CM_OSCCOUNT, osccount);
|
||
|
+
|
||
|
+ /* do a kind delay at the start */
|
||
|
+ mdelay(1);
|
||
|
+
|
||
|
+ /* Finish off whatever is left of OSCCOUNT */
|
||
|
+ timeout = ktime_add_ns(ktime_get(), LOCK_TIMEOUT_NS);
|
||
|
+ while (cprman_read(cprman, CM_OSCCOUNT)) {
|
||
|
+ if (ktime_after(ktime_get(), timeout)) {
|
||
|
+ dev_err(cprman->dev, "timeout waiting for OSCCOUNT\n");
|
||
|
+ count = 0;
|
||
|
+ goto out;
|
||
|
+ }
|
||
|
+ cpu_relax();
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Wait for BUSY to clear. */
|
||
|
+ timeout = ktime_add_ns(ktime_get(), LOCK_TIMEOUT_NS);
|
||
|
+ while (cprman_read(cprman, CM_TCNTCTL) & CM_BUSY) {
|
||
|
+ if (ktime_after(ktime_get(), timeout)) {
|
||
|
+ dev_err(cprman->dev, "timeout waiting for !BUSY\n");
|
||
|
+ count = 0;
|
||
|
+ goto out;
|
||
|
+ }
|
||
|
+ cpu_relax();
|
||
|
+ }
|
||
|
+
|
||
|
+ count = cprman_read(cprman, CM_TCNTCNT);
|
||
|
+
|
||
|
+ cprman_write(cprman, CM_TCNTCTL, 0);
|
||
|
+
|
||
|
+out:
|
||
|
+ spin_unlock(&cprman->regs_lock);
|
||
|
+
|
||
|
+ return count * 1000;
|
||
|
+}
|
||
|
+
|
||
|
static int bcm2835_debugfs_regset(struct bcm2835_cprman *cprman, u32 base,
|
||
|
struct debugfs_reg32 *regs, size_t nregs,
|
||
|
struct dentry *dentry)
|
||
|
@@ -473,6 +530,8 @@ struct bcm2835_clock_data {
|
||
|
|
||
|
bool is_vpu_clock;
|
||
|
bool is_mash_clock;
|
||
|
+
|
||
|
+ u32 tcnt_mux;
|
||
|
};
|
||
|
|
||
|
struct bcm2835_gate_data {
|
||
|
@@ -1008,6 +1067,17 @@ static int bcm2835_clock_on(struct clk_h
|
||
|
CM_GATE);
|
||
|
spin_unlock(&cprman->regs_lock);
|
||
|
|
||
|
+ /* Debug code to measure the clock once it's turned on to see
|
||
|
+ * if it's ticking at the rate we expect.
|
||
|
+ */
|
||
|
+ if (data->tcnt_mux && false) {
|
||
|
+ dev_info(cprman->dev,
|
||
|
+ "clk %s: rate %ld, measure %ld\n",
|
||
|
+ data->name,
|
||
|
+ clk_hw_get_rate(hw),
|
||
|
+ bcm2835_measure_tcnt_mux(cprman, data->tcnt_mux));
|
||
|
+ }
|
||
|
+
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
@@ -1774,7 +1844,8 @@ static const struct bcm2835_clk_desc clk
|
||
|
.ctl_reg = CM_OTPCTL,
|
||
|
.div_reg = CM_OTPDIV,
|
||
|
.int_bits = 4,
|
||
|
- .frac_bits = 0),
|
||
|
+ .frac_bits = 0,
|
||
|
+ .tcnt_mux = 6),
|
||
|
/*
|
||
|
* Used for a 1Mhz clock for the system clocksource, and also used
|
||
|
* bythe watchdog timer and the camera pulse generator.
|
||
|
@@ -1808,13 +1879,15 @@ static const struct bcm2835_clk_desc clk
|
||
|
.ctl_reg = CM_H264CTL,
|
||
|
.div_reg = CM_H264DIV,
|
||
|
.int_bits = 4,
|
||
|
- .frac_bits = 8),
|
||
|
+ .frac_bits = 8,
|
||
|
+ .tcnt_mux = 1),
|
||
|
[BCM2835_CLOCK_ISP] = REGISTER_VPU_CLK(
|
||
|
.name = "isp",
|
||
|
.ctl_reg = CM_ISPCTL,
|
||
|
.div_reg = CM_ISPDIV,
|
||
|
.int_bits = 4,
|
||
|
- .frac_bits = 8),
|
||
|
+ .frac_bits = 8,
|
||
|
+ .tcnt_mux = 2),
|
||
|
|
||
|
/*
|
||
|
* Secondary SDRAM clock. Used for low-voltage modes when the PLL
|
||
|
@@ -1825,13 +1898,15 @@ static const struct bcm2835_clk_desc clk
|
||
|
.ctl_reg = CM_SDCCTL,
|
||
|
.div_reg = CM_SDCDIV,
|
||
|
.int_bits = 6,
|
||
|
- .frac_bits = 0),
|
||
|
+ .frac_bits = 0,
|
||
|
+ .tcnt_mux = 3),
|
||
|
[BCM2835_CLOCK_V3D] = REGISTER_VPU_CLK(
|
||
|
.name = "v3d",
|
||
|
.ctl_reg = CM_V3DCTL,
|
||
|
.div_reg = CM_V3DDIV,
|
||
|
.int_bits = 4,
|
||
|
- .frac_bits = 8),
|
||
|
+ .frac_bits = 8,
|
||
|
+ .tcnt_mux = 4),
|
||
|
/*
|
||
|
* VPU clock. This doesn't have an enable bit, since it drives
|
||
|
* the bus for everything else, and is special so it doesn't need
|
||
|
@@ -1845,7 +1920,8 @@ static const struct bcm2835_clk_desc clk
|
||
|
.int_bits = 12,
|
||
|
.frac_bits = 8,
|
||
|
.flags = CLK_IS_CRITICAL,
|
||
|
- .is_vpu_clock = true),
|
||
|
+ .is_vpu_clock = true,
|
||
|
+ .tcnt_mux = 5),
|
||
|
|
||
|
/* clocks with per parent mux */
|
||
|
[BCM2835_CLOCK_AVEO] = REGISTER_PER_CLK(
|
||
|
@@ -1853,19 +1929,22 @@ static const struct bcm2835_clk_desc clk
|
||
|
.ctl_reg = CM_AVEOCTL,
|
||
|
.div_reg = CM_AVEODIV,
|
||
|
.int_bits = 4,
|
||
|
- .frac_bits = 0),
|
||
|
+ .frac_bits = 0,
|
||
|
+ .tcnt_mux = 38),
|
||
|
[BCM2835_CLOCK_CAM0] = REGISTER_PER_CLK(
|
||
|
.name = "cam0",
|
||
|
.ctl_reg = CM_CAM0CTL,
|
||
|
.div_reg = CM_CAM0DIV,
|
||
|
.int_bits = 4,
|
||
|
- .frac_bits = 8),
|
||
|
+ .frac_bits = 8,
|
||
|
+ .tcnt_mux = 14),
|
||
|
[BCM2835_CLOCK_CAM1] = REGISTER_PER_CLK(
|
||
|
.name = "cam1",
|
||
|
.ctl_reg = CM_CAM1CTL,
|
||
|
.div_reg = CM_CAM1DIV,
|
||
|
.int_bits = 4,
|
||
|
- .frac_bits = 8),
|
||
|
+ .frac_bits = 8,
|
||
|
+ .tcnt_mux = 15),
|
||
|
[BCM2835_CLOCK_DFT] = REGISTER_PER_CLK(
|
||
|
.name = "dft",
|
||
|
.ctl_reg = CM_DFTCTL,
|
||
|
@@ -1877,7 +1956,8 @@ static const struct bcm2835_clk_desc clk
|
||
|
.ctl_reg = CM_DPICTL,
|
||
|
.div_reg = CM_DPIDIV,
|
||
|
.int_bits = 4,
|
||
|
- .frac_bits = 8),
|
||
|
+ .frac_bits = 8,
|
||
|
+ .tcnt_mux = 17),
|
||
|
|
||
|
/* Arasan EMMC clock */
|
||
|
[BCM2835_CLOCK_EMMC] = REGISTER_PER_CLK(
|
||
|
@@ -1885,7 +1965,8 @@ static const struct bcm2835_clk_desc clk
|
||
|
.ctl_reg = CM_EMMCCTL,
|
||
|
.div_reg = CM_EMMCDIV,
|
||
|
.int_bits = 4,
|
||
|
- .frac_bits = 8),
|
||
|
+ .frac_bits = 8,
|
||
|
+ .tcnt_mux = 39),
|
||
|
|
||
|
/* General purpose (GPIO) clocks */
|
||
|
[BCM2835_CLOCK_GP0] = REGISTER_PER_CLK(
|
||
|
@@ -1894,7 +1975,8 @@ static const struct bcm2835_clk_desc clk
|
||
|
.div_reg = CM_GP0DIV,
|
||
|
.int_bits = 12,
|
||
|
.frac_bits = 12,
|
||
|
- .is_mash_clock = true),
|
||
|
+ .is_mash_clock = true,
|
||
|
+ .tcnt_mux = 20),
|
||
|
[BCM2835_CLOCK_GP1] = REGISTER_PER_CLK(
|
||
|
.name = "gp1",
|
||
|
.ctl_reg = CM_GP1CTL,
|
||
|
@@ -1902,7 +1984,8 @@ static const struct bcm2835_clk_desc clk
|
||
|
.int_bits = 12,
|
||
|
.frac_bits = 12,
|
||
|
.flags = CLK_IS_CRITICAL,
|
||
|
- .is_mash_clock = true),
|
||
|
+ .is_mash_clock = true,
|
||
|
+ .tcnt_mux = 21),
|
||
|
[BCM2835_CLOCK_GP2] = REGISTER_PER_CLK(
|
||
|
.name = "gp2",
|
||
|
.ctl_reg = CM_GP2CTL,
|
||
|
@@ -1917,40 +2000,46 @@ static const struct bcm2835_clk_desc clk
|
||
|
.ctl_reg = CM_HSMCTL,
|
||
|
.div_reg = CM_HSMDIV,
|
||
|
.int_bits = 4,
|
||
|
- .frac_bits = 8),
|
||
|
+ .frac_bits = 8,
|
||
|
+ .tcnt_mux = 22),
|
||
|
[BCM2835_CLOCK_PCM] = REGISTER_PER_CLK(
|
||
|
.name = "pcm",
|
||
|
.ctl_reg = CM_PCMCTL,
|
||
|
.div_reg = CM_PCMDIV,
|
||
|
.int_bits = 12,
|
||
|
.frac_bits = 12,
|
||
|
- .is_mash_clock = true),
|
||
|
+ .is_mash_clock = true,
|
||
|
+ .tcnt_mux = 23),
|
||
|
[BCM2835_CLOCK_PWM] = REGISTER_PER_CLK(
|
||
|
.name = "pwm",
|
||
|
.ctl_reg = CM_PWMCTL,
|
||
|
.div_reg = CM_PWMDIV,
|
||
|
.int_bits = 12,
|
||
|
.frac_bits = 12,
|
||
|
- .is_mash_clock = true),
|
||
|
+ .is_mash_clock = true,
|
||
|
+ .tcnt_mux = 24),
|
||
|
[BCM2835_CLOCK_SLIM] = REGISTER_PER_CLK(
|
||
|
.name = "slim",
|
||
|
.ctl_reg = CM_SLIMCTL,
|
||
|
.div_reg = CM_SLIMDIV,
|
||
|
.int_bits = 12,
|
||
|
.frac_bits = 12,
|
||
|
- .is_mash_clock = true),
|
||
|
+ .is_mash_clock = true,
|
||
|
+ .tcnt_mux = 25),
|
||
|
[BCM2835_CLOCK_SMI] = REGISTER_PER_CLK(
|
||
|
.name = "smi",
|
||
|
.ctl_reg = CM_SMICTL,
|
||
|
.div_reg = CM_SMIDIV,
|
||
|
.int_bits = 4,
|
||
|
- .frac_bits = 8),
|
||
|
+ .frac_bits = 8,
|
||
|
+ .tcnt_mux = 27),
|
||
|
[BCM2835_CLOCK_UART] = REGISTER_PER_CLK(
|
||
|
.name = "uart",
|
||
|
.ctl_reg = CM_UARTCTL,
|
||
|
.div_reg = CM_UARTDIV,
|
||
|
.int_bits = 10,
|
||
|
- .frac_bits = 12),
|
||
|
+ .frac_bits = 12,
|
||
|
+ .tcnt_mux = 28),
|
||
|
|
||
|
/* TV encoder clock. Only operating frequency is 108Mhz. */
|
||
|
[BCM2835_CLOCK_VEC] = REGISTER_PER_CLK(
|
||
|
@@ -1963,7 +2052,8 @@ static const struct bcm2835_clk_desc clk
|
||
|
* Allow rate change propagation only on PLLH_AUX which is
|
||
|
* assigned index 7 in the parent array.
|
||
|
*/
|
||
|
- .set_rate_parent = BIT(7)),
|
||
|
+ .set_rate_parent = BIT(7),
|
||
|
+ .tcnt_mux = 29),
|
||
|
|
||
|
/* dsi clocks */
|
||
|
[BCM2835_CLOCK_DSI0E] = REGISTER_PER_CLK(
|
||
|
@@ -1971,25 +2061,29 @@ static const struct bcm2835_clk_desc clk
|
||
|
.ctl_reg = CM_DSI0ECTL,
|
||
|
.div_reg = CM_DSI0EDIV,
|
||
|
.int_bits = 4,
|
||
|
- .frac_bits = 8),
|
||
|
+ .frac_bits = 8,
|
||
|
+ .tcnt_mux = 18),
|
||
|
[BCM2835_CLOCK_DSI1E] = REGISTER_PER_CLK(
|
||
|
.name = "dsi1e",
|
||
|
.ctl_reg = CM_DSI1ECTL,
|
||
|
.div_reg = CM_DSI1EDIV,
|
||
|
.int_bits = 4,
|
||
|
- .frac_bits = 8),
|
||
|
+ .frac_bits = 8,
|
||
|
+ .tcnt_mux = 19),
|
||
|
[BCM2835_CLOCK_DSI0P] = REGISTER_DSI0_CLK(
|
||
|
.name = "dsi0p",
|
||
|
.ctl_reg = CM_DSI0PCTL,
|
||
|
.div_reg = CM_DSI0PDIV,
|
||
|
.int_bits = 0,
|
||
|
- .frac_bits = 0),
|
||
|
+ .frac_bits = 0,
|
||
|
+ .tcnt_mux = 12),
|
||
|
[BCM2835_CLOCK_DSI1P] = REGISTER_DSI1_CLK(
|
||
|
.name = "dsi1p",
|
||
|
.ctl_reg = CM_DSI1PCTL,
|
||
|
.div_reg = CM_DSI1PDIV,
|
||
|
.int_bits = 0,
|
||
|
- .frac_bits = 0),
|
||
|
+ .frac_bits = 0,
|
||
|
+ .tcnt_mux = 13),
|
||
|
|
||
|
/* the gates */
|
||
|
|