18878302eSOndrej Jirman // SPDX-License-Identifier: GPL-2.0+ 28878302eSOndrej Jirman // 38878302eSOndrej Jirman // sy8106a-regulator.c - Regulator device driver for SY8106A 48878302eSOndrej Jirman // 58878302eSOndrej Jirman // Copyright (C) 2016 Ondřej Jirman <megous@megous.com> 68878302eSOndrej Jirman // Copyright (c) 2017-2018 Icenowy Zheng <icenowy@aosc.io> 78878302eSOndrej Jirman 88878302eSOndrej Jirman #include <linux/err.h> 98878302eSOndrej Jirman #include <linux/i2c.h> 108878302eSOndrej Jirman #include <linux/module.h> 118878302eSOndrej Jirman #include <linux/regmap.h> 128878302eSOndrej Jirman #include <linux/regulator/driver.h> 138878302eSOndrej Jirman #include <linux/regulator/of_regulator.h> 148878302eSOndrej Jirman 158878302eSOndrej Jirman #define SY8106A_REG_VOUT1_SEL 0x01 168878302eSOndrej Jirman #define SY8106A_REG_VOUT_COM 0x02 178878302eSOndrej Jirman #define SY8106A_REG_VOUT1_SEL_MASK 0x7f 188878302eSOndrej Jirman #define SY8106A_DISABLE_REG BIT(0) 198878302eSOndrej Jirman /* 208878302eSOndrej Jirman * The I2C controlled voltage will only work when this bit is set; otherwise 218878302eSOndrej Jirman * it will behave like a fixed regulator. 228878302eSOndrej Jirman */ 238878302eSOndrej Jirman #define SY8106A_GO_BIT BIT(7) 248878302eSOndrej Jirman 258878302eSOndrej Jirman static const struct regmap_config sy8106a_regmap_config = { 268878302eSOndrej Jirman .reg_bits = 8, 278878302eSOndrej Jirman .val_bits = 8, 288878302eSOndrej Jirman }; 298878302eSOndrej Jirman 308878302eSOndrej Jirman static const struct regulator_ops sy8106a_ops = { 318878302eSOndrej Jirman .set_voltage_sel = regulator_set_voltage_sel_regmap, 328878302eSOndrej Jirman .set_voltage_time_sel = regulator_set_voltage_time_sel, 338878302eSOndrej Jirman .get_voltage_sel = regulator_get_voltage_sel_regmap, 348878302eSOndrej Jirman .list_voltage = regulator_list_voltage_linear, 358878302eSOndrej Jirman /* Enabling/disabling the regulator is not yet implemented */ 368878302eSOndrej Jirman }; 378878302eSOndrej Jirman 388878302eSOndrej Jirman /* Default limits measured in millivolts */ 398878302eSOndrej Jirman #define SY8106A_MIN_MV 680 408878302eSOndrej Jirman #define SY8106A_MAX_MV 1950 418878302eSOndrej Jirman #define SY8106A_STEP_MV 10 428878302eSOndrej Jirman 438878302eSOndrej Jirman static const struct regulator_desc sy8106a_reg = { 448878302eSOndrej Jirman .name = "SY8106A", 458878302eSOndrej Jirman .id = 0, 468878302eSOndrej Jirman .ops = &sy8106a_ops, 478878302eSOndrej Jirman .type = REGULATOR_VOLTAGE, 488878302eSOndrej Jirman .n_voltages = ((SY8106A_MAX_MV - SY8106A_MIN_MV) / SY8106A_STEP_MV) + 1, 498878302eSOndrej Jirman .min_uV = (SY8106A_MIN_MV * 1000), 508878302eSOndrej Jirman .uV_step = (SY8106A_STEP_MV * 1000), 518878302eSOndrej Jirman .vsel_reg = SY8106A_REG_VOUT1_SEL, 528878302eSOndrej Jirman .vsel_mask = SY8106A_REG_VOUT1_SEL_MASK, 538878302eSOndrej Jirman /* 548878302eSOndrej Jirman * This ramp_delay is a conservative default value which works on 558878302eSOndrej Jirman * H3/H5 boards VDD-CPUX situations. 568878302eSOndrej Jirman */ 578878302eSOndrej Jirman .ramp_delay = 200, 588878302eSOndrej Jirman .owner = THIS_MODULE, 598878302eSOndrej Jirman }; 608878302eSOndrej Jirman 618878302eSOndrej Jirman /* 628878302eSOndrej Jirman * I2C driver interface functions 638878302eSOndrej Jirman */ 6477e29598SAxel Lin static int sy8106a_i2c_probe(struct i2c_client *i2c) 658878302eSOndrej Jirman { 668878302eSOndrej Jirman struct device *dev = &i2c->dev; 675d7ebba3SAxel Lin struct regulator_dev *rdev; 688878302eSOndrej Jirman struct regulator_config config = { }; 695d7ebba3SAxel Lin struct regmap *regmap; 708878302eSOndrej Jirman unsigned int reg, vsel; 715d7ebba3SAxel Lin u32 fixed_voltage; 728878302eSOndrej Jirman int error; 738878302eSOndrej Jirman 748878302eSOndrej Jirman error = of_property_read_u32(dev->of_node, "silergy,fixed-microvolt", 755d7ebba3SAxel Lin &fixed_voltage); 768878302eSOndrej Jirman if (error) 778878302eSOndrej Jirman return error; 788878302eSOndrej Jirman 795d7ebba3SAxel Lin if (fixed_voltage < SY8106A_MIN_MV * 1000 || 805d7ebba3SAxel Lin fixed_voltage > SY8106A_MAX_MV * 1000) 818878302eSOndrej Jirman return -EINVAL; 828878302eSOndrej Jirman 835d7ebba3SAxel Lin regmap = devm_regmap_init_i2c(i2c, &sy8106a_regmap_config); 845d7ebba3SAxel Lin if (IS_ERR(regmap)) { 855d7ebba3SAxel Lin error = PTR_ERR(regmap); 868878302eSOndrej Jirman dev_err(dev, "Failed to allocate register map: %d\n", error); 878878302eSOndrej Jirman return error; 888878302eSOndrej Jirman } 898878302eSOndrej Jirman 908878302eSOndrej Jirman config.dev = &i2c->dev; 915d7ebba3SAxel Lin config.regmap = regmap; 928878302eSOndrej Jirman 938878302eSOndrej Jirman config.of_node = dev->of_node; 948878302eSOndrej Jirman config.init_data = of_get_regulator_init_data(dev, dev->of_node, 958878302eSOndrej Jirman &sy8106a_reg); 968878302eSOndrej Jirman 978878302eSOndrej Jirman if (!config.init_data) 988878302eSOndrej Jirman return -ENOMEM; 998878302eSOndrej Jirman 1008878302eSOndrej Jirman /* Ensure GO_BIT is enabled when probing */ 1015d7ebba3SAxel Lin error = regmap_read(regmap, SY8106A_REG_VOUT1_SEL, ®); 1028878302eSOndrej Jirman if (error) 1038878302eSOndrej Jirman return error; 1048878302eSOndrej Jirman 1058878302eSOndrej Jirman if (!(reg & SY8106A_GO_BIT)) { 1065d7ebba3SAxel Lin vsel = (fixed_voltage / 1000 - SY8106A_MIN_MV) / 1078878302eSOndrej Jirman SY8106A_STEP_MV; 1088878302eSOndrej Jirman 1095d7ebba3SAxel Lin error = regmap_write(regmap, SY8106A_REG_VOUT1_SEL, 1108878302eSOndrej Jirman vsel | SY8106A_GO_BIT); 1118878302eSOndrej Jirman if (error) 1128878302eSOndrej Jirman return error; 1138878302eSOndrej Jirman } 1148878302eSOndrej Jirman 1158878302eSOndrej Jirman /* Probe regulator */ 1168878302eSOndrej Jirman rdev = devm_regulator_register(&i2c->dev, &sy8106a_reg, &config); 1178878302eSOndrej Jirman if (IS_ERR(rdev)) { 1188878302eSOndrej Jirman error = PTR_ERR(rdev); 1198878302eSOndrej Jirman dev_err(&i2c->dev, "Failed to register SY8106A regulator: %d\n", error); 1208878302eSOndrej Jirman return error; 1218878302eSOndrej Jirman } 1228878302eSOndrej Jirman 1238878302eSOndrej Jirman return 0; 1248878302eSOndrej Jirman } 1258878302eSOndrej Jirman 1268878302eSOndrej Jirman static const struct of_device_id sy8106a_i2c_of_match[] = { 1278878302eSOndrej Jirman { .compatible = "silergy,sy8106a" }, 1288878302eSOndrej Jirman { }, 1298878302eSOndrej Jirman }; 1308878302eSOndrej Jirman MODULE_DEVICE_TABLE(of, sy8106a_i2c_of_match); 1318878302eSOndrej Jirman 1328878302eSOndrej Jirman static const struct i2c_device_id sy8106a_i2c_id[] = { 1338878302eSOndrej Jirman { "sy8106a", 0 }, 1348878302eSOndrej Jirman { }, 1358878302eSOndrej Jirman }; 1368878302eSOndrej Jirman MODULE_DEVICE_TABLE(i2c, sy8106a_i2c_id); 1378878302eSOndrej Jirman 1388878302eSOndrej Jirman static struct i2c_driver sy8106a_regulator_driver = { 1398878302eSOndrej Jirman .driver = { 1408878302eSOndrej Jirman .name = "sy8106a", 1418878302eSOndrej Jirman .of_match_table = of_match_ptr(sy8106a_i2c_of_match), 1428878302eSOndrej Jirman }, 14377e29598SAxel Lin .probe_new = sy8106a_i2c_probe, 1448878302eSOndrej Jirman .id_table = sy8106a_i2c_id, 1458878302eSOndrej Jirman }; 1468878302eSOndrej Jirman 1478878302eSOndrej Jirman module_i2c_driver(sy8106a_regulator_driver); 1488878302eSOndrej Jirman 1498878302eSOndrej Jirman MODULE_AUTHOR("Ondřej Jirman <megous@megous.com>"); 1508878302eSOndrej Jirman MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.io>"); 1518878302eSOndrej Jirman MODULE_DESCRIPTION("Regulator device driver for Silergy SY8106A"); 1528878302eSOndrej Jirman MODULE_LICENSE("GPL"); 153