Merge tag 'sound-5.1-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai...
[sfrench/cifs-2.6.git] / drivers / clk / clk-twl6040.c
index ea846f77750b82db151ca8c46fa9c29ac1386458..0cad5748bf0ec0462d64580d02d4127538d1ecef 100644 (file)
@@ -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;
 }