Skip to content

Commit 035cd48

Browse files
rrw1000wbebarino
authored andcommitted
clk: ti: omap36xx: Work around sprz319 advisory 2.1
The OMAP36xx DPLL5, driving EHCI USB, can be subject to a long-term frequency drift. The frequency drift magnitude depends on the VCO update rate, which is inversely proportional to the PLL divider. The kernel DPLL configuration code results in a high value for the divider, leading to a long term drift high enough to cause USB transmission errors. In the worst case the USB PHY's ULPI interface can stop responding, breaking USB operation completely. This manifests itself on the Beagleboard xM by the LAN9514 reporting 'Cannot enable port 2. Maybe the cable is bad?' in the kernel log. Errata sprz319 advisory 2.1 documents PLL values that minimize the drift. Use them automatically when DPLL5 is used for USB operation, which we detect based on the requested clock rate. The clock framework will still compute the PLL parameters and resulting rate as usual, but the PLL M and N values will then be overridden. This can result in the effective clock rate being slightly different than the rate cached by the clock framework, but won't cause any adverse effect to USB operation. Signed-off-by: Richard Watts <[email protected]> [Upported from v3.2 to v4.9] Signed-off-by: Laurent Pinchart <[email protected]> Tested-by: Ladislav Michl <[email protected]> Signed-off-by: Stephen Boyd <[email protected]>
1 parent 2097920 commit 035cd48

File tree

4 files changed

+104
-11
lines changed

4 files changed

+104
-11
lines changed

drivers/clk/ti/clk-3xxx.c

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,6 @@
2222

2323
#include "clock.h"
2424

25-
/*
26-
* DPLL5_FREQ_FOR_USBHOST: USBHOST and USBTLL are the only clocks
27-
* that are sourced by DPLL5, and both of these require this clock
28-
* to be at 120 MHz for proper operation.
29-
*/
30-
#define DPLL5_FREQ_FOR_USBHOST 120000000
31-
3225
#define OMAP3430ES2_ST_DSS_IDLE_SHIFT 1
3326
#define OMAP3430ES2_ST_HSOTGUSB_IDLE_SHIFT 5
3427
#define OMAP3430ES2_ST_SSI_IDLE_SHIFT 8
@@ -546,14 +539,21 @@ void __init omap3_clk_lock_dpll5(void)
546539
struct clk *dpll5_clk;
547540
struct clk *dpll5_m2_clk;
548541

542+
/*
543+
* Errata sprz319f advisory 2.1 documents a USB host clock drift issue
544+
* that can be worked around using specially crafted dpll5 settings
545+
* with a dpll5_m2 divider set to 8. Set the dpll5 rate to 8x the USB
546+
* host clock rate, its .set_rate handler() will detect that frequency
547+
* and use the errata settings.
548+
*/
549549
dpll5_clk = clk_get(NULL, "dpll5_ck");
550-
clk_set_rate(dpll5_clk, DPLL5_FREQ_FOR_USBHOST);
550+
clk_set_rate(dpll5_clk, OMAP3_DPLL5_FREQ_FOR_USBHOST * 8);
551551
clk_prepare_enable(dpll5_clk);
552552

553-
/* Program dpll5_m2_clk divider for no division */
553+
/* Program dpll5_m2_clk divider */
554554
dpll5_m2_clk = clk_get(NULL, "dpll5_m2_ck");
555555
clk_prepare_enable(dpll5_m2_clk);
556-
clk_set_rate(dpll5_m2_clk, DPLL5_FREQ_FOR_USBHOST);
556+
clk_set_rate(dpll5_m2_clk, OMAP3_DPLL5_FREQ_FOR_USBHOST);
557557

558558
clk_disable_unprepare(dpll5_m2_clk);
559559
clk_disable_unprepare(dpll5_clk);

drivers/clk/ti/clock.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,11 +257,20 @@ long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate,
257257
unsigned long omap3_clkoutx2_recalc(struct clk_hw *hw,
258258
unsigned long parent_rate);
259259

260+
/*
261+
* OMAP3_DPLL5_FREQ_FOR_USBHOST: USBHOST and USBTLL are the only clocks
262+
* that are sourced by DPLL5, and both of these require this clock
263+
* to be at 120 MHz for proper operation.
264+
*/
265+
#define OMAP3_DPLL5_FREQ_FOR_USBHOST 120000000
266+
260267
unsigned long omap3_dpll_recalc(struct clk_hw *hw, unsigned long parent_rate);
261268
int omap3_dpll4_set_rate(struct clk_hw *clk, unsigned long rate,
262269
unsigned long parent_rate);
263270
int omap3_dpll4_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
264271
unsigned long parent_rate, u8 index);
272+
int omap3_dpll5_set_rate(struct clk_hw *hw, unsigned long rate,
273+
unsigned long parent_rate);
265274
void omap3_clk_lock_dpll5(void);
266275

267276
unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw,

drivers/clk/ti/dpll.c

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,18 @@ static const struct clk_ops omap3_dpll_ck_ops = {
114114
.round_rate = &omap2_dpll_round_rate,
115115
};
116116

117+
static const struct clk_ops omap3_dpll5_ck_ops = {
118+
.enable = &omap3_noncore_dpll_enable,
119+
.disable = &omap3_noncore_dpll_disable,
120+
.get_parent = &omap2_init_dpll_parent,
121+
.recalc_rate = &omap3_dpll_recalc,
122+
.set_rate = &omap3_dpll5_set_rate,
123+
.set_parent = &omap3_noncore_dpll_set_parent,
124+
.set_rate_and_parent = &omap3_noncore_dpll_set_rate_and_parent,
125+
.determine_rate = &omap3_noncore_dpll_determine_rate,
126+
.round_rate = &omap2_dpll_round_rate,
127+
};
128+
117129
static const struct clk_ops omap3_dpll_per_ck_ops = {
118130
.enable = &omap3_noncore_dpll_enable,
119131
.disable = &omap3_noncore_dpll_disable,
@@ -474,7 +486,12 @@ static void __init of_ti_omap3_dpll_setup(struct device_node *node)
474486
.modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
475487
};
476488

477-
of_ti_dpll_setup(node, &omap3_dpll_ck_ops, &dd);
489+
if ((of_machine_is_compatible("ti,omap3630") ||
490+
of_machine_is_compatible("ti,omap36xx")) &&
491+
!strcmp(node->name, "dpll5_ck"))
492+
of_ti_dpll_setup(node, &omap3_dpll5_ck_ops, &dd);
493+
else
494+
of_ti_dpll_setup(node, &omap3_dpll_ck_ops, &dd);
478495
}
479496
CLK_OF_DECLARE(ti_omap3_dpll_clock, "ti,omap3-dpll-clock",
480497
of_ti_omap3_dpll_setup);

drivers/clk/ti/dpll3xxx.c

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -838,3 +838,70 @@ int omap3_dpll4_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
838838
return omap3_noncore_dpll_set_rate_and_parent(hw, rate, parent_rate,
839839
index);
840840
}
841+
842+
/* Apply DM3730 errata sprz319 advisory 2.1. */
843+
static bool omap3_dpll5_apply_errata(struct clk_hw *hw,
844+
unsigned long parent_rate)
845+
{
846+
struct omap3_dpll5_settings {
847+
unsigned int rate, m, n;
848+
};
849+
850+
static const struct omap3_dpll5_settings precomputed[] = {
851+
/*
852+
* From DM3730 errata advisory 2.1, table 35 and 36.
853+
* The N value is increased by 1 compared to the tables as the
854+
* errata lists register values while last_rounded_field is the
855+
* real divider value.
856+
*/
857+
{ 12000000, 80, 0 + 1 },
858+
{ 13000000, 443, 5 + 1 },
859+
{ 19200000, 50, 0 + 1 },
860+
{ 26000000, 443, 11 + 1 },
861+
{ 38400000, 25, 0 + 1 }
862+
};
863+
864+
const struct omap3_dpll5_settings *d;
865+
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
866+
struct dpll_data *dd;
867+
unsigned int i;
868+
869+
for (i = 0; i < ARRAY_SIZE(precomputed); ++i) {
870+
if (parent_rate == precomputed[i].rate)
871+
break;
872+
}
873+
874+
if (i == ARRAY_SIZE(precomputed))
875+
return false;
876+
877+
d = &precomputed[i];
878+
879+
/* Update the M, N and rounded rate values and program the DPLL. */
880+
dd = clk->dpll_data;
881+
dd->last_rounded_m = d->m;
882+
dd->last_rounded_n = d->n;
883+
dd->last_rounded_rate = div_u64((u64)parent_rate * d->m, d->n);
884+
omap3_noncore_dpll_program(clk, 0);
885+
886+
return true;
887+
}
888+
889+
/**
890+
* omap3_dpll5_set_rate - set rate for omap3 dpll5
891+
* @hw: clock to change
892+
* @rate: target rate for clock
893+
* @parent_rate: rate of the parent clock
894+
*
895+
* Set rate for the DPLL5 clock. Apply the sprz319 advisory 2.1 on OMAP36xx if
896+
* the DPLL is used for USB host (detected through the requested rate).
897+
*/
898+
int omap3_dpll5_set_rate(struct clk_hw *hw, unsigned long rate,
899+
unsigned long parent_rate)
900+
{
901+
if (rate == OMAP3_DPLL5_FREQ_FOR_USBHOST * 8) {
902+
if (omap3_dpll5_apply_errata(hw, parent_rate))
903+
return 0;
904+
}
905+
906+
return omap3_noncore_dpll_set_rate(hw, rate, parent_rate);
907+
}

0 commit comments

Comments
 (0)