summaryrefslogtreecommitdiff
path: root/drivers/power
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2025-01-27 15:37:16 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2025-01-27 15:37:16 -0800
commitf28f4890454cc97c18d31ab4686957857cc862b5 (patch)
tree58a260060ce77a5d4c725b939658d4cb9ba4e6a9 /drivers/power
parentdeee7487f5d495d0d9e5ab40d866d69ad524c46a (diff)
parentb4a95b8fd3e67c1222c76bdd1078d43c9a11d132 (diff)
Merge tag 'for-v6.14' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply
Pull power supply and reset updates from Sebastian Reichel: "Power-supply core: - introduce power supply extensions, which allows adding properties to a power supply device from a separate driver. This will be used initially to extend the generic ACPI charger/battery driver with vendor extensions for charge thresholds. - convert all drivers from power_supply_for_each_device to new power_supply_for_each_psy(), which avoids lots of casting being done in the drivers. - avoid LED trigger like values in uevent for POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR - introduce POWER_SUPPLY_PROP_CHARGE_TYPES, which is similar to the POWER_SUPPLY_PROP_CHARGE_TYPE property, but also lists the available options on the specific platform Power-supply drivers - dell-laptop: use new power_supply_charge_types_show/_parse helpers - stc3117: new driver for equally named fuel gauge chip - bq24190: add support for new POWER_SUPPLY_PROP_CHARGE_TYPES - bq24190: add BQ24297 support - bq27xxx: add voltage min design for bq27000/bq27200 - cros_charge-control: convert to new power supply extension API - multiple drivers: constify 'struct bin_attribute' - ds2782: convert to device managed resources - max1720x: add charge full property - max1720x: support extra thermistor temperatures - max17042: add max77705 support - ip5xxx-power: add support for IP5306 - ltc4162-l-charger: add ltc4162-f/s and ltc4015 support - gpio-charger: support for default charge current limit - misc small cleanups and fixes Reset drivers: - at91-poweroff: add sam9x7 support" * tag 'for-v6.14' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (77 commits) power: supply: max1720x: add support for reading internal and thermistor temperatures power: supply: ltc4162l: Use GENMASK macro in bitmask operation power: supply: max17042: add max77705 fuel gauge support dt-bindings: power: supply: max17042: add max77705 support power: supply: add undervoltage health status property power: supply: max17042: add platform driver variant power: supply: max17042: make interrupt shared power: reset: keystone: Use syscon_regmap_lookup_by_phandle_args power: supply: Use str_enable_disable-like helpers platform/x86: dell-laptop: Use power_supply_charge_types_show/_parse() helpers power: supply: bq2415x_charger: Immediately reschedule delayed work on notifier events power: supply: Add STC3117 fuel gauge unit driver dt-bindings: power: supply: Add STC3117 Fuel Gauge power: supply: ug3105_battery: Let the core handle POWER_SUPPLY_PROP_TECHNOLOGY power: supply: gpio-charger: add support for default charge current limit dt-bindings: power: supply: gpio-charger: add support for default charge current limit power: supply: Use power_supply_external_power_changed() in __power_supply_changed_work() power: supply: core: fix build of extension sysfs group if CONFIG_SYSFS=n power: supply: bq2415x_charger: report charging state changes to userspace bq27xxx: add voltage min design for bq27000 and bq27200 ...
Diffstat (limited to 'drivers/power')
-rw-r--r--drivers/power/reset/Kconfig4
-rw-r--r--drivers/power/reset/as3722-poweroff.c2
-rw-r--r--drivers/power/reset/at91-sama5d2_shdwc.c1
-rw-r--r--drivers/power/reset/gpio-poweroff.c8
-rw-r--r--drivers/power/reset/keystone-reset.c18
-rw-r--r--drivers/power/supply/88pm860x_battery.c4
-rw-r--r--drivers/power/supply/Kconfig9
-rw-r--r--drivers/power/supply/Makefile1
-rw-r--r--drivers/power/supply/ab8500_btemp.c5
-rw-r--r--drivers/power/supply/ab8500_chargalg.c5
-rw-r--r--drivers/power/supply/ab8500_charger.c5
-rw-r--r--drivers/power/supply/ab8500_fg.c33
-rw-r--r--drivers/power/supply/apm_power.c6
-rw-r--r--drivers/power/supply/bq2415x_charger.c36
-rw-r--r--drivers/power/supply/bq24190_charger.c29
-rw-r--r--drivers/power/supply/bq24257_charger.c8
-rw-r--r--drivers/power/supply/bq27xxx_battery.c39
-rw-r--r--drivers/power/supply/charger-manager.c3
-rw-r--r--drivers/power/supply/cpcap-charger.c3
-rw-r--r--drivers/power/supply/cros_charge-control.c200
-rw-r--r--drivers/power/supply/da9030_battery.c3
-rw-r--r--drivers/power/supply/ds2760_battery.c8
-rw-r--r--drivers/power/supply/ds2780_battery.c24
-rw-r--r--drivers/power/supply/ds2781_battery.c24
-rw-r--r--drivers/power/supply/ds2782_battery.c89
-rw-r--r--drivers/power/supply/gpio-charger.c13
-rw-r--r--drivers/power/supply/ip5xxx_power.c572
-rw-r--r--drivers/power/supply/ltc4162-l-charger.c440
-rw-r--r--drivers/power/supply/max17042_battery.c203
-rw-r--r--drivers/power/supply/max1720x_battery.c66
-rw-r--r--drivers/power/supply/mm8013.c2
-rw-r--r--drivers/power/supply/olpc_battery.c11
-rw-r--r--drivers/power/supply/power_supply.h31
-rw-r--r--drivers/power/supply/power_supply_core.c266
-rw-r--r--drivers/power/supply/power_supply_hwmon.c50
-rw-r--r--drivers/power/supply/power_supply_sysfs.c192
-rw-r--r--drivers/power/supply/sbs-battery.c5
-rw-r--r--drivers/power/supply/stc3117_fuel_gauge.c612
-rw-r--r--drivers/power/supply/surface_battery.c4
-rw-r--r--drivers/power/supply/test_power.c113
-rw-r--r--drivers/power/supply/ug3105_battery.c4
41 files changed, 2473 insertions, 678 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-sama5d2_shdwc.c b/drivers/power/reset/at91-sama5d2_shdwc.c
index edb0df86aff4..c2801bd6384d 100644
--- a/drivers/power/reset/at91-sama5d2_shdwc.c
+++ b/drivers/power/reset/at91-sama5d2_shdwc.c
@@ -326,6 +326,7 @@ 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" },
{ /* 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/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..7b18358f194a 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -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..b55cc48a4c86 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -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..7a8d1feb8e90 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);
diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c
index cece8d6753ac..1042d37424f5 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)
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, &reg_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, &reg_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, &reg_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, &reg_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, &reg_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/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/bq2415x_charger.c b/drivers/power/supply/bq2415x_charger.c
index 25e28dac900d..22f6a3b71632 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;
diff --git a/drivers/power/supply/bq24190_charger.c b/drivers/power/supply/bq24190_charger.c
index c47f32f152e6..b4ba01744368 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)
@@ -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..1416586f2459 100644
--- a/drivers/power/supply/bq24257_charger.c
+++ b/drivers/power/supply/bq24257_charger.c
@@ -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/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
index 40c5ac7a1118..90a5bccfc6b9 100644
--- a/drivers/power/supply/bq27xxx_battery.c
+++ b/drivers/power/supply/bq27xxx_battery.c
@@ -123,6 +123,7 @@ 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_MAX, /* sentinel */
};
@@ -159,6 +160,7 @@ static u8
[BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR,
[BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
[BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
+ [BQ27XXX_REG_SEDVF] = 0x77,
},
bq27010_regs[BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_CTRL] = 0x00,
@@ -184,6 +186,7 @@ static u8
[BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR,
[BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
[BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
+ [BQ27XXX_REG_SEDVF] = 0x77,
},
bq2750x_regs[BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_CTRL] = 0x00,
@@ -579,6 +582,7 @@ 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,
};
static enum power_supply_property bq27010_props[] = {
@@ -599,6 +603,7 @@ 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,
};
#define bq2750x_props bq27510g3_props
@@ -2039,6 +2044,36 @@ static int bq27xxx_battery_voltage(struct bq27xxx_device_info *di,
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)
{
@@ -2119,8 +2154,10 @@ 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_CYCLE_COUNT:
ret = bq27xxx_battery_read_cyct(di, val);
break;
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/cpcap-charger.c b/drivers/power/supply/cpcap-charger.c
index 7781b45a67a7..6625d539d9ae 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,
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..ac2e319e9517 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);
diff --git a/drivers/power/supply/ds2760_battery.c b/drivers/power/supply/ds2760_battery.c
index 7cf4ea06b500..83bdec5a2bda 100644
--- a/drivers/power/supply/ds2760_battery.c
+++ b/drivers/power/supply/ds2760_battery.c
@@ -195,22 +195,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[] = {
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/gpio-charger.c b/drivers/power/supply/gpio-charger.c
index 6139f736ecbe..46d18ce6a739 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,
diff --git a/drivers/power/supply/ip5xxx_power.c b/drivers/power/supply/ip5xxx_power.c
index 82263646ddc6..c448e0ac0dfa 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,13 +686,152 @@ 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 = &ip51xx_fields;
struct power_supply_config psy_cfg = {};
struct device *dev = &client->dev;
+ const struct of_device_id *of_id;
struct power_supply *psy;
struct ip5xxx *ip5xxx;
@@ -601,6 +843,11 @@ static int ip5xxx_power_probe(struct i2c_client *client)
if (IS_ERR(ip5xxx->regmap))
return PTR_ERR(ip5xxx->regmap);
+ of_id = i2c_of_match_device(dev->driver->of_match_table, client);
+ if (of_id)
+ fields = (const struct ip5xxx_regfield_config *)of_id->data;
+ ip5xxx_setup_regs(dev, ip5xxx, fields);
+
psy_cfg.of_node = dev->of_node;
psy_cfg.drv_data = ip5xxx;
@@ -616,10 +863,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/ltc4162-l-charger.c b/drivers/power/supply/ltc4162-l-charger.c
index 2e4bc74e1c4a..23eb426295db 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, &regval);
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, &regval);
+ 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, &regval);
+ 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, &regval);
+ 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 */
@@ -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, &ltc4162l_regmap_config);
if (IS_ERR(info->regmap)) {
dev_err(dev, "Failed to initialize register map\n");
@@ -870,8 +1189,15 @@ static int ltc4162l_probe(struct i2c_client *client)
ltc4162l_config.drv_data = info;
ltc4162l_config.attr_grp = ltc4162l_attr_groups;
- info->charger = devm_power_supply_register(dev, &ltc4162l_desc,
- &ltc4162l_config);
+ /* Duplicate the default descriptor to set name based on chip_info. */
+ desc = devm_kmemdup(dev, &ltc4162l_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, &ltc4162l_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)&ltc4015_chip_info },
+ { "ltc4162-f", (kernel_ulong_t)&ltc4162f_chip_info },
+ { "ltc4162-l", (kernel_ulong_t)&ltc4162l_chip_info },
+ { "ltc4162-s", (kernel_ulong_t)&ltc4162s_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 = &ltc4015_chip_info },
+ { .compatible = "lltc,ltc4162-f", .data = &ltc4162f_chip_info },
+ { .compatible = "lltc,ltc4162-l", .data = &ltc4162l_chip_info },
+ { .compatible = "lltc,ltc4162-s", .data = &ltc4162s_chip_info },
+ { }
};
MODULE_DEVICE_TABLE(of, ltc4162l_of_match);
diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c
index 496c3e1f2ee6..655b3f25dbd7 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,33 +1046,25 @@ 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;
@@ -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..11580e414713 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 */
@@ -29,6 +34,7 @@
#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
@@ -112,11 +118,15 @@ static const struct regmap_config max1720x_regmap_cfg = {
};
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),
};
@@ -250,6 +260,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,
};
@@ -362,6 +373,10 @@ static int max1720x_battery_get_property(struct power_supply *psy,
ret = regmap_read(info->regmap, MAX172XX_AVG_CURRENT, &reg_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, &reg_val);
+ val->intval = max172xx_capacity_to_ps(reg_val);
+ break;
case POWER_SUPPLY_PROP_MODEL_NAME:
ret = regmap_read(info->regmap, MAX172XX_DEV_NAME, &reg_val);
reg_val = FIELD_GET(MAX172XX_DEV_NAME_TYPE_MASK, reg_val);
@@ -382,6 +397,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 +545,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/mm8013.c b/drivers/power/supply/mm8013.c
index 5bcfaeeda3db..4adf2acc2779 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;
diff --git a/drivers/power/supply/olpc_battery.c b/drivers/power/supply/olpc_battery.c
index 9f60094a5599..849f63e89ba0 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[] = {
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..d0bb52a7a036 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)
{
- return class_for_each_device(&power_supply_class, NULL, data, fn);
+ 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);
}
-EXPORT_SYMBOL_GPL(power_supply_for_each_device);
+
+int power_supply_for_each_psy(void *data, int (*fn)(struct power_supply *psy, void *data))
+{
+ 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_psy);
void power_supply_changed(struct power_supply *psy)
{
@@ -166,11 +192,10 @@ 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;
@@ -197,20 +222,19 @@ 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 */
+ /* returning non-zero breaks out of power_supply_for_each_psy loop */
if (epsy->of_node == np)
return 1;
@@ -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;
}
@@ -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)
@@ -444,7 +465,7 @@ 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 +488,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 +545,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);
}
@@ -1180,8 +1201,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 +1217,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 +1280,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 +1307,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 +1341,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(&reg->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(&reg->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(&reg->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 +1475,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)
@@ -1402,6 +1577,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;
@@ -1414,13 +1592,15 @@ __power_supply_register(struct device *parent,
if (rc)
goto register_thermal_failed;
- rc = power_supply_create_triggers(psy);
- if (rc)
- goto create_triggers_failed;
+ scoped_guard(rwsem_read, &psy->extensions_sem) {
+ rc = power_supply_create_triggers(psy);
+ if (rc)
+ goto create_triggers_failed;
- rc = power_supply_add_hwmon_sysfs(psy);
- if (rc)
- goto add_hwmon_sysfs_failed;
+ 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..edb058c19c9c 100644
--- a/drivers/power/supply/power_supply_sysfs.c
+++ b/drivers/power/supply/power_supply_sysfs.c
@@ -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/sbs-battery.c b/drivers/power/supply/sbs-battery.c
index a6c204c08232..6f3d0413b1c1 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);
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/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;