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/platform_device.h>
18 #include <linux/slab.h>
20 #define PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE1 1
21 #define PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE2 2
22 #define PALMAS_CLOCK_DT_EXT_CONTROL_NSLEEP 3
24 struct palmas_clk32k_desc
{
26 unsigned int control_reg
;
27 unsigned int enable_mask
;
28 unsigned int sleep_mask
;
29 unsigned int sleep_reqstr_id
;
33 struct palmas_clock_info
{
36 struct palmas
*palmas
;
37 const struct palmas_clk32k_desc
*clk_desc
;
41 static inline struct palmas_clock_info
*to_palmas_clks_info(struct clk_hw
*hw
)
43 return container_of(hw
, struct palmas_clock_info
, hw
);
46 static unsigned long palmas_clks_recalc_rate(struct clk_hw
*hw
,
47 unsigned long parent_rate
)
52 static int palmas_clks_prepare(struct clk_hw
*hw
)
54 struct palmas_clock_info
*cinfo
= to_palmas_clks_info(hw
);
57 ret
= palmas_update_bits(cinfo
->palmas
, PALMAS_RESOURCE_BASE
,
58 cinfo
->clk_desc
->control_reg
,
59 cinfo
->clk_desc
->enable_mask
,
60 cinfo
->clk_desc
->enable_mask
);
62 dev_err(cinfo
->dev
, "Reg 0x%02x update failed, %d\n",
63 cinfo
->clk_desc
->control_reg
, ret
);
64 else if (cinfo
->clk_desc
->delay
)
65 udelay(cinfo
->clk_desc
->delay
);
70 static void palmas_clks_unprepare(struct clk_hw
*hw
)
72 struct palmas_clock_info
*cinfo
= to_palmas_clks_info(hw
);
76 * Clock can be disabled through external pin if it is externally
79 if (cinfo
->ext_control_pin
)
82 ret
= palmas_update_bits(cinfo
->palmas
, PALMAS_RESOURCE_BASE
,
83 cinfo
->clk_desc
->control_reg
,
84 cinfo
->clk_desc
->enable_mask
, 0);
86 dev_err(cinfo
->dev
, "Reg 0x%02x update failed, %d\n",
87 cinfo
->clk_desc
->control_reg
, ret
);
90 static int palmas_clks_is_prepared(struct clk_hw
*hw
)
92 struct palmas_clock_info
*cinfo
= to_palmas_clks_info(hw
);
96 if (cinfo
->ext_control_pin
)
99 ret
= palmas_read(cinfo
->palmas
, PALMAS_RESOURCE_BASE
,
100 cinfo
->clk_desc
->control_reg
, &val
);
102 dev_err(cinfo
->dev
, "Reg 0x%02x read failed, %d\n",
103 cinfo
->clk_desc
->control_reg
, ret
);
106 return !!(val
& cinfo
->clk_desc
->enable_mask
);
109 static const struct clk_ops palmas_clks_ops
= {
110 .prepare
= palmas_clks_prepare
,
111 .unprepare
= palmas_clks_unprepare
,
112 .is_prepared
= palmas_clks_is_prepared
,
113 .recalc_rate
= palmas_clks_recalc_rate
,
116 struct palmas_clks_of_match_data
{
117 struct clk_init_data init
;
118 const struct palmas_clk32k_desc desc
;
121 static const struct palmas_clks_of_match_data palmas_of_clk32kg
= {
124 .ops
= &palmas_clks_ops
,
125 .flags
= CLK_IGNORE_UNUSED
,
128 .clk_name
= "clk32kg",
129 .control_reg
= PALMAS_CLK32KG_CTRL
,
130 .enable_mask
= PALMAS_CLK32KG_CTRL_MODE_ACTIVE
,
131 .sleep_mask
= PALMAS_CLK32KG_CTRL_MODE_SLEEP
,
132 .sleep_reqstr_id
= PALMAS_EXTERNAL_REQSTR_ID_CLK32KG
,
137 static const struct palmas_clks_of_match_data palmas_of_clk32kgaudio
= {
139 .name
= "clk32kgaudio",
140 .ops
= &palmas_clks_ops
,
141 .flags
= CLK_IGNORE_UNUSED
,
144 .clk_name
= "clk32kgaudio",
145 .control_reg
= PALMAS_CLK32KGAUDIO_CTRL
,
146 .enable_mask
= PALMAS_CLK32KG_CTRL_MODE_ACTIVE
,
147 .sleep_mask
= PALMAS_CLK32KG_CTRL_MODE_SLEEP
,
148 .sleep_reqstr_id
= PALMAS_EXTERNAL_REQSTR_ID_CLK32KGAUDIO
,
153 static const struct of_device_id palmas_clks_of_match
[] = {
155 .compatible
= "ti,palmas-clk32kg",
156 .data
= &palmas_of_clk32kg
,
159 .compatible
= "ti,palmas-clk32kgaudio",
160 .data
= &palmas_of_clk32kgaudio
,
164 MODULE_DEVICE_TABLE(of
, palmas_clks_of_match
);
166 static void palmas_clks_get_clk_data(struct platform_device
*pdev
,
167 struct palmas_clock_info
*cinfo
)
169 struct device_node
*node
= pdev
->dev
.of_node
;
173 ret
= of_property_read_u32(node
, "ti,external-sleep-control",
179 case PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE1
:
180 prop
= PALMAS_EXT_CONTROL_ENABLE1
;
182 case PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE2
:
183 prop
= PALMAS_EXT_CONTROL_ENABLE2
;
185 case PALMAS_CLOCK_DT_EXT_CONTROL_NSLEEP
:
186 prop
= PALMAS_EXT_CONTROL_NSLEEP
;
189 dev_warn(&pdev
->dev
, "%pOFn: Invalid ext control option: %u\n",
194 cinfo
->ext_control_pin
= prop
;
197 static int palmas_clks_init_configure(struct palmas_clock_info
*cinfo
)
201 ret
= palmas_update_bits(cinfo
->palmas
, PALMAS_RESOURCE_BASE
,
202 cinfo
->clk_desc
->control_reg
,
203 cinfo
->clk_desc
->sleep_mask
, 0);
205 dev_err(cinfo
->dev
, "Reg 0x%02x update failed, %d\n",
206 cinfo
->clk_desc
->control_reg
, ret
);
210 if (cinfo
->ext_control_pin
) {
211 ret
= clk_prepare(cinfo
->hw
.clk
);
213 dev_err(cinfo
->dev
, "Clock prep failed, %d\n", ret
);
217 ret
= palmas_ext_control_req_config(cinfo
->palmas
,
218 cinfo
->clk_desc
->sleep_reqstr_id
,
219 cinfo
->ext_control_pin
, true);
221 dev_err(cinfo
->dev
, "Ext config for %s failed, %d\n",
222 cinfo
->clk_desc
->clk_name
, ret
);
223 clk_unprepare(cinfo
->hw
.clk
);
230 static int palmas_clks_probe(struct platform_device
*pdev
)
232 struct palmas
*palmas
= dev_get_drvdata(pdev
->dev
.parent
);
233 struct device_node
*node
= pdev
->dev
.of_node
;
234 const struct palmas_clks_of_match_data
*match_data
;
235 struct palmas_clock_info
*cinfo
;
238 match_data
= of_device_get_match_data(&pdev
->dev
);
242 cinfo
= devm_kzalloc(&pdev
->dev
, sizeof(*cinfo
), GFP_KERNEL
);
246 palmas_clks_get_clk_data(pdev
, cinfo
);
247 platform_set_drvdata(pdev
, cinfo
);
249 cinfo
->dev
= &pdev
->dev
;
250 cinfo
->palmas
= palmas
;
252 cinfo
->clk_desc
= &match_data
->desc
;
253 cinfo
->hw
.init
= &match_data
->init
;
254 ret
= devm_clk_hw_register(&pdev
->dev
, &cinfo
->hw
);
256 dev_err(&pdev
->dev
, "Fail to register clock %s, %d\n",
257 match_data
->desc
.clk_name
, ret
);
261 ret
= palmas_clks_init_configure(cinfo
);
263 dev_err(&pdev
->dev
, "Clock config failed, %d\n", ret
);
267 ret
= of_clk_add_hw_provider(node
, of_clk_hw_simple_get
, &cinfo
->hw
);
269 dev_err(&pdev
->dev
, "Fail to add clock driver, %d\n", ret
);
273 static void palmas_clks_remove(struct platform_device
*pdev
)
275 of_clk_del_provider(pdev
->dev
.of_node
);
278 static struct platform_driver palmas_clks_driver
= {
280 .name
= "palmas-clk",
281 .of_match_table
= palmas_clks_of_match
,
283 .probe
= palmas_clks_probe
,
284 .remove
= palmas_clks_remove
,
287 module_platform_driver(palmas_clks_driver
);
289 MODULE_DESCRIPTION("Clock driver for Palmas Series Devices");
290 MODULE_ALIAS("platform:palmas-clk");
291 MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
292 MODULE_LICENSE("GPL v2");