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
{
46 struct palmas
*palmas
;
47 const struct palmas_clk32k_desc
*clk_desc
;
51 static inline struct palmas_clock_info
*to_palmas_clks_info(struct clk_hw
*hw
)
53 return container_of(hw
, struct palmas_clock_info
, hw
);
56 static unsigned long palmas_clks_recalc_rate(struct clk_hw
*hw
,
57 unsigned long parent_rate
)
62 static int palmas_clks_prepare(struct clk_hw
*hw
)
64 struct palmas_clock_info
*cinfo
= to_palmas_clks_info(hw
);
67 ret
= palmas_update_bits(cinfo
->palmas
, PALMAS_RESOURCE_BASE
,
68 cinfo
->clk_desc
->control_reg
,
69 cinfo
->clk_desc
->enable_mask
,
70 cinfo
->clk_desc
->enable_mask
);
72 dev_err(cinfo
->dev
, "Reg 0x%02x update failed, %d\n",
73 cinfo
->clk_desc
->control_reg
, ret
);
74 else if (cinfo
->clk_desc
->delay
)
75 udelay(cinfo
->clk_desc
->delay
);
80 static void palmas_clks_unprepare(struct clk_hw
*hw
)
82 struct palmas_clock_info
*cinfo
= to_palmas_clks_info(hw
);
86 * Clock can be disabled through external pin if it is externally
89 if (cinfo
->ext_control_pin
)
92 ret
= palmas_update_bits(cinfo
->palmas
, PALMAS_RESOURCE_BASE
,
93 cinfo
->clk_desc
->control_reg
,
94 cinfo
->clk_desc
->enable_mask
, 0);
96 dev_err(cinfo
->dev
, "Reg 0x%02x update failed, %d\n",
97 cinfo
->clk_desc
->control_reg
, ret
);
100 static int palmas_clks_is_prepared(struct clk_hw
*hw
)
102 struct palmas_clock_info
*cinfo
= to_palmas_clks_info(hw
);
106 if (cinfo
->ext_control_pin
)
109 ret
= palmas_read(cinfo
->palmas
, PALMAS_RESOURCE_BASE
,
110 cinfo
->clk_desc
->control_reg
, &val
);
112 dev_err(cinfo
->dev
, "Reg 0x%02x read failed, %d\n",
113 cinfo
->clk_desc
->control_reg
, ret
);
116 return !!(val
& cinfo
->clk_desc
->enable_mask
);
119 static struct clk_ops palmas_clks_ops
= {
120 .prepare
= palmas_clks_prepare
,
121 .unprepare
= palmas_clks_unprepare
,
122 .is_prepared
= palmas_clks_is_prepared
,
123 .recalc_rate
= palmas_clks_recalc_rate
,
126 struct palmas_clks_of_match_data
{
127 struct clk_init_data init
;
128 const struct palmas_clk32k_desc desc
;
131 static const struct palmas_clks_of_match_data palmas_of_clk32kg
= {
134 .ops
= &palmas_clks_ops
,
135 .flags
= CLK_IGNORE_UNUSED
,
138 .clk_name
= "clk32kg",
139 .control_reg
= PALMAS_CLK32KG_CTRL
,
140 .enable_mask
= PALMAS_CLK32KG_CTRL_MODE_ACTIVE
,
141 .sleep_mask
= PALMAS_CLK32KG_CTRL_MODE_SLEEP
,
142 .sleep_reqstr_id
= PALMAS_EXTERNAL_REQSTR_ID_CLK32KG
,
147 static const struct palmas_clks_of_match_data palmas_of_clk32kgaudio
= {
149 .name
= "clk32kgaudio",
150 .ops
= &palmas_clks_ops
,
151 .flags
= CLK_IGNORE_UNUSED
,
154 .clk_name
= "clk32kgaudio",
155 .control_reg
= PALMAS_CLK32KGAUDIO_CTRL
,
156 .enable_mask
= PALMAS_CLK32KG_CTRL_MODE_ACTIVE
,
157 .sleep_mask
= PALMAS_CLK32KG_CTRL_MODE_SLEEP
,
158 .sleep_reqstr_id
= PALMAS_EXTERNAL_REQSTR_ID_CLK32KGAUDIO
,
163 static const struct of_device_id palmas_clks_of_match
[] = {
165 .compatible
= "ti,palmas-clk32kg",
166 .data
= &palmas_of_clk32kg
,
169 .compatible
= "ti,palmas-clk32kgaudio",
170 .data
= &palmas_of_clk32kgaudio
,
174 MODULE_DEVICE_TABLE(of
, palmas_clks_of_match
);
176 static void palmas_clks_get_clk_data(struct platform_device
*pdev
,
177 struct palmas_clock_info
*cinfo
)
179 struct device_node
*node
= pdev
->dev
.of_node
;
183 ret
= of_property_read_u32(node
, "ti,external-sleep-control",
189 case PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE1
:
190 prop
= PALMAS_EXT_CONTROL_ENABLE1
;
192 case PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE2
:
193 prop
= PALMAS_EXT_CONTROL_ENABLE2
;
195 case PALMAS_CLOCK_DT_EXT_CONTROL_NSLEEP
:
196 prop
= PALMAS_EXT_CONTROL_NSLEEP
;
199 dev_warn(&pdev
->dev
, "%s: Invalid ext control option: %u\n",
204 cinfo
->ext_control_pin
= prop
;
207 static int palmas_clks_init_configure(struct palmas_clock_info
*cinfo
)
211 ret
= palmas_update_bits(cinfo
->palmas
, PALMAS_RESOURCE_BASE
,
212 cinfo
->clk_desc
->control_reg
,
213 cinfo
->clk_desc
->sleep_mask
, 0);
215 dev_err(cinfo
->dev
, "Reg 0x%02x update failed, %d\n",
216 cinfo
->clk_desc
->control_reg
, ret
);
220 if (cinfo
->ext_control_pin
) {
221 ret
= clk_prepare(cinfo
->clk
);
223 dev_err(cinfo
->dev
, "Clock prep failed, %d\n", ret
);
227 ret
= palmas_ext_control_req_config(cinfo
->palmas
,
228 cinfo
->clk_desc
->sleep_reqstr_id
,
229 cinfo
->ext_control_pin
, true);
231 dev_err(cinfo
->dev
, "Ext config for %s failed, %d\n",
232 cinfo
->clk_desc
->clk_name
, ret
);
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
;
248 match_data
= of_device_get_match_data(&pdev
->dev
);
252 cinfo
= devm_kzalloc(&pdev
->dev
, sizeof(*cinfo
), GFP_KERNEL
);
256 palmas_clks_get_clk_data(pdev
, cinfo
);
257 platform_set_drvdata(pdev
, cinfo
);
259 cinfo
->dev
= &pdev
->dev
;
260 cinfo
->palmas
= palmas
;
262 cinfo
->clk_desc
= &match_data
->desc
;
263 cinfo
->hw
.init
= &match_data
->init
;
264 clk
= devm_clk_register(&pdev
->dev
, &cinfo
->hw
);
267 dev_err(&pdev
->dev
, "Fail to register clock %s, %d\n",
268 match_data
->desc
.clk_name
, ret
);
273 ret
= palmas_clks_init_configure(cinfo
);
275 dev_err(&pdev
->dev
, "Clock config failed, %d\n", ret
);
279 ret
= of_clk_add_provider(node
, of_clk_src_simple_get
, cinfo
->clk
);
281 dev_err(&pdev
->dev
, "Fail to add clock driver, %d\n", ret
);
285 static int palmas_clks_remove(struct platform_device
*pdev
)
287 of_clk_del_provider(pdev
->dev
.of_node
);
291 static struct platform_driver palmas_clks_driver
= {
293 .name
= "palmas-clk",
294 .of_match_table
= palmas_clks_of_match
,
296 .probe
= palmas_clks_probe
,
297 .remove
= palmas_clks_remove
,
300 module_platform_driver(palmas_clks_driver
);
302 MODULE_DESCRIPTION("Clock driver for Palmas Series Devices");
303 MODULE_ALIAS("platform:palmas-clk");
304 MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
305 MODULE_LICENSE("GPL v2");