1 // SPDX-License-Identifier: GPL-2.0
3 // MP8867/MP8869 regulator driver
5 // Copyright (C) 2020 Synaptics Incorporated
7 // Author: Jisheng Zhang <jszhang@kernel.org>
9 #include <linux/gpio/consumer.h>
10 #include <linux/i2c.h>
11 #include <linux/module.h>
12 #include <linux/of_device.h>
13 #include <linux/regmap.h>
14 #include <linux/regulator/driver.h>
15 #include <linux/regulator/of_regulator.h>
17 #define MP886X_VSEL 0x00
18 #define MP886X_V_BOOT (1 << 7)
19 #define MP886X_SYSCNTLREG1 0x01
20 #define MP886X_MODE (1 << 0)
21 #define MP886X_SLEW_SHIFT 3
22 #define MP886X_SLEW_MASK (0x7 << MP886X_SLEW_SHIFT)
23 #define MP886X_GO (1 << 6)
24 #define MP886X_EN (1 << 7)
25 #define MP8869_SYSCNTLREG2 0x02
27 struct mp886x_cfg_info
{
28 const struct regulator_ops
*rops
;
29 const int slew_rates
[8];
30 const int switch_freq
[4];
35 struct mp886x_device_info
{
37 struct regulator_desc desc
;
38 struct regulator_init_data
*regulator
;
39 struct gpio_desc
*en_gpio
;
40 const struct mp886x_cfg_info
*ci
;
45 static int mp886x_set_ramp(struct regulator_dev
*rdev
, int ramp
)
47 struct mp886x_device_info
*di
= rdev_get_drvdata(rdev
);
48 const struct mp886x_cfg_info
*ci
= di
->ci
;
51 for (i
= 0; i
< ARRAY_SIZE(ci
->slew_rates
); i
++) {
52 if (ramp
<= ci
->slew_rates
[i
])
59 dev_err(di
->dev
, "unsupported ramp value %d\n", ramp
);
63 return regmap_update_bits(rdev
->regmap
, MP886X_SYSCNTLREG1
,
64 MP886X_SLEW_MASK
, reg
<< MP886X_SLEW_SHIFT
);
67 static void mp886x_set_switch_freq(struct mp886x_device_info
*di
,
68 struct regmap
*regmap
,
71 const struct mp886x_cfg_info
*ci
= di
->ci
;
74 for (i
= 0; i
< ARRAY_SIZE(ci
->switch_freq
); i
++) {
75 if (freq
== ci
->switch_freq
[i
]) {
76 regmap_update_bits(regmap
, ci
->fs_reg
,
77 0x3 << ci
->fs_shift
, i
<< ci
->fs_shift
);
82 dev_err(di
->dev
, "invalid frequency %d\n", freq
);
85 static int mp886x_set_mode(struct regulator_dev
*rdev
, unsigned int mode
)
88 case REGULATOR_MODE_FAST
:
89 regmap_update_bits(rdev
->regmap
, MP886X_SYSCNTLREG1
,
90 MP886X_MODE
, MP886X_MODE
);
92 case REGULATOR_MODE_NORMAL
:
93 regmap_update_bits(rdev
->regmap
, MP886X_SYSCNTLREG1
,
102 static unsigned int mp886x_get_mode(struct regulator_dev
*rdev
)
107 ret
= regmap_read(rdev
->regmap
, MP886X_SYSCNTLREG1
, &val
);
110 if (val
& MP886X_MODE
)
111 return REGULATOR_MODE_FAST
;
113 return REGULATOR_MODE_NORMAL
;
116 static int mp8869_set_voltage_sel(struct regulator_dev
*rdev
, unsigned int sel
)
120 ret
= regmap_update_bits(rdev
->regmap
, MP886X_SYSCNTLREG1
,
121 MP886X_GO
, MP886X_GO
);
125 sel
<<= ffs(rdev
->desc
->vsel_mask
) - 1;
126 return regmap_update_bits(rdev
->regmap
, rdev
->desc
->vsel_reg
,
127 MP886X_V_BOOT
| rdev
->desc
->vsel_mask
, sel
);
130 static inline unsigned int mp8869_scale(unsigned int uv
, u32 r1
, u32 r2
)
132 u32 tmp
= uv
* r1
/ r2
;
137 static int mp8869_get_voltage_sel(struct regulator_dev
*rdev
)
139 struct mp886x_device_info
*di
= rdev_get_drvdata(rdev
);
144 ret
= regmap_read(rdev
->regmap
, rdev
->desc
->vsel_reg
, &val
);
148 fbloop
= val
& MP886X_V_BOOT
;
150 uv
= rdev
->desc
->min_uV
;
151 uv
= mp8869_scale(uv
, di
->r
[0], di
->r
[1]);
152 return regulator_map_voltage_linear(rdev
, uv
, uv
);
155 val
&= rdev
->desc
->vsel_mask
;
156 val
>>= ffs(rdev
->desc
->vsel_mask
) - 1;
161 static const struct regulator_ops mp8869_regulator_ops
= {
162 .set_voltage_sel
= mp8869_set_voltage_sel
,
163 .get_voltage_sel
= mp8869_get_voltage_sel
,
164 .set_voltage_time_sel
= regulator_set_voltage_time_sel
,
165 .map_voltage
= regulator_map_voltage_linear
,
166 .list_voltage
= regulator_list_voltage_linear
,
167 .enable
= regulator_enable_regmap
,
168 .disable
= regulator_disable_regmap
,
169 .is_enabled
= regulator_is_enabled_regmap
,
170 .set_mode
= mp886x_set_mode
,
171 .get_mode
= mp886x_get_mode
,
172 .set_ramp_delay
= mp886x_set_ramp
,
175 static const struct mp886x_cfg_info mp8869_ci
= {
176 .rops
= &mp8869_regulator_ops
,
193 .fs_reg
= MP8869_SYSCNTLREG2
,
197 static int mp8867_set_voltage_sel(struct regulator_dev
*rdev
, unsigned int sel
)
199 struct mp886x_device_info
*di
= rdev_get_drvdata(rdev
);
202 ret
= mp8869_set_voltage_sel(rdev
, sel
);
206 delta
= di
->sel
- sel
;
208 ret
= regmap_update_bits(rdev
->regmap
, MP886X_SYSCNTLREG1
,
215 static int mp8867_get_voltage_sel(struct regulator_dev
*rdev
)
217 struct mp886x_device_info
*di
= rdev_get_drvdata(rdev
);
222 ret
= regmap_read(rdev
->regmap
, rdev
->desc
->vsel_reg
, &val
);
226 fbloop
= val
& MP886X_V_BOOT
;
228 val
&= rdev
->desc
->vsel_mask
;
229 val
>>= ffs(rdev
->desc
->vsel_mask
) - 1;
232 uv
= regulator_list_voltage_linear(rdev
, val
);
233 uv
= mp8869_scale(uv
, di
->r
[0], di
->r
[1]);
234 return regulator_map_voltage_linear(rdev
, uv
, uv
);
240 static const struct regulator_ops mp8867_regulator_ops
= {
241 .set_voltage_sel
= mp8867_set_voltage_sel
,
242 .get_voltage_sel
= mp8867_get_voltage_sel
,
243 .set_voltage_time_sel
= regulator_set_voltage_time_sel
,
244 .map_voltage
= regulator_map_voltage_linear
,
245 .list_voltage
= regulator_list_voltage_linear
,
246 .enable
= regulator_enable_regmap
,
247 .disable
= regulator_disable_regmap
,
248 .is_enabled
= regulator_is_enabled_regmap
,
249 .set_mode
= mp886x_set_mode
,
250 .get_mode
= mp886x_get_mode
,
251 .set_ramp_delay
= mp886x_set_ramp
,
254 static const struct mp886x_cfg_info mp8867_ci
= {
255 .rops
= &mp8867_regulator_ops
,
272 .fs_reg
= MP886X_SYSCNTLREG1
,
276 static int mp886x_regulator_register(struct mp886x_device_info
*di
,
277 struct regulator_config
*config
)
279 struct regulator_desc
*rdesc
= &di
->desc
;
280 struct regulator_dev
*rdev
;
282 rdesc
->name
= "mp886x-reg";
283 rdesc
->supply_name
= "vin";
284 rdesc
->ops
= di
->ci
->rops
;
285 rdesc
->type
= REGULATOR_VOLTAGE
;
286 rdesc
->n_voltages
= 128;
287 rdesc
->enable_reg
= MP886X_SYSCNTLREG1
;
288 rdesc
->enable_mask
= MP886X_EN
;
289 rdesc
->min_uV
= 600000;
290 rdesc
->uV_step
= 10000;
291 rdesc
->vsel_reg
= MP886X_VSEL
;
292 rdesc
->vsel_mask
= 0x3f;
293 rdesc
->owner
= THIS_MODULE
;
295 rdev
= devm_regulator_register(di
->dev
, &di
->desc
, config
);
297 return PTR_ERR(rdev
);
298 di
->sel
= rdesc
->ops
->get_voltage_sel(rdev
);
302 static const struct regmap_config mp886x_regmap_config
= {
307 static int mp886x_i2c_probe(struct i2c_client
*client
)
309 struct device
*dev
= &client
->dev
;
310 struct device_node
*np
= dev
->of_node
;
311 struct mp886x_device_info
*di
;
312 struct regulator_config config
= { };
313 struct regmap
*regmap
;
317 di
= devm_kzalloc(dev
, sizeof(struct mp886x_device_info
), GFP_KERNEL
);
321 di
->regulator
= of_get_regulator_init_data(dev
, np
, &di
->desc
);
322 if (!di
->regulator
) {
323 dev_err(dev
, "Platform data not found!\n");
327 ret
= of_property_read_u32_array(np
, "mps,fb-voltage-divider",
332 di
->en_gpio
= devm_gpiod_get(dev
, "enable", GPIOD_OUT_HIGH
);
333 if (IS_ERR(di
->en_gpio
))
334 return PTR_ERR(di
->en_gpio
);
336 di
->ci
= of_device_get_match_data(dev
);
339 regmap
= devm_regmap_init_i2c(client
, &mp886x_regmap_config
);
340 if (IS_ERR(regmap
)) {
341 dev_err(dev
, "Failed to allocate regmap!\n");
342 return PTR_ERR(regmap
);
344 i2c_set_clientdata(client
, di
);
346 config
.dev
= di
->dev
;
347 config
.init_data
= di
->regulator
;
348 config
.regmap
= regmap
;
349 config
.driver_data
= di
;
352 if (!of_property_read_u32(np
, "mps,switch-frequency-hz", &freq
))
353 mp886x_set_switch_freq(di
, regmap
, freq
);
355 ret
= mp886x_regulator_register(di
, &config
);
357 dev_err(dev
, "Failed to register regulator!\n");
361 static const struct of_device_id mp886x_dt_ids
[] = {
363 .compatible
= "mps,mp8867",
367 .compatible
= "mps,mp8869",
372 MODULE_DEVICE_TABLE(of
, mp886x_dt_ids
);
374 static const struct i2c_device_id mp886x_id
[] = {
378 MODULE_DEVICE_TABLE(i2c
, mp886x_id
);
380 static struct i2c_driver mp886x_regulator_driver
= {
382 .name
= "mp886x-regulator",
383 .of_match_table
= of_match_ptr(mp886x_dt_ids
),
385 .probe_new
= mp886x_i2c_probe
,
386 .id_table
= mp886x_id
,
388 module_i2c_driver(mp886x_regulator_driver
);
390 MODULE_AUTHOR("Jisheng Zhang <jszhang@kernel.org>");
391 MODULE_DESCRIPTION("MP886x regulator driver");
392 MODULE_LICENSE("GPL v2");