14536f3b9SChiYuan Huang // SPDX-License-Identifier: GPL-2.0-only
24536f3b9SChiYuan Huang /*
34536f3b9SChiYuan Huang * Device driver for RT5739 regulator
44536f3b9SChiYuan Huang *
54536f3b9SChiYuan Huang * Copyright (C) 2023 Richtek Technology Corp.
64536f3b9SChiYuan Huang *
74536f3b9SChiYuan Huang * Author: ChiYuan Huang <cy_huang@richtek.com>
84536f3b9SChiYuan Huang */
94536f3b9SChiYuan Huang
104536f3b9SChiYuan Huang #include <linux/bits.h>
114536f3b9SChiYuan Huang #include <linux/gpio/consumer.h>
124536f3b9SChiYuan Huang #include <linux/i2c.h>
134536f3b9SChiYuan Huang #include <linux/kernel.h>
144536f3b9SChiYuan Huang #include <linux/mod_devicetable.h>
154536f3b9SChiYuan Huang #include <linux/property.h>
164536f3b9SChiYuan Huang #include <linux/regmap.h>
174536f3b9SChiYuan Huang #include <linux/regulator/driver.h>
184536f3b9SChiYuan Huang #include <linux/regulator/of_regulator.h>
194536f3b9SChiYuan Huang
204536f3b9SChiYuan Huang #define RT5739_AUTO_MODE 0
214536f3b9SChiYuan Huang #define RT5739_FPWM_MODE 1
224536f3b9SChiYuan Huang
234536f3b9SChiYuan Huang #define RT5739_REG_NSEL0 0x00
244536f3b9SChiYuan Huang #define RT5739_REG_NSEL1 0x01
254536f3b9SChiYuan Huang #define RT5739_REG_CNTL1 0x02
264536f3b9SChiYuan Huang #define RT5739_REG_ID1 0x03
274536f3b9SChiYuan Huang #define RT5739_REG_CNTL2 0x06
284536f3b9SChiYuan Huang #define RT5739_REG_CNTL4 0x08
294536f3b9SChiYuan Huang
304536f3b9SChiYuan Huang #define RT5739_VSEL_MASK GENMASK(7, 0)
314536f3b9SChiYuan Huang #define RT5739_MODEVSEL1_MASK BIT(1)
324536f3b9SChiYuan Huang #define RT5739_MODEVSEL0_MASK BIT(0)
334536f3b9SChiYuan Huang #define RT5739_VID_MASK GENMASK(7, 5)
34*6f5e2858SChiYuan Huang #define RT5739_DID_MASK GENMASK(3, 0)
354536f3b9SChiYuan Huang #define RT5739_ACTD_MASK BIT(7)
364536f3b9SChiYuan Huang #define RT5739_ENVSEL1_MASK BIT(1)
374536f3b9SChiYuan Huang #define RT5739_ENVSEL0_MASK BIT(0)
384536f3b9SChiYuan Huang
39*6f5e2858SChiYuan Huang #define RT5733_CHIPDIE_ID 0x1
40*6f5e2858SChiYuan Huang #define RT5733_VOLT_MINUV 270000
41*6f5e2858SChiYuan Huang #define RT5733_VOLT_MAXUV 1401250
42*6f5e2858SChiYuan Huang #define RT5733_VOLT_STPUV 6250
43*6f5e2858SChiYuan Huang #define RT5733_N_VOLTS 182
44*6f5e2858SChiYuan Huang
454536f3b9SChiYuan Huang #define RT5739_VOLT_MINUV 300000
464536f3b9SChiYuan Huang #define RT5739_VOLT_MAXUV 1300000
474536f3b9SChiYuan Huang #define RT5739_VOLT_STPUV 5000
484536f3b9SChiYuan Huang #define RT5739_N_VOLTS 201
494536f3b9SChiYuan Huang #define RT5739_I2CRDY_TIMEUS 1000
504536f3b9SChiYuan Huang
rt5739_set_mode(struct regulator_dev * rdev,unsigned int mode)514536f3b9SChiYuan Huang static int rt5739_set_mode(struct regulator_dev *rdev, unsigned int mode)
524536f3b9SChiYuan Huang {
534536f3b9SChiYuan Huang const struct regulator_desc *desc = rdev->desc;
544536f3b9SChiYuan Huang struct regmap *regmap = rdev_get_regmap(rdev);
554536f3b9SChiYuan Huang unsigned int mask, val;
564536f3b9SChiYuan Huang
574536f3b9SChiYuan Huang if (desc->vsel_reg == RT5739_REG_NSEL0)
584536f3b9SChiYuan Huang mask = RT5739_MODEVSEL0_MASK;
594536f3b9SChiYuan Huang else
604536f3b9SChiYuan Huang mask = RT5739_MODEVSEL1_MASK;
614536f3b9SChiYuan Huang
624536f3b9SChiYuan Huang switch (mode) {
634536f3b9SChiYuan Huang case REGULATOR_MODE_FAST:
644536f3b9SChiYuan Huang val = mask;
654536f3b9SChiYuan Huang break;
664536f3b9SChiYuan Huang case REGULATOR_MODE_NORMAL:
674536f3b9SChiYuan Huang val = 0;
684536f3b9SChiYuan Huang break;
694536f3b9SChiYuan Huang default:
704536f3b9SChiYuan Huang return -EINVAL;
714536f3b9SChiYuan Huang }
724536f3b9SChiYuan Huang
734536f3b9SChiYuan Huang return regmap_update_bits(regmap, RT5739_REG_CNTL1, mask, val);
744536f3b9SChiYuan Huang }
754536f3b9SChiYuan Huang
rt5739_get_mode(struct regulator_dev * rdev)764536f3b9SChiYuan Huang static unsigned int rt5739_get_mode(struct regulator_dev *rdev)
774536f3b9SChiYuan Huang {
784536f3b9SChiYuan Huang const struct regulator_desc *desc = rdev->desc;
794536f3b9SChiYuan Huang struct regmap *regmap = rdev_get_regmap(rdev);
804536f3b9SChiYuan Huang unsigned int mask, val;
814536f3b9SChiYuan Huang int ret;
824536f3b9SChiYuan Huang
834536f3b9SChiYuan Huang if (desc->vsel_reg == RT5739_REG_NSEL0)
844536f3b9SChiYuan Huang mask = RT5739_MODEVSEL0_MASK;
854536f3b9SChiYuan Huang else
864536f3b9SChiYuan Huang mask = RT5739_MODEVSEL1_MASK;
874536f3b9SChiYuan Huang
884536f3b9SChiYuan Huang ret = regmap_read(regmap, RT5739_REG_CNTL1, &val);
894536f3b9SChiYuan Huang if (ret)
904536f3b9SChiYuan Huang return REGULATOR_MODE_INVALID;
914536f3b9SChiYuan Huang
924536f3b9SChiYuan Huang if (val & mask)
934536f3b9SChiYuan Huang return REGULATOR_MODE_FAST;
944536f3b9SChiYuan Huang
954536f3b9SChiYuan Huang return REGULATOR_MODE_NORMAL;
964536f3b9SChiYuan Huang }
974536f3b9SChiYuan Huang
rt5739_set_suspend_voltage(struct regulator_dev * rdev,int uV)984536f3b9SChiYuan Huang static int rt5739_set_suspend_voltage(struct regulator_dev *rdev, int uV)
994536f3b9SChiYuan Huang {
1004536f3b9SChiYuan Huang const struct regulator_desc *desc = rdev->desc;
1014536f3b9SChiYuan Huang struct regmap *regmap = rdev_get_regmap(rdev);
1024536f3b9SChiYuan Huang unsigned int reg, vsel;
103*6f5e2858SChiYuan Huang int max_uV;
1044536f3b9SChiYuan Huang
105*6f5e2858SChiYuan Huang max_uV = desc->min_uV + desc->uV_step * (desc->n_voltages - 1);
106*6f5e2858SChiYuan Huang
107*6f5e2858SChiYuan Huang if (uV < desc->min_uV || uV > max_uV)
1084536f3b9SChiYuan Huang return -EINVAL;
1094536f3b9SChiYuan Huang
1104536f3b9SChiYuan Huang if (desc->vsel_reg == RT5739_REG_NSEL0)
1114536f3b9SChiYuan Huang reg = RT5739_REG_NSEL1;
1124536f3b9SChiYuan Huang else
1134536f3b9SChiYuan Huang reg = RT5739_REG_NSEL0;
1144536f3b9SChiYuan Huang
115*6f5e2858SChiYuan Huang vsel = (uV - desc->min_uV) / desc->uV_step;
1164536f3b9SChiYuan Huang return regmap_write(regmap, reg, vsel);
1174536f3b9SChiYuan Huang }
1184536f3b9SChiYuan Huang
rt5739_set_suspend_enable(struct regulator_dev * rdev)1194536f3b9SChiYuan Huang static int rt5739_set_suspend_enable(struct regulator_dev *rdev)
1204536f3b9SChiYuan Huang {
1214536f3b9SChiYuan Huang const struct regulator_desc *desc = rdev->desc;
1224536f3b9SChiYuan Huang struct regmap *regmap = rdev_get_regmap(rdev);
1234536f3b9SChiYuan Huang unsigned int mask;
1244536f3b9SChiYuan Huang
1254536f3b9SChiYuan Huang if (desc->vsel_reg == RT5739_REG_NSEL0)
1264536f3b9SChiYuan Huang mask = RT5739_ENVSEL1_MASK;
1274536f3b9SChiYuan Huang else
1284536f3b9SChiYuan Huang mask = RT5739_ENVSEL0_MASK;
1294536f3b9SChiYuan Huang
1304536f3b9SChiYuan Huang return regmap_update_bits(regmap, desc->enable_reg, mask, mask);
1314536f3b9SChiYuan Huang }
1324536f3b9SChiYuan Huang
rt5739_set_suspend_disable(struct regulator_dev * rdev)1334536f3b9SChiYuan Huang static int rt5739_set_suspend_disable(struct regulator_dev *rdev)
1344536f3b9SChiYuan Huang {
1354536f3b9SChiYuan Huang const struct regulator_desc *desc = rdev->desc;
1364536f3b9SChiYuan Huang struct regmap *regmap = rdev_get_regmap(rdev);
1374536f3b9SChiYuan Huang unsigned int mask;
1384536f3b9SChiYuan Huang
1394536f3b9SChiYuan Huang if (desc->vsel_reg == RT5739_REG_NSEL0)
1404536f3b9SChiYuan Huang mask = RT5739_ENVSEL1_MASK;
1414536f3b9SChiYuan Huang else
1424536f3b9SChiYuan Huang mask = RT5739_ENVSEL0_MASK;
1434536f3b9SChiYuan Huang
1444536f3b9SChiYuan Huang return regmap_update_bits(regmap, desc->enable_reg, mask, 0);
1454536f3b9SChiYuan Huang }
1464536f3b9SChiYuan Huang
rt5739_set_suspend_mode(struct regulator_dev * rdev,unsigned int mode)1474536f3b9SChiYuan Huang static int rt5739_set_suspend_mode(struct regulator_dev *rdev,
1484536f3b9SChiYuan Huang unsigned int mode)
1494536f3b9SChiYuan Huang {
1504536f3b9SChiYuan Huang const struct regulator_desc *desc = rdev->desc;
1514536f3b9SChiYuan Huang struct regmap *regmap = rdev_get_regmap(rdev);
1524536f3b9SChiYuan Huang unsigned int mask, val;
1534536f3b9SChiYuan Huang
1544536f3b9SChiYuan Huang if (desc->vsel_reg == RT5739_REG_NSEL0)
1554536f3b9SChiYuan Huang mask = RT5739_MODEVSEL1_MASK;
1564536f3b9SChiYuan Huang else
1574536f3b9SChiYuan Huang mask = RT5739_MODEVSEL0_MASK;
1584536f3b9SChiYuan Huang
1594536f3b9SChiYuan Huang switch (mode) {
1604536f3b9SChiYuan Huang case REGULATOR_MODE_FAST:
1614536f3b9SChiYuan Huang val = mask;
1624536f3b9SChiYuan Huang break;
1634536f3b9SChiYuan Huang case REGULATOR_MODE_NORMAL:
1644536f3b9SChiYuan Huang val = 0;
1654536f3b9SChiYuan Huang break;
1664536f3b9SChiYuan Huang default:
1674536f3b9SChiYuan Huang return -EINVAL;
1684536f3b9SChiYuan Huang }
1694536f3b9SChiYuan Huang
1704536f3b9SChiYuan Huang return regmap_update_bits(regmap, RT5739_REG_CNTL1, mask, val);
1714536f3b9SChiYuan Huang }
1724536f3b9SChiYuan Huang
1734536f3b9SChiYuan Huang static const struct regulator_ops rt5739_regulator_ops = {
1744536f3b9SChiYuan Huang .list_voltage = regulator_list_voltage_linear,
1754536f3b9SChiYuan Huang .get_voltage_sel = regulator_get_voltage_sel_regmap,
1764536f3b9SChiYuan Huang .set_voltage_sel = regulator_set_voltage_sel_regmap,
1774536f3b9SChiYuan Huang .enable = regulator_enable_regmap,
1784536f3b9SChiYuan Huang .disable = regulator_disable_regmap,
1794536f3b9SChiYuan Huang .is_enabled = regulator_is_enabled_regmap,
1804536f3b9SChiYuan Huang .set_active_discharge = regulator_set_active_discharge_regmap,
1814536f3b9SChiYuan Huang .set_mode = rt5739_set_mode,
1824536f3b9SChiYuan Huang .get_mode = rt5739_get_mode,
1834536f3b9SChiYuan Huang .set_suspend_voltage = rt5739_set_suspend_voltage,
1844536f3b9SChiYuan Huang .set_suspend_enable = rt5739_set_suspend_enable,
1854536f3b9SChiYuan Huang .set_suspend_disable = rt5739_set_suspend_disable,
1864536f3b9SChiYuan Huang .set_suspend_mode = rt5739_set_suspend_mode,
1874536f3b9SChiYuan Huang };
1884536f3b9SChiYuan Huang
rt5739_of_map_mode(unsigned int mode)1894536f3b9SChiYuan Huang static unsigned int rt5739_of_map_mode(unsigned int mode)
1904536f3b9SChiYuan Huang {
1914536f3b9SChiYuan Huang switch (mode) {
1924536f3b9SChiYuan Huang case RT5739_AUTO_MODE:
1934536f3b9SChiYuan Huang return REGULATOR_MODE_NORMAL;
1944536f3b9SChiYuan Huang case RT5739_FPWM_MODE:
1954536f3b9SChiYuan Huang return REGULATOR_MODE_FAST;
1964536f3b9SChiYuan Huang default:
1974536f3b9SChiYuan Huang return REGULATOR_MODE_INVALID;
1984536f3b9SChiYuan Huang }
1994536f3b9SChiYuan Huang }
2004536f3b9SChiYuan Huang
rt5739_init_regulator_desc(struct regulator_desc * desc,bool vsel_active_high,u8 did)2014536f3b9SChiYuan Huang static void rt5739_init_regulator_desc(struct regulator_desc *desc,
202*6f5e2858SChiYuan Huang bool vsel_active_high, u8 did)
2034536f3b9SChiYuan Huang {
2044536f3b9SChiYuan Huang /* Fixed */
2054536f3b9SChiYuan Huang desc->name = "rt5739-regulator";
2064536f3b9SChiYuan Huang desc->owner = THIS_MODULE;
2074536f3b9SChiYuan Huang desc->ops = &rt5739_regulator_ops;
2084536f3b9SChiYuan Huang desc->vsel_mask = RT5739_VSEL_MASK;
2094536f3b9SChiYuan Huang desc->enable_reg = RT5739_REG_CNTL2;
2104536f3b9SChiYuan Huang desc->active_discharge_reg = RT5739_REG_CNTL1;
2114536f3b9SChiYuan Huang desc->active_discharge_mask = RT5739_ACTD_MASK;
2124536f3b9SChiYuan Huang desc->active_discharge_on = RT5739_ACTD_MASK;
2134536f3b9SChiYuan Huang desc->of_map_mode = rt5739_of_map_mode;
2144536f3b9SChiYuan Huang
2154536f3b9SChiYuan Huang /* Assigned by vsel level */
2164536f3b9SChiYuan Huang if (vsel_active_high) {
2174536f3b9SChiYuan Huang desc->vsel_reg = RT5739_REG_NSEL1;
2184536f3b9SChiYuan Huang desc->enable_mask = RT5739_ENVSEL1_MASK;
2194536f3b9SChiYuan Huang } else {
2204536f3b9SChiYuan Huang desc->vsel_reg = RT5739_REG_NSEL0;
2214536f3b9SChiYuan Huang desc->enable_mask = RT5739_ENVSEL0_MASK;
2224536f3b9SChiYuan Huang }
223*6f5e2858SChiYuan Huang
224*6f5e2858SChiYuan Huang /* Assigned by CHIPDIE ID */
225*6f5e2858SChiYuan Huang switch (did) {
226*6f5e2858SChiYuan Huang case RT5733_CHIPDIE_ID:
227*6f5e2858SChiYuan Huang desc->n_voltages = RT5733_N_VOLTS;
228*6f5e2858SChiYuan Huang desc->min_uV = RT5733_VOLT_MINUV;
229*6f5e2858SChiYuan Huang desc->uV_step = RT5733_VOLT_STPUV;
230*6f5e2858SChiYuan Huang break;
231*6f5e2858SChiYuan Huang default:
232*6f5e2858SChiYuan Huang desc->n_voltages = RT5739_N_VOLTS;
233*6f5e2858SChiYuan Huang desc->min_uV = RT5739_VOLT_MINUV;
234*6f5e2858SChiYuan Huang desc->uV_step = RT5739_VOLT_STPUV;
235*6f5e2858SChiYuan Huang break;
236*6f5e2858SChiYuan Huang }
2374536f3b9SChiYuan Huang }
2384536f3b9SChiYuan Huang
2394536f3b9SChiYuan Huang static const struct regmap_config rt5739_regmap_config = {
2404536f3b9SChiYuan Huang .name = "rt5739",
2414536f3b9SChiYuan Huang .reg_bits = 8,
2424536f3b9SChiYuan Huang .val_bits = 8,
2434536f3b9SChiYuan Huang .max_register = RT5739_REG_CNTL4,
2444536f3b9SChiYuan Huang };
2454536f3b9SChiYuan Huang
rt5739_probe(struct i2c_client * i2c)2464536f3b9SChiYuan Huang static int rt5739_probe(struct i2c_client *i2c)
2474536f3b9SChiYuan Huang {
2484536f3b9SChiYuan Huang struct device *dev = &i2c->dev;
2494536f3b9SChiYuan Huang struct regulator_desc *desc;
2504536f3b9SChiYuan Huang struct regmap *regmap;
2514536f3b9SChiYuan Huang struct gpio_desc *enable_gpio;
2524536f3b9SChiYuan Huang struct regulator_config cfg = {};
2534536f3b9SChiYuan Huang struct regulator_dev *rdev;
2544536f3b9SChiYuan Huang bool vsel_acth;
2554536f3b9SChiYuan Huang unsigned int vid;
2564536f3b9SChiYuan Huang int ret;
2574536f3b9SChiYuan Huang
2584536f3b9SChiYuan Huang desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
2594536f3b9SChiYuan Huang if (!desc)
2604536f3b9SChiYuan Huang return -ENOMEM;
2614536f3b9SChiYuan Huang
2624536f3b9SChiYuan Huang enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH);
2634536f3b9SChiYuan Huang if (IS_ERR(enable_gpio))
2644536f3b9SChiYuan Huang return dev_err_probe(dev, PTR_ERR(enable_gpio), "Failed to get 'enable' gpio\n");
2654536f3b9SChiYuan Huang else if (enable_gpio)
2664536f3b9SChiYuan Huang usleep_range(RT5739_I2CRDY_TIMEUS, RT5739_I2CRDY_TIMEUS + 1000);
2674536f3b9SChiYuan Huang
2684536f3b9SChiYuan Huang regmap = devm_regmap_init_i2c(i2c, &rt5739_regmap_config);
2694536f3b9SChiYuan Huang if (IS_ERR(regmap))
2704536f3b9SChiYuan Huang return dev_err_probe(dev, PTR_ERR(regmap), "Failed to init regmap\n");
2714536f3b9SChiYuan Huang
2724536f3b9SChiYuan Huang ret = regmap_read(regmap, RT5739_REG_ID1, &vid);
2734536f3b9SChiYuan Huang if (ret)
2744536f3b9SChiYuan Huang return dev_err_probe(dev, ret, "Failed to read VID\n");
2754536f3b9SChiYuan Huang
2764536f3b9SChiYuan Huang /* RT5739: (VID & MASK) must be 0 */
2774536f3b9SChiYuan Huang if (vid & RT5739_VID_MASK)
2784536f3b9SChiYuan Huang return dev_err_probe(dev, -ENODEV, "Incorrect VID (0x%02x)\n", vid);
2794536f3b9SChiYuan Huang
2804536f3b9SChiYuan Huang vsel_acth = device_property_read_bool(dev, "richtek,vsel-active-high");
2814536f3b9SChiYuan Huang
282*6f5e2858SChiYuan Huang rt5739_init_regulator_desc(desc, vsel_acth, vid & RT5739_DID_MASK);
2834536f3b9SChiYuan Huang
2844536f3b9SChiYuan Huang cfg.dev = dev;
2854536f3b9SChiYuan Huang cfg.of_node = dev_of_node(dev);
2864536f3b9SChiYuan Huang cfg.init_data = of_get_regulator_init_data(dev, dev_of_node(dev), desc);
2874536f3b9SChiYuan Huang rdev = devm_regulator_register(dev, desc, &cfg);
2884536f3b9SChiYuan Huang if (IS_ERR(rdev))
2894536f3b9SChiYuan Huang return dev_err_probe(dev, PTR_ERR(rdev), "Failed to register regulator\n");
2904536f3b9SChiYuan Huang
2914536f3b9SChiYuan Huang return 0;
2924536f3b9SChiYuan Huang }
2934536f3b9SChiYuan Huang
2944536f3b9SChiYuan Huang static const struct of_device_id rt5739_device_table[] = {
295*6f5e2858SChiYuan Huang { .compatible = "richtek,rt5733" },
2964536f3b9SChiYuan Huang { .compatible = "richtek,rt5739" },
2974536f3b9SChiYuan Huang { /* sentinel */ }
2984536f3b9SChiYuan Huang };
2994536f3b9SChiYuan Huang MODULE_DEVICE_TABLE(of, rt5739_device_table);
3004536f3b9SChiYuan Huang
3014536f3b9SChiYuan Huang static struct i2c_driver rt5739_driver = {
3024536f3b9SChiYuan Huang .driver = {
3034536f3b9SChiYuan Huang .name = "rt5739",
304bdce47bbSDouglas Anderson .probe_type = PROBE_PREFER_ASYNCHRONOUS,
3054536f3b9SChiYuan Huang .of_match_table = rt5739_device_table,
3064536f3b9SChiYuan Huang },
307d692cc61SUwe Kleine-König .probe = rt5739_probe,
3084536f3b9SChiYuan Huang };
3094536f3b9SChiYuan Huang module_i2c_driver(rt5739_driver);
3104536f3b9SChiYuan Huang
3114536f3b9SChiYuan Huang MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
3124536f3b9SChiYuan Huang MODULE_DESCRIPTION("Richtek RT5739 regulator driver");
3134536f3b9SChiYuan Huang MODULE_LICENSE("GPL");
314