From 87c3d579c8ed0eaea6b1567d529a8daa85a2bc6c Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Mon, 13 Apr 2020 16:10:51 +0200 Subject: power: supply: bq24257_charger: Replace depends on REGMAP_I2C with select regmap is a library function that gets selected by drivers that need it. No driver modules should depend on it. Depending on REGMAP_I2C makes this driver only build if another driver already selected REGMAP_I2C, as the symbol can't be selected through the menu kernel configuration. Fixes: 2219a935963e ("power_supply: Add TI BQ24257 charger driver") Signed-off-by: Enric Balletbo i Serra Signed-off-by: Sebastian Reichel --- drivers/power/supply/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index f3424fdce341..d37ec0d03237 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -577,7 +577,7 @@ config CHARGER_BQ24257 tristate "TI BQ24250/24251/24257 battery charger driver" depends on I2C depends on GPIOLIB || COMPILE_TEST - depends on REGMAP_I2C + select REGMAP_I2C help Say Y to enable support for the TI BQ24250, BQ24251, and BQ24257 battery chargers. -- cgit v1.2.3 From f05ec5073fdea165d13624322d6fc4d295bccc65 Mon Sep 17 00:00:00 2001 From: Jason Yan Date: Thu, 9 Apr 2020 16:54:01 +0800 Subject: power: supply: max14656: remove set but not used 'ret' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the following gcc warning: drivers/power/supply/max14656_charger_detector.c:142:6: warning: variable ‘ret’ set but not used [-Wunused-but-set-variable] int ret = 0; ^~~ Reported-by: Hulk Robot Signed-off-by: Jason Yan Signed-off-by: Sebastian Reichel --- drivers/power/supply/max14656_charger_detector.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/max14656_charger_detector.c b/drivers/power/supply/max14656_charger_detector.c index 3bbb8b4c8ae7..137f9fafce8c 100644 --- a/drivers/power/supply/max14656_charger_detector.c +++ b/drivers/power/supply/max14656_charger_detector.c @@ -139,10 +139,9 @@ static void max14656_irq_worker(struct work_struct *work) u8 buf[REG_TOTAL_NUM]; u8 chg_type; - int ret = 0; - ret = max14656_read_block_reg(chip->client, MAX14656_DEVICE_ID, - REG_TOTAL_NUM, buf); + max14656_read_block_reg(chip->client, MAX14656_DEVICE_ID, + REG_TOTAL_NUM, buf); if ((buf[MAX14656_STATUS_1] & STATUS1_VB_VALID_MASK) && (buf[MAX14656_STATUS_1] & STATUS1_CHG_TYPE_MASK)) { -- cgit v1.2.3 From 4ac54b88b6c191e400a4e5d09da3a693307365eb Mon Sep 17 00:00:00 2001 From: Rafael Gandolfi Date: Mon, 6 Apr 2020 16:05:38 +0200 Subject: power: supply: axp288_fuel_gauge: Add the Meegopad T02 to the blacklist. The Meegopad T02 is a PC in stick format and doesn't have a battery, it is reported with a random and constant battery charge but as discharging to userspace. Add it to the blacklist to avoid the bogus battery status reporting. Signed-off-by: Rafael Gandolfi Reviewed-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_fuel_gauge.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c index f40fa0e63b6e..148eb8105803 100644 --- a/drivers/power/supply/axp288_fuel_gauge.c +++ b/drivers/power/supply/axp288_fuel_gauge.c @@ -717,6 +717,12 @@ static const struct dmi_system_id axp288_fuel_gauge_blacklist[] = { DMI_MATCH(DMI_PRODUCT_NAME, "STK1A32SC"), }, }, + { + /* Meegopad T02 */ + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "MEEGOPAD T02"), + }, + }, { /* Meegopad T08 */ .matches = { -- cgit v1.2.3 From 164eaf6b435c31d98fdabe0151e979fa0b4280a0 Mon Sep 17 00:00:00 2001 From: Tang Bin Date: Mon, 6 Apr 2020 13:07:57 +0800 Subject: power: supply: 88pm860x_battery: remove redundant dev_err message In the pm860x_battery_probe(), when get irq failed, the function platform_get_irq() logs an dev_err message, so remove redundant message here. Signed-off-by: Tang Bin Signed-off-by: Sebastian Reichel --- drivers/power/supply/88pm860x_battery.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/88pm860x_battery.c b/drivers/power/supply/88pm860x_battery.c index 5ca047b3f58f..1308f3a185f3 100644 --- a/drivers/power/supply/88pm860x_battery.c +++ b/drivers/power/supply/88pm860x_battery.c @@ -919,16 +919,12 @@ static int pm860x_battery_probe(struct platform_device *pdev) return -ENOMEM; info->irq_cc = platform_get_irq(pdev, 0); - if (info->irq_cc <= 0) { - dev_err(&pdev->dev, "No IRQ resource!\n"); + if (info->irq_cc <= 0) return -EINVAL; - } info->irq_batt = platform_get_irq(pdev, 1); - if (info->irq_batt <= 0) { - dev_err(&pdev->dev, "No IRQ resource!\n"); + if (info->irq_batt <= 0) return -EINVAL; - } info->chip = chip; info->i2c = -- cgit v1.2.3 From ddd86c9534e186b4f043b7b828f984b90f9ff40a Mon Sep 17 00:00:00 2001 From: Jason Yan Date: Sun, 26 Apr 2020 17:42:50 +0800 Subject: power: supply: ab8500_fg: remove comparison to bool Fix the following coccicheck warning: drivers/power/supply/ab8500_fg.c:2402:5-24: WARNING: Comparison to bool Signed-off-by: Jason Yan Reviewed-by: Linus Walleij Signed-off-by: Sebastian Reichel --- drivers/power/supply/ab8500_fg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c index b96f90a82ecf..751c4f6c7487 100644 --- a/drivers/power/supply/ab8500_fg.c +++ b/drivers/power/supply/ab8500_fg.c @@ -2399,7 +2399,7 @@ static void ab8500_fg_reinit_work(struct work_struct *work) struct ab8500_fg *di = container_of(work, struct ab8500_fg, fg_reinit_work.work); - if (di->flags.calibrate == false) { + if (!di->flags.calibrate) { dev_dbg(di->dev, "Resetting FG state machine to init.\n"); ab8500_fg_clear_cap_samples(di); ab8500_fg_calc_cap_discharge_voltage(di, true); -- cgit v1.2.3 From f3912a5d56838f78ef2999368e4c7b132262125a Mon Sep 17 00:00:00 2001 From: Yuanjiang Yu Date: Mon, 20 Apr 2020 11:42:04 +0800 Subject: power: supply: sc27xx: Set 'no_thermal' flag for SC27xx fuel gauge There is no thermal zone should be created for the SC27XX FGU power supply, thus set the 'no_thermal' flag as true. Signed-off-by: Yuanjiang Yu Signed-off-by: Baolin Wang Signed-off-by: Sebastian Reichel --- drivers/power/supply/sc27xx_fuel_gauge.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/sc27xx_fuel_gauge.c b/drivers/power/supply/sc27xx_fuel_gauge.c index a7c8a8453db1..9dcd55f1d9f3 100644 --- a/drivers/power/supply/sc27xx_fuel_gauge.c +++ b/drivers/power/supply/sc27xx_fuel_gauge.c @@ -705,6 +705,7 @@ static const struct power_supply_desc sc27xx_fgu_desc = { .set_property = sc27xx_fgu_set_property, .external_power_changed = sc27xx_fgu_external_power_changed, .property_is_writeable = sc27xx_fgu_property_is_writeable, + .no_thermal = true, }; static void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap) -- cgit v1.2.3 From 8720b255f5280fdf89decfa9ff31720aff1c4e49 Mon Sep 17 00:00:00 2001 From: Yuanjiang Yu Date: Mon, 20 Apr 2020 11:42:05 +0800 Subject: power: supply: sc27xx: Allow to change the battery full capacity The battery full capacity can be affected by the temperature or the servicing time or other factors, so some platforms will track the real battery full capacity in charger manager service. Thus we should allow to change the battery full capacity by setting the 'POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN' property as writeable. Signed-off-by: Yuanjiang Yu Signed-off-by: Baolin Wang Signed-off-by: Sebastian Reichel --- drivers/power/supply/sc27xx_fuel_gauge.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/sc27xx_fuel_gauge.c b/drivers/power/supply/sc27xx_fuel_gauge.c index 9dcd55f1d9f3..5970d4a78016 100644 --- a/drivers/power/supply/sc27xx_fuel_gauge.c +++ b/drivers/power/supply/sc27xx_fuel_gauge.c @@ -656,6 +656,11 @@ static int sc27xx_fgu_set_property(struct power_supply *psy, ret = 0; break; + case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: + data->total_cap = val->intval / 1000; + ret = 0; + break; + default: ret = -EINVAL; } @@ -676,7 +681,8 @@ static int sc27xx_fgu_property_is_writeable(struct power_supply *psy, enum power_supply_property psp) { return psp == POWER_SUPPLY_PROP_CAPACITY || - psp == POWER_SUPPLY_PROP_CALIBRATE; + psp == POWER_SUPPLY_PROP_CALIBRATE || + psp == POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN; } static enum power_supply_property sc27xx_fgu_props[] = { -- cgit v1.2.3 From e5431c340852ad14c54f449451f884b161aae31f Mon Sep 17 00:00:00 2001 From: Yuanjiang Yu Date: Mon, 20 Apr 2020 11:42:06 +0800 Subject: power: supply: sc27xx: Add CURRENT_NOW/VOLTAGE_NOW properties support Add new properties to get present current and voltage of the fuel gauge. Signed-off-by: Yuanjiang Yu Signed-off-by: Baolin Wang Signed-off-by: Sebastian Reichel --- drivers/power/supply/sc27xx_fuel_gauge.c | 60 ++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/sc27xx_fuel_gauge.c b/drivers/power/supply/sc27xx_fuel_gauge.c index 5970d4a78016..82d0c64ef269 100644 --- a/drivers/power/supply/sc27xx_fuel_gauge.c +++ b/drivers/power/supply/sc27xx_fuel_gauge.c @@ -42,6 +42,8 @@ #define SC27XX_FGU_USER_AREA_SET 0xa0 #define SC27XX_FGU_USER_AREA_CLEAR 0xa4 #define SC27XX_FGU_USER_AREA_STATUS 0xa8 +#define SC27XX_FGU_VOLTAGE_BUF 0xd0 +#define SC27XX_FGU_CURRENT_BUF 0xf0 #define SC27XX_WRITE_SELCLB_EN BIT(0) #define SC27XX_FGU_CLBCNT_MASK GENMASK(15, 0) @@ -376,6 +378,44 @@ static int sc27xx_fgu_get_clbcnt(struct sc27xx_fgu_data *data, int *clb_cnt) return 0; } +static int sc27xx_fgu_get_vol_now(struct sc27xx_fgu_data *data, int *val) +{ + int ret; + u32 vol; + + ret = regmap_read(data->regmap, data->base + SC27XX_FGU_VOLTAGE_BUF, + &vol); + if (ret) + return ret; + + /* + * It is ADC values reading from registers which need to convert to + * corresponding voltage values. + */ + *val = sc27xx_fgu_adc_to_voltage(data, vol); + + return 0; +} + +static int sc27xx_fgu_get_cur_now(struct sc27xx_fgu_data *data, int *val) +{ + int ret; + u32 cur; + + ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CURRENT_BUF, + &cur); + if (ret) + return ret; + + /* + * It is ADC values reading from registers which need to convert to + * corresponding current values. + */ + *val = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC); + + return 0; +} + static int sc27xx_fgu_get_capacity(struct sc27xx_fgu_data *data, int *cap) { int ret, cur_clbcnt, delta_clbcnt, delta_cap, temp; @@ -577,7 +617,7 @@ static int sc27xx_fgu_get_property(struct power_supply *psy, val->intval = value; break; - case POWER_SUPPLY_PROP_VOLTAGE_NOW: + case POWER_SUPPLY_PROP_VOLTAGE_AVG: ret = sc27xx_fgu_get_vbat_vol(data, &value); if (ret) goto error; @@ -601,7 +641,6 @@ static int sc27xx_fgu_get_property(struct power_supply *psy, val->intval = value; break; - case POWER_SUPPLY_PROP_CURRENT_NOW: case POWER_SUPPLY_PROP_CURRENT_AVG: ret = sc27xx_fgu_get_current(data, &value); if (ret) @@ -625,6 +664,22 @@ static int sc27xx_fgu_get_property(struct power_supply *psy, break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = sc27xx_fgu_get_vol_now(data, &value); + if (ret) + goto error; + + val->intval = value * 1000; + break; + + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = sc27xx_fgu_get_cur_now(data, &value); + if (ret) + goto error; + + val->intval = value * 1000; + break; + default: ret = -EINVAL; break; @@ -694,6 +749,7 @@ static enum power_supply_property sc27xx_fgu_props[] = { POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_VOLTAGE_OCV, + POWER_SUPPLY_PROP_VOLTAGE_AVG, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_CURRENT_AVG, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, -- cgit v1.2.3 From 7c1c5e38fd31e89391211c0b3e5000666c7d6126 Mon Sep 17 00:00:00 2001 From: Yuanjiang Yu Date: Mon, 20 Apr 2020 11:42:07 +0800 Subject: power: supply: sc27xx: Add boot voltage support Add new property to allow to get the voltage measured during boot time. Signed-off-by: Yuanjiang Yu Signed-off-by: Baolin Wang Signed-off-by: Sebastian Reichel --- drivers/power/supply/sc27xx_fuel_gauge.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/sc27xx_fuel_gauge.c b/drivers/power/supply/sc27xx_fuel_gauge.c index 82d0c64ef269..be42e814ea34 100644 --- a/drivers/power/supply/sc27xx_fuel_gauge.c +++ b/drivers/power/supply/sc27xx_fuel_gauge.c @@ -84,6 +84,7 @@ * @init_clbcnt: the initial coulomb counter * @max_volt: the maximum constant input voltage in millivolt * @min_volt: the minimum drained battery voltage in microvolt + * @boot_volt: the voltage measured during boot in microvolt * @table_len: the capacity table length * @resist_table_len: the resistance table length * @cur_1000ma_adc: ADC value corresponding to 1000 mA @@ -109,6 +110,7 @@ struct sc27xx_fgu_data { int init_clbcnt; int max_volt; int min_volt; + int boot_volt; int table_len; int resist_table_len; int cur_1000ma_adc; @@ -321,6 +323,7 @@ static int sc27xx_fgu_get_boot_capacity(struct sc27xx_fgu_data *data, int *cap) volt = sc27xx_fgu_adc_to_voltage(data, volt); ocv = volt * 1000 - oci * data->internal_resist; + data->boot_volt = ocv; /* * Parse the capacity table to look up the correct capacity percent @@ -680,6 +683,10 @@ static int sc27xx_fgu_get_property(struct power_supply *psy, val->intval = value * 1000; break; + case POWER_SUPPLY_PROP_VOLTAGE_BOOT: + val->intval = data->boot_volt; + break; + default: ret = -EINVAL; break; @@ -750,6 +757,7 @@ static enum power_supply_property sc27xx_fgu_props[] = { POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_VOLTAGE_OCV, POWER_SUPPLY_PROP_VOLTAGE_AVG, + POWER_SUPPLY_PROP_VOLTAGE_BOOT, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_CURRENT_AVG, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, -- cgit v1.2.3 From aa86e90794c8a76c38f2897aff6c1eaa3b96bba6 Mon Sep 17 00:00:00 2001 From: Tang Bin Date: Wed, 15 Apr 2020 12:27:27 +0800 Subject: power: supply: axp288_charger: Omit superfluous error message In the axp288_charger_probe(), when get irq failed, the function platform_get_irq() logs an error message, so remove redundant message here. Signed-off-by: Tang Bin Signed-off-by: Shengju Zhang Reviewed-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_charger.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c index cf4c67b2d235..9d981b76c1e7 100644 --- a/drivers/power/supply/axp288_charger.c +++ b/drivers/power/supply/axp288_charger.c @@ -880,10 +880,9 @@ static int axp288_charger_probe(struct platform_device *pdev) /* Register charger interrupts */ for (i = 0; i < CHRG_INTR_END; i++) { pirq = platform_get_irq(info->pdev, i); - if (pirq < 0) { - dev_err(&pdev->dev, "Failed to get IRQ: %d\n", pirq); + if (pirq < 0) return pirq; - } + info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq); if (info->irq[i] < 0) { dev_warn(&info->pdev->dev, -- cgit v1.2.3 From 21b01cc879cc3ecef013b3576db2925adfd881aa Mon Sep 17 00:00:00 2001 From: Geordan Neukum Date: Mon, 30 Mar 2020 01:33:55 -0400 Subject: power: supply: max17042_battery: Add support for the TTE_NOW prop The max170{42,47,50,55} family of fuel gauges all provide time-to-empty estimation. As such, let's export this as a property. Signed-off-by: Geordan Neukum Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index 69ec4295d55d..f284547913d6 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -87,6 +87,7 @@ static enum power_supply_property max17042_battery_props[] = { POWER_SUPPLY_PROP_SCOPE, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, }; static int max17042_get_temperature(struct max17042_chip *chip, int *temp) @@ -411,6 +412,13 @@ static int max17042_get_property(struct power_supply *psy, return -EINVAL; } break; + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: + ret = regmap_read(map, MAX17042_TTE, &data); + if (ret < 0) + return ret; + + val->intval = data * 5625 / 1000; + break; default: return -EINVAL; } -- cgit v1.2.3 From de46e0289310b5e56994eafec2f0d2a5da095ea9 Mon Sep 17 00:00:00 2001 From: Michał Mirosław Date: Fri, 3 Apr 2020 22:20:32 +0200 Subject: power: supply: core: reduce power_supply_show_usb_type() parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce power_supply_show_usb_type() parameter count by folding power_supply_desc dereference into the function. This makes following patch making usb_types const easier. Signed-off-by: Michał Mirosław Signed-off-by: Sebastian Reichel --- drivers/power/supply/power_supply_sysfs.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index f37ad4eae60b..51de3f47b25d 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -78,8 +78,7 @@ static const char * const power_supply_scope_text[] = { }; static ssize_t power_supply_show_usb_type(struct device *dev, - enum power_supply_usb_type *usb_types, - ssize_t num_usb_types, + const struct power_supply_desc *desc, union power_supply_propval *value, char *buf) { @@ -88,8 +87,8 @@ static ssize_t power_supply_show_usb_type(struct device *dev, bool match = false; int i; - for (i = 0; i < num_usb_types; ++i) { - usb_type = usb_types[i]; + for (i = 0; i < desc->num_usb_types; ++i) { + usb_type = desc->usb_types[i]; if (value->intval == usb_type) { count += sprintf(buf + count, "[%s] ", @@ -163,8 +162,7 @@ static ssize_t power_supply_show_property(struct device *dev, power_supply_type_text[value.intval]); break; case POWER_SUPPLY_PROP_USB_TYPE: - ret = power_supply_show_usb_type(dev, psy->desc->usb_types, - psy->desc->num_usb_types, + ret = power_supply_show_usb_type(dev, psy->desc, &value, buf); break; case POWER_SUPPLY_PROP_SCOPE: -- cgit v1.2.3 From 4cb3825859dcc37eaf6994a32c3487d0a1fdda0b Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Mon, 13 Apr 2020 20:38:51 +0200 Subject: power: supply: charger-manager: Prepare for const properties This prepares the driver to work with the properties entry in power_supply_desc marked as const. Reviewed-by: Enric Balletbo i Serra Signed-off-by: Sebastian Reichel --- drivers/power/supply/charger-manager.c | 40 +++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 18 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index a21e1a2673f8..415a9efa6816 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -1422,7 +1422,9 @@ static int charger_manager_prepare_sysfs(struct charger_manager *cm) } static int cm_init_thermal_data(struct charger_manager *cm, - struct power_supply *fuel_gauge) + struct power_supply *fuel_gauge, + enum power_supply_property *properties, + size_t *num_properties) { struct charger_desc *desc = cm->desc; union power_supply_propval val; @@ -1433,9 +1435,8 @@ static int cm_init_thermal_data(struct charger_manager *cm, POWER_SUPPLY_PROP_TEMP, &val); if (!ret) { - cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] = - POWER_SUPPLY_PROP_TEMP; - cm->charger_psy_desc.num_properties++; + properties[*num_properties] = POWER_SUPPLY_PROP_TEMP; + (*num_properties)++; cm->desc->measure_battery_temp = true; } #ifdef CONFIG_THERMAL @@ -1446,9 +1447,8 @@ static int cm_init_thermal_data(struct charger_manager *cm, return PTR_ERR(cm->tzd_batt); /* Use external thermometer */ - cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] = - POWER_SUPPLY_PROP_TEMP_AMBIENT; - cm->charger_psy_desc.num_properties++; + properties[*num_properties] = POWER_SUPPLY_PROP_TEMP_AMBIENT; + (*num_properties)++; cm->desc->measure_battery_temp = true; ret = 0; } @@ -1621,6 +1621,8 @@ static int charger_manager_probe(struct platform_device *pdev) int j = 0; union power_supply_propval val; struct power_supply *fuel_gauge; + enum power_supply_property *properties; + size_t num_properties; struct power_supply_config psy_cfg = {}; if (IS_ERR(desc)) { @@ -1717,18 +1719,17 @@ static int charger_manager_probe(struct platform_device *pdev) cm->charger_psy_desc.name = cm->psy_name_buf; /* Allocate for psy properties because they may vary */ - cm->charger_psy_desc.properties = - devm_kcalloc(&pdev->dev, + properties = devm_kcalloc(&pdev->dev, ARRAY_SIZE(default_charger_props) + NUM_CHARGER_PSY_OPTIONAL, - sizeof(enum power_supply_property), GFP_KERNEL); - if (!cm->charger_psy_desc.properties) + sizeof(*properties), GFP_KERNEL); + if (!properties) return -ENOMEM; - memcpy(cm->charger_psy_desc.properties, default_charger_props, + memcpy(properties, default_charger_props, sizeof(enum power_supply_property) * ARRAY_SIZE(default_charger_props)); - cm->charger_psy_desc.num_properties = psy_default.num_properties; + num_properties = psy_default.num_properties; /* Find which optional psy-properties are available */ fuel_gauge = power_supply_get_by_name(desc->psy_fuel_gauge); @@ -1739,25 +1740,28 @@ static int charger_manager_probe(struct platform_device *pdev) } if (!power_supply_get_property(fuel_gauge, POWER_SUPPLY_PROP_CHARGE_NOW, &val)) { - cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] = + properties[num_properties] = POWER_SUPPLY_PROP_CHARGE_NOW; - cm->charger_psy_desc.num_properties++; + num_properties++; } if (!power_supply_get_property(fuel_gauge, POWER_SUPPLY_PROP_CURRENT_NOW, &val)) { - cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] = + properties[num_properties] = POWER_SUPPLY_PROP_CURRENT_NOW; - cm->charger_psy_desc.num_properties++; + num_properties++; } - ret = cm_init_thermal_data(cm, fuel_gauge); + ret = cm_init_thermal_data(cm, fuel_gauge, properties, &num_properties); if (ret) { dev_err(&pdev->dev, "Failed to initialize thermal data\n"); cm->desc->measure_battery_temp = false; } power_supply_put(fuel_gauge); + cm->charger_psy_desc.properties = properties; + cm->charger_psy_desc.num_properties = num_properties; + INIT_DELAYED_WORK(&cm->fullbatt_vchk_work, fullbatt_vchk); /* Register sysfs entry for charger(regulator) */ -- cgit v1.2.3 From 191e6bcf50dd54786c3560420b815bce5b523a61 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Mon, 13 Apr 2020 20:38:52 +0200 Subject: power: supply: generic-adc-battery: Prepare for const properties This prepares the driver to work with the properties entry in power_supply_desc marked as const. Reviewed-by: Enric Balletbo i Serra Signed-off-by: Sebastian Reichel --- drivers/power/supply/generic-adc-battery.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/generic-adc-battery.c b/drivers/power/supply/generic-adc-battery.c index bc462d1ec963..caa829738ef7 100644 --- a/drivers/power/supply/generic-adc-battery.c +++ b/drivers/power/supply/generic-adc-battery.c @@ -241,6 +241,7 @@ static int gab_probe(struct platform_device *pdev) struct power_supply_desc *psy_desc; struct power_supply_config psy_cfg = {}; struct gab_platform_data *pdata = pdev->dev.platform_data; + enum power_supply_property *properties; int ret = 0; int chan; int index = ARRAY_SIZE(gab_props); @@ -268,16 +269,16 @@ static int gab_probe(struct platform_device *pdev) * copying the static properties and allocating extra memory for holding * the extra configurable properties received from platform data. */ - psy_desc->properties = kcalloc(ARRAY_SIZE(gab_props) + - ARRAY_SIZE(gab_chan_name), - sizeof(*psy_desc->properties), - GFP_KERNEL); - if (!psy_desc->properties) { + properties = kcalloc(ARRAY_SIZE(gab_props) + + ARRAY_SIZE(gab_chan_name), + sizeof(*properties), + GFP_KERNEL); + if (!properties) { ret = -ENOMEM; goto first_mem_fail; } - memcpy(psy_desc->properties, gab_props, sizeof(gab_props)); + memcpy(properties, gab_props, sizeof(gab_props)); /* * getting channel from iio and copying the battery properties @@ -294,13 +295,11 @@ static int gab_probe(struct platform_device *pdev) int index2; for (index2 = 0; index2 < index; index2++) { - if (psy_desc->properties[index2] == - gab_dyn_props[chan]) + if (properties[index2] == gab_dyn_props[chan]) break; /* already known */ } if (index2 == index) /* really new */ - psy_desc->properties[index++] = - gab_dyn_props[chan]; + properties[index++] = gab_dyn_props[chan]; any = true; } } @@ -317,6 +316,7 @@ static int gab_probe(struct platform_device *pdev) * as come channels may be not be supported by the device.So * we need to take care of that. */ + psy_desc->properties = properties; psy_desc->num_properties = index; adc_bat->psy = power_supply_register(&pdev->dev, psy_desc, &psy_cfg); @@ -358,7 +358,7 @@ err_reg_fail: iio_channel_release(adc_bat->channel[chan]); } second_mem_fail: - kfree(psy_desc->properties); + kfree(properties); first_mem_fail: return ret; } -- cgit v1.2.3 From 6b20464ad9fb5fd76ef6f219ce62156aa9639dcc Mon Sep 17 00:00:00 2001 From: Michał Mirosław Date: Fri, 3 Apr 2020 22:20:33 +0200 Subject: power: supply: core: fix HWMON temperature labels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tempX_label files are swapped compared to what power_supply_hwmon_temp_to_property() uses. Make them match. Cc: stable@vger.kernel.org Fixes: e67d4dfc9ff1 ("power: supply: Add HWMON compatibility layer") Signed-off-by: Michał Mirosław Signed-off-by: Sebastian Reichel --- drivers/power/supply/power_supply_hwmon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/power_supply_hwmon.c b/drivers/power/supply/power_supply_hwmon.c index 75cf861ba492..67b6ee60085e 100644 --- a/drivers/power/supply/power_supply_hwmon.c +++ b/drivers/power/supply/power_supply_hwmon.c @@ -144,7 +144,7 @@ static int power_supply_hwmon_read_string(struct device *dev, u32 attr, int channel, const char **str) { - *str = channel ? "temp" : "temp ambient"; + *str = channel ? "temp ambient" : "temp"; return 0; } -- cgit v1.2.3 From b0e4aa97ea4b58881b85697a6f5628d1839d8e59 Mon Sep 17 00:00:00 2001 From: Michał Mirosław Date: Fri, 3 Apr 2020 22:20:34 +0200 Subject: power: supply: core: hide unused HWMON labels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently HWMON emulation shows all labels (temp and ambient temp) regardless if power supply supports reading the values. Check that at least one property is enabled for each label. Signed-off-by: Michał Mirosław Signed-off-by: Sebastian Reichel --- drivers/power/supply/power_supply_hwmon.c | 42 ++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/power_supply_hwmon.c b/drivers/power/supply/power_supply_hwmon.c index 67b6ee60085e..af72e5693f65 100644 --- a/drivers/power/supply/power_supply_hwmon.c +++ b/drivers/power/supply/power_supply_hwmon.c @@ -98,6 +98,39 @@ static bool power_supply_hwmon_is_a_label(enum hwmon_sensor_types type, return type == hwmon_temp && attr == hwmon_temp_label; } +struct hwmon_type_attr_list { + const u32 *attrs; + size_t n_attrs; +}; + +static const u32 ps_temp_attrs[] = { + hwmon_temp_input, + hwmon_temp_min, hwmon_temp_max, + hwmon_temp_min_alarm, hwmon_temp_max_alarm, +}; + +static const struct hwmon_type_attr_list ps_type_attrs[hwmon_max] = { + [hwmon_temp] = { ps_temp_attrs, ARRAY_SIZE(ps_temp_attrs) }, +}; + +static bool power_supply_hwmon_has_input( + const struct power_supply_hwmon *psyhw, + enum hwmon_sensor_types type, int channel) +{ + const struct hwmon_type_attr_list *attr_list = &ps_type_attrs[type]; + size_t i; + + for (i = 0; i < attr_list->n_attrs; ++i) { + int prop = power_supply_hwmon_to_property(type, + attr_list->attrs[i], channel); + + if (prop >= 0 && test_bit(prop, psyhw->props)) + return true; + } + + return false; +} + static bool power_supply_hwmon_is_writable(enum hwmon_sensor_types type, u32 attr) { @@ -124,9 +157,12 @@ static umode_t power_supply_hwmon_is_visible(const void *data, const struct power_supply_hwmon *psyhw = data; int prop; - - if (power_supply_hwmon_is_a_label(type, attr)) - return 0444; + if (power_supply_hwmon_is_a_label(type, attr)) { + if (power_supply_hwmon_has_input(psyhw, type, channel)) + return 0444; + else + return 0; + } prop = power_supply_hwmon_to_property(type, attr, channel); if (prop < 0 || !test_bit(prop, psyhw->props)) -- cgit v1.2.3 From 97ed79f4931964069fc3be7ae5122c3026273f9e Mon Sep 17 00:00:00 2001 From: Michał Mirosław Date: Fri, 1 May 2020 16:30:43 +0200 Subject: power: charger-manager: clarify num_properties starting value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Initialize num_properties with length of the copied array instead of relying on previously memcpy'd value. This makes it clear how the array and the counter are related. Signed-off-by: Michał Mirosław Signed-off-by: Sebastian Reichel --- drivers/power/supply/charger-manager.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index 415a9efa6816..2ef53dc1f2fb 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -1729,7 +1729,7 @@ static int charger_manager_probe(struct platform_device *pdev) memcpy(properties, default_charger_props, sizeof(enum power_supply_property) * ARRAY_SIZE(default_charger_props)); - num_properties = psy_default.num_properties; + num_properties = ARRAY_SIZE(default_charger_props); /* Find which optional psy-properties are available */ fuel_gauge = power_supply_get_by_name(desc->psy_fuel_gauge); -- cgit v1.2.3 From e83a2e443468664af5421ef5a3499980a4493248 Mon Sep 17 00:00:00 2001 From: Michał Mirosław Date: Fri, 1 May 2020 17:11:18 +0200 Subject: power: supply: core: tabularize HWMON temperature labels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rework power_supply_hwmon_read_string() to check it's parameters. This allows to extend it later with labels for other types of measurements. Signed-off-by: Michał Mirosław Signed-off-by: Sebastian Reichel --- drivers/power/supply/power_supply_hwmon.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/power_supply_hwmon.c b/drivers/power/supply/power_supply_hwmon.c index af72e5693f65..f5d538485aaa 100644 --- a/drivers/power/supply/power_supply_hwmon.c +++ b/drivers/power/supply/power_supply_hwmon.c @@ -13,6 +13,11 @@ struct power_supply_hwmon { unsigned long *props; }; +static const char *const ps_temp_label[] = { + "temp", + "ambient temp", +}; + static int power_supply_hwmon_in_to_property(u32 attr) { switch (attr) { @@ -180,7 +185,20 @@ static int power_supply_hwmon_read_string(struct device *dev, u32 attr, int channel, const char **str) { - *str = channel ? "temp ambient" : "temp"; + switch (type) { + case hwmon_temp: + *str = ps_temp_label[channel]; + break; + default: + /* unreachable, but see: + * gcc bug #51513 [1] and clang bug #978 [2] + * + * [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51513 + * [2] https://github.com/ClangBuiltLinux/linux/issues/978 + */ + break; + } + return 0; } -- cgit v1.2.3 From 1d7a7128a2e9e1f137c99b0a44e94d70a77343e3 Mon Sep 17 00:00:00 2001 From: Qiushi Wu Date: Sat, 2 May 2020 18:33:38 -0500 Subject: power: supply: core: fix memory leak in HWMON error path In function power_supply_add_hwmon_sysfs(), psyhw->props is allocated by bitmap_zalloc(). But this pointer is not deallocated when devm_add_action fail, which lead to a memory leak bug. To fix this, we replace devm_add_action with devm_add_action_or_reset. Cc: stable@kernel.org Fixes: e67d4dfc9ff19 ("power: supply: Add HWMON compatibility layer") Signed-off-by: Qiushi Wu Signed-off-by: Sebastian Reichel --- drivers/power/supply/power_supply_hwmon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/power_supply_hwmon.c b/drivers/power/supply/power_supply_hwmon.c index f5d538485aaa..7fe4b6b6ddc8 100644 --- a/drivers/power/supply/power_supply_hwmon.c +++ b/drivers/power/supply/power_supply_hwmon.c @@ -358,7 +358,7 @@ int power_supply_add_hwmon_sysfs(struct power_supply *psy) goto error; } - ret = devm_add_action(dev, power_supply_hwmon_bitmap_free, + ret = devm_add_action_or_reset(dev, power_supply_hwmon_bitmap_free, psyhw->props); if (ret) goto error; -- cgit v1.2.3 From b4c7715c10c106a041b0b3fabd26151c214ea394 Mon Sep 17 00:00:00 2001 From: Tobias Schramm Date: Tue, 14 Apr 2020 14:52:08 +0200 Subject: power: supply: add CellWise cw2015 fuel gauge driver This patch adds a driver for the CellWise cw2015 fuel gauge. The CellWise cw2015 is a shuntless, single-cell Li-Ion fuel gauge used in the pine64 Pinebook Pro laptop and some Raspberry Pi UPS HATs. Signed-off-by: Tobias Schramm Reviewed-by: Andy Shevchenko Signed-off-by: Sebastian Reichel --- MAINTAINERS | 6 + drivers/power/supply/Kconfig | 11 + drivers/power/supply/Makefile | 1 + drivers/power/supply/cw2015_battery.c | 749 ++++++++++++++++++++++++++++++++++ 4 files changed, 767 insertions(+) create mode 100644 drivers/power/supply/cw2015_battery.c (limited to 'drivers/power/supply') diff --git a/MAINTAINERS b/MAINTAINERS index 9821691396f8..2774b8ba0eb5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3932,6 +3932,12 @@ F: arch/powerpc/include/uapi/asm/spu*.h F: arch/powerpc/oprofile/*cell* F: arch/powerpc/platforms/cell/ +CELLWISE CW2015 BATTERY DRIVER +M: Tobias Schrammm +S: Maintained +F: Documentation/devicetree/bindings/power/supply/cw2015_battery.yaml +F: drivers/power/supply/cw2015_battery.c + CEPH COMMON CODE (LIBCEPH) M: Ilya Dryomov M: Jeff Layton diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index d37ec0d03237..efdcf49f27bf 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -116,6 +116,17 @@ config BATTERY_CPCAP Say Y here to enable support for battery on Motorola phones and tablets such as droid 4. +config BATTERY_CW2015 + tristate "CW2015 Battery driver" + depends on I2C + select REGMAP_I2C + help + Say Y here to enable support for the cellwise cw2015 + battery fuel gauge (used in the Pinebook Pro & others) + + This driver can also be built as a module. If so, the module will be + called cw2015_battery. + config BATTERY_DS2760 tristate "DS2760 battery driver (HP iPAQ & others)" depends on W1 diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index 6c7da920ea83..69727a10e835 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_BATTERY_ACT8945A) += act8945a_charger.o obj-$(CONFIG_BATTERY_AXP20X) += axp20x_battery.o obj-$(CONFIG_CHARGER_AXP20X) += axp20x_ac_power.o obj-$(CONFIG_BATTERY_CPCAP) += cpcap-battery.o +obj-$(CONFIG_BATTERY_CW2015) += cw2015_battery.o obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o diff --git a/drivers/power/supply/cw2015_battery.c b/drivers/power/supply/cw2015_battery.c new file mode 100644 index 000000000000..8603da6f4593 --- /dev/null +++ b/drivers/power/supply/cw2015_battery.c @@ -0,0 +1,749 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Fuel gauge driver for CellWise 2013 / 2015 + * + * Copyright (C) 2012, RockChip + * Copyright (C) 2020, Tobias Schramm + * + * Authors: xuhuicong + * Authors: Tobias Schramm + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CW2015_SIZE_BATINFO 64 + +#define CW2015_RESET_TRIES 5 + +#define CW2015_REG_VERSION 0x00 +#define CW2015_REG_VCELL 0x02 +#define CW2015_REG_SOC 0x04 +#define CW2015_REG_RRT_ALERT 0x06 +#define CW2015_REG_CONFIG 0x08 +#define CW2015_REG_MODE 0x0A +#define CW2015_REG_BATINFO 0x10 + +#define CW2015_MODE_SLEEP_MASK GENMASK(7, 6) +#define CW2015_MODE_SLEEP (0x03 << 6) +#define CW2015_MODE_NORMAL (0x00 << 6) +#define CW2015_MODE_QUICK_START (0x03 << 4) +#define CW2015_MODE_RESTART (0x0f << 0) + +#define CW2015_CONFIG_UPDATE_FLG (0x01 << 1) +#define CW2015_ATHD(x) ((x) << 3) +#define CW2015_MASK_ATHD GENMASK(7, 3) +#define CW2015_MASK_SOC GENMASK(12, 0) + +/* reset gauge of no valid state of charge could be polled for 40s */ +#define CW2015_BAT_SOC_ERROR_MS (40 * MSEC_PER_SEC) +/* reset gauge if state of charge stuck for half an hour during charging */ +#define CW2015_BAT_CHARGING_STUCK_MS (1800 * MSEC_PER_SEC) + +/* poll interval from CellWise GPL Android driver example */ +#define CW2015_DEFAULT_POLL_INTERVAL_MS 8000 + +#define CW2015_AVERAGING_SAMPLES 3 + +struct cw_battery { + struct device *dev; + struct workqueue_struct *battery_workqueue; + struct delayed_work battery_delay_work; + struct regmap *regmap; + struct power_supply *rk_bat; + struct power_supply_battery_info battery; + u8 *bat_profile; + + bool charger_attached; + bool battery_changed; + + int soc; + int voltage_mv; + int status; + int time_to_empty; + int charge_count; + + u32 poll_interval_ms; + u8 alert_level; + + unsigned int read_errors; + unsigned int charge_stuck_cnt; +}; + +static int cw_read_word(struct cw_battery *cw_bat, u8 reg, u16 *val) +{ + __be16 value; + int ret; + + ret = regmap_bulk_read(cw_bat->regmap, reg, &value, sizeof(value)); + if (ret) + return ret; + + *val = be16_to_cpu(value); + return 0; +} + +int cw_update_profile(struct cw_battery *cw_bat) +{ + int ret; + unsigned int reg_val; + u8 reset_val; + + /* make sure gauge is not in sleep mode */ + ret = regmap_read(cw_bat->regmap, CW2015_REG_MODE, ®_val); + if (ret) + return ret; + + reset_val = reg_val; + if ((reg_val & CW2015_MODE_SLEEP_MASK) == CW2015_MODE_SLEEP) { + dev_err(cw_bat->dev, + "Gauge is in sleep mode, can't update battery info\n"); + return -EINVAL; + } + + /* write new battery info */ + ret = regmap_raw_write(cw_bat->regmap, CW2015_REG_BATINFO, + cw_bat->bat_profile, + CW2015_SIZE_BATINFO); + if (ret) + return ret; + + /* set config update flag */ + reg_val |= CW2015_CONFIG_UPDATE_FLG; + reg_val &= ~CW2015_MASK_ATHD; + reg_val |= CW2015_ATHD(cw_bat->alert_level); + ret = regmap_write(cw_bat->regmap, CW2015_REG_CONFIG, reg_val); + if (ret) + return ret; + + /* reset gauge to apply new battery profile */ + reset_val &= ~CW2015_MODE_RESTART; + reg_val = reset_val | CW2015_MODE_RESTART; + ret = regmap_write(cw_bat->regmap, CW2015_REG_MODE, reg_val); + if (ret) + return ret; + + /* wait for gauge to reset */ + msleep(20); + + /* clear reset flag */ + ret = regmap_write(cw_bat->regmap, CW2015_REG_MODE, reset_val); + if (ret) + return ret; + + /* wait for gauge to become ready */ + ret = regmap_read_poll_timeout(cw_bat->regmap, CW2015_REG_SOC, + reg_val, reg_val <= 100, + 10 * USEC_PER_MSEC, 10 * USEC_PER_SEC); + if (ret) + dev_err(cw_bat->dev, + "Gauge did not become ready after profile upload\n"); + else + dev_dbg(cw_bat->dev, "Battery profile updated\n"); + + return ret; +} + +static int cw_init(struct cw_battery *cw_bat) +{ + int ret; + unsigned int reg_val = CW2015_MODE_SLEEP; + + if ((reg_val & CW2015_MODE_SLEEP_MASK) == CW2015_MODE_SLEEP) { + reg_val = CW2015_MODE_NORMAL; + ret = regmap_write(cw_bat->regmap, CW2015_REG_MODE, reg_val); + if (ret) + return ret; + } + + ret = regmap_read(cw_bat->regmap, CW2015_REG_CONFIG, ®_val); + if (ret) + return ret; + + if ((reg_val & CW2015_MASK_ATHD) != CW2015_ATHD(cw_bat->alert_level)) { + dev_dbg(cw_bat->dev, "Setting new alert level\n"); + reg_val &= ~CW2015_MASK_ATHD; + reg_val |= ~CW2015_ATHD(cw_bat->alert_level); + ret = regmap_write(cw_bat->regmap, CW2015_REG_CONFIG, reg_val); + if (ret) + return ret; + } + + ret = regmap_read(cw_bat->regmap, CW2015_REG_CONFIG, ®_val); + if (ret) + return ret; + + if (!(reg_val & CW2015_CONFIG_UPDATE_FLG)) { + dev_dbg(cw_bat->dev, + "Battery profile not present, uploading battery profile\n"); + if (cw_bat->bat_profile) { + ret = cw_update_profile(cw_bat); + if (ret) { + dev_err(cw_bat->dev, + "Failed to upload battery profile\n"); + return ret; + } + } else { + dev_warn(cw_bat->dev, + "No profile specified, continuing without profile\n"); + } + } else if (cw_bat->bat_profile) { + u8 bat_info[CW2015_SIZE_BATINFO]; + + ret = regmap_raw_read(cw_bat->regmap, CW2015_REG_BATINFO, + bat_info, CW2015_SIZE_BATINFO); + if (ret) { + dev_err(cw_bat->dev, + "Failed to read stored battery profile\n"); + return ret; + } + + if (memcmp(bat_info, cw_bat->bat_profile, CW2015_SIZE_BATINFO)) { + dev_warn(cw_bat->dev, "Replacing stored battery profile\n"); + ret = cw_update_profile(cw_bat); + if (ret) + return ret; + } + } else { + dev_warn(cw_bat->dev, + "Can't check current battery profile, no profile provided\n"); + } + + dev_dbg(cw_bat->dev, "Battery profile configured\n"); + return 0; +} + +static int cw_power_on_reset(struct cw_battery *cw_bat) +{ + int ret; + unsigned char reset_val; + + reset_val = CW2015_MODE_SLEEP; + ret = regmap_write(cw_bat->regmap, CW2015_REG_MODE, reset_val); + if (ret) + return ret; + + /* wait for gauge to enter sleep */ + msleep(20); + + reset_val = CW2015_MODE_NORMAL; + ret = regmap_write(cw_bat->regmap, CW2015_REG_MODE, reset_val); + if (ret) + return ret; + + ret = cw_init(cw_bat); + if (ret) + return ret; + return 0; +} + +#define HYSTERESIS(current, previous, up, down) \ + (((current) < (previous) + (up)) && ((current) > (previous) - (down))) + +static int cw_get_soc(struct cw_battery *cw_bat) +{ + unsigned int soc; + int ret; + + ret = regmap_read(cw_bat->regmap, CW2015_REG_SOC, &soc); + if (ret) + return ret; + + if (soc > 100) { + int max_error_cycles = + CW2015_BAT_SOC_ERROR_MS / cw_bat->poll_interval_ms; + + dev_err(cw_bat->dev, "Invalid SoC %d%%\n", soc); + cw_bat->read_errors++; + if (cw_bat->read_errors > max_error_cycles) { + dev_warn(cw_bat->dev, + "Too many invalid SoC reports, resetting gauge\n"); + cw_power_on_reset(cw_bat); + cw_bat->read_errors = 0; + } + return cw_bat->soc; + } + cw_bat->read_errors = 0; + + /* Reset gauge if stuck while charging */ + if (cw_bat->status == POWER_SUPPLY_STATUS_CHARGING && soc == cw_bat->soc) { + int max_stuck_cycles = + CW2015_BAT_CHARGING_STUCK_MS / cw_bat->poll_interval_ms; + + cw_bat->charge_stuck_cnt++; + if (cw_bat->charge_stuck_cnt > max_stuck_cycles) { + dev_warn(cw_bat->dev, + "SoC stuck @%u%%, resetting gauge\n", soc); + cw_power_on_reset(cw_bat); + cw_bat->charge_stuck_cnt = 0; + } + } else { + cw_bat->charge_stuck_cnt = 0; + } + + /* Ignore voltage dips during charge */ + if (cw_bat->charger_attached && HYSTERESIS(soc, cw_bat->soc, 0, 3)) + soc = cw_bat->soc; + + /* Ignore voltage spikes during discharge */ + if (!cw_bat->charger_attached && HYSTERESIS(soc, cw_bat->soc, 3, 0)) + soc = cw_bat->soc; + + return soc; +} + +static int cw_get_voltage(struct cw_battery *cw_bat) +{ + int ret, i, voltage_mv; + u16 reg_val; + u32 avg = 0; + + for (i = 0; i < CW2015_AVERAGING_SAMPLES; i++) { + ret = cw_read_word(cw_bat, CW2015_REG_VCELL, ®_val); + if (ret) + return ret; + + avg += reg_val; + } + avg /= CW2015_AVERAGING_SAMPLES; + + /* + * 305 uV per ADC step + * Use 312 / 1024 as efficient approximation of 305 / 1000 + * Negligible error of 0.1% + */ + voltage_mv = avg * 312 / 1024; + + dev_dbg(cw_bat->dev, "Read voltage: %d mV, raw=0x%04x\n", + voltage_mv, reg_val); + return voltage_mv; +} + +static int cw_get_time_to_empty(struct cw_battery *cw_bat) +{ + int ret; + u16 value16; + + ret = cw_read_word(cw_bat, CW2015_REG_RRT_ALERT, &value16); + if (ret) + return ret; + + return value16 & CW2015_MASK_SOC; +} + +static void cw_update_charge_status(struct cw_battery *cw_bat) +{ + int ret; + + ret = power_supply_am_i_supplied(cw_bat->rk_bat); + if (ret < 0) { + dev_warn(cw_bat->dev, "Failed to get supply state: %d\n", ret); + } else { + bool charger_attached; + + charger_attached = !!ret; + if (cw_bat->charger_attached != charger_attached) { + cw_bat->battery_changed = true; + if (charger_attached) + cw_bat->charge_count++; + } + cw_bat->charger_attached = charger_attached; + } +} + +static void cw_update_soc(struct cw_battery *cw_bat) +{ + int soc; + + soc = cw_get_soc(cw_bat); + if (soc < 0) + dev_err(cw_bat->dev, "Failed to get SoC from gauge: %d\n", soc); + else if (cw_bat->soc != soc) { + cw_bat->soc = soc; + cw_bat->battery_changed = true; + } +} + +static void cw_update_voltage(struct cw_battery *cw_bat) +{ + int voltage_mv; + + voltage_mv = cw_get_voltage(cw_bat); + if (voltage_mv < 0) + dev_err(cw_bat->dev, "Failed to get voltage from gauge: %d\n", + voltage_mv); + else + cw_bat->voltage_mv = voltage_mv; +} + +static void cw_update_status(struct cw_battery *cw_bat) +{ + int status = POWER_SUPPLY_STATUS_DISCHARGING; + + if (cw_bat->charger_attached) { + if (cw_bat->soc >= 100) + status = POWER_SUPPLY_STATUS_FULL; + else + status = POWER_SUPPLY_STATUS_CHARGING; + } + + if (cw_bat->status != status) + cw_bat->battery_changed = true; + cw_bat->status = status; +} + +static void cw_update_time_to_empty(struct cw_battery *cw_bat) +{ + int time_to_empty; + + time_to_empty = cw_get_time_to_empty(cw_bat); + if (time_to_empty < 0) + dev_err(cw_bat->dev, "Failed to get time to empty from gauge: %d\n", + time_to_empty); + else if (cw_bat->time_to_empty != time_to_empty) { + cw_bat->time_to_empty = time_to_empty; + cw_bat->battery_changed = true; + } +} + +static void cw_bat_work(struct work_struct *work) +{ + struct delayed_work *delay_work; + struct cw_battery *cw_bat; + int ret; + unsigned int reg_val; + + delay_work = to_delayed_work(work); + cw_bat = container_of(delay_work, struct cw_battery, battery_delay_work); + ret = regmap_read(cw_bat->regmap, CW2015_REG_MODE, ®_val); + if (ret) { + dev_err(cw_bat->dev, "Failed to read mode from gauge: %d\n", ret); + } else { + if ((reg_val & CW2015_MODE_SLEEP_MASK) == CW2015_MODE_SLEEP) { + int i; + + for (i = 0; i < CW2015_RESET_TRIES; i++) { + if (!cw_power_on_reset(cw_bat)) + break; + } + } + cw_update_soc(cw_bat); + cw_update_voltage(cw_bat); + cw_update_charge_status(cw_bat); + cw_update_status(cw_bat); + cw_update_time_to_empty(cw_bat); + } + dev_dbg(cw_bat->dev, "charger_attached = %d\n", cw_bat->charger_attached); + dev_dbg(cw_bat->dev, "status = %d\n", cw_bat->status); + dev_dbg(cw_bat->dev, "soc = %d%%\n", cw_bat->soc); + dev_dbg(cw_bat->dev, "voltage = %dmV\n", cw_bat->voltage_mv); + + if (cw_bat->battery_changed) + power_supply_changed(cw_bat->rk_bat); + cw_bat->battery_changed = false; + + queue_delayed_work(cw_bat->battery_workqueue, + &cw_bat->battery_delay_work, + msecs_to_jiffies(cw_bat->poll_interval_ms)); +} + +static bool cw_battery_valid_time_to_empty(struct cw_battery *cw_bat) +{ + return cw_bat->time_to_empty > 0 && + cw_bat->time_to_empty < CW2015_MASK_SOC && + cw_bat->status == POWER_SUPPLY_STATUS_DISCHARGING; +} + +static int cw_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct cw_battery *cw_bat; + + cw_bat = power_supply_get_drvdata(psy); + switch (psp) { + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = cw_bat->soc; + break; + + case POWER_SUPPLY_PROP_STATUS: + val->intval = cw_bat->status; + break; + + case POWER_SUPPLY_PROP_PRESENT: + val->intval = !!cw_bat->voltage_mv; + break; + + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = cw_bat->voltage_mv * 1000; + break; + + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: + if (cw_battery_valid_time_to_empty(cw_bat)) + val->intval = cw_bat->time_to_empty; + else + val->intval = 0; + break; + + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + val->intval = cw_bat->charge_count; + break; + + case POWER_SUPPLY_PROP_CHARGE_FULL: + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + if (cw_bat->battery.charge_full_design_uah > 0) + val->intval = cw_bat->battery.charge_full_design_uah; + else + val->intval = 0; + break; + + case POWER_SUPPLY_PROP_CURRENT_NOW: + if (cw_battery_valid_time_to_empty(cw_bat) && + cw_bat->battery.charge_full_design_uah > 0) { + /* calculate remaining capacity */ + val->intval = cw_bat->battery.charge_full_design_uah; + val->intval = val->intval * cw_bat->soc / 100; + + /* estimate current based on time to empty */ + val->intval = 60 * val->intval / cw_bat->time_to_empty; + } else { + val->intval = 0; + } + + break; + + default: + break; + } + return 0; +} + +static enum power_supply_property cw_battery_properties[] = { + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CURRENT_NOW, +}; + +static const struct power_supply_desc cw2015_bat_desc = { + .name = "cw2015-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = cw_battery_properties, + .num_properties = ARRAY_SIZE(cw_battery_properties), + .get_property = cw_battery_get_property, +}; + +static int cw2015_parse_properties(struct cw_battery *cw_bat) +{ + struct device *dev = cw_bat->dev; + int length; + int ret; + + length = device_property_count_u8(dev, "cellwise,battery-profile"); + if (length < 0) { + dev_warn(cw_bat->dev, + "No battery-profile found, using current flash contents\n"); + } else if (length != CW2015_SIZE_BATINFO) { + dev_err(cw_bat->dev, "battery-profile must be %d bytes\n", + CW2015_SIZE_BATINFO); + return -EINVAL; + } else { + cw_bat->bat_profile = devm_kzalloc(dev, length, GFP_KERNEL); + if (!cw_bat->bat_profile) + return -ENOMEM; + + ret = device_property_read_u8_array(dev, + "cellwise,battery-profile", + cw_bat->bat_profile, + length); + if (ret) + return ret; + } + + ret = device_property_read_u32(dev, "cellwise,monitor-interval-ms", + &cw_bat->poll_interval_ms); + if (ret) { + dev_dbg(cw_bat->dev, "Using default poll interval\n"); + cw_bat->poll_interval_ms = CW2015_DEFAULT_POLL_INTERVAL_MS; + } + + return 0; +} + +static const struct regmap_range regmap_ranges_rd_yes[] = { + regmap_reg_range(CW2015_REG_VERSION, CW2015_REG_VERSION), + regmap_reg_range(CW2015_REG_VCELL, CW2015_REG_CONFIG), + regmap_reg_range(CW2015_REG_MODE, CW2015_REG_MODE), + regmap_reg_range(CW2015_REG_BATINFO, + CW2015_REG_BATINFO + CW2015_SIZE_BATINFO - 1), +}; + +static const struct regmap_access_table regmap_rd_table = { + .yes_ranges = regmap_ranges_rd_yes, + .n_yes_ranges = 4, +}; + +static const struct regmap_range regmap_ranges_wr_yes[] = { + regmap_reg_range(CW2015_REG_RRT_ALERT, CW2015_REG_CONFIG), + regmap_reg_range(CW2015_REG_MODE, CW2015_REG_MODE), + regmap_reg_range(CW2015_REG_BATINFO, + CW2015_REG_BATINFO + CW2015_SIZE_BATINFO - 1), +}; + +static const struct regmap_access_table regmap_wr_table = { + .yes_ranges = regmap_ranges_wr_yes, + .n_yes_ranges = 3, +}; + +static const struct regmap_range regmap_ranges_vol_yes[] = { + regmap_reg_range(CW2015_REG_VCELL, CW2015_REG_SOC + 1), +}; + +static const struct regmap_access_table regmap_vol_table = { + .yes_ranges = regmap_ranges_vol_yes, + .n_yes_ranges = 1, +}; + +static const struct regmap_config cw2015_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .rd_table = ®map_rd_table, + .wr_table = ®map_wr_table, + .volatile_table = ®map_vol_table, + .max_register = CW2015_REG_BATINFO + CW2015_SIZE_BATINFO - 1, +}; + +static int cw_bat_probe(struct i2c_client *client) +{ + int ret; + struct cw_battery *cw_bat; + struct power_supply_config psy_cfg = { 0 }; + + cw_bat = devm_kzalloc(&client->dev, sizeof(*cw_bat), GFP_KERNEL); + if (!cw_bat) + return -ENOMEM; + + i2c_set_clientdata(client, cw_bat); + cw_bat->dev = &client->dev; + cw_bat->soc = 1; + + ret = cw2015_parse_properties(cw_bat); + if (ret) { + dev_err(cw_bat->dev, "Failed to parse cw2015 properties\n"); + return ret; + } + + cw_bat->regmap = devm_regmap_init_i2c(client, &cw2015_regmap_config); + if (IS_ERR(cw_bat->regmap)) { + dev_err(cw_bat->dev, "Failed to allocate regmap: %ld\n", + PTR_ERR(cw_bat->regmap)); + return PTR_ERR(cw_bat->regmap); + } + + ret = cw_init(cw_bat); + if (ret) { + dev_err(cw_bat->dev, "Init failed: %d\n", ret); + return ret; + } + + psy_cfg.drv_data = cw_bat; + psy_cfg.fwnode = dev_fwnode(cw_bat->dev); + + cw_bat->rk_bat = devm_power_supply_register(&client->dev, + &cw2015_bat_desc, + &psy_cfg); + if (IS_ERR(cw_bat->rk_bat)) { + dev_err(cw_bat->dev, "Failed to register power supply\n"); + return PTR_ERR(cw_bat->rk_bat); + } + + ret = power_supply_get_battery_info(cw_bat->rk_bat, &cw_bat->battery); + if (ret) { + dev_warn(cw_bat->dev, + "No monitored battery, some properties will be missing\n"); + } + + cw_bat->battery_workqueue = create_singlethread_workqueue("rk_battery"); + INIT_DELAYED_WORK(&cw_bat->battery_delay_work, cw_bat_work); + queue_delayed_work(cw_bat->battery_workqueue, + &cw_bat->battery_delay_work, msecs_to_jiffies(10)); + return 0; +} + +static int __maybe_unused cw_bat_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cw_battery *cw_bat = i2c_get_clientdata(client); + + cancel_delayed_work_sync(&cw_bat->battery_delay_work); + return 0; +} + +static int __maybe_unused cw_bat_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cw_battery *cw_bat = i2c_get_clientdata(client); + + queue_delayed_work(cw_bat->battery_workqueue, + &cw_bat->battery_delay_work, 0); + return 0; +} + +SIMPLE_DEV_PM_OPS(cw_bat_pm_ops, cw_bat_suspend, cw_bat_resume); + +static int cw_bat_remove(struct i2c_client *client) +{ + struct cw_battery *cw_bat = i2c_get_clientdata(client); + + cancel_delayed_work_sync(&cw_bat->battery_delay_work); + power_supply_put_battery_info(cw_bat->rk_bat, &cw_bat->battery); + return 0; +} + +static const struct i2c_device_id cw_bat_id_table[] = { + { "cw2015", 0 }, + { } +}; + +static const struct of_device_id cw2015_of_match[] = { + { .compatible = "cellwise,cw2015" }, + { } +}; +MODULE_DEVICE_TABLE(of, cw2015_of_match); + +static struct i2c_driver cw_bat_driver = { + .driver = { + .name = "cw2015", + .pm = &cw_bat_pm_ops, + }, + .probe_new = cw_bat_probe, + .remove = cw_bat_remove, + .id_table = cw_bat_id_table, +}; + +module_i2c_driver(cw_bat_driver); + +MODULE_AUTHOR("xhc"); +MODULE_AUTHOR("Tobias Schramm "); +MODULE_DESCRIPTION("cw2015/cw2013 battery driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 5956fca78f5c2a6390ab59919a277c3ef169f909 Mon Sep 17 00:00:00 2001 From: Michał Mirosław Date: Sun, 3 May 2020 17:21:10 +0200 Subject: power: bq25890: simplify chip name property getter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Driver rejects unknown chips early in the probe(), so when bq25890_power_supply_get_property() is made reachable, bq->chip_version will already be set to correct value - there is no need to check it again. Signed-off-by: Michał Mirosław Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq25890_charger.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index aebd1253dbc9..d44ef48ce8f9 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -32,6 +32,13 @@ enum bq25890_chip_version { BQ25896, }; +static const char *const bq25890_chip_name[] = { + "BQ25890", + "BQ25892", + "BQ25895", + "BQ25896", +}; + enum bq25890_fields { F_EN_HIZ, F_EN_ILIM, F_IILIM, /* Reg00 */ F_BHOT, F_BCOLD, F_VINDPM_OFS, /* Reg01 */ @@ -400,17 +407,7 @@ static int bq25890_power_supply_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_MODEL_NAME: - if (bq->chip_version == BQ25890) - val->strval = "BQ25890"; - else if (bq->chip_version == BQ25892) - val->strval = "BQ25892"; - else if (bq->chip_version == BQ25895) - val->strval = "BQ25895"; - else if (bq->chip_version == BQ25896) - val->strval = "BQ25896"; - else - val->strval = "UNKNOWN"; - + val->strval = bq25890_chip_name[bq->chip_version]; break; case POWER_SUPPLY_PROP_ONLINE: -- cgit v1.2.3 From a6a48fac96ae74f76422f908e09fdc9a6722050f Mon Sep 17 00:00:00 2001 From: Michał Mirosław Date: Sun, 3 May 2020 17:21:10 +0200 Subject: power: bq25890: make property table const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Property list should not change, so mark it const. Signed-off-by: Michał Mirosław Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq25890_charger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index d44ef48ce8f9..9e862e2a99cf 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -668,7 +668,7 @@ static int bq25890_hw_init(struct bq25890_device *bq) return 0; } -static enum power_supply_property bq25890_power_supply_props[] = { +static const enum power_supply_property bq25890_power_supply_props[] = { POWER_SUPPLY_PROP_MANUFACTURER, POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_STATUS, -- cgit v1.2.3 From a9c2419406b8e50351b691d966dc4b7254678dbd Mon Sep 17 00:00:00 2001 From: Michał Mirosław Date: Sun, 3 May 2020 17:21:10 +0200 Subject: power: bq25890: remove redundant I2C bus check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit regmap initialization will check I2C adapter functionality. Remove redundant check in the driver. Signed-off-by: Michał Mirosław Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq25890_charger.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index 9e862e2a99cf..c4a69fd69f34 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -878,17 +878,11 @@ static int bq25890_fw_probe(struct bq25890_device *bq) static int bq25890_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct i2c_adapter *adapter = client->adapter; struct device *dev = &client->dev; struct bq25890_device *bq; int ret; int i; - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - dev_err(dev, "No support for SMBUS_BYTE_DATA\n"); - return -ENODEV; - } - bq = devm_kzalloc(dev, sizeof(*bq), GFP_KERNEL); if (!bq) return -ENOMEM; -- cgit v1.2.3 From 72d9cd9cdc18b53c1df935dc9d40ea7bf057387f Mon Sep 17 00:00:00 2001 From: Michał Mirosław Date: Sun, 3 May 2020 17:21:11 +0200 Subject: power: bq25890: protect view of the chip's state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend bq->lock over whole updating of the chip's state. Might get useful later for switching ADC modes correctly. Signed-off-by: Michał Mirosław Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq25890_charger.c | 82 +++++++++++----------------------- 1 file changed, 26 insertions(+), 56 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index c4a69fd69f34..9339e216651f 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -510,74 +510,50 @@ static int bq25890_get_chip_state(struct bq25890_device *bq, return 0; } -static bool bq25890_state_changed(struct bq25890_device *bq, - struct bq25890_state *new_state) -{ - struct bq25890_state old_state; - - mutex_lock(&bq->lock); - old_state = bq->state; - mutex_unlock(&bq->lock); - - return (old_state.chrg_status != new_state->chrg_status || - old_state.chrg_fault != new_state->chrg_fault || - old_state.online != new_state->online || - old_state.bat_fault != new_state->bat_fault || - old_state.boost_fault != new_state->boost_fault || - old_state.vsys_status != new_state->vsys_status); -} - -static void bq25890_handle_state_change(struct bq25890_device *bq, - struct bq25890_state *new_state) +static irqreturn_t __bq25890_handle_irq(struct bq25890_device *bq) { + struct bq25890_state new_state; int ret; - struct bq25890_state old_state; - mutex_lock(&bq->lock); - old_state = bq->state; - mutex_unlock(&bq->lock); + ret = bq25890_get_chip_state(bq, &new_state); + if (ret < 0) + return IRQ_NONE; - if (!new_state->online) { /* power removed */ + if (!memcmp(&bq->state, &new_state, sizeof(new_state))) + return IRQ_NONE; + + if (!new_state.online && bq->state.online) { /* power removed */ /* disable ADC */ ret = bq25890_field_write(bq, F_CONV_START, 0); if (ret < 0) goto error; - } else if (!old_state.online) { /* power inserted */ + } 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_START, 1); if (ret < 0) goto error; } - return; + bq->state = new_state; + power_supply_changed(bq->charger); + return IRQ_HANDLED; error: - dev_err(bq->dev, "Error communicating with the chip.\n"); + dev_err(bq->dev, "Error communicating with the chip: %pe\n", + ERR_PTR(ret)); + return IRQ_HANDLED; } static irqreturn_t bq25890_irq_handler_thread(int irq, void *private) { struct bq25890_device *bq = private; - int ret; - struct bq25890_state state; - - ret = bq25890_get_chip_state(bq, &state); - if (ret < 0) - goto handled; - - if (!bq25890_state_changed(bq, &state)) - goto handled; - - bq25890_handle_state_change(bq, &state); + irqreturn_t ret; mutex_lock(&bq->lock); - bq->state = state; + ret = __bq25890_handle_irq(bq); mutex_unlock(&bq->lock); - power_supply_changed(bq->charger); - -handled: - return IRQ_HANDLED; + return ret; } static int bq25890_chip_reset(struct bq25890_device *bq) @@ -607,7 +583,6 @@ static int bq25890_hw_init(struct bq25890_device *bq) { int ret; int i; - struct bq25890_state state; const struct { enum bq25890_fields id; @@ -655,16 +630,12 @@ static int bq25890_hw_init(struct bq25890_device *bq) return ret; } - ret = bq25890_get_chip_state(bq, &state); + ret = bq25890_get_chip_state(bq, &bq->state); if (ret < 0) { dev_dbg(bq->dev, "Get state failed %d\n", ret); return ret; } - mutex_lock(&bq->lock); - bq->state = state; - mutex_unlock(&bq->lock); - return 0; } @@ -1001,19 +972,16 @@ static int bq25890_suspend(struct device *dev) static int bq25890_resume(struct device *dev) { int ret; - struct bq25890_state state; struct bq25890_device *bq = dev_get_drvdata(dev); - ret = bq25890_get_chip_state(bq, &state); + mutex_lock(&bq->lock); + + ret = bq25890_get_chip_state(bq, &bq->state); if (ret < 0) return ret; - mutex_lock(&bq->lock); - bq->state = state; - mutex_unlock(&bq->lock); - /* Re-enable ADC only if charger is plugged in. */ - if (state.online) { + if (bq->state.online) { ret = bq25890_field_write(bq, F_CONV_START, 1); if (ret < 0) return ret; @@ -1022,6 +990,8 @@ static int bq25890_resume(struct device *dev) /* signal userspace, maybe state changed while suspended */ power_supply_changed(bq->charger); + mutex_unlock(&bq->lock); + return 0; } #endif -- cgit v1.2.3 From 2cabeaf151294eceaa0f0e0fab2fe9fe66868869 Mon Sep 17 00:00:00 2001 From: Mathew King Date: Mon, 4 May 2020 14:29:27 -0600 Subject: power: supply: core: Cleanup power supply sysfs attribute list Make the device attribute list used to create sysfs attributes more robust by decoupling the list order from order of the enum defined in power_supply.h. This is done by using a designated initializer in the POWER_SUPPLY_ATTR macro. Signed-off-by: Mathew King Signed-off-by: Sebastian Reichel --- drivers/power/supply/power_supply_sysfs.c | 260 +++++++++++++++--------------- 1 file changed, 132 insertions(+), 128 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index 51de3f47b25d..cfc2550f494c 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -18,26 +18,20 @@ #include "power_supply.h" -/* - * This is because the name "current" breaks the device attr macro. - * The "current" word resolves to "(get_current())" so instead of - * "current" "(get_current())" appears in the sysfs. - * - * The source of this definition is the device.h which calls __ATTR - * macro in sysfs.h which calls the __stringify macro. - * - * Only modification that the name is not tried to be resolved - * (as a macro let's say). - */ +#define MAX_PROP_NAME_LEN 30 -#define POWER_SUPPLY_ATTR(_name) \ -{ \ - .attr = { .name = #_name }, \ - .show = power_supply_show_property, \ - .store = power_supply_store_property, \ -} +struct power_supply_attr { + const char *prop_name; + char attr_name[MAX_PROP_NAME_LEN + 1]; + struct device_attribute dev_attr; +}; -static struct device_attribute power_supply_attrs[]; +#define POWER_SUPPLY_ATTR(_name) \ +[POWER_SUPPLY_PROP_ ## _name] = \ +{ \ + .prop_name = #_name, \ + .attr_name = #_name "\0", \ +} static const char * const power_supply_type_text[] = { "Unknown", "Battery", "UPS", "Mains", "USB", @@ -77,6 +71,91 @@ static const char * const power_supply_scope_text[] = { "Unknown", "System", "Device" }; +static struct power_supply_attr power_supply_attrs[] = { + /* Properties of type `int' */ + POWER_SUPPLY_ATTR(STATUS), + POWER_SUPPLY_ATTR(CHARGE_TYPE), + POWER_SUPPLY_ATTR(HEALTH), + POWER_SUPPLY_ATTR(PRESENT), + POWER_SUPPLY_ATTR(ONLINE), + POWER_SUPPLY_ATTR(AUTHENTIC), + POWER_SUPPLY_ATTR(TECHNOLOGY), + POWER_SUPPLY_ATTR(CYCLE_COUNT), + POWER_SUPPLY_ATTR(VOLTAGE_MAX), + POWER_SUPPLY_ATTR(VOLTAGE_MIN), + POWER_SUPPLY_ATTR(VOLTAGE_MAX_DESIGN), + POWER_SUPPLY_ATTR(VOLTAGE_MIN_DESIGN), + POWER_SUPPLY_ATTR(VOLTAGE_NOW), + POWER_SUPPLY_ATTR(VOLTAGE_AVG), + POWER_SUPPLY_ATTR(VOLTAGE_OCV), + POWER_SUPPLY_ATTR(VOLTAGE_BOOT), + POWER_SUPPLY_ATTR(CURRENT_MAX), + POWER_SUPPLY_ATTR(CURRENT_NOW), + POWER_SUPPLY_ATTR(CURRENT_AVG), + POWER_SUPPLY_ATTR(CURRENT_BOOT), + POWER_SUPPLY_ATTR(POWER_NOW), + POWER_SUPPLY_ATTR(POWER_AVG), + POWER_SUPPLY_ATTR(CHARGE_FULL_DESIGN), + POWER_SUPPLY_ATTR(CHARGE_EMPTY_DESIGN), + POWER_SUPPLY_ATTR(CHARGE_FULL), + POWER_SUPPLY_ATTR(CHARGE_EMPTY), + POWER_SUPPLY_ATTR(CHARGE_NOW), + POWER_SUPPLY_ATTR(CHARGE_AVG), + POWER_SUPPLY_ATTR(CHARGE_COUNTER), + POWER_SUPPLY_ATTR(CONSTANT_CHARGE_CURRENT), + POWER_SUPPLY_ATTR(CONSTANT_CHARGE_CURRENT_MAX), + POWER_SUPPLY_ATTR(CONSTANT_CHARGE_VOLTAGE), + POWER_SUPPLY_ATTR(CONSTANT_CHARGE_VOLTAGE_MAX), + POWER_SUPPLY_ATTR(CHARGE_CONTROL_LIMIT), + POWER_SUPPLY_ATTR(CHARGE_CONTROL_LIMIT_MAX), + POWER_SUPPLY_ATTR(CHARGE_CONTROL_START_THRESHOLD), + POWER_SUPPLY_ATTR(CHARGE_CONTROL_END_THRESHOLD), + POWER_SUPPLY_ATTR(INPUT_CURRENT_LIMIT), + POWER_SUPPLY_ATTR(INPUT_VOLTAGE_LIMIT), + POWER_SUPPLY_ATTR(INPUT_POWER_LIMIT), + POWER_SUPPLY_ATTR(ENERGY_FULL_DESIGN), + POWER_SUPPLY_ATTR(ENERGY_EMPTY_DESIGN), + POWER_SUPPLY_ATTR(ENERGY_FULL), + POWER_SUPPLY_ATTR(ENERGY_EMPTY), + POWER_SUPPLY_ATTR(ENERGY_NOW), + POWER_SUPPLY_ATTR(ENERGY_AVG), + POWER_SUPPLY_ATTR(CAPACITY), + POWER_SUPPLY_ATTR(CAPACITY_ALERT_MIN), + POWER_SUPPLY_ATTR(CAPACITY_ALERT_MAX), + POWER_SUPPLY_ATTR(CAPACITY_LEVEL), + POWER_SUPPLY_ATTR(TEMP), + POWER_SUPPLY_ATTR(TEMP_MAX), + POWER_SUPPLY_ATTR(TEMP_MIN), + POWER_SUPPLY_ATTR(TEMP_ALERT_MIN), + POWER_SUPPLY_ATTR(TEMP_ALERT_MAX), + POWER_SUPPLY_ATTR(TEMP_AMBIENT), + POWER_SUPPLY_ATTR(TEMP_AMBIENT_ALERT_MIN), + POWER_SUPPLY_ATTR(TEMP_AMBIENT_ALERT_MAX), + POWER_SUPPLY_ATTR(TIME_TO_EMPTY_NOW), + POWER_SUPPLY_ATTR(TIME_TO_EMPTY_AVG), + POWER_SUPPLY_ATTR(TIME_TO_FULL_NOW), + POWER_SUPPLY_ATTR(TIME_TO_FULL_AVG), + POWER_SUPPLY_ATTR(TYPE), + POWER_SUPPLY_ATTR(USB_TYPE), + POWER_SUPPLY_ATTR(SCOPE), + POWER_SUPPLY_ATTR(PRECHARGE_CURRENT), + POWER_SUPPLY_ATTR(CHARGE_TERM_CURRENT), + POWER_SUPPLY_ATTR(CALIBRATE), + /* Properties of type `const char *' */ + POWER_SUPPLY_ATTR(MODEL_NAME), + POWER_SUPPLY_ATTR(MANUFACTURER), + POWER_SUPPLY_ATTR(SERIAL_NUMBER), +}; + +static struct attribute * +__power_supply_attrs[ARRAY_SIZE(power_supply_attrs) + 1]; + +static enum power_supply_property dev_attr_psp(struct device_attribute *attr) +{ + return container_of(attr, struct power_supply_attr, dev_attr) - + power_supply_attrs; +} + static ssize_t power_supply_show_usb_type(struct device *dev, const struct power_supply_desc *desc, union power_supply_propval *value, @@ -116,7 +195,7 @@ static ssize_t power_supply_show_property(struct device *dev, char *buf) { ssize_t ret; struct power_supply *psy = dev_get_drvdata(dev); - enum power_supply_property psp = attr - power_supply_attrs; + enum power_supply_property psp = dev_attr_psp(attr); union power_supply_propval value; if (psp == POWER_SUPPLY_PROP_TYPE) { @@ -184,7 +263,7 @@ static ssize_t power_supply_store_property(struct device *dev, const char *buf, size_t count) { ssize_t ret; struct power_supply *psy = dev_get_drvdata(dev); - enum power_supply_property psp = attr - power_supply_attrs; + enum power_supply_property psp = dev_attr_psp(attr); union power_supply_propval value; switch (psp) { @@ -233,86 +312,6 @@ static ssize_t power_supply_store_property(struct device *dev, return count; } -/* Must be in the same order as POWER_SUPPLY_PROP_* */ -static struct device_attribute power_supply_attrs[] = { - /* Properties of type `int' */ - POWER_SUPPLY_ATTR(status), - POWER_SUPPLY_ATTR(charge_type), - POWER_SUPPLY_ATTR(health), - POWER_SUPPLY_ATTR(present), - POWER_SUPPLY_ATTR(online), - POWER_SUPPLY_ATTR(authentic), - POWER_SUPPLY_ATTR(technology), - POWER_SUPPLY_ATTR(cycle_count), - POWER_SUPPLY_ATTR(voltage_max), - POWER_SUPPLY_ATTR(voltage_min), - POWER_SUPPLY_ATTR(voltage_max_design), - POWER_SUPPLY_ATTR(voltage_min_design), - POWER_SUPPLY_ATTR(voltage_now), - POWER_SUPPLY_ATTR(voltage_avg), - POWER_SUPPLY_ATTR(voltage_ocv), - POWER_SUPPLY_ATTR(voltage_boot), - POWER_SUPPLY_ATTR(current_max), - POWER_SUPPLY_ATTR(current_now), - POWER_SUPPLY_ATTR(current_avg), - POWER_SUPPLY_ATTR(current_boot), - POWER_SUPPLY_ATTR(power_now), - POWER_SUPPLY_ATTR(power_avg), - POWER_SUPPLY_ATTR(charge_full_design), - POWER_SUPPLY_ATTR(charge_empty_design), - POWER_SUPPLY_ATTR(charge_full), - POWER_SUPPLY_ATTR(charge_empty), - POWER_SUPPLY_ATTR(charge_now), - POWER_SUPPLY_ATTR(charge_avg), - POWER_SUPPLY_ATTR(charge_counter), - POWER_SUPPLY_ATTR(constant_charge_current), - POWER_SUPPLY_ATTR(constant_charge_current_max), - POWER_SUPPLY_ATTR(constant_charge_voltage), - POWER_SUPPLY_ATTR(constant_charge_voltage_max), - POWER_SUPPLY_ATTR(charge_control_limit), - POWER_SUPPLY_ATTR(charge_control_limit_max), - POWER_SUPPLY_ATTR(charge_control_start_threshold), - POWER_SUPPLY_ATTR(charge_control_end_threshold), - POWER_SUPPLY_ATTR(input_current_limit), - POWER_SUPPLY_ATTR(input_voltage_limit), - POWER_SUPPLY_ATTR(input_power_limit), - POWER_SUPPLY_ATTR(energy_full_design), - POWER_SUPPLY_ATTR(energy_empty_design), - POWER_SUPPLY_ATTR(energy_full), - POWER_SUPPLY_ATTR(energy_empty), - POWER_SUPPLY_ATTR(energy_now), - POWER_SUPPLY_ATTR(energy_avg), - POWER_SUPPLY_ATTR(capacity), - POWER_SUPPLY_ATTR(capacity_alert_min), - POWER_SUPPLY_ATTR(capacity_alert_max), - POWER_SUPPLY_ATTR(capacity_level), - POWER_SUPPLY_ATTR(temp), - POWER_SUPPLY_ATTR(temp_max), - POWER_SUPPLY_ATTR(temp_min), - POWER_SUPPLY_ATTR(temp_alert_min), - POWER_SUPPLY_ATTR(temp_alert_max), - POWER_SUPPLY_ATTR(temp_ambient), - POWER_SUPPLY_ATTR(temp_ambient_alert_min), - POWER_SUPPLY_ATTR(temp_ambient_alert_max), - POWER_SUPPLY_ATTR(time_to_empty_now), - POWER_SUPPLY_ATTR(time_to_empty_avg), - POWER_SUPPLY_ATTR(time_to_full_now), - POWER_SUPPLY_ATTR(time_to_full_avg), - POWER_SUPPLY_ATTR(type), - POWER_SUPPLY_ATTR(usb_type), - POWER_SUPPLY_ATTR(scope), - POWER_SUPPLY_ATTR(precharge_current), - POWER_SUPPLY_ATTR(charge_term_current), - POWER_SUPPLY_ATTR(calibrate), - /* Properties of type `const char *' */ - POWER_SUPPLY_ATTR(model_name), - POWER_SUPPLY_ATTR(manufacturer), - POWER_SUPPLY_ATTR(serial_number), -}; - -static struct attribute * -__power_supply_attrs[ARRAY_SIZE(power_supply_attrs) + 1]; - static umode_t power_supply_attr_is_visible(struct kobject *kobj, struct attribute *attr, int attrno) @@ -322,6 +321,9 @@ static umode_t power_supply_attr_is_visible(struct kobject *kobj, umode_t mode = S_IRUSR | S_IRGRP | S_IROTH; int i; + if (!power_supply_attrs[attrno].prop_name) + return 0; + if (attrno == POWER_SUPPLY_PROP_TYPE) return mode; @@ -350,31 +352,38 @@ static const struct attribute_group *power_supply_attr_groups[] = { NULL, }; -void power_supply_init_attrs(struct device_type *dev_type) +static void str_to_lower(char *str) { - int i; - - dev_type->groups = power_supply_attr_groups; - - for (i = 0; i < ARRAY_SIZE(power_supply_attrs); i++) - __power_supply_attrs[i] = &power_supply_attrs[i].attr; + while (*str) { + *str = tolower(*str); + str++; + } } -static char *kstruprdup(const char *str, gfp_t gfp) +void power_supply_init_attrs(struct device_type *dev_type) { - char *ret, *ustr; + int i; - ustr = ret = kmalloc(strlen(str) + 1, gfp); + dev_type->groups = power_supply_attr_groups; - if (!ret) - return NULL; + for (i = 0; i < ARRAY_SIZE(power_supply_attrs); i++) { + struct device_attribute *attr; - while (*str) - *ustr++ = toupper(*str++); + if (!power_supply_attrs[i].prop_name) { + pr_warn("%s: Property %d skipped because is is missing from power_supply_attrs\n", + __func__, i); + sprintf(power_supply_attrs[i].attr_name, "_err_%d", i); + } else { + str_to_lower(power_supply_attrs[i].attr_name); + } - *ustr = 0; + attr = &power_supply_attrs[i].dev_attr; - return ret; + attr->attr.name = power_supply_attrs[i].attr_name; + attr->show = power_supply_show_property; + attr->store = power_supply_store_property; + __power_supply_attrs[i] = &attr->attr; + } } int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env) @@ -382,7 +391,6 @@ int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env) struct power_supply *psy = dev_get_drvdata(dev); int ret = 0, j; char *prop_buf; - char *attrname; if (!psy || !psy->desc) { dev_dbg(dev, "No power supply yet\n"); @@ -398,12 +406,14 @@ int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env) return -ENOMEM; for (j = 0; j < psy->desc->num_properties; j++) { - struct device_attribute *attr; + struct power_supply_attr *pwr_attr; + struct device_attribute *dev_attr; char *line; - attr = &power_supply_attrs[psy->desc->properties[j]]; + pwr_attr = &power_supply_attrs[psy->desc->properties[j]]; + dev_attr = &pwr_attr->dev_attr; - ret = power_supply_show_property(dev, attr, prop_buf); + ret = power_supply_show_property(dev, dev_attr, prop_buf); if (ret == -ENODEV || ret == -ENODATA) { /* When a battery is absent, we expect -ENODEV. Don't abort; send the uevent with at least the the PRESENT=0 property */ @@ -418,14 +428,8 @@ int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env) if (line) *line = 0; - attrname = kstruprdup(attr->attr.name, GFP_KERNEL); - if (!attrname) { - ret = -ENOMEM; - goto out; - } - - ret = add_uevent_var(env, "POWER_SUPPLY_%s=%s", attrname, prop_buf); - kfree(attrname); + ret = add_uevent_var(env, "POWER_SUPPLY_%s=%s", + pwr_attr->prop_name, prop_buf); if (ret) goto out; } -- cgit v1.2.3 From d960d91f86880d28a6dc8edb4e7b605bcf6354eb Mon Sep 17 00:00:00 2001 From: Mathew King Date: Mon, 4 May 2020 14:29:28 -0600 Subject: power: supply: core: Use designated initializer for property text arrays Use designated initializers for the sysfs power supply text values. This will help ensure that the text values are kept in sync with the enum values from power_supply.h. Signed-off-by: Mathew King Signed-off-by: Sebastian Reichel --- drivers/power/supply/power_supply_sysfs.c | 74 +++++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 14 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index cfc2550f494c..cfd5ed1f775f 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -34,41 +34,87 @@ struct power_supply_attr { } static const char * const power_supply_type_text[] = { - "Unknown", "Battery", "UPS", "Mains", "USB", - "USB_DCP", "USB_CDP", "USB_ACA", "USB_C", - "USB_PD", "USB_PD_DRP", "BrickID" + [POWER_SUPPLY_TYPE_UNKNOWN] = "Unknown", + [POWER_SUPPLY_TYPE_BATTERY] = "Battery", + [POWER_SUPPLY_TYPE_UPS] = "UPS", + [POWER_SUPPLY_TYPE_MAINS] = "Mains", + [POWER_SUPPLY_TYPE_USB] = "USB", + [POWER_SUPPLY_TYPE_USB_DCP] = "USB_DCP", + [POWER_SUPPLY_TYPE_USB_CDP] = "USB_CDP", + [POWER_SUPPLY_TYPE_USB_ACA] = "USB_ACA", + [POWER_SUPPLY_TYPE_USB_TYPE_C] = "USB_C", + [POWER_SUPPLY_TYPE_USB_PD] = "USB_PD", + [POWER_SUPPLY_TYPE_USB_PD_DRP] = "USB_PD_DRP", + [POWER_SUPPLY_TYPE_APPLE_BRICK_ID] = "BrickID", }; static const char * const power_supply_usb_type_text[] = { - "Unknown", "SDP", "DCP", "CDP", "ACA", "C", - "PD", "PD_DRP", "PD_PPS", "BrickID" + [POWER_SUPPLY_USB_TYPE_UNKNOWN] = "Unknown", + [POWER_SUPPLY_USB_TYPE_SDP] = "SDP", + [POWER_SUPPLY_USB_TYPE_DCP] = "DCP", + [POWER_SUPPLY_USB_TYPE_CDP] = "CDP", + [POWER_SUPPLY_USB_TYPE_ACA] = "ACA", + [POWER_SUPPLY_USB_TYPE_C] = "C", + [POWER_SUPPLY_USB_TYPE_PD] = "PD", + [POWER_SUPPLY_USB_TYPE_PD_DRP] = "PD_DRP", + [POWER_SUPPLY_USB_TYPE_PD_PPS] = "PD_PPS", + [POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID] = "BrickID", }; static const char * const power_supply_status_text[] = { - "Unknown", "Charging", "Discharging", "Not charging", "Full" + [POWER_SUPPLY_STATUS_UNKNOWN] = "Unknown", + [POWER_SUPPLY_STATUS_CHARGING] = "Charging", + [POWER_SUPPLY_STATUS_DISCHARGING] = "Discharging", + [POWER_SUPPLY_STATUS_NOT_CHARGING] = "Not charging", + [POWER_SUPPLY_STATUS_FULL] = "Full", }; static const char * const power_supply_charge_type_text[] = { - "Unknown", "N/A", "Trickle", "Fast", "Standard", "Adaptive", "Custom" + [POWER_SUPPLY_CHARGE_TYPE_UNKNOWN] = "Unknown", + [POWER_SUPPLY_CHARGE_TYPE_NONE] = "N/A", + [POWER_SUPPLY_CHARGE_TYPE_TRICKLE] = "Trickle", + [POWER_SUPPLY_CHARGE_TYPE_FAST] = "Fast", + [POWER_SUPPLY_CHARGE_TYPE_STANDARD] = "Standard", + [POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE] = "Adaptive", + [POWER_SUPPLY_CHARGE_TYPE_CUSTOM] = "Custom", }; static const char * const power_supply_health_text[] = { - "Unknown", "Good", "Overheat", "Dead", "Over voltage", - "Unspecified failure", "Cold", "Watchdog timer expire", - "Safety timer expire", "Over current" + [POWER_SUPPLY_HEALTH_UNKNOWN] = "Unknown", + [POWER_SUPPLY_HEALTH_GOOD] = "Good", + [POWER_SUPPLY_HEALTH_OVERHEAT] = "Overheat", + [POWER_SUPPLY_HEALTH_DEAD] = "Dead", + [POWER_SUPPLY_HEALTH_OVERVOLTAGE] = "Over voltage", + [POWER_SUPPLY_HEALTH_UNSPEC_FAILURE] = "Unspecified failure", + [POWER_SUPPLY_HEALTH_COLD] = "Cold", + [POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE] = "Watchdog timer expire", + [POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE] = "Safety timer expire", + [POWER_SUPPLY_HEALTH_OVERCURRENT] = "Over current", }; static const char * const power_supply_technology_text[] = { - "Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd", - "LiMn" + [POWER_SUPPLY_TECHNOLOGY_UNKNOWN] = "Unknown", + [POWER_SUPPLY_TECHNOLOGY_NiMH] = "NiMH", + [POWER_SUPPLY_TECHNOLOGY_LION] = "Li-ion", + [POWER_SUPPLY_TECHNOLOGY_LIPO] = "Li-poly", + [POWER_SUPPLY_TECHNOLOGY_LiFe] = "LiFe", + [POWER_SUPPLY_TECHNOLOGY_NiCd] = "NiCd", + [POWER_SUPPLY_TECHNOLOGY_LiMn] = "LiMn", }; static const char * const power_supply_capacity_level_text[] = { - "Unknown", "Critical", "Low", "Normal", "High", "Full" + [POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN] = "Unknown", + [POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL] = "Critical", + [POWER_SUPPLY_CAPACITY_LEVEL_LOW] = "Low", + [POWER_SUPPLY_CAPACITY_LEVEL_NORMAL] = "Normal", + [POWER_SUPPLY_CAPACITY_LEVEL_HIGH] = "High", + [POWER_SUPPLY_CAPACITY_LEVEL_FULL] = "Full", }; static const char * const power_supply_scope_text[] = { - "Unknown", "System", "Device" + [POWER_SUPPLY_SCOPE_UNKNOWN] = "Unknown", + [POWER_SUPPLY_SCOPE_SYSTEM] = "System", + [POWER_SUPPLY_SCOPE_DEVICE] = "Device", }; static struct power_supply_attr power_supply_attrs[] = { -- cgit v1.2.3 From 5b505366ac6c77a623e18fe18cedb2d4d3c9a7f3 Mon Sep 17 00:00:00 2001 From: Mathew King Date: Mon, 4 May 2020 14:29:29 -0600 Subject: power: supply: core: Add a macro that maps enum properties to text values Reduce the number of touch points to add a new enum property to the power_supply class by mapping the array of text values to the device attribute descriptor. A new enum property can now added by creating an array with the text values named POWER_SUPPLY_${PROPNAME}_TEXT and adding POWER_SUPPLY_ENUM_ATTR(${PROPNAME}) to the power_supply_attrs array. Signed-off-by: Mathew King Signed-off-by: Sebastian Reichel --- drivers/power/supply/power_supply_sysfs.c | 124 ++++++++++++------------------ 1 file changed, 50 insertions(+), 74 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index cfd5ed1f775f..bf6e344cf451 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -24,16 +24,26 @@ struct power_supply_attr { const char *prop_name; char attr_name[MAX_PROP_NAME_LEN + 1]; struct device_attribute dev_attr; + const char * const *text_values; + int text_values_len; }; -#define POWER_SUPPLY_ATTR(_name) \ -[POWER_SUPPLY_PROP_ ## _name] = \ -{ \ - .prop_name = #_name, \ - .attr_name = #_name "\0", \ +#define _POWER_SUPPLY_ATTR(_name, _text, _len) \ +[POWER_SUPPLY_PROP_ ## _name] = \ +{ \ + .prop_name = #_name, \ + .attr_name = #_name "\0", \ + .text_values = _text, \ + .text_values_len = _len, \ } -static const char * const power_supply_type_text[] = { +#define POWER_SUPPLY_ATTR(_name) _POWER_SUPPLY_ATTR(_name, NULL, 0) +#define _POWER_SUPPLY_ENUM_ATTR(_name, _text) \ + _POWER_SUPPLY_ATTR(_name, _text, ARRAY_SIZE(_text)) +#define POWER_SUPPLY_ENUM_ATTR(_name) \ + _POWER_SUPPLY_ENUM_ATTR(_name, POWER_SUPPLY_ ## _name ## _TEXT) + +static const char * const POWER_SUPPLY_TYPE_TEXT[] = { [POWER_SUPPLY_TYPE_UNKNOWN] = "Unknown", [POWER_SUPPLY_TYPE_BATTERY] = "Battery", [POWER_SUPPLY_TYPE_UPS] = "UPS", @@ -48,7 +58,7 @@ static const char * const power_supply_type_text[] = { [POWER_SUPPLY_TYPE_APPLE_BRICK_ID] = "BrickID", }; -static const char * const power_supply_usb_type_text[] = { +static const char * const POWER_SUPPLY_USB_TYPE_TEXT[] = { [POWER_SUPPLY_USB_TYPE_UNKNOWN] = "Unknown", [POWER_SUPPLY_USB_TYPE_SDP] = "SDP", [POWER_SUPPLY_USB_TYPE_DCP] = "DCP", @@ -61,7 +71,7 @@ static const char * const power_supply_usb_type_text[] = { [POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID] = "BrickID", }; -static const char * const power_supply_status_text[] = { +static const char * const POWER_SUPPLY_STATUS_TEXT[] = { [POWER_SUPPLY_STATUS_UNKNOWN] = "Unknown", [POWER_SUPPLY_STATUS_CHARGING] = "Charging", [POWER_SUPPLY_STATUS_DISCHARGING] = "Discharging", @@ -69,7 +79,7 @@ static const char * const power_supply_status_text[] = { [POWER_SUPPLY_STATUS_FULL] = "Full", }; -static const char * const power_supply_charge_type_text[] = { +static const char * const POWER_SUPPLY_CHARGE_TYPE_TEXT[] = { [POWER_SUPPLY_CHARGE_TYPE_UNKNOWN] = "Unknown", [POWER_SUPPLY_CHARGE_TYPE_NONE] = "N/A", [POWER_SUPPLY_CHARGE_TYPE_TRICKLE] = "Trickle", @@ -79,7 +89,7 @@ static const char * const power_supply_charge_type_text[] = { [POWER_SUPPLY_CHARGE_TYPE_CUSTOM] = "Custom", }; -static const char * const power_supply_health_text[] = { +static const char * const POWER_SUPPLY_HEALTH_TEXT[] = { [POWER_SUPPLY_HEALTH_UNKNOWN] = "Unknown", [POWER_SUPPLY_HEALTH_GOOD] = "Good", [POWER_SUPPLY_HEALTH_OVERHEAT] = "Overheat", @@ -92,7 +102,7 @@ static const char * const power_supply_health_text[] = { [POWER_SUPPLY_HEALTH_OVERCURRENT] = "Over current", }; -static const char * const power_supply_technology_text[] = { +static const char * const POWER_SUPPLY_TECHNOLOGY_TEXT[] = { [POWER_SUPPLY_TECHNOLOGY_UNKNOWN] = "Unknown", [POWER_SUPPLY_TECHNOLOGY_NiMH] = "NiMH", [POWER_SUPPLY_TECHNOLOGY_LION] = "Li-ion", @@ -102,7 +112,7 @@ static const char * const power_supply_technology_text[] = { [POWER_SUPPLY_TECHNOLOGY_LiMn] = "LiMn", }; -static const char * const power_supply_capacity_level_text[] = { +static const char * const POWER_SUPPLY_CAPACITY_LEVEL_TEXT[] = { [POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN] = "Unknown", [POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL] = "Critical", [POWER_SUPPLY_CAPACITY_LEVEL_LOW] = "Low", @@ -111,7 +121,7 @@ static const char * const power_supply_capacity_level_text[] = { [POWER_SUPPLY_CAPACITY_LEVEL_FULL] = "Full", }; -static const char * const power_supply_scope_text[] = { +static const char * const POWER_SUPPLY_SCOPE_TEXT[] = { [POWER_SUPPLY_SCOPE_UNKNOWN] = "Unknown", [POWER_SUPPLY_SCOPE_SYSTEM] = "System", [POWER_SUPPLY_SCOPE_DEVICE] = "Device", @@ -119,13 +129,13 @@ static const char * const power_supply_scope_text[] = { static struct power_supply_attr power_supply_attrs[] = { /* Properties of type `int' */ - POWER_SUPPLY_ATTR(STATUS), - POWER_SUPPLY_ATTR(CHARGE_TYPE), - POWER_SUPPLY_ATTR(HEALTH), + POWER_SUPPLY_ENUM_ATTR(STATUS), + POWER_SUPPLY_ENUM_ATTR(CHARGE_TYPE), + POWER_SUPPLY_ENUM_ATTR(HEALTH), POWER_SUPPLY_ATTR(PRESENT), POWER_SUPPLY_ATTR(ONLINE), POWER_SUPPLY_ATTR(AUTHENTIC), - POWER_SUPPLY_ATTR(TECHNOLOGY), + POWER_SUPPLY_ENUM_ATTR(TECHNOLOGY), POWER_SUPPLY_ATTR(CYCLE_COUNT), POWER_SUPPLY_ATTR(VOLTAGE_MAX), POWER_SUPPLY_ATTR(VOLTAGE_MIN), @@ -168,7 +178,7 @@ static struct power_supply_attr power_supply_attrs[] = { POWER_SUPPLY_ATTR(CAPACITY), POWER_SUPPLY_ATTR(CAPACITY_ALERT_MIN), POWER_SUPPLY_ATTR(CAPACITY_ALERT_MAX), - POWER_SUPPLY_ATTR(CAPACITY_LEVEL), + POWER_SUPPLY_ENUM_ATTR(CAPACITY_LEVEL), POWER_SUPPLY_ATTR(TEMP), POWER_SUPPLY_ATTR(TEMP_MAX), POWER_SUPPLY_ATTR(TEMP_MIN), @@ -181,9 +191,9 @@ static struct power_supply_attr power_supply_attrs[] = { POWER_SUPPLY_ATTR(TIME_TO_EMPTY_AVG), POWER_SUPPLY_ATTR(TIME_TO_FULL_NOW), POWER_SUPPLY_ATTR(TIME_TO_FULL_AVG), - POWER_SUPPLY_ATTR(TYPE), + POWER_SUPPLY_ENUM_ATTR(TYPE), POWER_SUPPLY_ATTR(USB_TYPE), - POWER_SUPPLY_ATTR(SCOPE), + POWER_SUPPLY_ENUM_ATTR(SCOPE), POWER_SUPPLY_ATTR(PRECHARGE_CURRENT), POWER_SUPPLY_ATTR(CHARGE_TERM_CURRENT), POWER_SUPPLY_ATTR(CALIBRATE), @@ -196,10 +206,14 @@ static struct power_supply_attr power_supply_attrs[] = { static struct attribute * __power_supply_attrs[ARRAY_SIZE(power_supply_attrs) + 1]; +static struct power_supply_attr *to_ps_attr(struct device_attribute *attr) +{ + return container_of(attr, struct power_supply_attr, dev_attr); +} + static enum power_supply_property dev_attr_psp(struct device_attribute *attr) { - return container_of(attr, struct power_supply_attr, dev_attr) - - power_supply_attrs; + return to_ps_attr(attr) - power_supply_attrs; } static ssize_t power_supply_show_usb_type(struct device *dev, @@ -217,11 +231,11 @@ static ssize_t power_supply_show_usb_type(struct device *dev, if (value->intval == usb_type) { count += sprintf(buf + count, "[%s] ", - power_supply_usb_type_text[usb_type]); + POWER_SUPPLY_USB_TYPE_TEXT[usb_type]); match = true; } else { count += sprintf(buf + count, "%s ", - power_supply_usb_type_text[usb_type]); + POWER_SUPPLY_USB_TYPE_TEXT[usb_type]); } } @@ -241,6 +255,7 @@ static ssize_t power_supply_show_property(struct device *dev, char *buf) { ssize_t ret; struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply_attr *ps_attr = to_ps_attr(attr); enum power_supply_property psp = dev_attr_psp(attr); union power_supply_propval value; @@ -261,38 +276,15 @@ 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]); + } + switch (psp) { - case POWER_SUPPLY_PROP_STATUS: - ret = sprintf(buf, "%s\n", - power_supply_status_text[value.intval]); - break; - case POWER_SUPPLY_PROP_CHARGE_TYPE: - ret = sprintf(buf, "%s\n", - power_supply_charge_type_text[value.intval]); - break; - case POWER_SUPPLY_PROP_HEALTH: - ret = sprintf(buf, "%s\n", - power_supply_health_text[value.intval]); - break; - case POWER_SUPPLY_PROP_TECHNOLOGY: - ret = sprintf(buf, "%s\n", - power_supply_technology_text[value.intval]); - break; - case POWER_SUPPLY_PROP_CAPACITY_LEVEL: - ret = sprintf(buf, "%s\n", - power_supply_capacity_level_text[value.intval]); - break; - case POWER_SUPPLY_PROP_TYPE: - ret = sprintf(buf, "%s\n", - power_supply_type_text[value.intval]); - break; case POWER_SUPPLY_PROP_USB_TYPE: ret = power_supply_show_usb_type(dev, psy->desc, - &value, buf); - break; - case POWER_SUPPLY_PROP_SCOPE: - ret = sprintf(buf, "%s\n", - power_supply_scope_text[value.intval]); + &value, buf); break; case POWER_SUPPLY_PROP_MODEL_NAME ... POWER_SUPPLY_PROP_SERIAL_NUMBER: ret = sprintf(buf, "%s\n", value.strval); @@ -309,30 +301,14 @@ static ssize_t power_supply_store_property(struct device *dev, const char *buf, size_t count) { ssize_t ret; struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply_attr *ps_attr = to_ps_attr(attr); enum power_supply_property psp = dev_attr_psp(attr); union power_supply_propval value; - switch (psp) { - case POWER_SUPPLY_PROP_STATUS: - ret = sysfs_match_string(power_supply_status_text, buf); - break; - case POWER_SUPPLY_PROP_CHARGE_TYPE: - ret = sysfs_match_string(power_supply_charge_type_text, buf); - break; - case POWER_SUPPLY_PROP_HEALTH: - ret = sysfs_match_string(power_supply_health_text, buf); - break; - case POWER_SUPPLY_PROP_TECHNOLOGY: - ret = sysfs_match_string(power_supply_technology_text, buf); - break; - case POWER_SUPPLY_PROP_CAPACITY_LEVEL: - ret = sysfs_match_string(power_supply_capacity_level_text, buf); - break; - case POWER_SUPPLY_PROP_SCOPE: - ret = sysfs_match_string(power_supply_scope_text, buf); - break; - default: - ret = -EINVAL; + ret = -EINVAL; + if (ps_attr->text_values_len > 0) { + ret = __sysfs_match_string(ps_attr->text_values, + ps_attr->text_values_len, buf); } /* -- cgit v1.2.3 From 2ad3d74e3c69fb371412f37d8226f2da3a3b6cc2 Mon Sep 17 00:00:00 2001 From: Mathew King Date: Mon, 4 May 2020 14:29:30 -0600 Subject: power: supply: core: Add type property to uevent env Add POWER_SUPPLY_TYPE to the uevent env for power supply. Type is a property of all power supplies and there is a sysfs entry for it but it is not included in the properties array of the power supply so explicitly add it to the udev env. Signed-off-by: Mathew King Signed-off-by: Sebastian Reichel --- drivers/power/supply/power_supply_sysfs.c | 61 +++++++++++++++++++------------ 1 file changed, 37 insertions(+), 24 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index bf6e344cf451..d21b4e0edf38 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -408,6 +408,37 @@ void power_supply_init_attrs(struct device_type *dev_type) } } +static int add_prop_uevent(struct device *dev, struct kobj_uevent_env *env, + enum power_supply_property prop, char *prop_buf) +{ + int ret = 0; + struct power_supply_attr *pwr_attr; + struct device_attribute *dev_attr; + char *line; + + pwr_attr = &power_supply_attrs[prop]; + dev_attr = &pwr_attr->dev_attr; + + ret = power_supply_show_property(dev, dev_attr, prop_buf); + if (ret == -ENODEV || ret == -ENODATA) { + /* + * When a battery is absent, we expect -ENODEV. Don't abort; + * send the uevent with at least the the PRESENT=0 property + */ + return 0; + } + + if (ret < 0) + return ret; + + line = strchr(prop_buf, '\n'); + if (line) + *line = 0; + + return add_uevent_var(env, "POWER_SUPPLY_%s=%s", + pwr_attr->prop_name, prop_buf); +} + int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env) { struct power_supply *psy = dev_get_drvdata(dev); @@ -427,31 +458,13 @@ int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env) if (!prop_buf) return -ENOMEM; - for (j = 0; j < psy->desc->num_properties; j++) { - struct power_supply_attr *pwr_attr; - struct device_attribute *dev_attr; - char *line; - - pwr_attr = &power_supply_attrs[psy->desc->properties[j]]; - dev_attr = &pwr_attr->dev_attr; - - ret = power_supply_show_property(dev, dev_attr, prop_buf); - if (ret == -ENODEV || ret == -ENODATA) { - /* When a battery is absent, we expect -ENODEV. Don't abort; - send the uevent with at least the the PRESENT=0 property */ - ret = 0; - continue; - } - - if (ret < 0) - goto out; - - line = strchr(prop_buf, '\n'); - if (line) - *line = 0; + ret = add_prop_uevent(dev, env, POWER_SUPPLY_PROP_TYPE, prop_buf); + if (ret) + goto out; - ret = add_uevent_var(env, "POWER_SUPPLY_%s=%s", - pwr_attr->prop_name, prop_buf); + for (j = 0; j < psy->desc->num_properties; j++) { + ret = add_prop_uevent(dev, env, psy->desc->properties[j], + prop_buf); if (ret) goto out; } -- cgit v1.2.3 From cf5701bf749683cc85827fd68c0890838a2b7fd4 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 6 May 2020 13:11:16 +0300 Subject: power: bq25890: unlock on error paths in bq25890_resume() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We introduced some new locking here, but need to update the error paths so they unlock before returning. Fixes: 72d9cd9cdc18 ("power: bq25890: protect view of the chip's state") Signed-off-by: Dan Carpenter Reviewed-by: Michał Mirosław Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq25890_charger.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index 9339e216651f..20b9824ef5ac 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -978,21 +978,22 @@ static int bq25890_resume(struct device *dev) ret = bq25890_get_chip_state(bq, &bq->state); if (ret < 0) - return ret; + goto unlock; /* Re-enable ADC only if charger is plugged in. */ if (bq->state.online) { ret = bq25890_field_write(bq, F_CONV_START, 1); if (ret < 0) - return ret; + goto unlock; } /* signal userspace, maybe state changed while suspended */ power_supply_changed(bq->charger); +unlock: mutex_unlock(&bq->lock); - return 0; + return ret; } #endif -- cgit v1.2.3 From 1a45732926f33245b5cabd37f03cdbf4a1818a16 Mon Sep 17 00:00:00 2001 From: ChenTao Date: Wed, 6 May 2020 19:45:18 +0800 Subject: power: supply: cw2015: Make some symbols static Fix the following warning: drivers/power/supply/cw2015_battery.c:96:5: warning: 'cw_update_profile' was not declared. Should it be static? drivers/power/supply/cw2015_battery.c:712:1: warning: 'cw_bat_pm_ops' was not declared. Should it be static? Reported-by: Hulk Robot Signed-off-by: ChenTao Acked-by: Tobias Schramm Tested-by: Tobias Schramm Signed-off-by: Sebastian Reichel --- drivers/power/supply/cw2015_battery.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/cw2015_battery.c b/drivers/power/supply/cw2015_battery.c index 8603da6f4593..19f62ea957ee 100644 --- a/drivers/power/supply/cw2015_battery.c +++ b/drivers/power/supply/cw2015_battery.c @@ -93,7 +93,7 @@ static int cw_read_word(struct cw_battery *cw_bat, u8 reg, u16 *val) return 0; } -int cw_update_profile(struct cw_battery *cw_bat) +static int cw_update_profile(struct cw_battery *cw_bat) { int ret; unsigned int reg_val; @@ -709,7 +709,7 @@ static int __maybe_unused cw_bat_resume(struct device *dev) return 0; } -SIMPLE_DEV_PM_OPS(cw_bat_pm_ops, cw_bat_suspend, cw_bat_resume); +static SIMPLE_DEV_PM_OPS(cw_bat_pm_ops, cw_bat_suspend, cw_bat_resume); static int cw_bat_remove(struct i2c_client *client) { -- cgit v1.2.3 From 934ed3847a4ebc75b655659c4d2349ba4337941c Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 9 May 2020 10:23:23 +0200 Subject: power: supply: lp8788: Fix an error handling path in 'lp8788_charger_probe()' In the probe function, in case of error, resources allocated in 'lp8788_setup_adc_channel()' must be released. This can be achieved easily by using the devm_ variant of 'iio_channel_get()'. This has the extra benefit to simplify the remove function and to axe the 'lp8788_release_adc_channel()' function which is now useless. Fixes: 98a276649358 ("power_supply: Add new lp8788 charger driver") Signed-off-by: Christophe JAILLET Signed-off-by: Sebastian Reichel --- drivers/power/supply/lp8788-charger.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/lp8788-charger.c b/drivers/power/supply/lp8788-charger.c index 84a206f42a8e..e7931ffb7151 100644 --- a/drivers/power/supply/lp8788-charger.c +++ b/drivers/power/supply/lp8788-charger.c @@ -572,27 +572,14 @@ static void lp8788_setup_adc_channel(struct device *dev, return; /* ADC channel for battery voltage */ - chan = iio_channel_get(dev, pdata->adc_vbatt); + chan = devm_iio_channel_get(dev, pdata->adc_vbatt); pchg->chan[LP8788_VBATT] = IS_ERR(chan) ? NULL : chan; /* ADC channel for battery temperature */ - chan = iio_channel_get(dev, pdata->adc_batt_temp); + chan = devm_iio_channel_get(dev, pdata->adc_batt_temp); pchg->chan[LP8788_BATT_TEMP] = IS_ERR(chan) ? NULL : chan; } -static void lp8788_release_adc_channel(struct lp8788_charger *pchg) -{ - int i; - - for (i = 0; i < LP8788_NUM_CHG_ADC; i++) { - if (!pchg->chan[i]) - continue; - - iio_channel_release(pchg->chan[i]); - pchg->chan[i] = NULL; - } -} - static ssize_t lp8788_show_charger_status(struct device *dev, struct device_attribute *attr, char *buf) { @@ -735,7 +722,6 @@ static int lp8788_charger_remove(struct platform_device *pdev) flush_work(&pchg->charger_work); lp8788_irq_unregister(pdev, pchg); lp8788_psy_unregister(pchg); - lp8788_release_adc_channel(pchg); return 0; } -- cgit v1.2.3 From 92f7d909720e188bf5ce411dc2ebdb8049c855b1 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Fri, 8 May 2020 18:44:45 +0300 Subject: power: supply: bd70528: use linear ranges Change the bd70528 to use common linear_range code instead of implementing a copy of it in this driver. Signed-off-by: Matti Vaittinen Signed-off-by: Sebastian Reichel --- drivers/power/supply/Kconfig | 1 + drivers/power/supply/bd70528-charger.c | 144 +++++++++++++-------------------- 2 files changed, 56 insertions(+), 89 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index efdcf49f27bf..9448d6c66cae 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -706,6 +706,7 @@ config CHARGER_UCS1002 config CHARGER_BD70528 tristate "ROHM bd70528 charger driver" depends on MFD_ROHM_BD70528 + select LINEAR_RANGES default n help Say Y here to enable support for getting battery status diff --git a/drivers/power/supply/bd70528-charger.c b/drivers/power/supply/bd70528-charger.c index 3b820110ecfa..7c1f0b99c71b 100644 --- a/drivers/power/supply/bd70528-charger.c +++ b/drivers/power/supply/bd70528-charger.c @@ -72,6 +72,7 @@ #include #include #include +#include #define CHG_STAT_SUSPEND 0x0 #define CHG_STAT_TRICKLE 0x1 @@ -335,38 +336,37 @@ static int bd70528_get_present(struct bd70528_psy *bdpsy, int *val) return 0; } -struct bd70528_linear_range { - int min; - int step; - int vals; - int low_sel; -}; - -static const struct bd70528_linear_range current_limit_ranges[] = { +static const struct linear_range current_limit_ranges[] = { { .min = 5, .step = 1, - .vals = 36, - .low_sel = 0, + .min_sel = 0, + .max_sel = 0x22, }, { .min = 40, .step = 5, - .vals = 5, - .low_sel = 0x23, + .min_sel = 0x23, + .max_sel = 0x26, }, { .min = 60, .step = 20, - .vals = 8, - .low_sel = 0x27, + .min_sel = 0x27, + .max_sel = 0x2d, }, { .min = 200, .step = 50, - .vals = 7, - .low_sel = 0x2e, - } + .min_sel = 0x2e, + .max_sel = 0x34, + }, + { + .min = 500, + .step = 0, + .min_sel = 0x35, + .max_sel = 0x3f, + }, }; /* @@ -374,18 +374,18 @@ static const struct bd70528_linear_range current_limit_ranges[] = { * voltage for low temperatures. The driver currently only reads * the charge current at room temperature. We do set both though. */ -static const struct bd70528_linear_range warm_charge_curr[] = { +static const struct linear_range warm_charge_curr[] = { { .min = 10, .step = 10, - .vals = 20, - .low_sel = 0, + .min_sel = 0, + .max_sel = 0x12, }, { .min = 200, .step = 25, - .vals = 13, - .low_sel = 0x13, + .min_sel = 0x13, + .max_sel = 0x1f, }, }; @@ -398,56 +398,6 @@ static const struct bd70528_linear_range warm_charge_curr[] = { #define MAX_WARM_CHG_CURR_SEL 0x1f #define MIN_CHG_CURR_SEL 0x0 -static int find_value_for_selector_low(const struct bd70528_linear_range *r, - int selectors, unsigned int sel, - unsigned int *val) -{ - int i; - - for (i = 0; i < selectors; i++) { - if (r[i].low_sel <= sel && r[i].low_sel + r[i].vals >= sel) { - *val = r[i].min + (sel - r[i].low_sel) * r[i].step; - return 0; - } - } - return -EINVAL; -} - -/* - * For BD70528 voltage/current limits we happily accept any value which - * belongs the range. We could check if value matching the selector is - * desired by computing the range min + (sel - sel_low) * range step - but - * I guess it is enough if we use voltage/current which is closest (below) - * the requested? - */ -static int find_selector_for_value_low(const struct bd70528_linear_range *r, - int selectors, unsigned int val, - unsigned int *sel, bool *found) -{ - int i; - int ret = -EINVAL; - - *found = false; - for (i = 0; i < selectors; i++) { - if (r[i].min <= val) { - if (r[i].min + r[i].step * r[i].vals >= val) { - *found = true; - *sel = r[i].low_sel + (val - r[i].min) / - r[i].step; - ret = 0; - break; - } - /* - * If the range max is smaller than requested - * we can set the max supported value from range - */ - *sel = r[i].low_sel + r[i].vals; - ret = 0; - } - } - return ret; -} - static int get_charge_current(struct bd70528_psy *bdpsy, int *ma) { unsigned int sel; @@ -463,9 +413,9 @@ static int get_charge_current(struct bd70528_psy *bdpsy, int *ma) sel &= BD70528_MASK_CHG_CHG_CURR; - ret = find_value_for_selector_low(&warm_charge_curr[0], - ARRAY_SIZE(warm_charge_curr), sel, - ma); + ret = linear_range_get_value_array(&warm_charge_curr[0], + ARRAY_SIZE(warm_charge_curr), + sel, ma); if (ret) { dev_err(bdpsy->dev, "Unknown charge current value 0x%x\n", @@ -491,10 +441,9 @@ static int get_current_limit(struct bd70528_psy *bdpsy, int *ma) sel &= BD70528_MASK_CHG_DCIN_ILIM; - ret = find_value_for_selector_low(¤t_limit_ranges[0], - ARRAY_SIZE(current_limit_ranges), sel, - ma); - + ret = linear_range_get_value_array(¤t_limit_ranges[0], + ARRAY_SIZE(current_limit_ranges), + sel, ma); if (ret) { /* Unspecified values mean 500 mA */ *ma = 500; @@ -588,15 +537,28 @@ static int set_charge_current(struct bd70528_psy *bdpsy, int ma) goto set; } - ret = find_selector_for_value_low(&warm_charge_curr[0], - ARRAY_SIZE(warm_charge_curr), ma, - ®, &found); +/* + * For BD70528 voltage/current limits we happily accept any value which + * belongs the range. We could check if value matching the selector is + * desired by computing the range min + (sel - sel_low) * range step - but + * I guess it is enough if we use voltage/current which is closest (below) + * the requested? + */ + + ret = linear_range_get_selector_low_array(warm_charge_curr, + ARRAY_SIZE(warm_charge_curr), + ma, ®, &found); if (ret) { + dev_err(bdpsy->dev, + "Unsupported charge current %u mA\n", ma); reg = MIN_CHG_CURR_SEL; goto set; } if (!found) { - /* There was a gap in supported values and we hit it */ + /* + * There was a gap in supported values and we hit it. + * Yet a smaller value was found so we use it. + */ dev_warn(bdpsy->dev, "Unsupported charge current %u mA\n", ma); } @@ -648,17 +610,21 @@ static int set_current_limit(struct bd70528_psy *bdpsy, int ma) goto set; } - ret = find_selector_for_value_low(¤t_limit_ranges[0], - ARRAY_SIZE(current_limit_ranges), ma, - ®, &found); + ret = linear_range_get_selector_low_array(current_limit_ranges, + ARRAY_SIZE(current_limit_ranges), + ma, ®, &found); if (ret) { + dev_err(bdpsy->dev, "Unsupported current limit %umA\n", ma); reg = MIN_CURR_LIMIT_SEL; goto set; } if (!found) { - /* There was a gap in supported values and we hit it ?*/ - dev_warn(bdpsy->dev, "Unsupported current limit %umA\n", - ma); + /* + * There was a gap in supported values and we hit it. + * We found a smaller value from ranges and use it. + * Warn user though. + */ + dev_warn(bdpsy->dev, "Unsupported current limit %umA\n", ma); } set: -- cgit v1.2.3 From 5a63b7ba50fd6b7a897bf9353dbf31d579cfe116 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Fri, 8 May 2020 18:47:20 +0300 Subject: power: supply: add battery parameters Add parsing of new device-tree battery bindings. - trickle-charge-current-microamp - precharge-upper-limit-microvolt - re-charge-voltage-microvolt - over-voltage-threshold-microvolt Signed-off-by: Matti Vaittinen Signed-off-by: Sebastian Reichel --- drivers/power/supply/power_supply_core.c | 8 ++++++++ include/linux/power_supply.h | 4 ++++ 2 files changed, 12 insertions(+) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index 1a9a9fae73d3..02b37fe6061c 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -620,10 +620,18 @@ int power_supply_get_battery_info(struct power_supply *psy, &info->voltage_min_design_uv); of_property_read_u32(battery_np, "voltage-max-design-microvolt", &info->voltage_max_design_uv); + of_property_read_u32(battery_np, "trickle-charge-current-microamp", + &info->tricklecharge_current_ua); of_property_read_u32(battery_np, "precharge-current-microamp", &info->precharge_current_ua); + of_property_read_u32(battery_np, "precharge-upper-limit-microvolt", + &info->precharge_voltage_max_uv); of_property_read_u32(battery_np, "charge-term-current-microamp", &info->charge_term_current_ua); + of_property_read_u32(battery_np, "re-charge-voltage-microvolt", + &info->charge_restart_voltage_uv); + of_property_read_u32(battery_np, "over-voltage-threshold-microvolt", + &info->overvoltage_limit_uv); of_property_read_u32(battery_np, "constant-charge-current-max-microamp", &info->constant_charge_current_max_ua); of_property_read_u32(battery_np, "constant-charge-voltage-max-microvolt", diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 6a34df65d4d1..1f60731ec7fe 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -346,8 +346,12 @@ struct power_supply_battery_info { int charge_full_design_uah; /* microAmp-hours */ int voltage_min_design_uv; /* microVolts */ int voltage_max_design_uv; /* microVolts */ + int tricklecharge_current_ua; /* microAmps */ int precharge_current_ua; /* microAmps */ + int precharge_voltage_max_uv; /* microVolts */ int charge_term_current_ua; /* microAmps */ + int charge_restart_voltage_uv; /* microVolts */ + int overvoltage_limit_uv; /* microVolts */ int constant_charge_current_max_ua; /* microAmps */ int constant_charge_voltage_max_uv; /* microVolts */ int factory_internal_resistance_uohm; /* microOhms */ -- cgit v1.2.3 From 0902f8366491b7cf66ac01d8508ed3a12a36faa5 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Fri, 8 May 2020 18:49:26 +0300 Subject: power: supply: Support ROHM bd99954 charger The ROHM BD99954 is a Battery Management LSI for 1-4 cell Lithium-Ion secondary battery intended to be used in space-constraint equipment such as Low profile Notebook PC, Tablets and other applications. BD99954 provides a Dual-source Battery Charger, two port BC1.2 detection and a Battery Monitor. Support ROHM BD99954 Charger IC. Signed-off-by: Matti Vaittinen Reviewed-by: Andy Shevchenko Signed-off-by: Sebastian Reichel --- drivers/power/supply/Kconfig | 9 + drivers/power/supply/Makefile | 1 + drivers/power/supply/bd99954-charger.c | 1142 ++++++++++++++++++++++++++++++++ drivers/power/supply/bd99954-charger.h | 1075 ++++++++++++++++++++++++++++++ 4 files changed, 2227 insertions(+) create mode 100644 drivers/power/supply/bd99954-charger.c create mode 100644 drivers/power/supply/bd99954-charger.h (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 9448d6c66cae..d058f19922ad 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -713,6 +713,15 @@ config CHARGER_BD70528 information and altering charger configurations from charger block of the ROHM BD70528 Power Management IC. +config CHARGER_BD99954 + tristate "ROHM bd99954 charger driver" + depends on I2C + select LINEAR_RANGES + help + Say Y here to enable support for getting battery and charger + information and altering charger configurations from the ROHM + BD99954 charger IC. + config CHARGER_WILCO tristate "Wilco EC based charger for ChromeOS" depends on WILCO_EC diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index 69727a10e835..bb934545103d 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -92,4 +92,5 @@ obj-$(CONFIG_CHARGER_SC2731) += sc2731_charger.o obj-$(CONFIG_FUEL_GAUGE_SC27XX) += sc27xx_fuel_gauge.o obj-$(CONFIG_CHARGER_UCS1002) += ucs1002_power.o obj-$(CONFIG_CHARGER_BD70528) += bd70528-charger.o +obj-$(CONFIG_CHARGER_BD99954) += bd99954-charger.o obj-$(CONFIG_CHARGER_WILCO) += wilco-charger.o diff --git a/drivers/power/supply/bd99954-charger.c b/drivers/power/supply/bd99954-charger.c new file mode 100644 index 000000000000..3da39c7293e3 --- /dev/null +++ b/drivers/power/supply/bd99954-charger.c @@ -0,0 +1,1142 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ROHM BD99954 charger driver + * + * Copyright (C) 2020 Rohm Semiconductors + * Originally written by: + * Mikko Mutanen + * Markus Laine + * Bugs added by: + * Matti Vaittinen + */ + +/* + * The battery charging profile of BD99954. + * + * Curve (1) represents charging current. + * Curve (2) represents battery voltage. + * + * The BD99954 data sheet divides charging to three phases. + * a) Trickle-charge with constant current (8). + * b) pre-charge with constant current (6) + * c) fast-charge, first with constant current (5) phase. After + * the battery voltage has reached target level (4) we have constant + * voltage phase until charging current has dropped to termination + * level (7) + * + * V ^ ^ I + * . . + * . . + *(4)` `.` ` ` ` ` ` ` ` ` ` ` ` ` ` ----------------------------. + * . :/ . + * . o----+/:/ ` ` ` ` ` ` ` ` ` ` ` ` `.` ` (5) + * . + :: + . + * . + /- -- . + * . +`/- + . + * . o/- -: . + * . .s. +` . + * . .--+ `/ . + * . ..`` + .: . + * . -` + -- . + * . (2) ...`` + :- . + * . ...`` + -: . + *(3)` `.`."" ` ` ` `+-------- ` ` ` ` ` ` `.:` ` ` ` ` ` ` ` ` .` ` (6) + * . + `:. . + * . + -: . + * . + -:. . + * . + .--. . + * . (1) + `.+` ` ` `.` ` (7) + * -..............` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` + ` ` ` .` ` (8) + * . + - + * -------------------------------------------------+++++++++--> + * | trickle | pre | fast | + * + * Details of DT properties for different limits can be found from BD99954 + * device tree binding documentation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bd99954-charger.h" + +struct battery_data { + u16 precharge_current; /* Trickle-charge Current */ + u16 fc_reg_voltage; /* Fast Charging Regulation Voltage */ + u16 voltage_min; + u16 voltage_max; +}; + +/* Initial field values, converted to initial register values */ +struct bd9995x_init_data { + u16 vsysreg_set; /* VSYS Regulation Setting */ + u16 ibus_lim_set; /* VBUS input current limitation */ + u16 icc_lim_set; /* VCC/VACP Input Current Limit Setting */ + u16 itrich_set; /* Trickle-charge Current Setting */ + u16 iprech_set; /* Pre-Charge Current Setting */ + u16 ichg_set; /* Fast-Charge constant current */ + u16 vfastchg_reg_set1; /* Fast Charging Regulation Voltage */ + u16 vprechg_th_set; /* Pre-charge Voltage Threshold Setting */ + u16 vrechg_set; /* Re-charge Battery Voltage Setting */ + u16 vbatovp_set; /* Battery Over Voltage Threshold Setting */ + u16 iterm_set; /* Charging termination current */ +}; + +struct bd9995x_state { + u8 online; + u16 chgstm_status; + u16 vbat_vsys_status; + u16 vbus_vcc_status; +}; + +struct bd9995x_device { + struct i2c_client *client; + struct device *dev; + struct power_supply *charger; + + struct regmap *rmap; + struct regmap_field *rmap_fields[F_MAX_FIELDS]; + + int chip_id; + int chip_rev; + struct bd9995x_init_data init_data; + struct bd9995x_state state; + + struct mutex lock; /* Protect state data */ +}; + +static const struct regmap_range bd9995x_readonly_reg_ranges[] = { + regmap_reg_range(CHGSTM_STATUS, SEL_ILIM_VAL), + regmap_reg_range(IOUT_DACIN_VAL, IOUT_DACIN_VAL), + regmap_reg_range(VCC_UCD_STATUS, VCC_IDD_STATUS), + regmap_reg_range(VBUS_UCD_STATUS, VBUS_IDD_STATUS), + regmap_reg_range(CHIP_ID, CHIP_REV), + regmap_reg_range(SYSTEM_STATUS, SYSTEM_STATUS), + regmap_reg_range(IBATP_VAL, VBAT_AVE_VAL), + regmap_reg_range(VTH_VAL, EXTIADP_AVE_VAL), +}; + +static const struct regmap_access_table bd9995x_writeable_regs = { + .no_ranges = bd9995x_readonly_reg_ranges, + .n_no_ranges = ARRAY_SIZE(bd9995x_readonly_reg_ranges), +}; + +static const struct regmap_range bd9995x_volatile_reg_ranges[] = { + regmap_reg_range(CHGSTM_STATUS, WDT_STATUS), + regmap_reg_range(VCC_UCD_STATUS, VCC_IDD_STATUS), + regmap_reg_range(VBUS_UCD_STATUS, VBUS_IDD_STATUS), + regmap_reg_range(INT0_STATUS, INT7_STATUS), + regmap_reg_range(SYSTEM_STATUS, SYSTEM_CTRL_SET), + regmap_reg_range(IBATP_VAL, EXTIADP_AVE_VAL), /* Measurement regs */ +}; + +static const struct regmap_access_table bd9995x_volatile_regs = { + .yes_ranges = bd9995x_volatile_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(bd9995x_volatile_reg_ranges), +}; + +static const struct regmap_range_cfg regmap_range_cfg[] = { + { + .selector_reg = MAP_SET, + .selector_mask = 0xFFFF, + .selector_shift = 0, + .window_start = 0, + .window_len = 0x100, + .range_min = 0 * 0x100, + .range_max = 3 * 0x100, + }, +}; + +static const struct regmap_config bd9995x_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .reg_stride = 1, + + .max_register = 3 * 0x100, + .cache_type = REGCACHE_RBTREE, + + .ranges = regmap_range_cfg, + .num_ranges = ARRAY_SIZE(regmap_range_cfg), + .val_format_endian = REGMAP_ENDIAN_LITTLE, + .wr_table = &bd9995x_writeable_regs, + .volatile_table = &bd9995x_volatile_regs, +}; + +enum bd9995x_chrg_fault { + CHRG_FAULT_NORMAL, + CHRG_FAULT_INPUT, + CHRG_FAULT_THERMAL_SHUTDOWN, + CHRG_FAULT_TIMER_EXPIRED, +}; + +static int bd9995x_get_prop_batt_health(struct bd9995x_device *bd) +{ + int ret, tmp; + + ret = regmap_field_read(bd->rmap_fields[F_BATTEMP], &tmp); + if (ret) + return POWER_SUPPLY_HEALTH_UNKNOWN; + + /* TODO: Check these against datasheet page 34 */ + + switch (tmp) { + case ROOM: + return POWER_SUPPLY_HEALTH_GOOD; + case HOT1: + case HOT2: + case HOT3: + return POWER_SUPPLY_HEALTH_OVERHEAT; + case COLD1: + case COLD2: + return POWER_SUPPLY_HEALTH_COLD; + case TEMP_DIS: + case BATT_OPEN: + default: + return POWER_SUPPLY_HEALTH_UNKNOWN; + } +} + +static int bd9995x_get_prop_charge_type(struct bd9995x_device *bd) +{ + int ret, tmp; + + ret = regmap_field_read(bd->rmap_fields[F_CHGSTM_STATE], &tmp); + if (ret) + return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; + + switch (tmp) { + case CHGSTM_TRICKLE_CHARGE: + case CHGSTM_PRE_CHARGE: + return POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + case CHGSTM_FAST_CHARGE: + return POWER_SUPPLY_CHARGE_TYPE_FAST; + case CHGSTM_TOP_OFF: + case CHGSTM_DONE: + case CHGSTM_SUSPEND: + return POWER_SUPPLY_CHARGE_TYPE_NONE; + default: /* Rest of the states are error related, no charging */ + return POWER_SUPPLY_CHARGE_TYPE_NONE; + } +} + +static bool bd9995x_get_prop_batt_present(struct bd9995x_device *bd) +{ + int ret, tmp; + + ret = regmap_field_read(bd->rmap_fields[F_BATTEMP], &tmp); + if (ret) + return false; + + return tmp != BATT_OPEN; +} + +static int bd9995x_get_prop_batt_voltage(struct bd9995x_device *bd) +{ + int ret, tmp; + + ret = regmap_field_read(bd->rmap_fields[F_VBAT_VAL], &tmp); + if (ret) + return 0; + + tmp = min(tmp, 19200); + + return tmp * 1000; +} + +static int bd9995x_get_prop_batt_current(struct bd9995x_device *bd) +{ + int ret, tmp; + + ret = regmap_field_read(bd->rmap_fields[F_IBATP_VAL], &tmp); + if (ret) + return 0; + + return tmp * 1000; +} + +#define DEFAULT_BATTERY_TEMPERATURE 250 + +static int bd9995x_get_prop_batt_temp(struct bd9995x_device *bd) +{ + int ret, tmp; + + ret = regmap_field_read(bd->rmap_fields[F_THERM_VAL], &tmp); + if (ret) + return DEFAULT_BATTERY_TEMPERATURE; + + return (200 - tmp) * 10; +} + +static int bd9995x_power_supply_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret, tmp; + struct bd9995x_device *bd = power_supply_get_drvdata(psy); + struct bd9995x_state state; + + mutex_lock(&bd->lock); + state = bd->state; + mutex_unlock(&bd->lock); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + switch (state.chgstm_status) { + case CHGSTM_TRICKLE_CHARGE: + case CHGSTM_PRE_CHARGE: + case CHGSTM_FAST_CHARGE: + case CHGSTM_TOP_OFF: + val->intval = POWER_SUPPLY_STATUS_CHARGING; + break; + + case CHGSTM_DONE: + val->intval = POWER_SUPPLY_STATUS_FULL; + break; + + case CHGSTM_SUSPEND: + case CHGSTM_TEMPERATURE_ERROR_1: + case CHGSTM_TEMPERATURE_ERROR_2: + case CHGSTM_TEMPERATURE_ERROR_3: + case CHGSTM_TEMPERATURE_ERROR_4: + case CHGSTM_TEMPERATURE_ERROR_5: + case CHGSTM_TEMPERATURE_ERROR_6: + case CHGSTM_TEMPERATURE_ERROR_7: + case CHGSTM_THERMAL_SHUT_DOWN_1: + case CHGSTM_THERMAL_SHUT_DOWN_2: + case CHGSTM_THERMAL_SHUT_DOWN_3: + case CHGSTM_THERMAL_SHUT_DOWN_4: + case CHGSTM_THERMAL_SHUT_DOWN_5: + case CHGSTM_THERMAL_SHUT_DOWN_6: + case CHGSTM_THERMAL_SHUT_DOWN_7: + case CHGSTM_BATTERY_ERROR: + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + + default: + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + break; + } + break; + + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = BD9995X_MANUFACTURER; + break; + + case POWER_SUPPLY_PROP_ONLINE: + val->intval = state.online; + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + ret = regmap_field_read(bd->rmap_fields[F_IBATP_VAL], &tmp); + if (ret) + return ret; + val->intval = tmp * 1000; + break; + + case POWER_SUPPLY_PROP_CHARGE_AVG: + ret = regmap_field_read(bd->rmap_fields[F_IBATP_AVE_VAL], &tmp); + if (ret) + return ret; + val->intval = tmp * 1000; + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + /* + * Currently the DT uses this property to give the + * target current for fast-charging constant current phase. + * I think it is correct in a sense. + * + * Yet, this prop we read and return here is the programmed + * safety limit for combined input currents. This feels + * also correct in a sense. + * + * However, this results a mismatch to DT value and value + * read from sysfs. + */ + ret = regmap_field_read(bd->rmap_fields[F_SEL_ILIM_VAL], &tmp); + if (ret) + return ret; + val->intval = tmp * 1000; + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + if (!state.online) { + val->intval = 0; + break; + } + + ret = regmap_field_read(bd->rmap_fields[F_VFASTCHG_REG_SET1], + &tmp); + if (ret) + return ret; + + /* + * The actual range : 2560 to 19200 mV. No matter what the + * register says + */ + val->intval = clamp_val(tmp << 4, 2560, 19200); + val->intval *= 1000; + break; + + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: + ret = regmap_field_read(bd->rmap_fields[F_ITERM_SET], &tmp); + if (ret) + return ret; + /* Start step is 64 mA */ + val->intval = tmp << 6; + /* Maximum is 1024 mA - no matter what register says */ + val->intval = min(val->intval, 1024); + val->intval *= 1000; + break; + + /* Battery properties which we access through charger */ + case POWER_SUPPLY_PROP_PRESENT: + val->intval = bd9995x_get_prop_batt_present(bd); + break; + + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = bd9995x_get_prop_batt_voltage(bd); + break; + + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = bd9995x_get_prop_batt_current(bd); + break; + + case POWER_SUPPLY_PROP_CHARGE_TYPE: + val->intval = bd9995x_get_prop_charge_type(bd); + break; + + case POWER_SUPPLY_PROP_HEALTH: + val->intval = bd9995x_get_prop_batt_health(bd); + break; + + case POWER_SUPPLY_PROP_TEMP: + val->intval = bd9995x_get_prop_batt_temp(bd); + break; + + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = "bd99954"; + break; + + default: + return -EINVAL; + + } + + return 0; +} + +static int bd9995x_get_chip_state(struct bd9995x_device *bd, + struct bd9995x_state *state) +{ + int i, ret, tmp; + struct { + struct regmap_field *id; + u16 *data; + } state_fields[] = { + { + bd->rmap_fields[F_CHGSTM_STATE], &state->chgstm_status, + }, { + bd->rmap_fields[F_VBAT_VSYS_STATUS], + &state->vbat_vsys_status, + }, { + bd->rmap_fields[F_VBUS_VCC_STATUS], + &state->vbus_vcc_status, + }, + }; + + + for (i = 0; i < ARRAY_SIZE(state_fields); i++) { + ret = regmap_field_read(state_fields[i].id, &tmp); + if (ret) + return ret; + + *state_fields[i].data = tmp; + } + + if (state->vbus_vcc_status & STATUS_VCC_DET || + state->vbus_vcc_status & STATUS_VBUS_DET) + state->online = 1; + else + state->online = 0; + + return 0; +} + +static irqreturn_t bd9995x_irq_handler_thread(int irq, void *private) +{ + struct bd9995x_device *bd = private; + int ret, status, mask, i; + unsigned long tmp; + struct bd9995x_state state; + + /* + * The bd9995x does not seem to generate big amount of interrupts. + * The logic regarding which interrupts can cause relevant + * status changes seem to be pretty complex. + * + * So lets implement really simple and hopefully bullet-proof handler: + * It does not really matter which IRQ we handle, we just go and + * re-read all interesting statuses + give the framework a nudge. + * + * Other option would be building a _complex_ and error prone logic + * trying to decide what could have been changed (resulting this IRQ + * we are now handling). During the normal operation the BD99954 does + * not seem to be generating much of interrupts so benefit from such + * logic would probably be minimal. + */ + + ret = regmap_read(bd->rmap, INT0_STATUS, &status); + if (ret) { + dev_err(bd->dev, "Failed to read IRQ status\n"); + return IRQ_NONE; + } + + ret = regmap_field_read(bd->rmap_fields[F_INT0_SET], &mask); + if (ret) { + dev_err(bd->dev, "Failed to read IRQ mask\n"); + return IRQ_NONE; + } + + /* Handle only IRQs that are not masked */ + status &= mask; + tmp = status; + + /* Lowest bit does not represent any sub-registers */ + tmp >>= 1; + + /* + * Mask and ack IRQs we will handle (+ the idiot bit) + */ + ret = regmap_field_write(bd->rmap_fields[F_INT0_SET], 0); + if (ret) { + dev_err(bd->dev, "Failed to mask F_INT0\n"); + return IRQ_NONE; + } + + ret = regmap_write(bd->rmap, INT0_STATUS, status); + if (ret) { + dev_err(bd->dev, "Failed to ack F_INT0\n"); + goto err_umask; + } + + for_each_set_bit(i, &tmp, 7) { + int sub_status, sub_mask; + int sub_status_reg[] = { + INT1_STATUS, INT2_STATUS, INT3_STATUS, INT4_STATUS, + INT5_STATUS, INT6_STATUS, INT7_STATUS, + }; + struct regmap_field *sub_mask_f[] = { + bd->rmap_fields[F_INT1_SET], + bd->rmap_fields[F_INT2_SET], + bd->rmap_fields[F_INT3_SET], + bd->rmap_fields[F_INT4_SET], + bd->rmap_fields[F_INT5_SET], + bd->rmap_fields[F_INT6_SET], + bd->rmap_fields[F_INT7_SET], + }; + + /* Clear sub IRQs */ + ret = regmap_read(bd->rmap, sub_status_reg[i], &sub_status); + if (ret) { + dev_err(bd->dev, "Failed to read IRQ sub-status\n"); + goto err_umask; + } + + ret = regmap_field_read(sub_mask_f[i], &sub_mask); + if (ret) { + dev_err(bd->dev, "Failed to read IRQ sub-mask\n"); + goto err_umask; + } + + /* Ack active sub-statuses */ + sub_status &= sub_mask; + + ret = regmap_write(bd->rmap, sub_status_reg[i], sub_status); + if (ret) { + dev_err(bd->dev, "Failed to ack sub-IRQ\n"); + goto err_umask; + } + } + + ret = regmap_field_write(bd->rmap_fields[F_INT0_SET], mask); + if (ret) + /* May as well retry once */ + goto err_umask; + + /* Read whole chip state */ + ret = bd9995x_get_chip_state(bd, &state); + if (ret < 0) { + dev_err(bd->dev, "Failed to read chip state\n"); + } else { + mutex_lock(&bd->lock); + bd->state = state; + mutex_unlock(&bd->lock); + + power_supply_changed(bd->charger); + } + + return IRQ_HANDLED; + +err_umask: + ret = regmap_field_write(bd->rmap_fields[F_INT0_SET], mask); + if (ret) + dev_err(bd->dev, + "Failed to un-mask F_INT0 - IRQ permanently disabled\n"); + + return IRQ_NONE; +} + +static int __bd9995x_chip_reset(struct bd9995x_device *bd) +{ + int ret, state; + int rst_check_counter = 10; + u16 tmp = ALLRST | OTPLD; + + ret = regmap_raw_write(bd->rmap, SYSTEM_CTRL_SET, &tmp, 2); + if (ret < 0) + return ret; + + do { + ret = regmap_field_read(bd->rmap_fields[F_OTPLD_STATE], &state); + if (ret) + return ret; + + msleep(10); + } while (state == 0 && --rst_check_counter); + + if (!rst_check_counter) { + dev_err(bd->dev, "chip reset not completed\n"); + return -ETIMEDOUT; + } + + tmp = 0; + ret = regmap_raw_write(bd->rmap, SYSTEM_CTRL_SET, &tmp, 2); + + return ret; +} + +static int bd9995x_hw_init(struct bd9995x_device *bd) +{ + int ret; + int i; + struct bd9995x_state state; + struct bd9995x_init_data *id = &bd->init_data; + + const struct { + enum bd9995x_fields id; + u16 value; + } init_data[] = { + /* Enable the charging trigger after SDP charger attached */ + {F_SDP_CHG_TRIG_EN, 1}, + /* Enable charging trigger after SDP charger attached */ + {F_SDP_CHG_TRIG, 1}, + /* Disable charging trigger by BC1.2 detection */ + {F_VBUS_BC_DISEN, 1}, + /* Disable charging trigger by BC1.2 detection */ + {F_VCC_BC_DISEN, 1}, + /* Disable automatic limitation of the input current */ + {F_ILIM_AUTO_DISEN, 1}, + /* Select current limitation when SDP charger attached*/ + {F_SDP_500_SEL, 1}, + /* Select current limitation when DCP charger attached */ + {F_DCP_2500_SEL, 1}, + {F_VSYSREG_SET, id->vsysreg_set}, + /* Activate USB charging and DC/DC converter */ + {F_USB_SUS, 0}, + /* DCDC clock: 1200 kHz*/ + {F_DCDC_CLK_SEL, 3}, + /* Enable charging */ + {F_CHG_EN, 1}, + /* Disable Input current Limit setting voltage measurement */ + {F_EXTIADPEN, 0}, + /* Disable input current limiting */ + {F_VSYS_PRIORITY, 1}, + {F_IBUS_LIM_SET, id->ibus_lim_set}, + {F_ICC_LIM_SET, id->icc_lim_set}, + /* Charge Termination Current Setting to 0*/ + {F_ITERM_SET, id->iterm_set}, + /* Trickle-charge Current Setting */ + {F_ITRICH_SET, id->itrich_set}, + /* Pre-charge Current setting */ + {F_IPRECH_SET, id->iprech_set}, + /* Fast Charge Current for constant current phase */ + {F_ICHG_SET, id->ichg_set}, + /* Fast Charge Voltage Regulation Setting */ + {F_VFASTCHG_REG_SET1, id->vfastchg_reg_set1}, + /* Set Pre-charge Voltage Threshold for trickle charging. */ + {F_VPRECHG_TH_SET, id->vprechg_th_set}, + {F_VRECHG_SET, id->vrechg_set}, + {F_VBATOVP_SET, id->vbatovp_set}, + /* Reverse buck boost voltage Setting */ + {F_VRBOOST_SET, 0}, + /* Disable fast-charging watchdog */ + {F_WDT_FST, 0}, + /* Disable pre-charging watchdog */ + {F_WDT_PRE, 0}, + /* Power save off */ + {F_POWER_SAVE_MODE, 0}, + {F_INT1_SET, INT1_ALL}, + {F_INT2_SET, INT2_ALL}, + {F_INT3_SET, INT3_ALL}, + {F_INT4_SET, INT4_ALL}, + {F_INT5_SET, INT5_ALL}, + {F_INT6_SET, INT6_ALL}, + {F_INT7_SET, INT7_ALL}, + }; + + /* + * Currently we initialize charger to a known state at startup. + * If we want to allow for example the boot code to initialize + * charger we should get rid of this. + */ + ret = __bd9995x_chip_reset(bd); + if (ret < 0) + return ret; + + /* Initialize currents/voltages and other parameters */ + for (i = 0; i < ARRAY_SIZE(init_data); i++) { + ret = regmap_field_write(bd->rmap_fields[init_data[i].id], + init_data[i].value); + if (ret) { + dev_err(bd->dev, "failed to initialize charger (%d)\n", + ret); + return ret; + } + } + + ret = bd9995x_get_chip_state(bd, &state); + if (ret < 0) + return ret; + + mutex_lock(&bd->lock); + bd->state = state; + mutex_unlock(&bd->lock); + + return 0; +} + +static enum power_supply_property bd9995x_power_supply_props[] = { + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, + POWER_SUPPLY_PROP_CHARGE_AVG, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, + POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, + /* Battery props we access through charger */ + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_MODEL_NAME, +}; + +static const struct power_supply_desc bd9995x_power_supply_desc = { + .name = "bd9995x-charger", + .type = POWER_SUPPLY_TYPE_USB, + .properties = bd9995x_power_supply_props, + .num_properties = ARRAY_SIZE(bd9995x_power_supply_props), + .get_property = bd9995x_power_supply_get_property, +}; + +/* + * Limit configurations for vbus-input-current and vcc-vacp-input-current + * Minimum limit is 0 uA. Max is 511 * 32000 uA = 16352000 uA. This is + * configured by writing a register so that each increment in register + * value equals to 32000 uA limit increment. + * + * Eg, value 0x0 is limit 0, value 0x1 is limit 32000, ... + * Describe the setting in linear_range table. + */ +static const struct linear_range input_current_limit_ranges[] = { + { + .min = 0, + .step = 32000, + .min_sel = 0x0, + .max_sel = 0x1ff, + }, +}; + +/* Possible trickle, pre-charging and termination current values */ +static const struct linear_range charging_current_ranges[] = { + { + .min = 0, + .step = 64000, + .min_sel = 0x0, + .max_sel = 0x10, + }, { + .min = 1024000, + .step = 0, + .min_sel = 0x11, + .max_sel = 0x1f, + }, +}; + +/* + * Fast charging voltage regulation, starting re-charging limit + * and battery over voltage protection have same possible values + */ +static const struct linear_range charge_voltage_regulation_ranges[] = { + { + .min = 2560000, + .step = 0, + .min_sel = 0, + .max_sel = 0xA0, + }, { + .min = 2560000, + .step = 16000, + .min_sel = 0xA0, + .max_sel = 0x4B0, + }, { + .min = 19200000, + .step = 0, + .min_sel = 0x4B0, + .max_sel = 0x7FF, + }, +}; + +/* Possible VSYS voltage regulation values */ +static const struct linear_range vsys_voltage_regulation_ranges[] = { + { + .min = 2560000, + .step = 0, + .min_sel = 0, + .max_sel = 0x28, + }, { + .min = 2560000, + .step = 64000, + .min_sel = 0x28, + .max_sel = 0x12C, + }, { + .min = 19200000, + .step = 0, + .min_sel = 0x12C, + .max_sel = 0x1FF, + }, +}; + +/* Possible settings for switching from trickle to pre-charging limits */ +static const struct linear_range trickle_to_pre_threshold_ranges[] = { + { + .min = 2048000, + .step = 0, + .min_sel = 0, + .max_sel = 0x20, + }, { + .min = 2048000, + .step = 64000, + .min_sel = 0x20, + .max_sel = 0x12C, + }, { + .min = 19200000, + .step = 0, + .min_sel = 0x12C, + .max_sel = 0x1FF + } +}; + +/* Possible current values for fast-charging constant current phase */ +static const struct linear_range fast_charge_current_ranges[] = { + { + .min = 0, + .step = 64000, + .min_sel = 0, + .max_sel = 0xFF, + } +}; + +struct battery_init { + const char *name; + int *info_data; + const struct linear_range *range; + int ranges; + u16 *data; +}; + +struct dt_init { + char *prop; + const struct linear_range *range; + int ranges; + u16 *data; +}; + +static int bd9995x_fw_probe(struct bd9995x_device *bd) +{ + int ret; + struct power_supply_battery_info info; + u32 property; + int i; + int regval; + bool found; + struct bd9995x_init_data *init = &bd->init_data; + struct battery_init battery_inits[] = { + { + .name = "trickle-charging current", + .info_data = &info.tricklecharge_current_ua, + .range = &charging_current_ranges[0], + .ranges = 2, + .data = &init->itrich_set, + }, { + .name = "pre-charging current", + .info_data = &info.precharge_current_ua, + .range = &charging_current_ranges[0], + .ranges = 2, + .data = &init->iprech_set, + }, { + .name = "pre-to-trickle charge voltage threshold", + .info_data = &info.precharge_voltage_max_uv, + .range = &trickle_to_pre_threshold_ranges[0], + .ranges = 2, + .data = &init->vprechg_th_set, + }, { + .name = "charging termination current", + .info_data = &info.charge_term_current_ua, + .range = &charging_current_ranges[0], + .ranges = 2, + .data = &init->iterm_set, + }, { + .name = "charging re-start voltage", + .info_data = &info.charge_restart_voltage_uv, + .range = &charge_voltage_regulation_ranges[0], + .ranges = 2, + .data = &init->vrechg_set, + }, { + .name = "battery overvoltage limit", + .info_data = &info.overvoltage_limit_uv, + .range = &charge_voltage_regulation_ranges[0], + .ranges = 2, + .data = &init->vbatovp_set, + }, { + .name = "fast-charging max current", + .info_data = &info.constant_charge_current_max_ua, + .range = &fast_charge_current_ranges[0], + .ranges = 1, + .data = &init->ichg_set, + }, { + .name = "fast-charging voltage", + .info_data = &info.constant_charge_voltage_max_uv, + .range = &charge_voltage_regulation_ranges[0], + .ranges = 2, + .data = &init->vfastchg_reg_set1, + }, + }; + struct dt_init props[] = { + { + .prop = "rohm,vsys-regulation-microvolt", + .range = &vsys_voltage_regulation_ranges[0], + .ranges = 2, + .data = &init->vsysreg_set, + }, { + .prop = "rohm,vbus-input-current-limit-microamp", + .range = &input_current_limit_ranges[0], + .ranges = 1, + .data = &init->ibus_lim_set, + }, { + .prop = "rohm,vcc-input-current-limit-microamp", + .range = &input_current_limit_ranges[0], + .ranges = 1, + .data = &init->icc_lim_set, + }, + }; + + /* + * The power_supply_get_battery_info() does not support getting values + * from ACPI. Let's fix it if ACPI is required here. + */ + ret = power_supply_get_battery_info(bd->charger, &info); + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(battery_inits); i++) { + int val = *battery_inits[i].info_data; + const struct linear_range *range = battery_inits[i].range; + int ranges = battery_inits[i].ranges; + + if (val == -EINVAL) + continue; + + ret = linear_range_get_selector_low_array(range, ranges, val, + ®val, &found); + if (ret) { + dev_err(bd->dev, "Unsupported value for %s\n", + battery_inits[i].name); + + power_supply_put_battery_info(bd->charger, &info); + return -EINVAL; + } + if (!found) { + dev_warn(bd->dev, + "Unsupported value for %s - using smaller\n", + battery_inits[i].name); + } + *(battery_inits[i].data) = regval; + } + + power_supply_put_battery_info(bd->charger, &info); + + for (i = 0; i < ARRAY_SIZE(props); i++) { + ret = device_property_read_u32(bd->dev, props[i].prop, + &property); + if (ret < 0) { + dev_err(bd->dev, "failed to read %s", props[i].prop); + + return ret; + } + + ret = linear_range_get_selector_low_array(props[i].range, + props[i].ranges, + property, ®val, + &found); + if (ret) { + dev_err(bd->dev, "Unsupported value for '%s'\n", + props[i].prop); + + return -EINVAL; + } + + if (!found) { + dev_warn(bd->dev, + "Unsupported value for '%s' - using smaller\n", + props[i].prop); + } + + *(props[i].data) = regval; + } + + return 0; +} + +void bd9995x_chip_reset(void *bd) +{ + __bd9995x_chip_reset(bd); +} + +static int bd9995x_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct bd9995x_device *bd; + struct power_supply_config psy_cfg = {}; + int ret; + int i; + + bd = devm_kzalloc(dev, sizeof(*bd), GFP_KERNEL); + if (!bd) + return -ENOMEM; + + bd->client = client; + bd->dev = dev; + psy_cfg.drv_data = bd; + psy_cfg.of_node = dev->of_node; + + mutex_init(&bd->lock); + + bd->rmap = devm_regmap_init_i2c(client, &bd9995x_regmap_config); + if (IS_ERR(bd->rmap)) { + dev_err(dev, "Failed to setup register access via i2c\n"); + return PTR_ERR(bd->rmap); + } + + for (i = 0; i < ARRAY_SIZE(bd9995x_reg_fields); i++) { + const struct reg_field *reg_fields = bd9995x_reg_fields; + + bd->rmap_fields[i] = devm_regmap_field_alloc(dev, bd->rmap, + reg_fields[i]); + if (IS_ERR(bd->rmap_fields[i])) { + dev_err(dev, "cannot allocate regmap field\n"); + return PTR_ERR(bd->rmap_fields[i]); + } + } + + i2c_set_clientdata(client, bd); + + ret = regmap_field_read(bd->rmap_fields[F_CHIP_ID], &bd->chip_id); + if (ret) { + dev_err(dev, "Cannot read chip ID.\n"); + return ret; + } + + if (bd->chip_id != BD99954_ID) { + dev_err(dev, "Chip with ID=0x%x, not supported!\n", + bd->chip_id); + return -ENODEV; + } + + ret = regmap_field_read(bd->rmap_fields[F_CHIP_REV], &bd->chip_rev); + if (ret) { + dev_err(dev, "Cannot read revision.\n"); + return ret; + } + + dev_info(bd->dev, "Found BD99954 chip rev %d\n", bd->chip_rev); + + /* + * We need to init the psy before we can call + * power_supply_get_battery_info() for it + */ + bd->charger = devm_power_supply_register(bd->dev, + &bd9995x_power_supply_desc, + &psy_cfg); + if (IS_ERR(bd->charger)) { + dev_err(dev, "Failed to register power supply\n"); + return PTR_ERR(bd->charger); + } + + ret = bd9995x_fw_probe(bd); + if (ret < 0) { + dev_err(dev, "Cannot read device properties.\n"); + return ret; + } + + ret = bd9995x_hw_init(bd); + if (ret < 0) { + dev_err(dev, "Cannot initialize the chip.\n"); + return ret; + } + + ret = devm_add_action_or_reset(dev, bd9995x_chip_reset, bd); + if (ret) + return ret; + + return devm_request_threaded_irq(dev, client->irq, NULL, + bd9995x_irq_handler_thread, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + BD9995X_IRQ_PIN, bd); +} + +static const struct of_device_id bd9995x_of_match[] = { + { .compatible = "rohm,bd99954", }, + { } +}; +MODULE_DEVICE_TABLE(of, bd9995x_of_match); + +static struct i2c_driver bd9995x_driver = { + .driver = { + .name = "bd9995x-charger", + .of_match_table = bd9995x_of_match, + }, + .probe_new = bd9995x_probe, +}; +module_i2c_driver(bd9995x_driver); + +MODULE_AUTHOR("Laine Markus "); +MODULE_DESCRIPTION("ROHM BD99954 charger driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/supply/bd99954-charger.h b/drivers/power/supply/bd99954-charger.h new file mode 100644 index 000000000000..f58897925383 --- /dev/null +++ b/drivers/power/supply/bd99954-charger.h @@ -0,0 +1,1075 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2020 ROHM Semiconductors */ +#ifndef BD99954_CHARGER_H +#define BD99954_CHARGER_H + +#include + +#define BD9995X_MANUFACTURER "Rohm Semiconductor" +#define BD9995X_IRQ_PIN "bd9995x_irq" + +#define BD9995X_VSYS_PRECHARGE_OFFSET_MV 200 + +#define BD99954_ID 0x346 +#define BD99955_ID 0x221 +#define BD99956_ID 0x331 + +/* Battery Charger Commands */ +#define CHARGING_CURRENT 0x14 +#define CHARGING_VOLTAGE 0x15 +#define PROTECT_SET 0x3E +#define MAP_SET 0x3F + +/* Extended commands */ +#define CHGSTM_STATUS 0x100 +#define VBAT_VSYS_STATUS 0x101 +#define VBUS_VCC_STATUS 0x102 +#define CHGOP_STATUS 0x103 +#define WDT_STATUS 0x104 +#define CUR_ILIM_VAL 0x105 +#define SEL_ILIM_VAL 0x106 +#define IBUS_LIM_SET 0x107 +#define ICC_LIM_SET 0x108 +#define IOTG_LIM_SET 0x109 +#define VIN_CTRL_SET 0x10A +#define CHGOP_SET1 0x10B +#define CHGOP_SET2 0x10C +#define VBUSCLPS_TH_SET 0x10D +#define VCCCLPS_TH_SET 0x10E +#define CHGWDT_SET 0x10F +#define BATTWDT_SET 0x110 +#define VSYSREG_SET 0x111 +#define VSYSVAL_THH_SET 0x112 +#define VSYSVAL_THL_SET 0x113 +#define ITRICH_SET 0x114 +#define IPRECH_SET 0x115 +#define ICHG_SET 0x116 +#define ITERM_SET 0x117 +#define VPRECHG_TH_SET 0x118 +#define VRBOOST_SET 0x119 +#define VFASTCHG_REG_SET1 0x11A +#define VFASTCHG_REG_SET2 0x11B +#define VFASTCHG_REG_SET3 0x11C +#define VRECHG_SET 0x11D +#define VBATOVP_SET 0x11E +#define IBATSHORT_SET 0x11F +#define PROCHOT_CTRL_SET 0x120 +#define PROCHOT_ICRIT_SET 0x121 +#define PROCHOT_INORM_SET 0x122 +#define PROCHOT_IDCHG_SET 0x123 +#define PROCHOT_VSYS_SET 0x124 +#define PMON_IOUT_CTRL_SET 0x125 +#define PMON_DACIN_VAL 0x126 +#define IOUT_DACIN_VAL 0x127 +#define VCC_UCD_SET 0x128 +#define VCC_UCD_STATUS 0x129 +#define VCC_IDD_STATUS 0x12A +#define VCC_UCD_FCTRL_SET 0x12B +#define VCC_UCD_FCTRL_EN 0x12C +#define VBUS_UCD_SET 0x130 +#define VBUS_UCD_STATUS 0x131 +#define VBUS_IDD_STATUS 0x132 +#define VBUS_UCD_FCTRL_SET 0x133 +#define VBUS_UCD_FCTRL_EN 0x134 +#define CHIP_ID 0x138 +#define CHIP_REV 0x139 +#define IC_SET1 0x13A +#define IC_SET2 0x13B +#define SYSTEM_STATUS 0x13C +#define SYSTEM_CTRL_SET 0x13D +#define VM_CTRL_SET 0x140 +#define THERM_WINDOW_SET1 0x141 +#define THERM_WINDOW_SET2 0x142 +#define THERM_WINDOW_SET3 0x143 +#define THERM_WINDOW_SET4 0x144 +#define THERM_WINDOW_SET5 0x145 +#define IBATP_TH_SET 0x146 +#define IBATM_TH_SET 0x147 +#define VBAT_TH_SET 0x148 +#define THERM_TH_SET 0x149 +#define IACP_TH_SET 0x14A +#define VACP_TH_SET 0x14B +#define VBUS_TH_SET 0x14C +#define VCC_TH_SET 0x14D +#define VSYS_TH_SET 0x14E +#define EXTIADP_TH_SET 0x14F +#define IBATP_VAL 0x150 +#define IBATP_AVE_VAL 0x151 +#define IBATM_VAL 0x152 +#define IBATM_AVE_VAL 0x153 +#define VBAT_VAL 0x154 +#define VBAT_AVE_VAL 0x155 +#define THERM_VAL 0x156 +#define VTH_VAL 0x157 +#define IACP_VAL 0x158 +#define IACP_AVE_VAL 0x159 +#define VACP_VAL 0x15A +#define VACP_AVE_VAL 0x15B +#define VBUS_VAL 0x15C +#define VBUS_AVE_VAL 0x15D +#define VCC_VAL 0x15E +#define VCC_AVE_VAL 0x15F +#define VSYS_VAL 0x160 +#define VSYS_AVE_VAL 0x161 +#define EXTIADP_VAL 0x162 +#define EXTIADP_AVE_VAL 0x163 +#define VACPCLPS_TH_SET 0x164 +#define INT0_SET 0x168 +#define INT1_SET 0x169 +#define INT2_SET 0x16A +#define INT3_SET 0x16B +#define INT4_SET 0x16C +#define INT5_SET 0x16D +#define INT6_SET 0x16E +#define INT7_SET 0x16F +#define INT0_STATUS 0x170 +#define INT1_STATUS 0x171 +#define INT2_STATUS 0x172 +#define INT3_STATUS 0x173 +#define INT4_STATUS 0x174 +#define INT5_STATUS 0x175 +#define INT6_STATUS 0x176 +#define INT7_STATUS 0x177 +#define OTPREG0 0x17A +#define OTPREG1 0x17B +#define SMBREG 0x17C +#define DEBUG_MODE_SET 0x17F +#define DEBUG0x14 0x214 +#define DEBUG0x1A 0x21A + +enum bd9995x_fields { + F_PREV_CHGSTM_STATE, F_CHGSTM_STATE, + F_VBAT_VSYS_STATUS, + F_VBUS_VCC_STATUS, + F_BATTEMP, F_VRECHG_DET, F_RBOOST_UV, F_RBOOSTS, + F_THERMWDT_VAL, F_CHGWDT_VAL, + F_CUR_ILIM_VAL, + F_SEL_ILIM_VAL, + F_IBUS_LIM_SET, + F_ICC_LIM_SET, + F_IOTG_LIM_SET, + F_OTG_BOTH_EN, + F_VRBOOST_TRIG, + F_VRBOOST_EN, + F_PP_BOTH_THRU, + F_VIN_ORD, + F_VBUS_EN, + F_VCC_EN, + F_VSYS_PRIORITY, + F_PPC_SUB_CAP, + F_PPC_CAP, + F_DCP_2500_SEL, + F_SDP_500_SEL, + F_ILIM_AUTO_DISEN, + F_VCC_BC_DISEN, + F_VBUS_BC_DISEN, + F_SDP_CHG_TRIG_EN, + F_SDP_CHG_TRIG, + F_AUTO_TOF, + F_AUTO_FST, + F_AUTO_RECH, + F_ILIM_RESET_EN, + F_DCDC_1MS_SEL, + F_SEL_ILIM_DIV, + F_BATT_LEARN, + F_CHG_EN, + F_USB_SUS, + F_CHOP_SS_INIT, + F_CHOP_ALL_INIT, + F_DCDC_CLK_SEL, + F_CHOP_SS, + F_CHOP_ALL, + F_VBUSCLPS_TH_SET, + F_VCCCLPS_TH_SET, + F_WDT_FST, + F_WDT_PRE, + F_WDT_IBAT_SHORT, + F_WDT_THERM, + F_VSYSREG_SET, + F_VSYSVAL_THH_SET, + F_VSYSVAL_THL_SET, + F_ITRICH_SET, + F_IPRECH_SET, + F_ICHG_SET, + F_ITERM_SET, + F_VPRECHG_TH_SET, + F_VRBOOST_SET, + F_VFASTCHG_REG_SET1, + F_VFASTCHG_REG_SET2, + F_VFASTCHG_REG_SET3, + F_VRECHG_SET, + F_VBATOVP_SET, + F_IBATM_SHORT_SET, + F_PROCHOT_DG_SET, + F_PROCHOT_ICRIT_DG_SET, + F_PROCHOT_IDCHG_DG_SET, + F_PROCHOT_EN, + F_PROCHOT_ICRIT_SET, + F_PROCHOT_INORM_SET, + F_PROCHOT_IDCHG_SET, + F_PROCHOT_VSYS_SET, + F_IMON_INSEL, + F_PMON_INSEL, + F_IOUT_OUT_EN, + F_IOUT_SOURCE_SEL, + F_IOUT_GAIN_SET, + F_PMON_OUT_EN, + F_PMON_GAIN_SET, + F_PMON_DACIN_VAL, + F_IOUT_DACIN_VAL, + F_VCC_BCSRETRY, + F_VCC_ADCRTRY, + F_VCC_USBDETEN, + F_VCC_IDRDETEN, + F_VCC_ENUMRDY, + F_VCC_ADCPOLEN, + F_VCC_DCDMODE, + F_VCC_USB_SW_EN, + F_VCC_USB_SW, + F_VCC_DCDFAIL, + F_VCC_CHGPORT, + F_VCC_PUPDET, + F_VCC_VBUS_VLD, + F_VCC_CHGDET, + F_VCC_OTGDET, + F_VCC_VBINOP, + F_VCC_EXTID, + F_VCC_IDRDET, + F_VCC_INDO, + F_VCC_UCDSWEN, + F_VCC_RREF_EN, + F_VCC_DPPU_EN, + F_VCC_DPREF_EN, + F_VCC_DMREF_EN, + F_VCC_DPDET_EN, + F_VCC_DMDET_EN, + F_VCC_DPSINK_EN, + F_VCC_DMSINK_EN, + F_VCC_DP_BUFF_EN, + F_VCC_DM_BUFF_EN, + F_VCC_EXTCLKENBL, + F_VCC_PLSTESTEN, + F_VCC_UCDSWEN_TSTENB, + F_VCC_RREF_EN_TSTENB, + F_VCC_DPPU_EN_TSTENB, + F_VCC_DPREF_EN_TSTENB, + F_VCC_DMREF_EN_TSTENB, + F_VCC_DPDET_EN_TSTENB, + F_VCC_DMDET_EN_TSTENB, + F_VCC_DPSINK_EN_TSTENB, + F_VCC_DMSINK_EN_TSTENB, + F_VCC_DP_BUFF_EN_TSTENB, + F_VCC_DM_BUFF_EN_TSTENB, + F_VBUS_BCSRETRY, + F_VBUS_ADCRTRY, + F_VBUS_USBDETEN, + F_VBUS_IDRDETEN, + F_VBUS_ENUMRDY, + F_VBUS_ADCPOLEN, + F_VBUS_DCDMODE, + F_VBUS_USB_SW_EN, + F_VBUS_USB_SW, + F_VBUS_DCDFAIL, + F_VBUS_CHGPORT, + F_VBUS_PUPDET, + F_VBUS_VBUS_VLD, + F_VBUS_CHGDET, + F_VBUS_OTGDET, + F_VBUS_VBINOP, + F_VBUS_EXTID, + F_VBUS_IDRDET, + F_VBUS_INDO, + F_VBUS_UCDSWEN, + F_VBUS_RREF_EN, + F_VBUS_DPPU_EN, + F_VBUS_DPREF_EN, + F_VBUS_DMREF_EN, + F_VBUS_DPDET_EN, + F_VBUS_DMDET_EN, + F_VBUS_DPSINK_EN, + F_VBUS_DMSINK_EN, + F_VBUS_DP_BUFF_EN, + F_VBUS_DM_BUFF_EN, + F_VBUS_EXTCLKENBL, + F_VBUS_PLSTESTEN, + F_VBUS_UCDSWEN_TSTENB, + F_VBUS_RREF_EN_TSTENB, + F_VBUS_DPPU_EN_TSTENB, + F_VBUS_DPREF_EN_TSTENB, + F_VBUS_DMREF_EN_TSTENB, + F_VBUS_DPDET_EN_TSTENB, + F_VBUS_DMDET_EN_TSTENB, + F_VBUS_DPSINK_EN_TSTENB, + F_VBUS_DMSINK_EN_TSTENB, + F_VBUS_DP_BUFF_EN_TSTENB, + F_VBUS_DM_BUFF_EN_TSTENB, + F_CHIP_ID, + F_CHIP_REV, + F_ONE_CELL_MODE, + F_cell, + F_VACP_AUTO_DISCHG, + F_VACP_LOAD, + F_ACOK_POL, + F_ACOK_DISEN, + F_DEBUG_SET1, + F_DEBUG_SET0, + F_MONRST_STATE, + F_ALMRST_STATE, + F_CHGRST_STATE, + F_OTPLD_STATE, + F_ALLRST_STATE, + F_PROTECT_SET, + F_MAP_SET, + F_ADCINTERVAL, + F_ADCMOD, + F_ADCTMOD, + F_EXTIADPEN, + F_VSYSENB, + F_VCCENB, + F_VBUSENB, + F_VACPENB, + F_IACPENB, + F_THERMENB, + F_VBATENB, + F_IBATMENB, + F_IBATPENB, + F_TMPTHR1B, + F_TMPTHR1A, + F_TMPTHR2B, + F_TMPTHR2A, + F_TMPTHR3B, + F_TMPTHR3A, + F_TMPTHR4B, + F_TMPTHR4A, + F_TMPTHR5B, + F_TMPTHR5A, + F_IBATP_TH_SET, + F_IBATM_TH_SET, + F_VBAT_TH_SET, + F_THERM_TH_SET, + F_IACP_TH_SET, + F_VACP_TH_SET, + F_VBUS_TH_SET, + F_VCC_TH_SET, + F_VSYS_TH_SET, + F_EXTIADP_TH_SET, + F_IBATP_VAL, + F_IBATP_AVE_VAL, + F_IBATM_VAL, + F_IBATM_AVE_VAL, + F_VBAT_VAL, + F_VBAT_AVE_VAL, + F_THERM_VAL, + F_VTH_VAL, + F_IACP_VAL, + F_IACP_AVE_VAL, + F_VACP_VAL, + F_VACP_AVE_VAL, + F_VBUS_VAL, + F_VBUS_AVE_VAL, + F_VCC_VAL, + F_VCC_AVE_VAL, + F_VSYS_VAL, + F_VSYS_AVE_VAL, + F_EXTIADP_VAL, + F_EXTIADP_AVE_VAL, + F_VACPCLPS_TH_SET, + F_INT7_SET, + F_INT6_SET, + F_INT5_SET, + F_INT4_SET, + F_INT3_SET, + F_INT2_SET, + F_INT1_SET, + F_INT0_SET, + F_VBUS_RBUV_DET, + F_VBUS_RBUV_RES, + F_VBUS_TH_DET, + F_VBUS_TH_RES, + F_VBUS_IIN_MOD, + F_VBUS_OV_DET, + F_VBUS_OV_RES, + F_VBUS_CLPS_DET, + F_VBUS_CLPS, + F_VBUS_DET, + F_VBUS_RES, + F_VCC_RBUV_DET, + F_VCC_RBUV_RES, + F_VCC_TH_DET, + F_VCC_TH_RES, + F_VCC_IIN_MOD, + F_VCC_OVP_DET, + F_VCC_OVP_RES, + F_VCC_CLPS_DET, + F_VCC_CLPS_RES, + F_VCC_DET, + F_VCC_RES, + F_TH_DET, + F_TH_RMV, + F_TMP_OUT_DET, + F_TMP_OUT_RES, + F_VBAT_TH_DET, + F_VBAT_TH_RES, + F_IBAT_SHORT_DET, + F_IBAT_SHORT_RES, + F_VBAT_OV_DET, + F_VBAT_OV_RES, + F_BAT_ASSIST_DET, + F_BAT_ASSIST_RES, + F_VSYS_TH_DET, + F_VSYS_TH_RES, + F_VSYS_OV_DET, + F_VSYS_OV_RES, + F_VSYS_SHT_DET, + F_VSYS_SHT_RES, + F_VSYS_UV_DET, + F_VSYS_UV_RES, + F_OTP_LOAD_DONE, + F_PWR_ON, + F_EXTIADP_TRNS, + F_EXTIADP_TH_DET, + F_EXIADP_TH_RES, + F_BAT_MNT_DET, + F_BAT_MNT_RES, + F_TSD_DET, + F_TSD_RES, + F_CHGWDT_EXP, + F_THERMWDT_EXP, + F_TMP_TRNS, + F_CHG_TRNS, + F_VBUS_UCD_PORT_DET, + F_VBUS_UCD_UCHG_DET, + F_VBUS_UCD_URID_RMV, + F_VBUS_UCD_OTG_DET, + F_VBUS_UCD_URID_MOD, + F_VCC_UCD_PORT_DET, + F_VCC_UCD_UCHG_DET, + F_VCC_UCD_URID_RMV, + F_VCC_UCD_OTG_DET, + F_VCC_UCD_URID_MOD, + F_PROCHOT_DET, + F_PROCHOT_RES, + F_VACP_DET, + F_VACP_RES, + F_VACP_TH_DET, + F_VACP_TH_RES, + F_IACP_TH_DET, + F_IACP_THE_RES, + F_THERM_TH_DET, + F_THERM_TH_RES, + F_IBATM_TH_DET, + F_IBATM_TH_RES, + F_IBATP_TH_DET, + F_IBATP_TH_RES, + F_INT7_STATUS, + F_INT6_STATUS, + F_INT5_STATUS, + F_INT4_STATUS, + F_INT3_STATUS, + F_INT2_STATUS, + F_INT1_STATUS, + F_INT0_STATUS, + F_ILIM_DECREASE, + F_RESERVE_OTPREG1, + F_POWER_SAVE_MODE, + F_DEBUG_MODE_SET, + F_DEBUG0x14, + F_DEBUG0x1A, + F_MAX_FIELDS +}; + +static const struct reg_field bd9995x_reg_fields[] = { + [F_PREV_CHGSTM_STATE] = REG_FIELD(CHGSTM_STATUS, 8, 14), + [F_CHGSTM_STATE] = REG_FIELD(CHGSTM_STATUS, 0, 6), + [F_VBAT_VSYS_STATUS] = REG_FIELD(VBAT_VSYS_STATUS, 0, 15), + [F_VBUS_VCC_STATUS] = REG_FIELD(VBUS_VCC_STATUS, 0, 12), + [F_BATTEMP] = REG_FIELD(CHGOP_STATUS, 8, 10), + [F_VRECHG_DET] = REG_FIELD(CHGOP_STATUS, 6, 6), + [F_RBOOST_UV] = REG_FIELD(CHGOP_STATUS, 1, 1), + [F_RBOOSTS] = REG_FIELD(CHGOP_STATUS, 0, 0), + [F_THERMWDT_VAL] = REG_FIELD(WDT_STATUS, 8, 15), + [F_CHGWDT_VAL] = REG_FIELD(WDT_STATUS, 0, 7), + [F_CUR_ILIM_VAL] = REG_FIELD(CUR_ILIM_VAL, 0, 13), + [F_SEL_ILIM_VAL] = REG_FIELD(SEL_ILIM_VAL, 0, 13), + [F_IBUS_LIM_SET] = REG_FIELD(IBUS_LIM_SET, 5, 13), + [F_ICC_LIM_SET] = REG_FIELD(ICC_LIM_SET, 5, 13), + [F_IOTG_LIM_SET] = REG_FIELD(IOTG_LIM_SET, 5, 13), + [F_OTG_BOTH_EN] = REG_FIELD(VIN_CTRL_SET, 15, 15), + [F_VRBOOST_TRIG] = REG_FIELD(VIN_CTRL_SET, 14, 14), + [F_VRBOOST_EN] = REG_FIELD(VIN_CTRL_SET, 12, 13), + [F_PP_BOTH_THRU] = REG_FIELD(VIN_CTRL_SET, 11, 11), + [F_VIN_ORD] = REG_FIELD(VIN_CTRL_SET, 7, 7), + [F_VBUS_EN] = REG_FIELD(VIN_CTRL_SET, 6, 6), + [F_VCC_EN] = REG_FIELD(VIN_CTRL_SET, 5, 5), + [F_VSYS_PRIORITY] = REG_FIELD(VIN_CTRL_SET, 4, 4), + [F_PPC_SUB_CAP] = REG_FIELD(VIN_CTRL_SET, 2, 3), + [F_PPC_CAP] = REG_FIELD(VIN_CTRL_SET, 0, 1), + [F_DCP_2500_SEL] = REG_FIELD(CHGOP_SET1, 15, 15), + [F_SDP_500_SEL] = REG_FIELD(CHGOP_SET1, 14, 14), + [F_ILIM_AUTO_DISEN] = REG_FIELD(CHGOP_SET1, 13, 13), + [F_VCC_BC_DISEN] = REG_FIELD(CHGOP_SET1, 11, 11), + [F_VBUS_BC_DISEN] = REG_FIELD(CHGOP_SET1, 10, 10), + [F_SDP_CHG_TRIG_EN] = REG_FIELD(CHGOP_SET1, 9, 9), + [F_SDP_CHG_TRIG] = REG_FIELD(CHGOP_SET1, 8, 8), + [F_AUTO_TOF] = REG_FIELD(CHGOP_SET1, 6, 6), + [F_AUTO_FST] = REG_FIELD(CHGOP_SET1, 5, 5), + [F_AUTO_RECH] = REG_FIELD(CHGOP_SET1, 3, 3), + [F_ILIM_RESET_EN] = REG_FIELD(CHGOP_SET2, 14, 14), + [F_DCDC_1MS_SEL] = REG_FIELD(CHGOP_SET2, 12, 13), + [F_SEL_ILIM_DIV] = REG_FIELD(CHGOP_SET2, 10, 10), + [F_BATT_LEARN] = REG_FIELD(CHGOP_SET2, 8, 8), + [F_CHG_EN] = REG_FIELD(CHGOP_SET2, 7, 7), + [F_USB_SUS] = REG_FIELD(CHGOP_SET2, 6, 6), + [F_CHOP_SS_INIT] = REG_FIELD(CHGOP_SET2, 5, 5), + [F_CHOP_ALL_INIT] = REG_FIELD(CHGOP_SET2, 4, 4), + [F_DCDC_CLK_SEL] = REG_FIELD(CHGOP_SET2, 2, 3), + [F_CHOP_SS] = REG_FIELD(CHGOP_SET2, 1, 1), + [F_CHOP_ALL] = REG_FIELD(CHGOP_SET2, 0, 0), + [F_VBUSCLPS_TH_SET] = REG_FIELD(VBUSCLPS_TH_SET, 7, 14), + [F_VCCCLPS_TH_SET] = REG_FIELD(VCCCLPS_TH_SET, 7, 14), + [F_WDT_FST] = REG_FIELD(CHGWDT_SET, 8, 15), + [F_WDT_PRE] = REG_FIELD(CHGWDT_SET, 0, 7), + [F_WDT_IBAT_SHORT] = REG_FIELD(BATTWDT_SET, 8, 15), + [F_WDT_THERM] = REG_FIELD(BATTWDT_SET, 0, 7), + [F_VSYSREG_SET] = REG_FIELD(VSYSREG_SET, 6, 14), + [F_VSYSVAL_THH_SET] = REG_FIELD(VSYSVAL_THH_SET, 6, 14), + [F_VSYSVAL_THL_SET] = REG_FIELD(VSYSVAL_THL_SET, 6, 14), + [F_ITRICH_SET] = REG_FIELD(ITRICH_SET, 6, 10), + [F_IPRECH_SET] = REG_FIELD(IPRECH_SET, 6, 10), + [F_ICHG_SET] = REG_FIELD(ICHG_SET, 6, 13), + [F_ITERM_SET] = REG_FIELD(ITERM_SET, 6, 10), + [F_VPRECHG_TH_SET] = REG_FIELD(VPRECHG_TH_SET, 6, 14), + [F_VRBOOST_SET] = REG_FIELD(VRBOOST_SET, 6, 14), + [F_VFASTCHG_REG_SET1] = REG_FIELD(VFASTCHG_REG_SET1, 4, 14), + [F_VFASTCHG_REG_SET2] = REG_FIELD(VFASTCHG_REG_SET2, 4, 14), + [F_VFASTCHG_REG_SET3] = REG_FIELD(VFASTCHG_REG_SET3, 4, 14), + [F_VRECHG_SET] = REG_FIELD(VRECHG_SET, 4, 14), + [F_VBATOVP_SET] = REG_FIELD(VBATOVP_SET, 4, 14), + [F_IBATM_SHORT_SET] = REG_FIELD(IBATSHORT_SET, 0, 14), + [F_PROCHOT_DG_SET] = REG_FIELD(PROCHOT_CTRL_SET, 14, 15), + [F_PROCHOT_ICRIT_DG_SET] = REG_FIELD(PROCHOT_CTRL_SET, 10, 11), + [F_PROCHOT_IDCHG_DG_SET] = REG_FIELD(PROCHOT_CTRL_SET, 8, 9), + [F_PROCHOT_EN] = REG_FIELD(PROCHOT_CTRL_SET, 0, 4), + [F_PROCHOT_ICRIT_SET] = REG_FIELD(PROCHOT_ICRIT_SET, 0, 14), + [F_PROCHOT_INORM_SET] = REG_FIELD(PROCHOT_INORM_SET, 0, 14), + [F_PROCHOT_IDCHG_SET] = REG_FIELD(PROCHOT_IDCHG_SET, 0, 14), + [F_PROCHOT_VSYS_SET] = REG_FIELD(PROCHOT_VSYS_SET, 0, 14), + [F_IMON_INSEL] = REG_FIELD(PMON_IOUT_CTRL_SET, 9, 9), + [F_PMON_INSEL] = REG_FIELD(PMON_IOUT_CTRL_SET, 8, 8), + [F_IOUT_OUT_EN] = REG_FIELD(PMON_IOUT_CTRL_SET, 7, 7), + [F_IOUT_SOURCE_SEL] = REG_FIELD(PMON_IOUT_CTRL_SET, 6, 6), + [F_IOUT_GAIN_SET] = REG_FIELD(PMON_IOUT_CTRL_SET, 4, 5), + [F_PMON_OUT_EN] = REG_FIELD(PMON_IOUT_CTRL_SET, 3, 3), + [F_PMON_GAIN_SET] = REG_FIELD(PMON_IOUT_CTRL_SET, 0, 2), + [F_PMON_DACIN_VAL] = REG_FIELD(PMON_DACIN_VAL, 0, 9), + [F_IOUT_DACIN_VAL] = REG_FIELD(IOUT_DACIN_VAL, 0, 11), + [F_VCC_BCSRETRY] = REG_FIELD(VCC_UCD_SET, 12, 12), + [F_VCC_ADCRTRY] = REG_FIELD(VCC_UCD_SET, 8, 8), + [F_VCC_USBDETEN] = REG_FIELD(VCC_UCD_SET, 7, 7), + [F_VCC_IDRDETEN] = REG_FIELD(VCC_UCD_SET, 6, 6), + [F_VCC_ENUMRDY] = REG_FIELD(VCC_UCD_SET, 5, 5), + [F_VCC_ADCPOLEN] = REG_FIELD(VCC_UCD_SET, 4, 4), + [F_VCC_DCDMODE] = REG_FIELD(VCC_UCD_SET, 3, 3), + [F_VCC_USB_SW_EN] = REG_FIELD(VCC_UCD_SET, 1, 1), + [F_VCC_USB_SW] = REG_FIELD(VCC_UCD_SET, 0, 0), + [F_VCC_DCDFAIL] = REG_FIELD(VCC_UCD_STATUS, 15, 15), + [F_VCC_CHGPORT] = REG_FIELD(VCC_UCD_STATUS, 12, 13), + [F_VCC_PUPDET] = REG_FIELD(VCC_UCD_STATUS, 11, 11), + [F_VCC_VBUS_VLD] = REG_FIELD(VCC_UCD_STATUS, 7, 7), + [F_VCC_CHGDET] = REG_FIELD(VCC_UCD_STATUS, 6, 6), + [F_VCC_OTGDET] = REG_FIELD(VCC_UCD_STATUS, 3, 3), + [F_VCC_VBINOP] = REG_FIELD(VCC_IDD_STATUS, 6, 6), + [F_VCC_EXTID] = REG_FIELD(VCC_IDD_STATUS, 5, 5), + [F_VCC_IDRDET] = REG_FIELD(VCC_IDD_STATUS, 4, 4), + [F_VCC_INDO] = REG_FIELD(VCC_IDD_STATUS, 0, 3), + [F_VCC_UCDSWEN] = REG_FIELD(VCC_UCD_FCTRL_SET, 10, 10), + [F_VCC_RREF_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 9, 9), + [F_VCC_DPPU_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 8, 8), + [F_VCC_DPREF_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 7, 7), + [F_VCC_DMREF_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 6, 6), + [F_VCC_DPDET_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 5, 5), + [F_VCC_DMDET_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 4, 4), + [F_VCC_DPSINK_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 3, 3), + [F_VCC_DMSINK_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 2, 2), + [F_VCC_DP_BUFF_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 1, 1), + [F_VCC_DM_BUFF_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 0, 0), + [F_VCC_EXTCLKENBL] = REG_FIELD(VCC_UCD_FCTRL_EN, 15, 15), + [F_VCC_PLSTESTEN] = REG_FIELD(VCC_UCD_FCTRL_EN, 14, 14), + [F_VCC_UCDSWEN_TSTENB] = REG_FIELD(VCC_UCD_FCTRL_EN, 10, 10), + [F_VCC_RREF_EN_TSTENB] = REG_FIELD(VCC_UCD_FCTRL_EN, 9, 9), + [F_VCC_DPPU_EN_TSTENB] = REG_FIELD(VCC_UCD_FCTRL_EN, 8, 8), + [F_VCC_DPREF_EN_TSTENB] = REG_FIELD(VCC_UCD_FCTRL_EN, 7, 7), + [F_VCC_DMREF_EN_TSTENB] = REG_FIELD(VCC_UCD_FCTRL_EN, 6, 6), + [F_VCC_DPDET_EN_TSTENB] = REG_FIELD(VCC_UCD_FCTRL_EN, 5, 5), + [F_VCC_DMDET_EN_TSTENB] = REG_FIELD(VCC_UCD_FCTRL_EN, 4, 4), + [F_VCC_DPSINK_EN_TSTENB] = REG_FIELD(VCC_UCD_FCTRL_EN, 3, 3), + [F_VCC_DMSINK_EN_TSTENB] = REG_FIELD(VCC_UCD_FCTRL_EN, 2, 2), + [F_VCC_DP_BUFF_EN_TSTENB] = REG_FIELD(VCC_UCD_FCTRL_EN, 1, 1), + [F_VCC_DM_BUFF_EN_TSTENB] = REG_FIELD(VCC_UCD_FCTRL_EN, 0, 0), + + [F_VBUS_BCSRETRY] = REG_FIELD(VBUS_UCD_SET, 12, 12), + [F_VBUS_ADCRTRY] = REG_FIELD(VBUS_UCD_SET, 8, 8), + [F_VBUS_USBDETEN] = REG_FIELD(VBUS_UCD_SET, 7, 7), + [F_VBUS_IDRDETEN] = REG_FIELD(VBUS_UCD_SET, 6, 6), + [F_VBUS_ENUMRDY] = REG_FIELD(VBUS_UCD_SET, 5, 5), + [F_VBUS_ADCPOLEN] = REG_FIELD(VBUS_UCD_SET, 4, 4), + [F_VBUS_DCDMODE] = REG_FIELD(VBUS_UCD_SET, 3, 3), + [F_VBUS_USB_SW_EN] = REG_FIELD(VBUS_UCD_SET, 1, 1), + [F_VBUS_USB_SW] = REG_FIELD(VBUS_UCD_SET, 0, 0), + [F_VBUS_DCDFAIL] = REG_FIELD(VBUS_UCD_STATUS, 15, 15), + [F_VBUS_CHGPORT] = REG_FIELD(VBUS_UCD_STATUS, 12, 13), + [F_VBUS_PUPDET] = REG_FIELD(VBUS_UCD_STATUS, 11, 11), + [F_VBUS_VBUS_VLD] = REG_FIELD(VBUS_UCD_STATUS, 7, 7), + [F_VBUS_CHGDET] = REG_FIELD(VBUS_UCD_STATUS, 6, 6), + [F_VBUS_OTGDET] = REG_FIELD(VBUS_UCD_STATUS, 3, 3), + [F_VBUS_VBINOP] = REG_FIELD(VBUS_IDD_STATUS, 6, 6), + [F_VBUS_EXTID] = REG_FIELD(VBUS_IDD_STATUS, 5, 5), + [F_VBUS_IDRDET] = REG_FIELD(VBUS_IDD_STATUS, 4, 4), + [F_VBUS_INDO] = REG_FIELD(VBUS_IDD_STATUS, 0, 3), + [F_VBUS_UCDSWEN] = REG_FIELD(VCC_UCD_FCTRL_SET, 10, 10), + [F_VBUS_RREF_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 9, 9), + [F_VBUS_DPPU_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 8, 8), + [F_VBUS_DPREF_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 7, 7), + [F_VBUS_DMREF_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 6, 6), + [F_VBUS_DPDET_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 5, 5), + [F_VBUS_DMDET_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 4, 4), + [F_VBUS_DPSINK_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 3, 3), + [F_VBUS_DMSINK_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 2, 2), + [F_VBUS_DP_BUFF_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 1, 1), + [F_VBUS_DM_BUFF_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 0, 0), + + [F_VBUS_EXTCLKENBL] = REG_FIELD(VBUS_UCD_FCTRL_EN, 15, 15), + [F_VBUS_PLSTESTEN] = REG_FIELD(VBUS_UCD_FCTRL_EN, 14, 14), + [F_VBUS_UCDSWEN_TSTENB] = REG_FIELD(VBUS_UCD_FCTRL_EN, 10, 10), + [F_VBUS_RREF_EN_TSTENB] = REG_FIELD(VBUS_UCD_FCTRL_EN, 9, 9), + [F_VBUS_DPPU_EN_TSTENB] = REG_FIELD(VBUS_UCD_FCTRL_EN, 8, 8), + [F_VBUS_DPREF_EN_TSTENB] = REG_FIELD(VBUS_UCD_FCTRL_EN, 7, 7), + [F_VBUS_DMREF_EN_TSTENB] = REG_FIELD(VBUS_UCD_FCTRL_EN, 6, 6), + [F_VBUS_DPDET_EN_TSTENB] = REG_FIELD(VBUS_UCD_FCTRL_EN, 5, 5), + [F_VBUS_DMDET_EN_TSTENB] = REG_FIELD(VBUS_UCD_FCTRL_EN, 4, 4), + [F_VBUS_DPSINK_EN_TSTENB] = REG_FIELD(VBUS_UCD_FCTRL_EN, 3, 3), + [F_VBUS_DMSINK_EN_TSTENB] = REG_FIELD(VBUS_UCD_FCTRL_EN, 2, 2), + [F_VBUS_DP_BUFF_EN_TSTENB] = REG_FIELD(VBUS_UCD_FCTRL_EN, 1, 1), + [F_VBUS_DM_BUFF_EN_TSTENB] = REG_FIELD(VBUS_UCD_FCTRL_EN, 0, 0), + + [F_CHIP_ID] = REG_FIELD(CHIP_ID, 0, 15), + [F_CHIP_REV] = REG_FIELD(CHIP_REV, 0, 15), + [F_ONE_CELL_MODE] = REG_FIELD(IC_SET1, 11, 11), + [F_cell] = REG_FIELD(IC_SET1, 1, 1), + [F_VACP_AUTO_DISCHG] = REG_FIELD(IC_SET1, 9, 9), + [F_VACP_LOAD] = REG_FIELD(IC_SET1, 8, 8), + [F_ACOK_POL] = REG_FIELD(IC_SET1, 1, 1), + [F_ACOK_DISEN] = REG_FIELD(IC_SET1, 0, 0), + [F_DEBUG_SET1] = REG_FIELD(IC_SET2, 4, 8), + [F_DEBUG_SET0] = REG_FIELD(IC_SET2, 0, 0), + [F_MONRST_STATE] = REG_FIELD(SYSTEM_STATUS, 6, 6), + [F_ALMRST_STATE] = REG_FIELD(SYSTEM_STATUS, 5, 5), + [F_CHGRST_STATE] = REG_FIELD(SYSTEM_STATUS, 4, 4), + [F_OTPLD_STATE] = REG_FIELD(SYSTEM_STATUS, 1, 1), + [F_ALLRST_STATE] = REG_FIELD(SYSTEM_STATUS, 0, 0), + [F_PROTECT_SET] = REG_FIELD(PROTECT_SET, 0, 15), + [F_MAP_SET] = REG_FIELD(MAP_SET, 0, 15), + [F_ADCINTERVAL] = REG_FIELD(VM_CTRL_SET, 14, 15), + [F_ADCMOD] = REG_FIELD(VM_CTRL_SET, 12, 13), + [F_ADCTMOD] = REG_FIELD(VM_CTRL_SET, 10, 11), + [F_EXTIADPEN] = REG_FIELD(VM_CTRL_SET, 9, 9), + [F_VSYSENB] = REG_FIELD(VM_CTRL_SET, 8, 8), + [F_VCCENB] = REG_FIELD(VM_CTRL_SET, 7, 7), + [F_VBUSENB] = REG_FIELD(VM_CTRL_SET, 6, 6), + [F_VACPENB] = REG_FIELD(VM_CTRL_SET, 5, 5), + [F_IACPENB] = REG_FIELD(VM_CTRL_SET, 4, 4), + [F_THERMENB] = REG_FIELD(VM_CTRL_SET, 3, 3), + [F_VBATENB] = REG_FIELD(VM_CTRL_SET, 2, 2), + [F_IBATMENB] = REG_FIELD(VM_CTRL_SET, 1, 1), + [F_IBATPENB] = REG_FIELD(VM_CTRL_SET, 0, 0), + [F_TMPTHR1B] = REG_FIELD(THERM_WINDOW_SET1, 8, 15), + [F_TMPTHR1A] = REG_FIELD(THERM_WINDOW_SET1, 0, 7), + [F_TMPTHR2B] = REG_FIELD(THERM_WINDOW_SET2, 8, 15), + [F_TMPTHR2A] = REG_FIELD(THERM_WINDOW_SET2, 0, 7), + [F_TMPTHR3B] = REG_FIELD(THERM_WINDOW_SET3, 8, 15), + [F_TMPTHR3A] = REG_FIELD(THERM_WINDOW_SET3, 0, 7), + [F_TMPTHR4B] = REG_FIELD(THERM_WINDOW_SET4, 8, 15), + [F_TMPTHR4A] = REG_FIELD(THERM_WINDOW_SET4, 0, 7), + [F_TMPTHR5B] = REG_FIELD(THERM_WINDOW_SET5, 8, 15), + [F_TMPTHR5A] = REG_FIELD(THERM_WINDOW_SET5, 0, 7), + [F_IBATP_TH_SET] = REG_FIELD(IBATP_TH_SET, 0, 14), + [F_IBATM_TH_SET] = REG_FIELD(IBATM_TH_SET, 0, 14), + [F_VBAT_TH_SET] = REG_FIELD(VBAT_TH_SET, 0, 14), + [F_THERM_TH_SET] = REG_FIELD(THERM_TH_SET, 0, 7), + [F_IACP_TH_SET] = REG_FIELD(IACP_TH_SET, 0, 14), + [F_VACP_TH_SET] = REG_FIELD(VACP_TH_SET, 0, 14), + [F_VBUS_TH_SET] = REG_FIELD(VBUS_TH_SET, 0, 14), + [F_VCC_TH_SET] = REG_FIELD(VCC_TH_SET, 0, 14), + [F_VSYS_TH_SET] = REG_FIELD(VSYS_TH_SET, 0, 14), + [F_EXTIADP_TH_SET] = REG_FIELD(EXTIADP_TH_SET, 0, 11), + [F_IBATP_VAL] = REG_FIELD(IBATP_VAL, 0, 14), + [F_IBATP_AVE_VAL] = REG_FIELD(IBATP_AVE_VAL, 0, 14), + [F_IBATM_VAL] = REG_FIELD(IBATM_VAL, 0, 14), + [F_IBATM_AVE_VAL] = REG_FIELD(IBATM_AVE_VAL, 0, 14), + [F_VBAT_VAL] = REG_FIELD(VBAT_VAL, 0, 14), + [F_VBAT_AVE_VAL] = REG_FIELD(VBAT_AVE_VAL, 0, 14), + [F_THERM_VAL] = REG_FIELD(THERM_VAL, 0, 7), + [F_VTH_VAL] = REG_FIELD(VTH_VAL, 0, 11), + [F_IACP_VAL] = REG_FIELD(IACP_VAL, 0, 14), + [F_IACP_AVE_VAL] = REG_FIELD(IACP_AVE_VAL, 0, 14), + [F_VACP_VAL] = REG_FIELD(VACP_VAL, 0, 14), + [F_VACP_AVE_VAL] = REG_FIELD(VACP_AVE_VAL, 0, 14), + [F_VBUS_VAL] = REG_FIELD(VBUS_VAL, 0, 14), + [F_VBUS_AVE_VAL] = REG_FIELD(VBUS_AVE_VAL, 0, 14), + [F_VCC_VAL] = REG_FIELD(VCC_VAL, 0, 14), + [F_VCC_AVE_VAL] = REG_FIELD(VCC_AVE_VAL, 0, 14), + [F_VSYS_VAL] = REG_FIELD(VSYS_VAL, 0, 14), + [F_VSYS_AVE_VAL] = REG_FIELD(VSYS_AVE_VAL, 0, 14), + [F_EXTIADP_VAL] = REG_FIELD(EXTIADP_VAL, 0, 11), + [F_EXTIADP_AVE_VAL] = REG_FIELD(EXTIADP_AVE_VAL, 0, 11), + [F_VACPCLPS_TH_SET] = REG_FIELD(VACPCLPS_TH_SET, 7, 14), + [F_INT7_SET] = REG_FIELD(INT7_SET, 0, 15), + [F_INT6_SET] = REG_FIELD(INT6_SET, 0, 13), + [F_INT5_SET] = REG_FIELD(INT5_SET, 0, 13), + [F_INT4_SET] = REG_FIELD(INT4_SET, 0, 9), + [F_INT3_SET] = REG_FIELD(INT3_SET, 0, 15), + [F_INT2_SET] = REG_FIELD(INT2_SET, 0, 15), + [F_INT1_SET] = REG_FIELD(INT1_SET, 0, 15), + [F_INT0_SET] = REG_FIELD(INT0_SET, 0, 7), + [F_VBUS_RBUV_DET] = REG_FIELD(INT1_SET, 15, 15), + [F_VBUS_RBUV_RES] = REG_FIELD(INT1_SET, 14, 14), + [F_VBUS_TH_DET] = REG_FIELD(INT1_SET, 9, 9), + [F_VBUS_TH_RES] = REG_FIELD(INT1_SET, 8, 8), + [F_VBUS_IIN_MOD] = REG_FIELD(INT1_SET, 6, 6), + [F_VBUS_OV_DET] = REG_FIELD(INT1_SET, 5, 5), + [F_VBUS_OV_RES] = REG_FIELD(INT1_SET, 4, 4), + [F_VBUS_CLPS_DET] = REG_FIELD(INT1_SET, 3, 3), + [F_VBUS_CLPS] = REG_FIELD(INT1_SET, 2, 2), + [F_VBUS_DET] = REG_FIELD(INT1_SET, 1, 1), + [F_VBUS_RES] = REG_FIELD(INT1_SET, 0, 0), + [F_VCC_RBUV_DET] = REG_FIELD(INT2_SET, 15, 15), + [F_VCC_RBUV_RES] = REG_FIELD(INT2_SET, 14, 14), + [F_VCC_TH_DET] = REG_FIELD(INT2_SET, 9, 9), + [F_VCC_TH_RES] = REG_FIELD(INT2_SET, 8, 8), + [F_VCC_IIN_MOD] = REG_FIELD(INT2_SET, 6, 6), + [F_VCC_OVP_DET] = REG_FIELD(INT2_SET, 5, 5), + [F_VCC_OVP_RES] = REG_FIELD(INT2_SET, 4, 4), + [F_VCC_CLPS_DET] = REG_FIELD(INT2_SET, 3, 3), + [F_VCC_CLPS_RES] = REG_FIELD(INT2_SET, 2, 2), + [F_VCC_DET] = REG_FIELD(INT2_SET, 1, 1), + [F_VCC_RES] = REG_FIELD(INT2_SET, 0, 0), + [F_TH_DET] = REG_FIELD(INT3_SET, 15, 15), + [F_TH_RMV] = REG_FIELD(INT3_SET, 14, 14), + [F_TMP_OUT_DET] = REG_FIELD(INT3_SET, 11, 11), + [F_TMP_OUT_RES] = REG_FIELD(INT3_SET, 10, 10), + [F_VBAT_TH_DET] = REG_FIELD(INT3_SET, 9, 9), + [F_VBAT_TH_RES] = REG_FIELD(INT3_SET, 8, 8), + [F_IBAT_SHORT_DET] = REG_FIELD(INT3_SET, 7, 7), + [F_IBAT_SHORT_RES] = REG_FIELD(INT3_SET, 6, 6), + [F_VBAT_OV_DET] = REG_FIELD(INT3_SET, 5, 5), + [F_VBAT_OV_RES] = REG_FIELD(INT3_SET, 4, 4), + [F_BAT_ASSIST_DET] = REG_FIELD(INT3_SET, 3, 3), + [F_BAT_ASSIST_RES] = REG_FIELD(INT3_SET, 2, 2), + [F_VSYS_TH_DET] = REG_FIELD(INT4_SET, 9, 9), + [F_VSYS_TH_RES] = REG_FIELD(INT4_SET, 8, 8), + [F_VSYS_OV_DET] = REG_FIELD(INT4_SET, 5, 5), + [F_VSYS_OV_RES] = REG_FIELD(INT4_SET, 4, 4), + [F_VSYS_SHT_DET] = REG_FIELD(INT4_SET, 3, 3), + [F_VSYS_SHT_RES] = REG_FIELD(INT4_SET, 2, 2), + [F_VSYS_UV_DET] = REG_FIELD(INT4_SET, 1, 1), + [F_VSYS_UV_RES] = REG_FIELD(INT4_SET, 0, 0), + [F_OTP_LOAD_DONE] = REG_FIELD(INT5_SET, 13, 13), + [F_PWR_ON] = REG_FIELD(INT5_SET, 12, 12), + [F_EXTIADP_TRNS] = REG_FIELD(INT5_SET, 11, 11), + [F_EXTIADP_TH_DET] = REG_FIELD(INT5_SET, 9, 9), + [F_EXIADP_TH_RES] = REG_FIELD(INT5_SET, 8, 8), + [F_BAT_MNT_DET] = REG_FIELD(INT5_SET, 7, 7), + [F_BAT_MNT_RES] = REG_FIELD(INT5_SET, 6, 6), + [F_TSD_DET] = REG_FIELD(INT5_SET, 5, 5), + [F_TSD_RES] = REG_FIELD(INT5_SET, 4, 4), + [F_CHGWDT_EXP] = REG_FIELD(INT5_SET, 3, 3), + [F_THERMWDT_EXP] = REG_FIELD(INT5_SET, 2, 2), + [F_TMP_TRNS] = REG_FIELD(INT5_SET, 1, 1), + [F_CHG_TRNS] = REG_FIELD(INT5_SET, 0, 0), + [F_VBUS_UCD_PORT_DET] = REG_FIELD(INT6_SET, 13, 13), + [F_VBUS_UCD_UCHG_DET] = REG_FIELD(INT6_SET, 12, 12), + [F_VBUS_UCD_URID_RMV] = REG_FIELD(INT6_SET, 11, 11), + [F_VBUS_UCD_OTG_DET] = REG_FIELD(INT6_SET, 10, 10), + [F_VBUS_UCD_URID_MOD] = REG_FIELD(INT6_SET, 8, 8), + [F_VCC_UCD_PORT_DET] = REG_FIELD(INT6_SET, 5, 5), + [F_VCC_UCD_UCHG_DET] = REG_FIELD(INT6_SET, 4, 4), + [F_VCC_UCD_URID_RMV] = REG_FIELD(INT6_SET, 3, 3), + [F_VCC_UCD_OTG_DET] = REG_FIELD(INT6_SET, 2, 2), + [F_VCC_UCD_URID_MOD] = REG_FIELD(INT6_SET, 0, 0), + [F_PROCHOT_DET] = REG_FIELD(INT7_SET, 15, 15), + [F_PROCHOT_RES] = REG_FIELD(INT7_SET, 14, 14), + [F_VACP_DET] = REG_FIELD(INT7_SET, 11, 11), + [F_VACP_RES] = REG_FIELD(INT7_SET, 10, 10), + [F_VACP_TH_DET] = REG_FIELD(INT7_SET, 9, 9), + [F_VACP_TH_RES] = REG_FIELD(INT7_SET, 8, 8), + [F_IACP_TH_DET] = REG_FIELD(INT7_SET, 7, 7), + [F_IACP_THE_RES] = REG_FIELD(INT7_SET, 6, 6), + [F_THERM_TH_DET] = REG_FIELD(INT7_SET, 5, 5), + [F_THERM_TH_RES] = REG_FIELD(INT7_SET, 4, 4), + [F_IBATM_TH_DET] = REG_FIELD(INT7_SET, 3, 3), + [F_IBATM_TH_RES] = REG_FIELD(INT7_SET, 2, 2), + [F_IBATP_TH_DET] = REG_FIELD(INT7_SET, 1, 1), + [F_IBATP_TH_RES] = REG_FIELD(INT7_SET, 0, 0), + [F_INT7_STATUS] = REG_FIELD(INT7_STATUS, 0, 15), + [F_INT6_STATUS] = REG_FIELD(INT6_STATUS, 0, 13), + [F_INT5_STATUS] = REG_FIELD(INT5_STATUS, 0, 13), + [F_INT4_STATUS] = REG_FIELD(INT4_STATUS, 0, 9), + [F_INT3_STATUS] = REG_FIELD(INT3_STATUS, 0, 15), + [F_INT2_STATUS] = REG_FIELD(INT2_STATUS, 0, 15), + [F_INT1_STATUS] = REG_FIELD(INT1_STATUS, 0, 15), + [F_INT0_STATUS] = REG_FIELD(INT0_STATUS, 0, 7), + [F_ILIM_DECREASE] = REG_FIELD(OTPREG0, 0, 15), + [F_RESERVE_OTPREG1] = REG_FIELD(OTPREG1, 0, 15), + [F_POWER_SAVE_MODE] = REG_FIELD(SMBREG, 0, 15), + [F_DEBUG_MODE_SET] = REG_FIELD(DEBUG_MODE_SET, 0, 15), + [F_DEBUG0x14] = REG_FIELD(DEBUG0x14, 0, 15), + [F_DEBUG0x1A] = REG_FIELD(DEBUG0x1A, 0, 15), +}; + +/* CHGSTM_STATEs */ +#define CHGSTM_SUSPEND 0x00 +#define CHGSTM_TRICKLE_CHARGE 0x01 +#define CHGSTM_PRE_CHARGE 0x02 +#define CHGSTM_FAST_CHARGE 0x03 +#define CHGSTM_TOP_OFF 0x04 +#define CHGSTM_DONE 0x05 +#define CHGSTM_OTG 0x08 +#define CHGSTM_OTG_DONE 0x09 +#define CHGSTM_TEMPERATURE_ERROR_1 0x10 +#define CHGSTM_TEMPERATURE_ERROR_2 0x11 +#define CHGSTM_TEMPERATURE_ERROR_3 0x12 +#define CHGSTM_TEMPERATURE_ERROR_4 0x13 +#define CHGSTM_TEMPERATURE_ERROR_5 0x14 +#define CHGSTM_TEMPERATURE_ERROR_6 0x15 +#define CHGSTM_TEMPERATURE_ERROR_7 0x18 +#define CHGSTM_THERMAL_SHUT_DOWN_1 0x20 +#define CHGSTM_THERMAL_SHUT_DOWN_2 0x21 +#define CHGSTM_THERMAL_SHUT_DOWN_3 0x22 +#define CHGSTM_THERMAL_SHUT_DOWN_4 0x23 +#define CHGSTM_THERMAL_SHUT_DOWN_5 0x24 +#define CHGSTM_THERMAL_SHUT_DOWN_6 0x25 +#define CHGSTM_THERMAL_SHUT_DOWN_7 0x28 +#define CHGSTM_BATTERY_ERROR 0x40 + +/* VBAT_VSYS_STATUS */ +#define STATUS_VSYS_OV BIT(15) +#define STATUS_VSYS_SSD BIT(14) +#define STATUS_VSYS_SCP BIT(13) +#define STATUS_VSYS_UVN BIT(12) +#define STATUS_IBAT_SHORT BIT(6) +#define STATUS_VBAT_OV BIT(3) +#define STATUS_DEAD_BAT BIT(0) + +/* VBUS_VCC_STATUS */ +#define STATUS_VACP_DET BIT(12) +#define STATUS_VCC_OVP BIT(11) +#define STATUS_ILIM_VCC_MOD BIT(10) +#define STATUS_VCC_CLPS BIT(9) +#define STATUS_VCC_DET BIT(8) +#define STATUS_VBUS_OVP BIT(3) +#define STATUS_ILIM_VBUS_MOD BIT(2) +#define STATUS_VBUS_CLPS BIT(1) +#define STATUS_VBUS_DET BIT(0) + +/* Interrupt set/status definitions */ + +/* INT 0 */ +#define INT0_INT7_STATUS BIT(7) +#define INT0_INT6_STATUS BIT(6) +#define INT0_INT5_STATUS BIT(5) +#define INT0_INT4_STATUS BIT(4) +#define INT0_INT3_STATUS BIT(3) +#define INT0_INT2_STATUS BIT(2) +#define INT0_INT1_STATUS BIT(1) +#define INT0_INT0_STATUS BIT(0) +#define INT0_ALL 0xff + +/* INT 1 */ +#define VBUS_RBUV_DET BIT(15) +#define VBUS_RBUV_RES BIT(14) +#define VBUS_TH_DET BIT(9) +#define VBUS_TH_RES BIT(8) +#define VBUS_IIN_MOD BIT(6) +#define VBUS_OV_DET BIT(5) +#define VBUS_OV_RES BIT(4) +#define VBUS_CLPS_DET BIT(3) +#define VBUS_CLPS BIT(2) +#define VBUS_DET BIT(1) +#define VBUS_RES BIT(0) +#define INT1_ALL (VBUS_RBUV_DET|\ + VBUS_RBUV_RES|\ + VBUS_TH_DET |\ + VBUS_TH_RES |\ + VBUS_IIN_MOD|\ + VBUS_OV_DET |\ + VBUS_OV_RES |\ + VBUS_CLPS_DET |\ + VBUS_CLPS |\ + VBUS_DET |\ + VBUS_RES) + +/* INT 2 */ +#define VCC_RBUV_DET BIT(15) +#define VCC_RBUV_RES BIT(14) +#define VCC_TH_DET BIT(9) +#define VCC_TH_RES BIT(8) +#define VCC_IIN_MOD BIT(6) +#define VCC_OVP_DET BIT(5) +#define VCC_OVP_RES BIT(4) +#define VCC_CLPS_DET BIT(3) +#define VCC_CLPS_RES BIT(2) +#define VCC_DET BIT(1) +#define VCC_RES BIT(0) +#define INT2_ALL (VCC_RBUV_DET |\ + VCC_RBUV_RES |\ + VCC_TH_DET |\ + VCC_TH_RES |\ + VCC_IIN_MOD |\ + VCC_OVP_DET |\ + VCC_OVP_RES |\ + VCC_CLPS_DET |\ + VCC_CLPS_RES |\ + VCC_DET |\ + VCC_RES) +/* INT 3 */ +#define TH_DET BIT(15) +#define TH_RMV BIT(14) +#define TMP_OUT_DET BIT(11) +#define TMP_OUT_RES BIT(10) +#define VBAT_TH_DET BIT(9) +#define VBAT_TH_RES BIT(8) +#define IBAT_SHORT_DET BIT(7) +#define IBAT_SHORT_RES BIT(6) +#define VBAT_OV_DET BIT(5) +#define VBAT_OV_RES BIT(4) +#define BAT_ASSIST_DET BIT(3) +#define BAT_ASSIST_RES BIT(2) +#define INT3_ALL (TH_DET |\ + TH_RMV |\ + TMP_OUT_DET |\ + TMP_OUT_RES |\ + VBAT_TH_DET |\ + VBAT_TH_RES |\ + IBAT_SHORT_DET |\ + IBAT_SHORT_RES |\ + VBAT_OV_DET |\ + VBAT_OV_RES |\ + BAT_ASSIST_DET |\ + BAT_ASSIST_RES) + +/* INT 4 */ +#define VSYS_TH_DET BIT(9) +#define VSYS_TH_RES BIT(8) +#define VSYS_OV_DET BIT(5) +#define VSYS_OV_RES BIT(4) +#define VSYS_SHT_DET BIT(3) +#define VSYS_SHT_RES BIT(2) +#define VSYS_UV_DET BIT(1) +#define VSYS_UV_RES BIT(0) +#define INT4_ALL (VSYS_TH_DET |\ + VSYS_TH_RES |\ + VSYS_OV_DET |\ + VSYS_OV_RES |\ + VSYS_SHT_DET |\ + VSYS_SHT_RES |\ + VSYS_UV_DET |\ + VSYS_UV_RES) + +/* INT 5*/ +#define OTP_LOAD_DONE BIT(13) +#define PWR_ON BIT(12) +#define EXTIADP_TRNS BIT(11) +#define EXTIADP_TH_DET BIT(9) +#define EXIADP_TH_RES BIT(8) +#define BAT_MNT_DET BIT(7) +#define BAT_MNT_RES BIT(6) +#define TSD_DET BIT(5) +#define TSD_RES BIT(4) +#define CHGWDT_EXP BIT(3) +#define THERMWDT_EXP BIT(2) +#define TMP_TRNS BIT(1) +#define CHG_TRNS BIT(0) +#define INT5_ALL (OTP_LOAD_DONE |\ + PWR_ON |\ + EXTIADP_TRNS |\ + EXTIADP_TH_DET |\ + EXIADP_TH_RES |\ + BAT_MNT_DET |\ + BAT_MNT_RES |\ + TSD_DET |\ + TSD_RES |\ + CHGWDT_EXP |\ + THERMWDT_EXP |\ + TMP_TRNS |\ + CHG_TRNS) + +/* INT 6*/ +#define VBUS_UCD_PORT_DET BIT(13) +#define VBUS_UCD_UCHG_DET BIT(12) +#define VBUS_UCD_URID_RMV BIT(11) +#define VBUS_UCD_OTG_DET BIT(10) +#define VBUS_UCD_URID_MOD BIT(8) +#define VCC_UCD_PORT_DET BIT(5) +#define VCC_UCD_UCHG_DET BIT(4) +#define VCC_UCD_URID_RMV BIT(3) +#define VCC_UCD_OTG_DET BIT(2) +#define VCC_UCD_URID_MOD BIT(0) +#define INT6_ALL (VBUS_UCD_PORT_DET |\ + VBUS_UCD_UCHG_DET |\ + VBUS_UCD_URID_RMV |\ + VBUS_UCD_OTG_DET |\ + VBUS_UCD_URID_MOD |\ + VCC_UCD_PORT_DET |\ + VCC_UCD_UCHG_DET |\ + VCC_UCD_URID_RMV |\ + VCC_UCD_OTG_DET |\ + VCC_UCD_URID_MOD) + +/* INT 7 */ +#define PROCHOT_DET BIT(15) +#define PROCHOT_RES BIT(14) +#define VACP_DET BIT(11) +#define VACP_RES BIT(10) +#define VACP_TH_DET BIT(9) +#define VACP_TH_RES BIT(8) +#define IACP_TH_DET BIT(7) +#define IACP_THE_RES BIT(6) +#define THERM_TH_DET BIT(5) +#define THERM_TH_RES BIT(4) +#define IBATM_TH_DET BIT(3) +#define IBATM_TH_RES BIT(2) +#define IBATP_TH_DET BIT(1) +#define IBATP_TH_RES BIT(0) +#define INT7_ALL (PROCHOT_DET |\ + PROCHOT_RES |\ + VACP_DET |\ + VACP_RES |\ + VACP_TH_DET |\ + VACP_TH_RES |\ + IACP_TH_DET |\ + IACP_THE_RES |\ + THERM_TH_DET |\ + THERM_TH_RES |\ + IBATM_TH_DET |\ + IBATM_TH_RES |\ + IBATP_TH_DET |\ + IBATP_TH_RES) + +/* SYSTEM_CTRL_SET*/ +#define MONRST BIT(6) +#define ALMRST BIT(5) +#define CHGRST BIT(4) +#define OTPLD BIT(1) +#define ALLRST BIT(0) + +/* F_BATTEMP */ +#define ROOM 0x0 +#define HOT1 0x1 +#define HOT2 0x2 +#define HOT3 0x3 +#define COLD1 0x4 +#define COLD2 0x5 +#define TEMP_DIS 0x6 +#define BATT_OPEN 0x7 + +#endif -- cgit v1.2.3 From 72073aa1e262607126cb4025bd8f08044f677ab6 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Fri, 8 May 2020 18:51:42 +0300 Subject: power: supply: Fix Kconfig help text indentiation Indent the help text as explained in Documentation/process/coding-style.rst Signed-off-by: Matti Vaittinen Signed-off-by: Sebastian Reichel --- drivers/power/supply/Kconfig | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index d058f19922ad..6343e2ead1ab 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -426,7 +426,7 @@ config CHARGER_PCF50633 tristate "NXP PCF50633 MBC" depends on MFD_PCF50633 help - Say Y to include support for NXP PCF50633 Main Battery Charger. + Say Y to include support for NXP PCF50633 Main Battery Charger. config BATTERY_RX51 tristate "Nokia RX-51 (N900) battery driver" @@ -620,15 +620,15 @@ config CHARGER_TPS65090 tristate "TPS65090 battery charger driver" depends on MFD_TPS65090 help - Say Y here to enable support for battery charging with TPS65090 - PMIC chips. + Say Y here to enable support for battery charging with TPS65090 + PMIC chips. config CHARGER_TPS65217 tristate "TPS65217 battery charger driver" depends on MFD_TPS65217 help - Say Y here to enable support for battery charging with TPS65217 - PMIC chips. + Say Y here to enable support for battery charging with TPS65217 + PMIC chips. config BATTERY_GAUGE_LTC2941 tristate "LTC2941/LTC2943 Battery Gauge Driver" @@ -682,16 +682,16 @@ config CHARGER_SC2731 tristate "Spreadtrum SC2731 charger driver" depends on MFD_SC27XX_PMIC || COMPILE_TEST help - Say Y here to enable support for battery charging with SC2731 - PMIC chips. + Say Y here to enable support for battery charging with SC2731 + PMIC chips. config FUEL_GAUGE_SC27XX tristate "Spreadtrum SC27XX fuel gauge driver" depends on MFD_SC27XX_PMIC || COMPILE_TEST depends on IIO help - Say Y here to enable support for fuel gauge with SC27XX - PMIC chips. + Say Y here to enable support for fuel gauge with SC27XX + PMIC chips. config CHARGER_UCS1002 tristate "Microchip UCS1002 USB Port Power Controller" @@ -709,9 +709,9 @@ config CHARGER_BD70528 select LINEAR_RANGES default n help - Say Y here to enable support for getting battery status - information and altering charger configurations from charger - block of the ROHM BD70528 Power Management IC. + Say Y here to enable support for getting battery status + information and altering charger configurations from charger + block of the ROHM BD70528 Power Management IC. config CHARGER_BD99954 tristate "ROHM bd99954 charger driver" -- cgit v1.2.3 From 75ffb420aa715a3275bde04b64b101e91fe1f2b5 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Fri, 8 May 2020 18:52:45 +0300 Subject: power: supply: KConfig cleanup default n The "default n" is not needed as it is, well, default. Clean the KConfig by removing "default n". Signed-off-by: Matti Vaittinen Reviewed-by: Andy Shevchenko Signed-off-by: Sebastian Reichel --- drivers/power/supply/Kconfig | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 6343e2ead1ab..81b567ee1638 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -671,7 +671,6 @@ config CHARGER_RT9455 config CHARGER_CROS_USBPD tristate "ChromeOS EC based USBPD charger" depends on CROS_USBPD_NOTIFY - default n help Say Y here to enable ChromeOS EC based USBPD charger driver. This driver gets various bits of information about @@ -707,7 +706,6 @@ config CHARGER_BD70528 tristate "ROHM bd70528 charger driver" depends on MFD_ROHM_BD70528 select LINEAR_RANGES - default n help Say Y here to enable support for getting battery status information and altering charger configurations from charger -- cgit v1.2.3 From c32ea07a30630ace950e07ffe7a18bdcc25898e1 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sun, 29 Mar 2020 18:15:44 +0200 Subject: power: supply: smb347-charger: IRQSTAT_D is volatile Fix failure when USB cable is connected: smb347 2-006a: reading IRQSTAT_D failed Fixes: 1502cfe19bac ("smb347-charger: Fix battery status reporting logic for charger faults") Tested-by: David Heidelberg Signed-off-by: Dmitry Osipenko Signed-off-by: David Heidelberg Signed-off-by: Sebastian Reichel --- drivers/power/supply/smb347-charger.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/smb347-charger.c b/drivers/power/supply/smb347-charger.c index c1d124b8be0c..d102921b3ab2 100644 --- a/drivers/power/supply/smb347-charger.c +++ b/drivers/power/supply/smb347-charger.c @@ -1138,6 +1138,7 @@ static bool smb347_volatile_reg(struct device *dev, unsigned int reg) switch (reg) { case IRQSTAT_A: case IRQSTAT_C: + case IRQSTAT_D: case IRQSTAT_E: case IRQSTAT_F: case STAT_A: -- cgit v1.2.3 From fa7cc725a343e305bef78e8d65be48f37d3f7720 Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Sun, 29 Mar 2020 18:15:45 +0200 Subject: power: supply: smb347-charger: Add delay before getting IRQSTAT This delay-fix is picked up from downstream driver, we measured that 25 - 35 ms delay ensure that we get required data. Tested on SMB347 on Nexus 7 2012. Otherwise IRQSTAT_E fails to provide correct information. Signed-off-by: Dmitry Osipenko Signed-off-by: David Heidelberg Signed-off-by: Sebastian Reichel --- drivers/power/supply/smb347-charger.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/smb347-charger.c b/drivers/power/supply/smb347-charger.c index d102921b3ab2..f99026d81f2a 100644 --- a/drivers/power/supply/smb347-charger.c +++ b/drivers/power/supply/smb347-charger.c @@ -8,6 +8,7 @@ * Mika Westerberg */ +#include #include #include #include @@ -708,6 +709,9 @@ static irqreturn_t smb347_interrupt(int irq, void *data) bool handled = false; int ret; + /* SMB347 it needs at least 20ms for setting IRQSTAT_E_*IN_UV_IRQ */ + usleep_range(25000, 35000); + ret = regmap_read(smb->regmap, STAT_C, &stat_c); if (ret < 0) { dev_warn(smb->dev, "reading STAT_C failed\n"); -- cgit v1.2.3 From 29e9eff40f5edc2e5de63b28e700e82ed2b6b95c Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Sat, 21 Dec 2019 08:17:51 +0100 Subject: power: supply: olpc_battery: fix the power supply name The framework is unhappy about them, because it uses the names in sysfs attributes: power_supply olpc-ac: hwmon: 'olpc-ac' is not a valid name attribute, please fix power_supply olpc-battery: hwmon: 'olpc-battery' is not a valid name attribute, please fix See also commit 648cd48c9e56 ("hwmon: Do not accept invalid name attributes") and commit 74d3b6419772 ("hwmon: Relax name attribute validation for new APIs"). Signed-off-by: Lubomir Rintel Signed-off-by: Sebastian Reichel --- arch/x86/platform/olpc/olpc-xo1-sci.c | 4 ++-- arch/x86/platform/olpc/olpc-xo15-sci.c | 4 ++-- drivers/platform/olpc/olpc-xo175-ec.c | 4 ++-- drivers/power/supply/olpc_battery.c | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/power/supply') diff --git a/arch/x86/platform/olpc/olpc-xo1-sci.c b/arch/x86/platform/olpc/olpc-xo1-sci.c index 933dd4fe3a97..f03a6883dcc6 100644 --- a/arch/x86/platform/olpc/olpc-xo1-sci.c +++ b/arch/x86/platform/olpc/olpc-xo1-sci.c @@ -52,7 +52,7 @@ static const char * const lid_wake_mode_names[] = { static void battery_status_changed(void) { - struct power_supply *psy = power_supply_get_by_name("olpc-battery"); + struct power_supply *psy = power_supply_get_by_name("olpc_battery"); if (psy) { power_supply_changed(psy); @@ -62,7 +62,7 @@ static void battery_status_changed(void) static void ac_status_changed(void) { - struct power_supply *psy = power_supply_get_by_name("olpc-ac"); + struct power_supply *psy = power_supply_get_by_name("olpc_ac"); if (psy) { power_supply_changed(psy); diff --git a/arch/x86/platform/olpc/olpc-xo15-sci.c b/arch/x86/platform/olpc/olpc-xo15-sci.c index 089413cd944e..85f4638764d6 100644 --- a/arch/x86/platform/olpc/olpc-xo15-sci.c +++ b/arch/x86/platform/olpc/olpc-xo15-sci.c @@ -75,7 +75,7 @@ static struct kobj_attribute lid_wake_on_close_attr = static void battery_status_changed(void) { - struct power_supply *psy = power_supply_get_by_name("olpc-battery"); + struct power_supply *psy = power_supply_get_by_name("olpc_battery"); if (psy) { power_supply_changed(psy); @@ -85,7 +85,7 @@ static void battery_status_changed(void) static void ac_status_changed(void) { - struct power_supply *psy = power_supply_get_by_name("olpc-ac"); + struct power_supply *psy = power_supply_get_by_name("olpc_ac"); if (psy) { power_supply_changed(psy); diff --git a/drivers/platform/olpc/olpc-xo175-ec.c b/drivers/platform/olpc/olpc-xo175-ec.c index 83ed1fbf73cf..5e1d14e35f20 100644 --- a/drivers/platform/olpc/olpc-xo175-ec.c +++ b/drivers/platform/olpc/olpc-xo175-ec.c @@ -410,7 +410,7 @@ static void olpc_xo175_ec_complete(void *arg) dev_dbg(dev, "got event %.2x\n", byte); switch (byte) { case EVENT_AC_CHANGE: - psy = power_supply_get_by_name("olpc-ac"); + psy = power_supply_get_by_name("olpc_ac"); if (psy) { power_supply_changed(psy); power_supply_put(psy); @@ -420,7 +420,7 @@ static void olpc_xo175_ec_complete(void *arg) case EVENT_BATTERY_CRITICAL: case EVENT_BATTERY_SOC_CHANGE: case EVENT_BATTERY_ERROR: - psy = power_supply_get_by_name("olpc-battery"); + psy = power_supply_get_by_name("olpc_battery"); if (psy) { power_supply_changed(psy); power_supply_put(psy); diff --git a/drivers/power/supply/olpc_battery.c b/drivers/power/supply/olpc_battery.c index ad0e9e0edb3f..e0476ec06601 100644 --- a/drivers/power/supply/olpc_battery.c +++ b/drivers/power/supply/olpc_battery.c @@ -88,7 +88,7 @@ static enum power_supply_property olpc_ac_props[] = { }; static const struct power_supply_desc olpc_ac_desc = { - .name = "olpc-ac", + .name = "olpc_ac", .type = POWER_SUPPLY_TYPE_MAINS, .properties = olpc_ac_props, .num_properties = ARRAY_SIZE(olpc_ac_props), @@ -605,7 +605,7 @@ static const struct attribute_group *olpc_bat_sysfs_groups[] = { *********************************************************************/ static struct power_supply_desc olpc_bat_desc = { - .name = "olpc-battery", + .name = "olpc_battery", .get_property = olpc_bat_get_property, .use_for_apm = 1, }; -- cgit v1.2.3 From 0383024f811aa469df258039807810fc3793a105 Mon Sep 17 00:00:00 2001 From: Jonathan Bakker Date: Mon, 4 May 2020 15:12:58 -0700 Subject: power: supply: max17040: Correct voltage reading According to the datasheet available at (1), the bottom four bits are always zero and the actual voltage is 1.25x this value in mV. Since the kernel API specifies that voltages should be in uV, it should report 1250x the shifted value. 1) https://datasheets.maximintegrated.com/en/ds/MAX17040-MAX17041.pdf Signed-off-by: Jonathan Bakker Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17040_battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c index 8a1f0ee493aa..48aa44665e2f 100644 --- a/drivers/power/supply/max17040_battery.c +++ b/drivers/power/supply/max17040_battery.c @@ -126,7 +126,7 @@ static void max17040_get_vcell(struct i2c_client *client) vcell = max17040_read_reg(client, MAX17040_VCELL); - chip->vcell = vcell; + chip->vcell = (vcell >> 4) * 1250; } static void max17040_get_soc(struct i2c_client *client) -- cgit v1.2.3 From 1e4724d0b7d1db3b7307fc95fb902a97f4a7b85a Mon Sep 17 00:00:00 2001 From: Michał Mirosław Date: Mon, 4 May 2020 21:47:30 +0200 Subject: power: bq25890: use proper CURRENT_NOW property for I_BAT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Charge Current is more apropriately reflected by CURRENT_NOW property (measured current) than CONSTANT_CURRENT_VOLTAGE (configured CC-phase current limit). Fix the reference and make the sign reflect direction of the current. Signed-off-by: Michał Mirosław Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq25890_charger.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index 20b9824ef5ac..08d31ed4bc27 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -427,15 +427,6 @@ static int bq25890_power_supply_get_property(struct power_supply *psy, val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; break; - case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: - ret = bq25890_field_read(bq, F_ICHGR); /* read measured value */ - if (ret < 0) - return ret; - - /* converted_val = ADC_val * 50mA (table 10.3.19) */ - val->intval = ret * 50000; - break; - case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: val->intval = bq25890_find_val(bq->init_data.ichg, TBL_ICHG); break; @@ -471,6 +462,15 @@ static int bq25890_power_supply_get_property(struct power_supply *psy, val->intval = 2304000 + ret * 20000; break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = bq25890_field_read(bq, F_ICHGR); /* read measured value */ + if (ret < 0) + return ret; + + /* converted_val = ADC_val * 50mA (table 10.3.19) */ + val->intval = ret * -50000; + break; + default: return -EINVAL; } @@ -645,12 +645,12 @@ static const enum power_supply_property bq25890_power_supply_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_HEALTH, - 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_CHARGE_TERM_CURRENT, POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, }; static char *bq25890_charger_supplied_to[] = { -- cgit v1.2.3 From 21d90eda433f5d39930ca7133340225b8fb7940f Mon Sep 17 00:00:00 2001 From: Michał Mirosław Date: Mon, 4 May 2020 21:47:45 +0200 Subject: power: bq25890: fix ADC mode configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Datasheet describes two modes for reading ADC measurements: 1. continuous, 1 Hz - enabled and started by CONV_RATE bit 2. one-shot - triggered by CONV_START bit In continuous mode, CONV_START is read-only and signifies an ongoing conversion. Change the code to follow the datasheet and really disable continuous mode for power saving. Signed-off-by: Michał Mirosław Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq25890_charger.c | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index 08d31ed4bc27..c9f7877ded95 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -126,6 +126,7 @@ static const struct regmap_access_table bq25890_writeable_regs = { static const struct regmap_range bq25890_volatile_reg_ranges[] = { regmap_reg_range(0x00, 0x00), + regmap_reg_range(0x02, 0x02), regmap_reg_range(0x09, 0x09), regmap_reg_range(0x0b, 0x14), }; @@ -374,18 +375,38 @@ enum bq25890_chrg_fault { CHRG_FAULT_TIMER_EXPIRED, }; +static bool bq25890_is_adc_property(enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + case POWER_SUPPLY_PROP_CURRENT_NOW: + return true; + + default: + return false; + } +} + static int bq25890_power_supply_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - int ret; struct bq25890_device *bq = power_supply_get_drvdata(psy); struct bq25890_state state; + bool do_adc_conv; + int ret; mutex_lock(&bq->lock); 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); + if (do_adc_conv) + regmap_field_read_poll_timeout(bq->rmap_fields[F_CONV_START], + ret, !ret, 25000, 1000000); + switch (psp) { case POWER_SUPPLY_PROP_STATUS: if (!state.online) @@ -623,8 +644,8 @@ static int bq25890_hw_init(struct bq25890_device *bq) } } - /* Configure ADC for continuous conversions. This does not enable it. */ - ret = bq25890_field_write(bq, F_CONV_RATE, 1); + /* Configure ADC for continuous conversions when charging */ + ret = bq25890_field_write(bq, F_CONV_RATE, !!bq->state.online); if (ret < 0) { dev_dbg(bq->dev, "Config ADC failed %d\n", ret); return ret; @@ -966,7 +987,7 @@ static int bq25890_suspend(struct device *dev) * If charger is removed, while in suspend, make sure ADC is diabled * since it consumes slightly more power. */ - return bq25890_field_write(bq, F_CONV_START, 0); + return bq25890_field_write(bq, F_CONV_RATE, 0); } static int bq25890_resume(struct device *dev) @@ -982,7 +1003,7 @@ static int bq25890_resume(struct device *dev) /* Re-enable ADC only if charger is plugged in. */ if (bq->state.online) { - ret = bq25890_field_write(bq, F_CONV_START, 1); + ret = bq25890_field_write(bq, F_CONV_RATE, 1); if (ret < 0) goto unlock; } -- cgit v1.2.3 From 3b4df57bef695a41a23feb1017263be5f5f8d51c Mon Sep 17 00:00:00 2001 From: Michał Mirosław Date: Mon, 4 May 2020 21:47:45 +0200 Subject: power: bq25890: update state on property read MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Edge interrupts from the charger may be lost or stuck in fault mode since probe(). Check if something changed everytime userspace wants some data. Signed-off-by: Michał Mirosław Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq25890_charger.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index c9f7877ded95..be9de68a3fc2 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -387,6 +387,8 @@ static bool bq25890_is_adc_property(enum power_supply_property psp) } } +static irqreturn_t __bq25890_handle_irq(struct bq25890_device *bq); + static int bq25890_power_supply_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -397,6 +399,8 @@ static int bq25890_power_supply_get_property(struct power_supply *psy, 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); if (do_adc_conv) -- cgit v1.2.3 From b302a0ae7205a3ccda238cb057224b5115a6115c Mon Sep 17 00:00:00 2001 From: Michał Mirosław Date: Mon, 4 May 2020 21:47:46 +0200 Subject: power: bq25890: implement CHARGE_TYPE property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Report charging type based on recently read state. Signed-off-by: Michał Mirosław Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq25890_charger.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index be9de68a3fc2..6d8e0e2bfd8a 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -427,6 +427,18 @@ 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 || + state.chrg_status == STATUS_TERMINATION_DONE) + val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; + else if (state.chrg_status == STATUS_PRE_CHARGING) + val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD; + else if (state.chrg_status == STATUS_FAST_CHARGING) + val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; + else /* unreachable */ + val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; + break; + case POWER_SUPPLY_PROP_MANUFACTURER: val->strval = BQ25890_MANUFACTURER; break; @@ -668,6 +680,7 @@ static const enum power_supply_property bq25890_power_supply_props[] = { POWER_SUPPLY_PROP_MANUFACTURER, POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_CHARGE_TYPE, POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, -- cgit v1.2.3 From c942656ddb3a0c248e0c53a2ebd4a36adaa46cbc Mon Sep 17 00:00:00 2001 From: Michał Mirosław Date: Mon, 4 May 2020 21:47:46 +0200 Subject: power: bq25890: implement PRECHARGE_CURRENT property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Report configured precharge current. Signed-off-by: Michał Mirosław Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq25890_charger.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index 6d8e0e2bfd8a..0c9c02088b52 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -486,6 +486,10 @@ static int bq25890_power_supply_get_property(struct power_supply *psy, val->intval = bq25890_find_val(bq->init_data.vreg, TBL_VREG); break; + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: + val->intval = bq25890_find_val(bq->init_data.iprechg, TBL_ITERM); + break; + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: val->intval = bq25890_find_val(bq->init_data.iterm, TBL_ITERM); break; @@ -686,6 +690,7 @@ static const enum power_supply_property bq25890_power_supply_props[] = { POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, + POWER_SUPPLY_PROP_PRECHARGE_CURRENT, POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CURRENT_NOW, -- cgit v1.2.3 From 478efc79ee32a3f1227cda49295ef24cda8b114c Mon Sep 17 00:00:00 2001 From: Michał Mirosław Date: Mon, 4 May 2020 21:47:47 +0200 Subject: power: bq25890: implement INPUT_CURRENT_LIMIT property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Report REG00.IINLIM value as INPUT_CURRENT_LIMIT property. Signed-off-by: Michał Mirosław Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq25890_charger.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index 0c9c02088b52..77150667e36b 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -254,6 +254,7 @@ enum bq25890_table_ids { /* range tables */ TBL_ICHG, TBL_ITERM, + TBL_IILIM, TBL_VREG, TBL_BOOSTV, TBL_SYSVMIN, @@ -294,6 +295,7 @@ static const union { /* TODO: BQ25896 has max ICHG 3008 mA */ [TBL_ICHG] = { .rt = {0, 5056000, 64000} }, /* uA */ [TBL_ITERM] = { .rt = {64000, 1024000, 64000} }, /* uA */ + [TBL_IILIM] = { .rt = {50000, 3200000, 50000} }, /* uA */ [TBL_VREG] = { .rt = {3840000, 4608000, 16000} }, /* uV */ [TBL_BOOSTV] = { .rt = {4550000, 5510000, 64000} }, /* uV */ [TBL_SYSVMIN] = { .rt = {3000000, 3700000, 100000} }, /* uV */ @@ -494,6 +496,14 @@ static int bq25890_power_supply_get_property(struct power_supply *psy, val->intval = bq25890_find_val(bq->init_data.iterm, TBL_ITERM); break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = bq25890_field_read(bq, F_IILIM); + if (ret < 0) + return ret; + + val->intval = bq25890_find_val(ret, TBL_IILIM); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: ret = bq25890_field_read(bq, F_SYSV); /* read measured value */ if (ret < 0) @@ -692,6 +702,7 @@ static const enum power_supply_property bq25890_power_supply_props[] = { POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, POWER_SUPPLY_PROP_PRECHARGE_CURRENT, POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CURRENT_NOW, }; -- cgit v1.2.3 From 454b9c1ffd422bfd5cfb7212a66a00a0d8a2c1b4 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 26 Mar 2020 22:09:54 +0100 Subject: power: supply: bq24190_charger: convert to use i2c_new_client_device() Move away from the deprecated API in this comment. Signed-off-by: Wolfram Sang Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq24190_charger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/bq24190_charger.c b/drivers/power/supply/bq24190_charger.c index 453d6332d43a..4540e913057f 100644 --- a/drivers/power/supply/bq24190_charger.c +++ b/drivers/power/supply/bq24190_charger.c @@ -673,7 +673,7 @@ static int bq24190_register_reset(struct bq24190_dev_info *bdi) * { .type = "bq24190", .addr = 0x6b, .properties = pe, .irq = irq }; * struct i2c_adapter ad = { ... }; * i2c_add_adapter(&ad); - * i2c_new_device(&ad, &bi); + * i2c_new_client_device(&ad, &bi); */ if (device_property_read_bool(bdi->dev, "disable-reset")) return 0; -- cgit v1.2.3 From fac1cd327b627a0761d10bb93a2b0d0075452ff9 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 22 May 2020 12:24:48 +0200 Subject: power: charger: max14577: Add proper dt-compatible strings Add device tree compatible strings and create proper modalias structures to let this driver load automatically if compiled as module, because max14577 MFD driver creates MFD cells with such compatible strings. Signed-off-by: Marek Szyprowski Signed-off-by: Sebastian Reichel --- drivers/power/supply/max14577_charger.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/max14577_charger.c b/drivers/power/supply/max14577_charger.c index 8a59feac6468..dcedae18d7be 100644 --- a/drivers/power/supply/max14577_charger.c +++ b/drivers/power/supply/max14577_charger.c @@ -623,9 +623,19 @@ static const struct platform_device_id max14577_charger_id[] = { }; MODULE_DEVICE_TABLE(platform, max14577_charger_id); +static const struct of_device_id of_max14577_charger_dt_match[] = { + { .compatible = "maxim,max14577-charger", + .data = (void *)MAXIM_DEVICE_TYPE_MAX14577, }, + { .compatible = "maxim,max77836-charger", + .data = (void *)MAXIM_DEVICE_TYPE_MAX77836, }, + { }, +}; +MODULE_DEVICE_TABLE(of, of_max14577_charger_dt_match); + static struct platform_driver max14577_charger_driver = { .driver = { .name = "max14577-charger", + .of_match_table = of_max14577_charger_dt_match, }, .probe = max14577_charger_probe, .remove = max14577_charger_remove, -- cgit v1.2.3 From 2e9bcaccef40e81f052e4f228df91feaedd7b26c Mon Sep 17 00:00:00 2001 From: Samuel Zou Date: Mon, 11 May 2020 19:12:57 +0800 Subject: power: supply: Make bd9995x_chip_reset static Fix the following sparse warning: drivers/power/supply/bd99954-charger.c:1028:6: warning: symbol 'bd9995x_chip_reset' was not declared. The bd9995x_chip_reset() has only one call site within bd99954-charger.c It should be static Fixes: 0902f8366491 ("power: supply: Support ROHM bd99954 charger") Reported-by: Hulk Robot Signed-off-by: Samuel Zou Reviewed-by: Matti Vaittinen Signed-off-by: Sebastian Reichel --- drivers/power/supply/bd99954-charger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/bd99954-charger.c b/drivers/power/supply/bd99954-charger.c index 3da39c7293e3..ffd8bfa08179 100644 --- a/drivers/power/supply/bd99954-charger.c +++ b/drivers/power/supply/bd99954-charger.c @@ -1025,7 +1025,7 @@ static int bd9995x_fw_probe(struct bd9995x_device *bd) return 0; } -void bd9995x_chip_reset(void *bd) +static void bd9995x_chip_reset(void *bd) { __bd9995x_chip_reset(bd); } -- cgit v1.2.3 From bac705abcf345c28e419157cfcd1c44032cc9db2 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Wed, 13 May 2020 20:55:58 +0200 Subject: power: supply: core: add capacity error margin property Add a property for reporting the error margin expected by fuel gauge chips. Signed-off-by: Sebastian Reichel --- Documentation/ABI/testing/sysfs-class-power | 15 +++++++++++++++ drivers/power/supply/power_supply_sysfs.c | 1 + include/linux/power_supply.h | 1 + 3 files changed, 17 insertions(+) (limited to 'drivers/power/supply') diff --git a/Documentation/ABI/testing/sysfs-class-power b/Documentation/ABI/testing/sysfs-class-power index bf3b48f022dc..2f896555ae23 100644 --- a/Documentation/ABI/testing/sysfs-class-power +++ b/Documentation/ABI/testing/sysfs-class-power @@ -74,6 +74,21 @@ Description: Access: Read, Write Valid values: 0 - 100 (percent) +What: /sys/class/power_supply//capacity_error_margin +Date: April 2019 +Contact: linux-pm@vger.kernel.org +Description: + Battery capacity measurement becomes unreliable without + recalibration. This values provides the maximum error + margin expected to exist by the fuel gauge in percent. + Values close to 0% will be returned after (re-)calibration + has happened. Over time the error margin will increase. + 100% means, that the capacity related values are basically + completely useless. + + Access: Read + Valid values: 0 - 100 (percent) + What: /sys/class/power_supply//capacity_level Date: June 2009 Contact: linux-pm@vger.kernel.org diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index d21b4e0edf38..e664774a2d1e 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -178,6 +178,7 @@ static struct power_supply_attr power_supply_attrs[] = { POWER_SUPPLY_ATTR(CAPACITY), POWER_SUPPLY_ATTR(CAPACITY_ALERT_MIN), POWER_SUPPLY_ATTR(CAPACITY_ALERT_MAX), + POWER_SUPPLY_ATTR(CAPACITY_ERROR_MARGIN), POWER_SUPPLY_ENUM_ATTR(CAPACITY_LEVEL), POWER_SUPPLY_ATTR(TEMP), POWER_SUPPLY_ATTR(TEMP_MAX), diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 1f60731ec7fe..453a85f25635 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -139,6 +139,7 @@ enum power_supply_property { POWER_SUPPLY_PROP_CAPACITY, /* in percents! */ POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN, /* in percents! */ POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX, /* in percents! */ + POWER_SUPPLY_PROP_CAPACITY_ERROR_MARGIN, /* in percents! */ POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TEMP_MAX, -- cgit v1.2.3 From feabe49e46bb556b8d43e28d4a0d459940f7a5cb Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Wed, 13 May 2020 20:55:59 +0200 Subject: power: supply: core: add manufacture date properties Some smart batteries store their manufacture date, which is useful to identify the battery and/or to know about the cell quality. Signed-off-by: Sebastian Reichel --- Documentation/ABI/testing/sysfs-class-power | 28 ++++++++++++++++++++++++++++ drivers/power/supply/power_supply_sysfs.c | 3 +++ include/linux/power_supply.h | 3 +++ 3 files changed, 34 insertions(+) (limited to 'drivers/power/supply') diff --git a/Documentation/ABI/testing/sysfs-class-power b/Documentation/ABI/testing/sysfs-class-power index 2f896555ae23..e6d7348766b2 100644 --- a/Documentation/ABI/testing/sysfs-class-power +++ b/Documentation/ABI/testing/sysfs-class-power @@ -680,3 +680,31 @@ Description: Valid values: - 1: enabled - 0: disabled + +What: /sys/class/power_supply//manufacture_year +Date: January 2020 +Contact: linux-pm@vger.kernel.org +Description: + Reports the year (following Gregorian calendar) when the device has been + manufactured. + + Access: Read + Valid values: Reported as integer + +What: /sys/class/power_supply//manufacture_month +Date: January 2020 +Contact: linux-pm@vger.kernel.org +Description: + Reports the month when the device has been manufactured. + + Access: Read + Valid values: 1-12 + +What: /sys/class/power_supply//manufacture_day +Date: January 2020 +Contact: linux-pm@vger.kernel.org +Description: + Reports the day of month when the device has been manufactured. + + Access: Read + Valid values: 1-31 diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index e664774a2d1e..78d5382e69f1 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -198,6 +198,9 @@ static struct power_supply_attr power_supply_attrs[] = { POWER_SUPPLY_ATTR(PRECHARGE_CURRENT), POWER_SUPPLY_ATTR(CHARGE_TERM_CURRENT), POWER_SUPPLY_ATTR(CALIBRATE), + POWER_SUPPLY_ATTR(MANUFACTURE_YEAR), + POWER_SUPPLY_ATTR(MANUFACTURE_MONTH), + POWER_SUPPLY_ATTR(MANUFACTURE_DAY), /* Properties of type `const char *' */ POWER_SUPPLY_ATTR(MODEL_NAME), POWER_SUPPLY_ATTR(MANUFACTURER), diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 453a85f25635..63ffe2a0a87b 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -159,6 +159,9 @@ enum power_supply_property { POWER_SUPPLY_PROP_PRECHARGE_CURRENT, POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, POWER_SUPPLY_PROP_CALIBRATE, + POWER_SUPPLY_PROP_MANUFACTURE_YEAR, + POWER_SUPPLY_PROP_MANUFACTURE_MONTH, + POWER_SUPPLY_PROP_MANUFACTURE_DAY, /* Properties of type `const char *' */ POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_MANUFACTURER, -- cgit v1.2.3 From 601c2a543f02da484362b3ff9074b2cfe08750de Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Wed, 13 May 2020 20:56:00 +0200 Subject: power: supply: core: add POWER_SUPPLY_HEALTH_CALIBRATION_REQUIRED Some battery fuel gauges know when the battery needs to be recalibrated before providing usable values. This should be reported via the health property. Signed-off-by: Sebastian Reichel --- Documentation/ABI/testing/sysfs-class-power | 2 +- drivers/power/supply/power_supply_sysfs.c | 1 + include/linux/power_supply.h | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/power/supply') diff --git a/Documentation/ABI/testing/sysfs-class-power b/Documentation/ABI/testing/sysfs-class-power index e6d7348766b2..216d61a22f1e 100644 --- a/Documentation/ABI/testing/sysfs-class-power +++ b/Documentation/ABI/testing/sysfs-class-power @@ -205,7 +205,7 @@ Description: Valid values: "Unknown", "Good", "Overheat", "Dead", "Over voltage", "Unspecified failure", "Cold", "Watchdog timer expire", "Safety timer expire", - "Over current" + "Over current", "Calibration required" What: /sys/class/power_supply//precharge_current Date: June 2017 diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index 78d5382e69f1..bc79560229b5 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -100,6 +100,7 @@ static const char * const POWER_SUPPLY_HEALTH_TEXT[] = { [POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE] = "Watchdog timer expire", [POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE] = "Safety timer expire", [POWER_SUPPLY_HEALTH_OVERCURRENT] = "Over current", + [POWER_SUPPLY_HEALTH_CALIBRATION_REQUIRED] = "Calibration required", }; static const char * const POWER_SUPPLY_TECHNOLOGY_TEXT[] = { diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 63ffe2a0a87b..ac1345a48ad0 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -61,6 +61,7 @@ enum { POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE, POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE, POWER_SUPPLY_HEALTH_OVERCURRENT, + POWER_SUPPLY_HEALTH_CALIBRATION_REQUIRED, }; enum { -- cgit v1.2.3 From 0ff969158ac7167d19a0e86e365086c093836544 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Wed, 13 May 2020 20:56:01 +0200 Subject: power: supply: sbs-battery: Add TI BQ20Z65 support Add support for BQ20Z65 manufacturer data to the sbs-battery driver. Implementation has been verified using the public TRM available from [0] and tested using a GE Flex 3S2P battery. [0] http://www.ti.com/lit/pdf/sluu386 Acked-by: Rob Herring Signed-off-by: Sebastian Reichel --- .../devicetree/bindings/power/supply/sbs_sbs-battery.txt | 1 + drivers/power/supply/sbs-battery.c | 15 ++++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) (limited to 'drivers/power/supply') diff --git a/Documentation/devicetree/bindings/power/supply/sbs_sbs-battery.txt b/Documentation/devicetree/bindings/power/supply/sbs_sbs-battery.txt index 4e78e51018eb..fa5a8b516dbf 100644 --- a/Documentation/devicetree/bindings/power/supply/sbs_sbs-battery.txt +++ b/Documentation/devicetree/bindings/power/supply/sbs_sbs-battery.txt @@ -6,6 +6,7 @@ Required properties : part number compatible string might be used in order to take care of vendor specific registers. Known ,: + ti,bq20z65 ti,bq20z75 Optional properties : diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index 6acd242eed48..a15783802ef8 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -149,8 +149,8 @@ static enum power_supply_property sbs_properties[] = { POWER_SUPPLY_PROP_MODEL_NAME }; -/* Supports special manufacturer commands from TI BQ20Z75 IC. */ -#define SBS_FLAGS_TI_BQ20Z75 BIT(0) +/* Supports special manufacturer commands from TI BQ20Z65 and BQ20Z75 IC. */ +#define SBS_FLAGS_TI_BQ20ZX5 BIT(0) struct sbs_info { struct i2c_client *client; @@ -626,7 +626,7 @@ static int sbs_get_property(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_PRESENT: case POWER_SUPPLY_PROP_HEALTH: - if (chip->flags & SBS_FLAGS_TI_BQ20Z75) + if (chip->flags & SBS_FLAGS_TI_BQ20ZX5) ret = sbs_get_ti_battery_presence_and_health(client, psp, val); else @@ -950,7 +950,7 @@ static int sbs_suspend(struct device *dev) if (chip->poll_time > 0) cancel_delayed_work_sync(&chip->work); - if (chip->flags & SBS_FLAGS_TI_BQ20Z75) { + if (chip->flags & SBS_FLAGS_TI_BQ20ZX5) { /* Write to manufacturer access with sleep command. */ ret = sbs_write_word_data(client, sbs_data[REG_MANUFACTURER_DATA].addr, @@ -970,6 +970,7 @@ static SIMPLE_DEV_PM_OPS(sbs_pm_ops, sbs_suspend, NULL); #endif static const struct i2c_device_id sbs_id[] = { + { "bq20z65", 0 }, { "bq20z75", 0 }, { "sbs-battery", 1 }, {} @@ -978,9 +979,13 @@ MODULE_DEVICE_TABLE(i2c, sbs_id); static const struct of_device_id sbs_dt_ids[] = { { .compatible = "sbs,sbs-battery" }, + { + .compatible = "ti,bq20z65", + .data = (void *)SBS_FLAGS_TI_BQ20ZX5, + }, { .compatible = "ti,bq20z75", - .data = (void *)SBS_FLAGS_TI_BQ20Z75, + .data = (void *)SBS_FLAGS_TI_BQ20ZX5, }, { } }; -- cgit v1.2.3 From d6f56321089203a9f740c7dd7eba4de88f98f993 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Wed, 13 May 2020 20:56:02 +0200 Subject: power: supply: sbs-battery: add POWER_SUPPLY_PROP_CAPACITY_ERROR_MARGIN support Add support for reporting the MaxError register from battery fuel gauges following the smart battery standard. Reviewed-by: Emil Velikov Signed-off-by: Sebastian Reichel --- drivers/power/supply/sbs-battery.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index a15783802ef8..4356fdf25d4a 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -26,6 +26,7 @@ enum { REG_TEMPERATURE, REG_VOLTAGE, REG_CURRENT, + REG_MAX_ERR, REG_CAPACITY, REG_TIME_TO_EMPTY, REG_TIME_TO_FULL, @@ -85,6 +86,8 @@ static const struct chip_data { SBS_DATA(POWER_SUPPLY_PROP_VOLTAGE_NOW, 0x09, 0, 20000), [REG_CURRENT] = SBS_DATA(POWER_SUPPLY_PROP_CURRENT_NOW, 0x0A, -32768, 32767), + [REG_MAX_ERR] = + SBS_DATA(POWER_SUPPLY_PROP_CAPACITY_ERROR_MARGIN, 0x0c, 0, 100), [REG_CAPACITY] = SBS_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0D, 0, 100), [REG_REMAINING_CAPACITY] = @@ -132,6 +135,7 @@ static enum power_supply_property sbs_properties[] = { POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CAPACITY_ERROR_MARGIN, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, @@ -676,6 +680,7 @@ static int sbs_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: case POWER_SUPPLY_PROP_CAPACITY: + case POWER_SUPPLY_PROP_CAPACITY_ERROR_MARGIN: ret = sbs_get_property_index(client, psp); if (ret < 0) break; -- cgit v1.2.3 From c4b12a2f3f3de670f6be5e96092a2cab0b877f1a Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Wed, 13 May 2020 20:56:03 +0200 Subject: power: supply: sbs-battery: simplify read_read_string_data The SBS battery implements SMBus block reads. Currently the driver "emulates" this by doing an I2C byte read for the length followed by an I2C block read. The I2C subsystem actually provides a proper API for doing SMBus block reads, which can and should be used instead. The current implementation does not properly handle packet error checking (PEC). This change requires, that I2C bus drivers support I2C_M_RECV_LEN or directly provide the SMBus API to access device manufacturer and model name. Reviewed-by: Emil Velikov Signed-off-by: Sebastian Reichel --- drivers/power/supply/sbs-battery.c | 65 +++++++------------------------------- 1 file changed, 12 insertions(+), 53 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index 4356fdf25d4a..a9a1d28dabbe 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -202,66 +202,32 @@ static int sbs_read_string_data(struct i2c_client *client, u8 address, char *values) { struct sbs_info *chip = i2c_get_clientdata(client); - s32 ret = 0, block_length = 0; - int retries_length, retries_block; - u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1]; - - retries_length = chip->i2c_retry_count; - retries_block = chip->i2c_retry_count; + int retries = chip->i2c_retry_count; + s32 ret = 0; - /* Adapter needs to support these two functions */ if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_BYTE_DATA | - I2C_FUNC_SMBUS_I2C_BLOCK)){ + I2C_FUNC_SMBUS_READ_BLOCK_DATA)) { return -ENODEV; } - /* Get the length of block data */ - while (retries_length > 0) { - ret = i2c_smbus_read_byte_data(client, address); - if (ret >= 0) - break; - retries_length--; - } - - if (ret < 0) { - dev_dbg(&client->dev, - "%s: i2c read at address 0x%x failed\n", - __func__, address); - return ret; - } - - /* block_length does not include NULL terminator */ - block_length = ret; - if (block_length > I2C_SMBUS_BLOCK_MAX) { - dev_err(&client->dev, - "%s: Returned block_length is longer than 0x%x\n", - __func__, I2C_SMBUS_BLOCK_MAX); - return -EINVAL; - } - /* Get the block data */ - while (retries_block > 0) { - ret = i2c_smbus_read_i2c_block_data( - client, address, - block_length + 1, block_buffer); + while (retries > 0) { + ret = i2c_smbus_read_block_data(client, address, values); if (ret >= 0) break; - retries_block--; + retries--; } if (ret < 0) { - dev_dbg(&client->dev, - "%s: i2c read at address 0x%x failed\n", - __func__, address); + dev_dbg(&client->dev, "%s: failed to read block 0x%x: %d\n", + __func__, address, ret); return ret; } - /* block_buffer[0] == block_length */ - memcpy(values, block_buffer + 1, block_length); - values[block_length] = '\0'; + /* add string termination */ + values[ret] = '\0'; - return ret; + return 0; } static int sbs_write_word_data(struct i2c_client *client, u8 address, @@ -465,14 +431,7 @@ static int sbs_get_battery_property(struct i2c_client *client, static int sbs_get_battery_string_property(struct i2c_client *client, int reg_offset, enum power_supply_property psp, char *val) { - s32 ret; - - ret = sbs_read_string_data(client, sbs_data[reg_offset].addr, val); - - if (ret < 0) - return ret; - - return 0; + return sbs_read_string_data(client, sbs_data[reg_offset].addr, val); } static void sbs_unit_adjustment(struct i2c_client *client, -- cgit v1.2.3 From 79bcd5a4a66076a8a8dacd7f4a3be1952283aef4 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Wed, 13 May 2020 20:56:04 +0200 Subject: power: supply: sbs-battery: add PEC support SBS batteries optionally have support for PEC. This enables PEC handling based on the implemented SBS version as suggested by the standard. The support for PEC is re-evaluated when the battery is hotplugged into the system, since there might be systems supporting batteries from different SBS generations. Reviewed-by: Emil Velikov Signed-off-by: Sebastian Reichel --- drivers/power/supply/sbs-battery.c | 72 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 3 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index a9a1d28dabbe..ab774d491269 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -46,6 +46,14 @@ enum { REG_MODEL_NAME, }; +#define REG_ADDR_SPEC_INFO 0x1A +#define SPEC_INFO_VERSION_MASK GENMASK(7, 4) +#define SPEC_INFO_VERSION_SHIFT 4 + +#define SBS_VERSION_1_0 1 +#define SBS_VERSION_1_1 2 +#define SBS_VERSION_1_1_WITH_PEC 3 + /* Battery Mode defines */ #define BATTERY_MODE_OFFSET 0x03 #define BATTERY_MODE_CAPACITY_MASK BIT(15) @@ -175,6 +183,64 @@ static char model_name[I2C_SMBUS_BLOCK_MAX + 1]; static char manufacturer[I2C_SMBUS_BLOCK_MAX + 1]; static bool force_load; +static int sbs_update_presence(struct sbs_info *chip, bool is_present) +{ + struct i2c_client *client = chip->client; + int retries = chip->i2c_retry_count; + s32 ret = 0; + u8 version; + + if (chip->is_present == is_present) + return 0; + + if (!is_present) { + chip->is_present = false; + /* Disable PEC when no device is present */ + client->flags &= ~I2C_CLIENT_PEC; + return 0; + } + + /* Check if device supports packet error checking and use it */ + while (retries > 0) { + ret = i2c_smbus_read_word_data(client, REG_ADDR_SPEC_INFO); + if (ret >= 0) + break; + + /* + * Some batteries trigger the detection pin before the + * I2C bus is properly connected. This works around the + * issue. + */ + msleep(100); + + retries--; + } + + if (ret < 0) { + dev_dbg(&client->dev, "failed to read spec info: %d\n", ret); + + /* fallback to old behaviour */ + client->flags &= ~I2C_CLIENT_PEC; + chip->is_present = true; + + return ret; + } + + version = (ret & SPEC_INFO_VERSION_MASK) >> SPEC_INFO_VERSION_SHIFT; + + if (version == SBS_VERSION_1_1_WITH_PEC) + client->flags |= I2C_CLIENT_PEC; + else + client->flags &= ~I2C_CLIENT_PEC; + + dev_dbg(&client->dev, "PEC: %s\n", (client->flags & I2C_CLIENT_PEC) ? + "enabled" : "disabled"); + + chip->is_present = true; + + return 0; +} + static int sbs_read_word_data(struct i2c_client *client, u8 address) { struct sbs_info *chip = i2c_get_clientdata(client); @@ -579,7 +645,7 @@ static int sbs_get_property(struct power_supply *psy, return ret; if (psp == POWER_SUPPLY_PROP_PRESENT) { val->intval = ret; - chip->is_present = val->intval; + sbs_update_presence(chip, ret); return 0; } if (ret == 0) @@ -678,7 +744,7 @@ static int sbs_get_property(struct power_supply *psy, if (!chip->gpio_detect && chip->is_present != (ret >= 0)) { - chip->is_present = (ret >= 0); + sbs_update_presence(chip, (ret >= 0)); power_supply_changed(chip->power_supply); } @@ -709,7 +775,7 @@ static void sbs_supply_changed(struct sbs_info *chip) ret = gpiod_get_value_cansleep(chip->gpio_detect); if (ret < 0) return; - chip->is_present = ret; + sbs_update_presence(chip, ret); power_supply_changed(battery); } -- cgit v1.2.3 From 8ce6ee43bd6e8ec0dc1f4bcbfeb103e634233d28 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Wed, 13 May 2020 20:56:05 +0200 Subject: power: supply: sbs-battery: add POWER_SUPPLY_PROP_CURRENT_AVG support Expose averaged current information, which is part of the SBS standard and should be supported by all batteries. Reviewed-by: Emil Velikov Signed-off-by: Sebastian Reichel --- drivers/power/supply/sbs-battery.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index ab774d491269..611a11385496 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -25,7 +25,8 @@ enum { REG_MANUFACTURER_DATA, REG_TEMPERATURE, REG_VOLTAGE, - REG_CURRENT, + REG_CURRENT_NOW, + REG_CURRENT_AVG, REG_MAX_ERR, REG_CAPACITY, REG_TIME_TO_EMPTY, @@ -92,8 +93,10 @@ static const struct chip_data { SBS_DATA(POWER_SUPPLY_PROP_TEMP, 0x08, 0, 65535), [REG_VOLTAGE] = SBS_DATA(POWER_SUPPLY_PROP_VOLTAGE_NOW, 0x09, 0, 20000), - [REG_CURRENT] = + [REG_CURRENT_NOW] = SBS_DATA(POWER_SUPPLY_PROP_CURRENT_NOW, 0x0A, -32768, 32767), + [REG_CURRENT_AVG] = + SBS_DATA(POWER_SUPPLY_PROP_CURRENT_AVG, 0x0B, -32768, 32767), [REG_MAX_ERR] = SBS_DATA(POWER_SUPPLY_PROP_CAPACITY_ERROR_MARGIN, 0x0c, 0, 100), [REG_CAPACITY] = @@ -142,6 +145,7 @@ static enum power_supply_property sbs_properties[] = { POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_AVG, POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_CAPACITY_ERROR_MARGIN, POWER_SUPPLY_PROP_TEMP, @@ -324,7 +328,7 @@ static int sbs_status_correct(struct i2c_client *client, int *intval) { int ret; - ret = sbs_read_word_data(client, sbs_data[REG_CURRENT].addr); + ret = sbs_read_word_data(client, sbs_data[REG_CURRENT_NOW].addr); if (ret < 0) return ret; @@ -521,6 +525,7 @@ static void sbs_unit_adjustment(struct i2c_client *client, case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: case POWER_SUPPLY_PROP_CURRENT_NOW: + case POWER_SUPPLY_PROP_CURRENT_AVG: case POWER_SUPPLY_PROP_CHARGE_NOW: case POWER_SUPPLY_PROP_CHARGE_FULL: case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: @@ -699,6 +704,7 @@ static int sbs_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CYCLE_COUNT: case POWER_SUPPLY_PROP_VOLTAGE_NOW: case POWER_SUPPLY_PROP_CURRENT_NOW: + case POWER_SUPPLY_PROP_CURRENT_AVG: case POWER_SUPPLY_PROP_TEMP: case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: -- cgit v1.2.3 From 3e9544f7a3428a524f8877f6fd99d50848c765ff Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Wed, 13 May 2020 20:56:06 +0200 Subject: power: supply: sbs-battery: Improve POWER_SUPPLY_PROP_TECHNOLOGY support This reads the battery chemistry from the battery chip instead of incorrectly hardcoding the type to be Li-Ion. Reviewed-by: Emil Velikov Signed-off-by: Sebastian Reichel --- drivers/power/supply/sbs-battery.c | 43 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index 611a11385496..b697a42d9ccf 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -43,6 +43,7 @@ enum { REG_DESIGN_CAPACITY_CHARGE, REG_DESIGN_VOLTAGE_MIN, REG_DESIGN_VOLTAGE_MAX, + REG_CHEMISTRY, REG_MANUFACTURER, REG_MODEL_NAME, }; @@ -133,7 +134,9 @@ static const struct chip_data { [REG_MANUFACTURER] = SBS_DATA(POWER_SUPPLY_PROP_MANUFACTURER, 0x20, 0, 65535), [REG_MODEL_NAME] = - SBS_DATA(POWER_SUPPLY_PROP_MODEL_NAME, 0x21, 0, 65535) + SBS_DATA(POWER_SUPPLY_PROP_MODEL_NAME, 0x21, 0, 65535), + [REG_CHEMISTRY] = + SBS_DATA(POWER_SUPPLY_PROP_TECHNOLOGY, 0x22, 0, 65535) }; static enum power_supply_property sbs_properties[] = { @@ -185,6 +188,7 @@ struct sbs_info { static char model_name[I2C_SMBUS_BLOCK_MAX + 1]; static char manufacturer[I2C_SMBUS_BLOCK_MAX + 1]; +static char chemistry[I2C_SMBUS_BLOCK_MAX + 1]; static bool force_load; static int sbs_update_presence(struct sbs_info *chip, bool is_present) @@ -636,6 +640,38 @@ static int sbs_get_property_index(struct i2c_client *client, return -EINVAL; } +static int sbs_get_chemistry(struct i2c_client *client, + union power_supply_propval *val) +{ + enum power_supply_property psp = POWER_SUPPLY_PROP_TECHNOLOGY; + int ret; + + ret = sbs_get_property_index(client, psp); + if (ret < 0) + return ret; + + ret = sbs_get_battery_string_property(client, ret, psp, + chemistry); + if (ret < 0) + return ret; + + if (!strncasecmp(chemistry, "LION", 4)) + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + else if (!strncasecmp(chemistry, "LiP", 3)) + val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO; + else if (!strncasecmp(chemistry, "NiCd", 4)) + val->intval = POWER_SUPPLY_TECHNOLOGY_NiCd; + else if (!strncasecmp(chemistry, "NiMH", 4)) + val->intval = POWER_SUPPLY_TECHNOLOGY_NiMH; + else + val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; + + if (val->intval == POWER_SUPPLY_TECHNOLOGY_UNKNOWN) + dev_warn(&client->dev, "Unknown chemistry: %s\n", chemistry); + + return 0; +} + static int sbs_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -673,7 +709,10 @@ static int sbs_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_TECHNOLOGY: - val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + ret = sbs_get_chemistry(client, val); + if (ret < 0) + break; + goto done; /* don't trigger power_supply_changed()! */ case POWER_SUPPLY_PROP_ENERGY_NOW: -- cgit v1.2.3 From 787fdbcf5b8bce9eea96315eb618f38bf849a76c Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Wed, 13 May 2020 20:56:07 +0200 Subject: power: supply: sbs-battery: add POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT/VOLTAGE_MAX support Expose maximum charge current/voltage information requested by the battery. Reviewed-by: Emil Velikov Signed-off-by: Sebastian Reichel --- drivers/power/supply/sbs-battery.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index b697a42d9ccf..e6f61baa9bed 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -46,6 +46,8 @@ enum { REG_CHEMISTRY, REG_MANUFACTURER, REG_MODEL_NAME, + REG_CHARGE_CURRENT, + REG_CHARGE_VOLTAGE, }; #define REG_ADDR_SPEC_INFO 0x1A @@ -114,6 +116,10 @@ static const struct chip_data { SBS_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 0x12, 0, 65535), [REG_TIME_TO_FULL] = SBS_DATA(POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, 0x13, 0, 65535), + [REG_CHARGE_CURRENT] = + SBS_DATA(POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, 0x14, 0, 65535), + [REG_CHARGE_VOLTAGE] = + SBS_DATA(POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, 0x15, 0, 65535), [REG_STATUS] = SBS_DATA(POWER_SUPPLY_PROP_STATUS, 0x16, 0, 65535), [REG_CAPACITY_LEVEL] = @@ -163,6 +169,8 @@ static enum power_supply_property sbs_properties[] = { POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, /* Properties of type `const char *' */ POWER_SUPPLY_PROP_MANUFACTURER, POWER_SUPPLY_PROP_MODEL_NAME @@ -531,6 +539,8 @@ static void sbs_unit_adjustment(struct i2c_client *client, case POWER_SUPPLY_PROP_CURRENT_NOW: case POWER_SUPPLY_PROP_CURRENT_AVG: case POWER_SUPPLY_PROP_CHARGE_NOW: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: case POWER_SUPPLY_PROP_CHARGE_FULL: case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: val->intval *= BASE_UNIT_CONVERSION; @@ -749,6 +759,8 @@ static int sbs_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: case POWER_SUPPLY_PROP_CAPACITY: case POWER_SUPPLY_PROP_CAPACITY_ERROR_MARGIN: ret = sbs_get_property_index(client, psp); -- cgit v1.2.3 From 7721c2fd2668c751f9ba33b35db08b86293869e5 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Wed, 13 May 2020 20:56:08 +0200 Subject: power: supply: sbs-battery: add MANUFACTURE_DATE support Expose the battery's manufacture date to userspace. Reviewed-by: Emil Velikov Signed-off-by: Sebastian Reichel --- drivers/power/supply/sbs-battery.c | 43 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index e6f61baa9bed..4fa553d61db2 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -58,6 +58,8 @@ enum { #define SBS_VERSION_1_1 2 #define SBS_VERSION_1_1_WITH_PEC 3 +#define REG_ADDR_MANUFACTURE_DATE 0x1B + /* Battery Mode defines */ #define BATTERY_MODE_OFFSET 0x03 #define BATTERY_MODE_CAPACITY_MASK BIT(15) @@ -171,6 +173,9 @@ static enum power_supply_property sbs_properties[] = { POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, + POWER_SUPPLY_PROP_MANUFACTURE_YEAR, + POWER_SUPPLY_PROP_MANUFACTURE_MONTH, + POWER_SUPPLY_PROP_MANUFACTURE_DAY, /* Properties of type `const char *' */ POWER_SUPPLY_PROP_MANUFACTURER, POWER_SUPPLY_PROP_MODEL_NAME @@ -682,6 +687,38 @@ static int sbs_get_chemistry(struct i2c_client *client, return 0; } +static int sbs_get_battery_manufacture_date(struct i2c_client *client, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret; + u16 day, month, year; + + ret = sbs_read_word_data(client, REG_ADDR_MANUFACTURE_DATE); + if (ret < 0) + return ret; + + day = ret & GENMASK(4, 0); + month = (ret & GENMASK(8, 5)) >> 5; + year = ((ret & GENMASK(15, 9)) >> 9) + 1980; + + switch (psp) { + case POWER_SUPPLY_PROP_MANUFACTURE_YEAR: + val->intval = year; + break; + case POWER_SUPPLY_PROP_MANUFACTURE_MONTH: + val->intval = month; + break; + case POWER_SUPPLY_PROP_MANUFACTURE_DAY: + val->intval = day; + break; + default: + return -EINVAL; + } + + return 0; +} + static int sbs_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -790,6 +827,12 @@ static int sbs_get_property(struct power_supply *psy, val->strval = manufacturer; break; + case POWER_SUPPLY_PROP_MANUFACTURE_YEAR: + case POWER_SUPPLY_PROP_MANUFACTURE_MONTH: + case POWER_SUPPLY_PROP_MANUFACTURE_DAY: + ret = sbs_get_battery_manufacture_date(client, psp, val); + break; + default: dev_err(&client->dev, "%s: INVALID property\n", __func__); -- cgit v1.2.3 From 6f72a07aa6e9fc0f7aa08b19f960be7748bad10f Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Wed, 13 May 2020 20:56:09 +0200 Subject: power: supply: sbs-battery: add POWER_SUPPLY_HEALTH_CALIBRATION_REQUIRED support Add support for reporting the SBS battery's condition flag to userspace using the new "Calibration required" health status. Reviewed-by: Emil Velikov Signed-off-by: Sebastian Reichel --- drivers/power/supply/sbs-battery.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index 4fa553d61db2..2a2b926ad75c 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -23,6 +23,7 @@ enum { REG_MANUFACTURER_DATA, + REG_BATTERY_MODE, REG_TEMPERATURE, REG_VOLTAGE, REG_CURRENT_NOW, @@ -94,6 +95,8 @@ static const struct chip_data { } sbs_data[] = { [REG_MANUFACTURER_DATA] = SBS_DATA(POWER_SUPPLY_PROP_PRESENT, 0x00, 0, 65535), + [REG_BATTERY_MODE] = + SBS_DATA(-1, 0x03, 0, 65535), [REG_TEMPERATURE] = SBS_DATA(POWER_SUPPLY_PROP_TEMP, 0x08, 0, 65535), [REG_VOLTAGE] = @@ -366,6 +369,17 @@ static int sbs_status_correct(struct i2c_client *client, int *intval) return 0; } +static bool sbs_bat_needs_calibration(struct i2c_client *client) +{ + int ret; + + ret = sbs_read_word_data(client, sbs_data[REG_BATTERY_MODE].addr); + if (ret < 0) + return false; + + return !!(ret & BIT(7)); +} + static int sbs_get_battery_presence_and_health( struct i2c_client *client, enum power_supply_property psp, union power_supply_propval *val) @@ -385,9 +399,14 @@ static int sbs_get_battery_presence_and_health( if (psp == POWER_SUPPLY_PROP_PRESENT) val->intval = 1; /* battery present */ - else /* POWER_SUPPLY_PROP_HEALTH */ - /* SBS spec doesn't have a general health command. */ - val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; + else { /* POWER_SUPPLY_PROP_HEALTH */ + if (sbs_bat_needs_calibration(client)) { + val->intval = POWER_SUPPLY_HEALTH_CALIBRATION_REQUIRED; + } else { + /* SBS spec doesn't have a general health command. */ + val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; + } + } return 0; } @@ -441,6 +460,8 @@ static int sbs_get_ti_battery_presence_and_health( val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; else if (ret == 0x0C) val->intval = POWER_SUPPLY_HEALTH_DEAD; + else if (sbs_bat_needs_calibration(client)) + val->intval = POWER_SUPPLY_HEALTH_CALIBRATION_REQUIRED; else val->intval = POWER_SUPPLY_HEALTH_GOOD; } -- cgit v1.2.3 From f0318bc99c8107acddde3aa99a7f696c0999f37c Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Wed, 13 May 2020 20:56:10 +0200 Subject: power: supply: sbs-battery: fix idle battery status A battery, that is neither charged, nor discharged is not always Full. If the charger is disabled for other reasons it might simply be idle and should be marked accordingly. Signed-off-by: Sebastian Reichel --- drivers/power/supply/sbs-battery.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index 2a2b926ad75c..e3c685b2c247 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -354,9 +354,9 @@ static int sbs_status_correct(struct i2c_client *client, int *intval) ret = (s16)ret; - /* Not drawing current means full (cannot be not charging) */ - if (ret == 0) - *intval = POWER_SUPPLY_STATUS_FULL; + /* Not drawing current -> not charging (i.e. idle) */ + if (*intval != POWER_SUPPLY_STATUS_FULL && ret == 0) + *intval = POWER_SUPPLY_STATUS_NOT_CHARGING; if (*intval == POWER_SUPPLY_STATUS_FULL) { /* Drawing or providing current when full */ -- cgit v1.2.3 From 182fc88268f3e33fb81cbb373b30d656daf0cb48 Mon Sep 17 00:00:00 2001 From: Jean-Francois Dagenais Date: Wed, 13 May 2020 20:56:11 +0200 Subject: power: supply: sbs-battery: add ability to disable charger broadcasts In certain designs, it is possible to add a battery on a populated i2c bus without an sbs compliant charger. In that case, the battery will unnecessarily and sometimes undesirably master the bus trying to write info in the charger. It is observed in many occasion that these battery "broadcasts" are even corrupting other ongoing master to slave communication. I.e. the multi-master support in the battery is inadequate. Thankfully, the CHARGER_MODE bit allows designers to disable that SBS battery behaviour. This needs to be done once when the battery is first seen on the bus. Signed-off-by: Jean-Francois Dagenais [rebased code] Signed-off-by: Sebastian Reichel --- .../bindings/power/supply/sbs_sbs-battery.txt | 2 ++ drivers/power/supply/sbs-battery.c | 29 ++++++++++++++++++++++ 2 files changed, 31 insertions(+) (limited to 'drivers/power/supply') diff --git a/Documentation/devicetree/bindings/power/supply/sbs_sbs-battery.txt b/Documentation/devicetree/bindings/power/supply/sbs_sbs-battery.txt index fa5a8b516dbf..a5093ccef5c5 100644 --- a/Documentation/devicetree/bindings/power/supply/sbs_sbs-battery.txt +++ b/Documentation/devicetree/bindings/power/supply/sbs_sbs-battery.txt @@ -16,6 +16,7 @@ Optional properties : after an external change notification. - sbs,battery-detect-gpios : The gpio which signals battery detection and a flag specifying its polarity. + - sbs,disable-charger-broadcasts: for systems without sbs compliant chargers Example: @@ -25,4 +26,5 @@ Example: sbs,i2c-retry-count = <2>; sbs,poll-retry-count = <10>; sbs,battery-detect-gpios = <&gpio-controller 122 1>; + sbs,disable-charger-broadcasts; } diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index e3c685b2c247..7c6905a486da 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -68,6 +68,7 @@ enum sbs_capacity_mode { CAPACITY_MODE_AMPS = 0, CAPACITY_MODE_WATTS = BATTERY_MODE_CAPACITY_MASK }; +#define BATTERY_MODE_CHARGER_MASK (1<<14) /* manufacturer access defines */ #define MANUFACTURER_ACCESS_STATUS 0x0006 @@ -193,6 +194,7 @@ struct sbs_info { bool is_present; struct gpio_desc *gpio_detect; bool enable_detection; + bool charger_broadcasts; int last_state; int poll_time; u32 i2c_retry_count; @@ -207,6 +209,27 @@ static char manufacturer[I2C_SMBUS_BLOCK_MAX + 1]; static char chemistry[I2C_SMBUS_BLOCK_MAX + 1]; static bool force_load; +static int sbs_read_word_data(struct i2c_client *client, u8 address); +static int sbs_write_word_data(struct i2c_client *client, u8 address, u16 value); + +static void sbs_disable_charger_broadcasts(struct sbs_info *chip) +{ + int val = sbs_read_word_data(chip->client, BATTERY_MODE_OFFSET); + if (val < 0) + goto exit; + + val |= BATTERY_MODE_CHARGER_MASK; + + val = sbs_write_word_data(chip->client, BATTERY_MODE_OFFSET, val); + +exit: + if (val < 0) + dev_err(&chip->client->dev, + "Failed to disable charger broadcasting: %d\n", val); + else + dev_dbg(&chip->client->dev, "%s\n", __func__); +} + static int sbs_update_presence(struct sbs_info *chip, bool is_present) { struct i2c_client *client = chip->client; @@ -260,6 +283,9 @@ static int sbs_update_presence(struct sbs_info *chip, bool is_present) dev_dbg(&client->dev, "PEC: %s\n", (client->flags & I2C_CLIENT_PEC) ? "enabled" : "disabled"); + if (!chip->is_present && is_present && !chip->charger_broadcasts) + sbs_disable_charger_broadcasts(chip); + chip->is_present = true; return 0; @@ -1017,6 +1043,9 @@ static int sbs_probe(struct i2c_client *client, } chip->i2c_retry_count = chip->i2c_retry_count + 1; + chip->charger_broadcasts = !of_property_read_bool(client->dev.of_node, + "sbs,disable-charger-broadcasts"); + chip->gpio_detect = devm_gpiod_get_optional(&client->dev, "sbs,battery-detect", GPIOD_IN); if (IS_ERR(chip->gpio_detect)) { -- cgit v1.2.3 From 03b758ba36f0656d905f595eea33ab247d5fc945 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Wed, 13 May 2020 20:56:12 +0200 Subject: power: supply: sbs-battery: switch from of_property_* to device_property_* Switch from DT specific of_property_* API to generic and more modern device_property_* API. Signed-off-by: Sebastian Reichel --- drivers/power/supply/sbs-battery.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index 7c6905a486da..73dfe526c867 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include @@ -1016,7 +1016,7 @@ static int sbs_probe(struct i2c_client *client, if (!chip) return -ENOMEM; - chip->flags = (u32)(uintptr_t)of_device_get_match_data(&client->dev); + chip->flags = (u32)(uintptr_t)device_get_match_data(&client->dev); chip->client = client; chip->enable_detection = false; psy_cfg.of_node = client->dev.of_node; @@ -1027,13 +1027,13 @@ static int sbs_probe(struct i2c_client *client, /* use pdata if available, fall back to DT properties, * or hardcoded defaults if not */ - rc = of_property_read_u32(client->dev.of_node, "sbs,i2c-retry-count", - &chip->i2c_retry_count); + rc = device_property_read_u32(&client->dev, "sbs,i2c-retry-count", + &chip->i2c_retry_count); if (rc) chip->i2c_retry_count = 0; - rc = of_property_read_u32(client->dev.of_node, "sbs,poll-retry-count", - &chip->poll_retry_count); + rc = device_property_read_u32(&client->dev, "sbs,poll-retry-count", + &chip->poll_retry_count); if (rc) chip->poll_retry_count = 0; @@ -1043,7 +1043,7 @@ static int sbs_probe(struct i2c_client *client, } chip->i2c_retry_count = chip->i2c_retry_count + 1; - chip->charger_broadcasts = !of_property_read_bool(client->dev.of_node, + chip->charger_broadcasts = !device_property_read_bool(&client->dev, "sbs,disable-charger-broadcasts"); chip->gpio_detect = devm_gpiod_get_optional(&client->dev, -- cgit v1.2.3 From f9ca07a123c81b6b6e1070a515267d89956efc9e Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Wed, 13 May 2020 20:56:13 +0200 Subject: power: supply: sbs-battery: switch to i2c's probe_new sbs-battery does not use the ID parameter, so switch to i2c's probe_new API. Signed-off-by: Sebastian Reichel --- drivers/power/supply/sbs-battery.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index 73dfe526c867..f0392be350eb 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -992,8 +992,7 @@ static const struct power_supply_desc sbs_default_desc = { .external_power_changed = sbs_external_power_changed, }; -static int sbs_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int sbs_probe(struct i2c_client *client) { struct sbs_info *chip; struct power_supply_desc *sbs_desc; @@ -1172,7 +1171,7 @@ static const struct of_device_id sbs_dt_ids[] = { MODULE_DEVICE_TABLE(of, sbs_dt_ids); static struct i2c_driver sbs_battery_driver = { - .probe = sbs_probe, + .probe_new = sbs_probe, .remove = sbs_remove, .alert = sbs_alert, .id_table = sbs_id, -- cgit v1.2.3 From 68956dbe6fb446ae1da60b368116e7e04f385dcb Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Wed, 13 May 2020 20:56:14 +0200 Subject: power: supply: sbs-battery: constify power-supply property array Signed-off-by: Sebastian Reichel --- drivers/power/supply/sbs-battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index f0392be350eb..f4f73e669460 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -151,7 +151,7 @@ static const struct chip_data { SBS_DATA(POWER_SUPPLY_PROP_TECHNOLOGY, 0x22, 0, 65535) }; -static enum power_supply_property sbs_properties[] = { +static const enum power_supply_property sbs_properties[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_HEALTH, -- cgit v1.2.3 From cf1eb321d1cd134da0083f6430301a09f8012de1 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Tue, 2 Jun 2020 16:36:24 +0200 Subject: Revert "power: supply: sbs-battery: add PEC support" This depends on the simplification of sbs_read_string_data, which breaks booting exynos5 based chromebooks. More investigation is required, so this patch and the simplification patch are reverted for this merge window. Note, that this is only a partial revert, since sbs_update_presence() has not been removed. It is also required for the charger broadcast disabling. This reverts commit 79bcd5a4a66076a8a8dacd7f4a3be1952283aef4. Signed-off-by: Sebastian Reichel --- drivers/power/supply/sbs-battery.c | 51 -------------------------------------- 1 file changed, 51 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index f4f73e669460..00dfd9e9ac93 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -51,14 +51,6 @@ enum { REG_CHARGE_VOLTAGE, }; -#define REG_ADDR_SPEC_INFO 0x1A -#define SPEC_INFO_VERSION_MASK GENMASK(7, 4) -#define SPEC_INFO_VERSION_SHIFT 4 - -#define SBS_VERSION_1_0 1 -#define SBS_VERSION_1_1 2 -#define SBS_VERSION_1_1_WITH_PEC 3 - #define REG_ADDR_MANUFACTURE_DATE 0x1B /* Battery Mode defines */ @@ -232,57 +224,14 @@ exit: static int sbs_update_presence(struct sbs_info *chip, bool is_present) { - struct i2c_client *client = chip->client; - int retries = chip->i2c_retry_count; - s32 ret = 0; - u8 version; - if (chip->is_present == is_present) return 0; if (!is_present) { chip->is_present = false; - /* Disable PEC when no device is present */ - client->flags &= ~I2C_CLIENT_PEC; return 0; } - /* Check if device supports packet error checking and use it */ - while (retries > 0) { - ret = i2c_smbus_read_word_data(client, REG_ADDR_SPEC_INFO); - if (ret >= 0) - break; - - /* - * Some batteries trigger the detection pin before the - * I2C bus is properly connected. This works around the - * issue. - */ - msleep(100); - - retries--; - } - - if (ret < 0) { - dev_dbg(&client->dev, "failed to read spec info: %d\n", ret); - - /* fallback to old behaviour */ - client->flags &= ~I2C_CLIENT_PEC; - chip->is_present = true; - - return ret; - } - - version = (ret & SPEC_INFO_VERSION_MASK) >> SPEC_INFO_VERSION_SHIFT; - - if (version == SBS_VERSION_1_1_WITH_PEC) - client->flags |= I2C_CLIENT_PEC; - else - client->flags &= ~I2C_CLIENT_PEC; - - dev_dbg(&client->dev, "PEC: %s\n", (client->flags & I2C_CLIENT_PEC) ? - "enabled" : "disabled"); - if (!chip->is_present && is_present && !chip->charger_broadcasts) sbs_disable_charger_broadcasts(chip); -- cgit v1.2.3 From 972eabb97aab8e5d86c79f28a707e0d1e8a1d1a0 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Tue, 2 Jun 2020 16:49:08 +0200 Subject: Revert "power: supply: sbs-battery: simplify read_read_string_data" The commit is a nice cleanup, but breaks booting on exynos5 based chromebooks. It's seems to come down to exynos5's i2c driver not implementing I2C_FUNC_SMBUS_READ_BLOCK_DATA. It's not yet clear why that breaks boot / massively slows it down when userspace starts, so revert the problematic patch. This reverts commit c4b12a2f3f3de670f6be5e96092a2cab0b877f1a. Signed-off-by: Sebastian Reichel --- drivers/power/supply/sbs-battery.c | 65 +++++++++++++++++++++++++++++++------- 1 file changed, 53 insertions(+), 12 deletions(-) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index 00dfd9e9ac93..83b9924033bd 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -267,32 +267,66 @@ static int sbs_read_string_data(struct i2c_client *client, u8 address, char *values) { struct sbs_info *chip = i2c_get_clientdata(client); - int retries = chip->i2c_retry_count; - s32 ret = 0; + s32 ret = 0, block_length = 0; + int retries_length, retries_block; + u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1]; + retries_length = chip->i2c_retry_count; + retries_block = chip->i2c_retry_count; + + /* Adapter needs to support these two functions */ if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_READ_BLOCK_DATA)) { + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK)){ return -ENODEV; } + /* Get the length of block data */ + while (retries_length > 0) { + ret = i2c_smbus_read_byte_data(client, address); + if (ret >= 0) + break; + retries_length--; + } + + if (ret < 0) { + dev_dbg(&client->dev, + "%s: i2c read at address 0x%x failed\n", + __func__, address); + return ret; + } + + /* block_length does not include NULL terminator */ + block_length = ret; + if (block_length > I2C_SMBUS_BLOCK_MAX) { + dev_err(&client->dev, + "%s: Returned block_length is longer than 0x%x\n", + __func__, I2C_SMBUS_BLOCK_MAX); + return -EINVAL; + } + /* Get the block data */ - while (retries > 0) { - ret = i2c_smbus_read_block_data(client, address, values); + while (retries_block > 0) { + ret = i2c_smbus_read_i2c_block_data( + client, address, + block_length + 1, block_buffer); if (ret >= 0) break; - retries--; + retries_block--; } if (ret < 0) { - dev_dbg(&client->dev, "%s: failed to read block 0x%x: %d\n", - __func__, address, ret); + dev_dbg(&client->dev, + "%s: i2c read at address 0x%x failed\n", + __func__, address); return ret; } - /* add string termination */ - values[ret] = '\0'; + /* block_buffer[0] == block_length */ + memcpy(values, block_buffer + 1, block_length); + values[block_length] = '\0'; - return 0; + return ret; } static int sbs_write_word_data(struct i2c_client *client, u8 address, @@ -514,7 +548,14 @@ static int sbs_get_battery_property(struct i2c_client *client, static int sbs_get_battery_string_property(struct i2c_client *client, int reg_offset, enum power_supply_property psp, char *val) { - return sbs_read_string_data(client, sbs_data[reg_offset].addr, val); + s32 ret; + + ret = sbs_read_string_data(client, sbs_data[reg_offset].addr, val); + + if (ret < 0) + return ret; + + return 0; } static void sbs_unit_adjustment(struct i2c_client *client, -- cgit v1.2.3 From 152204dbdcee6df9406f87c81f9591aeaf1ba55b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sat, 30 May 2020 19:40:30 +0300 Subject: power: supply: cw2015: Attach OF ID table to the driver It appears that OF ID table was hanging around without being attached to the driver. Attach it properly. Fixes: b4c7715c10c1 ("power: supply: add CellWise cw2015 fuel gauge driver") Reported-by: kbuild test robot Signed-off-by: Andy Shevchenko Acked-By: Tobias Schramm Tested-By: Tobias Schramm Signed-off-by: Sebastian Reichel --- drivers/power/supply/cw2015_battery.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/power/supply') diff --git a/drivers/power/supply/cw2015_battery.c b/drivers/power/supply/cw2015_battery.c index 19f62ea957ee..0146f1bfc29b 100644 --- a/drivers/power/supply/cw2015_battery.c +++ b/drivers/power/supply/cw2015_battery.c @@ -734,6 +734,7 @@ MODULE_DEVICE_TABLE(of, cw2015_of_match); static struct i2c_driver cw_bat_driver = { .driver = { .name = "cw2015", + .of_match_table = cw2015_of_match, .pm = &cw_bat_pm_ops, }, .probe_new = cw_bat_probe, -- cgit v1.2.3