blob: 9aba3a8245e15f5c84f4e20b4afb0aece9c3a4f0 [file] [log] [blame]
Jonghwa Lee73118e62012-08-28 17:54:28 +09001/*
Laxman Dewangan8ad313f2016-06-17 16:21:04 +05302 * clk-max77686.c - Clock driver for Maxim 77686/MAX77802
Jonghwa Lee73118e62012-08-28 17:54:28 +09003 *
4 * Copyright (C) 2012 Samsung Electornics
5 * Jonghwa Lee <jonghwa3.lee@samsung.com>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <linux/kernel.h>
24#include <linux/slab.h>
25#include <linux/err.h>
Paul Gortmakera0c4dfe2015-05-01 20:02:30 -040026#include <linux/module.h>
Jonghwa Lee73118e62012-08-28 17:54:28 +090027#include <linux/platform_device.h>
28#include <linux/mfd/max77686.h>
29#include <linux/mfd/max77686-private.h>
30#include <linux/clk-provider.h>
31#include <linux/mutex.h>
32#include <linux/clkdev.h>
Laxman Dewangan8ad313f2016-06-17 16:21:04 +053033#include <linux/of.h>
34#include <linux/regmap.h>
Jonghwa Lee73118e62012-08-28 17:54:28 +090035
Javier Martinez Canillasa8a76f52014-08-18 10:32:59 +020036#include <dt-bindings/clock/maxim,max77686.h>
Laxman Dewangan8ad313f2016-06-17 16:21:04 +053037#include <dt-bindings/clock/maxim,max77802.h>
Jonghwa Lee73118e62012-08-28 17:54:28 +090038
Laxman Dewangan8ad313f2016-06-17 16:21:04 +053039#define MAX77802_CLOCK_LOW_JITTER_SHIFT 0x3
40
41enum max77686_chip_name {
42 CHIP_MAX77686,
43 CHIP_MAX77802,
44};
45
46struct max77686_hw_clk_info {
47 const char *name;
48 u32 clk_reg;
49 u32 clk_enable_mask;
50 u32 flags;
51};
52
53struct max77686_clk_init_data {
54 struct regmap *regmap;
55 struct clk_hw hw;
56 struct clk_init_data clk_idata;
57 const struct max77686_hw_clk_info *clk_info;
58};
59
60struct max77686_clk_driver_data {
61 enum max77686_chip_name chip;
62 struct clk **clks;
63 struct max77686_clk_init_data *max_clk_data;
64 struct clk_onecell_data of_data;
65};
66
67static const struct
68max77686_hw_clk_info max77686_hw_clks_info[MAX77686_CLKS_NUM] = {
Jonghwa Lee73118e62012-08-28 17:54:28 +090069 [MAX77686_CLK_AP] = {
70 .name = "32khz_ap",
Laxman Dewangan8ad313f2016-06-17 16:21:04 +053071 .clk_reg = MAX77686_REG_32KHZ,
72 .clk_enable_mask = BIT(MAX77686_CLK_AP),
Jonghwa Lee73118e62012-08-28 17:54:28 +090073 },
74 [MAX77686_CLK_CP] = {
75 .name = "32khz_cp",
Laxman Dewangan8ad313f2016-06-17 16:21:04 +053076 .clk_reg = MAX77686_REG_32KHZ,
77 .clk_enable_mask = BIT(MAX77686_CLK_CP),
Jonghwa Lee73118e62012-08-28 17:54:28 +090078 },
79 [MAX77686_CLK_PMIC] = {
80 .name = "32khz_pmic",
Laxman Dewangan8ad313f2016-06-17 16:21:04 +053081 .clk_reg = MAX77686_REG_32KHZ,
82 .clk_enable_mask = BIT(MAX77686_CLK_PMIC),
Jonghwa Lee73118e62012-08-28 17:54:28 +090083 },
84};
85
Laxman Dewangan8ad313f2016-06-17 16:21:04 +053086static const struct
87max77686_hw_clk_info max77802_hw_clks_info[MAX77802_CLKS_NUM] = {
88 [MAX77802_CLK_32K_AP] = {
89 .name = "32khz_ap",
90 .clk_reg = MAX77802_REG_32KHZ,
91 .clk_enable_mask = BIT(MAX77802_CLK_32K_AP),
92 },
93 [MAX77802_CLK_32K_CP] = {
94 .name = "32khz_cp",
95 .clk_reg = MAX77802_REG_32KHZ,
96 .clk_enable_mask = BIT(MAX77802_CLK_32K_CP),
97 },
98};
99
100static struct max77686_clk_init_data *to_max77686_clk_init_data(
101 struct clk_hw *hw)
102{
103 return container_of(hw, struct max77686_clk_init_data, hw);
104}
105
106static int max77686_clk_prepare(struct clk_hw *hw)
107{
108 struct max77686_clk_init_data *max77686 = to_max77686_clk_init_data(hw);
109
110 return regmap_update_bits(max77686->regmap, max77686->clk_info->clk_reg,
111 max77686->clk_info->clk_enable_mask,
112 max77686->clk_info->clk_enable_mask);
113}
114
115static void max77686_clk_unprepare(struct clk_hw *hw)
116{
117 struct max77686_clk_init_data *max77686 = to_max77686_clk_init_data(hw);
118
119 regmap_update_bits(max77686->regmap, max77686->clk_info->clk_reg,
120 max77686->clk_info->clk_enable_mask,
121 ~max77686->clk_info->clk_enable_mask);
122}
123
124static int max77686_clk_is_prepared(struct clk_hw *hw)
125{
126 struct max77686_clk_init_data *max77686 = to_max77686_clk_init_data(hw);
127 int ret;
128 u32 val;
129
130 ret = regmap_read(max77686->regmap, max77686->clk_info->clk_reg, &val);
131
132 if (ret < 0)
133 return -EINVAL;
134
135 return val & max77686->clk_info->clk_enable_mask;
136}
137
138static unsigned long max77686_recalc_rate(struct clk_hw *hw,
139 unsigned long parent_rate)
140{
141 return 32768;
142}
143
144static struct clk_ops max77686_clk_ops = {
145 .prepare = max77686_clk_prepare,
146 .unprepare = max77686_clk_unprepare,
147 .is_prepared = max77686_clk_is_prepared,
148 .recalc_rate = max77686_recalc_rate,
149};
150
Bill Pemberton018ae932012-11-19 13:22:52 -0500151static int max77686_clk_probe(struct platform_device *pdev)
Jonghwa Lee73118e62012-08-28 17:54:28 +0900152{
Laxman Dewangan8ad313f2016-06-17 16:21:04 +0530153 struct device *dev = &pdev->dev;
154 struct device *parent = dev->parent;
155 const struct platform_device_id *id = platform_get_device_id(pdev);
156 struct max77686_clk_driver_data *drv_data;
157 const struct max77686_hw_clk_info *hw_clks;
158 struct regmap *regmap;
159 int i, ret, num_clks;
Jonghwa Lee73118e62012-08-28 17:54:28 +0900160
Laxman Dewangan8ad313f2016-06-17 16:21:04 +0530161 drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL);
162 if (!drv_data)
163 return -ENOMEM;
164
165 regmap = dev_get_regmap(parent, NULL);
166 if (!regmap) {
167 dev_err(dev, "Failed to get rtc regmap\n");
168 return -ENODEV;
169 }
170
171 drv_data->chip = id->driver_data;
172
173 switch (drv_data->chip) {
174 case CHIP_MAX77686:
175 num_clks = MAX77686_CLKS_NUM;
176 hw_clks = max77686_hw_clks_info;
177 break;
178
179 case CHIP_MAX77802:
180 num_clks = MAX77802_CLKS_NUM;
181 hw_clks = max77802_hw_clks_info;
182 break;
183
184 default:
185 dev_err(dev, "Unknown Chip ID\n");
186 return -EINVAL;
187 }
188
189 drv_data->max_clk_data = devm_kcalloc(dev, num_clks,
190 sizeof(*drv_data->max_clk_data),
191 GFP_KERNEL);
192 if (!drv_data->max_clk_data)
193 return -ENOMEM;
194
195 drv_data->clks = devm_kcalloc(dev, num_clks,
196 sizeof(*drv_data->clks), GFP_KERNEL);
197 if (!drv_data->clks)
198 return -ENOMEM;
199
200 for (i = 0; i < num_clks; i++) {
201 struct max77686_clk_init_data *max_clk_data;
202 struct clk *clk;
203 const char *clk_name;
204
205 max_clk_data = &drv_data->max_clk_data[i];
206
207 max_clk_data->regmap = regmap;
208 max_clk_data->clk_info = &hw_clks[i];
209 max_clk_data->clk_idata.flags = hw_clks[i].flags;
210 max_clk_data->clk_idata.ops = &max77686_clk_ops;
211
212 if (parent->of_node &&
213 !of_property_read_string_index(parent->of_node,
214 "clock-output-names",
215 i, &clk_name))
216 max_clk_data->clk_idata.name = clk_name;
217 else
218 max_clk_data->clk_idata.name = hw_clks[i].name;
219
220 max_clk_data->hw.init = &max_clk_data->clk_idata;
221
222 clk = devm_clk_register(dev, &max_clk_data->hw);
223 if (IS_ERR(clk)) {
224 ret = PTR_ERR(clk);
225 dev_err(dev, "Failed to clock register: %d\n", ret);
226 return ret;
227 }
228
229 ret = clk_register_clkdev(clk, max_clk_data->clk_idata.name,
230 NULL);
231 if (ret < 0) {
232 dev_err(dev, "Failed to clkdev register: %d\n", ret);
233 return ret;
234 }
235 drv_data->clks[i] = clk;
236 }
237
238 platform_set_drvdata(pdev, drv_data);
239
240 if (parent->of_node) {
241 drv_data->of_data.clks = drv_data->clks;
242 drv_data->of_data.clk_num = num_clks;
243 ret = of_clk_add_provider(parent->of_node,
244 of_clk_src_onecell_get,
245 &drv_data->of_data);
246
247 if (ret < 0) {
248 dev_err(dev, "Failed to register OF clock provider: %d\n",
249 ret);
250 return ret;
251 }
252 }
253
254 /* MAX77802: Enable low-jitter mode on the 32khz clocks. */
255 if (drv_data->chip == CHIP_MAX77802) {
256 ret = regmap_update_bits(regmap, MAX77802_REG_32KHZ,
257 1 << MAX77802_CLOCK_LOW_JITTER_SHIFT,
258 1 << MAX77802_CLOCK_LOW_JITTER_SHIFT);
259 if (ret < 0) {
260 dev_err(dev, "Failed to config low-jitter: %d\n", ret);
261 goto remove_of_clk_provider;
262 }
263 }
264
265 return 0;
266
267remove_of_clk_provider:
268 if (parent->of_node)
269 of_clk_del_provider(parent->of_node);
270
271 return ret;
Jonghwa Lee73118e62012-08-28 17:54:28 +0900272}
273
Bill Pemberton1fc7ad52012-11-19 13:25:43 -0500274static int max77686_clk_remove(struct platform_device *pdev)
Jonghwa Lee73118e62012-08-28 17:54:28 +0900275{
Laxman Dewangan8ad313f2016-06-17 16:21:04 +0530276 struct device *parent = pdev->dev.parent;
277
278 if (parent->of_node)
279 of_clk_del_provider(parent->of_node);
280
281 return 0;
Jonghwa Lee73118e62012-08-28 17:54:28 +0900282}
283
284static const struct platform_device_id max77686_clk_id[] = {
Laxman Dewangan8ad313f2016-06-17 16:21:04 +0530285 { "max77686-clk", .driver_data = CHIP_MAX77686, },
286 { "max77802-clk", .driver_data = CHIP_MAX77802, },
287 {},
Jonghwa Lee73118e62012-08-28 17:54:28 +0900288};
289MODULE_DEVICE_TABLE(platform, max77686_clk_id);
290
291static struct platform_driver max77686_clk_driver = {
292 .driver = {
293 .name = "max77686-clk",
Jonghwa Lee73118e62012-08-28 17:54:28 +0900294 },
295 .probe = max77686_clk_probe,
Bill Pembertonf9cfa632012-11-19 13:19:59 -0500296 .remove = max77686_clk_remove,
Jonghwa Lee73118e62012-08-28 17:54:28 +0900297 .id_table = max77686_clk_id,
298};
299
Javier Martinez Canillas1887d692014-08-18 10:33:01 +0200300module_platform_driver(max77686_clk_driver);
Jonghwa Lee73118e62012-08-28 17:54:28 +0900301
302MODULE_DESCRIPTION("MAXIM 77686 Clock Driver");
303MODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>");
304MODULE_LICENSE("GPL");