diff options
author | Vikram Pandita <vikram.pandita@ti.com> | 2011-11-01 01:07:02 -0700 |
---|---|---|
committer | Todd Poynor <toddpoynor@google.com> | 2011-11-03 16:21:49 -0700 |
commit | 339e18619101575a5d16c2ec50a10bce5f99b890 (patch) | |
tree | 36707aa9d11119359cd3f201f6effd958f2707b0 /arch/arm/mach-omap2/dpll3xxx.c | |
parent | e8e054f7239c901c0938e0f6e392c759aab66d8c (diff) | |
download | kernel_samsung_tuna-339e18619101575a5d16c2ec50a10bce5f99b890.zip kernel_samsung_tuna-339e18619101575a5d16c2ec50a10bce5f99b890.tar.gz kernel_samsung_tuna-339e18619101575a5d16c2ec50a10bce5f99b890.tar.bz2 |
omap: usb-dpll: recovery from failed dpll locking
If usbdpll failed to lock, program the usbdpll to MN bypass.
This will reset the DPLL state machine resulting in reset value
for register CM_CLKSEL_DPLL_USB.
So after MN bypass, reprogram CM_CLKSEL_DPLL_USB so that USBDPLL
continues to supply two clocks at: 480Mhz and 960Mhz.
Force generate a request to PRCM by not putting dpll in autoidle.
CM_DIV_M2_DPLL_USB.DPLL_CLKOUT_GATE_CTRL = 1
CM_CLKDCOLDO_DPLL_USB.DPLL_CLKDCOLDO_GATE_CTRL = 1
Switch back to lock mode and wait for IDLEST to indicate success locking.
Change-Id: Icd3c4e1bf13d8a4c62889feceecfc7cba68b727f
Signed-off-by: Vikram Pandita <vikram.pandita@ti.com>
Diffstat (limited to 'arch/arm/mach-omap2/dpll3xxx.c')
-rw-r--r-- | arch/arm/mach-omap2/dpll3xxx.c | 58 |
1 files changed, 57 insertions, 1 deletions
diff --git a/arch/arm/mach-omap2/dpll3xxx.c b/arch/arm/mach-omap2/dpll3xxx.c index 73a1595..2dad3c9 100644 --- a/arch/arm/mach-omap2/dpll3xxx.c +++ b/arch/arm/mach-omap2/dpll3xxx.c @@ -32,8 +32,10 @@ #include <plat/clock.h> #include "clock.h" +#include "cm2_44xx.h" #include "cm2xxx_3xxx.h" #include "cm-regbits-34xx.h" +#include "cm-regbits-44xx.h" /* CM_AUTOIDLE_PLL*.AUTO_* bit values */ #define DPLL_AUTOIDLE_DISABLE 0x0 @@ -61,22 +63,76 @@ static void _omap3_dpll_write_clken(struct clk *clk, u8 clken_bits) static int _omap3_wait_dpll_status(struct clk *clk, u8 state) { const struct dpll_data *dd; - int i = 0; + int i; int ret = -EINVAL; + bool first_time = true; + u32 reg; + u32 orig_cm_div_m2_dpll_usb; + u32 orig_cm_clkdcoldo_dpll_usb; +retry: dd = clk->dpll_data; state <<= __ffs(dd->idlest_mask); + i = 0; while (((__raw_readl(dd->idlest_reg) & dd->idlest_mask) != state) && i < MAX_DPLL_WAIT_TRIES) { i++; udelay(1); } + /* restore back old values if hit work-around */ + if (!first_time) { + __raw_writel(orig_cm_div_m2_dpll_usb, + OMAP4430_CM_DIV_M2_DPLL_USB); + __raw_writel(orig_cm_clkdcoldo_dpll_usb, + OMAP4430_CM_CLKDCOLDO_DPLL_USB); + } + if (i == MAX_DPLL_WAIT_TRIES) { printk(KERN_ERR "clock: %s failed transition to '%s'\n", clk->name, (state) ? "locked" : "bypassed"); + + /* Try Error Recovery: for failing usbdpll locking */ + if (!strcmp(clk->name, "dpll_usb_ck")) { + + reg = __raw_readl(dd->mult_div1_reg); + + /* Put in MN bypass */ + _omap3_dpll_write_clken(clk, DPLL_MN_BYPASS); + i = 0; + while (!(__raw_readl(dd->idlest_reg) & (1 << OMAP4430_ST_MN_BYPASS_SHIFT)) && + i < MAX_DPLL_WAIT_TRIES) { + i++; + udelay(1); + } + + /* MN bypass looses contents of CM_CLKSEL_DPLL_USB */ + __raw_writel(reg, dd->mult_div1_reg); + + /* Force generate request to PRCM: put in Force mode */ + + /* a) CM_DIV_M2_DPLL_USB.DPLL_CLKOUT_GATE_CTRL = 1 */ + orig_cm_div_m2_dpll_usb = __raw_readl(OMAP4430_CM_DIV_M2_DPLL_USB); + __raw_writel(orig_cm_div_m2_dpll_usb | + (1 << OMAP4430_DPLL_CLKOUT_GATE_CTRL_SHIFT), + OMAP4430_CM_DIV_M2_DPLL_USB); + + /* b) CM_CLKDCOLDO_DPLL_USB.DPLL_CLKDCOLDO_GATE_CTRL = 1 */ + orig_cm_clkdcoldo_dpll_usb = __raw_readl(OMAP4430_CM_CLKDCOLDO_DPLL_USB); + __raw_writel(orig_cm_clkdcoldo_dpll_usb | + (1 << OMAP4430_DPLL_CLKDCOLDO_GATE_CTRL_SHIFT), + OMAP4430_CM_CLKDCOLDO_DPLL_USB); + + /* Put back to locked mode */ + _omap3_dpll_write_clken(clk, DPLL_LOCKED); + + if (first_time) { + first_time = false; + goto retry; + } + } } else { pr_debug("clock: %s transition to '%s' in %d loops\n", clk->name, (state) ? "locked" : "bypassed", i); |