USB fixes for 5.13-rc2
Here are some small USB fixes for 5.13-rc2. They consist of a number of resolutions for reported issues: - typec fixes for found problems - xhci fixes and quirk additions - dwc3 driver fixes - minor fixes found by Coverity - cdc-wdm fixes for reported problems All of these have been in linux-next for a few days with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCYKDexQ8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+yk72gCffEE5ZfO64iFqARPx9Mim04YaSDUAnRJM1mBk dsRT2X0yddqdKek6fN+g =C9x7 -----END PGP SIGNATURE----- Merge tag 'usb-5.13-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb Pull USB fixes from Greg KH: "Here are some small USB fixes for 5.13-rc2. They consist of a number of resolutions for reported issues: - typec fixes for found problems - xhci fixes and quirk additions - dwc3 driver fixes - minor fixes found by Coverity - cdc-wdm fixes for reported problems All of these have been in linux-next for a few days with no reported issues" * tag 'usb-5.13-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (28 commits) usb: core: hub: fix race condition about TRSMRCY of resume usb: typec: tcpm: Fix SINK_DISCOVERY current limit for Rp-default xhci: Add reset resume quirk for AMD xhci controller. usb: xhci: Increase timeout for HC halt xhci: Do not use GFP_KERNEL in (potentially) atomic context xhci: Fix giving back cancelled URBs even if halted endpoint can't reset xhci-pci: Allow host runtime PM as default for Intel Alder Lake xHCI usb: musb: Fix an error message usb: typec: tcpm: Fix wrong handling for Not_Supported in VDM AMS usb: typec: tcpm: Send DISCOVER_IDENTITY from dedicated work usb: typec: ucsi: Retrieve all the PDOs instead of just the first 4 usb: fotg210-hcd: Fix an error message docs: usb: function: Modify path name usb: dwc3: omap: improve extcon initialization usb: typec: ucsi: Put fwnode in any case during ->probe() usb: typec: tcpm: Fix wrong handling in GET_SINK_CAP usb: dwc2: Remove obsolete MODULE_ constants from platform.c usb: dwc3: imx8mp: fix error return code in dwc3_imx8mp_probe() usb: dwc3: imx8mp: detect dwc3 core node via compatible string usb: dwc3: gadget: Return success always for kick transfer in ep queue ...
This commit is contained in:
Коммит
4a668429e0
|
@ -109,16 +109,19 @@ well as to make sure they aren't relying on some HCD-specific behavior.
|
|||
USB-Standard Types
|
||||
==================
|
||||
|
||||
In ``drivers/usb/common/common.c`` and ``drivers/usb/common/debug.c`` you
|
||||
will find the USB data types defined in chapter 9 of the USB specification.
|
||||
These data types are used throughout USB, and in APIs including this host
|
||||
side API, gadget APIs, usb character devices and debugfs interfaces.
|
||||
In ``include/uapi/linux/usb/ch9.h`` you will find the USB data types defined
|
||||
in chapter 9 of the USB specification. These data types are used throughout
|
||||
USB, and in APIs including this host side API, gadget APIs, usb character
|
||||
devices and debugfs interfaces. That file is itself included by
|
||||
``include/linux/usb/ch9.h``, which also contains declarations of a few
|
||||
utility routines for manipulating these data types; the implementations
|
||||
are in ``drivers/usb/common/common.c``.
|
||||
|
||||
.. kernel-doc:: drivers/usb/common/common.c
|
||||
:export:
|
||||
|
||||
.. kernel-doc:: drivers/usb/common/debug.c
|
||||
:export:
|
||||
In addition, some functions useful for creating debugging output are
|
||||
defined in ``drivers/usb/common/debug.c``.
|
||||
|
||||
Host-Side Data Types and Macros
|
||||
===============================
|
||||
|
|
|
@ -140,7 +140,7 @@ is an arbitrary string allowed in a filesystem, e.g.::
|
|||
Each function provides its specific set of attributes, with either read-only
|
||||
or read-write access. Where applicable they need to be written to as
|
||||
appropriate.
|
||||
Please refer to Documentation/ABI/*/configfs-usb-gadget* for more information.
|
||||
Please refer to Documentation/ABI/testing/configfs-usb-gadget for more information.
|
||||
|
||||
4. Associating the functions with their configurations
|
||||
------------------------------------------------------
|
||||
|
|
|
@ -321,12 +321,23 @@ exit:
|
|||
|
||||
}
|
||||
|
||||
static void kill_urbs(struct wdm_device *desc)
|
||||
static void poison_urbs(struct wdm_device *desc)
|
||||
{
|
||||
/* the order here is essential */
|
||||
usb_kill_urb(desc->command);
|
||||
usb_kill_urb(desc->validity);
|
||||
usb_kill_urb(desc->response);
|
||||
usb_poison_urb(desc->command);
|
||||
usb_poison_urb(desc->validity);
|
||||
usb_poison_urb(desc->response);
|
||||
}
|
||||
|
||||
static void unpoison_urbs(struct wdm_device *desc)
|
||||
{
|
||||
/*
|
||||
* the order here is not essential
|
||||
* it is symmetrical just to be nice
|
||||
*/
|
||||
usb_unpoison_urb(desc->response);
|
||||
usb_unpoison_urb(desc->validity);
|
||||
usb_unpoison_urb(desc->command);
|
||||
}
|
||||
|
||||
static void free_urbs(struct wdm_device *desc)
|
||||
|
@ -741,11 +752,12 @@ static int wdm_release(struct inode *inode, struct file *file)
|
|||
if (!desc->count) {
|
||||
if (!test_bit(WDM_DISCONNECTING, &desc->flags)) {
|
||||
dev_dbg(&desc->intf->dev, "wdm_release: cleanup\n");
|
||||
kill_urbs(desc);
|
||||
poison_urbs(desc);
|
||||
spin_lock_irq(&desc->iuspin);
|
||||
desc->resp_count = 0;
|
||||
spin_unlock_irq(&desc->iuspin);
|
||||
desc->manage_power(desc->intf, 0);
|
||||
unpoison_urbs(desc);
|
||||
} else {
|
||||
/* must avoid dev_printk here as desc->intf is invalid */
|
||||
pr_debug(KBUILD_MODNAME " %s: device gone - cleaning up\n", __func__);
|
||||
|
@ -1037,9 +1049,9 @@ static void wdm_disconnect(struct usb_interface *intf)
|
|||
wake_up_all(&desc->wait);
|
||||
mutex_lock(&desc->rlock);
|
||||
mutex_lock(&desc->wlock);
|
||||
poison_urbs(desc);
|
||||
cancel_work_sync(&desc->rxwork);
|
||||
cancel_work_sync(&desc->service_outs_intr);
|
||||
kill_urbs(desc);
|
||||
mutex_unlock(&desc->wlock);
|
||||
mutex_unlock(&desc->rlock);
|
||||
|
||||
|
@ -1080,9 +1092,10 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
|
|||
set_bit(WDM_SUSPENDING, &desc->flags);
|
||||
spin_unlock_irq(&desc->iuspin);
|
||||
/* callback submits work - order is essential */
|
||||
kill_urbs(desc);
|
||||
poison_urbs(desc);
|
||||
cancel_work_sync(&desc->rxwork);
|
||||
cancel_work_sync(&desc->service_outs_intr);
|
||||
unpoison_urbs(desc);
|
||||
}
|
||||
if (!PMSG_IS_AUTO(message)) {
|
||||
mutex_unlock(&desc->wlock);
|
||||
|
@ -1140,7 +1153,7 @@ static int wdm_pre_reset(struct usb_interface *intf)
|
|||
wake_up_all(&desc->wait);
|
||||
mutex_lock(&desc->rlock);
|
||||
mutex_lock(&desc->wlock);
|
||||
kill_urbs(desc);
|
||||
poison_urbs(desc);
|
||||
cancel_work_sync(&desc->rxwork);
|
||||
cancel_work_sync(&desc->service_outs_intr);
|
||||
return 0;
|
||||
|
@ -1151,6 +1164,7 @@ static int wdm_post_reset(struct usb_interface *intf)
|
|||
struct wdm_device *desc = wdm_find_device(intf);
|
||||
int rv;
|
||||
|
||||
unpoison_urbs(desc);
|
||||
clear_bit(WDM_OVERFLOW, &desc->flags);
|
||||
clear_bit(WDM_RESETTING, &desc->flags);
|
||||
rv = recover_from_urb_loss(desc);
|
||||
|
|
|
@ -3642,9 +3642,6 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
|
|||
* sequence.
|
||||
*/
|
||||
status = hub_port_status(hub, port1, &portstatus, &portchange);
|
||||
|
||||
/* TRSMRCY = 10 msec */
|
||||
msleep(10);
|
||||
}
|
||||
|
||||
SuspendCleared:
|
||||
|
@ -3659,6 +3656,9 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
|
|||
usb_clear_port_feature(hub->hdev, port1,
|
||||
USB_PORT_FEAT_C_SUSPEND);
|
||||
}
|
||||
|
||||
/* TRSMRCY = 10 msec */
|
||||
msleep(10);
|
||||
}
|
||||
|
||||
if (udev->persist_enabled)
|
||||
|
|
|
@ -113,6 +113,7 @@ struct dwc2_hsotg_req;
|
|||
* @debugfs: File entry for debugfs file for this endpoint.
|
||||
* @dir_in: Set to true if this endpoint is of the IN direction, which
|
||||
* means that it is sending data to the Host.
|
||||
* @map_dir: Set to the value of dir_in when the DMA buffer is mapped.
|
||||
* @index: The index for the endpoint registers.
|
||||
* @mc: Multi Count - number of transactions per microframe
|
||||
* @interval: Interval for periodic endpoints, in frames or microframes.
|
||||
|
@ -162,6 +163,7 @@ struct dwc2_hsotg_ep {
|
|||
unsigned short fifo_index;
|
||||
|
||||
unsigned char dir_in;
|
||||
unsigned char map_dir;
|
||||
unsigned char index;
|
||||
unsigned char mc;
|
||||
u16 interval;
|
||||
|
|
|
@ -422,7 +422,7 @@ static void dwc2_hsotg_unmap_dma(struct dwc2_hsotg *hsotg,
|
|||
{
|
||||
struct usb_request *req = &hs_req->req;
|
||||
|
||||
usb_gadget_unmap_request(&hsotg->gadget, req, hs_ep->dir_in);
|
||||
usb_gadget_unmap_request(&hsotg->gadget, req, hs_ep->map_dir);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1242,6 +1242,7 @@ static int dwc2_hsotg_map_dma(struct dwc2_hsotg *hsotg,
|
|||
{
|
||||
int ret;
|
||||
|
||||
hs_ep->map_dir = hs_ep->dir_in;
|
||||
ret = usb_gadget_map_request(&hsotg->gadget, req, hs_ep->dir_in);
|
||||
if (ret)
|
||||
goto dma_error;
|
||||
|
|
|
@ -776,7 +776,3 @@ static struct platform_driver dwc2_platform_driver = {
|
|||
};
|
||||
|
||||
module_platform_driver(dwc2_platform_driver);
|
||||
|
||||
MODULE_DESCRIPTION("DESIGNWARE HS OTG Platform Glue");
|
||||
MODULE_AUTHOR("Matthijs Kooijman <matthijs@stdin.nl>");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
#define DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE 3
|
||||
#define DWC3_DEVICE_EVENT_WAKEUP 4
|
||||
#define DWC3_DEVICE_EVENT_HIBER_REQ 5
|
||||
#define DWC3_DEVICE_EVENT_EOPF 6
|
||||
#define DWC3_DEVICE_EVENT_SUSPEND 6
|
||||
#define DWC3_DEVICE_EVENT_SOF 7
|
||||
#define DWC3_DEVICE_EVENT_ERRATIC_ERROR 9
|
||||
#define DWC3_DEVICE_EVENT_CMD_CMPL 10
|
||||
|
@ -460,7 +460,7 @@
|
|||
#define DWC3_DEVTEN_CMDCMPLTEN BIT(10)
|
||||
#define DWC3_DEVTEN_ERRTICERREN BIT(9)
|
||||
#define DWC3_DEVTEN_SOFEN BIT(7)
|
||||
#define DWC3_DEVTEN_EOPFEN BIT(6)
|
||||
#define DWC3_DEVTEN_U3L2L1SUSPEN BIT(6)
|
||||
#define DWC3_DEVTEN_HIBERNATIONREQEVTEN BIT(5)
|
||||
#define DWC3_DEVTEN_WKUPEVTEN BIT(4)
|
||||
#define DWC3_DEVTEN_ULSTCNGEN BIT(3)
|
||||
|
@ -850,6 +850,7 @@ struct dwc3_trb {
|
|||
* @hwparams6: GHWPARAMS6
|
||||
* @hwparams7: GHWPARAMS7
|
||||
* @hwparams8: GHWPARAMS8
|
||||
* @hwparams9: GHWPARAMS9
|
||||
*/
|
||||
struct dwc3_hwparams {
|
||||
u32 hwparams0;
|
||||
|
@ -1374,7 +1375,7 @@ struct dwc3_event_depevt {
|
|||
* 3 - ULStChng
|
||||
* 4 - WkUpEvt
|
||||
* 5 - Reserved
|
||||
* 6 - EOPF
|
||||
* 6 - Suspend (EOPF on revisions 2.10a and prior)
|
||||
* 7 - SOF
|
||||
* 8 - Reserved
|
||||
* 9 - ErrticErr
|
||||
|
|
|
@ -221,8 +221,8 @@ static inline const char *dwc3_gadget_event_string(char *str, size_t size,
|
|||
snprintf(str, size, "WakeUp [%s]",
|
||||
dwc3_gadget_link_string(state));
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_EOPF:
|
||||
snprintf(str, size, "End-Of-Frame [%s]",
|
||||
case DWC3_DEVICE_EVENT_SUSPEND:
|
||||
snprintf(str, size, "Suspend [%s]",
|
||||
dwc3_gadget_link_string(state));
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_SOF:
|
||||
|
@ -353,8 +353,8 @@ static inline const char *dwc3_gadget_event_type_string(u8 event)
|
|||
return "Wake-Up";
|
||||
case DWC3_DEVICE_EVENT_HIBER_REQ:
|
||||
return "Hibernation";
|
||||
case DWC3_DEVICE_EVENT_EOPF:
|
||||
return "End of Periodic Frame";
|
||||
case DWC3_DEVICE_EVENT_SUSPEND:
|
||||
return "Suspend";
|
||||
case DWC3_DEVICE_EVENT_SOF:
|
||||
return "Start of Frame";
|
||||
case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
|
||||
|
|
|
@ -165,8 +165,9 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
|
|||
if (err < 0)
|
||||
goto disable_rpm;
|
||||
|
||||
dwc3_np = of_get_child_by_name(node, "dwc3");
|
||||
dwc3_np = of_get_compatible_child(node, "snps,dwc3");
|
||||
if (!dwc3_np) {
|
||||
err = -ENODEV;
|
||||
dev_err(dev, "failed to find dwc3 core child\n");
|
||||
goto disable_rpm;
|
||||
}
|
||||
|
|
|
@ -437,8 +437,13 @@ static int dwc3_omap_extcon_register(struct dwc3_omap *omap)
|
|||
|
||||
if (extcon_get_state(edev, EXTCON_USB) == true)
|
||||
dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_VALID);
|
||||
else
|
||||
dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_OFF);
|
||||
|
||||
if (extcon_get_state(edev, EXTCON_USB_HOST) == true)
|
||||
dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_GROUND);
|
||||
else
|
||||
dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_FLOAT);
|
||||
|
||||
omap->edev = edev;
|
||||
}
|
||||
|
|
|
@ -123,6 +123,7 @@ static const struct property_entry dwc3_pci_mrfld_properties[] = {
|
|||
PROPERTY_ENTRY_STRING("linux,extcon-name", "mrfld_bcove_pwrsrc"),
|
||||
PROPERTY_ENTRY_BOOL("snps,dis_u3_susphy_quirk"),
|
||||
PROPERTY_ENTRY_BOOL("snps,dis_u2_susphy_quirk"),
|
||||
PROPERTY_ENTRY_BOOL("snps,usb2-gadget-lpm-disable"),
|
||||
PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"),
|
||||
{}
|
||||
};
|
||||
|
|
|
@ -1684,7 +1684,9 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
|||
}
|
||||
}
|
||||
|
||||
return __dwc3_gadget_kick_transfer(dep);
|
||||
__dwc3_gadget_kick_transfer(dep);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
|
||||
|
@ -2323,6 +2325,10 @@ static void dwc3_gadget_enable_irq(struct dwc3 *dwc)
|
|||
if (DWC3_VER_IS_PRIOR(DWC3, 250A))
|
||||
reg |= DWC3_DEVTEN_ULSTCNGEN;
|
||||
|
||||
/* On 2.30a and above this bit enables U3/L2-L1 Suspend Events */
|
||||
if (!DWC3_VER_IS_PRIOR(DWC3, 230A))
|
||||
reg |= DWC3_DEVTEN_U3L2L1SUSPEN;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
|
||||
}
|
||||
|
||||
|
@ -3740,7 +3746,7 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
|
|||
case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
|
||||
dwc3_gadget_linksts_change_interrupt(dwc, event->event_info);
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_EOPF:
|
||||
case DWC3_DEVICE_EVENT_SUSPEND:
|
||||
/* It changed to be suspend event for version 2.30a and above */
|
||||
if (!DWC3_VER_IS_PRIOR(DWC3, 230A)) {
|
||||
/*
|
||||
|
@ -4058,8 +4064,9 @@ err0:
|
|||
|
||||
void dwc3_gadget_exit(struct dwc3 *dwc)
|
||||
{
|
||||
usb_del_gadget_udc(dwc->gadget);
|
||||
usb_del_gadget(dwc->gadget);
|
||||
dwc3_gadget_free_endpoints(dwc);
|
||||
usb_put_gadget(dwc->gadget);
|
||||
dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
|
||||
dwc->bounce_addr);
|
||||
kfree(dwc->setup_buf);
|
||||
|
|
|
@ -5568,7 +5568,7 @@ static int fotg210_hcd_probe(struct platform_device *pdev)
|
|||
struct usb_hcd *hcd;
|
||||
struct resource *res;
|
||||
int irq;
|
||||
int retval = -ENODEV;
|
||||
int retval;
|
||||
struct fotg210_hcd *fotg210;
|
||||
|
||||
if (usb_disabled())
|
||||
|
@ -5588,7 +5588,7 @@ static int fotg210_hcd_probe(struct platform_device *pdev)
|
|||
hcd = usb_create_hcd(&fotg210_fotg210_hc_driver, dev,
|
||||
dev_name(dev));
|
||||
if (!hcd) {
|
||||
dev_err(dev, "failed to create hcd with err %d\n", retval);
|
||||
dev_err(dev, "failed to create hcd\n");
|
||||
retval = -ENOMEM;
|
||||
goto fail_create_hcd;
|
||||
}
|
||||
|
|
|
@ -7,8 +7,9 @@
|
|||
* Author: Sarah Sharp
|
||||
* Some code borrowed from the Linux EHCI driver.
|
||||
*/
|
||||
/* Up to 16 ms to halt an HC */
|
||||
#define XHCI_MAX_HALT_USEC (16*1000)
|
||||
|
||||
/* HC should halt within 16 ms, but use 32 ms as some hosts take longer */
|
||||
#define XHCI_MAX_HALT_USEC (32 * 1000)
|
||||
/* HC not running - set to 1 when run/stop bit is cleared. */
|
||||
#define XHCI_STS_HALT (1<<0)
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
#define PCI_DEVICE_ID_INTEL_CML_XHCI 0xa3af
|
||||
#define PCI_DEVICE_ID_INTEL_TIGER_LAKE_XHCI 0x9a13
|
||||
#define PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_XHCI 0x1138
|
||||
#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_XHCI 0x461e
|
||||
|
||||
#define PCI_DEVICE_ID_AMD_PROMONTORYA_4 0x43b9
|
||||
#define PCI_DEVICE_ID_AMD_PROMONTORYA_3 0x43ba
|
||||
|
@ -166,8 +167,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
|
|||
(pdev->device == 0x15e0 || pdev->device == 0x15e1))
|
||||
xhci->quirks |= XHCI_SNPS_BROKEN_SUSPEND;
|
||||
|
||||
if (pdev->vendor == PCI_VENDOR_ID_AMD && pdev->device == 0x15e5)
|
||||
if (pdev->vendor == PCI_VENDOR_ID_AMD && pdev->device == 0x15e5) {
|
||||
xhci->quirks |= XHCI_DISABLE_SPARSE;
|
||||
xhci->quirks |= XHCI_RESET_ON_RESUME;
|
||||
}
|
||||
|
||||
if (pdev->vendor == PCI_VENDOR_ID_AMD)
|
||||
xhci->quirks |= XHCI_TRUST_TX_LENGTH;
|
||||
|
@ -243,7 +246,8 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
|
|||
pdev->device == PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_XHCI ||
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_ICE_LAKE_XHCI ||
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_TIGER_LAKE_XHCI ||
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_XHCI))
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_XHCI ||
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_ALDER_LAKE_XHCI))
|
||||
xhci->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW;
|
||||
|
||||
if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
|
||||
|
|
|
@ -862,7 +862,7 @@ done:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void xhci_handle_halted_endpoint(struct xhci_hcd *xhci,
|
||||
static int xhci_handle_halted_endpoint(struct xhci_hcd *xhci,
|
||||
struct xhci_virt_ep *ep, unsigned int stream_id,
|
||||
struct xhci_td *td,
|
||||
enum xhci_ep_reset_type reset_type)
|
||||
|
@ -875,7 +875,7 @@ static void xhci_handle_halted_endpoint(struct xhci_hcd *xhci,
|
|||
* Device will be reset soon to recover the link so don't do anything
|
||||
*/
|
||||
if (ep->vdev->flags & VDEV_PORT_ERROR)
|
||||
return;
|
||||
return -ENODEV;
|
||||
|
||||
/* add td to cancelled list and let reset ep handler take care of it */
|
||||
if (reset_type == EP_HARD_RESET) {
|
||||
|
@ -888,16 +888,18 @@ static void xhci_handle_halted_endpoint(struct xhci_hcd *xhci,
|
|||
|
||||
if (ep->ep_state & EP_HALTED) {
|
||||
xhci_dbg(xhci, "Reset ep command already pending\n");
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = xhci_reset_halted_ep(xhci, slot_id, ep->ep_index, reset_type);
|
||||
if (err)
|
||||
return;
|
||||
return err;
|
||||
|
||||
ep->ep_state |= EP_HALTED;
|
||||
|
||||
xhci_ring_cmd_db(xhci);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1014,6 +1016,7 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
|
|||
struct xhci_td *td = NULL;
|
||||
enum xhci_ep_reset_type reset_type;
|
||||
struct xhci_command *command;
|
||||
int err;
|
||||
|
||||
if (unlikely(TRB_TO_SUSPEND_PORT(le32_to_cpu(trb->generic.field[3])))) {
|
||||
if (!xhci->devs[slot_id])
|
||||
|
@ -1058,7 +1061,10 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
|
|||
td->status = -EPROTO;
|
||||
}
|
||||
/* reset ep, reset handler cleans up cancelled tds */
|
||||
xhci_handle_halted_endpoint(xhci, ep, 0, td, reset_type);
|
||||
err = xhci_handle_halted_endpoint(xhci, ep, 0, td,
|
||||
reset_type);
|
||||
if (err)
|
||||
break;
|
||||
xhci_stop_watchdog_timer_in_irq(xhci, ep);
|
||||
return;
|
||||
case EP_STATE_RUNNING:
|
||||
|
|
|
@ -1514,7 +1514,7 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
|
|||
* we need to issue an evaluate context command and wait on it.
|
||||
*/
|
||||
static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id,
|
||||
unsigned int ep_index, struct urb *urb)
|
||||
unsigned int ep_index, struct urb *urb, gfp_t mem_flags)
|
||||
{
|
||||
struct xhci_container_ctx *out_ctx;
|
||||
struct xhci_input_control_ctx *ctrl_ctx;
|
||||
|
@ -1545,7 +1545,7 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id,
|
|||
* changes max packet sizes.
|
||||
*/
|
||||
|
||||
command = xhci_alloc_command(xhci, true, GFP_KERNEL);
|
||||
command = xhci_alloc_command(xhci, true, mem_flags);
|
||||
if (!command)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -1639,7 +1639,7 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
|
|||
*/
|
||||
if (urb->dev->speed == USB_SPEED_FULL) {
|
||||
ret = xhci_check_maxpacket(xhci, slot_id,
|
||||
ep_index, urb);
|
||||
ep_index, urb, mem_flags);
|
||||
if (ret < 0) {
|
||||
xhci_urb_free_priv(urb_priv);
|
||||
urb->hcpriv = NULL;
|
||||
|
|
|
@ -518,8 +518,8 @@ static int mtk_musb_probe(struct platform_device *pdev)
|
|||
|
||||
glue->xceiv = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
|
||||
if (IS_ERR(glue->xceiv)) {
|
||||
dev_err(dev, "fail to getting usb-phy %d\n", ret);
|
||||
ret = PTR_ERR(glue->xceiv);
|
||||
dev_err(dev, "fail to getting usb-phy %d\n", ret);
|
||||
goto err_unregister_usb_phy;
|
||||
}
|
||||
|
||||
|
|
|
@ -259,6 +259,7 @@ enum frs_typec_current {
|
|||
#define ALTMODE_DISCOVERY_MAX (SVID_DISCOVERY_MAX * MODE_DISCOVERY_MAX)
|
||||
|
||||
#define GET_SINK_CAP_RETRY_MS 100
|
||||
#define SEND_DISCOVER_RETRY_MS 100
|
||||
|
||||
struct pd_mode_data {
|
||||
int svid_index; /* current SVID index */
|
||||
|
@ -366,6 +367,8 @@ struct tcpm_port {
|
|||
struct kthread_work vdm_state_machine;
|
||||
struct hrtimer enable_frs_timer;
|
||||
struct kthread_work enable_frs;
|
||||
struct hrtimer send_discover_timer;
|
||||
struct kthread_work send_discover_work;
|
||||
bool state_machine_running;
|
||||
bool vdm_sm_running;
|
||||
|
||||
|
@ -1178,6 +1181,16 @@ static void mod_enable_frs_delayed_work(struct tcpm_port *port, unsigned int del
|
|||
}
|
||||
}
|
||||
|
||||
static void mod_send_discover_delayed_work(struct tcpm_port *port, unsigned int delay_ms)
|
||||
{
|
||||
if (delay_ms) {
|
||||
hrtimer_start(&port->send_discover_timer, ms_to_ktime(delay_ms), HRTIMER_MODE_REL);
|
||||
} else {
|
||||
hrtimer_cancel(&port->send_discover_timer);
|
||||
kthread_queue_work(port->wq, &port->send_discover_work);
|
||||
}
|
||||
}
|
||||
|
||||
static void tcpm_set_state(struct tcpm_port *port, enum tcpm_state state,
|
||||
unsigned int delay_ms)
|
||||
{
|
||||
|
@ -1855,6 +1868,9 @@ static void vdm_run_state_machine(struct tcpm_port *port)
|
|||
res = tcpm_ams_start(port, DISCOVER_IDENTITY);
|
||||
if (res == 0)
|
||||
port->send_discover = false;
|
||||
else if (res == -EAGAIN)
|
||||
mod_send_discover_delayed_work(port,
|
||||
SEND_DISCOVER_RETRY_MS);
|
||||
break;
|
||||
case CMD_DISCOVER_SVID:
|
||||
res = tcpm_ams_start(port, DISCOVER_SVIDS);
|
||||
|
@ -1880,7 +1896,7 @@ static void vdm_run_state_machine(struct tcpm_port *port)
|
|||
}
|
||||
|
||||
if (res < 0) {
|
||||
port->vdm_sm_running = false;
|
||||
port->vdm_state = VDM_STATE_ERR_BUSY;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1896,6 +1912,7 @@ static void vdm_run_state_machine(struct tcpm_port *port)
|
|||
port->vdo_data[0] = port->vdo_retry;
|
||||
port->vdo_count = 1;
|
||||
port->vdm_state = VDM_STATE_READY;
|
||||
tcpm_ams_finish(port);
|
||||
break;
|
||||
case VDM_STATE_BUSY:
|
||||
port->vdm_state = VDM_STATE_ERR_TMOUT;
|
||||
|
@ -1961,7 +1978,7 @@ static void vdm_state_machine_work(struct kthread_work *work)
|
|||
port->vdm_state != VDM_STATE_BUSY &&
|
||||
port->vdm_state != VDM_STATE_SEND_MESSAGE);
|
||||
|
||||
if (port->vdm_state == VDM_STATE_ERR_TMOUT)
|
||||
if (port->vdm_state < VDM_STATE_READY)
|
||||
port->vdm_sm_running = false;
|
||||
|
||||
mutex_unlock(&port->lock);
|
||||
|
@ -2390,7 +2407,7 @@ static void tcpm_pd_data_request(struct tcpm_port *port,
|
|||
port->nr_sink_caps = cnt;
|
||||
port->sink_cap_done = true;
|
||||
if (port->ams == GET_SINK_CAPABILITIES)
|
||||
tcpm_pd_handle_state(port, ready_state(port), NONE_AMS, 0);
|
||||
tcpm_set_state(port, ready_state(port), 0);
|
||||
/* Unexpected Sink Capabilities */
|
||||
else
|
||||
tcpm_pd_handle_msg(port,
|
||||
|
@ -2552,6 +2569,16 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
|
|||
port->sink_cap_done = true;
|
||||
tcpm_set_state(port, ready_state(port), 0);
|
||||
break;
|
||||
case SRC_READY:
|
||||
case SNK_READY:
|
||||
if (port->vdm_state > VDM_STATE_READY) {
|
||||
port->vdm_state = VDM_STATE_DONE;
|
||||
if (tcpm_vdm_ams(port))
|
||||
tcpm_ams_finish(port);
|
||||
mod_vdm_delayed_work(port, 0);
|
||||
break;
|
||||
}
|
||||
fallthrough;
|
||||
default:
|
||||
tcpm_pd_handle_state(port,
|
||||
port->pwr_role == TYPEC_SOURCE ?
|
||||
|
@ -3682,14 +3709,6 @@ static inline enum tcpm_state unattached_state(struct tcpm_port *port)
|
|||
return SNK_UNATTACHED;
|
||||
}
|
||||
|
||||
static void tcpm_check_send_discover(struct tcpm_port *port)
|
||||
{
|
||||
if ((port->data_role == TYPEC_HOST || port->negotiated_rev > PD_REV20) &&
|
||||
port->send_discover && port->pd_capable)
|
||||
tcpm_send_vdm(port, USB_SID_PD, CMD_DISCOVER_IDENT, NULL, 0);
|
||||
port->send_discover = false;
|
||||
}
|
||||
|
||||
static void tcpm_swap_complete(struct tcpm_port *port, int result)
|
||||
{
|
||||
if (port->swap_pending) {
|
||||
|
@ -3926,7 +3945,18 @@ static void run_state_machine(struct tcpm_port *port)
|
|||
break;
|
||||
}
|
||||
|
||||
tcpm_check_send_discover(port);
|
||||
/*
|
||||
* 6.4.4.3.1 Discover Identity
|
||||
* "The Discover Identity Command Shall only be sent to SOP when there is an
|
||||
* Explicit Contract."
|
||||
* For now, this driver only supports SOP for DISCOVER_IDENTITY, thus using
|
||||
* port->explicit_contract to decide whether to send the command.
|
||||
*/
|
||||
if (port->explicit_contract)
|
||||
mod_send_discover_delayed_work(port, 0);
|
||||
else
|
||||
port->send_discover = false;
|
||||
|
||||
/*
|
||||
* 6.3.5
|
||||
* Sending ping messages is not necessary if
|
||||
|
@ -4055,7 +4085,7 @@ static void run_state_machine(struct tcpm_port *port)
|
|||
if (port->vbus_present) {
|
||||
u32 current_lim = tcpm_get_current_limit(port);
|
||||
|
||||
if (port->slow_charger_loop || (current_lim > PD_P_SNK_STDBY_MW / 5))
|
||||
if (port->slow_charger_loop && (current_lim > PD_P_SNK_STDBY_MW / 5))
|
||||
current_lim = PD_P_SNK_STDBY_MW / 5;
|
||||
tcpm_set_current_limit(port, current_lim, 5000);
|
||||
tcpm_set_charge(port, true);
|
||||
|
@ -4194,7 +4224,18 @@ static void run_state_machine(struct tcpm_port *port)
|
|||
break;
|
||||
}
|
||||
|
||||
tcpm_check_send_discover(port);
|
||||
/*
|
||||
* 6.4.4.3.1 Discover Identity
|
||||
* "The Discover Identity Command Shall only be sent to SOP when there is an
|
||||
* Explicit Contract."
|
||||
* For now, this driver only supports SOP for DISCOVER_IDENTITY, thus using
|
||||
* port->explicit_contract.
|
||||
*/
|
||||
if (port->explicit_contract)
|
||||
mod_send_discover_delayed_work(port, 0);
|
||||
else
|
||||
port->send_discover = false;
|
||||
|
||||
power_supply_changed(port->psy);
|
||||
break;
|
||||
|
||||
|
@ -5288,6 +5329,29 @@ unlock:
|
|||
mutex_unlock(&port->lock);
|
||||
}
|
||||
|
||||
static void tcpm_send_discover_work(struct kthread_work *work)
|
||||
{
|
||||
struct tcpm_port *port = container_of(work, struct tcpm_port, send_discover_work);
|
||||
|
||||
mutex_lock(&port->lock);
|
||||
/* No need to send DISCOVER_IDENTITY anymore */
|
||||
if (!port->send_discover)
|
||||
goto unlock;
|
||||
|
||||
/* Retry if the port is not idle */
|
||||
if ((port->state != SRC_READY && port->state != SNK_READY) || port->vdm_sm_running) {
|
||||
mod_send_discover_delayed_work(port, SEND_DISCOVER_RETRY_MS);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* Only send the Message if the port is host for PD rev2.0 */
|
||||
if (port->data_role == TYPEC_HOST || port->negotiated_rev > PD_REV20)
|
||||
tcpm_send_vdm(port, USB_SID_PD, CMD_DISCOVER_IDENT, NULL, 0);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&port->lock);
|
||||
}
|
||||
|
||||
static int tcpm_dr_set(struct typec_port *p, enum typec_data_role data)
|
||||
{
|
||||
struct tcpm_port *port = typec_get_drvdata(p);
|
||||
|
@ -6093,6 +6157,14 @@ static enum hrtimer_restart enable_frs_timer_handler(struct hrtimer *timer)
|
|||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
static enum hrtimer_restart send_discover_timer_handler(struct hrtimer *timer)
|
||||
{
|
||||
struct tcpm_port *port = container_of(timer, struct tcpm_port, send_discover_timer);
|
||||
|
||||
kthread_queue_work(port->wq, &port->send_discover_work);
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
|
||||
{
|
||||
struct tcpm_port *port;
|
||||
|
@ -6123,12 +6195,15 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
|
|||
kthread_init_work(&port->vdm_state_machine, vdm_state_machine_work);
|
||||
kthread_init_work(&port->event_work, tcpm_pd_event_handler);
|
||||
kthread_init_work(&port->enable_frs, tcpm_enable_frs_work);
|
||||
kthread_init_work(&port->send_discover_work, tcpm_send_discover_work);
|
||||
hrtimer_init(&port->state_machine_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
port->state_machine_timer.function = state_machine_timer_handler;
|
||||
hrtimer_init(&port->vdm_state_machine_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
port->vdm_state_machine_timer.function = vdm_state_machine_timer_handler;
|
||||
hrtimer_init(&port->enable_frs_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
port->enable_frs_timer.function = enable_frs_timer_handler;
|
||||
hrtimer_init(&port->send_discover_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
port->send_discover_timer.function = send_discover_timer_handler;
|
||||
|
||||
spin_lock_init(&port->pd_event_lock);
|
||||
|
||||
|
|
|
@ -495,7 +495,8 @@ static void ucsi_unregister_altmodes(struct ucsi_connector *con, u8 recipient)
|
|||
}
|
||||
}
|
||||
|
||||
static void ucsi_get_pdos(struct ucsi_connector *con, int is_partner)
|
||||
static int ucsi_get_pdos(struct ucsi_connector *con, int is_partner,
|
||||
u32 *pdos, int offset, int num_pdos)
|
||||
{
|
||||
struct ucsi *ucsi = con->ucsi;
|
||||
u64 command;
|
||||
|
@ -503,17 +504,39 @@ static void ucsi_get_pdos(struct ucsi_connector *con, int is_partner)
|
|||
|
||||
command = UCSI_COMMAND(UCSI_GET_PDOS) | UCSI_CONNECTOR_NUMBER(con->num);
|
||||
command |= UCSI_GET_PDOS_PARTNER_PDO(is_partner);
|
||||
command |= UCSI_GET_PDOS_NUM_PDOS(UCSI_MAX_PDOS - 1);
|
||||
command |= UCSI_GET_PDOS_PDO_OFFSET(offset);
|
||||
command |= UCSI_GET_PDOS_NUM_PDOS(num_pdos - 1);
|
||||
command |= UCSI_GET_PDOS_SRC_PDOS;
|
||||
ret = ucsi_send_command(ucsi, command, con->src_pdos,
|
||||
sizeof(con->src_pdos));
|
||||
if (ret < 0) {
|
||||
ret = ucsi_send_command(ucsi, command, pdos + offset,
|
||||
num_pdos * sizeof(u32));
|
||||
if (ret < 0)
|
||||
dev_err(ucsi->dev, "UCSI_GET_PDOS failed (%d)\n", ret);
|
||||
return;
|
||||
}
|
||||
con->num_pdos = ret / sizeof(u32); /* number of bytes to 32-bit PDOs */
|
||||
if (ret == 0)
|
||||
if (ret == 0 && offset == 0)
|
||||
dev_warn(ucsi->dev, "UCSI_GET_PDOS returned 0 bytes\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ucsi_get_src_pdos(struct ucsi_connector *con, int is_partner)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* UCSI max payload means only getting at most 4 PDOs at a time */
|
||||
ret = ucsi_get_pdos(con, 1, con->src_pdos, 0, UCSI_MAX_PDOS);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
con->num_pdos = ret / sizeof(u32); /* number of bytes to 32-bit PDOs */
|
||||
if (con->num_pdos < UCSI_MAX_PDOS)
|
||||
return;
|
||||
|
||||
/* get the remaining PDOs, if any */
|
||||
ret = ucsi_get_pdos(con, 1, con->src_pdos, UCSI_MAX_PDOS,
|
||||
PDO_MAX_OBJECTS - UCSI_MAX_PDOS);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
con->num_pdos += ret / sizeof(u32);
|
||||
}
|
||||
|
||||
static void ucsi_pwr_opmode_change(struct ucsi_connector *con)
|
||||
|
@ -522,7 +545,7 @@ static void ucsi_pwr_opmode_change(struct ucsi_connector *con)
|
|||
case UCSI_CONSTAT_PWR_OPMODE_PD:
|
||||
con->rdo = con->status.request_data_obj;
|
||||
typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_PD);
|
||||
ucsi_get_pdos(con, 1);
|
||||
ucsi_get_src_pdos(con, 1);
|
||||
break;
|
||||
case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
|
||||
con->rdo = 0;
|
||||
|
@ -999,6 +1022,7 @@ static const struct typec_operations ucsi_ops = {
|
|||
.pr_set = ucsi_pr_swap
|
||||
};
|
||||
|
||||
/* Caller must call fwnode_handle_put() after use */
|
||||
static struct fwnode_handle *ucsi_find_fwnode(struct ucsi_connector *con)
|
||||
{
|
||||
struct fwnode_handle *fwnode;
|
||||
|
@ -1033,7 +1057,7 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
|
|||
command |= UCSI_CONNECTOR_NUMBER(con->num);
|
||||
ret = ucsi_send_command(ucsi, command, &con->cap, sizeof(con->cap));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
goto out_unlock;
|
||||
|
||||
if (con->cap.op_mode & UCSI_CONCAP_OPMODE_DRP)
|
||||
cap->data = TYPEC_PORT_DRD;
|
||||
|
@ -1151,6 +1175,8 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
|
|||
trace_ucsi_register_port(con->num, &con->status);
|
||||
|
||||
out:
|
||||
fwnode_handle_put(cap->fwnode);
|
||||
out_unlock:
|
||||
mutex_unlock(&con->lock);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <linux/power_supply.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/usb/typec.h>
|
||||
#include <linux/usb/pd.h>
|
||||
#include <linux/usb/role.h>
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
@ -134,7 +135,9 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num);
|
|||
|
||||
/* GET_PDOS command bits */
|
||||
#define UCSI_GET_PDOS_PARTNER_PDO(_r_) ((u64)(_r_) << 23)
|
||||
#define UCSI_GET_PDOS_PDO_OFFSET(_r_) ((u64)(_r_) << 24)
|
||||
#define UCSI_GET_PDOS_NUM_PDOS(_r_) ((u64)(_r_) << 32)
|
||||
#define UCSI_MAX_PDOS (4)
|
||||
#define UCSI_GET_PDOS_SRC_PDOS ((u64)1 << 34)
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
@ -302,7 +305,6 @@ struct ucsi {
|
|||
|
||||
#define UCSI_MAX_SVID 5
|
||||
#define UCSI_MAX_ALTMODES (UCSI_MAX_SVID * 6)
|
||||
#define UCSI_MAX_PDOS (4)
|
||||
|
||||
#define UCSI_TYPEC_VSAFE5V 5000
|
||||
#define UCSI_TYPEC_1_5_CURRENT 1500
|
||||
|
@ -330,7 +332,7 @@ struct ucsi_connector {
|
|||
struct power_supply *psy;
|
||||
struct power_supply_desc psy_desc;
|
||||
u32 rdo;
|
||||
u32 src_pdos[UCSI_MAX_PDOS];
|
||||
u32 src_pdos[PDO_MAX_OBJECTS];
|
||||
int num_pdos;
|
||||
|
||||
struct usb_role_switch *usb_role_sw;
|
||||
|
|
Загрузка…
Ссылка в новой задаче