lm363x-regulator.c (10552B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * TI LM363X Regulator Driver 4 * 5 * Copyright 2015 Texas Instruments 6 * 7 * Author: Milo Kim <milo.kim@ti.com> 8 */ 9 10#include <linux/err.h> 11#include <linux/kernel.h> 12#include <linux/mfd/ti-lmu.h> 13#include <linux/mfd/ti-lmu-register.h> 14#include <linux/module.h> 15#include <linux/of.h> 16#include <linux/gpio/consumer.h> 17#include <linux/platform_device.h> 18#include <linux/regulator/driver.h> 19#include <linux/regulator/of_regulator.h> 20#include <linux/slab.h> 21 22/* LM3631 */ 23#define LM3631_BOOST_VSEL_MAX 0x25 24#define LM3631_LDO_VSEL_MAX 0x28 25#define LM3631_CONT_VSEL_MAX 0x03 26#define LM3631_VBOOST_MIN 4500000 27#define LM3631_VCONT_MIN 1800000 28#define LM3631_VLDO_MIN 4000000 29#define ENABLE_TIME_USEC 1000 30 31/* LM3632 */ 32#define LM3632_BOOST_VSEL_MAX 0x26 33#define LM3632_LDO_VSEL_MAX 0x28 34#define LM3632_VBOOST_MIN 4500000 35#define LM3632_VLDO_MIN 4000000 36 37/* LM36274 */ 38#define LM36274_BOOST_VSEL_MAX 0x3f 39#define LM36274_LDO_VSEL_MAX 0x32 40#define LM36274_VOLTAGE_MIN 4000000 41 42/* Common */ 43#define LM363X_STEP_50mV 50000 44#define LM363X_STEP_500mV 500000 45 46static const int ldo_cont_enable_time[] = { 47 0, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 48}; 49 50static int lm363x_regulator_enable_time(struct regulator_dev *rdev) 51{ 52 enum lm363x_regulator_id id = rdev_get_id(rdev); 53 unsigned int val, addr, mask; 54 55 switch (id) { 56 case LM3631_LDO_CONT: 57 addr = LM3631_REG_ENTIME_VCONT; 58 mask = LM3631_ENTIME_CONT_MASK; 59 break; 60 case LM3631_LDO_OREF: 61 addr = LM3631_REG_ENTIME_VOREF; 62 mask = LM3631_ENTIME_MASK; 63 break; 64 case LM3631_LDO_POS: 65 addr = LM3631_REG_ENTIME_VPOS; 66 mask = LM3631_ENTIME_MASK; 67 break; 68 case LM3631_LDO_NEG: 69 addr = LM3631_REG_ENTIME_VNEG; 70 mask = LM3631_ENTIME_MASK; 71 break; 72 default: 73 return 0; 74 } 75 76 if (regmap_read(rdev->regmap, addr, &val)) 77 return -EINVAL; 78 79 val = (val & mask) >> LM3631_ENTIME_SHIFT; 80 81 if (id == LM3631_LDO_CONT) 82 return ldo_cont_enable_time[val]; 83 else 84 return ENABLE_TIME_USEC * val; 85} 86 87static const struct regulator_ops lm363x_boost_voltage_table_ops = { 88 .list_voltage = regulator_list_voltage_linear, 89 .set_voltage_sel = regulator_set_voltage_sel_regmap, 90 .get_voltage_sel = regulator_get_voltage_sel_regmap, 91}; 92 93static const struct regulator_ops lm363x_regulator_voltage_table_ops = { 94 .list_voltage = regulator_list_voltage_linear, 95 .set_voltage_sel = regulator_set_voltage_sel_regmap, 96 .get_voltage_sel = regulator_get_voltage_sel_regmap, 97 .enable = regulator_enable_regmap, 98 .disable = regulator_disable_regmap, 99 .is_enabled = regulator_is_enabled_regmap, 100 .enable_time = lm363x_regulator_enable_time, 101}; 102 103static const struct regulator_desc lm363x_regulator_desc[] = { 104 /* LM3631 */ 105 { 106 .name = "vboost", 107 .of_match = "vboost", 108 .id = LM3631_BOOST, 109 .ops = &lm363x_boost_voltage_table_ops, 110 .n_voltages = LM3631_BOOST_VSEL_MAX + 1, 111 .min_uV = LM3631_VBOOST_MIN, 112 .uV_step = LM363X_STEP_50mV, 113 .type = REGULATOR_VOLTAGE, 114 .owner = THIS_MODULE, 115 .vsel_reg = LM3631_REG_VOUT_BOOST, 116 .vsel_mask = LM3631_VOUT_MASK, 117 }, 118 { 119 .name = "ldo_cont", 120 .of_match = "vcont", 121 .id = LM3631_LDO_CONT, 122 .ops = &lm363x_regulator_voltage_table_ops, 123 .n_voltages = LM3631_CONT_VSEL_MAX + 1, 124 .min_uV = LM3631_VCONT_MIN, 125 .uV_step = LM363X_STEP_500mV, 126 .type = REGULATOR_VOLTAGE, 127 .owner = THIS_MODULE, 128 .vsel_reg = LM3631_REG_VOUT_CONT, 129 .vsel_mask = LM3631_VOUT_CONT_MASK, 130 .enable_reg = LM3631_REG_LDO_CTRL2, 131 .enable_mask = LM3631_EN_CONT_MASK, 132 }, 133 { 134 .name = "ldo_oref", 135 .of_match = "voref", 136 .id = LM3631_LDO_OREF, 137 .ops = &lm363x_regulator_voltage_table_ops, 138 .n_voltages = LM3631_LDO_VSEL_MAX + 1, 139 .min_uV = LM3631_VLDO_MIN, 140 .uV_step = LM363X_STEP_50mV, 141 .type = REGULATOR_VOLTAGE, 142 .owner = THIS_MODULE, 143 .vsel_reg = LM3631_REG_VOUT_OREF, 144 .vsel_mask = LM3631_VOUT_MASK, 145 .enable_reg = LM3631_REG_LDO_CTRL1, 146 .enable_mask = LM3631_EN_OREF_MASK, 147 }, 148 { 149 .name = "ldo_vpos", 150 .of_match = "vpos", 151 .id = LM3631_LDO_POS, 152 .ops = &lm363x_regulator_voltage_table_ops, 153 .n_voltages = LM3631_LDO_VSEL_MAX + 1, 154 .min_uV = LM3631_VLDO_MIN, 155 .uV_step = LM363X_STEP_50mV, 156 .type = REGULATOR_VOLTAGE, 157 .owner = THIS_MODULE, 158 .vsel_reg = LM3631_REG_VOUT_POS, 159 .vsel_mask = LM3631_VOUT_MASK, 160 .enable_reg = LM3631_REG_LDO_CTRL1, 161 .enable_mask = LM3631_EN_VPOS_MASK, 162 }, 163 { 164 .name = "ldo_vneg", 165 .of_match = "vneg", 166 .id = LM3631_LDO_NEG, 167 .ops = &lm363x_regulator_voltage_table_ops, 168 .n_voltages = LM3631_LDO_VSEL_MAX + 1, 169 .min_uV = LM3631_VLDO_MIN, 170 .uV_step = LM363X_STEP_50mV, 171 .type = REGULATOR_VOLTAGE, 172 .owner = THIS_MODULE, 173 .vsel_reg = LM3631_REG_VOUT_NEG, 174 .vsel_mask = LM3631_VOUT_MASK, 175 .enable_reg = LM3631_REG_LDO_CTRL1, 176 .enable_mask = LM3631_EN_VNEG_MASK, 177 }, 178 /* LM3632 */ 179 { 180 .name = "vboost", 181 .of_match = "vboost", 182 .id = LM3632_BOOST, 183 .ops = &lm363x_boost_voltage_table_ops, 184 .n_voltages = LM3632_BOOST_VSEL_MAX + 1, 185 .min_uV = LM3632_VBOOST_MIN, 186 .uV_step = LM363X_STEP_50mV, 187 .type = REGULATOR_VOLTAGE, 188 .owner = THIS_MODULE, 189 .vsel_reg = LM3632_REG_VOUT_BOOST, 190 .vsel_mask = LM3632_VOUT_MASK, 191 }, 192 { 193 .name = "ldo_vpos", 194 .of_match = "vpos", 195 .id = LM3632_LDO_POS, 196 .ops = &lm363x_regulator_voltage_table_ops, 197 .n_voltages = LM3632_LDO_VSEL_MAX + 1, 198 .min_uV = LM3632_VLDO_MIN, 199 .uV_step = LM363X_STEP_50mV, 200 .type = REGULATOR_VOLTAGE, 201 .owner = THIS_MODULE, 202 .vsel_reg = LM3632_REG_VOUT_POS, 203 .vsel_mask = LM3632_VOUT_MASK, 204 .enable_reg = LM3632_REG_BIAS_CONFIG, 205 .enable_mask = LM3632_EN_VPOS_MASK, 206 }, 207 { 208 .name = "ldo_vneg", 209 .of_match = "vneg", 210 .id = LM3632_LDO_NEG, 211 .ops = &lm363x_regulator_voltage_table_ops, 212 .n_voltages = LM3632_LDO_VSEL_MAX + 1, 213 .min_uV = LM3632_VLDO_MIN, 214 .uV_step = LM363X_STEP_50mV, 215 .type = REGULATOR_VOLTAGE, 216 .owner = THIS_MODULE, 217 .vsel_reg = LM3632_REG_VOUT_NEG, 218 .vsel_mask = LM3632_VOUT_MASK, 219 .enable_reg = LM3632_REG_BIAS_CONFIG, 220 .enable_mask = LM3632_EN_VNEG_MASK, 221 }, 222 223 /* LM36274 */ 224 { 225 .name = "vboost", 226 .of_match = "vboost", 227 .id = LM36274_BOOST, 228 .ops = &lm363x_boost_voltage_table_ops, 229 .n_voltages = LM36274_BOOST_VSEL_MAX + 1, 230 .min_uV = LM36274_VOLTAGE_MIN, 231 .uV_step = LM363X_STEP_50mV, 232 .type = REGULATOR_VOLTAGE, 233 .owner = THIS_MODULE, 234 .vsel_reg = LM36274_REG_VOUT_BOOST, 235 .vsel_mask = LM36274_VOUT_MASK, 236 }, 237 { 238 .name = "ldo_vpos", 239 .of_match = "vpos", 240 .id = LM36274_LDO_POS, 241 .ops = &lm363x_regulator_voltage_table_ops, 242 .n_voltages = LM36274_LDO_VSEL_MAX + 1, 243 .min_uV = LM36274_VOLTAGE_MIN, 244 .uV_step = LM363X_STEP_50mV, 245 .type = REGULATOR_VOLTAGE, 246 .owner = THIS_MODULE, 247 .vsel_reg = LM36274_REG_VOUT_POS, 248 .vsel_mask = LM36274_VOUT_MASK, 249 .enable_reg = LM36274_REG_BIAS_CONFIG_1, 250 .enable_mask = LM36274_EN_VPOS_MASK, 251 }, 252 { 253 .name = "ldo_vneg", 254 .of_match = "vneg", 255 .id = LM36274_LDO_NEG, 256 .ops = &lm363x_regulator_voltage_table_ops, 257 .n_voltages = LM36274_LDO_VSEL_MAX + 1, 258 .min_uV = LM36274_VOLTAGE_MIN, 259 .uV_step = LM363X_STEP_50mV, 260 .type = REGULATOR_VOLTAGE, 261 .owner = THIS_MODULE, 262 .vsel_reg = LM36274_REG_VOUT_NEG, 263 .vsel_mask = LM36274_VOUT_MASK, 264 .enable_reg = LM36274_REG_BIAS_CONFIG_1, 265 .enable_mask = LM36274_EN_VNEG_MASK, 266 }, 267}; 268 269static struct gpio_desc *lm363x_regulator_of_get_enable_gpio(struct device *dev, int id) 270{ 271 /* 272 * Check LCM_EN1/2_GPIO is configured. 273 * Those pins are used for enabling VPOS/VNEG LDOs. 274 * Do not use devm* here: the regulator core takes over the 275 * lifecycle management of the GPIO descriptor. 276 */ 277 switch (id) { 278 case LM3632_LDO_POS: 279 case LM36274_LDO_POS: 280 return gpiod_get_index_optional(dev, "enable", 0, 281 GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE); 282 case LM3632_LDO_NEG: 283 case LM36274_LDO_NEG: 284 return gpiod_get_index_optional(dev, "enable", 1, 285 GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE); 286 default: 287 return NULL; 288 } 289} 290 291static int lm363x_regulator_set_ext_en(struct regmap *regmap, int id) 292{ 293 int ext_en_mask = 0; 294 295 switch (id) { 296 case LM3632_LDO_POS: 297 case LM3632_LDO_NEG: 298 ext_en_mask = LM3632_EXT_EN_MASK; 299 break; 300 case LM36274_LDO_POS: 301 case LM36274_LDO_NEG: 302 ext_en_mask = LM36274_EXT_EN_MASK; 303 break; 304 default: 305 return -ENODEV; 306 } 307 308 return regmap_update_bits(regmap, lm363x_regulator_desc[id].enable_reg, 309 ext_en_mask, ext_en_mask); 310} 311 312static int lm363x_regulator_probe(struct platform_device *pdev) 313{ 314 struct ti_lmu *lmu = dev_get_drvdata(pdev->dev.parent); 315 struct regmap *regmap = lmu->regmap; 316 struct regulator_config cfg = { }; 317 struct regulator_dev *rdev; 318 struct device *dev = &pdev->dev; 319 int id = pdev->id; 320 struct gpio_desc *gpiod; 321 int ret; 322 323 cfg.dev = dev; 324 cfg.regmap = regmap; 325 326 /* 327 * LM3632 LDOs can be controlled by external pin. 328 * Register update is required if the pin is used. 329 */ 330 gpiod = lm363x_regulator_of_get_enable_gpio(dev, id); 331 if (IS_ERR(gpiod)) 332 return PTR_ERR(gpiod); 333 334 if (gpiod) { 335 cfg.ena_gpiod = gpiod; 336 ret = lm363x_regulator_set_ext_en(regmap, id); 337 if (ret) { 338 gpiod_put(gpiod); 339 dev_err(dev, "External pin err: %d\n", ret); 340 return ret; 341 } 342 } 343 344 rdev = devm_regulator_register(dev, &lm363x_regulator_desc[id], &cfg); 345 if (IS_ERR(rdev)) { 346 ret = PTR_ERR(rdev); 347 dev_err(dev, "[%d] regulator register err: %d\n", id, ret); 348 return ret; 349 } 350 351 return 0; 352} 353 354static struct platform_driver lm363x_regulator_driver = { 355 .probe = lm363x_regulator_probe, 356 .driver = { 357 .name = "lm363x-regulator", 358 }, 359}; 360 361module_platform_driver(lm363x_regulator_driver); 362 363MODULE_DESCRIPTION("TI LM363X Regulator Driver"); 364MODULE_AUTHOR("Milo Kim"); 365MODULE_LICENSE("GPL v2"); 366MODULE_ALIAS("platform:lm363x-regulator");