linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] Fix LPC32XX's clock.c
@ 2012-01-27 14:17 Roland Stigge
  2012-01-27 22:32 ` Wolfram Sang
  2012-01-28 16:00 ` Russell King - ARM Linux
  0 siblings, 2 replies; 4+ messages in thread
From: Roland Stigge @ 2012-01-27 14:17 UTC (permalink / raw)
  To: w.sang, grant.likely, linux, linus.walleij, linux-arm-kernel,
	linux-kernel
  Cc: Roland Stigge

* Forward-ported LPC32XX's clock.c from git.lpclinux.com
* Added MLC NAND and ADC clock

Signed-off-by: Roland Stigge <stigge@antcom.de>

diff --git a/arch/arm/mach-lpc32xx/clock.c b/arch/arm/mach-lpc32xx/clock.c
index 1e02751..fabd587 100644
--- a/arch/arm/mach-lpc32xx/clock.c
+++ b/arch/arm/mach-lpc32xx/clock.c
@@ -82,10 +82,13 @@
  *   will also impact the individual peripheral rates.
  */
 
+#include <linux/module.h>
+#include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
 #include <linux/errno.h>
 #include <linux/device.h>
+#include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/clk.h>
 #include <linux/amba/bus.h>
@@ -97,9 +100,12 @@
 #include "clock.h"
 #include "common.h"
 
+static DEFINE_SPINLOCK(global_clkregs_lock);
+
+static int usb_pll_enable, usb_pll_valid;
+
 static struct clk clk_armpll;
 static struct clk clk_usbpll;
-static DEFINE_MUTEX(clkm_lock);
 
 /*
  * Post divider values for PLLs based on selected register value
@@ -127,7 +133,7 @@ static struct clk osc_32KHz = {
 static int local_pll397_enable(struct clk *clk, int enable)
 {
 	u32 reg;
-	unsigned long timeout = 1 + msecs_to_jiffies(10);
+	unsigned long timeout = jiffies + msecs_to_jiffies(10);
 
 	reg = __raw_readl(LPC32XX_CLKPWR_PLL397_CTRL);
 
@@ -156,7 +162,7 @@ static int local_pll397_enable(struct clk *clk, int enable)
 static int local_oscmain_enable(struct clk *clk, int enable)
 {
 	u32 reg;
-	unsigned long timeout = 1 + msecs_to_jiffies(10);
+	unsigned long timeout = jiffies + msecs_to_jiffies(10);
 
 	reg = __raw_readl(LPC32XX_CLKPWR_MAIN_OSC_CTRL);
 
@@ -382,30 +388,62 @@ static u32 local_clk_usbpll_setup(struct clk_pll_setup *pHCLKPllSetup)
 static int local_usbpll_enable(struct clk *clk, int enable)
 {
 	u32 reg;
-	int ret = -ENODEV;
-	unsigned long timeout = 1 + msecs_to_jiffies(10);
+	int ret = 0;
+	unsigned long timeout = jiffies + msecs_to_jiffies(20);
 
 	reg = __raw_readl(LPC32XX_CLKPWR_USB_CTRL);
 
-	if (enable == 0) {
-		reg &= ~(LPC32XX_CLKPWR_USBCTRL_CLK_EN1 |
-			LPC32XX_CLKPWR_USBCTRL_CLK_EN2);
-		__raw_writel(reg, LPC32XX_CLKPWR_USB_CTRL);
-	} else if (reg & LPC32XX_CLKPWR_USBCTRL_PLL_PWRUP) {
+	__raw_writel(reg & ~(LPC32XX_CLKPWR_USBCTRL_CLK_EN2 |
+		LPC32XX_CLKPWR_USBCTRL_PLL_PWRUP),
+		LPC32XX_CLKPWR_USB_CTRL);
+	__raw_writel(reg & ~LPC32XX_CLKPWR_USBCTRL_CLK_EN1,
+		LPC32XX_CLKPWR_USB_CTRL);
+
+	if (enable && usb_pll_valid && usb_pll_enable) {
+		ret = -ENODEV;
+		/*
+		 * If the PLL rate has been previously set, then the rate
+		 * in the PLL register is valid and can be enabled here.
+		 * Otherwise, it needs to be enabled as part of setrate.
+		 */
+
+		/*
+		 * Gate clock into PLL
+		 */
 		reg |= LPC32XX_CLKPWR_USBCTRL_CLK_EN1;
 		__raw_writel(reg, LPC32XX_CLKPWR_USB_CTRL);
 
-		/* Wait for PLL lock */
-		while ((timeout > jiffies) & (ret == -ENODEV)) {
+		/*
+		 * Enable PLL
+		 */
+		reg |= LPC32XX_CLKPWR_USBCTRL_PLL_PWRUP;
+		__raw_writel(reg, LPC32XX_CLKPWR_USB_CTRL);
+
+		/*
+		 * Wait for PLL to lock
+		 */
+		while ((timeout > jiffies) && (ret == -ENODEV)) {
 			reg = __raw_readl(LPC32XX_CLKPWR_USB_CTRL);
 			if (reg & LPC32XX_CLKPWR_USBCTRL_PLL_STS)
 				ret = 0;
+			else
+				udelay(10);
 		}
 
+		/*
+		 * Gate clock from PLL if PLL is locked
+		 */
 		if (ret == 0) {
-			reg |= LPC32XX_CLKPWR_USBCTRL_CLK_EN2;
-			__raw_writel(reg, LPC32XX_CLKPWR_USB_CTRL);
+			__raw_writel(reg | LPC32XX_CLKPWR_USBCTRL_CLK_EN2,
+				LPC32XX_CLKPWR_USB_CTRL);
+		} else {
+			__raw_writel(reg & ~(LPC32XX_CLKPWR_USBCTRL_CLK_EN1 |
+				LPC32XX_CLKPWR_USBCTRL_PLL_PWRUP),
+				LPC32XX_CLKPWR_USB_CTRL);
 		}
+	} else if ((enable == 0) && usb_pll_valid  && usb_pll_enable) {
+		usb_pll_valid = 0;
+		usb_pll_enable = 0;
 	}
 
 	return ret;
@@ -423,7 +461,7 @@ static unsigned long local_usbpll_round_rate(struct clk *clk,
 	 */
 	rate = rate * 1000;
 
-	clkin = clk->parent->rate;
+	clkin = clk->get_rate(clk);
 	usbdiv = (__raw_readl(LPC32XX_CLKPWR_USBCLK_PDIV) &
 		LPC32XX_CLKPWR_USBPDIV_PLL_MASK) + 1;
 	clkin = clkin / usbdiv;
@@ -437,7 +475,8 @@ static unsigned long local_usbpll_round_rate(struct clk *clk,
 
 static int local_usbpll_set_rate(struct clk *clk, unsigned long rate)
 {
-	u32 clkin, reg, usbdiv;
+	int ret = -ENODEV;
+	u32 clkin, usbdiv;
 	struct clk_pll_setup pllsetup;
 
 	/*
@@ -446,7 +485,7 @@ static int local_usbpll_set_rate(struct clk *clk, unsigned long rate)
 	 */
 	rate = rate * 1000;
 
-	clkin = clk->get_rate(clk);
+	clkin = clk->get_rate(clk->parent);
 	usbdiv = (__raw_readl(LPC32XX_CLKPWR_USBCLK_PDIV) &
 		LPC32XX_CLKPWR_USBPDIV_PLL_MASK) + 1;
 	clkin = clkin / usbdiv;
@@ -455,22 +494,25 @@ static int local_usbpll_set_rate(struct clk *clk, unsigned long rate)
 	if (local_clk_find_pll_cfg(clkin, rate, &pllsetup) == 0)
 		return -EINVAL;
 
+	/*
+	 * Disable PLL clocks during PLL change
+	 */
 	local_usbpll_enable(clk, 0);
-
-	reg = __raw_readl(LPC32XX_CLKPWR_USB_CTRL);
-	reg |= LPC32XX_CLKPWR_USBCTRL_CLK_EN1;
-	__raw_writel(reg, LPC32XX_CLKPWR_USB_CTRL);
-
-	pllsetup.analog_on = 1;
+	pllsetup.analog_on = 0;
 	local_clk_usbpll_setup(&pllsetup);
 
-	clk->rate = clk_check_pll_setup(clkin, &pllsetup);
+	/*
+	 * Start USB PLL and check PLL status
+	 */
+
+	usb_pll_valid = 1;
+	usb_pll_enable = 1;
 
-	reg = __raw_readl(LPC32XX_CLKPWR_USB_CTRL);
-	reg |= LPC32XX_CLKPWR_USBCTRL_CLK_EN2;
-	__raw_writel(reg, LPC32XX_CLKPWR_USB_CTRL);
+	ret = local_usbpll_enable(clk, 1);
+	if (ret >= 0)
+		clk->rate = clk_check_pll_setup(clkin, &pllsetup);
 
-	return 0;
+	return ret;
 }
 
 static struct clk clk_usbpll = {
@@ -654,6 +696,14 @@ static struct clk clk_nand = {
 	.get_rate	= local_return_parent_rate,
 };
 
+static struct clk clk_nand_mlc = {
+	.parent		= &clk_hclk,
+	.enable		= local_onoff_enable,
+	.enable_reg	= LPC32XX_CLKPWR_NAND_CLK_CTRL,
+	.enable_mask	= LPC32XX_CLKPWR_NANDCLK_MLCCLK_EN,
+	.get_rate	= local_return_parent_rate,
+};
+
 static struct clk clk_i2s0 = {
 	.parent		= &clk_hclk,
 	.enable		= local_onoff_enable,
@@ -719,6 +769,36 @@ static struct clk clk_tsc = {
 	.get_rate	= local_return_parent_rate,
 };
 
+static int adc_onoff_enable(struct clk *clk, int enable)
+{
+	u32 tmp;
+
+	/* Use PERIPH_CLOCK */
+	tmp = __raw_readl(LPC32XX_CLKPWR_ADC_CLK_CTRL_1);
+	tmp |= LPC32XX_CLKPWR_ADCCTRL1_PCLK_SEL;
+	/*
+	 * Set clock divider so that we have equal to or less than
+	 * 4.5MHz clock at ADC
+	 */
+	tmp |= clk->get_rate(clk) / 4500000 + 1;
+	__raw_writel(tmp, LPC32XX_CLKPWR_ADC_CLK_CTRL_1);
+
+	if (enable == 0)
+		__raw_writel(0, clk->enable_reg);
+	else
+		__raw_writel(clk->enable_mask, clk->enable_reg);
+
+	return 0;
+}
+
+static struct clk clk_adc = {
+	.parent		= &clk_pclk,
+	.enable		= adc_onoff_enable,
+	.enable_reg	= LPC32XX_CLKPWR_ADC_CLK_CTRL,
+	.enable_mask	= LPC32XX_CLKPWR_ADC32CLKCTRL_CLK_EN,
+	.get_rate	= local_return_parent_rate,
+};
+
 static int mmc_onoff_enable(struct clk *clk, int enable)
 {
 	u32 tmp;
@@ -730,6 +810,10 @@ static int mmc_onoff_enable(struct clk *clk, int enable)
 	if (enable != 0)
 		tmp |= LPC32XX_CLKPWR_MSCARD_SDCARD_EN;
 
+	/* Start clock at highest rate */
+	if (!(tmp & LPC32XX_CLKPWR_MSCARD_SDCARD_DIV(0xF)))
+		tmp |= LPC32XX_CLKPWR_MSCARD_SDCARD_DIV(1);
+
 	__raw_writel(tmp, LPC32XX_CLKPWR_MS_CTRL);
 
 	return 0;
@@ -737,14 +821,9 @@ static int mmc_onoff_enable(struct clk *clk, int enable)
 
 static unsigned long mmc_get_rate(struct clk *clk)
 {
-	u32 div, rate, oldclk;
+	u32 div, rate;
 
-	/* The MMC clock must be on when accessing an MMC register */
-	oldclk = __raw_readl(LPC32XX_CLKPWR_MS_CTRL);
-	__raw_writel(oldclk | LPC32XX_CLKPWR_MSCARD_SDCARD_EN,
-		LPC32XX_CLKPWR_MS_CTRL);
 	div = __raw_readl(LPC32XX_CLKPWR_MS_CTRL);
-	__raw_writel(oldclk, LPC32XX_CLKPWR_MS_CTRL);
 
 	/* Get the parent clock rate */
 	rate = clk->parent->get_rate(clk->parent);
@@ -772,12 +851,19 @@ static unsigned long mmc_round_rate(struct clk *clk, unsigned long rate)
 	if (div > 0xf)
 		div = 0xf;
 
+	/*
+	 * The divider is forced to 1 to keep the SD clock granularity
+	 * good. Using a non-0 divider will limit the SD card clock rates
+	 * the SD driver can generate. Remove it if your feeling crazy.
+	 */
+	div = 1;
+
 	return prate / div;
 }
 
 static int mmc_set_rate(struct clk *clk, unsigned long rate)
 {
-	u32 oldclk, tmp;
+	u32 tmp;
 	unsigned long prate, div, crate = mmc_round_rate(clk, rate);
 
 	prate = clk->parent->get_rate(clk->parent);
@@ -785,19 +871,17 @@ static int mmc_set_rate(struct clk *clk, unsigned long rate)
 	div = prate / crate;
 
 	/* The MMC clock must be on when accessing an MMC register */
-	oldclk = __raw_readl(LPC32XX_CLKPWR_MS_CTRL);
-	__raw_writel(oldclk | LPC32XX_CLKPWR_MSCARD_SDCARD_EN,
-		LPC32XX_CLKPWR_MS_CTRL);
 	tmp = __raw_readl(LPC32XX_CLKPWR_MS_CTRL) &
 		~LPC32XX_CLKPWR_MSCARD_SDCARD_DIV(0xf);
 	tmp |= LPC32XX_CLKPWR_MSCARD_SDCARD_DIV(div);
 	__raw_writel(tmp, LPC32XX_CLKPWR_MS_CTRL);
 
-	__raw_writel(oldclk, LPC32XX_CLKPWR_MS_CTRL);
-
 	return 0;
 }
 
+/*
+ * This is the MMC IP clock, not the MMC CLK signal rate!
+ */
 static struct clk clk_mmc = {
 	.parent		= &clk_armpll,
 	.set_rate	= mmc_set_rate,
@@ -891,19 +975,9 @@ static struct clk clk_lcd = {
 	.enable_mask	= LPC32XX_CLKPWR_LCDCTRL_CLK_EN,
 };
 
-static inline void clk_lock(void)
-{
-	mutex_lock(&clkm_lock);
-}
-
-static inline void clk_unlock(void)
-{
-	mutex_unlock(&clkm_lock);
-}
-
 static void local_clk_disable(struct clk *clk)
 {
-	WARN_ON(clk->usecount == 0);
+	/* WARN_ON(clk->usecount == 0); */
 
 	/* Don't attempt to disable clock if it has no users */
 	if (clk->usecount > 0) {
@@ -947,10 +1021,11 @@ static int local_clk_enable(struct clk *clk)
 int clk_enable(struct clk *clk)
 {
 	int ret;
+	unsigned long flags;
 
-	clk_lock();
+	spin_lock_irqsave(&global_clkregs_lock, flags);
 	ret = local_clk_enable(clk);
-	clk_unlock();
+	spin_unlock_irqrestore(&global_clkregs_lock, flags);
 
 	return ret;
 }
@@ -961,9 +1036,11 @@ EXPORT_SYMBOL(clk_enable);
  */
 void clk_disable(struct clk *clk)
 {
-	clk_lock();
+	unsigned long flags;
+
+	spin_lock_irqsave(&global_clkregs_lock, flags);
 	local_clk_disable(clk);
-	clk_unlock();
+	spin_unlock_irqrestore(&global_clkregs_lock, flags);
 }
 EXPORT_SYMBOL(clk_disable);
 
@@ -972,13 +1049,7 @@ EXPORT_SYMBOL(clk_disable);
  */
 unsigned long clk_get_rate(struct clk *clk)
 {
-	unsigned long rate;
-
-	clk_lock();
-	rate = clk->get_rate(clk);
-	clk_unlock();
-
-	return rate;
+	return clk->get_rate(clk);
 }
 EXPORT_SYMBOL(clk_get_rate);
 
@@ -994,11 +1065,8 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
 	 * the actual rate set as part of the peripheral dividers
 	 * instead of high level clock control
 	 */
-	if (clk->set_rate) {
-		clk_lock();
+	if (clk->set_rate)
 		ret = clk->set_rate(clk, rate);
-		clk_unlock();
-	}
 
 	return ret;
 }
@@ -1009,15 +1077,11 @@ EXPORT_SYMBOL(clk_set_rate);
  */
 long clk_round_rate(struct clk *clk, unsigned long rate)
 {
-	clk_lock();
-
 	if (clk->round_rate)
 		rate = clk->round_rate(clk, rate);
 	else
 		rate = clk->get_rate(clk);
 
-	clk_unlock();
-
 	return rate;
 }
 EXPORT_SYMBOL(clk_round_rate);
@@ -1075,6 +1139,8 @@ static struct clk_lookup lookups[] = {
 	_REGISTER_CLOCK("dev:ssp1", NULL, clk_ssp1)
 	_REGISTER_CLOCK("lpc32xx_keys.0", NULL, clk_kscan)
 	_REGISTER_CLOCK("lpc32xx-nand.0", "nand_ck", clk_nand)
+	_REGISTER_CLOCK("lpc32xx-nand-mlc.0", "nand_ck_mlc", clk_nand_mlc)
+	_REGISTER_CLOCK("lpc32xx-adc", NULL, clk_adc)
 	_REGISTER_CLOCK("tbd", "i2s0_ck", clk_i2s0)
 	_REGISTER_CLOCK("tbd", "i2s1_ck", clk_i2s1)
 	_REGISTER_CLOCK("ts-lpc32xx", NULL, clk_tsc)

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH] Fix LPC32XX's clock.c
  2012-01-27 14:17 [PATCH] Fix LPC32XX's clock.c Roland Stigge
@ 2012-01-27 22:32 ` Wolfram Sang
  2012-01-28 10:41   ` Roland Stigge
  2012-01-28 16:00 ` Russell King - ARM Linux
  1 sibling, 1 reply; 4+ messages in thread
From: Wolfram Sang @ 2012-01-27 22:32 UTC (permalink / raw)
  To: Roland Stigge
  Cc: grant.likely, linux, linus.walleij, linux-arm-kernel,
	linux-kernel, Kevin Wells

[-- Attachment #1: Type: text/plain, Size: 749 bytes --]

On Fri, Jan 27, 2012 at 03:17:48PM +0100, Roland Stigge wrote:
> * Forward-ported LPC32XX's clock.c from git.lpclinux.com
> * Added MLC NAND and ADC clock
> 
> Signed-off-by: Roland Stigge <stigge@antcom.de>

This needs to be split up further to make reviewing easier, I'd say. From a
glimpse, I'd guess:

- locking changes
- usb fixes
- adding MLC NAND
- adding ADC

I dunno, maybe the NXP git has the incremental changes? You should really CC
Kevin Wells on your patches. Maybe he will wake up again, at least with some
additional informaition.

Regards,

   Wolfram

-- 
Pengutronix e.K.                           | Wolfram Sang                |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] Fix LPC32XX's clock.c
  2012-01-27 22:32 ` Wolfram Sang
@ 2012-01-28 10:41   ` Roland Stigge
  0 siblings, 0 replies; 4+ messages in thread
From: Roland Stigge @ 2012-01-28 10:41 UTC (permalink / raw)
  To: Wolfram Sang
  Cc: grant.likely, linux, linus.walleij, linux-arm-kernel,
	linux-kernel, Kevin Wells, bangaragiri.g, srinvas.bakki,
	sundarapandian.andithevar

On 27/01/12 23:32, Wolfram Sang wrote:
>> [clock.c]
> This needs to be split up further to make reviewing easier, I'd
> say. From a glimpse, I'd guess:
> 
> - locking changes - usb fixes - adding MLC NAND - adding ADC

OK, will separate things out. Can move over the changes for ADC and
MLC NAND to the respected driver integration patches (following).

> I dunno, maybe the NXP git has the incremental changes? You should
> really CC Kevin Wells on your patches. Maybe he will wake up again,
> at least with some additional informaition.

Yes, I'm also re-forwarding all my integration patches to Kevin Wells
and his colleagues at NXP who also committed to git.lpclinux.com in
the past.

The patches have different sources, all collected together now:

* http://git.lpclinux.com/ (Kevin Wells et. al., NXP), repositories:
  - linux-2.6.27.8-lpc32xx.git
  - linux-2.6.34-lpc32xx.git
  - linux-2.6.39.2-lpc.git
* http://git.antcom.de/ (my repos)
  - linux-2.6, branch work92105

I basically collected the most recent versions and fixes from the
other different sources together and forward-ported to latest Linux
mainline. (Currently works really well for the WORK Microwave devices
based on LPC3250.) You can find many incremental changes in the above
repositories hopefully clearing things up.

Thanks in advance,

Roland

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] Fix LPC32XX's clock.c
  2012-01-27 14:17 [PATCH] Fix LPC32XX's clock.c Roland Stigge
  2012-01-27 22:32 ` Wolfram Sang
@ 2012-01-28 16:00 ` Russell King - ARM Linux
  1 sibling, 0 replies; 4+ messages in thread
From: Russell King - ARM Linux @ 2012-01-28 16:00 UTC (permalink / raw)
  To: Roland Stigge
  Cc: w.sang, grant.likely, linus.walleij, linux-arm-kernel, linux-kernel

On Fri, Jan 27, 2012 at 03:17:48PM +0100, Roland Stigge wrote:
> @@ -382,30 +388,62 @@ static u32 local_clk_usbpll_setup(struct clk_pll_setup *pHCLKPllSetup)
>  static int local_usbpll_enable(struct clk *clk, int enable)
>  {
>  	u32 reg;
> -	int ret = -ENODEV;
> -	unsigned long timeout = 1 + msecs_to_jiffies(10);
> +	int ret = 0;
> +	unsigned long timeout = jiffies + msecs_to_jiffies(20);

...

> -		/* Wait for PLL lock */
> -		while ((timeout > jiffies) & (ret == -ENODEV)) {
> +		/*
> +		 * Enable PLL
> +		 */
> +		reg |= LPC32XX_CLKPWR_USBCTRL_PLL_PWRUP;
> +		__raw_writel(reg, LPC32XX_CLKPWR_USB_CTRL);
> +
> +		/*
> +		 * Wait for PLL to lock
> +		 */
> +		while ((timeout > jiffies) && (ret == -ENODEV)) {

This is buggy - it's waiting to go wrong when jiffies wraps.  You really
want to use time_before() or time_after() to make these comparisons, which
are safe against wrap-around.

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2012-01-28 16:01 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-01-27 14:17 [PATCH] Fix LPC32XX's clock.c Roland Stigge
2012-01-27 22:32 ` Wolfram Sang
2012-01-28 10:41   ` Roland Stigge
2012-01-28 16:00 ` Russell King - ARM Linux

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).