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/clkdev.h>
22 #include <linux/clk-provider.h>
23 #include <linux/mfd/palmas.h>
24 #include <linux/module.h>
26 #include <linux/of_device.h>
27 #include <linux/platform_device.h>
28 #include <linux/slab.h>
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
34 struct palmas_clk32k_desc
{
36 unsigned int control_reg
;
37 unsigned int enable_mask
;
38 unsigned int sleep_mask
;
39 unsigned int sleep_reqstr_id
;
43 struct palmas_clock_info
{
47 struct palmas
*palmas
;
48 struct palmas_clk32k_desc
*clk_desc
;
52 static inline struct palmas_clock_info
*to_palmas_clks_info(struct clk_hw
*hw
)
54 return container_of(hw
, struct palmas_clock_info
, hw
);
57 static unsigned long palmas_clks_recalc_rate(struct clk_hw
*hw
,
58 unsigned long parent_rate
)
63 static int palmas_clks_prepare(struct clk_hw
*hw
)
65 struct palmas_clock_info
*cinfo
= to_palmas_clks_info(hw
);
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
);
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
);
81 static void palmas_clks_unprepare(struct clk_hw
*hw
)
83 struct palmas_clock_info
*cinfo
= to_palmas_clks_info(hw
);
87 * Clock can be disabled through external pin if it is externally
90 if (cinfo
->ext_control_pin
)
93 ret
= palmas_update_bits(cinfo
->palmas
, PALMAS_RESOURCE_BASE
,
94 cinfo
->clk_desc
->control_reg
,
95 cinfo
->clk_desc
->enable_mask
, 0);
97 dev_err(cinfo
->dev
, "Reg 0x%02x update failed, %d\n",
98 cinfo
->clk_desc
->control_reg
, ret
);
101 static int palmas_clks_is_prepared(struct clk_hw
*hw
)
103 struct palmas_clock_info
*cinfo
= to_palmas_clks_info(hw
);
107 if (cinfo
->ext_control_pin
)
110 ret
= palmas_read(cinfo
->palmas
, PALMAS_RESOURCE_BASE
,
111 cinfo
->clk_desc
->control_reg
, &val
);
113 dev_err(cinfo
->dev
, "Reg 0x%02x read failed, %d\n",
114 cinfo
->clk_desc
->control_reg
, ret
);
117 return !!(val
& cinfo
->clk_desc
->enable_mask
);
120 static 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
,
127 struct palmas_clks_of_match_data
{
128 struct clk_init_data init
;
129 struct palmas_clk32k_desc desc
;
132 static struct palmas_clks_of_match_data palmas_of_clk32kg
= {
135 .ops
= &palmas_clks_ops
,
136 .flags
= CLK_IS_ROOT
| CLK_IGNORE_UNUSED
,
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
,
148 static struct palmas_clks_of_match_data palmas_of_clk32kgaudio
= {
150 .name
= "clk32kgaudio",
151 .ops
= &palmas_clks_ops
,
152 .flags
= CLK_IS_ROOT
| CLK_IGNORE_UNUSED
,
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
,
164 static struct of_device_id palmas_clks_of_match
[] = {
166 .compatible
= "ti,palmas-clk32kg",
167 .data
= &palmas_of_clk32kg
,
170 .compatible
= "ti,palmas-clk32kgaudio",
171 .data
= &palmas_of_clk32kgaudio
,
175 MODULE_DEVICE_TABLE(of
, palmas_clks_of_match
);
177 static void palmas_clks_get_clk_data(struct platform_device
*pdev
,
178 struct palmas_clock_info
*cinfo
)
180 struct device_node
*node
= pdev
->dev
.of_node
;
184 ret
= of_property_read_u32(node
, "ti,external-sleep-control",
190 case PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE1
:
191 prop
= PALMAS_EXT_CONTROL_ENABLE1
;
193 case PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE2
:
194 prop
= PALMAS_EXT_CONTROL_ENABLE2
;
196 case PALMAS_CLOCK_DT_EXT_CONTROL_NSLEEP
:
197 prop
= PALMAS_EXT_CONTROL_NSLEEP
;
200 dev_warn(&pdev
->dev
, "%s: Invalid ext control option: %u\n",
205 cinfo
->ext_control_pin
= prop
;
208 static int palmas_clks_init_configure(struct palmas_clock_info
*cinfo
)
212 ret
= palmas_update_bits(cinfo
->palmas
, PALMAS_RESOURCE_BASE
,
213 cinfo
->clk_desc
->control_reg
,
214 cinfo
->clk_desc
->sleep_mask
, 0);
216 dev_err(cinfo
->dev
, "Reg 0x%02x update failed, %d\n",
217 cinfo
->clk_desc
->control_reg
, ret
);
221 if (cinfo
->ext_control_pin
) {
222 ret
= clk_prepare(cinfo
->clk
);
224 dev_err(cinfo
->dev
, "Clock prep failed, %d\n", ret
);
228 ret
= palmas_ext_control_req_config(cinfo
->palmas
,
229 cinfo
->clk_desc
->sleep_reqstr_id
,
230 cinfo
->ext_control_pin
, true);
232 dev_err(cinfo
->dev
, "Ext config for %s failed, %d\n",
233 cinfo
->clk_desc
->clk_name
, ret
);
240 static int palmas_clks_probe(struct platform_device
*pdev
)
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
;
250 match
= of_match_device(palmas_clks_of_match
, &pdev
->dev
);
251 match_data
= (struct palmas_clks_of_match_data
*)match
->data
;
253 cinfo
= devm_kzalloc(&pdev
->dev
, sizeof(*cinfo
), GFP_KERNEL
);
257 palmas_clks_get_clk_data(pdev
, cinfo
);
258 platform_set_drvdata(pdev
, cinfo
);
260 cinfo
->dev
= &pdev
->dev
;
261 cinfo
->palmas
= palmas
;
263 cinfo
->clk_desc
= &match_data
->desc
;
264 cinfo
->hw
.init
= &match_data
->init
;
265 clk
= devm_clk_register(&pdev
->dev
, &cinfo
->hw
);
268 dev_err(&pdev
->dev
, "Fail to register clock %s, %d\n",
269 match_data
->desc
.clk_name
, ret
);
274 ret
= palmas_clks_init_configure(cinfo
);
276 dev_err(&pdev
->dev
, "Clock config failed, %d\n", ret
);
280 ret
= of_clk_add_provider(node
, of_clk_src_simple_get
, cinfo
->clk
);
282 dev_err(&pdev
->dev
, "Fail to add clock driver, %d\n", ret
);
286 static int palmas_clks_remove(struct platform_device
*pdev
)
288 of_clk_del_provider(pdev
->dev
.of_node
);
292 static struct platform_driver palmas_clks_driver
= {
294 .name
= "palmas-clk",
295 .of_match_table
= palmas_clks_of_match
,
297 .probe
= palmas_clks_probe
,
298 .remove
= palmas_clks_remove
,
301 module_platform_driver(palmas_clks_driver
);
303 MODULE_DESCRIPTION("Clock driver for Palmas Series Devices");
304 MODULE_ALIAS("platform:palmas-clk");
305 MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
306 MODULE_LICENSE("GPL v2");