aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-omap2/dpll3xxx.c
diff options
context:
space:
mode:
authorVikram Pandita <vikram.pandita@ti.com>2011-11-01 01:07:02 -0700
committerTodd Poynor <toddpoynor@google.com>2011-11-03 16:21:49 -0700
commit339e18619101575a5d16c2ec50a10bce5f99b890 (patch)
tree36707aa9d11119359cd3f201f6effd958f2707b0 /arch/arm/mach-omap2/dpll3xxx.c
parente8e054f7239c901c0938e0f6e392c759aab66d8c (diff)
downloadkernel_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.c58
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);