From: Richard Watts Date: Mon, 20 Feb 2012 17:58:26 +0000 (+0000) Subject: Fix sprz319 erratum 2.1 X-Git-Tag: sz_161~58 X-Git-Url: https://git.openpandora.org/cgi-bin/gitweb.cgi?p=pandora-kernel.git;a=commitdiff_plain;h=597266f729424dfe00d1bf3d69c0162b731f022d Fix sprz319 erratum 2.1 There is an erratum in DM3730 which results in the EHCI USB PLL (DPLL5) not updating sufficiently frequently; this leads to USB PHY clock drift and once the clock has drifted far enough, the PHY's ULPI interface stops responding and USB drops out. This is manifested on a Beagle xM by having the attached SMSC9514 report 'Cannot enable port 2. Maybe the USB cable is bad?' or similar. The fix is to carefully adjust your DPLL5 settings so as to keep the PHY clock as close as possible to 120MHz over the long term; TI SPRZ319e gives a table of such settings and this patch applies that table to systems with a 13MHz or a 26MHz clock, thus fixing the issue (inasfar as it can be fixed) on Beagle xM and Overo Firestorm. Signed-off-by: Richard Watts --- diff --git a/arch/arm/mach-omap2/clkt_clksel.c b/arch/arm/mach-omap2/clkt_clksel.c index e25364de028a..e378fe7299f8 100644 --- a/arch/arm/mach-omap2/clkt_clksel.c +++ b/arch/arm/mach-omap2/clkt_clksel.c @@ -460,6 +460,21 @@ int omap2_clksel_set_rate(struct clk *clk, unsigned long rate) return 0; } +int omap2_clksel_force_divisor(struct clk *clk, int new_div) +{ + u32 field_val; + + field_val = _divisor_to_clksel(clk, new_div); + if (field_val == ~0) + return -EINVAL; + + _write_clksel_reg(clk, field_val); + + clk->rate = clk->parent->rate / new_div; + + return 0; +} + /* * Clksel parent setting function - not passed in struct clk function * pointer - instead, the OMAP clock code currently assumes that any diff --git a/arch/arm/mach-omap2/clock.h b/arch/arm/mach-omap2/clock.h index 2311bc217226..4d96a1788505 100644 --- a/arch/arm/mach-omap2/clock.h +++ b/arch/arm/mach-omap2/clock.h @@ -61,6 +61,12 @@ void omap3_dpll_allow_idle(struct clk *clk); void omap3_dpll_deny_idle(struct clk *clk); u32 omap3_dpll_autoidle_read(struct clk *clk); int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate); +#if CONFIG_ARCH_OMAP3 +int omap3_noncore_dpll_program(struct clk *clk, u16 m, u8 n, u16 freqsel); +/* If you are using this function and not on OMAP3, you are + * Doing It Wrong(tm), so there is no stub. + */ +#endif int omap3_noncore_dpll_enable(struct clk *clk); void omap3_noncore_dpll_disable(struct clk *clk); int omap4_dpllmx_gatectrl_read(struct clk *clk); @@ -86,6 +92,7 @@ unsigned long omap2_clksel_recalc(struct clk *clk); long omap2_clksel_round_rate(struct clk *clk, unsigned long target_rate); int omap2_clksel_set_rate(struct clk *clk, unsigned long rate); int omap2_clksel_set_parent(struct clk *clk, struct clk *new_parent); +int omap2_clksel_force_divisor(struct clk *clk, int new_div); /* clkt_iclk.c public functions */ extern void omap2_clkt_iclk_allow_idle(struct clk *clk); diff --git a/arch/arm/mach-omap2/clock3xxx.c b/arch/arm/mach-omap2/clock3xxx.c index 952c3e01c9eb..d5be0863dbb8 100644 --- a/arch/arm/mach-omap2/clock3xxx.c +++ b/arch/arm/mach-omap2/clock3xxx.c @@ -40,6 +40,60 @@ /* needed by omap3_core_dpll_m2_set_rate() */ struct clk *sdrc_ick_p, *arm_fck_p; +struct dpll_settings { + int rate, m, n, f; +}; + + +static int omap3_dpll5_apply_erratum21(struct clk *clk, struct clk *dpll5_m2) +{ + struct clk *sys_clk; + int i, rv; + static const struct dpll_settings precomputed[] = { + /* From DM3730 errata (sprz319e), table 36 + * +1 is because the values in the table are register values; + * dpll_program() will subtract one from what we give it, + * so ... + */ + { 13000000, 443+1, 5+1, 8 }, + { 26000000, 443+1, 11+1, 8 } + }; + + sys_clk = clk_get(NULL, "sys_ck"); + + for (i = 0 ; i < (sizeof(precomputed)/sizeof(struct dpll_settings)) ; + ++i) { + const struct dpll_settings *d = &precomputed[i]; + if (sys_clk->rate == d->rate) { + rv = omap3_noncore_dpll_program(clk, d->m , d->n, 0); + if (rv) + return 1; + rv = omap2_clksel_force_divisor(dpll5_m2 , d->f); + return 1; + } + } + return 0; +} + +int omap3_dpll5_set_rate(struct clk *clk, unsigned long rate) +{ + struct clk *dpll5_m2; + int rv; + dpll5_m2 = clk_get(NULL, "dpll5_m2_ck"); + + if (cpu_is_omap3630() && rate == DPLL5_FREQ_FOR_USBHOST && + omap3_dpll5_apply_erratum21(clk, dpll5_m2)) { + return 1; + } + rv = omap3_noncore_dpll_set_rate(clk, rate); + if (rv) + goto out; + rv = clk_set_rate(dpll5_m2, rate); + +out: + return rv; +} + int omap3_dpll4_set_rate(struct clk *clk, unsigned long rate) { /* @@ -59,19 +113,14 @@ int omap3_dpll4_set_rate(struct clk *clk, unsigned long rate) void __init omap3_clk_lock_dpll5(void) { struct clk *dpll5_clk; - struct clk *dpll5_m2_clk; dpll5_clk = clk_get(NULL, "dpll5_ck"); clk_set_rate(dpll5_clk, DPLL5_FREQ_FOR_USBHOST); - clk_enable(dpll5_clk); - /* Program dpll5_m2_clk divider for no division */ - dpll5_m2_clk = clk_get(NULL, "dpll5_m2_ck"); - clk_enable(dpll5_m2_clk); - clk_set_rate(dpll5_m2_clk, DPLL5_FREQ_FOR_USBHOST); + /* dpll5_m2_ck is now (grottily!) handled by dpll5_clk's set routine, + * to cope with an erratum on DM3730 + */ - clk_disable(dpll5_m2_clk); - clk_disable(dpll5_clk); return; } diff --git a/arch/arm/mach-omap2/clock3xxx.h b/arch/arm/mach-omap2/clock3xxx.h index 8bbeeaf399e2..0ede51317cc9 100644 --- a/arch/arm/mach-omap2/clock3xxx.h +++ b/arch/arm/mach-omap2/clock3xxx.h @@ -10,6 +10,7 @@ int omap3xxx_clk_init(void); int omap3_dpll4_set_rate(struct clk *clk, unsigned long rate); +int omap3_dpll5_set_rate(struct clk *clk, unsigned long rate); int omap3_core_dpll_m2_set_rate(struct clk *clk, unsigned long rate); void omap3_clk_lock_dpll5(void); diff --git a/arch/arm/mach-omap2/clock3xxx_data.c b/arch/arm/mach-omap2/clock3xxx_data.c index a352b66d673e..087c01b905f9 100644 --- a/arch/arm/mach-omap2/clock3xxx_data.c +++ b/arch/arm/mach-omap2/clock3xxx_data.c @@ -942,7 +942,7 @@ static struct clk dpll5_ck = { .parent = &sys_ck, .dpll_data = &dpll5_dd, .round_rate = &omap2_dpll_round_rate, - .set_rate = &omap3_noncore_dpll_set_rate, + .set_rate = &omap3_dpll5_set_rate, .clkdm_name = "dpll5_clkdm", .recalc = &omap3_dpll_recalc, }; diff --git a/arch/arm/mach-omap2/dpll3xxx.c b/arch/arm/mach-omap2/dpll3xxx.c index fc56745676fa..50ccee438d87 100644 --- a/arch/arm/mach-omap2/dpll3xxx.c +++ b/arch/arm/mach-omap2/dpll3xxx.c @@ -291,7 +291,7 @@ static void _lookup_sddiv(struct clk *clk, u8 *sd_div, u16 m, u8 n) * Program the DPLL with the supplied M, N values, and wait for the DPLL to * lock.. Returns -EINVAL upon error, or 0 upon success. */ -static int omap3_noncore_dpll_program(struct clk *clk, u16 m, u8 n, u16 freqsel) +int omap3_noncore_dpll_program(struct clk *clk, u16 m, u8 n, u16 freqsel) { struct dpll_data *dd = clk->dpll_data; u8 dco, sd_div;