blob: 8328863cb0e04709684d66efc128221ec92b4b0f [file] [log] [blame]
Peter Ujfalusi942d1d62014-06-27 09:01:11 +03001/*
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>
Peter Ujfalusi942d1d62014-06-27 09:01:11 +030021#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
33struct 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
42struct palmas_clock_info {
43 struct device *dev;
44 struct clk *clk;
45 struct clk_hw hw;
46 struct palmas *palmas;
LABBE Corentin93fffbb2015-11-25 13:52:01 +010047 const struct palmas_clk32k_desc *clk_desc;
Peter Ujfalusi942d1d62014-06-27 09:01:11 +030048 int ext_control_pin;
49};
50
51static 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
56static unsigned long palmas_clks_recalc_rate(struct clk_hw *hw,
57 unsigned long parent_rate)
58{
59 return 32768;
60}
61
62static 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
80static 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
100static 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
119static 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
126struct palmas_clks_of_match_data {
127 struct clk_init_data init;
LABBE Corentin93fffbb2015-11-25 13:52:01 +0100128 const struct palmas_clk32k_desc desc;
Peter Ujfalusi942d1d62014-06-27 09:01:11 +0300129};
130
LABBE Corentin93fffbb2015-11-25 13:52:01 +0100131static const struct palmas_clks_of_match_data palmas_of_clk32kg = {
Peter Ujfalusi942d1d62014-06-27 09:01:11 +0300132 .init = {
133 .name = "clk32kg",
134 .ops = &palmas_clks_ops,
Stephen Boyd1fb47422016-03-01 11:00:17 -0800135 .flags = CLK_IGNORE_UNUSED,
Peter Ujfalusi942d1d62014-06-27 09:01:11 +0300136 },
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
LABBE Corentin93fffbb2015-11-25 13:52:01 +0100147static const struct palmas_clks_of_match_data palmas_of_clk32kgaudio = {
Peter Ujfalusi942d1d62014-06-27 09:01:11 +0300148 .init = {
149 .name = "clk32kgaudio",
150 .ops = &palmas_clks_ops,
Stephen Boyd1fb47422016-03-01 11:00:17 -0800151 .flags = CLK_IGNORE_UNUSED,
Peter Ujfalusi942d1d62014-06-27 09:01:11 +0300152 },
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
Fabian Frederickf3755732015-03-31 20:50:42 +0200163static const struct of_device_id palmas_clks_of_match[] = {
Peter Ujfalusi942d1d62014-06-27 09:01:11 +0300164 {
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};
174MODULE_DEVICE_TABLE(of, palmas_clks_of_match);
175
176static 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
207static 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}
239static 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;
LABBE Corentin93fffbb2015-11-25 13:52:01 +0100243 const struct palmas_clks_of_match_data *match_data;
Peter Ujfalusi942d1d62014-06-27 09:01:11 +0300244 struct palmas_clock_info *cinfo;
245 struct clk *clk;
246 int ret;
247
LABBE Corentin8d0a69d2015-11-25 13:52:02 +0100248 match_data = of_device_get_match_data(&pdev->dev);
249 if (!match_data)
250 return 1;
Peter Ujfalusi942d1d62014-06-27 09:01:11 +0300251
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
285static int palmas_clks_remove(struct platform_device *pdev)
286{
287 of_clk_del_provider(pdev->dev.of_node);
288 return 0;
289}
290
291static struct platform_driver palmas_clks_driver = {
292 .driver = {
293 .name = "palmas-clk",
Peter Ujfalusi942d1d62014-06-27 09:01:11 +0300294 .of_match_table = palmas_clks_of_match,
295 },
296 .probe = palmas_clks_probe,
297 .remove = palmas_clks_remove,
298};
299
300module_platform_driver(palmas_clks_driver);
301
302MODULE_DESCRIPTION("Clock driver for Palmas Series Devices");
303MODULE_ALIAS("platform:palmas-clk");
304MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
305MODULE_LICENSE("GPL v2");