Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
ChiYuan Huang | 2057 | 99.71% | 1 | 33.33% |
Doug Anderson | 5 | 0.24% | 1 | 33.33% |
Uwe Kleine-König | 1 | 0.05% | 1 | 33.33% |
Total | 2063 | 3 |
// 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");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1