From c503672abe1348f10f5a54a662336358c6e1a297 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Wed, 2 Sep 2020 18:42:58 -0700 Subject: usb: dwc3: gadget: Resume pending requests after CLEAR_STALL The function driver may queue new requests right after halting the endpoint (i.e. queue new requests while the endpoint is stalled). There's no restriction preventing it from doing so. However, dwc3 currently drops those requests after CLEAR_STALL. The driver should only drop started requests. Keep the pending requests in the pending list to resume and process them after the host issues ClearFeature(Halt) to the endpoint. Cc: stable@vger.kernel.org Fixes: cb11ea56f37a ("usb: dwc3: gadget: Properly handle ClearFeature(halt)") Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index c2a0f64f8d1e..c04f7b29535e 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1628,8 +1628,13 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) if (dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE) return 0; - /* Start the transfer only after the END_TRANSFER is completed */ - if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) { + /* + * Start the transfer only after the END_TRANSFER is completed + * and endpoint STALL is cleared. + */ + if ((dep->flags & DWC3_EP_END_TRANSFER_PENDING) || + (dep->flags & DWC3_EP_WEDGE) || + (dep->flags & DWC3_EP_STALL)) { dep->flags |= DWC3_EP_DELAY_START; return 0; } @@ -1836,13 +1841,14 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) list_for_each_entry_safe(req, tmp, &dep->started_list, list) dwc3_gadget_move_cancelled_request(req); - list_for_each_entry_safe(req, tmp, &dep->pending_list, list) - dwc3_gadget_move_cancelled_request(req); - - if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING)) { - dep->flags &= ~DWC3_EP_DELAY_START; + if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING)) dwc3_gadget_ep_cleanup_cancelled_requests(dep); - } + + if ((dep->flags & DWC3_EP_DELAY_START) && + !usb_endpoint_xfer_isoc(dep->endpoint.desc)) + __dwc3_gadget_kick_transfer(dep); + + dep->flags &= ~DWC3_EP_DELAY_START; } return ret; -- cgit v1.2.3 From d97c78a1908e59a1fdbcbece87cd0440b5d7a1f2 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Wed, 2 Sep 2020 18:43:04 -0700 Subject: usb: dwc3: gadget: END_TRANSFER before CLEAR_STALL command According the programming guide (for all DWC3 IPs), when the driver handles ClearFeature(halt) request, it should issue CLEAR_STALL command _after_ the END_TRANSFER command completes. The END_TRANSFER command may take some time to complete. So, delay the ClearFeature(halt) request control status stage and wait for END_TRANSFER command completion interrupt. Only after END_TRANSFER command completes that the driver may issue CLEAR_STALL command. Cc: stable@vger.kernel.org Fixes: cb11ea56f37a ("usb: dwc3: gadget: Properly handle ClearFeature(halt)") Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 1 + drivers/usb/dwc3/ep0.c | 16 ++++++++++++++++ drivers/usb/dwc3/gadget.c | 40 ++++++++++++++++++++++++++++++++-------- drivers/usb/dwc3/gadget.h | 1 + 4 files changed, 50 insertions(+), 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 2f04b3e42bf1..eb026c9cca28 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -706,6 +706,7 @@ struct dwc3_ep { #define DWC3_EP_IGNORE_NEXT_NOSTREAM BIT(8) #define DWC3_EP_FORCE_RESTART_STREAM BIT(9) #define DWC3_EP_FIRST_STREAM_PRIMED BIT(10) +#define DWC3_EP_PENDING_CLEAR_STALL BIT(11) /* This last one is specific to EP0 */ #define DWC3_EP0_DIR_IN BIT(31) diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 59f2e8c31bd1..92bc1044e7ab 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -524,6 +524,11 @@ static int dwc3_ep0_handle_endpoint(struct dwc3 *dwc, ret = __dwc3_gadget_ep_set_halt(dep, set, true); if (ret) return -EINVAL; + + /* ClearFeature(Halt) may need delayed status */ + if (!set && (dep->flags & DWC3_EP_END_TRANSFER_PENDING)) + return USB_GADGET_DELAYED_STATUS; + break; default: return -EINVAL; @@ -1042,6 +1047,17 @@ static void dwc3_ep0_do_control_status(struct dwc3 *dwc, __dwc3_ep0_do_control_status(dwc, dep); } +void dwc3_ep0_send_delayed_status(struct dwc3 *dwc) +{ + unsigned int direction = !dwc->ep0_expect_in; + + if (dwc->ep0state != EP0_STATUS_PHASE) + return; + + dwc->delayed_status = false; + __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]); +} + static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep) { struct dwc3_gadget_ep_cmd_params params; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index c04f7b29535e..f9843ad93577 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1827,6 +1827,18 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) return 0; } + dwc3_stop_active_transfer(dep, true, true); + + list_for_each_entry_safe(req, tmp, &dep->started_list, list) + dwc3_gadget_move_cancelled_request(req); + + if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) { + dep->flags |= DWC3_EP_PENDING_CLEAR_STALL; + return 0; + } + + dwc3_gadget_ep_cleanup_cancelled_requests(dep); + ret = dwc3_send_clear_stall_ep_cmd(dep); if (ret) { dev_err(dwc->dev, "failed to clear STALL on %s\n", @@ -1836,14 +1848,6 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE); - dwc3_stop_active_transfer(dep, true, true); - - list_for_each_entry_safe(req, tmp, &dep->started_list, list) - dwc3_gadget_move_cancelled_request(req); - - if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING)) - dwc3_gadget_ep_cleanup_cancelled_requests(dep); - if ((dep->flags & DWC3_EP_DELAY_START) && !usb_endpoint_xfer_isoc(dep->endpoint.desc)) __dwc3_gadget_kick_transfer(dep); @@ -3003,6 +3007,26 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING; dep->flags &= ~DWC3_EP_TRANSFER_STARTED; dwc3_gadget_ep_cleanup_cancelled_requests(dep); + + if (dep->flags & DWC3_EP_PENDING_CLEAR_STALL) { + struct dwc3 *dwc = dep->dwc; + + dep->flags &= ~DWC3_EP_PENDING_CLEAR_STALL; + if (dwc3_send_clear_stall_ep_cmd(dep)) { + struct usb_ep *ep0 = &dwc->eps[0]->endpoint; + + dev_err(dwc->dev, "failed to clear STALL on %s\n", + dep->name); + if (dwc->delayed_status) + __dwc3_gadget_ep0_set_halt(ep0, 1); + return; + } + + dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE); + if (dwc->delayed_status) + dwc3_ep0_send_delayed_status(dwc); + } + if ((dep->flags & DWC3_EP_DELAY_START) && !usb_endpoint_xfer_isoc(dep->endpoint.desc)) __dwc3_gadget_kick_transfer(dep); diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index bd85eb7fa9ef..a7791cb827c4 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -113,6 +113,7 @@ int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value); int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, gfp_t gfp_flags); int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol); +void dwc3_ep0_send_delayed_status(struct dwc3 *dwc); /** * dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW -- cgit v1.2.3 From 98df91f8840cf750a0bc7c4c5d6b6085bac945b3 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Tue, 1 Sep 2020 10:35:49 +0800 Subject: usb: cdns3: gadget: free interrupt after gadget has deleted The interrupt may occur during the gadget deletion, it fixes the below oops. [ 2394.974604] configfs-gadget gadget: suspend [ 2395.042578] configfs-gadget 5b130000.usb: unregistering UDC driver [g1] [ 2395.382562] irq 229: nobody cared (try booting with the "irqpoll" option) [ 2395.389362] CPU: 0 PID: 301 Comm: kworker/u12:6 Not tainted 5.8.0-rc3-next-20200703-00060-g2f13b83cbf30-dirty #456 [ 2395.399712] Hardware name: Freescale i.MX8QM MEK (DT) [ 2395.404782] Workqueue: 2-0051 tcpm_state_machine_work [ 2395.409832] Call trace: [ 2395.412289] dump_backtrace+0x0/0x1d0 [ 2395.415950] show_stack+0x1c/0x28 [ 2395.419271] dump_stack+0xbc/0x118 [ 2395.422678] __report_bad_irq+0x50/0xe0 [ 2395.426513] note_interrupt+0x2cc/0x38c [ 2395.430355] handle_irq_event_percpu+0x88/0x90 [ 2395.434800] handle_irq_event+0x4c/0xe8 [ 2395.438640] handle_fasteoi_irq+0xbc/0x168 [ 2395.442740] generic_handle_irq+0x34/0x48 [ 2395.446752] __handle_domain_irq+0x68/0xc0 [ 2395.450846] gic_handle_irq+0x64/0x150 [ 2395.454596] el1_irq+0xb8/0x180 [ 2395.457733] __do_softirq+0xac/0x3b8 [ 2395.461310] irq_exit+0xc0/0xe0 [ 2395.464448] __handle_domain_irq+0x6c/0xc0 [ 2395.468540] gic_handle_irq+0x64/0x150 [ 2395.472295] el1_irq+0xb8/0x180 [ 2395.475436] _raw_spin_unlock_irqrestore+0x14/0x48 [ 2395.480232] usb_gadget_disconnect+0x120/0x140 [ 2395.484678] usb_gadget_remove_driver+0xb4/0xd0 [ 2395.489208] usb_del_gadget+0x6c/0xc8 [ 2395.492872] cdns3_gadget_exit+0x5c/0x120 [ 2395.496882] cdns3_role_stop+0x60/0x90 [ 2395.500634] cdns3_role_set+0x64/0xd8 [ 2395.504301] usb_role_switch_set_role.part.0+0x3c/0x90 [ 2395.509444] usb_role_switch_set_role+0x20/0x30 [ 2395.513978] tcpm_mux_set+0x60/0xf8 [ 2395.517470] tcpm_reset_port+0xa4/0xf0 [ 2395.521222] tcpm_detach.part.0+0x44/0x50 [ 2395.525227] tcpm_state_machine_work+0x8b0/0x2360 [ 2395.529932] process_one_work+0x1c8/0x470 [ 2395.533939] worker_thread+0x50/0x420 [ 2395.537603] kthread+0x148/0x168 [ 2395.540830] ret_from_fork+0x10/0x18 [ 2395.544399] handlers: [ 2395.546671] [<000000008dea28da>] cdns3_wakeup_irq [ 2395.551375] [<000000009fee5c61>] cdns3_drd_irq threaded [<000000005148eaec>] cdns3_drd_thread_irq [ 2395.560255] Disabling IRQ #229 [ 2395.563454] configfs-gadget gadget: unbind function 'Mass Storage Function'/000000000132f835 [ 2395.563657] configfs-gadget gadget: unbind [ 2395.563917] udc 5b130000.usb: releasing '5b130000.usb' Fixes: 7733f6c32e36 ("usb: cdns3: Add Cadence USB3 DRD Driver") Cc: Acked-by: Roger Quadros Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index dea649ee173b..02a69e20014b 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -2990,12 +2990,12 @@ void cdns3_gadget_exit(struct cdns3 *cdns) priv_dev = cdns->gadget_dev; - devm_free_irq(cdns->dev, cdns->dev_irq, priv_dev); pm_runtime_mark_last_busy(cdns->dev); pm_runtime_put_autosuspend(cdns->dev); usb_del_gadget_udc(&priv_dev->gadget); + devm_free_irq(cdns->dev, cdns->dev_irq, priv_dev); cdns3_free_all_eps(priv_dev); -- cgit v1.2.3 From b68d9251561f33661e53dd618f1cafe7ec9ec3c2 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 8 Sep 2020 11:58:23 +0200 Subject: usb: dwc3: simple: add support for Hikey 970 This binding driver is needed for Hikey 970 to work, as otherwise a Serror is produced: [ 1.837458] SError Interrupt on CPU0, code 0xbf000002 -- SError [ 1.837462] CPU: 0 PID: 74 Comm: kworker/0:1 Not tainted 5.8.0+ #205 [ 1.837463] Hardware name: HiKey970 (DT) [ 1.837465] Workqueue: events deferred_probe_work_func [ 1.837467] pstate: 20000005 (nzCv daif -PAN -UAO BTYPE=--) [ 1.837468] pc : _raw_spin_unlock_irqrestore+0x18/0x50 [ 1.837469] lr : regmap_unlock_spinlock+0x14/0x20 [ 1.837470] sp : ffff8000124dba60 [ 1.837471] x29: ffff8000124dba60 x28: 0000000000000000 [ 1.837474] x27: ffff0001b7e854c8 x26: ffff80001204ea18 [ 1.837476] x25: 0000000000000005 x24: ffff800011f918f8 [ 1.837479] x23: ffff800011fbb588 x22: ffff0001b7e40e00 [ 1.837481] x21: 0000000000000100 x20: 0000000000000000 [ 1.837483] x19: ffff0001b767ec00 x18: 00000000ff10c000 [ 1.837485] x17: 0000000000000002 x16: 0000b0740fdb9950 [ 1.837488] x15: ffff8000116c1198 x14: ffffffffffffffff [ 1.837490] x13: 0000000000000030 x12: 0101010101010101 [ 1.837493] x11: 0000000000000020 x10: ffff0001bf17d130 [ 1.837495] x9 : 0000000000000000 x8 : ffff0001b6938080 [ 1.837497] x7 : 0000000000000000 x6 : 000000000000003f [ 1.837500] x5 : 0000000000000000 x4 : 0000000000000000 [ 1.837502] x3 : ffff80001096a880 x2 : 0000000000000000 [ 1.837505] x1 : ffff0001b7e40e00 x0 : 0000000100000001 [ 1.837507] Kernel panic - not syncing: Asynchronous SError Interrupt [ 1.837509] CPU: 0 PID: 74 Comm: kworker/0:1 Not tainted 5.8.0+ #205 [ 1.837510] Hardware name: HiKey970 (DT) [ 1.837511] Workqueue: events deferred_probe_work_func [ 1.837513] Call trace: [ 1.837514] dump_backtrace+0x0/0x1e0 [ 1.837515] show_stack+0x18/0x24 [ 1.837516] dump_stack+0xc0/0x11c [ 1.837517] panic+0x15c/0x324 [ 1.837518] nmi_panic+0x8c/0x90 [ 1.837519] arm64_serror_panic+0x78/0x84 [ 1.837520] do_serror+0x158/0x15c [ 1.837521] el1_error+0x84/0x100 [ 1.837522] _raw_spin_unlock_irqrestore+0x18/0x50 [ 1.837523] regmap_write+0x58/0x80 [ 1.837524] hi3660_reset_deassert+0x28/0x34 [ 1.837526] reset_control_deassert+0x50/0x260 [ 1.837527] reset_control_deassert+0xf4/0x260 [ 1.837528] dwc3_probe+0x5dc/0xe6c [ 1.837529] platform_drv_probe+0x54/0xb0 [ 1.837530] really_probe+0xe0/0x490 [ 1.837531] driver_probe_device+0xf4/0x160 [ 1.837532] __device_attach_driver+0x8c/0x114 [ 1.837533] bus_for_each_drv+0x78/0xcc [ 1.837534] __device_attach+0x108/0x1a0 [ 1.837535] device_initial_probe+0x14/0x20 [ 1.837537] bus_probe_device+0x98/0xa0 [ 1.837538] deferred_probe_work_func+0x88/0xe0 [ 1.837539] process_one_work+0x1cc/0x350 [ 1.837540] worker_thread+0x2c0/0x470 [ 1.837541] kthread+0x154/0x160 [ 1.837542] ret_from_fork+0x10/0x30 [ 1.837569] SMP: stopping secondary CPUs [ 1.837570] Kernel Offset: 0x1d0000 from 0xffff800010000000 [ 1.837571] PHYS_OFFSET: 0x0 [ 1.837572] CPU features: 0x240002,20882004 [ 1.837573] Memory Limit: none Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-of-simple.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c index 7df115012935..2816e4a9813a 100644 --- a/drivers/usb/dwc3/dwc3-of-simple.c +++ b/drivers/usb/dwc3/dwc3-of-simple.c @@ -176,6 +176,7 @@ static const struct of_device_id of_dwc3_simple_match[] = { { .compatible = "cavium,octeon-7130-usb-uctl" }, { .compatible = "sprd,sc9860-dwc3" }, { .compatible = "allwinner,sun50i-h6-dwc3" }, + { .compatible = "hisilicon,hi3670-dwc3" }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(of, of_dwc3_simple_match); -- cgit v1.2.3 From 362b9398c962c9ec563653444e15ef9032ef3a90 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Fri, 24 Jul 2020 23:03:54 -0700 Subject: usb: dwc2: Fix parameter type in function pointer prototype When booting up on a Raspberry Pi 4 with Control Flow Integrity checking enabled, the following warning/panic happens: [ 1.626435] CFI failure (target: dwc2_set_bcm_params+0x0/0x4): [ 1.632408] WARNING: CPU: 0 PID: 32 at kernel/cfi.c:30 __cfi_check_fail+0x54/0x5c [ 1.640021] Modules linked in: [ 1.643137] CPU: 0 PID: 32 Comm: kworker/0:1 Not tainted 5.8.0-rc6-next-20200724-00051-g89ba619726de #1 [ 1.652693] Hardware name: Raspberry Pi 4 Model B Rev 1.2 (DT) [ 1.658637] Workqueue: events deferred_probe_work_func [ 1.663870] pstate: 60000005 (nZCv daif -PAN -UAO BTYPE=--) [ 1.669542] pc : __cfi_check_fail+0x54/0x5c [ 1.673798] lr : __cfi_check_fail+0x54/0x5c [ 1.678050] sp : ffff8000102bbaa0 [ 1.681419] x29: ffff8000102bbaa0 x28: ffffab09e21c7000 [ 1.686829] x27: 0000000000000402 x26: ffff0000f6e7c228 [ 1.692238] x25: 00000000fb7cdb0d x24: 0000000000000005 [ 1.697647] x23: ffffab09e2515000 x22: ffffab09e069a000 [ 1.703055] x21: 4c550309df1cf4c1 x20: ffffab09e2433c60 [ 1.708462] x19: ffffab09e160dc50 x18: ffff0000f6e8cc78 [ 1.713870] x17: 0000000000000041 x16: ffffab09e0bce6f8 [ 1.719278] x15: ffffab09e1c819b7 x14: 0000000000000003 [ 1.724686] x13: 00000000ffffefff x12: 0000000000000000 [ 1.730094] x11: 0000000000000000 x10: 00000000ffffffff [ 1.735501] x9 : c932f7abfc4bc600 x8 : c932f7abfc4bc600 [ 1.740910] x7 : 077207610770075f x6 : ffff0000f6c38f00 [ 1.746317] x5 : 0000000000000000 x4 : 0000000000000000 [ 1.751723] x3 : 0000000000000000 x2 : 0000000000000000 [ 1.757129] x1 : ffff8000102bb7d8 x0 : 0000000000000032 [ 1.762539] Call trace: [ 1.765030] __cfi_check_fail+0x54/0x5c [ 1.768938] __cfi_check+0x5fa6c/0x66afc [ 1.772932] dwc2_init_params+0xd74/0xd78 [ 1.777012] dwc2_driver_probe+0x484/0x6ec [ 1.781180] platform_drv_probe+0xb4/0x100 [ 1.785350] really_probe+0x228/0x63c [ 1.789076] driver_probe_device+0x80/0xc0 [ 1.793247] __device_attach_driver+0x114/0x160 [ 1.797857] bus_for_each_drv+0xa8/0x128 [ 1.801851] __device_attach.llvm.14901095709067289134+0xc0/0x170 [ 1.808050] bus_probe_device+0x44/0x100 [ 1.812044] deferred_probe_work_func+0x78/0xb8 [ 1.816656] process_one_work+0x204/0x3c4 [ 1.820736] worker_thread+0x2f0/0x4c4 [ 1.824552] kthread+0x174/0x184 [ 1.827837] ret_from_fork+0x10/0x18 CFI validates that all indirect calls go to a function with the same exact function pointer prototype. In this case, dwc2_set_bcm_params is the target, which has a parameter of type 'struct dwc2_hsotg *', but it is being implicitly cast to have a parameter of type 'void *' because that is the set_params function pointer prototype. Make the function pointer protoype match the definitions so that there is no more violation. Fixes: 7de1debcd2de ("usb: dwc2: Remove platform static params") Link: https://github.com/ClangBuiltLinux/linux/issues/1107 Signed-off-by: Nathan Chancellor Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/params.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index 8f9d061c4d5f..a3611cdd1dea 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -860,7 +860,7 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) int dwc2_init_params(struct dwc2_hsotg *hsotg) { const struct of_device_id *match; - void (*set_params)(void *data); + void (*set_params)(struct dwc2_hsotg *data); dwc2_set_default_params(hsotg); dwc2_get_device_properties(hsotg); -- cgit v1.2.3 From b574ce3ee45937f4a01edc98c59213bfc7effe50 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Fri, 24 Jul 2020 14:01:02 -0700 Subject: usb: dwc3: core: Properly default unspecified speed If the maximum_speed is not specified, default the device speed base on its HW capability. Don't prematurely check HW capability before validating the maximum_speed device property. The device property takes precedence in dwc->maximum_speed. Fixes: 0e1e5c47f7a9 ("usb: dwc3: add support for USB 2.0-only core configuration") Reported-by: Chunfeng Yun Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 2eb34c8b4065..c8e0ef2c1db3 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -929,13 +929,6 @@ static int dwc3_core_init(struct dwc3 *dwc) */ dwc3_writel(dwc->regs, DWC3_GUID, LINUX_VERSION_CODE); - /* Handle USB2.0-only core configuration */ - if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) == - DWC3_GHWPARAMS3_SSPHY_IFC_DIS) { - if (dwc->maximum_speed == USB_SPEED_SUPER) - dwc->maximum_speed = USB_SPEED_HIGH; - } - ret = dwc3_phy_setup(dwc); if (ret) goto err0; @@ -1381,6 +1374,8 @@ bool dwc3_has_imod(struct dwc3 *dwc) static void dwc3_check_params(struct dwc3 *dwc) { struct device *dev = dwc->dev; + unsigned int hwparam_gen = + DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3); /* Check for proper value of imod_interval */ if (dwc->imod_interval && !dwc3_has_imod(dwc)) { @@ -1412,17 +1407,23 @@ static void dwc3_check_params(struct dwc3 *dwc) dwc->maximum_speed); fallthrough; case USB_SPEED_UNKNOWN: - /* default to superspeed */ - dwc->maximum_speed = USB_SPEED_SUPER; - - /* - * default to superspeed plus if we are capable. - */ - if ((DWC3_IP_IS(DWC31) || DWC3_IP_IS(DWC32)) && - (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) == - DWC3_GHWPARAMS3_SSPHY_IFC_GEN2)) + switch (hwparam_gen) { + case DWC3_GHWPARAMS3_SSPHY_IFC_GEN2: dwc->maximum_speed = USB_SPEED_SUPER_PLUS; - + break; + case DWC3_GHWPARAMS3_SSPHY_IFC_GEN1: + if (DWC3_IP_IS(DWC32)) + dwc->maximum_speed = USB_SPEED_SUPER_PLUS; + else + dwc->maximum_speed = USB_SPEED_SUPER; + break; + case DWC3_GHWPARAMS3_SSPHY_IFC_DIS: + dwc->maximum_speed = USB_SPEED_HIGH; + break; + default: + dwc->maximum_speed = USB_SPEED_SUPER; + break; + } break; } } -- cgit v1.2.3 From e518bdd9f02c11c6fcbd75884f2a38782bedd1b3 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Fri, 24 Jul 2020 14:01:09 -0700 Subject: usb: dwc3: core: Print warning on unsupported speed The user may have more information to override the HW parameter to specify the maximum_speed. However, if the user specifies a maximum_speed that the controller doesn't support, print out a warning. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index c8e0ef2c1db3..f3093b4e5307 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1399,8 +1399,17 @@ static void dwc3_check_params(struct dwc3 *dwc) case USB_SPEED_LOW: case USB_SPEED_FULL: case USB_SPEED_HIGH: + break; case USB_SPEED_SUPER: + if (hwparam_gen == DWC3_GHWPARAMS3_SSPHY_IFC_DIS) + dev_warn(dev, "UDC doesn't support Gen 1\n"); + break; case USB_SPEED_SUPER_PLUS: + if ((DWC3_IP_IS(DWC32) && + hwparam_gen == DWC3_GHWPARAMS3_SSPHY_IFC_DIS) || + (!DWC3_IP_IS(DWC32) && + hwparam_gen != DWC3_GHWPARAMS3_SSPHY_IFC_GEN2)) + dev_warn(dev, "UDC doesn't support SSP\n"); break; default: dev_err(dev, "invalid maximum_speed parameter %d\n", -- cgit v1.2.3 From e1c08cf23172ed6fb228d75efc9f4c80a6812116 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sat, 4 Jul 2020 00:50:43 +0200 Subject: usb: dwc2: Add missing cleanups when usb_add_gadget_udc() fails Call dwc2_debugfs_exit() and dwc2_hcd_remove() (if the HCD was enabled earlier) when usb_add_gadget_udc() has failed. This ensures that the debugfs entries created by dwc2_debugfs_init() as well as the HCD are cleaned up in the error path. Fixes: 207324a321a866 ("usb: dwc2: Postponed gadget registration to the udc class driver") Acked-by: Minas Harutyunyan Signed-off-by: Martin Blumenstingl Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/platform.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index db9fd4bd1a38..b28e90e0b685 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -584,12 +584,16 @@ static int dwc2_driver_probe(struct platform_device *dev) if (retval) { hsotg->gadget.udc = NULL; dwc2_hsotg_remove(hsotg); - goto error_init; + goto error_debugfs; } } #endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */ return 0; +error_debugfs: + dwc2_debugfs_exit(hsotg); + if (hsotg->hcd_enabled) + dwc2_hcd_remove(hsotg); error_init: if (hsotg->params.activate_stm_id_vb_detection) regulator_disable(hsotg->usb33d); -- cgit v1.2.3 From 3a48217854458a35c0d74db20c3a52ec7f990360 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Thu, 23 Jul 2020 21:48:57 +0300 Subject: usb: gadget: udc: atmel: use of_find_matching_node_and_match Instead of trying to match every possible compatible use of_find_matching_node_and_match() and pass the compatible array. Signed-off-by: Claudiu Beznea Signed-off-by: Cristian Birsan Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/atmel_usba_udc.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index a6426dd1cfef..bc7f15742a4b 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -2112,11 +2112,19 @@ static const struct of_device_id atmel_udc_dt_ids[] = { MODULE_DEVICE_TABLE(of, atmel_udc_dt_ids); +static const struct of_device_id atmel_pmc_dt_ids[] = { + { .compatible = "atmel,at91sam9g45-pmc" }, + { .compatible = "atmel,at91sam9rl-pmc" }, + { .compatible = "atmel,at91sam9x5-pmc" }, + { /* sentinel */ } +}; + static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev, struct usba_udc *udc) { struct device_node *np = pdev->dev.of_node; const struct of_device_id *match; + struct device_node *pp; int i, ret; struct usba_ep *eps, *ep; const struct usba_udc_config *udc_config; @@ -2127,13 +2135,17 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev, udc_config = match->data; udc->errata = udc_config->errata; - udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9g45-pmc"); - if (IS_ERR(udc->pmc)) - udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9rl-pmc"); - if (IS_ERR(udc->pmc)) - udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9x5-pmc"); - if (udc->errata && IS_ERR(udc->pmc)) - return ERR_CAST(udc->pmc); + if (udc->errata) { + pp = of_find_matching_node_and_match(NULL, atmel_pmc_dt_ids, + NULL); + if (!pp) + return ERR_PTR(-ENODEV); + + udc->pmc = syscon_node_to_regmap(pp); + of_node_put(pp); + if (IS_ERR(udc->pmc)) + return ERR_CAST(udc->pmc); + } udc->num_ep = 0; -- cgit v1.2.3 From 033b8966e90611f6f76daf4d16bb7fe3ef36a819 Mon Sep 17 00:00:00 2001 From: Cristian Birsan Date: Thu, 23 Jul 2020 21:48:59 +0300 Subject: usb: gadget: udc: atmel: simplify endpoint allocation Simplify the endpoint allocation and cleanup the code. Signed-off-by: Cristian Birsan Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/atmel_usba_udc.c | 21 ++++++++------------- drivers/usb/gadget/udc/atmel_usba_udc.h | 1 - 2 files changed, 8 insertions(+), 14 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index bc7f15742a4b..2e9044bcac01 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -1091,8 +1091,6 @@ found_ep: USBA_BF(EPT_SIZE, fls(ep->fifo_size - 1) - 3); ep->ept_cfg |= USBA_BF(BK_NUMBER, ep->nr_banks); - - ep->udc->configured_ep++; } return _ep; @@ -1786,7 +1784,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) if (status & USBA_END_OF_RESET) { struct usba_ep *ep0, *ep; - int i, n; + int i; usba_writel(udc, INT_CLR, USBA_END_OF_RESET|USBA_END_OF_RESUME @@ -1834,13 +1832,14 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) "ODD: EP0 configuration is invalid!\n"); /* Preallocate other endpoints */ - n = fifo_mode ? udc->num_ep : udc->configured_ep; - for (i = 1; i < n; i++) { + for (i = 1; i < udc->num_ep; i++) { ep = &udc->usba_ep[i]; - usba_ep_writel(ep, CFG, ep->ept_cfg); - if (!(usba_ep_readl(ep, CFG) & USBA_EPT_MAPPED)) - dev_err(&udc->pdev->dev, - "ODD: EP%d configuration is invalid!\n", i); + if (ep->ep.claimed) { + usba_ep_writel(ep, CFG, ep->ept_cfg); + if (!(usba_ep_readl(ep, CFG) & USBA_EPT_MAPPED)) + dev_err(&udc->pdev->dev, + "ODD: EP%d configuration is invalid!\n", i); + } } } @@ -2025,9 +2024,6 @@ static int atmel_usba_stop(struct usb_gadget *gadget) if (udc->vbus_pin) disable_irq(gpiod_to_irq(udc->vbus_pin)); - if (fifo_mode == 0) - udc->configured_ep = 1; - udc->suspended = false; usba_stop(udc); @@ -2154,7 +2150,6 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev, if (fifo_mode == 0) { udc->num_ep = udc_config->num_ep; - udc->configured_ep = 1; } else { udc->num_ep = usba_config_fifo_table(udc); } diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h index 48e332439ed5..a9bf655eb513 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.h +++ b/drivers/usb/gadget/udc/atmel_usba_udc.h @@ -336,7 +336,6 @@ struct usba_udc { int irq; struct gpio_desc *vbus_pin; int num_ep; - int configured_ep; struct usba_fifo_cfg *fifo_cfg; struct clk *pclk; struct clk *hclk; -- cgit v1.2.3 From 5b041a30448f6af2676de01685812b260bd95b31 Mon Sep 17 00:00:00 2001 From: Cristian Birsan Date: Thu, 23 Jul 2020 21:49:00 +0300 Subject: usb: gadget: udc: atmel: use 1 bank endpoints for control transfers Use 1 bank endpoints for control transfers Signed-off-by: Cristian Birsan Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/atmel_usba_udc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 2e9044bcac01..d8b693b34b57 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -1056,6 +1056,7 @@ found_ep: switch (usb_endpoint_type(desc)) { case USB_ENDPOINT_XFER_CONTROL: + ep->nr_banks = 1; break; case USB_ENDPOINT_XFER_ISOC: -- cgit v1.2.3 From 26b324245018cc966c81097452017dea8f43ffc5 Mon Sep 17 00:00:00 2001 From: Cristian Birsan Date: Thu, 23 Jul 2020 21:49:01 +0300 Subject: usb: gadget: udc: atmel: update endpoint allocation for sam9x60 The DPRAM memory from the USB High Speed Device Port (UDPHS) hardware block was increased. This patch updates the endpoint allocation for sam9x60 to take advantage of this larger memory. At the same time the constraint to allocate the endpoints in order was lifted. To handle old and new hardware in the same driver the ep_prealloc was added. Signed-off-by: Cristian Birsan Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/atmel_usba_udc.c | 20 +++++++++++++++++--- drivers/usb/gadget/udc/atmel_usba_udc.h | 2 ++ 2 files changed, 19 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index d8b693b34b57..2b893bceea45 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -1061,12 +1061,14 @@ found_ep: case USB_ENDPOINT_XFER_ISOC: ep->fifo_size = 1024; - ep->nr_banks = 2; + if (ep->udc->ep_prealloc) + ep->nr_banks = 2; break; case USB_ENDPOINT_XFER_BULK: ep->fifo_size = 512; - ep->nr_banks = 1; + if (ep->udc->ep_prealloc) + ep->nr_banks = 1; break; case USB_ENDPOINT_XFER_INT: @@ -1076,7 +1078,8 @@ found_ep: else ep->fifo_size = roundup_pow_of_two(le16_to_cpu(desc->wMaxPacketSize)); - ep->nr_banks = 1; + if (ep->udc->ep_prealloc) + ep->nr_banks = 1; break; } @@ -2087,23 +2090,33 @@ static const struct usba_udc_config udc_at91sam9rl_cfg = { .errata = &at91sam9rl_errata, .config = ep_config_sam9, .num_ep = ARRAY_SIZE(ep_config_sam9), + .ep_prealloc = true, }; static const struct usba_udc_config udc_at91sam9g45_cfg = { .errata = &at91sam9g45_errata, .config = ep_config_sam9, .num_ep = ARRAY_SIZE(ep_config_sam9), + .ep_prealloc = true, }; static const struct usba_udc_config udc_sama5d3_cfg = { .config = ep_config_sama5, .num_ep = ARRAY_SIZE(ep_config_sama5), + .ep_prealloc = true, +}; + +static const struct usba_udc_config udc_sam9x60_cfg = { + .num_ep = ARRAY_SIZE(ep_config_sam9), + .config = ep_config_sam9, + .ep_prealloc = false, }; static const struct of_device_id atmel_udc_dt_ids[] = { { .compatible = "atmel,at91sam9rl-udc", .data = &udc_at91sam9rl_cfg }, { .compatible = "atmel,at91sam9g45-udc", .data = &udc_at91sam9g45_cfg }, { .compatible = "atmel,sama5d3-udc", .data = &udc_sama5d3_cfg }, + { .compatible = "microchip,sam9x60-udc", .data = &udc_sam9x60_cfg }, { /* sentinel */ } }; @@ -2131,6 +2144,7 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev, return ERR_PTR(-EINVAL); udc_config = match->data; + udc->ep_prealloc = udc_config->ep_prealloc; udc->errata = udc_config->errata; if (udc->errata) { pp = of_find_matching_node_and_match(NULL, atmel_pmc_dt_ids, diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h index a9bf655eb513..620472f218bc 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.h +++ b/drivers/usb/gadget/udc/atmel_usba_udc.h @@ -317,6 +317,7 @@ struct usba_udc_config { const struct usba_udc_errata *errata; const struct usba_ep_config *config; const int num_ep; + const bool ep_prealloc; }; struct usba_udc { @@ -343,6 +344,7 @@ struct usba_udc { bool bias_pulse_needed; bool clocked; bool suspended; + bool ep_prealloc; u16 devstatus; -- cgit v1.2.3 From 072f34c2ebdb0bbc34f05187c7a770a3a23996c6 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 27 Jun 2020 12:31:50 +0200 Subject: usb: gadget: udc: Drop surplus include The UDC NET2272 driver includes but does not use any symbols from this file, so drop the include. Cc: Felipe Balbi Signed-off-by: Linus Walleij Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2272.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c index 44d1ea2307bb..440bcb3b6c23 100644 --- a/drivers/usb/gadget/udc/net2272.c +++ b/drivers/usb/gadget/udc/net2272.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From bea46b9815154ac47baf16b64022d791a4471375 Mon Sep 17 00:00:00 2001 From: Sandeep Maheswaram Date: Mon, 27 Jul 2020 22:36:36 +0530 Subject: usb: dwc3: qcom: Add interconnect support in dwc3 driver Add interconnect support in dwc3-qcom driver to vote for bus bandwidth. This requires for two different paths - from USB to DDR. The other is from APPS to USB. Reviewed-by: Matthias Kaehlcke Signed-off-by: Sandeep Maheswaram Signed-off-by: Chandana Kishori Chiluveru Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-qcom.c | 120 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 118 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index e1e78e9824b1..fcf7f79fb983 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -43,6 +44,14 @@ #define SDM845_QSCRATCH_SIZE 0x400 #define SDM845_DWC3_CORE_SIZE 0xcd00 +/* Interconnect path bandwidths in MBps */ +#define USB_MEMORY_AVG_HS_BW MBps_to_icc(240) +#define USB_MEMORY_PEAK_HS_BW MBps_to_icc(700) +#define USB_MEMORY_AVG_SS_BW MBps_to_icc(1000) +#define USB_MEMORY_PEAK_SS_BW MBps_to_icc(2500) +#define APPS_USB_AVG_BW 0 +#define APPS_USB_PEAK_BW MBps_to_icc(40) + struct dwc3_acpi_pdata { u32 qscratch_base_offset; u32 qscratch_base_size; @@ -76,6 +85,8 @@ struct dwc3_qcom { enum usb_dr_mode mode; bool is_suspended; bool pm_suspended; + struct icc_path *icc_path_ddr; + struct icc_path *icc_path_apps; }; static inline void dwc3_qcom_setbits(void __iomem *base, u32 offset, u32 val) @@ -190,6 +201,96 @@ static int dwc3_qcom_register_extcon(struct dwc3_qcom *qcom) return 0; } +static int dwc3_qcom_interconnect_enable(struct dwc3_qcom *qcom) +{ + int ret; + + ret = icc_enable(qcom->icc_path_ddr); + if (ret) + return ret; + + ret = icc_enable(qcom->icc_path_apps); + if (ret) + icc_disable(qcom->icc_path_ddr); + + return ret; +} + +static int dwc3_qcom_interconnect_disable(struct dwc3_qcom *qcom) +{ + int ret; + + ret = icc_disable(qcom->icc_path_ddr); + if (ret) + return ret; + + ret = icc_disable(qcom->icc_path_apps); + if (ret) + icc_enable(qcom->icc_path_ddr); + + return ret; +} + +/** + * dwc3_qcom_interconnect_init() - Get interconnect path handles + * and set bandwidhth. + * @qcom: Pointer to the concerned usb core. + * + */ +static int dwc3_qcom_interconnect_init(struct dwc3_qcom *qcom) +{ + struct device *dev = qcom->dev; + int ret; + + qcom->icc_path_ddr = of_icc_get(dev, "usb-ddr"); + if (IS_ERR(qcom->icc_path_ddr)) { + dev_err(dev, "failed to get usb-ddr path: %ld\n", + PTR_ERR(qcom->icc_path_ddr)); + return PTR_ERR(qcom->icc_path_ddr); + } + + qcom->icc_path_apps = of_icc_get(dev, "apps-usb"); + if (IS_ERR(qcom->icc_path_apps)) { + dev_err(dev, "failed to get apps-usb path: %ld\n", + PTR_ERR(qcom->icc_path_apps)); + return PTR_ERR(qcom->icc_path_apps); + } + + if (usb_get_maximum_speed(&qcom->dwc3->dev) >= USB_SPEED_SUPER || + usb_get_maximum_speed(&qcom->dwc3->dev) == USB_SPEED_UNKNOWN) + ret = icc_set_bw(qcom->icc_path_ddr, + USB_MEMORY_AVG_SS_BW, USB_MEMORY_PEAK_SS_BW); + else + ret = icc_set_bw(qcom->icc_path_ddr, + USB_MEMORY_AVG_HS_BW, USB_MEMORY_PEAK_HS_BW); + + if (ret) { + dev_err(dev, "failed to set bandwidth for usb-ddr path: %d\n", ret); + return ret; + } + + ret = icc_set_bw(qcom->icc_path_apps, + APPS_USB_AVG_BW, APPS_USB_PEAK_BW); + if (ret) { + dev_err(dev, "failed to set bandwidth for apps-usb path: %d\n", ret); + return ret; + } + + return 0; +} + +/** + * dwc3_qcom_interconnect_exit() - Release interconnect path handles + * @qcom: Pointer to the concerned usb core. + * + * This function is used to release interconnect path handle. + */ +static void dwc3_qcom_interconnect_exit(struct dwc3_qcom *qcom) +{ + icc_put(qcom->icc_path_ddr); + icc_put(qcom->icc_path_apps); +} + static void dwc3_qcom_disable_interrupts(struct dwc3_qcom *qcom) { if (qcom->hs_phy_irq) { @@ -239,7 +340,7 @@ static void dwc3_qcom_enable_interrupts(struct dwc3_qcom *qcom) static int dwc3_qcom_suspend(struct dwc3_qcom *qcom) { u32 val; - int i; + int i, ret; if (qcom->is_suspended) return 0; @@ -251,6 +352,10 @@ static int dwc3_qcom_suspend(struct dwc3_qcom *qcom) for (i = qcom->num_clocks - 1; i >= 0; i--) clk_disable_unprepare(qcom->clks[i]); + ret = dwc3_qcom_interconnect_disable(qcom); + if (ret) + dev_warn(qcom->dev, "failed to disable interconnect: %d\n", ret); + qcom->is_suspended = true; dwc3_qcom_enable_interrupts(qcom); @@ -276,6 +381,10 @@ static int dwc3_qcom_resume(struct dwc3_qcom *qcom) } } + ret = dwc3_qcom_interconnect_enable(qcom); + if (ret) + dev_warn(qcom->dev, "failed to enable interconnect: %d\n", ret); + /* Clear existing events from PHY related to L2 in/out */ dwc3_qcom_setbits(qcom->qscratch_base, PWR_EVNT_IRQ_STAT_REG, PWR_EVNT_LPM_IN_L2_MASK | PWR_EVNT_LPM_OUT_L2_MASK); @@ -638,6 +747,10 @@ static int dwc3_qcom_probe(struct platform_device *pdev) goto depopulate; } + ret = dwc3_qcom_interconnect_init(qcom); + if (ret) + goto depopulate; + qcom->mode = usb_get_dr_mode(&qcom->dwc3->dev); /* enable vbus override for device mode */ @@ -647,7 +760,7 @@ static int dwc3_qcom_probe(struct platform_device *pdev) /* register extcon to override sw_vbus on Vbus change later */ ret = dwc3_qcom_register_extcon(qcom); if (ret) - goto depopulate; + goto interconnect_exit; device_init_wakeup(&pdev->dev, 1); qcom->is_suspended = false; @@ -657,6 +770,8 @@ static int dwc3_qcom_probe(struct platform_device *pdev) return 0; +interconnect_exit: + dwc3_qcom_interconnect_exit(qcom); depopulate: if (np) of_platform_depopulate(&pdev->dev); @@ -687,6 +802,7 @@ static int dwc3_qcom_remove(struct platform_device *pdev) } qcom->num_clocks = 0; + dwc3_qcom_interconnect_exit(qcom); reset_control_assert(qcom->resets); pm_runtime_allow(dev); -- cgit v1.2.3 From a793cf81ad0c54c13a38f1d7fac03f91fe7136dc Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 13 Aug 2020 08:09:55 +0300 Subject: usb: dwc3: meson: fix coccinelle WARNING Coccinelle suggests using PTR_ERR_OR_ZERO(). Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-meson-g12a.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c index 1f7f4d88ed9d..8fc7e0b39179 100644 --- a/drivers/usb/dwc3/dwc3-meson-g12a.c +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -626,10 +626,7 @@ static int dwc3_meson_gxl_setup_regmaps(struct dwc3_meson_g12a *priv, /* GXL controls the PHY mode in the PHY registers unlike G12A */ priv->usb_glue_regmap = devm_regmap_init_mmio(priv->dev, base, &phy_meson_g12a_usb_glue_regmap_conf); - if (IS_ERR(priv->usb_glue_regmap)) - return PTR_ERR(priv->usb_glue_regmap); - - return 0; + return PTR_ERR_OR_ZERO(priv->usb_glue_regmap); } static int dwc3_meson_g12a_setup_regmaps(struct dwc3_meson_g12a *priv, -- cgit v1.2.3 From 27c7ab0fdd0b0e5883930b9d9d333e0209efc036 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 13 Aug 2020 08:24:21 +0300 Subject: usb: dwc3: debug: fix sparse warning Fix the following sparse warning: drivers/usb/dwc3/trace.c: note: in included file (through drivers/usb/dwc3/trace.h): drivers/usb/dwc3/debug.h:374:39: warning: cast to non-scalar Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/debug.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h index 3d16dac4e5cc..8e03bcb77da8 100644 --- a/drivers/usb/dwc3/debug.h +++ b/drivers/usb/dwc3/debug.h @@ -371,7 +371,9 @@ static inline const char *dwc3_gadget_event_type_string(u8 event) static inline const char *dwc3_decode_event(char *str, size_t size, u32 event, u32 ep0state) { - const union dwc3_event evt = (union dwc3_event) event; + union dwc3_event evt; + + memcpy(&evt, &event, sizeof(event)); if (evt.type.is_devspec) return dwc3_gadget_event_string(str, size, &evt.devt); -- cgit v1.2.3 From e5ee93d42b3fa96aa678b026919950467043abe6 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 13 Aug 2020 08:29:22 +0300 Subject: usb: dwc3: meson: fix checkpatch errors and warnings no functional changes. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-meson-g12a.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c index 8fc7e0b39179..a966e4d16f73 100644 --- a/drivers/usb/dwc3/dwc3-meson-g12a.c +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -116,11 +116,11 @@ static struct clk_bulk_data meson_a1_clocks[] = { { .id = "xtal_usb_ctrl" }, }; -static const char *meson_gxm_phy_names[] = { +static const char * const meson_gxm_phy_names[] = { "usb2-phy0", "usb2-phy1", "usb2-phy2", }; -static const char *meson_g12a_phy_names[] = { +static const char * const meson_g12a_phy_names[] = { "usb2-phy0", "usb2-phy1", "usb3-phy0", }; @@ -132,7 +132,7 @@ static const char *meson_g12a_phy_names[] = { * correctly when only the "usb2-phy1" phy is specified on-par with the * DT bindings. */ -static const char *meson_a1_phy_names[] = { +static const char * const meson_a1_phy_names[] = { "usb2-phy0", "usb2-phy1" }; @@ -143,7 +143,7 @@ struct dwc3_meson_g12a_drvdata { bool otg_phy_host_port_disable; struct clk_bulk_data *clks; int num_clks; - const char **phy_names; + const char * const *phy_names; int num_phys; int (*setup_regmaps)(struct dwc3_meson_g12a *priv, void __iomem *base); int (*usb2_init_phy)(struct dwc3_meson_g12a *priv, int i, @@ -520,11 +520,7 @@ static int dwc3_meson_g12a_role_set(struct usb_role_switch *sw, return 0; if (priv->drvdata->otg_phy_host_port_disable) - dev_warn_once(priv->dev, "Manual OTG switch is broken on this "\ - "SoC, when manual switching from "\ - "Host to device, DWC3 controller "\ - "will need to be resetted in order "\ - "to recover usage of the Host port"); + dev_warn_once(priv->dev, "Broken manual OTG switch\n"); return dwc3_meson_g12a_otg_mode_set(priv, mode); } @@ -903,8 +899,8 @@ static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev) return ret; } - if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) { - ret = regulator_enable(priv->vbus); + if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) { + ret = regulator_enable(priv->vbus); if (ret) return ret; } -- cgit v1.2.3 From 2a499b45295206e7f3dc76edadde891c06cc4447 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 13 Aug 2020 08:30:38 +0300 Subject: usb: dwc3: ulpi: fix checkpatch warning no functional changes. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ulpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/ulpi.c b/drivers/usb/dwc3/ulpi.c index e6e6176386a4..aa213c9815f6 100644 --- a/drivers/usb/dwc3/ulpi.c +++ b/drivers/usb/dwc3/ulpi.c @@ -19,7 +19,7 @@ static int dwc3_ulpi_busyloop(struct dwc3 *dwc) { - unsigned count = 1000; + unsigned int count = 1000; u32 reg; while (count--) { -- cgit v1.2.3 From 159fdf295c67c1a8fbf1e2da58b4708023dbdb36 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 13 Aug 2020 08:32:00 +0300 Subject: usb: dwc3: trace: fix checkpatch warnings no functional changes Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/trace.h | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h index da1be01637c8..97f4f1125a41 100644 --- a/drivers/usb/dwc3/trace.h +++ b/drivers/usb/dwc3/trace.h @@ -104,8 +104,8 @@ DECLARE_EVENT_CLASS(dwc3_log_request, TP_STRUCT__entry( __string(name, req->dep->name) __field(struct dwc3_request *, req) - __field(unsigned, actual) - __field(unsigned, length) + __field(unsigned int, actual) + __field(unsigned int, length) __field(int, status) __field(int, zero) __field(int, short_not_ok) @@ -246,6 +246,7 @@ DECLARE_EVENT_CLASS(dwc3_log_trb, __entry->dequeue, __entry->bph, __entry->bpl, ({char *s; int pcm = ((__entry->size >> 24) & 3) + 1; + switch (__entry->type) { case USB_ENDPOINT_XFER_INT: case USB_ENDPOINT_XFER_ISOC: @@ -291,12 +292,12 @@ DECLARE_EVENT_CLASS(dwc3_log_ep, TP_ARGS(dep), TP_STRUCT__entry( __string(name, dep->name) - __field(unsigned, maxpacket) - __field(unsigned, maxpacket_limit) - __field(unsigned, max_streams) - __field(unsigned, maxburst) - __field(unsigned, flags) - __field(unsigned, direction) + __field(unsigned int, maxpacket) + __field(unsigned int, maxpacket_limit) + __field(unsigned int, max_streams) + __field(unsigned int, maxburst) + __field(unsigned int, flags) + __field(unsigned int, direction) __field(u8, trb_enqueue) __field(u8, trb_dequeue) ), -- cgit v1.2.3 From 035cbca1360a105288787da41ae0c1ee1366e0b5 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 13 Aug 2020 08:33:05 +0300 Subject: usb: dwc3: debug: fix checkpatch warning no functional changes Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/debug.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h index 8e03bcb77da8..8ab394942360 100644 --- a/drivers/usb/dwc3/debug.h +++ b/drivers/usb/dwc3/debug.h @@ -413,8 +413,8 @@ static inline const char *dwc3_gadget_generic_cmd_status_string(int status) #ifdef CONFIG_DEBUG_FS -extern void dwc3_debugfs_init(struct dwc3 *); -extern void dwc3_debugfs_exit(struct dwc3 *); +extern void dwc3_debugfs_init(struct dwc3 *d); +extern void dwc3_debugfs_exit(struct dwc3 *d); #else static inline void dwc3_debugfs_init(struct dwc3 *d) { } -- cgit v1.2.3 From c64b475b8488ae21f88e693f291e8785b0a519d3 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 13 Aug 2020 09:14:09 +0300 Subject: usb: dwc3: ep0: fix checkpatch warnings no functional changes Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 92bc1044e7ab..291482b2ef7e 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -105,7 +105,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, * IRQ we were waiting for is long gone. */ if (dep->flags & DWC3_EP_PENDING_REQUEST) { - unsigned direction; + unsigned int direction; direction = !!(dep->flags & DWC3_EP0_DIR_IN); @@ -127,7 +127,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, * handle it here. */ if (dwc->delayed_status) { - unsigned direction; + unsigned int direction; direction = !dwc->ep0_expect_in; dwc->delayed_status = false; @@ -172,7 +172,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, * XferNotReady(STATUS). */ if (dwc->three_stage_setup) { - unsigned direction; + unsigned int direction; direction = dwc->ep0_expect_in; dwc->ep0state = EP0_DATA_PHASE; -- cgit v1.2.3 From 993ffc5b32d29f7e94d8245f4775e0210671aea0 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 13 Aug 2020 09:15:12 +0300 Subject: usb: dwc3: qcom: fix checkpatch warnings no functional changes Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-qcom.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index fcf7f79fb983..c703d552bbcf 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -444,7 +444,9 @@ static int dwc3_qcom_setup_irq(struct platform_device *pdev) { struct dwc3_qcom *qcom = platform_get_drvdata(pdev); const struct dwc3_acpi_pdata *pdata = qcom->acpi_pdata; - int irq, ret; + int irq; + int ret; + irq = dwc3_qcom_get_irq(pdev, "hs_phy_irq", pdata ? pdata->hs_phy_irq_index : -1); if (irq > 0) { @@ -563,7 +565,7 @@ static const struct property_entry dwc3_qcom_acpi_properties[] = { static int dwc3_qcom_acpi_register_core(struct platform_device *pdev) { - struct dwc3_qcom *qcom = platform_get_drvdata(pdev); + struct dwc3_qcom *qcom = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; struct resource *res, *child_res = NULL; int irq; @@ -623,7 +625,7 @@ out: static int dwc3_qcom_of_register_core(struct platform_device *pdev) { - struct dwc3_qcom *qcom = platform_get_drvdata(pdev); + struct dwc3_qcom *qcom = platform_get_drvdata(pdev); struct device_node *np = pdev->dev.of_node, *dwc3_np; struct device *dev = &pdev->dev; int ret; -- cgit v1.2.3 From 9ae0eb455b9136b1857cbbf12e4bf4d31f334f1e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 13 Aug 2020 09:22:34 +0300 Subject: usb: dwc3: debugfs: fix checkpatch warnings no functional changes Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/debugfs.c | 56 +++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 30 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index 2c7b6dd79cdf..608639a0dea7 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -397,13 +397,13 @@ static int dwc3_mode_show(struct seq_file *s, void *unused) switch (DWC3_GCTL_PRTCAP(reg)) { case DWC3_GCTL_PRTCAP_HOST: - seq_printf(s, "host\n"); + seq_puts(s, "host\n"); break; case DWC3_GCTL_PRTCAP_DEVICE: - seq_printf(s, "device\n"); + seq_puts(s, "device\n"); break; case DWC3_GCTL_PRTCAP_OTG: - seq_printf(s, "otg\n"); + seq_puts(s, "otg\n"); break; default: seq_printf(s, "UNKNOWN %08x\n", DWC3_GCTL_PRTCAP(reg)); @@ -464,22 +464,22 @@ static int dwc3_testmode_show(struct seq_file *s, void *unused) switch (reg) { case 0: - seq_printf(s, "no test\n"); + seq_puts(s, "no test\n"); break; case USB_TEST_J: - seq_printf(s, "test_j\n"); + seq_puts(s, "test_j\n"); break; case USB_TEST_K: - seq_printf(s, "test_k\n"); + seq_puts(s, "test_k\n"); break; case USB_TEST_SE0_NAK: - seq_printf(s, "test_se0_nak\n"); + seq_puts(s, "test_se0_nak\n"); break; case USB_TEST_PACKET: - seq_printf(s, "test_packet\n"); + seq_puts(s, "test_packet\n"); break; case USB_TEST_FORCE_ENABLE: - seq_printf(s, "test_force_enable\n"); + seq_puts(s, "test_force_enable\n"); break; default: seq_printf(s, "UNKNOWN %d\n", reg); @@ -760,27 +760,26 @@ static int dwc3_transfer_type_show(struct seq_file *s, void *unused) unsigned long flags; spin_lock_irqsave(&dwc->lock, flags); - if (!(dep->flags & DWC3_EP_ENABLED) || - !dep->endpoint.desc) { - seq_printf(s, "--\n"); + if (!(dep->flags & DWC3_EP_ENABLED) || !dep->endpoint.desc) { + seq_puts(s, "--\n"); goto out; } switch (usb_endpoint_type(dep->endpoint.desc)) { case USB_ENDPOINT_XFER_CONTROL: - seq_printf(s, "control\n"); + seq_puts(s, "control\n"); break; case USB_ENDPOINT_XFER_ISOC: - seq_printf(s, "isochronous\n"); + seq_puts(s, "isochronous\n"); break; case USB_ENDPOINT_XFER_BULK: - seq_printf(s, "bulk\n"); + seq_puts(s, "bulk\n"); break; case USB_ENDPOINT_XFER_INT: - seq_printf(s, "interrupt\n"); + seq_puts(s, "interrupt\n"); break; default: - seq_printf(s, "--\n"); + seq_puts(s, "--\n"); } out: @@ -798,11 +797,11 @@ static int dwc3_trb_ring_show(struct seq_file *s, void *unused) spin_lock_irqsave(&dwc->lock, flags); if (dep->number <= 1) { - seq_printf(s, "--\n"); + seq_puts(s, "--\n"); goto out; } - seq_printf(s, "buffer_addr,size,type,ioc,isp_imi,csp,chn,lst,hwo\n"); + seq_puts(s, "buffer_addr,size,type,ioc,isp_imi,csp,chn,lst,hwo\n"); for (i = 0; i < DWC3_TRB_NUM; i++) { struct dwc3_trb *trb = &dep->trb_pool[i]; @@ -884,7 +883,7 @@ static void dwc3_debugfs_create_endpoint_files(struct dwc3_ep *dep, const struct file_operations *fops = dwc3_ep_file_map[i].fops; const char *name = dwc3_ep_file_map[i].name; - debugfs_create_file(name, S_IRUGO, parent, dep, fops); + debugfs_create_file(name, 0444, parent, dep, fops); } } @@ -929,21 +928,18 @@ void dwc3_debugfs_init(struct dwc3 *dwc) root = debugfs_create_dir(dev_name(dwc->dev), usb_debug_root); dwc->root = root; - debugfs_create_regset32("regdump", S_IRUGO, root, dwc->regset); + debugfs_create_regset32("regdump", 0444, root, dwc->regset); + debugfs_create_file("lsp_dump", 0644, root, dwc, &dwc3_lsp_fops); - debugfs_create_file("lsp_dump", S_IRUGO | S_IWUSR, root, dwc, - &dwc3_lsp_fops); - - if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)) { - debugfs_create_file("mode", S_IRUGO | S_IWUSR, root, dwc, + if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)) + debugfs_create_file("mode", 0644, root, dwc, &dwc3_mode_fops); - } if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) || IS_ENABLED(CONFIG_USB_DWC3_GADGET)) { - debugfs_create_file("testmode", S_IRUGO | S_IWUSR, root, dwc, - &dwc3_testmode_fops); - debugfs_create_file("link_state", S_IRUGO | S_IWUSR, root, dwc, + debugfs_create_file("testmode", 0644, root, dwc, + &dwc3_testmode_fops); + debugfs_create_file("link_state", 0644, root, dwc, &dwc3_link_state_fops); dwc3_debugfs_create_endpoint_dirs(dwc, root); } -- cgit v1.2.3 From 87b923a2e0591df07a48fd5e50dbc6cf3cff3d34 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 13 Aug 2020 08:35:38 +0300 Subject: usb: dwc3: core: fix checkpatch warnings no functional changes Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index eb026c9cca28..60672f149aaf 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -634,7 +634,7 @@ struct dwc3_trb; struct dwc3_event_buffer { void *buf; void *cache; - unsigned length; + unsigned int length; unsigned int lpos; unsigned int count; unsigned int flags; @@ -694,7 +694,7 @@ struct dwc3_ep { struct dwc3 *dwc; u32 saved_state; - unsigned flags; + unsigned int flags; #define DWC3_EP_ENABLED BIT(0) #define DWC3_EP_STALL BIT(1) #define DWC3_EP_WEDGE BIT(2) @@ -894,9 +894,9 @@ struct dwc3_request { struct scatterlist *sg; struct scatterlist *start_sg; - unsigned num_pending_sgs; + unsigned int num_pending_sgs; unsigned int num_queued_sgs; - unsigned remaining; + unsigned int remaining; unsigned int status; #define DWC3_REQUEST_STATUS_QUEUED 0 @@ -909,11 +909,11 @@ struct dwc3_request { struct dwc3_trb *trb; dma_addr_t trb_dma; - unsigned num_trbs; + unsigned int num_trbs; - unsigned needs_extra_trb:1; - unsigned direction:1; - unsigned mapped:1; + unsigned int needs_extra_trb:1; + unsigned int direction:1; + unsigned int mapped:1; }; /* @@ -1011,8 +1011,8 @@ struct dwc3_scratchpad_array { * @has_lpm_erratum: true when core was configured with LPM Erratum. Note that * there's now way for software to detect this in runtime. * @is_utmi_l1_suspend: the core asserts output signal - * 0 - utmi_sleep_n - * 1 - utmi_l1_suspend_n + * 0 - utmi_sleep_n + * 1 - utmi_l1_suspend_n * @is_fpga: true when we are using the FPGA board * @pending_events: true when we have pending IRQs to be handled * @pullups_connected: true when Run/Stop bit is set @@ -1048,13 +1048,13 @@ struct dwc3_scratchpad_array { * instances in park mode. * @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk * @tx_de_emphasis: Tx de-emphasis value - * 0 - -6dB de-emphasis - * 1 - -3.5dB de-emphasis - * 2 - No de-emphasis - * 3 - Reserved + * 0 - -6dB de-emphasis + * 1 - -3.5dB de-emphasis + * 2 - No de-emphasis + * 3 - Reserved * @dis_metastability_quirk: set to disable metastability quirk. * @imod_interval: set the interrupt moderation interval in 250ns - * increments or 0 to disable. + * increments or 0 to disable. */ struct dwc3 { struct work_struct drd_work; @@ -1457,9 +1457,10 @@ void dwc3_gadget_exit(struct dwc3 *dwc); int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode); int dwc3_gadget_get_link_state(struct dwc3 *dwc); int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state); -int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, +int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd, struct dwc3_gadget_ep_cmd_params *params); -int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param); +int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd, + u32 param); #else static inline int dwc3_gadget_init(struct dwc3 *dwc) { return 0; } @@ -1473,7 +1474,7 @@ static inline int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) { return 0; } -static inline int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, +static inline int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd, struct dwc3_gadget_ep_cmd_params *params) { return 0; } static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc, -- cgit v1.2.3 From e319bd62292cb9c40ce5396b416965c2517f70d1 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 13 Aug 2020 08:35:38 +0300 Subject: usb: dwc3: gadget: fix checkpatch warnings no functional changes Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index f9843ad93577..08111b97df04 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -227,7 +227,8 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, * Caller should take care of locking. Issue @cmd with a given @param to @dwc * and wait for its completion. */ -int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param) +int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd, + u32 param) { u32 timeout = 500; int status = 0; @@ -268,7 +269,7 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc); * Caller should handle locking. This function will issue @cmd with given * @params to @dep and wait for its completion. */ -int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, +int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd, struct dwc3_gadget_ep_cmd_params *params) { const struct usb_endpoint_descriptor *desc = dep->endpoint.desc; @@ -564,6 +565,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action) /* Burst size is only needed in SuperSpeed mode */ if (dwc->gadget.speed >= USB_SPEED_SUPER) { u32 burst = dep->endpoint.maxburst; + params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst - 1); } @@ -942,9 +944,10 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep) } 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, unsigned is_last) + dma_addr_t dma, unsigned int length, unsigned int chain, + unsigned int node, unsigned int stream_id, + unsigned int short_not_ok, unsigned int no_interrupt, + unsigned int is_last) { struct dwc3 *dwc = dep->dwc; struct usb_gadget *gadget = &dwc->gadget; @@ -1060,14 +1063,14 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb, */ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_request *req, unsigned int trb_length, - unsigned chain, unsigned node) + unsigned int chain, unsigned int node) { struct dwc3_trb *trb; dma_addr_t dma; - unsigned stream_id = req->request.stream_id; - unsigned short_not_ok = req->request.short_not_ok; - unsigned no_interrupt = req->request.no_interrupt; - unsigned is_last = req->request.is_last; + unsigned int stream_id = req->request.stream_id; + unsigned int short_not_ok = req->request.short_not_ok; + unsigned int no_interrupt = req->request.no_interrupt; + unsigned int is_last = req->request.is_last; if (req->request.num_sgs > 0) dma = sg_dma_address(req->start_sg); @@ -1109,7 +1112,7 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc); unsigned int rem = length % maxp; unsigned int trb_length; - unsigned chain = true; + unsigned int chain = true; trb_length = min_t(unsigned int, length, sg_dma_len(s)); @@ -1653,9 +1656,8 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) return 0; if ((dep->flags & DWC3_EP_PENDING_REQUEST)) { - if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) { + if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) return __dwc3_gadget_start_isoc(dep); - } } } @@ -1793,8 +1795,8 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) if (value) { struct dwc3_trb *trb; - unsigned transfer_in_flight; - unsigned started; + unsigned int transfer_in_flight; + unsigned int started; if (dep->number > 1) trb = dwc3_ep_prev_trb(dep, dep->trb_enqueue); -- cgit v1.2.3 From a1c0169a49fc537629cc63da50d2ce0e3b48281c Mon Sep 17 00:00:00 2001 From: Tao Ren Date: Wed, 27 May 2020 18:11:54 -0700 Subject: usb: gadget: aspeed: fixup vhub port irq handling This is a follow-on patch for commit a23be4ed8f48 ("usb: gadget: aspeed: improve vhub port irq handling"): for_each_set_bit() is replaced with simple for() loop because for() loop runs faster on ASPEED BMC. Signed-off-by: Tao Ren Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/aspeed-vhub/core.c | 10 +++------- drivers/usb/gadget/udc/aspeed-vhub/vhub.h | 3 +++ 2 files changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c index cdf96911e4b1..be7bb64e3594 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/core.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c @@ -135,13 +135,9 @@ static irqreturn_t ast_vhub_irq(int irq, void *data) /* Handle device interrupts */ if (istat & vhub->port_irq_mask) { - unsigned long bitmap = istat; - int offset = VHUB_IRQ_DEV1_BIT; - int size = VHUB_IRQ_DEV1_BIT + vhub->max_ports; - - for_each_set_bit_from(offset, &bitmap, size) { - i = offset - VHUB_IRQ_DEV1_BIT; - ast_vhub_dev_irq(&vhub->ports[i].dev); + for (i = 0; i < vhub->max_ports; i++) { + if (istat & VHUB_DEV_IRQ(i)) + ast_vhub_dev_irq(&vhub->ports[i].dev); } } diff --git a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h index 2e5a1ef14a75..87a5dea12d3c 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h +++ b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h @@ -67,6 +67,9 @@ #define VHUB_IRQ_HUB_EP0_SETUP (1 << 0) #define VHUB_IRQ_ACK_ALL 0x1ff +/* Downstream device IRQ mask. */ +#define VHUB_DEV_IRQ(n) (VHUB_IRQ_DEVICE1 << (n)) + /* SW reset reg */ #define VHUB_SW_RESET_EP_POOL (1 << 9) #define VHUB_SW_RESET_DMA_CONTROLLER (1 << 8) -- cgit v1.2.3 From e7a0ed3fa31be13a4bf5f81426cb4db560e031ee Mon Sep 17 00:00:00 2001 From: MichaÅ‚ MirosÅ‚aw Date: Thu, 28 May 2020 20:30:28 +0200 Subject: usb: gadget: f_acm: don't disable disabled EP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make debugging real problems easier by not trying to disable an EP that was not yet enabled. Reviewed-by: Peter Chen Signed-off-by: MichaÅ‚ MirosÅ‚aw Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_acm.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c index 200596ea9557..46647bfac2ef 100644 --- a/drivers/usb/gadget/function/f_acm.c +++ b/drivers/usb/gadget/function/f_acm.c @@ -425,9 +425,11 @@ static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) /* we know alt == 0, so this is an activation or a reset */ if (intf == acm->ctrl_id) { - dev_vdbg(&cdev->gadget->dev, - "reset acm control interface %d\n", intf); - usb_ep_disable(acm->notify); + if (acm->notify->enabled) { + dev_vdbg(&cdev->gadget->dev, + "reset acm control interface %d\n", intf); + usb_ep_disable(acm->notify); + } if (!acm->notify->desc) if (config_ep_by_speed(cdev->gadget, f, acm->notify)) -- cgit v1.2.3 From e8d5f92b8d30bb4ade76494490c3c065e12411b1 Mon Sep 17 00:00:00 2001 From: Zqiang Date: Fri, 5 Jun 2020 11:05:33 +0800 Subject: usb: gadget: function: printer: fix use-after-free in __lock_acquire Fix this by increase object reference count. BUG: KASAN: use-after-free in __lock_acquire+0x3fd4/0x4180 kernel/locking/lockdep.c:3831 Read of size 8 at addr ffff8880683b0018 by task syz-executor.0/3377 CPU: 1 PID: 3377 Comm: syz-executor.0 Not tainted 5.6.11 #1 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011 Call Trace: __dump_stack lib/dump_stack.c:77 [inline] dump_stack+0xce/0x128 lib/dump_stack.c:118 print_address_description.constprop.4+0x21/0x3c0 mm/kasan/report.c:374 __kasan_report+0x131/0x1b0 mm/kasan/report.c:506 kasan_report+0x12/0x20 mm/kasan/common.c:641 __asan_report_load8_noabort+0x14/0x20 mm/kasan/generic_report.c:135 __lock_acquire+0x3fd4/0x4180 kernel/locking/lockdep.c:3831 lock_acquire+0x127/0x350 kernel/locking/lockdep.c:4488 __raw_spin_lock_irqsave include/linux/spinlock_api_smp.h:110 [inline] _raw_spin_lock_irqsave+0x35/0x50 kernel/locking/spinlock.c:159 printer_ioctl+0x4a/0x110 drivers/usb/gadget/function/f_printer.c:723 vfs_ioctl fs/ioctl.c:47 [inline] ksys_ioctl+0xfb/0x130 fs/ioctl.c:763 __do_sys_ioctl fs/ioctl.c:772 [inline] __se_sys_ioctl fs/ioctl.c:770 [inline] __x64_sys_ioctl+0x73/0xb0 fs/ioctl.c:770 do_syscall_64+0x9e/0x510 arch/x86/entry/common.c:294 entry_SYSCALL_64_after_hwframe+0x49/0xbe RIP: 0033:0x4531a9 Code: ed 60 fc ff c3 66 2e 0f 1f 84 00 00 00 00 00 66 90 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 0f 83 bb 60 fc ff c3 66 2e 0f 1f 84 00 00 00 00 RSP: 002b:00007fd14ad72c78 EFLAGS: 00000246 ORIG_RAX: 0000000000000010 RAX: ffffffffffffffda RBX: 000000000073bfa8 RCX: 00000000004531a9 RDX: fffffffffffffff9 RSI: 000000000000009e RDI: 0000000000000003 RBP: 0000000000000003 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 00000000004bbd61 R13: 00000000004d0a98 R14: 00007fd14ad736d4 R15: 00000000ffffffff Allocated by task 2393: save_stack+0x21/0x90 mm/kasan/common.c:72 set_track mm/kasan/common.c:80 [inline] __kasan_kmalloc.constprop.3+0xa7/0xd0 mm/kasan/common.c:515 kasan_kmalloc+0x9/0x10 mm/kasan/common.c:529 kmem_cache_alloc_trace+0xfa/0x2d0 mm/slub.c:2813 kmalloc include/linux/slab.h:555 [inline] kzalloc include/linux/slab.h:669 [inline] gprinter_alloc+0xa1/0x870 drivers/usb/gadget/function/f_printer.c:1416 usb_get_function+0x58/0xc0 drivers/usb/gadget/functions.c:61 config_usb_cfg_link+0x1ed/0x3e0 drivers/usb/gadget/configfs.c:444 configfs_symlink+0x527/0x11d0 fs/configfs/symlink.c:202 vfs_symlink+0x33d/0x5b0 fs/namei.c:4201 do_symlinkat+0x11b/0x1d0 fs/namei.c:4228 __do_sys_symlinkat fs/namei.c:4242 [inline] __se_sys_symlinkat fs/namei.c:4239 [inline] __x64_sys_symlinkat+0x73/0xb0 fs/namei.c:4239 do_syscall_64+0x9e/0x510 arch/x86/entry/common.c:294 entry_SYSCALL_64_after_hwframe+0x49/0xbe Freed by task 3368: save_stack+0x21/0x90 mm/kasan/common.c:72 set_track mm/kasan/common.c:80 [inline] kasan_set_free_info mm/kasan/common.c:337 [inline] __kasan_slab_free+0x135/0x190 mm/kasan/common.c:476 kasan_slab_free+0xe/0x10 mm/kasan/common.c:485 slab_free_hook mm/slub.c:1444 [inline] slab_free_freelist_hook mm/slub.c:1477 [inline] slab_free mm/slub.c:3034 [inline] kfree+0xf7/0x410 mm/slub.c:3995 gprinter_free+0x49/0xd0 drivers/usb/gadget/function/f_printer.c:1353 usb_put_function+0x38/0x50 drivers/usb/gadget/functions.c:87 config_usb_cfg_unlink+0x2db/0x3b0 drivers/usb/gadget/configfs.c:485 configfs_unlink+0x3b9/0x7f0 fs/configfs/symlink.c:250 vfs_unlink+0x287/0x570 fs/namei.c:4073 do_unlinkat+0x4f9/0x620 fs/namei.c:4137 __do_sys_unlink fs/namei.c:4184 [inline] __se_sys_unlink fs/namei.c:4182 [inline] __x64_sys_unlink+0x42/0x50 fs/namei.c:4182 do_syscall_64+0x9e/0x510 arch/x86/entry/common.c:294 entry_SYSCALL_64_after_hwframe+0x49/0xbe The buggy address belongs to the object at ffff8880683b0000 which belongs to the cache kmalloc-1k of size 1024 The buggy address is located 24 bytes inside of 1024-byte region [ffff8880683b0000, ffff8880683b0400) The buggy address belongs to the page: page:ffffea0001a0ec00 refcount:1 mapcount:0 mapping:ffff88806c00e300 index:0xffff8880683b1800 compound_mapcount: 0 flags: 0x100000000010200(slab|head) raw: 0100000000010200 0000000000000000 0000000600000001 ffff88806c00e300 raw: ffff8880683b1800 000000008010000a 00000001ffffffff 0000000000000000 page dumped because: kasan: bad access detected Reported-by: Kyungtae Kim Signed-off-by: Zqiang Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_printer.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c index 68697f596066..64a4112068fc 100644 --- a/drivers/usb/gadget/function/f_printer.c +++ b/drivers/usb/gadget/function/f_printer.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -64,7 +65,7 @@ struct printer_dev { struct usb_gadget *gadget; s8 interface; struct usb_ep *in_ep, *out_ep; - + struct kref kref; struct list_head rx_reqs; /* List of free RX structs */ struct list_head rx_reqs_active; /* List of Active RX xfers */ struct list_head rx_buffers; /* List of completed xfers */ @@ -218,6 +219,13 @@ static inline struct usb_endpoint_descriptor *ep_desc(struct usb_gadget *gadget, /*-------------------------------------------------------------------------*/ +static void printer_dev_free(struct kref *kref) +{ + struct printer_dev *dev = container_of(kref, struct printer_dev, kref); + + kfree(dev); +} + static struct usb_request * printer_req_alloc(struct usb_ep *ep, unsigned len, gfp_t gfp_flags) { @@ -353,6 +361,7 @@ printer_open(struct inode *inode, struct file *fd) spin_unlock_irqrestore(&dev->lock, flags); + kref_get(&dev->kref); DBG(dev, "printer_open returned %x\n", ret); return ret; } @@ -370,6 +379,7 @@ printer_close(struct inode *inode, struct file *fd) dev->printer_status &= ~PRINTER_SELECTED; spin_unlock_irqrestore(&dev->lock, flags); + kref_put(&dev->kref, printer_dev_free); DBG(dev, "printer_close\n"); return 0; @@ -1386,7 +1396,8 @@ static void gprinter_free(struct usb_function *f) struct f_printer_opts *opts; opts = container_of(f->fi, struct f_printer_opts, func_inst); - kfree(dev); + + kref_put(&dev->kref, printer_dev_free); mutex_lock(&opts->lock); --opts->refcnt; mutex_unlock(&opts->lock); @@ -1455,6 +1466,7 @@ static struct usb_function *gprinter_alloc(struct usb_function_instance *fi) return ERR_PTR(-ENOMEM); } + kref_init(&dev->kref); ++opts->refcnt; dev->minor = opts->minor; dev->pnp_string = opts->pnp_string; -- cgit v1.2.3 From dc336b19e82d0454ea60270cd18fbb4749e162f6 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Sun, 26 Jul 2020 11:17:39 +0800 Subject: usb: dwc3: core: do not queue work if dr_mode is not USB_DR_MODE_OTG Do not try to queue a drd work if dr_mode is not USB_DR_MODE_OTG because the work is not inited, this may be triggered by user try to change mode file of debugfs on a single role port, which will cause below kernel dump: [ 60.115529] ------------[ cut here ]------------ [ 60.120166] WARNING: CPU: 1 PID: 627 at kernel/workqueue.c:1473 __queue_work+0x46c/0x520 [ 60.128254] Modules linked in: [ 60.131313] CPU: 1 PID: 627 Comm: sh Not tainted 5.7.0-rc4-00022-g914a586-dirty #135 [ 60.139054] Hardware name: NXP i.MX8MQ EVK (DT) [ 60.143585] pstate: a0000085 (NzCv daIf -PAN -UAO) [ 60.148376] pc : __queue_work+0x46c/0x520 [ 60.152385] lr : __queue_work+0x314/0x520 [ 60.156393] sp : ffff8000124ebc40 [ 60.159705] x29: ffff8000124ebc40 x28: ffff800011808018 [ 60.165018] x27: ffff800011819ef8 x26: ffff800011d39980 [ 60.170331] x25: ffff800011808018 x24: 0000000000000100 [ 60.175643] x23: 0000000000000013 x22: 0000000000000001 [ 60.180955] x21: ffff0000b7c08e00 x20: ffff0000b6c31080 [ 60.186267] x19: ffff0000bb99bc00 x18: 0000000000000000 [ 60.191579] x17: 0000000000000000 x16: 0000000000000000 [ 60.196891] x15: 0000000000000000 x14: 0000000000000000 [ 60.202202] x13: 0000000000000000 x12: 0000000000000000 [ 60.207515] x11: 0000000000000000 x10: 0000000000000040 [ 60.212827] x9 : ffff800011d55460 x8 : ffff800011d55458 [ 60.218138] x7 : ffff0000b7800028 x6 : 0000000000000000 [ 60.223450] x5 : ffff0000b7800000 x4 : 0000000000000000 [ 60.228762] x3 : ffff0000bb997cc0 x2 : 0000000000000001 [ 60.234074] x1 : 0000000000000000 x0 : ffff0000b6c31088 [ 60.239386] Call trace: [ 60.241834] __queue_work+0x46c/0x520 [ 60.245496] queue_work_on+0x6c/0x90 [ 60.249075] dwc3_set_mode+0x48/0x58 [ 60.252651] dwc3_mode_write+0xf8/0x150 [ 60.256489] full_proxy_write+0x5c/0xa8 [ 60.260327] __vfs_write+0x18/0x40 [ 60.263729] vfs_write+0xdc/0x1c8 [ 60.267045] ksys_write+0x68/0xf0 [ 60.270360] __arm64_sys_write+0x18/0x20 [ 60.274286] el0_svc_common.constprop.0+0x68/0x160 [ 60.279077] do_el0_svc+0x20/0x80 [ 60.282394] el0_sync_handler+0x10c/0x178 [ 60.286403] el0_sync+0x140/0x180 [ 60.289716] ---[ end trace 70b155582e2b7988 ]--- Signed-off-by: Li Jun Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index f3093b4e5307..9b49a6ce0132 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -120,9 +120,6 @@ static void __dwc3_set_mode(struct work_struct *work) unsigned long flags; int ret; - if (dwc->dr_mode != USB_DR_MODE_OTG) - return; - pm_runtime_get_sync(dwc->dev); if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG) @@ -203,6 +200,9 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode) { unsigned long flags; + if (dwc->dr_mode != USB_DR_MODE_OTG) + return; + spin_lock_irqsave(&dwc->lock, flags); dwc->desired_dr_role = mode; spin_unlock_irqrestore(&dwc->lock, flags); -- cgit v1.2.3 From 753a18c2596de2edf01259db24940010dd1c6d9e Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 31 Jul 2020 16:20:08 +0800 Subject: usb: mtu3: Remove unsused inline function is_first_entry It is never used, so can be removed. Signed-off-by: YueHaibing Signed-off-by: Felipe Balbi --- drivers/usb/mtu3/mtu3.h | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h index 71f4f02c05c6..aef0a0bba25a 100644 --- a/drivers/usb/mtu3/mtu3.h +++ b/drivers/usb/mtu3/mtu3.h @@ -370,12 +370,6 @@ static inline struct mtu3 *gadget_to_mtu3(struct usb_gadget *g) return container_of(g, struct mtu3, g); } -static inline int is_first_entry(const struct list_head *list, - const struct list_head *head) -{ - return list_is_last(head, list); -} - static inline struct mtu3_request *to_mtu3_request(struct usb_request *req) { return req ? container_of(req, struct mtu3_request, request) : NULL; -- cgit v1.2.3 From efe2fa0836a7a051a816c90b44f07590f2fd74c5 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 2 Sep 2020 17:57:31 +0800 Subject: usb: cdns3: introduce set_phy_power_on{off} APIs Since we have both USB2 and USB3 PHYs for cdns3 controller, it is better we have unity APIs to handle both USB2 and USB3's power, it could simplify code for error handling and further power management implementation. Reviewed-by: Pawel Laszczak Reviewed-by: Jun Li Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/core.c | 43 ++++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index 5c1586ec7824..e56dbb6a898c 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -371,6 +371,27 @@ pm_put: return ret; } +static int set_phy_power_on(struct cdns3 *cdns) +{ + int ret; + + ret = phy_power_on(cdns->usb2_phy); + if (ret) + return ret; + + ret = phy_power_on(cdns->usb3_phy); + if (ret) + phy_power_off(cdns->usb2_phy); + + return ret; +} + +static void set_phy_power_off(struct cdns3 *cdns) +{ + phy_power_off(cdns->usb3_phy); + phy_power_off(cdns->usb2_phy); +} + /** * cdns3_probe - probe for cdns3 core device * @pdev: Pointer to cdns3 core platform device @@ -463,14 +484,10 @@ static int cdns3_probe(struct platform_device *pdev) if (ret) goto err1; - ret = phy_power_on(cdns->usb2_phy); + ret = set_phy_power_on(cdns); if (ret) goto err2; - ret = phy_power_on(cdns->usb3_phy); - if (ret) - goto err3; - sw_desc.set = cdns3_role_set; sw_desc.get = cdns3_role_get; sw_desc.allow_userspace_control = true; @@ -482,16 +499,16 @@ static int cdns3_probe(struct platform_device *pdev) if (IS_ERR(cdns->role_sw)) { ret = PTR_ERR(cdns->role_sw); dev_warn(dev, "Unable to register Role Switch\n"); - goto err4; + goto err3; } ret = cdns3_drd_init(cdns); if (ret) - goto err5; + goto err4; ret = cdns3_core_init_role(cdns); if (ret) - goto err5; + goto err4; device_set_wakeup_capable(dev, true); pm_runtime_set_active(dev); @@ -508,14 +525,11 @@ static int cdns3_probe(struct platform_device *pdev) dev_dbg(dev, "Cadence USB3 core: probe succeed\n"); return 0; -err5: +err4: cdns3_drd_exit(cdns); usb_role_switch_unregister(cdns->role_sw); -err4: - phy_power_off(cdns->usb3_phy); - err3: - phy_power_off(cdns->usb2_phy); + set_phy_power_off(cdns); err2: phy_exit(cdns->usb3_phy); err1: @@ -539,8 +553,7 @@ static int cdns3_remove(struct platform_device *pdev) pm_runtime_put_noidle(&pdev->dev); cdns3_exit_roles(cdns); usb_role_switch_unregister(cdns->role_sw); - phy_power_off(cdns->usb2_phy); - phy_power_off(cdns->usb3_phy); + set_phy_power_off(cdns); phy_exit(cdns->usb2_phy); phy_exit(cdns->usb3_phy); return 0; -- cgit v1.2.3 From b1234e3b3b265588bab1e7a28508621045b87efa Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 2 Sep 2020 17:57:32 +0800 Subject: usb: cdns3: add runtime PM support Introduce runtime PM and wakeup interrupt handler for cdns3, the runtime PM is default off since other cdns3 may not implement glue layer support for runtime PM. One typical wakeup event use case is xHCI runtime suspend will clear USBCMD.RS bit, after that the xHCI will not trigger any interrupts, so its parent (cdns core device) needs to resume xHCI device when any (wakeup) events occurs at host port. When the controller is in low power mode, the lpm flag will be set. The interrupt triggered later than lpm flag is set considers as wakeup interrupt and handled at cdns_wakeup_irq. Once the wakeup occurs, it first disables interrupt to avoid later interrupt occurrence since the controller is in low power mode at that time, and access registers may be invalid at that time. At wakeup handler, it will call pm_request_resume to wakeup xHCI device, and at runtime resume handler, it will enable interrupt again. The API platform_suspend is introduced for glue layer to implement platform specific PM sequence. Reviewed-by: Pawel Laszczak Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/core.c | 153 ++++++++++++++++++++++++++++++++++++++++----- drivers/usb/cdns3/core.h | 16 +++++ drivers/usb/cdns3/drd.c | 3 + drivers/usb/cdns3/gadget.c | 4 ++ drivers/usb/cdns3/host.c | 7 +++ 5 files changed, 166 insertions(+), 17 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index e56dbb6a898c..4cf815882c5f 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -392,6 +392,29 @@ static void set_phy_power_off(struct cdns3 *cdns) phy_power_off(cdns->usb2_phy); } +/** + * cdns3_wakeup_irq - interrupt handler for wakeup events + * @irq: irq number for cdns3 core device + * @data: structure of cdns3 + * + * Returns IRQ_HANDLED or IRQ_NONE + */ +static irqreturn_t cdns3_wakeup_irq(int irq, void *data) +{ + struct cdns3 *cdns = data; + + if (cdns->in_lpm) { + disable_irq_nosync(irq); + cdns->wakeup_pending = true; + if ((cdns->role == USB_ROLE_HOST) && cdns->host_dev) + pm_request_resume(&cdns->host_dev->dev); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + /** * cdns3_probe - probe for cdns3 core device * @pdev: Pointer to cdns3 core platform device @@ -418,6 +441,7 @@ static int cdns3_probe(struct platform_device *pdev) return -ENOMEM; cdns->dev = dev; + cdns->pdata = dev_get_platdata(dev); platform_set_drvdata(pdev, cdns); @@ -466,6 +490,17 @@ static int cdns3_probe(struct platform_device *pdev) cdns->otg_res = *res; + cdns->wakeup_irq = platform_get_irq_byname_optional(pdev, "wakeup"); + if (cdns->wakeup_irq == -EPROBE_DEFER) + return cdns->wakeup_irq; + else if (cdns->wakeup_irq == 0) + return -EINVAL; + + if (cdns->wakeup_irq < 0) { + dev_dbg(dev, "couldn't get wakeup irq\n"); + cdns->wakeup_irq = 0x0; + } + mutex_init(&cdns->mutex); cdns->usb2_phy = devm_phy_optional_get(dev, "cdns3,usb2-phy"); @@ -502,6 +537,18 @@ static int cdns3_probe(struct platform_device *pdev) goto err3; } + if (cdns->wakeup_irq) { + ret = devm_request_irq(cdns->dev, cdns->wakeup_irq, + cdns3_wakeup_irq, + IRQF_SHARED, + dev_name(cdns->dev), cdns); + + if (ret) { + dev_err(cdns->dev, "couldn't register wakeup irq handler\n"); + goto err3; + } + } + ret = cdns3_drd_init(cdns); if (ret) goto err4; @@ -510,9 +557,11 @@ static int cdns3_probe(struct platform_device *pdev) if (ret) goto err4; + spin_lock_init(&cdns->lock); device_set_wakeup_capable(dev, true); pm_runtime_set_active(dev); pm_runtime_enable(dev); + pm_runtime_forbid(dev); /* * The controller needs less time between bus and controller suspend, @@ -559,52 +608,122 @@ static int cdns3_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PM -static int cdns3_suspend(struct device *dev) +static int cdns3_set_platform_suspend(struct device *dev, + bool suspend, bool wakeup) +{ + struct cdns3 *cdns = dev_get_drvdata(dev); + int ret = 0; + + if (cdns->pdata && cdns->pdata->platform_suspend) + ret = cdns->pdata->platform_suspend(dev, suspend, wakeup); + + return ret; +} + +static int cdns3_controller_suspend(struct device *dev, pm_message_t msg) { struct cdns3 *cdns = dev_get_drvdata(dev); + bool wakeup; unsigned long flags; - if (cdns->role == USB_ROLE_HOST) + if (cdns->in_lpm) return 0; - if (pm_runtime_status_suspended(dev)) - pm_runtime_resume(dev); + if (PMSG_IS_AUTO(msg)) + wakeup = true; + else + wakeup = device_may_wakeup(dev); - if (cdns->roles[cdns->role]->suspend) { - spin_lock_irqsave(&cdns->gadget_dev->lock, flags); - cdns->roles[cdns->role]->suspend(cdns, false); - spin_unlock_irqrestore(&cdns->gadget_dev->lock, flags); - } + cdns3_set_platform_suspend(cdns->dev, true, wakeup); + set_phy_power_off(cdns); + spin_lock_irqsave(&cdns->lock, flags); + cdns->in_lpm = true; + spin_unlock_irqrestore(&cdns->lock, flags); + dev_dbg(cdns->dev, "%s ends\n", __func__); return 0; } -static int cdns3_resume(struct device *dev) +static int cdns3_controller_resume(struct device *dev, pm_message_t msg) { struct cdns3 *cdns = dev_get_drvdata(dev); + int ret; unsigned long flags; - if (cdns->role == USB_ROLE_HOST) + if (!cdns->in_lpm) return 0; - if (cdns->roles[cdns->role]->resume) { - spin_lock_irqsave(&cdns->gadget_dev->lock, flags); + ret = set_phy_power_on(cdns); + if (ret) + return ret; + + cdns3_set_platform_suspend(cdns->dev, false, false); + + spin_lock_irqsave(&cdns->lock, flags); + if (cdns->roles[cdns->role]->resume && !PMSG_IS_AUTO(msg)) cdns->roles[cdns->role]->resume(cdns, false); - spin_unlock_irqrestore(&cdns->gadget_dev->lock, flags); + + cdns->in_lpm = false; + spin_unlock_irqrestore(&cdns->lock, flags); + if (cdns->wakeup_pending) { + cdns->wakeup_pending = false; + enable_irq(cdns->wakeup_irq); + } + dev_dbg(cdns->dev, "%s ends\n", __func__); + + return ret; +} + +static int cdns3_runtime_suspend(struct device *dev) +{ + return cdns3_controller_suspend(dev, PMSG_AUTO_SUSPEND); +} + +static int cdns3_runtime_resume(struct device *dev) +{ + return cdns3_controller_resume(dev, PMSG_AUTO_RESUME); +} +#ifdef CONFIG_PM_SLEEP + +static int cdns3_suspend(struct device *dev) +{ + struct cdns3 *cdns = dev_get_drvdata(dev); + unsigned long flags; + + if (pm_runtime_status_suspended(dev)) + pm_runtime_resume(dev); + + if (cdns->roles[cdns->role]->suspend) { + spin_lock_irqsave(&cdns->lock, flags); + cdns->roles[cdns->role]->suspend(cdns, false); + spin_unlock_irqrestore(&cdns->lock, flags); } + return cdns3_controller_suspend(dev, PMSG_SUSPEND); +} + +static int cdns3_resume(struct device *dev) +{ + int ret; + + ret = cdns3_controller_resume(dev, PMSG_RESUME); + if (ret) + return ret; + pm_runtime_disable(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); - return 0; + return ret; } -#endif +#endif /* CONFIG_PM_SLEEP */ +#endif /* CONFIG_PM */ static const struct dev_pm_ops cdns3_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(cdns3_suspend, cdns3_resume) + SET_RUNTIME_PM_OPS(cdns3_runtime_suspend, cdns3_runtime_resume, NULL) }; #ifdef CONFIG_OF diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h index 1ad1f1fe61e9..1b1707796db2 100644 --- a/drivers/usb/cdns3/core.h +++ b/drivers/usb/cdns3/core.h @@ -38,6 +38,12 @@ struct cdns3_role_driver { }; #define CDNS3_XHCI_RESOURCES_NUM 2 + +struct cdns3_platform_data { + int (*platform_suspend)(struct device *dev, + bool suspend, bool wakeup); +}; + /** * struct cdns3 - Representation of Cadence USB3 DRD controller. * @dev: pointer to Cadence device struct @@ -50,6 +56,7 @@ struct cdns3_role_driver { * @otg_regs: pointer to base of otg registers * @otg_irq: irq number for otg controller * @dev_irq: irq number for device controller + * @wakeup_irq: irq number for wakeup event, it is optional * @roles: array of supported roles for this controller * @role: current role * @host_dev: the child host device pointer for cdns3 core @@ -62,6 +69,10 @@ struct cdns3_role_driver { * This field based on firmware setting, kernel configuration * and hardware configuration. * @role_sw: pointer to role switch object. + * @in_lpm: indicate the controller is in low power mode + * @wakeup_pending: wakeup interrupt pending + * @pdata: platform data from glue layer + * @lock: spinlock structure */ struct cdns3 { struct device *dev; @@ -79,6 +90,7 @@ struct cdns3 { int otg_irq; int dev_irq; + int wakeup_irq; struct cdns3_role_driver *roles[USB_ROLE_DEVICE + 1]; enum usb_role role; struct platform_device *host_dev; @@ -89,6 +101,10 @@ struct cdns3 { struct mutex mutex; enum usb_dr_mode dr_mode; struct usb_role_switch *role_sw; + bool in_lpm; + bool wakeup_pending; + struct cdns3_platform_data *pdata; + spinlock_t lock; }; int cdns3_hw_role_switch(struct cdns3 *cdns); diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c index 6234bcd6158a..5f2685cadf5e 100644 --- a/drivers/usb/cdns3/drd.c +++ b/drivers/usb/cdns3/drd.c @@ -293,6 +293,9 @@ static irqreturn_t cdns3_drd_irq(int irq, void *data) if (cdns->dr_mode != USB_DR_MODE_OTG) return IRQ_NONE; + if (cdns->in_lpm) + return ret; + reg = readl(&cdns->otg_regs->ivect); if (!reg) diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index 02a69e20014b..9e6ff7d00784 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -1769,9 +1769,13 @@ static void cdns3_check_usb_interrupt_proceed(struct cdns3_device *priv_dev, static irqreturn_t cdns3_device_irq_handler(int irq, void *data) { struct cdns3_device *priv_dev = data; + struct cdns3 *cdns = dev_get_drvdata(priv_dev->dev); irqreturn_t ret = IRQ_NONE; u32 reg; + if (cdns->in_lpm) + return ret; + /* check USB device interrupt */ reg = readl(&priv_dev->regs->usb_ists); if (reg) { diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c index 36c63d9ecd37..b3e2cb69762c 100644 --- a/drivers/usb/cdns3/host.c +++ b/drivers/usb/cdns3/host.c @@ -13,11 +13,13 @@ #include "core.h" #include "drd.h" #include "host-export.h" +#include static int __cdns3_host_init(struct cdns3 *cdns) { struct platform_device *xhci; int ret; + struct usb_hcd *hcd; cdns3_drd_host_on(cdns); @@ -43,6 +45,11 @@ static int __cdns3_host_init(struct cdns3 *cdns) goto err1; } + /* Glue needs to access xHCI region register for Power management */ + hcd = platform_get_drvdata(xhci); + if (hcd) + cdns->xhci_regs = hcd->regs; + return 0; err1: platform_device_put(xhci); -- cgit v1.2.3 From ff6d6e6c6778da68a95074d6c2dda6db18c56888 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 2 Sep 2020 17:57:33 +0800 Subject: usb: cdns3: imx: add glue layer runtime pm implementation Add imx glue layer runtime pm implementation, and the runtime pm is default off. Reviewed-by: Pawel Laszczak Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/cdns3-imx.c | 191 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 186 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/cdns3/cdns3-imx.c b/drivers/usb/cdns3/cdns3-imx.c index aba988e71958..54a2d70a9c73 100644 --- a/drivers/usb/cdns3/cdns3-imx.c +++ b/drivers/usb/cdns3/cdns3-imx.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include "core.h" #define USB3_CORE_CTRL1 0x00 #define USB3_CORE_CTRL2 0x04 @@ -32,7 +34,7 @@ /* Register bits definition */ /* USB3_CORE_CTRL1 */ -#define SW_RESET_MASK (0x3f << 26) +#define SW_RESET_MASK GENMASK(31, 26) #define PWR_SW_RESET BIT(31) #define APB_SW_RESET BIT(30) #define AXI_SW_RESET BIT(29) @@ -53,8 +55,8 @@ #define LPM_CLK_REQ BIT(28) #define DEVU3_WAEKUP_EN BIT(14) #define OTG_WAKEUP_EN BIT(12) -#define DEV_INT_EN (3 << 8) /* DEV INT b9:8 */ -#define HOST_INT1_EN (1 << 0) /* HOST INT b7:0 */ +#define DEV_INT_EN (3 << 8) /* DEV INT b9:8 */ +#define HOST_INT1_EN (1 << 0) /* HOST INT b7:0 */ /* USB3_CORE_STATUS */ #define MDCTRL_CLK_STATUS BIT(15) @@ -66,11 +68,30 @@ #define CLK_VALID_COMPARE_BITS (0xf << 28) #define PHY_REFCLK_REQ (1 << 0) +/* OTG registers definition */ +#define OTGSTS 0x4 +/* OTGSTS */ +#define OTG_NRDY BIT(11) + +/* xHCI registers definition */ +#define XECP_PM_PMCSR 0x8018 +#define XECP_AUX_CTRL_REG1 0x8120 + +/* Register bits definition */ +/* XECP_AUX_CTRL_REG1 */ +#define CFG_RXDET_P3_EN BIT(15) + +/* XECP_PM_PMCSR */ +#define PS_MASK GENMASK(1, 0) +#define PS_D0 0 +#define PS_D1 1 + struct cdns_imx { struct device *dev; void __iomem *noncore; struct clk_bulk_data *clks; int num_clks; + struct platform_device *cdns3_pdev; }; static inline u32 cdns_imx_readl(struct cdns_imx *data, u32 offset) @@ -126,6 +147,20 @@ static int cdns_imx_noncore_init(struct cdns_imx *data) return ret; } +static int cdns_imx_platform_suspend(struct device *dev, + bool suspend, bool wakeup); +static struct cdns3_platform_data cdns_imx_pdata = { + .platform_suspend = cdns_imx_platform_suspend, +}; + +static const struct of_dev_auxdata cdns_imx_auxdata[] = { + { + .compatible = "cdns,usb3", + .platform_data = &cdns_imx_pdata, + }, + {}, +}; + static int cdns_imx_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -162,14 +197,18 @@ static int cdns_imx_probe(struct platform_device *pdev) if (ret) goto err; - ret = of_platform_populate(node, NULL, NULL, dev); + ret = of_platform_populate(node, NULL, cdns_imx_auxdata, dev); if (ret) { dev_err(dev, "failed to create children: %d\n", ret); goto err; } - return ret; + device_set_wakeup_capable(dev, true); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_forbid(dev); + return ret; err: clk_bulk_disable_unprepare(data->num_clks, data->clks); return ret; @@ -194,6 +233,147 @@ static int cdns_imx_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static void cdns3_set_wakeup(struct cdns_imx *data, bool enable) +{ + u32 value; + + value = cdns_imx_readl(data, USB3_INT_REG); + if (enable) + value |= OTG_WAKEUP_EN | DEVU3_WAEKUP_EN; + else + value &= ~(OTG_WAKEUP_EN | DEVU3_WAEKUP_EN); + + cdns_imx_writel(data, USB3_INT_REG, value); +} + +static int cdns_imx_platform_suspend(struct device *dev, + bool suspend, bool wakeup) +{ + struct cdns3 *cdns = dev_get_drvdata(dev); + struct device *parent = dev->parent; + struct cdns_imx *data = dev_get_drvdata(parent); + void __iomem *otg_regs = (void __iomem *)(cdns->otg_regs); + void __iomem *xhci_regs = cdns->xhci_regs; + u32 value; + int ret = 0; + + if (cdns->role != USB_ROLE_HOST) + return 0; + + if (suspend) { + /* SW request low power when all usb ports allow to it ??? */ + value = readl(xhci_regs + XECP_PM_PMCSR); + value &= ~PS_MASK; + value |= PS_D1; + writel(value, xhci_regs + XECP_PM_PMCSR); + + /* mdctrl_clk_sel */ + value = cdns_imx_readl(data, USB3_CORE_CTRL1); + value |= MDCTRL_CLK_SEL; + cdns_imx_writel(data, USB3_CORE_CTRL1, value); + + /* wait for mdctrl_clk_status */ + value = cdns_imx_readl(data, USB3_CORE_STATUS); + ret = readl_poll_timeout(data->noncore + USB3_CORE_STATUS, value, + (value & MDCTRL_CLK_STATUS) == MDCTRL_CLK_STATUS, + 10, 100000); + if (ret) + dev_warn(parent, "wait mdctrl_clk_status timeout\n"); + + /* wait lpm_clk_req to be 0 */ + value = cdns_imx_readl(data, USB3_INT_REG); + ret = readl_poll_timeout(data->noncore + USB3_INT_REG, value, + (value & LPM_CLK_REQ) != LPM_CLK_REQ, + 10, 100000); + if (ret) + dev_warn(parent, "wait lpm_clk_req timeout\n"); + + /* wait phy_refclk_req to be 0 */ + value = cdns_imx_readl(data, USB3_SSPHY_STATUS); + ret = readl_poll_timeout(data->noncore + USB3_SSPHY_STATUS, value, + (value & PHY_REFCLK_REQ) != PHY_REFCLK_REQ, + 10, 100000); + if (ret) + dev_warn(parent, "wait phy_refclk_req timeout\n"); + + cdns3_set_wakeup(data, wakeup); + } else { + cdns3_set_wakeup(data, false); + + /* SW request D0 */ + value = readl(xhci_regs + XECP_PM_PMCSR); + value &= ~PS_MASK; + value |= PS_D0; + writel(value, xhci_regs + XECP_PM_PMCSR); + + /* clr CFG_RXDET_P3_EN */ + value = readl(xhci_regs + XECP_AUX_CTRL_REG1); + value &= ~CFG_RXDET_P3_EN; + writel(value, xhci_regs + XECP_AUX_CTRL_REG1); + + /* clear mdctrl_clk_sel */ + value = cdns_imx_readl(data, USB3_CORE_CTRL1); + value &= ~MDCTRL_CLK_SEL; + cdns_imx_writel(data, USB3_CORE_CTRL1, value); + + /* wait CLK_125_REQ to be 1 */ + value = cdns_imx_readl(data, USB3_INT_REG); + ret = readl_poll_timeout(data->noncore + USB3_INT_REG, value, + (value & CLK_125_REQ) == CLK_125_REQ, + 10, 100000); + if (ret) + dev_warn(parent, "wait CLK_125_REQ timeout\n"); + + /* wait for mdctrl_clk_status is cleared */ + value = cdns_imx_readl(data, USB3_CORE_STATUS); + ret = readl_poll_timeout(data->noncore + USB3_CORE_STATUS, value, + (value & MDCTRL_CLK_STATUS) != MDCTRL_CLK_STATUS, + 10, 100000); + if (ret) + dev_warn(parent, "wait mdctrl_clk_status cleared timeout\n"); + + /* Wait until OTG_NRDY is 0 */ + value = readl(otg_regs + OTGSTS); + ret = readl_poll_timeout(otg_regs + OTGSTS, value, + (value & OTG_NRDY) != OTG_NRDY, + 10, 100000); + if (ret) + dev_warn(parent, "wait OTG ready timeout\n"); + } + + return ret; + +} + +static int cdns_imx_resume(struct device *dev) +{ + struct cdns_imx *data = dev_get_drvdata(dev); + + return clk_bulk_prepare_enable(data->num_clks, data->clks); +} + +static int cdns_imx_suspend(struct device *dev) +{ + struct cdns_imx *data = dev_get_drvdata(dev); + + clk_bulk_disable_unprepare(data->num_clks, data->clks); + + return 0; +} +#else +static int cdns_imx_platform_suspend(struct device *dev, + bool suspend, bool wakeup) +{ + return 0; +} + +#endif /* CONFIG_PM */ + +static const struct dev_pm_ops cdns_imx_pm_ops = { + SET_RUNTIME_PM_OPS(cdns_imx_suspend, cdns_imx_resume, NULL) +}; + static const struct of_device_id cdns_imx_of_match[] = { { .compatible = "fsl,imx8qm-usb3", }, {}, @@ -206,6 +386,7 @@ static struct platform_driver cdns_imx_driver = { .driver = { .name = "cdns3-imx", .of_match_table = cdns_imx_of_match, + .pm = &cdns_imx_pm_ops, }, }; module_platform_driver(cdns_imx_driver); -- cgit v1.2.3 From e20849a8c883abfe4c075f72bf5538d63b7562c4 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 28 Aug 2020 17:30:55 +0200 Subject: usb: gadget: pch_udc: Convert to use GPIO descriptors This switches the PCH UDC driver to use GPIO descriptors. The way this is supposed to be used is confusing. The code contains the following: /* GPIO port for VBUS detecting */ static int vbus_gpio_port = -1; /* GPIO port number (-1:Not used) */ So a hardcoded GPIO number in the code. Further the probe() path very clearly will exit if the GPIO is not found, so this driver can only be configured by editing the code, hard-coding a GPIO number into this variable. This is simply not how we do things. My guess is that this is used in products by patching a GPIO number into this variable and shipping a kernel that is compile-time tailored for the target system. I switched this mechanism to using a GPIO descriptor associated with the parent PCI device. This can be added by using the 16bit subsystem ID or similar to identify which exact machine we are running on and what GPIO is present on that machine, and then add a GPIO descriptor using gpiod_add_lookup_table() from . Since I don't have any target systems I cannot add this but I'm happy to help. I put in a FIXME so the people actually using this driver knows what to do. Cc: Felipe Balbi Tested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Signed-off-by: Linus Walleij Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/pch_udc.c | 55 ++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 33 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c index 8afc31d94b0e..a3c1fc924268 100644 --- a/drivers/usb/gadget/udc/pch_udc.c +++ b/drivers/usb/gadget/udc/pch_udc.c @@ -12,12 +12,9 @@ #include #include #include -#include +#include #include -/* GPIO port for VBUS detecting */ -static int vbus_gpio_port = -1; /* GPIO port number (-1:Not used) */ - #define PCH_VBUS_PERIOD 3000 /* VBUS polling period (msec) */ #define PCH_VBUS_INTERVAL 10 /* VBUS polling interval (msec) */ @@ -301,13 +298,13 @@ struct pch_udc_ep { /** * struct pch_vbus_gpio_data - Structure holding GPIO informaton * for detecting VBUS - * @port: gpio port number + * @port: gpio descriptor for the VBUS GPIO * @intr: gpio interrupt number * @irq_work_fall: Structure for WorkQueue * @irq_work_rise: Structure for WorkQueue */ struct pch_vbus_gpio_data { - int port; + struct gpio_desc *port; int intr; struct work_struct irq_work_fall; struct work_struct irq_work_rise; @@ -1254,7 +1251,7 @@ static int pch_vbus_gpio_get_value(struct pch_udc_dev *dev) int vbus = 0; if (dev->vbus_gpio.port) - vbus = gpio_get_value(dev->vbus_gpio.port) ? 1 : 0; + vbus = gpiod_get_value(dev->vbus_gpio.port) ? 1 : 0; else vbus = -1; @@ -1356,42 +1353,30 @@ static irqreturn_t pch_vbus_gpio_irq(int irq, void *data) /** * pch_vbus_gpio_init() - This API initializes GPIO port detecting VBUS. * @dev: Reference to the driver structure - * @vbus_gpio_port: Number of GPIO port to detect gpio * * Return codes: * 0: Success * -EINVAL: GPIO port is invalid or can't be initialized. */ -static int pch_vbus_gpio_init(struct pch_udc_dev *dev, int vbus_gpio_port) +static int pch_vbus_gpio_init(struct pch_udc_dev *dev) { int err; int irq_num = 0; + struct gpio_desc *gpiod; - dev->vbus_gpio.port = 0; + dev->vbus_gpio.port = NULL; dev->vbus_gpio.intr = 0; - if (vbus_gpio_port <= -1) - return -EINVAL; - - err = gpio_is_valid(vbus_gpio_port); - if (!err) { - pr_err("%s: gpio port %d is invalid\n", - __func__, vbus_gpio_port); - return -EINVAL; - } - - err = gpio_request(vbus_gpio_port, "pch_vbus"); - if (err) { - pr_err("%s: can't request gpio port %d, err: %d\n", - __func__, vbus_gpio_port, err); - return -EINVAL; - } + /* Retrieve the GPIO line from the USB gadget device */ + gpiod = devm_gpiod_get(dev->gadget.dev.parent, NULL, GPIOD_IN); + if (IS_ERR(gpiod)) + return PTR_ERR(gpiod); + gpiod_set_consumer_name(gpiod, "pch_vbus"); - dev->vbus_gpio.port = vbus_gpio_port; - gpio_direction_input(vbus_gpio_port); + dev->vbus_gpio.port = gpiod; INIT_WORK(&dev->vbus_gpio.irq_work_fall, pch_vbus_gpio_work_fall); - irq_num = gpio_to_irq(vbus_gpio_port); + irq_num = gpiod_to_irq(gpiod); if (irq_num > 0) { irq_set_irq_type(irq_num, IRQ_TYPE_EDGE_BOTH); err = request_irq(irq_num, pch_vbus_gpio_irq, 0, @@ -1417,9 +1402,6 @@ static void pch_vbus_gpio_free(struct pch_udc_dev *dev) { if (dev->vbus_gpio.intr) free_irq(dev->vbus_gpio.intr, dev); - - if (dev->vbus_gpio.port) - gpio_free(dev->vbus_gpio.port); } /** @@ -2894,7 +2876,7 @@ static int pch_udc_pcd_init(struct pch_udc_dev *dev) { pch_udc_init(dev); pch_udc_pcd_reinit(dev); - pch_vbus_gpio_init(dev, vbus_gpio_port); + pch_vbus_gpio_init(dev); return 0; } @@ -3096,6 +3078,13 @@ static int pch_udc_probe(struct pci_dev *pdev, dev->base_addr = pcim_iomap_table(pdev)[bar]; + /* + * FIXME: add a GPIO descriptor table to pdev.dev using + * gpiod_add_descriptor_table() from based on + * the PCI subsystem ID. The system-dependent GPIO is necessary for + * VBUS operation. + */ + /* initialize the hardware */ if (pch_udc_pcd_init(dev)) return -ENODEV; -- cgit v1.2.3 From 65f3d449f4386c4aced2fb7252905240b4f45cd3 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 17 Sep 2020 08:59:47 +0200 Subject: usb: dwc-meson-g12a: Add support for USB on AXG SoCs The Amlogic AXG is close to the GXL Glue but with a single OTG PHY. It needs the same init sequence as GXL & GXM, but it seems it doesn't need the host disconnect bit. Reviewed-by: Martin Blumenstingl Signed-off-by: Neil Armstrong Reviewed-by: Kevin Hilman Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-meson-g12a.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c index a966e4d16f73..417e05381b5d 100644 --- a/drivers/usb/dwc3/dwc3-meson-g12a.c +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -127,6 +127,7 @@ static const char * const meson_g12a_phy_names[] = { /* * Amlogic A1 has a single physical PHY, in slot 1, but still has the * two U2 PHY controls register blocks like G12A. + * AXG has the similar scheme, thus needs the same tweak. * Handling the first PHY on slot 1 would need a large amount of code * changes, and the current management is generic enough to handle it * correctly when only the "usb2-phy1" phy is specified on-par with the @@ -215,6 +216,19 @@ static struct dwc3_meson_g12a_drvdata gxm_drvdata = { .usb_post_init = dwc3_meson_gxl_usb_post_init, }; +static struct dwc3_meson_g12a_drvdata axg_drvdata = { + .otg_switch_supported = true, + .clks = meson_gxl_clocks, + .num_clks = ARRAY_SIZE(meson_gxl_clocks), + .phy_names = meson_a1_phy_names, + .num_phys = ARRAY_SIZE(meson_a1_phy_names), + .setup_regmaps = dwc3_meson_gxl_setup_regmaps, + .usb2_init_phy = dwc3_meson_gxl_usb2_init_phy, + .set_phy_mode = dwc3_meson_gxl_set_phy_mode, + .usb_init = dwc3_meson_g12a_usb_init, + .usb_post_init = dwc3_meson_gxl_usb_post_init, +}; + static struct dwc3_meson_g12a_drvdata g12a_drvdata = { .otg_switch_supported = true, .clks = meson_g12a_clocks, @@ -923,6 +937,10 @@ static const struct of_device_id dwc3_meson_g12a_match[] = { .compatible = "amlogic,meson-gxm-usb-ctrl", .data = &gxm_drvdata, }, + { + .compatible = "amlogic,meson-axg-usb-ctrl", + .data = &axg_drvdata, + }, { .compatible = "amlogic,meson-g12a-usb-ctrl", .data = &g12a_drvdata, -- cgit v1.2.3 From 2eae2dfd581420f94d6041dddc7a88d7ae9ce2ff Mon Sep 17 00:00:00 2001 From: Pawel Laszczak Date: Tue, 15 Sep 2020 14:45:43 +0300 Subject: usb: cdns3: Enable workaround for USB2.0 PHY Rx compliance test PHY lockup USB2.0 PHY hangs in Rx Compliance test when the incoming packet amplitude is varied below and above the Squelch Level of Receiver during the active packet multiple times. Version 1 of the controller allows PHY to be reset when RX fail condition is detected to work around the above issue. This feature is disabled by default and needs to be enabled using a bit from the newly added PHYRST_CFG register. This patch enables the workaround. There is no way to know controller version before device controller is started and the workaround needs to be applied for both host and device modes, so we rely on a DT property do decide when to apply the workaround. Signed-off-by: Pawel Laszczak Signed-off-by: Roger Quadros Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/core.c | 2 ++ drivers/usb/cdns3/core.h | 1 + drivers/usb/cdns3/drd.c | 12 ++++++++++++ drivers/usb/cdns3/drd.h | 5 ++++- 4 files changed, 19 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index 4cf815882c5f..4c1445cf2ad0 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -488,6 +488,8 @@ static int cdns3_probe(struct platform_device *pdev) return -ENXIO; } + cdns->phyrst_a_enable = device_property_read_bool(dev, "cdns,phyrst-a-enable"); + cdns->otg_res = *res; cdns->wakeup_irq = platform_get_irq_byname_optional(pdev, "wakeup"); diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h index 1b1707796db2..8a40d53d5ede 100644 --- a/drivers/usb/cdns3/core.h +++ b/drivers/usb/cdns3/core.h @@ -87,6 +87,7 @@ struct cdns3 { #define CDNS3_CONTROLLER_V0 0 #define CDNS3_CONTROLLER_V1 1 u32 version; + bool phyrst_a_enable; int otg_irq; int dev_irq; diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c index 5f2685cadf5e..fcd295f566b2 100644 --- a/drivers/usb/cdns3/drd.c +++ b/drivers/usb/cdns3/drd.c @@ -42,6 +42,18 @@ int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode) reg = readl(&cdns->otg_v1_regs->override); reg |= OVERRIDE_IDPULLUP; writel(reg, &cdns->otg_v1_regs->override); + + /* + * Enable work around feature built into the + * controller to address issue with RX Sensitivity + * est (EL_17) for USB2 PHY. The issue only occures + * for 0x0002450D controller version. + */ + if (cdns->phyrst_a_enable) { + reg = readl(&cdns->otg_v1_regs->phyrst_cfg); + reg |= PHYRST_CFG_PHYRST_A_ENABLE; + writel(reg, &cdns->otg_v1_regs->phyrst_cfg); + } } else { reg = readl(&cdns->otg_v0_regs->ctrl1); reg |= OVERRIDE_IDPULLUP_V0; diff --git a/drivers/usb/cdns3/drd.h b/drivers/usb/cdns3/drd.h index 7e7cf7fa2dd3..f1ccae285a16 100644 --- a/drivers/usb/cdns3/drd.h +++ b/drivers/usb/cdns3/drd.h @@ -31,7 +31,7 @@ struct cdns3_otg_regs { __le32 simulate; __le32 override; __le32 susp_ctrl; - __le32 reserved4; + __le32 phyrst_cfg; __le32 anasts; __le32 adp_ramp_time; __le32 ctrl1; @@ -153,6 +153,9 @@ struct cdns3_otg_common_regs { /* Only for CDNS3_CONTROLLER_V0 version */ #define OVERRIDE_IDPULLUP_V0 BIT(24) +/* PHYRST_CFG - bitmasks */ +#define PHYRST_CFG_PHYRST_A_ENABLE BIT(0) + #define CDNS3_ID_PERIPHERAL 1 #define CDNS3_ID_HOST 0 -- cgit v1.2.3 From 028296e480c782f13428f234a8239a0cd007bd92 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Sun, 20 Sep 2020 18:01:58 +0100 Subject: USB: gadget: f_ncm: Fix NDP16 datagram validation commit 2b74b0a04d3e ("USB: gadget: f_ncm: add bounds checks to ncm_unwrap_ntb()") adds important bounds checking however it unfortunately also introduces a bug with respect to section 3.3.1 of the NCM specification. wDatagramIndex[1] : "Byte index, in little endian, of the second datagram described by this NDP16. If zero, then this marks the end of the sequence of datagrams in this NDP16." wDatagramLength[1]: "Byte length, in little endian, of the second datagram described by this NDP16. If zero, then this marks the end of the sequence of datagrams in this NDP16." wDatagramIndex[1] and wDatagramLength[1] respectively then may be zero but that does not mean we should throw away the data referenced by wDatagramIndex[0] and wDatagramLength[0] as is currently the case. Breaking the loop on (index2 == 0 || dg_len2 == 0) should come at the end as was previously the case and checks for index2 and dg_len2 should be removed since zero is valid. I'm not sure how much testing the above patch received but for me right now after enumeration ping doesn't work. Reverting the commit restores ping, scp, etc. The extra validation associated with wDatagramIndex[0] and wDatagramLength[0] appears to be valid so, this change removes the incorrect restriction on wDatagramIndex[1] and wDatagramLength[1] restoring data processing between host and device. Fixes: 2b74b0a04d3e ("USB: gadget: f_ncm: add bounds checks to ncm_unwrap_ntb()") Cc: Ilja Van Sprundel Cc: Brooke Basile Cc: stable Signed-off-by: Bryan O'Donoghue Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_ncm.c | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index b4206b0dede5..1f638759a953 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -1189,7 +1189,6 @@ static int ncm_unwrap_ntb(struct gether *port, const struct ndp_parser_opts *opts = ncm->parser_opts; unsigned crc_len = ncm->is_crc ? sizeof(uint32_t) : 0; int dgram_counter; - bool ndp_after_header; /* dwSignature */ if (get_unaligned_le32(tmp) != opts->nth_sign) { @@ -1216,7 +1215,6 @@ static int ncm_unwrap_ntb(struct gether *port, } ndp_index = get_ncm(&tmp, opts->ndp_index); - ndp_after_header = false; /* Run through all the NDP's in the NTB */ do { @@ -1232,8 +1230,6 @@ static int ncm_unwrap_ntb(struct gether *port, ndp_index); goto err; } - if (ndp_index == opts->nth_size) - ndp_after_header = true; /* * walk through NDP @@ -1312,37 +1308,13 @@ static int ncm_unwrap_ntb(struct gether *port, index2 = get_ncm(&tmp, opts->dgram_item_len); dg_len2 = get_ncm(&tmp, opts->dgram_item_len); - if (index2 == 0 || dg_len2 == 0) - break; - /* wDatagramIndex[1] */ - if (ndp_after_header) { - if (index2 < opts->nth_size + opts->ndp_size) { - INFO(port->func.config->cdev, - "Bad index: %#X\n", index2); - goto err; - } - } else { - if (index2 < opts->nth_size + opts->dpe_size) { - INFO(port->func.config->cdev, - "Bad index: %#X\n", index2); - goto err; - } - } if (index2 > block_len - opts->dpe_size) { INFO(port->func.config->cdev, "Bad index: %#X\n", index2); goto err; } - /* wDatagramLength[1] */ - if ((dg_len2 < 14 + crc_len) || - (dg_len2 > frame_max)) { - INFO(port->func.config->cdev, - "Bad dgram length: %#X\n", dg_len); - goto err; - } - /* * Copy the data into a new skb. * This ensures the truesize is correct @@ -1359,6 +1331,8 @@ static int ncm_unwrap_ntb(struct gether *port, ndp_len -= 2 * (opts->dgram_item_len * 2); dgram_counter++; + if (index2 == 0 || dg_len2 == 0) + break; } while (ndp_len > 2 * (opts->dgram_item_len * 2)); } while (ndp_index); -- cgit v1.2.3 From 0abe3863d05f3175866cfaea50f66dc3ee043220 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sun, 20 Sep 2020 02:18:50 +0200 Subject: usb: dwc2: add support for APM82181 USB OTG adds the specific compatible string for the DWC2 IP found in the APM82181 SoCs. The IP is setup correctly through the auto detection... With the exception of the AHB Burst Size. The default of GAHBCFG_HBSTLEN_INCR4 of the "snps,dwc2" can cause a system hang when the USB and SATA is used concurrently. Because the predecessor (PPC460EX (Canyonlands)) already had the same problem, this SoC can make use of the existing dwc2_set_amcc_params() function. Signed-off-by: Christian Lamparter Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/params.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index a3611cdd1dea..4a454cca8d77 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -210,6 +210,7 @@ const struct of_device_id dwc2_of_match_table[] = { { .compatible = "amlogic,meson-g12a-usb", .data = dwc2_set_amlogic_g12a_params }, { .compatible = "amcc,dwc-otg", .data = dwc2_set_amcc_params }, + { .compatible = "apm,apm82181-dwc-otg", .data = dwc2_set_amcc_params }, { .compatible = "st,stm32f4x9-fsotg", .data = dwc2_set_stm32f4x9_fsotg_params }, { .compatible = "st,stm32f4x9-hsotg" }, -- cgit v1.2.3 From 5bb1d1197374aaf11523c8fd24a4e6f93fb3b343 Mon Sep 17 00:00:00 2001 From: Qinglang Miao Date: Sat, 19 Sep 2020 10:52:08 +0800 Subject: usb: gadget: lpc32xx_udc: Convert to DEFINE_SHOW_ATTRIBUTE Use DEFINE_SHOW_ATTRIBUTE macro to simplify the code. Acked-by: Vladimir Zapolskiy Signed-off-by: Qinglang Miao Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/lpc32xx_udc.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index e8a4637a9a17..3f1c62adce4b 100644 --- a/drivers/usb/gadget/udc/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -495,7 +495,7 @@ static void proc_ep_show(struct seq_file *s, struct lpc32xx_ep *ep) } } -static int proc_udc_show(struct seq_file *s, void *unused) +static int udc_show(struct seq_file *s, void *unused) { struct lpc32xx_udc *udc = s->private; struct lpc32xx_ep *ep; @@ -524,22 +524,11 @@ static int proc_udc_show(struct seq_file *s, void *unused) return 0; } -static int proc_udc_open(struct inode *inode, struct file *file) -{ - return single_open(file, proc_udc_show, PDE_DATA(inode)); -} - -static const struct file_operations proc_ops = { - .owner = THIS_MODULE, - .open = proc_udc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(udc); static void create_debug_file(struct lpc32xx_udc *udc) { - udc->pde = debugfs_create_file(debug_filename, 0, NULL, udc, &proc_ops); + udc->pde = debugfs_create_file(debug_filename, 0, NULL, udc, &udc_fops); } static void remove_debug_file(struct lpc32xx_udc *udc) -- cgit v1.2.3 From 864bc7e7297fe9282a7e17f01806029eb1d675c1 Mon Sep 17 00:00:00 2001 From: Pawel Laszczak Date: Fri, 18 Sep 2020 10:30:35 +0200 Subject: usb: gadget: config_ep_by_speed_and_alt instead of config_ep_by_speed This patch replace config_ep_by_speed with config_ep_by_speed_and_alt. This change allows to select proper usb_ss_ep_comp_descriptor for each stream capable endpoints. f_tcm function for SS use array of headers for both BOT/UAS alternate setting: static struct usb_descriptor_header *uasp_ss_function_desc[] = { (struct usb_descriptor_header *) &bot_intf_desc, (struct usb_descriptor_header *) &uasp_ss_bi_desc, (struct usb_descriptor_header *) &bot_bi_ep_comp_desc, (struct usb_descriptor_header *) &uasp_ss_bo_desc, (struct usb_descriptor_header *) &bot_bo_ep_comp_desc, (struct usb_descriptor_header *) &uasp_intf_desc, (struct usb_descriptor_header *) &uasp_ss_bi_desc, (struct usb_descriptor_header *) &uasp_bi_ep_comp_desc, (struct usb_descriptor_header *) &uasp_bi_pipe_desc, (struct usb_descriptor_header *) &uasp_ss_bo_desc, (struct usb_descriptor_header *) &uasp_bo_ep_comp_desc, (struct usb_descriptor_header *) &uasp_bo_pipe_desc, (struct usb_descriptor_header *) &uasp_ss_status_desc, (struct usb_descriptor_header *) &uasp_status_in_ep_comp_desc, (struct usb_descriptor_header *) &uasp_status_pipe_desc, (struct usb_descriptor_header *) &uasp_ss_cmd_desc, (struct usb_descriptor_header *) &uasp_cmd_comp_desc, (struct usb_descriptor_header *) &uasp_cmd_pipe_desc, NULL, }; The first 5 descriptors are associated with BOT alternate setting, and others are associated with UAS. During handling UAS alternate setting f_tcm driver invokes config_ep_by_speed and this function sets incorrect companion endpoint descriptor in usb_ep object. Instead setting ep->comp_desc to uasp_bi_ep_comp_desc function in this case set ep->comp_desc to bot_uasp_ss_bi_desc. And in result it uses the descriptor from BOT alternate setting instead UAS. Finally, it causes that controller driver during enabling endpoints detect that just enabled endpoint for bot. Signed-off-by: Jayshri Pawar Signed-off-by: Pawel Laszczak Reviewed-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_tcm.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c index 184165e27908..410fa89eae8f 100644 --- a/drivers/usb/gadget/function/f_tcm.c +++ b/drivers/usb/gadget/function/f_tcm.c @@ -392,12 +392,12 @@ static void bot_set_alt(struct f_uas *fu) fu->flags = USBG_IS_BOT; - config_ep_by_speed(gadget, f, fu->ep_in); + config_ep_by_speed_and_alt(gadget, f, fu->ep_in, USB_G_ALT_INT_BBB); ret = usb_ep_enable(fu->ep_in); if (ret) goto err_b_in; - config_ep_by_speed(gadget, f, fu->ep_out); + config_ep_by_speed_and_alt(gadget, f, fu->ep_out, USB_G_ALT_INT_BBB); ret = usb_ep_enable(fu->ep_out); if (ret) goto err_b_out; @@ -852,21 +852,21 @@ static void uasp_set_alt(struct f_uas *fu) if (gadget->speed >= USB_SPEED_SUPER) fu->flags |= USBG_USE_STREAMS; - config_ep_by_speed(gadget, f, fu->ep_in); + config_ep_by_speed_and_alt(gadget, f, fu->ep_in, USB_G_ALT_INT_UAS); ret = usb_ep_enable(fu->ep_in); if (ret) goto err_b_in; - config_ep_by_speed(gadget, f, fu->ep_out); + config_ep_by_speed_and_alt(gadget, f, fu->ep_out, USB_G_ALT_INT_UAS); ret = usb_ep_enable(fu->ep_out); if (ret) goto err_b_out; - config_ep_by_speed(gadget, f, fu->ep_cmd); + config_ep_by_speed_and_alt(gadget, f, fu->ep_cmd, USB_G_ALT_INT_UAS); ret = usb_ep_enable(fu->ep_cmd); if (ret) goto err_cmd; - config_ep_by_speed(gadget, f, fu->ep_status); + config_ep_by_speed_and_alt(gadget, f, fu->ep_status, USB_G_ALT_INT_UAS); ret = usb_ep_enable(fu->ep_status); if (ret) goto err_status; -- cgit v1.2.3 From 54c1960605107e1b5e562966e3dc3d29526f66b3 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 14 Sep 2020 14:06:34 +0100 Subject: usb: dwc2: Always disable regulators on driver teardown If the dwc2 driver fails to probe after having enabled the regulators, it ends up being unregistered with regulators enabled, something the core regulator code is legitimately upset about: dwc2 ff400000.usb: supply vusb_d not found, using dummy regulator dwc2 ff400000.usb: supply vusb_a not found, using dummy regulator dwc2 ff400000.usb: dwc2_core_reset: HANG! AHB Idle timeout GRSTCTL GRSTCTL_AHBIDLE WARNING: CPU: 2 PID: 112 at drivers/regulator/core.c:2074 _regulator_put.part.0+0x16c/0x174 Modules linked in: dwc2(E+) dwc3(E) udc_core(E) rtc_hym8563(E) dwmac_generic(E) ulpi(E) usbcore(E) dwc3_meson_g12a(E) roles(E) meson_gx_mmc(E+) i2c_meson(E) mdio_mux_meson_g12a(E) mdio_mux(E) dwmac_meson8b(E) stmmac_platform(E) stmmac(E) mdio_xpcs(E) phylink(E) of_mdio(E) fixed_phy(E) libphy(E) pwm_regulator(E) fixed(E) CPU: 2 PID: 112 Comm: systemd-udevd Tainted: G E 5.9.0-rc4-00102-g423583bc8cf9 #1840 Hardware name: amlogic w400/w400, BIOS 2020.04 05/22/2020 pstate: 80400009 (Nzcv daif +PAN -UAO BTYPE=--) pc : _regulator_put.part.0+0x16c/0x174 lr : regulator_bulk_free+0x6c/0x9c sp : ffffffc012353820 x29: ffffffc012353820 x28: ffffff805a4b7000 x27: ffffff8059c2eac0 x26: ffffff8059c2e810 x25: ffffff805a4b7d00 x24: ffffffc008cf3028 x23: ffffffc011729ef8 x22: ffffff807e2761d8 x21: ffffffc01171df78 x20: ffffff805a4b7700 x19: ffffff805a4b7700 x18: 0000000000000030 x17: 0000000000000000 x16: 0000000000000000 x15: ffffff807ea8d178 x14: 3935312820435455 x13: 2038323a36313a37 x12: ffffffffffffffff x11: 0000000000000040 x10: 0000000000000007 x9 : ffffffc0106f77d0 x8 : ffffffffffffffe0 x7 : ffffffffffffffff x6 : 0000000000017702 x5 : ffffff805a4b7400 x4 : 0000000000000000 x3 : ffffffc01171df78 x2 : ffffff807ea8cc40 x1 : 0000000000000000 x0 : 0000000000000001 Call trace: _regulator_put.part.0+0x16c/0x174 regulator_bulk_free+0x6c/0x9c devm_regulator_bulk_release+0x28/0x3c release_nodes+0x1c8/0x2c0 devres_release_all+0x44/0x6c really_probe+0x1ec/0x504 driver_probe_device+0x100/0x170 device_driver_attach+0xcc/0xd4 __driver_attach+0xb0/0x17c bus_for_each_dev+0x7c/0xd4 driver_attach+0x30/0x3c bus_add_driver+0x154/0x250 driver_register+0x84/0x140 __platform_driver_register+0x54/0x60 dwc2_platform_driver_init+0x2c/0x1000 [dwc2] do_one_initcall+0x54/0x2d0 do_init_module+0x68/0x29c In order to fix this, tie the regulator disabling to the teardown process by registering a devm action callback. This makes sure that the regulators are disabled at the right time (just before they are released). Cc: Minas Harutyunyan Cc: Greg Kroah-Hartman Signed-off-by: Marc Zyngier Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/platform.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index b28e90e0b685..c8844aea5607 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -121,6 +121,13 @@ static int dwc2_get_dr_mode(struct dwc2_hsotg *hsotg) return 0; } +static void __dwc2_disable_regulators(void *data) +{ + struct dwc2_hsotg *hsotg = data; + + regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); +} + static int __dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg) { struct platform_device *pdev = to_platform_device(hsotg->dev); @@ -131,6 +138,11 @@ static int __dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg) if (ret) return ret; + ret = devm_add_action_or_reset(&pdev->dev, + __dwc2_disable_regulators, hsotg); + if (ret) + return ret; + if (hsotg->clk) { ret = clk_prepare_enable(hsotg->clk); if (ret) @@ -186,10 +198,7 @@ static int __dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg) if (hsotg->clk) clk_disable_unprepare(hsotg->clk); - ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), - hsotg->supplies); - - return ret; + return 0; } /** -- cgit v1.2.3 From a609ce2a13360d639b384b6ca783b38c1247f2db Mon Sep 17 00:00:00 2001 From: Raymond Tan Date: Fri, 21 Aug 2020 16:11:01 +0300 Subject: usb: dwc3: pci: Allow Elkhart Lake to utilize DSM method for PM functionality Similar to some other IA platforms, Elkhart Lake too depends on the PMU register write to request transition of Dx power state. Thus, we add the PCI_DEVICE_ID_INTEL_EHLLP to the list of devices that shall execute the ACPI _DSM method during D0/D3 sequence. [heikki.krogerus@linux.intel.com: included Fixes tag] Fixes: dbb0569de852 ("usb: dwc3: pci: Add Support for Intel Elkhart Lake Devices") Cc: stable@vger.kernel.org Signed-off-by: Raymond Tan Signed-off-by: Heikki Krogerus Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-pci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index f5a61f57c74f..242b6210380a 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -147,7 +147,8 @@ static int dwc3_pci_quirks(struct dwc3_pci *dwc) if (pdev->vendor == PCI_VENDOR_ID_INTEL) { if (pdev->device == PCI_DEVICE_ID_INTEL_BXT || - pdev->device == PCI_DEVICE_ID_INTEL_BXT_M) { + pdev->device == PCI_DEVICE_ID_INTEL_BXT_M || + pdev->device == PCI_DEVICE_ID_INTEL_EHLLP) { guid_parse(PCI_INTEL_BXT_DSM_GUID, &dwc->guid); dwc->has_dsm_for_pm = true; } -- cgit v1.2.3 From 50642709f6590fe40afa6d22c32f23f5b842aed5 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Tue, 1 Sep 2020 10:33:48 +0800 Subject: usb: cdns3: core: quit if it uses role switch class If the board uses role switch class for switching the role, it should not depends on SoC OTG hardware siginal any more, so quit early. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/core.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index 4c1445cf2ad0..a0f73d4711ae 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -280,6 +280,10 @@ int cdns3_hw_role_switch(struct cdns3 *cdns) enum usb_role real_role, current_role; int ret = 0; + /* Depends on role switch class */ + if (cdns->role_sw) + return 0; + pm_runtime_get_sync(cdns->dev); current_role = cdns->role; -- cgit v1.2.3 From b5148d946f45bb7a780c2dc45a20f5b47e73f995 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Tue, 1 Sep 2020 10:33:49 +0800 Subject: usb: cdns3: gadget: set fast access bit Below is the recommendation from Cadence designer: Using this bit to be sure that PHY clock is keeping up in active state. It's good to keep Fast Access bit enabled as long as there is any access to USB register. It is used to fix the potential ARM core hang when visit controller register after DEVDS (.pullup is cleared) is set, the threaded irq may be scheduled at that time. Cc: Pawel Laszczak Reviewed-by: Jun Li Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/gadget.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index 9e6ff7d00784..081447bf7313 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -2783,6 +2783,8 @@ static void cdns3_gadget_config(struct cdns3_device *priv_dev) /* enable generic interrupt*/ writel(USB_IEN_INIT, ®s->usb_ien); writel(USB_CONF_CLK2OFFDS | USB_CONF_L1DS, ®s->usb_conf); + /* keep Fast Access bit */ + writel(PUSB_PWR_FST_REG_ACCESS, &priv_dev->regs->usb_pwr); cdns3_configure_dmult(priv_dev, NULL); } @@ -2866,6 +2868,7 @@ static int cdns3_gadget_udc_stop(struct usb_gadget *gadget) /* disable interrupt for device */ writel(0, &priv_dev->regs->usb_ien); + writel(0, &priv_dev->regs->usb_pwr); writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf); return 0; -- cgit v1.2.3 From 0eeda059956d57b16143cc1dd0aed7e5fc383d5d Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Tue, 1 Sep 2020 10:33:50 +0800 Subject: usb: cdns3: gadget: clear the interrupt status when disconnect the host It is meaningless to handle any interrupts after disconnecting with host Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/gadget.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index 081447bf7313..4c939dad9c33 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -2739,10 +2739,13 @@ static int cdns3_gadget_pullup(struct usb_gadget *gadget, int is_on) { struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget); - if (is_on) + if (is_on) { writel(USB_CONF_DEVEN, &priv_dev->regs->usb_conf); - else + } else { + writel(~0, &priv_dev->regs->ep_ists); + writel(~0, &priv_dev->regs->usb_ists); writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf); + } return 0; } -- cgit v1.2.3 From 9f650135945fb5dba6bd6340ce834570fe0686f2 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Tue, 1 Sep 2020 10:33:51 +0800 Subject: usb: cdns3: drd: call PHY .set_mode accordingly Some PHYs may need to enter related mode, and do some settings. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/drd.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c index fcd295f566b2..38ccd29e4cde 100644 --- a/drivers/usb/cdns3/drd.c +++ b/drivers/usb/cdns3/drd.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "gadget.h" #include "drd.h" @@ -157,6 +158,7 @@ int cdns3_drd_host_on(struct cdns3 *cdns) if (ret) dev_err(cdns->dev, "timeout waiting for xhci_ready\n"); + phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_HOST); return ret; } @@ -176,6 +178,7 @@ void cdns3_drd_host_off(struct cdns3 *cdns) readl_poll_timeout_atomic(&cdns->otg_regs->state, val, !(val & OTGSTATE_HOST_STATE_MASK), 1, 2000000); + phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID); } /** @@ -202,6 +205,7 @@ int cdns3_drd_gadget_on(struct cdns3 *cdns) return ret; } + phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_DEVICE); return 0; } @@ -225,6 +229,7 @@ void cdns3_drd_gadget_off(struct cdns3 *cdns) readl_poll_timeout_atomic(&cdns->otg_regs->state, val, !(val & OTGSTATE_DEV_STATE_MASK), 1, 2000000); + phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID); } /** -- cgit v1.2.3 From b21cf9371c2e659dbd0b7099b936b67f424fb555 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Tue, 1 Sep 2020 10:33:52 +0800 Subject: usb: cdns3: gadget: move wait configuration operation After commit f4cfe5ce607d ("usb: cdns3: gadget: improve the set_configuration handling"), the software will inform the hardware the request has finished at cdns3_ep0_complete_setup. The configuration set bit is only set after request has finished, so it needs to move waiting operation after that. Meanwhile, if it is timeout, it will show warning message and return error. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/ep0.c | 10 +++++++++- drivers/usb/cdns3/gadget.c | 5 ----- 2 files changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c index d9779abc65b2..4761c852d9c4 100644 --- a/drivers/usb/cdns3/ep0.c +++ b/drivers/usb/cdns3/ep0.c @@ -717,9 +717,17 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep, /* send STATUS stage. Should be called only for SET_CONFIGURATION */ if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE) { + u32 val; + cdns3_select_ep(priv_dev, 0x00); cdns3_set_hw_configuration(priv_dev); cdns3_ep0_complete_setup(priv_dev, 0, 1); + /* wait until configuration set */ + ret = readl_poll_timeout_atomic(&priv_dev->regs->usb_sts, val, + val & USB_STS_CFGSTS_MASK, 1, 100); + if (ret == -ETIMEDOUT) + dev_warn(priv_dev->dev, "timeout for waiting configuration set\n"); + request->actual = 0; priv_dev->status_completion_no_call = true; priv_dev->pending_status_request = request; @@ -731,7 +739,7 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep, * ep0_queue is back. */ queue_work(system_freezable_wq, &priv_dev->pending_status_wq); - return 0; + return ret; } if (!list_empty(&priv_ep->pending_req_list)) { diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index 4c939dad9c33..aca347b7da57 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -1310,7 +1310,6 @@ void cdns3_set_hw_configuration(struct cdns3_device *priv_dev) { struct cdns3_endpoint *priv_ep; struct usb_ep *ep; - int val; if (priv_dev->hw_configured_flag) return; @@ -1320,10 +1319,6 @@ void cdns3_set_hw_configuration(struct cdns3_device *priv_dev) cdns3_set_register_bit(&priv_dev->regs->usb_conf, USB_CONF_U1EN | USB_CONF_U2EN); - /* wait until configuration set */ - readl_poll_timeout_atomic(&priv_dev->regs->usb_sts, val, - val & USB_STS_CFGSTS_MASK, 1, 100); - priv_dev->hw_configured_flag = 1; list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) { -- cgit v1.2.3 From 986499b1569af980a819817f17238015b27793f6 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 25 Aug 2020 14:55:03 +0900 Subject: usb: gadget: f_ncm: fix ncm_bitrate for SuperSpeed and above. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, SuperSpeed NCM gadgets report a speed of 851 Mbps in USB_CDC_NOTIFY_SPEED_CHANGE. But the calculation appears to assume 16 packets per microframe, and USB 3 and above no longer use microframes. Maximum speed is actually much higher. On a direct connection, theoretical throughput is at most 3.86 Gbps for gen1x1 and 9.36 Gbps for gen2x1, and I have seen gadget->host iperf throughput of >2 Gbps for gen1x1 and >4 Gbps for gen2x1. Unfortunately the ConnectionSpeedChange defined in the CDC spec only uses 32-bit values, so we can't report accurate numbers for 10Gbps and above. So, report 3.75Gbps for SuperSpeed (which is roughly maximum theoretical performance) and 4.25Gbps for SuperSpeed Plus (which is close to the maximum that we can report in a 32-bit unsigned integer). This results in: [50879.191272] cdc_ncm 2-2:1.0 enx228b127e050c: renamed from usb0 [50879.234778] cdc_ncm 2-2:1.0 enx228b127e050c: 3750 mbit/s downlink 3750 mbit/s uplink on SuperSpeed and: [50798.434527] cdc_ncm 8-2:1.0 enx228b127e050c: renamed from usb0 [50798.524278] cdc_ncm 8-2:1.0 enx228b127e050c: 4250 mbit/s downlink 4250 mbit/s uplink on SuperSpeed Plus. Fixes: 1650113888fe ("usb: gadget: f_ncm: add SuperSpeed descriptors for CDC NCM") Reviewed-by: Maciej Å»enczykowski Signed-off-by: Lorenzo Colitti Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_ncm.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index 1f638759a953..7672fa25085b 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -85,8 +85,10 @@ static inline struct f_ncm *func_to_ncm(struct usb_function *f) /* peak (theoretical) bulk transfer rate in bits-per-second */ static inline unsigned ncm_bitrate(struct usb_gadget *g) { - if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER) - return 13 * 1024 * 8 * 1000 * 8; + if (gadget_is_superspeed(g) && g->speed >= USB_SPEED_SUPER_PLUS) + return 4250000000U; + else if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER) + return 3750000000U; else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) return 13 * 512 * 8 * 1000 * 8; else -- cgit v1.2.3 From a176b1a2a73c9598f77f2fa3df67184321092f55 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 25 Aug 2020 14:55:04 +0900 Subject: usb: gadget: f_ncm: set SuperSpeed bulk descriptor bMaxBurst to 15 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This improves performance on fast connections. When directly connecting to a Linux laptop running 5.6, single-stream iperf3 goes from ~1.7Gbps to ~2.3Gbps out, and from ~620Mbps to ~720Mbps in. Reviewed-by: Maciej Å»enczykowski Signed-off-by: Lorenzo Colitti Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_ncm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index 7672fa25085b..ffa397a1c3d4 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -378,7 +378,7 @@ static struct usb_ss_ep_comp_descriptor ss_ncm_bulk_comp_desc = { .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, /* the following 2 values can be tweaked if necessary */ - /* .bMaxBurst = 0, */ + .bMaxBurst = 15, /* .bmAttributes = 0, */ }; -- cgit v1.2.3 From 7974ecd7d3c0f42a98566f281e44ea8573a2ad88 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 25 Aug 2020 14:55:05 +0900 Subject: usb: gadget: f_ncm: allow using NCM in SuperSpeed Plus gadgets. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, enabling f_ncm at SuperSpeed Plus speeds results in an oops in config_ep_by_speed because ncm_set_alt passes in NULL ssp_descriptors. Fix this by re-using the SuperSpeed descriptors. This is safe because usb_assign_descriptors calls usb_copy_descriptors. Tested: enabled f_ncm on a dwc3 gadget and 10Gbps link, ran iperf Reviewed-by: Maciej Å»enczykowski Signed-off-by: Lorenzo Colitti Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_ncm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index ffa397a1c3d4..019bea8e09cc 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -1536,7 +1536,7 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) fs_ncm_notify_desc.bEndpointAddress; status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function, - ncm_ss_function, NULL); + ncm_ss_function, ncm_ss_function); if (status) goto fail; -- cgit v1.2.3 From 897b81384302bf2e1eaff008163e492dda4ceaca Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 8 Sep 2020 17:57:19 -0700 Subject: usb: phy: phy-ab8500-usb: fix spello of "function" Fix typo/spello of "function". Signed-off-by: Randy Dunlap Cc: Felipe Balbi Cc: linux-usb@vger.kernel.org Cc: Jiri Kosina Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-ab8500-usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index aa4a3140394b..4c52ba96f17e 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -518,7 +518,7 @@ static int ab8500_usb_link_status_update(struct ab8500_usb *ab, * 3. Enable AB regulators * 4. Enable USB phy * 5. Reset the musb controller - * 6. Switch the ULPI GPIO pins to fucntion mode + * 6. Switch the ULPI GPIO pins to function mode * 7. Enable the musb Peripheral5 clock * 8. Restore MUSB context */ -- cgit v1.2.3 From d98ef43bfb65b5201e1afe36aaf8c4f9d71b4307 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 9 Sep 2020 09:01:32 +0900 Subject: usb: gadget: u_serial: clear suspended flag when disconnecting The commit aba3a8d01d62 ("usb: gadget: u_serial: add suspend resume callbacks") set/cleared the suspended flag in USB bus suspend/resume only. But, when a USB cable is disconnected in the suspend, since some controllers will not detect USB bus resume, the suspended flag is not cleared. After that, user cannot send any data. To fix the issue, clears the suspended flag in the gserial_disconnect(). Fixes: aba3a8d01d62 ("usb: gadget: u_serial: add suspend resume callbacks") Signed-off-by: Yoshihiro Shimoda Tested-by: Linh Phung Tested-by: Tam Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/u_serial.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index 127ecc2b4317..2caccbb6e014 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -1391,6 +1391,7 @@ void gserial_disconnect(struct gserial *gser) if (port->port.tty) tty_hangup(port->port.tty); } + port->suspended = false; spin_unlock_irqrestore(&port->port_lock, flags); /* disable endpoints, aborting down any active I/O */ -- cgit v1.2.3 From 4eea21dc67b0c6ba15ae41b1defa113a680a858e Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 19 Aug 2020 01:19:49 +0900 Subject: usb: gadget: u_ether: enable qmult on SuperSpeed Plus as well MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The u_ether driver has a qmult setting that multiplies the transmit queue length (which by default is 2). The intent is that it should be enabled at high/super speed, but because the code does not explicitly check for USB_SUPER_PLUS, it is disabled at that speed. Fix this by ensuring that the queue multiplier is enabled for any wired link at high speed or above. Using >= for USB_SPEED_* constants seems correct because it is what the gadget_is_xxxspeed functions do. The queue multiplier substantially helps performance at higher speeds. On a direct SuperSpeed Plus link to a Linux laptop, iperf3 single TCP stream: Before (qmult=1): 1.3 Gbps After (qmult=5): 3.2 Gbps Fixes: 04617db7aa68 ("usb: gadget: add SS descriptors to Ethernet gadget") Reviewed-by: Maciej Å»enczykowski Signed-off-by: Lorenzo Colitti Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/u_ether.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index c3cc6bd14e61..31ea76adcc0d 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -93,7 +93,7 @@ struct eth_dev { static inline int qlen(struct usb_gadget *gadget, unsigned qmult) { if (gadget_is_dualspeed(gadget) && (gadget->speed == USB_SPEED_HIGH || - gadget->speed == USB_SPEED_SUPER)) + gadget->speed >= USB_SPEED_SUPER)) return qmult * DEFAULT_QLEN; else return DEFAULT_QLEN; -- cgit v1.2.3 From 87a2dfb136430c914f286e4b9870d538e559df29 Mon Sep 17 00:00:00 2001 From: Ye Bin Date: Mon, 24 Aug 2020 16:42:34 +0800 Subject: usb: gadget: fsl: Fix unsigned expression compared with zero in fsl_udc_probe udc_controller->irq is "unsigned int" always >= 0, but platform_get_irq may return little than zero. So "dc_controller->irq < 0" condition is never accessible. Acked-by: Li Yang Signed-off-by: Ye Bin Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/fsl_udc_core.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index a6f7b2594c09..3e98740b8cfc 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -2439,11 +2439,12 @@ static int fsl_udc_probe(struct platform_device *pdev) /* DEN is bidirectional ep number, max_ep doubles the number */ udc_controller->max_ep = (dccparams & DCCPARAMS_DEN_MASK) * 2; - udc_controller->irq = platform_get_irq(pdev, 0); - if (udc_controller->irq <= 0) { - ret = udc_controller->irq ? : -ENODEV; + ret = platform_get_irq(pdev, 0); + if (ret <= 0) { + ret = ret ? : -ENODEV; goto err_iounmap; } + udc_controller->irq = ret; ret = request_irq(udc_controller->irq, fsl_udc_irq, IRQF_SHARED, driver_name, udc_controller); -- cgit v1.2.3 From 8dafb3c04df3b3b5a3ee2c7f5d8285a8b2f0aa78 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Fri, 21 Aug 2020 11:14:37 +0800 Subject: usb: cdns3: gadget: fix some endian issues It is found by sparse. Reported-by: kbuild test robot Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/gadget.c | 60 +++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 30 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index aca347b7da57..d3f079af4a00 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -261,8 +261,8 @@ int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep) */ link_trb->control = 0; } else { - link_trb->buffer = TRB_BUFFER(priv_ep->trb_pool_dma); - link_trb->control = TRB_CYCLE | TRB_TYPE(TRB_LINK) | TRB_TOGGLE; + link_trb->buffer = cpu_to_le32(TRB_BUFFER(priv_ep->trb_pool_dma)); + link_trb->control = cpu_to_le32(TRB_CYCLE | TRB_TYPE(TRB_LINK) | TRB_TOGGLE); } return 0; } @@ -847,10 +847,10 @@ static void cdns3_wa1_restore_cycle_bit(struct cdns3_endpoint *priv_ep) priv_ep->wa1_trb_index = 0xFFFF; if (priv_ep->wa1_cycle_bit) { priv_ep->wa1_trb->control = - priv_ep->wa1_trb->control | 0x1; + priv_ep->wa1_trb->control | cpu_to_le32(0x1); } else { priv_ep->wa1_trb->control = - priv_ep->wa1_trb->control & ~0x1; + priv_ep->wa1_trb->control & cpu_to_le32(~0x1); } } } @@ -1008,17 +1008,16 @@ static int cdns3_ep_run_stream_transfer(struct cdns3_endpoint *priv_ep, TRB_STREAM_ID(priv_req->request.stream_id) | TRB_ISP; if (!request->num_sgs) { - trb->buffer = TRB_BUFFER(trb_dma); + trb->buffer = cpu_to_le32(TRB_BUFFER(trb_dma)); length = request->length; } else { - trb->buffer = TRB_BUFFER(request->sg[sg_idx].dma_address); + trb->buffer = cpu_to_le32(TRB_BUFFER(request->sg[sg_idx].dma_address)); length = request->sg[sg_idx].length; } tdl = DIV_ROUND_UP(length, priv_ep->endpoint.maxpacket); - trb->length = TRB_BURST_LEN(16 /*priv_ep->trb_burst_size*/) | - TRB_LEN(length); + trb->length = cpu_to_le32(TRB_BURST_LEN(16) | TRB_LEN(length)); /* * For DEV_VER_V2 controller version we have enabled @@ -1027,11 +1026,11 @@ static int cdns3_ep_run_stream_transfer(struct cdns3_endpoint *priv_ep, */ if (priv_dev->dev_ver >= DEV_VER_V2) { if (priv_dev->gadget.speed == USB_SPEED_SUPER) - trb->length |= TRB_TDL_SS_SIZE(tdl); + trb->length |= cpu_to_le32(TRB_TDL_SS_SIZE(tdl)); } priv_req->flags |= REQUEST_PENDING; - trb->control = control; + trb->control = cpu_to_le32(control); trace_cdns3_prepare_trb(priv_ep, priv_req->trb); @@ -1156,8 +1155,8 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, TRBS_PER_SEGMENT > 2) ch_bit = TRB_CHAIN; - link_trb->control = ((priv_ep->pcs) ? TRB_CYCLE : 0) | - TRB_TYPE(TRB_LINK) | TRB_TOGGLE | ch_bit; + link_trb->control = cpu_to_le32(((priv_ep->pcs) ? TRB_CYCLE : 0) | + TRB_TYPE(TRB_LINK) | TRB_TOGGLE | ch_bit); } if (priv_dev->dev_ver <= DEV_VER_V2) @@ -1172,8 +1171,8 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, /* fill TRB */ control |= TRB_TYPE(TRB_NORMAL); - trb->buffer = TRB_BUFFER(request->num_sgs == 0 - ? trb_dma : request->sg[sg_iter].dma_address); + trb->buffer = cpu_to_le32(TRB_BUFFER(request->num_sgs == 0 + ? trb_dma : request->sg[sg_iter].dma_address)); if (likely(!request->num_sgs)) length = request->length; @@ -1187,10 +1186,10 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, total_tdl += DIV_ROUND_UP(length, priv_ep->endpoint.maxpacket); - trb->length = TRB_BURST_LEN(priv_ep->trb_burst_size) | - TRB_LEN(length); + trb->length = cpu_to_le32(TRB_BURST_LEN(priv_ep->trb_burst_size) | + TRB_LEN(length)); if (priv_dev->gadget.speed == USB_SPEED_SUPER) - trb->length |= TRB_TDL_SS_SIZE(td_size); + trb->length |= cpu_to_le32(TRB_TDL_SS_SIZE(td_size)); else control |= TRB_TDL_HS_SIZE(td_size); @@ -1212,9 +1211,9 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, } if (sg_iter) - trb->control = control; + trb->control = cpu_to_le32(control); else - priv_req->trb->control = control; + priv_req->trb->control = cpu_to_le32(control); control = 0; ++sg_iter; @@ -1228,7 +1227,7 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, priv_req->flags |= REQUEST_PENDING; if (sg_iter == 1) - trb->control |= TRB_IOC | TRB_ISP; + trb->control |= cpu_to_le32(TRB_IOC | TRB_ISP); if (priv_dev->dev_ver < DEV_VER_V2 && (priv_ep->flags & EP_TDLCHK_EN)) { @@ -1254,7 +1253,7 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, /* give the TD to the consumer*/ if (togle_pcs) - trb->control = trb->control ^ 1; + trb->control = trb->control ^ cpu_to_le32(1); if (priv_dev->dev_ver <= DEV_VER_V2) cdns3_wa1_tray_restore_cycle_bit(priv_dev, priv_ep); @@ -1388,7 +1387,7 @@ static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep, trb = &priv_ep->trb_pool[priv_req->start_trb]; - if ((trb->control & TRB_CYCLE) != priv_ep->ccs) + if ((le32_to_cpu(trb->control) & TRB_CYCLE) != priv_ep->ccs) goto finish; if (doorbell == 1 && current_index == priv_ep->dequeue) @@ -1437,7 +1436,7 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev, trb = priv_ep->trb_pool + priv_ep->dequeue; /* Request was dequeued and TRB was changed to TRB_LINK. */ - if (TRB_FIELD_TO_TYPE(trb->control) == TRB_LINK) { + if (TRB_FIELD_TO_TYPE(le32_to_cpu(trb->control)) == TRB_LINK) { trace_cdns3_complete_trb(priv_ep, trb); cdns3_move_deq_to_next_trb(priv_req); } @@ -1569,7 +1568,7 @@ static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep) * that host ignore the ERDY packet and driver has to send it * again. */ - if (tdl && (dbusy | !EP_STS_BUFFEMPTY(ep_sts_reg) | + if (tdl && (dbusy || !EP_STS_BUFFEMPTY(ep_sts_reg) || EP_STS_HOSTPP(ep_sts_reg))) { writel(EP_CMD_ERDY | EP_CMD_ERDY_SID(priv_ep->last_stream_id), @@ -2551,10 +2550,10 @@ found: /* Update ring only if removed request is on pending_req_list list */ if (req_on_hw_ring && link_trb) { - link_trb->buffer = TRB_BUFFER(priv_ep->trb_pool_dma + - ((priv_req->end_trb + 1) * TRB_SIZE)); - link_trb->control = (link_trb->control & TRB_CYCLE) | - TRB_TYPE(TRB_LINK) | TRB_CHAIN; + link_trb->buffer = cpu_to_le32(TRB_BUFFER(priv_ep->trb_pool_dma + + ((priv_req->end_trb + 1) * TRB_SIZE))); + link_trb->control = cpu_to_le32((le32_to_cpu(link_trb->control) & TRB_CYCLE) | + TRB_TYPE(TRB_LINK) | TRB_CHAIN); if (priv_ep->wa1_trb == priv_req->trb) cdns3_wa1_restore_cycle_bit(priv_ep); @@ -2609,7 +2608,7 @@ int __cdns3_gadget_ep_clear_halt(struct cdns3_endpoint *priv_ep) priv_req = to_cdns3_request(request); trb = priv_req->trb; if (trb) - trb->control = trb->control ^ TRB_CYCLE; + trb->control = trb->control ^ cpu_to_le32(TRB_CYCLE); } writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd); @@ -2624,7 +2623,8 @@ int __cdns3_gadget_ep_clear_halt(struct cdns3_endpoint *priv_ep) if (request) { if (trb) - trb->control = trb->control ^ TRB_CYCLE; + trb->control = trb->control ^ cpu_to_le32(TRB_CYCLE); + cdns3_rearm_transfer(priv_ep, 1); } -- cgit v1.2.3 From 3301c215a2bb94b5a0afcb444bbe9bf2a395a65d Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 21 Aug 2020 10:55:44 +0800 Subject: USB: UDC: Expand device model API interface The routines used by the UDC core to interface with the kernel's device model, namely usb_add_gadget_udc(), usb_add_gadget_udc_release(), and usb_del_gadget_udc(), provide access to only a subset of the device model's full API. They include functionality equivalent to device_register() and device_unregister() for gadgets, but they omit device_initialize(), device_add(), device_del(), get_device(), and put_device(). This patch expands the UDC API by adding usb_initialize_gadget(), usb_add_gadget(), usb_del_gadget(), usb_get_gadget(), and usb_put_gadget() to fill in the gap. It rewrites the existing routines to call the new ones. CC: Anton Vasilyev CC: Evgeny Novikov CC: Benjamin Herrenschmidt Reviewed-by: Greg Kroah-Hartman Signed-off-by: Alan Stern Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/core.c | 78 ++++++++++++++++++++++++++++++++++--------- include/linux/usb/gadget.h | 27 +++++++++++---- 2 files changed, 84 insertions(+), 21 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index 4f82bcd31fd3..2b6770d9fb3f 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -1164,21 +1164,18 @@ static int check_pending_gadget_drivers(struct usb_udc *udc) } /** - * usb_add_gadget_udc_release - adds a new gadget to the udc class driver list + * usb_initialize_gadget - initialize a gadget and its embedded struct device * @parent: the parent device to this udc. Usually the controller driver's * device. - * @gadget: the gadget to be added to the list. + * @gadget: the gadget to be initialized. * @release: a gadget release function. * * Returns zero on success, negative errno otherwise. * Calls the gadget release function in the latter case. */ -int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, +void usb_initialize_gadget(struct device *parent, struct usb_gadget *gadget, void (*release)(struct device *dev)) { - struct usb_udc *udc; - int ret = -ENOMEM; - dev_set_name(&gadget->dev, "gadget"); INIT_WORK(&gadget->work, usb_gadget_state_work); gadget->dev.parent = parent; @@ -1189,17 +1186,32 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, gadget->dev.release = usb_udc_nop_release; device_initialize(&gadget->dev); +} +EXPORT_SYMBOL_GPL(usb_initialize_gadget); + +/** + * usb_add_gadget - adds a new gadget to the udc class driver list + * @gadget: the gadget to be added to the list. + * + * Returns zero on success, negative errno otherwise. + * Does not do a final usb_put_gadget() if an error occurs. + */ +int usb_add_gadget(struct usb_gadget *gadget) +{ + struct usb_udc *udc; + int ret = -ENOMEM; udc = kzalloc(sizeof(*udc), GFP_KERNEL); if (!udc) - goto err_put_gadget; + goto error; device_initialize(&udc->dev); udc->dev.release = usb_udc_release; udc->dev.class = udc_class; udc->dev.groups = usb_udc_attr_groups; - udc->dev.parent = parent; - ret = dev_set_name(&udc->dev, "%s", kobject_name(&parent->kobj)); + udc->dev.parent = gadget->dev.parent; + ret = dev_set_name(&udc->dev, "%s", + kobject_name(&gadget->dev.parent->kobj)); if (ret) goto err_put_udc; @@ -1242,8 +1254,30 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, err_put_udc: put_device(&udc->dev); - err_put_gadget: - put_device(&gadget->dev); + error: + return ret; +} +EXPORT_SYMBOL_GPL(usb_add_gadget); + +/** + * usb_add_gadget_udc_release - adds a new gadget to the udc class driver list + * @parent: the parent device to this udc. Usually the controller driver's + * device. + * @gadget: the gadget to be added to the list. + * @release: a gadget release function. + * + * Returns zero on success, negative errno otherwise. + * Calls the gadget release function in the latter case. + */ +int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, + void (*release)(struct device *dev)) +{ + int ret; + + usb_initialize_gadget(parent, gadget, release); + ret = usb_add_gadget(gadget); + if (ret) + usb_put_gadget(gadget); return ret; } EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release); @@ -1311,13 +1345,14 @@ static void usb_gadget_remove_driver(struct usb_udc *udc) } /** - * usb_del_gadget_udc - deletes @udc from udc_list + * usb_del_gadget - deletes @udc from udc_list * @gadget: the gadget to be removed. * - * This, will call usb_gadget_unregister_driver() if + * This will call usb_gadget_unregister_driver() if * the @udc is still busy. + * It will not do a final usb_put_gadget(). */ -void usb_del_gadget_udc(struct usb_gadget *gadget) +void usb_del_gadget(struct usb_gadget *gadget) { struct usb_udc *udc = gadget->udc; @@ -1340,7 +1375,20 @@ void usb_del_gadget_udc(struct usb_gadget *gadget) kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE); flush_work(&gadget->work); device_unregister(&udc->dev); - device_unregister(&gadget->dev); + device_del(&gadget->dev); +} +EXPORT_SYMBOL_GPL(usb_del_gadget); + +/** + * usb_del_gadget_udc - deletes @udc from udc_list + * @gadget: the gadget to be removed. + * + * Calls usb_del_gadget() and does a final usb_put_gadget(). + */ +void usb_del_gadget_udc(struct usb_gadget *gadget) +{ + usb_del_gadget(gadget); + usb_put_gadget(gadget); memset(&gadget->dev, 0x00, sizeof(gadget->dev)); } EXPORT_SYMBOL_GPL(usb_del_gadget_udc); diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 52ce1f6b8f83..e7351d64f11f 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -436,6 +436,7 @@ struct usb_gadget { }; #define work_to_gadget(w) (container_of((w), struct usb_gadget, work)) +/* Interface to the device model */ static inline void set_gadget_data(struct usb_gadget *gadget, void *data) { dev_set_drvdata(&gadget->dev, data); } static inline void *get_gadget_data(struct usb_gadget *gadget) @@ -444,6 +445,26 @@ static inline struct usb_gadget *dev_to_usb_gadget(struct device *dev) { return container_of(dev, struct usb_gadget, dev); } +static inline struct usb_gadget *usb_get_gadget(struct usb_gadget *gadget) +{ + get_device(&gadget->dev); + return gadget; +} +static inline void usb_put_gadget(struct usb_gadget *gadget) +{ + put_device(&gadget->dev); +} +extern void usb_initialize_gadget(struct device *parent, + struct usb_gadget *gadget, void (*release)(struct device *dev)); +extern int usb_add_gadget(struct usb_gadget *gadget); +extern void usb_del_gadget(struct usb_gadget *gadget); + +/* Legacy device-model interface */ +extern int usb_add_gadget_udc_release(struct device *parent, + struct usb_gadget *gadget, void (*release)(struct device *dev)); +extern int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget); +extern void usb_del_gadget_udc(struct usb_gadget *gadget); +extern char *usb_get_gadget_udc_name(void); /* iterates the non-control endpoints; 'tmp' is a struct usb_ep pointer */ #define gadget_for_each_ep(tmp, gadget) \ @@ -735,12 +756,6 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver); */ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver); -extern int usb_add_gadget_udc_release(struct device *parent, - struct usb_gadget *gadget, void (*release)(struct device *dev)); -extern int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget); -extern void usb_del_gadget_udc(struct usb_gadget *gadget); -extern char *usb_get_gadget_udc_name(void); - /*-------------------------------------------------------------------------*/ /* utility to simplify dealing with string descriptors */ -- cgit v1.2.3 From f770fbec4165b1acfabdeadb01ad6008d2c537b5 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 21 Aug 2020 10:55:45 +0800 Subject: USB: UDC: net2280: Fix memory leaks As Anton and Evgeny have noted, the net2280 UDC driver has a problem with leaking memory along some of its failure pathways. It also has another problem, not previously noted, in that some of the failure pathways will call usb_del_gadget_udc() without first calling usb_add_gadget_udc_release(). And it leaks memory by calling kfree() when it should call put_device(). Previous attempts to fix the problems have failed because of lack of support in the UDC core for separately initializing and adding gadgets, or for separately deleting and freeing gadgets. The previous patch in this series adds the necessary support, making it possible to fix the outstanding problems properly. This patch adds an "added" flag to the net2280 structure to indicate whether or not the gadget has been registered (and thus whether or not to call usb_del_gadget()), and it fixes the deallocation issues by calling usb_put_gadget() at the appropriate point. A similar memory leak issue, apparently never before recognized, stems from the fact that the driver never initializes the drvdata field in the gadget's embedded struct device! Evidently this wasn't noticed because the pointer is only ever used as an argument to kfree(), which doesn't mind getting called with a NULL pointer. In fact, the drvdata for gadget device will be written by usb_composite_dev structure if any gadget class is loaded, so it needs to use usb_gadget structure to get net2280 private data. CC: Benjamin Herrenschmidt Reported-by: Anton Vasilyev Reported-by: Evgeny Novikov Reviewed-by: Greg Kroah-Hartman Signed-off-by: Alan Stern Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2280.c | 11 +++++++---- drivers/usb/gadget/udc/net2280.h | 1 + 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 7530bd9a08c4..d50bc6e19f2a 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -3561,7 +3561,7 @@ static irqreturn_t net2280_irq(int irq, void *_dev) static void gadget_release(struct device *_dev) { - struct net2280 *dev = dev_get_drvdata(_dev); + struct net2280 *dev = container_of(_dev, struct net2280, gadget.dev); kfree(dev); } @@ -3572,7 +3572,8 @@ static void net2280_remove(struct pci_dev *pdev) { struct net2280 *dev = pci_get_drvdata(pdev); - usb_del_gadget_udc(&dev->gadget); + if (dev->added) + usb_del_gadget(&dev->gadget); BUG_ON(dev->driver); @@ -3603,6 +3604,7 @@ static void net2280_remove(struct pci_dev *pdev) device_remove_file(&pdev->dev, &dev_attr_registers); ep_info(dev, "unbind\n"); + usb_put_gadget(&dev->gadget); } /* wrap this driver around the specified device, but @@ -3624,6 +3626,7 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id) } pci_set_drvdata(pdev, dev); + usb_initialize_gadget(&pdev->dev, &dev->gadget, gadget_release); spin_lock_init(&dev->lock); dev->quirks = id->driver_data; dev->pdev = pdev; @@ -3774,10 +3777,10 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (retval) goto done; - retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget, - gadget_release); + retval = usb_add_gadget(&dev->gadget); if (retval) goto done; + dev->added = 1; return 0; done: diff --git a/drivers/usb/gadget/udc/net2280.h b/drivers/usb/gadget/udc/net2280.h index 85d3ca1698ba..7da3dc1e9729 100644 --- a/drivers/usb/gadget/udc/net2280.h +++ b/drivers/usb/gadget/udc/net2280.h @@ -156,6 +156,7 @@ struct net2280 { softconnect : 1, got_irq : 1, region:1, + added:1, u1_enable:1, u2_enable:1, ltm_enable:1, -- cgit v1.2.3 From 9b719c7119e77e8ddeefe4772c554d2863579c2b Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 21 Aug 2020 10:55:46 +0800 Subject: USB: UDC: net2272: Fix memory leaks Like net2280 (on which it was based), the net2272 UDC driver has a problem with leaking memory along some of its failure pathways. It also has another problem, not previously noted, in that some of the failure pathways will call usb_del_gadget_udc() without first calling usb_add_gadget_udc_release(). And it leaks memory by calling kfree() when it should call put_device(). Until now it has been impossible to handle the memory leaks, because of lack of support in the UDC core for separately initializing and adding gadgets, or for separately deleting and freeing gadgets. An earlier patch in this series adds the necessary support, making it possible to fix the outstanding problems properly. This patch adds an "added" flag to the net2272 structure to indicate whether or not the gadget has been registered (and thus whether or not to call usb_del_gadget()), and it fixes the deallocation issues by calling usb_put_gadget() at the appropriate places. A similar memory leak issue, apparently never before recognized, stems from the fact that the driver never initializes the drvdata field in the gadget's embedded struct device! Evidently this wasn't noticed because the pointer is only ever used as an argument to kfree(), which doesn't mind getting called with a NULL pointer. In fact, the drvdata for gadget device will be written by usb_composite_dev structure if any gadget class is loaded, so it needs to use usb_gadget structure to get net2280 private data. CC: Anton Vasilyev CC: Evgeny Novikov CC: Benjamin Herrenschmidt Reviewed-by: Greg Kroah-Hartman Signed-off-by: Alan Stern Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2272.c | 23 +++++++++++++---------- drivers/usb/gadget/udc/net2272.h | 1 + 2 files changed, 14 insertions(+), 10 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c index 440bcb3b6c23..23a735641c3d 100644 --- a/drivers/usb/gadget/udc/net2272.c +++ b/drivers/usb/gadget/udc/net2272.c @@ -2195,7 +2195,8 @@ static int net2272_present(struct net2272 *dev) static void net2272_gadget_release(struct device *_dev) { - struct net2272 *dev = dev_get_drvdata(_dev); + struct net2272 *dev = container_of(_dev, struct net2272, gadget.dev); + kfree(dev); } @@ -2204,7 +2205,8 @@ net2272_gadget_release(struct device *_dev) static void net2272_remove(struct net2272 *dev) { - usb_del_gadget_udc(&dev->gadget); + if (dev->added) + usb_del_gadget(&dev->gadget); free_irq(dev->irq, dev); iounmap(dev->base_addr); device_remove_file(dev->dev, &dev_attr_registers); @@ -2234,6 +2236,7 @@ static struct net2272 *net2272_probe_init(struct device *dev, unsigned int irq) /* the "gadget" abstracts/virtualizes the controller */ ret->gadget.name = driver_name; + usb_initialize_gadget(dev, &ret->gadget, net2272_gadget_release); return ret; } @@ -2272,10 +2275,10 @@ net2272_probe_fin(struct net2272 *dev, unsigned int irqflags) if (ret) goto err_irq; - ret = usb_add_gadget_udc_release(dev->dev, &dev->gadget, - net2272_gadget_release); + ret = usb_add_gadget(&dev->gadget); if (ret) goto err_add_udc; + dev->added = 1; return 0; @@ -2450,7 +2453,7 @@ net2272_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (pci_enable_device(pdev) < 0) { ret = -ENODEV; - goto err_free; + goto err_put; } pci_set_master(pdev); @@ -2473,8 +2476,8 @@ net2272_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) err_pci: pci_disable_device(pdev); - err_free: - kfree(dev); + err_put: + usb_put_gadget(&dev->gadget); return ret; } @@ -2535,7 +2538,7 @@ net2272_pci_remove(struct pci_dev *pdev) pci_disable_device(pdev); - kfree(dev); + usb_put_gadget(&dev->gadget); } /* Table of matching PCI IDs */ @@ -2648,7 +2651,7 @@ net2272_plat_probe(struct platform_device *pdev) err_req: release_mem_region(base, len); err: - kfree(dev); + usb_put_gadget(&dev->gadget); return ret; } @@ -2663,7 +2666,7 @@ net2272_plat_remove(struct platform_device *pdev) release_mem_region(pdev->resource[0].start, resource_size(&pdev->resource[0])); - kfree(dev); + usb_put_gadget(&dev->gadget); return 0; } diff --git a/drivers/usb/gadget/udc/net2272.h b/drivers/usb/gadget/udc/net2272.h index 87d0ab9ffeeb..c669308111c2 100644 --- a/drivers/usb/gadget/udc/net2272.h +++ b/drivers/usb/gadget/udc/net2272.h @@ -441,6 +441,7 @@ struct net2272 { unsigned protocol_stall:1, softconnect:1, wakeup:1, + added:1, dma_eot_polarity:1, dma_dack_polarity:1, dma_dreq_polarity:1, -- cgit v1.2.3 From 6b7778924c70dad395f6aa843d641e799590f890 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Fri, 21 Aug 2020 10:55:47 +0800 Subject: usb: cdns3: gadget: fix possible memory leak If cdns3_gadget_start is failed, it never frees cdns3_device structure. Meanwhile, there is no release function for gadget device, it causes there is no sync with driver core. To fix this, we add release function for gadget device, and free cdns3_device structure at there. Meanwhile, With the new UDC core APIs, we could work with driver core better to handle memory leak issue. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/gadget.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index d3f079af4a00..26ba41700672 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -2989,6 +2989,14 @@ err: return -ENOMEM; } +static void cdns3_gadget_release(struct device *dev) +{ + struct cdns3_device *priv_dev = container_of(dev, + struct cdns3_device, gadget.dev); + + kfree(priv_dev); +} + void cdns3_gadget_exit(struct cdns3 *cdns) { struct cdns3_device *priv_dev; @@ -2999,7 +3007,7 @@ void cdns3_gadget_exit(struct cdns3 *cdns) pm_runtime_mark_last_busy(cdns->dev); pm_runtime_put_autosuspend(cdns->dev); - usb_del_gadget_udc(&priv_dev->gadget); + usb_del_gadget(&priv_dev->gadget); devm_free_irq(cdns->dev, cdns->dev_irq, priv_dev); cdns3_free_all_eps(priv_dev); @@ -3020,7 +3028,7 @@ void cdns3_gadget_exit(struct cdns3 *cdns) priv_dev->setup_dma); kfree(priv_dev->zlp_buf); - kfree(priv_dev); + usb_put_gadget(&priv_dev->gadget); cdns->gadget_dev = NULL; cdns3_drd_gadget_off(cdns); } @@ -3035,6 +3043,8 @@ static int cdns3_gadget_start(struct cdns3 *cdns) if (!priv_dev) return -ENOMEM; + usb_initialize_gadget(cdns->dev, &priv_dev->gadget, + cdns3_gadget_release); cdns->gadget_dev = priv_dev; priv_dev->sysdev = cdns->dev; priv_dev->dev = cdns->dev; @@ -3122,10 +3132,9 @@ static int cdns3_gadget_start(struct cdns3 *cdns) } /* add USB gadget device */ - ret = usb_add_gadget_udc(priv_dev->dev, &priv_dev->gadget); + ret = usb_add_gadget(&priv_dev->gadget); if (ret < 0) { - dev_err(priv_dev->dev, - "Failed to register USB device controller\n"); + dev_err(priv_dev->dev, "Failed to add gadget\n"); goto err4; } @@ -3138,6 +3147,7 @@ err3: err2: cdns3_free_all_eps(priv_dev); err1: + usb_put_gadget(&priv_dev->gadget); cdns->gadget_dev = NULL; return ret; } -- cgit v1.2.3 From e81a7018d93a7de31a3f121c9a7eecd0a5ec58b0 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Fri, 21 Aug 2020 10:55:48 +0800 Subject: usb: dwc3: allocate gadget structure dynamically The current code uses commit fac323471df6 ("usb: udc: allow adding and removing the same gadget device") as the workaround to let the gadget device is re-used, but it is not allowed from driver core point. In this commit, we allocate gadget structure dynamically, and free it at its release function. Since the gadget device's driver_data has already occupied by usb_composite_dev structure, we have to use gadget device's platform data to store dwc3 structure. Cc: Greg Kroah-Hartman Cc: Alan Stern Reviewed-by: Greg Kroah-Hartman Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 2 +- drivers/usb/dwc3/ep0.c | 26 +++++------ drivers/usb/dwc3/gadget.c | 108 +++++++++++++++++++++++++++------------------- drivers/usb/dwc3/gadget.h | 2 +- 4 files changed, 78 insertions(+), 60 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 60672f149aaf..83b6c871d58d 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1080,7 +1080,7 @@ struct dwc3 { struct dwc3_event_buffer *ev_buf; struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM]; - struct usb_gadget gadget; + struct usb_gadget *gadget; struct usb_gadget_driver *gadget_driver; struct clk_bulk_data *clks; diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 291482b2ef7e..b0363e3ba4d1 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -131,7 +131,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, direction = !dwc->ep0_expect_in; dwc->delayed_status = false; - usb_gadget_set_state(&dwc->gadget, USB_STATE_CONFIGURED); + usb_gadget_set_state(dwc->gadget, USB_STATE_CONFIGURED); if (dwc->ep0state == EP0_STATUS_PHASE) __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]); @@ -325,7 +325,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc, /* * LTM will be set once we know how to set this in HW. */ - usb_status |= dwc->gadget.is_selfpowered; + usb_status |= dwc->gadget->is_selfpowered; if ((dwc->speed == DWC3_DSTS_SUPERSPEED) || (dwc->speed == DWC3_DSTS_SUPERSPEED_PLUS)) { @@ -450,7 +450,7 @@ static int dwc3_ep0_handle_device(struct dwc3 *dwc, wValue = le16_to_cpu(ctrl->wValue); wIndex = le16_to_cpu(ctrl->wIndex); - state = dwc->gadget.state; + state = dwc->gadget->state; switch (wValue) { case USB_DEVICE_REMOTE_WAKEUP: @@ -564,7 +564,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc, static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) { - enum usb_device_state state = dwc->gadget.state; + enum usb_device_state state = dwc->gadget->state; u32 addr; u32 reg; @@ -585,9 +585,9 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) dwc3_writel(dwc->regs, DWC3_DCFG, reg); if (addr) - usb_gadget_set_state(&dwc->gadget, USB_STATE_ADDRESS); + usb_gadget_set_state(dwc->gadget, USB_STATE_ADDRESS); else - usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT); + usb_gadget_set_state(dwc->gadget, USB_STATE_DEFAULT); return 0; } @@ -597,14 +597,14 @@ static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) int ret; spin_unlock(&dwc->lock); - ret = dwc->gadget_driver->setup(&dwc->gadget, ctrl); + ret = dwc->gadget_driver->setup(dwc->gadget, ctrl); spin_lock(&dwc->lock); return ret; } static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) { - enum usb_device_state state = dwc->gadget.state; + enum usb_device_state state = dwc->gadget->state; u32 cfg; int ret; u32 reg; @@ -627,7 +627,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) * to change the state on the next usb_ep_queue() */ if (ret == 0) - usb_gadget_set_state(&dwc->gadget, + usb_gadget_set_state(dwc->gadget, USB_STATE_CONFIGURED); /* @@ -646,7 +646,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) case USB_STATE_CONFIGURED: ret = dwc3_ep0_delegate_req(dwc, ctrl); if (!cfg && !ret) - usb_gadget_set_state(&dwc->gadget, + usb_gadget_set_state(dwc->gadget, USB_STATE_ADDRESS); break; default: @@ -702,7 +702,7 @@ static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req) static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) { struct dwc3_ep *dep; - enum usb_device_state state = dwc->gadget.state; + enum usb_device_state state = dwc->gadget->state; u16 wLength; if (state == USB_STATE_DEFAULT) @@ -746,7 +746,7 @@ static int dwc3_ep0_set_isoch_delay(struct dwc3 *dwc, struct usb_ctrlrequest *ct if (wIndex || wLength) return -EINVAL; - dwc->gadget.isoch_delay = wValue; + dwc->gadget->isoch_delay = wValue; return 0; } @@ -1118,7 +1118,7 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, */ if (!list_empty(&dep->pending_list)) { dwc->delayed_status = false; - usb_gadget_set_state(&dwc->gadget, + usb_gadget_set_state(dwc->gadget, USB_STATE_CONFIGURED); dwc3_ep0_do_control_status(dwc, event); } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 08111b97df04..4c7b6cceeafd 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -291,7 +291,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd, * * DWC_usb3 3.30a and DWC_usb31 1.90a programming guide section 3.2.2 */ - if (dwc->gadget.speed <= USB_SPEED_HIGH) { + if (dwc->gadget->speed <= USB_SPEED_HIGH) { reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) { saved_config |= DWC3_GUSB2PHYCFG_SUSPHY; @@ -423,7 +423,7 @@ static int dwc3_send_clear_stall_ep_cmd(struct dwc3_ep *dep) */ if (dep->direction && !DWC3_VER_IS_PRIOR(DWC3, 260A) && - (dwc->gadget.speed >= USB_SPEED_SUPER)) + (dwc->gadget->speed >= USB_SPEED_SUPER)) cmd |= DWC3_DEPCMD_CLEARPENDIN; memset(¶ms, 0, sizeof(params)); @@ -563,7 +563,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action) | DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc)); /* Burst size is only needed in SuperSpeed mode */ - if (dwc->gadget.speed >= USB_SPEED_SUPER) { + if (dwc->gadget->speed >= USB_SPEED_SUPER) { u32 burst = dep->endpoint.maxburst; params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst - 1); @@ -950,7 +950,7 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb, unsigned int is_last) { struct dwc3 *dwc = dep->dwc; - struct usb_gadget *gadget = &dwc->gadget; + struct usb_gadget *gadget = dwc->gadget; enum usb_device_speed speed = gadget->speed; trb->size = DWC3_TRB_SIZE_LENGTH(length); @@ -1542,12 +1542,12 @@ static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep) if (!dwc->dis_start_transfer_quirk && (DWC3_VER_IS_PRIOR(DWC31, 170A) || DWC3_VER_TYPE_IS_WITHIN(DWC31, 170A, EA01, EA06))) { - if (dwc->gadget.speed <= USB_SPEED_HIGH && dep->direction) + if (dwc->gadget->speed <= USB_SPEED_HIGH && dep->direction) return dwc3_gadget_start_isoc_quirk(dep); } if (desc->bInterval <= 14 && - dwc->gadget.speed >= USB_SPEED_HIGH) { + dwc->gadget->speed >= USB_SPEED_HIGH) { u32 frame = __dwc3_gadget_get_frame(dwc); bool rollover = frame < (dep->frame_number & DWC3_FRNUMBER_MASK); @@ -2256,7 +2256,7 @@ static int dwc3_gadget_start(struct usb_gadget *g, spin_lock_irqsave(&dwc->lock, flags); if (dwc->gadget_driver) { dev_err(dwc->dev, "%s is already bound to %s\n", - dwc->gadget.name, + dwc->gadget->name, dwc->gadget_driver->driver.name); ret = -EBUSY; goto err1; @@ -2428,7 +2428,7 @@ static int dwc3_gadget_init_control_endpoint(struct dwc3_ep *dep) dep->endpoint.maxburst = 1; dep->endpoint.ops = &dwc3_gadget_ep0_ops; if (!dep->direction) - dwc->gadget.ep0 = &dep->endpoint; + dwc->gadget->ep0 = &dep->endpoint; dep->endpoint.caps.type_control = true; @@ -2474,7 +2474,7 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep) dep->endpoint.max_streams = 15; dep->endpoint.ops = &dwc3_gadget_ep_ops; list_add_tail(&dep->endpoint.ep_list, - &dwc->gadget.ep_list); + &dwc->gadget->ep_list); dep->endpoint.caps.type_iso = true; dep->endpoint.caps.type_bulk = true; dep->endpoint.caps.type_int = true; @@ -2523,7 +2523,7 @@ static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep) dep->endpoint.max_streams = 15; dep->endpoint.ops = &dwc3_gadget_ep_ops; list_add_tail(&dep->endpoint.ep_list, - &dwc->gadget.ep_list); + &dwc->gadget->ep_list); dep->endpoint.caps.type_iso = true; dep->endpoint.caps.type_bulk = true; dep->endpoint.caps.type_int = true; @@ -2584,7 +2584,7 @@ static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total) { u8 epnum; - INIT_LIST_HEAD(&dwc->gadget.ep_list); + INIT_LIST_HEAD(&dwc->gadget->ep_list); for (epnum = 0; epnum < total; epnum++) { int ret; @@ -3051,7 +3051,7 @@ static void dwc3_disconnect_gadget(struct dwc3 *dwc) { if (dwc->gadget_driver && dwc->gadget_driver->disconnect) { spin_unlock(&dwc->lock); - dwc->gadget_driver->disconnect(&dwc->gadget); + dwc->gadget_driver->disconnect(dwc->gadget); spin_lock(&dwc->lock); } } @@ -3060,7 +3060,7 @@ static void dwc3_suspend_gadget(struct dwc3 *dwc) { if (dwc->gadget_driver && dwc->gadget_driver->suspend) { spin_unlock(&dwc->lock); - dwc->gadget_driver->suspend(&dwc->gadget); + dwc->gadget_driver->suspend(dwc->gadget); spin_lock(&dwc->lock); } } @@ -3069,7 +3069,7 @@ static void dwc3_resume_gadget(struct dwc3 *dwc) { if (dwc->gadget_driver && dwc->gadget_driver->resume) { spin_unlock(&dwc->lock); - dwc->gadget_driver->resume(&dwc->gadget); + dwc->gadget_driver->resume(dwc->gadget); spin_lock(&dwc->lock); } } @@ -3079,9 +3079,9 @@ static void dwc3_reset_gadget(struct dwc3 *dwc) if (!dwc->gadget_driver) return; - if (dwc->gadget.speed != USB_SPEED_UNKNOWN) { + if (dwc->gadget->speed != USB_SPEED_UNKNOWN) { spin_unlock(&dwc->lock); - usb_gadget_udc_reset(&dwc->gadget, dwc->gadget_driver); + usb_gadget_udc_reset(dwc->gadget, dwc->gadget_driver); spin_lock(&dwc->lock); } } @@ -3182,9 +3182,9 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) dwc3_disconnect_gadget(dwc); - dwc->gadget.speed = USB_SPEED_UNKNOWN; + dwc->gadget->speed = USB_SPEED_UNKNOWN; dwc->setup_packet_pending = false; - usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED); + usb_gadget_set_state(dwc->gadget, USB_STATE_NOTATTACHED); dwc->connected = false; } @@ -3263,8 +3263,8 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) switch (speed) { case DWC3_DSTS_SUPERSPEED_PLUS: dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); - dwc->gadget.ep0->maxpacket = 512; - dwc->gadget.speed = USB_SPEED_SUPER_PLUS; + dwc->gadget->ep0->maxpacket = 512; + dwc->gadget->speed = USB_SPEED_SUPER_PLUS; break; case DWC3_DSTS_SUPERSPEED: /* @@ -3284,27 +3284,27 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) dwc3_gadget_reset_interrupt(dwc); dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); - dwc->gadget.ep0->maxpacket = 512; - dwc->gadget.speed = USB_SPEED_SUPER; + dwc->gadget->ep0->maxpacket = 512; + dwc->gadget->speed = USB_SPEED_SUPER; break; case DWC3_DSTS_HIGHSPEED: dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64); - dwc->gadget.ep0->maxpacket = 64; - dwc->gadget.speed = USB_SPEED_HIGH; + dwc->gadget->ep0->maxpacket = 64; + dwc->gadget->speed = USB_SPEED_HIGH; break; case DWC3_DSTS_FULLSPEED: dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64); - dwc->gadget.ep0->maxpacket = 64; - dwc->gadget.speed = USB_SPEED_FULL; + dwc->gadget->ep0->maxpacket = 64; + dwc->gadget->speed = USB_SPEED_FULL; break; case DWC3_DSTS_LOWSPEED: dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8); - dwc->gadget.ep0->maxpacket = 8; - dwc->gadget.speed = USB_SPEED_LOW; + dwc->gadget->ep0->maxpacket = 8; + dwc->gadget->speed = USB_SPEED_LOW; break; } - dwc->eps[1]->endpoint.maxpacket = dwc->gadget.ep0->maxpacket; + dwc->eps[1]->endpoint.maxpacket = dwc->gadget->ep0->maxpacket; /* Enable USB2 LPM Capability */ @@ -3372,7 +3372,7 @@ static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc) if (dwc->gadget_driver && dwc->gadget_driver->resume) { spin_unlock(&dwc->lock); - dwc->gadget_driver->resume(&dwc->gadget); + dwc->gadget_driver->resume(dwc->gadget); spin_lock(&dwc->lock); } } @@ -3543,7 +3543,7 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc, * Ignore suspend event until the gadget enters into * USB_STATE_CONFIGURED state. */ - if (dwc->gadget.state >= USB_STATE_CONFIGURED) + if (dwc->gadget->state >= USB_STATE_CONFIGURED) dwc3_gadget_suspend_interrupt(dwc, event->event_info); } @@ -3718,6 +3718,13 @@ out: return irq; } +static void dwc_gadget_release(struct device *dev) +{ + struct usb_gadget *gadget = container_of(dev, struct usb_gadget, dev); + + kfree(gadget); +} + /** * dwc3_gadget_init - initializes gadget related registers * @dwc: pointer to our controller context structure @@ -3728,6 +3735,7 @@ int dwc3_gadget_init(struct dwc3 *dwc) { int ret; int irq; + struct device *dev; irq = dwc3_gadget_get_irq(dwc); if (irq < 0) { @@ -3760,12 +3768,21 @@ int dwc3_gadget_init(struct dwc3 *dwc) } init_completion(&dwc->ep0_in_setup); + dwc->gadget = kzalloc(sizeof(struct usb_gadget), GFP_KERNEL); + if (!dwc->gadget) { + ret = -ENOMEM; + goto err3; + } - dwc->gadget.ops = &dwc3_gadget_ops; - dwc->gadget.speed = USB_SPEED_UNKNOWN; - dwc->gadget.sg_supported = true; - dwc->gadget.name = "dwc3-gadget"; - dwc->gadget.lpm_capable = true; + + usb_initialize_gadget(dwc->dev, dwc->gadget, dwc_gadget_release); + dev = &dwc->gadget->dev; + dev->platform_data = dwc; + dwc->gadget->ops = &dwc3_gadget_ops; + dwc->gadget->speed = USB_SPEED_UNKNOWN; + dwc->gadget->sg_supported = true; + dwc->gadget->name = "dwc3-gadget"; + dwc->gadget->lpm_capable = true; /* * FIXME We might be setting max_speed to dev, "changing max_speed on rev %08x\n", dwc->revision); - dwc->gadget.max_speed = dwc->maximum_speed; + dwc->gadget->max_speed = dwc->maximum_speed; /* * REVISIT: Here we should clear all pending IRQs to be @@ -3797,21 +3814,22 @@ int dwc3_gadget_init(struct dwc3 *dwc) ret = dwc3_gadget_init_endpoints(dwc, dwc->num_eps); if (ret) - goto err3; + goto err4; - ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget); + ret = usb_add_gadget(dwc->gadget); if (ret) { - dev_err(dwc->dev, "failed to register udc\n"); - goto err4; + dev_err(dwc->dev, "failed to add gadget\n"); + goto err5; } - dwc3_gadget_set_speed(&dwc->gadget, dwc->maximum_speed); + dwc3_gadget_set_speed(dwc->gadget, dwc->maximum_speed); return 0; -err4: +err5: dwc3_gadget_free_endpoints(dwc); - +err4: + usb_put_gadget(dwc->gadget); err3: dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce, dwc->bounce_addr); @@ -3831,7 +3849,7 @@ err0: void dwc3_gadget_exit(struct dwc3 *dwc) { - usb_del_gadget_udc(&dwc->gadget); + usb_del_gadget_udc(dwc->gadget); dwc3_gadget_free_endpoints(dwc); dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce, dwc->bounce_addr); diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index a7791cb827c4..0cd281949970 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -17,7 +17,7 @@ struct dwc3; #define to_dwc3_ep(ep) (container_of(ep, struct dwc3_ep, endpoint)) -#define gadget_to_dwc(g) (container_of(g, struct dwc3, gadget)) +#define gadget_to_dwc(g) (dev_get_platdata(&g->dev)) /* DEPCFG parameter 1 */ #define DWC3_DEPCFG_INT_NUM(n) (((n) & 0x1f) << 0) -- cgit v1.2.3 From 7595c38bb1a64b3103ded1877bbf519e7dad4b1d Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Fri, 21 Aug 2020 10:55:49 +0800 Subject: Revert "usb: udc: allow adding and removing the same gadget device" We have already allocated gadget structure dynamically at UDC (dwc3) driver, so commit fac323471df6 ("usb: udc: allow adding and removing the same gadget device")could be reverted. Cc: Greg Kroah-Hartman Cc: Alan Stern Reviewed-by: Greg Kroah-Hartman Reviewed-by: Alan Stern Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/core.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index 2b6770d9fb3f..bf1b0efcfaac 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -1389,7 +1389,6 @@ void usb_del_gadget_udc(struct usb_gadget *gadget) { usb_del_gadget(gadget); usb_put_gadget(gadget); - memset(&gadget->dev, 0x00, sizeof(gadget->dev)); } EXPORT_SYMBOL_GPL(usb_del_gadget_udc); -- cgit v1.2.3 From 266d0493900ac5d6a21cdbe6b1624ed2da94d47a Mon Sep 17 00:00:00 2001 From: Li Jun Date: Tue, 28 Jul 2020 20:42:40 +0800 Subject: usb: dwc3: core: don't trigger runtime pm when remove driver No need to trigger runtime pm in driver removal, otherwise if user disable auto suspend via sys file, runtime suspend may be entered, which will call dwc3_core_exit() again and there will be clock disable not balance warning: [ 2026.820154] xhci-hcd xhci-hcd.0.auto: remove, state 4 [ 2026.825268] usb usb2: USB disconnect, device number 1 [ 2026.831017] xhci-hcd xhci-hcd.0.auto: USB bus 2 deregistered [ 2026.836806] xhci-hcd xhci-hcd.0.auto: remove, state 4 [ 2026.842029] usb usb1: USB disconnect, device number 1 [ 2026.848029] xhci-hcd xhci-hcd.0.auto: USB bus 1 deregistered [ 2026.865889] ------------[ cut here ]------------ [ 2026.870506] usb2_ctrl_root_clk already disabled [ 2026.875082] WARNING: CPU: 0 PID: 731 at drivers/clk/clk.c:958 clk_core_disable+0xa0/0xa8 [ 2026.883170] Modules linked in: dwc3(-) phy_fsl_imx8mq_usb [last unloaded: dwc3] [ 2026.890488] CPU: 0 PID: 731 Comm: rmmod Not tainted 5.8.0-rc7-00280-g9d08cca-dirty #245 [ 2026.898489] Hardware name: NXP i.MX8MQ EVK (DT) [ 2026.903020] pstate: 20000085 (nzCv daIf -PAN -UAO BTYPE=--) [ 2026.908594] pc : clk_core_disable+0xa0/0xa8 [ 2026.912777] lr : clk_core_disable+0xa0/0xa8 [ 2026.916958] sp : ffff8000121b39a0 [ 2026.920271] x29: ffff8000121b39a0 x28: ffff0000b11f3700 [ 2026.925583] x27: 0000000000000000 x26: ffff0000b539c700 [ 2026.930895] x25: 000001d7e44e1232 x24: ffff0000b76fa800 [ 2026.936208] x23: ffff0000b76fa6f8 x22: ffff800008d01040 [ 2026.941520] x21: ffff0000b539ce00 x20: ffff0000b7105000 [ 2026.946832] x19: ffff0000b7105000 x18: 0000000000000010 [ 2026.952144] x17: 0000000000000001 x16: 0000000000000000 [ 2026.957456] x15: ffff0000b11f3b70 x14: ffffffffffffffff [ 2026.962768] x13: ffff8000921b36f7 x12: ffff8000121b36ff [ 2026.968080] x11: ffff8000119e1000 x10: ffff800011bf26d0 [ 2026.973392] x9 : 0000000000000000 x8 : ffff800011bf3000 [ 2026.978704] x7 : ffff800010695d68 x6 : 0000000000000252 [ 2026.984016] x5 : ffff0000bb9881f0 x4 : 0000000000000000 [ 2026.989327] x3 : 0000000000000027 x2 : 0000000000000023 [ 2026.994639] x1 : ac2fa471aa7cab00 x0 : 0000000000000000 [ 2026.999951] Call trace: [ 2027.002401] clk_core_disable+0xa0/0xa8 [ 2027.006238] clk_core_disable_lock+0x20/0x38 [ 2027.010508] clk_disable+0x1c/0x28 [ 2027.013911] clk_bulk_disable+0x34/0x50 [ 2027.017758] dwc3_core_exit+0xec/0x110 [dwc3] [ 2027.022122] dwc3_suspend_common+0x84/0x188 [dwc3] [ 2027.026919] dwc3_runtime_suspend+0x74/0x9c [dwc3] [ 2027.031712] pm_generic_runtime_suspend+0x28/0x40 [ 2027.036419] genpd_runtime_suspend+0xa0/0x258 [ 2027.040777] __rpm_callback+0x88/0x140 [ 2027.044526] rpm_callback+0x20/0x80 [ 2027.048015] rpm_suspend+0xd0/0x418 [ 2027.051503] __pm_runtime_suspend+0x58/0xa0 [ 2027.055693] dwc3_runtime_idle+0x7c/0x90 [dwc3] [ 2027.060224] __rpm_callback+0x88/0x140 [ 2027.063973] rpm_idle+0x78/0x150 [ 2027.067201] __pm_runtime_idle+0x58/0xa0 [ 2027.071130] dwc3_remove+0x64/0xc0 [dwc3] [ 2027.075140] platform_drv_remove+0x28/0x48 [ 2027.079239] device_release_driver_internal+0xf4/0x1c0 [ 2027.084377] driver_detach+0x4c/0xd8 [ 2027.087954] bus_remove_driver+0x54/0xa8 [ 2027.091877] driver_unregister+0x2c/0x58 [ 2027.095799] platform_driver_unregister+0x10/0x18 [ 2027.100509] dwc3_driver_exit+0x14/0x1408 [dwc3] [ 2027.105129] __arm64_sys_delete_module+0x178/0x218 [ 2027.109922] el0_svc_common.constprop.0+0x68/0x160 [ 2027.114714] do_el0_svc+0x20/0x80 [ 2027.118031] el0_sync_handler+0x88/0x190 [ 2027.121953] el0_sync+0x140/0x180 [ 2027.125267] ---[ end trace 027f4f8189958f1f ]--- [ 2027.129976] ------------[ cut here ]------------ Fixes: fc8bb91bc83e ("usb: dwc3: implement runtime PM") Cc: Signed-off-by: Li Jun Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 9b49a6ce0132..24fba4ca3d12 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1599,9 +1599,9 @@ static int dwc3_remove(struct platform_device *pdev) dwc3_core_exit(dwc); dwc3_ulpi_exit(dwc); - pm_runtime_put_sync(&pdev->dev); - pm_runtime_allow(&pdev->dev); pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); dwc3_free_event_buffers(dwc); dwc3_free_scratch_buffers(dwc); -- cgit v1.2.3 From 03c1fd622f72c7624c81b64fdba4a567ae5ee9cb Mon Sep 17 00:00:00 2001 From: Li Jun Date: Tue, 28 Jul 2020 20:42:41 +0800 Subject: usb: dwc3: core: add phy cleanup for probe error handling Add the phy cleanup if dwc3 mode init fail, which is the missing part of de-init for dwc3 core init. Fixes: c499ff71ff2a ("usb: dwc3: core: re-factor init and exit paths") Cc: Signed-off-by: Li Jun Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 24fba4ca3d12..385262f6747d 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1564,6 +1564,17 @@ static int dwc3_probe(struct platform_device *pdev) err5: dwc3_event_buffers_cleanup(dwc); + + usb_phy_shutdown(dwc->usb2_phy); + usb_phy_shutdown(dwc->usb3_phy); + phy_exit(dwc->usb2_generic_phy); + phy_exit(dwc->usb3_generic_phy); + + usb_phy_set_suspend(dwc->usb2_phy, 1); + usb_phy_set_suspend(dwc->usb3_phy, 1); + phy_power_off(dwc->usb2_generic_phy); + phy_power_off(dwc->usb3_generic_phy); + dwc3_ulpi_exit(dwc); err4: -- cgit v1.2.3 From 5bde3f020a150aa16f396ce22b6a778627dd4484 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Wed, 22 Jul 2020 17:02:55 +0800 Subject: usb: dwc3: debugfs: do not queue work if try to change mode on non-drd Do not try to queue a drd work for change mode if the port is not a drd, this is to avoid below kernel dump: [ 60.115529] ------------[ cut here ]------------ [ 60.120166] WARNING: CPU: 1 PID: 627 at kernel/workqueue.c:1473 __queue_work+0x46c/0x520 [ 60.128254] Modules linked in: [ 60.131313] CPU: 1 PID: 627 Comm: sh Not tainted 5.7.0-rc4-00022-g914a586-dirty #135 [ 60.139054] Hardware name: NXP i.MX8MQ EVK (DT) [ 60.143585] pstate: a0000085 (NzCv daIf -PAN -UAO) [ 60.148376] pc : __queue_work+0x46c/0x520 [ 60.152385] lr : __queue_work+0x314/0x520 [ 60.156393] sp : ffff8000124ebc40 [ 60.159705] x29: ffff8000124ebc40 x28: ffff800011808018 [ 60.165018] x27: ffff800011819ef8 x26: ffff800011d39980 [ 60.170331] x25: ffff800011808018 x24: 0000000000000100 [ 60.175643] x23: 0000000000000013 x22: 0000000000000001 [ 60.180955] x21: ffff0000b7c08e00 x20: ffff0000b6c31080 [ 60.186267] x19: ffff0000bb99bc00 x18: 0000000000000000 [ 60.191579] x17: 0000000000000000 x16: 0000000000000000 [ 60.196891] x15: 0000000000000000 x14: 0000000000000000 [ 60.202202] x13: 0000000000000000 x12: 0000000000000000 [ 60.207515] x11: 0000000000000000 x10: 0000000000000040 [ 60.212827] x9 : ffff800011d55460 x8 : ffff800011d55458 [ 60.218138] x7 : ffff0000b7800028 x6 : 0000000000000000 [ 60.223450] x5 : ffff0000b7800000 x4 : 0000000000000000 [ 60.228762] x3 : ffff0000bb997cc0 x2 : 0000000000000001 [ 60.234074] x1 : 0000000000000000 x0 : ffff0000b6c31088 [ 60.239386] Call trace: [ 60.241834] __queue_work+0x46c/0x520 [ 60.245496] queue_work_on+0x6c/0x90 [ 60.249075] dwc3_set_mode+0x48/0x58 [ 60.252651] dwc3_mode_write+0xf8/0x150 [ 60.256489] full_proxy_write+0x5c/0xa8 [ 60.260327] __vfs_write+0x18/0x40 [ 60.263729] vfs_write+0xdc/0x1c8 [ 60.267045] ksys_write+0x68/0xf0 [ 60.270360] __arm64_sys_write+0x18/0x20 [ 60.274286] el0_svc_common.constprop.0+0x68/0x160 [ 60.279077] do_el0_svc+0x20/0x80 [ 60.282394] el0_sync_handler+0x10c/0x178 [ 60.286403] el0_sync+0x140/0x180 [ 60.289716] ---[ end trace 70b155582e2b7988 ]--- Signed-off-by: Li Jun Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/debugfs.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index 608639a0dea7..5da4f6082d93 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -428,6 +428,9 @@ static ssize_t dwc3_mode_write(struct file *file, if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) return -EFAULT; + if (dwc->dr_mode != USB_DR_MODE_OTG) + return count; + if (!strncmp(buf, "host", 4)) mode = DWC3_GCTL_PRTCAP_HOST; -- cgit v1.2.3 From de56298f78e449e9490a8621d3bd80a04ad40e6d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 31 Jul 2020 09:41:22 +0200 Subject: usb: gadget: s3c: Remove unused 'udc' variable Remove unused 'udc' variable to fix compile warnings: drivers/usb/gadget/udc/s3c2410_udc.c: In function 's3c2410_udc_dequeue': drivers/usb/gadget/udc/s3c2410_udc.c:1268:22: warning: variable 'udc' set but not used [-Wunused-but-set-variable] Reviewed-by: Nathan Chancellor Signed-off-by: Krzysztof Kozlowski Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/s3c2410_udc.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c index bc2e8eb737c3..e875a0b967c0 100644 --- a/drivers/usb/gadget/udc/s3c2410_udc.c +++ b/drivers/usb/gadget/udc/s3c2410_udc.c @@ -1270,7 +1270,6 @@ static int s3c2410_udc_queue(struct usb_ep *_ep, struct usb_request *_req, static int s3c2410_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req) { struct s3c2410_ep *ep = to_s3c2410_ep(_ep); - struct s3c2410_udc *udc; int retval = -EINVAL; unsigned long flags; struct s3c2410_request *req = NULL; @@ -1283,8 +1282,6 @@ static int s3c2410_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req) if (!_ep || !_req) return retval; - udc = to_s3c2410_udc(ep->gadget); - local_irq_save(flags); list_for_each_entry(req, &ep->queue, queue) { -- cgit v1.2.3 From 8266b08ed90cd9091dd8e3f37a740a409454e454 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Thu, 30 Jul 2020 16:29:03 -0700 Subject: usb: dwc3: gadget: Refactor ep command completion Refactor END_TRANSFER command completion handling and move it outside of the switch statement to its own function. This makes it cleaner and consistent with other event handler functions. No functional change here. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 71 +++++++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 33 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 4c7b6cceeafd..b2dfb3ada700 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2908,6 +2908,43 @@ static void dwc3_gadget_endpoint_transfer_not_ready(struct dwc3_ep *dep, (void) __dwc3_gadget_start_isoc(dep); } +static void dwc3_gadget_endpoint_command_complete(struct dwc3_ep *dep, + const struct dwc3_event_depevt *event) +{ + u8 cmd = DEPEVT_PARAMETER_CMD(event->parameters); + + if (cmd != DWC3_DEPCMD_ENDTRANSFER) + return; + + dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING; + dep->flags &= ~DWC3_EP_TRANSFER_STARTED; + dwc3_gadget_ep_cleanup_cancelled_requests(dep); + + if (dep->flags & DWC3_EP_PENDING_CLEAR_STALL) { + struct dwc3 *dwc = dep->dwc; + + dep->flags &= ~DWC3_EP_PENDING_CLEAR_STALL; + if (dwc3_send_clear_stall_ep_cmd(dep)) { + struct usb_ep *ep0 = &dwc->eps[0]->endpoint; + + dev_err(dwc->dev, "failed to clear STALL on %s\n", dep->name); + if (dwc->delayed_status) + __dwc3_gadget_ep0_set_halt(ep0, 1); + return; + } + + dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE); + if (dwc->delayed_status) + dwc3_ep0_send_delayed_status(dwc); + } + + if ((dep->flags & DWC3_EP_DELAY_START) && + !usb_endpoint_xfer_isoc(dep->endpoint.desc)) + __dwc3_gadget_kick_transfer(dep); + + dep->flags &= ~DWC3_EP_DELAY_START; +} + static void dwc3_gadget_endpoint_stream_event(struct dwc3_ep *dep, const struct dwc3_event_depevt *event) { @@ -2977,7 +3014,6 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, { struct dwc3_ep *dep; u8 epnum = event->endpoint_number; - u8 cmd; dep = dwc->eps[epnum]; @@ -3003,38 +3039,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, dwc3_gadget_endpoint_transfer_not_ready(dep, event); break; case DWC3_DEPEVT_EPCMDCMPLT: - cmd = DEPEVT_PARAMETER_CMD(event->parameters); - - if (cmd == DWC3_DEPCMD_ENDTRANSFER) { - dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING; - dep->flags &= ~DWC3_EP_TRANSFER_STARTED; - dwc3_gadget_ep_cleanup_cancelled_requests(dep); - - if (dep->flags & DWC3_EP_PENDING_CLEAR_STALL) { - struct dwc3 *dwc = dep->dwc; - - dep->flags &= ~DWC3_EP_PENDING_CLEAR_STALL; - if (dwc3_send_clear_stall_ep_cmd(dep)) { - struct usb_ep *ep0 = &dwc->eps[0]->endpoint; - - dev_err(dwc->dev, "failed to clear STALL on %s\n", - dep->name); - if (dwc->delayed_status) - __dwc3_gadget_ep0_set_halt(ep0, 1); - return; - } - - dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE); - if (dwc->delayed_status) - dwc3_ep0_send_delayed_status(dwc); - } - - if ((dep->flags & DWC3_EP_DELAY_START) && - !usb_endpoint_xfer_isoc(dep->endpoint.desc)) - __dwc3_gadget_kick_transfer(dep); - - dep->flags &= ~DWC3_EP_DELAY_START; - } + dwc3_gadget_endpoint_command_complete(dep, event); break; case DWC3_DEPEVT_XFERCOMPLETE: dwc3_gadget_endpoint_transfer_complete(dep, event); -- cgit v1.2.3 From 5a1da544e572f58986d7bee03d31c91d1f87f214 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Tue, 11 Aug 2020 10:00:26 +0800 Subject: usb: gadget: core: do not try to disconnect gadget if it is not connected Current UDC core connects gadget during the loading gadget flow (udc_bind_to_driver->usb_udc_connect_control), but for platforms which do not connect gadget if the VBUS is not there, they call usb_gadget_disconnect, but the gadget is not connected at this time, notify disconnecton for the gadget driver is meaningless at this situation. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/core.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index bf1b0efcfaac..debf54205d22 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -715,6 +715,9 @@ int usb_gadget_disconnect(struct usb_gadget *gadget) goto out; } + if (!gadget->connected) + goto out; + if (gadget->deactivated) { /* * If gadget is deactivated we only save new state. -- cgit v1.2.3 From 6c2a754a12ba9255cf34fac435fb0c448dce9a95 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 9 Aug 2020 09:29:48 +0200 Subject: usb: gadget: tegra-xudc: Avoid GFP_ATOMIC where it is not needed There is no need to use GFP_ATOMIC here. It is a probe function, no spinlock is taken. Reviewed-by: JC Kuo Acked-by: Thierry Reding Signed-off-by: Christophe JAILLET Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/tegra-xudc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c index d6ff68c06911..9aa4815c1c59 100644 --- a/drivers/usb/gadget/udc/tegra-xudc.c +++ b/drivers/usb/gadget/udc/tegra-xudc.c @@ -3733,7 +3733,7 @@ static int tegra_xudc_probe(struct platform_device *pdev) unsigned int i; int err; - xudc = devm_kzalloc(&pdev->dev, sizeof(*xudc), GFP_ATOMIC); + xudc = devm_kzalloc(&pdev->dev, sizeof(*xudc), GFP_KERNEL); if (!xudc) return -ENOMEM; -- cgit v1.2.3 From de21e7289b7abfe4b0a79a8a8c9d3429fd1d7263 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 6 Aug 2020 18:04:15 +0200 Subject: usb: gadget: tegra-xudc: Use consistent spelling and formatting Make sure to use consistent spelling and formatting in error messages. Signed-off-by: Thierry Reding Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/tegra-xudc.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c index 9aa4815c1c59..a59d6a9e8ef7 100644 --- a/drivers/usb/gadget/udc/tegra-xudc.c +++ b/drivers/usb/gadget/udc/tegra-xudc.c @@ -705,11 +705,11 @@ static void tegra_xudc_device_mode_on(struct tegra_xudc *xudc) err = phy_power_on(xudc->curr_utmi_phy); if (err < 0) - dev_err(xudc->dev, "utmi power on failed %d\n", err); + dev_err(xudc->dev, "UTMI power on failed: %d\n", err); err = phy_power_on(xudc->curr_usb3_phy); if (err < 0) - dev_err(xudc->dev, "usb3 phy power on failed %d\n", err); + dev_err(xudc->dev, "USB3 PHY power on failed: %d\n", err); dev_dbg(xudc->dev, "device mode on\n"); @@ -759,11 +759,11 @@ static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc) err = phy_power_off(xudc->curr_utmi_phy); if (err < 0) - dev_err(xudc->dev, "utmi_phy power off failed %d\n", err); + dev_err(xudc->dev, "UTMI PHY power off failed: %d\n", err); err = phy_power_off(xudc->curr_usb3_phy); if (err < 0) - dev_err(xudc->dev, "usb3_phy power off failed %d\n", err); + dev_err(xudc->dev, "USB3 PHY power off failed: %d\n", err); pm_runtime_put(xudc->dev); } @@ -1539,7 +1539,7 @@ static int __tegra_xudc_ep_set_halt(struct tegra_xudc_ep *ep, bool halt) return -EINVAL; if (usb_endpoint_xfer_isoc(ep->desc)) { - dev_err(xudc->dev, "can't halt isoc EP\n"); + dev_err(xudc->dev, "can't halt isochronous EP\n"); return -ENOTSUPP; } @@ -1788,7 +1788,7 @@ static int __tegra_xudc_ep_enable(struct tegra_xudc_ep *ep, if (usb_endpoint_xfer_isoc(desc)) { if (xudc->nr_isoch_eps > XUDC_MAX_ISOCH_EPS) { - dev_err(xudc->dev, "too many isoch endpoints\n"); + dev_err(xudc->dev, "too many isochronous endpoints\n"); return -EBUSY; } xudc->nr_isoch_eps++; @@ -3509,7 +3509,7 @@ static int tegra_xudc_phy_get(struct tegra_xudc *xudc) if (IS_ERR(xudc->utmi_phy[i])) { err = PTR_ERR(xudc->utmi_phy[i]); if (err != -EPROBE_DEFER) - dev_err(xudc->dev, "failed to get usb2-%d phy: %d\n", + dev_err(xudc->dev, "failed to get usb2-%d PHY: %d\n", i, err); goto clean_up; @@ -3539,12 +3539,12 @@ static int tegra_xudc_phy_get(struct tegra_xudc *xudc) if (IS_ERR(xudc->usb3_phy[i])) { err = PTR_ERR(xudc->usb3_phy[i]); if (err != -EPROBE_DEFER) - dev_err(xudc->dev, "failed to get usb3-%d phy: %d\n", + dev_err(xudc->dev, "failed to get usb3-%d PHY: %d\n", usb3, err); goto clean_up; } else if (xudc->usb3_phy[i]) - dev_dbg(xudc->dev, "usb3_phy-%d registered", usb3); + dev_dbg(xudc->dev, "usb3-%d PHY registered", usb3); } return err; @@ -3577,13 +3577,13 @@ static int tegra_xudc_phy_init(struct tegra_xudc *xudc) for (i = 0; i < xudc->soc->num_phys; i++) { err = phy_init(xudc->utmi_phy[i]); if (err < 0) { - dev_err(xudc->dev, "utmi phy init failed: %d\n", err); + dev_err(xudc->dev, "UTMI PHY #%u initialization failed: %d\n", i, err); goto exit_phy; } err = phy_init(xudc->usb3_phy[i]); if (err < 0) { - dev_err(xudc->dev, "usb3 phy init failed: %d\n", err); + dev_err(xudc->dev, "USB3 PHY #%u initialization failed: %d\n", i, err); goto exit_phy; } } @@ -3696,14 +3696,14 @@ static int tegra_xudc_powerdomain_init(struct tegra_xudc *xudc) "dev"); if (IS_ERR(xudc->genpd_dev_device)) { err = PTR_ERR(xudc->genpd_dev_device); - dev_err(dev, "failed to get dev pm-domain: %d\n", err); + dev_err(dev, "failed to get device power domain: %d\n", err); return err; } xudc->genpd_dev_ss = dev_pm_domain_attach_by_name(dev, "ss"); if (IS_ERR(xudc->genpd_dev_ss)) { err = PTR_ERR(xudc->genpd_dev_ss); - dev_err(dev, "failed to get superspeed pm-domain: %d\n", err); + dev_err(dev, "failed to get SuperSpeed power domain: %d\n", err); return err; } @@ -3711,7 +3711,7 @@ static int tegra_xudc_powerdomain_init(struct tegra_xudc *xudc) DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS); if (!xudc->genpd_dl_device) { - dev_err(dev, "adding usb device device link failed!\n"); + dev_err(dev, "failed to add USB device link\n"); return -ENODEV; } @@ -3719,7 +3719,7 @@ static int tegra_xudc_powerdomain_init(struct tegra_xudc *xudc) DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS); if (!xudc->genpd_dl_ss) { - dev_err(dev, "adding superspeed device link failed!\n"); + dev_err(dev, "failed to add SuperSpeed device link\n"); return -ENODEV; } @@ -3783,7 +3783,7 @@ static int tegra_xudc_probe(struct platform_device *pdev) err = devm_clk_bulk_get(&pdev->dev, xudc->soc->num_clks, xudc->clks); if (err) { - dev_err(xudc->dev, "failed to request clks %d\n", err); + dev_err(xudc->dev, "failed to request clocks: %d\n", err); return err; } @@ -3798,7 +3798,7 @@ static int tegra_xudc_probe(struct platform_device *pdev) err = devm_regulator_bulk_get(&pdev->dev, xudc->soc->num_supplies, xudc->supplies); if (err) { - dev_err(xudc->dev, "failed to request regulators %d\n", err); + dev_err(xudc->dev, "failed to request regulators: %d\n", err); return err; } @@ -3808,7 +3808,7 @@ static int tegra_xudc_probe(struct platform_device *pdev) err = regulator_bulk_enable(xudc->soc->num_supplies, xudc->supplies); if (err) { - dev_err(xudc->dev, "failed to enable regulators %d\n", err); + dev_err(xudc->dev, "failed to enable regulators: %d\n", err); goto put_padctl; } -- cgit v1.2.3 From 2003a419c7f3b5990433aa85f3804bb4ccacbba9 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 5 Aug 2020 14:14:58 +0100 Subject: usb: gadget: fix spelling mistake "Dectected" -> "Detected" There is a spelling mistake in a literal string. Fix it. Signed-off-by: Colin Ian King Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/fsl_udc_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index 3e98740b8cfc..de528e3b0662 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -2061,7 +2061,7 @@ static int fsl_proc_read(struct seq_file *m, void *v) "Sleep Enable: %d SOF Received Enable: %d " "Reset Enable: %d\n" "System Error Enable: %d " - "Port Change Dectected Enable: %d\n" + "Port Change Detected Enable: %d\n" "USB Error Intr Enable: %d USB Intr Enable: %d\n\n", (tmp_reg & USB_INTR_DEVICE_SUSPEND) ? 1 : 0, (tmp_reg & USB_INTR_SOF_EN) ? 1 : 0, -- cgit v1.2.3 From 230c1aa370890dcd87772e775c19fa603ca5ac11 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 6 Aug 2020 18:04:16 +0200 Subject: usb: gadget: tegra-xudc: Properly align parameters Align parameters on subsequent lines with the parameters on the first line for consistency. Signed-off-by: Thierry Reding Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/tegra-xudc.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c index a59d6a9e8ef7..606b70c2dec8 100644 --- a/drivers/usb/gadget/udc/tegra-xudc.c +++ b/drivers/usb/gadget/udc/tegra-xudc.c @@ -3692,8 +3692,7 @@ static int tegra_xudc_powerdomain_init(struct tegra_xudc *xudc) struct device *dev = xudc->dev; int err; - xudc->genpd_dev_device = dev_pm_domain_attach_by_name(dev, - "dev"); + xudc->genpd_dev_device = dev_pm_domain_attach_by_name(dev, "dev"); if (IS_ERR(xudc->genpd_dev_device)) { err = PTR_ERR(xudc->genpd_dev_device); dev_err(dev, "failed to get device power domain: %d\n", err); @@ -3708,16 +3707,16 @@ static int tegra_xudc_powerdomain_init(struct tegra_xudc *xudc) } xudc->genpd_dl_device = device_link_add(dev, xudc->genpd_dev_device, - DL_FLAG_PM_RUNTIME | - DL_FLAG_STATELESS); + DL_FLAG_PM_RUNTIME | + DL_FLAG_STATELESS); if (!xudc->genpd_dl_device) { dev_err(dev, "failed to add USB device link\n"); return -ENODEV; } xudc->genpd_dl_ss = device_link_add(dev, xudc->genpd_dev_ss, - DL_FLAG_PM_RUNTIME | - DL_FLAG_STATELESS); + DL_FLAG_PM_RUNTIME | + DL_FLAG_STATELESS); if (!xudc->genpd_dl_ss) { dev_err(dev, "failed to add SuperSpeed device link\n"); return -ENODEV; @@ -3772,16 +3771,15 @@ static int tegra_xudc_probe(struct platform_device *pdev) return err; } - xudc->clks = devm_kcalloc(&pdev->dev, xudc->soc->num_clks, - sizeof(*xudc->clks), GFP_KERNEL); + xudc->clks = devm_kcalloc(&pdev->dev, xudc->soc->num_clks, sizeof(*xudc->clks), + GFP_KERNEL); if (!xudc->clks) return -ENOMEM; for (i = 0; i < xudc->soc->num_clks; i++) xudc->clks[i].id = xudc->soc->clock_names[i]; - err = devm_clk_bulk_get(&pdev->dev, xudc->soc->num_clks, - xudc->clks); + err = devm_clk_bulk_get(&pdev->dev, xudc->soc->num_clks, xudc->clks); if (err) { dev_err(xudc->dev, "failed to request clocks: %d\n", err); return err; -- cgit v1.2.3 From a50758bb6c740954c81aecb5a15df8e0c8da6250 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 6 Aug 2020 18:04:17 +0200 Subject: usb: gadget: tegra-xudc: Do not print errors on probe deferral Probe deferral is an expected condition and can happen multiple times during boot. Make sure not to output an error message in that case because they are not useful. Signed-off-by: Thierry Reding Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/tegra-xudc.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c index 606b70c2dec8..580bef8eb4cb 100644 --- a/drivers/usb/gadget/udc/tegra-xudc.c +++ b/drivers/usb/gadget/udc/tegra-xudc.c @@ -3781,7 +3781,9 @@ static int tegra_xudc_probe(struct platform_device *pdev) err = devm_clk_bulk_get(&pdev->dev, xudc->soc->num_clks, xudc->clks); if (err) { - dev_err(xudc->dev, "failed to request clocks: %d\n", err); + if (err != -EPROBE_DEFER) + dev_err(xudc->dev, "failed to request clocks: %d\n", err); + return err; } @@ -3796,7 +3798,9 @@ static int tegra_xudc_probe(struct platform_device *pdev) err = devm_regulator_bulk_get(&pdev->dev, xudc->soc->num_supplies, xudc->supplies); if (err) { - dev_err(xudc->dev, "failed to request regulators: %d\n", err); + if (err != -EPROBE_DEFER) + dev_err(xudc->dev, "failed to request regulators: %d\n", err); + return err; } -- cgit v1.2.3 From 5b35dd1a5a666329192a9616ec21098591259058 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Mon, 14 Sep 2020 14:17:30 +0800 Subject: usb: gadget: bcm63xx_udc: fix up the error of undeclared usb_debug_root Fix up the build error caused by undeclared usb_debug_root Cc: stable Fixes: a66ada4f241c ("usb: gadget: bcm63xx_udc: create debugfs directory under usb root") Reported-by: kernel test robot Signed-off-by: Chunfeng Yun Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/bcm63xx_udc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c index feaec00a3c16..9cd4a70ccdd6 100644 --- a/drivers/usb/gadget/udc/bcm63xx_udc.c +++ b/drivers/usb/gadget/udc/bcm63xx_udc.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3 From ca3df3468eec87f6374662f7de425bc44c3810c1 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Thu, 24 Sep 2020 01:21:18 -0700 Subject: usb: dwc3: gadget: Check MPS of the request length When preparing for SG, not all the entries are prepared at once. When resume, don't use the remaining request length to calculate for MPS alignment. Use the entire request->length to do that. Cc: stable@vger.kernel.org Fixes: 5d187c0454ef ("usb: dwc3: gadget: Don't setup more than requested") Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index b2dfb3ada700..7e1909dd041b 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1098,6 +1098,8 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, struct scatterlist *s; int i; unsigned int length = req->request.length; + unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc); + unsigned int rem = length % maxp; unsigned int remaining = req->request.num_mapped_sgs - req->num_queued_sgs; @@ -1109,8 +1111,6 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, length -= sg_dma_len(s); for_each_sg(sg, s, remaining, i) { - unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc); - unsigned int rem = length % maxp; unsigned int trb_length; unsigned int chain = true; -- cgit v1.2.3 From 690e5c2dc29f8891fcfd30da67e0d5837c2c9df5 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Thu, 24 Sep 2020 01:21:24 -0700 Subject: usb: dwc3: gadget: Reclaim extra TRBs after request completion An SG request may be partially completed (due to no available TRBs). Don't reclaim extra TRBs and clear the needs_extra_trb flag until the request is fully completed. Otherwise, the driver will reclaim the wrong TRB. Cc: stable@vger.kernel.org Fixes: 1f512119a08c ("usb: dwc3: gadget: add remaining sg entries to ring") Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 7e1909dd041b..173421508635 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2744,6 +2744,11 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep, ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event, status); + req->request.actual = req->request.length - req->remaining; + + if (!dwc3_gadget_ep_request_completed(req)) + goto out; + if (req->needs_extra_trb) { unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc); @@ -2759,11 +2764,6 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep, req->needs_extra_trb = false; } - req->request.actual = req->request.length - req->remaining; - - if (!dwc3_gadget_ep_request_completed(req)) - goto out; - dwc3_gadget_giveback(dep, req, status); out: -- cgit v1.2.3 From 2b80357b773c4c572233d191cd849d25311c48fd Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Thu, 24 Sep 2020 01:21:30 -0700 Subject: usb: dwc3: gadget: Refactor preparing extra TRB When the driver prepares the extra TRB, it uses bounce buffer. If we just add a new parameter to dwc3_prepare_one_trb() to indicate this, then we can refactor and simplify the driver quite a bit. dwc3_prepare_one_trb() also checks if a request had been moved to the started list. This is a prerequisite to subsequence patches improving the handling of extra TRBs. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 93 +++++++++++++---------------------------------- 1 file changed, 25 insertions(+), 68 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 173421508635..e74d97aa8b53 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1060,10 +1060,11 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb, * @trb_length: buffer size of the TRB * @chain: should this TRB be chained to the next? * @node: only for isochronous endpoints. First TRB needs different type. + * @use_bounce_buffer: set to use bounce buffer */ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_request *req, unsigned int trb_length, - unsigned int chain, unsigned int node) + unsigned int chain, unsigned int node, bool use_bounce_buffer) { struct dwc3_trb *trb; dma_addr_t dma; @@ -1072,7 +1073,9 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, unsigned int no_interrupt = req->request.no_interrupt; unsigned int is_last = req->request.is_last; - if (req->request.num_sgs > 0) + if (use_bounce_buffer) + dma = dep->dwc->bounce_addr; + else if (req->request.num_sgs > 0) dma = sg_dma_address(req->start_sg); else dma = req->request.dma; @@ -1129,56 +1132,35 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, chain = false; if (rem && usb_endpoint_dir_out(dep->endpoint.desc) && !chain) { - struct dwc3 *dwc = dep->dwc; - struct dwc3_trb *trb; - req->needs_extra_trb = true; /* prepare normal TRB */ - dwc3_prepare_one_trb(dep, req, trb_length, true, i); + dwc3_prepare_one_trb(dep, req, trb_length, + true, i, false); /* Now prepare one extra TRB to align transfer size */ - trb = &dep->trb_pool[dep->trb_enqueue]; - req->num_trbs++; - __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, - maxp - rem, false, 1, - req->request.stream_id, - req->request.short_not_ok, - req->request.no_interrupt, - req->request.is_last); + dwc3_prepare_one_trb(dep, req, maxp - rem, + false, 1, true); } else if (req->request.zero && req->request.length && !usb_endpoint_xfer_isoc(dep->endpoint.desc) && !rem && !chain) { - struct dwc3 *dwc = dep->dwc; - struct dwc3_trb *trb; - req->needs_extra_trb = true; /* Prepare normal TRB */ - dwc3_prepare_one_trb(dep, req, trb_length, true, i); + dwc3_prepare_one_trb(dep, req, trb_length, + true, i, false); /* Prepare one extra TRB to handle ZLP */ - trb = &dep->trb_pool[dep->trb_enqueue]; - req->num_trbs++; - __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0, - !req->direction, 1, - req->request.stream_id, - req->request.short_not_ok, - req->request.no_interrupt, - req->request.is_last); + dwc3_prepare_one_trb(dep, req, 0, + !req->direction, 1, true); /* Prepare one more TRB to handle MPS alignment */ - if (!req->direction) { - trb = &dep->trb_pool[dep->trb_enqueue]; - req->num_trbs++; - __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp, - false, 1, req->request.stream_id, - req->request.short_not_ok, - req->request.no_interrupt, - req->request.is_last); - } + if (!req->direction) + dwc3_prepare_one_trb(dep, req, maxp, + false, 1, true); } else { - dwc3_prepare_one_trb(dep, req, trb_length, chain, i); + dwc3_prepare_one_trb(dep, req, trb_length, + chain, i, false); } /* @@ -1216,54 +1198,29 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, unsigned int rem = length % maxp; if ((!length || rem) && usb_endpoint_dir_out(dep->endpoint.desc)) { - struct dwc3 *dwc = dep->dwc; - struct dwc3_trb *trb; - req->needs_extra_trb = true; /* prepare normal TRB */ - dwc3_prepare_one_trb(dep, req, length, true, 0); + dwc3_prepare_one_trb(dep, req, length, true, 0, false); /* Now prepare one extra TRB to align transfer size */ - trb = &dep->trb_pool[dep->trb_enqueue]; - req->num_trbs++; - __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem, - false, 1, req->request.stream_id, - req->request.short_not_ok, - req->request.no_interrupt, - req->request.is_last); + dwc3_prepare_one_trb(dep, req, maxp - rem, false, 1, true); } else if (req->request.zero && req->request.length && !usb_endpoint_xfer_isoc(dep->endpoint.desc) && (IS_ALIGNED(req->request.length, maxp))) { - struct dwc3 *dwc = dep->dwc; - struct dwc3_trb *trb; - req->needs_extra_trb = true; /* prepare normal TRB */ - dwc3_prepare_one_trb(dep, req, length, true, 0); + dwc3_prepare_one_trb(dep, req, length, true, 0, false); /* Prepare one extra TRB to handle ZLP */ - trb = &dep->trb_pool[dep->trb_enqueue]; - req->num_trbs++; - __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0, - !req->direction, 1, req->request.stream_id, - req->request.short_not_ok, - req->request.no_interrupt, - req->request.is_last); + dwc3_prepare_one_trb(dep, req, 0, !req->direction, 1, true); /* Prepare one more TRB to handle MPS alignment for OUT */ - if (!req->direction) { - trb = &dep->trb_pool[dep->trb_enqueue]; - req->num_trbs++; - __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp, - false, 1, req->request.stream_id, - req->request.short_not_ok, - req->request.no_interrupt, - req->request.is_last); - } + if (!req->direction) + dwc3_prepare_one_trb(dep, req, maxp, false, 1, true); } else { - dwc3_prepare_one_trb(dep, req, length, false, 0); + dwc3_prepare_one_trb(dep, req, length, false, 0, false); } } -- cgit v1.2.3 From a2841f41d07fc85cf47d13fda30e254c5413e514 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Thu, 24 Sep 2020 01:21:36 -0700 Subject: usb: dwc3: gadget: Improve TRB ZLP setup For OUT requests that requires extra TRBs for ZLP. We don't need to prepare the 0-length TRB and simply prepare the MPS size TRB. This reduces 1 TRB needed to prepare for ZLP. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 50 +++++++++++++++++------------------------------ 1 file changed, 18 insertions(+), 32 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index e74d97aa8b53..97790f55be1a 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1132,11 +1132,12 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, chain = false; if (rem && usb_endpoint_dir_out(dep->endpoint.desc) && !chain) { - req->needs_extra_trb = true; - /* prepare normal TRB */ - dwc3_prepare_one_trb(dep, req, trb_length, + if (req->request.length) { + req->needs_extra_trb = true; + dwc3_prepare_one_trb(dep, req, trb_length, true, i, false); + } /* Now prepare one extra TRB to align transfer size */ dwc3_prepare_one_trb(dep, req, maxp - rem, @@ -1151,13 +1152,9 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, true, i, false); /* Prepare one extra TRB to handle ZLP */ - dwc3_prepare_one_trb(dep, req, 0, - !req->direction, 1, true); - - /* Prepare one more TRB to handle MPS alignment */ - if (!req->direction) - dwc3_prepare_one_trb(dep, req, maxp, - false, 1, true); + dwc3_prepare_one_trb(dep, req, + req->direction ? 0 : maxp, + false, 1, true); } else { dwc3_prepare_one_trb(dep, req, trb_length, chain, i, false); @@ -1198,10 +1195,11 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, unsigned int rem = length % maxp; if ((!length || rem) && usb_endpoint_dir_out(dep->endpoint.desc)) { - req->needs_extra_trb = true; - /* prepare normal TRB */ - dwc3_prepare_one_trb(dep, req, length, true, 0, false); + if (req->request.length) { + req->needs_extra_trb = true; + dwc3_prepare_one_trb(dep, req, length, true, 0, false); + } /* Now prepare one extra TRB to align transfer size */ dwc3_prepare_one_trb(dep, req, maxp - rem, false, 1, true); @@ -1214,11 +1212,8 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, dwc3_prepare_one_trb(dep, req, length, true, 0, false); /* Prepare one extra TRB to handle ZLP */ - dwc3_prepare_one_trb(dep, req, 0, !req->direction, 1, true); - - /* Prepare one more TRB to handle MPS alignment for OUT */ - if (!req->direction) - dwc3_prepare_one_trb(dep, req, maxp, false, 1, true); + dwc3_prepare_one_trb(dep, req, req->direction ? 0 : maxp, + false, 1, true); } else { dwc3_prepare_one_trb(dep, req, length, false, 0, false); } @@ -2621,12 +2616,12 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep, } /* - * 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. + * We use bounce buffer for requests that needs extra TRB or OUT ZLP. If + * this TRB points to the bounce buffer address, it's a MPS alignment + * TRB. Don't add it to req->remaining calculation. */ - - if (req->needs_extra_trb && !(trb->ctrl & DWC3_TRB_CTRL_CHN)) { + if (trb->bpl == lower_32_bits(dep->dwc->bounce_addr) && + trb->bph == upper_32_bits(dep->dwc->bounce_addr)) { trb->ctrl &= ~DWC3_TRB_CTRL_HWO; return 1; } @@ -2707,17 +2702,8 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep, goto out; if (req->needs_extra_trb) { - unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc); - ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event, status); - - /* Reclaim MPS padding TRB for ZLP */ - if (!req->direction && req->request.zero && req->request.length && - !usb_endpoint_xfer_isoc(dep->endpoint.desc) && - (IS_ALIGNED(req->request.length, maxp))) - ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event, status); - req->needs_extra_trb = false; } -- cgit v1.2.3 From 66706077dc89c66a4777a4c6298273816afb848c Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Thu, 24 Sep 2020 01:21:43 -0700 Subject: usb: dwc3: ep0: Fix ZLP for OUT ep0 requests The current ZLP handling for ep0 requests is only for control IN requests. For OUT direction, DWC3 needs to check and setup for MPS alignment. Usually, control OUT requests can indicate its transfer size via the wLength field of the control message. So usb_request->zero is usually not needed for OUT direction. To handle ZLP OUT for control endpoint, make sure the TRB is MPS size. Cc: stable@vger.kernel.org Fixes: c7fcdeb2627c ("usb: dwc3: ep0: simplify EP0 state machine") Fixes: d6e5a549cc4d ("usb: dwc3: simplify ZLP handling") Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index b0363e3ba4d1..5580caed8b0c 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -947,12 +947,16 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc, static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, struct dwc3_ep *dep, struct dwc3_request *req) { + unsigned int trb_length = 0; int ret; req->direction = !!dep->number; if (req->request.length == 0) { - dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 0, + if (!req->direction) + trb_length = dep->endpoint.maxpacket; + + dwc3_ep0_prepare_one_trb(dep, dwc->bounce_addr, trb_length, DWC3_TRBCTL_CONTROL_DATA, false); ret = dwc3_ep0_start_trans(dep); } else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) @@ -999,9 +1003,12 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, req->trb = &dwc->ep0_trb[dep->trb_enqueue - 1]; + if (!req->direction) + trb_length = dep->endpoint.maxpacket; + /* Now prepare one extra TRB to align transfer size */ dwc3_ep0_prepare_one_trb(dep, dwc->bounce_addr, - 0, DWC3_TRBCTL_CONTROL_DATA, + trb_length, DWC3_TRBCTL_CONTROL_DATA, false); ret = dwc3_ep0_start_trans(dep); } else { -- cgit v1.2.3 From 13111fcb0d64fb69bd7466d6e2ca6ed7b95a9d50 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Thu, 24 Sep 2020 01:21:49 -0700 Subject: usb: dwc3: gadget: Return the number of prepared TRBs In preparation for fixing the check for number of remaining TRBs, revise dwc3_prepare_one_trb_linear() and dwc3_prepare_one_trb_sg() to return the number of prepared TRBs. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 97790f55be1a..673a34b715e2 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1094,7 +1094,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, stream_id, short_not_ok, no_interrupt, is_last); } -static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, +static int dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, struct dwc3_request *req) { struct scatterlist *sg = req->start_sg; @@ -1105,6 +1105,7 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, unsigned int rem = length % maxp; unsigned int remaining = req->request.num_mapped_sgs - req->num_queued_sgs; + unsigned int num_trbs = req->num_trbs; /* * If we resume preparing the request, then get the remaining length of @@ -1131,9 +1132,15 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, if ((i == remaining - 1) || !length) chain = false; + if (!dwc3_calc_trbs_left(dep)) + break; + if (rem && usb_endpoint_dir_out(dep->endpoint.desc) && !chain) { /* prepare normal TRB */ if (req->request.length) { + if (dwc3_calc_trbs_left(dep) < 2) + goto out; + req->needs_extra_trb = true; dwc3_prepare_one_trb(dep, req, trb_length, true, i, false); @@ -1145,6 +1152,10 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, } else if (req->request.zero && req->request.length && !usb_endpoint_xfer_isoc(dep->endpoint.desc) && !rem && !chain) { + + if (dwc3_calc_trbs_left(dep) < 2) + goto out; + req->needs_extra_trb = true; /* Prepare normal TRB */ @@ -1185,18 +1196,28 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, if (!dwc3_calc_trbs_left(dep)) break; } + +out: + return req->num_trbs - num_trbs; } -static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, +static int dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, struct dwc3_request *req) { unsigned int length = req->request.length; unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc); unsigned int rem = length % maxp; + unsigned int num_trbs = req->num_trbs; + + if (!dwc3_calc_trbs_left(dep)) + goto out; if ((!length || rem) && usb_endpoint_dir_out(dep->endpoint.desc)) { /* prepare normal TRB */ if (req->request.length) { + if (dwc3_calc_trbs_left(dep) < 2) + goto out; + req->needs_extra_trb = true; dwc3_prepare_one_trb(dep, req, length, true, 0, false); } @@ -1206,6 +1227,10 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, } else if (req->request.zero && req->request.length && !usb_endpoint_xfer_isoc(dep->endpoint.desc) && (IS_ALIGNED(req->request.length, maxp))) { + + if (dwc3_calc_trbs_left(dep) < 2) + goto out; + req->needs_extra_trb = true; /* prepare normal TRB */ @@ -1215,8 +1240,14 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, dwc3_prepare_one_trb(dep, req, req->direction ? 0 : maxp, false, 1, true); } else { + if (!dwc3_calc_trbs_left(dep)) + goto out; + dwc3_prepare_one_trb(dep, req, length, false, 0, false); } + +out: + return req->num_trbs - num_trbs; } /* -- cgit v1.2.3 From 490410b2e73cd350ba91946913b017c8f6e1b612 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Thu, 24 Sep 2020 01:21:55 -0700 Subject: usb: dwc3: gadget: Check for number of TRBs prepared By returning the number of TRBs prepared, we know whether to execute __dwc3_gadget_kick_transfer(). This allows us to check if we ran out of TRBs when extra TRBs are needed for OUT transfers. It also allows us to properly handle usb_gadget_map_request_by_dev() error. Fixes: c6267a51639b ("usb: dwc3: gadget: align transfers to wMaxPacketSize") Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 673a34b715e2..ec142724e85c 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1257,10 +1257,13 @@ out: * The function goes through the requests list and sets up TRBs for the * transfers. The function returns once there are no more TRBs available or * it runs out of requests. + * + * Returns the number of TRBs prepared or negative errno. */ -static void dwc3_prepare_trbs(struct dwc3_ep *dep) +static int dwc3_prepare_trbs(struct dwc3_ep *dep) { struct dwc3_request *req, *n; + int ret = 0; BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM); @@ -1275,11 +1278,14 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep) * break things. */ list_for_each_entry(req, &dep->started_list, list) { - if (req->num_pending_sgs > 0) - dwc3_prepare_one_trb_sg(dep, req); + if (req->num_pending_sgs > 0) { + ret = dwc3_prepare_one_trb_sg(dep, req); + if (!ret) + return ret; + } if (!dwc3_calc_trbs_left(dep)) - return; + return ret; /* * Don't prepare beyond a transfer. In DWC_usb32, its transfer @@ -1287,17 +1293,16 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep) * active transfer instead of stopping. */ if (dep->stream_capable && req->request.is_last) - return; + return ret; } list_for_each_entry_safe(req, n, &dep->pending_list, list) { struct dwc3 *dwc = dep->dwc; - int ret; ret = usb_gadget_map_request_by_dev(dwc->sysdev, &req->request, dep->direction); if (ret) - return; + return ret; req->sg = req->request.sg; req->start_sg = req->sg; @@ -1305,12 +1310,12 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep) req->num_pending_sgs = req->request.num_mapped_sgs; if (req->num_pending_sgs > 0) - dwc3_prepare_one_trb_sg(dep, req); + ret = dwc3_prepare_one_trb_sg(dep, req); else - dwc3_prepare_one_trb_linear(dep, req); + ret = dwc3_prepare_one_trb_linear(dep, req); - if (!dwc3_calc_trbs_left(dep)) - return; + if (!ret || !dwc3_calc_trbs_left(dep)) + return ret; /* * Don't prepare beyond a transfer. In DWC_usb32, its transfer @@ -1318,8 +1323,10 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep) * active transfer instead of stopping. */ if (dep->stream_capable && req->request.is_last) - return; + return ret; } + + return ret; } static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep); @@ -1332,12 +1339,12 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep) int ret; u32 cmd; - if (!dwc3_calc_trbs_left(dep)) - return 0; + ret = dwc3_prepare_trbs(dep); + if (ret <= 0) + return ret; starting = !(dep->flags & DWC3_EP_TRANSFER_STARTED); - dwc3_prepare_trbs(dep); req = next_request(&dep->started_list); if (!req) { dep->flags |= DWC3_EP_PENDING_REQUEST; -- cgit v1.2.3 From 30892cba55968fe244feac811cd00cc12b1a574b Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Thu, 24 Sep 2020 01:22:01 -0700 Subject: usb: dwc3: gadget: Set IOC if not enough for extra TRBs If we run out of TRBs because we need extra TRBs, make sure to set the IOC bit for the previously prepared TRB to get completion notification to free up TRBs to resume later. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index ec142724e85c..f091e107d9cb 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1197,7 +1197,27 @@ static int dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, break; } + return req->num_trbs - num_trbs; + out: + /* + * If we run out of TRBs for MPS alignment setup, then set IOC on the + * previous TRB to get notified for TRB completion to resume when more + * TRBs are available. + * + * Note: normally we shouldn't update the TRB after the HWO bit is set. + * However, the controller doesn't update its internal cache to handle + * the newly prepared TRBs until UPDATE_TRANSFER or START_TRANSFER + * command is executed. At this point, it doesn't happen yet, so we + * should be fine modifying it here. + */ + if (i) { + struct dwc3_trb *trb; + + trb = dwc3_ep_prev_trb(dep, dep->trb_enqueue); + trb->ctrl |= DWC3_TRB_CTRL_IOC; + } + return req->num_trbs - num_trbs; } -- cgit v1.2.3 From cb1b3997b636f65cee70e03c86627be521272c5d Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Thu, 24 Sep 2020 01:22:07 -0700 Subject: usb: dwc3: gadget: Refactor preparing last TRBs There are a lot of common codes for preparing SG and linear TRBs. Refactor them for easier read. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 129 +++++++++++++++++----------------------------- 1 file changed, 48 insertions(+), 81 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index f091e107d9cb..f75ce10407dd 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1094,6 +1094,47 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, stream_id, short_not_ok, no_interrupt, is_last); } +/** + * dwc3_prepare_last_sg - prepare TRBs for the last SG entry + * @dep: The endpoint that the request belongs to + * @req: The request to prepare + * @entry_length: The last SG entry size + * @node: Indicates whether this is not the first entry (for isoc only) + * + * Return the number of TRBs prepared. + */ +static int dwc3_prepare_last_sg(struct dwc3_ep *dep, + struct dwc3_request *req, unsigned int entry_length, + unsigned int node) +{ + unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc); + unsigned int rem = req->request.length % maxp; + unsigned int num_trbs = 1; + + if ((req->request.length && req->request.zero && !rem && + !usb_endpoint_xfer_isoc(dep->endpoint.desc)) || + (!req->direction && rem)) + num_trbs++; + + if (dwc3_calc_trbs_left(dep) < num_trbs) + return 0; + + req->needs_extra_trb = num_trbs > 1; + + /* Prepare a normal TRB */ + if (req->direction || req->request.length) + dwc3_prepare_one_trb(dep, req, entry_length, + req->needs_extra_trb, node, false); + + /* Prepare extra TRBs for ZLP and MPS OUT transfer alignment */ + if ((!req->direction && !req->request.length) || req->needs_extra_trb) + dwc3_prepare_one_trb(dep, req, + req->direction ? 0 : maxp - rem, + false, 1, true); + + return num_trbs; +} + static int dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, struct dwc3_request *req) { @@ -1101,8 +1142,6 @@ static int dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, struct scatterlist *s; int i; unsigned int length = req->request.length; - unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc); - unsigned int rem = length % maxp; unsigned int remaining = req->request.num_mapped_sgs - req->num_queued_sgs; unsigned int num_trbs = req->num_trbs; @@ -1116,7 +1155,7 @@ static int dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, for_each_sg(sg, s, remaining, i) { unsigned int trb_length; - unsigned int chain = true; + bool last_sg = false; trb_length = min_t(unsigned int, length, sg_dma_len(s)); @@ -1130,45 +1169,16 @@ static int dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, * mapped sg. */ if ((i == remaining - 1) || !length) - chain = false; + last_sg = true; if (!dwc3_calc_trbs_left(dep)) break; - if (rem && usb_endpoint_dir_out(dep->endpoint.desc) && !chain) { - /* prepare normal TRB */ - if (req->request.length) { - if (dwc3_calc_trbs_left(dep) < 2) - goto out; - - req->needs_extra_trb = true; - dwc3_prepare_one_trb(dep, req, trb_length, - true, i, false); - } - - /* Now prepare one extra TRB to align transfer size */ - dwc3_prepare_one_trb(dep, req, maxp - rem, - false, 1, true); - } else if (req->request.zero && req->request.length && - !usb_endpoint_xfer_isoc(dep->endpoint.desc) && - !rem && !chain) { - - if (dwc3_calc_trbs_left(dep) < 2) + if (last_sg) { + if (!dwc3_prepare_last_sg(dep, req, trb_length, i)) goto out; - - req->needs_extra_trb = true; - - /* Prepare normal TRB */ - dwc3_prepare_one_trb(dep, req, trb_length, - true, i, false); - - /* Prepare one extra TRB to handle ZLP */ - dwc3_prepare_one_trb(dep, req, - req->direction ? 0 : maxp, - false, 1, true); } else { - dwc3_prepare_one_trb(dep, req, trb_length, - chain, i, false); + dwc3_prepare_one_trb(dep, req, trb_length, 1, i, false); } /* @@ -1178,7 +1188,7 @@ static int dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, * we have free trbs we can continue queuing from where we * previously stopped */ - if (chain) + if (!last_sg) req->start_sg = sg_next(s); req->num_queued_sgs++; @@ -1224,50 +1234,7 @@ out: static int dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, struct dwc3_request *req) { - unsigned int length = req->request.length; - unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc); - unsigned int rem = length % maxp; - unsigned int num_trbs = req->num_trbs; - - if (!dwc3_calc_trbs_left(dep)) - goto out; - - if ((!length || rem) && usb_endpoint_dir_out(dep->endpoint.desc)) { - /* prepare normal TRB */ - if (req->request.length) { - if (dwc3_calc_trbs_left(dep) < 2) - goto out; - - req->needs_extra_trb = true; - dwc3_prepare_one_trb(dep, req, length, true, 0, false); - } - - /* Now prepare one extra TRB to align transfer size */ - dwc3_prepare_one_trb(dep, req, maxp - rem, false, 1, true); - } else if (req->request.zero && req->request.length && - !usb_endpoint_xfer_isoc(dep->endpoint.desc) && - (IS_ALIGNED(req->request.length, maxp))) { - - if (dwc3_calc_trbs_left(dep) < 2) - goto out; - - req->needs_extra_trb = true; - - /* prepare normal TRB */ - dwc3_prepare_one_trb(dep, req, length, true, 0, false); - - /* Prepare one extra TRB to handle ZLP */ - dwc3_prepare_one_trb(dep, req, req->direction ? 0 : maxp, - false, 1, true); - } else { - if (!dwc3_calc_trbs_left(dep)) - goto out; - - dwc3_prepare_one_trb(dep, req, length, false, 0, false); - } - -out: - return req->num_trbs - num_trbs; + return dwc3_prepare_last_sg(dep, req, req->request.length, 0); } /* -- cgit v1.2.3 From 7f2958d9ad58a41df81a7c1e28dd5ddeeec58890 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Thu, 24 Sep 2020 01:22:14 -0700 Subject: usb: dwc3: gadget: Rename misleading function names The functions dwc3_prepare_one_trb_sg and dwc3_prepare_one_trb_linear are not necessarily preparing "one" TRB, it can prepare multiple TRBs. Rename these functions as follow: dwc3_prepare_one_trb_sg -> dwc3_prepare_trbs_sg dwc3_prepare_one_trb_linear -> dwc3_prepare_trbs_linear Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index f75ce10407dd..82bc075ba97c 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1135,7 +1135,7 @@ static int dwc3_prepare_last_sg(struct dwc3_ep *dep, return num_trbs; } -static int dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, +static int dwc3_prepare_trbs_sg(struct dwc3_ep *dep, struct dwc3_request *req) { struct scatterlist *sg = req->start_sg; @@ -1231,7 +1231,7 @@ out: return req->num_trbs - num_trbs; } -static int dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, +static int dwc3_prepare_trbs_linear(struct dwc3_ep *dep, struct dwc3_request *req) { return dwc3_prepare_last_sg(dep, req, req->request.length, 0); @@ -1266,7 +1266,7 @@ static int dwc3_prepare_trbs(struct dwc3_ep *dep) */ list_for_each_entry(req, &dep->started_list, list) { if (req->num_pending_sgs > 0) { - ret = dwc3_prepare_one_trb_sg(dep, req); + ret = dwc3_prepare_trbs_sg(dep, req); if (!ret) return ret; } @@ -1297,9 +1297,9 @@ static int dwc3_prepare_trbs(struct dwc3_ep *dep) req->num_pending_sgs = req->request.num_mapped_sgs; if (req->num_pending_sgs > 0) - ret = dwc3_prepare_one_trb_sg(dep, req); + ret = dwc3_prepare_trbs_sg(dep, req); else - ret = dwc3_prepare_one_trb_linear(dep, req); + ret = dwc3_prepare_trbs_linear(dep, req); if (!ret || !dwc3_calc_trbs_left(dep)) return ret; -- cgit v1.2.3 From f0c485663d5976acb1cb490981a4763a215df75e Mon Sep 17 00:00:00 2001 From: Zqiang Date: Sun, 27 Sep 2020 16:01:16 +0800 Subject: usb: gadget: uvc: Fix the wrong v4l2_device_unregister call If an error occurred before calling the 'v4l2_device_register' func, and then goto error, but no need to call 'v4l2_device_unregister' func. Signed-off-by: Zqiang Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uvc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 0b9712616455..44b4352a2676 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -740,20 +740,20 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) /* Initialise video. */ ret = uvcg_video_init(&uvc->video, uvc); if (ret < 0) - goto error; + goto v4l2_error; /* Register a V4L2 device. */ ret = uvc_register_video(uvc); if (ret < 0) { uvcg_err(f, "failed to register video device\n"); - goto error; + goto v4l2_error; } return 0; -error: +v4l2_error: v4l2_device_unregister(&uvc->v4l2_dev); - +error: if (uvc->control_req) usb_ep_free_request(cdev->gadget->ep0, uvc->control_req); kfree(uvc->control_buf); -- cgit v1.2.3 From 2a87445af23ec7db311ff81f141626e72a2bbd86 Mon Sep 17 00:00:00 2001 From: Tang Bin Date: Sun, 27 Sep 2020 21:53:04 +0800 Subject: usb: bdc: Fix unused assignment in bdc_probe() Delete unused initialized value of 'ret', because it will be assigned by the function clk_prepare_enable(). Signed-off-by: Zhang Shengju Signed-off-by: Tang Bin Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/bdc/bdc_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c index 5ff36525044e..96e1fca63b63 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_core.c +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c @@ -484,7 +484,7 @@ static void bdc_phy_exit(struct bdc *bdc) static int bdc_probe(struct platform_device *pdev) { struct bdc *bdc; - int ret = -ENOMEM; + int ret; int irq; u32 temp; struct device *dev = &pdev->dev; -- cgit v1.2.3 From f580170f135af14e287560d94045624d4242d712 Mon Sep 17 00:00:00 2001 From: Yu Chen Date: Tue, 8 Sep 2020 09:20:56 +0200 Subject: usb: dwc3: Add splitdisable quirk for Hisilicon Kirin Soc SPLIT_BOUNDARY_DISABLE should be set for DesignWare USB3 DRD Core of Hisilicon Kirin Soc when dwc3 core act as host. [mchehab: dropped a dev_dbg() as only traces are now allowwed on this driver] Signed-off-by: Yu Chen Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 25 +++++++++++++++++++++++++ drivers/usb/dwc3/core.h | 7 +++++++ 2 files changed, 32 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 385262f6747d..bdf0925da6b6 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -119,6 +119,7 @@ static void __dwc3_set_mode(struct work_struct *work) struct dwc3 *dwc = work_to_dwc(work); unsigned long flags; int ret; + u32 reg; pm_runtime_get_sync(dwc->dev); @@ -169,6 +170,11 @@ static void __dwc3_set_mode(struct work_struct *work) otg_set_vbus(dwc->usb2_phy->otg, true); phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST); phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST); + if (dwc->dis_split_quirk) { + reg = dwc3_readl(dwc->regs, DWC3_GUCTL3); + reg |= DWC3_GUCTL3_SPLITDISABLE; + dwc3_writel(dwc->regs, DWC3_GUCTL3, reg); + } } break; case DWC3_GCTL_PRTCAP_DEVICE: @@ -1349,6 +1355,9 @@ static void dwc3_get_properties(struct dwc3 *dwc) dwc->dis_metastability_quirk = device_property_read_bool(dev, "snps,dis_metastability_quirk"); + dwc->dis_split_quirk = device_property_read_bool(dev, + "snps,dis-split-quirk"); + dwc->lpm_nyet_threshold = lpm_nyet_threshold; dwc->tx_de_emphasis = tx_de_emphasis; @@ -1886,10 +1895,26 @@ static int dwc3_resume(struct device *dev) return 0; } + +static void dwc3_complete(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + u32 reg; + + if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST && + dwc->dis_split_quirk) { + reg = dwc3_readl(dwc->regs, DWC3_GUCTL3); + reg |= DWC3_GUCTL3_SPLITDISABLE; + dwc3_writel(dwc->regs, DWC3_GUCTL3, reg); + } +} +#else +#define dwc3_complete NULL #endif /* CONFIG_PM_SLEEP */ static const struct dev_pm_ops dwc3_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume) + .complete = dwc3_complete, SET_RUNTIME_PM_OPS(dwc3_runtime_suspend, dwc3_runtime_resume, dwc3_runtime_idle) }; diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 83b6c871d58d..74323b10a64a 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -138,6 +138,7 @@ #define DWC3_GEVNTCOUNT(n) (0xc40c + ((n) * 0x10)) #define DWC3_GHWPARAMS8 0xc600 +#define DWC3_GUCTL3 0xc60c #define DWC3_GFLADJ 0xc630 /* Device Registers */ @@ -380,6 +381,9 @@ /* Global User Control Register 2 */ #define DWC3_GUCTL2_RST_ACTBITLATER BIT(14) +/* Global User Control Register 3 */ +#define DWC3_GUCTL3_SPLITDISABLE BIT(14) + /* Device Configuration Register */ #define DWC3_DCFG_DEVADDR(addr) ((addr) << 3) #define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f) @@ -1053,6 +1057,7 @@ struct dwc3_scratchpad_array { * 2 - No de-emphasis * 3 - Reserved * @dis_metastability_quirk: set to disable metastability quirk. + * @dis_split_quirk: set to disable split boundary. * @imod_interval: set the interrupt moderation interval in 250ns * increments or 0 to disable. */ @@ -1246,6 +1251,8 @@ struct dwc3 { unsigned dis_metastability_quirk:1; + unsigned dis_split_quirk:1; + u16 imod_interval; }; -- cgit v1.2.3 From abc6b579048e2084ea28c677dd11106d67fe8498 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Thu, 10 Sep 2020 17:11:23 +0800 Subject: usb: cdns3: gadget: using correct sg operations It needs to use request->num_mapped_sgs to indicate mapped sg number, the request->num_sgs is the sg number before the mapping. These two entries have different values for the platforms which iommu or swiotlb is used. Besides, it needs to use correct sg APIs for mapped sg list for TRB assignment. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/gadget.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index 26ba41700672..caf011f59a4c 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -1098,11 +1098,13 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, u32 control; int pcs; u16 total_tdl = 0; + struct scatterlist *s = NULL; + bool sg_supported = !!(request->num_mapped_sgs); if (priv_ep->type == USB_ENDPOINT_XFER_ISOC) num_trb = priv_ep->interval; else - num_trb = request->num_sgs ? request->num_sgs : 1; + num_trb = sg_supported ? request->num_mapped_sgs : 1; if (num_trb > priv_ep->free_trbs) { priv_ep->flags |= EP_RING_FULL; @@ -1162,6 +1164,9 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, if (priv_dev->dev_ver <= DEV_VER_V2) togle_pcs = cdns3_wa1_update_guard(priv_ep, trb); + if (sg_supported) + s = request->sg; + /* set incorrect Cycle Bit for first trb*/ control = priv_ep->pcs ? 0 : TRB_CYCLE; @@ -1171,13 +1176,13 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, /* fill TRB */ control |= TRB_TYPE(TRB_NORMAL); - trb->buffer = cpu_to_le32(TRB_BUFFER(request->num_sgs == 0 - ? trb_dma : request->sg[sg_iter].dma_address)); - - if (likely(!request->num_sgs)) + if (sg_supported) { + trb->buffer = cpu_to_le32(TRB_BUFFER(sg_dma_address(s))); + length = sg_dma_len(s); + } else { + trb->buffer = cpu_to_le32(TRB_BUFFER(trb_dma)); length = request->length; - else - length = request->sg[sg_iter].length; + } if (likely(priv_dev->dev_ver >= DEV_VER_V2)) td_size = DIV_ROUND_UP(length, @@ -1215,6 +1220,9 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, else priv_req->trb->control = cpu_to_le32(control); + if (sg_supported) + s = sg_next(s); + control = 0; ++sg_iter; priv_req->end_trb = priv_ep->enqueue; -- cgit v1.2.3 From 4e218882eb5a967ea042ca68069140d2c969971a Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Thu, 10 Sep 2020 17:11:24 +0800 Subject: usb: cdns3: gadget: improve the dump TRB operation at cdns3_ep_run_transfer It only dumps the first TRB per request, it is not useful if only dump the first TRB when there are several TRBs per request. We improve it by dumpping all TRBs per request in this commit. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/gadget.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index caf011f59a4c..d33947d215f9 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -1090,6 +1090,7 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, struct cdns3_device *priv_dev = priv_ep->cdns3_dev; struct cdns3_request *priv_req; struct cdns3_trb *trb; + struct cdns3_trb *link_trb; dma_addr_t trb_dma; u32 togle_pcs = 1; int sg_iter = 0; @@ -1130,7 +1131,6 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, /* prepare ring */ if ((priv_ep->enqueue + num_trb) >= (priv_ep->num_trbs - 1)) { - struct cdns3_trb *link_trb; int doorbell, dma_index; u32 ch_bit = 0; @@ -1266,7 +1266,22 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, if (priv_dev->dev_ver <= DEV_VER_V2) cdns3_wa1_tray_restore_cycle_bit(priv_dev, priv_ep); - trace_cdns3_prepare_trb(priv_ep, priv_req->trb); + if (num_trb > 1) { + int i = 0; + + while (i < num_trb) { + trace_cdns3_prepare_trb(priv_ep, trb + i); + if (trb + i == link_trb) { + trb = priv_ep->trb_pool; + num_trb = num_trb - i; + i = 0; + } else { + i++; + } + } + } else { + trace_cdns3_prepare_trb(priv_ep, priv_req->trb); + } /* * Memory barrier - Cycle Bit must be set before trb->length and -- cgit v1.2.3 From 87e1dcd48970ea1cf2d2ce368eb70a46c2eff3ee Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Thu, 10 Sep 2020 17:11:26 +0800 Subject: usb: cdns3: gadget: add CHAIN and ISP bit for sg list use case For sg buffer list use case, we need to add ISP for each TRB, and add CHAIN bit for each TRB except for the last TRB. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/gadget.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index d33947d215f9..50aa993bff3c 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -1220,8 +1220,14 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, else priv_req->trb->control = cpu_to_le32(control); - if (sg_supported) + if (sg_supported) { + trb->control |= TRB_ISP; + /* Don't set chain bit for last TRB */ + if (sg_iter < num_trb - 1) + trb->control |= TRB_CHAIN; + s = sg_next(s); + } control = 0; ++sg_iter; -- cgit v1.2.3 From 249f0a25e8becebe351e0f70e00fc84f47d6584d Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Thu, 10 Sep 2020 17:11:27 +0800 Subject: usb: cdns3: gadget: handle sg list use case at completion correctly - Judge each TRB has been handled at cdns3_trb_handled, since the DMA pointer may be at the middle of the TD, we can't consider this TD has finished at that time. - Calculate req->actual according to finished TRBs. - Handle short transfer for sg list use case correctly. When the short transfer occurs, we check OUT_SMM at TRB to see if it is the last TRB. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/gadget.c | 85 ++++++++++++++++++++++++++++++---------------- drivers/usb/cdns3/gadget.h | 9 +++++ 2 files changed, 65 insertions(+), 29 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index 50aa993bff3c..eecc399e4314 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -817,6 +817,8 @@ void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep, request->length); priv_req->flags &= ~(REQUEST_PENDING | REQUEST_UNALIGNED); + /* All TRBs have finished, clear the counter */ + priv_req->finished_trb = 0; trace_cdns3_gadget_giveback(priv_req); if (priv_dev->dev_ver < DEV_VER_V2) { @@ -1239,6 +1241,7 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, trb = priv_req->trb; priv_req->flags |= REQUEST_PENDING; + priv_req->num_of_trb = num_trb; if (sg_iter == 1) trb->control |= cpu_to_le32(TRB_IOC | TRB_ISP); @@ -1360,7 +1363,7 @@ void cdns3_set_hw_configuration(struct cdns3_device *priv_dev) } /** - * cdns3_request_handled - check whether request has been handled by DMA + * cdns3_trb_handled - check whether trb has been handled by DMA * * @priv_ep: extended endpoint object. * @priv_req: request object for checking @@ -1377,32 +1380,28 @@ void cdns3_set_hw_configuration(struct cdns3_device *priv_dev) * ET = priv_req->end_trb - index of last TRB in transfer ring * CI = current_index - index of processed TRB by DMA. * - * As first step, function checks if cycle bit for priv_req->start_trb is - * correct. + * As first step, we check if the TRB between the ST and ET. + * Then, we check if cycle bit for index priv_ep->dequeue + * is correct. * * some rules: - * 1. priv_ep->dequeue never exceed current_index. + * 1. priv_ep->dequeue never equals to current_index. * 2 priv_ep->enqueue never exceed priv_ep->dequeue * 3. exception: priv_ep->enqueue == priv_ep->dequeue * and priv_ep->free_trbs is zero. * This case indicate that TR is full. * - * Then We can split recognition into two parts: + * At below two cases, the request have been handled. * Case 1 - priv_ep->dequeue < current_index * SR ... EQ ... DQ ... CI ... ER * SR ... DQ ... CI ... EQ ... ER * - * Request has been handled by DMA if ST and ET is between DQ and CI. - * * Case 2 - priv_ep->dequeue > current_index - * This situation take place when CI go through the LINK TRB at the end of + * This situation takes place when CI go through the LINK TRB at the end of * transfer ring. * SR ... CI ... EQ ... DQ ... ER - * - * Request has been handled by DMA if ET is less then CI or - * ET is greater or equal DQ. */ -static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep, +static bool cdns3_trb_handled(struct cdns3_endpoint *priv_ep, struct cdns3_request *priv_req) { struct cdns3_device *priv_dev = priv_ep->cdns3_dev; @@ -1414,7 +1413,25 @@ static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep, current_index = cdns3_get_dma_pos(priv_dev, priv_ep); doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY); - trb = &priv_ep->trb_pool[priv_req->start_trb]; + /* current trb doesn't belong to this request */ + if (priv_req->start_trb < priv_req->end_trb) { + if (priv_ep->dequeue > priv_req->end_trb) + goto finish; + + if (priv_ep->dequeue < priv_req->start_trb) + goto finish; + } + + if ((priv_req->start_trb > priv_req->end_trb) && + (priv_ep->dequeue > priv_req->end_trb) && + (priv_ep->dequeue < priv_req->start_trb)) + goto finish; + + if ((priv_req->start_trb == priv_req->end_trb) && + (priv_ep->dequeue != priv_req->end_trb)) + goto finish; + + trb = &priv_ep->trb_pool[priv_ep->dequeue]; if ((le32_to_cpu(trb->control) & TRB_CYCLE) != priv_ep->ccs) goto finish; @@ -1436,12 +1453,8 @@ static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep, !priv_ep->dequeue) goto finish; - if (priv_req->end_trb >= priv_ep->dequeue && - priv_req->end_trb < current_index) - handled = 1; + handled = 1; } else if (priv_ep->dequeue > current_index) { - if (priv_req->end_trb < current_index || - priv_req->end_trb >= priv_ep->dequeue) handled = 1; } @@ -1457,6 +1470,8 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev, struct cdns3_request *priv_req; struct usb_request *request; struct cdns3_trb *trb; + bool request_handled = false; + bool transfer_end = false; while (!list_empty(&priv_ep->pending_req_list)) { request = cdns3_next_request(&priv_ep->pending_req_list); @@ -1476,20 +1491,32 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev, */ cdns3_select_ep(priv_dev, priv_ep->endpoint.address); - if (!cdns3_request_handled(priv_ep, priv_req)) - goto prepare_next_td; + while (cdns3_trb_handled(priv_ep, priv_req)) { + priv_req->finished_trb++; + if (priv_req->finished_trb >= priv_req->num_of_trb) + request_handled = true; - trb = priv_ep->trb_pool + priv_ep->dequeue; - trace_cdns3_complete_trb(priv_ep, trb); + trb = priv_ep->trb_pool + priv_ep->dequeue; + trace_cdns3_complete_trb(priv_ep, trb); - if (trb != priv_req->trb) - dev_warn(priv_dev->dev, - "request_trb=0x%p, queue_trb=0x%p\n", - priv_req->trb, trb); + if (!transfer_end) + request->actual += + TRB_LEN(le32_to_cpu(trb->length)); - request->actual = TRB_LEN(le32_to_cpu(trb->length)); - cdns3_move_deq_to_next_trb(priv_req); - cdns3_gadget_giveback(priv_ep, priv_req, 0); + if (priv_req->num_of_trb > 1 && + le32_to_cpu(trb->control) & TRB_SMM) + transfer_end = true; + + cdns3_ep_inc_deq(priv_ep); + } + + if (request_handled) { + cdns3_gadget_giveback(priv_ep, priv_req, 0); + request_handled = false; + transfer_end = false; + } else { + goto prepare_next_td; + } if (priv_ep->type != USB_ENDPOINT_XFER_ISOC && TRBS_PER_SEGMENT == 2) diff --git a/drivers/usb/cdns3/gadget.h b/drivers/usb/cdns3/gadget.h index 52765b098b9e..9f8bd452847e 100644 --- a/drivers/usb/cdns3/gadget.h +++ b/drivers/usb/cdns3/gadget.h @@ -1030,6 +1030,11 @@ struct cdns3_trb { * When set to '1', the device will toggle its interpretation of the Cycle bit */ #define TRB_TOGGLE BIT(1) +/* + * The controller will set it if OUTSMM (OUT size mismatch) is detected, + * this bit is for normal TRB + */ +#define TRB_SMM BIT(1) /* * Short Packet (SP). OUT EPs at DMULT=1 only. Indicates if the TRB was @@ -1215,6 +1220,8 @@ struct cdns3_aligned_buf { * this endpoint * @flags: flag specifying special usage of request * @list: used by internally allocated request to add to wa2_descmiss_req_list. + * @finished_trb: number of trb has already finished per request + * @num_of_trb: how many trbs in this request */ struct cdns3_request { struct usb_request request; @@ -1230,6 +1237,8 @@ struct cdns3_request { #define REQUEST_UNALIGNED BIT(4) u32 flags; struct list_head list; + int finished_trb; + int num_of_trb; }; #define to_cdns3_request(r) (container_of(r, struct cdns3_request, request)) -- cgit v1.2.3 From 141e70fef4eea939a78b62517bda9ea01d936aad Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Thu, 10 Sep 2020 17:11:28 +0800 Subject: usb: cdns3: gadget: need to handle sg case for workaround 2 case Add sg case for workaround 2, the workaround 2 is described at the beginning of this file. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/gadget.c | 44 +++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index eecc399e4314..9116704fd48e 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -462,6 +462,36 @@ static int cdns3_start_all_request(struct cdns3_device *priv_dev, (reg) |= EP_STS_EN_DESCMISEN; \ } } while (0) +static void __cdns3_descmiss_copy_data(struct usb_request *request, + struct usb_request *descmiss_req) +{ + int length = request->actual + descmiss_req->actual; + struct scatterlist *s = request->sg; + + if (!s) { + if (length <= request->length) { + memcpy(&((u8 *)request->buf)[request->actual], + descmiss_req->buf, + descmiss_req->actual); + request->actual = length; + } else { + /* It should never occures */ + request->status = -ENOMEM; + } + } else { + if (length <= sg_dma_len(s)) { + void *p = phys_to_virt(sg_dma_address(s)); + + memcpy(&((u8 *)p)[request->actual], + descmiss_req->buf, + descmiss_req->actual); + request->actual = length; + } else { + request->status = -ENOMEM; + } + } +} + /** * cdns3_wa2_descmiss_copy_data copy data from internal requests to * request queued by class driver. @@ -488,21 +518,9 @@ static void cdns3_wa2_descmiss_copy_data(struct cdns3_endpoint *priv_ep, chunk_end = descmiss_priv_req->flags & REQUEST_INTERNAL_CH; length = request->actual + descmiss_req->actual; - request->status = descmiss_req->status; - - if (length <= request->length) { - memcpy(&((u8 *)request->buf)[request->actual], - descmiss_req->buf, - descmiss_req->actual); - request->actual = length; - } else { - /* It should never occures */ - request->status = -ENOMEM; - } - + __cdns3_descmiss_copy_data(request, descmiss_req); list_del_init(&descmiss_priv_req->list); - kfree(descmiss_req->buf); cdns3_gadget_ep_free_request(&priv_ep->endpoint, descmiss_req); --priv_ep->wa2_counter; -- cgit v1.2.3 From d6be7c94f9f80fdb64d896b89657ebc9ed96e3ed Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Thu, 10 Sep 2020 17:11:29 +0800 Subject: usb: cdns3: gadget: sg_support is only for DEV_VER_V2 or above The scatter buffer list support earlier than DEV_VER_V2 is not good enough, software can't know well about short transfer for it. Cc: Pawel Laszczak Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/gadget.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index 9116704fd48e..6e7b70a2e352 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -3159,7 +3159,6 @@ static int cdns3_gadget_start(struct cdns3 *cdns) priv_dev->gadget.speed = USB_SPEED_UNKNOWN; priv_dev->gadget.ops = &cdns3_gadget_ops; priv_dev->gadget.name = "usb-ss-gadget"; - priv_dev->gadget.sg_supported = 1; priv_dev->gadget.quirk_avoids_skb_reserve = 1; priv_dev->gadget.irq = cdns->dev_irq; @@ -3198,6 +3197,8 @@ static int cdns3_gadget_start(struct cdns3 *cdns) readl(&priv_dev->regs->usb_cap2)); priv_dev->dev_ver = GET_DEV_BASE_VERSION(priv_dev->dev_ver); + if (priv_dev->dev_ver >= DEV_VER_V2) + priv_dev->gadget.sg_supported = 1; priv_dev->zlp_buf = kzalloc(CDNS3_EP_ZLP_BUF_SIZE, GFP_KERNEL); if (!priv_dev->zlp_buf) { -- cgit v1.2.3 From 71ea88f6652a52f85b2110486ca99d26b71a3d8d Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Thu, 10 Sep 2020 17:11:30 +0800 Subject: usb: cdns3: gadget: enlarge the TRB ring length At Android ADB and MTP use case, it uses f_fs which supports scatter list, it means one request may need several TRBs for it. Besides, TRB consumes very fast compared to TRB has prepared for above use case, there are at most 120 pending requests, the date size is 16KB for each request, so four TRBs (4KB per TRB) per sg entry at worst case. so we need to enlarge the TRB ring length to avoid "no free TRB error". Since each TRB only consumes 12 bytes (3 * 32 bits), we enlarge the TRB length to 600, it leaves some buffers for potential "no free TRB error", and only increases a little memory cost. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/gadget.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/cdns3/gadget.h b/drivers/usb/cdns3/gadget.h index 9f8bd452847e..1ccecd237530 100644 --- a/drivers/usb/cdns3/gadget.h +++ b/drivers/usb/cdns3/gadget.h @@ -966,7 +966,7 @@ struct cdns3_usb_regs { /* * USBSS-DEV DMA interface. */ -#define TRBS_PER_SEGMENT 40 +#define TRBS_PER_SEGMENT 600 #define ISO_MAX_INTERVAL 10 -- cgit v1.2.3 From ae7e86108b12351028fa7e8796a59f9b2d9e1774 Mon Sep 17 00:00:00 2001 From: Wesley Cheng Date: Mon, 28 Sep 2020 17:20:59 -0700 Subject: usb: dwc3: Stop active transfers before halting the controller In the DWC3 databook, for a device initiated disconnect or bus reset, the driver is required to send dependxfer commands for any pending transfers. In addition, before the controller can move to the halted state, the SW needs to acknowledge any pending events. If the controller is not halted properly, there is a chance the controller will continue accessing stale or freed TRBs and buffers. Signed-off-by: Wesley Cheng Reviewed-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 2 +- drivers/usb/dwc3/gadget.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 5580caed8b0c..7be3903cb842 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -197,7 +197,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, int ret; spin_lock_irqsave(&dwc->lock, flags); - if (!dep->endpoint.desc) { + if (!dep->endpoint.desc || !dwc->pullups_connected) { dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n", dep->name); ret = -ESHUTDOWN; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 82bc075ba97c..359824c871cd 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1580,7 +1580,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) { struct dwc3 *dwc = dep->dwc; - if (!dep->endpoint.desc) { + if (!dep->endpoint.desc || !dwc->pullups_connected) { dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n", dep->name); return -ESHUTDOWN; @@ -1999,6 +1999,21 @@ static int dwc3_gadget_set_selfpowered(struct usb_gadget *g, return 0; } +static void dwc3_stop_active_transfers(struct dwc3 *dwc) +{ + u32 epnum; + + for (epnum = 2; epnum < dwc->num_eps; epnum++) { + struct dwc3_ep *dep; + + dep = dwc->eps[epnum]; + if (!dep) + continue; + + dwc3_remove_requests(dwc, dep); + } +} + static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) { u32 reg; @@ -2044,6 +2059,9 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) return 0; } +static void dwc3_gadget_disable_irq(struct dwc3 *dwc); +static void __dwc3_gadget_stop(struct dwc3 *dwc); + static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) { struct dwc3 *dwc = gadget_to_dwc(g); @@ -2067,7 +2085,46 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) } } + /* + * Synchronize any pending event handling before executing the controller + * halt routine. + */ + if (!is_on) { + dwc3_gadget_disable_irq(dwc); + synchronize_irq(dwc->irq_gadget); + } + spin_lock_irqsave(&dwc->lock, flags); + + if (!is_on) { + u32 count; + + /* + * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a + * Section 4.1.8 Table 4-7, it states that for a device-initiated + * disconnect, the SW needs to ensure that it sends "a DEPENDXFER + * command for any active transfers" before clearing the RunStop + * bit. + */ + dwc3_stop_active_transfers(dwc); + __dwc3_gadget_stop(dwc); + + /* + * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a + * Section 1.3.4, it mentions that for the DEVCTRLHLT bit, the + * "software needs to acknowledge the events that are generated + * (by writing to GEVNTCOUNTn) while it is waiting for this bit + * to be set to '1'." + */ + count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0)); + count &= DWC3_GEVNTCOUNT_MASK; + if (count > 0) { + dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count); + dwc->ev_buf->lpos = (dwc->ev_buf->lpos + count) % + dwc->ev_buf->length; + } + } + ret = dwc3_gadget_run_stop(dwc, is_on, false); spin_unlock_irqrestore(&dwc->lock, flags); @@ -3200,6 +3257,13 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) } dwc3_reset_gadget(dwc); + /* + * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a + * Section 4.1.2 Table 4-2, it states that during a USB reset, the SW + * needs to ensure that it sends "a DEPENDXFER command for any active + * transfers." + */ + dwc3_stop_active_transfers(dwc); reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_TSTCTRL_MASK; -- cgit v1.2.3 From a73abc28ce67989ebf881f33961d9dec9c7522b9 Mon Sep 17 00:00:00 2001 From: Tang Bin Date: Mon, 28 Sep 2020 20:00:51 +0800 Subject: usb: bdc: remove duplicated error message in case devm_platform_ioremap_resource() fails, that function already prints a relevant error message which renders the driver's dev_err() redundant. Let's remove the unnecessary message and, while at that, also make sure to pass along the error value returned by devm_platform_ioremap_resource() instead of always returning -ENOMEM. Signed-off-by: Zhang Shengju Signed-off-by: Tang Bin [balbi@kernel.org : improved commit log] Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/bdc/bdc_core.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c index 96e1fca63b63..0bef6b3f049b 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_core.c +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c @@ -510,10 +510,9 @@ static int bdc_probe(struct platform_device *pdev) bdc->clk = clk; bdc->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(bdc->regs)) { - dev_err(dev, "ioremap error\n"); - return -ENOMEM; - } + if (IS_ERR(bdc->regs)) + return PTR_ERR(bdc->regs); + irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; -- cgit v1.2.3 From d72ecc08dee49cc0b032a74c7efabab877f5c3fa Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Tue, 29 Sep 2020 00:18:48 -0700 Subject: usb: dwc3: gadget: Allow restarting a transfer It's possible that there's no new TRBs prepared when kicking a transfer. This happens when we need to stop and restart a transfer such as in the case of reinitiating a stream or retrying isoc transfer. For streams, sometime host may reject a stream and the device may need to reinitiate that stream by stopping and restarting a transfer. In this case, all the TRBs may have already been prepared. Allow the function __dwc3_gadget_kick_transfer() to go through even though there's no new TRB. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 359824c871cd..76c383eecfd3 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1326,8 +1326,13 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep) int ret; u32 cmd; + /* + * Note that it's normal to have no new TRBs prepared (i.e. ret == 0). + * This happens when we need to stop and restart a transfer such as in + * the case of reinitiating a stream or retrying an isoc transfer. + */ ret = dwc3_prepare_trbs(dep); - if (ret <= 0) + if (ret < 0) return ret; starting = !(dep->flags & DWC3_EP_TRANSFER_STARTED); -- cgit v1.2.3 From f9cc581badb144a3dcd841aee2d3bc2d242fcb2f Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Wed, 30 Sep 2020 17:44:19 -0700 Subject: usb: dwc3: gadget: Look ahead when setting IOC Previously if we run out of TRBs for the last SG entry that requires an extra TRB, we set interrupt-on-completion (IOC) bit to an already prepared TRB (i.e. HWO=1). This logic is not clean, and it's not a typical way to prepare TRB. Also, this prevents showing IOC setup in tracepoint when __dwc3_prepare_one_trb() is executed. Instead, let's look ahead when preparing TRB to know whether to set the IOC bit before the last SG entry. This requires adding a new parameter "must_interrupt" to dwc3_prepare_one_trb(). Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 72 +++++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 33 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 76c383eecfd3..0387b94b8f94 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -947,7 +947,7 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb, dma_addr_t dma, unsigned int length, unsigned int chain, unsigned int node, unsigned int stream_id, unsigned int short_not_ok, unsigned int no_interrupt, - unsigned int is_last) + unsigned int is_last, bool must_interrupt) { struct dwc3 *dwc = dep->dwc; struct usb_gadget *gadget = dwc->gadget; @@ -1034,7 +1034,7 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb, trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI; } - if ((!no_interrupt && !chain) || + if ((!no_interrupt && !chain) || must_interrupt || (dwc3_calc_trbs_left(dep) == 1)) trb->ctrl |= DWC3_TRB_CTRL_IOC; @@ -1061,10 +1061,12 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb, * @chain: should this TRB be chained to the next? * @node: only for isochronous endpoints. First TRB needs different type. * @use_bounce_buffer: set to use bounce buffer + * @must_interrupt: set to interrupt on TRB completion */ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_request *req, unsigned int trb_length, - unsigned int chain, unsigned int node, bool use_bounce_buffer) + unsigned int chain, unsigned int node, bool use_bounce_buffer, + bool must_interrupt) { struct dwc3_trb *trb; dma_addr_t dma; @@ -1091,7 +1093,21 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, req->num_trbs++; __dwc3_prepare_one_trb(dep, trb, dma, trb_length, chain, node, - stream_id, short_not_ok, no_interrupt, is_last); + stream_id, short_not_ok, no_interrupt, is_last, + must_interrupt); +} + +static bool dwc3_needs_extra_trb(struct dwc3_ep *dep, struct dwc3_request *req) +{ + unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc); + unsigned int rem = req->request.length % maxp; + + if ((req->request.length && req->request.zero && !rem && + !usb_endpoint_xfer_isoc(dep->endpoint.desc)) || + (!req->direction && rem)) + return true; + + return false; } /** @@ -1111,9 +1127,7 @@ static int dwc3_prepare_last_sg(struct dwc3_ep *dep, unsigned int rem = req->request.length % maxp; unsigned int num_trbs = 1; - if ((req->request.length && req->request.zero && !rem && - !usb_endpoint_xfer_isoc(dep->endpoint.desc)) || - (!req->direction && rem)) + if (dwc3_needs_extra_trb(dep, req)) num_trbs++; if (dwc3_calc_trbs_left(dep) < num_trbs) @@ -1124,13 +1138,13 @@ static int dwc3_prepare_last_sg(struct dwc3_ep *dep, /* Prepare a normal TRB */ if (req->direction || req->request.length) dwc3_prepare_one_trb(dep, req, entry_length, - req->needs_extra_trb, node, false); + req->needs_extra_trb, node, false, false); /* Prepare extra TRBs for ZLP and MPS OUT transfer alignment */ if ((!req->direction && !req->request.length) || req->needs_extra_trb) dwc3_prepare_one_trb(dep, req, req->direction ? 0 : maxp - rem, - false, 1, true); + false, 1, true, false); return num_trbs; } @@ -1145,6 +1159,7 @@ static int dwc3_prepare_trbs_sg(struct dwc3_ep *dep, unsigned int remaining = req->request.num_mapped_sgs - req->num_queued_sgs; unsigned int num_trbs = req->num_trbs; + bool needs_extra_trb = dwc3_needs_extra_trb(dep, req); /* * If we resume preparing the request, then get the remaining length of @@ -1155,6 +1170,7 @@ static int dwc3_prepare_trbs_sg(struct dwc3_ep *dep, for_each_sg(sg, s, remaining, i) { unsigned int trb_length; + bool must_interrupt = false; bool last_sg = false; trb_length = min_t(unsigned int, length, sg_dma_len(s)); @@ -1176,9 +1192,20 @@ static int dwc3_prepare_trbs_sg(struct dwc3_ep *dep, if (last_sg) { if (!dwc3_prepare_last_sg(dep, req, trb_length, i)) - goto out; + break; } else { - dwc3_prepare_one_trb(dep, req, trb_length, 1, i, false); + /* + * Look ahead to check if we have enough TRBs for the + * last SG entry. If not, set interrupt on this TRB to + * resume preparing the last SG entry when more TRBs are + * free. + */ + if (needs_extra_trb && dwc3_calc_trbs_left(dep) <= 2 && + sg_dma_len(sg_next(s)) >= length) + must_interrupt = true; + + dwc3_prepare_one_trb(dep, req, trb_length, 1, i, false, + must_interrupt); } /* @@ -1203,31 +1230,10 @@ static int dwc3_prepare_trbs_sg(struct dwc3_ep *dep, break; } - if (!dwc3_calc_trbs_left(dep)) + if (!dwc3_calc_trbs_left(dep) || must_interrupt) break; } - return req->num_trbs - num_trbs; - -out: - /* - * If we run out of TRBs for MPS alignment setup, then set IOC on the - * previous TRB to get notified for TRB completion to resume when more - * TRBs are available. - * - * Note: normally we shouldn't update the TRB after the HWO bit is set. - * However, the controller doesn't update its internal cache to handle - * the newly prepared TRBs until UPDATE_TRANSFER or START_TRANSFER - * command is executed. At this point, it doesn't happen yet, so we - * should be fine modifying it here. - */ - if (i) { - struct dwc3_trb *trb; - - trb = dwc3_ep_prev_trb(dep, dep->trb_enqueue); - trb->ctrl |= DWC3_TRB_CTRL_IOC; - } - return req->num_trbs - num_trbs; } -- cgit v1.2.3 From 8dbbe48c7a9989c75e39bfb2a886e163fa21660a Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Wed, 30 Sep 2020 17:44:25 -0700 Subject: usb: dwc3: gadget: Revise setting IOC when no TRB left To keep the setting of interrupt-on-completion (IOC) when out of TRBs consistent and easier to read, the caller of dwc3_prepare_one_trb() will determine if the TRB must have IOC bit set. This also reduces the number of times we need to call dwc3_calc_trbs_left(). Note that we only care about setting IOC from insufficient number of TRBs for SG and not linear requests (because we don't need to split linear requests). Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 0387b94b8f94..327cd556e8db 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1034,8 +1034,7 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb, trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI; } - if ((!no_interrupt && !chain) || must_interrupt || - (dwc3_calc_trbs_left(dep) == 1)) + if ((!no_interrupt && !chain) || must_interrupt) trb->ctrl |= DWC3_TRB_CTRL_IOC; if (chain) @@ -1169,6 +1168,7 @@ static int dwc3_prepare_trbs_sg(struct dwc3_ep *dep, length -= sg_dma_len(s); for_each_sg(sg, s, remaining, i) { + unsigned int num_trbs_left = dwc3_calc_trbs_left(dep); unsigned int trb_length; bool must_interrupt = false; bool last_sg = false; @@ -1187,7 +1187,7 @@ static int dwc3_prepare_trbs_sg(struct dwc3_ep *dep, if ((i == remaining - 1) || !length) last_sg = true; - if (!dwc3_calc_trbs_left(dep)) + if (!num_trbs_left) break; if (last_sg) { @@ -1196,12 +1196,13 @@ static int dwc3_prepare_trbs_sg(struct dwc3_ep *dep, } else { /* * Look ahead to check if we have enough TRBs for the - * last SG entry. If not, set interrupt on this TRB to - * resume preparing the last SG entry when more TRBs are + * next SG entry. If not, set interrupt on this TRB to + * resume preparing the next SG entry when more TRBs are * free. */ - if (needs_extra_trb && dwc3_calc_trbs_left(dep) <= 2 && - sg_dma_len(sg_next(s)) >= length) + if (num_trbs_left == 1 || (needs_extra_trb && + num_trbs_left <= 2 && + sg_dma_len(sg_next(s)) >= length)) must_interrupt = true; dwc3_prepare_one_trb(dep, req, trb_length, 1, i, false, @@ -1230,7 +1231,7 @@ static int dwc3_prepare_trbs_sg(struct dwc3_ep *dep, break; } - if (!dwc3_calc_trbs_left(dep) || must_interrupt) + if (must_interrupt) break; } -- cgit v1.2.3 From 346a15cdf65263d8a8dfdbc5d2702471a2dcef6c Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Wed, 30 Sep 2020 17:44:32 -0700 Subject: usb: dwc3: gadget: Keep TRBs in request order If we couldn't finish preparing all the TRBs of a request, don't prepare the next request. Otherwise, the TRBs order will be mixed up and the controller will process the wrong TRB. This is a corner case where there's not enough TRBs for a request that needs the extra TRB but there's still 1 available TRB in the pool. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 327cd556e8db..ff924656f690 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1274,7 +1274,7 @@ static int dwc3_prepare_trbs(struct dwc3_ep *dep) list_for_each_entry(req, &dep->started_list, list) { if (req->num_pending_sgs > 0) { ret = dwc3_prepare_trbs_sg(dep, req); - if (!ret) + if (!ret || req->num_pending_sgs) return ret; } @@ -1303,10 +1303,13 @@ static int dwc3_prepare_trbs(struct dwc3_ep *dep) req->num_queued_sgs = 0; req->num_pending_sgs = req->request.num_mapped_sgs; - if (req->num_pending_sgs > 0) + if (req->num_pending_sgs > 0) { ret = dwc3_prepare_trbs_sg(dep, req); - else + if (req->num_pending_sgs) + return ret; + } else { ret = dwc3_prepare_trbs_linear(dep, req); + } if (!ret || !dwc3_calc_trbs_left(dep)) return ret; -- cgit v1.2.3 From 2338484d14f3f4b3bd1f7bb416805976e7bd0cc5 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Wed, 30 Sep 2020 17:44:38 -0700 Subject: usb: dwc3: gadget: Return early if no TRB update If the transfer had already started and there's no TRB to update, then there's no need to go through __dwc3_gadget_kick_transfer(). There is no problem reissuing UPDATE_TRANSFER command. This change just saves the driver from doing a few operations. This happens when we run out of TRB and function driver still queues for more requests. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index ff924656f690..da1f2ad2ad90 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1347,6 +1347,13 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep) starting = !(dep->flags & DWC3_EP_TRANSFER_STARTED); + /* + * If there's no new TRB prepared and we don't need to restart a + * transfer, there's no need to update the transfer. + */ + if (!ret && !starting) + return ret; + req = next_request(&dep->started_list); if (!req) { dep->flags |= DWC3_EP_PENDING_REQUEST; -- cgit v1.2.3 From e0a93d98f488fafd9285ea835539ff58d3ab0438 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Tue, 29 Sep 2020 15:26:29 -0700 Subject: usb: dwc3: gadget: Support up to max stream id DWC3 IPs can use the maximum stream id (up to 2^16) specified by the USB 3.x specs. Don't limit to stream id 2^15 only. Note that this does not reflect the number of concurrent streams the controller handles internally. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index da1f2ad2ad90..78cb4db8a6e4 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2527,7 +2527,7 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep) usb_ep_set_maxpacket_limit(&dep->endpoint, size); - dep->endpoint.max_streams = 15; + dep->endpoint.max_streams = 16; dep->endpoint.ops = &dwc3_gadget_ep_ops; list_add_tail(&dep->endpoint.ep_list, &dwc->gadget->ep_list); @@ -2576,7 +2576,7 @@ static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep) size /= 3; usb_ep_set_maxpacket_limit(&dep->endpoint, size); - dep->endpoint.max_streams = 15; + dep->endpoint.max_streams = 16; dep->endpoint.ops = &dwc3_gadget_ep_ops; list_add_tail(&dep->endpoint.ep_list, &dwc->gadget->ep_list); -- cgit v1.2.3 From e2c53515b2a63828d226250fd62b38039f613977 Mon Sep 17 00:00:00 2001 From: Wan Ahmad Zainie Date: Mon, 21 Sep 2020 10:44:59 +0800 Subject: usb: dwc3: of-simple: Add compatible string for Intel Keem Bay platform Add compatible string to use this generic glue layer to support Intel Keem Bay platform's dwc3 controller. Signed-off-by: Wan Ahmad Zainie Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-of-simple.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c index 2816e4a9813a..e62ecd22b3ed 100644 --- a/drivers/usb/dwc3/dwc3-of-simple.c +++ b/drivers/usb/dwc3/dwc3-of-simple.c @@ -177,6 +177,7 @@ static const struct of_device_id of_dwc3_simple_match[] = { { .compatible = "sprd,sc9860-dwc3" }, { .compatible = "allwinner,sun50i-h6-dwc3" }, { .compatible = "hisilicon,hi3670-dwc3" }, + { .compatible = "intel,keembay-dwc3" }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(of, of_dwc3_simple_match); -- cgit v1.2.3 From 17f934024e84e73cd55dd620a48c1b9e21fac87f Mon Sep 17 00:00:00 2001 From: Amelie Delaunay Date: Wed, 9 Sep 2020 11:35:10 +0200 Subject: usb: dwc2: override PHY input signals with usb role switch support This patch adds support for usb role switch to dwc2, by using overriding control of the PHY voltage valid and ID input signals. iddig signal (ID) can be overridden: - when setting GUSBCFG_FORCEHOSTMODE, iddig input pin is overridden with 1; - when setting GUSBCFG_FORCEDEVMODE, iddig input pin is overridden with 0. avalid/bvalid/vbusvalid signals can be overridden respectively with: - GOTGCTL_AVALOEN + GOTGCTL_AVALOVAL - GOTGCTL_BVALOEN + GOTGCTL_BVALOVAL - GOTGCTL_VBVALEN + GOTGCTL_VBVALOVAL It is possible to determine valid sessions thanks to usb role switch: - if USB_ROLE_NONE then !avalid && !bvalid && !vbusvalid - if USB_ROLE_DEVICE then !avalid && bvalid && vbusvalid - if USB_ROLE_HOST then avalid && !bvalid && vbusvalid Acked-by: Minas Harutyunyan Acked-by: Martin Blumenstingl Signed-off-by: Amelie Delaunay Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/Kconfig | 1 + drivers/usb/dwc2/Makefile | 2 +- drivers/usb/dwc2/core.h | 9 +++ drivers/usb/dwc2/drd.c | 180 ++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/dwc2/gadget.c | 2 +- drivers/usb/dwc2/platform.c | 20 ++++- 6 files changed, 210 insertions(+), 4 deletions(-) create mode 100644 drivers/usb/dwc2/drd.c (limited to 'drivers/usb') diff --git a/drivers/usb/dwc2/Kconfig b/drivers/usb/dwc2/Kconfig index 16e1aa304edc..c131719367ec 100644 --- a/drivers/usb/dwc2/Kconfig +++ b/drivers/usb/dwc2/Kconfig @@ -5,6 +5,7 @@ config USB_DWC2 depends on HAS_DMA depends on USB || USB_GADGET depends on HAS_IOMEM + select USB_ROLE_SWITCH help Say Y here if your system has a Dual Role Hi-Speed USB controller based on the DesignWare HSOTG IP Core. diff --git a/drivers/usb/dwc2/Makefile b/drivers/usb/dwc2/Makefile index 440320cc20a4..2bcd6945df46 100644 --- a/drivers/usb/dwc2/Makefile +++ b/drivers/usb/dwc2/Makefile @@ -3,7 +3,7 @@ ccflags-$(CONFIG_USB_DWC2_DEBUG) += -DDEBUG ccflags-$(CONFIG_USB_DWC2_VERBOSE) += -DVERBOSE_DEBUG obj-$(CONFIG_USB_DWC2) += dwc2.o -dwc2-y := core.o core_intr.o platform.o +dwc2-y := core.o core_intr.o platform.o drd.o dwc2-y += params.o ifneq ($(filter y,$(CONFIG_USB_DWC2_HOST) $(CONFIG_USB_DWC2_DUAL_ROLE)),) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 9deff0400a92..7161344c6522 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -860,6 +860,7 @@ struct dwc2_hregs_backup { * - USB_DR_MODE_PERIPHERAL * - USB_DR_MODE_HOST * - USB_DR_MODE_OTG + * @role_sw: usb_role_switch handle * @hcd_enabled: Host mode sub-driver initialization indicator. * @gadget_enabled: Peripheral mode sub-driver initialization indicator. * @ll_hw_enabled: Status of low-level hardware resources. @@ -1054,6 +1055,7 @@ struct dwc2_hsotg { struct dwc2_core_params params; enum usb_otg_state op_state; enum usb_dr_mode dr_mode; + struct usb_role_switch *role_sw; unsigned int hcd_enabled:1; unsigned int gadget_enabled:1; unsigned int ll_hw_enabled:1; @@ -1376,6 +1378,11 @@ static inline int dwc2_is_device_mode(struct dwc2_hsotg *hsotg) return (dwc2_readl(hsotg, GINTSTS) & GINTSTS_CURMODE_HOST) == 0; } +int dwc2_drd_init(struct dwc2_hsotg *hsotg); +void dwc2_drd_suspend(struct dwc2_hsotg *hsotg); +void dwc2_drd_resume(struct dwc2_hsotg *hsotg); +void dwc2_drd_exit(struct dwc2_hsotg *hsotg); + /* * Dump core registers and SPRAM */ @@ -1392,6 +1399,7 @@ int dwc2_hsotg_resume(struct dwc2_hsotg *dwc2); int dwc2_gadget_init(struct dwc2_hsotg *hsotg); void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2, bool reset); +void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg); void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg); void dwc2_hsotg_disconnect(struct dwc2_hsotg *dwc2); int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode); @@ -1417,6 +1425,7 @@ static inline int dwc2_gadget_init(struct dwc2_hsotg *hsotg) { return 0; } static inline void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2, bool reset) {} +static inline void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg) {} static inline void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg) {} static inline void dwc2_hsotg_disconnect(struct dwc2_hsotg *dwc2) {} static inline int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, diff --git a/drivers/usb/dwc2/drd.c b/drivers/usb/dwc2/drd.c new file mode 100644 index 000000000000..2d4176f5788e --- /dev/null +++ b/drivers/usb/dwc2/drd.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * drd.c - DesignWare USB2 DRD Controller Dual-role support + * + * Copyright (C) 2020 STMicroelectronics + * + * Author(s): Amelie Delaunay + */ + +#include +#include +#include +#include "core.h" + +static void dwc2_ovr_init(struct dwc2_hsotg *hsotg) +{ + unsigned long flags; + u32 gotgctl; + + spin_lock_irqsave(&hsotg->lock, flags); + + gotgctl = dwc2_readl(hsotg, GOTGCTL); + gotgctl |= GOTGCTL_BVALOEN | GOTGCTL_AVALOEN | GOTGCTL_VBVALOEN; + gotgctl |= GOTGCTL_DBNCE_FLTR_BYPASS; + gotgctl &= ~(GOTGCTL_BVALOVAL | GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL); + dwc2_writel(hsotg, gotgctl, GOTGCTL); + + dwc2_force_mode(hsotg, false); + + spin_unlock_irqrestore(&hsotg->lock, flags); +} + +static int dwc2_ovr_avalid(struct dwc2_hsotg *hsotg, bool valid) +{ + u32 gotgctl = dwc2_readl(hsotg, GOTGCTL); + + /* Check if A-Session is already in the right state */ + if ((valid && (gotgctl & GOTGCTL_ASESVLD)) || + (!valid && !(gotgctl & GOTGCTL_ASESVLD))) + return -EALREADY; + + if (valid) + gotgctl |= GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL; + else + gotgctl &= ~(GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL); + dwc2_writel(hsotg, gotgctl, GOTGCTL); + + return 0; +} + +static int dwc2_ovr_bvalid(struct dwc2_hsotg *hsotg, bool valid) +{ + u32 gotgctl = dwc2_readl(hsotg, GOTGCTL); + + /* Check if B-Session is already in the right state */ + if ((valid && (gotgctl & GOTGCTL_BSESVLD)) || + (!valid && !(gotgctl & GOTGCTL_BSESVLD))) + return -EALREADY; + + if (valid) + gotgctl |= GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL; + else + gotgctl &= ~(GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL); + dwc2_writel(hsotg, gotgctl, GOTGCTL); + + return 0; +} + +static int dwc2_drd_role_sw_set(struct usb_role_switch *sw, enum usb_role role) +{ + struct dwc2_hsotg *hsotg = usb_role_switch_get_drvdata(sw); + unsigned long flags; + int already = 0; + + /* Skip session not in line with dr_mode */ + if ((role == USB_ROLE_DEVICE && hsotg->dr_mode == USB_DR_MODE_HOST) || + (role == USB_ROLE_HOST && hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)) + return -EINVAL; + +#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \ + IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) + /* Skip session if core is in test mode */ + if (role == USB_ROLE_NONE && hsotg->test_mode) { + dev_dbg(hsotg->dev, "Core is in test mode\n"); + return -EBUSY; + } +#endif + + spin_lock_irqsave(&hsotg->lock, flags); + + if (role == USB_ROLE_HOST) { + already = dwc2_ovr_avalid(hsotg, true); + } else if (role == USB_ROLE_DEVICE) { + already = dwc2_ovr_bvalid(hsotg, true); + /* This clear DCTL.SFTDISCON bit */ + dwc2_hsotg_core_connect(hsotg); + } else { + if (dwc2_is_device_mode(hsotg)) { + if (!dwc2_ovr_bvalid(hsotg, false)) + /* This set DCTL.SFTDISCON bit */ + dwc2_hsotg_core_disconnect(hsotg); + } else { + dwc2_ovr_avalid(hsotg, false); + } + } + + spin_unlock_irqrestore(&hsotg->lock, flags); + + if (!already && hsotg->dr_mode == USB_DR_MODE_OTG) + /* This will raise a Connector ID Status Change Interrupt */ + dwc2_force_mode(hsotg, role == USB_ROLE_HOST); + + dev_dbg(hsotg->dev, "%s-session valid\n", + role == USB_ROLE_NONE ? "No" : + role == USB_ROLE_HOST ? "A" : "B"); + + return 0; +} + +int dwc2_drd_init(struct dwc2_hsotg *hsotg) +{ + struct usb_role_switch_desc role_sw_desc = {0}; + struct usb_role_switch *role_sw; + int ret; + + if (!device_property_read_bool(hsotg->dev, "usb-role-switch")) + return 0; + + role_sw_desc.driver_data = hsotg; + role_sw_desc.fwnode = dev_fwnode(hsotg->dev); + role_sw_desc.set = dwc2_drd_role_sw_set; + role_sw_desc.allow_userspace_control = true; + + role_sw = usb_role_switch_register(hsotg->dev, &role_sw_desc); + if (IS_ERR(role_sw)) { + ret = PTR_ERR(role_sw); + dev_err(hsotg->dev, + "failed to register role switch: %d\n", ret); + return ret; + } + + hsotg->role_sw = role_sw; + + /* Enable override and initialize values */ + dwc2_ovr_init(hsotg); + + return 0; +} + +void dwc2_drd_suspend(struct dwc2_hsotg *hsotg) +{ + u32 gintsts, gintmsk; + + if (hsotg->role_sw && !hsotg->params.external_id_pin_ctl) { + gintmsk = dwc2_readl(hsotg, GINTMSK); + gintmsk &= ~GINTSTS_CONIDSTSCHNG; + dwc2_writel(hsotg, gintmsk, GINTMSK); + gintsts = dwc2_readl(hsotg, GINTSTS); + dwc2_writel(hsotg, gintsts | GINTSTS_CONIDSTSCHNG, GINTSTS); + } +} + +void dwc2_drd_resume(struct dwc2_hsotg *hsotg) +{ + u32 gintsts, gintmsk; + + if (hsotg->role_sw && !hsotg->params.external_id_pin_ctl) { + gintsts = dwc2_readl(hsotg, GINTSTS); + dwc2_writel(hsotg, gintsts | GINTSTS_CONIDSTSCHNG, GINTSTS); + gintmsk = dwc2_readl(hsotg, GINTMSK); + gintmsk |= GINTSTS_CONIDSTSCHNG; + dwc2_writel(hsotg, gintmsk, GINTMSK); + } +} + +void dwc2_drd_exit(struct dwc2_hsotg *hsotg) +{ + if (hsotg->role_sw) + usb_role_switch_unregister(hsotg->role_sw); +} diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 5b9d23991c99..16c5f976f141 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -3530,7 +3530,7 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, dwc2_readl(hsotg, DOEPCTL0)); } -static void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg) +void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg) { /* set the soft-disconnect bit */ dwc2_set_bit(hsotg, DCTL, DCTL_SFTDISCON); diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index c8844aea5607..e2820676beb1 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -323,6 +323,8 @@ static int dwc2_driver_remove(struct platform_device *dev) if (hsotg->gadget_enabled) dwc2_hsotg_remove(hsotg); + dwc2_drd_exit(hsotg); + if (hsotg->params.activate_stm_id_vb_detection) regulator_disable(hsotg->usb33d); @@ -542,10 +544,17 @@ static int dwc2_driver_probe(struct platform_device *dev) dwc2_writel(hsotg, ggpio, GGPIO); } + retval = dwc2_drd_init(hsotg); + if (retval) { + if (retval != -EPROBE_DEFER) + dev_err(hsotg->dev, "failed to initialize dual-role\n"); + goto error_init; + } + if (hsotg->dr_mode != USB_DR_MODE_HOST) { retval = dwc2_gadget_init(hsotg); if (retval) - goto error_init; + goto error_drd; hsotg->gadget_enabled = 1; } @@ -571,7 +580,7 @@ static int dwc2_driver_probe(struct platform_device *dev) if (retval) { if (hsotg->gadget_enabled) dwc2_hsotg_remove(hsotg); - goto error_init; + goto error_drd; } hsotg->hcd_enabled = 1; } @@ -603,6 +612,9 @@ error_debugfs: dwc2_debugfs_exit(hsotg); if (hsotg->hcd_enabled) dwc2_hcd_remove(hsotg); +error_drd: + dwc2_drd_exit(hsotg); + error_init: if (hsotg->params.activate_stm_id_vb_detection) regulator_disable(hsotg->usb33d); @@ -621,6 +633,8 @@ static int __maybe_unused dwc2_suspend(struct device *dev) if (is_device_mode) dwc2_hsotg_suspend(dwc2); + dwc2_drd_suspend(dwc2); + if (dwc2->params.activate_stm_id_vb_detection) { unsigned long flags; u32 ggpio, gotgctl; @@ -701,6 +715,8 @@ static int __maybe_unused dwc2_resume(struct device *dev) /* Need to restore FORCEDEVMODE/FORCEHOSTMODE */ dwc2_force_dr_mode(dwc2); + dwc2_drd_resume(dwc2); + if (dwc2_is_device_mode(dwc2)) ret = dwc2_hsotg_resume(dwc2); -- cgit v1.2.3 From d58ba480285a1741ec65c3e470074d7ba31c7e60 Mon Sep 17 00:00:00 2001 From: Amelie Delaunay Date: Wed, 9 Sep 2020 11:35:11 +0200 Subject: usb: dwc2: don't use ID/Vbus detection if usb-role-switch on STM32MP15 SoCs If usb-role-switch is present in the device tree, it means that ID and Vbus signals are not connected to the OTG controller but to an external component (GPIOs, Type-C controller). In this configuration, usb role switch is used to force valid sessions on STM32MP15 SoCs. Acked-by: Minas Harutyunyan Acked-by: Martin Blumenstingl Signed-off-by: Amelie Delaunay Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/params.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index 4a454cca8d77..267543c3dc38 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -185,7 +185,7 @@ static void dwc2_set_stm32mp15_hsotg_params(struct dwc2_hsotg *hsotg) struct dwc2_core_params *p = &hsotg->params; p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE; - p->activate_stm_id_vb_detection = true; + p->activate_stm_id_vb_detection = !device_property_read_bool(hsotg->dev, "usb-role-switch"); p->host_rx_fifo_size = 440; p->host_nperio_tx_fifo_size = 256; p->host_perio_tx_fifo_size = 256; -- cgit v1.2.3 From b2c586eb07efab982419f32b7c3bd96829bc8bcd Mon Sep 17 00:00:00 2001 From: Minas Harutyunyan Date: Thu, 24 Sep 2020 18:08:39 +0400 Subject: usb: dwc2: Fix INTR OUT transfers in DDMA mode. In DDMA mode if INTR OUT transfers mps not multiple of 4 then single packet corresponds to single descriptor. Descriptor limit set to mps and desc chain limit set to mps * MAX_DMA_DESC_NUM_GENERIC. On that descriptors complete, to calculate transfer size should be considered correction value for each descriptor. In start request function, if "continue" is true then dma buffer address should be incremmented by offset for all type of transfers, not only for Control DATA_OUT transfers. Fixes: cf77b5fb9b394 ("usb: dwc2: gadget: Transfer length limit checking for DDMA") Fixes: e02f9aa6119e0 ("usb: dwc2: gadget: EP 0 specific DDMA programming") Fixes: aa3e8bc81311e ("usb: dwc2: gadget: DDMA transfer start and complete") Signed-off-by: Minas Harutyunyan Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 16c5f976f141..0a0d11151cfb 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -713,8 +713,11 @@ static u32 dwc2_hsotg_read_frameno(struct dwc2_hsotg *hsotg) */ static unsigned int dwc2_gadget_get_chain_limit(struct dwc2_hsotg_ep *hs_ep) { + const struct usb_endpoint_descriptor *ep_desc = hs_ep->ep.desc; int is_isoc = hs_ep->isochronous; unsigned int maxsize; + u32 mps = hs_ep->ep.maxpacket; + int dir_in = hs_ep->dir_in; if (is_isoc) maxsize = (hs_ep->dir_in ? DEV_DMA_ISOC_TX_NBYTES_LIMIT : @@ -723,6 +726,11 @@ static unsigned int dwc2_gadget_get_chain_limit(struct dwc2_hsotg_ep *hs_ep) else maxsize = DEV_DMA_NBYTES_LIMIT * MAX_DMA_DESC_NUM_GENERIC; + /* Interrupt OUT EP with mps not multiple of 4 */ + if (hs_ep->index) + if (usb_endpoint_xfer_int(ep_desc) && !dir_in && (mps % 4)) + maxsize = mps * MAX_DMA_DESC_NUM_GENERIC; + return maxsize; } @@ -738,11 +746,14 @@ static unsigned int dwc2_gadget_get_chain_limit(struct dwc2_hsotg_ep *hs_ep) * Isochronous - descriptor rx/tx bytes bitfield limit, * Control In/Bulk/Interrupt - multiple of mps. This will allow to not * have concatenations from various descriptors within one packet. + * Interrupt OUT - if mps not multiple of 4 then a single packet corresponds + * to a single descriptor. * * Selects corresponding mask for RX/TX bytes as well. */ static u32 dwc2_gadget_get_desc_params(struct dwc2_hsotg_ep *hs_ep, u32 *mask) { + const struct usb_endpoint_descriptor *ep_desc = hs_ep->ep.desc; u32 mps = hs_ep->ep.maxpacket; int dir_in = hs_ep->dir_in; u32 desc_size = 0; @@ -766,6 +777,13 @@ static u32 dwc2_gadget_get_desc_params(struct dwc2_hsotg_ep *hs_ep, u32 *mask) desc_size -= desc_size % mps; } + /* Interrupt OUT EP with mps not multiple of 4 */ + if (hs_ep->index) + if (usb_endpoint_xfer_int(ep_desc) && !dir_in && (mps % 4)) { + desc_size = mps; + *mask = DEV_DMA_NBYTES_MASK; + } + return desc_size; } @@ -1123,13 +1141,7 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg, length += (mps - (length % mps)); } - /* - * If more data to send, adjust DMA for EP0 out data stage. - * ureq->dma stays unchanged, hence increment it by already - * passed passed data count before starting new transaction. - */ - if (!index && hsotg->ep0_state == DWC2_EP0_DATA_OUT && - continuing) + if (continuing) offset = ureq->actual; /* Fill DDMA chain entries */ @@ -2320,22 +2332,36 @@ static void dwc2_hsotg_change_ep_iso_parity(struct dwc2_hsotg *hsotg, */ static unsigned int dwc2_gadget_get_xfersize_ddma(struct dwc2_hsotg_ep *hs_ep) { + const struct usb_endpoint_descriptor *ep_desc = hs_ep->ep.desc; struct dwc2_hsotg *hsotg = hs_ep->parent; unsigned int bytes_rem = 0; + unsigned int bytes_rem_correction = 0; struct dwc2_dma_desc *desc = hs_ep->desc_list; int i; u32 status; + u32 mps = hs_ep->ep.maxpacket; + int dir_in = hs_ep->dir_in; if (!desc) return -EINVAL; + /* Interrupt OUT EP with mps not multiple of 4 */ + if (hs_ep->index) + if (usb_endpoint_xfer_int(ep_desc) && !dir_in && (mps % 4)) + bytes_rem_correction = 4 - (mps % 4); + for (i = 0; i < hs_ep->desc_count; ++i) { status = desc->status; bytes_rem += status & DEV_DMA_NBYTES_MASK; + bytes_rem -= bytes_rem_correction; if (status & DEV_DMA_STS_MASK) dev_err(hsotg->dev, "descriptor %d closed with %x\n", i, status & DEV_DMA_STS_MASK); + + if (status & DEV_DMA_L) + break; + desc++; } -- cgit v1.2.3