]>
Commit | Line | Data |
---|---|---|
b014e9fa MV |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (C) 2020 ROHM Semiconductors | |
3 | // ROHM BD9576MUF/BD9573MUF regulator driver | |
4 | ||
5 | #include <linux/delay.h> | |
6 | #include <linux/err.h> | |
7 | #include <linux/gpio/consumer.h> | |
8 | #include <linux/interrupt.h> | |
9 | #include <linux/kernel.h> | |
10 | #include <linux/mfd/rohm-bd957x.h> | |
11 | #include <linux/mfd/rohm-generic.h> | |
12 | #include <linux/module.h> | |
13 | #include <linux/of.h> | |
14 | #include <linux/platform_device.h> | |
15 | #include <linux/regulator/driver.h> | |
16 | #include <linux/regulator/machine.h> | |
17 | #include <linux/regulator/of_regulator.h> | |
18 | #include <linux/slab.h> | |
19 | ||
20 | #define BD957X_VOUTS1_VOLT 3300000 | |
21 | #define BD957X_VOUTS4_BASE_VOLT 1030000 | |
22 | #define BD957X_VOUTS34_NUM_VOLT 32 | |
23 | ||
24 | static int vout1_volt_table[] = {5000000, 4900000, 4800000, 4700000, 4600000, | |
25 | 4500000, 4500000, 4500000, 5000000, 5100000, | |
26 | 5200000, 5300000, 5400000, 5500000, 5500000, | |
27 | 5500000}; | |
28 | ||
29 | static int vout2_volt_table[] = {1800000, 1780000, 1760000, 1740000, 1720000, | |
30 | 1700000, 1680000, 1660000, 1800000, 1820000, | |
31 | 1840000, 1860000, 1880000, 1900000, 1920000, | |
32 | 1940000}; | |
33 | ||
34 | static int voutl1_volt_table[] = {2500000, 2540000, 2580000, 2620000, 2660000, | |
35 | 2700000, 2740000, 2780000, 2500000, 2460000, | |
36 | 2420000, 2380000, 2340000, 2300000, 2260000, | |
37 | 2220000}; | |
38 | ||
39 | struct bd957x_regulator_data { | |
40 | struct regulator_desc desc; | |
41 | int base_voltage; | |
42 | }; | |
43 | ||
44 | static int bd957x_vout34_list_voltage(struct regulator_dev *rdev, | |
45 | unsigned int selector) | |
46 | { | |
47 | const struct regulator_desc *desc = rdev->desc; | |
48 | int multiplier = selector & desc->vsel_mask & 0x7f; | |
49 | int tune; | |
50 | ||
51 | /* VOUT3 and 4 has 10mV step */ | |
52 | tune = multiplier * 10000; | |
53 | ||
54 | if (!(selector & 0x80)) | |
55 | return desc->fixed_uV - tune; | |
56 | ||
57 | return desc->fixed_uV + tune; | |
58 | } | |
59 | ||
60 | static int bd957x_list_voltage(struct regulator_dev *rdev, | |
61 | unsigned int selector) | |
62 | { | |
63 | const struct regulator_desc *desc = rdev->desc; | |
64 | int index = selector & desc->vsel_mask & 0x7f; | |
65 | ||
66 | if (!(selector & 0x80)) | |
67 | index += desc->n_voltages/2; | |
68 | ||
69 | if (index >= desc->n_voltages) | |
70 | return -EINVAL; | |
71 | ||
72 | return desc->volt_table[index]; | |
73 | } | |
74 | ||
75 | static const struct regulator_ops bd957x_vout34_ops = { | |
76 | .is_enabled = regulator_is_enabled_regmap, | |
77 | .list_voltage = bd957x_vout34_list_voltage, | |
78 | .get_voltage_sel = regulator_get_voltage_sel_regmap, | |
79 | }; | |
80 | ||
81 | static const struct regulator_ops bd957X_vouts1_regulator_ops = { | |
82 | .is_enabled = regulator_is_enabled_regmap, | |
83 | }; | |
84 | ||
85 | static const struct regulator_ops bd957x_ops = { | |
86 | .is_enabled = regulator_is_enabled_regmap, | |
87 | .list_voltage = bd957x_list_voltage, | |
88 | .get_voltage_sel = regulator_get_voltage_sel_regmap, | |
89 | }; | |
90 | ||
91 | static struct bd957x_regulator_data bd9576_regulators[] = { | |
92 | { | |
93 | .desc = { | |
94 | .name = "VD50", | |
95 | .of_match = of_match_ptr("regulator-vd50"), | |
96 | .regulators_node = of_match_ptr("regulators"), | |
97 | .id = BD957X_VD50, | |
98 | .type = REGULATOR_VOLTAGE, | |
99 | .ops = &bd957x_ops, | |
100 | .volt_table = &vout1_volt_table[0], | |
101 | .n_voltages = ARRAY_SIZE(vout1_volt_table), | |
102 | .vsel_reg = BD957X_REG_VOUT1_TUNE, | |
103 | .vsel_mask = BD957X_MASK_VOUT1_TUNE, | |
104 | .enable_reg = BD957X_REG_POW_TRIGGER1, | |
105 | .enable_mask = BD957X_REGULATOR_EN_MASK, | |
106 | .enable_val = BD957X_REGULATOR_DIS_VAL, | |
107 | .enable_is_inverted = true, | |
108 | .owner = THIS_MODULE, | |
109 | }, | |
110 | }, | |
111 | { | |
112 | .desc = { | |
113 | .name = "VD18", | |
114 | .of_match = of_match_ptr("regulator-vd18"), | |
115 | .regulators_node = of_match_ptr("regulators"), | |
116 | .id = BD957X_VD18, | |
117 | .type = REGULATOR_VOLTAGE, | |
118 | .ops = &bd957x_ops, | |
119 | .volt_table = &vout2_volt_table[0], | |
120 | .n_voltages = ARRAY_SIZE(vout2_volt_table), | |
121 | .vsel_reg = BD957X_REG_VOUT2_TUNE, | |
122 | .vsel_mask = BD957X_MASK_VOUT2_TUNE, | |
123 | .enable_reg = BD957X_REG_POW_TRIGGER2, | |
124 | .enable_mask = BD957X_REGULATOR_EN_MASK, | |
125 | .enable_val = BD957X_REGULATOR_DIS_VAL, | |
126 | .enable_is_inverted = true, | |
127 | .owner = THIS_MODULE, | |
128 | }, | |
129 | }, | |
130 | { | |
131 | .desc = { | |
132 | .name = "VDDDR", | |
133 | .of_match = of_match_ptr("regulator-vdddr"), | |
134 | .regulators_node = of_match_ptr("regulators"), | |
135 | .id = BD957X_VDDDR, | |
136 | .ops = &bd957x_vout34_ops, | |
137 | .type = REGULATOR_VOLTAGE, | |
138 | .n_voltages = BD957X_VOUTS34_NUM_VOLT, | |
139 | .vsel_reg = BD957X_REG_VOUT3_TUNE, | |
140 | .vsel_mask = BD957X_MASK_VOUT3_TUNE, | |
141 | .enable_reg = BD957X_REG_POW_TRIGGER3, | |
142 | .enable_mask = BD957X_REGULATOR_EN_MASK, | |
143 | .enable_val = BD957X_REGULATOR_DIS_VAL, | |
144 | .enable_is_inverted = true, | |
145 | .owner = THIS_MODULE, | |
146 | }, | |
147 | }, | |
148 | { | |
149 | .desc = { | |
150 | .name = "VD10", | |
151 | .of_match = of_match_ptr("regulator-vd10"), | |
152 | .regulators_node = of_match_ptr("regulators"), | |
153 | .id = BD957X_VD10, | |
154 | .ops = &bd957x_vout34_ops, | |
155 | .type = REGULATOR_VOLTAGE, | |
156 | .fixed_uV = BD957X_VOUTS4_BASE_VOLT, | |
157 | .n_voltages = BD957X_VOUTS34_NUM_VOLT, | |
158 | .vsel_reg = BD957X_REG_VOUT4_TUNE, | |
159 | .vsel_mask = BD957X_MASK_VOUT4_TUNE, | |
160 | .enable_reg = BD957X_REG_POW_TRIGGER4, | |
161 | .enable_mask = BD957X_REGULATOR_EN_MASK, | |
162 | .enable_val = BD957X_REGULATOR_DIS_VAL, | |
163 | .enable_is_inverted = true, | |
164 | .owner = THIS_MODULE, | |
165 | }, | |
166 | }, | |
167 | { | |
168 | .desc = { | |
169 | .name = "VOUTL1", | |
170 | .of_match = of_match_ptr("regulator-voutl1"), | |
171 | .regulators_node = of_match_ptr("regulators"), | |
172 | .id = BD957X_VOUTL1, | |
173 | .ops = &bd957x_ops, | |
174 | .type = REGULATOR_VOLTAGE, | |
175 | .volt_table = &voutl1_volt_table[0], | |
176 | .n_voltages = ARRAY_SIZE(voutl1_volt_table), | |
177 | .vsel_reg = BD957X_REG_VOUTL1_TUNE, | |
178 | .vsel_mask = BD957X_MASK_VOUTL1_TUNE, | |
179 | .enable_reg = BD957X_REG_POW_TRIGGERL1, | |
180 | .enable_mask = BD957X_REGULATOR_EN_MASK, | |
181 | .enable_val = BD957X_REGULATOR_DIS_VAL, | |
182 | .enable_is_inverted = true, | |
183 | .owner = THIS_MODULE, | |
184 | }, | |
185 | }, | |
186 | { | |
187 | .desc = { | |
188 | .name = "VOUTS1", | |
189 | .of_match = of_match_ptr("regulator-vouts1"), | |
190 | .regulators_node = of_match_ptr("regulators"), | |
191 | .id = BD957X_VOUTS1, | |
192 | .ops = &bd957X_vouts1_regulator_ops, | |
193 | .type = REGULATOR_VOLTAGE, | |
194 | .n_voltages = 1, | |
195 | .fixed_uV = BD957X_VOUTS1_VOLT, | |
196 | .enable_reg = BD957X_REG_POW_TRIGGERS1, | |
197 | .enable_mask = BD957X_REGULATOR_EN_MASK, | |
198 | .enable_val = BD957X_REGULATOR_DIS_VAL, | |
199 | .enable_is_inverted = true, | |
200 | .owner = THIS_MODULE, | |
201 | }, | |
202 | }, | |
203 | }; | |
204 | ||
205 | static int bd957x_probe(struct platform_device *pdev) | |
206 | { | |
207 | struct regmap *regmap; | |
208 | struct regulator_config config = { 0 }; | |
209 | int i, err; | |
210 | bool vout_mode, ddr_sel; | |
211 | const struct bd957x_regulator_data *reg_data = &bd9576_regulators[0]; | |
212 | unsigned int num_reg_data = ARRAY_SIZE(bd9576_regulators); | |
213 | enum rohm_chip_type chip = platform_get_device_id(pdev)->driver_data; | |
214 | ||
215 | regmap = dev_get_regmap(pdev->dev.parent, NULL); | |
216 | if (!regmap) { | |
217 | dev_err(&pdev->dev, "No regmap\n"); | |
218 | return -EINVAL; | |
219 | } | |
220 | vout_mode = of_property_read_bool(pdev->dev.parent->of_node, | |
221 | "rohm,vout1-en-low"); | |
222 | if (vout_mode) { | |
223 | struct gpio_desc *en; | |
224 | ||
225 | dev_dbg(&pdev->dev, "GPIO controlled mode\n"); | |
226 | ||
227 | /* VOUT1 enable state judged by VOUT1_EN pin */ | |
228 | /* See if we have GPIO defined */ | |
229 | en = devm_gpiod_get_from_of_node(&pdev->dev, | |
230 | pdev->dev.parent->of_node, | |
231 | "rohm,vout1-en-gpios", 0, | |
232 | GPIOD_OUT_LOW, "vout1-en"); | |
233 | if (!IS_ERR(en)) { | |
234 | /* VOUT1_OPS gpio ctrl */ | |
235 | /* | |
236 | * Regulator core prioritizes the ena_gpio over | |
237 | * enable/disable/is_enabled callbacks so no need to | |
238 | * clear them. We can still use same ops | |
239 | */ | |
240 | config.ena_gpiod = en; | |
241 | } else { | |
242 | /* | |
243 | * In theory it is possible someone wants to set | |
244 | * vout1-en LOW during OTP loading and set VOUT1 to be | |
245 | * controlled by GPIO - but control the GPIO from some | |
246 | * where else than this driver. For that to work we | |
247 | * should unset the is_enabled callback here. | |
248 | * | |
249 | * I believe such case where rohm,vout1-en-low is set | |
250 | * and vout1-en-gpios is not is likely to be a | |
251 | * misconfiguration. So let's just err out for now. | |
252 | */ | |
253 | dev_err(&pdev->dev, | |
254 | "Failed to get VOUT1 control GPIO\n"); | |
255 | return PTR_ERR(en); | |
256 | } | |
257 | } | |
258 | ||
259 | /* | |
260 | * If more than one PMIC needs to be controlled by same processor then | |
261 | * allocate the regulator data array here and use bd9576_regulators as | |
262 | * template. At the moment I see no such use-case so I spare some | |
263 | * bytes and use bd9576_regulators directly for non-constant configs | |
264 | * like DDR voltage selection. | |
265 | */ | |
266 | ddr_sel = of_property_read_bool(pdev->dev.parent->of_node, | |
267 | "rohm,ddr-sel-low"); | |
268 | if (ddr_sel) | |
269 | bd9576_regulators[2].desc.fixed_uV = 1350000; | |
270 | else | |
271 | bd9576_regulators[2].desc.fixed_uV = 1500000; | |
272 | ||
273 | switch (chip) { | |
274 | case ROHM_CHIP_TYPE_BD9576: | |
275 | dev_dbg(&pdev->dev, "Found BD9576MUF\n"); | |
276 | break; | |
277 | case ROHM_CHIP_TYPE_BD9573: | |
278 | pr_info(&pdev->dev, "Found BD9573MUF\n"); | |
279 | break; | |
280 | default: | |
281 | dev_err(&pdev->dev, "Unsupported chip type\n"); | |
282 | err = -EINVAL; | |
283 | goto err; | |
284 | } | |
285 | ||
286 | config.dev = pdev->dev.parent; | |
287 | config.regmap = regmap; | |
288 | ||
289 | for (i = 0; i < num_reg_data; i++) { | |
290 | ||
291 | const struct regulator_desc *desc; | |
292 | struct regulator_dev *rdev; | |
293 | const struct bd957x_regulator_data *r; | |
294 | ||
295 | r = ®_data[i]; | |
296 | desc = &r->desc; | |
297 | ||
298 | rdev = devm_regulator_register(&pdev->dev, desc, &config); | |
299 | if (IS_ERR(rdev)) { | |
300 | dev_err(&pdev->dev, | |
301 | "failed to register %s regulator\n", | |
302 | desc->name); | |
303 | err = PTR_ERR(rdev); | |
304 | goto err; | |
305 | } | |
306 | /* | |
307 | * Clear the VOUT1 GPIO setting - rest of the regulators do not | |
308 | * support GPIO control | |
309 | */ | |
310 | config.ena_gpiod = NULL; | |
311 | } | |
312 | ||
313 | err: | |
314 | return err; | |
315 | } | |
316 | ||
317 | static const struct platform_device_id bd957x_pmic_id[] = { | |
318 | { "bd9573-pmic", ROHM_CHIP_TYPE_BD9573 }, | |
319 | { "bd9576-pmic", ROHM_CHIP_TYPE_BD9576 }, | |
320 | { }, | |
321 | }; | |
322 | MODULE_DEVICE_TABLE(platform, bd957x_pmic_id); | |
323 | ||
324 | static struct platform_driver bd957x_regulator = { | |
325 | .driver = { | |
326 | .name = "bd957x-pmic", | |
327 | }, | |
328 | .probe = bd957x_probe, | |
329 | .id_table = bd957x_pmic_id, | |
330 | }; | |
331 | ||
332 | module_platform_driver(bd957x_regulator); | |
333 | ||
334 | MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>"); | |
335 | MODULE_DESCRIPTION("ROHM BD9576/BD9573 voltage regulator driver"); | |
336 | MODULE_LICENSE("GPL"); | |
337 | MODULE_ALIAS("platform:bd957x-pmic"); |