Matti Vaittinen | b014e9f | 2020-09-17 11:03:31 +0300 | [diff] [blame] | 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: |
Matti Vaittinen | 184cdb8 | 2020-10-05 11:19:24 +0300 | [diff] [blame] | 278 | dev_dbg(&pdev->dev, "Found BD9573MUF\n"); |
Matti Vaittinen | b014e9f | 2020-09-17 11:03:31 +0300 | [diff] [blame] | 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"); |