Skip to contentSkip to conversation

ARM: shmobile: r8a73a4: wait for completion when kicking the clock

PATCH New Guennadi Liakhovetski 2 participants 4 messages May 22, 2013 – Jun 27, 2013

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

PATCH 1/2 ARM: shmobile: r8a73a4: wait for completion when kicking the clock

To reconfigure clocks, controlled by FRQCRA and FRQCRB, a kick bit has to be set and to make sure the setting has taken effect, it has to be read back repeatedly until it is cleared by the hardware. This patch adds the waiting part, that was missing until now.

---
 arch/arm/mach-shmobile/clock-r8a73a4.c |   22 ++++++++++++++++------
 1 files changed, 16 insertions(+), 6 deletions(-)
arch/arm/mach-shmobile/clock-r8a73a4.c +16 -6
index 5f7fe62..d5176d0 100644
@@ -184,6 +184,21 @@ PLL_CLOCK(pll2h_clk, &main_div2_clk, pll_parent_main_extal, 3, 5, PLL2HCR, 5);
 
 SH_FIXED_RATIO_CLK(pll1_div2_clk,	pll1_clk,	div2);
 
+static int frqcr_kick_do(struct clk *clk)
+{
+	int i;
+
+	/* set KICK bit in FRQCRB to update hardware setting, check success */
+	iowrite32(ioread32(CPG_MAP(FRQCRB)) | BIT(31), CPG_MAP(FRQCRB));
+	for (i = 1000; i; i--)
+		if (ioread32(CPG_MAP(FRQCRB)) & BIT(31))
+			cpu_relax();
+		else
+			return 0;
+
+	return -ETIMEDOUT;
+}
+
 static struct clk *main_clks[] = {
 	&extalr_clk,
 	&extal1_clk,
@@ -205,12 +220,7 @@ static struct clk *main_clks[] = {
 /* DIV4 */
 static void div4_kick(struct clk *clk)
 {
-	unsigned long value;
-
-	/* set KICK bit in FRQCRB to update hardware setting */
-	value = ioread32(CPG_MAP(FRQCRB));
-	value |= (1 << 31);
-	iowrite32(value, CPG_MAP(FRQCRB));
+	frqcr_kick_do(clk);
 }
 
 static int divisors[] = { 2, 3, 4, 6, 8, 12, 16, 18, 24, 0, 36, 48, 10};
Signature · 1 line
1.7.2.5
#1 lore raw

PATCH 2/2 ARM: shmobile: r8a73a4: implement CPU clock scaling for CPUFreq

This patch adds support for the Z-clock on r8a73a4 SoCs, which is driving the Cortex A15 core, and a "cpufreq-cpu0" platform device. Adding an "operating-points" property to the CPU0 DT node and a regulator, this patch allows platforms to use the generic cpufreq-cpu0 driver to use SoC's DVFS capabilities.

---
 arch/arm/mach-shmobile/Kconfig         |    2 +
 arch/arm/mach-shmobile/clock-r8a73a4.c |  123 +++++++++++++++++++++++++++++++-
 arch/arm/mach-shmobile/setup-r8a73a4.c |    1 +
 3 files changed, 125 insertions(+), 1 deletions(-)
arch/arm/mach-shmobile/Kconfig +2 -0
index 97bc6cf..ec349ad 100644
@@ -26,6 +26,8 @@ config ARCH_R8A73A4
 	select ARM_ARCH_TIMER
 	select SH_CLK_CPG
 	select RENESAS_IRQC
+	select ARCH_HAS_CPUFREQ
+	select ARCH_HAS_OPP
 
 config ARCH_R8A7740
 	bool "R-Mobile A1 (R8A77400)"
arch/arm/mach-shmobile/clock-r8a73a4.c +122 -1

181 lines not on this page · raw message

arch/arm/mach-shmobile/setup-r8a73a4.c +1 -0
index c5a75a7..7265e14 100644
@@ -185,6 +185,7 @@ void __init r8a73a4_add_standard_devices(void)
 #ifdef CONFIG_USE_OF
 void __init r8a73a4_add_standard_devices_dt(void)
 {
+	platform_device_register_simple("cpufreq-cpu0", -1, NULL, 0);
 	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
 }
 
Signature · 1 line
1.7.2.5
#2 lore raw

PATCH 2/2 ARM: shmobile: r8a73a4: implement CPU clock scaling for CPUFreq

Quoted 7 lines · Guennadi Liakhovetski

This patch adds support for the Z-clock on r8a73a4 SoCs, which is driving the Cortex A15 core, and a "cpufreq-cpu0" platform device. Adding an "operating-points" property to the CPU0 DT node and a regulator, this patch allows platforms to use the generic cpufreq-cpu0 driver to use SoC's DVFS capabilities.

I have queued this up for v3.12 in the soc branch of the renesas tree. I have included an Ack from Morimoto-san which he provided here:

"Re: [PATCH 0/4] Add DVFS support on APE6EVM"
http://www.spinics.net/lists/arm-kernel/msg255480.html

Quoted 227 lines
---
 arch/arm/mach-shmobile/Kconfig         |    2 +
 arch/arm/mach-shmobile/clock-r8a73a4.c |  123 +++++++++++++++++++++++++++++++-
 arch/arm/mach-shmobile/setup-r8a73a4.c |    1 +
 3 files changed, 125 insertions(+), 1 deletions(-)
arch/arm/mach-shmobile/Kconfig +2 -0
index 97bc6cf..ec349ad 100644
@@ -26,6 +26,8 @@ config ARCH_R8A73A4
 	select ARM_ARCH_TIMER
 	select SH_CLK_CPG
 	select RENESAS_IRQC
+	select ARCH_HAS_CPUFREQ
+	select ARCH_HAS_OPP
 
 config ARCH_R8A7740
 	bool "R-Mobile A1 (R8A77400)"
arch/arm/mach-shmobile/clock-r8a73a4.c +122 -1
index d5176d0..824789c 100644
@@ -34,6 +34,7 @@
 
 #define FRQCRA		0xE6150000
 #define FRQCRB		0xE6150004
+#define FRQCRC		0xE61500E0
 #define VCLKCR1		0xE6150008
 #define VCLKCR2		0xE615000C
 #define VCLKCR3		0xE615001C
@@ -52,6 +53,7 @@
 #define HSICKCR		0xE615026C
 #define M4CKCR		0xE6150098
 #define PLLECR		0xE61500D0
+#define PLL0CR		0xE61500D8
 #define PLL1CR		0xE6150028
 #define PLL2CR		0xE615002C
 #define PLL2SCR		0xE61501F4
@@ -177,6 +179,7 @@ static struct sh_clk_ops pll_clk_ops = {
 		.mapping	= &cpg_mapping,		\
 	}
 
+PLL_CLOCK(pll0_clk,  &main_clk,      pll_parent_main,      1, 20, PLL0CR,  0);
 PLL_CLOCK(pll1_clk,  &main_clk,      pll_parent_main,       1, 7, PLL1CR,  1);
 PLL_CLOCK(pll2_clk,  &main_div2_clk, pll_parent_main_extal, 3, 5, PLL2CR,  2);
 PLL_CLOCK(pll2s_clk, &main_div2_clk, pll_parent_main_extal, 3, 5, PLL2SCR, 4);
@@ -184,6 +187,14 @@ PLL_CLOCK(pll2h_clk, &main_div2_clk, pll_parent_main_extal, 3, 5, PLL2HCR, 5);
 
 SH_FIXED_RATIO_CLK(pll1_div2_clk,	pll1_clk,	div2);
 
+static atomic_t frqcr_lock;
+
+/* Several clocks need to access FRQCRB, have to lock */
+static bool frqcr_kick_check(struct clk *clk)
+{
+	return !(ioread32(CPG_MAP(FRQCRB)) & BIT(31));
+}
+
 static int frqcr_kick_do(struct clk *clk)
 {
 	int i;
@@ -199,6 +210,107 @@ static int frqcr_kick_do(struct clk *clk)
 	return -ETIMEDOUT;
 }
 
+static int zclk_set_rate(struct clk *clk, unsigned long rate)
+{
+	void __iomem *frqcrc;
+	int ret;
+	unsigned long step, p_rate;
+	u32 val;
+
+	if (!clk->parent || !__clk_get(clk->parent))
+		return -ENODEV;
+
+	if (!atomic_inc_and_test(&frqcr_lock) || !frqcr_kick_check(clk)) {
+		ret = -EBUSY;
+		goto done;
+	}
+
+	frqcrc = clk->mapped_reg + (FRQCRC - (u32)clk->enable_reg);
+
+	p_rate = clk_get_rate(clk->parent);
+	if (rate == p_rate) {
+		val = 0;
+	} else {
+		step = DIV_ROUND_CLOSEST(p_rate, 32);
+		val = 32 - rate / step;
+	}
+
+	iowrite32((ioread32(frqcrc) & ~(clk->div_mask << clk->enable_bit)) |
+		  (val << clk->enable_bit), frqcrc);
+
+	ret = frqcr_kick_do(clk);
+
+done:
+	atomic_dec(&frqcr_lock);
+	__clk_put(clk->parent);
+	return ret;
+}
+
+static long zclk_round_rate(struct clk *clk, unsigned long rate)
+{
+	/*
+	 * theoretical rate = parent rate * multiplier / 32,
+	 * where 1 <= multiplier <= 32. Therefore we should do
+	 * multiplier = rate * 32 / parent rate
+	 * rounded rate = parent rate * multiplier / 32.
+	 * However, multiplication before division won't fit in 32 bits, so
+	 * we sacrifice some precision by first dividing and then multiplying.
+	 * To find the nearest divisor we calculate both and pick up the best
+	 * one. This avoids 64-bit arithmetics.
+	 */
+	unsigned long step, mul_min, mul_max, rate_min, rate_max;
+
+	rate_max = clk_get_rate(clk->parent);
+
+	/* output freq <= parent */
+	if (rate >= rate_max)
+		return rate_max;
+
+	step = DIV_ROUND_CLOSEST(rate_max, 32);
+	/* output freq >= parent / 32 */
+	if (step >= rate)
+		return step;
+
+	mul_min = rate / step;
+	mul_max = DIV_ROUND_UP(rate, step);
+	rate_min = step * mul_min;
+	if (mul_max == mul_min)
+		return rate_min;
+
+	rate_max = step * mul_max;
+
+	if (rate_max - rate <  rate - rate_min)
+		return rate_max;
+
+	return rate_min;
+}
+
+static unsigned long zclk_recalc(struct clk *clk)
+{
+	void __iomem *frqcrc = FRQCRC - (u32)clk->enable_reg + clk->mapped_reg;
+	unsigned int max = clk->div_mask + 1;
+	unsigned long val = ((ioread32(frqcrc) >> clk->enable_bit) &
+			     clk->div_mask);
+
+	return DIV_ROUND_CLOSEST(clk_get_rate(clk->parent), max) *
+		(max - val);
+}
+
+static struct sh_clk_ops zclk_ops = {
+	.recalc = zclk_recalc,
+	.set_rate = zclk_set_rate,
+	.round_rate = zclk_round_rate,
+};
+
+static struct clk z_clk = {
+	.parent = &pll0_clk,
+	.div_mask = 0x1f,
+	.enable_bit = 8,
+	/* We'll need to access FRQCRB and FRQCRC */
+	.enable_reg = (void __iomem *)FRQCRB,
+	.ops = &zclk_ops,
+};
+
 static struct clk *main_clks[] = {
 	&extalr_clk,
 	&extal1_clk,
@@ -210,17 +322,21 @@ static struct clk *main_clks[] = {
 	&main_div2_clk,
 	&fsiack_clk,
 	&fsibck_clk,
+	&pll0_clk,
 	&pll1_clk,
 	&pll1_div2_clk,
 	&pll2_clk,
 	&pll2s_clk,
 	&pll2h_clk,
+	&z_clk,
 };
 
 /* DIV4 */
 static void div4_kick(struct clk *clk)
 {
-	frqcr_kick_do(clk);
+	if (!WARN(!atomic_inc_and_test(&frqcr_lock), "FRQCR* lock broken!\n"))
+		frqcr_kick_do(clk);
+	atomic_dec(&frqcr_lock);
 }
 
 static int divisors[] = { 2, 3, 4, 6, 8, 12, 16, 18, 24, 0, 36, 48, 10};
@@ -396,6 +512,9 @@ static struct clk_lookup lookups[] = {
 	CLKDEV_CON_ID("pll2s",			&pll2s_clk),
 	CLKDEV_CON_ID("pll2h",			&pll2h_clk),
 
+	/* CPU clock */
+	CLKDEV_DEV_ID("cpufreq-cpu0",		&z_clk),
+
 	/* DIV6 */
 	CLKDEV_CON_ID("zb",			&div6_clks[DIV6_ZB]),
 	CLKDEV_CON_ID("vck1",			&div6_clks[DIV6_VCK1]),
@@ -439,6 +558,8 @@ void __init r8a73a4_clock_init(void)
 	int k, ret = 0;
 	u32 ckscr;
 
+	atomic_set(&frqcr_lock, -1);
+
 	reg = ioremap_nocache(CKSCR, PAGE_SIZE);
 	BUG_ON(!reg);
 	ckscr = ioread32(reg);
arch/arm/mach-shmobile/setup-r8a73a4.c +1 -0
index c5a75a7..7265e14 100644
@@ -185,6 +185,7 @@ void __init r8a73a4_add_standard_devices(void)
 #ifdef CONFIG_USE_OF
 void __init r8a73a4_add_standard_devices_dt(void)
 {
+	platform_device_register_simple("cpufreq-cpu0", -1, NULL, 0);
 	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
 }
 
Signature · 1 line
1.7.2.5
#3 lore raw

PATCH 1/2 ARM: shmobile: r8a73a4: wait for completion when kicking the clock

In reply to Guennadi Liakhovetski
Quoted 6 lines · Guennadi Liakhovetski

To reconfigure clocks, controlled by FRQCRA and FRQCRB, a kick bit has to be set and to make sure the setting has taken effect, it has to be read back repeatedly until it is cleared by the hardware. This patch adds the waiting part, that was missing until now.

I have queued this up for v3.12 in the soc branch of the renesas tree. I have included an Ack from Morimoto-san which he provided here:

"Re: [PATCH 0/4] Add DVFS support on APE6EVM"
http://www.spinics.net/lists/arm-kernel/msg255480.html

Quoted 52 lines
---
 arch/arm/mach-shmobile/clock-r8a73a4.c |   22 ++++++++++++++++------
 1 files changed, 16 insertions(+), 6 deletions(-)
arch/arm/mach-shmobile/clock-r8a73a4.c +16 -6
index 5f7fe62..d5176d0 100644
@@ -184,6 +184,21 @@ PLL_CLOCK(pll2h_clk, &main_div2_clk, pll_parent_main_extal, 3, 5, PLL2HCR, 5);
 
 SH_FIXED_RATIO_CLK(pll1_div2_clk,	pll1_clk,	div2);
 
+static int frqcr_kick_do(struct clk *clk)
+{
+	int i;
+
+	/* set KICK bit in FRQCRB to update hardware setting, check success */
+	iowrite32(ioread32(CPG_MAP(FRQCRB)) | BIT(31), CPG_MAP(FRQCRB));
+	for (i = 1000; i; i--)
+		if (ioread32(CPG_MAP(FRQCRB)) & BIT(31))
+			cpu_relax();
+		else
+			return 0;
+
+	return -ETIMEDOUT;
+}
+
 static struct clk *main_clks[] = {
 	&extalr_clk,
 	&extal1_clk,
@@ -205,12 +220,7 @@ static struct clk *main_clks[] = {
 /* DIV4 */
 static void div4_kick(struct clk *clk)
 {
-	unsigned long value;
-
-	/* set KICK bit in FRQCRB to update hardware setting */
-	value = ioread32(CPG_MAP(FRQCRB));
-	value |= (1 << 31);
-	iowrite32(value, CPG_MAP(FRQCRB));
+	frqcr_kick_do(clk);
 }
 
 static int divisors[] = { 2, 3, 4, 6, 8, 12, 16, 18, 24, 0, 36, 48, 10};
Signature · 6 lines
1.7.2.5

--
To unsubscribe from this list: send the line "unsubscribe linux-sh" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html