1*2b27bdccSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f9f8c043SPeter Ujfalusi /*
3f9f8c043SPeter Ujfalusi * TWL6040 clock module driver for OMAP4 McPDM functional clock
4f9f8c043SPeter Ujfalusi *
5f9f8c043SPeter Ujfalusi * Copyright (C) 2012 Texas Instruments Inc.
6f9f8c043SPeter Ujfalusi * Peter Ujfalusi <peter.ujfalusi@ti.com>
7f9f8c043SPeter Ujfalusi */
8f9f8c043SPeter Ujfalusi
9f9f8c043SPeter Ujfalusi #include <linux/module.h>
10f9f8c043SPeter Ujfalusi #include <linux/slab.h>
11f9f8c043SPeter Ujfalusi #include <linux/platform_device.h>
12f9f8c043SPeter Ujfalusi #include <linux/mfd/twl6040.h>
13f9f8c043SPeter Ujfalusi #include <linux/clk-provider.h>
14f9f8c043SPeter Ujfalusi
157e37deb7SPeter Ujfalusi struct twl6040_pdmclk {
16f9f8c043SPeter Ujfalusi struct twl6040 *twl6040;
17f9f8c043SPeter Ujfalusi struct device *dev;
187e37deb7SPeter Ujfalusi struct clk_hw pdmclk_hw;
19f9f8c043SPeter Ujfalusi int enabled;
20f9f8c043SPeter Ujfalusi };
21f9f8c043SPeter Ujfalusi
twl6040_pdmclk_is_prepared(struct clk_hw * hw)227e37deb7SPeter Ujfalusi static int twl6040_pdmclk_is_prepared(struct clk_hw *hw)
23f9f8c043SPeter Ujfalusi {
247e37deb7SPeter Ujfalusi struct twl6040_pdmclk *pdmclk = container_of(hw, struct twl6040_pdmclk,
257e37deb7SPeter Ujfalusi pdmclk_hw);
267e37deb7SPeter Ujfalusi
277e37deb7SPeter Ujfalusi return pdmclk->enabled;
28f9f8c043SPeter Ujfalusi }
29f9f8c043SPeter Ujfalusi
twl6040_pdmclk_reset_one_clock(struct twl6040_pdmclk * pdmclk,unsigned int reg)305ae51d67STony Lindgren static int twl6040_pdmclk_reset_one_clock(struct twl6040_pdmclk *pdmclk,
315ae51d67STony Lindgren unsigned int reg)
325ae51d67STony Lindgren {
335ae51d67STony Lindgren const u8 reset_mask = TWL6040_HPLLRST; /* Same for HPPLL and LPPLL */
345ae51d67STony Lindgren int ret;
355ae51d67STony Lindgren
365ae51d67STony Lindgren ret = twl6040_set_bits(pdmclk->twl6040, reg, reset_mask);
375ae51d67STony Lindgren if (ret < 0)
385ae51d67STony Lindgren return ret;
395ae51d67STony Lindgren
405ae51d67STony Lindgren ret = twl6040_clear_bits(pdmclk->twl6040, reg, reset_mask);
415ae51d67STony Lindgren if (ret < 0)
425ae51d67STony Lindgren return ret;
435ae51d67STony Lindgren
445ae51d67STony Lindgren return 0;
455ae51d67STony Lindgren }
465ae51d67STony Lindgren
475ae51d67STony Lindgren /*
485ae51d67STony Lindgren * TWL6040A2 Phoenix Audio IC erratum #6: "PDM Clock Generation Issue At
495ae51d67STony Lindgren * Cold Temperature". This affects cold boot and deeper idle states it
505ae51d67STony Lindgren * seems. The workaround consists of resetting HPPLL and LPPLL.
515ae51d67STony Lindgren */
twl6040_pdmclk_quirk_reset_clocks(struct twl6040_pdmclk * pdmclk)525ae51d67STony Lindgren static int twl6040_pdmclk_quirk_reset_clocks(struct twl6040_pdmclk *pdmclk)
535ae51d67STony Lindgren {
545ae51d67STony Lindgren int ret;
555ae51d67STony Lindgren
565ae51d67STony Lindgren ret = twl6040_pdmclk_reset_one_clock(pdmclk, TWL6040_REG_HPPLLCTL);
575ae51d67STony Lindgren if (ret)
585ae51d67STony Lindgren return ret;
595ae51d67STony Lindgren
605ae51d67STony Lindgren ret = twl6040_pdmclk_reset_one_clock(pdmclk, TWL6040_REG_LPPLLCTL);
615ae51d67STony Lindgren if (ret)
625ae51d67STony Lindgren return ret;
635ae51d67STony Lindgren
645ae51d67STony Lindgren return 0;
655ae51d67STony Lindgren }
665ae51d67STony Lindgren
twl6040_pdmclk_prepare(struct clk_hw * hw)677e37deb7SPeter Ujfalusi static int twl6040_pdmclk_prepare(struct clk_hw *hw)
68f9f8c043SPeter Ujfalusi {
697e37deb7SPeter Ujfalusi struct twl6040_pdmclk *pdmclk = container_of(hw, struct twl6040_pdmclk,
707e37deb7SPeter Ujfalusi pdmclk_hw);
71f9f8c043SPeter Ujfalusi int ret;
72f9f8c043SPeter Ujfalusi
737e37deb7SPeter Ujfalusi ret = twl6040_power(pdmclk->twl6040, 1);
745ae51d67STony Lindgren if (ret)
755ae51d67STony Lindgren return ret;
765ae51d67STony Lindgren
775ae51d67STony Lindgren ret = twl6040_pdmclk_quirk_reset_clocks(pdmclk);
785ae51d67STony Lindgren if (ret)
795ae51d67STony Lindgren goto out_err;
805ae51d67STony Lindgren
817e37deb7SPeter Ujfalusi pdmclk->enabled = 1;
82f9f8c043SPeter Ujfalusi
835ae51d67STony Lindgren return 0;
845ae51d67STony Lindgren
855ae51d67STony Lindgren out_err:
865ae51d67STony Lindgren dev_err(pdmclk->dev, "%s: error %i\n", __func__, ret);
875ae51d67STony Lindgren twl6040_power(pdmclk->twl6040, 0);
885ae51d67STony Lindgren
89f9f8c043SPeter Ujfalusi return ret;
90f9f8c043SPeter Ujfalusi }
91f9f8c043SPeter Ujfalusi
twl6040_pdmclk_unprepare(struct clk_hw * hw)927e37deb7SPeter Ujfalusi static void twl6040_pdmclk_unprepare(struct clk_hw *hw)
93f9f8c043SPeter Ujfalusi {
947e37deb7SPeter Ujfalusi struct twl6040_pdmclk *pdmclk = container_of(hw, struct twl6040_pdmclk,
957e37deb7SPeter Ujfalusi pdmclk_hw);
96f9f8c043SPeter Ujfalusi int ret;
97f9f8c043SPeter Ujfalusi
987e37deb7SPeter Ujfalusi ret = twl6040_power(pdmclk->twl6040, 0);
99f9f8c043SPeter Ujfalusi if (!ret)
1007e37deb7SPeter Ujfalusi pdmclk->enabled = 0;
1017e37deb7SPeter Ujfalusi
102f9f8c043SPeter Ujfalusi }
103f9f8c043SPeter Ujfalusi
twl6040_pdmclk_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)1047e37deb7SPeter Ujfalusi static unsigned long twl6040_pdmclk_recalc_rate(struct clk_hw *hw,
1057e37deb7SPeter Ujfalusi unsigned long parent_rate)
1067e37deb7SPeter Ujfalusi {
1077e37deb7SPeter Ujfalusi struct twl6040_pdmclk *pdmclk = container_of(hw, struct twl6040_pdmclk,
1087e37deb7SPeter Ujfalusi pdmclk_hw);
1097e37deb7SPeter Ujfalusi
1107e37deb7SPeter Ujfalusi return twl6040_get_sysclk(pdmclk->twl6040);
1117e37deb7SPeter Ujfalusi }
1127e37deb7SPeter Ujfalusi
1137e37deb7SPeter Ujfalusi static const struct clk_ops twl6040_pdmclk_ops = {
1147e37deb7SPeter Ujfalusi .is_prepared = twl6040_pdmclk_is_prepared,
1157e37deb7SPeter Ujfalusi .prepare = twl6040_pdmclk_prepare,
1167e37deb7SPeter Ujfalusi .unprepare = twl6040_pdmclk_unprepare,
1177e37deb7SPeter Ujfalusi .recalc_rate = twl6040_pdmclk_recalc_rate,
118f9f8c043SPeter Ujfalusi };
119f9f8c043SPeter Ujfalusi
1200777066dSBhumika Goyal static const struct clk_init_data twl6040_pdmclk_init = {
1217e37deb7SPeter Ujfalusi .name = "pdmclk",
1227e37deb7SPeter Ujfalusi .ops = &twl6040_pdmclk_ops,
1237e37deb7SPeter Ujfalusi .flags = CLK_GET_RATE_NOCACHE,
124f9f8c043SPeter Ujfalusi };
125f9f8c043SPeter Ujfalusi
twl6040_pdmclk_probe(struct platform_device * pdev)1267e37deb7SPeter Ujfalusi static int twl6040_pdmclk_probe(struct platform_device *pdev)
127f9f8c043SPeter Ujfalusi {
128f9f8c043SPeter Ujfalusi struct twl6040 *twl6040 = dev_get_drvdata(pdev->dev.parent);
1297e37deb7SPeter Ujfalusi struct twl6040_pdmclk *clkdata;
130f5b3715eSStephen Boyd int ret;
131f9f8c043SPeter Ujfalusi
132f9f8c043SPeter Ujfalusi clkdata = devm_kzalloc(&pdev->dev, sizeof(*clkdata), GFP_KERNEL);
133f9f8c043SPeter Ujfalusi if (!clkdata)
134f9f8c043SPeter Ujfalusi return -ENOMEM;
135f9f8c043SPeter Ujfalusi
136f9f8c043SPeter Ujfalusi clkdata->dev = &pdev->dev;
137f9f8c043SPeter Ujfalusi clkdata->twl6040 = twl6040;
138f9f8c043SPeter Ujfalusi
1397e37deb7SPeter Ujfalusi clkdata->pdmclk_hw.init = &twl6040_pdmclk_init;
140f5b3715eSStephen Boyd ret = devm_clk_hw_register(&pdev->dev, &clkdata->pdmclk_hw);
141f5b3715eSStephen Boyd if (ret)
142f5b3715eSStephen Boyd return ret;
143f9f8c043SPeter Ujfalusi
144c0431037SJingoo Han platform_set_drvdata(pdev, clkdata);
145f9f8c043SPeter Ujfalusi
146654dea6eSMatti Vaittinen return devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_simple_get,
147f5b3715eSStephen Boyd &clkdata->pdmclk_hw);
148f9f8c043SPeter Ujfalusi }
149f9f8c043SPeter Ujfalusi
1507e37deb7SPeter Ujfalusi static struct platform_driver twl6040_pdmclk_driver = {
151f9f8c043SPeter Ujfalusi .driver = {
1527e37deb7SPeter Ujfalusi .name = "twl6040-pdmclk",
153f9f8c043SPeter Ujfalusi },
1547e37deb7SPeter Ujfalusi .probe = twl6040_pdmclk_probe,
155f9f8c043SPeter Ujfalusi };
156f9f8c043SPeter Ujfalusi
1577e37deb7SPeter Ujfalusi module_platform_driver(twl6040_pdmclk_driver);
158f9f8c043SPeter Ujfalusi
159f9f8c043SPeter Ujfalusi MODULE_DESCRIPTION("TWL6040 clock driver for McPDM functional clock");
160f9f8c043SPeter Ujfalusi MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
1617e37deb7SPeter Ujfalusi MODULE_ALIAS("platform:twl6040-pdmclk");
162f9f8c043SPeter Ujfalusi MODULE_LICENSE("GPL");
163