From 5da93478251cf0dd6b34874c7c1c24a4a52eabc1 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 7 Dec 2012 21:42:03 +0200 Subject: usb: dwc3: decrease event buffer size Currently we're allocating an entire page to serve as our event buffer. Provided our events are 4 bytes long, it's very unlikely we will even trigger 1k events at once. Even in the worst case scenario where every endpoint triggers one event and we still have a couple of error events, that would still be less than 40 events. In order to cope with future versions of the IP which could (or could not) increase the amount of possible events to trigger simultaneously, we're using an arbitrary size of 64 events for our event buffer. We're saving 3840 bytes by doing so. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 4999563..5f79d9f 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -55,7 +55,9 @@ #define DWC3_ENDPOINTS_NUM 32 #define DWC3_XHCI_RESOURCES_NUM 2 -#define DWC3_EVENT_BUFFERS_SIZE PAGE_SIZE +#define DWC3_EVENT_SIZE 4 /* bytes */ +#define DWC3_EVENT_MAX_NUM 64 /* 2 events/endpoint */ +#define DWC3_EVENT_BUFFERS_SIZE (DWC3_EVENT_SIZE * DWC3_EVENT_MAX_NUM) #define DWC3_EVENT_TYPE_MASK 0xfe #define DWC3_EVENT_TYPE_DEV 0 -- cgit v0.10.2 From 20b97dc18323938a92319abf031936b7d2686eaf Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 13 Nov 2012 11:20:49 +0900 Subject: usb: dwc3: exynos: use devm_ functions The devm_ functions allocate memory that is released when a driver detaches. This makes the code smaller and a bit simpler. Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index aae5328..90e05e6 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -95,13 +95,14 @@ static int dwc3_exynos_probe(struct platform_device *pdev) struct platform_device *dwc3; struct dwc3_exynos *exynos; struct clk *clk; + struct device *dev = &pdev->dev; int ret = -ENOMEM; - exynos = kzalloc(sizeof(*exynos), GFP_KERNEL); + exynos = devm_kzalloc(dev, sizeof(*exynos), GFP_KERNEL); if (!exynos) { - dev_err(&pdev->dev, "not enough memory\n"); - goto err0; + dev_err(dev, "not enough memory\n"); + return -ENOMEM; } /* @@ -116,30 +117,30 @@ static int dwc3_exynos_probe(struct platform_device *pdev) ret = dwc3_exynos_register_phys(exynos); if (ret) { - dev_err(&pdev->dev, "couldn't register PHYs\n"); - goto err1; + dev_err(dev, "couldn't register PHYs\n"); + return ret; } dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO); if (!dwc3) { - dev_err(&pdev->dev, "couldn't allocate dwc3 device\n"); - goto err1; + dev_err(dev, "couldn't allocate dwc3 device\n"); + return -ENOMEM; } - clk = clk_get(&pdev->dev, "usbdrd30"); + clk = devm_clk_get(dev, "usbdrd30"); if (IS_ERR(clk)) { - dev_err(&pdev->dev, "couldn't get clock\n"); + dev_err(dev, "couldn't get clock\n"); ret = -EINVAL; - goto err3; + goto err1; } - dma_set_coherent_mask(&dwc3->dev, pdev->dev.coherent_dma_mask); + dma_set_coherent_mask(&dwc3->dev, dev->coherent_dma_mask); - dwc3->dev.parent = &pdev->dev; - dwc3->dev.dma_mask = pdev->dev.dma_mask; - dwc3->dev.dma_parms = pdev->dev.dma_parms; + dwc3->dev.parent = dev; + dwc3->dev.dma_mask = dev->dma_mask; + dwc3->dev.dma_parms = dev->dma_parms; exynos->dwc3 = dwc3; - exynos->dev = &pdev->dev; + exynos->dev = dev; exynos->clk = clk; clk_enable(exynos->clk); @@ -147,26 +148,23 @@ static int dwc3_exynos_probe(struct platform_device *pdev) ret = platform_device_add_resources(dwc3, pdev->resource, pdev->num_resources); if (ret) { - dev_err(&pdev->dev, "couldn't add resources to dwc3 device\n"); - goto err4; + dev_err(dev, "couldn't add resources to dwc3 device\n"); + goto err2; } ret = platform_device_add(dwc3); if (ret) { - dev_err(&pdev->dev, "failed to register dwc3 device\n"); - goto err4; + dev_err(dev, "failed to register dwc3 device\n"); + goto err2; } return 0; -err4: +err2: clk_disable(clk); - clk_put(clk); -err3: - platform_device_put(dwc3); err1: - kfree(exynos); -err0: + platform_device_put(dwc3); + return ret; } @@ -179,9 +177,6 @@ static int dwc3_exynos_remove(struct platform_device *pdev) platform_device_unregister(exynos->usb3_phy); clk_disable(exynos->clk); - clk_put(exynos->clk); - - kfree(exynos); return 0; } -- cgit v0.10.2 From 7dbdf4e4b37766a2389680c460b9dab278a5444c Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 13 Dec 2012 16:12:00 +0200 Subject: usb: dwc3: gadget: don't redefine 'ret' we have an extra 'ret' variable shadowing a previous definition. Remove it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 2e43b33..0b92e98 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1082,8 +1082,6 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) * */ if (dep->flags & DWC3_EP_PENDING_REQUEST) { - int ret; - /* * If xfernotready is already elapsed and it is a case * of isoc transfer, then issue END TRANSFER, so that -- cgit v0.10.2 From d7668024b3b5f9563eab8dad66cb9a4b80f36ebf Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 18 Jan 2013 10:21:34 +0200 Subject: usb: dwc3: debugfs: convert our regdump to use regsets regset is a generic implementation of regdump utility through debugfs. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 5f79d9f..b7ca82c 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -723,6 +723,7 @@ struct dwc3 { struct dwc3_hwparams hwparams; struct dentry *root; + struct debugfs_regset32 *regset; u8 test_mode; u8 test_mode_nr; diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index 5945aad..aff8fd3e 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -59,7 +59,7 @@ .offset = DWC3_ ##nm - DWC3_GLOBALS_REGS_START, \ } -static const struct debugfs_reg32 dwc3_regs[] = { +static struct debugfs_reg32 dwc3_regs[] = { dump_register(GSBUSCFG0), dump_register(GSBUSCFG1), dump_register(GTXTHRCFG), @@ -376,27 +376,6 @@ static const struct debugfs_reg32 dwc3_regs[] = { dump_register(OSTS), }; -static int dwc3_regdump_show(struct seq_file *s, void *unused) -{ - struct dwc3 *dwc = s->private; - - seq_printf(s, "DesignWare USB3 Core Register Dump\n"); - debugfs_print_regs32(s, dwc3_regs, ARRAY_SIZE(dwc3_regs), - dwc->regs, ""); - return 0; -} - -static int dwc3_regdump_open(struct inode *inode, struct file *file) -{ - return single_open(file, dwc3_regdump_show, inode->i_private); -} - -static const struct file_operations dwc3_regdump_fops = { - .open = dwc3_regdump_open, - .read = seq_read, - .release = single_release, -}; - static int dwc3_mode_show(struct seq_file *s, void *unused) { struct dwc3 *dwc = s->private; @@ -666,8 +645,17 @@ int dwc3_debugfs_init(struct dwc3 *dwc) dwc->root = root; - file = debugfs_create_file("regdump", S_IRUGO, root, dwc, - &dwc3_regdump_fops); + dwc->regset = kzalloc(sizeof(*dwc->regset), GFP_KERNEL); + if (!dwc->regset) { + ret = -ENOMEM; + goto err1; + } + + dwc->regset->regs = dwc3_regs; + dwc->regset->nregs = ARRAY_SIZE(dwc3_regs); + dwc->regset->base = dwc->regs; + + file = debugfs_create_regset32("regdump", S_IRUGO, root, dwc->regset); if (!file) { ret = -ENOMEM; goto err1; -- cgit v0.10.2 From 2b758350af19db9a5c98241cf222c2e211d7a912 Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Mon, 14 Jan 2013 15:59:31 +0530 Subject: usb: dwc3: Enable usb2 LPM only when connected as usb2.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Synopsys says: The HIRD Threshold field must be set to ‘0’ when the device core is operating in super speed mode. This patch implements above statement. Cc: # v3.6 v3.7 v3.8 Acked-by: Paul Zimmerman Signed-off-by: Pratyush Anand Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 0b92e98..113ec80 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2154,6 +2154,23 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) break; } + /* Enable USB2 LPM Capability */ + + if ((dwc->revision > DWC3_REVISION_194A) + && (speed != DWC3_DCFG_SUPERSPEED)) { + 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_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN); + + /* TODO: This should be configurable */ + reg |= DWC3_DCTL_HIRD_THRES(28); + + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + } + /* Recent versions support automatic phy suspend and don't need this */ if (dwc->revision < DWC3_REVISION_194A) { /* Suspend unneeded PHY */ @@ -2460,20 +2477,8 @@ int dwc3_gadget_init(struct dwc3 *dwc) DWC3_DEVTEN_DISCONNEVTEN); dwc3_writel(dwc->regs, DWC3_DEVTEN, reg); - /* Enable USB2 LPM and automatic phy suspend only on recent versions */ + /* automatic phy suspend only on recent versions */ if (dwc->revision >= DWC3_REVISION_194A) { - 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_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN); - - /* TODO: This should be configurable */ - reg |= DWC3_DCTL_HIRD_THRES(28); - - dwc3_writel(dwc->regs, DWC3_DCTL, reg); - dwc3_gadget_usb2_phy_suspend(dwc, false); dwc3_gadget_usb3_phy_suspend(dwc, false); } -- cgit v0.10.2 From 7efea86c2868b8fd9df65e589e33aebe498ce21d Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Mon, 14 Jan 2013 15:59:32 +0530 Subject: usb: dwc3: gadget: fix missed isoc There are two reasons to generate missed isoc. 1. when the host does not poll for all the data. 2. because of application-side delays that prevent all the data from being transferred in programmed microframe. Current code was able to handle first case only. This patch handles scenario 2 as well.Scenario 2 sometime may occur with complex gadget application, however it can be easily reproduced for testing purpose as follows: a. use isoc binterval as 1 in f_sourcesink. b. use pattern=0 c. introduce a delay of 150us deliberately in source_sink_complete, so that after few frames it lands into scenario 2. d. now run testusb 16 (isoc in test). You will notice that if this patch is not applied then isoc transfer is not able to recover after first missed. Current patch's approach is as under: If missed isoc occurs and there is no request queued then issue END TRANSFER, so that core generates next xfernotready and we will issue a fresh START TRANSFER. If there are still queued request then wait, do not issue either END or UPDATE TRANSFER, just attach next request in request_list during giveback. If any future queued request is successfully transferred then we will issue UPDATE TRANSFER for all request in the request_list. Cc: # v3.6 v3.7 v3.8 Signed-off-by: Pratyush Anand Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index b7ca82c..7610cf5 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -407,7 +407,6 @@ struct dwc3_event_buffer { * @number: endpoint number (1 - 15) * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK * @resource_index: Resource transfer index - * @current_uf: Current uf received through last event parameter * @interval: the intervall on which the ISOC transfer is started * @name: a human readable name e.g. ep1out-bulk * @direction: true for TX, false for RX @@ -441,7 +440,6 @@ struct dwc3_ep { u8 number; u8 type; u8 resource_index; - u16 current_uf; u32 interval; char name[20]; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 113ec80..356a5a2 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1115,16 +1115,6 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) dep->name); } - /* - * 3. Missed ISOC Handling. We need to start isoc transfer on the saved - * uframe number. - */ - if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && - (dep->flags & DWC3_EP_MISSED_ISOC)) { - __dwc3_gadget_start_isoc(dwc, dep, dep->current_uf); - dep->flags &= ~DWC3_EP_MISSED_ISOC; - } - return 0; } @@ -1686,14 +1676,29 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, if (trb_status == DWC3_TRBSTS_MISSED_ISOC) { dev_dbg(dwc->dev, "incomplete IN transfer %s\n", dep->name); - dep->current_uf = event->parameters & - ~(dep->interval - 1); + /* + * If missed isoc occurred and there is + * no request queued then issue END + * TRANSFER, so that core generates + * next xfernotready and we will issue + * a fresh START TRANSFER. + * If there are still queued request + * then wait, do not issue either END + * or UPDATE TRANSFER, just attach next + * request in request_list during + * giveback.If any future queued request + * is successfully transferred then we + * will issue UPDATE TRANSFER for all + * request in the request_list. + */ dep->flags |= DWC3_EP_MISSED_ISOC; } else { dev_err(dwc->dev, "incomplete IN transfer %s\n", dep->name); status = -ECONNRESET; } + } else { + dep->flags &= ~DWC3_EP_MISSED_ISOC; } } else { if (count && (event->status & DEPEVT_STATUS_SHORT)) @@ -1720,6 +1725,13 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, break; } while (1); + if (list_empty(&dep->req_queued) && + (dep->flags & DWC3_EP_MISSED_ISOC)) { + dwc3_stop_active_transfer(dwc, dep->number); + dep->flags &= ~DWC3_EP_MISSED_ISOC; + return 1; + } + if ((event->status & DEPEVT_STATUS_IOC) && (trb->ctrl & DWC3_TRB_CTRL_IOC)) return 0; -- cgit v0.10.2 From 15f86bde29f0fdfb877d9c753547fa2e2f5ef1fe Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Mon, 14 Jan 2013 15:59:33 +0530 Subject: usb: dwc3: gadget: correct return from ep_queue Its better to return from each if condition as they are mutually exclusive. Signed-off-by: Pratyush Anand Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 356a5a2..c4ffb35 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1097,6 +1097,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) if (ret && ret != -EBUSY) dev_dbg(dwc->dev, "%s: failed to kick transfers\n", dep->name); + return ret; } /* @@ -1113,6 +1114,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) if (ret && ret != -EBUSY) dev_dbg(dwc->dev, "%s: failed to kick transfers\n", dep->name); + return ret; } return 0; -- cgit v0.10.2 From cdc359dd87ab6c39a67dab724fd0b61c16e6f08b Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Mon, 14 Jan 2013 15:59:34 +0530 Subject: usb: dwc3: gadget: fix isoc END TRANSFER Condition There were still some corner cases where isoc transfer was not able to restart, specially when missed isoc does not happen , and in fact gadget does not queue any new request during giveback. Cleanup function calls giveback first, which provides a way to queue another request to gadget. But gadget did not had any data. So , it did not call ep_queue. To twist it further, gadget did not queue till cleanup for last queued TRB is called. If we ever reach this scenario, we must call END TRANSFER, so that we receive a new xfernotready with information about current microframe number. Also insure that there is no request submitted to core when issuing END TRANSFER. Cc: # v3.8 Signed-off-by: Pratyush Anand Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index c4ffb35..e60e72c 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1089,7 +1089,10 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) * notion of current microframe. */ if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { - dwc3_stop_active_transfer(dwc, dep->number); + if (list_empty(&dep->req_queued)) { + dwc3_stop_active_transfer(dwc, dep->number); + dep->flags = DWC3_EP_ENABLED; + } return 0; } @@ -1727,10 +1730,20 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, break; } while (1); - if (list_empty(&dep->req_queued) && - (dep->flags & DWC3_EP_MISSED_ISOC)) { - dwc3_stop_active_transfer(dwc, dep->number); - dep->flags &= ~DWC3_EP_MISSED_ISOC; + if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && + list_empty(&dep->req_queued)) { + if (list_empty(&dep->request_list)) { + /* + * If there is no entry in request list then do + * not issue END TRANSFER now. Just set PENDING + * flag, so that END TRANSFER is issued when an + * entry is added into request list. + */ + dep->flags = DWC3_EP_PENDING_REQUEST; + } else { + dwc3_stop_active_transfer(dwc, dep->number); + dep->flags = DWC3_EP_ENABLED; + } return 1; } -- cgit v0.10.2 From 915e202aeeb59e272992a6364c910aaef3073544 Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Mon, 14 Jan 2013 15:59:35 +0530 Subject: usb: dwc3: gadget: fix skip LINK_TRB on ISOC When we reach to link trb, we just need to increase free_slot and then calculate TRB. Return is not correct, as it will cause wrong TRB DMA address to fetch in case of update transfer. Cc: Signed-off-by: Pratyush Anand Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index e60e72c..da8d57c 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -754,21 +754,18 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3 *dwc = dep->dwc; struct dwc3_trb *trb; - unsigned int cur_slot; - dev_vdbg(dwc->dev, "%s: req %p dma %08llx length %d%s%s\n", dep->name, req, (unsigned long long) dma, length, last ? " last" : "", chain ? " chain" : ""); - trb = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK]; - cur_slot = dep->free_slot; - dep->free_slot++; - /* Skip the LINK-TRB on ISOC */ - if (((cur_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) && + if (((dep->free_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) && usb_endpoint_xfer_isoc(dep->endpoint.desc)) - return; + dep->free_slot++; + + trb = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK]; + dep->free_slot++; if (!req->trb) { dwc3_gadget_move_request_queued(req); -- cgit v0.10.2 From 1877d6c9a57802d50a059cf2dacdba10168cece7 Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Mon, 14 Jan 2013 15:59:36 +0530 Subject: usb: dwc3: gadget: no need to pass params in case of UPDATE_TRANSFER UPDATE_TRANSFER does not need any parameters. So, no need to prepare it. Signed-off-by: Pratyush Anand Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index da8d57c..686c31c 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -974,13 +974,14 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param, } memset(¶ms, 0, sizeof(params)); - params.param0 = upper_32_bits(req->trb_dma); - params.param1 = lower_32_bits(req->trb_dma); - if (start_new) + if (start_new) { + params.param0 = upper_32_bits(req->trb_dma); + params.param1 = lower_32_bits(req->trb_dma); cmd = DWC3_DEPCMD_STARTTRANSFER; - else + } else { cmd = DWC3_DEPCMD_UPDATETRANSFER; + } cmd |= DWC3_DEPCMD_PARAM(cmd_param); ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms); -- cgit v0.10.2 From e5ba5ec833aa4a76980b512d6a6779643516b850 Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Mon, 14 Jan 2013 15:59:37 +0530 Subject: usb: dwc3: gadget: fix scatter gather implementation To work with scatter gather properly, fixes have been done in number of functions. I will explain requirement of each fixes one by one. start_slot: used to retrieve all request of SG during cleanup dwc3_gadget_giveback: We need to skip link TRB if it was one of the intermediate TRB of SG. dwc3_prepare_one_trb: We need to track all submitted TRBs during cleanup. Since, all TRBs would be serially allocated, so we can just keep starting slot info and we can always find rest of them. We need to pass sg node number, so that we cab appropriately program ISOC_FIRST/ISOC, Chain etc. dwc3_prepare_trbs: last_one should be set when it is last node of SG as well as last node of request_list. __dwc3_cleanup_done_trbs: It has been prepared after re-factorization of dwc3_cleanup_done_reqs. It is called for each TRB of SG. Signed-off-by: Pratyush Anand Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 7610cf5..f02b3e0 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -581,6 +581,7 @@ struct dwc3_request { struct usb_request request; struct list_head list; struct dwc3_ep *dep; + u32 start_slot; u8 epnum; struct dwc3_trb *trb; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 686c31c..a4d10cf 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -241,21 +241,22 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, int status) { struct dwc3 *dwc = dep->dwc; + int i; if (req->queued) { - if (req->request.num_mapped_sgs) - dep->busy_slot += req->request.num_mapped_sgs; - else + i = 0; + do { dep->busy_slot++; - - /* - * Skip LINK TRB. We can't use req->trb and check for - * DWC3_TRBCTL_LINK_TRB because it points the TRB we just - * completed (not the LINK TRB). - */ - if (((dep->busy_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) && + /* + * Skip LINK TRB. We can't use req->trb and check for + * DWC3_TRBCTL_LINK_TRB because it points the TRB we + * just completed (not the LINK TRB). + */ + if (((dep->busy_slot & DWC3_TRB_MASK) == + DWC3_TRB_NUM- 1) && usb_endpoint_xfer_isoc(dep->endpoint.desc)) - dep->busy_slot++; + dep->busy_slot++; + } while(++i < req->request.num_mapped_sgs); } list_del(&req->list); req->trb = NULL; @@ -749,7 +750,7 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep, */ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_request *req, dma_addr_t dma, - unsigned length, unsigned last, unsigned chain) + unsigned length, unsigned last, unsigned chain, unsigned node) { struct dwc3 *dwc = dep->dwc; struct dwc3_trb *trb; @@ -765,14 +766,16 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, dep->free_slot++; trb = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK]; - dep->free_slot++; if (!req->trb) { dwc3_gadget_move_request_queued(req); req->trb = trb; req->trb_dma = dwc3_trb_dma_offset(dep, trb); + req->start_slot = dep->free_slot & DWC3_TRB_MASK; } + dep->free_slot++; + trb->size = DWC3_TRB_SIZE_LENGTH(length); trb->bpl = lower_32_bits(dma); trb->bph = upper_32_bits(dma); @@ -783,9 +786,12 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, break; case USB_ENDPOINT_XFER_ISOC: - trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST; + if (!node) + trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST; + else + trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS; - if (!req->request.no_interrupt) + if (!req->request.no_interrupt && !chain) trb->ctrl |= DWC3_TRB_CTRL_IOC; break; @@ -804,14 +810,13 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI; trb->ctrl |= DWC3_TRB_CTRL_CSP; - } else { - if (chain) - trb->ctrl |= DWC3_TRB_CTRL_CHN; - - if (last) - trb->ctrl |= DWC3_TRB_CTRL_LST; + } else if (last) { + trb->ctrl |= DWC3_TRB_CTRL_LST; } + if (chain) + 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); @@ -882,6 +887,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting) list_for_each_entry_safe(req, n, &dep->request_list, list) { unsigned length; dma_addr_t dma; + last_one = false; if (req->request.num_mapped_sgs > 0) { struct usb_request *request = &req->request; @@ -897,7 +903,9 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting) if (i == (request->num_mapped_sgs - 1) || sg_is_last(s)) { - last_one = true; + if (list_is_last(&req->list, + &dep->request_list)) + last_one = true; chain = false; } @@ -909,7 +917,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting) chain = false; dwc3_prepare_one_trb(dep, req, dma, length, - last_one, chain); + last_one, chain, i); if (last_one) break; @@ -927,7 +935,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting) last_one = 1; dwc3_prepare_one_trb(dep, req, dma, length, - last_one, false); + last_one, false, 0); if (last_one) break; @@ -1642,89 +1650,115 @@ static void dwc3_gadget_release(struct device *dev) } /* -------------------------------------------------------------------------- */ -static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, +static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep, + struct dwc3_request *req, struct dwc3_trb *trb, const struct dwc3_event_depevt *event, int status) { - struct dwc3_request *req; - struct dwc3_trb *trb; unsigned int count; unsigned int s_pkt = 0; unsigned int trb_status; + if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN) + /* + * We continue despite the error. There is not much we + * can do. If we don't clean it up we loop forever. If + * we skip the TRB then it gets overwritten after a + * while since we use them in a ring buffer. A BUG() + * would help. Lets hope that if this occurs, someone + * fixes the root cause instead of looking away :) + */ + dev_err(dwc->dev, "%s's TRB (%p) still owned by HW\n", + dep->name, trb); + count = trb->size & DWC3_TRB_SIZE_MASK; + + if (dep->direction) { + if (count) { + trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size); + if (trb_status == DWC3_TRBSTS_MISSED_ISOC) { + dev_dbg(dwc->dev, "incomplete IN transfer %s\n", + dep->name); + /* + * If missed isoc occurred and there is + * no request queued then issue END + * TRANSFER, so that core generates + * next xfernotready and we will issue + * a fresh START TRANSFER. + * If there are still queued request + * then wait, do not issue either END + * or UPDATE TRANSFER, just attach next + * request in request_list during + * giveback.If any future queued request + * is successfully transferred then we + * will issue UPDATE TRANSFER for all + * request in the request_list. + */ + dep->flags |= DWC3_EP_MISSED_ISOC; + } else { + dev_err(dwc->dev, "incomplete IN transfer %s\n", + dep->name); + status = -ECONNRESET; + } + } else { + dep->flags &= ~DWC3_EP_MISSED_ISOC; + } + } else { + if (count && (event->status & DEPEVT_STATUS_SHORT)) + s_pkt = 1; + } + + /* + * We assume here we will always receive the entire data block + * which we should receive. Meaning, if we program RX to + * receive 4K but we receive only 2K, we assume that's all we + * should receive and we simply bounce the request back to the + * gadget driver for further processing. + */ + req->request.actual += req->request.length - count; + if (s_pkt) + return 1; + if ((event->status & DEPEVT_STATUS_LST) && + (trb->ctrl & (DWC3_TRB_CTRL_LST | + DWC3_TRB_CTRL_HWO))) + return 1; + if ((event->status & DEPEVT_STATUS_IOC) && + (trb->ctrl & DWC3_TRB_CTRL_IOC)) + return 1; + return 0; +} + +static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, + const struct dwc3_event_depevt *event, int status) +{ + struct dwc3_request *req; + struct dwc3_trb *trb; + unsigned int slot; + unsigned int i; + int ret; + do { req = next_request(&dep->req_queued); if (!req) { WARN_ON_ONCE(1); return 1; } + i = 0; + do { + slot = req->start_slot + i; + if ((slot == DWC3_TRB_NUM - 1) && + usb_endpoint_xfer_isoc(dep->endpoint.desc)) + slot++; + slot %= DWC3_TRB_NUM; + trb = &dep->trb_pool[slot]; - trb = req->trb; - - if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN) - /* - * We continue despite the error. There is not much we - * can do. If we don't clean it up we loop forever. If - * we skip the TRB then it gets overwritten after a - * while since we use them in a ring buffer. A BUG() - * would help. Lets hope that if this occurs, someone - * fixes the root cause instead of looking away :) - */ - dev_err(dwc->dev, "%s's TRB (%p) still owned by HW\n", - dep->name, req->trb); - count = trb->size & DWC3_TRB_SIZE_MASK; - - if (dep->direction) { - if (count) { - trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size); - if (trb_status == DWC3_TRBSTS_MISSED_ISOC) { - dev_dbg(dwc->dev, "incomplete IN transfer %s\n", - dep->name); - /* - * If missed isoc occurred and there is - * no request queued then issue END - * TRANSFER, so that core generates - * next xfernotready and we will issue - * a fresh START TRANSFER. - * If there are still queued request - * then wait, do not issue either END - * or UPDATE TRANSFER, just attach next - * request in request_list during - * giveback.If any future queued request - * is successfully transferred then we - * will issue UPDATE TRANSFER for all - * request in the request_list. - */ - dep->flags |= DWC3_EP_MISSED_ISOC; - } else { - dev_err(dwc->dev, "incomplete IN transfer %s\n", - dep->name); - status = -ECONNRESET; - } - } else { - dep->flags &= ~DWC3_EP_MISSED_ISOC; - } - } else { - if (count && (event->status & DEPEVT_STATUS_SHORT)) - s_pkt = 1; - } + ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb, + event, status); + if (ret) + break; + }while (++i < req->request.num_mapped_sgs); - /* - * We assume here we will always receive the entire data block - * which we should receive. Meaning, if we program RX to - * receive 4K but we receive only 2K, we assume that's all we - * should receive and we simply bounce the request back to the - * gadget driver for further processing. - */ - req->request.actual += req->request.length - count; dwc3_gadget_giveback(dep, req, status); - if (s_pkt) - break; - if ((event->status & DEPEVT_STATUS_LST) && - (trb->ctrl & (DWC3_TRB_CTRL_LST | - DWC3_TRB_CTRL_HWO))) - break; - if ((event->status & DEPEVT_STATUS_IOC) && - (trb->ctrl & DWC3_TRB_CTRL_IOC)) + + if (ret) break; } while (1); -- cgit v0.10.2 From c9fda7d6f62a6520e01652d370654c5657d3c1a1 Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Mon, 14 Jan 2013 15:59:38 +0530 Subject: usb: dwc3: gadget: req->queued must be forced to false in cleanup I am not sure, why I found it during SG debugging. But, I noticed that even when req_queued list was empty, there were some request in request_list having queued flag true. If I run test second time, it first removes all request from request_list and hence busy_slot was wrongly incremented. Cc: Signed-off-by: Pratyush Anand Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index a4d10cf..38e8d3e 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -257,6 +257,7 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, usb_endpoint_xfer_isoc(dep->endpoint.desc)) dep->busy_slot++; } while(++i < req->request.num_mapped_sgs); + req->queued = false; } list_del(&req->list); req->trb = NULL; -- cgit v0.10.2 From 388e5c51135f817f01177c42261f1116a6d7f2ad Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Tue, 15 Jan 2013 16:09:21 +0530 Subject: usb: dwc3: remove dwc3 dependency on host AND gadget. DWC3 controller curretly depends on USB && USB_GADGET. Some hardware may like to use only host feature on dwc3, or only gadget feature. So, removing this dependency of USB_DWC3 on USB and USB_GADGET. Adding the mode of operaiton of DWC3 also here HOST/GADGET/DUAL_ROLE based on which features are enabled. [ balbi@ti.com : . make sure we have default modes for all possible Kernel configurations. . Remove the config -> menuconfig change as it's unnecessary . switch over to IS_ENABLED() ] CC: Doug Anderson Signed-off-by: Vivek Gautam Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index f6a6e07..77e3f40 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -1,6 +1,6 @@ config USB_DWC3 tristate "DesignWare USB3 DRD Core Support" - depends on (USB && USB_GADGET) + depends on (USB || USB_GADGET) select USB_OTG_UTILS select USB_XHCI_PLATFORM if USB_SUPPORT && USB_XHCI_HCD help @@ -12,6 +12,35 @@ config USB_DWC3 if USB_DWC3 +choice + bool "DWC3 Mode Selection" + default USB_DWC3_DUAL_ROLE if (USB && USB_GADGET) + default USB_DWC3_HOST if (USB && !USB_GADGET) + default USB_DWC3_GADGET if (!USB && USB_GADGET) + +config USB_DWC3_HOST + bool "Host only mode" + depends on USB + help + Select this when you want to use DWC3 in host mode only, + thereby the gadget feature will be regressed. + +config USB_DWC3_GADGET + bool "Gadget only mode" + depends on USB_GADGET + help + Select this when you want to use DWC3 in gadget mode only, + thereby the host feature will be regressed. + +config USB_DWC3_DUAL_ROLE + bool "Dual Role mode" + depends on (USB && USB_GADGET) + help + This is the default mode of working of DWC3 controller where + both host and gadget features are enabled. + +endchoice + config USB_DWC3_DEBUG bool "Enable Debugging Messages" help diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 4502648..0c7ac92 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -4,8 +4,14 @@ ccflags-$(CONFIG_USB_DWC3_VERBOSE) += -DVERBOSE_DEBUG obj-$(CONFIG_USB_DWC3) += dwc3.o dwc3-y := core.o -dwc3-y += host.o -dwc3-y += gadget.o ep0.o + +ifneq ($(filter y,$(CONFIG_USB_DWC3_HOST) $(CONFIG_USB_DWC3_DUAL_ROLE)),) + dwc3-y += host.o +endif + +ifneq ($(filter y,$(CONFIG_USB_DWC3_GADGET) $(CONFIG_USB_DWC3_DUAL_ROLE)),) + dwc3-y += gadget.o ep0.o +endif ifneq ($(CONFIG_DEBUG_FS),) dwc3-y += debugfs.o diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index f02b3e0..b417506 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -864,10 +864,24 @@ union dwc3_event { void dwc3_set_mode(struct dwc3 *dwc, u32 mode); int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc); +#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) int dwc3_host_init(struct dwc3 *dwc); void dwc3_host_exit(struct dwc3 *dwc); - +#else +static inline int dwc3_host_init(struct dwc3 *dwc) +{ return 0; } +static inline void dwc3_host_exit(struct dwc3 *dwc) +{ } +#endif + +#if IS_ENABLED(CONFIG_USB_DWC3_GADGET) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) int dwc3_gadget_init(struct dwc3 *dwc); void dwc3_gadget_exit(struct dwc3 *dwc); +#else +static inline int dwc3_gadget_init(struct dwc3 *dwc) +{ return 0; } +static inline void dwc3_gadget_exit(struct dwc3 *dwc) +{ } +#endif #endif /* __DRIVERS_USB_DWC3_CORE_H */ diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index aff8fd3e..4a752e7 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -661,6 +661,7 @@ int dwc3_debugfs_init(struct dwc3 *dwc) goto err1; } +#if IS_ENABLED(CONFIG_USB_DWC3_GADGET) file = debugfs_create_file("mode", S_IRUGO | S_IWUSR, root, dwc, &dwc3_mode_fops); if (!file) { @@ -681,6 +682,7 @@ int dwc3_debugfs_init(struct dwc3 *dwc) ret = -ENOMEM; goto err1; } +#endif return 0; -- cgit v0.10.2 From 1a947746dbe1486d0e305ab512ddf085b7874cb3 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 11:56:11 +0200 Subject: usb: dwc3: gadget: change HIRD threshold to 12 First of all, that 28 value makes no sense as HIRD threshold is a 4-bit value, second of all it's causing issues for OMAP5. Using 12 because commit cbc725b3 (usb: dwc3: keep default hird threshold value as 4b1100) had the intention of setting the maximum allowed value of 0xc. Also, original code has been wrong forever, so this should be backported as far back as possible. Cc: Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 38e8d3e..77a0013 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2225,8 +2225,11 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN); - /* TODO: This should be configurable */ - reg |= DWC3_DCTL_HIRD_THRES(28); + /* + * TODO: This should be configurable. For now using + * maximum allowed HIRD threshold value of 0b1100 + */ + reg |= DWC3_DCTL_HIRD_THRES(12); dwc3_writel(dwc->regs, DWC3_DCTL, reg); } -- cgit v0.10.2 From fe29db8fb22f5aa67af4bf30b85a0451c989a88b Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Thu, 24 Jan 2013 19:15:30 +0530 Subject: usb: dwc3: exynos: fix compatible strings for the device Using specific chip in compatible strings. Newer SOCs can claim device by using older string in the compatible list. Signed-off-by: Vivek Gautam Acked-by: Grant Likely Reviewed-by: Doug Anderson Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index 90e05e6..1e95551 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -183,7 +183,7 @@ static int dwc3_exynos_remove(struct platform_device *pdev) #ifdef CONFIG_OF static const struct of_device_id exynos_dwc3_match[] = { - { .compatible = "samsung,exynos-dwc3" }, + { .compatible = "samsung,exynos5250-dwusb3" }, {}, }; MODULE_DEVICE_TABLE(of, exynos_dwc3_match); -- cgit v0.10.2 From 94c6a436f606836dcb1ba0156757cea7f17a2102 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 25 Jan 2013 08:30:45 +0530 Subject: usb: dwc3: omap: use device_for_each_child to handle child removal Used device_for_each_child() to handle child device (dwc3 core) removal during devexit of dwc3 omap. This is in preparation for creating the child devices from subnode of dwc3 omap glue using of_platform_populate. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index f31867f..1d03a8a 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -262,6 +262,15 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap) return IRQ_HANDLED; } +static int dwc3_omap_remove_core(struct device *dev, void *c) +{ + struct platform_device *pdev = to_platform_device(dev); + + platform_device_unregister(pdev); + + return 0; +} + static int dwc3_omap_probe(struct platform_device *pdev) { struct dwc3_omap_data *pdata = pdev->dev.platform_data; @@ -425,9 +434,10 @@ static int dwc3_omap_remove(struct platform_device *pdev) { struct dwc3_omap *omap = platform_get_drvdata(pdev); - platform_device_unregister(omap->dwc3); platform_device_unregister(omap->usb2_phy); platform_device_unregister(omap->usb3_phy); + device_for_each_child(&pdev->dev, NULL, dwc3_omap_remove_core); + return 0; } -- cgit v0.10.2 From b4bfe6aa9b36c5ff42d96c64e2df7e36a8c61dfb Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 25 Jan 2013 08:30:46 +0530 Subject: usb: dwc3: omap: use of_platform API to create dwc3 core pdev Used of_platform_populate() to create dwc3 core platform_device from device tree data. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 1d03a8a..78bb2f6 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -133,7 +134,6 @@ struct dwc3_omap { /* device lock */ spinlock_t lock; - struct platform_device *dwc3; struct platform_device *usb2_phy; struct platform_device *usb3_phy; struct device *dev; @@ -276,7 +276,6 @@ static int dwc3_omap_probe(struct platform_device *pdev) struct dwc3_omap_data *pdata = pdev->dev.platform_data; struct device_node *node = pdev->dev.of_node; - struct platform_device *dwc3; struct dwc3_omap *omap; struct resource *res; struct device *dev = &pdev->dev; @@ -323,30 +322,19 @@ static int dwc3_omap_probe(struct platform_device *pdev) return ret; } - dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO); - if (!dwc3) { - dev_err(dev, "couldn't allocate dwc3 device\n"); - return -ENOMEM; - } - context = devm_kzalloc(dev, resource_size(res), GFP_KERNEL); if (!context) { dev_err(dev, "couldn't allocate dwc3 context memory\n"); - goto err2; + return -ENOMEM; } spin_lock_init(&omap->lock); - dma_set_coherent_mask(&dwc3->dev, dev->coherent_dma_mask); - dwc3->dev.parent = dev; - dwc3->dev.dma_mask = dev->dma_mask; - dwc3->dev.dma_parms = dev->dma_parms; omap->resource_size = resource_size(res); omap->context = context; omap->dev = dev; omap->irq = irq; omap->base = base; - omap->dwc3 = dwc3; reg = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS); @@ -391,7 +379,7 @@ static int dwc3_omap_probe(struct platform_device *pdev) if (ret) { dev_err(dev, "failed to request IRQ #%d --> %d\n", omap->irq, ret); - goto err2; + return ret; } /* enable all IRQs */ @@ -410,24 +398,16 @@ static int dwc3_omap_probe(struct platform_device *pdev) dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, reg); - ret = platform_device_add_resources(dwc3, pdev->resource, - pdev->num_resources); - if (ret) { - dev_err(dev, "couldn't add resources to dwc3 device\n"); - goto err2; - } - - ret = platform_device_add(dwc3); - if (ret) { - dev_err(dev, "failed to register dwc3 device\n"); - goto err2; + if (node) { + ret = of_platform_populate(node, NULL, NULL, dev); + if (ret) { + dev_err(&pdev->dev, + "failed to add create dwc3 core\n"); + return ret; + } } return 0; - -err2: - platform_device_put(dwc3); - return ret; } static int dwc3_omap_remove(struct platform_device *pdev) -- cgit v0.10.2 From af310e96a05bdea2517d639e46e2aea3aef21c5c Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 25 Jan 2013 08:30:47 +0530 Subject: usb: dwc3: omap: use runtime API's to enable clocks Before accessing any register, runtime API's should be invoked to enable the clocks. runtime API's are added here to prevent abort during register access. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 78bb2f6..8094230 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -336,6 +337,13 @@ static int dwc3_omap_probe(struct platform_device *pdev) omap->irq = irq; omap->base = base; + pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "get_sync failed with err %d\n", ret); + return ret; + } + reg = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS); utmi_mode = of_get_property(node, "utmi-mode", &size); @@ -416,6 +424,8 @@ static int dwc3_omap_remove(struct platform_device *pdev) platform_device_unregister(omap->usb2_phy); platform_device_unregister(omap->usb3_phy); + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); device_for_each_child(&pdev->dev, NULL, dwc3_omap_remove_core); return 0; -- cgit v0.10.2 From 6373218da195e9baade9416727720646b3a622aa Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 25 Jan 2013 08:30:48 +0530 Subject: usb: dwc3: omap: Remove explicit writes to SYSCONFIG register The runtime API's takes care of setting the SYSCONFIG register with appropriate values. Hence explicit writes to SYSCONFIG register is removed. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 8094230..f85ae5e 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -80,23 +80,6 @@ /* SYSCONFIG REGISTER */ #define USBOTGSS_SYSCONFIG_DMADISABLE (1 << 16) -#define USBOTGSS_SYSCONFIG_STANDBYMODE(x) ((x) << 4) - -#define USBOTGSS_STANDBYMODE_FORCE_STANDBY 0 -#define USBOTGSS_STANDBYMODE_NO_STANDBY 1 -#define USBOTGSS_STANDBYMODE_SMART_STANDBY 2 -#define USBOTGSS_STANDBYMODE_SMART_WAKEUP 3 - -#define USBOTGSS_STANDBYMODE_MASK (0x03 << 4) - -#define USBOTGSS_SYSCONFIG_IDLEMODE(x) ((x) << 2) - -#define USBOTGSS_IDLEMODE_FORCE_IDLE 0 -#define USBOTGSS_IDLEMODE_NO_IDLE 1 -#define USBOTGSS_IDLEMODE_SMART_IDLE 2 -#define USBOTGSS_IDLEMODE_SMART_WAKEUP 3 - -#define USBOTGSS_IDLEMODE_MASK (0x03 << 2) /* IRQ_EOI REGISTER */ #define USBOTGSS_IRQ_EOI_LINE_NUMBER (1 << 0) @@ -373,15 +356,6 @@ static int dwc3_omap_probe(struct platform_device *pdev) reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG); omap->dma_status = !!(reg & USBOTGSS_SYSCONFIG_DMADISABLE); - /* Set No-Idle and No-Standby */ - reg &= ~(USBOTGSS_STANDBYMODE_MASK - | USBOTGSS_IDLEMODE_MASK); - - reg |= (USBOTGSS_SYSCONFIG_STANDBYMODE(USBOTGSS_STANDBYMODE_NO_STANDBY) - | USBOTGSS_SYSCONFIG_IDLEMODE(USBOTGSS_IDLEMODE_NO_IDLE)); - - dwc3_omap_writel(omap->base, USBOTGSS_SYSCONFIG, reg); - ret = devm_request_irq(dev, omap->irq, dwc3_omap_interrupt, 0, "dwc3-omap", omap); if (ret) { -- cgit v0.10.2 From 7e41bba94617b7e4f77d3531a63fbfacdf6842a6 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 25 Jan 2013 08:30:49 +0530 Subject: usb: dwc3: omap: Add an API to write to dwc mailbox Add an API in the omap glue layer to write to the mailbox register which can be used by comparator driver(twl). To pass the detection of the attached device (signified by VBUS, ID) to the dwc3 core, dwc3 core has to write to the mailbox regiter. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index f85ae5e..831b75f 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -131,6 +132,8 @@ struct dwc3_omap { u32 dma_status:1; }; +struct dwc3_omap *_omap; + static inline u32 dwc3_omap_readl(void __iomem *base, u32 offset) { return readl(base + offset); @@ -141,6 +144,57 @@ static inline void dwc3_omap_writel(void __iomem *base, u32 offset, u32 value) writel(value, base + offset); } +void dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status) +{ + u32 val; + struct dwc3_omap *omap = _omap; + + switch (status) { + case OMAP_DWC3_ID_GROUND: + dev_dbg(omap->dev, "ID GND\n"); + + val = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS); + val &= ~(USBOTGSS_UTMI_OTG_STATUS_IDDIG + | USBOTGSS_UTMI_OTG_STATUS_VBUSVALID + | USBOTGSS_UTMI_OTG_STATUS_SESSEND); + val |= USBOTGSS_UTMI_OTG_STATUS_SESSVALID + | USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT; + dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, val); + break; + + case OMAP_DWC3_VBUS_VALID: + dev_dbg(omap->dev, "VBUS Connect\n"); + + val = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS); + val &= ~USBOTGSS_UTMI_OTG_STATUS_SESSEND; + val |= USBOTGSS_UTMI_OTG_STATUS_IDDIG + | USBOTGSS_UTMI_OTG_STATUS_VBUSVALID + | USBOTGSS_UTMI_OTG_STATUS_SESSVALID + | USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT; + dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, val); + break; + + case OMAP_DWC3_ID_FLOAT: + case OMAP_DWC3_VBUS_OFF: + dev_dbg(omap->dev, "VBUS Disconnect\n"); + + val = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS); + val &= ~(USBOTGSS_UTMI_OTG_STATUS_SESSVALID + | USBOTGSS_UTMI_OTG_STATUS_VBUSVALID + | USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT); + val |= USBOTGSS_UTMI_OTG_STATUS_SESSEND + | USBOTGSS_UTMI_OTG_STATUS_IDDIG; + dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, val); + break; + + default: + dev_dbg(omap->dev, "ID float\n"); + } + + return; +} +EXPORT_SYMBOL_GPL(dwc3_omap_mailbox); + static int dwc3_omap_register_phys(struct dwc3_omap *omap) { struct nop_usb_xceiv_platform_data pdata; @@ -320,6 +374,12 @@ static int dwc3_omap_probe(struct platform_device *pdev) omap->irq = irq; omap->base = base; + /* + * REVISIT if we ever have two instances of the wrapper, we will be + * in big trouble + */ + _omap = omap; + pm_runtime_enable(dev); ret = pm_runtime_get_sync(dev); if (ret < 0) { diff --git a/include/linux/usb/dwc3-omap.h b/include/linux/usb/dwc3-omap.h new file mode 100644 index 0000000..51eae14 --- /dev/null +++ b/include/linux/usb/dwc3-omap.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2013 by Texas Instruments + * + * The Inventra Controller Driver for Linux is free software; you + * can redistribute it and/or modify it under the terms of the GNU + * General Public License version 2 as published by the Free Software + * Foundation. + */ + +#ifndef __DWC3_OMAP_H__ +#define __DWC3_OMAP_H__ + +enum omap_dwc3_vbus_id_status { + OMAP_DWC3_UNKNOWN = 0, + OMAP_DWC3_ID_GROUND, + OMAP_DWC3_ID_FLOAT, + OMAP_DWC3_VBUS_VALID, + OMAP_DWC3_VBUS_OFF, +}; + +#if (defined(CONFIG_USB_DWC3) || defined(CONFIG_USB_DWC3_MODULE)) +extern void dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status); +#else +static inline void dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status) +{ + return; +} +#endif + +#endif /* __DWC3_OMAP_H__ */ -- cgit v0.10.2 From 8ba007a971bb236be017cb8351c383ce6eadf095 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 25 Jan 2013 08:30:54 +0530 Subject: usb: dwc3: core: enable the USB2 and USB3 phy in probe Enabled the USB2 and USB3 PHY in probe by calling usb_phy_set_suspend and disabled the PHYs on driver removal. When PM is implemented this will be optimized to enable the PHYs only when needed. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 3a4004a..f994d31 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -432,6 +432,9 @@ static int dwc3_probe(struct platform_device *pdev) return -EPROBE_DEFER; } + usb_phy_set_suspend(dwc->usb2_phy, 0); + usb_phy_set_suspend(dwc->usb3_phy, 0); + spin_lock_init(&dwc->lock); platform_set_drvdata(pdev, dwc); @@ -554,6 +557,9 @@ static int dwc3_remove(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + usb_phy_set_suspend(dwc->usb2_phy, 1); + usb_phy_set_suspend(dwc->usb3_phy, 1); + pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); -- cgit v0.10.2 From 5ea921320f0e64f07a41d35903f1d458c3a9bcd3 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 25 Jan 2013 08:30:55 +0530 Subject: usb: dwc3: core: stray statements are removed No functional change. Stray statements where removed from dwc3 core. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index f994d31..177f4c6 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -553,9 +553,6 @@ err0: static int dwc3_remove(struct platform_device *pdev) { struct dwc3 *dwc = platform_get_drvdata(pdev); - struct resource *res; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); usb_phy_set_suspend(dwc->usb2_phy, 1); usb_phy_set_suspend(dwc->usb3_phy, 1); -- cgit v0.10.2 From b0e45ddb96d5a972a8b76354f036b90549ae85b3 Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Fri, 25 Jan 2013 16:52:01 +0530 Subject: usb: dwc3: exynos/omap: Change platform device IDs for no_op_xceive to AUTO Multiple dwc3 probe calls try to allocate no_op_xceive platform device. Having static IDs for these will throw sysfs error -EEXIST. Changing these static platform device IDs to AUTO to enable multiple dwc3 controller support on a SoC. Signed-off-by: Vivek Gautam Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index 1e95551..b50da53 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -42,7 +42,7 @@ static int dwc3_exynos_register_phys(struct dwc3_exynos *exynos) memset(&pdata, 0x00, sizeof(pdata)); - pdev = platform_device_alloc("nop_usb_xceiv", 0); + pdev = platform_device_alloc("nop_usb_xceiv", PLATFORM_DEVID_AUTO); if (!pdev) return -ENOMEM; @@ -53,7 +53,7 @@ static int dwc3_exynos_register_phys(struct dwc3_exynos *exynos) if (ret) goto err1; - pdev = platform_device_alloc("nop_usb_xceiv", 1); + pdev = platform_device_alloc("nop_usb_xceiv", PLATFORM_DEVID_AUTO); if (!pdev) { ret = -ENOMEM; goto err1; diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 831b75f..22f337f 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -203,7 +203,7 @@ static int dwc3_omap_register_phys(struct dwc3_omap *omap) memset(&pdata, 0x00, sizeof(pdata)); - pdev = platform_device_alloc("nop_usb_xceiv", 0); + pdev = platform_device_alloc("nop_usb_xceiv", PLATFORM_DEVID_AUTO); if (!pdev) return -ENOMEM; @@ -214,7 +214,7 @@ static int dwc3_omap_register_phys(struct dwc3_omap *omap) if (ret) goto err1; - pdev = platform_device_alloc("nop_usb_xceiv", 1); + pdev = platform_device_alloc("nop_usb_xceiv", PLATFORM_DEVID_AUTO); if (!pdev) { ret = -ENOMEM; goto err1; -- cgit v0.10.2 From 52758bcb7c12bede2a81849dee13f1edcd44e1c1 Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Fri, 25 Jan 2013 16:52:02 +0530 Subject: usb: dwc3: host: Change platform device ID for xhci-hcd to AUTO Multiple dwc3 controllers will try to allocate multiple xhci-hcd interfaces. Changing platform device IDs from NONE to AUTO to support such cases. Signed-off-by: Vivek Gautam Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index 56a6234..0fa1846 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -44,7 +44,7 @@ int dwc3_host_init(struct dwc3 *dwc) struct platform_device *xhci; int ret; - xhci = platform_device_alloc("xhci-hcd", -1); + xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO); if (!xhci) { dev_err(dwc->dev, "couldn't allocate xHCI device\n"); ret = -ENOMEM; -- cgit v0.10.2