diff options
Diffstat (limited to 'drivers/power/supply')
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, ®_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, ®_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, ®_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(®->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(®->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; |
