1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * ROHM BD9571MWV-M and BD9574MWF-M regulator driver |
4 | * |
5 | * Copyright (C) 2017 Marek Vasut <marek.vasut+renesas@gmail.com> |
6 | * |
7 | * Based on the TPS65086 driver |
8 | * |
9 | * NOTE: VD09 is missing |
10 | */ |
11 | |
12 | #include <linux/mfd/rohm-generic.h> |
13 | #include <linux/module.h> |
14 | #include <linux/of.h> |
15 | #include <linux/platform_device.h> |
16 | #include <linux/regulator/driver.h> |
17 | |
18 | #include <linux/mfd/bd9571mwv.h> |
19 | |
20 | struct bd9571mwv_reg { |
21 | struct regmap *regmap; |
22 | |
23 | /* DDR Backup Power */ |
24 | u8 bkup_mode_cnt_keepon; /* from "rohm,ddr-backup-power" */ |
25 | u8 bkup_mode_cnt_saved; |
26 | bool bkup_mode_enabled; |
27 | |
28 | /* Power switch type */ |
29 | bool rstbmode_level; |
30 | bool rstbmode_pulse; |
31 | }; |
32 | |
33 | enum bd9571mwv_regulators { VD09, VD18, VD25, VD33, DVFS }; |
34 | |
35 | #define BD9571MWV_REG(_name, _of, _id, _ops, _vr, _vm, _nv, _min, _step, _lmin)\ |
36 | { \ |
37 | .name = _name, \ |
38 | .of_match = of_match_ptr(_of), \ |
39 | .regulators_node = "regulators", \ |
40 | .id = _id, \ |
41 | .ops = &_ops, \ |
42 | .n_voltages = _nv, \ |
43 | .type = REGULATOR_VOLTAGE, \ |
44 | .owner = THIS_MODULE, \ |
45 | .vsel_reg = _vr, \ |
46 | .vsel_mask = _vm, \ |
47 | .min_uV = _min, \ |
48 | .uV_step = _step, \ |
49 | .linear_min_sel = _lmin, \ |
50 | } |
51 | |
52 | static int bd9571mwv_avs_get_moni_state(struct regulator_dev *rdev) |
53 | { |
54 | unsigned int val; |
55 | int ret; |
56 | |
57 | ret = regmap_read(map: rdev->regmap, BD9571MWV_AVS_SET_MONI, val: &val); |
58 | if (ret != 0) |
59 | return ret; |
60 | |
61 | return val & BD9571MWV_AVS_SET_MONI_MASK; |
62 | } |
63 | |
64 | static int bd9571mwv_avs_set_voltage_sel_regmap(struct regulator_dev *rdev, |
65 | unsigned int sel) |
66 | { |
67 | int ret; |
68 | |
69 | ret = bd9571mwv_avs_get_moni_state(rdev); |
70 | if (ret < 0) |
71 | return ret; |
72 | |
73 | return regmap_write_bits(map: rdev->regmap, BD9571MWV_AVS_VD09_VID(ret), |
74 | mask: rdev->desc->vsel_mask, val: sel); |
75 | } |
76 | |
77 | static int bd9571mwv_avs_get_voltage_sel_regmap(struct regulator_dev *rdev) |
78 | { |
79 | unsigned int val; |
80 | int ret; |
81 | |
82 | ret = bd9571mwv_avs_get_moni_state(rdev); |
83 | if (ret < 0) |
84 | return ret; |
85 | |
86 | ret = regmap_read(map: rdev->regmap, BD9571MWV_AVS_VD09_VID(ret), val: &val); |
87 | if (ret != 0) |
88 | return ret; |
89 | |
90 | val &= rdev->desc->vsel_mask; |
91 | val >>= ffs(rdev->desc->vsel_mask) - 1; |
92 | |
93 | return val; |
94 | } |
95 | |
96 | static int bd9571mwv_reg_set_voltage_sel_regmap(struct regulator_dev *rdev, |
97 | unsigned int sel) |
98 | { |
99 | return regmap_write_bits(map: rdev->regmap, BD9571MWV_DVFS_SETVID, |
100 | mask: rdev->desc->vsel_mask, val: sel); |
101 | } |
102 | |
103 | /* Operations permitted on AVS voltage regulator */ |
104 | static const struct regulator_ops avs_ops = { |
105 | .set_voltage_sel = bd9571mwv_avs_set_voltage_sel_regmap, |
106 | .map_voltage = regulator_map_voltage_linear, |
107 | .get_voltage_sel = bd9571mwv_avs_get_voltage_sel_regmap, |
108 | .list_voltage = regulator_list_voltage_linear, |
109 | }; |
110 | |
111 | /* Operations permitted on voltage regulators */ |
112 | static const struct regulator_ops reg_ops = { |
113 | .set_voltage_sel = bd9571mwv_reg_set_voltage_sel_regmap, |
114 | .map_voltage = regulator_map_voltage_linear, |
115 | .get_voltage_sel = regulator_get_voltage_sel_regmap, |
116 | .list_voltage = regulator_list_voltage_linear, |
117 | }; |
118 | |
119 | /* Operations permitted on voltage monitors */ |
120 | static const struct regulator_ops vid_ops = { |
121 | .map_voltage = regulator_map_voltage_linear, |
122 | .get_voltage_sel = regulator_get_voltage_sel_regmap, |
123 | .list_voltage = regulator_list_voltage_linear, |
124 | }; |
125 | |
126 | static const struct regulator_desc regulators[] = { |
127 | BD9571MWV_REG("VD09" , "vd09" , VD09, avs_ops, 0, 0x7f, |
128 | 0x6f, 600000, 10000, 0x3c), |
129 | BD9571MWV_REG("VD18" , "vd18" , VD18, vid_ops, BD9571MWV_VD18_VID, 0xf, |
130 | 16, 1625000, 25000, 0), |
131 | BD9571MWV_REG("VD25" , "vd25" , VD25, vid_ops, BD9571MWV_VD25_VID, 0xf, |
132 | 16, 2150000, 50000, 0), |
133 | BD9571MWV_REG("VD33" , "vd33" , VD33, vid_ops, BD9571MWV_VD33_VID, 0xf, |
134 | 11, 2800000, 100000, 0), |
135 | BD9571MWV_REG("DVFS" , "dvfs" , DVFS, reg_ops, |
136 | BD9571MWV_DVFS_MONIVDAC, 0x7f, |
137 | 0x6f, 600000, 10000, 0x3c), |
138 | }; |
139 | |
140 | #ifdef CONFIG_PM_SLEEP |
141 | static int bd9571mwv_bkup_mode_read(struct bd9571mwv_reg *bdreg, |
142 | unsigned int *mode) |
143 | { |
144 | int ret; |
145 | |
146 | ret = regmap_read(map: bdreg->regmap, BD9571MWV_BKUP_MODE_CNT, val: mode); |
147 | if (ret) { |
148 | dev_err(regmap_get_device(bdreg->regmap), |
149 | "failed to read backup mode (%d)\n" , ret); |
150 | return ret; |
151 | } |
152 | |
153 | return 0; |
154 | } |
155 | |
156 | static int bd9571mwv_bkup_mode_write(struct bd9571mwv_reg *bdreg, |
157 | unsigned int mode) |
158 | { |
159 | int ret; |
160 | |
161 | ret = regmap_write(map: bdreg->regmap, BD9571MWV_BKUP_MODE_CNT, val: mode); |
162 | if (ret) { |
163 | dev_err(regmap_get_device(bdreg->regmap), |
164 | "failed to configure backup mode 0x%x (%d)\n" , |
165 | mode, ret); |
166 | return ret; |
167 | } |
168 | |
169 | return 0; |
170 | } |
171 | |
172 | static ssize_t backup_mode_show(struct device *dev, |
173 | struct device_attribute *attr, char *buf) |
174 | { |
175 | struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev); |
176 | |
177 | return sysfs_emit(buf, fmt: "%s\n" , bdreg->bkup_mode_enabled ? "on" : "off" ); |
178 | } |
179 | |
180 | static ssize_t backup_mode_store(struct device *dev, |
181 | struct device_attribute *attr, |
182 | const char *buf, size_t count) |
183 | { |
184 | struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev); |
185 | unsigned int mode; |
186 | int ret; |
187 | |
188 | if (!count) |
189 | return 0; |
190 | |
191 | ret = kstrtobool(s: buf, res: &bdreg->bkup_mode_enabled); |
192 | if (ret) |
193 | return ret; |
194 | |
195 | if (!bdreg->rstbmode_level) |
196 | return count; |
197 | |
198 | /* |
199 | * Configure DDR Backup Mode, to change the role of the accessory power |
200 | * switch from a power switch to a wake-up switch, or vice versa |
201 | */ |
202 | ret = bd9571mwv_bkup_mode_read(bdreg, mode: &mode); |
203 | if (ret) |
204 | return ret; |
205 | |
206 | mode &= ~BD9571MWV_BKUP_MODE_CNT_KEEPON_MASK; |
207 | if (bdreg->bkup_mode_enabled) |
208 | mode |= bdreg->bkup_mode_cnt_keepon; |
209 | |
210 | ret = bd9571mwv_bkup_mode_write(bdreg, mode); |
211 | if (ret) |
212 | return ret; |
213 | |
214 | return count; |
215 | } |
216 | |
217 | static DEVICE_ATTR_RW(backup_mode); |
218 | |
219 | static int bd9571mwv_suspend(struct device *dev) |
220 | { |
221 | struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev); |
222 | unsigned int mode; |
223 | int ret; |
224 | |
225 | if (!bdreg->bkup_mode_enabled) |
226 | return 0; |
227 | |
228 | /* Save DDR Backup Mode */ |
229 | ret = bd9571mwv_bkup_mode_read(bdreg, mode: &mode); |
230 | if (ret) |
231 | return ret; |
232 | |
233 | bdreg->bkup_mode_cnt_saved = mode; |
234 | |
235 | if (!bdreg->rstbmode_pulse) |
236 | return 0; |
237 | |
238 | /* Enable DDR Backup Mode */ |
239 | mode &= ~BD9571MWV_BKUP_MODE_CNT_KEEPON_MASK; |
240 | mode |= bdreg->bkup_mode_cnt_keepon; |
241 | |
242 | if (mode != bdreg->bkup_mode_cnt_saved) |
243 | return bd9571mwv_bkup_mode_write(bdreg, mode); |
244 | |
245 | return 0; |
246 | } |
247 | |
248 | static int bd9571mwv_resume(struct device *dev) |
249 | { |
250 | struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev); |
251 | |
252 | if (!bdreg->bkup_mode_enabled) |
253 | return 0; |
254 | |
255 | /* Restore DDR Backup Mode */ |
256 | return bd9571mwv_bkup_mode_write(bdreg, mode: bdreg->bkup_mode_cnt_saved); |
257 | } |
258 | |
259 | static const struct dev_pm_ops bd9571mwv_pm = { |
260 | SET_SYSTEM_SLEEP_PM_OPS(bd9571mwv_suspend, bd9571mwv_resume) |
261 | }; |
262 | |
263 | static void bd9571mwv_regulator_remove(struct platform_device *pdev) |
264 | { |
265 | device_remove_file(dev: &pdev->dev, attr: &dev_attr_backup_mode); |
266 | } |
267 | #define DEV_PM_OPS &bd9571mwv_pm |
268 | #else |
269 | #define DEV_PM_OPS NULL |
270 | #define bd9571mwv_regulator_remove NULL |
271 | #endif /* CONFIG_PM_SLEEP */ |
272 | |
273 | static int bd9571mwv_regulator_probe(struct platform_device *pdev) |
274 | { |
275 | struct regulator_config config = { }; |
276 | struct bd9571mwv_reg *bdreg; |
277 | struct regulator_dev *rdev; |
278 | unsigned int val; |
279 | int i; |
280 | enum rohm_chip_type chip = platform_get_device_id(pdev)->driver_data; |
281 | |
282 | bdreg = devm_kzalloc(dev: &pdev->dev, size: sizeof(*bdreg), GFP_KERNEL); |
283 | if (!bdreg) |
284 | return -ENOMEM; |
285 | |
286 | bdreg->regmap = dev_get_regmap(dev: pdev->dev.parent, NULL); |
287 | |
288 | platform_set_drvdata(pdev, data: bdreg); |
289 | |
290 | config.dev = &pdev->dev; |
291 | config.dev->of_node = pdev->dev.parent->of_node; |
292 | config.driver_data = bdreg; |
293 | config.regmap = bdreg->regmap; |
294 | |
295 | for (i = 0; i < ARRAY_SIZE(regulators); i++) { |
296 | /* BD9574MWF supports DVFS only */ |
297 | if (chip == ROHM_CHIP_TYPE_BD9574 && regulators[i].id != DVFS) |
298 | continue; |
299 | rdev = devm_regulator_register(dev: &pdev->dev, regulator_desc: ®ulators[i], |
300 | config: &config); |
301 | if (IS_ERR(ptr: rdev)) { |
302 | dev_err(&pdev->dev, "failed to register %s regulator\n" , |
303 | regulators[i].name); |
304 | return PTR_ERR(ptr: rdev); |
305 | } |
306 | } |
307 | |
308 | val = 0; |
309 | of_property_read_u32(np: config.dev->of_node, propname: "rohm,ddr-backup-power" , out_value: &val); |
310 | if (val & ~BD9571MWV_BKUP_MODE_CNT_KEEPON_MASK) { |
311 | dev_err(&pdev->dev, "invalid %s mode %u\n" , |
312 | "rohm,ddr-backup-power" , val); |
313 | return -EINVAL; |
314 | } |
315 | bdreg->bkup_mode_cnt_keepon = val; |
316 | |
317 | bdreg->rstbmode_level = of_property_read_bool(np: config.dev->of_node, |
318 | propname: "rohm,rstbmode-level" ); |
319 | bdreg->rstbmode_pulse = of_property_read_bool(np: config.dev->of_node, |
320 | propname: "rohm,rstbmode-pulse" ); |
321 | if (bdreg->rstbmode_level && bdreg->rstbmode_pulse) { |
322 | dev_err(&pdev->dev, "only one rohm,rstbmode-* may be specified" ); |
323 | return -EINVAL; |
324 | } |
325 | |
326 | #ifdef CONFIG_PM_SLEEP |
327 | if (bdreg->bkup_mode_cnt_keepon) { |
328 | int ret; |
329 | |
330 | /* |
331 | * Backup mode is enabled by default in pulse mode, but needs |
332 | * explicit user setup in level mode. |
333 | */ |
334 | bdreg->bkup_mode_enabled = bdreg->rstbmode_pulse; |
335 | |
336 | ret = device_create_file(device: &pdev->dev, entry: &dev_attr_backup_mode); |
337 | if (ret) |
338 | return ret; |
339 | } |
340 | #endif /* CONFIG_PM_SLEEP */ |
341 | |
342 | return 0; |
343 | } |
344 | |
345 | static const struct platform_device_id bd9571mwv_regulator_id_table[] = { |
346 | { "bd9571mwv-regulator" , ROHM_CHIP_TYPE_BD9571 }, |
347 | { "bd9574mwf-regulator" , ROHM_CHIP_TYPE_BD9574 }, |
348 | { /* sentinel */ } |
349 | }; |
350 | MODULE_DEVICE_TABLE(platform, bd9571mwv_regulator_id_table); |
351 | |
352 | static struct platform_driver bd9571mwv_regulator_driver = { |
353 | .driver = { |
354 | .name = "bd9571mwv-regulator" , |
355 | .probe_type = PROBE_PREFER_ASYNCHRONOUS, |
356 | .pm = DEV_PM_OPS, |
357 | }, |
358 | .probe = bd9571mwv_regulator_probe, |
359 | .remove_new = bd9571mwv_regulator_remove, |
360 | .id_table = bd9571mwv_regulator_id_table, |
361 | }; |
362 | module_platform_driver(bd9571mwv_regulator_driver); |
363 | |
364 | MODULE_AUTHOR("Marek Vasut <marek.vasut+renesas@gmail.com>" ); |
365 | MODULE_DESCRIPTION("BD9571MWV Regulator driver" ); |
366 | MODULE_LICENSE("GPL v2" ); |
367 | |