Merge tag 'trace-printf-v6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/trace...
[drm/drm-misc.git] / drivers / clk / clk-twl.c
blob20bc3bf8fd62d3f92716c09ca4fa81d181bfe4e3
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Clock driver for twl device.
5 * inspired by the driver for the Palmas device
6 */
8 #include <linux/clk-provider.h>
9 #include <linux/mfd/twl.h>
10 #include <linux/module.h>
11 #include <linux/platform_device.h>
12 #include <linux/slab.h>
14 #define VREG_STATE 2
15 #define VREG_GRP 0
16 #define TWL6030_CFG_STATE_OFF 0x00
17 #define TWL6030_CFG_STATE_ON 0x01
18 #define TWL6030_CFG_STATE_MASK 0x03
19 #define TWL6030_CFG_STATE_GRP_SHIFT 5
20 #define TWL6030_CFG_STATE_APP_SHIFT 2
21 #define TWL6030_CFG_STATE_APP_MASK (0x03 << TWL6030_CFG_STATE_APP_SHIFT)
22 #define TWL6030_CFG_STATE_APP(v) (((v) & TWL6030_CFG_STATE_APP_MASK) >>\
23 TWL6030_CFG_STATE_APP_SHIFT)
24 #define P1_GRP BIT(0) /* processor power group */
25 #define P2_GRP BIT(1)
26 #define P3_GRP BIT(2)
27 #define ALL_GRP (P1_GRP | P2_GRP | P3_GRP)
29 enum twl_type {
30 TWL_TYPE_6030,
31 TWL_TYPE_6032,
34 struct twl_clock_info {
35 struct device *dev;
36 enum twl_type type;
37 u8 base;
38 struct clk_hw hw;
41 static inline int
42 twlclk_read(struct twl_clock_info *info, unsigned int slave_subgp,
43 unsigned int offset)
45 u8 value;
46 int status;
48 status = twl_i2c_read_u8(slave_subgp, &value,
49 info->base + offset);
50 return (status < 0) ? status : value;
53 static inline int
54 twlclk_write(struct twl_clock_info *info, unsigned int slave_subgp,
55 unsigned int offset, u8 value)
57 return twl_i2c_write_u8(slave_subgp, value,
58 info->base + offset);
61 static inline struct twl_clock_info *to_twl_clks_info(struct clk_hw *hw)
63 return container_of(hw, struct twl_clock_info, hw);
66 static unsigned long twl_clks_recalc_rate(struct clk_hw *hw,
67 unsigned long parent_rate)
69 return 32768;
72 static int twl6032_clks_prepare(struct clk_hw *hw)
74 struct twl_clock_info *cinfo = to_twl_clks_info(hw);
76 if (cinfo->type == TWL_TYPE_6030) {
77 int grp;
79 grp = twlclk_read(cinfo, TWL_MODULE_PM_RECEIVER, VREG_GRP);
80 if (grp < 0)
81 return grp;
83 return twlclk_write(cinfo, TWL_MODULE_PM_RECEIVER, VREG_STATE,
84 grp << TWL6030_CFG_STATE_GRP_SHIFT |
85 TWL6030_CFG_STATE_ON);
88 return twlclk_write(cinfo, TWL_MODULE_PM_RECEIVER, VREG_STATE,
89 TWL6030_CFG_STATE_ON);
92 static void twl6032_clks_unprepare(struct clk_hw *hw)
94 struct twl_clock_info *cinfo = to_twl_clks_info(hw);
95 int ret;
97 if (cinfo->type == TWL_TYPE_6030)
98 ret = twlclk_write(cinfo, TWL_MODULE_PM_RECEIVER, VREG_STATE,
99 ALL_GRP << TWL6030_CFG_STATE_GRP_SHIFT |
100 TWL6030_CFG_STATE_OFF);
101 else
102 ret = twlclk_write(cinfo, TWL_MODULE_PM_RECEIVER, VREG_STATE,
103 TWL6030_CFG_STATE_OFF);
105 if (ret < 0)
106 dev_err(cinfo->dev, "clk unprepare failed\n");
109 static const struct clk_ops twl6032_clks_ops = {
110 .prepare = twl6032_clks_prepare,
111 .unprepare = twl6032_clks_unprepare,
112 .recalc_rate = twl_clks_recalc_rate,
115 struct twl_clks_data {
116 struct clk_init_data init;
117 u8 base;
120 static const struct twl_clks_data twl6032_clks[] = {
122 .init = {
123 .name = "clk32kg",
124 .ops = &twl6032_clks_ops,
125 .flags = CLK_IGNORE_UNUSED,
127 .base = 0x8C,
130 .init = {
131 .name = "clk32kaudio",
132 .ops = &twl6032_clks_ops,
133 .flags = CLK_IGNORE_UNUSED,
135 .base = 0x8F,
138 /* sentinel */
142 static int twl_clks_probe(struct platform_device *pdev)
144 struct clk_hw_onecell_data *clk_data;
145 const struct twl_clks_data *hw_data;
147 struct twl_clock_info *cinfo;
148 int ret;
149 int i;
150 int count;
152 hw_data = twl6032_clks;
153 for (count = 0; hw_data[count].init.name; count++)
156 clk_data = devm_kzalloc(&pdev->dev,
157 struct_size(clk_data, hws, count),
158 GFP_KERNEL);
159 if (!clk_data)
160 return -ENOMEM;
162 clk_data->num = count;
163 cinfo = devm_kcalloc(&pdev->dev, count, sizeof(*cinfo), GFP_KERNEL);
164 if (!cinfo)
165 return -ENOMEM;
167 for (i = 0; i < count; i++) {
168 cinfo[i].base = hw_data[i].base;
169 cinfo[i].dev = &pdev->dev;
170 cinfo[i].type = platform_get_device_id(pdev)->driver_data;
171 cinfo[i].hw.init = &hw_data[i].init;
172 ret = devm_clk_hw_register(&pdev->dev, &cinfo[i].hw);
173 if (ret) {
174 return dev_err_probe(&pdev->dev, ret,
175 "Fail to register clock %s\n",
176 hw_data[i].init.name);
178 clk_data->hws[i] = &cinfo[i].hw;
181 ret = devm_of_clk_add_hw_provider(&pdev->dev,
182 of_clk_hw_onecell_get, clk_data);
183 if (ret < 0)
184 return dev_err_probe(&pdev->dev, ret,
185 "Fail to add clock driver\n");
187 return 0;
190 static const struct platform_device_id twl_clks_id[] = {
192 .name = "twl6030-clk",
193 .driver_data = TWL_TYPE_6030,
194 }, {
195 .name = "twl6032-clk",
196 .driver_data = TWL_TYPE_6032,
197 }, {
198 /* sentinel */
201 MODULE_DEVICE_TABLE(platform, twl_clks_id);
203 static struct platform_driver twl_clks_driver = {
204 .driver = {
205 .name = "twl-clk",
207 .probe = twl_clks_probe,
208 .id_table = twl_clks_id,
211 module_platform_driver(twl_clks_driver);
213 MODULE_DESCRIPTION("Clock driver for TWL Series Devices");
214 MODULE_LICENSE("GPL");