Merge tag 'usb-6.15-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB fixes from Greg KH: "Here are some small USB driver fixes for 6.15-rc6. Included in here are: - typec driver fixes - usbtmc ioctl fixes - xhci driver fixes - cdnsp driver fixes - some gadget driver fixes Nothing really major, just all little stuff that people have reported being issues. All of these have been in linux-next this week with no reported issues" * tag 'usb-6.15-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: xhci: dbc: Avoid event polling busyloop if pending rx transfers are inactive. usb: xhci: Don't trust the EP Context cycle bit when moving HW dequeue usb: usbtmc: Fix erroneous generic_read ioctl return usb: usbtmc: Fix erroneous wait_srq ioctl return usb: usbtmc: Fix erroneous get_stb ioctl error returns usb: typec: tcpm: delay SNK_TRY_WAIT_DEBOUNCE to SRC_TRYWAIT transition USB: usbtmc: use interruptible sleep in usbtmc_read usb: cdnsp: fix L1 resume issue for RTL_REVISION_NEW_LPM version usb: typec: ucsi: displayport: Fix NULL pointer access usb: typec: ucsi: displayport: Fix deadlock usb: misc: onboard_usb_dev: fix support for Cypress HX3 hubs usb: uhci-platform: Make the clock really optional usb: dwc3: gadget: Make gadget_wakeup asynchronous usb: gadget: Use get_status callback to set remote wakeup capability usb: gadget: f_ecm: Add get_status callback usb: host: tegra: Prevent host controller crash when OTG port is used usb: cdnsp: Fix issue with resuming from L1 usb: gadget: tegra-xudc: ACK ST_RC after clearing CTRL_RUN
This commit is contained in:
@@ -139,6 +139,26 @@ static void cdnsp_clear_port_change_bit(struct cdnsp_device *pdev,
|
||||
(portsc & PORT_CHANGE_BITS), port_regs);
|
||||
}
|
||||
|
||||
static void cdnsp_set_apb_timeout_value(struct cdnsp_device *pdev)
|
||||
{
|
||||
struct cdns *cdns = dev_get_drvdata(pdev->dev);
|
||||
__le32 __iomem *reg;
|
||||
void __iomem *base;
|
||||
u32 offset = 0;
|
||||
u32 val;
|
||||
|
||||
if (!cdns->override_apb_timeout)
|
||||
return;
|
||||
|
||||
base = &pdev->cap_regs->hc_capbase;
|
||||
offset = cdnsp_find_next_ext_cap(base, offset, D_XEC_PRE_REGS_CAP);
|
||||
reg = base + offset + REG_CHICKEN_BITS_3_OFFSET;
|
||||
|
||||
val = le32_to_cpu(readl(reg));
|
||||
val = CHICKEN_APB_TIMEOUT_SET(val, cdns->override_apb_timeout);
|
||||
writel(cpu_to_le32(val), reg);
|
||||
}
|
||||
|
||||
static void cdnsp_set_chicken_bits_2(struct cdnsp_device *pdev, u32 bit)
|
||||
{
|
||||
__le32 __iomem *reg;
|
||||
@@ -1773,6 +1793,8 @@ static void cdnsp_get_rev_cap(struct cdnsp_device *pdev)
|
||||
reg += cdnsp_find_next_ext_cap(reg, 0, RTL_REV_CAP);
|
||||
pdev->rev_cap = reg;
|
||||
|
||||
pdev->rtl_revision = readl(&pdev->rev_cap->rtl_revision);
|
||||
|
||||
dev_info(pdev->dev, "Rev: %08x/%08x, eps: %08x, buff: %08x/%08x\n",
|
||||
readl(&pdev->rev_cap->ctrl_revision),
|
||||
readl(&pdev->rev_cap->rtl_revision),
|
||||
@@ -1798,6 +1820,15 @@ static int cdnsp_gen_setup(struct cdnsp_device *pdev)
|
||||
pdev->hci_version = HC_VERSION(pdev->hcc_params);
|
||||
pdev->hcc_params = readl(&pdev->cap_regs->hcc_params);
|
||||
|
||||
/*
|
||||
* Override the APB timeout value to give the controller more time for
|
||||
* enabling UTMI clock and synchronizing APB and UTMI clock domains.
|
||||
* This fix is platform specific and is required to fixes issue with
|
||||
* reading incorrect value from PORTSC register after resuming
|
||||
* from L1 state.
|
||||
*/
|
||||
cdnsp_set_apb_timeout_value(pdev);
|
||||
|
||||
cdnsp_get_rev_cap(pdev);
|
||||
|
||||
/* Make sure the Device Controller is halted. */
|
||||
|
||||
@@ -520,6 +520,9 @@ struct cdnsp_rev_cap {
|
||||
#define REG_CHICKEN_BITS_2_OFFSET 0x48
|
||||
#define CHICKEN_XDMA_2_TP_CACHE_DIS BIT(28)
|
||||
|
||||
#define REG_CHICKEN_BITS_3_OFFSET 0x4C
|
||||
#define CHICKEN_APB_TIMEOUT_SET(p, val) (((p) & ~GENMASK(21, 0)) | (val))
|
||||
|
||||
/* XBUF Extended Capability ID. */
|
||||
#define XBUF_CAP_ID 0xCB
|
||||
#define XBUF_RX_TAG_MASK_0_OFFSET 0x1C
|
||||
@@ -1357,6 +1360,7 @@ struct cdnsp_port {
|
||||
* @rev_cap: Controller Capabilities Registers.
|
||||
* @hcs_params1: Cached register copies of read-only HCSPARAMS1
|
||||
* @hcc_params: Cached register copies of read-only HCCPARAMS1
|
||||
* @rtl_revision: Cached controller rtl revision.
|
||||
* @setup: Temporary buffer for setup packet.
|
||||
* @ep0_preq: Internal allocated request used during enumeration.
|
||||
* @ep0_stage: ep0 stage during enumeration process.
|
||||
@@ -1411,6 +1415,8 @@ struct cdnsp_device {
|
||||
__u32 hcs_params1;
|
||||
__u32 hcs_params3;
|
||||
__u32 hcc_params;
|
||||
#define RTL_REVISION_NEW_LPM 0x2700
|
||||
__u32 rtl_revision;
|
||||
/* Lock used in interrupt thread context. */
|
||||
spinlock_t lock;
|
||||
struct usb_ctrlrequest setup;
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#define PCI_DRIVER_NAME "cdns-pci-usbssp"
|
||||
#define PLAT_DRIVER_NAME "cdns-usbssp"
|
||||
|
||||
#define CHICKEN_APB_TIMEOUT_VALUE 0x1C20
|
||||
|
||||
static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev)
|
||||
{
|
||||
/*
|
||||
@@ -139,6 +141,14 @@ static int cdnsp_pci_probe(struct pci_dev *pdev,
|
||||
cdnsp->otg_irq = pdev->irq;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cadence PCI based platform require some longer timeout for APB
|
||||
* to fixes domain clock synchronization issue after resuming
|
||||
* controller from L1 state.
|
||||
*/
|
||||
cdnsp->override_apb_timeout = CHICKEN_APB_TIMEOUT_VALUE;
|
||||
pci_set_drvdata(pdev, cdnsp);
|
||||
|
||||
if (pci_is_enabled(func)) {
|
||||
cdnsp->dev = dev;
|
||||
cdnsp->gadget_init = cdnsp_gadget_init;
|
||||
@@ -148,8 +158,6 @@ static int cdnsp_pci_probe(struct pci_dev *pdev,
|
||||
goto free_cdnsp;
|
||||
}
|
||||
|
||||
pci_set_drvdata(pdev, cdnsp);
|
||||
|
||||
device_wakeup_enable(&pdev->dev);
|
||||
if (pci_dev_run_wake(pdev))
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
|
||||
@@ -308,7 +308,8 @@ static bool cdnsp_ring_ep_doorbell(struct cdnsp_device *pdev,
|
||||
|
||||
writel(db_value, reg_addr);
|
||||
|
||||
cdnsp_force_l0_go(pdev);
|
||||
if (pdev->rtl_revision < RTL_REVISION_NEW_LPM)
|
||||
cdnsp_force_l0_go(pdev);
|
||||
|
||||
/* Doorbell was set. */
|
||||
return true;
|
||||
|
||||
@@ -79,6 +79,8 @@ struct cdns3_platform_data {
|
||||
* @pdata: platform data from glue layer
|
||||
* @lock: spinlock structure
|
||||
* @xhci_plat_data: xhci private data structure pointer
|
||||
* @override_apb_timeout: hold value of APB timeout. For value 0 the default
|
||||
* value in CHICKEN_BITS_3 will be preserved.
|
||||
* @gadget_init: pointer to gadget initialization function
|
||||
*/
|
||||
struct cdns {
|
||||
@@ -117,6 +119,7 @@ struct cdns {
|
||||
struct cdns3_platform_data *pdata;
|
||||
spinlock_t lock;
|
||||
struct xhci_plat_priv *xhci_plat_data;
|
||||
u32 override_apb_timeout;
|
||||
|
||||
int (*gadget_init)(struct cdns *cdns);
|
||||
};
|
||||
|
||||
@@ -482,6 +482,7 @@ static int usbtmc_get_stb(struct usbtmc_file_data *file_data, __u8 *stb)
|
||||
u8 *buffer;
|
||||
u8 tag;
|
||||
int rv;
|
||||
long wait_rv;
|
||||
|
||||
dev_dbg(dev, "Enter ioctl_read_stb iin_ep_present: %d\n",
|
||||
data->iin_ep_present);
|
||||
@@ -511,16 +512,17 @@ static int usbtmc_get_stb(struct usbtmc_file_data *file_data, __u8 *stb)
|
||||
}
|
||||
|
||||
if (data->iin_ep_present) {
|
||||
rv = wait_event_interruptible_timeout(
|
||||
wait_rv = wait_event_interruptible_timeout(
|
||||
data->waitq,
|
||||
atomic_read(&data->iin_data_valid) != 0,
|
||||
file_data->timeout);
|
||||
if (rv < 0) {
|
||||
dev_dbg(dev, "wait interrupted %d\n", rv);
|
||||
if (wait_rv < 0) {
|
||||
dev_dbg(dev, "wait interrupted %ld\n", wait_rv);
|
||||
rv = wait_rv;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (rv == 0) {
|
||||
if (wait_rv == 0) {
|
||||
dev_dbg(dev, "wait timed out\n");
|
||||
rv = -ETIMEDOUT;
|
||||
goto exit;
|
||||
@@ -539,6 +541,8 @@ static int usbtmc_get_stb(struct usbtmc_file_data *file_data, __u8 *stb)
|
||||
|
||||
dev_dbg(dev, "stb:0x%02x received %d\n", (unsigned int)*stb, rv);
|
||||
|
||||
rv = 0;
|
||||
|
||||
exit:
|
||||
/* bump interrupt bTag */
|
||||
data->iin_bTag += 1;
|
||||
@@ -602,9 +606,9 @@ static int usbtmc488_ioctl_wait_srq(struct usbtmc_file_data *file_data,
|
||||
{
|
||||
struct usbtmc_device_data *data = file_data->data;
|
||||
struct device *dev = &data->intf->dev;
|
||||
int rv;
|
||||
u32 timeout;
|
||||
unsigned long expire;
|
||||
long wait_rv;
|
||||
|
||||
if (!data->iin_ep_present) {
|
||||
dev_dbg(dev, "no interrupt endpoint present\n");
|
||||
@@ -618,25 +622,24 @@ static int usbtmc488_ioctl_wait_srq(struct usbtmc_file_data *file_data,
|
||||
|
||||
mutex_unlock(&data->io_mutex);
|
||||
|
||||
rv = wait_event_interruptible_timeout(
|
||||
data->waitq,
|
||||
atomic_read(&file_data->srq_asserted) != 0 ||
|
||||
atomic_read(&file_data->closing),
|
||||
expire);
|
||||
wait_rv = wait_event_interruptible_timeout(
|
||||
data->waitq,
|
||||
atomic_read(&file_data->srq_asserted) != 0 ||
|
||||
atomic_read(&file_data->closing),
|
||||
expire);
|
||||
|
||||
mutex_lock(&data->io_mutex);
|
||||
|
||||
/* Note! disconnect or close could be called in the meantime */
|
||||
if (atomic_read(&file_data->closing) || data->zombie)
|
||||
rv = -ENODEV;
|
||||
return -ENODEV;
|
||||
|
||||
if (rv < 0) {
|
||||
/* dev can be invalid now! */
|
||||
pr_debug("%s - wait interrupted %d\n", __func__, rv);
|
||||
return rv;
|
||||
if (wait_rv < 0) {
|
||||
dev_dbg(dev, "%s - wait interrupted %ld\n", __func__, wait_rv);
|
||||
return wait_rv;
|
||||
}
|
||||
|
||||
if (rv == 0) {
|
||||
if (wait_rv == 0) {
|
||||
dev_dbg(dev, "%s - wait timed out\n", __func__);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
@@ -830,6 +833,7 @@ static ssize_t usbtmc_generic_read(struct usbtmc_file_data *file_data,
|
||||
unsigned long expire;
|
||||
int bufcount = 1;
|
||||
int again = 0;
|
||||
long wait_rv;
|
||||
|
||||
/* mutex already locked */
|
||||
|
||||
@@ -942,19 +946,24 @@ static ssize_t usbtmc_generic_read(struct usbtmc_file_data *file_data,
|
||||
if (!(flags & USBTMC_FLAG_ASYNC)) {
|
||||
dev_dbg(dev, "%s: before wait time %lu\n",
|
||||
__func__, expire);
|
||||
retval = wait_event_interruptible_timeout(
|
||||
wait_rv = wait_event_interruptible_timeout(
|
||||
file_data->wait_bulk_in,
|
||||
usbtmc_do_transfer(file_data),
|
||||
expire);
|
||||
|
||||
dev_dbg(dev, "%s: wait returned %d\n",
|
||||
__func__, retval);
|
||||
dev_dbg(dev, "%s: wait returned %ld\n",
|
||||
__func__, wait_rv);
|
||||
|
||||
if (retval <= 0) {
|
||||
if (retval == 0)
|
||||
retval = -ETIMEDOUT;
|
||||
if (wait_rv < 0) {
|
||||
retval = wait_rv;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (wait_rv == 0) {
|
||||
retval = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
urb = usb_get_from_anchor(&file_data->in_anchor);
|
||||
@@ -1380,7 +1389,10 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf,
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_lock(&data->io_mutex);
|
||||
retval = mutex_lock_interruptible(&data->io_mutex);
|
||||
if (retval < 0)
|
||||
goto exit_nolock;
|
||||
|
||||
if (data->zombie) {
|
||||
retval = -ENODEV;
|
||||
goto exit;
|
||||
@@ -1503,6 +1515,7 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf,
|
||||
|
||||
exit:
|
||||
mutex_unlock(&data->io_mutex);
|
||||
exit_nolock:
|
||||
kfree(buffer);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -1164,6 +1164,9 @@ struct dwc3_scratchpad_array {
|
||||
* @gsbuscfg0_reqinfo: store GSBUSCFG0.DATRDREQINFO, DESRDREQINFO,
|
||||
* DATWRREQINFO, and DESWRREQINFO value passed from
|
||||
* glue driver.
|
||||
* @wakeup_pending_funcs: Indicates whether any interface has requested for
|
||||
* function wakeup in bitmap format where bit position
|
||||
* represents interface_id.
|
||||
*/
|
||||
struct dwc3 {
|
||||
struct work_struct drd_work;
|
||||
@@ -1394,6 +1397,7 @@ struct dwc3 {
|
||||
int num_ep_resized;
|
||||
struct dentry *debug_root;
|
||||
u32 gsbuscfg0_reqinfo;
|
||||
u32 wakeup_pending_funcs;
|
||||
};
|
||||
|
||||
#define INCRX_BURST_MODE 0
|
||||
|
||||
@@ -276,8 +276,6 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async);
|
||||
|
||||
/**
|
||||
* dwc3_send_gadget_ep_cmd - issue an endpoint command
|
||||
* @dep: the endpoint to which the command is going to be issued
|
||||
@@ -2359,10 +2357,8 @@ static int dwc3_gadget_get_frame(struct usb_gadget *g)
|
||||
return __dwc3_gadget_get_frame(dwc);
|
||||
}
|
||||
|
||||
static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async)
|
||||
static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
|
||||
{
|
||||
int retries;
|
||||
|
||||
int ret;
|
||||
u32 reg;
|
||||
|
||||
@@ -2390,8 +2386,7 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (async)
|
||||
dwc3_gadget_enable_linksts_evts(dwc, true);
|
||||
dwc3_gadget_enable_linksts_evts(dwc, true);
|
||||
|
||||
ret = dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RECOV);
|
||||
if (ret < 0) {
|
||||
@@ -2410,27 +2405,8 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async)
|
||||
|
||||
/*
|
||||
* Since link status change events are enabled we will receive
|
||||
* an U0 event when wakeup is successful. So bail out.
|
||||
* an U0 event when wakeup is successful.
|
||||
*/
|
||||
if (async)
|
||||
return 0;
|
||||
|
||||
/* poll until Link State changes to ON */
|
||||
retries = 20000;
|
||||
|
||||
while (retries--) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||
|
||||
/* in HS, means ON */
|
||||
if (DWC3_DSTS_USBLNKST(reg) == DWC3_LINK_STATE_U0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (DWC3_DSTS_USBLNKST(reg) != DWC3_LINK_STATE_U0) {
|
||||
dev_err(dwc->dev, "failed to send remote wakeup\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2451,7 +2427,7 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = __dwc3_gadget_wakeup(dwc, true);
|
||||
ret = __dwc3_gadget_wakeup(dwc);
|
||||
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
@@ -2479,14 +2455,10 @@ static int dwc3_gadget_func_wakeup(struct usb_gadget *g, int intf_id)
|
||||
*/
|
||||
link_state = dwc3_gadget_get_link_state(dwc);
|
||||
if (link_state == DWC3_LINK_STATE_U3) {
|
||||
ret = __dwc3_gadget_wakeup(dwc, false);
|
||||
if (ret) {
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
dwc3_resume_gadget(dwc);
|
||||
dwc->suspended = false;
|
||||
dwc->link_state = DWC3_LINK_STATE_U0;
|
||||
dwc->wakeup_pending_funcs |= BIT(intf_id);
|
||||
ret = __dwc3_gadget_wakeup(dwc);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = dwc3_send_gadget_generic_command(dwc, DWC3_DGCMD_DEV_NOTIFICATION,
|
||||
@@ -4353,6 +4325,8 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
|
||||
{
|
||||
enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK;
|
||||
unsigned int pwropt;
|
||||
int ret;
|
||||
int intf_id;
|
||||
|
||||
/*
|
||||
* WORKAROUND: DWC3 < 2.50a have an issue when configured without
|
||||
@@ -4428,7 +4402,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
|
||||
|
||||
switch (next) {
|
||||
case DWC3_LINK_STATE_U0:
|
||||
if (dwc->gadget->wakeup_armed) {
|
||||
if (dwc->gadget->wakeup_armed || dwc->wakeup_pending_funcs) {
|
||||
dwc3_gadget_enable_linksts_evts(dwc, false);
|
||||
dwc3_resume_gadget(dwc);
|
||||
dwc->suspended = false;
|
||||
@@ -4451,6 +4425,18 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
|
||||
}
|
||||
|
||||
dwc->link_state = next;
|
||||
|
||||
/* Proceed with func wakeup if any interfaces that has requested */
|
||||
while (dwc->wakeup_pending_funcs && (next == DWC3_LINK_STATE_U0)) {
|
||||
intf_id = ffs(dwc->wakeup_pending_funcs) - 1;
|
||||
ret = dwc3_send_gadget_generic_command(dwc, DWC3_DGCMD_DEV_NOTIFICATION,
|
||||
DWC3_DGCMDPAR_DN_FUNC_WAKE |
|
||||
DWC3_DGCMDPAR_INTF_SEL(intf_id));
|
||||
if (ret)
|
||||
dev_err(dwc->dev, "Failed to send DN wake for intf %d\n", intf_id);
|
||||
|
||||
dwc->wakeup_pending_funcs &= ~BIT(intf_id);
|
||||
}
|
||||
}
|
||||
|
||||
static void dwc3_gadget_suspend_interrupt(struct dwc3 *dwc,
|
||||
|
||||
@@ -2011,15 +2011,13 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
||||
|
||||
if (f->get_status) {
|
||||
status = f->get_status(f);
|
||||
|
||||
if (status < 0)
|
||||
break;
|
||||
} else {
|
||||
/* Set D0 and D1 bits based on func wakeup capability */
|
||||
if (f->config->bmAttributes & USB_CONFIG_ATT_WAKEUP) {
|
||||
status |= USB_INTRF_STAT_FUNC_RW_CAP;
|
||||
if (f->func_wakeup_armed)
|
||||
status |= USB_INTRF_STAT_FUNC_RW;
|
||||
}
|
||||
|
||||
/* if D5 is not set, then device is not wakeup capable */
|
||||
if (!(f->config->bmAttributes & USB_CONFIG_ATT_WAKEUP))
|
||||
status &= ~(USB_INTRF_STAT_FUNC_RW_CAP | USB_INTRF_STAT_FUNC_RW);
|
||||
}
|
||||
|
||||
put_unaligned_le16(status & 0x0000ffff, req->buf);
|
||||
|
||||
@@ -892,6 +892,12 @@ static void ecm_resume(struct usb_function *f)
|
||||
gether_resume(&ecm->port);
|
||||
}
|
||||
|
||||
static int ecm_get_status(struct usb_function *f)
|
||||
{
|
||||
return (f->func_wakeup_armed ? USB_INTRF_STAT_FUNC_RW : 0) |
|
||||
USB_INTRF_STAT_FUNC_RW_CAP;
|
||||
}
|
||||
|
||||
static void ecm_free(struct usb_function *f)
|
||||
{
|
||||
struct f_ecm *ecm;
|
||||
@@ -960,6 +966,7 @@ static struct usb_function *ecm_alloc(struct usb_function_instance *fi)
|
||||
ecm->port.func.disable = ecm_disable;
|
||||
ecm->port.func.free_func = ecm_free;
|
||||
ecm->port.func.suspend = ecm_suspend;
|
||||
ecm->port.func.get_status = ecm_get_status;
|
||||
ecm->port.func.resume = ecm_resume;
|
||||
|
||||
return &ecm->port.func;
|
||||
|
||||
@@ -1749,6 +1749,10 @@ static int __tegra_xudc_ep_disable(struct tegra_xudc_ep *ep)
|
||||
val = xudc_readl(xudc, CTRL);
|
||||
val &= ~CTRL_RUN;
|
||||
xudc_writel(xudc, val, CTRL);
|
||||
|
||||
val = xudc_readl(xudc, ST);
|
||||
if (val & ST_RC)
|
||||
xudc_writel(xudc, ST_RC, ST);
|
||||
}
|
||||
|
||||
dev_info(xudc->dev, "ep %u disabled\n", ep->index);
|
||||
|
||||
@@ -121,7 +121,7 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
/* Get and enable clock if any specified */
|
||||
uhci->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
uhci->clk = devm_clk_get_optional(&pdev->dev, NULL);
|
||||
if (IS_ERR(uhci->clk)) {
|
||||
ret = PTR_ERR(uhci->clk);
|
||||
goto err_rmr;
|
||||
|
||||
@@ -823,6 +823,7 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
|
||||
{
|
||||
dma_addr_t deq;
|
||||
union xhci_trb *evt;
|
||||
enum evtreturn ret = EVT_DONE;
|
||||
u32 ctrl, portsc;
|
||||
bool update_erdp = false;
|
||||
|
||||
@@ -909,6 +910,7 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
|
||||
break;
|
||||
case TRB_TYPE(TRB_TRANSFER):
|
||||
dbc_handle_xfer_event(dbc, evt);
|
||||
ret = EVT_XFER_DONE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -927,7 +929,7 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
|
||||
lo_hi_writeq(deq, &dbc->regs->erdp);
|
||||
}
|
||||
|
||||
return EVT_DONE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void xhci_dbc_handle_events(struct work_struct *work)
|
||||
@@ -936,6 +938,7 @@ static void xhci_dbc_handle_events(struct work_struct *work)
|
||||
struct xhci_dbc *dbc;
|
||||
unsigned long flags;
|
||||
unsigned int poll_interval;
|
||||
unsigned long busypoll_timelimit;
|
||||
|
||||
dbc = container_of(to_delayed_work(work), struct xhci_dbc, event_work);
|
||||
poll_interval = dbc->poll_interval;
|
||||
@@ -954,11 +957,21 @@ static void xhci_dbc_handle_events(struct work_struct *work)
|
||||
dbc->driver->disconnect(dbc);
|
||||
break;
|
||||
case EVT_DONE:
|
||||
/* set fast poll rate if there are pending data transfers */
|
||||
/*
|
||||
* Set fast poll rate if there are pending out transfers, or
|
||||
* a transfer was recently processed
|
||||
*/
|
||||
busypoll_timelimit = dbc->xfer_timestamp +
|
||||
msecs_to_jiffies(DBC_XFER_INACTIVITY_TIMEOUT);
|
||||
|
||||
if (!list_empty(&dbc->eps[BULK_OUT].list_pending) ||
|
||||
!list_empty(&dbc->eps[BULK_IN].list_pending))
|
||||
time_is_after_jiffies(busypoll_timelimit))
|
||||
poll_interval = 0;
|
||||
break;
|
||||
case EVT_XFER_DONE:
|
||||
dbc->xfer_timestamp = jiffies;
|
||||
poll_interval = 0;
|
||||
break;
|
||||
default:
|
||||
dev_info(dbc->dev, "stop handling dbc events\n");
|
||||
return;
|
||||
|
||||
@@ -96,6 +96,7 @@ struct dbc_ep {
|
||||
#define DBC_WRITE_BUF_SIZE 8192
|
||||
#define DBC_POLL_INTERVAL_DEFAULT 64 /* milliseconds */
|
||||
#define DBC_POLL_INTERVAL_MAX 5000 /* milliseconds */
|
||||
#define DBC_XFER_INACTIVITY_TIMEOUT 10 /* milliseconds */
|
||||
/*
|
||||
* Private structure for DbC hardware state:
|
||||
*/
|
||||
@@ -142,6 +143,7 @@ struct xhci_dbc {
|
||||
enum dbc_state state;
|
||||
struct delayed_work event_work;
|
||||
unsigned int poll_interval; /* ms */
|
||||
unsigned long xfer_timestamp;
|
||||
unsigned resume_required:1;
|
||||
struct dbc_ep eps[2];
|
||||
|
||||
@@ -187,6 +189,7 @@ struct dbc_request {
|
||||
enum evtreturn {
|
||||
EVT_ERR = -1,
|
||||
EVT_DONE,
|
||||
EVT_XFER_DONE,
|
||||
EVT_GSER,
|
||||
EVT_DISC,
|
||||
};
|
||||
|
||||
@@ -699,7 +699,7 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
|
||||
int new_cycle;
|
||||
dma_addr_t addr;
|
||||
u64 hw_dequeue;
|
||||
bool cycle_found = false;
|
||||
bool hw_dequeue_found = false;
|
||||
bool td_last_trb_found = false;
|
||||
u32 trb_sct = 0;
|
||||
int ret;
|
||||
@@ -715,25 +715,24 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
|
||||
hw_dequeue = xhci_get_hw_deq(xhci, dev, ep_index, stream_id);
|
||||
new_seg = ep_ring->deq_seg;
|
||||
new_deq = ep_ring->dequeue;
|
||||
new_cycle = hw_dequeue & 0x1;
|
||||
new_cycle = le32_to_cpu(td->end_trb->generic.field[3]) & TRB_CYCLE;
|
||||
|
||||
/*
|
||||
* We want to find the pointer, segment and cycle state of the new trb
|
||||
* (the one after current TD's end_trb). We know the cycle state at
|
||||
* hw_dequeue, so walk the ring until both hw_dequeue and end_trb are
|
||||
* found.
|
||||
* Walk the ring until both the next TRB and hw_dequeue are found (don't
|
||||
* move hw_dequeue back if it went forward due to a HW bug). Cycle state
|
||||
* is loaded from a known good TRB, track later toggles to maintain it.
|
||||
*/
|
||||
do {
|
||||
if (!cycle_found && xhci_trb_virt_to_dma(new_seg, new_deq)
|
||||
if (!hw_dequeue_found && xhci_trb_virt_to_dma(new_seg, new_deq)
|
||||
== (dma_addr_t)(hw_dequeue & ~0xf)) {
|
||||
cycle_found = true;
|
||||
hw_dequeue_found = true;
|
||||
if (td_last_trb_found)
|
||||
break;
|
||||
}
|
||||
if (new_deq == td->end_trb)
|
||||
td_last_trb_found = true;
|
||||
|
||||
if (cycle_found && trb_is_link(new_deq) &&
|
||||
if (td_last_trb_found && trb_is_link(new_deq) &&
|
||||
link_trb_toggles_cycle(new_deq))
|
||||
new_cycle ^= 0x1;
|
||||
|
||||
@@ -745,7 +744,7 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
} while (!cycle_found || !td_last_trb_found);
|
||||
} while (!hw_dequeue_found || !td_last_trb_found);
|
||||
|
||||
/* Don't update the ring cycle state for the producer (us). */
|
||||
addr = xhci_trb_virt_to_dma(new_seg, new_deq);
|
||||
|
||||
@@ -1364,6 +1364,7 @@ static void tegra_xhci_id_work(struct work_struct *work)
|
||||
tegra->otg_usb3_port = tegra_xusb_padctl_get_usb3_companion(tegra->padctl,
|
||||
tegra->otg_usb2_port);
|
||||
|
||||
pm_runtime_get_sync(tegra->dev);
|
||||
if (tegra->host_mode) {
|
||||
/* switch to host mode */
|
||||
if (tegra->otg_usb3_port >= 0) {
|
||||
@@ -1393,6 +1394,7 @@ static void tegra_xhci_id_work(struct work_struct *work)
|
||||
}
|
||||
|
||||
tegra_xhci_set_port_power(tegra, true, true);
|
||||
pm_runtime_mark_last_busy(tegra->dev);
|
||||
|
||||
} else {
|
||||
if (tegra->otg_usb3_port >= 0)
|
||||
@@ -1400,6 +1402,7 @@ static void tegra_xhci_id_work(struct work_struct *work)
|
||||
|
||||
tegra_xhci_set_port_power(tegra, true, false);
|
||||
}
|
||||
pm_runtime_put_autosuspend(tegra->dev);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_PM) || IS_ENABLED(CONFIG_PM_SLEEP)
|
||||
|
||||
@@ -569,8 +569,14 @@ static void onboard_dev_usbdev_disconnect(struct usb_device *udev)
|
||||
}
|
||||
|
||||
static const struct usb_device_id onboard_dev_id_table[] = {
|
||||
{ USB_DEVICE(VENDOR_ID_CYPRESS, 0x6504) }, /* CYUSB33{0,1,2}x/CYUSB230x 3.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_CYPRESS, 0x6506) }, /* CYUSB33{0,1,2}x/CYUSB230x 2.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_CYPRESS, 0x6500) }, /* CYUSB330x 3.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_CYPRESS, 0x6502) }, /* CYUSB330x 2.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_CYPRESS, 0x6503) }, /* CYUSB33{0,1}x 2.0 HUB, Vendor Mode */
|
||||
{ USB_DEVICE(VENDOR_ID_CYPRESS, 0x6504) }, /* CYUSB331x 3.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_CYPRESS, 0x6506) }, /* CYUSB331x 2.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_CYPRESS, 0x6507) }, /* CYUSB332x 2.0 HUB, Vendor Mode */
|
||||
{ USB_DEVICE(VENDOR_ID_CYPRESS, 0x6508) }, /* CYUSB332x 3.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_CYPRESS, 0x650a) }, /* CYUSB332x 2.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_CYPRESS, 0x6570) }, /* CY7C6563x 2.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_GENESYS, 0x0608) }, /* Genesys Logic GL850G USB 2.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_GENESYS, 0x0610) }, /* Genesys Logic GL852G USB 2.0 HUB */
|
||||
|
||||
@@ -5965,7 +5965,7 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1,
|
||||
case SNK_TRY_WAIT_DEBOUNCE:
|
||||
if (!tcpm_port_is_sink(port)) {
|
||||
port->max_wait = 0;
|
||||
tcpm_set_state(port, SRC_TRYWAIT, 0);
|
||||
tcpm_set_state(port, SRC_TRYWAIT, PD_T_PD_DEBOUNCE);
|
||||
}
|
||||
break;
|
||||
case SRC_TRY_WAIT:
|
||||
|
||||
@@ -54,7 +54,8 @@ static int ucsi_displayport_enter(struct typec_altmode *alt, u32 *vdo)
|
||||
u8 cur = 0;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&dp->con->lock);
|
||||
if (!ucsi_con_mutex_lock(dp->con))
|
||||
return -ENOTCONN;
|
||||
|
||||
if (!dp->override && dp->initialized) {
|
||||
const struct typec_altmode *p = typec_altmode_get_partner(alt);
|
||||
@@ -100,7 +101,7 @@ static int ucsi_displayport_enter(struct typec_altmode *alt, u32 *vdo)
|
||||
schedule_work(&dp->work);
|
||||
ret = 0;
|
||||
err_unlock:
|
||||
mutex_unlock(&dp->con->lock);
|
||||
ucsi_con_mutex_unlock(dp->con);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -112,7 +113,8 @@ static int ucsi_displayport_exit(struct typec_altmode *alt)
|
||||
u64 command;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&dp->con->lock);
|
||||
if (!ucsi_con_mutex_lock(dp->con))
|
||||
return -ENOTCONN;
|
||||
|
||||
if (!dp->override) {
|
||||
const struct typec_altmode *p = typec_altmode_get_partner(alt);
|
||||
@@ -144,7 +146,7 @@ static int ucsi_displayport_exit(struct typec_altmode *alt)
|
||||
schedule_work(&dp->work);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&dp->con->lock);
|
||||
ucsi_con_mutex_unlock(dp->con);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -202,20 +204,21 @@ static int ucsi_displayport_vdm(struct typec_altmode *alt,
|
||||
int cmd = PD_VDO_CMD(header);
|
||||
int svdm_version;
|
||||
|
||||
mutex_lock(&dp->con->lock);
|
||||
if (!ucsi_con_mutex_lock(dp->con))
|
||||
return -ENOTCONN;
|
||||
|
||||
if (!dp->override && dp->initialized) {
|
||||
const struct typec_altmode *p = typec_altmode_get_partner(alt);
|
||||
|
||||
dev_warn(&p->dev,
|
||||
"firmware doesn't support alternate mode overriding\n");
|
||||
mutex_unlock(&dp->con->lock);
|
||||
ucsi_con_mutex_unlock(dp->con);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
svdm_version = typec_altmode_get_svdm_version(alt);
|
||||
if (svdm_version < 0) {
|
||||
mutex_unlock(&dp->con->lock);
|
||||
ucsi_con_mutex_unlock(dp->con);
|
||||
return svdm_version;
|
||||
}
|
||||
|
||||
@@ -259,7 +262,7 @@ static int ucsi_displayport_vdm(struct typec_altmode *alt,
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&dp->con->lock);
|
||||
ucsi_con_mutex_unlock(dp->con);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -296,6 +299,8 @@ void ucsi_displayport_remove_partner(struct typec_altmode *alt)
|
||||
if (!dp)
|
||||
return;
|
||||
|
||||
cancel_work_sync(&dp->work);
|
||||
|
||||
dp->data.conf = 0;
|
||||
dp->data.status = 0;
|
||||
dp->initialized = false;
|
||||
|
||||
@@ -1922,6 +1922,40 @@ void ucsi_set_drvdata(struct ucsi *ucsi, void *data)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ucsi_set_drvdata);
|
||||
|
||||
/**
|
||||
* ucsi_con_mutex_lock - Acquire the connector mutex
|
||||
* @con: The connector interface to lock
|
||||
*
|
||||
* Returns true on success, false if the connector is disconnected
|
||||
*/
|
||||
bool ucsi_con_mutex_lock(struct ucsi_connector *con)
|
||||
{
|
||||
bool mutex_locked = false;
|
||||
bool connected = true;
|
||||
|
||||
while (connected && !mutex_locked) {
|
||||
mutex_locked = mutex_trylock(&con->lock) != 0;
|
||||
connected = UCSI_CONSTAT(con, CONNECTED);
|
||||
if (connected && !mutex_locked)
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
connected = connected && con->partner;
|
||||
if (!connected && mutex_locked)
|
||||
mutex_unlock(&con->lock);
|
||||
|
||||
return connected;
|
||||
}
|
||||
|
||||
/**
|
||||
* ucsi_con_mutex_unlock - Release the connector mutex
|
||||
* @con: The connector interface to unlock
|
||||
*/
|
||||
void ucsi_con_mutex_unlock(struct ucsi_connector *con)
|
||||
{
|
||||
mutex_unlock(&con->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* ucsi_create - Allocate UCSI instance
|
||||
* @dev: Device interface to the PPM (Platform Policy Manager)
|
||||
|
||||
@@ -94,6 +94,8 @@ int ucsi_register(struct ucsi *ucsi);
|
||||
void ucsi_unregister(struct ucsi *ucsi);
|
||||
void *ucsi_get_drvdata(struct ucsi *ucsi);
|
||||
void ucsi_set_drvdata(struct ucsi *ucsi, void *data);
|
||||
bool ucsi_con_mutex_lock(struct ucsi_connector *con);
|
||||
void ucsi_con_mutex_unlock(struct ucsi_connector *con);
|
||||
|
||||
void ucsi_connector_change(struct ucsi *ucsi, u8 num);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user