diff options
Diffstat (limited to 'drivers/power')
89 files changed, 3313 insertions, 1315 deletions
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index f5fc33a8bf44..60bf0ca64cf3 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -26,7 +26,7 @@ config POWER_RESET_AT91_POWEROFF config POWER_RESET_AT91_RESET tristate "Atmel AT91 reset driver" depends on ARCH_AT91 - default SOC_AT91SAM9 || SOC_SAM9X60 || SOC_SAMA5 + default SOC_AT91SAM9 || SOC_SAM9X60 || SOC_SAM9X7 || SOC_SAMA5 help This driver supports restart for Atmel AT91SAM9 and SAMA5 SoCs @@ -34,7 +34,7 @@ config POWER_RESET_AT91_RESET config POWER_RESET_AT91_SAMA5D2_SHDWC tristate "Atmel AT91 SAMA5D2-Compatible shutdown controller driver" depends on ARCH_AT91 - default SOC_SAM9X60 || SOC_SAMA5 + default SOC_SAM9X60 || SOC_SAM9X7 || SOC_SAMA5 help This driver supports the alternate shutdown controller for some Atmel SAMA5 SoCs. It is present for example on SAMA5D2 SoC. diff --git a/drivers/power/reset/as3722-poweroff.c b/drivers/power/reset/as3722-poweroff.c index bb26fa6fa67c..8075382cbc36 100644 --- a/drivers/power/reset/as3722-poweroff.c +++ b/drivers/power/reset/as3722-poweroff.c @@ -57,8 +57,6 @@ static int as3722_poweroff_probe(struct platform_device *pdev) SYS_OFF_PRIO_DEFAULT, as3722_pm_power_off, as3722_poweroff); - - return 0; } static struct platform_driver as3722_poweroff_driver = { diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c index 036b18a1f90f..511f5a8f8961 100644 --- a/drivers/power/reset/at91-reset.c +++ b/drivers/power/reset/at91-reset.c @@ -129,12 +129,11 @@ static int at91_reset(struct notifier_block *this, unsigned long mode, " str %4, [%0, %6]\n\t" /* Disable SDRAM1 accesses */ "1: tst %1, #0\n\t" - " beq 2f\n\t" " strne %3, [%1, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t" /* Power down SDRAM1 */ " strne %4, [%1, %6]\n\t" /* Reset CPU */ - "2: str %5, [%2, #" __stringify(AT91_RSTC_CR) "]\n\t" + " str %5, [%2, #" __stringify(AT91_RSTC_CR) "]\n\t" " b .\n\t" : @@ -145,7 +144,7 @@ static int at91_reset(struct notifier_block *this, unsigned long mode, "r" cpu_to_le32(AT91_DDRSDRC_LPCB_POWER_DOWN), "r" (reset->data->reset_args), "r" (reset->ramc_lpr) - : "r4"); + ); return NOTIFY_DONE; } diff --git a/drivers/power/reset/at91-sama5d2_shdwc.c b/drivers/power/reset/at91-sama5d2_shdwc.c index edb0df86aff4..e9fe08ee3812 100644 --- a/drivers/power/reset/at91-sama5d2_shdwc.c +++ b/drivers/power/reset/at91-sama5d2_shdwc.c @@ -326,6 +326,8 @@ static const struct of_device_id at91_pmc_ids[] = { { .compatible = "atmel,sama5d2-pmc" }, { .compatible = "microchip,sam9x60-pmc" }, { .compatible = "microchip,sama7g5-pmc" }, + { .compatible = "microchip,sam9x7-pmc" }, + { .compatible = "microchip,sama7d65-pmc" }, { /* Sentinel. */ } }; diff --git a/drivers/power/reset/gpio-poweroff.c b/drivers/power/reset/gpio-poweroff.c index 52cfeee2cb28..3eaae352ffb9 100644 --- a/drivers/power/reset/gpio-poweroff.c +++ b/drivers/power/reset/gpio-poweroff.c @@ -44,7 +44,13 @@ static int gpio_poweroff_do_poweroff(struct sys_off_data *data) /* give it some time */ mdelay(gpio_poweroff->timeout_ms); - WARN_ON(1); + /* + * If code reaches this point, it means that gpio-poweroff has failed + * to actually power off the system. + * Warn the user that the attempt to poweroff via gpio-poweroff + * has gone wrong. + */ + WARN(1, "Failed to poweroff via gpio-poweroff mechanism\n"); return NOTIFY_DONE; } diff --git a/drivers/power/reset/keystone-reset.c b/drivers/power/reset/keystone-reset.c index cfaa54ced0d0..d9268d150e1f 100644 --- a/drivers/power/reset/keystone-reset.c +++ b/drivers/power/reset/keystone-reset.c @@ -87,26 +87,16 @@ static int rsctrl_probe(struct platform_device *pdev) return -ENODEV; /* get regmaps */ - pllctrl_regs = syscon_regmap_lookup_by_phandle(np, "ti,syscon-pll"); + pllctrl_regs = syscon_regmap_lookup_by_phandle_args(np, "ti,syscon-pll", + 1, &rspll_offset); if (IS_ERR(pllctrl_regs)) return PTR_ERR(pllctrl_regs); - devctrl_regs = syscon_regmap_lookup_by_phandle(np, "ti,syscon-dev"); + devctrl_regs = syscon_regmap_lookup_by_phandle_args(np, "ti,syscon-dev", + 1, &rsmux_offset); if (IS_ERR(devctrl_regs)) return PTR_ERR(devctrl_regs); - ret = of_property_read_u32_index(np, "ti,syscon-pll", 1, &rspll_offset); - if (ret) { - dev_err(dev, "couldn't read the reset pll offset!\n"); - return -EINVAL; - } - - ret = of_property_read_u32_index(np, "ti,syscon-dev", 1, &rsmux_offset); - if (ret) { - dev_err(dev, "couldn't read the rsmux offset!\n"); - return -EINVAL; - } - /* set soft/hard reset */ val = of_property_read_bool(np, "ti,soft-reset"); val = val ? RSCFG_RSTYPE_SOFT : RSCFG_RSTYPE_HARD; diff --git a/drivers/power/reset/ltc2952-poweroff.c b/drivers/power/reset/ltc2952-poweroff.c index 1a6fc8d38e20..90c664d344d0 100644 --- a/drivers/power/reset/ltc2952-poweroff.c +++ b/drivers/power/reset/ltc2952-poweroff.c @@ -162,11 +162,11 @@ static void ltc2952_poweroff_default(struct ltc2952_poweroff *data) data->wde_interval = 300L * NSEC_PER_MSEC; data->trigger_delay = ktime_set(2, 500L * NSEC_PER_MSEC); - hrtimer_init(&data->timer_trigger, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - data->timer_trigger.function = ltc2952_poweroff_timer_trigger; + hrtimer_setup(&data->timer_trigger, ltc2952_poweroff_timer_trigger, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); - hrtimer_init(&data->timer_wde, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - data->timer_wde.function = ltc2952_poweroff_timer_wde; + hrtimer_setup(&data->timer_wde, ltc2952_poweroff_timer_wde, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); } static int ltc2952_poweroff_init(struct platform_device *pdev) diff --git a/drivers/power/sequencing/pwrseq-qcom-wcn.c b/drivers/power/sequencing/pwrseq-qcom-wcn.c index 682a9beac69e..e8f5030f2639 100644 --- a/drivers/power/sequencing/pwrseq-qcom-wcn.c +++ b/drivers/power/sequencing/pwrseq-qcom-wcn.c @@ -272,6 +272,24 @@ static const struct pwrseq_qcom_wcn_pdata pwrseq_qca6390_of_data = { .targets = pwrseq_qcom_wcn_targets, }; +static const char *const pwrseq_wcn6750_vregs[] = { + "vddaon", + "vddasd", + "vddpmu", + "vddrfa0p8", + "vddrfa1p2", + "vddrfa1p7", + "vddrfa2p2", +}; + +static const struct pwrseq_qcom_wcn_pdata pwrseq_wcn6750_of_data = { + .vregs = pwrseq_wcn6750_vregs, + .num_vregs = ARRAY_SIZE(pwrseq_wcn6750_vregs), + .pwup_delay_ms = 50, + .gpio_enable_delay_ms = 5, + .targets = pwrseq_qcom_wcn_targets, +}; + static const char *const pwrseq_wcn6855_vregs[] = { "vddio", "vddaon", @@ -378,6 +396,13 @@ static int pwrseq_qcom_wcn_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(ctx->bt_gpio), "Failed to get the Bluetooth enable GPIO\n"); + /* + * FIXME: This should actually be GPIOD_OUT_LOW, but doing so would + * cause the WLAN power to be toggled, resulting in PCIe link down. + * Since the PCIe controller driver is not handling link down currently, + * the device becomes unusable. So we need to keep this workaround until + * the link down handling is implemented in the controller driver. + */ ctx->wlan_gpio = devm_gpiod_get_optional(dev, "wlan-enable", GPIOD_ASIS); if (IS_ERR(ctx->wlan_gpio)) @@ -431,6 +456,10 @@ static const struct of_device_id pwrseq_qcom_wcn_of_match[] = { .compatible = "qcom,wcn7850-pmu", .data = &pwrseq_wcn7850_of_data, }, + { + .compatible = "qcom,wcn6750-pmu", + .data = &pwrseq_wcn6750_of_data, + }, { } }; MODULE_DEVICE_TABLE(of, pwrseq_qcom_wcn_of_match); diff --git a/drivers/power/supply/88pm860x_battery.c b/drivers/power/supply/88pm860x_battery.c index b7938fbb24a5..edae1e843c51 100644 --- a/drivers/power/supply/88pm860x_battery.c +++ b/drivers/power/supply/88pm860x_battery.c @@ -14,6 +14,7 @@ #include <linux/mutex.h> #include <linux/string.h> #include <linux/power_supply.h> +#include <linux/string_choices.h> #include <linux/mfd/88pm860x.h> #include <linux/delay.h> @@ -503,8 +504,7 @@ static void pm860x_init_battery(struct pm860x_battery_info *info) data = pm860x_reg_read(info->i2c, PM8607_POWER_UP_LOG); bat_remove = data & BAT_WU_LOG; - dev_dbg(info->dev, "battery wake up? %s\n", - bat_remove != 0 ? "yes" : "no"); + dev_dbg(info->dev, "battery wake up? %s\n", str_yes_no(bat_remove)); /* restore SOC from RTC domain register */ if (bat_remove == 0) { diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 9f2eef6787f7..8dbd39afa43c 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -449,12 +449,6 @@ config CHARGER_88PM860X help Say Y here to enable charger for Marvell 88PM860x chip. -config CHARGER_PCF50633 - tristate "NXP PCF50633 MBC" - depends on MFD_PCF50633 - help - Say Y to include support for NXP PCF50633 Main Battery Charger. - config BATTERY_RX51 tristate "Nokia RX-51 (N900) battery driver" depends on TWL4030_MADC @@ -583,6 +577,12 @@ config CHARGER_MAX77693 help Say Y to enable support for the Maxim MAX77693 battery charger. +config CHARGER_MAX77705 + tristate "Maxim MAX77705 battery charger driver" + depends on MFD_MAX77705 + help + Say Y to enable support for the Maxim MAX77705 battery charger. + config CHARGER_MAX77976 tristate "Maxim MAX77976 battery charger driver" depends on I2C @@ -918,6 +918,15 @@ config FUEL_GAUGE_SC27XX Say Y here to enable support for fuel gauge with SC27XX PMIC chips. +config FUEL_GAUGE_STC3117 + tristate "STMicroelectronics STC3117 fuel gauge driver" + depends on CRC8 + depends on I2C + select REGMAP_I2C + help + Say Y here to enable support for fuel gauge with STC3117 + chip. + config CHARGER_UCS1002 tristate "Microchip UCS1002 USB Port Power Controller" depends on I2C diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index 59c4a9f40d28..61677be328b0 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -62,7 +62,6 @@ obj-$(CONFIG_CHARGER_RT9467) += rt9467-charger.o obj-$(CONFIG_CHARGER_RT9471) += rt9471.o obj-$(CONFIG_BATTERY_TWL4030_MADC) += twl4030_madc_battery.o obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o -obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o obj-$(CONFIG_BATTERY_RX51) += rx51_battery.o obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o ab8500_chargalg.o obj-$(CONFIG_CHARGER_CPCAP) += cpcap-charger.o @@ -80,6 +79,7 @@ obj-$(CONFIG_CHARGER_MAX14577) += max14577_charger.o obj-$(CONFIG_CHARGER_DETECTOR_MAX14656) += max14656_charger_detector.o obj-$(CONFIG_CHARGER_MAX77650) += max77650-charger.o obj-$(CONFIG_CHARGER_MAX77693) += max77693_charger.o +obj-$(CONFIG_CHARGER_MAX77705) += max77705_charger.o obj-$(CONFIG_CHARGER_MAX77976) += max77976_charger.o obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o @@ -108,6 +108,7 @@ obj-$(CONFIG_CHARGER_CROS_USBPD) += cros_usbpd-charger.o obj-$(CONFIG_CHARGER_CROS_PCHG) += cros_peripheral_charger.o obj-$(CONFIG_CHARGER_SC2731) += sc2731_charger.o obj-$(CONFIG_FUEL_GAUGE_SC27XX) += sc27xx_fuel_gauge.o +obj-$(CONFIG_FUEL_GAUGE_STC3117) += stc3117_fuel_gauge.o obj-$(CONFIG_CHARGER_UCS1002) += ucs1002_power.o obj-$(CONFIG_CHARGER_BD99954) += bd99954-charger.o obj-$(CONFIG_CHARGER_WILCO) += wilco-charger.o diff --git a/drivers/power/supply/ab8500_btemp.c b/drivers/power/supply/ab8500_btemp.c index 37039e28fc4b..b00c84fbc33c 100644 --- a/drivers/power/supply/ab8500_btemp.c +++ b/drivers/power/supply/ab8500_btemp.c @@ -540,10 +540,9 @@ static int ab8500_btemp_get_property(struct power_supply *psy, return 0; } -static int ab8500_btemp_get_ext_psy_data(struct device *dev, void *data) +static int ab8500_btemp_get_ext_psy_data(struct power_supply *ext, void *data) { struct power_supply *psy; - struct power_supply *ext = dev_get_drvdata(dev); const char **supplicants = (const char **)ext->supplied_to; struct ab8500_btemp *di; union power_supply_propval ret; @@ -617,7 +616,7 @@ static int ab8500_btemp_get_ext_psy_data(struct device *dev, void *data) */ static void ab8500_btemp_external_power_changed(struct power_supply *psy) { - power_supply_for_each_device(psy, ab8500_btemp_get_ext_psy_data); + power_supply_for_each_psy(psy, ab8500_btemp_get_ext_psy_data); } /* ab8500 btemp driver interrupts and their respective isr */ diff --git a/drivers/power/supply/ab8500_chargalg.c b/drivers/power/supply/ab8500_chargalg.c index 14e1b448bd39..dc6c8b0dd1cf 100644 --- a/drivers/power/supply/ab8500_chargalg.c +++ b/drivers/power/supply/ab8500_chargalg.c @@ -844,10 +844,9 @@ static void handle_maxim_chg_curr(struct ab8500_chargalg *di) } } -static int ab8500_chargalg_get_ext_psy_data(struct device *dev, void *data) +static int ab8500_chargalg_get_ext_psy_data(struct power_supply *ext, void *data) { struct power_supply *psy; - struct power_supply *ext = dev_get_drvdata(dev); const char **supplicants = (const char **)ext->supplied_to; struct ab8500_chargalg *di; union power_supply_propval ret; @@ -1231,7 +1230,7 @@ static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di) int ret; /* Collect data from all power_supply class devices */ - power_supply_for_each_device(di->chargalg_psy, ab8500_chargalg_get_ext_psy_data); + power_supply_for_each_psy(di->chargalg_psy, ab8500_chargalg_get_ext_psy_data); ab8500_chargalg_end_of_charge(di); ab8500_chargalg_check_temp(di); @@ -1788,13 +1787,12 @@ static int ab8500_chargalg_probe(struct platform_device *pdev) psy_cfg.drv_data = di; /* Initilialize safety timer */ - hrtimer_init(&di->safety_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - di->safety_timer.function = ab8500_chargalg_safety_timer_expired; + hrtimer_setup(&di->safety_timer, ab8500_chargalg_safety_timer_expired, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); /* Initilialize maintenance timer */ - hrtimer_init(&di->maintenance_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - di->maintenance_timer.function = - ab8500_chargalg_maintenance_timer_expired; + hrtimer_setup(&di->maintenance_timer, ab8500_chargalg_maintenance_timer_expired, + CLOCK_MONOTONIC, HRTIMER_MODE_REL); /* Init work for chargalg */ INIT_DEFERRABLE_WORK(&di->chargalg_periodic_work, diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c index cece8d6753ac..5f4537766e5b 100644 --- a/drivers/power/supply/ab8500_charger.c +++ b/drivers/power/supply/ab8500_charger.c @@ -1894,10 +1894,9 @@ static int ab8500_charger_update_charger_current(struct ux500_charger *charger, return ret; } -static int ab8500_charger_get_ext_psy_data(struct device *dev, void *data) +static int ab8500_charger_get_ext_psy_data(struct power_supply *ext, void *data) { struct power_supply *psy; - struct power_supply *ext = dev_get_drvdata(dev); const char **supplicants = (const char **)ext->supplied_to; struct ab8500_charger *di; union power_supply_propval ret; @@ -1961,7 +1960,7 @@ static void ab8500_charger_check_vbat_work(struct work_struct *work) struct ab8500_charger *di = container_of(work, struct ab8500_charger, check_vbat_work.work); - power_supply_for_each_device(&di->usb_chg, ab8500_charger_get_ext_psy_data); + power_supply_for_each_psy(&di->usb_chg, ab8500_charger_get_ext_psy_data); /* First run old_vbat is 0. */ if (di->old_vbat == 0) @@ -3495,11 +3494,11 @@ static int ab8500_charger_probe(struct platform_device *pdev) di->invalid_charger_detect_state = 0; /* AC and USB supply config */ - ac_psy_cfg.of_node = np; + ac_psy_cfg.fwnode = dev_fwnode(dev); ac_psy_cfg.supplied_to = supply_interface; ac_psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface); ac_psy_cfg.drv_data = &di->ac_chg; - usb_psy_cfg.of_node = np; + usb_psy_cfg.fwnode = dev_fwnode(dev); usb_psy_cfg.supplied_to = supply_interface; usb_psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface); usb_psy_cfg.drv_data = &di->usb_chg; diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c index 78871a2143de..9dd99722667a 100644 --- a/drivers/power/supply/ab8500_fg.c +++ b/drivers/power/supply/ab8500_fg.c @@ -2174,10 +2174,9 @@ static int ab8500_fg_get_property(struct power_supply *psy, return 0; } -static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data) +static int ab8500_fg_get_ext_psy_data(struct power_supply *ext, void *data) { struct power_supply *psy; - struct power_supply *ext = dev_get_drvdata(dev); const char **supplicants = (const char **)ext->supplied_to; struct ab8500_fg *di; struct power_supply_battery_info *bi; @@ -2402,7 +2401,7 @@ out: */ static void ab8500_fg_external_power_changed(struct power_supply *psy) { - power_supply_for_each_device(psy, ab8500_fg_get_ext_psy_data); + power_supply_for_each_psy(psy, ab8500_fg_get_ext_psy_data); } /** @@ -2575,7 +2574,7 @@ static ssize_t ab8505_powercut_flagtime_read(struct device *dev, { int ret; u8 reg_value; - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct ab8500_fg *di = power_supply_get_drvdata(psy); ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, @@ -2598,7 +2597,7 @@ static ssize_t ab8505_powercut_flagtime_write(struct device *dev, { int ret; int reg_value; - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct ab8500_fg *di = power_supply_get_drvdata(psy); if (kstrtoint(buf, 10, ®_value)) @@ -2625,7 +2624,7 @@ static ssize_t ab8505_powercut_maxtime_read(struct device *dev, { int ret; u8 reg_value; - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct ab8500_fg *di = power_supply_get_drvdata(psy); ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, @@ -2649,7 +2648,7 @@ static ssize_t ab8505_powercut_maxtime_write(struct device *dev, { int ret; int reg_value; - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct ab8500_fg *di = power_supply_get_drvdata(psy); if (kstrtoint(buf, 10, ®_value)) @@ -2676,7 +2675,7 @@ static ssize_t ab8505_powercut_restart_read(struct device *dev, { int ret; u8 reg_value; - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct ab8500_fg *di = power_supply_get_drvdata(psy); ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, @@ -2699,7 +2698,7 @@ static ssize_t ab8505_powercut_restart_write(struct device *dev, { int ret; int reg_value; - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct ab8500_fg *di = power_supply_get_drvdata(psy); if (kstrtoint(buf, 10, ®_value)) @@ -2727,7 +2726,7 @@ static ssize_t ab8505_powercut_timer_read(struct device *dev, { int ret; u8 reg_value; - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct ab8500_fg *di = power_supply_get_drvdata(psy); ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, @@ -2750,7 +2749,7 @@ static ssize_t ab8505_powercut_restart_counter_read(struct device *dev, { int ret; u8 reg_value; - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct ab8500_fg *di = power_supply_get_drvdata(psy); ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, @@ -2773,7 +2772,7 @@ static ssize_t ab8505_powercut_read(struct device *dev, { int ret; u8 reg_value; - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct ab8500_fg *di = power_supply_get_drvdata(psy); ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, @@ -2794,7 +2793,7 @@ static ssize_t ab8505_powercut_write(struct device *dev, { int ret; int reg_value; - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct ab8500_fg *di = power_supply_get_drvdata(psy); if (kstrtoint(buf, 10, ®_value)) @@ -2822,7 +2821,7 @@ static ssize_t ab8505_powercut_flag_read(struct device *dev, int ret; u8 reg_value; - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct ab8500_fg *di = power_supply_get_drvdata(psy); ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, @@ -2845,7 +2844,7 @@ static ssize_t ab8505_powercut_debounce_read(struct device *dev, { int ret; u8 reg_value; - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct ab8500_fg *di = power_supply_get_drvdata(psy); ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, @@ -2868,7 +2867,7 @@ static ssize_t ab8505_powercut_debounce_write(struct device *dev, { int ret; int reg_value; - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct ab8500_fg *di = power_supply_get_drvdata(psy); if (kstrtoint(buf, 10, ®_value)) @@ -2895,7 +2894,7 @@ static ssize_t ab8505_powercut_enable_status_read(struct device *dev, { int ret; u8 reg_value; - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct ab8500_fg *di = power_supply_get_drvdata(psy); ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, diff --git a/drivers/power/supply/acer_a500_battery.c b/drivers/power/supply/acer_a500_battery.c index 39d85b11a13c..daf01dc8025b 100644 --- a/drivers/power/supply/acer_a500_battery.c +++ b/drivers/power/supply/acer_a500_battery.c @@ -17,6 +17,7 @@ #include <linux/sched.h> #include <linux/slab.h> #include <linux/workqueue.h> +#include <linux/property.h> enum { REG_CAPACITY, @@ -231,7 +232,7 @@ static int a500_battery_probe(struct platform_device *pdev) platform_set_drvdata(pdev, bat); - psy_cfg.of_node = pdev->dev.parent->of_node; + psy_cfg.fwnode = dev_fwnode(pdev->dev.parent); psy_cfg.drv_data = bat; psy_cfg.no_wakeup_source = true; diff --git a/drivers/power/supply/act8945a_charger.c b/drivers/power/supply/act8945a_charger.c index b2b82f97a471..3901a02f326a 100644 --- a/drivers/power/supply/act8945a_charger.c +++ b/drivers/power/supply/act8945a_charger.c @@ -614,7 +614,7 @@ static int act8945a_charger_probe(struct platform_device *pdev) if (ret) return -EINVAL; - psy_cfg.of_node = pdev->dev.of_node; + psy_cfg.fwnode = dev_fwnode(&pdev->dev); psy_cfg.drv_data = charger; charger->psy = devm_power_supply_register(&pdev->dev, diff --git a/drivers/power/supply/apm_power.c b/drivers/power/supply/apm_power.c index 8ef1b6f1f787..9236e0078578 100644 --- a/drivers/power/supply/apm_power.c +++ b/drivers/power/supply/apm_power.c @@ -42,11 +42,11 @@ struct find_bat_param { int max_energy; }; -static int __find_main_battery(struct device *dev, void *data) +static int __find_main_battery(struct power_supply *psy, void *data) { struct find_bat_param *bp = (struct find_bat_param *)data; - bp->bat = dev_get_drvdata(dev); + bp->bat = psy; if (bp->bat->desc->use_for_apm) { /* nice, we explicitly asked to report this battery. */ @@ -79,7 +79,7 @@ static void find_main_battery(void) main_battery = NULL; bp.main = main_battery; - error = power_supply_for_each_device(&bp, __find_main_battery); + error = power_supply_for_each_psy(&bp, __find_main_battery); if (error) { main_battery = bp.main; return; diff --git a/drivers/power/supply/axp20x_ac_power.c b/drivers/power/supply/axp20x_ac_power.c index e5733cb9e19e..5f6ea416fa30 100644 --- a/drivers/power/supply/axp20x_ac_power.c +++ b/drivers/power/supply/axp20x_ac_power.c @@ -364,7 +364,7 @@ static int axp20x_ac_power_probe(struct platform_device *pdev) platform_set_drvdata(pdev, power); - psy_cfg.of_node = pdev->dev.of_node; + psy_cfg.fwnode = dev_fwnode(&pdev->dev); psy_cfg.drv_data = power; power->supply = devm_power_supply_register(&pdev->dev, diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c index 3c3158f31a48..50ca8e110085 100644 --- a/drivers/power/supply/axp20x_battery.c +++ b/drivers/power/supply/axp20x_battery.c @@ -89,6 +89,8 @@ #define AXP717_BAT_CC_MIN_UA 0 #define AXP717_BAT_CC_MAX_UA 3008000 +#define AXP717_TS_PIN_DISABLE BIT(4) + struct axp20x_batt_ps; struct axp_data { @@ -117,6 +119,7 @@ struct axp20x_batt_ps { /* Maximum constant charge current */ unsigned int max_ccc; const struct axp_data *data; + bool ts_disable; }; static int axp20x_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt, @@ -984,6 +987,24 @@ static void axp717_set_battery_info(struct platform_device *pdev, int ccc = info->constant_charge_current_max_ua; int val; + axp_batt->ts_disable = (device_property_read_bool(axp_batt->dev, + "x-powers,no-thermistor")); + + /* + * Under rare conditions an incorrectly programmed efuse for + * the temp sensor on the PMIC may trigger a fault condition. + * Allow users to hard-code if the ts pin is not used to work + * around this problem. Note that this requires the battery + * be correctly defined in the device tree with a monitored + * battery node. + */ + if (axp_batt->ts_disable) { + regmap_update_bits(axp_batt->regmap, + AXP717_TS_PIN_CFG, + AXP717_TS_PIN_DISABLE, + AXP717_TS_PIN_DISABLE); + } + if (vmin > 0 && axp717_set_voltage_min_design(axp_batt, vmin)) dev_err(&pdev->dev, "couldn't set voltage_min_design\n"); @@ -1090,7 +1111,7 @@ static int axp20x_power_probe(struct platform_device *pdev) platform_set_drvdata(pdev, axp20x_batt); psy_cfg.drv_data = axp20x_batt; - psy_cfg.of_node = pdev->dev.of_node; + psy_cfg.fwnode = dev_fwnode(&pdev->dev); axp20x_batt->data = (struct axp_data *)of_device_get_match_data(dev); diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c index 9722912268fe..e75d1e377ac1 100644 --- a/drivers/power/supply/axp20x_usb_power.c +++ b/drivers/power/supply/axp20x_usb_power.c @@ -492,7 +492,7 @@ static int axp717_usb_power_set_input_current_limit(struct axp20x_usb_power *pow if (power->max_input_cur && (intval > power->max_input_cur)) { dev_warn(power->dev, - "reqested current %d clamped to max current %d\n", + "requested current %d clamped to max current %d\n", intval, power->max_input_cur); intval = power->max_input_cur; } @@ -1011,7 +1011,7 @@ static int axp20x_usb_power_probe(struct platform_device *pdev) return ret; } - psy_cfg.of_node = pdev->dev.of_node; + psy_cfg.fwnode = dev_fwnode(&pdev->dev); psy_cfg.drv_data = power; power->supply = devm_power_supply_register(&pdev->dev, diff --git a/drivers/power/supply/bd99954-charger.c b/drivers/power/supply/bd99954-charger.c index 54bf88262510..d03a70cf8406 100644 --- a/drivers/power/supply/bd99954-charger.c +++ b/drivers/power/supply/bd99954-charger.c @@ -156,7 +156,7 @@ static const struct regmap_config bd9995x_regmap_config = { .reg_stride = 1, .max_register = 3 * 0x100, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .ranges = regmap_range_cfg, .num_ranges = ARRAY_SIZE(regmap_range_cfg), @@ -982,7 +982,7 @@ static int bd9995x_probe(struct i2c_client *client) bd->client = client; bd->dev = dev; psy_cfg.drv_data = bd; - psy_cfg.of_node = dev->of_node; + psy_cfg.fwnode = dev_fwnode(dev); mutex_init(&bd->lock); diff --git a/drivers/power/supply/bq2415x_charger.c b/drivers/power/supply/bq2415x_charger.c index 25e28dac900d..9e3b9181ee76 100644 --- a/drivers/power/supply/bq2415x_charger.c +++ b/drivers/power/supply/bq2415x_charger.c @@ -171,6 +171,7 @@ struct bq2415x_device { char *name; int autotimer; /* 1 - if driver automatically reset timer, 0 - not */ int automode; /* 1 - enabled, 0 - disabled; -1 - not supported */ + int charge_status; int id; }; @@ -835,11 +836,13 @@ static int bq2415x_notifier_call(struct notifier_block *nb, if (!bq2415x_update_reported_mode(bq, prop.intval)) return NOTIFY_OK; + power_supply_changed(bq->charger); + /* if automode is not enabled do not tell about reported_mode */ if (bq->automode < 1) return NOTIFY_OK; - schedule_delayed_work(&bq->work, 0); + mod_delayed_work(system_wq, &bq->work, 0); return NOTIFY_OK; } @@ -889,12 +892,19 @@ static void bq2415x_timer_work(struct work_struct *work) int ret; int error; int boost; + int charge; if (bq->automode > 0 && (bq->reported_mode != bq->mode)) { sysfs_notify(&bq->charger->dev.kobj, NULL, "reported_mode"); bq2415x_set_mode(bq, bq->reported_mode); } + charge = bq2415x_exec_command(bq, BQ2415X_CHARGE_STATUS); + if (bq->charge_status != charge) { + power_supply_changed(bq->charger); + bq->charge_status = charge; + } + if (!bq->autotimer) return; @@ -1050,7 +1060,7 @@ static ssize_t bq2415x_sysfs_show_status(struct device *dev, struct device_attribute *attr, char *buf) { - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct bq2415x_device *bq = power_supply_get_drvdata(psy); enum bq2415x_command command; int ret; @@ -1083,7 +1093,7 @@ static ssize_t bq2415x_sysfs_set_timer(struct device *dev, const char *buf, size_t count) { - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct bq2415x_device *bq = power_supply_get_drvdata(psy); int ret = 0; @@ -1104,7 +1114,7 @@ static ssize_t bq2415x_sysfs_show_timer(struct device *dev, struct device_attribute *attr, char *buf) { - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct bq2415x_device *bq = power_supply_get_drvdata(psy); if (bq->timer_error) @@ -1128,7 +1138,7 @@ static ssize_t bq2415x_sysfs_set_mode(struct device *dev, const char *buf, size_t count) { - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct bq2415x_device *bq = power_supply_get_drvdata(psy); enum bq2415x_mode mode; int ret = 0; @@ -1180,7 +1190,7 @@ static ssize_t bq2415x_sysfs_show_mode(struct device *dev, struct device_attribute *attr, char *buf) { - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct bq2415x_device *bq = power_supply_get_drvdata(psy); ssize_t ret = 0; @@ -1217,7 +1227,7 @@ static ssize_t bq2415x_sysfs_show_reported_mode(struct device *dev, struct device_attribute *attr, char *buf) { - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct bq2415x_device *bq = power_supply_get_drvdata(psy); if (bq->automode < 0) @@ -1245,7 +1255,7 @@ static ssize_t bq2415x_sysfs_set_registers(struct device *dev, const char *buf, size_t count) { - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct bq2415x_device *bq = power_supply_get_drvdata(psy); ssize_t ret = 0; unsigned int reg; @@ -1280,7 +1290,7 @@ static ssize_t bq2415x_sysfs_show_registers(struct device *dev, struct device_attribute *attr, char *buf) { - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct bq2415x_device *bq = power_supply_get_drvdata(psy); ssize_t ret = 0; @@ -1298,7 +1308,7 @@ static ssize_t bq2415x_sysfs_set_limit(struct device *dev, const char *buf, size_t count) { - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct bq2415x_device *bq = power_supply_get_drvdata(psy); long val; int ret; @@ -1329,7 +1339,7 @@ static ssize_t bq2415x_sysfs_show_limit(struct device *dev, struct device_attribute *attr, char *buf) { - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct bq2415x_device *bq = power_supply_get_drvdata(psy); int ret; @@ -1357,7 +1367,7 @@ static ssize_t bq2415x_sysfs_set_enable(struct device *dev, const char *buf, size_t count) { - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct bq2415x_device *bq = power_supply_get_drvdata(psy); enum bq2415x_command command; long val; @@ -1392,7 +1402,7 @@ static ssize_t bq2415x_sysfs_show_enable(struct device *dev, struct device_attribute *attr, char *buf) { - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct bq2415x_device *bq = power_supply_get_drvdata(psy); enum bq2415x_command command; int ret; @@ -1487,7 +1497,7 @@ static int bq2415x_power_supply_init(struct bq2415x_device *bq) char revstr[8]; struct power_supply_config psy_cfg = { .drv_data = bq, - .of_node = bq->dev->of_node, + .fwnode = dev_fwnode(bq->dev), .attr_grp = bq2415x_sysfs_groups, }; diff --git a/drivers/power/supply/bq24190_charger.c b/drivers/power/supply/bq24190_charger.c index c47f32f152e6..f0d97ab45bd8 100644 --- a/drivers/power/supply/bq24190_charger.c +++ b/drivers/power/supply/bq24190_charger.c @@ -152,6 +152,7 @@ #define BQ24296_REG_VPRS_PN_MASK (BIT(7) | BIT(6) | BIT(5)) #define BQ24296_REG_VPRS_PN_SHIFT 5 #define BQ24296_REG_VPRS_PN_24296 0x1 +#define BQ24296_REG_VPRS_PN_24297 0x3 #define BQ24190_REG_VPRS_TS_PROFILE_MASK BIT(2) #define BQ24190_REG_VPRS_TS_PROFILE_SHIFT 2 #define BQ24190_REG_VPRS_DEV_REG_MASK (BIT(1) | BIT(0)) @@ -208,6 +209,7 @@ enum bq24190_chip { BQ24192i, BQ24196, BQ24296, + BQ24297, }; /* @@ -422,7 +424,7 @@ static struct bq24190_sysfs_field_info bq24190_sysfs_field_tbl[] = { BQ24190_SYSFS_FIELD_RO(watchdog, CTTC, WATCHDOG), BQ24190_SYSFS_FIELD_RW(en_timer, CTTC, EN_TIMER), BQ24190_SYSFS_FIELD_RW(chg_timer, CTTC, CHG_TIMER), - BQ24190_SYSFS_FIELD_RW(jeta_iset, CTTC, JEITA_ISET), + BQ24190_SYSFS_FIELD_RW(jeita_iset, CTTC, JEITA_ISET), BQ24190_SYSFS_FIELD_RW(bat_comp, ICTRC, BAT_COMP), BQ24190_SYSFS_FIELD_RW(vclamp, ICTRC, VCLAMP), BQ24190_SYSFS_FIELD_RW(treg, ICTRC, TREG), @@ -480,7 +482,7 @@ static struct bq24190_sysfs_field_info *bq24190_sysfs_field_lookup( static ssize_t bq24190_sysfs_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy); struct bq24190_sysfs_field_info *info; ssize_t count; @@ -510,7 +512,7 @@ static ssize_t bq24190_sysfs_show(struct device *dev, static ssize_t bq24190_sysfs_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy); struct bq24190_sysfs_field_info *info; int ret; @@ -1319,6 +1321,7 @@ static int bq24190_charger_get_property(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_CHARGE_TYPE: + case POWER_SUPPLY_PROP_CHARGE_TYPES: ret = bq24190_charger_get_charge_type(bdi, val); break; case POWER_SUPPLY_PROP_HEALTH: @@ -1399,6 +1402,7 @@ static int bq24190_charger_set_property(struct power_supply *psy, ret = bq24190_charger_set_temp_alert_max(bdi, val); break; case POWER_SUPPLY_PROP_CHARGE_TYPE: + case POWER_SUPPLY_PROP_CHARGE_TYPES: ret = bq24190_charger_set_charge_type(bdi, val); break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: @@ -1427,6 +1431,7 @@ static int bq24190_charger_property_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_ONLINE: case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: case POWER_SUPPLY_PROP_CHARGE_TYPE: + case POWER_SUPPLY_PROP_CHARGE_TYPES: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: @@ -1475,6 +1480,7 @@ static void bq24190_charger_external_power_changed(struct power_supply *psy) static enum power_supply_property bq24190_charger_properties[] = { POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_CHARGE_TYPES, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_STATUS, @@ -1504,6 +1510,9 @@ static const struct power_supply_desc bq24190_charger_desc = { .set_property = bq24190_charger_set_property, .property_is_writeable = bq24190_charger_property_is_writeable, .external_power_changed = bq24190_charger_external_power_changed, + .charge_types = BIT(POWER_SUPPLY_CHARGE_TYPE_NONE) | + BIT(POWER_SUPPLY_CHARGE_TYPE_TRICKLE) | + BIT(POWER_SUPPLY_CHARGE_TYPE_FAST), }; /* Battery power supply property routines */ @@ -1897,6 +1906,7 @@ static int bq24296_check_chip(struct bq24190_dev_info *bdi) switch (v) { case BQ24296_REG_VPRS_PN_24296: + case BQ24296_REG_VPRS_PN_24297: break; default: dev_err(bdi->dev, "Error unknown model: 0x%02x\n", v); @@ -2033,6 +2043,17 @@ static const struct bq24190_chip_info bq24190_chip_info_tbl[] = { .get_ntc_status = bq24296_charger_get_ntc_status, .set_otg_vbus = bq24296_set_otg_vbus, }, + [BQ24297] = { + .ichg_array_size = BQ24296_CCC_ICHG_VALUES_LEN, +#ifdef CONFIG_REGULATOR + .vbus_desc = &bq24296_vbus_desc, +#endif + .check_chip = bq24296_check_chip, + .set_chg_config = bq24296_battery_set_chg_config, + .ntc_fault_mask = BQ24296_REG_F_NTC_FAULT_MASK, + .get_ntc_status = bq24296_charger_get_ntc_status, + .set_otg_vbus = bq24296_set_otg_vbus, + }, }; static int bq24190_probe(struct i2c_client *client) @@ -2096,7 +2117,7 @@ static int bq24190_probe(struct i2c_client *client) #endif charger_cfg.drv_data = bdi; - charger_cfg.of_node = dev->of_node; + charger_cfg.fwnode = dev_fwnode(dev); charger_cfg.supplied_to = bq24190_charger_supplied_to; charger_cfg.num_supplicants = ARRAY_SIZE(bq24190_charger_supplied_to); bdi->charger = power_supply_register(dev, &bq24190_charger_desc, @@ -2289,6 +2310,7 @@ static const struct i2c_device_id bq24190_i2c_ids[] = { { "bq24192i", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24192i] }, { "bq24196", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24196] }, { "bq24296", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24296] }, + { "bq24297", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24297] }, { }, }; MODULE_DEVICE_TABLE(i2c, bq24190_i2c_ids); @@ -2299,6 +2321,7 @@ static const struct of_device_id bq24190_of_match[] = { { .compatible = "ti,bq24192i", .data = &bq24190_chip_info_tbl[BQ24192i] }, { .compatible = "ti,bq24196", .data = &bq24190_chip_info_tbl[BQ24196] }, { .compatible = "ti,bq24296", .data = &bq24190_chip_info_tbl[BQ24296] }, + { .compatible = "ti,bq24297", .data = &bq24190_chip_info_tbl[BQ24297] }, { }, }; MODULE_DEVICE_TABLE(of, bq24190_of_match); diff --git a/drivers/power/supply/bq24257_charger.c b/drivers/power/supply/bq24257_charger.c index 801d0d2c5f2e..766eecb35694 100644 --- a/drivers/power/supply/bq24257_charger.c +++ b/drivers/power/supply/bq24257_charger.c @@ -113,7 +113,7 @@ static const struct regmap_config bq24257_regmap_config = { .val_bits = 8, .max_register = BQ24257_REG_7, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = bq24257_is_volatile_reg, }; @@ -759,7 +759,7 @@ static ssize_t bq24257_show_ovp_voltage(struct device *dev, struct device_attribute *attr, char *buf) { - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct bq24257_device *bq = power_supply_get_drvdata(psy); return sysfs_emit(buf, "%u\n", bq24257_vovp_map[bq->init_data.vovp]); @@ -769,7 +769,7 @@ static ssize_t bq24257_show_in_dpm_voltage(struct device *dev, struct device_attribute *attr, char *buf) { - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct bq24257_device *bq = power_supply_get_drvdata(psy); return sysfs_emit(buf, "%u\n", bq24257_vindpm_map[bq->init_data.vindpm]); @@ -779,7 +779,7 @@ static ssize_t bq24257_sysfs_show_enable(struct device *dev, struct device_attribute *attr, char *buf) { - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct bq24257_device *bq = power_supply_get_drvdata(psy); int ret; @@ -801,7 +801,7 @@ static ssize_t bq24257_sysfs_set_enable(struct device *dev, const char *buf, size_t count) { - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct bq24257_device *bq = power_supply_get_drvdata(psy); long val; int ret; diff --git a/drivers/power/supply/bq24735-charger.c b/drivers/power/supply/bq24735-charger.c index 73a7fc867b03..637e0da65f87 100644 --- a/drivers/power/supply/bq24735-charger.c +++ b/drivers/power/supply/bq24735-charger.c @@ -402,7 +402,7 @@ static int bq24735_charger_probe(struct i2c_client *client) psy_cfg.supplied_to = charger->pdata->supplied_to; psy_cfg.num_supplicants = charger->pdata->num_supplicants; - psy_cfg.of_node = client->dev.of_node; + psy_cfg.fwnode = dev_fwnode(&client->dev); psy_cfg.drv_data = charger; i2c_set_clientdata(client, charger); diff --git a/drivers/power/supply/bq2515x_charger.c b/drivers/power/supply/bq2515x_charger.c index a3424f67f2b1..437bff5bc420 100644 --- a/drivers/power/supply/bq2515x_charger.c +++ b/drivers/power/supply/bq2515x_charger.c @@ -1060,7 +1060,7 @@ static const struct regmap_config bq25150_regmap_config = { .max_register = BQ2515X_DEVICE_ID, .reg_defaults = bq25150_reg_defaults, .num_reg_defaults = ARRAY_SIZE(bq25150_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = bq2515x_volatile_register, }; @@ -1071,7 +1071,7 @@ static const struct regmap_config bq25155_regmap_config = { .max_register = BQ2515X_DEVICE_ID, .reg_defaults = bq25155_reg_defaults, .num_reg_defaults = ARRAY_SIZE(bq25155_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = bq2515x_volatile_register, }; @@ -1102,7 +1102,7 @@ static int bq2515x_probe(struct i2c_client *client) i2c_set_clientdata(client, bq2515x); charger_cfg.drv_data = bq2515x; - charger_cfg.of_node = dev->of_node; + charger_cfg.fwnode = dev_fwnode(dev); ret = bq2515x_read_properties(bq2515x); if (ret) { diff --git a/drivers/power/supply/bq256xx_charger.c b/drivers/power/supply/bq256xx_charger.c index 5514d1896bb8..9f9b6019f8e1 100644 --- a/drivers/power/supply/bq256xx_charger.c +++ b/drivers/power/supply/bq256xx_charger.c @@ -1657,7 +1657,7 @@ static int bq256xx_parse_dt(struct bq256xx_device *bq, int ret = 0; psy_cfg->drv_data = bq; - psy_cfg->of_node = dev->of_node; + psy_cfg->fwnode = dev_fwnode(dev); ret = device_property_read_u32(bq->dev, "ti,watchdog-timeout-ms", &bq->watchdog_timer); diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index 2f5ceaf00b94..868e86e1749b 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -164,7 +164,7 @@ static const struct regmap_config bq25890_regmap_config = { .val_bits = 8, .max_register = 0x14, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .wr_table = &bq25890_writeable_regs, .volatile_table = &bq25890_volatile_regs, diff --git a/drivers/power/supply/bq25980_charger.c b/drivers/power/supply/bq25980_charger.c index 0c5e2938bb36..4ff76e3dddf6 100644 --- a/drivers/power/supply/bq25980_charger.c +++ b/drivers/power/supply/bq25980_charger.c @@ -932,7 +932,7 @@ static const struct regmap_config bq25980_regmap_config = { .max_register = BQ25980_CHRGR_CTRL_6, .reg_defaults = bq25980_reg_defs, .num_reg_defaults = ARRAY_SIZE(bq25980_reg_defs), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = bq25980_is_volatile_reg, }; @@ -943,7 +943,7 @@ static const struct regmap_config bq25975_regmap_config = { .max_register = BQ25980_CHRGR_CTRL_6, .reg_defaults = bq25975_reg_defs, .num_reg_defaults = ARRAY_SIZE(bq25975_reg_defs), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = bq25980_is_volatile_reg, }; @@ -954,7 +954,7 @@ static const struct regmap_config bq25960_regmap_config = { .max_register = BQ25980_CHRGR_CTRL_6, .reg_defaults = bq25960_reg_defs, .num_reg_defaults = ARRAY_SIZE(bq25960_reg_defs), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = bq25980_is_volatile_reg, }; @@ -1057,7 +1057,7 @@ static int bq25980_power_supply_init(struct bq25980_device *bq, struct device *dev) { struct power_supply_config psy_cfg = { .drv_data = bq, - .of_node = dev->of_node, }; + .fwnode = dev_fwnode(dev), }; psy_cfg.supplied_to = bq25980_charger_supplied_to; psy_cfg.num_supplicants = ARRAY_SIZE(bq25980_charger_supplied_to); diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c index 40c5ac7a1118..93dcebbe1141 100644 --- a/drivers/power/supply/bq27xxx_battery.c +++ b/drivers/power/supply/bq27xxx_battery.c @@ -123,6 +123,8 @@ enum bq27xxx_reg_index { BQ27XXX_DM_BLOCK, /* Data Block */ BQ27XXX_DM_DATA, /* Block Data */ BQ27XXX_DM_CKSUM, /* Block Data Checksum */ + BQ27XXX_REG_SEDVF, /* End-of-discharge Voltage */ + BQ27XXX_REG_PKCFG, /* Pack Configuration */ BQ27XXX_REG_MAX, /* sentinel */ }; @@ -159,6 +161,8 @@ static u8 [BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR, [BQ27XXX_DM_DATA] = INVALID_REG_ADDR, [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR, + [BQ27XXX_REG_SEDVF] = 0x77, + [BQ27XXX_REG_PKCFG] = 0x7C, }, bq27010_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, @@ -184,6 +188,8 @@ static u8 [BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR, [BQ27XXX_DM_DATA] = INVALID_REG_ADDR, [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR, + [BQ27XXX_REG_SEDVF] = 0x77, + [BQ27XXX_REG_PKCFG] = 0x7C, }, bq2750x_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, @@ -579,6 +585,8 @@ static enum power_supply_property bq27000_props[] = { POWER_SUPPLY_PROP_POWER_AVG, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, }; static enum power_supply_property bq27010_props[] = { @@ -599,6 +607,8 @@ static enum power_supply_property bq27010_props[] = { POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, }; #define bq2750x_props bq27510g3_props @@ -1913,7 +1923,6 @@ static void bq27xxx_battery_update_unlocked(struct bq27xxx_device_info *di) cache.flags = -1; /* read error */ if (cache.flags >= 0) { cache.capacity = bq27xxx_battery_read_soc(di); - di->cache.flags = cache.flags; /* * On gauges with signed current reporting the current must be @@ -2039,6 +2048,65 @@ static int bq27xxx_battery_voltage(struct bq27xxx_device_info *di, return 0; } +/* + * Return the design maximum battery Voltage in microvolts, or < 0 if something + * fails. The programmed value of the maximum battery voltage is determined by + * QV0 and QV1 (bits 5 and 6) in the Pack Configuration register. + */ +static int bq27xxx_battery_read_dmax_volt(struct bq27xxx_device_info *di, + union power_supply_propval *val) +{ + int reg_val, qv; + + if (di->voltage_max_design > 0) { + val->intval = di->voltage_max_design; + return 0; + } + + reg_val = bq27xxx_read(di, BQ27XXX_REG_PKCFG, true); + if (reg_val < 0) { + dev_err(di->dev, "error reading design max voltage\n"); + return reg_val; + } + + qv = (reg_val >> 5) & 0x3; + val->intval = 3968000 + 48000 * qv; + + di->voltage_max_design = val->intval; + + return 0; +} + +/* + * Return the design minimum battery Voltage in microvolts + * Or < 0 if something fails. + */ +static int bq27xxx_battery_read_dmin_volt(struct bq27xxx_device_info *di, + union power_supply_propval *val) +{ + int volt; + + /* We only have to read design minimum voltage once */ + if (di->voltage_min_design > 0) { + val->intval = di->voltage_min_design; + return 0; + } + + volt = bq27xxx_read(di, BQ27XXX_REG_SEDVF, true); + if (volt < 0) { + dev_err(di->dev, "error reading design min voltage\n"); + return volt; + } + + /* SEDVF = Design EDVF / 8 - 256 */ + val->intval = volt * 8000 + 2048000; + + /* Save for later reads */ + di->voltage_min_design = val->intval; + + return 0; +} + static int bq27xxx_simple_value(int value, union power_supply_propval *val) { @@ -2063,7 +2131,7 @@ static int bq27xxx_battery_get_property(struct power_supply *psy, mutex_unlock(&di->lock); if (psp != POWER_SUPPLY_PROP_PRESENT && di->cache.flags < 0) - return -ENODEV; + return di->cache.flags; switch (psp) { case POWER_SUPPLY_PROP_STATUS: @@ -2119,8 +2187,13 @@ static int bq27xxx_battery_get_property(struct power_supply *psy, * power_supply_battery_info visible in sysfs. */ case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: - case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: return -EINVAL; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + ret = bq27xxx_battery_read_dmin_volt(di, val); + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + ret = bq27xxx_battery_read_dmax_volt(di, val); + break; case POWER_SUPPLY_PROP_CYCLE_COUNT: ret = bq27xxx_battery_read_cyct(di, val); break; @@ -2162,7 +2235,7 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di) { struct power_supply_desc *psy_desc; struct power_supply_config psy_cfg = { - .of_node = di->dev->of_node, + .fwnode = dev_fwnode(di->dev), .drv_data = di, .no_wakeup_source = true, }; diff --git a/drivers/power/supply/bq27xxx_battery_i2c.c b/drivers/power/supply/bq27xxx_battery_i2c.c index ba0d22d90429..868e95f0887e 100644 --- a/drivers/power/supply/bq27xxx_battery_i2c.c +++ b/drivers/power/supply/bq27xxx_battery_i2c.c @@ -6,6 +6,7 @@ * Andrew F. Davis <afd@ti.com> */ +#include <linux/delay.h> #include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/module.h> @@ -31,6 +32,7 @@ static int bq27xxx_battery_i2c_read(struct bq27xxx_device_info *di, u8 reg, struct i2c_msg msg[2]; u8 data[2]; int ret; + int retry = 0; if (!client->adapter) return -ENODEV; @@ -47,7 +49,16 @@ static int bq27xxx_battery_i2c_read(struct bq27xxx_device_info *di, u8 reg, else msg[1].len = 2; - ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + do { + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + if (ret == -EBUSY && ++retry < 3) { + /* sleep 10 milliseconds when busy */ + usleep_range(10000, 11000); + continue; + } + break; + } while (1); + if (ret < 0) return ret; diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index a69faef444c0..c49e0e4d02f7 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -22,6 +22,7 @@ #include <linux/platform_device.h> #include <linux/power/charger-manager.h> #include <linux/regulator/consumer.h> +#include <linux/string_choices.h> #include <linux/sysfs.h> #include <linux/of.h> #include <linux/thermal.h> @@ -1088,7 +1089,7 @@ static ssize_t charger_state_show(struct device *dev, if (!charger->externally_control) state = regulator_is_enabled(charger->consumer); - return sysfs_emit(buf, "%s\n", state ? "enabled" : "disabled"); + return sysfs_emit(buf, "%s\n", str_enabled_disabled(state)); } static ssize_t charger_externally_control_show(struct device *dev, diff --git a/drivers/power/supply/collie_battery.c b/drivers/power/supply/collie_battery.c index 68390bd1004f..3daf7befc0bf 100644 --- a/drivers/power/supply/collie_battery.c +++ b/drivers/power/supply/collie_battery.c @@ -440,6 +440,7 @@ err_put_gpio_full: static void collie_bat_remove(struct ucb1x00_dev *dev) { + device_init_wakeup(&ucb->dev, 0); free_irq(gpiod_to_irq(collie_bat_main.gpio_full), &collie_bat_main); power_supply_unregister(collie_bat_bu.psy); power_supply_unregister(collie_bat_main.psy); diff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 813037c00ded..8106d1edcbc2 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -1130,7 +1130,7 @@ static int cpcap_battery_probe(struct platform_device *pdev) if (error) return error; - psy_cfg.of_node = pdev->dev.of_node; + psy_cfg.fwnode = dev_fwnode(&pdev->dev); psy_cfg.drv_data = ddata; ddata->psy = devm_power_supply_register(ddata->dev, diff --git a/drivers/power/supply/cpcap-charger.c b/drivers/power/supply/cpcap-charger.c index 7781b45a67a7..13300dc60baf 100644 --- a/drivers/power/supply/cpcap-charger.c +++ b/drivers/power/supply/cpcap-charger.c @@ -14,6 +14,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/err.h> #include <linux/interrupt.h> #include <linux/notifier.h> @@ -515,7 +516,7 @@ static void cpcap_charger_vbus_work(struct work_struct *work) out_err: cpcap_charger_update_state(ddata, POWER_SUPPLY_STATUS_UNKNOWN); dev_err(ddata->dev, "%s could not %s vbus: %i\n", __func__, - ddata->vbus_enabled ? "enable" : "disable", error); + str_enable_disable(ddata->vbus_enabled), error); } static int cpcap_charger_set_vbus(struct phy_companion *comparator, @@ -901,7 +902,7 @@ static int cpcap_charger_probe(struct platform_device *pdev) atomic_set(&ddata->active, 1); - psy_cfg.of_node = pdev->dev.of_node; + psy_cfg.fwnode = dev_fwnode(&pdev->dev); psy_cfg.drv_data = ddata; psy_cfg.supplied_to = cpcap_charger_supplied_to; psy_cfg.num_supplicants = ARRAY_SIZE(cpcap_charger_supplied_to); diff --git a/drivers/power/supply/cros_charge-control.c b/drivers/power/supply/cros_charge-control.c index 9b0a7500296b..02d5bdbe2e8d 100644 --- a/drivers/power/supply/cros_charge-control.c +++ b/drivers/power/supply/cros_charge-control.c @@ -20,13 +20,6 @@ BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE) | \ BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE)) -enum CROS_CHCTL_ATTR { - CROS_CHCTL_ATTR_START_THRESHOLD, - CROS_CHCTL_ATTR_END_THRESHOLD, - CROS_CHCTL_ATTR_CHARGE_BEHAVIOUR, - _CROS_CHCTL_ATTR_COUNT -}; - /* * Semantics of data *returned* from the EC API and Linux sysfs differ * slightly, also the v1 API can not return any data. @@ -38,18 +31,13 @@ enum CROS_CHCTL_ATTR { */ struct cros_chctl_priv { + struct device *dev; struct cros_ec_device *cros_ec; struct acpi_battery_hook battery_hook; struct power_supply *hooked_battery; u8 cmd_version; - /* The callbacks need to access this priv structure. - * As neither the struct device nor power_supply are under the drivers - * control, embed the attributes within priv to use with container_of(). - */ - struct device_attribute device_attrs[_CROS_CHCTL_ATTR_COUNT]; - struct attribute *attributes[_CROS_CHCTL_ATTR_COUNT]; - struct attribute_group group; + const struct power_supply_ext *psy_ext; struct mutex lock; /* protects fields below and cros_ec */ enum power_supply_charge_behaviour current_behaviour; @@ -119,26 +107,39 @@ static int cros_chctl_configure_ec(struct cros_chctl_priv *priv) return cros_chctl_send_charge_control_cmd(priv->cros_ec, priv->cmd_version, &req); } -static struct cros_chctl_priv *cros_chctl_attr_to_priv(struct attribute *attr, - enum CROS_CHCTL_ATTR idx) +static int cros_chctl_psy_ext_get_prop(struct power_supply *psy, + const struct power_supply_ext *ext, + void *data, + enum power_supply_property psp, + union power_supply_propval *val) { - struct device_attribute *dev_attr = container_of(attr, struct device_attribute, attr); + struct cros_chctl_priv *priv = data; - return container_of(dev_attr, struct cros_chctl_priv, device_attrs[idx]); + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD: + val->intval = priv->current_start_threshold; + return 0; + case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD: + val->intval = priv->current_end_threshold; + return 0; + case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: + val->intval = priv->current_behaviour; + return 0; + default: + return -EINVAL; + } } -static ssize_t cros_chctl_store_threshold(struct device *dev, struct cros_chctl_priv *priv, - int is_end_threshold, const char *buf, size_t count) +static int cros_chctl_psy_ext_set_threshold(struct cros_chctl_priv *priv, + enum power_supply_property psp, + int val) { - int ret, val; + int ret; - ret = kstrtoint(buf, 10, &val); - if (ret < 0) - return ret; if (val < 0 || val > 100) return -EINVAL; - if (is_end_threshold) { + if (psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD) { /* Start threshold is not exposed, use fixed value */ if (priv->cmd_version == 2) priv->current_start_threshold = val == 100 ? 0 : val; @@ -158,93 +159,73 @@ static ssize_t cros_chctl_store_threshold(struct device *dev, struct cros_chctl_ return ret; } - return count; -} - -static ssize_t charge_control_start_threshold_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(&attr->attr, - CROS_CHCTL_ATTR_START_THRESHOLD); - - guard(mutex)(&priv->lock); - return sysfs_emit(buf, "%u\n", (unsigned int)priv->current_start_threshold); + return 0; } -static ssize_t charge_control_start_threshold_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(&attr->attr, - CROS_CHCTL_ATTR_START_THRESHOLD); - - guard(mutex)(&priv->lock); - return cros_chctl_store_threshold(dev, priv, 0, buf, count); -} -static ssize_t charge_control_end_threshold_show(struct device *dev, struct device_attribute *attr, - char *buf) +static int cros_chctl_psy_ext_set_prop(struct power_supply *psy, + const struct power_supply_ext *ext, + void *data, + enum power_supply_property psp, + const union power_supply_propval *val) { - struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(&attr->attr, - CROS_CHCTL_ATTR_END_THRESHOLD); + struct cros_chctl_priv *priv = data; + int ret; guard(mutex)(&priv->lock); - return sysfs_emit(buf, "%u\n", (unsigned int)priv->current_end_threshold); -} - -static ssize_t charge_control_end_threshold_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(&attr->attr, - CROS_CHCTL_ATTR_END_THRESHOLD); - guard(mutex)(&priv->lock); - return cros_chctl_store_threshold(dev, priv, 1, buf, count); + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD: + case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD: + return cros_chctl_psy_ext_set_threshold(priv, psp, val->intval); + case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: + priv->current_behaviour = val->intval; + ret = cros_chctl_configure_ec(priv); + if (ret < 0) + return ret; + return 0; + default: + return -EINVAL; + } } -static ssize_t charge_behaviour_show(struct device *dev, struct device_attribute *attr, char *buf) +static int cros_chctl_psy_prop_is_writeable(struct power_supply *psy, + const struct power_supply_ext *ext, + void *data, + enum power_supply_property psp) { - struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(&attr->attr, - CROS_CHCTL_ATTR_CHARGE_BEHAVIOUR); - - guard(mutex)(&priv->lock); - return power_supply_charge_behaviour_show(dev, EC_CHARGE_CONTROL_BEHAVIOURS, - priv->current_behaviour, buf); + return true; } -static ssize_t charge_behaviour_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(&attr->attr, - CROS_CHCTL_ATTR_CHARGE_BEHAVIOUR); - int ret; - - ret = power_supply_charge_behaviour_parse(EC_CHARGE_CONTROL_BEHAVIOURS, buf); - if (ret < 0) - return ret; - - guard(mutex)(&priv->lock); - priv->current_behaviour = ret; +#define DEFINE_CROS_CHCTL_POWER_SUPPLY_EXTENSION(_name, ...) \ + static const enum power_supply_property _name ## _props[] = { \ + __VA_ARGS__, \ + }; \ + \ + static const struct power_supply_ext _name = { \ + .name = "cros-charge-control", \ + .properties = _name ## _props, \ + .num_properties = ARRAY_SIZE(_name ## _props), \ + .charge_behaviours = EC_CHARGE_CONTROL_BEHAVIOURS, \ + .get_property = cros_chctl_psy_ext_get_prop, \ + .set_property = cros_chctl_psy_ext_set_prop, \ + .property_is_writeable = cros_chctl_psy_prop_is_writeable, \ + } - ret = cros_chctl_configure_ec(priv); - if (ret < 0) - return ret; +DEFINE_CROS_CHCTL_POWER_SUPPLY_EXTENSION(cros_chctl_psy_ext_v1, + POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR +); - return count; -} +DEFINE_CROS_CHCTL_POWER_SUPPLY_EXTENSION(cros_chctl_psy_ext_v2, + POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR, + POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD +); -static umode_t cros_chtl_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) -{ - struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(attr, n); - - if (n == CROS_CHCTL_ATTR_START_THRESHOLD && priv->cmd_version < 3) - return 0; - else if (n == CROS_CHCTL_ATTR_END_THRESHOLD && priv->cmd_version < 2) - return 0; - - return attr->mode; -} +DEFINE_CROS_CHCTL_POWER_SUPPLY_EXTENSION(cros_chctl_psy_ext_v3, + POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR, + POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD, + POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD +); static int cros_chctl_add_battery(struct power_supply *battery, struct acpi_battery_hook *hook) { @@ -254,7 +235,7 @@ static int cros_chctl_add_battery(struct power_supply *battery, struct acpi_batt return 0; priv->hooked_battery = battery; - return device_add_group(&battery->dev, &priv->group); + return power_supply_register_extension(battery, priv->psy_ext, priv->dev, priv); } static int cros_chctl_remove_battery(struct power_supply *battery, struct acpi_battery_hook *hook) @@ -262,7 +243,7 @@ static int cros_chctl_remove_battery(struct power_supply *battery, struct acpi_b struct cros_chctl_priv *priv = container_of(hook, struct cros_chctl_priv, battery_hook); if (priv->hooked_battery == battery) { - device_remove_group(&battery->dev, &priv->group); + power_supply_unregister_extension(battery, priv->psy_ext); priv->hooked_battery = NULL; } @@ -288,7 +269,6 @@ static int cros_chctl_probe(struct platform_device *pdev) struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent); struct cros_ec_device *cros_ec = ec_dev->ec_dev; struct cros_chctl_priv *priv; - size_t i; int ret; ret = cros_chctl_fwk_charge_control_versions(cros_ec); @@ -321,19 +301,15 @@ static int cros_chctl_probe(struct platform_device *pdev) dev_dbg(dev, "Command version: %u\n", (unsigned int)priv->cmd_version); + priv->dev = dev; priv->cros_ec = cros_ec; - priv->device_attrs[CROS_CHCTL_ATTR_START_THRESHOLD] = - (struct device_attribute)__ATTR_RW(charge_control_start_threshold); - priv->device_attrs[CROS_CHCTL_ATTR_END_THRESHOLD] = - (struct device_attribute)__ATTR_RW(charge_control_end_threshold); - priv->device_attrs[CROS_CHCTL_ATTR_CHARGE_BEHAVIOUR] = - (struct device_attribute)__ATTR_RW(charge_behaviour); - for (i = 0; i < _CROS_CHCTL_ATTR_COUNT; i++) { - sysfs_attr_init(&priv->device_attrs[i].attr); - priv->attributes[i] = &priv->device_attrs[i].attr; - } - priv->group.is_visible = cros_chtl_attr_is_visible; - priv->group.attrs = priv->attributes; + + if (priv->cmd_version == 1) + priv->psy_ext = &cros_chctl_psy_ext_v1; + else if (priv->cmd_version == 2) + priv->psy_ext = &cros_chctl_psy_ext_v2; + else + priv->psy_ext = &cros_chctl_psy_ext_v3; priv->battery_hook.name = dev_name(dev); priv->battery_hook.add_battery = cros_chctl_add_battery; diff --git a/drivers/power/supply/da9030_battery.c b/drivers/power/supply/da9030_battery.c index 34328f5d556e..d25279c26030 100644 --- a/drivers/power/supply/da9030_battery.c +++ b/drivers/power/supply/da9030_battery.c @@ -15,6 +15,7 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/power_supply.h> +#include <linux/string_choices.h> #include <linux/mfd/da903x.h> #include <linux/debugfs.h> @@ -138,7 +139,7 @@ static int bat_debug_show(struct seq_file *s, void *data) { struct da9030_charger *charger = s->private; - seq_printf(s, "charger is %s\n", charger->is_on ? "on" : "off"); + seq_printf(s, "charger is %s\n", str_on_off(charger->is_on)); if (charger->chdet) { seq_printf(s, "iset = %dmA, vset = %dmV\n", charger->mA, charger->mV); @@ -501,8 +502,7 @@ static int da9030_battery_probe(struct platform_device *pdev) /* 10 seconds between monitor runs unless platform defines other interval */ - charger->interval = msecs_to_jiffies( - (pdata->batmon_interval ? : 10) * 1000); + charger->interval = secs_to_jiffies(pdata->batmon_interval ? : 10); charger->charge_milliamp = pdata->charge_milliamp; charger->charge_millivolt = pdata->charge_millivolt; diff --git a/drivers/power/supply/ds2760_battery.c b/drivers/power/supply/ds2760_battery.c index 7cf4ea06b500..5badf58c6edb 100644 --- a/drivers/power/supply/ds2760_battery.c +++ b/drivers/power/supply/ds2760_battery.c @@ -112,7 +112,6 @@ struct ds2760_device_info { struct power_supply_desc bat_desc; struct workqueue_struct *monitor_wqueue; struct delayed_work monitor_work; - struct delayed_work set_charged_work; struct notifier_block pm_notifier; }; @@ -195,22 +194,22 @@ static int w1_ds2760_recall_eeprom(struct device *dev, int addr) } static ssize_t w1_slave_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); return w1_ds2760_read(dev, buf, off, count); } -static BIN_ATTR_RO(w1_slave, DS2760_DATA_SIZE); +static const BIN_ATTR_RO(w1_slave, DS2760_DATA_SIZE); -static struct bin_attribute *w1_ds2760_bin_attrs[] = { +static const struct bin_attribute *const w1_ds2760_bin_attrs[] = { &bin_attr_w1_slave, NULL, }; static const struct attribute_group w1_ds2760_group = { - .bin_attrs = w1_ds2760_bin_attrs, + .bin_attrs_new = w1_ds2760_bin_attrs, }; static const struct attribute_group *w1_ds2760_groups[] = { @@ -489,50 +488,6 @@ static void ds2760_battery_external_power_changed(struct power_supply *psy) } -static void ds2760_battery_set_charged_work(struct work_struct *work) -{ - char bias; - struct ds2760_device_info *di = container_of(work, - struct ds2760_device_info, set_charged_work.work); - - dev_dbg(di->dev, "%s\n", __func__); - - ds2760_battery_read_status(di); - - /* When we get notified by external circuitry that the battery is - * considered fully charged now, we know that there is no current - * flow any more. However, the ds2760's internal current meter is - * too inaccurate to rely on - spec say something ~15% failure. - * Hence, we use the current offset bias register to compensate - * that error. - */ - - if (!power_supply_am_i_supplied(di->bat)) - return; - - bias = (signed char) di->current_raw + - (signed char) di->raw[DS2760_CURRENT_OFFSET_BIAS]; - - dev_dbg(di->dev, "%s: bias = %d\n", __func__, bias); - - w1_ds2760_write(di->dev, &bias, DS2760_CURRENT_OFFSET_BIAS, 1); - w1_ds2760_store_eeprom(di->dev, DS2760_EEPROM_BLOCK1); - w1_ds2760_recall_eeprom(di->dev, DS2760_EEPROM_BLOCK1); - - /* Write to the di->raw[] buffer directly - the CURRENT_OFFSET_BIAS - * value won't be read back by ds2760_battery_read_status() */ - di->raw[DS2760_CURRENT_OFFSET_BIAS] = bias; -} - -static void ds2760_battery_set_charged(struct power_supply *psy) -{ - struct ds2760_device_info *di = power_supply_get_drvdata(psy); - - /* postpone the actual work by 20 secs. This is for debouncing GPIO - * signals and to let the current value settle. See AN4188. */ - mod_delayed_work(di->monitor_wqueue, &di->set_charged_work, HZ * 20); -} - static int ds2760_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -692,17 +647,15 @@ static int w1_ds2760_add_slave(struct w1_slave *sl) di->bat_desc.set_property = ds2760_battery_set_property; di->bat_desc.property_is_writeable = ds2760_battery_property_is_writeable; - di->bat_desc.set_charged = ds2760_battery_set_charged; di->bat_desc.external_power_changed = ds2760_battery_external_power_changed; psy_cfg.drv_data = di; + psy_cfg.fwnode = dev_fwnode(dev); if (dev->of_node) { u32 tmp; - psy_cfg.of_node = dev->of_node; - if (!of_property_read_bool(dev->of_node, "maxim,pmod-enabled")) pmod_enabled = true; @@ -747,8 +700,6 @@ static int w1_ds2760_add_slave(struct w1_slave *sl) } INIT_DELAYED_WORK(&di->monitor_work, ds2760_battery_work); - INIT_DELAYED_WORK(&di->set_charged_work, - ds2760_battery_set_charged_work); di->monitor_wqueue = alloc_ordered_workqueue(name, WQ_MEM_RECLAIM); if (!di->monitor_wqueue) { retval = -ESRCH; @@ -774,7 +725,6 @@ static void w1_ds2760_remove_slave(struct w1_slave *sl) unregister_pm_notifier(&di->pm_notifier); cancel_delayed_work_sync(&di->monitor_work); - cancel_delayed_work_sync(&di->set_charged_work); destroy_workqueue(di->monitor_wqueue); } diff --git a/drivers/power/supply/ds2780_battery.c b/drivers/power/supply/ds2780_battery.c index 1e7f297f6cb1..dd9ac7a32967 100644 --- a/drivers/power/supply/ds2780_battery.c +++ b/drivers/power/supply/ds2780_battery.c @@ -621,7 +621,7 @@ static ssize_t ds2780_set_pio_pin(struct device *dev, static ssize_t ds2780_read_param_eeprom_bin(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); @@ -634,7 +634,7 @@ static ssize_t ds2780_read_param_eeprom_bin(struct file *filp, static ssize_t ds2780_write_param_eeprom_bin(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); @@ -654,19 +654,19 @@ static ssize_t ds2780_write_param_eeprom_bin(struct file *filp, return count; } -static struct bin_attribute ds2780_param_eeprom_bin_attr = { +static const struct bin_attribute ds2780_param_eeprom_bin_attr = { .attr = { .name = "param_eeprom", .mode = S_IRUGO | S_IWUSR, }, .size = DS2780_PARAM_EEPROM_SIZE, - .read = ds2780_read_param_eeprom_bin, - .write = ds2780_write_param_eeprom_bin, + .read_new = ds2780_read_param_eeprom_bin, + .write_new = ds2780_write_param_eeprom_bin, }; static ssize_t ds2780_read_user_eeprom_bin(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); @@ -679,7 +679,7 @@ static ssize_t ds2780_read_user_eeprom_bin(struct file *filp, static ssize_t ds2780_write_user_eeprom_bin(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); @@ -699,14 +699,14 @@ static ssize_t ds2780_write_user_eeprom_bin(struct file *filp, return count; } -static struct bin_attribute ds2780_user_eeprom_bin_attr = { +static const struct bin_attribute ds2780_user_eeprom_bin_attr = { .attr = { .name = "user_eeprom", .mode = S_IRUGO | S_IWUSR, }, .size = DS2780_USER_EEPROM_SIZE, - .read = ds2780_read_user_eeprom_bin, - .write = ds2780_write_user_eeprom_bin, + .read_new = ds2780_read_user_eeprom_bin, + .write_new = ds2780_write_user_eeprom_bin, }; static DEVICE_ATTR(pmod_enabled, S_IRUGO | S_IWUSR, ds2780_get_pmod_enabled, @@ -726,7 +726,7 @@ static struct attribute *ds2780_sysfs_attrs[] = { NULL }; -static struct bin_attribute *ds2780_sysfs_bin_attrs[] = { +static const struct bin_attribute *const ds2780_sysfs_bin_attrs[] = { &ds2780_param_eeprom_bin_attr, &ds2780_user_eeprom_bin_attr, NULL @@ -734,7 +734,7 @@ static struct bin_attribute *ds2780_sysfs_bin_attrs[] = { static const struct attribute_group ds2780_sysfs_group = { .attrs = ds2780_sysfs_attrs, - .bin_attrs = ds2780_sysfs_bin_attrs, + .bin_attrs_new = ds2780_sysfs_bin_attrs, }; static const struct attribute_group *ds2780_sysfs_groups[] = { diff --git a/drivers/power/supply/ds2781_battery.c b/drivers/power/supply/ds2781_battery.c index c4f8ccc687f9..8a1f1f9835e0 100644 --- a/drivers/power/supply/ds2781_battery.c +++ b/drivers/power/supply/ds2781_battery.c @@ -623,7 +623,7 @@ static ssize_t ds2781_set_pio_pin(struct device *dev, static ssize_t ds2781_read_param_eeprom_bin(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); @@ -636,7 +636,7 @@ static ssize_t ds2781_read_param_eeprom_bin(struct file *filp, static ssize_t ds2781_write_param_eeprom_bin(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); @@ -656,19 +656,19 @@ static ssize_t ds2781_write_param_eeprom_bin(struct file *filp, return count; } -static struct bin_attribute ds2781_param_eeprom_bin_attr = { +static const struct bin_attribute ds2781_param_eeprom_bin_attr = { .attr = { .name = "param_eeprom", .mode = S_IRUGO | S_IWUSR, }, .size = DS2781_PARAM_EEPROM_SIZE, - .read = ds2781_read_param_eeprom_bin, - .write = ds2781_write_param_eeprom_bin, + .read_new = ds2781_read_param_eeprom_bin, + .write_new = ds2781_write_param_eeprom_bin, }; static ssize_t ds2781_read_user_eeprom_bin(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); @@ -682,7 +682,7 @@ static ssize_t ds2781_read_user_eeprom_bin(struct file *filp, static ssize_t ds2781_write_user_eeprom_bin(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); @@ -702,14 +702,14 @@ static ssize_t ds2781_write_user_eeprom_bin(struct file *filp, return count; } -static struct bin_attribute ds2781_user_eeprom_bin_attr = { +static const struct bin_attribute ds2781_user_eeprom_bin_attr = { .attr = { .name = "user_eeprom", .mode = S_IRUGO | S_IWUSR, }, .size = DS2781_USER_EEPROM_SIZE, - .read = ds2781_read_user_eeprom_bin, - .write = ds2781_write_user_eeprom_bin, + .read_new = ds2781_read_user_eeprom_bin, + .write_new = ds2781_write_user_eeprom_bin, }; static DEVICE_ATTR(pmod_enabled, S_IRUGO | S_IWUSR, ds2781_get_pmod_enabled, @@ -729,7 +729,7 @@ static struct attribute *ds2781_sysfs_attrs[] = { NULL }; -static struct bin_attribute *ds2781_sysfs_bin_attrs[] = { +static const struct bin_attribute *const ds2781_sysfs_bin_attrs[] = { &ds2781_param_eeprom_bin_attr, &ds2781_user_eeprom_bin_attr, NULL, @@ -737,7 +737,7 @@ static struct bin_attribute *ds2781_sysfs_bin_attrs[] = { static const struct attribute_group ds2781_sysfs_group = { .attrs = ds2781_sysfs_attrs, - .bin_attrs = ds2781_sysfs_bin_attrs, + .bin_attrs_new = ds2781_sysfs_bin_attrs, }; diff --git a/drivers/power/supply/ds2782_battery.c b/drivers/power/supply/ds2782_battery.c index 85aa9c465aa4..cae95d35d398 100644 --- a/drivers/power/supply/ds2782_battery.c +++ b/drivers/power/supply/ds2782_battery.c @@ -11,6 +11,7 @@ * UEvent sending added by Evgeny Romanov <romanov@neurosoft.ru> */ +#include <linux/devm-helpers.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/types.h> @@ -57,14 +58,12 @@ struct ds278x_info { struct power_supply_desc battery_desc; const struct ds278x_battery_ops *ops; struct delayed_work bat_work; - int id; int rsns; int capacity; int status; /* State Of Charge */ }; -static DEFINE_IDR(battery_id); -static DEFINE_MUTEX(battery_lock); +static DEFINE_IDA(battery_id); static inline int ds278x_read_reg(struct ds278x_info *info, int reg, u8 *val) { @@ -312,21 +311,6 @@ static void ds278x_power_supply_init(struct power_supply_desc *battery) battery->external_power_changed = NULL; } -static void ds278x_battery_remove(struct i2c_client *client) -{ - struct ds278x_info *info = i2c_get_clientdata(client); - int id = info->id; - - power_supply_unregister(info->battery); - cancel_delayed_work_sync(&info->bat_work); - kfree(info->battery_desc.name); - kfree(info); - - mutex_lock(&battery_lock); - idr_remove(&battery_id, id); - mutex_unlock(&battery_lock); -} - #ifdef CONFIG_PM_SLEEP static int ds278x_suspend(struct device *dev) @@ -368,6 +352,13 @@ static const struct ds278x_battery_ops ds278x_ops[] = { } }; +static void ds278x_free_ida(void *data) +{ + int num = (uintptr_t)data; + + ida_free(&battery_id, num); +} + static int ds278x_battery_probe(struct i2c_client *client) { const struct i2c_device_id *id = i2c_client_get_device_id(client); @@ -387,32 +378,27 @@ static int ds278x_battery_probe(struct i2c_client *client) } /* Get an ID for this battery */ - mutex_lock(&battery_lock); - ret = idr_alloc(&battery_id, client, 0, 0, GFP_KERNEL); - mutex_unlock(&battery_lock); - if (ret < 0) - goto fail_id; - num = ret; - - info = kzalloc(sizeof(*info), GFP_KERNEL); - if (!info) { - ret = -ENOMEM; - goto fail_info; - } + num = ida_alloc(&battery_id, GFP_KERNEL); + if (num < 0) + return num; + ret = devm_add_action_or_reset(&client->dev, ds278x_free_ida, (void *)(uintptr_t)num); + if (ret) + return ret; - info->battery_desc.name = kasprintf(GFP_KERNEL, "%s-%d", - client->name, num); - if (!info->battery_desc.name) { - ret = -ENOMEM; - goto fail_name; - } + info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->battery_desc.name = devm_kasprintf(&client->dev, GFP_KERNEL, + "%s-%d", client->name, num); + if (!info->battery_desc.name) + return -ENOMEM; if (id->driver_data == DS2786) info->rsns = pdata->rsns; i2c_set_clientdata(client, info); info->client = client; - info->id = num; info->ops = &ds278x_ops[id->driver_data]; ds278x_power_supply_init(&info->battery_desc); psy_cfg.drv_data = info; @@ -420,30 +406,20 @@ static int ds278x_battery_probe(struct i2c_client *client) info->capacity = 100; info->status = POWER_SUPPLY_STATUS_FULL; - INIT_DELAYED_WORK(&info->bat_work, ds278x_bat_work); - - info->battery = power_supply_register(&client->dev, - &info->battery_desc, &psy_cfg); + info->battery = devm_power_supply_register(&client->dev, + &info->battery_desc, + &psy_cfg); if (IS_ERR(info->battery)) { dev_err(&client->dev, "failed to register battery\n"); - ret = PTR_ERR(info->battery); - goto fail_register; - } else { - schedule_delayed_work(&info->bat_work, DS278x_DELAY); + return PTR_ERR(info->battery); } - return 0; + ret = devm_delayed_work_autocancel(&client->dev, &info->bat_work, ds278x_bat_work); + if (ret) + return ret; + schedule_delayed_work(&info->bat_work, DS278x_DELAY); -fail_register: - kfree(info->battery_desc.name); -fail_name: - kfree(info); -fail_info: - mutex_lock(&battery_lock); - idr_remove(&battery_id, num); - mutex_unlock(&battery_lock); -fail_id: - return ret; + return 0; } static const struct i2c_device_id ds278x_id[] = { @@ -459,7 +435,6 @@ static struct i2c_driver ds278x_battery_driver = { .pm = &ds278x_battery_pm_ops, }, .probe = ds278x_battery_probe, - .remove = ds278x_battery_remove, .id_table = ds278x_id, }; module_i2c_driver(ds278x_battery_driver); diff --git a/drivers/power/supply/generic-adc-battery.c b/drivers/power/supply/generic-adc-battery.c index d5d215f5ad8b..f5f2566b3a32 100644 --- a/drivers/power/supply/generic-adc-battery.c +++ b/drivers/power/supply/generic-adc-battery.c @@ -166,7 +166,7 @@ static int gab_probe(struct platform_device *pdev) if (!adc_bat) return -ENOMEM; - psy_cfg.of_node = pdev->dev.of_node; + psy_cfg.fwnode = dev_fwnode(&pdev->dev); psy_cfg.drv_data = adc_bat; psy_desc = &adc_bat->psy_desc; psy_desc->name = dev_name(&pdev->dev); diff --git a/drivers/power/supply/gpio-charger.c b/drivers/power/supply/gpio-charger.c index 6139f736ecbe..1b2da9b5fb65 100644 --- a/drivers/power/supply/gpio-charger.c +++ b/drivers/power/supply/gpio-charger.c @@ -195,6 +195,8 @@ static int init_charge_current_limit(struct device *dev, { int i, len; u32 cur_limit = U32_MAX; + bool set_def_limit; + u32 def_limit; gpio_charger->current_limit_gpios = devm_gpiod_get_array_optional(dev, "charge-current-limit", GPIOD_OUT_LOW); @@ -228,6 +230,9 @@ static int init_charge_current_limit(struct device *dev, if (len < 0) return len; + set_def_limit = !device_property_read_u32(dev, + "charge-current-limit-default-microamp", + &def_limit); for (i=0; i < gpio_charger->current_limit_map_size; i++) { if (gpio_charger->current_limit_map[i].limit_ua > cur_limit) { dev_err(dev, "charge-current-limit-mapping not sorted by current in descending order\n"); @@ -235,8 +240,16 @@ static int init_charge_current_limit(struct device *dev, } cur_limit = gpio_charger->current_limit_map[i].limit_ua; + if (set_def_limit && def_limit == cur_limit) { + set_charge_current_limit(gpio_charger, cur_limit); + return 0; + } } + if (set_def_limit) + dev_warn(dev, "charge-current-limit-default-microamp %u not listed in charge-current-limit-mapping\n", + def_limit); + /* default to smallest current limitation for safety reasons */ len = gpio_charger->current_limit_map_size - 1; set_charge_current_limit(gpio_charger, @@ -320,7 +333,7 @@ static int gpio_charger_probe(struct platform_device *pdev) charger_desc->property_is_writeable = gpio_charger_property_is_writeable; - psy_cfg.of_node = dev->of_node; + psy_cfg.fwnode = dev_fwnode(dev); psy_cfg.drv_data = gpio_charger; if (pdata) { @@ -353,7 +366,9 @@ static int gpio_charger_probe(struct platform_device *pdev) platform_set_drvdata(pdev, gpio_charger); - device_init_wakeup(dev, 1); + ret = devm_device_init_wakeup(dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to init wakeup\n"); return 0; } diff --git a/drivers/power/supply/ingenic-battery.c b/drivers/power/supply/ingenic-battery.c index 0a40f425c277..b111c7ce2be3 100644 --- a/drivers/power/supply/ingenic-battery.c +++ b/drivers/power/supply/ingenic-battery.c @@ -146,7 +146,7 @@ static int ingenic_battery_probe(struct platform_device *pdev) desc->num_properties = ARRAY_SIZE(ingenic_battery_properties); desc->get_property = ingenic_battery_get_property; psy_cfg.drv_data = bat; - psy_cfg.of_node = dev->of_node; + psy_cfg.fwnode = dev_fwnode(dev); bat->battery = devm_power_supply_register(dev, desc, &psy_cfg); if (IS_ERR(bat->battery)) diff --git a/drivers/power/supply/ip5xxx_power.c b/drivers/power/supply/ip5xxx_power.c index 82263646ddc6..24eea7a91b30 100644 --- a/drivers/power/supply/ip5xxx_power.c +++ b/drivers/power/supply/ip5xxx_power.c @@ -7,76 +7,154 @@ #include <linux/power_supply.h> #include <linux/regmap.h> -#define IP5XXX_SYS_CTL0 0x01 -#define IP5XXX_SYS_CTL0_WLED_DET_EN BIT(4) -#define IP5XXX_SYS_CTL0_WLED_EN BIT(3) -#define IP5XXX_SYS_CTL0_BOOST_EN BIT(2) -#define IP5XXX_SYS_CTL0_CHARGER_EN BIT(1) -#define IP5XXX_SYS_CTL1 0x02 -#define IP5XXX_SYS_CTL1_LIGHT_SHDN_EN BIT(1) -#define IP5XXX_SYS_CTL1_LOAD_PWRUP_EN BIT(0) -#define IP5XXX_SYS_CTL2 0x0c -#define IP5XXX_SYS_CTL2_LIGHT_SHDN_TH GENMASK(7, 3) -#define IP5XXX_SYS_CTL3 0x03 -#define IP5XXX_SYS_CTL3_LONG_PRESS_TIME_SEL GENMASK(7, 6) -#define IP5XXX_SYS_CTL3_BTN_SHDN_EN BIT(5) -#define IP5XXX_SYS_CTL4 0x04 -#define IP5XXX_SYS_CTL4_SHDN_TIME_SEL GENMASK(7, 6) -#define IP5XXX_SYS_CTL4_VIN_PULLOUT_BOOST_EN BIT(5) -#define IP5XXX_SYS_CTL5 0x07 -#define IP5XXX_SYS_CTL5_NTC_DIS BIT(6) -#define IP5XXX_SYS_CTL5_WLED_MODE_SEL BIT(1) -#define IP5XXX_SYS_CTL5_BTN_SHDN_SEL BIT(0) -#define IP5XXX_CHG_CTL1 0x22 -#define IP5XXX_CHG_CTL1_BOOST_UVP_SEL GENMASK(3, 2) -#define IP5XXX_CHG_CTL2 0x24 -#define IP5XXX_CHG_CTL2_BAT_TYPE_SEL GENMASK(6, 5) -#define IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_2V (0x0 << 5) -#define IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_3V (0x1 << 5) -#define IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_35V (0x2 << 5) -#define IP5XXX_CHG_CTL2_CONST_VOLT_SEL GENMASK(2, 1) -#define IP5XXX_CHG_CTL4 0x26 -#define IP5XXX_CHG_CTL4_BAT_TYPE_SEL_EN BIT(6) -#define IP5XXX_CHG_CTL4A 0x25 -#define IP5XXX_CHG_CTL4A_CONST_CUR_SEL GENMASK(4, 0) -#define IP5XXX_MFP_CTL0 0x51 -#define IP5XXX_MFP_CTL1 0x52 -#define IP5XXX_GPIO_CTL2 0x53 -#define IP5XXX_GPIO_CTL2A 0x54 -#define IP5XXX_GPIO_CTL3 0x55 -#define IP5XXX_READ0 0x71 -#define IP5XXX_READ0_CHG_STAT GENMASK(7, 5) -#define IP5XXX_READ0_CHG_STAT_IDLE (0x0 << 5) -#define IP5XXX_READ0_CHG_STAT_TRICKLE (0x1 << 5) -#define IP5XXX_READ0_CHG_STAT_CONST_VOLT (0x2 << 5) -#define IP5XXX_READ0_CHG_STAT_CONST_CUR (0x3 << 5) -#define IP5XXX_READ0_CHG_STAT_CONST_VOLT_STOP (0x4 << 5) -#define IP5XXX_READ0_CHG_STAT_FULL (0x5 << 5) -#define IP5XXX_READ0_CHG_STAT_TIMEOUT (0x6 << 5) -#define IP5XXX_READ0_CHG_OP BIT(4) -#define IP5XXX_READ0_CHG_END BIT(3) -#define IP5XXX_READ0_CONST_VOLT_TIMEOUT BIT(2) -#define IP5XXX_READ0_CHG_TIMEOUT BIT(1) -#define IP5XXX_READ0_TRICKLE_TIMEOUT BIT(0) -#define IP5XXX_READ0_TIMEOUT GENMASK(2, 0) -#define IP5XXX_READ1 0x72 -#define IP5XXX_READ1_WLED_PRESENT BIT(7) -#define IP5XXX_READ1_LIGHT_LOAD BIT(6) -#define IP5XXX_READ1_VIN_OVERVOLT BIT(5) -#define IP5XXX_READ2 0x77 -#define IP5XXX_READ2_BTN_PRESS BIT(3) -#define IP5XXX_READ2_BTN_LONG_PRESS BIT(1) -#define IP5XXX_READ2_BTN_SHORT_PRESS BIT(0) -#define IP5XXX_BATVADC_DAT0 0xa2 -#define IP5XXX_BATVADC_DAT1 0xa3 -#define IP5XXX_BATIADC_DAT0 0xa4 -#define IP5XXX_BATIADC_DAT1 0xa5 -#define IP5XXX_BATOCV_DAT0 0xa8 -#define IP5XXX_BATOCV_DAT1 0xa9 +#define IP5XXX_BAT_TYPE_4_2V 0x0 +#define IP5XXX_BAT_TYPE_4_3V 0x1 +#define IP5XXX_BAT_TYPE_4_35V 0x2 +#define IP5XXX_BAT_TYPE_4_4V 0x3 +#define IP5XXX_CHG_STAT_IDLE 0x0 +#define IP5XXX_CHG_STAT_TRICKLE 0x1 +#define IP5XXX_CHG_STAT_CONST_VOLT 0x2 +#define IP5XXX_CHG_STAT_CONST_CUR 0x3 +#define IP5XXX_CHG_STAT_CONST_VOLT_STOP 0x4 +#define IP5XXX_CHG_STAT_FULL 0x5 +#define IP5XXX_CHG_STAT_TIMEOUT 0x6 struct ip5xxx { struct regmap *regmap; bool initialized; + struct { + struct { + /* Charger enable */ + struct regmap_field *enable; + /* Constant voltage value */ + struct regmap_field *const_volt_sel; + /* Constant current value */ + struct regmap_field *const_curr_sel; + /* Charger status */ + struct regmap_field *status; + /* Charging ended flag */ + struct regmap_field *chg_end; + /* Timeout flags (CV, charge, trickle) */ + struct regmap_field *timeout; + /* Overvoltage limit */ + struct regmap_field *vin_overvolt; + } charger; + struct { + /* Boost converter enable */ + struct regmap_field *enable; + struct { + /* Light load shutdown enable */ + struct regmap_field *enable; + /* Light load shutdown current limit */ + struct regmap_field *i_limit; + } light_load_shutdown; + /* Automatic powerup on increased load */ + struct regmap_field *load_powerup_en; + /* Automatic powerup on VIN pull-out */ + struct regmap_field *vin_pullout_en; + /* Undervoltage limit */ + struct regmap_field *undervolt_limit; + /* Light load status flag */ + struct regmap_field *light_load_status; + } boost; + struct { + /* NTC disable */ + struct regmap_field *ntc_dis; + /* Battery voltage type */ + struct regmap_field *type; + /* Battery voltage autoset from Vset pin */ + struct regmap_field *vset_en; + struct { + /* Battery measurement registers */ + struct ip5xxx_battery_adc_regs { + struct regmap_field *low; + struct regmap_field *high; + } volt, curr, open_volt; + } adc; + } battery; + struct { + /* Double/long press shutdown enable */ + struct regmap_field *shdn_enable; + /* WLED activation: double press or long press */ + struct regmap_field *wled_mode; + /* Shutdown activation: double press or long press */ + struct regmap_field *shdn_mode; + /* Long press time */ + struct regmap_field *long_press_time; + /* Button pressed */ + struct regmap_field *pressed; + /* Button long-pressed */ + struct regmap_field *long_pressed; + /* Button short-pressed */ + struct regmap_field *short_pressed; + } btn; + struct { + /* WLED enable */ + struct regmap_field *enable; + /* WLED detect */ + struct regmap_field *detect_en; + /* WLED present */ + struct regmap_field *present; + } wled; + } regs; + + /* Maximum supported battery voltage (via regs.battery.type) */ + int vbat_max; + /* Scaling constants for regs.boost.undervolt_limit */ + struct { + int setpoint; + int microvolts_per_bit; + } boost_undervolt; + /* Scaling constants for regs.charger.const_curr_sel */ + struct { + int setpoint; + } const_curr; + /* Whether regs.charger.chg_end is inverted */ + u8 chg_end_inverted; +}; + +#define REG_FIELD_UNSUPPORTED { .lsb = 1 } +/* Register fields layout. Unsupported registers marked as { .lsb = 1 } */ +struct ip5xxx_regfield_config { + const struct reg_field charger_enable; + const struct reg_field charger_const_volt_sel; + const struct reg_field charger_const_curr_sel; + const struct reg_field charger_status; + const struct reg_field charger_chg_end; + const struct reg_field charger_timeout; + const struct reg_field charger_vin_overvolt; + const struct reg_field boost_enable; + const struct reg_field boost_llshdn_enable; + const struct reg_field boost_llshdn_i_limit; + const struct reg_field boost_load_powerup_en; + const struct reg_field boost_vin_pullout_en; + const struct reg_field boost_undervolt_limit; + const struct reg_field boost_light_load_status; + const struct reg_field battery_ntc_dis; + const struct reg_field battery_type; + const struct reg_field battery_vset_en; + const struct reg_field battery_adc_volt_low; + const struct reg_field battery_adc_volt_high; + const struct reg_field battery_adc_curr_low; + const struct reg_field battery_adc_curr_high; + const struct reg_field battery_adc_ovolt_low; + const struct reg_field battery_adc_ovolt_high; + const struct reg_field btn_shdn_enable; + const struct reg_field btn_wled_mode; + const struct reg_field btn_shdn_mode; + const struct reg_field btn_long_press_time; + const struct reg_field btn_pressed; + const struct reg_field btn_long_pressed; + const struct reg_field btn_short_pressed; + const struct reg_field wled_enable; + const struct reg_field wled_detect_en; + const struct reg_field wled_present; + + int vbat_max; + int boost_undervolt_setpoint; + int boost_undervolt_uv_per_bit; + int const_curr_setpoint; + u8 chg_end_inverted; }; /* @@ -87,24 +165,30 @@ struct ip5xxx { * 2) Attempt the initialization sequence on each subsequent register access * until it succeeds. */ -static int ip5xxx_read(struct ip5xxx *ip5xxx, unsigned int reg, +static int ip5xxx_read(struct ip5xxx *ip5xxx, struct regmap_field *field, unsigned int *val) { int ret; - ret = regmap_read(ip5xxx->regmap, reg, val); + if (!field) + return -EOPNOTSUPP; + + ret = regmap_field_read(field, val); if (ret) ip5xxx->initialized = false; return ret; } -static int ip5xxx_update_bits(struct ip5xxx *ip5xxx, unsigned int reg, - unsigned int mask, unsigned int val) +static int ip5xxx_write(struct ip5xxx *ip5xxx, struct regmap_field *field, + unsigned int val) { int ret; - ret = regmap_update_bits(ip5xxx->regmap, reg, mask, val); + if (!field) + return -EOPNOTSUPP; + + ret = regmap_field_write(field, val); if (ret) ip5xxx->initialized = false; @@ -123,28 +207,26 @@ static int ip5xxx_initialize(struct power_supply *psy) * Disable shutdown under light load. * Enable power on when under load. */ - ret = ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL1, - IP5XXX_SYS_CTL1_LIGHT_SHDN_EN | - IP5XXX_SYS_CTL1_LOAD_PWRUP_EN, - IP5XXX_SYS_CTL1_LOAD_PWRUP_EN); + if (ip5xxx->regs.boost.light_load_shutdown.enable) { + ret = ip5xxx_write(ip5xxx, ip5xxx->regs.boost.light_load_shutdown.enable, 0); + if (ret) + return ret; + } + ret = ip5xxx_write(ip5xxx, ip5xxx->regs.boost.load_powerup_en, 1); if (ret) return ret; /* * Enable shutdown after a long button press (as configured below). */ - ret = ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL3, - IP5XXX_SYS_CTL3_BTN_SHDN_EN, - IP5XXX_SYS_CTL3_BTN_SHDN_EN); + ret = ip5xxx_write(ip5xxx, ip5xxx->regs.btn.shdn_enable, 1); if (ret) return ret; /* * Power on automatically when VIN is removed. */ - ret = ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL4, - IP5XXX_SYS_CTL4_VIN_PULLOUT_BOOST_EN, - IP5XXX_SYS_CTL4_VIN_PULLOUT_BOOST_EN); + ret = ip5xxx_write(ip5xxx, ip5xxx->regs.boost.vin_pullout_en, 1); if (ret) return ret; @@ -152,12 +234,15 @@ static int ip5xxx_initialize(struct power_supply *psy) * Enable the NTC. * Configure the button for two presses => LED, long press => shutdown. */ - ret = ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL5, - IP5XXX_SYS_CTL5_NTC_DIS | - IP5XXX_SYS_CTL5_WLED_MODE_SEL | - IP5XXX_SYS_CTL5_BTN_SHDN_SEL, - IP5XXX_SYS_CTL5_WLED_MODE_SEL | - IP5XXX_SYS_CTL5_BTN_SHDN_SEL); + if (ip5xxx->regs.battery.ntc_dis) { + ret = ip5xxx_write(ip5xxx, ip5xxx->regs.battery.ntc_dis, 0); + if (ret) + return ret; + } + ret = ip5xxx_write(ip5xxx, ip5xxx->regs.btn.wled_mode, 1); + if (ret) + return ret; + ret = ip5xxx_write(ip5xxx, ip5xxx->regs.btn.shdn_mode, 1); if (ret) return ret; @@ -186,24 +271,37 @@ static int ip5xxx_battery_get_status(struct ip5xxx *ip5xxx, int *val) unsigned int rval; int ret; - ret = ip5xxx_read(ip5xxx, IP5XXX_READ0, &rval); + if (!ip5xxx->regs.charger.status) { + // Fall-back to Charging Ended bit + ret = ip5xxx_read(ip5xxx, ip5xxx->regs.charger.chg_end, &rval); + if (ret) + return ret; + + if (rval == ip5xxx->chg_end_inverted) + *val = POWER_SUPPLY_STATUS_CHARGING; + else + *val = POWER_SUPPLY_STATUS_NOT_CHARGING; + return 0; + } + + ret = ip5xxx_read(ip5xxx, ip5xxx->regs.charger.status, &rval); if (ret) return ret; - switch (rval & IP5XXX_READ0_CHG_STAT) { - case IP5XXX_READ0_CHG_STAT_IDLE: + switch (rval) { + case IP5XXX_CHG_STAT_IDLE: *val = POWER_SUPPLY_STATUS_DISCHARGING; break; - case IP5XXX_READ0_CHG_STAT_TRICKLE: - case IP5XXX_READ0_CHG_STAT_CONST_CUR: - case IP5XXX_READ0_CHG_STAT_CONST_VOLT: + case IP5XXX_CHG_STAT_TRICKLE: + case IP5XXX_CHG_STAT_CONST_CUR: + case IP5XXX_CHG_STAT_CONST_VOLT: *val = POWER_SUPPLY_STATUS_CHARGING; break; - case IP5XXX_READ0_CHG_STAT_CONST_VOLT_STOP: - case IP5XXX_READ0_CHG_STAT_FULL: + case IP5XXX_CHG_STAT_CONST_VOLT_STOP: + case IP5XXX_CHG_STAT_FULL: *val = POWER_SUPPLY_STATUS_FULL; break; - case IP5XXX_READ0_CHG_STAT_TIMEOUT: + case IP5XXX_CHG_STAT_TIMEOUT: *val = POWER_SUPPLY_STATUS_NOT_CHARGING; break; default: @@ -218,22 +316,22 @@ static int ip5xxx_battery_get_charge_type(struct ip5xxx *ip5xxx, int *val) unsigned int rval; int ret; - ret = ip5xxx_read(ip5xxx, IP5XXX_READ0, &rval); + ret = ip5xxx_read(ip5xxx, ip5xxx->regs.charger.status, &rval); if (ret) return ret; - switch (rval & IP5XXX_READ0_CHG_STAT) { - case IP5XXX_READ0_CHG_STAT_IDLE: - case IP5XXX_READ0_CHG_STAT_CONST_VOLT_STOP: - case IP5XXX_READ0_CHG_STAT_FULL: - case IP5XXX_READ0_CHG_STAT_TIMEOUT: + switch (rval) { + case IP5XXX_CHG_STAT_IDLE: + case IP5XXX_CHG_STAT_CONST_VOLT_STOP: + case IP5XXX_CHG_STAT_FULL: + case IP5XXX_CHG_STAT_TIMEOUT: *val = POWER_SUPPLY_CHARGE_TYPE_NONE; break; - case IP5XXX_READ0_CHG_STAT_TRICKLE: + case IP5XXX_CHG_STAT_TRICKLE: *val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; break; - case IP5XXX_READ0_CHG_STAT_CONST_CUR: - case IP5XXX_READ0_CHG_STAT_CONST_VOLT: + case IP5XXX_CHG_STAT_CONST_CUR: + case IP5XXX_CHG_STAT_CONST_VOLT: *val = POWER_SUPPLY_CHARGE_TYPE_STANDARD; break; default: @@ -248,11 +346,11 @@ static int ip5xxx_battery_get_health(struct ip5xxx *ip5xxx, int *val) unsigned int rval; int ret; - ret = ip5xxx_read(ip5xxx, IP5XXX_READ0, &rval); + ret = ip5xxx_read(ip5xxx, ip5xxx->regs.charger.timeout, &rval); if (ret) return ret; - if (rval & IP5XXX_READ0_TIMEOUT) + if (rval) *val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; else *val = POWER_SUPPLY_HEALTH_GOOD; @@ -265,7 +363,7 @@ static int ip5xxx_battery_get_voltage_max(struct ip5xxx *ip5xxx, int *val) unsigned int rval; int ret; - ret = ip5xxx_read(ip5xxx, IP5XXX_CHG_CTL2, &rval); + ret = ip5xxx_read(ip5xxx, ip5xxx->regs.battery.type, &rval); if (ret) return ret; @@ -273,16 +371,19 @@ static int ip5xxx_battery_get_voltage_max(struct ip5xxx *ip5xxx, int *val) * It is not clear what this will return if * IP5XXX_CHG_CTL4_BAT_TYPE_SEL_EN is not set... */ - switch (rval & IP5XXX_CHG_CTL2_BAT_TYPE_SEL) { - case IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_2V: + switch (rval) { + case IP5XXX_BAT_TYPE_4_2V: *val = 4200000; break; - case IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_3V: + case IP5XXX_BAT_TYPE_4_3V: *val = 4300000; break; - case IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_35V: + case IP5XXX_BAT_TYPE_4_35V: *val = 4350000; break; + case IP5XXX_BAT_TYPE_4_4V: + *val = 4400000; + break; default: return -EINVAL; } @@ -291,16 +392,16 @@ static int ip5xxx_battery_get_voltage_max(struct ip5xxx *ip5xxx, int *val) } static int ip5xxx_battery_read_adc(struct ip5xxx *ip5xxx, - u8 lo_reg, u8 hi_reg, int *val) + struct ip5xxx_battery_adc_regs *regs, int *val) { unsigned int hi, lo; int ret; - ret = ip5xxx_read(ip5xxx, lo_reg, &lo); + ret = ip5xxx_read(ip5xxx, regs->low, &lo); if (ret) return ret; - ret = ip5xxx_read(ip5xxx, hi_reg, &hi); + ret = ip5xxx_read(ip5xxx, regs->high, &hi); if (ret) return ret; @@ -335,33 +436,35 @@ static int ip5xxx_battery_get_property(struct power_supply *psy, return ip5xxx_battery_get_voltage_max(ip5xxx, &val->intval); case POWER_SUPPLY_PROP_VOLTAGE_NOW: - ret = ip5xxx_battery_read_adc(ip5xxx, IP5XXX_BATVADC_DAT0, - IP5XXX_BATVADC_DAT1, &raw); + ret = ip5xxx_battery_read_adc(ip5xxx, &ip5xxx->regs.battery.adc.volt, &raw); + if (ret) + return ret; val->intval = 2600000 + DIV_ROUND_CLOSEST(raw * 26855, 100); return 0; case POWER_SUPPLY_PROP_VOLTAGE_OCV: - ret = ip5xxx_battery_read_adc(ip5xxx, IP5XXX_BATOCV_DAT0, - IP5XXX_BATOCV_DAT1, &raw); + ret = ip5xxx_battery_read_adc(ip5xxx, &ip5xxx->regs.battery.adc.open_volt, &raw); + if (ret) + return ret; val->intval = 2600000 + DIV_ROUND_CLOSEST(raw * 26855, 100); return 0; case POWER_SUPPLY_PROP_CURRENT_NOW: - ret = ip5xxx_battery_read_adc(ip5xxx, IP5XXX_BATIADC_DAT0, - IP5XXX_BATIADC_DAT1, &raw); + ret = ip5xxx_battery_read_adc(ip5xxx, &ip5xxx->regs.battery.adc.curr, &raw); + if (ret) + return ret; val->intval = DIV_ROUND_CLOSEST(raw * 149197, 200); return 0; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: - ret = ip5xxx_read(ip5xxx, IP5XXX_CHG_CTL4A, &rval); + ret = ip5xxx_read(ip5xxx, ip5xxx->regs.charger.const_curr_sel, &rval); if (ret) return ret; - rval &= IP5XXX_CHG_CTL4A_CONST_CUR_SEL; - val->intval = 100000 * rval; + val->intval = ip5xxx->const_curr.setpoint + 100000 * rval; return 0; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: @@ -373,12 +476,11 @@ static int ip5xxx_battery_get_property(struct power_supply *psy, if (ret) return ret; - ret = ip5xxx_read(ip5xxx, IP5XXX_CHG_CTL2, &rval); + ret = ip5xxx_read(ip5xxx, ip5xxx->regs.charger.const_volt_sel, &rval); if (ret) return ret; - rval &= IP5XXX_CHG_CTL2_CONST_VOLT_SEL; - val->intval = vmax + 14000 * (rval >> 1); + val->intval = vmax + 14000 * rval; return 0; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: @@ -399,30 +501,36 @@ static int ip5xxx_battery_set_voltage_max(struct ip5xxx *ip5xxx, int val) unsigned int rval; int ret; + if (val > ip5xxx->vbat_max) + return -EINVAL; + switch (val) { case 4200000: - rval = IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_2V; + rval = IP5XXX_BAT_TYPE_4_2V; break; case 4300000: - rval = IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_3V; + rval = IP5XXX_BAT_TYPE_4_3V; break; case 4350000: - rval = IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_35V; + rval = IP5XXX_BAT_TYPE_4_35V; + break; + case 4400000: + rval = IP5XXX_BAT_TYPE_4_4V; break; default: return -EINVAL; } - ret = ip5xxx_update_bits(ip5xxx, IP5XXX_CHG_CTL2, - IP5XXX_CHG_CTL2_BAT_TYPE_SEL, rval); + ret = ip5xxx_write(ip5xxx, ip5xxx->regs.battery.type, rval); if (ret) return ret; - ret = ip5xxx_update_bits(ip5xxx, IP5XXX_CHG_CTL4, - IP5XXX_CHG_CTL4_BAT_TYPE_SEL_EN, - IP5XXX_CHG_CTL4_BAT_TYPE_SEL_EN); - if (ret) - return ret; + /* Don't try to auto-detect battery type, even if the IC could */ + if (ip5xxx->regs.battery.vset_en) { + ret = ip5xxx_write(ip5xxx, ip5xxx->regs.battery.vset_en, 1); + if (ret) + return ret; + } return 0; } @@ -443,7 +551,7 @@ static int ip5xxx_battery_set_property(struct power_supply *psy, case POWER_SUPPLY_PROP_STATUS: switch (val->intval) { case POWER_SUPPLY_STATUS_CHARGING: - rval = IP5XXX_SYS_CTL0_CHARGER_EN; + rval = 1; break; case POWER_SUPPLY_STATUS_DISCHARGING: case POWER_SUPPLY_STATUS_NOT_CHARGING: @@ -452,25 +560,22 @@ static int ip5xxx_battery_set_property(struct power_supply *psy, default: return -EINVAL; } - return ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL0, - IP5XXX_SYS_CTL0_CHARGER_EN, rval); + return ip5xxx_write(ip5xxx, ip5xxx->regs.charger.enable, rval); case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: return ip5xxx_battery_set_voltage_max(ip5xxx, val->intval); case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: - rval = val->intval / 100000; - return ip5xxx_update_bits(ip5xxx, IP5XXX_CHG_CTL4A, - IP5XXX_CHG_CTL4A_CONST_CUR_SEL, rval); + rval = (val->intval - ip5xxx->const_curr.setpoint) / 100000; + return ip5xxx_write(ip5xxx, ip5xxx->regs.charger.const_curr_sel, rval); case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: ret = ip5xxx_battery_get_voltage_max(ip5xxx, &vmax); if (ret) return ret; - rval = ((val->intval - vmax) / 14000) << 1; - return ip5xxx_update_bits(ip5xxx, IP5XXX_CHG_CTL2, - IP5XXX_CHG_CTL2_CONST_VOLT_SEL, rval); + rval = (val->intval - vmax) / 14000; + return ip5xxx_write(ip5xxx, ip5xxx->regs.charger.const_volt_sel, rval); default: return -EINVAL; @@ -515,20 +620,20 @@ static int ip5xxx_boost_get_property(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_ONLINE: - ret = ip5xxx_read(ip5xxx, IP5XXX_SYS_CTL0, &rval); + ret = ip5xxx_read(ip5xxx, ip5xxx->regs.boost.enable, &rval); if (ret) return ret; - val->intval = !!(rval & IP5XXX_SYS_CTL0_BOOST_EN); + val->intval = !!rval; return 0; case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: - ret = ip5xxx_read(ip5xxx, IP5XXX_CHG_CTL1, &rval); + ret = ip5xxx_read(ip5xxx, ip5xxx->regs.boost.undervolt_limit, &rval); if (ret) return ret; - rval &= IP5XXX_CHG_CTL1_BOOST_UVP_SEL; - val->intval = 4530000 + 100000 * (rval >> 2); + val->intval = ip5xxx->boost_undervolt.setpoint + + ip5xxx->boost_undervolt.microvolts_per_bit * rval; return 0; default: @@ -550,14 +655,12 @@ static int ip5xxx_boost_set_property(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_ONLINE: - rval = val->intval ? IP5XXX_SYS_CTL0_BOOST_EN : 0; - return ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL0, - IP5XXX_SYS_CTL0_BOOST_EN, rval); + return ip5xxx_write(ip5xxx, ip5xxx->regs.boost.enable, !!val->intval); case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: - rval = ((val->intval - 4530000) / 100000) << 2; - return ip5xxx_update_bits(ip5xxx, IP5XXX_CHG_CTL1, - IP5XXX_CHG_CTL1_BOOST_UVP_SEL, rval); + rval = (val->intval - ip5xxx->boost_undervolt.setpoint) / + ip5xxx->boost_undervolt.microvolts_per_bit; + return ip5xxx_write(ip5xxx, ip5xxx->regs.boost.undervolt_limit, rval); default: return -EINVAL; @@ -583,11 +686,149 @@ static const struct power_supply_desc ip5xxx_boost_desc = { static const struct regmap_config ip5xxx_regmap_config = { .reg_bits = 8, .val_bits = 8, - .max_register = IP5XXX_BATOCV_DAT1, + .max_register = 0xa9, +}; + +static struct ip5xxx_regfield_config ip51xx_fields = { + .charger_enable = REG_FIELD(0x01, 1, 1), + .charger_const_volt_sel = REG_FIELD(0x24, 1, 2), + .charger_const_curr_sel = REG_FIELD(0x25, 0, 4), + .charger_status = REG_FIELD(0x71, 5, 7), + .charger_chg_end = REG_FIELD(0x71, 3, 3), + .charger_timeout = REG_FIELD(0x71, 0, 2), + .charger_vin_overvolt = REG_FIELD(0x72, 5, 5), + .boost_enable = REG_FIELD(0x01, 2, 2), + .boost_llshdn_enable = REG_FIELD(0x02, 1, 1), + .boost_llshdn_i_limit = REG_FIELD(0x0c, 3, 7), + .boost_load_powerup_en = REG_FIELD(0x02, 0, 0), + .boost_vin_pullout_en = REG_FIELD(0x04, 5, 5), + .boost_undervolt_limit = REG_FIELD(0x22, 2, 3), + .boost_light_load_status = REG_FIELD(0x72, 6, 6), + .battery_ntc_dis = REG_FIELD(0x07, 6, 6), + .battery_type = REG_FIELD(0x24, 5, 6), + .battery_vset_en = REG_FIELD(0x26, 6, 6), + .battery_adc_volt_low = REG_FIELD(0xa2, 0, 7), + .battery_adc_volt_high = REG_FIELD(0xa3, 0, 5), + .battery_adc_curr_low = REG_FIELD(0xa4, 0, 7), + .battery_adc_curr_high = REG_FIELD(0xa5, 0, 5), + .battery_adc_ovolt_low = REG_FIELD(0xa8, 0, 7), + .battery_adc_ovolt_high = REG_FIELD(0xa9, 0, 5), + .btn_shdn_enable = REG_FIELD(0x03, 5, 5), + .btn_wled_mode = REG_FIELD(0x07, 1, 1), + .btn_shdn_mode = REG_FIELD(0x07, 0, 0), + .btn_long_press_time = REG_FIELD(0x03, 6, 7), + .btn_pressed = REG_FIELD(0x77, 3, 3), + .btn_long_pressed = REG_FIELD(0x77, 1, 1), + .btn_short_pressed = REG_FIELD(0x77, 0, 0), + .wled_enable = REG_FIELD(0x01, 3, 3), + .wled_detect_en = REG_FIELD(0x01, 4, 4), + .wled_present = REG_FIELD(0x72, 7, 7), + + .vbat_max = 4350000, + .boost_undervolt_setpoint = 4530000, + .boost_undervolt_uv_per_bit = 100000, +}; + +static struct ip5xxx_regfield_config ip5306_fields = { + .charger_enable = REG_FIELD(0x00, 4, 4), + .charger_const_volt_sel = REG_FIELD(0x22, 0, 1), + .charger_const_curr_sel = REG_FIELD(0x24, 0, 4), + .charger_status = REG_FIELD_UNSUPPORTED, // other bits... + .charger_chg_end = REG_FIELD(0x71, 3, 3), + .charger_timeout = REG_FIELD_UNSUPPORTED, + .charger_vin_overvolt = REG_FIELD_UNSUPPORTED, + .boost_enable = REG_FIELD(0x00, 5, 5), + .boost_llshdn_enable = REG_FIELD_UNSUPPORTED, + .boost_llshdn_i_limit = REG_FIELD_UNSUPPORTED, + .boost_load_powerup_en = REG_FIELD(0x00, 2, 2), + .boost_vin_pullout_en = REG_FIELD(0x01, 2, 2), + .boost_undervolt_limit = REG_FIELD(0x21, 2, 4), + .boost_light_load_status = REG_FIELD(0x72, 2, 2), + .battery_ntc_dis = REG_FIELD_UNSUPPORTED, + .battery_type = REG_FIELD(0x22, 2, 3), + .battery_vset_en = REG_FIELD_UNSUPPORTED, + .battery_adc_volt_low = REG_FIELD_UNSUPPORTED, + .battery_adc_volt_high = REG_FIELD_UNSUPPORTED, + .battery_adc_curr_low = REG_FIELD_UNSUPPORTED, + .battery_adc_curr_high = REG_FIELD_UNSUPPORTED, + .battery_adc_ovolt_low = REG_FIELD_UNSUPPORTED, + .battery_adc_ovolt_high = REG_FIELD_UNSUPPORTED, + .btn_shdn_enable = REG_FIELD(0x00, 0, 0), + .btn_wled_mode = REG_FIELD(0x01, 6, 6), + .btn_shdn_mode = REG_FIELD(0x01, 7, 7), + .btn_long_press_time = REG_FIELD(0x02, 4, 4), // +1s + .btn_pressed = REG_FIELD_UNSUPPORTED, + /* TODO: double press */ + .btn_long_pressed = REG_FIELD(0x77, 1, 1), + .btn_short_pressed = REG_FIELD(0x77, 0, 0), + .wled_enable = REG_FIELD_UNSUPPORTED, + .wled_detect_en = REG_FIELD_UNSUPPORTED, + .wled_present = REG_FIELD_UNSUPPORTED, + + .vbat_max = 4400000, + .boost_undervolt_setpoint = 4450000, + .boost_undervolt_uv_per_bit = 50000, + .const_curr_setpoint = 50000, + .chg_end_inverted = 1, }; +#define ip5xxx_setup_reg(_field, _reg) \ + do { \ + if (likely(cfg->_field.lsb <= cfg->_field.msb)) { \ + struct regmap_field *_tmp = devm_regmap_field_alloc(dev, \ + ip5xxx->regmap, cfg->_field); \ + if (!IS_ERR(_tmp)) \ + ip5xxx->regs._reg = _tmp; \ + } \ + } while (0) + +static void ip5xxx_setup_regs(struct device *dev, struct ip5xxx *ip5xxx, + const struct ip5xxx_regfield_config *cfg) +{ + ip5xxx_setup_reg(charger_enable, charger.enable); + ip5xxx_setup_reg(charger_const_volt_sel, charger.const_volt_sel); + ip5xxx_setup_reg(charger_const_curr_sel, charger.const_curr_sel); + ip5xxx_setup_reg(charger_status, charger.status); + ip5xxx_setup_reg(charger_chg_end, charger.chg_end); + ip5xxx_setup_reg(charger_timeout, charger.timeout); + ip5xxx_setup_reg(charger_vin_overvolt, charger.vin_overvolt); + ip5xxx_setup_reg(boost_enable, boost.enable); + ip5xxx_setup_reg(boost_llshdn_enable, boost.light_load_shutdown.enable); + ip5xxx_setup_reg(boost_llshdn_i_limit, boost.light_load_shutdown.i_limit); + ip5xxx_setup_reg(boost_load_powerup_en, boost.load_powerup_en); + ip5xxx_setup_reg(boost_vin_pullout_en, boost.vin_pullout_en); + ip5xxx_setup_reg(boost_undervolt_limit, boost.undervolt_limit); + ip5xxx_setup_reg(boost_light_load_status, boost.light_load_status); + ip5xxx_setup_reg(battery_ntc_dis, battery.ntc_dis); + ip5xxx_setup_reg(battery_type, battery.type); + ip5xxx_setup_reg(battery_vset_en, battery.vset_en); + ip5xxx_setup_reg(battery_adc_volt_low, battery.adc.volt.low); + ip5xxx_setup_reg(battery_adc_volt_high, battery.adc.volt.high); + ip5xxx_setup_reg(battery_adc_curr_low, battery.adc.curr.low); + ip5xxx_setup_reg(battery_adc_curr_high, battery.adc.curr.high); + ip5xxx_setup_reg(battery_adc_ovolt_low, battery.adc.open_volt.low); + ip5xxx_setup_reg(battery_adc_ovolt_high, battery.adc.open_volt.high); + ip5xxx_setup_reg(btn_shdn_enable, btn.shdn_enable); + ip5xxx_setup_reg(btn_wled_mode, btn.wled_mode); + ip5xxx_setup_reg(btn_shdn_mode, btn.shdn_mode); + ip5xxx_setup_reg(btn_long_press_time, btn.long_press_time); + ip5xxx_setup_reg(btn_pressed, btn.pressed); + ip5xxx_setup_reg(btn_long_pressed, btn.long_pressed); + ip5xxx_setup_reg(btn_short_pressed, btn.short_pressed); + ip5xxx_setup_reg(wled_enable, wled.enable); + ip5xxx_setup_reg(wled_detect_en, wled.detect_en); + ip5xxx_setup_reg(wled_present, wled.present); + + ip5xxx->vbat_max = cfg->vbat_max; + ip5xxx->boost_undervolt.setpoint = cfg->boost_undervolt_setpoint; + ip5xxx->boost_undervolt.microvolts_per_bit = cfg->boost_undervolt_uv_per_bit; + ip5xxx->const_curr.setpoint = cfg->const_curr_setpoint; + ip5xxx->chg_end_inverted = cfg->chg_end_inverted; +} + static int ip5xxx_power_probe(struct i2c_client *client) { + const struct ip5xxx_regfield_config *fields; struct power_supply_config psy_cfg = {}; struct device *dev = &client->dev; struct power_supply *psy; @@ -601,7 +842,10 @@ static int ip5xxx_power_probe(struct i2c_client *client) if (IS_ERR(ip5xxx->regmap)) return PTR_ERR(ip5xxx->regmap); - psy_cfg.of_node = dev->of_node; + fields = i2c_get_match_data(client) ?: &ip51xx_fields; + ip5xxx_setup_regs(dev, ip5xxx, fields); + + psy_cfg.fwnode = dev_fwnode(dev); psy_cfg.drv_data = ip5xxx; psy = devm_power_supply_register(dev, &ip5xxx_battery_desc, &psy_cfg); @@ -616,10 +860,11 @@ static int ip5xxx_power_probe(struct i2c_client *client) } static const struct of_device_id ip5xxx_power_of_match[] = { - { .compatible = "injoinic,ip5108" }, - { .compatible = "injoinic,ip5109" }, - { .compatible = "injoinic,ip5207" }, - { .compatible = "injoinic,ip5209" }, + { .compatible = "injoinic,ip5108", .data = &ip51xx_fields }, + { .compatible = "injoinic,ip5109", .data = &ip51xx_fields }, + { .compatible = "injoinic,ip5207", .data = &ip51xx_fields }, + { .compatible = "injoinic,ip5209", .data = &ip51xx_fields }, + { .compatible = "injoinic,ip5306", .data = &ip5306_fields }, { } }; MODULE_DEVICE_TABLE(of, ip5xxx_power_of_match); diff --git a/drivers/power/supply/lego_ev3_battery.c b/drivers/power/supply/lego_ev3_battery.c index 9085de0ae1b2..28454de05761 100644 --- a/drivers/power/supply/lego_ev3_battery.c +++ b/drivers/power/supply/lego_ev3_battery.c @@ -23,6 +23,7 @@ #include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include <linux/power_supply.h> +#include <linux/property.h> struct lego_ev3_battery { struct iio_channel *iio_v; @@ -198,7 +199,7 @@ static int lego_ev3_battery_probe(struct platform_device *pdev) batt->v_min = 48000000; } - psy_cfg.of_node = pdev->dev.of_node; + psy_cfg.fwnode = dev_fwnode(&pdev->dev); psy_cfg.drv_data = batt; batt->psy = devm_power_supply_register(dev, &lego_ev3_battery_desc, diff --git a/drivers/power/supply/lt3651-charger.c b/drivers/power/supply/lt3651-charger.c index 8de500ffad95..ebfbdbcb7683 100644 --- a/drivers/power/supply/lt3651-charger.c +++ b/drivers/power/supply/lt3651-charger.c @@ -131,7 +131,7 @@ static int lt3651_charger_probe(struct platform_device *pdev) charger_desc->properties = lt3651_charger_properties; charger_desc->num_properties = ARRAY_SIZE(lt3651_charger_properties); charger_desc->get_property = lt3651_charger_get_property; - psy_cfg.of_node = pdev->dev.of_node; + psy_cfg.fwnode = dev_fwnode(&pdev->dev); psy_cfg.drv_data = lt3651_charger; lt3651_charger->charger = devm_power_supply_register(&pdev->dev, diff --git a/drivers/power/supply/ltc4162-l-charger.c b/drivers/power/supply/ltc4162-l-charger.c index 2e4bc74e1c4a..9dd74fa9552d 100644 --- a/drivers/power/supply/ltc4162-l-charger.c +++ b/drivers/power/supply/ltc4162-l-charger.c @@ -1,9 +1,14 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Driver for Analog Devices (Linear Technology) LTC4162-L charger IC. + * Driver for Analog Devices (Linear Technology) + * LTC4162-L 35V/3.2A Multi-Cell Lithium-Ion Step-Down Battery Charger + * LTC4162-F 35V/3.2A Multi-Cell LiFePO4 Step-Down Battery Charger + * LTC4162-S 35V/3.2A Lead-Acid Step-Down Battery Charger + * LTC4015 35V/3.2A Multichemistry Buck Battery Charger Controller * Copyright (C) 2020, Topic Embedded Products */ +#include <linux/bitfield.h> #include <linux/module.h> #include <linux/delay.h> #include <linux/of.h> @@ -47,6 +52,20 @@ #define LTC4162L_VBAT_FILT 0x47 #define LTC4162L_INPUT_UNDERVOLTAGE_DAC 0x4B +#define LTC4162L_CHEM_MASK GENMASK(11, 8) + +enum ltc4162_chem { + ltc4162_lad, + ltc4162_l42, + ltc4162_l41, + ltc4162_l40, + ltc4162_fad, + ltc4162_ffs, + ltc4162_fst, + ltc4162_sst = 8, + ltc4162_sad, +}; + /* Enumeration as in datasheet. Individual bits are mutually exclusive. */ enum ltc4162l_state { battery_detection = 2048, @@ -75,10 +94,28 @@ enum ltc4162l_charge_status { /* Magic number to write to ARM_SHIP_MODE register */ #define LTC4162L_ARM_SHIP_MODE_MAGIC 21325 +struct ltc4162l_info; + +struct ltc4162l_chip_info { + const char *name; + int (*get_vbat)(struct ltc4162l_info *info, unsigned int reg, + union power_supply_propval *val); + int (*get_vcharge)(struct ltc4162l_info *info, unsigned int reg, + union power_supply_propval *val); + int (*set_vcharge)(struct ltc4162l_info *info, unsigned int reg, + unsigned int value); + int (*get_die_temp)(struct ltc4162l_info *info, + union power_supply_propval *val); + unsigned int ibat_resolution_pv; + unsigned int vin_resolution_uv; + u8 telemetry_mask; +}; + struct ltc4162l_info { struct i2c_client *client; struct regmap *regmap; struct power_supply *charger; + const struct ltc4162l_chip_info *chip_info; u32 rsnsb; /* Series resistor that sets charge current, microOhm */ u32 rsnsi; /* Series resistor to measure input current, microOhm */ u8 cell_count; /* Number of connected cells, 0 while unknown */ @@ -108,6 +145,18 @@ static u8 ltc4162l_get_cell_count(struct ltc4162l_info *info) return val; }; +static u8 ltc4162l_get_chem_type(struct ltc4162l_info *info) +{ + int ret; + unsigned int val; + + ret = regmap_read(info->regmap, LTC4162L_CHEM_CELLS_REG, &val); + if (ret) + return ret; + + return FIELD_GET(LTC4162L_CHEM_MASK, val); +}; + /* Convert enum value to POWER_SUPPLY_STATUS value */ static int ltc4162l_state_decode(enum ltc4162l_state value) { @@ -223,25 +272,83 @@ static int ltc4162l_get_vbat(struct ltc4162l_info *info, unsigned int reg, union power_supply_propval *val) { - unsigned int regval; + unsigned int regval, chem_type; int ret; ret = regmap_read(info->regmap, reg, ®val); if (ret) return ret; - /* cell_count × 192.4μV/LSB */ - regval *= 1924; - regval *= ltc4162l_get_cell_count(info); - regval /= 10; - val->intval = regval; + /* + * cell_count × scaling factor + * For ltc4162-s, it uses a cell_count value of 2 for each group of 3 + * physical (2V) cells, thus will return 2, 4, 6, 8 for 6V, 12V, 18V, + * and 24V respectively, and has to divide by 2 to multiply the scale + * factor by 1, 2, 3, or 4 to represent a 6V, 12V, 18V, or 24V battery + * respectively. + */ + chem_type = ltc4162l_get_chem_type(info); + switch (chem_type) { + case ltc4162_lad ... ltc4162_fst: + regval *= 1924; + regval *= ltc4162l_get_cell_count(info); + regval /= 10; + val->intval = regval; - return 0; + return 0; + case ltc4162_sst ... ltc4162_sad: + regval *= 3848; + regval *= ltc4162l_get_cell_count(info) / 2; + regval /= 10; + val->intval = regval; + + return 0; + default: + return -EINVAL; + } +} + +static int ltc4015_get_vbat(struct ltc4162l_info *info, + unsigned int reg, + union power_supply_propval *val) +{ + unsigned int regval, chem_type; + int ret; + + ret = regmap_read(info->regmap, reg, ®val); + if (ret) + return ret; + + /* + * cell count x scaling factor + * ltc4015 lead-acid fixed and lead-acid programmable corresponds to + * 0x7 and 0x8 chem respectively + */ + chem_type = ltc4162l_get_chem_type(info); + switch (chem_type) { + case ltc4162_lad ... ltc4162_fst: + regval *= 192264; + regval *= ltc4162l_get_cell_count(info); + regval /= 1000; + val->intval = regval; + + return 0; + case ltc4162_sst - 1 ... ltc4162_sad - 1: + regval *= 128176; + regval *= ltc4162l_get_cell_count(info); + regval /= 1000; + val->intval = regval; + + return 0; + default: + return -EINVAL; + } } static int ltc4162l_get_ibat(struct ltc4162l_info *info, union power_supply_propval *val) { + const struct ltc4162l_chip_info *chip_info = info->chip_info; unsigned int regval; int ret; @@ -249,9 +356,8 @@ static int ltc4162l_get_ibat(struct ltc4162l_info *info, if (ret) return ret; - /* Signed 16-bit number, 1.466μV / RSNSB amperes/LSB. */ ret = (s16)(regval & 0xFFFF); - val->intval = 100 * mult_frac(ret, 14660, (int)info->rsnsb); + val->intval = mult_frac(ret, chip_info->ibat_resolution_pv, info->rsnsb); return 0; } @@ -260,6 +366,7 @@ static int ltc4162l_get_ibat(struct ltc4162l_info *info, static int ltc4162l_get_input_voltage(struct ltc4162l_info *info, union power_supply_propval *val) { + const struct ltc4162l_chip_info *chip_info = info->chip_info; unsigned int regval; int ret; @@ -267,8 +374,7 @@ static int ltc4162l_get_input_voltage(struct ltc4162l_info *info, if (ret) return ret; - /* 1.649mV/LSB */ - val->intval = regval * 1694; + val->intval = regval * chip_info->vin_resolution_uv; return 0; } @@ -276,6 +382,7 @@ static int ltc4162l_get_input_voltage(struct ltc4162l_info *info, static int ltc4162l_get_input_current(struct ltc4162l_info *info, union power_supply_propval *val) { + const struct ltc4162l_chip_info *chip_info = info->chip_info; unsigned int regval; int ret; @@ -283,11 +390,9 @@ static int ltc4162l_get_input_current(struct ltc4162l_info *info, if (ret) return ret; - /* Signed 16-bit number, 1.466μV / RSNSI amperes/LSB. */ ret = (s16)(regval & 0xFFFF); - ret *= 14660; + ret *= chip_info->ibat_resolution_pv; ret /= info->rsnsi; - ret *= 100; val->intval = ret; @@ -305,7 +410,7 @@ static int ltc4162l_get_icharge(struct ltc4162l_info *info, if (ret) return ret; - regval &= BIT(6) - 1; /* Only the lower 5 bits */ + regval &= GENMASK(5, 0); /* The charge current servo level: (icharge_dac + 1) × 1mV/RSNSB */ ++regval; @@ -336,7 +441,7 @@ static int ltc4162l_get_vcharge(struct ltc4162l_info *info, unsigned int reg, union power_supply_propval *val) { - unsigned int regval; + unsigned int regval, chem_type; int ret; u32 voltage; @@ -344,41 +449,181 @@ static int ltc4162l_get_vcharge(struct ltc4162l_info *info, if (ret) return ret; - regval &= BIT(6) - 1; /* Only the lower 5 bits */ + regval &= GENMASK(5, 0); /* * charge voltage setting can be computed from - * cell_count × (vcharge_setting × 12.5mV + 3.8125V) - * where vcharge_setting ranges from 0 to 31 (4.2V max). + * cell_count × (vcharge_setting × a + b) + * where vcharge_setting ranges from 0 to c (d). + * for ltc4162l: a = 12.5mV , b = 3.8125V, c = 31, d = 4.2Vmax + * for ltc4162f: a = 12.5mV , b = 3.4125V, c = 31, d = 3.8Vmax + * + * for ltc4162s, the charge voltage setting can be computed from + * N x (vcharge_setting x 28.571mV + 6.0V) + * where N is 1, 2, 3, or 4 for 6V, 12V, 18V, or 24V battery respectively, + * and vcharge_setting ranges from 0 to 31 */ - voltage = 3812500 + (regval * 12500); - voltage *= ltc4162l_get_cell_count(info); - val->intval = voltage; + chem_type = ltc4162l_get_chem_type(info); + switch (chem_type) { + case ltc4162_lad ... ltc4162_l40: + voltage = 3812500 + (regval * 12500); + voltage *= ltc4162l_get_cell_count(info); + val->intval = voltage; - return 0; + return 0; + case ltc4162_fad ... ltc4162_fst: + voltage = 3412500 + (regval * 12500); + voltage *= ltc4162l_get_cell_count(info); + val->intval = voltage; + + return 0; + case ltc4162_sst ... ltc4162_sad: + voltage = 6000000 + (regval * 28571); + voltage *= ltc4162l_get_cell_count(info) / 2; + val->intval = voltage; + + return 0; + default: + return -EINVAL; + } } -static int ltc4162l_set_vcharge(struct ltc4162l_info *info, - unsigned int reg, - unsigned int value) +static int ltc4015_get_vcharge(struct ltc4162l_info *info, + unsigned int reg, + union power_supply_propval *val) { - u8 cell_count = ltc4162l_get_cell_count(info); + unsigned int regval, chem_type; + int ret; + u32 voltage; + + ret = regmap_read(info->regmap, reg, ®val); + if (ret) + return ret; - if (!cell_count) - return -EBUSY; /* Not available yet, try again later */ + regval &= GENMASK(5, 0); + /* + * charge voltage setting can be computed from: + * cell_count × (vcharge_setting × a + b) + * where vcharge_setting ranges from 0 to c (d). + * Li-Ion: a = 1/80V, b = 3.8125V, c = 31, d = 4.2Vmax + * LiFePO4: a = 1/80V, b = 3.4125V, c = 31, d = 3.8Vmax + * Lead Acid: a = 1/105V, b = 2V, c = 35, d = 2.6Vmax + */ + chem_type = ltc4162l_get_chem_type(info); + switch (chem_type) { + case ltc4162_lad ... ltc4162_l40: + voltage = 3812500 + (regval * 12500); + voltage *= ltc4162l_get_cell_count(info); + val->intval = voltage; + + return 0; + case ltc4162_fad ... ltc4162_fst: + voltage = 3412500 + (regval * 12500); + voltage *= ltc4162l_get_cell_count(info); + val->intval = voltage; + + return 0; + case ltc4162_sst - 1 ... ltc4162_sad - 1: + voltage = 2000000 + mult_frac(regval, 1000000, 105); + voltage *= ltc4162l_get_cell_count(info); + val->intval = voltage; + + return 0; + default: + return -EINVAL; + } +} + +static int ltc4162l_vcharge(unsigned int base_voltage, + unsigned int scale_factor, + unsigned int range, + unsigned int value, + u8 cell_count) +{ value /= cell_count; - if (value < 3812500) + if (value < base_voltage) return -EINVAL; - value -= 3812500; - value /= 12500; + value -= base_voltage; + value /= scale_factor; - if (value > 31) + if (value > range) return -EINVAL; - return regmap_write(info->regmap, reg, value); + return value; +} + +static int ltc4162l_set_vcharge(struct ltc4162l_info *info, + unsigned int reg, + unsigned int value) +{ + unsigned int chem_type; + u8 cell_count; + + chem_type = ltc4162l_get_chem_type(info); + switch (chem_type) { + case ltc4162_lad ... ltc4162_l40: + cell_count = ltc4162l_get_cell_count(info); + if (!cell_count) + return -EBUSY; + + value = ltc4162l_vcharge(3812500, 12500, 31, value, cell_count); + return regmap_write(info->regmap, reg, value); + case ltc4162_fad ... ltc4162_fst: + cell_count = ltc4162l_get_cell_count(info); + if (!cell_count) + return -EBUSY; + + value = ltc4162l_vcharge(3412500, 12500, 31, value, cell_count); + return regmap_write(info->regmap, reg, value); + case ltc4162_sst ... ltc4162_sad: + cell_count = ltc4162l_get_cell_count(info) / 2; + if (!cell_count) + return -EBUSY; + + value = ltc4162l_vcharge(6000000, 28571, 31, value, cell_count); + return regmap_write(info->regmap, reg, value); + default: + return -EINVAL; + } +} + +static int ltc4015_set_vcharge(struct ltc4162l_info *info, + unsigned int reg, + unsigned int value) +{ + unsigned int chem_type; + u8 cell_count; + + chem_type = ltc4162l_get_chem_type(info); + switch (chem_type) { + case ltc4162_lad ... ltc4162_l40: + cell_count = ltc4162l_get_cell_count(info); + if (!cell_count) + return -EBUSY; + + value = ltc4162l_vcharge(3812500, 12500, 31, value, cell_count); + return regmap_write(info->regmap, reg, value); + case ltc4162_fad ... ltc4162_fst: + cell_count = ltc4162l_get_cell_count(info); + if (!cell_count) + return -EBUSY; + + value = ltc4162l_vcharge(3412500, 12500, 31, value, cell_count); + return regmap_write(info->regmap, reg, value); + case ltc4162_sst - 1 ... ltc4162_sad - 1: + cell_count = ltc4162l_get_cell_count(info); + if (!cell_count) + return -EBUSY; + + value = ltc4162l_vcharge(2000000, 1000000 / 105, 35, + value, cell_count); + return regmap_write(info->regmap, reg, value); + default: + return -EINVAL; + } } static int ltc4162l_get_iin_limit_dac(struct ltc4162l_info *info, @@ -391,7 +636,7 @@ static int ltc4162l_get_iin_limit_dac(struct ltc4162l_info *info, if (ret) return ret; - regval &= BIT(6) - 1; /* Only 6 bits */ + regval &= GENMASK(5, 0); /* (iin_limit_dac + 1) × 500μV / RSNSI */ ++regval; @@ -437,9 +682,30 @@ static int ltc4162l_get_die_temp(struct ltc4162l_info *info, return 0; } +static int ltc4015_get_die_temp(struct ltc4162l_info *info, + union power_supply_propval *val) +{ + unsigned int regval; + int ret; + + ret = regmap_read(info->regmap, LTC4162L_DIE_TEMPERATURE, ®val); + if (ret) + return ret; + + /* (die_temp - 12010) / 45.6°C */ + ret = (s16)(regval & 0xFFFF); + ret -= 12010; + ret *= 1000; + ret /= 456; + val->intval = ret; + + return 0; +} + static int ltc4162l_get_term_current(struct ltc4162l_info *info, union power_supply_propval *val) { + const struct ltc4162l_chip_info *chip_info = info->chip_info; unsigned int regval; int ret; @@ -457,10 +723,9 @@ static int ltc4162l_get_term_current(struct ltc4162l_info *info, if (ret) return ret; - /* 1.466μV / RSNSB amperes/LSB */ - regval *= 14660u; + regval *= chip_info->ibat_resolution_pv; regval /= info->rsnsb; - val->intval = 100 * regval; + val->intval = regval; return 0; } @@ -534,10 +799,11 @@ static ssize_t vbat_show(struct device *dev, { struct power_supply *psy = to_power_supply(dev); struct ltc4162l_info *info = power_supply_get_drvdata(psy); + const struct ltc4162l_chip_info *chip_info = info->chip_info; union power_supply_propval val; int ret; - ret = ltc4162l_get_vbat(info, LTC4162L_VBAT, &val); + ret = chip_info->get_vbat(info, LTC4162L_VBAT, &val); if (ret) return ret; @@ -550,10 +816,11 @@ static ssize_t vbat_avg_show(struct device *dev, { struct power_supply *psy = to_power_supply(dev); struct ltc4162l_info *info = power_supply_get_drvdata(psy); + const struct ltc4162l_chip_info *chip_info = info->chip_info; union power_supply_propval val; int ret; - ret = ltc4162l_get_vbat(info, LTC4162L_VBAT_FILT, &val); + ret = chip_info->get_vbat(info, LTC4162L_VBAT_FILT, &val); if (ret) return ret; @@ -589,7 +856,8 @@ static ssize_t force_telemetry_show(struct device *dev, if (ret) return ret; - return sysfs_emit(buf, "%u\n", regval & BIT(2) ? 1 : 0); + return sysfs_emit(buf, "%u\n", regval & + info->chip_info->telemetry_mask ? 1 : 0); } static ssize_t force_telemetry_store(struct device *dev, @@ -607,7 +875,8 @@ static ssize_t force_telemetry_store(struct device *dev, return ret; ret = regmap_update_bits(info->regmap, LTC4162L_CONFIG_BITS_REG, - BIT(2), value ? BIT(2) : 0); + info->chip_info->telemetry_mask, + value ? info->chip_info->telemetry_mask : 0); if (ret < 0) return ret; @@ -681,6 +950,7 @@ static int ltc4162l_get_property(struct power_supply *psy, union power_supply_propval *val) { struct ltc4162l_info *info = power_supply_get_drvdata(psy); + const struct ltc4162l_chip_info *chip_info = info->chip_info; switch (psp) { case POWER_SUPPLY_PROP_STATUS: @@ -702,15 +972,13 @@ static int ltc4162l_get_property(struct power_supply *psy, return ltc4162l_get_icharge(info, LTC4162L_CHARGE_CURRENT_SETTING, val); case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: - return ltc4162l_get_vcharge(info, - LTC4162L_VCHARGE_DAC, val); + return chip_info->get_vcharge(info, LTC4162L_VCHARGE_DAC, val); case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: - return ltc4162l_get_vcharge(info, - LTC4162L_VCHARGE_SETTING, val); + return chip_info->get_vcharge(info, LTC4162L_VCHARGE_SETTING, val); case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: return ltc4162l_get_iin_limit_dac(info, val); case POWER_SUPPLY_PROP_TEMP: - return ltc4162l_get_die_temp(info, val); + return chip_info->get_die_temp(info, val); case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: return ltc4162l_get_term_current(info, val); default: @@ -772,7 +1040,6 @@ static enum power_supply_property ltc4162l_properties[] = { }; static const struct power_supply_desc ltc4162l_desc = { - .name = "ltc4162-l", .type = POWER_SUPPLY_TYPE_MAINS, .properties = ltc4162l_properties, .num_properties = ARRAY_SIZE(ltc4162l_properties), @@ -781,6 +1048,50 @@ static const struct power_supply_desc ltc4162l_desc = { .property_is_writeable = ltc4162l_property_is_writeable, }; +static const struct ltc4162l_chip_info ltc4162l_chip_info = { + .name = "ltc4162-l", + .get_vbat = ltc4162l_get_vbat, + .get_vcharge = ltc4162l_get_vcharge, + .set_vcharge = ltc4162l_set_vcharge, + .get_die_temp = ltc4162l_get_die_temp, + .ibat_resolution_pv = 1466000, + .vin_resolution_uv = 1649, + .telemetry_mask = BIT(2), +}; + +static const struct ltc4162l_chip_info ltc4162f_chip_info = { + .name = "ltc4162-f", + .get_vbat = ltc4162l_get_vbat, + .get_vcharge = ltc4162l_get_vcharge, + .set_vcharge = ltc4162l_set_vcharge, + .get_die_temp = ltc4162l_get_die_temp, + .ibat_resolution_pv = 1466000, + .vin_resolution_uv = 1649, + .telemetry_mask = BIT(2), +}; + +static const struct ltc4162l_chip_info ltc4162s_chip_info = { + .name = "ltc4162-s", + .get_vbat = ltc4162l_get_vbat, + .get_vcharge = ltc4162l_get_vcharge, + .set_vcharge = ltc4162l_set_vcharge, + .get_die_temp = ltc4162l_get_die_temp, + .ibat_resolution_pv = 1466000, + .vin_resolution_uv = 1649, + .telemetry_mask = BIT(2), +}; + +static const struct ltc4162l_chip_info ltc4015_chip_info = { + .name = "ltc4015", + .get_vbat = ltc4015_get_vbat, + .get_vcharge = ltc4015_get_vcharge, + .set_vcharge = ltc4015_set_vcharge, + .get_die_temp = ltc4015_get_die_temp, + .ibat_resolution_pv = 1464870, + .vin_resolution_uv = 1648, + .telemetry_mask = BIT(4), +}; + static bool ltc4162l_is_writeable_reg(struct device *dev, unsigned int reg) { /* all registers up to this one are writeable */ @@ -808,7 +1119,7 @@ static const struct regmap_config ltc4162l_regmap_config = { .writeable_reg = ltc4162l_is_writeable_reg, .volatile_reg = ltc4162l_is_volatile_reg, .max_register = LTC4162L_INPUT_UNDERVOLTAGE_DAC, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static void ltc4162l_clear_interrupts(struct ltc4162l_info *info) @@ -825,6 +1136,8 @@ static int ltc4162l_probe(struct i2c_client *client) struct device *dev = &client->dev; struct ltc4162l_info *info; struct power_supply_config ltc4162l_config = {}; + struct power_supply_desc *desc; + const struct ltc4162l_chip_info *chip_info; u32 value; int ret; @@ -839,6 +1152,12 @@ static int ltc4162l_probe(struct i2c_client *client) info->client = client; i2c_set_clientdata(client, info); + chip_info = i2c_get_match_data(client); + if (!chip_info) + return -ENODEV; + + info->chip_info = chip_info; + info->regmap = devm_regmap_init_i2c(client, <c4162l_regmap_config); if (IS_ERR(info->regmap)) { dev_err(dev, "Failed to initialize register map\n"); @@ -866,12 +1185,19 @@ static int ltc4162l_probe(struct i2c_client *client) if (!device_property_read_u32(dev, "lltc,cell-count", &value)) info->cell_count = value; - ltc4162l_config.of_node = dev->of_node; + ltc4162l_config.fwnode = dev_fwnode(dev); ltc4162l_config.drv_data = info; ltc4162l_config.attr_grp = ltc4162l_attr_groups; - info->charger = devm_power_supply_register(dev, <c4162l_desc, - <c4162l_config); + /* Duplicate the default descriptor to set name based on chip_info. */ + desc = devm_kmemdup(dev, <c4162l_desc, + sizeof(struct power_supply_desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; + + desc->name = chip_info->name; + + info->charger = devm_power_supply_register(dev, desc, <c4162l_config); if (IS_ERR(info->charger)) { dev_err(dev, "Failed to register charger\n"); return PTR_ERR(info->charger); @@ -903,14 +1229,20 @@ static void ltc4162l_alert(struct i2c_client *client, } static const struct i2c_device_id ltc4162l_i2c_id_table[] = { - { "ltc4162-l" }, + { "ltc4015", (kernel_ulong_t)<c4015_chip_info }, + { "ltc4162-f", (kernel_ulong_t)<c4162f_chip_info }, + { "ltc4162-l", (kernel_ulong_t)<c4162l_chip_info }, + { "ltc4162-s", (kernel_ulong_t)<c4162s_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, ltc4162l_i2c_id_table); static const struct of_device_id ltc4162l_of_match[] __maybe_unused = { - { .compatible = "lltc,ltc4162-l", }, - { }, + { .compatible = "lltc,ltc4015", .data = <c4015_chip_info }, + { .compatible = "lltc,ltc4162-f", .data = <c4162f_chip_info }, + { .compatible = "lltc,ltc4162-l", .data = <c4162l_chip_info }, + { .compatible = "lltc,ltc4162-s", .data = <c4162s_chip_info }, + { } }; MODULE_DEVICE_TABLE(of, ltc4162l_of_match); diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c index 51310f6e4803..c1640bc6accd 100644 --- a/drivers/power/supply/max17040_battery.c +++ b/drivers/power/supply/max17040_battery.c @@ -410,8 +410,9 @@ static int max17040_get_property(struct power_supply *psy, if (!chip->channel_temp) return -ENODATA; - iio_read_channel_processed_scale(chip->channel_temp, - &val->intval, 10); + iio_read_channel_processed(chip->channel_temp, &val->intval); + val->intval /= 100; /* Convert from milli- to deci-degree */ + break; default: return -EINVAL; diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index 496c3e1f2ee6..acea176101fa 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -16,6 +16,7 @@ #include <linux/i2c.h> #include <linux/delay.h> #include <linux/interrupt.h> +#include <linux/platform_device.h> #include <linux/pm.h> #include <linux/mod_devicetable.h> #include <linux/power_supply.h> @@ -52,13 +53,14 @@ #define MAX17042_VMAX_TOLERANCE 50 /* 50 mV */ struct max17042_chip { - struct i2c_client *client; + struct device *dev; struct regmap *regmap; struct power_supply *battery; enum max170xx_chip_type chip_type; struct max17042_platform_data *pdata; struct work_struct work; int init_complete; + int irq; }; static enum power_supply_property max17042_battery_props[] = { @@ -573,11 +575,11 @@ static inline int max17042_model_data_compare(struct max17042_chip *chip, int i; if (memcmp(data1, data2, size)) { - dev_err(&chip->client->dev, "%s compare failed\n", __func__); + dev_err(chip->dev, "%s compare failed\n", __func__); for (i = 0; i < size; i++) - dev_info(&chip->client->dev, "0x%x, 0x%x", + dev_info(chip->dev, "0x%x, 0x%x", data1[i], data2[i]); - dev_info(&chip->client->dev, "\n"); + dev_info(chip->dev, "\n"); return -EINVAL; } return 0; @@ -812,14 +814,14 @@ static int max17042_init_chip(struct max17042_chip *chip) /* write cell characterization data */ ret = max17042_init_model(chip); if (ret) { - dev_err(&chip->client->dev, "%s init failed\n", + dev_err(chip->dev, "%s init failed\n", __func__); return -EIO; } ret = max17042_verify_model_lock(chip); if (ret) { - dev_err(&chip->client->dev, "%s lock verify failed\n", + dev_err(chip->dev, "%s lock verify failed\n", __func__); return -EIO; } @@ -875,7 +877,7 @@ static irqreturn_t max17042_thread_handler(int id, void *dev) return IRQ_HANDLED; if ((val & STATUS_SMN_BIT) || (val & STATUS_SMX_BIT)) { - dev_dbg(&chip->client->dev, "SOC threshold INTR\n"); + dev_dbg(chip->dev, "SOC threshold INTR\n"); max17042_set_soc_threshold(chip, 1); } @@ -907,7 +909,7 @@ static void max17042_init_worker(struct work_struct *work) static struct max17042_platform_data * max17042_get_of_pdata(struct max17042_chip *chip) { - struct device *dev = &chip->client->dev; + struct device *dev = chip->dev; struct device_node *np = dev->of_node; u32 prop; struct max17042_platform_data *pdata; @@ -949,7 +951,7 @@ static struct max17042_reg_data max17047_default_pdata_init_regs[] = { static struct max17042_platform_data * max17042_get_default_pdata(struct max17042_chip *chip) { - struct device *dev = &chip->client->dev; + struct device *dev = chip->dev; struct max17042_platform_data *pdata; int ret, misc_cfg; @@ -990,7 +992,7 @@ max17042_get_default_pdata(struct max17042_chip *chip) static struct max17042_platform_data * max17042_get_pdata(struct max17042_chip *chip) { - struct device *dev = &chip->client->dev; + struct device *dev = chip->dev; #ifdef CONFIG_OF if (dev->of_node) @@ -1003,6 +1005,7 @@ max17042_get_pdata(struct max17042_chip *chip) } static const struct regmap_config max17042_regmap_config = { + .name = "max17042", .reg_bits = 8, .val_bits = 16, .val_format_endian = REGMAP_ENDIAN_NATIVE, @@ -1029,14 +1032,12 @@ static const struct power_supply_desc max17042_no_current_sense_psy_desc = { .num_properties = ARRAY_SIZE(max17042_battery_props) - 2, }; -static int max17042_probe(struct i2c_client *client) +static int max17042_probe(struct i2c_client *client, struct device *dev, int irq, + enum max170xx_chip_type chip_type) { - const struct i2c_device_id *id = i2c_client_get_device_id(client); struct i2c_adapter *adapter = client->adapter; const struct power_supply_desc *max17042_desc = &max17042_psy_desc; struct power_supply_config psy_cfg = {}; - const struct acpi_device_id *acpi_id = NULL; - struct device *dev = &client->dev; struct max17042_chip *chip; int ret; int i; @@ -1045,35 +1046,27 @@ static int max17042_probe(struct i2c_client *client) if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) return -EIO; - chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); if (!chip) return -ENOMEM; - chip->client = client; - if (id) { - chip->chip_type = id->driver_data; - } else { - acpi_id = acpi_match_device(dev->driver->acpi_match_table, dev); - if (!acpi_id) - return -ENODEV; - - chip->chip_type = acpi_id->driver_data; - } + chip->dev = dev; + chip->chip_type = chip_type; chip->regmap = devm_regmap_init_i2c(client, &max17042_regmap_config); if (IS_ERR(chip->regmap)) { - dev_err(&client->dev, "Failed to initialize regmap\n"); + dev_err(dev, "Failed to initialize regmap\n"); return -EINVAL; } chip->pdata = max17042_get_pdata(chip); if (!chip->pdata) { - dev_err(&client->dev, "no platform data provided\n"); + dev_err(dev, "no platform data provided\n"); return -EINVAL; } - i2c_set_clientdata(client, chip); + dev_set_drvdata(dev, chip); psy_cfg.drv_data = chip; - psy_cfg.of_node = dev->of_node; + psy_cfg.fwnode = dev_fwnode(dev); /* When current is not measured, * CURRENT_NOW and CURRENT_AVG properties should be invisible. */ @@ -1095,24 +1088,17 @@ static int max17042_probe(struct i2c_client *client) regmap_write(chip->regmap, MAX17042_LearnCFG, 0x0007); } - chip->battery = devm_power_supply_register(&client->dev, max17042_desc, + chip->battery = devm_power_supply_register(dev, max17042_desc, &psy_cfg); if (IS_ERR(chip->battery)) { - dev_err(&client->dev, "failed: power supply register\n"); + dev_err(dev, "failed: power supply register\n"); return PTR_ERR(chip->battery); } - if (client->irq) { - unsigned int flags = IRQF_ONESHOT; - - /* - * On ACPI systems the IRQ may be handled by ACPI-event code, - * so we need to share (if the ACPI code is willing to share). - */ - if (acpi_id) - flags |= IRQF_SHARED | IRQF_PROBE_SHARED; + if (irq) { + unsigned int flags = IRQF_ONESHOT | IRQF_SHARED | IRQF_PROBE_SHARED; - ret = devm_request_threaded_irq(&client->dev, client->irq, + ret = devm_request_threaded_irq(dev, irq, NULL, max17042_thread_handler, flags, chip->battery->desc->name, @@ -1123,18 +1109,20 @@ static int max17042_probe(struct i2c_client *client) CFG_ALRT_BIT_ENBL); max17042_set_soc_threshold(chip, 1); } else { - client->irq = 0; + irq = 0; if (ret != -EBUSY) - dev_err(&client->dev, "Failed to get IRQ\n"); + dev_err(dev, "Failed to get IRQ\n"); } } /* Not able to update the charge threshold when exceeded? -> disable */ - if (!client->irq) + if (!irq) regmap_write(chip->regmap, MAX17042_SALRT_Th, 0xff00); + chip->irq = irq; + regmap_read(chip->regmap, MAX17042_STATUS, &val); if (val & STATUS_POR_BIT) { - ret = devm_work_autocancel(&client->dev, &chip->work, + ret = devm_work_autocancel(dev, &chip->work, max17042_init_worker); if (ret) return ret; @@ -1146,6 +1134,44 @@ static int max17042_probe(struct i2c_client *client) return 0; } +static int max17042_i2c_probe(struct i2c_client *client) +{ + const struct i2c_device_id *id = i2c_client_get_device_id(client); + const struct acpi_device_id *acpi_id = NULL; + struct device *dev = &client->dev; + enum max170xx_chip_type chip_type; + + if (id) { + chip_type = id->driver_data; + } else { + acpi_id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!acpi_id) + return -ENODEV; + + chip_type = acpi_id->driver_data; + } + + return max17042_probe(client, dev, client->irq, chip_type); +} + +static int max17042_platform_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct i2c_client *i2c; + const struct platform_device_id *id; + int irq; + + i2c = to_i2c_client(pdev->dev.parent); + if (!i2c) + return -EINVAL; + + dev->of_node = dev->parent->of_node; + id = platform_get_device_id(pdev); + irq = platform_get_irq(pdev, 0); + + return max17042_probe(i2c, dev, irq, id->driver_data); +} + #ifdef CONFIG_PM_SLEEP static int max17042_suspend(struct device *dev) { @@ -1155,9 +1181,9 @@ static int max17042_suspend(struct device *dev) * disable the irq and enable irq_wake * capability to the interrupt line. */ - if (chip->client->irq) { - disable_irq(chip->client->irq); - enable_irq_wake(chip->client->irq); + if (chip->irq) { + disable_irq(chip->irq); + enable_irq_wake(chip->irq); } return 0; @@ -1167,9 +1193,9 @@ static int max17042_resume(struct device *dev) { struct max17042_chip *chip = dev_get_drvdata(dev); - if (chip->client->irq) { - disable_irq_wake(chip->client->irq); - enable_irq(chip->client->irq); + if (chip->irq) { + disable_irq_wake(chip->irq); + enable_irq(chip->irq); /* re-program the SOC thresholds to 1% change */ max17042_set_soc_threshold(chip, 1); } @@ -1190,12 +1216,28 @@ MODULE_DEVICE_TABLE(acpi, max17042_acpi_match); #endif #ifdef CONFIG_OF -static const struct of_device_id max17042_dt_match[] = { - { .compatible = "maxim,max17042" }, - { .compatible = "maxim,max17047" }, - { .compatible = "maxim,max17050" }, - { .compatible = "maxim,max17055" }, - { .compatible = "maxim,max77849-battery" }, +/* + * Device may be instantiated through parent MFD device and device matching is done + * through platform_device_id. + * + * However if device's DT node contains proper clock compatible and driver is + * built as a module, then the *module* matching will be done trough DT aliases. + * This requires of_device_id table. In the same time this will not change the + * actual *device* matching so do not add .of_match_table. + */ +static const struct of_device_id max17042_dt_match[] __used = { + { .compatible = "maxim,max17042", + .data = (void *) MAXIM_DEVICE_TYPE_MAX17042 }, + { .compatible = "maxim,max17047", + .data = (void *) MAXIM_DEVICE_TYPE_MAX17047 }, + { .compatible = "maxim,max17050", + .data = (void *) MAXIM_DEVICE_TYPE_MAX17050 }, + { .compatible = "maxim,max17055", + .data = (void *) MAXIM_DEVICE_TYPE_MAX17055 }, + { .compatible = "maxim,max77705-battery", + .data = (void *) MAXIM_DEVICE_TYPE_MAX17047 }, + { .compatible = "maxim,max77849-battery", + .data = (void *) MAXIM_DEVICE_TYPE_MAX17047 }, { }, }; MODULE_DEVICE_TABLE(of, max17042_dt_match); @@ -1211,6 +1253,17 @@ static const struct i2c_device_id max17042_id[] = { }; MODULE_DEVICE_TABLE(i2c, max17042_id); +static const struct platform_device_id max17042_platform_id[] = { + { "max17042", MAXIM_DEVICE_TYPE_MAX17042 }, + { "max17047", MAXIM_DEVICE_TYPE_MAX17047 }, + { "max17050", MAXIM_DEVICE_TYPE_MAX17050 }, + { "max17055", MAXIM_DEVICE_TYPE_MAX17055 }, + { "max77705-battery", MAXIM_DEVICE_TYPE_MAX17047 }, + { "max77849-battery", MAXIM_DEVICE_TYPE_MAX17047 }, + { } +}; +MODULE_DEVICE_TABLE(platform, max17042_platform_id); + static struct i2c_driver max17042_i2c_driver = { .driver = { .name = "max17042", @@ -1218,10 +1271,44 @@ static struct i2c_driver max17042_i2c_driver = { .of_match_table = of_match_ptr(max17042_dt_match), .pm = &max17042_pm_ops, }, - .probe = max17042_probe, + .probe = max17042_i2c_probe, .id_table = max17042_id, }; -module_i2c_driver(max17042_i2c_driver); + +static struct platform_driver max17042_platform_driver = { + .driver = { + .name = "max17042", + .acpi_match_table = ACPI_PTR(max17042_acpi_match), + .pm = &max17042_pm_ops, + }, + .probe = max17042_platform_probe, + .id_table = max17042_platform_id, +}; + +static int __init max17042_init(void) +{ + int ret; + + ret = platform_driver_register(&max17042_platform_driver); + if (ret) + return ret; + + ret = i2c_add_driver(&max17042_i2c_driver); + if (ret) { + platform_driver_unregister(&max17042_platform_driver); + return ret; + } + + return 0; +} +module_init(max17042_init); + +static void __exit max17042_exit(void) +{ + i2c_del_driver(&max17042_i2c_driver); + platform_driver_unregister(&max17042_platform_driver); +} +module_exit(max17042_exit); MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); MODULE_DESCRIPTION("MAX17042 Fuel Gauge"); diff --git a/drivers/power/supply/max1720x_battery.c b/drivers/power/supply/max1720x_battery.c index 33105419e242..ea3912fd1de8 100644 --- a/drivers/power/supply/max1720x_battery.c +++ b/drivers/power/supply/max1720x_battery.c @@ -16,6 +16,11 @@ #include <linux/unaligned.h> +/* SBS compliant registers */ +#define MAX172XX_TEMP1 0x34 +#define MAX172XX_INT_TEMP 0x35 +#define MAX172XX_TEMP2 0x3B + /* Nonvolatile registers */ #define MAX1720X_NXTABLE0 0x80 #define MAX1720X_NRSENSE 0xCF /* RSense in 10^-5 Ohm */ @@ -24,11 +29,17 @@ /* ModelGauge m5 */ #define MAX172XX_STATUS 0x00 /* Status */ #define MAX172XX_STATUS_BAT_ABSENT BIT(3) /* Battery absent */ +#define MAX172XX_STATUS_IMX BIT(6) /* Maximum Current Alert Threshold Exceeded */ +#define MAX172XX_STATUS_VMN BIT(8) /* Minimum Voltage Alert Threshold Exceeded */ +#define MAX172XX_STATUS_TMN BIT(9) /* Minimum Temperature Alert Threshold Exceeded */ +#define MAX172XX_STATUS_VMX BIT(12) /* Maximum Voltage Alert Threshold Exceeded */ +#define MAX172XX_STATUS_TMX BIT(13) /* Maximum Temperature Alert Threshold Exceeded */ #define MAX172XX_REPCAP 0x05 /* Average capacity */ #define MAX172XX_REPSOC 0x06 /* Percentage of charge */ #define MAX172XX_TEMP 0x08 /* Temperature */ #define MAX172XX_CURRENT 0x0A /* Actual current */ #define MAX172XX_AVG_CURRENT 0x0B /* Average current */ +#define MAX172XX_FULL_CAP 0x10 /* Calculated full capacity */ #define MAX172XX_TTE 0x11 /* Time to empty */ #define MAX172XX_AVG_TA 0x16 /* Average temperature */ #define MAX172XX_CYCLES 0x17 @@ -108,15 +119,19 @@ static const struct regmap_config max1720x_regmap_cfg = { .val_format_endian = REGMAP_ENDIAN_LITTLE, .rd_table = &max1720x_readable_regs, .volatile_table = &max1720x_volatile_regs, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static const struct regmap_range max1720x_nvmem_allow[] = { + regmap_reg_range(MAX172XX_TEMP1, MAX172XX_INT_TEMP), + regmap_reg_range(MAX172XX_TEMP2, MAX172XX_TEMP2), regmap_reg_range(MAX1720X_NXTABLE0, MAX1720X_NDEVICE_NAME4), }; static const struct regmap_range max1720x_nvmem_deny[] = { - regmap_reg_range(0x00, 0x7F), + regmap_reg_range(0x00, 0x33), + regmap_reg_range(0x36, 0x3A), + regmap_reg_range(0x3C, 0x7F), regmap_reg_range(0xE0, 0xFF), }; @@ -240,6 +255,7 @@ static const struct nvmem_cell_info max1720x_nvmem_cells[] = { }; static const enum power_supply_property max1720x_battery_props[] = { + POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_VOLTAGE_NOW, @@ -250,6 +266,7 @@ static const enum power_supply_property max1720x_battery_props[] = { POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_MANUFACTURER, }; @@ -291,7 +308,7 @@ static int max172xx_temperature_to_ps(unsigned int reg) /* * Calculating current registers resolution: * - * RSense stored in 10^-5 Ohm, so mesaurment voltage must be + * RSense stored in 10^-5 Ohm, so measurement voltage must be * in 10^-11 Volts for get current in uA. * 16 bit current reg fullscale +/-51.2mV is 102400 uV. * So: 102400 / 65535 * 10^5 = 156252 @@ -303,6 +320,43 @@ static int max172xx_current_to_voltage(unsigned int reg) return val * 156252; } +static int max172xx_battery_health(struct max1720x_device_info *info, + unsigned int *health) +{ + unsigned int status; + int ret; + + ret = regmap_read(info->regmap, MAX172XX_STATUS, &status); + if (ret < 0) + return ret; + + if (status & MAX172XX_STATUS_VMN) + *health = POWER_SUPPLY_HEALTH_DEAD; + else if (status & MAX172XX_STATUS_VMX) + *health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + else if (status & MAX172XX_STATUS_TMN) + *health = POWER_SUPPLY_HEALTH_COLD; + else if (status & MAX172XX_STATUS_TMX) + *health = POWER_SUPPLY_HEALTH_OVERHEAT; + else if (status & MAX172XX_STATUS_IMX) + *health = POWER_SUPPLY_HEALTH_OVERCURRENT; + else + *health = POWER_SUPPLY_HEALTH_GOOD; + + /* Clear events which are not self-clearing to detect next events */ + if (status > 0 && status != MAX172XX_STATUS_IMX) { + ret = regmap_set_bits(info->regmap, MAX172XX_STATUS, + MAX172XX_STATUS_VMN | + MAX172XX_STATUS_VMX | + MAX172XX_STATUS_TMN | + MAX172XX_STATUS_TMX); + if (ret < 0) + return ret; + } + + return 0; +} + static int max1720x_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -312,6 +366,10 @@ static int max1720x_battery_get_property(struct power_supply *psy, int ret = 0; switch (psp) { + case POWER_SUPPLY_PROP_HEALTH: + ret = max172xx_battery_health(info, ®_val); + val->intval = reg_val; + break; case POWER_SUPPLY_PROP_PRESENT: /* * POWER_SUPPLY_PROP_PRESENT will always readable via @@ -362,6 +420,10 @@ static int max1720x_battery_get_property(struct power_supply *psy, ret = regmap_read(info->regmap, MAX172XX_AVG_CURRENT, ®_val); val->intval = max172xx_current_to_voltage(reg_val) / info->rsense; break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + ret = regmap_read(info->regmap, MAX172XX_FULL_CAP, ®_val); + val->intval = max172xx_capacity_to_ps(reg_val); + break; case POWER_SUPPLY_PROP_MODEL_NAME: ret = regmap_read(info->regmap, MAX172XX_DEV_NAME, ®_val); reg_val = FIELD_GET(MAX172XX_DEV_NAME_TYPE_MASK, reg_val); @@ -382,6 +444,54 @@ static int max1720x_battery_get_property(struct power_supply *psy, return ret; } +static int max1720x_read_temp(struct device *dev, u8 reg, char *buf) +{ + struct power_supply *psy = dev_get_drvdata(dev); + struct max1720x_device_info *info = power_supply_get_drvdata(psy); + unsigned int val; + int ret; + + ret = regmap_read(info->regmap_nv, reg, &val); + if (ret < 0) + return ret; + + /* + * Temperature in degrees Celsius starting at absolute zero, -273C or + * 0K with an LSb of 0.1C + */ + return sysfs_emit(buf, "%d\n", val - 2730); +} + +static ssize_t temp_ain1_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return max1720x_read_temp(dev, MAX172XX_TEMP1, buf); +} + +static ssize_t temp_ain2_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return max1720x_read_temp(dev, MAX172XX_TEMP2, buf); +} + +static ssize_t temp_int_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return max1720x_read_temp(dev, MAX172XX_INT_TEMP, buf); +} + +static DEVICE_ATTR_RO(temp_ain1); +static DEVICE_ATTR_RO(temp_ain2); +static DEVICE_ATTR_RO(temp_int); + +static struct attribute *max1720x_attrs[] = { + &dev_attr_temp_ain1.attr, + &dev_attr_temp_ain2.attr, + &dev_attr_temp_int.attr, + NULL +}; +ATTRIBUTE_GROUPS(max1720x); + static int max1720x_nvmem_reg_read(void *priv, unsigned int off, void *val, size_t len) { @@ -482,6 +592,7 @@ static int max1720x_probe(struct i2c_client *client) psy_cfg.drv_data = info; psy_cfg.fwnode = dev_fwnode(dev); + psy_cfg.attr_grp = max1720x_groups; i2c_set_clientdata(client, info); info->regmap = devm_regmap_init_i2c(client, &max1720x_regmap_cfg); if (IS_ERR(info->regmap)) diff --git a/drivers/power/supply/max77650-charger.c b/drivers/power/supply/max77650-charger.c index 5f58c0c24b4d..4ae43668992e 100644 --- a/drivers/power/supply/max77650-charger.c +++ b/drivers/power/supply/max77650-charger.c @@ -298,7 +298,7 @@ static int max77650_charger_probe(struct platform_device *pdev) chg->dev = dev; - pscfg.of_node = dev->of_node; + pscfg.fwnode = dev_fwnode(dev); pscfg.drv_data = chg; chg_irq = platform_get_irq_byname(pdev, "CHG"); diff --git a/drivers/power/supply/max77693_charger.c b/drivers/power/supply/max77693_charger.c index cdea35c0d1de..027d6a539b65 100644 --- a/drivers/power/supply/max77693_charger.c +++ b/drivers/power/supply/max77693_charger.c @@ -608,7 +608,7 @@ static int max77693_set_charge_input_threshold_volt(struct max77693_charger *chg case 4700000: case 4800000: case 4900000: - data = (uvolt - 4700000) / 100000; + data = ((uvolt - 4700000) / 100000) + 1; break; default: dev_err(chg->dev, "Wrong value for charge input voltage regulation threshold\n"); diff --git a/drivers/power/supply/max77705_charger.c b/drivers/power/supply/max77705_charger.c new file mode 100644 index 000000000000..329b430d0e50 --- /dev/null +++ b/drivers/power/supply/max77705_charger.c @@ -0,0 +1,589 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Based on max77650-charger.c + * + * Copyright (C) 2025 Dzmitry Sankouski <dsankouski@gmail.org> + * + * Battery charger driver for MAXIM 77705 charger/power-supply. + */ + +#include <linux/devm-helpers.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/mfd/max77693-common.h> +#include <linux/mfd/max77705-private.h> +#include <linux/power/max77705_charger.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/regmap.h> + +static const char *max77705_charger_model = "max77705"; +static const char *max77705_charger_manufacturer = "Maxim Integrated"; + +static const struct regmap_config max77705_chg_regmap_config = { + .reg_base = MAX77705_CHG_REG_BASE, + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX77705_CHG_REG_SAFEOUT_CTRL, +}; + +static enum power_supply_property max77705_charger_props[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, +}; + +static int max77705_chgin_irq(void *irq_drv_data) +{ + struct max77705_charger_data *charger = irq_drv_data; + + queue_work(charger->wqueue, &charger->chgin_work); + + return 0; +} + +static const struct regmap_irq max77705_charger_irqs[] = { + { .mask = MAX77705_BYP_IM, }, + { .mask = MAX77705_INP_LIMIT_IM, }, + { .mask = MAX77705_BATP_IM, }, + { .mask = MAX77705_BAT_IM, }, + { .mask = MAX77705_CHG_IM, }, + { .mask = MAX77705_WCIN_IM, }, + { .mask = MAX77705_CHGIN_IM, }, + { .mask = MAX77705_AICL_IM, }, +}; + +static struct regmap_irq_chip max77705_charger_irq_chip = { + .name = "max77705-charger", + .status_base = MAX77705_CHG_REG_INT, + .mask_base = MAX77705_CHG_REG_INT_MASK, + .handle_post_irq = max77705_chgin_irq, + .num_regs = 1, + .irqs = max77705_charger_irqs, + .num_irqs = ARRAY_SIZE(max77705_charger_irqs), +}; + +static int max77705_charger_enable(struct max77705_charger_data *chg) +{ + int rv; + + rv = regmap_update_bits(chg->regmap, MAX77705_CHG_REG_CNFG_09, + MAX77705_CHG_EN_MASK, MAX77705_CHG_EN_MASK); + if (rv) + dev_err(chg->dev, "unable to enable the charger: %d\n", rv); + + return rv; +} + +static void max77705_charger_disable(void *data) +{ + struct max77705_charger_data *chg = data; + int rv; + + rv = regmap_update_bits(chg->regmap, + MAX77705_CHG_REG_CNFG_09, + MAX77705_CHG_EN_MASK, + MAX77705_CHG_DISABLE); + if (rv) + dev_err(chg->dev, "unable to disable the charger: %d\n", rv); +} + +static int max77705_get_online(struct regmap *regmap, int *val) +{ + unsigned int data; + int ret; + + ret = regmap_read(regmap, MAX77705_CHG_REG_INT_OK, &data); + if (ret < 0) + return ret; + + *val = !!(data & MAX77705_CHGIN_OK); + + return 0; +} + +static int max77705_check_battery(struct max77705_charger_data *charger, int *val) +{ + unsigned int reg_data; + unsigned int reg_data2; + struct regmap *regmap = charger->regmap; + + regmap_read(regmap, MAX77705_CHG_REG_INT_OK, ®_data); + + dev_dbg(charger->dev, "CHG_INT_OK(0x%x)\n", reg_data); + + regmap_read(regmap, MAX77705_CHG_REG_DETAILS_00, ®_data2); + + dev_dbg(charger->dev, "CHG_DETAILS00(0x%x)\n", reg_data2); + + if ((reg_data & MAX77705_BATP_OK) || !(reg_data2 & MAX77705_BATP_DTLS)) + *val = true; + else + *val = false; + + return 0; +} + +static int max77705_get_charge_type(struct max77705_charger_data *charger, int *val) +{ + struct regmap *regmap = charger->regmap; + unsigned int reg_data; + + regmap_read(regmap, MAX77705_CHG_REG_CNFG_09, ®_data); + if (!MAX77705_CHARGER_CHG_CHARGING(reg_data)) { + *val = POWER_SUPPLY_CHARGE_TYPE_NONE; + return 0; + } + + regmap_read(regmap, MAX77705_CHG_REG_DETAILS_01, ®_data); + reg_data &= MAX77705_CHG_DTLS; + + switch (reg_data) { + case 0x0: + case MAX77705_CHARGER_CONSTANT_CURRENT: + case MAX77705_CHARGER_CONSTANT_VOLTAGE: + *val = POWER_SUPPLY_CHARGE_TYPE_FAST; + return 0; + default: + *val = POWER_SUPPLY_CHARGE_TYPE_NONE; + return 0; + } + + return 0; +} + +static int max77705_get_status(struct max77705_charger_data *charger, int *val) +{ + struct regmap *regmap = charger->regmap; + unsigned int reg_data; + + regmap_read(regmap, MAX77705_CHG_REG_CNFG_09, ®_data); + if (!MAX77705_CHARGER_CHG_CHARGING(reg_data)) { + *val = POWER_SUPPLY_CHARGE_TYPE_NONE; + return 0; + } + + regmap_read(regmap, MAX77705_CHG_REG_DETAILS_01, ®_data); + reg_data &= MAX77705_CHG_DTLS; + + switch (reg_data) { + case 0x0: + case MAX77705_CHARGER_CONSTANT_CURRENT: + case MAX77705_CHARGER_CONSTANT_VOLTAGE: + *val = POWER_SUPPLY_STATUS_CHARGING; + return 0; + case MAX77705_CHARGER_END_OF_CHARGE: + case MAX77705_CHARGER_DONE: + *val = POWER_SUPPLY_STATUS_FULL; + return 0; + /* those values hard coded as in vendor kernel, because of */ + /* failure to determine it's actual meaning. */ + case 0x05: + case 0x06: + case 0x07: + *val = POWER_SUPPLY_STATUS_NOT_CHARGING; + return 0; + case 0x08: + case 0xA: + case 0xB: + *val = POWER_SUPPLY_STATUS_DISCHARGING; + return 0; + default: + *val = POWER_SUPPLY_STATUS_UNKNOWN; + return 0; + } + + return 0; +} + +static int max77705_get_vbus_state(struct regmap *regmap, int *value) +{ + int ret; + unsigned int charge_dtls; + + ret = regmap_read(regmap, MAX77705_CHG_REG_DETAILS_00, &charge_dtls); + if (ret) + return ret; + + charge_dtls = ((charge_dtls & MAX77705_CHGIN_DTLS) >> + MAX77705_CHGIN_DTLS_SHIFT); + + switch (charge_dtls) { + case 0x00: + *value = POWER_SUPPLY_HEALTH_UNDERVOLTAGE; + break; + case 0x01: + *value = POWER_SUPPLY_HEALTH_UNDERVOLTAGE; + break; + case 0x02: + *value = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + break; + case 0x03: + *value = POWER_SUPPLY_HEALTH_GOOD; + break; + default: + return 0; + } + return 0; +} + +static int max77705_get_battery_health(struct max77705_charger_data *charger, + int *value) +{ + struct regmap *regmap = charger->regmap; + unsigned int bat_dtls; + + regmap_read(regmap, MAX77705_CHG_REG_DETAILS_01, &bat_dtls); + bat_dtls = ((bat_dtls & MAX77705_BAT_DTLS) >> MAX77705_BAT_DTLS_SHIFT); + + switch (bat_dtls) { + case MAX77705_BATTERY_NOBAT: + dev_dbg(charger->dev, "%s: No battery and the charger is suspended\n", + __func__); + *value = POWER_SUPPLY_HEALTH_NO_BATTERY; + break; + case MAX77705_BATTERY_PREQUALIFICATION: + dev_dbg(charger->dev, "%s: battery is okay but its voltage is low(~VPQLB)\n", + __func__); + break; + case MAX77705_BATTERY_DEAD: + dev_dbg(charger->dev, "%s: battery dead\n", __func__); + *value = POWER_SUPPLY_HEALTH_DEAD; + break; + case MAX77705_BATTERY_GOOD: + case MAX77705_BATTERY_LOWVOLTAGE: + *value = POWER_SUPPLY_HEALTH_GOOD; + break; + case MAX77705_BATTERY_OVERVOLTAGE: + dev_dbg(charger->dev, "%s: battery ovp\n", __func__); + *value = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + break; + default: + dev_dbg(charger->dev, "%s: battery unknown\n", __func__); + *value = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; + break; + } + + return 0; +} + +static int max77705_get_health(struct max77705_charger_data *charger, int *val) +{ + struct regmap *regmap = charger->regmap; + int ret, is_online = 0; + + ret = max77705_get_online(regmap, &is_online); + if (ret) + return ret; + if (is_online) { + ret = max77705_get_vbus_state(regmap, val); + if (ret || (*val != POWER_SUPPLY_HEALTH_GOOD)) + return ret; + } + return max77705_get_battery_health(charger, val); +} + +static int max77705_get_input_current(struct max77705_charger_data *charger, + int *val) +{ + unsigned int reg_data; + int get_current = 0; + struct regmap *regmap = charger->regmap; + + regmap_read(regmap, MAX77705_CHG_REG_CNFG_09, ®_data); + + reg_data &= MAX77705_CHG_CHGIN_LIM_MASK; + + if (reg_data <= 3) + get_current = MAX77705_CURRENT_CHGIN_MIN; + else if (reg_data >= MAX77705_CHG_CHGIN_LIM_MASK) + get_current = MAX77705_CURRENT_CHGIN_MAX; + else + get_current = (reg_data + 1) * MAX77705_CURRENT_CHGIN_STEP; + + *val = get_current; + + return 0; +} + +static int max77705_get_charge_current(struct max77705_charger_data *charger, + int *val) +{ + unsigned int reg_data; + struct regmap *regmap = charger->regmap; + + regmap_read(regmap, MAX77705_CHG_REG_CNFG_02, ®_data); + reg_data &= MAX77705_CHG_CC; + + *val = reg_data <= 0x2 ? MAX77705_CURRENT_CHGIN_MIN : reg_data * MAX77705_CURRENT_CHG_STEP; + + return 0; +} + +static int max77705_set_float_voltage(struct max77705_charger_data *charger, + int float_voltage) +{ + int float_voltage_mv; + unsigned int reg_data = 0; + struct regmap *regmap = charger->regmap; + + float_voltage_mv = float_voltage / 1000; + reg_data = float_voltage_mv <= 4000 ? 0x0 : + float_voltage_mv >= 4500 ? 0x23 : + (float_voltage_mv <= 4200) ? (float_voltage_mv - 4000) / 50 : + (((float_voltage_mv - 4200) / 10) + 0x04); + + return regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_04, + MAX77705_CHG_CV_PRM_MASK, + (reg_data << MAX77705_CHG_CV_PRM_SHIFT)); +} + +static int max77705_get_float_voltage(struct max77705_charger_data *charger, + int *val) +{ + unsigned int reg_data = 0; + int voltage_mv; + struct regmap *regmap = charger->regmap; + + regmap_read(regmap, MAX77705_CHG_REG_CNFG_04, ®_data); + reg_data &= MAX77705_CHG_PRM_MASK; + voltage_mv = reg_data <= 0x04 ? reg_data * 50 + 4000 : + (reg_data - 4) * 10 + 4200; + *val = voltage_mv * 1000; + + return 0; +} + +static int max77705_chg_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct max77705_charger_data *charger = power_supply_get_drvdata(psy); + struct regmap *regmap = charger->regmap; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + return max77705_get_online(regmap, &val->intval); + case POWER_SUPPLY_PROP_PRESENT: + return max77705_check_battery(charger, &val->intval); + case POWER_SUPPLY_PROP_STATUS: + return max77705_get_status(charger, &val->intval); + case POWER_SUPPLY_PROP_CHARGE_TYPE: + return max77705_get_charge_type(charger, &val->intval); + case POWER_SUPPLY_PROP_HEALTH: + return max77705_get_health(charger, &val->intval); + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + return max77705_get_input_current(charger, &val->intval); + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + return max77705_get_charge_current(charger, &val->intval); + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + return max77705_get_float_voltage(charger, &val->intval); + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + val->intval = charger->bat_info->voltage_max_design_uv; + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = max77705_charger_model; + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = max77705_charger_manufacturer; + break; + default: + return -EINVAL; + } + return 0; +} + +static const struct power_supply_desc max77705_charger_psy_desc = { + .name = "max77705-charger", + .type = POWER_SUPPLY_TYPE_USB, + .properties = max77705_charger_props, + .num_properties = ARRAY_SIZE(max77705_charger_props), + .get_property = max77705_chg_get_property, +}; + +static void max77705_chgin_isr_work(struct work_struct *work) +{ + struct max77705_charger_data *charger = + container_of(work, struct max77705_charger_data, chgin_work); + + power_supply_changed(charger->psy_chg); +} + +static void max77705_charger_initialize(struct max77705_charger_data *chg) +{ + u8 reg_data; + struct power_supply_battery_info *info; + struct regmap *regmap = chg->regmap; + + if (power_supply_get_battery_info(chg->psy_chg, &info) < 0) + return; + + chg->bat_info = info; + + /* unlock charger setting protect */ + /* slowest LX slope */ + reg_data = MAX77705_CHGPROT_MASK | MAX77705_SLOWEST_LX_SLOPE; + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_06, reg_data, + reg_data); + + /* fast charge timer disable */ + /* restart threshold disable */ + /* pre-qual charge disable */ + reg_data = (MAX77705_FCHGTIME_DISABLE << MAX77705_FCHGTIME_SHIFT) | + (MAX77705_CHG_RSTRT_DISABLE << MAX77705_CHG_RSTRT_SHIFT) | + (MAX77705_CHG_PQEN_DISABLE << MAX77705_PQEN_SHIFT); + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_01, + (MAX77705_FCHGTIME_MASK | + MAX77705_CHG_RSTRT_MASK | + MAX77705_PQEN_MASK), + reg_data); + + /* OTG off(UNO on), boost off */ + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_00, + MAX77705_OTG_CTRL, 0); + + /* charge current 450mA(default) */ + /* otg current limit 900mA */ + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_02, + MAX77705_OTG_ILIM_MASK, + MAX77705_OTG_ILIM_900 << MAX77705_OTG_ILIM_SHIFT); + + /* BAT to SYS OCP 4.80A */ + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_05, + MAX77705_REG_B2SOVRC_MASK, + MAX77705_B2SOVRC_4_8A << MAX77705_REG_B2SOVRC_SHIFT); + /* top off current 150mA */ + /* top off timer 30min */ + reg_data = (MAX77705_TO_ITH_150MA << MAX77705_TO_ITH_SHIFT) | + (MAX77705_TO_TIME_30M << MAX77705_TO_TIME_SHIFT) | + (MAX77705_SYS_TRACK_DISABLE << MAX77705_SYS_TRACK_DIS_SHIFT); + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_03, + (MAX77705_TO_ITH_MASK | + MAX77705_TO_TIME_MASK | + MAX77705_SYS_TRACK_DIS_MASK), reg_data); + + /* cv voltage 4.2V or 4.35V */ + /* MINVSYS 3.6V(default) */ + if (info->voltage_max_design_uv < 0) { + dev_warn(chg->dev, "missing battery:voltage-max-design-microvolt\n"); + max77705_set_float_voltage(chg, 4200000); + } else { + max77705_set_float_voltage(chg, info->voltage_max_design_uv); + } + + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_12, + MAX77705_VCHGIN_REG_MASK, MAX77705_VCHGIN_4_5); + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_12, + MAX77705_WCIN_REG_MASK, MAX77705_WCIN_4_5); + + /* Watchdog timer */ + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_00, + MAX77705_WDTEN_MASK, 0); + + /* Active Discharge Enable */ + regmap_update_bits(regmap, MAX77705_PMIC_REG_MAINCTRL1, 1, 1); + + /* VBYPSET=5.0V */ + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_11, MAX77705_VBYPSET_MASK, 0); + + /* Switching Frequency : 1.5MHz */ + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_08, MAX77705_REG_FSW_MASK, + (MAX77705_CHG_FSW_1_5MHz << MAX77705_REG_FSW_SHIFT)); + + /* Auto skip mode */ + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_12, MAX77705_REG_DISKIP_MASK, + (MAX77705_AUTO_SKIP << MAX77705_REG_DISKIP_SHIFT)); +} + +static int max77705_charger_probe(struct i2c_client *i2c) +{ + struct power_supply_config pscfg = {}; + struct max77705_charger_data *chg; + struct device *dev; + struct regmap_irq_chip_data *irq_data; + int ret; + + dev = &i2c->dev; + + chg = devm_kzalloc(dev, sizeof(*chg), GFP_KERNEL); + if (!chg) + return -ENOMEM; + + chg->dev = dev; + i2c_set_clientdata(i2c, chg); + + chg->regmap = devm_regmap_init_i2c(i2c, &max77705_chg_regmap_config); + if (IS_ERR(chg->regmap)) + return PTR_ERR(chg->regmap); + + ret = regmap_update_bits(chg->regmap, + MAX77705_CHG_REG_INT_MASK, + MAX77705_CHGIN_IM, 0); + if (ret) + return ret; + + pscfg.fwnode = dev_fwnode(dev); + pscfg.drv_data = chg; + + chg->psy_chg = devm_power_supply_register(dev, &max77705_charger_psy_desc, &pscfg); + if (IS_ERR(chg->psy_chg)) + return PTR_ERR(chg->psy_chg); + + max77705_charger_irq_chip.irq_drv_data = chg; + ret = devm_regmap_add_irq_chip(chg->dev, chg->regmap, i2c->irq, + IRQF_ONESHOT | IRQF_SHARED, 0, + &max77705_charger_irq_chip, + &irq_data); + if (ret) + return dev_err_probe(dev, ret, "failed to add irq chip\n"); + + chg->wqueue = create_singlethread_workqueue(dev_name(dev)); + if (!chg->wqueue) + return dev_err_probe(dev, -ENOMEM, "failed to create workqueue\n"); + + ret = devm_work_autocancel(dev, &chg->chgin_work, max77705_chgin_isr_work); + if (ret) { + dev_err_probe(dev, ret, "failed to initialize interrupt work\n"); + goto destroy_wq; + } + + max77705_charger_initialize(chg); + + ret = max77705_charger_enable(chg); + if (ret) { + dev_err_probe(dev, ret, "failed to enable charge\n"); + goto destroy_wq; + } + + return devm_add_action_or_reset(dev, max77705_charger_disable, chg); + +destroy_wq: + destroy_workqueue(chg->wqueue); + return ret; +} + +static const struct of_device_id max77705_charger_of_match[] = { + { .compatible = "maxim,max77705-charger" }, + { } +}; +MODULE_DEVICE_TABLE(of, max77705_charger_of_match); + +static struct i2c_driver max77705_charger_driver = { + .driver = { + .name = "max77705-charger", + .of_match_table = max77705_charger_of_match, + }, + .probe = max77705_charger_probe, +}; +module_i2c_driver(max77705_charger_driver); + +MODULE_AUTHOR("Dzmitry Sankouski <dsankouski@gmail.com>"); +MODULE_DESCRIPTION("Maxim MAX77705 charger driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/supply/max8903_charger.c b/drivers/power/supply/max8903_charger.c index e65d0141f260..45fbaad6c647 100644 --- a/drivers/power/supply/max8903_charger.c +++ b/drivers/power/supply/max8903_charger.c @@ -349,7 +349,7 @@ static int max8903_probe(struct platform_device *pdev) data->psy_desc.properties = max8903_charger_props; data->psy_desc.num_properties = ARRAY_SIZE(max8903_charger_props); - psy_cfg.of_node = dev->of_node; + psy_cfg.fwnode = dev_fwnode(dev); psy_cfg.drv_data = data; data->psy = devm_power_supply_register(dev, &data->psy_desc, &psy_cfg); diff --git a/drivers/power/supply/mm8013.c b/drivers/power/supply/mm8013.c index 5bcfaeeda3db..93c50cff31bc 100644 --- a/drivers/power/supply/mm8013.c +++ b/drivers/power/supply/mm8013.c @@ -90,7 +90,7 @@ static int mm8013_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct mm8013_chip *chip = psy->drv_data; + struct mm8013_chip *chip = power_supply_get_drvdata(psy); int ret = 0; u32 regval; @@ -274,7 +274,7 @@ static int mm8013_probe(struct i2c_client *client) return dev_err_probe(dev, ret, "MM8013 not found\n"); psy_cfg.drv_data = chip; - psy_cfg.of_node = dev->of_node; + psy_cfg.fwnode = dev_fwnode(dev); psy = devm_power_supply_register(dev, &mm8013_desc, &psy_cfg); if (IS_ERR(psy)) diff --git a/drivers/power/supply/mt6360_charger.c b/drivers/power/supply/mt6360_charger.c index e99e55148976..77747eb51667 100644 --- a/drivers/power/supply/mt6360_charger.c +++ b/drivers/power/supply/mt6360_charger.c @@ -810,7 +810,7 @@ static int mt6360_charger_probe(struct platform_device *pdev) memcpy(&mci->psy_desc, &mt6360_charger_desc, sizeof(mci->psy_desc)); mci->psy_desc.name = dev_name(&pdev->dev); charger_cfg.drv_data = mci; - charger_cfg.of_node = pdev->dev.of_node; + charger_cfg.fwnode = dev_fwnode(&pdev->dev); mci->psy = devm_power_supply_register(&pdev->dev, &mci->psy_desc, &charger_cfg); if (IS_ERR(mci->psy)) diff --git a/drivers/power/supply/mt6370-charger.c b/drivers/power/supply/mt6370-charger.c index ad8793bf997e..98579998b300 100644 --- a/drivers/power/supply/mt6370-charger.c +++ b/drivers/power/supply/mt6370-charger.c @@ -752,7 +752,7 @@ static int mt6370_chg_init_psy(struct mt6370_priv *priv) { struct power_supply_config cfg = { .drv_data = priv, - .of_node = dev_of_node(priv->dev), + .fwnode = dev_fwnode(priv->dev), }; priv->psy = devm_power_supply_register(priv->dev, &mt6370_chg_psy_desc, @@ -772,7 +772,6 @@ static void mt6370_chg_destroy_wq(void *data) { struct workqueue_struct *wq = data; - flush_workqueue(wq); destroy_workqueue(wq); } diff --git a/drivers/power/supply/olpc_battery.c b/drivers/power/supply/olpc_battery.c index 9f60094a5599..b9b607822676 100644 --- a/drivers/power/supply/olpc_battery.c +++ b/drivers/power/supply/olpc_battery.c @@ -527,7 +527,7 @@ static enum power_supply_property olpc_xo15_bat_props[] = { #define EEPROM_SIZE (EEPROM_END - EEPROM_START) static ssize_t olpc_bat_eeprom_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t off, size_t count) + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { uint8_t ec_byte; int ret; @@ -547,13 +547,13 @@ static ssize_t olpc_bat_eeprom_read(struct file *filp, struct kobject *kobj, return count; } -static struct bin_attribute olpc_bat_eeprom = { +static const struct bin_attribute olpc_bat_eeprom = { .attr = { .name = "eeprom", .mode = S_IRUGO, }, .size = EEPROM_SIZE, - .read = olpc_bat_eeprom_read, + .read_new = olpc_bat_eeprom_read, }; /* Allow userspace to see the specific error value pulled from the EC */ @@ -584,15 +584,14 @@ static struct attribute *olpc_bat_sysfs_attrs[] = { NULL }; -static struct bin_attribute *olpc_bat_sysfs_bin_attrs[] = { +static const struct bin_attribute *const olpc_bat_sysfs_bin_attrs[] = { &olpc_bat_eeprom, NULL }; static const struct attribute_group olpc_bat_sysfs_group = { .attrs = olpc_bat_sysfs_attrs, - .bin_attrs = olpc_bat_sysfs_bin_attrs, - + .bin_attrs_new = olpc_bat_sysfs_bin_attrs, }; static const struct attribute_group *olpc_bat_sysfs_groups[] = { @@ -675,7 +674,7 @@ static int olpc_battery_probe(struct platform_device *pdev) /* Ignore the status. It doesn't actually matter */ - ac_psy_cfg.of_node = pdev->dev.of_node; + ac_psy_cfg.fwnode = dev_fwnode(&pdev->dev); ac_psy_cfg.drv_data = data; data->olpc_ac = devm_power_supply_register(&pdev->dev, &olpc_ac_desc, @@ -693,7 +692,7 @@ static int olpc_battery_probe(struct platform_device *pdev) olpc_bat_desc.num_properties = ARRAY_SIZE(olpc_xo1_bat_props); } - bat_psy_cfg.of_node = pdev->dev.of_node; + bat_psy_cfg.fwnode = dev_fwnode(&pdev->dev); bat_psy_cfg.drv_data = data; bat_psy_cfg.attr_grp = olpc_bat_sysfs_groups; diff --git a/drivers/power/supply/pcf50633-charger.c b/drivers/power/supply/pcf50633-charger.c deleted file mode 100644 index 0136bc87b105..000000000000 --- a/drivers/power/supply/pcf50633-charger.c +++ /dev/null @@ -1,466 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* NXP PCF50633 Main Battery Charger Driver - * - * (C) 2006-2008 by Openmoko, Inc. - * Author: Balaji Rao <balajirrao@openmoko.org> - * All rights reserved. - * - * Broken down from monstrous PCF50633 driver mainly by - * Harald Welte, Andy Green and Werner Almesberger - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/init.h> -#include <linux/types.h> -#include <linux/device.h> -#include <linux/sysfs.h> -#include <linux/platform_device.h> -#include <linux/power_supply.h> - -#include <linux/mfd/pcf50633/core.h> -#include <linux/mfd/pcf50633/mbc.h> - -struct pcf50633_mbc { - struct pcf50633 *pcf; - - int adapter_online; - int usb_online; - - struct power_supply *usb; - struct power_supply *adapter; - struct power_supply *ac; -}; - -int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma) -{ - struct pcf50633_mbc *mbc = platform_get_drvdata(pcf->mbc_pdev); - int ret = 0; - u8 bits; - u8 mbcs2, chgmod; - unsigned int mbcc5; - - if (ma >= 1000) { - bits = PCF50633_MBCC7_USB_1000mA; - ma = 1000; - } else if (ma >= 500) { - bits = PCF50633_MBCC7_USB_500mA; - ma = 500; - } else if (ma >= 100) { - bits = PCF50633_MBCC7_USB_100mA; - ma = 100; - } else { - bits = PCF50633_MBCC7_USB_SUSPEND; - ma = 0; - } - - ret = pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC7, - PCF50633_MBCC7_USB_MASK, bits); - if (ret) - dev_err(pcf->dev, "error setting usb curlim to %d mA\n", ma); - else - dev_info(pcf->dev, "usb curlim to %d mA\n", ma); - - /* - * We limit the charging current to be the USB current limit. - * The reason is that on pcf50633, when it enters PMU Standby mode, - * which it does when the device goes "off", the USB current limit - * reverts to the variant default. In at least one common case, that - * default is 500mA. By setting the charging current to be the same - * as the USB limit we set here before PMU standby, we enforce it only - * using the correct amount of current even when the USB current limit - * gets reset to the wrong thing - */ - - if (mbc->pcf->pdata->charger_reference_current_ma) { - mbcc5 = (ma << 8) / mbc->pcf->pdata->charger_reference_current_ma; - if (mbcc5 > 255) - mbcc5 = 255; - pcf50633_reg_write(mbc->pcf, PCF50633_REG_MBCC5, mbcc5); - } - - mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2); - chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK); - - /* If chgmod == BATFULL, setting chgena has no effect. - * Datasheet says we need to set resume instead but when autoresume is - * used resume doesn't work. Clear and set chgena instead. - */ - if (chgmod != PCF50633_MBCS2_MBC_BAT_FULL) - pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1, - PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA); - else { - pcf50633_reg_clear_bits(pcf, PCF50633_REG_MBCC1, - PCF50633_MBCC1_CHGENA); - pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1, - PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA); - } - - power_supply_changed(mbc->usb); - - return ret; -} -EXPORT_SYMBOL_GPL(pcf50633_mbc_usb_curlim_set); - -int pcf50633_mbc_get_status(struct pcf50633 *pcf) -{ - struct pcf50633_mbc *mbc = platform_get_drvdata(pcf->mbc_pdev); - int status = 0; - u8 chgmod; - - if (!mbc) - return 0; - - chgmod = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2) - & PCF50633_MBCS2_MBC_MASK; - - if (mbc->usb_online) - status |= PCF50633_MBC_USB_ONLINE; - if (chgmod == PCF50633_MBCS2_MBC_USB_PRE || - chgmod == PCF50633_MBCS2_MBC_USB_PRE_WAIT || - chgmod == PCF50633_MBCS2_MBC_USB_FAST || - chgmod == PCF50633_MBCS2_MBC_USB_FAST_WAIT) - status |= PCF50633_MBC_USB_ACTIVE; - if (mbc->adapter_online) - status |= PCF50633_MBC_ADAPTER_ONLINE; - if (chgmod == PCF50633_MBCS2_MBC_ADP_PRE || - chgmod == PCF50633_MBCS2_MBC_ADP_PRE_WAIT || - chgmod == PCF50633_MBCS2_MBC_ADP_FAST || - chgmod == PCF50633_MBCS2_MBC_ADP_FAST_WAIT) - status |= PCF50633_MBC_ADAPTER_ACTIVE; - - return status; -} -EXPORT_SYMBOL_GPL(pcf50633_mbc_get_status); - -int pcf50633_mbc_get_usb_online_status(struct pcf50633 *pcf) -{ - struct pcf50633_mbc *mbc = platform_get_drvdata(pcf->mbc_pdev); - - if (!mbc) - return 0; - - return mbc->usb_online; -} -EXPORT_SYMBOL_GPL(pcf50633_mbc_get_usb_online_status); - -static ssize_t -show_chgmode(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct pcf50633_mbc *mbc = dev_get_drvdata(dev); - - u8 mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2); - u8 chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK); - - return sysfs_emit(buf, "%d\n", chgmod); -} -static DEVICE_ATTR(chgmode, S_IRUGO, show_chgmode, NULL); - -static ssize_t -show_usblim(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct pcf50633_mbc *mbc = dev_get_drvdata(dev); - u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) & - PCF50633_MBCC7_USB_MASK; - unsigned int ma; - - if (usblim == PCF50633_MBCC7_USB_1000mA) - ma = 1000; - else if (usblim == PCF50633_MBCC7_USB_500mA) - ma = 500; - else if (usblim == PCF50633_MBCC7_USB_100mA) - ma = 100; - else - ma = 0; - - return sysfs_emit(buf, "%u\n", ma); -} - -static ssize_t set_usblim(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct pcf50633_mbc *mbc = dev_get_drvdata(dev); - unsigned long ma; - int ret; - - ret = kstrtoul(buf, 10, &ma); - if (ret) - return ret; - - pcf50633_mbc_usb_curlim_set(mbc->pcf, ma); - - return count; -} - -static DEVICE_ATTR(usb_curlim, S_IRUGO | S_IWUSR, show_usblim, set_usblim); - -static ssize_t -show_chglim(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct pcf50633_mbc *mbc = dev_get_drvdata(dev); - u8 mbcc5 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC5); - unsigned int ma; - - if (!mbc->pcf->pdata->charger_reference_current_ma) - return -ENODEV; - - ma = (mbc->pcf->pdata->charger_reference_current_ma * mbcc5) >> 8; - - return sysfs_emit(buf, "%u\n", ma); -} - -static ssize_t set_chglim(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct pcf50633_mbc *mbc = dev_get_drvdata(dev); - unsigned long ma; - unsigned int mbcc5; - int ret; - - if (!mbc->pcf->pdata->charger_reference_current_ma) - return -ENODEV; - - ret = kstrtoul(buf, 10, &ma); - if (ret) - return ret; - - mbcc5 = (ma << 8) / mbc->pcf->pdata->charger_reference_current_ma; - if (mbcc5 > 255) - mbcc5 = 255; - pcf50633_reg_write(mbc->pcf, PCF50633_REG_MBCC5, mbcc5); - - return count; -} - -/* - * This attribute allows to change MBC charging limit on the fly - * independently of usb current limit. It also gets set automatically every - * time usb current limit is changed. - */ -static DEVICE_ATTR(chg_curlim, S_IRUGO | S_IWUSR, show_chglim, set_chglim); - -static struct attribute *pcf50633_mbc_sysfs_attrs[] = { - &dev_attr_chgmode.attr, - &dev_attr_usb_curlim.attr, - &dev_attr_chg_curlim.attr, - NULL, -}; - -ATTRIBUTE_GROUPS(pcf50633_mbc_sysfs); - -static void -pcf50633_mbc_irq_handler(int irq, void *data) -{ - struct pcf50633_mbc *mbc = data; - - /* USB */ - if (irq == PCF50633_IRQ_USBINS) { - mbc->usb_online = 1; - } else if (irq == PCF50633_IRQ_USBREM) { - mbc->usb_online = 0; - pcf50633_mbc_usb_curlim_set(mbc->pcf, 0); - } - - /* Adapter */ - if (irq == PCF50633_IRQ_ADPINS) - mbc->adapter_online = 1; - else if (irq == PCF50633_IRQ_ADPREM) - mbc->adapter_online = 0; - - power_supply_changed(mbc->ac); - power_supply_changed(mbc->usb); - power_supply_changed(mbc->adapter); - - if (mbc->pcf->pdata->mbc_event_callback) - mbc->pcf->pdata->mbc_event_callback(mbc->pcf, irq); -} - -static int adapter_get_property(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) -{ - struct pcf50633_mbc *mbc = power_supply_get_drvdata(psy); - int ret = 0; - - switch (psp) { - case POWER_SUPPLY_PROP_ONLINE: - val->intval = mbc->adapter_online; - break; - default: - ret = -EINVAL; - break; - } - return ret; -} - -static int usb_get_property(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) -{ - struct pcf50633_mbc *mbc = power_supply_get_drvdata(psy); - int ret = 0; - u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) & - PCF50633_MBCC7_USB_MASK; - - switch (psp) { - case POWER_SUPPLY_PROP_ONLINE: - val->intval = mbc->usb_online && - (usblim <= PCF50633_MBCC7_USB_500mA); - break; - default: - ret = -EINVAL; - break; - } - return ret; -} - -static int ac_get_property(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) -{ - struct pcf50633_mbc *mbc = power_supply_get_drvdata(psy); - int ret = 0; - u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) & - PCF50633_MBCC7_USB_MASK; - - switch (psp) { - case POWER_SUPPLY_PROP_ONLINE: - val->intval = mbc->usb_online && - (usblim == PCF50633_MBCC7_USB_1000mA); - break; - default: - ret = -EINVAL; - break; - } - return ret; -} - -static enum power_supply_property power_props[] = { - POWER_SUPPLY_PROP_ONLINE, -}; - -static const u8 mbc_irq_handlers[] = { - PCF50633_IRQ_ADPINS, - PCF50633_IRQ_ADPREM, - PCF50633_IRQ_USBINS, - PCF50633_IRQ_USBREM, - PCF50633_IRQ_BATFULL, - PCF50633_IRQ_CHGHALT, - PCF50633_IRQ_THLIMON, - PCF50633_IRQ_THLIMOFF, - PCF50633_IRQ_USBLIMON, - PCF50633_IRQ_USBLIMOFF, - PCF50633_IRQ_LOWSYS, - PCF50633_IRQ_LOWBAT, -}; - -static const struct power_supply_desc pcf50633_mbc_adapter_desc = { - .name = "adapter", - .type = POWER_SUPPLY_TYPE_MAINS, - .properties = power_props, - .num_properties = ARRAY_SIZE(power_props), - .get_property = &adapter_get_property, -}; - -static const struct power_supply_desc pcf50633_mbc_usb_desc = { - .name = "usb", - .type = POWER_SUPPLY_TYPE_USB, - .properties = power_props, - .num_properties = ARRAY_SIZE(power_props), - .get_property = usb_get_property, -}; - -static const struct power_supply_desc pcf50633_mbc_ac_desc = { - .name = "ac", - .type = POWER_SUPPLY_TYPE_MAINS, - .properties = power_props, - .num_properties = ARRAY_SIZE(power_props), - .get_property = ac_get_property, -}; - -static int pcf50633_mbc_probe(struct platform_device *pdev) -{ - struct power_supply_config psy_cfg = {}; - struct power_supply_config usb_psy_cfg; - struct pcf50633_mbc *mbc; - int i; - u8 mbcs1; - - mbc = devm_kzalloc(&pdev->dev, sizeof(*mbc), GFP_KERNEL); - if (!mbc) - return -ENOMEM; - - platform_set_drvdata(pdev, mbc); - mbc->pcf = dev_to_pcf50633(pdev->dev.parent); - - /* Set up IRQ handlers */ - for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++) - pcf50633_register_irq(mbc->pcf, mbc_irq_handlers[i], - pcf50633_mbc_irq_handler, mbc); - - psy_cfg.supplied_to = mbc->pcf->pdata->batteries; - psy_cfg.num_supplicants = mbc->pcf->pdata->num_batteries; - psy_cfg.drv_data = mbc; - - /* Create power supplies */ - mbc->adapter = devm_power_supply_register(&pdev->dev, - &pcf50633_mbc_adapter_desc, - &psy_cfg); - if (IS_ERR(mbc->adapter)) { - dev_err(mbc->pcf->dev, "failed to register adapter\n"); - return PTR_ERR(mbc->adapter); - } - - usb_psy_cfg = psy_cfg; - usb_psy_cfg.attr_grp = pcf50633_mbc_sysfs_groups; - - mbc->usb = devm_power_supply_register(&pdev->dev, - &pcf50633_mbc_usb_desc, - &usb_psy_cfg); - if (IS_ERR(mbc->usb)) { - dev_err(mbc->pcf->dev, "failed to register usb\n"); - return PTR_ERR(mbc->usb); - } - - mbc->ac = devm_power_supply_register(&pdev->dev, - &pcf50633_mbc_ac_desc, - &psy_cfg); - if (IS_ERR(mbc->ac)) { - dev_err(mbc->pcf->dev, "failed to register ac\n"); - return PTR_ERR(mbc->ac); - } - - mbcs1 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS1); - if (mbcs1 & PCF50633_MBCS1_USBPRES) - pcf50633_mbc_irq_handler(PCF50633_IRQ_USBINS, mbc); - if (mbcs1 & PCF50633_MBCS1_ADAPTPRES) - pcf50633_mbc_irq_handler(PCF50633_IRQ_ADPINS, mbc); - - return 0; -} - -static void pcf50633_mbc_remove(struct platform_device *pdev) -{ - struct pcf50633_mbc *mbc = platform_get_drvdata(pdev); - int i; - - /* Remove IRQ handlers */ - for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++) - pcf50633_free_irq(mbc->pcf, mbc_irq_handlers[i]); -} - -static struct platform_driver pcf50633_mbc_driver = { - .driver = { - .name = "pcf50633-mbc", - }, - .probe = pcf50633_mbc_probe, - .remove = pcf50633_mbc_remove, -}; - -module_platform_driver(pcf50633_mbc_driver); - -MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>"); -MODULE_DESCRIPTION("PCF50633 mbc driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:pcf50633-mbc"); diff --git a/drivers/power/supply/pm8916_bms_vm.c b/drivers/power/supply/pm8916_bms_vm.c index 5d0dd842509c..5120be086e6f 100644 --- a/drivers/power/supply/pm8916_bms_vm.c +++ b/drivers/power/supply/pm8916_bms_vm.c @@ -210,7 +210,7 @@ static int pm8916_bms_vm_battery_probe(struct platform_device *pdev) bat->vbat_now = bat->last_ocv; psy_cfg.drv_data = bat; - psy_cfg.of_node = dev->of_node; + psy_cfg.fwnode = dev_fwnode(dev); bat->battery = devm_power_supply_register(dev, &pm8916_bms_vm_battery_psy_desc, &psy_cfg); if (IS_ERR(bat->battery)) diff --git a/drivers/power/supply/pm8916_lbc.c b/drivers/power/supply/pm8916_lbc.c index 6d92e98cbecc..c74b75b1b267 100644 --- a/drivers/power/supply/pm8916_lbc.c +++ b/drivers/power/supply/pm8916_lbc.c @@ -322,7 +322,7 @@ static int pm8916_lbc_charger_probe(struct platform_device *pdev) dev_err_probe(dev, ret, "Error while parsing device tree\n"); psy_cfg.drv_data = chg; - psy_cfg.of_node = dev->of_node; + psy_cfg.fwnode = dev_fwnode(dev); chg->charger = devm_power_supply_register(dev, &pm8916_lbc_charger_psy_desc, &psy_cfg); if (IS_ERR(chg->charger)) diff --git a/drivers/power/supply/power_supply.h b/drivers/power/supply/power_supply.h index 7434a6f24775..8f6a2d44b996 100644 --- a/drivers/power/supply/power_supply.h +++ b/drivers/power/supply/power_supply.h @@ -9,24 +9,55 @@ * Modified: 2004, Oct Szabolcs Gyurko */ +#include <linux/lockdep.h> + struct device; struct device_type; struct power_supply; extern int power_supply_property_is_writeable(struct power_supply *psy, enum power_supply_property psp); +extern bool power_supply_has_property(struct power_supply *psy, + enum power_supply_property psp); +extern bool power_supply_ext_has_property(const struct power_supply_ext *ext, + enum power_supply_property psp); + +struct power_supply_ext_registration { + struct list_head list_head; + const struct power_supply_ext *ext; + struct device *dev; + void *data; +}; + +/* Make sure that the macro is a single expression */ +#define power_supply_for_each_extension(pos, psy) \ + if ( ({ lockdep_assert_held(&(psy)->extensions_sem); 0; }) ) \ + ; \ + else \ + list_for_each_entry(pos, &(psy)->extensions, list_head) \ #ifdef CONFIG_SYSFS extern void __init power_supply_init_attrs(void); extern int power_supply_uevent(const struct device *dev, struct kobj_uevent_env *env); extern const struct attribute_group *power_supply_attr_groups[]; +extern int power_supply_sysfs_add_extension(struct power_supply *psy, + const struct power_supply_ext *ext, + struct device *dev); +extern void power_supply_sysfs_remove_extension(struct power_supply *psy, + const struct power_supply_ext *ext); #else static inline void power_supply_init_attrs(void) {} #define power_supply_attr_groups NULL #define power_supply_uevent NULL +static inline int power_supply_sysfs_add_extension(struct power_supply *psy, + const struct power_supply_ext *ext, + struct device *dev) +{ return 0; } +static inline void power_supply_sysfs_remove_extension(struct power_supply *psy, + const struct power_supply_ext *ext) {} #endif /* CONFIG_SYSFS */ diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index 16085eff0084..33a5bfce4604 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -66,21 +66,19 @@ static bool __power_supply_is_supplied_by(struct power_supply *supplier, return false; } -static int __power_supply_changed_work(struct device *dev, void *data) +static int __power_supply_changed_work(struct power_supply *pst, void *data) { struct power_supply *psy = data; - struct power_supply *pst = dev_get_drvdata(dev); - if (__power_supply_is_supplied_by(psy, pst)) { - if (pst->desc->external_power_changed) - pst->desc->external_power_changed(pst); - } + if (__power_supply_is_supplied_by(psy, pst)) + power_supply_external_power_changed(pst); return 0; } static void power_supply_changed_work(struct work_struct *work) { + int ret; unsigned long flags; struct power_supply *psy = container_of(work, struct power_supply, changed_work); @@ -88,6 +86,16 @@ static void power_supply_changed_work(struct work_struct *work) dev_dbg(&psy->dev, "%s\n", __func__); spin_lock_irqsave(&psy->changed_lock, flags); + + if (unlikely(psy->update_groups)) { + psy->update_groups = false; + spin_unlock_irqrestore(&psy->changed_lock, flags); + ret = sysfs_update_groups(&psy->dev.kobj, power_supply_dev_type.groups); + if (ret) + dev_warn(&psy->dev, "failed to update sysfs groups: %pe\n", ERR_PTR(ret)); + spin_lock_irqsave(&psy->changed_lock, flags); + } + /* * Check 'changed' here to avoid issues due to race between * power_supply_changed() and this routine. In worst case @@ -98,7 +106,7 @@ static void power_supply_changed_work(struct work_struct *work) if (likely(psy->changed)) { psy->changed = false; spin_unlock_irqrestore(&psy->changed_lock, flags); - power_supply_for_each_device(psy, __power_supply_changed_work); + power_supply_for_each_psy(psy, __power_supply_changed_work); power_supply_update_leds(psy); blocking_notifier_call_chain(&power_supply_notifier, PSY_EVENT_PROP_CHANGED, psy); @@ -116,11 +124,29 @@ static void power_supply_changed_work(struct work_struct *work) spin_unlock_irqrestore(&psy->changed_lock, flags); } -int power_supply_for_each_device(void *data, int (*fn)(struct device *dev, void *data)) +struct psy_for_each_psy_cb_data { + int (*fn)(struct power_supply *psy, void *data); + void *data; +}; + +static int psy_for_each_psy_cb(struct device *dev, void *data) +{ + struct psy_for_each_psy_cb_data *cb_data = data; + struct power_supply *psy = dev_to_psy(dev); + + return cb_data->fn(psy, cb_data->data); +} + +int power_supply_for_each_psy(void *data, int (*fn)(struct power_supply *psy, void *data)) { - return class_for_each_device(&power_supply_class, NULL, data, fn); + struct psy_for_each_psy_cb_data cb_data = { + .fn = fn, + .data = data, + }; + + return class_for_each_device(&power_supply_class, NULL, &cb_data, psy_for_each_psy_cb); } -EXPORT_SYMBOL_GPL(power_supply_for_each_device); +EXPORT_SYMBOL_GPL(power_supply_for_each_psy); void power_supply_changed(struct power_supply *psy) { @@ -166,20 +192,19 @@ static void power_supply_deferred_register_work(struct work_struct *work) } #ifdef CONFIG_OF -static int __power_supply_populate_supplied_from(struct device *dev, +static int __power_supply_populate_supplied_from(struct power_supply *epsy, void *data) { struct power_supply *psy = data; - struct power_supply *epsy = dev_get_drvdata(dev); struct device_node *np; int i = 0; do { - np = of_parse_phandle(psy->of_node, "power-supplies", i++); + np = of_parse_phandle(psy->dev.of_node, "power-supplies", i++); if (!np) break; - if (np == epsy->of_node) { + if (np == epsy->dev.of_node) { dev_dbg(&psy->dev, "%s: Found supply : %s\n", psy->desc->name, epsy->desc->name); psy->supplied_from[i-1] = (char *)epsy->desc->name; @@ -197,21 +222,20 @@ static int power_supply_populate_supplied_from(struct power_supply *psy) { int error; - error = power_supply_for_each_device(psy, __power_supply_populate_supplied_from); + error = power_supply_for_each_psy(psy, __power_supply_populate_supplied_from); dev_dbg(&psy->dev, "%s %d\n", __func__, error); return error; } -static int __power_supply_find_supply_from_node(struct device *dev, +static int __power_supply_find_supply_from_node(struct power_supply *epsy, void *data) { struct device_node *np = data; - struct power_supply *epsy = dev_get_drvdata(dev); - /* returning non-zero breaks out of power_supply_for_each_device loop */ - if (epsy->of_node == np) + /* returning non-zero breaks out of power_supply_for_each_psy loop */ + if (epsy->dev.of_node == np) return 1; return 0; @@ -222,16 +246,16 @@ static int power_supply_find_supply_from_node(struct device_node *supply_node) int error; /* - * power_supply_for_each_device() either returns its own errors or values + * power_supply_for_each_psy() either returns its own errors or values * returned by __power_supply_find_supply_from_node(). * * __power_supply_find_supply_from_node() will return 0 (no match) * or 1 (match). * - * We return 0 if power_supply_for_each_device() returned 1, -EPROBE_DEFER if + * We return 0 if power_supply_for_each_psy() returned 1, -EPROBE_DEFER if * it returned 0, or error as returned by it. */ - error = power_supply_for_each_device(supply_node, __power_supply_find_supply_from_node); + error = power_supply_for_each_psy(supply_node, __power_supply_find_supply_from_node); return error ? (error == 1 ? 0 : error) : -EPROBE_DEFER; } @@ -246,13 +270,13 @@ static int power_supply_check_supplies(struct power_supply *psy) return 0; /* No device node found, nothing to do */ - if (!psy->of_node) + if (!psy->dev.of_node) return 0; do { int ret; - np = of_parse_phandle(psy->of_node, "power-supplies", cnt++); + np = of_parse_phandle(psy->dev.of_node, "power-supplies", cnt++); if (!np) break; @@ -316,10 +340,9 @@ struct psy_am_i_supplied_data { unsigned int count; }; -static int __power_supply_am_i_supplied(struct device *dev, void *_data) +static int __power_supply_am_i_supplied(struct power_supply *epsy, void *_data) { union power_supply_propval ret = {0,}; - struct power_supply *epsy = dev_get_drvdata(dev); struct psy_am_i_supplied_data *data = _data; if (__power_supply_is_supplied_by(epsy, data->psy)) { @@ -337,7 +360,7 @@ int power_supply_am_i_supplied(struct power_supply *psy) struct psy_am_i_supplied_data data = { psy, 0 }; int error; - error = power_supply_for_each_device(&data, __power_supply_am_i_supplied); + error = power_supply_for_each_psy(&data, __power_supply_am_i_supplied); dev_dbg(&psy->dev, "%s count %u err %d\n", __func__, data.count, error); @@ -348,10 +371,9 @@ int power_supply_am_i_supplied(struct power_supply *psy) } EXPORT_SYMBOL_GPL(power_supply_am_i_supplied); -static int __power_supply_is_system_supplied(struct device *dev, void *data) +static int __power_supply_is_system_supplied(struct power_supply *psy, void *data) { union power_supply_propval ret = {0,}; - struct power_supply *psy = dev_get_drvdata(dev); unsigned int *count = data; if (!psy->desc->get_property(psy, POWER_SUPPLY_PROP_SCOPE, &ret)) @@ -372,7 +394,7 @@ int power_supply_is_system_supplied(void) int error; unsigned int count = 0; - error = power_supply_for_each_device(&count, __power_supply_is_system_supplied); + error = power_supply_for_each_psy(&count, __power_supply_is_system_supplied); /* * If no system scope power class device was found at all, most probably we @@ -391,9 +413,8 @@ struct psy_get_supplier_prop_data { union power_supply_propval *val; }; -static int __power_supply_get_supplier_property(struct device *dev, void *_data) +static int __power_supply_get_supplier_property(struct power_supply *epsy, void *_data) { - struct power_supply *epsy = dev_get_drvdata(dev); struct psy_get_supplier_prop_data *data = _data; if (__power_supply_is_supplied_by(epsy, data->psy)) @@ -418,7 +439,7 @@ int power_supply_get_property_from_supplier(struct power_supply *psy, * This function is not intended for use with a supply with multiple * suppliers, we simply pick the first supply to report the psp. */ - ret = power_supply_for_each_device(&data, __power_supply_get_supplier_property); + ret = power_supply_for_each_psy(&data, __power_supply_get_supplier_property); if (ret < 0) return ret; if (ret == 0) @@ -428,23 +449,10 @@ int power_supply_get_property_from_supplier(struct power_supply *psy, } EXPORT_SYMBOL_GPL(power_supply_get_property_from_supplier); -int power_supply_set_battery_charged(struct power_supply *psy) -{ - if (atomic_read(&psy->use_cnt) >= 0 && - psy->desc->type == POWER_SUPPLY_TYPE_BATTERY && - psy->desc->set_charged) { - psy->desc->set_charged(psy); - return 0; - } - - return -EINVAL; -} -EXPORT_SYMBOL_GPL(power_supply_set_battery_charged); - static int power_supply_match_device_by_name(struct device *dev, const void *data) { const char *name = data; - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); return strcmp(psy->desc->name, name) == 0; } @@ -467,7 +475,7 @@ struct power_supply *power_supply_get_by_name(const char *name) power_supply_match_device_by_name); if (dev) { - psy = dev_get_drvdata(dev); + psy = dev_to_psy(dev); atomic_inc(&psy->use_cnt); } @@ -524,7 +532,7 @@ struct power_supply *power_supply_get_by_phandle(struct device_node *np, of_node_put(power_supply_np); if (dev) { - psy = dev_get_drvdata(dev); + psy = dev_to_psy(dev); atomic_inc(&psy->use_cnt); } @@ -585,8 +593,8 @@ int power_supply_get_battery_info(struct power_supply *psy, const __be32 *list; u32 min_max[2]; - if (psy->of_node) { - battery_np = of_parse_phandle(psy->of_node, "monitored-battery", 0); + if (psy->dev.of_node) { + battery_np = of_parse_phandle(psy->dev.of_node, "monitored-battery", 0); if (!battery_np) return -ENODEV; @@ -1180,8 +1188,8 @@ bool power_supply_battery_bti_in_range(struct power_supply_battery_info *info, } EXPORT_SYMBOL_GPL(power_supply_battery_bti_in_range); -static bool psy_has_property(const struct power_supply_desc *psy_desc, - enum power_supply_property psp) +static bool psy_desc_has_property(const struct power_supply_desc *psy_desc, + enum power_supply_property psp) { bool found = false; int i; @@ -1196,17 +1204,57 @@ static bool psy_has_property(const struct power_supply_desc *psy_desc, return found; } +bool power_supply_ext_has_property(const struct power_supply_ext *psy_ext, + enum power_supply_property psp) +{ + int i; + + for (i = 0; i < psy_ext->num_properties; i++) + if (psy_ext->properties[i] == psp) + return true; + + return false; +} + +bool power_supply_has_property(struct power_supply *psy, + enum power_supply_property psp) +{ + struct power_supply_ext_registration *reg; + + if (psy_desc_has_property(psy->desc, psp)) + return true; + + if (power_supply_battery_info_has_prop(psy->battery_info, psp)) + return true; + + power_supply_for_each_extension(reg, psy) { + if (power_supply_ext_has_property(reg->ext, psp)) + return true; + } + + return false; +} + int power_supply_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { + struct power_supply_ext_registration *reg; + if (atomic_read(&psy->use_cnt) <= 0) { if (!psy->initialized) return -EAGAIN; return -ENODEV; } - if (psy_has_property(psy->desc, psp)) + scoped_guard(rwsem_read, &psy->extensions_sem) { + power_supply_for_each_extension(reg, psy) { + if (power_supply_ext_has_property(reg->ext, psp)) + return reg->ext->get_property(psy, reg->ext, reg->data, psp, val); + } + } + + if (psy_desc_has_property(psy->desc, psp)) return psy->desc->get_property(psy, psp, val); else if (power_supply_battery_info_has_prop(psy->battery_info, psp)) return power_supply_battery_info_get_prop(psy->battery_info, psp, val); @@ -1219,7 +1267,24 @@ int power_supply_set_property(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *val) { - if (atomic_read(&psy->use_cnt) <= 0 || !psy->desc->set_property) + struct power_supply_ext_registration *reg; + + if (atomic_read(&psy->use_cnt) <= 0) + return -ENODEV; + + scoped_guard(rwsem_read, &psy->extensions_sem) { + power_supply_for_each_extension(reg, psy) { + if (power_supply_ext_has_property(reg->ext, psp)) { + if (reg->ext->set_property) + return reg->ext->set_property(psy, reg->ext, reg->data, + psp, val); + else + return -ENODEV; + } + } + } + + if (!psy->desc->set_property) return -ENODEV; return psy->desc->set_property(psy, psp, val); @@ -1229,7 +1294,22 @@ EXPORT_SYMBOL_GPL(power_supply_set_property); int power_supply_property_is_writeable(struct power_supply *psy, enum power_supply_property psp) { - return psy->desc->property_is_writeable && psy->desc->property_is_writeable(psy, psp); + struct power_supply_ext_registration *reg; + + power_supply_for_each_extension(reg, psy) { + if (power_supply_ext_has_property(reg->ext, psp)) { + if (reg->ext->property_is_writeable) + return reg->ext->property_is_writeable(psy, reg->ext, + reg->data, psp); + else + return 0; + } + } + + if (!psy->desc->property_is_writeable) + return 0; + + return psy->desc->property_is_writeable(psy, psp); } void power_supply_external_power_changed(struct power_supply *psy) @@ -1248,6 +1328,88 @@ int power_supply_powers(struct power_supply *psy, struct device *dev) } EXPORT_SYMBOL_GPL(power_supply_powers); +static int power_supply_update_sysfs_and_hwmon(struct power_supply *psy) +{ + unsigned long flags; + + spin_lock_irqsave(&psy->changed_lock, flags); + psy->update_groups = true; + spin_unlock_irqrestore(&psy->changed_lock, flags); + + power_supply_changed(psy); + + power_supply_remove_hwmon_sysfs(psy); + return power_supply_add_hwmon_sysfs(psy); +} + +int power_supply_register_extension(struct power_supply *psy, const struct power_supply_ext *ext, + struct device *dev, void *data) +{ + struct power_supply_ext_registration *reg; + size_t i; + int ret; + + if (!psy || !dev || !ext || !ext->name || !ext->properties || !ext->num_properties) + return -EINVAL; + + guard(rwsem_write)(&psy->extensions_sem); + + power_supply_for_each_extension(reg, psy) + if (strcmp(ext->name, reg->ext->name) == 0) + return -EEXIST; + + for (i = 0; i < ext->num_properties; i++) + if (power_supply_has_property(psy, ext->properties[i])) + return -EEXIST; + + reg = kmalloc(sizeof(*reg), GFP_KERNEL); + if (!reg) + return -ENOMEM; + + reg->ext = ext; + reg->dev = dev; + reg->data = data; + list_add(®->list_head, &psy->extensions); + + ret = power_supply_sysfs_add_extension(psy, ext, dev); + if (ret) + goto sysfs_add_failed; + + ret = power_supply_update_sysfs_and_hwmon(psy); + if (ret) + goto sysfs_hwmon_failed; + + return 0; + +sysfs_hwmon_failed: + power_supply_sysfs_remove_extension(psy, ext); +sysfs_add_failed: + list_del(®->list_head); + kfree(reg); + return ret; +} +EXPORT_SYMBOL_GPL(power_supply_register_extension); + +void power_supply_unregister_extension(struct power_supply *psy, const struct power_supply_ext *ext) +{ + struct power_supply_ext_registration *reg; + + guard(rwsem_write)(&psy->extensions_sem); + + power_supply_for_each_extension(reg, psy) { + if (reg->ext == ext) { + list_del(®->list_head); + power_supply_sysfs_remove_extension(psy, ext); + kfree(reg); + power_supply_update_sysfs_and_hwmon(psy); + return; + } + } + + dev_warn(&psy->dev, "Trying to unregister invalid extension"); +} +EXPORT_SYMBOL_GPL(power_supply_unregister_extension); + static void power_supply_dev_release(struct device *dev) { struct power_supply *psy = to_power_supply(dev); @@ -1300,7 +1462,7 @@ static int psy_register_thermal(struct power_supply *psy) return 0; /* Register battery zone device psy reports temperature */ - if (psy_has_property(psy->desc, POWER_SUPPLY_PROP_TEMP)) { + if (psy_desc_has_property(psy->desc, POWER_SUPPLY_PROP_TEMP)) { /* Prefer our hwmon device and avoid duplicates */ struct thermal_zone_params tzp = { .no_hwmon = IS_ENABLED(CONFIG_POWER_SUPPLY_HWMON) @@ -1369,9 +1531,8 @@ __power_supply_register(struct device *parent, if (cfg) { dev->groups = cfg->attr_grp; psy->drv_data = cfg->drv_data; - psy->of_node = + dev->of_node = cfg->fwnode ? to_of_node(cfg->fwnode) : cfg->of_node; - dev->of_node = psy->of_node; psy->supplied_to = cfg->supplied_to; psy->num_supplicants = cfg->num_supplicants; } @@ -1402,6 +1563,9 @@ __power_supply_register(struct device *parent, } spin_lock_init(&psy->changed_lock); + init_rwsem(&psy->extensions_sem); + INIT_LIST_HEAD(&psy->extensions); + rc = device_add(dev); if (rc) goto device_add_failed; @@ -1418,9 +1582,11 @@ __power_supply_register(struct device *parent, if (rc) goto create_triggers_failed; - rc = power_supply_add_hwmon_sysfs(psy); - if (rc) - goto add_hwmon_sysfs_failed; + scoped_guard(rwsem_read, &psy->extensions_sem) { + rc = power_supply_add_hwmon_sysfs(psy); + if (rc) + goto add_hwmon_sysfs_failed; + } /* * Update use_cnt after any uevents (most notably from device_add()). diff --git a/drivers/power/supply/power_supply_hwmon.c b/drivers/power/supply/power_supply_hwmon.c index 01be04903d7d..95245e6a6baa 100644 --- a/drivers/power/supply/power_supply_hwmon.c +++ b/drivers/power/supply/power_supply_hwmon.c @@ -349,9 +349,28 @@ static const struct hwmon_chip_info power_supply_hwmon_chip_info = { .info = power_supply_hwmon_info, }; +static const enum power_supply_property power_supply_hwmon_props[] = { + POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_CURRENT_MAX, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_POWER_AVG, + POWER_SUPPLY_PROP_POWER_NOW, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TEMP_MAX, + POWER_SUPPLY_PROP_TEMP_MIN, + POWER_SUPPLY_PROP_TEMP_ALERT_MIN, + POWER_SUPPLY_PROP_TEMP_ALERT_MAX, + POWER_SUPPLY_PROP_TEMP_AMBIENT, + POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN, + POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX, + POWER_SUPPLY_PROP_VOLTAGE_AVG, + POWER_SUPPLY_PROP_VOLTAGE_MIN, + POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_VOLTAGE_NOW, +}; + int power_supply_add_hwmon_sysfs(struct power_supply *psy) { - const struct power_supply_desc *desc = psy->desc; struct power_supply_hwmon *psyhw; struct device *dev = &psy->dev; struct device *hwmon; @@ -377,32 +396,11 @@ int power_supply_add_hwmon_sysfs(struct power_supply *psy) goto error; } - for (i = 0; i < desc->num_properties; i++) { - const enum power_supply_property prop = desc->properties[i]; - - switch (prop) { - case POWER_SUPPLY_PROP_CURRENT_AVG: - case POWER_SUPPLY_PROP_CURRENT_MAX: - case POWER_SUPPLY_PROP_CURRENT_NOW: - case POWER_SUPPLY_PROP_POWER_AVG: - case POWER_SUPPLY_PROP_POWER_NOW: - case POWER_SUPPLY_PROP_TEMP: - case POWER_SUPPLY_PROP_TEMP_MAX: - case POWER_SUPPLY_PROP_TEMP_MIN: - case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: - case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: - case POWER_SUPPLY_PROP_TEMP_AMBIENT: - case POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN: - case POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX: - case POWER_SUPPLY_PROP_VOLTAGE_AVG: - case POWER_SUPPLY_PROP_VOLTAGE_MIN: - case POWER_SUPPLY_PROP_VOLTAGE_MAX: - case POWER_SUPPLY_PROP_VOLTAGE_NOW: + for (i = 0; i < ARRAY_SIZE(power_supply_hwmon_props); i++) { + const enum power_supply_property prop = power_supply_hwmon_props[i]; + + if (power_supply_has_property(psy, prop)) set_bit(prop, psyhw->props); - break; - default: - break; - } } name = psy->desc->name; diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index 571de43fcca9..439dd0bf8644 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -33,7 +33,7 @@ struct power_supply_attr { [POWER_SUPPLY_PROP_ ## _name] = \ { \ .prop_name = #_name, \ - .attr_name = #_name "\0", \ + .attr_name = #_name, \ .text_values = _text, \ .text_values_len = _len, \ } @@ -99,6 +99,7 @@ static const char * const POWER_SUPPLY_HEALTH_TEXT[] = { [POWER_SUPPLY_HEALTH_OVERHEAT] = "Overheat", [POWER_SUPPLY_HEALTH_DEAD] = "Dead", [POWER_SUPPLY_HEALTH_OVERVOLTAGE] = "Over voltage", + [POWER_SUPPLY_HEALTH_UNDERVOLTAGE] = "Under voltage", [POWER_SUPPLY_HEALTH_UNSPEC_FAILURE] = "Unspecified failure", [POWER_SUPPLY_HEALTH_COLD] = "Cold", [POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE] = "Watchdog timer expire", @@ -182,6 +183,8 @@ static struct power_supply_attr power_supply_attrs[] __ro_after_init = { POWER_SUPPLY_ATTR(CHARGE_CONTROL_START_THRESHOLD), POWER_SUPPLY_ATTR(CHARGE_CONTROL_END_THRESHOLD), POWER_SUPPLY_ENUM_ATTR(CHARGE_BEHAVIOUR), + /* Same enum value texts as "charge_type" without the 's' at the end */ + _POWER_SUPPLY_ENUM_ATTR(CHARGE_TYPES, POWER_SUPPLY_CHARGE_TYPE_TEXT), POWER_SUPPLY_ATTR(INPUT_CURRENT_LIMIT), POWER_SUPPLY_ATTR(INPUT_VOLTAGE_LIMIT), POWER_SUPPLY_ATTR(INPUT_POWER_LIMIT), @@ -237,23 +240,52 @@ static enum power_supply_property dev_attr_psp(struct device_attribute *attr) return to_ps_attr(attr) - power_supply_attrs; } +static void power_supply_escape_spaces(const char *str, char *buf, size_t bufsize) +{ + strscpy(buf, str, bufsize); + strreplace(buf, ' ', '_'); +} + +static int power_supply_match_string(const char * const *array, size_t n, const char *s) +{ + int ret; + + /* First try an exact match */ + ret = __sysfs_match_string(array, n, s); + if (ret >= 0) + return ret; + + /* Second round, try matching with spaces replaced by '_' */ + for (size_t i = 0; i < n; i++) { + char buf[32]; + + power_supply_escape_spaces(array[i], buf, sizeof(buf)); + if (sysfs_streq(buf, s)) + return i; + } + + return -EINVAL; +} + static ssize_t power_supply_show_enum_with_available( struct device *dev, const char * const labels[], int label_count, unsigned int available_values, int value, char *buf) { bool match = false, available, active; + char escaped_label[32]; ssize_t count = 0; int i; for (i = 0; i < label_count; i++) { available = available_values & BIT(i); active = i == value; + power_supply_escape_spaces(labels[i], escaped_label, sizeof(escaped_label)); if (available && active) { - count += sysfs_emit_at(buf, count, "[%s] ", labels[i]); + count += sysfs_emit_at(buf, count, "[%s] ", escaped_label); match = true; } else if (available) { - count += sysfs_emit_at(buf, count, "%s ", labels[i]); + count += sysfs_emit_at(buf, count, "%s ", escaped_label); } } @@ -268,11 +300,34 @@ static ssize_t power_supply_show_enum_with_available( return count; } -static ssize_t power_supply_show_property(struct device *dev, - struct device_attribute *attr, - char *buf) { +static ssize_t power_supply_show_charge_behaviour(struct device *dev, + struct power_supply *psy, + union power_supply_propval *value, + char *buf) +{ + struct power_supply_ext_registration *reg; + + scoped_guard(rwsem_read, &psy->extensions_sem) { + power_supply_for_each_extension(reg, psy) { + if (power_supply_ext_has_property(reg->ext, + POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR)) + return power_supply_charge_behaviour_show(dev, + reg->ext->charge_behaviours, + value->intval, buf); + } + } + + return power_supply_charge_behaviour_show(dev, psy->desc->charge_behaviours, + value->intval, buf); +} + +static ssize_t power_supply_format_property(struct device *dev, + bool uevent, + struct device_attribute *attr, + char *buf) +{ ssize_t ret; - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); const struct power_supply_attr *ps_attr = to_ps_attr(attr); enum power_supply_property psp = dev_attr_psp(attr); union power_supply_propval value; @@ -287,7 +342,7 @@ static ssize_t power_supply_show_property(struct device *dev, dev_dbg_ratelimited(dev, "driver has no data for `%s' property\n", attr->attr.name); - else if (ret != -ENODEV && ret != -EAGAIN) + else if (ret != -ENODEV && ret != -EAGAIN && ret != -EINVAL) dev_err_ratelimited(dev, "driver failed to report `%s' property: %zd\n", attr->attr.name, ret); @@ -303,13 +358,21 @@ static ssize_t power_supply_show_property(struct device *dev, psy->desc->usb_types, value.intval, buf); break; case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: - ret = power_supply_charge_behaviour_show(dev, psy->desc->charge_behaviours, - value.intval, buf); + if (uevent) /* no possible values in uevents */ + goto default_format; + ret = power_supply_show_charge_behaviour(dev, psy, &value, buf); + break; + case POWER_SUPPLY_PROP_CHARGE_TYPES: + if (uevent) /* no possible values in uevents */ + goto default_format; + ret = power_supply_charge_types_show(dev, psy->desc->charge_types, + value.intval, buf); break; case POWER_SUPPLY_PROP_MODEL_NAME ... POWER_SUPPLY_PROP_SERIAL_NUMBER: ret = sysfs_emit(buf, "%s\n", value.strval); break; default: +default_format: if (ps_attr->text_values_len > 0 && value.intval < ps_attr->text_values_len && value.intval >= 0) { ret = sysfs_emit(buf, "%s\n", ps_attr->text_values[value.intval]); @@ -321,19 +384,26 @@ static ssize_t power_supply_show_property(struct device *dev, return ret; } +static ssize_t power_supply_show_property(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return power_supply_format_property(dev, false, attr, buf); +} + static ssize_t power_supply_store_property(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { ssize_t ret; - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); const struct power_supply_attr *ps_attr = to_ps_attr(attr); enum power_supply_property psp = dev_attr_psp(attr); union power_supply_propval value; ret = -EINVAL; if (ps_attr->text_values_len > 0) { - ret = __sysfs_match_string(ps_attr->text_values, - ps_attr->text_values_len, buf); + ret = power_supply_match_string(ps_attr->text_values, + ps_attr->text_values_len, buf); } /* @@ -364,9 +434,8 @@ static umode_t power_supply_attr_is_visible(struct kobject *kobj, int attrno) { struct device *dev = kobj_to_dev(kobj); - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); umode_t mode = S_IRUSR | S_IRGRP | S_IROTH; - int i; if (!power_supply_attrs[attrno].prop_name) return 0; @@ -374,19 +443,13 @@ static umode_t power_supply_attr_is_visible(struct kobject *kobj, if (attrno == POWER_SUPPLY_PROP_TYPE) return mode; - for (i = 0; i < psy->desc->num_properties; i++) { - int property = psy->desc->properties[i]; - - if (property == attrno) { - if (power_supply_property_is_writeable(psy, property) > 0) - mode |= S_IWUSR; + guard(rwsem_read)(&psy->extensions_sem); - return mode; - } - } - - if (power_supply_battery_info_has_prop(psy->battery_info, attrno)) + if (power_supply_has_property(psy, attrno)) { + if (power_supply_property_is_writeable(psy, attrno) > 0) + mode |= S_IWUSR; return mode; + } return 0; } @@ -396,8 +459,18 @@ static const struct attribute_group power_supply_attr_group = { .is_visible = power_supply_attr_is_visible, }; +static struct attribute *power_supply_extension_attrs[] = { + NULL +}; + +static const struct attribute_group power_supply_extension_group = { + .name = "extensions", + .attrs = power_supply_extension_attrs, +}; + const struct attribute_group *power_supply_attr_groups[] = { &power_supply_attr_group, + &power_supply_extension_group, NULL }; @@ -437,8 +510,8 @@ static int add_prop_uevent(const struct device *dev, struct kobj_uevent_env *env pwr_attr = &power_supply_attrs[prop]; dev_attr = &pwr_attr->dev_attr; - ret = power_supply_show_property((struct device *)dev, dev_attr, prop_buf); - if (ret == -ENODEV || ret == -ENODATA) { + ret = power_supply_format_property((struct device *)dev, true, dev_attr, prop_buf); + if (ret == -ENODEV || ret == -ENODATA || ret == -EINVAL) { /* * When a battery is absent, we expect -ENODEV. Don't abort; * send the uevent with at least the PRESENT=0 property @@ -459,11 +532,7 @@ static int add_prop_uevent(const struct device *dev, struct kobj_uevent_env *env int power_supply_uevent(const struct device *dev, struct kobj_uevent_env *env) { - const struct power_supply *psy = dev_get_drvdata(dev); - const enum power_supply_property *battery_props = - power_supply_battery_info_properties; - unsigned long psy_drv_properties[POWER_SUPPLY_ATTR_CNT / - sizeof(unsigned long) + 1] = {0}; + const struct power_supply *psy = dev_to_psy(dev); int ret = 0, j; char *prop_buf; @@ -491,22 +560,8 @@ int power_supply_uevent(const struct device *dev, struct kobj_uevent_env *env) if (ret) goto out; - for (j = 0; j < psy->desc->num_properties; j++) { - set_bit(psy->desc->properties[j], psy_drv_properties); - ret = add_prop_uevent(dev, env, psy->desc->properties[j], - prop_buf); - if (ret) - goto out; - } - - for (j = 0; j < power_supply_battery_info_properties_size; j++) { - if (test_bit(battery_props[j], psy_drv_properties)) - continue; - if (!power_supply_battery_info_has_prop(psy->battery_info, - battery_props[j])) - continue; - ret = add_prop_uevent(dev, env, battery_props[j], - prop_buf); + for (j = 0; j < POWER_SUPPLY_ATTR_CNT; j++) { + ret = add_prop_uevent(dev, env, j, prop_buf); if (ret) goto out; } @@ -542,3 +597,44 @@ int power_supply_charge_behaviour_parse(unsigned int available_behaviours, const return -EINVAL; } EXPORT_SYMBOL_GPL(power_supply_charge_behaviour_parse); + +ssize_t power_supply_charge_types_show(struct device *dev, + unsigned int available_types, + enum power_supply_charge_type current_type, + char *buf) +{ + return power_supply_show_enum_with_available( + dev, POWER_SUPPLY_CHARGE_TYPE_TEXT, + ARRAY_SIZE(POWER_SUPPLY_CHARGE_TYPE_TEXT), + available_types, current_type, buf); +} +EXPORT_SYMBOL_GPL(power_supply_charge_types_show); + +int power_supply_charge_types_parse(unsigned int available_types, const char *buf) +{ + int i = power_supply_match_string(POWER_SUPPLY_CHARGE_TYPE_TEXT, + ARRAY_SIZE(POWER_SUPPLY_CHARGE_TYPE_TEXT), + buf); + + if (i < 0) + return i; + + if (available_types & BIT(i)) + return i; + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(power_supply_charge_types_parse); + +int power_supply_sysfs_add_extension(struct power_supply *psy, const struct power_supply_ext *ext, + struct device *dev) +{ + return sysfs_add_link_to_group(&psy->dev.kobj, power_supply_extension_group.name, + &dev->kobj, ext->name); +} + +void power_supply_sysfs_remove_extension(struct power_supply *psy, + const struct power_supply_ext *ext) +{ + sysfs_remove_link_from_group(&psy->dev.kobj, power_supply_extension_group.name, ext->name); +} diff --git a/drivers/power/supply/qcom_battmgr.c b/drivers/power/supply/qcom_battmgr.c index 47d29271ddf4..fe27676fbc7c 100644 --- a/drivers/power/supply/qcom_battmgr.c +++ b/drivers/power/supply/qcom_battmgr.c @@ -8,6 +8,7 @@ #include <linux/mutex.h> #include <linux/of_device.h> #include <linux/power_supply.h> +#include <linux/property.h> #include <linux/soc/qcom/pdr.h> #include <linux/soc/qcom/pmic_glink.h> #include <linux/math.h> @@ -1336,10 +1337,10 @@ static int qcom_battmgr_probe(struct auxiliary_device *adev, battmgr->dev = dev; psy_cfg.drv_data = battmgr; - psy_cfg.of_node = adev->dev.of_node; + psy_cfg.fwnode = dev_fwnode(&adev->dev); psy_cfg_supply.drv_data = battmgr; - psy_cfg_supply.of_node = adev->dev.of_node; + psy_cfg_supply.fwnode = dev_fwnode(&adev->dev); psy_cfg_supply.supplied_to = qcom_battmgr_battery; psy_cfg_supply.num_supplicants = 1; diff --git a/drivers/power/supply/qcom_pmi8998_charger.c b/drivers/power/supply/qcom_pmi8998_charger.c index 3b4132376649..74a8d8ed8d9f 100644 --- a/drivers/power/supply/qcom_pmi8998_charger.c +++ b/drivers/power/supply/qcom_pmi8998_charger.c @@ -964,7 +964,7 @@ static int smb2_probe(struct platform_device *pdev) return rc; supply_config.drv_data = chip; - supply_config.of_node = pdev->dev.of_node; + supply_config.fwnode = dev_fwnode(&pdev->dev); desc = devm_kzalloc(chip->dev, sizeof(smb2_psy_desc), GFP_KERNEL); if (!desc) diff --git a/drivers/power/supply/qcom_smbb.c b/drivers/power/supply/qcom_smbb.c index a79563f6ff7a..28afe758a2da 100644 --- a/drivers/power/supply/qcom_smbb.c +++ b/drivers/power/supply/qcom_smbb.c @@ -880,7 +880,7 @@ static int smbb_charger_probe(struct platform_device *pdev) } bat_cfg.drv_data = chg; - bat_cfg.of_node = pdev->dev.of_node; + bat_cfg.fwnode = dev_fwnode(&pdev->dev); chg->bat_psy = devm_power_supply_register(&pdev->dev, &bat_psy_desc, &bat_cfg); diff --git a/drivers/power/supply/rk817_charger.c b/drivers/power/supply/rk817_charger.c index e5f35d083c23..945c7720c4ae 100644 --- a/drivers/power/supply/rk817_charger.c +++ b/drivers/power/supply/rk817_charger.c @@ -1088,7 +1088,7 @@ static int rk817_charger_probe(struct platform_device *pdev) rk817_bat_calib_vol(charger); pscfg.drv_data = charger; - pscfg.of_node = node; + pscfg.fwnode = node ? &node->fwnode : NULL; /* * Get sample resistor value. Note only values of 10000 or 20000 diff --git a/drivers/power/supply/rt5033_battery.c b/drivers/power/supply/rt5033_battery.c index 7a27b262fb84..b2674adfa30b 100644 --- a/drivers/power/supply/rt5033_battery.c +++ b/drivers/power/supply/rt5033_battery.c @@ -160,7 +160,7 @@ static int rt5033_battery_probe(struct i2c_client *client) } i2c_set_clientdata(client, battery); - psy_cfg.of_node = client->dev.of_node; + psy_cfg.fwnode = dev_fwnode(&client->dev); psy_cfg.drv_data = battery; battery->psy = devm_power_supply_register(&client->dev, diff --git a/drivers/power/supply/rt5033_charger.c b/drivers/power/supply/rt5033_charger.c index d19c7e80a92a..2fdc58439707 100644 --- a/drivers/power/supply/rt5033_charger.c +++ b/drivers/power/supply/rt5033_charger.c @@ -16,6 +16,7 @@ #include <linux/power_supply.h> #include <linux/regmap.h> #include <linux/mfd/rt5033-private.h> +#include <linux/property.h> struct rt5033_charger_data { unsigned int pre_uamp; @@ -675,7 +676,7 @@ static int rt5033_charger_probe(struct platform_device *pdev) charger->regmap = dev_get_regmap(pdev->dev.parent, NULL); mutex_init(&charger->lock); - psy_cfg.of_node = pdev->dev.of_node; + psy_cfg.fwnode = dev_fwnode(&pdev->dev); psy_cfg.drv_data = charger; charger->psy = devm_power_supply_register(charger->dev, diff --git a/drivers/power/supply/rt9455_charger.c b/drivers/power/supply/rt9455_charger.c index 64a23e3d7bb0..1ffe7f02932f 100644 --- a/drivers/power/supply/rt9455_charger.c +++ b/drivers/power/supply/rt9455_charger.c @@ -1579,7 +1579,7 @@ static const struct regmap_config rt9455_regmap_config = { .writeable_reg = rt9455_is_writeable_reg, .volatile_reg = rt9455_is_volatile_reg, .max_register = RT9455_REG_MASK3, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static int rt9455_probe(struct i2c_client *client) @@ -1658,7 +1658,7 @@ static int rt9455_probe(struct i2c_client *client) INIT_DEFERRABLE_WORK(&info->batt_presence_work, rt9455_batt_presence_work_callback); - rt9455_charger_config.of_node = dev->of_node; + rt9455_charger_config.fwnode = dev_fwnode(dev); rt9455_charger_config.drv_data = info; rt9455_charger_config.supplied_to = rt9455_charger_supplied_to; rt9455_charger_config.num_supplicants = diff --git a/drivers/power/supply/rt9467-charger.c b/drivers/power/supply/rt9467-charger.c index 235169c85c5d..e9aba9ad393c 100644 --- a/drivers/power/supply/rt9467-charger.c +++ b/drivers/power/supply/rt9467-charger.c @@ -826,7 +826,7 @@ static int rt9467_register_psy(struct rt9467_chg_data *data) { struct power_supply_config cfg = { .drv_data = data, - .of_node = dev_of_node(data->dev), + .fwnode = dev_fwnode(data->dev), .attr_grp = rt9467_sysfs_groups, }; diff --git a/drivers/power/supply/rt9471.c b/drivers/power/supply/rt9471.c index 67b86ac91a21..bd966abb4df5 100644 --- a/drivers/power/supply/rt9471.c +++ b/drivers/power/supply/rt9471.c @@ -723,7 +723,7 @@ static int rt9471_register_psy(struct rt9471_chip *chip) char *psy_name; cfg.drv_data = chip; - cfg.of_node = dev->of_node; + cfg.fwnode = dev_fwnode(dev); cfg.attr_grp = rt9471_sysfs_groups; psy_name = devm_kasprintf(dev, GFP_KERNEL, "rt9471-%s", dev_name(dev)); diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index a6c204c08232..943c82ee978f 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -21,6 +21,7 @@ #include <linux/power_supply.h> #include <linux/slab.h> #include <linux/stat.h> +#include <linux/string_choices.h> enum { REG_MANUFACTURER_DATA, @@ -320,8 +321,8 @@ static int sbs_update_presence(struct sbs_info *chip, bool is_present) client->flags &= ~I2C_CLIENT_PEC; } - dev_dbg(&client->dev, "PEC: %s\n", (client->flags & I2C_CLIENT_PEC) ? - "enabled" : "disabled"); + dev_dbg(&client->dev, "PEC: %s\n", + str_enabled_disabled(client->flags & I2C_CLIENT_PEC)); if (!chip->is_present && is_present && !chip->charger_broadcasts) sbs_disable_charger_broadcasts(chip); @@ -1137,7 +1138,7 @@ static int sbs_probe(struct i2c_client *client) chip->flags = (uintptr_t)i2c_get_match_data(client); chip->client = client; - psy_cfg.of_node = client->dev.of_node; + psy_cfg.fwnode = dev_fwnode(&client->dev); psy_cfg.drv_data = chip; chip->last_state = POWER_SUPPLY_STATUS_UNKNOWN; sbs_invalidate_cached_props(chip); diff --git a/drivers/power/supply/sbs-charger.c b/drivers/power/supply/sbs-charger.c index ab3f095d90ea..27764123b929 100644 --- a/drivers/power/supply/sbs-charger.c +++ b/drivers/power/supply/sbs-charger.c @@ -173,7 +173,7 @@ static int sbs_probe(struct i2c_client *client) return -ENOMEM; chip->client = client; - psy_cfg.of_node = client->dev.of_node; + psy_cfg.fwnode = dev_fwnode(&client->dev); psy_cfg.drv_data = chip; i2c_set_clientdata(client, chip); diff --git a/drivers/power/supply/sbs-manager.c b/drivers/power/supply/sbs-manager.c index 7d2f39f19acb..869729dfcd66 100644 --- a/drivers/power/supply/sbs-manager.c +++ b/drivers/power/supply/sbs-manager.c @@ -379,7 +379,7 @@ static int sbsm_probe(struct i2c_client *client) return ret; psy_cfg.drv_data = data; - psy_cfg.of_node = dev->of_node; + psy_cfg.fwnode = dev_fwnode(dev); data->psy = devm_power_supply_register(dev, psy_desc, &psy_cfg); if (IS_ERR(data->psy)) return dev_err_probe(dev, PTR_ERR(data->psy), diff --git a/drivers/power/supply/sc2731_charger.c b/drivers/power/supply/sc2731_charger.c index 50d5157af927..58b86fd78771 100644 --- a/drivers/power/supply/sc2731_charger.c +++ b/drivers/power/supply/sc2731_charger.c @@ -480,7 +480,7 @@ static int sc2731_charger_probe(struct platform_device *pdev) } charger_cfg.drv_data = info; - charger_cfg.of_node = np; + charger_cfg.fwnode = dev_fwnode(&pdev->dev); info->psy_usb = devm_power_supply_register(&pdev->dev, &sc2731_charger_desc, &charger_cfg); diff --git a/drivers/power/supply/sc27xx_fuel_gauge.c b/drivers/power/supply/sc27xx_fuel_gauge.c index f36edc2ba708..a7ed9de8a289 100644 --- a/drivers/power/supply/sc27xx_fuel_gauge.c +++ b/drivers/power/supply/sc27xx_fuel_gauge.c @@ -1014,9 +1014,8 @@ static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data) if (!table) return -EINVAL; - data->cap_table = devm_kmemdup(data->dev, table, - data->table_len * sizeof(*table), - GFP_KERNEL); + data->cap_table = devm_kmemdup_array(data->dev, table, data->table_len, + sizeof(*table), GFP_KERNEL); if (!data->cap_table) { power_supply_put_battery_info(data->battery, info); return -ENOMEM; @@ -1141,7 +1140,6 @@ disable_fgu: static int sc27xx_fgu_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; struct power_supply_config fgu_cfg = { }; struct sc27xx_fgu_data *data; int ret, irq; @@ -1205,7 +1203,7 @@ static int sc27xx_fgu_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); fgu_cfg.drv_data = data; - fgu_cfg.of_node = np; + fgu_cfg.fwnode = dev_fwnode(dev); data->battery = devm_power_supply_register(dev, &sc27xx_fgu_desc, &fgu_cfg); if (IS_ERR(data->battery)) { diff --git a/drivers/power/supply/smb347-charger.c b/drivers/power/supply/smb347-charger.c index c8392933ee28..8b95f7e8712f 100644 --- a/drivers/power/supply/smb347-charger.c +++ b/drivers/power/supply/smb347-charger.c @@ -1488,7 +1488,7 @@ static const struct regmap_config smb347_regmap = { .max_register = SMB347_MAX_REGISTER, .volatile_reg = smb347_volatile_reg, .readable_reg = smb347_readable_reg, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static const struct regulator_ops smb347_usb_vbus_regulator_ops = { @@ -1553,7 +1553,7 @@ static int smb347_probe(struct i2c_client *client) return PTR_ERR(smb->regmap); mains_usb_cfg.drv_data = smb; - mains_usb_cfg.of_node = dev->of_node; + mains_usb_cfg.fwnode = dev_fwnode(dev); if (smb->use_mains) { smb->mains = devm_power_supply_register(dev, &smb347_mains_desc, &mains_usb_cfg); diff --git a/drivers/power/supply/stc3117_fuel_gauge.c b/drivers/power/supply/stc3117_fuel_gauge.c new file mode 100644 index 000000000000..a1bc5970370a --- /dev/null +++ b/drivers/power/supply/stc3117_fuel_gauge.c @@ -0,0 +1,612 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * stc3117_fuel_gauge.c - STMicroelectronics STC3117 Fuel Gauge Driver + * + * Copyright (c) 2024 Silicon Signals Pvt Ltd. + * Author: Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io> + * Bhavin Sharma <bhavin.sharma@siliconsignals.io> + */ + +#include <linux/crc8.h> +#include <linux/devm-helpers.h> +#include <linux/i2c.h> +#include <linux/power_supply.h> +#include <linux/regmap.h> +#include <linux/workqueue.h> + +#define STC3117_ADDR_MODE 0x00 +#define STC3117_ADDR_CTRL 0x01 +#define STC3117_ADDR_SOC_L 0x02 +#define STC3117_ADDR_SOC_H 0x03 +#define STC3117_ADDR_COUNTER_L 0x04 +#define STC3117_ADDR_COUNTER_H 0x05 +#define STC3117_ADDR_CURRENT_L 0x06 +#define STC3117_ADDR_CURRENT_H 0x07 +#define STC3117_ADDR_VOLTAGE_L 0x08 +#define STC3117_ADDR_VOLTAGE_H 0x09 +#define STC3117_ADDR_TEMPERATURE 0x0A +#define STC3117_ADDR_AVG_CURRENT_L 0x0B +#define STC3117_ADDR_AVG_CURRENT_H 0x0C +#define STC3117_ADDR_OCV_L 0x0D +#define STC3117_ADDR_OCV_H 0x0E +#define STC3117_ADDR_CC_CNF_L 0x0F +#define STC3117_ADDR_CC_CNF_H 0x10 +#define STC3117_ADDR_VM_CNF_L 0x11 +#define STC3117_ADDR_VM_CNF_H 0x12 +#define STC3117_ADDR_ALARM_soc 0x13 +#define STC3117_ADDR_ALARM_VOLTAGE 0x14 +#define STC3117_ADDR_ID 0x18 +#define STC3117_ADDR_CC_ADJ_L 0x1B +#define STC3117_ADDR_CC_ADJ_H 0x1C +#define STC3117_ADDR_VM_ADJ_L 0x1D +#define STC3117_ADDR_VM_ADJ_H 0x1E +#define STC3117_ADDR_RAM 0x20 +#define STC3117_ADDR_OCV_TABLE 0x30 +#define STC3117_ADDR_SOC_TABLE 0x30 + +/* Bit mask definition */ +#define STC3117_ID 0x16 +#define STC3117_MIXED_MODE 0x00 +#define STC3117_VMODE BIT(0) +#define STC3117_GG_RUN BIT(4) +#define STC3117_CC_MODE BIT(5) +#define STC3117_BATFAIL BIT(3) +#define STC3117_PORDET BIT(4) +#define STC3117_RAM_SIZE 16 +#define STC3117_OCV_TABLE_SIZE 16 +#define STC3117_RAM_TESTWORD 0x53A9 +#define STC3117_SOFT_RESET 0x11 +#define STC3117_NOMINAL_CAPACITY 2600 + +#define VOLTAGE_LSB_VALUE 9011 +#define CURRENT_LSB_VALUE 24084 +#define APP_CUTOFF_VOLTAGE 2500 +#define MAX_HRSOC 51200 +#define MAX_SOC 1000 +#define CHG_MIN_CURRENT 200 +#define CHG_END_CURRENT 20 +#define APP_MIN_CURRENT (-5) +#define BATTERY_FULL 95 +#define CRC8_POLYNOMIAL 0x07 +#define CRC8_INIT 0x00 + +DECLARE_CRC8_TABLE(stc3117_crc_table); + +enum stc3117_state { + STC3117_INIT, + STC3117_RUNNING, + STC3117_POWERDN, +}; + +/* Default ocv curve Li-ion battery */ +static const int ocv_value[16] = { + 3400, 3582, 3669, 3676, 3699, 3737, 3757, 3774, + 3804, 3844, 3936, 3984, 4028, 4131, 4246, 4320 +}; + +union stc3117_internal_ram { + u8 ram_bytes[STC3117_RAM_SIZE]; + struct { + u16 testword; /* 0-1 Bytes */ + u16 hrsoc; /* 2-3 Bytes */ + u16 cc_cnf; /* 4-5 Bytes */ + u16 vm_cnf; /* 6-7 Bytes */ + u8 soc; /* 8 Byte */ + u8 state; /* 9 Byte */ + u8 unused[5]; /* 10-14 Bytes */ + u8 crc; /* 15 Byte */ + } reg; +}; + +struct stc3117_battery_info { + int voltage_min_mv; + int voltage_max_mv; + int battery_capacity_mah; + int sense_resistor; +}; + +struct stc3117_data { + struct i2c_client *client; + struct regmap *regmap; + struct delayed_work update_work; + struct power_supply *battery; + union stc3117_internal_ram ram_data; + struct stc3117_battery_info battery_info; + + u8 soc_tab[16]; + int cc_cnf; + int vm_cnf; + int cc_adj; + int vm_adj; + int avg_current; + int avg_voltage; + int batt_current; + int voltage; + int temp; + int soc; + int ocv; + int hrsoc; + int presence; +}; + +static int stc3117_convert(int value, int factor) +{ + value = (value * factor) / 4096; + return value * 1000; +} + +static int stc3117_get_battery_data(struct stc3117_data *data) +{ + u8 reg_list[16]; + u8 data_adjust[4]; + int value, mode; + + regmap_bulk_read(data->regmap, STC3117_ADDR_MODE, + reg_list, sizeof(reg_list)); + + /* soc */ + value = (reg_list[3] << 8) + reg_list[2]; + data->hrsoc = value; + data->soc = (value * 10 + 256) / 512; + + /* current in uA*/ + value = (reg_list[7] << 8) + reg_list[6]; + data->batt_current = stc3117_convert(value, + CURRENT_LSB_VALUE / data->battery_info.sense_resistor); + + /* voltage in uV */ + value = (reg_list[9] << 8) + reg_list[8]; + data->voltage = stc3117_convert(value, VOLTAGE_LSB_VALUE); + + /* temp in 1/10 °C */ + data->temp = reg_list[10] * 10; + + /* Avg current in uA */ + value = (reg_list[12] << 8) + reg_list[11]; + regmap_read(data->regmap, STC3117_ADDR_MODE, &mode); + if (!(mode & STC3117_VMODE)) { + value = stc3117_convert(value, + CURRENT_LSB_VALUE / data->battery_info.sense_resistor); + value = value / 4; + } else { + value = stc3117_convert(value, 36 * STC3117_NOMINAL_CAPACITY); + } + data->avg_current = value; + + /* ocv in uV */ + value = (reg_list[14] << 8) + reg_list[13]; + value = stc3117_convert(value, VOLTAGE_LSB_VALUE); + value = (value + 2) / 4; + data->ocv = value; + + /* CC & VM adjustment counters */ + regmap_bulk_read(data->regmap, STC3117_ADDR_CC_ADJ_L, + data_adjust, sizeof(data_adjust)); + value = (data_adjust[1] << 8) + data_adjust[0]; + data->cc_adj = value; + + value = (data_adjust[3] << 8) + data_adjust[2]; + data->vm_adj = value; + + return 0; +} + +static int ram_write(struct stc3117_data *data) +{ + int ret; + + ret = regmap_bulk_write(data->regmap, STC3117_ADDR_RAM, + data->ram_data.ram_bytes, STC3117_RAM_SIZE); + if (ret) + return ret; + + return 0; +}; + +static int ram_read(struct stc3117_data *data) +{ + int ret; + + ret = regmap_bulk_read(data->regmap, STC3117_ADDR_RAM, + data->ram_data.ram_bytes, STC3117_RAM_SIZE); + if (ret) + return ret; + + return 0; +}; + +static int stc3117_set_para(struct stc3117_data *data) +{ + int ret; + + ret = regmap_write(data->regmap, STC3117_ADDR_MODE, STC3117_VMODE); + + for (int i = 0; i < STC3117_OCV_TABLE_SIZE; i++) + ret |= regmap_write(data->regmap, STC3117_ADDR_OCV_TABLE + i, + ocv_value[i] * 100 / 55); + if (data->soc_tab[1] != 0) + ret |= regmap_bulk_write(data->regmap, STC3117_ADDR_SOC_TABLE, + data->soc_tab, STC3117_OCV_TABLE_SIZE); + + ret |= regmap_write(data->regmap, STC3117_ADDR_CC_CNF_H, + (data->ram_data.reg.cc_cnf >> 8) & 0xFF); + + ret |= regmap_write(data->regmap, STC3117_ADDR_CC_CNF_L, + data->ram_data.reg.cc_cnf & 0xFF); + + ret |= regmap_write(data->regmap, STC3117_ADDR_VM_CNF_H, + (data->ram_data.reg.vm_cnf >> 8) & 0xFF); + + ret |= regmap_write(data->regmap, STC3117_ADDR_VM_CNF_L, + data->ram_data.reg.vm_cnf & 0xFF); + + ret |= regmap_write(data->regmap, STC3117_ADDR_CTRL, 0x03); + + ret |= regmap_write(data->regmap, STC3117_ADDR_MODE, + STC3117_MIXED_MODE | STC3117_GG_RUN); + + return ret; +}; + +static int stc3117_init(struct stc3117_data *data) +{ + int id, ret; + int ctrl; + int ocv_m, ocv_l; + + regmap_read(data->regmap, STC3117_ADDR_ID, &id); + if (id != STC3117_ID) + return -EINVAL; + + data->cc_cnf = (data->battery_info.battery_capacity_mah * + data->battery_info.sense_resistor * 250 + 6194) / 12389; + data->vm_cnf = (data->battery_info.battery_capacity_mah + * 200 * 50 + 24444) / 48889; + + /* Battery has not been removed */ + data->presence = 1; + + /* Read RAM data */ + ret = ram_read(data); + if (ret) + return ret; + + if (data->ram_data.reg.testword != STC3117_RAM_TESTWORD || + (crc8(stc3117_crc_table, data->ram_data.ram_bytes, + STC3117_RAM_SIZE, CRC8_INIT)) != 0) { + data->ram_data.reg.testword = STC3117_RAM_TESTWORD; + data->ram_data.reg.cc_cnf = data->cc_cnf; + data->ram_data.reg.vm_cnf = data->vm_cnf; + data->ram_data.reg.crc = crc8(stc3117_crc_table, + data->ram_data.ram_bytes, + STC3117_RAM_SIZE - 1, CRC8_INIT); + + ret = regmap_read(data->regmap, STC3117_ADDR_OCV_H, &ocv_m); + + ret |= regmap_read(data->regmap, STC3117_ADDR_OCV_L, &ocv_l); + + ret |= stc3117_set_para(data); + + ret |= regmap_write(data->regmap, STC3117_ADDR_OCV_H, ocv_m); + + ret |= regmap_write(data->regmap, STC3117_ADDR_OCV_L, ocv_l); + if (ret) + return ret; + } else { + ret = regmap_read(data->regmap, STC3117_ADDR_CTRL, &ctrl); + if (ret) + return ret; + + if ((ctrl & STC3117_BATFAIL) != 0 || + (ctrl & STC3117_PORDET) != 0) { + ret = regmap_read(data->regmap, + STC3117_ADDR_OCV_H, &ocv_m); + + ret |= regmap_read(data->regmap, + STC3117_ADDR_OCV_L, &ocv_l); + + ret |= stc3117_set_para(data); + + ret |= regmap_write(data->regmap, + STC3117_ADDR_OCV_H, ocv_m); + + ret |= regmap_write(data->regmap, + STC3117_ADDR_OCV_L, ocv_l); + if (ret) + return ret; + } else { + ret = stc3117_set_para(data); + ret |= regmap_write(data->regmap, STC3117_ADDR_SOC_H, + (data->ram_data.reg.hrsoc >> 8 & 0xFF)); + ret |= regmap_write(data->regmap, STC3117_ADDR_SOC_L, + (data->ram_data.reg.hrsoc & 0xFF)); + if (ret) + return ret; + } + } + + data->ram_data.reg.state = STC3117_INIT; + data->ram_data.reg.crc = crc8(stc3117_crc_table, + data->ram_data.ram_bytes, + STC3117_RAM_SIZE - 1, CRC8_INIT); + ret = ram_write(data); + if (ret) + return ret; + + return 0; +}; + +static int stc3117_task(struct stc3117_data *data) +{ + int id, mode, ret; + int count_l, count_m; + int ocv_l, ocv_m; + + regmap_read(data->regmap, STC3117_ADDR_ID, &id); + if (id != STC3117_ID) { + data->presence = 0; + return -EINVAL; + } + + stc3117_get_battery_data(data); + + /* Read RAM data */ + ret = ram_read(data); + if (ret) + return ret; + + if (data->ram_data.reg.testword != STC3117_RAM_TESTWORD || + (crc8(stc3117_crc_table, data->ram_data.ram_bytes, + STC3117_RAM_SIZE, CRC8_INIT) != 0)) { + data->ram_data.reg.testword = STC3117_RAM_TESTWORD; + data->ram_data.reg.cc_cnf = data->cc_cnf; + data->ram_data.reg.vm_cnf = data->vm_cnf; + data->ram_data.reg.crc = crc8(stc3117_crc_table, + data->ram_data.ram_bytes, + STC3117_RAM_SIZE - 1, CRC8_INIT); + data->ram_data.reg.state = STC3117_INIT; + } + + /* check battery presence status */ + ret = regmap_read(data->regmap, STC3117_ADDR_CTRL, &mode); + if ((mode & STC3117_BATFAIL) != 0) { + data->presence = 0; + data->ram_data.reg.testword = 0; + data->ram_data.reg.state = STC3117_INIT; + ret = ram_write(data); + ret |= regmap_write(data->regmap, STC3117_ADDR_CTRL, STC3117_PORDET); + if (ret) + return ret; + } + + data->presence = 1; + + ret = regmap_read(data->regmap, STC3117_ADDR_MODE, &mode); + if (ret) + return ret; + if ((mode & STC3117_GG_RUN) == 0) { + if (data->ram_data.reg.state > STC3117_INIT) { + ret = stc3117_set_para(data); + + ret |= regmap_write(data->regmap, STC3117_ADDR_SOC_H, + (data->ram_data.reg.hrsoc >> 8 & 0xFF)); + ret |= regmap_write(data->regmap, STC3117_ADDR_SOC_L, + (data->ram_data.reg.hrsoc & 0xFF)); + if (ret) + return ret; + } else { + ret = regmap_read(data->regmap, STC3117_ADDR_OCV_H, &ocv_m); + + ret |= regmap_read(data->regmap, STC3117_ADDR_OCV_L, &ocv_l); + + ret |= stc3117_set_para(data); + + ret |= regmap_write(data->regmap, STC3117_ADDR_OCV_H, ocv_m); + + ret |= regmap_write(data->regmap, STC3117_ADDR_OCV_L, ocv_l); + if (ret) + return ret; + } + data->ram_data.reg.state = STC3117_INIT; + } + + regmap_read(data->regmap, STC3117_ADDR_COUNTER_L, &count_l); + regmap_read(data->regmap, STC3117_ADDR_COUNTER_H, &count_m); + + count_m = (count_m << 8) + count_l; + + /* INIT state, wait for batt_current & temperature value available: */ + if (data->ram_data.reg.state == STC3117_INIT && count_m > 4) { + data->avg_voltage = data->voltage; + data->avg_current = data->batt_current; + data->ram_data.reg.state = STC3117_RUNNING; + } + + if (data->ram_data.reg.state != STC3117_RUNNING) { + data->batt_current = -ENODATA; + data->temp = -ENODATA; + } else { + if (data->voltage < APP_CUTOFF_VOLTAGE) + data->soc = -ENODATA; + + if (mode & STC3117_VMODE) { + data->avg_current = -ENODATA; + data->batt_current = -ENODATA; + } + } + + data->ram_data.reg.hrsoc = data->hrsoc; + data->ram_data.reg.soc = (data->soc + 5) / 10; + data->ram_data.reg.crc = crc8(stc3117_crc_table, + data->ram_data.ram_bytes, + STC3117_RAM_SIZE - 1, CRC8_INIT); + + ret = ram_write(data); + if (ret) + return ret; + return 0; +}; + +static void fuel_gauge_update_work(struct work_struct *work) +{ + struct stc3117_data *data = + container_of(work, struct stc3117_data, update_work.work); + + stc3117_task(data); + + /* Schedule the work to run again in 2 seconds */ + schedule_delayed_work(&data->update_work, msecs_to_jiffies(2000)); +} + +static int stc3117_get_property(struct power_supply *psy, + enum power_supply_property psp, union power_supply_propval *val) +{ + struct stc3117_data *data = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + if (data->soc > BATTERY_FULL) + val->intval = POWER_SUPPLY_STATUS_FULL; + else if (data->batt_current < 0) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else if (data->batt_current > 0) + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + else + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = data->voltage; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = data->batt_current; + break; + case POWER_SUPPLY_PROP_VOLTAGE_OCV: + val->intval = data->ocv; + break; + case POWER_SUPPLY_PROP_CURRENT_AVG: + val->intval = data->avg_current; + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = data->soc; + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = data->temp; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = data->presence; + break; + default: + return -EINVAL; + } + return 0; +} + +static enum power_supply_property stc3117_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_VOLTAGE_OCV, + POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_PRESENT, +}; + +static const struct power_supply_desc stc3117_battery_desc = { + .name = "stc3117-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .get_property = stc3117_get_property, + .properties = stc3117_battery_props, + .num_properties = ARRAY_SIZE(stc3117_battery_props), +}; + +static const struct regmap_config stc3117_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int stc3117_probe(struct i2c_client *client) +{ + struct stc3117_data *data; + struct power_supply_config psy_cfg = {}; + struct power_supply_battery_info *info; + int ret; + + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + data->regmap = devm_regmap_init_i2c(client, &stc3117_regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); + + psy_cfg.drv_data = data; + psy_cfg.fwnode = dev_fwnode(&client->dev); + + crc8_populate_msb(stc3117_crc_table, CRC8_POLYNOMIAL); + + data->battery = devm_power_supply_register(&client->dev, + &stc3117_battery_desc, &psy_cfg); + if (IS_ERR(data->battery)) + return dev_err_probe(&client->dev, PTR_ERR(data->battery), + "failed to register battery\n"); + + ret = device_property_read_u32(&client->dev, "shunt-resistor-micro-ohms", + &data->battery_info.sense_resistor); + if (ret) + return dev_err_probe(&client->dev, ret, + "failed to get shunt-resistor-micro-ohms\n"); + data->battery_info.sense_resistor = data->battery_info.sense_resistor / 1000; + + ret = power_supply_get_battery_info(data->battery, &info); + if (ret) + return dev_err_probe(&client->dev, ret, + "failed to get battery information\n"); + + data->battery_info.battery_capacity_mah = info->charge_full_design_uah / 1000; + data->battery_info.voltage_min_mv = info->voltage_min_design_uv / 1000; + data->battery_info.voltage_max_mv = info->voltage_max_design_uv / 1000; + + ret = stc3117_init(data); + if (ret) + return dev_err_probe(&client->dev, ret, + "failed to initialize of stc3117\n"); + + ret = devm_delayed_work_autocancel(&client->dev, &data->update_work, + fuel_gauge_update_work); + if (ret) + return ret; + + schedule_delayed_work(&data->update_work, 0); + + return 0; +} + +static const struct i2c_device_id stc3117_id[] = { + { "stc3117", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, stc3117_id); + +static const struct of_device_id stc3117_of_match[] = { + { .compatible = "st,stc3117" }, + { } +}; +MODULE_DEVICE_TABLE(of, stc3117_of_match); + +static struct i2c_driver stc3117_i2c_driver = { + .driver = { + .name = "stc3117_i2c_driver", + .of_match_table = stc3117_of_match, + }, + .probe = stc3117_probe, + .id_table = stc3117_id, +}; + +module_i2c_driver(stc3117_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>"); +MODULE_AUTHOR("Bhavin Sharma <bhavin.sharma@siliconsignals.io>"); +MODULE_DESCRIPTION("STC3117 Fuel Gauge Driver"); diff --git a/drivers/power/supply/surface_battery.c b/drivers/power/supply/surface_battery.c index ebd1edde28f1..c759add4df49 100644 --- a/drivers/power/supply/surface_battery.c +++ b/drivers/power/supply/surface_battery.c @@ -667,7 +667,7 @@ out: static ssize_t alarm_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct spwr_battery_device *bat = power_supply_get_drvdata(psy); int status; @@ -681,7 +681,7 @@ static ssize_t alarm_show(struct device *dev, struct device_attribute *attr, cha static ssize_t alarm_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct power_supply *psy = dev_get_drvdata(dev); + struct power_supply *psy = dev_to_psy(dev); struct spwr_battery_device *bat = power_supply_get_drvdata(psy); unsigned long value; int status; diff --git a/drivers/power/supply/test_power.c b/drivers/power/supply/test_power.c index 442ceb7795e1..2a975a110f48 100644 --- a/drivers/power/supply/test_power.c +++ b/drivers/power/supply/test_power.c @@ -37,6 +37,7 @@ static int battery_charge_counter = -1000; static int battery_current = -1600; static enum power_supply_charge_behaviour battery_charge_behaviour = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO; +static bool battery_extension; static bool module_initialized; @@ -238,6 +239,87 @@ static const struct power_supply_config test_power_configs[] = { }, }; +static int test_power_battery_extmanufacture_year = 1234; +static int test_power_battery_exttemp_max = 1000; +static const enum power_supply_property test_power_battery_extprops[] = { + POWER_SUPPLY_PROP_MANUFACTURE_YEAR, + POWER_SUPPLY_PROP_TEMP_MAX, +}; + +static int test_power_battery_extget_property(struct power_supply *psy, + const struct power_supply_ext *ext, + void *ext_data, + enum power_supply_property psp, + union power_supply_propval *val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_MANUFACTURE_YEAR: + val->intval = test_power_battery_extmanufacture_year; + break; + case POWER_SUPPLY_PROP_TEMP_MAX: + val->intval = test_power_battery_exttemp_max; + break; + default: + return -EINVAL; + } + return 0; +} + +static int test_power_battery_extset_property(struct power_supply *psy, + const struct power_supply_ext *ext, + void *ext_data, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_MANUFACTURE_YEAR: + test_power_battery_extmanufacture_year = val->intval; + break; + case POWER_SUPPLY_PROP_TEMP_MAX: + test_power_battery_exttemp_max = val->intval; + break; + default: + return -EINVAL; + } + return 0; +} + +static int test_power_battery_extproperty_is_writeable(struct power_supply *psy, + const struct power_supply_ext *ext, + void *ext_data, + enum power_supply_property psp) +{ + return true; +} + +static const struct power_supply_ext test_power_battery_ext = { + .name = "test_power", + .properties = test_power_battery_extprops, + .num_properties = ARRAY_SIZE(test_power_battery_extprops), + .get_property = test_power_battery_extget_property, + .set_property = test_power_battery_extset_property, + .property_is_writeable = test_power_battery_extproperty_is_writeable, +}; + +static void test_power_configure_battery_extension(bool enable) +{ + struct power_supply *psy; + + psy = test_power_supplies[TEST_BATTERY]; + + if (enable) { + if (power_supply_register_extension(psy, &test_power_battery_ext, &psy->dev, + NULL)) { + pr_err("registering battery extension failed\n"); + return; + } + } else { + power_supply_unregister_extension(psy, &test_power_battery_ext); + } + + battery_extension = enable; +} + static int __init test_power_init(void) { int i; @@ -258,6 +340,8 @@ static int __init test_power_init(void) } } + test_power_configure_battery_extension(true); + module_initialized = true; return 0; failed: @@ -524,6 +608,26 @@ static int param_set_battery_current(const char *key, #define param_get_battery_current param_get_int +static int param_set_battery_extension(const char *key, + const struct kernel_param *kp) +{ + bool prev_battery_extension; + int ret; + + prev_battery_extension = battery_extension; + + ret = param_set_bool(key, kp); + if (ret) + return ret; + + if (prev_battery_extension != battery_extension) + test_power_configure_battery_extension(battery_extension); + + return 0; +} + +#define param_get_battery_extension param_get_bool + static const struct kernel_param_ops param_ops_ac_online = { .set = param_set_ac_online, .get = param_get_ac_online, @@ -574,6 +678,11 @@ static const struct kernel_param_ops param_ops_battery_current = { .get = param_get_battery_current, }; +static const struct kernel_param_ops param_ops_battery_extension = { + .set = param_set_battery_extension, + .get = param_get_battery_extension, +}; + #define param_check_ac_online(name, p) __param_check(name, p, void); #define param_check_usb_online(name, p) __param_check(name, p, void); #define param_check_battery_status(name, p) __param_check(name, p, void); @@ -584,6 +693,7 @@ static const struct kernel_param_ops param_ops_battery_current = { #define param_check_battery_voltage(name, p) __param_check(name, p, void); #define param_check_battery_charge_counter(name, p) __param_check(name, p, void); #define param_check_battery_current(name, p) __param_check(name, p, void); +#define param_check_battery_extension(name, p) __param_check(name, p, void); module_param(ac_online, ac_online, 0644); @@ -621,6 +731,9 @@ MODULE_PARM_DESC(battery_charge_counter, module_param(battery_current, battery_current, 0644); MODULE_PARM_DESC(battery_current, "battery current (milliampere)"); +module_param(battery_extension, battery_extension, 0644); +MODULE_PARM_DESC(battery_extension, "battery extension"); + MODULE_DESCRIPTION("Power supply driver for testing"); MODULE_AUTHOR("Anton Vorontsov <cbouatmailru@gmail.com>"); MODULE_LICENSE("GPL"); diff --git a/drivers/power/supply/tps65090-charger.c b/drivers/power/supply/tps65090-charger.c index d65193e410a6..d010f013af8c 100644 --- a/drivers/power/supply/tps65090-charger.c +++ b/drivers/power/supply/tps65090-charger.c @@ -259,7 +259,7 @@ static int tps65090_charger_probe(struct platform_device *pdev) psy_cfg.supplied_to = pdata->supplied_to; psy_cfg.num_supplicants = pdata->num_supplicants; - psy_cfg.of_node = pdev->dev.of_node; + psy_cfg.fwnode = dev_fwnode(&pdev->dev); psy_cfg.drv_data = cdata; cdata->ac = devm_power_supply_register(&pdev->dev, &tps65090_charger_desc, diff --git a/drivers/power/supply/tps65217_charger.c b/drivers/power/supply/tps65217_charger.c index 6fff44e1ecac..6af17ce0b204 100644 --- a/drivers/power/supply/tps65217_charger.c +++ b/drivers/power/supply/tps65217_charger.c @@ -198,7 +198,7 @@ static int tps65217_charger_probe(struct platform_device *pdev) charger->tps = tps; charger->dev = &pdev->dev; - cfg.of_node = pdev->dev.of_node; + cfg.fwnode = dev_fwnode(&pdev->dev); cfg.drv_data = charger; charger->psy = devm_power_supply_register(&pdev->dev, diff --git a/drivers/power/supply/ucs1002_power.c b/drivers/power/supply/ucs1002_power.c index 7382bec6a43c..d32a7633f9e7 100644 --- a/drivers/power/supply/ucs1002_power.c +++ b/drivers/power/supply/ucs1002_power.c @@ -560,7 +560,7 @@ static int ucs1002_probe(struct i2c_client *client) irq_a_det = of_irq_get_byname(dev->of_node, "a_det"); irq_alert = of_irq_get_byname(dev->of_node, "alert"); - charger_config.of_node = dev->of_node; + charger_config.fwnode = dev_fwnode(dev); charger_config.drv_data = info; ret = regmap_read(info->regmap, UCS1002_REG_PRODUCT_ID, ®val); diff --git a/drivers/power/supply/ug3105_battery.c b/drivers/power/supply/ug3105_battery.c index ccc5c4d2e230..38e23bdd4603 100644 --- a/drivers/power/supply/ug3105_battery.c +++ b/drivers/power/supply/ug3105_battery.c @@ -287,7 +287,6 @@ out: static enum power_supply_property ug3105_battery_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_SCOPE, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_VOLTAGE_OCV, @@ -316,9 +315,6 @@ static int ug3105_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_PRESENT: val->intval = 1; break; - case POWER_SUPPLY_PROP_TECHNOLOGY: - val->intval = chip->info->technology; - break; case POWER_SUPPLY_PROP_SCOPE: val->intval = POWER_SUPPLY_SCOPE_SYSTEM; break; |