14444a1c1SMarkus Reichl // SPDX-License-Identifier: GPL-2.0 24444a1c1SMarkus Reichl // 34444a1c1SMarkus Reichl // Copyright (c) 2019 five technologies GmbH 44444a1c1SMarkus Reichl // Author: Markus Reichl <m.reichl@fivetechno.de> 54444a1c1SMarkus Reichl 64444a1c1SMarkus Reichl #include <linux/module.h> 74444a1c1SMarkus Reichl #include <linux/i2c.h> 84444a1c1SMarkus Reichl #include <linux/of.h> 94444a1c1SMarkus Reichl #include <linux/regulator/driver.h> 104444a1c1SMarkus Reichl #include <linux/regmap.h> 114444a1c1SMarkus Reichl 124444a1c1SMarkus Reichl 134444a1c1SMarkus Reichl #define VOL_MIN_IDX 0x00 144444a1c1SMarkus Reichl #define VOL_MAX_IDX 0x7ff 154444a1c1SMarkus Reichl 164444a1c1SMarkus Reichl /* Register definitions */ 174444a1c1SMarkus Reichl #define MP8859_VOUT_L_REG 0 //3 lo Bits 184444a1c1SMarkus Reichl #define MP8859_VOUT_H_REG 1 //8 hi Bits 194444a1c1SMarkus Reichl #define MP8859_VOUT_GO_REG 2 204444a1c1SMarkus Reichl #define MP8859_IOUT_LIM_REG 3 214444a1c1SMarkus Reichl #define MP8859_CTL1_REG 4 224444a1c1SMarkus Reichl #define MP8859_CTL2_REG 5 234444a1c1SMarkus Reichl #define MP8859_RESERVED1_REG 6 244444a1c1SMarkus Reichl #define MP8859_RESERVED2_REG 7 254444a1c1SMarkus Reichl #define MP8859_RESERVED3_REG 8 264444a1c1SMarkus Reichl #define MP8859_STATUS_REG 9 274444a1c1SMarkus Reichl #define MP8859_INTERRUPT_REG 0x0A 284444a1c1SMarkus Reichl #define MP8859_MASK_REG 0x0B 294444a1c1SMarkus Reichl #define MP8859_ID1_REG 0x0C 304444a1c1SMarkus Reichl #define MP8859_MFR_ID_REG 0x27 314444a1c1SMarkus Reichl #define MP8859_DEV_ID_REG 0x28 324444a1c1SMarkus Reichl #define MP8859_IC_REV_REG 0x29 334444a1c1SMarkus Reichl 344444a1c1SMarkus Reichl #define MP8859_MAX_REG 0x29 354444a1c1SMarkus Reichl 364444a1c1SMarkus Reichl #define MP8859_GO_BIT 0x01 374444a1c1SMarkus Reichl 384444a1c1SMarkus Reichl 394444a1c1SMarkus Reichl static int mp8859_set_voltage_sel(struct regulator_dev *rdev, unsigned int sel) 404444a1c1SMarkus Reichl { 414444a1c1SMarkus Reichl int ret; 424444a1c1SMarkus Reichl 434444a1c1SMarkus Reichl ret = regmap_write(rdev->regmap, MP8859_VOUT_L_REG, sel & 0x7); 444444a1c1SMarkus Reichl 454444a1c1SMarkus Reichl if (ret) 464444a1c1SMarkus Reichl return ret; 474444a1c1SMarkus Reichl ret = regmap_write(rdev->regmap, MP8859_VOUT_H_REG, sel >> 3); 484444a1c1SMarkus Reichl 494444a1c1SMarkus Reichl if (ret) 504444a1c1SMarkus Reichl return ret; 514444a1c1SMarkus Reichl ret = regmap_update_bits(rdev->regmap, MP8859_VOUT_GO_REG, 524444a1c1SMarkus Reichl MP8859_GO_BIT, 1); 534444a1c1SMarkus Reichl return ret; 544444a1c1SMarkus Reichl } 554444a1c1SMarkus Reichl 564444a1c1SMarkus Reichl static int mp8859_get_voltage_sel(struct regulator_dev *rdev) 574444a1c1SMarkus Reichl { 584444a1c1SMarkus Reichl unsigned int val_tmp; 594444a1c1SMarkus Reichl unsigned int val; 604444a1c1SMarkus Reichl int ret; 614444a1c1SMarkus Reichl 624444a1c1SMarkus Reichl ret = regmap_read(rdev->regmap, MP8859_VOUT_H_REG, &val_tmp); 634444a1c1SMarkus Reichl 644444a1c1SMarkus Reichl if (ret) 654444a1c1SMarkus Reichl return ret; 664444a1c1SMarkus Reichl val = val_tmp << 3; 674444a1c1SMarkus Reichl 684444a1c1SMarkus Reichl ret = regmap_read(rdev->regmap, MP8859_VOUT_L_REG, &val_tmp); 694444a1c1SMarkus Reichl 704444a1c1SMarkus Reichl if (ret) 714444a1c1SMarkus Reichl return ret; 724444a1c1SMarkus Reichl val |= val_tmp & 0x07; 734444a1c1SMarkus Reichl return val; 744444a1c1SMarkus Reichl } 754444a1c1SMarkus Reichl 764444a1c1SMarkus Reichl static const struct regulator_linear_range mp8859_dcdc_ranges[] = { 774444a1c1SMarkus Reichl REGULATOR_LINEAR_RANGE(0, VOL_MIN_IDX, VOL_MAX_IDX, 10000), 784444a1c1SMarkus Reichl }; 794444a1c1SMarkus Reichl 804444a1c1SMarkus Reichl static const struct regmap_config mp8859_regmap = { 814444a1c1SMarkus Reichl .reg_bits = 8, 824444a1c1SMarkus Reichl .val_bits = 8, 834444a1c1SMarkus Reichl .max_register = MP8859_MAX_REG, 844444a1c1SMarkus Reichl .cache_type = REGCACHE_RBTREE, 854444a1c1SMarkus Reichl }; 864444a1c1SMarkus Reichl 874444a1c1SMarkus Reichl static const struct regulator_ops mp8859_ops = { 884444a1c1SMarkus Reichl .set_voltage_sel = mp8859_set_voltage_sel, 894444a1c1SMarkus Reichl .get_voltage_sel = mp8859_get_voltage_sel, 904444a1c1SMarkus Reichl .list_voltage = regulator_list_voltage_linear_range, 914444a1c1SMarkus Reichl }; 924444a1c1SMarkus Reichl 934444a1c1SMarkus Reichl static const struct regulator_desc mp8859_regulators[] = { 944444a1c1SMarkus Reichl { 954444a1c1SMarkus Reichl .id = 0, 964444a1c1SMarkus Reichl .type = REGULATOR_VOLTAGE, 974444a1c1SMarkus Reichl .name = "mp8859_dcdc", 984444a1c1SMarkus Reichl .of_match = of_match_ptr("mp8859_dcdc"), 994444a1c1SMarkus Reichl .n_voltages = VOL_MAX_IDX + 1, 1004444a1c1SMarkus Reichl .linear_ranges = mp8859_dcdc_ranges, 1014444a1c1SMarkus Reichl .n_linear_ranges = 1, 1024444a1c1SMarkus Reichl .ops = &mp8859_ops, 1034444a1c1SMarkus Reichl .owner = THIS_MODULE, 1044444a1c1SMarkus Reichl }, 1054444a1c1SMarkus Reichl }; 1064444a1c1SMarkus Reichl 1074444a1c1SMarkus Reichl static int mp8859_i2c_probe(struct i2c_client *i2c) 1084444a1c1SMarkus Reichl { 1094444a1c1SMarkus Reichl int ret; 1104444a1c1SMarkus Reichl struct regulator_config config = {.dev = &i2c->dev}; 1114444a1c1SMarkus Reichl struct regmap *regmap = devm_regmap_init_i2c(i2c, &mp8859_regmap); 1124444a1c1SMarkus Reichl struct regulator_dev *rdev; 1134444a1c1SMarkus Reichl 1144444a1c1SMarkus Reichl if (IS_ERR(regmap)) { 1154444a1c1SMarkus Reichl ret = PTR_ERR(regmap); 1164444a1c1SMarkus Reichl dev_err(&i2c->dev, "regmap init failed: %d\n", ret); 1174444a1c1SMarkus Reichl return ret; 1184444a1c1SMarkus Reichl } 1194444a1c1SMarkus Reichl rdev = devm_regulator_register(&i2c->dev, &mp8859_regulators[0], 1204444a1c1SMarkus Reichl &config); 1214444a1c1SMarkus Reichl 1224444a1c1SMarkus Reichl if (IS_ERR(rdev)) { 1234444a1c1SMarkus Reichl ret = PTR_ERR(rdev); 1244444a1c1SMarkus Reichl dev_err(&i2c->dev, "failed to register %s: %d\n", 1254444a1c1SMarkus Reichl mp8859_regulators[0].name, ret); 1264444a1c1SMarkus Reichl return ret; 1274444a1c1SMarkus Reichl } 1284444a1c1SMarkus Reichl return 0; 1294444a1c1SMarkus Reichl } 1304444a1c1SMarkus Reichl 1314444a1c1SMarkus Reichl static const struct of_device_id mp8859_dt_id[] = { 1324444a1c1SMarkus Reichl {.compatible = "mps,mp8859"}, 1334444a1c1SMarkus Reichl {}, 1344444a1c1SMarkus Reichl }; 1354444a1c1SMarkus Reichl MODULE_DEVICE_TABLE(of, mp8859_dt_id); 1364444a1c1SMarkus Reichl 1374444a1c1SMarkus Reichl static const struct i2c_device_id mp8859_i2c_id[] = { 1384444a1c1SMarkus Reichl { "mp8859", }, 1394444a1c1SMarkus Reichl { }, 1404444a1c1SMarkus Reichl }; 1414444a1c1SMarkus Reichl MODULE_DEVICE_TABLE(i2c, mp8859_i2c_id); 1424444a1c1SMarkus Reichl 1434444a1c1SMarkus Reichl static struct i2c_driver mp8859_regulator_driver = { 1444444a1c1SMarkus Reichl .driver = { 1454444a1c1SMarkus Reichl .name = "mp8859", 1464444a1c1SMarkus Reichl .of_match_table = of_match_ptr(mp8859_dt_id), 1474444a1c1SMarkus Reichl }, 1484444a1c1SMarkus Reichl .probe_new = mp8859_i2c_probe, 1494444a1c1SMarkus Reichl .id_table = mp8859_i2c_id, 1504444a1c1SMarkus Reichl }; 1514444a1c1SMarkus Reichl 1524444a1c1SMarkus Reichl module_i2c_driver(mp8859_regulator_driver); 1534444a1c1SMarkus Reichl 1544444a1c1SMarkus Reichl MODULE_DESCRIPTION("Monolithic Power Systems MP8859 voltage regulator driver"); 1554444a1c1SMarkus Reichl MODULE_AUTHOR("Markus Reichl <m.reichl@fivetechno.de>"); 1564444a1c1SMarkus Reichl MODULE_LICENSE("GPL v2"); 157