mirror of
https://github.com/torvalds/linux.git
synced 2024-12-17 16:43:08 +00:00
usb: dwc3: patches for v3.5 merge window
This pull request contains one workaround for a Silicon Issue found on all RTL releases prior to 2.20a, which would cause a metastability state on Run/Stop bit. We also have some patches implementing a few extra Standard requests introduced by USB3 spec (Set SEL and Set Isoch Delay), as well as one patch, which has been pending for a long time, implementing LPM support. Last, but not least, we are splitting the host address space out of the dwc3 core driver otherwise xHCI won't be able to request_mem_region() its own address space. This patch is only needed because we are (as we should) re-using the xHCI driver, which is a completely separate module. Together with these three big changes, come a few extra preparatory patches which most move code around, define macros and so on, as well as a fix for Isochronous transfers which hasn't been triggered before. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQIcBAABAgAGBQJPo7lXAAoJEIaOsuA1yqRESaIP/AgxZIfOAUbPx0GWLnhub3qr SxaUplweFc9q4KXRLn0kGdY9QArPR3bqW9g8KOTiRCBYRtjpACyMjibAUaAht81h +vLdPt87Slj2c14t1uguWFvgCUYQOCugkVvDIjRg9PCLIuTahm4cIBFqL3RJOHFf 9WCd8JjH9ahr85ZtoCBk9B5bDNn71nS+Yh6/8+Ab90AE4vZ6t8Xx3+wLTHy2CBYQ UH1o61QZreAJ0J3OiUobjqrVbYwz6TM0dFYMjA6ko+OiPRhVOj8/C8aNl/U1whRm +7jjJiWO9aHp+Tu2OAQOBF6ydc3ZLBEiCl9RiE+O9MppmtOykzkTHFm1ZXatCEY7 UUYOy43VXLNlHoz8nidNw6P25hAwwlSijzlyawpihKbIaE8le2MpE6I00AlciM2q BEo4LpluC8Rr6CUUr5W9dPZUexRlzxdAL5nQSJUnJgfEPphpP3x7dWTxUZBaWjq6 akqjgGqVj1QKwMnqL4GILtRgdqWj6WYrw67fYVLHqj8QQla4cgXQ2sHp9/R0imvT nmjiL5ZiuIWWr965DgVHZwqIkdvMpSQb99a1xmptw8lFDGkVJDCssPDdEErbBMwy KmOSaqKeg/Yway05i+Pwo/NUKHQSZeiyuguzniMrF7iYFF1/2hVYRgfpH4V+95w/ Xrnz4uH2YJGQGPddf87P =qgf/ -----END PGP SIGNATURE----- Merge tag 'dwc3-for-v3.5' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next usb: dwc3: patches for v3.5 merge window This pull request contains one workaround for a Silicon Issue found on all RTL releases prior to 2.20a, which would cause a metastability state on Run/Stop bit. We also have some patches implementing a few extra Standard requests introduced by USB3 spec (Set SEL and Set Isoch Delay), as well as one patch, which has been pending for a long time, implementing LPM support. Last, but not least, we are splitting the host address space out of the dwc3 core driver otherwise xHCI won't be able to request_mem_region() its own address space. This patch is only needed because we are (as we should) re-using the xHCI driver, which is a completely separate module. Together with these three big changes, come a few extra preparatory patches which most move code around, define macros and so on, as well as a fix for Isochronous transfers which hasn't been triggered before. [ resolved conflicts and build error in drivers/usb/dwc3/gadget.c - gregkh]
This commit is contained in:
commit
23063b378d
@ -410,7 +410,6 @@ static int __devinit dwc3_probe(struct platform_device *pdev)
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
int ret = -ENOMEM;
|
||||
int irq;
|
||||
|
||||
void __iomem *regs;
|
||||
void *mem;
|
||||
@ -425,15 +424,28 @@ static int __devinit dwc3_probe(struct platform_device *pdev)
|
||||
dwc = PTR_ALIGN(mem, DWC3_ALIGN_MASK + 1);
|
||||
dwc->mem = mem;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "missing resource\n");
|
||||
dev_err(dev, "missing IRQ\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
dwc->xhci_resources[1] = *res;
|
||||
|
||||
dwc->res = res;
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "missing memory resource\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
dwc->xhci_resources[0] = *res;
|
||||
dwc->xhci_resources[0].end = dwc->xhci_resources[0].start +
|
||||
DWC3_XHCI_REGS_END;
|
||||
|
||||
res = devm_request_mem_region(dev, res->start, resource_size(res),
|
||||
/*
|
||||
* Request memory region but exclude xHCI regs,
|
||||
* since it will be requested by the xhci-plat driver.
|
||||
*/
|
||||
res = devm_request_mem_region(dev, res->start + DWC3_GLOBALS_REGS_START,
|
||||
resource_size(res) - DWC3_GLOBALS_REGS_START,
|
||||
dev_name(dev));
|
||||
if (!res) {
|
||||
dev_err(dev, "can't request mem region\n");
|
||||
@ -446,19 +458,12 @@ static int __devinit dwc3_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "missing IRQ\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
spin_lock_init(&dwc->lock);
|
||||
platform_set_drvdata(pdev, dwc);
|
||||
|
||||
dwc->regs = regs;
|
||||
dwc->regs_size = resource_size(res);
|
||||
dwc->dev = dev;
|
||||
dwc->irq = irq;
|
||||
|
||||
if (!strncmp("super", maximum_speed, 5))
|
||||
dwc->maximum_speed = DWC3_DCFG_SUPERSPEED;
|
||||
|
@ -51,7 +51,9 @@
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
/* Global constants */
|
||||
#define DWC3_EP0_BOUNCE_SIZE 512
|
||||
#define DWC3_ENDPOINTS_NUM 32
|
||||
#define DWC3_XHCI_RESOURCES_NUM 2
|
||||
|
||||
#define DWC3_EVENT_BUFFERS_SIZE PAGE_SIZE
|
||||
#define DWC3_EVENT_TYPE_MASK 0xfe
|
||||
@ -75,6 +77,16 @@
|
||||
#define DWC3_GSNPSID_MASK 0xffff0000
|
||||
#define DWC3_GSNPSREV_MASK 0xffff
|
||||
|
||||
/* DWC3 registers memory space boundries */
|
||||
#define DWC3_XHCI_REGS_START 0x0
|
||||
#define DWC3_XHCI_REGS_END 0x7fff
|
||||
#define DWC3_GLOBALS_REGS_START 0xc100
|
||||
#define DWC3_GLOBALS_REGS_END 0xc6ff
|
||||
#define DWC3_DEVICE_REGS_START 0xc700
|
||||
#define DWC3_DEVICE_REGS_END 0xcbff
|
||||
#define DWC3_OTG_REGS_START 0xcc00
|
||||
#define DWC3_OTG_REGS_END 0xccff
|
||||
|
||||
/* Global Registers */
|
||||
#define DWC3_GSBUSCFG0 0xc100
|
||||
#define DWC3_GSBUSCFG1 0xc104
|
||||
@ -183,6 +195,7 @@
|
||||
#define DWC3_GHWPARAMS1_EN_PWROPT_CLK 1
|
||||
|
||||
/* Device Configuration Register */
|
||||
#define DWC3_DCFG_LPM_CAP (1 << 22)
|
||||
#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3)
|
||||
#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f)
|
||||
|
||||
@ -272,12 +285,14 @@
|
||||
#define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c
|
||||
#define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10
|
||||
|
||||
#define DWC3_DGCMD_STATUS(n) (((n) >> 15) & 1)
|
||||
#define DWC3_DGCMD_CMDACT (1 << 10)
|
||||
|
||||
/* Device Endpoint Command Register */
|
||||
#define DWC3_DEPCMD_PARAM_SHIFT 16
|
||||
#define DWC3_DEPCMD_PARAM(x) ((x) << DWC3_DEPCMD_PARAM_SHIFT)
|
||||
#define DWC3_DEPCMD_GET_RSC_IDX(x) (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f)
|
||||
#define DWC3_DEPCMD_STATUS_MASK (0x0f << 12)
|
||||
#define DWC3_DEPCMD_STATUS(x) (((x) & DWC3_DEPCMD_STATUS_MASK) >> 12)
|
||||
#define DWC3_DEPCMD_STATUS(x) (((x) >> 15) & 1)
|
||||
#define DWC3_DEPCMD_HIPRI_FORCERM (1 << 11)
|
||||
#define DWC3_DEPCMD_CMDACT (1 << 10)
|
||||
#define DWC3_DEPCMD_CMDIOC (1 << 8)
|
||||
@ -560,6 +575,11 @@ struct dwc3_request {
|
||||
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
|
||||
* @needs_fifo_resize: not all users might want fifo resizing, flag it
|
||||
* @resize_fifos: tells us it's ok to reconfigure our TxFIFO sizes.
|
||||
* @isoch_delay: wValue from Set Isochronous Delay request;
|
||||
* @u2sel: parameter from Set SEL request.
|
||||
* @u2pel: parameter from Set SEL request.
|
||||
* @u1sel: parameter from Set SEL request.
|
||||
* @u1pel: parameter from Set SEL request.
|
||||
* @ep0_next_event: hold the next expected event
|
||||
* @ep0state: state of endpoint zero
|
||||
* @link_state: link state
|
||||
@ -582,7 +602,7 @@ struct dwc3 {
|
||||
struct device *dev;
|
||||
|
||||
struct platform_device *xhci;
|
||||
struct resource *res;
|
||||
struct resource xhci_resources[DWC3_XHCI_RESOURCES_NUM];
|
||||
|
||||
struct dwc3_event_buffer **ev_buffs;
|
||||
struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM];
|
||||
@ -593,8 +613,6 @@ struct dwc3 {
|
||||
void __iomem *regs;
|
||||
size_t regs_size;
|
||||
|
||||
int irq;
|
||||
|
||||
u32 num_event_buffers;
|
||||
u32 u1u2;
|
||||
u32 maximum_speed;
|
||||
@ -608,6 +626,10 @@ struct dwc3 {
|
||||
#define DWC3_REVISION_185A 0x5533185a
|
||||
#define DWC3_REVISION_188A 0x5533188a
|
||||
#define DWC3_REVISION_190A 0x5533190a
|
||||
#define DWC3_REVISION_200A 0x5533200a
|
||||
#define DWC3_REVISION_202A 0x5533202a
|
||||
#define DWC3_REVISION_210A 0x5533210a
|
||||
#define DWC3_REVISION_220A 0x5533220a
|
||||
|
||||
unsigned is_selfpowered:1;
|
||||
unsigned three_stage_setup:1;
|
||||
@ -624,7 +646,14 @@ struct dwc3 {
|
||||
enum dwc3_link_state link_state;
|
||||
enum dwc3_device_state dev_state;
|
||||
|
||||
u16 isoch_delay;
|
||||
u16 u2sel;
|
||||
u16 u2pel;
|
||||
u8 u1sel;
|
||||
u8 u1pel;
|
||||
|
||||
u8 speed;
|
||||
|
||||
void *mem;
|
||||
|
||||
struct dwc3_hwparams hwparams;
|
||||
|
@ -49,7 +49,6 @@
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "io.h"
|
||||
|
||||
/*
|
||||
* All these registers belong to OMAP's Wrapper around the
|
||||
@ -143,6 +142,17 @@ struct dwc3_omap {
|
||||
u32 dma_status:1;
|
||||
};
|
||||
|
||||
static inline u32 dwc3_omap_readl(void __iomem *base, u32 offset)
|
||||
{
|
||||
return readl(base + offset);
|
||||
}
|
||||
|
||||
static inline void dwc3_omap_writel(void __iomem *base, u32 offset, u32 value)
|
||||
{
|
||||
writel(value, base + offset);
|
||||
}
|
||||
|
||||
|
||||
static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
|
||||
{
|
||||
struct dwc3_omap *omap = _omap;
|
||||
@ -150,7 +160,7 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
|
||||
|
||||
spin_lock(&omap->lock);
|
||||
|
||||
reg = dwc3_readl(omap->base, USBOTGSS_IRQSTATUS_1);
|
||||
reg = dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_1);
|
||||
|
||||
if (reg & USBOTGSS_IRQ1_DMADISABLECLR) {
|
||||
dev_dbg(omap->dev, "DMA Disable was Cleared\n");
|
||||
@ -184,10 +194,10 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
|
||||
if (reg & USBOTGSS_IRQ1_IDPULLUP_FALL)
|
||||
dev_dbg(omap->dev, "IDPULLUP Fall\n");
|
||||
|
||||
dwc3_writel(omap->base, USBOTGSS_IRQSTATUS_1, reg);
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_IRQSTATUS_1, reg);
|
||||
|
||||
reg = dwc3_readl(omap->base, USBOTGSS_IRQSTATUS_0);
|
||||
dwc3_writel(omap->base, USBOTGSS_IRQSTATUS_0, reg);
|
||||
reg = dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_0);
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_IRQSTATUS_0, reg);
|
||||
|
||||
spin_unlock(&omap->lock);
|
||||
|
||||
@ -270,7 +280,7 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev)
|
||||
omap->base = base;
|
||||
omap->dwc3 = dwc3;
|
||||
|
||||
reg = dwc3_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
|
||||
reg = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
|
||||
|
||||
utmi_mode = of_get_property(node, "utmi-mode", &size);
|
||||
if (utmi_mode && size == sizeof(*utmi_mode)) {
|
||||
@ -293,10 +303,10 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
dwc3_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, reg);
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, reg);
|
||||
|
||||
/* check the DMA Status */
|
||||
reg = dwc3_readl(omap->base, USBOTGSS_SYSCONFIG);
|
||||
reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);
|
||||
omap->dma_status = !!(reg & USBOTGSS_SYSCONFIG_DMADISABLE);
|
||||
|
||||
/* Set No-Idle and No-Standby */
|
||||
@ -306,7 +316,7 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev)
|
||||
reg |= (USBOTGSS_SYSCONFIG_STANDBYMODE(USBOTGSS_STANDBYMODE_NO_STANDBY)
|
||||
| USBOTGSS_SYSCONFIG_IDLEMODE(USBOTGSS_IDLEMODE_NO_IDLE));
|
||||
|
||||
dwc3_writel(omap->base, USBOTGSS_SYSCONFIG, reg);
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_SYSCONFIG, reg);
|
||||
|
||||
ret = devm_request_irq(dev, omap->irq, dwc3_omap_interrupt, 0,
|
||||
"dwc3-omap", omap);
|
||||
@ -318,7 +328,7 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev)
|
||||
|
||||
/* enable all IRQs */
|
||||
reg = USBOTGSS_IRQO_COREIRQ_ST;
|
||||
dwc3_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, reg);
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, reg);
|
||||
|
||||
reg = (USBOTGSS_IRQ1_OEVT |
|
||||
USBOTGSS_IRQ1_DRVVBUS_RISE |
|
||||
@ -330,7 +340,7 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev)
|
||||
USBOTGSS_IRQ1_DISCHRGVBUS_FALL |
|
||||
USBOTGSS_IRQ1_IDPULLUP_FALL);
|
||||
|
||||
dwc3_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, reg);
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, reg);
|
||||
|
||||
ret = platform_device_add_resources(dwc3, pdev->resource,
|
||||
pdev->num_resources);
|
||||
|
@ -261,6 +261,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
|
||||
{
|
||||
struct dwc3_ep *dep;
|
||||
u32 recip;
|
||||
u32 reg;
|
||||
u16 usb_status = 0;
|
||||
__le16 *response_pkt;
|
||||
|
||||
@ -268,10 +269,18 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
|
||||
switch (recip) {
|
||||
case USB_RECIP_DEVICE:
|
||||
/*
|
||||
* We are self-powered. U1/U2/LTM will be set later
|
||||
* once we handle this states. RemoteWakeup is 0 on SS
|
||||
* LTM will be set once we know how to set this in HW.
|
||||
*/
|
||||
usb_status |= dwc->is_selfpowered << USB_DEVICE_SELF_POWERED;
|
||||
|
||||
if (dwc->speed == DWC3_DSTS_SUPERSPEED) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
if (reg & DWC3_DCTL_INITU1ENA)
|
||||
usb_status |= 1 << USB_DEV_STAT_U1_ENABLED;
|
||||
if (reg & DWC3_DCTL_INITU2ENA)
|
||||
usb_status |= 1 << USB_DEV_STAT_U2_ENABLED;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case USB_RECIP_INTERFACE:
|
||||
@ -312,6 +321,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
|
||||
u32 recip;
|
||||
u32 wValue;
|
||||
u32 wIndex;
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
wValue = le16_to_cpu(ctrl->wValue);
|
||||
@ -320,29 +330,43 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
|
||||
switch (recip) {
|
||||
case USB_RECIP_DEVICE:
|
||||
|
||||
switch (wValue) {
|
||||
case USB_DEVICE_REMOTE_WAKEUP:
|
||||
break;
|
||||
/*
|
||||
* 9.4.1 says only only for SS, in AddressState only for
|
||||
* default control pipe
|
||||
*/
|
||||
switch (wValue) {
|
||||
case USB_DEVICE_U1_ENABLE:
|
||||
case USB_DEVICE_U2_ENABLE:
|
||||
case USB_DEVICE_LTM_ENABLE:
|
||||
if (dwc->dev_state != DWC3_CONFIGURED_STATE)
|
||||
return -EINVAL;
|
||||
if (dwc->speed != DWC3_DSTS_SUPERSPEED)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* XXX add U[12] & LTM */
|
||||
switch (wValue) {
|
||||
case USB_DEVICE_REMOTE_WAKEUP:
|
||||
break;
|
||||
case USB_DEVICE_U1_ENABLE:
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
if (set)
|
||||
reg |= DWC3_DCTL_INITU1ENA;
|
||||
else
|
||||
reg &= ~DWC3_DCTL_INITU1ENA;
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
break;
|
||||
|
||||
case USB_DEVICE_U2_ENABLE:
|
||||
if (dwc->dev_state != DWC3_CONFIGURED_STATE)
|
||||
return -EINVAL;
|
||||
if (dwc->speed != DWC3_DSTS_SUPERSPEED)
|
||||
return -EINVAL;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
if (set)
|
||||
reg |= DWC3_DCTL_INITU2ENA;
|
||||
else
|
||||
reg &= ~DWC3_DCTL_INITU2ENA;
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
break;
|
||||
|
||||
case USB_DEVICE_LTM_ENABLE:
|
||||
return -EINVAL;
|
||||
break;
|
||||
|
||||
case USB_DEVICE_TEST_MODE:
|
||||
@ -469,6 +493,107 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct dwc3_ep *dep = to_dwc3_ep(ep);
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
|
||||
u32 param = 0;
|
||||
u32 reg;
|
||||
|
||||
struct timing {
|
||||
u8 u1sel;
|
||||
u8 u1pel;
|
||||
u16 u2sel;
|
||||
u16 u2pel;
|
||||
} __packed timing;
|
||||
|
||||
int ret;
|
||||
|
||||
memcpy(&timing, req->buf, sizeof(timing));
|
||||
|
||||
dwc->u1sel = timing.u1sel;
|
||||
dwc->u1pel = timing.u1pel;
|
||||
dwc->u2sel = timing.u2sel;
|
||||
dwc->u2pel = timing.u2pel;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
if (reg & DWC3_DCTL_INITU2ENA)
|
||||
param = dwc->u2pel;
|
||||
if (reg & DWC3_DCTL_INITU1ENA)
|
||||
param = dwc->u1pel;
|
||||
|
||||
/*
|
||||
* According to Synopsys Databook, if parameter is
|
||||
* greater than 125, a value of zero should be
|
||||
* programmed in the register.
|
||||
*/
|
||||
if (param > 125)
|
||||
param = 0;
|
||||
|
||||
/* now that we have the time, issue DGCMD Set Sel */
|
||||
ret = dwc3_send_gadget_generic_command(dwc,
|
||||
DWC3_DGCMD_SET_PERIODIC_PAR, param);
|
||||
WARN_ON(ret < 0);
|
||||
}
|
||||
|
||||
static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct dwc3_ep *dep;
|
||||
u16 wLength;
|
||||
u16 wValue;
|
||||
|
||||
if (dwc->dev_state == DWC3_DEFAULT_STATE)
|
||||
return -EINVAL;
|
||||
|
||||
wValue = le16_to_cpu(ctrl->wValue);
|
||||
wLength = le16_to_cpu(ctrl->wLength);
|
||||
|
||||
if (wLength != 6) {
|
||||
dev_err(dwc->dev, "Set SEL should be 6 bytes, got %d\n",
|
||||
wLength);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* To handle Set SEL we need to receive 6 bytes from Host. So let's
|
||||
* queue a usb_request for 6 bytes.
|
||||
*
|
||||
* Remember, though, this controller can't handle non-wMaxPacketSize
|
||||
* aligned transfers on the OUT direction, so we queue a request for
|
||||
* wMaxPacketSize instead.
|
||||
*/
|
||||
dep = dwc->eps[0];
|
||||
dwc->ep0_usb_req.dep = dep;
|
||||
dwc->ep0_usb_req.request.length = dep->endpoint.maxpacket;
|
||||
dwc->ep0_usb_req.request.buf = dwc->setup_buf;
|
||||
dwc->ep0_usb_req.request.complete = dwc3_ep0_set_sel_cmpl;
|
||||
|
||||
return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req);
|
||||
}
|
||||
|
||||
static int dwc3_ep0_set_isoch_delay(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
u16 wLength;
|
||||
u16 wValue;
|
||||
u16 wIndex;
|
||||
|
||||
wValue = le16_to_cpu(ctrl->wValue);
|
||||
wLength = le16_to_cpu(ctrl->wLength);
|
||||
wIndex = le16_to_cpu(ctrl->wIndex);
|
||||
|
||||
if (wIndex || wLength)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* REVISIT It's unclear from Databook what to do with this
|
||||
* value. For now, just cache it.
|
||||
*/
|
||||
dwc->isoch_delay = wValue;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
int ret;
|
||||
@ -494,6 +619,14 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
||||
dev_vdbg(dwc->dev, "USB_REQ_SET_CONFIGURATION\n");
|
||||
ret = dwc3_ep0_set_config(dwc, ctrl);
|
||||
break;
|
||||
case USB_REQ_SET_SEL:
|
||||
dev_vdbg(dwc->dev, "USB_REQ_SET_SEL\n");
|
||||
ret = dwc3_ep0_set_sel(dwc, ctrl);
|
||||
break;
|
||||
case USB_REQ_SET_ISOCH_DELAY:
|
||||
dev_vdbg(dwc->dev, "USB_REQ_SET_ISOCH_DELAY\n");
|
||||
ret = dwc3_ep0_set_isoch_delay(dwc, ctrl);
|
||||
break;
|
||||
default:
|
||||
dev_vdbg(dwc->dev, "Forwarding to gadget driver\n");
|
||||
ret = dwc3_ep0_delegate_req(dwc, ctrl);
|
||||
|
@ -276,6 +276,33 @@ static const char *dwc3_gadget_ep_cmd_string(u8 cmd)
|
||||
}
|
||||
}
|
||||
|
||||
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param)
|
||||
{
|
||||
u32 timeout = 500;
|
||||
u32 reg;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param);
|
||||
dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT);
|
||||
|
||||
do {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DGCMD);
|
||||
if (!(reg & DWC3_DGCMD_CMDACT)) {
|
||||
dev_vdbg(dwc->dev, "Command Complete --> %d\n",
|
||||
DWC3_DGCMD_STATUS(reg));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We can't sleep here, because it's also called from
|
||||
* interrupt context.
|
||||
*/
|
||||
timeout--;
|
||||
if (!timeout)
|
||||
return -ETIMEDOUT;
|
||||
udelay(1);
|
||||
} while (1);
|
||||
}
|
||||
|
||||
int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
||||
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params)
|
||||
{
|
||||
@ -929,10 +956,12 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
|
||||
}
|
||||
|
||||
dep->flags |= DWC3_EP_BUSY;
|
||||
dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc,
|
||||
dep->number);
|
||||
|
||||
WARN_ON_ONCE(!dep->res_trans_idx);
|
||||
if (start_new) {
|
||||
dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc,
|
||||
dep->number);
|
||||
WARN_ON_ONCE(!dep->res_trans_idx);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -966,28 +995,37 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
||||
|
||||
list_add_tail(&req->list, &dep->request_list);
|
||||
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && (dep->flags & DWC3_EP_BUSY))
|
||||
dep->flags |= DWC3_EP_PENDING_REQUEST;
|
||||
|
||||
/*
|
||||
* There is one special case: XferNotReady with
|
||||
* empty list of requests. We need to kick the
|
||||
* transfer here in that situation, otherwise
|
||||
* we will be NAKing forever.
|
||||
* There are two special cases:
|
||||
*
|
||||
* If we get XferNotReady before gadget driver
|
||||
* has a chance to queue a request, we will ACK
|
||||
* the IRQ but won't be able to receive the data
|
||||
* until the next request is queued. The following
|
||||
* code is handling exactly that.
|
||||
* 1. XferNotReady with empty list of requests. We need to kick the
|
||||
* transfer here in that situation, otherwise we will be NAKing
|
||||
* forever. If we get XferNotReady before gadget driver has a
|
||||
* chance to queue a request, we will ACK the IRQ but won't be
|
||||
* able to receive the data until the next request is queued.
|
||||
* The following code is handling exactly that.
|
||||
*
|
||||
* 2. XferInProgress on Isoc EP with an active transfer. We need to
|
||||
* kick the transfer here after queuing a request, otherwise the
|
||||
* core may not see the modified TRB(s).
|
||||
*/
|
||||
if (dep->flags & DWC3_EP_PENDING_REQUEST) {
|
||||
int ret;
|
||||
int start_trans;
|
||||
int ret;
|
||||
int start_trans = 1;
|
||||
u8 trans_idx = dep->res_trans_idx;
|
||||
|
||||
start_trans = 1;
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
|
||||
(dep->flags & DWC3_EP_BUSY))
|
||||
(dep->flags & DWC3_EP_BUSY)) {
|
||||
start_trans = 0;
|
||||
WARN_ON_ONCE(!trans_idx);
|
||||
} else {
|
||||
trans_idx = 0;
|
||||
}
|
||||
|
||||
ret = __dwc3_gadget_kick_transfer(dep, 0, start_trans);
|
||||
ret = __dwc3_gadget_kick_transfer(dep, trans_idx, start_trans);
|
||||
if (ret && ret != -EBUSY) {
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
|
||||
@ -1355,7 +1393,24 @@ static int dwc3_gadget_start(struct usb_gadget *g,
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
||||
reg &= ~(DWC3_DCFG_SPEED_MASK);
|
||||
reg |= dwc->maximum_speed;
|
||||
|
||||
/**
|
||||
* WORKAROUND: DWC3 revision < 2.20a have an issue
|
||||
* which would cause metastability state on Run/Stop
|
||||
* bit if we try to force the IP to USB2-only mode.
|
||||
*
|
||||
* Because of that, we cannot configure the IP to any
|
||||
* speed other than the SuperSpeed
|
||||
*
|
||||
* Refers to:
|
||||
*
|
||||
* STAR#9000525659: Clock Domain Crossing on DCTL in
|
||||
* USB 2.0 Mode
|
||||
*/
|
||||
if (dwc->revision < DWC3_REVISION_220A)
|
||||
reg |= DWC3_DCFG_SUPERSPEED;
|
||||
else
|
||||
reg |= dwc->maximum_speed;
|
||||
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
||||
|
||||
dwc->start_config_issued = false;
|
||||
@ -1915,6 +1970,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
|
||||
reg &= ~(DWC3_DCTL_INITU1ENA | DWC3_DCTL_INITU2ENA);
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
dwc->test_mode = false;
|
||||
|
||||
@ -2262,8 +2318,7 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)
|
||||
goto err1;
|
||||
}
|
||||
|
||||
dwc->setup_buf = kzalloc(sizeof(*dwc->setup_buf) * 2,
|
||||
GFP_KERNEL);
|
||||
dwc->setup_buf = kzalloc(DWC3_EP0_BOUNCE_SIZE, GFP_KERNEL);
|
||||
if (!dwc->setup_buf) {
|
||||
dev_err(dwc->dev, "failed to allocate setup buffer\n");
|
||||
ret = -ENOMEM;
|
||||
@ -2271,7 +2326,8 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)
|
||||
}
|
||||
|
||||
dwc->ep0_bounce = dma_alloc_coherent(dwc->dev,
|
||||
512, &dwc->ep0_bounce_addr, GFP_KERNEL);
|
||||
DWC3_EP0_BOUNCE_SIZE, &dwc->ep0_bounce_addr,
|
||||
GFP_KERNEL);
|
||||
if (!dwc->ep0_bounce) {
|
||||
dev_err(dwc->dev, "failed to allocate ep0 bounce buffer\n");
|
||||
ret = -ENOMEM;
|
||||
@ -2312,6 +2368,14 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)
|
||||
goto err5;
|
||||
}
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
||||
reg |= DWC3_DCFG_LPM_CAP;
|
||||
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg |= DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA;
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
|
||||
/* Enable all but Start and End of Frame IRQs */
|
||||
reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN |
|
||||
DWC3_DEVTEN_EVNTOVERFLOWEN |
|
||||
@ -2350,8 +2414,8 @@ err5:
|
||||
dwc3_gadget_free_endpoints(dwc);
|
||||
|
||||
err4:
|
||||
dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce,
|
||||
dwc->ep0_bounce_addr);
|
||||
dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
|
||||
dwc->ep0_bounce, dwc->ep0_bounce_addr);
|
||||
|
||||
err3:
|
||||
kfree(dwc->setup_buf);
|
||||
@ -2380,8 +2444,8 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
|
||||
|
||||
dwc3_gadget_free_endpoints(dwc);
|
||||
|
||||
dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce,
|
||||
dwc->ep0_bounce_addr);
|
||||
dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
|
||||
dwc->ep0_bounce, dwc->ep0_bounce_addr);
|
||||
|
||||
kfree(dwc->setup_buf);
|
||||
|
||||
|
@ -111,6 +111,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
|
||||
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value);
|
||||
int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
||||
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params);
|
||||
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param);
|
||||
|
||||
/**
|
||||
* dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
|
||||
|
@ -39,15 +39,6 @@
|
||||
|
||||
#include "core.h"
|
||||
|
||||
static struct resource generic_resources[] = {
|
||||
{
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
{
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
};
|
||||
|
||||
int dwc3_host_init(struct dwc3 *dwc)
|
||||
{
|
||||
struct platform_device *xhci;
|
||||
@ -68,14 +59,8 @@ int dwc3_host_init(struct dwc3 *dwc)
|
||||
|
||||
dwc->xhci = xhci;
|
||||
|
||||
/* setup resources */
|
||||
generic_resources[0].start = dwc->irq;
|
||||
|
||||
generic_resources[1].start = dwc->res->start;
|
||||
generic_resources[1].end = dwc->res->start + 0x7fff;
|
||||
|
||||
ret = platform_device_add_resources(xhci, generic_resources,
|
||||
ARRAY_SIZE(generic_resources));
|
||||
ret = platform_device_add_resources(xhci, dwc->xhci_resources,
|
||||
DWC3_XHCI_RESOURCES_NUM);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "couldn't add resources to xHCI device\n");
|
||||
goto err1;
|
||||
|
@ -41,14 +41,26 @@
|
||||
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
static inline u32 dwc3_readl(void __iomem *base, u32 offset)
|
||||
{
|
||||
return readl(base + offset);
|
||||
/*
|
||||
* We requested the mem region starting from the Globals address
|
||||
* space, see dwc3_probe in core.c.
|
||||
* However, the offsets are given starting from xHCI address space.
|
||||
*/
|
||||
return readl(base + (offset - DWC3_GLOBALS_REGS_START));
|
||||
}
|
||||
|
||||
static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value)
|
||||
{
|
||||
writel(value, base + offset);
|
||||
/*
|
||||
* We requested the mem region starting from the Globals address
|
||||
* space, see dwc3_probe in core.c.
|
||||
* However, the offsets are given starting from xHCI address space.
|
||||
*/
|
||||
writel(value, base + (offset - DWC3_GLOBALS_REGS_START));
|
||||
}
|
||||
|
||||
#endif /* __DRIVERS_USB_DWC3_IO_H */
|
||||
|
@ -88,6 +88,8 @@
|
||||
#define USB_REQ_GET_INTERFACE 0x0A
|
||||
#define USB_REQ_SET_INTERFACE 0x0B
|
||||
#define USB_REQ_SYNCH_FRAME 0x0C
|
||||
#define USB_REQ_SET_SEL 0x30
|
||||
#define USB_REQ_SET_ISOCH_DELAY 0x31
|
||||
|
||||
#define USB_REQ_SET_ENCRYPTION 0x0D /* Wireless USB */
|
||||
#define USB_REQ_GET_ENCRYPTION 0x0E
|
||||
|
Loading…
Reference in New Issue
Block a user