From 35aa06286c0927a8444d851d6a1f2603e3dc9229 Mon Sep 17 00:00:00 2001 From: ye xingchen Date: Tue, 6 Dec 2022 11:14:08 +0800 Subject: power: supply: use sysfs_emit() instead of scnprintf() for sysfs show() As documented in Documentation/filesystems/sysfs.rst the sysfs show() function should use sysfs_emit() or sysfs_emit_at() to format the userspace return value. This replaces all instances of scnprintf() with sysfs_emit() in the power-supply subsystem. Signed-off-by: ye xingchen [reword commit message] Signed-off-by: Sebastian Reichel --- drivers/power/supply/ab8500_fg.c | 18 +++++++++--------- drivers/power/supply/bq24190_charger.c | 2 +- drivers/power/supply/bq24257_charger.c | 8 +++----- drivers/power/supply/lp8788-charger.c | 7 +++---- drivers/power/supply/max14577_charger.c | 2 +- drivers/power/supply/max77693_charger.c | 6 +++--- drivers/power/supply/twl4030_charger.c | 6 ++---- 7 files changed, 22 insertions(+), 27 deletions(-) diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c index c6c9804280db..d989eadaa933 100644 --- a/drivers/power/supply/ab8500_fg.c +++ b/drivers/power/supply/ab8500_fg.c @@ -2594,7 +2594,7 @@ static ssize_t ab8505_powercut_flagtime_read(struct device *dev, goto fail; } - return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F)); + return sysfs_emit(buf, "%d\n", (reg_value & 0x7F)); fail: return ret; @@ -2644,7 +2644,7 @@ static ssize_t ab8505_powercut_maxtime_read(struct device *dev, goto fail; } - return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F)); + return sysfs_emit(buf, "%d\n", (reg_value & 0x7F)); fail: return ret; @@ -2695,7 +2695,7 @@ static ssize_t ab8505_powercut_restart_read(struct device *dev, goto fail; } - return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0xF)); + return sysfs_emit(buf, "%d\n", (reg_value & 0xF)); fail: return ret; @@ -2746,7 +2746,7 @@ static ssize_t ab8505_powercut_timer_read(struct device *dev, goto fail; } - return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F)); + return sysfs_emit(buf, "%d\n", (reg_value & 0x7F)); fail: return ret; @@ -2769,7 +2769,7 @@ static ssize_t ab8505_powercut_restart_counter_read(struct device *dev, goto fail; } - return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0xF0) >> 4); + return sysfs_emit(buf, "%d\n", (reg_value & 0xF0) >> 4); fail: return ret; @@ -2790,7 +2790,7 @@ static ssize_t ab8505_powercut_read(struct device *dev, if (ret < 0) goto fail; - return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x1)); + return sysfs_emit(buf, "%d\n", (reg_value & 0x1)); fail: return ret; @@ -2841,7 +2841,7 @@ static ssize_t ab8505_powercut_flag_read(struct device *dev, goto fail; } - return scnprintf(buf, PAGE_SIZE, "%d\n", ((reg_value & 0x10) >> 4)); + return sysfs_emit(buf, "%d\n", ((reg_value & 0x10) >> 4)); fail: return ret; @@ -2864,7 +2864,7 @@ static ssize_t ab8505_powercut_debounce_read(struct device *dev, goto fail; } - return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7)); + return sysfs_emit(buf, "%d\n", (reg_value & 0x7)); fail: return ret; @@ -2914,7 +2914,7 @@ static ssize_t ab8505_powercut_enable_status_read(struct device *dev, goto fail; } - return scnprintf(buf, PAGE_SIZE, "%d\n", ((reg_value & 0x20) >> 5)); + return sysfs_emit(buf, "%d\n", ((reg_value & 0x20) >> 5)); fail: return ret; diff --git a/drivers/power/supply/bq24190_charger.c b/drivers/power/supply/bq24190_charger.c index 2b2c3a4391c1..be34b9848450 100644 --- a/drivers/power/supply/bq24190_charger.c +++ b/drivers/power/supply/bq24190_charger.c @@ -463,7 +463,7 @@ static ssize_t bq24190_sysfs_show(struct device *dev, if (ret) count = ret; else - count = scnprintf(buf, PAGE_SIZE, "%hhx\n", v); + count = sysfs_emit(buf, "%hhx\n", v); pm_runtime_mark_last_busy(bdi->dev); pm_runtime_put_autosuspend(bdi->dev); diff --git a/drivers/power/supply/bq24257_charger.c b/drivers/power/supply/bq24257_charger.c index ab4c49788c58..103ddc2b3def 100644 --- a/drivers/power/supply/bq24257_charger.c +++ b/drivers/power/supply/bq24257_charger.c @@ -767,8 +767,7 @@ static ssize_t bq24257_show_ovp_voltage(struct device *dev, struct power_supply *psy = dev_get_drvdata(dev); struct bq24257_device *bq = power_supply_get_drvdata(psy); - return scnprintf(buf, PAGE_SIZE, "%u\n", - bq24257_vovp_map[bq->init_data.vovp]); + return sysfs_emit(buf, "%u\n", bq24257_vovp_map[bq->init_data.vovp]); } static ssize_t bq24257_show_in_dpm_voltage(struct device *dev, @@ -778,8 +777,7 @@ static ssize_t bq24257_show_in_dpm_voltage(struct device *dev, struct power_supply *psy = dev_get_drvdata(dev); struct bq24257_device *bq = power_supply_get_drvdata(psy); - return scnprintf(buf, PAGE_SIZE, "%u\n", - bq24257_vindpm_map[bq->init_data.vindpm]); + return sysfs_emit(buf, "%u\n", bq24257_vindpm_map[bq->init_data.vindpm]); } static ssize_t bq24257_sysfs_show_enable(struct device *dev, @@ -800,7 +798,7 @@ static ssize_t bq24257_sysfs_show_enable(struct device *dev, if (ret < 0) return ret; - return scnprintf(buf, PAGE_SIZE, "%d\n", ret); + return sysfs_emit(buf, "%d\n", ret); } static ssize_t bq24257_sysfs_set_enable(struct device *dev, diff --git a/drivers/power/supply/lp8788-charger.c b/drivers/power/supply/lp8788-charger.c index f5f47a0aa1e3..755b6a4379b8 100644 --- a/drivers/power/supply/lp8788-charger.c +++ b/drivers/power/supply/lp8788-charger.c @@ -602,7 +602,7 @@ static ssize_t lp8788_show_charger_status(struct device *dev, lp8788_read_byte(pchg->lp, LP8788_CHG_STATUS, &data); state = (data & LP8788_CHG_STATE_M) >> LP8788_CHG_STATE_S; - return scnprintf(buf, PAGE_SIZE, "%s\n", desc[state]); + return sysfs_emit(buf, "%s\n", desc[state]); } static ssize_t lp8788_show_eoc_time(struct device *dev, @@ -618,8 +618,7 @@ static ssize_t lp8788_show_eoc_time(struct device *dev, lp8788_read_byte(pchg->lp, LP8788_CHG_EOC, &val); val = (val & LP8788_CHG_EOC_TIME_M) >> LP8788_CHG_EOC_TIME_S; - return scnprintf(buf, PAGE_SIZE, "End Of Charge Time: %s\n", - stime[val]); + return sysfs_emit(buf, "End Of Charge Time: %s\n", stime[val]); } static ssize_t lp8788_show_eoc_level(struct device *dev, @@ -642,7 +641,7 @@ static ssize_t lp8788_show_eoc_level(struct device *dev, val = (val & LP8788_CHG_EOC_LEVEL_M) >> LP8788_CHG_EOC_LEVEL_S; level = mode ? abs_level[val] : relative_level[val]; - return scnprintf(buf, PAGE_SIZE, "End Of Charge Level: %s\n", level); + return sysfs_emit(buf, "End Of Charge Level: %s\n", level); } static DEVICE_ATTR(charger_status, S_IRUSR, lp8788_show_charger_status, NULL); diff --git a/drivers/power/supply/max14577_charger.c b/drivers/power/supply/max14577_charger.c index f244cd902eb9..96f9de775043 100644 --- a/drivers/power/supply/max14577_charger.c +++ b/drivers/power/supply/max14577_charger.c @@ -532,7 +532,7 @@ static ssize_t show_fast_charge_timer(struct device *dev, break; } - return scnprintf(buf, PAGE_SIZE, "%u\n", val); + return sysfs_emit(buf, "%u\n", val); } static ssize_t store_fast_charge_timer(struct device *dev, diff --git a/drivers/power/supply/max77693_charger.c b/drivers/power/supply/max77693_charger.c index a2c5c9858639..794c8c054450 100644 --- a/drivers/power/supply/max77693_charger.c +++ b/drivers/power/supply/max77693_charger.c @@ -296,7 +296,7 @@ static ssize_t fast_charge_timer_show(struct device *dev, break; } - return scnprintf(buf, PAGE_SIZE, "%u\n", val); + return sysfs_emit(buf, "%u\n", val); } static int max77693_set_fast_charge_timer(struct max77693_charger *chg, @@ -357,7 +357,7 @@ static ssize_t top_off_threshold_current_show(struct device *dev, else val = data * 50000; - return scnprintf(buf, PAGE_SIZE, "%u\n", val); + return sysfs_emit(buf, "%u\n", val); } static int max77693_set_top_off_threshold_current(struct max77693_charger *chg, @@ -405,7 +405,7 @@ static ssize_t top_off_timer_show(struct device *dev, val = data * 10; - return scnprintf(buf, PAGE_SIZE, "%u\n", val); + return sysfs_emit(buf, "%u\n", val); } static int max77693_set_top_off_timer(struct max77693_charger *chg, diff --git a/drivers/power/supply/twl4030_charger.c b/drivers/power/supply/twl4030_charger.c index 1bc49b2e12e8..53a0ea5a61da 100644 --- a/drivers/power/supply/twl4030_charger.c +++ b/drivers/power/supply/twl4030_charger.c @@ -726,11 +726,9 @@ twl4030_bci_mode_show(struct device *dev, for (i = 0; i < ARRAY_SIZE(modes); i++) if (mode == i) - len += scnprintf(buf+len, PAGE_SIZE-len, - "[%s] ", modes[i]); + len += sysfs_emit_at(buf, len, "[%s] ", modes[i]); else - len += scnprintf(buf+len, PAGE_SIZE-len, - "%s ", modes[i]); + len += sysfs_emit_at(buf, len, "%s ", modes[i]); buf[len-1] = '\n'; return len; } -- cgit From a441f3b90a340e5c94df36c33fb7000193ee0aa7 Mon Sep 17 00:00:00 2001 From: ye xingchen Date: Tue, 6 Dec 2022 11:16:08 +0800 Subject: power: supply: use sysfs_emit() instead of sprintf() for sysfs show() As documented in Documentation/filesystems/sysfs.rst the sysfs show() function should use sysfs_emit() or sysfs_emit_at() to format the userspace return value. This replaces all sysfs related instances of sprintf() with sysfs_emit() in the power-supply subsystem. Signed-off-by: ye xingchen [Drop sysfs_emit changes done for code not related to sysfs show and reword commit message] Signed-off-by: Sebastian Reichel --- drivers/power/supply/ab8500_fg.c | 4 +-- drivers/power/supply/bq2415x_charger.c | 42 +++++++++++++++---------------- drivers/power/supply/charger-manager.c | 6 ++--- drivers/power/supply/ds2780_battery.c | 8 +++--- drivers/power/supply/ds2781_battery.c | 8 +++--- drivers/power/supply/ltc4162-l-charger.c | 12 ++++----- drivers/power/supply/mp2629_charger.c | 2 +- drivers/power/supply/olpc_battery.c | 2 +- drivers/power/supply/pcf50633-charger.c | 6 ++--- drivers/power/supply/power_supply_sysfs.c | 10 ++++---- drivers/power/supply/wm8350_power.c | 2 +- 11 files changed, 51 insertions(+), 51 deletions(-) diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c index d989eadaa933..41a7bff9ac37 100644 --- a/drivers/power/supply/ab8500_fg.c +++ b/drivers/power/supply/ab8500_fg.c @@ -2453,7 +2453,7 @@ struct ab8500_fg_sysfs_entry { static ssize_t charge_full_show(struct ab8500_fg *di, char *buf) { - return sprintf(buf, "%d\n", di->bat_cap.max_mah); + return sysfs_emit(buf, "%d\n", di->bat_cap.max_mah); } static ssize_t charge_full_store(struct ab8500_fg *di, const char *buf, @@ -2472,7 +2472,7 @@ static ssize_t charge_full_store(struct ab8500_fg *di, const char *buf, static ssize_t charge_now_show(struct ab8500_fg *di, char *buf) { - return sprintf(buf, "%d\n", di->bat_cap.prev_mah); + return sysfs_emit(buf, "%d\n", di->bat_cap.prev_mah); } static ssize_t charge_now_store(struct ab8500_fg *di, const char *buf, diff --git a/drivers/power/supply/bq2415x_charger.c b/drivers/power/supply/bq2415x_charger.c index d2cb7431dced..349b69d634e6 100644 --- a/drivers/power/supply/bq2415x_charger.c +++ b/drivers/power/supply/bq2415x_charger.c @@ -1059,7 +1059,7 @@ static ssize_t bq2415x_sysfs_show_status(struct device *dev, ret = bq2415x_exec_command(bq, command); if (ret < 0) return ret; - return sprintf(buf, "%d\n", ret); + return sysfs_emit(buf, "%d\n", ret); } /* @@ -1098,11 +1098,11 @@ static ssize_t bq2415x_sysfs_show_timer(struct device *dev, struct bq2415x_device *bq = power_supply_get_drvdata(psy); if (bq->timer_error) - return sprintf(buf, "%s\n", bq->timer_error); + return sysfs_emit(buf, "%s\n", bq->timer_error); if (bq->autotimer) - return sprintf(buf, "auto\n"); - return sprintf(buf, "off\n"); + return sysfs_emit(buf, "auto\n"); + return sysfs_emit(buf, "off\n"); } /* @@ -1175,30 +1175,30 @@ static ssize_t bq2415x_sysfs_show_mode(struct device *dev, ssize_t ret = 0; if (bq->automode > 0) - ret += sprintf(buf+ret, "auto ("); + ret += sysfs_emit_at(buf, ret, "auto ("); switch (bq->mode) { case BQ2415X_MODE_OFF: - ret += sprintf(buf+ret, "off"); + ret += sysfs_emit_at(buf, ret, "off"); break; case BQ2415X_MODE_NONE: - ret += sprintf(buf+ret, "none"); + ret += sysfs_emit_at(buf, ret, "none"); break; case BQ2415X_MODE_HOST_CHARGER: - ret += sprintf(buf+ret, "host"); + ret += sysfs_emit_at(buf, ret, "host"); break; case BQ2415X_MODE_DEDICATED_CHARGER: - ret += sprintf(buf+ret, "dedicated"); + ret += sysfs_emit_at(buf, ret, "dedicated"); break; case BQ2415X_MODE_BOOST: - ret += sprintf(buf+ret, "boost"); + ret += sysfs_emit_at(buf, ret, "boost"); break; } if (bq->automode > 0) - ret += sprintf(buf+ret, ")"); + ret += sysfs_emit_at(buf, ret, ")"); - ret += sprintf(buf+ret, "\n"); + ret += sysfs_emit_at(buf, ret, "\n"); return ret; } @@ -1215,15 +1215,15 @@ static ssize_t bq2415x_sysfs_show_reported_mode(struct device *dev, switch (bq->reported_mode) { case BQ2415X_MODE_OFF: - return sprintf(buf, "off\n"); + return sysfs_emit(buf, "off\n"); case BQ2415X_MODE_NONE: - return sprintf(buf, "none\n"); + return sysfs_emit(buf, "none\n"); case BQ2415X_MODE_HOST_CHARGER: - return sprintf(buf, "host\n"); + return sysfs_emit(buf, "host\n"); case BQ2415X_MODE_DEDICATED_CHARGER: - return sprintf(buf, "dedicated\n"); + return sysfs_emit(buf, "dedicated\n"); case BQ2415X_MODE_BOOST: - return sprintf(buf, "boost\n"); + return sysfs_emit(buf, "boost\n"); } return -EINVAL; @@ -1261,8 +1261,8 @@ static ssize_t bq2415x_sysfs_print_reg(struct bq2415x_device *bq, int ret = bq2415x_i2c_read(bq, reg); if (ret < 0) - return sprintf(buf, "%#.2x=error %d\n", reg, ret); - return sprintf(buf, "%#.2x=%#.2x\n", reg, ret); + return sysfs_emit(buf, "%#.2x=error %d\n", reg, ret); + return sysfs_emit(buf, "%#.2x=%#.2x\n", reg, ret); } /* show all raw values of chip register, format per line: 'register=value' */ @@ -1338,7 +1338,7 @@ static ssize_t bq2415x_sysfs_show_limit(struct device *dev, if (ret < 0) return ret; - return sprintf(buf, "%d\n", ret); + return sysfs_emit(buf, "%d\n", ret); } /* set *_enable entries */ @@ -1401,7 +1401,7 @@ static ssize_t bq2415x_sysfs_show_enable(struct device *dev, ret = bq2415x_exec_command(bq, command); if (ret < 0) return ret; - return sprintf(buf, "%d\n", ret); + return sysfs_emit(buf, "%d\n", ret); } static DEVICE_ATTR(current_limit, S_IWUSR | S_IRUGO, diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index 92db79400a6a..c9e8450c646f 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -1075,7 +1075,7 @@ static ssize_t charger_name_show(struct device *dev, struct charger_regulator *charger = container_of(attr, struct charger_regulator, attr_name); - return sprintf(buf, "%s\n", charger->regulator_name); + return sysfs_emit(buf, "%s\n", charger->regulator_name); } static ssize_t charger_state_show(struct device *dev, @@ -1088,7 +1088,7 @@ static ssize_t charger_state_show(struct device *dev, if (!charger->externally_control) state = regulator_is_enabled(charger->consumer); - return sprintf(buf, "%s\n", state ? "enabled" : "disabled"); + return sysfs_emit(buf, "%s\n", state ? "enabled" : "disabled"); } static ssize_t charger_externally_control_show(struct device *dev, @@ -1097,7 +1097,7 @@ static ssize_t charger_externally_control_show(struct device *dev, struct charger_regulator *charger = container_of(attr, struct charger_regulator, attr_externally_control); - return sprintf(buf, "%d\n", charger->externally_control); + return sysfs_emit(buf, "%d\n", charger->externally_control); } static ssize_t charger_externally_control_store(struct device *dev, diff --git a/drivers/power/supply/ds2780_battery.c b/drivers/power/supply/ds2780_battery.c index 2b8c90d84325..1e7f297f6cb1 100644 --- a/drivers/power/supply/ds2780_battery.c +++ b/drivers/power/supply/ds2780_battery.c @@ -454,7 +454,7 @@ static ssize_t ds2780_get_pmod_enabled(struct device *dev, if (ret < 0) return ret; - return sprintf(buf, "%d\n", + return sysfs_emit(buf, "%d\n", !!(control_reg & DS2780_CONTROL_REG_PMOD)); } @@ -507,7 +507,7 @@ static ssize_t ds2780_get_sense_resistor_value(struct device *dev, if (ret < 0) return ret; - ret = sprintf(buf, "%d\n", sense_resistor); + ret = sysfs_emit(buf, "%d\n", sense_resistor); return ret; } @@ -545,7 +545,7 @@ static ssize_t ds2780_get_rsgain_setting(struct device *dev, if (ret < 0) return ret; - return sprintf(buf, "%d\n", rsgain); + return sysfs_emit(buf, "%d\n", rsgain); } static ssize_t ds2780_set_rsgain_setting(struct device *dev, @@ -588,7 +588,7 @@ static ssize_t ds2780_get_pio_pin(struct device *dev, if (ret < 0) return ret; - ret = sprintf(buf, "%d\n", sfr & DS2780_SFR_REG_PIOSC); + ret = sysfs_emit(buf, "%d\n", sfr & DS2780_SFR_REG_PIOSC); return ret; } diff --git a/drivers/power/supply/ds2781_battery.c b/drivers/power/supply/ds2781_battery.c index 05b859bf2dc0..c4f8ccc687f9 100644 --- a/drivers/power/supply/ds2781_battery.c +++ b/drivers/power/supply/ds2781_battery.c @@ -456,7 +456,7 @@ static ssize_t ds2781_get_pmod_enabled(struct device *dev, if (ret < 0) return ret; - return sprintf(buf, "%d\n", + return sysfs_emit(buf, "%d\n", !!(control_reg & DS2781_CONTROL_PMOD)); } @@ -509,7 +509,7 @@ static ssize_t ds2781_get_sense_resistor_value(struct device *dev, if (ret < 0) return ret; - ret = sprintf(buf, "%d\n", sense_resistor); + ret = sysfs_emit(buf, "%d\n", sense_resistor); return ret; } @@ -547,7 +547,7 @@ static ssize_t ds2781_get_rsgain_setting(struct device *dev, if (ret < 0) return ret; - return sprintf(buf, "%d\n", rsgain); + return sysfs_emit(buf, "%d\n", rsgain); } static ssize_t ds2781_set_rsgain_setting(struct device *dev, @@ -590,7 +590,7 @@ static ssize_t ds2781_get_pio_pin(struct device *dev, if (ret < 0) return ret; - ret = sprintf(buf, "%d\n", sfr & DS2781_SFR_PIOSC); + ret = sysfs_emit(buf, "%d\n", sfr & DS2781_SFR_PIOSC); return ret; } diff --git a/drivers/power/supply/ltc4162-l-charger.c b/drivers/power/supply/ltc4162-l-charger.c index db2bb5233570..0e95c65369b8 100644 --- a/drivers/power/supply/ltc4162-l-charger.c +++ b/drivers/power/supply/ltc4162-l-charger.c @@ -525,7 +525,7 @@ static ssize_t charge_status_show(struct device *dev, } } - return sprintf(buf, "%s\n", result); + return sysfs_emit(buf, "%s\n", result); } static DEVICE_ATTR_RO(charge_status); @@ -541,7 +541,7 @@ static ssize_t vbat_show(struct device *dev, if (ret) return ret; - return sprintf(buf, "%d\n", val.intval); + return sysfs_emit(buf, "%d\n", val.intval); } static DEVICE_ATTR_RO(vbat); @@ -557,7 +557,7 @@ static ssize_t vbat_avg_show(struct device *dev, if (ret) return ret; - return sprintf(buf, "%d\n", val.intval); + return sysfs_emit(buf, "%d\n", val.intval); } static DEVICE_ATTR_RO(vbat_avg); @@ -573,7 +573,7 @@ static ssize_t ibat_show(struct device *dev, if (ret) return ret; - return sprintf(buf, "%d\n", val.intval); + return sysfs_emit(buf, "%d\n", val.intval); } static DEVICE_ATTR_RO(ibat); @@ -589,7 +589,7 @@ static ssize_t force_telemetry_show(struct device *dev, if (ret) return ret; - return sprintf(buf, "%u\n", regval & BIT(2) ? 1 : 0); + return sysfs_emit(buf, "%u\n", regval & BIT(2) ? 1 : 0); } static ssize_t force_telemetry_store(struct device *dev, @@ -628,7 +628,7 @@ static ssize_t arm_ship_mode_show(struct device *dev, if (ret) return ret; - return sprintf(buf, "%u\n", + return sysfs_emit(buf, "%u\n", regval == LTC4162L_ARM_SHIP_MODE_MAGIC ? 1 : 0); } diff --git a/drivers/power/supply/mp2629_charger.c b/drivers/power/supply/mp2629_charger.c index bf9c27b463a8..3a2a28fbba73 100644 --- a/drivers/power/supply/mp2629_charger.c +++ b/drivers/power/supply/mp2629_charger.c @@ -519,7 +519,7 @@ static ssize_t batt_impedance_compensation_show(struct device *dev, return ret; rval = (rval >> 4) * 10; - return sprintf(buf, "%d mohm\n", rval); + return sysfs_emit(buf, "%d mohm\n", rval); } static ssize_t batt_impedance_compensation_store(struct device *dev, diff --git a/drivers/power/supply/olpc_battery.c b/drivers/power/supply/olpc_battery.c index a5da20ffd685..9f60094a5599 100644 --- a/drivers/power/supply/olpc_battery.c +++ b/drivers/power/supply/olpc_battery.c @@ -568,7 +568,7 @@ static ssize_t olpc_bat_error_read(struct device *dev, if (ret < 0) return ret; - return sprintf(buf, "%d\n", ec_byte); + return sysfs_emit(buf, "%d\n", ec_byte); } static struct device_attribute olpc_bat_error = { diff --git a/drivers/power/supply/pcf50633-charger.c b/drivers/power/supply/pcf50633-charger.c index 8c5d892f6350..fd44cb8ac0e2 100644 --- a/drivers/power/supply/pcf50633-charger.c +++ b/drivers/power/supply/pcf50633-charger.c @@ -153,7 +153,7 @@ show_chgmode(struct device *dev, struct device_attribute *attr, char *buf) u8 mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2); u8 chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK); - return sprintf(buf, "%d\n", chgmod); + return sysfs_emit(buf, "%d\n", chgmod); } static DEVICE_ATTR(chgmode, S_IRUGO, show_chgmode, NULL); @@ -174,7 +174,7 @@ show_usblim(struct device *dev, struct device_attribute *attr, char *buf) else ma = 0; - return sprintf(buf, "%u\n", ma); + return sysfs_emit(buf, "%u\n", ma); } static ssize_t set_usblim(struct device *dev, @@ -207,7 +207,7 @@ show_chglim(struct device *dev, struct device_attribute *attr, char *buf) ma = (mbc->pcf->pdata->charger_reference_current_ma * mbcc5) >> 8; - return sprintf(buf, "%u\n", ma); + return sysfs_emit(buf, "%u\n", ma); } static ssize_t set_chglim(struct device *dev, diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index 6ca7d3985a40..c228205e0953 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -249,11 +249,11 @@ static ssize_t power_supply_show_usb_type(struct device *dev, usb_type = desc->usb_types[i]; if (value->intval == usb_type) { - count += sprintf(buf + count, "[%s] ", + count += sysfs_emit_at(buf, count, "[%s] ", POWER_SUPPLY_USB_TYPE_TEXT[usb_type]); match = true; } else { - count += sprintf(buf + count, "%s ", + count += sysfs_emit_at(buf, count, "%s ", POWER_SUPPLY_USB_TYPE_TEXT[usb_type]); } } @@ -297,7 +297,7 @@ static ssize_t power_supply_show_property(struct device *dev, if (ps_attr->text_values_len > 0 && value.intval < ps_attr->text_values_len && value.intval >= 0) { - return sprintf(buf, "%s\n", ps_attr->text_values[value.intval]); + return sysfs_emit(buf, "%s\n", ps_attr->text_values[value.intval]); } switch (psp) { @@ -306,10 +306,10 @@ static ssize_t power_supply_show_property(struct device *dev, &value, buf); break; case POWER_SUPPLY_PROP_MODEL_NAME ... POWER_SUPPLY_PROP_SERIAL_NUMBER: - ret = sprintf(buf, "%s\n", value.strval); + ret = sysfs_emit(buf, "%s\n", value.strval); break; default: - ret = sprintf(buf, "%d\n", value.intval); + ret = sysfs_emit(buf, "%d\n", value.intval); } return ret; diff --git a/drivers/power/supply/wm8350_power.c b/drivers/power/supply/wm8350_power.c index 908cfd45d262..f2786761299c 100644 --- a/drivers/power/supply/wm8350_power.c +++ b/drivers/power/supply/wm8350_power.c @@ -176,7 +176,7 @@ static ssize_t charger_state_show(struct device *dev, return 0; } - return sprintf(buf, "%s\n", charge); + return sysfs_emit(buf, "%s\n", charge); } static DEVICE_ATTR_RO(charger_state); -- cgit From 5dd482688ad34a92c8b0907a64f7f022310f982d Mon Sep 17 00:00:00 2001 From: Deepak R Varma Date: Thu, 22 Dec 2022 22:48:29 +0530 Subject: power: supply: da9150: Remove redundant error logging A call to platform_get_irq_byname() already prints an error on failure within its own implementation. So printing another error based on its return value in the caller is redundant and should be removed. The clean up also makes if condition block braces and the device pointer variable dev unnecessary. Remove those as well. Issue identified using platform_get_irq.cocci coccinelle semantic patch. Signed-off-by: Deepak R Varma Signed-off-by: Sebastian Reichel --- drivers/power/supply/da9150-charger.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/power/supply/da9150-charger.c b/drivers/power/supply/da9150-charger.c index f9314cc0cd75..14da5c595dd9 100644 --- a/drivers/power/supply/da9150-charger.c +++ b/drivers/power/supply/da9150-charger.c @@ -466,10 +466,8 @@ static int da9150_charger_register_irq(struct platform_device *pdev, int irq, ret; irq = platform_get_irq_byname(pdev, irq_name); - if (irq < 0) { - dev_err(dev, "Failed to get IRQ CHG_STATUS: %d\n", irq); + if (irq < 0) return irq; - } ret = request_threaded_irq(irq, NULL, handler, IRQF_ONESHOT, irq_name, charger); @@ -482,15 +480,12 @@ static int da9150_charger_register_irq(struct platform_device *pdev, static void da9150_charger_unregister_irq(struct platform_device *pdev, const char *irq_name) { - struct device *dev = &pdev->dev; struct da9150_charger *charger = platform_get_drvdata(pdev); int irq; irq = platform_get_irq_byname(pdev, irq_name); - if (irq < 0) { - dev_err(dev, "Failed to get IRQ CHG_STATUS: %d\n", irq); + if (irq < 0) return; - } free_irq(irq, charger); } -- cgit From d1b25092b3dce96a69fc31b462e61291848fda9f Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 28 Nov 2022 10:28:50 +0100 Subject: power: supply: bq25890: Factor out chip state update Pull the chip state and ADC conversion update functionality out into separate function, so it can be reused elsewhere in the driver. This is a preparatory patch, no functional change. Reviewed-by: Hans de Goede Signed-off-by: Marek Vasut Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq25890_charger.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index 2d731ea58323..e9f5964f9dcd 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -454,20 +454,18 @@ static int bq25890_get_vbus_voltage(struct bq25890_device *bq) return bq25890_find_val(ret, TBL_VBUSV); } -static int bq25890_power_supply_get_property(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) +static void bq25890_update_state(struct bq25890_device *bq, + enum power_supply_property psp, + struct bq25890_state *state) { - struct bq25890_device *bq = power_supply_get_drvdata(psy); - struct bq25890_state state; bool do_adc_conv; int ret; mutex_lock(&bq->lock); /* update state in case we lost an interrupt */ __bq25890_handle_irq(bq); - state = bq->state; - do_adc_conv = !state.online && bq25890_is_adc_property(psp); + *state = bq->state; + do_adc_conv = !state->online && bq25890_is_adc_property(psp); if (do_adc_conv) bq25890_field_write(bq, F_CONV_START, 1); mutex_unlock(&bq->lock); @@ -475,6 +473,17 @@ static int bq25890_power_supply_get_property(struct power_supply *psy, if (do_adc_conv) regmap_field_read_poll_timeout(bq->rmap_fields[F_CONV_START], ret, !ret, 25000, 1000000); +} + +static int bq25890_power_supply_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct bq25890_device *bq = power_supply_get_drvdata(psy); + struct bq25890_state state; + int ret; + + bq25890_update_state(bq, psp, &state); switch (psp) { case POWER_SUPPLY_PROP_STATUS: -- cgit From c688e0c436cb5292285a193134346fcdaaa3a56d Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 28 Nov 2022 10:28:51 +0100 Subject: power: supply: bq25890: Add HiZ mode support The bq25890 is capable of disconnecting itself from the external supply, in which case the system is supplied only from the battery. This can be useful e.g. to test the pure battery operation, or draw no power from USB port. Implement support for this mode, which can be toggled by writing 0 or non-zero to sysfs 'online' attribute, to select either offline or online mode. The IRQ handler has to be triggered to update chip state, as switching to and from HiZ mode does not generate an interrupt automatically. The IRQ handler reinstates the HiZ mode in case a cable is replugged by the user, the chip itself clears the HiZ mode bit when cable is plugged in by the user and the chip detects PG bad-to-good transition. Signed-off-by: Marek Vasut [hdegoede@redhat.com: Replace "&" with "&&" in a boolean check] Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq25890_charger.c | 58 ++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index e9f5964f9dcd..f5fa39dca832 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -95,6 +95,7 @@ struct bq25890_init_data { struct bq25890_state { u8 online; + u8 hiz; u8 chrg_status; u8 chrg_fault; u8 vsys_status; @@ -119,6 +120,7 @@ struct bq25890_device { bool skip_reset; bool read_back_init_data; + bool force_hiz; u32 pump_express_vbus_max; enum bq25890_chip_version chip_version; struct bq25890_init_data init_data; @@ -487,7 +489,7 @@ static int bq25890_power_supply_get_property(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_STATUS: - if (!state.online) + if (!state.online || state.hiz) val->intval = POWER_SUPPLY_STATUS_DISCHARGING; else if (state.chrg_status == STATUS_NOT_CHARGING) val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; @@ -502,7 +504,8 @@ static int bq25890_power_supply_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CHARGE_TYPE: - if (!state.online || state.chrg_status == STATUS_NOT_CHARGING || + if (!state.online || state.hiz || + state.chrg_status == STATUS_NOT_CHARGING || state.chrg_status == STATUS_TERMINATION_DONE) val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; else if (state.chrg_status == STATUS_PRE_CHARGING) @@ -522,7 +525,7 @@ static int bq25890_power_supply_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_ONLINE: - val->intval = state.online; + val->intval = state.online && !state.hiz; break; case POWER_SUPPLY_PROP_HEALTH: @@ -676,7 +679,8 @@ static int bq25890_power_supply_set_property(struct power_supply *psy, const union power_supply_propval *val) { struct bq25890_device *bq = power_supply_get_drvdata(psy); - int maxval; + struct bq25890_state state; + int maxval, ret; u8 lval; switch (psp) { @@ -691,6 +695,12 @@ static int bq25890_power_supply_set_property(struct power_supply *psy, case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: lval = bq25890_find_idx(val->intval, TBL_IINLIM); return bq25890_field_write(bq, F_IINLIM, lval); + case POWER_SUPPLY_PROP_ONLINE: + ret = bq25890_field_write(bq, F_EN_HIZ, !val->intval); + if (!ret) + bq->force_hiz = !val->intval; + bq25890_update_state(bq, psp, &state); + return ret; default: return -EINVAL; } @@ -703,6 +713,7 @@ static int bq25890_power_supply_property_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + case POWER_SUPPLY_PROP_ONLINE: return true; default: return false; @@ -757,6 +768,7 @@ static int bq25890_get_chip_state(struct bq25890_device *bq, } state_fields[] = { {F_CHG_STAT, &state->chrg_status}, {F_PG_STAT, &state->online}, + {F_EN_HIZ, &state->hiz}, {F_VSYS_STAT, &state->vsys_status}, {F_BOOST_FAULT, &state->boost_fault}, {F_BAT_FAULT, &state->bat_fault}, @@ -772,10 +784,11 @@ static int bq25890_get_chip_state(struct bq25890_device *bq, *state_fields[i].data = ret; } - dev_dbg(bq->dev, "S:CHG/PG/VSYS=%d/%d/%d, F:CHG/BOOST/BAT/NTC=%d/%d/%d/%d\n", - state->chrg_status, state->online, state->vsys_status, - state->chrg_fault, state->boost_fault, state->bat_fault, - state->ntc_fault); + dev_dbg(bq->dev, "S:CHG/PG/HIZ/VSYS=%d/%d/%d/%d, F:CHG/BOOST/BAT/NTC=%d/%d/%d/%d\n", + state->chrg_status, state->online, + state->hiz, state->vsys_status, + state->chrg_fault, state->boost_fault, + state->bat_fault, state->ntc_fault); return 0; } @@ -792,16 +805,33 @@ static irqreturn_t __bq25890_handle_irq(struct bq25890_device *bq) if (!memcmp(&bq->state, &new_state, sizeof(new_state))) return IRQ_NONE; - if (!new_state.online && bq->state.online) { /* power removed */ + /* power removed or HiZ */ + if ((!new_state.online || new_state.hiz) && bq->state.online) { /* disable ADC */ ret = bq25890_field_write(bq, F_CONV_RATE, 0); if (ret < 0) goto error; - } else if (new_state.online && !bq->state.online) { /* power inserted */ - /* enable ADC, to have control of charge current/voltage */ - ret = bq25890_field_write(bq, F_CONV_RATE, 1); - if (ret < 0) - goto error; + } else if (new_state.online && !bq->state.online) { + /* + * Restore HiZ bit in case it was set by user. + * The chip does not retain this bit once the + * cable is re-plugged, hence the bit must be + * reset manually here. + */ + if (bq->force_hiz) { + ret = bq25890_field_write(bq, F_EN_HIZ, bq->force_hiz); + if (ret < 0) + goto error; + new_state.hiz = 1; + } + + if (!new_state.hiz) { + /* power inserted and not HiZ */ + /* enable ADC, to have control of charge current/voltage */ + ret = bq25890_field_write(bq, F_CONV_RATE, 1); + if (ret < 0) + goto error; + } } bq->state = new_state; -- cgit From 4413f9e9138fe4c50db80596635a1f7f1e8bfa6a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 28 Nov 2022 10:28:52 +0100 Subject: power: supply: bq25890: Fix setting of F_CONV_RATE rate when disabling HiZ mode The recent "power: supply: bq25890: Add HiZ mode support" change leaves F_CONV_RATE rate unset when disabling HiZ mode (setting POWER_SUPPLY_PROP_ONLINE to 1) while a charger is connected. Separate the resetting HiZ mode (when necessary because of a charger (re)plug event) into its own "if {}" block which runs first. And fix the setting of F_CONV_RATE rate by adding helper variables for the old and new F_CONV_RATE state which check both the online and hiz bits and then compare the helper variables to see if a F_CONV_RATE update is necessary. Reviewed-by: Marek Vasut Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq25890_charger.c | 41 ++++++++++++++-------------------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index f5fa39dca832..0d188c0d94ff 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -795,6 +795,7 @@ static int bq25890_get_chip_state(struct bq25890_device *bq, static irqreturn_t __bq25890_handle_irq(struct bq25890_device *bq) { + bool adc_conv_rate, new_adc_conv_rate; struct bq25890_state new_state; int ret; @@ -805,33 +806,25 @@ static irqreturn_t __bq25890_handle_irq(struct bq25890_device *bq) if (!memcmp(&bq->state, &new_state, sizeof(new_state))) return IRQ_NONE; - /* power removed or HiZ */ - if ((!new_state.online || new_state.hiz) && bq->state.online) { - /* disable ADC */ - ret = bq25890_field_write(bq, F_CONV_RATE, 0); + /* + * Restore HiZ bit in case it was set by user. The chip does not retain + * this bit on cable replug, hence the bit must be reset manually here. + */ + if (new_state.online && !bq->state.online && bq->force_hiz) { + ret = bq25890_field_write(bq, F_EN_HIZ, bq->force_hiz); if (ret < 0) goto error; - } else if (new_state.online && !bq->state.online) { - /* - * Restore HiZ bit in case it was set by user. - * The chip does not retain this bit once the - * cable is re-plugged, hence the bit must be - * reset manually here. - */ - if (bq->force_hiz) { - ret = bq25890_field_write(bq, F_EN_HIZ, bq->force_hiz); - if (ret < 0) - goto error; - new_state.hiz = 1; - } + new_state.hiz = 1; + } - if (!new_state.hiz) { - /* power inserted and not HiZ */ - /* enable ADC, to have control of charge current/voltage */ - ret = bq25890_field_write(bq, F_CONV_RATE, 1); - if (ret < 0) - goto error; - } + /* Should period ADC sampling be enabled? */ + adc_conv_rate = bq->state.online && !bq->state.hiz; + new_adc_conv_rate = new_state.online && !new_state.hiz; + + if (new_adc_conv_rate != adc_conv_rate) { + ret = bq25890_field_write(bq, F_CONV_RATE, new_adc_conv_rate); + if (ret < 0) + goto error; } bq->state = new_state; -- cgit From dee0df8496c1d13afd1bb447d284e1a76eb8e83e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 28 Nov 2022 10:28:53 +0100 Subject: power: supply: bq25890: Always take HiZ mode into account for ADC rate The code to check if F_CONV_RATE has been set, or if a manual ADC conversion needs to be triggered, as well as the code to set the initial F_CONV_RATE value at probe both where not taking HiZ mode into account. Add checks for this. Reviewed-by: Marek Vasut Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq25890_charger.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index 0d188c0d94ff..9b3a173b316a 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -467,7 +467,7 @@ static void bq25890_update_state(struct bq25890_device *bq, /* update state in case we lost an interrupt */ __bq25890_handle_irq(bq); *state = bq->state; - do_adc_conv = !state->online && bq25890_is_adc_property(psp); + do_adc_conv = (!state->online || state->hiz) && bq25890_is_adc_property(psp); if (do_adc_conv) bq25890_field_write(bq, F_CONV_START, 1); mutex_unlock(&bq->lock); @@ -956,7 +956,7 @@ static int bq25890_hw_init(struct bq25890_device *bq) } /* Configure ADC for continuous conversions when charging */ - ret = bq25890_field_write(bq, F_CONV_RATE, !!bq->state.online); + ret = bq25890_field_write(bq, F_CONV_RATE, bq->state.online && !bq->state.hiz); if (ret < 0) { dev_dbg(bq->dev, "Config ADC failed %d\n", ret); return ret; -- cgit From 4e9498b835ab31b83fc32fae4f77426f668010ac Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 28 Nov 2022 10:28:54 +0100 Subject: power: supply: bq25890: Support boards with more then one charger IC Some devices, such as the Lenovo Yoga Tab 3 Pro (YT3-X90F) have multiple batteries with a separate bq25890 charger for each battery. This requires the bq25890_charger code to use a unique name per registered power_supply class device, rather then hardcoding "bq25890-charger" as power_supply class device name. Add a "-%d" prefix to the name, allocated through idr in the same way as several other power_supply drivers are already doing this. Note this also updates: drivers/platform/x86/x86-android-tablets.c which refers to the charger by power_supply-class-device-name for the purpose of setting the "supplied-from" property on the fuel-gauge to this name. Reviewed-by: Marek Vasut Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/platform/x86/x86-android-tablets.c | 2 +- drivers/power/supply/bq25890_charger.c | 29 +++++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/x86-android-tablets.c b/drivers/platform/x86/x86-android-tablets.c index 123a4618db55..111b007656fc 100644 --- a/drivers/platform/x86/x86-android-tablets.c +++ b/drivers/platform/x86/x86-android-tablets.c @@ -187,7 +187,7 @@ struct x86_dev_info { /* Generic / shared charger / battery settings */ static const char * const tusb1211_chg_det_psy[] = { "tusb1211-charger-detect" }; static const char * const bq24190_psy[] = { "bq24190-charger" }; -static const char * const bq25890_psy[] = { "bq25890-charger" }; +static const char * const bq25890_psy[] = { "bq25890-charger-0" }; static const struct property_entry fg_bq24190_supply_props[] = { PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq24190_psy), diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index 9b3a173b316a..30854d08bba9 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -108,6 +108,9 @@ struct bq25890_device { struct i2c_client *client; struct device *dev; struct power_supply *charger; + struct power_supply_desc desc; + char name[28]; /* "bq25890-charger-%d" */ + int id; struct usb_phy *usb_phy; struct notifier_block usb_nb; @@ -129,6 +132,9 @@ struct bq25890_device { struct mutex lock; /* protect state data */ }; +static DEFINE_IDR(bq25890_id); +static DEFINE_MUTEX(bq25890_id_mutex); + static const struct regmap_range bq25890_readonly_reg_ranges[] = { regmap_reg_range(0x0b, 0x0c), regmap_reg_range(0x0e, 0x13), @@ -989,7 +995,6 @@ static char *bq25890_charger_supplied_to[] = { }; static const struct power_supply_desc bq25890_power_supply_desc = { - .name = "bq25890-charger", .type = POWER_SUPPLY_TYPE_USB, .properties = bq25890_power_supply_props, .num_properties = ARRAY_SIZE(bq25890_power_supply_props), @@ -1003,12 +1008,21 @@ static int bq25890_power_supply_init(struct bq25890_device *bq) { struct power_supply_config psy_cfg = { .drv_data = bq, }; + /* Get ID for the device */ + mutex_lock(&bq25890_id_mutex); + bq->id = idr_alloc(&bq25890_id, bq, 0, 0, GFP_KERNEL); + mutex_unlock(&bq25890_id_mutex); + if (bq->id < 0) + return bq->id; + + snprintf(bq->name, sizeof(bq->name), "bq25890-charger-%d", bq->id); + bq->desc = bq25890_power_supply_desc; + bq->desc.name = bq->name; + psy_cfg.supplied_to = bq25890_charger_supplied_to; psy_cfg.num_supplicants = ARRAY_SIZE(bq25890_charger_supplied_to); - bq->charger = devm_power_supply_register(bq->dev, - &bq25890_power_supply_desc, - &psy_cfg); + bq->charger = devm_power_supply_register(bq->dev, &bq->desc, &psy_cfg); return PTR_ERR_OR_ZERO(bq->charger); } @@ -1354,6 +1368,12 @@ static void bq25890_non_devm_cleanup(void *data) struct bq25890_device *bq = data; cancel_delayed_work_sync(&bq->pump_express_work); + + if (bq->id >= 0) { + mutex_lock(&bq25890_id_mutex); + idr_remove(&bq25890_id, bq->id); + mutex_unlock(&bq25890_id_mutex); + } } static int bq25890_probe(struct i2c_client *client) @@ -1368,6 +1388,7 @@ static int bq25890_probe(struct i2c_client *client) bq->client = client; bq->dev = dev; + bq->id = -1; mutex_init(&bq->lock); INIT_DELAYED_WORK(&bq->pump_express_work, bq25890_pump_express_work); -- cgit From d54bf877fd878ee45cbc88d399fb98b0b1c4484d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 25 Jan 2023 11:58:49 +0100 Subject: power: supply: bq25890: Add support for having a secondary charger IC Some devices, such as the Lenovo Yoga Tab 3 Pro (YT3-X90F) have multiple batteries with a separate bq25890 charger for each battery. This requires some coordination between the chargers specifically the main charger needs to put the secondary charger in Hi-Z mode when: 1. Enabling its 5V boost (OTG) output to power an external USB device, to avoid the secondary charger IC seeing this as external Vbus and then trying to charge the secondary battery from this. 2. Talking the Pump Express protocol to increase the external Vbus voltage. Having the secondary charger drawing current when the main charger is trying to talk the Pump Express protocol results in the external Vbus voltage not being raised. Add a new "linux,secondary-charger-name" string device-property, which can be set to the power_supply class device's name of the secondary charger when there is a secondary charger; and make the Vbus regulator and Pump Express code put the secondary charger in Hi-Z mode when necessary. So far this new property is only used on x86/ACPI (non devicetree) devs, IOW it is not used in actual devicetree files. The devicetree-bindings maintainers have requested properties like these to not be added to the devicetree-bindings, so the new property is deliberately not added to the existing devicetree-bindings. Reviewed-by: Marek Vasut Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq25890_charger.c | 45 +++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index 30854d08bba9..aff55bf3ecc3 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -108,6 +108,7 @@ struct bq25890_device { struct i2c_client *client; struct device *dev; struct power_supply *charger; + struct power_supply *secondary_chrg; struct power_supply_desc desc; char name[28]; /* "bq25890-charger-%d" */ int id; @@ -1042,10 +1043,17 @@ static void bq25890_pump_express_work(struct work_struct *data) { struct bq25890_device *bq = container_of(data, struct bq25890_device, pump_express_work.work); + union power_supply_propval value; int voltage, i, ret; dev_dbg(bq->dev, "Start to request input voltage increasing\n"); + /* If there is a second charger put in Hi-Z mode */ + if (bq->secondary_chrg) { + value.intval = 0; + power_supply_set_property(bq->secondary_chrg, POWER_SUPPLY_PROP_ONLINE, &value); + } + /* Enable current pulse voltage control protocol */ ret = bq25890_field_write(bq, F_PUMPX_EN, 1); if (ret < 0) @@ -1077,6 +1085,11 @@ static void bq25890_pump_express_work(struct work_struct *data) bq25890_field_write(bq, F_PUMPX_EN, 0); + if (bq->secondary_chrg) { + value.intval = 1; + power_supply_set_property(bq->secondary_chrg, POWER_SUPPLY_PROP_ONLINE, &value); + } + dev_info(bq->dev, "Hi-voltage charging requested, input voltage is %d mV\n", voltage); @@ -1123,6 +1136,17 @@ static int bq25890_usb_notifier(struct notifier_block *nb, unsigned long val, static int bq25890_vbus_enable(struct regulator_dev *rdev) { struct bq25890_device *bq = rdev_get_drvdata(rdev); + union power_supply_propval val = { + .intval = 0, + }; + + /* + * When enabling 5V boost / Vbus output, we need to put the secondary + * charger in Hi-Z mode to avoid it trying to charge the secondary + * battery from the 5V boost output. + */ + if (bq->secondary_chrg) + power_supply_set_property(bq->secondary_chrg, POWER_SUPPLY_PROP_ONLINE, &val); return bq25890_set_otg_cfg(bq, 1); } @@ -1130,8 +1154,19 @@ static int bq25890_vbus_enable(struct regulator_dev *rdev) static int bq25890_vbus_disable(struct regulator_dev *rdev) { struct bq25890_device *bq = rdev_get_drvdata(rdev); + union power_supply_propval val = { + .intval = 1, + }; + int ret; + + ret = bq25890_set_otg_cfg(bq, 0); + if (ret) + return ret; - return bq25890_set_otg_cfg(bq, 0); + if (bq->secondary_chrg) + power_supply_set_property(bq->secondary_chrg, POWER_SUPPLY_PROP_ONLINE, &val); + + return 0; } static int bq25890_vbus_is_enabled(struct regulator_dev *rdev) @@ -1342,6 +1377,14 @@ static int bq25890_fw_probe(struct bq25890_device *bq) { int ret; struct bq25890_init_data *init = &bq->init_data; + const char *str; + + ret = device_property_read_string(bq->dev, "linux,secondary-charger-name", &str); + if (ret == 0) { + bq->secondary_chrg = power_supply_get_by_name(str); + if (!bq->secondary_chrg) + return -EPROBE_DEFER; + } /* Optional, left at 0 if property is not present */ device_property_read_u32(bq->dev, "linux,pump-express-vbus-max", -- cgit From 6adaa9a4ece44e22e0c4d2e9dbce175679383cc5 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 25 Jan 2023 11:58:50 +0100 Subject: power: supply: bq25890: Add new linux,iinlim-percentage property Some devices, such as the Lenovo Yoga Tab 3 Pro (YT3-X90F) have multiple batteries with a separate bq25890 charger for each battery. This requires the maximum current the external power-supply can deliver to be divided over the chargers. The Android vendor kernel shipped on the YT3-X90F divides this current with a 40/60 percent split so that batteries are done charging at approx. the same time if both were fully empty at the start. Add support for a new "linux,iinlim-percentage" percentage property which can be set to indicate that a bq25890 charger should only use that percentage of the external power-supply's maximum current. So far this new property is only used on x86/ACPI (non devicetree) devs, IOW it is not used in actual devicetree files. The devicetree-bindings maintainers have requested properties like these to not be added to the devicetree-bindings, so the new property is deliberately not added to the existing devicetree-bindings. Signed-off-by: Hans de Goede Reviewed-by: Marek Vasut Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq25890_charger.c | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index aff55bf3ecc3..bfe08d7bfaf3 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -126,6 +126,7 @@ struct bq25890_device { bool read_back_init_data; bool force_hiz; u32 pump_express_vbus_max; + u32 iinlim_percentage; enum bq25890_chip_version chip_version; struct bq25890_init_data init_data; struct bq25890_state state; @@ -727,6 +728,18 @@ static int bq25890_power_supply_property_is_writeable(struct power_supply *psy, } } +/* + * If there are multiple chargers the maximum current the external power-supply + * can deliver needs to be divided over the chargers. This is done according + * to the bq->iinlim_percentage setting. + */ +static int bq25890_charger_get_scaled_iinlim_regval(struct bq25890_device *bq, + int iinlim_ua) +{ + iinlim_ua = iinlim_ua * bq->iinlim_percentage / 100; + return bq25890_find_idx(iinlim_ua, TBL_IINLIM); +} + /* On the BQ25892 try to get charger-type info from our supplier */ static void bq25890_charger_external_power_changed(struct power_supply *psy) { @@ -745,7 +758,7 @@ static void bq25890_charger_external_power_changed(struct power_supply *psy) switch (val.intval) { case POWER_SUPPLY_USB_TYPE_DCP: - input_current_limit = bq25890_find_idx(2000000, TBL_IINLIM); + input_current_limit = bq25890_charger_get_scaled_iinlim_regval(bq, 2000000); if (bq->pump_express_vbus_max) { queue_delayed_work(system_power_efficient_wq, &bq->pump_express_work, @@ -754,11 +767,11 @@ static void bq25890_charger_external_power_changed(struct power_supply *psy) break; case POWER_SUPPLY_USB_TYPE_CDP: case POWER_SUPPLY_USB_TYPE_ACA: - input_current_limit = bq25890_find_idx(1500000, TBL_IINLIM); + input_current_limit = bq25890_charger_get_scaled_iinlim_regval(bq, 1500000); break; case POWER_SUPPLY_USB_TYPE_SDP: default: - input_current_limit = bq25890_find_idx(500000, TBL_IINLIM); + input_current_limit = bq25890_charger_get_scaled_iinlim_regval(bq, 500000); } bq25890_field_write(bq, F_IINLIM, input_current_limit); @@ -1378,6 +1391,7 @@ static int bq25890_fw_probe(struct bq25890_device *bq) int ret; struct bq25890_init_data *init = &bq->init_data; const char *str; + u32 val; ret = device_property_read_string(bq->dev, "linux,secondary-charger-name", &str); if (ret == 0) { @@ -1390,6 +1404,17 @@ static int bq25890_fw_probe(struct bq25890_device *bq) device_property_read_u32(bq->dev, "linux,pump-express-vbus-max", &bq->pump_express_vbus_max); + ret = device_property_read_u32(bq->dev, "linux,iinlim-percentage", &val); + if (ret == 0) { + if (val > 100) { + dev_err(bq->dev, "Error linux,iinlim-percentage %u > 100\n", val); + return -EINVAL; + } + bq->iinlim_percentage = val; + } else { + bq->iinlim_percentage = 100; + } + bq->skip_reset = device_property_read_bool(bq->dev, "linux,skip-reset"); bq->read_back_init_data = device_property_read_bool(bq->dev, "linux,read-back-settings"); -- cgit From d7544cbe04e74e9dd33a94481ffe9b2e63222cd6 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Mon, 26 Dec 2022 12:45:11 +0100 Subject: dt-bindings: reset: syscon-reboot: Add priority property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This new optional priority property allows to specify custom priority level of reset device. Prior this change priority level was hardcoded to 192 and not possible to specify or change. Specifying other value is needed for some boards. Default level when not specified stays at 192 as before. Signed-off-by: Pali Rohár Acked-by: Krzysztof Kozlowski [add $ref to existing allOf to fix duplication warning] Signed-off-by: Sebastian Reichel --- Documentation/devicetree/bindings/power/reset/syscon-reboot.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/power/reset/syscon-reboot.yaml b/Documentation/devicetree/bindings/power/reset/syscon-reboot.yaml index da2509724812..75061124d9a8 100644 --- a/Documentation/devicetree/bindings/power/reset/syscon-reboot.yaml +++ b/Documentation/devicetree/bindings/power/reset/syscon-reboot.yaml @@ -42,6 +42,9 @@ properties: $ref: /schemas/types.yaml#/definitions/uint32 description: The reset value written to the reboot register (32 bit access). + priority: + default: 192 + required: - compatible - offset @@ -49,6 +52,7 @@ required: additionalProperties: false allOf: + - $ref: restart-handler.yaml# - if: not: required: -- cgit From e6333293f27cd395e77c6521afd52ff0bdc58107 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Mon, 26 Dec 2022 12:45:12 +0100 Subject: power: reset: syscon-reboot: Add support for specifying priority MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Read new optional device tree property priority for specifying priority level of reset handler. Default value is 192 as before. Signed-off-by: Pali Rohár Signed-off-by: Sebastian Reichel --- drivers/power/reset/syscon-reboot.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/power/reset/syscon-reboot.c b/drivers/power/reset/syscon-reboot.c index 510e363381ca..45e34e6885f7 100644 --- a/drivers/power/reset/syscon-reboot.c +++ b/drivers/power/reset/syscon-reboot.c @@ -44,6 +44,7 @@ static int syscon_reboot_probe(struct platform_device *pdev) struct syscon_reboot_context *ctx; struct device *dev = &pdev->dev; int mask_err, value_err; + int priority; int err; ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); @@ -57,6 +58,9 @@ static int syscon_reboot_probe(struct platform_device *pdev) return PTR_ERR(ctx->map); } + if (of_property_read_s32(pdev->dev.of_node, "priority", &priority)) + priority = 192; + if (of_property_read_u32(pdev->dev.of_node, "offset", &ctx->offset)) return -EINVAL; @@ -77,7 +81,7 @@ static int syscon_reboot_probe(struct platform_device *pdev) } ctx->restart_handler.notifier_call = syscon_restart_handle; - ctx->restart_handler.priority = 192; + ctx->restart_handler.priority = priority; err = register_restart_handler(&ctx->restart_handler); if (err) dev_err(dev, "can't register restart notifier (err=%d)\n", err); -- cgit From c85c191694cb1cf290b11059b3d2de8a2732ffd0 Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Sat, 21 Jan 2023 12:16:21 +0100 Subject: power: supply: remove faulty cooling logic The rn5t618 power driver fails to register a cooling device because POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX is missing but availability is not checked before registering cooling device. After improved error checking in the thermal code, the registration of the power supply fails entirely. Checking for availability of _MAX before registering cooling device fixes the rn5t618 problem. But the whole logic feels questionable. First, the logic is inverted here: the code tells: max_current = max_cooling but 0 = max_cooling, so there needs to be some inversion in the code which cannot be found. Comparing with other cooling devices, it can be found that value for fan speed is not inverted, value for cpufreq cooling is inverted (similar situation as here lowest frequency = max cooling) Second, analyzing usage of _MAX: it is seems that maximum capabilities of charging controller are specified and not of the battery. Probably there is not too much mismatch in the drivers actually implementing that. So nothing has exploded yet. So there is no easy and safe way to specifify a max cooling value now. Conclusion for now (as a regression fix) just remove the cooling device registration and do it properly later on. Fixes: e49a1e1ee078 ("thermal/core: fix error code in __thermal_cooling_device_register()") Fixes: 952aeeb3ee28 ("power_supply: Register power supply for thermal cooling device") Signed-off-by: Andreas Kemnade Signed-off-by: Sebastian Reichel --- drivers/power/supply/power_supply_core.c | 93 -------------------------------- 1 file changed, 93 deletions(-) diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index 7c790c41e2fe..cc5b2e22b42a 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -1186,83 +1186,6 @@ static void psy_unregister_thermal(struct power_supply *psy) thermal_zone_device_unregister(psy->tzd); } -/* thermal cooling device callbacks */ -static int ps_get_max_charge_cntl_limit(struct thermal_cooling_device *tcd, - unsigned long *state) -{ - struct power_supply *psy; - union power_supply_propval val; - int ret; - - psy = tcd->devdata; - ret = power_supply_get_property(psy, - POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, &val); - if (ret) - return ret; - - *state = val.intval; - - return ret; -} - -static int ps_get_cur_charge_cntl_limit(struct thermal_cooling_device *tcd, - unsigned long *state) -{ - struct power_supply *psy; - union power_supply_propval val; - int ret; - - psy = tcd->devdata; - ret = power_supply_get_property(psy, - POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &val); - if (ret) - return ret; - - *state = val.intval; - - return ret; -} - -static int ps_set_cur_charge_cntl_limit(struct thermal_cooling_device *tcd, - unsigned long state) -{ - struct power_supply *psy; - union power_supply_propval val; - int ret; - - psy = tcd->devdata; - val.intval = state; - ret = psy->desc->set_property(psy, - POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &val); - - return ret; -} - -static const struct thermal_cooling_device_ops psy_tcd_ops = { - .get_max_state = ps_get_max_charge_cntl_limit, - .get_cur_state = ps_get_cur_charge_cntl_limit, - .set_cur_state = ps_set_cur_charge_cntl_limit, -}; - -static int psy_register_cooler(struct power_supply *psy) -{ - /* Register for cooling device if psy can control charging */ - if (psy_has_property(psy->desc, POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT)) { - psy->tcd = thermal_cooling_device_register( - (char *)psy->desc->name, - psy, &psy_tcd_ops); - return PTR_ERR_OR_ZERO(psy->tcd); - } - - return 0; -} - -static void psy_unregister_cooler(struct power_supply *psy) -{ - if (IS_ERR_OR_NULL(psy->tcd)) - return; - thermal_cooling_device_unregister(psy->tcd); -} #else static int psy_register_thermal(struct power_supply *psy) { @@ -1272,15 +1195,6 @@ static int psy_register_thermal(struct power_supply *psy) static void psy_unregister_thermal(struct power_supply *psy) { } - -static int psy_register_cooler(struct power_supply *psy) -{ - return 0; -} - -static void psy_unregister_cooler(struct power_supply *psy) -{ -} #endif static struct power_supply *__must_check @@ -1354,10 +1268,6 @@ __power_supply_register(struct device *parent, if (rc) goto register_thermal_failed; - rc = psy_register_cooler(psy); - if (rc) - goto register_cooler_failed; - rc = power_supply_create_triggers(psy); if (rc) goto create_triggers_failed; @@ -1387,8 +1297,6 @@ __power_supply_register(struct device *parent, add_hwmon_sysfs_failed: power_supply_remove_triggers(psy); create_triggers_failed: - psy_unregister_cooler(psy); -register_cooler_failed: psy_unregister_thermal(psy); register_thermal_failed: wakeup_init_failed: @@ -1540,7 +1448,6 @@ void power_supply_unregister(struct power_supply *psy) sysfs_remove_link(&psy->dev.kobj, "powers"); power_supply_remove_hwmon_sysfs(psy); power_supply_remove_triggers(psy); - psy_unregister_cooler(psy); psy_unregister_thermal(psy); device_init_wakeup(&psy->dev, false); device_unregister(&psy->dev); -- cgit From fccd2b763c3476d20c16e2e8534d345e5e013b4a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 12 Jan 2023 16:02:09 +0200 Subject: power: supply: collie_battery: Convert to GPIO descriptors (part 2) Finish the job started by the commit ba940ed83218 ("power: supply: collie_battery: Convert to GPIO descriptors"), i.e. convert the use of gpio_to_irq() to gpiod_to_irq(). No functional changes intended. Signed-off-by: Andy Shevchenko Reviewed-by: Linus Walleij Signed-off-by: Sebastian Reichel --- drivers/power/supply/collie_battery.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/power/supply/collie_battery.c b/drivers/power/supply/collie_battery.c index 7fb9b549f2de..68390bd1004f 100644 --- a/drivers/power/supply/collie_battery.c +++ b/drivers/power/supply/collie_battery.c @@ -404,7 +404,7 @@ static int collie_bat_probe(struct ucb1x00_dev *dev) goto err_psy_reg_bu; } - ret = request_irq(gpio_to_irq(COLLIE_GPIO_CO), + ret = request_irq(gpiod_to_irq(collie_bat_main.gpio_full), collie_bat_gpio_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "main full", &collie_bat_main); @@ -440,7 +440,7 @@ err_put_gpio_full: static void collie_bat_remove(struct ucb1x00_dev *dev) { - free_irq(gpio_to_irq(COLLIE_GPIO_CO), &collie_bat_main); + free_irq(gpiod_to_irq(collie_bat_main.gpio_full), &collie_bat_main); power_supply_unregister(collie_bat_bu.psy); power_supply_unregister(collie_bat_main.psy); -- cgit From 4651b6b72934e602202def29c88813d95716f7c7 Mon Sep 17 00:00:00 2001 From: Hermes Zhang Date: Tue, 10 Jan 2023 10:47:46 +0800 Subject: power: supply: bq256xx: Init ichg/vbat value with chip default value Init the ichg/vbat reg with chip default value instead of the max value used now. The max value set in driver will result an unsafe case (e.g. battery is over charging when in a hot environment) if no user space update the value later. Signed-off-by: Hermes Zhang Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq256xx_charger.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/power/supply/bq256xx_charger.c b/drivers/power/supply/bq256xx_charger.c index db13e288e439..9cf4936440c9 100644 --- a/drivers/power/supply/bq256xx_charger.c +++ b/drivers/power/supply/bq256xx_charger.c @@ -1563,7 +1563,7 @@ static int bq256xx_hw_init(struct bq256xx_device *bq) return ret; ret = bq->chip_info->bq256xx_set_ichg(bq, - bat_info->constant_charge_current_max_ua); + bq->chip_info->bq256xx_def_ichg); if (ret) return ret; @@ -1573,7 +1573,7 @@ static int bq256xx_hw_init(struct bq256xx_device *bq) return ret; ret = bq->chip_info->bq256xx_set_vbatreg(bq, - bat_info->constant_charge_voltage_max_uv); + bq->chip_info->bq256xx_def_vbatreg); if (ret) return ret; -- cgit From e2b018cb55151cbee2c4b8f48ef731f0a683b9b6 Mon Sep 17 00:00:00 2001 From: "Sicelo A. Mhlongo" Date: Mon, 2 Jan 2023 11:13:26 +0200 Subject: power: supply: bq27xxx: fix reporting critical level The EDV1/SOC1 flag is set when the battery voltage drops below the threshold set in EEPROM. From observing the capacity_level reported by the driver, and reading the datasheet, EDV1 remains set even when EDVF/SOCF gets set. Thus, bq27xxx_battery_capacity_level() never reaches the CAPACITY_LEVEL_CRITICAL code path, since CAPACITY_LEVEL_LOW takes precedence. This commit fixes the issue by swapping the order in which the flags are tested. It was tested with bq27200 in the Nokia N900. Signed-off-by: Sicelo A. Mhlongo Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq27xxx_battery.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c index 8bf048fbd36a..5ff6f44fd47b 100644 --- a/drivers/power/supply/bq27xxx_battery.c +++ b/drivers/power/supply/bq27xxx_battery.c @@ -1917,10 +1917,10 @@ static int bq27xxx_battery_capacity_level(struct bq27xxx_device_info *di, if (di->opts & BQ27XXX_O_ZERO) { if (di->cache.flags & BQ27000_FLAG_FC) level = POWER_SUPPLY_CAPACITY_LEVEL_FULL; - else if (di->cache.flags & BQ27000_FLAG_EDV1) - level = POWER_SUPPLY_CAPACITY_LEVEL_LOW; else if (di->cache.flags & BQ27000_FLAG_EDVF) level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; + else if (di->cache.flags & BQ27000_FLAG_EDV1) + level = POWER_SUPPLY_CAPACITY_LEVEL_LOW; else level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; } else if (di->opts & BQ27Z561_O_BITS) { @@ -1933,10 +1933,10 @@ static int bq27xxx_battery_capacity_level(struct bq27xxx_device_info *di, } else { if (di->cache.flags & BQ27XXX_FLAG_FC) level = POWER_SUPPLY_CAPACITY_LEVEL_FULL; - else if (di->cache.flags & BQ27XXX_FLAG_SOC1) - level = POWER_SUPPLY_CAPACITY_LEVEL_LOW; else if (di->cache.flags & BQ27XXX_FLAG_SOCF) level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; + else if (di->cache.flags & BQ27XXX_FLAG_SOC1) + level = POWER_SUPPLY_CAPACITY_LEVEL_LOW; else level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; } -- cgit From 3639dbd74e2e827d544bdb28b663a44449c014e1 Mon Sep 17 00:00:00 2001 From: Xu Panda Date: Fri, 23 Dec 2022 10:42:35 +0800 Subject: power: supply: test-power: use strscpy() instead of strncpy() The implementation of strscpy() is more robust and safer. That's now the recommended way to copy NUL-terminated strings. Signed-off-by: Xu Panda Signed-off-by: Yang Yang Signed-off-by: Sebastian Reichel --- drivers/power/supply/test_power.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/power/supply/test_power.c b/drivers/power/supply/test_power.c index 5f510ddc946d..0d0a77584c5d 100644 --- a/drivers/power/supply/test_power.c +++ b/drivers/power/supply/test_power.c @@ -306,8 +306,7 @@ static int map_get_value(struct battery_property_map *map, const char *key, char buf[MAX_KEYLENGTH]; int cr; - strncpy(buf, key, MAX_KEYLENGTH); - buf[MAX_KEYLENGTH-1] = '\0'; + strscpy(buf, key, MAX_KEYLENGTH); cr = strnlen(buf, MAX_KEYLENGTH) - 1; if (cr < 0) -- cgit From 301cfbc1249759415ff864bdf46e15c7e103279b Mon Sep 17 00:00:00 2001 From: Minghao Chi Date: Mon, 19 Sep 2022 02:49:19 +0000 Subject: power: supply: max1721x: Use strscpy() is more robust and safer The implementation of strscpy() is more robust and safer. That's now the recommended way to copy NUL terminated strings. Reported-by: Zeal Robot Signed-off-by: Minghao Chi Signed-off-by: Sebastian Reichel --- drivers/power/supply/max1721x_battery.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/power/supply/max1721x_battery.c b/drivers/power/supply/max1721x_battery.c index d8d52e09da7b..bac43ab9e97c 100644 --- a/drivers/power/supply/max1721x_battery.c +++ b/drivers/power/supply/max1721x_battery.c @@ -384,7 +384,7 @@ static int devm_w1_max1721x_add_device(struct w1_slave *sl) } if (!info->ManufacturerName[0]) - strncpy(info->ManufacturerName, DEF_MFG_NAME, + strscpy(info->ManufacturerName, DEF_MFG_NAME, 2 * MAX1721X_REG_MFG_NUMB); if (get_string(info, MAX1721X_REG_DEV_STR, @@ -403,15 +403,15 @@ static int devm_w1_max1721x_add_device(struct w1_slave *sl) switch (dev_name & MAX172XX_DEV_MASK) { case MAX172X1_DEV: - strncpy(info->DeviceName, DEF_DEV_NAME_MAX17211, + strscpy(info->DeviceName, DEF_DEV_NAME_MAX17211, 2 * MAX1721X_REG_DEV_NUMB); break; case MAX172X5_DEV: - strncpy(info->DeviceName, DEF_DEV_NAME_MAX17215, + strscpy(info->DeviceName, DEF_DEV_NAME_MAX17215, 2 * MAX1721X_REG_DEV_NUMB); break; default: - strncpy(info->DeviceName, DEF_DEV_NAME_UNKNOWN, + strscpy(info->DeviceName, DEF_DEV_NAME_UNKNOWN, 2 * MAX1721X_REG_DEV_NUMB); } } -- cgit From 2bc68e5881a47437150da2edba07f79a71508cdd Mon Sep 17 00:00:00 2001 From: ChiYuan Huang Date: Fri, 13 Jan 2023 14:17:44 +0800 Subject: dt-bindings: power: supply: Add Richtek RT9471 battery charger Add bindings for the Richtek RT9471 I2C controlled battery charger. Reviewed-by: Krzysztof Kozlowski Co-developed-by: Alina Yu Signed-off-by: Alina Yu Signed-off-by: ChiYuan Huang Signed-off-by: Sebastian Reichel --- .../bindings/power/supply/richtek,rt9471.yaml | 73 ++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 Documentation/devicetree/bindings/power/supply/richtek,rt9471.yaml diff --git a/Documentation/devicetree/bindings/power/supply/richtek,rt9471.yaml b/Documentation/devicetree/bindings/power/supply/richtek,rt9471.yaml new file mode 100644 index 000000000000..fbb54cfeca08 --- /dev/null +++ b/Documentation/devicetree/bindings/power/supply/richtek,rt9471.yaml @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/power/supply/richtek,rt9471.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Richtek RT9471 3A Single Cell Switching Battery charger + +maintainers: + - Alina Yu + - ChiYuan Huang + +description: | + RT9471 is a switch-mode single cell Li-Ion/Li-Polymer battery charger for + portable applications. It supports USB BC1.2 port detection, current and + voltage regulations in both charging and boost mode. + + Datasheet is available at + https://www.richtek.com/assets/product_file/RT9471=RT9471D/DS9471D-02.pdf + +properties: + compatible: + const: richtek,rt9471 + + reg: + maxItems: 1 + + charge-enable-gpios: + description: GPIO used to turn on and off charging. + maxItems: 1 + + wakeup-source: true + + interrupts: + maxItems: 1 + + usb-otg-vbus-regulator: + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + +required: + - compatible + - reg + - wakeup-source + - interrupts + +additionalProperties: false + +examples: + - | + #include + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + + charger@53 { + compatible = "richtek,rt9471"; + reg = <0x53>; + charge-enable-gpios = <&gpio26 1 GPIO_ACTIVE_LOW>; + wakeup-source; + interrupts-extended = <&gpio_intc 32 IRQ_TYPE_EDGE_FALLING>; + + usb-otg-vbus-regulator { + regulator-name = "usb-otg-vbus"; + regulator-min-microvolt = <4850000>; + regulator-max-microvolt = <5300000>; + regulator-min-microamp = <500000>; + regulator-max-microamp = <1200000>; + }; + }; + }; -- cgit From 4a1a5f6781d8a25f5b1d421bdd4285ee3633b1fe Mon Sep 17 00:00:00 2001 From: ChiYuan Huang Date: Fri, 13 Jan 2023 14:17:45 +0800 Subject: power: supply: rt9471: Add Richtek RT9471 charger driver Add support for the RT9471 3A 1-Cell Li+ battery charger. The RT9471 is a highly-integrated 3A switch mode battery charger with low impedance power path to better optimize the charging efficiency. Co-developed-by: Alina Yu Signed-off-by: Alina Yu Signed-off-by: ChiYuan Huang Signed-off-by: Sebastian Reichel --- drivers/power/supply/Kconfig | 16 + drivers/power/supply/Makefile | 1 + drivers/power/supply/rt9471.c | 931 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 948 insertions(+) create mode 100644 drivers/power/supply/rt9471.c diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 0bbfe6a7ce4d..95e7137af18a 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -793,6 +793,22 @@ config CHARGER_RT9455 help Say Y to enable support for Richtek RT9455 battery charger. +config CHARGER_RT9471 + tristate "Richtek RT9471 battery charger driver" + depends on I2C && GPIOLIB && REGULATOR + select REGMAP_I2C + select REGMAP_IRQ + select LINEAR_RANGES + help + This adds support for Richtek RT9471 battery charger. RT9471 is + highly-integrated switch mode battery charger which is system power + patch manageable device for single cell Li-Ion and Li-polymer battery. + It can support BC12 detection on DPDM, and current and voltage + regulation on both charging and boost mode. + + This driver can also be built as a module. If so, the module will be + called rt9471. + config CHARGER_CROS_USBPD tristate "ChromeOS EC based USBPD charger" depends on CROS_USBPD_NOTIFY diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index 0ee8653e882e..cf687e21e6e2 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_BATTERY_MAX1721X) += max1721x_battery.o obj-$(CONFIG_BATTERY_Z2) += z2_battery.o obj-$(CONFIG_BATTERY_RT5033) += rt5033_battery.o obj-$(CONFIG_CHARGER_RT9455) += rt9455_charger.o +obj-$(CONFIG_CHARGER_RT9471) += rt9471.o obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o obj-$(CONFIG_BATTERY_TWL4030_MADC) += twl4030_madc_battery.o obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o diff --git a/drivers/power/supply/rt9471.c b/drivers/power/supply/rt9471.c new file mode 100644 index 000000000000..5d3cf375ad5c --- /dev/null +++ b/drivers/power/supply/rt9471.c @@ -0,0 +1,931 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 Richtek Technology Corp. + * + * Authors: Alina Yu + * ChiYuan Huang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RT9471_REG_OTGCFG 0x00 +#define RT9471_REG_TOP 0x01 +#define RT9471_REG_FUNC 0x02 +#define RT9471_REG_IBUS 0x03 +#define RT9471_REG_VBUS 0x04 +#define RT9471_REG_PRECHG 0x05 +#define RT9471_REG_VCHG 0x07 +#define RT9471_REG_ICHG 0x08 +#define RT9471_REG_CHGTMR 0x09 +#define RT9471_REG_EOC 0x0A +#define RT9471_REG_INFO 0x0B +#define RT9471_REG_JEITA 0x0C +#define RT9471_REG_PUMP_EXP 0x0D +#define RT9471_REG_DPDMDET 0x0E +#define RT9471_REG_ICSTAT 0x0F +#define RT9471_REG_STAT0 0x10 +#define RT9471_REG_STAT1 0x11 +#define RT9471_REG_STAT2 0x12 +#define RT9471_REG_IRQ0 0x20 +#define RT9471_REG_MASK0 0x30 + +#define RT9471_OTGCV_MASK GENMASK(7, 6) +#define RT9471_OTGCC_MASK BIT(0) +#define RT9471_OTGEN_MASK BIT(1) +#define RT9471_CHGFAULT_MASK GENMASK(4, 1) + +#define RT9471_NUM_IRQ_REGS 4 +#define RT9471_OTGCV_MINUV 4850000 +#define RT9471_OTGCV_STEPUV 150000 +#define RT9471_NUM_VOTG 4 +#define RT9471_VCHG_MAXUV 4700000 +#define RT9471_ICHG_MAXUA 3150000 + +/* Device ID */ +#define RT9470_DEVID 0x09 +#define RT9470D_DEVID 0x0A +#define RT9471_DEVID 0x0D +#define RT9471D_DEVID 0x0E + +/* IRQ number */ +#define RT9471_IRQ_BC12_DONE 0 +#define RT9471_IRQ_DETACH 1 +#define RT9471_IRQ_RECHG 2 +#define RT9471_IRQ_CHG_DONE 3 +#define RT9471_IRQ_BG_CHG 4 +#define RT9471_IRQ_IE0C 5 +#define RT9471_IRQ_CHG_RDY 6 +#define RT9471_IRQ_VBUS_GD 7 +#define RT9471_IRQ_CHG_BATOV 9 +#define RT9471_IRQ_CHG_SYSOV 10 +#define RT9471_IRQ_CHG_TOUT 11 +#define RT9471_IRQ_CHG_BUSUV 12 +#define RT9471_IRQ_CHG_THREG 13 +#define RT9471_IRQ_CHG_AICR 14 +#define RT9471_IRQ_CHG_MIVR 15 +#define RT9471_IRQ_SYS_SHORT 16 +#define RT9471_IRQ_SYS_MIN 17 +#define RT9471_IRQ_AICC_DONE 18 +#define RT9471_IRQ_PE_DONE 19 +#define RT9471_IRQ_JEITA_COLD 20 +#define RT9471_IRQ_JEITA_COOL 21 +#define RT9471_IRQ_JEITA_WARM 22 +#define RT9471_IRQ_JEITA_HOT 23 +#define RT9471_IRQ_OTG_FAULT 24 +#define RT9471_IRQ_OTG_LBP 25 +#define RT9471_IRQ_OTG_CC 26 +#define RT9471_IRQ_WDT 29 +#define RT9471_IRQ_VAC_OV 30 +#define RT9471_IRQ_OTP 31 + +enum rt9471_fields { + F_WDT = 0, + F_WDT_RST, + F_CHG_EN, + F_HZ, + F_BATFET_DIS, + F_AICR, + F_AICC_EN, + F_MIVR, + F_IPRE_CHG, + F_VPRE_CHG, + F_VBAT_REG, + F_ICHG_REG, + F_EOC_RST, + F_TE, + F_IEOC_CHG, + F_DEVICE_ID, + F_REG_RST, + F_BC12_EN, + F_IC_STAT, + F_PORT_STAT, + F_ST_CHG_DONE, + F_ST_CHG_RDY, + F_ST_VBUS_GD, + F_MAX_FIELDS +}; + +enum rt9471_ranges { + RT9471_RANGE_AICR = 0, + RT9471_RANGE_MIVR, + RT9471_RANGE_IPRE, + RT9471_RANGE_VCHG, + RT9471_RANGE_ICHG, + RT9471_RANGE_IEOC, + RT9471_MAX_RANGES +}; + +enum { + RT9471_PORTSTAT_APPLE_10W = 8, + RT9471_PORTSTAT_SAMSUNG_10W, + RT9471_PORTSTAT_APPLE_5W, + RT9471_PORTSTAT_APPLE_12W, + RT9471_PORTSTAT_NSTD, + RT9471_PORTSTAT_SDP, + RT9471_PORTSTAT_CDP, + RT9471_PORTSTAT_DCP, +}; + +struct rt9471_chip { + struct device *dev; + struct gpio_desc *ce_gpio; + struct regmap *regmap; + struct regmap_field *rm_fields[F_MAX_FIELDS]; + struct regmap_irq_chip_data *irq_chip_data; + struct regulator_dev *otg_rdev; + struct power_supply *psy; + struct power_supply_desc psy_desc; + struct mutex var_lock; + enum power_supply_usb_type psy_usb_type; + int psy_usb_curr; +}; + +static const struct reg_field rt9471_reg_fields[F_MAX_FIELDS] = { + [F_WDT] = REG_FIELD(RT9471_REG_TOP, 0, 0), + [F_WDT_RST] = REG_FIELD(RT9471_REG_TOP, 1, 1), + [F_CHG_EN] = REG_FIELD(RT9471_REG_FUNC, 0, 0), + [F_HZ] = REG_FIELD(RT9471_REG_FUNC, 5, 5), + [F_BATFET_DIS] = REG_FIELD(RT9471_REG_FUNC, 7, 7), + [F_AICR] = REG_FIELD(RT9471_REG_IBUS, 0, 5), + [F_AICC_EN] = REG_FIELD(RT9471_REG_IBUS, 7, 7), + [F_MIVR] = REG_FIELD(RT9471_REG_VBUS, 0, 3), + [F_IPRE_CHG] = REG_FIELD(RT9471_REG_PRECHG, 0, 3), + [F_VPRE_CHG] = REG_FIELD(RT9471_REG_PRECHG, 4, 6), + [F_VBAT_REG] = REG_FIELD(RT9471_REG_VCHG, 0, 6), + [F_ICHG_REG] = REG_FIELD(RT9471_REG_ICHG, 0, 5), + [F_EOC_RST] = REG_FIELD(RT9471_REG_EOC, 0, 0), + [F_TE] = REG_FIELD(RT9471_REG_EOC, 1, 1), + [F_IEOC_CHG] = REG_FIELD(RT9471_REG_EOC, 4, 7), + [F_DEVICE_ID] = REG_FIELD(RT9471_REG_INFO, 3, 6), + [F_REG_RST] = REG_FIELD(RT9471_REG_INFO, 7, 7), + [F_BC12_EN] = REG_FIELD(RT9471_REG_DPDMDET, 7, 7), + [F_IC_STAT] = REG_FIELD(RT9471_REG_ICSTAT, 0, 3), + [F_PORT_STAT] = REG_FIELD(RT9471_REG_ICSTAT, 4, 7), + [F_ST_CHG_DONE] = REG_FIELD(RT9471_REG_STAT0, 3, 3), + [F_ST_CHG_RDY] = REG_FIELD(RT9471_REG_STAT0, 6, 6), + [F_ST_VBUS_GD] = REG_FIELD(RT9471_REG_STAT0, 7, 7), +}; + +static const struct linear_range rt9471_chg_ranges[RT9471_MAX_RANGES] = { + [RT9471_RANGE_AICR] = { .min = 50000, .min_sel = 1, .max_sel = 63, .step = 50000 }, + [RT9471_RANGE_MIVR] = { .min = 3900000, .min_sel = 0, .max_sel = 15, .step = 100000 }, + [RT9471_RANGE_IPRE] = { .min = 50000, .min_sel = 0, .max_sel = 15, .step = 50000 }, + [RT9471_RANGE_VCHG] = { .min = 3900000, .min_sel = 0, .max_sel = 80, .step = 10000 }, + [RT9471_RANGE_ICHG] = { .min = 0, .min_sel = 0, .max_sel = 63, .step = 50000 }, + [RT9471_RANGE_IEOC] = { .min = 50000, .min_sel = 0, .max_sel = 15, .step = 50000 }, +}; + +static int rt9471_set_value_by_field_range(struct rt9471_chip *chip, + enum rt9471_fields field, + enum rt9471_ranges range, int val) +{ + unsigned int sel; + + if (val < 0) + return -EINVAL; + + linear_range_get_selector_within(rt9471_chg_ranges + range, val, &sel); + + return regmap_field_write(chip->rm_fields[field], sel); +} + + +static int rt9471_get_value_by_field_range(struct rt9471_chip *chip, + enum rt9471_fields field, + enum rt9471_ranges range, int *val) +{ + unsigned int sel, rvalue; + int ret; + + ret = regmap_field_read(chip->rm_fields[field], &sel); + if (ret) + return ret; + + ret = linear_range_get_value(rt9471_chg_ranges + range, sel, &rvalue); + if (ret) + return ret; + + *val = rvalue; + return 0; +} + +static int rt9471_set_ieoc(struct rt9471_chip *chip, int microamp) +{ + int ret; + + if (microamp == 0) + return regmap_field_write(chip->rm_fields[F_TE], 0); + + ret = rt9471_set_value_by_field_range(chip, F_IEOC_CHG, RT9471_RANGE_IEOC, microamp); + if (ret) + return ret; + + /* After applying the new IEOC value, enable charge termination */ + return regmap_field_write(chip->rm_fields[F_TE], 1); +} + +static int rt9471_get_ieoc(struct rt9471_chip *chip, int *microamp) +{ + unsigned int chg_term_enable; + int ret; + + ret = regmap_field_read(chip->rm_fields[F_TE], &chg_term_enable); + if (ret) + return ret; + + if (!chg_term_enable) { + *microamp = 0; + return 0; + } + + return rt9471_get_value_by_field_range(chip, F_IEOC_CHG, RT9471_RANGE_IEOC, microamp); +} + +static int rt9471_get_status(struct rt9471_chip *chip, int *status) +{ + unsigned int chg_ready, chg_done, fault_stat; + int ret; + + ret = regmap_field_read(chip->rm_fields[F_ST_CHG_RDY], &chg_ready); + if (ret) + return ret; + + ret = regmap_field_read(chip->rm_fields[F_ST_CHG_DONE], &chg_done); + if (ret) + return ret; + + ret = regmap_read(chip->regmap, RT9471_REG_STAT1, &fault_stat); + if (ret) + return ret; + + fault_stat &= RT9471_CHGFAULT_MASK; + + if (chg_ready && chg_done) + *status = POWER_SUPPLY_STATUS_FULL; + else if (chg_ready && fault_stat) + *status = POWER_SUPPLY_STATUS_NOT_CHARGING; + else if (chg_ready && !fault_stat) + *status = POWER_SUPPLY_STATUS_CHARGING; + else + *status = POWER_SUPPLY_STATUS_DISCHARGING; + + return 0; +} + +static int rt9471_get_vbus_good(struct rt9471_chip *chip, int *stat) +{ + unsigned int vbus_gd; + int ret; + + ret = regmap_field_read(chip->rm_fields[F_ST_VBUS_GD], &vbus_gd); + if (ret) + return ret; + + *stat = vbus_gd; + return 0; +} + +static int rt9471_get_usb_type(struct rt9471_chip *chip, int *usb_type) +{ + mutex_lock(&chip->var_lock); + *usb_type = chip->psy_usb_type; + mutex_unlock(&chip->var_lock); + + return 0; +} + +static int rt9471_get_usb_type_current(struct rt9471_chip *chip, + int *microamp) +{ + mutex_lock(&chip->var_lock); + *microamp = chip->psy_usb_curr; + mutex_unlock(&chip->var_lock); + + return 0; +} + +static enum power_supply_property rt9471_charger_properties[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_CURRENT_MAX, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT, + POWER_SUPPLY_PROP_USB_TYPE, + POWER_SUPPLY_PROP_PRECHARGE_CURRENT, + POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, +}; + +static enum power_supply_usb_type rt9471_charger_usb_types[] = { + POWER_SUPPLY_USB_TYPE_UNKNOWN, + POWER_SUPPLY_USB_TYPE_SDP, + POWER_SUPPLY_USB_TYPE_DCP, + POWER_SUPPLY_USB_TYPE_CDP, + POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID, +}; + +static int rt9471_charger_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + case POWER_SUPPLY_PROP_ONLINE: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: + return 1; + default: + return 0; + } +} + +static int rt9471_charger_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct rt9471_chip *chip = power_supply_get_drvdata(psy); + int value = val->intval; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + return regmap_field_write(chip->rm_fields[F_CHG_EN], !!value); + case POWER_SUPPLY_PROP_ONLINE: + return regmap_field_write(chip->rm_fields[F_HZ], !value); + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + return rt9471_set_value_by_field_range(chip, F_ICHG_REG, RT9471_RANGE_ICHG, value); + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + return rt9471_set_value_by_field_range(chip, F_VBAT_REG, RT9471_RANGE_VCHG, value); + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + return rt9471_set_value_by_field_range(chip, F_AICR, RT9471_RANGE_AICR, value); + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + return rt9471_set_value_by_field_range(chip, F_MIVR, RT9471_RANGE_MIVR, value); + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: + return rt9471_set_value_by_field_range(chip, F_IPRE_CHG, RT9471_RANGE_IPRE, value); + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: + return rt9471_set_ieoc(chip, val->intval); + default: + return -EINVAL; + } +} + +static const char * const rt9471_manufacturer = "Richtek Technology Corp."; +static const char * const rt9471_model = "RT9471"; + +static int rt9471_charger_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct rt9471_chip *chip = power_supply_get_drvdata(psy); + int *pvalue = &val->intval; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + return rt9471_get_status(chip, pvalue); + case POWER_SUPPLY_PROP_ONLINE: + return rt9471_get_vbus_good(chip, pvalue); + case POWER_SUPPLY_PROP_CURRENT_MAX: + return rt9471_get_usb_type_current(chip, pvalue); + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + return rt9471_get_value_by_field_range(chip, F_ICHG_REG, RT9471_RANGE_ICHG, pvalue); + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + *pvalue = RT9471_ICHG_MAXUA; + return 0; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + return rt9471_get_value_by_field_range(chip, F_VBAT_REG, RT9471_RANGE_VCHG, pvalue); + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: + val->intval = RT9471_VCHG_MAXUV; + return 0; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + return rt9471_get_value_by_field_range(chip, F_AICR, RT9471_RANGE_AICR, pvalue); + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + return rt9471_get_value_by_field_range(chip, F_MIVR, RT9471_RANGE_MIVR, pvalue); + case POWER_SUPPLY_PROP_USB_TYPE: + return rt9471_get_usb_type(chip, pvalue); + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: + return rt9471_get_value_by_field_range(chip, F_IPRE_CHG, RT9471_RANGE_IPRE, pvalue); + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: + return rt9471_get_ieoc(chip, pvalue); + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = rt9471_model; + return 0; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = rt9471_manufacturer; + return 0; + default: + return -ENODATA; + } +} + +static irqreturn_t rt9471_vbus_gd_handler(int irqno, void *devid) +{ + struct rt9471_chip *chip = devid; + + power_supply_changed(chip->psy); + + return IRQ_HANDLED; +} + +static irqreturn_t rt9471_detach_handler(int irqno, void *devid) +{ + struct rt9471_chip *chip = devid; + unsigned int vbus_gd; + int ret; + + ret = regmap_field_read(chip->rm_fields[F_ST_VBUS_GD], &vbus_gd); + if (ret) + return IRQ_NONE; + + /* Only focus on really detached */ + if (vbus_gd) + return IRQ_HANDLED; + + mutex_lock(&chip->var_lock); + chip->psy_usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN; + chip->psy_usb_curr = 0; + mutex_unlock(&chip->var_lock); + + power_supply_changed(chip->psy); + + return IRQ_HANDLED; +} + +static irqreturn_t rt9471_bc12_done_handler(int irqno, void *devid) +{ + struct rt9471_chip *chip = devid; + enum power_supply_usb_type usb_type; + unsigned int port_stat; + int usb_curr, ret; + + ret = regmap_field_read(chip->rm_fields[F_PORT_STAT], &port_stat); + if (ret) + return IRQ_NONE; + + switch (port_stat) { + case RT9471_PORTSTAT_APPLE_10W: + usb_type = POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID; + usb_curr = 2000000; + break; + case RT9471_PORTSTAT_APPLE_5W: + usb_type = POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID; + usb_curr = 1000000; + break; + case RT9471_PORTSTAT_APPLE_12W: + usb_type = POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID; + usb_curr = 2400000; + break; + case RT9471_PORTSTAT_SAMSUNG_10W: + usb_type = POWER_SUPPLY_USB_TYPE_DCP; + usb_curr = 2000000; + break; + case RT9471_PORTSTAT_DCP: + usb_type = POWER_SUPPLY_USB_TYPE_DCP; + usb_curr = 1500000; + break; + case RT9471_PORTSTAT_NSTD: + case RT9471_PORTSTAT_SDP: + usb_type = POWER_SUPPLY_USB_TYPE_SDP; + usb_curr = 500000; + break; + case RT9471_PORTSTAT_CDP: + usb_type = POWER_SUPPLY_USB_TYPE_CDP; + usb_curr = 1500000; + break; + default: + usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN; + usb_curr = 0; + break; + } + + mutex_lock(&chip->var_lock); + chip->psy_usb_type = usb_type; + chip->psy_usb_curr = usb_curr; + mutex_unlock(&chip->var_lock); + + power_supply_changed(chip->psy); + + return IRQ_HANDLED; +} + +static irqreturn_t rt9471_wdt_handler(int irqno, void *devid) +{ + struct rt9471_chip *chip = devid; + int ret; + + ret = regmap_field_write(chip->rm_fields[F_WDT_RST], 1); + + return ret ? IRQ_NONE : IRQ_HANDLED; +} + +static irqreturn_t rt9471_otg_fault_handler(int irqno, void *devid) +{ + struct rt9471_chip *chip = devid; + + regulator_notifier_call_chain(chip->otg_rdev, REGULATOR_EVENT_FAIL, NULL); + + return IRQ_HANDLED; +} + +#define RT9471_IRQ_DESC(_name, _hwirq) \ +{ \ + .name = #_name, \ + .hwirq = _hwirq, \ + .handler = rt9471_##_name##_handler, \ +} + +static int rt9471_register_interrupts(struct rt9471_chip *chip) +{ + struct device *dev = chip->dev; + static const struct { + char *name; + int hwirq; + irq_handler_t handler; + } chg_irqs[] = { + RT9471_IRQ_DESC(vbus_gd, RT9471_IRQ_VBUS_GD), + RT9471_IRQ_DESC(detach, RT9471_IRQ_DETACH), + RT9471_IRQ_DESC(bc12_done, RT9471_IRQ_BC12_DONE), + RT9471_IRQ_DESC(wdt, RT9471_IRQ_WDT), + RT9471_IRQ_DESC(otg_fault, RT9471_IRQ_OTG_FAULT), + }, *curr; + int i, virq, ret; + + for (i = 0; i < ARRAY_SIZE(chg_irqs); i++) { + curr = chg_irqs + i; + + virq = regmap_irq_get_virq(chip->irq_chip_data, curr->hwirq); + if (virq <= 0) + return virq; + + ret = devm_request_threaded_irq(dev, virq, NULL, curr->handler, + IRQF_ONESHOT, curr->name, chip); + if (ret) + return dev_err_probe(dev, ret, "Failed to register IRQ (%s)\n", + curr->name); + } + + return 0; +} + +static const struct regulator_ops rt9471_otg_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .set_current_limit = regulator_set_current_limit_regmap, + .get_current_limit = regulator_get_current_limit_regmap, +}; + +static const unsigned int rt9471_otg_microamp[] = { 500000, 1200000, }; + +static const struct regulator_desc rt9471_otg_rdesc = { + .of_match = of_match_ptr("usb-otg-vbus-regulator"), + .name = "rt9471-otg-vbus", + .owner = THIS_MODULE, + .type = REGULATOR_VOLTAGE, + .ops = &rt9471_otg_ops, + .min_uV = RT9471_OTGCV_MINUV, + .uV_step = RT9471_OTGCV_STEPUV, + .n_voltages = RT9471_NUM_VOTG, + .curr_table = rt9471_otg_microamp, + .n_current_limits = ARRAY_SIZE(rt9471_otg_microamp), + .enable_mask = RT9471_OTGEN_MASK, + .enable_reg = RT9471_REG_FUNC, + .vsel_reg = RT9471_REG_OTGCFG, + .vsel_mask = RT9471_OTGCV_MASK, + .csel_reg = RT9471_REG_OTGCFG, + .csel_mask = RT9471_OTGCC_MASK, +}; + +static int rt9471_register_otg_regulator(struct rt9471_chip *chip) +{ + struct device *dev = chip->dev; + struct regulator_config cfg = { .dev = dev, .driver_data = chip }; + + chip->otg_rdev = devm_regulator_register(dev, &rt9471_otg_rdesc, &cfg); + + return PTR_ERR_OR_ZERO(chip->otg_rdev); +} + +static inline struct rt9471_chip *psy_device_to_chip(struct device *dev) +{ + return power_supply_get_drvdata(to_power_supply(dev)); +} + +static ssize_t sysoff_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rt9471_chip *chip = psy_device_to_chip(dev); + unsigned int sysoff_enable; + int ret; + + ret = regmap_field_read(chip->rm_fields[F_BATFET_DIS], &sysoff_enable); + if (ret) + return ret; + + return sysfs_emit(buf, "%d\n", sysoff_enable); +} + +static ssize_t sysoff_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rt9471_chip *chip = psy_device_to_chip(dev); + unsigned int tmp; + int ret; + + ret = kstrtouint(buf, 10, &tmp); + if (ret) + return ret; + + ret = regmap_field_write(chip->rm_fields[F_BATFET_DIS], !!tmp); + if (ret) + return ret; + + return count; +} + +static ssize_t port_detect_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rt9471_chip *chip = psy_device_to_chip(dev); + unsigned int bc12_enable; + int ret; + + ret = regmap_field_read(chip->rm_fields[F_BC12_EN], &bc12_enable); + if (ret) + return ret; + + return sysfs_emit(buf, "%d\n", bc12_enable); +} + +static ssize_t port_detect_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rt9471_chip *chip = psy_device_to_chip(dev); + unsigned int tmp; + int ret; + + ret = kstrtouint(buf, 10, &tmp); + if (ret) + return ret; + + ret = regmap_field_write(chip->rm_fields[F_BC12_EN], !!tmp); + if (ret) + return ret; + + return count; +} + +static DEVICE_ATTR_RW(sysoff_enable); +static DEVICE_ATTR_RW(port_detect_enable); + +static struct attribute *rt9471_sysfs_attrs[] = { + &dev_attr_sysoff_enable.attr, + &dev_attr_port_detect_enable.attr, + NULL +}; + +ATTRIBUTE_GROUPS(rt9471_sysfs); + +static int rt9471_register_psy(struct rt9471_chip *chip) +{ + struct device *dev = chip->dev; + struct power_supply_desc *desc = &chip->psy_desc; + struct power_supply_config cfg = {}; + char *psy_name; + + cfg.drv_data = chip; + cfg.of_node = dev->of_node; + cfg.attr_grp = rt9471_sysfs_groups; + + psy_name = devm_kasprintf(dev, GFP_KERNEL, "rt9471-%s", dev_name(dev)); + if (!psy_name) + return -ENOMEM; + + desc->name = psy_name; + desc->type = POWER_SUPPLY_TYPE_USB; + desc->usb_types = rt9471_charger_usb_types; + desc->num_usb_types = ARRAY_SIZE(rt9471_charger_usb_types); + desc->properties = rt9471_charger_properties; + desc->num_properties = ARRAY_SIZE(rt9471_charger_properties); + desc->get_property = rt9471_charger_get_property; + desc->set_property = rt9471_charger_set_property; + desc->property_is_writeable = rt9471_charger_property_is_writeable; + + chip->psy = devm_power_supply_register(dev, desc, &cfg); + + return PTR_ERR_OR_ZERO(chip->psy); +} + +static const struct regmap_irq rt9471_regmap_irqs[] = { + REGMAP_IRQ_REG_LINE(RT9471_IRQ_BC12_DONE, 8), + REGMAP_IRQ_REG_LINE(RT9471_IRQ_DETACH, 8), + REGMAP_IRQ_REG_LINE(RT9471_IRQ_RECHG, 8), + REGMAP_IRQ_REG_LINE(RT9471_IRQ_CHG_DONE, 8), + REGMAP_IRQ_REG_LINE(RT9471_IRQ_BG_CHG, 8), + REGMAP_IRQ_REG_LINE(RT9471_IRQ_IE0C, 8), + REGMAP_IRQ_REG_LINE(RT9471_IRQ_CHG_RDY, 8), + REGMAP_IRQ_REG_LINE(RT9471_IRQ_VBUS_GD, 8), + REGMAP_IRQ_REG_LINE(RT9471_IRQ_CHG_BATOV, 8), + REGMAP_IRQ_REG_LINE(RT9471_IRQ_CHG_SYSOV, 8), + REGMAP_IRQ_REG_LINE(RT9471_IRQ_CHG_TOUT, 8), + REGMAP_IRQ_REG_LINE(RT9471_IRQ_CHG_BUSUV, 8), + REGMAP_IRQ_REG_LINE(RT9471_IRQ_CHG_THREG, 8), + REGMAP_IRQ_REG_LINE(RT9471_IRQ_CHG_AICR, 8), + REGMAP_IRQ_REG_LINE(RT9471_IRQ_CHG_MIVR, 8), + REGMAP_IRQ_REG_LINE(RT9471_IRQ_SYS_SHORT, 8), + REGMAP_IRQ_REG_LINE(RT9471_IRQ_SYS_MIN, 8), + REGMAP_IRQ_REG_LINE(RT9471_IRQ_AICC_DONE, 8), + REGMAP_IRQ_REG_LINE(RT9471_IRQ_PE_DONE, 8), + REGMAP_IRQ_REG_LINE(RT9471_IRQ_JEITA_COLD, 8), + REGMAP_IRQ_REG_LINE(RT9471_IRQ_JEITA_COOL, 8), + REGMAP_IRQ_REG_LINE(RT9471_IRQ_JEITA_WARM, 8), + REGMAP_IRQ_REG_LINE(RT9471_IRQ_JEITA_HOT, 8), + REGMAP_IRQ_REG_LINE(RT9471_IRQ_OTG_FAULT, 8), + REGMAP_IRQ_REG_LINE(RT9471_IRQ_OTG_LBP, 8), + REGMAP_IRQ_REG_LINE(RT9471_IRQ_OTG_CC, 8), + REGMAP_IRQ_REG_LINE(RT9471_IRQ_WDT, 8), + REGMAP_IRQ_REG_LINE(RT9471_IRQ_VAC_OV, 8), + REGMAP_IRQ_REG_LINE(RT9471_IRQ_OTP, 8), +}; + +static const struct regmap_irq_chip rt9471_irq_chip = { + .name = "rt9471-irqs", + .status_base = RT9471_REG_IRQ0, + .mask_base = RT9471_REG_MASK0, + .num_regs = RT9471_NUM_IRQ_REGS, + .irqs = rt9471_regmap_irqs, + .num_irqs = ARRAY_SIZE(rt9471_regmap_irqs), +}; + +static const struct reg_sequence rt9471_init_regs[] = { + REG_SEQ0(RT9471_REG_INFO, 0x80), /* REG_RST */ + REG_SEQ0(RT9471_REG_TOP, 0xC0), /* WDT = 0 */ + REG_SEQ0(RT9471_REG_FUNC, 0x01), /* BATFET_DIS_DLY = 0 */ + REG_SEQ0(RT9471_REG_IBUS, 0x0A), /* AUTO_AICR = 0 */ + REG_SEQ0(RT9471_REG_VBUS, 0xC6), /* VAC_OVP = 14V */ + REG_SEQ0(RT9471_REG_JEITA, 0x38), /* JEITA = 0 */ + REG_SEQ0(RT9471_REG_DPDMDET, 0x31), /* BC12_EN = 0, DCP_DP_OPT = 1 */ +}; + +static int rt9471_check_devinfo(struct rt9471_chip *chip) +{ + struct device *dev = chip->dev; + unsigned int dev_id; + int ret; + + ret = regmap_field_read(chip->rm_fields[F_DEVICE_ID], &dev_id); + if (ret) + return dev_err_probe(dev, ret, "Failed to read device_id\n"); + + switch (dev_id) { + case RT9470_DEVID: + case RT9470D_DEVID: + case RT9471_DEVID: + case RT9471D_DEVID: + return 0; + default: + return dev_err_probe(dev, -ENODEV, "Incorrect device id\n"); + } +} + +static bool rt9471_accessible_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x00 ... 0x0F: + case 0x10 ... 0x13: + case 0x20 ... 0x33: + case 0x40 ... 0xA1: + return true; + default: + return false; + } +} + +static const struct regmap_config rt9471_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xA1, + .writeable_reg = rt9471_accessible_reg, + .readable_reg = rt9471_accessible_reg, +}; + +static int rt9471_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct rt9471_chip *chip; + struct gpio_desc *ce_gpio; + struct regmap *regmap; + int ret; + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = dev; + mutex_init(&chip->var_lock); + i2c_set_clientdata(i2c, chip); + + /* Default pull charge enable gpio to make 'CHG_EN' by SW control only */ + ce_gpio = devm_gpiod_get_optional(dev, "charge-enable", GPIOD_OUT_HIGH); + if (IS_ERR(chip->ce_gpio)) + return dev_err_probe(dev, PTR_ERR(ce_gpio), + "Failed to config charge enable gpio\n"); + + regmap = devm_regmap_init_i2c(i2c, &rt9471_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "Failed to init regmap\n"); + + chip->regmap = regmap; + + ret = devm_regmap_field_bulk_alloc(dev, regmap, chip->rm_fields, + rt9471_reg_fields, + ARRAY_SIZE(rt9471_reg_fields)); + if (ret) + return dev_err_probe(dev, ret, "Failed to alloc regmap field\n"); + + ret = rt9471_check_devinfo(chip); + if (ret) + return ret; + + ret = regmap_register_patch(regmap, rt9471_init_regs, + ARRAY_SIZE(rt9471_init_regs)); + if (ret) + return dev_err_probe(dev, ret, "Failed to init registers\n"); + + ret = devm_regmap_add_irq_chip(dev, regmap, i2c->irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 0, + &rt9471_irq_chip, &chip->irq_chip_data); + if (ret) + return dev_err_probe(dev, ret, "Failed to add IRQ chip\n"); + + ret = rt9471_register_psy(chip); + if (ret) + return dev_err_probe(dev, ret, "Failed to register psy\n"); + + ret = rt9471_register_otg_regulator(chip); + if (ret) + return dev_err_probe(dev, ret, "Failed to register otg\n"); + + ret = rt9471_register_interrupts(chip); + if (ret) + return ret; + + /* After IRQs are all initialized, enable port detection by default */ + return regmap_field_write(chip->rm_fields[F_BC12_EN], 1); +} + +static void rt9471_shutdown(struct i2c_client *i2c) +{ + struct rt9471_chip *chip = i2c_get_clientdata(i2c); + + /* + * There's no external reset pin. Do register reset to guarantee charger + * function is normal after shutdown + */ + regmap_field_write(chip->rm_fields[F_REG_RST], 1); +} + +static const struct of_device_id rt9471_of_device_id[] = { + { .compatible = "richtek,rt9471" }, + {} +}; +MODULE_DEVICE_TABLE(of, rt9471_of_device_id); + +static struct i2c_driver rt9471_driver = { + .driver = { + .name = "rt9471", + .of_match_table = rt9471_of_device_id, + }, + .probe_new = rt9471_probe, + .shutdown = rt9471_shutdown, +}; +module_i2c_driver(rt9471_driver); + +MODULE_DESCRIPTION("Richtek RT9471 charger driver"); +MODULE_AUTHOR("Alina Yu "); +MODULE_AUTHOR("ChiYuan Huang "); +MODULE_LICENSE("GPL"); -- cgit From ab8174bbc39669321b25a0fbeef630fdbe1b8ffe Mon Sep 17 00:00:00 2001 From: ChiYuan Huang Date: Fri, 13 Jan 2023 14:17:46 +0800 Subject: Documentation: power: rt9471: Document exported sysfs entries Document the settings exported by rt9471 charger driver through sysfs entries: - sysoff_enable - port_detect_enable Signed-off-by: ChiYuan Huang [update kernel version and date] Signed-off-by: Sebastian Reichel --- Documentation/ABI/testing/sysfs-class-power-rt9471 | 32 ++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-class-power-rt9471 diff --git a/Documentation/ABI/testing/sysfs-class-power-rt9471 b/Documentation/ABI/testing/sysfs-class-power-rt9471 new file mode 100644 index 000000000000..0a390ee5ac21 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-power-rt9471 @@ -0,0 +1,32 @@ +What: /sys/class/power_supply/rt9471-*/sysoff_enable +Date: Feb 2023 +KernelVersion: 6.3 +Contact: ChiYuan Huang +Description: + This entry allows enabling the sysoff mode of rt9471 charger devices. + If enabled and the input is removed, the internal battery FET is turned + off to reduce the leakage from the BAT pin. See device datasheet for details. + It's commonly used when the product enter shipping stage. After entering + shipping mode, only 'VBUS' or 'Power key" pressed can make it leave this + mode. 'Disable' also can help to leave it, but it's more like to abort + the action before the device really enter shipping mode. + + Access: Read, Write + Valid values: + - 1: enabled + - 0: disabled + +What: /sys/class/power_supply/rt9471-*/port_detect_enable +Date: Feb 2023 +KernelVersion: 6.3 +Contact: ChiYuan Huang +Description: + This entry allows enabling the USB BC12 port detect function of rt9471 charger + devices. If enabled and VBUS is inserted, device will start to do the BC12 + port detect and report the usb port type when port detect is done. See + datasheet for details. Normally controlled when TypeC/USBPD port integrated. + + Access: Read, Write + Valid values: + - 1: enabled + - 0: disabled -- cgit From e1b4620fb50316e0b271bd50a1dfdd11eee369c4 Mon Sep 17 00:00:00 2001 From: ChiaEn Wu Date: Tue, 3 Jan 2023 15:29:56 +0800 Subject: dt-bindings: power: supply: Add Richtek RT9467 battery charger Add bindings for the Richtek RT9467 battery charger. Reviewed-by: Krzysztof Kozlowski Co-developed-by: ChiYuan Huang Signed-off-by: ChiYuan Huang Signed-off-by: ChiaEn Wu Signed-off-by: Sebastian Reichel --- .../power/supply/richtek,rt9467-charger.yaml | 82 ++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 Documentation/devicetree/bindings/power/supply/richtek,rt9467-charger.yaml diff --git a/Documentation/devicetree/bindings/power/supply/richtek,rt9467-charger.yaml b/Documentation/devicetree/bindings/power/supply/richtek,rt9467-charger.yaml new file mode 100644 index 000000000000..92c570643d2c --- /dev/null +++ b/Documentation/devicetree/bindings/power/supply/richtek,rt9467-charger.yaml @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/power/supply/richtek,rt9467-charger.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Richtek RT9467 Switching Battery Charger with Power Path Management + +maintainers: + - ChiYuan Huang + - ChiaEn Wu + +description: | + RT9467 is a switch-mode single cell Li-Ion/Li-Polymer battery charger for + portable applications. It integrates a synchronous PWM controller, power + MOSFETs, input current sensing and regulation, high-accuracy voltage + regulation, and charge termination. The charge current is regulated through + integrated sensing resistors. + + The RT9467 also features USB On-The-Go (OTG) support. It also integrates + D+/D- pin for USB host/charging port detection. + + Datasheet is available at + https://www.richtek.com/assets/product_file/RT9467/DS9467-01.pdf + +properties: + compatible: + const: richtek,rt9467-charger + + reg: + maxItems: 1 + + wakeup-source: true + + interrupts: + maxItems: 1 + + charge-enable-gpios: + description: GPIO is used to turn on and off charging. + maxItems: 1 + + usb-otg-vbus-regulator: + type: object + description: OTG boost regulator. + unevaluatedProperties: false + $ref: /schemas/regulator/regulator.yaml# + + properties: + enable-gpios: true + +required: + - compatible + - reg + - wakeup-source + - interrupts + +additionalProperties: false + +examples: + - | + #include + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + + charger@5b { + compatible = "richtek,rt9467-charger"; + reg = <0x5b>; + wakeup-source; + interrupts-extended = <&gpio_intc 32 IRQ_TYPE_LEVEL_LOW>; + charge-enable-gpios = <&gpio26 1 GPIO_ACTIVE_LOW>; + + rt9467_otg_vbus: usb-otg-vbus-regulator { + regulator-name = "rt9467-usb-otg-vbus"; + regulator-min-microvolt = <4425000>; + regulator-max-microvolt = <5825000>; + regulator-min-microamp = <500000>; + regulator-max-microamp = <3000000>; + }; + }; + }; -- cgit From 6f7f70e3a8dd1fbce95eaea2a8eff70f01363c00 Mon Sep 17 00:00:00 2001 From: ChiaEn Wu Date: Tue, 3 Jan 2023 15:29:57 +0800 Subject: power: supply: rt9467: Add Richtek RT9467 charger driver RT9467 is a switch-mode single cell Li-Ion/Li-Polymer battery charger for portable applications. It integrates a synchronous PWM controller, power MOSFETs, input current sensing and regulation, high-accuracy voltage regulation, and charge termination. The charge current is regulated through integrated sensing resistors. The RT9467 also features USB On-The-Go (OTG) support. It also integrates D+/D- pin for USB host/charging port detection. Co-developed-by: ChiYuan Huang Signed-off-by: ChiYuan Huang Signed-off-by: ChiaEn Wu Signed-off-by: Sebastian Reichel --- drivers/power/supply/Kconfig | 19 + drivers/power/supply/Makefile | 1 + drivers/power/supply/rt9467-charger.c | 1282 +++++++++++++++++++++++++++++++++ 3 files changed, 1302 insertions(+) create mode 100644 drivers/power/supply/rt9467-charger.c diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 95e7137af18a..ba69d7bf2d70 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -793,6 +793,25 @@ config CHARGER_RT9455 help Say Y to enable support for Richtek RT9455 battery charger. +config CHARGER_RT9467 + tristate "Richtek RT9467 Battery Charger Driver" + depends on I2C && GPIOLIB && REGULATOR + select REGMAP_I2C + select REGMAP_IRQ + select LINEAR_RANGES + help + Say Y here to enable RT9467 Battery Charger. + RT9467 is a switch-mode single cell Li-Ion/Li-Polymer battery charger + for portable applications. It integrates a synchronous PWM controller, + power MOSFETs, input current sensing and regulation, high-accuracy + voltage regulation, and charge termination. The charge current is + regulated through integrated sensing resistors. It also features + USB On-The-Go (OTG) support and integrates D+/D- pin for USB + host/charging port detection. + + This driver can also be built as a module. If so, the module + will be called "rt9467-charger". + config CHARGER_RT9471 tristate "Richtek RT9471 battery charger driver" depends on I2C && GPIOLIB && REGULATOR diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index cf687e21e6e2..f8f9716d3ba4 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_BATTERY_MAX1721X) += max1721x_battery.o obj-$(CONFIG_BATTERY_Z2) += z2_battery.o obj-$(CONFIG_BATTERY_RT5033) += rt5033_battery.o obj-$(CONFIG_CHARGER_RT9455) += rt9455_charger.o +obj-$(CONFIG_CHARGER_RT9467) += rt9467-charger.o obj-$(CONFIG_CHARGER_RT9471) += rt9471.o obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o obj-$(CONFIG_BATTERY_TWL4030_MADC) += twl4030_madc_battery.o diff --git a/drivers/power/supply/rt9467-charger.c b/drivers/power/supply/rt9467-charger.c new file mode 100644 index 000000000000..96ad0d7d3af4 --- /dev/null +++ b/drivers/power/supply/rt9467-charger.c @@ -0,0 +1,1282 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 Richtek Technology Corp. + * + * Author: ChiYuan Huang + * ChiaEn Wu + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RT9467_REG_CORE_CTRL0 0x00 +#define RT9467_REG_CHG_CTRL1 0x01 +#define RT9467_REG_CHG_CTRL2 0x02 +#define RT9467_REG_CHG_CTRL3 0x03 +#define RT9467_REG_CHG_CTRL4 0x04 +#define RT9467_REG_CHG_CTRL5 0x05 +#define RT9467_REG_CHG_CTRL6 0x06 +#define RT9467_REG_CHG_CTRL7 0x07 +#define RT9467_REG_CHG_CTRL8 0x08 +#define RT9467_REG_CHG_CTRL9 0x09 +#define RT9467_REG_CHG_CTRL10 0x0A +#define RT9467_REG_CHG_CTRL12 0x0C +#define RT9467_REG_CHG_CTRL13 0x0D +#define RT9467_REG_CHG_CTRL14 0x0E +#define RT9467_REG_CHG_ADC 0x11 +#define RT9467_REG_CHG_DPDM1 0x12 +#define RT9467_REG_CHG_DPDM2 0x13 +#define RT9467_REG_DEVICE_ID 0x40 +#define RT9467_REG_CHG_STAT 0x42 +#define RT9467_REG_ADC_DATA_H 0x44 +#define RT9467_REG_CHG_STATC 0x50 +#define RT9467_REG_CHG_IRQ1 0x53 +#define RT9467_REG_CHG_STATC_CTRL 0x60 +#define RT9467_REG_CHG_IRQ1_CTRL 0x63 + +#define RT9467_MASK_PWR_RDY BIT(7) +#define RT9467_MASK_MIVR_STAT BIT(6) +#define RT9467_MASK_OTG_CSEL GENMASK(2, 0) +#define RT9467_MASK_OTG_VSEL GENMASK(7, 2) +#define RT9467_MASK_OTG_EN BIT(0) +#define RT9467_MASK_ADC_IN_SEL GENMASK(7, 4) +#define RT9467_MASK_ADC_START BIT(0) + +#define RT9467_NUM_IRQ_REGS 4 +#define RT9467_ICHG_MIN_uA 100000 +#define RT9467_ICHG_MAX_uA 5000000 +#define RT9467_CV_MAX_uV 4710000 +#define RT9467_OTG_MIN_uV 4425000 +#define RT9467_OTG_MAX_uV 5825000 +#define RT9467_OTG_STEP_uV 25000 +#define RT9467_NUM_VOTG (RT9467_OTG_MAX_uV - RT9467_OTG_MIN_uV + 1) +#define RT9467_AICLVTH_GAP_uV 200000 +#define RT9467_ADCCONV_TIME_MS 35 + +#define RT9466_VID 0x8 +#define RT9467_VID 0x9 + +/* IRQ number */ +#define RT9467_IRQ_TS_STATC 0 +#define RT9467_IRQ_CHG_FAULT 1 +#define RT9467_IRQ_CHG_STATC 2 +#define RT9467_IRQ_CHG_TMR 3 +#define RT9467_IRQ_CHG_BATABS 4 +#define RT9467_IRQ_CHG_ADPBAD 5 +#define RT9467_IRQ_CHG_RVP 6 +#define RT9467_IRQ_OTP 7 + +#define RT9467_IRQ_CHG_AICLM 8 +#define RT9467_IRQ_CHG_ICHGM 9 +#define RT9467_IRQ_WDTMR 11 +#define RT9467_IRQ_SSFINISH 12 +#define RT9467_IRQ_CHG_RECHG 13 +#define RT9467_IRQ_CHG_TERM 14 +#define RT9467_IRQ_CHG_IEOC 15 + +#define RT9467_IRQ_ADC_DONE 16 +#define RT9467_IRQ_PUMPX_DONE 17 +#define RT9467_IRQ_BST_BATUV 21 +#define RT9467_IRQ_BST_MIDOV 22 +#define RT9467_IRQ_BST_OLP 23 + +#define RT9467_IRQ_ATTACH 24 +#define RT9467_IRQ_DETACH 25 +#define RT9467_IRQ_HVDCP_DET 29 +#define RT9467_IRQ_CHGDET 30 +#define RT9467_IRQ_DCDT 31 + +enum rt9467_fields { + /* RT9467_REG_CORE_CTRL0 */ + F_RST = 0, + /* RT9467_REG_CHG_CTRL1 */ + F_HZ, F_OTG_PIN_EN, F_OPA_MODE, + /* RT9467_REG_CHG_CTRL2 */ + F_SHIP_MODE, F_TE, F_IINLMTSEL, F_CFO_EN, F_CHG_EN, + /* RT9467_REG_CHG_CTRL3 */ + F_IAICR, F_ILIM_EN, + /* RT9467_REG_CHG_CTRL4 */ + F_VOREG, + /* RT9467_REG_CHG_CTRL6 */ + F_VMIVR, + /* RT9467_REG_CHG_CTRL7 */ + F_ICHG, + /* RT9467_REG_CHG_CTRL8 */ + F_IPREC, + /* RT9467_REG_CHG_CTRL9 */ + F_IEOC, + /* RT9467_REG_CHG_CTRL12 */ + F_WT_FC, + /* RT9467_REG_CHG_CTRL13 */ + F_OCP, + /* RT9467_REG_CHG_CTRL14 */ + F_AICL_MEAS, F_AICL_VTH, + /* RT9467_REG_CHG_DPDM1 */ + F_USBCHGEN, + /* RT9467_REG_CHG_DPDM2 */ + F_USB_STATUS, + /* RT9467_REG_DEVICE_ID */ + F_VENDOR, + /* RT9467_REG_CHG_STAT */ + F_CHG_STAT, + /* RT9467_REG_CHG_STATC */ + F_PWR_RDY, F_CHG_MIVR, + F_MAX_FIELDS +}; + +static const struct regmap_irq rt9467_irqs[] = { + REGMAP_IRQ_REG_LINE(RT9467_IRQ_TS_STATC, 8), + REGMAP_IRQ_REG_LINE(RT9467_IRQ_CHG_FAULT, 8), + REGMAP_IRQ_REG_LINE(RT9467_IRQ_CHG_STATC, 8), + REGMAP_IRQ_REG_LINE(RT9467_IRQ_CHG_TMR, 8), + REGMAP_IRQ_REG_LINE(RT9467_IRQ_CHG_BATABS, 8), + REGMAP_IRQ_REG_LINE(RT9467_IRQ_CHG_ADPBAD, 8), + REGMAP_IRQ_REG_LINE(RT9467_IRQ_CHG_RVP, 8), + REGMAP_IRQ_REG_LINE(RT9467_IRQ_OTP, 8), + REGMAP_IRQ_REG_LINE(RT9467_IRQ_CHG_AICLM, 8), + REGMAP_IRQ_REG_LINE(RT9467_IRQ_CHG_ICHGM, 8), + REGMAP_IRQ_REG_LINE(RT9467_IRQ_WDTMR, 8), + REGMAP_IRQ_REG_LINE(RT9467_IRQ_SSFINISH, 8), + REGMAP_IRQ_REG_LINE(RT9467_IRQ_CHG_RECHG, 8), + REGMAP_IRQ_REG_LINE(RT9467_IRQ_CHG_TERM, 8), + REGMAP_IRQ_REG_LINE(RT9467_IRQ_CHG_IEOC, 8), + REGMAP_IRQ_REG_LINE(RT9467_IRQ_ADC_DONE, 8), + REGMAP_IRQ_REG_LINE(RT9467_IRQ_PUMPX_DONE, 8), + REGMAP_IRQ_REG_LINE(RT9467_IRQ_BST_BATUV, 8), + REGMAP_IRQ_REG_LINE(RT9467_IRQ_BST_MIDOV, 8), + REGMAP_IRQ_REG_LINE(RT9467_IRQ_BST_OLP, 8), + REGMAP_IRQ_REG_LINE(RT9467_IRQ_ATTACH, 8), + REGMAP_IRQ_REG_LINE(RT9467_IRQ_DETACH, 8), + REGMAP_IRQ_REG_LINE(RT9467_IRQ_HVDCP_DET, 8), + REGMAP_IRQ_REG_LINE(RT9467_IRQ_CHGDET, 8), + REGMAP_IRQ_REG_LINE(RT9467_IRQ_DCDT, 8) +}; + +static const struct regmap_irq_chip rt9467_irq_chip = { + .name = "rt9467-irqs", + .status_base = RT9467_REG_CHG_IRQ1, + .mask_base = RT9467_REG_CHG_IRQ1_CTRL, + .num_regs = RT9467_NUM_IRQ_REGS, + .irqs = rt9467_irqs, + .num_irqs = ARRAY_SIZE(rt9467_irqs), +}; + +enum rt9467_ranges { + RT9467_RANGE_IAICR = 0, + RT9467_RANGE_VOREG, + RT9467_RANGE_VMIVR, + RT9467_RANGE_ICHG, + RT9467_RANGE_IPREC, + RT9467_RANGE_IEOC, + RT9467_RANGE_AICL_VTH, + RT9467_RANGES_MAX +}; + +static const struct linear_range rt9467_ranges[RT9467_RANGES_MAX] = { + LINEAR_RANGE_IDX(RT9467_RANGE_IAICR, 100000, 0x0, 0x3F, 50000), + LINEAR_RANGE_IDX(RT9467_RANGE_VOREG, 3900000, 0x0, 0x51, 10000), + LINEAR_RANGE_IDX(RT9467_RANGE_VMIVR, 3900000, 0x0, 0x5F, 100000), + LINEAR_RANGE_IDX(RT9467_RANGE_ICHG, 900000, 0x08, 0x31, 100000), + LINEAR_RANGE_IDX(RT9467_RANGE_IPREC, 100000, 0x0, 0x0F, 50000), + LINEAR_RANGE_IDX(RT9467_RANGE_IEOC, 100000, 0x0, 0x0F, 50000), + LINEAR_RANGE_IDX(RT9467_RANGE_AICL_VTH, 4100000, 0x0, 0x7, 100000), +}; + +static const struct reg_field rt9467_chg_fields[] = { + [F_RST] = REG_FIELD(RT9467_REG_CORE_CTRL0, 7, 7), + [F_HZ] = REG_FIELD(RT9467_REG_CHG_CTRL1, 2, 2), + [F_OTG_PIN_EN] = REG_FIELD(RT9467_REG_CHG_CTRL1, 1, 1), + [F_OPA_MODE] = REG_FIELD(RT9467_REG_CHG_CTRL1, 0, 0), + [F_SHIP_MODE] = REG_FIELD(RT9467_REG_CHG_CTRL2, 7, 7), + [F_TE] = REG_FIELD(RT9467_REG_CHG_CTRL2, 4, 4), + [F_IINLMTSEL] = REG_FIELD(RT9467_REG_CHG_CTRL2, 2, 3), + [F_CFO_EN] = REG_FIELD(RT9467_REG_CHG_CTRL2, 1, 1), + [F_CHG_EN] = REG_FIELD(RT9467_REG_CHG_CTRL2, 0, 0), + [F_IAICR] = REG_FIELD(RT9467_REG_CHG_CTRL3, 2, 7), + [F_ILIM_EN] = REG_FIELD(RT9467_REG_CHG_CTRL3, 0, 0), + [F_VOREG] = REG_FIELD(RT9467_REG_CHG_CTRL4, 1, 7), + [F_VMIVR] = REG_FIELD(RT9467_REG_CHG_CTRL6, 1, 7), + [F_ICHG] = REG_FIELD(RT9467_REG_CHG_CTRL7, 2, 7), + [F_IPREC] = REG_FIELD(RT9467_REG_CHG_CTRL8, 0, 3), + [F_IEOC] = REG_FIELD(RT9467_REG_CHG_CTRL9, 4, 7), + [F_WT_FC] = REG_FIELD(RT9467_REG_CHG_CTRL12, 5, 7), + [F_OCP] = REG_FIELD(RT9467_REG_CHG_CTRL13, 2, 2), + [F_AICL_MEAS] = REG_FIELD(RT9467_REG_CHG_CTRL14, 7, 7), + [F_AICL_VTH] = REG_FIELD(RT9467_REG_CHG_CTRL14, 0, 2), + [F_USBCHGEN] = REG_FIELD(RT9467_REG_CHG_DPDM1, 7, 7), + [F_USB_STATUS] = REG_FIELD(RT9467_REG_CHG_DPDM2, 0, 2), + [F_VENDOR] = REG_FIELD(RT9467_REG_DEVICE_ID, 4, 7), + [F_CHG_STAT] = REG_FIELD(RT9467_REG_CHG_STAT, 6, 7), + [F_PWR_RDY] = REG_FIELD(RT9467_REG_CHG_STATC, 7, 7), + [F_CHG_MIVR] = REG_FIELD(RT9467_REG_CHG_STATC, 6, 6), +}; + +enum { + RT9467_STAT_READY = 0, + RT9467_STAT_PROGRESS, + RT9467_STAT_CHARGE_DONE, + RT9467_STAT_FAULT +}; + +enum rt9467_adc_chan { + RT9467_ADC_VBUS_DIV5 = 0, + RT9467_ADC_VBUS_DIV2, + RT9467_ADC_VSYS, + RT9467_ADC_VBAT, + RT9467_ADC_TS_BAT, + RT9467_ADC_IBUS, + RT9467_ADC_IBAT, + RT9467_ADC_REGN, + RT9467_ADC_TEMP_JC +}; + +enum rt9467_chg_type { + RT9467_CHG_TYPE_NOVBUS = 0, + RT9467_CHG_TYPE_UNDER_GOING, + RT9467_CHG_TYPE_SDP, + RT9467_CHG_TYPE_SDPNSTD, + RT9467_CHG_TYPE_DCP, + RT9467_CHG_TYPE_CDP, + RT9467_CHG_TYPE_MAX +}; + +enum rt9467_iin_limit_sel { + RT9467_IINLMTSEL_3_2A = 0, + RT9467_IINLMTSEL_CHG_TYP, + RT9467_IINLMTSEL_AICR, + RT9467_IINLMTSEL_LOWER_LEVEL, /* lower of above three */ +}; + +struct rt9467_chg_data { + struct device *dev; + struct regmap *regmap; + struct regmap_field *rm_field[F_MAX_FIELDS]; + struct regmap_irq_chip_data *irq_chip_data; + struct power_supply *psy; + struct mutex adc_lock; + struct mutex attach_lock; + struct mutex ichg_ieoc_lock; + struct regulator_dev *rdev; + struct completion aicl_done; + enum power_supply_usb_type psy_usb_type; + unsigned int old_stat; + unsigned int vid; + int ichg_ua; + int ieoc_ua; +}; + +static int rt9467_otg_of_parse_cb(struct device_node *of, + const struct regulator_desc *desc, + struct regulator_config *cfg) +{ + struct rt9467_chg_data *data = cfg->driver_data; + + cfg->ena_gpiod = fwnode_gpiod_get_index(of_fwnode_handle(of), + "enable", 0, GPIOD_OUT_LOW | + GPIOD_FLAGS_BIT_NONEXCLUSIVE, + desc->name); + if (IS_ERR(cfg->ena_gpiod)) { + cfg->ena_gpiod = NULL; + return 0; + } + + return regmap_field_write(data->rm_field[F_OTG_PIN_EN], 1); +} + +static const struct regulator_ops rt9467_otg_regulator_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_current_limit = regulator_set_current_limit_regmap, + .get_current_limit = regulator_get_current_limit_regmap, +}; + +static const u32 rt9467_otg_microamp[] = { + 500000, 700000, 1100000, 1300000, 1800000, 2100000, 2400000, 3000000 +}; + +static const struct regulator_desc rt9467_otg_desc = { + .name = "rt9476-usb-otg-vbus", + .of_match = "usb-otg-vbus-regulator", + .of_parse_cb = rt9467_otg_of_parse_cb, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .min_uV = RT9467_OTG_MIN_uV, + .uV_step = RT9467_OTG_STEP_uV, + .n_voltages = RT9467_NUM_VOTG, + .curr_table = rt9467_otg_microamp, + .n_current_limits = ARRAY_SIZE(rt9467_otg_microamp), + .csel_reg = RT9467_REG_CHG_CTRL10, + .csel_mask = RT9467_MASK_OTG_CSEL, + .vsel_reg = RT9467_REG_CHG_CTRL5, + .vsel_mask = RT9467_MASK_OTG_VSEL, + .enable_reg = RT9467_REG_CHG_CTRL1, + .enable_mask = RT9467_MASK_OTG_EN, + .ops = &rt9467_otg_regulator_ops, +}; + +static int rt9467_register_otg_regulator(struct rt9467_chg_data *data) +{ + struct regulator_config cfg = { + .dev = data->dev, + .regmap = data->regmap, + .driver_data = data, + }; + + data->rdev = devm_regulator_register(data->dev, &rt9467_otg_desc, &cfg); + return PTR_ERR_OR_ZERO(data->rdev); +} + +static int rt9467_get_value_from_ranges(struct rt9467_chg_data *data, + enum rt9467_fields field, + enum rt9467_ranges rsel, + int *value) +{ + const struct linear_range *range = rt9467_ranges + rsel; + unsigned int sel; + int ret; + + ret = regmap_field_read(data->rm_field[field], &sel); + if (ret) + return ret; + + return linear_range_get_value(range, sel, value); +} + +static int rt9467_set_value_from_ranges(struct rt9467_chg_data *data, + enum rt9467_fields field, + enum rt9467_ranges rsel, + int value) +{ + const struct linear_range *range = rt9467_ranges + rsel; + unsigned int sel; + bool found; + int ret; + + if (rsel == RT9467_RANGE_VMIVR) { + ret = linear_range_get_selector_high(range, value, &sel, &found); + if (ret) + value = range->max_sel; + } else { + linear_range_get_selector_within(range, value, &sel); + } + + return regmap_field_write(data->rm_field[field], sel); +} + +static int rt9467_get_adc_sel(enum rt9467_adc_chan chan, int *sel) +{ + switch (chan) { + case RT9467_ADC_VBUS_DIV5: + case RT9467_ADC_VBUS_DIV2: + case RT9467_ADC_VSYS: + case RT9467_ADC_VBAT: + *sel = chan + 1; + return 0; + case RT9467_ADC_TS_BAT: + *sel = chan + 2; + return 0; + case RT9467_ADC_IBUS: + case RT9467_ADC_IBAT: + *sel = chan + 3; + return 0; + case RT9467_ADC_REGN: + case RT9467_ADC_TEMP_JC: + *sel = chan + 4; + return 0; + default: + return -EINVAL; + } +} + +static int rt9467_get_adc_raw_data(struct rt9467_chg_data *data, + enum rt9467_adc_chan chan, int *val) +{ + unsigned int adc_stat, reg_val, adc_sel; + __be16 chan_raw_data; + int ret; + + mutex_lock(&data->adc_lock); + + ret = rt9467_get_adc_sel(chan, &adc_sel); + if (ret) + goto adc_unlock; + + ret = regmap_write(data->regmap, RT9467_REG_CHG_ADC, 0); + if (ret) { + dev_err(data->dev, "Failed to clear ADC enable\n"); + goto adc_unlock; + } + + reg_val = RT9467_MASK_ADC_START | FIELD_PREP(RT9467_MASK_ADC_IN_SEL, adc_sel); + ret = regmap_write(data->regmap, RT9467_REG_CHG_ADC, reg_val); + if (ret) + goto adc_unlock; + + /* Minimum wait time for one channel processing */ + msleep(RT9467_ADCCONV_TIME_MS); + + ret = regmap_read_poll_timeout(data->regmap, RT9467_REG_CHG_ADC, + adc_stat, + !(adc_stat & RT9467_MASK_ADC_START), + MILLI, RT9467_ADCCONV_TIME_MS * MILLI); + if (ret) { + dev_err(data->dev, "Failed to wait ADC conversion, chan = %d\n", chan); + goto adc_unlock; + } + + ret = regmap_raw_read(data->regmap, RT9467_REG_ADC_DATA_H, + &chan_raw_data, sizeof(chan_raw_data)); + if (ret) + goto adc_unlock; + + *val = be16_to_cpu(chan_raw_data); + +adc_unlock: + mutex_unlock(&data->adc_lock); + return ret; +} + +static int rt9467_get_adc(struct rt9467_chg_data *data, + enum rt9467_adc_chan chan, int *val) +{ + unsigned int aicr_ua, ichg_ua; + int ret; + + ret = rt9467_get_adc_raw_data(data, chan, val); + if (ret) + return ret; + + switch (chan) { + case RT9467_ADC_VBUS_DIV5: + *val *= 25000; + return 0; + case RT9467_ADC_VBUS_DIV2: + *val *= 10000; + return 0; + case RT9467_ADC_VBAT: + case RT9467_ADC_VSYS: + case RT9467_ADC_REGN: + *val *= 5000; + return 0; + case RT9467_ADC_TS_BAT: + *val /= 400; + return 0; + case RT9467_ADC_IBUS: + /* UUG MOS turn-on ratio will affect the IBUS adc scale */ + ret = rt9467_get_value_from_ranges(data, F_IAICR, + RT9467_RANGE_IAICR, &aicr_ua); + if (ret) + return ret; + + *val *= aicr_ua < 400000 ? 29480 : 50000; + return 0; + case RT9467_ADC_IBAT: + /* PP MOS turn-on ratio will affect the ICHG adc scale */ + ret = rt9467_get_value_from_ranges(data, F_ICHG, + RT9467_RANGE_ICHG, &ichg_ua); + if (ret) + return ret; + + *val *= ichg_ua <= 400000 ? 28500 : + ichg_ua <= 800000 ? 31500 : 500000; + return 0; + case RT9467_ADC_TEMP_JC: + *val = ((*val * 2) - 40) * 10; + return 0; + default: + return -EINVAL; + } +} + +static int rt9467_psy_get_status(struct rt9467_chg_data *data, int *state) +{ + unsigned int status; + int ret; + + ret = regmap_field_read(data->rm_field[F_CHG_STAT], &status); + if (ret) + return ret; + + switch (status) { + case RT9467_STAT_READY: + *state = POWER_SUPPLY_STATUS_NOT_CHARGING; + return 0; + case RT9467_STAT_PROGRESS: + *state = POWER_SUPPLY_STATUS_CHARGING; + return 0; + case RT9467_STAT_CHARGE_DONE: + *state = POWER_SUPPLY_STATUS_FULL; + return 0; + default: + *state = POWER_SUPPLY_STATUS_UNKNOWN; + return 0; + } +} + +static int rt9467_psy_set_ichg(struct rt9467_chg_data *data, int microamp) +{ + int ret; + + mutex_lock(&data->ichg_ieoc_lock); + + if (microamp < 500000) { + dev_err(data->dev, "Minimum value must be 500mA\n"); + microamp = 500000; + } + + ret = rt9467_set_value_from_ranges(data, F_ICHG, RT9467_RANGE_ICHG, microamp); + if (ret) + goto out; + + ret = rt9467_get_value_from_ranges(data, F_ICHG, RT9467_RANGE_ICHG, + &data->ichg_ua); + if (ret) + goto out; + +out: + mutex_unlock(&data->ichg_ieoc_lock); + return ret; +} + +static int rt9467_run_aicl(struct rt9467_chg_data *data) +{ + unsigned int statc, aicl_vth; + int mivr_vth, aicr_get; + int ret = 0; + + + ret = regmap_read(data->regmap, RT9467_REG_CHG_STATC, &statc); + if (ret) { + dev_err(data->dev, "Failed to read status\n"); + return ret; + } + + if (!(statc & RT9467_MASK_PWR_RDY) || !(statc & RT9467_MASK_MIVR_STAT)) { + dev_info(data->dev, "Condition not matched %d\n", statc); + return 0; + } + + ret = rt9467_get_value_from_ranges(data, F_VMIVR, RT9467_RANGE_VMIVR, + &mivr_vth); + if (ret) { + dev_err(data->dev, "Failed to get mivr\n"); + return ret; + } + + /* AICL_VTH = MIVR_VTH + 200mV */ + aicl_vth = mivr_vth + RT9467_AICLVTH_GAP_uV; + ret = rt9467_set_value_from_ranges(data, F_AICL_VTH, + RT9467_RANGE_AICL_VTH, aicl_vth); + + /* Trigger AICL function */ + ret = regmap_field_write(data->rm_field[F_AICL_MEAS], 1); + if (ret) { + dev_err(data->dev, "Failed to set aicl measurement\n"); + return ret; + } + + reinit_completion(&data->aicl_done); + ret = wait_for_completion_timeout(&data->aicl_done, msecs_to_jiffies(3500)); + if (ret) + return ret; + + ret = rt9467_get_value_from_ranges(data, F_IAICR, RT9467_RANGE_IAICR, &aicr_get); + if (ret) { + dev_err(data->dev, "Failed to get aicr\n"); + return ret; + } + + dev_info(data->dev, "aicr get = %d uA\n", aicr_get); + return 0; +} + +static int rt9467_psy_set_ieoc(struct rt9467_chg_data *data, int microamp) +{ + int ret; + + mutex_lock(&data->ichg_ieoc_lock); + + ret = rt9467_set_value_from_ranges(data, F_IEOC, RT9467_RANGE_IEOC, microamp); + if (ret) + goto out; + + ret = rt9467_get_value_from_ranges(data, F_IEOC, RT9467_RANGE_IEOC, &data->ieoc_ua); + if (ret) + goto out; + +out: + mutex_unlock(&data->ichg_ieoc_lock); + return ret; +} + +static const enum power_supply_usb_type rt9467_chg_usb_types[] = { + POWER_SUPPLY_USB_TYPE_UNKNOWN, + POWER_SUPPLY_USB_TYPE_SDP, + POWER_SUPPLY_USB_TYPE_DCP, + POWER_SUPPLY_USB_TYPE_CDP, +}; + +static const enum power_supply_property rt9467_chg_properties[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_CURRENT_MAX, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT, + POWER_SUPPLY_PROP_USB_TYPE, + POWER_SUPPLY_PROP_PRECHARGE_CURRENT, + POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, +}; + +static int rt9467_psy_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct rt9467_chg_data *data = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + return rt9467_psy_get_status(data, &val->intval); + case POWER_SUPPLY_PROP_ONLINE: + return regmap_field_read(data->rm_field[F_PWR_RDY], &val->intval); + case POWER_SUPPLY_PROP_CURRENT_MAX: + mutex_lock(&data->attach_lock); + if (data->psy_usb_type == POWER_SUPPLY_USB_TYPE_UNKNOWN || + data->psy_usb_type == POWER_SUPPLY_USB_TYPE_SDP) + val->intval = 500000; + else + val->intval = 1500000; + mutex_unlock(&data->attach_lock); + return 0; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + mutex_lock(&data->ichg_ieoc_lock); + val->intval = data->ichg_ua; + mutex_unlock(&data->ichg_ieoc_lock); + return 0; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + val->intval = RT9467_ICHG_MAX_uA; + return 0; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + return rt9467_get_value_from_ranges(data, F_VOREG, + RT9467_RANGE_VOREG, + &val->intval); + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: + val->intval = RT9467_CV_MAX_uV; + return 0; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + return rt9467_get_value_from_ranges(data, F_IAICR, + RT9467_RANGE_IAICR, + &val->intval); + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + return rt9467_get_value_from_ranges(data, F_VMIVR, + RT9467_RANGE_VMIVR, + &val->intval); + case POWER_SUPPLY_PROP_USB_TYPE: + mutex_lock(&data->attach_lock); + val->intval = data->psy_usb_type; + mutex_unlock(&data->attach_lock); + return 0; + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: + return rt9467_get_value_from_ranges(data, F_IPREC, + RT9467_RANGE_IPREC, + &val->intval); + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: + mutex_lock(&data->ichg_ieoc_lock); + val->intval = data->ieoc_ua; + mutex_unlock(&data->ichg_ieoc_lock); + return 0; + default: + return -ENODATA; + } +} + +static int rt9467_psy_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct rt9467_chg_data *data = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + return regmap_field_write(data->rm_field[F_CHG_EN], val->intval); + case POWER_SUPPLY_PROP_ONLINE: + return regmap_field_write(data->rm_field[F_HZ], val->intval); + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + return rt9467_psy_set_ichg(data, val->intval); + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + return rt9467_set_value_from_ranges(data, F_VOREG, + RT9467_RANGE_VOREG, val->intval); + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + if (val->intval == -1) + return rt9467_run_aicl(data); + else + return rt9467_set_value_from_ranges(data, F_IAICR, + RT9467_RANGE_IAICR, + val->intval); + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + return rt9467_set_value_from_ranges(data, F_VMIVR, + RT9467_RANGE_VMIVR, val->intval); + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: + return rt9467_set_value_from_ranges(data, F_IPREC, + RT9467_RANGE_IPREC, val->intval); + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: + return rt9467_psy_set_ieoc(data, val->intval); + case POWER_SUPPLY_PROP_USB_TYPE: + return regmap_field_write(data->rm_field[F_USBCHGEN], val->intval); + default: + return -EINVAL; + } +} + +static int rt9467_chg_prop_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + case POWER_SUPPLY_PROP_ONLINE: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: + case POWER_SUPPLY_PROP_USB_TYPE: + return 1; + default: + return 0; + } +} + +static const struct power_supply_desc rt9467_chg_psy_desc = { + .name = "rt9467-charger", + .type = POWER_SUPPLY_TYPE_USB, + .usb_types = rt9467_chg_usb_types, + .num_usb_types = ARRAY_SIZE(rt9467_chg_usb_types), + .properties = rt9467_chg_properties, + .num_properties = ARRAY_SIZE(rt9467_chg_properties), + .property_is_writeable = rt9467_chg_prop_is_writeable, + .get_property = rt9467_psy_get_property, + .set_property = rt9467_psy_set_property, +}; + +static inline struct rt9467_chg_data *psy_device_to_chip(struct device *dev) +{ + return power_supply_get_drvdata(to_power_supply(dev)); +} + +static ssize_t sysoff_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rt9467_chg_data *data = psy_device_to_chip(dev); + unsigned int sysoff_enable; + int ret; + + ret = regmap_field_read(data->rm_field[F_SHIP_MODE], &sysoff_enable); + if (ret) + return ret; + + return sysfs_emit(buf, "%d\n", sysoff_enable); +} + +static ssize_t sysoff_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rt9467_chg_data *data = psy_device_to_chip(dev); + unsigned int tmp; + int ret; + + ret = kstrtouint(buf, 10, &tmp); + if (ret) + return ret; + + ret = regmap_field_write(data->rm_field[F_SHIP_MODE], !!tmp); + if (ret) + return ret; + + return count; +} + +static DEVICE_ATTR_RW(sysoff_enable); + +static struct attribute *rt9467_sysfs_attrs[] = { + &dev_attr_sysoff_enable.attr, + NULL +}; + +ATTRIBUTE_GROUPS(rt9467_sysfs); + +static int rt9467_register_psy(struct rt9467_chg_data *data) +{ + struct power_supply_config cfg = { + .drv_data = data, + .of_node = dev_of_node(data->dev), + .attr_grp = rt9467_sysfs_groups, + }; + + data->psy = devm_power_supply_register(data->dev, &rt9467_chg_psy_desc, + &cfg); + return PTR_ERR_OR_ZERO(data->psy); +} + +static int rt9467_mivr_handler(struct rt9467_chg_data *data) +{ + unsigned int mivr_act; + int ret, ibus_ma; + + /* + * back-boost workaround + * If (mivr_active & ibus < 100mA), toggle cfo bit + */ + ret = regmap_field_read(data->rm_field[F_CHG_MIVR], &mivr_act); + if (ret) { + dev_err(data->dev, "Failed to read MIVR stat\n"); + return ret; + } + + if (!mivr_act) + return 0; + + ret = rt9467_get_adc(data, RT9467_ADC_IBUS, &ibus_ma); + if (ret) { + dev_err(data->dev, "Failed to get IBUS\n"); + return ret; + } + + if (ibus_ma < 100000) { + ret = regmap_field_write(data->rm_field[F_CFO_EN], 0); + ret |= regmap_field_write(data->rm_field[F_CFO_EN], 1); + if (ret) + dev_err(data->dev, "Failed to toggle cfo\n"); + } + + return ret; +} + +static irqreturn_t rt9467_statc_handler(int irq, void *priv) +{ + struct rt9467_chg_data *data = priv; + unsigned int new_stat, evts = 0; + int ret; + + ret = regmap_read(data->regmap, RT9467_REG_CHG_STATC, &new_stat); + if (ret) { + dev_err(data->dev, "Failed to read chg_statc\n"); + return IRQ_NONE; + } + + evts = data->old_stat ^ new_stat; + data->old_stat = new_stat; + + if ((evts & new_stat) & RT9467_MASK_MIVR_STAT) { + ret = rt9467_mivr_handler(data); + if (ret) + dev_err(data->dev, "Failed to handle mivr stat\n"); + } + + return IRQ_HANDLED; +} + +static irqreturn_t rt9467_wdt_handler(int irq, void *priv) +{ + struct rt9467_chg_data *data = priv; + unsigned int dev_id; + int ret; + + /* Any i2c communication can kick watchdog timer */ + ret = regmap_read(data->regmap, RT9467_REG_DEVICE_ID, &dev_id); + if (ret) { + dev_err(data->dev, "Failed to kick wdt (%d)\n", ret); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static int rt9467_report_usb_state(struct rt9467_chg_data *data) +{ + unsigned int usb_stat, power_ready; + bool psy_changed = true; + int ret; + + ret = regmap_field_read(data->rm_field[F_USB_STATUS], &usb_stat); + ret |= regmap_field_read(data->rm_field[F_PWR_RDY], &power_ready); + if (ret) + return ret; + + if (!power_ready) + usb_stat = RT9467_CHG_TYPE_NOVBUS; + + mutex_lock(&data->attach_lock); + + switch (usb_stat) { + case RT9467_CHG_TYPE_NOVBUS: + data->psy_usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN; + break; + case RT9467_CHG_TYPE_SDP: + data->psy_usb_type = POWER_SUPPLY_USB_TYPE_SDP; + break; + case RT9467_CHG_TYPE_SDPNSTD: + data->psy_usb_type = POWER_SUPPLY_USB_TYPE_DCP; + break; + case RT9467_CHG_TYPE_DCP: + data->psy_usb_type = POWER_SUPPLY_USB_TYPE_DCP; + break; + case RT9467_CHG_TYPE_CDP: + data->psy_usb_type = POWER_SUPPLY_USB_TYPE_CDP; + break; + case RT9467_CHG_TYPE_UNDER_GOING: + default: + psy_changed = false; + break; + } + + mutex_unlock(&data->attach_lock); + + if (psy_changed) + power_supply_changed(data->psy); + + return 0; +} + +static irqreturn_t rt9467_usb_state_handler(int irq, void *priv) +{ + struct rt9467_chg_data *data = priv; + int ret; + + ret = rt9467_report_usb_state(data); + if (ret) { + dev_err(data->dev, "Failed to report attache type (%d)\n", ret); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static irqreturn_t rt9467_aiclmeas_handler(int irq, void *priv) +{ + struct rt9467_chg_data *data = priv; + + complete(&data->aicl_done); + return IRQ_HANDLED; +} + +#define RT9467_IRQ_DESC(_name, _handler_func, _hwirq) \ +{ \ + .name = #_name, \ + .handler = rt9467_##_handler_func##_handler, \ + .hwirq = _hwirq, \ +} + +static int rt9467_request_interrupt(struct rt9467_chg_data *data) +{ + struct device *dev = data->dev; + static const struct { + const char *name; + int hwirq; + irq_handler_t handler; + } rt9467_exclusive_irqs[] = { + RT9467_IRQ_DESC(statc, statc, RT9467_IRQ_TS_STATC), + RT9467_IRQ_DESC(wdt, wdt, RT9467_IRQ_WDTMR), + RT9467_IRQ_DESC(attach, usb_state, RT9467_IRQ_ATTACH), + RT9467_IRQ_DESC(detach, usb_state, RT9467_IRQ_DETACH), + RT9467_IRQ_DESC(aiclmeas, aiclmeas, RT9467_IRQ_CHG_AICLM), + }, rt9466_exclusive_irqs[] = { + RT9467_IRQ_DESC(statc, statc, RT9467_IRQ_TS_STATC), + RT9467_IRQ_DESC(wdt, wdt, RT9467_IRQ_WDTMR), + RT9467_IRQ_DESC(aiclmeas, aiclmeas, RT9467_IRQ_CHG_AICLM), + }, *chg_irqs; + int num_chg_irqs, i, virq, ret; + + if (data->vid == RT9466_VID) { + chg_irqs = rt9466_exclusive_irqs; + num_chg_irqs = ARRAY_SIZE(rt9466_exclusive_irqs); + } else { + chg_irqs = rt9467_exclusive_irqs; + num_chg_irqs = ARRAY_SIZE(rt9467_exclusive_irqs); + } + + for (i = 0; i < num_chg_irqs; i++) { + virq = regmap_irq_get_virq(data->irq_chip_data, chg_irqs[i].hwirq); + if (virq <= 0) + return dev_err_probe(dev, virq, "Failed to get (%s) irq\n", + chg_irqs[i].name); + + ret = devm_request_threaded_irq(dev, virq, NULL, chg_irqs[i].handler, + IRQF_ONESHOT, chg_irqs[i].name, data); + if (ret) + return dev_err_probe(dev, ret, "Failed to request (%s) irq\n", + chg_irqs[i].name); + } + + return 0; +} + +static int rt9467_do_charger_init(struct rt9467_chg_data *data) +{ + struct device *dev = data->dev; + int ret; + + ret = regmap_write(data->regmap, RT9467_REG_CHG_ADC, 0); + if (ret) + return dev_err_probe(dev, ret, "Failed to reset ADC\n"); + + ret = rt9467_get_value_from_ranges(data, F_ICHG, RT9467_RANGE_ICHG, + &data->ichg_ua); + ret |= rt9467_get_value_from_ranges(data, F_IEOC, RT9467_RANGE_IEOC, + &data->ieoc_ua); + if (ret) + return dev_err_probe(dev, ret, "Failed to init ichg/ieoc value\n"); + + ret = regmap_update_bits(data->regmap, RT9467_REG_CHG_STATC_CTRL, + RT9467_MASK_PWR_RDY | RT9467_MASK_MIVR_STAT, 0); + if (ret) + return dev_err_probe(dev, ret, "Failed to make statc unmask\n"); + + /* Select IINLMTSEL to use AICR */ + ret = regmap_field_write(data->rm_field[F_IINLMTSEL], + RT9467_IINLMTSEL_AICR); + if (ret) + return dev_err_probe(dev, ret, "Failed to set iinlmtsel to AICR\n"); + + /* Wait for AICR Rampping */ + msleep(150); + + /* Disable hardware ILIM */ + ret = regmap_field_write(data->rm_field[F_ILIM_EN], 0); + if (ret) + return dev_err_probe(dev, ret, "Failed to disable hardware ILIM\n"); + + /* Set inductor OCP to high level */ + ret = regmap_field_write(data->rm_field[F_OCP], 1); + if (ret) + return dev_err_probe(dev, ret, "Failed to set higher inductor OCP level\n"); + + /* Set charge termination default enable */ + ret = regmap_field_write(data->rm_field[F_TE], 1); + if (ret) + return dev_err_probe(dev, ret, "Failed to set TE=1\n"); + + /* Set 12hrs fast charger timer */ + ret = regmap_field_write(data->rm_field[F_WT_FC], 4); + if (ret) + return dev_err_probe(dev, ret, "Failed to set WT_FC\n"); + + /* Toggle BC12 function */ + ret = regmap_field_write(data->rm_field[F_USBCHGEN], 0); + if (ret) + return dev_err_probe(dev, ret, "Failed to disable BC12\n"); + + return regmap_field_write(data->rm_field[F_USBCHGEN], 1); +} + +static bool rt9467_is_accessible_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x00 ... 0x1A: + case 0x20 ... 0x38: + case 0x40 ... 0x49: + case 0x50 ... 0x57: + case 0x60 ... 0x67: + case 0x70 ... 0x79: + case 0x82 ... 0x85: + return true; + default: + return false; + } +} + +static const struct regmap_config rt9467_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x85, + .writeable_reg = rt9467_is_accessible_reg, + .readable_reg = rt9467_is_accessible_reg, +}; + +static int rt9467_check_vendor_info(struct rt9467_chg_data *data) +{ + unsigned int vid; + int ret; + + ret = regmap_field_read(data->rm_field[F_VENDOR], &vid); + if (ret) { + dev_err(data->dev, "Failed to get vid\n"); + return ret; + } + + if ((vid != RT9466_VID) && (vid != RT9467_VID)) + return dev_err_probe(data->dev, -ENODEV, + "VID not correct [0x%02X]\n", vid); + + data->vid = vid; + return 0; +} + +static int rt9467_reset_chip(struct rt9467_chg_data *data) +{ + int ret; + + /* Disable HZ before reset chip */ + ret = regmap_field_write(data->rm_field[F_HZ], 0); + if (ret) + return ret; + + return regmap_field_write(data->rm_field[F_RST], 1); +} + +static void rt9467_chg_destroy_adc_lock(void *data) +{ + struct mutex *adc_lock = data; + + mutex_destroy(adc_lock); +} + +static void rt9467_chg_destroy_attach_lock(void *data) +{ + struct mutex *attach_lock = data; + + mutex_destroy(attach_lock); +} + +static void rt9467_chg_destroy_ichg_ieoc_lock(void *data) +{ + struct mutex *ichg_ieoc_lock = data; + + mutex_destroy(ichg_ieoc_lock); +} + +static void rt9467_chg_complete_aicl_done(void *data) +{ + struct completion *aicl_done = data; + + complete(aicl_done); +} + +static int rt9467_charger_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct rt9467_chg_data *data; + struct gpio_desc *ceb_gpio; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->dev = &i2c->dev; + i2c_set_clientdata(i2c, data); + + /* Default pull charge enable gpio to make 'CHG_EN' by SW control only */ + ceb_gpio = devm_gpiod_get_optional(dev, "charge-enable", GPIOD_OUT_LOW); + if (IS_ERR(ceb_gpio)) + return dev_err_probe(dev, PTR_ERR(ceb_gpio), + "Failed to config charge enable gpio\n"); + + data->regmap = devm_regmap_init_i2c(i2c, &rt9467_regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), + "Failed to init regmap\n"); + + ret = devm_regmap_field_bulk_alloc(dev, data->regmap, + data->rm_field, rt9467_chg_fields, + ARRAY_SIZE(rt9467_chg_fields)); + if (ret) + return dev_err_probe(dev, ret, "Failed to alloc regmap fields\n"); + + ret = rt9467_check_vendor_info(data); + if (ret) + return dev_err_probe(dev, ret, "Failed to check vendor info"); + + ret = rt9467_reset_chip(data); + if (ret) + return dev_err_probe(dev, ret, "Failed to reset chip\n"); + + ret = devm_regmap_add_irq_chip(dev, data->regmap, i2c->irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, 0, + &rt9467_irq_chip, &data->irq_chip_data); + if (ret) + return dev_err_probe(dev, ret, "Failed to add irq chip\n"); + + mutex_init(&data->adc_lock); + ret = devm_add_action_or_reset(dev, rt9467_chg_destroy_adc_lock, + &data->adc_lock); + if (ret) + return dev_err_probe(dev, ret, "Failed to init ADC lock\n"); + + mutex_init(&data->attach_lock); + ret = devm_add_action_or_reset(dev, rt9467_chg_destroy_attach_lock, + &data->attach_lock); + if (ret) + return dev_err_probe(dev, ret, "Failed to init attach lock\n"); + + mutex_init(&data->ichg_ieoc_lock); + ret = devm_add_action_or_reset(dev, rt9467_chg_destroy_ichg_ieoc_lock, + &data->ichg_ieoc_lock); + if (ret) + return dev_err_probe(dev, ret, "Failed to init ICHG/IEOC lock\n"); + + init_completion(&data->aicl_done); + ret = devm_add_action_or_reset(dev, rt9467_chg_complete_aicl_done, + &data->aicl_done); + if (ret) + return dev_err_probe(dev, ret, "Failed to init AICL done completion\n"); + + ret = rt9467_do_charger_init(data); + if (ret) + return ret; + + ret = rt9467_register_otg_regulator(data); + if (ret) + return ret; + + ret = rt9467_register_psy(data); + if (ret) + return ret; + + return rt9467_request_interrupt(data); +} + +static const struct of_device_id rt9467_charger_of_match_table[] = { + { .compatible = "richtek,rt9467", }, + {} +}; +MODULE_DEVICE_TABLE(of, rt9467_charger_of_match_table); + +static struct i2c_driver rt9467_charger_driver = { + .driver = { + .name = "rt9467-charger", + .of_match_table = rt9467_charger_of_match_table, + }, + .probe_new = rt9467_charger_probe, +}; +module_i2c_driver(rt9467_charger_driver); + +MODULE_DESCRIPTION("Richtek RT9467 Charger Driver"); +MODULE_AUTHOR("ChiYuan Huang "); +MODULE_AUTHOR("ChiaEn Wu "); +MODULE_LICENSE("GPL"); -- cgit From eedb923279b78cbfe16dd54dade0d73e9977f118 Mon Sep 17 00:00:00 2001 From: ChiaEn Wu Date: Tue, 3 Jan 2023 15:29:58 +0800 Subject: Documentation: power: rt9467: Document exported sysfs entries Document the settings exported by rt9467 charger driver through sysfs entries: - sysoff_enable Signed-off-by: ChiaEn Wu [update kernel version and date] Signed-off-by: Sebastian Reichel --- Documentation/ABI/testing/sysfs-class-power-rt9467 | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-class-power-rt9467 diff --git a/Documentation/ABI/testing/sysfs-class-power-rt9467 b/Documentation/ABI/testing/sysfs-class-power-rt9467 new file mode 100644 index 000000000000..619b7c45d145 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-power-rt9467 @@ -0,0 +1,19 @@ +What: /sys/class/power_supply/rt9467-*/sysoff_enable +Date: Feb 2023 +KernelVersion: 6.3 +Contact: ChiaEn Wu +Description: + This entry allows enabling the sysoff mode of rt9467 charger + devices. + If enabled and the input is removed, the internal battery FET + is turned off to reduce the leakage from the BAT pin. See + device datasheet for details. It's commonly used when the + product enter shipping stage. After entering shipping mode, + only 'VBUS' or 'Power key" pressed can make it leave this mode. + 'Disable' also can help to leave it, but it's more like to + abort the action before the device really enter shipping mode. + + Access: Read, Write + Valid values: + - 1: enabled + - 0: disabled -- cgit From a915dfd099b7e324de60fcbc29ef373479c5f69e Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 10 Feb 2023 22:25:28 +0100 Subject: power: supply: max77650: Make max77650_charger_disable() return void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The return value of max77650_charger_disable() is ignored by all but one caller. That one caller propagates the error code in the platform driver's remove function. The only effect of that is that the driver core emits a generic error message (but still removes the device). As max77650_charger_disable() already emits an error message, this can better be changed to return zero. This is a preparation for making struct platform_driver::remove return void, too. Signed-off-by: Uwe Kleine-König Reviewed-by: Bartosz Golaszewski Signed-off-by: Sebastian Reichel --- drivers/power/supply/max77650-charger.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/power/supply/max77650-charger.c b/drivers/power/supply/max77650-charger.c index d913428bedc0..e8c25da40ab2 100644 --- a/drivers/power/supply/max77650-charger.c +++ b/drivers/power/supply/max77650-charger.c @@ -141,7 +141,7 @@ static int max77650_charger_enable(struct max77650_charger_data *chg) return rv; } -static int max77650_charger_disable(struct max77650_charger_data *chg) +static void max77650_charger_disable(struct max77650_charger_data *chg) { int rv; @@ -151,8 +151,6 @@ static int max77650_charger_disable(struct max77650_charger_data *chg) MAX77650_CHARGER_DISABLED); if (rv) dev_err(chg->dev, "unable to disable the charger: %d\n", rv); - - return rv; } static irqreturn_t max77650_charger_check_status(int irq, void *data) @@ -351,7 +349,9 @@ static int max77650_charger_remove(struct platform_device *pdev) { struct max77650_charger_data *chg = platform_get_drvdata(pdev); - return max77650_charger_disable(chg); + max77650_charger_disable(chg); + + return 0; } static const struct of_device_id max77650_charger_of_match[] = { -- cgit From 1d6cdc47fec9f7935e04deb640b444e30322ca57 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Mon, 6 Feb 2023 20:20:18 +0800 Subject: power: supply: rt9471: fix using wrong ce_gpio in rt9471_probe() Pass the correct 'ce_gpio' to IS_ERR(), and remove the ce_gpio in chip data, make it all by SW control only, not to control by HW pin. Fixes: 4a1a5f6781d8 ("power: supply: rt9471: Add Richtek RT9471 charger driver") Reviewed-by: ChiYuan Huang Signed-off-by: Yang Yingliang Signed-off-by: Sebastian Reichel --- drivers/power/supply/rt9471.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/power/supply/rt9471.c b/drivers/power/supply/rt9471.c index 5d3cf375ad5c..1ea40876494b 100644 --- a/drivers/power/supply/rt9471.c +++ b/drivers/power/supply/rt9471.c @@ -141,7 +141,6 @@ enum { struct rt9471_chip { struct device *dev; - struct gpio_desc *ce_gpio; struct regmap *regmap; struct regmap_field *rm_fields[F_MAX_FIELDS]; struct regmap_irq_chip_data *irq_chip_data; @@ -851,7 +850,7 @@ static int rt9471_probe(struct i2c_client *i2c) /* Default pull charge enable gpio to make 'CHG_EN' by SW control only */ ce_gpio = devm_gpiod_get_optional(dev, "charge-enable", GPIOD_OUT_HIGH); - if (IS_ERR(chip->ce_gpio)) + if (IS_ERR(ce_gpio)) return dev_err_probe(dev, PTR_ERR(ce_gpio), "Failed to config charge enable gpio\n"); -- cgit From 469bb6093f8e963aab3b1836dc9eb86135c14163 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 6 Feb 2023 09:17:42 +0000 Subject: power: supply: rt9467: Fix spelling mistake "attache" -> "attach" There is a spelling mistake in a dev_err message. Fix it. Signed-off-by: Colin Ian King Reviewed-by: ChiaEn Wu Signed-off-by: Sebastian Reichel --- drivers/power/supply/rt9467-charger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/supply/rt9467-charger.c b/drivers/power/supply/rt9467-charger.c index 96ad0d7d3af4..73f744a3155d 100644 --- a/drivers/power/supply/rt9467-charger.c +++ b/drivers/power/supply/rt9467-charger.c @@ -970,7 +970,7 @@ static irqreturn_t rt9467_usb_state_handler(int irq, void *priv) ret = rt9467_report_usb_state(data); if (ret) { - dev_err(data->dev, "Failed to report attache type (%d)\n", ret); + dev_err(data->dev, "Failed to report attach type (%d)\n", ret); return IRQ_NONE; } -- cgit From cec3b46b8bda0cdc93d3ab2bdd14aae5d30ecfd9 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 14 Feb 2023 17:41:18 +0100 Subject: power: reset: add Odroid Go Ultra poweroff driver The Hardkernel Odroid Go Ultra poweroff scheme requires requesting a poweroff to its two PMICs in order, this represents the poweroff scheme needed to complete a clean poweroff of the system. This implement this scheme by implementing a self registering driver to permit using probe defer until both pmics are finally probed. Signed-off-by: Neil Armstrong Signed-off-by: Sebastian Reichel --- drivers/power/reset/Kconfig | 7 + drivers/power/reset/Makefile | 1 + drivers/power/reset/odroid-go-ultra-poweroff.c | 177 +++++++++++++++++++++++++ 3 files changed, 185 insertions(+) create mode 100644 drivers/power/reset/odroid-go-ultra-poweroff.c diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index a8c46ba5878f..7059bd1f2ee7 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -141,6 +141,13 @@ config POWER_RESET_OCELOT_RESET help This driver supports restart for Microsemi Ocelot SoC and similar. +config POWER_RESET_ODROID_GO_ULTRA_POWEROFF + bool "Odroid Go Ultra power-off driver" + depends on ARCH_MESON || COMPILE_TEST + depends on I2C && OF + help + This driver supports Power off for Odroid Go Ultra device. + config POWER_RESET_OXNAS bool "OXNAS SoC restart driver" depends on ARCH_OXNAS diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index 0a39424fc558..d763e6735ee3 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_POWER_RESET_MT6323) += mt6323-poweroff.o obj-$(CONFIG_POWER_RESET_OXNAS) += oxnas-restart.o obj-$(CONFIG_POWER_RESET_QCOM_PON) += qcom-pon.o obj-$(CONFIG_POWER_RESET_OCELOT_RESET) += ocelot-reset.o +obj-$(CONFIG_POWER_RESET_ODROID_GO_ULTRA_POWEROFF) += odroid-go-ultra-poweroff.o obj-$(CONFIG_POWER_RESET_PIIX4_POWEROFF) += piix4-poweroff.o obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o diff --git a/drivers/power/reset/odroid-go-ultra-poweroff.c b/drivers/power/reset/odroid-go-ultra-poweroff.c new file mode 100644 index 000000000000..f46271da4e8e --- /dev/null +++ b/drivers/power/reset/odroid-go-ultra-poweroff.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023 Neil Armstrong + */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The Odroid Go Ultra has 2 PMICs: + * - RK818 (manages the battery and USB-C power supply) + * - RK817 + * Both PMICs feeds power to the S922X SoC, so they must be powered-off in sequence. + * Vendor does power-off the RK817 first, then the RK818 so here we follow this sequence. + */ + +struct odroid_go_ultra_poweroff_data { + struct device *dev; + struct device *rk817; + struct device *rk818; +}; + +static int odroid_go_ultra_poweroff_prepare(struct sys_off_data *data) +{ + struct odroid_go_ultra_poweroff_data *poweroff_data = data->cb_data; + struct regmap *rk817, *rk818; + int ret; + + /* RK817 Regmap */ + rk817 = dev_get_regmap(poweroff_data->rk817, NULL); + if (!rk817) { + dev_err(poweroff_data->dev, "failed to get rk817 regmap\n"); + return notifier_from_errno(-EINVAL); + } + + /* RK818 Regmap */ + rk818 = dev_get_regmap(poweroff_data->rk818, NULL); + if (!rk818) { + dev_err(poweroff_data->dev, "failed to get rk818 regmap\n"); + return notifier_from_errno(-EINVAL); + } + + dev_info(poweroff_data->dev, "Setting PMICs for power off"); + + /* RK817 */ + ret = regmap_update_bits(rk817, RK817_SYS_CFG(3), DEV_OFF, DEV_OFF); + if (ret) { + dev_err(poweroff_data->dev, "failed to poweroff rk817\n"); + return notifier_from_errno(ret); + } + + /* RK818 */ + ret = regmap_update_bits(rk818, RK818_DEVCTRL_REG, DEV_OFF, DEV_OFF); + if (ret) { + dev_err(poweroff_data->dev, "failed to poweroff rk818\n"); + return notifier_from_errno(ret); + } + + return NOTIFY_OK; +} + +static void odroid_go_ultra_poweroff_put_pmic_device(void *data) +{ + struct device *dev = data; + + put_device(dev); +} + +static int odroid_go_ultra_poweroff_get_pmic_device(struct device *dev, const char *compatible, + struct device **pmic) +{ + struct device_node *pmic_node; + struct i2c_client *pmic_client; + + pmic_node = of_find_compatible_node(NULL, NULL, compatible); + if (!pmic_node) + return -ENODEV; + + pmic_client = of_find_i2c_device_by_node(pmic_node); + of_node_put(pmic_node); + if (!pmic_client) + return -EPROBE_DEFER; + + *pmic = &pmic_client->dev; + + return devm_add_action_or_reset(dev, odroid_go_ultra_poweroff_put_pmic_device, *pmic); +} + +static int odroid_go_ultra_poweroff_probe(struct platform_device *pdev) +{ + struct odroid_go_ultra_poweroff_data *poweroff_data; + int ret; + + poweroff_data = devm_kzalloc(&pdev->dev, sizeof(*poweroff_data), GFP_KERNEL); + if (!poweroff_data) + return -ENOMEM; + + dev_set_drvdata(&pdev->dev, poweroff_data); + + /* RK818 PMIC Device */ + ret = odroid_go_ultra_poweroff_get_pmic_device(&pdev->dev, "rockchip,rk818", + &poweroff_data->rk818); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failed to get rk818 mfd data\n"); + + /* RK817 PMIC Device */ + ret = odroid_go_ultra_poweroff_get_pmic_device(&pdev->dev, "rockchip,rk817", + &poweroff_data->rk817); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failed to get rk817 mfd data\n"); + + /* Register as SYS_OFF_MODE_POWER_OFF_PREPARE because regmap_update_bits may sleep */ + ret = devm_register_sys_off_handler(&pdev->dev, + SYS_OFF_MODE_POWER_OFF_PREPARE, + SYS_OFF_PRIO_DEFAULT, + odroid_go_ultra_poweroff_prepare, + poweroff_data); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failed to register sys-off handler\n"); + + dev_info(&pdev->dev, "Registered Power-Off handler\n"); + + return 0; +} +static struct platform_device *pdev; + +static struct platform_driver odroid_go_ultra_poweroff_driver = { + .driver = { + .name = "odroid-go-ultra-poweroff", + }, + .probe = odroid_go_ultra_poweroff_probe, +}; + +static int __init odroid_go_ultra_poweroff_init(void) +{ + int ret; + + /* Only create when running on the Odroid Go Ultra device */ + if (!of_device_is_compatible(of_root, "hardkernel,odroid-go-ultra")) + return -ENODEV; + + ret = platform_driver_register(&odroid_go_ultra_poweroff_driver); + if (ret) + return ret; + + pdev = platform_device_register_resndata(NULL, "odroid-go-ultra-poweroff", -1, + NULL, 0, NULL, 0); + + if (IS_ERR(pdev)) { + platform_driver_unregister(&odroid_go_ultra_poweroff_driver); + return PTR_ERR(pdev); + } + + return 0; +} + +static void __exit odroid_go_ultra_poweroff_exit(void) +{ + /* Only delete when running on the Odroid Go Ultra device */ + if (!of_device_is_compatible(of_root, "hardkernel,odroid-go-ultra")) + return; + + platform_device_unregister(pdev); + platform_driver_unregister(&odroid_go_ultra_poweroff_driver); +} + +module_init(odroid_go_ultra_poweroff_init); +module_exit(odroid_go_ultra_poweroff_exit); + +MODULE_AUTHOR("Neil Armstrong "); +MODULE_DESCRIPTION("Odroid Go Ultra poweroff driver"); +MODULE_LICENSE("GPL"); -- cgit From b8ad34ce75a2e029946d7bf6832c363ce5578e39 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Tue, 14 Feb 2023 10:08:47 +0100 Subject: dt-bindings: power: supply: pm8941-coincell: Add PM8998 compatible Add a specific compatible for the coincell charger present on PM8998. Acked-by: Krzysztof Kozlowski Signed-off-by: Konrad Dybcio Signed-off-by: Sebastian Reichel --- .../devicetree/bindings/power/supply/qcom,pm8941-coincell.yaml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/power/supply/qcom,pm8941-coincell.yaml b/Documentation/devicetree/bindings/power/supply/qcom,pm8941-coincell.yaml index 0450f4dd4e51..b7b58aed3f3c 100644 --- a/Documentation/devicetree/bindings/power/supply/qcom,pm8941-coincell.yaml +++ b/Documentation/devicetree/bindings/power/supply/qcom,pm8941-coincell.yaml @@ -16,7 +16,13 @@ maintainers: properties: compatible: - const: qcom,pm8941-coincell + oneOf: + - items: + - enum: + - qcom,pm8998-coincell + - const: qcom,pm8941-coincell + + - const: qcom,pm8941-coincell reg: maxItems: 1 -- cgit From b2b911afd86631e601537c7dd7bb0acc0b1f9b51 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Tue, 14 Feb 2023 10:08:48 +0100 Subject: dt-bindings: power: supply: pm8941-coincell: Don't require charging properties It's fine for these properties to be absent, as the driver doesn't fail without them and functions with settings inherited from the reset/previous stage bootloader state. Fixes: 6c463222a21d ("dt-bindings: power: supply: pm8941-coincell: Convert to DT schema format") Signed-off-by: Konrad Dybcio Acked-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- .../bindings/power/supply/qcom,pm8941-coincell.yaml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/power/supply/qcom,pm8941-coincell.yaml b/Documentation/devicetree/bindings/power/supply/qcom,pm8941-coincell.yaml index b7b58aed3f3c..1d2405bea109 100644 --- a/Documentation/devicetree/bindings/power/supply/qcom,pm8941-coincell.yaml +++ b/Documentation/devicetree/bindings/power/supply/qcom,pm8941-coincell.yaml @@ -28,12 +28,18 @@ properties: maxItems: 1 qcom,rset-ohms: - description: resistance (in ohms) for current-limiting resistor + description: | + Resistance (in ohms) for current-limiting resistor. If unspecified, + inherit the previous configuration (e.g. from bootloader or hardware + default value). enum: [ 800, 1200, 1700, 2100 ] qcom,vset-millivolts: $ref: /schemas/types.yaml#/definitions/uint32 - description: voltage (in millivolts) to apply for charging + description: | + Voltage (in millivolts) to apply for charging. If unspecified, inherit + the previous configuration (e.g. from bootloader or hardware default + value). enum: [ 2500, 3000, 3100, 3200 ] qcom,charger-disable: @@ -43,8 +49,6 @@ properties: required: - compatible - reg - - qcom,rset-ohms - - qcom,vset-millivolts additionalProperties: false -- cgit From 9de10a51b0c6e7c1ca99a65c043243597002202e Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Wed, 15 Feb 2023 01:12:55 +0000 Subject: power: supply: leds: explicitly include linux/leds.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of relying on an accidental, transitive inclusion of linux/leds.h use it directly. Signed-off-by: Thomas Weißschuh Signed-off-by: Sebastian Reichel --- drivers/power/supply/power_supply_leds.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/power/supply/power_supply_leds.c b/drivers/power/supply/power_supply_leds.c index d69880cc3593..702bf83f6e6d 100644 --- a/drivers/power/supply/power_supply_leds.c +++ b/drivers/power/supply/power_supply_leds.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "power_supply.h" -- cgit From c142872ea40a99258e2a86bf5c471bcc81752f56 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 15 Feb 2023 13:47:08 +0100 Subject: power: reset: odroid-go-ultra: fix I2C dependency Since this driver can only be built-in, it fails to link when the I2C layer is in a loadable module: x86_64-linux-ld: drivers/power/reset/odroid-go-ultra-poweroff.o: in function `odroid_go_ultra_poweroff_get_pmic_device': odroid-go-ultra-poweroff.c:(.text+0x30): undefined reference to `i2c_find_device_by_fwnode' Tighten the dependency to only allow enabling POWER_RESET_ODROID_GO_ULTRA_POWEROFF is I2C is built-in as well. Fixes: cec3b46b8bda ("power: reset: add Odroid Go Ultra poweroff driver") Signed-off-by: Arnd Bergmann Acked-by: Neil Armstrong Signed-off-by: Sebastian Reichel --- drivers/power/reset/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 7059bd1f2ee7..8c87eeda0fec 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -144,7 +144,7 @@ config POWER_RESET_OCELOT_RESET config POWER_RESET_ODROID_GO_ULTRA_POWEROFF bool "Odroid Go Ultra power-off driver" depends on ARCH_MESON || COMPILE_TEST - depends on I2C && OF + depends on I2C=y && OF help This driver supports Power off for Odroid Go Ultra device. -- cgit