[RFC PATCH] ARM: Add SSI Clock Adjustment for i.MX27 TO2

Stuart Longland redhatter at gentoo.org
Sun May 30 20:55:00 EDT 2010


This adds support for adjusting the SSI clocks using the generic clock
infrastructure.

Known bug:
	The generated clock is approximately 1/4 of what is requested.
	I'm not sure if this is due to a bug in reading MCLK, or
	something else dividing the clock down.  Hence the printk's in
	get_rate_ssix, and the patch's "RFC" status.

This should open the door to using the SSI ports on the i.MX27 in I²S
Master mode amongst other things.

Signed-off-by: Stuart Longland <redhatter at gentoo.org>
---
 arch/arm/mach-mx2/clock_imx27.c |   75 +++++++++++++++++++++++++++++++++++++--
 1 files changed, 72 insertions(+), 3 deletions(-)

diff --git a/arch/arm/mach-mx2/clock_imx27.c b/arch/arm/mach-mx2/clock_imx27.c
index 0f0823c..48a4f62 100644
--- a/arch/arm/mach-mx2/clock_imx27.c
+++ b/arch/arm/mach-mx2/clock_imx27.c
@@ -244,12 +244,59 @@ static unsigned long get_rate_ssix(struct clk *clk, unsigned long pdf)
 
 	parent_rate = clk_get_rate(clk->parent);
 
+	printk("%s: parent = %ld Hz; raw pdf = %ld\n",
+		__FUNCTION__, parent_rate, pdf );
+
 	if (mx27_revision() >= CHIP_REV_2_0)
 		pdf += 4;  /* MX27 TO2+ */
 	else
 		pdf = (pdf < 2) ? 124UL : pdf;  /* MX21 & MX27 TO1 */
 
-	return 2UL * parent_rate / pdf;
+	printk("%s: parent = %ld Hz;     pdf = %ld\n",
+		__FUNCTION__, parent_rate, pdf );
+
+	return (2UL * parent_rate) / pdf;
+}
+
+static unsigned long set_rate_ssix(struct clk *clk, unsigned long rate)
+{
+	unsigned long parent_rate;
+	unsigned long pdf;
+
+	parent_rate = clk_get_rate(clk->parent);
+	pdf = (2UL * parent_rate) / rate;
+
+	if (mx27_revision() >= CHIP_REV_2_0)
+		pdf -= 4;  /* MX27 TO2+ */
+
+	/* TODO: Not sure how to handle other CPU types */
+#if 0
+	else
+		pdf = (pdf < 2) ? 124UL : pdf;  /* MX21 & MX27 TO1 */
+#endif
+	printk("%s: %ld Hz => %ld; pdf = %ld\n",
+			__FUNCTION__, parent_rate, rate, pdf );
+
+	return pdf & 0x3f;
+}
+
+static unsigned long round_rate_ssix(struct clk *clk, unsigned long rate)
+{
+	unsigned long div;
+	unsigned long parent_rate = clk_get_rate(clk->parent);
+
+	div = (2UL * parent_rate) / rate;
+	printk("%s: 2* %ld Hz / %ld Hz = %ld\n",
+			__FUNCTION__, parent_rate, rate, div );
+
+	if ( ( div < 4 ) || ( div > 67 ) )
+		/* Corresponds to divider value < 2 or > 33.5 */
+		return -EINVAL;
+
+	printk("%s: 2* %ld Hz / %ld = %ld\n",
+			__FUNCTION__, parent_rate, div, (2UL*parent_rate) / div);
+
+	return (2UL * parent_rate) / div;
 }
 
 static unsigned long get_rate_ssi1(struct clk *clk)
@@ -257,11 +304,33 @@ static unsigned long get_rate_ssi1(struct clk *clk)
 	return get_rate_ssix(clk, (__raw_readl(CCM_PCDR0) >> 16) & 0x3f);
 }
 
+static int set_rate_ssi1(struct clk *clk, unsigned long rate)
+{
+	__raw_writel( set_rate_ssix(clk, rate) << 16, CCM_PCDR0 );
+	return 0;
+}
+
+static unsigned long round_rate_ssi1(struct clk *clk, unsigned long rate)
+{
+	return round_rate_ssix(clk, rate);
+}
+
 static unsigned long get_rate_ssi2(struct clk *clk)
 {
 	return get_rate_ssix(clk, (__raw_readl(CCM_PCDR0) >> 26) & 0x3f);
 }
 
+static int set_rate_ssi2(struct clk *clk, unsigned long rate)
+{
+	__raw_writel( set_rate_ssix(clk, rate) << 26, CCM_PCDR0 );
+	return 0;
+}
+
+static unsigned long round_rate_ssi2(struct clk *clk, unsigned long rate)
+{
+	return round_rate_ssix(clk, rate);
+}
+
 static unsigned long get_rate_nfc(struct clk *clk)
 {
 	unsigned long nfc_pdf;
@@ -564,8 +633,8 @@ DEFINE_CLOCK(cspi1_clk1,   0, PCCR0, 31, NULL, NULL, &ipg_clk);
 
 DEFINE_CLOCK(mstick_clk,   0, PCCR1,  2, NULL, &mstick_clk1, &ipg_clk);
 DEFINE_CLOCK(nfc_clk,      0, PCCR1,  3, get_rate_nfc, NULL, &cpu_clk);
-DEFINE_CLOCK(ssi2_clk,     1, PCCR1,  4, get_rate_ssi2, &ssi2_clk1, &mpll_main2_clk);
-DEFINE_CLOCK(ssi1_clk,     0, PCCR1,  5, get_rate_ssi1, &ssi1_clk1, &mpll_main2_clk);
+DEFINE_CLOCK1(ssi2_clk,     1, PCCR1,  4, ssi2, &ssi2_clk1, &mpll_main2_clk);
+DEFINE_CLOCK1(ssi1_clk,     0, PCCR1,  5, ssi1, &ssi1_clk1, &mpll_main2_clk);
 DEFINE_CLOCK(vpu_clk,      0, PCCR1,  6, get_rate_vpu, &vpu_clk1, &mpll_main2_clk);
 DEFINE_CLOCK1(per4_clk,    3, PCCR1,  7, per, NULL, &mpll_main2_clk);
 DEFINE_CLOCK1(per3_clk,    2, PCCR1,  8, per, NULL, &mpll_main2_clk);
-- 
1.6.4.4




More information about the linux-arm-kernel mailing list