xref: /openbmc/linux/drivers/clk/clk-s2mps11.c (revision 6774def6)
1 /*
2  * clk-s2mps11.c - Clock driver for S2MPS11.
3  *
4  * Copyright (C) 2013,2014 Samsung Electornics
5  *
6  * This program is free software; you can redistribute  it and/or modify it
7  * under  the terms of  the GNU General  Public License as published by the
8  * Free Software Foundation;  either version 2 of the  License, or (at your
9  * option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  */
17 
18 #include <linux/module.h>
19 #include <linux/err.h>
20 #include <linux/of.h>
21 #include <linux/clkdev.h>
22 #include <linux/regmap.h>
23 #include <linux/clk-provider.h>
24 #include <linux/platform_device.h>
25 #include <linux/mfd/samsung/s2mps11.h>
26 #include <linux/mfd/samsung/s2mps14.h>
27 #include <linux/mfd/samsung/s5m8767.h>
28 #include <linux/mfd/samsung/core.h>
29 
30 #define s2mps11_name(a) (a->hw.init->name)
31 
32 static struct clk **clk_table;
33 static struct clk_onecell_data clk_data;
34 
35 enum {
36 	S2MPS11_CLK_AP = 0,
37 	S2MPS11_CLK_CP,
38 	S2MPS11_CLK_BT,
39 	S2MPS11_CLKS_NUM,
40 };
41 
42 struct s2mps11_clk {
43 	struct sec_pmic_dev *iodev;
44 	struct device_node *clk_np;
45 	struct clk_hw hw;
46 	struct clk *clk;
47 	struct clk_lookup *lookup;
48 	u32 mask;
49 	unsigned int reg;
50 };
51 
52 static struct s2mps11_clk *to_s2mps11_clk(struct clk_hw *hw)
53 {
54 	return container_of(hw, struct s2mps11_clk, hw);
55 }
56 
57 static int s2mps11_clk_prepare(struct clk_hw *hw)
58 {
59 	struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw);
60 	int ret;
61 
62 	ret = regmap_update_bits(s2mps11->iodev->regmap_pmic,
63 				 s2mps11->reg,
64 				 s2mps11->mask, s2mps11->mask);
65 
66 	return ret;
67 }
68 
69 static void s2mps11_clk_unprepare(struct clk_hw *hw)
70 {
71 	struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw);
72 	int ret;
73 
74 	ret = regmap_update_bits(s2mps11->iodev->regmap_pmic, s2mps11->reg,
75 			   s2mps11->mask, ~s2mps11->mask);
76 }
77 
78 static int s2mps11_clk_is_prepared(struct clk_hw *hw)
79 {
80 	int ret;
81 	u32 val;
82 	struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw);
83 
84 	ret = regmap_read(s2mps11->iodev->regmap_pmic,
85 				s2mps11->reg, &val);
86 	if (ret < 0)
87 		return -EINVAL;
88 
89 	return val & s2mps11->mask;
90 }
91 
92 static unsigned long s2mps11_clk_recalc_rate(struct clk_hw *hw,
93 					     unsigned long parent_rate)
94 {
95 	return 32768;
96 }
97 
98 static struct clk_ops s2mps11_clk_ops = {
99 	.prepare	= s2mps11_clk_prepare,
100 	.unprepare	= s2mps11_clk_unprepare,
101 	.is_prepared	= s2mps11_clk_is_prepared,
102 	.recalc_rate	= s2mps11_clk_recalc_rate,
103 };
104 
105 static struct clk_init_data s2mps11_clks_init[S2MPS11_CLKS_NUM] = {
106 	[S2MPS11_CLK_AP] = {
107 		.name = "s2mps11_ap",
108 		.ops = &s2mps11_clk_ops,
109 		.flags = CLK_IS_ROOT,
110 	},
111 	[S2MPS11_CLK_CP] = {
112 		.name = "s2mps11_cp",
113 		.ops = &s2mps11_clk_ops,
114 		.flags = CLK_IS_ROOT,
115 	},
116 	[S2MPS11_CLK_BT] = {
117 		.name = "s2mps11_bt",
118 		.ops = &s2mps11_clk_ops,
119 		.flags = CLK_IS_ROOT,
120 	},
121 };
122 
123 static struct clk_init_data s2mps14_clks_init[S2MPS11_CLKS_NUM] = {
124 	[S2MPS11_CLK_AP] = {
125 		.name = "s2mps14_ap",
126 		.ops = &s2mps11_clk_ops,
127 		.flags = CLK_IS_ROOT,
128 	},
129 	[S2MPS11_CLK_BT] = {
130 		.name = "s2mps14_bt",
131 		.ops = &s2mps11_clk_ops,
132 		.flags = CLK_IS_ROOT,
133 	},
134 };
135 
136 static struct device_node *s2mps11_clk_parse_dt(struct platform_device *pdev,
137 		struct clk_init_data *clks_init)
138 {
139 	struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
140 	struct device_node *clk_np;
141 	int i;
142 
143 	if (!iodev->dev->of_node)
144 		return ERR_PTR(-EINVAL);
145 
146 	clk_np = of_get_child_by_name(iodev->dev->of_node, "clocks");
147 	if (!clk_np) {
148 		dev_err(&pdev->dev, "could not find clock sub-node\n");
149 		return ERR_PTR(-EINVAL);
150 	}
151 
152 	for (i = 0; i < S2MPS11_CLKS_NUM; i++) {
153 		if (!clks_init[i].name)
154 			continue; /* Skip clocks not present in some devices */
155 		of_property_read_string_index(clk_np, "clock-output-names", i,
156 				&clks_init[i].name);
157 	}
158 
159 	return clk_np;
160 }
161 
162 static int s2mps11_clk_probe(struct platform_device *pdev)
163 {
164 	struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
165 	struct s2mps11_clk *s2mps11_clks, *s2mps11_clk;
166 	unsigned int s2mps11_reg;
167 	struct clk_init_data *clks_init;
168 	int i, ret = 0;
169 
170 	s2mps11_clks = devm_kzalloc(&pdev->dev, sizeof(*s2mps11_clk) *
171 					S2MPS11_CLKS_NUM, GFP_KERNEL);
172 	if (!s2mps11_clks)
173 		return -ENOMEM;
174 
175 	s2mps11_clk = s2mps11_clks;
176 
177 	clk_table = devm_kzalloc(&pdev->dev, sizeof(struct clk *) *
178 				 S2MPS11_CLKS_NUM, GFP_KERNEL);
179 	if (!clk_table)
180 		return -ENOMEM;
181 
182 	switch(platform_get_device_id(pdev)->driver_data) {
183 	case S2MPS11X:
184 		s2mps11_reg = S2MPS11_REG_RTC_CTRL;
185 		clks_init = s2mps11_clks_init;
186 		break;
187 	case S2MPS14X:
188 		s2mps11_reg = S2MPS14_REG_RTCCTRL;
189 		clks_init = s2mps14_clks_init;
190 		break;
191 	case S5M8767X:
192 		s2mps11_reg = S5M8767_REG_CTRL1;
193 		clks_init = s2mps11_clks_init;
194 		break;
195 	default:
196 		dev_err(&pdev->dev, "Invalid device type\n");
197 		return -EINVAL;
198 	};
199 
200 	/* Store clocks of_node in first element of s2mps11_clks array */
201 	s2mps11_clks->clk_np = s2mps11_clk_parse_dt(pdev, clks_init);
202 	if (IS_ERR(s2mps11_clks->clk_np))
203 		return PTR_ERR(s2mps11_clks->clk_np);
204 
205 	for (i = 0; i < S2MPS11_CLKS_NUM; i++, s2mps11_clk++) {
206 		if (!clks_init[i].name)
207 			continue; /* Skip clocks not present in some devices */
208 		s2mps11_clk->iodev = iodev;
209 		s2mps11_clk->hw.init = &clks_init[i];
210 		s2mps11_clk->mask = 1 << i;
211 		s2mps11_clk->reg = s2mps11_reg;
212 
213 		s2mps11_clk->clk = devm_clk_register(&pdev->dev,
214 							&s2mps11_clk->hw);
215 		if (IS_ERR(s2mps11_clk->clk)) {
216 			dev_err(&pdev->dev, "Fail to register : %s\n",
217 						s2mps11_name(s2mps11_clk));
218 			ret = PTR_ERR(s2mps11_clk->clk);
219 			goto err_reg;
220 		}
221 
222 		s2mps11_clk->lookup = clkdev_alloc(s2mps11_clk->clk,
223 					s2mps11_name(s2mps11_clk), NULL);
224 		if (!s2mps11_clk->lookup) {
225 			ret = -ENOMEM;
226 			goto err_lup;
227 		}
228 
229 		clkdev_add(s2mps11_clk->lookup);
230 	}
231 
232 	for (i = 0; i < S2MPS11_CLKS_NUM; i++) {
233 		/* Skip clocks not present on S2MPS14 */
234 		if (!clks_init[i].name)
235 			continue;
236 		clk_table[i] = s2mps11_clks[i].clk;
237 	}
238 
239 	clk_data.clks = clk_table;
240 	clk_data.clk_num = S2MPS11_CLKS_NUM;
241 	of_clk_add_provider(s2mps11_clks->clk_np, of_clk_src_onecell_get,
242 			&clk_data);
243 
244 	platform_set_drvdata(pdev, s2mps11_clks);
245 
246 	return ret;
247 err_lup:
248 	devm_clk_unregister(&pdev->dev, s2mps11_clk->clk);
249 err_reg:
250 	while (s2mps11_clk > s2mps11_clks) {
251 		if (s2mps11_clk->lookup) {
252 			clkdev_drop(s2mps11_clk->lookup);
253 			devm_clk_unregister(&pdev->dev, s2mps11_clk->clk);
254 		}
255 		s2mps11_clk--;
256 	}
257 
258 	return ret;
259 }
260 
261 static int s2mps11_clk_remove(struct platform_device *pdev)
262 {
263 	struct s2mps11_clk *s2mps11_clks = platform_get_drvdata(pdev);
264 	int i;
265 
266 	of_clk_del_provider(s2mps11_clks[0].clk_np);
267 	/* Drop the reference obtained in s2mps11_clk_parse_dt */
268 	of_node_put(s2mps11_clks[0].clk_np);
269 
270 	for (i = 0; i < S2MPS11_CLKS_NUM; i++) {
271 		/* Skip clocks not present on S2MPS14 */
272 		if (!s2mps11_clks[i].lookup)
273 			continue;
274 		clkdev_drop(s2mps11_clks[i].lookup);
275 	}
276 
277 	return 0;
278 }
279 
280 static const struct platform_device_id s2mps11_clk_id[] = {
281 	{ "s2mps11-clk", S2MPS11X},
282 	{ "s2mps14-clk", S2MPS14X},
283 	{ "s5m8767-clk", S5M8767X},
284 	{ },
285 };
286 MODULE_DEVICE_TABLE(platform, s2mps11_clk_id);
287 
288 static struct platform_driver s2mps11_clk_driver = {
289 	.driver = {
290 		.name  = "s2mps11-clk",
291 		.owner = THIS_MODULE,
292 	},
293 	.probe = s2mps11_clk_probe,
294 	.remove = s2mps11_clk_remove,
295 	.id_table = s2mps11_clk_id,
296 };
297 
298 static int __init s2mps11_clk_init(void)
299 {
300 	return platform_driver_register(&s2mps11_clk_driver);
301 }
302 subsys_initcall(s2mps11_clk_init);
303 
304 static void __init s2mps11_clk_cleanup(void)
305 {
306 	platform_driver_unregister(&s2mps11_clk_driver);
307 }
308 module_exit(s2mps11_clk_cleanup);
309 
310 MODULE_DESCRIPTION("S2MPS11 Clock Driver");
311 MODULE_AUTHOR("Yadwinder Singh Brar <yadi.brar@samsung.com>");
312 MODULE_LICENSE("GPL");
313