GNU Linux-libre 6.8.9-gnu
[releases.git] / drivers / clk / clk-twl.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Clock driver for twl device.
4  *
5  * inspired by the driver for the Palmas device
6  */
7
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>
13
14 #define VREG_STATE              2
15 #define TWL6030_CFG_STATE_OFF   0x00
16 #define TWL6030_CFG_STATE_ON    0x01
17 #define TWL6030_CFG_STATE_MASK  0x03
18
19 struct twl_clock_info {
20         struct device *dev;
21         u8 base;
22         struct clk_hw hw;
23 };
24
25 static inline int
26 twlclk_read(struct twl_clock_info *info, unsigned int slave_subgp,
27             unsigned int offset)
28 {
29         u8 value;
30         int status;
31
32         status = twl_i2c_read_u8(slave_subgp, &value,
33                                  info->base + offset);
34         return (status < 0) ? status : value;
35 }
36
37 static inline int
38 twlclk_write(struct twl_clock_info *info, unsigned int slave_subgp,
39              unsigned int offset, u8 value)
40 {
41         return twl_i2c_write_u8(slave_subgp, value,
42                                 info->base + offset);
43 }
44
45 static inline struct twl_clock_info *to_twl_clks_info(struct clk_hw *hw)
46 {
47         return container_of(hw, struct twl_clock_info, hw);
48 }
49
50 static unsigned long twl_clks_recalc_rate(struct clk_hw *hw,
51                                           unsigned long parent_rate)
52 {
53         return 32768;
54 }
55
56 static int twl6032_clks_prepare(struct clk_hw *hw)
57 {
58         struct twl_clock_info *cinfo = to_twl_clks_info(hw);
59         int ret;
60
61         ret = twlclk_write(cinfo, TWL_MODULE_PM_RECEIVER, VREG_STATE,
62                            TWL6030_CFG_STATE_ON);
63         if (ret < 0)
64                 dev_err(cinfo->dev, "clk prepare failed\n");
65
66         return ret;
67 }
68
69 static void twl6032_clks_unprepare(struct clk_hw *hw)
70 {
71         struct twl_clock_info *cinfo = to_twl_clks_info(hw);
72         int ret;
73
74         ret = twlclk_write(cinfo, TWL_MODULE_PM_RECEIVER, VREG_STATE,
75                            TWL6030_CFG_STATE_OFF);
76         if (ret < 0)
77                 dev_err(cinfo->dev, "clk unprepare failed\n");
78 }
79
80 static int twl6032_clks_is_prepared(struct clk_hw *hw)
81 {
82         struct twl_clock_info *cinfo = to_twl_clks_info(hw);
83         int val;
84
85         val = twlclk_read(cinfo, TWL_MODULE_PM_RECEIVER, VREG_STATE);
86         if (val < 0) {
87                 dev_err(cinfo->dev, "clk read failed\n");
88                 return val;
89         }
90
91         val &= TWL6030_CFG_STATE_MASK;
92
93         return val == TWL6030_CFG_STATE_ON;
94 }
95
96 static const struct clk_ops twl6032_clks_ops = {
97         .prepare        = twl6032_clks_prepare,
98         .unprepare      = twl6032_clks_unprepare,
99         .is_prepared    = twl6032_clks_is_prepared,
100         .recalc_rate    = twl_clks_recalc_rate,
101 };
102
103 struct twl_clks_data {
104         struct clk_init_data init;
105         u8 base;
106 };
107
108 static const struct twl_clks_data twl6032_clks[] = {
109         {
110                 .init = {
111                         .name = "clk32kg",
112                         .ops = &twl6032_clks_ops,
113                         .flags = CLK_IGNORE_UNUSED,
114                 },
115                 .base = 0x8C,
116         },
117         {
118                 .init = {
119                         .name = "clk32kaudio",
120                         .ops = &twl6032_clks_ops,
121                         .flags = CLK_IGNORE_UNUSED,
122                 },
123                 .base = 0x8F,
124         },
125         {
126                 /* sentinel */
127         }
128 };
129
130 static int twl_clks_probe(struct platform_device *pdev)
131 {
132         struct clk_hw_onecell_data *clk_data;
133         const struct twl_clks_data *hw_data;
134
135         struct twl_clock_info *cinfo;
136         int ret;
137         int i;
138         int count;
139
140         hw_data = twl6032_clks;
141         for (count = 0; hw_data[count].init.name; count++)
142                 ;
143
144         clk_data = devm_kzalloc(&pdev->dev,
145                                 struct_size(clk_data, hws, count),
146                                 GFP_KERNEL);
147         if (!clk_data)
148                 return -ENOMEM;
149
150         clk_data->num = count;
151         cinfo = devm_kcalloc(&pdev->dev, count, sizeof(*cinfo), GFP_KERNEL);
152         if (!cinfo)
153                 return -ENOMEM;
154
155         for (i = 0; i < count; i++) {
156                 cinfo[i].base = hw_data[i].base;
157                 cinfo[i].dev = &pdev->dev;
158                 cinfo[i].hw.init = &hw_data[i].init;
159                 ret = devm_clk_hw_register(&pdev->dev, &cinfo[i].hw);
160                 if (ret) {
161                         return dev_err_probe(&pdev->dev, ret,
162                                              "Fail to register clock %s\n",
163                                              hw_data[i].init.name);
164                 }
165                 clk_data->hws[i] = &cinfo[i].hw;
166         }
167
168         ret = devm_of_clk_add_hw_provider(&pdev->dev,
169                                           of_clk_hw_onecell_get, clk_data);
170         if (ret < 0)
171                 return dev_err_probe(&pdev->dev, ret,
172                                      "Fail to add clock driver\n");
173
174         return 0;
175 }
176
177 static const struct platform_device_id twl_clks_id[] = {
178         {
179                 .name = "twl6032-clk",
180         }, {
181                 /* sentinel */
182         }
183 };
184 MODULE_DEVICE_TABLE(platform, twl_clks_id);
185
186 static struct platform_driver twl_clks_driver = {
187         .driver = {
188                 .name = "twl-clk",
189         },
190         .probe = twl_clks_probe,
191         .id_table = twl_clks_id,
192 };
193
194 module_platform_driver(twl_clks_driver);
195
196 MODULE_DESCRIPTION("Clock driver for TWL Series Devices");
197 MODULE_LICENSE("GPL");