mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-01 10:45:49 +00:00
USB Fixes for 6.9-rc2
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. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZggDDg8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ymuXQCg0/LF/RSoCer/7dczP7zglY+Mw+sAni6ft9jx gzxF9jiqPAjjePT7YFgE =AB/K -----END PGP SIGNATURE----- Merge tag 'usb-6.9-rc2' of git://git.kernel.org/pub/scm/linux/kernel/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() ...
This commit is contained in:
commit
ff789a26cc
@ -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)
|
||||
@ -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);
|
||||
|
@ -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,
|
||||
@ -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)
|
||||
@ -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
|
||||
@ -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 */
|
||||
@ -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)
|
||||
@ -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)
|
||||
@ -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();
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -729,8 +729,14 @@ 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
|
||||
@ -738,8 +744,14 @@ struct dwc2_dregs_backup {
|
||||
*/
|
||||
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;
|
||||
@ -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
|
||||
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
@ -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");
|
||||
@ -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);
|
||||
}
|
||||
|
||||
udelay(1);
|
||||
} while (++i < 200);
|
||||
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;
|
||||
}
|
||||
|
||||
if (i == 200) {
|
||||
dev_err(hsotg->dev, "Failed to exit L1 sleep state in 200us.\n");
|
||||
dctl = dwc2_readl(hsotg, DCTL);
|
||||
dctl |= DCTL_RMTWKUPSIG;
|
||||
dwc2_writel(hsotg, dctl, DCTL);
|
||||
|
||||
if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS, GINTSTS_WKUPINT, 1000)) {
|
||||
dev_warn(hsotg->dev, "%s: timeout GINTSTS_WKUPINT\n", __func__);
|
||||
goto fail;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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 */
|
||||
@ -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);
|
||||
|
||||
/*
|
||||
|
@ -1415,6 +1415,10 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
|
||||
ep->name, req, req->length, req->buf, req->no_interrupt,
|
||||
req->zero, req->short_not_ok);
|
||||
|
||||
if (hs->lx_state == DWC2_L1) {
|
||||
dwc2_wakeup_from_lpm_l1(hs, true);
|
||||
}
|
||||
|
||||
/* Prevent new request submission when controller is suspended */
|
||||
if (hs->lx_state != DWC2_L0) {
|
||||
dev_dbg(hs->dev, "%s: submit request only in active state\n",
|
||||
@ -3727,6 +3731,12 @@ static irqreturn_t dwc2_hsotg_irq(int irq, void *pw)
|
||||
if (hsotg->in_ppd && hsotg->lx_state == DWC2_L2)
|
||||
dwc2_exit_partial_power_down(hsotg, 0, true);
|
||||
|
||||
/* Exit gadget mode clock gating. */
|
||||
if (hsotg->params.power_down ==
|
||||
DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended &&
|
||||
!hsotg->params.no_clock_gating)
|
||||
dwc2_gadget_exit_clock_gating(hsotg, 0);
|
||||
|
||||
hsotg->lx_state = DWC2_L0;
|
||||
}
|
||||
|
||||
|
@ -2701,8 +2701,11 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions(
|
||||
hsotg->available_host_channels--;
|
||||
}
|
||||
qh = list_entry(qh_ptr, struct dwc2_qh, qh_list_entry);
|
||||
if (dwc2_assign_and_init_hc(hsotg, qh))
|
||||
if (dwc2_assign_and_init_hc(hsotg, qh)) {
|
||||
if (hsotg->params.uframe_sched)
|
||||
hsotg->available_host_channels++;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Move the QH from the periodic ready schedule to the
|
||||
@ -2735,8 +2738,11 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions(
|
||||
hsotg->available_host_channels--;
|
||||
}
|
||||
|
||||
if (dwc2_assign_and_init_hc(hsotg, qh))
|
||||
if (dwc2_assign_and_init_hc(hsotg, qh)) {
|
||||
if (hsotg->params.uframe_sched)
|
||||
hsotg->available_host_channels++;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Move the QH from the non-periodic inactive schedule to the
|
||||
@ -4143,6 +4149,8 @@ void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
|
||||
urb->actual_length);
|
||||
|
||||
if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
|
||||
if (!hsotg->params.dma_desc_enable)
|
||||
urb->start_frame = qtd->qh->start_active_frame;
|
||||
urb->error_count = dwc2_hcd_urb_get_error_count(qtd->urb);
|
||||
for (i = 0; i < urb->number_of_packets; ++i) {
|
||||
urb->iso_frame_desc[i].actual_length =
|
||||
@ -4649,7 +4657,7 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
|
||||
}
|
||||
|
||||
if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_NONE &&
|
||||
hsotg->bus_suspended) {
|
||||
hsotg->bus_suspended && !hsotg->params.no_clock_gating) {
|
||||
if (dwc2_is_device_mode(hsotg))
|
||||
dwc2_gadget_exit_clock_gating(hsotg, 0);
|
||||
else
|
||||
@ -5406,9 +5414,16 @@ int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg)
|
||||
/* Backup Host regs */
|
||||
hr = &hsotg->hr_backup;
|
||||
hr->hcfg = dwc2_readl(hsotg, HCFG);
|
||||
hr->hflbaddr = dwc2_readl(hsotg, HFLBADDR);
|
||||
hr->haintmsk = dwc2_readl(hsotg, HAINTMSK);
|
||||
for (i = 0; i < hsotg->params.host_channels; ++i)
|
||||
for (i = 0; i < hsotg->params.host_channels; ++i) {
|
||||
hr->hcchar[i] = dwc2_readl(hsotg, HCCHAR(i));
|
||||
hr->hcsplt[i] = dwc2_readl(hsotg, HCSPLT(i));
|
||||
hr->hcintmsk[i] = dwc2_readl(hsotg, HCINTMSK(i));
|
||||
hr->hctsiz[i] = dwc2_readl(hsotg, HCTSIZ(i));
|
||||
hr->hcidma[i] = dwc2_readl(hsotg, HCDMA(i));
|
||||
hr->hcidmab[i] = dwc2_readl(hsotg, HCDMAB(i));
|
||||
}
|
||||
|
||||
hr->hprt0 = dwc2_read_hprt0(hsotg);
|
||||
hr->hfir = dwc2_readl(hsotg, HFIR);
|
||||
@ -5442,10 +5457,17 @@ int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
|
||||
hr->valid = false;
|
||||
|
||||
dwc2_writel(hsotg, hr->hcfg, HCFG);
|
||||
dwc2_writel(hsotg, hr->hflbaddr, HFLBADDR);
|
||||
dwc2_writel(hsotg, hr->haintmsk, HAINTMSK);
|
||||
|
||||
for (i = 0; i < hsotg->params.host_channels; ++i)
|
||||
for (i = 0; i < hsotg->params.host_channels; ++i) {
|
||||
dwc2_writel(hsotg, hr->hcchar[i], HCCHAR(i));
|
||||
dwc2_writel(hsotg, hr->hcsplt[i], HCSPLT(i));
|
||||
dwc2_writel(hsotg, hr->hcintmsk[i], HCINTMSK(i));
|
||||
dwc2_writel(hsotg, hr->hctsiz[i], HCTSIZ(i));
|
||||
dwc2_writel(hsotg, hr->hcidma[i], HCDMA(i));
|
||||
dwc2_writel(hsotg, hr->hcidmab[i], HCDMAB(i));
|
||||
}
|
||||
|
||||
dwc2_writel(hsotg, hr->hprt0, HPRT0);
|
||||
dwc2_writel(hsotg, hr->hfir, HFIR);
|
||||
@ -5610,10 +5632,12 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup,
|
||||
dwc2_writel(hsotg, hr->hcfg, HCFG);
|
||||
|
||||
/* De-assert Wakeup Logic */
|
||||
gpwrdn = dwc2_readl(hsotg, GPWRDN);
|
||||
gpwrdn &= ~GPWRDN_PMUACTV;
|
||||
dwc2_writel(hsotg, gpwrdn, GPWRDN);
|
||||
udelay(10);
|
||||
if (!(rem_wakeup && hsotg->hw_params.snpsid >= DWC2_CORE_REV_4_30a)) {
|
||||
gpwrdn = dwc2_readl(hsotg, GPWRDN);
|
||||
gpwrdn &= ~GPWRDN_PMUACTV;
|
||||
dwc2_writel(hsotg, gpwrdn, GPWRDN);
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
hprt0 = hr->hprt0;
|
||||
hprt0 |= HPRT0_PWR;
|
||||
@ -5638,6 +5662,13 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup,
|
||||
hprt0 |= HPRT0_RES;
|
||||
dwc2_writel(hsotg, hprt0, HPRT0);
|
||||
|
||||
/* De-assert Wakeup Logic */
|
||||
if ((rem_wakeup && hsotg->hw_params.snpsid >= DWC2_CORE_REV_4_30a)) {
|
||||
gpwrdn = dwc2_readl(hsotg, GPWRDN);
|
||||
gpwrdn &= ~GPWRDN_PMUACTV;
|
||||
dwc2_writel(hsotg, gpwrdn, GPWRDN);
|
||||
udelay(10);
|
||||
}
|
||||
/* Wait for Resume time and then program HPRT again */
|
||||
mdelay(100);
|
||||
hprt0 &= ~HPRT0_RES;
|
||||
|
@ -559,7 +559,7 @@ static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
|
||||
idx = qh->td_last;
|
||||
inc = qh->host_interval;
|
||||
hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
|
||||
cur_idx = dwc2_frame_list_idx(hsotg->frame_number);
|
||||
cur_idx = idx;
|
||||
next_idx = dwc2_desclist_idx_inc(qh->td_last, inc, qh->dev_speed);
|
||||
|
||||
/*
|
||||
@ -866,6 +866,8 @@ static int dwc2_cmpl_host_isoc_dma_desc(struct dwc2_hsotg *hsotg,
|
||||
{
|
||||
struct dwc2_dma_desc *dma_desc;
|
||||
struct dwc2_hcd_iso_packet_desc *frame_desc;
|
||||
u16 frame_desc_idx;
|
||||
struct urb *usb_urb = qtd->urb->priv;
|
||||
u16 remain = 0;
|
||||
int rc = 0;
|
||||
|
||||
@ -878,8 +880,11 @@ static int dwc2_cmpl_host_isoc_dma_desc(struct dwc2_hsotg *hsotg,
|
||||
DMA_FROM_DEVICE);
|
||||
|
||||
dma_desc = &qh->desc_list[idx];
|
||||
frame_desc_idx = (idx - qtd->isoc_td_first) & (usb_urb->number_of_packets - 1);
|
||||
|
||||
frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index_last];
|
||||
frame_desc = &qtd->urb->iso_descs[frame_desc_idx];
|
||||
if (idx == qtd->isoc_td_first)
|
||||
usb_urb->start_frame = dwc2_hcd_get_frame_number(hsotg);
|
||||
dma_desc->buf = (u32)(qtd->urb->dma + frame_desc->offset);
|
||||
if (chan->ep_is_in)
|
||||
remain = (dma_desc->status & HOST_DMA_ISOC_NBYTES_MASK) >>
|
||||
@ -900,7 +905,7 @@ static int dwc2_cmpl_host_isoc_dma_desc(struct dwc2_hsotg *hsotg,
|
||||
frame_desc->status = 0;
|
||||
}
|
||||
|
||||
if (++qtd->isoc_frame_index == qtd->urb->packet_count) {
|
||||
if (++qtd->isoc_frame_index == usb_urb->number_of_packets) {
|
||||
/*
|
||||
* urb->status is not used for isoc transfers here. The
|
||||
* individual frame_desc status are used instead.
|
||||
@ -1005,11 +1010,11 @@ static void dwc2_complete_isoc_xfer_ddma(struct dwc2_hsotg *hsotg,
|
||||
return;
|
||||
idx = dwc2_desclist_idx_inc(idx, qh->host_interval,
|
||||
chan->speed);
|
||||
if (!rc)
|
||||
if (rc == 0)
|
||||
continue;
|
||||
|
||||
if (rc == DWC2_CMPL_DONE)
|
||||
break;
|
||||
if (rc == DWC2_CMPL_DONE || rc == DWC2_CMPL_STOP)
|
||||
goto stop_scan;
|
||||
|
||||
/* rc == DWC2_CMPL_STOP */
|
||||
|
||||
|
@ -698,7 +698,7 @@
|
||||
#define TXSTS_QTOP_TOKEN_MASK (0x3 << 25)
|
||||
#define TXSTS_QTOP_TOKEN_SHIFT 25
|
||||
#define TXSTS_QTOP_TERMINATE BIT(24)
|
||||
#define TXSTS_QSPCAVAIL_MASK (0xff << 16)
|
||||
#define TXSTS_QSPCAVAIL_MASK (0x7f << 16)
|
||||
#define TXSTS_QSPCAVAIL_SHIFT 16
|
||||
#define TXSTS_FSPCAVAIL_MASK (0xffff << 0)
|
||||
#define TXSTS_FSPCAVAIL_SHIFT 0
|
||||
|
@ -331,7 +331,7 @@ static void dwc2_driver_remove(struct platform_device *dev)
|
||||
|
||||
/* Exit clock gating when driver is removed. */
|
||||
if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_NONE &&
|
||||
hsotg->bus_suspended) {
|
||||
hsotg->bus_suspended && !hsotg->params.no_clock_gating) {
|
||||
if (dwc2_is_device_mode(hsotg))
|
||||
dwc2_gadget_exit_clock_gating(hsotg, 0);
|
||||
else
|
||||
|
@ -1519,6 +1519,8 @@ static void dwc3_get_properties(struct dwc3 *dwc)
|
||||
else
|
||||
dwc->sysdev = dwc->dev;
|
||||
|
||||
dwc->sys_wakeup = device_may_wakeup(dwc->sysdev);
|
||||
|
||||
ret = device_property_read_string(dev, "usb-psy-name", &usb_psy_name);
|
||||
if (ret >= 0) {
|
||||
dwc->usb_psy = power_supply_get_by_name(usb_psy_name);
|
||||
|
@ -1133,6 +1133,7 @@ struct dwc3_scratchpad_array {
|
||||
* 3 - Reserved
|
||||
* @dis_metastability_quirk: set to disable metastability quirk.
|
||||
* @dis_split_quirk: set to disable split boundary.
|
||||
* @sys_wakeup: set if the device may do system wakeup.
|
||||
* @wakeup_configured: set if the device is configured for remote wakeup.
|
||||
* @suspended: set to track suspend event due to U3/L2.
|
||||
* @imod_interval: set the interrupt moderation interval in 250ns
|
||||
@ -1357,6 +1358,7 @@ struct dwc3 {
|
||||
|
||||
unsigned dis_split_quirk:1;
|
||||
unsigned async_callbacks:1;
|
||||
unsigned sys_wakeup:1;
|
||||
unsigned wakeup_configured:1;
|
||||
unsigned suspended:1;
|
||||
|
||||
|
@ -51,7 +51,6 @@
|
||||
#define PCI_DEVICE_ID_INTEL_MTLP 0x7ec1
|
||||
#define PCI_DEVICE_ID_INTEL_MTLS 0x7f6f
|
||||
#define PCI_DEVICE_ID_INTEL_MTL 0x7e7e
|
||||
#define PCI_DEVICE_ID_INTEL_ARLH 0x7ec1
|
||||
#define PCI_DEVICE_ID_INTEL_ARLH_PCH 0x777e
|
||||
#define PCI_DEVICE_ID_INTEL_TGL 0x9a15
|
||||
#define PCI_DEVICE_ID_AMD_MR 0x163a
|
||||
@ -423,7 +422,6 @@ static const struct pci_device_id dwc3_pci_id_table[] = {
|
||||
{ PCI_DEVICE_DATA(INTEL, MTLP, &dwc3_pci_intel_swnode) },
|
||||
{ PCI_DEVICE_DATA(INTEL, MTL, &dwc3_pci_intel_swnode) },
|
||||
{ PCI_DEVICE_DATA(INTEL, MTLS, &dwc3_pci_intel_swnode) },
|
||||
{ PCI_DEVICE_DATA(INTEL, ARLH, &dwc3_pci_intel_swnode) },
|
||||
{ PCI_DEVICE_DATA(INTEL, ARLH_PCH, &dwc3_pci_intel_swnode) },
|
||||
{ PCI_DEVICE_DATA(INTEL, TGL, &dwc3_pci_intel_swnode) },
|
||||
|
||||
|
@ -2955,6 +2955,9 @@ static int dwc3_gadget_start(struct usb_gadget *g,
|
||||
dwc->gadget_driver = driver;
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
if (dwc->sys_wakeup)
|
||||
device_wakeup_enable(dwc->sysdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2970,6 +2973,9 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
|
||||
struct dwc3 *dwc = gadget_to_dwc(g);
|
||||
unsigned long flags;
|
||||
|
||||
if (dwc->sys_wakeup)
|
||||
device_wakeup_disable(dwc->sysdev);
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
dwc->gadget_driver = NULL;
|
||||
dwc->max_cfg_eps = 0;
|
||||
@ -4651,6 +4657,10 @@ int dwc3_gadget_init(struct dwc3 *dwc)
|
||||
else
|
||||
dwc3_gadget_set_speed(dwc->gadget, dwc->maximum_speed);
|
||||
|
||||
/* No system wakeup if no gadget driver bound */
|
||||
if (dwc->sys_wakeup)
|
||||
device_wakeup_disable(dwc->sysdev);
|
||||
|
||||
return 0;
|
||||
|
||||
err5:
|
||||
|
@ -173,6 +173,14 @@ int dwc3_host_init(struct dwc3 *dwc)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (dwc->sys_wakeup) {
|
||||
/* Restore wakeup setting if switched from device */
|
||||
device_wakeup_enable(dwc->sysdev);
|
||||
|
||||
/* Pass on wakeup setting to the new xhci platform device */
|
||||
device_init_wakeup(&xhci->dev, true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
platform_device_put(xhci);
|
||||
@ -181,6 +189,9 @@ int dwc3_host_init(struct dwc3 *dwc)
|
||||
|
||||
void dwc3_host_exit(struct dwc3 *dwc)
|
||||
{
|
||||
if (dwc->sys_wakeup)
|
||||
device_init_wakeup(&dwc->xhci->dev, false);
|
||||
|
||||
platform_device_unregister(dwc->xhci);
|
||||
dwc->xhci = NULL;
|
||||
}
|
||||
|
@ -292,7 +292,9 @@ int usb_ep_queue(struct usb_ep *ep,
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (WARN_ON_ONCE(!ep->enabled && ep->address)) {
|
||||
if (!ep->enabled && ep->address) {
|
||||
pr_debug("USB gadget: queue request to disabled ep 0x%x (%s)\n",
|
||||
ep->address, ep->name);
|
||||
ret = -ESHUTDOWN;
|
||||
goto out;
|
||||
}
|
||||
|
@ -518,8 +518,10 @@ static int ljca_new_client_device(struct ljca_adapter *adap, u8 type, u8 id,
|
||||
int ret;
|
||||
|
||||
client = kzalloc(sizeof *client, GFP_KERNEL);
|
||||
if (!client)
|
||||
if (!client) {
|
||||
kfree(data);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
client->type = type;
|
||||
client->id = id;
|
||||
@ -535,8 +537,10 @@ static int ljca_new_client_device(struct ljca_adapter *adap, u8 type, u8 id,
|
||||
auxdev->dev.release = ljca_auxdev_release;
|
||||
|
||||
ret = auxiliary_device_init(auxdev);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
kfree(data);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
ljca_auxdev_acpi_bind(adap, auxdev, adr, id);
|
||||
|
||||
@ -590,12 +594,8 @@ static int ljca_enumerate_gpio(struct ljca_adapter *adap)
|
||||
valid_pin[i] = get_unaligned_le32(&desc->bank_desc[i].valid_pins);
|
||||
bitmap_from_arr32(gpio_info->valid_pin_map, valid_pin, gpio_num);
|
||||
|
||||
ret = ljca_new_client_device(adap, LJCA_CLIENT_GPIO, 0, "ljca-gpio",
|
||||
return ljca_new_client_device(adap, LJCA_CLIENT_GPIO, 0, "ljca-gpio",
|
||||
gpio_info, LJCA_GPIO_ACPI_ADR);
|
||||
if (ret)
|
||||
kfree(gpio_info);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ljca_enumerate_i2c(struct ljca_adapter *adap)
|
||||
@ -629,10 +629,8 @@ static int ljca_enumerate_i2c(struct ljca_adapter *adap)
|
||||
ret = ljca_new_client_device(adap, LJCA_CLIENT_I2C, i,
|
||||
"ljca-i2c", i2c_info,
|
||||
LJCA_I2C1_ACPI_ADR + i);
|
||||
if (ret) {
|
||||
kfree(i2c_info);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -669,10 +667,8 @@ static int ljca_enumerate_spi(struct ljca_adapter *adap)
|
||||
ret = ljca_new_client_device(adap, LJCA_CLIENT_SPI, i,
|
||||
"ljca-spi", spi_info,
|
||||
LJCA_SPI1_ACPI_ADR + i);
|
||||
if (ret) {
|
||||
kfree(spi_info);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -262,13 +262,6 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop)
|
||||
return dev_err_probe(dev, PTR_ERR(nop->vbus_draw),
|
||||
"could not get vbus regulator\n");
|
||||
|
||||
nop->vbus_draw = devm_regulator_get_exclusive(dev, "vbus");
|
||||
if (PTR_ERR(nop->vbus_draw) == -ENODEV)
|
||||
nop->vbus_draw = NULL;
|
||||
if (IS_ERR(nop->vbus_draw))
|
||||
return dev_err_probe(dev, PTR_ERR(nop->vbus_draw),
|
||||
"could not get vbus regulator\n");
|
||||
|
||||
nop->dev = dev;
|
||||
nop->phy.dev = nop->dev;
|
||||
nop->phy.label = "nop-xceiv";
|
||||
|
@ -533,7 +533,7 @@ static struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp,
|
||||
* daft to me.
|
||||
*/
|
||||
|
||||
static struct urb *uas_submit_sense_urb(struct scsi_cmnd *cmnd, gfp_t gfp)
|
||||
static int uas_submit_sense_urb(struct scsi_cmnd *cmnd, gfp_t gfp)
|
||||
{
|
||||
struct uas_dev_info *devinfo = cmnd->device->hostdata;
|
||||
struct urb *urb;
|
||||
@ -541,30 +541,28 @@ static struct urb *uas_submit_sense_urb(struct scsi_cmnd *cmnd, gfp_t gfp)
|
||||
|
||||
urb = uas_alloc_sense_urb(devinfo, gfp, cmnd);
|
||||
if (!urb)
|
||||
return NULL;
|
||||
return -ENOMEM;
|
||||
usb_anchor_urb(urb, &devinfo->sense_urbs);
|
||||
err = usb_submit_urb(urb, gfp);
|
||||
if (err) {
|
||||
usb_unanchor_urb(urb);
|
||||
uas_log_cmd_state(cmnd, "sense submit err", err);
|
||||
usb_free_urb(urb);
|
||||
return NULL;
|
||||
}
|
||||
return urb;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int uas_submit_urbs(struct scsi_cmnd *cmnd,
|
||||
struct uas_dev_info *devinfo)
|
||||
{
|
||||
struct uas_cmd_info *cmdinfo = scsi_cmd_priv(cmnd);
|
||||
struct urb *urb;
|
||||
int err;
|
||||
|
||||
lockdep_assert_held(&devinfo->lock);
|
||||
if (cmdinfo->state & SUBMIT_STATUS_URB) {
|
||||
urb = uas_submit_sense_urb(cmnd, GFP_ATOMIC);
|
||||
if (!urb)
|
||||
return SCSI_MLQUEUE_DEVICE_BUSY;
|
||||
err = uas_submit_sense_urb(cmnd, GFP_ATOMIC);
|
||||
if (err)
|
||||
return err;
|
||||
cmdinfo->state &= ~SUBMIT_STATUS_URB;
|
||||
}
|
||||
|
||||
@ -572,7 +570,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
|
||||
cmdinfo->data_in_urb = uas_alloc_data_urb(devinfo, GFP_ATOMIC,
|
||||
cmnd, DMA_FROM_DEVICE);
|
||||
if (!cmdinfo->data_in_urb)
|
||||
return SCSI_MLQUEUE_DEVICE_BUSY;
|
||||
return -ENOMEM;
|
||||
cmdinfo->state &= ~ALLOC_DATA_IN_URB;
|
||||
}
|
||||
|
||||
@ -582,7 +580,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
|
||||
if (err) {
|
||||
usb_unanchor_urb(cmdinfo->data_in_urb);
|
||||
uas_log_cmd_state(cmnd, "data in submit err", err);
|
||||
return SCSI_MLQUEUE_DEVICE_BUSY;
|
||||
return err;
|
||||
}
|
||||
cmdinfo->state &= ~SUBMIT_DATA_IN_URB;
|
||||
cmdinfo->state |= DATA_IN_URB_INFLIGHT;
|
||||
@ -592,7 +590,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
|
||||
cmdinfo->data_out_urb = uas_alloc_data_urb(devinfo, GFP_ATOMIC,
|
||||
cmnd, DMA_TO_DEVICE);
|
||||
if (!cmdinfo->data_out_urb)
|
||||
return SCSI_MLQUEUE_DEVICE_BUSY;
|
||||
return -ENOMEM;
|
||||
cmdinfo->state &= ~ALLOC_DATA_OUT_URB;
|
||||
}
|
||||
|
||||
@ -602,7 +600,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
|
||||
if (err) {
|
||||
usb_unanchor_urb(cmdinfo->data_out_urb);
|
||||
uas_log_cmd_state(cmnd, "data out submit err", err);
|
||||
return SCSI_MLQUEUE_DEVICE_BUSY;
|
||||
return err;
|
||||
}
|
||||
cmdinfo->state &= ~SUBMIT_DATA_OUT_URB;
|
||||
cmdinfo->state |= DATA_OUT_URB_INFLIGHT;
|
||||
@ -611,7 +609,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
|
||||
if (cmdinfo->state & ALLOC_CMD_URB) {
|
||||
cmdinfo->cmd_urb = uas_alloc_cmd_urb(devinfo, GFP_ATOMIC, cmnd);
|
||||
if (!cmdinfo->cmd_urb)
|
||||
return SCSI_MLQUEUE_DEVICE_BUSY;
|
||||
return -ENOMEM;
|
||||
cmdinfo->state &= ~ALLOC_CMD_URB;
|
||||
}
|
||||
|
||||
@ -621,7 +619,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
|
||||
if (err) {
|
||||
usb_unanchor_urb(cmdinfo->cmd_urb);
|
||||
uas_log_cmd_state(cmnd, "cmd submit err", err);
|
||||
return SCSI_MLQUEUE_DEVICE_BUSY;
|
||||
return err;
|
||||
}
|
||||
cmdinfo->cmd_urb = NULL;
|
||||
cmdinfo->state &= ~SUBMIT_CMD_URB;
|
||||
@ -698,7 +696,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd)
|
||||
* of queueing, no matter how fatal the error
|
||||
*/
|
||||
if (err == -ENODEV) {
|
||||
set_host_byte(cmnd, DID_ERROR);
|
||||
set_host_byte(cmnd, DID_NO_CONNECT);
|
||||
scsi_done(cmnd);
|
||||
goto zombie;
|
||||
}
|
||||
|
@ -1310,6 +1310,7 @@ static ssize_t select_usb_power_delivery_store(struct device *dev,
|
||||
{
|
||||
struct typec_port *port = to_typec_port(dev);
|
||||
struct usb_power_delivery *pd;
|
||||
int ret;
|
||||
|
||||
if (!port->ops || !port->ops->pd_set)
|
||||
return -EOPNOTSUPP;
|
||||
@ -1318,7 +1319,11 @@ static ssize_t select_usb_power_delivery_store(struct device *dev,
|
||||
if (!pd)
|
||||
return -EINVAL;
|
||||
|
||||
return port->ops->pd_set(port, pd);
|
||||
ret = port->ops->pd_set(port, pd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t select_usb_power_delivery_show(struct device *dev,
|
||||
|
@ -6861,7 +6861,7 @@ static int tcpm_pd_set(struct typec_port *p, struct usb_power_delivery *pd)
|
||||
|
||||
if (data->source_desc.pdo[0]) {
|
||||
for (i = 0; i < PDO_MAX_OBJECTS && data->source_desc.pdo[i]; i++)
|
||||
port->snk_pdo[i] = data->source_desc.pdo[i];
|
||||
port->src_pdo[i] = data->source_desc.pdo[i];
|
||||
port->nr_src_pdo = i + 1;
|
||||
}
|
||||
|
||||
@ -6910,7 +6910,9 @@ static int tcpm_pd_set(struct typec_port *p, struct usb_power_delivery *pd)
|
||||
|
||||
port->port_source_caps = data->source_cap;
|
||||
port->port_sink_caps = data->sink_cap;
|
||||
typec_port_set_usb_power_delivery(p, NULL);
|
||||
port->selected_pd = pd;
|
||||
typec_port_set_usb_power_delivery(p, port->selected_pd);
|
||||
unlock:
|
||||
mutex_unlock(&port->lock);
|
||||
return ret;
|
||||
@ -6943,9 +6945,7 @@ static void tcpm_port_unregister_pd(struct tcpm_port *port)
|
||||
port->port_source_caps = NULL;
|
||||
for (i = 0; i < port->pd_count; i++) {
|
||||
usb_power_delivery_unregister_capabilities(port->pd_list[i]->sink_cap);
|
||||
kfree(port->pd_list[i]->sink_cap);
|
||||
usb_power_delivery_unregister_capabilities(port->pd_list[i]->source_cap);
|
||||
kfree(port->pd_list[i]->source_cap);
|
||||
devm_kfree(port->dev, port->pd_list[i]);
|
||||
port->pd_list[i] = NULL;
|
||||
usb_power_delivery_unregister(port->pds[i]);
|
||||
|
@ -151,8 +151,12 @@ static int ucsi_exec_command(struct ucsi *ucsi, u64 cmd)
|
||||
if (!(cci & UCSI_CCI_COMMAND_COMPLETE))
|
||||
return -EIO;
|
||||
|
||||
if (cci & UCSI_CCI_NOT_SUPPORTED)
|
||||
if (cci & UCSI_CCI_NOT_SUPPORTED) {
|
||||
if (ucsi_acknowledge_command(ucsi) < 0)
|
||||
dev_err(ucsi->dev,
|
||||
"ACK of unsupported command failed\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (cci & UCSI_CCI_ERROR) {
|
||||
if (cmd == UCSI_GET_ERROR_STATUS)
|
||||
@ -1133,17 +1137,21 @@ static int ucsi_check_cable(struct ucsi_connector *con)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = ucsi_get_cable_identity(con);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (con->ucsi->cap.features & UCSI_CAP_GET_PD_MESSAGE) {
|
||||
ret = ucsi_get_cable_identity(con);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ucsi_register_plug(con);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (con->ucsi->cap.features & UCSI_CAP_ALT_MODE_DETAILS) {
|
||||
ret = ucsi_register_plug(con);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_SOP_P);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_SOP_P);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1189,8 +1197,10 @@ static void ucsi_handle_connector_change(struct work_struct *work)
|
||||
ucsi_register_partner(con);
|
||||
ucsi_partner_task(con, ucsi_check_connection, 1, HZ);
|
||||
ucsi_partner_task(con, ucsi_check_connector_capability, 1, HZ);
|
||||
ucsi_partner_task(con, ucsi_get_partner_identity, 1, HZ);
|
||||
ucsi_partner_task(con, ucsi_check_cable, 1, HZ);
|
||||
if (con->ucsi->cap.features & UCSI_CAP_GET_PD_MESSAGE)
|
||||
ucsi_partner_task(con, ucsi_get_partner_identity, 1, HZ);
|
||||
if (con->ucsi->cap.features & UCSI_CAP_CABLE_DETAILS)
|
||||
ucsi_partner_task(con, ucsi_check_cable, 1, HZ);
|
||||
|
||||
if (UCSI_CONSTAT_PWR_OPMODE(con->status.flags) ==
|
||||
UCSI_CONSTAT_PWR_OPMODE_PD)
|
||||
@ -1215,11 +1225,11 @@ static void ucsi_handle_connector_change(struct work_struct *work)
|
||||
if (con->status.change & UCSI_CONSTAT_CAM_CHANGE)
|
||||
ucsi_partner_task(con, ucsi_check_altmodes, 1, 0);
|
||||
|
||||
clear_bit(EVENT_PENDING, &con->ucsi->flags);
|
||||
|
||||
mutex_lock(&ucsi->ppm_lock);
|
||||
clear_bit(EVENT_PENDING, &con->ucsi->flags);
|
||||
ret = ucsi_acknowledge_connector_change(ucsi);
|
||||
mutex_unlock(&ucsi->ppm_lock);
|
||||
|
||||
if (ret)
|
||||
dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret);
|
||||
|
||||
@ -1237,7 +1247,7 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num)
|
||||
struct ucsi_connector *con = &ucsi->connector[num - 1];
|
||||
|
||||
if (!(ucsi->ntfy & UCSI_ENABLE_NTFY_CONNECTOR_CHANGE)) {
|
||||
dev_dbg(ucsi->dev, "Bogus connector change event\n");
|
||||
dev_dbg(ucsi->dev, "Early connector change event\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1260,13 +1270,47 @@ static int ucsi_reset_connector(struct ucsi_connector *con, bool hard)
|
||||
|
||||
static int ucsi_reset_ppm(struct ucsi *ucsi)
|
||||
{
|
||||
u64 command = UCSI_PPM_RESET;
|
||||
u64 command;
|
||||
unsigned long tmo;
|
||||
u32 cci;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ucsi->ppm_lock);
|
||||
|
||||
ret = ucsi->ops->read(ucsi, UCSI_CCI, &cci, sizeof(cci));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* If UCSI_CCI_RESET_COMPLETE is already set we must clear
|
||||
* the flag before we start another reset. Send a
|
||||
* UCSI_SET_NOTIFICATION_ENABLE command to achieve this.
|
||||
* Ignore a timeout and try the reset anyway if this fails.
|
||||
*/
|
||||
if (cci & UCSI_CCI_RESET_COMPLETE) {
|
||||
command = UCSI_SET_NOTIFICATION_ENABLE;
|
||||
ret = ucsi->ops->async_write(ucsi, UCSI_CONTROL, &command,
|
||||
sizeof(command));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
tmo = jiffies + msecs_to_jiffies(UCSI_TIMEOUT_MS);
|
||||
do {
|
||||
ret = ucsi->ops->read(ucsi, UCSI_CCI,
|
||||
&cci, sizeof(cci));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
if (cci & UCSI_CCI_COMMAND_COMPLETE)
|
||||
break;
|
||||
if (time_is_before_jiffies(tmo))
|
||||
break;
|
||||
msleep(20);
|
||||
} while (1);
|
||||
|
||||
WARN_ON(cci & UCSI_CCI_RESET_COMPLETE);
|
||||
}
|
||||
|
||||
command = UCSI_PPM_RESET;
|
||||
ret = ucsi->ops->async_write(ucsi, UCSI_CONTROL, &command,
|
||||
sizeof(command));
|
||||
if (ret < 0)
|
||||
@ -1589,8 +1633,10 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con)
|
||||
ucsi_register_partner(con);
|
||||
ucsi_pwr_opmode_change(con);
|
||||
ucsi_port_psy_changed(con);
|
||||
ucsi_get_partner_identity(con);
|
||||
ucsi_check_cable(con);
|
||||
if (con->ucsi->cap.features & UCSI_CAP_GET_PD_MESSAGE)
|
||||
ucsi_get_partner_identity(con);
|
||||
if (con->ucsi->cap.features & UCSI_CAP_CABLE_DETAILS)
|
||||
ucsi_check_cable(con);
|
||||
}
|
||||
|
||||
/* Only notify USB controller if partner supports USB data */
|
||||
@ -1636,6 +1682,7 @@ static int ucsi_init(struct ucsi *ucsi)
|
||||
{
|
||||
struct ucsi_connector *con, *connector;
|
||||
u64 command, ntfy;
|
||||
u32 cci;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
@ -1688,6 +1735,13 @@ static int ucsi_init(struct ucsi *ucsi)
|
||||
|
||||
ucsi->connector = connector;
|
||||
ucsi->ntfy = ntfy;
|
||||
|
||||
ret = ucsi->ops->read(ucsi, UCSI_CCI, &cci, sizeof(cci));
|
||||
if (ret)
|
||||
return ret;
|
||||
if (UCSI_CCI_CONNECTOR(READ_ONCE(cci)))
|
||||
ucsi_connector_change(ucsi, cci);
|
||||
|
||||
return 0;
|
||||
|
||||
err_unregister:
|
||||
|
@ -206,7 +206,7 @@ struct ucsi_capability {
|
||||
#define UCSI_CAP_ATTR_POWER_OTHER BIT(10)
|
||||
#define UCSI_CAP_ATTR_POWER_VBUS BIT(14)
|
||||
u8 num_connectors;
|
||||
u8 features;
|
||||
u16 features;
|
||||
#define UCSI_CAP_SET_UOM BIT(0)
|
||||
#define UCSI_CAP_SET_PDM BIT(1)
|
||||
#define UCSI_CAP_ALT_MODE_DETAILS BIT(2)
|
||||
@ -215,7 +215,8 @@ struct ucsi_capability {
|
||||
#define UCSI_CAP_CABLE_DETAILS BIT(5)
|
||||
#define UCSI_CAP_EXT_SUPPLY_NOTIFICATIONS BIT(6)
|
||||
#define UCSI_CAP_PD_RESET BIT(7)
|
||||
u16 reserved_1;
|
||||
#define UCSI_CAP_GET_PD_MESSAGE BIT(8)
|
||||
u8 reserved_1;
|
||||
u8 num_alt_modes;
|
||||
u8 reserved_2;
|
||||
u16 bc_version;
|
||||
|
@ -23,10 +23,11 @@ struct ucsi_acpi {
|
||||
void *base;
|
||||
struct completion complete;
|
||||
unsigned long flags;
|
||||
#define UCSI_ACPI_SUPPRESS_EVENT 0
|
||||
#define UCSI_ACPI_COMMAND_PENDING 1
|
||||
#define UCSI_ACPI_ACK_PENDING 2
|
||||
guid_t guid;
|
||||
u64 cmd;
|
||||
bool dell_quirk_probed;
|
||||
bool dell_quirk_active;
|
||||
};
|
||||
|
||||
static int ucsi_acpi_dsm(struct ucsi_acpi *ua, int func)
|
||||
@ -79,9 +80,9 @@ static int ucsi_acpi_sync_write(struct ucsi *ucsi, unsigned int offset,
|
||||
int ret;
|
||||
|
||||
if (ack)
|
||||
set_bit(ACK_PENDING, &ua->flags);
|
||||
set_bit(UCSI_ACPI_ACK_PENDING, &ua->flags);
|
||||
else
|
||||
set_bit(COMMAND_PENDING, &ua->flags);
|
||||
set_bit(UCSI_ACPI_COMMAND_PENDING, &ua->flags);
|
||||
|
||||
ret = ucsi_acpi_async_write(ucsi, offset, val, val_len);
|
||||
if (ret)
|
||||
@ -92,9 +93,9 @@ static int ucsi_acpi_sync_write(struct ucsi *ucsi, unsigned int offset,
|
||||
|
||||
out_clear_bit:
|
||||
if (ack)
|
||||
clear_bit(ACK_PENDING, &ua->flags);
|
||||
clear_bit(UCSI_ACPI_ACK_PENDING, &ua->flags);
|
||||
else
|
||||
clear_bit(COMMAND_PENDING, &ua->flags);
|
||||
clear_bit(UCSI_ACPI_COMMAND_PENDING, &ua->flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -129,51 +130,40 @@ static const struct ucsi_operations ucsi_zenbook_ops = {
|
||||
};
|
||||
|
||||
/*
|
||||
* Some Dell laptops expect that an ACK command with the
|
||||
* UCSI_ACK_CONNECTOR_CHANGE bit set is followed by a (separate)
|
||||
* ACK command that only has the UCSI_ACK_COMMAND_COMPLETE bit set.
|
||||
* If this is not done events are not delivered to OSPM and
|
||||
* subsequent commands will timeout.
|
||||
* Some Dell laptops don't like ACK commands with the
|
||||
* UCSI_ACK_CONNECTOR_CHANGE but not the UCSI_ACK_COMMAND_COMPLETE
|
||||
* bit set. To work around this send a dummy command and bundle the
|
||||
* UCSI_ACK_CONNECTOR_CHANGE with the UCSI_ACK_COMMAND_COMPLETE
|
||||
* for the dummy command.
|
||||
*/
|
||||
static int
|
||||
ucsi_dell_sync_write(struct ucsi *ucsi, unsigned int offset,
|
||||
const void *val, size_t val_len)
|
||||
{
|
||||
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
|
||||
u64 cmd = *(u64 *)val, ack = 0;
|
||||
u64 cmd = *(u64 *)val;
|
||||
u64 dummycmd = UCSI_GET_CAPABILITY;
|
||||
int ret;
|
||||
|
||||
if (UCSI_COMMAND(cmd) == UCSI_ACK_CC_CI &&
|
||||
cmd & UCSI_ACK_CONNECTOR_CHANGE)
|
||||
ack = UCSI_ACK_CC_CI | UCSI_ACK_COMMAND_COMPLETE;
|
||||
if (cmd == (UCSI_ACK_CC_CI | UCSI_ACK_CONNECTOR_CHANGE)) {
|
||||
cmd |= UCSI_ACK_COMMAND_COMPLETE;
|
||||
|
||||
ret = ucsi_acpi_sync_write(ucsi, offset, val, val_len);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
if (ack == 0)
|
||||
return ret;
|
||||
/*
|
||||
* The UCSI core thinks it is sending a connector change ack
|
||||
* and will accept new connector change events. We don't want
|
||||
* this to happen for the dummy command as its response will
|
||||
* still report the very event that the core is trying to clear.
|
||||
*/
|
||||
set_bit(UCSI_ACPI_SUPPRESS_EVENT, &ua->flags);
|
||||
ret = ucsi_acpi_sync_write(ucsi, UCSI_CONTROL, &dummycmd,
|
||||
sizeof(dummycmd));
|
||||
clear_bit(UCSI_ACPI_SUPPRESS_EVENT, &ua->flags);
|
||||
|
||||
if (!ua->dell_quirk_probed) {
|
||||
ua->dell_quirk_probed = true;
|
||||
|
||||
cmd = UCSI_GET_CAPABILITY;
|
||||
ret = ucsi_acpi_sync_write(ucsi, UCSI_CONTROL, &cmd,
|
||||
sizeof(cmd));
|
||||
if (ret == 0)
|
||||
return ucsi_acpi_sync_write(ucsi, UCSI_CONTROL,
|
||||
&ack, sizeof(ack));
|
||||
if (ret != -ETIMEDOUT)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ua->dell_quirk_active = true;
|
||||
dev_err(ua->dev, "Firmware bug: Additional ACK required after ACKing a connector change.\n");
|
||||
dev_err(ua->dev, "Firmware bug: Enabling workaround\n");
|
||||
}
|
||||
|
||||
if (!ua->dell_quirk_active)
|
||||
return ret;
|
||||
|
||||
return ucsi_acpi_sync_write(ucsi, UCSI_CONTROL, &ack, sizeof(ack));
|
||||
return ucsi_acpi_sync_write(ucsi, UCSI_CONTROL, &cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static const struct ucsi_operations ucsi_dell_ops = {
|
||||
@ -209,13 +199,14 @@ static void ucsi_acpi_notify(acpi_handle handle, u32 event, void *data)
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
if (UCSI_CCI_CONNECTOR(cci))
|
||||
if (UCSI_CCI_CONNECTOR(cci) &&
|
||||
!test_bit(UCSI_ACPI_SUPPRESS_EVENT, &ua->flags))
|
||||
ucsi_connector_change(ua->ucsi, UCSI_CCI_CONNECTOR(cci));
|
||||
|
||||
if (cci & UCSI_CCI_ACK_COMPLETE && test_bit(ACK_PENDING, &ua->flags))
|
||||
complete(&ua->complete);
|
||||
if (cci & UCSI_CCI_COMMAND_COMPLETE &&
|
||||
test_bit(COMMAND_PENDING, &ua->flags))
|
||||
test_bit(UCSI_ACPI_COMMAND_PENDING, &ua->flags))
|
||||
complete(&ua->complete);
|
||||
}
|
||||
|
||||
|
@ -255,6 +255,20 @@ static void pmic_glink_ucsi_notify(struct work_struct *work)
|
||||
static void pmic_glink_ucsi_register(struct work_struct *work)
|
||||
{
|
||||
struct pmic_glink_ucsi *ucsi = container_of(work, struct pmic_glink_ucsi, register_work);
|
||||
int orientation;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PMIC_GLINK_MAX_PORTS; i++) {
|
||||
if (!ucsi->port_orientation[i])
|
||||
continue;
|
||||
orientation = gpiod_get_value(ucsi->port_orientation[i]);
|
||||
|
||||
if (orientation >= 0) {
|
||||
typec_switch_set(ucsi->port_switch[i],
|
||||
orientation ? TYPEC_ORIENTATION_REVERSE
|
||||
: TYPEC_ORIENTATION_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
ucsi_register(ucsi->ucsi);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user