1 // SPDX-License-Identifier: GPL-2.0 2 // 3 // MP8869 regulator driver 4 // 5 // Copyright (C) 2020 Synaptics Incorporated 6 // 7 // Author: Jisheng Zhang <jszhang@kernel.org> 8 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> 16 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_GO (1 << 6) 22 #define MP886X_EN (1 << 7) 23 24 struct mp886x_device_info { 25 struct device *dev; 26 struct regulator_desc desc; 27 struct regulator_init_data *regulator; 28 struct gpio_desc *en_gpio; 29 u32 r[2]; 30 unsigned int sel; 31 }; 32 33 static int mp886x_set_mode(struct regulator_dev *rdev, unsigned int mode) 34 { 35 switch (mode) { 36 case REGULATOR_MODE_FAST: 37 regmap_update_bits(rdev->regmap, MP886X_SYSCNTLREG1, 38 MP886X_MODE, MP886X_MODE); 39 break; 40 case REGULATOR_MODE_NORMAL: 41 regmap_update_bits(rdev->regmap, MP886X_SYSCNTLREG1, 42 MP886X_MODE, 0); 43 break; 44 default: 45 return -EINVAL; 46 } 47 return 0; 48 } 49 50 static unsigned int mp886x_get_mode(struct regulator_dev *rdev) 51 { 52 u32 val; 53 int ret; 54 55 ret = regmap_read(rdev->regmap, MP886X_SYSCNTLREG1, &val); 56 if (ret < 0) 57 return ret; 58 if (val & MP886X_MODE) 59 return REGULATOR_MODE_FAST; 60 else 61 return REGULATOR_MODE_NORMAL; 62 } 63 64 static int mp8869_set_voltage_sel(struct regulator_dev *rdev, unsigned int sel) 65 { 66 int ret; 67 68 ret = regmap_update_bits(rdev->regmap, MP886X_SYSCNTLREG1, 69 MP886X_GO, MP886X_GO); 70 if (ret < 0) 71 return ret; 72 73 sel <<= ffs(rdev->desc->vsel_mask) - 1; 74 return regmap_update_bits(rdev->regmap, rdev->desc->vsel_reg, 75 MP886X_V_BOOT | rdev->desc->vsel_mask, sel); 76 } 77 78 static inline unsigned int mp8869_scale(unsigned int uv, u32 r1, u32 r2) 79 { 80 u32 tmp = uv * r1 / r2; 81 82 return uv + tmp; 83 } 84 85 static int mp8869_get_voltage_sel(struct regulator_dev *rdev) 86 { 87 struct mp886x_device_info *di = rdev_get_drvdata(rdev); 88 int ret, uv; 89 unsigned int val; 90 bool fbloop; 91 92 ret = regmap_read(rdev->regmap, rdev->desc->vsel_reg, &val); 93 if (ret) 94 return ret; 95 96 fbloop = val & MP886X_V_BOOT; 97 if (fbloop) { 98 uv = rdev->desc->min_uV; 99 uv = mp8869_scale(uv, di->r[0], di->r[1]); 100 return regulator_map_voltage_linear(rdev, uv, uv); 101 } 102 103 val &= rdev->desc->vsel_mask; 104 val >>= ffs(rdev->desc->vsel_mask) - 1; 105 106 return val; 107 } 108 109 static const struct regulator_ops mp8869_regulator_ops = { 110 .set_voltage_sel = mp8869_set_voltage_sel, 111 .get_voltage_sel = mp8869_get_voltage_sel, 112 .set_voltage_time_sel = regulator_set_voltage_time_sel, 113 .map_voltage = regulator_map_voltage_linear, 114 .list_voltage = regulator_list_voltage_linear, 115 .enable = regulator_enable_regmap, 116 .disable = regulator_disable_regmap, 117 .is_enabled = regulator_is_enabled_regmap, 118 .set_mode = mp886x_set_mode, 119 .get_mode = mp886x_get_mode, 120 }; 121 122 static int mp886x_regulator_register(struct mp886x_device_info *di, 123 struct regulator_config *config) 124 { 125 struct regulator_desc *rdesc = &di->desc; 126 struct regulator_dev *rdev; 127 128 rdesc->name = "mp886x-reg"; 129 rdesc->supply_name = "vin"; 130 rdesc->ops = of_device_get_match_data(di->dev); 131 rdesc->type = REGULATOR_VOLTAGE; 132 rdesc->n_voltages = 128; 133 rdesc->enable_reg = MP886X_SYSCNTLREG1; 134 rdesc->enable_mask = MP886X_EN; 135 rdesc->min_uV = 600000; 136 rdesc->uV_step = 10000; 137 rdesc->vsel_reg = MP886X_VSEL; 138 rdesc->vsel_mask = 0x3f; 139 rdesc->owner = THIS_MODULE; 140 141 rdev = devm_regulator_register(di->dev, &di->desc, config); 142 if (IS_ERR(rdev)) 143 return PTR_ERR(rdev); 144 di->sel = rdesc->ops->get_voltage_sel(rdev); 145 return 0; 146 } 147 148 static const struct regmap_config mp886x_regmap_config = { 149 .reg_bits = 8, 150 .val_bits = 8, 151 }; 152 153 static int mp886x_i2c_probe(struct i2c_client *client, 154 const struct i2c_device_id *id) 155 { 156 struct device *dev = &client->dev; 157 struct device_node *np = dev->of_node; 158 struct mp886x_device_info *di; 159 struct regulator_config config = { }; 160 struct regmap *regmap; 161 int ret; 162 163 di = devm_kzalloc(dev, sizeof(struct mp886x_device_info), GFP_KERNEL); 164 if (!di) 165 return -ENOMEM; 166 167 di->regulator = of_get_regulator_init_data(dev, np, &di->desc); 168 if (!di->regulator) { 169 dev_err(dev, "Platform data not found!\n"); 170 return -EINVAL; 171 } 172 173 ret = of_property_read_u32_array(np, "mps,fb-voltage-divider", 174 di->r, 2); 175 if (ret) 176 return ret; 177 178 di->en_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH); 179 if (IS_ERR(di->en_gpio)) 180 return PTR_ERR(di->en_gpio); 181 182 di->dev = dev; 183 184 regmap = devm_regmap_init_i2c(client, &mp886x_regmap_config); 185 if (IS_ERR(regmap)) { 186 dev_err(dev, "Failed to allocate regmap!\n"); 187 return PTR_ERR(regmap); 188 } 189 i2c_set_clientdata(client, di); 190 191 config.dev = di->dev; 192 config.init_data = di->regulator; 193 config.regmap = regmap; 194 config.driver_data = di; 195 config.of_node = np; 196 197 ret = mp886x_regulator_register(di, &config); 198 if (ret < 0) 199 dev_err(dev, "Failed to register regulator!\n"); 200 return ret; 201 } 202 203 static const struct of_device_id mp886x_dt_ids[] = { 204 { 205 .compatible = "mps,mp8869", 206 .data = &mp8869_regulator_ops 207 }, 208 { } 209 }; 210 MODULE_DEVICE_TABLE(of, mp886x_dt_ids); 211 212 static const struct i2c_device_id mp886x_id[] = { 213 { "mp886x", }, 214 { }, 215 }; 216 MODULE_DEVICE_TABLE(i2c, mp886x_id); 217 218 static struct i2c_driver mp886x_regulator_driver = { 219 .driver = { 220 .name = "mp886x-regulator", 221 .of_match_table = of_match_ptr(mp886x_dt_ids), 222 }, 223 .probe = mp886x_i2c_probe, 224 .id_table = mp886x_id, 225 }; 226 module_i2c_driver(mp886x_regulator_driver); 227 228 MODULE_AUTHOR("Jisheng Zhang <jszhang@kernel.org>"); 229 MODULE_DESCRIPTION("MP886x regulator driver"); 230 MODULE_LICENSE("GPL v2"); 231