From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 091793ED12B for ; Wed, 16 Sep 2026 06:49:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789541381; cv=none; b=oTAPTW9Vnlqor6IM8ysgrQ5VpXT30u0A8eRZE+Hs2sn4FjH6lDhD+nVWL8fnp57A9XeDced5M/6j6dAWh05R/HK97M6yY21VKshFGhcfsXm/n6MqaRsHTLCcl8Zm/WT70uqjVErIRXv1JCYS1E5LformJGseHuN87rp8zT+loCE= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789541381; c=relaxed/simple; bh=HGCokA/t4tCLeXUpZjqsBUWfL4ySwtcSgzefyusLI2w=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=NScTS260XH7DnIdXf+RAEdLACVVQk0I8puRdj99a5I8YDJLuR6XFo7y2iT6zRgMq4mMETxxZBG89xQdCLrLpEvwwBymkwZehRtm8zjq2L+cVy5p2zOhxgxFlDZijDaDeclnxvYC6RbZR3/glwloJevtu3oo305oSNbOHeSwHqyc= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Ntk3CGBW; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Ntk3CGBW" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 416901F00898; Wed, 16 Sep 2026 06:49:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1789541379; bh=k1nHhGSE/X/DFT5XkNsCCWAPXba5qMXipRDj4DZxzb8=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=Ntk3CGBWkySWGcCYYQVWZgq3mhVGKefCRO/Sid//yKgP5q6EKL9+R0W+w1opzF/vL jZNGfBmStIj75hVXKQbvPxvDudHSBRsJAOrFi/0RchHtPg/ylwHLf6eqIVt0fQAz3H zi5NjE4rvULoP+C890gvFMQ8zG9iXN1YzknNTqOJJrDLay/Pj7kL1drnZ4ShLDM58t ICPRkVZODuiDp2IbNufrzCI3m9ol+34RdrS0811jg0KJTOe9hv+8iZyHOLr/h4CgFg UC0/A6HSRsqHF5/g18cvHap+qG0Ik9hiEPVtqy7XEOAjpr3SC4kmJZNpZS4tciPd6n kkBwmTr4zpsIw== From: Linus Walleij Date: Wed, 16 Sep 2026 08:49:34 +0200 Subject: [PATCH 12/15] power: supply: ab8500_charger: Retry AB8505 USB detection Precedence: bulk X-Mailing-List: linux-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20260916-ab8500-charging-v1-12-e986ed321cc6@kernel.org> References: <20260916-ab8500-charging-v1-0-e986ed321cc6@kernel.org> In-Reply-To: <20260916-ab8500-charging-v1-0-e986ed321cc6@kernel.org> To: Sebastian Reichel Cc: linux-pm@vger.kernel.org, Linus Walleij X-Mailer: b4 0.14.3 AB8505 reports UsbLink1Status state 14 when the USB charger port is not OK. The manual says that software can return the port detector to the detection state by setting the self-clearing ChargerCtrl.DropCountReset bit. The Samsung-GT-S7710-Skomer, Samsung-SGH-I407-Kyle, and Samsung-SGH-T599-Codina-TMO product trees are reference points for a workaround that cycles the USB charger and retries detection three times before rejecting VBUS. Only the Kyle tree writes 1 to DropCountReset; the other two write 0 and therefore do not request the documented reset. Add the same bounded recovery for AB8505, preserving the configured USB charger control bits across each cycle. Keep the charger connected while recovering, then report the fault and disconnect it if all three attempts fail. Leave AB8500 behavior unchanged. Fixes: d4337660d069 ("ab8500-charger: Add AB8505_USB_LINK_STATUS") Assisted-by: LLM Signed-off-by: Linus Walleij --- drivers/power/supply/ab8500_charger.c | 114 ++++++++++++++++++++++++++++++++-- 1 file changed, 109 insertions(+), 5 deletions(-) diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c index c7225135af57..8430bc247df7 100644 --- a/drivers/power/supply/ab8500_charger.c +++ b/drivers/power/supply/ab8500_charger.c @@ -99,6 +99,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 @@ -261,6 +263,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 @@ -314,6 +318,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; @@ -473,6 +478,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; @@ -796,10 +804,19 @@ static int ab8505_charger_max_usb_curr(struct ab8500_charger *di, ret = -ENXIO; break; case AB8505_USB_STAT_CHARGER_NOT_OK: - di->flags.vbus_collapse = true; - dev_err(di->dev, "USB Type - VBUS has collapsed\n"); di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_0P05; - ret = -ENXIO; + 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: @@ -2587,6 +2604,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 @@ -2596,7 +2655,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, @@ -2609,19 +2671,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); - if (reg_value & VBUS_CH_NOK) { + ret = ab8505_charger_retry_usb_detection(di); + if (ret < 0) + dev_err(di->dev, "USB charger retry failed: %d\n", ret); + + 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); } -- 2.55.0