summaryrefslogtreecommitdiff
path: root/drivers/power/supply
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/power/supply')
-rw-r--r--drivers/power/supply/Kconfig1
-rw-r--r--drivers/power/supply/ab8500-bm.h8
-rw-r--r--drivers/power/supply/ab8500_bmdata.c3
-rw-r--r--drivers/power/supply/ab8500_btemp.c30
-rw-r--r--drivers/power/supply/ab8500_chargalg.c9
-rw-r--r--drivers/power/supply/ab8500_charger.c404
-rw-r--r--drivers/power/supply/ab8500_fg.c179
-rw-r--r--drivers/power/supply/axp20x_ac_power.c3
-rw-r--r--drivers/power/supply/axp20x_battery.c3
-rw-r--r--drivers/power/supply/axp20x_usb_power.c50
-rw-r--r--drivers/power/supply/bq24190_charger.c30
-rw-r--r--drivers/power/supply/bq256xx_charger.c96
-rw-r--r--drivers/power/supply/bq257xx_charger.c124
-rw-r--r--drivers/power/supply/bq27xxx_battery.c6
-rw-r--r--drivers/power/supply/bq27xxx_battery_i2c.c2
-rw-r--r--drivers/power/supply/cpcap-charger.c4
-rw-r--r--drivers/power/supply/generic-adc-battery.c9
-rw-r--r--drivers/power/supply/intel_dc_ti_battery.c2
-rw-r--r--drivers/power/supply/ltc2941-battery-gauge.c6
-rw-r--r--drivers/power/supply/max17042_battery.c26
-rw-r--r--drivers/power/supply/pf1550-charger.c32
-rw-r--r--drivers/power/supply/power_supply_core.c124
-rw-r--r--drivers/power/supply/qcom_battmgr.c1
-rw-r--r--drivers/power/supply/qcom_smbx.c4
-rw-r--r--drivers/power/supply/sbs-battery.c11
25 files changed, 749 insertions, 418 deletions
diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index b89ef40df7f8..ee7dc5f2617a 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -850,6 +850,7 @@ config CHARGER_BQ256XX
tristate "TI BQ256XX battery charger driver"
depends on I2C
depends on GPIOLIB || COMPILE_TEST
+ depends on EXTCON || !EXTCON
select REGMAP_I2C
help
Say Y to enable support for the TI BQ256XX battery chargers. The
diff --git a/drivers/power/supply/ab8500-bm.h b/drivers/power/supply/ab8500-bm.h
index 180a016b3662..209744c77c3d 100644
--- a/drivers/power/supply/ab8500-bm.h
+++ b/drivers/power/supply/ab8500-bm.h
@@ -151,8 +151,7 @@
#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
+#define CH_OP_CUR_LVL_1P5_MAX 0x0F
/* BTEMP High thermal limits */
#define BTEMP_HIGH_TH_57_0 0x00
@@ -233,7 +232,7 @@ enum bup_vch_sel {
/* 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_8U_ENA 0x01
#define BAT_CTRL_16U_ENA 0x02
#define BAT_CTRL_CMP_ENA 0x04
#define FORCE_BAT_CTRL_CMP_HIGH 0x08
@@ -246,7 +245,6 @@ enum bup_vch_sel {
#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
@@ -287,7 +285,6 @@ struct ab8500_fg;
* 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
*/
@@ -308,7 +305,6 @@ struct ab8500_fg_parameters {
int maint_thres;
bool pcut_enable;
u8 pcut_max_time;
- u8 pcut_flag_time;
u8 pcut_max_restart;
u8 pcut_debounce_time;
};
diff --git a/drivers/power/supply/ab8500_bmdata.c b/drivers/power/supply/ab8500_bmdata.c
index 19ed52852804..0f04893d6bf9 100644
--- a/drivers/power/supply/ab8500_bmdata.c
+++ b/drivers/power/supply/ab8500_bmdata.c
@@ -98,7 +98,6 @@ static const struct ab8500_fg_parameters fg = {
.user_cap_limit = 15,
.pcut_enable = 1,
.pcut_max_time = 127,
- .pcut_flag_time = 112,
.pcut_max_restart = 15,
.pcut_debounce_time = 2,
};
@@ -174,7 +173,7 @@ int ab8500_bm_of_probe(struct power_supply *psy,
if (bi->constant_charge_voltage_max_uv < 0)
bi->constant_charge_voltage_max_uv = 4100000;
- if (bi->charge_term_current_ua)
+ if (bi->charge_term_current_ua < 0)
/* Charging stops when we drop below this current */
bi->charge_term_current_ua = 200000;
diff --git a/drivers/power/supply/ab8500_btemp.c b/drivers/power/supply/ab8500_btemp.c
index 90a61d9db72c..d8845fd2f084 100644
--- a/drivers/power/supply/ab8500_btemp.c
+++ b/drivers/power/supply/ab8500_btemp.c
@@ -42,7 +42,7 @@
#define BTEMP_BATCTRL_CURR_SRC_20UA 20
#define BTEMP_BATCTRL_CURR_SRC_16UA 16
-#define BTEMP_BATCTRL_CURR_SRC_18UA 18
+#define BTEMP_BATCTRL_CURR_SRC_8UA 8
#define BTEMP_BATCTRL_CURR_SRC_60UA 60
#define BTEMP_BATCTRL_CURR_SRC_120UA 120
@@ -468,39 +468,29 @@ static void ab8500_btemp_periodic(struct ab8500_btemp *di,
*/
static int ab8500_btemp_get_temp(struct ab8500_btemp *di)
{
- int temp = 0;
+ int temp = di->bat_temp;
/*
* The BTEMP events are not reliabe on AB8500 cut3.3
* and prior versions
*/
- if (is_ab8500_3p3_or_earlier(di->parent)) {
- temp = di->bat_temp * 10;
- } else {
+ if (!is_ab8500_3p3_or_earlier(di->parent)) {
if (di->events.btemp_low) {
if (temp > di->btemp_ranges.btemp_low_limit)
- temp = di->btemp_ranges.btemp_low_limit * 10;
- else
- temp = di->bat_temp * 10;
+ temp = di->btemp_ranges.btemp_low_limit;
} else if (di->events.btemp_high) {
if (temp < di->btemp_ranges.btemp_high_limit)
- temp = di->btemp_ranges.btemp_high_limit * 10;
- else
- temp = di->bat_temp * 10;
+ temp = di->btemp_ranges.btemp_high_limit;
} else if (di->events.btemp_lowmed) {
if (temp > di->btemp_ranges.btemp_med_limit)
- temp = di->btemp_ranges.btemp_med_limit * 10;
- else
- temp = di->bat_temp * 10;
+ temp = di->btemp_ranges.btemp_med_limit;
} else if (di->events.btemp_medhigh) {
if (temp < di->btemp_ranges.btemp_med_limit)
- temp = di->btemp_ranges.btemp_med_limit * 10;
- else
- temp = di->bat_temp * 10;
- } else
- temp = di->bat_temp * 10;
+ temp = di->btemp_ranges.btemp_med_limit;
+ }
}
- return temp;
+
+ return temp * 10;
}
/**
diff --git a/drivers/power/supply/ab8500_chargalg.c b/drivers/power/supply/ab8500_chargalg.c
index d2b9880b0770..71d82c6c193e 100644
--- a/drivers/power/supply/ab8500_chargalg.c
+++ b/drivers/power/supply/ab8500_chargalg.c
@@ -1241,12 +1241,9 @@ static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di)
charger_status = ab8500_chargalg_check_charger_connection(di);
- if (is_ab8500(di->parent)) {
- ret = ab8500_chargalg_check_charger_enable(di);
- if (ret < 0)
- dev_err(di->dev, "Checking charger is enabled error"
- ": Returned Value %d\n", ret);
- }
+ ret = ab8500_chargalg_check_charger_enable(di);
+ if (ret < 0)
+ dev_err(di->dev, "Checking charger enable failed: %d\n", ret);
/*
* First check if we have a charger connected.
diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c
index ff38254d06ef..19cc56624ee4 100644
--- a/drivers/power/supply/ab8500_charger.c
+++ b/drivers/power/supply/ab8500_charger.c
@@ -52,7 +52,8 @@
#define MAIN_CH_DET 0x01
#define MAIN_CH_CV_ON 0x04
#define USB_CH_CV_ON 0x08
-#define VBUS_DET_DBNC100 0x02
+#define USB_CH_ON 0x04
+#define VBUS_DET_DBNC_LONG 0x02
#define VBUS_DET_DBNC1 0x01
#define OTP_ENABLE_WD 0x01
#define DROP_COUNT_RESET 0x01
@@ -66,7 +67,7 @@
#define LED_INDICATOR_PWM_ENA 0x01
#define LED_INDICATOR_PWM_DIS 0x00
#define LED_IND_CUR_5MA 0x04
-#define LED_INDICATOR_PWM_DUTY_252_256 0xBF
+#define LED_INDICATOR_PWM_DUTY_252_256 0xFB
/* HW failure constants */
#define MAIN_CH_TH_PROT 0x02
@@ -99,6 +100,8 @@
#define CHARGER_STATUS_POLL 10 /* in ms */
+#define AB8505_USB_CHARGER_NOT_OK_RETRIES 3
+
#define CHG_WD_INTERVAL (60 * HZ)
#define AB8500_SW_CONTROL_FALLBACK 0x03
@@ -136,6 +139,33 @@ enum ab8500_charger_link_status {
USB_STAT_ACA_DOCK_CHARGER,
};
+/* UsbLink1Status register values used by AB8505 */
+enum ab8505_charger_link_status {
+ AB8505_USB_STAT_OFF,
+ AB8505_USB_STAT_SDP_NOT_CHARGING,
+ AB8505_USB_STAT_SDP_CHARGING,
+ AB8505_USB_STAT_SDP_SUSPENDED,
+ AB8505_USB_STAT_CDP,
+ AB8505_USB_STAT_RESERVED_5,
+ AB8505_USB_STAT_RESERVED_6,
+ AB8505_USB_STAT_DCP,
+ AB8505_USB_STAT_ACA_RID_A,
+ AB8505_USB_STAT_ACA_RID_B,
+ AB8505_USB_STAT_ACA_RID_C,
+ AB8505_USB_STAT_RESERVED_11,
+ AB8505_USB_STAT_RESERVED_12,
+ AB8505_USB_STAT_UPSTREAM_IDGND,
+ AB8505_USB_STAT_CHARGER_NOT_OK,
+ AB8505_USB_STAT_CHARGER_DM_HIGH,
+ AB8505_USB_STAT_PHY_ENABLED,
+ AB8505_USB_STAT_UPSTREAM_NO_IDGND,
+ AB8505_USB_STAT_UPSTREAM_IDGND_VBUS,
+ AB8505_USB_STAT_CHARGER_SE1,
+ AB8505_USB_STAT_CARKIT_1,
+ AB8505_USB_STAT_CARKIT_2,
+ AB8505_USB_STAT_ACA_DOCK_CHARGER,
+};
+
enum ab8500_usb_state {
AB8500_BM_USB_STATE_RESET_HS, /* HighSpeed Reset */
AB8500_BM_USB_STATE_RESET_FS, /* FullSpeed/LowSpeed Reset */
@@ -234,6 +264,8 @@ struct ab8500_charger_max_usb_in_curr {
* @is_aca_rid: Incicate if accessory is ACA type
* @current_stepping_sessions:
* Counter for current stepping sessions
+ * @usb_charger_not_ok_retries:
+ * Number of AB8505 USB charger recovery attempts
* @parent: Pointer to the struct ab8500
* @adc_main_charger_v ADC channel for main charger voltage
* @adc_main_charger_c ADC channel for main charger current
@@ -287,6 +319,7 @@ struct ab8500_charger {
int invalid_charger_detect_state;
int is_aca_rid;
atomic_t current_stepping_sessions;
+ unsigned int usb_charger_not_ok_retries;
struct ab8500 *parent;
struct iio_channel *adc_main_charger_v;
struct iio_channel *adc_main_charger_c;
@@ -356,6 +389,10 @@ static void ab8500_enable_disable_sw_fallback(struct ab8500_charger *di,
u8 bit;
int ret;
+ /* SwControlFallback is reserved and must remain clear on AB8505. */
+ if (is_ab8505(di->parent))
+ return;
+
dev_dbg(di->dev, "SW Fallback: %d\n", fallback);
if (is_ab8500(di->parent)) {
@@ -442,6 +479,9 @@ static void ab8500_power_supply_changed(struct ab8500_charger *di,
static void ab8500_charger_set_usb_connected(struct ab8500_charger *di,
bool connected)
{
+ if (!connected && !di->vbus_detected)
+ di->usb_charger_not_ok_retries = 0;
+
if (connected != di->usb.charger_connected) {
dev_dbg(di->dev, "USB connected:%i\n", connected);
di->usb.charger_connected = connected;
@@ -661,27 +701,34 @@ static int ab8500_charger_detect_chargers(struct ab8500_charger *di, bool probe)
int ret;
u8 val;
- /* Check for AC charger */
- ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
- AB8500_CH_STATUS1_REG, &val);
- if (ret < 0) {
- dev_err(di->dev, "%s ab8500 read failed\n", __func__);
- return ret;
- }
+ /* AB8505 has no integrated main charger. */
+ if (!is_ab8505(di->parent)) {
+ ret = abx500_get_register_interruptible(di->dev,
+ AB8500_CHARGER,
+ AB8500_CH_STATUS1_REG, &val);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ return ret;
+ }
- if (val & MAIN_CH_DET)
- result = AC_PW_CONN;
+ if (val & MAIN_CH_DET)
+ result = AC_PW_CONN;
+ }
/* Check for USB charger */
if (!probe) {
/*
- * AB8500 says VBUS_DET_DBNC1 & VBUS_DET_DBNC100
+ * AB8500 says VBUS_DET_DBNC1 & VBUS_DET_DBNC_LONG
* when disconnecting ACA even though no
* charger was connected. Try waiting a little
- * longer than the 100 ms of VBUS_DET_DBNC100...
+ * longer than its 100 ms debounce. AB8505 uses
+ * a 300 ms falling debounce for the same bit.
*/
- msleep(110);
+ if (is_ab8505(di->parent))
+ msleep(310);
+ else
+ msleep(110);
}
ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
AB8500_CH_USBCH_STAT1_REG, &val);
@@ -692,27 +739,118 @@ static int ab8500_charger_detect_chargers(struct ab8500_charger *di, bool probe)
dev_dbg(di->dev,
"%s AB8500_CH_USBCH_STAT1_REG %x\n", __func__,
val);
- if ((val & VBUS_DET_DBNC1) && (val & VBUS_DET_DBNC100))
+ if ((val & VBUS_DET_DBNC1) && (val & VBUS_DET_DBNC_LONG))
result |= USB_PW_CONN;
return result;
}
/**
- * ab8500_charger_max_usb_curr() - get the max curr for the USB type
+ * ab8505_charger_max_usb_curr() - get the max current for the USB type
* @di: pointer to the ab8500_charger structure
* @link_status: the identified USB type
*
- * Get the maximum current that is allowed to be drawn from the host
- * based on the USB type.
+ * Decode AB8505-specific UsbLink1Status values and get the maximum current
+ * that is allowed to be drawn based on the USB type.
* Returns error code in case of failure else 0 on success
*/
+static int ab8505_charger_max_usb_curr(struct ab8500_charger *di,
+ u8 link_status)
+{
+ int ret = 0;
+
+ di->is_aca_rid = 0;
+
+ switch (link_status) {
+ case AB8505_USB_STAT_SDP_NOT_CHARGING:
+ case AB8505_USB_STAT_SDP_CHARGING:
+ case AB8505_USB_STAT_SDP_SUSPENDED:
+ case AB8505_USB_STAT_CDP:
+ case AB8505_USB_STAT_CHARGER_DM_HIGH:
+ case AB8505_USB_STAT_UPSTREAM_NO_IDGND:
+ case AB8505_USB_STAT_UPSTREAM_IDGND_VBUS:
+ case AB8505_USB_STAT_CHARGER_SE1:
+ case AB8505_USB_STAT_CARKIT_1:
+ case AB8505_USB_STAT_CARKIT_2:
+ case AB8505_USB_STAT_ACA_DOCK_CHARGER:
+ di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_0P5;
+ break;
+ case AB8505_USB_STAT_DCP:
+ di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_1P5;
+ break;
+ case AB8505_USB_STAT_ACA_RID_A:
+ di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_0P5;
+ di->is_aca_rid = 1;
+ break;
+ case AB8505_USB_STAT_ACA_RID_B:
+ di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_1P3;
+ di->is_aca_rid = 1;
+ break;
+ case AB8505_USB_STAT_ACA_RID_C:
+ di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_1P5;
+ di->is_aca_rid = 1;
+ break;
+ case AB8505_USB_STAT_OFF:
+ if (di->vbus_detected) {
+ di->usb_device_is_unrecognised = true;
+ di->max_usb_in_curr.usb_type_max_ua =
+ USB_CH_IP_CUR_LVL_1P5;
+ break;
+ }
+ fallthrough;
+ case AB8505_USB_STAT_UPSTREAM_IDGND:
+ case AB8505_USB_STAT_PHY_ENABLED:
+ dev_err(di->dev, "USB Type - Charging not allowed\n");
+ di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_0P05;
+ ret = -ENXIO;
+ break;
+ case AB8505_USB_STAT_CHARGER_NOT_OK:
+ di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_0P05;
+ if (di->usb_charger_not_ok_retries <
+ AB8505_USB_CHARGER_NOT_OK_RETRIES) {
+ dev_warn(di->dev,
+ "USB Type - VBUS has collapsed, retrying\n");
+ queue_delayed_work(di->charger_wq,
+ &di->check_usbchgnotok_work, 0);
+ ret = -EAGAIN;
+ } else {
+ dev_err(di->dev,
+ "USB Type - VBUS recovery failed\n");
+ ret = -ENXIO;
+ }
+ break;
+ case AB8505_USB_STAT_RESERVED_5:
+ case AB8505_USB_STAT_RESERVED_6:
+ case AB8505_USB_STAT_RESERVED_11:
+ case AB8505_USB_STAT_RESERVED_12:
+ default:
+ dev_err(di->dev, "USB Type - Unknown\n");
+ di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_0P05;
+ ret = -ENXIO;
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * ab8500_charger_max_usb_curr() - get the max current for the USB type
+ * @di: pointer to the ab8500_charger structure
+ * @link_status: the identified USB type
+ *
+ * Get the maximum current that is allowed to be drawn based on the USB type.
+ * Returns error code in case of failure else 0 on success.
+ */
static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
- enum ab8500_charger_link_status link_status)
+ u8 link_status)
{
int ret = 0;
di->usb_device_is_unrecognised = false;
+ if (is_ab8505(di->parent)) {
+ ret = ab8505_charger_max_usb_curr(di, link_status);
+ goto out;
+ }
/*
* Platform only supports USB 2.0.
@@ -721,7 +859,7 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
* should set USB_CH_IP_CUR_LVL_0P5.
*/
- switch (link_status) {
+ switch ((enum ab8500_charger_link_status)link_status) {
case USB_STAT_STD_HOST_NC:
case USB_STAT_STD_HOST_C_NS:
case USB_STAT_STD_HOST_C_S:
@@ -825,6 +963,7 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
break;
}
+out:
di->max_usb_in_curr.set_max_ua = di->max_usb_in_curr.usb_type_max_ua;
dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d",
link_status, di->max_usb_in_curr.set_max_ua);
@@ -866,8 +1005,7 @@ static int ab8500_charger_read_usb_type(struct ab8500_charger *di)
val = (val & AB8500_USB_LINK_STATUS) >> USB_LINK_STATUS_SHIFT;
else
val = (val & AB8505_USB_LINK_STATUS) >> USB_LINK_STATUS_SHIFT;
- ret = ab8500_charger_max_usb_curr(di,
- (enum ab8500_charger_link_status) val);
+ ret = ab8500_charger_max_usb_curr(di, val);
return ret;
}
@@ -929,8 +1067,7 @@ static int ab8500_charger_detect_usb_type(struct ab8500_charger *di)
if (val)
break;
}
- ret = ab8500_charger_max_usb_curr(di,
- (enum ab8500_charger_link_status) val);
+ ret = ab8500_charger_max_usb_curr(di, val);
return ret;
}
@@ -1741,6 +1878,9 @@ static int ab8500_charger_usb_check_enable(struct ux500_charger *charger,
int vset_uv, int iset_ua)
{
u8 usbch_ctrl1 = 0;
+ u8 usbch_status1;
+ u8 usbch_status2;
+ bool reenable;
int ret = 0;
struct ab8500_charger *di = to_ab8500_charger_usb_device_info(charger);
@@ -1756,7 +1896,52 @@ static int ab8500_charger_usb_check_enable(struct ux500_charger *charger,
}
dev_dbg(di->dev, "USB charger ctrl: 0x%02x\n", usbch_ctrl1);
- if (!(usbch_ctrl1 & USB_CH_ENA)) {
+ reenable = !(usbch_ctrl1 & USB_CH_ENA);
+ if (is_ab8505(di->parent)) {
+ ret = abx500_get_register_interruptible(di->dev,
+ AB8500_CHARGER,
+ AB8500_CH_USBCH_STAT1_REG,
+ &usbch_status1);
+ if (ret < 0) {
+ dev_err(di->dev, "USB charger status read failed\n");
+ return ret;
+ }
+
+ if (!reenable && (usbch_status1 & USB_CH_ON))
+ return 0;
+
+ if (!(usbch_status1 & VBUS_DET_DBNC_LONG))
+ return 0;
+
+ ret = abx500_get_register_interruptible(di->dev,
+ AB8500_CHARGER,
+ AB8500_CH_USBCH_STAT2_REG,
+ &usbch_status2);
+ if (ret < 0) {
+ dev_err(di->dev, "USB charger fault status read failed\n");
+ return ret;
+ }
+
+ if ((usbch_status2 &
+ (VBUS_CH_NOK | USB_CH_TH_PROT | VBUS_OVV_TH)) ||
+ di->flags.usbchargernotok || di->flags.usb_thermal_prot ||
+ di->flags.vbus_ovv || di->usb.wd_expired)
+ return 0;
+
+ if (!reenable) {
+ dev_info(di->dev,
+ "USB charger enabled but inactive, cycling it\n");
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_CHARGER,
+ AB8500_USBCH_CTRL1_REG,
+ USB_CH_ENA, 0);
+ if (ret < 0)
+ return ret;
+ reenable = true;
+ }
+ }
+
+ if (reenable) {
dev_info(di->dev, "Charging has been disabled abnormally and will be re-enabled\n");
ret = abx500_mask_and_set_register_interruptible(di->dev,
@@ -1778,7 +1963,7 @@ static int ab8500_charger_usb_check_enable(struct ux500_charger *charger,
}
/**
- * ab8500_charger_ac_check_enable() - enable usb charging
+ * ab8500_charger_ac_check_enable() - enable AC charging
* @charger: pointer to the ux500_charger structure
* @vset_uv: charging voltage in microvolt
* @iset_ua: charger output current in micrompere
@@ -1817,7 +2002,7 @@ static int ab8500_charger_ac_check_enable(struct ux500_charger *charger,
return ret;
}
- ret = ab8500_charger_ac_en(&di->usb_chg, true, vset_uv, iset_ua);
+ ret = ab8500_charger_ac_en(&di->ac_chg, true, vset_uv, iset_ua);
if (ret < 0) {
dev_err(di->dev, "failed to enable AC charger %d\n",
__LINE__);
@@ -2277,6 +2462,7 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
int ret;
u8 val;
u8 link_status;
+ u8 link_status_mask;
struct ab8500_charger *di = container_of(work,
struct ab8500_charger, usb_link_status_work);
@@ -2303,20 +2489,22 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
AB8500_USB_LINK1_STAT_REG, &val);
- if (ret >= 0)
- dev_dbg(di->dev, "UsbLineStatus register = 0x%02x\n", val);
- else
- dev_dbg(di->dev, "Error reading USB link status\n");
+ if (ret < 0) {
+ dev_err(di->dev, "Error reading USB link status\n");
+ return;
+ }
+ dev_dbg(di->dev, "UsbLineStatus register = 0x%02x\n", val);
if (is_ab8500(di->parent))
- link_status = AB8500_USB_LINK_STATUS;
+ link_status_mask = AB8500_USB_LINK_STATUS;
else
- link_status = AB8505_USB_LINK_STATUS;
+ link_status_mask = AB8505_USB_LINK_STATUS;
+ link_status = (val & link_status_mask) >> USB_LINK_STATUS_SHIFT;
if (detected_chargers & USB_PW_CONN) {
- if (((val & link_status) >> USB_LINK_STATUS_SHIFT) ==
- USB_STAT_NOT_VALID_LINK &&
- di->invalid_charger_detect_state == 0) {
+ if (is_ab8500(di->parent) &&
+ link_status == USB_STAT_NOT_VALID_LINK &&
+ di->invalid_charger_detect_state == 0) {
dev_dbg(di->dev,
"Invalid charger detected, state= 0\n");
/*Enable charger*/
@@ -2339,18 +2527,21 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
abx500_mask_and_set_register_interruptible(di->dev,
AB8500_USB, AB8500_USB_LINE_CTRL2_REG,
USB_CH_DET, 0x00);
- /*Check link status*/
- if (is_ab8500(di->parent))
- ret = abx500_get_register_interruptible(di->dev,
- AB8500_USB, AB8500_USB_LINE_STAT_REG,
- &val);
- else
- ret = abx500_get_register_interruptible(di->dev,
- AB8500_USB, AB8500_USB_LINK1_STAT_REG,
- &val);
+ /* Check link status */
+ ret = abx500_get_register_interruptible(di->dev,
+ AB8500_USB,
+ AB8500_USB_LINE_STAT_REG,
+ &val);
+ if (ret < 0) {
+ dev_err(di->dev,
+ "Error reading USB link status\n");
+ return;
+ }
+ link_status = (val & link_status_mask) >>
+ USB_LINK_STATUS_SHIFT;
dev_dbg(di->dev, "USB link status= 0x%02x\n",
- (val & link_status) >> USB_LINK_STATUS_SHIFT);
+ link_status);
di->invalid_charger_detect_state = 2;
}
} else {
@@ -2462,6 +2653,48 @@ static void ab8500_charger_usb_state_changed_work(struct work_struct *work)
}
}
+static int ab8505_charger_retry_usb_detection(struct ab8500_charger *di)
+{
+ u8 usbch_ctrl1;
+ bool usb_enabled;
+ int restore_ret;
+ int ret;
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_USBCH_CTRL1_REG,
+ &usbch_ctrl1);
+ if (ret < 0)
+ return ret;
+
+ usb_enabled = usbch_ctrl1 & USB_CH_ENA;
+ if (usb_enabled) {
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_CHARGER,
+ AB8500_USBCH_CTRL1_REG,
+ USB_CH_ENA, 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_CHARGER,
+ AB8500_CHARGER_CTRL,
+ DROP_COUNT_RESET,
+ DROP_COUNT_RESET);
+
+ if (usb_enabled) {
+ restore_ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_CHARGER,
+ AB8500_USBCH_CTRL1_REG,
+ USB_CH_ENA,
+ USB_CH_ENA);
+ if (restore_ret < 0)
+ return restore_ret;
+ }
+
+ return ret;
+}
+
/**
* ab8500_charger_check_usbchargernotok_work() - check USB chg not ok status
* @work: pointer to the work_struct structure
@@ -2471,7 +2704,10 @@ static void ab8500_charger_usb_state_changed_work(struct work_struct *work)
static void ab8500_charger_check_usbchargernotok_work(struct work_struct *work)
{
int ret;
+ u8 link_status = 0;
u8 reg_value;
+ bool charger_not_ok;
+ bool prev_collapse;
bool prev_status;
struct ab8500_charger *di = container_of(work,
@@ -2484,19 +2720,61 @@ static void ab8500_charger_check_usbchargernotok_work(struct work_struct *work)
dev_err(di->dev, "%s ab8500 read failed\n", __func__);
return;
}
+ charger_not_ok = reg_value & VBUS_CH_NOK;
+ if (is_ab8505(di->parent)) {
+ ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
+ AB8500_USB_LINK1_STAT_REG,
+ &link_status);
+ if (ret < 0) {
+ dev_err(di->dev, "%s USB link status read failed\n",
+ __func__);
+ return;
+ }
+
+ link_status = (link_status & AB8505_USB_LINK_STATUS) >>
+ USB_LINK_STATUS_SHIFT;
+ charger_not_ok |=
+ link_status == AB8505_USB_STAT_CHARGER_NOT_OK;
+ }
+
prev_status = di->flags.usbchargernotok;
+ prev_collapse = di->flags.vbus_collapse;
+
+ if (is_ab8505(di->parent) &&
+ link_status == AB8505_USB_STAT_CHARGER_NOT_OK &&
+ di->usb_charger_not_ok_retries <
+ AB8505_USB_CHARGER_NOT_OK_RETRIES) {
+ di->usb_charger_not_ok_retries++;
+ di->flags.usbchargernotok = false;
+ di->flags.vbus_collapse = true;
+ dev_warn(di->dev, "Retrying USB charger detection (%u/%u)\n",
+ di->usb_charger_not_ok_retries,
+ AB8505_USB_CHARGER_NOT_OK_RETRIES);
+
+ ret = ab8505_charger_retry_usb_detection(di);
+ if (ret < 0)
+ dev_err(di->dev, "USB charger retry failed: %d\n", ret);
- if (reg_value & VBUS_CH_NOK) {
+ queue_delayed_work(di->charger_wq,
+ &di->check_usbchgnotok_work, HZ);
+ } else if (charger_not_ok) {
di->flags.usbchargernotok = true;
+ if (is_ab8505(di->parent) &&
+ link_status == AB8505_USB_STAT_CHARGER_NOT_OK) {
+ di->flags.vbus_collapse = false;
+ ab8500_charger_set_usb_connected(di, false);
+ }
/* Check again in 1sec */
queue_delayed_work(di->charger_wq,
&di->check_usbchgnotok_work, HZ);
} else {
di->flags.usbchargernotok = false;
di->flags.vbus_collapse = false;
+ di->usb_charger_not_ok_retries = 0;
}
- if (prev_status != di->flags.usbchargernotok)
+ if (prev_status != di->flags.usbchargernotok ||
+ prev_collapse != di->flags.vbus_collapse)
ab8500_power_supply_changed(di, di->usb_chg.psy);
}
@@ -3063,7 +3341,7 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di)
ret = abx500_set_register_interruptible(di->dev,
AB8500_CHARGER, AB8500_CH_OPT_CRNTLVL_MAX_REG,
- CH_OP_CUR_LVL_1P6);
+ CH_OP_CUR_LVL_1P5_MAX);
if (ret) {
dev_err(di->dev,
"failed to set CH_OPT_CRNTLVL_MAX_REG\n");
@@ -3071,7 +3349,7 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di)
}
}
- if (is_ab8505_2p0(di->parent))
+ if (is_ab8505(di->parent))
ret = abx500_mask_and_set_register_interruptible(di->dev,
AB8500_CHARGER,
AB8500_USBCH_CTRL2_REG,
@@ -3340,6 +3618,10 @@ static int ab8500_charger_bind(struct device *dev)
}
ch_stat = ab8500_charger_detect_chargers(di, false);
+ if (ch_stat < 0) {
+ destroy_workqueue(di->charger_wq);
+ return ch_stat;
+ }
if (ch_stat & AC_PW_CONN) {
if (is_ab8500(di->parent))
@@ -3374,7 +3656,8 @@ static void ab8500_charger_unbind(struct device *dev)
int ret;
/* Disable AC charging */
- ab8500_charger_ac_en(&di->ac_chg, false, 0, 0);
+ if (di->ac_chg.enabled)
+ ab8500_charger_ac_en(&di->ac_chg, false, 0, 0);
/* Disable USB charging */
ab8500_charger_usb_en(&di->usb_chg, false, 0, 0);
@@ -3420,11 +3703,14 @@ static int ab8500_charger_probe(struct platform_device *pdev)
di->bm = &ab8500_bm_data;
- di->autopower_cfg = of_property_read_bool(np, "autopower_cfg");
-
/* get parent data */
di->dev = dev;
di->parent = dev_get_drvdata(pdev->dev.parent);
+ di->autopower_cfg = of_property_read_bool(np, "autopower_cfg");
+ if (di->autopower_cfg && is_ab8505(di->parent)) {
+ dev_warn(dev, "autopower is not supported on AB8505\n");
+ di->autopower_cfg = false;
+ }
/* Get ADC channels */
if (!is_ab8505(di->parent)) {
@@ -3595,7 +3881,13 @@ static int ab8500_charger_probe(struct platform_device *pdev)
}
/* Request interrupts */
- for (i = 0; i < ARRAY_SIZE(ab8500_charger_irq); i++) {
+ /* The first five interrupts belong to the AB8500 main charger. */
+ if (is_ab8505(di->parent))
+ i = 5;
+ else
+ i = 0;
+
+ for (; i < ARRAY_SIZE(ab8500_charger_irq); i++) {
irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name);
if (irq < 0)
return irq;
@@ -3622,6 +3914,10 @@ static int ab8500_charger_probe(struct platform_device *pdev)
/* Identify the connected charger types during startup */
charger_status = ab8500_charger_detect_chargers(di, true);
+ if (charger_status < 0) {
+ ret = charger_status;
+ goto remove_ab8500_bm;
+ }
if (charger_status & AC_PW_CONN) {
di->ac.charger_connected = 1;
di->ac_conn = true;
diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c
index e6b5efdf5dc9..069e7fee1d16 100644
--- a/drivers/power/supply/ab8500_fg.c
+++ b/drivers/power/supply/ab8500_fg.c
@@ -451,6 +451,16 @@ static void ab8500_fg_fill_cap_sample(struct ab8500_fg *di, int sample)
avg->avg = sample;
}
+static u8 ab8500_fg_cc_config(struct ab8500_fg *di)
+{
+ u8 config = CC_PWR_UP_ENA;
+
+ if (!is_ab8505(di->parent))
+ config |= CC_DEEP_SLEEP_ENA;
+
+ return config;
+}
+
/**
* ab8500_fg_coulomb_counter() - enable coulomb counter
* @di: pointer to the ab8500_fg structure
@@ -481,7 +491,7 @@ static int ab8500_fg_coulomb_counter(struct ab8500_fg *di, bool enable)
/* Start the CC */
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
AB8500_RTC_CC_CONF_REG,
- (CC_DEEP_SLEEP_ENA | CC_PWR_UP_ENA));
+ ab8500_fg_cc_config(di));
if (ret)
goto cc_err;
@@ -555,7 +565,7 @@ int ab8500_fg_inst_curr_start(struct ab8500_fg *di)
/* Start the CC */
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
AB8500_RTC_CC_CONF_REG,
- (CC_DEEP_SLEEP_ENA | CC_PWR_UP_ENA));
+ ab8500_fg_cc_config(di));
if (ret)
goto fail;
} else {
@@ -934,9 +944,15 @@ static int ab8500_load_comp_fg_bat_voltage(struct ab8500_fg *di, bool always)
int i = 0;
int vbat_uv = 0;
int rcomp;
+ int ret;
/* Average the instant current to get a stable current measurement */
- ab8500_fg_inst_curr_start(di);
+ ret = ab8500_fg_inst_curr_start(di);
+ if (ret) {
+ dev_err(di->dev, "failed to start instantaneous current measurement\n");
+ di->vbat_uv = ab8500_fg_bat_voltage(di);
+ return di->vbat_uv;
+ }
do {
vbat_uv += ab8500_fg_bat_voltage(di);
@@ -945,15 +961,14 @@ static int ab8500_load_comp_fg_bat_voltage(struct ab8500_fg *di, bool always)
} while (!ab8500_fg_inst_curr_done(di) &&
i <= WAIT_FOR_INST_CURRENT_MAX);
- if (i > WAIT_FOR_INST_CURRENT_MAX) {
+ ret = ab8500_fg_inst_curr_finalize(di, &di->inst_curr_ua);
+ if (ret) {
dev_err(di->dev,
"TIMEOUT: return uncompensated measurement of VBAT\n");
di->vbat_uv = vbat_uv / i;
return di->vbat_uv;
}
- ab8500_fg_inst_curr_finalize(di, &di->inst_curr_ua);
-
/*
* If there is too high current dissipation, the compensation cannot be
* trusted so return an error unless we must return something here, as
@@ -1387,6 +1402,13 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init)
}
}
+ /* Keep the charge properties current between percentage changes. */
+ if (di->bat_cap.prev_percent == percent &&
+ di->bat_cap.prev_mah != di->bat_cap.mah) {
+ di->bat_cap.prev_mah = di->bat_cap.mah;
+ changed = true;
+ }
+
if (changed) {
if (di->bm->capacity_scaling) {
di->bat_cap.cap_scale.scaled_cap =
@@ -2205,42 +2227,36 @@ static int ab8500_fg_get_ext_psy_data(struct power_supply *ext, void *data)
switch (prop) {
case POWER_SUPPLY_PROP_STATUS:
- switch (ext->desc->type) {
- case POWER_SUPPLY_TYPE_BATTERY:
- switch (ret.intval) {
- case POWER_SUPPLY_STATUS_UNKNOWN:
- case POWER_SUPPLY_STATUS_DISCHARGING:
- case POWER_SUPPLY_STATUS_NOT_CHARGING:
- if (!di->flags.charging)
- break;
- di->flags.charging = false;
- di->flags.fully_charged = false;
- if (di->bm->capacity_scaling)
- ab8500_fg_update_cap_scalers(di);
- queue_work(di->fg_wq, &di->fg_work);
- break;
- case POWER_SUPPLY_STATUS_FULL:
- if (di->flags.fully_charged)
- break;
- di->flags.fully_charged = true;
- di->flags.force_full = true;
- /* Save current capacity as maximum */
- di->bat_cap.max_mah = di->bat_cap.mah;
- queue_work(di->fg_wq, &di->fg_work);
+ switch (ret.intval) {
+ case POWER_SUPPLY_STATUS_UNKNOWN:
+ case POWER_SUPPLY_STATUS_DISCHARGING:
+ case POWER_SUPPLY_STATUS_NOT_CHARGING:
+ if (!di->flags.charging)
break;
- case POWER_SUPPLY_STATUS_CHARGING:
- if (di->flags.charging &&
- !di->flags.fully_charged)
- break;
- di->flags.charging = true;
- di->flags.fully_charged = false;
- if (di->bm->capacity_scaling)
- ab8500_fg_update_cap_scalers(di);
- queue_work(di->fg_wq, &di->fg_work);
+ di->flags.charging = false;
+ di->flags.fully_charged = false;
+ if (di->bm->capacity_scaling)
+ ab8500_fg_update_cap_scalers(di);
+ queue_work(di->fg_wq, &di->fg_work);
+ break;
+ case POWER_SUPPLY_STATUS_FULL:
+ if (di->flags.fully_charged)
break;
- }
+ di->flags.fully_charged = true;
+ di->flags.force_full = true;
+ /* Save current capacity as maximum */
+ di->bat_cap.max_mah = di->bat_cap.mah;
+ queue_work(di->fg_wq, &di->fg_work);
break;
- default:
+ case POWER_SUPPLY_STATUS_CHARGING:
+ if (di->flags.charging &&
+ !di->flags.fully_charged)
+ break;
+ di->flags.charging = true;
+ di->flags.fully_charged = false;
+ if (di->bm->capacity_scaling)
+ ab8500_fg_update_cap_scalers(di);
+ queue_work(di->fg_wq, &di->fg_work);
break;
}
break;
@@ -2355,14 +2371,6 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di)
}
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
- AB8505_RTC_PCUT_FLAG_TIME_REG, di->bm->fg_params->pcut_flag_time);
-
- if (ret) {
- dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_FLAG_TIME_REG\n", __func__);
- goto out;
- }
-
- ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
AB8505_RTC_PCUT_RESTART_REG, di->bm->fg_params->pcut_max_restart);
if (ret) {
@@ -2568,56 +2576,6 @@ static int ab8500_fg_sysfs_init(struct ab8500_fg *di)
return ret;
}
-static ssize_t ab8505_powercut_flagtime_read(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- int ret;
- u8 reg_value;
- struct power_supply *psy = dev_to_psy(dev);
- struct ab8500_fg *di = power_supply_get_drvdata(psy);
-
- ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
- AB8505_RTC_PCUT_FLAG_TIME_REG, &reg_value);
-
- if (ret < 0) {
- dev_err(dev, "Failed to read AB8505_RTC_PCUT_FLAG_TIME_REG\n");
- goto fail;
- }
-
- return sysfs_emit(buf, "%d\n", (reg_value & 0x7F));
-
-fail:
- return ret;
-}
-
-static ssize_t ab8505_powercut_flagtime_write(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- int ret;
- int reg_value;
- struct power_supply *psy = dev_to_psy(dev);
- struct ab8500_fg *di = power_supply_get_drvdata(psy);
-
- if (kstrtoint(buf, 10, &reg_value))
- goto fail;
-
- if (reg_value > 0x7F) {
- dev_err(dev, "Incorrect parameter, echo 0 (1.98s) - 127 (15.625ms) for flagtime\n");
- goto fail;
- }
-
- ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
- AB8505_RTC_PCUT_FLAG_TIME_REG, (u8)reg_value);
-
- if (ret < 0)
- dev_err(dev, "Failed to set AB8505_RTC_PCUT_FLAG_TIME_REG\n");
-
-fail:
- return count;
-}
-
static ssize_t ab8505_powercut_maxtime_read(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -2814,30 +2772,6 @@ fail:
return count;
}
-static ssize_t ab8505_powercut_flag_read(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
-
- int ret;
- u8 reg_value;
- struct power_supply *psy = dev_to_psy(dev);
- struct ab8500_fg *di = power_supply_get_drvdata(psy);
-
- ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
- AB8505_RTC_PCUT_CTL_STATUS_REG, &reg_value);
-
- if (ret < 0) {
- dev_err(dev, "Failed to read AB8505_RTC_PCUT_CTL_STATUS_REG\n");
- goto fail;
- }
-
- return sysfs_emit(buf, "%d\n", ((reg_value & 0x10) >> 4));
-
-fail:
- return ret;
-}
-
static ssize_t ab8505_powercut_debounce_read(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -2912,8 +2846,6 @@ fail:
}
static struct device_attribute ab8505_fg_sysfs_psy_attrs[] = {
- __ATTR(powercut_flagtime, (S_IRUGO | S_IWUSR | S_IWGRP),
- ab8505_powercut_flagtime_read, ab8505_powercut_flagtime_write),
__ATTR(powercut_maxtime, (S_IRUGO | S_IWUSR | S_IWGRP),
ab8505_powercut_maxtime_read, ab8505_powercut_maxtime_write),
__ATTR(powercut_restart_max, (S_IRUGO | S_IWUSR | S_IWGRP),
@@ -2923,7 +2855,6 @@ static struct device_attribute ab8505_fg_sysfs_psy_attrs[] = {
ab8505_powercut_restart_counter_read, NULL),
__ATTR(powercut_enable, (S_IRUGO | S_IWUSR | S_IWGRP),
ab8505_powercut_read, ab8505_powercut_write),
- __ATTR(powercut_flag, S_IRUGO, ab8505_powercut_flag_read, NULL),
__ATTR(powercut_debounce_time, (S_IRUGO | S_IWUSR | S_IWGRP),
ab8505_powercut_debounce_read, ab8505_powercut_debounce_write),
__ATTR(powercut_enable_status, S_IRUGO,
diff --git a/drivers/power/supply/axp20x_ac_power.c b/drivers/power/supply/axp20x_ac_power.c
index d9892b351621..46889567bc18 100644
--- a/drivers/power/supply/axp20x_ac_power.c
+++ b/drivers/power/supply/axp20x_ac_power.c
@@ -326,9 +326,6 @@ static int axp20x_ac_power_probe(struct platform_device *pdev)
const struct axp_data *axp_data;
int i, irq, ret;
- if (!of_device_is_available(pdev->dev.of_node))
- return -ENODEV;
-
if (!axp20x) {
dev_err(&pdev->dev, "Parent drvdata not set\n");
return -EINVAL;
diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c
index ee8701a6e907..c119014b319a 100644
--- a/drivers/power/supply/axp20x_battery.c
+++ b/drivers/power/supply/axp20x_battery.c
@@ -1097,9 +1097,6 @@ static int axp20x_power_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
int ret;
- if (!of_device_is_available(pdev->dev.of_node))
- return -ENODEV;
-
axp20x_batt = devm_kzalloc(&pdev->dev, sizeof(*axp20x_batt),
GFP_KERNEL);
if (!axp20x_batt)
diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c
index e951b2b64b25..7625366104bc 100644
--- a/drivers/power/supply/axp20x_usb_power.c
+++ b/drivers/power/supply/axp20x_usb_power.c
@@ -918,73 +918,68 @@ static void axp20x_usb_power_parse_dt(struct device *dev,
static int axp20x_usb_power_probe(struct platform_device *pdev)
{
- struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+ struct device *dev = &pdev->dev;
+ struct axp20x_dev *axp20x = dev_get_drvdata(dev->parent);
struct power_supply_config psy_cfg = {};
struct axp20x_usb_power *power;
const struct axp_data *axp_data;
int i, irq, ret;
- if (!of_device_is_available(pdev->dev.of_node))
- return -ENODEV;
+ if (!axp20x)
+ return dev_err_probe(dev, -EINVAL, "Parent drvdata not set\n");
- if (!axp20x) {
- dev_err(&pdev->dev, "Parent drvdata not set\n");
- return -EINVAL;
- }
+ axp_data = of_device_get_match_data(dev);
- axp_data = of_device_get_match_data(&pdev->dev);
-
- power = devm_kzalloc(&pdev->dev,
- struct_size(power, irqs, axp_data->num_irq_names),
+ power = devm_kzalloc(dev, struct_size(power, irqs, axp_data->num_irq_names),
GFP_KERNEL);
if (!power)
return -ENOMEM;
platform_set_drvdata(pdev, power);
- power->dev = &pdev->dev;
+ power->dev = dev;
power->axp_data = axp_data;
power->regmap = axp20x->regmap;
power->num_irqs = axp_data->num_irq_names;
- power->curr_lim_fld = devm_regmap_field_alloc(&pdev->dev, power->regmap,
+ power->curr_lim_fld = devm_regmap_field_alloc(dev, power->regmap,
axp_data->curr_lim_fld);
if (IS_ERR(power->curr_lim_fld))
return PTR_ERR(power->curr_lim_fld);
- axp20x_usb_power_parse_dt(&pdev->dev, power);
+ axp20x_usb_power_parse_dt(dev, power);
- ret = axp20x_regmap_field_alloc_optional(&pdev->dev, power->regmap,
+ ret = axp20x_regmap_field_alloc_optional(dev, power->regmap,
axp_data->vbus_valid_bit,
&power->vbus_valid_bit);
if (ret)
return ret;
- ret = axp20x_regmap_field_alloc_optional(&pdev->dev, power->regmap,
+ ret = axp20x_regmap_field_alloc_optional(dev, power->regmap,
axp_data->vbus_mon_bit,
&power->vbus_mon_bit);
if (ret)
return ret;
- ret = axp20x_regmap_field_alloc_optional(&pdev->dev, power->regmap,
+ ret = axp20x_regmap_field_alloc_optional(dev, power->regmap,
axp_data->usb_bc_en_bit,
&power->usb_bc_en_bit);
if (ret)
return ret;
- ret = axp20x_regmap_field_alloc_optional(&pdev->dev, power->regmap,
+ ret = axp20x_regmap_field_alloc_optional(dev, power->regmap,
axp_data->usb_bc_det_fld,
&power->usb_bc_det_fld);
if (ret)
return ret;
- ret = axp20x_regmap_field_alloc_optional(&pdev->dev, power->regmap,
+ ret = axp20x_regmap_field_alloc_optional(dev, power->regmap,
axp_data->vbus_disable_bit,
&power->vbus_disable_bit);
if (ret)
return ret;
- ret = devm_delayed_work_autocancel(&pdev->dev, &power->vbus_detect,
+ ret = devm_delayed_work_autocancel(dev, &power->vbus_detect,
axp_data->axp20x_read_vbus);
if (ret)
return ret;
@@ -1001,7 +996,9 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
ret = axp_data->axp20x_cfg_adc_reg(power);
if (ret)
- return ret;
+ return dev_err_probe(dev, ret,
+ "Failed to initialize for %s usage\n",
+ IS_ENABLED(CONFIG_AXP20X_ADC) ? "iio" : "adc");
}
if (power->usb_bc_en_bit) {
@@ -1011,26 +1008,29 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
return ret;
}
- psy_cfg.fwnode = dev_fwnode(&pdev->dev);
+ psy_cfg.fwnode = dev_fwnode(dev);
psy_cfg.drv_data = power;
- power->supply = devm_power_supply_register(&pdev->dev,
+ power->supply = devm_power_supply_register(dev,
axp_data->power_desc,
&psy_cfg);
if (IS_ERR(power->supply))
- return PTR_ERR(power->supply);
+ return dev_err_probe(dev, PTR_ERR(power->supply),
+ "Failed to register power supply\n");
/* Request irqs after registering, as irqs may trigger immediately */
for (i = 0; i < axp_data->num_irq_names; i++) {
irq = platform_get_irq_byname(pdev, axp_data->irq_names[i]);
if (irq < 0)
+ /* platform_get_irq_byname() already produced an error message */
return irq;
power->irqs[i] = regmap_irq_get_virq(axp20x->regmap_irqc, irq);
- ret = devm_request_any_context_irq(&pdev->dev, power->irqs[i],
+ ret = devm_request_any_context_irq(dev, power->irqs[i],
axp20x_usb_power_irq, 0,
DRVNAME, power);
if (ret < 0)
+ /* devm_request_any_context_irq() already produced an error message */
return ret;
}
diff --git a/drivers/power/supply/bq24190_charger.c b/drivers/power/supply/bq24190_charger.c
index c9b86d5a21a4..7a5867dd74c2 100644
--- a/drivers/power/supply/bq24190_charger.c
+++ b/drivers/power/supply/bq24190_charger.c
@@ -2311,25 +2311,6 @@ static __maybe_unused int bq24190_runtime_resume(struct device *dev)
return 0;
}
-static __maybe_unused int bq24190_pm_suspend(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct bq24190_dev_info *bdi = i2c_get_clientdata(client);
- int error;
-
- error = pm_runtime_resume_and_get(bdi->dev);
- if (error < 0)
- dev_warn(bdi->dev, "pm_runtime_get failed: %i\n", error);
-
- bq24190_register_reset(bdi);
-
- if (error >= 0) {
- pm_runtime_put_autosuspend(bdi->dev);
- }
-
- return 0;
-}
-
static __maybe_unused int bq24190_pm_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
@@ -2343,7 +2324,14 @@ static __maybe_unused int bq24190_pm_resume(struct device *dev)
if (error < 0)
dev_warn(bdi->dev, "pm_runtime_get failed: %i\n", error);
- bq24190_register_reset(bdi);
+ /*
+ * The chip kept its configuration through the sleep: it is in host
+ * mode with the i2c watchdog off, so nothing expired and nothing was
+ * reset. Do not reset it here either -- a userspace setting such as
+ * EN_HIZ or IINLIM would otherwise be silently lost on every resume.
+ * Re-applying the probe-time configuration is idempotent and cheap,
+ * and covers a part that did somehow fall back to default mode.
+ */
bq24190_set_config(bdi);
bq24190_read(bdi, BQ24190_REG_SS, &bdi->ss_reg);
@@ -2362,7 +2350,7 @@ static __maybe_unused int bq24190_pm_resume(struct device *dev)
static const struct dev_pm_ops bq24190_pm_ops = {
SET_RUNTIME_PM_OPS(bq24190_runtime_suspend, bq24190_runtime_resume,
NULL)
- SET_SYSTEM_SLEEP_PM_OPS(bq24190_pm_suspend, bq24190_pm_resume)
+ SET_SYSTEM_SLEEP_PM_OPS(NULL, bq24190_pm_resume)
};
static const struct i2c_device_id bq24190_i2c_ids[] = {
diff --git a/drivers/power/supply/bq256xx_charger.c b/drivers/power/supply/bq256xx_charger.c
index 4b1f81b1ed86..454bc497b0d8 100644
--- a/drivers/power/supply/bq256xx_charger.c
+++ b/drivers/power/supply/bq256xx_charger.c
@@ -16,6 +16,7 @@
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/acpi.h>
+#include <linux/extcon-provider.h>
#define BQ256XX_MANUFACTURER "Texas Instruments"
@@ -209,6 +210,7 @@ enum bq256xx_id {
* @client: i2c client structure
* @regmap: register map structure
* @dev: device structure
+ * @edev: extcon device registered to report vbus state changes
* @charger: power supply registered for the charger
* @battery: power supply registered for the battery
* @lock: mutex lock structure
@@ -229,6 +231,7 @@ enum bq256xx_id {
struct bq256xx_device {
struct i2c_client *client;
struct device *dev;
+ struct extcon_dev *edev;
struct power_supply *charger;
struct power_supply *battery;
struct mutex lock;
@@ -1164,8 +1167,14 @@ static int bq256xx_get_charger_property(struct power_supply *psy,
return ret;
}
-static bool bq256xx_state_changed(struct bq256xx_device *bq,
- struct bq256xx_state *new_state)
+enum bq256xx_state_change {
+ CHANGED_NONE,
+ CHANGED_VBUS,
+ CHANGED_OTHER
+};
+
+static enum bq256xx_state_change bq256xx_state_changed(struct bq256xx_device *bq,
+ struct bq256xx_state *new_state)
{
struct bq256xx_state old_state;
@@ -1173,27 +1182,92 @@ static bool bq256xx_state_changed(struct bq256xx_device *bq,
old_state = bq->state;
mutex_unlock(&bq->lock);
- return memcmp(&old_state, new_state, sizeof(struct bq256xx_state)) != 0;
+ if (new_state->online != old_state.online)
+ return CHANGED_VBUS;
+
+ if (memcmp(&old_state, new_state, sizeof(struct bq256xx_state)) != 0)
+ return CHANGED_OTHER;
+
+ return CHANGED_NONE;
+}
+
+static const unsigned int bq256xx_usb_extcon_cable[] = {
+ EXTCON_USB,
+ EXTCON_NONE,
+};
+
+static void bq256xx_extcon_update(struct bq256xx_device *bq, bool online)
+{
+ int ret;
+
+ if (!bq->edev)
+ return;
+
+ ret = extcon_set_state_sync(bq->edev, EXTCON_USB, online);
+ if (ret)
+ dev_err(bq->dev, "Failed to update extcon state: %d\n", ret);
+}
+
+static int bq256xx_extcon_init(struct bq256xx_device *bq)
+{
+ unsigned int charger_status_0;
+ int ret;
+
+ if (!IS_REACHABLE(CONFIG_EXTCON)) {
+ dev_dbg(bq->dev, "Extcon support is disabled\n");
+ return 0;
+ }
+
+ bq->edev = devm_extcon_dev_allocate(bq->dev, bq256xx_usb_extcon_cable);
+ if (IS_ERR(bq->edev)) {
+ dev_err(bq->dev, "Failed to allocate extcon device\n");
+ return PTR_ERR(bq->edev);
+ }
+
+ ret = devm_extcon_dev_register(bq->dev, bq->edev);
+ if (ret < 0) {
+ dev_err(bq->dev, "Failed to register extcon device\n");
+ return ret;
+ }
+
+ ret = regmap_read(bq->regmap, BQ256XX_CHARGER_STATUS_0,
+ &charger_status_0);
+ if (ret) {
+ dev_err(bq->dev, "Failed to read charger status\n");
+ return ret;
+ }
+
+ bq256xx_extcon_update(bq, !!(charger_status_0 & BQ256XX_PG_STAT_MASK));
+
+ return 0;
}
static irqreturn_t bq256xx_irq_handler_thread(int irq, void *private)
{
struct bq256xx_device *bq = private;
struct bq256xx_state state;
+ enum bq256xx_state_change changed;
int ret;
ret = bq256xx_get_state(bq, &state);
if (ret < 0)
goto irq_out;
- if (!bq256xx_state_changed(bq, &state))
+ changed = bq256xx_state_changed(bq, &state);
+ switch (changed) {
+ case CHANGED_NONE:
goto irq_out;
- mutex_lock(&bq->lock);
- bq->state = state;
- mutex_unlock(&bq->lock);
+ case CHANGED_VBUS:
+ bq256xx_extcon_update(bq, state.online);
+ fallthrough;
+ case CHANGED_OTHER:
+ mutex_lock(&bq->lock);
+ bq->state = state;
+ mutex_unlock(&bq->lock);
- power_supply_changed(bq->charger);
+ power_supply_changed(bq->charger);
+ }
irq_out:
return IRQ_HANDLED;
@@ -1743,6 +1817,12 @@ static int bq256xx_probe(struct i2c_client *client)
if (!IS_ERR_OR_NULL(bq->usb3_phy))
usb_register_notifier(bq->usb3_phy, &bq->usb_nb);
+ ret = bq256xx_extcon_init(bq);
+ if (ret) {
+ dev_err(dev, "Failed to register extcon device\n");
+ return ret;
+ }
+
if (client->irq) {
ret = devm_request_threaded_irq(dev, client->irq, NULL,
bq256xx_irq_handler_thread,
diff --git a/drivers/power/supply/bq257xx_charger.c b/drivers/power/supply/bq257xx_charger.c
index b735bb02a8e0..2177cee29a4f 100644
--- a/drivers/power/supply/bq257xx_charger.c
+++ b/drivers/power/supply/bq257xx_charger.c
@@ -73,6 +73,7 @@ struct bq257xx_chg {
const struct bq257xx_chip_info *chip;
struct bq257xx_device *bq;
struct power_supply *charger;
+ struct device *dev;
bool online;
bool charging;
bool fast_charge;
@@ -1055,7 +1056,7 @@ static void bq257xx_external_power_changed(struct power_supply *psy)
pdata->chip->bq257xx_get_state(pdata);
- pdata->supplied = power_supply_am_i_supplied(pdata->charger);
+ pdata->supplied = power_supply_am_i_supplied(psy);
if (pdata->supplied < 0)
return;
@@ -1112,6 +1113,54 @@ static irqreturn_t bq257xx_irq_handler_thread(int irq, void *private)
return IRQ_HANDLED;
}
+/**
+ * bq257xx_charger_init() - Initialization for charger power supply device
+ * @psy: power supply device
+ *
+ * Set device parameters during registration, before the power supply becomes
+ * accessible to the broader system.
+ *
+ * Return: Returns 0 on success or error code on failure.
+ */
+static int bq257xx_charger_init(struct power_supply *psy)
+{
+ struct bq257xx_chg *pdata = power_supply_get_drvdata(psy);
+ struct power_supply_battery_info *bat_info;
+ struct device *dev = pdata->dev;
+ int ret;
+
+ ret = power_supply_get_battery_info(psy, &bat_info);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Unable to get battery info\n");
+
+ if ((bat_info->voltage_min_design_uv <= 0) ||
+ (bat_info->constant_charge_voltage_max_uv <= 0) ||
+ (bat_info->constant_charge_current_max_ua <= 0)) {
+ power_supply_put_battery_info(psy, bat_info);
+ return dev_err_probe(dev, -EINVAL,
+ "Required bat info missing or invalid\n");
+ }
+
+ pdata->vsys_min = bat_info->voltage_min_design_uv;
+ pdata->vbat_max = bat_info->constant_charge_voltage_max_uv;
+ pdata->ichg_max = bat_info->constant_charge_current_max_ua;
+
+ power_supply_put_battery_info(psy, bat_info);
+
+ ret = device_property_read_u32(dev,
+ "input-current-limit-microamp",
+ &pdata->iindpm_max);
+ if (ret)
+ pdata->iindpm_max = pdata->chip->default_iindpm_uA;
+
+ ret = pdata->chip->bq257xx_hw_init(pdata);
+ if (ret)
+ return dev_err_probe(dev, ret, "Cannot initialize the charger\n");
+
+ return 0;
+}
+
static const struct power_supply_desc bq257xx_power_supply_desc = {
.name = "bq257xx-charger",
.type = POWER_SUPPLY_TYPE_USB,
@@ -1126,6 +1175,7 @@ static const struct power_supply_desc bq257xx_power_supply_desc = {
.set_property = bq257xx_set_charger_property,
.property_is_writeable = bq257xx_property_is_writeable,
.external_power_changed = bq257xx_external_power_changed,
+ .init = bq257xx_charger_init,
};
static const struct bq257xx_chip_info bq25703_chip_info = {
@@ -1161,57 +1211,11 @@ static const struct bq257xx_chip_info bq25792_chip_info = {
};
/**
- * bq257xx_parse_dt() - Parse the device tree for required properties
- * @pdata: driver platform data
- * @psy_cfg: power supply config data
- * @dev: device struct
- *
- * Read the device tree to identify the minimum system voltage, the
- * maximum charge current, the maximum charge voltage, and the maximum
- * input current.
- *
- * Return: Returns 0 on success or error code on error.
- */
-static int bq257xx_parse_dt(struct bq257xx_chg *pdata,
- struct power_supply_config *psy_cfg, struct device *dev)
-{
- struct power_supply_battery_info *bat_info;
- int ret;
-
- ret = power_supply_get_battery_info(pdata->charger,
- &bat_info);
- if (ret)
- return dev_err_probe(dev, ret,
- "Unable to get battery info\n");
-
- if ((bat_info->voltage_min_design_uv <= 0) ||
- (bat_info->constant_charge_voltage_max_uv <= 0) ||
- (bat_info->constant_charge_current_max_ua <= 0))
- return dev_err_probe(dev, -EINVAL,
- "Required bat info missing or invalid\n");
-
- pdata->vsys_min = bat_info->voltage_min_design_uv;
- pdata->vbat_max = bat_info->constant_charge_voltage_max_uv;
- pdata->ichg_max = bat_info->constant_charge_current_max_ua;
-
- power_supply_put_battery_info(pdata->charger, bat_info);
-
- ret = device_property_read_u32(dev,
- "input-current-limit-microamp",
- &pdata->iindpm_max);
- if (ret)
- pdata->iindpm_max = pdata->chip->default_iindpm_uA;
-
- return 0;
-}
-
-/**
* bq257xx_charger_probe() - Probe routine for charger platform device
* @pdev: platform device
*
* Probe the charger device, allocate driver data structure, select the
* appropriate chip-specific function pointers, register the power supply,
- * parse device tree properties for battery limits, initialize hardware,
* and set up the interrupt handler if available.
*
* Return: Returns 0 on success or error code on failure.
@@ -1222,7 +1226,6 @@ static int bq257xx_charger_probe(struct platform_device *pdev)
struct bq257xx_device *bq = dev_get_drvdata(pdev->dev.parent);
struct bq257xx_chg *pdata;
struct power_supply_config psy_cfg = { };
- int ret;
device_set_of_node_from_dev(dev, pdev->dev.parent);
@@ -1231,6 +1234,7 @@ static int bq257xx_charger_probe(struct platform_device *pdev)
return -ENOMEM;
pdata->bq = bq;
+ pdata->dev = dev;
switch (bq->type) {
case BQ25703A:
@@ -1255,26 +1259,16 @@ static int bq257xx_charger_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(pdata->charger),
"Power supply register charger failed\n");
- ret = bq257xx_parse_dt(pdata, &psy_cfg, dev);
- if (ret)
- return ret;
-
- ret = pdata->chip->bq257xx_hw_init(pdata);
- if (ret)
- return dev_err_probe(dev, ret, "Cannot initialize the charger\n");
-
- platform_set_drvdata(pdev, pdata);
-
if (bq->client->irq) {
- ret = devm_request_threaded_irq(dev, bq->client->irq, NULL,
- bq257xx_irq_handler_thread,
- IRQF_TRIGGER_RISING |
- IRQF_TRIGGER_FALLING |
- IRQF_ONESHOT,
- dev_name(&bq->client->dev), pdata);
+ return devm_request_threaded_irq(dev, bq->client->irq, NULL,
+ bq257xx_irq_handler_thread,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ dev_name(&bq->client->dev), pdata);
}
- return ret;
+ return 0;
}
/**
diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
index 17f5b58609be..09272249b4f7 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/bq27z746
* https://www.ti.com/product/bq28z610
+ * https://www.ti.com/product/bq28z620
* https://www.ti.com/product/bq34z100-g1
* https://www.ti.com/product/bq78z100
*/
@@ -525,6 +526,7 @@ static u8
[BQ27XXX_REG_AP] = 0x22,
BQ27XXX_DM_REG_ROWS,
},
+#define bq28z620_regs bq28z610_regs
bq34z100_regs[BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_CTRL] = 0x00,
[BQ27XXX_REG_TEMP] = 0x0c,
@@ -861,6 +863,8 @@ static enum power_supply_property bq28z610_props[] = {
POWER_SUPPLY_PROP_MANUFACTURER,
};
+#define bq28z620_props bq28z610_props
+
static enum power_supply_property bq34z100_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
@@ -1001,6 +1005,7 @@ static struct bq27xxx_dm_reg bq27621_dm_regs[] = {
#define bq27z561_dm_regs NULL
#define bq27z746_dm_regs NULL
#define bq28z610_dm_regs NULL
+#define bq28z620_dm_regs NULL
#define bq34z100_dm_regs NULL
#define bq78z100_dm_regs NULL
@@ -1060,6 +1065,7 @@ static struct {
[BQ27Z561] = BQ27XXX_DATA(bq27z561, 0 , BQ27Z561_O_BITS),
[BQ27Z746] = BQ27XXX_DATA(bq27z746, 0 , BQ27Z561_O_BITS),
[BQ28Z610] = BQ27XXX_DATA(bq28z610, 0 , BQ27Z561_O_BITS),
+ [BQ28Z620] = BQ27XXX_DATA(bq28z620, 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),
diff --git a/drivers/power/supply/bq27xxx_battery_i2c.c b/drivers/power/supply/bq27xxx_battery_i2c.c
index 2a6b4534cb28..715ce8b6e401 100644
--- a/drivers/power/supply/bq27xxx_battery_i2c.c
+++ b/drivers/power/supply/bq27xxx_battery_i2c.c
@@ -255,6 +255,7 @@ static const struct i2c_device_id bq27xxx_i2c_id_table[] = {
{ .name = "bq27z561", .driver_data = BQ27Z561 },
{ .name = "bq27z746", .driver_data = BQ27Z746 },
{ .name = "bq28z610", .driver_data = BQ28Z610 },
+ { .name = "bq28z620", .driver_data = BQ28Z620 },
{ .name = "bq34z100", .driver_data = BQ34Z100 },
{ .name = "bq78z100", .driver_data = BQ78Z100 },
{ }
@@ -293,6 +294,7 @@ static const struct of_device_id bq27xxx_battery_i2c_of_match_table[] = {
{ .compatible = "ti,bq27z561" },
{ .compatible = "ti,bq27z746" },
{ .compatible = "ti,bq28z610" },
+ { .compatible = "ti,bq28z620" },
{ .compatible = "ti,bq34z100" },
{ .compatible = "ti,bq78z100" },
{},
diff --git a/drivers/power/supply/cpcap-charger.c b/drivers/power/supply/cpcap-charger.c
index 34a57dd5c3cf..d423a8bdc0c2 100644
--- a/drivers/power/supply/cpcap-charger.c
+++ b/drivers/power/supply/cpcap-charger.c
@@ -808,8 +808,8 @@ static void cpcap_charger_init_optional_gpios(struct cpcap_charger_ddata *ddata)
ddata->gpio[i] = devm_gpiod_get_index(ddata->dev, "mode",
i, GPIOD_OUT_HIGH);
if (IS_ERR(ddata->gpio[i])) {
- dev_info(ddata->dev, "no mode change GPIO%i: %li\n",
- i, PTR_ERR(ddata->gpio[i]));
+ dev_info(ddata->dev, "no mode change GPIO%i: %pe\n",
+ i, ddata->gpio[i]);
ddata->gpio[i] = NULL;
}
}
diff --git a/drivers/power/supply/generic-adc-battery.c b/drivers/power/supply/generic-adc-battery.c
index 185f55d2fd70..f11a5c794e7e 100644
--- a/drivers/power/supply/generic-adc-battery.c
+++ b/drivers/power/supply/generic-adc-battery.c
@@ -90,12 +90,17 @@ static int gab_read_channel(struct gab *adc_bat, enum gab_chan_type channel,
int ret;
ret = iio_read_channel_processed(adc_bat->channel[channel], result);
- if (ret < 0)
+ if (ret < 0) {
dev_err(&adc_bat->psy->dev, "read channel error: %d\n", ret);
+ return ret;
+ }
+
+ if (channel == GAB_TEMP)
+ *result /= 100; /* Convert from milli- to deci-degree */
else
*result *= 1000;
- return ret;
+ return 0;
}
static int gab_get_property(struct power_supply *psy,
diff --git a/drivers/power/supply/intel_dc_ti_battery.c b/drivers/power/supply/intel_dc_ti_battery.c
index cb6fa8d88b43..5819df23a543 100644
--- a/drivers/power/supply/intel_dc_ti_battery.c
+++ b/drivers/power/supply/intel_dc_ti_battery.c
@@ -349,7 +349,7 @@ static int dc_ti_battery_probe(struct platform_device *pdev)
chip->vbat_channel = devm_iio_channel_get(dev, "VBAT");
if (IS_ERR(chip->vbat_channel)) {
- dev_dbg(dev, "devm_iio_channel_get() ret %ld\n", PTR_ERR(chip->vbat_channel));
+ dev_dbg(dev, "devm_iio_channel_get() ret %pe\n", chip->vbat_channel);
return dev_err_probe(dev, -EPROBE_DEFER, "Waiting for VBAT IIO channel\n");
}
diff --git a/drivers/power/supply/ltc2941-battery-gauge.c b/drivers/power/supply/ltc2941-battery-gauge.c
index d1be314f36a0..6ebb555f203d 100644
--- a/drivers/power/supply/ltc2941-battery-gauge.c
+++ b/drivers/power/supply/ltc2941-battery-gauge.c
@@ -464,9 +464,11 @@ static int ltc294x_i2c_probe(struct i2c_client *client)
/* r_sense can be negative, when sense+ is connected to the battery
* instead of the sense-. This results in reversed measurements. */
ret = of_property_read_s32(np, "lltc,resistor-sense", &r_sense);
- if (ret < 0)
+ if (ret < 0) {
+ of_node_put(np);
return dev_err_probe(&client->dev, ret,
"Could not find lltc,resistor-sense in devicetree\n");
+ }
info->r_sense = r_sense;
ret = of_property_read_u32(np, "lltc,prescaler-exponent",
@@ -477,6 +479,8 @@ static int ltc294x_i2c_probe(struct i2c_client *client)
prescaler_exp = LTC2941_MAX_PRESCALER_EXP;
}
+ of_node_put(np);
+
if (info->id == LTC2943_ID) {
if (prescaler_exp > LTC2943_MAX_PRESCALER_EXP)
prescaler_exp = LTC2943_MAX_PRESCALER_EXP;
diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c
index 7b0b0c88c443..8138b52e05e1 100644
--- a/drivers/power/supply/max17042_battery.c
+++ b/drivers/power/supply/max17042_battery.c
@@ -16,6 +16,7 @@
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
+#include <linux/math.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/power_supply.h>
@@ -57,10 +58,11 @@
#define MAX17042_CURRENT_LSB 1562500ll /* 1.5625µV/Rsense */
#define MAX17042_CAPACITY_LSB 5000000ll /* 5.0µVH/Rsense */
-#define MAX17042_TIME_LSB 5625 / 1000 /* s */
-#define MAX17042_VOLTAGE_LSB 625 / 8 /* µV */
-#define MAX17042_RESISTANCE_LSB 1 / 4096 /* Ω */
-#define MAX17042_TEMPERATURE_LSB 1 / 256 /* °C */
+
+#define MAX17042_TIME_LSB(val) mult_frac(val, 5625, 1000) /* s */
+#define MAX17042_VOLTAGE_LSB(val) mult_frac(val, 625, 8) /* µV */
+#define MAX17042_RESISTANCE_LSB(val) mult_frac(val, 1, 4096) /* Ω */
+#define MAX17042_TEMPERATURE_LSB(val) mult_frac(val, 1, 256) /* °C */
#define MAX17055_DQACC_DIV 32
#define MAX17055_DPACC_FACTOR 44138
@@ -138,7 +140,7 @@ static int max17042_get_temperature(struct max17042_chip *chip, int *temp)
*temp = sign_extend32(data, 15);
/* The value is converted into deci-centigrade scale */
- *temp = *temp * 10 * MAX17042_TEMPERATURE_LSB;
+ *temp = MAX17042_TEMPERATURE_LSB(*temp * 10);
return 0;
}
@@ -214,7 +216,7 @@ static int max17042_get_battery_health(struct max17042_chip *chip, int *health)
goto health_error;
/* bits [0-3] unused */
- vavg = val * MAX17042_VOLTAGE_LSB;
+ vavg = MAX17042_VOLTAGE_LSB(val);
/* Convert to millivolts */
vavg /= 1000;
@@ -223,7 +225,7 @@ static int max17042_get_battery_health(struct max17042_chip *chip, int *health)
goto health_error;
/* bits [0-3] unused */
- vbatt = val * MAX17042_VOLTAGE_LSB;
+ vbatt = MAX17042_VOLTAGE_LSB(val);
/* Convert to millivolts */
vbatt /= 1000;
@@ -330,21 +332,21 @@ static int max17042_get_property(struct power_supply *psy,
if (ret < 0)
return ret;
- val->intval = data * MAX17042_VOLTAGE_LSB;
+ val->intval = MAX17042_VOLTAGE_LSB(data);
break;
case POWER_SUPPLY_PROP_VOLTAGE_AVG:
ret = regmap_read(map, MAX17042_AvgVCELL, &data);
if (ret < 0)
return ret;
- val->intval = data * MAX17042_VOLTAGE_LSB;
+ val->intval = MAX17042_VOLTAGE_LSB(data);
break;
case POWER_SUPPLY_PROP_VOLTAGE_OCV:
ret = regmap_read(map, MAX17042_OCVInternal, &data);
if (ret < 0)
return ret;
- val->intval = data * MAX17042_VOLTAGE_LSB;
+ val->intval = MAX17042_VOLTAGE_LSB(data);
break;
case POWER_SUPPLY_PROP_CAPACITY:
if (chip->enable_current_sense)
@@ -473,7 +475,7 @@ static int max17042_get_property(struct power_supply *psy,
if (data == U16_MAX)
return -ENODATA;
- val->intval = data * MAX17042_TIME_LSB;
+ val->intval = MAX17042_TIME_LSB(data);
break;
case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
if (chip->chip_type != MAXIM_DEVICE_TYPE_MAX17055 &&
@@ -488,7 +490,7 @@ static int max17042_get_property(struct power_supply *psy,
if (data == U16_MAX)
return -ENODATA;
- val->intval = data * MAX17042_TIME_LSB;
+ val->intval = MAX17042_TIME_LSB(data);
break;
default:
return -EINVAL;
diff --git a/drivers/power/supply/pf1550-charger.c b/drivers/power/supply/pf1550-charger.c
index 2ead1df60e65..e3d4e9817f91 100644
--- a/drivers/power/supply/pf1550-charger.c
+++ b/drivers/power/supply/pf1550-charger.c
@@ -566,6 +566,22 @@ static int pf1550_charger_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, chg);
+ psy_cfg.drv_data = chg;
+
+ chg->charger = devm_power_supply_register(&pdev->dev,
+ &pf1550_charger_desc,
+ &psy_cfg);
+ if (IS_ERR(chg->charger))
+ return dev_err_probe(&pdev->dev, PTR_ERR(chg->charger),
+ "failed: power supply register\n");
+
+ chg->battery = devm_power_supply_register(&pdev->dev,
+ &pf1550_battery_desc,
+ &psy_cfg);
+ if (IS_ERR(chg->battery))
+ return dev_err_probe(&pdev->dev, PTR_ERR(chg->battery),
+ "failed: power supply register\n");
+
ret = devm_delayed_work_autocancel(chg->dev, &chg->vbus_sense_work,
pf1550_chg_vbus_work);
if (ret)
@@ -584,22 +600,6 @@ static int pf1550_charger_probe(struct platform_device *pdev)
return dev_err_probe(chg->dev, ret,
"failed to add battery sense work\n");
- psy_cfg.drv_data = chg;
-
- chg->charger = devm_power_supply_register(&pdev->dev,
- &pf1550_charger_desc,
- &psy_cfg);
- if (IS_ERR(chg->charger))
- return dev_err_probe(&pdev->dev, PTR_ERR(chg->charger),
- "failed: power supply register\n");
-
- chg->battery = devm_power_supply_register(&pdev->dev,
- &pf1550_battery_desc,
- &psy_cfg);
- if (IS_ERR(chg->battery))
- return dev_err_probe(&pdev->dev, PTR_ERR(chg->battery),
- "failed: power supply register\n");
-
for (i = 0; i < PF1550_CHARGER_IRQ_NR; i++) {
irq = platform_get_irq(pdev, i);
if (irq < 0)
diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c
index 47e307709e5e..09fa22e2ffde 100644
--- a/drivers/power/supply/power_supply_core.c
+++ b/drivers/power/supply/power_supply_core.c
@@ -190,7 +190,35 @@ static void power_supply_deferred_register_work(struct work_struct *work)
device_unlock(psy->dev.parent);
}
-#ifdef CONFIG_OF
+static int power_supply_check_supplies_by_name(struct power_supply *psy)
+{
+ struct device *parent = psy->dev.parent;
+ int nval, ret;
+
+ if (!parent)
+ return 0;
+
+ nval = device_property_string_array_count(parent, "supplied-from");
+ if (nval <= 0)
+ return 0;
+
+ psy->supplied_from = devm_kmalloc_array(&psy->dev, nval,
+ sizeof(*psy->supplied_from),
+ GFP_KERNEL);
+ if (!psy->supplied_from)
+ return -ENOMEM;
+
+ ret = device_property_read_string_array(parent, "supplied-from",
+ (const char **)psy->supplied_from,
+ nval);
+ if (ret < 0)
+ return ret;
+
+ psy->num_supplies = nval;
+
+ return 0;
+}
+
static int __power_supply_populate_supplied_from(struct power_supply *epsy,
void *data)
{
@@ -262,22 +290,21 @@ static int power_supply_find_supply_from_fwnode(struct fwnode_handle *supply_nod
static int power_supply_check_supplies(struct power_supply *psy)
{
struct fwnode_handle *np;
- int cnt = 0;
+ int cnt, ret;
/* If there is already a list honor it */
if (psy->supplied_from && psy->num_supplies > 0)
return 0;
- /* No device node found, nothing to do */
- if (!psy->dev.fwnode)
- return 0;
+ for (cnt = 0; psy->dev.fwnode; cnt++) {
+ np = fwnode_find_reference(psy->dev.fwnode, "power-supplies", cnt);
+ if (IS_ERR(np)) {
+ ret = PTR_ERR(np);
+ if (ret != -ENOENT)
+ return ret;
- do {
- int ret;
-
- np = fwnode_find_reference(psy->dev.fwnode, "power-supplies", cnt++);
- if (IS_ERR(np))
break;
+ }
ret = power_supply_find_supply_from_fwnode(np);
fwnode_handle_put(np);
@@ -286,48 +313,21 @@ static int power_supply_check_supplies(struct power_supply *psy)
dev_dbg(&psy->dev, "Failed to find supply!\n");
return ret;
}
- } while (!IS_ERR(np));
+ }
- /* Missing valid "power-supplies" entries */
- if (cnt == 1)
- return 0;
+ /* Fall back to the name-based property if no references were specified. */
+ if (!cnt)
+ return power_supply_check_supplies_by_name(psy);
/* All supplies found, allocate char * array for filling */
psy->supplied_from = devm_kcalloc(&psy->dev,
- cnt - 1, sizeof(*psy->supplied_from),
+ cnt, sizeof(*psy->supplied_from),
GFP_KERNEL);
if (!psy->supplied_from)
return -ENOMEM;
return power_supply_populate_supplied_from(psy);
}
-#else
-static int power_supply_check_supplies(struct power_supply *psy)
-{
- int nval, ret;
-
- if (!psy->dev.parent)
- return 0;
-
- nval = device_property_string_array_count(psy->dev.parent, "supplied-from");
- if (nval <= 0)
- return 0;
-
- psy->supplied_from = devm_kmalloc_array(&psy->dev, nval,
- sizeof(char *), GFP_KERNEL);
- if (!psy->supplied_from)
- return -ENOMEM;
-
- ret = device_property_read_string_array(psy->dev.parent,
- "supplied-from", (const char **)psy->supplied_from, nval);
- if (ret < 0)
- return ret;
-
- psy->num_supplies = nval;
-
- return 0;
-}
-#endif
struct psy_am_i_supplied_data {
struct power_supply *psy;
@@ -907,7 +907,7 @@ int power_supply_get_battery_info(struct power_supply *psy,
u32 *propdata __free(kfree) = kzalloc_objs(*propdata, proplen);
if (!propdata) {
power_supply_put_battery_info(psy, info);
- err = -EINVAL;
+ err = -ENOMEM;
goto out_put_node;
}
err = fwnode_property_read_u32_array(fwnode, propname, propdata, proplen);
@@ -1442,7 +1442,7 @@ EXPORT_SYMBOL_GPL(power_supply_get_property);
int power_supply_get_property_direct(struct power_supply *psy, enum power_supply_property psp,
union power_supply_propval *val)
{
- return __power_supply_get_property(psy, psp, val, false);
+ return __power_supply_get_property(psy, psp, val, false);
}
EXPORT_SYMBOL_GPL(power_supply_get_property_direct);
@@ -1530,6 +1530,19 @@ void power_supply_external_power_changed(struct power_supply *psy)
!psy->desc->external_power_changed)
return;
+ /*
+ * Keep power_supply_unregister() from returning, and thus from letting
+ * the driver's data be freed, while the callback is running. The
+ * ->removing check has to happen under the lock: on its own, just like
+ * the use_cnt check above, it only tells us the supply was still there
+ * when we looked, not that it still is by the time the callback
+ * dereferences its driver data.
+ */
+ guard(rwsem_read)(&psy->epc_sem);
+
+ if (psy->removing)
+ return;
+
psy->desc->external_power_changed(psy);
}
EXPORT_SYMBOL_GPL(power_supply_external_power_changed);
@@ -1595,6 +1608,16 @@ int power_supply_register_extension(struct power_supply *psy, const struct power
sysfs_hwmon_failed:
power_supply_sysfs_remove_extension(psy, ext);
+ list_del(&reg->list_head);
+ kfree(reg);
+ /*
+ * update_sysfs_and_hwmon() may have already torn down hwmon before
+ * failing to recreate it. Recreate without the failed extension.
+ */
+ if (power_supply_add_hwmon_sysfs(psy))
+ dev_warn(&psy->dev, "failed to restore hwmon after extension error\n");
+ return ret;
+
sysfs_add_failed:
list_del(&reg->list_head);
kfree(reg);
@@ -1774,6 +1797,7 @@ __power_supply_register(struct device *parent,
}
spin_lock_init(&psy->changed_lock);
+ init_rwsem(&psy->epc_sem);
init_rwsem(&psy->extensions_sem);
INIT_LIST_HEAD(&psy->extensions);
@@ -1914,7 +1938,17 @@ EXPORT_SYMBOL_GPL(devm_power_supply_register);
void power_supply_unregister(struct power_supply *psy)
{
WARN_ON(atomic_dec_return(&psy->use_cnt));
- psy->removing = true;
+
+ /*
+ * Publish ->removing so that no new ->external_power_changed() call can
+ * start, and wait via semaphore for one that is already running: it may
+ * be a supplier's changed_work, which cancel_work_sync() below does not
+ * cover, and it may still dereference driver data that the caller is
+ * about to free.
+ */
+ scoped_guard(rwsem_write, &psy->epc_sem)
+ psy->removing = true;
+
cancel_work_sync(&psy->changed_work);
cancel_delayed_work_sync(&psy->deferred_register_work);
sysfs_remove_link(&psy->dev.kobj, "powers");
diff --git a/drivers/power/supply/qcom_battmgr.c b/drivers/power/supply/qcom_battmgr.c
index 7716eb9e1aff..a7237ba09be1 100644
--- a/drivers/power/supply/qcom_battmgr.c
+++ b/drivers/power/supply/qcom_battmgr.c
@@ -830,6 +830,7 @@ static const enum power_supply_property x1e80100_bat_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_CYCLE_COUNT,
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
diff --git a/drivers/power/supply/qcom_smbx.c b/drivers/power/supply/qcom_smbx.c
index f5557b8cc92a..3951326f488f 100644
--- a/drivers/power/supply/qcom_smbx.c
+++ b/drivers/power/supply/qcom_smbx.c
@@ -618,7 +618,7 @@ static int smb_get_iio_chan(struct smb_chip *chip, struct iio_channel *chan,
}
if (IS_ERR(chan)) {
- dev_err(chip->dev, "Failed to chan, err = %li", PTR_ERR(chan));
+ dev_err(chip->dev, "Failed to chan, err = %pe", chan);
return PTR_ERR(chan);
}
@@ -1005,7 +1005,7 @@ static int smb_probe(struct platform_device *pdev)
supply_config.drv_data = chip;
supply_config.fwnode = dev_fwnode(&pdev->dev);
- desc = devm_kzalloc(chip->dev, sizeof(smb_psy_desc), GFP_KERNEL);
+ desc = devm_kzalloc(chip->dev, sizeof(*desc), GFP_KERNEL);
if (!desc)
return -ENOMEM;
memcpy(desc, &smb_psy_desc, sizeof(smb_psy_desc));
diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c
index 501c8e069755..6714313bb3a3 100644
--- a/drivers/power/supply/sbs-battery.c
+++ b/drivers/power/supply/sbs-battery.c
@@ -1068,6 +1068,13 @@ static void sbs_alert(struct i2c_client *client, enum i2c_alert_protocol prot,
sbs_supply_changed(i2c_get_clientdata(client));
}
+static void sbs_disable_work(void *data)
+{
+ struct sbs_info *chip = data;
+
+ disable_delayed_work_sync(&chip->work);
+}
+
static void sbs_external_power_changed(struct power_supply *psy)
{
struct sbs_info *chip = power_supply_get_drvdata(psy);
@@ -1208,6 +1215,10 @@ static int sbs_probe(struct i2c_client *client)
return dev_err_probe(&client->dev, PTR_ERR(chip->power_supply),
"Failed to register power supply\n");
+ rc = devm_add_action_or_reset(&client->dev, sbs_disable_work, chip);
+ if (rc)
+ return rc;
+
if (!chip->gpio_detect)
goto out;