clk-twl6040.c (3822B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3* TWL6040 clock module driver for OMAP4 McPDM functional clock 4* 5* Copyright (C) 2012 Texas Instruments Inc. 6* Peter Ujfalusi <peter.ujfalusi@ti.com> 7*/ 8 9#include <linux/module.h> 10#include <linux/slab.h> 11#include <linux/platform_device.h> 12#include <linux/mfd/twl6040.h> 13#include <linux/clk-provider.h> 14 15struct twl6040_pdmclk { 16 struct twl6040 *twl6040; 17 struct device *dev; 18 struct clk_hw pdmclk_hw; 19 int enabled; 20}; 21 22static int twl6040_pdmclk_is_prepared(struct clk_hw *hw) 23{ 24 struct twl6040_pdmclk *pdmclk = container_of(hw, struct twl6040_pdmclk, 25 pdmclk_hw); 26 27 return pdmclk->enabled; 28} 29 30static int twl6040_pdmclk_reset_one_clock(struct twl6040_pdmclk *pdmclk, 31 unsigned int reg) 32{ 33 const u8 reset_mask = TWL6040_HPLLRST; /* Same for HPPLL and LPPLL */ 34 int ret; 35 36 ret = twl6040_set_bits(pdmclk->twl6040, reg, reset_mask); 37 if (ret < 0) 38 return ret; 39 40 ret = twl6040_clear_bits(pdmclk->twl6040, reg, reset_mask); 41 if (ret < 0) 42 return ret; 43 44 return 0; 45} 46 47/* 48 * TWL6040A2 Phoenix Audio IC erratum #6: "PDM Clock Generation Issue At 49 * Cold Temperature". This affects cold boot and deeper idle states it 50 * seems. The workaround consists of resetting HPPLL and LPPLL. 51 */ 52static int twl6040_pdmclk_quirk_reset_clocks(struct twl6040_pdmclk *pdmclk) 53{ 54 int ret; 55 56 ret = twl6040_pdmclk_reset_one_clock(pdmclk, TWL6040_REG_HPPLLCTL); 57 if (ret) 58 return ret; 59 60 ret = twl6040_pdmclk_reset_one_clock(pdmclk, TWL6040_REG_LPPLLCTL); 61 if (ret) 62 return ret; 63 64 return 0; 65} 66 67static int twl6040_pdmclk_prepare(struct clk_hw *hw) 68{ 69 struct twl6040_pdmclk *pdmclk = container_of(hw, struct twl6040_pdmclk, 70 pdmclk_hw); 71 int ret; 72 73 ret = twl6040_power(pdmclk->twl6040, 1); 74 if (ret) 75 return ret; 76 77 ret = twl6040_pdmclk_quirk_reset_clocks(pdmclk); 78 if (ret) 79 goto out_err; 80 81 pdmclk->enabled = 1; 82 83 return 0; 84 85out_err: 86 dev_err(pdmclk->dev, "%s: error %i\n", __func__, ret); 87 twl6040_power(pdmclk->twl6040, 0); 88 89 return ret; 90} 91 92static void twl6040_pdmclk_unprepare(struct clk_hw *hw) 93{ 94 struct twl6040_pdmclk *pdmclk = container_of(hw, struct twl6040_pdmclk, 95 pdmclk_hw); 96 int ret; 97 98 ret = twl6040_power(pdmclk->twl6040, 0); 99 if (!ret) 100 pdmclk->enabled = 0; 101 102} 103 104static unsigned long twl6040_pdmclk_recalc_rate(struct clk_hw *hw, 105 unsigned long parent_rate) 106{ 107 struct twl6040_pdmclk *pdmclk = container_of(hw, struct twl6040_pdmclk, 108 pdmclk_hw); 109 110 return twl6040_get_sysclk(pdmclk->twl6040); 111} 112 113static const struct clk_ops twl6040_pdmclk_ops = { 114 .is_prepared = twl6040_pdmclk_is_prepared, 115 .prepare = twl6040_pdmclk_prepare, 116 .unprepare = twl6040_pdmclk_unprepare, 117 .recalc_rate = twl6040_pdmclk_recalc_rate, 118}; 119 120static const struct clk_init_data twl6040_pdmclk_init = { 121 .name = "pdmclk", 122 .ops = &twl6040_pdmclk_ops, 123 .flags = CLK_GET_RATE_NOCACHE, 124}; 125 126static int twl6040_pdmclk_probe(struct platform_device *pdev) 127{ 128 struct twl6040 *twl6040 = dev_get_drvdata(pdev->dev.parent); 129 struct twl6040_pdmclk *clkdata; 130 int ret; 131 132 clkdata = devm_kzalloc(&pdev->dev, sizeof(*clkdata), GFP_KERNEL); 133 if (!clkdata) 134 return -ENOMEM; 135 136 clkdata->dev = &pdev->dev; 137 clkdata->twl6040 = twl6040; 138 139 clkdata->pdmclk_hw.init = &twl6040_pdmclk_init; 140 ret = devm_clk_hw_register(&pdev->dev, &clkdata->pdmclk_hw); 141 if (ret) 142 return ret; 143 144 platform_set_drvdata(pdev, clkdata); 145 146 return devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_simple_get, 147 &clkdata->pdmclk_hw); 148} 149 150static struct platform_driver twl6040_pdmclk_driver = { 151 .driver = { 152 .name = "twl6040-pdmclk", 153 }, 154 .probe = twl6040_pdmclk_probe, 155}; 156 157module_platform_driver(twl6040_pdmclk_driver); 158 159MODULE_DESCRIPTION("TWL6040 clock driver for McPDM functional clock"); 160MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>"); 161MODULE_ALIAS("platform:twl6040-pdmclk"); 162MODULE_LICENSE("GPL");