From 5c73536738e1aa411b127d84dff41a5e60a4e610 Mon Sep 17 00:00:00 2001 From: Kever Yang Date: Fri, 10 Mar 2017 12:05:14 +0800 Subject: [PATCH 1/8] usb: dwc2: add support for external vbus supply Some board do not use the dwc2 internal VBUS_DRV signal, but use a gpio pin to enable the 5.0V VBUS power, add interface to enable the power in dwc2 driver. Signed-off-by: Kever Yang Signed-off-by: Simon Glass --- drivers/usb/host/dwc2.c | 43 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c index d253b946f3..ae7d8fb1da 100644 --- a/drivers/usb/host/dwc2.c +++ b/drivers/usb/host/dwc2.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "dwc2.h" @@ -159,6 +160,33 @@ static void dwc_otg_core_reset(struct dwc2_core_regs *regs) mdelay(100); } +#if defined(CONFIG_DM_USB) && defined(CONFIG_DM_REGULATOR) +static int dwc_vbus_supply_init(struct udevice *dev) +{ + struct udevice *vbus_supply; + int ret; + + ret = device_get_supply_regulator(dev, "vbus-supply", &vbus_supply); + if (ret) { + debug("%s: No vbus supply\n", dev->name); + return 0; + } + + ret = regulator_set_enable(vbus_supply, true); + if (ret) { + error("Error enabling vbus supply\n"); + return ret; + } + + return 0; +} +#else +static int dwc_vbus_supply_init(struct udevice *dev) +{ + return 0; +} +#endif + /* * This function initializes the DWC_otg controller registers for * host mode. @@ -167,10 +195,12 @@ static void dwc_otg_core_reset(struct dwc2_core_regs *regs) * request queues. Host channels are reset to ensure that they are ready for * performing transfers. * + * @param dev USB Device (NULL if driver model is not being used) * @param regs Programming view of DWC_otg controller * */ -static void dwc_otg_core_host_init(struct dwc2_core_regs *regs) +static void dwc_otg_core_host_init(struct udevice *dev, + struct dwc2_core_regs *regs) { uint32_t nptxfifosize = 0; uint32_t ptxfifosize = 0; @@ -248,6 +278,9 @@ static void dwc_otg_core_host_init(struct dwc2_core_regs *regs) writel(hprt0, ®s->hprt0); } } + + if (dev) + dwc_vbus_supply_init(dev); } /* @@ -1048,7 +1081,7 @@ int _submit_int_msg(struct dwc2_priv *priv, struct usb_device *dev, } } -static int dwc2_init_common(struct dwc2_priv *priv) +static int dwc2_init_common(struct udevice *dev, struct dwc2_priv *priv) { struct dwc2_core_regs *regs = priv->regs; uint32_t snpsid; @@ -1070,7 +1103,7 @@ static int dwc2_init_common(struct dwc2_priv *priv) #endif dwc_otg_core_init(priv); - dwc_otg_core_host_init(regs); + dwc_otg_core_host_init(dev, regs); clrsetbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | DWC2_HPRT0_PRTCONNDET | DWC2_HPRT0_PRTENCHNG | @@ -1143,7 +1176,7 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) if (board_usb_init(index, USB_INIT_HOST)) return -1; - return dwc2_init_common(priv); + return dwc2_init_common(NULL, priv); } int usb_lowlevel_stop(int index) @@ -1214,7 +1247,7 @@ static int dwc2_usb_probe(struct udevice *dev) bus_priv->desc_before_addr = true; - return dwc2_init_common(priv); + return dwc2_init_common(dev, priv); } static int dwc2_usb_remove(struct udevice *dev) From 12d0b8f5f07b76d7447d60f7528d578233553909 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 22 Feb 2017 17:12:39 +0200 Subject: [PATCH 2/8] usb: gadget: g_dnl: hold maximum string descriptor A USB String descriptor can be up to 255 characters long and it's not NULL terminated according to the USB spec. This means our MAX_STRING_SERIAL should be 256 (to cope with NULL terminator). Signed-off-by: Felipe Balbi --- drivers/usb/gadget/g_dnl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/g_dnl.c b/drivers/usb/gadget/g_dnl.c index 4ba7c1da7c..fcedb554c4 100644 --- a/drivers/usb/gadget/g_dnl.c +++ b/drivers/usb/gadget/g_dnl.c @@ -36,7 +36,7 @@ #define STRING_USBDOWN 2 /* Index of String serial */ #define STRING_SERIAL 3 -#define MAX_STRING_SERIAL 32 +#define MAX_STRING_SERIAL 256 /* Number of supported configurations */ #define CONFIGURATION_NUMBER 1 From 842778a091047b0c868efa12229633959f711152 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 22 Feb 2017 17:12:40 +0200 Subject: [PATCH 3/8] usb: gadget: g_dnl: only set iSerialNumber if we have a serial# We don't want to claim that we support a serial number string and later return nothing. Because of that, if g_dnl_serial is an empty string, let's skip setting iSerialNumber to a valid number. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/g_dnl.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/g_dnl.c b/drivers/usb/gadget/g_dnl.c index fcedb554c4..4cc4438fdb 100644 --- a/drivers/usb/gadget/g_dnl.c +++ b/drivers/usb/gadget/g_dnl.c @@ -224,12 +224,14 @@ static int g_dnl_bind(struct usb_composite_dev *cdev) g_dnl_string_defs[1].id = id; device_desc.iProduct = id; - id = usb_string_id(cdev); - if (id < 0) - return id; + if (strlen(g_dnl_serial)) { + id = usb_string_id(cdev); + if (id < 0) + return id; - g_dnl_string_defs[2].id = id; - device_desc.iSerialNumber = id; + g_dnl_string_defs[2].id = id; + device_desc.iSerialNumber = id; + } g_dnl_bind_fixup(&device_desc, cdev->driver->name); ret = g_dnl_config_register(cdev); From 207835b13feeae15db0555574d89352a4e5379a4 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 22 Feb 2017 17:12:41 +0200 Subject: [PATCH 4/8] usb: gadget: g_dnl: don't set iProduct nor iSerialNumber Both these numbers are calculated in runtime and dynamically assigned to the device descriptor during bind(). Signed-off-by: Felipe Balbi --- drivers/usb/gadget/g_dnl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/g_dnl.c b/drivers/usb/gadget/g_dnl.c index 4cc4438fdb..d4bee9b03e 100644 --- a/drivers/usb/gadget/g_dnl.c +++ b/drivers/usb/gadget/g_dnl.c @@ -62,8 +62,8 @@ static struct usb_device_descriptor device_desc = { .idVendor = __constant_cpu_to_le16(CONFIG_G_DNL_VENDOR_NUM), .idProduct = __constant_cpu_to_le16(CONFIG_G_DNL_PRODUCT_NUM), - .iProduct = STRING_PRODUCT, - .iSerialNumber = STRING_SERIAL, + /* .iProduct = DYNAMIC */ + /* .iSerialNumber = DYNAMIC */ .bNumConfigurations = 1, }; From b7bf4a95922c3e1a4974aa34ebb714ac2eb89937 Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Thu, 6 Apr 2017 16:58:52 +0200 Subject: [PATCH 5/8] usb: dwc3: ensure consistent types for dwc3_flush_cache The dwc3_flush_cache() call was declared and used inconsistently: * The declaration assumed 'int' for addresses (a potential issue when running in a LP64 memory model). * The invocation cast the address to 'long'. This change ensures that both the declaration and usage of this function consistently uses 'uintptr_t' for correct behaviour even when the allocated buffers (to be flushed) reside outside of the lower 32bits of memory. Signed-off-by: Philipp Tomsich --- drivers/usb/dwc3/ep0.c | 10 +++++----- drivers/usb/dwc3/gadget.c | 10 +++++----- drivers/usb/dwc3/io.h | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 12b133f93e..e61d98046f 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -81,8 +81,8 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, trb->ctrl |= (DWC3_TRB_CTRL_IOC | DWC3_TRB_CTRL_LST); - dwc3_flush_cache((long)buf_dma, len); - dwc3_flush_cache((long)trb, sizeof(*trb)); + dwc3_flush_cache((uintptr_t)buf_dma, len); + dwc3_flush_cache((uintptr_t)trb, sizeof(*trb)); if (chain) return 0; @@ -790,7 +790,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, if (!r) return; - dwc3_flush_cache((long)trb, sizeof(*trb)); + dwc3_flush_cache((uintptr_t)trb, sizeof(*trb)); status = DWC3_TRB_SIZE_TRBSTS(trb->size); if (status == DWC3_TRBSTS_SETUP_PENDING) { @@ -821,7 +821,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, ur->actual += transferred; trb++; - dwc3_flush_cache((long)trb, sizeof(*trb)); + dwc3_flush_cache((uintptr_t)trb, sizeof(*trb)); length = trb->size & DWC3_TRB_SIZE_MASK; ep0->free_slot = 0; @@ -831,7 +831,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, maxp); transferred = min_t(u32, ur->length - transferred, transfer_size - length); - dwc3_flush_cache((long)dwc->ep0_bounce, DWC3_EP0_BOUNCE_SIZE); + dwc3_flush_cache((uintptr_t)dwc->ep0_bounce, DWC3_EP0_BOUNCE_SIZE); memcpy(buf, dwc->ep0_bounce, transferred); } else { transferred = ur->length - length; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 1156662ae8..8c53cc0b8a 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -244,7 +244,7 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, list_del(&req->list); req->trb = NULL; - dwc3_flush_cache((long)req->request.dma, req->request.length); + dwc3_flush_cache((uintptr_t)req->request.dma, req->request.length); if (req->request.status == -EINPROGRESS) req->request.status = status; @@ -771,8 +771,8 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, trb->ctrl |= DWC3_TRB_CTRL_HWO; - dwc3_flush_cache((long)dma, length); - dwc3_flush_cache((long)trb, sizeof(*trb)); + dwc3_flush_cache((uintptr_t)dma, length); + dwc3_flush_cache((uintptr_t)trb, sizeof(*trb)); } /* @@ -1769,7 +1769,7 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, slot %= DWC3_TRB_NUM; trb = &dep->trb_pool[slot]; - dwc3_flush_cache((long)trb, sizeof(*trb)); + dwc3_flush_cache((uintptr_t)trb, sizeof(*trb)); __dwc3_cleanup_done_trbs(dwc, dep, req, trb, event, status); dwc3_gadget_giveback(dep, req, status); @@ -2670,7 +2670,7 @@ void dwc3_gadget_uboot_handle_interrupt(struct dwc3 *dwc) for (i = 0; i < dwc->num_event_buffers; i++) { evt = dwc->ev_buffs[i]; - dwc3_flush_cache((long)evt->buf, evt->length); + dwc3_flush_cache((uintptr_t)evt->buf, evt->length); } dwc3_thread_interrupt(0, dwc); diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h index 0d9fa220e9..810980f613 100644 --- a/drivers/usb/dwc3/io.h +++ b/drivers/usb/dwc3/io.h @@ -48,7 +48,7 @@ static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value) writel(value, base + offs); } -static inline void dwc3_flush_cache(int addr, int length) +static inline void dwc3_flush_cache(uintptr_t addr, int length) { flush_dcache_range(addr, addr + ROUND(length, CACHELINE_SIZE)); } From 889239d6b5c15c82d507498ded5e3b8fea2d44cf Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Thu, 6 Apr 2017 16:58:53 +0200 Subject: [PATCH 6/8] usb: dwc3: gadget: make cache-maintenance on event buffers more robust Merely using dma_alloc_coherent does not ensure that there is no stale data left in the caches for the allocated DMA buffer (i.e. that the affected cacheline may still be dirty). The original code was doing the following (on AArch64, which translates a 'flush' into a 'clean + invalidate'): # during initialisation: 1. allocate buffers via memalign => buffers may still be modified (cached, dirty) # during interrupt processing 2. clean + invalidate buffers => may commit stale data from a modified cacheline 3. read from buffers This could lead to garbage info being written to buffers before reading them during even-processing. To make the event processing more robust, we use the following sequence for the cache-maintenance: # during initialisation: 1. allocate buffers via memalign 2. clean + invalidate buffers (we only need the 'invalidate' part, but dwc3_flush_cache() always performs a 'clean + invalidate') # during interrupt processing 3. read the buffers (we know these lines are not cached, due to the previous invalidation and no other code touching them in-between) 4. clean + invalidate buffers => writes back any modification we may have made during event processing and ensures that the lines are not in the cache the next time we enter interrupt processing Note that with the original sequence, we observe reproducible (depending on the cache state: i.e. running dhcp/usb start before will upset caches to get us around this) issues in the event processing (a fatal synchronous abort in dwc3_gadget_uboot_handle_interrupt on the first time interrupt handling is invoked) when running USB mass storage emulation on our RK3399-Q7 with data-caches on. Signed-off-by: Philipp Tomsich --- drivers/usb/dwc3/core.c | 2 ++ drivers/usb/dwc3/gadget.c | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 85cc96ac87..87b9c87edf 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -122,6 +122,8 @@ static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc, if (!evt->buf) return ERR_PTR(-ENOMEM); + dwc3_flush_cache((uintptr_t)evt->buf, evt->length); + return evt; } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 8c53cc0b8a..e065c5aeb3 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2668,11 +2668,12 @@ void dwc3_gadget_uboot_handle_interrupt(struct dwc3 *dwc) int i; struct dwc3_event_buffer *evt; + dwc3_thread_interrupt(0, dwc); + + /* Clean + Invalidate the buffers after touching them */ for (i = 0; i < dwc->num_event_buffers; i++) { evt = dwc->ev_buffs[i]; dwc3_flush_cache((uintptr_t)evt->buf, evt->length); } - - dwc3_thread_interrupt(0, dwc); } } From 57ca63b86e5987029cd4970890aa28602ba50856 Mon Sep 17 00:00:00 2001 From: Eddie Cai Date: Thu, 6 Apr 2017 11:37:04 +0800 Subject: [PATCH 7/8] usb: dwc2: invalidate the dcache before starting the DMA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We should invalidate the dcache before starting the DMA. In case there are any dirty lines from the DMA buffer in the cache, subsequent cache-line replacements may corrupt the buffer in memory while the DMA is still going on. Cache-line replacement can happen if the CPU tries to bring some other memory locations into the cache while the DMA is going on. Signed-off-by: Eddie Cai Reviewed-by: Stefan BrĂ¼ns --- drivers/usb/host/dwc2.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c index ae7d8fb1da..0e5df15a0d 100644 --- a/drivers/usb/host/dwc2.c +++ b/drivers/usb/host/dwc2.c @@ -817,12 +817,19 @@ static int transfer_chunk(struct dwc2_hc_regs *hc_regs, void *aligned_buffer, (*pid << DWC2_HCTSIZ_PID_OFFSET), &hc_regs->hctsiz); - if (!in && xfer_len) { - memcpy(aligned_buffer, buffer, xfer_len); - - flush_dcache_range((unsigned long)aligned_buffer, - (unsigned long)aligned_buffer + - roundup(xfer_len, ARCH_DMA_MINALIGN)); + if (xfer_len) { + if (in) { + invalidate_dcache_range( + (uintptr_t)aligned_buffer, + (uintptr_t)aligned_buffer + + roundup(xfer_len, ARCH_DMA_MINALIGN)); + } else { + memcpy(aligned_buffer, buffer, xfer_len); + flush_dcache_range( + (uintptr_t)aligned_buffer, + (uintptr_t)aligned_buffer + + roundup(xfer_len, ARCH_DMA_MINALIGN)); + } } writel(phys_to_bus((unsigned long)aligned_buffer), &hc_regs->hcdma); From 1e5eca7d422563df91a54e270c297b0c465f2659 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Mon, 10 Apr 2017 18:23:11 -0700 Subject: [PATCH 8/8] usb: return 0 from usb_stor_get_info even if removable media This fixes a regression caused by commit 07b2b78ce4bc8ae25e066c65245eaf58c0d9a67c dm: usb: Convert USB storage to use driver-model for block devs which caused part_init to be called when it was not previously. Without this patch, the following happens when a USB sd card reader is used. => usb start starting USB... USB0: Port not available. USB1: USB EHCI 1.00 scanning bus 1 for devices... 3 USB Device(s) found scanning usb for storage devices... Device NOT ready Request Sense returned 02 3A 00 ### ERROR ### Please RESET the board ### This happens because dev_desc->blksz is 0. Signed-off-by: Troy Kisky --- common/usb_storage.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/common/usb_storage.c b/common/usb_storage.c index b524a15e2b..83279c449d 100644 --- a/common/usb_storage.c +++ b/common/usb_storage.c @@ -1440,10 +1440,8 @@ int usb_stor_get_info(struct usb_device *dev, struct us_data *ss, " Request Sense returned %02X %02X %02X\n", pccb->sense_buf[2], pccb->sense_buf[12], pccb->sense_buf[13]); - if (dev_desc->removable == 1) { + if (dev_desc->removable == 1) dev_desc->type = perq; - return 1; - } return 0; } pccb->pdata = (unsigned char *)cap;