* [PATCH 00/10] Add Raspberry Pi USB support @ 2019-12-20 14:32 Sascha Hauer 2019-12-20 14:32 ` [PATCH 01/10] usb: Make timeout unit clear Sascha Hauer ` (9 more replies) 0 siblings, 10 replies; 24+ messages in thread From: Sascha Hauer @ 2019-12-20 14:32 UTC (permalink / raw) To: Barebox List This series adds USB host support for the Raspberry Pi. The dwc2 USB host driver is ported from U-Boot and tested on a Raspberry Pi 3b with the USB ethernet adapter found on that board. Finally we have ethernet support on the Raspberry Pi \o/ Sascha Sascha Hauer (10): usb: Make timeout unit clear of: Add of_bus_n_xxx_cells() device: Introduce dma_offset of: Read dma_offset from device tree usb: Add usbroothubdes.h regulator: add function to get regulator by its name rpi: Enable USB Power domain during startup usb: Forward error code from usb_set_configuration usb: Add dwc2 host driver ARM: rpi_defconfig: Enable networking support arch/arm/boards/raspberry-pi/rpi-common.c | 10 + arch/arm/configs/rpi_defconfig | 13 + arch/arm/cpu/mmu-common.c | 33 +- arch/arm/cpu/mmu.c | 4 + arch/arm/cpu/mmu_64.c | 4 + drivers/of/address.c | 109 ++ drivers/of/base.c | 46 +- drivers/of/platform.c | 18 + drivers/regulator/core.c | 30 +- drivers/usb/core/usb.c | 18 +- drivers/usb/host/Kconfig | 3 + drivers/usb/host/Makefile | 1 + drivers/usb/host/dwc2.c | 1132 +++++++++++++++++++++ drivers/usb/host/dwc2.h | 778 ++++++++++++++ include/driver.h | 2 + include/of.h | 12 + include/of_address.h | 9 + include/regulator.h | 1 + include/usb/usb.h | 8 +- include/usb/usbroothubdes.h | 128 +++ 20 files changed, 2326 insertions(+), 33 deletions(-) create mode 100644 drivers/usb/host/dwc2.c create mode 100644 drivers/usb/host/dwc2.h create mode 100644 include/usb/usbroothubdes.h -- 2.24.0 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH 01/10] usb: Make timeout unit clear 2019-12-20 14:32 [PATCH 00/10] Add Raspberry Pi USB support Sascha Hauer @ 2019-12-20 14:32 ` Sascha Hauer 2019-12-20 14:32 ` [PATCH 02/10] of: Add of_bus_n_xxx_cells() Sascha Hauer ` (8 subsequent siblings) 9 siblings, 0 replies; 24+ messages in thread From: Sascha Hauer @ 2019-12-20 14:32 UTC (permalink / raw) To: Barebox List The usb_*_msg() functions take a timeout parameter. Make clear which unit is expected by adding a _ms suffix to the variable name. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- drivers/usb/core/usb.c | 8 ++++---- include/usb/usb.h | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index d29cd1328b..ad3bacf236 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -593,7 +593,7 @@ int usb_submit_int_msg(struct usb_device *dev, unsigned long pipe, int usb_control_msg(struct usb_device *dev, unsigned int pipe, unsigned char request, unsigned char requesttype, unsigned short value, unsigned short index, - void *data, unsigned short size, int timeout) + void *data, unsigned short size, int timeout_ms) { struct usb_host *host = dev->host; int ret; @@ -615,7 +615,7 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe, dev->status = USB_ST_NOT_PROC; /*not yet processed */ ret = host->submit_control_msg(dev, pipe, data, size, setup_packet, - timeout); + timeout_ms); usb_host_release(host); @@ -631,7 +631,7 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe, * synchronous behavior */ int usb_bulk_msg(struct usb_device *dev, unsigned int pipe, - void *data, int len, int *actual_length, int timeout) + void *data, int len, int *actual_length, int timeout_ms) { struct usb_host *host = dev->host; int ret; @@ -644,7 +644,7 @@ int usb_bulk_msg(struct usb_device *dev, unsigned int pipe, return ret; dev->status = USB_ST_NOT_PROC; /* not yet processed */ - ret = host->submit_bulk_msg(dev, pipe, data, len, timeout); + ret = host->submit_bulk_msg(dev, pipe, data, len, timeout_ms); usb_host_release(host); diff --git a/include/usb/usb.h b/include/usb/usb.h index d39de71aff..95dedfd5b7 100644 --- a/include/usb/usb.h +++ b/include/usb/usb.h @@ -140,9 +140,9 @@ struct usb_host { int (*init)(struct usb_host *); int (*exit)(struct usb_host *); int (*submit_bulk_msg)(struct usb_device *dev, unsigned long pipe, - void *buffer, int transfer_len, int timeout); + void *buffer, int transfer_len, int timeout_ms); int (*submit_control_msg)(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len, struct devrequest *setup, int timeout); + int transfer_len, struct devrequest *setup, int timeout_ms); int (*submit_int_msg)(struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len, int interval); void (*usb_event_poll)(void); @@ -167,9 +167,9 @@ int usb_set_idle(struct usb_device *dev, int ifnum, int duration, int usb_control_msg(struct usb_device *dev, unsigned int pipe, unsigned char request, unsigned char requesttype, unsigned short value, unsigned short index, - void *data, unsigned short size, int timeout); + void *data, unsigned short size, int timeout_ms); int usb_bulk_msg(struct usb_device *dev, unsigned int pipe, - void *data, int len, int *actual_length, int timeout); + void *data, int len, int *actual_length, int timeout_ms); int usb_submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len, int interval); int usb_maxpacket(struct usb_device *dev, unsigned long pipe); -- 2.24.0 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH 02/10] of: Add of_bus_n_xxx_cells() 2019-12-20 14:32 [PATCH 00/10] Add Raspberry Pi USB support Sascha Hauer 2019-12-20 14:32 ` [PATCH 01/10] usb: Make timeout unit clear Sascha Hauer @ 2019-12-20 14:32 ` Sascha Hauer 2019-12-20 14:32 ` [PATCH 03/10] device: Introduce dma_offset Sascha Hauer ` (7 subsequent siblings) 9 siblings, 0 replies; 24+ messages in thread From: Sascha Hauer @ 2019-12-20 14:32 UTC (permalink / raw) To: Barebox List Added straight from the Kernel. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- drivers/of/base.c | 46 ++++++++++++++++++++++++++++------------------ include/of.h | 12 ++++++++++++ 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/drivers/of/base.c b/drivers/of/base.c index 80ceeab13b..9ede052274 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -78,36 +78,46 @@ static struct device_node *of_aliases; #define OF_ROOT_NODE_SIZE_CELLS_DEFAULT 1 #define OF_ROOT_NODE_ADDR_CELLS_DEFAULT 1 -int of_n_addr_cells(struct device_node *np) +int of_bus_n_addr_cells(struct device_node *np) { - const __be32 *ip; + u32 cells; + + for (; np; np = np->parent) + if (!of_property_read_u32(np, "#address-cells", &cells)) + return cells; - do { - if (np->parent) - np = np->parent; - ip = of_get_property(np, "#address-cells", NULL); - if (ip) - return be32_to_cpup(ip); - } while (np->parent); /* No #address-cells property for the root node */ return OF_ROOT_NODE_ADDR_CELLS_DEFAULT; } + +int of_n_addr_cells(struct device_node *np) +{ + if (np->parent) + np = np->parent; + + return of_bus_n_addr_cells(np); +} EXPORT_SYMBOL(of_n_addr_cells); -int of_n_size_cells(struct device_node *np) +int of_bus_n_size_cells(struct device_node *np) { - const __be32 *ip; + u32 cells; + + for (; np; np = np->parent) + if (!of_property_read_u32(np, "#size-cells", &cells)) + return cells; - do { - if (np->parent) - np = np->parent; - ip = of_get_property(np, "#size-cells", NULL); - if (ip) - return be32_to_cpup(ip); - } while (np->parent); /* No #size-cells property for the root node */ return OF_ROOT_NODE_SIZE_CELLS_DEFAULT; } + +int of_n_size_cells(struct device_node *np) +{ + if (np->parent) + np = np->parent; + + return of_bus_n_size_cells(np); +} EXPORT_SYMBOL(of_n_size_cells); struct property *of_find_property(const struct device_node *np, diff --git a/include/of.h b/include/of.h index f63a3efe13..67601ce80c 100644 --- a/include/of.h +++ b/include/of.h @@ -113,7 +113,9 @@ struct device_node *of_unflatten_dtb_const(const void *infdt); struct cdev; #ifdef CONFIG_OFTREE +extern int of_bus_n_addr_cells(struct device_node *np); extern int of_n_addr_cells(struct device_node *np); +extern int of_bus_n_size_cells(struct device_node *np); extern int of_n_size_cells(struct device_node *np); extern struct property *of_find_property(const struct device_node *np, @@ -328,11 +330,21 @@ static inline struct device_d *of_platform_device_create(struct device_node *np, return NULL; } +static inline int of_bus_n_addr_cells(struct device_node *np) +{ + return 0; +} + static inline int of_n_addr_cells(struct device_node *np) { return 0; } +static inline int of_bus_n_size_cells(struct device_node *np) +{ + return 0; +} + static inline int of_n_size_cells(struct device_node *np) { return 0; -- 2.24.0 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH 03/10] device: Introduce dma_offset 2019-12-20 14:32 [PATCH 00/10] Add Raspberry Pi USB support Sascha Hauer 2019-12-20 14:32 ` [PATCH 01/10] usb: Make timeout unit clear Sascha Hauer 2019-12-20 14:32 ` [PATCH 02/10] of: Add of_bus_n_xxx_cells() Sascha Hauer @ 2019-12-20 14:32 ` Sascha Hauer 2019-12-20 14:32 ` [PATCH 04/10] of: Read dma_offset from device tree Sascha Hauer ` (6 subsequent siblings) 9 siblings, 0 replies; 24+ messages in thread From: Sascha Hauer @ 2019-12-20 14:32 UTC (permalink / raw) To: Barebox List For devices that do not have a 1:1 mapping between DMA and CPU we need a dma_offset. This adds dma_offset to struct device_d and starts honoring it in ARM dma_(un)map_single(). Also we add some comments to functions that would normally need a device argument to make the DMA <-> CPU translations device specific. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- arch/arm/cpu/mmu-common.c | 33 +++++++++++++++++++++++++++++++-- arch/arm/cpu/mmu.c | 4 ++++ arch/arm/cpu/mmu_64.c | 4 ++++ include/driver.h | 2 ++ 4 files changed, 41 insertions(+), 2 deletions(-) diff --git a/arch/arm/cpu/mmu-common.c b/arch/arm/cpu/mmu-common.c index aeefbb2daa..287622b203 100644 --- a/arch/arm/cpu/mmu-common.c +++ b/arch/arm/cpu/mmu-common.c @@ -11,9 +11,32 @@ #include "mmu.h" +static inline dma_addr_t cpu_to_dma(struct device_d *dev, unsigned long cpu_addr) +{ + dma_addr_t dma_addr = cpu_addr; + + if (dev) + dma_addr -= dev->dma_offset; + + return dma_addr; +} + +static inline unsigned long dma_to_cpu(struct device_d *dev, dma_addr_t addr) +{ + unsigned long cpu_addr = addr; + + if (dev) + cpu_addr += dev->dma_offset; + + return cpu_addr; +} + void dma_sync_single_for_cpu(dma_addr_t address, size_t size, enum dma_data_direction dir) { + /* + * FIXME: This function needs a device argument to support non 1:1 mappings + */ if (dir != DMA_TO_DEVICE) dma_inv_range((void *)address, size); } @@ -25,12 +48,14 @@ dma_addr_t dma_map_single(struct device_d *dev, void *ptr, size_t size, dma_sync_single_for_device(addr, size, dir); - return addr; + return cpu_to_dma(dev, addr); } -void dma_unmap_single(struct device_d *dev, dma_addr_t addr, size_t size, +void dma_unmap_single(struct device_d *dev, dma_addr_t dma_addr, size_t size, enum dma_data_direction dir) { + unsigned long addr = dma_to_cpu(dev, dma_addr); + dma_sync_single_for_cpu(addr, size, dir); } @@ -53,6 +78,10 @@ void *dma_alloc_map(size_t size, dma_addr_t *dma_handle, unsigned flags) void *dma_alloc_coherent(size_t size, dma_addr_t *dma_handle) { + /* + * FIXME: This function needs a device argument to support non 1:1 mappings + */ + return dma_alloc_map(size, dma_handle, MAP_UNCACHED); } diff --git a/arch/arm/cpu/mmu.c b/arch/arm/cpu/mmu.c index 158b130b57..1f97c28ec6 100644 --- a/arch/arm/cpu/mmu.c +++ b/arch/arm/cpu/mmu.c @@ -491,6 +491,10 @@ void *dma_alloc_writecombine(size_t size, dma_addr_t *dma_handle) void dma_sync_single_for_device(dma_addr_t address, size_t size, enum dma_data_direction dir) { + /* + * FIXME: This function needs a device argument to support non 1:1 mappings + */ + if (dir == DMA_FROM_DEVICE) { __dma_inv_range(address, address + size); if (outer_cache.inv_range) diff --git a/arch/arm/cpu/mmu_64.c b/arch/arm/cpu/mmu_64.c index f7a13014af..98cd4c754e 100644 --- a/arch/arm/cpu/mmu_64.c +++ b/arch/arm/cpu/mmu_64.c @@ -245,6 +245,10 @@ void dma_flush_range(void *ptr, size_t size) void dma_sync_single_for_device(dma_addr_t address, size_t size, enum dma_data_direction dir) { + /* + * FIXME: This function needs a device argument to support non 1:1 mappings + */ + if (dir == DMA_FROM_DEVICE) v8_inv_dcache_range(address, address + size - 1); else diff --git a/include/driver.h b/include/driver.h index ad59ce90c3..74be1b3e8e 100644 --- a/include/driver.h +++ b/include/driver.h @@ -93,6 +93,8 @@ struct device_d { u64 dma_mask; + unsigned long dma_offset; + void (*info) (struct device_d *); /* * For devices which take longer to probe this is called -- 2.24.0 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH 04/10] of: Read dma_offset from device tree 2019-12-20 14:32 [PATCH 00/10] Add Raspberry Pi USB support Sascha Hauer ` (2 preceding siblings ...) 2019-12-20 14:32 ` [PATCH 03/10] device: Introduce dma_offset Sascha Hauer @ 2019-12-20 14:32 ` Sascha Hauer 2019-12-20 14:32 ` [PATCH 05/10] usb: Add usbroothubdes.h Sascha Hauer ` (5 subsequent siblings) 9 siblings, 0 replies; 24+ messages in thread From: Sascha Hauer @ 2019-12-20 14:32 UTC (permalink / raw) To: Barebox List This reads the dma-ranges property from the device tree and sets dma_offset in the devices accordingly. The code is mostly taken from the Kernel as of v5.5-rc1. of_dma_configure() is trimmed down to the cases we want to support currently. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- drivers/of/address.c | 109 ++++++++++++++++++++++++++++++++++++++++++ drivers/of/platform.c | 18 +++++++ include/of_address.h | 9 ++++ 3 files changed, 136 insertions(+) diff --git a/drivers/of/address.c b/drivers/of/address.c index 4e12522a0a..2020f5b7b1 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -458,6 +458,33 @@ bool of_can_translate_address(struct device_node *dev) } EXPORT_SYMBOL(of_can_translate_address); +static struct device_node *__of_get_dma_parent(struct device_node *np) +{ + struct of_phandle_args args; + int ret, index; + + index = of_property_match_string(np, "interconnect-names", "dma-mem"); + if (index < 0) + return of_get_parent(np); + + ret = of_parse_phandle_with_args(np, "interconnects", + "#interconnect-cells", + index, &args); + if (ret < 0) + return of_get_parent(np); + + return args.np; +} + +static struct device_node *of_get_next_dma_parent(struct device_node *np) +{ + struct device_node *parent; + + parent = __of_get_dma_parent(np); + + return parent; +} + const __be32 *of_get_address(struct device_node *dev, int index, u64 *size, unsigned int *flags) { @@ -586,3 +613,85 @@ void __iomem *of_iomap(struct device_node *np, int index) return IOMEM(res.start); } EXPORT_SYMBOL(of_iomap); + +/** + * of_dma_get_range - Get DMA range info + * @np: device node to get DMA range info + * @dma_addr: pointer to store initial DMA address of DMA range + * @paddr: pointer to store initial CPU address of DMA range + * @size: pointer to store size of DMA range + * + * Look in bottom up direction for the first "dma-ranges" property + * and parse it. + * dma-ranges format: + * DMA addr (dma_addr) : naddr cells + * CPU addr (phys_addr_t) : pna cells + * size : nsize cells + * + * It returns -ENODEV if "dma-ranges" property was not found + * for this device in DT. + */ +int of_dma_get_range(struct device_node *np, u64 *dma_addr, u64 *paddr, u64 *size) +{ + struct device_node *node = np; + const __be32 *ranges = NULL; + int len, naddr, nsize, pna; + int ret = 0; + bool found_dma_ranges = false; + u64 dmaaddr; + + while (node) { + ranges = of_get_property(node, "dma-ranges", &len); + + /* Ignore empty ranges, they imply no translation required */ + if (ranges && len > 0) + break; + + /* Once we find 'dma-ranges', then a missing one is an error */ + if (found_dma_ranges && !ranges) { + ret = -ENODEV; + goto out; + } + found_dma_ranges = true; + + node = of_get_next_dma_parent(node); + } + + if (!node || !ranges) { + pr_debug("no dma-ranges found for node(%pOF)\n", np); + ret = -ENODEV; + goto out; + } + + naddr = of_bus_n_addr_cells(node); + nsize = of_bus_n_size_cells(node); + pna = of_n_addr_cells(node); + if ((len / sizeof(__be32)) % (pna + naddr + nsize)) { + ret = -EINVAL; + goto out; + } + + /* dma-ranges format: + * DMA addr : naddr cells + * CPU addr : pna cells + * size : nsize cells + */ + dmaaddr = of_read_number(ranges, naddr); + *paddr = of_translate_dma_address(node, ranges + naddr); + if (*paddr == OF_BAD_ADDR) { + pr_err("translation of DMA address(%llx) to CPU address failed node(%pOF)\n", + dmaaddr, np); + ret = -EINVAL; + goto out; + } + *dma_addr = dmaaddr; + + *size = of_read_number(ranges + naddr + pna, nsize); + + pr_debug("dma_addr(%llx) cpu_addr(%llx) size(%llx)\n", + *dma_addr, *paddr, *size); + +out: + + return ret; +} diff --git a/drivers/of/platform.c b/drivers/of/platform.c index d3795d799a..b1a7eb6730 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -74,6 +74,22 @@ static void of_device_make_bus_id(struct device_d *dev) } } +static void of_dma_configure(struct device_d *dev, struct device_node *np) +{ + u64 dma_addr, paddr, size = 0; + unsigned long offset; + int ret; + + ret = of_dma_get_range(np, &dma_addr, &paddr, &size); + if (ret < 0) { + dma_addr = offset = 0; + } else { + offset = paddr - dma_addr; + } + + dev->dma_offset = offset; +} + /** * of_platform_device_create - Alloc, initialize and register an of_device * @np: pointer to node to create device for @@ -148,6 +164,8 @@ struct device_d *of_platform_device_create(struct device_node *np, dev->num_resources = num_reg; of_device_make_bus_id(dev); + of_dma_configure(dev, np); + resinval = (-1); debug("%s: register device %s, io=%pa\n", diff --git a/include/of_address.h b/include/of_address.h index ebf3ec2a24..350ecaec82 100644 --- a/include/of_address.h +++ b/include/of_address.h @@ -56,6 +56,9 @@ extern struct device_node *of_find_matching_node_by_address( u64 base_address); extern void __iomem *of_iomap(struct device_node *np, int index); +extern int of_dma_get_range(struct device_node *np, u64 *dma_addr, u64 *paddr, + u64 *size); + #else /* CONFIG_OFTREE */ static inline u64 of_translate_address(struct device_node *dev, @@ -99,6 +102,12 @@ static inline void __iomem *of_iomap(struct device_node *np, int index) return NULL; } +static inline int of_dma_get_range(struct device_node *np, u64 *dma_addr, + u64 *paddr, u64 *size) +{ + return -ENOSYS; +} + #endif /* CONFIG_OFTREE */ #ifdef CONFIG_OF_PCI -- 2.24.0 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH 05/10] usb: Add usbroothubdes.h 2019-12-20 14:32 [PATCH 00/10] Add Raspberry Pi USB support Sascha Hauer ` (3 preceding siblings ...) 2019-12-20 14:32 ` [PATCH 04/10] of: Read dma_offset from device tree Sascha Hauer @ 2019-12-20 14:32 ` Sascha Hauer 2019-12-20 14:32 ` [PATCH 06/10] regulator: add function to get regulator by its name Sascha Hauer ` (4 subsequent siblings) 9 siblings, 0 replies; 24+ messages in thread From: Sascha Hauer @ 2019-12-20 14:32 UTC (permalink / raw) To: Barebox List Taken from U-Boot, needed for the upcoming dwc2 driver. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- include/usb/usbroothubdes.h | 128 ++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 include/usb/usbroothubdes.h diff --git a/include/usb/usbroothubdes.h b/include/usb/usbroothubdes.h new file mode 100644 index 0000000000..e743555d8e --- /dev/null +++ b/include/usb/usbroothubdes.h @@ -0,0 +1,128 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * USB virtual root hub descriptors + * + * (C) Copyright 2014 + * Stephen Warren swarren@wwwdotorg.org + * + * Based on ohci-hcd.c + */ + +#ifndef __USBROOTHUBDES_H__ +#define __USBROOTHUBDES_H__ + +/* Device descriptor */ +static __u8 root_hub_dev_des[] = { + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x10, /* __u16 bcdUSB; v1.1 */ + 0x01, + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x00, /* __u16 idVendor; */ + 0x00, + 0x00, /* __u16 idProduct; */ + 0x00, + 0x00, /* __u16 bcdDevice; */ + 0x00, + 0x00, /* __u8 iManufacturer; */ + 0x01, /* __u8 iProduct; */ + 0x00, /* __u8 iSerialNumber; */ + 0x01, /* __u8 bNumConfigurations; */ +}; + +/* Configuration descriptor */ +static __u8 root_hub_config_des[] = { + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, /* __u16 wTotalLength; */ + 0x00, + 0x01, /* __u8 bNumInterfaces; */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0x40, /* __u8 bmAttributes; + * Bit 7: Bus-powered + * 6: Self-powered, + * 5 Remote-wakwup, + * 4..0: resvd + */ + 0x00, /* __u8 MaxPower; */ + /* interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; */ + 0x00, /* __u8 if_iInterface; */ + /* endpoint */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */ + 0x00, + 0xff, /* __u8 ep_bInterval; 255 ms */ +}; + +#ifdef WANT_USB_ROOT_HUB_HUB_DES +static unsigned char root_hub_hub_des[] = { + 0x09, /* __u8 bLength; */ + 0x29, /* __u8 bDescriptorType; Hub-descriptor */ + 0x02, /* __u8 bNbrPorts; */ + 0x00, /* __u16 wHubCharacteristics; */ + 0x00, + 0x01, /* __u8 bPwrOn2pwrGood; 2ms */ + 0x00, /* __u8 bHubContrCurrent; 0 mA */ + 0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */ + 0xff, /* __u8 PortPwrCtrlMask; *** 7 ports max *** */ +}; +#endif + +static unsigned char root_hub_str_index0[] = { + 0x04, /* __u8 bLength; */ + 0x03, /* __u8 bDescriptorType; String-descriptor */ + 0x09, /* __u8 lang ID */ + 0x04, /* __u8 lang ID */ +}; + +static unsigned char root_hub_str_index1[] = { + 32, /* __u8 bLength; */ + 0x03, /* __u8 bDescriptorType; String-descriptor */ + 'U', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + '-', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'B', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'o', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'o', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 't', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + ' ', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'R', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'o', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'o', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 't', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + ' ', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'H', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'u', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'b', /* __u8 Unicode */ + 0, /* __u8 Unicode */ +}; + +#endif -- 2.24.0 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH 06/10] regulator: add function to get regulator by its name 2019-12-20 14:32 [PATCH 00/10] Add Raspberry Pi USB support Sascha Hauer ` (4 preceding siblings ...) 2019-12-20 14:32 ` [PATCH 05/10] usb: Add usbroothubdes.h Sascha Hauer @ 2019-12-20 14:32 ` Sascha Hauer 2019-12-20 14:32 ` [PATCH 07/10] rpi: Enable USB Power domain during startup Sascha Hauer ` (3 subsequent siblings) 9 siblings, 0 replies; 24+ messages in thread From: Sascha Hauer @ 2019-12-20 14:32 UTC (permalink / raw) To: Barebox List Useful for getting regulators that are not correctly associated with a device. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- drivers/regulator/core.c | 30 +++++++++++++++++++++++++++++- include/regulator.h | 1 + 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 4ca035ae94..f0de7a52e3 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -327,6 +327,34 @@ struct regulator *regulator_get(struct device_d *dev, const char *supply) return r; } +static struct regulator_internal *regulator_by_name(const char *name) +{ + struct regulator_internal *ri; + + list_for_each_entry(ri, ®ulator_list, list) + if (ri->name && !strcmp(ri->name, name)) + return ri; + + return NULL; +} + +struct regulator *regulator_get_name(const char *name) +{ + struct regulator_internal *ri; + struct regulator *r; + + ri = regulator_by_name(name); + if (!ri) + return ERR_PTR(-ENODEV); + + r = xzalloc(sizeof(*r)); + r->ri = ri; + + list_add_tail(&r->list, &ri->consumer_list); + + return r; +} + /* * regulator_enable - enable a regulator. * @r: the regulator to enable @@ -379,7 +407,7 @@ static void regulator_print_one(struct regulator_internal *ri) printf(" consumers:\n"); list_for_each_entry(r, &ri->consumer_list, list) - printf(" %s\n", dev_name(r->dev)); + printf(" %s\n", r->dev ? dev_name(r->dev) : "none"); } } diff --git a/include/regulator.h b/include/regulator.h index 156acb82f8..a445c5c3d1 100644 --- a/include/regulator.h +++ b/include/regulator.h @@ -116,6 +116,7 @@ void regulators_print(void); #ifdef CONFIG_REGULATOR struct regulator *regulator_get(struct device_d *, const char *); +struct regulator *regulator_get_name(const char *name); int regulator_enable(struct regulator *); int regulator_disable(struct regulator *); int regulator_is_enabled_regmap(struct regulator_dev *); -- 2.24.0 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH 07/10] rpi: Enable USB Power domain during startup 2019-12-20 14:32 [PATCH 00/10] Add Raspberry Pi USB support Sascha Hauer ` (5 preceding siblings ...) 2019-12-20 14:32 ` [PATCH 06/10] regulator: add function to get regulator by its name Sascha Hauer @ 2019-12-20 14:32 ` Sascha Hauer 2019-12-20 14:32 ` [PATCH 08/10] usb: Forward error code from usb_set_configuration Sascha Hauer ` (2 subsequent siblings) 9 siblings, 0 replies; 24+ messages in thread From: Sascha Hauer @ 2019-12-20 14:32 UTC (permalink / raw) To: Barebox List Enable the USB Power domain during startup. The power domain is abstracted as a regulator in barebox, but modelled as a power domain in the device tree. Until this is sorted out just enable the power domain or regulator in the board code. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- arch/arm/boards/raspberry-pi/rpi-common.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/arm/boards/raspberry-pi/rpi-common.c b/arch/arm/boards/raspberry-pi/rpi-common.c index acb26f6a64..45961b52ee 100644 --- a/arch/arm/boards/raspberry-pi/rpi-common.c +++ b/arch/arm/boards/raspberry-pi/rpi-common.c @@ -21,6 +21,7 @@ #include <linux/clk.h> #include <linux/clkdev.h> #include <envfs.h> +#include <regulator.h> #include <malloc.h> #include <libfile.h> #include <gpio.h> @@ -462,11 +463,20 @@ static void rpi_vc_fdt(void) static int rpi_devices_init(void) { + struct regulator *reg; + rpi_model_init(); bcm2835_register_fb(); armlinux_set_architecture(MACH_TYPE_BCM2708); rpi_env_init(); rpi_vc_fdt(); + + reg = regulator_get_name("bcm2835_usb"); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + regulator_enable(reg); + return 0; } late_initcall(rpi_devices_init); -- 2.24.0 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH 08/10] usb: Forward error code from usb_set_configuration 2019-12-20 14:32 [PATCH 00/10] Add Raspberry Pi USB support Sascha Hauer ` (6 preceding siblings ...) 2019-12-20 14:32 ` [PATCH 07/10] rpi: Enable USB Power domain during startup Sascha Hauer @ 2019-12-20 14:32 ` Sascha Hauer 2019-12-20 14:32 ` [PATCH 09/10] usb: Add dwc2 host driver Sascha Hauer 2019-12-20 14:32 ` [PATCH 10/10] ARM: rpi_defconfig: Enable networking support Sascha Hauer 9 siblings, 0 replies; 24+ messages in thread From: Sascha Hauer @ 2019-12-20 14:32 UTC (permalink / raw) To: Barebox List Instead of returning -1 forward the error code and take the opportunity to print the error string. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- drivers/usb/core/usb.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index ad3bacf236..1c3dcb79a8 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -124,7 +124,7 @@ static int usb_set_configuration(struct usb_device *dev, int configuration) dev->toggle[1] = 0; return 0; } else - return -1; + return res; } /* The routine usb_set_maxpacket_ep() is extracted from the loop of routine @@ -412,9 +412,11 @@ int usb_new_device(struct usb_device *dev) usb_parse_config(dev, buf, 0); usb_set_maxpacket(dev); /* we set the default configuration here */ - if (usb_set_configuration(dev, dev->config.desc.bConfigurationValue)) { - printf("failed to set default configuration " \ - "len %d, status %lX\n", dev->act_len, dev->status); + err = usb_set_configuration(dev, dev->config.desc.bConfigurationValue); + if (err) { + printf("Setting default configuration failed with: %s\n" \ + "len %d, status %lX\n", strerror(-err), + dev->act_len, dev->status); goto err_out; } pr_debug("new device: Mfr=%d, Product=%d, SerialNumber=%d\n", -- 2.24.0 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH 09/10] usb: Add dwc2 host driver 2019-12-20 14:32 [PATCH 00/10] Add Raspberry Pi USB support Sascha Hauer ` (7 preceding siblings ...) 2019-12-20 14:32 ` [PATCH 08/10] usb: Forward error code from usb_set_configuration Sascha Hauer @ 2019-12-20 14:32 ` Sascha Hauer 2020-01-14 13:21 ` [RFC PATCH 0/7] usb: " Jules Maselbas 2019-12-20 14:32 ` [PATCH 10/10] ARM: rpi_defconfig: Enable networking support Sascha Hauer 9 siblings, 1 reply; 24+ messages in thread From: Sascha Hauer @ 2019-12-20 14:32 UTC (permalink / raw) To: Barebox List This adds a driver for the dwc2 controller in host mode. The driver is taken from U-Boot-2019.10 and tested on a Raspberry Pi 3. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- drivers/usb/host/Kconfig | 3 + drivers/usb/host/Makefile | 1 + drivers/usb/host/dwc2.c | 1132 +++++++++++++++++++++++++++++++++++++ drivers/usb/host/dwc2.h | 778 +++++++++++++++++++++++++ 4 files changed, 1914 insertions(+) create mode 100644 drivers/usb/host/dwc2.c create mode 100644 drivers/usb/host/dwc2.h diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index d2029bc7d7..b0f32faee9 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -21,6 +21,9 @@ config USB_OHCI_AT91 depends on ARCH_AT91 bool "AT91 OHCI driver" +config USB_DWC2_HOST + bool "DWC2 Host driver" + config USB_XHCI bool "xHCI driver" depends on HAS_DMA diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 0478d34272..fa042e9a54 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -3,5 +3,6 @@ obj-$(CONFIG_USB_EHCI_OMAP) += ehci-omap.o obj-$(CONFIG_USB_EHCI_ATMEL) += ehci-atmel.o obj-$(CONFIG_USB_OHCI) += ohci-hcd.o obj-$(CONFIG_USB_OHCI_AT91) += ohci-at91.o +obj-$(CONFIG_USB_DWC2_HOST) += dwc2.o obj-$(CONFIG_USB_XHCI) += xhci-hcd.o xhci-hub.o obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c new file mode 100644 index 0000000000..1df1149686 --- /dev/null +++ b/drivers/usb/host/dwc2.c @@ -0,0 +1,1132 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org> + * Copyright (C) 2014 Marek Vasut <marex@denx.de> + */ + +#include <common.h> +#include <usb/usb.h> +#include <usb/usbroothubdes.h> +#include <malloc.h> +#include <init.h> +#include <io.h> +#include <of.h> +#include <linux/iopoll.h> +#include <dma.h> + +#include "dwc2.h" + +/* Use only HC channel 0. */ +#define DWC2_HC_CHANNEL 0 + +#define DWC2_STATUS_BUF_SIZE 64 +#define DWC2_DATA_BUF_SIZE (16 * 1024) + +#define MAX_DEVICE 16 +#define MAX_ENDPOINT 16 + +struct dwc2_priv { + struct device_d *dev; + struct usb_host host; + uint8_t *dmabuf; + + u8 in_data_toggle[MAX_DEVICE][MAX_ENDPOINT]; + u8 out_data_toggle[MAX_DEVICE][MAX_ENDPOINT]; + struct dwc2_core_regs *regs; + int root_hub_devnum; + bool ext_vbus; + /* + * The hnp/srp capability must be disabled if the platform + * does't support hnp/srp. Otherwise the force mode can't work. + */ + bool hnp_srp_disable; + bool oc_disable; +}; + +/* + * Initializes the FSLSPClkSel field of the HCFG register + * depending on the PHY type. + */ +static void init_fslspclksel(struct dwc2_priv *priv) +{ + struct dwc2_core_regs *regs = priv->regs; + uint32_t phyclk; + + phyclk = DWC2_HCFG_FSLSPCLKSEL_48_MHZ; /* Full speed PHY */ + + clrsetbits_le32(®s->host_regs.hcfg, + DWC2_HCFG_FSLSPCLKSEL_MASK, + phyclk << DWC2_HCFG_FSLSPCLKSEL_OFFSET); +} + +/* + * Flush a Tx FIFO. + * + * @param regs Programming view of DWC_otg controller. + * @param num Tx FIFO to flush. + */ +static void dwc_otg_flush_tx_fifo(struct dwc2_priv *priv, const int num) +{ + struct dwc2_core_regs *regs = priv->regs; + struct device_d *dev = priv->dev; + int ret; + uint32_t val; + + writel(DWC2_GRSTCTL_TXFFLSH | (num << DWC2_GRSTCTL_TXFNUM_OFFSET), + ®s->grstctl); + ret = readl_poll_timeout(®s->grstctl, val, !(val & DWC2_GRSTCTL_TXFFLSH), + 1000000); + if (ret) + dev_err(dev, "%s: Timeout!\n", __func__); + + /* Wait for 3 PHY Clocks */ + udelay(1); +} + +/* + * Flush Rx FIFO. + * + * @param regs Programming view of DWC_otg controller. + */ +static void dwc_otg_flush_rx_fifo(struct dwc2_priv *priv) +{ + struct dwc2_core_regs *regs = priv->regs; + struct device_d *dev = priv->dev; + int ret; + uint32_t val; + + writel(DWC2_GRSTCTL_RXFFLSH, ®s->grstctl); + ret = readl_poll_timeout(®s->grstctl, val, !(val & DWC2_GRSTCTL_RXFFLSH), + 1000000); + if (ret) + dev_err(dev, "%s: Timeout!\n", __func__); + + /* Wait for 3 PHY Clocks */ + udelay(1); +} + +/* + * Do core a soft reset of the core. Be careful with this because it + * resets all the internal state machines of the core. + */ +static void dwc_otg_core_reset(struct dwc2_priv *priv) +{ + struct dwc2_core_regs *regs = priv->regs; + struct device_d *dev = priv->dev; + uint32_t val; + int ret; + + /* Wait for AHB master IDLE state. */ + ret = readl_poll_timeout(®s->grstctl, val, val & DWC2_GRSTCTL_AHBIDLE, + 1000000); + if (ret) + dev_err(dev, "%s: Timeout!\n", __func__); + + /* Core Soft Reset */ + writel(DWC2_GRSTCTL_CSFTRST, ®s->grstctl); + ret = readl_poll_timeout(®s->grstctl, val, !(val & DWC2_GRSTCTL_CSFTRST), + 1000000); + if (ret) + dev_err(dev, "%s: Timeout!\n", __func__); + + /* + * Wait for core to come out of reset. + * NOTE: This long sleep is _very_ important, otherwise the core will + * not stay in host mode after a connector ID change! + */ + mdelay(100); +} + +/* + * This function initializes the DWC_otg controller registers for + * host mode. + * + * This function flushes the Tx and Rx FIFOs and it flushes any entries in the + * 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_priv *priv) +{ + struct dwc2_core_regs *regs = priv->regs; + struct device_d *dev = priv->dev; + uint32_t nptxfifosize = 0; + uint32_t ptxfifosize = 0; + uint32_t hprt0 = 0; + uint32_t val; + int i, ret, num_channels; + + /* Restart the Phy Clock */ + writel(0, ®s->pcgcctl); + + /* Initialize Host Configuration Register */ + init_fslspclksel(priv); + + /* Configure data FIFO sizes */ + if (readl(®s->ghwcfg2) & DWC2_HWCFG2_DYNAMIC_FIFO) { + /* Rx FIFO */ + writel(CONFIG_DWC2_HOST_RX_FIFO_SIZE, ®s->grxfsiz); + + /* Non-periodic Tx FIFO */ + nptxfifosize |= CONFIG_DWC2_HOST_NPERIO_TX_FIFO_SIZE << + DWC2_FIFOSIZE_DEPTH_OFFSET; + nptxfifosize |= CONFIG_DWC2_HOST_RX_FIFO_SIZE << + DWC2_FIFOSIZE_STARTADDR_OFFSET; + writel(nptxfifosize, ®s->gnptxfsiz); + + /* Periodic Tx FIFO */ + ptxfifosize |= CONFIG_DWC2_HOST_PERIO_TX_FIFO_SIZE << + DWC2_FIFOSIZE_DEPTH_OFFSET; + ptxfifosize |= (CONFIG_DWC2_HOST_RX_FIFO_SIZE + + CONFIG_DWC2_HOST_NPERIO_TX_FIFO_SIZE) << + DWC2_FIFOSIZE_STARTADDR_OFFSET; + writel(ptxfifosize, ®s->hptxfsiz); + } + + /* Clear Host Set HNP Enable in the OTG Control Register */ + clrbits_le32(®s->gotgctl, DWC2_GOTGCTL_HSTSETHNPEN); + + /* Make sure the FIFOs are flushed. */ + dwc_otg_flush_tx_fifo(priv, 0x10); /* All Tx FIFOs */ + dwc_otg_flush_rx_fifo(priv); + + /* Flush out any leftover queued requests. */ + num_channels = readl(®s->ghwcfg2); + num_channels &= DWC2_HWCFG2_NUM_HOST_CHAN_MASK; + num_channels >>= DWC2_HWCFG2_NUM_HOST_CHAN_OFFSET; + num_channels += 1; + + for (i = 0; i < num_channels; i++) + clrsetbits_le32(®s->hc_regs[i].hcchar, + DWC2_HCCHAR_CHEN | DWC2_HCCHAR_EPDIR, + DWC2_HCCHAR_CHDIS); + + /* Halt all channels to put them into a known state. */ + for (i = 0; i < num_channels; i++) { + clrsetbits_le32(®s->hc_regs[i].hcchar, + DWC2_HCCHAR_EPDIR, + DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS); + ret = readl_poll_timeout(®s->hc_regs[i].hcchar, val, + !(val & DWC2_HCCHAR_CHEN), + 1000000); + if (ret) + dev_err(dev, "%s: Timeout!\n", __func__); + } + + /* Turn on the vbus power. */ + if (readl(®s->gintsts) & DWC2_GINTSTS_CURMODE_HOST) { + hprt0 = readl(®s->hprt0); + hprt0 &= ~(DWC2_HPRT0_PRTENA | DWC2_HPRT0_PRTCONNDET); + hprt0 &= ~(DWC2_HPRT0_PRTENCHNG | DWC2_HPRT0_PRTOVRCURRCHNG); + if (!(hprt0 & DWC2_HPRT0_PRTPWR)) { + hprt0 |= DWC2_HPRT0_PRTPWR; + writel(hprt0, ®s->hprt0); + } + } +} + +/* + * This function initializes the DWC_otg controller registers and + * prepares the core for device mode or host mode operation. + * + * @param regs Programming view of the DWC_otg controller + */ +static void dwc_otg_core_init(struct dwc2_priv *priv) +{ + struct dwc2_core_regs *regs = priv->regs; + uint32_t ahbcfg = 0; + uint32_t usbcfg = 0; + uint8_t brst_sz = 32; + + /* Common Initialization */ + usbcfg = readl(®s->gusbcfg); + + /* Program the ULPI External VBUS bit if needed */ + if (priv->ext_vbus) { + usbcfg |= DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV; + if (!priv->oc_disable) { + usbcfg |= DWC2_GUSBCFG_ULPI_INT_VBUS_INDICATOR | + DWC2_GUSBCFG_INDICATOR_PASSTHROUGH; + } + } else { + usbcfg &= ~DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV; + } + + /* Set external TS Dline pulsing */ + usbcfg &= ~DWC2_GUSBCFG_TERM_SEL_DL_PULSE; + writel(usbcfg, ®s->gusbcfg); + + /* Reset the Controller */ + dwc_otg_core_reset(priv); + + /* High speed PHY. */ + + /* + * HS PHY parameters. These parameters are preserved during + * soft reset so only program the first time. Do a soft reset + * immediately after setting phyif. + */ + usbcfg &= ~(DWC2_GUSBCFG_ULPI_UTMI_SEL | DWC2_GUSBCFG_PHYIF); + usbcfg |= CONFIG_DWC2_PHY_TYPE << DWC2_GUSBCFG_ULPI_UTMI_SEL_OFFSET; + + if (usbcfg & DWC2_GUSBCFG_ULPI_UTMI_SEL) /* ULPI interface */ + usbcfg &= ~DWC2_GUSBCFG_DDRSEL; + + writel(usbcfg, ®s->gusbcfg); + + /* Reset after setting the PHY parameters */ + dwc_otg_core_reset(priv); + + usbcfg = readl(®s->gusbcfg); + usbcfg &= ~(DWC2_GUSBCFG_ULPI_FSLS | DWC2_GUSBCFG_ULPI_CLK_SUS_M); + + if (priv->hnp_srp_disable) + usbcfg |= DWC2_GUSBCFG_FORCEHOSTMODE; + + writel(usbcfg, ®s->gusbcfg); + + /* Program the GAHBCFG Register. */ + switch (readl(®s->ghwcfg2) & DWC2_HWCFG2_ARCHITECTURE_MASK) { + case DWC2_HWCFG2_ARCHITECTURE_SLAVE_ONLY: + break; + case DWC2_HWCFG2_ARCHITECTURE_EXT_DMA: + while (brst_sz > 1) { + ahbcfg |= ahbcfg + (1 << DWC2_GAHBCFG_HBURSTLEN_OFFSET); + ahbcfg &= DWC2_GAHBCFG_HBURSTLEN_MASK; + brst_sz >>= 1; + } + + ahbcfg |= DWC2_GAHBCFG_DMAENABLE; + break; + + case DWC2_HWCFG2_ARCHITECTURE_INT_DMA: + ahbcfg |= DWC2_GAHBCFG_HBURSTLEN_INCR4; + ahbcfg |= DWC2_GAHBCFG_DMAENABLE; + break; + } + + writel(ahbcfg, ®s->gahbcfg); + + /* Program the capabilities in GUSBCFG Register */ + usbcfg = 0; + + if (!priv->hnp_srp_disable) + usbcfg |= DWC2_GUSBCFG_HNPCAP | DWC2_GUSBCFG_SRPCAP; + + setbits_le32(®s->gusbcfg, usbcfg); +} + +/* + * Prepares a host channel for transferring packets to/from a specific + * endpoint. The HCCHARn register is set up with the characteristics specified + * in _hc. Host channel interrupts that may need to be serviced while this + * transfer is in progress are enabled. + * + * @param regs Programming view of DWC_otg controller + * @param hc Information needed to initialize the host channel + */ +static void dwc_otg_hc_init(struct dwc2_core_regs *regs, uint8_t hc_num, + struct usb_device *dev, uint8_t dev_addr, uint8_t ep_num, + uint8_t ep_is_in, uint8_t ep_type, uint16_t max_packet) +{ + struct dwc2_hc_regs *hc_regs = ®s->hc_regs[hc_num]; + uint32_t hcchar = (dev_addr << DWC2_HCCHAR_DEVADDR_OFFSET) | + (ep_num << DWC2_HCCHAR_EPNUM_OFFSET) | + (ep_is_in << DWC2_HCCHAR_EPDIR_OFFSET) | + (ep_type << DWC2_HCCHAR_EPTYPE_OFFSET) | + (max_packet << DWC2_HCCHAR_MPS_OFFSET); + + if (dev->speed == USB_SPEED_LOW) + hcchar |= DWC2_HCCHAR_LSPDDEV; + + /* + * Program the HCCHARn register with the endpoint characteristics + * for the current transfer. + */ + writel(hcchar, &hc_regs->hcchar); + + /* Program the HCSPLIT register, default to no SPLIT */ + writel(0, &hc_regs->hcsplt); +} + +static void dwc_otg_hc_init_split(struct dwc2_hc_regs *hc_regs, + uint8_t hub_devnum, uint8_t hub_port) +{ + uint32_t hcsplt = 0; + + hcsplt = DWC2_HCSPLT_SPLTENA; + hcsplt |= hub_devnum << DWC2_HCSPLT_HUBADDR_OFFSET; + hcsplt |= hub_port << DWC2_HCSPLT_PRTADDR_OFFSET; + + /* Program the HCSPLIT register for SPLITs */ + writel(hcsplt, &hc_regs->hcsplt); +} + +/* + * DWC2 to USB API interface + */ +/* Direction: In ; Request: Status */ +static int dwc_otg_submit_rh_msg_in_status(struct dwc2_core_regs *regs, + struct usb_device *dev, void *buffer, + int txlen, struct devrequest *cmd) +{ + uint32_t hprt0 = 0; + uint32_t port_status = 0; + uint32_t port_change = 0; + int len = 0; + int stat = 0; + + switch (cmd->requesttype & ~USB_DIR_IN) { + case 0: + *(uint16_t *)buffer = cpu_to_le16(1); + len = 2; + break; + case USB_RECIP_INTERFACE: + case USB_RECIP_ENDPOINT: + *(uint16_t *)buffer = cpu_to_le16(0); + len = 2; + break; + case USB_TYPE_CLASS: + *(uint32_t *)buffer = cpu_to_le32(0); + len = 4; + break; + case USB_RECIP_OTHER | USB_TYPE_CLASS: + hprt0 = readl(®s->hprt0); + if (hprt0 & DWC2_HPRT0_PRTCONNSTS) + port_status |= USB_PORT_STAT_CONNECTION; + if (hprt0 & DWC2_HPRT0_PRTENA) + port_status |= USB_PORT_STAT_ENABLE; + if (hprt0 & DWC2_HPRT0_PRTSUSP) + port_status |= USB_PORT_STAT_SUSPEND; + if (hprt0 & DWC2_HPRT0_PRTOVRCURRACT) + port_status |= USB_PORT_STAT_OVERCURRENT; + if (hprt0 & DWC2_HPRT0_PRTRST) + port_status |= USB_PORT_STAT_RESET; + if (hprt0 & DWC2_HPRT0_PRTPWR) + port_status |= USB_PORT_STAT_POWER; + + if ((hprt0 & DWC2_HPRT0_PRTSPD_MASK) == DWC2_HPRT0_PRTSPD_LOW) + port_status |= USB_PORT_STAT_LOW_SPEED; + else if ((hprt0 & DWC2_HPRT0_PRTSPD_MASK) == + DWC2_HPRT0_PRTSPD_HIGH) + port_status |= USB_PORT_STAT_HIGH_SPEED; + + if (hprt0 & DWC2_HPRT0_PRTENCHNG) + port_change |= USB_PORT_STAT_C_ENABLE; + if (hprt0 & DWC2_HPRT0_PRTCONNDET) + port_change |= USB_PORT_STAT_C_CONNECTION; + if (hprt0 & DWC2_HPRT0_PRTOVRCURRCHNG) + port_change |= USB_PORT_STAT_C_OVERCURRENT; + + *(uint32_t *)buffer = cpu_to_le32(port_status | + (port_change << 16)); + len = 4; + break; + default: + pr_err("%s: unsupported root hub command\n", __func__); + stat = USB_ST_STALLED; + } + + dev->act_len = min(len, txlen); + dev->status = stat; + + return stat; +} + +/* Direction: In ; Request: Descriptor */ +static int dwc_otg_submit_rh_msg_in_descriptor(struct usb_device *dev, + void *buffer, int txlen, + struct devrequest *cmd) +{ + unsigned char data[32]; + uint32_t dsc; + int len = 0; + int stat = 0; + uint16_t wValue = cpu_to_le16(cmd->value); + uint16_t wLength = cpu_to_le16(cmd->length); + + switch (cmd->requesttype & ~USB_DIR_IN) { + case 0: + switch (wValue & 0xff00) { + case 0x0100: /* device descriptor */ + len = min3(txlen, (int)sizeof(root_hub_dev_des), (int)wLength); + memcpy(buffer, root_hub_dev_des, len); + break; + case 0x0200: /* configuration descriptor */ + len = min3(txlen, (int)sizeof(root_hub_config_des), (int)wLength); + memcpy(buffer, root_hub_config_des, len); + break; + case 0x0300: /* string descriptors */ + switch (wValue & 0xff) { + case 0x00: + len = min3(txlen, (int)sizeof(root_hub_str_index0), + (int)wLength); + memcpy(buffer, root_hub_str_index0, len); + break; + case 0x01: + len = min3(txlen, (int)sizeof(root_hub_str_index1), + (int)wLength); + memcpy(buffer, root_hub_str_index1, len); + break; + } + break; + default: + stat = USB_ST_STALLED; + } + break; + + case USB_TYPE_CLASS: + /* Root port config, set 1 port and nothing else. */ + dsc = 0x00000001; + + data[0] = 9; /* min length; */ + data[1] = 0x29; + data[2] = dsc & RH_A_NDP; + data[3] = 0; + if (dsc & RH_A_PSM) + data[3] |= 0x1; + if (dsc & RH_A_NOCP) + data[3] |= 0x10; + else if (dsc & RH_A_OCPM) + data[3] |= 0x8; + + /* corresponds to data[4-7] */ + data[5] = (dsc & RH_A_POTPGT) >> 24; + data[7] = dsc & RH_B_DR; + if (data[2] < 7) { + data[8] = 0xff; + } else { + data[0] += 2; + data[8] = (dsc & RH_B_DR) >> 8; + data[9] = 0xff; + data[10] = data[9]; + } + + len = min3(txlen, (int)data[0], (int)wLength); + memcpy(buffer, data, len); + break; + default: + pr_err("%s: unsupported root hub command\n", __func__); + stat = USB_ST_STALLED; + } + + dev->act_len = min(len, txlen); + dev->status = stat; + + return stat; +} + +/* Direction: In ; Request: Configuration */ +static int dwc_otg_submit_rh_msg_in_configuration(struct usb_device *dev, + void *buffer, int txlen, + struct devrequest *cmd) +{ + int len = 0; + int stat = 0; + + switch (cmd->requesttype & ~USB_DIR_IN) { + case 0: + *(uint8_t *)buffer = 0x01; + len = 1; + break; + default: + pr_err("%s: unsupported root hub command\n", __func__); + stat = USB_ST_STALLED; + } + + dev->act_len = min(len, txlen); + dev->status = stat; + + return stat; +} + +/* Direction: In */ +static int dwc_otg_submit_rh_msg_in(struct dwc2_priv *priv, + struct usb_device *dev, void *buffer, + int txlen, struct devrequest *cmd) +{ + switch (cmd->request) { + case USB_REQ_GET_STATUS: + return dwc_otg_submit_rh_msg_in_status(priv->regs, dev, buffer, + txlen, cmd); + case USB_REQ_GET_DESCRIPTOR: + return dwc_otg_submit_rh_msg_in_descriptor(dev, buffer, + txlen, cmd); + case USB_REQ_GET_CONFIGURATION: + return dwc_otg_submit_rh_msg_in_configuration(dev, buffer, + txlen, cmd); + default: + pr_err("%s: unsupported root hub command\n", __func__); + return USB_ST_STALLED; + } +} + +/* Direction: Out */ +static int dwc_otg_submit_rh_msg_out(struct dwc2_priv *priv, + struct usb_device *dev, + void *buffer, int txlen, + struct devrequest *cmd) +{ + struct dwc2_core_regs *regs = priv->regs; + int len = 0; + int stat = 0; + uint16_t bmrtype_breq = cmd->requesttype | (cmd->request << 8); + uint16_t wValue = cpu_to_le16(cmd->value); + + switch (bmrtype_breq & ~USB_DIR_IN) { + case (USB_REQ_CLEAR_FEATURE << 8) | USB_RECIP_ENDPOINT: + case (USB_REQ_CLEAR_FEATURE << 8) | USB_TYPE_CLASS: + break; + + case (USB_REQ_CLEAR_FEATURE << 8) | USB_RECIP_OTHER | USB_TYPE_CLASS: + switch (wValue) { + case USB_PORT_FEAT_C_CONNECTION: + setbits_le32(®s->hprt0, DWC2_HPRT0_PRTCONNDET); + break; + } + break; + + case (USB_REQ_SET_FEATURE << 8) | USB_RECIP_OTHER | USB_TYPE_CLASS: + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + break; + + case USB_PORT_FEAT_RESET: + clrsetbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | + DWC2_HPRT0_PRTCONNDET | + DWC2_HPRT0_PRTENCHNG | + DWC2_HPRT0_PRTOVRCURRCHNG, + DWC2_HPRT0_PRTRST); + mdelay(50); + clrbits_le32(®s->hprt0, DWC2_HPRT0_PRTRST); + break; + + case USB_PORT_FEAT_POWER: + clrsetbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | + DWC2_HPRT0_PRTCONNDET | + DWC2_HPRT0_PRTENCHNG | + DWC2_HPRT0_PRTOVRCURRCHNG, + DWC2_HPRT0_PRTRST); + break; + + case USB_PORT_FEAT_ENABLE: + break; + } + break; + case (USB_REQ_SET_ADDRESS << 8): + priv->root_hub_devnum = wValue; + break; + case (USB_REQ_SET_CONFIGURATION << 8): + break; + default: + pr_err("%s: unsupported root hub command\n", __func__); + stat = USB_ST_STALLED; + } + + len = min(len, txlen); + + dev->act_len = len; + dev->status = stat; + + return stat; +} + +static int dwc_otg_submit_rh_msg(struct dwc2_priv *priv, struct usb_device *dev, + unsigned long pipe, void *buffer, int txlen, + struct devrequest *cmd) +{ + int stat = 0; + + if (usb_pipeint(pipe)) { + pr_err("Root-Hub submit IRQ: NOT implemented\n"); + return 0; + } + + if (cmd->requesttype & USB_DIR_IN) + stat = dwc_otg_submit_rh_msg_in(priv, dev, buffer, txlen, cmd); + else + stat = dwc_otg_submit_rh_msg_out(priv, dev, buffer, txlen, cmd); + + mdelay(1); + + return stat; +} + +static int wait_for_chhltd(struct dwc2_priv *priv, uint32_t *sub, + u8 *toggle, int timeout_ms) +{ + struct dwc2_core_regs *regs = priv->regs; + struct dwc2_hc_regs *hc_regs = ®s->hc_regs[DWC2_HC_CHANNEL]; + struct device_d *dev = priv->dev; + int ret; + uint32_t hcint, hctsiz; + uint32_t val; + int timeout_us = timeout_ms * 1000; + + ret = readl_poll_timeout(&hc_regs->hcint, val, + val & DWC2_HCINT_CHHLTD, timeout_us); + if (ret) { + clrsetbits_le32(&hc_regs->hcchar, 0, DWC2_HCCHAR_CHDIS); + readl_poll_timeout(&hc_regs->hcint, val, + val & DWC2_HCINT_CHHLTD, 10000); + + return ret; + } + + hcint = readl(&hc_regs->hcint); + hctsiz = readl(&hc_regs->hctsiz); + *sub = (hctsiz & DWC2_HCTSIZ_XFERSIZE_MASK) >> + DWC2_HCTSIZ_XFERSIZE_OFFSET; + *toggle = (hctsiz & DWC2_HCTSIZ_PID_MASK) >> DWC2_HCTSIZ_PID_OFFSET; + + dev_dbg(dev, "%s: HCINT=%08x sub=%u toggle=%d\n", __func__, hcint, *sub, + *toggle); + + if (hcint & DWC2_HCINT_XFERCOMP) + return 0; + + if (hcint & (DWC2_HCINT_NAK | DWC2_HCINT_FRMOVRUN)) + return -EAGAIN; + + dev_dbg(dev, "%s: Error (HCINT=%08x)\n", __func__, hcint); + + return -EINVAL; +} + +static int dwc2_eptype[] = { + DWC2_HCCHAR_EPTYPE_ISOC, + DWC2_HCCHAR_EPTYPE_INTR, + DWC2_HCCHAR_EPTYPE_CONTROL, + DWC2_HCCHAR_EPTYPE_BULK, +}; + +static int transfer_chunk(struct dwc2_priv *priv, u8 *pid, int in, void *buffer, + int num_packets, int xfer_len, int *actual_len, + int odd_frame, int timeout_ms) +{ + struct dwc2_core_regs *regs = priv->regs; + struct dwc2_hc_regs *hc_regs = ®s->hc_regs[DWC2_HC_CHANNEL]; + int ret = 0; + uint32_t sub = 0; + enum dma_data_direction dir; + dma_addr_t dma = 0; + + dev_dbg(priv->dev, "%s: chunk: pid %d xfer_len %u pkts %u\n", + __func__, *pid, xfer_len, num_packets); + + writel((xfer_len << DWC2_HCTSIZ_XFERSIZE_OFFSET) | + (num_packets << DWC2_HCTSIZ_PKTCNT_OFFSET) | + (*pid << DWC2_HCTSIZ_PID_OFFSET), + &hc_regs->hctsiz); + + if (xfer_len) { + if (in) { + dir = DMA_FROM_DEVICE; + } else { + memcpy(priv->dmabuf, buffer, xfer_len); + dir = DMA_TO_DEVICE; + } + dma = dma_map_single(priv->dev, priv->dmabuf, xfer_len, dir); + } + + writel(dma, &hc_regs->hcdma); + + /* Clear old interrupt conditions for this host channel. */ + writel(0x3fff, &hc_regs->hcint); + + /* Set host channel enable after all other setup is complete. */ + clrsetbits_le32(&hc_regs->hcchar, DWC2_HCCHAR_MULTICNT_MASK | + DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS | + DWC2_HCCHAR_ODDFRM, + (1 << DWC2_HCCHAR_MULTICNT_OFFSET) | + (odd_frame << DWC2_HCCHAR_ODDFRM_OFFSET) | + DWC2_HCCHAR_CHEN); + + ret = wait_for_chhltd(priv, &sub, pid, timeout_ms); + + if (xfer_len) + dma_unmap_single(priv->dev, dma, xfer_len, dir); + + if (in) { + xfer_len -= sub; + + memcpy(buffer, priv->dmabuf, xfer_len); + } + + if (!ret) + *actual_len = xfer_len; + + return ret; +} + +static int usb_find_usb2_hub_address_port(struct usb_device *udev, + uint8_t *hub_address, uint8_t *hub_port) +{ + /* Find out the nearest parent which is high speed */ + while (udev->parent->parent) { + if (udev->parent->speed != USB_SPEED_HIGH) { + udev = udev->parent; + } else { + *hub_address = udev->parent->devnum; + *hub_port = udev->portnr; + return 0; + } + } + + return -EINVAL; +} + +static int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev, + unsigned long pipe, u8 *pid, int in, void *buffer, int len, + int timeout_ms) +{ + struct dwc2_core_regs *regs = priv->regs; + struct dwc2_hc_regs *hc_regs = ®s->hc_regs[DWC2_HC_CHANNEL]; + struct dwc2_host_regs *host_regs = ®s->host_regs; + int devnum = usb_pipedevice(pipe); + int ep = usb_pipeendpoint(pipe); + int max = usb_maxpacket(dev, pipe); + int eptype = dwc2_eptype[usb_pipetype(pipe)]; + int done = 0; + int ret = 0; + int do_split = 0; + int complete_split = 0; + uint32_t xfer_len; + uint32_t num_packets; + int stop_transfer = 0; + uint32_t max_xfer_len; + int ssplit_frame_num = 0; + + dev_dbg(priv->dev, "%s: msg: pipe %lx pid %d in %d len %d\n", + __func__, pipe, *pid, in, len); + + max_xfer_len = CONFIG_DWC2_MAX_PACKET_COUNT * max; + if (max_xfer_len > CONFIG_DWC2_MAX_TRANSFER_SIZE) + max_xfer_len = CONFIG_DWC2_MAX_TRANSFER_SIZE; + if (max_xfer_len > DWC2_DATA_BUF_SIZE) + max_xfer_len = DWC2_DATA_BUF_SIZE; + + /* Make sure that max_xfer_len is a multiple of max packet size. */ + num_packets = max_xfer_len / max; + max_xfer_len = num_packets * max; + + /* Initialize channel */ + dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, dev, devnum, ep, in, + eptype, max); + + /* Check if the target is a FS/LS device behind a HS hub */ + if (dev->speed != USB_SPEED_HIGH) { + uint8_t hub_addr; + uint8_t hub_port; + uint32_t hprt0 = readl(®s->hprt0); + + if ((hprt0 & DWC2_HPRT0_PRTSPD_MASK) == DWC2_HPRT0_PRTSPD_HIGH) { + ret = usb_find_usb2_hub_address_port(dev, &hub_addr, + &hub_port); + if (ret) + return ret; + dwc_otg_hc_init_split(hc_regs, hub_addr, hub_port); + + do_split = 1; + num_packets = 1; + max_xfer_len = max; + } + } + + do { + int actual_len = 0; + uint32_t hcint; + int odd_frame = 0; + xfer_len = len - done; + + if (xfer_len > max_xfer_len) + xfer_len = max_xfer_len; + else if (xfer_len > max) + num_packets = (xfer_len + max - 1) / max; + else + num_packets = 1; + + if (complete_split) + setbits_le32(&hc_regs->hcsplt, DWC2_HCSPLT_COMPSPLT); + else if (do_split) + clrbits_le32(&hc_regs->hcsplt, DWC2_HCSPLT_COMPSPLT); + + if (eptype == DWC2_HCCHAR_EPTYPE_INTR) { + int uframe_num = readl(&host_regs->hfnum); + if (!(uframe_num & 0x1)) + odd_frame = 1; + } + + ret = transfer_chunk(priv, pid, in, (char *)buffer + done, + num_packets, xfer_len, &actual_len, + odd_frame, timeout_ms); + + hcint = readl(&hc_regs->hcint); + if (complete_split) { + stop_transfer = 0; + if (hcint & DWC2_HCINT_NYET) { + int frame_num = DWC2_HFNUM_MAX_FRNUM & + readl(&host_regs->hfnum); + ret = 0; + if (((frame_num - ssplit_frame_num) & + DWC2_HFNUM_MAX_FRNUM) > 4) + ret = -EAGAIN; + } else + complete_split = 0; + } else if (do_split) { + if (hcint & DWC2_HCINT_ACK) { + ssplit_frame_num = DWC2_HFNUM_MAX_FRNUM & + readl(&host_regs->hfnum); + ret = 0; + complete_split = 1; + } + } + + if (ret) + break; + + if (actual_len < xfer_len) + stop_transfer = 1; + + done += actual_len; + + /* + * Transactions are done when when either all data is transferred or + * there is a short transfer. In case of a SPLIT make sure the CSPLIT + * is executed. + */ + } while (((done < len) && !stop_transfer) || complete_split); + + writel(0, &hc_regs->hcintmsk); + writel(0xFFFFFFFF, &hc_regs->hcint); + + dev->status = 0; + dev->act_len = done; + + return ret; +} + +#define to_dwc2(ptr) container_of(ptr, struct dwc2_priv, host) + +static int dwc2_init_common(struct usb_host *host) +{ + struct dwc2_priv *priv = to_dwc2(host); + struct dwc2_core_regs *regs = priv->regs; + int i, j; + + priv->ext_vbus = 0; + + dwc_otg_core_init(priv); + dwc_otg_core_host_init(priv); + + clrsetbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | + DWC2_HPRT0_PRTCONNDET | DWC2_HPRT0_PRTENCHNG | + DWC2_HPRT0_PRTOVRCURRCHNG, + DWC2_HPRT0_PRTRST); + mdelay(50); + clrbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | DWC2_HPRT0_PRTCONNDET | + DWC2_HPRT0_PRTENCHNG | DWC2_HPRT0_PRTOVRCURRCHNG | + DWC2_HPRT0_PRTRST); + + for (i = 0; i < MAX_DEVICE; i++) { + for (j = 0; j < MAX_ENDPOINT; j++) { + priv->in_data_toggle[i][j] = DWC2_HC_PID_DATA0; + priv->out_data_toggle[i][j] = DWC2_HC_PID_DATA0; + } + } + + return 0; +} + +static void dwc2_uninit_common(struct dwc2_core_regs *regs) +{ + /* Put everything in reset. */ + clrsetbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | + DWC2_HPRT0_PRTCONNDET | DWC2_HPRT0_PRTENCHNG | + DWC2_HPRT0_PRTOVRCURRCHNG, + DWC2_HPRT0_PRTRST); +} + +static int dwc2_submit_control_msg(struct usb_device *udev, + unsigned long pipe, void *buffer, int len, + struct devrequest *setup, int timeout_ms) +{ + struct usb_host *host = udev->host; + struct dwc2_priv *priv = to_dwc2(host); + int devnum = usb_pipedevice(pipe); + int ret, act_len; + u8 pid; + /* For CONTROL endpoint pid should start with DATA1 */ + int status_direction; + + if (devnum == priv->root_hub_devnum) { + udev->status = 0; + udev->speed = USB_SPEED_HIGH; + return dwc_otg_submit_rh_msg(priv, udev, pipe, buffer, len, + setup); + } + + /* SETUP stage */ + pid = DWC2_HC_PID_SETUP; + do { + ret = chunk_msg(priv, udev, pipe, &pid, 0, setup, 8, timeout_ms); + } while (ret == -EAGAIN); + + if (ret) + return ret; + + /* DATA stage */ + act_len = 0; + if (buffer) { + pid = DWC2_HC_PID_DATA1; + do { + ret = chunk_msg(priv, udev, pipe, &pid, usb_pipein(pipe), + buffer, len, timeout_ms); + act_len += udev->act_len; + buffer += udev->act_len; + len -= udev->act_len; + } while (ret == -EAGAIN); + if (ret) + return ret; + status_direction = usb_pipeout(pipe); + } else { + /* No-data CONTROL always ends with an IN transaction */ + status_direction = 1; + } + + /* STATUS stage */ + pid = DWC2_HC_PID_DATA1; + do { + ret = chunk_msg(priv, udev, pipe, &pid, status_direction, + NULL, 0, timeout_ms); + } while (ret == -EAGAIN); + + if (ret) + return ret; + + udev->act_len = act_len; + + return 0; +} + +static int dwc2_submit_bulk_msg(struct usb_device *udev, unsigned long pipe, + void *buffer, int len, int timeout_ms) +{ + struct usb_host *host = udev->host; + struct dwc2_priv *priv = to_dwc2(host); + int devnum = usb_pipedevice(pipe); + int ep = usb_pipeendpoint(pipe); + u8* pid; + + if ((devnum >= MAX_DEVICE) || (devnum == priv->root_hub_devnum)) { + udev->status = 0; + return -EINVAL; + } + + if (usb_pipein(pipe)) + pid = &priv->in_data_toggle[devnum][ep]; + else + pid = &priv->out_data_toggle[devnum][ep]; + + return chunk_msg(priv, udev, pipe, pid, usb_pipein(pipe), buffer, len, + timeout_ms); +} + +static int dwc2_submit_int_msg(struct usb_device *udev, unsigned long pipe, + void *buffer, int len, int interval) +{ + uint64_t start; + int ret; + + start = get_time_ns(); + + while (1) { + ret = dwc2_submit_bulk_msg(udev, pipe, buffer, len, 0); + if (ret != -EAGAIN) + return ret; + if (is_timeout(start, USB_CNTL_TIMEOUT * MSECOND)) + return -ETIMEDOUT; + } +} + +static int dwc2_detect(struct device_d *dev) +{ + struct dwc2_priv *priv = dev->priv; + + return usb_host_detect(&priv->host); +} + +static int dwc2_probe(struct device_d *dev) +{ + struct resource *iores; + struct dwc2_priv *priv; + struct usb_host *host; + struct device_node *np = dev->device_node; + int ret; + uint32_t snpsid; + + priv = xzalloc(sizeof(*priv)); + + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); + + priv->regs = IOMEM(iores->start); + priv->dev = dev; + + snpsid = readl(&priv->regs->gsnpsid); + dev_info(dev, "Core Release: %x.%03x\n", + snpsid >> 12 & 0xf, snpsid & 0xfff); + + if ((snpsid & DWC2_SNPSID_DEVID_MASK) != DWC2_SNPSID_DEVID_VER_2xx && + (snpsid & DWC2_SNPSID_DEVID_MASK) != DWC2_SNPSID_DEVID_VER_3xx) { + dev_info(dev, "SNPSID invalid (not DWC2 OTG device): %08x\n", + snpsid); + return -ENODEV; + } + + priv->oc_disable = of_property_read_bool(np, "disable-over-current"); + priv->hnp_srp_disable = of_property_read_bool(np, "hnp-srp-disable"); + priv->dmabuf = dma_alloc(DWC2_DATA_BUF_SIZE); + + host = &priv->host; + + host->init = dwc2_init_common; + host->submit_int_msg = dwc2_submit_int_msg; + host->submit_control_msg = dwc2_submit_control_msg; + host->submit_bulk_msg = dwc2_submit_bulk_msg; + + dev->priv = priv; + dev->detect = dwc2_detect; + + ret = usb_register_host(host); + if (ret) + return ret; + + return 0; +} + +static void dwc2_remove(struct device_d *dev) +{ + struct dwc2_priv *priv = dev->priv; + + dwc2_uninit_common(priv->regs); +} + +static const struct of_device_id dwc2_dt_ids[] = { + { .compatible = "brcm,bcm2835-usb" }, + { .compatible = "brcm,bcm2708-usb" }, + { .compatible = "snps,dwc2" }, + { /* sentinel */ } +}; + +static struct driver_d dwc2_driver = { + .name = "dwc2", + .probe = dwc2_probe, + .remove = dwc2_remove, + .of_compatible = DRV_OF_COMPAT(dwc2_dt_ids), +}; +device_platform_driver(dwc2_driver); diff --git a/drivers/usb/host/dwc2.h b/drivers/usb/host/dwc2.h new file mode 100644 index 0000000000..bdf338f1ed --- /dev/null +++ b/drivers/usb/host/dwc2.h @@ -0,0 +1,778 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2014 Marek Vasut <marex@denx.de> + */ + +#ifndef __DWC2_H__ +#define __DWC2_H__ + +struct dwc2_hc_regs { + u32 hcchar; /* 0x00 */ + u32 hcsplt; + u32 hcint; + u32 hcintmsk; + u32 hctsiz; /* 0x10 */ + u32 hcdma; + u32 reserved; + u32 hcdmab; +}; + +struct dwc2_host_regs { + u32 hcfg; /* 0x00 */ + u32 hfir; + u32 hfnum; + u32 _pad_0x40c; + u32 hptxsts; /* 0x10 */ + u32 haint; + u32 haintmsk; + u32 hflbaddr; +}; + +struct dwc2_core_regs { + u32 gotgctl; /* 0x000 */ + u32 gotgint; + u32 gahbcfg; + u32 gusbcfg; + u32 grstctl; /* 0x010 */ + u32 gintsts; + u32 gintmsk; + u32 grxstsr; + u32 grxstsp; /* 0x020 */ + u32 grxfsiz; + u32 gnptxfsiz; + u32 gnptxsts; + u32 gi2cctl; /* 0x030 */ + u32 gpvndctl; + u32 ggpio; + u32 guid; + u32 gsnpsid; /* 0x040 */ + u32 ghwcfg1; + u32 ghwcfg2; + u32 ghwcfg3; + u32 ghwcfg4; /* 0x050 */ + u32 glpmcfg; + u32 _pad_0x58_0x9c[42]; + u32 hptxfsiz; /* 0x100 */ + u32 dptxfsiz_dieptxf[15]; + u32 _pad_0x140_0x3fc[176]; + struct dwc2_host_regs host_regs; /* 0x400 */ + u32 _pad_0x420_0x43c[8]; + u32 hprt0; /* 0x440 */ + u32 _pad_0x444_0x4fc[47]; + struct dwc2_hc_regs hc_regs[16]; /* 0x500 */ + u32 _pad_0x700_0xe00[448]; + u32 pcgcctl; /* 0xe00 */ +}; + +#define DWC2_GOTGCTL_SESREQSCS (1 << 0) +#define DWC2_GOTGCTL_SESREQSCS_OFFSET 0 +#define DWC2_GOTGCTL_SESREQ (1 << 1) +#define DWC2_GOTGCTL_SESREQ_OFFSET 1 +#define DWC2_GOTGCTL_HSTNEGSCS (1 << 8) +#define DWC2_GOTGCTL_HSTNEGSCS_OFFSET 8 +#define DWC2_GOTGCTL_HNPREQ (1 << 9) +#define DWC2_GOTGCTL_HNPREQ_OFFSET 9 +#define DWC2_GOTGCTL_HSTSETHNPEN (1 << 10) +#define DWC2_GOTGCTL_HSTSETHNPEN_OFFSET 10 +#define DWC2_GOTGCTL_DEVHNPEN (1 << 11) +#define DWC2_GOTGCTL_DEVHNPEN_OFFSET 11 +#define DWC2_GOTGCTL_CONIDSTS (1 << 16) +#define DWC2_GOTGCTL_CONIDSTS_OFFSET 16 +#define DWC2_GOTGCTL_DBNCTIME (1 << 17) +#define DWC2_GOTGCTL_DBNCTIME_OFFSET 17 +#define DWC2_GOTGCTL_ASESVLD (1 << 18) +#define DWC2_GOTGCTL_ASESVLD_OFFSET 18 +#define DWC2_GOTGCTL_BSESVLD (1 << 19) +#define DWC2_GOTGCTL_BSESVLD_OFFSET 19 +#define DWC2_GOTGCTL_OTGVER (1 << 20) +#define DWC2_GOTGCTL_OTGVER_OFFSET 20 +#define DWC2_GOTGINT_SESENDDET (1 << 2) +#define DWC2_GOTGINT_SESENDDET_OFFSET 2 +#define DWC2_GOTGINT_SESREQSUCSTSCHNG (1 << 8) +#define DWC2_GOTGINT_SESREQSUCSTSCHNG_OFFSET 8 +#define DWC2_GOTGINT_HSTNEGSUCSTSCHNG (1 << 9) +#define DWC2_GOTGINT_HSTNEGSUCSTSCHNG_OFFSET 9 +#define DWC2_GOTGINT_RESERVER10_16_MASK (0x7F << 10) +#define DWC2_GOTGINT_RESERVER10_16_OFFSET 10 +#define DWC2_GOTGINT_HSTNEGDET (1 << 17) +#define DWC2_GOTGINT_HSTNEGDET_OFFSET 17 +#define DWC2_GOTGINT_ADEVTOUTCHNG (1 << 18) +#define DWC2_GOTGINT_ADEVTOUTCHNG_OFFSET 18 +#define DWC2_GOTGINT_DEBDONE (1 << 19) +#define DWC2_GOTGINT_DEBDONE_OFFSET 19 +#define DWC2_GAHBCFG_GLBLINTRMSK (1 << 0) +#define DWC2_GAHBCFG_GLBLINTRMSK_OFFSET 0 +#define DWC2_GAHBCFG_HBURSTLEN_SINGLE (0 << 1) +#define DWC2_GAHBCFG_HBURSTLEN_INCR (1 << 1) +#define DWC2_GAHBCFG_HBURSTLEN_INCR4 (3 << 1) +#define DWC2_GAHBCFG_HBURSTLEN_INCR8 (5 << 1) +#define DWC2_GAHBCFG_HBURSTLEN_INCR16 (7 << 1) +#define DWC2_GAHBCFG_HBURSTLEN_MASK (0xF << 1) +#define DWC2_GAHBCFG_HBURSTLEN_OFFSET 1 +#define DWC2_GAHBCFG_DMAENABLE (1 << 5) +#define DWC2_GAHBCFG_DMAENABLE_OFFSET 5 +#define DWC2_GAHBCFG_NPTXFEMPLVL_TXFEMPLVL (1 << 7) +#define DWC2_GAHBCFG_NPTXFEMPLVL_TXFEMPLVL_OFFSET 7 +#define DWC2_GAHBCFG_PTXFEMPLVL (1 << 8) +#define DWC2_GAHBCFG_PTXFEMPLVL_OFFSET 8 +#define DWC2_GUSBCFG_TOUTCAL_MASK (0x7 << 0) +#define DWC2_GUSBCFG_TOUTCAL_OFFSET 0 +#define DWC2_GUSBCFG_PHYIF (1 << 3) +#define DWC2_GUSBCFG_PHYIF_OFFSET 3 +#define DWC2_GUSBCFG_ULPI_UTMI_SEL (1 << 4) +#define DWC2_GUSBCFG_ULPI_UTMI_SEL_OFFSET 4 +#define DWC2_GUSBCFG_FSINTF (1 << 5) +#define DWC2_GUSBCFG_FSINTF_OFFSET 5 +#define DWC2_GUSBCFG_PHYSEL (1 << 6) +#define DWC2_GUSBCFG_PHYSEL_OFFSET 6 +#define DWC2_GUSBCFG_DDRSEL (1 << 7) +#define DWC2_GUSBCFG_DDRSEL_OFFSET 7 +#define DWC2_GUSBCFG_SRPCAP (1 << 8) +#define DWC2_GUSBCFG_SRPCAP_OFFSET 8 +#define DWC2_GUSBCFG_HNPCAP (1 << 9) +#define DWC2_GUSBCFG_HNPCAP_OFFSET 9 +#define DWC2_GUSBCFG_USBTRDTIM_MASK (0xF << 10) +#define DWC2_GUSBCFG_USBTRDTIM_OFFSET 10 +#define DWC2_GUSBCFG_NPTXFRWNDEN (1 << 14) +#define DWC2_GUSBCFG_NPTXFRWNDEN_OFFSET 14 +#define DWC2_GUSBCFG_PHYLPWRCLKSEL (1 << 15) +#define DWC2_GUSBCFG_PHYLPWRCLKSEL_OFFSET 15 +#define DWC2_GUSBCFG_OTGUTMIFSSEL (1 << 16) +#define DWC2_GUSBCFG_OTGUTMIFSSEL_OFFSET 16 +#define DWC2_GUSBCFG_ULPI_FSLS (1 << 17) +#define DWC2_GUSBCFG_ULPI_FSLS_OFFSET 17 +#define DWC2_GUSBCFG_ULPI_AUTO_RES (1 << 18) +#define DWC2_GUSBCFG_ULPI_AUTO_RES_OFFSET 18 +#define DWC2_GUSBCFG_ULPI_CLK_SUS_M (1 << 19) +#define DWC2_GUSBCFG_ULPI_CLK_SUS_M_OFFSET 19 +#define DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV (1 << 20) +#define DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV_OFFSET 20 +#define DWC2_GUSBCFG_ULPI_INT_VBUS_INDICATOR (1 << 21) +#define DWC2_GUSBCFG_ULPI_INT_VBUS_INDICATOR_OFFSET 21 +#define DWC2_GUSBCFG_TERM_SEL_DL_PULSE (1 << 22) +#define DWC2_GUSBCFG_TERM_SEL_DL_PULSE_OFFSET 22 +#define DWC2_GUSBCFG_INDICATOR_PASSTHROUGH (1 << 24) +#define DWC2_GUSBCFG_INDICATOR_PASSTHROUGH_OFFSET 24 +#define DWC2_GUSBCFG_IC_USB_CAP (1 << 26) +#define DWC2_GUSBCFG_IC_USB_CAP_OFFSET 26 +#define DWC2_GUSBCFG_IC_TRAFFIC_PULL_REMOVE (1 << 27) +#define DWC2_GUSBCFG_IC_TRAFFIC_PULL_REMOVE_OFFSET 27 +#define DWC2_GUSBCFG_TX_END_DELAY (1 << 28) +#define DWC2_GUSBCFG_TX_END_DELAY_OFFSET 28 +#define DWC2_GUSBCFG_FORCEHOSTMODE (1 << 29) +#define DWC2_GUSBCFG_FORCEHOSTMODE_OFFSET 29 +#define DWC2_GUSBCFG_FORCEDEVMODE (1 << 30) +#define DWC2_GUSBCFG_FORCEDEVMODE_OFFSET 30 +#define DWC2_GLPMCTL_LPM_CAP_EN (1 << 0) +#define DWC2_GLPMCTL_LPM_CAP_EN_OFFSET 0 +#define DWC2_GLPMCTL_APPL_RESP (1 << 1) +#define DWC2_GLPMCTL_APPL_RESP_OFFSET 1 +#define DWC2_GLPMCTL_HIRD_MASK (0xF << 2) +#define DWC2_GLPMCTL_HIRD_OFFSET 2 +#define DWC2_GLPMCTL_REM_WKUP_EN (1 << 6) +#define DWC2_GLPMCTL_REM_WKUP_EN_OFFSET 6 +#define DWC2_GLPMCTL_EN_UTMI_SLEEP (1 << 7) +#define DWC2_GLPMCTL_EN_UTMI_SLEEP_OFFSET 7 +#define DWC2_GLPMCTL_HIRD_THRES_MASK (0x1F << 8) +#define DWC2_GLPMCTL_HIRD_THRES_OFFSET 8 +#define DWC2_GLPMCTL_LPM_RESP_MASK (0x3 << 13) +#define DWC2_GLPMCTL_LPM_RESP_OFFSET 13 +#define DWC2_GLPMCTL_PRT_SLEEP_STS (1 << 15) +#define DWC2_GLPMCTL_PRT_SLEEP_STS_OFFSET 15 +#define DWC2_GLPMCTL_SLEEP_STATE_RESUMEOK (1 << 16) +#define DWC2_GLPMCTL_SLEEP_STATE_RESUMEOK_OFFSET 16 +#define DWC2_GLPMCTL_LPM_CHAN_INDEX_MASK (0xF << 17) +#define DWC2_GLPMCTL_LPM_CHAN_INDEX_OFFSET 17 +#define DWC2_GLPMCTL_RETRY_COUNT_MASK (0x7 << 21) +#define DWC2_GLPMCTL_RETRY_COUNT_OFFSET 21 +#define DWC2_GLPMCTL_SEND_LPM (1 << 24) +#define DWC2_GLPMCTL_SEND_LPM_OFFSET 24 +#define DWC2_GLPMCTL_RETRY_COUNT_STS_MASK (0x7 << 25) +#define DWC2_GLPMCTL_RETRY_COUNT_STS_OFFSET 25 +#define DWC2_GLPMCTL_HSIC_CONNECT (1 << 30) +#define DWC2_GLPMCTL_HSIC_CONNECT_OFFSET 30 +#define DWC2_GLPMCTL_INV_SEL_HSIC (1 << 31) +#define DWC2_GLPMCTL_INV_SEL_HSIC_OFFSET 31 +#define DWC2_GRSTCTL_CSFTRST (1 << 0) +#define DWC2_GRSTCTL_CSFTRST_OFFSET 0 +#define DWC2_GRSTCTL_HSFTRST (1 << 1) +#define DWC2_GRSTCTL_HSFTRST_OFFSET 1 +#define DWC2_GRSTCTL_HSTFRM (1 << 2) +#define DWC2_GRSTCTL_HSTFRM_OFFSET 2 +#define DWC2_GRSTCTL_INTKNQFLSH (1 << 3) +#define DWC2_GRSTCTL_INTKNQFLSH_OFFSET 3 +#define DWC2_GRSTCTL_RXFFLSH (1 << 4) +#define DWC2_GRSTCTL_RXFFLSH_OFFSET 4 +#define DWC2_GRSTCTL_TXFFLSH (1 << 5) +#define DWC2_GRSTCTL_TXFFLSH_OFFSET 5 +#define DWC2_GRSTCTL_TXFNUM_MASK (0x1F << 6) +#define DWC2_GRSTCTL_TXFNUM_OFFSET 6 +#define DWC2_GRSTCTL_DMAREQ (1 << 30) +#define DWC2_GRSTCTL_DMAREQ_OFFSET 30 +#define DWC2_GRSTCTL_AHBIDLE (1 << 31) +#define DWC2_GRSTCTL_AHBIDLE_OFFSET 31 +#define DWC2_GINTMSK_MODEMISMATCH (1 << 1) +#define DWC2_GINTMSK_MODEMISMATCH_OFFSET 1 +#define DWC2_GINTMSK_OTGINTR (1 << 2) +#define DWC2_GINTMSK_OTGINTR_OFFSET 2 +#define DWC2_GINTMSK_SOFINTR (1 << 3) +#define DWC2_GINTMSK_SOFINTR_OFFSET 3 +#define DWC2_GINTMSK_RXSTSQLVL (1 << 4) +#define DWC2_GINTMSK_RXSTSQLVL_OFFSET 4 +#define DWC2_GINTMSK_NPTXFEMPTY (1 << 5) +#define DWC2_GINTMSK_NPTXFEMPTY_OFFSET 5 +#define DWC2_GINTMSK_GINNAKEFF (1 << 6) +#define DWC2_GINTMSK_GINNAKEFF_OFFSET 6 +#define DWC2_GINTMSK_GOUTNAKEFF (1 << 7) +#define DWC2_GINTMSK_GOUTNAKEFF_OFFSET 7 +#define DWC2_GINTMSK_I2CINTR (1 << 9) +#define DWC2_GINTMSK_I2CINTR_OFFSET 9 +#define DWC2_GINTMSK_ERLYSUSPEND (1 << 10) +#define DWC2_GINTMSK_ERLYSUSPEND_OFFSET 10 +#define DWC2_GINTMSK_USBSUSPEND (1 << 11) +#define DWC2_GINTMSK_USBSUSPEND_OFFSET 11 +#define DWC2_GINTMSK_USBRESET (1 << 12) +#define DWC2_GINTMSK_USBRESET_OFFSET 12 +#define DWC2_GINTMSK_ENUMDONE (1 << 13) +#define DWC2_GINTMSK_ENUMDONE_OFFSET 13 +#define DWC2_GINTMSK_ISOOUTDROP (1 << 14) +#define DWC2_GINTMSK_ISOOUTDROP_OFFSET 14 +#define DWC2_GINTMSK_EOPFRAME (1 << 15) +#define DWC2_GINTMSK_EOPFRAME_OFFSET 15 +#define DWC2_GINTMSK_EPMISMATCH (1 << 17) +#define DWC2_GINTMSK_EPMISMATCH_OFFSET 17 +#define DWC2_GINTMSK_INEPINTR (1 << 18) +#define DWC2_GINTMSK_INEPINTR_OFFSET 18 +#define DWC2_GINTMSK_OUTEPINTR (1 << 19) +#define DWC2_GINTMSK_OUTEPINTR_OFFSET 19 +#define DWC2_GINTMSK_INCOMPLISOIN (1 << 20) +#define DWC2_GINTMSK_INCOMPLISOIN_OFFSET 20 +#define DWC2_GINTMSK_INCOMPLISOOUT (1 << 21) +#define DWC2_GINTMSK_INCOMPLISOOUT_OFFSET 21 +#define DWC2_GINTMSK_PORTINTR (1 << 24) +#define DWC2_GINTMSK_PORTINTR_OFFSET 24 +#define DWC2_GINTMSK_HCINTR (1 << 25) +#define DWC2_GINTMSK_HCINTR_OFFSET 25 +#define DWC2_GINTMSK_PTXFEMPTY (1 << 26) +#define DWC2_GINTMSK_PTXFEMPTY_OFFSET 26 +#define DWC2_GINTMSK_LPMTRANRCVD (1 << 27) +#define DWC2_GINTMSK_LPMTRANRCVD_OFFSET 27 +#define DWC2_GINTMSK_CONIDSTSCHNG (1 << 28) +#define DWC2_GINTMSK_CONIDSTSCHNG_OFFSET 28 +#define DWC2_GINTMSK_DISCONNECT (1 << 29) +#define DWC2_GINTMSK_DISCONNECT_OFFSET 29 +#define DWC2_GINTMSK_SESSREQINTR (1 << 30) +#define DWC2_GINTMSK_SESSREQINTR_OFFSET 30 +#define DWC2_GINTMSK_WKUPINTR (1 << 31) +#define DWC2_GINTMSK_WKUPINTR_OFFSET 31 +#define DWC2_GINTSTS_CURMODE_DEVICE (0 << 0) +#define DWC2_GINTSTS_CURMODE_HOST (1 << 0) +#define DWC2_GINTSTS_CURMODE (1 << 0) +#define DWC2_GINTSTS_CURMODE_OFFSET 0 +#define DWC2_GINTSTS_MODEMISMATCH (1 << 1) +#define DWC2_GINTSTS_MODEMISMATCH_OFFSET 1 +#define DWC2_GINTSTS_OTGINTR (1 << 2) +#define DWC2_GINTSTS_OTGINTR_OFFSET 2 +#define DWC2_GINTSTS_SOFINTR (1 << 3) +#define DWC2_GINTSTS_SOFINTR_OFFSET 3 +#define DWC2_GINTSTS_RXSTSQLVL (1 << 4) +#define DWC2_GINTSTS_RXSTSQLVL_OFFSET 4 +#define DWC2_GINTSTS_NPTXFEMPTY (1 << 5) +#define DWC2_GINTSTS_NPTXFEMPTY_OFFSET 5 +#define DWC2_GINTSTS_GINNAKEFF (1 << 6) +#define DWC2_GINTSTS_GINNAKEFF_OFFSET 6 +#define DWC2_GINTSTS_GOUTNAKEFF (1 << 7) +#define DWC2_GINTSTS_GOUTNAKEFF_OFFSET 7 +#define DWC2_GINTSTS_I2CINTR (1 << 9) +#define DWC2_GINTSTS_I2CINTR_OFFSET 9 +#define DWC2_GINTSTS_ERLYSUSPEND (1 << 10) +#define DWC2_GINTSTS_ERLYSUSPEND_OFFSET 10 +#define DWC2_GINTSTS_USBSUSPEND (1 << 11) +#define DWC2_GINTSTS_USBSUSPEND_OFFSET 11 +#define DWC2_GINTSTS_USBRESET (1 << 12) +#define DWC2_GINTSTS_USBRESET_OFFSET 12 +#define DWC2_GINTSTS_ENUMDONE (1 << 13) +#define DWC2_GINTSTS_ENUMDONE_OFFSET 13 +#define DWC2_GINTSTS_ISOOUTDROP (1 << 14) +#define DWC2_GINTSTS_ISOOUTDROP_OFFSET 14 +#define DWC2_GINTSTS_EOPFRAME (1 << 15) +#define DWC2_GINTSTS_EOPFRAME_OFFSET 15 +#define DWC2_GINTSTS_INTOKENRX (1 << 16) +#define DWC2_GINTSTS_INTOKENRX_OFFSET 16 +#define DWC2_GINTSTS_EPMISMATCH (1 << 17) +#define DWC2_GINTSTS_EPMISMATCH_OFFSET 17 +#define DWC2_GINTSTS_INEPINT (1 << 18) +#define DWC2_GINTSTS_INEPINT_OFFSET 18 +#define DWC2_GINTSTS_OUTEPINTR (1 << 19) +#define DWC2_GINTSTS_OUTEPINTR_OFFSET 19 +#define DWC2_GINTSTS_INCOMPLISOIN (1 << 20) +#define DWC2_GINTSTS_INCOMPLISOIN_OFFSET 20 +#define DWC2_GINTSTS_INCOMPLISOOUT (1 << 21) +#define DWC2_GINTSTS_INCOMPLISOOUT_OFFSET 21 +#define DWC2_GINTSTS_PORTINTR (1 << 24) +#define DWC2_GINTSTS_PORTINTR_OFFSET 24 +#define DWC2_GINTSTS_HCINTR (1 << 25) +#define DWC2_GINTSTS_HCINTR_OFFSET 25 +#define DWC2_GINTSTS_PTXFEMPTY (1 << 26) +#define DWC2_GINTSTS_PTXFEMPTY_OFFSET 26 +#define DWC2_GINTSTS_LPMTRANRCVD (1 << 27) +#define DWC2_GINTSTS_LPMTRANRCVD_OFFSET 27 +#define DWC2_GINTSTS_CONIDSTSCHNG (1 << 28) +#define DWC2_GINTSTS_CONIDSTSCHNG_OFFSET 28 +#define DWC2_GINTSTS_DISCONNECT (1 << 29) +#define DWC2_GINTSTS_DISCONNECT_OFFSET 29 +#define DWC2_GINTSTS_SESSREQINTR (1 << 30) +#define DWC2_GINTSTS_SESSREQINTR_OFFSET 30 +#define DWC2_GINTSTS_WKUPINTR (1 << 31) +#define DWC2_GINTSTS_WKUPINTR_OFFSET 31 +#define DWC2_GRXSTS_EPNUM_MASK (0xF << 0) +#define DWC2_GRXSTS_EPNUM_OFFSET 0 +#define DWC2_GRXSTS_BCNT_MASK (0x7FF << 4) +#define DWC2_GRXSTS_BCNT_OFFSET 4 +#define DWC2_GRXSTS_DPID_MASK (0x3 << 15) +#define DWC2_GRXSTS_DPID_OFFSET 15 +#define DWC2_GRXSTS_PKTSTS_MASK (0xF << 17) +#define DWC2_GRXSTS_PKTSTS_OFFSET 17 +#define DWC2_GRXSTS_FN_MASK (0xF << 21) +#define DWC2_GRXSTS_FN_OFFSET 21 +#define DWC2_FIFOSIZE_STARTADDR_MASK (0xFFFF << 0) +#define DWC2_FIFOSIZE_STARTADDR_OFFSET 0 +#define DWC2_FIFOSIZE_DEPTH_MASK (0xFFFF << 16) +#define DWC2_FIFOSIZE_DEPTH_OFFSET 16 +#define DWC2_GNPTXSTS_NPTXFSPCAVAIL_MASK (0xFFFF << 0) +#define DWC2_GNPTXSTS_NPTXFSPCAVAIL_OFFSET 0 +#define DWC2_GNPTXSTS_NPTXQSPCAVAIL_MASK (0xFF << 16) +#define DWC2_GNPTXSTS_NPTXQSPCAVAIL_OFFSET 16 +#define DWC2_GNPTXSTS_NPTXQTOP_TERMINATE (1 << 24) +#define DWC2_GNPTXSTS_NPTXQTOP_TERMINATE_OFFSET 24 +#define DWC2_GNPTXSTS_NPTXQTOP_TOKEN_MASK (0x3 << 25) +#define DWC2_GNPTXSTS_NPTXQTOP_TOKEN_OFFSET 25 +#define DWC2_GNPTXSTS_NPTXQTOP_CHNEP_MASK (0xF << 27) +#define DWC2_GNPTXSTS_NPTXQTOP_CHNEP_OFFSET 27 +#define DWC2_DTXFSTS_TXFSPCAVAIL_MASK (0xFFFF << 0) +#define DWC2_DTXFSTS_TXFSPCAVAIL_OFFSET 0 +#define DWC2_GI2CCTL_RWDATA_MASK (0xFF << 0) +#define DWC2_GI2CCTL_RWDATA_OFFSET 0 +#define DWC2_GI2CCTL_REGADDR_MASK (0xFF << 8) +#define DWC2_GI2CCTL_REGADDR_OFFSET 8 +#define DWC2_GI2CCTL_ADDR_MASK (0x7F << 16) +#define DWC2_GI2CCTL_ADDR_OFFSET 16 +#define DWC2_GI2CCTL_I2CEN (1 << 23) +#define DWC2_GI2CCTL_I2CEN_OFFSET 23 +#define DWC2_GI2CCTL_ACK (1 << 24) +#define DWC2_GI2CCTL_ACK_OFFSET 24 +#define DWC2_GI2CCTL_I2CSUSPCTL (1 << 25) +#define DWC2_GI2CCTL_I2CSUSPCTL_OFFSET 25 +#define DWC2_GI2CCTL_I2CDEVADDR_MASK (0x3 << 26) +#define DWC2_GI2CCTL_I2CDEVADDR_OFFSET 26 +#define DWC2_GI2CCTL_RW (1 << 30) +#define DWC2_GI2CCTL_RW_OFFSET 30 +#define DWC2_GI2CCTL_BSYDNE (1 << 31) +#define DWC2_GI2CCTL_BSYDNE_OFFSET 31 +#define DWC2_HWCFG1_EP_DIR0_MASK (0x3 << 0) +#define DWC2_HWCFG1_EP_DIR0_OFFSET 0 +#define DWC2_HWCFG1_EP_DIR1_MASK (0x3 << 2) +#define DWC2_HWCFG1_EP_DIR1_OFFSET 2 +#define DWC2_HWCFG1_EP_DIR2_MASK (0x3 << 4) +#define DWC2_HWCFG1_EP_DIR2_OFFSET 4 +#define DWC2_HWCFG1_EP_DIR3_MASK (0x3 << 6) +#define DWC2_HWCFG1_EP_DIR3_OFFSET 6 +#define DWC2_HWCFG1_EP_DIR4_MASK (0x3 << 8) +#define DWC2_HWCFG1_EP_DIR4_OFFSET 8 +#define DWC2_HWCFG1_EP_DIR5_MASK (0x3 << 10) +#define DWC2_HWCFG1_EP_DIR5_OFFSET 10 +#define DWC2_HWCFG1_EP_DIR6_MASK (0x3 << 12) +#define DWC2_HWCFG1_EP_DIR6_OFFSET 12 +#define DWC2_HWCFG1_EP_DIR7_MASK (0x3 << 14) +#define DWC2_HWCFG1_EP_DIR7_OFFSET 14 +#define DWC2_HWCFG1_EP_DIR8_MASK (0x3 << 16) +#define DWC2_HWCFG1_EP_DIR8_OFFSET 16 +#define DWC2_HWCFG1_EP_DIR9_MASK (0x3 << 18) +#define DWC2_HWCFG1_EP_DIR9_OFFSET 18 +#define DWC2_HWCFG1_EP_DIR10_MASK (0x3 << 20) +#define DWC2_HWCFG1_EP_DIR10_OFFSET 20 +#define DWC2_HWCFG1_EP_DIR11_MASK (0x3 << 22) +#define DWC2_HWCFG1_EP_DIR11_OFFSET 22 +#define DWC2_HWCFG1_EP_DIR12_MASK (0x3 << 24) +#define DWC2_HWCFG1_EP_DIR12_OFFSET 24 +#define DWC2_HWCFG1_EP_DIR13_MASK (0x3 << 26) +#define DWC2_HWCFG1_EP_DIR13_OFFSET 26 +#define DWC2_HWCFG1_EP_DIR14_MASK (0x3 << 28) +#define DWC2_HWCFG1_EP_DIR14_OFFSET 28 +#define DWC2_HWCFG1_EP_DIR15_MASK (0x3 << 30) +#define DWC2_HWCFG1_EP_DIR15_OFFSET 30 +#define DWC2_HWCFG2_OP_MODE_MASK (0x7 << 0) +#define DWC2_HWCFG2_OP_MODE_OFFSET 0 +#define DWC2_HWCFG2_ARCHITECTURE_SLAVE_ONLY (0x0 << 3) +#define DWC2_HWCFG2_ARCHITECTURE_EXT_DMA (0x1 << 3) +#define DWC2_HWCFG2_ARCHITECTURE_INT_DMA (0x2 << 3) +#define DWC2_HWCFG2_ARCHITECTURE_MASK (0x3 << 3) +#define DWC2_HWCFG2_ARCHITECTURE_OFFSET 3 +#define DWC2_HWCFG2_POINT2POINT (1 << 5) +#define DWC2_HWCFG2_POINT2POINT_OFFSET 5 +#define DWC2_HWCFG2_HS_PHY_TYPE_MASK (0x3 << 6) +#define DWC2_HWCFG2_HS_PHY_TYPE_OFFSET 6 +#define DWC2_HWCFG2_FS_PHY_TYPE_MASK (0x3 << 8) +#define DWC2_HWCFG2_FS_PHY_TYPE_OFFSET 8 +#define DWC2_HWCFG2_NUM_DEV_EP_MASK (0xF << 10) +#define DWC2_HWCFG2_NUM_DEV_EP_OFFSET 10 +#define DWC2_HWCFG2_NUM_HOST_CHAN_MASK (0xF << 14) +#define DWC2_HWCFG2_NUM_HOST_CHAN_OFFSET 14 +#define DWC2_HWCFG2_PERIO_EP_SUPPORTED (1 << 18) +#define DWC2_HWCFG2_PERIO_EP_SUPPORTED_OFFSET 18 +#define DWC2_HWCFG2_DYNAMIC_FIFO (1 << 19) +#define DWC2_HWCFG2_DYNAMIC_FIFO_OFFSET 19 +#define DWC2_HWCFG2_MULTI_PROC_INT (1 << 20) +#define DWC2_HWCFG2_MULTI_PROC_INT_OFFSET 20 +#define DWC2_HWCFG2_NONPERIO_TX_Q_DEPTH_MASK (0x3 << 22) +#define DWC2_HWCFG2_NONPERIO_TX_Q_DEPTH_OFFSET 22 +#define DWC2_HWCFG2_HOST_PERIO_TX_Q_DEPTH_MASK (0x3 << 24) +#define DWC2_HWCFG2_HOST_PERIO_TX_Q_DEPTH_OFFSET 24 +#define DWC2_HWCFG2_DEV_TOKEN_Q_DEPTH_MASK (0x1F << 26) +#define DWC2_HWCFG2_DEV_TOKEN_Q_DEPTH_OFFSET 26 +#define DWC2_HWCFG3_XFER_SIZE_CNTR_WIDTH_MASK (0xF << 0) +#define DWC2_HWCFG3_XFER_SIZE_CNTR_WIDTH_OFFSET 0 +#define DWC2_HWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK (0x7 << 4) +#define DWC2_HWCFG3_PACKET_SIZE_CNTR_WIDTH_OFFSET 4 +#define DWC2_HWCFG3_OTG_FUNC (1 << 7) +#define DWC2_HWCFG3_OTG_FUNC_OFFSET 7 +#define DWC2_HWCFG3_I2C (1 << 8) +#define DWC2_HWCFG3_I2C_OFFSET 8 +#define DWC2_HWCFG3_VENDOR_CTRL_IF (1 << 9) +#define DWC2_HWCFG3_VENDOR_CTRL_IF_OFFSET 9 +#define DWC2_HWCFG3_OPTIONAL_FEATURES (1 << 10) +#define DWC2_HWCFG3_OPTIONAL_FEATURES_OFFSET 10 +#define DWC2_HWCFG3_SYNCH_RESET_TYPE (1 << 11) +#define DWC2_HWCFG3_SYNCH_RESET_TYPE_OFFSET 11 +#define DWC2_HWCFG3_OTG_ENABLE_IC_USB (1 << 12) +#define DWC2_HWCFG3_OTG_ENABLE_IC_USB_OFFSET 12 +#define DWC2_HWCFG3_OTG_ENABLE_HSIC (1 << 13) +#define DWC2_HWCFG3_OTG_ENABLE_HSIC_OFFSET 13 +#define DWC2_HWCFG3_OTG_LPM_EN (1 << 15) +#define DWC2_HWCFG3_OTG_LPM_EN_OFFSET 15 +#define DWC2_HWCFG3_DFIFO_DEPTH_MASK (0xFFFF << 16) +#define DWC2_HWCFG3_DFIFO_DEPTH_OFFSET 16 +#define DWC2_HWCFG4_NUM_DEV_PERIO_IN_EP_MASK (0xF << 0) +#define DWC2_HWCFG4_NUM_DEV_PERIO_IN_EP_OFFSET 0 +#define DWC2_HWCFG4_POWER_OPTIMIZ (1 << 4) +#define DWC2_HWCFG4_POWER_OPTIMIZ_OFFSET 4 +#define DWC2_HWCFG4_MIN_AHB_FREQ_MASK (0x1FF << 5) +#define DWC2_HWCFG4_MIN_AHB_FREQ_OFFSET 5 +#define DWC2_HWCFG4_UTMI_PHY_DATA_WIDTH_MASK (0x3 << 14) +#define DWC2_HWCFG4_UTMI_PHY_DATA_WIDTH_OFFSET 14 +#define DWC2_HWCFG4_NUM_DEV_MODE_CTRL_EP_MASK (0xF << 16) +#define DWC2_HWCFG4_NUM_DEV_MODE_CTRL_EP_OFFSET 16 +#define DWC2_HWCFG4_IDDIG_FILT_EN (1 << 20) +#define DWC2_HWCFG4_IDDIG_FILT_EN_OFFSET 20 +#define DWC2_HWCFG4_VBUS_VALID_FILT_EN (1 << 21) +#define DWC2_HWCFG4_VBUS_VALID_FILT_EN_OFFSET 21 +#define DWC2_HWCFG4_A_VALID_FILT_EN (1 << 22) +#define DWC2_HWCFG4_A_VALID_FILT_EN_OFFSET 22 +#define DWC2_HWCFG4_B_VALID_FILT_EN (1 << 23) +#define DWC2_HWCFG4_B_VALID_FILT_EN_OFFSET 23 +#define DWC2_HWCFG4_SESSION_END_FILT_EN (1 << 24) +#define DWC2_HWCFG4_SESSION_END_FILT_EN_OFFSET 24 +#define DWC2_HWCFG4_DED_FIFO_EN (1 << 25) +#define DWC2_HWCFG4_DED_FIFO_EN_OFFSET 25 +#define DWC2_HWCFG4_NUM_IN_EPS_MASK (0xF << 26) +#define DWC2_HWCFG4_NUM_IN_EPS_OFFSET 26 +#define DWC2_HWCFG4_DESC_DMA (1 << 30) +#define DWC2_HWCFG4_DESC_DMA_OFFSET 30 +#define DWC2_HWCFG4_DESC_DMA_DYN (1 << 31) +#define DWC2_HWCFG4_DESC_DMA_DYN_OFFSET 31 +#define DWC2_HCFG_FSLSPCLKSEL_30_60_MHZ 0 +#define DWC2_HCFG_FSLSPCLKSEL_48_MHZ 1 +#define DWC2_HCFG_FSLSPCLKSEL_6_MHZ 2 +#define DWC2_HCFG_FSLSPCLKSEL_MASK (0x3 << 0) +#define DWC2_HCFG_FSLSPCLKSEL_OFFSET 0 +#define DWC2_HCFG_FSLSSUPP (1 << 2) +#define DWC2_HCFG_FSLSSUPP_OFFSET 2 +#define DWC2_HCFG_DESCDMA (1 << 23) +#define DWC2_HCFG_DESCDMA_OFFSET 23 +#define DWC2_HCFG_FRLISTEN_MASK (0x3 << 24) +#define DWC2_HCFG_FRLISTEN_OFFSET 24 +#define DWC2_HCFG_PERSCHEDENA (1 << 26) +#define DWC2_HCFG_PERSCHEDENA_OFFSET 26 +#define DWC2_HCFG_PERSCHEDSTAT (1 << 27) +#define DWC2_HCFG_PERSCHEDSTAT_OFFSET 27 +#define DWC2_HFIR_FRINT_MASK (0xFFFF << 0) +#define DWC2_HFIR_FRINT_OFFSET 0 +#define DWC2_HFNUM_FRNUM_MASK (0xFFFF << 0) +#define DWC2_HFNUM_FRNUM_OFFSET 0 +#define DWC2_HFNUM_FRREM_MASK (0xFFFF << 16) +#define DWC2_HFNUM_FRREM_OFFSET 16 +#define DWC2_HFNUM_MAX_FRNUM 0x3FFF +#define DWC2_HPTXSTS_PTXFSPCAVAIL_MASK (0xFFFF << 0) +#define DWC2_HPTXSTS_PTXFSPCAVAIL_OFFSET 0 +#define DWC2_HPTXSTS_PTXQSPCAVAIL_MASK (0xFF << 16) +#define DWC2_HPTXSTS_PTXQSPCAVAIL_OFFSET 16 +#define DWC2_HPTXSTS_PTXQTOP_TERMINATE (1 << 24) +#define DWC2_HPTXSTS_PTXQTOP_TERMINATE_OFFSET 24 +#define DWC2_HPTXSTS_PTXQTOP_TOKEN_MASK (0x3 << 25) +#define DWC2_HPTXSTS_PTXQTOP_TOKEN_OFFSET 25 +#define DWC2_HPTXSTS_PTXQTOP_CHNUM_MASK (0xF << 27) +#define DWC2_HPTXSTS_PTXQTOP_CHNUM_OFFSET 27 +#define DWC2_HPTXSTS_PTXQTOP_ODD (1 << 31) +#define DWC2_HPTXSTS_PTXQTOP_ODD_OFFSET 31 +#define DWC2_HPRT0_PRTCONNSTS (1 << 0) +#define DWC2_HPRT0_PRTCONNSTS_OFFSET 0 +#define DWC2_HPRT0_PRTCONNDET (1 << 1) +#define DWC2_HPRT0_PRTCONNDET_OFFSET 1 +#define DWC2_HPRT0_PRTENA (1 << 2) +#define DWC2_HPRT0_PRTENA_OFFSET 2 +#define DWC2_HPRT0_PRTENCHNG (1 << 3) +#define DWC2_HPRT0_PRTENCHNG_OFFSET 3 +#define DWC2_HPRT0_PRTOVRCURRACT (1 << 4) +#define DWC2_HPRT0_PRTOVRCURRACT_OFFSET 4 +#define DWC2_HPRT0_PRTOVRCURRCHNG (1 << 5) +#define DWC2_HPRT0_PRTOVRCURRCHNG_OFFSET 5 +#define DWC2_HPRT0_PRTRES (1 << 6) +#define DWC2_HPRT0_PRTRES_OFFSET 6 +#define DWC2_HPRT0_PRTSUSP (1 << 7) +#define DWC2_HPRT0_PRTSUSP_OFFSET 7 +#define DWC2_HPRT0_PRTRST (1 << 8) +#define DWC2_HPRT0_PRTRST_OFFSET 8 +#define DWC2_HPRT0_PRTLNSTS_MASK (0x3 << 10) +#define DWC2_HPRT0_PRTLNSTS_OFFSET 10 +#define DWC2_HPRT0_PRTPWR (1 << 12) +#define DWC2_HPRT0_PRTPWR_OFFSET 12 +#define DWC2_HPRT0_PRTTSTCTL_MASK (0xF << 13) +#define DWC2_HPRT0_PRTTSTCTL_OFFSET 13 +#define DWC2_HPRT0_PRTSPD_HIGH (0 << 17) +#define DWC2_HPRT0_PRTSPD_FULL (1 << 17) +#define DWC2_HPRT0_PRTSPD_LOW (2 << 17) +#define DWC2_HPRT0_PRTSPD_MASK (0x3 << 17) +#define DWC2_HPRT0_PRTSPD_OFFSET 17 +#define DWC2_HAINT_CH0 (1 << 0) +#define DWC2_HAINT_CH0_OFFSET 0 +#define DWC2_HAINT_CH1 (1 << 1) +#define DWC2_HAINT_CH1_OFFSET 1 +#define DWC2_HAINT_CH2 (1 << 2) +#define DWC2_HAINT_CH2_OFFSET 2 +#define DWC2_HAINT_CH3 (1 << 3) +#define DWC2_HAINT_CH3_OFFSET 3 +#define DWC2_HAINT_CH4 (1 << 4) +#define DWC2_HAINT_CH4_OFFSET 4 +#define DWC2_HAINT_CH5 (1 << 5) +#define DWC2_HAINT_CH5_OFFSET 5 +#define DWC2_HAINT_CH6 (1 << 6) +#define DWC2_HAINT_CH6_OFFSET 6 +#define DWC2_HAINT_CH7 (1 << 7) +#define DWC2_HAINT_CH7_OFFSET 7 +#define DWC2_HAINT_CH8 (1 << 8) +#define DWC2_HAINT_CH8_OFFSET 8 +#define DWC2_HAINT_CH9 (1 << 9) +#define DWC2_HAINT_CH9_OFFSET 9 +#define DWC2_HAINT_CH10 (1 << 10) +#define DWC2_HAINT_CH10_OFFSET 10 +#define DWC2_HAINT_CH11 (1 << 11) +#define DWC2_HAINT_CH11_OFFSET 11 +#define DWC2_HAINT_CH12 (1 << 12) +#define DWC2_HAINT_CH12_OFFSET 12 +#define DWC2_HAINT_CH13 (1 << 13) +#define DWC2_HAINT_CH13_OFFSET 13 +#define DWC2_HAINT_CH14 (1 << 14) +#define DWC2_HAINT_CH14_OFFSET 14 +#define DWC2_HAINT_CH15 (1 << 15) +#define DWC2_HAINT_CH15_OFFSET 15 +#define DWC2_HAINT_CHINT_MASK 0xffff +#define DWC2_HAINT_CHINT_OFFSET 0 +#define DWC2_HAINTMSK_CH0 (1 << 0) +#define DWC2_HAINTMSK_CH0_OFFSET 0 +#define DWC2_HAINTMSK_CH1 (1 << 1) +#define DWC2_HAINTMSK_CH1_OFFSET 1 +#define DWC2_HAINTMSK_CH2 (1 << 2) +#define DWC2_HAINTMSK_CH2_OFFSET 2 +#define DWC2_HAINTMSK_CH3 (1 << 3) +#define DWC2_HAINTMSK_CH3_OFFSET 3 +#define DWC2_HAINTMSK_CH4 (1 << 4) +#define DWC2_HAINTMSK_CH4_OFFSET 4 +#define DWC2_HAINTMSK_CH5 (1 << 5) +#define DWC2_HAINTMSK_CH5_OFFSET 5 +#define DWC2_HAINTMSK_CH6 (1 << 6) +#define DWC2_HAINTMSK_CH6_OFFSET 6 +#define DWC2_HAINTMSK_CH7 (1 << 7) +#define DWC2_HAINTMSK_CH7_OFFSET 7 +#define DWC2_HAINTMSK_CH8 (1 << 8) +#define DWC2_HAINTMSK_CH8_OFFSET 8 +#define DWC2_HAINTMSK_CH9 (1 << 9) +#define DWC2_HAINTMSK_CH9_OFFSET 9 +#define DWC2_HAINTMSK_CH10 (1 << 10) +#define DWC2_HAINTMSK_CH10_OFFSET 10 +#define DWC2_HAINTMSK_CH11 (1 << 11) +#define DWC2_HAINTMSK_CH11_OFFSET 11 +#define DWC2_HAINTMSK_CH12 (1 << 12) +#define DWC2_HAINTMSK_CH12_OFFSET 12 +#define DWC2_HAINTMSK_CH13 (1 << 13) +#define DWC2_HAINTMSK_CH13_OFFSET 13 +#define DWC2_HAINTMSK_CH14 (1 << 14) +#define DWC2_HAINTMSK_CH14_OFFSET 14 +#define DWC2_HAINTMSK_CH15 (1 << 15) +#define DWC2_HAINTMSK_CH15_OFFSET 15 +#define DWC2_HAINTMSK_CHINT_MASK 0xffff +#define DWC2_HAINTMSK_CHINT_OFFSET 0 +#define DWC2_HCCHAR_MPS_MASK (0x7FF << 0) +#define DWC2_HCCHAR_MPS_OFFSET 0 +#define DWC2_HCCHAR_EPNUM_MASK (0xF << 11) +#define DWC2_HCCHAR_EPNUM_OFFSET 11 +#define DWC2_HCCHAR_EPDIR (1 << 15) +#define DWC2_HCCHAR_EPDIR_OFFSET 15 +#define DWC2_HCCHAR_LSPDDEV (1 << 17) +#define DWC2_HCCHAR_LSPDDEV_OFFSET 17 +#define DWC2_HCCHAR_EPTYPE_CONTROL 0 +#define DWC2_HCCHAR_EPTYPE_ISOC 1 +#define DWC2_HCCHAR_EPTYPE_BULK 2 +#define DWC2_HCCHAR_EPTYPE_INTR 3 +#define DWC2_HCCHAR_EPTYPE_MASK (0x3 << 18) +#define DWC2_HCCHAR_EPTYPE_OFFSET 18 +#define DWC2_HCCHAR_MULTICNT_MASK (0x3 << 20) +#define DWC2_HCCHAR_MULTICNT_OFFSET 20 +#define DWC2_HCCHAR_DEVADDR_MASK (0x7F << 22) +#define DWC2_HCCHAR_DEVADDR_OFFSET 22 +#define DWC2_HCCHAR_ODDFRM (1 << 29) +#define DWC2_HCCHAR_ODDFRM_OFFSET 29 +#define DWC2_HCCHAR_CHDIS (1 << 30) +#define DWC2_HCCHAR_CHDIS_OFFSET 30 +#define DWC2_HCCHAR_CHEN (1 << 31) +#define DWC2_HCCHAR_CHEN_OFFSET 31 +#define DWC2_HCSPLT_PRTADDR_MASK (0x7F << 0) +#define DWC2_HCSPLT_PRTADDR_OFFSET 0 +#define DWC2_HCSPLT_HUBADDR_MASK (0x7F << 7) +#define DWC2_HCSPLT_HUBADDR_OFFSET 7 +#define DWC2_HCSPLT_XACTPOS_MASK (0x3 << 14) +#define DWC2_HCSPLT_XACTPOS_OFFSET 14 +#define DWC2_HCSPLT_COMPSPLT (1 << 16) +#define DWC2_HCSPLT_COMPSPLT_OFFSET 16 +#define DWC2_HCSPLT_SPLTENA (1 << 31) +#define DWC2_HCSPLT_SPLTENA_OFFSET 31 +#define DWC2_HCINT_XFERCOMP (1 << 0) +#define DWC2_HCINT_XFERCOMP_OFFSET 0 +#define DWC2_HCINT_CHHLTD (1 << 1) +#define DWC2_HCINT_CHHLTD_OFFSET 1 +#define DWC2_HCINT_AHBERR (1 << 2) +#define DWC2_HCINT_AHBERR_OFFSET 2 +#define DWC2_HCINT_STALL (1 << 3) +#define DWC2_HCINT_STALL_OFFSET 3 +#define DWC2_HCINT_NAK (1 << 4) +#define DWC2_HCINT_NAK_OFFSET 4 +#define DWC2_HCINT_ACK (1 << 5) +#define DWC2_HCINT_ACK_OFFSET 5 +#define DWC2_HCINT_NYET (1 << 6) +#define DWC2_HCINT_NYET_OFFSET 6 +#define DWC2_HCINT_XACTERR (1 << 7) +#define DWC2_HCINT_XACTERR_OFFSET 7 +#define DWC2_HCINT_BBLERR (1 << 8) +#define DWC2_HCINT_BBLERR_OFFSET 8 +#define DWC2_HCINT_FRMOVRUN (1 << 9) +#define DWC2_HCINT_FRMOVRUN_OFFSET 9 +#define DWC2_HCINT_DATATGLERR (1 << 10) +#define DWC2_HCINT_DATATGLERR_OFFSET 10 +#define DWC2_HCINT_BNA (1 << 11) +#define DWC2_HCINT_BNA_OFFSET 11 +#define DWC2_HCINT_XCS_XACT (1 << 12) +#define DWC2_HCINT_XCS_XACT_OFFSET 12 +#define DWC2_HCINT_FRM_LIST_ROLL (1 << 13) +#define DWC2_HCINT_FRM_LIST_ROLL_OFFSET 13 +#define DWC2_HCINTMSK_XFERCOMPL (1 << 0) +#define DWC2_HCINTMSK_XFERCOMPL_OFFSET 0 +#define DWC2_HCINTMSK_CHHLTD (1 << 1) +#define DWC2_HCINTMSK_CHHLTD_OFFSET 1 +#define DWC2_HCINTMSK_AHBERR (1 << 2) +#define DWC2_HCINTMSK_AHBERR_OFFSET 2 +#define DWC2_HCINTMSK_STALL (1 << 3) +#define DWC2_HCINTMSK_STALL_OFFSET 3 +#define DWC2_HCINTMSK_NAK (1 << 4) +#define DWC2_HCINTMSK_NAK_OFFSET 4 +#define DWC2_HCINTMSK_ACK (1 << 5) +#define DWC2_HCINTMSK_ACK_OFFSET 5 +#define DWC2_HCINTMSK_NYET (1 << 6) +#define DWC2_HCINTMSK_NYET_OFFSET 6 +#define DWC2_HCINTMSK_XACTERR (1 << 7) +#define DWC2_HCINTMSK_XACTERR_OFFSET 7 +#define DWC2_HCINTMSK_BBLERR (1 << 8) +#define DWC2_HCINTMSK_BBLERR_OFFSET 8 +#define DWC2_HCINTMSK_FRMOVRUN (1 << 9) +#define DWC2_HCINTMSK_FRMOVRUN_OFFSET 9 +#define DWC2_HCINTMSK_DATATGLERR (1 << 10) +#define DWC2_HCINTMSK_DATATGLERR_OFFSET 10 +#define DWC2_HCINTMSK_BNA (1 << 11) +#define DWC2_HCINTMSK_BNA_OFFSET 11 +#define DWC2_HCINTMSK_XCS_XACT (1 << 12) +#define DWC2_HCINTMSK_XCS_XACT_OFFSET 12 +#define DWC2_HCINTMSK_FRM_LIST_ROLL (1 << 13) +#define DWC2_HCINTMSK_FRM_LIST_ROLL_OFFSET 13 +#define DWC2_HCTSIZ_XFERSIZE_MASK 0x7ffff +#define DWC2_HCTSIZ_XFERSIZE_OFFSET 0 +#define DWC2_HCTSIZ_SCHINFO_MASK 0xff +#define DWC2_HCTSIZ_SCHINFO_OFFSET 0 +#define DWC2_HCTSIZ_NTD_MASK (0xff << 8) +#define DWC2_HCTSIZ_NTD_OFFSET 8 +#define DWC2_HCTSIZ_PKTCNT_MASK (0x3ff << 19) +#define DWC2_HCTSIZ_PKTCNT_OFFSET 19 +#define DWC2_HCTSIZ_PID_MASK (0x3 << 29) +#define DWC2_HCTSIZ_PID_OFFSET 29 +#define DWC2_HCTSIZ_DOPNG (1 << 31) +#define DWC2_HCTSIZ_DOPNG_OFFSET 31 +#define DWC2_HCDMA_CTD_MASK (0xFF << 3) +#define DWC2_HCDMA_CTD_OFFSET 3 +#define DWC2_HCDMA_DMA_ADDR_MASK (0x1FFFFF << 11) +#define DWC2_HCDMA_DMA_ADDR_OFFSET 11 +#define DWC2_PCGCCTL_STOPPCLK (1 << 0) +#define DWC2_PCGCCTL_STOPPCLK_OFFSET 0 +#define DWC2_PCGCCTL_GATEHCLK (1 << 1) +#define DWC2_PCGCCTL_GATEHCLK_OFFSET 1 +#define DWC2_PCGCCTL_PWRCLMP (1 << 2) +#define DWC2_PCGCCTL_PWRCLMP_OFFSET 2 +#define DWC2_PCGCCTL_RSTPDWNMODULE (1 << 3) +#define DWC2_PCGCCTL_RSTPDWNMODULE_OFFSET 3 +#define DWC2_PCGCCTL_PHYSUSPENDED (1 << 4) +#define DWC2_PCGCCTL_PHYSUSPENDED_OFFSET 4 +#define DWC2_PCGCCTL_ENBL_SLEEP_GATING (1 << 5) +#define DWC2_PCGCCTL_ENBL_SLEEP_GATING_OFFSET 5 +#define DWC2_PCGCCTL_PHY_IN_SLEEP (1 << 6) +#define DWC2_PCGCCTL_PHY_IN_SLEEP_OFFSET 6 +#define DWC2_PCGCCTL_DEEP_SLEEP (1 << 7) +#define DWC2_PCGCCTL_DEEP_SLEEP_OFFSET 7 +#define DWC2_SNPSID_DEVID_VER_2xx (0x4f542 << 12) +#define DWC2_SNPSID_DEVID_VER_3xx (0x4f543 << 12) +#define DWC2_SNPSID_DEVID_MASK (0xfffff << 12) +#define DWC2_SNPSID_DEVID_OFFSET 12 + +/* Host controller specific */ +#define DWC2_HC_PID_DATA0 0 +#define DWC2_HC_PID_DATA2 1 +#define DWC2_HC_PID_DATA1 2 +#define DWC2_HC_PID_MDATA 3 +#define DWC2_HC_PID_SETUP 3 + +/* roothub.a masks */ +#define RH_A_NDP (0xff << 0) /* number of downstream ports */ +#define RH_A_PSM (1 << 8) /* power switching mode */ +#define RH_A_NPS (1 << 9) /* no power switching */ +#define RH_A_DT (1 << 10) /* device type (mbz) */ +#define RH_A_OCPM (1 << 11) /* over current protection mode */ +#define RH_A_NOCP (1 << 12) /* no over current protection */ +#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ + +/* roothub.b masks */ +#define RH_B_DR 0x0000ffff /* device removable flags */ +#define RH_B_PPCM 0xffff0000 /* port power control mask */ + +/* Default driver configuration */ +#define CONFIG_DWC2_MAX_CHANNELS 16 /* Max # of EPs */ +#define CONFIG_DWC2_HOST_RX_FIFO_SIZE (516 + CONFIG_DWC2_MAX_CHANNELS) +#define CONFIG_DWC2_HOST_NPERIO_TX_FIFO_SIZE 0x100 /* nPeriodic TX FIFO */ +#define CONFIG_DWC2_HOST_PERIO_TX_FIFO_SIZE 0x200 /* Periodic TX FIFO */ +#define CONFIG_DWC2_MAX_TRANSFER_SIZE 65535 +#define CONFIG_DWC2_MAX_PACKET_COUNT 511 + +#define DWC2_PHY_TYPE_FS 0 +#define DWC2_PHY_TYPE_UTMI 1 +#define DWC2_PHY_TYPE_ULPI 2 +#define CONFIG_DWC2_PHY_TYPE DWC2_PHY_TYPE_UTMI /* PHY type */ +#define CONFIG_DWC2_UTMI_WIDTH 8 /* UTMI bus width (8/16) */ + +#define CONFIG_DWC2_PHY_ULPI_EXT_VBUS /* ULPI PHY controls VBUS */ +#define CONFIG_DWC2_TX_THR_LENGTH 64 + +#endif /* __DWC2_H__ */ -- 2.24.0 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 24+ messages in thread
* [RFC PATCH 0/7] usb: dwc2 host driver 2019-12-20 14:32 ` [PATCH 09/10] usb: Add dwc2 host driver Sascha Hauer @ 2020-01-14 13:21 ` Jules Maselbas 2020-01-14 13:21 ` [RFC PATCH 1/7] usb: dwc2: Add host controller driver Jules Maselbas ` (7 more replies) 0 siblings, 8 replies; 24+ messages in thread From: Jules Maselbas @ 2020-01-14 13:21 UTC (permalink / raw) To: Sascha Hauer; +Cc: Barebox List, Jules Maselbas Hi Sascha, I've been working on a driver for the dwc2 otg controller, for both host and device mode. Like you I've started from U-Boot driver and I mixed it with some part from Linux. For instance I've removed the register structs and I've been using the same defines for register bit-fields as in Linux. I would like to share my version of the host driver, as the gadget driver one still requires some cleanup. This series is not to be applied on the driver you proposed. However I am willing to propose a new series that can be applied on the driver you proposed. What do you think? Cheers, Jules --- Jules Maselbas (7): usb: dwc2: Add host controller driver usb: dwc2: host: Handle dma mapping errors usb: dwc2: Dynamic fifo size support from Linux usb: dwc2: Rework roothub interface HACK: usb: dwc2: Fix toggle reset usb: dwc2: Rewrite dwc2_hc_init usb: dwc2: Read dr_mode from device tree drivers/usb/Kconfig | 2 + drivers/usb/Makefile | 1 + drivers/usb/dwc2/Kconfig | 4 + drivers/usb/dwc2/Makefile | 1 + drivers/usb/dwc2/core.c | 703 ++++++++++++++++++++++++++++++++ drivers/usb/dwc2/core.h | 546 +++++++++++++++++++++++++ drivers/usb/dwc2/dwc2.c | 103 +++++ drivers/usb/dwc2/dwc2.h | 42 ++ drivers/usb/dwc2/host.c | 749 ++++++++++++++++++++++++++++++++++ drivers/usb/dwc2/regs.h | 839 ++++++++++++++++++++++++++++++++++++++ drivers/usb/dwc2/rhub.c | 384 +++++++++++++++++ 11 files changed, 3374 insertions(+) create mode 100644 drivers/usb/dwc2/Kconfig create mode 100644 drivers/usb/dwc2/Makefile create mode 100644 drivers/usb/dwc2/core.c create mode 100644 drivers/usb/dwc2/core.h create mode 100644 drivers/usb/dwc2/dwc2.c create mode 100644 drivers/usb/dwc2/dwc2.h create mode 100644 drivers/usb/dwc2/host.c create mode 100644 drivers/usb/dwc2/regs.h create mode 100644 drivers/usb/dwc2/rhub.c -- 2.21.0.196.g041f5ea _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 24+ messages in thread
* [RFC PATCH 1/7] usb: dwc2: Add host controller driver 2020-01-14 13:21 ` [RFC PATCH 0/7] usb: " Jules Maselbas @ 2020-01-14 13:21 ` Jules Maselbas 2020-01-14 13:21 ` [RFC PATCH 2/7] usb: dwc2: host: Handle dma mapping errors Jules Maselbas ` (6 subsequent siblings) 7 siblings, 0 replies; 24+ messages in thread From: Jules Maselbas @ 2020-01-14 13:21 UTC (permalink / raw) To: Sascha Hauer; +Cc: Barebox List, Jules Maselbas The host driver is mostly taken from U-Boot, other part are from Linux Signed-off-by: Jules Maselbas <jmaselbas@kalray.eu> --- drivers/usb/Kconfig | 2 + drivers/usb/Makefile | 1 + drivers/usb/dwc2/Kconfig | 4 + drivers/usb/dwc2/Makefile | 1 + drivers/usb/dwc2/core.c | 614 +++++++++++++++++++++++++++ drivers/usb/dwc2/core.h | 546 ++++++++++++++++++++++++ drivers/usb/dwc2/dwc2.c | 101 +++++ drivers/usb/dwc2/dwc2.h | 41 ++ drivers/usb/dwc2/host.c | 618 +++++++++++++++++++++++++++ drivers/usb/dwc2/regs.h | 847 ++++++++++++++++++++++++++++++++++++++ drivers/usb/dwc2/rhub.c | 418 +++++++++++++++++++ 11 files changed, 3193 insertions(+) create mode 100644 drivers/usb/dwc2/Kconfig create mode 100644 drivers/usb/dwc2/Makefile create mode 100644 drivers/usb/dwc2/core.c create mode 100644 drivers/usb/dwc2/core.h create mode 100644 drivers/usb/dwc2/dwc2.c create mode 100644 drivers/usb/dwc2/dwc2.h create mode 100644 drivers/usb/dwc2/host.c create mode 100644 drivers/usb/dwc2/regs.h create mode 100644 drivers/usb/dwc2/rhub.c diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 99eff1c8d..aab1564ab 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -9,6 +9,8 @@ if USB_HOST source "drivers/usb/imx/Kconfig" +source "drivers/usb/dwc2/Kconfig" + source "drivers/usb/dwc3/Kconfig" source "drivers/usb/host/Kconfig" diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 9e9809950..ecd7ad1d3 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_USB) += core/ obj-$(CONFIG_USB_IMX_CHIPIDEA) += imx/ +obj-$(CONFIG_USB_DWC2) += dwc2/ obj-$(CONFIG_USB_DWC3) += dwc3/ obj-$(CONFIG_USB_MUSB) += musb/ obj-$(CONFIG_USB_GADGET) += gadget/ diff --git a/drivers/usb/dwc2/Kconfig b/drivers/usb/dwc2/Kconfig new file mode 100644 index 000000000..c88134cbb --- /dev/null +++ b/drivers/usb/dwc2/Kconfig @@ -0,0 +1,4 @@ +config USB_DWC2 + bool "DWC2 driver" + help + DesignWare Core USB2 OTG driver. diff --git a/drivers/usb/dwc2/Makefile b/drivers/usb/dwc2/Makefile new file mode 100644 index 000000000..3b922c282 --- /dev/null +++ b/drivers/usb/dwc2/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_USB_DWC2) += dwc2.o core.o host.o rhub.o diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c new file mode 100644 index 000000000..9a36b0904 --- /dev/null +++ b/drivers/usb/dwc2/core.c @@ -0,0 +1,614 @@ +// SPDX-License-Identifier: GPL-2.0+ +#include "dwc2.h" + +void dwc2_set_param_otg_cap(struct dwc2 *dwc2) +{ + u8 val; + + switch (dwc2->hw_params.op_mode) { + case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE: + val = DWC2_CAP_PARAM_HNP_SRP_CAPABLE; + break; + case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE: + case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE: + case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST: + val = DWC2_CAP_PARAM_SRP_ONLY_CAPABLE; + break; + default: + val = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE; + break; + } + + dwc2->params.otg_cap = val; +} + +void dwc2_set_param_phy_type(struct dwc2 *dwc2) +{ + u8 val; + + switch (dwc2->hw_params.hs_phy_type) { + case GHWCFG2_HS_PHY_TYPE_UTMI: + case GHWCFG2_HS_PHY_TYPE_UTMI_ULPI: + val = DWC2_PHY_TYPE_PARAM_UTMI; + break; + case GHWCFG2_HS_PHY_TYPE_ULPI: + val = DWC2_PHY_TYPE_PARAM_ULPI; + break; + case GHWCFG2_HS_PHY_TYPE_NOT_SUPPORTED: + val = DWC2_PHY_TYPE_PARAM_FS; + break; + } + + dwc2->params.phy_type = val; +} + +void dwc2_set_param_speed(struct dwc2 *dwc2) +{ + if (dwc2->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) + dwc2->params.speed = DWC2_SPEED_PARAM_FULL; + else + dwc2->params.speed = DWC2_SPEED_PARAM_HIGH; +} + +void dwc2_set_param_phy_utmi_width(struct dwc2 *dwc2) +{ + int val; + + val = (dwc2->hw_params.utmi_phy_data_width == + GHWCFG4_UTMI_PHY_DATA_WIDTH_8) ? 8 : 16; + + dwc2->params.phy_utmi_width = val; +} + +/** + * dwc2_set_default_params() - Set all core parameters to their + * auto-detected default values. + * + * @dwc2: Programming view of the DWC2 controller + * + */ +void dwc2_set_default_params(struct dwc2 *dwc2) +{ + struct dwc2_hw_params *hw = &dwc2->hw_params; + struct dwc2_core_params *p = &dwc2->params; + bool dma_capable = !(hw->arch == GHWCFG2_SLAVE_ONLY_ARCH); + + dwc2_set_param_otg_cap(dwc2); + dwc2_set_param_phy_type(dwc2); + dwc2_set_param_speed(dwc2); + dwc2_set_param_phy_utmi_width(dwc2); + p->phy_ulpi_ddr = false; + p->phy_ulpi_ext_vbus = false; + + p->enable_dynamic_fifo = hw->enable_dynamic_fifo; + p->en_multiple_tx_fifo = hw->en_multiple_tx_fifo; + p->i2c_enable = hw->i2c_enable; + p->acg_enable = hw->acg_enable; + p->ulpi_fs_ls = false; + p->ts_dline = false; + p->reload_ctl = (hw->snpsid >= DWC2_CORE_REV_2_92a); + p->uframe_sched = true; + p->external_id_pin_ctl = false; + p->lpm = true; + p->lpm_clock_gating = true; + p->besl = true; + p->hird_threshold_en = true; + p->hird_threshold = 4; + p->ipg_isoc_en = false; + p->max_packet_count = hw->max_packet_count; + p->max_transfer_size = hw->max_transfer_size; + p->ahbcfg = GAHBCFG_HBSTLEN_INCR << GAHBCFG_HBSTLEN_SHIFT; + + p->dma = dma_capable; + p->dma_desc = false; + + if (dwc2->dr_mode == USB_DR_MODE_HOST || + dwc2->dr_mode == USB_DR_MODE_OTG) { + p->host_support_fs_ls_low_power = false; + p->host_ls_low_power_phy_clk = false; + p->host_channels = hw->host_channels; + p->host_rx_fifo_size = hw->rx_fifo_size; + p->host_nperio_tx_fifo_size = hw->host_nperio_tx_fifo_size; + p->host_perio_tx_fifo_size = hw->host_perio_tx_fifo_size; + } +} + +int dwc2_core_snpsid(struct dwc2 *dwc2) +{ + struct dwc2_hw_params *hw = &dwc2->hw_params; + + hw->snpsid = dwc2_readl(dwc2, GSNPSID); + + /* + * Attempt to ensure this device is really a DWC2 Controller. + * Read and verify the GSNPSID register contents. The value should be + * 0x4f54xxxx, 0x5531xxxx or 0x5532xxxx + */ + hw->snpsid = dwc2_readl(dwc2, GSNPSID); + if ((hw->snpsid & GSNPSID_ID_MASK) != DWC2_OTG_ID && + (hw->snpsid & GSNPSID_ID_MASK) != DWC2_FS_IOT_ID && + (hw->snpsid & GSNPSID_ID_MASK) != DWC2_HS_IOT_ID) { + dwc2_err(dwc2, "Bad value for GSNPSID: 0x%08x\n", + hw->snpsid); + return -ENODEV; + } + + dwc2_dbg(dwc2, "Core Release: %1x.%1x%1x%1x (snpsid=%x)\n", + hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf, + hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid); + + return 0; +} + +/** + * During device initialization, read various hardware configuration + * registers and interpret the contents. + * + * @dwc2: Programming view of the DWC2 controller + * + */ +void dwc2_get_hwparams(struct dwc2 *dwc2) +{ + struct dwc2_hw_params *hw = &dwc2->hw_params; + unsigned int width; + u32 hwcfg1, hwcfg2, hwcfg3, hwcfg4; + u32 grxfsiz; + + hwcfg1 = dwc2_readl(dwc2, GHWCFG1); + hwcfg2 = dwc2_readl(dwc2, GHWCFG2); + hwcfg3 = dwc2_readl(dwc2, GHWCFG3); + hwcfg4 = dwc2_readl(dwc2, GHWCFG4); + grxfsiz = dwc2_readl(dwc2, GRXFSIZ); + + /* hwcfg1 */ + hw->dev_ep_dirs = hwcfg1; + + /* hwcfg2 */ + hw->op_mode = (hwcfg2 & GHWCFG2_OP_MODE_MASK) >> + GHWCFG2_OP_MODE_SHIFT; + hw->arch = (hwcfg2 & GHWCFG2_ARCHITECTURE_MASK) >> + GHWCFG2_ARCHITECTURE_SHIFT; + hw->enable_dynamic_fifo = !!(hwcfg2 & GHWCFG2_DYNAMIC_FIFO); + hw->host_channels = 1 + ((hwcfg2 & GHWCFG2_NUM_HOST_CHAN_MASK) >> + GHWCFG2_NUM_HOST_CHAN_SHIFT); + hw->hs_phy_type = (hwcfg2 & GHWCFG2_HS_PHY_TYPE_MASK) >> + GHWCFG2_HS_PHY_TYPE_SHIFT; + hw->fs_phy_type = (hwcfg2 & GHWCFG2_FS_PHY_TYPE_MASK) >> + GHWCFG2_FS_PHY_TYPE_SHIFT; + hw->num_dev_ep = (hwcfg2 & GHWCFG2_NUM_DEV_EP_MASK) >> + GHWCFG2_NUM_DEV_EP_SHIFT; + hw->nperio_tx_q_depth = + (hwcfg2 & GHWCFG2_NONPERIO_TX_Q_DEPTH_MASK) >> + GHWCFG2_NONPERIO_TX_Q_DEPTH_SHIFT << 1; + hw->host_perio_tx_q_depth = + (hwcfg2 & GHWCFG2_HOST_PERIO_TX_Q_DEPTH_MASK) >> + GHWCFG2_HOST_PERIO_TX_Q_DEPTH_SHIFT << 1; + hw->dev_token_q_depth = + (hwcfg2 & GHWCFG2_DEV_TOKEN_Q_DEPTH_MASK) >> + GHWCFG2_DEV_TOKEN_Q_DEPTH_SHIFT; + + /* hwcfg3 */ + width = (hwcfg3 & GHWCFG3_XFER_SIZE_CNTR_WIDTH_MASK) >> + GHWCFG3_XFER_SIZE_CNTR_WIDTH_SHIFT; + hw->max_transfer_size = (1 << (width + 11)) - 1; + width = (hwcfg3 & GHWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK) >> + GHWCFG3_PACKET_SIZE_CNTR_WIDTH_SHIFT; + hw->max_packet_count = (1 << (width + 4)) - 1; + hw->i2c_enable = !!(hwcfg3 & GHWCFG3_I2C); + hw->total_fifo_size = (hwcfg3 & GHWCFG3_DFIFO_DEPTH_MASK) >> + GHWCFG3_DFIFO_DEPTH_SHIFT; + hw->lpm_mode = !!(hwcfg3 & GHWCFG3_OTG_LPM_EN); + + /* hwcfg4 */ + hw->en_multiple_tx_fifo = !!(hwcfg4 & GHWCFG4_DED_FIFO_EN); + hw->num_dev_perio_in_ep = (hwcfg4 & GHWCFG4_NUM_DEV_PERIO_IN_EP_MASK) >> + GHWCFG4_NUM_DEV_PERIO_IN_EP_SHIFT; + hw->num_dev_in_eps = (hwcfg4 & GHWCFG4_NUM_IN_EPS_MASK) >> + GHWCFG4_NUM_IN_EPS_SHIFT; + hw->dma_desc_enable = !!(hwcfg4 & GHWCFG4_DESC_DMA); + hw->power_optimized = !!(hwcfg4 & GHWCFG4_POWER_OPTIMIZ); + hw->hibernation = !!(hwcfg4 & GHWCFG4_HIBER); + hw->utmi_phy_data_width = (hwcfg4 & GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK) >> + GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT; + hw->acg_enable = !!(hwcfg4 & GHWCFG4_ACG_SUPPORTED); + hw->ipg_isoc_en = !!(hwcfg4 & GHWCFG4_IPG_ISOC_SUPPORTED); + + /* fifo sizes */ + hw->rx_fifo_size = (grxfsiz & GRXFSIZ_DEPTH_MASK) >> + GRXFSIZ_DEPTH_SHIFT; +} + +/* + * Initializes the FSLSPClkSel field of the HCFG register depending on the + * PHY type + */ +void dwc2_init_fs_ls_pclk_sel(struct dwc2 *dwc2) +{ + u32 hcfg, val; + + if ((dwc2->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI && + dwc2->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED && + dwc2->params.ulpi_fs_ls) || + dwc2->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) { + /* Full speed PHY */ + val = HCFG_FSLSPCLKSEL_48_MHZ; + } else { + /* High speed PHY running at full speed or high speed */ + val = HCFG_FSLSPCLKSEL_30_60_MHZ; + } + + dwc2_dbg(dwc2, "Initializing HCFG.FSLSPClkSel to %08x\n", val); + hcfg = dwc2_readl(dwc2, HCFG); + hcfg &= ~HCFG_FSLSPCLKSEL_MASK; + hcfg |= val << HCFG_FSLSPCLKSEL_SHIFT; + dwc2_writel(dwc2, hcfg, HCFG); +} + +void dwc2_flush_all_fifo(struct dwc2 *dwc2) +{ + uint32_t greset; + + /* Wait for AHB master IDLE state */ + if (dwc2_wait_bit_set(dwc2, GRSTCTL, GRSTCTL_AHBIDLE, 100)) { + dwc2_err(dwc2, "%s: Timeout waiting for AHB Idle\n", __func__); + return; + } + + greset = GRSTCTL_TXFFLSH | GRSTCTL_RXFFLSH; + /* TXFNUM of 0x10 is to flush all TX FIFO */ + dwc2_writel(dwc2, greset | GRSTCTL_TXFNUM(0x10), GRSTCTL); + + /* Wait for TxFIFO and RxFIFO flush done */ + if (dwc2_wait_bit_clear(dwc2, GRSTCTL, greset, 100)) + dwc2_err(dwc2, "Timeout flushing fifos (GRSTCTL=%08x)\n", + dwc2_readl(dwc2, GRSTCTL)); + + /* Wait for 3 PHY Clocks */ + udelay(1); +} + +static int dwc2_fs_phy_init(struct dwc2 *dwc2, bool select_phy) +{ + u32 usbcfg, ggpio, i2cctl; + int retval = 0; + + /* + * core_init() is now called on every switch so only call the + * following for the first time through + */ + if (select_phy) { + dwc2_dbg(dwc2, "FS PHY selected\n"); + + usbcfg = dwc2_readl(dwc2, GUSBCFG); + if (!(usbcfg & GUSBCFG_PHYSEL)) { + usbcfg |= GUSBCFG_PHYSEL; + dwc2_writel(dwc2, usbcfg, GUSBCFG); + + /* Reset after a PHY select */ + retval = dwc2_core_reset(dwc2); + + if (retval) { + dwc2_err(dwc2, + "%s: Reset failed, aborting", __func__); + return retval; + } + } + + if (dwc2->params.activate_stm_fs_transceiver) { + ggpio = dwc2_readl(dwc2, GGPIO); + if (!(ggpio & GGPIO_STM32_OTG_GCCFG_PWRDWN)) { + dwc2_dbg(dwc2, "Activating transceiver\n"); + /* + * STM32F4x9 uses the GGPIO register as general + * core configuration register. + */ + ggpio |= GGPIO_STM32_OTG_GCCFG_PWRDWN; + dwc2_writel(dwc2, ggpio, GGPIO); + } + } + } + + if (dwc2->params.i2c_enable) { + dwc2_dbg(dwc2, "FS PHY enabling I2C\n"); + + /* Program GUSBCFG.OtgUtmiFsSel to I2C */ + usbcfg = dwc2_readl(dwc2, GUSBCFG); + usbcfg |= GUSBCFG_OTG_UTMI_FS_SEL; + dwc2_writel(dwc2, usbcfg, GUSBCFG); + + /* Program GI2CCTL.I2CEn */ + i2cctl = dwc2_readl(dwc2, GI2CCTL); + i2cctl &= ~GI2CCTL_I2CDEVADDR_MASK; + i2cctl |= 1 << GI2CCTL_I2CDEVADDR_SHIFT; + i2cctl &= ~GI2CCTL_I2CEN; + dwc2_writel(dwc2, i2cctl, GI2CCTL); + i2cctl |= GI2CCTL_I2CEN; + dwc2_writel(dwc2, i2cctl, GI2CCTL); + } + + return retval; +} + +static int dwc2_hs_phy_init(struct dwc2 *dwc2, bool select_phy) +{ + u32 usbcfg, usbcfg_old; + int retval = 0; + + if (!select_phy) + return 0; + + usbcfg = dwc2_readl(dwc2, GUSBCFG); + usbcfg_old = usbcfg; + + /* + * HS PHY parameters. These parameters are preserved during soft reset + * so only program the first time. Do a soft reset immediately after + * setting phyif. + */ + switch (dwc2->params.phy_type) { + case DWC2_PHY_TYPE_PARAM_ULPI: + /* ULPI interface */ + dwc2_dbg(dwc2, "HS ULPI PHY selected\n"); + usbcfg |= GUSBCFG_ULPI_UTMI_SEL; + usbcfg &= ~(GUSBCFG_PHYIF16 | GUSBCFG_DDRSEL | GUSBCFG_PHYSEL); + if (dwc2->params.phy_ulpi_ddr) + usbcfg |= GUSBCFG_DDRSEL; + + /* Set external VBUS indicator as needed. */ + if (dwc2->params.phy_ulpi_ext_vbus_ind) { + dwc2_dbg(dwc2, "Use external VBUS indicator\n"); + usbcfg |= GUSBCFG_ULPI_EXT_VBUS_IND; + usbcfg &= ~GUSBCFG_INDICATORCOMPLEMENT; + usbcfg &= ~GUSBCFG_INDICATORPASSTHROUGH; + + if (dwc2->params.phy_ulpi_ext_vbus_ind_complement) + usbcfg |= GUSBCFG_INDICATORCOMPLEMENT; + if (dwc2->params.phy_ulpi_ext_vbus_ind_passthrough) + usbcfg |= GUSBCFG_INDICATORPASSTHROUGH; + } + break; + case DWC2_PHY_TYPE_PARAM_UTMI: + /* UTMI+ interface */ + dwc2_dbg(dwc2, "HS UTMI+ PHY selected\n"); + usbcfg &= ~(GUSBCFG_ULPI_UTMI_SEL | GUSBCFG_PHYIF16); + if (dwc2->params.phy_utmi_width == 16) + usbcfg |= GUSBCFG_PHYIF16; + break; + default: + dwc2_err(dwc2, "FS PHY selected at HS!\n"); + break; + } + + if (usbcfg != usbcfg_old) { + dwc2_writel(dwc2, usbcfg, GUSBCFG); + + /* Reset after setting the PHY parameters */ + retval = dwc2_core_reset(dwc2); + if (retval) { + dwc2_err(dwc2, + "%s: Reset failed, aborting", __func__); + return retval; + } + } + + return retval; +} + +int dwc2_phy_init(struct dwc2 *dwc2, bool select_phy) +{ + u32 usbcfg; + int retval = 0; + + if ((dwc2->params.speed == DWC2_SPEED_PARAM_FULL || + dwc2->params.speed == DWC2_SPEED_PARAM_LOW) && + dwc2->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) { + /* If FS/LS mode with FS/LS PHY */ + retval = dwc2_fs_phy_init(dwc2, select_phy); + if (retval) + return retval; + } else { + /* High speed PHY */ + retval = dwc2_hs_phy_init(dwc2, select_phy); + if (retval) + return retval; + } + + usbcfg = dwc2_readl(dwc2, GUSBCFG); + usbcfg &= ~GUSBCFG_ULPI_FS_LS; + usbcfg &= ~GUSBCFG_ULPI_CLK_SUSP_M; + if (dwc2->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI && + dwc2->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED && + dwc2->params.ulpi_fs_ls) { + dwc2_dbg(dwc2, "Setting ULPI FSLS\n"); + usbcfg |= GUSBCFG_ULPI_FS_LS; + usbcfg |= GUSBCFG_ULPI_CLK_SUSP_M; + } + dwc2_writel(dwc2, usbcfg, GUSBCFG); + + return retval; +} + +int dwc2_gahbcfg_init(struct dwc2 *dwc2) +{ + u32 ahbcfg = dwc2_readl(dwc2, GAHBCFG); + + switch (dwc2->hw_params.arch) { + case GHWCFG2_EXT_DMA_ARCH: + dwc2_err(dwc2, "External DMA Mode not supported\n"); + return -EINVAL; + + case GHWCFG2_INT_DMA_ARCH: + dwc2_dbg(dwc2, "Internal DMA Mode\n"); + if (dwc2->params.ahbcfg != -1) { + ahbcfg &= GAHBCFG_CTRL_MASK; + ahbcfg |= dwc2->params.ahbcfg & + ~GAHBCFG_CTRL_MASK; + } + break; + + case GHWCFG2_SLAVE_ONLY_ARCH: + default: + dwc2_dbg(dwc2, "Slave Only Mode\n"); + break; + } + + if (dwc2->params.dma) + ahbcfg |= GAHBCFG_DMA_EN; + else + dwc2->params.dma_desc = false; + + dwc2_writel(dwc2, ahbcfg, GAHBCFG); + + return 0; +} + +void dwc2_gusbcfg_init(struct dwc2 *dwc2) +{ + u32 usbcfg; + + usbcfg = dwc2_readl(dwc2, GUSBCFG); + usbcfg &= ~(GUSBCFG_HNPCAP | GUSBCFG_SRPCAP); + + switch (dwc2->hw_params.op_mode) { + case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE: + if (dwc2->params.otg_cap == + DWC2_CAP_PARAM_HNP_SRP_CAPABLE) + usbcfg |= GUSBCFG_HNPCAP; + + case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE: + case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE: + case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST: + if (dwc2->params.otg_cap != + DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE) + usbcfg |= GUSBCFG_SRPCAP; + break; + + case GHWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE: + case GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE: + case GHWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST: + default: + break; + } + + dwc2_writel(dwc2, usbcfg, GUSBCFG); +} + +/* + * Do core a soft reset of the core. Be careful with this because it + * resets all the internal state machines of the core. + */ +int dwc2_core_reset(struct dwc2 *dwc2) +{ + bool wait_for_host_mode = false; + uint32_t greset; + int ret; + + dwc2_dbg(dwc2, "%s(%p)\n", __func__, dwc2); + + /* Wait for AHB master IDLE state. */ + ret = dwc2_wait_bit_set(dwc2, GRSTCTL, GRSTCTL_AHBIDLE, 10000); + if (ret) { + pr_info("%s: Timeout! Waiting for AHB master IDLE state\n", + __func__); + return ret; + } + + /* + * If the current mode is host, either due to the force mode + * bit being set (which persists after core reset) or the + * connector id pin, a core soft reset will temporarily reset + * the mode to device. A delay from the IDDIG debounce filter + * will occur before going back to host mode. + * + * Determine whether we will go back into host mode after a + * reset and account for this delay after the reset. + */ + { + u32 gotgctl = dwc2_readl(dwc2, GOTGCTL); + u32 gusbcfg = dwc2_readl(dwc2, GUSBCFG); + + if (!(gotgctl & GOTGCTL_CONID_B) || + (gusbcfg & GUSBCFG_FORCEHOSTMODE)) { + dwc2_dbg(dwc2, "HOST MODE\n"); + wait_for_host_mode = true; + } + } + /* Core Soft Reset */ + greset = dwc2_readl(dwc2, GRSTCTL); + greset |= GRSTCTL_CSFTRST; + dwc2_writel(dwc2, greset, GRSTCTL); + + ret = dwc2_wait_bit_clear(dwc2, GRSTCTL, GRSTCTL_CSFTRST, 10000); + if (ret) { + pr_info("%s: Timeout! Waiting for Core Soft Reset\n", __func__); + return ret; + } + + /* + * Wait for core to come out of reset. + * NOTE: This long sleep is _very_ important, otherwise the core will + * not stay in host mode after a connector ID change! + */ + mdelay(100); + + return 0; +} + +/* + * This function initializes the DWC2 controller registers and + * prepares the core for device mode or host mode operation. + * + * @param regs Programming view of the DWC2 controller + */ +void dwc2_core_init(struct dwc2 *dwc2) +{ + uint32_t otgctl = 0; + uint32_t usbcfg = 0; + int retval; + + dwc2_dbg(dwc2, "%s(%p)\n", __func__, dwc2); + + /* Common Initialization */ + usbcfg = dwc2_readl(dwc2, GUSBCFG); + + /* Set ULPI External VBUS bit if needed */ + usbcfg &= ~GUSBCFG_ULPI_EXT_VBUS_DRV; + if (dwc2->params.phy_ulpi_ext_vbus) + usbcfg |= GUSBCFG_ULPI_EXT_VBUS_DRV; + + /* Set external TS Dline pulsing bit if needed */ + usbcfg &= ~GUSBCFG_TERMSELDLPULSE; + if (dwc2->params.ts_dline) + usbcfg |= GUSBCFG_TERMSELDLPULSE; + + dwc2_writel(dwc2, usbcfg, GUSBCFG); + + /* Reset the Controller */ + dwc2_core_reset(dwc2); + + /* + * This programming sequence needs to happen in FS mode before + * any other programming occurs + */ + retval = dwc2_phy_init(dwc2, true); + if (retval) + return; + + /* Program the GAHBCFG Register */ + retval = dwc2_gahbcfg_init(dwc2); + if (retval) + return; + + /* Program the GUSBCFG register */ + dwc2_gusbcfg_init(dwc2); + + /* Program the GOTGCTL register */ + otgctl = dwc2_readl(dwc2, GOTGCTL); + otgctl &= ~GOTGCTL_OTGVER; + dwc2_writel(dwc2, otgctl, GOTGCTL); + + if (dwc2_is_host_mode(dwc2)) + dwc2_dbg(dwc2, "Host Mode\n"); + else + dwc2_dbg(dwc2, "Device Mode\n"); +} diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h new file mode 100644 index 000000000..0c89217c5 --- /dev/null +++ b/drivers/usb/dwc2/core.h @@ -0,0 +1,546 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Maximum number of Endpoints/HostChannels */ +#define DWC2_MAX_EPS_CHANNELS 16 + +/** + * struct dwc2_core_params - Parameters for configuring the core + * + * @otg_cap: Specifies the OTG capabilities. + * 0 - HNP and SRP capable + * 1 - SRP Only capable + * 2 - No HNP/SRP capable (always available) + * Defaults to best available option (0, 1, then 2) + * @host_dma: Specifies whether to use slave or DMA mode for accessing + * the data FIFOs. The driver will automatically detect the + * value for this parameter if none is specified. + * 0 - Slave (always available) + * 1 - DMA (default, if available) + * @dma_desc_enable: When DMA mode is enabled, specifies whether to use + * address DMA mode or descriptor DMA mode for accessing + * the data FIFOs. The driver will automatically detect the + * value for this if none is specified. + * 0 - Address DMA + * 1 - Descriptor DMA (default, if available) + * @dma_desc_fs_enable: When DMA mode is enabled, specifies whether to use + * address DMA mode or descriptor DMA mode for accessing + * the data FIFOs in Full Speed mode only. The driver + * will automatically detect the value for this if none is + * specified. + * 0 - Address DMA + * 1 - Descriptor DMA in FS (default, if available) + * @speed: Specifies the maximum speed of operation in host and + * device mode. The actual speed depends on the speed of + * the attached device and the value of phy_type. + * 0 - High Speed + * (default when phy_type is UTMI+ or ULPI) + * 1 - Full Speed + * (default when phy_type is Full Speed) + * @enable_dynamic_fifo: 0 - Use coreConsultant-specified FIFO size parameters + * 1 - Allow dynamic FIFO sizing (default, if available) + * @en_multiple_tx_fifo: Specifies whether dedicated per-endpoint transmit FIFOs + * are enabled for non-periodic IN endpoints in device + * mode. + * @host_rx_fifo_size: Number of 4-byte words in the Rx FIFO in host mode when + * dynamic FIFO sizing is enabled + * 16 to 32768 + * Actual maximum value is autodetected and also + * the default. + * @host_nperio_tx_fifo_size: Number of 4-byte words in the non-periodic Tx FIFO + * in host mode when dynamic FIFO sizing is enabled + * 16 to 32768 + * Actual maximum value is autodetected and also + * the default. + * @host_perio_tx_fifo_size: Number of 4-byte words in the periodic Tx FIFO in + * host mode when dynamic FIFO sizing is enabled + * 16 to 32768 + * Actual maximum value is autodetected and also + * the default. + * @max_transfer_size: The maximum transfer size supported, in bytes + * 2047 to 65,535 + * Actual maximum value is autodetected and also + * the default. + * @max_packet_count: The maximum number of packets in a transfer + * 15 to 511 + * Actual maximum value is autodetected and also + * the default. + * @host_channels: The number of host channel registers to use + * 1 to 16 + * Actual maximum value is autodetected and also + * the default. + * @phy_type: Specifies the type of PHY interface to use. By default, + * the driver will automatically detect the phy_type. + * 0 - Full Speed Phy + * 1 - UTMI+ Phy + * 2 - ULPI Phy + * Defaults to best available option (2, 1, then 0) + * @phy_utmi_width: Specifies the UTMI+ Data Width (in bits). This parameter + * is applicable for a phy_type of UTMI+ or ULPI. (For a + * ULPI phy_type, this parameter indicates the data width + * between the MAC and the ULPI Wrapper.) Also, this + * parameter is applicable only if the OTG_HSPHY_WIDTH cC + * parameter was set to "8 and 16 bits", meaning that the + * core has been configured to work at either data path + * width. + * 8 or 16 (default 16 if available) + * @phy_ulpi_ddr: Specifies whether the ULPI operates at double or single + * data rate. This parameter is only applicable if phy_type + * is ULPI. + * 0 - single data rate ULPI interface with 8 bit wide + * data bus (default) + * 1 - double data rate ULPI interface with 4 bit wide + * data bus + * @phy_ulpi_ext_vbus: For a ULPI phy, specifies whether to use the internal or + * external supply to drive the VBus + * 0 - Internal supply (default) + * 1 - External supply + * @i2c_enable: Specifies whether to use the I2Cinterface for a full + * speed PHY. This parameter is only applicable if phy_type + * is FS. + * 0 - No (default) + * 1 - Yes + * @ipg_isoc_en: Indicates the IPG supports is enabled or disabled. + * 0 - Disable (default) + * 1 - Enable + * @acg_enable: For enabling Active Clock Gating in the controller + * 0 - No + * 1 - Yes + * @ulpi_fs_ls: Make ULPI phy operate in FS/LS mode only + * 0 - No (default) + * 1 - Yes + * @host_support_fs_ls_low_power: Specifies whether low power mode is supported + * when attached to a Full Speed or Low Speed device in + * host mode. + * 0 - Don't support low power mode (default) + * 1 - Support low power mode + * @host_ls_low_power_phy_clk: Specifies the PHY clock rate in low power mode + * when connected to a Low Speed device in host + * mode. This parameter is applicable only if + * host_support_fs_ls_low_power is enabled. + * 0 - 48 MHz + * (default when phy_type is UTMI+ or ULPI) + * 1 - 6 MHz + * (default when phy_type is Full Speed) + * @oc_disable: Flag to disable overcurrent condition. + * 0 - Allow overcurrent condition to get detected + * 1 - Disable overcurrent condtion to get detected + * @ts_dline: Enable Term Select Dline pulsing + * 0 - No (default) + * 1 - Yes + * @reload_ctl: Allow dynamic reloading of HFIR register during runtime + * 0 - No (default for core < 2.92a) + * 1 - Yes (default for core >= 2.92a) + * @ahbcfg: This field allows the default value of the GAHBCFG + * register to be overridden + * -1 - GAHBCFG value will be set to 0x06 + * (INCR, default) + * all others - GAHBCFG value will be overridden with + * this value + * Not all bits can be controlled like this, the + * bits defined by GAHBCFG_CTRL_MASK are controlled + * by the driver and are ignored in this + * configuration value. + * @uframe_sched: True to enable the microframe scheduler + * @external_id_pin_ctl: Specifies whether ID pin is handled externally. + * Disable CONIDSTSCHNG controller interrupt in such + * case. + * 0 - No (default) + * 1 - Yes + * @power_down: Specifies whether the controller support power_down. + * If power_down is enabled, the controller will enter + * power_down in both peripheral and host mode when + * needed. + * 0 - No (default) + * 1 - Partial power down + * 2 - Hibernation + * @lpm: Enable LPM support. + * 0 - No + * 1 - Yes + * @lpm_clock_gating: Enable core PHY clock gating. + * 0 - No + * 1 - Yes + * @besl: Enable LPM Errata support. + * 0 - No + * 1 - Yes + * @hird_threshold_en: HIRD or HIRD Threshold enable. + * 0 - No + * 1 - Yes + * @hird_threshold: Value of BESL or HIRD Threshold. + * @activate_stm_fs_transceiver: Activate internal transceiver using GGPIO + * register. + * 0 - Deactivate the transceiver (default) + * 1 - Activate the transceiver + * @g_dma: Enables gadget dma usage (default: autodetect). + * @g_dma_desc: Enables gadget descriptor DMA (default: autodetect). + * @g_rx_fifo_size: The periodic rx fifo size for the device, in + * DWORDS from 16-32768 (default: 2048 if + * possible, otherwise autodetect). + * @g_np_tx_fifo_size: The non-periodic tx fifo size for the device in + * DWORDS from 16-32768 (default: 1024 if + * possible, otherwise autodetect). + * @g_tx_fifo_size: An array of TX fifo sizes in dedicated fifo + * mode. Each value corresponds to one EP + * starting from EP1 (max 15 values). Sizes are + * in DWORDS with possible values from from + * 16-32768 (default: 256, 256, 256, 256, 768, + * 768, 768, 768, 0, 0, 0, 0, 0, 0, 0). + * @change_speed_quirk: Change speed configuration to DWC2_SPEED_PARAM_FULL + * while full&low speed device connect. And change speed + * back to DWC2_SPEED_PARAM_HIGH while device is gone. + * 0 - No (default) + * 1 - Yes + * + * The following parameters may be specified when starting the module. These + * parameters define how the DWC_otg controller should be configured. A + * value of -1 (or any other out of range value) for any parameter means + * to read the value from hardware (if possible) or use the builtin + * default described above. + */ +struct dwc2_core_params { + u8 otg_cap; +#define DWC2_CAP_PARAM_HNP_SRP_CAPABLE 0 +#define DWC2_CAP_PARAM_SRP_ONLY_CAPABLE 1 +#define DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE 2 + + u8 phy_type; +#define DWC2_PHY_TYPE_PARAM_FS 0 +#define DWC2_PHY_TYPE_PARAM_UTMI 1 +#define DWC2_PHY_TYPE_PARAM_ULPI 2 + + u8 speed; +#define DWC2_SPEED_PARAM_HIGH 0 +#define DWC2_SPEED_PARAM_FULL 1 +#define DWC2_SPEED_PARAM_LOW 2 + + u8 phy_utmi_width; + bool phy_ulpi_ddr; + bool phy_ulpi_ext_vbus; + bool phy_ulpi_ext_vbus_ind; + bool phy_ulpi_ext_vbus_ind_complement; + bool phy_ulpi_ext_vbus_ind_passthrough; + + bool enable_dynamic_fifo; + bool en_multiple_tx_fifo; + bool i2c_enable; + bool acg_enable; + bool ulpi_fs_ls; + bool ts_dline; + bool reload_ctl; + bool uframe_sched; + bool external_id_pin_ctl; + + int power_down; +#define DWC2_POWER_DOWN_PARAM_NONE 0 +#define DWC2_POWER_DOWN_PARAM_PARTIAL 1 +#define DWC2_POWER_DOWN_PARAM_HIBERNATION 2 + + bool lpm; + bool lpm_clock_gating; + bool besl; + bool hird_threshold_en; + u8 hird_threshold; + bool activate_stm_fs_transceiver; + bool ipg_isoc_en; + u16 max_packet_count; + u32 max_transfer_size; + u32 ahbcfg; + + bool dma; + bool dma_desc; + + /* Host parameters */ + bool host_support_fs_ls_low_power; + bool host_ls_low_power_phy_clk; + + u8 host_channels; + u16 host_rx_fifo_size; + u16 host_nperio_tx_fifo_size; + u16 host_perio_tx_fifo_size; + + /* Gadget parameters */ + u32 g_rx_fifo_size; + u32 g_np_tx_fifo_size; + u32 g_tx_fifo_size[DWC2_MAX_EPS_CHANNELS]; + + bool change_speed_quirk; +}; + +/** + * struct dwc2_hw_params - Autodetected parameters. + * + * These parameters are the various parameters read from hardware + * registers during initialization. They typically contain the best + * supported or maximum value that can be configured in the + * corresponding dwc2_core_params value. + * + * The values that are not in dwc2_core_params are documented below. + * + * @op_mode: Mode of Operation + * 0 - HNP- and SRP-Capable OTG (Host & Device) + * 1 - SRP-Capable OTG (Host & Device) + * 2 - Non-HNP and Non-SRP Capable OTG (Host & Device) + * 3 - SRP-Capable Device + * 4 - Non-OTG Device + * 5 - SRP-Capable Host + * 6 - Non-OTG Host + * @arch: Architecture + * 0 - Slave only + * 1 - External DMA + * 2 - Internal DMA + * @ipg_isoc_en: This feature indicates that the controller supports + * the worst-case scenario of Rx followed by Rx + * Interpacket Gap (IPG) (32 bitTimes) as per the utmi + * specification for any token following ISOC OUT token. + * 0 - Don't support + * 1 - Support + * @power_optimized: Are power optimizations enabled? + * @num_dev_ep: Number of device endpoints available + * @num_dev_in_eps: Number of device IN endpoints available + * @num_dev_perio_in_ep: Number of device periodic IN endpoints + * available + * @dev_token_q_depth: Device Mode IN Token Sequence Learning Queue + * Depth + * 0 to 30 + * @host_perio_tx_q_depth: + * Host Mode Periodic Request Queue Depth + * 2, 4 or 8 + * @nperio_tx_q_depth: + * Non-Periodic Request Queue Depth + * 2, 4 or 8 + * @hs_phy_type: High-speed PHY interface type + * 0 - High-speed interface not supported + * 1 - UTMI+ + * 2 - ULPI + * 3 - UTMI+ and ULPI + * @fs_phy_type: Full-speed PHY interface type + * 0 - Full speed interface not supported + * 1 - Dedicated full speed interface + * 2 - FS pins shared with UTMI+ pins + * 3 - FS pins shared with ULPI pins + * @total_fifo_size: Total internal RAM for FIFOs (bytes) + * @hibernation: Is hibernation enabled? + * @utmi_phy_data_width: UTMI+ PHY data width + * 0 - 8 bits + * 1 - 16 bits + * 2 - 8 or 16 bits + * @snpsid: Value from SNPSID register + * @dev_ep_dirs: Direction of device endpoints (GHWCFG1) + * @g_tx_fifo_size: Power-on values of TxFIFO sizes + * @dma_desc_enable: When DMA mode is enabled, specifies whether to use + * address DMA mode or descriptor DMA mode for accessing + * the data FIFOs. The driver will automatically detect the + * value for this if none is specified. + * 0 - Address DMA + * 1 - Descriptor DMA (default, if available) + * @enable_dynamic_fifo: 0 - Use coreConsultant-specified FIFO size parameters + * 1 - Allow dynamic FIFO sizing (default, if available) + * @en_multiple_tx_fifo: Specifies whether dedicated per-endpoint transmit FIFOs + * are enabled for non-periodic IN endpoints in device + * mode. + * @host_nperio_tx_fifo_size: Number of 4-byte words in the non-periodic Tx FIFO + * in host mode when dynamic FIFO sizing is enabled + * 16 to 32768 + * Actual maximum value is autodetected and also + * the default. + * @host_perio_tx_fifo_size: Number of 4-byte words in the periodic Tx FIFO in + * host mode when dynamic FIFO sizing is enabled + * 16 to 32768 + * Actual maximum value is autodetected and also + * the default. + * @max_transfer_size: The maximum transfer size supported, in bytes + * 2047 to 65,535 + * Actual maximum value is autodetected and also + * the default. + * @max_packet_count: The maximum number of packets in a transfer + * 15 to 511 + * Actual maximum value is autodetected and also + * the default. + * @host_channels: The number of host channel registers to use + * 1 to 16 + * Actual maximum value is autodetected and also + * the default. + * @dev_nperio_tx_fifo_size: Number of 4-byte words in the non-periodic Tx FIFO + * in device mode when dynamic FIFO sizing is enabled + * 16 to 32768 + * Actual maximum value is autodetected and also + * the default. + * @i2c_enable: Specifies whether to use the I2Cinterface for a full + * speed PHY. This parameter is only applicable if phy_type + * is FS. + * 0 - No (default) + * 1 - Yes + * @acg_enable: For enabling Active Clock Gating in the controller + * 0 - Disable + * 1 - Enable + * @lpm_mode: For enabling Link Power Management in the controller + * 0 - Disable + * 1 - Enable + * @rx_fifo_size: Number of 4-byte words in the Rx FIFO when dynamic + * FIFO sizing is enabled 16 to 32768 + * Actual maximum value is autodetected and also + * the default. + */ +struct dwc2_hw_params { + unsigned op_mode:3; + unsigned arch:2; + unsigned dma_desc_enable:1; + unsigned enable_dynamic_fifo:1; + unsigned en_multiple_tx_fifo:1; + unsigned rx_fifo_size:16; + unsigned host_nperio_tx_fifo_size:16; + unsigned dev_nperio_tx_fifo_size:16; + unsigned host_perio_tx_fifo_size:16; + unsigned nperio_tx_q_depth:3; + unsigned host_perio_tx_q_depth:3; + unsigned dev_token_q_depth:5; + unsigned max_transfer_size:26; + unsigned max_packet_count:11; + unsigned host_channels:5; + unsigned hs_phy_type:2; + unsigned fs_phy_type:2; + unsigned i2c_enable:1; + unsigned acg_enable:1; + unsigned num_dev_ep:4; + unsigned num_dev_in_eps:4; + unsigned num_dev_perio_in_ep:4; + unsigned total_fifo_size:16; + unsigned power_optimized:1; + unsigned hibernation:1; + unsigned utmi_phy_data_width:2; + unsigned lpm_mode:1; + unsigned ipg_isoc_en:1; + u32 snpsid; + u32 dev_ep_dirs; + u32 g_tx_fifo_size[DWC2_MAX_EPS_CHANNELS]; +}; + +#define MAX_DEVICE 16 +#define MAX_ENDPOINT DWC2_MAX_EPS_CHANNELS + +struct dwc2 { + struct device_d *dev; + struct usb_host host; + struct usb_gadget gadget; + struct dwc2_hw_params hw_params; + struct dwc2_core_params params; + + u8 in_data_toggle[MAX_DEVICE][MAX_ENDPOINT]; + u8 out_data_toggle[MAX_DEVICE][MAX_ENDPOINT]; + void __iomem *regs; + int root_hub_devnum; + + enum usb_dr_mode dr_mode; +}; + +#define host_to_dwc2(ptr) container_of(ptr, struct dwc2, host) +#define gadget_to_dwc2(ptr) container_of(ptr, struct dwc2, gadget) + +#define dwc2_err(d, arg...) dev_err((d)->dev, ## arg) +#define dwc2_warn(d, arg...) dev_err((d)->dev, ## arg) +#define dwc2_info(d, arg...) dev_info((d)->dev, ## arg) +#define dwc2_dbg(d, arg...) dev_dbg((d)->dev, ## arg) +#define dwc2_vdbg(d, arg...) dev_vdbg((d)->dev, ## arg) + +static inline u32 dwc2_readl(struct dwc2 *dwc2, u32 offset) +{ + u32 val; + + val = readl(dwc2->regs + offset); + + return val; +} + +static inline void dwc2_writel(struct dwc2 *dwc2, u32 value, u32 offset) +{ + writel(value, dwc2->regs + offset); +} + +/** + * dwc2_wait_bit_set - Waits for bit to be set. + * @dwc2: Programming view of DWC2 controller. + * @offset: Register's offset where bit/bits must be set. + * @mask: Mask of the bit/bits which must be set. + * @timeout: Timeout to wait. + * + * Return: 0 if bit/bits are set or -ETIMEDOUT in case of timeout. + */ +static inline int dwc2_wait_bit_set(struct dwc2 *dwc2, u32 offset, u32 mask, + u32 timeout) +{ + u32 i; + + for (i = 0; i < timeout; i++) { + if (dwc2_readl(dwc2, offset) & mask) + return 0; + udelay(1); + } + + return -ETIMEDOUT; +} + +/** + * dwc2_wait_bit_clear - Waits for bit to be clear. + * @dwc2: Programming view of DWC2 controller. + * @offset: Register's offset where bit/bits must be set. + * @mask: Mask of the bit/bits which must be set. + * @timeout: Timeout to wait. + * + * Return: 0 if bit/bits are set or -ETIMEDOUT in case of timeout. + */ +static inline int dwc2_wait_bit_clear(struct dwc2 *dwc2, u32 offset, u32 mask, + u32 timeout) +{ + u32 i; + + for (i = 0; i < timeout; i++) { + if (!(dwc2_readl(dwc2, offset) & mask)) + return 0; + udelay(1); + } + + return -ETIMEDOUT; +} + +/* + * Returns the mode of operation, host or device + */ +static inline int dwc2_is_host_mode(struct dwc2 *dwc2) +{ + return (dwc2_readl(dwc2, GINTSTS) & GINTSTS_CURMODE_HOST) != 0; +} + +static inline int dwc2_is_device_mode(struct dwc2 *dwc2) +{ + return (dwc2_readl(dwc2, GINTSTS) & GINTSTS_CURMODE_HOST) == 0; +} + +static inline int phy_read(struct dwc2 *dwc2, u8 addr) +{ + u32 gpvndctl = 0; + + gpvndctl |= (addr & 0x3f) << GPVNDCTL_REGADDR_SHIFT; + gpvndctl &= ~GPVNDCTL_REGWR; /* read */ + gpvndctl |= GPVNDCTL_NEWREGREQ; + + dwc2_writel(dwc2, gpvndctl, GPVNDCTL); + + if (dwc2_wait_bit_set(dwc2, GPVNDCTL, GPVNDCTL_VSTSDONE, 10000)) + pr_err("Timeout: Waiting for phy read to complete\n"); + + gpvndctl = dwc2_readl(dwc2, GPVNDCTL) >> GPVNDCTL_REGDATA_SHIFT; + return gpvndctl & GPVNDCTL_REGDATA_MASK; +} + +static inline void phy_write(struct dwc2 *dwc2, u8 val, u8 addr) +{ + u32 gpvndctl = 0; + + gpvndctl |= (addr & 0x3f) << GPVNDCTL_REGADDR_SHIFT; + gpvndctl |= (val & 0xff) << GPVNDCTL_REGDATA_SHIFT; + gpvndctl |= GPVNDCTL_REGWR; /* write */ + gpvndctl |= GPVNDCTL_NEWREGREQ; + + dwc2_writel(dwc2, gpvndctl, GPVNDCTL); + + if (dwc2_wait_bit_set(dwc2, GPVNDCTL, GPVNDCTL_VSTSDONE, 10000)) + pr_err("Timeout: Waiting for phy write to complete\n"); +} diff --git a/drivers/usb/dwc2/dwc2.c b/drivers/usb/dwc2/dwc2.c new file mode 100644 index 000000000..893f573bc --- /dev/null +++ b/drivers/usb/dwc2/dwc2.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org> + * Copyright (C) 2014 Marek Vasut <marex@denx.de> + * + * Copied from u-Boot + */ + +#define DEBUG + +#include <common.h> +#include <of.h> +#include <dma.h> +#include <init.h> +#include <errno.h> +#include <driver.h> +#include <linux/clk.h> + +#include "dwc2.h" + +static void dwc2_uninit_common(struct dwc2 *dwc2) +{ + uint32_t hprt0; + + hprt0 = dwc2_readl(dwc2, HPRT0); + + /* Put everything in reset. */ + hprt0 &= ~(HPRT0_ENA | HPRT0_ENACHG | HPRT0_CONNDET | HPRT0_OVRCURRCHG); + hprt0 |= HPRT0_RST; + + dwc2_writel(dwc2, hprt0, HPRT0); +} + +static int dwc2_probe(struct device_d *dev) +{ + struct resource *iores; + struct usb_host *host; + struct dwc2 *dwc2; + int retval; + + dwc2 = xzalloc(sizeof(struct dwc2)); + dev->priv = dwc2; + dwc2->dev = dev; + + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); + dwc2->regs = IOMEM(iores->start); + + retval = dwc2_core_snpsid(dwc2); + if (retval) + goto error; + + /* + * Reset before dwc2_get_hwparams() then it could get power-on real + * reset value form registers. + */ + retval = dwc2_core_reset(dwc2); + if (retval) + goto error; + + /* Detect config values from hardware */ + dwc2_get_hwparams(dwc2); + + dwc2_set_default_params(dwc2); + + dma_set_mask(dev, DMA_BIT_MASK(32)); + + host = &dwc2->host; + host->hw_dev = dev; + host->init = dwc2_host_init; + host->submit_bulk_msg = dwc2_submit_bulk_msg; + host->submit_control_msg = dwc2_submit_control_msg; + host->submit_int_msg = dwc2_submit_int_msg; + retval = usb_register_host(host); + +error: + return retval; +} + +static void dwc2_remove(struct device_d *dev) +{ + struct dwc2 *dwc2 = dev->priv; + + dwc2_uninit_common(dwc2); +} + +static const struct of_device_id dwc2_platform_dt_ids[] = { + { .compatible = "brcm,bcm2835-usb", }, + { .compatible = "brcm,bcm2708-usb", }, + { .compatible = "snps,dwc2", }, + { } +}; + +static struct driver_d dwc2_driver = { + .name = "dwc2", + .probe = dwc2_probe, + .remove = dwc2_remove, + .of_compatible = DRV_OF_COMPAT(dwc2_platform_dt_ids), +}; +device_platform_driver(dwc2_driver); diff --git a/drivers/usb/dwc2/dwc2.h b/drivers/usb/dwc2/dwc2.h new file mode 100644 index 000000000..0ac4b40fc --- /dev/null +++ b/drivers/usb/dwc2/dwc2.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +#include <usb/usb.h> +#include <usb/usb_defs.h> +#include <usb/gadget.h> + +#include "regs.h" +#include "core.h" + +/* Core functions */ +void dwc2_set_param_otg_cap(struct dwc2 *dwc2); +void dwc2_set_param_phy_type(struct dwc2 *dwc2); +void dwc2_set_param_speed(struct dwc2 *dwc2); +void dwc2_set_param_phy_utmi_width(struct dwc2 *dwc2); +void dwc2_set_default_params(struct dwc2 *dwc2); +int dwc2_core_snpsid(struct dwc2 *dwc2); +void dwc2_get_hwparams(struct dwc2 *dwc2); + +void dwc2_init_fs_ls_pclk_sel(struct dwc2 *dwc2); +void dwc2_flush_all_fifo(struct dwc2 *dwc2); + +int dwc2_phy_init(struct dwc2 *dwc2, bool select_phy); +int dwc2_gahbcfg_init(struct dwc2 *dwc2); +void dwc2_gusbcfg_init(struct dwc2 *dwc2); + +int dwc2_core_reset(struct dwc2 *dwc2); +void dwc2_core_init(struct dwc2 *dwc2); + +/* Host functions */ +int dwc2_submit_control_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, + struct devrequest *setup, int timeout); +int dwc2_submit_bulk_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, int timeout); +int dwc2_submit_int_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, int interval); +int dwc2_host_init(struct usb_host *host); + +int dwc2_submit_rh_msg(struct dwc2 *dwc2, struct usb_device *dev, + unsigned long pipe, void *buf, int len, + struct devrequest *setup); + diff --git a/drivers/usb/dwc2/host.c b/drivers/usb/dwc2/host.c new file mode 100644 index 000000000..4137dd3cb --- /dev/null +++ b/drivers/usb/dwc2/host.c @@ -0,0 +1,618 @@ +// SPDX-License-Identifier: GPL-2.0+ +#include <dma.h> +#include "dwc2.h" + +#define to_dwc2 host_to_dwc2 + +/* Use only HC channel 0. */ +#define DWC2_HC_CHANNEL 0 + +static int dwc2_eptype[] = { + DXEPCTL_EPTYPE_ISO, + DXEPCTL_EPTYPE_INTERRUPT, + DXEPCTL_EPTYPE_CONTROL, + DXEPCTL_EPTYPE_BULK, +}; + +static int dwc2_do_split(struct dwc2 *dwc2, struct usb_device *dev) +{ + uint32_t hprt0 = dwc2_readl(dwc2, HPRT0); + uint32_t prtspd = (hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT; + + return prtspd == HPRT0_SPD_HIGH_SPEED && dev->speed != USB_SPEED_HIGH; +} + +static void dwc2_host_hub_info(struct dwc2 *dwc2, struct usb_device *dev, + uint8_t *hub_addr, uint8_t *hub_port) +{ + *hub_addr = dev->devnum; + *hub_port = dev->portnr; + + for (; dev->parent; dev = dev->parent) { + if (dev->parent->descriptor->bDeviceClass == USB_CLASS_HUB) { + *hub_addr = dev->parent->devnum; + *hub_port = dev->parent->portnr; + break; + } + } +} + +static void dwc2_hc_init_split(struct dwc2 *dwc2, struct usb_device *dev, + uint8_t hc) +{ + uint8_t hub_addr, hub_port; + uint32_t hcsplt = 0; + + dwc2_host_hub_info(dwc2, dev, &hub_addr, &hub_port); + + hcsplt = HCSPLT_SPLTENA; + hcsplt |= hub_addr << HCSPLT_HUBADDR_SHIFT; + hcsplt |= hub_port << HCSPLT_PRTADDR_SHIFT; + + /* Program the HCSPLIT register for SPLITs */ + dwc2_writel(dwc2, hcsplt, HCSPLT(hc)); +} + +static void dwc2_hc_enable_ints(struct dwc2 *dwc2, uint8_t hc) +{ + uint32_t intmsk; + uint32_t hcintmsk = HCINTMSK_CHHLTD; + + dwc2_writel(dwc2, hcintmsk, HCINTMSK(hc)); + + /* Enable the top level host channel interrupt */ + intmsk = dwc2_readl(dwc2, HAINTMSK); + intmsk |= 1 << hc; + dwc2_writel(dwc2, intmsk, HAINTMSK); + + /* Make sure host channel interrupts are enabled */ + intmsk = dwc2_readl(dwc2, GINTMSK); + intmsk |= GINTSTS_HCHINT; + dwc2_writel(dwc2, intmsk, GINTMSK); +} + +/** + * Prepares a host channel for transferring packets to/from a specific + * endpoint. The HCCHARn register is set up with the characteristics specified + * in _hc. Host channel interrupts that may need to be serviced while this + * transfer is in progress are enabled. + * + * @param regs Programming view of DWC2 controller + * @param hc Information needed to initialize the host channel + */ +static void dwc2_hc_init(struct dwc2 *dwc2, uint8_t hc, + struct usb_device *dev, uint8_t dev_addr, uint8_t ep_num, + uint8_t ep_is_in, uint32_t ep_type, uint16_t max_packet) +{ + uint32_t hcchar = (dev_addr << HCCHAR_DEVADDR_SHIFT) | + (ep_num << HCCHAR_EPNUM_SHIFT) | + (ep_is_in ? HCCHAR_EPDIR : 0) | + ep_type | + (max_packet << HCCHAR_MPS_SHIFT); + + if (dev->speed == USB_SPEED_LOW) + hcchar |= HCCHAR_LSPDDEV; + + /* Clear old interrupt conditions for this dwc2 channel */ + dwc2_writel(dwc2, ~HCINTMSK_RESERVED14_31, HCINT(hc)); + + /* Enable channel interrupts required for this transfer */ + dwc2_hc_enable_ints(dwc2, hc); + + /* + * Program the HCCHARn register with the endpoint characteristics + * for the current transfer. + */ + dwc2_writel(dwc2, hcchar, HCCHAR(hc)); + + /* Program the HCSPLIT register, default to no SPLIT */ + dwc2_writel(dwc2, 0, HCSPLT(hc)); +} + +static int wait_for_chhltd(struct dwc2 *dwc2, u8 hc, uint32_t *sub, u8 *tgl) +{ + int ret; + uint32_t hcint, hctsiz; + + ret = dwc2_wait_bit_set(dwc2, HCINT(hc), HCINTMSK_CHHLTD, 10000); + if (ret) { + dwc2_err(dwc2, "%s: Timeout! Channel not halted\n", __func__); + } + + hcint = dwc2_readl(dwc2, HCINT(hc)); + + if (hcint & HCINTMSK_AHBERR) + dwc2_err(dwc2, "%s: AHB error during internal DMA access\n", + __func__); + + if (hcint & HCINTMSK_XFERCOMPL) { + hctsiz = dwc2_readl(dwc2, HCTSIZ(hc)); + *sub = (hctsiz & TSIZ_XFERSIZE_MASK) >> TSIZ_XFERSIZE_SHIFT; + *tgl = (hctsiz & TSIZ_SC_MC_PID_MASK) >> TSIZ_SC_MC_PID_SHIFT; + + dwc2_dbg(dwc2, "%s: HCINT=%08x sub=%u toggle=%d\n", __func__, + hcint, *sub, *tgl); + return 0; + } + + if (hcint & (HCINTMSK_NAK | HCINTMSK_FRMOVRUN)) + return -EAGAIN; + + dwc2_dbg(dwc2, "%s: Unknown channel status: (HCINT=%08x)\n", __func__, + hcint); + return -EINVAL; +} + +static int transfer_chunk(struct dwc2 *dwc2, u8 hc, + u8 *pid, int in, void *buffer, int num_packets, + int xfer_len, int *actual_len, int odd_frame) +{ + uint32_t hctsiz, hcchar, sub; + dma_addr_t dma_addr; + int ret = 0; + + dma_addr = dma_map_single(dwc2->dev, buffer, xfer_len, + in ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + + dwc2_dbg(dwc2, "chunk: pid=%d xfer_len=%u pkts=%u dma_addr=%llx\n", + *pid, xfer_len, num_packets, dma_addr); + + if (!in) + dma_sync_single_for_device(dma_addr, xfer_len, DMA_TO_DEVICE); + + dwc2_writel(dwc2, dma_addr, HCDMA(hc)); + + hctsiz = (xfer_len << TSIZ_XFERSIZE_SHIFT) + | (1 << TSIZ_PKTCNT_SHIFT) + | (*pid << TSIZ_SC_MC_PID_SHIFT); + + dwc2_writel(dwc2, hctsiz, HCTSIZ(hc)); + + /* Clear old interrupt conditions for this dwc2 channel. */ + dwc2_writel(dwc2, 0x3fff, HCINT(hc)); + + /* Set dwc2 channel enable after all other setup is complete. */ + hcchar = dwc2_readl(dwc2, HCCHAR(hc)); + hcchar &= ~(HCCHAR_MULTICNT_MASK | HCCHAR_CHDIS); + hcchar |= (1 << HCCHAR_MULTICNT_SHIFT) | HCCHAR_CHENA; + if (odd_frame) + hcchar |= HCCHAR_ODDFRM; + else + hcchar &= ~HCCHAR_ODDFRM; + dwc2_writel(dwc2, hcchar, HCCHAR(hc)); + + ret = wait_for_chhltd(dwc2, hc, &sub, pid); + if (ret < 0) + goto exit; + + if (in) { + xfer_len -= sub; + dma_sync_single_for_cpu(dma_addr, xfer_len, DMA_FROM_DEVICE); + } + *actual_len = xfer_len; + +exit: + dma_unmap_single(dwc2->dev, dma_addr, xfer_len, + in ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + + return ret; +} + +static int dwc2_submit_packet(struct dwc2 *dwc2, struct usb_device *dev, + unsigned long pipe, u8 *pid, int in, void *buf, + int len) +{ + int devnum = usb_pipedevice(pipe); + int ep = usb_pipeendpoint(pipe); + int mps = usb_maxpacket(dev, pipe); + int eptype = dwc2_eptype[usb_pipetype(pipe)]; + int do_split = dwc2_do_split(dwc2, dev); + int complete_split = 0; + int done = 0; + int ret = 0; + uint32_t xfer_len; + uint32_t num_packets; + int stop_transfer = 0; + uint32_t max_xfer_len; + int ssplit_frame_num = 0; + + max_xfer_len = dwc2->params.max_packet_count * mps; + + if (max_xfer_len > dwc2->params.max_transfer_size) + max_xfer_len = dwc2->params.max_transfer_size; + + /* Make sure that max_xfer_len is a multiple of max packet size. */ + num_packets = max_xfer_len / mps; + max_xfer_len = num_packets * mps; + + /* Initialize channel */ + dwc2_hc_init(dwc2, DWC2_HC_CHANNEL, dev, devnum, ep, in, eptype, mps); + + /* Check if the target is a FS/LS device behind a HS hub */ + if (do_split) { + dwc2_hc_init_split(dwc2, dev, DWC2_HC_CHANNEL); + num_packets = 1; + max_xfer_len = mps; + } + do { + int actual_len = 0; + uint32_t hcint, hcsplt; + int odd_frame = 0; + + xfer_len = len - done; + + if (xfer_len > max_xfer_len) + xfer_len = max_xfer_len; + else if (xfer_len > mps) + num_packets = (xfer_len + mps - 1) / mps; + else + num_packets = 1; + + if (complete_split || do_split) { + hcsplt = dwc2_readl(dwc2, HCSPLT(DWC2_HC_CHANNEL)); + if (complete_split) + hcsplt |= HCSPLT_COMPSPLT; + else if (do_split) + hcsplt &= ~HCSPLT_COMPSPLT; + dwc2_writel(dwc2, hcsplt, HCSPLT(DWC2_HC_CHANNEL)); + } + + if (eptype == DXEPCTL_EPTYPE_INTERRUPT) { + int uframe_num = dwc2_readl(dwc2, HFNUM); + + if (!(uframe_num & 0x1)) + odd_frame = 1; + } + + ret = transfer_chunk(dwc2, DWC2_HC_CHANNEL, pid, + in, (char *)buf + done, num_packets, + xfer_len, &actual_len, odd_frame); + + hcint = dwc2_readl(dwc2, HCINT(DWC2_HC_CHANNEL)); + if (complete_split) { + stop_transfer = 0; + if (hcint & HCINTMSK_NYET) { + int frame_num = HFNUM_MAX_FRNUM & + dwc2_readl(dwc2, HFNUM); + + ret = 0; + if (((frame_num - ssplit_frame_num) & + HFNUM_MAX_FRNUM) > 4) + ret = -EAGAIN; + } else { + complete_split = 0; + } + } else if (do_split) { + if (hcint & HCINTMSK_ACK) { + ssplit_frame_num = HFNUM_MAX_FRNUM & + dwc2_readl(dwc2, HFNUM); + ret = 0; + complete_split = 1; + } + } + + if (ret) + break; + + if (actual_len < xfer_len) + stop_transfer = 1; + + done += actual_len; + + /* Transactions are done when when either all data is transferred or + * there is a short transfer. In case of a SPLIT make sure the CSPLIT + * is executed. + */ + } while (((done < len) && !stop_transfer) || complete_split); + + dwc2_writel(dwc2, 0, HCINTMSK(DWC2_HC_CHANNEL)); + dwc2_writel(dwc2, 0xFFFFFFFF, HCINT(DWC2_HC_CHANNEL)); + + dev->status = 0; + dev->act_len = done; + + return ret; +} + +int dwc2_submit_control_msg(struct usb_device *udev, + unsigned long pipe, void *buffer, int len, + struct devrequest *setup, int timeout) +{ + struct usb_host *host = udev->host; + struct dwc2 *dwc2 = to_dwc2(host); + int devnum = usb_pipedevice(pipe); + int ret, act_len; + u8 pid; + /* For CONTROL endpoint pid should start with DATA1 */ + int status_direction; + + if (devnum == dwc2->root_hub_devnum) { + udev->status = 0; + udev->speed = USB_SPEED_HIGH; + return dwc2_submit_rh_msg(dwc2, udev, pipe, buffer, len, setup); + } + + /* SETUP stage */ + pid = TSIZ_SC_MC_PID_SETUP; + do { + ret = dwc2_submit_packet(dwc2, udev, pipe, &pid, 0, setup, 8); + } while (ret == -EAGAIN); + if (ret) + return ret; + + /* DATA stage */ + act_len = 0; + if (buffer) { + pid = TSIZ_SC_MC_PID_DATA1; + do { + ret = dwc2_submit_packet(dwc2, udev, pipe, &pid, + usb_pipein(pipe), buffer, len); + act_len += udev->act_len; + buffer += udev->act_len; + len -= udev->act_len; + } while (ret == -EAGAIN); + if (ret) + return ret; + status_direction = usb_pipeout(pipe); + } else { + /* No-data CONTROL always ends with an IN transaction */ + status_direction = 1; + } + + /* STATUS stage */ + pid = TSIZ_SC_MC_PID_DATA1; + do { + ret = dwc2_submit_packet(dwc2, udev, pipe, &pid, + status_direction, NULL, 0); + } while (ret == -EAGAIN); + if (ret) + return ret; + + udev->act_len = act_len; + + return 0; +} + +int dwc2_submit_bulk_msg(struct usb_device *udev, unsigned long pipe, + void *buffer, int len, int timeout) +{ + struct usb_host *host = udev->host; + struct dwc2 *dwc2 = to_dwc2(host); + int devnum = usb_pipedevice(pipe); + int ep = usb_pipeendpoint(pipe); + int in = usb_pipein(pipe); + u8 *pid; + + if ((devnum >= MAX_DEVICE) || (devnum == dwc2->root_hub_devnum)) { + udev->status = 0; + return -EINVAL; + } + + if (in) + pid = &dwc2->in_data_toggle[devnum][ep]; + else + pid = &dwc2->out_data_toggle[devnum][ep]; + + return dwc2_submit_packet(dwc2, udev, pipe, pid, in, buffer, len); +} + +int dwc2_submit_int_msg(struct usb_device *udev, unsigned long pipe, + void *buffer, int len, int interval) +{ + struct usb_host *host = udev->host; + struct dwc2 *dwc2 = to_dwc2(host); + int devnum = usb_pipedevice(pipe); + int ep = usb_pipeendpoint(pipe); + int in = usb_pipein(pipe); + u8 *pid; + uint64_t start; + int ret; + + if ((devnum >= MAX_DEVICE) || (devnum == dwc2->root_hub_devnum)) { + udev->status = 0; + return -EINVAL; + } + + if (usb_pipein(pipe)) + pid = &dwc2->in_data_toggle[devnum][ep]; + else + pid = &dwc2->out_data_toggle[devnum][ep]; + + start = get_time_ns(); + + while (1) { + ret = dwc2_submit_packet(dwc2, udev, pipe, pid, in, + buffer, len); + if (ret != -EAGAIN) + return ret; + if (is_timeout(start, USB_CNTL_TIMEOUT * MSECOND)) { + dwc2_err(dwc2, "Timeout on interrupt endpoint\n"); + return -ETIMEDOUT; + } + } +} + +/* + * This function initializes the DWC2 controller registers for + * host mode. + * + * This function flushes the Tx and Rx FIFOs and it flushes any entries in the + * 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 DWC2 controller + * + */ +static void dwc2_core_host_init(struct device_d *dev, + struct dwc2 *dwc2) +{ + uint32_t nptxfifosize = 0; + uint32_t ptxfifosize = 0; + uint32_t hcchar, hcfg, hprt0, hotgctl, usbcfg; + int i, ret, num_channels; + + dwc2_dbg(dwc2, "%s(%p)\n", __func__, dwc2); + + /* Set HS/FS Timeout Calibration to 7 (max available value). + * The number of PHY clocks that the application programs in + * this field is added to the high/full speed interpacket timeout + * duration in the core to account for any additional delays + * introduced by the PHY. This can be required, because the delay + * introduced by the PHY in generating the linestate condition + * can vary from one PHY to another. + */ + usbcfg = dwc2_readl(dwc2, GUSBCFG); + usbcfg |= GUSBCFG_TOUTCAL(7); + dwc2_writel(dwc2, usbcfg, GUSBCFG); + + /* Restart the Phy Clock */ + dwc2_writel(dwc2, 0, PCGCTL); + + /* Initialize Host Configuration Register */ + dwc2_init_fs_ls_pclk_sel(dwc2); + if (dwc2->params.speed == DWC2_SPEED_PARAM_FULL || + dwc2->params.speed == DWC2_SPEED_PARAM_LOW) { + hcfg = dwc2_readl(dwc2, HCFG); + hcfg |= HCFG_FSLSSUPP; + dwc2_writel(dwc2, hcfg, HCFG); + } + + if (dwc2->params.dma_desc) { + u32 op_mode = dwc2->hw_params.op_mode; + + if (dwc2->hw_params.snpsid < DWC2_CORE_REV_2_90a || + !dwc2->hw_params.dma_desc_enable || + op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE || + op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE || + op_mode == GHWCFG2_OP_MODE_UNDEFINED) { + dwc2_err(dwc2, "Descriptor DMA not suppported\n"); + dwc2_err(dwc2, "falling back to buffer DMA mode.\n"); + dwc2->params.dma_desc = false; + } else { + hcfg = dwc2_readl(dwc2, HCFG); + hcfg |= HCFG_DESCDMA; + dwc2_writel(dwc2, hcfg, HCFG); + } + } + + /* Configure data FIFO sizes */ + if (dwc2_readl(dwc2, GHWCFG2) & GHWCFG2_DYNAMIC_FIFO) { + /* Rx FIFO */ + dwc2_writel(dwc2, CONFIG_DWC2_HOST_RX_FIFO_SIZE, GRXFSIZ); + + /* Non-periodic Tx FIFO */ + nptxfifosize |= CONFIG_DWC2_HOST_NPERIO_TX_FIFO_SIZE << + FIFOSIZE_DEPTH_SHIFT; + nptxfifosize |= CONFIG_DWC2_HOST_RX_FIFO_SIZE << + FIFOSIZE_STARTADDR_SHIFT; + dwc2_writel(dwc2, nptxfifosize, GNPTXFSIZ); + + /* Periodic Tx FIFO */ + ptxfifosize |= CONFIG_DWC2_HOST_PERIO_TX_FIFO_SIZE << + FIFOSIZE_DEPTH_SHIFT; + ptxfifosize |= (CONFIG_DWC2_HOST_RX_FIFO_SIZE + + CONFIG_DWC2_HOST_NPERIO_TX_FIFO_SIZE) << + FIFOSIZE_STARTADDR_SHIFT; + dwc2_writel(dwc2, ptxfifosize, HPTXFSIZ); + } + + /* Clear Host Set HNP Enable in the OTG Control Register */ + hotgctl = dwc2_readl(dwc2, GOTGCTL); + hotgctl &= ~GOTGCTL_HSTSETHNPEN; + dwc2_writel(dwc2, hotgctl, GOTGCTL); + + /* Make sure the FIFOs are flushed. */ + dwc2_flush_all_fifo(dwc2); + + /* Flush out any leftover queued requests. */ + num_channels = dwc2->params.host_channels; + for (i = 0; i < num_channels; i++) { + hcchar = dwc2_readl(dwc2, HCCHAR(i)); + if (!(hcchar & HCCHAR_CHENA)) + continue; + hcchar |= HCCHAR_CHDIS; + hcchar &= ~(HCCHAR_CHENA | HCCHAR_EPDIR); + dwc2_writel(dwc2, hcchar, HCCHAR(i)); + } + + /* Halt all channels to put them into a known state. */ + for (i = 0; i < num_channels; i++) { + hcchar = dwc2_readl(dwc2, HCCHAR(i)); + if (!(hcchar & HCCHAR_CHENA)) + continue; + hcchar |= HCCHAR_CHENA | HCCHAR_CHDIS; + hcchar &= ~HCCHAR_EPDIR; + + dwc2_writel(dwc2, hcchar, HCCHAR(i)); + ret = dwc2_wait_bit_clear(dwc2, HCCHAR(i), HCCHAR_CHENA, 10000); + if (ret) + dwc2_warn(dwc2, "%s: Timeout! Reseting channel %d\n", + __func__, i); + } + + /* Turn on the vbus power */ + if (dwc2_is_host_mode(dwc2)) { + hprt0 = dwc2_readl(dwc2, HPRT0); + hprt0 &= ~(HPRT0_ENA | HPRT0_CONNDET); + hprt0 &= ~(HPRT0_ENACHG | HPRT0_OVRCURRCHG); + if (!(hprt0 & HPRT0_PWR)) { + hprt0 |= HPRT0_PWR; + dwc2_writel(dwc2, hprt0, HPRT0); + } + } + + /* Disable all interrupts */ + dwc2_writel(dwc2, 0, GINTMSK); + dwc2_writel(dwc2, 0, HAINTMSK); +} + +int dwc2_host_init(struct usb_host *host) +{ + struct dwc2 *dwc2 = to_dwc2(host); + struct device_d *dev = dwc2->dev; + uint32_t hprt0, gusbcfg; + int i, j; + + /* Force Host mode in case the dwc2 controller is otg, + * otherwise the mode selection is dictated by the id + * pin, thus will require a otg A cable to be plugged-in. + */ + gusbcfg = dwc2_readl(dwc2, GUSBCFG) | GUSBCFG_FORCEHOSTMODE; + dwc2_writel(dwc2, gusbcfg, GUSBCFG); + mdelay(25); + + dwc2_core_init(dwc2); + dwc2_core_host_init(dev, dwc2); + + hprt0 = dwc2_readl(dwc2, HPRT0); + hprt0 &= ~(HPRT0_ENA | HPRT0_CONNDET); + /* clear HPRT0_ENACHG and HPRT0_OVRCURRCHG by writing 1 */ + hprt0 |= HPRT0_ENACHG | HPRT0_OVRCURRCHG; + hprt0 |= HPRT0_RST; + dwc2_writel(dwc2, hprt0, HPRT0); + + mdelay(50); + + hprt0 = dwc2_readl(dwc2, HPRT0); + hprt0 &= ~(HPRT0_ENA | HPRT0_CONNDET | HPRT0_RST); + dwc2_writel(dwc2, hprt0, HPRT0); + + for (i = 0; i < MAX_DEVICE; i++) { + for (j = 0; j < MAX_ENDPOINT; j++) { + dwc2->in_data_toggle[i][j] = TSIZ_SC_MC_PID_DATA0; + dwc2->out_data_toggle[i][j] = TSIZ_SC_MC_PID_DATA0; + } + } + + /* + * Add a 1 second delay here. This gives the host controller + * a bit time before the comminucation with the USB devices + * is started (the bus is scanned) and fixes the USB detection + * problems with some problematic USB keys. + */ + if (dwc2_is_host_mode(dwc2)) + mdelay(1000); + + return 0; +} diff --git a/drivers/usb/dwc2/regs.h b/drivers/usb/dwc2/regs.h new file mode 100644 index 000000000..b2b312e1b --- /dev/null +++ b/drivers/usb/dwc2/regs.h @@ -0,0 +1,847 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2014 Marek Vasut <marex@denx.de> + */ + +#ifndef __DWC2_H__ +#define __DWC2_H__ + +#define HSOTG_REG(x) (x) + +#define GOTGCTL HSOTG_REG(0x000) +#define GOTGCTL_CHIRPEN BIT(27) +#define GOTGCTL_MULT_VALID_BC_MASK (0x1f << 22) +#define GOTGCTL_MULT_VALID_BC_SHIFT 22 +#define GOTGCTL_CURMODE BIT(21) /* was missing wtf ? */ +#define GOTGCTL_OTGVER BIT(20) +#define GOTGCTL_BSESVLD BIT(19) +#define GOTGCTL_ASESVLD BIT(18) +#define GOTGCTL_DBNC_SHORT BIT(17) +#define GOTGCTL_CONID_B BIT(16) +#define GOTGCTL_DBNCE_FLTR_BYPASS BIT(15) +#define GOTGCTL_EMBHOSTEN BIT(12) +#define GOTGCTL_DEVHNPEN BIT(11) +#define GOTGCTL_HSTSETHNPEN BIT(10) +#define GOTGCTL_HNPREQ BIT(9) +#define GOTGCTL_HSTNEGSCS BIT(8) +#define GOTGCTL_SESREQ BIT(1) +#define GOTGCTL_SESREQSCS BIT(0) + +#define GOTGINT HSOTG_REG(0x004) +#define GOTGINT_DBNCE_DONE BIT(19) +#define GOTGINT_A_DEV_TOUT_CHG BIT(18) +#define GOTGINT_HST_NEG_DET BIT(17) +#define GOTGINT_HST_NEG_SUC_STS_CHNG BIT(9) +#define GOTGINT_SES_REQ_SUC_STS_CHNG BIT(8) +#define GOTGINT_SES_END_DET BIT(2) + +#define GAHBCFG HSOTG_REG(0x008) +#define GAHBCFG_AHB_SINGLE BIT(23) +#define GAHBCFG_NOTI_ALL_DMA_WRIT BIT(22) +#define GAHBCFG_REM_MEM_SUPP BIT(21) +#define GAHBCFG_P_TXF_EMP_LVL BIT(8) +#define GAHBCFG_NP_TXF_EMP_LVL BIT(7) +#define GAHBCFG_DMA_EN BIT(5) +#define GAHBCFG_HBSTLEN_MASK (0xf << 1) +#define GAHBCFG_HBSTLEN_SHIFT 1 +#define GAHBCFG_HBSTLEN_SINGLE 0 +#define GAHBCFG_HBSTLEN_INCR 1 +#define GAHBCFG_HBSTLEN_INCR4 3 +#define GAHBCFG_HBSTLEN_INCR8 5 +#define GAHBCFG_HBSTLEN_INCR16 7 +#define GAHBCFG_GLBL_INTR_EN BIT(0) +#define GAHBCFG_CTRL_MASK (GAHBCFG_P_TXF_EMP_LVL | \ + GAHBCFG_NP_TXF_EMP_LVL | \ + GAHBCFG_DMA_EN | \ + GAHBCFG_GLBL_INTR_EN) + +#define GUSBCFG HSOTG_REG(0x00C) +#define GUSBCFG_FORCEDEVMODE BIT(30) +#define GUSBCFG_FORCEHOSTMODE BIT(29) +#define GUSBCFG_TXENDDELAY BIT(28) +#define GUSBCFG_ICTRAFFICPULLREMOVE BIT(27) +#define GUSBCFG_ICUSBCAP BIT(26) +#define GUSBCFG_ULPI_INT_PROT_DIS BIT(25) +#define GUSBCFG_INDICATORPASSTHROUGH BIT(24) +#define GUSBCFG_INDICATORCOMPLEMENT BIT(23) +#define GUSBCFG_TERMSELDLPULSE BIT(22) +#define GUSBCFG_ULPI_EXT_VBUS_IND BIT(21) +#define GUSBCFG_ULPI_EXT_VBUS_DRV BIT(20) +#define GUSBCFG_ULPI_CLK_SUSP_M BIT(19) +#define GUSBCFG_ULPI_AUTO_RES BIT(18) +#define GUSBCFG_ULPI_FS_LS BIT(17) +#define GUSBCFG_OTG_UTMI_FS_SEL BIT(16) +#define GUSBCFG_PHY_LP_CLK_SEL BIT(15) +#define GUSBCFG_USBTRDTIM_MASK (0xf << 10) +#define GUSBCFG_USBTRDTIM_SHIFT 10 +#define GUSBCFG_HNPCAP BIT(9) +#define GUSBCFG_SRPCAP BIT(8) +#define GUSBCFG_DDRSEL BIT(7) +#define GUSBCFG_PHYSEL BIT(6) +#define GUSBCFG_FSINTF BIT(5) +#define GUSBCFG_ULPI_UTMI_SEL BIT(4) +#define GUSBCFG_PHYIF16 BIT(3) +#define GUSBCFG_PHYIF8 (0 << 3) +#define GUSBCFG_TOUTCAL_MASK (0x7 << 0) +#define GUSBCFG_TOUTCAL_SHIFT 0 +#define GUSBCFG_TOUTCAL_LIMIT 0x7 +#define GUSBCFG_TOUTCAL(_x) ((_x) << 0) + +#define GRSTCTL HSOTG_REG(0x010) +#define GRSTCTL_AHBIDLE BIT(31) +#define GRSTCTL_DMAREQ BIT(30) +#define GRSTCTL_TXFNUM_MASK (0x1f << 6) +#define GRSTCTL_TXFNUM_SHIFT 6 +#define GRSTCTL_TXFNUM_LIMIT 0x1f +#define GRSTCTL_TXFNUM(_x) ((_x) << 6) +#define GRSTCTL_TXFFLSH BIT(5) +#define GRSTCTL_RXFFLSH BIT(4) +#define GRSTCTL_IN_TKNQ_FLSH BIT(3) +#define GRSTCTL_FRMCNTRRST BIT(2) +#define GRSTCTL_HSFTRST BIT(1) +#define GRSTCTL_CSFTRST BIT(0) + +#define GINTSTS HSOTG_REG(0x014) +#define GINTMSK HSOTG_REG(0x018) +#define GINTSTS_WKUPINT BIT(31) +#define GINTSTS_SESSREQINT BIT(30) +#define GINTSTS_DISCONNINT BIT(29) +#define GINTSTS_CONIDSTSCHNG BIT(28) +#define GINTSTS_LPMTRANRCVD BIT(27) +#define GINTSTS_PTXFEMP BIT(26) +#define GINTSTS_HCHINT BIT(25) +#define GINTSTS_PRTINT BIT(24) +#define GINTSTS_RESETDET BIT(23) +#define GINTSTS_FET_SUSP BIT(22) +#define GINTSTS_INCOMPL_IP BIT(21) +#define GINTSTS_INCOMPL_SOOUT BIT(21) +#define GINTSTS_INCOMPL_SOIN BIT(20) +#define GINTSTS_OEPINT BIT(19) +#define GINTSTS_IEPINT BIT(18) +#define GINTSTS_EPMIS BIT(17) +#define GINTSTS_RESTOREDONE BIT(16) +#define GINTSTS_EOPF BIT(15) +#define GINTSTS_ISOUTDROP BIT(14) +#define GINTSTS_ENUMDONE BIT(13) +#define GINTSTS_USBRST BIT(12) +#define GINTSTS_USBSUSP BIT(11) +#define GINTSTS_ERLYSUSP BIT(10) +#define GINTSTS_I2CINT BIT(9) +#define GINTSTS_ULPI_CK_INT BIT(8) +#define GINTSTS_GOUTNAKEFF BIT(7) +#define GINTSTS_GINNAKEFF BIT(6) +#define GINTSTS_NPTXFEMP BIT(5) +#define GINTSTS_RXFLVL BIT(4) +#define GINTSTS_SOF BIT(3) +#define GINTSTS_OTGINT BIT(2) +#define GINTSTS_MODEMIS BIT(1) +#define GINTSTS_CURMODE_HOST BIT(0) + +#define GRXSTSR HSOTG_REG(0x01C) +#define GRXSTSP HSOTG_REG(0x020) +#define GRXSTS_FN_MASK (0x7f << 25) +#define GRXSTS_FN_SHIFT 25 +#define GRXSTS_PKTSTS_MASK (0xf << 17) +#define GRXSTS_PKTSTS_SHIFT 17 +#define GRXSTS_PKTSTS_GLOBALOUTNAK 1 +#define GRXSTS_PKTSTS_OUTRX 2 +#define GRXSTS_PKTSTS_HCHIN 2 +#define GRXSTS_PKTSTS_OUTDONE 3 +#define GRXSTS_PKTSTS_HCHIN_XFER_COMP 3 +#define GRXSTS_PKTSTS_SETUPDONE 4 +#define GRXSTS_PKTSTS_DATATOGGLEERR 5 +#define GRXSTS_PKTSTS_SETUPRX 6 +#define GRXSTS_PKTSTS_HCHHALTED 7 +#define GRXSTS_HCHNUM_MASK (0xf << 0) +#define GRXSTS_HCHNUM_SHIFT 0 +#define GRXSTS_DPID_MASK (0x3 << 15) +#define GRXSTS_DPID_SHIFT 15 +#define GRXSTS_BYTECNT_MASK (0x7ff << 4) +#define GRXSTS_BYTECNT_SHIFT 4 +#define GRXSTS_EPNUM_MASK (0xf << 0) +#define GRXSTS_EPNUM_SHIFT 0 + +#define GRXFSIZ HSOTG_REG(0x024) +#define GRXFSIZ_DEPTH_MASK (0xffff << 0) +#define GRXFSIZ_DEPTH_SHIFT 0 + +#define GNPTXFSIZ HSOTG_REG(0x028) +/* Use FIFOSIZE_* constants to access this register */ + +#define GNPTXSTS HSOTG_REG(0x02C) +#define GNPTXSTS_NP_TXQ_TOP_MASK (0x7f << 24) +#define GNPTXSTS_NP_TXQ_TOP_SHIFT 24 +#define GNPTXSTS_NP_TXQ_SPC_AVAIL_MASK (0xff << 16) +#define GNPTXSTS_NP_TXQ_SPC_AVAIL_SHIFT 16 +#define GNPTXSTS_NP_TXQ_SPC_AVAIL_GET(_v) (((_v) >> 16) & 0xff) +#define GNPTXSTS_NP_TXF_SPC_AVAIL_MASK (0xffff << 0) +#define GNPTXSTS_NP_TXF_SPC_AVAIL_SHIFT 0 +#define GNPTXSTS_NP_TXF_SPC_AVAIL_GET(_v) (((_v) >> 0) & 0xffff) + +#define GI2CCTL HSOTG_REG(0x0030) +#define GI2CCTL_BSYDNE BIT(31) +#define GI2CCTL_RW BIT(30) +#define GI2CCTL_I2CDATSE0 BIT(28) +#define GI2CCTL_I2CDEVADDR_MASK (0x3 << 26) +#define GI2CCTL_I2CDEVADDR_SHIFT 26 +#define GI2CCTL_I2CSUSPCTL BIT(25) +#define GI2CCTL_ACK BIT(24) +#define GI2CCTL_I2CEN BIT(23) +#define GI2CCTL_ADDR_MASK (0x7f << 16) +#define GI2CCTL_ADDR_SHIFT 16 +#define GI2CCTL_REGADDR_MASK (0xff << 8) +#define GI2CCTL_REGADDR_SHIFT 8 +#define GI2CCTL_RWDATA_MASK (0xff << 0) +#define GI2CCTL_RWDATA_SHIFT 0 + +#define GPVNDCTL HSOTG_REG(0x0034) +#define GPVNDCTL_REGWR BIT(22) +#define GPVNDCTL_NEWREGREQ BIT(25) +#define GPVNDCTL_VSTSDONE BIT(27) +#define GPVNDCTL_REGADDR_SHIFT 16 +#define GPVNDCTL_REGDATA_SHIFT 0 +#define GPVNDCTL_REGDATA_MASK 0xff + +#define GGPIO HSOTG_REG(0x0038) +#define GGPIO_STM32_OTG_GCCFG_PWRDWN BIT(16) + +#define GUID HSOTG_REG(0x003c) +#define GSNPSID HSOTG_REG(0x0040) +#define GHWCFG1 HSOTG_REG(0x0044) +#define GSNPSID_ID_MASK GENMASK(31, 16) + +#define GHWCFG2 HSOTG_REG(0x0048) +#define GHWCFG2_OTG_ENABLE_IC_USB BIT(31) +#define GHWCFG2_DEV_TOKEN_Q_DEPTH_MASK (0x1f << 26) +#define GHWCFG2_DEV_TOKEN_Q_DEPTH_SHIFT 26 +#define GHWCFG2_HOST_PERIO_TX_Q_DEPTH_MASK (0x3 << 24) +#define GHWCFG2_HOST_PERIO_TX_Q_DEPTH_SHIFT 24 +#define GHWCFG2_NONPERIO_TX_Q_DEPTH_MASK (0x3 << 22) +#define GHWCFG2_NONPERIO_TX_Q_DEPTH_SHIFT 22 +#define GHWCFG2_MULTI_PROC_INT BIT(20) +#define GHWCFG2_DYNAMIC_FIFO BIT(19) +#define GHWCFG2_PERIO_EP_SUPPORTED BIT(18) +#define GHWCFG2_NUM_HOST_CHAN_MASK (0xf << 14) +#define GHWCFG2_NUM_HOST_CHAN_SHIFT 14 +#define GHWCFG2_NUM_DEV_EP_MASK (0xf << 10) +#define GHWCFG2_NUM_DEV_EP_SHIFT 10 +#define GHWCFG2_FS_PHY_TYPE_MASK (0x3 << 8) +#define GHWCFG2_FS_PHY_TYPE_SHIFT 8 +#define GHWCFG2_FS_PHY_TYPE_NOT_SUPPORTED 0 +#define GHWCFG2_FS_PHY_TYPE_DEDICATED 1 +#define GHWCFG2_FS_PHY_TYPE_SHARED_UTMI 2 +#define GHWCFG2_FS_PHY_TYPE_SHARED_ULPI 3 +#define GHWCFG2_HS_PHY_TYPE_MASK (0x3 << 6) +#define GHWCFG2_HS_PHY_TYPE_SHIFT 6 +#define GHWCFG2_HS_PHY_TYPE_NOT_SUPPORTED 0 +#define GHWCFG2_HS_PHY_TYPE_UTMI 1 +#define GHWCFG2_HS_PHY_TYPE_ULPI 2 +#define GHWCFG2_HS_PHY_TYPE_UTMI_ULPI 3 +#define GHWCFG2_POINT2POINT BIT(5) +#define GHWCFG2_ARCHITECTURE_MASK (0x3 << 3) +#define GHWCFG2_ARCHITECTURE_SHIFT 3 +#define GHWCFG2_SLAVE_ONLY_ARCH 0 +#define GHWCFG2_EXT_DMA_ARCH 1 +#define GHWCFG2_INT_DMA_ARCH 2 +#define GHWCFG2_OP_MODE_MASK (0x7 << 0) +#define GHWCFG2_OP_MODE_SHIFT 0 +#define GHWCFG2_OP_MODE_HNP_SRP_CAPABLE 0 +#define GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE 1 +#define GHWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE 2 +#define GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE 3 +#define GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE 4 +#define GHWCFG2_OP_MODE_SRP_CAPABLE_HOST 5 +#define GHWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST 6 +#define GHWCFG2_OP_MODE_UNDEFINED 7 + +#define GHWCFG3 HSOTG_REG(0x004c) +#define GHWCFG3_DFIFO_DEPTH_MASK (0xffff << 16) +#define GHWCFG3_DFIFO_DEPTH_SHIFT 16 +#define GHWCFG3_OTG_LPM_EN BIT(15) +#define GHWCFG3_BC_SUPPORT BIT(14) +#define GHWCFG3_OTG_ENABLE_HSIC BIT(13) +#define GHWCFG3_ADP_SUPP BIT(12) +#define GHWCFG3_SYNCH_RESET_TYPE BIT(11) +#define GHWCFG3_OPTIONAL_FEATURES BIT(10) +#define GHWCFG3_VENDOR_CTRL_IF BIT(9) +#define GHWCFG3_I2C BIT(8) +#define GHWCFG3_OTG_FUNC BIT(7) +#define GHWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK (0x7 << 4) +#define GHWCFG3_PACKET_SIZE_CNTR_WIDTH_SHIFT 4 +#define GHWCFG3_XFER_SIZE_CNTR_WIDTH_MASK (0xf << 0) +#define GHWCFG3_XFER_SIZE_CNTR_WIDTH_SHIFT 0 + +#define GHWCFG4 HSOTG_REG(0x0050) +#define GHWCFG4_DESC_DMA_DYN BIT(31) +#define GHWCFG4_DESC_DMA BIT(30) +#define GHWCFG4_NUM_IN_EPS_MASK (0xf << 26) +#define GHWCFG4_NUM_IN_EPS_SHIFT 26 +#define GHWCFG4_DED_FIFO_EN BIT(25) +#define GHWCFG4_DED_FIFO_SHIFT 25 +#define GHWCFG4_SESSION_END_FILT_EN BIT(24) +#define GHWCFG4_B_VALID_FILT_EN BIT(23) +#define GHWCFG4_A_VALID_FILT_EN BIT(22) +#define GHWCFG4_VBUS_VALID_FILT_EN BIT(21) +#define GHWCFG4_IDDIG_FILT_EN BIT(20) +#define GHWCFG4_NUM_DEV_MODE_CTRL_EP_MASK (0xf << 16) +#define GHWCFG4_NUM_DEV_MODE_CTRL_EP_SHIFT 16 +#define GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK (0x3 << 14) +#define GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT 14 +#define GHWCFG4_ACG_SUPPORTED BIT(12) +#define GHWCFG4_IPG_ISOC_SUPPORTED BIT(11) +#define GHWCFG4_UTMI_PHY_DATA_WIDTH_8 0 +#define GHWCFG4_UTMI_PHY_DATA_WIDTH_16 1 +#define GHWCFG4_UTMI_PHY_DATA_WIDTH_8_OR_16 2 +#define GHWCFG4_XHIBER BIT(7) +#define GHWCFG4_HIBER BIT(6) +#define GHWCFG4_MIN_AHB_FREQ BIT(5) +#define GHWCFG4_POWER_OPTIMIZ BIT(4) +#define GHWCFG4_NUM_DEV_PERIO_IN_EP_MASK (0xf << 0) +#define GHWCFG4_NUM_DEV_PERIO_IN_EP_SHIFT 0 + +#define GLPMCFG HSOTG_REG(0x0054) +#define GLPMCFG_INVSELHSIC BIT(31) +#define GLPMCFG_HSICCON BIT(30) +#define GLPMCFG_RSTRSLPSTS BIT(29) +#define GLPMCFG_ENBESL BIT(28) +#define GLPMCFG_LPM_RETRYCNT_STS_MASK (0x7 << 25) +#define GLPMCFG_LPM_RETRYCNT_STS_SHIFT 25 +#define GLPMCFG_SNDLPM BIT(24) +#define GLPMCFG_RETRY_CNT_MASK (0x7 << 21) +#define GLPMCFG_RETRY_CNT_SHIFT 21 +#define GLPMCFG_LPM_CHNL_INDX_MASK (0xf << 17) +#define GLPMCFG_LPM_CHNL_INDX_SHIFT 17 +#define GLPMCFG_L1RESUMEOK BIT(16) +#define GLPMCFG_SLPSTS BIT(15) +#define GLPMCFG_COREL1RES_MASK (0x3 << 13) +#define GLPMCFG_COREL1RES_SHIFT 13 +#define GLPMCFG_HIRD_THRES_MASK (0x1f << 8) +#define GLPMCFG_HIRD_THRES_SHIFT 8 +#define GLPMCFG_HIRD_THRES_EN (0x10 << 8) +#define GLPMCFG_ENBLSLPM BIT(7) +#define GLPMCFG_BREMOTEWAKE BIT(6) +#define GLPMCFG_HIRD_MASK (0xf << 2) +#define GLPMCFG_HIRD_SHIFT 2 +#define GLPMCFG_APPL1RES BIT(1) +#define GLPMCFG_LPMCAP BIT(0) + +#define GPWRDN HSOTG_REG(0x0058) +#define GPWRDN_MULT_VAL_ID_BC_MASK (0x1f << 24) +#define GPWRDN_MULT_VAL_ID_BC_SHIFT 24 +#define GPWRDN_ADP_INT BIT(23) +#define GPWRDN_BSESSVLD BIT(22) +#define GPWRDN_IDSTS BIT(21) +#define GPWRDN_LINESTATE_MASK (0x3 << 19) +#define GPWRDN_LINESTATE_SHIFT 19 +#define GPWRDN_STS_CHGINT_MSK BIT(18) +#define GPWRDN_STS_CHGINT BIT(17) +#define GPWRDN_SRP_DET_MSK BIT(16) +#define GPWRDN_SRP_DET BIT(15) +#define GPWRDN_CONNECT_DET_MSK BIT(14) +#define GPWRDN_CONNECT_DET BIT(13) +#define GPWRDN_DISCONN_DET_MSK BIT(12) +#define GPWRDN_DISCONN_DET BIT(11) +#define GPWRDN_RST_DET_MSK BIT(10) +#define GPWRDN_RST_DET BIT(9) +#define GPWRDN_LNSTSCHG_MSK BIT(8) +#define GPWRDN_LNSTSCHG BIT(7) +#define GPWRDN_DIS_VBUS BIT(6) +#define GPWRDN_PWRDNSWTCH BIT(5) +#define GPWRDN_PWRDNRSTN BIT(4) +#define GPWRDN_PWRDNCLMP BIT(3) +#define GPWRDN_RESTORE BIT(2) +#define GPWRDN_PMUACTV BIT(1) +#define GPWRDN_PMUINTSEL BIT(0) + +#define GDFIFOCFG HSOTG_REG(0x005c) +#define GDFIFOCFG_EPINFOBASE_MASK (0xffff << 16) +#define GDFIFOCFG_EPINFOBASE_SHIFT 16 +#define GDFIFOCFG_GDFIFOCFG_MASK (0xffff << 0) +#define GDFIFOCFG_GDFIFOCFG_SHIFT 0 + +#define ADPCTL HSOTG_REG(0x0060) +#define ADPCTL_AR_MASK (0x3 << 27) +#define ADPCTL_AR_SHIFT 27 +#define ADPCTL_ADP_TMOUT_INT_MSK BIT(26) +#define ADPCTL_ADP_SNS_INT_MSK BIT(25) +#define ADPCTL_ADP_PRB_INT_MSK BIT(24) +#define ADPCTL_ADP_TMOUT_INT BIT(23) +#define ADPCTL_ADP_SNS_INT BIT(22) +#define ADPCTL_ADP_PRB_INT BIT(21) +#define ADPCTL_ADPENA BIT(20) +#define ADPCTL_ADPRES BIT(19) +#define ADPCTL_ENASNS BIT(18) +#define ADPCTL_ENAPRB BIT(17) +#define ADPCTL_RTIM_MASK (0x7ff << 6) +#define ADPCTL_RTIM_SHIFT 6 +#define ADPCTL_PRB_PER_MASK (0x3 << 4) +#define ADPCTL_PRB_PER_SHIFT 4 +#define ADPCTL_PRB_DELTA_MASK (0x3 << 2) +#define ADPCTL_PRB_DELTA_SHIFT 2 +#define ADPCTL_PRB_DSCHRG_MASK (0x3 << 0) +#define ADPCTL_PRB_DSCHRG_SHIFT 0 + +#define HPTXFSIZ HSOTG_REG(0x100) +/* Use FIFOSIZE_* constants to access this register */ + +#define DPTXFSIZN(_a) HSOTG_REG(0x104 + (((_a) - 1) * 4)) +/* Use FIFOSIZE_* constants to access this register */ + +/* These apply to the GNPTXFSIZ, HPTXFSIZ and DPTXFSIZN registers */ +#define FIFOSIZE_DEPTH_MASK (0xffff << 16) +#define FIFOSIZE_DEPTH_SHIFT 16 +#define FIFOSIZE_STARTADDR_MASK (0xffff << 0) +#define FIFOSIZE_STARTADDR_SHIFT 0 +#define FIFOSIZE_DEPTH_GET(_x) (((_x) >> 16) & 0xffff) + +/* Device mode registers */ + +#define DCFG HSOTG_REG(0x800) +#define DCFG_DESCDMA_EN BIT(23) +#define DCFG_EPMISCNT_MASK (0x1f << 18) +#define DCFG_EPMISCNT_SHIFT 18 +#define DCFG_EPMISCNT_LIMIT 0x1f +#define DCFG_EPMISCNT(_x) ((_x) << 18) +#define DCFG_IPG_ISOC_SUPPORDED BIT(17) +#define DCFG_PERFRINT_MASK (0x3 << 11) +#define DCFG_PERFRINT_SHIFT 11 +#define DCFG_PERFRINT_LIMIT 0x3 +#define DCFG_PERFRINT(_x) ((_x) << 11) +#define DCFG_DEVADDR_MASK (0x7f << 4) +#define DCFG_DEVADDR_SHIFT 4 +#define DCFG_DEVADDR_LIMIT 0x7f +#define DCFG_DEVADDR(_x) ((_x) << 4) +#define DCFG_NZ_STS_OUT_HSHK BIT(2) +#define DCFG_DEVSPD_MASK (0x3 << 0) +#define DCFG_DEVSPD_SHIFT 0 +#define DCFG_DEVSPD_HS 0 +#define DCFG_DEVSPD_FS 1 +#define DCFG_DEVSPD_LS 2 +#define DCFG_DEVSPD_FS48 3 + +#define DCTL HSOTG_REG(0x804) +#define DCTL_PWRONPRGDONE BIT(11) +#define DCTL_CGOUTNAK BIT(10) +#define DCTL_SGOUTNAK BIT(9) +#define DCTL_CGNPINNAK BIT(8) +#define DCTL_SGNPINNAK BIT(7) +#define DCTL_TSTCTL_MASK (0x7 << 4) +#define DCTL_TSTCTL_SHIFT 4 +#define DCTL_GOUTNAKSTS BIT(3) +#define DCTL_GNPINNAKSTS BIT(2) +#define DCTL_SFTDISCON BIT(1) +#define DCTL_RMTWKUPSIG BIT(0) + +#define DSTS HSOTG_REG(0x808) +#define DSTS_SOFFN_MASK (0x3fff << 8) +#define DSTS_SOFFN_SHIFT 8 +#define DSTS_SOFFN_LIMIT 0x3fff +#define DSTS_SOFFN(_x) ((_x) << 8) +#define DSTS_ERRATICERR BIT(3) +#define DSTS_ENUMSPD_MASK (0x3 << 1) +#define DSTS_ENUMSPD_SHIFT 1 +#define DSTS_ENUMSPD_HS 0 +#define DSTS_ENUMSPD_FS 1 +#define DSTS_ENUMSPD_LS 2 +#define DSTS_ENUMSPD_FS48 3 +#define DSTS_SUSPSTS BIT(0) + +#define DIEPMSK HSOTG_REG(0x810) +#define DIEPMSK_NAKMSK BIT(13) +#define DIEPMSK_BNAININTRMSK BIT(9) +#define DIEPMSK_TXFIFOUNDRNMSK BIT(8) +#define DIEPMSK_TXFIFOEMPTY BIT(7) +#define DIEPMSK_INEPNAKEFFMSK BIT(6) +#define DIEPMSK_INTKNEPMISMSK BIT(5) +#define DIEPMSK_INTKNTXFEMPMSK BIT(4) +#define DIEPMSK_TIMEOUTMSK BIT(3) +#define DIEPMSK_AHBERRMSK BIT(2) +#define DIEPMSK_EPDISBLDMSK BIT(1) +#define DIEPMSK_XFERCOMPLMSK BIT(0) + +#define DOEPMSK HSOTG_REG(0x814) +#define DOEPMSK_BNAMSK BIT(9) +#define DOEPMSK_BACK2BACKSETUP BIT(6) +#define DOEPMSK_STSPHSERCVDMSK BIT(5) +#define DOEPMSK_OUTTKNEPDISMSK BIT(4) +#define DOEPMSK_SETUPMSK BIT(3) +#define DOEPMSK_AHBERRMSK BIT(2) +#define DOEPMSK_EPDISBLDMSK BIT(1) +#define DOEPMSK_XFERCOMPLMSK BIT(0) + +#define DAINT HSOTG_REG(0x818) +#define DAINTMSK HSOTG_REG(0x81C) +#define DAINT_OUTEP_SHIFT 16 +#define DAINT_OUTEP(_x) (1 << ((_x) + 16)) +#define DAINT_INEP(_x) (1 << (_x)) + +#define DTKNQR1 HSOTG_REG(0x820) +#define DTKNQR2 HSOTG_REG(0x824) +#define DTKNQR3 HSOTG_REG(0x830) +#define DTKNQR4 HSOTG_REG(0x834) +#define DIEPEMPMSK HSOTG_REG(0x834) + +#define DVBUSDIS HSOTG_REG(0x828) +#define DVBUSPULSE HSOTG_REG(0x82C) + +#define DIEPCTL0 HSOTG_REG(0x900) +#define DIEPCTL(_a) HSOTG_REG(0x900 + ((_a) * 0x20)) + +#define DOEPCTL0 HSOTG_REG(0xB00) +#define DOEPCTL(_a) HSOTG_REG(0xB00 + ((_a) * 0x20)) + +/* EP0 specialness: + * bits[29..28] - reserved (no SetD0PID, SetD1PID) + * bits[25..22] - should always be zero, this isn't a periodic endpoint + * bits[10..0] - MPS setting different for EP0 + */ +#define D0EPCTL_MPS_MASK (0x3 << 0) +#define D0EPCTL_MPS_SHIFT 0 +#define D0EPCTL_MPS_64 0 +#define D0EPCTL_MPS_32 1 +#define D0EPCTL_MPS_16 2 +#define D0EPCTL_MPS_8 3 + +#define DXEPCTL_EPENA BIT(31) +#define DXEPCTL_EPDIS BIT(30) +#define DXEPCTL_SETD1PID BIT(29) +#define DXEPCTL_SETODDFR BIT(29) +#define DXEPCTL_SETD0PID BIT(28) +#define DXEPCTL_SETEVENFR BIT(28) +#define DXEPCTL_SNAK BIT(27) +#define DXEPCTL_CNAK BIT(26) +#define DXEPCTL_TXFNUM_MASK (0xf << 22) +#define DXEPCTL_TXFNUM_SHIFT 22 +#define DXEPCTL_TXFNUM_LIMIT 0xf +#define DXEPCTL_TXFNUM(_x) ((_x) << 22) +#define DXEPCTL_STALL BIT(21) +#define DXEPCTL_SNP BIT(20) +#define DXEPCTL_EPTYPE_MASK (0x3 << 18) +#define DXEPCTL_EPTYPE_CONTROL (0x0 << 18) +#define DXEPCTL_EPTYPE_ISO (0x1 << 18) +#define DXEPCTL_EPTYPE_BULK (0x2 << 18) +#define DXEPCTL_EPTYPE_INTERRUPT (0x3 << 18) + +#define DXEPCTL_NAKSTS BIT(17) +#define DXEPCTL_DPID BIT(16) +#define DXEPCTL_EOFRNUM BIT(16) +#define DXEPCTL_USBACTEP BIT(15) +#define DXEPCTL_NEXTEP_MASK (0xf << 11) +#define DXEPCTL_NEXTEP_SHIFT 11 +#define DXEPCTL_NEXTEP_LIMIT 0xf +#define DXEPCTL_NEXTEP(_x) ((_x) << 11) +#define DXEPCTL_MPS_MASK (0x7ff << 0) +#define DXEPCTL_MPS_SHIFT 0 +#define DXEPCTL_MPS_LIMIT 0x7ff +#define DXEPCTL_MPS(_x) ((_x) << 0) + +#define DIEPINT(_a) HSOTG_REG(0x908 + ((_a) * 0x20)) +#define DOEPINT(_a) HSOTG_REG(0xB08 + ((_a) * 0x20)) +#define DXEPINT_SETUP_RCVD BIT(15) +#define DXEPINT_NYETINTRPT BIT(14) +#define DXEPINT_NAKINTRPT BIT(13) +#define DXEPINT_BBLEERRINTRPT BIT(12) +#define DXEPINT_PKTDRPSTS BIT(11) +#define DXEPINT_BNAINTR BIT(9) +#define DXEPINT_TXFIFOUNDRN BIT(8) +#define DXEPINT_OUTPKTERR BIT(8) +#define DXEPINT_TXFEMP BIT(7) +#define DXEPINT_INEPNAKEFF BIT(6) +#define DXEPINT_BACK2BACKSETUP BIT(6) +#define DXEPINT_INTKNEPMIS BIT(5) +#define DXEPINT_STSPHSERCVD BIT(5) +#define DXEPINT_INTKNTXFEMP BIT(4) +#define DXEPINT_OUTTKNEPDIS BIT(4) +#define DXEPINT_TIMEOUT BIT(3) +#define DXEPINT_SETUP BIT(3) +#define DXEPINT_AHBERR BIT(2) +#define DXEPINT_EPDISBLD BIT(1) +#define DXEPINT_XFERCOMPL BIT(0) + +#define DIEPTSIZ0 HSOTG_REG(0x910) +#define DIEPTSIZ0_PKTCNT_MASK (0x3 << 19) +#define DIEPTSIZ0_PKTCNT_SHIFT 19 +#define DIEPTSIZ0_PKTCNT_LIMIT 0x3 +#define DIEPTSIZ0_PKTCNT(_x) ((_x) << 19) +#define DIEPTSIZ0_XFERSIZE_MASK (0x7f << 0) +#define DIEPTSIZ0_XFERSIZE_SHIFT 0 +#define DIEPTSIZ0_XFERSIZE_LIMIT 0x7f +#define DIEPTSIZ0_XFERSIZE(_x) ((_x) << 0) + +#define DOEPTSIZ0 HSOTG_REG(0xB10) +#define DOEPTSIZ0_SUPCNT_MASK (0x3 << 29) +#define DOEPTSIZ0_SUPCNT_SHIFT 29 +#define DOEPTSIZ0_SUPCNT_LIMIT 0x3 +#define DOEPTSIZ0_SUPCNT(_x) ((_x) << 29) +#define DOEPTSIZ0_PKTCNT BIT(19) +#define DOEPTSIZ0_XFERSIZE_MASK (0x7f << 0) +#define DOEPTSIZ0_XFERSIZE_SHIFT 0 + +#define DIEPTSIZ(_a) HSOTG_REG(0x910 + ((_a) * 0x20)) +#define DOEPTSIZ(_a) HSOTG_REG(0xB10 + ((_a) * 0x20)) +#define DXEPTSIZ_MC_MASK (0x3 << 29) +#define DXEPTSIZ_MC_SHIFT 29 +#define DXEPTSIZ_MC_LIMIT 0x3 +#define DXEPTSIZ_MC(_x) ((_x) << 29) +#define DXEPTSIZ_PKTCNT_MASK (0x3ff << 19) +#define DXEPTSIZ_PKTCNT_SHIFT 19 +#define DXEPTSIZ_PKTCNT_LIMIT 0x3ff +#define DXEPTSIZ_PKTCNT_GET(_v) (((_v) >> 19) & 0x3ff) +#define DXEPTSIZ_PKTCNT(_x) ((_x) << 19) +#define DXEPTSIZ_XFERSIZE_MASK (0x7ffff << 0) +#define DXEPTSIZ_XFERSIZE_SHIFT 0 +#define DXEPTSIZ_XFERSIZE_LIMIT 0x7ffff +#define DXEPTSIZ_XFERSIZE_GET(_v) (((_v) >> 0) & 0x7ffff) +#define DXEPTSIZ_XFERSIZE(_x) ((_x) << 0) + +#define DIEPDMA(_a) HSOTG_REG(0x914 + ((_a) * 0x20)) +#define DOEPDMA(_a) HSOTG_REG(0xB14 + ((_a) * 0x20)) + +#define DTXFSTS(_a) HSOTG_REG(0x918 + ((_a) * 0x20)) + +#define PCGCTL HSOTG_REG(0x0e00) +#define PCGCTL_IF_DEV_MODE BIT(31) +#define PCGCTL_P2HD_PRT_SPD_MASK (0x3 << 29) +#define PCGCTL_P2HD_PRT_SPD_SHIFT 29 +#define PCGCTL_P2HD_DEV_ENUM_SPD_MASK (0x3 << 27) +#define PCGCTL_P2HD_DEV_ENUM_SPD_SHIFT 27 +#define PCGCTL_MAC_DEV_ADDR_MASK (0x7f << 20) +#define PCGCTL_MAC_DEV_ADDR_SHIFT 20 +#define PCGCTL_MAX_TERMSEL BIT(19) +#define PCGCTL_MAX_XCVRSELECT_MASK (0x3 << 17) +#define PCGCTL_MAX_XCVRSELECT_SHIFT 17 +#define PCGCTL_PORT_POWER BIT(16) +#define PCGCTL_PRT_CLK_SEL_MASK (0x3 << 14) +#define PCGCTL_PRT_CLK_SEL_SHIFT 14 +#define PCGCTL_ESS_REG_RESTORED BIT(13) +#define PCGCTL_EXTND_HIBER_SWITCH BIT(12) +#define PCGCTL_EXTND_HIBER_PWRCLMP BIT(11) +#define PCGCTL_ENBL_EXTND_HIBER BIT(10) +#define PCGCTL_RESTOREMODE BIT(9) +#define PCGCTL_RESETAFTSUSP BIT(8) +#define PCGCTL_DEEP_SLEEP BIT(7) +#define PCGCTL_PHY_IN_SLEEP BIT(6) +#define PCGCTL_ENBL_SLEEP_GATING BIT(5) +#define PCGCTL_RSTPDWNMODULE BIT(3) +#define PCGCTL_PWRCLMP BIT(2) +#define PCGCTL_GATEHCLK BIT(1) +#define PCGCTL_STOPPCLK BIT(0) + +#define PCGCCTL1 HSOTG_REG(0xe04) +#define PCGCCTL1_TIMER (0x3 << 1) +#define PCGCCTL1_GATEEN BIT(0) + +#define EPFIFO(_a) HSOTG_REG(0x1000 + ((_a) * 0x1000)) + +/* Host Mode Registers */ + +#define HCFG HSOTG_REG(0x0400) +#define HCFG_MODECHTIMEN BIT(31) +#define HCFG_PERSCHEDENA BIT(26) +#define HCFG_FRLISTEN_MASK (0x3 << 24) +#define HCFG_FRLISTEN_SHIFT 24 +#define HCFG_FRLISTEN_8 (0 << 24) +#define FRLISTEN_8_SIZE 8 +#define HCFG_FRLISTEN_16 BIT(24) +#define FRLISTEN_16_SIZE 16 +#define HCFG_FRLISTEN_32 (2 << 24) +#define FRLISTEN_32_SIZE 32 +#define HCFG_FRLISTEN_64 (3 << 24) +#define FRLISTEN_64_SIZE 64 +#define HCFG_DESCDMA BIT(23) +#define HCFG_RESVALID_MASK (0xff << 8) +#define HCFG_RESVALID_SHIFT 8 +#define HCFG_ENA32KHZ BIT(7) +#define HCFG_FSLSSUPP BIT(2) +#define HCFG_FSLSPCLKSEL_MASK (0x3 << 0) +#define HCFG_FSLSPCLKSEL_SHIFT 0 +#define HCFG_FSLSPCLKSEL_30_60_MHZ 0 +#define HCFG_FSLSPCLKSEL_48_MHZ 1 +#define HCFG_FSLSPCLKSEL_6_MHZ 2 + +#define HFIR HSOTG_REG(0x0404) +#define HFIR_FRINT_MASK (0xffff << 0) +#define HFIR_FRINT_SHIFT 0 +#define HFIR_RLDCTRL BIT(16) + +#define HFNUM HSOTG_REG(0x0408) +#define HFNUM_FRREM_MASK (0xffff << 16) +#define HFNUM_FRREM_SHIFT 16 +#define HFNUM_FRNUM_MASK (0xffff << 0) +#define HFNUM_FRNUM_SHIFT 0 +#define HFNUM_MAX_FRNUM 0x3fff + +#define HPTXSTS HSOTG_REG(0x0410) +#define TXSTS_QTOP_ODD BIT(31) +#define TXSTS_QTOP_CHNEP_MASK (0xf << 27) +#define TXSTS_QTOP_CHNEP_SHIFT 27 +#define TXSTS_QTOP_TOKEN_MASK (0x3 << 25) +#define TXSTS_QTOP_TOKEN_SHIFT 25 +#define TXSTS_QTOP_TERMINATE BIT(24) +#define TXSTS_QSPCAVAIL_MASK (0xff << 16) +#define TXSTS_QSPCAVAIL_SHIFT 16 +#define TXSTS_FSPCAVAIL_MASK (0xffff << 0) +#define TXSTS_FSPCAVAIL_SHIFT 0 + +#define HAINT HSOTG_REG(0x0414) +#define HAINTMSK HSOTG_REG(0x0418) +#define HFLBADDR HSOTG_REG(0x041c) + +#define HPRT0 HSOTG_REG(0x0440) +#define HPRT0_SPD_MASK (0x3 << 17) +#define HPRT0_SPD_SHIFT 17 +#define HPRT0_SPD_HIGH_SPEED 0 +#define HPRT0_SPD_FULL_SPEED 1 +#define HPRT0_SPD_LOW_SPEED 2 +#define HPRT0_TSTCTL_MASK (0xf << 13) +#define HPRT0_TSTCTL_SHIFT 13 +#define HPRT0_PWR BIT(12) +#define HPRT0_LNSTS_MASK (0x3 << 10) +#define HPRT0_LNSTS_SHIFT 10 +#define HPRT0_RST BIT(8) +#define HPRT0_SUSP BIT(7) +#define HPRT0_RES BIT(6) +#define HPRT0_OVRCURRCHG BIT(5) +#define HPRT0_OVRCURRACT BIT(4) +#define HPRT0_ENACHG BIT(3) +#define HPRT0_ENA BIT(2) +#define HPRT0_CONNDET BIT(1) +#define HPRT0_CONNSTS BIT(0) + +#define HCCHAR(_ch) HSOTG_REG(0x0500 + 0x20 * (_ch)) +#define HCCHAR_CHENA BIT(31) +#define HCCHAR_CHDIS BIT(30) +#define HCCHAR_ODDFRM BIT(29) +#define HCCHAR_DEVADDR_MASK (0x7f << 22) +#define HCCHAR_DEVADDR_SHIFT 22 +#define HCCHAR_MULTICNT_MASK (0x3 << 20) +#define HCCHAR_MULTICNT_SHIFT 20 +#define HCCHAR_EPTYPE_MASK (0x3 << 18) +#define HCCHAR_EPTYPE_SHIFT 18 +#define HCCHAR_LSPDDEV BIT(17) +#define HCCHAR_EPDIR BIT(15) +#define HCCHAR_EPNUM_MASK (0xf << 11) +#define HCCHAR_EPNUM_SHIFT 11 +#define HCCHAR_MPS_MASK (0x7ff << 0) +#define HCCHAR_MPS_SHIFT 0 + +#define HCSPLT(_ch) HSOTG_REG(0x0504 + 0x20 * (_ch)) +#define HCSPLT_SPLTENA BIT(31) +#define HCSPLT_COMPSPLT BIT(16) +#define HCSPLT_XACTPOS_MASK (0x3 << 14) +#define HCSPLT_XACTPOS_SHIFT 14 +#define HCSPLT_XACTPOS_MID 0 +#define HCSPLT_XACTPOS_END 1 +#define HCSPLT_XACTPOS_BEGIN 2 +#define HCSPLT_XACTPOS_ALL 3 +#define HCSPLT_HUBADDR_MASK (0x7f << 7) +#define HCSPLT_HUBADDR_SHIFT 7 +#define HCSPLT_PRTADDR_MASK (0x7f << 0) +#define HCSPLT_PRTADDR_SHIFT 0 + +#define HCINT(_ch) HSOTG_REG(0x0508 + 0x20 * (_ch)) +#define HCINTMSK(_ch) HSOTG_REG(0x050c + 0x20 * (_ch)) +#define HCINTMSK_RESERVED14_31 (0x3ffff << 14) +#define HCINTMSK_FRM_LIST_ROLL BIT(13) +#define HCINTMSK_XCS_XACT BIT(12) +#define HCINTMSK_BNA BIT(11) +#define HCINTMSK_DATATGLERR BIT(10) +#define HCINTMSK_FRMOVRUN BIT(9) +#define HCINTMSK_BBLERR BIT(8) +#define HCINTMSK_XACTERR BIT(7) +#define HCINTMSK_NYET BIT(6) +#define HCINTMSK_ACK BIT(5) +#define HCINTMSK_NAK BIT(4) +#define HCINTMSK_STALL BIT(3) +#define HCINTMSK_AHBERR BIT(2) +#define HCINTMSK_CHHLTD BIT(1) +#define HCINTMSK_XFERCOMPL BIT(0) + +#define HCTSIZ(_ch) HSOTG_REG(0x0510 + 0x20 * (_ch)) +#define TSIZ_DOPNG BIT(31) +#define TSIZ_SC_MC_PID_MASK (0x3 << 29) +#define TSIZ_SC_MC_PID_SHIFT 29 +#define TSIZ_SC_MC_PID_DATA0 0 +#define TSIZ_SC_MC_PID_DATA2 1 +#define TSIZ_SC_MC_PID_DATA1 2 +#define TSIZ_SC_MC_PID_MDATA 3 +#define TSIZ_SC_MC_PID_SETUP 3 +#define TSIZ_PKTCNT_MASK (0x3ff << 19) +#define TSIZ_PKTCNT_SHIFT 19 +#define TSIZ_NTD_MASK (0xff << 8) +#define TSIZ_NTD_SHIFT 8 +#define TSIZ_SCHINFO_MASK (0xff << 0) +#define TSIZ_SCHINFO_SHIFT 0 +#define TSIZ_XFERSIZE_MASK (0x7ffff << 0) +#define TSIZ_XFERSIZE_SHIFT 0 + +#define HCDMA(_ch) HSOTG_REG(0x0514 + 0x20 * (_ch)) + +#define HCDMAB(_ch) HSOTG_REG(0x051c + 0x20 * (_ch)) + +#define HCFIFO(_ch) HSOTG_REG(0x1000 + 0x1000 * (_ch)) + +/** + * struct dwc2_dma_desc - DMA descriptor structure, + * used for both host and gadget modes + * + * @status: DMA descriptor status quadlet + * @buf: DMA descriptor data buffer pointer + * + * DMA Descriptor structure contains two quadlets: + * Status quadlet and Data buffer pointer. + */ +struct dwc2_dma_desc { + u32 status; + u32 buf; +} __packed; + +/* Host Mode DMA descriptor status quadlet */ + +#define HOST_DMA_A BIT(31) +#define HOST_DMA_STS_MASK (0x3 << 28) +#define HOST_DMA_STS_SHIFT 28 +#define HOST_DMA_STS_PKTERR BIT(28) +#define HOST_DMA_EOL BIT(26) +#define HOST_DMA_IOC BIT(25) +#define HOST_DMA_SUP BIT(24) +#define HOST_DMA_ALT_QTD BIT(23) +#define HOST_DMA_QTD_OFFSET_MASK (0x3f << 17) +#define HOST_DMA_QTD_OFFSET_SHIFT 17 +#define HOST_DMA_ISOC_NBYTES_MASK (0xfff << 0) +#define HOST_DMA_ISOC_NBYTES_SHIFT 0 +#define HOST_DMA_NBYTES_MASK (0x1ffff << 0) +#define HOST_DMA_NBYTES_SHIFT 0 +#define HOST_DMA_NBYTES_LIMIT 131071 + +#define MAX_DMA_DESC_NUM_GENERIC 64 +#define MAX_DMA_DESC_NUM_HS_ISOC 256 + + /* DWC OTG HW Release versions */ +#define DWC2_CORE_REV_2_71a 0x4f54271a +#define DWC2_CORE_REV_2_72a 0x4f54272a +#define DWC2_CORE_REV_2_80a 0x4f54280a +#define DWC2_CORE_REV_2_90a 0x4f54290a +#define DWC2_CORE_REV_2_91a 0x4f54291a +#define DWC2_CORE_REV_2_92a 0x4f54292a +#define DWC2_CORE_REV_2_94a 0x4f54294a +#define DWC2_CORE_REV_3_00a 0x4f54300a +#define DWC2_CORE_REV_3_10a 0x4f54310a +#define DWC2_CORE_REV_4_00a 0x4f54400a +#define DWC2_FS_IOT_REV_1_00a 0x5531100a +#define DWC2_HS_IOT_REV_1_00a 0x5532100a + + /* DWC OTG HW Core ID */ +#define DWC2_OTG_ID 0x4f540000 +#define DWC2_FS_IOT_ID 0x55310000 +#define DWC2_HS_IOT_ID 0x55320000 + +/* ==== u-boot ==== */ + +/* Default driver configuration */ +#define CONFIG_DWC2_MAX_CHANNELS DWC2_MAX_EPS_CHANNELS /* Max # of EPs */ +#define CONFIG_DWC2_HOST_RX_FIFO_SIZE (516 + CONFIG_DWC2_MAX_CHANNELS) +#define CONFIG_DWC2_HOST_NPERIO_TX_FIFO_SIZE 0x100 /* nPeriodic TX FIFO */ +#define CONFIG_DWC2_HOST_PERIO_TX_FIFO_SIZE 0x200 /* Periodic TX FIFO */ + +#endif /* __DWC2_H__ */ diff --git a/drivers/usb/dwc2/rhub.c b/drivers/usb/dwc2/rhub.c new file mode 100644 index 000000000..b94723d7b --- /dev/null +++ b/drivers/usb/dwc2/rhub.c @@ -0,0 +1,418 @@ +#include "dwc2.h" + +static struct descriptor { + struct usb_hub_descriptor hub; + struct usb_device_descriptor device; + struct usb_config_descriptor config; + struct usb_interface_descriptor interface; + struct usb_endpoint_descriptor endpoint; +} __attribute__ ((packed)) descriptor = { + .hub = { + .bLength = USB_DT_HUB_NONVAR_SIZE + + ((USB_MAXCHILDREN + 1 + 7) / 8), + .bDescriptorType = USB_DT_HUB, + .bNbrPorts = 1, /* runtime modified */ + .wHubCharacteristics = 0, + .bPwrOn2PwrGood = 0, + .bHubContrCurrent = 0, + .u.hs.DeviceRemovable = {0xff}, + .u.hs.PortPwrCtrlMask = {} + }, + .device = { + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = __constant_cpu_to_le16(2), /* v2.0 */ + .bDeviceClass = USB_CLASS_HUB, + .bDeviceSubClass = 0, + .bDeviceProtocol = USB_HUB_PR_HS_NO_TT, + .bMaxPacketSize0 = 64, + .idVendor = 0x0000, + .idProduct = 0x0000, + .bcdDevice = 0x0000, + .iManufacturer = 1, + .iProduct = 2, + .iSerialNumber = 0, + .bNumConfigurations = 1 + }, + .config = { + .bLength = USB_DT_CONFIG_SIZE, + .bDescriptorType = USB_DT_CONFIG, + .wTotalLength = __constant_cpu_to_le16( + USB_DT_CONFIG_SIZE + + USB_DT_INTERFACE_SIZE + + USB_DT_ENDPOINT_SIZE), + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = 0 + }, + .interface = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HUB, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = 0 + }, + .endpoint = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 1 | USB_DIR_IN, /* 0x81 */ + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16( + (USB_MAXCHILDREN + 1 + 7) / 8), + .bInterval = 255 + }, +}; + +static char *language_string = "\x09\x04"; +static char *vendor_string = "u-boot"; +static char *product_string = "DWC2 root hub"; + +/* + * DWC2 to USB API interface + */ +static int dwc2_submit_rh_msg_in_status(struct dwc2 *dwc2, + struct usb_device *dev, void *buffer, + int txlen, struct devrequest *cmd) +{ + struct usb_port_status *portsts; + uint32_t hprt0 = 0; + uint32_t port_status = 0; + uint32_t port_change = 0; + int len = 0; + int speed; + + switch (cmd->requesttype & (USB_TYPE_MASK | USB_RECIP_MASK)) { + case USB_TYPE_STANDARD | USB_RECIP_DEVICE: + *(uint16_t *)buffer = cpu_to_le16(1); + len = 2; + break; + case USB_TYPE_STANDARD | USB_RECIP_INTERFACE: + case USB_TYPE_STANDARD | USB_RECIP_ENDPOINT: + *(uint16_t *)buffer = cpu_to_le16(0); + len = 2; + break; + case USB_RT_HUB: /* USB_TYPE_CLASS | USB_RECIP_DEVICE */ + *(uint32_t *)buffer = cpu_to_le32(0); + len = 4; + break; + case USB_RT_PORT: /* USB_TYPE_CLASS | USB_RECIP_OTHER */ + hprt0 = dwc2_readl(dwc2, HPRT0); + + if (hprt0 & HPRT0_CONNSTS) + port_status |= USB_PORT_STAT_CONNECTION; + if (hprt0 & HPRT0_ENA) + port_status |= USB_PORT_STAT_ENABLE; + if (hprt0 & HPRT0_SUSP) + port_status |= USB_PORT_STAT_SUSPEND; + if (hprt0 & HPRT0_OVRCURRACT) + port_status |= USB_PORT_STAT_OVERCURRENT; + if (hprt0 & HPRT0_RST) + port_status |= USB_PORT_STAT_RESET; + if (hprt0 & HPRT0_PWR) + port_status |= USB_PORT_STAT_POWER; + + speed = (hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT; + if (speed == HPRT0_SPD_HIGH_SPEED) + port_status |= USB_PORT_STAT_HIGH_SPEED; + else if (speed == HPRT0_SPD_LOW_SPEED) + port_status |= USB_PORT_STAT_LOW_SPEED; + + if (hprt0 & HPRT0_ENACHG) + port_change |= USB_PORT_STAT_C_ENABLE; + if (hprt0 & HPRT0_CONNDET) + port_change |= USB_PORT_STAT_C_CONNECTION; + if (hprt0 & HPRT0_OVRCURRCHG) + port_change |= USB_PORT_STAT_C_OVERCURRENT; + + portsts = buffer; + portsts->wPortStatus = cpu_to_le16(port_status); + portsts->wPortChange = cpu_to_le16(port_change); + len = sizeof(*portsts); + + break; + default: + goto unknown; + } + + dev->act_len = min(len, txlen); + dev->status = 0; + + return 0; + +unknown: + dev->act_len = 0; + dev->status = USB_ST_STALLED; + + return -1; +} + +static void strtodesc(char *dest, char *src, size_t n) +{ + unsigned int i; + + dest[0] = n; + dest[1] = 0x3; + for (i = 2; i < n && *src != '\0'; i += 2) { + dest[i] = *(src++); + dest[i + 1] = 0; + } +} + +/* Direction: In ; Request: Descriptor */ +static int dwc2_submit_rh_msg_in_descriptor(struct usb_device *dev, + void *buffer, int txlen, + struct devrequest *cmd) +{ + int len = 0; + char *src; + uint16_t wValue = le16_to_cpu(cmd->value); + uint16_t wLength = le16_to_cpu(cmd->length); + + switch (cmd->requesttype & (USB_TYPE_MASK | USB_RECIP_MASK)) { + case USB_TYPE_STANDARD | USB_RECIP_DEVICE: + switch (wValue >> 8) { + case USB_DT_DEVICE: + debug("USB_DT_DEVICE request\n"); + len = min3(txlen, (int)descriptor.device.bLength, (int)wLength); + memcpy(buffer, &descriptor.device, len); + break; + case USB_DT_CONFIG: + debug("USB_DT_CONFIG config\n"); + len = min3(txlen, (int)descriptor.config.wTotalLength, (int)wLength); + memcpy(buffer, &descriptor.config, len); + break; + case USB_DT_STRING: + debug("USB_DT_STRING: %#x\n", wValue); + switch (wValue & 0xff) { + case 0: /* Language */ + src = language_string; + len = strlen(src) + 2; + ((char *)buffer)[0] = len; + ((char *)buffer)[1] = 0x03; + memcpy(buffer + 2, src, strlen(src)); + break; + case 1: /* Vendor */ + src = vendor_string; + len = 2 * strlen(src) + 2; + strtodesc(buffer, src, len); + break; + case 2: /* Product */ + src = product_string; + len = 2 * strlen(src) + 2; + strtodesc(buffer, src, len); + break; + default: + debug("%s(): unknown string index 0x%x\n", __func__, wValue & 0xff); + goto unknown; + } + len = min3(txlen, len, (int)wLength); + break; + default: + debug("%s(): unknown requesttype: 0x%x\n", __func__, + cmd->requesttype & (USB_TYPE_MASK | USB_RECIP_MASK)); + goto unknown; + } + break; + + case USB_RT_HUB: /* USB_TYPE_CLASS | USB_RECIP_DEVICE */ + debug("USB_RT_HUB\n"); + + len = min3(txlen, (int)descriptor.hub.bLength, (int)wLength); + memcpy(buffer, &descriptor.hub, len); + break; + default: + goto unknown; + } + + dev->act_len = len; + dev->status = 0; + + return 0; + +unknown: + dev->act_len = 0; + dev->status = USB_ST_STALLED; + + return -1; +} + +/* Direction: In ; Request: Configuration */ +static int dwc2_submit_rh_msg_in_configuration(struct usb_device *dev, + void *buffer, int txlen, + struct devrequest *cmd) +{ + int len = 0; + + switch (cmd->requesttype & (USB_TYPE_MASK | USB_RECIP_MASK)) { + case USB_TYPE_STANDARD | USB_RECIP_DEVICE: + *(uint8_t *)buffer = 0x01; + len = min(txlen, 1); + break; + default: + goto unknown; + } + + dev->act_len = len; + dev->status = 0; + + return 0; + +unknown: + dev->act_len = 0; + dev->status = USB_ST_STALLED; + + return -1; +} + +/* Direction: In */ +static int dwc2_submit_rh_msg_in(struct dwc2 *dwc2, + struct usb_device *dev, void *buffer, + int txlen, struct devrequest *cmd) +{ + switch (cmd->request) { + case USB_REQ_GET_STATUS: + return dwc2_submit_rh_msg_in_status(dwc2, dev, buffer, + txlen, cmd); + case USB_REQ_GET_DESCRIPTOR: + return dwc2_submit_rh_msg_in_descriptor(dev, buffer, + txlen, cmd); + case USB_REQ_GET_CONFIGURATION: + return dwc2_submit_rh_msg_in_configuration(dev, buffer, + txlen, cmd); + default: + dev->act_len = 0; + dev->status = USB_ST_STALLED; + + return -1; + } +} + +/* Direction: Out */ +static int dwc2_submit_rh_msg_out(struct dwc2 *dwc2, + struct usb_device *dev, + void *buffer, int txlen, + struct devrequest *cmd) +{ + uint16_t wValue = le16_to_cpu(cmd->value); + uint32_t hprt0; + + switch (cmd->requesttype & (USB_TYPE_MASK | USB_RECIP_MASK)) { + case USB_TYPE_STANDARD | USB_RECIP_DEVICE: + switch (cmd->request) { + case USB_REQ_SET_ADDRESS: + dwc2_dbg(dwc2, "set root hub addr %d\n", wValue); + dwc2->root_hub_devnum = wValue; + break; + case USB_REQ_SET_CONFIGURATION: + break; + default: + goto unknown; + } + break; + case USB_TYPE_STANDARD | USB_RECIP_ENDPOINT: + case USB_RT_HUB: /* USB_TYPE_CLASS | USB_RECIP_DEVICE */ + switch (cmd->request) { + case USB_REQ_CLEAR_FEATURE: + break; + } + break; + case USB_RT_PORT: /* USB_TYPE_CLASS | USB_RECIP_OTHER */ + switch (cmd->request) { + case USB_REQ_CLEAR_FEATURE: + hprt0 = dwc2_readl(dwc2, HPRT0); + hprt0 &= ~(HPRT0_ENA | HPRT0_CONNDET + | HPRT0_ENACHG | HPRT0_OVRCURRCHG); + switch (wValue) { + case USB_PORT_FEAT_ENABLE: + hprt0 |= HPRT0_ENA; + break; + case USB_PORT_FEAT_SUSPEND: + break; + case USB_PORT_FEAT_POWER: + break; + case USB_PORT_FEAT_C_CONNECTION: + hprt0 |= HPRT0_CONNDET; + break; + case USB_PORT_FEAT_C_ENABLE: + hprt0 |= HPRT0_ENACHG; + break; + case USB_PORT_FEAT_C_OVER_CURRENT: + hprt0 |= HPRT0_OVRCURRCHG; + break; + default: + dwc2_dbg(dwc2, "unknown feature 0x%x\n", wValue); + goto unknown; + } + dwc2_writel(dwc2, hprt0, HPRT0); + break; + case USB_REQ_SET_FEATURE: + hprt0 = dwc2_readl(dwc2, HPRT0); + hprt0 &= ~(HPRT0_ENA | HPRT0_CONNDET + | HPRT0_ENACHG | HPRT0_OVRCURRCHG); + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + break; + case USB_PORT_FEAT_RESET: + hprt0 |= HPRT0_RST; + dwc2_writel(dwc2, hprt0, HPRT0); + + mdelay(60); + + hprt0 = dwc2_readl(dwc2, HPRT0); + hprt0 &= ~HPRT0_RST; + dwc2_writel(dwc2, hprt0, HPRT0); + break; + case USB_PORT_FEAT_POWER: + break; + case USB_PORT_FEAT_ENABLE: + /* Set by the core after a reset */ + break; + default: + dwc2_dbg(dwc2, "unknown feature 0x%x\n", wValue); + goto unknown; + } + break; + default: goto unknown; + } + break; + default: + goto unknown; + } + + dev->act_len = 0; + dev->status = 0; + + return 0; + +unknown: + dev->act_len = 0; + dev->status = USB_ST_STALLED; + return -1; +} + +int +dwc2_submit_rh_msg(struct dwc2 *dwc2, struct usb_device *dev, + unsigned long pipe, void *buffer, int txlen, + struct devrequest *cmd) +{ + int stat = 0; + + if (usb_pipeint(pipe)) { + dwc2_err(dwc2, "Root-Hub submit IRQ: NOT implemented\n"); + return 0; + } + + if (cmd->requesttype & USB_DIR_IN) + stat = dwc2_submit_rh_msg_in(dwc2, dev, buffer, txlen, cmd); + else + stat = dwc2_submit_rh_msg_out(dwc2, dev, buffer, txlen, cmd); + + mdelay(1); + return stat; +} -- 2.21.0.196.g041f5ea _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 24+ messages in thread
* [RFC PATCH 2/7] usb: dwc2: host: Handle dma mapping errors 2020-01-14 13:21 ` [RFC PATCH 0/7] usb: " Jules Maselbas 2020-01-14 13:21 ` [RFC PATCH 1/7] usb: dwc2: Add host controller driver Jules Maselbas @ 2020-01-14 13:21 ` Jules Maselbas 2020-01-14 13:21 ` [RFC PATCH 3/7] usb: dwc2: Dynamic fifo size support from Linux Jules Maselbas ` (5 subsequent siblings) 7 siblings, 0 replies; 24+ messages in thread From: Jules Maselbas @ 2020-01-14 13:21 UTC (permalink / raw) To: Sascha Hauer; +Cc: Barebox List, Jules Maselbas Signed-off-by: Jules Maselbas <jmaselbas@kalray.eu> --- drivers/usb/dwc2/host.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/dwc2/host.c b/drivers/usb/dwc2/host.c index 4137dd3cb..a1f2e3ea1 100644 --- a/drivers/usb/dwc2/host.c +++ b/drivers/usb/dwc2/host.c @@ -154,6 +154,11 @@ static int transfer_chunk(struct dwc2 *dwc2, u8 hc, dma_addr = dma_map_single(dwc2->dev, buffer, xfer_len, in ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (dma_mapping_error(dwc2->dev, dma_addr)) { + dwc2_err(dwc2, "Failed to map buffer@0x%p for dma\n", buffer); + return -EFAULT; + } + dwc2_dbg(dwc2, "chunk: pid=%d xfer_len=%u pkts=%u dma_addr=%llx\n", *pid, xfer_len, num_packets, dma_addr); -- 2.21.0.196.g041f5ea _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 24+ messages in thread
* [RFC PATCH 3/7] usb: dwc2: Dynamic fifo size support from Linux 2020-01-14 13:21 ` [RFC PATCH 0/7] usb: " Jules Maselbas 2020-01-14 13:21 ` [RFC PATCH 1/7] usb: dwc2: Add host controller driver Jules Maselbas 2020-01-14 13:21 ` [RFC PATCH 2/7] usb: dwc2: host: Handle dma mapping errors Jules Maselbas @ 2020-01-14 13:21 ` Jules Maselbas 2020-01-14 13:21 ` [RFC PATCH 4/7] usb: dwc2: Rework roothub interface Jules Maselbas ` (4 subsequent siblings) 7 siblings, 0 replies; 24+ messages in thread From: Jules Maselbas @ 2020-01-14 13:21 UTC (permalink / raw) To: Sascha Hauer; +Cc: Barebox List, Jules Maselbas Signed-off-by: Jules Maselbas <jmaselbas@kalray.eu> --- drivers/usb/dwc2/host.c | 146 ++++++++++++++++++++++++++++++++++------ drivers/usb/dwc2/regs.h | 8 --- 2 files changed, 124 insertions(+), 30 deletions(-) diff --git a/drivers/usb/dwc2/host.c b/drivers/usb/dwc2/host.c index a1f2e3ea1..84eb9b627 100644 --- a/drivers/usb/dwc2/host.c +++ b/drivers/usb/dwc2/host.c @@ -437,6 +437,129 @@ int dwc2_submit_int_msg(struct usb_device *udev, unsigned long pipe, } } +/* + * dwc2_calculate_dynamic_fifo() - Calculates the default fifo size + * For system that have a total fifo depth that is smaller than the default + * RX + TX fifo size. + * + * @dwc2: Programming view of DWC_otg controller + */ +static void dwc2_calculate_dynamic_fifo(struct dwc2 *dwc2) +{ + struct dwc2_core_params *params = &dwc2->params; + struct dwc2_hw_params *hw = &dwc2->hw_params; + u32 rxfsiz, nptxfsiz, ptxfsiz, total_fifo_size; + + total_fifo_size = hw->total_fifo_size; + rxfsiz = params->host_rx_fifo_size; + nptxfsiz = params->host_nperio_tx_fifo_size; + ptxfsiz = params->host_perio_tx_fifo_size; + + /* + * Will use Method 2 defined in the DWC2 spec: minimum FIFO depth + * allocation with support for high bandwidth endpoints. Synopsys + * defines MPS(Max Packet size) for a periodic EP=1024, and for + * non-periodic as 512. + */ + if (total_fifo_size < (rxfsiz + nptxfsiz + ptxfsiz)) { + /* + * For Buffer DMA mode/Scatter Gather DMA mode + * 2 * ((Largest Packet size / 4) + 1 + 1) + n + * with n = number of host channel. + * 2 * ((1024/4) + 2) = 516 + */ + rxfsiz = 516 + hw->host_channels; + + /* + * min non-periodic tx fifo depth + * 2 * (largest non-periodic USB packet used / 4) + * 2 * (512/4) = 256 + */ + nptxfsiz = 256; + + /* + * min periodic tx fifo depth + * (largest packet size*MC)/4 + * (1024 * 3)/4 = 768 + */ + ptxfsiz = 768; + } + + params->host_rx_fifo_size = rxfsiz; + params->host_nperio_tx_fifo_size = nptxfsiz; + params->host_perio_tx_fifo_size = ptxfsiz; + + /* + * If the summation of RX, NPTX and PTX fifo sizes is still + * bigger than the total_fifo_size, then we have a problem. + * + * We won't be able to allocate as many endpoints. Right now, + * we're just printing an error message, but ideally this FIFO + * allocation algorithm would be improved in the future. + * + * FIXME improve this FIFO allocation algorithm. + */ + if (unlikely(total_fifo_size < (rxfsiz + nptxfsiz + ptxfsiz))) + dwc2_err(dwc2, "invalid fifo sizes\n"); +} + +static void dwc2_config_fifos(struct dwc2 *dwc2) +{ + struct dwc2_core_params *params = &dwc2->params; + u32 nptxfsiz, hptxfsiz, dfifocfg, grxfsiz; + + if (!params->enable_dynamic_fifo) + return; + + dwc2_calculate_dynamic_fifo(dwc2); + + /* Rx FIFO */ + grxfsiz = dwc2_readl(dwc2, GRXFSIZ); + dwc2_dbg(dwc2, "initial grxfsiz=%08x\n", grxfsiz); + grxfsiz &= ~GRXFSIZ_DEPTH_MASK; + grxfsiz |= params->host_rx_fifo_size << + GRXFSIZ_DEPTH_SHIFT & GRXFSIZ_DEPTH_MASK; + dwc2_writel(dwc2, grxfsiz, GRXFSIZ); + dwc2_dbg(dwc2, "new grxfsiz=%08x\n", dwc2_readl(dwc2, GRXFSIZ)); + + /* Non-periodic Tx FIFO */ + dwc2_dbg(dwc2, "initial gnptxfsiz=%08x\n", dwc2_readl(dwc2, GNPTXFSIZ)); + nptxfsiz = params->host_nperio_tx_fifo_size << + FIFOSIZE_DEPTH_SHIFT & FIFOSIZE_DEPTH_MASK; + nptxfsiz |= params->host_rx_fifo_size << + FIFOSIZE_STARTADDR_SHIFT & FIFOSIZE_STARTADDR_MASK; + dwc2_writel(dwc2, nptxfsiz, GNPTXFSIZ); + dwc2_dbg(dwc2, "new gnptxfsiz=%08x\n", dwc2_readl(dwc2, GNPTXFSIZ)); + + /* Periodic Tx FIFO */ + dwc2_dbg(dwc2, "initial hptxfsiz=%08x\n", dwc2_readl(dwc2, HPTXFSIZ)); + hptxfsiz = params->host_perio_tx_fifo_size << + FIFOSIZE_DEPTH_SHIFT & FIFOSIZE_DEPTH_MASK; + hptxfsiz |= (params->host_rx_fifo_size + + params->host_nperio_tx_fifo_size) << + FIFOSIZE_STARTADDR_SHIFT & FIFOSIZE_STARTADDR_MASK; + dwc2_writel(dwc2, hptxfsiz, HPTXFSIZ); + dwc2_dbg(dwc2, "new hptxfsiz=%08x\n", dwc2_readl(dwc2, HPTXFSIZ)); + + if (dwc2->params.en_multiple_tx_fifo && + dwc2->hw_params.snpsid >= DWC2_CORE_REV_2_91a) { + /* + * This feature was implemented in 2.91a version + * Global DFIFOCFG calculation for Host mode - + * include RxFIFO, NPTXFIFO and HPTXFIFO + */ + dfifocfg = dwc2_readl(dwc2, GDFIFOCFG); + dfifocfg &= ~GDFIFOCFG_EPINFOBASE_MASK; + dfifocfg |= (params->host_rx_fifo_size + + params->host_nperio_tx_fifo_size + + params->host_perio_tx_fifo_size) << + GDFIFOCFG_EPINFOBASE_SHIFT & + GDFIFOCFG_EPINFOBASE_MASK; + dwc2_writel(dwc2, dfifocfg, GDFIFOCFG); + dwc2_dbg(dwc2, "new dfifocfg=%08x\n", dfifocfg); + } +} + /* * This function initializes the DWC2 controller registers for * host mode. @@ -452,8 +575,6 @@ int dwc2_submit_int_msg(struct usb_device *udev, unsigned long pipe, static void dwc2_core_host_init(struct device_d *dev, struct dwc2 *dwc2) { - uint32_t nptxfifosize = 0; - uint32_t ptxfifosize = 0; uint32_t hcchar, hcfg, hprt0, hotgctl, usbcfg; int i, ret, num_channels; @@ -501,26 +622,7 @@ static void dwc2_core_host_init(struct device_d *dev, } } - /* Configure data FIFO sizes */ - if (dwc2_readl(dwc2, GHWCFG2) & GHWCFG2_DYNAMIC_FIFO) { - /* Rx FIFO */ - dwc2_writel(dwc2, CONFIG_DWC2_HOST_RX_FIFO_SIZE, GRXFSIZ); - - /* Non-periodic Tx FIFO */ - nptxfifosize |= CONFIG_DWC2_HOST_NPERIO_TX_FIFO_SIZE << - FIFOSIZE_DEPTH_SHIFT; - nptxfifosize |= CONFIG_DWC2_HOST_RX_FIFO_SIZE << - FIFOSIZE_STARTADDR_SHIFT; - dwc2_writel(dwc2, nptxfifosize, GNPTXFSIZ); - - /* Periodic Tx FIFO */ - ptxfifosize |= CONFIG_DWC2_HOST_PERIO_TX_FIFO_SIZE << - FIFOSIZE_DEPTH_SHIFT; - ptxfifosize |= (CONFIG_DWC2_HOST_RX_FIFO_SIZE + - CONFIG_DWC2_HOST_NPERIO_TX_FIFO_SIZE) << - FIFOSIZE_STARTADDR_SHIFT; - dwc2_writel(dwc2, ptxfifosize, HPTXFSIZ); - } + dwc2_config_fifos(dwc2); /* Clear Host Set HNP Enable in the OTG Control Register */ hotgctl = dwc2_readl(dwc2, GOTGCTL); diff --git a/drivers/usb/dwc2/regs.h b/drivers/usb/dwc2/regs.h index b2b312e1b..0dee1813d 100644 --- a/drivers/usb/dwc2/regs.h +++ b/drivers/usb/dwc2/regs.h @@ -836,12 +836,4 @@ struct dwc2_dma_desc { #define DWC2_FS_IOT_ID 0x55310000 #define DWC2_HS_IOT_ID 0x55320000 -/* ==== u-boot ==== */ - -/* Default driver configuration */ -#define CONFIG_DWC2_MAX_CHANNELS DWC2_MAX_EPS_CHANNELS /* Max # of EPs */ -#define CONFIG_DWC2_HOST_RX_FIFO_SIZE (516 + CONFIG_DWC2_MAX_CHANNELS) -#define CONFIG_DWC2_HOST_NPERIO_TX_FIFO_SIZE 0x100 /* nPeriodic TX FIFO */ -#define CONFIG_DWC2_HOST_PERIO_TX_FIFO_SIZE 0x200 /* Periodic TX FIFO */ - #endif /* __DWC2_H__ */ -- 2.21.0.196.g041f5ea _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 24+ messages in thread
* [RFC PATCH 4/7] usb: dwc2: Rework roothub interface 2020-01-14 13:21 ` [RFC PATCH 0/7] usb: " Jules Maselbas ` (2 preceding siblings ...) 2020-01-14 13:21 ` [RFC PATCH 3/7] usb: dwc2: Dynamic fifo size support from Linux Jules Maselbas @ 2020-01-14 13:21 ` Jules Maselbas 2020-01-14 13:21 ` [RFC PATCH 5/7] HACK: usb: dwc2: Fix toggle reset Jules Maselbas ` (3 subsequent siblings) 7 siblings, 0 replies; 24+ messages in thread From: Jules Maselbas @ 2020-01-14 13:21 UTC (permalink / raw) To: Sascha Hauer; +Cc: Barebox List, Jules Maselbas Roothub requests are now decoded in one place. Signed-off-by: Jules Maselbas <jmaselbas@kalray.eu> --- drivers/usb/dwc2/dwc2.h | 2 +- drivers/usb/dwc2/host.c | 4 +- drivers/usb/dwc2/rhub.c | 520 +++++++++++++++++++--------------------- 3 files changed, 246 insertions(+), 280 deletions(-) diff --git a/drivers/usb/dwc2/dwc2.h b/drivers/usb/dwc2/dwc2.h index 0ac4b40fc..0a4323ab4 100644 --- a/drivers/usb/dwc2/dwc2.h +++ b/drivers/usb/dwc2/dwc2.h @@ -35,7 +35,7 @@ int dwc2_submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len, int interval); int dwc2_host_init(struct usb_host *host); -int dwc2_submit_rh_msg(struct dwc2 *dwc2, struct usb_device *dev, +int dwc2_submit_roothub(struct dwc2 *dwc2, struct usb_device *dev, unsigned long pipe, void *buf, int len, struct devrequest *setup); diff --git a/drivers/usb/dwc2/host.c b/drivers/usb/dwc2/host.c index 84eb9b627..2cde5977b 100644 --- a/drivers/usb/dwc2/host.c +++ b/drivers/usb/dwc2/host.c @@ -332,9 +332,9 @@ int dwc2_submit_control_msg(struct usb_device *udev, int status_direction; if (devnum == dwc2->root_hub_devnum) { - udev->status = 0; udev->speed = USB_SPEED_HIGH; - return dwc2_submit_rh_msg(dwc2, udev, pipe, buffer, len, setup); + ret = dwc2_submit_roothub(dwc2, udev, pipe, buffer, len, setup); + return ret; } /* SETUP stage */ diff --git a/drivers/usb/dwc2/rhub.c b/drivers/usb/dwc2/rhub.c index b94723d7b..cc733f34e 100644 --- a/drivers/usb/dwc2/rhub.c +++ b/drivers/usb/dwc2/rhub.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ #include "dwc2.h" static struct descriptor { @@ -6,7 +7,7 @@ static struct descriptor { struct usb_config_descriptor config; struct usb_interface_descriptor interface; struct usb_endpoint_descriptor endpoint; -} __attribute__ ((packed)) descriptor = { +} __packed descriptor = { .hub = { .bLength = USB_DT_HUB_NONVAR_SIZE + ((USB_MAXCHILDREN + 1 + 7) / 8), @@ -21,7 +22,7 @@ static struct descriptor { .device = { .bLength = USB_DT_DEVICE_SIZE, .bDescriptorType = USB_DT_DEVICE, - .bcdUSB = __constant_cpu_to_le16(2), /* v2.0 */ + .bcdUSB = cpu_to_le16(2), /* v2.0 */ .bDeviceClass = USB_CLASS_HUB, .bDeviceSubClass = 0, .bDeviceProtocol = USB_HUB_PR_HS_NO_TT, @@ -37,7 +38,7 @@ static struct descriptor { .config = { .bLength = USB_DT_CONFIG_SIZE, .bDescriptorType = USB_DT_CONFIG, - .wTotalLength = __constant_cpu_to_le16( + .wTotalLength = cpu_to_le16( USB_DT_CONFIG_SIZE + USB_DT_INTERFACE_SIZE + USB_DT_ENDPOINT_SIZE), @@ -63,356 +64,321 @@ static struct descriptor { .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 1 | USB_DIR_IN, /* 0x81 */ .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = __constant_cpu_to_le16( + .wMaxPacketSize = cpu_to_le16( (USB_MAXCHILDREN + 1 + 7) / 8), .bInterval = 255 }, }; -static char *language_string = "\x09\x04"; -static char *vendor_string = "u-boot"; -static char *product_string = "DWC2 root hub"; - -/* - * DWC2 to USB API interface - */ -static int dwc2_submit_rh_msg_in_status(struct dwc2 *dwc2, - struct usb_device *dev, void *buffer, - int txlen, struct devrequest *cmd) +static int dwc2_get_port_status(struct dwc2 *dwc2, struct usb_device *dev, + void *buf, int len) { struct usb_port_status *portsts; - uint32_t hprt0 = 0; - uint32_t port_status = 0; - uint32_t port_change = 0; - int len = 0; + uint32_t hprt0; + uint32_t status = 0; + uint32_t change = 0; int speed; - switch (cmd->requesttype & (USB_TYPE_MASK | USB_RECIP_MASK)) { - case USB_TYPE_STANDARD | USB_RECIP_DEVICE: - *(uint16_t *)buffer = cpu_to_le16(1); - len = 2; - break; - case USB_TYPE_STANDARD | USB_RECIP_INTERFACE: - case USB_TYPE_STANDARD | USB_RECIP_ENDPOINT: - *(uint16_t *)buffer = cpu_to_le16(0); - len = 2; - break; - case USB_RT_HUB: /* USB_TYPE_CLASS | USB_RECIP_DEVICE */ - *(uint32_t *)buffer = cpu_to_le32(0); - len = 4; - break; - case USB_RT_PORT: /* USB_TYPE_CLASS | USB_RECIP_OTHER */ - hprt0 = dwc2_readl(dwc2, HPRT0); + if (!buf || len < sizeof(*portsts)) + return -1; - if (hprt0 & HPRT0_CONNSTS) - port_status |= USB_PORT_STAT_CONNECTION; - if (hprt0 & HPRT0_ENA) - port_status |= USB_PORT_STAT_ENABLE; - if (hprt0 & HPRT0_SUSP) - port_status |= USB_PORT_STAT_SUSPEND; - if (hprt0 & HPRT0_OVRCURRACT) - port_status |= USB_PORT_STAT_OVERCURRENT; - if (hprt0 & HPRT0_RST) - port_status |= USB_PORT_STAT_RESET; - if (hprt0 & HPRT0_PWR) - port_status |= USB_PORT_STAT_POWER; - - speed = (hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT; - if (speed == HPRT0_SPD_HIGH_SPEED) - port_status |= USB_PORT_STAT_HIGH_SPEED; - else if (speed == HPRT0_SPD_LOW_SPEED) - port_status |= USB_PORT_STAT_LOW_SPEED; - - if (hprt0 & HPRT0_ENACHG) - port_change |= USB_PORT_STAT_C_ENABLE; - if (hprt0 & HPRT0_CONNDET) - port_change |= USB_PORT_STAT_C_CONNECTION; - if (hprt0 & HPRT0_OVRCURRCHG) - port_change |= USB_PORT_STAT_C_OVERCURRENT; - - portsts = buffer; - portsts->wPortStatus = cpu_to_le16(port_status); - portsts->wPortChange = cpu_to_le16(port_change); - len = sizeof(*portsts); + hprt0 = dwc2_readl(dwc2, HPRT0); + + if (hprt0 & HPRT0_CONNSTS) + status |= USB_PORT_STAT_CONNECTION; + if (hprt0 & HPRT0_ENA) + status |= USB_PORT_STAT_ENABLE; + if (hprt0 & HPRT0_SUSP) + status |= USB_PORT_STAT_SUSPEND; + if (hprt0 & HPRT0_OVRCURRACT) + status |= USB_PORT_STAT_OVERCURRENT; + if (hprt0 & HPRT0_RST) + status |= USB_PORT_STAT_RESET; + if (hprt0 & HPRT0_PWR) + status |= USB_PORT_STAT_POWER; + + speed = (hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT; + if (speed == HPRT0_SPD_HIGH_SPEED) + status |= USB_PORT_STAT_HIGH_SPEED; + else if (speed == HPRT0_SPD_LOW_SPEED) + status |= USB_PORT_STAT_LOW_SPEED; + + if (hprt0 & HPRT0_ENACHG) + change |= USB_PORT_STAT_C_ENABLE; + if (hprt0 & HPRT0_CONNDET) + change |= USB_PORT_STAT_C_CONNECTION; + if (hprt0 & HPRT0_OVRCURRCHG) + change |= USB_PORT_STAT_C_OVERCURRENT; + + portsts = buf; + portsts->wPortStatus = cpu_to_le16(status); + portsts->wPortChange = cpu_to_le16(change); + + dev->act_len = sizeof(*portsts); + dev->status = 0; - break; - default: - goto unknown; - } + return 0; +} - dev->act_len = min(len, txlen); - dev->status = 0; +static int dwc2_get_hub_status(struct dwc2 *dwc2, struct usb_device *dev, + void *buf, int len) +{ + if (!buf || len < 4) + return -1; + + *(uint32_t *)buf = 0; + dev->act_len = 4; + dev->status = 0; return 0; +} -unknown: - dev->act_len = 0; - dev->status = USB_ST_STALLED; +static int dwc2_get_hub_descriptor(struct dwc2 *dwc2, struct usb_device *dev, + void *buf, int len) +{ + if (!buf) + return -1; + + dev->act_len = min_t(int, len, descriptor.hub.bLength); + dev->status = 0; + memcpy(buf, &descriptor.hub, dev->act_len); - return -1; + return 0; } -static void strtodesc(char *dest, char *src, size_t n) +static void strle16(__le16 *dest, char *src, size_t n) { unsigned int i; - dest[0] = n; - dest[1] = 0x3; - for (i = 2; i < n && *src != '\0'; i += 2) { - dest[i] = *(src++); - dest[i + 1] = 0; - } + for (i = 0; i < n && *src != '\0'; i++, src++) + dest[i] = cpu_to_le16(*src); } -/* Direction: In ; Request: Descriptor */ -static int dwc2_submit_rh_msg_in_descriptor(struct usb_device *dev, - void *buffer, int txlen, - struct devrequest *cmd) +static int dwc2_get_string_descriptor(struct dwc2 *dwc2, struct usb_device *dev, + void *buf, int len, int index) { - int len = 0; - char *src; - uint16_t wValue = le16_to_cpu(cmd->value); - uint16_t wLength = le16_to_cpu(cmd->length); - - switch (cmd->requesttype & (USB_TYPE_MASK | USB_RECIP_MASK)) { - case USB_TYPE_STANDARD | USB_RECIP_DEVICE: - switch (wValue >> 8) { - case USB_DT_DEVICE: - debug("USB_DT_DEVICE request\n"); - len = min3(txlen, (int)descriptor.device.bLength, (int)wLength); - memcpy(buffer, &descriptor.device, len); - break; - case USB_DT_CONFIG: - debug("USB_DT_CONFIG config\n"); - len = min3(txlen, (int)descriptor.config.wTotalLength, (int)wLength); - memcpy(buffer, &descriptor.config, len); - break; - case USB_DT_STRING: - debug("USB_DT_STRING: %#x\n", wValue); - switch (wValue & 0xff) { - case 0: /* Language */ - src = language_string; - len = strlen(src) + 2; - ((char *)buffer)[0] = len; - ((char *)buffer)[1] = 0x03; - memcpy(buffer + 2, src, strlen(src)); - break; - case 1: /* Vendor */ - src = vendor_string; - len = 2 * strlen(src) + 2; - strtodesc(buffer, src, len); - break; - case 2: /* Product */ - src = product_string; - len = 2 * strlen(src) + 2; - strtodesc(buffer, src, len); - break; - default: - debug("%s(): unknown string index 0x%x\n", __func__, wValue & 0xff); - goto unknown; - } - len = min3(txlen, len, (int)wLength); - break; - default: - debug("%s(): unknown requesttype: 0x%x\n", __func__, - cmd->requesttype & (USB_TYPE_MASK | USB_RECIP_MASK)); - goto unknown; - } - break; + char *src, *str = buf; + __le16 *le16 = (__le16 *)(str + 2); + int size; + + if (!buf || len < 2) + return -1; - case USB_RT_HUB: /* USB_TYPE_CLASS | USB_RECIP_DEVICE */ - debug("USB_RT_HUB\n"); + switch (index) { + case 0: /* Language */ + src = "\x09\x04"; + size = strlen(src) + 2; + len = min_t(int, len, size); - len = min3(txlen, (int)descriptor.hub.bLength, (int)wLength); - memcpy(buffer, &descriptor.hub, len); + str[0] = size; + str[1] = 0x03; + memcpy(str + 2, src, len - 2); + break; + case 1: /* Vendor */ + src = "u-boot"; + size = 2 * strlen(src) + 2; + len = min_t(int, len, size); + + str[0] = size; + str[1] = 0x03; + strle16(le16, src, (len - 2) / 2); + break; + case 2: /* Product */ + src = "DWC2 root hub"; + size = 2 * strlen(src) + 2; + len = min_t(int, len, size); + + str[0] = size; + str[1] = 0x03; + strle16(le16, src, (len - 2) / 2); break; default: - goto unknown; + dwc2_err(dwc2, "roothub: unknown string descriptor: 0x%x\n", + index); + return -1; } dev->act_len = len; dev->status = 0; return 0; - -unknown: - dev->act_len = 0; - dev->status = USB_ST_STALLED; - - return -1; } -/* Direction: In ; Request: Configuration */ -static int dwc2_submit_rh_msg_in_configuration(struct usb_device *dev, - void *buffer, int txlen, - struct devrequest *cmd) +static int dwc2_get_descriptor(struct dwc2 *dwc2, struct usb_device *dev, + void *buf, int len, int value) { - int len = 0; + int index = value >> 8; + + if (!buf || len < 0) + return -1; - switch (cmd->requesttype & (USB_TYPE_MASK | USB_RECIP_MASK)) { - case USB_TYPE_STANDARD | USB_RECIP_DEVICE: - *(uint8_t *)buffer = 0x01; - len = min(txlen, 1); + switch (index) { + case USB_DT_DEVICE: + len = min(len, (int)descriptor.device.bLength); + memcpy(buf, &descriptor.device, len); break; + case USB_DT_CONFIG: + len = min(len, (int)descriptor.config.wTotalLength); + memcpy(buf, &descriptor.config, len); + break; + case USB_DT_STRING: + value &= 0xff; + return dwc2_get_string_descriptor(dwc2, dev, buf, len, value); default: - goto unknown; + dwc2_err(dwc2, "roothub: unknown descriptor: 0x%x\n", index); + return -1; } dev->act_len = len; dev->status = 0; return 0; - -unknown: - dev->act_len = 0; - dev->status = USB_ST_STALLED; - - return -1; } -/* Direction: In */ -static int dwc2_submit_rh_msg_in(struct dwc2 *dwc2, - struct usb_device *dev, void *buffer, - int txlen, struct devrequest *cmd) +static int dwc2_set_port_feature(struct dwc2 *dwc2, struct usb_device *dev, + int feature) { - switch (cmd->request) { - case USB_REQ_GET_STATUS: - return dwc2_submit_rh_msg_in_status(dwc2, dev, buffer, - txlen, cmd); - case USB_REQ_GET_DESCRIPTOR: - return dwc2_submit_rh_msg_in_descriptor(dev, buffer, - txlen, cmd); - case USB_REQ_GET_CONFIGURATION: - return dwc2_submit_rh_msg_in_configuration(dev, buffer, - txlen, cmd); - default: - dev->act_len = 0; - dev->status = USB_ST_STALLED; + uint32_t hprt0; + + hprt0 = dwc2_readl(dwc2, HPRT0); + hprt0 &= ~(HPRT0_ENA | HPRT0_CONNDET | HPRT0_ENACHG | HPRT0_OVRCURRCHG); + + switch (feature) { + case USB_PORT_FEAT_SUSPEND: + break; + case USB_PORT_FEAT_RESET: + hprt0 |= HPRT0_RST; + dwc2_writel(dwc2, hprt0, HPRT0); + + mdelay(60); + hprt0 = dwc2_readl(dwc2, HPRT0); + hprt0 &= ~HPRT0_RST; + dwc2_writel(dwc2, hprt0, HPRT0); + break; + case USB_PORT_FEAT_POWER: + break; + case USB_PORT_FEAT_ENABLE: + /* Set by the core after a reset */ + break; + default: + dwc2_dbg(dwc2, "roothub: unsupported set port feature 0x%x\n", + feature); return -1; } + + dev->act_len = 0; + dev->status = 0; + + return 0; } -/* Direction: Out */ -static int dwc2_submit_rh_msg_out(struct dwc2 *dwc2, - struct usb_device *dev, - void *buffer, int txlen, - struct devrequest *cmd) +static int dwc2_clear_port_feature(struct dwc2 *dwc2, struct usb_device *dev, + int feature) { - uint16_t wValue = le16_to_cpu(cmd->value); uint32_t hprt0; - switch (cmd->requesttype & (USB_TYPE_MASK | USB_RECIP_MASK)) { - case USB_TYPE_STANDARD | USB_RECIP_DEVICE: - switch (cmd->request) { - case USB_REQ_SET_ADDRESS: - dwc2_dbg(dwc2, "set root hub addr %d\n", wValue); - dwc2->root_hub_devnum = wValue; - break; - case USB_REQ_SET_CONFIGURATION: - break; - default: - goto unknown; - } + hprt0 = dwc2_readl(dwc2, HPRT0); + hprt0 &= ~(HPRT0_ENA | HPRT0_CONNDET | HPRT0_ENACHG | HPRT0_OVRCURRCHG); + + switch (feature) { + case USB_PORT_FEAT_ENABLE: + hprt0 |= HPRT0_ENA; + break; + case USB_PORT_FEAT_SUSPEND: + break; + case USB_PORT_FEAT_POWER: break; - case USB_TYPE_STANDARD | USB_RECIP_ENDPOINT: - case USB_RT_HUB: /* USB_TYPE_CLASS | USB_RECIP_DEVICE */ - switch (cmd->request) { - case USB_REQ_CLEAR_FEATURE: - break; - } + case USB_PORT_FEAT_C_CONNECTION: + hprt0 |= HPRT0_CONNDET; break; - case USB_RT_PORT: /* USB_TYPE_CLASS | USB_RECIP_OTHER */ - switch (cmd->request) { - case USB_REQ_CLEAR_FEATURE: - hprt0 = dwc2_readl(dwc2, HPRT0); - hprt0 &= ~(HPRT0_ENA | HPRT0_CONNDET - | HPRT0_ENACHG | HPRT0_OVRCURRCHG); - switch (wValue) { - case USB_PORT_FEAT_ENABLE: - hprt0 |= HPRT0_ENA; - break; - case USB_PORT_FEAT_SUSPEND: - break; - case USB_PORT_FEAT_POWER: - break; - case USB_PORT_FEAT_C_CONNECTION: - hprt0 |= HPRT0_CONNDET; - break; - case USB_PORT_FEAT_C_ENABLE: - hprt0 |= HPRT0_ENACHG; - break; - case USB_PORT_FEAT_C_OVER_CURRENT: - hprt0 |= HPRT0_OVRCURRCHG; - break; - default: - dwc2_dbg(dwc2, "unknown feature 0x%x\n", wValue); - goto unknown; - } - dwc2_writel(dwc2, hprt0, HPRT0); - break; - case USB_REQ_SET_FEATURE: - hprt0 = dwc2_readl(dwc2, HPRT0); - hprt0 &= ~(HPRT0_ENA | HPRT0_CONNDET - | HPRT0_ENACHG | HPRT0_OVRCURRCHG); - switch (wValue) { - case USB_PORT_FEAT_SUSPEND: - break; - case USB_PORT_FEAT_RESET: - hprt0 |= HPRT0_RST; - dwc2_writel(dwc2, hprt0, HPRT0); - - mdelay(60); - - hprt0 = dwc2_readl(dwc2, HPRT0); - hprt0 &= ~HPRT0_RST; - dwc2_writel(dwc2, hprt0, HPRT0); - break; - case USB_PORT_FEAT_POWER: - break; - case USB_PORT_FEAT_ENABLE: - /* Set by the core after a reset */ - break; - default: - dwc2_dbg(dwc2, "unknown feature 0x%x\n", wValue); - goto unknown; - } - break; - default: goto unknown; - } + case USB_PORT_FEAT_C_ENABLE: + hprt0 |= HPRT0_ENACHG; + break; + case USB_PORT_FEAT_C_OVER_CURRENT: + hprt0 |= HPRT0_OVRCURRCHG; break; default: - goto unknown; + dwc2_dbg(dwc2, "roothub: unsupported clear port feature 0x%x\n", + feature); + return -1; } + dwc2_writel(dwc2, hprt0, HPRT0); + dev->act_len = 0; dev->status = 0; return 0; +} + +static int dwc2_set_address(struct dwc2 *dwc2, struct usb_device *dev, int addr) +{ + dwc2_dbg(dwc2, "roothub: set address to %d\n", addr); + dwc2->root_hub_devnum = addr; -unknown: dev->act_len = 0; - dev->status = USB_ST_STALLED; - return -1; + dev->status = 0; + + return 0; } -int -dwc2_submit_rh_msg(struct dwc2 *dwc2, struct usb_device *dev, - unsigned long pipe, void *buffer, int txlen, - struct devrequest *cmd) +int dwc2_submit_roothub(struct dwc2 *dwc2, struct usb_device *dev, + unsigned long pipe, void *buf, int len, + struct devrequest *setup) { - int stat = 0; + unsigned char reqtype = setup->requesttype; + unsigned char request = setup->request; + unsigned short value = le16_to_cpu(setup->value); + unsigned short size = le16_to_cpu(setup->length); + int minlen = min_t(int, len, size); if (usb_pipeint(pipe)) { - dwc2_err(dwc2, "Root-Hub submit IRQ: NOT implemented\n"); + dwc2_err(dwc2, "roothub: submit IRQ NOT implemented\n"); return 0; } - if (cmd->requesttype & USB_DIR_IN) - stat = dwc2_submit_rh_msg_in(dwc2, dev, buffer, txlen, cmd); - else - stat = dwc2_submit_rh_msg_out(dwc2, dev, buffer, txlen, cmd); + dev->act_len = 0; + dev->status = USB_ST_STALLED; + +#define REQ(l, u) ((l) | ((u) << 8)) + + switch (REQ(request, reqtype)) { + case REQ(USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB): + return dwc2_get_hub_descriptor(dwc2, dev, buf, minlen); + + case REQ(USB_REQ_GET_DESCRIPTOR, USB_DIR_IN): + return dwc2_get_descriptor(dwc2, dev, buf, minlen, value); + + case REQ(USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB): + return dwc2_get_hub_status(dwc2, dev, buf, len); + + case REQ(USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT): + return dwc2_get_port_status(dwc2, dev, buf, len); - mdelay(1); - return stat; + case REQ(USB_REQ_SET_FEATURE, USB_DIR_OUT | USB_RT_PORT): + return dwc2_set_port_feature(dwc2, dev, value); + + case REQ(USB_REQ_CLEAR_FEATURE, USB_DIR_OUT | USB_RT_PORT): + return dwc2_clear_port_feature(dwc2, dev, value); + + case REQ(USB_REQ_SET_ADDRESS, USB_DIR_OUT): + return dwc2_set_address(dwc2, dev, value); + + case REQ(USB_REQ_SET_CONFIGURATION, USB_DIR_OUT): + dev->act_len = 0; + dev->status = 0; + return 0; + + case REQ(USB_REQ_GET_CONFIGURATION, USB_DIR_IN): + *(char *)buf = 1; + dev->act_len = 1; + dev->status = 0; + return 0; + } + + dwc2_err(dwc2, "roothub: unsupported request 0x%x requesttype 0x%x\n", + request, reqtype); + + return 0; } -- 2.21.0.196.g041f5ea _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 24+ messages in thread
* [RFC PATCH 5/7] HACK: usb: dwc2: Fix toggle reset 2020-01-14 13:21 ` [RFC PATCH 0/7] usb: " Jules Maselbas ` (3 preceding siblings ...) 2020-01-14 13:21 ` [RFC PATCH 4/7] usb: dwc2: Rework roothub interface Jules Maselbas @ 2020-01-14 13:21 ` Jules Maselbas 2020-01-14 13:21 ` [RFC PATCH 6/7] usb: dwc2: Rewrite dwc2_hc_init Jules Maselbas ` (2 subsequent siblings) 7 siblings, 0 replies; 24+ messages in thread From: Jules Maselbas @ 2020-01-14 13:21 UTC (permalink / raw) To: Sascha Hauer; +Cc: Barebox List, Jules Maselbas From USB 2.0 specification, section 9.4.5: ClearFeature(ENDPOINT_HALT) request always results in the data toggle being reinitialized to DATA0. Signed-off-by: Jules Maselbas <jmaselbas@kalray.eu> --- drivers/usb/dwc2/host.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/usb/dwc2/host.c b/drivers/usb/dwc2/host.c index 2cde5977b..a97c7dac5 100644 --- a/drivers/usb/dwc2/host.c +++ b/drivers/usb/dwc2/host.c @@ -373,7 +373,24 @@ int dwc2_submit_control_msg(struct usb_device *udev, if (ret) return ret; + if (setup->requesttype == USB_RECIP_ENDPOINT + && setup->request == USB_REQ_CLEAR_FEATURE) { + /* From USB 2.0, section 9.4.5: + * ClearFeature(ENDPOINT_HALT) request always results + * in the data toggle being reinitialized to DATA0. + */ + int ep = le16_to_cpu(setup->index & 0x7f); + int in = le16_to_cpu(setup->index & 0x80); + int data0 = TSIZ_SC_MC_PID_DATA0; + + if (in) + dwc2->in_data_toggle[devnum][ep] = data0; + else + dwc2->out_data_toggle[devnum][ep] = data0; + } + udev->act_len = act_len; + udev->status = 0; return 0; } -- 2.21.0.196.g041f5ea _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 24+ messages in thread
* [RFC PATCH 6/7] usb: dwc2: Rewrite dwc2_hc_init 2020-01-14 13:21 ` [RFC PATCH 0/7] usb: " Jules Maselbas ` (4 preceding siblings ...) 2020-01-14 13:21 ` [RFC PATCH 5/7] HACK: usb: dwc2: Fix toggle reset Jules Maselbas @ 2020-01-14 13:21 ` Jules Maselbas 2020-01-14 13:21 ` [RFC PATCH 7/7] usb: dwc2: Read dr_mode from device tree Jules Maselbas 2020-01-14 14:27 ` [RFC PATCH 0/7] usb: dwc2 host driver Sascha Hauer 7 siblings, 0 replies; 24+ messages in thread From: Jules Maselbas @ 2020-01-14 13:21 UTC (permalink / raw) To: Sascha Hauer; +Cc: Barebox List, Jules Maselbas Removed the uses of a table to convert the usb endpoint type for the controller. Signed-off-by: Jules Maselbas <jmaselbas@kalray.eu> --- drivers/usb/dwc2/host.c | 47 +++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/drivers/usb/dwc2/host.c b/drivers/usb/dwc2/host.c index a97c7dac5..083c964e8 100644 --- a/drivers/usb/dwc2/host.c +++ b/drivers/usb/dwc2/host.c @@ -7,13 +7,6 @@ /* Use only HC channel 0. */ #define DWC2_HC_CHANNEL 0 -static int dwc2_eptype[] = { - DXEPCTL_EPTYPE_ISO, - DXEPCTL_EPTYPE_INTERRUPT, - DXEPCTL_EPTYPE_CONTROL, - DXEPCTL_EPTYPE_BULK, -}; - static int dwc2_do_split(struct dwc2 *dwc2, struct usb_device *dev) { uint32_t hprt0 = dwc2_readl(dwc2, HPRT0); @@ -80,15 +73,32 @@ static void dwc2_hc_enable_ints(struct dwc2 *dwc2, uint8_t hc) * @param regs Programming view of DWC2 controller * @param hc Information needed to initialize the host channel */ -static void dwc2_hc_init(struct dwc2 *dwc2, uint8_t hc, - struct usb_device *dev, uint8_t dev_addr, uint8_t ep_num, - uint8_t ep_is_in, uint32_t ep_type, uint16_t max_packet) +static void dwc2_hc_init(struct dwc2 *dwc2, struct usb_device *dev, u8 hc, + unsigned long pipe, int is_in) { - uint32_t hcchar = (dev_addr << HCCHAR_DEVADDR_SHIFT) | - (ep_num << HCCHAR_EPNUM_SHIFT) | - (ep_is_in ? HCCHAR_EPDIR : 0) | - ep_type | - (max_packet << HCCHAR_MPS_SHIFT); + int addr = usb_pipedevice(pipe); + int endp = usb_pipeendpoint(pipe); + int type = usb_pipetype(pipe); + int mps = usb_maxpacket(dev, pipe); + uint32_t hcchar = (addr << HCCHAR_DEVADDR_SHIFT) | + (endp << HCCHAR_EPNUM_SHIFT) | + (is_in ? HCCHAR_EPDIR : 0) | + (mps << HCCHAR_MPS_SHIFT); + + switch (type) { + case PIPE_ISOCHRONOUS: + hcchar |= DXEPCTL_EPTYPE_ISO; + break; + case PIPE_INTERRUPT: + hcchar |= DXEPCTL_EPTYPE_INTERRUPT; + break; + case PIPE_CONTROL: + hcchar |= DXEPCTL_EPTYPE_CONTROL; + break; + case PIPE_BULK: + hcchar |= DXEPCTL_EPTYPE_BULK; + break; + } if (dev->speed == USB_SPEED_LOW) hcchar |= HCCHAR_LSPDDEV; @@ -207,10 +217,7 @@ static int dwc2_submit_packet(struct dwc2 *dwc2, struct usb_device *dev, unsigned long pipe, u8 *pid, int in, void *buf, int len) { - int devnum = usb_pipedevice(pipe); - int ep = usb_pipeendpoint(pipe); int mps = usb_maxpacket(dev, pipe); - int eptype = dwc2_eptype[usb_pipetype(pipe)]; int do_split = dwc2_do_split(dwc2, dev); int complete_split = 0; int done = 0; @@ -231,7 +238,7 @@ static int dwc2_submit_packet(struct dwc2 *dwc2, struct usb_device *dev, max_xfer_len = num_packets * mps; /* Initialize channel */ - dwc2_hc_init(dwc2, DWC2_HC_CHANNEL, dev, devnum, ep, in, eptype, mps); + dwc2_hc_init(dwc2, dev, DWC2_HC_CHANNEL, pipe, in); /* Check if the target is a FS/LS device behind a HS hub */ if (do_split) { @@ -262,7 +269,7 @@ static int dwc2_submit_packet(struct dwc2 *dwc2, struct usb_device *dev, dwc2_writel(dwc2, hcsplt, HCSPLT(DWC2_HC_CHANNEL)); } - if (eptype == DXEPCTL_EPTYPE_INTERRUPT) { + if (usb_pipeint(pipe)) { int uframe_num = dwc2_readl(dwc2, HFNUM); if (!(uframe_num & 0x1)) -- 2.21.0.196.g041f5ea _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 24+ messages in thread
* [RFC PATCH 7/7] usb: dwc2: Read dr_mode from device tree 2020-01-14 13:21 ` [RFC PATCH 0/7] usb: " Jules Maselbas ` (5 preceding siblings ...) 2020-01-14 13:21 ` [RFC PATCH 6/7] usb: dwc2: Rewrite dwc2_hc_init Jules Maselbas @ 2020-01-14 13:21 ` Jules Maselbas 2020-01-14 14:27 ` [RFC PATCH 0/7] usb: dwc2 host driver Sascha Hauer 7 siblings, 0 replies; 24+ messages in thread From: Jules Maselbas @ 2020-01-14 13:21 UTC (permalink / raw) To: Sascha Hauer; +Cc: Barebox List, Jules Maselbas Signed-off-by: Jules Maselbas <jmaselbas@kalray.eu> --- drivers/usb/dwc2/core.c | 89 +++++++++++++++++++++++++++++++++++++++++ drivers/usb/dwc2/dwc2.c | 2 + drivers/usb/dwc2/dwc2.h | 1 + 3 files changed, 92 insertions(+) diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 9a36b0904..94ff977bb 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -1,6 +1,33 @@ // SPDX-License-Identifier: GPL-2.0+ #include "dwc2.h" +/* Returns the controller's GHWCFG2.OTG_MODE. */ +static unsigned int dwc2_op_mode(struct dwc2 *dwc2) +{ + u32 ghwcfg2 = dwc2_readl(dwc2, GHWCFG2); + + return (ghwcfg2 & GHWCFG2_OP_MODE_MASK) >> + GHWCFG2_OP_MODE_SHIFT; +} + +/* Returns true if the controller is host-only. */ +static bool dwc2_hw_is_host(struct dwc2 *dwc2) +{ + unsigned int op_mode = dwc2_op_mode(dwc2); + + return (op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_HOST) || + (op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST); +} + +/* Returns true if the controller is device-only. */ +static bool dwc2_hw_is_device(struct dwc2 *dwc2) +{ + unsigned int op_mode = dwc2_op_mode(dwc2); + + return (op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE) || + (op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE); +} + void dwc2_set_param_otg_cap(struct dwc2 *dwc2) { u8 val; @@ -493,6 +520,68 @@ void dwc2_gusbcfg_init(struct dwc2 *dwc2) dwc2_writel(dwc2, usbcfg, GUSBCFG); } +/* + * Check the dr_mode against the module configuration and hardware + * capabilities. + * + * The hardware, module, and dr_mode, can each be set to host, device, + * or otg. Check that all these values are compatible and adjust the + * value of dr_mode if possible. + * + * actual + * HW MOD dr_mode dr_mode + * ------------------------------ + * HST HST any : HST + * HST DEV any : --- + * HST OTG any : HST + * + * DEV HST any : --- + * DEV DEV any : DEV + * DEV OTG any : DEV + * + * OTG HST any : HST + * OTG DEV any : DEV + * OTG OTG any : dr_mode + */ +int dwc2_get_dr_mode(struct dwc2 *dwc2) +{ + enum usb_dr_mode mode; + + mode = of_usb_get_dr_mode(dwc2->dev->device_node, NULL); + dwc2->dr_mode = mode; + + if (dwc2_hw_is_device(dwc2)) { + if (IS_ENABLED(CONFIG_USB_DWC2_HOST)) { + dwc2_err(dwc2, + "Controller does not support host mode.\n"); + return -EINVAL; + } + mode = USB_DR_MODE_PERIPHERAL; + } else if (dwc2_hw_is_host(dwc2)) { + if (IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL)) { + dwc2_err(dwc2, + "Controller does not support device mode.\n"); + return -EINVAL; + } + mode = USB_DR_MODE_HOST; + } else { + if (IS_ENABLED(CONFIG_USB_DWC2_HOST)) + mode = USB_DR_MODE_HOST; + else if (IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL)) + mode = USB_DR_MODE_PERIPHERAL; + } + + if (mode != dwc2->dr_mode) { + dwc2_warn(dwc2, + "Configuration mismatch. dr_mode forced to %s\n", + mode == USB_DR_MODE_HOST ? "host" : "device"); + + dwc2->dr_mode = mode; + } + + return 0; +} + /* * Do core a soft reset of the core. Be careful with this because it * resets all the internal state machines of the core. diff --git a/drivers/usb/dwc2/dwc2.c b/drivers/usb/dwc2/dwc2.c index 893f573bc..1df393e77 100644 --- a/drivers/usb/dwc2/dwc2.c +++ b/drivers/usb/dwc2/dwc2.c @@ -62,6 +62,8 @@ static int dwc2_probe(struct device_d *dev) /* Detect config values from hardware */ dwc2_get_hwparams(dwc2); + dwc2_get_dr_mode(dwc2); + dwc2_set_default_params(dwc2); dma_set_mask(dev, DMA_BIT_MASK(32)); diff --git a/drivers/usb/dwc2/dwc2.h b/drivers/usb/dwc2/dwc2.h index 0a4323ab4..35ba00660 100644 --- a/drivers/usb/dwc2/dwc2.h +++ b/drivers/usb/dwc2/dwc2.h @@ -21,6 +21,7 @@ void dwc2_flush_all_fifo(struct dwc2 *dwc2); int dwc2_phy_init(struct dwc2 *dwc2, bool select_phy); int dwc2_gahbcfg_init(struct dwc2 *dwc2); void dwc2_gusbcfg_init(struct dwc2 *dwc2); +int dwc2_get_dr_mode(struct dwc2 *dwc2); int dwc2_core_reset(struct dwc2 *dwc2); void dwc2_core_init(struct dwc2 *dwc2); -- 2.21.0.196.g041f5ea _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC PATCH 0/7] usb: dwc2 host driver 2020-01-14 13:21 ` [RFC PATCH 0/7] usb: " Jules Maselbas ` (6 preceding siblings ...) 2020-01-14 13:21 ` [RFC PATCH 7/7] usb: dwc2: Read dr_mode from device tree Jules Maselbas @ 2020-01-14 14:27 ` Sascha Hauer 2020-01-14 17:12 ` Jules Maselbas 7 siblings, 1 reply; 24+ messages in thread From: Sascha Hauer @ 2020-01-14 14:27 UTC (permalink / raw) To: Jules Maselbas; +Cc: Barebox List Hi Jules, On Tue, Jan 14, 2020 at 02:21:05PM +0100, Jules Maselbas wrote: > Hi Sascha, > > I've been working on a driver for the dwc2 otg controller, for both host > and device mode. Like you I've started from U-Boot driver and I mixed > it with some part from Linux. For instance I've removed the register > structs and I've been using the same defines for register bit-fields as > in Linux. > > I would like to share my version of the host driver, as the gadget driver > one still requires some cleanup. This series is not to be applied on the > driver you proposed. However I am willing to propose a new series that can > be applied on the driver you proposed. What do you think? The dwc2 driver was a quick shot, I am all in for improvements. Removing the register structs is very nice and I would be interested in gadget support as well. What SoC are you using the driver on? I am not sure if it makes sense to rebase your patches on my driver. If you can convince me that yours is better then we could just remove mine and merge yours instead. Sascha -- Pengutronix e.K. | | Steuerwalder Str. 21 | http://www.pengutronix.de/ | 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 | _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC PATCH 0/7] usb: dwc2 host driver 2020-01-14 14:27 ` [RFC PATCH 0/7] usb: dwc2 host driver Sascha Hauer @ 2020-01-14 17:12 ` Jules Maselbas 2020-01-16 6:50 ` Sascha Hauer 0 siblings, 1 reply; 24+ messages in thread From: Jules Maselbas @ 2020-01-14 17:12 UTC (permalink / raw) To: Sascha Hauer; +Cc: Barebox List On Tue, Jan 14, 2020 at 03:27:04PM +0100, Sascha Hauer wrote: > Hi Jules, > > On Tue, Jan 14, 2020 at 02:21:05PM +0100, Jules Maselbas wrote: > > Hi Sascha, > > > > I've been working on a driver for the dwc2 otg controller, for both host > > and device mode. Like you I've started from U-Boot driver and I mixed > > it with some part from Linux. For instance I've removed the register > > structs and I've been using the same defines for register bit-fields as > > in Linux. > > > > I would like to share my version of the host driver, as the gadget driver > > one still requires some cleanup. This series is not to be applied on the > > driver you proposed. However I am willing to propose a new series that can > > be applied on the driver you proposed. What do you think? > > The dwc2 driver was a quick shot, I am all in for improvements. Removing > the register structs is very nice and I would be interested in gadget > support as well. The gadget support is almost ready, I got the dfu gadget working. :) > What SoC are you using the driver on? I am using this driver on our custom SoC, the Kalray Coolidge MPPA (k1c). I didn't tried on other SoC. > I am not sure if it makes sense to rebase your patches on my driver. If > you can convince me that yours is better then we could just remove mine > and merge yours instead. I don't have a strong argument in favor of my version. Except that I have already made some changes over the original U-Boot version: I've tried to improve the roothub interface making it easier to decode new requests. I have removed the memcpy done in transfer_chunk between the usb data buffer and the dma buffer :/ However this mean that all buffer used for usb tranfert must be dma capable. Not all usb transfers use dma_alloc for it's buffers but sometime they are allocated on the stack, this can be problematic if the stack cannot be used by DMA. I also have fixed an error in the handling of the toggle bit, but I think the solution is a bit hacky. I think the driver should rather use the toggle bit stored in the usb_device structure, but this will require more rework. If theses changes make sense then let's use this version. Best, Jules > Sascha > > -- > Pengutronix e.K. | | > Steuerwalder Str. 21 | http://www.pengutronix.de/ | > 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | > Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 | _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC PATCH 0/7] usb: dwc2 host driver 2020-01-14 17:12 ` Jules Maselbas @ 2020-01-16 6:50 ` Sascha Hauer 2020-01-16 17:25 ` Jules Maselbas 0 siblings, 1 reply; 24+ messages in thread From: Sascha Hauer @ 2020-01-16 6:50 UTC (permalink / raw) To: Matthias Mann; +Cc: Barebox List On Tue, Jan 14, 2020 at 06:12:26PM +0100, Jules Maselbas wrote: > On Tue, Jan 14, 2020 at 03:27:04PM +0100, Sascha Hauer wrote: > > Hi Jules, > > > > On Tue, Jan 14, 2020 at 02:21:05PM +0100, Jules Maselbas wrote: > > > Hi Sascha, > > > > > > I've been working on a driver for the dwc2 otg controller, for both host > > > and device mode. Like you I've started from U-Boot driver and I mixed > > > it with some part from Linux. For instance I've removed the register > > > structs and I've been using the same defines for register bit-fields as > > > in Linux. > > > > > > I would like to share my version of the host driver, as the gadget driver > > > one still requires some cleanup. This series is not to be applied on the > > > driver you proposed. However I am willing to propose a new series that can > > > be applied on the driver you proposed. What do you think? > > > > The dwc2 driver was a quick shot, I am all in for improvements. Removing > > the register structs is very nice and I would be interested in gadget > > support as well. > The gadget support is almost ready, I got the dfu gadget working. :) Well, USB gadget support would be a strong argument for your version ;) > > > What SoC are you using the driver on? > I am using this driver on our custom SoC, the Kalray Coolidge MPPA (k1c). > I didn't tried on other SoC. > > > I am not sure if it makes sense to rebase your patches on my driver. If > > you can convince me that yours is better then we could just remove mine > > and merge yours instead. > I don't have a strong argument in favor of my version. Except that I have > already made some changes over the original U-Boot version: > > I've tried to improve the roothub interface making it easier to decode > new requests. > > I have removed the memcpy done in transfer_chunk between the usb data > buffer and the dma buffer :/ > However this mean that all buffer used for usb tranfert must be dma > capable. Not all usb transfers use dma_alloc for it's buffers but > sometime they are allocated on the stack, this can be problematic if > the stack cannot be used by DMA. We do this on other controllers as well, so this shouldn't be a problem. The day will come we either have to fix the callers or create some bounce buffer implementation for non DMA capable memory. Sascha -- Pengutronix e.K. | | Steuerwalder Str. 21 | http://www.pengutronix.de/ | 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 | _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC PATCH 0/7] usb: dwc2 host driver 2020-01-16 6:50 ` Sascha Hauer @ 2020-01-16 17:25 ` Jules Maselbas 2020-01-21 13:32 ` Michael Grzeschik 0 siblings, 1 reply; 24+ messages in thread From: Jules Maselbas @ 2020-01-16 17:25 UTC (permalink / raw) To: Sascha Hauer; +Cc: Barebox List, Matthias Mann On Thu, Jan 16, 2020 at 07:50:27AM +0100, Sascha Hauer wrote: > > Well, USB gadget support would be a strong argument for your version ;) I am going to send a new version for the host driver alongside the gadget support. I've made few cleanup (in both host and gadget driver). Although there is still some stub functions in the gadget driver, I think is this a good start. cheers, Jules _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC PATCH 0/7] usb: dwc2 host driver 2020-01-16 17:25 ` Jules Maselbas @ 2020-01-21 13:32 ` Michael Grzeschik 0 siblings, 0 replies; 24+ messages in thread From: Michael Grzeschik @ 2020-01-21 13:32 UTC (permalink / raw) To: Jules Maselbas; +Cc: Barebox List, Matthias Mann [-- Attachment #1.1: Type: text/plain, Size: 1077 bytes --] Hi Jules, On Thu, Jan 16, 2020 at 06:25:08PM +0100, Jules Maselbas wrote: > On Thu, Jan 16, 2020 at 07:50:27AM +0100, Sascha Hauer wrote: > > > > Well, USB gadget support would be a strong argument for your version ;) > I am going to send a new version for the host driver alongside the gadget > support. I've made few cleanup (in both host and gadget driver). Although > there is still some stub functions in the gadget driver, I think is this > a good start. I did not get this conversation and also started the gadget support on dwc2. My target machine is stm32, so my work includes also the internal stm32 phy and some regulators, to get this done. I could rework this on top of your patches, if you send them on the list. Regards, Michael -- Pengutronix e.K. | | Steuerwalder Str. 21 | http://www.pengutronix.de/ | 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 | [-- Attachment #1.2: signature.asc --] [-- Type: application/pgp-signature, Size: 833 bytes --] [-- Attachment #2: Type: text/plain, Size: 149 bytes --] _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH 10/10] ARM: rpi_defconfig: Enable networking support 2019-12-20 14:32 [PATCH 00/10] Add Raspberry Pi USB support Sascha Hauer ` (8 preceding siblings ...) 2019-12-20 14:32 ` [PATCH 09/10] usb: Add dwc2 host driver Sascha Hauer @ 2019-12-20 14:32 ` Sascha Hauer 9 siblings, 0 replies; 24+ messages in thread From: Sascha Hauer @ 2019-12-20 14:32 UTC (permalink / raw) To: Barebox List Now that we have USB support for the Raspberry Pi enable USB and USB network support. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- arch/arm/configs/rpi_defconfig | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/arch/arm/configs/rpi_defconfig b/arch/arm/configs/rpi_defconfig index 0ae0c42b12..9603a0fa30 100644 --- a/arch/arm/configs/rpi_defconfig +++ b/arch/arm/configs/rpi_defconfig @@ -22,9 +22,12 @@ CONFIG_BOOTM_OFTREE=y CONFIG_BLSPEC=y CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW=y CONFIG_DEFAULT_ENVIRONMENT_PATH="arch/arm/boards/raspberry-pi/env" +CONFIG_CMD_DMESG=y CONFIG_LONGHELP=y CONFIG_CMD_IOMEM=y +CONFIG_CMD_IMD=y CONFIG_CMD_MEMINFO=y +CONFIG_CMD_REGULATOR=y CONFIG_CMD_GO=y CONFIG_CMD_LOADB=y CONFIG_CMD_LOADY=y @@ -43,6 +46,9 @@ CONFIG_CMD_UNCOMPRESS=y CONFIG_CMD_LET=y CONFIG_CMD_MSLEEP=y CONFIG_CMD_SLEEP=y +CONFIG_CMD_DHCP=y +CONFIG_CMD_MIITOOL=y +CONFIG_CMD_PING=y CONFIG_CMD_ECHO_E=y CONFIG_CMD_EDIT=y CONFIG_CMD_LOGIN=y @@ -62,8 +68,13 @@ CONFIG_CMD_OF_NODE=y CONFIG_CMD_OF_PROPERTY=y CONFIG_CMD_OFTREE=y CONFIG_CMD_TIME=y +CONFIG_NET=y CONFIG_SERIAL_AMBA_PL011=y CONFIG_DRIVER_SERIAL_NS16550=y +CONFIG_NET_USB=y +CONFIG_NET_USB_SMSC95XX=y +CONFIG_USB_HOST=y +CONFIG_USB_DWC2_HOST=y CONFIG_MCI=y CONFIG_MCI_BCM283X=y CONFIG_MCI_BCM283X_SDHOST=y @@ -75,6 +86,8 @@ CONFIG_WATCHDOG_BCM2835=y CONFIG_PINCTRL_BCM283X=y CONFIG_REGULATOR=y CONFIG_FS_EXT4=y +CONFIG_FS_TFTP=y +CONFIG_FS_NFS=y CONFIG_FS_FAT=y CONFIG_FS_FAT_WRITE=y CONFIG_FS_FAT_LFN=y -- 2.24.0 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 24+ messages in thread
end of thread, other threads:[~2020-01-21 13:32 UTC | newest] Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2019-12-20 14:32 [PATCH 00/10] Add Raspberry Pi USB support Sascha Hauer 2019-12-20 14:32 ` [PATCH 01/10] usb: Make timeout unit clear Sascha Hauer 2019-12-20 14:32 ` [PATCH 02/10] of: Add of_bus_n_xxx_cells() Sascha Hauer 2019-12-20 14:32 ` [PATCH 03/10] device: Introduce dma_offset Sascha Hauer 2019-12-20 14:32 ` [PATCH 04/10] of: Read dma_offset from device tree Sascha Hauer 2019-12-20 14:32 ` [PATCH 05/10] usb: Add usbroothubdes.h Sascha Hauer 2019-12-20 14:32 ` [PATCH 06/10] regulator: add function to get regulator by its name Sascha Hauer 2019-12-20 14:32 ` [PATCH 07/10] rpi: Enable USB Power domain during startup Sascha Hauer 2019-12-20 14:32 ` [PATCH 08/10] usb: Forward error code from usb_set_configuration Sascha Hauer 2019-12-20 14:32 ` [PATCH 09/10] usb: Add dwc2 host driver Sascha Hauer 2020-01-14 13:21 ` [RFC PATCH 0/7] usb: " Jules Maselbas 2020-01-14 13:21 ` [RFC PATCH 1/7] usb: dwc2: Add host controller driver Jules Maselbas 2020-01-14 13:21 ` [RFC PATCH 2/7] usb: dwc2: host: Handle dma mapping errors Jules Maselbas 2020-01-14 13:21 ` [RFC PATCH 3/7] usb: dwc2: Dynamic fifo size support from Linux Jules Maselbas 2020-01-14 13:21 ` [RFC PATCH 4/7] usb: dwc2: Rework roothub interface Jules Maselbas 2020-01-14 13:21 ` [RFC PATCH 5/7] HACK: usb: dwc2: Fix toggle reset Jules Maselbas 2020-01-14 13:21 ` [RFC PATCH 6/7] usb: dwc2: Rewrite dwc2_hc_init Jules Maselbas 2020-01-14 13:21 ` [RFC PATCH 7/7] usb: dwc2: Read dr_mode from device tree Jules Maselbas 2020-01-14 14:27 ` [RFC PATCH 0/7] usb: dwc2 host driver Sascha Hauer 2020-01-14 17:12 ` Jules Maselbas 2020-01-16 6:50 ` Sascha Hauer 2020-01-16 17:25 ` Jules Maselbas 2020-01-21 13:32 ` Michael Grzeschik 2019-12-20 14:32 ` [PATCH 10/10] ARM: rpi_defconfig: Enable networking support Sascha Hauer
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox