summaryrefslogtreecommitdiff
path: root/drivers/usb/dwc2
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/dwc2')
-rw-r--r--drivers/usb/dwc2/core.c31
-rw-r--r--drivers/usb/dwc2/core.h8
-rw-r--r--drivers/usb/dwc2/gadget.c190
-rw-r--r--drivers/usb/dwc2/hcd.c42
-rw-r--r--drivers/usb/dwc2/hcd.h4
-rw-r--r--drivers/usb/dwc2/hcd_intr.c30
-rw-r--r--drivers/usb/dwc2/hcd_queue.c16
-rw-r--r--drivers/usb/dwc2/platform.c33
8 files changed, 224 insertions, 130 deletions
diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index 27d2c9b8a034..d9269459d481 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -118,6 +118,7 @@ static int dwc2_core_reset(struct dwc2_hsotg *hsotg)
{
u32 greset;
int count = 0;
+ u32 gusbcfg;
dev_vdbg(hsotg->dev, "%s()\n", __func__);
@@ -148,6 +149,23 @@ static int dwc2_core_reset(struct dwc2_hsotg *hsotg)
}
} while (greset & GRSTCTL_CSFTRST);
+ if (hsotg->dr_mode == USB_DR_MODE_HOST) {
+ gusbcfg = readl(hsotg->regs + GUSBCFG);
+ gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
+ gusbcfg |= GUSBCFG_FORCEHOSTMODE;
+ writel(gusbcfg, hsotg->regs + GUSBCFG);
+ } else if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) {
+ gusbcfg = readl(hsotg->regs + GUSBCFG);
+ gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
+ gusbcfg |= GUSBCFG_FORCEDEVMODE;
+ writel(gusbcfg, hsotg->regs + GUSBCFG);
+ } else if (hsotg->dr_mode == USB_DR_MODE_OTG) {
+ gusbcfg = readl(hsotg->regs + GUSBCFG);
+ gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
+ gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
+ writel(gusbcfg, hsotg->regs + GUSBCFG);
+ }
+
/*
* NOTE: This long sleep is _very_ important, otherwise the core will
* not stay in host mode after a connector ID change!
@@ -2674,23 +2692,23 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
hwcfg2 = readl(hsotg->regs + GHWCFG2);
hwcfg3 = readl(hsotg->regs + GHWCFG3);
hwcfg4 = readl(hsotg->regs + GHWCFG4);
- gnptxfsiz = readl(hsotg->regs + GNPTXFSIZ);
grxfsiz = readl(hsotg->regs + GRXFSIZ);
dev_dbg(hsotg->dev, "hwcfg1=%08x\n", hwcfg1);
dev_dbg(hsotg->dev, "hwcfg2=%08x\n", hwcfg2);
dev_dbg(hsotg->dev, "hwcfg3=%08x\n", hwcfg3);
dev_dbg(hsotg->dev, "hwcfg4=%08x\n", hwcfg4);
- dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz);
dev_dbg(hsotg->dev, "grxfsiz=%08x\n", grxfsiz);
- /* Force host mode to get HPTXFSIZ exact power on value */
+ /* Force host mode to get HPTXFSIZ / GNPTXFSIZ exact power on value */
gusbcfg = readl(hsotg->regs + GUSBCFG);
gusbcfg |= GUSBCFG_FORCEHOSTMODE;
writel(gusbcfg, hsotg->regs + GUSBCFG);
usleep_range(100000, 150000);
+ gnptxfsiz = readl(hsotg->regs + GNPTXFSIZ);
hptxfsiz = readl(hsotg->regs + HPTXFSIZ);
+ dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz);
dev_dbg(hsotg->dev, "hptxfsiz=%08x\n", hptxfsiz);
gusbcfg = readl(hsotg->regs + GUSBCFG);
gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
@@ -2725,6 +2743,13 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
width = (hwcfg3 & GHWCFG3_XFER_SIZE_CNTR_WIDTH_MASK) >>
GHWCFG3_XFER_SIZE_CNTR_WIDTH_SHIFT;
hw->max_transfer_size = (1 << (width + 11)) - 1;
+ /*
+ * Clip max_transfer_size to 65535. dwc2_hc_setup_align_buf() allocates
+ * coherent buffers with this size, and if it's too large we can
+ * exhaust the coherent DMA pool.
+ */
+ if (hw->max_transfer_size > 65535)
+ hw->max_transfer_size = 65535;
width = (hwcfg3 & GHWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK) >>
GHWCFG3_PACKET_SIZE_CNTR_WIDTH_SHIFT;
hw->max_packet_count = (1 << (width + 4)) - 1;
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 1efd10cc9629..bf015ab3b44c 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -139,6 +139,7 @@ struct s3c_hsotg_ep {
unsigned int last_load;
unsigned int fifo_load;
unsigned short fifo_size;
+ unsigned short fifo_index;
unsigned char dir_in;
unsigned char index;
@@ -194,8 +195,10 @@ struct s3c_hsotg {
struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsotg_supply_names)];
u32 phyif;
+ int fifo_mem;
unsigned int dedicated_fifos:1;
unsigned char num_of_eps;
+ u32 fifo_map;
struct dentry *debug_root;
struct dentry *debug_file;
@@ -501,6 +504,10 @@ struct dwc2_hw_params {
* a_peripheral and b_device=>b_host) this may not match
* the core, but allows the software to determine
* transitions
+ * @dr_mode: Requested mode of operation, one of following:
+ * - USB_DR_MODE_PERIPHERAL
+ * - USB_DR_MODE_HOST
+ * - USB_DR_MODE_OTG
* @queuing_high_bandwidth: True if multiple packets of a high-bandwidth
* transfer are in process of being queued
* @srp_success: Stores status of SRP request in the case of a FS PHY
@@ -592,6 +599,7 @@ struct dwc2_hsotg {
/** Params to actually use */
struct dwc2_core_params *core_params;
enum usb_otg_state op_state;
+ enum usb_dr_mode dr_mode;
unsigned int queuing_high_bandwidth:1;
unsigned int srp_success:1;
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index 7c9618e916e2..7b5856fadd93 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -182,16 +182,33 @@ static void s3c_hsotg_init_fifo(struct s3c_hsotg *hsotg)
/* start at the end of the GNPTXFSIZ, rounded up */
addr = 2048 + 1024;
- size = 768;
/*
- * currently we allocate TX FIFOs for all possible endpoints,
- * and assume that they are all the same size.
+ * Because we have not enough memory to have each TX FIFO of size at
+ * least 3072 bytes (the maximum single packet size), we create four
+ * FIFOs of lenght 1024, and four of length 3072 bytes, and assing
+ * them to endpoints dynamically according to maxpacket size value of
+ * given endpoint.
*/
- for (ep = 1; ep <= 15; ep++) {
+ /* 256*4=1024 bytes FIFO length */
+ size = 256;
+ for (ep = 1; ep <= 4; ep++) {
+ val = addr;
+ val |= size << FIFOSIZE_DEPTH_SHIFT;
+ WARN_ONCE(addr + size > hsotg->fifo_mem,
+ "insufficient fifo memory");
+ addr += size;
+
+ writel(val, hsotg->regs + DPTXFSIZN(ep));
+ }
+ /* 768*4=3072 bytes FIFO length */
+ size = 768;
+ for (ep = 5; ep <= 8; ep++) {
val = addr;
val |= size << FIFOSIZE_DEPTH_SHIFT;
+ WARN_ONCE(addr + size > hsotg->fifo_mem,
+ "insufficient fifo memory");
addr += size;
writel(val, hsotg->regs + DPTXFSIZN(ep));
@@ -987,8 +1004,8 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg,
hs_req = ep->req;
ep->req = NULL;
list_del_init(&hs_req->queue);
- hs_req->req.complete(&ep->ep,
- &hs_req->req);
+ usb_gadget_giveback_request(&ep->ep,
+ &hs_req->req);
}
/* If we have pending request, then start it */
@@ -1245,7 +1262,7 @@ static void s3c_hsotg_complete_request(struct s3c_hsotg *hsotg,
if (hs_req->req.complete) {
spin_unlock(&hsotg->lock);
- hs_req->req.complete(&hs_ep->ep, &hs_req->req);
+ usb_gadget_giveback_request(&hs_ep->ep, &hs_req->req);
spin_lock(&hsotg->lock);
}
@@ -1649,6 +1666,7 @@ static void s3c_hsotg_txfifo_flush(struct s3c_hsotg *hsotg, unsigned int idx)
dev_err(hsotg->dev,
"%s: timeout flushing fifo (GRSTCTL=%08x)\n",
__func__, val);
+ break;
}
udelay(1);
@@ -1831,7 +1849,7 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx,
if (dir_in) {
int epctl = readl(hsotg->regs + epctl_reg);
- s3c_hsotg_txfifo_flush(hsotg, idx);
+ s3c_hsotg_txfifo_flush(hsotg, hs_ep->fifo_index);
if ((epctl & DXEPCTL_STALL) &&
(epctl & DXEPCTL_EPTYPE_BULK)) {
@@ -1980,6 +1998,7 @@ static void kill_all_requests(struct s3c_hsotg *hsotg,
int result, bool force)
{
struct s3c_hsotg_req *req, *treq;
+ unsigned size;
list_for_each_entry_safe(req, treq, &ep->queue, queue) {
/*
@@ -1993,9 +2012,11 @@ static void kill_all_requests(struct s3c_hsotg *hsotg,
s3c_hsotg_complete_request(hsotg, ep, req,
result);
}
- if (hsotg->dedicated_fifos)
- if ((readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4 < 3072)
- s3c_hsotg_txfifo_flush(hsotg, ep->index);
+ if (!hsotg->dedicated_fifos)
+ return;
+ size = (readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4;
+ if (size < ep->fifo_size)
+ s3c_hsotg_txfifo_flush(hsotg, ep->fifo_index);
}
/**
@@ -2436,6 +2457,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
u32 epctrl;
u32 mps;
int dir_in;
+ int i, val, size;
int ret = 0;
dev_dbg(hsotg->dev,
@@ -2508,17 +2530,8 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
break;
case USB_ENDPOINT_XFER_INT:
- if (dir_in) {
- /*
- * Allocate our TxFNum by simply using the index
- * of the endpoint for the moment. We could do
- * something better if the host indicates how
- * many FIFOs we are expecting to use.
- */
-
+ if (dir_in)
hs_ep->periodic = 1;
- epctrl |= DXEPCTL_TXFNUM(index);
- }
epctrl |= DXEPCTL_EPTYPE_INTERRUPT;
break;
@@ -2532,8 +2545,25 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
* if the hardware has dedicated fifos, we must give each IN EP
* a unique tx-fifo even if it is non-periodic.
*/
- if (dir_in && hsotg->dedicated_fifos)
- epctrl |= DXEPCTL_TXFNUM(index);
+ if (dir_in && hsotg->dedicated_fifos) {
+ size = hs_ep->ep.maxpacket*hs_ep->mc;
+ for (i = 1; i <= 8; ++i) {
+ if (hsotg->fifo_map & (1<<i))
+ continue;
+ val = readl(hsotg->regs + DPTXFSIZN(i));
+ val = (val >> FIFOSIZE_DEPTH_SHIFT)*4;
+ if (val < size)
+ continue;
+ hsotg->fifo_map |= 1<<i;
+
+ epctrl |= DXEPCTL_TXFNUM(i);
+ hs_ep->fifo_index = i;
+ hs_ep->fifo_size = val;
+ break;
+ }
+ if (i == 8)
+ return -ENOMEM;
+ }
/* for non control endpoints, set PID to D0 */
if (index)
@@ -2567,7 +2597,7 @@ static int s3c_hsotg_ep_disable(struct usb_ep *ep)
u32 epctrl_reg;
u32 ctrl;
- dev_info(hsotg->dev, "%s(ep %p)\n", __func__, ep);
+ dev_dbg(hsotg->dev, "%s(ep %p)\n", __func__, ep);
if (ep == &hsotg->eps[0].ep) {
dev_err(hsotg->dev, "%s: called for ep0\n", __func__);
@@ -2580,6 +2610,9 @@ static int s3c_hsotg_ep_disable(struct usb_ep *ep)
/* terminate all requests with shutdown */
kill_all_requests(hsotg, hs_ep, -ESHUTDOWN, false);
+ hsotg->fifo_map &= ~(1<<hs_ep->fifo_index);
+ hs_ep->fifo_index = 0;
+ hs_ep->fifo_size = 0;
ctrl = readl(hsotg->regs + epctrl_reg);
ctrl &= ~DXEPCTL_EPENA;
@@ -2625,7 +2658,7 @@ static int s3c_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
struct s3c_hsotg *hs = hs_ep->parent;
unsigned long flags;
- dev_info(hs->dev, "ep_dequeue(%p,%p)\n", ep, req);
+ dev_dbg(hs->dev, "ep_dequeue(%p,%p)\n", ep, req);
spin_lock_irqsave(&hs->lock, flags);
@@ -2747,13 +2780,14 @@ static void s3c_hsotg_phy_enable(struct s3c_hsotg *hsotg)
dev_dbg(hsotg->dev, "pdev 0x%p\n", pdev);
- if (hsotg->phy) {
- phy_init(hsotg->phy);
- phy_power_on(hsotg->phy);
- } else if (hsotg->uphy)
+ if (hsotg->uphy)
usb_phy_init(hsotg->uphy);
- else if (hsotg->plat->phy_init)
+ else if (hsotg->plat && hsotg->plat->phy_init)
hsotg->plat->phy_init(pdev, hsotg->plat->phy_type);
+ else {
+ phy_init(hsotg->phy);
+ phy_power_on(hsotg->phy);
+ }
}
/**
@@ -2767,13 +2801,14 @@ static void s3c_hsotg_phy_disable(struct s3c_hsotg *hsotg)
{
struct platform_device *pdev = to_platform_device(hsotg->dev);
- if (hsotg->phy) {
- phy_power_off(hsotg->phy);
- phy_exit(hsotg->phy);
- } else if (hsotg->uphy)
+ if (hsotg->uphy)
usb_phy_shutdown(hsotg->uphy);
- else if (hsotg->plat->phy_exit)
+ else if (hsotg->plat && hsotg->plat->phy_exit)
hsotg->plat->phy_exit(pdev, hsotg->plat->phy_type);
+ else {
+ phy_power_off(hsotg->phy);
+ phy_exit(hsotg->phy);
+ }
}
/**
@@ -2858,6 +2893,8 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget,
hsotg->gadget.dev.of_node = hsotg->dev->of_node;
hsotg->gadget.speed = USB_SPEED_UNKNOWN;
+ clk_enable(hsotg->clk);
+
ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies),
hsotg->supplies);
if (ret) {
@@ -2892,13 +2929,11 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget,
return -ENODEV;
/* all endpoints should be shutdown */
- for (ep = 0; ep < hsotg->num_of_eps; ep++)
+ for (ep = 1; ep < hsotg->num_of_eps; ep++)
s3c_hsotg_ep_disable(&hsotg->eps[ep].ep);
spin_lock_irqsave(&hsotg->lock, flags);
- s3c_hsotg_phy_disable(hsotg);
-
if (!driver)
hsotg->driver = NULL;
@@ -2908,6 +2943,8 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget,
regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
+ clk_disable(hsotg->clk);
+
return 0;
}
@@ -2934,14 +2971,15 @@ static int s3c_hsotg_pullup(struct usb_gadget *gadget, int is_on)
struct s3c_hsotg *hsotg = to_hsotg(gadget);
unsigned long flags = 0;
- dev_dbg(hsotg->dev, "%s: is_in: %d\n", __func__, is_on);
+ dev_dbg(hsotg->dev, "%s: is_on: %d\n", __func__, is_on);
spin_lock_irqsave(&hsotg->lock, flags);
if (is_on) {
s3c_hsotg_phy_enable(hsotg);
+ clk_enable(hsotg->clk);
s3c_hsotg_core_init(hsotg);
} else {
- s3c_hsotg_disconnect(hsotg);
+ clk_disable(hsotg->clk);
s3c_hsotg_phy_disable(hsotg);
}
@@ -2972,7 +3010,6 @@ static void s3c_hsotg_initep(struct s3c_hsotg *hsotg,
struct s3c_hsotg_ep *hs_ep,
int epnum)
{
- u32 ptxfifo;
char *dir;
if (epnum == 0)
@@ -3001,15 +3038,6 @@ static void s3c_hsotg_initep(struct s3c_hsotg *hsotg,
hs_ep->ep.ops = &s3c_hsotg_ep_ops;
/*
- * Read the FIFO size for the Periodic TX FIFO, even if we're
- * an OUT endpoint, we may as well do this if in future the
- * code is changed to make each endpoint's direction changeable.
- */
-
- ptxfifo = readl(hsotg->regs + DPTXFSIZN(epnum));
- hs_ep->fifo_size = FIFOSIZE_DEPTH_GET(ptxfifo) * 4;
-
- /*
* if we're using dma, we need to set the next-endpoint pointer
* to be something valid.
*/
@@ -3029,19 +3057,22 @@ static void s3c_hsotg_initep(struct s3c_hsotg *hsotg,
*/
static void s3c_hsotg_hw_cfg(struct s3c_hsotg *hsotg)
{
- u32 cfg2, cfg4;
+ u32 cfg2, cfg3, cfg4;
/* check hardware configuration */
cfg2 = readl(hsotg->regs + 0x48);
hsotg->num_of_eps = (cfg2 >> 10) & 0xF;
- dev_info(hsotg->dev, "EPs:%d\n", hsotg->num_of_eps);
+ cfg3 = readl(hsotg->regs + 0x4C);
+ hsotg->fifo_mem = (cfg3 >> 16);
cfg4 = readl(hsotg->regs + 0x50);
hsotg->dedicated_fifos = (cfg4 >> 25) & 1;
- dev_info(hsotg->dev, "%s fifos\n",
- hsotg->dedicated_fifos ? "dedicated" : "shared");
+ dev_info(hsotg->dev, "EPs: %d, %s fifos, %d entries in SPRAM\n",
+ hsotg->num_of_eps,
+ hsotg->dedicated_fifos ? "dedicated" : "shared",
+ hsotg->fifo_mem);
}
/**
@@ -3392,6 +3423,9 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
if (!hsotg)
return -ENOMEM;
+ /* Set default UTMI width */
+ hsotg->phyif = GUSBCFG_PHYIF16;
+
/*
* Attempt to find a generic PHY, then look for an old style
* USB PHY, finally fall back to pdata
@@ -3410,8 +3444,15 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
hsotg->plat = plat;
} else
hsotg->uphy = uphy;
- } else
+ } else {
hsotg->phy = phy;
+ /*
+ * If using the generic PHY framework, check if the PHY bus
+ * width is 8-bit and set the phyif appropriately.
+ */
+ if (phy_get_bus_width(phy) == 8)
+ hsotg->phyif = GUSBCFG_PHYIF8;
+ }
hsotg->dev = dev;
@@ -3441,13 +3482,6 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
hsotg->irq = ret;
- ret = devm_request_irq(&pdev->dev, hsotg->irq, s3c_hsotg_irq, 0,
- dev_name(dev), hsotg);
- if (ret < 0) {
- dev_err(dev, "cannot claim IRQ\n");
- goto err_clk;
- }
-
dev_info(dev, "regs %p, irq %d\n", hsotg->regs, hsotg->irq);
hsotg->gadget.max_speed = USB_SPEED_HIGH;
@@ -3478,25 +3512,23 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
goto err_supplies;
}
- /* Set default UTMI width */
- hsotg->phyif = GUSBCFG_PHYIF16;
-
- /*
- * If using the generic PHY framework, check if the PHY bus
- * width is 8-bit and set the phyif appropriately.
- */
- if (hsotg->phy && (phy_get_bus_width(phy) == 8))
- hsotg->phyif = GUSBCFG_PHYIF8;
-
- if (hsotg->phy)
- phy_init(hsotg->phy);
-
/* usb phy enable */
s3c_hsotg_phy_enable(hsotg);
s3c_hsotg_corereset(hsotg);
- s3c_hsotg_init(hsotg);
s3c_hsotg_hw_cfg(hsotg);
+ s3c_hsotg_init(hsotg);
+
+ ret = devm_request_irq(&pdev->dev, hsotg->irq, s3c_hsotg_irq, 0,
+ dev_name(dev), hsotg);
+ if (ret < 0) {
+ s3c_hsotg_phy_disable(hsotg);
+ clk_disable_unprepare(hsotg->clk);
+ regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies),
+ hsotg->supplies);
+ dev_err(dev, "cannot claim IRQ\n");
+ goto err_clk;
+ }
/* hsotg->num_of_eps holds number of EPs other than ep0 */
@@ -3582,9 +3614,6 @@ static int s3c_hsotg_remove(struct platform_device *pdev)
usb_gadget_unregister_driver(hsotg->driver);
}
- s3c_hsotg_phy_disable(hsotg);
- if (hsotg->phy)
- phy_exit(hsotg->phy);
clk_disable_unprepare(hsotg->clk);
return 0;
@@ -3613,6 +3642,7 @@ static int s3c_hsotg_suspend(struct platform_device *pdev, pm_message_t state)
ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies),
hsotg->supplies);
+ clk_disable(hsotg->clk);
}
return ret;
@@ -3627,6 +3657,8 @@ static int s3c_hsotg_resume(struct platform_device *pdev)
if (hsotg->driver) {
dev_info(hsotg->dev, "resuming usb gadget %s\n",
hsotg->driver->driver.name);
+
+ clk_enable(hsotg->clk);
ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies),
hsotg->supplies);
}
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 4d918ed8d343..0a0e6f0ad15f 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -697,29 +697,45 @@ static void *dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
}
static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
- struct dwc2_host_chan *chan, void *bufptr)
+ struct dwc2_host_chan *chan,
+ struct dwc2_hcd_urb *urb, void *bufptr)
{
u32 buf_size;
-
- if (chan->ep_type != USB_ENDPOINT_XFER_ISOC)
- buf_size = hsotg->core_params->max_transfer_size;
- else
- buf_size = 4096;
+ struct urb *usb_urb;
+ struct usb_hcd *hcd;
if (!qh->dw_align_buf) {
+ if (chan->ep_type != USB_ENDPOINT_XFER_ISOC)
+ buf_size = hsotg->core_params->max_transfer_size;
+ else
+ /* 3072 = 3 max-size Isoc packets */
+ buf_size = 3072;
+
qh->dw_align_buf = dma_alloc_coherent(hsotg->dev, buf_size,
&qh->dw_align_buf_dma,
GFP_ATOMIC);
if (!qh->dw_align_buf)
return -ENOMEM;
+ qh->dw_align_buf_size = buf_size;
}
- if (!chan->ep_is_in && chan->xfer_len) {
- dma_sync_single_for_cpu(hsotg->dev, chan->xfer_dma, buf_size,
- DMA_TO_DEVICE);
- memcpy(qh->dw_align_buf, bufptr, chan->xfer_len);
- dma_sync_single_for_device(hsotg->dev, chan->xfer_dma, buf_size,
- DMA_TO_DEVICE);
+ if (chan->xfer_len) {
+ dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
+ usb_urb = urb->priv;
+
+ if (usb_urb) {
+ if (usb_urb->transfer_flags &
+ (URB_SETUP_MAP_SINGLE | URB_DMA_MAP_SG |
+ URB_DMA_MAP_PAGE | URB_DMA_MAP_SINGLE)) {
+ hcd = dwc2_hsotg_to_hcd(hsotg);
+ usb_hcd_unmap_urb_for_dma(hcd, usb_urb);
+ }
+ if (!chan->ep_is_in)
+ memcpy(qh->dw_align_buf, bufptr,
+ chan->xfer_len);
+ } else {
+ dev_warn(hsotg->dev, "no URB in dwc2_urb\n");
+ }
}
chan->align_buf = qh->dw_align_buf_dma;
@@ -828,7 +844,7 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
/* Non DWORD-aligned buffer case */
if (bufptr) {
dev_vdbg(hsotg->dev, "Non-aligned buffer\n");
- if (dwc2_hc_setup_align_buf(hsotg, qh, chan, bufptr)) {
+ if (dwc2_hc_setup_align_buf(hsotg, qh, chan, urb, bufptr)) {
dev_err(hsotg->dev,
"%s: Failed to allocate memory to handle non-dword aligned buffer\n",
__func__);
diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index fdc6d489084a..a12bb1538666 100644
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -243,7 +243,8 @@ enum dwc2_transaction_type {
* @ntd: Actual number of transfer descriptors in a list
* @dw_align_buf: Used instead of original buffer if its physical address
* is not dword-aligned
- * @dw_align_buf_dma: DMA address for align_buf
+ * @dw_align_buf_size: Size of dw_align_buf
+ * @dw_align_buf_dma: DMA address for dw_align_buf
* @qtd_list: List of QTDs for this QH
* @channel: Host channel currently processing transfers for this QH
* @qh_list_entry: Entry for QH in either the periodic or non-periodic
@@ -276,6 +277,7 @@ struct dwc2_qh {
u16 start_split_frame;
u16 ntd;
u8 *dw_align_buf;
+ int dw_align_buf_size;
dma_addr_t dw_align_buf_dma;
struct list_head qtd_list;
struct dwc2_host_chan *channel;
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index 47b9eb5389b4..551ba878b003 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -465,12 +465,8 @@ static int dwc2_update_urb_state(struct dwc2_hsotg *hsotg,
/* Non DWORD-aligned buffer case handling */
if (chan->align_buf && xfer_length && chan->ep_is_in) {
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
- dma_sync_single_for_cpu(hsotg->dev, urb->dma, urb->length,
- DMA_FROM_DEVICE);
memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf,
xfer_length);
- dma_sync_single_for_device(hsotg->dev, urb->dma, urb->length,
- DMA_FROM_DEVICE);
}
dev_vdbg(hsotg->dev, "urb->actual_length=%d xfer_length=%d\n",
@@ -560,14 +556,9 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state(
chan->ep_is_in) {
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n",
__func__);
- dma_sync_single_for_cpu(hsotg->dev, urb->dma,
- urb->length, DMA_FROM_DEVICE);
memcpy(urb->buf + frame_desc->offset +
qtd->isoc_split_offset, chan->qh->dw_align_buf,
frame_desc->actual_length);
- dma_sync_single_for_device(hsotg->dev, urb->dma,
- urb->length,
- DMA_FROM_DEVICE);
}
break;
case DWC2_HC_XFER_FRAME_OVERRUN:
@@ -594,14 +585,9 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state(
chan->ep_is_in) {
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n",
__func__);
- dma_sync_single_for_cpu(hsotg->dev, urb->dma,
- urb->length, DMA_FROM_DEVICE);
memcpy(urb->buf + frame_desc->offset +
qtd->isoc_split_offset, chan->qh->dw_align_buf,
frame_desc->actual_length);
- dma_sync_single_for_device(hsotg->dev, urb->dma,
- urb->length,
- DMA_FROM_DEVICE);
}
/* Skip whole frame */
@@ -937,12 +923,8 @@ static int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg,
if (chan->align_buf) {
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
- dma_sync_single_for_cpu(hsotg->dev, qtd->urb->dma,
- qtd->urb->length, DMA_FROM_DEVICE);
memcpy(qtd->urb->buf + frame_desc->offset +
qtd->isoc_split_offset, chan->qh->dw_align_buf, len);
- dma_sync_single_for_device(hsotg->dev, qtd->urb->dma,
- qtd->urb->length, DMA_FROM_DEVICE);
}
qtd->isoc_split_offset += len;
@@ -1170,12 +1152,8 @@ static void dwc2_update_urb_state_abn(struct dwc2_hsotg *hsotg,
/* Non DWORD-aligned buffer case handling */
if (chan->align_buf && xfer_length && chan->ep_is_in) {
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
- dma_sync_single_for_cpu(hsotg->dev, urb->dma, urb->length,
- DMA_FROM_DEVICE);
memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf,
xfer_length);
- dma_sync_single_for_device(hsotg->dev, urb->dma, urb->length,
- DMA_FROM_DEVICE);
}
urb->actual_length += xfer_length;
@@ -1890,12 +1868,20 @@ static void dwc2_hc_chhltd_intr_dma(struct dwc2_hsotg *hsotg,
"hcint 0x%08x, intsts 0x%08x\n",
chan->hcint,
readl(hsotg->regs + GINTSTS));
+ goto error;
}
}
} else {
dev_info(hsotg->dev,
"NYET/NAK/ACK/other in non-error case, 0x%08x\n",
chan->hcint);
+error:
+ /* Failthrough: use 3-strikes rule */
+ qtd->error_count++;
+ dwc2_update_urb_state_abn(hsotg, chan, chnum, qtd->urb,
+ qtd, DWC2_HC_XFER_XACT_ERR);
+ dwc2_hcd_save_data_toggle(hsotg, chan, chnum, qtd);
+ dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_XACT_ERR);
}
}
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 9540f7e1e20e..bb97838bc6c0 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -229,19 +229,11 @@ static struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
*/
void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
{
- u32 buf_size;
-
- if (hsotg->core_params->dma_desc_enable > 0) {
+ if (hsotg->core_params->dma_desc_enable > 0)
dwc2_hcd_qh_free_ddma(hsotg, qh);
- } else if (qh->dw_align_buf) {
- if (qh->ep_type == USB_ENDPOINT_XFER_ISOC)
- buf_size = 4096;
- else
- buf_size = hsotg->core_params->max_transfer_size;
- dma_free_coherent(hsotg->dev, buf_size, qh->dw_align_buf,
- qh->dw_align_buf_dma);
- }
-
+ else if (qh->dw_align_buf)
+ dma_free_coherent(hsotg->dev, qh->dw_align_buf_size,
+ qh->dw_align_buf, qh->dw_align_buf_dma);
kfree(qh);
}
diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
index a10e7a353576..121dbdafc06b 100644
--- a/drivers/usb/dwc2/platform.c
+++ b/drivers/usb/dwc2/platform.c
@@ -42,6 +42,8 @@
#include <linux/of_device.h>
#include <linux/platform_device.h>
+#include <linux/usb/of.h>
+
#include "core.h"
#include "hcd.h"
@@ -75,6 +77,34 @@ static const struct dwc2_core_params params_bcm2835 = {
.uframe_sched = 0,
};
+static const struct dwc2_core_params params_rk3066 = {
+ .otg_cap = 2, /* non-HNP/non-SRP */
+ .otg_ver = -1,
+ .dma_enable = -1,
+ .dma_desc_enable = 0,
+ .speed = -1,
+ .enable_dynamic_fifo = 1,
+ .en_multiple_tx_fifo = -1,
+ .host_rx_fifo_size = 520, /* 520 DWORDs */
+ .host_nperio_tx_fifo_size = 128, /* 128 DWORDs */
+ .host_perio_tx_fifo_size = 256, /* 256 DWORDs */
+ .max_transfer_size = 65535,
+ .max_packet_count = -1,
+ .host_channels = -1,
+ .phy_type = -1,
+ .phy_utmi_width = -1,
+ .phy_ulpi_ddr = -1,
+ .phy_ulpi_ext_vbus = -1,
+ .i2c_enable = -1,
+ .ulpi_fs_ls = -1,
+ .host_support_fs_ls_low_power = -1,
+ .host_ls_low_power_phy_clk = -1,
+ .ts_dline = -1,
+ .reload_ctl = -1,
+ .ahbcfg = 0x7, /* INCR16 */
+ .uframe_sched = -1,
+};
+
/**
* dwc2_driver_remove() - Called when the DWC_otg core is unregistered with the
* DWC_otg driver
@@ -97,6 +127,7 @@ static int dwc2_driver_remove(struct platform_device *dev)
static const struct of_device_id dwc2_of_match_table[] = {
{ .compatible = "brcm,bcm2835-usb", .data = &params_bcm2835 },
+ { .compatible = "rockchip,rk3066-usb", .data = &params_rk3066 },
{ .compatible = "snps,dwc2", .data = NULL },
{},
};
@@ -171,6 +202,8 @@ static int dwc2_driver_probe(struct platform_device *dev)
dev_dbg(&dev->dev, "mapped PA %08lx to VA %p\n",
(unsigned long)res->start, hsotg->regs);
+ hsotg->dr_mode = of_usb_get_dr_mode(dev->dev.of_node);
+
retval = dwc2_hcd_init(hsotg, irq, params);
if (retval)
return retval;