summaryrefslogtreecommitdiffstats
path: root/drivers/usb/dwc3
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-01-26 15:36:28 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-01-26 15:36:28 +0100
commitbc46e23c34259c842a138f10acc371e4e2cdb287 (patch)
tree4f6d6938f824abae24e0bb9c563cf07522a19c89 /drivers/usb/dwc3
parentxhci: refactor xhci_urb_enqueue (diff)
parentusb: dwc3: host: pass quirk-broken-port-ped property for known broken revisions (diff)
downloadlinux-bc46e23c34259c842a138f10acc371e4e2cdb287.tar.xz
linux-bc46e23c34259c842a138f10acc371e4e2cdb287.zip
Merge tag 'usb-for-v4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
Felipe writes: USB: changes for v4.11 Here's the big pull request for the Gadget API. Again the majority of changes sit in dwc2 driver. Most important changes contain a workaround for GOTGCTL being wrong, a sleep-inside-spinlock fix and the big series of cleanups on dwc2. One important thing on dwc3 is that we don't anymore need gadget drivers to cope with unaligned OUT transfers for us. We have support for appending one extra chained TRB to align transfer ourselves. Apart from these, the usual set of typos, non-critical fixes, etc.
Diffstat (limited to 'drivers/usb/dwc3')
-rw-r--r--drivers/usb/dwc3/core.h5
-rw-r--r--drivers/usb/dwc3/dwc3-exynos.c5
-rw-r--r--drivers/usb/dwc3/dwc3-omap.c20
-rw-r--r--drivers/usb/dwc3/ep0.c14
-rw-r--r--drivers/usb/dwc3/gadget.c194
-rw-r--r--drivers/usb/dwc3/host.c21
6 files changed, 197 insertions, 62 deletions
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 14b760209680..2b9e4ca3c932 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -40,6 +40,7 @@
/* Global constants */
#define DWC3_PULL_UP_TIMEOUT 500 /* ms */
#define DWC3_ZLP_BUF_SIZE 1024 /* size of a superspeed bulk */
+#define DWC3_BOUNCE_SIZE 1024 /* size of a superspeed bulk */
#define DWC3_EP0_BOUNCE_SIZE 512
#define DWC3_ENDPOINTS_NUM 32
#define DWC3_XHCI_RESOURCES_NUM 2
@@ -724,6 +725,7 @@ struct dwc3_hwparams {
* @epnum: endpoint number to which this request refers
* @trb: pointer to struct dwc3_trb
* @trb_dma: DMA address of @trb
+ * @unaligned: true for OUT endpoints with length not divisible by maxp
* @direction: IN or OUT direction flag
* @mapped: true when request has been dma-mapped
* @queued: true when request has been queued to HW
@@ -740,6 +742,7 @@ struct dwc3_request {
struct dwc3_trb *trb;
dma_addr_t trb_dma;
+ unsigned unaligned:1;
unsigned direction:1;
unsigned mapped:1;
unsigned started:1;
@@ -857,12 +860,14 @@ struct dwc3_scratchpad_array {
struct dwc3 {
struct usb_ctrlrequest *ctrl_req;
struct dwc3_trb *ep0_trb;
+ void *bounce;
void *ep0_bounce;
void *zlp_buf;
void *scratchbuf;
u8 *setup_buf;
dma_addr_t ctrl_req_addr;
dma_addr_t ep0_trb_addr;
+ dma_addr_t bounce_addr;
dma_addr_t ep0_bounce_addr;
dma_addr_t scratch_addr;
struct dwc3_request ep0_usb_req;
diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c
index e956306d9b0f..1515d45ebcec 100644
--- a/drivers/usb/dwc3/dwc3-exynos.c
+++ b/drivers/usb/dwc3/dwc3-exynos.c
@@ -128,10 +128,8 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
clk_prepare_enable(exynos->clk);
exynos->susp_clk = devm_clk_get(dev, "usbdrd30_susp_clk");
- if (IS_ERR(exynos->susp_clk)) {
- dev_info(dev, "no suspend clk specified\n");
+ if (IS_ERR(exynos->susp_clk))
exynos->susp_clk = NULL;
- }
clk_prepare_enable(exynos->susp_clk);
if (of_device_is_compatible(node, "samsung,exynos7-dwusb3")) {
@@ -290,7 +288,6 @@ static struct platform_driver dwc3_exynos_driver = {
module_platform_driver(dwc3_exynos_driver);
-MODULE_ALIAS("platform:exynos-dwc3");
MODULE_AUTHOR("Anton Tikhomirov <av.tikhomirov@samsung.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("DesignWare USB3 EXYNOS Glue Layer");
diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index eb1b9cb3f9d1..2092e46b1380 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -426,20 +426,20 @@ static int dwc3_omap_extcon_register(struct dwc3_omap *omap)
}
omap->vbus_nb.notifier_call = dwc3_omap_vbus_notifier;
- ret = extcon_register_notifier(edev, EXTCON_USB,
- &omap->vbus_nb);
+ ret = devm_extcon_register_notifier(omap->dev, edev,
+ EXTCON_USB, &omap->vbus_nb);
if (ret < 0)
dev_vdbg(omap->dev, "failed to register notifier for USB\n");
omap->id_nb.notifier_call = dwc3_omap_id_notifier;
- ret = extcon_register_notifier(edev, EXTCON_USB_HOST,
- &omap->id_nb);
+ ret = devm_extcon_register_notifier(omap->dev, edev,
+ EXTCON_USB_HOST, &omap->id_nb);
if (ret < 0)
dev_vdbg(omap->dev, "failed to register notifier for USB-HOST\n");
- if (extcon_get_cable_state_(edev, EXTCON_USB) == true)
+ if (extcon_get_state(edev, EXTCON_USB) == true)
dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_VALID);
- if (extcon_get_cable_state_(edev, EXTCON_USB_HOST) == true)
+ if (extcon_get_state(edev, EXTCON_USB_HOST) == true)
dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_GROUND);
omap->edev = edev;
@@ -528,17 +528,13 @@ static int dwc3_omap_probe(struct platform_device *pdev)
ret = of_platform_populate(node, NULL, NULL, dev);
if (ret) {
dev_err(&pdev->dev, "failed to create dwc3 core\n");
- goto err2;
+ goto err1;
}
dwc3_omap_enable_irqs(omap);
enable_irq(omap->irq);
return 0;
-err2:
- extcon_unregister_notifier(omap->edev, EXTCON_USB, &omap->vbus_nb);
- extcon_unregister_notifier(omap->edev, EXTCON_USB_HOST, &omap->id_nb);
-
err1:
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
@@ -550,8 +546,6 @@ static int dwc3_omap_remove(struct platform_device *pdev)
{
struct dwc3_omap *omap = platform_get_drvdata(pdev);
- extcon_unregister_notifier(omap->edev, EXTCON_USB, &omap->vbus_nb);
- extcon_unregister_notifier(omap->edev, EXTCON_USB_HOST, &omap->id_nb);
dwc3_omap_disable_irqs(omap);
disable_irq(omap->irq);
of_platform_depopulate(omap->dev);
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 9bb1f8526f3e..e689cede9b0e 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -1123,7 +1123,21 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
dwc->ep0state = EP0_STATUS_PHASE;
if (dwc->delayed_status) {
+ struct dwc3_ep *dep = dwc->eps[0];
+
WARN_ON_ONCE(event->endpoint_number != 1);
+ /*
+ * We should handle the delay STATUS phase here if the
+ * request for handling delay STATUS has been queued
+ * into the list.
+ */
+ if (!list_empty(&dep->pending_list)) {
+ dwc->delayed_status = false;
+ usb_gadget_set_state(&dwc->gadget,
+ USB_STATE_CONFIGURED);
+ dwc3_ep0_do_control_status(dwc, event);
+ }
+
return;
}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 204c754cc647..4db97ecae885 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -833,29 +833,14 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep);
-/**
- * dwc3_prepare_one_trb - setup one TRB from one request
- * @dep: endpoint for which this request is prepared
- * @req: dwc3_request pointer
- */
-static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
- struct dwc3_request *req, dma_addr_t dma,
- unsigned length, unsigned chain, unsigned node)
+static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
+ dma_addr_t dma, unsigned length, unsigned chain, unsigned node,
+ unsigned stream_id, unsigned short_not_ok, unsigned no_interrupt)
{
- struct dwc3_trb *trb;
struct dwc3 *dwc = dep->dwc;
struct usb_gadget *gadget = &dwc->gadget;
enum usb_device_speed speed = gadget->speed;
- trb = &dep->trb_pool[dep->trb_enqueue];
-
- if (!req->trb) {
- dwc3_gadget_move_started_request(req);
- req->trb = trb;
- req->trb_dma = dwc3_trb_dma_offset(dep, trb);
- dep->queued_requests++;
- }
-
dwc3_ep_inc_enq(dep);
trb->size = DWC3_TRB_SIZE_LENGTH(length);
@@ -900,11 +885,11 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
if (usb_endpoint_dir_out(dep->endpoint.desc)) {
trb->ctrl |= DWC3_TRB_CTRL_CSP;
- if (req->request.short_not_ok)
+ if (short_not_ok)
trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
}
- if ((!req->request.no_interrupt && !chain) ||
+ if ((!no_interrupt && !chain) ||
(dwc3_calc_trbs_left(dep) == 0))
trb->ctrl |= DWC3_TRB_CTRL_IOC;
@@ -912,7 +897,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
trb->ctrl |= DWC3_TRB_CTRL_CHN;
if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable)
- trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(req->request.stream_id);
+ trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(stream_id);
trb->ctrl |= DWC3_TRB_CTRL_HWO;
@@ -920,6 +905,36 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
}
/**
+ * dwc3_prepare_one_trb - setup one TRB from one request
+ * @dep: endpoint for which this request is prepared
+ * @req: dwc3_request pointer
+ * @chain: should this TRB be chained to the next?
+ * @node: only for isochronous endpoints. First TRB needs different type.
+ */
+static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
+ struct dwc3_request *req, unsigned chain, unsigned node)
+{
+ struct dwc3_trb *trb;
+ unsigned length = req->request.length;
+ unsigned stream_id = req->request.stream_id;
+ unsigned short_not_ok = req->request.short_not_ok;
+ unsigned no_interrupt = req->request.no_interrupt;
+ dma_addr_t dma = req->request.dma;
+
+ trb = &dep->trb_pool[dep->trb_enqueue];
+
+ if (!req->trb) {
+ dwc3_gadget_move_started_request(req);
+ req->trb = trb;
+ req->trb_dma = dwc3_trb_dma_offset(dep, trb);
+ dep->queued_requests++;
+ }
+
+ __dwc3_prepare_one_trb(dep, trb, dma, length, chain, node,
+ stream_id, short_not_ok, no_interrupt);
+}
+
+/**
* dwc3_ep_prev_trb() - Returns the previous TRB in the ring
* @dep: The endpoint with the TRB ring
* @index: The index of the current TRB in the ring
@@ -974,21 +989,36 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
{
struct scatterlist *sg = req->sg;
struct scatterlist *s;
- unsigned int length;
- dma_addr_t dma;
int i;
for_each_sg(sg, s, req->num_pending_sgs, i) {
+ unsigned int length = req->request.length;
+ unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
+ unsigned int rem = length % maxp;
unsigned chain = true;
- length = sg_dma_len(s);
- dma = sg_dma_address(s);
-
if (sg_is_last(s))
chain = false;
- dwc3_prepare_one_trb(dep, req, dma, length,
- chain, i);
+ if (rem && usb_endpoint_dir_out(dep->endpoint.desc) && !chain) {
+ struct dwc3 *dwc = dep->dwc;
+ struct dwc3_trb *trb;
+
+ req->unaligned = true;
+
+ /* prepare normal TRB */
+ dwc3_prepare_one_trb(dep, req, true, i);
+
+ /* Now prepare one extra TRB to align transfer size */
+ trb = &dep->trb_pool[dep->trb_enqueue];
+ __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr,
+ maxp - rem, false, 0,
+ req->request.stream_id,
+ req->request.short_not_ok,
+ req->request.no_interrupt);
+ } else {
+ dwc3_prepare_one_trb(dep, req, chain, i);
+ }
if (!dwc3_calc_trbs_left(dep))
break;
@@ -998,14 +1028,28 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
struct dwc3_request *req)
{
- unsigned int length;
- dma_addr_t dma;
+ unsigned int length = req->request.length;
+ unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
+ unsigned int rem = length % maxp;
+
+ if (rem && usb_endpoint_dir_out(dep->endpoint.desc)) {
+ struct dwc3 *dwc = dep->dwc;
+ struct dwc3_trb *trb;
- dma = req->request.dma;
- length = req->request.length;
+ req->unaligned = true;
- dwc3_prepare_one_trb(dep, req, dma, length,
- false, 0);
+ /* prepare normal TRB */
+ dwc3_prepare_one_trb(dep, req, true, 0);
+
+ /* Now prepare one extra TRB to align transfer size */
+ trb = &dep->trb_pool[dep->trb_enqueue];
+ __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem,
+ false, 0, req->request.stream_id,
+ req->request.short_not_ok,
+ req->request.no_interrupt);
+ } else {
+ dwc3_prepare_one_trb(dep, req, false, 0);
+ }
}
/*
@@ -1335,6 +1379,9 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
unsigned transfer_in_flight;
unsigned started;
+ if (dep->flags & DWC3_EP_STALL)
+ return 0;
+
if (dep->number > 1)
trb = dwc3_ep_prev_trb(dep, dep->trb_enqueue);
else
@@ -1356,6 +1403,8 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
else
dep->flags |= DWC3_EP_STALL;
} else {
+ if (!(dep->flags & DWC3_EP_STALL))
+ return 0;
ret = dwc3_send_clear_stall_ep_cmd(dep);
if (ret)
@@ -1918,6 +1967,44 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
dep->endpoint.ops = &dwc3_gadget_ep0_ops;
if (!epnum)
dwc->gadget.ep0 = &dep->endpoint;
+ } else if (direction) {
+ int mdwidth;
+ int size;
+ int ret;
+ int num;
+
+ mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
+ /* MDWIDTH is represented in bits, we need it in bytes */
+ mdwidth /= 8;
+
+ size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(i));
+ size = DWC3_GTXFIFOSIZ_TXFDEF(size);
+
+ /* FIFO Depth is in MDWDITH bytes. Multiply */
+ size *= mdwidth;
+
+ num = size / 1024;
+ if (num == 0)
+ num = 1;
+
+ /*
+ * FIFO sizes account an extra MDWIDTH * (num + 1) bytes for
+ * internal overhead. We don't really know how these are used,
+ * but documentation say it exists.
+ */
+ size -= mdwidth * (num + 1);
+ size /= num;
+
+ usb_ep_set_maxpacket_limit(&dep->endpoint, size);
+
+ dep->endpoint.max_streams = 15;
+ dep->endpoint.ops = &dwc3_gadget_ep_ops;
+ list_add_tail(&dep->endpoint.ep_list,
+ &dwc->gadget.ep_list);
+
+ ret = dwc3_alloc_trb_pool(dep);
+ if (ret)
+ return ret;
} else {
int ret;
@@ -2029,6 +2116,16 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
if (chain && (trb->ctrl & DWC3_TRB_CTRL_HWO))
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+ /*
+ * If we're dealing with unaligned size OUT transfer, we will be left
+ * with one TRB pending in the ring. We need to manually clear HWO bit
+ * from that TRB.
+ */
+ if (req->unaligned && (trb->ctrl & DWC3_TRB_CTRL_HWO)) {
+ trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+ return 1;
+ }
+
if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN)
return 1;
@@ -2118,6 +2215,13 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
event, status, chain);
}
+ if (req->unaligned) {
+ trb = &dep->trb_pool[dep->trb_dequeue];
+ ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
+ event, status, false);
+ req->unaligned = false;
+ }
+
req->request.actual = length - req->remaining;
if ((req->request.actual < length) && req->num_pending_sgs)
@@ -3019,6 +3123,13 @@ int dwc3_gadget_init(struct dwc3 *dwc)
goto err4;
}
+ dwc->bounce = dma_alloc_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE,
+ &dwc->bounce_addr, GFP_KERNEL);
+ if (!dwc->bounce) {
+ ret = -ENOMEM;
+ goto err5;
+ }
+
init_completion(&dwc->ep0_in_setup);
dwc->gadget.ops = &dwc3_gadget_ops;
@@ -3050,27 +3161,24 @@ int dwc3_gadget_init(struct dwc3 *dwc)
dwc->gadget.max_speed = dwc->maximum_speed;
/*
- * Per databook, DWC3 needs buffer size to be aligned to MaxPacketSize
- * on ep out.
- */
- dwc->gadget.quirk_ep_out_aligned_size = true;
-
- /*
* REVISIT: Here we should clear all pending IRQs to be
* sure we're starting from a well known location.
*/
ret = dwc3_gadget_init_endpoints(dwc);
if (ret)
- goto err5;
+ goto err6;
ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
if (ret) {
dev_err(dwc->dev, "failed to register udc\n");
- goto err5;
+ goto err6;
}
return 0;
+err6:
+ dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
+ dwc->bounce_addr);
err5:
kfree(dwc->zlp_buf);
@@ -3103,6 +3211,8 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
dwc3_gadget_free_endpoints(dwc);
+ dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
+ dwc->bounce_addr);
dma_free_coherent(dwc->sysdev, DWC3_EP0_BOUNCE_SIZE,
dwc->ep0_bounce, dwc->ep0_bounce_addr);
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index 487f0ff6ae25..76f0b0df37c1 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -54,11 +54,12 @@ out:
int dwc3_host_init(struct dwc3 *dwc)
{
- struct property_entry props[2];
+ struct property_entry props[3];
struct platform_device *xhci;
int ret, irq;
struct resource *res;
struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
+ int prop_idx = 0;
irq = dwc3_host_get_irq(dwc);
if (irq < 0)
@@ -97,8 +98,22 @@ int dwc3_host_init(struct dwc3 *dwc)
memset(props, 0, sizeof(struct property_entry) * ARRAY_SIZE(props));
- if (dwc->usb3_lpm_capable) {
- props[0].name = "usb3-lpm-capable";
+ if (dwc->usb3_lpm_capable)
+ props[prop_idx++].name = "usb3-lpm-capable";
+
+ /**
+ * WORKAROUND: dwc3 revisions <=3.00a have a limitation
+ * where Port Disable command doesn't work.
+ *
+ * The suggested workaround is that we avoid Port Disable
+ * completely.
+ *
+ * This following flag tells XHCI to do just that.
+ */
+ if (dwc->revision <= DWC3_REVISION_300A)
+ props[prop_idx++].name = "quirk-broken-port-ped";
+
+ if (prop_idx) {
ret = platform_device_add_properties(xhci, props);
if (ret) {
dev_err(dwc->dev, "failed to add properties to xHCI\n");