Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <rrw@kynesim.co.uk>
[Upported from v3.2 to v4.9]
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Tested-by: Ladislav Michl <ladis@linux-mips.org>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>

authored by

Richard Watts and committed by
Stephen Boyd
035cd485 20979202

+104 -11
+10 -10
drivers/clk/ti/clk-3xxx.c
··· 22 22 23 23 #include "clock.h" 24 24 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 - 32 25 #define OMAP3430ES2_ST_DSS_IDLE_SHIFT 1 33 26 #define OMAP3430ES2_ST_HSOTGUSB_IDLE_SHIFT 5 34 27 #define OMAP3430ES2_ST_SSI_IDLE_SHIFT 8 ··· 539 546 struct clk *dpll5_clk; 540 547 struct clk *dpll5_m2_clk; 541 548 549 + /* 550 + * Errata sprz319f advisory 2.1 documents a USB host clock drift issue 551 + * that can be worked around using specially crafted dpll5 settings 552 + * with a dpll5_m2 divider set to 8. Set the dpll5 rate to 8x the USB 553 + * host clock rate, its .set_rate handler() will detect that frequency 554 + * and use the errata settings. 555 + */ 542 556 dpll5_clk = clk_get(NULL, "dpll5_ck"); 543 - clk_set_rate(dpll5_clk, DPLL5_FREQ_FOR_USBHOST); 557 + clk_set_rate(dpll5_clk, OMAP3_DPLL5_FREQ_FOR_USBHOST * 8); 544 558 clk_prepare_enable(dpll5_clk); 545 559 546 - /* Program dpll5_m2_clk divider for no division */ 560 + /* Program dpll5_m2_clk divider */ 547 561 dpll5_m2_clk = clk_get(NULL, "dpll5_m2_ck"); 548 562 clk_prepare_enable(dpll5_m2_clk); 549 - clk_set_rate(dpll5_m2_clk, DPLL5_FREQ_FOR_USBHOST); 563 + clk_set_rate(dpll5_m2_clk, OMAP3_DPLL5_FREQ_FOR_USBHOST); 550 564 551 565 clk_disable_unprepare(dpll5_m2_clk); 552 566 clk_disable_unprepare(dpll5_clk);
+9
drivers/clk/ti/clock.h
··· 257 257 unsigned long omap3_clkoutx2_recalc(struct clk_hw *hw, 258 258 unsigned long parent_rate); 259 259 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 + 260 267 unsigned long omap3_dpll_recalc(struct clk_hw *hw, unsigned long parent_rate); 261 268 int omap3_dpll4_set_rate(struct clk_hw *clk, unsigned long rate, 262 269 unsigned long parent_rate); 263 270 int omap3_dpll4_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, 264 271 unsigned long parent_rate, u8 index); 272 + int omap3_dpll5_set_rate(struct clk_hw *hw, unsigned long rate, 273 + unsigned long parent_rate); 265 274 void omap3_clk_lock_dpll5(void); 266 275 267 276 unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw,
+18 -1
drivers/clk/ti/dpll.c
··· 114 114 .round_rate = &omap2_dpll_round_rate, 115 115 }; 116 116 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 + 117 129 static const struct clk_ops omap3_dpll_per_ck_ops = { 118 130 .enable = &omap3_noncore_dpll_enable, 119 131 .disable = &omap3_noncore_dpll_disable, ··· 486 474 .modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED), 487 475 }; 488 476 489 - of_ti_dpll_setup(node, &omap3_dpll_ck_ops, &dd); 477 + if ((of_machine_is_compatible("ti,omap3630") || 478 + of_machine_is_compatible("ti,omap36xx")) && 479 + !strcmp(node->name, "dpll5_ck")) 480 + of_ti_dpll_setup(node, &omap3_dpll5_ck_ops, &dd); 481 + else 482 + of_ti_dpll_setup(node, &omap3_dpll_ck_ops, &dd); 490 483 } 491 484 CLK_OF_DECLARE(ti_omap3_dpll_clock, "ti,omap3-dpll-clock", 492 485 of_ti_omap3_dpll_setup);
+67
drivers/clk/ti/dpll3xxx.c
··· 838 838 return omap3_noncore_dpll_set_rate_and_parent(hw, rate, parent_rate, 839 839 index); 840 840 } 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 + }