diff options
Diffstat (limited to 'drivers/usb/gadget/udc')
-rw-r--r-- | drivers/usb/gadget/udc/core.c | 4 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/goku_udc.c | 2 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/net2280.c | 80 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/pxa27x_udc.c | 51 |
4 files changed, 128 insertions, 9 deletions
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index 40c04bb..9483489 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -107,10 +107,8 @@ int usb_ep_enable(struct usb_ep *ep) goto out; ret = ep->ops->enable(ep, ep->desc); - if (ret) { - ret = ret; + if (ret) goto out; - } ep->enabled = true; diff --git a/drivers/usb/gadget/udc/goku_udc.c b/drivers/usb/gadget/udc/goku_udc.c index 1400415..5107987 100644 --- a/drivers/usb/gadget/udc/goku_udc.c +++ b/drivers/usb/gadget/udc/goku_udc.c @@ -1838,6 +1838,8 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) err: if (dev) goku_remove (pdev); + /* gadget_release is not registered yet, kfree explicitly */ + kfree(dev); return retval; } diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 614ab951..61c938c 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -589,7 +589,7 @@ static void net2280_free_request(struct usb_ep *_ep, struct usb_request *_req) ep = container_of(_ep, struct net2280_ep, ep); if (!_ep || !_req) { - dev_err(&ep->dev->pdev->dev, "%s: Inavlid ep=%p or req=%p\n", + dev_err(&ep->dev->pdev->dev, "%s: Invalid ep=%p or req=%p\n", __func__, _ep, _req); return; } @@ -1137,8 +1137,10 @@ dma_done(struct net2280_ep *ep, struct net2280_request *req, u32 dmacount, done(ep, req, status); } -static void scan_dma_completions(struct net2280_ep *ep) +static int scan_dma_completions(struct net2280_ep *ep) { + int num_completed = 0; + /* only look at descriptors that were "naturally" retired, * so fifo and list head state won't matter */ @@ -1166,6 +1168,7 @@ static void scan_dma_completions(struct net2280_ep *ep) break; /* single transfer mode */ dma_done(ep, req, tmp, 0); + num_completed++; break; } else if (!ep->is_in && (req->req.length % ep->ep.maxpacket) && @@ -1194,7 +1197,10 @@ static void scan_dma_completions(struct net2280_ep *ep) } } dma_done(ep, req, tmp, 0); + num_completed++; } + + return num_completed; } static void restart_dma(struct net2280_ep *ep) @@ -1567,6 +1573,44 @@ static struct usb_ep *net2280_match_ep(struct usb_gadget *_gadget, return ep; } + /* USB3380: Only first four endpoints have DMA channels. Allocate + * slower interrupt endpoints from PIO hw endpoints, to allow bulk/isoc + * endpoints use DMA hw endpoints. + */ + if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_INT && + usb_endpoint_dir_in(desc)) { + ep = gadget_find_ep_by_name(_gadget, "ep2in"); + if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) + return ep; + ep = gadget_find_ep_by_name(_gadget, "ep4in"); + if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) + return ep; + } else if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_INT && + !usb_endpoint_dir_in(desc)) { + ep = gadget_find_ep_by_name(_gadget, "ep1out"); + if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) + return ep; + ep = gadget_find_ep_by_name(_gadget, "ep3out"); + if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) + return ep; + } else if (usb_endpoint_type(desc) != USB_ENDPOINT_XFER_BULK && + usb_endpoint_dir_in(desc)) { + ep = gadget_find_ep_by_name(_gadget, "ep1in"); + if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) + return ep; + ep = gadget_find_ep_by_name(_gadget, "ep3in"); + if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) + return ep; + } else if (usb_endpoint_type(desc) != USB_ENDPOINT_XFER_BULK && + !usb_endpoint_dir_in(desc)) { + ep = gadget_find_ep_by_name(_gadget, "ep2out"); + if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) + return ep; + ep = gadget_find_ep_by_name(_gadget, "ep4out"); + if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) + return ep; + } + /* USB3380: use same address for usb and hardware endpoints */ snprintf(name, sizeof(name), "ep%d%s", usb_endpoint_num(desc), usb_endpoint_dir_in(desc) ? "in" : "out"); @@ -2547,8 +2591,11 @@ static void handle_ep_small(struct net2280_ep *ep) /* manual DMA queue advance after short OUT */ if (likely(ep->dma)) { if (t & BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT)) { - u32 count; + struct net2280_request *stuck_req = NULL; int stopped = ep->stopped; + int num_completed; + int stuck = 0; + u32 count; /* TRANSFERRED works around OUT_DONE erratum 0112. * we expect (N <= maxpacket) bytes; host wrote M. @@ -2560,7 +2607,7 @@ static void handle_ep_small(struct net2280_ep *ep) /* any preceding dma transfers must finish. * dma handles (M >= N), may empty the queue */ - scan_dma_completions(ep); + num_completed = scan_dma_completions(ep); if (unlikely(list_empty(&ep->queue) || ep->out_overflow)) { req = NULL; @@ -2580,6 +2627,31 @@ static void handle_ep_small(struct net2280_ep *ep) req = NULL; break; } + + /* Escape loop if no dma transfers completed + * after few retries. + */ + if (num_completed == 0) { + if (stuck_req == req && + readl(&ep->dma->dmadesc) != + req->td_dma && stuck++ > 5) { + count = readl( + &ep->dma->dmacount); + count &= DMA_BYTE_COUNT_MASK; + req = NULL; + ep_dbg(ep->dev, "%s escape stuck %d, count %u\n", + ep->ep.name, stuck, + count); + break; + } else if (stuck_req != req) { + stuck_req = req; + stuck = 0; + } + } else { + stuck_req = NULL; + stuck = 0; + } + udelay(1); } diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index ad140aa..7fa60f5 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -33,6 +33,7 @@ #include <linux/usb.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> +#include <linux/usb/phy.h> #include "pxa27x_udc.h" @@ -1655,6 +1656,37 @@ static int pxa_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA) return -EOPNOTSUPP; } +/** + * pxa_udc_phy_event - Called by phy upon VBus event + * @nb: notifier block + * @action: phy action, is vbus connect or disconnect + * @data: the usb_gadget structure in pxa_udc + * + * Called by the USB Phy when a cable connect or disconnect is sensed. + * + * Returns 0 + */ +static int pxa_udc_phy_event(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct usb_gadget *gadget = data; + + switch (action) { + case USB_EVENT_VBUS: + usb_gadget_vbus_connect(gadget); + return NOTIFY_OK; + case USB_EVENT_NONE: + usb_gadget_vbus_disconnect(gadget); + return NOTIFY_OK; + default: + return NOTIFY_DONE; + } +} + +static struct notifier_block pxa27x_udc_phy = { + .notifier_call = pxa_udc_phy_event, +}; + static int pxa27x_udc_start(struct usb_gadget *g, struct usb_gadget_driver *driver); static int pxa27x_udc_stop(struct usb_gadget *g); @@ -2432,7 +2464,14 @@ static int pxa_udc_probe(struct platform_device *pdev) return udc->irq; udc->dev = &pdev->dev; - udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); + if (of_have_populated_dt()) { + udc->transceiver = + devm_usb_get_phy_by_phandle(udc->dev, "phys", 0); + if (IS_ERR(udc->transceiver)) + return PTR_ERR(udc->transceiver); + } else { + udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); + } if (IS_ERR(udc->gpiod)) { dev_err(&pdev->dev, "Couldn't find or request D+ gpio : %ld\n", @@ -2465,14 +2504,20 @@ static int pxa_udc_probe(struct platform_device *pdev) goto err; } + if (!IS_ERR_OR_NULL(udc->transceiver)) + usb_register_notifier(udc->transceiver, &pxa27x_udc_phy); retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget); if (retval) - goto err; + goto err_add_gadget; pxa_init_debugfs(udc); if (should_enable_udc(udc)) udc_enable(udc); return 0; + +err_add_gadget: + if (!IS_ERR_OR_NULL(udc->transceiver)) + usb_unregister_notifier(udc->transceiver, &pxa27x_udc_phy); err: clk_unprepare(udc->clk); return retval; @@ -2489,6 +2534,8 @@ static int pxa_udc_remove(struct platform_device *_dev) usb_del_gadget_udc(&udc->gadget); pxa_cleanup_debugfs(udc); + if (!IS_ERR_OR_NULL(udc->transceiver)) + usb_unregister_notifier(udc->transceiver, &pxa27x_udc_phy); usb_put_phy(udc->transceiver); udc->transceiver = NULL; |