409 lines · c
1// SPDX-License-Identifier: GPL-2.02//3// Copyright (c) 2019 five technologies GmbH4// Author: Markus Reichl <m.reichl@fivetechno.de>5 6#include <linux/module.h>7#include <linux/i2c.h>8#include <linux/of.h>9#include <linux/regulator/driver.h>10#include <linux/regmap.h>11 12 13#define VOL_MIN_IDX 0x0014#define VOL_MAX_IDX 0x7ff15 16/* Register definitions */17#define MP8859_VOUT_L_REG 0 //3 lo Bits18#define MP8859_VOUT_H_REG 1 //8 hi Bits19#define MP8859_VOUT_GO_REG 220#define MP8859_IOUT_LIM_REG 321#define MP8859_CTL1_REG 422#define MP8859_CTL2_REG 523#define MP8859_RESERVED1_REG 624#define MP8859_RESERVED2_REG 725#define MP8859_RESERVED3_REG 826#define MP8859_STATUS_REG 927#define MP8859_INTERRUPT_REG 0x0A28#define MP8859_MASK_REG 0x0B29#define MP8859_ID1_REG 0x0C30#define MP8859_MFR_ID_REG 0x2731#define MP8859_DEV_ID_REG 0x2832#define MP8859_IC_REV_REG 0x2933 34#define MP8859_MAX_REG 0x2935 36#define MP8859_GO_BIT 0x0137 38#define MP8859_IOUT_LIM_MASK 0x7f39 40#define MP8859_ENABLE_MASK 0x8041#define MP8859_DISCHG_EN_MASK 0x1042#define MP8859_MODE_MASK 0x0843 44#define MP8859_PG_MASK 0x8045#define MP8859_OTP_MASK 0x4046#define MP8859_OTW_MASK 0x2047#define MP8859_CC_CV_MASK 0x1048 49static int mp8859_set_voltage_sel(struct regulator_dev *rdev, unsigned int sel)50{51 int ret;52 53 ret = regmap_write(rdev->regmap, MP8859_VOUT_L_REG, sel & 0x7);54 55 if (ret)56 return ret;57 ret = regmap_write(rdev->regmap, MP8859_VOUT_H_REG, sel >> 3);58 59 if (ret)60 return ret;61 ret = regmap_update_bits(rdev->regmap, MP8859_VOUT_GO_REG,62 MP8859_GO_BIT, 1);63 return ret;64}65 66static int mp8859_get_voltage_sel(struct regulator_dev *rdev)67{68 unsigned int val_tmp;69 unsigned int val;70 int ret;71 72 ret = regmap_read(rdev->regmap, MP8859_VOUT_H_REG, &val_tmp);73 74 if (ret)75 return ret;76 val = val_tmp << 3;77 78 ret = regmap_read(rdev->regmap, MP8859_VOUT_L_REG, &val_tmp);79 80 if (ret)81 return ret;82 val |= val_tmp & 0x07;83 return val;84}85 86static int mp8859_set_voltage_time_sel(struct regulator_dev *rdev,87 unsigned int from, unsigned int to)88{89 int change;90 91 /* The voltage ramps at 1mV/uS, selectors are 10mV */92 if (from > to)93 change = from - to;94 else95 change = to - from;96 97 return change * 10 * 1000;98}99 100static unsigned int mp8859_get_mode(struct regulator_dev *rdev)101{102 unsigned int val;103 int ret;104 105 ret = regmap_read(rdev->regmap, MP8859_CTL1_REG, &val);106 if (ret != 0) {107 dev_err(&rdev->dev, "Failed to read mode: %d\n", ret);108 return 0;109 }110 111 if (val & MP8859_MODE_MASK)112 return REGULATOR_MODE_FAST;113 else114 return REGULATOR_MODE_NORMAL;115}116 117static int mp8859_set_mode(struct regulator_dev *rdev, unsigned int mode)118{119 unsigned int val;120 121 switch (mode) {122 case REGULATOR_MODE_FAST:123 val = MP8859_MODE_MASK;124 break;125 case REGULATOR_MODE_NORMAL:126 val = 0;127 break;128 default:129 return -EINVAL;130 }131 132 return regmap_update_bits(rdev->regmap, MP8859_CTL1_REG,133 MP8859_MODE_MASK, val);134}135 136static int mp8859_set_current_limit(struct regulator_dev *rdev,137 int min_uA, int max_uA)138{139 unsigned int cur_val, new_val;140 int ret, i;141 142 /* Steps of 50mA */143 new_val = max_uA / 50000;144 if (new_val > MP8859_IOUT_LIM_MASK)145 return -EINVAL;146 if (new_val == 0)147 return -EINVAL;148 149 /*150 * If the regulator is limiting then ramp gradually as per151 * datasheet, otherwise just set the value directly.152 */153 ret = regmap_read(rdev->regmap, MP8859_STATUS_REG, &cur_val);154 if (ret != 0)155 return ret;156 if (!(cur_val & MP8859_CC_CV_MASK)) {157 return regmap_update_bits(rdev->regmap, MP8859_IOUT_LIM_REG,158 MP8859_IOUT_LIM_MASK, new_val);159 }160 161 ret = regmap_read(rdev->regmap, MP8859_IOUT_LIM_REG, &cur_val);162 if (ret != 0)163 return ret;164 165 if (cur_val >= new_val) {166 for (i = cur_val; i >= new_val; i--) {167 ret = regmap_update_bits(rdev->regmap,168 MP8859_IOUT_LIM_REG,169 MP8859_IOUT_LIM_MASK,170 cur_val - i);171 if (ret != 0)172 return ret;173 }174 } else {175 for (i = cur_val; i <= new_val; i++) {176 ret = regmap_update_bits(rdev->regmap,177 MP8859_IOUT_LIM_REG,178 MP8859_IOUT_LIM_MASK,179 cur_val + i);180 if (ret != 0)181 return ret;182 }183 }184 185 return 0;186}187 188static int mp8859_get_status(struct regulator_dev *rdev)189{190 unsigned int val;191 int ret;192 193 /* Output status is only meaingful when enabled */194 ret = regmap_read(rdev->regmap, MP8859_CTL1_REG, &val);195 if (ret != 0)196 return ret;197 if (!(val & MP8859_ENABLE_MASK))198 return REGULATOR_STATUS_UNDEFINED;199 200 ret = regmap_read(rdev->regmap, MP8859_STATUS_REG, &val);201 if (ret != 0)202 return ret;203 204 if (val & MP8859_PG_MASK)205 return REGULATOR_STATUS_ON;206 else207 return REGULATOR_STATUS_ERROR;208}209 210static int mp8859_get_error_flags(struct regulator_dev *rdev,211 unsigned int *flags)212{213 unsigned int status, enabled;214 int ret;215 216 *flags = 0;217 218 /* Output status is only meaingful when enabled */219 ret = regmap_read(rdev->regmap, MP8859_CTL1_REG, &enabled);220 if (ret != 0)221 return ret;222 enabled &= MP8859_ENABLE_MASK;223 224 ret = regmap_read(rdev->regmap, MP8859_STATUS_REG, &status);225 if (ret != 0)226 return ret;227 228 if (enabled && !(status & MP8859_PG_MASK))229 status |= REGULATOR_ERROR_FAIL;230 if (status & MP8859_OTP_MASK)231 status |= REGULATOR_ERROR_OVER_TEMP;232 if (status & MP8859_OTW_MASK)233 status |= REGULATOR_ERROR_OVER_TEMP_WARN;234 if (status & MP8859_CC_CV_MASK)235 status |= REGULATOR_ERROR_OVER_CURRENT;236 237 return 0;238}239 240static const struct linear_range mp8859_dcdc_ranges[] = {241 REGULATOR_LINEAR_RANGE(0, VOL_MIN_IDX, VOL_MAX_IDX, 10000),242};243 244static bool mp8859_readable(struct device *dev, unsigned int reg)245{246 switch (reg) {247 case MP8859_VOUT_L_REG:248 case MP8859_VOUT_H_REG:249 case MP8859_VOUT_GO_REG:250 case MP8859_IOUT_LIM_REG:251 case MP8859_CTL1_REG:252 case MP8859_CTL2_REG:253 case MP8859_STATUS_REG:254 case MP8859_INTERRUPT_REG:255 case MP8859_MASK_REG:256 case MP8859_ID1_REG:257 case MP8859_MFR_ID_REG:258 case MP8859_DEV_ID_REG:259 case MP8859_IC_REV_REG:260 return true;261 default:262 return false;263 }264}265 266static bool mp8859_volatile(struct device *dev, unsigned int reg)267{268 switch (reg) {269 case MP8859_VOUT_GO_REG:270 case MP8859_STATUS_REG:271 case MP8859_INTERRUPT_REG:272 return true;273 default:274 return false;275 }276}277 278static const struct regmap_config mp8859_regmap = {279 .reg_bits = 8,280 .val_bits = 8,281 .max_register = MP8859_MAX_REG,282 .cache_type = REGCACHE_MAPLE,283 .readable_reg = mp8859_readable,284 .volatile_reg = mp8859_volatile,285};286 287static const struct regulator_ops mp8859_ops = {288 .set_voltage_sel = mp8859_set_voltage_sel,289 .get_voltage_sel = mp8859_get_voltage_sel,290 .list_voltage = regulator_list_voltage_linear_range,291 .set_voltage_time_sel = mp8859_set_voltage_time_sel,292 .enable = regulator_enable_regmap,293 .disable = regulator_disable_regmap,294 .is_enabled = regulator_is_enabled_regmap,295 .set_mode = mp8859_set_mode,296 .get_mode = mp8859_get_mode,297 .set_active_discharge = regulator_set_active_discharge_regmap,298 .set_current_limit = mp8859_set_current_limit,299 .get_status = mp8859_get_status,300 .get_error_flags = mp8859_get_error_flags,301};302 303static const struct regulator_desc mp8859_regulators[] = {304 {305 .id = 0,306 .type = REGULATOR_VOLTAGE,307 .name = "mp8859_dcdc",308 .supply_name = "vin",309 .of_match = of_match_ptr("mp8859_dcdc"),310 .n_voltages = VOL_MAX_IDX + 1,311 .linear_ranges = mp8859_dcdc_ranges,312 .n_linear_ranges = 1,313 .enable_reg = MP8859_CTL1_REG,314 .enable_mask = MP8859_ENABLE_MASK,315 .enable_val = MP8859_ENABLE_MASK,316 .active_discharge_reg = MP8859_CTL1_REG,317 .active_discharge_on = MP8859_DISCHG_EN_MASK,318 .active_discharge_mask = MP8859_DISCHG_EN_MASK,319 .ops = &mp8859_ops,320 .owner = THIS_MODULE,321 },322};323 324static int mp8859_i2c_probe(struct i2c_client *i2c)325{326 int ret;327 struct regulator_config config = {.dev = &i2c->dev};328 struct regmap *regmap = devm_regmap_init_i2c(i2c, &mp8859_regmap);329 struct regulator_dev *rdev;330 unsigned int val, rev;331 332 if (IS_ERR(regmap)) {333 ret = PTR_ERR(regmap);334 dev_err(&i2c->dev, "regmap init failed: %d\n", ret);335 return ret;336 }337 338 ret = regmap_read(regmap, MP8859_MFR_ID_REG, &val);339 if (ret != 0) {340 dev_err(&i2c->dev, "Failed to read manufacturer ID: %d\n", ret);341 return ret;342 }343 if (val != 0x9) {344 dev_err(&i2c->dev, "Manufacturer ID %x != 9\n", val);345 return -EINVAL;346 }347 348 ret = regmap_read(regmap, MP8859_DEV_ID_REG, &val);349 if (ret != 0) {350 dev_err(&i2c->dev, "Failed to read device ID: %d\n", ret);351 return ret;352 }353 if (val != 0x58) {354 dev_err(&i2c->dev, "Manufacturer ID %x != 0x58\n", val);355 return -EINVAL;356 }357 358 ret = regmap_read(regmap, MP8859_IC_REV_REG, &rev);359 if (ret != 0) {360 dev_err(&i2c->dev, "Failed to read device revision: %d\n", ret);361 return ret;362 }363 ret = regmap_read(regmap, MP8859_ID1_REG, &val);364 if (ret != 0) {365 dev_err(&i2c->dev, "Failed to read device ID1: %d\n", ret);366 return ret;367 }368 dev_info(&i2c->dev, "MP8859-%04d revision %d\n", val, rev);369 370 rdev = devm_regulator_register(&i2c->dev, &mp8859_regulators[0],371 &config);372 373 if (IS_ERR(rdev)) {374 ret = PTR_ERR(rdev);375 dev_err(&i2c->dev, "failed to register %s: %d\n",376 mp8859_regulators[0].name, ret);377 return ret;378 }379 return 0;380}381 382static const struct of_device_id mp8859_dt_id[] __maybe_unused = {383 {.compatible = "mps,mp8859"},384 {},385};386MODULE_DEVICE_TABLE(of, mp8859_dt_id);387 388static const struct i2c_device_id mp8859_i2c_id[] = {389 { "mp8859", },390 { },391};392MODULE_DEVICE_TABLE(i2c, mp8859_i2c_id);393 394static struct i2c_driver mp8859_regulator_driver = {395 .driver = {396 .name = "mp8859",397 .probe_type = PROBE_PREFER_ASYNCHRONOUS,398 .of_match_table = of_match_ptr(mp8859_dt_id),399 },400 .probe = mp8859_i2c_probe,401 .id_table = mp8859_i2c_id,402};403 404module_i2c_driver(mp8859_regulator_driver);405 406MODULE_DESCRIPTION("Monolithic Power Systems MP8859 voltage regulator driver");407MODULE_AUTHOR("Markus Reichl <m.reichl@fivetechno.de>");408MODULE_LICENSE("GPL v2");409