summaryrefslogtreecommitdiffstats
path: root/drivers/power
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2021-04-29 00:43:58 +0200
committerLinus Torvalds <torvalds@linux-foundation.org>2021-04-29 00:43:58 +0200
commit5a69e9bce9984806029926f405b4517878e703e2 (patch)
tree97d3a817b17a3f8a2e7d6099c6d96bcb67d1cd66 /drivers/power
parentMerge tag 'hsi-for-5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/sre... (diff)
parentpower: supply: cpcap-battery: fix invalid usage of list cursor (diff)
downloadlinux-5a69e9bce9984806029926f405b4517878e703e2.tar.xz
linux-5a69e9bce9984806029926f405b4517878e703e2.zip
Merge tag 'for-v5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply
Pull power supply and reset updates from Sebastian Reichel: "battery/charger driver changes: - core: - provide function stubs if CONFIG_POWER_SUPPLY=n - reduce loglevel for probe defer info - surface: - new battery and charger drivers for Surface - bq27xxx: - add bq78z100 support - fix current_now/power_avg for newer chips - cw2015: - add CHARGE_NOW support - ab8500: - drop pdata support - convert most DT bindings to YAML - lots of minor fixes and cleanups reset drivers: - ltc2952-poweroff: - make trigger delay configurable from DT - minor fixes and cleanups" * tag 'for-v5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (97 commits) power: supply: cpcap-battery: fix invalid usage of list cursor power: supply: bq256xx: add kerneldoc for structure members power: supply: act8945a: correct kerneldoc power: supply: max17040: remove unneeded double cast power: supply: max17040: handle device_property_read_u8_array() failure power: supply: max14577: remove unneeded variable initialization power: supply: surface-charger: Make symbol 'surface_ac_pm_ops' static power: supply: surface-battery: Make some symbols static power: reset: restart-poweroff: Add missing MODULE_DEVICE_TABLE power: reset: hisi-reboot: add missing MODULE_DEVICE_TABLE power: supply: s3c_adc_battery: fix possible use-after-free in s3c_adc_bat_remove() power: supply: generic-adc-battery: fix possible use-after-free in gab_remove() power: supply: Add AC driver for Surface Aggregator Module power: supply: Add battery driver for Surface Aggregator Module power: supply: bq25980: Move props from battery node power: supply: core: Use true and false for bool variable power: supply: goldfish: Remove the GOLDFISH dependency power: reset: ltc2952: make trigger delay configurable power: supply: cpcap-charger: Simplify bool conversion power: supply: cpcap-charger: Add usleep to cpcap charger to avoid usb plug bounce ...
Diffstat (limited to 'drivers/power')
-rw-r--r--drivers/power/reset/Makefile2
-rw-r--r--drivers/power/reset/at91-reset.c4
-rw-r--r--drivers/power/reset/hisi-reboot.c5
-rw-r--r--drivers/power/reset/ltc2952-poweroff.c8
-rw-r--r--drivers/power/reset/restart-poweroff.c1
-rw-r--r--drivers/power/reset/vexpress-poweroff.c2
-rw-r--r--drivers/power/supply/88pm860x_battery.c4
-rw-r--r--drivers/power/supply/Kconfig40
-rw-r--r--drivers/power/supply/Makefile2
-rw-r--r--drivers/power/supply/ab8500-bm.h733
-rw-r--r--drivers/power/supply/ab8500-chargalg.h51
-rw-r--r--drivers/power/supply/ab8500_bmdata.c3
-rw-r--r--drivers/power/supply/ab8500_btemp.c45
-rw-r--r--drivers/power/supply/ab8500_charger.c27
-rw-r--r--drivers/power/supply/ab8500_fg.c20
-rw-r--r--drivers/power/supply/abx500_chargalg.c22
-rw-r--r--drivers/power/supply/act8945a_charger.c2
-rw-r--r--drivers/power/supply/axp20x_usb_power.c4
-rw-r--r--drivers/power/supply/bq256xx_charger.c2
-rw-r--r--drivers/power/supply/bq25980_charger.c40
-rw-r--r--drivers/power/supply/bq27xxx_battery.c169
-rw-r--r--drivers/power/supply/bq27xxx_battery_i2c.c2
-rw-r--r--drivers/power/supply/charger-manager.c2
-rw-r--r--drivers/power/supply/cpcap-battery.c2
-rw-r--r--drivers/power/supply/cpcap-charger.c7
-rw-r--r--drivers/power/supply/cw2015_battery.c6
-rw-r--r--drivers/power/supply/ds2781_battery.c8
-rw-r--r--drivers/power/supply/generic-adc-battery.c2
-rw-r--r--drivers/power/supply/lp8788-charger.c2
-rw-r--r--drivers/power/supply/max14577_charger.c2
-rw-r--r--drivers/power/supply/max17040_battery.c12
-rw-r--r--drivers/power/supply/max17042_battery.c8
-rw-r--r--drivers/power/supply/max1721x_battery.c14
-rw-r--r--drivers/power/supply/max8997_charger.c14
-rw-r--r--drivers/power/supply/pm2301_charger.c6
-rw-r--r--drivers/power/supply/power_supply_core.c4
-rw-r--r--drivers/power/supply/s3c_adc_battery.c21
-rw-r--r--drivers/power/supply/sbs-battery.c28
-rw-r--r--drivers/power/supply/sbs-charger.c24
-rw-r--r--drivers/power/supply/sbs-manager.c78
-rw-r--r--drivers/power/supply/smb347-charger.c65
-rw-r--r--drivers/power/supply/surface_battery.c865
-rw-r--r--drivers/power/supply/surface_charger.c282
-rw-r--r--drivers/power/supply/tps65090-charger.c2
-rw-r--r--drivers/power/supply/tps65217_charger.c2
-rw-r--r--drivers/power/supply/z2_battery.c3
46 files changed, 2291 insertions, 356 deletions
diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile
index 4f959b697606..cf3f4d02d8a5 100644
--- a/drivers/power/reset/Makefile
+++ b/drivers/power/reset/Makefile
@@ -11,7 +11,7 @@ obj-$(CONFIG_POWER_RESET_GEMINI_POWEROFF) += gemini-poweroff.o
obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o
obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o
-obj-${CONFIG_POWER_RESET_LINKSTATION} += linkstation-poweroff.o
+obj-$(CONFIG_POWER_RESET_LINKSTATION) += linkstation-poweroff.o
obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o
obj-$(CONFIG_POWER_RESET_MT6323) += mt6323-poweroff.o
obj-$(CONFIG_POWER_RESET_OXNAS) += oxnas-restart.o
diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c
index 3ff9d93a5226..026649409135 100644
--- a/drivers/power/reset/at91-reset.c
+++ b/drivers/power/reset/at91-reset.c
@@ -192,7 +192,7 @@ static int __init at91_reset_probe(struct platform_device *pdev)
if (!reset)
return -ENOMEM;
- reset->rstc_base = of_iomap(pdev->dev.of_node, 0);
+ reset->rstc_base = devm_of_iomap(&pdev->dev, pdev->dev.of_node, 0, NULL);
if (!reset->rstc_base) {
dev_err(&pdev->dev, "Could not map reset controller address\n");
return -ENODEV;
@@ -202,7 +202,7 @@ static int __init at91_reset_probe(struct platform_device *pdev)
/* we need to shutdown the ddr controller, so get ramc base */
for_each_matching_node_and_match(np, at91_ramc_of_match, &match) {
reset->ramc_lpr = (u32)match->data;
- reset->ramc_base[idx] = of_iomap(np, 0);
+ reset->ramc_base[idx] = devm_of_iomap(&pdev->dev, np, 0, NULL);
if (!reset->ramc_base[idx]) {
dev_err(&pdev->dev, "Could not map ram controller address\n");
of_node_put(np);
diff --git a/drivers/power/reset/hisi-reboot.c b/drivers/power/reset/hisi-reboot.c
index 0ba5fdce186f..34409340528f 100644
--- a/drivers/power/reset/hisi-reboot.c
+++ b/drivers/power/reset/hisi-reboot.c
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Hisilicon SoC reset code
+ * HiSilicon SoC reset code
*
- * Copyright (c) 2014 Hisilicon Ltd.
+ * Copyright (c) 2014 HiSilicon Ltd.
* Copyright (c) 2014 Linaro Ltd.
*
* Author: Haojian Zhuang <haojian.zhuang@linaro.org>
@@ -68,6 +68,7 @@ static const struct of_device_id hisi_reboot_of_match[] = {
{ .compatible = "hisilicon,sysctrl" },
{}
};
+MODULE_DEVICE_TABLE(of, hisi_reboot_of_match);
static struct platform_driver hisi_reboot_driver = {
.probe = hisi_reboot_probe,
diff --git a/drivers/power/reset/ltc2952-poweroff.c b/drivers/power/reset/ltc2952-poweroff.c
index 318927938b05..d1495af30081 100644
--- a/drivers/power/reset/ltc2952-poweroff.c
+++ b/drivers/power/reset/ltc2952-poweroff.c
@@ -55,6 +55,7 @@
#include <linux/mod_devicetable.h>
#include <linux/gpio/consumer.h>
#include <linux/reboot.h>
+#include <linux/property.h>
struct ltc2952_poweroff {
struct hrtimer timer_trigger;
@@ -172,10 +173,17 @@ static void ltc2952_poweroff_default(struct ltc2952_poweroff *data)
static int ltc2952_poweroff_init(struct platform_device *pdev)
{
int ret;
+ u32 trigger_delay_ms;
struct ltc2952_poweroff *data = platform_get_drvdata(pdev);
ltc2952_poweroff_default(data);
+ if (!device_property_read_u32(&pdev->dev, "trigger-delay-ms",
+ &trigger_delay_ms)) {
+ data->trigger_delay = ktime_set(trigger_delay_ms / MSEC_PER_SEC,
+ (trigger_delay_ms % MSEC_PER_SEC) * NSEC_PER_MSEC);
+ }
+
data->gpio_watchdog = devm_gpiod_get(&pdev->dev, "watchdog",
GPIOD_OUT_LOW);
if (IS_ERR(data->gpio_watchdog)) {
diff --git a/drivers/power/reset/restart-poweroff.c b/drivers/power/reset/restart-poweroff.c
index d233daa5835b..04d4228119b2 100644
--- a/drivers/power/reset/restart-poweroff.c
+++ b/drivers/power/reset/restart-poweroff.c
@@ -45,6 +45,7 @@ static const struct of_device_id of_restart_poweroff_match[] = {
{ .compatible = "restart-poweroff", },
{},
};
+MODULE_DEVICE_TABLE(of, of_restart_poweroff_match);
static struct platform_driver restart_poweroff_driver = {
.probe = restart_poweroff_probe,
diff --git a/drivers/power/reset/vexpress-poweroff.c b/drivers/power/reset/vexpress-poweroff.c
index 1fdbcbd95fc2..447ffdacddf9 100644
--- a/drivers/power/reset/vexpress-poweroff.c
+++ b/drivers/power/reset/vexpress-poweroff.c
@@ -133,7 +133,7 @@ static int vexpress_reset_probe(struct platform_device *pdev)
case FUNC_REBOOT:
ret = _vexpress_register_restart_handler(&pdev->dev);
break;
- };
+ }
return ret;
}
diff --git a/drivers/power/supply/88pm860x_battery.c b/drivers/power/supply/88pm860x_battery.c
index 590da88a17a2..f3f3f8cd1a7f 100644
--- a/drivers/power/supply/88pm860x_battery.c
+++ b/drivers/power/supply/88pm860x_battery.c
@@ -109,8 +109,8 @@ struct pm860x_battery_info {
};
struct ccnt {
- unsigned long long int pos;
- unsigned long long int neg;
+ unsigned long long pos;
+ unsigned long long neg;
unsigned int spos;
unsigned int sneg;
diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index 006b95eca673..e696364126f1 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -698,17 +698,17 @@ config BATTERY_GAUGE_LTC2941
config AB8500_BM
bool "AB8500 Battery Management Driver"
- depends on AB8500_CORE && AB8500_GPADC && (IIO = y)
+ depends on AB8500_CORE && AB8500_GPADC && (IIO = y) && OF
help
Say Y to include support for AB8500 battery management.
config BATTERY_GOLDFISH
tristate "Goldfish battery driver"
- depends on GOLDFISH || COMPILE_TEST
depends on HAS_IOMEM
help
- Say Y to enable support for the battery and AC power in the
- Goldfish emulator.
+ Say Y to enable support for the Goldfish battery and AC power
+ driver. Originated in the Android Studio Emulator (goldfish) it is
+ going to be used in other emulators.
config BATTERY_RT5033
tristate "RT5033 fuel gauge support"
@@ -801,4 +801,36 @@ config BATTERY_ACER_A500
help
Say Y to include support for Acer Iconia Tab A500 battery fuel gauge.
+config BATTERY_SURFACE
+ tristate "Battery driver for 7th-generation Microsoft Surface devices"
+ depends on SURFACE_AGGREGATOR_REGISTRY
+ help
+ Driver for battery devices connected via/managed by the Surface System
+ Aggregator Module (SSAM).
+
+ This driver provides battery-information and -status support for
+ Surface devices where said data is not exposed via the standard ACPI
+ devices. On those models (7th-generation), battery-information is
+ instead handled directly via SSAM client devices and this driver.
+
+ Say M or Y here to include battery status support for 7th-generation
+ Microsoft Surface devices, i.e. Surface Pro 7, Surface Laptop 3,
+ Surface Book 3, and Surface Laptop Go.
+
+config CHARGER_SURFACE
+ tristate "AC driver for 7th-generation Microsoft Surface devices"
+ depends on SURFACE_AGGREGATOR_REGISTRY
+ help
+ Driver for AC devices connected via/managed by the Surface System
+ Aggregator Module (SSAM).
+
+ This driver provides AC-information and -status support for Surface
+ devices where said data is not exposed via the standard ACPI devices.
+ On those models (7th-generation), AC-information is instead handled
+ directly via a SSAM client device and this driver.
+
+ Say M or Y here to include AC status support for 7th-generation
+ Microsoft Surface devices, i.e. Surface Pro 7, Surface Laptop 3,
+ Surface Book 3, and Surface Laptop Go.
+
endif # POWER_SUPPLY
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
index 5e5fdbbef531..a7309a3d1a47 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -101,3 +101,5 @@ obj-$(CONFIG_CHARGER_BD99954) += bd99954-charger.o
obj-$(CONFIG_CHARGER_WILCO) += wilco-charger.o
obj-$(CONFIG_RN5T618_POWER) += rn5t618_power.o
obj-$(CONFIG_BATTERY_ACER_A500) += acer_a500_battery.o
+obj-$(CONFIG_BATTERY_SURFACE) += surface_battery.o
+obj-$(CONFIG_CHARGER_SURFACE) += surface_charger.o
diff --git a/drivers/power/supply/ab8500-bm.h b/drivers/power/supply/ab8500-bm.h
new file mode 100644
index 000000000000..41c69a4f2a1f
--- /dev/null
+++ b/drivers/power/supply/ab8500-bm.h
@@ -0,0 +1,733 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _AB8500_CHARGER_H_
+#define _AB8500_CHARGER_H_
+
+#include <linux/kernel.h>
+
+/*
+ * System control 2 register offsets.
+ * bank = 0x02
+ */
+#define AB8500_MAIN_WDOG_CTRL_REG 0x01
+#define AB8500_LOW_BAT_REG 0x03
+#define AB8500_BATT_OK_REG 0x04
+/*
+ * USB/ULPI register offsets
+ * Bank : 0x5
+ */
+#define AB8500_USB_LINE_STAT_REG 0x80
+#define AB8500_USB_LINE_CTRL2_REG 0x82
+#define AB8500_USB_LINK1_STAT_REG 0x94
+
+/*
+ * Charger / status register offfsets
+ * Bank : 0x0B
+ */
+#define AB8500_CH_STATUS1_REG 0x00
+#define AB8500_CH_STATUS2_REG 0x01
+#define AB8500_CH_USBCH_STAT1_REG 0x02
+#define AB8500_CH_USBCH_STAT2_REG 0x03
+#define AB8540_CH_USBCH_STAT3_REG 0x04
+#define AB8500_CH_STAT_REG 0x05
+
+/*
+ * Charger / control register offfsets
+ * Bank : 0x0B
+ */
+#define AB8500_CH_VOLT_LVL_REG 0x40
+#define AB8500_CH_VOLT_LVL_MAX_REG 0x41 /*Only in Cut2.0*/
+#define AB8500_CH_OPT_CRNTLVL_REG 0x42
+#define AB8500_CH_OPT_CRNTLVL_MAX_REG 0x43 /*Only in Cut2.0*/
+#define AB8500_CH_WD_TIMER_REG 0x50
+#define AB8500_CHARG_WD_CTRL 0x51
+#define AB8500_BTEMP_HIGH_TH 0x52
+#define AB8500_LED_INDICATOR_PWM_CTRL 0x53
+#define AB8500_LED_INDICATOR_PWM_DUTY 0x54
+#define AB8500_BATT_OVV 0x55
+#define AB8500_CHARGER_CTRL 0x56
+#define AB8500_BAT_CTRL_CURRENT_SOURCE 0x60 /*Only in Cut2.0*/
+
+/*
+ * Charger / main control register offsets
+ * Bank : 0x0B
+ */
+#define AB8500_MCH_CTRL1 0x80
+#define AB8500_MCH_CTRL2 0x81
+#define AB8500_MCH_IPT_CURLVL_REG 0x82
+#define AB8500_CH_WD_REG 0x83
+
+/*
+ * Charger / USB control register offsets
+ * Bank : 0x0B
+ */
+#define AB8500_USBCH_CTRL1_REG 0xC0
+#define AB8500_USBCH_CTRL2_REG 0xC1
+#define AB8500_USBCH_IPT_CRNTLVL_REG 0xC2
+#define AB8540_USB_PP_MODE_REG 0xC5
+#define AB8540_USB_PP_CHR_REG 0xC6
+
+/*
+ * Gas Gauge register offsets
+ * Bank : 0x0C
+ */
+#define AB8500_GASG_CC_CTRL_REG 0x00
+#define AB8500_GASG_CC_ACCU1_REG 0x01
+#define AB8500_GASG_CC_ACCU2_REG 0x02
+#define AB8500_GASG_CC_ACCU3_REG 0x03
+#define AB8500_GASG_CC_ACCU4_REG 0x04
+#define AB8500_GASG_CC_SMPL_CNTRL_REG 0x05
+#define AB8500_GASG_CC_SMPL_CNTRH_REG 0x06
+#define AB8500_GASG_CC_SMPL_CNVL_REG 0x07
+#define AB8500_GASG_CC_SMPL_CNVH_REG 0x08
+#define AB8500_GASG_CC_CNTR_AVGOFF_REG 0x09
+#define AB8500_GASG_CC_OFFSET_REG 0x0A
+#define AB8500_GASG_CC_NCOV_ACCU 0x10
+#define AB8500_GASG_CC_NCOV_ACCU_CTRL 0x11
+#define AB8500_GASG_CC_NCOV_ACCU_LOW 0x12
+#define AB8500_GASG_CC_NCOV_ACCU_MED 0x13
+#define AB8500_GASG_CC_NCOV_ACCU_HIGH 0x14
+
+/*
+ * Interrupt register offsets
+ * Bank : 0x0E
+ */
+#define AB8500_IT_SOURCE2_REG 0x01
+#define AB8500_IT_SOURCE21_REG 0x14
+
+/*
+ * RTC register offsets
+ * Bank: 0x0F
+ */
+#define AB8500_RTC_BACKUP_CHG_REG 0x0C
+#define AB8500_RTC_CC_CONF_REG 0x01
+#define AB8500_RTC_CTRL_REG 0x0B
+#define AB8500_RTC_CTRL1_REG 0x11
+
+/*
+ * OTP register offsets
+ * Bank : 0x15
+ */
+#define AB8500_OTP_CONF_15 0x0E
+
+/* GPADC constants from AB8500 spec, UM0836 */
+#define ADC_RESOLUTION 1024
+#define ADC_CH_MAIN_MIN 0
+#define ADC_CH_MAIN_MAX 20030
+#define ADC_CH_VBUS_MIN 0
+#define ADC_CH_VBUS_MAX 20030
+#define ADC_CH_VBAT_MIN 2300
+#define ADC_CH_VBAT_MAX 4800
+#define ADC_CH_BKBAT_MIN 0
+#define ADC_CH_BKBAT_MAX 3200
+
+/* Main charge i/p current */
+#define MAIN_CH_IP_CUR_0P9A 0x80
+#define MAIN_CH_IP_CUR_1P0A 0x90
+#define MAIN_CH_IP_CUR_1P1A 0xA0
+#define MAIN_CH_IP_CUR_1P2A 0xB0
+#define MAIN_CH_IP_CUR_1P3A 0xC0
+#define MAIN_CH_IP_CUR_1P4A 0xD0
+#define MAIN_CH_IP_CUR_1P5A 0xE0
+
+/* ChVoltLevel */
+#define CH_VOL_LVL_3P5 0x00
+#define CH_VOL_LVL_4P0 0x14
+#define CH_VOL_LVL_4P05 0x16
+#define CH_VOL_LVL_4P1 0x1B
+#define CH_VOL_LVL_4P15 0x20
+#define CH_VOL_LVL_4P2 0x25
+#define CH_VOL_LVL_4P6 0x4D
+
+/* ChOutputCurrentLevel */
+#define CH_OP_CUR_LVL_0P1 0x00
+#define CH_OP_CUR_LVL_0P2 0x01
+#define CH_OP_CUR_LVL_0P3 0x02
+#define CH_OP_CUR_LVL_0P4 0x03
+#define CH_OP_CUR_LVL_0P5 0x04
+#define CH_OP_CUR_LVL_0P6 0x05
+#define CH_OP_CUR_LVL_0P7 0x06
+#define CH_OP_CUR_LVL_0P8 0x07
+#define CH_OP_CUR_LVL_0P9 0x08
+#define CH_OP_CUR_LVL_1P4 0x0D
+#define CH_OP_CUR_LVL_1P5 0x0E
+#define CH_OP_CUR_LVL_1P6 0x0F
+#define CH_OP_CUR_LVL_2P 0x3F
+
+/* BTEMP High thermal limits */
+#define BTEMP_HIGH_TH_57_0 0x00
+#define BTEMP_HIGH_TH_52 0x01
+#define BTEMP_HIGH_TH_57_1 0x02
+#define BTEMP_HIGH_TH_62 0x03
+
+/* current is mA */
+#define USB_0P1A 100
+#define USB_0P2A 200
+#define USB_0P3A 300
+#define USB_0P4A 400
+#define USB_0P5A 500
+
+#define LOW_BAT_3P1V 0x20
+#define LOW_BAT_2P3V 0x00
+#define LOW_BAT_RESET 0x01
+#define LOW_BAT_ENABLE 0x01
+
+/* Backup battery constants */
+#define BUP_ICH_SEL_50UA 0x00
+#define BUP_ICH_SEL_150UA 0x04
+#define BUP_ICH_SEL_300UA 0x08
+#define BUP_ICH_SEL_700UA 0x0C
+
+enum bup_vch_sel {
+ BUP_VCH_SEL_2P5V,
+ BUP_VCH_SEL_2P6V,
+ BUP_VCH_SEL_2P8V,
+ BUP_VCH_SEL_3P1V,
+ /*
+ * Note that the following 5 values 2.7v, 2.9v, 3.0v, 3.2v, 3.3v
+ * are only available on ab8540. You can't choose these 5
+ * voltage on ab8500/ab8505/ab9540.
+ */
+ BUP_VCH_SEL_2P7V,
+ BUP_VCH_SEL_2P9V,
+ BUP_VCH_SEL_3P0V,
+ BUP_VCH_SEL_3P2V,
+ BUP_VCH_SEL_3P3V,
+};
+
+#define BUP_VCH_RANGE 0x02
+#define VBUP33_VRTCN 0x01
+
+/* Battery OVV constants */
+#define BATT_OVV_ENA 0x02
+#define BATT_OVV_TH_3P7 0x00
+#define BATT_OVV_TH_4P75 0x01
+
+/* A value to indicate over voltage */
+#define BATT_OVV_VALUE 4750
+
+/* VBUS OVV constants */
+#define VBUS_OVV_SELECT_MASK 0x78
+#define VBUS_OVV_SELECT_5P6V 0x00
+#define VBUS_OVV_SELECT_5P7V 0x08
+#define VBUS_OVV_SELECT_5P8V 0x10
+#define VBUS_OVV_SELECT_5P9V 0x18
+#define VBUS_OVV_SELECT_6P0V 0x20
+#define VBUS_OVV_SELECT_6P1V 0x28
+#define VBUS_OVV_SELECT_6P2V 0x30
+#define VBUS_OVV_SELECT_6P3V 0x38
+
+#define VBUS_AUTO_IN_CURR_LIM_ENA 0x04
+
+/* Fuel Gauge constants */
+#define RESET_ACCU 0x02
+#define READ_REQ 0x01
+#define CC_DEEP_SLEEP_ENA 0x02
+#define CC_PWR_UP_ENA 0x01
+#define CC_SAMPLES_40 0x28
+#define RD_NCONV_ACCU_REQ 0x01
+#define CC_CALIB 0x08
+#define CC_INTAVGOFFSET_ENA 0x10
+#define CC_MUXOFFSET 0x80
+#define CC_INT_CAL_N_AVG_MASK 0x60
+#define CC_INT_CAL_SAMPLES_16 0x40
+#define CC_INT_CAL_SAMPLES_8 0x20
+#define CC_INT_CAL_SAMPLES_4 0x00
+
+/* RTC constants */
+#define RTC_BUP_CH_ENA 0x10
+
+/* BatCtrl Current Source Constants */
+#define BAT_CTRL_7U_ENA 0x01
+#define BAT_CTRL_20U_ENA 0x02
+#define BAT_CTRL_18U_ENA 0x01
+#define BAT_CTRL_16U_ENA 0x02
+#define BAT_CTRL_CMP_ENA 0x04
+#define FORCE_BAT_CTRL_CMP_HIGH 0x08
+#define BAT_CTRL_PULL_UP_ENA 0x10
+
+/* Battery type */
+#define BATTERY_UNKNOWN 00
+
+/* Registers for pcut feature in ab8505 and ab9540 */
+#define AB8505_RTC_PCUT_CTL_STATUS_REG 0x12
+#define AB8505_RTC_PCUT_TIME_REG 0x13
+#define AB8505_RTC_PCUT_MAX_TIME_REG 0x14
+#define AB8505_RTC_PCUT_FLAG_TIME_REG 0x15
+#define AB8505_RTC_PCUT_RESTART_REG 0x16
+#define AB8505_RTC_PCUT_DEBOUNCE_REG 0x17
+
+/* USB Power Path constants for ab8540 */
+#define BUS_VSYS_VOL_SELECT_MASK 0x06
+#define BUS_VSYS_VOL_SELECT_3P6V 0x00
+#define BUS_VSYS_VOL_SELECT_3P325V 0x02
+#define BUS_VSYS_VOL_SELECT_3P9V 0x04
+#define BUS_VSYS_VOL_SELECT_4P3V 0x06
+#define BUS_POWER_PATH_MODE_ENA 0x01
+#define BUS_PP_PRECHG_CURRENT_MASK 0x0E
+#define BUS_POWER_PATH_PRECHG_ENA 0x01
+
+/*
+ * ADC for the battery thermistor.
+ * When using the ABx500_ADC_THERM_BATCTRL the battery ID resistor is combined
+ * with a NTC resistor to both identify the battery and to measure its
+ * temperature. Different phone manufactures uses different techniques to both
+ * identify the battery and to read its temperature.
+ */
+enum abx500_adc_therm {
+ ABx500_ADC_THERM_BATCTRL,
+ ABx500_ADC_THERM_BATTEMP,
+};
+
+/**
+ * struct abx500_res_to_temp - defines one point in a temp to res curve. To
+ * be used in battery packs that combines the identification resistor with a
+ * NTC resistor.
+ * @temp: battery pack temperature in Celsius
+ * @resist: NTC resistor net total resistance
+ */
+struct abx500_res_to_temp {
+ int temp;
+ int resist;
+};
+
+/**
+ * struct abx500_v_to_cap - Table for translating voltage to capacity
+ * @voltage: Voltage in mV
+ * @capacity: Capacity in percent
+ */
+struct abx500_v_to_cap {
+ int voltage;
+ int capacity;
+};
+
+/* Forward declaration */
+struct abx500_fg;
+
+/**
+ * struct abx500_fg_parameters - Fuel gauge algorithm parameters, in seconds
+ * if not specified
+ * @recovery_sleep_timer: Time between measurements while recovering
+ * @recovery_total_time: Total recovery time
+ * @init_timer: Measurement interval during startup
+ * @init_discard_time: Time we discard voltage measurement at startup
+ * @init_total_time: Total init time during startup
+ * @high_curr_time: Time current has to be high to go to recovery
+ * @accu_charging: FG accumulation time while charging
+ * @accu_high_curr: FG accumulation time in high current mode
+ * @high_curr_threshold: High current threshold, in mA
+ * @lowbat_threshold: Low battery threshold, in mV
+ * @overbat_threshold: Over battery threshold, in mV
+ * @battok_falling_th_sel0 Threshold in mV for battOk signal sel0
+ * Resolution in 50 mV step.
+ * @battok_raising_th_sel1 Threshold in mV for battOk signal sel1
+ * Resolution in 50 mV step.
+ * @user_cap_limit Capacity reported from user must be within this
+ * limit to be considered as sane, in percentage
+ * points.
+ * @maint_thres This is the threshold where we stop reporting
+ * battery full while in maintenance, in per cent
+ * @pcut_enable: Enable power cut feature in ab8505
+ * @pcut_max_time: Max time threshold
+ * @pcut_flag_time: Flagtime threshold
+ * @pcut_max_restart: Max number of restarts
+ * @pcut_debounce_time: Sets battery debounce time
+ */
+struct abx500_fg_parameters {
+ int recovery_sleep_timer;
+ int recovery_total_time;
+ int init_timer;
+ int init_discard_time;
+ int init_total_time;
+ int high_curr_time;
+ int accu_charging;
+ int accu_high_curr;
+ int high_curr_threshold;
+ int lowbat_threshold;
+ int overbat_threshold;
+ int battok_falling_th_sel0;
+ int battok_raising_th_sel1;
+ int user_cap_limit;
+ int maint_thres;
+ bool pcut_enable;
+ u8 pcut_max_time;
+ u8 pcut_flag_time;
+ u8 pcut_max_restart;
+ u8 pcut_debounce_time;
+};
+
+/**
+ * struct abx500_charger_maximization - struct used by the board config.
+ * @use_maxi: Enable maximization for this battery type
+ * @maxi_chg_curr: Maximum charger current allowed
+ * @maxi_wait_cycles: cycles to wait before setting charger current
+ * @charger_curr_step delta between two charger current settings (mA)
+ */
+struct abx500_maxim_parameters {
+ bool ena_maxi;
+ int chg_curr;
+ int wait_cycles;
+ int charger_curr_step;
+};
+
+/**
+ * struct abx500_battery_type - different batteries supported
+ * @name: battery technology
+ * @resis_high: battery upper resistance limit
+ * @resis_low: battery lower resistance limit
+ * @charge_full_design: Maximum battery capacity in mAh
+ * @nominal_voltage: Nominal voltage of the battery in mV
+ * @termination_vol: max voltage upto which battery can be charged
+ * @termination_curr battery charging termination current in mA
+ * @recharge_cap battery capacity limit that will trigger a new
+ * full charging cycle in the case where maintenan-
+ * -ce charging has been disabled
+ * @normal_cur_lvl: charger current in normal state in mA
+ * @normal_vol_lvl: charger voltage in normal state in mV
+ * @maint_a_cur_lvl: charger current in maintenance A state in mA
+ * @maint_a_vol_lvl: charger voltage in maintenance A state in mV
+ * @maint_a_chg_timer_h: charge time in maintenance A state
+ * @maint_b_cur_lvl: charger current in maintenance B state in mA
+ * @maint_b_vol_lvl: charger voltage in maintenance B state in mV
+ * @maint_b_chg_timer_h: charge time in maintenance B state
+ * @low_high_cur_lvl: charger current in temp low/high state in mA
+ * @low_high_vol_lvl: charger voltage in temp low/high state in mV'
+ * @battery_resistance: battery inner resistance in mOhm.
+ * @n_r_t_tbl_elements: number of elements in r_to_t_tbl
+ * @r_to_t_tbl: table containing resistance to temp points
+ * @n_v_cap_tbl_elements: number of elements in v_to_cap_tbl
+ * @v_to_cap_tbl: Voltage to capacity (in %) table
+ * @n_batres_tbl_elements number of elements in the batres_tbl
+ * @batres_tbl battery internal resistance vs temperature table
+ */
+struct abx500_battery_type {
+ int name;
+ int resis_high;
+ int resis_low;
+ int charge_full_design;
+ int nominal_voltage;
+ int termination_vol;
+ int termination_curr;
+ int recharge_cap;
+ int normal_cur_lvl;
+ int normal_vol_lvl;
+ int maint_a_cur_lvl;
+ int maint_a_vol_lvl;
+ int maint_a_chg_timer_h;
+ int maint_b_cur_lvl;
+ int maint_b_vol_lvl;
+ int maint_b_chg_timer_h;
+ int low_high_cur_lvl;
+ int low_high_vol_lvl;
+ int battery_resistance;
+ int n_temp_tbl_elements;
+ const struct abx500_res_to_temp *r_to_t_tbl;
+ int n_v_cap_tbl_elements;
+ const struct abx500_v_to_cap *v_to_cap_tbl;
+ int n_batres_tbl_elements;
+ const struct batres_vs_temp *batres_tbl;
+};
+
+/**
+ * struct abx500_bm_capacity_levels - abx500 capacity level data
+ * @critical: critical capacity level in percent
+ * @low: low capacity level in percent
+ * @normal: normal capacity level in percent
+ * @high: high capacity level in percent
+ * @full: full capacity level in percent
+ */
+struct abx500_bm_capacity_levels {
+ int critical;
+ int low;
+ int normal;
+ int high;
+ int full;
+};
+
+/**
+ * struct abx500_bm_charger_parameters - Charger specific parameters
+ * @usb_volt_max: maximum allowed USB charger voltage in mV
+ * @usb_curr_max: maximum allowed USB charger current in mA
+ * @ac_volt_max: maximum allowed AC charger voltage in mV
+ * @ac_curr_max: maximum allowed AC charger current in mA
+ */
+struct abx500_bm_charger_parameters {
+ int usb_volt_max;
+ int usb_curr_max;
+ int ac_volt_max;
+ int ac_curr_max;
+};
+
+/**
+ * struct abx500_bm_data - abx500 battery management data
+ * @temp_under under this temp, charging is stopped
+ * @temp_low between this temp and temp_under charging is reduced
+ * @temp_high between this temp and temp_over charging is reduced
+ * @temp_over over this temp, charging is stopped
+ * @temp_now present battery temperature
+ * @temp_interval_chg temperature measurement interval in s when charging
+ * @temp_interval_nochg temperature measurement interval in s when not charging
+ * @main_safety_tmr_h safety timer for main charger
+ * @usb_safety_tmr_h safety timer for usb charger
+ * @bkup_bat_v voltage which we charge the backup battery with
+ * @bkup_bat_i current which we charge the backup battery with
+ * @no_maintenance indicates that maintenance charging is disabled
+ * @capacity_scaling indicates whether capacity scaling is to be used
+ * @abx500_adc_therm placement of thermistor, batctrl or battemp adc
+ * @chg_unknown_bat flag to enable charging of unknown batteries
+ * @enable_overshoot flag to enable VBAT overshoot control
+ * @auto_trig flag to enable auto adc trigger
+ * @fg_res resistance of FG resistor in 0.1mOhm
+ * @n_btypes number of elements in array bat_type
+ * @batt_id index of the identified battery in array bat_type
+ * @interval_charging charge alg cycle period time when charging (sec)
+ * @interval_not_charging charge alg cycle period time when not charging (sec)
+ * @temp_hysteresis temperature hysteresis
+ * @gnd_lift_resistance Battery ground to phone ground resistance (mOhm)
+ * @n_chg_out_curr number of elements in array chg_output_curr
+ * @n_chg_in_curr number of elements in array chg_input_curr
+ * @chg_output_curr charger output current level map
+ * @chg_input_curr charger input current level map
+ * @maxi maximization parameters
+ * @cap_levels capacity in percent for the different capacity levels
+ * @bat_type table of supported battery types
+ * @chg_params charger parameters
+ * @fg_params fuel gauge parameters
+ */
+struct abx500_bm_data {
+ int temp_under;
+ int temp_low;
+ int temp_high;
+ int temp_over;
+ int temp_now;
+ int temp_interval_chg;
+ int temp_interval_nochg;
+ int main_safety_tmr_h;
+ int usb_safety_tmr_h;
+ int bkup_bat_v;
+ int bkup_bat_i;
+ bool autopower_cfg;
+ bool ac_enabled;
+ bool usb_enabled;
+ bool no_maintenance;
+ bool capacity_scaling;
+ bool chg_unknown_bat;
+ bool enable_overshoot;
+ bool auto_trig;
+ enum abx500_adc_therm adc_therm;
+ int fg_res;
+ int n_btypes;
+ int batt_id;
+ int interval_charging;
+ int interval_not_charging;
+ int temp_hysteresis;
+ int gnd_lift_resistance;
+ int n_chg_out_curr;
+ int n_chg_in_curr;
+ int *chg_output_curr;
+ int *chg_input_curr;
+ const struct abx500_maxim_parameters *maxi;
+ const struct abx500_bm_capacity_levels *cap_levels;
+ struct abx500_battery_type *bat_type;
+ const struct abx500_bm_charger_parameters *chg_params;
+ const struct abx500_fg_parameters *fg_params;
+};
+
+enum {
+ NTC_EXTERNAL = 0,
+ NTC_INTERNAL,
+};
+
+/**
+ * struct res_to_temp - defines one point in a temp to res curve. To
+ * be used in battery packs that combines the identification resistor with a
+ * NTC resistor.
+ * @temp: battery pack temperature in Celsius
+ * @resist: NTC resistor net total resistance
+ */
+struct res_to_temp {
+ int temp;
+ int resist;
+};
+
+/**
+ * struct batres_vs_temp - defines one point in a temp vs battery internal
+ * resistance curve.
+ * @temp: battery pack temperature in Celsius
+ * @resist: battery internal reistance in mOhm
+ */
+struct batres_vs_temp {
+ int temp;
+ int resist;
+};
+
+/* Forward declaration */
+struct ab8500_fg;
+
+/**
+ * struct ab8500_fg_parameters - Fuel gauge algorithm parameters, in seconds
+ * if not specified
+ * @recovery_sleep_timer: Time between measurements while recovering
+ * @recovery_total_time: Total recovery time
+ * @init_timer: Measurement interval during startup
+ * @init_discard_time: Time we discard voltage measurement at startup
+ * @init_total_time: Total init time during startup
+ * @high_curr_time: Time current has to be high to go to recovery
+ * @accu_charging: FG accumulation time while charging
+ * @accu_high_curr: FG accumulation time in high current mode
+ * @high_curr_threshold: High current threshold, in mA
+ * @lowbat_threshold: Low battery threshold, in mV
+ * @battok_falling_th_sel0 Threshold in mV for battOk signal sel0
+ * Resolution in 50 mV step.
+ * @battok_raising_th_sel1 Threshold in mV for battOk signal sel1
+ * Resolution in 50 mV step.
+ * @user_cap_limit Capacity reported from user must be within this
+ * limit to be considered as sane, in percentage
+ * points.
+ * @maint_thres This is the threshold where we stop reporting
+ * battery full while in maintenance, in per cent
+ * @pcut_enable: Enable power cut feature in ab8505
+ * @pcut_max_time: Max time threshold
+ * @pcut_flag_time: Flagtime threshold
+ * @pcut_max_restart: Max number of restarts
+ * @pcut_debunce_time: Sets battery debounce time
+ */
+struct ab8500_fg_parameters {
+ int recovery_sleep_timer;
+ int recovery_total_time;
+ int init_timer;
+ int init_discard_time;
+ int init_total_time;
+ int high_curr_time;
+ int accu_charging;
+ int accu_high_curr;
+ int high_curr_threshold;
+ int lowbat_threshold;
+ int battok_falling_th_sel0;
+ int battok_raising_th_sel1;
+ int user_cap_limit;
+ int maint_thres;
+ bool pcut_enable;
+ u8 pcut_max_time;
+ u8 pcut_flag_time;
+ u8 pcut_max_restart;
+ u8 pcut_debunce_time;
+};
+
+/**
+ * struct ab8500_charger_maximization - struct used by the board config.
+ * @use_maxi: Enable maximization for this battery type
+ * @maxi_chg_curr: Maximum charger current allowed
+ * @maxi_wait_cycles: cycles to wait before setting charger current
+ * @charger_curr_step delta between two charger current settings (mA)
+ */
+struct ab8500_maxim_parameters {
+ bool ena_maxi;
+ int chg_curr;
+ int wait_cycles;
+ int charger_curr_step;
+};
+
+/**
+ * struct ab8500_bm_capacity_levels - ab8500 capacity level data
+ * @critical: critical capacity level in percent
+ * @low: low capacity level in percent
+ * @normal: normal capacity level in percent
+ * @high: high capacity level in percent
+ * @full: full capacity level in percent
+ */
+struct ab8500_bm_capacity_levels {
+ int critical;
+ int low;
+ int normal;
+ int high;
+ int full;
+};
+
+/**
+ * struct ab8500_bm_charger_parameters - Charger specific parameters
+ * @usb_volt_max: maximum allowed USB charger voltage in mV
+ * @usb_curr_max: maximum allowed USB charger current in mA
+ * @ac_volt_max: maximum allowed AC charger voltage in mV
+ * @ac_curr_max: maximum allowed AC charger current in mA
+ */
+struct ab8500_bm_charger_parameters {
+ int usb_volt_max;
+ int usb_curr_max;
+ int ac_volt_max;
+ int ac_curr_max;
+};
+
+/**
+ * struct ab8500_bm_data - ab8500 battery management data
+ * @temp_under under this temp, charging is stopped
+ * @temp_low between this temp and temp_under charging is reduced
+ * @temp_high between this temp and temp_over charging is reduced
+ * @temp_over over this temp, charging is stopped
+ * @temp_interval_chg temperature measurement interval in s when charging
+ * @temp_interval_nochg temperature measurement interval in s when not charging
+ * @main_safety_tmr_h safety timer for main charger
+ * @usb_safety_tmr_h safety timer for usb charger
+ * @bkup_bat_v voltage which we charge the backup battery with
+ * @bkup_bat_i current which we charge the backup battery with
+ * @no_maintenance indicates that maintenance charging is disabled
+ * @capacity_scaling indicates whether capacity scaling is to be used
+ * @adc_therm placement of thermistor, batctrl or battemp adc
+ * @chg_unknown_bat flag to enable charging of unknown batteries
+ * @enable_overshoot flag to enable VBAT overshoot control
+ * @fg_res resistance of FG resistor in 0.1mOhm
+ * @n_btypes number of elements in array bat_type
+ * @batt_id index of the identified battery in array bat_type
+ * @interval_charging charge alg cycle period time when charging (sec)
+ * @interval_not_charging charge alg cycle period time when not charging (sec)
+ * @temp_hysteresis temperature hysteresis
+ * @gnd_lift_resistance Battery ground to phone ground resistance (mOhm)
+ * @maxi: maximization parameters
+ * @cap_levels capacity in percent for the different capacity levels
+ * @bat_type table of supported battery types
+ * @chg_params charger parameters
+ * @fg_params fuel gauge parameters
+ */
+struct ab8500_bm_data {
+ int temp_under;
+ int temp_low;
+ int temp_high;
+ int temp_over;
+ int temp_interval_chg;
+ int temp_interval_nochg;
+ int main_safety_tmr_h;
+ int usb_safety_tmr_h;
+ int bkup_bat_v;
+ int bkup_bat_i;
+ bool no_maintenance;
+ bool capacity_scaling;
+ bool chg_unknown_bat;
+ bool enable_overshoot;
+ enum abx500_adc_therm adc_therm;
+ int fg_res;
+ int n_btypes;
+ int batt_id;
+ int interval_charging;
+ int interval_not_charging;
+ int temp_hysteresis;
+ int gnd_lift_resistance;
+ const struct ab8500_maxim_parameters *maxi;
+ const struct ab8500_bm_capacity_levels *cap_levels;
+ const struct ab8500_bm_charger_parameters *chg_params;
+ const struct ab8500_fg_parameters *fg_params;
+};
+
+extern struct abx500_bm_data ab8500_bm_data;
+
+void ab8500_charger_usb_state_changed(u8 bm_usb_state, u16 mA);
+struct ab8500_fg *ab8500_fg_get(void);
+int ab8500_fg_inst_curr_blocking(struct ab8500_fg *dev);
+int ab8500_fg_inst_curr_start(struct ab8500_fg *di);
+int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res);
+int ab8500_fg_inst_curr_started(struct ab8500_fg *di);
+int ab8500_fg_inst_curr_done(struct ab8500_fg *di);
+int ab8500_bm_of_probe(struct device *dev,
+ struct device_node *np,
+ struct abx500_bm_data *bm);
+
+#endif /* _AB8500_CHARGER_H_ */
diff --git a/drivers/power/supply/ab8500-chargalg.h b/drivers/power/supply/ab8500-chargalg.h
new file mode 100644
index 000000000000..94a6f9068bc5
--- /dev/null
+++ b/drivers/power/supply/ab8500-chargalg.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ * Author: Johan Gardsmark <johan.gardsmark@stericsson.com> for ST-Ericsson.
+ */
+
+#ifndef _AB8500_CHARGALG_H_
+#define _AB8500_CHARGALG_H_
+
+#include <linux/power_supply.h>
+
+/*
+ * Valid only for supplies of type:
+ * - POWER_SUPPLY_TYPE_MAINS,
+ * - POWER_SUPPLY_TYPE_USB,
+ * because only them store as drv_data pointer to struct ux500_charger.
+ */
+#define psy_to_ux500_charger(x) power_supply_get_drvdata(psy)
+
+/* Forward declaration */
+struct ux500_charger;
+
+struct ux500_charger_ops {
+ int (*enable) (struct ux500_charger *, int, int, int);
+ int (*check_enable) (struct ux500_charger *, int, int);
+ int (*kick_wd) (struct ux500_charger *);
+ int (*update_curr) (struct ux500_charger *, int);
+};
+
+/**
+ * struct ux500_charger - power supply ux500 charger sub class
+ * @psy power supply base class
+ * @ops ux500 charger operations
+ * @max_out_volt maximum output charger voltage in mV
+ * @max_out_curr maximum output charger current in mA
+ * @enabled indicates if this charger is used or not
+ * @external external charger unit (pm2xxx)
+ */
+struct ux500_charger {
+ struct power_supply *psy;
+ struct ux500_charger_ops ops;
+ int max_out_volt;
+ int max_out_curr;
+ int wdt_refresh;
+ bool enabled;
+ bool external;
+};
+
+extern struct blocking_notifier_head charger_notifier_list;
+
+#endif /* _AB8500_CHARGALG_H_ */
diff --git a/drivers/power/supply/ab8500_bmdata.c b/drivers/power/supply/ab8500_bmdata.c
index f6a66979cbb5..c2b8c0bb77e2 100644
--- a/drivers/power/supply/ab8500_bmdata.c
+++ b/drivers/power/supply/ab8500_bmdata.c
@@ -4,7 +4,8 @@
#include <linux/of.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
-#include <linux/mfd/abx500/ab8500-bm.h>
+
+#include "ab8500-bm.h"
/*
* These are the defined batteries that uses a NTC and ID resistor placed
diff --git a/drivers/power/supply/ab8500_btemp.c b/drivers/power/supply/ab8500_btemp.c
index d20345386b1e..fdfcd59fc43e 100644
--- a/drivers/power/supply/ab8500_btemp.c
+++ b/drivers/power/supply/ab8500_btemp.c
@@ -25,9 +25,10 @@
#include <linux/mfd/core.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
-#include <linux/mfd/abx500/ab8500-bm.h>
#include <linux/iio/consumer.h>
+#include "ab8500-bm.h"
+
#define VTVOUT_V 1800
#define BTEMP_THERMAL_LOW_LIMIT -10
@@ -120,16 +121,6 @@ static enum power_supply_property ab8500_btemp_props[] = {
static LIST_HEAD(ab8500_btemp_list);
/**
- * ab8500_btemp_get() - returns a reference to the primary AB8500 BTEMP
- * (i.e. the first BTEMP in the instance list)
- */
-struct ab8500_btemp *ab8500_btemp_get(void)
-{
- return list_first_entry(&ab8500_btemp_list, struct ab8500_btemp, node);
-}
-EXPORT_SYMBOL(ab8500_btemp_get);
-
-/**
* ab8500_btemp_batctrl_volt_to_res() - convert batctrl voltage to resistance
* @di: pointer to the ab8500_btemp structure
* @v_batctrl: measured batctrl voltage
@@ -754,7 +745,7 @@ static void ab8500_btemp_periodic(struct ab8500_btemp *di,
*
* Returns battery temperature
*/
-int ab8500_btemp_get_temp(struct ab8500_btemp *di)
+static int ab8500_btemp_get_temp(struct ab8500_btemp *di)
{
int temp = 0;
@@ -790,19 +781,6 @@ int ab8500_btemp_get_temp(struct ab8500_btemp *di)
}
return temp;
}
-EXPORT_SYMBOL(ab8500_btemp_get_temp);
-
-/**
- * ab8500_btemp_get_batctrl_temp() - get the temperature
- * @btemp: pointer to the btemp structure
- *
- * Returns the batctrl temperature in millidegrees
- */
-int ab8500_btemp_get_batctrl_temp(struct ab8500_btemp *btemp)
-{
- return btemp->bat_temp * 1000;
-}
-EXPORT_SYMBOL(ab8500_btemp_get_batctrl_temp);
/**
* ab8500_btemp_get_property() - get the btemp properties
@@ -991,7 +969,6 @@ static const struct power_supply_desc ab8500_btemp_desc = {
static int ab8500_btemp_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
- struct abx500_bm_data *plat = pdev->dev.platform_data;
struct power_supply_config psy_cfg = {};
struct device *dev = &pdev->dev;
struct ab8500_btemp *di;
@@ -1002,18 +979,12 @@ static int ab8500_btemp_probe(struct platform_device *pdev)
if (!di)
return -ENOMEM;
- if (!plat) {
- dev_err(dev, "no battery management data supplied\n");
- return -EINVAL;
- }
- di->bm = plat;
+ di->bm = &ab8500_bm_data;
- if (np) {
- ret = ab8500_bm_of_probe(dev, np, di->bm);
- if (ret) {
- dev_err(dev, "failed to get battery information\n");
- return ret;
- }
+ ret = ab8500_bm_of_probe(dev, np, di->bm);
+ if (ret) {
+ dev_err(dev, "failed to get battery information\n");
+ return ret;
}
/* get parent data */
diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c
index ac77c8882d17..a9be10eb2c22 100644
--- a/drivers/power/supply/ab8500_charger.c
+++ b/drivers/power/supply/ab8500_charger.c
@@ -28,12 +28,13 @@
#include <linux/mfd/core.h>
#include <linux/mfd/abx500/ab8500.h>
#include <linux/mfd/abx500.h>
-#include <linux/mfd/abx500/ab8500-bm.h>
-#include <linux/mfd/abx500/ux500_chargalg.h>
#include <linux/usb/otg.h>
#include <linux/mutex.h>
#include <linux/iio/consumer.h>
+#include "ab8500-bm.h"
+#include "ab8500-chargalg.h"
+
/* Charger constants */
#define NO_PW_CONN 0
#define AC_PW_CONN 1
@@ -3344,7 +3345,6 @@ static const struct power_supply_desc ab8500_usb_chg_desc = {
static int ab8500_charger_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
- struct abx500_bm_data *plat = pdev->dev.platform_data;
struct power_supply_config ac_psy_cfg = {}, usb_psy_cfg = {};
struct ab8500_charger *di;
int irq, i, charger_status, ret = 0, ch_stat;
@@ -3354,21 +3354,14 @@ static int ab8500_charger_probe(struct platform_device *pdev)
if (!di)
return -ENOMEM;
- if (!plat) {
- dev_err(dev, "no battery management data supplied\n");
- return -EINVAL;
- }
- di->bm = plat;
+ di->bm = &ab8500_bm_data;
- if (np) {
- ret = ab8500_bm_of_probe(dev, np, di->bm);
- if (ret) {
- dev_err(dev, "failed to get battery information\n");
- return ret;
- }
- di->autopower_cfg = of_property_read_bool(np, "autopower_cfg");
- } else
- di->autopower_cfg = false;
+ ret = ab8500_bm_of_probe(dev, np, di->bm);
+ if (ret) {
+ dev_err(dev, "failed to get battery information\n");
+ return ret;
+ }
+ di->autopower_cfg = of_property_read_bool(np, "autopower_cfg");
/* get parent data */
di->dev = dev;
diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c
index 06ff42c71f24..0c7c01a0d979 100644
--- a/drivers/power/supply/ab8500_fg.c
+++ b/drivers/power/supply/ab8500_fg.c
@@ -31,10 +31,11 @@
#include <linux/mfd/core.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
-#include <linux/mfd/abx500/ab8500-bm.h>
#include <linux/iio/consumer.h>
#include <linux/kernel.h>
+#include "ab8500-bm.h"
+
#define MILLI_TO_MICRO 1000
#define FG_LSB_IN_MA 1627
#define QLSB_NANO_AMP_HOURS_X10 1071
@@ -3026,7 +3027,6 @@ static const struct power_supply_desc ab8500_fg_desc = {
static int ab8500_fg_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
- struct abx500_bm_data *plat = pdev->dev.platform_data;
struct power_supply_config psy_cfg = {};
struct device *dev = &pdev->dev;
struct ab8500_fg *di;
@@ -3037,18 +3037,12 @@ static int ab8500_fg_probe(struct platform_device *pdev)
if (!di)
return -ENOMEM;
- if (!plat) {
- dev_err(dev, "no battery management data supplied\n");
- return -EINVAL;
- }
- di->bm = plat;
+ di->bm = &ab8500_bm_data;
- if (np) {
- ret = ab8500_bm_of_probe(dev, np, di->bm);
- if (ret) {
- dev_err(dev, "failed to get battery information\n");
- return ret;
- }
+ ret = ab8500_bm_of_probe(dev, np, di->bm);
+ if (ret) {
+ dev_err(dev, "failed to get battery information\n");
+ return ret;
}
mutex_init(&di->cc_lock);
diff --git a/drivers/power/supply/abx500_chargalg.c b/drivers/power/supply/abx500_chargalg.c
index a9d84d845f24..f5b792243727 100644
--- a/drivers/power/supply/abx500_chargalg.c
+++ b/drivers/power/supply/abx500_chargalg.c
@@ -28,10 +28,11 @@
#include <linux/mfd/core.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
-#include <linux/mfd/abx500/ux500_chargalg.h>
-#include <linux/mfd/abx500/ab8500-bm.h>
#include <linux/notifier.h>
+#include "ab8500-bm.h"
+#include "ab8500-chargalg.h"
+
/* Watchdog kick interval */
#define CHG_WD_INTERVAL (6 * HZ)
@@ -1980,7 +1981,6 @@ static const struct power_supply_desc abx500_chargalg_desc = {
static int abx500_chargalg_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
- struct abx500_bm_data *plat = pdev->dev.platform_data;
struct power_supply_config psy_cfg = {};
struct abx500_chargalg *di;
int ret = 0;
@@ -1991,18 +1991,12 @@ static int abx500_chargalg_probe(struct platform_device *pdev)
return -ENOMEM;
}
- if (!plat) {
- dev_err(&pdev->dev, "no battery management data supplied\n");
- return -EINVAL;
- }
- di->bm = plat;
+ di->bm = &ab8500_bm_data;
- if (np) {
- ret = ab8500_bm_of_probe(&pdev->dev, np, di->bm);
- if (ret) {
- dev_err(&pdev->dev, "failed to get battery information\n");
- return ret;
- }
+ ret = ab8500_bm_of_probe(&pdev->dev, np, di->bm);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get battery information\n");
+ return ret;
}
/* get device struct and parent */
diff --git a/drivers/power/supply/act8945a_charger.c b/drivers/power/supply/act8945a_charger.c
index 5f3eb6941d05..e9b5f4283772 100644
--- a/drivers/power/supply/act8945a_charger.c
+++ b/drivers/power/supply/act8945a_charger.c
@@ -18,7 +18,7 @@
static const char *act8945a_charger_model = "ACT8945A";
static const char *act8945a_charger_manufacturer = "Active-semi";
-/**
+/*
* ACT8945A Charger Register Map
*/
diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c
index e954970b50e6..a1d110f7ddce 100644
--- a/drivers/power/supply/axp20x_usb_power.c
+++ b/drivers/power/supply/axp20x_usb_power.c
@@ -619,8 +619,10 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
if (power->axp20x_id == AXP813_ID) {
/* Enable USB Battery Charging specification detection */
- regmap_update_bits(axp20x->regmap, AXP288_BC_GLOBAL,
+ ret = regmap_update_bits(axp20x->regmap, AXP288_BC_GLOBAL,
AXP813_BC_EN, AXP813_BC_EN);
+ if (ret)
+ return ret;
}
psy_cfg.of_node = pdev->dev.of_node;
diff --git a/drivers/power/supply/bq256xx_charger.c b/drivers/power/supply/bq256xx_charger.c
index 2ab5ba4af92b..f501ecd49202 100644
--- a/drivers/power/supply/bq256xx_charger.c
+++ b/drivers/power/supply/bq256xx_charger.c
@@ -202,6 +202,8 @@ enum bq256xx_id {
* @client: i2c client structure
* @regmap: register map structure
* @dev: device structure
+ * @charger: power supply registered for the charger
+ * @battery: power supply registered for the battery
* @lock: mutex lock structure
*
* @usb2_phy: usb_phy identifier
diff --git a/drivers/power/supply/bq25980_charger.c b/drivers/power/supply/bq25980_charger.c
index 530ff4025b31..0008c229fd9c 100644
--- a/drivers/power/supply/bq25980_charger.c
+++ b/drivers/power/supply/bq25980_charger.c
@@ -606,33 +606,6 @@ static int bq25980_get_state(struct bq25980_device *bq,
return 0;
}
-static int bq25980_set_battery_property(struct power_supply *psy,
- enum power_supply_property psp,
- const union power_supply_propval *val)
-{
- struct bq25980_device *bq = power_supply_get_drvdata(psy);
- int ret = 0;
-
- switch (psp) {
- case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
- ret = bq25980_set_const_charge_curr(bq, val->intval);
- if (ret)
- return ret;
- break;
-
- case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
- ret = bq25980_set_const_charge_volt(bq, val->intval);
- if (ret)
- return ret;
- break;
-
- default:
- return -EINVAL;
- }
-
- return ret;
-}
-
static int bq25980_get_battery_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
@@ -701,6 +674,18 @@ static int bq25980_set_charger_property(struct power_supply *psy,
return ret;
break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+ ret = bq25980_set_const_charge_curr(bq, val->intval);
+ if (ret)
+ return ret;
+ break;
+
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+ ret = bq25980_set_const_charge_volt(bq, val->intval);
+ if (ret)
+ return ret;
+ break;
+
default:
return -EINVAL;
}
@@ -922,7 +907,6 @@ static struct power_supply_desc bq25980_battery_desc = {
.name = "bq25980-battery",
.type = POWER_SUPPLY_TYPE_BATTERY,
.get_property = bq25980_get_battery_property,
- .set_property = bq25980_set_battery_property,
.properties = bq25980_battery_props,
.num_properties = ARRAY_SIZE(bq25980_battery_props),
.property_is_writeable = bq25980_property_is_writeable,
diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
index 4c4a7b1c64c5..7e5e24b585d8 100644
--- a/drivers/power/supply/bq27xxx_battery.c
+++ b/drivers/power/supply/bq27xxx_battery.c
@@ -39,6 +39,7 @@
* https://www.ti.com/product/bq27z561
* https://www.ti.com/product/bq28z610
* https://www.ti.com/product/bq34z100-g1
+ * https://www.ti.com/product/bq78z100
*/
#include <linux/device.h>
@@ -515,6 +516,27 @@ static u8
[BQ27XXX_REG_DCAP] = 0x3c,
[BQ27XXX_REG_AP] = 0x22,
BQ27XXX_DM_REG_ROWS,
+ },
+ bq78z100_regs[BQ27XXX_REG_MAX] = {
+ [BQ27XXX_REG_CTRL] = 0x00,
+ [BQ27XXX_REG_TEMP] = 0x06,
+ [BQ27XXX_REG_INT_TEMP] = 0x28,
+ [BQ27XXX_REG_VOLT] = 0x08,
+ [BQ27XXX_REG_AI] = 0x14,
+ [BQ27XXX_REG_FLAGS] = 0x0a,
+ [BQ27XXX_REG_TTE] = 0x16,
+ [BQ27XXX_REG_TTF] = 0x18,
+ [BQ27XXX_REG_TTES] = 0x1c,
+ [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
+ [BQ27XXX_REG_NAC] = INVALID_REG_ADDR,
+ [BQ27XXX_REG_RC] = 0x10,
+ [BQ27XXX_REG_FCC] = 0x12,
+ [BQ27XXX_REG_CYCT] = 0x2a,
+ [BQ27XXX_REG_AE] = INVALID_REG_ADDR,
+ [BQ27XXX_REG_SOC] = 0x2c,
+ [BQ27XXX_REG_DCAP] = 0x3c,
+ [BQ27XXX_REG_AP] = 0x22,
+ BQ27XXX_DM_REG_ROWS,
};
static enum power_supply_property bq27000_props[] = {
@@ -813,6 +835,26 @@ static enum power_supply_property bq34z100_props[] = {
POWER_SUPPLY_PROP_MANUFACTURER,
};
+static enum power_supply_property bq78z100_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+ POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
+ POWER_SUPPLY_PROP_POWER_AVG,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
struct bq27xxx_dm_reg {
u8 subclass_id;
u8 offset;
@@ -911,6 +953,7 @@ static struct bq27xxx_dm_reg bq27621_dm_regs[] = {
#define bq27z561_dm_regs 0
#define bq28z610_dm_regs 0
#define bq34z100_dm_regs 0
+#define bq78z100_dm_regs 0
#define BQ27XXX_O_ZERO BIT(0)
#define BQ27XXX_O_OTDC BIT(1) /* has OTC/OTD overtemperature flags */
@@ -969,6 +1012,7 @@ static struct {
[BQ28Z610] = BQ27XXX_DATA(bq28z610, 0 , BQ27Z561_O_BITS),
[BQ34Z100] = BQ27XXX_DATA(bq34z100, 0 , BQ27XXX_O_OTDC | BQ27XXX_O_SOC_SI | \
BQ27XXX_O_HAS_CI | BQ27XXX_O_MUL_CHEM),
+ [BQ78Z100] = BQ27XXX_DATA(bq78z100, 0 , BQ27Z561_O_BITS),
};
static DEFINE_MUTEX(bq27xxx_list_lock);
@@ -1662,27 +1706,6 @@ static int bq27xxx_battery_read_time(struct bq27xxx_device_info *di, u8 reg)
}
/*
- * Read an average power register.
- * Return < 0 if something fails.
- */
-static int bq27xxx_battery_read_pwr_avg(struct bq27xxx_device_info *di)
-{
- int tval;
-
- tval = bq27xxx_read(di, BQ27XXX_REG_AP, false);
- if (tval < 0) {
- dev_err(di->dev, "error reading average power register %02x: %d\n",
- BQ27XXX_REG_AP, tval);
- return tval;
- }
-
- if (di->opts & BQ27XXX_O_ZERO)
- return (tval * BQ27XXX_POWER_CONSTANT) / BQ27XXX_RS;
- else
- return tval;
-}
-
-/*
* Returns true if a battery over temperature condition is detected
*/
static bool bq27xxx_battery_overtemp(struct bq27xxx_device_info *di, u16 flags)
@@ -1769,8 +1792,6 @@ void bq27xxx_battery_update(struct bq27xxx_device_info *di)
}
if (di->regs[BQ27XXX_REG_CYCT] != INVALID_REG_ADDR)
cache.cycle_count = bq27xxx_battery_read_cyct(di);
- if (di->regs[BQ27XXX_REG_AP] != INVALID_REG_ADDR)
- cache.power_avg = bq27xxx_battery_read_pwr_avg(di);
/* We only have to read charge design full once */
if (di->charge_design_full <= 0)
@@ -1800,14 +1821,27 @@ static void bq27xxx_battery_poll(struct work_struct *work)
schedule_delayed_work(&di->work, poll_interval * HZ);
}
+static bool bq27xxx_battery_is_full(struct bq27xxx_device_info *di, int flags)
+{
+ if (di->opts & BQ27XXX_O_ZERO)
+ return (flags & BQ27000_FLAG_FC);
+ else if (di->opts & BQ27Z561_O_BITS)
+ return (flags & BQ27Z561_FLAG_FC);
+ else
+ return (flags & BQ27XXX_FLAG_FC);
+}
+
/*
- * Return the battery average current in µA
+ * Return the battery average current in µA and the status
* Note that current can be negative signed as well
* Or 0 if something fails.
*/
-static int bq27xxx_battery_current(struct bq27xxx_device_info *di,
- union power_supply_propval *val)
+static int bq27xxx_battery_current_and_status(
+ struct bq27xxx_device_info *di,
+ union power_supply_propval *val_curr,
+ union power_supply_propval *val_status)
{
+ bool single_flags = (di->opts & BQ27XXX_O_ZERO);
int curr;
int flags;
@@ -1817,55 +1851,66 @@ static int bq27xxx_battery_current(struct bq27xxx_device_info *di,
return curr;
}
+ flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, single_flags);
+ if (flags < 0) {
+ dev_err(di->dev, "error reading flags\n");
+ return flags;
+ }
+
if (di->opts & BQ27XXX_O_ZERO) {
- flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, true);
if (!(flags & BQ27000_FLAG_CHGS)) {
dev_dbg(di->dev, "negative current!\n");
curr = -curr;
}
- val->intval = curr * BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS;
+ curr = curr * BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS;
} else {
/* Other gauges return signed value */
- val->intval = -(int)((s16)curr) * 1000;
+ curr = (int)((s16)curr) * 1000;
+ }
+
+ if (val_curr)
+ val_curr->intval = curr;
+
+ if (val_status) {
+ if (curr > 0) {
+ val_status->intval = POWER_SUPPLY_STATUS_CHARGING;
+ } else if (curr < 0) {
+ val_status->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ } else {
+ if (bq27xxx_battery_is_full(di, flags))
+ val_status->intval = POWER_SUPPLY_STATUS_FULL;
+ else
+ val_status->intval =
+ POWER_SUPPLY_STATUS_NOT_CHARGING;
+ }
}
return 0;
}
-static int bq27xxx_battery_status(struct bq27xxx_device_info *di,
- union power_supply_propval *val)
+/*
+ * Get the average power in µW
+ * Return < 0 if something fails.
+ */
+static int bq27xxx_battery_pwr_avg(struct bq27xxx_device_info *di,
+ union power_supply_propval *val)
{
- int status;
-
- if (di->opts & BQ27XXX_O_ZERO) {
- if (di->cache.flags & BQ27000_FLAG_FC)
- status = POWER_SUPPLY_STATUS_FULL;
- else if (di->cache.flags & BQ27000_FLAG_CHGS)
- status = POWER_SUPPLY_STATUS_CHARGING;
- else
- status = POWER_SUPPLY_STATUS_DISCHARGING;
- } else if (di->opts & BQ27Z561_O_BITS) {
- if (di->cache.flags & BQ27Z561_FLAG_FC)
- status = POWER_SUPPLY_STATUS_FULL;
- else if (di->cache.flags & BQ27Z561_FLAG_DIS_CH)
- status = POWER_SUPPLY_STATUS_DISCHARGING;
- else
- status = POWER_SUPPLY_STATUS_CHARGING;
- } else {
- if (di->cache.flags & BQ27XXX_FLAG_FC)
- status = POWER_SUPPLY_STATUS_FULL;
- else if (di->cache.flags & BQ27XXX_FLAG_DSC)
- status = POWER_SUPPLY_STATUS_DISCHARGING;
- else
- status = POWER_SUPPLY_STATUS_CHARGING;
+ int power;
+
+ power = bq27xxx_read(di, BQ27XXX_REG_AP, false);
+ if (power < 0) {
+ dev_err(di->dev,
+ "error reading average power register %02x: %d\n",
+ BQ27XXX_REG_AP, power);
+ return power;
}
- if ((status == POWER_SUPPLY_STATUS_DISCHARGING) &&
- (power_supply_am_i_supplied(di->bat) > 0))
- status = POWER_SUPPLY_STATUS_NOT_CHARGING;
-
- val->intval = status;
+ if (di->opts & BQ27XXX_O_ZERO)
+ val->intval = (power * BQ27XXX_POWER_CONSTANT) / BQ27XXX_RS;
+ else
+ /* Other gauges return a signed value in units of 10mW */
+ val->intval = (int)((s16)power) * 10000;
return 0;
}
@@ -1957,7 +2002,7 @@ static int bq27xxx_battery_get_property(struct power_supply *psy,
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
- ret = bq27xxx_battery_status(di, val);
+ ret = bq27xxx_battery_current_and_status(di, NULL, val);
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
ret = bq27xxx_battery_voltage(di, val);
@@ -1966,7 +2011,7 @@ static int bq27xxx_battery_get_property(struct power_supply *psy,
val->intval = di->cache.flags < 0 ? 0 : 1;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
- ret = bq27xxx_battery_current(di, val);
+ ret = bq27xxx_battery_current_and_status(di, val, NULL);
break;
case POWER_SUPPLY_PROP_CAPACITY:
ret = bq27xxx_simple_value(di->cache.capacity, val);
@@ -2020,7 +2065,7 @@ static int bq27xxx_battery_get_property(struct power_supply *psy,
ret = bq27xxx_simple_value(di->cache.energy, val);
break;
case POWER_SUPPLY_PROP_POWER_AVG:
- ret = bq27xxx_simple_value(di->cache.power_avg, val);
+ ret = bq27xxx_battery_pwr_avg(di, val);
break;
case POWER_SUPPLY_PROP_HEALTH:
ret = bq27xxx_simple_value(di->cache.health, val);
diff --git a/drivers/power/supply/bq27xxx_battery_i2c.c b/drivers/power/supply/bq27xxx_battery_i2c.c
index eb4f4284982f..46f078350fd3 100644
--- a/drivers/power/supply/bq27xxx_battery_i2c.c
+++ b/drivers/power/supply/bq27xxx_battery_i2c.c
@@ -248,6 +248,7 @@ static const struct i2c_device_id bq27xxx_i2c_id_table[] = {
{ "bq27z561", BQ27Z561 },
{ "bq28z610", BQ28Z610 },
{ "bq34z100", BQ34Z100 },
+ { "bq78z100", BQ78Z100 },
{},
};
MODULE_DEVICE_TABLE(i2c, bq27xxx_i2c_id_table);
@@ -284,6 +285,7 @@ static const struct of_device_id bq27xxx_battery_i2c_of_match_table[] = {
{ .compatible = "ti,bq27z561" },
{ .compatible = "ti,bq28z610" },
{ .compatible = "ti,bq34z100" },
+ { .compatible = "ti,bq78z100" },
{},
};
MODULE_DEVICE_TABLE(of, bq27xxx_battery_i2c_of_match_table);
diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c
index 4dea8ecd70bc..45da870aecca 100644
--- a/drivers/power/supply/charger-manager.c
+++ b/drivers/power/supply/charger-manager.c
@@ -1604,7 +1604,7 @@ static int charger_manager_probe(struct platform_device *pdev)
mutex_unlock(&cm_list_mtx);
/*
- * Charger-manager is capable of waking up the systme from sleep
+ * Charger-manager is capable of waking up the system from sleep
* when event is happened through cm_notify_event()
*/
device_init_wakeup(&pdev->dev, true);
diff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c
index 6d5bcdb9f45d..a3fc0084cda0 100644
--- a/drivers/power/supply/cpcap-battery.c
+++ b/drivers/power/supply/cpcap-battery.c
@@ -786,7 +786,7 @@ static irqreturn_t cpcap_battery_irq_thread(int irq, void *data)
break;
}
- if (!d)
+ if (list_entry_is_head(d, &ddata->irq_list, node))
return IRQ_NONE;
latest = cpcap_battery_latest(ddata);
diff --git a/drivers/power/supply/cpcap-charger.c b/drivers/power/supply/cpcap-charger.c
index 641dcad1133f..df01abc49ce8 100644
--- a/drivers/power/supply/cpcap-charger.c
+++ b/drivers/power/supply/cpcap-charger.c
@@ -318,7 +318,7 @@ static int cpcap_charger_current_to_regval(int microamp)
return CPCAP_REG_CRM_ICHRG(0x0);
if (miliamp < 177)
return CPCAP_REG_CRM_ICHRG(0x1);
- if (miliamp > 1596)
+ if (miliamp >= 1596)
return CPCAP_REG_CRM_ICHRG(0xe);
res = microamp / 88666;
@@ -465,7 +465,7 @@ static bool cpcap_charger_vbus_valid(struct cpcap_charger_ddata *ddata)
error = iio_read_channel_processed(channel, &value);
if (error >= 0)
- return value > 3900 ? true : false;
+ return value > 3900;
dev_err(ddata->dev, "error reading VBUS: %i\n", error);
@@ -668,6 +668,9 @@ static void cpcap_usb_detect(struct work_struct *work)
return;
}
+ /* Delay for 80ms to avoid vbus bouncing when usb cable is plugged in */
+ usleep_range(80000, 120000);
+
/* Throttle chrgcurr2 interrupt for charger done and retry */
switch (ddata->status) {
case POWER_SUPPLY_STATUS_CHARGING:
diff --git a/drivers/power/supply/cw2015_battery.c b/drivers/power/supply/cw2015_battery.c
index 0146f1bfc29b..d110597746b0 100644
--- a/drivers/power/supply/cw2015_battery.c
+++ b/drivers/power/supply/cw2015_battery.c
@@ -511,6 +511,11 @@ static int cw_battery_get_property(struct power_supply *psy,
val->intval = 0;
break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ val->intval = cw_bat->battery.charge_full_design_uah;
+ val->intval = val->intval * cw_bat->soc / 100;
+ break;
+
case POWER_SUPPLY_PROP_CURRENT_NOW:
if (cw_battery_valid_time_to_empty(cw_bat) &&
cw_bat->battery.charge_full_design_uah > 0) {
@@ -542,6 +547,7 @@ static enum power_supply_property cw_battery_properties[] = {
POWER_SUPPLY_PROP_CHARGE_COUNTER,
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
};
diff --git a/drivers/power/supply/ds2781_battery.c b/drivers/power/supply/ds2781_battery.c
index 3df3c820b38c..05b859bf2dc0 100644
--- a/drivers/power/supply/ds2781_battery.c
+++ b/drivers/power/supply/ds2781_battery.c
@@ -626,7 +626,7 @@ static ssize_t ds2781_read_param_eeprom_bin(struct file *filp,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct device *dev = container_of(kobj, struct device, kobj);
+ struct device *dev = kobj_to_dev(kobj);
struct power_supply *psy = to_power_supply(dev);
struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
@@ -639,7 +639,7 @@ static ssize_t ds2781_write_param_eeprom_bin(struct file *filp,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct device *dev = container_of(kobj, struct device, kobj);
+ struct device *dev = kobj_to_dev(kobj);
struct power_supply *psy = to_power_supply(dev);
struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
int ret;
@@ -671,7 +671,7 @@ static ssize_t ds2781_read_user_eeprom_bin(struct file *filp,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct device *dev = container_of(kobj, struct device, kobj);
+ struct device *dev = kobj_to_dev(kobj);
struct power_supply *psy = to_power_supply(dev);
struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
@@ -685,7 +685,7 @@ static ssize_t ds2781_write_user_eeprom_bin(struct file *filp,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct device *dev = container_of(kobj, struct device, kobj);
+ struct device *dev = kobj_to_dev(kobj);
struct power_supply *psy = to_power_supply(dev);
struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
int ret;
diff --git a/drivers/power/supply/generic-adc-battery.c b/drivers/power/supply/generic-adc-battery.c
index 0032069fbc2b..66039c665dd1 100644
--- a/drivers/power/supply/generic-adc-battery.c
+++ b/drivers/power/supply/generic-adc-battery.c
@@ -373,7 +373,7 @@ static int gab_remove(struct platform_device *pdev)
}
kfree(adc_bat->psy_desc.properties);
- cancel_delayed_work(&adc_bat->bat_work);
+ cancel_delayed_work_sync(&adc_bat->bat_work);
return 0;
}
diff --git a/drivers/power/supply/lp8788-charger.c b/drivers/power/supply/lp8788-charger.c
index e7931ffb7151..397e5a03b7d9 100644
--- a/drivers/power/supply/lp8788-charger.c
+++ b/drivers/power/supply/lp8788-charger.c
@@ -501,7 +501,7 @@ static int lp8788_set_irqs(struct platform_device *pdev,
ret = request_threaded_irq(virq, NULL,
lp8788_charger_irq_thread,
- 0, name, pchg);
+ IRQF_ONESHOT, name, pchg);
if (ret)
break;
}
diff --git a/drivers/power/supply/max14577_charger.c b/drivers/power/supply/max14577_charger.c
index dcedae18d7be..f244cd902eb9 100644
--- a/drivers/power/supply/max14577_charger.c
+++ b/drivers/power/supply/max14577_charger.c
@@ -261,7 +261,7 @@ static int max14577_init_constant_voltage(struct max14577_charger *chg,
static int max14577_init_eoc(struct max14577_charger *chg,
unsigned int uamp)
{
- unsigned int current_bits = 0xf;
+ unsigned int current_bits;
u8 reg_data;
switch (chg->max14577->dev_type) {
diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c
index d956c67d5155..1aab868adabf 100644
--- a/drivers/power/supply/max17040_battery.c
+++ b/drivers/power/supply/max17040_battery.c
@@ -268,11 +268,10 @@ static int max17040_get_of_data(struct max17040_chip *chip)
rcomp_len = device_property_count_u8(dev, "maxim,rcomp");
chip->rcomp = MAX17040_RCOMP_DEFAULT;
if (rcomp_len == data->rcomp_bytes) {
- device_property_read_u8_array(dev, "maxim,rcomp",
- rcomp, rcomp_len);
- chip->rcomp = rcomp_len == 2 ?
- rcomp[0] << 8 | rcomp[1] :
- rcomp[0] << 8;
+ if (!device_property_read_u8_array(dev, "maxim,rcomp",
+ rcomp, rcomp_len))
+ chip->rcomp = rcomp_len == 2 ? rcomp[0] << 8 | rcomp[1] :
+ rcomp[0] << 8;
} else if (rcomp_len > 0) {
dev_err(dev, "maxim,rcomp has incorrect length\n");
return -EINVAL;
@@ -487,8 +486,7 @@ static int max17040_probe(struct i2c_client *client,
ret = max17040_get_of_data(chip);
if (ret)
return ret;
- chip_id = (enum chip_id) (uintptr_t)
- of_device_get_match_data(&client->dev);
+ chip_id = (uintptr_t)of_device_get_match_data(&client->dev);
}
chip->data = max17040_family[chip_id];
diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c
index 79d4b5988360..1d7326cd8fc6 100644
--- a/drivers/power/supply/max17042_battery.c
+++ b/drivers/power/supply/max17042_battery.c
@@ -131,7 +131,7 @@ static int max17042_get_status(struct max17042_chip *chip, int *status)
*
* When this cycle the battery gets charged to a higher (calculated)
* capacity then the previous cycle then FullCAP will get updated
- * contineously once end-of-charge detection kicks in, so allow the
+ * continuously once end-of-charge detection kicks in, so allow the
* 2 to differ a bit.
*/
@@ -739,7 +739,7 @@ static void max17042_load_new_capacity_params(struct max17042_chip *chip)
/*
* Block write all the override values coming from platform data.
- * This function MUST be called before the POR initialization proceedure
+ * This function MUST be called before the POR initialization procedure
* specified by maxim.
*/
static inline void max17042_override_por_values(struct max17042_chip *chip)
@@ -811,7 +811,7 @@ static int max17042_init_chip(struct max17042_chip *chip)
*/
msleep(500);
- /* Initialize configaration */
+ /* Initialize configuration */
max17042_write_config_regs(chip);
/* write cell characterization data */
@@ -855,7 +855,7 @@ static void max17042_set_soc_threshold(struct max17042_chip *chip, u16 off)
struct regmap *map = chip->regmap;
u32 soc, soc_tr;
- /* program interrupt thesholds such that we should
+ /* program interrupt thresholds such that we should
* get interrupt for every 'off' perc change in the soc
*/
regmap_read(map, MAX17042_RepSOC, &soc);
diff --git a/drivers/power/supply/max1721x_battery.c b/drivers/power/supply/max1721x_battery.c
index 1b1a36f8e929..473e53cd2801 100644
--- a/drivers/power/supply/max1721x_battery.c
+++ b/drivers/power/supply/max1721x_battery.c
@@ -1,6 +1,6 @@
/*
* 1-Wire implementation for Maxim Semiconductor
- * MAX7211/MAX17215 stanalone fuel gauge chip
+ * MAX7211/MAX17215 standalone fuel gauge chip
*
* Copyright (C) 2017 Radioavionica Corporation
* Author: Alex A. Mihaylov <minimumlaw@rambler.ru>
@@ -28,7 +28,7 @@
/* Number of valid register addresses in W1 mode */
#define MAX1721X_MAX_REG_NR 0x1EF
-/* Factory settings (nonvilatile registers) (W1 specific) */
+/* Factory settings (nonvolatile registers) (W1 specific) */
#define MAX1721X_REG_NRSENSE 0x1CF /* RSense in 10^-5 Ohm */
/* Strings */
#define MAX1721X_REG_MFG_STR 0x1CC
@@ -105,7 +105,7 @@ static inline int max172xx_temperature_to_ps(unsigned int reg)
/*
* Calculating current registers resolution:
*
- * RSense stored in 10^-5 Ohm, so mesaurment voltage must be
+ * RSense stored in 10^-5 Ohm, so measurement voltage must be
* in 10^-11 Volts for get current in uA.
* 16 bit current reg fullscale +/-51.2mV is 102400 uV.
* So: 102400 / 65535 * 10^5 = 156252
@@ -137,7 +137,7 @@ static int max1721x_battery_get_property(struct power_supply *psy,
/*
* POWER_SUPPLY_PROP_PRESENT will always readable via
* sysfs interface. Value return 0 if battery not
- * present or unaccesable via W1.
+ * present or unaccessible via W1.
*/
val->intval =
regmap_read(info->regmap, MAX172XX_REG_STATUS,
@@ -334,9 +334,9 @@ static int devm_w1_max1721x_add_device(struct w1_slave *sl)
/*
* power_supply class battery name translated from W1 slave device
- * unical ID (look like 26-0123456789AB) to "max1721x-0123456789AB\0"
- * so, 26 (device family) correcpondent to max1721x devices.
- * Device name still unical for any numbers connected devices.
+ * unique ID (look like 26-0123456789AB) to "max1721x-0123456789AB\0"
+ * so, 26 (device family) correspond to max1721x devices.
+ * Device name still unique for any number of connected devices.
*/
snprintf(info->name, sizeof(info->name),
"max1721x-%012X", (unsigned int)sl->reg_num.id);
diff --git a/drivers/power/supply/max8997_charger.c b/drivers/power/supply/max8997_charger.c
index 321bd6b8ee41..25207fe2aa68 100644
--- a/drivers/power/supply/max8997_charger.c
+++ b/drivers/power/supply/max8997_charger.c
@@ -168,6 +168,7 @@ static int max8997_battery_probe(struct platform_device *pdev)
int ret = 0;
struct charger_data *charger;
struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+ struct device_node *np = pdev->dev.of_node;
struct i2c_client *i2c = iodev->i2c;
struct max8997_platform_data *pdata = iodev->pdata;
struct power_supply_config psy_cfg = {};
@@ -237,20 +238,23 @@ static int max8997_battery_probe(struct platform_device *pdev)
return PTR_ERR(charger->battery);
}
+ // grab regulator from parent device's node
+ pdev->dev.of_node = iodev->dev->of_node;
charger->reg = devm_regulator_get_optional(&pdev->dev, "charger");
+ pdev->dev.of_node = np;
if (IS_ERR(charger->reg)) {
if (PTR_ERR(charger->reg) == -EPROBE_DEFER)
return -EPROBE_DEFER;
dev_info(&pdev->dev, "couldn't get charger regulator\n");
}
- charger->edev = extcon_get_edev_by_phandle(&pdev->dev, 0);
- if (IS_ERR(charger->edev)) {
- if (PTR_ERR(charger->edev) == -EPROBE_DEFER)
+ charger->edev = extcon_get_extcon_dev("max8997-muic");
+ if (IS_ERR_OR_NULL(charger->edev)) {
+ if (!charger->edev)
return -EPROBE_DEFER;
dev_info(charger->dev, "couldn't get extcon device\n");
}
- if (!IS_ERR(charger->reg) && !IS_ERR(charger->edev)) {
+ if (!IS_ERR(charger->reg) && !IS_ERR_OR_NULL(charger->edev)) {
INIT_WORK(&charger->extcon_work, max8997_battery_extcon_evt_worker);
ret = devm_add_action(&pdev->dev, max8997_battery_extcon_evt_stop_work, charger);
if (ret) {
@@ -263,7 +267,7 @@ static int max8997_battery_probe(struct platform_device *pdev)
if (ret) {
dev_err(&pdev->dev, "failed to register extcon notifier\n");
return ret;
- };
+ }
}
return 0;
diff --git a/drivers/power/supply/pm2301_charger.c b/drivers/power/supply/pm2301_charger.c
index ac06ecf7fc9c..f86bbbeaff6c 100644
--- a/drivers/power/supply/pm2301_charger.c
+++ b/drivers/power/supply/pm2301_charger.c
@@ -18,13 +18,13 @@
#include <linux/i2c.h>
#include <linux/workqueue.h>
#include <linux/mfd/abx500/ab8500.h>
-#include <linux/mfd/abx500/ab8500-bm.h>
-#include <linux/mfd/abx500/ux500_chargalg.h>
#include <linux/pm2301_charger.h>
#include <linux/gpio.h>
#include <linux/pm_runtime.h>
#include <linux/pm.h>
+#include "ab8500-bm.h"
+#include "ab8500-chargalg.h"
#include "pm2301_charger.h"
#define to_pm2xxx_charger_ac_device_info(x) container_of((x), \
@@ -1089,7 +1089,7 @@ static int pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
ret = request_threaded_irq(gpio_to_irq(pm2->pdata->gpio_irq_number),
NULL,
pm2xxx_charger_irq[0].isr,
- pm2->pdata->irq_type,
+ pm2->pdata->irq_type | IRQF_ONESHOT,
pm2xxx_charger_irq[0].name, pm2);
if (ret != 0) {
diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c
index 38e3aa642131..d99e2f11c183 100644
--- a/drivers/power/supply/power_supply_core.c
+++ b/drivers/power/supply/power_supply_core.c
@@ -169,7 +169,7 @@ static int __power_supply_populate_supplied_from(struct device *dev,
break;
if (np == epsy->of_node) {
- dev_info(&psy->dev, "%s: Found supply : %s\n",
+ dev_dbg(&psy->dev, "%s: Found supply : %s\n",
psy->desc->name, epsy->desc->name);
psy->supplied_from[i-1] = (char *)epsy->desc->name;
psy->num_supplies++;
@@ -1143,7 +1143,7 @@ __power_supply_register(struct device *parent,
rc = power_supply_check_supplies(psy);
if (rc) {
- dev_info(dev, "Not all required supplies found, defer probe\n");
+ dev_dbg(dev, "Not all required supplies found, defer probe\n");
goto check_supplies_failed;
}
diff --git a/drivers/power/supply/s3c_adc_battery.c b/drivers/power/supply/s3c_adc_battery.c
index a2addc24ee8b..68d31a3bee48 100644
--- a/drivers/power/supply/s3c_adc_battery.c
+++ b/drivers/power/supply/s3c_adc_battery.c
@@ -1,13 +1,8 @@
-/*
- * iPAQ h1930/h1940/rx1950 battery controller driver
- * Copyright (c) Vasily Khoruzhick
- * Based on h1940_battery.c by Arnaud Patard
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file COPYING in the main directory of this archive for
- * more details.
- *
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// iPAQ h1930/h1940/rx1950 battery controller driver
+// Copyright (c) Vasily Khoruzhick
+// Based on h1940_battery.c by Arnaud Patard
#include <linux/interrupt.h>
#include <linux/platform_device.h>
@@ -395,7 +390,7 @@ static int s3c_adc_bat_remove(struct platform_device *pdev)
if (main_bat.charge_finished)
free_irq(gpiod_to_irq(main_bat.charge_finished), NULL);
- cancel_delayed_work(&bat_work);
+ cancel_delayed_work_sync(&bat_work);
if (pdata->exit)
pdata->exit();
@@ -407,8 +402,6 @@ static int s3c_adc_bat_remove(struct platform_device *pdev)
static int s3c_adc_bat_suspend(struct platform_device *pdev,
pm_message_t state)
{
- struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
-
if (main_bat.charge_finished) {
if (device_may_wakeup(&pdev->dev))
enable_irq_wake(
@@ -424,8 +417,6 @@ static int s3c_adc_bat_suspend(struct platform_device *pdev,
static int s3c_adc_bat_resume(struct platform_device *pdev)
{
- struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
-
if (main_bat.charge_finished) {
if (device_may_wakeup(&pdev->dev))
disable_irq_wake(
diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c
index 70ea404b2a36..8d7a10730e43 100644
--- a/drivers/power/supply/sbs-battery.c
+++ b/drivers/power/supply/sbs-battery.c
@@ -1124,11 +1124,9 @@ static int sbs_probe(struct i2c_client *client)
chip->gpio_detect = devm_gpiod_get_optional(&client->dev,
"sbs,battery-detect", GPIOD_IN);
- if (IS_ERR(chip->gpio_detect)) {
- dev_err(&client->dev, "Failed to get gpio: %ld\n",
- PTR_ERR(chip->gpio_detect));
- return PTR_ERR(chip->gpio_detect);
- }
+ if (IS_ERR(chip->gpio_detect))
+ return dev_err_probe(&client->dev, PTR_ERR(chip->gpio_detect),
+ "Failed to get gpio\n");
i2c_set_clientdata(client, chip);
@@ -1159,11 +1157,9 @@ skip_gpio:
rc = sbs_get_battery_presence_and_health(
client, POWER_SUPPLY_PROP_PRESENT, &val);
- if (rc < 0 || !val.intval) {
- dev_err(&client->dev, "Failed to get present status\n");
- rc = -ENODEV;
- goto exit_psupply;
- }
+ if (rc < 0 || !val.intval)
+ return dev_err_probe(&client->dev, -ENODEV,
+ "Failed to get present status\n");
}
rc = devm_delayed_work_autocancel(&client->dev, &chip->work,
@@ -1173,20 +1169,14 @@ skip_gpio:
chip->power_supply = devm_power_supply_register(&client->dev, sbs_desc,
&psy_cfg);
- if (IS_ERR(chip->power_supply)) {
- dev_err(&client->dev,
- "%s: Failed to register power supply\n", __func__);
- rc = PTR_ERR(chip->power_supply);
- goto exit_psupply;
- }
+ if (IS_ERR(chip->power_supply))
+ return dev_err_probe(&client->dev, PTR_ERR(chip->power_supply),
+ "Failed to register power supply\n");
dev_info(&client->dev,
"%s: battery gas gauge device registered\n", client->name);
return 0;
-
-exit_psupply:
- return rc;
}
#if defined CONFIG_PM_SLEEP
diff --git a/drivers/power/supply/sbs-charger.c b/drivers/power/supply/sbs-charger.c
index fbfb6a620961..6fa65d118ec1 100644
--- a/drivers/power/supply/sbs-charger.c
+++ b/drivers/power/supply/sbs-charger.c
@@ -16,9 +16,7 @@
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
-#include <linux/gpio.h>
#include <linux/regmap.h>
-#include <linux/of_gpio.h>
#include <linux/bitops.h>
#define SBS_CHARGER_REG_SPEC_INFO 0x11
@@ -189,18 +187,14 @@ static int sbs_probe(struct i2c_client *client,
* to the battery.
*/
ret = regmap_read(chip->regmap, SBS_CHARGER_REG_STATUS, &val);
- if (ret) {
- dev_err(&client->dev, "Failed to get device status\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(&client->dev, ret, "Failed to get device status\n");
chip->last_state = val;
- chip->power_supply = devm_power_supply_register(&client->dev, &sbs_desc,
- &psy_cfg);
- if (IS_ERR(chip->power_supply)) {
- dev_err(&client->dev, "Failed to register power supply\n");
- return PTR_ERR(chip->power_supply);
- }
+ chip->power_supply = devm_power_supply_register(&client->dev, &sbs_desc, &psy_cfg);
+ if (IS_ERR(chip->power_supply))
+ return dev_err_probe(&client->dev, PTR_ERR(chip->power_supply),
+ "Failed to register power supply\n");
/*
* The sbs-charger spec doesn't impose the use of an interrupt. So in
@@ -212,10 +206,8 @@ static int sbs_probe(struct i2c_client *client,
NULL, sbs_irq_thread,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
dev_name(&client->dev), chip);
- if (ret) {
- dev_err(&client->dev, "Failed to request irq, %d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(&client->dev, ret, "Failed to request irq\n");
} else {
INIT_DELAYED_WORK(&chip->work, sbs_delayed_work);
schedule_delayed_work(&chip->work,
diff --git a/drivers/power/supply/sbs-manager.c b/drivers/power/supply/sbs-manager.c
index 666243d9dd59..71ec8f74f835 100644
--- a/drivers/power/supply/sbs-manager.c
+++ b/drivers/power/supply/sbs-manager.c
@@ -13,7 +13,7 @@
* Karl-Heinz Schneider <karl-heinz@schneider-inet.de>
*/
-#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
@@ -294,10 +294,8 @@ static int sbsm_gpio_setup(struct sbsm_data *data)
gc->owner = THIS_MODULE;
ret = devm_gpiochip_add_data(dev, gc, data);
- if (ret) {
- dev_err(dev, "devm_gpiochip_add_data failed: %d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "devm_gpiochip_add_data failed\n");
return ret;
}
@@ -311,6 +309,12 @@ static const struct power_supply_desc sbsm_default_psy_desc = {
.property_is_writeable = &sbsm_prop_is_writeable,
};
+static void sbsm_del_mux_adapter(void *data)
+{
+ struct sbsm_data *sbsm = data;
+ i2c_mux_del_adapters(sbsm->muxc);
+}
+
static int sbsm_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -343,13 +347,14 @@ static int sbsm_probe(struct i2c_client *client,
data->supported_bats = ret & SBSM_MASK_BAT_SUPPORTED;
data->muxc = i2c_mux_alloc(adapter, dev, SBSM_MAX_BATS, 0,
I2C_MUX_LOCKED, &sbsm_select, NULL);
- if (!data->muxc) {
- dev_err(dev, "failed to alloc i2c mux\n");
- ret = -ENOMEM;
- goto err_mux_alloc;
- }
+ if (!data->muxc)
+ return dev_err_probe(dev, -ENOMEM, "failed to alloc i2c mux\n");
data->muxc->priv = data;
+ ret = devm_add_action_or_reset(dev, sbsm_del_mux_adapter, data);
+ if (ret)
+ return ret;
+
/* register muxed i2c channels. One for each supported battery */
for (i = 0; i < SBSM_MAX_BATS; ++i) {
if (data->supported_bats & BIT(i)) {
@@ -358,54 +363,28 @@ static int sbsm_probe(struct i2c_client *client,
break;
}
}
- if (ret) {
- dev_err(dev, "failed to register i2c mux channel %d\n", i + 1);
- goto err_mux_register;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register i2c mux channel %d\n", i + 1);
- psy_desc = devm_kmemdup(dev, &sbsm_default_psy_desc,
- sizeof(struct power_supply_desc),
- GFP_KERNEL);
- if (!psy_desc) {
- ret = -ENOMEM;
- goto err_psy;
- }
+ psy_desc = devm_kmemdup(dev, &sbsm_default_psy_desc, sizeof(*psy_desc), GFP_KERNEL);
+ if (!psy_desc)
+ return -ENOMEM;
+
+ psy_desc->name = devm_kasprintf(dev, GFP_KERNEL, "sbsm-%s", dev_name(&client->dev));
+ if (!psy_desc->name)
+ return -ENOMEM;
- psy_desc->name = devm_kasprintf(dev, GFP_KERNEL, "sbsm-%s",
- dev_name(&client->dev));
- if (!psy_desc->name) {
- ret = -ENOMEM;
- goto err_psy;
- }
ret = sbsm_gpio_setup(data);
if (ret < 0)
- goto err_psy;
+ return ret;
psy_cfg.drv_data = data;
psy_cfg.of_node = dev->of_node;
data->psy = devm_power_supply_register(dev, psy_desc, &psy_cfg);
- if (IS_ERR(data->psy)) {
- ret = PTR_ERR(data->psy);
- dev_err(dev, "failed to register power supply %s\n",
- psy_desc->name);
- goto err_psy;
- }
-
- return 0;
-
-err_psy:
-err_mux_register:
- i2c_mux_del_adapters(data->muxc);
-
-err_mux_alloc:
- return ret;
-}
-
-static int sbsm_remove(struct i2c_client *client)
-{
- struct sbsm_data *data = i2c_get_clientdata(client);
+ if (IS_ERR(data->psy))
+ return dev_err_probe(dev, PTR_ERR(data->psy),
+ "failed to register power supply %s\n", psy_desc->name);
- i2c_mux_del_adapters(data->muxc);
return 0;
}
@@ -431,7 +410,6 @@ static struct i2c_driver sbsm_driver = {
.of_match_table = of_match_ptr(sbsm_dt_ids),
},
.probe = sbsm_probe,
- .remove = sbsm_remove,
.alert = sbsm_alert,
.id_table = sbsm_ids
};
diff --git a/drivers/power/supply/smb347-charger.c b/drivers/power/supply/smb347-charger.c
index 8cfbd8d6b478..3376f42d46c3 100644
--- a/drivers/power/supply/smb347-charger.c
+++ b/drivers/power/supply/smb347-charger.c
@@ -911,11 +911,14 @@ static int smb347_irq_init(struct smb347_charger *smb,
{
int ret;
- ret = devm_request_threaded_irq(smb->dev, client->irq, NULL,
- smb347_interrupt, IRQF_ONESHOT,
- client->name, smb);
- if (ret < 0)
- return ret;
+ smb->irq_unsupported = true;
+
+ /*
+ * Interrupt pin is optional. If it is connected, we setup the
+ * interrupt support here.
+ */
+ if (!client->irq)
+ return 0;
ret = smb347_set_writable(smb, true);
if (ret < 0)
@@ -931,7 +934,25 @@ static int smb347_irq_init(struct smb347_charger *smb,
smb347_set_writable(smb, false);
- return ret;
+ if (ret < 0) {
+ dev_warn(smb->dev, "failed to initialize IRQ: %d\n", ret);
+ dev_warn(smb->dev, "disabling IRQ support\n");
+ return 0;
+ }
+
+ ret = devm_request_threaded_irq(smb->dev, client->irq, NULL,
+ smb347_interrupt, IRQF_ONESHOT,
+ client->name, smb);
+ if (ret)
+ return ret;
+
+ smb->irq_unsupported = false;
+
+ ret = smb347_irq_enable(smb);
+ if (ret < 0)
+ return ret;
+
+ return 0;
}
/*
@@ -1120,9 +1141,13 @@ static int smb347_get_property(struct power_supply *psy,
struct i2c_client *client = to_i2c_client(smb->dev);
int ret;
- disable_irq(client->irq);
+ if (!smb->irq_unsupported)
+ disable_irq(client->irq);
+
ret = smb347_get_property_locked(psy, prop, val);
- enable_irq(client->irq);
+
+ if (!smb->irq_unsupported)
+ enable_irq(client->irq);
return ret;
}
@@ -1339,20 +1364,9 @@ static int smb347_probe(struct i2c_client *client,
if (ret < 0)
return ret;
- /*
- * Interrupt pin is optional. If it is connected, we setup the
- * interrupt support here.
- */
- if (client->irq) {
- ret = smb347_irq_init(smb, client);
- if (ret < 0) {
- dev_warn(dev, "failed to initialize IRQ: %d\n", ret);
- dev_warn(dev, "disabling IRQ support\n");
- smb->irq_unsupported = true;
- } else {
- smb347_irq_enable(smb);
- }
- }
+ ret = smb347_irq_init(smb, client);
+ if (ret)
+ return ret;
return 0;
}
@@ -1387,11 +1401,10 @@ static struct i2c_driver smb347_driver = {
.name = "smb347",
.of_match_table = smb3xx_of_match,
},
- .probe = smb347_probe,
- .remove = smb347_remove,
- .id_table = smb347_id,
+ .probe = smb347_probe,
+ .remove = smb347_remove,
+ .id_table = smb347_id,
};
-
module_i2c_driver(smb347_driver);
MODULE_AUTHOR("Bruce E. Robertson <bruce.e.robertson@intel.com>");
diff --git a/drivers/power/supply/surface_battery.c b/drivers/power/supply/surface_battery.c
new file mode 100644
index 000000000000..7efa431a62b2
--- /dev/null
+++ b/drivers/power/supply/surface_battery.c
@@ -0,0 +1,865 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Battery driver for 7th-generation Microsoft Surface devices via Surface
+ * System Aggregator Module (SSAM).
+ *
+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
+ */
+
+#include <asm/unaligned.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/power_supply.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#include <linux/surface_aggregator/device.h>
+
+
+/* -- SAM interface. -------------------------------------------------------- */
+
+enum sam_event_cid_bat {
+ SAM_EVENT_CID_BAT_BIX = 0x15,
+ SAM_EVENT_CID_BAT_BST = 0x16,
+ SAM_EVENT_CID_BAT_ADP = 0x17,
+ SAM_EVENT_CID_BAT_PROT = 0x18,
+ SAM_EVENT_CID_BAT_DPTF = 0x53,
+};
+
+enum sam_battery_sta {
+ SAM_BATTERY_STA_OK = 0x0f,
+ SAM_BATTERY_STA_PRESENT = 0x10,
+};
+
+enum sam_battery_state {
+ SAM_BATTERY_STATE_DISCHARGING = BIT(0),
+ SAM_BATTERY_STATE_CHARGING = BIT(1),
+ SAM_BATTERY_STATE_CRITICAL = BIT(2),
+};
+
+enum sam_battery_power_unit {
+ SAM_BATTERY_POWER_UNIT_mW = 0,
+ SAM_BATTERY_POWER_UNIT_mA = 1,
+};
+
+/* Equivalent to data returned in ACPI _BIX method, revision 0. */
+struct spwr_bix {
+ u8 revision;
+ __le32 power_unit;
+ __le32 design_cap;
+ __le32 last_full_charge_cap;
+ __le32 technology;
+ __le32 design_voltage;
+ __le32 design_cap_warn;
+ __le32 design_cap_low;
+ __le32 cycle_count;
+ __le32 measurement_accuracy;
+ __le32 max_sampling_time;
+ __le32 min_sampling_time;
+ __le32 max_avg_interval;
+ __le32 min_avg_interval;
+ __le32 bat_cap_granularity_1;
+ __le32 bat_cap_granularity_2;
+ __u8 model[21];
+ __u8 serial[11];
+ __u8 type[5];
+ __u8 oem_info[21];
+} __packed;
+
+static_assert(sizeof(struct spwr_bix) == 119);
+
+/* Equivalent to data returned in ACPI _BST method. */
+struct spwr_bst {
+ __le32 state;
+ __le32 present_rate;
+ __le32 remaining_cap;
+ __le32 present_voltage;
+} __packed;
+
+static_assert(sizeof(struct spwr_bst) == 16);
+
+#define SPWR_BIX_REVISION 0
+#define SPWR_BATTERY_VALUE_UNKNOWN 0xffffffff
+
+/* Get battery status (_STA) */
+SSAM_DEFINE_SYNC_REQUEST_CL_R(ssam_bat_get_sta, __le32, {
+ .target_category = SSAM_SSH_TC_BAT,
+ .command_id = 0x01,
+});
+
+/* Get battery static information (_BIX). */
+SSAM_DEFINE_SYNC_REQUEST_CL_R(ssam_bat_get_bix, struct spwr_bix, {
+ .target_category = SSAM_SSH_TC_BAT,
+ .command_id = 0x02,
+});
+
+/* Get battery dynamic information (_BST). */
+SSAM_DEFINE_SYNC_REQUEST_CL_R(ssam_bat_get_bst, struct spwr_bst, {
+ .target_category = SSAM_SSH_TC_BAT,
+ .command_id = 0x03,
+});
+
+/* Set battery trip point (_BTP). */
+SSAM_DEFINE_SYNC_REQUEST_CL_W(ssam_bat_set_btp, __le32, {
+ .target_category = SSAM_SSH_TC_BAT,
+ .command_id = 0x04,
+});
+
+
+/* -- Device structures. ---------------------------------------------------- */
+
+struct spwr_psy_properties {
+ const char *name;
+ struct ssam_event_registry registry;
+};
+
+struct spwr_battery_device {
+ struct ssam_device *sdev;
+
+ char name[32];
+ struct power_supply *psy;
+ struct power_supply_desc psy_desc;
+
+ struct delayed_work update_work;
+
+ struct ssam_event_notifier notif;
+
+ struct mutex lock; /* Guards access to state data below. */
+ unsigned long timestamp;
+
+ __le32 sta;
+ struct spwr_bix bix;
+ struct spwr_bst bst;
+ u32 alarm;
+};
+
+
+/* -- Module parameters. ---------------------------------------------------- */
+
+static unsigned int cache_time = 1000;
+module_param(cache_time, uint, 0644);
+MODULE_PARM_DESC(cache_time, "battery state caching time in milliseconds [default: 1000]");
+
+
+/* -- State management. ----------------------------------------------------- */
+
+/*
+ * Delay for battery update quirk. See spwr_external_power_changed() below
+ * for more details.
+ */
+#define SPWR_AC_BAT_UPDATE_DELAY msecs_to_jiffies(5000)
+
+static bool spwr_battery_present(struct spwr_battery_device *bat)
+{
+ lockdep_assert_held(&bat->lock);
+
+ return le32_to_cpu(bat->sta) & SAM_BATTERY_STA_PRESENT;
+}
+
+static int spwr_battery_load_sta(struct spwr_battery_device *bat)
+{
+ lockdep_assert_held(&bat->lock);
+
+ return ssam_retry(ssam_bat_get_sta, bat->sdev, &bat->sta);
+}
+
+static int spwr_battery_load_bix(struct spwr_battery_device *bat)
+{
+ int status;
+
+ lockdep_assert_held(&bat->lock);
+
+ if (!spwr_battery_present(bat))
+ return 0;
+
+ status = ssam_retry(ssam_bat_get_bix, bat->sdev, &bat->bix);
+
+ /* Enforce NULL terminated strings in case anything goes wrong... */
+ bat->bix.model[ARRAY_SIZE(bat->bix.model) - 1] = 0;
+ bat->bix.serial[ARRAY_SIZE(bat->bix.serial) - 1] = 0;
+ bat->bix.type[ARRAY_SIZE(bat->bix.type) - 1] = 0;
+ bat->bix.oem_info[ARRAY_SIZE(bat->bix.oem_info) - 1] = 0;
+
+ return status;
+}
+
+static int spwr_battery_load_bst(struct spwr_battery_device *bat)
+{
+ lockdep_assert_held(&bat->lock);
+
+ if (!spwr_battery_present(bat))
+ return 0;
+
+ return ssam_retry(ssam_bat_get_bst, bat->sdev, &bat->bst);
+}
+
+static int spwr_battery_set_alarm_unlocked(struct spwr_battery_device *bat, u32 value)
+{
+ __le32 value_le = cpu_to_le32(value);
+
+ lockdep_assert_held(&bat->lock);
+
+ bat->alarm = value;
+ return ssam_retry(ssam_bat_set_btp, bat->sdev, &value_le);
+}
+
+static int spwr_battery_update_bst_unlocked(struct spwr_battery_device *bat, bool cached)
+{
+ unsigned long cache_deadline = bat->timestamp + msecs_to_jiffies(cache_time);
+ int status;
+
+ lockdep_assert_held(&bat->lock);
+
+ if (cached && bat->timestamp && time_is_after_jiffies(cache_deadline))
+ return 0;
+
+ status = spwr_battery_load_sta(bat);
+ if (status)
+ return status;
+
+ status = spwr_battery_load_bst(bat);
+ if (status)
+ return status;
+
+ bat->timestamp = jiffies;
+ return 0;
+}
+
+static int spwr_battery_update_bst(struct spwr_battery_device *bat, bool cached)
+{
+ int status;
+
+ mutex_lock(&bat->lock);
+ status = spwr_battery_update_bst_unlocked(bat, cached);
+ mutex_unlock(&bat->lock);
+
+ return status;
+}
+
+static int spwr_battery_update_bix_unlocked(struct spwr_battery_device *bat)
+{
+ int status;
+
+ lockdep_assert_held(&bat->lock);
+
+ status = spwr_battery_load_sta(bat);
+ if (status)
+ return status;
+
+ status = spwr_battery_load_bix(bat);
+ if (status)
+ return status;
+
+ status = spwr_battery_load_bst(bat);
+ if (status)
+ return status;
+
+ if (bat->bix.revision != SPWR_BIX_REVISION)
+ dev_warn(&bat->sdev->dev, "unsupported battery revision: %u\n", bat->bix.revision);
+
+ bat->timestamp = jiffies;
+ return 0;
+}
+
+static u32 sprw_battery_get_full_cap_safe(struct spwr_battery_device *bat)
+{
+ u32 full_cap = get_unaligned_le32(&bat->bix.last_full_charge_cap);
+
+ lockdep_assert_held(&bat->lock);
+
+ if (full_cap == 0 || full_cap == SPWR_BATTERY_VALUE_UNKNOWN)
+ full_cap = get_unaligned_le32(&bat->bix.design_cap);
+
+ return full_cap;
+}
+
+static bool spwr_battery_is_full(struct spwr_battery_device *bat)
+{
+ u32 state = get_unaligned_le32(&bat->bst.state);
+ u32 full_cap = sprw_battery_get_full_cap_safe(bat);
+ u32 remaining_cap = get_unaligned_le32(&bat->bst.remaining_cap);
+
+ lockdep_assert_held(&bat->lock);
+
+ return full_cap != SPWR_BATTERY_VALUE_UNKNOWN && full_cap != 0 &&
+ remaining_cap != SPWR_BATTERY_VALUE_UNKNOWN &&
+ remaining_cap >= full_cap &&
+ state == 0;
+}
+
+static int spwr_battery_recheck_full(struct spwr_battery_device *bat)
+{
+ bool present;
+ u32 unit;
+ int status;
+
+ mutex_lock(&bat->lock);
+ unit = get_unaligned_le32(&bat->bix.power_unit);
+ present = spwr_battery_present(bat);
+
+ status = spwr_battery_update_bix_unlocked(bat);
+ if (status)
+ goto out;
+
+ /* If battery has been attached, (re-)initialize alarm. */
+ if (!present && spwr_battery_present(bat)) {
+ u32 cap_warn = get_unaligned_le32(&bat->bix.design_cap_warn);
+
+ status = spwr_battery_set_alarm_unlocked(bat, cap_warn);
+ if (status)
+ goto out;
+ }
+
+ /*
+ * Warn if the unit has changed. This is something we genuinely don't
+ * expect to happen, so make this a big warning. If it does, we'll
+ * need to add support for it.
+ */
+ WARN_ON(unit != get_unaligned_le32(&bat->bix.power_unit));
+
+out:
+ mutex_unlock(&bat->lock);
+
+ if (!status)
+ power_supply_changed(bat->psy);
+
+ return status;
+}
+
+static int spwr_battery_recheck_status(struct spwr_battery_device *bat)
+{
+ int status;
+
+ status = spwr_battery_update_bst(bat, false);
+ if (!status)
+ power_supply_changed(bat->psy);
+
+ return status;
+}
+
+static u32 spwr_notify_bat(struct ssam_event_notifier *nf, const struct ssam_event *event)
+{
+ struct spwr_battery_device *bat = container_of(nf, struct spwr_battery_device, notif);
+ int status;
+
+ dev_dbg(&bat->sdev->dev, "power event (cid = %#04x, iid = %#04x, tid = %#04x)\n",
+ event->command_id, event->instance_id, event->target_id);
+
+ switch (event->command_id) {
+ case SAM_EVENT_CID_BAT_BIX:
+ status = spwr_battery_recheck_full(bat);
+ break;
+
+ case SAM_EVENT_CID_BAT_BST:
+ status = spwr_battery_recheck_status(bat);
+ break;
+
+ case SAM_EVENT_CID_BAT_PROT:
+ /*
+ * TODO: Implement support for battery protection status change
+ * event.
+ */
+ status = 0;
+ break;
+
+ case SAM_EVENT_CID_BAT_DPTF:
+ /*
+ * TODO: Implement support for DPTF event.
+ */
+ status = 0;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return ssam_notifier_from_errno(status) | SSAM_NOTIF_HANDLED;
+}
+
+static void spwr_battery_update_bst_workfn(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct spwr_battery_device *bat;
+ int status;
+
+ bat = container_of(dwork, struct spwr_battery_device, update_work);
+
+ status = spwr_battery_update_bst(bat, false);
+ if (status) {
+ dev_err(&bat->sdev->dev, "failed to update battery state: %d\n", status);
+ return;
+ }
+
+ power_supply_changed(bat->psy);
+}
+
+static void spwr_external_power_changed(struct power_supply *psy)
+{
+ struct spwr_battery_device *bat = power_supply_get_drvdata(psy);
+
+ /*
+ * Handle battery update quirk: When the battery is fully charged (or
+ * charged up to the limit imposed by the UEFI battery limit) and the
+ * adapter is plugged in or removed, the EC does not send a separate
+ * event for the state (charging/discharging) change. Furthermore it
+ * may take some time until the state is updated on the battery.
+ * Schedule an update to solve this.
+ */
+
+ schedule_delayed_work(&bat->update_work, SPWR_AC_BAT_UPDATE_DELAY);
+}
+
+
+/* -- Properties. ----------------------------------------------------------- */
+
+static const enum power_supply_property spwr_battery_props_chg[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+ POWER_SUPPLY_PROP_SERIAL_NUMBER,
+};
+
+static const enum power_supply_property spwr_battery_props_eng[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_POWER_NOW,
+ POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
+ POWER_SUPPLY_PROP_ENERGY_FULL,
+ POWER_SUPPLY_PROP_ENERGY_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+ POWER_SUPPLY_PROP_SERIAL_NUMBER,
+};
+
+static int spwr_battery_prop_status(struct spwr_battery_device *bat)
+{
+ u32 state = get_unaligned_le32(&bat->bst.state);
+ u32 present_rate = get_unaligned_le32(&bat->bst.present_rate);
+
+ lockdep_assert_held(&bat->lock);
+
+ if (state & SAM_BATTERY_STATE_DISCHARGING)
+ return POWER_SUPPLY_STATUS_DISCHARGING;
+
+ if (state & SAM_BATTERY_STATE_CHARGING)
+ return POWER_SUPPLY_STATUS_CHARGING;
+
+ if (spwr_battery_is_full(bat))
+ return POWER_SUPPLY_STATUS_FULL;
+
+ if (present_rate == 0)
+ return POWER_SUPPLY_STATUS_NOT_CHARGING;
+
+ return POWER_SUPPLY_STATUS_UNKNOWN;
+}
+
+static int spwr_battery_prop_technology(struct spwr_battery_device *bat)
+{
+ lockdep_assert_held(&bat->lock);
+
+ if (!strcasecmp("NiCd", bat->bix.type))
+ return POWER_SUPPLY_TECHNOLOGY_NiCd;
+
+ if (!strcasecmp("NiMH", bat->bix.type))
+ return POWER_SUPPLY_TECHNOLOGY_NiMH;
+
+ if (!strcasecmp("LION", bat->bix.type))
+ return POWER_SUPPLY_TECHNOLOGY_LION;
+
+ if (!strncasecmp("LI-ION", bat->bix.type, 6))
+ return POWER_SUPPLY_TECHNOLOGY_LION;
+
+ if (!strcasecmp("LiP", bat->bix.type))
+ return POWER_SUPPLY_TECHNOLOGY_LIPO;
+
+ return POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
+}
+
+static int spwr_battery_prop_capacity(struct spwr_battery_device *bat)
+{
+ u32 full_cap = sprw_battery_get_full_cap_safe(bat);
+ u32 remaining_cap = get_unaligned_le32(&bat->bst.remaining_cap);
+
+ lockdep_assert_held(&bat->lock);
+
+ if (full_cap == 0 || full_cap == SPWR_BATTERY_VALUE_UNKNOWN)
+ return -ENODATA;
+
+ if (remaining_cap == SPWR_BATTERY_VALUE_UNKNOWN)
+ return -ENODATA;
+
+ return remaining_cap * 100 / full_cap;
+}
+
+static int spwr_battery_prop_capacity_level(struct spwr_battery_device *bat)
+{
+ u32 state = get_unaligned_le32(&bat->bst.state);
+ u32 remaining_cap = get_unaligned_le32(&bat->bst.remaining_cap);
+
+ lockdep_assert_held(&bat->lock);
+
+ if (state & SAM_BATTERY_STATE_CRITICAL)
+ return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
+
+ if (spwr_battery_is_full(bat))
+ return POWER_SUPPLY_CAPACITY_LEVEL_FULL;
+
+ if (remaining_cap <= bat->alarm)
+ return POWER_SUPPLY_CAPACITY_LEVEL_LOW;
+
+ return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
+}
+
+static int spwr_battery_get_property(struct power_supply *psy, enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct spwr_battery_device *bat = power_supply_get_drvdata(psy);
+ u32 value;
+ int status;
+
+ mutex_lock(&bat->lock);
+
+ status = spwr_battery_update_bst_unlocked(bat, true);
+ if (status)
+ goto out;
+
+ /* Abort if battery is not present. */
+ if (!spwr_battery_present(bat) && psp != POWER_SUPPLY_PROP_PRESENT) {
+ status = -ENODEV;
+ goto out;
+ }
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = spwr_battery_prop_status(bat);
+ break;
+
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = spwr_battery_present(bat);
+ break;
+
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = spwr_battery_prop_technology(bat);
+ break;
+
+ case POWER_SUPPLY_PROP_CYCLE_COUNT:
+ value = get_unaligned_le32(&bat->bix.cycle_count);
+ if (value != SPWR_BATTERY_VALUE_UNKNOWN)
+ val->intval = value;
+ else
+ status = -ENODATA;
+ break;
+
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+ value = get_unaligned_le32(&bat->bix.design_voltage);
+ if (value != SPWR_BATTERY_VALUE_UNKNOWN)
+ val->intval = value * 1000;
+ else
+ status = -ENODATA;
+ break;
+
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ value = get_unaligned_le32(&bat->bst.present_voltage);
+ if (value != SPWR_BATTERY_VALUE_UNKNOWN)
+ val->intval = value * 1000;
+ else
+ status = -ENODATA;
+ break;
+
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ case POWER_SUPPLY_PROP_POWER_NOW:
+ value = get_unaligned_le32(&bat->bst.present_rate);
+ if (value != SPWR_BATTERY_VALUE_UNKNOWN)
+ val->intval = value * 1000;
+ else
+ status = -ENODATA;
+ break;
+
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
+ value = get_unaligned_le32(&bat->bix.design_cap);
+ if (value != SPWR_BATTERY_VALUE_UNKNOWN)
+ val->intval = value * 1000;
+ else
+ status = -ENODATA;
+ break;
+
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ case POWER_SUPPLY_PROP_ENERGY_FULL:
+ value = get_unaligned_le32(&bat->bix.last_full_charge_cap);
+ if (value != SPWR_BATTERY_VALUE_UNKNOWN)
+ val->intval = value * 1000;
+ else
+ status = -ENODATA;
+ break;
+
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ case POWER_SUPPLY_PROP_ENERGY_NOW:
+ value = get_unaligned_le32(&bat->bst.remaining_cap);
+ if (value != SPWR_BATTERY_VALUE_UNKNOWN)
+ val->intval = value * 1000;
+ else
+ status = -ENODATA;
+ break;
+
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = spwr_battery_prop_capacity(bat);
+ break;
+
+ case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
+ val->intval = spwr_battery_prop_capacity_level(bat);
+ break;
+
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = bat->bix.model;
+ break;
+
+ case POWER_SUPPLY_PROP_MANUFACTURER:
+ val->strval = bat->bix.oem_info;
+ break;
+
+ case POWER_SUPPLY_PROP_SERIAL_NUMBER:
+ val->strval = bat->bix.serial;
+ break;
+
+ default:
+ status = -EINVAL;
+ break;
+ }
+
+out:
+ mutex_unlock(&bat->lock);
+ return status;
+}
+
+
+/* -- Alarm attribute. ------------------------------------------------------ */
+
+static ssize_t alarm_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct spwr_battery_device *bat = power_supply_get_drvdata(psy);
+ int status;
+
+ mutex_lock(&bat->lock);
+ status = sysfs_emit(buf, "%d\n", bat->alarm * 1000);
+ mutex_unlock(&bat->lock);
+
+ return status;
+}
+
+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 spwr_battery_device *bat = power_supply_get_drvdata(psy);
+ unsigned long value;
+ int status;
+
+ status = kstrtoul(buf, 0, &value);
+ if (status)
+ return status;
+
+ mutex_lock(&bat->lock);
+
+ if (!spwr_battery_present(bat)) {
+ mutex_unlock(&bat->lock);
+ return -ENODEV;
+ }
+
+ status = spwr_battery_set_alarm_unlocked(bat, value / 1000);
+ if (status) {
+ mutex_unlock(&bat->lock);
+ return status;
+ }
+
+ mutex_unlock(&bat->lock);
+ return count;
+}
+
+static DEVICE_ATTR_RW(alarm);
+
+static struct attribute *spwr_battery_attrs[] = {
+ &dev_attr_alarm.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(spwr_battery);
+
+
+/* -- Device setup. --------------------------------------------------------- */
+
+static void spwr_battery_init(struct spwr_battery_device *bat, struct ssam_device *sdev,
+ struct ssam_event_registry registry, const char *name)
+{
+ mutex_init(&bat->lock);
+ strncpy(bat->name, name, ARRAY_SIZE(bat->name) - 1);
+
+ bat->sdev = sdev;
+
+ bat->notif.base.priority = 1;
+ bat->notif.base.fn = spwr_notify_bat;
+ bat->notif.event.reg = registry;
+ bat->notif.event.id.target_category = sdev->uid.category;
+ bat->notif.event.id.instance = 0;
+ bat->notif.event.mask = SSAM_EVENT_MASK_STRICT;
+ bat->notif.event.flags = SSAM_EVENT_SEQUENCED;
+
+ bat->psy_desc.name = bat->name;
+ bat->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY;
+ bat->psy_desc.get_property = spwr_battery_get_property;
+
+ INIT_DELAYED_WORK(&bat->update_work, spwr_battery_update_bst_workfn);
+}
+
+static int spwr_battery_register(struct spwr_battery_device *bat)
+{
+ struct power_supply_config psy_cfg = {};
+ __le32 sta;
+ int status;
+
+ /* Make sure the device is there and functioning properly. */
+ status = ssam_retry(ssam_bat_get_sta, bat->sdev, &sta);
+ if (status)
+ return status;
+
+ if ((le32_to_cpu(sta) & SAM_BATTERY_STA_OK) != SAM_BATTERY_STA_OK)
+ return -ENODEV;
+
+ /* Satisfy lockdep although we are in an exclusive context here. */
+ mutex_lock(&bat->lock);
+
+ status = spwr_battery_update_bix_unlocked(bat);
+ if (status) {
+ mutex_unlock(&bat->lock);
+ return status;
+ }
+
+ if (spwr_battery_present(bat)) {
+ u32 cap_warn = get_unaligned_le32(&bat->bix.design_cap_warn);
+
+ status = spwr_battery_set_alarm_unlocked(bat, cap_warn);
+ if (status) {
+ mutex_unlock(&bat->lock);
+ return status;
+ }
+ }
+
+ mutex_unlock(&bat->lock);
+
+ bat->psy_desc.external_power_changed = spwr_external_power_changed;
+
+ switch (get_unaligned_le32(&bat->bix.power_unit)) {
+ case SAM_BATTERY_POWER_UNIT_mW:
+ bat->psy_desc.properties = spwr_battery_props_eng;
+ bat->psy_desc.num_properties = ARRAY_SIZE(spwr_battery_props_eng);
+ break;
+
+ case SAM_BATTERY_POWER_UNIT_mA:
+ bat->psy_desc.properties = spwr_battery_props_chg;
+ bat->psy_desc.num_properties = ARRAY_SIZE(spwr_battery_props_chg);
+ break;
+
+ default:
+ dev_err(&bat->sdev->dev, "unsupported battery power unit: %u\n",
+ get_unaligned_le32(&bat->bix.power_unit));
+ return -EINVAL;
+ }
+
+ psy_cfg.drv_data = bat;
+ psy_cfg.attr_grp = spwr_battery_groups;
+
+ bat->psy = devm_power_supply_register(&bat->sdev->dev, &bat->psy_desc, &psy_cfg);
+ if (IS_ERR(bat->psy))
+ return PTR_ERR(bat->psy);
+
+ return ssam_notifier_register(bat->sdev->ctrl, &bat->notif);
+}
+
+
+/* -- Driver setup. --------------------------------------------------------- */
+
+static int __maybe_unused surface_battery_resume(struct device *dev)
+{
+ return spwr_battery_recheck_full(dev_get_drvdata(dev));
+}
+static SIMPLE_DEV_PM_OPS(surface_battery_pm_ops, NULL, surface_battery_resume);
+
+static int surface_battery_probe(struct ssam_device *sdev)
+{
+ const struct spwr_psy_properties *p;
+ struct spwr_battery_device *bat;
+
+ p = ssam_device_get_match_data(sdev);
+ if (!p)
+ return -ENODEV;
+
+ bat = devm_kzalloc(&sdev->dev, sizeof(*bat), GFP_KERNEL);
+ if (!bat)
+ return -ENOMEM;
+
+ spwr_battery_init(bat, sdev, p->registry, p->name);
+ ssam_device_set_drvdata(sdev, bat);
+
+ return spwr_battery_register(bat);
+}
+
+static void surface_battery_remove(struct ssam_device *sdev)
+{
+ struct spwr_battery_device *bat = ssam_device_get_drvdata(sdev);
+
+ ssam_notifier_unregister(sdev->ctrl, &bat->notif);
+ cancel_delayed_work_sync(&bat->update_work);
+}
+
+static const struct spwr_psy_properties spwr_psy_props_bat1 = {
+ .name = "BAT1",
+ .registry = SSAM_EVENT_REGISTRY_SAM,
+};
+
+static const struct spwr_psy_properties spwr_psy_props_bat2_sb3 = {
+ .name = "BAT2",
+ .registry = SSAM_EVENT_REGISTRY_KIP,
+};
+
+static const struct ssam_device_id surface_battery_match[] = {
+ { SSAM_SDEV(BAT, 0x01, 0x01, 0x00), (unsigned long)&spwr_psy_props_bat1 },
+ { SSAM_SDEV(BAT, 0x02, 0x01, 0x00), (unsigned long)&spwr_psy_props_bat2_sb3 },
+ { },
+};
+MODULE_DEVICE_TABLE(ssam, surface_battery_match);
+
+static struct ssam_device_driver surface_battery_driver = {
+ .probe = surface_battery_probe,
+ .remove = surface_battery_remove,
+ .match_table = surface_battery_match,
+ .driver = {
+ .name = "surface_battery",
+ .pm = &surface_battery_pm_ops,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+};
+module_ssam_device_driver(surface_battery_driver);
+
+MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
+MODULE_DESCRIPTION("Battery driver for Surface System Aggregator Module");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/surface_charger.c b/drivers/power/supply/surface_charger.c
new file mode 100644
index 000000000000..81a5b79822c9
--- /dev/null
+++ b/drivers/power/supply/surface_charger.c
@@ -0,0 +1,282 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * AC driver for 7th-generation Microsoft Surface devices via Surface System
+ * Aggregator Module (SSAM).
+ *
+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
+ */
+
+#include <asm/unaligned.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/power_supply.h>
+#include <linux/types.h>
+
+#include <linux/surface_aggregator/device.h>
+
+
+/* -- SAM interface. -------------------------------------------------------- */
+
+enum sam_event_cid_bat {
+ SAM_EVENT_CID_BAT_ADP = 0x17,
+};
+
+enum sam_battery_sta {
+ SAM_BATTERY_STA_OK = 0x0f,
+ SAM_BATTERY_STA_PRESENT = 0x10,
+};
+
+/* Get battery status (_STA). */
+SSAM_DEFINE_SYNC_REQUEST_CL_R(ssam_bat_get_sta, __le32, {
+ .target_category = SSAM_SSH_TC_BAT,
+ .command_id = 0x01,
+});
+
+/* Get platform power source for battery (_PSR / DPTF PSRC). */
+SSAM_DEFINE_SYNC_REQUEST_CL_R(ssam_bat_get_psrc, __le32, {
+ .target_category = SSAM_SSH_TC_BAT,
+ .command_id = 0x0d,
+});
+
+
+/* -- Device structures. ---------------------------------------------------- */
+
+struct spwr_psy_properties {
+ const char *name;
+ struct ssam_event_registry registry;
+};
+
+struct spwr_ac_device {
+ struct ssam_device *sdev;
+
+ char name[32];
+ struct power_supply *psy;
+ struct power_supply_desc psy_desc;
+
+ struct ssam_event_notifier notif;
+
+ struct mutex lock; /* Guards access to state below. */
+
+ __le32 state;
+};
+
+
+/* -- State management. ----------------------------------------------------- */
+
+static int spwr_ac_update_unlocked(struct spwr_ac_device *ac)
+{
+ u32 old = ac->state;
+ int status;
+
+ lockdep_assert_held(&ac->lock);
+
+ status = ssam_retry(ssam_bat_get_psrc, ac->sdev, &ac->state);
+ if (status < 0)
+ return status;
+
+ return old != ac->state;
+}
+
+static int spwr_ac_update(struct spwr_ac_device *ac)
+{
+ int status;
+
+ mutex_lock(&ac->lock);
+ status = spwr_ac_update_unlocked(ac);
+ mutex_unlock(&ac->lock);
+
+ return status;
+}
+
+static int spwr_ac_recheck(struct spwr_ac_device *ac)
+{
+ int status;
+
+ status = spwr_ac_update(ac);
+ if (status > 0)
+ power_supply_changed(ac->psy);
+
+ return status >= 0 ? 0 : status;
+}
+
+static u32 spwr_notify_ac(struct ssam_event_notifier *nf, const struct ssam_event *event)
+{
+ struct spwr_ac_device *ac;
+ int status;
+
+ ac = container_of(nf, struct spwr_ac_device, notif);
+
+ dev_dbg(&ac->sdev->dev, "power event (cid = %#04x, iid = %#04x, tid = %#04x)\n",
+ event->command_id, event->instance_id, event->target_id);
+
+ /*
+ * Allow events of all targets/instances here. Global adapter status
+ * seems to be handled via target=1 and instance=1, but events are
+ * reported on all targets/instances in use.
+ *
+ * While it should be enough to just listen on 1/1, listen everywhere to
+ * make sure we don't miss anything.
+ */
+
+ switch (event->command_id) {
+ case SAM_EVENT_CID_BAT_ADP:
+ status = spwr_ac_recheck(ac);
+ return ssam_notifier_from_errno(status) | SSAM_NOTIF_HANDLED;
+
+ default:
+ return 0;
+ }
+}
+
+
+/* -- Properties. ----------------------------------------------------------- */
+
+static const enum power_supply_property spwr_ac_props[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static int spwr_ac_get_property(struct power_supply *psy, enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct spwr_ac_device *ac = power_supply_get_drvdata(psy);
+ int status;
+
+ mutex_lock(&ac->lock);
+
+ status = spwr_ac_update_unlocked(ac);
+ if (status)
+ goto out;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = !!le32_to_cpu(ac->state);
+ break;
+
+ default:
+ status = -EINVAL;
+ goto out;
+ }
+
+out:
+ mutex_unlock(&ac->lock);
+ return status;
+}
+
+
+/* -- Device setup. --------------------------------------------------------- */
+
+static char *battery_supplied_to[] = {
+ "BAT1",
+ "BAT2",
+};
+
+static void spwr_ac_init(struct spwr_ac_device *ac, struct ssam_device *sdev,
+ struct ssam_event_registry registry, const char *name)
+{
+ mutex_init(&ac->lock);
+ strncpy(ac->name, name, ARRAY_SIZE(ac->name) - 1);
+
+ ac->sdev = sdev;
+
+ ac->notif.base.priority = 1;
+ ac->notif.base.fn = spwr_notify_ac;
+ ac->notif.event.reg = registry;
+ ac->notif.event.id.target_category = sdev->uid.category;
+ ac->notif.event.id.instance = 0;
+ ac->notif.event.mask = SSAM_EVENT_MASK_NONE;
+ ac->notif.event.flags = SSAM_EVENT_SEQUENCED;
+
+ ac->psy_desc.name = ac->name;
+ ac->psy_desc.type = POWER_SUPPLY_TYPE_MAINS;
+ ac->psy_desc.properties = spwr_ac_props;
+ ac->psy_desc.num_properties = ARRAY_SIZE(spwr_ac_props);
+ ac->psy_desc.get_property = spwr_ac_get_property;
+}
+
+static int spwr_ac_register(struct spwr_ac_device *ac)
+{
+ struct power_supply_config psy_cfg = {};
+ __le32 sta;
+ int status;
+
+ /* Make sure the device is there and functioning properly. */
+ status = ssam_retry(ssam_bat_get_sta, ac->sdev, &sta);
+ if (status)
+ return status;
+
+ if ((le32_to_cpu(sta) & SAM_BATTERY_STA_OK) != SAM_BATTERY_STA_OK)
+ return -ENODEV;
+
+ psy_cfg.drv_data = ac;
+ psy_cfg.supplied_to = battery_supplied_to;
+ psy_cfg.num_supplicants = ARRAY_SIZE(battery_supplied_to);
+
+ ac->psy = devm_power_supply_register(&ac->sdev->dev, &ac->psy_desc, &psy_cfg);
+ if (IS_ERR(ac->psy))
+ return PTR_ERR(ac->psy);
+
+ return ssam_notifier_register(ac->sdev->ctrl, &ac->notif);
+}
+
+
+/* -- Driver setup. --------------------------------------------------------- */
+
+static int __maybe_unused surface_ac_resume(struct device *dev)
+{
+ return spwr_ac_recheck(dev_get_drvdata(dev));
+}
+static SIMPLE_DEV_PM_OPS(surface_ac_pm_ops, NULL, surface_ac_resume);
+
+static int surface_ac_probe(struct ssam_device *sdev)
+{
+ const struct spwr_psy_properties *p;
+ struct spwr_ac_device *ac;
+
+ p = ssam_device_get_match_data(sdev);
+ if (!p)
+ return -ENODEV;
+
+ ac = devm_kzalloc(&sdev->dev, sizeof(*ac), GFP_KERNEL);
+ if (!ac)
+ return -ENOMEM;
+
+ spwr_ac_init(ac, sdev, p->registry, p->name);
+ ssam_device_set_drvdata(sdev, ac);
+
+ return spwr_ac_register(ac);
+}
+
+static void surface_ac_remove(struct ssam_device *sdev)
+{
+ struct spwr_ac_device *ac = ssam_device_get_drvdata(sdev);
+
+ ssam_notifier_unregister(sdev->ctrl, &ac->notif);
+}
+
+static const struct spwr_psy_properties spwr_psy_props_adp1 = {
+ .name = "ADP1",
+ .registry = SSAM_EVENT_REGISTRY_SAM,
+};
+
+static const struct ssam_device_id surface_ac_match[] = {
+ { SSAM_SDEV(BAT, 0x01, 0x01, 0x01), (unsigned long)&spwr_psy_props_adp1 },
+ { },
+};
+MODULE_DEVICE_TABLE(ssam, surface_ac_match);
+
+static struct ssam_device_driver surface_ac_driver = {
+ .probe = surface_ac_probe,
+ .remove = surface_ac_remove,
+ .match_table = surface_ac_match,
+ .driver = {
+ .name = "surface_ac",
+ .pm = &surface_ac_pm_ops,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+};
+module_ssam_device_driver(surface_ac_driver);
+
+MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
+MODULE_DESCRIPTION("AC driver for Surface System Aggregator Module");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/tps65090-charger.c b/drivers/power/supply/tps65090-charger.c
index 6b0098e5a88b..0990b2fa6cd8 100644
--- a/drivers/power/supply/tps65090-charger.c
+++ b/drivers/power/supply/tps65090-charger.c
@@ -301,7 +301,7 @@ static int tps65090_charger_probe(struct platform_device *pdev)
if (irq != -ENXIO) {
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
- tps65090_charger_isr, 0, "tps65090-charger", cdata);
+ tps65090_charger_isr, IRQF_ONESHOT, "tps65090-charger", cdata);
if (ret) {
dev_err(cdata->dev,
"Unable to register irq %d err %d\n", irq,
diff --git a/drivers/power/supply/tps65217_charger.c b/drivers/power/supply/tps65217_charger.c
index 814c2b81fdfe..ba33d1617e0b 100644
--- a/drivers/power/supply/tps65217_charger.c
+++ b/drivers/power/supply/tps65217_charger.c
@@ -238,7 +238,7 @@ static int tps65217_charger_probe(struct platform_device *pdev)
for (i = 0; i < NUM_CHARGER_IRQS; i++) {
ret = devm_request_threaded_irq(&pdev->dev, irq[i], NULL,
tps65217_charger_irq,
- 0, "tps65217-charger",
+ IRQF_ONESHOT, "tps65217-charger",
charger);
if (ret) {
dev_err(charger->dev,
diff --git a/drivers/power/supply/z2_battery.c b/drivers/power/supply/z2_battery.c
index b1508fe70e5e..7ed4e4bb26ec 100644
--- a/drivers/power/supply/z2_battery.c
+++ b/drivers/power/supply/z2_battery.c
@@ -90,9 +90,6 @@ static void z2_batt_ext_power_changed(struct power_supply *batt_ps)
static void z2_batt_update(struct z2_charger *charger)
{
int old_status = charger->bat_status;
- struct z2_battery_info *info;
-
- info = charger->info;
mutex_lock(&charger->work_lock);