aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clk
diff options
context:
space:
mode:
authorPeter Ujfalusi <peter.ujfalusi@ti.com>2014-06-27 02:01:11 -0400
committerMike Turquette <mturquette@linaro.org>2014-07-02 00:37:57 -0400
commit942d1d674931250e81af2aa124549562a06a28c7 (patch)
tree81007728d139d3acd212ed5ba4e13881137b9886 /drivers/clk
parent5974b794cbcd5fac1bb605749c1ac445f5a2d07c (diff)
clk: Add driver for Palmas clk32kg and clk32kgaudio clocks
Palmas class of devices can provide 32K clock(s) to be used by other devices on the board. Depending on the actual device the provided clocks can be: CLK32K_KG and CLK32K_KGAUDIO or only one: CLK32K_KG (TPS659039 for example) Use separate compatible flags for the two 32K clock. A system which needs or have only one of the 32k clock from Palmas will need to add node(s) for each clock as separate section in the dts file. The two compatible property is: "ti,palmas-clk32kg" for clk32kg clock "ti,palmas-clk32kgaudio" for clk32kgaudio clock Apart from the register control of the clocks - which is done via the clock API there is a posibility to enable the external sleep control. In this way the clock can be enabled/disabled on demand by the user of the clock. See the documentation for more details. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com> Reviewed-by: Nishanth Menon <nm@ti.com> Signed-off-by: Mike Turquette <mturquette@linaro.org>
Diffstat (limited to 'drivers/clk')
-rw-r--r--drivers/clk/Kconfig7
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/clk-palmas.c307
3 files changed, 315 insertions, 0 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 9f9c5ae5359b..cfd3af7b2cbd 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -102,6 +102,13 @@ config COMMON_CLK_KEYSTONE
102 Supports clock drivers for Keystone based SOCs. These SOCs have local 102 Supports clock drivers for Keystone based SOCs. These SOCs have local
103 a power sleep control module that gate the clock to the IPs and PLLs. 103 a power sleep control module that gate the clock to the IPs and PLLs.
104 104
105config COMMON_CLK_PALMAS
106 tristate "Clock driver for TI Palmas devices"
107 depends on MFD_PALMAS
108 ---help---
109 This driver supports TI Palmas devices 32KHz output KG and KG_AUDIO
110 using common clock framework.
111
105source "drivers/clk/qcom/Kconfig" 112source "drivers/clk/qcom/Kconfig"
106 113
107endmenu 114endmenu
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 567f10259029..312742c10661 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
22obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o 22obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o
23obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o 23obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o
24obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o 24obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o
25obj-$(CONFIG_COMMON_CLK_PALMAS) += clk-palmas.o
25obj-$(CONFIG_CLK_PPC_CORENET) += clk-ppc-corenet.o 26obj-$(CONFIG_CLK_PPC_CORENET) += clk-ppc-corenet.o
26obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o 27obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o
27obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o 28obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o
diff --git a/drivers/clk/clk-palmas.c b/drivers/clk/clk-palmas.c
new file mode 100644
index 000000000000..781630e1372b
--- /dev/null
+++ b/drivers/clk/clk-palmas.c
@@ -0,0 +1,307 @@
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/clkdev.h>
22#include <linux/clk-provider.h>
23#include <linux/mfd/palmas.h>
24#include <linux/module.h>
25#include <linux/of.h>
26#include <linux/of_device.h>
27#include <linux/platform_device.h>
28#include <linux/slab.h>
29
30#define PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE1 1
31#define PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE2 2
32#define PALMAS_CLOCK_DT_EXT_CONTROL_NSLEEP 3
33
34struct palmas_clk32k_desc {
35 const char *clk_name;
36 unsigned int control_reg;
37 unsigned int enable_mask;
38 unsigned int sleep_mask;
39 unsigned int sleep_reqstr_id;
40 int delay;
41};
42
43struct palmas_clock_info {
44 struct device *dev;
45 struct clk *clk;
46 struct clk_hw hw;
47 struct palmas *palmas;
48 struct palmas_clk32k_desc *clk_desc;
49 int ext_control_pin;
50};
51
52static inline struct palmas_clock_info *to_palmas_clks_info(struct clk_hw *hw)
53{
54 return container_of(hw, struct palmas_clock_info, hw);
55}
56
57static unsigned long palmas_clks_recalc_rate(struct clk_hw *hw,
58 unsigned long parent_rate)
59{
60 return 32768;
61}
62
63static int palmas_clks_prepare(struct clk_hw *hw)
64{
65 struct palmas_clock_info *cinfo = to_palmas_clks_info(hw);
66 int ret;
67
68 ret = palmas_update_bits(cinfo->palmas, PALMAS_RESOURCE_BASE,
69 cinfo->clk_desc->control_reg,
70 cinfo->clk_desc->enable_mask,
71 cinfo->clk_desc->enable_mask);
72 if (ret < 0)
73 dev_err(cinfo->dev, "Reg 0x%02x update failed, %d\n",
74 cinfo->clk_desc->control_reg, ret);
75 else if (cinfo->clk_desc->delay)
76 udelay(cinfo->clk_desc->delay);
77
78 return ret;
79}
80
81static void palmas_clks_unprepare(struct clk_hw *hw)
82{
83 struct palmas_clock_info *cinfo = to_palmas_clks_info(hw);
84 int ret;
85
86 /*
87 * Clock can be disabled through external pin if it is externally
88 * controlled.
89 */
90 if (cinfo->ext_control_pin)
91 return;
92
93 ret = palmas_update_bits(cinfo->palmas, PALMAS_RESOURCE_BASE,
94 cinfo->clk_desc->control_reg,
95 cinfo->clk_desc->enable_mask, 0);
96 if (ret < 0)
97 dev_err(cinfo->dev, "Reg 0x%02x update failed, %d\n",
98 cinfo->clk_desc->control_reg, ret);
99}
100
101static int palmas_clks_is_prepared(struct clk_hw *hw)
102{
103 struct palmas_clock_info *cinfo = to_palmas_clks_info(hw);
104 int ret;
105 u32 val;
106
107 if (cinfo->ext_control_pin)
108 return 1;
109
110 ret = palmas_read(cinfo->palmas, PALMAS_RESOURCE_BASE,
111 cinfo->clk_desc->control_reg, &val);
112 if (ret < 0) {
113 dev_err(cinfo->dev, "Reg 0x%02x read failed, %d\n",
114 cinfo->clk_desc->control_reg, ret);
115 return ret;
116 }
117 return !!(val & cinfo->clk_desc->enable_mask);
118}
119
120static struct clk_ops palmas_clks_ops = {
121 .prepare = palmas_clks_prepare,
122 .unprepare = palmas_clks_unprepare,
123 .is_prepared = palmas_clks_is_prepared,
124 .recalc_rate = palmas_clks_recalc_rate,
125};
126
127struct palmas_clks_of_match_data {
128 struct clk_init_data init;
129 struct palmas_clk32k_desc desc;
130};
131
132static struct palmas_clks_of_match_data palmas_of_clk32kg = {
133 .init = {
134 .name = "clk32kg",
135 .ops = &palmas_clks_ops,
136 .flags = CLK_IS_ROOT | CLK_IGNORE_UNUSED,
137 },
138 .desc = {
139 .clk_name = "clk32kg",
140 .control_reg = PALMAS_CLK32KG_CTRL,
141 .enable_mask = PALMAS_CLK32KG_CTRL_MODE_ACTIVE,
142 .sleep_mask = PALMAS_CLK32KG_CTRL_MODE_SLEEP,
143 .sleep_reqstr_id = PALMAS_EXTERNAL_REQSTR_ID_CLK32KG,
144 .delay = 200,
145 },
146};
147
148static struct palmas_clks_of_match_data palmas_of_clk32kgaudio = {
149 .init = {
150 .name = "clk32kgaudio",
151 .ops = &palmas_clks_ops,
152 .flags = CLK_IS_ROOT | CLK_IGNORE_UNUSED,
153 },
154 .desc = {
155 .clk_name = "clk32kgaudio",
156 .control_reg = PALMAS_CLK32KGAUDIO_CTRL,
157 .enable_mask = PALMAS_CLK32KG_CTRL_MODE_ACTIVE,
158 .sleep_mask = PALMAS_CLK32KG_CTRL_MODE_SLEEP,
159 .sleep_reqstr_id = PALMAS_EXTERNAL_REQSTR_ID_CLK32KGAUDIO,
160 .delay = 200,
161 },
162};
163
164static struct of_device_id palmas_clks_of_match[] = {
165 {
166 .compatible = "ti,palmas-clk32kg",
167 .data = &palmas_of_clk32kg,
168 },
169 {
170 .compatible = "ti,palmas-clk32kgaudio",
171 .data = &palmas_of_clk32kgaudio,
172 },
173 { },
174};
175MODULE_DEVICE_TABLE(of, palmas_clks_of_match);
176
177static void palmas_clks_get_clk_data(struct platform_device *pdev,
178 struct palmas_clock_info *cinfo)
179{
180 struct device_node *node = pdev->dev.of_node;
181 unsigned int prop;
182 int ret;
183
184 ret = of_property_read_u32(node, "ti,external-sleep-control",
185 &prop);
186 if (ret)
187 return;
188
189 switch (prop) {
190 case PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE1:
191 prop = PALMAS_EXT_CONTROL_ENABLE1;
192 break;
193 case PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE2:
194 prop = PALMAS_EXT_CONTROL_ENABLE2;
195 break;
196 case PALMAS_CLOCK_DT_EXT_CONTROL_NSLEEP:
197 prop = PALMAS_EXT_CONTROL_NSLEEP;
198 break;
199 default:
200 dev_warn(&pdev->dev, "%s: Invalid ext control option: %u\n",
201 node->name, prop);
202 prop = 0;
203 break;
204 }
205 cinfo->ext_control_pin = prop;
206}
207
208static int palmas_clks_init_configure(struct palmas_clock_info *cinfo)
209{
210 int ret;
211
212 ret = palmas_update_bits(cinfo->palmas, PALMAS_RESOURCE_BASE,
213 cinfo->clk_desc->control_reg,
214 cinfo->clk_desc->sleep_mask, 0);
215 if (ret < 0) {
216 dev_err(cinfo->dev, "Reg 0x%02x update failed, %d\n",
217 cinfo->clk_desc->control_reg, ret);
218 return ret;
219 }
220
221 if (cinfo->ext_control_pin) {
222 ret = clk_prepare(cinfo->clk);
223 if (ret < 0) {
224 dev_err(cinfo->dev, "Clock prep failed, %d\n", ret);
225 return ret;
226 }
227
228 ret = palmas_ext_control_req_config(cinfo->palmas,
229 cinfo->clk_desc->sleep_reqstr_id,
230 cinfo->ext_control_pin, true);
231 if (ret < 0) {
232 dev_err(cinfo->dev, "Ext config for %s failed, %d\n",
233 cinfo->clk_desc->clk_name, ret);
234 return ret;
235 }
236 }
237
238 return ret;
239}
240static int palmas_clks_probe(struct platform_device *pdev)
241{
242 struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
243 struct device_node *node = pdev->dev.of_node;
244 struct palmas_clks_of_match_data *match_data;
245 const struct of_device_id *match;
246 struct palmas_clock_info *cinfo;
247 struct clk *clk;
248 int ret;
249
250 match = of_match_device(palmas_clks_of_match, &pdev->dev);
251 match_data = (struct palmas_clks_of_match_data *)match->data;
252
253 cinfo = devm_kzalloc(&pdev->dev, sizeof(*cinfo), GFP_KERNEL);
254 if (!cinfo)
255 return -ENOMEM;
256
257 palmas_clks_get_clk_data(pdev, cinfo);
258 platform_set_drvdata(pdev, cinfo);
259
260 cinfo->dev = &pdev->dev;
261 cinfo->palmas = palmas;
262
263 cinfo->clk_desc = &match_data->desc;
264 cinfo->hw.init = &match_data->init;
265 clk = devm_clk_register(&pdev->dev, &cinfo->hw);
266 if (IS_ERR(clk)) {
267 ret = PTR_ERR(clk);
268 dev_err(&pdev->dev, "Fail to register clock %s, %d\n",
269 match_data->desc.clk_name, ret);
270 return ret;
271 }
272
273 cinfo->clk = clk;
274 ret = palmas_clks_init_configure(cinfo);
275 if (ret < 0) {
276 dev_err(&pdev->dev, "Clock config failed, %d\n", ret);
277 return ret;
278 }
279
280 ret = of_clk_add_provider(node, of_clk_src_simple_get, cinfo->clk);
281 if (ret < 0)
282 dev_err(&pdev->dev, "Fail to add clock driver, %d\n", ret);
283 return ret;
284}
285
286static int palmas_clks_remove(struct platform_device *pdev)
287{
288 of_clk_del_provider(pdev->dev.of_node);
289 return 0;
290}
291
292static struct platform_driver palmas_clks_driver = {
293 .driver = {
294 .name = "palmas-clk",
295 .owner = THIS_MODULE,
296 .of_match_table = palmas_clks_of_match,
297 },
298 .probe = palmas_clks_probe,
299 .remove = palmas_clks_remove,
300};
301
302module_platform_driver(palmas_clks_driver);
303
304MODULE_DESCRIPTION("Clock driver for Palmas Series Devices");
305MODULE_ALIAS("platform:palmas-clk");
306MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
307MODULE_LICENSE("GPL v2");