aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/drivers/clk/clk-twl6040.c
diff options
context:
space:
mode:
authorTony Lindgren <tony@atomide.com>2019-02-11 14:59:07 -0800
committerStephen Boyd <sboyd@kernel.org>2019-02-21 14:11:23 -0800
commit5ae51d67aec95f6f9386aa8dd5db424964895575 (patch)
tree96982b4ac92ef812e521c7fbbedadccf5a968cbe /drivers/clk/clk-twl6040.c
parentMerge tag 'ti-clk-for-5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/kristo/linux into clk-ti (diff)
downloadwireguard-linux-5ae51d67aec95f6f9386aa8dd5db424964895575.tar.xz
wireguard-linux-5ae51d67aec95f6f9386aa8dd5db424964895575.zip
clk: clk-twl6040: Fix imprecise external abort for pdmclk
I noticed that modprobe clk-twl6040 can fail after a cold boot with: abe_cm:clk:0010:0: failed to enable ... Unhandled fault: imprecise external abort (0x1406) at 0xbe896b20 WARNING: CPU: 1 PID: 29 at drivers/clk/clk.c:828 clk_core_disable_lock+0x18/0x24 ... (clk_core_disable_lock) from [<c0123534>] (_disable_clocks+0x18/0x90) (_disable_clocks) from [<c0124040>] (_idle+0x17c/0x244) (_idle) from [<c0125ad4>] (omap_hwmod_idle+0x24/0x44) (omap_hwmod_idle) from [<c053a038>] (sysc_runtime_suspend+0x48/0x108) (sysc_runtime_suspend) from [<c06084c4>] (__rpm_callback+0x144/0x1d8) (__rpm_callback) from [<c0608578>] (rpm_callback+0x20/0x80) (rpm_callback) from [<c0607034>] (rpm_suspend+0x120/0x694) (rpm_suspend) from [<c0607a78>] (__pm_runtime_idle+0x60/0x84) (__pm_runtime_idle) from [<c053aaf0>] (sysc_probe+0x874/0xf2c) (sysc_probe) from [<c05fecd4>] (platform_drv_probe+0x48/0x98) After searching around for a similar issue, I came across an earlier fix that never got merged upstream in the Android tree for glass-omap-xrr02. There is patch "MFD: twl6040-codec: Implement PDMCLK cold temp errata" by Misael Lopez Cruz <misael.lopez@ti.com>. Based on my observations, this fix is also needed when cold booting devices, and not just for deeper idle modes. Since we now have a clock driver for pdmclk, let's fix the issue in twl6040_pdmclk_prepare(). Cc: Misael Lopez Cruz <misael.lopez@ti.com> Cc: Peter Ujfalusi <peter.ujfalusi@ti.com> Signed-off-by: Tony Lindgren <tony@atomide.com> Acked-by: Peter Ujfalusi <peter.ujfalusi@ti.com> Cc: <stable@vger.kernel.org> Signed-off-by: Stephen Boyd <sboyd@kernel.org>
Diffstat (limited to 'drivers/clk/clk-twl6040.c')
-rw-r--r--drivers/clk/clk-twl6040.c53
1 files changed, 51 insertions, 2 deletions
diff --git a/drivers/clk/clk-twl6040.c b/drivers/clk/clk-twl6040.c
index ea846f77750b..0cad5748bf0e 100644
--- a/drivers/clk/clk-twl6040.c
+++ b/drivers/clk/clk-twl6040.c
@@ -41,6 +41,43 @@ static int twl6040_pdmclk_is_prepared(struct clk_hw *hw)
return pdmclk->enabled;
}
+static int twl6040_pdmclk_reset_one_clock(struct twl6040_pdmclk *pdmclk,
+ unsigned int reg)
+{
+ const u8 reset_mask = TWL6040_HPLLRST; /* Same for HPPLL and LPPLL */
+ int ret;
+
+ ret = twl6040_set_bits(pdmclk->twl6040, reg, reset_mask);
+ if (ret < 0)
+ return ret;
+
+ ret = twl6040_clear_bits(pdmclk->twl6040, reg, reset_mask);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * TWL6040A2 Phoenix Audio IC erratum #6: "PDM Clock Generation Issue At
+ * Cold Temperature". This affects cold boot and deeper idle states it
+ * seems. The workaround consists of resetting HPPLL and LPPLL.
+ */
+static int twl6040_pdmclk_quirk_reset_clocks(struct twl6040_pdmclk *pdmclk)
+{
+ int ret;
+
+ ret = twl6040_pdmclk_reset_one_clock(pdmclk, TWL6040_REG_HPPLLCTL);
+ if (ret)
+ return ret;
+
+ ret = twl6040_pdmclk_reset_one_clock(pdmclk, TWL6040_REG_LPPLLCTL);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
static int twl6040_pdmclk_prepare(struct clk_hw *hw)
{
struct twl6040_pdmclk *pdmclk = container_of(hw, struct twl6040_pdmclk,
@@ -48,8 +85,20 @@ static int twl6040_pdmclk_prepare(struct clk_hw *hw)
int ret;
ret = twl6040_power(pdmclk->twl6040, 1);
- if (!ret)
- pdmclk->enabled = 1;
+ if (ret)
+ return ret;
+
+ ret = twl6040_pdmclk_quirk_reset_clocks(pdmclk);
+ if (ret)
+ goto out_err;
+
+ pdmclk->enabled = 1;
+
+ return 0;
+
+out_err:
+ dev_err(pdmclk->dev, "%s: error %i\n", __func__, ret);
+ twl6040_power(pdmclk->twl6040, 0);
return ret;
}