]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blob - drivers/clk/clk-palmas.c
Merge tag 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dledford/rdma
[mirror_ubuntu-zesty-kernel.git] / drivers / clk / clk-palmas.c
1 /*
2 * Clock driver for Palmas device.
3 *
4 * Copyright (c) 2013, NVIDIA Corporation.
5 * Copyright (c) 2013-2014 Texas Instruments, Inc.
6 *
7 * Author: Laxman Dewangan <ldewangan@nvidia.com>
8 * Peter Ujfalusi <peter.ujfalusi@ti.com>
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License as
12 * published by the Free Software Foundation version 2.
13 *
14 * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
15 * whether express or implied; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 */
19
20 #include <linux/clk.h>
21 #include <linux/clk-provider.h>
22 #include <linux/mfd/palmas.h>
23 #include <linux/module.h>
24 #include <linux/of.h>
25 #include <linux/of_device.h>
26 #include <linux/platform_device.h>
27 #include <linux/slab.h>
28
29 #define PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE1 1
30 #define PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE2 2
31 #define PALMAS_CLOCK_DT_EXT_CONTROL_NSLEEP 3
32
33 struct palmas_clk32k_desc {
34 const char *clk_name;
35 unsigned int control_reg;
36 unsigned int enable_mask;
37 unsigned int sleep_mask;
38 unsigned int sleep_reqstr_id;
39 int delay;
40 };
41
42 struct palmas_clock_info {
43 struct device *dev;
44 struct clk *clk;
45 struct clk_hw hw;
46 struct palmas *palmas;
47 const struct palmas_clk32k_desc *clk_desc;
48 int ext_control_pin;
49 };
50
51 static inline struct palmas_clock_info *to_palmas_clks_info(struct clk_hw *hw)
52 {
53 return container_of(hw, struct palmas_clock_info, hw);
54 }
55
56 static unsigned long palmas_clks_recalc_rate(struct clk_hw *hw,
57 unsigned long parent_rate)
58 {
59 return 32768;
60 }
61
62 static int palmas_clks_prepare(struct clk_hw *hw)
63 {
64 struct palmas_clock_info *cinfo = to_palmas_clks_info(hw);
65 int ret;
66
67 ret = palmas_update_bits(cinfo->palmas, PALMAS_RESOURCE_BASE,
68 cinfo->clk_desc->control_reg,
69 cinfo->clk_desc->enable_mask,
70 cinfo->clk_desc->enable_mask);
71 if (ret < 0)
72 dev_err(cinfo->dev, "Reg 0x%02x update failed, %d\n",
73 cinfo->clk_desc->control_reg, ret);
74 else if (cinfo->clk_desc->delay)
75 udelay(cinfo->clk_desc->delay);
76
77 return ret;
78 }
79
80 static void palmas_clks_unprepare(struct clk_hw *hw)
81 {
82 struct palmas_clock_info *cinfo = to_palmas_clks_info(hw);
83 int ret;
84
85 /*
86 * Clock can be disabled through external pin if it is externally
87 * controlled.
88 */
89 if (cinfo->ext_control_pin)
90 return;
91
92 ret = palmas_update_bits(cinfo->palmas, PALMAS_RESOURCE_BASE,
93 cinfo->clk_desc->control_reg,
94 cinfo->clk_desc->enable_mask, 0);
95 if (ret < 0)
96 dev_err(cinfo->dev, "Reg 0x%02x update failed, %d\n",
97 cinfo->clk_desc->control_reg, ret);
98 }
99
100 static int palmas_clks_is_prepared(struct clk_hw *hw)
101 {
102 struct palmas_clock_info *cinfo = to_palmas_clks_info(hw);
103 int ret;
104 u32 val;
105
106 if (cinfo->ext_control_pin)
107 return 1;
108
109 ret = palmas_read(cinfo->palmas, PALMAS_RESOURCE_BASE,
110 cinfo->clk_desc->control_reg, &val);
111 if (ret < 0) {
112 dev_err(cinfo->dev, "Reg 0x%02x read failed, %d\n",
113 cinfo->clk_desc->control_reg, ret);
114 return ret;
115 }
116 return !!(val & cinfo->clk_desc->enable_mask);
117 }
118
119 static struct clk_ops palmas_clks_ops = {
120 .prepare = palmas_clks_prepare,
121 .unprepare = palmas_clks_unprepare,
122 .is_prepared = palmas_clks_is_prepared,
123 .recalc_rate = palmas_clks_recalc_rate,
124 };
125
126 struct palmas_clks_of_match_data {
127 struct clk_init_data init;
128 const struct palmas_clk32k_desc desc;
129 };
130
131 static const struct palmas_clks_of_match_data palmas_of_clk32kg = {
132 .init = {
133 .name = "clk32kg",
134 .ops = &palmas_clks_ops,
135 .flags = CLK_IGNORE_UNUSED,
136 },
137 .desc = {
138 .clk_name = "clk32kg",
139 .control_reg = PALMAS_CLK32KG_CTRL,
140 .enable_mask = PALMAS_CLK32KG_CTRL_MODE_ACTIVE,
141 .sleep_mask = PALMAS_CLK32KG_CTRL_MODE_SLEEP,
142 .sleep_reqstr_id = PALMAS_EXTERNAL_REQSTR_ID_CLK32KG,
143 .delay = 200,
144 },
145 };
146
147 static const struct palmas_clks_of_match_data palmas_of_clk32kgaudio = {
148 .init = {
149 .name = "clk32kgaudio",
150 .ops = &palmas_clks_ops,
151 .flags = CLK_IGNORE_UNUSED,
152 },
153 .desc = {
154 .clk_name = "clk32kgaudio",
155 .control_reg = PALMAS_CLK32KGAUDIO_CTRL,
156 .enable_mask = PALMAS_CLK32KG_CTRL_MODE_ACTIVE,
157 .sleep_mask = PALMAS_CLK32KG_CTRL_MODE_SLEEP,
158 .sleep_reqstr_id = PALMAS_EXTERNAL_REQSTR_ID_CLK32KGAUDIO,
159 .delay = 200,
160 },
161 };
162
163 static const struct of_device_id palmas_clks_of_match[] = {
164 {
165 .compatible = "ti,palmas-clk32kg",
166 .data = &palmas_of_clk32kg,
167 },
168 {
169 .compatible = "ti,palmas-clk32kgaudio",
170 .data = &palmas_of_clk32kgaudio,
171 },
172 { },
173 };
174 MODULE_DEVICE_TABLE(of, palmas_clks_of_match);
175
176 static void palmas_clks_get_clk_data(struct platform_device *pdev,
177 struct palmas_clock_info *cinfo)
178 {
179 struct device_node *node = pdev->dev.of_node;
180 unsigned int prop;
181 int ret;
182
183 ret = of_property_read_u32(node, "ti,external-sleep-control",
184 &prop);
185 if (ret)
186 return;
187
188 switch (prop) {
189 case PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE1:
190 prop = PALMAS_EXT_CONTROL_ENABLE1;
191 break;
192 case PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE2:
193 prop = PALMAS_EXT_CONTROL_ENABLE2;
194 break;
195 case PALMAS_CLOCK_DT_EXT_CONTROL_NSLEEP:
196 prop = PALMAS_EXT_CONTROL_NSLEEP;
197 break;
198 default:
199 dev_warn(&pdev->dev, "%s: Invalid ext control option: %u\n",
200 node->name, prop);
201 prop = 0;
202 break;
203 }
204 cinfo->ext_control_pin = prop;
205 }
206
207 static int palmas_clks_init_configure(struct palmas_clock_info *cinfo)
208 {
209 int ret;
210
211 ret = palmas_update_bits(cinfo->palmas, PALMAS_RESOURCE_BASE,
212 cinfo->clk_desc->control_reg,
213 cinfo->clk_desc->sleep_mask, 0);
214 if (ret < 0) {
215 dev_err(cinfo->dev, "Reg 0x%02x update failed, %d\n",
216 cinfo->clk_desc->control_reg, ret);
217 return ret;
218 }
219
220 if (cinfo->ext_control_pin) {
221 ret = clk_prepare(cinfo->clk);
222 if (ret < 0) {
223 dev_err(cinfo->dev, "Clock prep failed, %d\n", ret);
224 return ret;
225 }
226
227 ret = palmas_ext_control_req_config(cinfo->palmas,
228 cinfo->clk_desc->sleep_reqstr_id,
229 cinfo->ext_control_pin, true);
230 if (ret < 0) {
231 dev_err(cinfo->dev, "Ext config for %s failed, %d\n",
232 cinfo->clk_desc->clk_name, ret);
233 return ret;
234 }
235 }
236
237 return ret;
238 }
239 static int palmas_clks_probe(struct platform_device *pdev)
240 {
241 struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
242 struct device_node *node = pdev->dev.of_node;
243 const struct palmas_clks_of_match_data *match_data;
244 struct palmas_clock_info *cinfo;
245 struct clk *clk;
246 int ret;
247
248 match_data = of_device_get_match_data(&pdev->dev);
249 if (!match_data)
250 return 1;
251
252 cinfo = devm_kzalloc(&pdev->dev, sizeof(*cinfo), GFP_KERNEL);
253 if (!cinfo)
254 return -ENOMEM;
255
256 palmas_clks_get_clk_data(pdev, cinfo);
257 platform_set_drvdata(pdev, cinfo);
258
259 cinfo->dev = &pdev->dev;
260 cinfo->palmas = palmas;
261
262 cinfo->clk_desc = &match_data->desc;
263 cinfo->hw.init = &match_data->init;
264 clk = devm_clk_register(&pdev->dev, &cinfo->hw);
265 if (IS_ERR(clk)) {
266 ret = PTR_ERR(clk);
267 dev_err(&pdev->dev, "Fail to register clock %s, %d\n",
268 match_data->desc.clk_name, ret);
269 return ret;
270 }
271
272 cinfo->clk = clk;
273 ret = palmas_clks_init_configure(cinfo);
274 if (ret < 0) {
275 dev_err(&pdev->dev, "Clock config failed, %d\n", ret);
276 return ret;
277 }
278
279 ret = of_clk_add_provider(node, of_clk_src_simple_get, cinfo->clk);
280 if (ret < 0)
281 dev_err(&pdev->dev, "Fail to add clock driver, %d\n", ret);
282 return ret;
283 }
284
285 static int palmas_clks_remove(struct platform_device *pdev)
286 {
287 of_clk_del_provider(pdev->dev.of_node);
288 return 0;
289 }
290
291 static struct platform_driver palmas_clks_driver = {
292 .driver = {
293 .name = "palmas-clk",
294 .of_match_table = palmas_clks_of_match,
295 },
296 .probe = palmas_clks_probe,
297 .remove = palmas_clks_remove,
298 };
299
300 module_platform_driver(palmas_clks_driver);
301
302 MODULE_DESCRIPTION("Clock driver for Palmas Series Devices");
303 MODULE_ALIAS("platform:palmas-clk");
304 MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
305 MODULE_LICENSE("GPL v2");