Commit ff789a26 authored by Linus Torvalds's avatar Linus Torvalds

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()
  ...
parents 4e6e4229 f4d19607
...@@ -485,6 +485,7 @@ static ssize_t wdm_write ...@@ -485,6 +485,7 @@ static ssize_t wdm_write
static int service_outstanding_interrupt(struct wdm_device *desc) static int service_outstanding_interrupt(struct wdm_device *desc)
{ {
int rv = 0; int rv = 0;
int used;
/* submit read urb only if the device is waiting for it */ /* submit read urb only if the device is waiting for it */
if (!desc->resp_count || !--desc->resp_count) if (!desc->resp_count || !--desc->resp_count)
...@@ -499,7 +500,10 @@ static int service_outstanding_interrupt(struct wdm_device *desc) ...@@ -499,7 +500,10 @@ static int service_outstanding_interrupt(struct wdm_device *desc)
goto out; 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); spin_unlock_irq(&desc->iuspin);
rv = usb_submit_urb(desc->response, GFP_KERNEL); rv = usb_submit_urb(desc->response, GFP_KERNEL);
spin_lock_irq(&desc->iuspin); spin_lock_irq(&desc->iuspin);
......
...@@ -130,7 +130,6 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem); ...@@ -130,7 +130,6 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
#define HUB_DEBOUNCE_STEP 25 #define HUB_DEBOUNCE_STEP 25
#define HUB_DEBOUNCE_STABLE 100 #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 usb_reset_and_verify_device(struct usb_device *udev);
static int hub_port_disable(struct usb_hub *hub, int port1, int set_state); 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, 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) ...@@ -720,14 +719,14 @@ static void kick_hub_wq(struct usb_hub *hub)
*/ */
intf = to_usb_interface(hub->intfdev); intf = to_usb_interface(hub->intfdev);
usb_autopm_get_interface_no_resume(intf); usb_autopm_get_interface_no_resume(intf);
kref_get(&hub->kref); hub_get(hub);
if (queue_work(hub_wq, &hub->events)) if (queue_work(hub_wq, &hub->events))
return; return;
/* the work has already been scheduled */ /* the work has already been scheduled */
usb_autopm_put_interface_async(intf); usb_autopm_put_interface_async(intf);
kref_put(&hub->kref, hub_release); hub_put(hub);
} }
void usb_kick_hub_wq(struct usb_device *hdev) 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) ...@@ -1095,7 +1094,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
goto init2; goto init2;
goto init3; goto init3;
} }
kref_get(&hub->kref); hub_get(hub);
/* The superspeed hub except for root hub has to use Hub Depth /* The superspeed hub except for root hub has to use Hub Depth
* value as an offset into the route string to locate the bits * 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) ...@@ -1343,7 +1342,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
device_unlock(&hdev->dev); device_unlock(&hdev->dev);
} }
kref_put(&hub->kref, hub_release); hub_put(hub);
} }
/* Implement the continuations for the delays above */ /* Implement the continuations for the delays above */
...@@ -1759,6 +1758,16 @@ static void hub_release(struct kref *kref) ...@@ -1759,6 +1758,16 @@ static void hub_release(struct kref *kref)
kfree(hub); 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 unsigned highspeed_hubs;
static void hub_disconnect(struct usb_interface *intf) static void hub_disconnect(struct usb_interface *intf)
...@@ -1807,7 +1816,7 @@ 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); 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) static bool hub_descriptor_is_sane(struct usb_host_interface *desc)
...@@ -5934,7 +5943,7 @@ static void hub_event(struct work_struct *work) ...@@ -5934,7 +5943,7 @@ static void hub_event(struct work_struct *work)
/* Balance the stuff in kick_hub_wq() and allow autosuspend */ /* Balance the stuff in kick_hub_wq() and allow autosuspend */
usb_autopm_put_interface(intf); usb_autopm_put_interface(intf);
kref_put(&hub->kref, hub_release); hub_put(hub);
kcov_remote_stop(); kcov_remote_stop();
} }
......
...@@ -129,6 +129,8 @@ extern void usb_hub_remove_port_device(struct usb_hub *hub, ...@@ -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, extern int usb_hub_set_port_power(struct usb_device *hdev, struct usb_hub *hub,
int port1, bool set); int port1, bool set);
extern struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev); 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, extern int hub_port_debounce(struct usb_hub *hub, int port1,
bool must_be_connected); bool must_be_connected);
extern int usb_clear_port_feature(struct usb_device *hdev, extern int usb_clear_port_feature(struct usb_device *hdev,
......
...@@ -56,11 +56,22 @@ static ssize_t disable_show(struct device *dev, ...@@ -56,11 +56,22 @@ static ssize_t disable_show(struct device *dev,
u16 portstatus, unused; u16 portstatus, unused;
bool disabled; bool disabled;
int rc; int rc;
struct kernfs_node *kn;
hub_get(hub);
rc = usb_autopm_get_interface(intf); rc = usb_autopm_get_interface(intf);
if (rc < 0) 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); usb_lock_device(hdev);
if (hub->disconnected) { if (hub->disconnected) {
rc = -ENODEV; rc = -ENODEV;
...@@ -70,9 +81,13 @@ static ssize_t disable_show(struct device *dev, ...@@ -70,9 +81,13 @@ static ssize_t disable_show(struct device *dev,
usb_hub_port_status(hub, port1, &portstatus, &unused); usb_hub_port_status(hub, port1, &portstatus, &unused);
disabled = !usb_port_is_power_on(hub, portstatus); disabled = !usb_port_is_power_on(hub, portstatus);
out_hdev_lock: out_hdev_lock:
usb_unlock_device(hdev); usb_unlock_device(hdev);
sysfs_unbreak_active_protection(kn);
out_autopm:
usb_autopm_put_interface(intf); usb_autopm_put_interface(intf);
out_hub_get:
hub_put(hub);
if (rc) if (rc)
return rc; return rc;
...@@ -90,15 +105,26 @@ static ssize_t disable_store(struct device *dev, struct device_attribute *attr, ...@@ -90,15 +105,26 @@ static ssize_t disable_store(struct device *dev, struct device_attribute *attr,
int port1 = port_dev->portnum; int port1 = port_dev->portnum;
bool disabled; bool disabled;
int rc; int rc;
struct kernfs_node *kn;
rc = kstrtobool(buf, &disabled); rc = kstrtobool(buf, &disabled);
if (rc) if (rc)
return rc; return rc;
hub_get(hub);
rc = usb_autopm_get_interface(intf); rc = usb_autopm_get_interface(intf);
if (rc < 0) 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); usb_lock_device(hdev);
if (hub->disconnected) { if (hub->disconnected) {
rc = -ENODEV; rc = -ENODEV;
...@@ -119,9 +145,13 @@ static ssize_t disable_store(struct device *dev, struct device_attribute *attr, ...@@ -119,9 +145,13 @@ static ssize_t disable_store(struct device *dev, struct device_attribute *attr,
if (!rc) if (!rc)
rc = count; rc = count;
out_hdev_lock: out_hdev_lock:
usb_unlock_device(hdev); usb_unlock_device(hdev);
sysfs_unbreak_active_protection(kn);
out_autopm:
usb_autopm_put_interface(intf); usb_autopm_put_interface(intf);
out_hub_get:
hub_put(hub);
return rc; return rc;
} }
......
...@@ -1217,14 +1217,24 @@ static ssize_t interface_authorized_store(struct device *dev, ...@@ -1217,14 +1217,24 @@ static ssize_t interface_authorized_store(struct device *dev,
{ {
struct usb_interface *intf = to_usb_interface(dev); struct usb_interface *intf = to_usb_interface(dev);
bool val; bool val;
struct kernfs_node *kn;
if (kstrtobool(buf, &val) != 0) if (kstrtobool(buf, &val) != 0)
return -EINVAL; return -EINVAL;
if (val) if (val) {
usb_authorize_interface(intf); usb_authorize_interface(intf);
else } else {
usb_deauthorize_interface(intf); /*
* 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; return count;
} }
......
...@@ -729,8 +729,14 @@ struct dwc2_dregs_backup { ...@@ -729,8 +729,14 @@ struct dwc2_dregs_backup {
* struct dwc2_hregs_backup - Holds host registers state before * struct dwc2_hregs_backup - Holds host registers state before
* entering partial power down * entering partial power down
* @hcfg: Backup of HCFG register * @hcfg: Backup of HCFG register
* @hflbaddr: Backup of HFLBADDR register
* @haintmsk: Backup of HAINTMSK register * @haintmsk: Backup of HAINTMSK register
* @hcchar: Backup of HCCHAR register
* @hcsplt: Backup of HCSPLT register
* @hcintmsk: Backup of HCINTMSK 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 * @hprt0: Backup of HPTR0 register
* @hfir: Backup of HFIR register * @hfir: Backup of HFIR register
* @hptxfsiz: Backup of HPTXFSIZ register * @hptxfsiz: Backup of HPTXFSIZ register
...@@ -738,8 +744,14 @@ struct dwc2_dregs_backup { ...@@ -738,8 +744,14 @@ struct dwc2_dregs_backup {
*/ */
struct dwc2_hregs_backup { struct dwc2_hregs_backup {
u32 hcfg; u32 hcfg;
u32 hflbaddr;
u32 haintmsk; u32 haintmsk;
u32 hcchar[MAX_EPS_CHANNELS];
u32 hcsplt[MAX_EPS_CHANNELS];
u32 hcintmsk[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 hprt0;
u32 hfir; u32 hfir;
u32 hptxfsiz; u32 hptxfsiz;
...@@ -1086,6 +1098,7 @@ struct dwc2_hsotg { ...@@ -1086,6 +1098,7 @@ struct dwc2_hsotg {
bool needs_byte_swap; bool needs_byte_swap;
/* DWC OTG HW Release versions */ /* DWC OTG HW Release versions */
#define DWC2_CORE_REV_4_30a 0x4f54430a
#define DWC2_CORE_REV_2_71a 0x4f54271a #define DWC2_CORE_REV_2_71a 0x4f54271a
#define DWC2_CORE_REV_2_72a 0x4f54272a #define DWC2_CORE_REV_2_72a 0x4f54272a
#define DWC2_CORE_REV_2_80a 0x4f54280a #define DWC2_CORE_REV_2_80a 0x4f54280a
...@@ -1323,6 +1336,7 @@ int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg); ...@@ -1323,6 +1336,7 @@ int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg);
int dwc2_restore_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_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. */ /* This function should be called on every hardware interrupt. */
irqreturn_t dwc2_handle_common_intr(int irq, void *dev); 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) ...@@ -297,7 +297,8 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
/* Exit gadget mode clock gating. */ /* Exit gadget mode clock gating. */
if (hsotg->params.power_down == 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); dwc2_gadget_exit_clock_gating(hsotg, 0);
} }
...@@ -322,10 +323,11 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg) ...@@ -322,10 +323,11 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
* @hsotg: Programming view of DWC_otg controller * @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 glpmcfg;
u32 i = 0; u32 pcgctl;
u32 dctl;
if (hsotg->lx_state != DWC2_L1) { if (hsotg->lx_state != DWC2_L1) {
dev_err(hsotg->dev, "Core isn't in DWC2_L1 state\n"); 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) ...@@ -334,37 +336,57 @@ static void dwc2_wakeup_from_lpm_l1(struct dwc2_hsotg *hsotg)
glpmcfg = dwc2_readl(hsotg, GLPMCFG); glpmcfg = dwc2_readl(hsotg, GLPMCFG);
if (dwc2_is_device_mode(hsotg)) { 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_ENBLSLPM;
glpmcfg &= ~GLPMCFG_HIRD_THRES_EN; glpmcfg &= ~GLPMCFG_HIRD_THRES_MASK;
dwc2_writel(hsotg, glpmcfg, GLPMCFG); dwc2_writel(hsotg, glpmcfg, GLPMCFG);
do { pcgctl = dwc2_readl(hsotg, PCGCTL);
glpmcfg = dwc2_readl(hsotg, GLPMCFG); pcgctl &= ~PCGCTL_ENBL_SLEEP_GATING;
dwc2_writel(hsotg, pcgctl, PCGCTL);
if (!(glpmcfg & (GLPMCFG_COREL1RES_MASK | glpmcfg = dwc2_readl(hsotg, GLPMCFG);
GLPMCFG_L1RESUMEOK | GLPMCFG_SLPSTS))) if (glpmcfg & GLPMCFG_ENBESL) {
break; 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); if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS, GINTSTS_WKUPINT, 1000)) {
} while (++i < 200); dev_warn(hsotg->dev, "%s: timeout GINTSTS_WKUPINT\n", __func__);
goto fail;
return;
}
}
if (i == 200) { glpmcfg = dwc2_readl(hsotg, GLPMCFG);
dev_err(hsotg->dev, "Failed to exit L1 sleep state in 200us.\n"); if (glpmcfg & GLPMCFG_COREL1RES_MASK || glpmcfg & GLPMCFG_SLPSTS ||
glpmcfg & GLPMCFG_L1RESUMEOK) {
goto fail;
return; 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 { } else {
/* TODO */ /* TODO */
dev_err(hsotg->dev, "Host side LPM is not supported.\n"); dev_err(hsotg->dev, "Host side LPM is not supported.\n");
return; 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) ...@@ -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); dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state);
if (hsotg->lx_state == DWC2_L1) { if (hsotg->lx_state == DWC2_L1) {
dwc2_wakeup_from_lpm_l1(hsotg); dwc2_wakeup_from_lpm_l1(hsotg, false);
return; return;
} }
...@@ -408,7 +430,8 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) ...@@ -408,7 +430,8 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
/* Exit gadget mode clock gating. */ /* Exit gadget mode clock gating. */
if (hsotg->params.power_down == 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); dwc2_gadget_exit_clock_gating(hsotg, 0);
} else { } else {
/* Change to L0 state */ /* Change to L0 state */
...@@ -425,7 +448,8 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) ...@@ -425,7 +448,8 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
} }
if (hsotg->params.power_down == 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); 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, ...@@ -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, ep->name, req, req->length, req->buf, req->no_interrupt,
req->zero, req->short_not_ok); 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 */ /* Prevent new request submission when controller is suspended */
if (hs->lx_state != DWC2_L0) { if (hs->lx_state != DWC2_L0) {
dev_dbg(hs->dev, "%s: submit request only in active state\n", 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) ...@@ -3727,6 +3731,12 @@ static irqreturn_t dwc2_hsotg_irq(int irq, void *pw)
if (hsotg->in_ppd && hsotg->lx_state == DWC2_L2) if (hsotg->in_ppd && hsotg->lx_state == DWC2_L2)
dwc2_exit_partial_power_down(hsotg, 0, true); 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; hsotg->lx_state = DWC2_L0;
} }
......
...@@ -2701,8 +2701,11 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions( ...@@ -2701,8 +2701,11 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions(
hsotg->available_host_channels--; hsotg->available_host_channels--;
} }
qh = list_entry(qh_ptr, struct dwc2_qh, qh_list_entry); 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; break;
}
/* /*
* Move the QH from the periodic ready schedule to the * Move the QH from the periodic ready schedule to the
...@@ -2735,8 +2738,11 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions( ...@@ -2735,8 +2738,11 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions(
hsotg->available_host_channels--; 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; break;
}
/* /*
* Move the QH from the non-periodic inactive schedule to the * 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, ...@@ -4143,6 +4149,8 @@ void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
urb->actual_length); urb->actual_length);
if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { 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); urb->error_count = dwc2_hcd_urb_get_error_count(qtd->urb);
for (i = 0; i < urb->number_of_packets; ++i) { for (i = 0; i < urb->number_of_packets; ++i) {
urb->iso_frame_desc[i].actual_length = urb->iso_frame_desc[i].actual_length =
...@@ -4649,7 +4657,7 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, ...@@ -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 && 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)) if (dwc2_is_device_mode(hsotg))
dwc2_gadget_exit_clock_gating(hsotg, 0); dwc2_gadget_exit_clock_gating(hsotg, 0);
else else
...@@ -5406,9 +5414,16 @@ int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg) ...@@ -5406,9 +5414,16 @@ int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg)
/* Backup Host regs */ /* Backup Host regs */
hr = &hsotg->hr_backup; hr = &hsotg->hr_backup;
hr->hcfg = dwc2_readl(hsotg, HCFG); hr->hcfg = dwc2_readl(hsotg, HCFG);
hr->hflbaddr = dwc2_readl(hsotg, HFLBADDR);
hr->haintmsk = dwc2_readl(hsotg, HAINTMSK); 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->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->hprt0 = dwc2_read_hprt0(hsotg);
hr->hfir = dwc2_readl(hsotg, HFIR); hr->hfir = dwc2_readl(hsotg, HFIR);
...@@ -5442,10 +5457,17 @@ int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg) ...@@ -5442,10 +5457,17 @@ int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
hr->valid = false; hr->valid = false;
dwc2_writel(hsotg, hr->hcfg, HCFG); dwc2_writel(hsotg, hr->hcfg, HCFG);
dwc2_writel(hsotg, hr->hflbaddr, HFLBADDR);
dwc2_writel(hsotg, hr->haintmsk, HAINTMSK); 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->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->hprt0, HPRT0);
dwc2_writel(hsotg, hr->hfir, HFIR); dwc2_writel(hsotg, hr->hfir, HFIR);
...@@ -5610,10 +5632,12 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, ...@@ -5610,10 +5632,12 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup,
dwc2_writel(hsotg, hr->hcfg, HCFG); dwc2_writel(hsotg, hr->hcfg, HCFG);
/* De-assert Wakeup Logic */ /* De-assert Wakeup Logic */
gpwrdn = dwc2_readl(hsotg, GPWRDN); if (!(rem_wakeup && hsotg->hw_params.snpsid >= DWC2_CORE_REV_4_30a)) {
gpwrdn &= ~GPWRDN_PMUACTV; gpwrdn = dwc2_readl(hsotg, GPWRDN);
dwc2_writel(hsotg, gpwrdn, GPWRDN); gpwrdn &= ~GPWRDN_PMUACTV;
udelay(10); dwc2_writel(hsotg, gpwrdn, GPWRDN);
udelay(10);
}
hprt0 = hr->hprt0; hprt0 = hr->hprt0;
hprt0 |= HPRT0_PWR; hprt0 |= HPRT0_PWR;
...@@ -5638,6 +5662,13 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, ...@@ -5638,6 +5662,13 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup,
hprt0 |= HPRT0_RES; hprt0 |= HPRT0_RES;
dwc2_writel(hsotg, hprt0, HPRT0); 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 */ /* Wait for Resume time and then program HPRT again */
mdelay(100); mdelay(100);
hprt0 &= ~HPRT0_RES; hprt0 &= ~HPRT0_RES;
......
...@@ -559,7 +559,7 @@ static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg, ...@@ -559,7 +559,7 @@ static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
idx = qh->td_last; idx = qh->td_last;
inc = qh->host_interval; inc = qh->host_interval;
hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg); 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); 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, ...@@ -866,6 +866,8 @@ static int dwc2_cmpl_host_isoc_dma_desc(struct dwc2_hsotg *hsotg,
{ {
struct dwc2_dma_desc *dma_desc; struct dwc2_dma_desc *dma_desc;
struct dwc2_hcd_iso_packet_desc *frame_desc; struct dwc2_hcd_iso_packet_desc *frame_desc;
u16 frame_desc_idx;
struct urb *usb_urb = qtd->urb->priv;
u16 remain = 0; u16 remain = 0;
int rc = 0; int rc = 0;
...@@ -878,8 +880,11 @@ static int dwc2_cmpl_host_isoc_dma_desc(struct dwc2_hsotg *hsotg, ...@@ -878,8 +880,11 @@ static int dwc2_cmpl_host_isoc_dma_desc(struct dwc2_hsotg *hsotg,
DMA_FROM_DEVICE); DMA_FROM_DEVICE);
dma_desc = &qh->desc_list[idx]; 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); dma_desc->buf = (u32)(qtd->urb->dma + frame_desc->offset);
if (chan->ep_is_in) if (chan->ep_is_in)
remain = (dma_desc->status & HOST_DMA_ISOC_NBYTES_MASK) >> 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, ...@@ -900,7 +905,7 @@ static int dwc2_cmpl_host_isoc_dma_desc(struct dwc2_hsotg *hsotg,
frame_desc->status = 0; 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 * urb->status is not used for isoc transfers here. The
* individual frame_desc status are used instead. * individual frame_desc status are used instead.
...@@ -1005,11 +1010,11 @@ static void dwc2_complete_isoc_xfer_ddma(struct dwc2_hsotg *hsotg, ...@@ -1005,11 +1010,11 @@ static void dwc2_complete_isoc_xfer_ddma(struct dwc2_hsotg *hsotg,
return; return;
idx = dwc2_desclist_idx_inc(idx, qh->host_interval, idx = dwc2_desclist_idx_inc(idx, qh->host_interval,
chan->speed); chan->speed);
if (!rc) if (rc == 0)
continue; continue;
if (rc == DWC2_CMPL_DONE) if (rc == DWC2_CMPL_DONE || rc == DWC2_CMPL_STOP)
break; goto stop_scan;
/* rc == DWC2_CMPL_STOP */ /* rc == DWC2_CMPL_STOP */
......
...@@ -698,7 +698,7 @@ ...@@ -698,7 +698,7 @@
#define TXSTS_QTOP_TOKEN_MASK (0x3 << 25) #define TXSTS_QTOP_TOKEN_MASK (0x3 << 25)
#define TXSTS_QTOP_TOKEN_SHIFT 25 #define TXSTS_QTOP_TOKEN_SHIFT 25
#define TXSTS_QTOP_TERMINATE BIT(24) #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_QSPCAVAIL_SHIFT 16
#define TXSTS_FSPCAVAIL_MASK (0xffff << 0) #define TXSTS_FSPCAVAIL_MASK (0xffff << 0)
#define TXSTS_FSPCAVAIL_SHIFT 0 #define TXSTS_FSPCAVAIL_SHIFT 0
......
...@@ -331,7 +331,7 @@ static void dwc2_driver_remove(struct platform_device *dev) ...@@ -331,7 +331,7 @@ static void dwc2_driver_remove(struct platform_device *dev)
/* Exit clock gating when driver is removed. */ /* Exit clock gating when driver is removed. */
if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_NONE && 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)) if (dwc2_is_device_mode(hsotg))
dwc2_gadget_exit_clock_gating(hsotg, 0); dwc2_gadget_exit_clock_gating(hsotg, 0);
else else
......
...@@ -1519,6 +1519,8 @@ static void dwc3_get_properties(struct dwc3 *dwc) ...@@ -1519,6 +1519,8 @@ static void dwc3_get_properties(struct dwc3 *dwc)
else else
dwc->sysdev = dwc->dev; dwc->sysdev = dwc->dev;
dwc->sys_wakeup = device_may_wakeup(dwc->sysdev);
ret = device_property_read_string(dev, "usb-psy-name", &usb_psy_name); ret = device_property_read_string(dev, "usb-psy-name", &usb_psy_name);
if (ret >= 0) { if (ret >= 0) {
dwc->usb_psy = power_supply_get_by_name(usb_psy_name); dwc->usb_psy = power_supply_get_by_name(usb_psy_name);
......
...@@ -1133,6 +1133,7 @@ struct dwc3_scratchpad_array { ...@@ -1133,6 +1133,7 @@ struct dwc3_scratchpad_array {
* 3 - Reserved * 3 - Reserved
* @dis_metastability_quirk: set to disable metastability quirk. * @dis_metastability_quirk: set to disable metastability quirk.
* @dis_split_quirk: set to disable split boundary. * @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. * @wakeup_configured: set if the device is configured for remote wakeup.
* @suspended: set to track suspend event due to U3/L2. * @suspended: set to track suspend event due to U3/L2.
* @imod_interval: set the interrupt moderation interval in 250ns * @imod_interval: set the interrupt moderation interval in 250ns
...@@ -1357,6 +1358,7 @@ struct dwc3 { ...@@ -1357,6 +1358,7 @@ struct dwc3 {
unsigned dis_split_quirk:1; unsigned dis_split_quirk:1;
unsigned async_callbacks:1; unsigned async_callbacks:1;
unsigned sys_wakeup:1;
unsigned wakeup_configured:1; unsigned wakeup_configured:1;
unsigned suspended:1; unsigned suspended:1;
......
...@@ -51,7 +51,6 @@ ...@@ -51,7 +51,6 @@
#define PCI_DEVICE_ID_INTEL_MTLP 0x7ec1 #define PCI_DEVICE_ID_INTEL_MTLP 0x7ec1
#define PCI_DEVICE_ID_INTEL_MTLS 0x7f6f #define PCI_DEVICE_ID_INTEL_MTLS 0x7f6f
#define PCI_DEVICE_ID_INTEL_MTL 0x7e7e #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_ARLH_PCH 0x777e
#define PCI_DEVICE_ID_INTEL_TGL 0x9a15 #define PCI_DEVICE_ID_INTEL_TGL 0x9a15
#define PCI_DEVICE_ID_AMD_MR 0x163a #define PCI_DEVICE_ID_AMD_MR 0x163a
...@@ -423,7 +422,6 @@ static const struct pci_device_id dwc3_pci_id_table[] = { ...@@ -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, MTLP, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, MTL, &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, 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, ARLH_PCH, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, TGL, &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, ...@@ -2955,6 +2955,9 @@ static int dwc3_gadget_start(struct usb_gadget *g,
dwc->gadget_driver = driver; dwc->gadget_driver = driver;
spin_unlock_irqrestore(&dwc->lock, flags); spin_unlock_irqrestore(&dwc->lock, flags);
if (dwc->sys_wakeup)
device_wakeup_enable(dwc->sysdev);
return 0; return 0;
} }
...@@ -2970,6 +2973,9 @@ static int dwc3_gadget_stop(struct usb_gadget *g) ...@@ -2970,6 +2973,9 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
struct dwc3 *dwc = gadget_to_dwc(g); struct dwc3 *dwc = gadget_to_dwc(g);
unsigned long flags; unsigned long flags;
if (dwc->sys_wakeup)
device_wakeup_disable(dwc->sysdev);
spin_lock_irqsave(&dwc->lock, flags); spin_lock_irqsave(&dwc->lock, flags);
dwc->gadget_driver = NULL; dwc->gadget_driver = NULL;
dwc->max_cfg_eps = 0; dwc->max_cfg_eps = 0;
...@@ -4651,6 +4657,10 @@ int dwc3_gadget_init(struct dwc3 *dwc) ...@@ -4651,6 +4657,10 @@ int dwc3_gadget_init(struct dwc3 *dwc)
else else
dwc3_gadget_set_speed(dwc->gadget, dwc->maximum_speed); 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; return 0;
err5: err5:
......
...@@ -173,6 +173,14 @@ int dwc3_host_init(struct dwc3 *dwc) ...@@ -173,6 +173,14 @@ int dwc3_host_init(struct dwc3 *dwc)
goto err; 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; return 0;
err: err:
platform_device_put(xhci); platform_device_put(xhci);
...@@ -181,6 +189,9 @@ int dwc3_host_init(struct dwc3 *dwc) ...@@ -181,6 +189,9 @@ int dwc3_host_init(struct dwc3 *dwc)
void dwc3_host_exit(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); platform_device_unregister(dwc->xhci);
dwc->xhci = NULL; dwc->xhci = NULL;
} }
...@@ -292,7 +292,9 @@ int usb_ep_queue(struct usb_ep *ep, ...@@ -292,7 +292,9 @@ int usb_ep_queue(struct usb_ep *ep,
{ {
int ret = 0; 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; ret = -ESHUTDOWN;
goto out; goto out;
} }
......
...@@ -518,8 +518,10 @@ static int ljca_new_client_device(struct ljca_adapter *adap, u8 type, u8 id, ...@@ -518,8 +518,10 @@ static int ljca_new_client_device(struct ljca_adapter *adap, u8 type, u8 id,
int ret; int ret;
client = kzalloc(sizeof *client, GFP_KERNEL); client = kzalloc(sizeof *client, GFP_KERNEL);
if (!client) if (!client) {
kfree(data);
return -ENOMEM; return -ENOMEM;
}
client->type = type; client->type = type;
client->id = id; client->id = id;
...@@ -535,8 +537,10 @@ static int ljca_new_client_device(struct ljca_adapter *adap, u8 type, u8 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; auxdev->dev.release = ljca_auxdev_release;
ret = auxiliary_device_init(auxdev); ret = auxiliary_device_init(auxdev);
if (ret) if (ret) {
kfree(data);
goto err_free; goto err_free;
}
ljca_auxdev_acpi_bind(adap, auxdev, adr, id); ljca_auxdev_acpi_bind(adap, auxdev, adr, id);
...@@ -590,12 +594,8 @@ static int ljca_enumerate_gpio(struct ljca_adapter *adap) ...@@ -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); valid_pin[i] = get_unaligned_le32(&desc->bank_desc[i].valid_pins);
bitmap_from_arr32(gpio_info->valid_pin_map, valid_pin, gpio_num); 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); gpio_info, LJCA_GPIO_ACPI_ADR);
if (ret)
kfree(gpio_info);
return ret;
} }
static int ljca_enumerate_i2c(struct ljca_adapter *adap) static int ljca_enumerate_i2c(struct ljca_adapter *adap)
...@@ -629,10 +629,8 @@ 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, ret = ljca_new_client_device(adap, LJCA_CLIENT_I2C, i,
"ljca-i2c", i2c_info, "ljca-i2c", i2c_info,
LJCA_I2C1_ACPI_ADR + i); LJCA_I2C1_ACPI_ADR + i);
if (ret) { if (ret)
kfree(i2c_info);
return ret; return ret;
}
} }
return 0; return 0;
...@@ -669,10 +667,8 @@ static int ljca_enumerate_spi(struct ljca_adapter *adap) ...@@ -669,10 +667,8 @@ static int ljca_enumerate_spi(struct ljca_adapter *adap)
ret = ljca_new_client_device(adap, LJCA_CLIENT_SPI, i, ret = ljca_new_client_device(adap, LJCA_CLIENT_SPI, i,
"ljca-spi", spi_info, "ljca-spi", spi_info,
LJCA_SPI1_ACPI_ADR + i); LJCA_SPI1_ACPI_ADR + i);
if (ret) { if (ret)
kfree(spi_info);
return ret; return ret;
}
} }
return 0; return 0;
......
...@@ -262,13 +262,6 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop) ...@@ -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), return dev_err_probe(dev, PTR_ERR(nop->vbus_draw),
"could not get vbus regulator\n"); "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->dev = dev;
nop->phy.dev = nop->dev; nop->phy.dev = nop->dev;
nop->phy.label = "nop-xceiv"; nop->phy.label = "nop-xceiv";
......
...@@ -533,7 +533,7 @@ static struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp, ...@@ -533,7 +533,7 @@ static struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp,
* daft to me. * 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 uas_dev_info *devinfo = cmnd->device->hostdata;
struct urb *urb; struct urb *urb;
...@@ -541,30 +541,28 @@ static struct urb *uas_submit_sense_urb(struct scsi_cmnd *cmnd, gfp_t gfp) ...@@ -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); urb = uas_alloc_sense_urb(devinfo, gfp, cmnd);
if (!urb) if (!urb)
return NULL; return -ENOMEM;
usb_anchor_urb(urb, &devinfo->sense_urbs); usb_anchor_urb(urb, &devinfo->sense_urbs);
err = usb_submit_urb(urb, gfp); err = usb_submit_urb(urb, gfp);
if (err) { if (err) {
usb_unanchor_urb(urb); usb_unanchor_urb(urb);
uas_log_cmd_state(cmnd, "sense submit err", err); uas_log_cmd_state(cmnd, "sense submit err", err);
usb_free_urb(urb); usb_free_urb(urb);
return NULL;
} }
return urb; return err;
} }
static int uas_submit_urbs(struct scsi_cmnd *cmnd, static int uas_submit_urbs(struct scsi_cmnd *cmnd,
struct uas_dev_info *devinfo) struct uas_dev_info *devinfo)
{ {
struct uas_cmd_info *cmdinfo = scsi_cmd_priv(cmnd); struct uas_cmd_info *cmdinfo = scsi_cmd_priv(cmnd);
struct urb *urb;
int err; int err;
lockdep_assert_held(&devinfo->lock); lockdep_assert_held(&devinfo->lock);
if (cmdinfo->state & SUBMIT_STATUS_URB) { if (cmdinfo->state & SUBMIT_STATUS_URB) {
urb = uas_submit_sense_urb(cmnd, GFP_ATOMIC); err = uas_submit_sense_urb(cmnd, GFP_ATOMIC);
if (!urb) if (err)
return SCSI_MLQUEUE_DEVICE_BUSY; return err;
cmdinfo->state &= ~SUBMIT_STATUS_URB; cmdinfo->state &= ~SUBMIT_STATUS_URB;
} }
...@@ -572,7 +570,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, ...@@ -572,7 +570,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
cmdinfo->data_in_urb = uas_alloc_data_urb(devinfo, GFP_ATOMIC, cmdinfo->data_in_urb = uas_alloc_data_urb(devinfo, GFP_ATOMIC,
cmnd, DMA_FROM_DEVICE); cmnd, DMA_FROM_DEVICE);
if (!cmdinfo->data_in_urb) if (!cmdinfo->data_in_urb)
return SCSI_MLQUEUE_DEVICE_BUSY; return -ENOMEM;
cmdinfo->state &= ~ALLOC_DATA_IN_URB; cmdinfo->state &= ~ALLOC_DATA_IN_URB;
} }
...@@ -582,7 +580,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, ...@@ -582,7 +580,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
if (err) { if (err) {
usb_unanchor_urb(cmdinfo->data_in_urb); usb_unanchor_urb(cmdinfo->data_in_urb);
uas_log_cmd_state(cmnd, "data in submit err", err); 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 &= ~SUBMIT_DATA_IN_URB;
cmdinfo->state |= DATA_IN_URB_INFLIGHT; cmdinfo->state |= DATA_IN_URB_INFLIGHT;
...@@ -592,7 +590,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, ...@@ -592,7 +590,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
cmdinfo->data_out_urb = uas_alloc_data_urb(devinfo, GFP_ATOMIC, cmdinfo->data_out_urb = uas_alloc_data_urb(devinfo, GFP_ATOMIC,
cmnd, DMA_TO_DEVICE); cmnd, DMA_TO_DEVICE);
if (!cmdinfo->data_out_urb) if (!cmdinfo->data_out_urb)
return SCSI_MLQUEUE_DEVICE_BUSY; return -ENOMEM;
cmdinfo->state &= ~ALLOC_DATA_OUT_URB; cmdinfo->state &= ~ALLOC_DATA_OUT_URB;
} }
...@@ -602,7 +600,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, ...@@ -602,7 +600,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
if (err) { if (err) {
usb_unanchor_urb(cmdinfo->data_out_urb); usb_unanchor_urb(cmdinfo->data_out_urb);
uas_log_cmd_state(cmnd, "data out submit err", err); 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 &= ~SUBMIT_DATA_OUT_URB;
cmdinfo->state |= DATA_OUT_URB_INFLIGHT; cmdinfo->state |= DATA_OUT_URB_INFLIGHT;
...@@ -611,7 +609,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, ...@@ -611,7 +609,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
if (cmdinfo->state & ALLOC_CMD_URB) { if (cmdinfo->state & ALLOC_CMD_URB) {
cmdinfo->cmd_urb = uas_alloc_cmd_urb(devinfo, GFP_ATOMIC, cmnd); cmdinfo->cmd_urb = uas_alloc_cmd_urb(devinfo, GFP_ATOMIC, cmnd);
if (!cmdinfo->cmd_urb) if (!cmdinfo->cmd_urb)
return SCSI_MLQUEUE_DEVICE_BUSY; return -ENOMEM;
cmdinfo->state &= ~ALLOC_CMD_URB; cmdinfo->state &= ~ALLOC_CMD_URB;
} }
...@@ -621,7 +619,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, ...@@ -621,7 +619,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
if (err) { if (err) {
usb_unanchor_urb(cmdinfo->cmd_urb); usb_unanchor_urb(cmdinfo->cmd_urb);
uas_log_cmd_state(cmnd, "cmd submit err", err); uas_log_cmd_state(cmnd, "cmd submit err", err);
return SCSI_MLQUEUE_DEVICE_BUSY; return err;
} }
cmdinfo->cmd_urb = NULL; cmdinfo->cmd_urb = NULL;
cmdinfo->state &= ~SUBMIT_CMD_URB; cmdinfo->state &= ~SUBMIT_CMD_URB;
...@@ -698,7 +696,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd) ...@@ -698,7 +696,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd)
* of queueing, no matter how fatal the error * of queueing, no matter how fatal the error
*/ */
if (err == -ENODEV) { if (err == -ENODEV) {
set_host_byte(cmnd, DID_ERROR); set_host_byte(cmnd, DID_NO_CONNECT);
scsi_done(cmnd); scsi_done(cmnd);
goto zombie; goto zombie;
} }
......
...@@ -1310,6 +1310,7 @@ static ssize_t select_usb_power_delivery_store(struct device *dev, ...@@ -1310,6 +1310,7 @@ static ssize_t select_usb_power_delivery_store(struct device *dev,
{ {
struct typec_port *port = to_typec_port(dev); struct typec_port *port = to_typec_port(dev);
struct usb_power_delivery *pd; struct usb_power_delivery *pd;
int ret;
if (!port->ops || !port->ops->pd_set) if (!port->ops || !port->ops->pd_set)
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -1318,7 +1319,11 @@ static ssize_t select_usb_power_delivery_store(struct device *dev, ...@@ -1318,7 +1319,11 @@ static ssize_t select_usb_power_delivery_store(struct device *dev,
if (!pd) if (!pd)
return -EINVAL; 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, 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) ...@@ -6861,7 +6861,7 @@ static int tcpm_pd_set(struct typec_port *p, struct usb_power_delivery *pd)
if (data->source_desc.pdo[0]) { if (data->source_desc.pdo[0]) {
for (i = 0; i < PDO_MAX_OBJECTS && data->source_desc.pdo[i]; i++) 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; port->nr_src_pdo = i + 1;
} }
...@@ -6910,7 +6910,9 @@ static int tcpm_pd_set(struct typec_port *p, struct usb_power_delivery *pd) ...@@ -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_source_caps = data->source_cap;
port->port_sink_caps = data->sink_cap; port->port_sink_caps = data->sink_cap;
typec_port_set_usb_power_delivery(p, NULL);
port->selected_pd = pd; port->selected_pd = pd;
typec_port_set_usb_power_delivery(p, port->selected_pd);
unlock: unlock:
mutex_unlock(&port->lock); mutex_unlock(&port->lock);
return ret; return ret;
...@@ -6943,9 +6945,7 @@ static void tcpm_port_unregister_pd(struct tcpm_port *port) ...@@ -6943,9 +6945,7 @@ static void tcpm_port_unregister_pd(struct tcpm_port *port)
port->port_source_caps = NULL; port->port_source_caps = NULL;
for (i = 0; i < port->pd_count; i++) { for (i = 0; i < port->pd_count; i++) {
usb_power_delivery_unregister_capabilities(port->pd_list[i]->sink_cap); 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); 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]); devm_kfree(port->dev, port->pd_list[i]);
port->pd_list[i] = NULL; port->pd_list[i] = NULL;
usb_power_delivery_unregister(port->pds[i]); usb_power_delivery_unregister(port->pds[i]);
......
...@@ -151,8 +151,12 @@ static int ucsi_exec_command(struct ucsi *ucsi, u64 cmd) ...@@ -151,8 +151,12 @@ static int ucsi_exec_command(struct ucsi *ucsi, u64 cmd)
if (!(cci & UCSI_CCI_COMMAND_COMPLETE)) if (!(cci & UCSI_CCI_COMMAND_COMPLETE))
return -EIO; 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; return -EOPNOTSUPP;
}
if (cci & UCSI_CCI_ERROR) { if (cci & UCSI_CCI_ERROR) {
if (cmd == UCSI_GET_ERROR_STATUS) if (cmd == UCSI_GET_ERROR_STATUS)
...@@ -1133,17 +1137,21 @@ static int ucsi_check_cable(struct ucsi_connector *con) ...@@ -1133,17 +1137,21 @@ static int ucsi_check_cable(struct ucsi_connector *con)
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = ucsi_get_cable_identity(con); if (con->ucsi->cap.features & UCSI_CAP_GET_PD_MESSAGE) {
if (ret < 0) ret = ucsi_get_cable_identity(con);
return ret; if (ret < 0)
return ret;
}
ret = ucsi_register_plug(con); if (con->ucsi->cap.features & UCSI_CAP_ALT_MODE_DETAILS) {
if (ret < 0) ret = ucsi_register_plug(con);
return ret; if (ret < 0)
return ret;
ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_SOP_P); ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_SOP_P);
if (ret < 0) if (ret < 0)
return ret; return ret;
}
return 0; return 0;
} }
...@@ -1189,8 +1197,10 @@ static void ucsi_handle_connector_change(struct work_struct *work) ...@@ -1189,8 +1197,10 @@ static void ucsi_handle_connector_change(struct work_struct *work)
ucsi_register_partner(con); ucsi_register_partner(con);
ucsi_partner_task(con, ucsi_check_connection, 1, HZ); ucsi_partner_task(con, ucsi_check_connection, 1, HZ);
ucsi_partner_task(con, ucsi_check_connector_capability, 1, HZ); ucsi_partner_task(con, ucsi_check_connector_capability, 1, HZ);
ucsi_partner_task(con, ucsi_get_partner_identity, 1, HZ); if (con->ucsi->cap.features & UCSI_CAP_GET_PD_MESSAGE)
ucsi_partner_task(con, ucsi_check_cable, 1, HZ); 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) == if (UCSI_CONSTAT_PWR_OPMODE(con->status.flags) ==
UCSI_CONSTAT_PWR_OPMODE_PD) UCSI_CONSTAT_PWR_OPMODE_PD)
...@@ -1215,11 +1225,11 @@ static void ucsi_handle_connector_change(struct work_struct *work) ...@@ -1215,11 +1225,11 @@ static void ucsi_handle_connector_change(struct work_struct *work)
if (con->status.change & UCSI_CONSTAT_CAM_CHANGE) if (con->status.change & UCSI_CONSTAT_CAM_CHANGE)
ucsi_partner_task(con, ucsi_check_altmodes, 1, 0); ucsi_partner_task(con, ucsi_check_altmodes, 1, 0);
clear_bit(EVENT_PENDING, &con->ucsi->flags);
mutex_lock(&ucsi->ppm_lock); mutex_lock(&ucsi->ppm_lock);
clear_bit(EVENT_PENDING, &con->ucsi->flags);
ret = ucsi_acknowledge_connector_change(ucsi); ret = ucsi_acknowledge_connector_change(ucsi);
mutex_unlock(&ucsi->ppm_lock); mutex_unlock(&ucsi->ppm_lock);
if (ret) if (ret)
dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret); dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret);
...@@ -1237,7 +1247,7 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num) ...@@ -1237,7 +1247,7 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num)
struct ucsi_connector *con = &ucsi->connector[num - 1]; struct ucsi_connector *con = &ucsi->connector[num - 1];
if (!(ucsi->ntfy & UCSI_ENABLE_NTFY_CONNECTOR_CHANGE)) { 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; return;
} }
...@@ -1260,13 +1270,47 @@ static int ucsi_reset_connector(struct ucsi_connector *con, bool hard) ...@@ -1260,13 +1270,47 @@ static int ucsi_reset_connector(struct ucsi_connector *con, bool hard)
static int ucsi_reset_ppm(struct ucsi *ucsi) static int ucsi_reset_ppm(struct ucsi *ucsi)
{ {
u64 command = UCSI_PPM_RESET; u64 command;
unsigned long tmo; unsigned long tmo;
u32 cci; u32 cci;
int ret; int ret;
mutex_lock(&ucsi->ppm_lock); 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, ret = ucsi->ops->async_write(ucsi, UCSI_CONTROL, &command,
sizeof(command)); sizeof(command));
if (ret < 0) if (ret < 0)
...@@ -1589,8 +1633,10 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) ...@@ -1589,8 +1633,10 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con)
ucsi_register_partner(con); ucsi_register_partner(con);
ucsi_pwr_opmode_change(con); ucsi_pwr_opmode_change(con);
ucsi_port_psy_changed(con); ucsi_port_psy_changed(con);
ucsi_get_partner_identity(con); if (con->ucsi->cap.features & UCSI_CAP_GET_PD_MESSAGE)
ucsi_check_cable(con); 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 */ /* Only notify USB controller if partner supports USB data */
...@@ -1636,6 +1682,7 @@ static int ucsi_init(struct ucsi *ucsi) ...@@ -1636,6 +1682,7 @@ static int ucsi_init(struct ucsi *ucsi)
{ {
struct ucsi_connector *con, *connector; struct ucsi_connector *con, *connector;
u64 command, ntfy; u64 command, ntfy;
u32 cci;
int ret; int ret;
int i; int i;
...@@ -1688,6 +1735,13 @@ static int ucsi_init(struct ucsi *ucsi) ...@@ -1688,6 +1735,13 @@ static int ucsi_init(struct ucsi *ucsi)
ucsi->connector = connector; ucsi->connector = connector;
ucsi->ntfy = ntfy; 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; return 0;
err_unregister: err_unregister:
......
...@@ -206,7 +206,7 @@ struct ucsi_capability { ...@@ -206,7 +206,7 @@ struct ucsi_capability {
#define UCSI_CAP_ATTR_POWER_OTHER BIT(10) #define UCSI_CAP_ATTR_POWER_OTHER BIT(10)
#define UCSI_CAP_ATTR_POWER_VBUS BIT(14) #define UCSI_CAP_ATTR_POWER_VBUS BIT(14)
u8 num_connectors; u8 num_connectors;
u8 features; u16 features;
#define UCSI_CAP_SET_UOM BIT(0) #define UCSI_CAP_SET_UOM BIT(0)
#define UCSI_CAP_SET_PDM BIT(1) #define UCSI_CAP_SET_PDM BIT(1)
#define UCSI_CAP_ALT_MODE_DETAILS BIT(2) #define UCSI_CAP_ALT_MODE_DETAILS BIT(2)
...@@ -215,7 +215,8 @@ struct ucsi_capability { ...@@ -215,7 +215,8 @@ struct ucsi_capability {
#define UCSI_CAP_CABLE_DETAILS BIT(5) #define UCSI_CAP_CABLE_DETAILS BIT(5)
#define UCSI_CAP_EXT_SUPPLY_NOTIFICATIONS BIT(6) #define UCSI_CAP_EXT_SUPPLY_NOTIFICATIONS BIT(6)
#define UCSI_CAP_PD_RESET BIT(7) #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 num_alt_modes;
u8 reserved_2; u8 reserved_2;
u16 bc_version; u16 bc_version;
......
...@@ -23,10 +23,11 @@ struct ucsi_acpi { ...@@ -23,10 +23,11 @@ struct ucsi_acpi {
void *base; void *base;
struct completion complete; struct completion complete;
unsigned long flags; unsigned long flags;
#define UCSI_ACPI_SUPPRESS_EVENT 0
#define UCSI_ACPI_COMMAND_PENDING 1
#define UCSI_ACPI_ACK_PENDING 2
guid_t guid; guid_t guid;
u64 cmd; u64 cmd;
bool dell_quirk_probed;
bool dell_quirk_active;
}; };
static int ucsi_acpi_dsm(struct ucsi_acpi *ua, int func) 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, ...@@ -79,9 +80,9 @@ static int ucsi_acpi_sync_write(struct ucsi *ucsi, unsigned int offset,
int ret; int ret;
if (ack) if (ack)
set_bit(ACK_PENDING, &ua->flags); set_bit(UCSI_ACPI_ACK_PENDING, &ua->flags);
else 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); ret = ucsi_acpi_async_write(ucsi, offset, val, val_len);
if (ret) if (ret)
...@@ -92,9 +93,9 @@ static int ucsi_acpi_sync_write(struct ucsi *ucsi, unsigned int offset, ...@@ -92,9 +93,9 @@ static int ucsi_acpi_sync_write(struct ucsi *ucsi, unsigned int offset,
out_clear_bit: out_clear_bit:
if (ack) if (ack)
clear_bit(ACK_PENDING, &ua->flags); clear_bit(UCSI_ACPI_ACK_PENDING, &ua->flags);
else else
clear_bit(COMMAND_PENDING, &ua->flags); clear_bit(UCSI_ACPI_COMMAND_PENDING, &ua->flags);
return ret; return ret;
} }
...@@ -129,51 +130,40 @@ static const struct ucsi_operations ucsi_zenbook_ops = { ...@@ -129,51 +130,40 @@ static const struct ucsi_operations ucsi_zenbook_ops = {
}; };
/* /*
* Some Dell laptops expect that an ACK command with the * Some Dell laptops don't like ACK commands with the
* UCSI_ACK_CONNECTOR_CHANGE bit set is followed by a (separate) * UCSI_ACK_CONNECTOR_CHANGE but not the UCSI_ACK_COMMAND_COMPLETE
* ACK command that only has the UCSI_ACK_COMMAND_COMPLETE bit set. * bit set. To work around this send a dummy command and bundle the
* If this is not done events are not delivered to OSPM and * UCSI_ACK_CONNECTOR_CHANGE with the UCSI_ACK_COMMAND_COMPLETE
* subsequent commands will timeout. * for the dummy command.
*/ */
static int static int
ucsi_dell_sync_write(struct ucsi *ucsi, unsigned int offset, ucsi_dell_sync_write(struct ucsi *ucsi, unsigned int offset,
const void *val, size_t val_len) const void *val, size_t val_len)
{ {
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); 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; int ret;
if (UCSI_COMMAND(cmd) == UCSI_ACK_CC_CI && if (cmd == (UCSI_ACK_CC_CI | UCSI_ACK_CONNECTOR_CHANGE)) {
cmd & UCSI_ACK_CONNECTOR_CHANGE) cmd |= UCSI_ACK_COMMAND_COMPLETE;
ack = UCSI_ACK_CC_CI | UCSI_ACK_COMMAND_COMPLETE;
/*
ret = ucsi_acpi_sync_write(ucsi, offset, val, val_len); * The UCSI core thinks it is sending a connector change ack
if (ret != 0) * and will accept new connector change events. We don't want
return ret; * this to happen for the dummy command as its response will
if (ack == 0) * still report the very event that the core is trying to clear.
return ret; */
set_bit(UCSI_ACPI_SUPPRESS_EVENT, &ua->flags);
if (!ua->dell_quirk_probed) { ret = ucsi_acpi_sync_write(ucsi, UCSI_CONTROL, &dummycmd,
ua->dell_quirk_probed = true; sizeof(dummycmd));
clear_bit(UCSI_ACPI_SUPPRESS_EVENT, &ua->flags);
cmd = UCSI_GET_CAPABILITY;
ret = ucsi_acpi_sync_write(ucsi, UCSI_CONTROL, &cmd, if (ret < 0)
sizeof(cmd));
if (ret == 0)
return ucsi_acpi_sync_write(ucsi, UCSI_CONTROL,
&ack, sizeof(ack));
if (ret != -ETIMEDOUT)
return ret; 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 ucsi_acpi_sync_write(ucsi, UCSI_CONTROL, &cmd, sizeof(cmd));
return ret;
return ucsi_acpi_sync_write(ucsi, UCSI_CONTROL, &ack, sizeof(ack));
} }
static const struct ucsi_operations ucsi_dell_ops = { static const struct ucsi_operations ucsi_dell_ops = {
...@@ -209,13 +199,14 @@ static void ucsi_acpi_notify(acpi_handle handle, u32 event, void *data) ...@@ -209,13 +199,14 @@ static void ucsi_acpi_notify(acpi_handle handle, u32 event, void *data)
if (ret) if (ret)
return; 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)); ucsi_connector_change(ua->ucsi, UCSI_CCI_CONNECTOR(cci));
if (cci & UCSI_CCI_ACK_COMPLETE && test_bit(ACK_PENDING, &ua->flags)) if (cci & UCSI_CCI_ACK_COMPLETE && test_bit(ACK_PENDING, &ua->flags))
complete(&ua->complete); complete(&ua->complete);
if (cci & UCSI_CCI_COMMAND_COMPLETE && if (cci & UCSI_CCI_COMMAND_COMPLETE &&
test_bit(COMMAND_PENDING, &ua->flags)) test_bit(UCSI_ACPI_COMMAND_PENDING, &ua->flags))
complete(&ua->complete); complete(&ua->complete);
} }
......
...@@ -255,6 +255,20 @@ static void pmic_glink_ucsi_notify(struct work_struct *work) ...@@ -255,6 +255,20 @@ static void pmic_glink_ucsi_notify(struct work_struct *work)
static void pmic_glink_ucsi_register(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); 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); ucsi_register(ucsi->ucsi);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment