1 // SPDX-License-Identifier: GPL-2.0
3 * Clock driver for Palmas device.
5 * Copyright (c) 2013, NVIDIA Corporation.
6 * Copyright (c) 2013-2014 Texas Instruments, Inc.
8 * Author: Laxman Dewangan <ldewangan@nvidia.com>
9 * Peter Ujfalusi <peter.ujfalusi@ti.com>
12 #include <linux/clk.h>
13 #include <linux/clk-provider.h>
14 #include <linux/mfd/palmas.h>
15 #include <linux/module.h>
17 #include <linux/of_device.h>
18 #include <linux/platform_device.h>
19 #include <linux/slab.h>
21 #define PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE1 1
22 #define PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE2 2
23 #define PALMAS_CLOCK_DT_EXT_CONTROL_NSLEEP 3
25 struct palmas_clk32k_desc
{
27 unsigned int control_reg
;
28 unsigned int enable_mask
;
29 unsigned int sleep_mask
;
30 unsigned int sleep_reqstr_id
;
34 struct palmas_clock_info
{
37 struct palmas
*palmas
;
38 const struct palmas_clk32k_desc
*clk_desc
;
42 static inline struct palmas_clock_info
*to_palmas_clks_info(struct clk_hw
*hw
)
44 return container_of(hw
, struct palmas_clock_info
, hw
);
47 static unsigned long palmas_clks_recalc_rate(struct clk_hw
*hw
,
48 unsigned long parent_rate
)
53 static int palmas_clks_prepare(struct clk_hw
*hw
)
55 struct palmas_clock_info
*cinfo
= to_palmas_clks_info(hw
);
58 ret
= palmas_update_bits(cinfo
->palmas
, PALMAS_RESOURCE_BASE
,
59 cinfo
->clk_desc
->control_reg
,
60 cinfo
->clk_desc
->enable_mask
,
61 cinfo
->clk_desc
->enable_mask
);
63 dev_err(cinfo
->dev
, "Reg 0x%02x update failed, %d\n",
64 cinfo
->clk_desc
->control_reg
, ret
);
65 else if (cinfo
->clk_desc
->delay
)
66 udelay(cinfo
->clk_desc
->delay
);
71 static void palmas_clks_unprepare(struct clk_hw
*hw
)
73 struct palmas_clock_info
*cinfo
= to_palmas_clks_info(hw
);
77 * Clock can be disabled through external pin if it is externally
80 if (cinfo
->ext_control_pin
)
83 ret
= palmas_update_bits(cinfo
->palmas
, PALMAS_RESOURCE_BASE
,
84 cinfo
->clk_desc
->control_reg
,
85 cinfo
->clk_desc
->enable_mask
, 0);
87 dev_err(cinfo
->dev
, "Reg 0x%02x update failed, %d\n",
88 cinfo
->clk_desc
->control_reg
, ret
);
91 static int palmas_clks_is_prepared(struct clk_hw
*hw
)
93 struct palmas_clock_info
*cinfo
= to_palmas_clks_info(hw
);
97 if (cinfo
->ext_control_pin
)
100 ret
= palmas_read(cinfo
->palmas
, PALMAS_RESOURCE_BASE
,
101 cinfo
->clk_desc
->control_reg
, &val
);
103 dev_err(cinfo
->dev
, "Reg 0x%02x read failed, %d\n",
104 cinfo
->clk_desc
->control_reg
, ret
);
107 return !!(val
& cinfo
->clk_desc
->enable_mask
);
110 static const struct clk_ops palmas_clks_ops
= {
111 .prepare
= palmas_clks_prepare
,
112 .unprepare
= palmas_clks_unprepare
,
113 .is_prepared
= palmas_clks_is_prepared
,
114 .recalc_rate
= palmas_clks_recalc_rate
,
117 struct palmas_clks_of_match_data
{
118 struct clk_init_data init
;
119 const struct palmas_clk32k_desc desc
;
122 static const struct palmas_clks_of_match_data palmas_of_clk32kg
= {
125 .ops
= &palmas_clks_ops
,
126 .flags
= CLK_IGNORE_UNUSED
,
129 .clk_name
= "clk32kg",
130 .control_reg
= PALMAS_CLK32KG_CTRL
,
131 .enable_mask
= PALMAS_CLK32KG_CTRL_MODE_ACTIVE
,
132 .sleep_mask
= PALMAS_CLK32KG_CTRL_MODE_SLEEP
,
133 .sleep_reqstr_id
= PALMAS_EXTERNAL_REQSTR_ID_CLK32KG
,
138 static const struct palmas_clks_of_match_data palmas_of_clk32kgaudio
= {
140 .name
= "clk32kgaudio",
141 .ops
= &palmas_clks_ops
,
142 .flags
= CLK_IGNORE_UNUSED
,
145 .clk_name
= "clk32kgaudio",
146 .control_reg
= PALMAS_CLK32KGAUDIO_CTRL
,
147 .enable_mask
= PALMAS_CLK32KG_CTRL_MODE_ACTIVE
,
148 .sleep_mask
= PALMAS_CLK32KG_CTRL_MODE_SLEEP
,
149 .sleep_reqstr_id
= PALMAS_EXTERNAL_REQSTR_ID_CLK32KGAUDIO
,
154 static const struct of_device_id palmas_clks_of_match
[] = {
156 .compatible
= "ti,palmas-clk32kg",
157 .data
= &palmas_of_clk32kg
,
160 .compatible
= "ti,palmas-clk32kgaudio",
161 .data
= &palmas_of_clk32kgaudio
,
165 MODULE_DEVICE_TABLE(of
, palmas_clks_of_match
);
167 static void palmas_clks_get_clk_data(struct platform_device
*pdev
,
168 struct palmas_clock_info
*cinfo
)
170 struct device_node
*node
= pdev
->dev
.of_node
;
174 ret
= of_property_read_u32(node
, "ti,external-sleep-control",
180 case PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE1
:
181 prop
= PALMAS_EXT_CONTROL_ENABLE1
;
183 case PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE2
:
184 prop
= PALMAS_EXT_CONTROL_ENABLE2
;
186 case PALMAS_CLOCK_DT_EXT_CONTROL_NSLEEP
:
187 prop
= PALMAS_EXT_CONTROL_NSLEEP
;
190 dev_warn(&pdev
->dev
, "%pOFn: Invalid ext control option: %u\n",
195 cinfo
->ext_control_pin
= prop
;
198 static int palmas_clks_init_configure(struct palmas_clock_info
*cinfo
)
202 ret
= palmas_update_bits(cinfo
->palmas
, PALMAS_RESOURCE_BASE
,
203 cinfo
->clk_desc
->control_reg
,
204 cinfo
->clk_desc
->sleep_mask
, 0);
206 dev_err(cinfo
->dev
, "Reg 0x%02x update failed, %d\n",
207 cinfo
->clk_desc
->control_reg
, ret
);
211 if (cinfo
->ext_control_pin
) {
212 ret
= clk_prepare(cinfo
->hw
.clk
);
214 dev_err(cinfo
->dev
, "Clock prep failed, %d\n", ret
);
218 ret
= palmas_ext_control_req_config(cinfo
->palmas
,
219 cinfo
->clk_desc
->sleep_reqstr_id
,
220 cinfo
->ext_control_pin
, true);
222 dev_err(cinfo
->dev
, "Ext config for %s failed, %d\n",
223 cinfo
->clk_desc
->clk_name
, ret
);
224 clk_unprepare(cinfo
->hw
.clk
);
231 static int palmas_clks_probe(struct platform_device
*pdev
)
233 struct palmas
*palmas
= dev_get_drvdata(pdev
->dev
.parent
);
234 struct device_node
*node
= pdev
->dev
.of_node
;
235 const struct palmas_clks_of_match_data
*match_data
;
236 struct palmas_clock_info
*cinfo
;
239 match_data
= of_device_get_match_data(&pdev
->dev
);
243 cinfo
= devm_kzalloc(&pdev
->dev
, sizeof(*cinfo
), GFP_KERNEL
);
247 palmas_clks_get_clk_data(pdev
, cinfo
);
248 platform_set_drvdata(pdev
, cinfo
);
250 cinfo
->dev
= &pdev
->dev
;
251 cinfo
->palmas
= palmas
;
253 cinfo
->clk_desc
= &match_data
->desc
;
254 cinfo
->hw
.init
= &match_data
->init
;
255 ret
= devm_clk_hw_register(&pdev
->dev
, &cinfo
->hw
);
257 dev_err(&pdev
->dev
, "Fail to register clock %s, %d\n",
258 match_data
->desc
.clk_name
, ret
);
262 ret
= palmas_clks_init_configure(cinfo
);
264 dev_err(&pdev
->dev
, "Clock config failed, %d\n", ret
);
268 ret
= of_clk_add_hw_provider(node
, of_clk_hw_simple_get
, &cinfo
->hw
);
270 dev_err(&pdev
->dev
, "Fail to add clock driver, %d\n", ret
);
274 static int palmas_clks_remove(struct platform_device
*pdev
)
276 of_clk_del_provider(pdev
->dev
.of_node
);
280 static struct platform_driver palmas_clks_driver
= {
282 .name
= "palmas-clk",
283 .of_match_table
= palmas_clks_of_match
,
285 .probe
= palmas_clks_probe
,
286 .remove
= palmas_clks_remove
,
289 module_platform_driver(palmas_clks_driver
);
291 MODULE_DESCRIPTION("Clock driver for Palmas Series Devices");
292 MODULE_ALIAS("platform:palmas-clk");
293 MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
294 MODULE_LICENSE("GPL v2");