BACKPORT: FROMGIT: usb: common: usb-conn-gpio: Prevent bailing out if initial role is none

Currently if we bootup a device without cable connected, then
usb-conn-gpio won't call set_role() because last_role is same
as current role. This happens since last_role gets initialised
to zero during the probe.

To avoid this, add a new flag initial_detection into struct
usb_conn_info, which prevents bailing out during initial
detection.

Cc: <stable@vger.kernel.org> # 5.4
Fixes: 4602f3bff2 ("usb: common: add USB GPIO based connection detection driver")
Signed-off-by: Prashanth K <quic_prashk@quicinc.com>
Tested-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Link: https://lore.kernel.org/r/1690880632-12588-1-git-send-email-quic_prashk@quicinc.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

In-order to comply with KMI, added new struct usb_conn_info_vendor
to avoid modifying existing struct usb_conn_info.

Bug: 287406049
(cherry picked from commit 8e21a620c7e6e00347ade1a6ed4967b359eada5a
https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git/ usb-linus)
Signed-off-by: Prashanth K <quic_prashk@quicinc.com>
Change-Id: Iace5bc9fd6a9a77fb45e4336ce7bba2ab009f9a7
This commit is contained in:
Prashanth K
2023-08-01 14:33:52 +05:30
committed by Todd Kjos
parent 697790241e
commit e77972189d

View File

@@ -44,6 +44,16 @@ struct usb_conn_info {
struct power_supply *charger;
};
/**
* struct usb_conn_info_vendor - contains parameters without modifying the format of usb_conn_info
* @info: contains usb_conn_info structure reference
* initial_detection: bool to check if it's initial detection after probe
*/
struct usb_conn_info_vendor {
struct usb_conn_info info;
bool initial_detection;
};
/*
* "DEVICE" = VBUS and "HOST" = !ID, so we have:
* Both "DEVICE" and "HOST" can't be set as active at the same time
@@ -64,12 +74,15 @@ struct usb_conn_info {
static void usb_conn_detect_cable(struct work_struct *work)
{
struct usb_conn_info *info;
struct usb_conn_info_vendor *v_info;
enum usb_role role;
int id, vbus, ret;
info = container_of(to_delayed_work(work),
struct usb_conn_info, dw_det);
v_info = container_of(info, struct usb_conn_info_vendor, info);
/* check ID and VBUS */
id = info->id_gpiod ?
gpiod_get_value_cansleep(info->id_gpiod) : 1;
@@ -86,11 +99,13 @@ static void usb_conn_detect_cable(struct work_struct *work)
dev_dbg(info->dev, "role %s -> %s, gpios: id %d, vbus %d\n",
usb_role_string(info->last_role), usb_role_string(role), id, vbus);
if (info->last_role == role) {
if (!v_info->initial_detection && info->last_role == role) {
dev_warn(info->dev, "repeated role: %s\n", usb_role_string(role));
return;
}
v_info->initial_detection = false;
if (info->last_role == USB_ROLE_HOST && info->vbus)
regulator_disable(info->vbus);
@@ -175,13 +190,15 @@ static int usb_conn_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct usb_conn_info *info;
struct usb_conn_info_vendor *v_info;
bool need_vbus = true;
int ret = 0;
info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
if (!info)
v_info = devm_kzalloc(dev, sizeof(*v_info), GFP_KERNEL);
if (!v_info)
return -ENOMEM;
info = &v_info->info;
info->dev = dev;
info->id_gpiod = devm_gpiod_get_optional(dev, "id", GPIOD_IN);
if (IS_ERR(info->id_gpiod))
@@ -274,6 +291,7 @@ static int usb_conn_probe(struct platform_device *pdev)
device_set_wakeup_capable(&pdev->dev, true);
/* Perform initial detection */
v_info->initial_detection = true;
usb_conn_queue_dwork(info, 0);
return 0;