| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (C) 2022 Richtek Technology Corp.
- *
- * Author: ChiYuan Huang <cy_huang@richtek.com>
- *
- */
- #include <linux/bits.h>
- #include <linux/delay.h>
- #include <linux/gpio/consumer.h>
- #include <linux/i2c.h>
- #include <linux/interrupt.h>
- #include <linux/kernel.h>
- #include <linux/mod_devicetable.h>
- #include <linux/module.h>
- #include <linux/pm_runtime.h>
- #include <linux/regmap.h>
- #include <linux/regulator/consumer.h>
- #include <linux/regulator/driver.h>
- #include <linux/regulator/of_regulator.h>
- #define RT6190_REG_VID 0x00
- #define RT6190_REG_OUTV 0x01
- #define RT6190_REG_OUTC 0x03
- #define RT6190_REG_SET1 0x0D
- #define RT6190_REG_SET2 0x0E
- #define RT6190_REG_SET4 0x10
- #define RT6190_REG_RATIO 0x11
- #define RT6190_REG_OUT_VOLT_L 0x12
- #define RT6190_REG_TEMP_H 0x1B
- #define RT6190_REG_STAT1 0x1C
- #define RT6190_REG_ALERT1 0x1E
- #define RT6190_REG_ALERT2 0x1F
- #define RT6190_REG_MASK2 0x21
- #define RT6190_REG_OCPEN 0x28
- #define RT6190_REG_SET5 0x29
- #define RT6190_REG_VBUSC_ADC 0x32
- #define RT6190_REG_BUSC_VOLT_L 0x33
- #define RT6190_REG_BUSC_VOLT_H 0x34
- #define RT6190_REG_STAT3 0x37
- #define RT6190_REG_ALERT3 0x38
- #define RT6190_REG_MASK3 0x39
- #define RT6190_ENPWM_MASK BIT(7)
- #define RT6190_ENDCHG_MASK BIT(4)
- #define RT6190_ALERT_OTPEVT BIT(6)
- #define RT6190_ALERT_UVPEVT BIT(5)
- #define RT6190_ALERT_OVPEVT BIT(4)
- #define RT6190_ENGCP_MASK BIT(1)
- #define RT6190_FCCM_MASK BIT(7)
- #define RICHTEK_VID 0x82
- #define RT6190_OUT_MIN_UV 3000000
- #define RT6190_OUT_MAX_UV 32000000
- #define RT6190_OUT_STEP_UV 20000
- #define RT6190_OUT_N_VOLT (RT6190_OUT_MAX_UV / RT6190_OUT_STEP_UV + 1)
- #define RT6190_OUTV_MINSEL 150
- #define RT6190_OUT_MIN_UA 306000
- #define RT6190_OUT_MAX_UA 12114000
- #define RT6190_OUT_STEP_UA 24000
- #define RT6190_OUTC_MINSEL 19
- #define RT6190_EN_TIME_US 500
- #define RT6190_PSM_MODE 0
- #define RT6190_FCCM_MODE 1
- struct rt6190_data {
- struct device *dev;
- struct regmap *regmap;
- struct gpio_desc *enable_gpio;
- unsigned int cached_alert_evt;
- };
- static int rt6190_out_set_voltage_sel(struct regulator_dev *rdev,
- unsigned int selector)
- {
- struct regmap *regmap = rdev_get_regmap(rdev);
- __le16 le_sel = cpu_to_le16(selector);
- return regmap_raw_write(regmap, RT6190_REG_OUTV, &le_sel,
- sizeof(le_sel));
- }
- static int rt6190_out_get_voltage_sel(struct regulator_dev *rdev)
- {
- struct regmap *regmap = rdev_get_regmap(rdev);
- __le16 le_sel;
- int ret;
- ret = regmap_raw_read(regmap, RT6190_REG_OUTV, &le_sel, sizeof(le_sel));
- return ret ?: le16_to_cpu(le_sel);
- }
- static int rt6190_out_enable(struct regulator_dev *rdev)
- {
- struct rt6190_data *data = rdev_get_drvdata(rdev);
- struct regmap *regmap = rdev_get_regmap(rdev);
- u8 out_cfg[4];
- int ret;
- pm_runtime_get_sync(data->dev);
- /*
- * From off to on, vout config will restore to IC default.
- * Read vout configs before enable, and restore them after enable
- */
- ret = regmap_raw_read(regmap, RT6190_REG_OUTV, out_cfg,
- sizeof(out_cfg));
- if (ret)
- return ret;
- ret = regulator_enable_regmap(rdev);
- if (ret)
- return ret;
- ret = regmap_raw_write(regmap, RT6190_REG_OUTV, out_cfg,
- sizeof(out_cfg));
- if (ret)
- return ret;
- return regmap_update_bits(regmap, RT6190_REG_SET5, RT6190_ENGCP_MASK,
- RT6190_ENGCP_MASK);
- }
- static int rt6190_out_disable(struct regulator_dev *rdev)
- {
- struct rt6190_data *data = rdev_get_drvdata(rdev);
- struct regmap *regmap = rdev_get_regmap(rdev);
- int ret;
- ret = regmap_update_bits(regmap, RT6190_REG_SET5, RT6190_ENGCP_MASK, 0);
- if (ret)
- return ret;
- ret = regulator_disable_regmap(rdev);
- if (ret)
- return ret;
- /* cleared cached alert event */
- data->cached_alert_evt = 0;
- pm_runtime_put(data->dev);
- return 0;
- }
- static int rt6190_out_set_current_limit(struct regulator_dev *rdev, int min_uA,
- int max_uA)
- {
- struct regmap *regmap = rdev_get_regmap(rdev);
- int csel, clim;
- __le16 le_csel;
- if (min_uA < RT6190_OUT_MIN_UA || max_uA > RT6190_OUT_MAX_UA)
- return -EINVAL;
- csel = DIV_ROUND_UP(min_uA - RT6190_OUT_MIN_UA, RT6190_OUT_STEP_UA);
- clim = RT6190_OUT_MIN_UA + RT6190_OUT_STEP_UA * csel;
- if (clim > max_uA)
- return -EINVAL;
- csel += RT6190_OUTC_MINSEL;
- le_csel = cpu_to_le16(csel);
- return regmap_raw_write(regmap, RT6190_REG_OUTC, &le_csel,
- sizeof(le_csel));
- }
- static int rt6190_out_get_current_limit(struct regulator_dev *rdev)
- {
- struct regmap *regmap = rdev_get_regmap(rdev);
- __le16 le_csel;
- int csel, ret;
- ret = regmap_raw_read(regmap, RT6190_REG_OUTC, &le_csel,
- sizeof(le_csel));
- if (ret)
- return ret;
- csel = le16_to_cpu(le_csel);
- csel -= RT6190_OUTC_MINSEL;
- return RT6190_OUT_MIN_UA + RT6190_OUT_STEP_UA * csel;
- }
- static int rt6190_out_set_mode(struct regulator_dev *rdev, unsigned int mode)
- {
- struct regmap *regmap = rdev_get_regmap(rdev);
- unsigned int val;
- switch (mode) {
- case REGULATOR_MODE_FAST:
- val = RT6190_FCCM_MASK;
- break;
- case REGULATOR_MODE_NORMAL:
- val = 0;
- break;
- default:
- return -EINVAL;
- }
- return regmap_update_bits(regmap, RT6190_REG_SET1, RT6190_FCCM_MASK,
- val);
- }
- static unsigned int rt6190_out_get_mode(struct regulator_dev *rdev)
- {
- struct regmap *regmap = rdev_get_regmap(rdev);
- unsigned int config;
- int ret;
- ret = regmap_read(regmap, RT6190_REG_SET1, &config);
- if (ret)
- return REGULATOR_MODE_INVALID;
- if (config & RT6190_FCCM_MASK)
- return REGULATOR_MODE_FAST;
- return REGULATOR_MODE_NORMAL;
- }
- static int rt6190_out_get_error_flags(struct regulator_dev *rdev,
- unsigned int *flags)
- {
- struct rt6190_data *data = rdev_get_drvdata(rdev);
- unsigned int state, rpt_flags = 0;
- int ret;
- ret = regmap_read(data->regmap, RT6190_REG_STAT1, &state);
- if (ret)
- return ret;
- state |= data->cached_alert_evt;
- if (state & RT6190_ALERT_OTPEVT)
- rpt_flags |= REGULATOR_ERROR_OVER_TEMP;
- if (state & RT6190_ALERT_UVPEVT)
- rpt_flags |= REGULATOR_ERROR_UNDER_VOLTAGE;
- if (state & RT6190_ALERT_OVPEVT)
- rpt_flags |= REGULATOR_ERROR_REGULATION_OUT;
- *flags = rpt_flags;
- return 0;
- }
- static unsigned int rt6190_out_of_map_mode(unsigned int mode)
- {
- switch (mode) {
- case RT6190_PSM_MODE:
- return REGULATOR_MODE_NORMAL;
- case RT6190_FCCM_MODE:
- return REGULATOR_MODE_FAST;
- default:
- return REGULATOR_MODE_INVALID;
- }
- }
- static const struct regulator_ops rt6190_regulator_ops = {
- .list_voltage = regulator_list_voltage_linear,
- .set_voltage_sel = rt6190_out_set_voltage_sel,
- .get_voltage_sel = rt6190_out_get_voltage_sel,
- .enable = rt6190_out_enable,
- .disable = rt6190_out_disable,
- .is_enabled = regulator_is_enabled_regmap,
- .set_current_limit = rt6190_out_set_current_limit,
- .get_current_limit = rt6190_out_get_current_limit,
- .set_active_discharge = regulator_set_active_discharge_regmap,
- .set_mode = rt6190_out_set_mode,
- .get_mode = rt6190_out_get_mode,
- .get_error_flags = rt6190_out_get_error_flags,
- };
- static const struct regulator_desc rt6190_regulator_desc = {
- .name = "rt6190-regulator",
- .type = REGULATOR_VOLTAGE,
- .owner = THIS_MODULE,
- .ops = &rt6190_regulator_ops,
- .min_uV = RT6190_OUT_MIN_UV,
- .uV_step = RT6190_OUT_STEP_UV,
- .n_voltages = RT6190_OUT_N_VOLT,
- .linear_min_sel = RT6190_OUTV_MINSEL,
- .enable_reg = RT6190_REG_SET2,
- .enable_mask = RT6190_ENPWM_MASK,
- .active_discharge_reg = RT6190_REG_SET2,
- .active_discharge_mask = RT6190_ENDCHG_MASK,
- .active_discharge_on = RT6190_ENDCHG_MASK,
- .of_map_mode = rt6190_out_of_map_mode,
- };
- static bool rt6190_is_volatile_reg(struct device *dev, unsigned int reg)
- {
- switch (reg) {
- case RT6190_REG_OUT_VOLT_L ... RT6190_REG_ALERT2:
- case RT6190_REG_BUSC_VOLT_L ... RT6190_REG_BUSC_VOLT_H:
- case RT6190_REG_STAT3 ... RT6190_REG_ALERT3:
- return true;
- default:
- return false;
- }
- }
- static const struct regmap_config rt6190_regmap_config = {
- .name = "rt6190",
- .cache_type = REGCACHE_FLAT,
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = RT6190_REG_MASK3,
- .num_reg_defaults_raw = RT6190_REG_MASK3 + 1,
- .volatile_reg = rt6190_is_volatile_reg,
- };
- static irqreturn_t rt6190_irq_handler(int irq, void *devid)
- {
- struct regulator_dev *rdev = devid;
- struct rt6190_data *data = rdev_get_drvdata(rdev);
- unsigned int alert;
- int ret;
- ret = regmap_read(data->regmap, RT6190_REG_ALERT1, &alert);
- if (ret)
- return IRQ_NONE;
- /* Write clear alert events */
- ret = regmap_write(data->regmap, RT6190_REG_ALERT1, alert);
- if (ret)
- return IRQ_NONE;
- data->cached_alert_evt |= alert;
- if (alert & RT6190_ALERT_OTPEVT)
- regulator_notifier_call_chain(rdev, REGULATOR_EVENT_OVER_TEMP, NULL);
- if (alert & RT6190_ALERT_UVPEVT)
- regulator_notifier_call_chain(rdev, REGULATOR_EVENT_UNDER_VOLTAGE, NULL);
- if (alert & RT6190_ALERT_OVPEVT)
- regulator_notifier_call_chain(rdev, REGULATOR_EVENT_REGULATION_OUT, NULL);
- return IRQ_HANDLED;
- }
- static int rt6190_init_registers(struct regmap *regmap)
- {
- int ret;
- /* Enable_ADC = 1 */
- ret = regmap_write(regmap, RT6190_REG_SET4, 0x82);
- if (ret)
- return ret;
- /* Config default VOUT ratio to be higher */
- ret = regmap_write(regmap, RT6190_REG_RATIO, 0x20);
- /* Mask unused alert */
- ret = regmap_write(regmap, RT6190_REG_MASK2, 0);
- if (ret)
- return ret;
- /* OCP config */
- ret = regmap_write(regmap, RT6190_REG_OCPEN, 0);
- if (ret)
- return ret;
- /* Enable VBUSC ADC */
- return regmap_write(regmap, RT6190_REG_VBUSC_ADC, 0x02);
- }
- static int rt6190_probe(struct i2c_client *i2c)
- {
- struct device *dev = &i2c->dev;
- struct rt6190_data *data;
- struct gpio_desc *enable_gpio;
- struct regmap *regmap;
- struct regulator_dev *rdev;
- struct regulator_config cfg = {};
- unsigned int vid;
- int ret;
- data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
- enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH);
- if (IS_ERR(enable_gpio))
- return dev_err_probe(dev, PTR_ERR(enable_gpio), "Failed to get 'enable' gpio\n");
- else if (enable_gpio)
- usleep_range(RT6190_EN_TIME_US, RT6190_EN_TIME_US * 2);
- regmap = devm_regmap_init_i2c(i2c, &rt6190_regmap_config);
- if (IS_ERR(regmap))
- return dev_err_probe(dev, PTR_ERR(regmap), "Failed to init regmap\n");
- data->dev = dev;
- data->enable_gpio = enable_gpio;
- data->regmap = regmap;
- i2c_set_clientdata(i2c, data);
- ret = regmap_read(regmap, RT6190_REG_VID, &vid);
- if (ret)
- return dev_err_probe(dev, ret, "Failed to read VID\n");
- if (vid != RICHTEK_VID)
- return dev_err_probe(dev, -ENODEV, "Incorrect VID 0x%02x\n", vid);
- ret = rt6190_init_registers(regmap);
- if (ret)
- return dev_err_probe(dev, ret, "Failed to init registers\n");
- pm_runtime_set_active(dev);
- ret = devm_pm_runtime_enable(dev);
- if (ret)
- return dev_err_probe(dev, ret, "Failed to set pm_runtime enable\n");
- cfg.dev = dev;
- cfg.of_node = dev->of_node;
- cfg.driver_data = data;
- cfg.init_data = of_get_regulator_init_data(dev, dev->of_node,
- &rt6190_regulator_desc);
- rdev = devm_regulator_register(dev, &rt6190_regulator_desc, &cfg);
- if (IS_ERR(rdev))
- return dev_err_probe(dev, PTR_ERR(rdev), "Failed to register regulator\n");
- if (i2c->irq) {
- ret = devm_request_threaded_irq(dev, i2c->irq, NULL,
- rt6190_irq_handler,
- IRQF_ONESHOT, dev_name(dev),
- rdev);
- if (ret)
- return dev_err_probe(dev, ret, "Failed to register interrupt\n");
- }
- return 0;
- }
- static int rt6190_runtime_suspend(struct device *dev)
- {
- struct rt6190_data *data = dev_get_drvdata(dev);
- struct regmap *regmap = data->regmap;
- if (!data->enable_gpio)
- return 0;
- regcache_cache_only(regmap, true);
- regcache_mark_dirty(regmap);
- gpiod_set_value(data->enable_gpio, 0);
- return 0;
- }
- static int rt6190_runtime_resume(struct device *dev)
- {
- struct rt6190_data *data = dev_get_drvdata(dev);
- struct regmap *regmap = data->regmap;
- if (!data->enable_gpio)
- return 0;
- gpiod_set_value(data->enable_gpio, 1);
- usleep_range(RT6190_EN_TIME_US, RT6190_EN_TIME_US * 2);
- regcache_cache_only(regmap, false);
- return regcache_sync(regmap);
- }
- static const struct dev_pm_ops __maybe_unused rt6190_dev_pm = {
- RUNTIME_PM_OPS(rt6190_runtime_suspend, rt6190_runtime_resume, NULL)
- };
- static const struct of_device_id rt6190_of_dev_table[] = {
- { .compatible = "richtek,rt6190" },
- {}
- };
- MODULE_DEVICE_TABLE(of, rt6190_of_dev_table);
- static struct i2c_driver rt6190_driver = {
- .driver = {
- .name = "rt6190",
- .probe_type = PROBE_PREFER_ASYNCHRONOUS,
- .of_match_table = rt6190_of_dev_table,
- .pm = pm_ptr(&rt6190_dev_pm),
- },
- .probe = rt6190_probe,
- };
- module_i2c_driver(rt6190_driver);
- MODULE_DESCRIPTION("Richtek RT6190 regulator driver");
- MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
- MODULE_LICENSE("GPL");
|