Skip to content

Commit

Permalink
Merge tag 'usb-6.9-rc2' of git://git.kernel.org/pub/scm/linux/kernel/…
Browse files Browse the repository at this point in the history
…git/gregkh/usb

Pull USB fixes from Greg KH:
 "Here are a bunch of small USB fixes for reported problems and
  regressions for 6.9-rc2. Included in here are:

   - deadlock fixes for long-suffering issues

   - USB phy driver revert for reported problem

   - typec fixes for reported problems

   - duplicate id in dwc3 dropped

   - dwc2 driver fixes

   - udc driver warning fix

   - cdc-wdm race bugfix

   - other tiny USB bugfixes

  All of these have been in linux-next this past week with no reported
  issues"

* tag 'usb-6.9-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (26 commits)
  USB: core: Fix deadlock in port "disable" sysfs attribute
  USB: core: Add hub_get() and hub_put() routines
  usb: typec: ucsi: Check capabilities before cable and identity discovery
  usb: typec: ucsi: Clear UCSI_CCI_RESET_COMPLETE before reset
  usb: typec: ucsi_acpi: Refactor and fix DELL quirk
  usb: typec: ucsi: Ack unsupported commands
  usb: typec: ucsi: Check for notifications after init
  usb: typec: ucsi: Clear EVENT_PENDING under PPM lock
  usb: typec: Return size of buffer if pd_set operation succeeds
  usb: udc: remove warning when queue disabled ep
  usb: dwc3: pci: Drop duplicate ID
  usb: dwc3: Properly set system wakeup
  Revert "usb: phy: generic: Get the vbus supply"
  usb: cdc-wdm: close race between read and workqueue
  usb: dwc2: gadget: LPM flow fix
  usb: dwc2: gadget: Fix exiting from clock gating
  usb: dwc2: host: Fix ISOC flow in DDMA mode
  usb: dwc2: host: Fix remote wakeup from hibernation
  usb: dwc2: host: Fix hibernation flow
  USB: core: Fix deadlock in usb_deauthorize_interface()
  ...
  • Loading branch information
torvalds committed Mar 30, 2024
2 parents 4e6e422 + f4d1960 commit ff789a2
Show file tree
Hide file tree
Showing 27 changed files with 376 additions and 160 deletions.
6 changes: 5 additions & 1 deletion drivers/usb/class/cdc-wdm.c
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,7 @@ static ssize_t wdm_write
static int service_outstanding_interrupt(struct wdm_device *desc)
{
int rv = 0;
int used;

/* submit read urb only if the device is waiting for it */
if (!desc->resp_count || !--desc->resp_count)
Expand All @@ -499,7 +500,10 @@ static int service_outstanding_interrupt(struct wdm_device *desc)
goto out;
}

set_bit(WDM_RESPONDING, &desc->flags);
used = test_and_set_bit(WDM_RESPONDING, &desc->flags);
if (used)
goto out;

spin_unlock_irq(&desc->iuspin);
rv = usb_submit_urb(desc->response, GFP_KERNEL);
spin_lock_irq(&desc->iuspin);
Expand Down
23 changes: 16 additions & 7 deletions drivers/usb/core/hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,6 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
#define HUB_DEBOUNCE_STEP 25
#define HUB_DEBOUNCE_STABLE 100

static void hub_release(struct kref *kref);
static int usb_reset_and_verify_device(struct usb_device *udev);
static int hub_port_disable(struct usb_hub *hub, int port1, int set_state);
static bool hub_port_warm_reset_required(struct usb_hub *hub, int port1,
Expand Down Expand Up @@ -720,14 +719,14 @@ static void kick_hub_wq(struct usb_hub *hub)
*/
intf = to_usb_interface(hub->intfdev);
usb_autopm_get_interface_no_resume(intf);
kref_get(&hub->kref);
hub_get(hub);

if (queue_work(hub_wq, &hub->events))
return;

/* the work has already been scheduled */
usb_autopm_put_interface_async(intf);
kref_put(&hub->kref, hub_release);
hub_put(hub);
}

void usb_kick_hub_wq(struct usb_device *hdev)
Expand Down Expand Up @@ -1095,7 +1094,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
goto init2;
goto init3;
}
kref_get(&hub->kref);
hub_get(hub);

/* The superspeed hub except for root hub has to use Hub Depth
* value as an offset into the route string to locate the bits
Expand Down Expand Up @@ -1343,7 +1342,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
device_unlock(&hdev->dev);
}

kref_put(&hub->kref, hub_release);
hub_put(hub);
}

/* Implement the continuations for the delays above */
Expand Down Expand Up @@ -1759,6 +1758,16 @@ static void hub_release(struct kref *kref)
kfree(hub);
}

void hub_get(struct usb_hub *hub)
{
kref_get(&hub->kref);
}

void hub_put(struct usb_hub *hub)
{
kref_put(&hub->kref, hub_release);
}

static unsigned highspeed_hubs;

static void hub_disconnect(struct usb_interface *intf)
Expand Down Expand Up @@ -1807,7 +1816,7 @@ static void hub_disconnect(struct usb_interface *intf)

onboard_hub_destroy_pdevs(&hub->onboard_hub_devs);

kref_put(&hub->kref, hub_release);
hub_put(hub);
}

static bool hub_descriptor_is_sane(struct usb_host_interface *desc)
Expand Down Expand Up @@ -5934,7 +5943,7 @@ static void hub_event(struct work_struct *work)

/* Balance the stuff in kick_hub_wq() and allow autosuspend */
usb_autopm_put_interface(intf);
kref_put(&hub->kref, hub_release);
hub_put(hub);

kcov_remote_stop();
}
Expand Down
2 changes: 2 additions & 0 deletions drivers/usb/core/hub.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ extern void usb_hub_remove_port_device(struct usb_hub *hub,
extern int usb_hub_set_port_power(struct usb_device *hdev, struct usb_hub *hub,
int port1, bool set);
extern struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev);
extern void hub_get(struct usb_hub *hub);
extern void hub_put(struct usb_hub *hub);
extern int hub_port_debounce(struct usb_hub *hub, int port1,
bool must_be_connected);
extern int usb_clear_port_feature(struct usb_device *hdev,
Expand Down
38 changes: 34 additions & 4 deletions drivers/usb/core/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,22 @@ static ssize_t disable_show(struct device *dev,
u16 portstatus, unused;
bool disabled;
int rc;
struct kernfs_node *kn;

hub_get(hub);
rc = usb_autopm_get_interface(intf);
if (rc < 0)
return rc;
goto out_hub_get;

/*
* Prevent deadlock if another process is concurrently
* trying to unregister hdev.
*/
kn = sysfs_break_active_protection(&dev->kobj, &attr->attr);
if (!kn) {
rc = -ENODEV;
goto out_autopm;
}
usb_lock_device(hdev);
if (hub->disconnected) {
rc = -ENODEV;
Expand All @@ -70,9 +81,13 @@ static ssize_t disable_show(struct device *dev,
usb_hub_port_status(hub, port1, &portstatus, &unused);
disabled = !usb_port_is_power_on(hub, portstatus);

out_hdev_lock:
out_hdev_lock:
usb_unlock_device(hdev);
sysfs_unbreak_active_protection(kn);
out_autopm:
usb_autopm_put_interface(intf);
out_hub_get:
hub_put(hub);

if (rc)
return rc;
Expand All @@ -90,15 +105,26 @@ static ssize_t disable_store(struct device *dev, struct device_attribute *attr,
int port1 = port_dev->portnum;
bool disabled;
int rc;
struct kernfs_node *kn;

rc = kstrtobool(buf, &disabled);
if (rc)
return rc;

hub_get(hub);
rc = usb_autopm_get_interface(intf);
if (rc < 0)
return rc;
goto out_hub_get;

/*
* Prevent deadlock if another process is concurrently
* trying to unregister hdev.
*/
kn = sysfs_break_active_protection(&dev->kobj, &attr->attr);
if (!kn) {
rc = -ENODEV;
goto out_autopm;
}
usb_lock_device(hdev);
if (hub->disconnected) {
rc = -ENODEV;
Expand All @@ -119,9 +145,13 @@ static ssize_t disable_store(struct device *dev, struct device_attribute *attr,
if (!rc)
rc = count;

out_hdev_lock:
out_hdev_lock:
usb_unlock_device(hdev);
sysfs_unbreak_active_protection(kn);
out_autopm:
usb_autopm_put_interface(intf);
out_hub_get:
hub_put(hub);

return rc;
}
Expand Down
16 changes: 13 additions & 3 deletions drivers/usb/core/sysfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -1217,14 +1217,24 @@ static ssize_t interface_authorized_store(struct device *dev,
{
struct usb_interface *intf = to_usb_interface(dev);
bool val;
struct kernfs_node *kn;

if (kstrtobool(buf, &val) != 0)
return -EINVAL;

if (val)
if (val) {
usb_authorize_interface(intf);
else
usb_deauthorize_interface(intf);
} else {
/*
* Prevent deadlock if another process is concurrently
* trying to unregister intf.
*/
kn = sysfs_break_active_protection(&dev->kobj, &attr->attr);
if (kn) {
usb_deauthorize_interface(intf);
sysfs_unbreak_active_protection(kn);
}
}

return count;
}
Expand Down
14 changes: 14 additions & 0 deletions drivers/usb/dwc2/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -729,17 +729,29 @@ struct dwc2_dregs_backup {
* struct dwc2_hregs_backup - Holds host registers state before
* entering partial power down
* @hcfg: Backup of HCFG register
* @hflbaddr: Backup of HFLBADDR register
* @haintmsk: Backup of HAINTMSK register
* @hcchar: Backup of HCCHAR register
* @hcsplt: Backup of HCSPLT register
* @hcintmsk: Backup of HCINTMSK register
* @hctsiz: Backup of HCTSIZ register
* @hdma: Backup of HCDMA register
* @hcdmab: Backup of HCDMAB register
* @hprt0: Backup of HPTR0 register
* @hfir: Backup of HFIR register
* @hptxfsiz: Backup of HPTXFSIZ register
* @valid: True if registers values backuped.
*/
struct dwc2_hregs_backup {
u32 hcfg;
u32 hflbaddr;
u32 haintmsk;
u32 hcchar[MAX_EPS_CHANNELS];
u32 hcsplt[MAX_EPS_CHANNELS];
u32 hcintmsk[MAX_EPS_CHANNELS];
u32 hctsiz[MAX_EPS_CHANNELS];
u32 hcidma[MAX_EPS_CHANNELS];
u32 hcidmab[MAX_EPS_CHANNELS];
u32 hprt0;
u32 hfir;
u32 hptxfsiz;
Expand Down Expand Up @@ -1086,6 +1098,7 @@ struct dwc2_hsotg {
bool needs_byte_swap;

/* DWC OTG HW Release versions */
#define DWC2_CORE_REV_4_30a 0x4f54430a
#define DWC2_CORE_REV_2_71a 0x4f54271a
#define DWC2_CORE_REV_2_72a 0x4f54272a
#define DWC2_CORE_REV_2_80a 0x4f54280a
Expand Down Expand Up @@ -1323,6 +1336,7 @@ int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg);
int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg);

void dwc2_enable_acg(struct dwc2_hsotg *hsotg);
void dwc2_wakeup_from_lpm_l1(struct dwc2_hsotg *hsotg, bool remotewakeup);

/* This function should be called on every hardware interrupt. */
irqreturn_t dwc2_handle_common_intr(int irq, void *dev);
Expand Down
72 changes: 48 additions & 24 deletions drivers/usb/dwc2/core_intr.c
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,8 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)

/* Exit gadget mode clock gating. */
if (hsotg->params.power_down ==
DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended)
DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended &&
!hsotg->params.no_clock_gating)
dwc2_gadget_exit_clock_gating(hsotg, 0);
}

Expand All @@ -322,10 +323,11 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
* @hsotg: Programming view of DWC_otg controller
*
*/
static void dwc2_wakeup_from_lpm_l1(struct dwc2_hsotg *hsotg)
void dwc2_wakeup_from_lpm_l1(struct dwc2_hsotg *hsotg, bool remotewakeup)
{
u32 glpmcfg;
u32 i = 0;
u32 pcgctl;
u32 dctl;

if (hsotg->lx_state != DWC2_L1) {
dev_err(hsotg->dev, "Core isn't in DWC2_L1 state\n");
Expand All @@ -334,37 +336,57 @@ static void dwc2_wakeup_from_lpm_l1(struct dwc2_hsotg *hsotg)

glpmcfg = dwc2_readl(hsotg, GLPMCFG);
if (dwc2_is_device_mode(hsotg)) {
dev_dbg(hsotg->dev, "Exit from L1 state\n");
dev_dbg(hsotg->dev, "Exit from L1 state, remotewakeup=%d\n", remotewakeup);
glpmcfg &= ~GLPMCFG_ENBLSLPM;
glpmcfg &= ~GLPMCFG_HIRD_THRES_EN;
glpmcfg &= ~GLPMCFG_HIRD_THRES_MASK;
dwc2_writel(hsotg, glpmcfg, GLPMCFG);

do {
glpmcfg = dwc2_readl(hsotg, GLPMCFG);
pcgctl = dwc2_readl(hsotg, PCGCTL);
pcgctl &= ~PCGCTL_ENBL_SLEEP_GATING;
dwc2_writel(hsotg, pcgctl, PCGCTL);

if (!(glpmcfg & (GLPMCFG_COREL1RES_MASK |
GLPMCFG_L1RESUMEOK | GLPMCFG_SLPSTS)))
break;
glpmcfg = dwc2_readl(hsotg, GLPMCFG);
if (glpmcfg & GLPMCFG_ENBESL) {
glpmcfg |= GLPMCFG_RSTRSLPSTS;
dwc2_writel(hsotg, glpmcfg, GLPMCFG);
}

if (remotewakeup) {
if (dwc2_hsotg_wait_bit_set(hsotg, GLPMCFG, GLPMCFG_L1RESUMEOK, 1000)) {
dev_warn(hsotg->dev, "%s: timeout GLPMCFG_L1RESUMEOK\n", __func__);
goto fail;
return;
}

dctl = dwc2_readl(hsotg, DCTL);
dctl |= DCTL_RMTWKUPSIG;
dwc2_writel(hsotg, dctl, DCTL);

udelay(1);
} while (++i < 200);
if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS, GINTSTS_WKUPINT, 1000)) {
dev_warn(hsotg->dev, "%s: timeout GINTSTS_WKUPINT\n", __func__);
goto fail;
return;
}
}

if (i == 200) {
dev_err(hsotg->dev, "Failed to exit L1 sleep state in 200us.\n");
glpmcfg = dwc2_readl(hsotg, GLPMCFG);
if (glpmcfg & GLPMCFG_COREL1RES_MASK || glpmcfg & GLPMCFG_SLPSTS ||
glpmcfg & GLPMCFG_L1RESUMEOK) {
goto fail;
return;
}
dwc2_gadget_init_lpm(hsotg);

/* Inform gadget to exit from L1 */
call_gadget(hsotg, resume);
/* Change to L0 state */
hsotg->lx_state = DWC2_L0;
hsotg->bus_suspended = false;
fail: dwc2_gadget_init_lpm(hsotg);
} else {
/* TODO */
dev_err(hsotg->dev, "Host side LPM is not supported.\n");
return;
}

/* Change to L0 state */
hsotg->lx_state = DWC2_L0;

/* Inform gadget to exit from L1 */
call_gadget(hsotg, resume);
}

/*
Expand All @@ -385,7 +407,7 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state);

if (hsotg->lx_state == DWC2_L1) {
dwc2_wakeup_from_lpm_l1(hsotg);
dwc2_wakeup_from_lpm_l1(hsotg, false);
return;
}

Expand All @@ -408,7 +430,8 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)

/* Exit gadget mode clock gating. */
if (hsotg->params.power_down ==
DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended)
DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended &&
!hsotg->params.no_clock_gating)
dwc2_gadget_exit_clock_gating(hsotg, 0);
} else {
/* Change to L0 state */
Expand All @@ -425,7 +448,8 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
}

if (hsotg->params.power_down ==
DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended)
DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended &&
!hsotg->params.no_clock_gating)
dwc2_host_exit_clock_gating(hsotg, 1);

/*
Expand Down
Loading

0 comments on commit ff789a2

Please sign in to comment.