2 * Clock driver for Palmas device.
4 * Copyright (c) 2013, NVIDIA Corporation.
5 * Copyright (c) 2013-2014 Texas Instruments, Inc.
7 * Author: Laxman Dewangan <ldewangan@nvidia.com>
8 * Peter Ujfalusi <peter.ujfalusi@ti.com>
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.
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.
20 #include <linux/clk.h>
21 #include <linux/clk-provider.h>
22 #include <linux/mfd/palmas.h>
23 #include <linux/module.h>
25 #include <linux/of_device.h>
26 #include <linux/platform_device.h>
27 #include <linux/slab.h>
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
33 struct palmas_clk32k_desc
{
35 unsigned int control_reg
;
36 unsigned int enable_mask
;
37 unsigned int sleep_mask
;
38 unsigned int sleep_reqstr_id
;
42 struct palmas_clock_info
{
45 struct palmas
*palmas
;
46 const struct palmas_clk32k_desc
*clk_desc
;
50 static inline struct palmas_clock_info
*to_palmas_clks_info(struct clk_hw
*hw
)
52 return container_of(hw
, struct palmas_clock_info
, hw
);
55 static unsigned long palmas_clks_recalc_rate(struct clk_hw
*hw
,
56 unsigned long parent_rate
)
61 static int palmas_clks_prepare(struct clk_hw
*hw
)
63 struct palmas_clock_info
*cinfo
= to_palmas_clks_info(hw
);
66 ret
= palmas_update_bits(cinfo
->palmas
, PALMAS_RESOURCE_BASE
,
67 cinfo
->clk_desc
->control_reg
,
68 cinfo
->clk_desc
->enable_mask
,
69 cinfo
->clk_desc
->enable_mask
);
71 dev_err(cinfo
->dev
, "Reg 0x%02x update failed, %d\n",
72 cinfo
->clk_desc
->control_reg
, ret
);
73 else if (cinfo
->clk_desc
->delay
)
74 udelay(cinfo
->clk_desc
->delay
);
79 static void palmas_clks_unprepare(struct clk_hw
*hw
)
81 struct palmas_clock_info
*cinfo
= to_palmas_clks_info(hw
);
85 * Clock can be disabled through external pin if it is externally
88 if (cinfo
->ext_control_pin
)
91 ret
= palmas_update_bits(cinfo
->palmas
, PALMAS_RESOURCE_BASE
,
92 cinfo
->clk_desc
->control_reg
,
93 cinfo
->clk_desc
->enable_mask
, 0);
95 dev_err(cinfo
->dev
, "Reg 0x%02x update failed, %d\n",
96 cinfo
->clk_desc
->control_reg
, ret
);
99 static int palmas_clks_is_prepared(struct clk_hw
*hw
)
101 struct palmas_clock_info
*cinfo
= to_palmas_clks_info(hw
);
105 if (cinfo
->ext_control_pin
)
108 ret
= palmas_read(cinfo
->palmas
, PALMAS_RESOURCE_BASE
,
109 cinfo
->clk_desc
->control_reg
, &val
);
111 dev_err(cinfo
->dev
, "Reg 0x%02x read failed, %d\n",
112 cinfo
->clk_desc
->control_reg
, ret
);
115 return !!(val
& cinfo
->clk_desc
->enable_mask
);
118 static struct clk_ops palmas_clks_ops
= {
119 .prepare
= palmas_clks_prepare
,
120 .unprepare
= palmas_clks_unprepare
,
121 .is_prepared
= palmas_clks_is_prepared
,
122 .recalc_rate
= palmas_clks_recalc_rate
,
125 struct palmas_clks_of_match_data
{
126 struct clk_init_data init
;
127 const struct palmas_clk32k_desc desc
;
130 static const struct palmas_clks_of_match_data palmas_of_clk32kg
= {
133 .ops
= &palmas_clks_ops
,
134 .flags
= CLK_IGNORE_UNUSED
,
137 .clk_name
= "clk32kg",
138 .control_reg
= PALMAS_CLK32KG_CTRL
,
139 .enable_mask
= PALMAS_CLK32KG_CTRL_MODE_ACTIVE
,
140 .sleep_mask
= PALMAS_CLK32KG_CTRL_MODE_SLEEP
,
141 .sleep_reqstr_id
= PALMAS_EXTERNAL_REQSTR_ID_CLK32KG
,
146 static const struct palmas_clks_of_match_data palmas_of_clk32kgaudio
= {
148 .name
= "clk32kgaudio",
149 .ops
= &palmas_clks_ops
,
150 .flags
= CLK_IGNORE_UNUSED
,
153 .clk_name
= "clk32kgaudio",
154 .control_reg
= PALMAS_CLK32KGAUDIO_CTRL
,
155 .enable_mask
= PALMAS_CLK32KG_CTRL_MODE_ACTIVE
,
156 .sleep_mask
= PALMAS_CLK32KG_CTRL_MODE_SLEEP
,
157 .sleep_reqstr_id
= PALMAS_EXTERNAL_REQSTR_ID_CLK32KGAUDIO
,
162 static const struct of_device_id palmas_clks_of_match
[] = {
164 .compatible
= "ti,palmas-clk32kg",
165 .data
= &palmas_of_clk32kg
,
168 .compatible
= "ti,palmas-clk32kgaudio",
169 .data
= &palmas_of_clk32kgaudio
,
173 MODULE_DEVICE_TABLE(of
, palmas_clks_of_match
);
175 static void palmas_clks_get_clk_data(struct platform_device
*pdev
,
176 struct palmas_clock_info
*cinfo
)
178 struct device_node
*node
= pdev
->dev
.of_node
;
182 ret
= of_property_read_u32(node
, "ti,external-sleep-control",
188 case PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE1
:
189 prop
= PALMAS_EXT_CONTROL_ENABLE1
;
191 case PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE2
:
192 prop
= PALMAS_EXT_CONTROL_ENABLE2
;
194 case PALMAS_CLOCK_DT_EXT_CONTROL_NSLEEP
:
195 prop
= PALMAS_EXT_CONTROL_NSLEEP
;
198 dev_warn(&pdev
->dev
, "%s: Invalid ext control option: %u\n",
203 cinfo
->ext_control_pin
= prop
;
206 static int palmas_clks_init_configure(struct palmas_clock_info
*cinfo
)
210 ret
= palmas_update_bits(cinfo
->palmas
, PALMAS_RESOURCE_BASE
,
211 cinfo
->clk_desc
->control_reg
,
212 cinfo
->clk_desc
->sleep_mask
, 0);
214 dev_err(cinfo
->dev
, "Reg 0x%02x update failed, %d\n",
215 cinfo
->clk_desc
->control_reg
, ret
);
219 if (cinfo
->ext_control_pin
) {
220 ret
= clk_prepare(cinfo
->hw
.clk
);
222 dev_err(cinfo
->dev
, "Clock prep failed, %d\n", ret
);
226 ret
= palmas_ext_control_req_config(cinfo
->palmas
,
227 cinfo
->clk_desc
->sleep_reqstr_id
,
228 cinfo
->ext_control_pin
, true);
230 dev_err(cinfo
->dev
, "Ext config for %s failed, %d\n",
231 cinfo
->clk_desc
->clk_name
, ret
);
232 clk_unprepare(cinfo
->hw
.clk
);
239 static int palmas_clks_probe(struct platform_device
*pdev
)
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
;
247 match_data
= of_device_get_match_data(&pdev
->dev
);
251 cinfo
= devm_kzalloc(&pdev
->dev
, sizeof(*cinfo
), GFP_KERNEL
);
255 palmas_clks_get_clk_data(pdev
, cinfo
);
256 platform_set_drvdata(pdev
, cinfo
);
258 cinfo
->dev
= &pdev
->dev
;
259 cinfo
->palmas
= palmas
;
261 cinfo
->clk_desc
= &match_data
->desc
;
262 cinfo
->hw
.init
= &match_data
->init
;
263 ret
= devm_clk_hw_register(&pdev
->dev
, &cinfo
->hw
);
265 dev_err(&pdev
->dev
, "Fail to register clock %s, %d\n",
266 match_data
->desc
.clk_name
, ret
);
270 ret
= palmas_clks_init_configure(cinfo
);
272 dev_err(&pdev
->dev
, "Clock config failed, %d\n", ret
);
276 ret
= of_clk_add_hw_provider(node
, of_clk_hw_simple_get
, &cinfo
->hw
);
278 dev_err(&pdev
->dev
, "Fail to add clock driver, %d\n", ret
);
282 static int palmas_clks_remove(struct platform_device
*pdev
)
284 of_clk_del_provider(pdev
->dev
.of_node
);
288 static struct platform_driver palmas_clks_driver
= {
290 .name
= "palmas-clk",
291 .of_match_table
= palmas_clks_of_match
,
293 .probe
= palmas_clks_probe
,
294 .remove
= palmas_clks_remove
,
297 module_platform_driver(palmas_clks_driver
);
299 MODULE_DESCRIPTION("Clock driver for Palmas Series Devices");
300 MODULE_ALIAS("platform:palmas-clk");
301 MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
302 MODULE_LICENSE("GPL v2");