xref: /openbmc/linux/drivers/clk/clk-palmas.c (revision 23c2b932)
1 /*
2  * Clock driver for Palmas device.
3  *
4  * Copyright (c) 2013, NVIDIA Corporation.
5  * Copyright (c) 2013-2014 Texas Instruments, Inc.
6  *
7  * Author:	Laxman Dewangan <ldewangan@nvidia.com>
8  *		Peter Ujfalusi <peter.ujfalusi@ti.com>
9  *
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.
13  *
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.
18  */
19 
20 #include <linux/clk.h>
21 #include <linux/clk-provider.h>
22 #include <linux/mfd/palmas.h>
23 #include <linux/module.h>
24 #include <linux/of.h>
25 #include <linux/of_device.h>
26 #include <linux/platform_device.h>
27 #include <linux/slab.h>
28 
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
32 
33 struct palmas_clk32k_desc {
34 	const char *clk_name;
35 	unsigned int control_reg;
36 	unsigned int enable_mask;
37 	unsigned int sleep_mask;
38 	unsigned int sleep_reqstr_id;
39 	int delay;
40 };
41 
42 struct palmas_clock_info {
43 	struct device *dev;
44 	struct clk *clk;
45 	struct clk_hw hw;
46 	struct palmas *palmas;
47 	const struct palmas_clk32k_desc *clk_desc;
48 	int ext_control_pin;
49 };
50 
51 static inline struct palmas_clock_info *to_palmas_clks_info(struct clk_hw *hw)
52 {
53 	return container_of(hw, struct palmas_clock_info, hw);
54 }
55 
56 static unsigned long palmas_clks_recalc_rate(struct clk_hw *hw,
57 					     unsigned long parent_rate)
58 {
59 	return 32768;
60 }
61 
62 static int palmas_clks_prepare(struct clk_hw *hw)
63 {
64 	struct palmas_clock_info *cinfo = to_palmas_clks_info(hw);
65 	int ret;
66 
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);
71 	if (ret < 0)
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);
76 
77 	return ret;
78 }
79 
80 static void palmas_clks_unprepare(struct clk_hw *hw)
81 {
82 	struct palmas_clock_info *cinfo = to_palmas_clks_info(hw);
83 	int ret;
84 
85 	/*
86 	 * Clock can be disabled through external pin if it is externally
87 	 * controlled.
88 	 */
89 	if (cinfo->ext_control_pin)
90 		return;
91 
92 	ret = palmas_update_bits(cinfo->palmas, PALMAS_RESOURCE_BASE,
93 				 cinfo->clk_desc->control_reg,
94 				 cinfo->clk_desc->enable_mask, 0);
95 	if (ret < 0)
96 		dev_err(cinfo->dev, "Reg 0x%02x update failed, %d\n",
97 			cinfo->clk_desc->control_reg, ret);
98 }
99 
100 static int palmas_clks_is_prepared(struct clk_hw *hw)
101 {
102 	struct palmas_clock_info *cinfo = to_palmas_clks_info(hw);
103 	int ret;
104 	u32 val;
105 
106 	if (cinfo->ext_control_pin)
107 		return 1;
108 
109 	ret = palmas_read(cinfo->palmas, PALMAS_RESOURCE_BASE,
110 			  cinfo->clk_desc->control_reg, &val);
111 	if (ret < 0) {
112 		dev_err(cinfo->dev, "Reg 0x%02x read failed, %d\n",
113 			cinfo->clk_desc->control_reg, ret);
114 		return ret;
115 	}
116 	return !!(val & cinfo->clk_desc->enable_mask);
117 }
118 
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,
124 };
125 
126 struct palmas_clks_of_match_data {
127 	struct clk_init_data init;
128 	const struct palmas_clk32k_desc desc;
129 };
130 
131 static const struct palmas_clks_of_match_data palmas_of_clk32kg = {
132 	.init = {
133 		.name = "clk32kg",
134 		.ops = &palmas_clks_ops,
135 		.flags = CLK_IGNORE_UNUSED,
136 	},
137 	.desc = {
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,
143 		.delay = 200,
144 	},
145 };
146 
147 static const struct palmas_clks_of_match_data palmas_of_clk32kgaudio = {
148 	.init = {
149 		.name = "clk32kgaudio",
150 		.ops = &palmas_clks_ops,
151 		.flags = CLK_IGNORE_UNUSED,
152 	},
153 	.desc = {
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,
159 		.delay = 200,
160 	},
161 };
162 
163 static const struct of_device_id palmas_clks_of_match[] = {
164 	{
165 		.compatible = "ti,palmas-clk32kg",
166 		.data = &palmas_of_clk32kg,
167 	},
168 	{
169 		.compatible = "ti,palmas-clk32kgaudio",
170 		.data = &palmas_of_clk32kgaudio,
171 	},
172 	{ },
173 };
174 MODULE_DEVICE_TABLE(of, palmas_clks_of_match);
175 
176 static void palmas_clks_get_clk_data(struct platform_device *pdev,
177 				     struct palmas_clock_info *cinfo)
178 {
179 	struct device_node *node = pdev->dev.of_node;
180 	unsigned int prop;
181 	int ret;
182 
183 	ret = of_property_read_u32(node, "ti,external-sleep-control",
184 				   &prop);
185 	if (ret)
186 		return;
187 
188 	switch (prop) {
189 	case PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE1:
190 		prop = PALMAS_EXT_CONTROL_ENABLE1;
191 		break;
192 	case PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE2:
193 		prop = PALMAS_EXT_CONTROL_ENABLE2;
194 		break;
195 	case PALMAS_CLOCK_DT_EXT_CONTROL_NSLEEP:
196 		prop = PALMAS_EXT_CONTROL_NSLEEP;
197 		break;
198 	default:
199 		dev_warn(&pdev->dev, "%s: Invalid ext control option: %u\n",
200 			 node->name, prop);
201 		prop = 0;
202 		break;
203 	}
204 	cinfo->ext_control_pin = prop;
205 }
206 
207 static int palmas_clks_init_configure(struct palmas_clock_info *cinfo)
208 {
209 	int ret;
210 
211 	ret = palmas_update_bits(cinfo->palmas, PALMAS_RESOURCE_BASE,
212 				 cinfo->clk_desc->control_reg,
213 				 cinfo->clk_desc->sleep_mask, 0);
214 	if (ret < 0) {
215 		dev_err(cinfo->dev, "Reg 0x%02x update failed, %d\n",
216 			cinfo->clk_desc->control_reg, ret);
217 		return ret;
218 	}
219 
220 	if (cinfo->ext_control_pin) {
221 		ret = clk_prepare(cinfo->clk);
222 		if (ret < 0) {
223 			dev_err(cinfo->dev, "Clock prep failed, %d\n", ret);
224 			return ret;
225 		}
226 
227 		ret = palmas_ext_control_req_config(cinfo->palmas,
228 					cinfo->clk_desc->sleep_reqstr_id,
229 					cinfo->ext_control_pin, true);
230 		if (ret < 0) {
231 			dev_err(cinfo->dev, "Ext config for %s failed, %d\n",
232 				cinfo->clk_desc->clk_name, ret);
233 			return ret;
234 		}
235 	}
236 
237 	return ret;
238 }
239 static int palmas_clks_probe(struct platform_device *pdev)
240 {
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;
245 	struct clk *clk;
246 	int ret;
247 
248 	match_data = of_device_get_match_data(&pdev->dev);
249 	if (!match_data)
250 		return 1;
251 
252 	cinfo = devm_kzalloc(&pdev->dev, sizeof(*cinfo), GFP_KERNEL);
253 	if (!cinfo)
254 		return -ENOMEM;
255 
256 	palmas_clks_get_clk_data(pdev, cinfo);
257 	platform_set_drvdata(pdev, cinfo);
258 
259 	cinfo->dev = &pdev->dev;
260 	cinfo->palmas = palmas;
261 
262 	cinfo->clk_desc = &match_data->desc;
263 	cinfo->hw.init = &match_data->init;
264 	clk = devm_clk_register(&pdev->dev, &cinfo->hw);
265 	if (IS_ERR(clk)) {
266 		ret = PTR_ERR(clk);
267 		dev_err(&pdev->dev, "Fail to register clock %s, %d\n",
268 			match_data->desc.clk_name, ret);
269 		return ret;
270 	}
271 
272 	cinfo->clk = clk;
273 	ret = palmas_clks_init_configure(cinfo);
274 	if (ret < 0) {
275 		dev_err(&pdev->dev, "Clock config failed, %d\n", ret);
276 		return ret;
277 	}
278 
279 	ret = of_clk_add_provider(node, of_clk_src_simple_get, cinfo->clk);
280 	if (ret < 0)
281 		dev_err(&pdev->dev, "Fail to add clock driver, %d\n", ret);
282 	return ret;
283 }
284 
285 static int palmas_clks_remove(struct platform_device *pdev)
286 {
287 	of_clk_del_provider(pdev->dev.of_node);
288 	return 0;
289 }
290 
291 static struct platform_driver palmas_clks_driver = {
292 	.driver = {
293 		.name = "palmas-clk",
294 		.of_match_table = palmas_clks_of_match,
295 	},
296 	.probe = palmas_clks_probe,
297 	.remove = palmas_clks_remove,
298 };
299 
300 module_platform_driver(palmas_clks_driver);
301 
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");
306