]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.9-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 4 Jan 2017 09:41:32 +0000 (10:41 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 4 Jan 2017 09:41:32 +0000 (10:41 +0100)
added patches:
clk-ti-omap36xx-work-around-sprz319-advisory-2.1.patch

queue-4.9/clk-ti-omap36xx-work-around-sprz319-advisory-2.1.patch [new file with mode: 0644]
queue-4.9/series

diff --git a/queue-4.9/clk-ti-omap36xx-work-around-sprz319-advisory-2.1.patch b/queue-4.9/clk-ti-omap36xx-work-around-sprz319-advisory-2.1.patch
new file mode 100644 (file)
index 0000000..aaba36b
--- /dev/null
@@ -0,0 +1,215 @@
+From 035cd485a47dda64f25ccf8a90b11a07d0b7aa7a Mon Sep 17 00:00:00 2001
+From: Richard Watts <rrw@kynesim.co.uk>
+Date: Fri, 2 Dec 2016 23:14:38 +0200
+Subject: clk: ti: omap36xx: Work around sprz319 advisory 2.1
+
+From: Richard Watts <rrw@kynesim.co.uk>
+
+commit 035cd485a47dda64f25ccf8a90b11a07d0b7aa7a upstream.
+
+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>
+Cc: Adam Ford <aford173@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/clk/ti/clk-3xxx.c |   20 ++++++-------
+ drivers/clk/ti/clock.h    |    9 ++++++
+ drivers/clk/ti/dpll.c     |   19 ++++++++++++-
+ drivers/clk/ti/dpll3xxx.c |   67 ++++++++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 104 insertions(+), 11 deletions(-)
+
+--- a/drivers/clk/ti/clk-3xxx.c
++++ b/drivers/clk/ti/clk-3xxx.c
+@@ -22,13 +22,6 @@
+ #include "clock.h"
+-/*
+- * DPLL5_FREQ_FOR_USBHOST: USBHOST and USBTLL are the only clocks
+- * that are sourced by DPLL5, and both of these require this clock
+- * to be at 120 MHz for proper operation.
+- */
+-#define DPLL5_FREQ_FOR_USBHOST                120000000
+-
+ #define OMAP3430ES2_ST_DSS_IDLE_SHIFT                 1
+ #define OMAP3430ES2_ST_HSOTGUSB_IDLE_SHIFT            5
+ #define OMAP3430ES2_ST_SSI_IDLE_SHIFT                 8
+@@ -546,14 +539,21 @@ void __init omap3_clk_lock_dpll5(void)
+       struct clk *dpll5_clk;
+       struct clk *dpll5_m2_clk;
++      /*
++       * Errata sprz319f advisory 2.1 documents a USB host clock drift issue
++       * that can be worked around using specially crafted dpll5 settings
++       * with a dpll5_m2 divider set to 8. Set the dpll5 rate to 8x the USB
++       * host clock rate, its .set_rate handler() will detect that frequency
++       * and use the errata settings.
++       */
+       dpll5_clk = clk_get(NULL, "dpll5_ck");
+-      clk_set_rate(dpll5_clk, DPLL5_FREQ_FOR_USBHOST);
++      clk_set_rate(dpll5_clk, OMAP3_DPLL5_FREQ_FOR_USBHOST * 8);
+       clk_prepare_enable(dpll5_clk);
+-      /* Program dpll5_m2_clk divider for no division */
++      /* Program dpll5_m2_clk divider */
+       dpll5_m2_clk = clk_get(NULL, "dpll5_m2_ck");
+       clk_prepare_enable(dpll5_m2_clk);
+-      clk_set_rate(dpll5_m2_clk, DPLL5_FREQ_FOR_USBHOST);
++      clk_set_rate(dpll5_m2_clk, OMAP3_DPLL5_FREQ_FOR_USBHOST);
+       clk_disable_unprepare(dpll5_m2_clk);
+       clk_disable_unprepare(dpll5_clk);
+--- a/drivers/clk/ti/clock.h
++++ b/drivers/clk/ti/clock.h
+@@ -257,11 +257,20 @@ long omap2_dpll_round_rate(struct clk_hw
+ unsigned long omap3_clkoutx2_recalc(struct clk_hw *hw,
+                                   unsigned long parent_rate);
++/*
++ * OMAP3_DPLL5_FREQ_FOR_USBHOST: USBHOST and USBTLL are the only clocks
++ * that are sourced by DPLL5, and both of these require this clock
++ * to be at 120 MHz for proper operation.
++ */
++#define OMAP3_DPLL5_FREQ_FOR_USBHOST  120000000
++
+ unsigned long omap3_dpll_recalc(struct clk_hw *hw, unsigned long parent_rate);
+ int omap3_dpll4_set_rate(struct clk_hw *clk, unsigned long rate,
+                        unsigned long parent_rate);
+ int omap3_dpll4_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
+                                   unsigned long parent_rate, u8 index);
++int omap3_dpll5_set_rate(struct clk_hw *hw, unsigned long rate,
++                       unsigned long parent_rate);
+ void omap3_clk_lock_dpll5(void);
+ unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw,
+--- a/drivers/clk/ti/dpll.c
++++ b/drivers/clk/ti/dpll.c
+@@ -114,6 +114,18 @@ static const struct clk_ops omap3_dpll_c
+       .round_rate     = &omap2_dpll_round_rate,
+ };
++static const struct clk_ops omap3_dpll5_ck_ops = {
++      .enable         = &omap3_noncore_dpll_enable,
++      .disable        = &omap3_noncore_dpll_disable,
++      .get_parent     = &omap2_init_dpll_parent,
++      .recalc_rate    = &omap3_dpll_recalc,
++      .set_rate       = &omap3_dpll5_set_rate,
++      .set_parent     = &omap3_noncore_dpll_set_parent,
++      .set_rate_and_parent    = &omap3_noncore_dpll_set_rate_and_parent,
++      .determine_rate = &omap3_noncore_dpll_determine_rate,
++      .round_rate     = &omap2_dpll_round_rate,
++};
++
+ static const struct clk_ops omap3_dpll_per_ck_ops = {
+       .enable         = &omap3_noncore_dpll_enable,
+       .disable        = &omap3_noncore_dpll_disable,
+@@ -474,7 +486,12 @@ static void __init of_ti_omap3_dpll_setu
+               .modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
+       };
+-      of_ti_dpll_setup(node, &omap3_dpll_ck_ops, &dd);
++      if ((of_machine_is_compatible("ti,omap3630") ||
++           of_machine_is_compatible("ti,omap36xx")) &&
++          !strcmp(node->name, "dpll5_ck"))
++              of_ti_dpll_setup(node, &omap3_dpll5_ck_ops, &dd);
++      else
++              of_ti_dpll_setup(node, &omap3_dpll_ck_ops, &dd);
+ }
+ CLK_OF_DECLARE(ti_omap3_dpll_clock, "ti,omap3-dpll-clock",
+              of_ti_omap3_dpll_setup);
+--- a/drivers/clk/ti/dpll3xxx.c
++++ b/drivers/clk/ti/dpll3xxx.c
+@@ -838,3 +838,70 @@ int omap3_dpll4_set_rate_and_parent(stru
+       return omap3_noncore_dpll_set_rate_and_parent(hw, rate, parent_rate,
+                                                     index);
+ }
++
++/* Apply DM3730 errata sprz319 advisory 2.1. */
++static bool omap3_dpll5_apply_errata(struct clk_hw *hw,
++                                   unsigned long parent_rate)
++{
++      struct omap3_dpll5_settings {
++              unsigned int rate, m, n;
++      };
++
++      static const struct omap3_dpll5_settings precomputed[] = {
++              /*
++               * From DM3730 errata advisory 2.1, table 35 and 36.
++               * The N value is increased by 1 compared to the tables as the
++               * errata lists register values while last_rounded_field is the
++               * real divider value.
++               */
++              { 12000000,  80,  0 + 1 },
++              { 13000000, 443,  5 + 1 },
++              { 19200000,  50,  0 + 1 },
++              { 26000000, 443, 11 + 1 },
++              { 38400000,  25,  0 + 1 }
++      };
++
++      const struct omap3_dpll5_settings *d;
++      struct clk_hw_omap *clk = to_clk_hw_omap(hw);
++      struct dpll_data *dd;
++      unsigned int i;
++
++      for (i = 0; i < ARRAY_SIZE(precomputed); ++i) {
++              if (parent_rate == precomputed[i].rate)
++                      break;
++      }
++
++      if (i == ARRAY_SIZE(precomputed))
++              return false;
++
++      d = &precomputed[i];
++
++      /* Update the M, N and rounded rate values and program the DPLL. */
++      dd = clk->dpll_data;
++      dd->last_rounded_m = d->m;
++      dd->last_rounded_n = d->n;
++      dd->last_rounded_rate = div_u64((u64)parent_rate * d->m, d->n);
++      omap3_noncore_dpll_program(clk, 0);
++
++      return true;
++}
++
++/**
++ * omap3_dpll5_set_rate - set rate for omap3 dpll5
++ * @hw: clock to change
++ * @rate: target rate for clock
++ * @parent_rate: rate of the parent clock
++ *
++ * Set rate for the DPLL5 clock. Apply the sprz319 advisory 2.1 on OMAP36xx if
++ * the DPLL is used for USB host (detected through the requested rate).
++ */
++int omap3_dpll5_set_rate(struct clk_hw *hw, unsigned long rate,
++                       unsigned long parent_rate)
++{
++      if (rate == OMAP3_DPLL5_FREQ_FOR_USBHOST * 8) {
++              if (omap3_dpll5_apply_errata(hw, parent_rate))
++                      return 0;
++      }
++
++      return omap3_noncore_dpll_set_rate(hw, rate, parent_rate);
++}
index eed577548fd58433814e6d582d903a4b561e05d6..e158e8848eb6358f172093ea2719c33e53fc2c88 100644 (file)
@@ -25,3 +25,4 @@ alsa-hda-ignore-the-assoc-and-seq-when-comparing-pin-configurations.patch
 alsa-hda-fix-headset-mic-problem-on-a-dell-laptop.patch
 alsa-hda-gate-the-mic-jack-on-hp-z1-gen3-aio.patch
 alsa-hda-when-comparing-pin-configurations-ignore-assoc-in-addition-to-seq.patch
+clk-ti-omap36xx-work-around-sprz319-advisory-2.1.patch