diff options
Diffstat (limited to 'drivers/usb')
122 files changed, 22857 insertions, 9090 deletions
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 99eff1c8d2..d66a75635d 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config USB bool @@ -9,6 +10,8 @@ if USB_HOST source "drivers/usb/imx/Kconfig" +source "drivers/usb/dwc2/Kconfig" + source "drivers/usb/dwc3/Kconfig" source "drivers/usb/host/Kconfig" @@ -21,6 +24,8 @@ source "drivers/usb/misc/Kconfig" endif +source "drivers/usb/typec/Kconfig" + source "drivers/usb/gadget/Kconfig" source "drivers/usb/musb/Kconfig" diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 9e98099502..0cac50c0f3 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -1,10 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0-only 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/ obj-$(CONFIG_USB_STORAGE) += storage/ obj-y += host/ obj-y += otg/ +obj-y += gadget/ +obj-y += typec/ obj-$(CONFIG_USB) += misc/ diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile index 58f6c5e027..60e03caad7 100644 --- a/drivers/usb/core/Makefile +++ b/drivers/usb/core/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_USB_HOST) += usb.o hub.o obj-$(CONFIG_USB) += common.o obj-$(CONFIG_OFDEVICE) += of.o diff --git a/drivers/usb/core/common.c b/drivers/usb/core/common.c index bcbe3a155d..61ccc13024 100644 --- a/drivers/usb/core/common.c +++ b/drivers/usb/core/common.c @@ -1,5 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> -#include <usb/ch9.h> +#include <linux/usb/ch9.h> static const char *const speed_names[] = { [USB_SPEED_UNKNOWN] = "UNKNOWN", diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 8874775f17..bef428f7fb 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1,32 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * hub.c - USB hub support * * Copyright (c) 2011 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <common.h> #include <init.h> #include <malloc.h> #include <errno.h> -#include <usb/phy.h> -#include <usb/usb.h> -#include <usb/usb_defs.h> +#include <linux/usb/phy.h> +#include <linux/usb/usb.h> +#include <linux/usb/usb_defs.h> #include "usb.h" -#include "hub.h" #define USB_BUFSIZ 512 @@ -44,11 +31,38 @@ struct usb_device_scan { static LIST_HEAD(usb_scan_list); +static bool usb_hub_is_superspeed(struct usb_device *hdev) +{ + return hdev->descriptor->bDeviceProtocol == 3; +} + +bool usb_hub_is_root_hub(struct usb_device *hdev) +{ + return hdev->level == 0; +} + +static int usb_set_hub_depth(struct usb_device *dev, int depth) +{ + dev_dbg(&dev->dev, "set hub depth to %d\n", dev->level); + + if (depth < 0 || depth > 4) + return -EINVAL; + + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + HUB_SET_DEPTH, USB_DIR_OUT | USB_RT_HUB, + depth, 0, NULL, 0, USB_CNTL_TIMEOUT); +} + static int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size) { + unsigned short dtype = USB_DT_HUB; + + if (usb_hub_is_superspeed(dev)) + dtype = USB_DT_SS_HUB; + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB, - USB_DT_HUB << 8, 0, data, size, USB_CNTL_TIMEOUT); + dtype << 8, 0, data, size, USB_CNTL_TIMEOUT); } static int usb_clear_port_feature(struct usb_device *dev, int port, int feature) @@ -72,11 +86,39 @@ static int usb_get_hub_status(struct usb_device *dev, void *data) data, sizeof(struct usb_hub_status), USB_CNTL_TIMEOUT); } -static int usb_get_port_status(struct usb_device *dev, int port, void *data) +static int usb_get_port_status(struct usb_device *dev, int port, + struct usb_port_status *status) { - return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + struct usb_port_status *data; + int ret; + + data = dma_alloc(sizeof(*data)); + if (!data) + return -ENOMEM; + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port, data, sizeof(struct usb_hub_status), USB_CNTL_TIMEOUT); + if (ret < 0) + goto out; + + *status = *data; + + if (!usb_hub_is_root_hub(dev) && usb_hub_is_superspeed(dev)) { + u16 tmp = status->wPortStatus & USB_SS_PORT_STAT_MASK; + + if (status->wPortStatus & USB_SS_PORT_STAT_POWER) + tmp |= USB_PORT_STAT_POWER; + if ((status->wPortStatus & USB_SS_PORT_STAT_SPEED) == + USB_PORT_STAT_SPEED_5GBPS) + tmp |= USB_PORT_STAT_SUPER_SPEED; + + status->wPortStatus = tmp; + } + +out: + dma_free(data); + return ret; } @@ -88,22 +130,13 @@ static void usb_hub_power_on(struct usb_hub_device *hub) dev = hub->pusb_dev; - /* - * Enable power to the ports: - * Here we Power-cycle the ports: aka, - * turning them off and turning on again. - */ - for (i = 0; i < dev->maxchild; i++) { - usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_POWER); - dev_dbg(&dev->dev, "port %d returns %lX\n", i + 1, dev->status); - } - /* Enable power to the ports */ dev_dbg(&dev->dev, "enabling power on all ports\n"); for (i = 0; i < dev->maxchild; i++) { usb_set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER); - dev_dbg(&dev->dev, "port %d returns %lX\n", i + 1, dev->status); + dev_dbg(&dev->dev, "port%d: usb_set_port_feature returns 0x%08lx\n", + i + 1, dev->status); } /* @@ -120,8 +153,7 @@ static void usb_hub_power_on(struct usb_hub_device *hub) */ hub->connect_timeout = hub->query_delay + 1000 * MSECOND; - dev_dbg(&dev->dev, "devnum=%d poweron: query_delay=%d \ - connect_timeout=%d\n", + dev_dbg(&dev->dev, "devnum=%d poweron: query_delay=%d connect_timeout=%d\n", dev->devnum, max(100, (int) pgood_delay), max(100, (int) pgood_delay) + 1000); } @@ -130,41 +162,48 @@ static void usb_hub_power_on(struct usb_hub_device *hub) static inline char *portspeed(int portstatus) { - if (portstatus & (1 << USB_PORT_FEAT_HIGHSPEED)) + switch (portstatus & USB_PORT_STAT_SPEED_MASK) { + case USB_PORT_STAT_SUPER_SPEED: + return "5 Gb/s"; + case USB_PORT_STAT_HIGH_SPEED: return "480 Mb/s"; - else if (portstatus & (1 << USB_PORT_FEAT_LOWSPEED)) + case USB_PORT_STAT_LOW_SPEED: return "1.5 Mb/s"; - else + default: return "12 Mb/s"; + } } -int hub_port_reset(struct usb_device *hub, int port, struct usb_device *usb) +static int hub_port_reset(struct usb_device *hub, int port, + struct usb_device *usb) { int tries; struct usb_port_status portsts; unsigned short portstatus, portchange; int delay = HUB_SHORT_RESET_TIME; /* start with short reset delay */ - dev_dbg(&hub->dev, "hub_port_reset: resetting port %d...\n", port); + dev_dbg(&hub->dev, "port%d: resetting...\n", port + 1); for (tries = 0; tries < MAX_TRIES; tries++) { usb_set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET); mdelay(delay); if (usb_get_port_status(hub, port + 1, &portsts) < 0) { - dev_dbg(&hub->dev, "get_port_status failed status %lX\n", - hub->status); + dev_dbg(&hub->dev, "port%d: get_port_status failed status 0x%lX\n", + port + 1, hub->status); return -1; } portstatus = le16_to_cpu(portsts.wPortStatus); portchange = le16_to_cpu(portsts.wPortChange); - dev_dbg(&hub->dev, "portstatus %x, change %x, %s\n", + dev_dbg(&hub->dev, "port%d: status 0x%04x, change 0x%04x, %s\n", + port + 1, portstatus, portchange, portspeed(portstatus)); - dev_dbg(&hub->dev, "STAT_C_CONNECTION = %d STAT_CONNECTION = %d" \ + dev_dbg(&hub->dev, "port%d: STAT_C_CONNECTION = %d STAT_CONNECTION = %d" " USB_PORT_STAT_ENABLE %d\n", + port + 1, (portchange & USB_PORT_STAT_C_CONNECTION) ? 1 : 0, (portstatus & USB_PORT_STAT_CONNECTION) ? 1 : 0, (portstatus & USB_PORT_STAT_ENABLE) ? 1 : 0); @@ -181,48 +220,44 @@ int hub_port_reset(struct usb_device *hub, int port, struct usb_device *usb) } if (tries == MAX_TRIES) { - dev_dbg(&hub->dev, "Cannot enable port %i after %i retries, " \ - "disabling port.\n", port + 1, MAX_TRIES); + dev_dbg(&hub->dev, "port%d: Cannot enable after %i retries, disabling port.\n", + port + 1, MAX_TRIES); dev_dbg(&hub->dev, "Maybe the USB cable is bad?\n"); return -1; } usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_RESET); - if (portstatus & USB_PORT_STAT_HIGH_SPEED) + switch (portstatus & USB_PORT_STAT_SPEED_MASK) { + case USB_PORT_STAT_SUPER_SPEED: + usb->speed = USB_SPEED_SUPER; + break; + case USB_PORT_STAT_HIGH_SPEED: usb->speed = USB_SPEED_HIGH; - else if (portstatus & USB_PORT_STAT_LOW_SPEED) + break; + case USB_PORT_STAT_LOW_SPEED: usb->speed = USB_SPEED_LOW; - else + break; + default: usb->speed = USB_SPEED_FULL; + break; + } return 0; } -static void usb_hub_port_connect_change(struct usb_device *dev, int port) +static void usb_hub_port_connect_change(struct usb_device *dev, int port, + uint16_t portstatus, uint16_t portchange) { struct usb_device *usb; - struct usb_port_status portsts; - unsigned short portstatus, portchange; - - /* Check status */ - if (usb_get_port_status(dev, port + 1, &portsts) < 0) { - dev_dbg(&dev->dev, "get_port_status failed\n"); - return; - } - - portstatus = le16_to_cpu(portsts.wPortStatus); - portchange = le16_to_cpu(portsts.wPortChange); - dev_dbg(&dev->dev, "portstatus %x, change %x, %s\n", - portstatus, portchange, portspeed(portstatus)); /* Clear the connection change status */ usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_CONNECTION); /* Disconnect any existing devices under this port */ if (dev->children[port] && !(portstatus & USB_PORT_STAT_CONNECTION)) { - dev_dbg(&dev->dev, "disconnect detected on port %d\n", port + 1); + dev_dbg(&dev->dev, "port%d: disconnect detected\n", port + 1); usb_remove_device(dev->children[port]); if (!dev->parent && dev->host->usbphy) @@ -242,7 +277,7 @@ static void usb_hub_port_connect_change(struct usb_device *dev, int port) /* Reset it */ if (hub_port_reset(dev, port, usb) < 0) { - dev_warn(&dev->dev, "cannot reset port %i!?\n", port + 1); + dev_warn(&dev->dev, "port%d: cannot reset\n", port + 1); usb_free_device(usb); return; } @@ -267,7 +302,7 @@ static void usb_hub_port_connect_change(struct usb_device *dev, int port) device_detect(&usb->dev); } -static int usb_scan_port(struct usb_device_scan *usb_scan) +static void usb_scan_port(struct usb_device_scan *usb_scan) { struct usb_port_status portsts; unsigned short portstatus, portchange; @@ -284,68 +319,65 @@ static int usb_scan_port(struct usb_device_scan *usb_scan) * This is needed for voltages to stabilize. */ if (get_time_ns() < hub->query_delay) - return 0; + return; if (usb_get_port_status(dev, port + 1, &portsts) < 0) { - dev_dbg(&dev->dev, "get_port_status failed\n"); + dev_dbg(&dev->dev, "port%d: get_port_status failed\n", port + 1); if(get_time_ns() >= hub->connect_timeout) { - dev_dbg(&dev->dev, "port=%d: timeout\n", port + 1); + dev_dbg(&dev->dev, "port%d: timeout\n", port + 1); /* Remove this device from scanning list */ goto remove; } - return 0; + return; } portstatus = le16_to_cpu(portsts.wPortStatus); portchange = le16_to_cpu(portsts.wPortChange); - dev_dbg(&dev->dev, "Port %d Status %X Change %X\n", + dev_dbg(&dev->dev, "port%d: Status 0x%04x Change 0x%04x\n", port + 1, portstatus, portchange); - if (!(portchange & USB_PORT_STAT_C_CONNECTION)) { - if(get_time_ns() >= hub->connect_timeout) { - dev_dbg(&dev->dev, "port=%d: timeout\n", port + 1); + if (!(portchange & USB_PORT_STAT_C_CONNECTION) || + !(portstatus & USB_PORT_STAT_CONNECTION)) { + if (get_time_ns() >= hub->connect_timeout) { + dev_dbg(&dev->dev, "port%d: timeout\n", port + 1); /* Remove this device from scanning list */ goto remove; } - return 0; + return; } - /* Test if the connection came up, and if not exit */ - if(!(portstatus & USB_PORT_STAT_CONNECTION)) - return 0; + if (portchange & USB_PORT_STAT_C_RESET) { + dev_dbg(&dev->dev, "port%d: reset change\n", port + 1); + usb_clear_port_feature(dev, port + 1, + USB_PORT_FEAT_C_RESET); + } + + if ((portchange & USB_PORT_STAT_C_BH_RESET) && + usb_hub_is_superspeed(dev)) { + dev_dbg(&dev->dev, "port%d: BH reset change\n", port + 1); + usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_BH_PORT_RESET); + } /* A new USB device is ready at this point */ - dev_dbg(&dev->dev, "port=%d: USB dev found\n", port + 1); + dev_dbg(&dev->dev, "port%d: USB dev found\n", port + 1); - usb_hub_port_connect_change(dev, port); + usb_hub_port_connect_change(dev, port, portstatus, portchange); if (portchange & USB_PORT_STAT_C_ENABLE) { - dev_dbg(&dev->dev, "port %d enable change, status %x\n", + dev_dbg(&dev->dev, "port%d: enable change, status 0x%04x\n", port + 1, portstatus); usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_ENABLE); - - /* EM interference sometimes causes bad shielded USB - * devices to be shutdown by the hub, this hack enables - * them again. Works at least with mouse driver */ - if (!(portstatus & USB_PORT_STAT_ENABLE) && - (portstatus & USB_PORT_STAT_CONNECTION) && - ((dev->children[port]))) { - dev_dbg(&dev->dev, "already running port %i " \ - "disabled by hub (EMI?), " \ - "re-enabling...\n", port + 1); - usb_hub_port_connect_change(dev, port); - } } if (portstatus & USB_PORT_STAT_SUSPEND) { - dev_dbg(&dev->dev, "port %d suspend change\n", port + 1); + dev_dbg(&dev->dev, "port%d: suspend change\n", port + 1); usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_SUSPEND); } if (portchange & USB_PORT_STAT_C_OVERCURRENT) { - dev_dbg(&dev->dev, "port %d over-current change\n", port + 1); + dev_dbg(&dev->dev, "port%d: over-current change\n", port + 1); usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_OVER_CURRENT); /* Only power-on this one port */ @@ -358,19 +390,13 @@ static int usb_scan_port(struct usb_device_scan *usb_scan) */ if (hub->overcurrent_count[port] <= PORT_OVERCURRENT_MAX_SCAN_COUNT) - return 0; + return; /* Otherwise the device will get removed */ - dev_dbg(&dev->dev,"Port %d over-current occurred %d times\n", + dev_dbg(&dev->dev,"port%d: over-current occurred %d times\n", port + 1, hub->overcurrent_count[port]); } - if (portchange & USB_PORT_STAT_C_RESET) { - dev_dbg(&dev->dev, "port %d reset change\n", port + 1); - usb_clear_port_feature(dev, port + 1, - USB_PORT_FEAT_C_RESET); - } - remove: /* * We're done with this device, so let's remove this device from @@ -378,8 +404,6 @@ remove: */ list_del(&usb_scan->list); free(usb_scan); - - return 0; } static int usb_device_list_scan(void) @@ -387,7 +411,6 @@ static int usb_device_list_scan(void) struct usb_device_scan *usb_scan; struct usb_device_scan *tmp; static int running; - int ret = 0; /* Only run this loop once for each controller */ if (running) @@ -400,12 +423,11 @@ static int usb_device_list_scan(void) if (list_empty(&usb_scan_list)) goto out; - list_for_each_entry_safe(usb_scan, tmp, &usb_scan_list, list) { - /* Scan this port */ - ret = usb_scan_port(usb_scan); - if (ret) - goto out; - } + list_for_each_entry_safe(usb_scan, tmp, &usb_scan_list, list) + usb_scan_port(usb_scan); + + /* Avoid hammering the HUB with port scans */ + mdelay(25); } out: @@ -416,17 +438,21 @@ out: */ running = 0; - return ret; + return 0; } static int usb_hub_configure(struct usb_device *dev) { - unsigned char buffer[USB_BUFSIZ], *bitmap; + unsigned char *buffer, *bitmap; struct usb_hub_descriptor *descriptor; struct usb_hub_status *hubsts; - int i; + int i, ret; struct usb_hub_device *hub; + buffer = dma_alloc(USB_BUFSIZ); + if (!buffer) + return -ENOMEM; + hub = xzalloc(sizeof (*hub)); dev->hub = hub; @@ -435,7 +461,8 @@ static int usb_hub_configure(struct usb_device *dev) if (usb_get_hub_descriptor(dev, buffer, 4) < 0) { dev_dbg(&dev->dev, "%s: failed to get hub " \ "descriptor, giving up %lX\n", __func__, dev->status); - return -1; + ret = -1; + goto out; } descriptor = (struct usb_hub_descriptor *)buffer; @@ -445,13 +472,15 @@ static int usb_hub_configure(struct usb_device *dev) dev_dbg(&dev->dev, "%s: failed to get hub " \ "descriptor - too long: %d\n", __func__, descriptor->bLength); - return -1; + ret = -1; + goto out; } if (usb_get_hub_descriptor(dev, buffer, descriptor->bLength) < 0) { dev_dbg(&dev->dev, "%s: failed to get hub " \ "descriptor 2nd giving up %lX\n", __func__, dev->status); - return -1; + ret = -1; + goto out; } memcpy((unsigned char *)&hub->desc, buffer, descriptor->bLength); /* adjust 16bit values */ @@ -504,6 +533,56 @@ static int usb_hub_configure(struct usb_device *dev) break; } + switch (dev->descriptor->bDeviceProtocol) { + case USB_HUB_PR_FS: + break; + case USB_HUB_PR_HS_SINGLE_TT: + dev_dbg(&dev->dev, "Single TT\n"); + break; + case USB_HUB_PR_HS_MULTI_TT: + ret = usb_set_interface(dev, 0, 1); + if (ret == 0) { + dev_dbg(&dev->dev, "TT per port\n"); + hub->tt.multi = true; + } else { + dev_dbg(&dev->dev, "Using single TT (err %d)\n", ret); + } + break; + case USB_HUB_PR_SS: + /* USB 3.0 hubs don't have a TT */ + break; + default: + dev_dbg(&dev->dev, "Unrecognized hub protocol %d\n", + dev->descriptor->bDeviceProtocol); + break; + } + + /* Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns */ + switch (hub->desc.wHubCharacteristics & HUB_CHAR_TTTT) { + case HUB_TTTT_8_BITS: + if (dev->descriptor->bDeviceProtocol != 0) { + hub->tt.think_time = 666; + dev_dbg(&dev->dev, "TT requires at most %d FS bit times (%d ns)\n", + 8, hub->tt.think_time); + } + break; + case HUB_TTTT_16_BITS: + hub->tt.think_time = 666 * 2; + dev_dbg(&dev->dev, "TT requires at most %d FS bit times (%d ns)\n", + 16, hub->tt.think_time); + break; + case HUB_TTTT_24_BITS: + hub->tt.think_time = 666 * 3; + dev_dbg(&dev->dev, "TT requires at most %d FS bit times (%d ns)\n", + 24, hub->tt.think_time); + break; + case HUB_TTTT_32_BITS: + hub->tt.think_time = 666 * 4; + dev_dbg(&dev->dev, "TT requires at most %d FS bit times (%d ns)\n", + 32, hub->tt.think_time); + break; + } + dev_dbg(&dev->dev, "power on to power good time: %dms\n", descriptor->bPwrOn2PwrGood * 2); dev_dbg(&dev->dev, "hub controller current requirement: %dmA\n", @@ -517,13 +596,15 @@ static int usb_hub_configure(struct usb_device *dev) if (sizeof(struct usb_hub_status) > USB_BUFSIZ) { dev_dbg(&dev->dev, "%s: failed to get Status - " \ "too long: %d\n", __func__, descriptor->bLength); - return -1; + ret = -1; + goto out; } if (usb_get_hub_status(dev, buffer) < 0) { dev_dbg(&dev->dev, "%s: failed to get Status %lX\n", __func__, dev->status); - return -1; + ret = -1; + goto out; } hubsts = (struct usb_hub_status *)buffer; @@ -536,9 +617,33 @@ static int usb_hub_configure(struct usb_device *dev) dev_dbg(&dev->dev, "%sover-current condition exists\n", (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_OVERCURRENT) ? \ "" : "no "); + + if (dev->host->update_hub_device) { + ret = dev->host->update_hub_device(dev); + if (ret) + goto out; + } + + if (!usb_hub_is_root_hub(dev) && usb_hub_is_superspeed(dev)) { + /* + * This request sets the value that the hub uses to + * determine the index into the 'route string index' + * for this hub. + */ + ret = usb_set_hub_depth(dev, dev->level - 1); + if (ret < 0) { + dev_dbg(&dev->dev, "failed to set hub depth (0x%08lx)\n", + dev->status); + goto out; + } + } + usb_hub_power_on(hub); - return 0; + ret = 0; +out: + dma_free(buffer); + return ret; } static int usb_hub_configure_ports(struct usb_device *dev) @@ -572,7 +677,7 @@ static int usb_hub_configure_ports(struct usb_device *dev) return usb_device_list_scan(); } -static int usb_hub_detect(struct device_d *dev) +static int usb_hub_detect(struct device *dev) { struct usb_device *usbdev = container_of(dev, struct usb_device, dev); int i; diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h deleted file mode 100644 index 74921b66fd..0000000000 --- a/drivers/usb/core/hub.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef __CORE_HUB_H -#define __CORE_HUB_H - -int hub_port_reset(struct usb_device *hub, int port, - struct usb_device *usb); - -#endif /* __CORE_HUB_H */ diff --git a/drivers/usb/core/of.c b/drivers/usb/core/of.c index 979088ef4e..25203c6064 100644 --- a/drivers/usb/core/of.c +++ b/drivers/usb/core/of.c @@ -1,22 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * usb devicetree helper functions - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <common.h> -#include <usb/usb.h> -#include <usb/phy.h> +#include <linux/usb/usb.h> +#include <linux/usb/phy.h> #include <of.h> static const char *usb_dr_modes[] = { diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index d29cd1328b..1f6f1d7c41 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * * Most of this source has been derived from the Linux USB @@ -14,21 +15,6 @@ * * Adapted for barebox: * (C) Copyright 2001 Denis Peter, MPL AG Switzerland - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * */ /* @@ -40,7 +26,6 @@ * * For each transfer (except "Interrupt") we wait for completion. */ - #define pr_fmt(fmt) "usb: " fmt #include <common.h> @@ -53,11 +38,10 @@ #include <init.h> #include <dma.h> -#include <usb/usb.h> -#include <usb/ch9.h> +#include <linux/usb/usb.h> +#include <linux/usb/ch9.h> #include "usb.h" -#include "hub.h" #define USB_BUFSIZ 512 @@ -69,7 +53,7 @@ LIST_HEAD(usb_device_list); static void print_usb_device(struct usb_device *dev) { - pr_info("Bus %03d Device %03d: ID %04x:%04x %s\n", + dev_info(&dev->dev, "Bus %03d Device %03d: ID %04x:%04x %s\n", dev->host->busnum, dev->devnum, dev->descriptor->idVendor, dev->descriptor->idProduct, @@ -80,28 +64,44 @@ static int host_busnum = 1; static inline int usb_host_acquire(struct usb_host *host) { - if (host->sem) + if (slice_acquired(&host->slice)) return -EAGAIN; - host->sem++; + + slice_acquire(&host->slice); + return 0; } static inline void usb_host_release(struct usb_host *host) { - if (host->sem > 0) - host->sem--; + slice_release(&host->slice); +} + +static int usb_hw_detect(struct device *dev) +{ + struct usb_host *host; + + list_for_each_entry(host, &host_list, list) { + if (dev == host->hw_dev) + return usb_host_detect(host); + } + + return -ENODEV; } int usb_register_host(struct usb_host *host) { list_add_tail(&host->list, &host_list); host->busnum = host_busnum++; - host->sem = 0; + slice_init(&host->slice, dev_name(host->hw_dev)); + if (!host->hw_dev->detect) + host->hw_dev->detect = usb_hw_detect; return 0; } void usb_unregister_host(struct usb_host *host) { + slice_exit(&host->slice); list_del(&host->list); } @@ -112,7 +112,7 @@ static int usb_set_configuration(struct usb_device *dev, int configuration) { int res; - pr_debug("set configuration %d\n", configuration); + dev_dbg(&dev->dev, "set configuration %d\n", configuration); /* set setup command */ res = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), @@ -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 @@ -147,21 +147,21 @@ usb_set_maxpacket_ep(struct usb_device *dev, struct usb_endpoint_descriptor *ep) /* Control => bidirectional */ dev->epmaxpacketout[b] = ep->wMaxPacketSize; dev->epmaxpacketin[b] = ep->wMaxPacketSize; - pr_debug("##Control EP epmaxpacketout/in[%d] = %d\n", + dev_dbg(&dev->dev, "##Control EP epmaxpacketout/in[%d] = %d\n", b, dev->epmaxpacketin[b]); } else { if ((ep->bEndpointAddress & 0x80) == 0) { /* OUT Endpoint */ if (ep->wMaxPacketSize > dev->epmaxpacketout[b]) { dev->epmaxpacketout[b] = ep->wMaxPacketSize; - pr_debug("##EP epmaxpacketout[%d] = %d\n", + dev_dbg(&dev->dev, "##EP epmaxpacketout[%d] = %d\n", b, dev->epmaxpacketout[b]); } } else { /* IN Endpoint */ if (ep->wMaxPacketSize > dev->epmaxpacketin[b]) { dev->epmaxpacketin[b] = ep->wMaxPacketSize; - pr_debug("##EP epmaxpacketin[%d] = %d\n", + dev_dbg(&dev->dev, "##EP epmaxpacketin[%d] = %d\n", b, dev->epmaxpacketin[b]); } } /* if out */ @@ -193,6 +193,7 @@ static int usb_parse_config(struct usb_device *dev, unsigned char *buffer, int c int index, ifno, epno, curr_if_num; int i; unsigned char *ch; + struct usb_interface *if_desc; ifno = -1; epno = -1; @@ -201,7 +202,7 @@ static int usb_parse_config(struct usb_device *dev, unsigned char *buffer, int c dev->configno = cfgno; head = (struct usb_descriptor_header *) &buffer[0]; if (head->bDescriptorType != USB_DT_CONFIG) { - printf(" ERROR: NOT USB_CONFIG_DESC %x\n", + dev_err(&dev->dev, " ERROR: NOT USB_CONFIG_DESC %x\n", head->bDescriptorType); return -1; } @@ -224,7 +225,7 @@ static int usb_parse_config(struct usb_device *dev, unsigned char *buffer, int c * next memcpy() will corrupt dev->config */ if (ifno > USB_MAXINTERFACES) { - printf("ifno = %d > " + dev_err(&dev->dev, "ifno = %d > " "USB_MAXINTERFACES = %d !\n", ifno, USB_MAXINTERFACES); @@ -250,13 +251,18 @@ static int usb_parse_config(struct usb_device *dev, unsigned char *buffer, int c &buffer[index], buffer[index]); le16_to_cpus(&(dev->config.interface[ifno].ep_desc[epno].\ wMaxPacketSize)); - pr_debug("if %d, ep %d\n", ifno, epno); + dev_dbg(&dev->dev, "if %d, ep %d\n", ifno, epno); + break; + case USB_DT_SS_ENDPOINT_COMP: + if_desc = &dev->config.interface[ifno]; + memcpy(&if_desc->ss_ep_comp_desc[epno], + &buffer[index], buffer[index]); break; default: if (head->bLength == 0) return 1; - pr_debug("unknown Description Type : %x\n", + dev_dbg(&dev->dev, "unknown Description Type : %x\n", head->bDescriptorType); { @@ -273,23 +279,6 @@ static int usb_parse_config(struct usb_device *dev, unsigned char *buffer, int c return 1; } -/** - * set address of a device to the value in dev->devnum. - * This can only be done by addressing the device via the default address (0) - */ -static int usb_set_address(struct usb_device *dev) -{ - int res; - - pr_debug("set address %d\n", dev->devnum); - - res = usb_control_msg(dev, usb_snddefctrl(dev), - USB_REQ_SET_ADDRESS, 0, - (dev->devnum), 0, - NULL, 0, USB_CNTL_TIMEOUT); - return res; -} - static int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char index, void *buf, int size) { @@ -301,29 +290,30 @@ static int usb_get_descriptor(struct usb_device *dev, unsigned char type, return res; } -/* - * By the time we get here, the device has gotten a new device ID - * and is in the default state. We need to identify the thing and - * get the ball rolling.. - * - * Returns 0 for success, != 0 for error. - */ -int usb_new_device(struct usb_device *dev) +static int get_descriptor_len(struct usb_device *dev, int len, int expect_len) { - int addr, err; - int tmp; - void *buf; - struct usb_device_descriptor *desc; - struct usb_device *parent = dev->parent; - char str[16]; + int err; - buf = dma_alloc(USB_BUFSIZ); + err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, dev->descriptor, len); + if (err < expect_len) { + if (err < 0) { + dev_err(&dev->dev, "unable to get device descriptor (error=%d)\n", + err); + return err; + } else { + dev_err(&dev->dev, "USB device descriptor short read (expected %i, got %i)\n", + expect_len, err); + return -EIO; + } + } - /* We still haven't set the Address yet */ - addr = dev->devnum; - dev->devnum = 0; + return 0; +} - /* This is a Windows scheme of initialization sequence, with double +static int usb_setup_descriptor(struct usb_device *dev, bool do_read) +{ + /* + * This is a Windows scheme of initialization sequence, with double * reset of the device (Linux uses the same sequence) * Some equipment is said to work only with such init sequence; this * patch is based on the work by Alan Stern: @@ -331,38 +321,50 @@ int usb_new_device(struct usb_device *dev) * thread_id=5729457&forum_id=5398 */ - /* send 64-byte GET-DEVICE-DESCRIPTOR request. Since the descriptor is + /* + * send 64-byte GET-DEVICE-DESCRIPTOR request. Since the descriptor is * only 18 bytes long, this will terminate with a short packet. But if * the maxpacket size is 8 or 16 the device may be waiting to transmit - * some more, or keeps on retransmitting the 8 byte header. */ - - desc = buf; - dev->descriptor->bMaxPacketSize0 = 64; /* Start off at 64 bytes */ - /* Default to 64 byte max packet size */ - dev->maxpacketsize = PACKET_SIZE_64; - dev->epmaxpacketin[0] = 64; - dev->epmaxpacketout[0] = 64; + * some more, or keeps on retransmitting the 8 byte header. + */ - err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, 64); - if (err < 0) { - pr_debug("%s: usb_get_descriptor() failed with %d\n", __func__, err); - goto err_out; + if (dev->speed == USB_SPEED_LOW) { + dev->descriptor->bMaxPacketSize0 = 8; + dev->maxpacketsize = PACKET_SIZE_8; + } else { + dev->descriptor->bMaxPacketSize0 = 64; + dev->maxpacketsize = PACKET_SIZE_64; } + dev->epmaxpacketin[0] = dev->descriptor->bMaxPacketSize0; + dev->epmaxpacketout[0] = dev->descriptor->bMaxPacketSize0; - dev->descriptor->bMaxPacketSize0 = desc->bMaxPacketSize0; - - /* find the port number we're at */ - if (parent) { - /* reset the port for the second time */ - err = hub_port_reset(dev->parent, dev->portnr - 1, dev); - if (err < 0) { - printf("\n Couldn't reset port %i\n", dev->portnr); - goto err_out; - } + if (do_read && dev->speed == USB_SPEED_FULL) { + int err; + + /* + * Validate we've received only at least 8 bytes, not that + * we've received the entire descriptor. The reasoning is: + * - The code only uses fields in the first 8 bytes, so + * that's all we need to have fetched at this stage. + * - The smallest maxpacket size is 8 bytes. Before we know + * the actual maxpacket the device uses, the USB controller + * may only accept a single packet. Consequently we are only + * guaranteed to receive 1 packet (at least 8 bytes) even in + * a non-error case. + * + * At least the DWC2 controller needs to be programmed with + * the number of packets in addition to the number of bytes. + * A request for 64 bytes of data with the maxpacket guessed + * as 64 (above) yields a request for 1 packet. + */ + err = get_descriptor_len(dev, 64, 8); + if (err) + return err; + + dev->epmaxpacketin[0] = dev->descriptor->bMaxPacketSize0; + dev->epmaxpacketout[0] = dev->descriptor->bMaxPacketSize0; } - dev->epmaxpacketin[0] = dev->descriptor->bMaxPacketSize0; - dev->epmaxpacketout[0] = dev->descriptor->bMaxPacketSize0; switch (dev->descriptor->bMaxPacketSize0) { case 8: dev->maxpacketsize = PACKET_SIZE_8; @@ -377,29 +379,90 @@ int usb_new_device(struct usb_device *dev) dev->maxpacketsize = PACKET_SIZE_64; break; } - dev->devnum = addr; + + return 0; +} + +/** + * set address of a device to the value in dev->devnum. + * This can only be done by addressing the device via the default address (0) + */ +static int usb_set_address(struct usb_device *dev) +{ + int res; + + dev_dbg(&dev->dev, "set address %d\n", dev->devnum); + + res = usb_control_msg(dev, usb_snddefctrl(dev), + USB_REQ_SET_ADDRESS, 0, + (dev->devnum), 0, + NULL, 0, USB_CNTL_TIMEOUT); + return res; +} + +/* + * By the time we get here, the device is in the default state. We need to + * identify the thing and get the ball rolling.. + * + * Returns 0 for success, != 0 for error. + */ +int usb_new_device(struct usb_device *dev) +{ + int err; + void *buf; + struct usb_host *host = dev->host; + struct usb_device *parent = dev->parent; + char str[16]; + + if (parent) + dev_set_name(&dev->dev, "%s-%d", parent->dev.name, + dev->portnr - 1); + else + dev_set_name(&dev->dev, "usb%d", dev->host->busnum); + + dev->dev.id = DEVICE_ID_SINGLE; + + buf = dma_alloc(USB_BUFSIZ); + + if (parent) + dev->level = parent->level + 1; + + if (host->alloc_device) { + err = host->alloc_device(dev); + if (err) + goto err_out; + } + + usb_setup_descriptor(dev, !host->no_desc_before_addr); + + dev->devnum = ++dev_index; err = usb_set_address(dev); /* set address */ if (err < 0) { - printf("\n USB device not accepting new address " \ + dev_err(&dev->dev, "USB device not accepting new address " \ "(error=%lX)\n", dev->status); goto err_out; } mdelay(10); /* Let the SET_ADDRESS settle */ - tmp = sizeof(*dev->descriptor); + if (host->no_desc_before_addr) { + err = usb_setup_descriptor(dev, true); + if (err) + goto err_out; + } err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, dev->descriptor, sizeof(*dev->descriptor)); - if (err < tmp) { + if (err < sizeof(*dev->descriptor)) { if (err < 0) - printf("unable to get device descriptor (error=%d)\n", + dev_err(&dev->dev, "unable to get device descriptor (error=%d)\n", err); else - printf("USB device descriptor short read " \ - "(expected %i, got %i)\n", tmp, err); + dev_err(&dev->dev, "USB device descriptor short read " \ + "(expected %zu, got %i)\n", + sizeof(*dev->descriptor), err); goto err_out; } /* correct le values */ @@ -412,12 +475,14 @@ 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) { + dev_err(&dev->dev, "Setting default configuration failed with: %pe\n" \ + "len %d, status %lX\n", ERR_PTR(err), + dev->act_len, dev->status); goto err_out; } - pr_debug("new device: Mfr=%d, Product=%d, SerialNumber=%d\n", + dev_dbg(&dev->dev, "new device: Mfr=%d, Product=%d, SerialNumber=%d\n", dev->descriptor->iManufacturer, dev->descriptor->iProduct, dev->descriptor->iSerialNumber); memset(dev->mf, 0, sizeof(dev->mf)); @@ -433,20 +498,11 @@ int usb_new_device(struct usb_device *dev) usb_string(dev, dev->descriptor->iSerialNumber, dev->serial, sizeof(dev->serial)); - if (parent) { - dev_set_name(&dev->dev, "%s-%d", parent->dev.name, - dev->portnr - 1); - } else { - dev_set_name(&dev->dev, "usb%d", dev->host->busnum); - } - - dev->dev.id = DEVICE_ID_SINGLE; - print_usb_device(dev); err = register_device(&dev->dev); if (err) { - printf("Failed to register device: %s\n", strerror(-err)); + dev_err(&dev->dev, "Failed to register device: %pe\n", ERR_PTR(err)); goto err_out; } @@ -478,6 +534,7 @@ void usb_free_device(struct usb_device *usbdev) { dma_free(usbdev->descriptor); dma_free(usbdev->setup_packet); + free_device_res(&usbdev->dev); free(usbdev); } @@ -507,7 +564,6 @@ struct usb_device *usb_alloc_new_device(void) { struct usb_device *usbdev = xzalloc(sizeof (*usbdev)); - usbdev->devnum = ++dev_index; usbdev->maxchild = 0; usbdev->dev.bus = &usb_bus_type; usbdev->setup_packet = dma_alloc(sizeof(*usbdev->setup_packet)); @@ -520,10 +576,14 @@ int usb_host_detect(struct usb_host *host) { int ret; + of_usb_host_probe_hubs(host); + if (!host->root_dev) { - ret = host->init(host); - if (ret) - return ret; + if (host->init) { + ret = host->init(host); + if (ret) + return ret; + } host->root_dev = usb_alloc_new_device(); host->root_dev->dev.parent = host->hw_dev; @@ -541,7 +601,7 @@ int usb_host_detect(struct usb_host *host) return 0; } -void usb_rescan(void) +int usb_rescan(void) { struct usb_host *host; int ret; @@ -555,6 +615,22 @@ void usb_rescan(void) } pr_info("%d USB Device(s) found\n", dev_count); + + if (IS_ENABLED(CONFIG_USB_OTGDEV)) { + unsigned int skipped_otg = 0; + struct device *dev; + + bus_for_each_device(&otg_bus_type, dev) { + if (otg_device_get_mode(dev) == USB_DR_MODE_OTG) + skipped_otg++; + } + + if (skipped_otg) + pr_notice("%u unconfigured OTG controller(s) were not scanned\n", + skipped_otg); + } + + return dev_count; } /*------------------------------------------------------------------- @@ -593,7 +669,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; @@ -609,13 +685,13 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe, setup_packet->value = cpu_to_le16(value); setup_packet->index = cpu_to_le16(index); setup_packet->length = cpu_to_le16(size); - pr_debug("usb_control_msg: request: 0x%X, requesttype: 0x%X, " \ + dev_dbg(&dev->dev, "usb_control_msg: request: 0x%X, requesttype: 0x%X, " \ "value 0x%X index 0x%X length 0x%X\n", request, requesttype, value, index, size); 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 +707,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 +720,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); @@ -719,23 +795,23 @@ int usb_get_configuration_no(struct usb_device *dev, result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, 9); if (result < 9) { if (result < 0) - printf("unable to get descriptor, error %lX\n", + dev_err(&dev->dev, "unable to get descriptor, error %lX\n", dev->status); else - printf("config descriptor too short " \ + dev_err(&dev->dev, "config descriptor too short " \ "(expected %i, got %i)\n", 9, result); return -1; } tmp = le16_to_cpu(config->wTotalLength); if (tmp > USB_BUFSIZ) { - pr_debug("usb_get_configuration_no: failed to get " \ + dev_dbg(&dev->dev, "usb_get_configuration_no: failed to get " \ "descriptor - too long: %u\n", tmp); return -1; } result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, tmp); - pr_debug("get_conf_no %d Result %d, wLength %u\n", + dev_dbg(&dev->dev, "get_conf_no %d Result %d, wLength %u\n", cfgno, result, tmp); return result; } @@ -755,7 +831,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) } } if (!if_face) { - printf("selecting invalid interface %d", interface); + dev_err(&dev->dev, "selecting invalid interface %d", interface); return -1; } /* @@ -902,31 +978,33 @@ static int usb_string_sub(struct usb_device *dev, unsigned int langid, */ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) { - unsigned char mybuf[USB_BUFSIZ]; unsigned char *tbuf; - int err; + int err = 0; unsigned int u, idx; if (size <= 0 || !buf || !index) return -1; + + tbuf = dma_alloc(USB_BUFSIZ); buf[0] = 0; - tbuf = &mybuf[0]; /* get langid for strings if it's not yet known */ if (!dev->have_langid) { err = usb_string_sub(dev, 0, 0, tbuf); if (err < 0) { - pr_debug("error getting string descriptor 0 " \ + dev_dbg(&dev->dev, "error getting string descriptor 0 " \ "(error=%lx)\n", dev->status); - return -1; + err = -1; + goto fail; } else if (tbuf[0] < 4) { pr_debug("string descriptor 0 too short\n"); - return -1; + err = -1; + goto fail; } else { dev->have_langid = -1; dev->string_langid = tbuf[2] | (tbuf[3] << 8); /* always use the first langid listed */ - pr_debug("USB device number %d default " \ + dev_dbg(&dev->dev, "USB device number %d default " \ "language ID 0x%x\n", dev->devnum, dev->string_langid); } @@ -934,7 +1012,7 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) err = usb_string_sub(dev, dev->string_langid, index, tbuf); if (err < 0) - return err; + goto fail; size--; /* leave room for trailing NULL char in output buffer */ for (idx = 0, u = 2; u < err; u += 2) { @@ -947,6 +1025,8 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) } buf[idx] = 0; err = idx; +fail: + dma_free(tbuf); return err; } @@ -1042,7 +1122,7 @@ static const struct usb_device_id *usb_match_id(struct usb_device *usbdev, } EXPORT_SYMBOL(usb_match_id); -static int usb_match(struct device_d *dev, struct driver_d *drv) +static int usb_match(struct device *dev, struct driver *drv) { struct usb_device *usbdev = container_of(dev, struct usb_device, dev); struct usb_driver *usbdrv = container_of(dev->driver, struct usb_driver, driver); @@ -1059,7 +1139,7 @@ static int usb_match(struct device_d *dev, struct driver_d *drv) return 1; } -static int usb_probe(struct device_d *dev) +static int usb_probe(struct device *dev) { struct usb_device *usbdev = container_of(dev, struct usb_device, dev); struct usb_driver *usbdrv = container_of(dev->driver, struct usb_driver, driver); @@ -1070,7 +1150,7 @@ static int usb_probe(struct device_d *dev) return usbdrv->probe(usbdev, id); } -static void usb_remove(struct device_d *dev) +static void usb_remove(struct device *dev) { struct usb_device *usbdev = container_of(dev, struct usb_device, dev); struct usb_driver *usbdrv = container_of(dev->driver, struct usb_driver, driver); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index a5bb255121..0d4f80c21d 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ #ifndef __CORE_USB_H #define __CORE_USB_H diff --git a/drivers/usb/dwc2/Kconfig b/drivers/usb/dwc2/Kconfig new file mode 100644 index 0000000000..08bb11cc6a --- /dev/null +++ b/drivers/usb/dwc2/Kconfig @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0-only +config USB_DWC2 + bool + depends on USB && HAS_DMA + select USB_OTGDEV + select OFDEVICE + help + DesignWare Core USB2 OTG driver. + +config USB_DWC2_HOST + bool "DWC2 Host mode support" + depends on USB_HOST + select USB_DWC2 + help + Select this when you want to use DWC2 in host mode. + +config USB_DWC2_GADGET + bool "DWC2 Gadget mode support" + depends on USB_GADGET + select USB_DWC2 + help + Select this when you want to use DWC2 in gadget mode. diff --git a/drivers/usb/dwc2/Makefile b/drivers/usb/dwc2/Makefile new file mode 100644 index 0000000000..1ca89e9b66 --- /dev/null +++ b/drivers/usb/dwc2/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_USB_DWC2_HOST) += dwc2.o core.o host.o rhub.o +obj-$(CONFIG_USB_DWC2_GADGET) += dwc2.o core.o gadget.o diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c new file mode 100644 index 0000000000..b198ba6bf8 --- /dev/null +++ b/drivers/usb/dwc2/core.c @@ -0,0 +1,882 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#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); +} + +static 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; +} + +static 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; + default: + dwc2_warn(dwc2, "Unhandled HS PHY type\n"); + fallthrough; + case GHWCFG2_HS_PHY_TYPE_NOT_SUPPORTED: + val = DWC2_PHY_TYPE_PARAM_FS; + break; + } + + dwc2->params.phy_type = val; +} + +static 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; +} + +static 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_hsotg_tx_fifo_count - return count of TX FIFOs in device mode + * + * @hsotg: Programming view of the DWC_otg controller + */ +int dwc2_tx_fifo_count(struct dwc2 *dwc2) +{ + if (dwc2->hw_params.en_multiple_tx_fifo) + /* In dedicated FIFO mode we need count of IN EPs */ + return dwc2->hw_params.num_dev_in_eps; + else + /* In shared FIFO mode we need count of Periodic IN EPs */ + return dwc2->hw_params.num_dev_perio_in_ep; +} + +static void dwc2_set_param_fifo_sizes(struct dwc2 *dwc2) +{ + struct dwc2_hw_params *hw = &dwc2->hw_params; + struct dwc2_core_params *p = &dwc2->params; + u32 total_fifo_size = dwc2->hw_params.total_fifo_size; + u32 max_np_tx_fifo_size = hw->dev_nperio_tx_fifo_size; + u32 max_rx_fifo_size = hw->rx_fifo_size; + u32 depth; + int count, i; + + count = dwc2_tx_fifo_count(dwc2); + + depth = total_fifo_size / 4; + p->g_np_tx_fifo_size = min(max_np_tx_fifo_size, depth); + total_fifo_size -= p->g_np_tx_fifo_size; + + depth = 8 * count + 256; + depth = max(total_fifo_size / count, depth); + p->g_rx_fifo_size = min(max_rx_fifo_size, depth); + total_fifo_size -= p->g_rx_fifo_size; + + for (i = 1; i <= count; i++) + p->g_tx_fifo_size[i] = total_fifo_size / count; +} + +/** + * 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; + } + + if ((dwc2->dr_mode == USB_DR_MODE_PERIPHERAL) || + (dwc2->dr_mode == USB_DR_MODE_OTG)) { + dwc2_set_param_fifo_sizes(dwc2); + } +} + +void dwc2_get_device_properties(struct dwc2 *dwc2) +{ + struct dwc2_core_params *p = &dwc2->params; + struct device_node *np = dwc2->dev->of_node; + int num; + + if ((dwc2->dr_mode == USB_DR_MODE_PERIPHERAL) || + (dwc2->dr_mode == USB_DR_MODE_OTG)) { + of_property_read_u32(np, "g-rx-fifo-size", + &p->g_rx_fifo_size); + + of_property_read_u32(np, "g-np-tx-fifo-size", + &p->g_np_tx_fifo_size); + + num = of_property_count_elems_of_size(np, "g-tx-fifo-size", sizeof(u32)); + if (num > 0) { + num = min(num, 15); + memset(p->g_tx_fifo_size, 0, + sizeof(p->g_tx_fifo_size)); + of_property_read_u32_array(np, + "g-tx-fifo-size", + &p->g_tx_fifo_size[1], + num); + } + } +} + +int dwc2_check_core_version(struct dwc2 *dwc2) +{ + struct dwc2_hw_params *hw = &dwc2->hw_params; + + /* + * 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; +} + +static void dwc2_get_dev_hwparams(struct dwc2 *dwc2) +{ + struct dwc2_hw_params *hw = &dwc2->hw_params; + u32 size; + int count, i; + + size = FIFOSIZE_DEPTH_GET(dwc2_readl(dwc2, GNPTXFSIZ)); + hw->dev_nperio_tx_fifo_size = size; + + count = dwc2_tx_fifo_count(dwc2); + + for (i = 1; i <= count; i++) { + size = FIFOSIZE_DEPTH_GET(dwc2_readl(dwc2, DPTXFSIZN(i))); + hw->g_tx_fifo_size[i] = size; + } +} + +/** + * 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; + + dwc2_get_dev_hwparams(dwc2); +} + +/* + * 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, 10000)) { + dwc2_warn(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, 10000)) + dwc2_warn(dwc2, "Timeout flushing fifos (GRSTCTL=%08x)\n", + dwc2_readl(dwc2, GRSTCTL)); + + /* Wait for 3 PHY Clocks */ + udelay(1); +} + +/** + * dwc2_flush_tx_fifo() - Flushes a Tx FIFO + * + * @hsotg: Programming view of DWC_otg controller + * @idx: The fifo index (0..15) + */ +void dwc2_flush_tx_fifo(struct dwc2 *dwc2, const int idx) +{ + u32 greset; + + if (idx > 15) + return; + + dwc2_dbg(dwc2, "Flush Tx FIFO %d\n", idx); + + /* Wait for AHB master IDLE state */ + if (dwc2_wait_bit_set(dwc2, GRSTCTL, GRSTCTL_AHBIDLE, 10000)) { + dwc2_warn(dwc2, "%s: Timeout waiting for AHB Idle\n", __func__); + return; + } + + greset = GRSTCTL_TXFFLSH; + greset |= GRSTCTL_TXFNUM(idx) & GRSTCTL_TXFNUM_MASK; + dwc2_writel(dwc2, greset, GRSTCTL); + + if (dwc2_wait_bit_clear(dwc2, GRSTCTL, GRSTCTL_TXFFLSH, 10000)) + dwc2_warn(dwc2, "%s: Timeout flushing tx fifo (GRSTCTL=%08x)\n", + __func__, dwc2_readl(dwc2, GRSTCTL)); + + /* Wait for at least 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); +} + +/* + * 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->of_node, NULL); + dwc2->dr_mode = mode; + + if (dwc2_hw_is_device(dwc2)) { + dwc2_dbg(dwc2, "Controller is device only\n"); + if (!IS_ENABLED(CONFIG_USB_DWC2_GADGET)) { + dwc2_err(dwc2, "gadget mode support not compiled in!\n"); + return -ENOTSUPP; + } + mode = USB_DR_MODE_PERIPHERAL; + } else if (dwc2_hw_is_host(dwc2)) { + dwc2_dbg(dwc2, "Controller is host only\n"); + if (!IS_ENABLED(CONFIG_USB_DWC2_HOST)) { + dwc2_err(dwc2, "host mode support not compiled in!\n"); + return -ENOTSUPP; + } + mode = USB_DR_MODE_HOST; + } else { + dwc2_dbg(dwc2, "Controller is otg\n"); + if (IS_ENABLED(CONFIG_USB_DWC2_HOST) && + IS_ENABLED(CONFIG_USB_DWC2_GADGET)) + mode = dwc2->dr_mode; + else if (IS_ENABLED(CONFIG_USB_DWC2_HOST)) + mode = USB_DR_MODE_HOST; + else if (IS_ENABLED(CONFIG_USB_DWC2_GADGET)) + mode = USB_DR_MODE_PERIPHERAL; + } + + if (mode != dwc2->dr_mode) { + dwc2_warn(dwc2, + "Selected dr_mode not supported by controller/driver. Enforcing '%s' mode.\n", + mode == USB_DR_MODE_HOST ? "host" : "peripheral"); + + dwc2->dr_mode = mode; + } + + return 0; +} + +/** + * dwc2_wait_for_mode() - Waits for the controller mode. + * @dwc2: Programming view of the DWC_otg controller. + * @host_mode: If true, waits for host mode, otherwise device mode. + */ +void dwc2_wait_for_mode(struct dwc2 *dwc2, bool host_mode) +{ + unsigned int timeout = 110 * MSECOND; + int ret; + + dev_vdbg(dwc2->dev, "Waiting for %s mode\n", + host_mode ? "host" : "device"); + + ret = wait_on_timeout(timeout, dwc2_is_host_mode(dwc2) == host_mode); + if (ret) + dev_err(dwc2->dev, "%s: Couldn't set %s mode\n", + __func__, host_mode ? "host" : "device"); + + dev_vdbg(dwc2->dev, "%s mode set\n", + host_mode ? "Host" : "Device"); +} + +/** + * dwc2_iddig_filter_enabled() - Returns true if the IDDIG debounce + * filter is enabled. + * + * @hsotg: Programming view of DWC_otg controller + */ +bool dwc2_iddig_filter_enabled(struct dwc2 *dwc2) +{ + u32 gsnpsid; + u32 ghwcfg4; + + /* Check if core configuration includes the IDDIG filter. */ + ghwcfg4 = dwc2_readl(dwc2, GHWCFG4); + if (!(ghwcfg4 & GHWCFG4_IDDIG_FILT_EN)) + return false; + + /* + * Check if the IDDIG debounce filter is bypassed. Available + * in core version >= 3.10a. + */ + gsnpsid = dwc2_readl(dwc2, GSNPSID); + if (gsnpsid >= DWC2_CORE_REV_3_10a) { + u32 gotgctl = dwc2_readl(dwc2, GOTGCTL); + + if (gotgctl & GOTGCTL_DBNCE_FLTR_BYPASS) + return false; + } + + return true; +} + +/* + * 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) { + dwc2_warn(dwc2, "%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. + */ + if (dwc2_iddig_filter_enabled(dwc2)) { + u32 gotgctl = dwc2_readl(dwc2, GOTGCTL); + u32 gusbcfg = dwc2_readl(dwc2, GUSBCFG); + + if (!(gotgctl & GOTGCTL_CONID_B) || + (gusbcfg & GUSBCFG_FORCEHOSTMODE)) { + 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) { + dwc2_warn(dwc2, "%s: Timeout! Waiting for Core Soft Reset\n", + __func__); + return ret; + } + + if (wait_for_host_mode) + dwc2_wait_for_mode(dwc2, wait_for_host_mode); + + 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 0000000000..a9526a8c5d --- /dev/null +++ b/drivers/usb/dwc2/core.h @@ -0,0 +1,565 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* 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_ep { + struct usb_ep ep; + struct dwc2 *dwc2; + struct list_head queue; + struct dwc2_request *req; + char name[8]; + + unsigned int size_loaded; + unsigned int last_load; + unsigned short fifo_size; + unsigned short fifo_index; + + u8 dir_in; + u8 epnum; + u8 mc; + u16 interval; + + unsigned int halted:1; + unsigned int periodic:1; + unsigned int isochronous:1; + unsigned int send_zlp:1; + unsigned int target_frame; +#define TARGET_FRAME_INITIAL 0xFFFFFFFF + bool frame_overrun; +}; + +struct dwc2_request { + struct usb_request req; + struct list_head queue; +}; + +/* Gadget ep0 states */ +enum dwc2_ep0_state { + DWC2_EP0_SETUP, + DWC2_EP0_DATA_IN, + DWC2_EP0_DATA_OUT, + DWC2_EP0_STATUS_IN, + DWC2_EP0_STATUS_OUT, +}; + +/* Size of control and EP0 buffers */ +#define DWC2_CTRL_BUFF_SIZE 8 + +struct dwc2 { + struct device *dev; + void __iomem *regs; + enum usb_dr_mode dr_mode; + struct dwc2_hw_params hw_params; + struct dwc2_core_params params; + + struct phy *phy; /* optional */ + struct clk *clk; + +#ifdef CONFIG_USB_DWC2_HOST + struct usb_host host; + u8 in_data_toggle[MAX_DEVICE][MAX_ENDPOINT]; + u8 out_data_toggle[MAX_DEVICE][MAX_ENDPOINT]; + int root_hub_devnum; +#endif + +#ifdef CONFIG_USB_DWC2_GADGET + struct usb_gadget gadget; + struct dwc2_ep *eps_in[MAX_ENDPOINT]; + struct dwc2_ep *eps_out[MAX_ENDPOINT]; + struct usb_request *ctrl_req; + void *ep0_buff; + void *ctrl_buff; + enum dwc2_ep0_state ep0_state; + struct usb_gadget_driver *driver; + + int num_eps; + u16 frame_number; + u32 fifo_map; + unsigned int dedicated_fifos:1; + unsigned int enabled:1; + unsigned int connected:1; + unsigned int is_selfpowered:1; +#endif +}; + +#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_warn((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) +{ + return wait_on_timeout(timeout * USECOND, + (dwc2_readl(dwc2, offset) & mask) == mask); +} + +/** + * 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) +{ + return wait_on_timeout(timeout * USECOND, + !(dwc2_readl(dwc2, offset) & mask)); +} + +/* + * 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; +} diff --git a/drivers/usb/dwc2/dwc2.c b/drivers/usb/dwc2/dwc2.c new file mode 100644 index 0000000000..a35fd0e717 --- /dev/null +++ b/drivers/usb/dwc2/dwc2.c @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org> + * Copyright (C) 2014 Marek Vasut <marex@denx.de> + * + * Copied from u-Boot + */ +#include <common.h> +#include <of.h> +#include <dma.h> +#include <init.h> +#include <errno.h> +#include <driver.h> +#include <linux/clk.h> +#include <linux/reset.h> +#include <of_device.h> + +#include "dwc2.h" + +static void dwc2_set_stm32mp15_fsotg_params(struct dwc2 *dwc2) +{ + struct dwc2_core_params *p = &dwc2->params; + + p->otg_cap &= ~(DWC2_CAP_PARAM_HNP_SRP_CAPABLE + | DWC2_CAP_PARAM_SRP_ONLY_CAPABLE); + p->otg_cap |= DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE; + p->speed = DWC2_SPEED_PARAM_FULL; + p->host_rx_fifo_size = 128; + p->host_nperio_tx_fifo_size = 96; + p->host_perio_tx_fifo_size = 96; + p->max_packet_count = 256; + p->phy_type = DWC2_PHY_TYPE_PARAM_FS; + p->i2c_enable = false; + p->activate_stm_fs_transceiver = true; + p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << GAHBCFG_HBSTLEN_SHIFT; + p->power_down = DWC2_POWER_DOWN_PARAM_NONE; + p->host_support_fs_ls_low_power = true; + p->host_ls_low_power_phy_clk = true; +} + +static void dwc2_set_stm32mp15_hsotg_params(struct dwc2 *dwc2) +{ + struct dwc2_core_params *p = &dwc2->params; + + p->otg_cap &= ~(DWC2_CAP_PARAM_HNP_SRP_CAPABLE + | DWC2_CAP_PARAM_SRP_ONLY_CAPABLE); + p->otg_cap |= DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE; + p->host_rx_fifo_size = 440; + p->host_nperio_tx_fifo_size = 256; + p->host_perio_tx_fifo_size = 256; + p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << GAHBCFG_HBSTLEN_SHIFT; + p->power_down = DWC2_POWER_DOWN_PARAM_NONE; + p->lpm = false; + p->lpm_clock_gating = false; + p->besl = false; + p->hird_threshold_en = false; +} + +static int dwc2_set_mode(void *ctx, enum usb_dr_mode mode) +{ + struct dwc2 *dwc2 = ctx; + int ret = -ENODEV; + int oldmode = dwc2->dr_mode; + + dwc2->dr_mode = mode; + + if (mode == USB_DR_MODE_HOST || mode == USB_DR_MODE_OTG) { + if (IS_ENABLED(CONFIG_USB_DWC2_HOST)) + ret = dwc2_register_host(dwc2); + else + dwc2_err(dwc2, "Host support not available\n"); + } + if (mode == USB_DR_MODE_PERIPHERAL || mode == USB_DR_MODE_OTG) { + if (IS_ENABLED(CONFIG_USB_DWC2_GADGET)) + ret = dwc2_gadget_init(dwc2); + else + dwc2_err(dwc2, "Peripheral support not available\n"); + } + + if (ret) + dwc2->dr_mode = oldmode; + + return ret; +} + +typedef void (*set_params_cb)(struct dwc2 *dwc2); + +static int dwc2_probe(struct device *dev) +{ + struct resource *iores; + struct dwc2 *dwc2; + set_params_cb set_params; + int ret; + + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); + dwc2 = xzalloc(sizeof(*dwc2)); + dwc2->regs = IOMEM(iores->start); + dwc2->dev = dev; + + dwc2->clk = clk_get(dev, "otg"); + if (IS_ERR(dwc2->clk)) { + ret = PTR_ERR(dwc2->clk); + dev_err(dev, "Failed to get USB clock %d\n", ret); + goto release_region; + } + + ret = clk_enable(dwc2->clk); + if (ret) + goto clk_put; + + ret = device_reset_us(dev, 2); + if (ret) + goto clk_disable; + + dwc2->phy = phy_optional_get(dev, "usb2-phy"); + if (IS_ERR(dwc2->phy)) { + ret = PTR_ERR(dwc2->phy); + goto clk_disable; + } + + ret = phy_power_on(dwc2->phy); + if (ret) + goto clk_disable; + + ret = phy_init(dwc2->phy); + if (ret) + goto phy_power_off; + + ret = dwc2_check_core_version(dwc2); + if (ret) + goto error; + + /* + * Reset before dwc2_get_hwparams() then it could get power-on real + * reset value form registers. + */ + ret = dwc2_core_reset(dwc2); + if (ret) + goto error; + + /* Detect config values from hardware */ + dwc2_get_hwparams(dwc2); + + ret = dwc2_get_dr_mode(dwc2); + if (ret) + goto error; + + dwc2_set_default_params(dwc2); + dwc2_get_device_properties(dwc2); + + set_params = of_device_get_match_data(dev); + if (set_params) + set_params(dwc2); + + dma_set_mask(dev, DMA_BIT_MASK(32)); + dev->priv = dwc2; + + if (dwc2->dr_mode == USB_DR_MODE_OTG) + ret = usb_register_otg_device(dwc2->dev, dwc2_set_mode, dwc2); + else + ret = dwc2_set_mode(dwc2, dwc2->dr_mode); + + if (ret) + goto error; + + return 0; +error: + phy_exit(dwc2->phy); +phy_power_off: + phy_power_off(dwc2->phy); +clk_disable: + clk_disable(dwc2->clk); +clk_put: + clk_put(dwc2->clk); +release_region: + release_region(iores); + + free(dwc2); + + return ret; +} + +static void dwc2_remove(struct device *dev) +{ + struct dwc2 *dwc2 = dev->priv; + + dwc2_host_uninit(dwc2); + dwc2_gadget_uninit(dwc2); + + phy_exit(dwc2->phy); + phy_power_off(dwc2->phy); +} + +static const struct of_device_id dwc2_platform_dt_ids[] = { + { .compatible = "brcm,bcm2835-usb", }, + { .compatible = "brcm,bcm2708-usb", }, + { .compatible = "snps,dwc2", }, + { .compatible = "st,stm32mp15-fsotg", + .data = dwc2_set_stm32mp15_fsotg_params }, + { .compatible = "st,stm32mp15-hsotg", + .data = dwc2_set_stm32mp15_hsotg_params }, + { } +}; +MODULE_DEVICE_TABLE(of, dwc2_platform_dt_ids); + +static struct driver 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 0000000000..2e740a890e --- /dev/null +++ b/drivers/usb/dwc2/dwc2.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#include <linux/usb/usb.h> +#include <linux/usb/usb_defs.h> +#include <linux/usb/gadget.h> +#include <linux/phy/phy.h> + +#include "regs.h" +#include "core.h" + +/* Core functions */ +void dwc2_set_default_params(struct dwc2 *dwc2); +void dwc2_get_device_properties(struct dwc2 *dwc2); +int dwc2_check_core_version(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); +void dwc2_flush_tx_fifo(struct dwc2 *dwc2, const int idx); +int dwc2_tx_fifo_count(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); + +void dwc2_wait_for_mode(struct dwc2 *dwc2, bool host_mode); +bool dwc2_iddig_filter_enabled(struct dwc2 *dwc2); + +/* Host functions */ +#ifdef CONFIG_USB_DWC2_HOST +int dwc2_submit_roothub(struct dwc2 *dwc2, struct usb_device *dev, + unsigned long pipe, void *buf, int len, + struct devrequest *setup); +int dwc2_register_host(struct dwc2 *dwc2); +void dwc2_host_uninit(struct dwc2 *dwc2); +#else +static inline int dwc2_register_host(struct dwc2 *dwc2) { return -ENODEV; } +static inline void dwc2_host_uninit(struct dwc2 *dwc2) {}; +#endif + +/* Gadget functions */ +#ifdef CONFIG_USB_DWC2_GADGET +int dwc2_gadget_init(struct dwc2 *dwc2); +void dwc2_gadget_uninit(struct dwc2 *dwc2); +#else +static inline int dwc2_gadget_init(struct dwc2 *dwc2) { return -ENODEV; } +static inline void dwc2_gadget_uninit(struct dwc2 *dwc2) {}; +#endif diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c new file mode 100644 index 0000000000..3c06c438b6 --- /dev/null +++ b/drivers/usb/dwc2/gadget.c @@ -0,0 +1,2818 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include <dma.h> +#include <linux/usb/gadget.h> +#include <linux/spinlock.h> +#include "dwc2.h" + +#define to_dwc2 gadget_to_dwc2 +#define dwc2_set_bit(d, r, b) dwc2_writel(d, (b) | dwc2_readl(d, r), r) +#define dwc2_clear_bit(d, r, b) dwc2_writel(d, ~(b) & dwc2_readl(d, r), r) + +#define local_irq_save(flags)(void)(flags) +#define local_irq_restore(flags) (void)(flags) + +static void kill_all_requests(struct dwc2 *, struct dwc2_ep *, int); + +static inline struct dwc2_ep *index_to_ep(struct dwc2 *dwc2, + unsigned int ep, int in) +{ + if (ep >= DWC2_MAX_EPS_CHANNELS) + return NULL; + if (in) + return dwc2->eps_in[ep]; + else + return dwc2->eps_out[ep]; +} + +static inline int using_dma(struct dwc2 *dwc2) +{ + /* Only buffer dma is supported */ + return 1; +} + +static void dwc2_dcfg_set_addr(struct dwc2 *dwc2, int addr) +{ + u32 dcfg = dwc2_readl(dwc2, DCFG); + + dcfg &= ~DCFG_DEVADDR_MASK; + dcfg |= (addr << DCFG_DEVADDR_SHIFT) & DCFG_DEVADDR_MASK; + dwc2_writel(dwc2, dcfg, DCFG); +} + +/** + * dwc2_hsotg_ctrl_epint - enable/disable an endpoint irq + * @hsotg: The device state + * @ep: The endpoint index + * @dir_in: True if direction is in. + * @en: The enable value, true to enable + * + * Set or clear the mask for an individual endpoint's interrupt + * request. + */ +static void dwc2_hsotg_ctrl_epint(struct dwc2 *dwc2, + unsigned int ep, unsigned int dir_in, + unsigned int en) +{ + unsigned long flags; + u32 bit = 1 << ep; + u32 daint; + + if (!dir_in) + bit <<= 16; + + local_irq_save(flags); + daint = dwc2_readl(dwc2, DAINTMSK); + if (en) + daint |= bit; + else + daint &= ~bit; + dwc2_writel(dwc2, daint, DAINTMSK); + local_irq_restore(flags); + +} + +/** + * get_ep_head - return the first request on the endpoint + * @hs_ep: The controller endpoint to get + * + * Get the first request on the endpoint. + */ +static struct dwc2_request *get_ep_head(struct dwc2_ep *hs_ep) +{ + return list_first_entry_or_null(&hs_ep->queue, struct dwc2_request, + queue); +} + +/** + * get_ep_limit - get the maximum data legnth for this endpoint + * @hs_ep: The endpoint + * + * Return the maximum data that can be queued in one go on a given endpoint + * so that transfers that are too long can be split. + */ +static unsigned int get_ep_limit(struct dwc2_ep *hs_ep) +{ + int index = hs_ep->epnum; + unsigned int maxsize; + unsigned int maxpkt; + + if (index != 0) { + maxsize = DXEPTSIZ_XFERSIZE_LIMIT + 1; + maxpkt = DXEPTSIZ_PKTCNT_LIMIT + 1; + } else { + maxsize = 64 + 64; + if (hs_ep->dir_in) + maxpkt = DIEPTSIZ0_PKTCNT_LIMIT + 1; + else + maxpkt = 2; + } + + /* we made the constant loading easier above by using +1 */ + maxpkt--; + maxsize--; + + /* + * constrain by packet count if maxpkts*pktsize is greater + * than the length register size. + */ + + if ((maxpkt * hs_ep->ep.maxpacket) < maxsize) + maxsize = maxpkt * hs_ep->ep.maxpacket; + + return maxsize; +} + +static u32 dwc2_read_frameno(struct dwc2 *dwc2) +{ + u32 dsts; + + dsts = dwc2_readl(dwc2, DSTS); + dsts &= DSTS_SOFFN_MASK; + dsts >>= DSTS_SOFFN_SHIFT; + + return dsts; +} + +/** + * dwc2_gadget_incr_frame_num - Increments the targeted frame number. + * @hs_ep: The endpoint + * + * This function will also check if the frame number overruns DSTS_SOFFN_LIMIT. + * If an overrun occurs it will wrap the value and set the frame_overrun flag. + */ +static inline void dwc2_gadget_incr_frame_num(struct dwc2_ep *hs_ep) +{ + hs_ep->target_frame += hs_ep->interval; + if (hs_ep->target_frame > DSTS_SOFFN_LIMIT) { + hs_ep->frame_overrun = true; + hs_ep->target_frame &= DSTS_SOFFN_LIMIT; + } else { + hs_ep->frame_overrun = false; + } +} + +/** + * dwc2_gadget_target_frame_elapsed - Checks target frame + * @hs_ep: The driver endpoint to check + * + * Returns 1 if targeted frame elapsed. If returned 1 then we need to drop + * corresponding transfer. + */ +static bool dwc2_gadget_target_frame_elapsed(struct dwc2_ep *hs_ep) +{ + struct dwc2 *dwc2 = hs_ep->dwc2; + u32 target_frame = hs_ep->target_frame; + u32 current_frame = dwc2->frame_number; + bool frame_overrun = hs_ep->frame_overrun; + + if (!frame_overrun && current_frame >= target_frame) + return true; + + if (frame_overrun && current_frame >= target_frame && + ((current_frame - target_frame) < DSTS_SOFFN_LIMIT / 2)) + return true; + + return false; +} + + +/** + * dwc2_gadget_start_req - start a USB request from an endpoint's queue + * @dwc2: The controller state. + * @hs_ep: The endpoint to process a request for + * @hs_req: The request to start. + * @continuing: True if we are doing more for the current request. + * + * Start the given request running by setting the endpoint registers + * appropriately, and writing any data to the FIFOs. + */ +static void dwc2_gadget_start_req(struct dwc2 *dwc2, + struct dwc2_ep *hs_ep, + struct dwc2_request *hs_req, + bool continuing) +{ + struct usb_request *ureq = &hs_req->req; + int index = hs_ep->epnum; + int dir_in = hs_ep->dir_in; + u32 epctrl_reg; + u32 epsize_reg; + u32 epsize; + u32 ctrl; + unsigned int length; + unsigned int packets; + unsigned int maxreq; + unsigned int dma_reg; + + if (index != 0) { + if (hs_ep->req && !continuing) { + dwc2_err(dwc2, "%s: active request\n", __func__); + WARN_ON(1); + return; + } else if (hs_ep->req != hs_req && continuing) { + dwc2_err(dwc2, + "%s: continue different req\n", __func__); + WARN_ON(1); + return; + } + } + + dma_reg = dir_in ? DIEPDMA(index) : DOEPDMA(index); + epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index); + epsize_reg = dir_in ? DIEPTSIZ(index) : DOEPTSIZ(index); + + dwc2_dbg(dwc2, "%s: DxEPCTL=0x%08x, ep %d, dir %s\n", + __func__, dwc2_readl(dwc2, epctrl_reg), index, + hs_ep->dir_in ? "in" : "out"); + + /* If endpoint is stalled, we will restart request later */ + ctrl = dwc2_readl(dwc2, epctrl_reg); + + if (index && ctrl & DXEPCTL_STALL) { + dwc2_warn(dwc2, "%s: ep%d is stalled\n", __func__, index); + return; + } + + length = ureq->length - ureq->actual; + dwc2_dbg(dwc2, "ureq->length:%d ureq->actual:%d\n", + ureq->length, ureq->actual); + + maxreq = get_ep_limit(hs_ep); + + if (length > maxreq) { + int round = maxreq % hs_ep->ep.maxpacket; + + dwc2_dbg(dwc2, "%s: length %d, max-req %d, r %d\n", + __func__, length, maxreq, round); + + /* round down to multiple of packets */ + if (round) + maxreq -= round; + + length = maxreq; + } + + if (length) + packets = DIV_ROUND_UP(length, hs_ep->ep.maxpacket); + else + packets = 1; /* send one packet if length is zero. */ + + if (dir_in && index != 0) + if (hs_ep->isochronous) + epsize = DXEPTSIZ_MC(packets); + else + epsize = DXEPTSIZ_MC(1); + else + epsize = 0; + + /* + * zero length packet should be programmed on its own and should not + * be counted in DIEPTSIZ.PktCnt with other packets. + */ + if (dir_in && ureq->zero && !continuing) { + /* Test if zlp is actually required. */ + if ((ureq->length >= hs_ep->ep.maxpacket) && + !(ureq->length % hs_ep->ep.maxpacket)) + hs_ep->send_zlp = 1; + } + + epsize |= DXEPTSIZ_PKTCNT(packets); + epsize |= DXEPTSIZ_XFERSIZE(length); + + dwc2_dbg(dwc2, "%s: %d@%d/%d, 0x%08x => 0x%08x\n", + __func__, packets, length, ureq->length, epsize, epsize_reg); + + /* store the request as the current one we're doing */ + hs_ep->req = hs_req; + + /* write size / packets */ + dwc2_writel(dwc2, epsize, epsize_reg); + + if (using_dma(dwc2) && !continuing && (length != 0)) { + /* + * write DMA address to control register, buffer + * already synced by dwc2_ep_queue(). + */ + + dwc2_writel(dwc2, ureq->dma, dma_reg); + + dwc2_dbg(dwc2, "%s: 0x%pad => 0x%08x\n", + __func__, &ureq->dma, dma_reg); + } + + if (hs_ep->isochronous && hs_ep->interval == 1) { + hs_ep->target_frame = dwc2_read_frameno(dwc2); + dwc2_gadget_incr_frame_num(hs_ep); + + if (hs_ep->target_frame & 0x1) + ctrl |= DXEPCTL_SETODDFR; + else + ctrl |= DXEPCTL_SETEVENFR; + } + + ctrl |= DXEPCTL_EPENA; /* ensure ep enabled */ + + dwc2_dbg(dwc2, "%s: ep0 state:%d\n", __func__, dwc2->ep0_state); + + /* For Setup request do not clear NAK */ + if (!(index == 0 && dwc2->ep0_state == DWC2_EP0_SETUP)) + ctrl |= DXEPCTL_CNAK; /* clear NAK set by core */ + + dwc2_dbg(dwc2, "%s: DxEPCTL=0x%08x\n", __func__, ctrl); + dwc2_writel(dwc2, ctrl, epctrl_reg); + + /* + * set these, it seems that DMA support increments past the end + * of the packet buffer so we need to calculate the length from + * this information. + */ + hs_ep->size_loaded = length; + hs_ep->last_load = ureq->actual; + + /* + * Note, trying to clear the NAK here causes problems with transmit + * on the S3C6400 ending up with the TXFIFO becoming full. + */ + + /* check ep is enabled */ + if (!(dwc2_readl(dwc2, epctrl_reg) & DXEPCTL_EPENA)) + dwc2_dbg(dwc2, + "ep%d: failed to become enabled (DXEPCTL=0x%08x)?\n", + index, dwc2_readl(dwc2, epctrl_reg)); + + dwc2_dbg(dwc2, "%s: DXEPCTL=0x%08x\n", + __func__, dwc2_readl(dwc2, epctrl_reg)); + + /* enable ep interrupts */ + dwc2_hsotg_ctrl_epint(dwc2, hs_ep->epnum, hs_ep->dir_in, 1); +} + +/* conversion functions */ +static inline struct dwc2_request *our_req(struct usb_request *req) +{ + return container_of(req, struct dwc2_request, req); +} + +static inline struct dwc2_ep *our_ep(struct usb_ep *ep) +{ + return container_of(ep, struct dwc2_ep, ep); +} + + +/** + * dwc2_ep0_mps - turn max packet size into register setting + * @mps: The maximum packet size in bytes. + */ +static u32 dwc2_ep0_mps(unsigned int mps) +{ + switch (mps) { + case 64: + return D0EPCTL_MPS_64; + case 32: + return D0EPCTL_MPS_32; + case 16: + return D0EPCTL_MPS_16; + case 8: + return D0EPCTL_MPS_8; + } + + /* bad max packet size, warn and return invalid result */ + WARN_ON(1); + return (u32)-1; +} + +/** + * dwc2_set_ep_maxpacket - set endpoint's max-packet field + * @dwc2: The driver state. + * @ep: The index number of the endpoint + * @mps: The maximum packet size in bytes + * @mc: The multicount value + * @dir_in: True if direction is in. + * + * Configure the maximum packet size for the given endpoint, updating + * the hardware control registers to reflect this. + */ +static void dwc2_set_ep_maxpacket(struct dwc2 *dwc2, + unsigned int ep, unsigned int mps, + unsigned int mc, unsigned int dir_in) +{ + struct dwc2_ep *hs_ep; + u32 reg; + + hs_ep = index_to_ep(dwc2, ep, dir_in); + if (!hs_ep) + return; + + if (ep == 0) { + u32 mps_bytes = mps; + + /* EP0 is a special case */ + mps = dwc2_ep0_mps(mps_bytes); + if (mps > 3) + goto bad_mps; + hs_ep->ep.maxpacket = mps_bytes; + hs_ep->mc = 1; + } else { + if (mps > 1024) + goto bad_mps; + hs_ep->mc = mc; + if (mc > 3) + goto bad_mps; + hs_ep->ep.maxpacket = mps; + } + + if (dir_in) { + reg = dwc2_readl(dwc2, DIEPCTL(ep)); + reg &= ~DXEPCTL_MPS_MASK; + reg |= mps; + dwc2_writel(dwc2, reg, DIEPCTL(ep)); + } else { + reg = dwc2_readl(dwc2, DOEPCTL(ep)); + reg &= ~DXEPCTL_MPS_MASK; + reg |= mps; + dwc2_writel(dwc2, reg, DOEPCTL(ep)); + } + + return; + +bad_mps: + dwc2_err(dwc2, "ep%d: bad mps of %d\n", ep, mps); +} + +static int dwc2_ep_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + struct dwc2_ep *hs_ep = our_ep(ep); + struct dwc2 *dwc2 = hs_ep->dwc2; + unsigned long flags; + unsigned int index = hs_ep->epnum; + u32 epctrl_reg; + u32 epctrl; + u32 mps; + u32 mc; + u32 mask; + unsigned int dir_in; + unsigned int i, val, size; + int ret = 0; + unsigned char ep_type; + + dwc2_dbg(dwc2, + "%s: ep %s: a 0x%02x, attr 0x%02x, mps 0x%04x, intr %d\n", + __func__, ep->name, desc->bEndpointAddress, desc->bmAttributes, + desc->wMaxPacketSize, desc->bInterval); + + /* not to be called for EP0 */ + if (index == 0) { + dwc2_err(dwc2, "%s: called for EP 0\n", __func__); + return -EINVAL; + } + + dir_in = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? 1 : 0; + if (dir_in != hs_ep->dir_in) { + dwc2_err(dwc2, "%s: direction mismatch!\n", __func__); + return -EINVAL; + } + + ep_type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + mps = usb_endpoint_maxp(desc) & USB_ENDPOINT_MAXP_MASK; + mc = usb_endpoint_maxp_mult(desc); + + /* note, we handle this here instead of dwc2_set_ep_maxpacket */ + epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index); + epctrl = dwc2_readl(dwc2, epctrl_reg); + + dwc2_dbg(dwc2, "%s: read DxEPCTL=0x%08x from 0x%08x\n", + __func__, epctrl, epctrl_reg); + + spin_lock_irqsave(&dwc2->lock, flags); + + epctrl &= ~(DXEPCTL_EPTYPE_MASK | DXEPCTL_MPS_MASK); + epctrl |= DXEPCTL_MPS(mps); + + /* + * mark the endpoint as active, otherwise the core may ignore + * transactions entirely for this endpoint + */ + epctrl |= DXEPCTL_USBACTEP; + + /* update the endpoint state */ + dwc2_set_ep_maxpacket(dwc2, index, mps, mc, dir_in); + + /* default, set to non-periodic */ + hs_ep->isochronous = 0; + hs_ep->periodic = 0; + hs_ep->halted = 0; + hs_ep->interval = desc->bInterval; + + switch (ep_type) { + case USB_ENDPOINT_XFER_ISOC: + epctrl |= DXEPCTL_EPTYPE_ISO; + epctrl |= DXEPCTL_SETEVENFR; + hs_ep->isochronous = 1; + hs_ep->interval = 1 << (desc->bInterval - 1); + hs_ep->target_frame = TARGET_FRAME_INITIAL; + if (dir_in) { + hs_ep->periodic = 1; + mask = dwc2_readl(dwc2, DIEPMSK); + mask |= DIEPMSK_NAKMSK; + dwc2_writel(dwc2, mask, DIEPMSK); + } else { + mask = dwc2_readl(dwc2, DOEPMSK); + mask |= DOEPMSK_OUTTKNEPDISMSK; + dwc2_writel(dwc2, mask, DOEPMSK); + } + break; + + case USB_ENDPOINT_XFER_BULK: + epctrl |= DXEPCTL_EPTYPE_BULK; + break; + + case USB_ENDPOINT_XFER_INT: + if (dir_in) + hs_ep->periodic = 1; + + if (dwc2->gadget.speed == USB_SPEED_HIGH) + hs_ep->interval = 1 << (desc->bInterval - 1); + + epctrl |= DXEPCTL_EPTYPE_INTERRUPT; + break; + + case USB_ENDPOINT_XFER_CONTROL: + epctrl |= DXEPCTL_EPTYPE_CONTROL; + break; + } + + /* + * if the hardware has dedicated fifos, we must give each IN EP + * a unique tx-fifo even if it is non-periodic. + */ + if (dir_in && dwc2->dedicated_fifos) { + unsigned int fifo_count = dwc2_tx_fifo_count(dwc2); + u32 fifo_index = 0; + u32 fifo_size = UINT_MAX; + + size = hs_ep->ep.maxpacket * hs_ep->mc; + for (i = 1; i <= fifo_count; i++) { + if (dwc2->fifo_map & (1 << i)) + continue; + + val = dwc2_readl(dwc2, DPTXFSIZN(i)); + val = (val >> FIFOSIZE_DEPTH_SHIFT) * 4; + if (val < size) + continue; + /* Search for smallest acceptable fifo */ + if (val < fifo_size) { + fifo_size = val; + fifo_index = i; + } + } + if (!fifo_index) { + dwc2_err(dwc2, + "%s: No suitable fifo found\n", __func__); + ret = -ENOMEM; + goto error; + } + epctrl &= ~(DXEPCTL_TXFNUM_LIMIT << DXEPCTL_TXFNUM_SHIFT); + epctrl |= DXEPCTL_TXFNUM(fifo_index); + dwc2->fifo_map |= 1 << fifo_index; + hs_ep->fifo_index = fifo_index; + hs_ep->fifo_size = fifo_size; + } + + /* for non control endpoints, set PID to D0 */ + if (index && !hs_ep->isochronous) + epctrl |= DXEPCTL_SETD0PID; + + dwc2_dbg(dwc2, "%s: write DxEPCTL=0x%08x\n", + __func__, epctrl); + + dwc2_writel(dwc2, epctrl, epctrl_reg); + dwc2_dbg(dwc2, "%s: read DxEPCTL=0x%08x\n", + __func__, dwc2_readl(dwc2, epctrl_reg)); + + /* enable the endpoint interrupt */ + dwc2_hsotg_ctrl_epint(dwc2, index, dir_in, 1); + +error: + spin_unlock_irqrestore(&dwc2->lock, flags); + + return ret; +} + +static void dwc2_ep_stop_xfr(struct dwc2 *dwc2, struct dwc2_ep *hs_ep) +{ + int in = hs_ep->dir_in; + int epnum = hs_ep->epnum; + u32 epctl_reg = in ? DIEPCTL(epnum) : DOEPCTL(epnum); + u32 epint_reg = in ? DIEPINT(epnum) : DOEPINT(epnum); + + dwc2_dbg(dwc2, "%s: stopping transfer on %s\n", __func__, + hs_ep->name); + + if (in) { + if (dwc2->dedicated_fifos || hs_ep->periodic) { + dwc2_set_bit(dwc2, epctl_reg, DXEPCTL_SNAK); + /* Wait for Nak effect */ + if (dwc2_wait_bit_set(dwc2, epint_reg, + DXEPINT_INEPNAKEFF, 100)) + dwc2_warn(dwc2, + "%s: timeout DIEPINT.NAKEFF\n", + __func__); + } else { + dwc2_set_bit(dwc2, DCTL, DCTL_SGNPINNAK); + /* Wait for Nak effect */ + if (dwc2_wait_bit_set(dwc2, GINTSTS, + GINTSTS_GINNAKEFF, 100)) + dwc2_warn(dwc2, + "%s: timeout GINTSTS.GINNAKEFF\n", + __func__); + } + } else { + if (!(dwc2_readl(dwc2, GINTSTS) & GINTSTS_GOUTNAKEFF)) + dwc2_set_bit(dwc2, DCTL, DCTL_SGOUTNAK); + + /* Wait for global nak to take effect */ + if (dwc2_wait_bit_set(dwc2, GINTSTS, + GINTSTS_GOUTNAKEFF, 100)) + dwc2_warn(dwc2, "%s: timeout GINTSTS.GOUTNAKEFF\n", + __func__); + } + + /* Disable ep */ + dwc2_set_bit(dwc2, epctl_reg, DXEPCTL_EPDIS | DXEPCTL_SNAK); + + /* Wait for ep to be disabled */ + if (dwc2_wait_bit_set(dwc2, epint_reg, DXEPINT_EPDISBLD, 100)) + dwc2_warn(dwc2, "%s: timeout DOEPCTL.EPDisable\n", __func__); + + /* Clear EPDISBLD interrupt */ + dwc2_set_bit(dwc2, epint_reg, DXEPINT_EPDISBLD); + + if (in) { + unsigned short fifo_index; + + if (dwc2->dedicated_fifos || hs_ep->periodic) + fifo_index = hs_ep->fifo_index; + else + fifo_index = 0; + + dwc2_flush_tx_fifo(dwc2, fifo_index); + + /* Clear Global In NP NAK in Shared FIFO for non periodic ep */ + if (!dwc2->dedicated_fifos && !hs_ep->periodic) + dwc2_set_bit(dwc2, DCTL, DCTL_CGNPINNAK); + + } else { + /* Remove global NAKs */ + dwc2_set_bit(dwc2, DCTL, DCTL_CGOUTNAK); + } +} + +static int dwc2_ep_disable(struct usb_ep *ep) +{ + struct dwc2_ep *hs_ep = our_ep(ep); + struct dwc2 *dwc2 = hs_ep->dwc2; + int dir_in = hs_ep->dir_in; + int index = hs_ep->epnum; + u32 epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index); + u32 ctrl; + + dwc2_dbg(dwc2, "%s(ep %p)\n", __func__, ep); + + if (index == 0) { + dwc2_err(dwc2, "%s: called for ep0\n", __func__); + return -EINVAL; + } + + ctrl = dwc2_readl(dwc2, epctrl_reg); + if (ctrl & DXEPCTL_EPENA) + dwc2_ep_stop_xfr(dwc2, hs_ep); + + ctrl &= ~DXEPCTL_EPENA; + ctrl &= ~DXEPCTL_USBACTEP; + ctrl |= DXEPCTL_SNAK; + + dwc2_dbg(dwc2, "%s: DxEPCTL=0x%08x\n", __func__, ctrl); + dwc2_writel(dwc2, ctrl, epctrl_reg); + + /* disable endpoint interrupts */ + dwc2_hsotg_ctrl_epint(dwc2, index, hs_ep->dir_in, 0); + + /* terminate all requests with shutdown */ + kill_all_requests(dwc2, hs_ep, -ESHUTDOWN); + + dwc2->fifo_map &= ~(1 << hs_ep->fifo_index); + hs_ep->fifo_index = 0; + hs_ep->fifo_size = 0; + + return 0; +} + +static struct usb_request *dwc2_ep_alloc_req(struct usb_ep *ep) +{ + struct dwc2_request *req; + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return NULL; + + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void dwc2_ep_free_req(struct usb_ep *ep, struct usb_request *req) +{ + struct dwc2_request *hs_req = our_req(req); + + kfree(hs_req); +} + +/** + * dwc2_start_next_request - Starts next request from ep queue + * @hs_ep: Endpoint structure + * + * If queue is empty and EP is ISOC-OUT - unmasks OUTTKNEPDIS which is masked + * in its handler. Hence we need to unmask it here to be able to do + * resynchronization. + */ +static void dwc2_start_next_request(struct dwc2_ep *hs_ep) +{ + u32 mask; + struct dwc2 *dwc2 = hs_ep->dwc2; + int dir_in = hs_ep->dir_in; + struct dwc2_request *hs_req; + u32 epmsk_reg = dir_in ? DIEPMSK : DOEPMSK; + + dwc2_dbg(dwc2, "%s: next req\n", __func__); + + if (!list_empty(&hs_ep->queue)) { + hs_req = get_ep_head(hs_ep); + dwc2_gadget_start_req(dwc2, hs_ep, hs_req, false); + return; + } + if (!hs_ep->isochronous) + return; + + if (dir_in) { + dwc2_dbg(dwc2, "%s: No more ISOC-IN requests\n", __func__); + } else { + dwc2_dbg(dwc2, "%s: No more ISOC-OUT requests\n", __func__); + mask = dwc2_readl(dwc2, epmsk_reg); + mask |= DOEPMSK_OUTTKNEPDISMSK; + dwc2_writel(dwc2, mask, epmsk_reg); + } +} + +static int dwc2_ep_queue(struct usb_ep *ep, struct usb_request *req) +{ + + struct dwc2_request *hs_req = our_req(req); + struct dwc2_ep *hs_ep = our_ep(ep); + struct dwc2 *dwc2 = hs_ep->dwc2; + bool first; + + dwc2_dbg(dwc2, "%s: req %p: %d@%p, noi=%d, zero=%d, snok=%d\n", + ep->name, req, req->length, req->buf, req->no_interrupt, + req->zero, req->short_not_ok); + + /* initialise status of the request */ + INIT_LIST_HEAD(&hs_req->queue); + req->actual = 0; + req->status = -EINPROGRESS; + + /* Don't queue ISOC request if length greater than mps*mc */ + if (hs_ep->isochronous && + req->length > (hs_ep->mc * hs_ep->ep.maxpacket)) { + dwc2_err(dwc2, "req length > maxpacket*mc\n"); + return -EINVAL; + } + + if ((long)req->buf & 3) + dwc2_err(dwc2, "dma buffer not aligned\n"); + + req->dma = dma_map_single(dwc2->dev, req->buf, req->length, + hs_ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + if (dma_mapping_error(dwc2->dev, req->dma)) { + dwc2_err(dwc2, "failed to map buffer\n"); + return -EFAULT; + } + + first = list_empty(&hs_ep->queue); + list_add_tail(&hs_req->queue, &hs_ep->queue); + + /* Change EP direction if status phase request is after data out */ + if (hs_ep->epnum == 0 && !req->length && !hs_ep->dir_in && + dwc2->ep0_state == DWC2_EP0_DATA_OUT) + hs_ep->dir_in = 1; + + if (first) { + if (!hs_ep->isochronous) { + dwc2_gadget_start_req(dwc2, hs_ep, hs_req, false); + return 0; + } + + /* Update current frame number value. */ + dwc2->frame_number = dwc2_read_frameno(dwc2); + while (dwc2_gadget_target_frame_elapsed(hs_ep)) { + dwc2_gadget_incr_frame_num(hs_ep); + /* Update current frame number value once more as it + * changes here. + */ + dwc2->frame_number = dwc2_read_frameno(dwc2); + } + + if (hs_ep->target_frame != TARGET_FRAME_INITIAL) + dwc2_gadget_start_req(dwc2, hs_ep, hs_req, false); + } + return 0; +} + +static int dwc2_ep_dequeue(struct usb_ep *ep, struct usb_request *req) +{ + struct dwc2_ep *hs_ep = our_ep(ep); + struct dwc2 *dwc2 = hs_ep->dwc2; + dwc2_warn(dwc2, "%s\n", __func__); + return -EOPNOTSUPP; +} + +static int dwc2_ep_set_halt(struct usb_ep *ep, int value) +{ + struct dwc2_ep *hs_ep = our_ep(ep); + struct dwc2 *dwc2 = hs_ep->dwc2; + dwc2_warn(dwc2, "%s\n", __func__); + return -EOPNOTSUPP; +} + +static int dwc2_ep_set_wedge(struct usb_ep *ep) +{ + struct dwc2_ep *hs_ep = our_ep(ep); + struct dwc2 *dwc2 = hs_ep->dwc2; + dwc2_warn(dwc2, "%s\n", __func__); + return -EOPNOTSUPP; +} + +static int dwc2_ep_fifo_status(struct usb_ep *ep) +{ + struct dwc2_ep *hs_ep = our_ep(ep); + struct dwc2 *dwc2 = hs_ep->dwc2; + dwc2_warn(dwc2, "%s\n", __func__); + return -EOPNOTSUPP; +} + +static void dwc2_ep_fifo_flush(struct usb_ep *ep) +{ + struct dwc2_ep *hs_ep = our_ep(ep); + struct dwc2 *dwc2 = hs_ep->dwc2; + dwc2_warn(dwc2, "%s\n", __func__); +} + +static const struct usb_ep_ops dwc2_ep_ops = { + .enable = dwc2_ep_enable, + .disable = dwc2_ep_disable, + .alloc_request = dwc2_ep_alloc_req, + .free_request = dwc2_ep_free_req, + .queue = dwc2_ep_queue, + .dequeue = dwc2_ep_dequeue, + .set_halt = dwc2_ep_set_halt, + .set_wedge = dwc2_ep_set_wedge, + .fifo_status = dwc2_ep_fifo_status, + .fifo_flush = dwc2_ep_fifo_flush, +}; + +static void dwc2_program_zlp(struct dwc2 *dwc2, struct dwc2_ep *hs_ep) +{ + u32 ctrl; + u8 index = hs_ep->epnum; + u32 epctl_reg = hs_ep->dir_in ? DIEPCTL(index) : DOEPCTL(index); + u32 epsiz_reg = hs_ep->dir_in ? DIEPTSIZ(index) : DOEPTSIZ(index); + + if (hs_ep->dir_in) + dwc2_dbg(dwc2, "Sending zero-length packet on ep%d\n", index); + else + dwc2_dbg(dwc2, "Receiving zero-length packet on ep%d\n", index); + + dwc2_writel(dwc2, DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) | + DXEPTSIZ_XFERSIZE(0), + epsiz_reg); + + ctrl = dwc2_readl(dwc2, epctl_reg); + ctrl |= DXEPCTL_CNAK; /* clear NAK set by core */ + ctrl |= DXEPCTL_EPENA; /* ensure ep enabled */ + ctrl |= DXEPCTL_USBACTEP; + dwc2_writel(dwc2, ctrl, epctl_reg); +} + +/** + * windex_to_ep - convert control wIndex value to endpoint + * @dwc2: The driver state. + * @windex: The control request wIndex field (in host order). + * + * Convert the given wIndex into a pointer to an driver endpoint + * structure, or return NULL if it is not a valid endpoint. + */ +static struct dwc2_ep *windex_to_ep(struct dwc2 *dwc2, u16 windex) +{ + struct dwc2_ep *ep; + int dir = (windex & USB_DIR_IN) ? 1 : 0; + int idx = windex & 0x7F; + + if (windex >= 0x100) + return NULL; + + if (idx >= dwc2->num_eps) + return NULL; + + ep = index_to_ep(dwc2, idx, dir); + + if (idx && ep->dir_in != dir) + return NULL; + + return ep; +} + +/** + * dwc2_hsotg_send_reply - send reply to control request + * @dwc2: The device state + * @buff: Buffer for request + * @length: Length of reply. + * + * Create a request and queue it on the given endpoint. This is useful as + * an internal method of sending replies to certain control requests, etc. + */ +static int dwc2_hsotg_send_reply(struct dwc2 *dwc2, void *buff, int length) +{ + struct dwc2_ep *ep0 = dwc2->eps_out[0]; + struct usb_request *req; + int ret; + + if (length == 0 && (dwc2->ep0_state == DWC2_EP0_STATUS_IN || + dwc2->ep0_state == DWC2_EP0_STATUS_OUT)) { + dwc2_program_zlp(dwc2, ep0); + return 0; + } + + dwc2_dbg(dwc2, "%s: buff %p, len %d\n", __func__, buff, length); + + req = dwc2_ep_alloc_req(&ep0->ep); + if (!req) { + dwc2_warn(dwc2, "%s: cannot alloc req\n", __func__); + return -ENOMEM; + } + + req->buf = dwc2->ep0_buff; + req->length = length; + /* + * zero flag is for sending zlp in DATA IN stage. It has no impact on + * STATUS stage. + */ + req->zero = 0; + req->complete = dwc2_ep_free_req; + + if (length) + memcpy(req->buf, buff, length); + + ret = dwc2_ep_queue(&ep0->ep, req); + if (ret) { + dwc2_warn(dwc2, "%s: cannot queue req\n", __func__); + return ret; + } + + return 0; +} + +/** + * dwc2_process_req_status - process request GET_STATUS + * @dwc2: The device state + * @ctrl: USB control request + */ +static int dwc2_process_req_status(struct dwc2 *dwc2, + struct usb_ctrlrequest *ctrl) +{ + struct dwc2_ep *ep0 = dwc2->eps_out[0]; + struct dwc2_ep *ep; + __le16 reply; + u16 status; + int ret; + + dwc2_dbg(dwc2, "%s: USB_REQ_GET_STATUS\n", __func__); + + if (!ep0->dir_in) { + dwc2_warn(dwc2, "%s: direction out?\n", __func__); + return -EINVAL; + } + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + status = dwc2->is_selfpowered << USB_DEVICE_SELF_POWERED; + status |= 0 << USB_DEVICE_REMOTE_WAKEUP; + + reply = cpu_to_le16(status); + break; + + case USB_RECIP_INTERFACE: + /* currently, the data result should be zero */ + reply = cpu_to_le16(0); + break; + + case USB_RECIP_ENDPOINT: + ep = windex_to_ep(dwc2, le16_to_cpu(ctrl->wIndex)); + if (!ep) + return -ENOENT; + + reply = cpu_to_le16(ep->halted ? 1 : 0); + break; + + default: + return 0; + } + + if (le16_to_cpu(ctrl->wLength) != 2) + return -EINVAL; + + ret = dwc2_hsotg_send_reply(dwc2, &reply, 2); + if (ret) { + dwc2_err(dwc2, "%s: failed to send reply\n", __func__); + return ret; + } + + return 1; +} + +/** + * dwc2_process_req_feature - process request {SET,CLEAR}_FEATURE + * @dwc2: The device state + * @ctrl: USB control request + */ +static int dwc2_process_req_feature(struct dwc2 *dwc2, + struct usb_ctrlrequest *ctrl) +{ + struct dwc2_request *hs_req; + bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE); + struct dwc2_ep *ep; + int ret; + bool halted; + u32 recip; + u16 wValue; + u16 wIndex; + + dwc2_dbg(dwc2, "%s: %s_FEATURE\n", __func__, set ? "SET" : "CLEAR"); + + wValue = le16_to_cpu(ctrl->wValue); + wIndex = le16_to_cpu(ctrl->wIndex); + recip = ctrl->bRequestType & USB_RECIP_MASK; + + switch (recip) { + case USB_RECIP_DEVICE: + switch (wValue) { + case USB_DEVICE_REMOTE_WAKEUP: + case USB_DEVICE_TEST_MODE: + /* Not supported */ + default: + return -ENOENT; + } + break; + + case USB_RECIP_ENDPOINT: + ep = windex_to_ep(dwc2, wIndex); + if (!ep) + return -ENOENT; + + switch (wValue) { + case USB_ENDPOINT_HALT: + halted = ep->halted; + + dwc2_ep_set_halt(&ep->ep, set); + + ret = dwc2_hsotg_send_reply(dwc2, NULL, 0); + if (ret) { + dwc2_err(dwc2, + "%s: failed to send reply\n", __func__); + return ret; + } + + /* + * we have to complete all requests for ep if it was + * halted, and the halt was cleared by CLEAR_FEATURE + */ + + if (!set && halted) { + /* + * If we have request in progress, + * then complete it + */ + if (ep->req) { + hs_req = ep->req; + ep->req = NULL; + list_del_init(&hs_req->queue); + if (hs_req->req.complete) { + spin_unlock(&dwc2->lock); + hs_req->req.complete( + &ep->ep, &hs_req->req); + spin_lock(&dwc2->lock); + } + } + + /* If we have pending request, then start it */ + if (!ep->req) + dwc2_start_next_request(ep); + } + + break; + + default: + return -ENOENT; + } + break; + default: + return -ENOENT; + } + return 1; +} + +static int dwc2_gadget_get_frame(struct usb_gadget *gadget) +{ + return dwc2_read_frameno(to_dwc2(gadget)); +} + +static void dwc2_core_disconnect(struct dwc2 *dwc2) +{ + u32 dctl; + + dctl = dwc2_readl(dwc2, DCTL); + /* set the soft-disconnect bit */ + dctl |= DCTL_SFTDISCON; + dwc2_writel(dwc2, dctl, DCTL); +} + +static void dwc2_core_connect(struct dwc2 *dwc2) +{ + u32 dctl; + + dctl = dwc2_readl(dwc2, DCTL); + /* clear the soft-disconnect bit */ + dctl &= ~DCTL_SFTDISCON; + dwc2_writel(dwc2, dctl, DCTL); +} + +static void dwc2_enqueue_setup(struct dwc2 *dwc2); + +/** + * dwc2_stall_ep0 - stall ep0 + * @dwc2: The device state + * + * Set stall for ep0 as response for setup request. + */ +static void dwc2_stall_ep0(struct dwc2 *dwc2) +{ + struct dwc2_ep *ep0 = dwc2->eps_out[0]; + u32 reg; + u32 ctrl; + + dwc2_dbg(dwc2, "ep0 stall (dir=%d)\n", ep0->dir_in); + reg = (ep0->dir_in) ? DIEPCTL0 : DOEPCTL0; + + /* + * DxEPCTL_Stall will be cleared by EP once it has + * taken effect, so no need to clear later. + */ + + ctrl = dwc2_readl(dwc2, reg); + ctrl |= DXEPCTL_STALL; + ctrl |= DXEPCTL_CNAK; + dwc2_writel(dwc2, ctrl, reg); + + dwc2_dbg(dwc2, + "written DXEPCTL=0x%08x to %08x (DXEPCTL=0x%08x)\n", + ctrl, reg, dwc2_readl(dwc2, reg)); + + /* + * complete won't be called, so we enqueue + * setup request here + */ + dwc2_enqueue_setup(dwc2); +} + +/** + * dwc2_process_control - process a control request + * @dwc2: The device state + * @ctrl: The control request received + * + * The controller has received the SETUP phase of a control request, and + * needs to work out what to do next (and whether to pass it on to the + * gadget driver). + */ +static void dwc2_process_control(struct dwc2 *dwc2, + struct usb_ctrlrequest *ctrl) +{ + struct dwc2_ep *ep0 = dwc2->eps_out[0]; + int handled = false; + int ret = 0; + + dwc2_dbg(dwc2, + "ctrl Type=%02x, Req=%02x, V=%04x, I=%04x, L=%04x\n", + ctrl->bRequestType, ctrl->bRequest, ctrl->wValue, + ctrl->wIndex, ctrl->wLength); + + if (ctrl->wLength == 0) { + ep0->dir_in = 1; + dwc2->ep0_state = DWC2_EP0_STATUS_IN; + } else if (ctrl->bRequestType & USB_DIR_IN) { + ep0->dir_in = 1; + dwc2->ep0_state = DWC2_EP0_DATA_IN; + } else { + ep0->dir_in = 0; + dwc2->ep0_state = DWC2_EP0_DATA_OUT; + } + + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + switch (ctrl->bRequest) { + case USB_REQ_SET_ADDRESS: + dwc2->connected = 1; + dwc2_dcfg_set_addr(dwc2, le16_to_cpu(ctrl->wValue)); + ret = dwc2_hsotg_send_reply(dwc2, NULL, 0); + handled = true; + dwc2_info(dwc2, "new address %d\n", ctrl->wValue); + break; + + case USB_REQ_GET_STATUS: + ret = dwc2_process_req_status(dwc2, ctrl); + handled = true; + break; + + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + ret = dwc2_process_req_feature(dwc2, ctrl); + handled = true; + break; + } + } + + /* as a fallback, try delivering it to the driver to deal with */ + if (!handled && dwc2->driver) { + spin_unlock(&dwc2->lock); + ret = dwc2->driver->setup(&dwc2->gadget, ctrl); + spin_lock(&dwc2->lock); + if (ret < 0) + dwc2_dbg(dwc2, "driver->setup() ret %d\n", ret); + } + + /* + * the request is either unhandlable, or is not formatted correctly + * so respond with a STALL for the status stage to indicate failure. + */ + + if (ret < 0) { + dwc2_dbg(dwc2, "unhandled ctrl request "); + dwc2_stall_ep0(dwc2); + } +} + +/** + * dwc2_complete_setup - completion of a setup transfer + * @ep: The endpoint the request was on. + * @req: The request completed. + * + * Called on completion of any requests the driver itself submitted for + * EP0 setup packets + */ +static void dwc2_complete_setup(struct usb_ep *ep, struct usb_request *req) +{ + struct dwc2_ep *hs_ep = our_ep(ep); + struct dwc2 *dwc2 = hs_ep->dwc2; + + if (req->status < 0) { + dwc2_dbg(dwc2, "%s: failed %d\n", __func__, req->status); + return; + } + + spin_lock(&dwc2->lock); + if (req->actual == 0) + dwc2_enqueue_setup(dwc2); + else + dwc2_process_control(dwc2, req->buf); + spin_unlock(&dwc2->lock); +} + +static void dwc2_enqueue_setup(struct dwc2 *dwc2) +{ + struct usb_request *req = dwc2->ctrl_req; + struct dwc2_request *hs_req = our_req(req); + int ret; + + dwc2_dbg(dwc2, "%s: queueing setup request\n", __func__); + + req->zero = 0; + req->length = 8; + req->buf = dwc2->ctrl_buff; + req->complete = dwc2_complete_setup; + + if (!list_empty(&hs_req->queue)) { + dwc2_dbg(dwc2, "%s already queued???\n", __func__); + return; + } + + dwc2->eps_out[0]->dir_in = 0; + dwc2->eps_out[0]->send_zlp = 0; + dwc2->ep0_state = DWC2_EP0_SETUP; + + ret = dwc2_ep_queue(&dwc2->eps_out[0]->ep, req); + if (ret < 0) { + dwc2_err(dwc2, "%s: failed queue (%d)\n", __func__, ret); + /* + * Don't think there's much we can do other than watch the + * driver fail. + */ + } +} + +/** + * dwc2_complete_request - complete a request given to us + * @dwc2: The device state. + * @hs_ep: The endpoint the request was on. + * @hs_req: The request to complete. + * @result: The result code (0 => Ok, otherwise errno) + * + * The given request has finished, so call the necessary completion + * if it has one and then look to see if we can start a new request + * on the endpoint. + * + * Note, expects the ep to already be locked as appropriate. + */ +static void dwc2_complete_request(struct dwc2 *dwc2, struct dwc2_ep *hs_ep, + struct dwc2_request *hs_req, int status) +{ + if (!hs_req) { + dwc2_dbg(dwc2, "%s: nothing to complete?\n", __func__); + return; + } + + dwc2_dbg(dwc2, "complete: ep %p %s, req %p, %d => %p\n", + hs_ep, hs_ep->ep.name, hs_req, status, hs_req->req.complete); + + /* + * only replace the status if we've not already set an error + * from a previous transaction + */ + + if (hs_req->req.status == -EINPROGRESS) + hs_req->req.status = status; + + dma_unmap_single(dwc2->dev, hs_req->req.dma, hs_req->req.length, + hs_ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + hs_ep->req = NULL; + list_del_init(&hs_req->queue); + + /* + * call the complete request with the locks off, just in case the + * request tries to queue more work for this endpoint. + */ + + if (hs_req->req.complete) { + spin_unlock(&dwc2->lock); + hs_req->req.complete(&hs_ep->ep, &hs_req->req); + spin_lock(&dwc2->lock); + } + + /* + * Look to see if there is anything else to do. Note, the completion + * of the previous request may have caused a new request to be started + * so be careful when doing this. + */ + dwc2_dbg(dwc2, "%s: req %p, status %d\n", __func__, hs_ep->req, status); + if (!hs_ep->req && status >= 0) + dwc2_start_next_request(hs_ep); +} + +/** + * dwc2_handle_outdone - handle receiving OutDone/SetupDone from RXFIFO + * @dwc2: The device instance + * @epnum: The endpoint received from + * + * The RXFIFO has delivered an OutDone event, which means that the data + * transfer for an OUT endpoint has been completed, either by a short + * packet or by the finish of a transfer. + */ +static void dwc2_handle_outdone(struct dwc2 *dwc2, int epnum) +{ + u32 epsize = dwc2_readl(dwc2, DOEPTSIZ(epnum)); + struct dwc2_ep *hs_ep = dwc2->eps_out[epnum]; + struct dwc2_request *hs_req = hs_ep->req; + struct usb_request *req = &hs_req->req; + unsigned int size_left = DXEPTSIZ_XFERSIZE_GET(epsize); + int result = 0; + + if (!hs_req) { + dwc2_dbg(dwc2, "%s: no request active\n", __func__); + return; + } + + if (epnum == 0 && dwc2->ep0_state == DWC2_EP0_STATUS_OUT) { + dwc2_dbg(dwc2, "zlp packet received\n"); + dwc2_complete_request(dwc2, hs_ep, hs_req, 0); + dwc2_enqueue_setup(dwc2); + return; + } + + if (using_dma(dwc2)) { + unsigned int size_done; + + /* + * Calculate the size of the transfer by checking how much + * is left in the endpoint size register and then working it + * out from the amount we loaded for the transfer. + * + * We need to do this as DMA pointers are always 32bit aligned + * so may overshoot/undershoot the transfer. + */ + size_done = hs_ep->size_loaded - size_left; + size_done += hs_ep->last_load; + + req->actual = size_done; + } + + if (req->actual < req->length && size_left == 0) { + dwc2_gadget_start_req(dwc2, hs_ep, hs_req, true); + return; + } + + if (req->actual < req->length && req->short_not_ok) { + dwc2_dbg(dwc2, "%s: got %d/%d (short not ok) => error\n", + __func__, req->actual, req->length); + + /* + * todo - what should we return here? there's no one else + * even bothering to check the status. + */ + } + + if (epnum == 0 && dwc2->ep0_state == DWC2_EP0_DATA_OUT) { + /* Move to STATUS IN */ + dwc2->eps_out[0]->dir_in = 1; + dwc2->ep0_state = DWC2_EP0_STATUS_IN; + dwc2_program_zlp(dwc2, dwc2->eps_out[0]); + } + + /* + * Slave mode OUT transfers do not go through XferComplete so + * adjust the ISOC parity here. + */ +#if 0 + if (!using_dma(dwc2)) { + if (hs_ep->isochronous && hs_ep->interval == 1) + dwc2_hsotg_change_ep_iso_parity(dwc2, DOEPCTL(epnum)); + else if (hs_ep->isochronous && hs_ep->interval > 1) + dwc2_gadget_incr_frame_num(hs_ep); + } + + /* Set actual frame number for completed transfers */ + if (hs_ep->isochronous) + req->frame_number = dwc2->frame_number; +#endif + + dwc2_complete_request(dwc2, hs_ep, hs_req, result); +} + +/** + * kill_all_requests - remove all requests from the endpoint's queue + * @dwc2: The device state. + * @ep: The endpoint the requests may be on. + * @result: The result code to use. + * + * Go through the requests on the given endpoint and mark them + * completed with the given result code. + */ +static void kill_all_requests(struct dwc2 *dwc2, struct dwc2_ep *ep, int result) +{ + struct dwc2_request *req, *treq; + unsigned int size; + + ep->req = NULL; + + list_for_each_entry_safe(req, treq, &ep->queue, queue) + dwc2_complete_request(dwc2, ep, req, result); + + if (!dwc2->dedicated_fifos) + return; + size = (dwc2_readl(dwc2, DTXFSTS(ep->fifo_index)) & 0xffff) * 4; + if (size < ep->fifo_size) + dwc2_flush_tx_fifo(dwc2, ep->fifo_index); +} + +/** + * dwc2_gadget_disconnect - disconnect service + * @dwc2: The device state. + * + * The device has been disconnected. Remove all current + * transactions and signal the gadget driver that this + * has happened. + */ +static void dwc2_gadget_disconnect(struct dwc2 *dwc2) +{ + + unsigned int ep; + + if (!dwc2->connected) + return; + + dwc2->connected = 0; + + /* all endpoints should be shutdown */ + for (ep = 0; ep < dwc2->num_eps; ep++) { + if (dwc2->eps_in[ep]) + kill_all_requests(dwc2, dwc2->eps_in[ep], -ESHUTDOWN); + if (dwc2->eps_out[ep]) + kill_all_requests(dwc2, dwc2->eps_out[ep], -ESHUTDOWN); + } + + spin_unlock(&dwc2->lock); + dwc2->driver->disconnect(&dwc2->gadget); + spin_lock(&dwc2->lock); + + usb_gadget_set_state(&dwc2->gadget, USB_STATE_NOTATTACHED); +} + +static void dwc2_gadget_setup_fifo(struct dwc2 *dwc2) +{ + unsigned int ep; + unsigned int addr; + u32 np_tx_fifo_size = dwc2->params.g_np_tx_fifo_size; + u32 rx_fifo_size = dwc2->params.g_rx_fifo_size; + u32 fifo_size = dwc2->hw_params.total_fifo_size; + u32 *txfsz = dwc2->params.g_tx_fifo_size; + u32 size, val; + + /* Reset fifo map if not correctly cleared during previous session */ + WARN_ON(dwc2->fifo_map); + dwc2->fifo_map = 0; + + /* set RX/NPTX FIFO sizes */ + dwc2_writel(dwc2, rx_fifo_size, GRXFSIZ); + size = rx_fifo_size << FIFOSIZE_STARTADDR_SHIFT; + size |= np_tx_fifo_size << FIFOSIZE_DEPTH_SHIFT; + dwc2_writel(dwc2, size, GNPTXFSIZ); + + /* + * arange all the rest of the TX FIFOs, as some versions of this + * block have overlapping default addresses. This also ensures + * that if the settings have been changed, then they are set to + * known values. + */ + + /* start at the end of the GNPTXFSIZ, rounded up */ + addr = rx_fifo_size + np_tx_fifo_size; + + /* + * Configure fifos sizes from provided configuration and assign + * them to endpoints dynamically according to maxpacket size value of + * given endpoint. + */ + for (ep = 1; ep < DWC2_MAX_EPS_CHANNELS; ep++) { + if (!txfsz[ep]) + continue; + val = addr; + val |= txfsz[ep] << FIFOSIZE_DEPTH_SHIFT; + WARN_ONCE(addr + txfsz[ep] > fifo_size, + "insufficient fifo memory"); + addr += txfsz[ep]; + + dwc2_writel(dwc2, val, DPTXFSIZN(ep)); + val = dwc2_readl(dwc2, DPTXFSIZN(ep)); + } + + dwc2_writel(dwc2, dwc2->hw_params.total_fifo_size | + addr << GDFIFOCFG_EPINFOBASE_SHIFT, + GDFIFOCFG); + + dwc2_flush_all_fifo(dwc2); +} + +static void dwc2_gadget_interrupt_init(struct dwc2 *dwc2) +{ + /* unmask subset of endpoint interrupts */ + dwc2_writel(dwc2, DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK | + DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK, + DIEPMSK); + + dwc2_writel(dwc2, DOEPMSK_SETUPMSK | DOEPMSK_AHBERRMSK | + DOEPMSK_EPDISBLDMSK | DOEPMSK_XFERCOMPLMSK, + DOEPMSK); + + dwc2_writel(dwc2, 0, DAINTMSK); +} + +/** + * dwc2_complete_in - complete IN transfer + * @dwc2: The device state. + * @hs_ep: The endpoint that has just completed. + * + * An IN transfer has been completed, update the transfer's state and then + * call the relevant completion routines. + */ +static void dwc2_complete_in(struct dwc2 *dwc2, struct dwc2_ep *hs_ep) +{ + struct dwc2_request *hs_req = hs_ep->req; + u32 epsize = dwc2_readl(dwc2, DIEPTSIZ(hs_ep->epnum)); + int size_left, size_done; + + if (!hs_req) { + dwc2_dbg(dwc2, "XferCompl but no req\n"); + return; + } + + /* Finish ZLP handling for IN EP0 transactions */ + if (hs_ep->epnum == 0 && dwc2->ep0_state == DWC2_EP0_STATUS_IN) { + dwc2_dbg(dwc2, "zlp packet sent\n"); + /* + * While send zlp for DWC2_EP0_STATUS_IN EP direction was + * changed to IN. Change back to complete OUT transfer request + */ + hs_ep->dir_in = 0; + dwc2_complete_request(dwc2, hs_ep, hs_req, 0); + dwc2_enqueue_setup(dwc2); + return; + } + + /* + * Calculate the size of the transfer by checking how much is left + * in the endpoint size register and then working it out from + * the amount we loaded for the transfer. + * + * We do this even for DMA, as the transfer may have incremented + * past the end of the buffer (DMA transfers are always 32bit + * aligned). + */ + size_left = DXEPTSIZ_XFERSIZE_GET(epsize); + + size_done = hs_ep->size_loaded - size_left; + size_done += hs_ep->last_load; + + if (hs_req->req.actual != size_done) + dwc2_dbg(dwc2, "%s: adjusting size done %d => %d\n", + __func__, hs_req->req.actual, size_done); + + hs_req->req.actual = size_done; + dwc2_dbg(dwc2, "req->length:%d req->actual:%d req->zero:%d\n", + hs_req->req.length, hs_req->req.actual, hs_req->req.zero); + + if (!size_left && hs_req->req.actual < hs_req->req.length) { + dwc2_dbg(dwc2, "%s trying more for req...\n", __func__); + dwc2_gadget_start_req(dwc2, hs_ep, hs_req, true); + return; + } + + /* Zlp for all endpoints, for ep0 only in DATA IN stage */ + if (hs_ep->send_zlp) { + dwc2_program_zlp(dwc2, hs_ep); + hs_ep->send_zlp = 0; + /* transfer will be completed on next complete interrupt */ + return; + } + + if (hs_ep->epnum == 0 && dwc2->ep0_state == DWC2_EP0_DATA_IN) { + /* Move to STATUS OUT */ + dwc2->eps_out[0]->dir_in = 0; + dwc2->ep0_state = DWC2_EP0_STATUS_OUT; + dwc2_program_zlp(dwc2, dwc2->eps_out[0]); + return; + } + + dwc2_complete_request(dwc2, hs_ep, hs_req, 0); +} + +/** + * dwc2_core_gadget_init - issue softreset to the core + * @dwc2: The device state + * @usb_reset: Usb resetting flag + * + * Issue a soft reset to the core, and await the core finishing it. + */ +static void dwc2_core_gadget_init(struct dwc2 *dwc2, bool usb_reset) +{ + u32 intmsk; + u32 dctl; + u32 usbcfg; + u32 dcfg; + int ep; + + /* Kill any ep0 requests as controller will be reinitialized */ + kill_all_requests(dwc2, dwc2->eps_out[0], -ECONNRESET); + + if (!usb_reset) { + if (dwc2_core_reset(dwc2)) + return; + } else { + /* all endpoints should be shutdown */ + for (ep = 1; ep < dwc2->num_eps; ep++) { + if (dwc2->eps_in[ep]) + dwc2_ep_disable(&dwc2->eps_in[ep]->ep); + if (dwc2->eps_out[ep]) + dwc2_ep_disable(&dwc2->eps_out[ep]->ep); + } + } + + /* + * we must now enable ep0 ready for host detection and then + * set configuration. + */ + + /* keep other bits untouched (so e.g. forced modes are not lost) */ + usbcfg = dwc2_readl(dwc2, GUSBCFG); + usbcfg &= ~GUSBCFG_TOUTCAL_MASK; + usbcfg |= GUSBCFG_TOUTCAL(7); + dwc2_writel(dwc2, usbcfg, GUSBCFG); + + dwc2_phy_init(dwc2, true); + + dwc2_gadget_setup_fifo(dwc2); + + if (!usb_reset) + dwc2_core_disconnect(dwc2); + + dcfg = DCFG_EPMISCNT(1); + + if (dwc2->params.speed == DWC2_SPEED_PARAM_LOW) { + dcfg |= DCFG_DEVSPD_LS; + } else if (dwc2->params.speed == DWC2_SPEED_PARAM_FULL && + dwc2->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) { + dcfg |= DCFG_DEVSPD_FS48; + } else if (dwc2->params.speed == DWC2_SPEED_PARAM_FULL && + dwc2->params.phy_type != DWC2_PHY_TYPE_PARAM_FS) { + dcfg |= DCFG_DEVSPD_FS; + } + + if (dwc2->params.ipg_isoc_en) + dcfg |= DCFG_IPG_ISOC_SUPPORDED; + + dwc2_writel(dwc2, dcfg, DCFG); + + /* Clear any pending OTG interrupts */ + dwc2_writel(dwc2, 0xffffffff, GOTGINT); + + /* Clear any pending interrupts */ + dwc2_writel(dwc2, 0xffffffff, GINTSTS); + intmsk = GINTSTS_ERLYSUSP | + GINTSTS_GOUTNAKEFF | GINTSTS_GINNAKEFF | + GINTSTS_USBRST | GINTSTS_RESETDET | + GINTSTS_ENUMDONE | + GINTSTS_USBSUSP | GINTSTS_WKUPINT | + GINTSTS_LPMTRANRCVD; + + intmsk |= GINTSTS_INCOMPL_SOIN | GINTSTS_INCOMPL_SOOUT; + + /* enable in and out endpoint interrupts */ + intmsk |= GINTSTS_OEPINT | GINTSTS_IEPINT; + + /* + * Enable the RXFIFO when in slave mode, as this is how we collect + * the data. In DMA mode, we get events from the FIFO but also + * things we cannot process, so do not use it. + */ + if (!using_dma(dwc2)) + intmsk |= GINTSTS_RXFLVL; + + if (!dwc2->params.external_id_pin_ctl) + intmsk |= GINTSTS_CONIDSTSCHNG; + + dwc2_writel(dwc2, intmsk, GINTMSK); + + dwc2_gahbcfg_init(dwc2); + + /* + * If INTknTXFEmpMsk is enabled, it's important to disable ep interrupts + * when we have no data to transfer. Otherwise we get being flooded by + * interrupts. + */ + intmsk = DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK | + DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK; + + if (dwc2->dedicated_fifos && !using_dma(dwc2)) + intmsk |= DIEPMSK_TXFIFOEMPTY | DIEPMSK_INTKNTXFEMPMSK; + + dwc2_writel(dwc2, intmsk, DIEPMSK); + + /* + * don't need XferCompl, we get that from RXFIFO in slave mode. In + * DMA mode we may need this and StsPhseRcvd. + */ + intmsk = DOEPMSK_EPDISBLDMSK | DOEPMSK_AHBERRMSK | DOEPMSK_SETUPMSK; + if (using_dma(dwc2)) + intmsk |= DIEPMSK_XFERCOMPLMSK | DOEPMSK_STSPHSERCVDMSK; + dwc2_writel(dwc2, intmsk, DOEPMSK); + + dwc2_writel(dwc2, 0, DAINTMSK); + + dwc2_dbg(dwc2, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n", + dwc2_readl(dwc2, DIEPCTL0), + dwc2_readl(dwc2, DOEPCTL0)); + + /* Enable interrupts for EP0 in and out */ + dwc2_hsotg_ctrl_epint(dwc2, 0, 0, 1); + dwc2_hsotg_ctrl_epint(dwc2, 0, 1, 1); + dwc2_dbg(dwc2, "DAINTMSK=0x%08x\n", dwc2_readl(dwc2, DAINTMSK)); + + if (!usb_reset) { + dctl = dwc2_readl(dwc2, DCTL); + dwc2_writel(dwc2, dctl | DCTL_PWRONPRGDONE, DCTL); + + udelay(10); /* see openiboot */ + + dctl = dwc2_readl(dwc2, DCTL); + dwc2_writel(dwc2, dctl & ~DCTL_PWRONPRGDONE, DCTL); + } + + dwc2_dbg(dwc2, "DCTL=0x%08x\n", dwc2_readl(dwc2, DCTL)); + + /* + * DxEPCTL_USBActEp says RO in manual, but seems to be set by + * writing to the EPCTL register.. + */ + dwc2_writel(dwc2, dwc2_ep0_mps(dwc2->eps_out[0]->ep.maxpacket) | + DXEPCTL_CNAK | DXEPCTL_EPENA | + DXEPCTL_USBACTEP, DOEPCTL0); + + /* enable, but don't activate EP0in */ + dwc2_writel(dwc2, dwc2_ep0_mps(dwc2->eps_out[0]->ep.maxpacket) | + DXEPCTL_USBACTEP, DIEPCTL0); + + /* clear global NAKs */ + dctl = dwc2_readl(dwc2, DCTL); + dctl |= DCTL_CGOUTNAK | DCTL_CGNPINNAK; + + if (!usb_reset) + dctl |= DCTL_SFTDISCON; + dwc2_writel(dwc2, dctl, DCTL); + + /* must be at-least 3ms to allow bus to see disconnect */ + mdelay(3); +} + +/** + * dwc2_set_selfpowered - set if device is self/bus powered + * @gadget: The usb gadget state + * @is_selfpowered: Whether the device is self-powered + * + * Set if the device is self or bus powered. + */ +static int dwc2_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered) +{ + struct dwc2 *dwc2 = to_dwc2(gadget); + + dwc2->is_selfpowered = !!is_selfpowered; + + return 0; +} + +/** + * dwc2_gadget_pullup - connect/disconnect the USB PHY + * @gadget: The usb gadget state + * @is_on: Current state of the USB PHY + * + * Connect/Disconnect the USB PHY pullup + */ +static int dwc2_gadget_pullup(struct usb_gadget *gadget, int is_on) +{ + struct dwc2 *dwc2 = to_dwc2(gadget); + unsigned long flags = 0; + + dwc2_dbg(dwc2, "%s: is_on: %d\n", __func__, is_on); + + dwc2->enabled = is_on; + /* Don't modify pullup state while in host mode */ + if (dwc2_is_host_mode(dwc2)) { + WARN_ON(dwc2_is_host_mode(dwc2)); + return -EINVAL; + } + + spin_lock_irqsave(&dwc2->lock, flags); + if (is_on) { + dwc2_core_gadget_init(dwc2, false); + /* Enable ACG feature in device mode,if supported */ + dwc2_core_connect(dwc2); + } else { + dwc2_core_disconnect(dwc2); + dwc2_gadget_disconnect(dwc2); + } + + dwc2->gadget.speed = USB_SPEED_UNKNOWN; + spin_unlock_irqrestore(&dwc2->lock, flags); + + return 0; +} + +static int dwc2_gadget_vbus_session(struct usb_gadget *gadget, int is_active) +{ + struct dwc2 *dwc2 = to_dwc2(gadget); + unsigned long flags; + + dwc2_dbg(dwc2, "%s: is_active: %d\n", __func__, is_active); + spin_lock_irqsave(&dwc2->lock, flags); + + if (is_active) { + dwc2_core_gadget_init(dwc2, false); + if (dwc2->enabled) + dwc2_core_connect(dwc2); + } else { + dwc2_core_disconnect(dwc2); + dwc2_gadget_disconnect(dwc2); + } + + spin_unlock_irqrestore(&dwc2->lock, flags); + return 0; +} + + +/** + * dwc2_handle_ep_disabled - handle DXEPINT_EPDISBLD + * @hs_ep: The endpoint on which interrupt is asserted. + * + * This interrupt indicates that the endpoint has been disabled per the + * application's request. + * + * For IN endpoints flushes txfifo, in case of BULK clears DCTL_CGNPINNAK, + * in case of ISOC completes current request. + * + * For ISOC-OUT endpoints completes expired requests. If there is remaining + * request starts it. + */ +static void dwc2_handle_ep_disabled(struct dwc2_ep *hs_ep) +{ + struct dwc2 *dwc2 = hs_ep->dwc2; + struct dwc2_request *hs_req; + unsigned char idx = hs_ep->epnum; + int dir_in = hs_ep->dir_in; + u32 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(idx); + int dctl = dwc2_readl(dwc2, DCTL); + + dwc2_dbg(dwc2, "%s: EPDisbld\n", __func__); + + if (dir_in) { + int epctl = dwc2_readl(dwc2, epctl_reg); + + dwc2_flush_tx_fifo(dwc2, hs_ep->fifo_index); + + if (hs_ep->isochronous) { + dwc2_complete_in(dwc2, hs_ep); + return; + } + + if ((epctl & DXEPCTL_STALL) && (epctl & DXEPCTL_EPTYPE_BULK)) { + int dctl = dwc2_readl(dwc2, DCTL); + + dctl |= DCTL_CGNPINNAK; + dwc2_writel(dwc2, dctl, DCTL); + } + return; + } + + if (dctl & DCTL_GOUTNAKSTS) { + dctl |= DCTL_CGOUTNAK; + dwc2_writel(dwc2, dctl, DCTL); + } + + if (!hs_ep->isochronous) + return; + + if (list_empty(&hs_ep->queue)) { + dwc2_dbg(dwc2, "%s: complete_ep 0x%p, ep->queue empty!\n", + __func__, hs_ep); + return; + } + + do { + hs_req = get_ep_head(hs_ep); + if (hs_req) + dwc2_complete_request(dwc2, hs_ep, hs_req, -ENODATA); + dwc2_gadget_incr_frame_num(hs_ep); + /* Update current frame number value. */ + dwc2->frame_number = dwc2_read_frameno(dwc2); + } while (dwc2_gadget_target_frame_elapsed(hs_ep)); + + dwc2_start_next_request(hs_ep); +} + +/** + * dwc2_handle_out_token_ep_disabled - handle DXEPINT_OUTTKNEPDIS + * @ep: The endpoint on which interrupt is asserted. + * + * This is starting point for ISOC-OUT transfer, synchronization done with + * first out token received from host while corresponding EP is disabled. + * + * Device does not know initial frame in which out token will come. For this + * HW generates OUTTKNEPDIS - out token is received while EP is disabled. Upon + * getting this interrupt SW starts calculation for next transfer frame. + */ +static void dwc2_handle_out_token_ep_disabled(struct dwc2_ep *ep) +{ + struct dwc2 *dwc2 = ep->dwc2; + int dir_in = ep->dir_in; + u32 doepmsk; + u32 ctrl; + + dwc2_dbg(dwc2, "%s\n", __func__); + + if (dir_in || !ep->isochronous) + return; + + if (ep->interval > 1 && ep->target_frame == TARGET_FRAME_INITIAL) { + ep->target_frame = dwc2->frame_number; + + dwc2_gadget_incr_frame_num(ep); + + ctrl = dwc2_readl(dwc2, DOEPCTL(ep->epnum)); + + if (ep->target_frame & 0x1) + ctrl |= DXEPCTL_SETODDFR; + else + ctrl |= DXEPCTL_SETEVENFR; + + dwc2_writel(dwc2, ctrl, DOEPCTL(ep->epnum)); + } + + dwc2_start_next_request(ep); + + doepmsk = dwc2_readl(dwc2, DOEPMSK); + doepmsk &= ~DOEPMSK_OUTTKNEPDISMSK; + dwc2_writel(dwc2, doepmsk, DOEPMSK); +} + +/** + * dwc2_handle_nak - handle NAK interrupt + * @hs_ep: The endpoint on which interrupt is asserted. + * + * This is starting point for ISOC-IN transfer, synchronization done with + * first IN token received from host while corresponding EP is disabled. + * + * Device does not know when first one token will arrive from host. On first + * token arrival HW generates 2 interrupts: 'in token received while FIFO empty' + * and 'NAK'. NAK interrupt for ISOC-IN means that token has arrived and ZLP was + * sent in response to that as there was no data in FIFO. SW is basing on this + * interrupt to obtain frame in which token has come and then based on the + * interval calculates next frame for transfer. + */ +static void dwc2_handle_nak(struct dwc2_ep *hs_ep) +{ + struct dwc2 *dwc2 = hs_ep->dwc2; + int dir_in = hs_ep->dir_in; + u32 ctrl; + + if (!dir_in || !hs_ep->isochronous) + return; + + if (hs_ep->target_frame == TARGET_FRAME_INITIAL) { + hs_ep->target_frame = dwc2->frame_number; + if (hs_ep->interval > 1) { + ctrl = dwc2_readl(dwc2, DIEPCTL(hs_ep->epnum)); + + if (hs_ep->target_frame & 0x1) + ctrl |= DXEPCTL_SETODDFR; + else + ctrl |= DXEPCTL_SETEVENFR; + + dwc2_writel(dwc2, ctrl, DIEPCTL(hs_ep->epnum)); + } + + dwc2_complete_request(dwc2, hs_ep, get_ep_head(hs_ep), 0); + } + + dwc2_gadget_incr_frame_num(hs_ep); +} + +/** + * dwc2_handle_epint - handle an in/out endpoint interrupt + * @dwc2: The driver state + * @idx: The index for the endpoint (0..15) + * @dir_in: Set if this is an IN endpoint + * + * Process and clear any interrupt pending for an individual endpoint + */ +static void dwc2_handle_epint(struct dwc2 *dwc2, unsigned int idx, + int dir_in) +{ + struct dwc2_ep *hs_ep = index_to_ep(dwc2, idx, dir_in); + u32 epmsk_reg = dir_in ? DIEPMSK : DOEPMSK; + u32 epint_reg = dir_in ? DIEPINT(idx) : DOEPINT(idx); + u32 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(idx); + u32 epsiz_reg = dir_in ? DIEPTSIZ(idx) : DOEPTSIZ(idx); + u32 ints; + u32 mask; + u32 ctrl; + + mask = dwc2_readl(dwc2, epmsk_reg); + if ((dwc2_readl(dwc2, DIEPEMPMSK) >> idx) & 1) + mask |= DIEPMSK_TXFIFOEMPTY; + mask |= DXEPINT_SETUP_RCVD; + + ints = dwc2_readl(dwc2, epint_reg); + + if (ints & DXEPINT_AHBERR) + dwc2_err(dwc2, "%s: AHBErr\n", __func__); + + ints &= mask; + + ctrl = dwc2_readl(dwc2, epctl_reg); + + /* Clear endpoint interrupts */ + dwc2_writel(dwc2, ints, epint_reg); + + if (!hs_ep) { + dwc2_err(dwc2, "%s:Interrupt for unconfigured ep%d(%s)\n", + __func__, idx, dir_in ? "in" : "out"); + return; + } + + dwc2_dbg(dwc2, "%s: ep%d(%s) DxEPINT=0x%08x\n", + __func__, idx, dir_in ? "in" : "out", ints); + + /* Don't process XferCompl interrupt if it is a setup packet */ + if (idx == 0 && (ints & (DXEPINT_SETUP | DXEPINT_SETUP_RCVD))) + ints &= ~DXEPINT_XFERCOMPL; + + if (ints & DXEPINT_XFERCOMPL) { + dwc2_dbg(dwc2, + "%s: XferCompl: DxEPCTL=0x%08x, DXEPTSIZ=%08x\n", + __func__, dwc2_readl(dwc2, epctl_reg), + dwc2_readl(dwc2, epsiz_reg)); + if (dir_in) { + /* + * We get OutDone from the FIFO, so we only + * need to look at completing IN requests here + * if operating slave mode + */ + if (hs_ep->isochronous && hs_ep->interval > 1) + dwc2_gadget_incr_frame_num(hs_ep); + + dwc2_complete_in(dwc2, hs_ep); + if (ints & DXEPINT_NAKINTRPT) + ints &= ~DXEPINT_NAKINTRPT; + + if (idx == 0 && !hs_ep->req) + dwc2_enqueue_setup(dwc2); + } else if (using_dma(dwc2)) { + /* + * We're using DMA, we need to fire an OutDone here + * as we ignore the RXFIFO. + */ + if (hs_ep->isochronous && hs_ep->interval > 1) + dwc2_gadget_incr_frame_num(hs_ep); + + dwc2_handle_outdone(dwc2, idx); + } + } + + if (ints & DXEPINT_EPDISBLD) + dwc2_handle_ep_disabled(hs_ep); + + if (ints & DXEPINT_OUTTKNEPDIS) + dwc2_handle_out_token_ep_disabled(hs_ep); + + if (ints & DXEPINT_NAKINTRPT) + dwc2_handle_nak(hs_ep); + + if (ints & DXEPINT_SETUP) { /* Setup or Timeout */ + dwc2_dbg(dwc2, "%s: Setup/Timeout\n", __func__); + + if (using_dma(dwc2) && idx == 0) { + /* + * this is the notification we've received a + * setup packet. In non-DMA mode we'd get this + * from the RXFIFO, instead we need to process + * the setup here. + */ + + if (!dir_in && dwc2->ep0_state == DWC2_EP0_SETUP) + dwc2_handle_outdone(dwc2, 0); + } + } + + if (ints & DXEPINT_STSPHSERCVD) + dwc2_dbg(dwc2, "%s: StsPhseRcvd\n", __func__); + + if (ints & DXEPINT_BACK2BACKSETUP) + dwc2_dbg(dwc2, "%s: B2BSetup/INEPNakEff\n", __func__); + + if (ints & DXEPINT_BNAINTR) { + dwc2_dbg(dwc2, "%s: BNA interrupt\n", __func__); +#if 0 + if (hs_ep->isochronous) + dwc2_handle_isoc_bna(hs_ep); +#endif + } + + if (dir_in && !hs_ep->isochronous) { + /* not sure if this is important, but we'll clear it anyway */ + if (ints & DXEPINT_INTKNTXFEMP) { + dwc2_dbg(dwc2, "%s: ep%d: INTknTXFEmpMsk\n", + __func__, idx); + } + + /* this probably means something bad is happening */ + if (ints & DXEPINT_INTKNEPMIS) { + dwc2_warn(dwc2, "%s: ep%d: INTknEP\n", + __func__, idx); + } + + /* FIFO has space or is empty (see GAHBCFG) */ + if (dwc2->dedicated_fifos && ints & DXEPINT_TXFEMP) { + dwc2_dbg(dwc2, "%s: ep%d: TxFIFOEmpty\n", + __func__, idx); + } + } +} + +/** + * dwc2_handle_enumdone - Handle EnumDone interrupt (enumeration done) + * @dwc2: The device state. + * + * Handle updating the device settings after the enumeration phase has + * been completed. + */ +static void dwc2_handle_enumdone(struct dwc2 *dwc2) +{ + u32 dsts = dwc2_readl(dwc2, DSTS); + int ep0_mps = 0, ep_mps = 8; + int i; + + /* + * This should signal the finish of the enumeration phase + * of the USB handshaking, so we should now know what rate + * we connected at. + */ + dwc2_dbg(dwc2, "EnumDone (DSTS=0x%08x)\n", dsts); + + /* + * note, since we're limited by the size of transfer on EP0, and + * it seems IN transfers must be a even number of packets we do + * not advertise a 64byte MPS on EP0. + */ + + /* catch both EnumSpd_FS and EnumSpd_FS48 */ + switch ((dsts & DSTS_ENUMSPD_MASK) >> DSTS_ENUMSPD_SHIFT) { + case DSTS_ENUMSPD_FS: + case DSTS_ENUMSPD_FS48: + dwc2->gadget.speed = USB_SPEED_FULL; + ep0_mps = 64; + ep_mps = 1023; + break; + case DSTS_ENUMSPD_HS: + dwc2->gadget.speed = USB_SPEED_HIGH; + ep0_mps = 64; + ep_mps = 1024; + break; + case DSTS_ENUMSPD_LS: + dwc2->gadget.speed = USB_SPEED_LOW; + ep0_mps = 8; + ep_mps = 8; + /* + * note, we don't actually support LS in this driver at the + * moment, and the documentation seems to imply that it isn't + * supported by the PHYs on some of the devices. + */ + break; + } + dwc2_dbg(dwc2, "new %s device\n", usb_speed_string(dwc2->gadget.speed)); + + /* + * we should now know the maximum packet size for an + * endpoint, so set the endpoints to a default value. + */ + if (ep0_mps) { + /* Initialize ep0 for both in and out directions */ + dwc2_set_ep_maxpacket(dwc2, 0, ep0_mps, 0, 1); + dwc2_set_ep_maxpacket(dwc2, 0, ep0_mps, 0, 0); + for (i = 1; i < dwc2->num_eps; i++) { + if (dwc2->eps_in[i]) + dwc2_set_ep_maxpacket(dwc2, i, ep_mps, 0, 1); + if (dwc2->eps_out[i]) + dwc2_set_ep_maxpacket(dwc2, i, ep_mps, 0, 0); + } + } + + /* ensure after enumeration our EP0 is active */ + dwc2_enqueue_setup(dwc2); + + dwc2_dbg(dwc2, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n", + dwc2_readl(dwc2, DIEPCTL0), + dwc2_readl(dwc2, DOEPCTL0)); +} + +/* IRQ flags which will trigger a retry around the IRQ loop */ +#define IRQ_RETRY_MASK (GINTSTS_NPTXFEMP | \ + GINTSTS_PTXFEMP | \ + GINTSTS_RXFLVL) + +static void dwc2_gadget_udc_poll(struct usb_gadget *gadget) +{ + struct dwc2 *dwc2 = to_dwc2(gadget); + int retry_count = 4; + u32 gintsts; + u32 gintmsk; + + if (!dwc2_is_device_mode(dwc2)) + return; + + spin_lock(&dwc2->lock); + +irq_retry: + gintsts = readl(dwc2->regs + GINTSTS); + gintmsk = readl(dwc2->regs + GINTMSK); + gintsts &= gintmsk; + + if (!gintsts) + return; + + dwc2_dbg(dwc2, "%s: %08x (%08x) retry %d\n", + __func__, gintsts, gintmsk, 4 - retry_count); + + if (gintsts & GINTSTS_RESETDET) { + dwc2_dbg(dwc2, "%s: USBRstDet\n", __func__); + dwc2_writel(dwc2, GINTSTS_RESETDET, GINTSTS); + } + + if (gintsts & (GINTSTS_USBRST | GINTSTS_RESETDET)) { + u32 usb_status = dwc2_readl(dwc2, GOTGCTL); + u32 connected = dwc2->connected; + + dwc2_dbg(dwc2, "%s: USBRst\n", __func__); + dwc2_dbg(dwc2, "GNPTXSTS=%08x\n", dwc2_readl(dwc2, GNPTXSTS)); + + dwc2_writel(dwc2, GINTSTS_USBRST, GINTSTS); + + /* Report disconnection if it is not already done. */ + dwc2_gadget_disconnect(dwc2); + + /* Reset device address to zero */ + dwc2_dcfg_set_addr(dwc2, 0); + + if (usb_status & GOTGCTL_BSESVLD && connected) + dwc2_core_gadget_init(dwc2, true); + } + + if (gintsts & GINTSTS_ENUMDONE) { + dwc2_writel(dwc2, GINTSTS_ENUMDONE, GINTSTS); + + dwc2_handle_enumdone(dwc2); + } + + if (gintsts & (GINTSTS_OEPINT | GINTSTS_IEPINT)) { + u32 daint = dwc2_readl(dwc2, DAINT); + u32 daintmsk = dwc2_readl(dwc2, DAINTMSK); + u32 daint_out, daint_in; + int ep; + + daint &= daintmsk; + daint_out = daint >> DAINT_OUTEP_SHIFT; + daint_in = daint & ~(daint_out << DAINT_OUTEP_SHIFT); + + dwc2_dbg(dwc2, "%s: daint=%08x\n", __func__, daint); + + for (ep = 0; ep < dwc2->num_eps && daint_out; + ep++, daint_out >>= 1) { + if (daint_out & 1) + dwc2_handle_epint(dwc2, ep, 0); + } + + for (ep = 0; ep < dwc2->num_eps && daint_in; + ep++, daint_in >>= 1) { + if (daint_in & 1) + dwc2_handle_epint(dwc2, ep, 1); + } + } + + /* check both FIFOs */ + if (gintsts & GINTSTS_NPTXFEMP) { + dwc2_dbg(dwc2, "NPTxFEmp\n"); + + /* + * Disable the interrupt to stop it happening again + * unless one of these endpoint routines decides that + * it needs re-enabling + */ +#if 0 + dwc2_hsotg_disable_gsint(dwc2, GINTSTS_NPTXFEMP); + dwc2_hsotg_irq_fifoempty(dwc2, false); +#endif + } + + if (gintsts & GINTSTS_PTXFEMP) { + dwc2_dbg(dwc2, "PTxFEmp\n"); + + /* See note in GINTSTS_NPTxFEmp */ +#if 0 + dwc2_hsotg_disable_gsint(dwc2, GINTSTS_PTXFEMP); + dwc2_hsotg_irq_fifoempty(dwc2, true); +#endif + } + + if (gintsts & GINTSTS_RXFLVL) { + /* + * note, since GINTSTS_RxFLvl doubles as FIFO-not-empty, + * we need to retry dwc2_hsotg_handle_rx if this is still + * set. + */ + dwc2_err(dwc2, "RXFLVL\n"); +#if 0 + dwc2_handle_rx(dwc2); +#endif + } + + if (gintsts & GINTSTS_ERLYSUSP) { + dwc2_dbg(dwc2, "GINTSTS_ErlySusp\n"); + dwc2_writel(dwc2, GINTSTS_ERLYSUSP, GINTSTS); + } + if (gintsts & GINTSTS_USBSUSP) { + dwc2_dbg(dwc2, "USBSusp\n"); + dwc2_writel(dwc2, GINTSTS_USBSUSP, GINTSTS); + } + + /* + * these next two seem to crop-up occasionally causing the core + * to shutdown the USB transfer, so try clearing them and logging + * the occurrence. + */ + + if (gintsts & GINTSTS_GOUTNAKEFF) { + u8 idx; + u32 epctrl; + u32 gintmsk; + u32 daintmsk; + struct dwc2_ep *hs_ep; + + daintmsk = dwc2_readl(dwc2, DAINTMSK); + daintmsk >>= DAINT_OUTEP_SHIFT; + /* Mask this interrupt */ + gintmsk = dwc2_readl(dwc2, GINTMSK); + gintmsk &= ~GINTSTS_GOUTNAKEFF; + dwc2_writel(dwc2, gintmsk, GINTMSK); + + dwc2_dbg(dwc2, "GOUTNakEff triggered\n"); + for (idx = 1; idx < dwc2->num_eps; idx++) { + hs_ep = dwc2->eps_out[idx]; + /* Proceed only unmasked ISOC EPs */ + if ((BIT(idx) & ~daintmsk) || !hs_ep->isochronous) + continue; + + epctrl = dwc2_readl(dwc2, DOEPCTL(idx)); + + if (epctrl & DXEPCTL_EPENA) { + epctrl |= DXEPCTL_SNAK; + epctrl |= DXEPCTL_EPDIS; + dwc2_writel(dwc2, epctrl, DOEPCTL(idx)); + } + } + + /* This interrupt bit is cleared in DXEPINT_EPDISBLD handler */ + } + + if (gintsts & GINTSTS_GINNAKEFF) { + dwc2_info(dwc2, "GINNakEff triggered\n"); + dwc2_set_bit(dwc2, DCTL, DCTL_CGNPINNAK); + } +#if 0 + if (gintsts & GINTSTS_INCOMPL_SOIN) + dwc2_handle_incomplete_isoc_in(dwc2); + + if (gintsts & GINTSTS_INCOMPL_SOOUT) + dwc2_handle_incomplete_isoc_out(dwc2); +#endif + + /* + * if we've had fifo events, we should try and go around the + * loop again to see if there's any point in returning yet. + */ + if (gintsts & IRQ_RETRY_MASK && --retry_count > 0) + goto irq_retry; + + spin_unlock(&dwc2->lock); +} + +/** + * dwc2_dwc2_udc_start - prepare the udc for work + * @gadget: The usb gadget state + * @driver: The usb gadget driver + * + * Perform initialization to prepare udc device and driver + * to work. + */ +static int dwc2_gadget_udc_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct dwc2 *dwc2 = to_dwc2(gadget); + + if (!driver) { + dwc2_err(dwc2, "%s: no driver\n", __func__); + return -EINVAL; + } + + if (driver->max_speed < USB_SPEED_FULL) { + dwc2_err(dwc2, "%s: bad speed\n", __func__); + return -EINVAL; + } + + dwc2->driver = driver; + dwc2->gadget.speed = USB_SPEED_UNKNOWN; + + dwc2_core_gadget_init(dwc2, false); + dwc2_gadget_interrupt_init(dwc2); + + dwc2_info(dwc2, "bound driver %s\n", driver->driver.name); + + return 0; +} + +static int dwc2_gadget_udc_stop(struct usb_gadget *gadget) +{ + struct dwc2 *dwc2 = to_dwc2(gadget); + unsigned long flags = 0; + + dwc2_dbg(dwc2, "%s\n", __func__); + + /* all endpoints should be shutdown */ + spin_lock_irqsave(&dwc2->lock, flags); + + dwc2->driver = NULL; + dwc2->gadget.speed = USB_SPEED_UNKNOWN; + dwc2->enabled = 0; + + spin_unlock_irqrestore(&dwc2->lock, flags); + + return 0; +} + +static const struct usb_gadget_ops dwc2_gadget_ops = { + .get_frame = dwc2_gadget_get_frame, + .set_selfpowered = dwc2_set_selfpowered, + .vbus_session = dwc2_gadget_vbus_session, + .vbus_draw = NULL, + .pullup = dwc2_gadget_pullup, + .udc_start = dwc2_gadget_udc_start, + .udc_stop = dwc2_gadget_udc_stop, + .udc_poll = dwc2_gadget_udc_poll, +}; + +/** + * dwc2_gadget_ep_init - initialise a single endpoint + * @dwc2: The device state. + * @epnum: The endpoint number + * @dir_in: True if direction is in. + * + * Initialise the given endpoint (as part of the probe and device state + * creation) to give to the gadget driver. Setup the endpoint name, any + * direction information and other state that may be required. + */ +static void dwc2_ep_init(struct dwc2 *dwc2, int epnum, bool dir_in) +{ + struct dwc2_ep *ep; + char *dir; + + if (dir_in) + ep = dwc2->eps_in[epnum]; + else + ep = dwc2->eps_out[epnum]; + + if (epnum == 0) + dir = ""; + else if (dir_in) + dir = "in"; + else + dir = "out"; + + ep->dir_in = dir_in; + ep->epnum = epnum; + + snprintf(ep->name, sizeof(ep->name), "ep%d%s", epnum, dir); + + INIT_LIST_HEAD(&ep->queue); + INIT_LIST_HEAD(&ep->ep.ep_list); + + if (epnum) + list_add_tail(&ep->ep.ep_list, &dwc2->gadget.ep_list); + + ep->dwc2 = dwc2; + ep->ep.name = ep->name; + + if (dwc2->params.speed == DWC2_SPEED_PARAM_LOW) + usb_ep_set_maxpacket_limit(&ep->ep, 8); + else if (epnum == 0) + usb_ep_set_maxpacket_limit(&ep->ep, D0EPCTL_MPS_LIMIT); + else + usb_ep_set_maxpacket_limit(&ep->ep, 1024); + + ep->ep.ops = &dwc2_ep_ops; +} + +static int dwc2_eps_alloc(struct dwc2 *dwc2) +{ + struct dwc2_ep *ep; + u32 cfg; + u32 ep_type; + u32 i; + + /* Number of Device Endpoints */ + dwc2->num_eps = 1 + dwc2->hw_params.num_dev_ep; + + ep = kzalloc(sizeof(*ep), GFP_KERNEL); + if (!ep) + return -ENOMEM; + + /* Same endpoint is used in both directions for ep0 */ + dwc2->eps_out[0] = dwc2->eps_in[0] = ep; + + cfg = dwc2->hw_params.dev_ep_dirs; + for (i = 1, cfg >>= 2; i < dwc2->num_eps; i++, cfg >>= 2) { + ep_type = cfg & 3; + /* Direction in or both */ + if (!(ep_type & 2)) { + ep = kzalloc(sizeof(*ep), GFP_KERNEL); + if (!ep) + return -ENOMEM; + dwc2->eps_in[i] = ep; + } + /* Direction out or both */ + if (!(ep_type & 1)) { + ep = kzalloc(sizeof(*ep), GFP_KERNEL); + if (!ep) + return -ENOMEM; + dwc2->eps_out[i] = ep; + } + } + + dwc2->dedicated_fifos = dwc2->hw_params.en_multiple_tx_fifo; + + dwc2_info(dwc2, "EPs: %d, %s fifos, 0x%x entries in SPRAM\n", + dwc2->num_eps, + dwc2->dedicated_fifos ? "dedicated" : "shared", + dwc2->hw_params.total_fifo_size); + return 0; +} + +/** + * dwc2_force_mode() - Force the mode of the controller. + * + * Forcing the mode is needed for two cases: + * + * 1) If the dr_mode is set to either HOST or PERIPHERAL we force the + * controller to stay in a particular mode regardless of ID pin + * changes. We do this once during probe. + * + * 2) During probe we want to read reset values of the hw + * configuration registers that are only available in either host or + * device mode. We may need to force the mode if the current mode does + * not allow us to access the register in the mode that we want. + * + * In either case it only makes sense to force the mode if the + * controller hardware is OTG capable. + * + * Checks are done in this function to determine whether doing a force + * would be valid or not. + * + * If a force is done, it requires a IDDIG debounce filter delay if + * the filter is configured and enabled. We poll the current mode of + * the controller to account for this delay. + * + * @dwc2: Programming view of DWC_otg controller + * @host: Host mode flag + */ +static void dwc2_force_mode(struct dwc2 *dwc2, bool host) +{ + u32 gusbcfg; + u32 set; + u32 clear; + + dev_dbg(dwc2->dev, "Forcing mode to %s\n", host ? "host" : "device"); + + /* + * If dr_mode is either peripheral or host only, there is no + * need to ever force the mode to the opposite mode. + */ + if (WARN_ON(host && dwc2->dr_mode == USB_DR_MODE_PERIPHERAL)) + return; + + if (WARN_ON(!host && dwc2->dr_mode == USB_DR_MODE_HOST)) + return; + + gusbcfg = dwc2_readl(dwc2, GUSBCFG); + + set = host ? GUSBCFG_FORCEHOSTMODE : GUSBCFG_FORCEDEVMODE; + clear = host ? GUSBCFG_FORCEDEVMODE : GUSBCFG_FORCEHOSTMODE; + + gusbcfg &= ~clear; + gusbcfg |= set; + dwc2_writel(dwc2, gusbcfg, GUSBCFG); + + dwc2_wait_for_mode(dwc2, host); + + return; +} + +/** + * dwc2_clear_force_mode() - Clears the force mode bits. + * + * After clearing the bits, wait up to 100 ms to account for any + * potential IDDIG filter delay. We can't know if we expect this delay + * or not because the value of the connector ID status is affected by + * the force mode. We only need to call this once during probe if + * dr_mode == OTG. + * + * @dwc2: Programming view of DWC_otg controller + */ +static void dwc2_clear_force_mode(struct dwc2 *dwc2) +{ + u32 gusbcfg; + + dev_dbg(dwc2->dev, "Clearing force mode bits\n"); + + gusbcfg = dwc2_readl(dwc2, GUSBCFG); + gusbcfg &= ~GUSBCFG_FORCEHOSTMODE; + gusbcfg &= ~GUSBCFG_FORCEDEVMODE; + dwc2_writel(dwc2, gusbcfg, GUSBCFG); + + if (dwc2_iddig_filter_enabled(dwc2)) + mdelay(100); +} + +int dwc2_gadget_init(struct dwc2 *dwc2) +{ + u32 dctl; + int epnum; + int ret; + + if (!dwc2->params.dma) { + dwc2_err(dwc2, "no DMA support, required by gadget driver"); + return -ENOTSUPP; + } + + dwc2_core_init(dwc2); + + dwc2->gadget.speed = USB_SPEED_UNKNOWN; + if (dwc2->params.speed == DWC2_SPEED_PARAM_HIGH) + dwc2->gadget.max_speed = USB_SPEED_HIGH; + else + dwc2->gadget.max_speed = USB_SPEED_FULL; + + dwc2->gadget.ops = &dwc2_gadget_ops; + dwc2->gadget.name = "DWC2 gadget"; + + dwc2->gadget.is_otg = (dwc2->dr_mode == USB_DR_MODE_OTG) ? 1 : 0; + + if (dwc2->gadget.is_otg) + dwc2_clear_force_mode(dwc2); + else + dwc2_force_mode(dwc2, false); + + ret = dwc2_eps_alloc(dwc2); + if (ret) { + dwc2_err(dwc2, "Endpoints allocation failed: %d\n", ret); + return ret; + } + + dwc2->ctrl_buff = dma_alloc(DWC2_CTRL_BUFF_SIZE); + if (!dwc2->ctrl_buff) + return -ENOMEM; + + dwc2->ep0_buff = dma_alloc(DWC2_CTRL_BUFF_SIZE); + if (!dwc2->ep0_buff) + return -ENOMEM; + + if (dwc2->num_eps == 0) { + dwc2_err(dwc2, "wrong number of EPs (zero)\n"); + return -EINVAL; + } + + /* setup endpoint information */ + INIT_LIST_HEAD(&dwc2->gadget.ep_list); + dwc2->gadget.ep0 = &dwc2->eps_out[0]->ep; + + /* allocate EP0 request */ + dwc2->ctrl_req = dwc2_ep_alloc_req(&dwc2->eps_out[0]->ep); + if (!dwc2->ctrl_req) { + dwc2_err(dwc2, "failed to allocate ctrl req\n"); + return -ENOMEM; + } + + /* initialise the endpoints now the core has been initialised */ + for (epnum = 0; epnum < dwc2->num_eps; epnum++) { + if (dwc2->eps_in[epnum]) + dwc2_ep_init(dwc2, epnum, 1); + if (dwc2->eps_out[epnum]) + dwc2_ep_init(dwc2, epnum, 0); + } + + /* Be in disconnected state until gadget is registered */ + dctl = dwc2_readl(dwc2, DCTL); + dwc2_writel(dwc2, dctl | DCTL_SFTDISCON, DCTL); + + ret = usb_add_gadget_udc(dwc2->dev, &dwc2->gadget); + if (ret) { + dwc2_ep_free_req(&dwc2->eps_out[0]->ep, dwc2->ctrl_req); + return ret; + } + + return 0; +} + +void dwc2_gadget_uninit(struct dwc2 *dwc2) +{ + dwc2_core_disconnect(dwc2); + dwc2_gadget_disconnect(dwc2); +} diff --git a/drivers/usb/dwc2/host.c b/drivers/usb/dwc2/host.c new file mode 100644 index 0000000000..a9b25aeeaa --- /dev/null +++ b/drivers/usb/dwc2/host.c @@ -0,0 +1,796 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#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_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, struct usb_device *dev, u8 hc, + unsigned long pipe, int is_in) +{ + 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; + + /* 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 void dwc2_endpoint_reset(struct dwc2 *dwc2, int in, int devnum, int ep) +{ + if (in) + dwc2->in_data_toggle[devnum][ep] = TSIZ_SC_MC_PID_DATA0; + else + dwc2->out_data_toggle[devnum][ep] = TSIZ_SC_MC_PID_DATA0; +} + +static int wait_for_chhltd(struct dwc2 *dwc2, u8 hc, uint32_t *sub, u8 *tgl) +{ + int ret; + uint32_t hcint, hctsiz, hcchar; + + ret = dwc2_wait_bit_set(dwc2, HCINT(hc), HCINTMSK_CHHLTD, 10000); + if (ret) { + hcchar = dwc2_readl(dwc2, HCCHAR(hc)); + dwc2_writel(dwc2, hcchar | HCCHAR_CHDIS, HCCHAR(hc)); + dwc2_wait_bit_set(dwc2, HCINT(hc), HCINTMSK_CHHLTD, 10000); + return ret; + } + + 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 = 0; + int ret = 0; + + if (xfer_len) + 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=%pad\n", + *pid, xfer_len, num_packets, &dma_addr); + + dwc2_writel(dwc2, dma_addr, HCDMA(hc)); + + hctsiz = (xfer_len << TSIZ_XFERSIZE_SHIFT) + | (num_packets << 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; + + *actual_len = xfer_len; + if (in) + *actual_len -= sub; + +exit: + if (xfer_len) + 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, u8 hc, + unsigned long pipe, u8 *pid, int in, void *buf, + int len) +{ + int mps = usb_maxpacket(dev, 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, dev, hc, pipe, in); + + /* Check if the target is a FS/LS device behind a HS hub */ + if (do_split) { + dwc2_hc_init_split(dwc2, dev, hc); + 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(hc)); + if (complete_split) + hcsplt |= HCSPLT_COMPSPLT; + else if (do_split) + hcsplt &= ~HCSPLT_COMPSPLT; + dwc2_writel(dwc2, hcsplt, HCSPLT(hc)); + } + + if (usb_pipeint(pipe)) { + int uframe_num = dwc2_readl(dwc2, HFNUM); + + if (!(uframe_num & 0x1)) + odd_frame = 1; + } + + ret = transfer_chunk(dwc2, hc, pid, + in, (char *)buf + done, num_packets, + xfer_len, &actual_len, odd_frame); + + hcint = dwc2_readl(dwc2, HCINT(hc)); + 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(hc)); + dwc2_writel(dwc2, 0xFFFFFFFF, HCINT(hc)); + + dev->status = 0; + dev->act_len = done; + + return ret; +} + +static 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; + u8 hc = DWC2_HC_CHANNEL; + /* For CONTROL endpoint pid should start with DATA1 */ + int status_direction; + + if (devnum == dwc2->root_hub_devnum) { + udev->speed = USB_SPEED_HIGH; + ret = dwc2_submit_roothub(dwc2, udev, pipe, buffer, len, setup); + return ret; + } + + /* SETUP stage */ + pid = TSIZ_SC_MC_PID_SETUP; + do { + ret = dwc2_submit_packet(dwc2, udev, hc, 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, hc, 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, hc, pipe, &pid, + status_direction, NULL, 0); + } while (ret == -EAGAIN); + 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) & 0xf; + dwc2_endpoint_reset(dwc2, usb_pipein(pipe), devnum, ep); + } + + udev->act_len = act_len; + udev->status = 0; + + return 0; +} + +static 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; + u8 hc = DWC2_HC_CHANNEL; + uint64_t start; + int ret; + + 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]; + + start = get_time_ns(); + do { + ret = dwc2_submit_packet(dwc2, udev, hc, pipe, pid, in, + buffer, len); + } while (ret == -EAGAIN && !is_timeout(start, timeout * MSECOND)); + if (ret == -EAGAIN) { + dwc2_err(dwc2, "Timeout on bulk endpoint\n"); + ret = -ETIMEDOUT; + } + + dwc2_dbg(dwc2, "%s: return %d\n", __func__, ret); + + return ret; +} + +static 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; + u8 hc = DWC2_HC_CHANNEL; + 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, hc, 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; + } + } +} + +/** + * 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. + * + * 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 *dev, + struct dwc2 *dwc2) +{ + 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); + } + } + + dwc2_config_fifos(dwc2); + + /* 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); +} + +static int dwc2_host_init(struct usb_host *host) +{ + struct dwc2 *dwc2 = to_dwc2(host); + struct device *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; +} + +int dwc2_register_host(struct dwc2 *dwc2) +{ + struct usb_host *host; + + host = &dwc2->host; + host->hw_dev = dwc2->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; + + return usb_register_host(host); +} + +void dwc2_host_uninit(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); +} diff --git a/drivers/usb/dwc2/regs.h b/drivers/usb/dwc2/regs.h new file mode 100644 index 0000000000..4c74f95eb4 --- /dev/null +++ b/drivers/usb/dwc2/regs.h @@ -0,0 +1,841 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * 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_REGADDR_MASK (0x3f << 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 D0EPCTL_MPS_LIMIT 64 + +#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 + +#endif /* __DWC2_H__ */ diff --git a/drivers/usb/dwc2/rhub.c b/drivers/usb/dwc2/rhub.c new file mode 100644 index 0000000000..de4d2837ac --- /dev/null +++ b/drivers/usb/dwc2/rhub.c @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#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; +} __packed descriptor = { + .hub = { + .bLength = USB_DT_HUB_NONVAR_SIZE + + ((USB_MAXCHILDREN + 1 + 7) / 8), + .bDescriptorType = USB_DT_HUB, + .bNbrPorts = 1, + .wHubCharacteristics = 0, + .bPwrOn2PwrGood = 0, + .bHubContrCurrent = 0, + .u.hs.DeviceRemovable = {0xff}, + .u.hs.PortPwrCtrlMask = {} + }, + .device = { + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = 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 = 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 = cpu_to_le16( + (USB_MAXCHILDREN + 1 + 7) / 8), + .bInterval = 255 + }, +}; + +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; + uint32_t status = 0; + uint32_t change = 0; + int speed; + + if (!buf || len < sizeof(*portsts)) + return -1; + + 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; + + return 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; +} + +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 0; +} + +static void strle16(__le16 *dest, char *src, size_t n) +{ + unsigned int i; + + for (i = 0; i < n && *src != '\0'; i++, src++) + dest[i] = cpu_to_le16(*src); +} + +static int dwc2_get_string_descriptor(struct dwc2 *dwc2, struct usb_device *dev, + void *buf, int len, int index) +{ + char *src, *str = buf; + __le16 *le16 = (__le16 *)(str + 2); + int size; + + if (!buf || len < 2) + return -1; + + switch (index) { + case 0: /* Language */ + src = "\x09\x04"; + size = strlen(src) + 2; + len = min_t(int, len, size); + + 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: + dwc2_err(dwc2, "roothub: unknown string descriptor: 0x%x\n", + index); + return -1; + } + + dev->act_len = len; + dev->status = 0; + + return 0; +} + +static int dwc2_get_descriptor(struct dwc2 *dwc2, struct usb_device *dev, + void *buf, int len, int value) +{ + int index = value >> 8; + + if (!buf || len < 0) + return -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: + dwc2_err(dwc2, "roothub: unknown descriptor: 0x%x\n", index); + return -1; + } + + dev->act_len = len; + dev->status = 0; + + return 0; +} + +static int dwc2_set_port_feature(struct dwc2 *dwc2, struct usb_device *dev, + int feature) +{ + 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; +} + +static int dwc2_clear_port_feature(struct dwc2 *dwc2, struct usb_device *dev, + int feature) +{ + uint32_t hprt0; + + 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_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, "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; + + dev->act_len = 0; + dev->status = 0; + + return 0; +} + +int dwc2_submit_roothub(struct dwc2 *dwc2, struct usb_device *dev, + unsigned long pipe, void *buf, int len, + struct devrequest *setup) +{ + 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, "roothub: submit IRQ NOT implemented\n"); + return 0; + } + + 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); + + 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; +} diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 09da121374..721c0dec63 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -1,7 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-only config USB_DWC3 tristate "DesignWare USB3 DRD Core Support" depends on USB && HAS_DMA + depends on OFDEVICE select USB_XHCI + select USB_OTGDEV help Say Y or M here if your system has a Dual Role SuperSpeed USB controller based on the DesignWare USB3 IP Core. @@ -29,6 +32,7 @@ config USB_DWC3_GADGET config USB_DWC3_DUAL_ROLE bool "Dual Role mode" + depends on USB_GADGET help This is the default mode of working of DWC3 controller where both host and gadget features are enabled. diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index d0c812c883..6672913333 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: GPL-2.0 +# SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_USB_DWC3) += dwc3.o diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index d3f9d9ef27..8e6dc59a5d 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * core.c - DesignWare USB3 DRD Controller Core file * - * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com * * Authors: Felipe Balbi <balbi@ti.com>, * Sebastian Andrzej Siewior <bigeasy@linutronix.de> @@ -14,11 +14,13 @@ #include <dma.h> #include <driver.h> #include <init.h> +#include <linux/reset.h> -#include "gadget.h" #include "core.h" +#include "gadget.h" #include "io.h" +#include "debug.h" #define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */ @@ -29,7 +31,7 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc) { enum usb_dr_mode mode; - struct device_d *dev = dwc->dev; + struct device *dev = dwc->dev; unsigned int hw_mode; if (dwc->dr_mode == USB_DR_MODE_UNKNOWN) @@ -66,8 +68,10 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc) * mode. If the controller supports DRD but the dr_mode is not * specified or set to OTG, then set the mode to peripheral. */ - if (mode == USB_DR_MODE_OTG && - dwc->revision >= DWC3_REVISION_330A) + if (mode == USB_DR_MODE_OTG && !dwc->edev && + (!IS_ENABLED(CONFIG_USB_ROLE_SWITCH) || + !of_property_read_bool(dwc->dev->of_node, "usb-role-switch")) && + !DWC3_VER_IS_PRIOR(DWC3, 330A)) mode = USB_DR_MODE_PERIPHERAL; } @@ -94,25 +98,28 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode) dwc->current_dr_role = mode; } +u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type) +{ + struct dwc3 *dwc = dep->dwc; + u32 reg; + + dwc3_writel(dwc->regs, DWC3_GDBGFIFOSPACE, + DWC3_GDBGFIFOSPACE_NUM(dep->number) | + DWC3_GDBGFIFOSPACE_TYPE(type)); + + reg = dwc3_readl(dwc->regs, DWC3_GDBGFIFOSPACE); + + return DWC3_GDBGFIFOSPACE_SPACE_AVAILABLE(reg); +} + /** * dwc3_core_soft_reset - Issues core soft reset and PHY reset * @dwc: pointer to our context structure */ -static int dwc3_core_soft_reset(struct dwc3 *dwc) +int dwc3_core_soft_reset(struct dwc3 *dwc) { u32 reg; int retries = 1000; - int ret; - - ret = phy_init(dwc->usb2_generic_phy); - if (ret < 0) - return ret; - - ret = phy_init(dwc->usb3_generic_phy); - if (ret < 0) { - phy_exit(dwc->usb2_generic_phy); - return ret; - } /* * We're resetting only the device side because, if we're in host mode, @@ -124,33 +131,94 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc) reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg |= DWC3_DCTL_CSFTRST; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + reg &= ~DWC3_DCTL_RUN_STOP; + dwc3_gadget_dctl_write_safe(dwc, reg); + + /* + * For DWC_usb31 controller 1.90a and later, the DCTL.CSFRST bit + * is cleared only after all the clocks are synchronized. This can + * take a little more than 50ms. Set the polling rate at 20ms + * for 10 times instead. + */ + if (DWC3_VER_IS_WITHIN(DWC31, 190A, ANY) || DWC3_IP_IS(DWC32)) + retries = 10; do { reg = dwc3_readl(dwc->regs, DWC3_DCTL); if (!(reg & DWC3_DCTL_CSFTRST)) goto done; - udelay(1); + if (DWC3_VER_IS_WITHIN(DWC31, 190A, ANY) || DWC3_IP_IS(DWC32)) + mdelay(20); + else + udelay(1); } while (--retries); - phy_exit(dwc->usb3_generic_phy); - phy_exit(dwc->usb2_generic_phy); - + dev_warn(dwc->dev, "DWC3 controller soft reset failed.\n"); return -ETIMEDOUT; done: /* - * For DWC_usb31 controller, once DWC3_DCTL_CSFTRST bit is cleared, - * we must wait at least 50ms before accessing the PHY domain - * (synchronization delay). DWC_usb31 programming guide section 1.3.2. + * For DWC_usb31 controller 1.80a and prior, once DCTL.CSFRST bit + * is cleared, we must wait at least 50ms before accessing the PHY + * domain (synchronization delay). */ - if (dwc3_is_usb31(dwc)) + if (DWC3_VER_IS_WITHIN(DWC31, ANY, 180A)) mdelay(50); return 0; } +/* + * dwc3_frame_length_adjustment - Adjusts frame length if required + * @dwc3: Pointer to our controller context structure + */ +static void dwc3_frame_length_adjustment(struct dwc3 *dwc) +{ + u32 reg; + u32 dft; + + if (DWC3_VER_IS_PRIOR(DWC3, 250A)) + return; + + if (dwc->fladj == 0) + return; + + reg = dwc3_readl(dwc->regs, DWC3_GFLADJ); + dft = reg & DWC3_GFLADJ_30MHZ_MASK; + if (dft != dwc->fladj) { + reg &= ~DWC3_GFLADJ_30MHZ_MASK; + reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | dwc->fladj; + dwc3_writel(dwc->regs, DWC3_GFLADJ, reg); + } +} + +/** + * dwc3_ref_clk_period - Reference clock period configuration + * Default reference clock period depends on hardware + * configuration. For systems with reference clock that differs + * from the default, this will set clock period in DWC3_GUCTL + * register. + * @dwc: Pointer to our controller context structure + */ +static void dwc3_ref_clk_period(struct dwc3 *dwc) +{ + u32 reg; + u32 dft; + + if (DWC3_VER_IS_PRIOR(DWC3, 250A)) + return; + + if (dwc->fladj == 0) + return; + + reg = dwc3_readl(dwc->regs, DWC3_GFLADJ); + dft = reg & DWC3_GFLADJ_30MHZ_MASK; + reg &= ~DWC3_GFLADJ_30MHZ_MASK; + reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | dwc->fladj; + dwc3_writel(dwc->regs, DWC3_GFLADJ, reg); +} + /** * dwc3_free_one_event_buffer - Frees one event buffer * @dwc: Pointer to our controller context structure @@ -159,7 +227,7 @@ done: static void dwc3_free_one_event_buffer(struct dwc3 *dwc, struct dwc3_event_buffer *evt) { - dma_free_coherent(evt->buf, 0, sizeof(dma_addr_t)); + dma_free_coherent(evt->buf, evt->dma, evt->length); } /** @@ -171,16 +239,20 @@ static void dwc3_free_one_event_buffer(struct dwc3 *dwc, * otherwise ERR_PTR(errno). */ static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc, - unsigned length) + unsigned int length) { struct dwc3_event_buffer *evt; - evt = xzalloc(sizeof(*evt)); + evt = kzalloc(sizeof(*evt), GFP_KERNEL); if (!evt) return ERR_PTR(-ENOMEM); evt->dwc = dwc; evt->length = length; + evt->cache = kzalloc(length, GFP_KERNEL); + if (!evt->cache) + return ERR_PTR(-ENOMEM); + evt->buf = dma_alloc_coherent(length, &evt->dma); if (!evt->buf) return ERR_PTR(-ENOMEM); @@ -209,7 +281,7 @@ static void dwc3_free_event_buffers(struct dwc3 *dwc) * Returns 0 on success otherwise negative errno. In the error case, dwc * may contain some buffers allocated but not all which were requested. */ -static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length) +static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned int length) { struct dwc3_event_buffer *evt; @@ -229,7 +301,7 @@ static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length) * * Returns 0 on success otherwise negative errno. */ -static int dwc3_event_buffers_setup(struct dwc3 *dwc) +int dwc3_event_buffers_setup(struct dwc3 *dwc) { struct dwc3_event_buffer *evt; @@ -246,8 +318,7 @@ static int dwc3_event_buffers_setup(struct dwc3 *dwc) return 0; } - -static void dwc3_event_buffers_cleanup(struct dwc3 *dwc) +void dwc3_event_buffers_cleanup(struct dwc3 *dwc) { struct dwc3_event_buffer *evt; @@ -290,11 +361,15 @@ static int dwc3_setup_scratch_buffers(struct dwc3 *dwc) if (!dwc->nr_scratch) return 0; - scratch_addr = dma_map_single(dwc->dev, dwc->scratchbuf, - dwc->nr_scratch * DWC3_SCRATCHBUF_SIZE, - DMA_BIDIRECTIONAL); - if (dma_mapping_error(dwc->dev, scratch_addr)) { - dev_err(dwc->dev, "failed to map scratch buffer\n"); + /* should never fall here */ + if (!WARN_ON(dwc->scratchbuf)) + return 0; + + scratch_addr = dma_map_single(dwc->sysdev, dwc->scratchbuf, + dwc->nr_scratch * DWC3_SCRATCHBUF_SIZE, + DMA_BIDIRECTIONAL); + if (dma_mapping_error(dwc->sysdev, scratch_addr)) { + dev_err(dwc->sysdev, "failed to map scratch buffer\n"); ret = -EFAULT; goto err0; } @@ -318,42 +393,28 @@ static int dwc3_setup_scratch_buffers(struct dwc3 *dwc) return 0; err1: - dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch * - DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL); + dma_unmap_single(dwc->sysdev, dwc->scratch_addr, dwc->nr_scratch * + DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL); err0: return ret; } -static const struct clk_bulk_data dwc3_core_clks[] = { - { .id = "ref" }, - { .id = "bus_early" }, - { .id = "suspend" }, -}; - -/* - * dwc3_frame_length_adjustment - Adjusts frame length if required - * @dwc3: Pointer to our controller context structure - */ -static void dwc3_frame_length_adjustment(struct dwc3 *dwc) +static void dwc3_free_scratch_buffers(struct dwc3 *dwc) { - u32 reg; - u32 dft; + if (!dwc->has_hibernation) + return; - if (dwc->revision < DWC3_REVISION_250A) + if (!dwc->nr_scratch) return; - if (dwc->fladj == 0) + /* should never fall here */ + if (!WARN_ON(dwc->scratchbuf)) return; - reg = dwc3_readl(dwc->regs, DWC3_GFLADJ); - dft = reg & DWC3_GFLADJ_30MHZ_MASK; - if (!WARN(dft == dwc->fladj, - "request value same as default, ignoring\n")) { - reg &= ~DWC3_GFLADJ_30MHZ_MASK; - reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | dwc->fladj; - dwc3_writel(dwc->regs, DWC3_GFLADJ, reg); - } + dma_unmap_single(dwc->sysdev, dwc->scratch_addr, dwc->nr_scratch * + DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL); + kfree(dwc->scratchbuf); } static void dwc3_core_num_eps(struct dwc3 *dwc) @@ -376,6 +437,9 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc) parms->hwparams6 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6); parms->hwparams7 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS7); parms->hwparams8 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS8); + + if (DWC3_IP_IS(DWC32)) + parms->hwparams9 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS9); } static int dwc3_core_ulpi_init(struct dwc3 *dwc) @@ -404,8 +468,11 @@ static int dwc3_core_ulpi_init(struct dwc3 *dwc) */ static int dwc3_phy_setup(struct dwc3 *dwc) { + unsigned int hw_mode; u32 reg; + hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0); + reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); /* @@ -420,9 +487,17 @@ static int dwc3_phy_setup(struct dwc3 *dwc) * will be '0' when the core is reset. Application needs to set it * to '1' after the core initialization is completed. */ - if (dwc->revision > DWC3_REVISION_194A) + if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A)) reg |= DWC3_GUSB3PIPECTL_SUSPHY; + /* + * For DRD controllers, GUSB3PIPECTL.SUSPENDENABLE must be cleared after + * power-on reset, and it can be set after core initialization, which is + * after device soft-reset during initialization. + */ + if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD) + reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; + if (dwc->u2ss_inp3_quirk) reg |= DWC3_GUSB3PIPECTL_U2SSINP3OK; @@ -473,9 +548,8 @@ static int dwc3_phy_setup(struct dwc3 *dwc) if (!(reg & DWC3_GUSB2PHYCFG_ULPI_UTMI)) break; } - /* FALLTHROUGH */ + fallthrough; case DWC3_GHWPARAMS3_HSPHY_IFC_ULPI: - /* FALLTHROUGH */ default: break; } @@ -503,9 +577,17 @@ static int dwc3_phy_setup(struct dwc3 *dwc) * be '0' when the core is reset. Application needs to set it to * '1' after the core initialization is completed. */ - if (dwc->revision > DWC3_REVISION_194A) + if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A)) reg |= DWC3_GUSB2PHYCFG_SUSPHY; + /* + * For DRD controllers, GUSB2PHYCFG.SUSPHY must be cleared after + * power-on reset, and it can be set after core initialization, which is + * after device soft-reset during initialization. + */ + if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD) + reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; + if (dwc->dis_u2_susphy_quirk) reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; @@ -514,7 +596,7 @@ static int dwc3_phy_setup(struct dwc3 *dwc) else reg |= DWC3_GUSB2PHYCFG_ENBLSLPM; - if (dwc->dis_u2_freeclk_exists_quirk) + if (dwc->dis_u2_freeclk_exists_quirk || dwc->gfladj_refclk_lpm_sel) reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS; dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); @@ -526,14 +608,17 @@ static void dwc3_core_exit(struct dwc3 *dwc) { dwc3_event_buffers_cleanup(dwc); - phy_exit(dwc->usb2_generic_phy); - phy_exit(dwc->usb3_generic_phy); - + usb_phy_set_suspend(dwc->usb2_phy, 1); + usb_phy_set_suspend(dwc->usb3_phy, 1); phy_power_off(dwc->usb2_generic_phy); phy_power_off(dwc->usb3_generic_phy); - clk_bulk_disable(dwc->num_clks, dwc->clks); - dwc3_free_event_buffers(dwc); + usb_phy_shutdown(dwc->usb2_phy); + usb_phy_shutdown(dwc->usb3_phy); + phy_exit(dwc->usb2_generic_phy); + phy_exit(dwc->usb3_generic_phy); + + reset_control_assert(dwc->reset); } static bool dwc3_core_is_valid(struct dwc3 *dwc) @@ -541,15 +626,13 @@ static bool dwc3_core_is_valid(struct dwc3 *dwc) u32 reg; reg = dwc3_readl(dwc->regs, DWC3_GSNPSID); + dwc->ip = DWC3_GSNPS_ID(reg); /* This should read as U3 followed by revision number */ - if ((reg & DWC3_GSNPSID_MASK) == 0x55330000) { - /* Detected DWC_usb3 IP */ + if (DWC3_IP_IS(DWC3)) { dwc->revision = reg; - } else if ((reg & DWC3_GSNPSID_MASK) == 0x33310000) { - /* Detected DWC_usb31 IP */ + } else if (DWC3_IP_IS(DWC31) || DWC3_IP_IS(DWC32)) { dwc->revision = dwc3_readl(dwc->regs, DWC3_VER_NUMBER); - dwc->revision |= DWC3_REVISION_IS_DWC31; dwc->version_type = dwc3_readl(dwc->regs, DWC3_VER_TYPE); } else { return false; @@ -582,8 +665,7 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc) */ if ((dwc->dr_mode == USB_DR_MODE_HOST || dwc->dr_mode == USB_DR_MODE_OTG) && - (dwc->revision >= DWC3_REVISION_210A && - dwc->revision <= DWC3_REVISION_250A)) + DWC3_VER_IS_WITHIN(DWC3, 210A, 250A)) reg |= DWC3_GCTL_DSBLCLKGTNG | DWC3_GCTL_SOFITPSYNC; else reg &= ~DWC3_GCTL_DSBLCLKGTNG; @@ -609,8 +691,8 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc) dwc->is_fpga = true; } - WARN(dwc->disable_scramble_quirk && !dwc->is_fpga, - "disable_scramble cannot be used on non-FPGA builds\n"); + WARN_ONCE(dwc->disable_scramble_quirk && !dwc->is_fpga, + "disable_scramble cannot be used on non-FPGA builds\n"); if (dwc->disable_scramble_quirk && dwc->is_fpga) reg |= DWC3_GCTL_DISSCRAMBLE; @@ -626,25 +708,26 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc) * and falls back to high-speed mode which causes * the device to enter a Connect/Disconnect loop */ - if (dwc->revision < DWC3_REVISION_190A) + if (DWC3_VER_IS_PRIOR(DWC3, 190A)) reg |= DWC3_GCTL_U2RSTECN; dwc3_writel(dwc->regs, DWC3_GCTL, reg); } static int dwc3_core_get_phy(struct dwc3 *dwc); +static int dwc3_core_ulpi_init(struct dwc3 *dwc); /* set global incr burst type configuration registers */ static void dwc3_set_incr_burst_type(struct dwc3 *dwc) { - struct device_d *dev = dwc->dev; + struct device *dev = dwc->dev; /* incrx_mode : for INCR burst type. */ bool incrx_mode; /* incrx_size : for size of INCRX burst. */ u32 incrx_size; u32 *vals; u32 cfg; - int ntype = 0; + int ntype; int ret; int i; @@ -657,24 +740,19 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc) * result = 1, means INCRx burst mode supported. * result > 1, means undefined length burst mode supported. */ - of_find_property(dev->device_node, "snps,incr-burst-type-adjustment", - &ntype); - - ntype /= sizeof(u32); - + ntype = of_property_count_elems_of_size(dev->of_node, + "snps,incr-burst-type-adjustment", + sizeof(u32)); if (ntype <= 0) return; vals = kcalloc(ntype, sizeof(u32), GFP_KERNEL); - if (!vals) { - dev_err(dev, "Error to get memory\n"); + if (!vals) return; - } /* Get INCR burst type, and parse it */ - ret = of_property_read_u32_array(dev->device_node, - "snps,incr-burst-type-adjustment", - vals, ntype); + ret = of_property_read_u32_array(dev->of_node, + "snps,incr-burst-type-adjustment", vals, ntype); if (ret) { kfree(vals); dev_err(dev, "Error to get property\n"); @@ -741,14 +819,11 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc) */ static int dwc3_core_init(struct dwc3 *dwc) { + unsigned int hw_mode; u32 reg; int ret; - if (!dwc3_core_is_valid(dwc)) { - dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n"); - ret = -ENODEV; - goto err0; - } + hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0); /* * Write Linux Version Code to our GUID register so it's easy to figure @@ -756,21 +831,19 @@ static int dwc3_core_init(struct dwc3 *dwc) */ dwc3_writel(dwc->regs, DWC3_GUID, 0xdeadbeef); - /* Handle USB2.0-only core configuration */ - if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) == - DWC3_GHWPARAMS3_SSPHY_IFC_DIS) { - if (dwc->maximum_speed == USB_SPEED_SUPER) - dwc->maximum_speed = USB_SPEED_HIGH; - } - ret = dwc3_phy_setup(dwc); if (ret) goto err0; if (!dwc->ulpi_ready) { ret = dwc3_core_ulpi_init(dwc); - if (ret) + if (ret) { + if (ret == -ETIMEDOUT) { + dwc3_core_soft_reset(dwc); + ret = -EPROBE_DEFER; + } goto err0; + } dwc->ulpi_ready = true; } @@ -781,34 +854,66 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc->phys_ready = true; } + usb_phy_init(dwc->usb2_phy); + usb_phy_init(dwc->usb3_phy); + ret = phy_init(dwc->usb2_generic_phy); + if (ret < 0) + goto err0a; + + ret = phy_init(dwc->usb3_generic_phy); + if (ret < 0) { + phy_exit(dwc->usb2_generic_phy); + goto err0a; + } + ret = dwc3_core_soft_reset(dwc); if (ret) - goto err0a; + goto err1; + + if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD && + !DWC3_VER_IS_WITHIN(DWC3, ANY, 194A)) { + if (!dwc->dis_u3_susphy_quirk) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); + reg |= DWC3_GUSB3PIPECTL_SUSPHY; + dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); + } + + if (!dwc->dis_u2_susphy_quirk) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + reg |= DWC3_GUSB2PHYCFG_SUSPHY; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + } + } dwc3_core_setup_global_control(dwc); dwc3_core_num_eps(dwc); ret = dwc3_setup_scratch_buffers(dwc); if (ret) - goto err0a; + goto err1; /* Adjust Frame Length */ dwc3_frame_length_adjustment(dwc); + /* Adjust Reference Clock Period */ + dwc3_ref_clk_period(dwc); + dwc3_set_incr_burst_type(dwc); + usb_phy_set_suspend(dwc->usb2_phy, 0); + usb_phy_set_suspend(dwc->usb3_phy, 0); ret = phy_power_on(dwc->usb2_generic_phy); if (ret < 0) - goto err1; + goto err2; ret = phy_power_on(dwc->usb3_generic_phy); if (ret < 0) - goto err2; + goto err3; ret = dwc3_event_buffers_setup(dwc); if (ret) { dev_err(dwc->dev, "failed to setup event buffers\n"); - goto err3; + goto err4; } /* @@ -816,25 +921,57 @@ static int dwc3_core_init(struct dwc3 *dwc) * the DWC_usb3 controller. It is NOT available in the * DWC_usb31 controller. */ - if (!dwc3_is_usb31(dwc) && dwc->revision >= DWC3_REVISION_310A) { + if (DWC3_VER_IS_WITHIN(DWC3, 310A, ANY)) { reg = dwc3_readl(dwc->regs, DWC3_GUCTL2); reg |= DWC3_GUCTL2_RST_ACTBITLATER; dwc3_writel(dwc->regs, DWC3_GUCTL2, reg); } - if (dwc->revision >= DWC3_REVISION_250A) { + /* + * When configured in HOST mode, after issuing U3/L2 exit controller + * fails to send proper CRC checksum in CRC5 feild. Because of this + * behaviour Transaction Error is generated, resulting in reset and + * re-enumeration of usb device attached. All the termsel, xcvrsel, + * opmode becomes 0 during end of resume. Enabling bit 10 of GUCTL1 + * will correct this problem. This option is to support certain + * legacy ULPI PHYs. + */ + if (dwc->resume_hs_terminations) { + reg = dwc3_readl(dwc->regs, DWC3_GUCTL1); + reg |= DWC3_GUCTL1_RESUME_OPMODE_HS_HOST; + dwc3_writel(dwc->regs, DWC3_GUCTL1, reg); + } + + if (!DWC3_VER_IS_PRIOR(DWC3, 250A)) { reg = dwc3_readl(dwc->regs, DWC3_GUCTL1); /* * Enable hardware control of sending remote wakeup * in HS when the device is in the L1 state. */ - if (dwc->revision >= DWC3_REVISION_290A) + if (!DWC3_VER_IS_PRIOR(DWC3, 290A)) reg |= DWC3_GUCTL1_DEV_L1_EXIT_BY_HW; + /* + * Decouple USB 2.0 L1 & L2 events which will allow for + * gadget driver to only receive U3/L2 suspend & wakeup + * events and prevent the more frequent L1 LPM transitions + * from interrupting the driver. + */ + if (!DWC3_VER_IS_PRIOR(DWC3, 300A)) + reg |= DWC3_GUCTL1_DEV_DECOUPLE_L1L2_EVT; + if (dwc->dis_tx_ipgap_linecheck_quirk) reg |= DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS; + if (dwc->parkmode_disable_ss_quirk) + reg |= DWC3_GUCTL1_PARKMODE_DISABLE_SS; + + if (DWC3_VER_IS_WITHIN(DWC3, 290A, ANY) && + (dwc->maximum_speed == USB_SPEED_HIGH || + dwc->maximum_speed == USB_SPEED_FULL)) + reg |= DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK; + dwc3_writel(dwc->regs, DWC3_GUCTL1, reg); } @@ -858,7 +995,7 @@ static int dwc3_core_init(struct dwc3 *dwc) * Must config both number of packets and max burst settings to enable * RX and/or TX threshold. */ - if (dwc3_is_usb31(dwc) && dwc->dr_mode == USB_DR_MODE_HOST) { + if (!DWC3_IP_IS(DWC3) && dwc->dr_mode == USB_DR_MODE_HOST) { u8 rx_thr_num = dwc->rx_thr_num_pkt_prd; u8 rx_maxburst = dwc->rx_max_burst_prd; u8 tx_thr_num = dwc->tx_thr_num_pkt_prd; @@ -893,79 +1030,85 @@ static int dwc3_core_init(struct dwc3 *dwc) return 0; -err3: +err4: phy_power_off(dwc->usb3_generic_phy); -err2: + +err3: phy_power_off(dwc->usb2_generic_phy); + +err2: + usb_phy_set_suspend(dwc->usb2_phy, 1); + usb_phy_set_suspend(dwc->usb3_phy, 1); + err1: + usb_phy_shutdown(dwc->usb2_phy); + usb_phy_shutdown(dwc->usb3_phy); phy_exit(dwc->usb2_generic_phy); phy_exit(dwc->usb3_generic_phy); + err0a: + dwc3_ulpi_exit(dwc); + err0: return ret; } static int dwc3_core_get_phy(struct dwc3 *dwc) { - struct device_d *dev = dwc->dev; + struct device *dev = dwc->dev; int ret; dwc->usb2_generic_phy = phy_get(dev, "usb2-phy"); if (IS_ERR(dwc->usb2_generic_phy)) { ret = PTR_ERR(dwc->usb2_generic_phy); - if (ret == -ENOSYS || ret == -ENODEV) { - dev_err(dev, "no usb2 phy configured\n"); + if (ret == -ENOSYS || ret == -ENODEV) dwc->usb2_generic_phy = NULL; - } else if (ret == -EPROBE_DEFER) { - return ret; - } else { - dev_err(dev, "no usb2 phy configured\n"); - return ret; - } + else + return dev_err_probe(dev, ret, "no usb2 phy configured\n"); } dwc->usb3_generic_phy = phy_get(dev, "usb3-phy"); if (IS_ERR(dwc->usb3_generic_phy)) { ret = PTR_ERR(dwc->usb3_generic_phy); - if (ret == -ENOSYS || ret == -ENODEV) { - dev_err(dev, "no usb2 phy configured\n"); + if (ret == -ENOSYS || ret == -ENODEV) dwc->usb3_generic_phy = NULL; - } else if (ret == -EPROBE_DEFER) { - return ret; - } else { - dev_err(dev, "no usb3 phy configured\n"); - return ret; - } + else + return dev_err_probe(dev, ret, "no usb3 phy configured\n"); } return 0; } -static int dwc3_core_init_mode(struct dwc3 *dwc) +static int dwc3_set_dr_mode(void *ctx, enum usb_dr_mode mode) { - struct device_d *dev = dwc->dev; + struct dwc3 *dwc = ctx; + struct device *dev = dwc->dev; int ret; - switch (dwc->dr_mode) { + switch (mode) { case USB_DR_MODE_PERIPHERAL: dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE); +// if (dwc->usb2_phy) +// otg_set_vbus(dwc->usb2_phy->otg, false); + phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE); + phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_DEVICE); + ret = dwc3_gadget_init(dwc); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to initialize gadget\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to initialize gadget\n"); break; case USB_DR_MODE_HOST: dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST); +// if (dwc->usb2_phy) +// otg_set_vbus(dwc->usb2_phy->otg, true); + phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST); + phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST); + ret = dwc3_host_init(dwc); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to initialize host\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to initialize host\n"); break; default: dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode); @@ -975,15 +1118,46 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) return 0; } +static int dwc3_core_init_mode(struct dwc3 *dwc) +{ + if (dwc->dr_mode == USB_DR_MODE_OTG) + return usb_register_otg_device(dwc->dev, dwc3_set_dr_mode, dwc); + else + return dwc3_set_dr_mode(dwc, dwc->dr_mode); +} + +static void dwc3_core_exit_mode(struct dwc3 *dwc) +{ + switch (dwc->dr_mode) { + case USB_DR_MODE_PERIPHERAL: + dwc3_gadget_exit(dwc); + break; + case USB_DR_MODE_HOST: + dwc3_host_exit(dwc); + break; + default: + /* do nothing */ + break; + } + + /* de-assert DRVVBUS for HOST and OTG mode */ + dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE); +} + static void dwc3_get_properties(struct dwc3 *dwc) { - struct device_d *dev = dwc->dev; + struct device *dev = dwc->dev; u8 lpm_nyet_threshold; u8 tx_de_emphasis; u8 hird_threshold; + u8 rx_thr_num_pkt_prd = 0; + u8 rx_max_burst_prd = 0; + u8 tx_thr_num_pkt_prd = 0; + u8 tx_max_burst_prd = 0; + u8 tx_fifo_resize_max_num; /* default to highest possible threshold */ - lpm_nyet_threshold = 0xff; + lpm_nyet_threshold = 0xf; /* default to -3.5dB de-emphasis */ tx_de_emphasis = 1; @@ -994,39 +1168,142 @@ static void dwc3_get_properties(struct dwc3 *dwc) */ hird_threshold = 12; - dwc->maximum_speed = of_usb_get_maximum_speed(dev->device_node, NULL); - dwc->dr_mode = of_usb_get_dr_mode(dev->device_node, NULL); - dwc->hsphy_mode = of_usb_get_phy_mode(dev->device_node, NULL); + /* + * default to a TXFIFO size large enough to fit 6 max packets. This + * allows for systems with larger bus latencies to have some headroom + * for endpoints that have a large bMaxBurst value. + */ + tx_fifo_resize_max_num = 6; + + dwc->maximum_speed = of_usb_get_maximum_speed(dev->of_node, NULL); +// dwc->max_ssp_rate = usb_get_maximum_ssp_rate(dev); + dwc->dr_mode = of_usb_get_dr_mode(dev->of_node, NULL); + dwc->hsphy_mode = of_usb_get_phy_mode(dev->of_node, NULL); + + dwc->sysdev_is_parent = of_property_read_bool(dev->of_node, + "linux,sysdev_is_parent"); + if (dwc->sysdev_is_parent) + dwc->sysdev = dwc->dev->parent; + else + dwc->sysdev = dwc->dev; + + dwc->has_lpm_erratum = of_property_read_bool(dev->of_node, + "snps,has-lpm-erratum"); + of_property_read_u8(dev->of_node, "snps,lpm-nyet-threshold", + &lpm_nyet_threshold); + dwc->is_utmi_l1_suspend = of_property_read_bool(dev->of_node, + "snps,is-utmi-l1-suspend"); + of_property_read_u8(dev->of_node, "snps,hird-threshold", + &hird_threshold); + dwc->dis_start_transfer_quirk = of_property_read_bool(dev->of_node, + "snps,dis-start-transfer-quirk"); + dwc->usb3_lpm_capable = of_property_read_bool(dev->of_node, + "snps,usb3_lpm_capable"); + dwc->usb2_lpm_disable = of_property_read_bool(dev->of_node, + "snps,usb2-lpm-disable"); + dwc->usb2_gadget_lpm_disable = of_property_read_bool(dev->of_node, + "snps,usb2-gadget-lpm-disable"); + of_property_read_u8(dev->of_node, "snps,rx-thr-num-pkt-prd", + &rx_thr_num_pkt_prd); + of_property_read_u8(dev->of_node, "snps,rx-max-burst-prd", + &rx_max_burst_prd); + of_property_read_u8(dev->of_node, "snps,tx-thr-num-pkt-prd", + &tx_thr_num_pkt_prd); + of_property_read_u8(dev->of_node, "snps,tx-max-burst-prd", + &tx_max_burst_prd); + dwc->do_fifo_resize = of_property_read_bool(dev->of_node, + "tx-fifo-resize"); + if (dwc->do_fifo_resize) + of_property_read_u8(dev->of_node, "tx-fifo-max-num", + &tx_fifo_resize_max_num); + + dwc->disable_scramble_quirk = of_property_read_bool(dev->of_node, + "snps,disable_scramble_quirk"); + dwc->u2exit_lfps_quirk = of_property_read_bool(dev->of_node, + "snps,u2exit_lfps_quirk"); + dwc->u2ss_inp3_quirk = of_property_read_bool(dev->of_node, + "snps,u2ss_inp3_quirk"); + dwc->req_p1p2p3_quirk = of_property_read_bool(dev->of_node, + "snps,req_p1p2p3_quirk"); + dwc->del_p1p2p3_quirk = of_property_read_bool(dev->of_node, + "snps,del_p1p2p3_quirk"); + dwc->del_phy_power_chg_quirk = of_property_read_bool(dev->of_node, + "snps,del_phy_power_chg_quirk"); + dwc->lfps_filter_quirk = of_property_read_bool(dev->of_node, + "snps,lfps_filter_quirk"); + dwc->rx_detect_poll_quirk = of_property_read_bool(dev->of_node, + "snps,rx_detect_poll_quirk"); + dwc->dis_u3_susphy_quirk = of_property_read_bool(dev->of_node, + "snps,dis_u3_susphy_quirk"); + dwc->dis_u2_susphy_quirk = of_property_read_bool(dev->of_node, + "snps,dis_u2_susphy_quirk"); + dwc->dis_enblslpm_quirk = of_property_read_bool(dev->of_node, + "snps,dis_enblslpm_quirk"); + dwc->dis_u1_entry_quirk = of_property_read_bool(dev->of_node, + "snps,dis-u1-entry-quirk"); + dwc->dis_u2_entry_quirk = of_property_read_bool(dev->of_node, + "snps,dis-u2-entry-quirk"); + dwc->dis_rxdet_inp3_quirk = of_property_read_bool(dev->of_node, + "snps,dis_rxdet_inp3_quirk"); + dwc->dis_u2_freeclk_exists_quirk = of_property_read_bool(dev->of_node, + "snps,dis-u2-freeclk-exists-quirk"); + dwc->dis_del_phy_power_chg_quirk = of_property_read_bool(dev->of_node, + "snps,dis-del-phy-power-chg-quirk"); + dwc->dis_tx_ipgap_linecheck_quirk = of_property_read_bool(dev->of_node, + "snps,dis-tx-ipgap-linecheck-quirk"); + dwc->resume_hs_terminations = of_property_read_bool(dev->of_node, + "snps,resume-hs-terminations"); + dwc->parkmode_disable_ss_quirk = of_property_read_bool(dev->of_node, + "snps,parkmode-disable-ss-quirk"); + dwc->gfladj_refclk_lpm_sel = of_property_read_bool(dev->of_node, + "snps,gfladj-refclk-lpm-sel-quirk"); + + dwc->tx_de_emphasis_quirk = of_property_read_bool(dev->of_node, + "snps,tx_de_emphasis_quirk"); + of_property_read_u8(dev->of_node, "snps,tx_de_emphasis", + &tx_de_emphasis); + of_property_read_string(dev->of_node, "snps,hsphy_interface", + &dwc->hsphy_interface); + of_property_read_u32(dev->of_node, "snps,quirk-frame-length-adjustment", + &dwc->fladj); + of_property_read_u32(dev->of_node, "snps,ref-clock-period-ns", + &dwc->ref_clk_per); + + dwc->dis_metastability_quirk = of_property_read_bool(dev->of_node, + "snps,dis_metastability_quirk"); + + dwc->dis_split_quirk = of_property_read_bool(dev->of_node, + "snps,dis-split-quirk"); dwc->lpm_nyet_threshold = lpm_nyet_threshold; dwc->tx_de_emphasis = tx_de_emphasis; - if (of_get_property(dev->device_node, "snps,dis_rxdet_inp3_quirk", - NULL)) - dwc->dis_rxdet_inp3_quirk = 1; + dwc->hird_threshold = hird_threshold; - of_property_read_u32_array(dev->device_node, - "snps,quirk-frame-length-adjustment", - &dwc->fladj, 1); + dwc->rx_thr_num_pkt_prd = rx_thr_num_pkt_prd; + dwc->rx_max_burst_prd = rx_max_burst_prd; - dwc->hird_threshold = hird_threshold - | (dwc->is_utmi_l1_suspend << 4); + dwc->tx_thr_num_pkt_prd = tx_thr_num_pkt_prd; + dwc->tx_max_burst_prd = tx_max_burst_prd; dwc->imod_interval = 0; + + dwc->tx_fifo_resize_max_num = tx_fifo_resize_max_num; } /* check whether the core supports IMOD */ bool dwc3_has_imod(struct dwc3 *dwc) { - return ((dwc3_is_usb3(dwc) && - dwc->revision >= DWC3_REVISION_300A) || - (dwc3_is_usb31(dwc) && - dwc->revision >= DWC3_USB31_REVISION_120A)); + return DWC3_VER_IS_WITHIN(DWC3, 300A, ANY) || + DWC3_VER_IS_WITHIN(DWC31, 120A, ANY) || + DWC3_IP_IS(DWC32); } static void dwc3_check_params(struct dwc3 *dwc) { - struct device_d *dev = dwc->dev; + struct device *dev = dwc->dev; + unsigned int hwparam_gen = + DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3); /* Check for proper value of imod_interval */ if (dwc->imod_interval && !dwc3_has_imod(dwc)) { @@ -1042,57 +1319,88 @@ static void dwc3_check_params(struct dwc3 *dwc) * affected version. */ if (!dwc->imod_interval && - (dwc->revision == DWC3_REVISION_300A)) + DWC3_VER_IS(DWC3, 300A)) dwc->imod_interval = 1; /* Check the maximum_speed parameter */ switch (dwc->maximum_speed) { - case USB_SPEED_LOW: case USB_SPEED_FULL: case USB_SPEED_HIGH: + break; case USB_SPEED_SUPER: + if (hwparam_gen == DWC3_GHWPARAMS3_SSPHY_IFC_DIS) + dev_warn(dev, "UDC doesn't support Gen 1\n"); + break; case USB_SPEED_SUPER_PLUS: + if ((DWC3_IP_IS(DWC32) && + hwparam_gen == DWC3_GHWPARAMS3_SSPHY_IFC_DIS) || + (!DWC3_IP_IS(DWC32) && + hwparam_gen != DWC3_GHWPARAMS3_SSPHY_IFC_GEN2)) + dev_warn(dev, "UDC doesn't support SSP\n"); break; default: dev_err(dev, "invalid maximum_speed parameter %d\n", dwc->maximum_speed); - /* fall through */ + fallthrough; case USB_SPEED_UNKNOWN: - /* default to superspeed */ - dwc->maximum_speed = USB_SPEED_SUPER; - - /* - * default to superspeed plus if we are capable. - */ - if (dwc3_is_usb31(dwc) && - (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) == - DWC3_GHWPARAMS3_SSPHY_IFC_GEN2)) + switch (hwparam_gen) { + case DWC3_GHWPARAMS3_SSPHY_IFC_GEN2: dwc->maximum_speed = USB_SPEED_SUPER_PLUS; - + break; + case DWC3_GHWPARAMS3_SSPHY_IFC_GEN1: + if (DWC3_IP_IS(DWC32)) + dwc->maximum_speed = USB_SPEED_SUPER_PLUS; + else + dwc->maximum_speed = USB_SPEED_SUPER; + break; + case DWC3_GHWPARAMS3_SSPHY_IFC_DIS: + dwc->maximum_speed = USB_SPEED_HIGH; + break; + default: + dwc->maximum_speed = USB_SPEED_SUPER; + break; + } break; } -} - -static void dwc3_coresoft_reset(struct dwc3 *dwc) -{ - u32 reg; - - reg = dwc3_readl(dwc->regs, DWC3_GCTL); - reg |= DWC3_GCTL_CORESOFTRESET; - dwc3_writel(dwc->regs, DWC3_GCTL, reg); /* - * Similar reset sequence in U-Boot has a 100ms delay here. In - * practice reset sequence seem to work as expected even - * without a delay. + * Currently the controller does not have visibility into the HW + * parameter to determine the maximum number of lanes the HW supports. + * If the number of lanes is not specified in the device property, then + * set the default to support dual-lane for DWC_usb32 and single-lane + * for DWC_usb31 for super-speed-plus. */ - - reg = dwc3_readl(dwc->regs, DWC3_GCTL); - reg &= ~DWC3_GCTL_CORESOFTRESET; - dwc3_writel(dwc->regs, DWC3_GCTL, reg); + if (dwc->maximum_speed == USB_SPEED_SUPER_PLUS) { + switch (dwc->max_ssp_rate) { + case USB_SSP_GEN_2x1: + if (hwparam_gen == DWC3_GHWPARAMS3_SSPHY_IFC_GEN1) + dev_warn(dev, "UDC only supports Gen 1\n"); + break; + case USB_SSP_GEN_1x2: + case USB_SSP_GEN_2x2: + if (DWC3_IP_IS(DWC31)) + dev_warn(dev, "UDC only supports single lane\n"); + break; + case USB_SSP_GEN_UNKNOWN: + default: + switch (hwparam_gen) { + case DWC3_GHWPARAMS3_SSPHY_IFC_GEN2: + if (DWC3_IP_IS(DWC32)) + dwc->max_ssp_rate = USB_SSP_GEN_2x2; + else + dwc->max_ssp_rate = USB_SSP_GEN_2x1; + break; + case DWC3_GHWPARAMS3_SSPHY_IFC_GEN1: + if (DWC3_IP_IS(DWC32)) + dwc->max_ssp_rate = USB_SSP_GEN_1x2; + break; + } + break; + } + } } -static int dwc3_probe(struct device_d *dev) +static int dwc3_probe(struct device *dev) { struct dwc3 *dwc; int ret; @@ -1100,29 +1408,39 @@ static int dwc3_probe(struct device_d *dev) dwc = xzalloc(sizeof(*dwc)); dev->priv = dwc; - dwc->clks = xmemdup(dwc3_core_clks, sizeof(dwc3_core_clks)); dwc->dev = dev; dwc->regs = dev_get_mem_region(dwc->dev, 0) + DWC3_GLOBALS_REGS_START; dwc3_get_properties(dwc); - if (dev->device_node) { - dwc->num_clks = ARRAY_SIZE(dwc3_core_clks); + if (dev->of_node) { + ret = clk_bulk_get_all(dev, &dwc->clks); + if (ret < 0) + return ret; - if (of_find_property(dev->device_node, "clocks", NULL)) { - ret = clk_bulk_get(dev, dwc->num_clks, dwc->clks); - if (ret == -EPROBE_DEFER) - return ret; - if (ret) - return ret; - } + dwc->num_clks = ret; } ret = clk_bulk_enable(dwc->num_clks, dwc->clks); if (ret) return ret; - dwc3_coresoft_reset(dwc); + dwc->reset = reset_control_get(dev, NULL); + if (IS_ERR(dwc->reset)) { + dev_err(dev, "Failed to get reset control: %pe\n", dwc->reset); + return PTR_ERR(dwc->reset); + } + + reset_control_assert(dwc->reset); + mdelay(1); + reset_control_deassert(dwc->reset); + + if (!dwc3_core_is_valid(dwc)) { + dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n"); + return -ENODEV; + } + + dwc3_core_soft_reset(dwc); dwc3_cache_hwparams(dwc); @@ -1156,25 +1474,32 @@ static int dwc3_probe(struct device_d *dev) return 0; } -static void dwc3_remove(struct device_d *dev) +static void dwc3_remove(struct device *dev) { struct dwc3 *dwc = dev->priv; + dwc3_core_exit_mode(dwc); dwc3_core_exit(dwc); clk_bulk_put(dwc->num_clks, dwc->clks); + dwc3_free_event_buffers(dwc); + dwc3_free_scratch_buffers(dwc); } static const struct of_device_id of_dwc3_match[] = { { - .compatible = "snps,dwc3" + .compatible = "snps,dwc3", + }, + { + .compatible = "synopsys,dwc3", }, { - .compatible = "synopsys,dwc3" + .compatible = "rockchip,rk3568-dwc3", }, { }, }; +MODULE_DEVICE_TABLE(of, of_dwc3_match); -static struct driver_d dwc3_driver = { +static struct driver dwc3_driver = { .probe = dwc3_probe, .remove = dwc3_remove, .name = "dwc3", diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index df0a188a63..52853a4370 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1,8 +1,8 @@ -// SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0 */ /* * core.h - DesignWare USB3 DRD Core Header * - * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com * * Authors: Felipe Balbi <balbi@ti.com>, * Sebastian Andrzej Siewior <bigeasy@linutronix.de> @@ -12,9 +12,11 @@ #define __DRIVERS_USB_DWC3_CORE_H #include <linux/spinlock.h> -#include <usb/usb.h> -#include <usb/phy.h> -#include <usb/gadget.h> +#include <linux/usb/usb.h> +#include <linux/usb/phy.h> +#include <linux/usb/gadget.h> +#include <linux/mutex.h> +#include <linux/completion.h> #define DWC3_MSG_MAX 500 @@ -40,7 +42,7 @@ #define DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE 3 #define DWC3_DEVICE_EVENT_WAKEUP 4 #define DWC3_DEVICE_EVENT_HIBER_REQ 5 -#define DWC3_DEVICE_EVENT_EOPF 6 +#define DWC3_DEVICE_EVENT_SUSPEND 6 #define DWC3_DEVICE_EVENT_SOF 7 #define DWC3_DEVICE_EVENT_ERRATIC_ERROR 9 #define DWC3_DEVICE_EVENT_CMD_CMPL 10 @@ -55,6 +57,7 @@ #define DWC3_GEVNTCOUNT_EHB BIT(31) #define DWC3_GSNPSID_MASK 0xffff0000 #define DWC3_GSNPSREV_MASK 0xffff +#define DWC3_GSNPS_ID(p) (((p) & DWC3_GSNPSID_MASK) >> 16) /* DWC3 registers memory space boundries */ #define DWC3_XHCI_REGS_START 0x0 @@ -123,7 +126,9 @@ #define DWC3_GEVNTCOUNT(n) (0xc40c + ((n) * 0x10)) #define DWC3_GHWPARAMS8 0xc600 +#define DWC3_GUCTL3 0xc60c #define DWC3_GFLADJ 0xc630 +#define DWC3_GHWPARAMS9 0xc6e0 /* Device Registers */ #define DWC3_DCFG 0xc700 @@ -133,6 +138,7 @@ #define DWC3_DGCMDPAR 0xc710 #define DWC3_DGCMD 0xc714 #define DWC3_DALEPENA 0xc720 +#define DWC3_DCFG1 0xc740 /* DWC_usb32 only */ #define DWC3_DEP_BASE(n) (0xc800 + ((n) * 0x10)) #define DWC3_DEPCMDPAR2 0x00 @@ -210,6 +216,7 @@ /* Global Configuration Register */ #define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19) +#define DWC3_GCTL_PWRDNSCALE_MASK GENMASK(31, 19) #define DWC3_GCTL_U2RSTECN BIT(16) #define DWC3_GCTL_RAMCLKSEL(x) (((x) & DWC3_GCTL_CLK_MASK) << 6) #define DWC3_GCTL_CLK_BUS (0) @@ -236,8 +243,12 @@ #define DWC3_GUCTL_HSTINAUTORETRY BIT(14) /* Global User Control 1 Register */ +#define DWC3_GUCTL1_DEV_DECOUPLE_L1L2_EVT BIT(31) #define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28) -#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24) +#define DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK BIT(26) +#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24) +#define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17) +#define DWC3_GUCTL1_RESUME_OPMODE_HS_HOST BIT(10) /* Global Status Register */ #define DWC3_GSTS_OTG_IP BIT(10) @@ -268,6 +279,7 @@ /* Global USB2 PHY Vendor Control Register */ #define DWC3_GUSB2PHYACC_NEWREGREQ BIT(25) +#define DWC3_GUSB2PHYACC_DONE BIT(24) #define DWC3_GUSB2PHYACC_BUSY BIT(23) #define DWC3_GUSB2PHYACC_WRITE BIT(22) #define DWC3_GUSB2PHYACC_ADDR(n) (n << 16) @@ -292,10 +304,14 @@ /* Global TX Fifo Size Register */ #define DWC31_GTXFIFOSIZ_TXFRAMNUM BIT(15) /* DWC_usb31 only */ -#define DWC31_GTXFIFOSIZ_TXFDEF(n) ((n) & 0x7fff) /* DWC_usb31 only */ -#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff) +#define DWC31_GTXFIFOSIZ_TXFDEP(n) ((n) & 0x7fff) /* DWC_usb31 only */ +#define DWC3_GTXFIFOSIZ_TXFDEP(n) ((n) & 0xffff) #define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000) +/* Global RX Fifo Size Register */ +#define DWC31_GRXFIFOSIZ_RXFDEP(n) ((n) & 0x7fff) /* DWC_usb31 only */ +#define DWC3_GRXFIFOSIZ_RXFDEP(n) ((n) & 0xffff) + /* Global Event Size Registers */ #define DWC3_GEVNTSIZ_INTMASK BIT(31) #define DWC3_GEVNTSIZ_SIZE(n) ((n) & 0xffff) @@ -346,18 +362,38 @@ #define DWC3_GHWPARAMS6_SRPSUPPORT BIT(10) #define DWC3_GHWPARAMS6_EN_FPGA BIT(7) +/* DWC_usb32 only */ +#define DWC3_GHWPARAMS6_MDWIDTH(n) ((n) & (0x3 << 8)) + /* Global HWPARAMS7 Register */ #define DWC3_GHWPARAMS7_RAM1_DEPTH(n) ((n) & 0xffff) #define DWC3_GHWPARAMS7_RAM2_DEPTH(n) (((n) >> 16) & 0xffff) +/* Global HWPARAMS9 Register */ +#define DWC3_GHWPARAMS9_DEV_TXF_FLUSH_BYPASS BIT(0) +#define DWC3_GHWPARAMS9_DEV_MST BIT(1) + /* Global Frame Length Adjustment Register */ #define DWC3_GFLADJ_30MHZ_SDBND_SEL BIT(7) #define DWC3_GFLADJ_30MHZ_MASK 0x3f +#define DWC3_GFLADJ_REFCLK_FLADJ_MASK GENMASK(21, 8) +#define DWC3_GFLADJ_REFCLK_LPM_SEL BIT(23) +#define DWC3_GFLADJ_240MHZDECR GENMASK(30, 24) +#define DWC3_GFLADJ_240MHZDECR_PLS1 BIT(31) + +/* Global User Control Register*/ +#define DWC3_GUCTL_REFCLKPER_MASK 0xffc00000 +#define DWC3_GUCTL_REFCLKPER_SEL 22 /* Global User Control Register 2 */ #define DWC3_GUCTL2_RST_ACTBITLATER BIT(14) +/* Global User Control Register 3 */ +#define DWC3_GUCTL3_SPLITDISABLE BIT(14) + /* Device Configuration Register */ +#define DWC3_DCFG_NUMLANES(n) (((n) & 0x3) << 30) /* DWC_usb32 only */ + #define DWC3_DCFG_DEVADDR(addr) ((addr) << 3) #define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f) @@ -366,13 +402,12 @@ #define DWC3_DCFG_SUPERSPEED (4 << 0) #define DWC3_DCFG_HIGHSPEED (0 << 0) #define DWC3_DCFG_FULLSPEED BIT(0) -#define DWC3_DCFG_LOWSPEED (2 << 0) -#define DWC3_DCFG_FULLSPEED1 (3 << 0) #define DWC3_DCFG_NUMP_SHIFT 17 #define DWC3_DCFG_NUMP(n) (((n) >> DWC3_DCFG_NUMP_SHIFT) & 0x1f) #define DWC3_DCFG_NUMP_MASK (0x1f << DWC3_DCFG_NUMP_SHIFT) #define DWC3_DCFG_LPM_CAP BIT(22) +#define DWC3_DCFG_IGNSTRMPP BIT(23) /* Device Control Register */ #define DWC3_DCTL_RUN_STOP BIT(31) @@ -394,8 +429,7 @@ #define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6)) /* These apply for core versions 1.94a and later */ -#define DWC3_DCTL_LPM_ERRATA_MASK DWC3_DCTL_LPM_ERRATA(0xf) -#define DWC3_DCTL_LPM_ERRATA(n) ((n) << 20) +#define DWC3_DCTL_NYET_THRES(n) (((n) & 0xf) << 20) #define DWC3_DCTL_KEEP_CONNECT BIT(19) #define DWC3_DCTL_L1_HIBER_EN BIT(18) @@ -425,7 +459,7 @@ #define DWC3_DEVTEN_CMDCMPLTEN BIT(10) #define DWC3_DEVTEN_ERRTICERREN BIT(9) #define DWC3_DEVTEN_SOFEN BIT(7) -#define DWC3_DEVTEN_EOPFEN BIT(6) +#define DWC3_DEVTEN_U3L2L1SUSPEN BIT(6) #define DWC3_DEVTEN_HIBERNATIONREQEVTEN BIT(5) #define DWC3_DEVTEN_WKUPEVTEN BIT(4) #define DWC3_DEVTEN_ULSTCNGEN BIT(3) @@ -433,6 +467,8 @@ #define DWC3_DEVTEN_USBRSTEN BIT(1) #define DWC3_DEVTEN_DISCONNEVTEN BIT(0) +#define DWC3_DSTS_CONNLANES(n) (((n) >> 30) & 0x3) /* DWC_usb32 only */ + /* Device Status Register */ #define DWC3_DSTS_DCNRD BIT(29) @@ -460,8 +496,6 @@ #define DWC3_DSTS_SUPERSPEED (4 << 0) #define DWC3_DSTS_HIGHSPEED (0 << 0) #define DWC3_DSTS_FULLSPEED BIT(0) -#define DWC3_DSTS_LOWSPEED (2 << 0) -#define DWC3_DSTS_FULLSPEED1 (3 << 0) /* Device Generic Command Register */ #define DWC3_DGCMD_SET_LMP 0x01 @@ -475,6 +509,7 @@ #define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09 #define DWC3_DGCMD_ALL_FIFO_FLUSH 0x0a #define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c +#define DWC3_DGCMD_SET_ENDPOINT_PRIME 0x0d #define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10 #define DWC3_DGCMD_STATUS(n) (((n) >> 12) & 0x0F) @@ -517,6 +552,9 @@ /* The EP number goes 0..31 so ep0 is always out and ep1 is always in */ #define DWC3_DALEPENA_EP(n) BIT(n) +/* DWC_usb32 DCFG1 config */ +#define DWC3_DCFG1_DIS_MST_ENH BIT(1) + #define DWC3_DEPCMD_TYPE_CONTROL 0 #define DWC3_DEPCMD_TYPE_ISOC 1 #define DWC3_DEPCMD_TYPE_BULK 2 @@ -602,16 +640,26 @@ struct dwc3_trb; /** * struct dwc3_event_buffer - Software event buffer representation * @buf: _THE_ buffer + * @cache: The buffer cache used in the threaded interrupt * @length: size of this buffer * @lpos: event offset + * @count: cache of last read event count register + * @flags: flags related to this event buffer * @dma: dma_addr_t * @dwc: pointer to DWC controller */ struct dwc3_event_buffer { void *buf; - unsigned length; + void *cache; + unsigned int length; unsigned int lpos; + unsigned int count; + unsigned int flags; + +#define DWC3_EVENT_PENDING BIT(0) + dma_addr_t dma; + struct dwc3 *dwc; }; @@ -622,57 +670,66 @@ struct dwc3_event_buffer { #define DWC3_EP_DIRECTION_RX false #define DWC3_TRB_NUM 256 -#define DWC3_TRB_MASK (DWC3_TRB_NUM - 1) /** * struct dwc3_ep - device side endpoint representation * @endpoint: usb endpoint - * @pending_list: list of requests for this endpoint + * @cancelled_list: list of cancelled requests for this endpoint + * @pending_list: list of pending requests for this endpoint * @started_list: list of started requests on this endpoint + * @regs: pointer to first endpoint register * @trb_pool: array of transaction buffers * @trb_pool_dma: dma address of @trb_pool - * @free_slot: next slot which is going to be used - * @busy_slot: first slot which is owned by HW - * @desc: usb_endpoint_descriptor pointer + * @trb_enqueue: enqueue 'pointer' into TRB array + * @trb_dequeue: dequeue 'pointer' into TRB array * @dwc: pointer to DWC controller * @saved_state: ep state saved during hibernation * @flags: endpoint flags (wedged, stalled, ...) - * @current_trb: index of current used trb * @number: endpoint number (1 - 15) * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK * @resource_index: Resource transfer index + * @frame_number: set to the frame number we want this transfer to start (ISOC) * @interval: the interval on which the ISOC transfer is started * @name: a human readable name e.g. ep1out-bulk * @direction: true for TX, false for RX * @stream_capable: true when streams are enabled + * @combo_num: the test combination BIT[15:14] of the frame number to test + * isochronous START TRANSFER command failure workaround + * @start_cmd_status: the status of testing START TRANSFER command with + * combo_num = 'b00 */ struct dwc3_ep { - struct usb_ep endpoint; - struct list_head cancelled_list; - struct list_head pending_list; - struct list_head started_list; - - void __iomem *regs; - - struct dwc3_trb *trb_pool; - dma_addr_t trb_pool_dma; - u32 free_slot; - u32 busy_slot; - const struct usb_ss_ep_comp_descriptor *comp_desc; - struct dwc3 *dwc; - - u32 saved_state; - unsigned flags; + struct usb_ep endpoint; + struct list_head cancelled_list; + struct list_head pending_list; + struct list_head started_list; + + void __iomem *regs; + + struct dwc3_trb *trb_pool; + dma_addr_t trb_pool_dma; + struct dwc3 *dwc; + + u32 saved_state; + unsigned int flags; #define DWC3_EP_ENABLED BIT(0) -#define DWC3_EP_STALL BIT(1) -#define DWC3_EP_WEDGE BIT(2) -#define DWC3_EP_TRANSFER_STARTED BIT(3) -#define DWC3_EP_PENDING_REQUEST BIT(4) +#define DWC3_EP_STALL BIT(1) +#define DWC3_EP_WEDGE BIT(2) +#define DWC3_EP_TRANSFER_STARTED BIT(3) +#define DWC3_EP_END_TRANSFER_PENDING BIT(4) +#define DWC3_EP_PENDING_REQUEST BIT(5) +#define DWC3_EP_DELAY_START BIT(6) +#define DWC3_EP_WAIT_TRANSFER_COMPLETE BIT(7) +#define DWC3_EP_IGNORE_NEXT_NOSTREAM BIT(8) +#define DWC3_EP_FORCE_RESTART_STREAM BIT(9) +#define DWC3_EP_FIRST_STREAM_PRIMED BIT(10) +#define DWC3_EP_PENDING_CLEAR_STALL BIT(11) +#define DWC3_EP_TXFIFO_RESIZED BIT(12) +#define DWC3_EP_DELAY_STOP BIT(13) /* This last one is specific to EP0 */ #define DWC3_EP0_DIR_IN BIT(31) - unsigned current_trb; /* * IMPORTANT: we *know* we have 256 TRBs in our @trb_pool, so we will * use a u8 type here. If anybody decides to increase number of TRBs to @@ -682,23 +739,23 @@ struct dwc3_ep { * By using u8 types we ensure that our % operator when incrementing * enqueue and dequeue get optimized away by the compiler. */ - u8 trb_enqueue; - u8 trb_dequeue; + u8 trb_enqueue; + u8 trb_dequeue; - u8 number; - u8 type; - u8 resource_index; - u32 frame_number; - u32 interval; + u8 number; + u8 type; + u8 resource_index; + u32 frame_number; + u32 interval; - char name[20]; + char name[20]; - unsigned direction:1; - unsigned stream_capable:1; + unsigned direction:1; + unsigned stream_capable:1; /* For isochronous START TRANSFER workaround only */ - u8 combo_num; - int start_cmd_status; + u8 combo_num; + int start_cmd_status; }; enum dwc3_phy { @@ -797,6 +854,7 @@ struct dwc3_trb { * @hwparams6: GHWPARAMS6 * @hwparams7: GHWPARAMS7 * @hwparams8: GHWPARAMS8 + * @hwparams9: GHWPARAMS9 */ struct dwc3_hwparams { u32 hwparams0; @@ -808,13 +866,12 @@ struct dwc3_hwparams { u32 hwparams6; u32 hwparams7; u32 hwparams8; + u32 hwparams9; }; /* HWPARAMS0 */ #define DWC3_MODE(n) ((n) & 0x7) -#define DWC3_MDWIDTH(n) (((n) & 0xff00) >> 8) - /* HWPARAMS1 */ #define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15) @@ -829,31 +886,67 @@ struct dwc3_hwparams { /* HWPARAMS7 */ #define DWC3_RAM1_DEPTH(n) ((n) & 0xffff) -struct dwc3_request { - struct usb_request request; - struct list_head list; - struct dwc3_ep *dep; - u32 start_slot; - - unsigned remaining; +/* HWPARAMS9 */ +#define DWC3_MST_CAPABLE(p) (!!((p)->hwparams9 & \ + DWC3_GHWPARAMS9_DEV_MST)) - unsigned int status; -#define DWC3_REQUEST_STATUS_QUEUED 0 -#define DWC3_REQUEST_STATUS_STARTED 1 -#define DWC3_REQUEST_STATUS_CANCELLED 2 -#define DWC3_REQUEST_STATUS_COMPLETED 3 -#define DWC3_REQUEST_STATUS_UNKNOWN -1 - - u8 epnum; +/** + * struct dwc3_request - representation of a transfer request + * @request: struct usb_request to be transferred + * @list: a list_head used for request queueing + * @dep: struct dwc3_ep owning this request + * @sg: pointer to first incomplete sg + * @start_sg: pointer to the sg which should be queued next + * @num_pending_sgs: counter to pending sgs + * @num_queued_sgs: counter to the number of sgs which already got queued + * @remaining: amount of data remaining + * @status: internal dwc3 request status tracking + * @epnum: endpoint number to which this request refers + * @trb: pointer to struct dwc3_trb + * @trb_dma: DMA address of @trb + * @num_trbs: number of TRBs used by this request + * @needs_extra_trb: true when request needs one extra TRB (either due to ZLP + * or unaligned OUT) + * @direction: IN or OUT direction flag + * @mapped: true when request has been dma-mapped + */ +struct dwc3_request { + struct usb_request request; + struct list_head list; + struct dwc3_ep *dep; + struct scatterlist *sg; + struct scatterlist *start_sg; + + unsigned int num_pending_sgs; + unsigned int num_queued_sgs; + unsigned int remaining; + + unsigned int status; +#define DWC3_REQUEST_STATUS_QUEUED 0 +#define DWC3_REQUEST_STATUS_STARTED 1 +#define DWC3_REQUEST_STATUS_DISCONNECTED 2 +#define DWC3_REQUEST_STATUS_DEQUEUED 3 +#define DWC3_REQUEST_STATUS_STALLED 4 +#define DWC3_REQUEST_STATUS_COMPLETED 5 +#define DWC3_REQUEST_STATUS_UNKNOWN -1 + + u8 epnum; struct dwc3_trb *trb; - dma_addr_t trb_dma; + dma_addr_t trb_dma; - unsigned num_trbs; + unsigned int num_trbs; + + unsigned int needs_extra_trb:1; + unsigned int direction:1; + unsigned int mapped:1; +}; - unsigned needs_extra_trb:1; - unsigned direction:1; - unsigned mapped:1; - unsigned queued:1; +/* + * struct dwc3_scratchpad_array - hibernation scratchpad array + * (format defined by hw) + */ +struct dwc3_scratchpad_array { + __le64 dma_adr[DWC3_MAX_HIBER_SCRATCHBUFS]; }; /** @@ -869,6 +962,7 @@ struct dwc3_request { * @scratch_addr: dma address of scratchbuf * @ep0_in_setup: one control transfer is completed and enter setup phase * @lock: for synchronizing + * @mutex: for mode switching * @dev: pointer to our struct device * @sysdev: pointer to the DMA-capable device * @xhci: pointer to our xHCI child @@ -877,12 +971,14 @@ struct dwc3_request { * @eps: endpoint array * @gadget: device side representation of the peripheral controller * @gadget_driver: pointer to the gadget driver - * @clks: array of clocks - * @num_clks: number of clocks + * @bus_clk: clock for accessing the registers + * @ref_clk: reference clock + * @susp_clk: clock used when the SS phy is in low power (S3) state * @reset: reset control * @regs: base address for our registers * @regs_size: address space size * @fladj: frame length adjustment + * @ref_clk_per: reference clock period configuration * @irq_gadget: peripheral controller's IRQ number * @otg_irq: IRQ number for OTG IRQs * @current_otg_role: current role of operation while using the OTG block @@ -891,7 +987,12 @@ struct dwc3_request { * @nr_scratch: number of scratch buffers * @u1u2: only used on revisions <1.83a for workaround * @maximum_speed: maximum speed requested (mainly for testing purposes) - * @revision: revision register contents + * @max_ssp_rate: SuperSpeed Plus maximum signaling rate and lane count + * @gadget_max_speed: maximum gadget speed requested + * @gadget_ssp_rate: Gadget driver's maximum supported SuperSpeed Plus signaling + * rate and lane count. + * @ip: controller's ID + * @revision: controller's version of an IP * @version_type: VERSIONTYPE register contents, a sub release of a revision * @dr_mode: requested mode of operation * @current_dr_role: current role of operation when in dual-role mode @@ -901,6 +1002,10 @@ struct dwc3_request { * @hsphy_mode: UTMI phy mode, one of following: * - USBPHY_INTERFACE_MODE_UTMI * - USBPHY_INTERFACE_MODE_UTMIW + * @role_sw: usb_role_switch handle + * @role_switch_default_mode: default operation mode of controller while + * usb role is USB_ROLE_NONE. + * @usb_psy: pointer to power supply interface. * @usb2_phy: pointer to USB2 PHY * @usb3_phy: pointer to USB3 PHY * @usb2_generic_phy: pointer to USB2 PHY @@ -918,7 +1023,6 @@ struct dwc3_request { * @link_state: link state * @speed: device speed (super, high, full, low) * @hwparams: copy of hwparams registers - * @root: debugfs root folder pointer * @regset: debugfs pointer to regdump file * @dbg_lsp_select: current debug lsp mux register selection * @test_mode: true when we're entering a USB test mode @@ -929,8 +1033,11 @@ struct dwc3_request { * @rx_max_burst_prd: max periodic ESS receive burst size * @tx_thr_num_pkt_prd: periodic ESS transmit packet count * @tx_max_burst_prd: max periodic ESS transmit burst size + * @tx_fifo_resize_max_num: max number of fifos allocated during txfifo resize + * @clear_stall_protocol: endpoint number that requires a delayed status phase * @hsphy_interface: "utmi" or "ulpi" * @connected: true when we're connected to a host, false otherwise + * @softconnect: true when gadget connect is called, false when disconnect runs * @delayed_status: true when gadget driver asks for delayed status * @ep0_bounced: true when we used bounce buffer * @ep0_expect_in: true when we expect a DATA IN transfer @@ -939,17 +1046,19 @@ struct dwc3_request { * @has_lpm_erratum: true when core was configured with LPM Erratum. Note that * there's now way for software to detect this in runtime. * @is_utmi_l1_suspend: the core asserts output signal - * 0 - utmi_sleep_n - * 1 - utmi_l1_suspend_n + * 0 - utmi_sleep_n + * 1 - utmi_l1_suspend_n * @is_fpga: true when we are using the FPGA board * @pending_events: true when we have pending IRQs to be handled + * @do_fifo_resize: true when txfifo resizing is enabled for dwc3 endpoints * @pullups_connected: true when Run/Stop bit is set * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround * @three_stage_setup: set if we perform a three phase setup * @dis_start_transfer_quirk: set if start_transfer failure SW workaround is * not needed for DWC_usb31 version 1.70a-ea06 and below * @usb3_lpm_capable: set if hadrware supports Link Power Management - * @usb2_lpm_disable: set to disable usb2 lpm + * @usb2_lpm_disable: set to disable usb2 lpm for host + * @usb2_gadget_lpm_disable: set to disable usb2 lpm for gadget * @disable_scramble_quirk: set if we enable the disable scramble quirk * @u2exit_lfps_quirk: set if we enable u2exit lfps quirk * @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk @@ -962,7 +1071,11 @@ struct dwc3_request { * @dis_u2_susphy_quirk: set if we disable usb2 suspend phy * @dis_enblslpm_quirk: set if we clear enblslpm in GUSB2PHYCFG, * disabling the suspend signal to the PHY. + * @dis_u1_entry_quirk: set if link entering into U1 state needs to be disabled. + * @dis_u2_entry_quirk: set if link entering into U2 state needs to be disabled. * @dis_rxdet_inp3_quirk: set if we disable Rx.Detect in P3 + * @async_callbacks: if set, indicate that async callbacks will be used. + * * @dis_u2_freeclk_exists_quirk : set if we clear u2_freeclk_exists * in GUSB2PHYCFG, specify that USB2 PHY doesn't * provide a free-running PHY clock. @@ -970,53 +1083,89 @@ struct dwc3_request { * change quirk. * @dis_tx_ipgap_linecheck_quirk: set if we disable u2mac linestate * check during HS transmit. + * @resume-hs-terminations: Set if we enable quirk for fixing improper crc + * generation after resume from suspend. + * @parkmode_disable_ss_quirk: set if we need to disable all SuperSpeed + * instances in park mode. * @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk * @tx_de_emphasis: Tx de-emphasis value - * 0 - -6dB de-emphasis - * 1 - -3.5dB de-emphasis - * 2 - No de-emphasis - * 3 - Reserved + * 0 - -6dB de-emphasis + * 1 - -3.5dB de-emphasis + * 2 - No de-emphasis + * 3 - Reserved * @dis_metastability_quirk: set to disable metastability quirk. + * @dis_split_quirk: set to disable split boundary. * @imod_interval: set the interrupt moderation interval in 250ns - * increments or 0 to disable. + * increments or 0 to disable. + * @max_cfg_eps: current max number of IN eps used across all USB configs. + * @last_fifo_depth: last fifo depth used to determine next fifo ram start + * address. + * @num_ep_resized: carries the current number endpoints which have had its tx + * fifo resized. + * @debug_root: root debugfs directory for this device to put its files in. */ struct dwc3 { + struct work_struct drd_work; struct dwc3_trb *ep0_trb; - void *bounce; - void *scratchbuf; - u8 *setup_buf; - dma_addr_t ep0_trb_addr; - dma_addr_t bounce_addr; - dma_addr_t scratch_addr; - struct dwc3_request ep0_usb_req; + void *bounce; + void *scratchbuf; + u8 *setup_buf; + dma_addr_t ep0_trb_addr; + dma_addr_t bounce_addr; + dma_addr_t scratch_addr; + struct dwc3_request ep0_usb_req; + struct completion ep0_in_setup; + + /* device lock */ + spinlock_t lock; - struct device_d *dev; + /* mode switching lock */ + struct mutex mutex; - struct device_d *xhci; + struct device *dev; + struct device *sysdev; - struct dwc3_event_buffer *ev_buf; - struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM]; + struct device *xhci; + struct resource xhci_resources[DWC3_XHCI_RESOURCES_NUM]; - struct usb_gadget gadget; - struct usb_gadget_driver *gadget_driver; + struct dwc3_event_buffer *ev_buf; + struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM]; + + struct usb_gadget *gadget; + struct usb_gadget_driver *gadget_driver; struct clk_bulk_data *clks; int num_clks; + struct reset_control *reset; + + struct usb_phy *usb2_phy; + struct usb_phy *usb3_phy; + struct phy *usb2_generic_phy; struct phy *usb3_generic_phy; bool phys_ready; + + struct ulpi *ulpi; bool ulpi_ready; void __iomem *regs; + size_t regs_size; enum usb_dr_mode dr_mode; u32 current_dr_role; u32 desired_dr_role; + struct extcon_dev *edev; + struct notifier_block edev_nb; enum usb_phy_interface hsphy_mode; + struct usb_role_switch *role_sw; + enum usb_dr_mode role_switch_default_mode; + + struct power_supply *usb_psy; u32 fladj; + u32 ref_clk_per; u32 irq_gadget; u32 otg_irq; u32 current_otg_role; @@ -1025,16 +1174,19 @@ struct dwc3 { u32 nr_scratch; u32 u1u2; u32 maximum_speed; + u32 gadget_max_speed; + enum usb_ssp_rate max_ssp_rate; + enum usb_ssp_rate gadget_ssp_rate; + + u32 ip; + +#define DWC3_IP 0x5533 +#define DWC31_IP 0x3331 +#define DWC32_IP 0x3332 - /* - * All 3.1 IP version constants are greater than the 3.0 IP - * version constants. This works for most version checks in - * dwc3. However, in the future, this may not apply as - * features may be developed on newer versions of the 3.0 IP - * that are not in the 3.1 IP. - */ u32 revision; +#define DWC3_REVISION_ANY 0x0 #define DWC3_REVISION_173A 0x5533173a #define DWC3_REVISION_175A 0x5533175a #define DWC3_REVISION_180A 0x5533180a @@ -1059,18 +1211,20 @@ struct dwc3 { #define DWC3_REVISION_310A 0x5533310a #define DWC3_REVISION_330A 0x5533330a -/* - * NOTICE: we're using bit 31 as a "is usb 3.1" flag. This is really - * just so dwc31 revisions are always larger than dwc3. - */ -#define DWC3_REVISION_IS_DWC31 0x80000000 -#define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_DWC31) -#define DWC3_USB31_REVISION_120A (0x3132302a | DWC3_REVISION_IS_DWC31) -#define DWC3_USB31_REVISION_160A (0x3136302a | DWC3_REVISION_IS_DWC31) -#define DWC3_USB31_REVISION_170A (0x3137302a | DWC3_REVISION_IS_DWC31) +#define DWC31_REVISION_ANY 0x0 +#define DWC31_REVISION_110A 0x3131302a +#define DWC31_REVISION_120A 0x3132302a +#define DWC31_REVISION_160A 0x3136302a +#define DWC31_REVISION_170A 0x3137302a +#define DWC31_REVISION_180A 0x3138302a +#define DWC31_REVISION_190A 0x3139302a + +#define DWC32_REVISION_ANY 0x0 +#define DWC32_REVISION_100A 0x3130302a u32 version_type; +#define DWC31_VERSIONTYPE_ANY 0x0 #define DWC31_VERSIONTYPE_EA01 0x65613031 #define DWC31_VERSIONTYPE_EA02 0x65613032 #define DWC31_VERSIONTYPE_EA03 0x65613033 @@ -1082,7 +1236,6 @@ struct dwc3 { enum dwc3_ep0_state ep0state; enum dwc3_link_state link_state; - u16 isoch_delay; u16 u2sel; u16 u2pel; u8 u1sel; @@ -1093,6 +1246,7 @@ struct dwc3 { u8 num_eps; struct dwc3_hwparams hwparams; + struct debugfs_regset32 *regset; u32 dbg_lsp_select; @@ -1104,10 +1258,13 @@ struct dwc3 { u8 rx_max_burst_prd; u8 tx_thr_num_pkt_prd; u8 tx_max_burst_prd; + u8 tx_fifo_resize_max_num; + u8 clear_stall_protocol; const char *hsphy_interface; unsigned connected:1; + unsigned softconnect:1; unsigned delayed_status:1; unsigned ep0_bounced:1; unsigned ep0_expect_in:1; @@ -1115,18 +1272,16 @@ struct dwc3 { unsigned sysdev_is_parent:1; unsigned has_lpm_erratum:1; unsigned is_utmi_l1_suspend:1; - unsigned is_selfpowered:1; unsigned is_fpga:1; unsigned pending_events:1; - unsigned needs_fifo_resize:1; + unsigned do_fifo_resize:1; unsigned pullups_connected:1; - unsigned resize_fifos:1; unsigned setup_packet_pending:1; - unsigned start_config_issued:1; unsigned three_stage_setup:1; unsigned dis_start_transfer_quirk:1; unsigned usb3_lpm_capable:1; unsigned usb2_lpm_disable:1; + unsigned usb2_gadget_lpm_disable:1; unsigned disable_scramble_quirk:1; unsigned u2exit_lfps_quirk:1; @@ -1139,17 +1294,30 @@ struct dwc3 { unsigned dis_u3_susphy_quirk:1; unsigned dis_u2_susphy_quirk:1; unsigned dis_enblslpm_quirk:1; + unsigned dis_u1_entry_quirk:1; + unsigned dis_u2_entry_quirk:1; unsigned dis_rxdet_inp3_quirk:1; unsigned dis_u2_freeclk_exists_quirk:1; unsigned dis_del_phy_power_chg_quirk:1; unsigned dis_tx_ipgap_linecheck_quirk:1; + unsigned resume_hs_terminations:1; + unsigned parkmode_disable_ss_quirk:1; + unsigned gfladj_refclk_lpm_sel:1; unsigned tx_de_emphasis_quirk:1; unsigned tx_de_emphasis:2; unsigned dis_metastability_quirk:1; + unsigned dis_split_quirk:1; + unsigned async_callbacks:1; + u16 imod_interval; + + int max_cfg_eps; + int last_fifo_depth; + int num_ep_resized; + struct dentry *debug_root; }; #define INCRX_BURST_MODE 0 @@ -1173,31 +1341,7 @@ struct dwc3_event_type { #define DWC3_DEPEVT_EPCMDCMPLT 0x07 /** - * dwc3_ep_event_string - returns event name - * @event: then event code - */ -static inline const char *dwc3_ep_event_string(u8 event) -{ - switch (event) { - case DWC3_DEPEVT_XFERCOMPLETE: - return "Transfer Complete"; - case DWC3_DEPEVT_XFERINPROGRESS: - return "Transfer In-Progress"; - case DWC3_DEPEVT_XFERNOTREADY: - return "Transfer Not Ready"; - case DWC3_DEPEVT_RXTXFIFOEVT: - return "FIFO"; - case DWC3_DEPEVT_STREAMEVT: - return "Stream"; - case DWC3_DEPEVT_EPCMDCMPLT: - return "Endpoint Command Complete"; - } - - return "UNKNOWN"; -} - -/** - * struct dwc3_event_depvt - Device Endpoint Events + * struct dwc3_event_depevt - Device Endpoint Events * @one_bit: indicates this is an endpoint event (not used) * @endpoint_number: number of the endpoint * @endpoint_event: The event we have: @@ -1236,6 +1380,10 @@ struct dwc3_event_depevt { #define DEPEVT_STREAMEVT_FOUND 1 #define DEPEVT_STREAMEVT_NOTFOUND 2 +/* Stream event parameter */ +#define DEPEVT_STREAM_PRIME 0xfffe +#define DEPEVT_STREAM_NOSTREAM 0x0 + /* Control-only Status */ #define DEPEVT_STATUS_CONTROL_DATA 1 #define DEPEVT_STATUS_CONTROL_STATUS 2 @@ -1262,7 +1410,7 @@ struct dwc3_event_depevt { * 3 - ULStChng * 4 - WkUpEvt * 5 - Reserved - * 6 - EOPF + * 6 - Suspend (EOPF on revisions 2.10a and prior) * 7 - SOF * 8 - Reserved * 9 - ErrticErr @@ -1334,29 +1482,63 @@ struct dwc3_gadget_ep_cmd_params { #define DWC3_HAS_OTG BIT(3) /* prototypes */ -int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc); void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode); void dwc3_set_mode(struct dwc3 *dwc, u32 mode); +u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type); -/* check whether we are on the DWC_usb3 core */ -static inline bool dwc3_is_usb3(struct dwc3 *dwc) -{ - return !(dwc->revision & DWC3_REVISION_IS_DWC31); -} +#define DWC3_IP_IS(_ip) \ + (dwc->ip == _ip##_IP) + +#define DWC3_VER_IS(_ip, _ver) \ + (DWC3_IP_IS(_ip) && dwc->revision == _ip##_REVISION_##_ver) + +#define DWC3_VER_IS_PRIOR(_ip, _ver) \ + (DWC3_IP_IS(_ip) && dwc->revision < _ip##_REVISION_##_ver) -/* check whether we are on the DWC_usb31 core */ -static inline bool dwc3_is_usb31(struct dwc3 *dwc) +#define DWC3_VER_IS_WITHIN(_ip, _from, _to) \ + (DWC3_IP_IS(_ip) && \ + (int)dwc->revision >= _ip##_REVISION_##_from && \ + (!(_ip##_REVISION_##_to) || \ + dwc->revision <= _ip##_REVISION_##_to)) + +#define DWC3_VER_TYPE_IS_WITHIN(_ip, _ver, _from, _to) \ + (DWC3_VER_IS(_ip, _ver) && \ + dwc->version_type >= _ip##_VERSIONTYPE_##_from && \ + (!(_ip##_VERSIONTYPE_##_to) || \ + dwc->version_type <= _ip##_VERSIONTYPE_##_to)) + +/** + * dwc3_mdwidth - get MDWIDTH value in bits + * @dwc: pointer to our context structure + * + * Return MDWIDTH configuration value in bits. + */ +static inline u32 dwc3_mdwidth(struct dwc3 *dwc) { - return !!(dwc->revision & DWC3_REVISION_IS_DWC31); + u32 mdwidth; + + mdwidth = DWC3_GHWPARAMS0_MDWIDTH(dwc->hwparams.hwparams0); + if (DWC3_IP_IS(DWC32)) + mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6); + + return mdwidth; } bool dwc3_has_imod(struct dwc3 *dwc); +int dwc3_event_buffers_setup(struct dwc3 *dwc); +void dwc3_event_buffers_cleanup(struct dwc3 *dwc); + +int dwc3_core_soft_reset(struct dwc3 *dwc); + #if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) int dwc3_host_init(struct dwc3 *dwc); +void dwc3_host_exit(struct dwc3 *dwc); #else static inline int dwc3_host_init(struct dwc3 *dwc) { return 0; } +static inline void dwc3_host_exit(struct dwc3 *dwc) +{ } #endif #if IS_ENABLED(CONFIG_USB_DWC3_GADGET) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) @@ -1365,9 +1547,12 @@ void dwc3_gadget_exit(struct dwc3 *dwc); int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode); int dwc3_gadget_get_link_state(struct dwc3 *dwc); int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state); -int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, +int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd, struct dwc3_gadget_ep_cmd_params *params); -int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param); +int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd, + u32 param); +void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc); +void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep, int status); #else static inline int dwc3_gadget_init(struct dwc3 *dwc) { return 0; } @@ -1381,9 +1566,14 @@ static inline int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) { return 0; } +static inline int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd, + struct dwc3_gadget_ep_cmd_params *params) +{ return 0; } static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param) { return 0; } +static inline void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc) +{ } #endif #if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h index 4f75ab3505..8bb2c9e3b9 100644 --- a/drivers/usb/dwc3/debug.h +++ b/drivers/usb/dwc3/debug.h @@ -1,8 +1,8 @@ -// SPDX-License-Identifier: GPL-2.0 -/** +/* SPDX-License-Identifier: GPL-2.0 */ +/* * debug.h - DesignWare USB3 DRD Controller Debug Header * - * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com * * Authors: Felipe Balbi <balbi@ti.com>, * Sebastian Andrzej Siewior <bigeasy@linutronix.de> @@ -68,6 +68,8 @@ dwc3_gadget_generic_cmd_string(u8 cmd) return "All FIFO Flush"; case DWC3_DGCMD_SET_ENDPOINT_NRDY: return "Set Endpoint NRDY"; + case DWC3_DGCMD_SET_ENDPOINT_PRIME: + return "Set Endpoint Prime"; case DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK: return "Run SoC Bus Loopback Test"; default: @@ -112,7 +114,7 @@ dwc3_gadget_link_string(enum dwc3_link_state link_state) case DWC3_LINK_STATE_RESUME: return "Resume"; default: - return "UNKNOWN link state\n"; + return "UNKNOWN link state"; } } @@ -141,7 +143,7 @@ dwc3_gadget_hs_link_string(enum dwc3_link_state link_state) case DWC3_LINK_STATE_RESUME: return "Resume"; default: - return "UNKNOWN link state\n"; + return "UNKNOWN link state"; } } @@ -193,294 +195,54 @@ static inline const char *dwc3_ep0_state_string(enum dwc3_ep0_state state) * dwc3_gadget_event_string - returns event name * @event: the event code */ -static inline const char * -dwc3_gadget_event_string(char *str, const struct dwc3_event_devt *event) +static inline const char *dwc3_gadget_event_string(char *str, size_t size, + const struct dwc3_event_devt *event) { enum dwc3_link_state state = event->event_info & DWC3_LINK_STATE_MASK; switch (event->type) { case DWC3_DEVICE_EVENT_DISCONNECT: - sprintf(str, "Disconnect: [%s]", + snprintf(str, size, "Disconnect: [%s]", dwc3_gadget_link_string(state)); break; case DWC3_DEVICE_EVENT_RESET: - sprintf(str, "Reset [%s]", dwc3_gadget_link_string(state)); + snprintf(str, size, "Reset [%s]", + dwc3_gadget_link_string(state)); break; case DWC3_DEVICE_EVENT_CONNECT_DONE: - sprintf(str, "Connection Done [%s]", + snprintf(str, size, "Connection Done [%s]", dwc3_gadget_link_string(state)); break; case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: - sprintf(str, "Link Change [%s]", + snprintf(str, size, "Link Change [%s]", dwc3_gadget_link_string(state)); break; case DWC3_DEVICE_EVENT_WAKEUP: - sprintf(str, "WakeUp [%s]", dwc3_gadget_link_string(state)); + snprintf(str, size, "WakeUp [%s]", + dwc3_gadget_link_string(state)); break; - case DWC3_DEVICE_EVENT_EOPF: - sprintf(str, "End-Of-Frame [%s]", + case DWC3_DEVICE_EVENT_SUSPEND: + snprintf(str, size, "Suspend [%s]", dwc3_gadget_link_string(state)); break; case DWC3_DEVICE_EVENT_SOF: - sprintf(str, "Start-Of-Frame [%s]", + snprintf(str, size, "Start-Of-Frame [%s]", dwc3_gadget_link_string(state)); break; case DWC3_DEVICE_EVENT_ERRATIC_ERROR: - sprintf(str, "Erratic Error [%s]", + snprintf(str, size, "Erratic Error [%s]", dwc3_gadget_link_string(state)); break; case DWC3_DEVICE_EVENT_CMD_CMPL: - sprintf(str, "Command Complete [%s]", + snprintf(str, size, "Command Complete [%s]", dwc3_gadget_link_string(state)); break; case DWC3_DEVICE_EVENT_OVERFLOW: - sprintf(str, "Overflow [%s]", dwc3_gadget_link_string(state)); - break; - default: - sprintf(str, "UNKNOWN"); - } - - return str; -} - -static inline void dwc3_decode_get_status(__u8 t, __u16 i, __u16 l, char *str) -{ - switch (t & USB_RECIP_MASK) { - case USB_RECIP_INTERFACE: - sprintf(str, "Get Interface Status(Intf = %d, Length = %d)", - i, l); - break; - case USB_RECIP_ENDPOINT: - sprintf(str, "Get Endpoint Status(ep%d%s)", - i & ~USB_DIR_IN, - i & USB_DIR_IN ? "in" : "out"); - break; - } -} - -static inline void dwc3_decode_set_clear_feature(__u8 t, __u8 b, __u16 v, - __u16 i, char *str) -{ - switch (t & USB_RECIP_MASK) { - case USB_RECIP_DEVICE: - sprintf(str, "%s Device Feature(%s%s)", - b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set", - ({char *s; - switch (v) { - case USB_DEVICE_SELF_POWERED: - s = "Self Powered"; - break; - case USB_DEVICE_REMOTE_WAKEUP: - s = "Remote Wakeup"; - break; - case USB_DEVICE_TEST_MODE: - s = "Test Mode"; - break; - case USB_DEVICE_U1_ENABLE: - s = "U1 Enable"; - break; - case USB_DEVICE_U2_ENABLE: - s = "U2 Enable"; - break; - case USB_DEVICE_LTM_ENABLE: - s = "LTM Enable"; - break; - default: - s = "UNKNOWN"; - } s; }), - v == USB_DEVICE_TEST_MODE ? - ({ char *s; - switch (i) { - case TEST_J: - s = ": TEST_J"; - break; - case TEST_K: - s = ": TEST_K"; - break; - case TEST_SE0_NAK: - s = ": TEST_SE0_NAK"; - break; - case TEST_PACKET: - s = ": TEST_PACKET"; - break; - case TEST_FORCE_EN: - s = ": TEST_FORCE_EN"; - break; - default: - s = ": UNKNOWN"; - } s; }) : ""); - break; - case USB_RECIP_INTERFACE: - sprintf(str, "%s Interface Feature(%s)", - b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set", - v == USB_INTRF_FUNC_SUSPEND ? - "Function Suspend" : "UNKNOWN"); - break; - case USB_RECIP_ENDPOINT: - sprintf(str, "%s Endpoint Feature(%s ep%d%s)", - b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set", - v == USB_ENDPOINT_HALT ? "Halt" : "UNKNOWN", - i & ~USB_DIR_IN, - i & USB_DIR_IN ? "in" : "out"); - break; - } -} - -static inline void dwc3_decode_set_address(__u16 v, char *str) -{ - sprintf(str, "Set Address(Addr = %02x)", v); -} - -static inline void dwc3_decode_get_set_descriptor(__u8 t, __u8 b, __u16 v, - __u16 i, __u16 l, char *str) -{ - sprintf(str, "%s %s Descriptor(Index = %d, Length = %d)", - b == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set", - ({ char *s; - switch (v >> 8) { - case USB_DT_DEVICE: - s = "Device"; - break; - case USB_DT_CONFIG: - s = "Configuration"; - break; - case USB_DT_STRING: - s = "String"; - break; - case USB_DT_INTERFACE: - s = "Interface"; - break; - case USB_DT_ENDPOINT: - s = "Endpoint"; - break; - case USB_DT_DEVICE_QUALIFIER: - s = "Device Qualifier"; - break; - case USB_DT_OTHER_SPEED_CONFIG: - s = "Other Speed Config"; - break; - case USB_DT_INTERFACE_POWER: - s = "Interface Power"; - break; - case USB_DT_OTG: - s = "OTG"; - break; - case USB_DT_DEBUG: - s = "Debug"; - break; - case USB_DT_INTERFACE_ASSOCIATION: - s = "Interface Association"; - break; - case USB_DT_BOS: - s = "BOS"; - break; - case USB_DT_DEVICE_CAPABILITY: - s = "Device Capability"; - break; - case USB_DT_PIPE_USAGE: - s = "Pipe Usage"; - break; - case USB_DT_SS_ENDPOINT_COMP: - s = "SS Endpoint Companion"; - break; - case USB_DT_SSP_ISOC_ENDPOINT_COMP: - s = "SSP Isochronous Endpoint Companion"; - break; - default: - s = "UNKNOWN"; - break; - } s; }), v & 0xff, l); -} - - -static inline void dwc3_decode_get_configuration(__u16 l, char *str) -{ - sprintf(str, "Get Configuration(Length = %d)", l); -} - -static inline void dwc3_decode_set_configuration(__u8 v, char *str) -{ - sprintf(str, "Set Configuration(Config = %d)", v); -} - -static inline void dwc3_decode_get_intf(__u16 i, __u16 l, char *str) -{ - sprintf(str, "Get Interface(Intf = %d, Length = %d)", i, l); -} - -static inline void dwc3_decode_set_intf(__u8 v, __u16 i, char *str) -{ - sprintf(str, "Set Interface(Intf = %d, Alt.Setting = %d)", i, v); -} - -static inline void dwc3_decode_synch_frame(__u16 i, __u16 l, char *str) -{ - sprintf(str, "Synch Frame(Endpoint = %d, Length = %d)", i, l); -} - -static inline void dwc3_decode_set_sel(__u16 l, char *str) -{ - sprintf(str, "Set SEL(Length = %d)", l); -} - -static inline void dwc3_decode_set_isoch_delay(__u8 v, char *str) -{ - sprintf(str, "Set Isochronous Delay(Delay = %d ns)", v); -} - -/** - * dwc3_decode_ctrl - returns a string represetion of ctrl request - */ -static inline const char *dwc3_decode_ctrl(char *str, __u8 bRequestType, - __u8 bRequest, __u16 wValue, __u16 wIndex, __u16 wLength) -{ - switch (bRequest) { - case USB_REQ_GET_STATUS: - dwc3_decode_get_status(bRequestType, wIndex, wLength, str); - break; - case USB_REQ_CLEAR_FEATURE: - case USB_REQ_SET_FEATURE: - dwc3_decode_set_clear_feature(bRequestType, bRequest, wValue, - wIndex, str); - break; - case USB_REQ_SET_ADDRESS: - dwc3_decode_set_address(wValue, str); - break; - case USB_REQ_GET_DESCRIPTOR: - case USB_REQ_SET_DESCRIPTOR: - dwc3_decode_get_set_descriptor(bRequestType, bRequest, wValue, - wIndex, wLength, str); - break; - case USB_REQ_GET_CONFIGURATION: - dwc3_decode_get_configuration(wLength, str); - break; - case USB_REQ_SET_CONFIGURATION: - dwc3_decode_set_configuration(wValue, str); - break; - case USB_REQ_GET_INTERFACE: - dwc3_decode_get_intf(wIndex, wLength, str); - break; - case USB_REQ_SET_INTERFACE: - dwc3_decode_set_intf(wValue, wIndex, str); - break; - case USB_REQ_SYNCH_FRAME: - dwc3_decode_synch_frame(wIndex, wLength, str); - break; - case USB_REQ_SET_SEL: - dwc3_decode_set_sel(wLength, str); - break; - case USB_REQ_SET_ISOCH_DELAY: - dwc3_decode_set_isoch_delay(wValue, str); + snprintf(str, size, "Overflow [%s]", + dwc3_gadget_link_string(state)); break; default: - sprintf(str, "%02x %02x %02x %02x %02x %02x %02x %02x", - bRequestType, bRequest, - cpu_to_le16(wValue) & 0xff, - cpu_to_le16(wValue) >> 8, - cpu_to_le16(wIndex) & 0xff, - cpu_to_le16(wIndex) >> 8, - cpu_to_le16(wLength) & 0xff, - cpu_to_le16(wLength) >> 8); + snprintf(str, size, "UNKNOWN"); } return str; @@ -490,48 +252,41 @@ static inline const char *dwc3_decode_ctrl(char *str, __u8 bRequestType, * dwc3_ep_event_string - returns event name * @event: then event code */ -static inline const char * -dwc3_ep_event_string(char *str, const struct dwc3_event_depevt *event, - u32 ep0state) +static inline const char *dwc3_ep_event_string(char *str, size_t size, + const struct dwc3_event_depevt *event, u32 ep0state) { u8 epnum = event->endpoint_number; size_t len; int status; - int ret; - ret = sprintf(str, "ep%d%s: ", epnum >> 1, + len = scnprintf(str, size, "ep%d%s: ", epnum >> 1, (epnum & 1) ? "in" : "out"); - if (ret < 0) - return "UNKNOWN"; status = event->status; switch (event->endpoint_event) { case DWC3_DEPEVT_XFERCOMPLETE: - len = strlen(str); - sprintf(str + len, "Transfer Complete (%c%c%c)", + len += scnprintf(str + len, size - len, + "Transfer Complete (%c%c%c)", status & DEPEVT_STATUS_SHORT ? 'S' : 's', status & DEPEVT_STATUS_IOC ? 'I' : 'i', status & DEPEVT_STATUS_LST ? 'L' : 'l'); - len = strlen(str); - if (epnum <= 1) - sprintf(str + len, " [%s]", dwc3_ep0_state_string(ep0state)); + scnprintf(str + len, size - len, " [%s]", + dwc3_ep0_state_string(ep0state)); break; case DWC3_DEPEVT_XFERINPROGRESS: - len = strlen(str); - - sprintf(str + len, "Transfer In Progress [%d] (%c%c%c)", + scnprintf(str + len, size - len, + "Transfer In Progress [%08x] (%c%c%c)", event->parameters, status & DEPEVT_STATUS_SHORT ? 'S' : 's', status & DEPEVT_STATUS_IOC ? 'I' : 'i', status & DEPEVT_STATUS_LST ? 'M' : 'm'); break; case DWC3_DEPEVT_XFERNOTREADY: - len = strlen(str); - - sprintf(str + len, "Transfer Not Ready [%d]%s", + len += scnprintf(str + len, size - len, + "Transfer Not Ready [%08x]%s", event->parameters, status & DEPEVT_STATUS_TRANSFER_ACTIVE ? " (Active)" : " (Not Active)"); @@ -542,36 +297,38 @@ dwc3_ep_event_string(char *str, const struct dwc3_event_depevt *event, switch (phase) { case DEPEVT_STATUS_CONTROL_DATA: - strcat(str, " [Data Phase]"); + scnprintf(str + len, size - len, + " [Data Phase]"); break; case DEPEVT_STATUS_CONTROL_STATUS: - strcat(str, " [Status Phase]"); + scnprintf(str + len, size - len, + " [Status Phase]"); } } break; case DWC3_DEPEVT_RXTXFIFOEVT: - strcat(str, "FIFO"); + scnprintf(str + len, size - len, "FIFO"); break; case DWC3_DEPEVT_STREAMEVT: status = event->status; switch (status) { case DEPEVT_STREAMEVT_FOUND: - sprintf(str + ret, " Stream %d Found", + scnprintf(str + len, size - len, " Stream %d Found", event->parameters); break; case DEPEVT_STREAMEVT_NOTFOUND: default: - strcat(str, " Stream Not Found"); + scnprintf(str + len, size - len, " Stream Not Found"); break; } break; case DWC3_DEPEVT_EPCMDCMPLT: - strcat(str, "Endpoint Command Complete"); + scnprintf(str + len, size - len, "Endpoint Command Complete"); break; default: - sprintf(str, "UNKNOWN"); + scnprintf(str + len, size - len, "UNKNOWN"); } return str; @@ -596,8 +353,8 @@ static inline const char *dwc3_gadget_event_type_string(u8 event) return "Wake-Up"; case DWC3_DEVICE_EVENT_HIBER_REQ: return "Hibernation"; - case DWC3_DEVICE_EVENT_EOPF: - return "End of Periodic Frame"; + case DWC3_DEVICE_EVENT_SUSPEND: + return "Suspend"; case DWC3_DEVICE_EVENT_SOF: return "Start of Frame"; case DWC3_DEVICE_EVENT_ERRATIC_ERROR: @@ -611,14 +368,17 @@ static inline const char *dwc3_gadget_event_type_string(u8 event) } } -static inline const char *dwc3_decode_event(char *str, u32 event, u32 ep0state) +static inline const char *dwc3_decode_event(char *str, size_t size, u32 event, + u32 ep0state) { - const union dwc3_event evt = (union dwc3_event) event; + union dwc3_event evt; + + memcpy(&evt, &event, sizeof(event)); if (evt.type.is_devspec) - return dwc3_gadget_event_string(str, &evt.devt); + return dwc3_gadget_event_string(str, size, &evt.devt); else - return dwc3_ep_event_string(str, &evt.depevt, ep0state); + return dwc3_ep_event_string(str, size, &evt.depevt, ep0state); } static inline const char *dwc3_ep_cmd_status_string(int status) @@ -653,9 +413,15 @@ static inline const char *dwc3_gadget_generic_cmd_status_string(int status) #ifdef CONFIG_DEBUG_FS -extern void dwc3_debugfs_init(struct dwc3 *); -extern void dwc3_debugfs_exit(struct dwc3 *); +extern void dwc3_debugfs_create_endpoint_dir(struct dwc3_ep *dep); +extern void dwc3_debugfs_remove_endpoint_dir(struct dwc3_ep *dep); +extern void dwc3_debugfs_init(struct dwc3 *d); +extern void dwc3_debugfs_exit(struct dwc3 *d); #else +static inline void dwc3_debugfs_create_endpoint_dir(struct dwc3_ep *dep) +{ } +static inline void dwc3_debugfs_remove_endpoint_dir(struct dwc3_ep *dep) +{ } static inline void dwc3_debugfs_init(struct dwc3 *d) { } static inline void dwc3_debugfs_exit(struct dwc3 *d) diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c index e58d9f95fe..1e62224015 100644 --- a/drivers/usb/dwc3/dwc3-of-simple.c +++ b/drivers/usb/dwc3/dwc3-of-simple.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /** * dwc3-of-simple.c - OF glue layer for simple integrations * @@ -20,100 +20,58 @@ #include <of.h> struct dwc3_of_simple { - struct device_d *dev; - struct clk **clks; + struct device *dev; + struct clk_bulk_data *clks; int num_clocks; }; -static int dwc3_of_simple_clk_init(struct dwc3_of_simple *simple, int count) -{ - struct device_d *dev = simple->dev; - struct device_node *np = dev->device_node; - int i; - - simple->num_clocks = count; - - if (!count) - return 0; - - simple->clks = xzalloc(sizeof(struct clk *)); - if (!simple->clks) - return -ENOMEM; - - for (i = 0; i < simple->num_clocks; i++) { - struct clk *clk; - - clk = of_clk_get(np, i); - if (IS_ERR(clk)) { - while (--i >= 0) { - clk_disable(simple->clks[i]); - clk_put(simple->clks[i]); - } - return PTR_ERR(clk); - } - - simple->clks[i] = clk; - } - - return 0; -} - -static int dwc3_of_simple_probe(struct device_d *dev) +static int dwc3_of_simple_probe(struct device *dev) { struct dwc3_of_simple *simple; - struct device_node *np = dev->device_node; + struct device_node *np = dev->of_node; int ret; - int i; simple = xzalloc(sizeof(*simple)); if (!simple) return -ENOMEM; - dev->priv = simple; + dev->priv = simple; simple->dev = dev; - ret = dwc3_of_simple_clk_init(simple, of_count_phandle_with_args(np, - "clocks", "#clock-cells")); + ret = clk_bulk_get_all(simple->dev, &simple->clks); + if (ret < 0) + return ret; + + simple->num_clocks = ret; + ret = clk_bulk_enable(simple->num_clocks, simple->clks); if (ret) return ret; ret = of_platform_populate(np, NULL, dev); if (ret) { - for (i = 0; i < simple->num_clocks; i++) { - clk_disable(simple->clks[i]); - clk_put(simple->clks[i]); - } + clk_bulk_disable(simple->num_clocks, simple->clks); return ret; } - return 0; + return 0; } -static void dwc3_of_simple_remove(struct device_d *dev) +static void dwc3_of_simple_remove(struct device *dev) { struct dwc3_of_simple *simple = dev->priv; - int i; - for (i = 0; i < simple->num_clocks; i++) { - clk_disable(simple->clks[i]); - clk_put(simple->clks[i]); - } - simple->num_clocks = 0; + clk_bulk_disable(simple->num_clocks, simple->clks); } static const struct of_device_id of_dwc3_simple_match[] = { {.compatible = "rockchip,rk3399-dwc3"}, {.compatible = "xlnx,zynqmp-dwc3"}, - {.compatible = "fsl,ls1046a-dwc3"}, - {.compatible = "cavium,octeon-7130-usb-uctl"}, - {.compatible = "sprd,sc9860-dwc3"}, - {.compatible = "amlogic,meson-axg-dwc3"}, - {.compatible = "amlogic,meson-gxl-dwc3"}, - {.compatible = "allwinner,sun50i-h6-dwc3"}, + {.compatible = "fsl,imx8mp-dwc3"}, {/* Sentinel */}}; +MODULE_DEVICE_TABLE(of, of_dwc3_simple_match); -static struct driver_d dwc3_of_simple_driver = { +static struct driver dwc3_of_simple_driver = { .probe = dwc3_of_simple_probe, .remove = dwc3_of_simple_remove, .name = "dwc3-of-simple", diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index b757a57886..6285566b4b 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -1,26 +1,26 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * ep0.c - DesignWare USB3 DRD Controller Endpoint 0 Handling * - * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com * * Authors: Felipe Balbi <balbi@ti.com>, * Sebastian Andrzej Siewior <bigeasy@linutronix.de> - * - * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/ep0.c) and ported - * to uboot. - * - * commit c00552ebaf : Merge 3.18-rc7 into usb-next */ #include <common.h> #include <dma.h> #include <linux/kernel.h> #include <linux/list.h> +#include <linux/completion.h> -#include <usb/gadget.h> +#include <linux/usb/gadget.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/composite.h> #include "core.h" +#include "debug.h" #include "gadget.h" #include "io.h" @@ -29,11 +29,10 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, struct dwc3_ep *dep, struct dwc3_request *req); static void dwc3_ep0_prepare_one_trb(struct dwc3_ep *dep, - dma_addr_t buf_dma, u32 len, u32 type, - bool chain) + dma_addr_t buf_dma, u32 len, u32 type, bool chain) { - struct dwc3_trb *trb; - struct dwc3 *dwc; + struct dwc3_trb *trb; + struct dwc3 *dwc; dwc = dep->dwc; trb = &dwc->ep0_trb[dep->trb_enqueue]; @@ -47,67 +46,47 @@ static void dwc3_ep0_prepare_one_trb(struct dwc3_ep *dep, trb->ctrl = type; trb->ctrl |= (DWC3_TRB_CTRL_HWO - | DWC3_TRB_CTRL_ISP_IMI); + | DWC3_TRB_CTRL_ISP_IMI); if (chain) trb->ctrl |= DWC3_TRB_CTRL_CHN; else trb->ctrl |= (DWC3_TRB_CTRL_IOC - | DWC3_TRB_CTRL_LST); + | DWC3_TRB_CTRL_LST); } static int dwc3_ep0_start_trans(struct dwc3_ep *dep) { struct dwc3_gadget_ep_cmd_params params; - struct dwc3 *dwc; - int ret; - - dwc = dep->dwc; + struct dwc3 *dwc; + int ret; - if (dep->flags & DWC3_EP_TRANSFER_STARTED) { - dev_err(dwc->dev, "%s: transfer already started\n", dep->name); + if (dep->flags & DWC3_EP_TRANSFER_STARTED) return 0; - } + + dwc = dep->dwc; memset(¶ms, 0, sizeof(params)); params.param0 = upper_32_bits(dwc->ep0_trb_addr); params.param1 = lower_32_bits(dwc->ep0_trb_addr); ret = dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_STARTTRANSFER, ¶ms); - if (ret < 0) { - dev_err(dwc->dev, "%s: STARTTRANSFER failed\n", dep->name); + if (ret < 0) return ret; - } dwc->ep0_next_event = DWC3_EP0_COMPLETE; return 0; } -static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state) -{ - switch (state) { - case EP0_UNCONNECTED: - return "Unconnected"; - case EP0_SETUP_PHASE: - return "Setup Phase"; - case EP0_DATA_PHASE: - return "Data Phase"; - case EP0_STATUS_PHASE: - return "Status Phase"; - default: - return "UNKNOWN"; - } -} - static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, - struct dwc3_request *req) + struct dwc3_request *req) { - struct dwc3 *dwc = dep->dwc; + struct dwc3 *dwc = dep->dwc; - req->request.actual = 0; - req->request.status = -EINPROGRESS; - req->epnum = dep->number; + req->request.actual = 0; + req->request.status = -EINPROGRESS; + req->epnum = dep->number; list_add_tail(&req->list, &dep->pending_list); @@ -121,7 +100,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, * IRQ we were waiting for is long gone. */ if (dep->flags & DWC3_EP_PENDING_REQUEST) { - unsigned direction; + unsigned int direction; direction = !!(dep->flags & DWC3_EP0_DIR_IN); @@ -143,16 +122,14 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, * handle it here. */ if (dwc->delayed_status) { - unsigned direction; + unsigned int direction; direction = !dwc->ep0_expect_in; dwc->delayed_status = false; - usb_gadget_set_state(&dwc->gadget, USB_STATE_CONFIGURED); + usb_gadget_set_state(dwc->gadget, USB_STATE_CONFIGURED); if (dwc->ep0state == EP0_STATUS_PHASE) __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]); - else - dev_dbg(dwc->dev, "too early for delayed status\n"); return 0; } @@ -190,7 +167,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, * XferNotReady(STATUS). */ if (dwc->three_stage_setup) { - unsigned direction; + unsigned int direction; direction = dwc->ep0_expect_in; dwc->ep0state = EP0_DATA_PHASE; @@ -205,16 +182,18 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request) { - struct dwc3_request *req = to_dwc3_request(request); - struct dwc3_ep *dep = to_dwc3_ep(ep); - struct dwc3 *dwc = dep->dwc; - unsigned long flags; - int ret; + struct dwc3_request *req = to_dwc3_request(request); + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + + unsigned long flags; + + int ret; spin_lock_irqsave(&dwc->lock, flags); - if (!dep->endpoint.desc) { - dev_err(dwc->dev, "trying to queue request %p to disabled %s\n", - request, dep->name); + if (!dep->endpoint.desc || !dwc->pullups_connected || !dwc->connected) { + dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n", + dep->name); ret = -ESHUTDOWN; goto out; } @@ -225,10 +204,6 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request) goto out; } - dev_dbg(dwc->dev, "queueing request %p to %s length %d state '%s'\n", - request, dep->name, request->length, - dwc3_ep0_state_string(dwc->ep0state)); - ret = __dwc3_gadget_ep0_queue(dep, req); out: @@ -237,9 +212,9 @@ out: return ret; } -static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) +void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) { - struct dwc3_ep *dep; + struct dwc3_ep *dep; /* reinitialize physical ep1 */ dep = dwc->eps[1]; @@ -252,20 +227,22 @@ static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) dwc->delayed_status = false; if (!list_empty(&dep->pending_list)) { - struct dwc3_request *req; + struct dwc3_request *req; req = next_request(&dep->pending_list); dwc3_gadget_giveback(dep, req, -ECONNRESET); } + dwc->eps[0]->trb_enqueue = 0; + dwc->eps[1]->trb_enqueue = 0; dwc->ep0state = EP0_SETUP_PHASE; dwc3_ep0_out_start(dwc); } int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value) { - struct dwc3_ep *dep = to_dwc3_ep(ep); - struct dwc3 *dwc = dep->dwc; + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; dwc3_ep0_stall_and_restart(dwc); @@ -274,8 +251,8 @@ int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value) int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value) { - unsigned long flags; - int ret; + unsigned long flags; + int ret; spin_lock_irqsave(&dwc->lock, flags); ret = __dwc3_gadget_ep0_set_halt(ep, value); @@ -286,27 +263,49 @@ int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value) void dwc3_ep0_out_start(struct dwc3 *dwc) { - struct dwc3_ep *dep; - int ret; + struct dwc3_ep *dep; + int ret; + int i; + + complete(&dwc->ep0_in_setup); dep = dwc->eps[0]; dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 8, - DWC3_TRBCTL_CONTROL_SETUP, false); + DWC3_TRBCTL_CONTROL_SETUP, false); ret = dwc3_ep0_start_trans(dep); WARN_ON(ret < 0); + for (i = 2; i < DWC3_ENDPOINTS_NUM; i++) { + struct dwc3_ep *dwc3_ep; + + dwc3_ep = dwc->eps[i]; + if (!dwc3_ep) + continue; + + if (!(dwc3_ep->flags & DWC3_EP_DELAY_STOP)) + continue; + + dwc3_ep->flags &= ~DWC3_EP_DELAY_STOP; + if (dwc->connected) + dwc3_stop_active_transfer(dwc3_ep, true, true); + else + dwc3_remove_requests(dwc, dwc3_ep, -ESHUTDOWN); + } } static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le) { - struct dwc3_ep *dep; - u32 windex = le16_to_cpu(wIndex_le); - u32 epnum; + struct dwc3_ep *dep; + u32 windex = le16_to_cpu(wIndex_le); + u32 epnum; epnum = (windex & USB_ENDPOINT_NUMBER_MASK) << 1; if ((windex & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) epnum |= 1; dep = dwc->eps[epnum]; + if (dep == NULL) + return NULL; + if (dep->flags & DWC3_EP_ENABLED) return dep; @@ -320,14 +319,14 @@ static void dwc3_ep0_status_cmpl(struct usb_ep *ep, struct usb_request *req) * ch 9.4.5 */ static int dwc3_ep0_handle_status(struct dwc3 *dwc, - struct usb_ctrlrequest *ctrl) + struct usb_ctrlrequest *ctrl) { - struct dwc3_ep *dep; - u32 recip; - u32 value; - u32 reg; - u16 usb_status = 0; - __le16 *response_pkt; + struct dwc3_ep *dep; + u32 recip; + u32 value; + u32 reg; + u16 usb_status = 0; + __le16 *response_pkt; /* We don't support PTM_STATUS */ value = le16_to_cpu(ctrl->wValue); @@ -340,7 +339,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc, /* * LTM will be set once we know how to set this in HW. */ - usb_status |= dwc->is_selfpowered << USB_DEVICE_SELF_POWERED; + usb_status |= dwc->gadget->is_selfpowered; if ((dwc->speed == DWC3_DSTS_SUPERSPEED) || (dwc->speed == DWC3_DSTS_SUPERSPEED_PLUS)) { @@ -385,7 +384,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc, } static int dwc3_ep0_handle_u1(struct dwc3 *dwc, enum usb_device_state state, - int set) + int set) { u32 reg; @@ -394,6 +393,8 @@ static int dwc3_ep0_handle_u1(struct dwc3 *dwc, enum usb_device_state state, if ((dwc->speed != DWC3_DSTS_SUPERSPEED) && (dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS)) return -EINVAL; + if (set && dwc->dis_u1_entry_quirk) + return -EINVAL; reg = dwc3_readl(dwc->regs, DWC3_DCTL); if (set) @@ -406,15 +407,18 @@ static int dwc3_ep0_handle_u1(struct dwc3 *dwc, enum usb_device_state state, } static int dwc3_ep0_handle_u2(struct dwc3 *dwc, enum usb_device_state state, - int set) + int set) { u32 reg; + if (state != USB_STATE_CONFIGURED) return -EINVAL; if ((dwc->speed != DWC3_DSTS_SUPERSPEED) && (dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS)) return -EINVAL; + if (set && dwc->dis_u2_entry_quirk) + return -EINVAL; reg = dwc3_readl(dwc->regs, DWC3_DCTL); if (set) @@ -427,7 +431,7 @@ static int dwc3_ep0_handle_u2(struct dwc3 *dwc, enum usb_device_state state, } static int dwc3_ep0_handle_test(struct dwc3 *dwc, enum usb_device_state state, - u32 wIndex, int set) + u32 wIndex, int set) { if ((wIndex & 0xff) != 0) return -EINVAL; @@ -435,11 +439,11 @@ static int dwc3_ep0_handle_test(struct dwc3 *dwc, enum usb_device_state state, return -EINVAL; switch (wIndex >> 8) { - case TEST_J: - case TEST_K: - case TEST_SE0_NAK: - case TEST_PACKET: - case TEST_FORCE_EN: + case USB_TEST_J: + case USB_TEST_K: + case USB_TEST_SE0_NAK: + case USB_TEST_PACKET: + case USB_TEST_FORCE_ENABLE: dwc->test_mode_nr = wIndex >> 8; dwc->test_mode = true; break; @@ -451,22 +455,22 @@ static int dwc3_ep0_handle_test(struct dwc3 *dwc, enum usb_device_state state, } static int dwc3_ep0_handle_device(struct dwc3 *dwc, - struct usb_ctrlrequest *ctrl, int set) + struct usb_ctrlrequest *ctrl, int set) { - enum usb_device_state state; - u32 wValue; - u32 wIndex; - int ret = 0; + enum usb_device_state state; + u32 wValue; + u32 wIndex; + int ret = 0; wValue = le16_to_cpu(ctrl->wValue); wIndex = le16_to_cpu(ctrl->wIndex); - state = dwc->gadget.state; + state = dwc->gadget->state; switch (wValue) { case USB_DEVICE_REMOTE_WAKEUP: break; /* - * 9.4.1 says only only for SS, in AddressState only for + * 9.4.1 says only for SS, in AddressState only for * default control pipe */ case USB_DEVICE_U1_ENABLE: @@ -489,10 +493,10 @@ static int dwc3_ep0_handle_device(struct dwc3 *dwc, } static int dwc3_ep0_handle_intf(struct dwc3 *dwc, - struct usb_ctrlrequest *ctrl, int set) + struct usb_ctrlrequest *ctrl, int set) { - u32 wValue; - int ret = 0; + u32 wValue; + int ret = 0; wValue = le16_to_cpu(ctrl->wValue); @@ -514,11 +518,11 @@ static int dwc3_ep0_handle_intf(struct dwc3 *dwc, } static int dwc3_ep0_handle_endpoint(struct dwc3 *dwc, - struct usb_ctrlrequest *ctrl, int set) + struct usb_ctrlrequest *ctrl, int set) { - struct dwc3_ep *dep; - u32 wValue; - int ret; + struct dwc3_ep *dep; + u32 wValue; + int ret; wValue = le16_to_cpu(ctrl->wValue); @@ -534,6 +538,11 @@ static int dwc3_ep0_handle_endpoint(struct dwc3 *dwc, ret = __dwc3_gadget_ep_set_halt(dep, set, true); if (ret) return -EINVAL; + + /* ClearFeature(Halt) may need delayed status */ + if (!set && (dep->flags & DWC3_EP_END_TRANSFER_PENDING)) + return USB_GADGET_DELAYED_STATUS; + break; default: return -EINVAL; @@ -542,12 +551,11 @@ static int dwc3_ep0_handle_endpoint(struct dwc3 *dwc, return 0; } - static int dwc3_ep0_handle_feature(struct dwc3 *dwc, - struct usb_ctrlrequest *ctrl, int set) + struct usb_ctrlrequest *ctrl, int set) { - u32 recip; - int ret; + u32 recip; + int ret; recip = ctrl->bRequestType & USB_RECIP_MASK; @@ -570,7 +578,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc, static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) { - enum usb_device_state state = dwc->gadget.state; + enum usb_device_state state = dwc->gadget->state; u32 addr; u32 reg; @@ -581,7 +589,7 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) } if (state == USB_STATE_CONFIGURED) { - dev_err(dwc->dev, "trying to set address when configured\n"); + dev_err(dwc->dev, "can't SetAddress() from Configured State\n"); return -EINVAL; } @@ -591,27 +599,28 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) dwc3_writel(dwc->regs, DWC3_DCFG, reg); if (addr) - usb_gadget_set_state(&dwc->gadget, USB_STATE_ADDRESS); + usb_gadget_set_state(dwc->gadget, USB_STATE_ADDRESS); else - usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT); + usb_gadget_set_state(dwc->gadget, USB_STATE_DEFAULT); return 0; } static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) { - int ret; + int ret = -EINVAL; - spin_unlock(&dwc->lock); - ret = dwc->gadget_driver->setup(&dwc->gadget, ctrl); - spin_lock(&dwc->lock); + if (dwc->async_callbacks) { + spin_unlock(&dwc->lock); + ret = dwc->gadget_driver->setup(dwc->gadget, ctrl); + spin_lock(&dwc->lock); + } return ret; } -#define USB_GADGET_DELAYED_STATUS 0x7fff /* Impossibly large value */ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) { - enum usb_device_state state = dwc->gadget.state; + enum usb_device_state state = dwc->gadget->state; u32 cfg; int ret; u32 reg; @@ -623,6 +632,8 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) return -EINVAL; case USB_STATE_ADDRESS: + dwc3_gadget_clear_tx_fifos(dwc); + ret = dwc3_ep0_delegate_req(dwc, ctrl); /* if the cfg matches and the cfg is non zero */ if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) { @@ -634,7 +645,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) * to change the state on the next usb_ep_queue() */ if (ret == 0) - usb_gadget_set_state(&dwc->gadget, + usb_gadget_set_state(dwc->gadget, USB_STATE_CONFIGURED); /* @@ -642,7 +653,10 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) * nothing is pending from application. */ reg = dwc3_readl(dwc->regs, DWC3_DCTL); - reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA); + if (!dwc->dis_u1_entry_quirk) + reg |= DWC3_DCTL_ACCEPTU1ENA; + if (!dwc->dis_u2_entry_quirk) + reg |= DWC3_DCTL_ACCEPTU2ENA; dwc3_writel(dwc->regs, DWC3_DCTL, reg); } break; @@ -650,7 +664,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) case USB_STATE_CONFIGURED: ret = dwc3_ep0_delegate_req(dwc, ctrl); if (!cfg && !ret) - usb_gadget_set_state(&dwc->gadget, + usb_gadget_set_state(dwc->gadget, USB_STATE_ADDRESS); break; default: @@ -661,11 +675,11 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req) { - struct dwc3_ep *dep = to_dwc3_ep(ep); - struct dwc3 *dwc = dep->dwc; + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; - u32 param = 0; - u32 reg; + u32 param = 0; + u32 reg; struct timing { u8 u1sel; @@ -705,9 +719,9 @@ static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req) static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) { - struct dwc3_ep *dep; - enum usb_device_state state = dwc->gadget.state; - u16 wLength; + struct dwc3_ep *dep; + enum usb_device_state state = dwc->gadget->state; + u16 wLength; if (state == USB_STATE_DEFAULT) return -EINVAL; @@ -737,12 +751,11 @@ static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req); } -static int dwc3_ep0_set_isoch_delay(struct dwc3 *dwc, - struct usb_ctrlrequest *ctrl) +static int dwc3_ep0_set_isoch_delay(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) { - u16 wLength; - u16 wValue; - u16 wIndex; + u16 wLength; + u16 wValue; + u16 wIndex; wValue = le16_to_cpu(ctrl->wValue); wLength = le16_to_cpu(ctrl->wLength); @@ -751,11 +764,7 @@ static int dwc3_ep0_set_isoch_delay(struct dwc3 *dwc, if (wIndex || wLength) return -EINVAL; - /* - * REVISIT It's unclear from Databook what to do with this - * value. For now, just cache it. - */ - dwc->isoch_delay = wValue; + dwc->gadget->isoch_delay = wValue; return 0; } @@ -766,35 +775,27 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) switch (ctrl->bRequest) { case USB_REQ_GET_STATUS: - dev_dbg(dwc->dev, "USB_REQ_GET_STATUS\n"); ret = dwc3_ep0_handle_status(dwc, ctrl); break; case USB_REQ_CLEAR_FEATURE: - dev_dbg(dwc->dev, "USB_REQ_CLEAR_FEATURE\n"); ret = dwc3_ep0_handle_feature(dwc, ctrl, 0); break; case USB_REQ_SET_FEATURE: - dev_dbg(dwc->dev, "USB_REQ_SET_FEATURE\n"); ret = dwc3_ep0_handle_feature(dwc, ctrl, 1); break; case USB_REQ_SET_ADDRESS: - dev_dbg(dwc->dev, "USB_REQ_SET_ADDRESS\n"); ret = dwc3_ep0_set_address(dwc, ctrl); break; case USB_REQ_SET_CONFIGURATION: - dev_dbg(dwc->dev, "USB_REQ_SET_CONFIGURATION\n"); ret = dwc3_ep0_set_config(dwc, ctrl); break; case USB_REQ_SET_SEL: - dev_dbg(dwc->dev, "USB_REQ_SET_SEL\n"); ret = dwc3_ep0_set_sel(dwc, ctrl); break; case USB_REQ_SET_ISOCH_DELAY: - dev_dbg(dwc->dev, "USB_REQ_SET_ISOCH_DELAY\n"); ret = dwc3_ep0_set_isoch_delay(dwc, ctrl); break; default: - dev_dbg(dwc->dev, "Forwarding to gadget driver\n"); ret = dwc3_ep0_delegate_req(dwc, ctrl); break; } @@ -803,13 +804,13 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) } static void dwc3_ep0_inspect_setup(struct dwc3 *dwc, - const struct dwc3_event_depevt *event) + const struct dwc3_event_depevt *event) { struct usb_ctrlrequest *ctrl = (void *) dwc->ep0_trb; int ret = -EINVAL; u32 len; - if (!dwc->gadget_driver) + if (!dwc->gadget_driver || !dwc->softconnect || !dwc->connected) goto out; len = le16_to_cpu(ctrl->wLength); @@ -837,16 +838,16 @@ out: } static void dwc3_ep0_complete_data(struct dwc3 *dwc, - const struct dwc3_event_depevt *event) + const struct dwc3_event_depevt *event) { - struct dwc3_request *r = NULL; - struct usb_request *ur; - struct dwc3_trb *trb; - struct dwc3_ep *ep0; - u32 transferred = 0; - u32 status; - u32 length; - u8 epnum; + struct dwc3_request *r; + struct usb_request *ur; + struct dwc3_trb *trb; + struct dwc3_ep *ep0; + u32 transferred = 0; + u32 status; + u32 length; + u8 epnum; epnum = event->endpoint_number; ep0 = dwc->eps[0]; @@ -860,9 +861,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, status = DWC3_TRB_SIZE_TRBSTS(trb->size); if (status == DWC3_TRBSTS_SETUP_PENDING) { - dev_dbg(dwc->dev, "Setup Pending received\n"); dwc->setup_packet_pending = true; - if (r) dwc3_gadget_giveback(ep0, r, -ECONNRESET); @@ -876,7 +875,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, ur->actual += transferred; if ((IS_ALIGNED(ur->length, ep0->endpoint.maxpacket) && - ur->length && ur->zero) || dwc->ep0_bounced) { + ur->length && ur->zero) || dwc->ep0_bounced) { trb++; trb->ctrl &= ~DWC3_TRB_CTRL_HWO; @@ -895,12 +894,12 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, } static void dwc3_ep0_complete_status(struct dwc3 *dwc, - const struct dwc3_event_depevt *event) + const struct dwc3_event_depevt *event) { - struct dwc3_request *r; - struct dwc3_ep *dep; - struct dwc3_trb *trb; - u32 status; + struct dwc3_request *r; + struct dwc3_ep *dep; + struct dwc3_trb *trb; + u32 status; dep = dwc->eps[0]; trb = dwc->ep0_trb; @@ -916,27 +915,25 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc, ret = dwc3_gadget_set_test_mode(dwc, dwc->test_mode_nr); if (ret < 0) { - dev_dbg(dwc->dev, "Invalid Test #%d\n", - dwc->test_mode_nr); + dev_err(dwc->dev, "invalid test #%d\n", + dwc->test_mode_nr); dwc3_ep0_stall_and_restart(dwc); return; } } status = DWC3_TRB_SIZE_TRBSTS(trb->size); - if (status == DWC3_TRBSTS_SETUP_PENDING) { - dev_dbg(dwc->dev, "Setup Pending received\n"); + if (status == DWC3_TRBSTS_SETUP_PENDING) dwc->setup_packet_pending = true; - } dwc->ep0state = EP0_SETUP_PHASE; dwc3_ep0_out_start(dwc); } static void dwc3_ep0_xfer_complete(struct dwc3 *dwc, - const struct dwc3_event_depevt *event) + const struct dwc3_event_depevt *event) { - struct dwc3_ep *dep = dwc->eps[event->endpoint_number]; + struct dwc3_ep *dep = dwc->eps[event->endpoint_number]; dep->flags &= ~DWC3_EP_TRANSFER_STARTED; dep->resource_index = 0; @@ -944,17 +941,14 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc, switch (dwc->ep0state) { case EP0_SETUP_PHASE: - dev_dbg(dwc->dev, "Setup Phase\n"); dwc3_ep0_inspect_setup(dwc, event); break; case EP0_DATA_PHASE: - dev_dbg(dwc->dev, "Data Phase\n"); dwc3_ep0_complete_data(dwc, event); break; case EP0_STATUS_PHASE: - dev_dbg(dwc->dev, "Status Phase\n"); dwc3_ep0_complete_status(dwc, event); break; default: @@ -963,31 +957,29 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc, } static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, - struct dwc3_ep *dep, - struct dwc3_request *req) + struct dwc3_ep *dep, struct dwc3_request *req) { - dma_addr_t dma_addr; - int ret; + unsigned int trb_length = 0; + int ret; req->direction = !!dep->number; if (req->request.length == 0) { - dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 0, - DWC3_TRBCTL_CONTROL_DATA, false); + if (!req->direction) + trb_length = dep->endpoint.maxpacket; + + dwc3_ep0_prepare_one_trb(dep, dwc->bounce_addr, trb_length, + DWC3_TRBCTL_CONTROL_DATA, false); ret = dwc3_ep0_start_trans(dep); - } else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) && - (dep->number == 0)) { - u32 maxpacket; - u32 rem; - - dma_addr = dma_map_single(dwc->dev, req->request.buf, - req->request.length, - dep->number ? - DMA_TO_DEVICE : DMA_FROM_DEVICE); - if (dma_mapping_error(dwc->dev, dma_addr)) - return; + } else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) + && (dep->number == 0)) { + u32 maxpacket; + u32 rem; - req->request.dma = dma_addr; + ret = usb_gadget_map_request_by_dev(dwc->sysdev, + &req->request, dep->number); + if (ret) + return; maxpacket = dep->endpoint.maxpacket; rem = req->request.length % maxpacket; @@ -1009,14 +1001,11 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, ret = dwc3_ep0_start_trans(dep); } else if (IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) && req->request.length && req->request.zero) { - dma_addr = dma_map_single(dwc->dev, req->request.buf, - req->request.length, - dep->number ? - DMA_TO_DEVICE : DMA_FROM_DEVICE); - if (dma_mapping_error(dwc->dev, dma_addr)) - return; - req->request.dma = dma_addr; + ret = usb_gadget_map_request_by_dev(dwc->sysdev, + &req->request, dep->number); + if (ret) + return; /* prepare normal TRB */ dwc3_ep0_prepare_one_trb(dep, req->request.dma, @@ -1026,21 +1015,20 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, req->trb = &dwc->ep0_trb[dep->trb_enqueue - 1]; + if (!req->direction) + trb_length = dep->endpoint.maxpacket; + /* Now prepare one extra TRB to align transfer size */ dwc3_ep0_prepare_one_trb(dep, dwc->bounce_addr, - 0, DWC3_TRBCTL_CONTROL_DATA, + trb_length, DWC3_TRBCTL_CONTROL_DATA, false); ret = dwc3_ep0_start_trans(dep); } else { - dma_addr = dma_map_single(dwc->dev, req->request.buf, - req->request.length, - dep->number ? - DMA_TO_DEVICE : DMA_FROM_DEVICE); - if (dma_mapping_error(dwc->dev, dma_addr)) + ret = usb_gadget_map_request_by_dev(dwc->sysdev, + &req->request, dep->number); + if (ret) return; - req->request.dma = dma_addr; - dwc3_ep0_prepare_one_trb(dep, req->request.dma, req->request.length, DWC3_TRBCTL_CONTROL_DATA, false); @@ -1055,8 +1043,8 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, static int dwc3_ep0_start_control_status(struct dwc3_ep *dep) { - struct dwc3 *dwc = dep->dwc; - u32 type; + struct dwc3 *dwc = dep->dwc; + u32 type; type = dwc->three_stage_setup ? DWC3_TRBCTL_CONTROL_STATUS3 : DWC3_TRBCTL_CONTROL_STATUS2; @@ -1071,20 +1059,38 @@ static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep) } static void dwc3_ep0_do_control_status(struct dwc3 *dwc, - const struct dwc3_event_depevt *event) + const struct dwc3_event_depevt *event) { - struct dwc3_ep *dep = dwc->eps[event->endpoint_number]; + struct dwc3_ep *dep = dwc->eps[event->endpoint_number]; __dwc3_ep0_do_control_status(dwc, dep); } -static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep) +void dwc3_ep0_send_delayed_status(struct dwc3 *dwc) +{ + unsigned int direction = !dwc->ep0_expect_in; + + dwc->delayed_status = false; + dwc->clear_stall_protocol = 0; + + if (dwc->ep0state != EP0_STATUS_PHASE) + return; + + __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]); +} + +void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep) { struct dwc3_gadget_ep_cmd_params params; - u32 cmd; - int ret; + u32 cmd; + int ret; - if (!dep->resource_index) + /* + * For status/DATA OUT stage, TRB will be queued on ep0 out + * endpoint for which resource index is zero. Hence allow + * queuing ENDXFER command for ep0 out endpoint. + */ + if (!dep->resource_index && dep->number) return; cmd = DWC3_DEPCMD_ENDTRANSFER; @@ -1092,17 +1098,17 @@ static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep) cmd |= DWC3_DEPCMD_PARAM(dep->resource_index); memset(¶ms, 0, sizeof(params)); ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms); - WARN_ON(ret); + WARN_ON_ONCE(ret); dep->resource_index = 0; } static void dwc3_ep0_xfernotready(struct dwc3 *dwc, - const struct dwc3_event_depevt *event) + const struct dwc3_event_depevt *event) { switch (event->status) { case DEPEVT_STATUS_CONTROL_DATA: - dev_dbg(dwc->dev, "Control Data\n"); - + if (!dwc->softconnect || !dwc->connected) + return; /* * We already have a DATA transfer in the controller's cache, * if we receive a XferNotReady(DATA) we will ignore it, unless @@ -1113,9 +1119,9 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, * control endpoint. */ if (dwc->ep0_expect_in != event->endpoint_number) { - struct dwc3_ep *dep = dwc->eps[dwc->ep0_expect_in]; + struct dwc3_ep *dep = dwc->eps[dwc->ep0_expect_in]; - dev_dbg(dwc->dev, "Wrong direction for Data phase\n"); + dev_err(dwc->dev, "unexpected direction for Data Phase\n"); dwc3_ep0_end_control_data(dwc, dep); dwc3_ep0_stall_and_restart(dwc); return; @@ -1127,15 +1133,17 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS) return; - dev_dbg(dwc->dev, "Control Status\n"); + if (dwc->setup_packet_pending) { + dwc3_ep0_stall_and_restart(dwc); + return; + } dwc->ep0state = EP0_STATUS_PHASE; if (dwc->delayed_status) { struct dwc3_ep *dep = dwc->eps[0]; - WARN_ON(event->endpoint_number != 1); - dev_dbg(dwc->dev, "Delayed Status\n"); + WARN_ON_ONCE(event->endpoint_number != 1); /* * We should handle the delay STATUS phase here if the * request for handling delay STATUS has been queued @@ -1143,7 +1151,7 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, */ if (!list_empty(&dep->pending_list)) { dwc->delayed_status = false; - usb_gadget_set_state(&dwc->gadget, + usb_gadget_set_state(dwc->gadget, USB_STATE_CONFIGURED); dwc3_ep0_do_control_status(dwc, event); } @@ -1156,14 +1164,10 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, } void dwc3_ep0_interrupt(struct dwc3 *dwc, - const struct dwc3_event_depevt *event) + const struct dwc3_event_depevt *event) { - u8 epnum = event->endpoint_number; - - dev_dbg(dwc->dev, "%s while ep%d%s in state '%s'\n", - dwc3_ep_event_string(event->endpoint_event), - epnum >> 1, (epnum & 1) ? "in" : "out", - dwc3_ep0_state_string(dwc->ep0state)); + struct dwc3_ep *dep = dwc->eps[event->endpoint_number]; + u8 cmd; switch (event->endpoint_event) { case DWC3_DEPEVT_XFERCOMPLETE: @@ -1177,7 +1181,14 @@ void dwc3_ep0_interrupt(struct dwc3 *dwc, case DWC3_DEPEVT_XFERINPROGRESS: case DWC3_DEPEVT_RXTXFIFOEVT: case DWC3_DEPEVT_STREAMEVT: + break; case DWC3_DEPEVT_EPCMDCMPLT: + cmd = DEPEVT_PARAMETER_CMD(event->parameters); + + if (cmd == DWC3_DEPCMD_ENDTRANSFER) { + dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING; + dep->flags &= ~DWC3_EP_TRANSFER_STARTED; + } break; } } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index f416acc999..48be74f7e9 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1,40 +1,37 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * gadget.c - DesignWare USB3 DRD Controller Gadget Framework Link * - * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com * * Authors: Felipe Balbi <balbi@ti.com>, * Sebastian Andrzej Siewior <bigeasy@linutronix.de> - * - * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/gadget.c) and ported - * to uboot. - * - * commit 8e74475b0e : usb: dwc3: gadget: use udc-core's reset notifier */ #include <common.h> #include <dma.h> #include <io.h> #include <linux/list.h> -#include <usb/gadget.h> -#include <usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/ch9.h> +#include "debug.h" #include "core.h" #include "gadget.h" +#include "io.h" #define DWC3_ALIGN_FRAME(d, n) (((d)->frame_number + ((d)->interval * (n))) \ & ~((d)->interval - 1)) - /** - * dwc3_gadget_set_test_mode - Enables USB2 Test Modes + * dwc3_gadget_set_test_mode - enables usb2 test modes * @dwc: pointer to our context structure * @mode: the mode to set (J, K SE0 NAK, Force Enable) * - * Caller should take care of locking. This function will - * return 0 on success or -EINVAL if wrong Test Selector - * is passed + * Caller should take care of locking. This function will return 0 on + * success or -EINVAL if wrong Test Selector is passed. */ int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode) { @@ -44,24 +41,24 @@ int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode) reg &= ~DWC3_DCTL_TSTCTRL_MASK; switch (mode) { - case TEST_J: - case TEST_K: - case TEST_SE0_NAK: - case TEST_PACKET: - case TEST_FORCE_EN: + case USB_TEST_J: + case USB_TEST_K: + case USB_TEST_SE0_NAK: + case USB_TEST_PACKET: + case USB_TEST_FORCE_ENABLE: reg |= mode << 1; break; default: return -EINVAL; } - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_gadget_dctl_write_safe(dwc, reg); return 0; } /** - * dwc3_gadget_get_link_state - Gets current state of USB Link + * dwc3_gadget_get_link_state - gets current state of usb link * @dwc: pointer to our context structure * * Caller should take care of locking. This function will @@ -69,7 +66,7 @@ int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode) */ int dwc3_gadget_get_link_state(struct dwc3 *dwc) { - u32 reg; + u32 reg; reg = dwc3_readl(dwc->regs, DWC3_DSTS); @@ -77,7 +74,7 @@ int dwc3_gadget_get_link_state(struct dwc3 *dwc) } /** - * dwc3_gadget_set_link_state - Sets USB Link to a particular State + * dwc3_gadget_set_link_state - sets usb link to a particular state * @dwc: pointer to our context structure * @state: the state to put link into * @@ -86,14 +83,14 @@ int dwc3_gadget_get_link_state(struct dwc3 *dwc) */ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) { - int retries = 10000; - u32 reg; + int retries = 10000; + u32 reg; /* * Wait until device controller is ready. Only applies to 1.94a and * later RTL. */ - if (dwc->revision >= DWC3_REVISION_194A) { + if (!DWC3_VER_IS_PRIOR(DWC3, 194A)) { while (--retries) { reg = dwc3_readl(dwc->regs, DWC3_DSTS); if (reg & DWC3_DSTS_DCNRD) @@ -109,6 +106,9 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; + /* set no action before sending new link state change */ + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + /* set requested state */ reg |= DWC3_DCTL_ULSTCHNGREQ(state); dwc3_writel(dwc->regs, DWC3_DCTL, reg); @@ -117,7 +117,7 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) * The following code is racy when called from dwc3_gadget_wakeup, * and is not needed, at least on newer versions */ - if (dwc->revision >= DWC3_REVISION_194A) + if (!DWC3_VER_IS_PRIOR(DWC3, 194A)) return 0; /* wait for a change in DSTS */ @@ -131,8 +131,6 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) udelay(5); } - dev_dbg(dwc->dev, "link state change request timed out\n"); - return -ETIMEDOUT; } @@ -170,10 +168,9 @@ static void dwc3_ep_inc_deq(struct dwc3_ep *dep) } static void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep, - struct dwc3_request *req, - int status) + struct dwc3_request *req, int status) { - struct dwc3 *dwc = dep->dwc; + struct dwc3 *dwc = dep->dwc; list_del(&req->list); req->remaining = 0; @@ -182,13 +179,10 @@ static void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep, if (req->request.status == -EINPROGRESS) req->request.status = status; - if (req->request.length == 0) - return; - if (req->trb) dma_unmap_single(dwc->dev, req->request.dma, - req->request.length, - req->direction ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + req->request.length, + req->direction ? DMA_TO_DEVICE : DMA_FROM_DEVICE); req->trb = NULL; } @@ -204,18 +198,13 @@ static void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep, * layers that it has completed. */ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, - int status) + int status) { - struct dwc3 *dwc = dep->dwc; - dwc3_gadget_del_and_unmap_request(dep, req, status); - dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n", - req, dep->name, req->request.actual, - req->request.length, status); req->status = DWC3_REQUEST_STATUS_COMPLETED; spin_unlock(&dwc->lock); - req->request.complete(&dep->endpoint, &req->request); + usb_gadget_giveback_request(&dep->endpoint, &req->request); spin_lock(&dwc->lock); } @@ -228,12 +217,13 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, * Caller should take care of locking. Issue @cmd with a given @param to @dwc * and wait for its completion. */ -int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param) +int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd, + u32 param) { - u32 timeout = 500; - int status = 0; - int ret = 0; - u32 reg; + u32 timeout = 500; + int status = 0; + int ret = 0; + u32 reg; dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param); dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT); @@ -241,16 +231,11 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param) do { reg = dwc3_readl(dwc->regs, DWC3_DGCMD); if (!(reg & DWC3_DGCMD_CMDACT)) { - dev_dbg(dwc->dev, "%s: Command Complete --> %d\n", - __func__, - DWC3_DGCMD_STATUS(reg)); status = DWC3_DGCMD_STATUS(reg); if (status) ret = -EINVAL; break; } - - udelay(1); } while (--timeout); if (!timeout) { @@ -262,6 +247,7 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param) } static int __dwc3_gadget_wakeup(struct dwc3 *dwc); + /** * dwc3_send_gadget_ep_cmd - issue an endpoint command * @dep: the endpoint to which the command is going to be issued @@ -271,17 +257,17 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc); * Caller should handle locking. This function will issue @cmd with given * @params to @dep and wait for its completion. */ -int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, - struct dwc3_gadget_ep_cmd_params *params) +int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd, + struct dwc3_gadget_ep_cmd_params *params) { const struct usb_endpoint_descriptor *desc = dep->endpoint.desc; - struct dwc3 *dwc = dep->dwc; - u32 timeout = 1000; - u32 saved_config = 0; - u32 reg; + struct dwc3 *dwc = dep->dwc; + u32 timeout = 5000; + u32 saved_config = 0; + u32 reg; - int cmd_status = 0; - int ret = -EINVAL; + int cmd_status = 0; + int ret = -EINVAL; /* * When operating in USB 2.0 speeds (HS/FS), if GUSB2PHYCFG.ENBLSLPM or @@ -293,7 +279,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, * * DWC_usb3 3.30a and DWC_usb31 1.90a programming guide section 3.2.2 */ - if (dwc->gadget.speed <= USB_SPEED_HIGH) { + if (dwc->gadget->speed <= USB_SPEED_HIGH || + DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_ENDTRANSFER) { reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) { saved_config |= DWC3_GUSB2PHYCFG_SUSPHY; @@ -310,21 +297,40 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, } if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) { - int needs_wakeup; + int link_state; - needs_wakeup = (dwc->link_state == DWC3_LINK_STATE_U1 || - dwc->link_state == DWC3_LINK_STATE_U2 || - dwc->link_state == DWC3_LINK_STATE_U3); + /* + * Initiate remote wakeup if the link state is in U3 when + * operating in SS/SSP or L1/L2 when operating in HS/FS. If the + * link state is in U1/U2, no remote wakeup is needed. The Start + * Transfer command will initiate the link recovery. + */ + link_state = dwc3_gadget_get_link_state(dwc); + switch (link_state) { + case DWC3_LINK_STATE_U2: + if (dwc->gadget->speed >= USB_SPEED_SUPER) + break; - if (unlikely(needs_wakeup)) { + fallthrough; + case DWC3_LINK_STATE_U3: ret = __dwc3_gadget_wakeup(dwc); - dev_warn(dwc->dev, "wakeup failed --> %d\n", ret); + dev_WARN_ONCE(dwc->dev, ret, "wakeup failed --> %d\n", + ret); + break; } } - dwc3_writel(dep->regs, DWC3_DEPCMDPAR0, params->param0); - dwc3_writel(dep->regs, DWC3_DEPCMDPAR1, params->param1); - dwc3_writel(dep->regs, DWC3_DEPCMDPAR2, params->param2); + /* + * For some commands such as Update Transfer command, DEPCMDPARn + * registers are reserved. Since the driver often sends Update Transfer + * command, don't write to DEPCMDPARn to avoid register write delays and + * improve performance. + */ + if (DWC3_DEPCMD_CMD(cmd) != DWC3_DEPCMD_UPDATETRANSFER) { + dwc3_writel(dep->regs, DWC3_DEPCMDPAR0, params->param0); + dwc3_writel(dep->regs, DWC3_DEPCMDPAR1, params->param1); + dwc3_writel(dep->regs, DWC3_DEPCMDPAR2, params->param2); + } /* * Synopsys Databook 2.60a states in section 6.3.2.5.6 of that if we're @@ -348,6 +354,14 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, cmd |= DWC3_DEPCMD_CMDACT; dwc3_writel(dep->regs, DWC3_DEPCMD, cmd); + + if (!(cmd & DWC3_DEPCMD_CMDACT) || + (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_ENDTRANSFER && + !(cmd & DWC3_DEPCMD_CMDIOC))) { + ret = 0; + goto skip_status; + } + do { reg = dwc3_readl(dep->regs, DWC3_DEPCMD); if (!(reg & DWC3_DEPCMD_CMDACT)) { @@ -358,6 +372,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, ret = 0; break; case DEPEVT_TRANSFER_NO_RESOURCE: + dev_warn(dwc->dev, "No resource for %s\n", + dep->name); ret = -EINVAL; break; case DEPEVT_TRANSFER_BUS_EXPIRY: @@ -387,9 +403,14 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, cmd_status = -ETIMEDOUT; } - if (ret == 0 && DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) { - dep->flags |= DWC3_EP_TRANSFER_STARTED; - dwc3_gadget_ep_get_transfer_index(dep); +skip_status: + + if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) { + if (ret == 0) + dep->flags |= DWC3_EP_TRANSFER_STARTED; + + if (ret != -ETIMEDOUT) + dwc3_gadget_ep_get_transfer_index(dep); } if (saved_config) { @@ -399,7 +420,6 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, } return ret; - } static int dwc3_send_clear_stall_ep_cmd(struct dwc3_ep *dep) @@ -416,8 +436,9 @@ static int dwc3_send_clear_stall_ep_cmd(struct dwc3_ep *dep) * IN transfers due to a mishandled error condition. Synopsys * STAR 9000614252. */ - if (dep->direction && (dwc->revision >= DWC3_REVISION_260A) && - (dwc->gadget.speed >= USB_SPEED_SUPER)) + if (dep->direction && + !DWC3_VER_IS_PRIOR(DWC3, 260A) && + (dwc->gadget->speed >= USB_SPEED_SUPER)) cmd |= DWC3_DEPCMD_CLEARPENDIN; memset(¶ms, 0, sizeof(params)); @@ -426,9 +447,9 @@ static int dwc3_send_clear_stall_ep_cmd(struct dwc3_ep *dep) } static dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep, - struct dwc3_trb *trb) + struct dwc3_trb *trb) { - u32 offset = (char *) trb - (char *) dep->trb_pool; + u32 offset = (char *) trb - (char *) dep->trb_pool; return dep->trb_pool_dma + offset; } @@ -438,12 +459,11 @@ static int dwc3_alloc_trb_pool(struct dwc3_ep *dep) if (dep->trb_pool) return 0; - dep->trb_pool = dma_alloc_coherent(sizeof(struct dwc3_trb) * - DWC3_TRB_NUM, - &dep->trb_pool_dma); + dep->trb_pool = dma_alloc_coherent(sizeof(struct dwc3_trb) * DWC3_TRB_NUM, + &dep->trb_pool_dma); if (!dep->trb_pool) { dev_err(dep->dwc->dev, "failed to allocate trb pool for %s\n", - dep->name); + dep->name); return -ENOMEM; } @@ -452,7 +472,8 @@ static int dwc3_alloc_trb_pool(struct dwc3_ep *dep) static void dwc3_free_trb_pool(struct dwc3_ep *dep) { - dma_free_coherent(dep->trb_pool, 0, sizeof(dma_addr_t)); + dma_free_coherent(dep->trb_pool, dep->trb_pool_dma, + sizeof(struct dwc3_trb) * DWC3_TRB_NUM); dep->trb_pool = NULL; dep->trb_pool_dma = 0; @@ -467,7 +488,7 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3_ep *dep) params.param0 = DWC3_DEPXFERCFG_NUM_XFER_RES(1); return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETTRANSFRESOURCE, - ¶ms); + ¶ms); } /** @@ -506,10 +527,10 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3_ep *dep) static int dwc3_gadget_start_config(struct dwc3_ep *dep) { struct dwc3_gadget_ep_cmd_params params; - struct dwc3 *dwc; - u32 cmd; - int i; - int ret; + struct dwc3 *dwc; + u32 cmd; + int i; + int ret; if (dep->number) return 0; @@ -552,9 +573,10 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action) | DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc)); /* Burst size is only needed in SuperSpeed mode */ - if (dwc->gadget.speed == USB_SPEED_SUPER) { - u32 burst = dep->endpoint.maxburst - 1; - params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst); + if (dwc->gadget->speed >= USB_SPEED_SUPER) { + u32 burst = dep->endpoint.maxburst; + + params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst - 1); } params.param0 |= action; @@ -569,6 +591,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action) if (usb_ss_max_streams(comp_desc) && usb_endpoint_xfer_bulk(desc)) { params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE + | DWC3_DEPCFG_XFER_COMPLETE_EN | DWC3_DEPCFG_STREAM_EVENT_EN; dep->stream_capable = true; } @@ -592,31 +615,239 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action) params.param0 |= DWC3_DEPCFG_FIFO_NUMBER(dep->number >> 1); if (desc->bInterval) { - params.param1 |= DWC3_DEPCFG_BINTERVAL_M1(desc->bInterval - 1); - dep->interval = 1 << (desc->bInterval - 1); + u8 bInterval_m1; + + /* + * Valid range for DEPCFG.bInterval_m1 is from 0 to 13. + * + * NOTE: The programming guide incorrectly stated bInterval_m1 + * must be set to 0 when operating in fullspeed. Internally the + * controller does not have this limitation. See DWC_usb3x + * programming guide section 3.2.2.1. + */ + bInterval_m1 = min_t(u8, desc->bInterval - 1, 13); + + if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_INT && + dwc->gadget->speed == USB_SPEED_FULL) + dep->interval = desc->bInterval; + else + dep->interval = 1 << (desc->bInterval - 1); + + params.param1 |= DWC3_DEPCFG_BINTERVAL_M1(bInterval_m1); } return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, ¶ms); } /** - * __dwc3_gadget_ep_enable - Initializes a HW endpoint + * dwc3_gadget_calc_tx_fifo_size - calculates the txfifo size value + * @dwc: pointer to the DWC3 context + * @mult: multiplier to be used when calculating the fifo_size + * + * Calculates the size value based on the equation below: + * + * DWC3 revision 280A and prior: + * fifo_size = mult * (max_packet / mdwidth) + 1; + * + * DWC3 revision 290A and onwards: + * fifo_size = mult * ((max_packet + mdwidth)/mdwidth + 1) + 1 + * + * The max packet size is set to 1024, as the txfifo requirements mainly apply + * to super speed USB use cases. However, it is safe to overestimate the fifo + * allocations for other scenarios, i.e. high speed USB. + */ +static int dwc3_gadget_calc_tx_fifo_size(struct dwc3 *dwc, int mult) +{ + int max_packet = 1024; + int fifo_size; + int mdwidth; + + mdwidth = dwc3_mdwidth(dwc); + + /* MDWIDTH is represented in bits, we need it in bytes */ + mdwidth >>= 3; + + if (DWC3_VER_IS_PRIOR(DWC3, 290A)) + fifo_size = mult * (max_packet / mdwidth) + 1; + else + fifo_size = mult * ((max_packet + mdwidth) / mdwidth) + 1; + return fifo_size; +} + +/** + * dwc3_gadget_clear_tx_fifos - Clears txfifo allocation + * @dwc: pointer to the DWC3 context + * + * Iterates through all the endpoint registers and clears the previous txfifo + * allocations. + */ +void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc) +{ + struct dwc3_ep *dep; + int fifo_depth; + int size; + int num; + + if (!dwc->do_fifo_resize) + return; + + /* Read ep0IN related TXFIFO size */ + dep = dwc->eps[1]; + size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0)); + if (DWC3_IP_IS(DWC3)) + fifo_depth = DWC3_GTXFIFOSIZ_TXFDEP(size); + else + fifo_depth = DWC31_GTXFIFOSIZ_TXFDEP(size); + + dwc->last_fifo_depth = fifo_depth; + /* Clear existing TXFIFO for all IN eps except ep0 */ + for (num = 3; num < min_t(int, dwc->num_eps, DWC3_ENDPOINTS_NUM); + num += 2) { + dep = dwc->eps[num]; + /* Don't change TXFRAMNUM on usb31 version */ + size = DWC3_IP_IS(DWC3) ? 0 : + dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1)) & + DWC31_GTXFIFOSIZ_TXFRAMNUM; + + dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1), size); + dep->flags &= ~DWC3_EP_TXFIFO_RESIZED; + } + dwc->num_ep_resized = 0; +} + +/* + * dwc3_gadget_resize_tx_fifos - reallocate fifo spaces for current use-case + * @dwc: pointer to our context structure + * + * This function will a best effort FIFO allocation in order + * to improve FIFO usage and throughput, while still allowing + * us to enable as many endpoints as possible. + * + * Keep in mind that this operation will be highly dependent + * on the configured size for RAM1 - which contains TxFifo -, + * the amount of endpoints enabled on coreConsultant tool, and + * the width of the Master Bus. + * + * In general, FIFO depths are represented with the following equation: + * + * fifo_size = mult * ((max_packet + mdwidth)/mdwidth + 1) + 1 + * + * In conjunction with dwc3_gadget_check_config(), this resizing logic will + * ensure that all endpoints will have enough internal memory for one max + * packet per endpoint. + */ +static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep) +{ + struct dwc3 *dwc = dep->dwc; + int fifo_0_start; + int ram1_depth; + int fifo_size; + int min_depth; + int num_in_ep; + int remaining; + int num_fifos = 1; + int fifo; + int tmp; + + if (!dwc->do_fifo_resize) + return 0; + + /* resize IN endpoints except ep0 */ + if (!usb_endpoint_dir_in(dep->endpoint.desc) || dep->number <= 1) + return 0; + + /* bail if already resized */ + if (dep->flags & DWC3_EP_TXFIFO_RESIZED) + return 0; + + ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7); + + if ((dep->endpoint.maxburst > 1 && + usb_endpoint_xfer_bulk(dep->endpoint.desc)) || + usb_endpoint_xfer_isoc(dep->endpoint.desc)) + num_fifos = 3; + + if (dep->endpoint.maxburst > 6 && + (usb_endpoint_xfer_bulk(dep->endpoint.desc) || + usb_endpoint_xfer_isoc(dep->endpoint.desc)) && DWC3_IP_IS(DWC31)) + num_fifos = dwc->tx_fifo_resize_max_num; + + /* FIFO size for a single buffer */ + fifo = dwc3_gadget_calc_tx_fifo_size(dwc, 1); + + /* Calculate the number of remaining EPs w/o any FIFO */ + num_in_ep = dwc->max_cfg_eps; + num_in_ep -= dwc->num_ep_resized; + + /* Reserve at least one FIFO for the number of IN EPs */ + min_depth = num_in_ep * (fifo + 1); + remaining = ram1_depth - min_depth - dwc->last_fifo_depth; + remaining = max_t(int, 0, remaining); + /* + * We've already reserved 1 FIFO per EP, so check what we can fit in + * addition to it. If there is not enough remaining space, allocate + * all the remaining space to the EP. + */ + fifo_size = (num_fifos - 1) * fifo; + if (remaining < fifo_size) + fifo_size = remaining; + + fifo_size += fifo; + /* Last increment according to the TX FIFO size equation */ + fifo_size++; + + /* Check if TXFIFOs start at non-zero addr */ + tmp = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0)); + fifo_0_start = DWC3_GTXFIFOSIZ_TXFSTADDR(tmp); + + fifo_size |= (fifo_0_start + (dwc->last_fifo_depth << 16)); + if (DWC3_IP_IS(DWC3)) + dwc->last_fifo_depth += DWC3_GTXFIFOSIZ_TXFDEP(fifo_size); + else + dwc->last_fifo_depth += DWC31_GTXFIFOSIZ_TXFDEP(fifo_size); + + /* Check fifo size allocation doesn't exceed available RAM size. */ + if (dwc->last_fifo_depth >= ram1_depth) { + dev_err(dwc->dev, "Fifosize(%d) > RAM size(%d) %s depth:%d\n", + dwc->last_fifo_depth, ram1_depth, + dep->endpoint.name, fifo_size); + if (DWC3_IP_IS(DWC3)) + fifo_size = DWC3_GTXFIFOSIZ_TXFDEP(fifo_size); + else + fifo_size = DWC31_GTXFIFOSIZ_TXFDEP(fifo_size); + + dwc->last_fifo_depth -= fifo_size; + return -ENOMEM; + } + + dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1), fifo_size); + dep->flags |= DWC3_EP_TXFIFO_RESIZED; + dwc->num_ep_resized++; + + return 0; +} + +/** + * __dwc3_gadget_ep_enable - initializes a hw endpoint * @dep: endpoint to be initialized - * @desc: USB Endpoint Descriptor + * @action: one of INIT, MODIFY or RESTORE * - * Caller should take care of locking + * Caller should take care of locking. Execute all necessary commands to + * initialize a HW endpoint so it can be used by a gadget driver. */ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action) { const struct usb_endpoint_descriptor *desc = dep->endpoint.desc; - struct dwc3 *dwc = dep->dwc; + struct dwc3 *dwc = dep->dwc; - u32 reg; - int ret; - - dev_dbg(dwc->dev, "Enabling %s\n", dep->name); + u32 reg; + int ret; if (!(dep->flags & DWC3_EP_ENABLED)) { + ret = dwc3_gadget_resize_tx_fifos(dep); + if (ret) + return ret; + ret = dwc3_gadget_start_config(dep); if (ret) return ret; @@ -627,8 +858,8 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action) return ret; if (!(dep->flags & DWC3_EP_ENABLED)) { - struct dwc3_trb *trb_st_hw; - struct dwc3_trb *trb_link; + struct dwc3_trb *trb_st_hw; + struct dwc3_trb *trb_link; dep->type = usb_endpoint_type(desc); dep->flags |= DWC3_EP_ENABLED; @@ -637,25 +868,22 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action) reg |= DWC3_DALEPENA_EP(dep->number); dwc3_writel(dwc->regs, DWC3_DALEPENA, reg); + dep->trb_dequeue = 0; + dep->trb_enqueue = 0; + if (usb_endpoint_xfer_control(desc)) - return 0; + goto out; /* Initialize the TRB ring */ - dep->trb_dequeue = 0; - dep->trb_enqueue = 0; - memset(dep->trb_pool, 0, + memset_io(dep->trb_pool, 0, sizeof(struct dwc3_trb) * DWC3_TRB_NUM); /* Link TRB. The HWO bit is never reset */ trb_st_hw = &dep->trb_pool[0]; trb_link = &dep->trb_pool[DWC3_TRB_NUM - 1]; - memset(trb_link, 0, sizeof(*trb_link)); - - trb_link->bpl = lower_32_bits(dwc3_trb_dma_offset(dep, - trb_st_hw)); - trb_link->bph = upper_32_bits(dwc3_trb_dma_offset(dep, - trb_st_hw)); + trb_link->bpl = lower_32_bits(dwc3_trb_dma_offset(dep, trb_st_hw)); + trb_link->bph = upper_32_bits(dwc3_trb_dma_offset(dep, trb_st_hw)); trb_link->ctrl |= DWC3_TRBCTL_LINK_TRB; trb_link->ctrl |= DWC3_TRB_CTRL_HWO; } @@ -664,10 +892,10 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action) * Issue StartTransfer here with no-op TRB so we can always rely on No * Response Update Transfer command. */ - if ((usb_endpoint_xfer_bulk(desc) && !dep->stream_capable) || + if (usb_endpoint_xfer_bulk(desc) || usb_endpoint_xfer_int(desc)) { struct dwc3_gadget_ep_cmd_params params; - struct dwc3_trb *trb; + struct dwc3_trb *trb; dma_addr_t trb_dma; u32 cmd; @@ -683,47 +911,88 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action) ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms); if (ret < 0) return ret; + + if (dep->stream_capable) { + /* + * For streams, at start, there maybe a race where the + * host primes the endpoint before the function driver + * queues a request to initiate a stream. In that case, + * the controller will not see the prime to generate the + * ERDY and start stream. To workaround this, issue a + * no-op TRB as normal, but end it immediately. As a + * result, when the function driver queues the request, + * the next START_TRANSFER command will cause the + * controller to generate an ERDY to initiate the + * stream. + */ + dwc3_stop_active_transfer(dep, true, true); + + /* + * All stream eps will reinitiate stream on NoStream + * rejection until we can determine that the host can + * prime after the first transfer. + * + * However, if the controller is capable of + * TXF_FLUSH_BYPASS, then IN direction endpoints will + * automatically restart the stream without the driver + * initiation. + */ + if (!dep->direction || + !(dwc->hwparams.hwparams9 & + DWC3_GHWPARAMS9_DEV_TXF_FLUSH_BYPASS)) + dep->flags |= DWC3_EP_FORCE_RESTART_STREAM; + } } +out: return 0; } -static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, - bool interrupt); -static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep) +void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep, int status) { - struct dwc3_request *req; + struct dwc3_request *req; dwc3_stop_active_transfer(dep, true, false); + /* If endxfer is delayed, avoid unmapping requests */ + if (dep->flags & DWC3_EP_DELAY_STOP) + return; + /* - giveback all requests to gadget driver */ while (!list_empty(&dep->started_list)) { req = next_request(&dep->started_list); - dwc3_gadget_giveback(dep, req, -ESHUTDOWN); + dwc3_gadget_giveback(dep, req, status); } while (!list_empty(&dep->pending_list)) { req = next_request(&dep->pending_list); - dwc3_gadget_giveback(dep, req, -ESHUTDOWN); + dwc3_gadget_giveback(dep, req, status); + } + + while (!list_empty(&dep->cancelled_list)) { + req = next_request(&dep->cancelled_list); + + dwc3_gadget_giveback(dep, req, status); } } /** - * __dwc3_gadget_ep_disable - Disables a HW endpoint + * __dwc3_gadget_ep_disable - disables a hw endpoint * @dep: the endpoint to disable * - * This function also removes requests which are currently processed ny the - * hardware and those which are not yet scheduled. + * This function undoes what __dwc3_gadget_ep_enable did and also removes + * requests which are currently being processed by the hardware and those which + * are not yet scheduled. + * * Caller should take care of locking. */ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep) { - struct dwc3 *dwc = dep->dwc; - u32 reg; - - dwc3_remove_requests(dwc, dep); + struct dwc3 *dwc = dep->dwc; + u32 reg; + u32 mask; /* make sure HW endpoint isn't stalled */ if (dep->flags & DWC3_EP_STALL) @@ -733,9 +1002,19 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep) reg &= ~DWC3_DALEPENA_EP(dep->number); dwc3_writel(dwc->regs, DWC3_DALEPENA, reg); + dwc3_remove_requests(dwc, dep, -ESHUTDOWN); + dep->stream_capable = false; dep->type = 0; - dep->flags = 0; + mask = DWC3_EP_TXFIFO_RESIZED; + /* + * dwc3_remove_requests() can exit early if DWC3 EP delayed stop is + * set. Do not clear DEP flags, so that the end transfer command will + * be reattempted during the next SETUP stage. + */ + if (dep->flags & DWC3_EP_DELAY_STOP) + mask |= (DWC3_EP_DELAY_STOP | DWC3_EP_TRANSFER_STARTED); + dep->flags &= mask; /* Clear out the ep descriptors for non-ep0 */ if (dep->number > 1) { @@ -749,7 +1028,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep) /* -------------------------------------------------------------------------- */ static int dwc3_gadget_ep0_enable(struct usb_ep *ep, - const struct usb_endpoint_descriptor *desc) + const struct usb_endpoint_descriptor *desc) { return -EINVAL; } @@ -762,12 +1041,12 @@ static int dwc3_gadget_ep0_disable(struct usb_ep *ep) /* -------------------------------------------------------------------------- */ static int dwc3_gadget_ep_enable(struct usb_ep *ep, - const struct usb_endpoint_descriptor *desc) + const struct usb_endpoint_descriptor *desc) { - struct dwc3_ep *dep; - struct dwc3 *dwc; - unsigned long flags; - int ret; + struct dwc3_ep *dep; + struct dwc3 *dwc; + unsigned long flags; + int ret; if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) { pr_debug("dwc3: invalid parameters\n"); @@ -782,11 +1061,10 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep, dep = to_dwc3_ep(ep); dwc = dep->dwc; - if (dep->flags & DWC3_EP_ENABLED) { - WARN(true, "%s is already enabled\n", - dep->name); + if (dev_WARN_ONCE(dwc->dev, dep->flags & DWC3_EP_ENABLED, + "%s is already enabled\n", + dep->name)) return 0; - } spin_lock_irqsave(&dwc->lock, flags); ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT); @@ -797,10 +1075,10 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep, static int dwc3_gadget_ep_disable(struct usb_ep *ep) { - struct dwc3_ep *dep; - struct dwc3 *dwc; - unsigned long flags; - int ret; + struct dwc3_ep *dep; + struct dwc3 *dwc; + unsigned long flags; + int ret; if (!ep) { pr_debug("dwc3: invalid parameters\n"); @@ -810,11 +1088,10 @@ static int dwc3_gadget_ep_disable(struct usb_ep *ep) dep = to_dwc3_ep(ep); dwc = dep->dwc; - if (!(dep->flags & DWC3_EP_ENABLED)) { - WARN(true, "%s is already disabled\n", - dep->name); + if (dev_WARN_ONCE(dwc->dev, !(dep->flags & DWC3_EP_ENABLED), + "%s is already disabled\n", + dep->name)) return 0; - } spin_lock_irqsave(&dwc->lock, flags); ret = __dwc3_gadget_ep_disable(dep); @@ -825,10 +1102,10 @@ static int dwc3_gadget_ep_disable(struct usb_ep *ep) static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep) { - struct dwc3_request *req; - struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3_request *req; + struct dwc3_ep *dep = to_dwc3_ep(ep); - req = xzalloc(sizeof(*req)); + req = kzalloc(sizeof(*req), GFP_KERNEL); if (!req) return NULL; @@ -841,9 +1118,9 @@ static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep) } static void dwc3_gadget_ep_free_request(struct usb_ep *ep, - struct usb_request *request) + struct usb_request *request) { - struct dwc3_request *req = to_dwc3_request(request); + struct dwc3_request *req = to_dwc3_request(request); kfree(req); } @@ -869,19 +1146,19 @@ static struct dwc3_trb *dwc3_ep_prev_trb(struct dwc3_ep *dep, u8 index) static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep) { - struct dwc3_trb *tmp; - u8 trbs_left; + u8 trbs_left; /* - * If enqueue & dequeue are equal than it is either full or empty. - * - * One way to know for sure is if the TRB right before us has HWO bit - * set or not. If it has, then we're definitely full and can't fit any - * more transfers in our ring. + * If the enqueue & dequeue are equal then the TRB ring is either full + * or empty. It's considered full when there are DWC3_TRB_NUM-1 of TRBs + * pending to be processed by the driver. */ if (dep->trb_enqueue == dep->trb_dequeue) { - tmp = dwc3_ep_prev_trb(dep, dep->trb_enqueue); - if (tmp->ctrl & DWC3_TRB_CTRL_HWO) + /* + * If there is any request remained in the started_list at + * this point, that means there is no TRB available. + */ + if (!list_empty(&dep->started_list)) return 0; return DWC3_TRB_NUM - 1; @@ -896,17 +1173,47 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep) return trbs_left; } -static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb, - dma_addr_t dma, unsigned length, - unsigned chain, unsigned node, - unsigned stream_id, unsigned short_not_ok, - unsigned no_interrupt) +/** + * dwc3_prepare_one_trb - setup one TRB from one request + * @dep: endpoint for which this request is prepared + * @req: dwc3_request pointer + * @trb_length: buffer size of the TRB + * @chain: should this TRB be chained to the next? + * @node: only for isochronous endpoints. First TRB needs different type. + * @use_bounce_buffer: set to use bounce buffer + * @must_interrupt: set to interrupt on TRB completion + */ +static void dwc3_prepare_one_trb(struct dwc3_ep *dep, + struct dwc3_request *req, unsigned int trb_length, + unsigned int chain, unsigned int node, bool use_bounce_buffer, + bool must_interrupt) { - struct dwc3 *dwc = dep->dwc; - struct usb_gadget *gadget = &dwc->gadget; - enum usb_device_speed speed = gadget->speed; + struct dwc3_trb *trb; + dma_addr_t dma; + unsigned int stream_id = req->request.stream_id; + unsigned int short_not_ok = req->request.short_not_ok; + unsigned int no_interrupt = req->request.no_interrupt; + unsigned int is_last = req->request.is_last; + struct dwc3 *dwc = dep->dwc; + struct usb_gadget *gadget = dwc->gadget; + enum usb_device_speed speed = gadget->speed; + + if (use_bounce_buffer) + dma = dep->dwc->bounce_addr; + else + dma = req->request.dma; + + trb = &dep->trb_pool[dep->trb_enqueue]; - trb->size = DWC3_TRB_SIZE_LENGTH(length); + if (!req->trb) { + dwc3_gadget_move_started_request(req); + req->trb = trb; + req->trb_dma = dwc3_trb_dma_offset(dep, trb); + } + + req->num_trbs++; + + trb->size = DWC3_TRB_SIZE_LENGTH(trb_length); trb->bpl = lower_32_bits(dma); trb->bph = upper_32_bits(dma); @@ -946,10 +1253,10 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb, unsigned int mult = 2; unsigned int maxp = usb_endpoint_maxp(ep->desc); - if (length <= (2 * maxp)) + if (req->request.length <= (2 * maxp)) mult--; - if (length <= maxp) + if (req->request.length <= maxp) mult--; trb->size |= DWC3_TRB_SIZE_PCM1(mult); @@ -958,8 +1265,8 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb, trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS; } - /* always enable Interrupt on Missed ISOC */ - trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI; + if (!no_interrupt && !chain) + trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI; break; case USB_ENDPOINT_XFER_BULK: @@ -972,7 +1279,7 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb, * checked it already :) */ dev_warn(dwc->dev, "Unknown endpoint type %d\n", - usb_endpoint_type(dep->endpoint.desc)); + usb_endpoint_type(dep->endpoint.desc)); } /* @@ -987,149 +1294,197 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb, trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI; } - if ((!no_interrupt && !chain) || - (dwc3_calc_trbs_left(dep) == 1)) + /* All TRBs setup for MST must set CSP=1 when LST=0 */ + if (dep->stream_capable && DWC3_MST_CAPABLE(&dwc->hwparams)) + trb->ctrl |= DWC3_TRB_CTRL_CSP; + + if ((!no_interrupt && !chain) || must_interrupt) trb->ctrl |= DWC3_TRB_CTRL_IOC; if (chain) trb->ctrl |= DWC3_TRB_CTRL_CHN; + else if (dep->stream_capable && is_last && + !DWC3_MST_CAPABLE(&dwc->hwparams)) + trb->ctrl |= DWC3_TRB_CTRL_LST; if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable) trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(stream_id); + /* + * As per data book 4.2.3.2TRB Control Bit Rules section + * + * The controller autonomously checks the HWO field of a TRB to determine if the + * entire TRB is valid. Therefore, software must ensure that the rest of the TRB + * is valid before setting the HWO field to '1'. In most systems, this means that + * software must update the fourth DWORD of a TRB last. + * + * However there is a possibility of CPU re-ordering here which can cause + * controller to observe the HWO bit set prematurely. + * Add a write memory barrier to prevent CPU re-ordering. + */ + /* wmb() FIXME */ trb->ctrl |= DWC3_TRB_CTRL_HWO; dwc3_ep_inc_enq(dep); } -/** - * dwc3_prepare_one_trb - setup one TRB from one request - * @dep: endpoint for which this request is prepared - * @req: dwc3_request pointer - * @chain: should this TRB be chained to the next? - * @node: only for isochronous endpoints. First TRB needs different type. - */ -static void dwc3_prepare_one_trb(struct dwc3_ep *dep, - struct dwc3_request *req, - unsigned chain, unsigned node) +static bool dwc3_needs_extra_trb(struct dwc3_ep *dep, struct dwc3_request *req) { - struct dwc3_trb *trb; - unsigned int length; - dma_addr_t dma; - unsigned stream_id = req->request.stream_id; - unsigned short_not_ok = req->request.short_not_ok; - unsigned no_interrupt = req->request.no_interrupt; - - length = req->request.length; - dma = req->request.dma; - - trb = &dep->trb_pool[dep->trb_enqueue]; - - if (!req->trb) { - dwc3_gadget_move_started_request(req); - req->trb = trb; - req->trb_dma = dwc3_trb_dma_offset(dep, trb); - } + unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc); + unsigned int rem = req->request.length % maxp; - req->num_trbs++; + if ((req->request.length && req->request.zero && !rem && + !usb_endpoint_xfer_isoc(dep->endpoint.desc)) || + (!req->direction && rem)) + return true; - __dwc3_prepare_one_trb(dep, trb, dma, length, chain, node, - stream_id, short_not_ok, no_interrupt); + return false; } -static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, - struct dwc3_request *req) +/** + * dwc3_prepare_last_sg - prepare TRBs for the last SG entry + * @dep: The endpoint that the request belongs to + * @req: The request to prepare + * @entry_length: The last SG entry size + * @node: Indicates whether this is not the first entry (for isoc only) + * + * Return the number of TRBs prepared. + */ +static int dwc3_prepare_last_sg(struct dwc3_ep *dep, + struct dwc3_request *req, unsigned int entry_length, + unsigned int node) { - unsigned int length = req->request.length; unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc); - unsigned int rem = length % maxp; + unsigned int rem = req->request.length % maxp; + unsigned int num_trbs = 1; - if ((!length || rem) && usb_endpoint_dir_out(dep->endpoint.desc)) { - struct dwc3 *dwc = dep->dwc; - struct dwc3_trb *trb; + if (dwc3_needs_extra_trb(dep, req)) + num_trbs++; - req->needs_extra_trb = true; + if (dwc3_calc_trbs_left(dep) < num_trbs) + return 0; - /* prepare normal TRB */ - dwc3_prepare_one_trb(dep, req, true, 0); + req->needs_extra_trb = num_trbs > 1; - /* Now prepare one extra TRB to align transfer size */ - trb = &dep->trb_pool[dep->trb_enqueue]; - req->num_trbs++; - __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem, - false, 1, req->request.stream_id, - req->request.short_not_ok, - req->request.no_interrupt); - } else if (req->request.zero && req->request.length && - (IS_ALIGNED(req->request.length, maxp))) { - struct dwc3 *dwc = dep->dwc; - struct dwc3_trb *trb; + /* Prepare a normal TRB */ + if (req->direction || req->request.length) + dwc3_prepare_one_trb(dep, req, entry_length, + req->needs_extra_trb, node, false, false); - req->needs_extra_trb = true; + /* Prepare extra TRBs for ZLP and MPS OUT transfer alignment */ + if ((!req->direction && !req->request.length) || req->needs_extra_trb) + dwc3_prepare_one_trb(dep, req, + req->direction ? 0 : maxp - rem, + false, 1, true, false); - /* prepare normal TRB */ - dwc3_prepare_one_trb(dep, req, true, 0); + return num_trbs; +} - /* Now prepare one extra TRB to handle ZLP */ - trb = &dep->trb_pool[dep->trb_enqueue]; - req->num_trbs++; - __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0, - false, 1, req->request.stream_id, - req->request.short_not_ok, - req->request.no_interrupt); - } else { - dwc3_prepare_one_trb(dep, req, false, 0); - } +static int dwc3_prepare_trbs_linear(struct dwc3_ep *dep, + struct dwc3_request *req) +{ + return dwc3_prepare_last_sg(dep, req, req->request.length, 0); } /* * dwc3_prepare_trbs - setup TRBs from requests * @dep: endpoint for which requests are being prepared - * @starting: true if the endpoint is idle and no requests are queued. * * The function goes through the requests list and sets up TRBs for the * transfers. The function returns once there are no more TRBs available or * it runs out of requests. + * + * Returns the number of TRBs prepared or negative errno. */ -static void dwc3_prepare_trbs(struct dwc3_ep *dep) +static int dwc3_prepare_trbs(struct dwc3_ep *dep) { - struct dwc3_request *req, *n; - struct dwc3 *dwc = dep->dwc; - dma_addr_t dma_addr; + struct dwc3_request *req, *n; + int ret = 0; BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM); + /* + * We can get in a situation where there's a request in the started list + * but there weren't enough TRBs to fully kick it in the first time + * around, so it has been waiting for more TRBs to be freed up. + * + * In that case, we should check if we have a request with pending_sgs + * in the started list and prepare TRBs for that request first, + * otherwise we will prepare TRBs completely out of order and that will + * break things. + */ + list_for_each_entry(req, &dep->started_list, list) { + if (!dwc3_calc_trbs_left(dep)) + return ret; + + /* + * Don't prepare beyond a transfer. In DWC_usb32, its transfer + * burst capability may try to read and use TRBs beyond the + * active transfer instead of stopping. + */ + if (dep->stream_capable && req->request.is_last && + !DWC3_MST_CAPABLE(&dep->dwc->hwparams)) + return ret; + } + list_for_each_entry_safe(req, n, &dep->pending_list, list) { - dma_addr = dma_map_single(dwc->dev, req->request.buf, - req->request.length, - dep->number ? - DMA_TO_DEVICE : DMA_FROM_DEVICE); - if (dma_mapping_error(dwc->dev, dma_addr)) - return; + struct dwc3 *dwc = dep->dwc; - req->request.dma = dma_addr; + ret = usb_gadget_map_request_by_dev(dwc->sysdev, &req->request, + dep->direction); + if (ret) + return ret; - dwc3_prepare_one_trb_linear(dep, req); + req->sg = req->request.sg; + req->start_sg = req->sg; + req->num_queued_sgs = 0; + req->num_pending_sgs = req->request.num_mapped_sgs; - if (!dwc3_calc_trbs_left(dep)) - return; + ret = dwc3_prepare_trbs_linear(dep, req); + + if (!ret || !dwc3_calc_trbs_left(dep)) + return ret; + + /* + * Don't prepare beyond a transfer. In DWC_usb32, its transfer + * burst capability may try to read and use TRBs beyond the + * active transfer instead of stopping. + */ + if (dep->stream_capable && req->request.is_last && + !DWC3_MST_CAPABLE(&dwc->hwparams)) + return ret; } + + return ret; } +static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep); + static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep) { struct dwc3_gadget_ep_cmd_params params; - struct dwc3_request *req; - int starting; - int ret; - u32 cmd; + struct dwc3_request *req; + int starting; + int ret; + u32 cmd; - if (!dwc3_calc_trbs_left(dep)) - return 0; + /* + * Note that it's normal to have no new TRBs prepared (i.e. ret == 0). + * This happens when we need to stop and restart a transfer such as in + * the case of reinitiating a stream or retrying an isoc transfer. + */ + ret = dwc3_prepare_trbs(dep); + if (ret < 0) + return ret; starting = !(dep->flags & DWC3_EP_TRANSFER_STARTED); - dwc3_prepare_trbs(dep); + /* + * If there's no new TRB prepared and we don't need to restart a + * transfer, there's no need to update the transfer. + */ + if (!ret && !starting) + return ret; req = next_request(&dep->started_list); if (!req) { @@ -1156,29 +1511,84 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep) ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms); if (ret < 0) { - /* - * FIXME we need to iterate over the list of requests - * here and stop, unmap, free and del each of the linked - * requests instead of what we do now. - */ - if (req->trb) - memset(req->trb, 0, sizeof(struct dwc3_trb)); - dwc3_gadget_del_and_unmap_request(dep, req, ret); + struct dwc3_request *tmp; + + if (ret == -EAGAIN) + return ret; + + dwc3_stop_active_transfer(dep, true, true); + + list_for_each_entry_safe(req, tmp, &dep->started_list, list) + dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_DEQUEUED); + + /* If ep isn't started, then there's no end transfer pending */ + if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING)) + dwc3_gadget_ep_cleanup_cancelled_requests(dep); + return ret; } + if (dep->stream_capable && req->request.is_last && + !DWC3_MST_CAPABLE(&dep->dwc->hwparams)) + dep->flags |= DWC3_EP_WAIT_TRANSFER_COMPLETE; + return 0; } static int __dwc3_gadget_get_frame(struct dwc3 *dwc) { - u32 reg; + u32 reg; reg = dwc3_readl(dwc->regs, DWC3_DSTS); return DWC3_DSTS_SOFFN(reg); } /** + * __dwc3_stop_active_transfer - stop the current active transfer + * @dep: isoc endpoint + * @force: set forcerm bit in the command + * @interrupt: command complete interrupt after End Transfer command + * + * When setting force, the ForceRM bit will be set. In that case + * the controller won't update the TRB progress on command + * completion. It also won't clear the HWO bit in the TRB. + * The command will also not complete immediately in that case. + */ +static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt) +{ + struct dwc3_gadget_ep_cmd_params params; + u32 cmd; + int ret; + + cmd = DWC3_DEPCMD_ENDTRANSFER; + cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0; + cmd |= interrupt ? DWC3_DEPCMD_CMDIOC : 0; + cmd |= DWC3_DEPCMD_PARAM(dep->resource_index); + memset(¶ms, 0, sizeof(params)); + ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms); + /* + * If the End Transfer command was timed out while the device is + * not in SETUP phase, it's possible that an incoming Setup packet + * may prevent the command's completion. Let's retry when the + * ep0state returns to EP0_SETUP_PHASE. + */ + if (ret == -ETIMEDOUT && dep->dwc->ep0state != EP0_SETUP_PHASE) { + dep->flags |= DWC3_EP_DELAY_STOP; + return 0; + } + WARN_ON_ONCE(ret); + dep->resource_index = 0; + + if (!interrupt) + dep->flags &= ~DWC3_EP_TRANSFER_STARTED; + else if (!ret) + dep->flags |= DWC3_EP_END_TRANSFER_PENDING; + + dep->flags &= ~DWC3_EP_DELAY_STOP; + return ret; +} + +/** * dwc3_gadget_start_isoc_quirk - workaround invalid frame number * @dep: isoc endpoint * @@ -1235,7 +1645,7 @@ static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep) * Check if we can start isoc transfer on the next interval or * 4 uframes in the future with BIT[15:14] as dep->combo_num */ - test_frame_number = dep->frame_number & 0x3fff; + test_frame_number = dep->frame_number & DWC3_FRNUMBER_MASK; test_frame_number |= dep->combo_num << 14; test_frame_number += max_t(u32, 4, dep->interval); @@ -1282,7 +1692,7 @@ static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep) else if (test0 && test1) dep->combo_num = 0; - dep->frame_number &= 0x3fff; + dep->frame_number &= DWC3_FRNUMBER_MASK; dep->frame_number |= dep->combo_num << 14; dep->frame_number += max_t(u32, 4, dep->interval); @@ -1295,63 +1705,110 @@ static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep) static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep) { + const struct usb_endpoint_descriptor *desc = dep->endpoint.desc; struct dwc3 *dwc = dep->dwc; int ret; int i; - if (list_empty(&dep->pending_list)) { + if (list_empty(&dep->pending_list) && + list_empty(&dep->started_list)) { dep->flags |= DWC3_EP_PENDING_REQUEST; return -EAGAIN; } - if (!dwc->dis_start_transfer_quirk && dwc3_is_usb31(dwc) && - (dwc->revision <= DWC3_USB31_REVISION_160A || - (dwc->revision == DWC3_USB31_REVISION_170A && - dwc->version_type >= DWC31_VERSIONTYPE_EA01 && - dwc->version_type <= DWC31_VERSIONTYPE_EA06))) { - - if (dwc->gadget.speed <= USB_SPEED_HIGH && dep->direction) + if (!dwc->dis_start_transfer_quirk && + (DWC3_VER_IS_PRIOR(DWC31, 170A) || + DWC3_VER_TYPE_IS_WITHIN(DWC31, 170A, EA01, EA06))) { + if (dwc->gadget->speed <= USB_SPEED_HIGH && dep->direction) return dwc3_gadget_start_isoc_quirk(dep); } + if (desc->bInterval <= 14 && + dwc->gadget->speed >= USB_SPEED_HIGH) { + u32 frame = __dwc3_gadget_get_frame(dwc); + bool rollover = frame < + (dep->frame_number & DWC3_FRNUMBER_MASK); + + /* + * frame_number is set from XferNotReady and may be already + * out of date. DSTS only provides the lower 14 bit of the + * current frame number. So add the upper two bits of + * frame_number and handle a possible rollover. + * This will provide the correct frame_number unless more than + * rollover has happened since XferNotReady. + */ + + dep->frame_number = (dep->frame_number & ~DWC3_FRNUMBER_MASK) | + frame; + if (rollover) + dep->frame_number += BIT(14); + } + for (i = 0; i < DWC3_ISOC_MAX_RETRIES; i++) { - dep->frame_number = DWC3_ALIGN_FRAME(dep, i + 1); + int future_interval = i + 1; + + /* Give the controller at least 500us to schedule transfers */ + if (desc->bInterval < 3) + future_interval += 3 - desc->bInterval; + + dep->frame_number = DWC3_ALIGN_FRAME(dep, future_interval); ret = __dwc3_gadget_kick_transfer(dep); if (ret != -EAGAIN) break; } + /* + * After a number of unsuccessful start attempts due to bus-expiry + * status, issue END_TRANSFER command and retry on the next XferNotReady + * event. + */ + if (ret == -EAGAIN) + ret = __dwc3_stop_active_transfer(dep, false, true); + return ret; } static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) { - struct dwc3 *dwc = dep->dwc; + struct dwc3 *dwc = dep->dwc; - if (!dep->endpoint.desc) { - dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n", + if (!dep->endpoint.desc || !dwc->pullups_connected || !dwc->connected) { + dev_dbg(dwc->dev, "%s: can't queue to disabled endpoint\n", dep->name); return -ESHUTDOWN; } - if (req->dep != dep) { - WARN(true, "request %p belongs to '%s'\n", - &req->request, req->dep->name); + if (WARN(req->dep != dep, "request %pK belongs to '%s'\n", + &req->request, req->dep->name)) return -EINVAL; - } - if (req->status < DWC3_REQUEST_STATUS_COMPLETED) { - WARN(true, "request %p already in flight\n", &req->request); + if (WARN(req->status < DWC3_REQUEST_STATUS_COMPLETED, + "%s: request %pK already in flight\n", + dep->name, &req->request)) return -EINVAL; - } - req->request.actual = 0; - req->request.status = -EINPROGRESS; + req->request.actual = 0; + req->request.status = -EINPROGRESS; list_add_tail(&req->list, &dep->pending_list); req->status = DWC3_REQUEST_STATUS_QUEUED; + if (dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE) + return 0; + + /* + * Start the transfer only after the END_TRANSFER is completed + * and endpoint STALL is cleared. + */ + if ((dep->flags & DWC3_EP_END_TRANSFER_PENDING) || + (dep->flags & DWC3_EP_WEDGE) || + (dep->flags & DWC3_EP_DELAY_STOP) || + (dep->flags & DWC3_EP_STALL)) { + dep->flags |= DWC3_EP_DELAY_START; + return 0; + } + /* * NOTICE: Isochronous endpoints should NEVER be prestarted. We must * wait for a XferNotReady event so we will know what's the current @@ -1361,28 +1818,27 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) * errors which will force us issue EndTransfer command. */ if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { - if (!(dep->flags & DWC3_EP_PENDING_REQUEST) && - !(dep->flags & DWC3_EP_TRANSFER_STARTED)) - return 0; - - if ((dep->flags & DWC3_EP_PENDING_REQUEST)) { - if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) { + if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) { + if ((dep->flags & DWC3_EP_PENDING_REQUEST)) return __dwc3_gadget_start_isoc(dep); - } + + return 0; } } - return __dwc3_gadget_kick_transfer(dep); + __dwc3_gadget_kick_transfer(dep); + + return 0; } static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request) { - struct dwc3_request *req = to_dwc3_request(request); - struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3_request *req = to_dwc3_request(request); + struct dwc3_ep *dep = to_dwc3_ep(ep); - unsigned long flags; + unsigned long flags; - int ret; + int ret; spin_lock_irqsave(&dwc->lock, flags); ret = __dwc3_gadget_ep_queue(dep, req); @@ -1391,11 +1847,14 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request) return ret; } -static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, - struct dwc3_request *req) +static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *req) { int i; + /* If req->trb is not set, then the request has not started */ + if (!req->trb) + return; + /* * If request was already started, this means we had to * stop the transfer. With that we also need to ignore @@ -1409,7 +1868,7 @@ static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, for (i = 0; i < req->num_trbs; i++) { struct dwc3_trb *trb; - trb = req->trb + i; + trb = &dep->trb_pool[dep->trb_dequeue]; trb->ctrl &= ~DWC3_TRB_CTRL_HWO; dwc3_ep_inc_deq(dep); } @@ -1419,61 +1878,87 @@ static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep) { - struct dwc3_request *req; - struct dwc3_request *tmp; + struct dwc3_request *req; + struct dwc3 *dwc = dep->dwc; - list_for_each_entry_safe(req, tmp, &dep->cancelled_list, list) { + while (!list_empty(&dep->cancelled_list)) { + req = next_request(&dep->cancelled_list); dwc3_gadget_ep_skip_trbs(dep, req); - dwc3_gadget_giveback(dep, req, -ECONNRESET); + switch (req->status) { + case DWC3_REQUEST_STATUS_DISCONNECTED: + dwc3_gadget_giveback(dep, req, -ESHUTDOWN); + break; + case DWC3_REQUEST_STATUS_DEQUEUED: + dwc3_gadget_giveback(dep, req, -ECONNRESET); + break; + case DWC3_REQUEST_STATUS_STALLED: + dwc3_gadget_giveback(dep, req, -EPIPE); + break; + default: + dev_err(dwc->dev, "request cancelled with wrong reason:%d\n", req->status); + dwc3_gadget_giveback(dep, req, -ECONNRESET); + break; + } + /* + * The endpoint is disabled, let the dwc3_remove_requests() + * handle the cleanup. + */ + if (!dep->endpoint.desc) + break; } } static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, - struct usb_request *request) + struct usb_request *request) { - struct dwc3_request *req = to_dwc3_request(request); - struct dwc3_request *r = NULL; - struct dwc3_ep *dep = to_dwc3_ep(ep); - struct dwc3 *dwc = dep->dwc; - unsigned long flags; - int ret = 0; + struct dwc3_request *req = to_dwc3_request(request); + struct dwc3_request *r = NULL; + + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + + unsigned long flags; + int ret = 0; spin_lock_irqsave(&dwc->lock, flags); - list_for_each_entry(r, &dep->pending_list, list) { + list_for_each_entry(r, &dep->cancelled_list, list) { if (r == req) - break; + goto out; } - if (r != req) { - list_for_each_entry(r, &dep->started_list, list) { - if (r == req) - break; + list_for_each_entry(r, &dep->pending_list, list) { + if (r == req) { + dwc3_gadget_giveback(dep, req, -ECONNRESET); + goto out; } + } + + list_for_each_entry(r, &dep->started_list, list) { if (r == req) { + struct dwc3_request *t; + /* wait until it is processed */ dwc3_stop_active_transfer(dep, true, true); - if (!r->trb) - goto out0; + /* + * Remove any started request if the transfer is + * cancelled. + */ + list_for_each_entry_safe(r, t, &dep->started_list, list) + dwc3_gadget_move_cancelled_request(r, + DWC3_REQUEST_STATUS_DEQUEUED); - dwc3_gadget_move_cancelled_request(req); - if (dep->flags & DWC3_EP_TRANSFER_STARTED) - goto out0; - else - goto out1; + dep->flags &= ~DWC3_EP_WAIT_TRANSFER_COMPLETE; + + goto out; } - dev_err(dwc->dev, "request %p was not queued to %s\n", - request, ep->name); - ret = -EINVAL; - goto out0; } -out1: - /* giveback the request */ - dwc3_gadget_giveback(dep, req, -ECONNRESET); - -out0: + dev_err(dwc->dev, "request %pK was not queued to %s\n", + request, ep->name); + ret = -EINVAL; +out: spin_unlock_irqrestore(&dwc->lock, flags); return ret; @@ -1481,9 +1966,11 @@ out0: int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) { - struct dwc3_gadget_ep_cmd_params params; - struct dwc3 *dwc = dep->dwc; - int ret; + struct dwc3_gadget_ep_cmd_params params; + struct dwc3 *dwc = dep->dwc; + struct dwc3_request *req; + struct dwc3_request *tmp; + int ret; if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { dev_err(dwc->dev, "%s is of Isochronous type\n", dep->name); @@ -1494,8 +1981,9 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) if (value) { struct dwc3_trb *trb; - unsigned transfer_in_flight; - unsigned started; + + unsigned int transfer_in_flight; + unsigned int started; if (dep->number > 1) trb = dwc3_ep_prev_trb(dep, dep->trb_enqueue); @@ -1511,19 +1999,53 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) } ret = dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETSTALL, - ¶ms); + ¶ms); if (ret) dev_err(dwc->dev, "failed to set STALL on %s\n", dep->name); else dep->flags |= DWC3_EP_STALL; } else { + /* + * Don't issue CLEAR_STALL command to control endpoints. The + * controller automatically clears the STALL when it receives + * the SETUP token. + */ + if (dep->number <= 1) { + dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE); + return 0; + } + + dwc3_stop_active_transfer(dep, true, true); + + list_for_each_entry_safe(req, tmp, &dep->started_list, list) + dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_STALLED); + + if (dep->flags & DWC3_EP_END_TRANSFER_PENDING || + (dep->flags & DWC3_EP_DELAY_STOP)) { + dep->flags |= DWC3_EP_PENDING_CLEAR_STALL; + if (protocol) + dwc->clear_stall_protocol = dep->number; + + return 0; + } + + dwc3_gadget_ep_cleanup_cancelled_requests(dep); + ret = dwc3_send_clear_stall_ep_cmd(dep); - if (ret) + if (ret) { dev_err(dwc->dev, "failed to clear STALL on %s\n", dep->name); - else - dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE); + return ret; + } + + dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE); + + if ((dep->flags & DWC3_EP_DELAY_START) && + !usb_endpoint_xfer_isoc(dep->endpoint.desc)) + __dwc3_gadget_kick_transfer(dep); + + dep->flags &= ~DWC3_EP_DELAY_START; } return ret; @@ -1531,10 +2053,11 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value) { - struct dwc3_ep *dep = to_dwc3_ep(ep); - unsigned long flags; + struct dwc3_ep *dep = to_dwc3_ep(ep); - int ret; + unsigned long flags; + + int ret; spin_lock_irqsave(&dwc->lock, flags); ret = __dwc3_gadget_ep_set_halt(dep, value, false); @@ -1545,9 +2068,9 @@ static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value) static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep) { - struct dwc3_ep *dep = to_dwc3_ep(ep); - unsigned long flags; - int ret; + struct dwc3_ep *dep = to_dwc3_ep(ep); + unsigned long flags; + int ret; spin_lock_irqsave(&dwc->lock, flags); dep->flags |= DWC3_EP_WEDGE; @@ -1564,13 +2087,13 @@ static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep) /* -------------------------------------------------------------------------- */ static struct usb_endpoint_descriptor dwc3_gadget_ep0_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, }; static const struct usb_ep_ops dwc3_gadget_ep0_ops = { - .enable = dwc3_gadget_ep0_enable, + .enable = dwc3_gadget_ep0_enable, .disable = dwc3_gadget_ep0_disable, .alloc_request = dwc3_gadget_ep_alloc_request, .free_request = dwc3_gadget_ep_free_request, @@ -1581,7 +2104,7 @@ static const struct usb_ep_ops dwc3_gadget_ep0_ops = { }; static const struct usb_ep_ops dwc3_gadget_ep_ops = { - .enable = dwc3_gadget_ep_enable, + .enable = dwc3_gadget_ep_enable, .disable = dwc3_gadget_ep_disable, .alloc_request = dwc3_gadget_ep_alloc_request, .free_request = dwc3_gadget_ep_free_request, @@ -1595,20 +2118,19 @@ static const struct usb_ep_ops dwc3_gadget_ep_ops = { static int dwc3_gadget_get_frame(struct usb_gadget *g) { - struct dwc3 *dwc = gadget_to_dwc(g); + struct dwc3 *dwc = gadget_to_dwc(g); return __dwc3_gadget_get_frame(dwc); } static int __dwc3_gadget_wakeup(struct dwc3 *dwc) { - int retries; + int retries; - int ret; - u32 reg; + int ret; + u32 reg; - u8 link_state; - u8 speed; + u8 link_state; /* * According to the Databook Remote wakeup request should @@ -1618,16 +2140,15 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc) */ reg = dwc3_readl(dwc->regs, DWC3_DSTS); - speed = reg & DWC3_DSTS_CONNECTSPD; - if ((speed == DWC3_DSTS_SUPERSPEED) || - (speed == DWC3_DSTS_SUPERSPEED_PLUS)) - return 0; - link_state = DWC3_DSTS_USBLNKST(reg); switch (link_state) { + case DWC3_LINK_STATE_RESET: case DWC3_LINK_STATE_RX_DET: /* in HS, means Early Suspend */ case DWC3_LINK_STATE_U3: /* in HS, means SUSPEND */ + case DWC3_LINK_STATE_U2: /* in HS, means Sleep (L1) */ + case DWC3_LINK_STATE_U1: + case DWC3_LINK_STATE_RESUME: break; default: return -EINVAL; @@ -1640,7 +2161,7 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc) } /* Recent versions do this automatically */ - if (dwc->revision < DWC3_REVISION_194A) { + if (DWC3_VER_IS_PRIOR(DWC3, 194A)) { /* write zeroes to Link Change Request */ reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; @@ -1668,9 +2189,9 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc) static int dwc3_gadget_wakeup(struct usb_gadget *g) { - struct dwc3 *dwc = gadget_to_dwc(g); - unsigned long flags; - int ret; + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; + int ret; spin_lock_irqsave(&dwc->lock, flags); ret = __dwc3_gadget_wakeup(dwc); @@ -1680,37 +2201,145 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g) } static int dwc3_gadget_set_selfpowered(struct usb_gadget *g, - int is_selfpowered) + int is_selfpowered) { - struct dwc3 *dwc = gadget_to_dwc(g); - unsigned long flags; + unsigned long flags; spin_lock_irqsave(&dwc->lock, flags); - dwc->is_selfpowered = !!is_selfpowered; + g->is_selfpowered = !!is_selfpowered; spin_unlock_irqrestore(&dwc->lock, flags); return 0; } +static void dwc3_stop_active_transfers(struct dwc3 *dwc) +{ + u32 epnum; + + for (epnum = 2; epnum < dwc->num_eps; epnum++) { + struct dwc3_ep *dep; + + dep = dwc->eps[epnum]; + if (!dep) + continue; + + dwc3_remove_requests(dwc, dep, -ESHUTDOWN); + } +} + +static void __dwc3_gadget_set_ssp_rate(struct dwc3 *dwc) +{ + enum usb_ssp_rate ssp_rate = dwc->gadget_ssp_rate; + u32 reg; + + if (ssp_rate == USB_SSP_GEN_UNKNOWN) + ssp_rate = dwc->max_ssp_rate; + + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg &= ~DWC3_DCFG_SPEED_MASK; + reg &= ~DWC3_DCFG_NUMLANES(~0); + + if (ssp_rate == USB_SSP_GEN_1x2) + reg |= DWC3_DCFG_SUPERSPEED; + else if (dwc->max_ssp_rate != USB_SSP_GEN_1x2) + reg |= DWC3_DCFG_SUPERSPEED_PLUS; + + if (ssp_rate != USB_SSP_GEN_2x1 && + dwc->max_ssp_rate != USB_SSP_GEN_2x1) + reg |= DWC3_DCFG_NUMLANES(1); + + dwc3_writel(dwc->regs, DWC3_DCFG, reg); +} + +static void __dwc3_gadget_set_speed(struct dwc3 *dwc) +{ + enum usb_device_speed speed; + u32 reg; + + speed = dwc->gadget_max_speed; + if (speed == USB_SPEED_UNKNOWN || speed > dwc->maximum_speed) + speed = dwc->maximum_speed; + + if (speed == USB_SPEED_SUPER_PLUS && + DWC3_IP_IS(DWC32)) { + __dwc3_gadget_set_ssp_rate(dwc); + return; + } + + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg &= ~(DWC3_DCFG_SPEED_MASK); + + /* + * WORKAROUND: DWC3 revision < 2.20a have an issue + * which would cause metastability state on Run/Stop + * bit if we try to force the IP to USB2-only mode. + * + * Because of that, we cannot configure the IP to any + * speed other than the SuperSpeed + * + * Refers to: + * + * STAR#9000525659: Clock Domain Crossing on DCTL in + * USB 2.0 Mode + */ + if (DWC3_VER_IS_PRIOR(DWC3, 220A) && + !dwc->dis_metastability_quirk) { + reg |= DWC3_DCFG_SUPERSPEED; + } else { + switch (speed) { + case USB_SPEED_FULL: + reg |= DWC3_DCFG_FULLSPEED; + break; + case USB_SPEED_HIGH: + reg |= DWC3_DCFG_HIGHSPEED; + break; + case USB_SPEED_SUPER: + reg |= DWC3_DCFG_SUPERSPEED; + break; + case USB_SPEED_SUPER_PLUS: + if (DWC3_IP_IS(DWC3)) + reg |= DWC3_DCFG_SUPERSPEED; + else + reg |= DWC3_DCFG_SUPERSPEED_PLUS; + break; + default: + dev_err(dwc->dev, "invalid speed (%d)\n", speed); + + if (DWC3_IP_IS(DWC3)) + reg |= DWC3_DCFG_SUPERSPEED; + else + reg |= DWC3_DCFG_SUPERSPEED_PLUS; + } + } + + if (DWC3_IP_IS(DWC32) && + speed > USB_SPEED_UNKNOWN && + speed < USB_SPEED_SUPER_PLUS) + reg &= ~DWC3_DCFG_NUMLANES(~0); + + dwc3_writel(dwc->regs, DWC3_DCFG, reg); +} + static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) { - u32 reg; - u32 timeout = 500; + u32 reg; + u32 timeout = 2000; reg = dwc3_readl(dwc->regs, DWC3_DCTL); if (is_on) { - if (dwc->revision <= DWC3_REVISION_187A) { + if (DWC3_VER_IS_WITHIN(DWC3, ANY, 187A)) { reg &= ~DWC3_DCTL_TRGTULST_MASK; reg |= DWC3_DCTL_TRGTULST_RX_DET; } - if (dwc->revision >= DWC3_REVISION_194A) + if (!DWC3_VER_IS_PRIOR(DWC3, 194A)) reg &= ~DWC3_DCTL_KEEP_CONNECT; reg |= DWC3_DCTL_RUN_STOP; if (dwc->has_hibernation) reg |= DWC3_DCTL_KEEP_CONNECT; + __dwc3_gadget_set_speed(dwc); dwc->pullups_connected = true; } else { reg &= ~DWC3_DCTL_RUN_STOP; @@ -1721,9 +2350,10 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) dwc->pullups_connected = false; } - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_gadget_dctl_write_safe(dwc, reg); do { + udelay(1000); reg = dwc3_readl(dwc->regs, DWC3_DSTS); reg &= DWC3_DSTS_DEVCTRLHLT; } while (--timeout && !(!is_on ^ !reg)); @@ -1731,54 +2361,104 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) if (!timeout) return -ETIMEDOUT; - dev_dbg(dwc->dev, "gadget %s data soft-%s\n", - dwc->gadget_driver - ? dwc->gadget_driver->function : "no-function", - is_on ? "connect" : "disconnect"); - return 0; } -static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) +static void dwc3_gadget_disable_irq(struct dwc3 *dwc); +static void __dwc3_gadget_stop(struct dwc3 *dwc); +static int __dwc3_gadget_start(struct dwc3 *dwc); + +static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc) { - struct dwc3 *dwc = gadget_to_dwc(g); unsigned long flags; - int ret; - is_on = !!is_on; + spin_lock_irqsave(&dwc->lock, flags); + dwc->connected = false; /* * Per databook, when we want to stop the gadget, if a control transfer * is still in process, complete it and get the core into setup phase. */ - if (!is_on && dwc->ep0state != EP0_SETUP_PHASE) - dev_warn(dwc->dev, "not in SETUP phase\n"); + if (dwc->ep0state != EP0_SETUP_PHASE) { + /* + * Original Linux code waits for ep0state bein in setup + * phase here using completions. Completions are not properly + * implemented in barebox, hence the code is skipped here. I wasn't + * able to trigger this code in barebox, but if you hit this compare + * it with Linux code and implement it here. + */ + dev_warn(dwc->dev, "%s: unexpected state\n", __func__); + } - spin_lock_irqsave(&dwc->lock, flags); - ret = dwc3_gadget_run_stop(dwc, is_on, false); + /* + * In the Synopsys DesignWare Cores USB3 Databook Rev. 3.30a + * Section 4.1.8 Table 4-7, it states that for a device-initiated + * disconnect, the SW needs to ensure that it sends "a DEPENDXFER + * command for any active transfers" before clearing the RunStop + * bit. + */ + dwc3_stop_active_transfers(dwc); + __dwc3_gadget_stop(dwc); spin_unlock_irqrestore(&dwc->lock, flags); + /* + * Note: if the GEVNTCOUNT indicates events in the event buffer, the + * driver needs to acknowledge them before the controller can halt. + * Simply let the interrupt handler acknowledges and handle the + * remaining event generated by the controller while polling for + * DSTS.DEVCTLHLT. + */ + return dwc3_gadget_run_stop(dwc, false, false); +} + +static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + int ret; + + is_on = !!is_on; + + dwc->softconnect = is_on; + + if (!is_on) { + ret = dwc3_gadget_soft_disconnect(dwc); + } else { + /* + * In the Synopsys DWC_usb31 1.90a programming guide section + * 4.1.9, it specifies that for a reconnect after a + * device-initiated disconnect requires a core soft reset + * (DCTL.CSftRst) before enabling the run/stop bit. + */ + dwc3_core_soft_reset(dwc); + + dwc3_event_buffers_setup(dwc); + __dwc3_gadget_start(dwc); + ret = dwc3_gadget_run_stop(dwc, true, false); + } + return ret; } static void dwc3_gadget_enable_irq(struct dwc3 *dwc) { - u32 reg; + u32 reg; /* Enable all but Start and End of Frame IRQs */ - reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN | - DWC3_DEVTEN_EVNTOVERFLOWEN | - DWC3_DEVTEN_CMDCMPLTEN | - DWC3_DEVTEN_ERRTICERREN | - DWC3_DEVTEN_WKUPEVTEN | - DWC3_DEVTEN_ULSTCNGEN | - DWC3_DEVTEN_CONNECTDONEEN | - DWC3_DEVTEN_USBRSTEN | - DWC3_DEVTEN_DISCONNEVTEN); - - if (dwc->revision < DWC3_REVISION_250A) + reg = (DWC3_DEVTEN_EVNTOVERFLOWEN | + DWC3_DEVTEN_CMDCMPLTEN | + DWC3_DEVTEN_ERRTICERREN | + DWC3_DEVTEN_WKUPEVTEN | + DWC3_DEVTEN_CONNECTDONEEN | + DWC3_DEVTEN_USBRSTEN | + DWC3_DEVTEN_DISCONNEVTEN); + + if (DWC3_VER_IS_PRIOR(DWC3, 250A)) reg |= DWC3_DEVTEN_ULSTCNGEN; + /* On 2.30a and above this bit enables U3/L2-L1 Suspend Events */ + if (!DWC3_VER_IS_PRIOR(DWC3, 230A)) + reg |= DWC3_DEVTEN_U3L2L1SUSPEN; + dwc3_writel(dwc->regs, DWC3_DEVTEN, reg); } @@ -1817,7 +2497,7 @@ static void dwc3_gadget_setup_nump(struct dwc3 *dwc) u32 reg; ram2_depth = DWC3_GHWPARAMS7_RAM2_DEPTH(dwc->hwparams.hwparams7); - mdwidth = DWC3_GHWPARAMS0_MDWIDTH(dwc->hwparams.hwparams0); + mdwidth = dwc3_mdwidth(dwc); nump = ((ram2_depth * mdwidth / 8) - 24 - 16) / 1024; nump = min_t(u32, nump, 16); @@ -1831,9 +2511,20 @@ static void dwc3_gadget_setup_nump(struct dwc3 *dwc) static int __dwc3_gadget_start(struct dwc3 *dwc) { - struct dwc3_ep *dep; - int ret = 0; - u32 reg; + struct dwc3_ep *dep; + int ret = 0; + u32 reg; + + /* + * Use IMOD if enabled via dwc->imod_interval. Otherwise, if + * the core supports IMOD, disable it. + */ + if (dwc->imod_interval) { + dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval); + dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB); + } else if (dwc3_has_imod(dwc)) { + dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), 0); + } /* * We are telling dwc3 that we want to use DCFG.NUMP as ACK TP's NUMP @@ -1843,19 +2534,38 @@ static int __dwc3_gadget_start(struct dwc3 *dwc) * bursts of data without going through any sort of endpoint throttling. */ reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG); - if (dwc3_is_usb31(dwc)) - reg &= ~DWC31_GRXTHRCFG_PKTCNTSEL; - else + if (DWC3_IP_IS(DWC3)) reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL; + else + reg &= ~DWC31_GRXTHRCFG_PKTCNTSEL; dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg); dwc3_gadget_setup_nump(dwc); + /* + * Currently the controller handles single stream only. So, Ignore + * Packet Pending bit for stream selection and don't search for another + * stream if the host sends Data Packet with PP=0 (for OUT direction) or + * ACK with NumP=0 and PP=0 (for IN direction). This slightly improves + * the stream performance. + */ + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg |= DWC3_DCFG_IGNSTRMPP; + dwc3_writel(dwc->regs, DWC3_DCFG, reg); + + /* Enable MST by default if the device is capable of MST */ + if (DWC3_MST_CAPABLE(&dwc->hwparams)) { + reg = dwc3_readl(dwc->regs, DWC3_DCFG1); + reg &= ~DWC3_DCFG1_DIS_MST_ENH; + dwc3_writel(dwc->regs, DWC3_DCFG1, reg); + } + /* Start with SuperSpeed Default */ dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); dep = dwc->eps[0]; + dep->flags = 0; ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); @@ -1863,6 +2573,7 @@ static int __dwc3_gadget_start(struct dwc3 *dwc) } dep = dwc->eps[1]; + dep->flags = 0; ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); @@ -1871,7 +2582,9 @@ static int __dwc3_gadget_start(struct dwc3 *dwc) /* begin to receive SETUP packets */ dwc->ep0state = EP0_SETUP_PHASE; + dwc->ep0_bounced = false; dwc->link_state = DWC3_LINK_STATE_SS_DIS; + dwc->delayed_status = false; dwc3_ep0_out_start(dwc); dwc3_gadget_enable_irq(dwc); @@ -1888,109 +2601,163 @@ err0: static int dwc3_gadget_start(struct usb_gadget *g, struct usb_gadget_driver *driver) { - struct dwc3 *dwc = gadget_to_dwc(g); - unsigned long flags; - int ret = 0; + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; - //dwc3_gadget_wakeup(g); spin_lock_irqsave(&dwc->lock, flags); - if (dwc->gadget_driver) { - dev_err(dwc->dev, "%s is already bound to %s\n", - dwc->gadget.name, - dwc->gadget_driver->function); - ret = -EBUSY; - goto err1; - } + dwc->gadget_driver = driver; + spin_unlock_irqrestore(&dwc->lock, flags); - dwc->gadget_driver = driver; + return 0; +} - __dwc3_gadget_start(dwc); +static void __dwc3_gadget_stop(struct dwc3 *dwc) +{ + dwc3_gadget_disable_irq(dwc); + __dwc3_gadget_ep_disable(dwc->eps[0]); + __dwc3_gadget_ep_disable(dwc->eps[1]); +} +static int dwc3_gadget_stop(struct usb_gadget *g) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + dwc->gadget_driver = NULL; + dwc->max_cfg_eps = 0; spin_unlock_irqrestore(&dwc->lock, flags); return 0; +} -err1: - spin_unlock_irqrestore(&dwc->lock, flags); +static void dwc3_gadget_config_params(struct usb_gadget *g, + struct usb_dcd_config_params *params) +{ + struct dwc3 *dwc = gadget_to_dwc(g); - return ret; + params->besl_baseline = USB_DEFAULT_BESL_UNSPECIFIED; + params->besl_deep = USB_DEFAULT_BESL_UNSPECIFIED; + + /* Recommended BESL */ + if (!dwc->dis_enblslpm_quirk) { + /* + * If the recommended BESL baseline is 0 or if the BESL deep is + * less than 2, Microsoft's Windows 10 host usb stack will issue + * a usb reset immediately after it receives the extended BOS + * descriptor and the enumeration will fail. To maintain + * compatibility with the Windows' usb stack, let's set the + * recommended BESL baseline to 1 and clamp the BESL deep to be + * within 2 to 15. + */ + params->besl_baseline = 1; + if (dwc->is_utmi_l1_suspend) + params->besl_deep = + clamp_t(u8, dwc->hird_threshold, 2, 15); + } + + /* U1 Device exit Latency */ + if (dwc->dis_u1_entry_quirk) + params->bU1devExitLat = 0; + else + params->bU1devExitLat = DWC3_DEFAULT_U1_DEV_EXIT_LAT; + + /* U2 Device exit Latency */ + if (dwc->dis_u2_entry_quirk) + params->bU2DevExitLat = 0; + else + params->bU2DevExitLat = + cpu_to_le16(DWC3_DEFAULT_U2_DEV_EXIT_LAT); } -static int dwc3_gadget_stop(struct usb_gadget *g, - struct usb_gadget_driver *driver) +static void dwc3_gadget_set_speed(struct usb_gadget *g, + enum usb_device_speed speed) { - struct dwc3 *dwc = gadget_to_dwc(g); - unsigned long flags; + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; spin_lock_irqsave(&dwc->lock, flags); + dwc->gadget_max_speed = speed; + spin_unlock_irqrestore(&dwc->lock, flags); +} - dwc3_gadget_disable_irq(dwc); - __dwc3_gadget_ep_disable(dwc->eps[0]); - __dwc3_gadget_ep_disable(dwc->eps[1]); +static void dwc3_gadget_set_ssp_rate(struct usb_gadget *g, + enum usb_ssp_rate rate) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; - dwc->gadget_driver = NULL; + spin_lock_irqsave(&dwc->lock, flags); + dwc->gadget_max_speed = USB_SPEED_SUPER_PLUS; + dwc->gadget_ssp_rate = rate; spin_unlock_irqrestore(&dwc->lock, flags); +} + +static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + + if (dwc->usb2_phy) + return usb_phy_set_power(dwc->usb2_phy, mA); + + if (!dwc->usb_psy) + return -EOPNOTSUPP; return 0; } -static void dwc3_gadget_set_speed(struct dwc3 *dwc, - enum usb_device_speed speed) +/** + * dwc3_gadget_check_config - ensure dwc3 can support the USB configuration + * @g: pointer to the USB gadget + * + * Used to record the maximum number of endpoints being used in a USB composite + * device. (across all configurations) This is to be used in the calculation + * of the TXFIFO sizes when resizing internal memory for individual endpoints. + * It will help ensured that the resizing logic reserves enough space for at + * least one max packet. + */ +static int dwc3_gadget_check_config(struct usb_gadget *g) { - unsigned long flags; - u32 reg; - - spin_lock_irqsave(&dwc->lock, flags); - reg = dwc3_readl(dwc->regs, DWC3_DCFG); - reg &= ~(DWC3_DCFG_SPEED_MASK); + struct dwc3 *dwc = gadget_to_dwc(g); + struct usb_ep *ep; + int fifo_size = 0; + int ram1_depth; + int ep_num = 0; - /* - * WORKAROUND: DWC3 revision < 2.20a have an issue - * which would cause metastability state on Run/Stop - * bit if we try to force the IP to USB2-only mode. - * - * Because of that, we cannot configure the IP to any - * speed other than the SuperSpeed - * - * Refers to: - * - * STAR#9000525659: Clock Domain Crossing on DCTL in - * USB 2.0 Mode - */ - if (dwc->revision < DWC3_REVISION_220A && - !dwc->dis_metastability_quirk) { - reg |= DWC3_DCFG_SUPERSPEED; - } else { - switch (speed) { - case USB_SPEED_LOW: - reg |= DWC3_DCFG_LOWSPEED; - break; - case USB_SPEED_FULL: - reg |= DWC3_DCFG_FULLSPEED; - break; - case USB_SPEED_HIGH: - reg |= DWC3_DCFG_HIGHSPEED; - break; - case USB_SPEED_SUPER: - reg |= DWC3_DCFG_SUPERSPEED; - break; - case USB_SPEED_SUPER_PLUS: - if (dwc3_is_usb31(dwc)) - reg |= DWC3_DCFG_SUPERSPEED_PLUS; - else - reg |= DWC3_DCFG_SUPERSPEED; - break; - default: - dev_err(dwc->dev, "invalid speed (%d)\n", speed); + if (!dwc->do_fifo_resize) + return 0; - if (dwc->revision & DWC3_REVISION_IS_DWC31) - reg |= DWC3_DCFG_SUPERSPEED_PLUS; - else - reg |= DWC3_DCFG_SUPERSPEED; - } + list_for_each_entry(ep, &g->ep_list, ep_list) { + /* Only interested in the IN endpoints */ + if (ep->claimed && (ep->address & USB_DIR_IN)) + ep_num++; } - dwc3_writel(dwc->regs, DWC3_DCFG, reg); + if (ep_num <= dwc->max_cfg_eps) + return 0; + + /* Update the max number of eps in the composition */ + dwc->max_cfg_eps = ep_num; + + fifo_size = dwc3_gadget_calc_tx_fifo_size(dwc, dwc->max_cfg_eps); + /* Based on the equation, increment by one for every ep */ + fifo_size += dwc->max_cfg_eps; + + /* Check if we can fit a single fifo per endpoint */ + ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7); + if (fifo_size > ram1_depth) + return -ENOMEM; + + return 0; +} + +static void dwc3_gadget_async_callbacks(struct usb_gadget *g, bool enable) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + dwc->async_callbacks = enable; spin_unlock_irqrestore(&dwc->lock, flags); } @@ -1998,12 +2765,18 @@ static void dwc3_gadget_poll(struct usb_gadget *g); static const struct usb_gadget_ops dwc3_gadget_ops = { .get_frame = dwc3_gadget_get_frame, - .wakeup = dwc3_gadget_wakeup, + .wakeup = dwc3_gadget_wakeup, .set_selfpowered = dwc3_gadget_set_selfpowered, - .pullup = dwc3_gadget_pullup, + .pullup = dwc3_gadget_pullup, .udc_start = dwc3_gadget_start, .udc_stop = dwc3_gadget_stop, .udc_poll = dwc3_gadget_poll, + .udc_set_speed = dwc3_gadget_set_speed, + .udc_set_ssp_rate = dwc3_gadget_set_ssp_rate, + .get_config_params = dwc3_gadget_config_params, + .vbus_draw = dwc3_gadget_vbus_draw, + .check_config = dwc3_gadget_check_config, + .udc_async_callbacks = dwc3_gadget_async_callbacks, }; /* -------------------------------------------------------------------------- */ @@ -2016,7 +2789,9 @@ static int dwc3_gadget_init_control_endpoint(struct dwc3_ep *dep) dep->endpoint.maxburst = 1; dep->endpoint.ops = &dwc3_gadget_ep0_ops; if (!dep->direction) - dwc->gadget.ep0 = &dep->endpoint; + dwc->gadget->ep0 = &dep->endpoint; + + dep->endpoint.caps.type_control = true; return 0; } @@ -2024,41 +2799,48 @@ static int dwc3_gadget_init_control_endpoint(struct dwc3_ep *dep) static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep) { struct dwc3 *dwc = dep->dwc; - int mdwidth; - int kbytes; + u32 mdwidth; int size; + int maxpacket; + + mdwidth = dwc3_mdwidth(dwc); - mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0); /* MDWIDTH is represented in bits, we need it in bytes */ mdwidth /= 8; size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1)); - if (dwc3_is_usb31(dwc)) - size = DWC31_GTXFIFOSIZ_TXFDEF(size); + if (DWC3_IP_IS(DWC3)) + size = DWC3_GTXFIFOSIZ_TXFDEP(size); else - size = DWC3_GTXFIFOSIZ_TXFDEF(size); - - /* FIFO Depth is in MDWDITH bytes. Multiply */ - size *= mdwidth; - - kbytes = size / 1024; - if (kbytes == 0) - kbytes = 1; + size = DWC31_GTXFIFOSIZ_TXFDEP(size); /* - * FIFO sizes account an extra MDWIDTH * (kbytes + 1) bytes for - * internal overhead. We don't really know how these are used, - * but documentation say it exists. + * maxpacket size is determined as part of the following, after assuming + * a mult value of one maxpacket: + * DWC3 revision 280A and prior: + * fifo_size = mult * (max_packet / mdwidth) + 1; + * maxpacket = mdwidth * (fifo_size - 1); + * + * DWC3 revision 290A and onwards: + * fifo_size = mult * ((max_packet + mdwidth)/mdwidth + 1) + 1 + * maxpacket = mdwidth * ((fifo_size - 1) - 1) - mdwidth; */ - size -= mdwidth * (kbytes + 1); - size /= kbytes; + if (DWC3_VER_IS_PRIOR(DWC3, 290A)) + maxpacket = mdwidth * (size - 1); + else + maxpacket = mdwidth * ((size - 1) - 1) - mdwidth; + /* Functionally, space for one max packet is sufficient */ + size = min_t(int, maxpacket, 1024); usb_ep_set_maxpacket_limit(&dep->endpoint, size); - dep->endpoint.max_streams = 15; + dep->endpoint.max_streams = 16; dep->endpoint.ops = &dwc3_gadget_ep_ops; list_add_tail(&dep->endpoint.ep_list, - &dwc->gadget.ep_list); + &dwc->gadget->ep_list); + dep->endpoint.caps.type_iso = true; + dep->endpoint.caps.type_bulk = true; + dep->endpoint.caps.type_int = true; return dwc3_alloc_trb_pool(dep); } @@ -2066,22 +2848,56 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep) static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep) { struct dwc3 *dwc = dep->dwc; + u32 mdwidth; + int size; + + mdwidth = dwc3_mdwidth(dwc); + + /* MDWIDTH is represented in bits, convert to bytes */ + mdwidth /= 8; + + /* All OUT endpoints share a single RxFIFO space */ + size = dwc3_readl(dwc->regs, DWC3_GRXFIFOSIZ(0)); + if (DWC3_IP_IS(DWC3)) + size = DWC3_GRXFIFOSIZ_RXFDEP(size); + else + size = DWC31_GRXFIFOSIZ_RXFDEP(size); + + /* FIFO depth is in MDWDITH bytes */ + size *= mdwidth; + + /* + * To meet performance requirement, a minimum recommended RxFIFO size + * is defined as follow: + * RxFIFO size >= (3 x MaxPacketSize) + + * (3 x 8 bytes setup packets size) + (16 bytes clock crossing margin) + * + * Then calculate the max packet limit as below. + */ + size -= (3 * 8) + 16; + if (size < 0) + size = 0; + else + size /= 3; - usb_ep_set_maxpacket_limit(&dep->endpoint, 1024); - dep->endpoint.max_streams = 15; + usb_ep_set_maxpacket_limit(&dep->endpoint, size); + dep->endpoint.max_streams = 16; dep->endpoint.ops = &dwc3_gadget_ep_ops; list_add_tail(&dep->endpoint.ep_list, - &dwc->gadget.ep_list); + &dwc->gadget->ep_list); + dep->endpoint.caps.type_iso = true; + dep->endpoint.caps.type_bulk = true; + dep->endpoint.caps.type_int = true; return dwc3_alloc_trb_pool(dep); } static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum) { - struct dwc3_ep *dep; - bool direction = epnum & 1; - int ret; - u8 num = epnum >> 1; + struct dwc3_ep *dep; + bool direction = epnum & 1; + int ret; + u8 num = epnum >> 1; dep = kzalloc(sizeof(*dep), GFP_KERNEL); if (!dep) @@ -2096,7 +2912,7 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum) dep->start_cmd_status = 0; snprintf(dep->name, sizeof(dep->name), "ep%u%s", num, - direction ? "in" : "out"); + direction ? "in" : "out"); dep->endpoint.name = dep->name; @@ -2105,8 +2921,6 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum) dep->endpoint.comp_desc = NULL; } - spin_lock_init(&dep->lock); - if (num == 0) ret = dwc3_gadget_init_control_endpoint(dep); else if (direction) @@ -2117,21 +2931,26 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum) if (ret) return ret; + dep->endpoint.caps.dir_in = direction; + dep->endpoint.caps.dir_out = !direction; + INIT_LIST_HEAD(&dep->pending_list); INIT_LIST_HEAD(&dep->started_list); INIT_LIST_HEAD(&dep->cancelled_list); + dwc3_debugfs_create_endpoint_dir(dep); + return 0; } static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total) { - u8 epnum; + u8 epnum; - INIT_LIST_HEAD(&dwc->gadget.ep_list); + INIT_LIST_HEAD(&dwc->gadget->ep_list); for (epnum = 0; epnum < total; epnum++) { - int ret; + int ret; ret = dwc3_gadget_init_endpoint(dwc, epnum); if (ret) @@ -2143,8 +2962,8 @@ static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total) static void dwc3_gadget_free_endpoints(struct dwc3 *dwc) { - struct dwc3_ep *dep; - u8 epnum; + struct dwc3_ep *dep; + u8 epnum; for (epnum = 0; epnum < DWC3_ENDPOINTS_NUM; epnum++) { dep = dwc->eps[epnum]; @@ -2164,6 +2983,7 @@ static void dwc3_gadget_free_endpoints(struct dwc3 *dwc) list_del(&dep->endpoint.ep_list); } + dwc3_debugfs_remove_endpoint_dir(dep); kfree(dep); } } @@ -2171,12 +2991,10 @@ static void dwc3_gadget_free_endpoints(struct dwc3 *dwc) /* -------------------------------------------------------------------------- */ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep, - struct dwc3_request *req, - struct dwc3_trb *trb, - const struct dwc3_event_depevt *event, - int status, int chain) + struct dwc3_request *req, struct dwc3_trb *trb, + const struct dwc3_event_depevt *event, int status, int chain) { - unsigned int count; + unsigned int count; dwc3_ep_inc_deq(dep); @@ -2205,15 +3023,16 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep, frame_number = DWC3_TRB_CTRL_GET_SID_SOFN(trb->ctrl); frame_number &= ~(dep->interval - 1); + req->request.frame_number = frame_number; } /* - * If we're dealing with unaligned size OUT transfer, we will be left - * with one TRB pending in the ring. We need to manually clear HWO bit - * from that TRB. + * We use bounce buffer for requests that needs extra TRB or OUT ZLP. If + * this TRB points to the bounce buffer address, it's a MPS alignment + * TRB. Don't add it to req->remaining calculation. */ - - if (req->needs_extra_trb && !(trb->ctrl & DWC3_TRB_CTRL_CHN)) { + if (trb->bpl == lower_32_bits(dep->dwc->bounce_addr) && + trb->bph == upper_32_bits(dep->dwc->bounce_addr)) { trb->ctrl &= ~DWC3_TRB_CTRL_HWO; return 1; } @@ -2227,16 +3046,20 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep, if (event->status & DEPEVT_STATUS_SHORT && !chain) return 1; - if (event->status & DEPEVT_STATUS_IOC) + if ((trb->ctrl & DWC3_TRB_CTRL_ISP_IMI) && + DWC3_TRB_SIZE_TRBSTS(trb->size) == DWC3_TRBSTS_MISSED_ISOC) + return 1; + + if ((trb->ctrl & DWC3_TRB_CTRL_IOC) || + (trb->ctrl & DWC3_TRB_CTRL_LST)) return 1; return 0; } static int dwc3_gadget_ep_reclaim_trb_linear(struct dwc3_ep *dep, - struct dwc3_request *req, - const struct dwc3_event_depevt *event, - int status) + struct dwc3_request *req, const struct dwc3_event_depevt *event, + int status) { struct dwc3_trb *trb = &dep->trb_pool[dep->trb_dequeue]; @@ -2244,15 +3067,24 @@ static int dwc3_gadget_ep_reclaim_trb_linear(struct dwc3_ep *dep, event, status, false); } +static bool dwc3_gadget_ep_request_completed(struct dwc3_request *req) +{ + return req->num_pending_sgs == 0 && req->num_queued_sgs == 0; +} + static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep, - const struct dwc3_event_depevt *event, - struct dwc3_request *req, - int status) + const struct dwc3_event_depevt *event, + struct dwc3_request *req, int status) { + int request_status; int ret; - ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event, - status); + ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event, status); + + req->request.actual = req->request.length - req->remaining; + + if (!dwc3_gadget_ep_request_completed(req)) + goto out; if (req->needs_extra_trb) { ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event, @@ -2260,69 +3092,121 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep, req->needs_extra_trb = false; } - req->request.actual = req->request.length - req->remaining; + /* + * The event status only reflects the status of the TRB with IOC set. + * For the requests that don't set interrupt on completion, the driver + * needs to check and return the status of the completed TRBs associated + * with the request. Use the status of the last TRB of the request. + */ + if (req->request.no_interrupt) { + struct dwc3_trb *trb; - dwc3_gadget_giveback(dep, req, status); + trb = dwc3_ep_prev_trb(dep, dep->trb_dequeue); + switch (DWC3_TRB_SIZE_TRBSTS(trb->size)) { + case DWC3_TRBSTS_MISSED_ISOC: + /* Isoc endpoint only */ + request_status = -EXDEV; + break; + case DWC3_TRB_STS_XFER_IN_PROG: + /* Applicable when End Transfer with ForceRM=0 */ + case DWC3_TRBSTS_SETUP_PENDING: + /* Control endpoint only */ + case DWC3_TRBSTS_OK: + default: + request_status = 0; + break; + } + } else { + request_status = status; + } + + dwc3_gadget_giveback(dep, req, request_status); +out: return ret; } static void dwc3_gadget_ep_cleanup_completed_requests(struct dwc3_ep *dep, - const struct dwc3_event_depevt *event, - int status) + const struct dwc3_event_depevt *event, int status) { - struct dwc3_request *req; - struct dwc3_request *tmp; + struct dwc3_request *req; - list_for_each_entry_safe(req, tmp, &dep->started_list, list) { + while (!list_empty(&dep->started_list)) { int ret; + req = next_request(&dep->started_list); ret = dwc3_gadget_ep_cleanup_completed_request(dep, event, req, status); if (ret) break; + /* + * The endpoint is disabled, let the dwc3_remove_requests() + * handle the cleanup. + */ + if (!dep->endpoint.desc) + break; } } +static bool dwc3_gadget_ep_should_continue(struct dwc3_ep *dep) +{ + struct dwc3_request *req; + struct dwc3 *dwc = dep->dwc; + + if (!dep->endpoint.desc || !dwc->pullups_connected || + !dwc->connected) + return false; + + if (!list_empty(&dep->pending_list)) + return true; + + /* + * We only need to check the first entry of the started list. We can + * assume the completed requests are removed from the started list. + */ + req = next_request(&dep->started_list); + if (!req) + return false; + + return !dwc3_gadget_ep_request_completed(req); +} + static void dwc3_gadget_endpoint_frame_from_event(struct dwc3_ep *dep, - const struct dwc3_event_depevt *event) + const struct dwc3_event_depevt *event) { dep->frame_number = event->parameters; } -static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep, - const struct dwc3_event_depevt *event) +static bool dwc3_gadget_endpoint_trbs_complete(struct dwc3_ep *dep, + const struct dwc3_event_depevt *event, int status) { - struct dwc3 *dwc = dep->dwc; - unsigned status = 0; - bool stop = false; - - dwc3_gadget_endpoint_frame_from_event(dep, event); - - if (event->status & DEPEVT_STATUS_BUSERR) - status = -ECONNRESET; + struct dwc3 *dwc = dep->dwc; + bool no_started_trb = true; - if (event->status & DEPEVT_STATUS_MISSED_ISOC) { - status = -EXDEV; + dwc3_gadget_ep_cleanup_completed_requests(dep, event, status); - if (list_empty(&dep->started_list)) - stop = true; - } + if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) + goto out; - dwc3_gadget_ep_cleanup_completed_requests(dep, event, status); + if (!dep->endpoint.desc) + return no_started_trb; - if (stop) { + if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && + list_empty(&dep->started_list) && + (list_empty(&dep->pending_list) || status == -EXDEV)) dwc3_stop_active_transfer(dep, true, true); - dep->flags = DWC3_EP_ENABLED; - } + else if (dwc3_gadget_ep_should_continue(dep)) + if (__dwc3_gadget_kick_transfer(dep) == 0) + no_started_trb = false; +out: /* * WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround. * See dwc3_gadget_linksts_change_interrupt() for 1st half. */ - if (dwc->revision < DWC3_REVISION_183A) { - u32 reg; - int i; + if (DWC3_VER_IS_PRIOR(DWC3, 183A)) { + u32 reg; + int i; for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) { dep = dwc->eps[i]; @@ -2331,7 +3215,7 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep, continue; if (!list_empty(&dep->started_list)) - return; + return no_started_trb; } reg = dwc3_readl(dwc->regs, DWC3_DCTL); @@ -2340,30 +3224,188 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep, dwc->u1u2 = 0; } + + return no_started_trb; +} + +static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep, + const struct dwc3_event_depevt *event) +{ + int status = 0; + + if (!dep->endpoint.desc) + return; + + if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) + dwc3_gadget_endpoint_frame_from_event(dep, event); + + if (event->status & DEPEVT_STATUS_BUSERR) + status = -ECONNRESET; + + if (event->status & DEPEVT_STATUS_MISSED_ISOC) + status = -EXDEV; + + dwc3_gadget_endpoint_trbs_complete(dep, event, status); +} + +static void dwc3_gadget_endpoint_transfer_complete(struct dwc3_ep *dep, + const struct dwc3_event_depevt *event) +{ + int status = 0; + + dep->flags &= ~DWC3_EP_TRANSFER_STARTED; + + if (event->status & DEPEVT_STATUS_BUSERR) + status = -ECONNRESET; + + if (dwc3_gadget_endpoint_trbs_complete(dep, event, status)) + dep->flags &= ~DWC3_EP_WAIT_TRANSFER_COMPLETE; } static void dwc3_gadget_endpoint_transfer_not_ready(struct dwc3_ep *dep, - const struct dwc3_event_depevt *event) + const struct dwc3_event_depevt *event) { dwc3_gadget_endpoint_frame_from_event(dep, event); + + /* + * The XferNotReady event is generated only once before the endpoint + * starts. It will be generated again when END_TRANSFER command is + * issued. For some controller versions, the XferNotReady event may be + * generated while the END_TRANSFER command is still in process. Ignore + * it and wait for the next XferNotReady event after the command is + * completed. + */ + if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) + return; + (void) __dwc3_gadget_start_isoc(dep); } +static void dwc3_gadget_endpoint_command_complete(struct dwc3_ep *dep, + const struct dwc3_event_depevt *event) +{ + u8 cmd = DEPEVT_PARAMETER_CMD(event->parameters); + + if (cmd != DWC3_DEPCMD_ENDTRANSFER) + return; + + /* + * The END_TRANSFER command will cause the controller to generate a + * NoStream Event, and it's not due to the host DP NoStream rejection. + * Ignore the next NoStream event. + */ + if (dep->stream_capable) + dep->flags |= DWC3_EP_IGNORE_NEXT_NOSTREAM; + + dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING; + dep->flags &= ~DWC3_EP_TRANSFER_STARTED; + dwc3_gadget_ep_cleanup_cancelled_requests(dep); + + if (dep->flags & DWC3_EP_PENDING_CLEAR_STALL) { + struct dwc3 *dwc = dep->dwc; + + dep->flags &= ~DWC3_EP_PENDING_CLEAR_STALL; + if (dwc3_send_clear_stall_ep_cmd(dep)) { + struct usb_ep *ep0 = &dwc->eps[0]->endpoint; + + dev_err(dwc->dev, "failed to clear STALL on %s\n", dep->name); + if (dwc->delayed_status) + __dwc3_gadget_ep0_set_halt(ep0, 1); + return; + } + + dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE); + if (dwc->clear_stall_protocol == dep->number) + dwc3_ep0_send_delayed_status(dwc); + } + + if ((dep->flags & DWC3_EP_DELAY_START) && + !usb_endpoint_xfer_isoc(dep->endpoint.desc)) + __dwc3_gadget_kick_transfer(dep); + + dep->flags &= ~DWC3_EP_DELAY_START; +} + +static void dwc3_gadget_endpoint_stream_event(struct dwc3_ep *dep, + const struct dwc3_event_depevt *event) +{ + struct dwc3 *dwc = dep->dwc; + + if (event->status == DEPEVT_STREAMEVT_FOUND) { + dep->flags |= DWC3_EP_FIRST_STREAM_PRIMED; + goto out; + } + + /* Note: NoStream rejection event param value is 0 and not 0xFFFF */ + switch (event->parameters) { + case DEPEVT_STREAM_PRIME: + /* + * If the host can properly transition the endpoint state from + * idle to prime after a NoStream rejection, there's no need to + * force restarting the endpoint to reinitiate the stream. To + * simplify the check, assume the host follows the USB spec if + * it primed the endpoint more than once. + */ + if (dep->flags & DWC3_EP_FORCE_RESTART_STREAM) { + if (dep->flags & DWC3_EP_FIRST_STREAM_PRIMED) + dep->flags &= ~DWC3_EP_FORCE_RESTART_STREAM; + else + dep->flags |= DWC3_EP_FIRST_STREAM_PRIMED; + } + + break; + case DEPEVT_STREAM_NOSTREAM: + if ((dep->flags & DWC3_EP_IGNORE_NEXT_NOSTREAM) || + !(dep->flags & DWC3_EP_FORCE_RESTART_STREAM) || + (!DWC3_MST_CAPABLE(&dwc->hwparams) && + !(dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE))) + break; + + /* + * If the host rejects a stream due to no active stream, by the + * USB and xHCI spec, the endpoint will be put back to idle + * state. When the host is ready (buffer added/updated), it will + * prime the endpoint to inform the usb device controller. This + * triggers the device controller to issue ERDY to restart the + * stream. However, some hosts don't follow this and keep the + * endpoint in the idle state. No prime will come despite host + * streams are updated, and the device controller will not be + * triggered to generate ERDY to move the next stream data. To + * workaround this and maintain compatibility with various + * hosts, force to reinitiate the stream until the host is ready + * instead of waiting for the host to prime the endpoint. + */ + if (DWC3_VER_IS_WITHIN(DWC32, 100A, ANY)) { + unsigned int cmd = DWC3_DGCMD_SET_ENDPOINT_PRIME; + + dwc3_send_gadget_generic_command(dwc, cmd, dep->number); + } else { + dep->flags |= DWC3_EP_DELAY_START; + dwc3_stop_active_transfer(dep, true, true); + return; + } + break; + } + +out: + dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM; +} + static void dwc3_endpoint_interrupt(struct dwc3 *dwc, - const struct dwc3_event_depevt *event) + const struct dwc3_event_depevt *event) { - struct dwc3_ep *dep; - u8 epnum = event->endpoint_number; - u8 cmd; + struct dwc3_ep *dep; + u8 epnum = event->endpoint_number; dep = dwc->eps[epnum]; if (!(dep->flags & DWC3_EP_ENABLED)) { - if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) + if ((epnum > 1) && !(dep->flags & DWC3_EP_TRANSFER_STARTED)) return; /* Handle only EPCMDCMPLT when EP disabled */ - if (event->endpoint_event != DWC3_DEPEVT_EPCMDCMPLT) + if ((event->endpoint_event != DWC3_DEPEVT_EPCMDCMPLT) && + !(epnum <= 1 && event->endpoint_event == DWC3_DEPEVT_XFERCOMPLETE)) return; } @@ -2380,15 +3422,14 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, dwc3_gadget_endpoint_transfer_not_ready(dep, event); break; case DWC3_DEPEVT_EPCMDCMPLT: - cmd = DEPEVT_PARAMETER_CMD(event->parameters); - - if (cmd == DWC3_DEPCMD_ENDTRANSFER) { - dep->flags &= ~DWC3_EP_TRANSFER_STARTED; - dwc3_gadget_ep_cleanup_cancelled_requests(dep); - } + dwc3_gadget_endpoint_command_complete(dep, event); break; - case DWC3_DEPEVT_STREAMEVT: case DWC3_DEPEVT_XFERCOMPLETE: + dwc3_gadget_endpoint_transfer_complete(dep, event); + break; + case DWC3_DEPEVT_STREAMEVT: + dwc3_gadget_endpoint_stream_event(dep, event); + break; case DWC3_DEPEVT_RXTXFIFOEVT: break; } @@ -2396,27 +3437,27 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, static void dwc3_disconnect_gadget(struct dwc3 *dwc) { - if (dwc->gadget_driver && dwc->gadget_driver->disconnect) { + if (dwc->async_callbacks && dwc->gadget_driver->disconnect) { spin_unlock(&dwc->lock); - dwc->gadget_driver->disconnect(&dwc->gadget); + dwc->gadget_driver->disconnect(dwc->gadget); spin_lock(&dwc->lock); } } static void dwc3_suspend_gadget(struct dwc3 *dwc) { - if (dwc->gadget_driver && dwc->gadget_driver->suspend) { + if (dwc->async_callbacks && dwc->gadget_driver->suspend) { spin_unlock(&dwc->lock); - dwc->gadget_driver->suspend(&dwc->gadget); + dwc->gadget_driver->suspend(dwc->gadget); spin_lock(&dwc->lock); } } static void dwc3_resume_gadget(struct dwc3 *dwc) { - if (dwc->gadget_driver && dwc->gadget_driver->resume) { + if (dwc->async_callbacks && dwc->gadget_driver->resume) { spin_unlock(&dwc->lock); - dwc->gadget_driver->resume(&dwc->gadget); + dwc->gadget_driver->resume(dwc->gadget); spin_lock(&dwc->lock); } } @@ -2426,53 +3467,74 @@ static void dwc3_reset_gadget(struct dwc3 *dwc) if (!dwc->gadget_driver) return; - if (dwc->gadget.speed != USB_SPEED_UNKNOWN) { + if (dwc->async_callbacks && dwc->gadget->speed != USB_SPEED_UNKNOWN) { spin_unlock(&dwc->lock); - usb_gadget_udc_reset(&dwc->gadget, dwc->gadget_driver); + usb_gadget_udc_reset(dwc->gadget, dwc->gadget_driver); spin_lock(&dwc->lock); } } -static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, - bool interrupt) +void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, + bool interrupt) { struct dwc3 *dwc = dep->dwc; - struct dwc3_gadget_ep_cmd_params params; - u32 cmd; - int ret; - if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) + /* + * Only issue End Transfer command to the control endpoint of a started + * Data Phase. Typically we should only do so in error cases such as + * invalid/unexpected direction as described in the control transfer + * flow of the programming guide. + */ + if (dep->number <= 1 && dwc->ep0state != EP0_DATA_PHASE) + return; + + if (interrupt && (dep->flags & DWC3_EP_DELAY_STOP)) + return; + + if (!(dep->flags & DWC3_EP_TRANSFER_STARTED) || + (dep->flags & DWC3_EP_END_TRANSFER_PENDING)) return; /* + * If a Setup packet is received but yet to DMA out, the controller will + * not process the End Transfer command of any endpoint. Polling of its + * DEPCMD.CmdAct may block setting up TRB for Setup packet, causing a + * timeout. Delay issuing the End Transfer command until the Setup TRB is + * prepared. + */ + if (dwc->ep0state != EP0_SETUP_PHASE && !dwc->delayed_status) { + dep->flags |= DWC3_EP_DELAY_STOP; + return; + } + + /* * NOTICE: We are violating what the Databook says about the * EndTransfer command. Ideally we would _always_ wait for the * EndTransfer Command Completion IRQ, but that's causing too * much trouble synchronizing between us and gadget driver. * * We have discussed this with the IP Provider and it was - * suggested to giveback all requests here, but give HW some - * extra time to synchronize with the interconnect. We're using - * an arbitraty 100us delay for that. + * suggested to giveback all requests here. * * Note also that a similar handling was tested by Synopsys * (thanks a lot Paul) and nothing bad has come out of it. - * In short, what we're doing is: + * In short, what we're doing is issuing EndTransfer with + * CMDIOC bit set and delay kicking transfer until the + * EndTransfer command had completed. + * + * As of IP version 3.10a of the DWC_usb3 IP, the controller + * supports a mode to work around the above limitation. The + * software can poll the CMDACT bit in the DEPCMD register + * after issuing a EndTransfer command. This mode is enabled + * by writing GUCTL2[14]. This polling is already done in the + * dwc3_send_gadget_ep_cmd() function so if the mode is + * enabled, the EndTransfer command will have completed upon + * returning from this function. * - * - Issue EndTransfer WITH CMDIOC bit set - * - Wait 100us + * This mode is NOT available on the DWC_usb31 IP. */ - cmd = DWC3_DEPCMD_ENDTRANSFER; - cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0; - cmd |= DWC3_DEPCMD_CMDIOC; - cmd |= DWC3_DEPCMD_PARAM(dep->resource_index); - memset(¶ms, 0, sizeof(params)); - ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms); - dep->resource_index = 0; - - if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A) - udelay(100); + __dwc3_stop_active_transfer(dep, force, interrupt); } static void dwc3_clear_stall_all_ep(struct dwc3 *dwc) @@ -2481,6 +3543,7 @@ static void dwc3_clear_stall_all_ep(struct dwc3 *dwc) for (epnum = 1; epnum < DWC3_ENDPOINTS_NUM; epnum++) { struct dwc3_ep *dep; + int ret; dep = dwc->eps[epnum]; if (!dep) @@ -2491,35 +3554,54 @@ static void dwc3_clear_stall_all_ep(struct dwc3 *dwc) dep->flags &= ~DWC3_EP_STALL; - dwc3_send_clear_stall_ep_cmd(dep); + ret = dwc3_send_clear_stall_ep_cmd(dep); + WARN_ON_ONCE(ret); } } static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) { - int reg; + int reg; + + dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RX_DET); reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_INITU1ENA; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); - reg &= ~DWC3_DCTL_INITU2ENA; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_gadget_dctl_write_safe(dwc, reg); + + dwc->connected = false; dwc3_disconnect_gadget(dwc); - dwc->gadget.speed = USB_SPEED_UNKNOWN; + dwc->gadget->speed = USB_SPEED_UNKNOWN; dwc->setup_packet_pending = false; - usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED); + usb_gadget_set_state(dwc->gadget, USB_STATE_NOTATTACHED); - dwc->connected = false; + if (dwc->ep0state != EP0_SETUP_PHASE) { + unsigned int dir; + + dir = !!dwc->ep0_expect_in; + if (dwc->ep0state == EP0_DATA_PHASE) + dwc3_ep0_end_control_data(dwc, dwc->eps[dir]); + else + dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]); + dwc3_ep0_stall_and_restart(dwc); + } } static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) { - u32 reg; + u32 reg; - dwc->connected = true; + /* + * Ideally, dwc3_reset_gadget() would trigger the function + * drivers to stop any active transfers through ep disable. + * However, for functions which defer ep disable, such as mass + * storage, we will need to rely on the call to stop active + * transfers here, and avoid allowing of request queuing. + */ + dwc->connected = false; /* * WORKAROUND: DWC3 revisions <1.88a have an issue which @@ -2547,16 +3629,45 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) * STAR#9000466709: RTL: Device : Disconnect event not * generated if setup packet pending in FIFO */ - if (dwc->revision < DWC3_REVISION_188A) { + if (DWC3_VER_IS_PRIOR(DWC3, 188A)) { if (dwc->setup_packet_pending) dwc3_gadget_disconnect_interrupt(dwc); } dwc3_reset_gadget(dwc); + /* + * From SNPS databook section 8.1.2, the EP0 should be in setup + * phase. So ensure that EP0 is in setup phase by issuing a stall + * and restart if EP0 is not in setup phase. + */ + if (dwc->ep0state != EP0_SETUP_PHASE) { + unsigned int dir; + + dir = !!dwc->ep0_expect_in; + if (dwc->ep0state == EP0_DATA_PHASE) + dwc3_ep0_end_control_data(dwc, dwc->eps[dir]); + else + dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]); + + dwc->eps[0]->trb_enqueue = 0; + dwc->eps[1]->trb_enqueue = 0; + + dwc3_ep0_stall_and_restart(dwc); + } + + /* + * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a + * Section 4.1.2 Table 4-2, it states that during a USB reset, the SW + * needs to ensure that it sends "a DEPENDXFER command for any active + * transfers." + */ + dwc3_stop_active_transfers(dwc); + dwc->connected = true; + reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_TSTCTRL_MASK; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_gadget_dctl_write_safe(dwc, reg); dwc->test_mode = false; dwc3_clear_stall_all_ep(dwc); @@ -2568,22 +3679,45 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) { - struct dwc3_ep *dep; - int ret; - u32 reg; - u8 speed; + struct dwc3_ep *dep; + int ret; + u32 reg; + u8 lanes = 1; + u8 speed; + + if (!dwc->softconnect) + return; reg = dwc3_readl(dwc->regs, DWC3_DSTS); speed = reg & DWC3_DSTS_CONNECTSPD; dwc->speed = speed; + if (DWC3_IP_IS(DWC32)) + lanes = DWC3_DSTS_CONNLANES(reg) + 1; + + dwc->gadget->ssp_rate = USB_SSP_GEN_UNKNOWN; + + /* + * RAMClkSel is reset to 0 after USB reset, so it must be reprogrammed + * each time on Connect Done. + * + * Currently we always use the reset value. If any platform + * wants to set this to a different value, we need to add a + * setting and update GCTL.RAMCLKSEL here. + */ + switch (speed) { case DWC3_DSTS_SUPERSPEED_PLUS: dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); - dwc->gadget.ep0->maxpacket = 512; - dwc->gadget.speed = USB_SPEED_SUPER_PLUS; + dwc->gadget->ep0->maxpacket = 512; + dwc->gadget->speed = USB_SPEED_SUPER_PLUS; + + if (lanes > 1) + dwc->gadget->ssp_rate = USB_SSP_GEN_2x2; + else + dwc->gadget->ssp_rate = USB_SSP_GEN_2x1; break; - case DWC3_DCFG_SUPERSPEED: + case DWC3_DSTS_SUPERSPEED: /* * WORKAROUND: DWC3 revisions <1.90a have an issue which * would cause a missing USB3 Reset event. @@ -2597,36 +3731,36 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) * STAR#9000483510: RTL: SS : USB3 reset event may * not be generated always when the link enters poll */ - if (dwc->revision < DWC3_REVISION_190A) + if (DWC3_VER_IS_PRIOR(DWC3, 190A)) dwc3_gadget_reset_interrupt(dwc); dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); - dwc->gadget.ep0->maxpacket = 512; - dwc->gadget.speed = USB_SPEED_SUPER; + dwc->gadget->ep0->maxpacket = 512; + dwc->gadget->speed = USB_SPEED_SUPER; + + if (lanes > 1) { + dwc->gadget->speed = USB_SPEED_SUPER_PLUS; + dwc->gadget->ssp_rate = USB_SSP_GEN_1x2; + } break; - case DWC3_DCFG_HIGHSPEED: + case DWC3_DSTS_HIGHSPEED: dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64); - dwc->gadget.ep0->maxpacket = 64; - dwc->gadget.speed = USB_SPEED_HIGH; + dwc->gadget->ep0->maxpacket = 64; + dwc->gadget->speed = USB_SPEED_HIGH; break; - case DWC3_DCFG_FULLSPEED: - case DWC3_DCFG_FULLSPEED1: + case DWC3_DSTS_FULLSPEED: dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64); - dwc->gadget.ep0->maxpacket = 64; - dwc->gadget.speed = USB_SPEED_FULL; - break; - case DWC3_DCFG_LOWSPEED: - dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8); - dwc->gadget.ep0->maxpacket = 8; - dwc->gadget.speed = USB_SPEED_LOW; + dwc->gadget->ep0->maxpacket = 64; + dwc->gadget->speed = USB_SPEED_FULL; break; } - dwc->eps[1]->endpoint.maxpacket = dwc->gadget.ep0->maxpacket; + dwc->eps[1]->endpoint.maxpacket = dwc->gadget->ep0->maxpacket; /* Enable USB2 LPM Capability */ - if ((dwc->revision > DWC3_REVISION_194A) && + if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A) && + !dwc->usb2_gadget_lpm_disable && (speed != DWC3_DSTS_SUPERSPEED) && (speed != DWC3_DSTS_SUPERSPEED_PLUS)) { reg = dwc3_readl(dwc->regs, DWC3_DCFG); @@ -2636,7 +3770,8 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN); - reg |= DWC3_DCTL_HIRD_THRES(dwc->hird_threshold); + reg |= DWC3_DCTL_HIRD_THRES(dwc->hird_threshold | + (dwc->is_utmi_l1_suspend << 4)); /* * When dwc3 revisions >= 2.40a, LPM Erratum is enabled and @@ -2644,17 +3779,23 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) * BESL value in the LPM token is less than or equal to LPM * NYET threshold. */ - if (dwc->revision < DWC3_REVISION_240A && dwc->has_lpm_erratum) - WARN(true, "LPM Erratum not available on dwc3 revisisions < 2.40a\n"); + WARN_ONCE(DWC3_VER_IS_PRIOR(DWC3, 240A) && dwc->has_lpm_erratum, + "LPM Erratum not available on dwc3 revisions < 2.40a\n"); - if (dwc->has_lpm_erratum && dwc->revision >= DWC3_REVISION_240A) - reg |= DWC3_DCTL_LPM_ERRATA(dwc->lpm_nyet_threshold); + if (dwc->has_lpm_erratum && !DWC3_VER_IS_PRIOR(DWC3, 240A)) + reg |= DWC3_DCTL_NYET_THRES(dwc->lpm_nyet_threshold); - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_gadget_dctl_write_safe(dwc, reg); } else { + if (dwc->usb2_gadget_lpm_disable) { + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg &= ~DWC3_DCFG_LPM_CAP; + dwc3_writel(dwc->regs, DWC3_DCFG, reg); + } + reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_HIRD_THRES_MASK; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_gadget_dctl_write_safe(dwc, reg); } dep = dwc->eps[0]; @@ -2687,15 +3828,18 @@ static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc) * implemented. */ - if (dwc->gadget_driver && dwc->gadget_driver->resume) - dwc->gadget_driver->resume(&dwc->gadget); + if (dwc->async_callbacks && dwc->gadget_driver->resume) { + spin_unlock(&dwc->lock); + dwc->gadget_driver->resume(dwc->gadget); + spin_lock(&dwc->lock); + } } static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, - unsigned int evtinfo) + unsigned int evtinfo) { - enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK; - unsigned int pwropt; + enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK; + unsigned int pwropt; /* * WORKAROUND: DWC3 < 2.50a have an issue when configured without @@ -2715,11 +3859,10 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, * operational mode */ pwropt = DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1); - if ((dwc->revision < DWC3_REVISION_250A) && - (pwropt != DWC3_GHWPARAMS1_EN_PWROPT_HIB)) { + if (DWC3_VER_IS_PRIOR(DWC3, 250A) && + (pwropt != DWC3_GHWPARAMS1_EN_PWROPT_HIB)) { if ((dwc->link_state == DWC3_LINK_STATE_U3) && - (next == DWC3_LINK_STATE_RESUME)) { - dev_dbg(dwc->dev, "ignoring transition U3 -> Resume\n"); + (next == DWC3_LINK_STATE_RESUME)) { return; } } @@ -2742,10 +3885,10 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, * STAR#9000446952: RTL: Device SS : if U1/U2 ->U0 takes >128us * core send LGO_Ux entering U0 */ - if (dwc->revision < DWC3_REVISION_183A) { + if (DWC3_VER_IS_PRIOR(DWC3, 183A)) { if (next == DWC3_LINK_STATE_U0) { - u32 u1u2; - u32 reg; + u32 u1u2; + u32 reg; switch (dwc->link_state) { case DWC3_LINK_STATE_U1: @@ -2761,7 +3904,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, reg &= ~u1u2; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_gadget_dctl_write_safe(dwc, reg); break; default: /* do nothing */ @@ -2777,7 +3920,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, break; case DWC3_LINK_STATE_U2: case DWC3_LINK_STATE_U3: - //dwc3_suspend_gadget(dwc); + dwc3_suspend_gadget(dwc); break; case DWC3_LINK_STATE_RESUME: dwc3_resume_gadget(dwc); @@ -2802,12 +3945,12 @@ static void dwc3_gadget_suspend_interrupt(struct dwc3 *dwc, } static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc, - unsigned int evtinfo) + unsigned int evtinfo) { - unsigned int is_ss = evtinfo & (1UL << 4); + unsigned int is_ss = evtinfo & BIT(4); - /** - * WORKAROUND: DWC3 revison 2.20a with hibernation support + /* + * WORKAROUND: DWC3 revision 2.20a with hibernation support * have a known issue which can cause USB CV TD.9.23 to fail * randomly. * @@ -2826,9 +3969,8 @@ static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc, } static void dwc3_gadget_interrupt(struct dwc3 *dwc, - const struct dwc3_event_devt *event) + const struct dwc3_event_devt *event) { - switch (event->type) { case DWC3_DEVICE_EVENT_DISCONNECT: dwc3_gadget_disconnect_interrupt(dwc); @@ -2843,96 +3985,154 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc, dwc3_gadget_wakeup_interrupt(dwc); break; case DWC3_DEVICE_EVENT_HIBER_REQ: - if (!dwc->has_hibernation) { - WARN(1 ,"unexpected hibernation event\n"); + if (dev_WARN_ONCE(dwc->dev, !dwc->has_hibernation, + "unexpected hibernation event\n")) break; - } + dwc3_gadget_hibernation_interrupt(dwc, event->event_info); break; case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: dwc3_gadget_linksts_change_interrupt(dwc, event->event_info); break; - case DWC3_DEVICE_EVENT_EOPF: - dev_dbg(dwc->dev, "End of Periodic Frame\n"); + case DWC3_DEVICE_EVENT_SUSPEND: /* It changed to be suspend event for version 2.30a and above */ - if (dwc->revision >= DWC3_REVISION_230A) { + if (!DWC3_VER_IS_PRIOR(DWC3, 230A)) { /* * Ignore suspend event until the gadget enters into * USB_STATE_CONFIGURED state. */ - if (dwc->gadget.state >= USB_STATE_CONFIGURED) + if (dwc->gadget->state >= USB_STATE_CONFIGURED) dwc3_gadget_suspend_interrupt(dwc, event->event_info); } break; case DWC3_DEVICE_EVENT_SOF: - dev_dbg(dwc->dev, "Start of Periodic Frame\n"); - break; case DWC3_DEVICE_EVENT_ERRATIC_ERROR: - dev_dbg(dwc->dev, "Erratic Error\n"); - break; case DWC3_DEVICE_EVENT_CMD_CMPL: - dev_dbg(dwc->dev, "Command Complete\n"); - break; case DWC3_DEVICE_EVENT_OVERFLOW: - dev_dbg(dwc->dev, "Overflow\n"); break; default: - dev_dbg(dwc->dev, "UNKNOWN IRQ %d\n", event->type); + dev_warn(dwc->dev, "UNKNOWN IRQ %d\n", event->type); } } static void dwc3_process_event_entry(struct dwc3 *dwc, - const union dwc3_event *event) + const union dwc3_event *event) { if (!event->type.is_devspec) dwc3_endpoint_interrupt(dwc, &event->depevt); else if (event->type.type == DWC3_EVENT_TYPE_DEV) dwc3_gadget_interrupt(dwc, &event->devt); + else + dev_err(dwc->dev, "UNKNOWN IRQ type %d\n", event->raw); } -static void dwc3_gadget_poll(struct usb_gadget * g) +static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt) { - struct dwc3 *dwc = gadget_to_dwc(g); - struct dwc3_event_buffer *evt = dwc->ev_buf; + struct dwc3 *dwc = evt->dwc; + irqreturn_t ret = IRQ_NONE; + int left; + + left = evt->count; + + if (!(evt->flags & DWC3_EVENT_PENDING)) + return IRQ_NONE; + + while (left > 0) { + union dwc3_event event; + + event.raw = *(u32 *) (evt->cache + evt->lpos); + + dwc3_process_event_entry(dwc, &event); + + /* + * FIXME we wrap around correctly to the next entry as + * almost all entries are 4 bytes in size. There is one + * entry which has 12 bytes which is a regular entry + * followed by 8 bytes data. ATM I don't know how + * things are organized if we get next to the a + * boundary so I worry about that once we try to handle + * that. + */ + evt->lpos = (evt->lpos + 4) % evt->length; + left -= 4; + } + + evt->count = 0; + ret = IRQ_HANDLED; + + /* Unmask interrupt */ + dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), + DWC3_GEVNTSIZ_SIZE(evt->length)); + + if (dwc->imod_interval) { + dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB); + dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval); + } + + /* Keep the clearing of DWC3_EVENT_PENDING at the end */ + evt->flags &= ~DWC3_EVENT_PENDING; + + return ret; +} + +static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt) +{ + struct dwc3 *dwc = evt->dwc; u32 amount; u32 count; - void *buf; - int pos = 0; + + /* + * With PCIe legacy interrupt, test shows that top-half irq handler can + * be called again after HW interrupt deassertion. Check if bottom-half + * irq event handler completes before caching new event to prevent + * losing events. + */ + if (evt->flags & DWC3_EVENT_PENDING) + return IRQ_HANDLED; count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0)); count &= DWC3_GEVNTCOUNT_MASK; if (!count) - return; + return IRQ_NONE; - buf = xzalloc(count); + evt->count = count; + evt->flags |= DWC3_EVENT_PENDING; + + /* Mask interrupt */ + dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), + DWC3_GEVNTSIZ_INTMASK | DWC3_GEVNTSIZ_SIZE(evt->length)); amount = min(count, evt->length - evt->lpos); - memcpy(buf, evt->buf + evt->lpos, amount); + memcpy_fromio(evt->cache + evt->lpos, evt->buf + evt->lpos, amount); if (amount < count) - memcpy(buf + amount, evt->buf, count - amount); - - evt->lpos = (evt->lpos + count) % evt->length; + memcpy_fromio(evt->cache, evt->buf, count - amount); dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count); - while (count > 0) { - union dwc3_event event; + dwc3_process_event_buf(evt); - event.raw = *(u32 *)(buf + pos); + return IRQ_HANDLED; +} - dwc3_process_event_entry(dwc, &event); +static void dwc3_gadget_poll(struct usb_gadget *g) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + struct dwc3_event_buffer *evt = dwc->ev_buf; - count -= 4; - pos += 4; - } + dwc3_check_event_buf(evt); +} + +static void dwc_gadget_release(struct device *dev) +{ + struct usb_gadget *gadget = container_of(dev, struct usb_gadget, dev); - free(buf); + kfree(gadget); } /** - * dwc3_gadget_init - Initializes gadget related registers + * dwc3_gadget_init - initializes gadget related registers * @dwc: pointer to our controller context structure * * Returns 0 on success otherwise negative errno. @@ -2940,46 +4140,69 @@ static void dwc3_gadget_poll(struct usb_gadget * g) int dwc3_gadget_init(struct dwc3 *dwc) { int ret; + struct device *dev; dwc->ep0_trb = dma_alloc_coherent(sizeof(*dwc->ep0_trb) * 2, - &dwc->ep0_trb_addr); + &dwc->ep0_trb_addr); if (!dwc->ep0_trb) { dev_err(dwc->dev, "failed to allocate ep0 trb\n"); ret = -ENOMEM; - goto err1; + goto err0; } - dwc->setup_buf = xzalloc(DWC3_EP0_SETUP_SIZE); + dwc->setup_buf = kzalloc(DWC3_EP0_SETUP_SIZE, GFP_KERNEL); if (!dwc->setup_buf) { ret = -ENOMEM; - goto err2; + goto err1; } - dwc->bounce = dma_alloc_coherent(DWC3_BOUNCE_SIZE, - &dwc->bounce_addr); + dwc->bounce = dma_alloc_coherent(DWC3_BOUNCE_SIZE, &dwc->bounce_addr); if (!dwc->bounce) { - dev_err(dwc->dev, "failed to allocate ep0 bounce buffer\n"); + ret = -ENOMEM; + goto err2; + } + + init_completion(&dwc->ep0_in_setup); + dwc->gadget = kzalloc(sizeof(struct usb_gadget), GFP_KERNEL); + if (!dwc->gadget) { ret = -ENOMEM; goto err3; } - dwc->gadget.ops = &dwc3_gadget_ops; - dwc->gadget.max_speed = USB_SPEED_SUPER; - dwc->gadget.speed = USB_SPEED_UNKNOWN; - dwc->gadget.name = "dwc3-gadget"; + + usb_initialize_gadget(dwc->dev, dwc->gadget, dwc_gadget_release); + dev = &dwc->gadget->dev; + dev->platform_data = dwc; + dwc->gadget->ops = &dwc3_gadget_ops; + dwc->gadget->speed = USB_SPEED_UNKNOWN; + dwc->gadget->ssp_rate = USB_SSP_GEN_UNKNOWN; + dwc->gadget->sg_supported = true; + dwc->gadget->name = "dwc3-gadget"; + dwc->gadget->lpm_capable = !dwc->usb2_gadget_lpm_disable; /* - * Per databook, DWC3 needs buffer size to be aligned to MaxPacketSize - * on ep out. + * FIXME We might be setting max_speed to <SUPER, however versions + * <2.20a of dwc3 have an issue with metastability (documented + * elsewhere in this driver) which tells us we can't set max speed to + * anything lower than SUPER. + * + * Because gadget.max_speed is only used by composite.c and function + * drivers (i.e. it won't go into dwc3's registers) we are allowing this + * to happen so we avoid sending SuperSpeed Capability descriptor + * together with our BOS descriptor as that could confuse host into + * thinking we can handle super speed. + * + * Note that, in fact, we won't even support GetBOS requests when speed + * is less than super speed because we don't have means, yet, to tell + * composite.c that we are USB 2.0 + LPM ECN. */ - dwc->gadget.quirk_ep_out_aligned_size = true; - - if (dwc->revision < DWC3_REVISION_220A && + if (DWC3_VER_IS_PRIOR(DWC3, 220A) && !dwc->dis_metastability_quirk) dev_info(dwc->dev, "changing max_speed on rev %08x\n", dwc->revision); - dwc->gadget.max_speed = dwc->maximum_speed; + dwc->gadget->max_speed = dwc->maximum_speed; + dwc->gadget->max_ssp_rate = dwc->max_ssp_rate; /* * REVISIT: Here we should clear all pending IRQs to be @@ -2990,26 +4213,47 @@ int dwc3_gadget_init(struct dwc3 *dwc) if (ret) goto err4; - ret = usb_add_gadget_udc((struct device_d *)dwc->dev, &dwc->gadget); + ret = usb_add_gadget(dwc->gadget); if (ret) { - dev_err(dwc->dev, "failed to register udc\n"); - goto err4; + dev_err(dwc->dev, "failed to add gadget\n"); + goto err5; } - dwc3_gadget_set_speed(dwc, dwc->maximum_speed); + if (DWC3_IP_IS(DWC32) && dwc->maximum_speed == USB_SPEED_SUPER_PLUS) + dwc3_gadget_set_ssp_rate(dwc->gadget, dwc->max_ssp_rate); + else + dwc3_gadget_set_speed(dwc->gadget, dwc->maximum_speed); return 0; -err4: +err5: dwc3_gadget_free_endpoints(dwc); +err4: + usb_put_gadget(dwc->gadget); + dwc->gadget = NULL; err3: - dma_free_coherent(dwc->bounce, 0, DWC3_BOUNCE_SIZE); + dma_free_coherent(dwc->bounce, dwc->bounce_addr, DWC3_BOUNCE_SIZE); err2: kfree(dwc->setup_buf); err1: - dma_free_coherent(dwc->ep0_trb, 0, sizeof(*dwc->ep0_trb) * 2); + dma_free_coherent(dwc->ep0_trb, dwc->ep0_trb_addr, sizeof(*dwc->ep0_trb) * 2); +err0: return ret; } + +/* -------------------------------------------------------------------------- */ + +void dwc3_gadget_exit(struct dwc3 *dwc) +{ + if (!dwc->gadget) + return; + + usb_del_gadget_udc(dwc->gadget); + dwc3_gadget_free_endpoints(dwc); + dma_free_coherent(dwc->bounce, dwc->bounce_addr, DWC3_BOUNCE_SIZE); + kfree(dwc->setup_buf); + dma_free_coherent(dwc->ep0_trb, dwc->ep0_trb_addr, sizeof(*dwc->ep0_trb) * 2); +} diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index 3ce748c0b4..0afa10b318 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -1,70 +1,75 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/** +/* * gadget.h - DesignWare USB3 DRD Gadget Header * - * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com * * Authors: Felipe Balbi <balbi@ti.com>, * Sebastian Andrzej Siewior <bigeasy@linutronix.de> - * - * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/gadget.h) and ported - * to uboot. - * - * commit 7a60855972 : usb: dwc3: gadget: fix set_halt() bug with pending - transfers - * */ #ifndef __DRIVERS_USB_DWC3_GADGET_H #define __DRIVERS_USB_DWC3_GADGET_H -#include <usb/gadget.h> +#include <linux/usb/gadget.h> #include <linux/list.h> #include "io.h" struct dwc3; #define to_dwc3_ep(ep) (container_of(ep, struct dwc3_ep, endpoint)) -#define gadget_to_dwc(g) (container_of(g, struct dwc3, gadget)) +#define gadget_to_dwc(g) (g->dev.platform_data) /* DEPCFG parameter 1 */ -#define DWC3_DEPCFG_INT_NUM(n) ((n) << 0) -#define DWC3_DEPCFG_XFER_COMPLETE_EN (1 << 8) -#define DWC3_DEPCFG_XFER_IN_PROGRESS_EN (1 << 9) -#define DWC3_DEPCFG_XFER_NOT_READY_EN (1 << 10) -#define DWC3_DEPCFG_FIFO_ERROR_EN (1 << 11) -#define DWC3_DEPCFG_STREAM_EVENT_EN (1 << 13) -#define DWC3_DEPCFG_BINTERVAL_M1(n) ((n) << 16) -#define DWC3_DEPCFG_STREAM_CAPABLE (1 << 24) -#define DWC3_DEPCFG_EP_NUMBER(n) ((n) << 25) -#define DWC3_DEPCFG_BULK_BASED (1 << 30) -#define DWC3_DEPCFG_FIFO_BASED (1 << 31) +#define DWC3_DEPCFG_INT_NUM(n) (((n) & 0x1f) << 0) +#define DWC3_DEPCFG_XFER_COMPLETE_EN BIT(8) +#define DWC3_DEPCFG_XFER_IN_PROGRESS_EN BIT(9) +#define DWC3_DEPCFG_XFER_NOT_READY_EN BIT(10) +#define DWC3_DEPCFG_FIFO_ERROR_EN BIT(11) +#define DWC3_DEPCFG_STREAM_EVENT_EN BIT(13) +#define DWC3_DEPCFG_BINTERVAL_M1(n) (((n) & 0xff) << 16) +#define DWC3_DEPCFG_STREAM_CAPABLE BIT(24) +#define DWC3_DEPCFG_EP_NUMBER(n) (((n) & 0x1f) << 25) +#define DWC3_DEPCFG_BULK_BASED BIT(30) +#define DWC3_DEPCFG_FIFO_BASED BIT(31) /* DEPCFG parameter 0 */ -#define DWC3_DEPCFG_EP_TYPE(n) ((n) << 1) -#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) ((n) << 3) -#define DWC3_DEPCFG_FIFO_NUMBER(n) ((n) << 17) -#define DWC3_DEPCFG_BURST_SIZE(n) ((n) << 22) +#define DWC3_DEPCFG_EP_TYPE(n) (((n) & 0x3) << 1) +#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) (((n) & 0x7ff) << 3) +#define DWC3_DEPCFG_FIFO_NUMBER(n) (((n) & 0x1f) << 17) +#define DWC3_DEPCFG_BURST_SIZE(n) (((n) & 0xf) << 22) #define DWC3_DEPCFG_DATA_SEQ_NUM(n) ((n) << 26) /* This applies for core versions earlier than 1.94a */ -#define DWC3_DEPCFG_IGN_SEQ_NUM (1 << 31) +#define DWC3_DEPCFG_IGN_SEQ_NUM BIT(31) /* These apply for core versions 1.94a and later */ -#define DWC3_DEPCFG_ACTION_INIT (0 << 30) -#define DWC3_DEPCFG_ACTION_RESTORE (1 << 30) +#define DWC3_DEPCFG_ACTION_INIT (0 << 30) +#define DWC3_DEPCFG_ACTION_RESTORE BIT(30) #define DWC3_DEPCFG_ACTION_MODIFY (2 << 30) /* DEPXFERCFG parameter 0 */ #define DWC3_DEPXFERCFG_NUM_XFER_RES(n) ((n) & 0xffff) +/* U1 Device exit Latency */ +#define DWC3_DEFAULT_U1_DEV_EXIT_LAT 0x0A /* Less then 10 microsec */ + +/* U2 Device exit Latency */ +#define DWC3_DEFAULT_U2_DEV_EXIT_LAT 0x1FF /* Less then 511 microsec */ + +/* Frame/Microframe Number Mask */ +#define DWC3_FRNUMBER_MASK 0x3fff /* -------------------------------------------------------------------------- */ #define to_dwc3_request(r) (container_of(r, struct dwc3_request, request)) +/** + * next_request - gets the next request on the given list + * @list: the request list to operate on + * + * Caller should take care of locking. This function return %NULL or the first + * request available on @list. + */ static inline struct dwc3_request *next_request(struct list_head *list) { - if (list_empty(list)) - return NULL; - - return list_first_entry(list, struct dwc3_request, list); + return list_first_entry_or_null(list, struct dwc3_request, list); } /** @@ -76,7 +81,7 @@ static inline struct dwc3_request *next_request(struct list_head *list) */ static inline void dwc3_gadget_move_started_request(struct dwc3_request *req) { - struct dwc3_ep *dep = req->dep; + struct dwc3_ep *dep = req->dep; req->status = DWC3_REQUEST_STATUS_STARTED; list_move_tail(&req->list, &dep->started_list); @@ -85,15 +90,17 @@ static inline void dwc3_gadget_move_started_request(struct dwc3_request *req) /** * dwc3_gadget_move_cancelled_request - move @req to the cancelled_list * @req: the request to be moved + * @reason: cancelled reason for the dwc3 request * * Caller should take care of locking. This function will move @req from its * current list to the endpoint's cancelled_list. */ -static inline void dwc3_gadget_move_cancelled_request(struct dwc3_request *req) +static inline void dwc3_gadget_move_cancelled_request(struct dwc3_request *req, + unsigned int reason) { - struct dwc3_ep *dep = req->dep; + struct dwc3_ep *dep = req->dep; - req->status = DWC3_REQUEST_STATUS_CANCELLED; + req->status = reason; list_move_tail(&req->list, &dep->cancelled_list); } @@ -103,11 +110,14 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, void dwc3_ep0_interrupt(struct dwc3 *dwc, const struct dwc3_event_depevt *event); void dwc3_ep0_out_start(struct dwc3 *dwc); +void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep); +void dwc3_ep0_stall_and_restart(struct dwc3 *dwc); int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value); int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value); int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request); int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol); -void dwc3_gadget_handle_interrupt(struct dwc3 *dwc); +void dwc3_ep0_send_delayed_status(struct dwc3 *dwc); +void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt); /** * dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW @@ -118,10 +128,24 @@ void dwc3_gadget_handle_interrupt(struct dwc3 *dwc); */ static inline void dwc3_gadget_ep_get_transfer_index(struct dwc3_ep *dep) { - u32 res_id; + u32 res_id; res_id = dwc3_readl(dep->regs, DWC3_DEPCMD); dep->resource_index = DWC3_DEPCMD_GET_RSC_IDX(res_id); } +/** + * dwc3_gadget_dctl_write_safe - write to DCTL safe from link state change + * @dwc: pointer to our context structure + * @value: value to write to DCTL + * + * Use this function when doing read-modify-write to DCTL. It will not + * send link state change request. + */ +static inline void dwc3_gadget_dctl_write_safe(struct dwc3 *dwc, u32 value) +{ + value &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; + dwc3_writel(dwc->regs, DWC3_DCTL, value); +} + #endif /* __DRIVERS_USB_DWC3_GADGET_H */ diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index d5daa7f19e..281d016a86 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /** * host.c - DesignWare USB3 DRD Controller Host Glue * @@ -16,7 +16,7 @@ int dwc3_host_init(struct dwc3 *dwc) { struct resource *io; - struct device_d *dev = dwc->dev; + struct device *dev = dwc->dev; io = dev_get_resource(dev, IORESOURCE_MEM, 0); if (IS_ERR(io)) { @@ -24,13 +24,17 @@ int dwc3_host_init(struct dwc3 *dwc) return PTR_ERR(io); } - dwc->xhci = add_generic_device("xHCI", DEVICE_ID_DYNAMIC, NULL, - io->start, resource_size(io), - IORESOURCE_MEM, NULL); + dwc->xhci = add_child_device(dev, "xHCI", DEVICE_ID_DYNAMIC, NULL, + io->start, resource_size(io), + IORESOURCE_MEM, NULL); if (!dwc->xhci) { dev_err(dev, "Failed to register xHCI device\n"); return -ENODEV; } - + return 0; } + +void dwc3_host_exit(struct dwc3 *dwc) +{ +} diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h index f87b173e90..c76878a3a4 100644 --- a/drivers/usb/dwc3/io.h +++ b/drivers/usb/dwc3/io.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /** * io.h - DesignWare USB3 DRD IO Header * diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 9d6a262038..517255f477 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -1,6 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-only menuconfig USB_GADGET select USB select POLLER + select NLS bool "USB gadget support" if USB_GADGET @@ -33,16 +35,18 @@ config USB_GADGET_AUTOSTART bool default y prompt "Automatically start usbgadget on boot" + depends on GLOBALVAR + select SYSTEM_PARTITIONS if USB_GADGET_MASS_STORAGE help Enabling this option allows to automatically start a dfu or fastboot gadget during boot. This behaviour is controlled with - the global.usbgadget.{dfu,fastboot}_function variable. + the global.usbgadget.dfu_function, global.system.partitions + and global.fastboot.* variables. comment "USB Gadget drivers" config USB_GADGET_DFU bool - select FILE_LIST prompt "Device Firmware Update Gadget" config USB_GADGET_SERIAL @@ -53,28 +57,17 @@ config USB_GADGET_SERIAL config USB_GADGET_FASTBOOT bool select BANNER - select FILE_LIST - prompt "Android Fastboot support" + select FASTBOOT_BASE + prompt "Android Fastboot USB Gadget" -config USB_GADGET_FASTBOOT_SPARSE +config USB_GADGET_MASS_STORAGE bool - select IMAGE_SPARSE - prompt "Enable Fastboot sparse image support" + select BTHREAD + prompt "USB Mass Storage Gadget" help - Sparse images are a way for the fastboot protocol to write - images that are bigger than the available memory. If unsure, - say yes here. + The Mass Storage Gadget acts as a USB Mass Storage disk drive. + As its storage repository it can use a regular file or a block + device. Multiple storages can be specified at once on + instantiation time. -config USB_GADGET_FASTBOOT_BUF - bool - depends on USB_GADGET_FASTBOOT - prompt "Download files to temporary buffer instead of file" - help - With this option enabled the fastboot code will download files to a - temporary buffer instead of a temporary file. Normally you want to - use a file as this also works when your memory is fragmented. However, - in some special cases, when the file consumer also better copes with - a buffer, then using a buffer might be better. - - Say no here unless you know what you are doing. endif diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 9ef594575b..f45b23f22d 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -1,8 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_USB_GADGET) += composite.o config.o usbstring.o epautoconf.o udc-core.o functions.o config.o multi.o -obj-$(CONFIG_USB_GADGET_SERIAL) += u_serial.o serial.o f_serial.o f_acm.o -obj-$(CONFIG_USB_GADGET_DFU) += dfu.o -obj-$(CONFIG_USB_GADGET_FASTBOOT) += f_fastboot.o -obj-$(CONFIG_USB_GADGET_DRIVER_ARC) += fsl_udc.o -obj-$(CONFIG_USB_GADGET_DRIVER_AT91) += at91_udc.o -obj-$(CONFIG_USB_GADGET_DRIVER_PXA27X) += pxa27x_udc.o +obj-$(CONFIG_USB_GADGET) += composite.o config.o usbstring.o epautoconf.o functions.o config.o + +obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/ diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index b66aa6be97..f55ae5698e 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1,12 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * composite.c - infrastructure for Composite USB Gadgets * * Copyright (C) 2006-2008 David Brownell - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ /* #define VERBOSE_DEBUG */ @@ -16,11 +12,31 @@ #include <dma.h> #include <linux/err.h> #include <linux/bitmap.h> -#include <usb/composite.h> +#include <linux/usb/composite.h> +#include <linux/bitfield.h> +#include <linux/uuid.h> #include <asm/unaligned.h> #include <asm/byteorder.h> -static unsigned int usb_gadget_vbus_draw_ma = 2; +#include "u_os_desc.h" + +#define CONFIG_USB_GADGET_VBUS_DRAW 2 /* FIXME */ + +/** + * struct usb_os_string - represents OS String to be reported by a gadget + * @bLength: total length of the entire descritor, always 0x12 + * @bDescriptorType: USB_DT_STRING + * @qwSignature: the OS String proper + * @bMS_VendorCode: code used by the host for subsequent requests + * @bPad: not used, must be zero + */ +struct usb_os_string { + __u8 bLength; + __u8 bDescriptorType; + __u8 qwSignature[OS_STRING_QW_SIGN_LEN]; + __u8 bMS_VendorCode; + __u8 bPad; +} __packed; /* * The code in this file is utility code, used to build a gadget driver @@ -36,40 +52,89 @@ static struct usb_gadget_strings **get_containers_gs( } /** - * next_ep_desc() - advance to the next EP descriptor + * function_descriptors() - get function descriptors for speed + * @f: the function + * @speed: the speed + * + * Returns the descriptors or NULL if not set. + */ +static struct usb_descriptor_header ** +function_descriptors(struct usb_function *f, + enum usb_device_speed speed) +{ + struct usb_descriptor_header **descriptors; + + /* + * NOTE: we try to help gadget drivers which might not be setting + * max_speed appropriately. + */ + + switch (speed) { + case USB_SPEED_SUPER_PLUS: + descriptors = f->ssp_descriptors; + if (descriptors) + break; + fallthrough; + case USB_SPEED_SUPER: + descriptors = f->ss_descriptors; + if (descriptors) + break; + fallthrough; + case USB_SPEED_HIGH: + descriptors = f->hs_descriptors; + if (descriptors) + break; + fallthrough; + default: + descriptors = f->fs_descriptors; + } + + /* + * if we can't find any descriptors at all, then this gadget deserves to + * Oops with a NULL pointer dereference + */ + + return descriptors; +} + +/** + * next_desc() - advance to the next desc_type descriptor * @t: currect pointer within descriptor array + * @desc_type: descriptor type * - * Return: next EP descriptor or NULL + * Return: next desc_type descriptor or NULL * - * Iterate over @t until either EP descriptor found or + * Iterate over @t until either desc_type descriptor found or * NULL (that indicates end of list) encountered */ static struct usb_descriptor_header** -next_ep_desc(struct usb_descriptor_header **t) +next_desc(struct usb_descriptor_header **t, u8 desc_type) { for (; *t; t++) { - if ((*t)->bDescriptorType == USB_DT_ENDPOINT) + if ((*t)->bDescriptorType == desc_type) return t; } return NULL; } /* - * for_each_ep_desc()- iterate over endpoint descriptors in the - * descriptors list - * @start: pointer within descriptor array. - * @ep_desc: endpoint descriptor to use as the loop cursor + * for_each_desc() - iterate over desc_type descriptors in the + * descriptors list + * @start: pointer within descriptor array. + * @iter_desc: desc_type descriptor to use as the loop cursor + * @desc_type: wanted descriptr type */ -#define for_each_ep_desc(start, ep_desc) \ - for (ep_desc = next_ep_desc(start); \ - ep_desc; ep_desc = next_ep_desc(ep_desc+1)) +#define for_each_desc(start, iter_desc, desc_type) \ + for (iter_desc = next_desc(start, desc_type); \ + iter_desc; iter_desc = next_desc(iter_desc + 1, desc_type)) /** - * config_ep_by_speed() - configures the given endpoint + * config_ep_by_speed_and_alt() - configures the given endpoint * according to gadget speed. * @g: pointer to the gadget * @f: usb function * @_ep: the endpoint to configure + * @alt: alternate setting number * * Return: error code, 0 on success * @@ -82,44 +147,80 @@ next_ep_desc(struct usb_descriptor_header **t) * Note: the supplied function should hold all the descriptors * for supported speeds */ -int config_ep_by_speed(struct usb_gadget *g, - struct usb_function *f, - struct usb_ep *_ep) +int config_ep_by_speed_and_alt(struct usb_gadget *g, + struct usb_function *f, + struct usb_ep *_ep, + u8 alt) { - struct usb_composite_dev *cdev; struct usb_endpoint_descriptor *chosen_desc = NULL; + struct usb_interface_descriptor *int_desc = NULL; struct usb_descriptor_header **speed_desc = NULL; struct usb_ss_ep_comp_descriptor *comp_desc = NULL; int want_comp_desc = 0; struct usb_descriptor_header **d_spd; /* cursor for speed desc */ + struct usb_composite_dev *cdev; + bool incomplete_desc = false; if (!g || !f || !_ep) return -EIO; - cdev = get_gadget_data(g); - /* select desired speed */ switch (g->speed) { + case USB_SPEED_SUPER_PLUS: + if (gadget_is_superspeed_plus(g)) { + if (f->ssp_descriptors) { + speed_desc = f->ssp_descriptors; + want_comp_desc = 1; + break; + } + incomplete_desc = true; + } + fallthrough; case USB_SPEED_SUPER: if (gadget_is_superspeed(g)) { - speed_desc = f->ss_descriptors; - want_comp_desc = 1; - break; + if (f->ss_descriptors) { + speed_desc = f->ss_descriptors; + want_comp_desc = 1; + break; + } + incomplete_desc = true; } - /* else: Fall trough */ + fallthrough; case USB_SPEED_HIGH: if (gadget_is_dualspeed(g)) { - speed_desc = f->hs_descriptors; - break; + if (f->hs_descriptors) { + speed_desc = f->hs_descriptors; + break; + } + incomplete_desc = true; } - /* else: fall through */ + fallthrough; default: speed_desc = f->fs_descriptors; } + + cdev = get_gadget_data(g); + if (incomplete_desc) + WARNING(cdev, + "%s doesn't hold the descriptors for current speed\n", + f->name); + + /* find correct alternate setting descriptor */ + for_each_desc(speed_desc, d_spd, USB_DT_INTERFACE) { + int_desc = (struct usb_interface_descriptor *)*d_spd; + + if (int_desc->bAlternateSetting == alt) { + speed_desc = d_spd; + goto intf_found; + } + } + return -EIO; + +intf_found: /* find descriptors */ - for_each_ep_desc(speed_desc, d_spd) { + for_each_desc(speed_desc, d_spd, USB_DT_ENDPOINT) { chosen_desc = (struct usb_endpoint_descriptor *)*d_spd; if (chosen_desc->bEndpointAddress == _ep->address) goto ep_found; @@ -132,7 +233,12 @@ ep_found: _ep->desc = chosen_desc; _ep->comp_desc = NULL; _ep->maxburst = 0; - _ep->mult = 0; + _ep->mult = 1; + + if (g->speed == USB_SPEED_HIGH && (usb_endpoint_xfer_isoc(_ep->desc) || + usb_endpoint_xfer_int(_ep->desc))) + _ep->mult = usb_endpoint_maxp_mult(_ep->desc); + if (!want_comp_desc) return 0; @@ -145,11 +251,12 @@ ep_found: (comp_desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP)) return -EIO; _ep->comp_desc = comp_desc; - if (g->speed == USB_SPEED_SUPER) { + if (g->speed >= USB_SPEED_SUPER) { switch (usb_endpoint_type(_ep->desc)) { case USB_ENDPOINT_XFER_ISOC: /* mult: bits 1:0 of bmAttributes */ - _ep->mult = comp_desc->bmAttributes & 0x3; + _ep->mult = (comp_desc->bmAttributes & 0x3) + 1; + fallthrough; case USB_ENDPOINT_XFER_BULK: case USB_ENDPOINT_XFER_INT: _ep->maxburst = comp_desc->bMaxBurst + 1; @@ -163,6 +270,32 @@ ep_found: } return 0; } +EXPORT_SYMBOL_GPL(config_ep_by_speed_and_alt); + +/** + * config_ep_by_speed() - configures the given endpoint + * according to gadget speed. + * @g: pointer to the gadget + * @f: usb function + * @_ep: the endpoint to configure + * + * Return: error code, 0 on success + * + * This function chooses the right descriptors for a given + * endpoint according to gadget speed and saves it in the + * endpoint desc field. If the endpoint already has a descriptor + * assigned to it - overwrites it with currently corresponding + * descriptor. The endpoint maxpacket field is updated according + * to the chosen descriptor. + * Note: the supplied function should hold all the descriptors + * for supported speeds + */ +int config_ep_by_speed(struct usb_gadget *g, + struct usb_function *f, + struct usb_ep *_ep) +{ + return config_ep_by_speed_and_alt(g, f, _ep, 0); +} EXPORT_SYMBOL_GPL(config_ep_by_speed); /** @@ -194,6 +327,12 @@ int usb_add_function(struct usb_configuration *config, function->config = config; list_add_tail(&function->list, &config->functions); + if (function->bind_deactivated) { + value = usb_function_deactivate(function); + if (value) + goto done; + } + /* REVISIT *require* function->bind? */ if (function->bind) { value = function->bind(config, function); @@ -215,6 +354,8 @@ int usb_add_function(struct usb_configuration *config, config->highspeed = true; if (!config->superspeed && function->ss_descriptors) config->superspeed = true; + if (!config->superspeed_plus && function->ssp_descriptors) + config->superspeed_plus = true; done: if (value) @@ -233,6 +374,9 @@ void usb_remove_function(struct usb_configuration *c, struct usb_function *f) list_del(&f->list); if (f->unbind) f->unbind(c, f); + + if (f->bind_deactivated) + usb_function_activate(f); } EXPORT_SYMBOL_GPL(usb_remove_function); @@ -258,13 +402,20 @@ EXPORT_SYMBOL_GPL(usb_remove_function); int usb_function_deactivate(struct usb_function *function) { struct usb_composite_dev *cdev = function->config->cdev; + unsigned long flags; int status = 0; - if (cdev->deactivations == 0) - status = usb_gadget_disconnect(cdev->gadget); + spin_lock_irqsave(&cdev->lock, flags); + + if (cdev->deactivations == 0) { + spin_unlock_irqrestore(&cdev->lock, flags); + status = usb_gadget_deactivate(cdev->gadget); + spin_lock_irqsave(&cdev->lock, flags); + } if (status == 0) cdev->deactivations++; + spin_unlock_irqrestore(&cdev->lock, flags); return status; } EXPORT_SYMBOL_GPL(usb_function_deactivate); @@ -282,16 +433,23 @@ EXPORT_SYMBOL_GPL(usb_function_deactivate); int usb_function_activate(struct usb_function *function) { struct usb_composite_dev *cdev = function->config->cdev; + unsigned long flags; int status = 0; + spin_lock_irqsave(&cdev->lock, flags); + if (WARN_ON(cdev->deactivations == 0)) status = -EINVAL; else { cdev->deactivations--; - if (cdev->deactivations == 0) - status = usb_gadget_connect(cdev->gadget); + if (cdev->deactivations == 0) { + spin_unlock_irqrestore(&cdev->lock, flags); + status = usb_gadget_activate(cdev->gadget); + spin_lock_irqsave(&cdev->lock, flags); + } } + spin_unlock_irqrestore(&cdev->lock, flags); return status; } EXPORT_SYMBOL_GPL(usb_function_activate); @@ -338,18 +496,20 @@ static u8 encode_bMaxPower(enum usb_device_speed speed, { unsigned val; - if (c->MaxPower) + if (c->MaxPower || (c->bmAttributes & USB_CONFIG_ATT_SELFPOWER)) val = c->MaxPower; else - val = usb_gadget_vbus_draw_ma; + val = CONFIG_USB_GADGET_VBUS_DRAW; if (!val) return 0; - switch (speed) { - case USB_SPEED_SUPER: - return DIV_ROUND_UP(val, 8); - default: - return DIV_ROUND_UP(val, 2); - } + if (speed < USB_SPEED_SUPER) + return min(val, 500U) / 2; + else + /* + * USB 3.x supports up to 900mA, but since 900 isn't divisible + * by 8 the integral division will effectively cap to 896mA. + */ + return min(val, 900U) / 8; } static int config_buf(struct usb_configuration *config, @@ -387,17 +547,7 @@ static int config_buf(struct usb_configuration *config, list_for_each_entry(f, &config->functions, list) { struct usb_descriptor_header **descriptors; - switch (speed) { - case USB_SPEED_SUPER: - descriptors = f->ss_descriptors; - break; - case USB_SPEED_HIGH: - descriptors = f->hs_descriptors; - break; - default: - descriptors = f->fs_descriptors; - } - + descriptors = function_descriptors(f, speed); if (!descriptors) continue; status = usb_descriptor_fillbuf(next, len, @@ -417,10 +567,11 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value) { struct usb_gadget *gadget = cdev->gadget; struct usb_configuration *c; + struct list_head *pos; u8 type = w_value >> 8; enum usb_device_speed speed = USB_SPEED_UNKNOWN; - if (gadget->speed == USB_SPEED_SUPER) + if (gadget->speed >= USB_SPEED_SUPER) speed = gadget->speed; else if (gadget_is_dualspeed(gadget)) { int hs = 0; @@ -435,9 +586,26 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value) /* This is a lookup by config *INDEX* */ w_value &= 0xff; - list_for_each_entry(c, &cdev->configs, list) { + + pos = &cdev->configs; + c = cdev->os_desc_config; + if (c) + goto check_config; + + while ((pos = pos->next) != &cdev->configs) { + c = list_entry(pos, typeof(*c), list); + + /* skip OS Descriptors config which is handled separately */ + if (c == cdev->os_desc_config) + continue; + +check_config: /* ignore configs that won't work at this speed */ switch (speed) { + case USB_SPEED_SUPER_PLUS: + if (!c->superspeed_plus) + continue; + break; case USB_SPEED_SUPER: if (!c->superspeed) continue; @@ -465,18 +633,24 @@ static int count_configs(struct usb_composite_dev *cdev, unsigned type) unsigned count = 0; int hs = 0; int ss = 0; + int ssp = 0; if (gadget_is_dualspeed(gadget)) { if (gadget->speed == USB_SPEED_HIGH) hs = 1; if (gadget->speed == USB_SPEED_SUPER) ss = 1; + if (gadget->speed == USB_SPEED_SUPER_PLUS) + ssp = 1; if (type == USB_DT_DEVICE_QUALIFIER) hs = !hs; } list_for_each_entry(c, &cdev->configs, list) { /* ignore configs that won't work at this speed */ - if (ss) { + if (ssp) { + if (!c->superspeed_plus) + continue; + } else if (ss) { if (!c->superspeed) continue; } else if (hs) { @@ -503,9 +677,9 @@ static int count_configs(struct usb_composite_dev *cdev, unsigned type) static int bos_desc(struct usb_composite_dev *cdev) { struct usb_ext_cap_descriptor *usb_ext; - struct usb_ss_cap_descriptor *ss_cap; struct usb_dcd_config_params dcd_config_params; struct usb_bos_descriptor *bos = cdev->req->buf; + unsigned int besl = 0; bos->bLength = USB_DT_BOS_SIZE; bos->bDescriptorType = USB_DT_BOS; @@ -513,45 +687,173 @@ static int bos_desc(struct usb_composite_dev *cdev) bos->wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE); bos->bNumDeviceCaps = 0; + /* Get Controller configuration */ + if (cdev->gadget->ops->get_config_params) { + cdev->gadget->ops->get_config_params(cdev->gadget, + &dcd_config_params); + } else { + dcd_config_params.besl_baseline = + USB_DEFAULT_BESL_UNSPECIFIED; + dcd_config_params.besl_deep = + USB_DEFAULT_BESL_UNSPECIFIED; + dcd_config_params.bU1devExitLat = + USB_DEFAULT_U1_DEV_EXIT_LAT; + dcd_config_params.bU2DevExitLat = + cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT); + } + + if (dcd_config_params.besl_baseline != USB_DEFAULT_BESL_UNSPECIFIED) + besl = USB_BESL_BASELINE_VALID | + USB_SET_BESL_BASELINE(dcd_config_params.besl_baseline); + + if (dcd_config_params.besl_deep != USB_DEFAULT_BESL_UNSPECIFIED) + besl |= USB_BESL_DEEP_VALID | + USB_SET_BESL_DEEP(dcd_config_params.besl_deep); + /* * A SuperSpeed device shall include the USB2.0 extension descriptor * and shall support LPM when operating in USB2.0 HS mode. */ - usb_ext = cdev->req->buf + le16_to_cpu(bos->wTotalLength); - bos->bNumDeviceCaps++; - le16_add_cpu(&bos->wTotalLength, USB_DT_USB_EXT_CAP_SIZE); - usb_ext->bLength = USB_DT_USB_EXT_CAP_SIZE; - usb_ext->bDescriptorType = USB_DT_DEVICE_CAPABILITY; - usb_ext->bDevCapabilityType = USB_CAP_TYPE_EXT; - usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT); + if (cdev->gadget->lpm_capable) { + usb_ext = cdev->req->buf + le16_to_cpu(bos->wTotalLength); + bos->bNumDeviceCaps++; + le16_add_cpu(&bos->wTotalLength, USB_DT_USB_EXT_CAP_SIZE); + usb_ext->bLength = USB_DT_USB_EXT_CAP_SIZE; + usb_ext->bDescriptorType = USB_DT_DEVICE_CAPABILITY; + usb_ext->bDevCapabilityType = USB_CAP_TYPE_EXT; + usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT | + USB_BESL_SUPPORT | besl); + } /* * The Superspeed USB Capability descriptor shall be implemented by all * SuperSpeed devices. */ - ss_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength); - bos->bNumDeviceCaps++; - le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SS_CAP_SIZE); - ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE; - ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY; - ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE; - ss_cap->bmAttributes = 0; /* LTM is not supported yet */ - ss_cap->wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION | - USB_FULL_SPEED_OPERATION | - USB_HIGH_SPEED_OPERATION | - USB_5GBPS_OPERATION); - ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION; + if (gadget_is_superspeed(cdev->gadget)) { + struct usb_ss_cap_descriptor *ss_cap; + + ss_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength); + bos->bNumDeviceCaps++; + le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SS_CAP_SIZE); + ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE; + ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY; + ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE; + ss_cap->bmAttributes = 0; /* LTM is not supported yet */ + ss_cap->wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION | + USB_FULL_SPEED_OPERATION | + USB_HIGH_SPEED_OPERATION | + USB_5GBPS_OPERATION); + ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION; + ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat; + ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat; + } - /* Get Controller configuration */ - if (cdev->gadget->ops->get_config_params) - cdev->gadget->ops->get_config_params(&dcd_config_params); - else { - dcd_config_params.bU1devExitLat = USB_DEFAULT_U1_DEV_EXIT_LAT; - dcd_config_params.bU2DevExitLat = - cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT); + /* The SuperSpeedPlus USB Device Capability descriptor */ + if (gadget_is_superspeed_plus(cdev->gadget)) { + struct usb_ssp_cap_descriptor *ssp_cap; + u8 ssac = 1; + u8 ssic; + int i; + + if (cdev->gadget->max_ssp_rate == USB_SSP_GEN_2x2) + ssac = 3; + + /* + * Paired RX and TX sublink speed attributes share + * the same SSID. + */ + ssic = (ssac + 1) / 2 - 1; + + ssp_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength); + bos->bNumDeviceCaps++; + + le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SSP_CAP_SIZE(ssac)); + ssp_cap->bLength = USB_DT_USB_SSP_CAP_SIZE(ssac); + ssp_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY; + ssp_cap->bDevCapabilityType = USB_SSP_CAP_TYPE; + ssp_cap->bReserved = 0; + ssp_cap->wReserved = 0; + + ssp_cap->bmAttributes = + cpu_to_le32(FIELD_PREP(USB_SSP_SUBLINK_SPEED_ATTRIBS, ssac) | + FIELD_PREP(USB_SSP_SUBLINK_SPEED_IDS, ssic)); + + ssp_cap->wFunctionalitySupport = + cpu_to_le16(FIELD_PREP(USB_SSP_MIN_SUBLINK_SPEED_ATTRIBUTE_ID, 0) | + FIELD_PREP(USB_SSP_MIN_RX_LANE_COUNT, 1) | + FIELD_PREP(USB_SSP_MIN_TX_LANE_COUNT, 1)); + + /* + * Use 1 SSID if the gadget supports up to gen2x1 or not + * specified: + * - SSID 0 for symmetric RX/TX sublink speed of 10 Gbps. + * + * Use 1 SSID if the gadget supports up to gen1x2: + * - SSID 0 for symmetric RX/TX sublink speed of 5 Gbps. + * + * Use 2 SSIDs if the gadget supports up to gen2x2: + * - SSID 0 for symmetric RX/TX sublink speed of 5 Gbps. + * - SSID 1 for symmetric RX/TX sublink speed of 10 Gbps. + */ + for (i = 0; i < ssac + 1; i++) { + u8 ssid; + u8 mantissa; + u8 type; + + ssid = i >> 1; + + if (cdev->gadget->max_ssp_rate == USB_SSP_GEN_2x1 || + cdev->gadget->max_ssp_rate == USB_SSP_GEN_UNKNOWN) + mantissa = 10; + else + mantissa = 5 << ssid; + + if (i % 2) + type = USB_SSP_SUBLINK_SPEED_ST_SYM_TX; + else + type = USB_SSP_SUBLINK_SPEED_ST_SYM_RX; + + ssp_cap->bmSublinkSpeedAttr[i] = + cpu_to_le32(FIELD_PREP(USB_SSP_SUBLINK_SPEED_SSID, ssid) | + FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSE, + USB_SSP_SUBLINK_SPEED_LSE_GBPS) | + FIELD_PREP(USB_SSP_SUBLINK_SPEED_ST, type) | + FIELD_PREP(USB_SSP_SUBLINK_SPEED_LP, + USB_SSP_SUBLINK_SPEED_LP_SSP) | + FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSM, mantissa)); + } + } + + /* The WebUSB Platform Capability descriptor */ + if (cdev->use_webusb) { + struct usb_plat_dev_cap_descriptor *webusb_cap; + struct usb_webusb_cap_data *webusb_cap_data; + guid_t webusb_uuid = WEBUSB_UUID; + + webusb_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength); + webusb_cap_data = (struct usb_webusb_cap_data *) webusb_cap->CapabilityData; + bos->bNumDeviceCaps++; + le16_add_cpu(&bos->wTotalLength, + USB_DT_USB_PLAT_DEV_CAP_SIZE(USB_WEBUSB_CAP_DATA_SIZE)); + + webusb_cap->bLength = USB_DT_USB_PLAT_DEV_CAP_SIZE(USB_WEBUSB_CAP_DATA_SIZE); + webusb_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY; + webusb_cap->bDevCapabilityType = USB_PLAT_DEV_CAP_TYPE; + webusb_cap->bReserved = 0; + export_guid(webusb_cap->UUID, &webusb_uuid); + + if (cdev->bcd_webusb_version != 0) + webusb_cap_data->bcdVersion = cpu_to_le16(cdev->bcd_webusb_version); + else + webusb_cap_data->bcdVersion = WEBUSB_VERSION_1_00; + + webusb_cap_data->bVendorCode = cdev->b_webusb_vendor_code; + + if (strnlen(cdev->landing_page, sizeof(cdev->landing_page)) > 0) + webusb_cap_data->iLandingPage = WEBUSB_LANDING_PAGE_PRESENT; + else + webusb_cap_data->iLandingPage = WEBUSB_LANDING_PAGE_NOT_PRESENT; } - ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat; - ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat; return le16_to_cpu(bos->wTotalLength); } @@ -579,45 +881,41 @@ static void reset_config(struct usb_composite_dev *cdev) { struct usb_function *f; - if (cdev->in_reset_config) - return; - - cdev->in_reset_config = 1; - DBG(cdev, "reset config\n"); list_for_each_entry(f, &cdev->config->functions, list) { if (f->disable) f->disable(f); + bitmap_zero(f->endpoints, 32); } cdev->config = NULL; cdev->delayed_status = 0; - cdev->in_reset_config = 0; } static int set_config(struct usb_composite_dev *cdev, const struct usb_ctrlrequest *ctrl, unsigned number) { struct usb_gadget *gadget = cdev->gadget; - struct usb_configuration *c = NULL; + struct usb_configuration *c = NULL, *iter; int result = -EINVAL; unsigned power = gadget_is_otg(gadget) ? 8 : 100; int tmp; if (number) { - list_for_each_entry(c, &cdev->configs, list) { - if (c->bConfigurationValue == number) { - /* - * We disable the FDs of the previous - * configuration only if the new configuration - * is a valid one - */ - if (cdev->config) - reset_config(cdev); - result = 0; - break; - } + list_for_each_entry(iter, &cdev->configs, list) { + if (iter->bConfigurationValue != number) + continue; + /* + * We disable the FDs of the previous + * configuration only if the new configuration + * is a valid one + */ + if (cdev->config) + reset_config(cdev); + c = iter; + result = 0; + break; } if (result < 0) goto done; @@ -628,12 +926,13 @@ static int set_config(struct usb_composite_dev *cdev, } INFO(cdev, "%s config #%d: %s\n", - usb_speed_string(gadget->speed), - number, c ? c->label : "unconfigured"); + usb_speed_string(gadget->speed), + number, c ? c->label : "unconfigured"); if (!c) goto done; + usb_gadget_set_state(gadget, USB_STATE_CONFIGURED); cdev->config = c; /* Initialize all interfaces by setting them to altsetting zero. */ @@ -650,16 +949,7 @@ static int set_config(struct usb_composite_dev *cdev, * function's setup callback instead of the current * configuration's setup callback. */ - switch (gadget->speed) { - case USB_SPEED_SUPER: - descriptors = f->ss_descriptors; - break; - case USB_SPEED_HIGH: - descriptors = f->hs_descriptors; - break; - default: - descriptors = f->fs_descriptors; - } + descriptors = function_descriptors(f, gadget->speed); for (; *descriptors; ++descriptors) { struct usb_endpoint_descriptor *ep; @@ -694,8 +984,21 @@ static int set_config(struct usb_composite_dev *cdev, } /* when we return, be sure our power usage is valid */ - power = c->MaxPower ? c->MaxPower : usb_gadget_vbus_draw_ma; + if (c->MaxPower || (c->bmAttributes & USB_CONFIG_ATT_SELFPOWER)) + power = c->MaxPower; + else + power = CONFIG_USB_GADGET_VBUS_DRAW; + + if (gadget->speed < USB_SPEED_SUPER) + power = min(power, 500U); + else + power = min(power, 900U); done: + if (power <= USB_SELF_POWER_VBUS_MAX_DRAW) + usb_gadget_set_selfpowered(gadget); + else + usb_gadget_clear_selfpowered(gadget); + usb_gadget_vbus_draw(gadget, power); if (result >= 0 && cdev->delayed_status) result = USB_GADGET_DELAYED_STATUS; @@ -779,8 +1082,9 @@ int usb_add_config(struct usb_composite_dev *cdev, } else { unsigned i; - DBG(cdev, "cfg %d/%p speeds:%s%s%s\n", + DBG(cdev, "cfg %d/%p speeds:%s%s%s%s\n", config->bConfigurationValue, config, + config->superspeed_plus ? " superplus" : "", config->superspeed ? " super" : "", config->highspeed ? " high" : "", config->fullspeed @@ -799,9 +1103,7 @@ int usb_add_config(struct usb_composite_dev *cdev, } } - /* set_alt(), or next bind(), sets up - * ep->driver_data as needed. - */ + /* set_alt(), or next bind(), sets up ep->claimed as needed */ usb_ep_autoconfig_reset(cdev->gadget); done: @@ -820,12 +1122,8 @@ static void remove_config(struct usb_composite_dev *cdev, f = list_first_entry(&config->functions, struct usb_function, list); - list_del(&f->list); - if (f->unbind) { - DBG(cdev, "unbind function '%s'/%p\n", f->name, f); - f->unbind(config, f); - /* may free memory for "f" */ - } + + usb_remove_function(config, f); } list_del(&config->list); if (config->unbind) { @@ -847,9 +1145,15 @@ static void remove_config(struct usb_composite_dev *cdev, void usb_remove_config(struct usb_composite_dev *cdev, struct usb_configuration *config) { + unsigned long flags; + + spin_lock_irqsave(&cdev->lock, flags); + if (cdev->config == config) reset_config(cdev); + spin_unlock_irqrestore(&cdev->lock, flags); + remove_config(cdev, config); } @@ -857,7 +1161,7 @@ void usb_remove_config(struct usb_composite_dev *cdev, /* We support strings in multiple languages ... string descriptor zero * says which languages are supported. The typical case will be that - * only one language (probably English) is used, with I18N handled on + * only one language (probably English) is used, with i18n handled on * the host side. */ @@ -870,7 +1174,7 @@ static void collect_langs(struct usb_gadget_strings **sp, __le16 *buf) while (*sp) { s = *sp; language = cpu_to_le16(s->language); - for (tmp = buf; *tmp && tmp < &buf[126]; tmp++) { + for (tmp = buf; *tmp && tmp < &buf[USB_MAX_STRING_LEN]; tmp++) { if (*tmp == language) goto repeat; } @@ -910,7 +1214,7 @@ static int get_string(struct usb_composite_dev *cdev, struct usb_function *f; int len; - /* Yes, not only is USB's I18N support probably more than most + /* Yes, not only is USB's i18n support probably more than most * folk will ever care about ... also, it's all supported here. * (Except for UTF8 support for Unicode's "Astral Planes".) */ @@ -945,7 +1249,7 @@ static int get_string(struct usb_composite_dev *cdev, collect_langs(sp, s->wData); } - for (len = 0; len <= 126 && s->wData[len]; len++) + for (len = 0; len <= USB_MAX_STRING_LEN && s->wData[len]; len++) continue; if (!len) return -EINVAL; @@ -954,6 +1258,19 @@ static int get_string(struct usb_composite_dev *cdev, return s->bLength; } + if (cdev->use_os_string && language == 0 && id == OS_STRING_IDX) { + struct usb_os_string *b = buf; + b->bLength = sizeof(*b); + b->bDescriptorType = USB_DT_STRING; + compiletime_assert( + sizeof(b->qwSignature) == sizeof(cdev->qw_sign), + "qwSignature size must be equal to qw_sign"); + memcpy(&b->qwSignature, cdev->qw_sign, sizeof(b->qwSignature)); + b->bMS_VendorCode = cdev->b_vendor_code; + b->bPad = 0; + return sizeof(*b); + } + list_for_each_entry(uc, &cdev->gstrings, list) { struct usb_gadget_strings **sp; @@ -1017,7 +1334,7 @@ int usb_string_id(struct usb_composite_dev *cdev) EXPORT_SYMBOL_GPL(usb_string_id); /** - * usb_string_ids() - allocate unused string IDs in batch + * usb_string_ids_tab() - allocate unused string IDs in batch * @cdev: the device whose string descriptor IDs are being allocated * @str: an array of usb_string objects to assign numbers to * Context: single threaded during gadget setup @@ -1109,11 +1426,11 @@ static struct usb_gadget_string_container *copy_gadget_strings( * This function will create a deep copy of usb_gadget_strings and usb_string * and attach it to the cdev. The actual string (usb_string.s) will not be * copied but only a referenced will be made. The struct usb_gadget_strings - * array may contain multiple languges and should be NULL terminated. + * array may contain multiple languages and should be NULL terminated. * The ->language pointer of each struct usb_gadget_strings has to contain the * same amount of entries. * For instance: sp[0] is en-US, sp[1] is es-ES. It is expected that the first - * usb_string entry of es-ES containts the translation of the first usb_string + * usb_string entry of es-ES contains the translation of the first usb_string * entry of en-US. Therefore both entries become the same id assign. */ struct usb_string *usb_gstrings_attach(struct usb_composite_dev *cdev, @@ -1215,6 +1532,8 @@ static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req) if (cdev->req == req) cdev->setup_pending = false; + else if (cdev->os_desc_req == req) + cdev->os_desc_pending = false; else WARN(1, "unknown request %p\n", req); } @@ -1228,6 +1547,8 @@ static int composite_ep0_queue(struct usb_composite_dev *cdev, if (ret == 0) { if (cdev->req == req) cdev->setup_pending = true; + else if (cdev->os_desc_req == req) + cdev->os_desc_pending = true; else WARN(1, "unknown request %p\n", req); } @@ -1235,6 +1556,156 @@ static int composite_ep0_queue(struct usb_composite_dev *cdev, return ret; } +static int count_ext_compat(struct usb_configuration *c) +{ + int i, res; + + res = 0; + for (i = 0; i < c->next_interface_id; ++i) { + struct usb_function *f; + int j; + + f = c->interface[i]; + for (j = 0; j < f->os_desc_n; ++j) { + struct usb_os_desc *d; + + if (i != f->os_desc_table[j].if_id) + continue; + d = f->os_desc_table[j].os_desc; + if (d && d->ext_compat_id) + ++res; + } + } + BUG_ON(res > 255); + return res; +} + +static int fill_ext_compat(struct usb_configuration *c, u8 *buf) +{ + int i, count; + + count = 16; + buf += 16; + for (i = 0; i < c->next_interface_id; ++i) { + struct usb_function *f; + int j; + + f = c->interface[i]; + for (j = 0; j < f->os_desc_n; ++j) { + struct usb_os_desc *d; + + if (i != f->os_desc_table[j].if_id) + continue; + d = f->os_desc_table[j].os_desc; + if (d && d->ext_compat_id) { + *buf++ = i; + *buf++ = 0x01; + memcpy(buf, d->ext_compat_id, 16); + buf += 22; + } else { + ++buf; + *buf = 0x01; + buf += 23; + } + count += 24; + if (count + 24 >= USB_COMP_EP0_OS_DESC_BUFSIZ) + return count; + } + } + + return count; +} + +static int count_ext_prop(struct usb_configuration *c, int interface) +{ + struct usb_function *f; + int j; + + f = c->interface[interface]; + for (j = 0; j < f->os_desc_n; ++j) { + struct usb_os_desc *d; + + if (interface != f->os_desc_table[j].if_id) + continue; + d = f->os_desc_table[j].os_desc; + if (d && d->ext_compat_id) + return d->ext_prop_count; + } + return 0; +} + +static int len_ext_prop(struct usb_configuration *c, int interface) +{ + struct usb_function *f; + struct usb_os_desc *d; + int j, res; + + res = 10; /* header length */ + f = c->interface[interface]; + for (j = 0; j < f->os_desc_n; ++j) { + if (interface != f->os_desc_table[j].if_id) + continue; + d = f->os_desc_table[j].os_desc; + if (d) + return min(res + d->ext_prop_len, 4096); + } + return res; +} + +static int fill_ext_prop(struct usb_configuration *c, int interface, u8 *buf) +{ + struct usb_function *f; + struct usb_os_desc *d; + struct usb_os_desc_ext_prop *ext_prop; + int j, count, n, ret; + + f = c->interface[interface]; + count = 10; /* header length */ + buf += 10; + for (j = 0; j < f->os_desc_n; ++j) { + if (interface != f->os_desc_table[j].if_id) + continue; + d = f->os_desc_table[j].os_desc; + if (d) + list_for_each_entry(ext_prop, &d->ext_prop, entry) { + n = ext_prop->data_len + + ext_prop->name_len + 14; + if (count + n >= USB_COMP_EP0_OS_DESC_BUFSIZ) + return count; + usb_ext_prop_put_size(buf, n); + usb_ext_prop_put_type(buf, ext_prop->type); + ret = usb_ext_prop_put_name(buf, ext_prop->name, + ext_prop->name_len); + if (ret < 0) + return ret; + switch (ext_prop->type) { + case USB_EXT_PROP_UNICODE: + case USB_EXT_PROP_UNICODE_ENV: + case USB_EXT_PROP_UNICODE_LINK: + usb_ext_prop_put_unicode(buf, ret, + ext_prop->data, + ext_prop->data_len); + break; + case USB_EXT_PROP_BINARY: + usb_ext_prop_put_binary(buf, ret, + ext_prop->data, + ext_prop->data_len); + break; + case USB_EXT_PROP_LE32: + /* not implemented */ + case USB_EXT_PROP_BE32: + /* not implemented */ + default: + return -EINVAL; + } + buf += n; + count += n; + } + } + + return count; +} + /* * The setup() callback implements all the ep0 functionality that's * not handled lower down, in hardware or the hardware driver(like @@ -1254,8 +1725,21 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) u16 w_value = le16_to_cpu(ctrl->wValue); u16 w_length = le16_to_cpu(ctrl->wLength); struct usb_function *f = NULL; + struct usb_function *iter; u8 endp; + if (w_length > USB_COMP_EP0_BUFSIZ) { + if (ctrl->bRequestType & USB_DIR_IN) { + /* Cast away the const, we are going to overwrite on purpose. */ + __le16 *temp = (__le16 *)&ctrl->wLength; + + *temp = cpu_to_le16(USB_COMP_EP0_BUFSIZ); + w_length = USB_COMP_EP0_BUFSIZ; + } else { + goto done; + } + } + /* partial re-init of the response message; the function or the * gadget might need to intercept e.g. a control-OUT completion * when we delegate to it. @@ -1266,6 +1750,13 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) req->length = 0; gadget->ep0->driver_data = cdev; + /* + * Don't let non-standard requests match any of the cases below + * by accident. + */ + if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) + goto unknown; + switch (ctrl->bRequest) { /* we handle all standard USB descriptors */ @@ -1281,11 +1772,16 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) cdev->gadget->ep0->maxpacket; if (gadget_is_superspeed(gadget)) { if (gadget->speed >= USB_SPEED_SUPER) { - cdev->desc.bcdUSB = cpu_to_le16(0x0300); + cdev->desc.bcdUSB = cpu_to_le16(0x0320); cdev->desc.bMaxPacketSize0 = 9; } else { cdev->desc.bcdUSB = cpu_to_le16(0x0210); } + } else { + if (gadget->lpm_capable || cdev->use_webusb) + cdev->desc.bcdUSB = cpu_to_le16(0x0201); + else + cdev->desc.bcdUSB = cpu_to_le16(0x0200); } value = min(w_length, (u16) sizeof cdev->desc); @@ -1303,7 +1799,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) if (!gadget_is_dualspeed(gadget) || gadget->speed >= USB_SPEED_SUPER) break; - /* FALLTHROUGH */ + fallthrough; case USB_DT_CONFIG: value = config_desc(cdev, w_value); if (value >= 0) @@ -1316,11 +1812,32 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) value = min(w_length, (u16) value); break; case USB_DT_BOS: - if (gadget_is_superspeed(gadget)) { + if (gadget_is_superspeed(gadget) || + gadget->lpm_capable || cdev->use_webusb) { value = bos_desc(cdev); value = min(w_length, (u16) value); } break; + case USB_DT_OTG: + if (gadget_is_otg(gadget)) { + struct usb_configuration *config; + int otg_desc_len = 0; + + if (cdev->config) + config = cdev->config; + else + config = list_first_entry( + &cdev->configs, + struct usb_configuration, list); + if (!config) + goto done; + + otg_desc_len += sizeof(struct usb_otg_descriptor); + + value = min_t(int, w_length, otg_desc_len); + memcpy(req->buf, config->descriptors[0], value); + } + break; } break; @@ -1336,7 +1853,9 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) else VDBG(cdev, "HNP inactive\n"); } + spin_lock(&cdev->lock); value = set_config(cdev, ctrl, w_value); + spin_unlock(&cdev->lock); break; case USB_REQ_GET_CONFIGURATION: if (ctrl->bRequestType != USB_DIR_IN) @@ -1348,9 +1867,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) value = min(w_length, (u16) 1); break; - /* function drivers must handle get/set altsetting; if there's - * no get() method, we know only altsetting zero works. - */ + /* function drivers must handle get/set altsetting */ case USB_REQ_SET_INTERFACE: if (ctrl->bRequestType != USB_RECIP_INTERFACE) goto unknown; @@ -1359,8 +1876,16 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) f = cdev->config->interface[intf]; if (!f) break; - if (w_value && !f->set_alt) + + /* + * If there's no get_alt() method, we know only altsetting zero + * works. There is no need to check if set_alt() is not NULL + * as we check this in usb_add_function(). + */ + if (w_value && !f->get_alt) break; + + spin_lock(&cdev->lock); value = f->set_alt(f, w_index, w_value); if (value == USB_GADGET_DELAYED_STATUS) { DBG(cdev, @@ -1370,6 +1895,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) DBG(cdev, "delayed_status count %d\n", cdev->delayed_status); } + spin_unlock(&cdev->lock); break; case USB_REQ_GET_INTERFACE: if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) @@ -1386,15 +1912,24 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) *((u8 *)req->buf) = value; value = min(w_length, (u16) 1); break; - - /* - * USB 3.0 additions: - * Function driver should handle get_status request. If such cb - * wasn't supplied we respond with default value = 0 - * Note: function driver should supply such cb only for the first - * interface of the function - */ case USB_REQ_GET_STATUS: + if (gadget_is_otg(gadget) && gadget->hnp_polling_support && + (w_index == OTG_STS_SELECTOR)) { + if (ctrl->bRequestType != (USB_DIR_IN | + USB_RECIP_DEVICE)) + goto unknown; + *((u8 *)req->buf) = gadget->host_request_flag; + value = 1; + break; + } + + /* + * USB 3.0 additions: + * Function driver should handle get_status request. If such cb + * wasn't supplied we respond with default value = 0 + * Note: function driver should supply such cb only for the + * first interface of the function + */ if (!gadget_is_superspeed(gadget)) goto unknown; if (ctrl->bRequestType != (USB_DIR_IN | USB_RECIP_INTERFACE)) @@ -1443,6 +1978,116 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) break; default: unknown: + /* + * OS descriptors handling + */ + if (cdev->use_os_string && cdev->os_desc_config && + (ctrl->bRequestType & USB_TYPE_VENDOR) && + ctrl->bRequest == cdev->b_vendor_code) { + struct usb_configuration *os_desc_cfg; + u8 *buf; + int interface; + int count = 0; + + req = cdev->os_desc_req; + req->context = cdev; + req->complete = composite_setup_complete; + buf = req->buf; + os_desc_cfg = cdev->os_desc_config; + w_length = min_t(u16, w_length, USB_COMP_EP0_OS_DESC_BUFSIZ); + memset(buf, 0, w_length); + buf[5] = 0x01; + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + if (w_index != 0x4 || (w_value >> 8)) + break; + buf[6] = w_index; + /* Number of ext compat interfaces */ + count = count_ext_compat(os_desc_cfg); + buf[8] = count; + count *= 24; /* 24 B/ext compat desc */ + count += 16; /* header */ + put_unaligned_le32(count, buf); + value = w_length; + if (w_length > 0x10) { + value = fill_ext_compat(os_desc_cfg, buf); + value = min_t(u16, w_length, value); + } + break; + case USB_RECIP_INTERFACE: + if (w_index != 0x5 || (w_value >> 8)) + break; + interface = w_value & 0xFF; + if (interface >= MAX_CONFIG_INTERFACES || + !os_desc_cfg->interface[interface]) + break; + buf[6] = w_index; + count = count_ext_prop(os_desc_cfg, + interface); + put_unaligned_le16(count, buf + 8); + count = len_ext_prop(os_desc_cfg, + interface); + put_unaligned_le32(count, buf); + value = w_length; + if (w_length > 0x0A) { + value = fill_ext_prop(os_desc_cfg, + interface, buf); + if (value >= 0) + value = min_t(u16, w_length, value); + } + break; + } + + goto check_value; + } + + /* + * WebUSB URL descriptor handling, following: + * https://wicg.github.io/webusb/#device-requests + */ + if (cdev->use_webusb && + ctrl->bRequestType == (USB_DIR_IN | USB_TYPE_VENDOR) && + w_index == WEBUSB_GET_URL && + w_value == WEBUSB_LANDING_PAGE_PRESENT && + ctrl->bRequest == cdev->b_webusb_vendor_code) { + unsigned int landing_page_length; + unsigned int landing_page_offset; + struct webusb_url_descriptor *url_descriptor = + (struct webusb_url_descriptor *)cdev->req->buf; + + url_descriptor->bDescriptorType = WEBUSB_URL_DESCRIPTOR_TYPE; + + if (strncasecmp(cdev->landing_page, "https://", 8) == 0) { + landing_page_offset = 8; + url_descriptor->bScheme = WEBUSB_URL_SCHEME_HTTPS; + } else if (strncasecmp(cdev->landing_page, "http://", 7) == 0) { + landing_page_offset = 7; + url_descriptor->bScheme = WEBUSB_URL_SCHEME_HTTP; + } else { + landing_page_offset = 0; + url_descriptor->bScheme = WEBUSB_URL_SCHEME_NONE; + } + + landing_page_length = strnlen(cdev->landing_page, + sizeof(url_descriptor->URL) + - WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_offset); + + if (ctrl->wLength < WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + + landing_page_length) + landing_page_length = ctrl->wLength + - WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_offset; + + memcpy(url_descriptor->URL, + cdev->landing_page + landing_page_offset, + landing_page_length - landing_page_offset); + url_descriptor->bLength = landing_page_length + - landing_page_offset + WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH; + + value = url_descriptor->bLength; + + goto check_value; + } + VDBG(cdev, "non-core control req%02x.%02x v%04x i%04x l%d\n", ctrl->bRequestType, ctrl->bRequest, @@ -1451,11 +2096,22 @@ unknown: /* functions always handle their interfaces and endpoints... * punt other recipients (other, WUSB, ...) to the current * configuration code. - * - * REVISIT it could make sense to let the composite device - * take such requests too, if that's ever needed: to work - * in config 0, etc. */ + if (cdev->config) { + list_for_each_entry(f, &cdev->config->functions, list) + if (f->req_match && + f->req_match(f, ctrl, false)) + goto try_fun_setup; + } else { + struct usb_configuration *c; + list_for_each_entry(c, &cdev->configs, list) + list_for_each_entry(f, &c->functions, list) + if (f->req_match && + f->req_match(f, ctrl, true)) + goto try_fun_setup; + } + f = NULL; + switch (ctrl->bRequestType & USB_RECIP_MASK) { case USB_RECIP_INTERFACE: if (!cdev->config || intf >= MAX_CONFIG_INTERFACES) @@ -1464,16 +2120,18 @@ unknown: break; case USB_RECIP_ENDPOINT: + if (!cdev->config) + break; endp = ((w_index & 0x80) >> 3) | (w_index & 0x0f); - list_for_each_entry(f, &cdev->config->functions, list) { - if (test_bit(endp, f->endpoints)) + list_for_each_entry(iter, &cdev->config->functions, list) { + if (test_bit(endp, iter->endpoints)) { + f = iter; break; + } } - if (&f->list == &cdev->config->functions) - f = NULL; break; } - +try_fun_setup: if (f && f->setup) value = f->setup(f, ctrl); else { @@ -1501,9 +2159,11 @@ unknown: goto done; } +check_value: /* respond with data transfer before status phase? */ if (value >= 0 && value != USB_GADGET_DELAYED_STATUS) { req->length = value; + req->context = cdev; req->zero = value < w_length; value = composite_ep0_queue(cdev, req); if (value < 0) { @@ -1522,17 +2182,38 @@ done: return value; } -void composite_disconnect(struct usb_gadget *gadget) +static void __composite_disconnect(struct usb_gadget *gadget) { struct usb_composite_dev *cdev = get_gadget_data(gadget); + unsigned long flags; /* REVISIT: should we have config and device level * disconnect callbacks? */ + spin_lock_irqsave(&cdev->lock, flags); + cdev->suspended = 0; if (cdev->config) reset_config(cdev); if (cdev->driver->disconnect) cdev->driver->disconnect(cdev); + spin_unlock_irqrestore(&cdev->lock, flags); +} + +void composite_disconnect(struct usb_gadget *gadget) +{ + usb_gadget_vbus_draw(gadget, 0); + __composite_disconnect(gadget); +} + +void composite_reset(struct usb_gadget *gadget) +{ + /* + * Section 1.4.13 Standard Downstream Port of the USB battery charging + * specification v1.2 states that a device connected on a SDP shall only + * draw at max 100mA while in a connected, but unconfigured state. + */ + usb_gadget_vbus_draw(gadget, 100); + __composite_disconnect(gadget); } /*-------------------------------------------------------------------------*/ @@ -1540,6 +2221,8 @@ void composite_disconnect(struct usb_gadget *gadget) static void __composite_unbind(struct usb_gadget *gadget, bool unbind_driver) { struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_gadget_strings *gstr = cdev->driver->strings[0]; + struct usb_string *dev_str = gstr->strings; /* composite_disconnect() must already have been called * by the underlying peripheral controller driver! @@ -1559,6 +2242,9 @@ static void __composite_unbind(struct usb_gadget *gadget, bool unbind_driver) composite_dev_cleanup(cdev); + if (dev_str[USB_GADGET_MANUFACTURER_IDX].s == cdev->def_manufacturer) + dev_str[USB_GADGET_MANUFACTURER_IDX].s = ""; + kfree(cdev->def_manufacturer); kfree(cdev); set_gadget_data(gadget, NULL); @@ -1623,6 +2309,7 @@ int composite_dev_prepare(struct usb_composite_driver *composite, goto fail; cdev->req->complete = composite_setup_complete; + cdev->req->context = cdev; gadget->ep0->driver_data = cdev; cdev->driver = composite; @@ -1632,7 +2319,7 @@ int composite_dev_prepare(struct usb_composite_driver *composite, * more than 100mA from USB must report itself as bus-powered in * the GetStatus(DEVICE) call. */ - if (usb_gadget_vbus_draw_ma <= USB_SELF_POWER_VBUS_MAX_DRAW) + if (CONFIG_USB_GADGET_VBUS_DRAW <= USB_SELF_POWER_VBUS_MAX_DRAW) usb_gadget_set_selfpowered(gadget); /* interface and string IDs start at zero via kzalloc. @@ -1648,21 +2335,73 @@ fail: return ret; } +int composite_os_desc_req_prepare(struct usb_composite_dev *cdev, + struct usb_ep *ep0) +{ + int ret = 0; + + cdev->os_desc_req = usb_ep_alloc_request(ep0); + if (!cdev->os_desc_req) { + ret = -ENOMEM; + goto end; + } + + cdev->os_desc_req->buf = kmalloc(USB_COMP_EP0_OS_DESC_BUFSIZ, + GFP_KERNEL); + if (!cdev->os_desc_req->buf) { + ret = -ENOMEM; + usb_ep_free_request(ep0, cdev->os_desc_req); + goto end; + } + cdev->os_desc_req->context = cdev; + cdev->os_desc_req->complete = composite_setup_complete; +end: + return ret; +} + void composite_dev_cleanup(struct usb_composite_dev *cdev) { struct usb_gadget_string_container *uc, *tmp; + struct usb_ep *ep, *tmp_ep; list_for_each_entry_safe(uc, tmp, &cdev->gstrings, list) { list_del(&uc->list); kfree(uc); } + if (cdev->os_desc_req) { + if (cdev->os_desc_pending) + usb_ep_dequeue(cdev->gadget->ep0, cdev->os_desc_req); + + kfree(cdev->os_desc_req->buf); + cdev->os_desc_req->buf = NULL; + usb_ep_free_request(cdev->gadget->ep0, cdev->os_desc_req); + cdev->os_desc_req = NULL; + } if (cdev->req) { if (cdev->setup_pending) usb_ep_dequeue(cdev->gadget->ep0, cdev->req); + kfree(cdev->req->buf); + cdev->req->buf = NULL; usb_ep_free_request(cdev->gadget->ep0, cdev->req); + cdev->req = NULL; } cdev->next_string_id = 0; + + /* + * Some UDC backends have a dynamic EP allocation scheme. + * + * In that case, the dispose() callback is used to notify the + * backend that the EPs are no longer in use. + * + * Note: The UDC backend can remove the EP from the ep_list as + * a result, so we need to use the _safe list iterator. + */ + list_for_each_entry_safe(ep, tmp_ep, + &cdev->gadget->ep_list, ep_list) { + if (ep->ops->dispose) + ep->ops->dispose(ep); + } } static int composite_bind(struct usb_gadget *gadget, @@ -1676,6 +2415,7 @@ static int composite_bind(struct usb_gadget *gadget, if (!cdev) return status; + spin_lock_init(&cdev->lock); cdev->gadget = gadget; set_gadget_data(gadget, cdev); INIT_LIST_HEAD(&cdev->configs); @@ -1693,6 +2433,12 @@ static int composite_bind(struct usb_gadget *gadget, if (status < 0) goto fail; + if (cdev->use_os_string) { + status = composite_os_desc_req_prepare(cdev, gadget->ep0); + if (status) + goto fail; + } + update_unchanged_dev_desc(&cdev->desc, composite->dev); /* has userspace failed to provide a serial number? */ @@ -1714,6 +2460,7 @@ static const struct usb_gadget_driver composite_driver_template = { .unbind = composite_unbind, .setup = composite_setup, + .reset = composite_reset, .disconnect = composite_disconnect, }; @@ -1749,8 +2496,9 @@ int usb_composite_probe(struct usb_composite_driver *driver) gadget_driver->function = (char *) driver->name; gadget_driver->driver.name = driver->name; gadget_driver->max_speed = driver->max_speed; + gadget_driver->match_existing_only = true; - return usb_gadget_probe_driver(gadget_driver); + return usb_gadget_register_driver(gadget_driver); } EXPORT_SYMBOL_GPL(usb_composite_probe); @@ -1781,8 +2529,10 @@ void usb_composite_setup_continue(struct usb_composite_dev *cdev) { int value; struct usb_request *req = cdev->req; + unsigned long flags; DBG(cdev, "%s\n", __func__); + spin_lock_irqsave(&cdev->lock, flags); if (cdev->delayed_status == 0) { WARN(cdev, "%s: Unexpected call\n", __func__); @@ -1790,6 +2540,7 @@ void usb_composite_setup_continue(struct usb_composite_dev *cdev) } else if (--cdev->delayed_status == 0) { DBG(cdev, "%s: Completing delayed status\n", __func__); req->length = 0; + req->context = cdev; value = composite_ep0_queue(cdev, req); if (value < 0) { DBG(cdev, "ep_queue --> %d\n", value); @@ -1797,12 +2548,14 @@ void usb_composite_setup_continue(struct usb_composite_dev *cdev) composite_setup_complete(cdev->gadget->ep0, req); } } + + spin_unlock_irqrestore(&cdev->lock, flags); } EXPORT_SYMBOL_GPL(usb_composite_setup_continue); static char *composite_default_mfr(struct usb_gadget *gadget) { - return basprintf("barebox %s", gadget->name); + return basprintf("barebox with %s", gadget->name); } void usb_composite_overwrite_options(struct usb_composite_dev *cdev, diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index b463f79faa..27e4dda52f 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -1,19 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * usb/gadget/config.c -- simplify building config descriptors * * Copyright (C) 2003 David Brownell - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <common.h> -#include <usb/ch9.h> -#include <usb/gadget.h> -#include <usb/composite.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/composite.h> /** * usb_descriptor_fillbuf - fill buffer with descriptors @@ -156,7 +152,8 @@ EXPORT_SYMBOL_GPL(usb_copy_descriptors); int usb_assign_descriptors(struct usb_function *f, struct usb_descriptor_header **fs, struct usb_descriptor_header **hs, - struct usb_descriptor_header **ss) + struct usb_descriptor_header **ss, + struct usb_descriptor_header **ssp) { struct usb_gadget *g = f->config->cdev->gadget; @@ -175,6 +172,11 @@ int usb_assign_descriptors(struct usb_function *f, if (!f->ss_descriptors) goto err; } + if (ssp && gadget_is_superspeed_plus(g)) { + f->ssp_descriptors = usb_copy_descriptors(ssp); + if (!f->ssp_descriptors) + goto err; + } return 0; err: usb_free_all_descriptors(f); diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index ced568921b..ff16abaf12 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -1,12 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * epautoconf.c -- endpoint autoconfiguration for usb gadget drivers * * Copyright (C) 2004 David Brownell - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <init.h> @@ -15,10 +11,10 @@ #include <linux/ctype.h> #include <asm/byteorder.h> -#include <usb/ch9.h> -#include <usb/gadget.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> -#include "gadget_chips.h" +#define gadget_is_pxa(g) (!strcmp("pxa25x_udc", (g)->name)) /* * This should work with endpoints from controller drivers sharing the @@ -186,18 +182,6 @@ ep_matches ( return 1; } -static struct usb_ep * -find_ep (struct usb_gadget *gadget, const char *name) -{ - struct usb_ep *ep; - - list_for_each_entry (ep, &gadget->ep_list, ep_list) { - if (0 == strcmp (ep->name, name)) - return ep; - } - return NULL; -} - /** * usb_ep_autoconfig_ss() - choose an endpoint matching the ep * descriptor and ep companion descriptor @@ -253,34 +237,6 @@ struct usb_ep *usb_ep_autoconfig_ss( type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; - /* First, apply chip-specific "best usage" knowledge. - * This might make a good usb_gadget_ops hook ... - */ - if (gadget_is_net2280 (gadget) && type == USB_ENDPOINT_XFER_INT) { - /* ep-e, ep-f are PIO with only 64 byte fifos */ - ep = find_ep (gadget, "ep-e"); - if (ep && ep_matches(gadget, ep, desc, ep_comp)) - goto found_ep; - ep = find_ep (gadget, "ep-f"); - if (ep && ep_matches(gadget, ep, desc, ep_comp)) - goto found_ep; - - } else if (gadget_is_goku (gadget)) { - if (USB_ENDPOINT_XFER_INT == type) { - /* single buffering is enough */ - ep = find_ep(gadget, "ep3-bulk"); - if (ep && ep_matches(gadget, ep, desc, ep_comp)) - goto found_ep; - } else if (USB_ENDPOINT_XFER_BULK == type - && (USB_DIR_IN & desc->bEndpointAddress)) { - /* DMA may be available */ - ep = find_ep(gadget, "ep2-bulk"); - if (ep && ep_matches(gadget, ep, desc, - ep_comp)) - goto found_ep; - } - } - /* Second, look at endpoints until an unclaimed one looks usable */ list_for_each_entry (ep, &gadget->ep_list, ep_list) { if (ep_matches(gadget, ep, desc, ep_comp)) diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c deleted file mode 100644 index 82a202cc21..0000000000 --- a/drivers/usb/gadget/f_fastboot.c +++ /dev/null @@ -1,1329 +0,0 @@ -/* - * (C) Copyright 2008 - 2009 - * Windriver, <www.windriver.com> - * Tom Rix <Tom.Rix@windriver.com> - * - * Copyright 2011 Sebastian Andrzej Siewior <bigeasy@linutronix.de> - * - * Copyright 2014 Linaro, Ltd. - * Rob Herring <robh@kernel.org> - * - * Copyright 2014 Sascha Hauer <s.hauer@pengutronix.de> - * Ported to barebox - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#define pr_fmt(fmt) "fastboot: " fmt - -#include <common.h> -#include <command.h> -#include <errno.h> -#include <malloc.h> -#include <fcntl.h> -#include <clock.h> -#include <ioctl.h> -#include <libbb.h> -#include <bbu.h> -#include <bootm.h> -#include <dma.h> -#include <fs.h> -#include <libfile.h> -#include <ubiformat.h> -#include <stdlib.h> -#include <file-list.h> -#include <magicvar.h> -#include <linux/sizes.h> -#include <progress.h> -#include <environment.h> -#include <globalvar.h> -#include <restart.h> -#include <console_countdown.h> -#include <image-sparse.h> -#include <usb/ch9.h> -#include <usb/gadget.h> -#include <usb/fastboot.h> -#include <usb/composite.h> -#include <linux/err.h> -#include <linux/compiler.h> -#include <linux/stat.h> -#include <linux/mtd/mtd-abi.h> -#include <linux/mtd/mtd.h> - -#define FASTBOOT_VERSION "0.4" - -#define FASTBOOT_INTERFACE_CLASS 0xff -#define FASTBOOT_INTERFACE_SUB_CLASS 0x42 -#define FASTBOOT_INTERFACE_PROTOCOL 0x03 - -#define FASTBOOT_TMPFILE "/.fastboot.img" - -#define EP_BUFFER_SIZE 4096 - -static unsigned int fastboot_max_download_size = SZ_8M; - -struct fb_variable { - char *name; - char *value; - struct list_head list; -}; - -struct f_fastboot { - struct usb_function func; - - /* IN/OUT EP's and corresponding requests */ - struct usb_ep *in_ep, *out_ep; - struct usb_request *in_req, *out_req; - struct file_list *files; - int (*cmd_exec)(struct f_fastboot *, const char *cmd); - int (*cmd_flash)(struct f_fastboot *, struct file_list_entry *entry, - const char *filename, const void *buf, size_t len); - int download_fd; - void *buf; - bool active; - - size_t download_bytes; - size_t download_size; - struct list_head variables; -}; - -static inline bool fastboot_download_to_buf(struct f_fastboot *f_fb) -{ - if (IS_ENABLED(CONFIG_USB_GADGET_FASTBOOT_BUF)) - return true; - else - return false; -} - -static inline struct f_fastboot *func_to_fastboot(struct usb_function *f) -{ - return container_of(f, struct f_fastboot, func); -} - -static struct usb_endpoint_descriptor fs_ep_in = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = cpu_to_le16(64), - .bInterval = 0x00, -}; - -static struct usb_endpoint_descriptor fs_ep_out = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = cpu_to_le16(64), - .bInterval = 0x00, -}; - -static struct usb_endpoint_descriptor hs_ep_in = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = cpu_to_le16(512), - .bInterval = 0x00, -}; - -static struct usb_endpoint_descriptor hs_ep_out = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = cpu_to_le16(512), - .bInterval = 0x00, -}; - -static struct usb_interface_descriptor interface_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = 0x00, - .bAlternateSetting = 0x00, - .bNumEndpoints = 0x02, - .bInterfaceClass = FASTBOOT_INTERFACE_CLASS, - .bInterfaceSubClass = FASTBOOT_INTERFACE_SUB_CLASS, - .bInterfaceProtocol = FASTBOOT_INTERFACE_PROTOCOL, -}; - -static struct usb_descriptor_header *fb_fs_descs[] = { - (struct usb_descriptor_header *)&interface_desc, - (struct usb_descriptor_header *)&fs_ep_in, - (struct usb_descriptor_header *)&fs_ep_out, - NULL, -}; - -static struct usb_descriptor_header *fb_hs_descs[] = { - (struct usb_descriptor_header *)&interface_desc, - (struct usb_descriptor_header *)&hs_ep_in, - (struct usb_descriptor_header *)&hs_ep_out, - NULL, -}; - -/* - * static strings, in UTF-8 - */ -static const char fastboot_name[] = "Android Fastboot"; - -static struct usb_string fastboot_string_defs[] = { - [0].s = fastboot_name, - { } /* end of list */ -}; - -static struct usb_gadget_strings stringtab_fastboot = { - .language = 0x0409, /* en-us */ - .strings = fastboot_string_defs, -}; - -static struct usb_gadget_strings *fastboot_strings[] = { - &stringtab_fastboot, - NULL, -}; - -static void rx_handler_command(struct usb_ep *ep, struct usb_request *req); - -static void fastboot_complete(struct usb_ep *ep, struct usb_request *req) -{ -} - -static struct usb_request *fastboot_alloc_request(struct usb_ep *ep) -{ - struct usb_request *req; - - req = usb_ep_alloc_request(ep); - if (!req) - return NULL; - - req->length = EP_BUFFER_SIZE; - req->buf = dma_alloc(EP_BUFFER_SIZE); - if (!req->buf) { - usb_ep_free_request(ep, req); - return NULL; - } - memset(req->buf, 0, EP_BUFFER_SIZE); - - return req; -} - -static void fb_setvar(struct fb_variable *var, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - var->value = bvasprintf(fmt, ap); - va_end(ap); -} - -static struct fb_variable *fb_addvar(struct f_fastboot *f_fb, const char *fmt, ...) -{ - struct fb_variable *var = xzalloc(sizeof(*var)); - va_list ap; - - va_start(ap, fmt); - var->name = bvasprintf(fmt, ap); - va_end(ap); - - list_add_tail(&var->list, &f_fb->variables); - - return var; -} - -static int fastboot_add_partition_variables(struct f_fastboot *f_fb, - struct file_list_entry *fentry) -{ - struct stat s; - size_t size = 0; - int fd, ret; - struct mtd_info_user mtdinfo; - char *type = NULL; - struct fb_variable *var; - - ret = stat(fentry->filename, &s); - if (ret) { - device_detect_by_name(devpath_to_name(fentry->filename)); - ret = stat(fentry->filename, &s); - } - - if (ret) { - if (fentry->flags & FILE_LIST_FLAG_CREATE) { - ret = 0; - type = "file"; - goto out; - } - - goto out; - } - - fd = open(fentry->filename, O_RDWR); - if (fd < 0) { - ret = -EINVAL; - goto out; - } - - size = s.st_size; - - ret = ioctl(fd, MEMGETINFO, &mtdinfo); - - close(fd); - - if (!ret) { - switch (mtdinfo.type) { - case MTD_NANDFLASH: - type = "NAND-flash"; - break; - case MTD_NORFLASH: - type = "NOR-flash"; - break; - case MTD_UBIVOLUME: - type = "UBI"; - break; - default: - type = "flash"; - break; - } - - goto out; - } - - type = "basic"; - ret = 0; - -out: - if (ret) - return ret; - - var = fb_addvar(f_fb, "partition-size:%s", fentry->name); - fb_setvar(var, "%08zx", size); - var = fb_addvar(f_fb, "partition-type:%s", fentry->name); - fb_setvar(var, "%s", type); - - return ret; -} - -static int fastboot_add_bbu_variables(struct bbu_handler *handler, void *ctx) -{ - struct f_fastboot *f_fb = ctx; - char *name; - int ret; - - name = basprintf("bbu-%s", handler->name); - - ret = file_list_add_entry(f_fb->files, name, handler->devicefile, 0); - - free(name); - - return ret; -} - -static int fastboot_bind(struct usb_configuration *c, struct usb_function *f) -{ - struct usb_composite_dev *cdev = c->cdev; - int id, ret; - struct usb_gadget *gadget = c->cdev->gadget; - struct f_fastboot *f_fb = func_to_fastboot(f); - struct usb_string *us; - const struct usb_function_instance *fi = f->fi; - struct f_fastboot_opts *opts = container_of(fi, struct f_fastboot_opts, func_inst); - struct file_list_entry *fentry; - struct fb_variable *var; - - f_fb->files = opts->files; - f_fb->cmd_exec = opts->cmd_exec; - f_fb->cmd_flash = opts->cmd_flash; - - var = fb_addvar(f_fb, "version"); - fb_setvar(var, "0.4"); - var = fb_addvar(f_fb, "bootloader-version"); - fb_setvar(var, release_string); - if (IS_ENABLED(CONFIG_USB_GADGET_FASTBOOT_SPARSE)) { - var = fb_addvar(f_fb, "max-download-size"); - fb_setvar(var, "%u", fastboot_max_download_size); - } - - if (IS_ENABLED(CONFIG_BAREBOX_UPDATE) && opts->export_bbu) - bbu_handlers_iterate(fastboot_add_bbu_variables, f_fb); - - file_list_for_each_entry(f_fb->files, fentry) { - ret = fastboot_add_partition_variables(f_fb, fentry); - if (ret) - return ret; - } - - /* DYNAMIC interface numbers assignments */ - id = usb_interface_id(c, f); - if (id < 0) - return id; - interface_desc.bInterfaceNumber = id; - - id = usb_string_id(c->cdev); - if (id < 0) - return id; - fastboot_string_defs[0].id = id; - interface_desc.iInterface = id; - - us = usb_gstrings_attach(cdev, fastboot_strings, 1); - if (IS_ERR(us)) { - ret = PTR_ERR(us); - return ret; - } - - f_fb->in_ep = usb_ep_autoconfig(gadget, &fs_ep_in); - if (!f_fb->in_ep) - return -ENODEV; - f_fb->in_ep->driver_data = c->cdev; - - f_fb->out_ep = usb_ep_autoconfig(gadget, &fs_ep_out); - if (!f_fb->out_ep) - return -ENODEV; - f_fb->out_ep->driver_data = c->cdev; - - hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress; - hs_ep_in.bEndpointAddress = fs_ep_in.bEndpointAddress; - - f_fb->out_req = fastboot_alloc_request(f_fb->out_ep); - if (!f_fb->out_req) { - puts("failed to alloc out req\n"); - ret = -EINVAL; - return ret; - } - - f_fb->out_req->complete = rx_handler_command; - f_fb->out_req->context = f_fb; - - f_fb->in_req = fastboot_alloc_request(f_fb->in_ep); - if (!f_fb->in_req) { - puts("failed alloc req in\n"); - ret = -EINVAL; - return ret; - } - f_fb->in_req->complete = fastboot_complete; - - ret = usb_assign_descriptors(f, fb_fs_descs, fb_hs_descs, NULL); - if (ret) - return ret; - - return 0; -} - -static void fastboot_unbind(struct usb_configuration *c, struct usb_function *f) -{ - struct f_fastboot *f_fb = func_to_fastboot(f); - struct fb_variable *var, *tmp; - - usb_ep_dequeue(f_fb->in_ep, f_fb->in_req); - free(f_fb->in_req->buf); - usb_ep_free_request(f_fb->in_ep, f_fb->in_req); - f_fb->in_req = NULL; - - usb_ep_dequeue(f_fb->out_ep, f_fb->out_req); - free(f_fb->out_req->buf); - usb_ep_free_request(f_fb->out_ep, f_fb->out_req); - f_fb->out_req = NULL; - - list_for_each_entry_safe(var, tmp, &f_fb->variables, list) { - free(var->name); - free(var->value); - list_del(&var->list); - free(var); - } - - f_fb->active = false; -} - -static void fastboot_disable(struct usb_function *f) -{ - struct f_fastboot *f_fb = func_to_fastboot(f); - - usb_ep_disable(f_fb->out_ep); - usb_ep_disable(f_fb->in_ep); -} - -static int fastboot_set_alt(struct usb_function *f, - unsigned interface, unsigned alt) -{ - int ret; - struct f_fastboot *f_fb = func_to_fastboot(f); - - pr_debug("%s: func: %s intf: %d alt: %d\n", - __func__, f->name, interface, alt); - - ret = config_ep_by_speed(f->config->cdev->gadget, f, - f_fb->out_ep); - if (ret) - return ret; - - ret = usb_ep_enable(f_fb->out_ep); - if (ret) { - pr_err("failed to enable out ep: %s\n", strerror(-ret)); - return ret; - } - - ret = config_ep_by_speed(f->config->cdev->gadget, f, - f_fb->in_ep); - if (ret) - return ret; - - ret = usb_ep_enable(f_fb->in_ep); - if (ret) { - pr_err("failed to enable in ep: %s\n", strerror(-ret)); - return ret; - } - - memset(f_fb->out_req->buf, 0, EP_BUFFER_SIZE); - ret = usb_ep_queue(f_fb->out_ep, f_fb->out_req); - if (ret) - goto err; - - return 0; -err: - fastboot_disable(f); - return ret; -} - -static struct f_fastboot *g_f_fb; - -static void fastboot_free_func(struct usb_function *f) -{ - struct f_fastboot *f_fb = container_of(f, struct f_fastboot, func); - - if (g_f_fb == f_fb) - g_f_fb = NULL; - - free(f_fb); -} - -/* - * A "oem exec bootm" or similar commands will stop barebox. Tell the - * fastboot command on the other side so that it doesn't run into a - * timeout. - */ -static void fastboot_shutdown(void) -{ - struct f_fastboot *f_fb = g_f_fb; - - if (!f_fb || !f_fb->active) - return; - - fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, "barebox shutting down"); - fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, ""); -} - -early_exitcall(fastboot_shutdown); - -static struct usb_function *fastboot_alloc_func(struct usb_function_instance *fi) -{ - struct f_fastboot *f_fb; - - f_fb = xzalloc(sizeof(*f_fb)); - - INIT_LIST_HEAD(&f_fb->variables); - - f_fb->func.name = "fastboot"; - f_fb->func.strings = fastboot_strings; - f_fb->func.bind = fastboot_bind; - f_fb->func.set_alt = fastboot_set_alt; - f_fb->func.disable = fastboot_disable; - f_fb->func.unbind = fastboot_unbind; - f_fb->func.free_func = fastboot_free_func; - - if (!g_f_fb) - g_f_fb = f_fb; - - return &f_fb->func; -} - -static void fastboot_free_instance(struct usb_function_instance *fi) -{ - struct f_fastboot_opts *opts; - - opts = container_of(fi, struct f_fastboot_opts, func_inst); - kfree(opts); -} - -static struct usb_function_instance *fastboot_alloc_instance(void) -{ - struct f_fastboot_opts *opts; - - opts = xzalloc(sizeof(*opts)); - opts->func_inst.free_func_inst = fastboot_free_instance; - - return &opts->func_inst; -} - -DECLARE_USB_FUNCTION_INIT(fastboot, fastboot_alloc_instance, fastboot_alloc_func); - -static int fastboot_tx_write(struct f_fastboot *f_fb, const char *buffer, unsigned int buffer_size) -{ - struct usb_request *in_req = f_fb->in_req; - uint64_t start; - int ret; - - memcpy(in_req->buf, buffer, buffer_size); - in_req->length = buffer_size; - - ret = usb_ep_queue(f_fb->in_ep, in_req); - if (ret) - pr_err("Error %d on queue\n", ret); - - start = get_time_ns(); - - while (in_req->status == -EINPROGRESS) { - if (is_timeout(start, 2 * SECOND)) - return -ETIMEDOUT; - usb_gadget_poll(); - } - - if (in_req->status) - pr_err("Failed to send answer: %d\n", in_req->status); - - return 0; -} - -static char *fastboot_msg[] = { - [FASTBOOT_MSG_OKAY] = "OKAY", - [FASTBOOT_MSG_FAIL] = "FAIL", - [FASTBOOT_MSG_INFO] = "INFO", - [FASTBOOT_MSG_DATA] = "DATA", -}; - -int fastboot_tx_print(struct f_fastboot *f_fb, enum fastboot_msg_type type, - const char *fmt, ...) -{ - struct va_format vaf; - char buf[64]; - va_list ap; - int n; - const char *msg = fastboot_msg[type]; - - va_start(ap, fmt); - vaf.fmt = fmt; - vaf.va = ≈ - - n = snprintf(buf, 64, "%s%pV", msg, &vaf); - - switch (type) { - case FASTBOOT_MSG_OKAY: - f_fb->active = false; - break; - case FASTBOOT_MSG_FAIL: - f_fb->active = false; - pr_err("%pV\n", &vaf); - break; - case FASTBOOT_MSG_INFO: - pr_info("%pV\n", &vaf); - break; - case FASTBOOT_MSG_DATA: - break; - } - - va_end(ap); - - if (n > 64) - n = 64; - - return fastboot_tx_write(f_fb, buf, n); -} - -static void cb_reboot(struct f_fastboot *f_fb, const char *cmd) -{ - restart_machine(); -} - -static int strcmp_l1(const char *s1, const char *s2) -{ - if (!s1 || !s2) - return -1; - return strncmp(s1, s2, strlen(s1)); -} - -static void cb_getvar(struct f_fastboot *f_fb, const char *cmd) -{ - struct fb_variable *var; - - pr_debug("getvar: \"%s\"\n", cmd); - - if (!strcmp_l1(cmd, "all")) { - list_for_each_entry(var, &f_fb->variables, list) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, "%s: %s", - var->name, var->value); - } - fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, ""); - return; - } - - list_for_each_entry(var, &f_fb->variables, list) { - if (!strcmp(cmd, var->name)) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, var->value); - return; - } - } - - fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, ""); -} - -static int rx_bytes_expected(struct f_fastboot *f_fb) -{ - int remaining = f_fb->download_size - f_fb->download_bytes; - - if (remaining >= EP_BUFFER_SIZE) - return EP_BUFFER_SIZE; - - return ALIGN(remaining, f_fb->out_ep->maxpacket); -} - -static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req) -{ - struct f_fastboot *f_fb = req->context; - const unsigned char *buffer = req->buf; - int ret; - - if (req->status != 0) { - pr_err("Bad status: %d\n", req->status); - return; - } - - if (fastboot_download_to_buf(f_fb)) { - memcpy(f_fb->buf + f_fb->download_bytes, buffer, req->actual); - } else { - ret = write(f_fb->download_fd, buffer, req->actual); - if (ret < 0) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, strerror(-ret)); - return; - } - } - - f_fb->download_bytes += req->actual; - - req->length = rx_bytes_expected(f_fb); - - show_progress(f_fb->download_bytes); - - /* Check if transfer is done */ - if (f_fb->download_bytes >= f_fb->download_size) { - req->complete = rx_handler_command; - req->length = EP_BUFFER_SIZE; - close(f_fb->download_fd); - - printf("\n"); - - fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, "Downloading %d bytes finished", - f_fb->download_bytes); - - fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, ""); - } - - req->actual = 0; - usb_ep_queue(ep, req); -} - -static void cb_download(struct f_fastboot *f_fb, const char *cmd) -{ - f_fb->download_size = simple_strtoul(cmd, NULL, 16); - f_fb->download_bytes = 0; - - fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, "Downloading %d bytes...", - f_fb->download_size); - - init_progression_bar(f_fb->download_size); - - if (fastboot_download_to_buf(f_fb)) { - free(f_fb->buf); - f_fb->buf = malloc(f_fb->download_size); - if (!f_fb->buf) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "not enough memory"); - return; - } - } else { - f_fb->download_fd = open(FASTBOOT_TMPFILE, O_WRONLY | O_CREAT | O_TRUNC); - if (f_fb->download_fd < 0) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "internal error"); - return; - } - } - - if (!f_fb->download_size) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "data invalid size"); - } else { - struct usb_request *req = f_fb->out_req; - fastboot_tx_print(f_fb, FASTBOOT_MSG_DATA, - "%08x", f_fb->download_size); - req->complete = rx_handler_dl_image; - req->length = rx_bytes_expected(f_fb); - } -} - -static void __maybe_unused cb_boot(struct f_fastboot *f_fb, const char *opt) -{ - int ret; - struct bootm_data data = { - .initrd_address = UIMAGE_INVALID_ADDRESS, - .os_address = UIMAGE_SOME_ADDRESS, - }; - - fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, "Booting kernel..\n"); - - globalvar_set_match("linux.bootargs.dyn.", ""); - globalvar_set_match("bootm.image", ""); - - data.os_file = xstrdup(FASTBOOT_TMPFILE); - - ret = bootm_boot(&data); - - if (ret) - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, "Booting failed: %s", - strerror(-ret)); - else - fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, ""); -} - -static struct mtd_info *get_mtd(struct f_fastboot *f_fb, const char *filename) -{ - int fd, ret; - struct mtd_info_user meminfo; - - fd = open(filename, O_RDONLY); - if (fd < 0) - return ERR_PTR(-errno); - - ret = ioctl(fd, MEMGETINFO, &meminfo); - - close(fd); - - if (ret) - return ERR_PTR(ret); - - return meminfo.mtd; -} - -static int do_ubiformat(struct f_fastboot *f_fb, struct mtd_info *mtd, - const char *file, const void *buf, size_t len) -{ - struct ubiformat_args args = { - .yes = 1, - .image = file, - .image_buf = buf, - .image_size = len, - }; - - if (!file) - args.novtbl = 1; - - if (!IS_ENABLED(CONFIG_UBIFORMAT)) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "ubiformat is not available"); - return -ENODEV; - } - - return ubiformat(mtd, &args); -} - - -static int check_ubi(struct f_fastboot *f_fb, struct file_list_entry *fentry, - enum filetype filetype) -{ - struct mtd_info *mtd; - - mtd = get_mtd(f_fb, fentry->filename); - - /* - * Issue a warning when we are about to write a UBI image to a MTD device - * and the FILE_LIST_FLAG_UBI is not given as this means we loose all - * erase counters. - */ - if (!IS_ERR(mtd) && filetype == filetype_ubi && - !(fentry->flags & FILE_LIST_FLAG_UBI)) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, - "writing UBI image to MTD device, " - "add the 'u' "); - fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, - "flag to the partition description"); - return 0; - } - - if (!(fentry->flags & FILE_LIST_FLAG_UBI)) - return 0; - - if (!IS_ENABLED(CONFIG_UBIFORMAT)) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "ubiformat not available"); - return -ENOSYS; - } - - if (IS_ERR(mtd)) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "UBI flag given on non-MTD device"); - return -EINVAL; - } - - if (filetype == filetype_ubi) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, - "This is an UBI image..."); - return 1; - } else { - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "This is no UBI image but %s", - file_type_to_string(filetype)); - return -EINVAL; - } -} - -static int fastboot_handle_sparse(struct f_fastboot *f_fb, - struct file_list_entry *fentry) -{ - struct sparse_image_ctx *sparse; - void *buf = NULL; - int ret, fd; - unsigned int flags = O_RDWR; - int bufsiz = SZ_128K; - struct stat s; - struct mtd_info *mtd = NULL; - - ret = stat(fentry->filename, &s); - if (ret) { - if (fentry->flags & FILE_LIST_FLAG_CREATE) - flags |= O_CREAT; - else - return ret; - } - - fd = open(fentry->filename, flags); - if (fd < 0) - return -errno; - - ret = fstat(fd, &s); - if (ret) - goto out_close_fd; - - sparse = sparse_image_open(FASTBOOT_TMPFILE); - if (IS_ERR(sparse)) { - pr_err("Cannot open sparse image\n"); - ret = PTR_ERR(sparse); - goto out_close_fd; - } - - if (S_ISREG(s.st_mode)) { - ret = ftruncate(fd, sparse_image_size(sparse)); - if (ret) - goto out; - } - - buf = malloc(bufsiz); - if (!buf) { - ret = -ENOMEM; - goto out; - } - - if (fentry->flags & FILE_LIST_FLAG_UBI) { - mtd = get_mtd(f_fb, fentry->filename); - if (IS_ERR(mtd)) { - ret = PTR_ERR(mtd); - goto out; - } - } - - while (1) { - int retlen; - loff_t pos; - - ret = sparse_image_read(sparse, buf, &pos, bufsiz, &retlen); - if (ret) - goto out; - if (!retlen) - break; - - if (pos == 0) { - ret = check_ubi(f_fb, fentry, file_detect_type(buf, retlen)); - if (ret < 0) - goto out; - } - - if (fentry->flags & FILE_LIST_FLAG_UBI) { - if (!IS_ENABLED(CONFIG_UBIFORMAT)) { - ret = -ENOSYS; - goto out; - } - - if (pos == 0) { - ret = do_ubiformat(f_fb, mtd, NULL, NULL, 0); - if (ret) - goto out; - } - - ret = ubiformat_write(mtd, buf, retlen, pos); - if (ret) - goto out; - } else { - pos = lseek(fd, pos, SEEK_SET); - if (pos == -1) { - ret = -errno; - goto out; - } - - ret = write_full(fd, buf, retlen); - if (ret < 0) - goto out; - } - } - - ret = 0; - -out: - free(buf); - sparse_image_close(sparse); -out_close_fd: - close(fd); - - return ret; -} - -static void cb_flash(struct f_fastboot *f_fb, const char *cmd) -{ - struct file_list_entry *fentry; - int ret; - const char *filename = NULL, *sourcefile; - enum filetype filetype; - - if (fastboot_download_to_buf(f_fb)) { - sourcefile = NULL; - filetype = file_detect_type(f_fb->buf, f_fb->download_bytes); - } else { - sourcefile = FASTBOOT_TMPFILE; - filetype = file_name_detect_type(FASTBOOT_TMPFILE); - } - - fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, "Copying file to %s...", - cmd); - - fentry = file_list_entry_by_name(f_fb->files, cmd); - - if (!fentry) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, "No such partition: %s", - cmd); - ret = -ENOENT; - goto out; - } - - if (f_fb->cmd_flash) { - ret = f_fb->cmd_flash(f_fb, fentry, sourcefile, f_fb->buf, - f_fb->download_size); - if (ret != FASTBOOT_CMD_FALLTHROUGH) - goto out; - } - - filename = fentry->filename; - - if (filetype == filetype_android_sparse) { - if (!IS_ENABLED(CONFIG_USB_GADGET_FASTBOOT_SPARSE) || - fastboot_download_to_buf(f_fb)) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "sparse image not supported"); - ret = -EOPNOTSUPP; - goto out; - } - - ret = fastboot_handle_sparse(f_fb, fentry); - if (ret) - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "writing sparse image: %s", - strerror(-ret)); - - goto out; - } - - ret = check_ubi(f_fb, fentry, filetype); - if (ret < 0) - goto out; - - if (ret > 0) { - struct mtd_info *mtd; - - mtd = get_mtd(f_fb, fentry->filename); - - ret = do_ubiformat(f_fb, mtd, sourcefile, f_fb->buf, - f_fb->download_size); - if (ret) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "write partition: %s", - strerror(-ret)); - goto out; - } - - goto out; - } - - if (IS_ENABLED(CONFIG_BAREBOX_UPDATE) && filetype_is_barebox_image(filetype)) { - struct bbu_handler *handler; - struct bbu_data data = { - .devicefile = filename, - .flags = BBU_FLAG_YES, - }; - - handler = bbu_find_handler_by_device(data.devicefile); - if (!handler) - goto copy; - - fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, - "This is a barebox image..."); - - if (fastboot_download_to_buf(f_fb)) { - data.len = f_fb->download_size; - } else { - ret = read_file_2(sourcefile, &data.len, &f_fb->buf, - f_fb->download_size); - if (ret) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "reading barebox"); - goto out; - } - } - - data.image = f_fb->buf; - data.imagefile = sourcefile; - - ret = barebox_update(&data, handler); - - if (ret) - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "update barebox: %s", strerror(-ret)); - - goto out; - } - -copy: - if (fastboot_download_to_buf(f_fb)) - ret = write_file(filename, f_fb->buf, f_fb->download_size); - else - ret = copy_file(FASTBOOT_TMPFILE, filename, 1); - - if (ret) - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "write partition: %s", strerror(-ret)); - -out: - if (!ret) - fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, ""); - - free(f_fb->buf); - f_fb->buf = NULL; - - if (!fastboot_download_to_buf(f_fb)) - unlink(FASTBOOT_TMPFILE); -} - -static void cb_erase(struct f_fastboot *f_fb, const char *cmd) -{ - struct file_list_entry *fentry; - int ret; - const char *filename = NULL; - int fd; - - fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, "Erasing %s...", cmd); - - file_list_for_each_entry(f_fb->files, fentry) { - if (!strcmp(cmd, fentry->name)) { - filename = fentry->filename; - break; - } - } - - if (!filename) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "No such partition: %s", cmd); - return; - } - - fd = open(filename, O_RDWR); - if (fd < 0) - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, strerror(-fd)); - - ret = erase(fd, ERASE_SIZE_ALL, 0); - - close(fd); - - if (ret) - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "cannot erase partition %s: %s", - filename, strerror(-ret)); - else - fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, ""); -} - -struct cmd_dispatch_info { - char *cmd; - void (*cb)(struct f_fastboot *f_fb, const char *opt); -}; - -static void fb_run_command(struct f_fastboot *f_fb, const char *cmdbuf, - const struct cmd_dispatch_info *cmds, int num_commands) -{ - const struct cmd_dispatch_info *cmd; - int i; - - console_countdown_abort(); - - for (i = 0; i < num_commands; i++) { - cmd = &cmds[i]; - - if (!strcmp_l1(cmd->cmd, cmdbuf)) { - cmd->cb(f_fb, cmdbuf + strlen(cmd->cmd)); - - return; - } - } - - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, "unknown command %s", - cmdbuf); -} - -static void cb_oem_getenv(struct f_fastboot *f_fb, const char *cmd) -{ - const char *value; - - pr_debug("%s: \"%s\"\n", __func__, cmd); - - value = getenv(cmd); - - fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, value ? value : ""); - fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, ""); -} - -static void cb_oem_setenv(struct f_fastboot *f_fb, const char *cmd) -{ - char *var = xstrdup(cmd); - char *value; - int ret; - - pr_debug("%s: \"%s\"\n", __func__, cmd); - - value = strchr(var, '='); - if (!value) { - ret = -EINVAL; - goto out; - } - - *value++ = 0; - - ret = setenv(var, value); - if (ret) - goto out; - - fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, ""); -out: - free(var); - - if (ret) - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, strerror(-ret)); -} - -static void cb_oem_exec(struct f_fastboot *f_fb, const char *cmd) -{ - int ret; - - if (!IS_ENABLED(CONFIG_COMMAND_SUPPORT)) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "no command support available"); - return; - } - - ret = run_command(cmd); - if (ret < 0) - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, strerror(-ret)); - else if (ret > 0) - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, ""); - else - fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, ""); -} - -static const struct cmd_dispatch_info cmd_oem_dispatch_info[] = { - { - .cmd = "getenv ", - .cb = cb_oem_getenv, - }, { - .cmd = "setenv ", - .cb = cb_oem_setenv, - }, { - .cmd = "exec ", - .cb = cb_oem_exec, - }, -}; - -static void cb_oem(struct f_fastboot *f_fb, const char *cmd) -{ - pr_debug("%s: \"%s\"\n", __func__, cmd); - - fb_run_command(f_fb, cmd, cmd_oem_dispatch_info, ARRAY_SIZE(cmd_oem_dispatch_info)); -} - -static const struct cmd_dispatch_info cmd_dispatch_info[] = { - { - .cmd = "reboot", - .cb = cb_reboot, - }, { - .cmd = "getvar:", - .cb = cb_getvar, - }, { - .cmd = "download:", - .cb = cb_download, -#if defined(CONFIG_BOOTM) - }, { - .cmd = "boot", - .cb = cb_boot, -#endif - }, { - .cmd = "flash:", - .cb = cb_flash, - }, { - .cmd = "erase:", - .cb = cb_erase, - }, { - .cmd = "oem ", - .cb = cb_oem, - }, -}; - -static void rx_handler_command(struct usb_ep *ep, struct usb_request *req) -{ - char *cmdbuf = req->buf; - struct f_fastboot *f_fb = req->context; - int ret; - - if (req->status != 0) - return; - - f_fb->active = true; - - *(cmdbuf + req->actual) = 0; - - if (f_fb->cmd_exec) { - ret = f_fb->cmd_exec(f_fb, cmdbuf); - if (ret != FASTBOOT_CMD_FALLTHROUGH) - goto done; - } - - fb_run_command(f_fb, cmdbuf, cmd_dispatch_info, - ARRAY_SIZE(cmd_dispatch_info)); -done: - *cmdbuf = '\0'; - req->actual = 0; - memset(req->buf, 0, EP_BUFFER_SIZE); - usb_ep_queue(ep, req); -} - -static int fastboot_globalvars_init(void) -{ - if (IS_ENABLED(CONFIG_USB_GADGET_FASTBOOT_SPARSE)) - globalvar_add_simple_int("usbgadget.fastboot_max_download_size", - &fastboot_max_download_size, "%u"); - - return 0; -} - -device_initcall(fastboot_globalvars_init); - -BAREBOX_MAGICVAR_NAMED(global_usbgadget_fastboot_max_download_size, - global.usbgadget.fastboot_max_download_size, - "Fastboot maximum download size"); diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile new file mode 100644 index 0000000000..de306b929f --- /dev/null +++ b/drivers/usb/gadget/function/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_USB_GADGET_SERIAL) += u_serial.o f_serial.o f_acm.o +obj-$(CONFIG_USB_GADGET_DFU) += dfu.o +obj-$(CONFIG_USB_GADGET_FASTBOOT) += f_fastboot.o +obj-$(CONFIG_USB_GADGET_MASS_STORAGE) += f_mass_storage.o storage_common.o diff --git a/drivers/usb/gadget/dfu.c b/drivers/usb/gadget/function/dfu.c index c2b3d481af..3a6d2cf385 100644 --- a/drivers/usb/gadget/dfu.c +++ b/drivers/usb/gadget/function/dfu.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * (C) 2007 by OpenMoko, Inc. * Author: Harald Welte <laforge@openmoko.org> @@ -5,20 +6,6 @@ * based on existing SAM7DFU code from OpenPCD: * (C) Copyright 2006 by Harald Welte <hwelte@hmw-consulting.de> * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * * TODO: * - make NAND support reasonably self-contained and put in apropriate * ifdefs @@ -35,18 +22,19 @@ * checking? * - make 'dnstate' attached to 'struct usb_device_instance' */ +#define pr_fmt(fmt) "dfu: " fmt #include <dma.h> #include <asm/byteorder.h> -#include <usb/composite.h> +#include <linux/usb/composite.h> #include <linux/types.h> #include <linux/list.h> -#include <usb/gadget.h> +#include <linux/usb/gadget.h> #include <linux/stat.h> #include <libfile.h> #include <linux/err.h> -#include <usb/ch9.h> -#include <usb/dfu.h> +#include <linux/usb/ch9.h> +#include <linux/usb/dfu.h> #include <config.h> #include <common.h> #include <malloc.h> @@ -55,6 +43,9 @@ #include <libbb.h> #include <init.h> #include <fs.h> +#include <ioctl.h> +#include <linux/mtd/mtd-abi.h> +#include <work.h> #define USB_DT_DFU 0x21 @@ -132,6 +123,10 @@ struct file_list_entry *dfu_file_entry; static int dfufd = -EINVAL; static struct file_list *dfu_files; static int dfudetach; +static struct mtd_info_user dfu_mtdinfo; +static loff_t dfu_written; +static loff_t dfu_erased; +static int prog_erase; /* USB DFU functional descriptor */ static struct usb_dfu_func_descriptor usb_dfu_func = { @@ -150,6 +145,7 @@ struct f_dfu { u8 dfu_state; u8 dfu_status; struct usb_request *dnreq; + struct work_queue wq; }; static inline struct f_dfu *func_to_dfu(struct usb_function *f) @@ -170,6 +166,192 @@ static struct usb_gadget_strings *dfu_strings[] = { }; static void dn_complete(struct usb_ep *ep, struct usb_request *req); +static void up_complete(struct usb_ep *ep, struct usb_request *req); +static void dfu_cleanup(struct f_dfu *dfu); + +struct dfu_work { + struct work_struct work; + struct f_dfu *dfu; + void (*task)(struct dfu_work *dw); + size_t len; + uint8_t *rbuf; + uint8_t wbuf[CONFIG_USBD_DFU_XFER_SIZE]; +}; + +static void dfu_do_work(struct work_struct *w) +{ + struct dfu_work *dw = container_of(w, struct dfu_work, work); + struct f_dfu *dfu = dw->dfu; + + if (dfu->dfu_state != DFU_STATE_dfuERROR && dfu->dfu_status == DFU_STATUS_OK) + dw->task(dw); + else + pr_debug("skip work\n"); + + free(dw); +} + +static void dfu_work_cancel(struct work_struct *w) +{ + struct dfu_work *dw = container_of(w, struct dfu_work, work); + + free(dw); +} + +static void dfu_do_write(struct dfu_work *dw) +{ + struct f_dfu *dfu = dw->dfu; + ssize_t size, wlen = dw->len; + ssize_t ret; + + pr_debug("do write\n"); + + if (prog_erase && (dfu_written + wlen) > dfu_erased) { + size = roundup(wlen, dfu_mtdinfo.erasesize); + ret = erase(dfufd, size, dfu_erased); + dfu_erased += size; + if (ret && ret != -ENOSYS) { + perror("erase"); + dfu->dfu_state = DFU_STATE_dfuERROR; + dfu->dfu_status = DFU_STATUS_errERASE; + return; + } + } + + dfu_written += wlen; + ret = write(dfufd, dw->wbuf, wlen); + if (ret < wlen) { + perror("write"); + dfu->dfu_state = DFU_STATE_dfuERROR; + dfu->dfu_status = DFU_STATUS_errWRITE; + } +} + +static void dfu_do_read(struct dfu_work *dw) +{ + struct f_dfu *dfu = dw->dfu; + struct usb_composite_dev *cdev = dfu->func.config->cdev; + ssize_t size, rlen = dw->len; + + pr_debug("do read\n"); + + size = read(dfufd, dfu->dnreq->buf, rlen); + dfu->dnreq->length = size; + if (size < 0) { + perror("read"); + dfu->dnreq->length = 0; + dfu->dfu_state = DFU_STATE_dfuERROR; + dfu->dfu_status = DFU_STATUS_errFILE; + } else if (size < rlen) { + /* this is the last chunk, go to IDLE and close file */ + dfu_cleanup(dfu); + } + + dfu->dnreq->complete = up_complete; + usb_ep_queue(cdev->gadget->ep0, dfu->dnreq); +} + +static void dfu_do_open_dnload(struct dfu_work *dw) +{ + struct f_dfu *dfu = dw->dfu; + int ret; + + pr_debug("do open dnload\n"); + + if (dfu_file_entry->flags & FILE_LIST_FLAG_SAFE) { + dfufd = open(DFU_TEMPFILE, O_WRONLY | O_CREAT); + } else { + unsigned flags = O_WRONLY; + + if (dfu_file_entry->flags & FILE_LIST_FLAG_CREATE) + flags |= O_CREAT | O_TRUNC; + + dfufd = open(dfu_file_entry->filename, flags); + } + + if (dfufd < 0) { + perror("open"); + dfu->dfu_state = DFU_STATE_dfuERROR; + dfu->dfu_status = DFU_STATUS_errFILE; + return; + } + + if (!(dfu_file_entry->flags & FILE_LIST_FLAG_SAFE)) { + ret = ioctl(dfufd, MEMGETINFO, &dfu_mtdinfo); + if (!ret) /* file is on a mtd device */ + prog_erase = 1; + } +} + +static void dfu_do_open_upload(struct dfu_work *dw) +{ + struct f_dfu *dfu = dw->dfu; + + pr_debug("do open upload\n"); + + dfufd = open(dfu_file_entry->filename, O_RDONLY); + if (dfufd < 0) { + perror("open"); + dfu->dfu_state = DFU_STATE_dfuERROR; + dfu->dfu_status = DFU_STATUS_errFILE; + } +} + +static void dfu_do_close(struct dfu_work *dw) +{ + struct stat s; + + pr_debug("do close\n"); + + if (dfufd > 0) { + close(dfufd); + dfufd = -EINVAL; + } + + if (!stat(DFU_TEMPFILE, &s)) + unlink(DFU_TEMPFILE); + + dw->dfu->dfu_state = DFU_STATE_dfuIDLE; +} + +static void dfu_do_copy(struct dfu_work *dw) +{ + struct f_dfu *dfu = dw->dfu; + unsigned flags = O_WRONLY; + int ret, fd; + + pr_debug("do copy\n"); + + if (dfu_file_entry->flags & FILE_LIST_FLAG_CREATE) + flags |= O_CREAT | O_TRUNC; + + fd = open(dfu_file_entry->filename, flags); + if (fd < 0) { + perror("open"); + dfu->dfu_state = DFU_STATE_dfuERROR; + dfu->dfu_status = DFU_STATUS_errERASE; + return; + } + + ret = erase(fd, ERASE_SIZE_ALL, 0); + close(fd); + if (ret && ret != -ENOSYS) { + perror("erase"); + dfu->dfu_state = DFU_STATE_dfuERROR; + dfu->dfu_status = DFU_STATUS_errERASE; + return; + } + + ret = copy_file(DFU_TEMPFILE, dfu_file_entry->filename, 0); + if (ret) { + pr_err("copy file failed\n"); + dfu->dfu_state = DFU_STATE_dfuERROR; + dfu->dfu_status = DFU_STATUS_errWRITE; + return; + } + + dfu->dfu_state = DFU_STATE_dfuIDLE; +} static int dfu_bind(struct usb_configuration *c, struct usb_function *f) @@ -178,8 +360,8 @@ dfu_bind(struct usb_configuration *c, struct usb_function *f) struct usb_descriptor_header **header; struct usb_interface_descriptor *desc; struct file_list_entry *fentry; - struct f_dfu *dfu = container_of(f, struct f_dfu, func); - int i; + struct f_dfu *dfu = func_to_dfu(f); + int i, n_entries; int status; struct usb_string *us; @@ -190,7 +372,9 @@ dfu_bind(struct usb_configuration *c, struct usb_function *f) dfu_files = opts->files; } - dfu_string_defs = xzalloc(sizeof(struct usb_string) * (dfu_files->num_entries + 2)); + n_entries = list_count_nodes(&dfu_files->list); + + dfu_string_defs = xzalloc(sizeof(struct usb_string) * (n_entries + 2)); dfu_string_defs[0].s = "Generic DFU"; i = 0; file_list_for_each_entry(dfu_files, fentry) { @@ -206,7 +390,7 @@ dfu_bind(struct usb_configuration *c, struct usb_function *f) dfu->dnreq = usb_ep_alloc_request(c->cdev->gadget->ep0); if (!dfu->dnreq) { - printf("usb_ep_alloc_request failed\n"); + pr_err("usb_ep_alloc_request failed\n"); status = -ENOMEM; goto out; } @@ -214,26 +398,30 @@ dfu_bind(struct usb_configuration *c, struct usb_function *f) dfu->dnreq->complete = dn_complete; dfu->dnreq->zero = 0; - us = usb_gstrings_attach(cdev, dfu_strings, dfu_files->num_entries + 1); + us = usb_gstrings_attach(cdev, dfu_strings, n_entries + 1); if (IS_ERR(us)) { status = PTR_ERR(us); goto out; } + dfu->wq.fn = dfu_do_work; + dfu->wq.cancel = dfu_work_cancel; + wq_register(&dfu->wq); + /* allocate instance-specific interface IDs, and patch descriptors */ status = usb_interface_id(c, f); if (status < 0) goto out; - header = xzalloc(sizeof(void *) * (dfu_files->num_entries + 2)); - desc = xzalloc(sizeof(struct usb_interface_descriptor) * dfu_files->num_entries); - for (i = 0; i < dfu_files->num_entries; i++) { + header = xzalloc(sizeof(void *) * (n_entries + 2)); + desc = xzalloc(sizeof(struct usb_interface_descriptor) * n_entries); + for (i = 0; i < n_entries; i++) { desc[i].bLength = USB_DT_INTERFACE_SIZE; desc[i].bDescriptorType = USB_DT_INTERFACE; desc[i].bNumEndpoints = 0; desc[i].bInterfaceClass = 0xfe; desc[i].bInterfaceSubClass = 1; - desc[i].bInterfaceProtocol = 1; + desc[i].bInterfaceProtocol = 2; desc[i].bAlternateSetting = i; desc[i].iInterface = us[i + 1].id; header[i] = (struct usb_descriptor_header *)&desc[i]; @@ -241,7 +429,7 @@ dfu_bind(struct usb_configuration *c, struct usb_function *f) header[i] = (struct usb_descriptor_header *) &usb_dfu_func; header[i + 1] = NULL; - status = usb_assign_descriptors(f, header, header, NULL); + status = usb_assign_descriptors(f, header, header, header, header); free(desc); free(header); @@ -251,8 +439,7 @@ dfu_bind(struct usb_configuration *c, struct usb_function *f) i = 0; file_list_for_each_entry(dfu_files, fentry) { - printf("dfu: register alt%d(%s) with device %s\n", - i, fentry->name, fentry->filename); + pr_info("register alt%d(%s) with device %s\n", i, fentry->name, fentry->filename); i++; } @@ -271,6 +458,12 @@ dfu_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_dfu *dfu = func_to_dfu(f); + dfu_files = NULL; + dfu_file_entry = NULL; + dfudetach = 0; + + wq_unregister(&dfu->wq); + usb_free_all_descriptors(f); dma_free(dfu->dnreq->buf); @@ -294,6 +487,20 @@ static int dfu_set_alt(struct usb_function *f, unsigned intf, unsigned alt) return -EINVAL; } +static int dfu_get_alt(struct usb_function *f, unsigned intf) +{ + struct file_list_entry *fentry; + int i = 0; + + file_list_for_each_entry(dfu_files, fentry) { + if (fentry == dfu_file_entry) + return i; + i++; + } + + return -EINVAL; +} + static int dfu_status(struct usb_function *f, const struct usb_ctrlrequest *ctrl) { struct f_dfu *dfu = func_to_dfu(f); @@ -313,28 +520,55 @@ static int dfu_status(struct usb_function *f, const struct usb_ctrlrequest *ctrl static void dfu_cleanup(struct f_dfu *dfu) { - struct stat s; + struct dfu_work *dw; - if (dfufd > 0) { - close(dfufd); - dfufd = -EINVAL; - } + pr_debug("dfu cleanup\n"); - if (!stat(DFU_TEMPFILE, &s)) - unlink(DFU_TEMPFILE); + memset(&dfu_mtdinfo, 0, sizeof(dfu_mtdinfo)); + dfu_written = 0; + dfu_erased = 0; + prog_erase = 0; + + dfu->dfu_state = DFU_STATE_dfuIDLE; + dfu->dfu_status = DFU_STATUS_OK; + + dw = xzalloc(sizeof(*dw)); + dw->dfu = dfu; + dw->task = dfu_do_close; + wq_queue_work(&dfu->wq, &dw->work); } static void dn_complete(struct usb_ep *ep, struct usb_request *req) { struct f_dfu *dfu = req->context; - int ret; + struct dfu_work *dw; + + dw = xzalloc(sizeof(*dw)); + dw->dfu = dfu; + dw->task = dfu_do_write; + dw->len = min_t(unsigned int, req->length, CONFIG_USBD_DFU_XFER_SIZE); + memcpy(dw->wbuf, req->buf, dw->len); + wq_queue_work(&dfu->wq, &dw->work); +} - ret = write(dfufd, req->buf, req->length); - if (ret < (int)req->length) { - perror("write"); - dfu->dfu_status = DFU_STATUS_errWRITE; - dfu_cleanup(dfu); +static int handle_manifest(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct f_dfu *dfu = func_to_dfu(f); + struct dfu_work *dw; + + if (dfu_file_entry->flags & FILE_LIST_FLAG_SAFE) { + dw = xzalloc(sizeof(*dw)); + dw->dfu = dfu; + dw->task = dfu_do_copy; + wq_queue_work(&dfu->wq, &dw->work); } + + dw = xzalloc(sizeof(*dw)); + dw->dfu = dfu; + dw->task = dfu_do_close; + wq_queue_work(&dfu->wq, &dw->work); + + return 0; } static int handle_dnload(struct usb_function *f, const struct usb_ctrlrequest *ctrl) @@ -342,38 +576,10 @@ static int handle_dnload(struct usb_function *f, const struct usb_ctrlrequest *c struct f_dfu *dfu = func_to_dfu(f); struct usb_composite_dev *cdev = f->config->cdev; u16 w_length = le16_to_cpu(ctrl->wLength); - int ret; if (w_length == 0) { - dfu->dfu_state = DFU_STATE_dfuIDLE; - if (dfu_file_entry->flags & FILE_LIST_FLAG_SAFE) { - int fd; - unsigned flags = O_WRONLY; - - if (dfu_file_entry->flags & FILE_LIST_FLAG_CREATE) - flags |= O_CREAT | O_TRUNC; - - fd = open(dfu_file_entry->filename, flags); - if (fd < 0) { - perror("open"); - ret = -EINVAL; - goto err_out; - } - ret = erase(fd, ERASE_SIZE_ALL, 0); - close(fd); - if (ret && ret != -ENOSYS) { - perror("erase"); - ret = -EINVAL; - goto err_out; - } - ret = copy_file(DFU_TEMPFILE, dfu_file_entry->filename, 0); - if (ret) { - printf("copy file failed\n"); - ret = -EINVAL; - goto err_out; - } - } - dfu_cleanup(dfu); + handle_manifest(f, ctrl); + dfu->dfu_state = DFU_STATE_dfuMANIFEST; return 0; } @@ -382,10 +588,6 @@ static int handle_dnload(struct usb_function *f, const struct usb_ctrlrequest *c usb_ep_queue(cdev->gadget->ep0, dfu->dnreq); return 0; - -err_out: - dfu_cleanup(dfu); - return ret; } static void up_complete(struct usb_ep *ep, struct usb_request *req) @@ -395,28 +597,22 @@ static void up_complete(struct usb_ep *ep, struct usb_request *req) static int handle_upload(struct usb_function *f, const struct usb_ctrlrequest *ctrl) { struct f_dfu *dfu = func_to_dfu(f); - struct usb_composite_dev *cdev = f->config->cdev; + struct dfu_work *dw; u16 w_length = le16_to_cpu(ctrl->wLength); - int len; - len = read(dfufd, dfu->dnreq->buf, w_length); - - dfu->dnreq->length = len; - if (len < w_length) { - dfu_cleanup(dfu); - dfu->dfu_state = DFU_STATE_dfuIDLE; - } - - dfu->dnreq->complete = up_complete; - usb_ep_queue(cdev->gadget->ep0, dfu->dnreq); + dw = xzalloc(sizeof(*dw)); + dw->dfu = dfu; + dw->task = dfu_do_read; + dw->len = w_length; + dw->rbuf = dfu->dnreq->buf; + wq_queue_work(&dfu->wq, &dw->work); return 0; } static void dfu_abort(struct f_dfu *dfu) { - dfu->dfu_state = DFU_STATE_dfuIDLE; - dfu->dfu_status = DFU_STATUS_OK; + wq_cancel_work(&dfu->wq); dfu_cleanup(dfu); } @@ -429,7 +625,7 @@ static int dfu_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) int value = -EOPNOTSUPP; int w_length = le16_to_cpu(ctrl->wLength); int w_value = le16_to_cpu(ctrl->wValue); - int ret; + struct dfu_work *dw; if (ctrl->bRequestType == USB_DIR_IN && ctrl->bRequest == USB_REQ_GET_DESCRIPTOR && (w_value >> 8) == 0x21) { @@ -438,70 +634,45 @@ static int dfu_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) goto out; } - /* Allow GETSTATUS in every state */ - if (ctrl->bRequest == USB_REQ_DFU_GETSTATUS) { - value = dfu_status(f, ctrl); - value = min(value, w_length); - goto out; - } - - /* Allow GETSTATE in every state */ - if (ctrl->bRequest == USB_REQ_DFU_GETSTATE) { - *(u8 *)req->buf = dfu->dfu_state; - value = sizeof(u8); - goto out; - } - switch (dfu->dfu_state) { case DFU_STATE_dfuIDLE: switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + value = dfu_status(f, ctrl); + value = min(value, w_length); + break; + case USB_REQ_DFU_GETSTATE: + *(u8 *)req->buf = dfu->dfu_state; + value = sizeof(u8); + break; case USB_REQ_DFU_DNLOAD: if (w_length == 0) { dfu->dfu_state = DFU_STATE_dfuERROR; value = -EINVAL; goto out; } - debug("dfu: starting download to %s\n", dfu_file_entry->filename); - if (dfu_file_entry->flags & FILE_LIST_FLAG_SAFE) { - dfufd = open(DFU_TEMPFILE, O_WRONLY | O_CREAT); - } else { - unsigned flags = O_WRONLY; - - if (dfu_file_entry->flags & FILE_LIST_FLAG_CREATE) - flags |= O_CREAT | O_TRUNC; - - dfufd = open(dfu_file_entry->filename, flags); - } - - if (dfufd < 0) { - dfu->dfu_state = DFU_STATE_dfuERROR; - perror("open"); - goto out; - } - - ret = erase(dfufd, ERASE_SIZE_ALL, 0); - if (ret && ret != -ENOSYS) { - dfu->dfu_status = DFU_STATUS_errERASE; - perror("erase"); - goto out; - } + pr_debug("starting download to %s\n", dfu_file_entry->filename); + dw = xzalloc(sizeof(*dw)); + dw->dfu = dfu; + dw->task = dfu_do_open_dnload; + wq_queue_work(&dfu->wq, &dw->work); value = handle_dnload(f, ctrl); dfu->dfu_state = DFU_STATE_dfuDNLOAD_IDLE; return 0; case USB_REQ_DFU_UPLOAD: dfu->dfu_state = DFU_STATE_dfuUPLOAD_IDLE; - debug("dfu: starting upload from %s\n", dfu_file_entry->filename); + pr_debug("starting upload from %s\n", dfu_file_entry->filename); if (!(dfu_file_entry->flags & FILE_LIST_FLAG_READBACK)) { dfu->dfu_state = DFU_STATE_dfuERROR; goto out; } - dfufd = open(dfu_file_entry->filename, O_RDONLY); - if (dfufd < 0) { - dfu->dfu_state = DFU_STATE_dfuERROR; - perror("open"); - goto out; - } + + dw = xzalloc(sizeof(*dw)); + dw->dfu = dfu; + dw->task = dfu_do_open_upload; + wq_queue_work(&dfu->wq, &dw->work); + handle_upload(f, ctrl); return 0; case USB_REQ_DFU_ABORT: @@ -520,9 +691,17 @@ static int dfu_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) break; case DFU_STATE_dfuDNLOAD_IDLE: switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + value = dfu_status(f, ctrl); + value = min(value, w_length); + break; + case USB_REQ_DFU_GETSTATE: + *(u8 *)req->buf = dfu->dfu_state; + value = sizeof(u8); + break; case USB_REQ_DFU_DNLOAD: value = handle_dnload(f, ctrl); - if (dfu->dfu_state != DFU_STATE_dfuIDLE) { + if (dfu->dfu_state == DFU_STATE_dfuDNLOAD_IDLE) { return 0; } break; @@ -538,6 +717,14 @@ static int dfu_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) break; case DFU_STATE_dfuUPLOAD_IDLE: switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + value = dfu_status(f, ctrl); + value = min(value, w_length); + break; + case USB_REQ_DFU_GETSTATE: + *(u8 *)req->buf = dfu->dfu_state; + value = sizeof(u8); + break; case USB_REQ_DFU_UPLOAD: handle_upload(f, ctrl); return 0; @@ -552,7 +739,16 @@ static int dfu_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) } break; case DFU_STATE_dfuERROR: + wq_cancel_work(&dfu->wq); switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + value = dfu_status(f, ctrl); + value = min(value, w_length); + break; + case USB_REQ_DFU_GETSTATE: + *(u8 *)req->buf = dfu->dfu_state; + value = sizeof(u8); + break; case USB_REQ_DFU_CLRSTATUS: dfu_abort(dfu); /* no zlp? */ @@ -564,10 +760,42 @@ static int dfu_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) break; } break; - case DFU_STATE_dfuDNLOAD_SYNC: - case DFU_STATE_dfuDNBUSY: case DFU_STATE_dfuMANIFEST_SYNC: + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + dfu->dfu_state = DFU_STATE_dfuMANIFEST; + value = dfu_status(f, ctrl); + value = min(value, w_length); + break; + case USB_REQ_DFU_GETSTATE: + *(u8 *)req->buf = dfu->dfu_state; + value = sizeof(u8); + break; + default: + dfu->dfu_state = DFU_STATE_dfuERROR; + value = -EINVAL; + break; + } + break; case DFU_STATE_dfuMANIFEST: + dfu->dfu_state = DFU_STATE_dfuMANIFEST_SYNC; + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + value = dfu_status(f, ctrl); + value = min(value, w_length); + break; + case USB_REQ_DFU_GETSTATE: + *(u8 *)req->buf = dfu->dfu_state; + value = sizeof(u8); + break; + default: + dfu->dfu_state = DFU_STATE_dfuERROR; + value = -EINVAL; + break; + } + break; + case DFU_STATE_dfuDNLOAD_SYNC: + case DFU_STATE_dfuDNBUSY: dfu->dfu_state = DFU_STATE_dfuERROR; value = -EINVAL; break; @@ -592,178 +820,17 @@ static void dfu_disable(struct usb_function *f) { struct f_dfu *dfu = func_to_dfu(f); - dfu->dfu_state = DFU_STATE_dfuIDLE; - - dfu_cleanup(dfu); -} - -#define STRING_MANUFACTURER_IDX 0 -#define STRING_PRODUCT_IDX 1 -#define STRING_DESCRIPTION_IDX 2 - -static struct usb_string strings_dev[] = { - [STRING_MANUFACTURER_IDX].s = NULL, - [STRING_PRODUCT_IDX].s = NULL, - [STRING_DESCRIPTION_IDX].s = "USB Device Firmware Upgrade", - { } /* end of list */ -}; - -static struct usb_gadget_strings stringtab_dev = { - .language = 0x0409, /* en-us */ - .strings = strings_dev, -}; - -static struct usb_gadget_strings *dev_strings[] = { - &stringtab_dev, - NULL, -}; - -static void dfu_unbind_config(struct usb_configuration *c) -{ - free(dfu_string_defs); -} - -static struct usb_configuration dfu_config_driver = { - .label = "USB DFU", - .unbind = dfu_unbind_config, - .bConfigurationValue = 1, - .bmAttributes = USB_CONFIG_ATT_SELFPOWER, -}; - -static struct usb_device_descriptor dfu_dev_descriptor = { - .bLength = USB_DT_DEVICE_SIZE, - .bDescriptorType = USB_DT_DEVICE, - .bcdUSB = 0x0100, - .bDeviceClass = 0x00, - .bDeviceSubClass = 0x00, - .bDeviceProtocol = 0x00, -/* .idVendor = dynamic */ -/* .idProduct = dynamic */ - .bcdDevice = 0x0000, - .bNumConfigurations = 0x01, -}; - -static struct usb_function_instance *fi_dfu; -static struct usb_function *f_dfu; - -static int dfu_driver_bind(struct usb_composite_dev *cdev) -{ - struct usb_gadget *gadget = cdev->gadget; - int status; - - if (gadget->vendor_id && gadget->product_id) { - dfu_dev_descriptor.idVendor = cpu_to_le16(gadget->vendor_id); - dfu_dev_descriptor.idProduct = cpu_to_le16(gadget->product_id); - } else { - dfu_dev_descriptor.idVendor = cpu_to_le16(0x1d50); /* Openmoko, Inc */ - dfu_dev_descriptor.idProduct = cpu_to_le16(0x60a2); /* barebox bootloader USB DFU Mode */ - } - - strings_dev[STRING_MANUFACTURER_IDX].s = gadget->manufacturer; - strings_dev[STRING_PRODUCT_IDX].s = gadget->productname; - - status = usb_string_id(cdev); - if (status < 0) - goto fail; - strings_dev[STRING_MANUFACTURER_IDX].id = status; - dfu_dev_descriptor.iManufacturer = status; - - status = usb_string_id(cdev); - if (status < 0) - goto fail; - strings_dev[STRING_PRODUCT_IDX].id = status; - dfu_dev_descriptor.iProduct = status; - - /* config description */ - status = usb_string_id(cdev); - if (status < 0) - goto fail; - strings_dev[STRING_DESCRIPTION_IDX].id = status; - dfu_config_driver.iConfiguration = status; - - status = usb_add_config_only(cdev, &dfu_config_driver); - if (status < 0) - goto fail; - - fi_dfu = usb_get_function_instance("dfu"); - if (IS_ERR(fi_dfu)) { - status = PTR_ERR(fi_dfu); - goto fail; - } - - f_dfu = usb_get_function(fi_dfu); - if (IS_ERR(f_dfu)) { - status = PTR_ERR(f_dfu); - goto fail; - } - - status = usb_add_function(&dfu_config_driver, f_dfu); - if (status) - goto fail; - - return 0; -fail: - return status; -} - -static int dfu_driver_unbind(struct usb_composite_dev *cdev) -{ - usb_put_function(f_dfu); - usb_put_function_instance(fi_dfu); - - return 0; + dfu_abort(dfu); } -static struct usb_composite_driver dfu_driver = { - .name = "g_dfu", - .dev = &dfu_dev_descriptor, - .strings = dev_strings, - .max_speed = USB_SPEED_HIGH, - .bind = dfu_driver_bind, - .unbind = dfu_driver_unbind, -}; - -int usb_dfu_register(struct f_dfu_opts *opts) +int usb_dfu_detached(void) { - int ret; - - if (dfu_files) - return -EBUSY; - - dfu_files = opts->files; - - ret = usb_composite_probe(&dfu_driver); - if (ret) - goto out; - - while (1) { - ret = usb_gadget_poll(); - if (ret < 0) - goto out1; - - if (dfudetach) { - ret = 0; - goto out1; - } - - if (ctrlc()) { - ret = -EINTR; - goto out1; - } - } - -out1: - dfudetach = 0; - usb_composite_unregister(&dfu_driver); -out: - dfu_files = NULL; - - return ret; + return dfudetach; } static void dfu_free_func(struct usb_function *f) { - struct f_dfu *dfu = container_of(f, struct f_dfu, func); + struct f_dfu *dfu = func_to_dfu(f); free(dfu); } @@ -779,6 +846,7 @@ static struct usb_function *dfu_alloc_func(struct usb_function_instance *fi) /* descriptors are per-instance copies */ dfu->func.bind = dfu_bind; dfu->func.set_alt = dfu_set_alt; + dfu->func.get_alt = dfu_get_alt; dfu->func.setup = dfu_setup; dfu->func.disable = dfu_disable; dfu->func.unbind = dfu_unbind; diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/function/f_acm.c index 42a2b03ad2..3532fd5892 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/function/f_acm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * f_acm.c -- USB CDC serial (ACM) function driver * @@ -6,23 +7,18 @@ * Copyright (C) 2008 by Nokia Corporation * Copyright (C) 2009 by Samsung Electronics * Author: Michal Nazarewicz (mina86@mina86.com) - * - * This software is distributed under the terms of the GNU General - * Public License ("GPL") as published by the Free Software Foundation, - * either version 2 of that License or (at your option) any later version. */ /* #define VERBOSE_DEBUG */ #include <common.h> -#include <usb/cdc.h> +#include <linux/usb/cdc.h> #include <linux/err.h> #include <linux/spinlock.h> #include <asm/byteorder.h> -#include <usb/composite.h> +#include <linux/usb/composite.h> #include "u_serial.h" -#include "gadget_chips.h" /* @@ -682,7 +678,7 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) acm_ss_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress; status = usb_assign_descriptors(f, acm_fs_function, acm_hs_function, - acm_ss_function); + acm_ss_function, acm_ss_function); if (status) goto fail; diff --git a/drivers/usb/gadget/function/f_fastboot.c b/drivers/usb/gadget/function/f_fastboot.c new file mode 100644 index 0000000000..30d257b500 --- /dev/null +++ b/drivers/usb/gadget/function/f_fastboot.c @@ -0,0 +1,519 @@ +/* + * (C) Copyright 2008 - 2009 + * Windriver, <www.windriver.com> + * Tom Rix <Tom.Rix@windriver.com> + * + * Copyright 2011 Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Copyright 2014 Linaro, Ltd. + * Rob Herring <robh@kernel.org> + * + * Copyright 2014 Sascha Hauer <s.hauer@pengutronix.de> + * Ported to barebox + * + * Copyright 2020 Edmund Henniges <eh@emlix.com> + * Copyright 2020 Daniel Glöckner <dg@emlix.com> + * Split off of generic parts + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#define pr_fmt(fmt) "fastboot: " fmt + +#include <dma.h> +#include <work.h> +#include <unistd.h> +#include <progress.h> +#include <fastboot.h> +#include <linux/usb/fastboot.h> + +#define FASTBOOT_INTERFACE_CLASS 0xff +#define FASTBOOT_INTERFACE_SUB_CLASS 0x42 +#define FASTBOOT_INTERFACE_PROTOCOL 0x03 + +#define EP_BUFFER_SIZE 4096 + +struct f_fastboot { + struct fastboot fastboot; + struct usb_function func; + + /* IN/OUT EP's and corresponding requests */ + struct usb_ep *in_ep, *out_ep; + struct usb_request *out_req; + struct work_queue wq; +}; + +static inline struct f_fastboot *func_to_fastboot(struct usb_function *f) +{ + return container_of(f, struct f_fastboot, func); +} + +static struct usb_endpoint_descriptor fs_ep_in = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(64), + .bInterval = 0x00, +}; + +static struct usb_endpoint_descriptor fs_ep_out = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(64), + .bInterval = 0x00, +}; + +static struct usb_endpoint_descriptor hs_ep_in = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), + .bInterval = 0x00, +}; + +static struct usb_endpoint_descriptor hs_ep_out = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), + .bInterval = 0x00, +}; + +static struct usb_interface_descriptor interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0x00, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x02, + .bInterfaceClass = FASTBOOT_INTERFACE_CLASS, + .bInterfaceSubClass = FASTBOOT_INTERFACE_SUB_CLASS, + .bInterfaceProtocol = FASTBOOT_INTERFACE_PROTOCOL, +}; + +static struct usb_descriptor_header *fb_fs_descs[] = { + (struct usb_descriptor_header *)&interface_desc, + (struct usb_descriptor_header *)&fs_ep_in, + (struct usb_descriptor_header *)&fs_ep_out, + NULL, +}; + +static struct usb_descriptor_header *fb_hs_descs[] = { + (struct usb_descriptor_header *)&interface_desc, + (struct usb_descriptor_header *)&hs_ep_in, + (struct usb_descriptor_header *)&hs_ep_out, + NULL, +}; + +static struct usb_endpoint_descriptor ss_ep_in = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_endpoint_descriptor ss_ep_out = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_ss_ep_comp_descriptor fb_ss_bulk_comp_desc = { + .bLength = sizeof(fb_ss_bulk_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, +}; + +static struct usb_descriptor_header *fb_ss_descs[] = { + (struct usb_descriptor_header *)&interface_desc, + (struct usb_descriptor_header *)&ss_ep_in, + (struct usb_descriptor_header *)&fb_ss_bulk_comp_desc, + (struct usb_descriptor_header *)&ss_ep_out, + (struct usb_descriptor_header *)&fb_ss_bulk_comp_desc, + NULL, +}; + +/* + * static strings, in UTF-8 + */ +static const char fastboot_name[] = "Android Fastboot"; + +static struct usb_string fastboot_string_defs[] = { + [0].s = fastboot_name, + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_fastboot = { + .language = 0x0409, /* en-us */ + .strings = fastboot_string_defs, +}; + +static struct usb_gadget_strings *fastboot_strings[] = { + &stringtab_fastboot, + NULL, +}; + +static void rx_handler_command(struct usb_ep *ep, struct usb_request *req); +static int fastboot_write_usb(struct fastboot *fb, const char *buffer, + unsigned int buffer_size); +static void fastboot_start_download_usb(struct fastboot *fb); + +struct fastboot_work { + struct work_struct work; + struct f_fastboot *f_fb; + char command[FASTBOOT_MAX_CMD_LEN + 1]; +}; + +static void fastboot_do_work(struct work_struct *w) +{ + struct fastboot_work *fw = container_of(w, struct fastboot_work, work); + struct f_fastboot *f_fb = fw->f_fb; + + fastboot_exec_cmd(&f_fb->fastboot, fw->command); + + memset(f_fb->out_req->buf, 0, EP_BUFFER_SIZE); + usb_ep_queue(f_fb->out_ep, f_fb->out_req); + + free(fw); +} + +static void fastboot_work_cancel(struct work_struct *w) +{ + struct fastboot_work *fw = container_of(w, struct fastboot_work, work); + + free(fw); +} + +static struct usb_request *fastboot_alloc_request(struct usb_ep *ep) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep); + if (!req) + return NULL; + + req->length = EP_BUFFER_SIZE; + req->buf = dma_zalloc(EP_BUFFER_SIZE); + if (!req->buf) { + usb_ep_free_request(ep, req); + return NULL; + } + + return req; +} + +static void fastboot_free_request(struct usb_ep *ep, struct usb_request *req) +{ + free(req->buf); + usb_ep_free_request(ep, req); +} + +static void fastboot_complete(struct usb_ep *ep, struct usb_request *req) +{ + fastboot_free_request(ep, req); +} + +static int fastboot_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + int id, ret; + struct usb_gadget *gadget = c->cdev->gadget; + struct f_fastboot *f_fb = func_to_fastboot(f); + struct usb_string *us; + const struct usb_function_instance *fi = f->fi; + struct f_fastboot_opts *opts = container_of(fi, struct f_fastboot_opts, func_inst); + + f_fb->fastboot.write = fastboot_write_usb; + f_fb->fastboot.start_download = fastboot_start_download_usb; + + f_fb->fastboot.files = opts->common.files; + f_fb->fastboot.cmd_exec = opts->common.cmd_exec; + f_fb->fastboot.cmd_flash = opts->common.cmd_flash; + + f_fb->wq.fn = fastboot_do_work; + f_fb->wq.cancel = fastboot_work_cancel; + + wq_register(&f_fb->wq); + + ret = fastboot_generic_init(&f_fb->fastboot, opts->common.export_bbu); + if (ret) + goto err_wq_unregister; + + /* DYNAMIC interface numbers assignments */ + id = usb_interface_id(c, f); + if (id < 0) { + ret = id; + goto fb_generic_free; + } + + interface_desc.bInterfaceNumber = id; + + id = usb_string_id(c->cdev); + if (id < 0) { + ret = id; + goto fb_generic_free; + } + fastboot_string_defs[0].id = id; + interface_desc.iInterface = id; + + us = usb_gstrings_attach(cdev, fastboot_strings, 1); + if (IS_ERR(us)) { + ret = PTR_ERR(us); + goto fb_generic_free; + } + + f_fb->in_ep = usb_ep_autoconfig(gadget, &fs_ep_in); + if (!f_fb->in_ep) { + ret = -ENODEV; + goto fb_generic_free; + } + f_fb->in_ep->driver_data = c->cdev; + + f_fb->out_ep = usb_ep_autoconfig(gadget, &fs_ep_out); + if (!f_fb->out_ep) { + ret = -ENODEV; + goto fb_generic_free; + } + f_fb->out_ep->driver_data = c->cdev; + + hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress; + hs_ep_in.bEndpointAddress = fs_ep_in.bEndpointAddress; + ss_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress; + ss_ep_in.bEndpointAddress = fs_ep_in.bEndpointAddress; + + f_fb->out_req = fastboot_alloc_request(f_fb->out_ep); + if (!f_fb->out_req) { + puts("failed to alloc out req\n"); + ret = -EINVAL; + goto fb_generic_free; + } + + f_fb->out_req->complete = rx_handler_command; + f_fb->out_req->context = f_fb; + + ret = usb_assign_descriptors(f, fb_fs_descs, fb_hs_descs, fb_ss_descs, fb_ss_descs); + if (ret) + goto err_free_in_req; + + return 0; + +err_free_in_req: + free(f_fb->out_req->buf); + usb_ep_free_request(f_fb->out_ep, f_fb->out_req); +fb_generic_free: + fastboot_generic_free(&f_fb->fastboot); +err_wq_unregister: + wq_unregister(&f_fb->wq); + + return ret; +} + +static void fastboot_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_fastboot *f_fb = func_to_fastboot(f); + + free(f_fb->out_req->buf); + usb_ep_free_request(f_fb->out_ep, f_fb->out_req); + f_fb->out_req = NULL; + + wq_unregister(&f_fb->wq); + + fastboot_generic_free(&f_fb->fastboot); +} + +static void fastboot_disable(struct usb_function *f) +{ + struct f_fastboot *f_fb = func_to_fastboot(f); + + usb_ep_disable(f_fb->out_ep); + usb_ep_disable(f_fb->in_ep); +} + +static int fastboot_set_alt(struct usb_function *f, + unsigned interface, unsigned alt) +{ + int ret; + struct f_fastboot *f_fb = func_to_fastboot(f); + + pr_debug("%s: func: %s intf: %d alt: %d\n", + __func__, f->name, interface, alt); + + ret = config_ep_by_speed(f->config->cdev->gadget, f, + f_fb->out_ep); + if (ret) + return ret; + + ret = usb_ep_enable(f_fb->out_ep); + if (ret) { + pr_err("failed to enable out ep: %s\n", strerror(-ret)); + return ret; + } + + ret = config_ep_by_speed(f->config->cdev->gadget, f, + f_fb->in_ep); + if (ret) + return ret; + + ret = usb_ep_enable(f_fb->in_ep); + if (ret) { + pr_err("failed to enable in ep: %s\n", strerror(-ret)); + return ret; + } + + memset(f_fb->out_req->buf, 0, EP_BUFFER_SIZE); + ret = usb_ep_queue(f_fb->out_ep, f_fb->out_req); + if (ret) + goto err; + + return 0; +err: + fastboot_disable(f); + return ret; +} + +static void fastboot_free_func(struct usb_function *f) +{ + struct f_fastboot *f_fb = container_of(f, struct f_fastboot, func); + + fastboot_generic_close(&f_fb->fastboot); + free(f_fb); +} + +static struct usb_function *fastboot_alloc_func(struct usb_function_instance *fi) +{ + struct f_fastboot *f_fb; + + f_fb = xzalloc(sizeof(*f_fb)); + + f_fb->func.name = "fastboot"; + f_fb->func.strings = fastboot_strings; + f_fb->func.bind = fastboot_bind; + f_fb->func.set_alt = fastboot_set_alt; + f_fb->func.disable = fastboot_disable; + f_fb->func.unbind = fastboot_unbind; + f_fb->func.free_func = fastboot_free_func; + + return &f_fb->func; +} + +static void fastboot_free_instance(struct usb_function_instance *fi) +{ + struct f_fastboot_opts *opts; + + opts = container_of(fi, struct f_fastboot_opts, func_inst); + kfree(opts); +} + +static struct usb_function_instance *fastboot_alloc_instance(void) +{ + struct f_fastboot_opts *opts; + + opts = xzalloc(sizeof(*opts)); + opts->func_inst.free_func_inst = fastboot_free_instance; + + return &opts->func_inst; +} + +DECLARE_USB_FUNCTION_INIT(fastboot, fastboot_alloc_instance, fastboot_alloc_func); + +static int fastboot_write_usb(struct fastboot *fb, const char *buffer, unsigned int buffer_size) +{ + struct f_fastboot *f_fb = container_of(fb, struct f_fastboot, fastboot); + struct usb_request *in_req; + int ret; + + in_req = fastboot_alloc_request(f_fb->in_ep); + if (!in_req) + return -ENOMEM; + + memcpy(in_req->buf, buffer, buffer_size); + in_req->length = buffer_size; + in_req->complete = fastboot_complete; + + ret = usb_ep_queue(f_fb->in_ep, in_req); + if (ret) { + fastboot_free_request(f_fb->in_ep, in_req); + pr_err("Error %d on queue\n", ret); + } + + return 0; +} + +static int rx_bytes_expected(struct f_fastboot *f_fb) +{ + int remaining = f_fb->fastboot.download_size + - f_fb->fastboot.download_bytes; + + if (remaining >= EP_BUFFER_SIZE) + return EP_BUFFER_SIZE; + + return ALIGN(remaining, f_fb->out_ep->maxpacket); +} + +static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req) +{ + struct f_fastboot *f_fb = req->context; + const unsigned char *buffer = req->buf; + int ret; + + if (req->status != 0) { + pr_err("Bad status: %d\n", req->status); + return; + } + + ret = fastboot_handle_download_data(&f_fb->fastboot, buffer, + req->actual); + if (ret < 0) { + fastboot_tx_print(&f_fb->fastboot, FASTBOOT_MSG_FAIL, + strerror(-ret)); + return; + } + + req->length = rx_bytes_expected(f_fb); + + /* Check if transfer is done */ + if (f_fb->fastboot.download_bytes >= f_fb->fastboot.download_size) { + req->complete = rx_handler_command; + req->length = EP_BUFFER_SIZE; + + fastboot_download_finished(&f_fb->fastboot); + } + + req->actual = 0; + usb_ep_queue(ep, req); +} + +static void fastboot_start_download_usb(struct fastboot *fb) +{ + struct f_fastboot *f_fb = container_of(fb, struct f_fastboot, fastboot); + struct usb_request *req = f_fb->out_req; + + req->complete = rx_handler_dl_image; + req->length = rx_bytes_expected(f_fb); + fastboot_start_download_generic(fb); +} + +static void rx_handler_command(struct usb_ep *ep, struct usb_request *req) +{ + struct f_fastboot *f_fb = req->context; + struct fastboot_work *w; + int len; + + if (req->status != 0) + return; + + w = xzalloc(sizeof(*w)); + w->f_fb = f_fb; + + len = min_t(unsigned int, req->actual, FASTBOOT_MAX_CMD_LEN); + + memcpy(w->command, req->buf, len); + + wq_queue_work(&f_fb->wq, &w->work); +} diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c new file mode 100644 index 0000000000..2c934c621a --- /dev/null +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -0,0 +1,2764 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * f_mass_storage.c -- Mass Storage USB Composite Function + * + * Copyright (C) 2003-2008 Alan Stern + * Copyright (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz <m.nazarewicz@samsung.com> + * All rights reserved. + */ + +/* + * The Mass Storage Function acts as a USB Mass Storage device, + * appearing to the host as a disk drive or as a CD-ROM drive. In + * addition to providing an example of a genuinely useful composite + * function for a USB device, it also illustrates a technique of + * double-buffering for increased throughput. + * + * Function supports multiple logical units (LUNs). Backing storage + * for each LUN is provided by a regular file or a block device. + * Access for each LUN can be limited to read-only. Moreover, the + * function can indicate that LUN is removable and/or CD-ROM. (The + * later implies read-only access.) + * + * MSF is configured by specifying a fsg_config structure. It has the + * following fields: + * + * nluns Number of LUNs function have (anywhere from 1 + * to FSG_MAX_LUNS which is 8). + * luns An array of LUN configuration values. This + * should be filled for each LUN that + * function will include (ie. for "nluns" + * LUNs). Each element of the array has + * the following fields: + * ->filename The path to the backing file for the LUN. + * Required if LUN is not marked as + * removable. + * ->ro Flag specifying access to the LUN shall be + * read-only. This is implied if CD-ROM + * emulation is enabled as well as when + * it was impossible to open "filename" + * in R/W mode. + * ->removable Flag specifying that LUN shall be indicated as + * being removable. + * ->cdrom Flag specifying that LUN shall be reported as + * being a CD-ROM. + * + * vendor_name + * product_name + * release Information used as a reply to INQUIRY + * request. To use default set to NULL, + * NULL, 0xffff respectively. The first + * field should be 8 and the second 16 + * characters or less. + * + * can_stall Set to permit function to halt bulk endpoints. + * Disabled on some USB devices known not + * to work correctly. You should set it + * to true. + * + * If "removable" is not set for a LUN then a backing file must be + * specified. If it is set, then NULL filename means the LUN's medium + * is not loaded (an empty string as "filename" in the fsg_config + * structure causes error). The CD-ROM emulation includes a single + * data track and no audio tracks; hence there need be only one + * backing file per LUN. Note also that the CD-ROM block length is + * set to 512 rather than the more common value 2048. + * + * + * MSF includes support for module parameters. If gadget using it + * decides to use it, the following module parameters will be + * available: + * + * file=filename[,filename...] + * Names of the files or block devices used for + * backing storage. + * ro=b[,b...] Default false, boolean for read-only access. + * removable=b[,b...] + * Default true, boolean for removable media. + * cdrom=b[,b...] Default false, boolean for whether to emulate + * a CD-ROM drive. + * luns=N Default N = number of filenames, number of + * LUNs to support. + * stall Default determined according to the type of + * USB device controller (usually true), + * boolean to permit the driver to halt + * bulk endpoints. + * + * The module parameters may be prefixed with some string. You need + * to consult gadget's documentation or source to verify whether it is + * using those module parameters and if it does what are the prefixes + * (look for FSG_MODULE_PARAMETERS() macro usage, what's inside it is + * the prefix). + * + * + * Requirements are modest; only a bulk-in and a bulk-out endpoint are + * needed. The memory requirement amounts to two 16K buffers, size + * configurable by a parameter. Support is included for both + * full-speed and high-speed operation. + * + * Note that the driver is slightly non-portable in that it assumes a + * single memory/DMA buffer will be useable for bulk-in, bulk-out, and + * interrupt-in endpoints. With most device controllers this isn't an + * issue, but there may be some with hardware restrictions that prevent + * a buffer from being used by more than one endpoint. + * + * When a LUN receive an "eject" SCSI request (Start/Stop Unit), + * if the LUN is removable, the backing file is released to simulate + * ejection. + * + * + * This function is heavily based on "File-backed Storage Gadget" by + * Alan Stern which in turn is heavily based on "Gadget Zero" by David + * Brownell. The driver's SCSI command interface was based on the + * "Information technology - Small Computer System Interface - 2" + * document from X3T9.2 Project 375D, Revision 10L, 7-SEP-93, + * available at <http://www.t10.org/ftp/t10/drafts/s2/s2-r10l.pdf>. + * The single exception is opcode 0x23 (READ FORMAT CAPACITIES), which + * was based on the "Universal Serial Bus Mass Storage Class UFI + * Command Specification" document, Revision 1.0, December 14, 1998, + * available at + * <http://www.usb.org/developers/devclass_docs/usbmass-ufi10.pdf>. + */ + +/* + * Driver Design + * + * The MSF is fairly straightforward. There is a main kernel + * thread that handles most of the work. Interrupt routines field + * callbacks from the controller driver: bulk- and interrupt-request + * completion notifications, endpoint-0 events, and disconnect events. + * Completion events are passed to the main thread by wakeup calls. Many + * ep0 requests are handled at interrupt time, but SetInterface, + * SetConfiguration, and device reset requests are forwarded to the + * thread in the form of "exceptions" using SIGUSR1 signals (since they + * should interrupt any ongoing file I/O operations). + * + * The thread's main routine implements the standard command/data/status + * parts of a SCSI interaction. It and its subroutines are full of tests + * for pending signals/exceptions -- all this polling is necessary since + * the kernel has no setjmp/longjmp equivalents. (Maybe this is an + * indication that the driver really wants to be running in userspace.) + * An important point is that so long as the thread is alive it keeps an + * open reference to the backing file. This will prevent unmounting + * the backing file's underlying filesystem and could cause problems + * during system shutdown, for example. To prevent such problems, the + * thread catches INT, TERM, and KILL signals and converts them into + * an EXIT exception. + * + * In normal operation the main thread is started during the gadget's + * fsg_bind() callback and stopped during fsg_unbind(). But it can + * also exit when it receives a signal, and there's no point leaving + * the gadget running when the thread is dead. At of this moment, MSF + * provides no way to deregister the gadget when thread dies -- maybe + * a callback functions is needed. + * + * To provide maximum throughput, the driver uses a circular pipeline of + * buffer heads (struct fsg_buffhd). In principle the pipeline can be + * arbitrarily long; in practice the benefits don't justify having more + * than 2 stages (i.e., double buffering). But it helps to think of the + * pipeline as being a long one. Each buffer head contains a bulk-in and + * a bulk-out request pointer (since the buffer can be used for both + * output and input -- directions always are given from the host's + * point of view) as well as a pointer to the buffer and various state + * variables. + * + * Use of the pipeline follows a simple protocol. There is a variable + * (fsg->next_buffhd_to_fill) that points to the next buffer head to use. + * At any time that buffer head may still be in use from an earlier + * request, so each buffer head has a state variable indicating whether + * it is EMPTY, FULL, or BUSY. Typical use involves waiting for the + * buffer head to be EMPTY, filling the buffer either by file I/O or by + * USB I/O (during which the buffer head is BUSY), and marking the buffer + * head FULL when the I/O is complete. Then the buffer will be emptied + * (again possibly by USB I/O, during which it is marked BUSY) and + * finally marked EMPTY again (possibly by a completion routine). + * + * A module parameter tells the driver to avoid stalling the bulk + * endpoints wherever the transport specification allows. This is + * necessary for some UDCs like the SuperH, which cannot reliably clear a + * halt on a bulk endpoint. However, under certain circumstances the + * Bulk-only specification requires a stall. In such cases the driver + * will halt the endpoint and set a flag indicating that it should clear + * the halt in software during the next device reset. Hopefully this + * will permit everything to work correctly. Furthermore, although the + * specification allows the bulk-out endpoint to halt when the host sends + * too much data, implementing this would cause an unavoidable race. + * The driver will always use the "no-stall" approach for OUT transfers. + * + * One subtle point concerns sending status-stage responses for ep0 + * requests. Some of these requests, such as device reset, can involve + * interrupting an ongoing file I/O operation, which might take an + * arbitrarily long time. During that delay the host might give up on + * the original ep0 request and issue a new one. When that happens the + * driver should not notify the host about completion of the original + * request, as the host will no longer be waiting for it. So the driver + * assigns to each ep0 request a unique tag, and it keeps track of the + * tag value of the request associated with a long-running exception + * (device-reset, interface-change, or configuration-change). When the + * exception handler is finished, the status-stage response is submitted + * only if the current ep0 request tag is equal to the exception request + * tag. Thus only the most recently received ep0 request will get a + * status-stage response. + * + * Warning: This driver source file is too long. It ought to be split up + * into a header file plus about 3 separate .c files, to handle the details + * of the Gadget, USB Mass Storage, and SCSI protocols. + */ + +/* #define VERBOSE_DEBUG */ +/* #define DUMP_MSGS */ + +#define pr_fmt(fmt) "f_ums: " fmt + +#include <common.h> +#include <unistd.h> +#include <linux/stat.h> +#include <linux/wait.h> +#include <fcntl.h> +#include <file-list.h> +#include <dma.h> +#include <linux/bug.h> +#include <linux/rwsem.h> +#include <linux/pagemap.h> +#include <disks.h> +#include <scsi.h> + +#include <linux/err.h> +#include <linux/usb/mass_storage.h> + +#include <asm/unaligned.h> +#include <linux/bitops.h> +#include <linux/usb/gadget.h> +#include <linux/usb/composite.h> +#include <linux/bitmap.h> +#include <linux/completion.h> +#include <bthread.h> +#include <sched.h> + + +/*------------------------------------------------------------------------*/ + +#define FSG_DRIVER_DESC "ums" +#define UMS_NAME_LEN 16 + +#define FSG_DRIVER_VERSION "2012/06/5" + +static const char fsg_string_interface[] = "Mass Storage"; + +#include "storage_common.h" + +/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */ +struct usb_string fsg_strings[] = { + {FSG_STRING_INTERFACE, fsg_string_interface}, + {} +}; + +static struct usb_gadget_strings fsg_stringtab = { + .language = 0x0409, /* en-us */ + .strings = fsg_strings, +}; + +/*-------------------------------------------------------------------------*/ + +struct bthread *thread_task; + +struct fsg_dev; + +static struct file_list *ums_files; + +/* Data shared by all the FSG instances. */ +struct fsg_common { + struct usb_gadget *gadget; + struct fsg_dev *fsg, *new_fsg; + + struct usb_ep *ep0; /* Copy of gadget->ep0 */ + struct usb_request *ep0req; /* Copy of cdev->req */ + unsigned int ep0_req_tag; + + struct fsg_buffhd *next_buffhd_to_fill; + struct fsg_buffhd *next_buffhd_to_drain; + struct fsg_buffhd buffhds[FSG_NUM_BUFFERS]; + + struct f_ums_opts *opts; + + int cmnd_size; + u8 cmnd[MAX_COMMAND_SIZE]; + + unsigned int nluns; + unsigned int lun; + struct fsg_lun luns[FSG_MAX_LUNS]; + + unsigned int bulk_out_maxpacket; + enum fsg_state state; /* For exception handling */ + unsigned int exception_req_tag; + + enum data_direction data_dir; + u32 data_size; + u32 data_size_from_cmnd; + u32 tag; + u32 residue; + u32 usb_amount_left; + + unsigned int can_stall:1; + unsigned int phase_error:1; + unsigned int short_packet_received:1; + unsigned int bad_lun_okay:1; + unsigned int running:1; + + struct completion thread_wakeup_needed; + + /* Callback functions. */ + const struct fsg_operations *ops; + /* Gadget's private data. */ + void *private_data; + + const char *vendor_name; /* 8 characters or less */ + const char *product_name; /* 16 characters or less */ + u16 release; + + /* Vendor (8 chars), product (16 chars), release (4 + * hexadecimal digits) and NUL byte */ + char inquiry_string[8 + 16 + 4 + 1]; +}; + +static struct f_ums_opts *f_ums_opts_get(struct f_ums_opts *opts) +{ + opts->refcnt++; + return opts; +} + +static void f_ums_opts_put(struct f_ums_opts *opts) +{ + if (--opts->refcnt == 0) { + kfree(opts->common); + kfree(opts); + } +} + +struct fsg_config { + unsigned nluns; + struct fsg_lun_config { + const char *filename; + char ro; + char removable; + char cdrom; + char nofua; + } luns[FSG_MAX_LUNS]; + + /* Callback functions. */ + const struct fsg_operations *ops; + /* Gadget's private data. */ + void *private_data; + + const char *vendor_name; /* 8 characters or less */ + const char *product_name; /* 16 characters or less */ + + char can_stall; +}; + +struct fsg_dev { + struct usb_function function; + struct usb_gadget *gadget; /* Copy of cdev->gadget */ + struct fsg_common *common; + + int refcnt; + + u16 interface_number; + + unsigned int bulk_in_enabled:1; + unsigned int bulk_out_enabled:1; + + unsigned long atomic_bitflags; +#define IGNORE_BULK_OUT 0 + + struct usb_ep *bulk_in; + struct usb_ep *bulk_out; +}; + +static struct fsg_dev *fsg_dev_get(struct fsg_dev *fsg) +{ + fsg->refcnt++; + return fsg; +} + +static void fsg_dev_put(struct fsg_dev *fsg) +{ + if (--fsg->refcnt == 0) + kfree(fsg); +} + +static inline int __fsg_is_set(struct fsg_common *common, + const char *func, unsigned line) +{ + if (common->fsg) + return 1; + ERROR(common, "common->fsg is NULL in %s at %u\n", func, line); + WARN_ON(1); + + return 0; +} + +#define fsg_is_set(common) likely(__fsg_is_set(common, __func__, __LINE__)) + + +static inline struct fsg_dev *fsg_from_func(struct usb_function *f) +{ + return container_of(f, struct fsg_dev, function); +} + +static inline struct f_ums_opts * +fsg_opts_from_func_inst(const struct usb_function_instance *fi) +{ + return container_of(fi, struct f_ums_opts, func_inst); +} + +typedef void (*fsg_routine_t)(struct fsg_dev *); + +static int exception_in_progress(struct fsg_common *common) +{ + return common->state > FSG_STATE_IDLE; +} + +/* Make bulk-out requests be divisible by the maxpacket size */ +static void set_bulk_out_req_length(struct fsg_common *common, + struct fsg_buffhd *bh, unsigned int length) +{ + unsigned int rem; + + bh->bulk_out_intended_length = length; + rem = length % common->bulk_out_maxpacket; + if (rem > 0) + length += common->bulk_out_maxpacket - rem; + bh->outreq->length = length; +} + +/*-------------------------------------------------------------------------*/ + +static struct f_ums_opts ums[14]; // FIXME +static int ums_count; + +static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep) +{ + const char *name; + + if (ep == fsg->bulk_in) + name = "bulk-in"; + else if (ep == fsg->bulk_out) + name = "bulk-out"; + else + name = ep->name; + DBG(fsg, "%s set halt\n", name); + return usb_ep_set_halt(ep); +} + +/*-------------------------------------------------------------------------*/ + +/* These routines may be called in process context or in_irq */ + +/* Caller must hold fsg->lock */ +static void wakeup_thread(struct fsg_common *common) +{ + complete(&common->thread_wakeup_needed); +} + +static void report_exception(const char *prefix, enum fsg_state state) +{ + const char *msg = "<unknown>"; + switch (state) { + /* This one isn't used anywhere */ + case FSG_STATE_COMMAND_PHASE: + msg = "Command Phase"; + break; + case FSG_STATE_DATA_PHASE: + msg = "Data Phase"; + break; + case FSG_STATE_STATUS_PHASE: + msg = "Status Phase"; + break; + + case FSG_STATE_IDLE: + msg = "Idle"; + break; + case FSG_STATE_ABORT_BULK_OUT: + msg = "abort bulk out"; + break; + case FSG_STATE_RESET: + msg = "reset"; + break; + case FSG_STATE_INTERFACE_CHANGE: + msg = "interface change"; + break; + case FSG_STATE_CONFIG_CHANGE: + msg = "config change"; + break; + case FSG_STATE_DISCONNECT: + msg = "disconnect"; + break; + case FSG_STATE_EXIT: + msg = "exit"; + break; + case FSG_STATE_TERMINATED: + msg = "terminated"; + break; + } + + pr_debug("%s: %s\n", prefix, msg); +} + +static void raise_exception(struct fsg_common *common, enum fsg_state new_state) +{ + /* Do nothing if a higher-priority exception is already in progress. + * If a lower-or-equal priority exception is in progress, preempt it + * and notify the main thread by sending it a signal. */ + if (common->state <= new_state) { + report_exception("raising", new_state); + common->exception_req_tag = common->ep0_req_tag; + common->state = new_state; + wakeup_thread(common); + } +} + +/*-------------------------------------------------------------------------*/ + +static int ep0_queue(struct fsg_common *common) +{ + int rc; + + rc = usb_ep_queue(common->ep0, common->ep0req); + common->ep0->driver_data = common; + if (rc != 0 && rc != -ESHUTDOWN) { + /* We can't do much more than wait for a reset */ + WARNING(common, "error in submission: %s --> %d\n", + common->ep0->name, rc); + } + return rc; +} + +/*-------------------------------------------------------------------------*/ + +/* Bulk and interrupt endpoint completion handlers. + * These always run in_irq. */ + +static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct fsg_common *common = ep->driver_data; + struct fsg_buffhd *bh = req->context; + + if (req->status || req->actual != req->length) + DBG(common, "%s --> %d, %u/%u\n", __func__, + req->status, req->actual, req->length); + if (req->status == -ECONNRESET) /* Request was cancelled */ + usb_ep_fifo_flush(ep); + + /* Hold the lock while we update the request and buffer states */ + bh->inreq_busy = 0; + bh->state = BUF_STATE_EMPTY; + wakeup_thread(common); +} + +static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct fsg_common *common = ep->driver_data; + struct fsg_buffhd *bh = req->context; + + dump_msg(common, "bulk-out", req->buf, req->actual); + if (req->status || req->actual != bh->bulk_out_intended_length) + DBG(common, "%s --> %d, %u/%u\n", __func__, + req->status, req->actual, + bh->bulk_out_intended_length); + if (req->status == -ECONNRESET) /* Request was cancelled */ + usb_ep_fifo_flush(ep); + + /* Hold the lock while we update the request and buffer states */ + bh->outreq_busy = 0; + bh->state = BUF_STATE_FULL; + wakeup_thread(common); +} + +/*-------------------------------------------------------------------------*/ + +/* Ep0 class-specific handlers. These always run in_irq. */ + +static int fsg_setup(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct fsg_dev *fsg = fsg_from_func(f); + struct usb_request *req = fsg->common->ep0req; + u16 w_index = get_unaligned_le16(&ctrl->wIndex); + u16 w_value = get_unaligned_le16(&ctrl->wValue); + u16 w_length = get_unaligned_le16(&ctrl->wLength); + + if (!fsg_is_set(fsg->common)) + return -EOPNOTSUPP; + + switch (ctrl->bRequest) { + + case US_BULK_RESET_REQUEST: + if (ctrl->bRequestType != + (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) + break; + if (w_index != fsg->interface_number || w_value != 0) + return -EDOM; + + /* Raise an exception to stop the current operation + * and reinitialize our state. */ + DBG(fsg, "bulk reset request\n"); + raise_exception(fsg->common, FSG_STATE_RESET); + return DELAYED_STATUS; + + case US_BULK_GET_MAX_LUN: + if (ctrl->bRequestType != + (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) + break; + if (w_index != fsg->interface_number || w_value != 0) + return -EDOM; + VDBG(fsg, "get max LUN\n"); + *(u8 *) req->buf = fsg->common->nluns - 1; + + /* Respond with data/status */ + req->length = min((u16)1, w_length); + return ep0_queue(fsg->common); + } + + VDBG(fsg, + "unknown class-specific control req " + "%02x.%02x v%04x i%04x l%u\n", + ctrl->bRequestType, ctrl->bRequest, + get_unaligned_le16(&ctrl->wValue), w_index, w_length); + return -EOPNOTSUPP; +} + +/*-------------------------------------------------------------------------*/ + +/* All the following routines run in process context */ + +/* Use this for bulk or interrupt transfers, not ep0 */ +static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, + struct usb_request *req, int *pbusy, + enum fsg_buffer_state *state) +{ + int rc; + + if (ep == fsg->bulk_in) + dump_msg(fsg, "bulk-in", req->buf, req->length); + + *pbusy = 1; + *state = BUF_STATE_BUSY; + rc = usb_ep_queue(ep, req); + if (rc != 0) { + *pbusy = 0; + *state = BUF_STATE_EMPTY; + + /* We can't do much more than wait for a reset */ + + /* Note: currently the net2280 driver fails zero-length + * submissions if DMA is enabled. */ + if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP && + req->length == 0)) + WARNING(fsg, "error in submission: %s --> %d\n", + ep->name, rc); + } +} + +#define START_TRANSFER_OR(common, ep_name, req, pbusy, state) \ + if (fsg_is_set(common)) \ + start_transfer((common)->fsg, (common)->fsg->ep_name, \ + req, pbusy, state); \ + else + +#define START_TRANSFER(common, ep_name, req, pbusy, state) \ + START_TRANSFER_OR(common, ep_name, req, pbusy, state) (void)0 + +static int sleep_thread(struct fsg_common *common) +{ + int ret; + + /* Wait until a signal arrives or we are woken up */ + ret = wait_for_completion_interruptible(&common->thread_wakeup_needed); + if (ret) + return ret; + + reinit_completion(&common->thread_wakeup_needed); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int do_read(struct fsg_common *common) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + u32 lba; + struct fsg_buffhd *bh; + int rc; + u32 amount_left; + loff_t file_offset; + unsigned int amount; + unsigned int partial_page; + ssize_t nread; + + /* Get the starting Logical Block Address and check that it's + * not too big */ + if (common->cmnd[0] == SCSI_READ6) + lba = get_unaligned_be24(&common->cmnd[1]); + else { + lba = get_unaligned_be32(&common->cmnd[2]); + + /* We allow DPO (Disable Page Out = don't save data in the + * cache) and FUA (Force Unit Access = don't read from the + * cache), but we don't implement them. */ + if ((common->cmnd[1] & ~0x18) != 0) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + } + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + file_offset = ((loff_t) lba) << 9; + + /* Carry out the file reads */ + amount_left = common->data_size_from_cmnd; + if (unlikely(amount_left == 0)) + return -EIO; /* No default reply */ + + for (;;) { + /* Wait for the next buffer to become available */ + bh = common->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(common); + if (rc) + return rc; + } + + /* Figure out how much we need to read: + * Try to read the remaining amount. + * But don't read more than the buffer size. + * And don't try to read past the end of the file. + * Finally, if we're not at a page boundary, don't read past + * the next page. + * If this means reading 0 then we were asked to read past + * the end of file. */ + amount = min(amount_left, FSG_BUFLEN); + partial_page = file_offset & (PAGE_CACHE_SIZE - 1); + if (partial_page > 0) + amount = min(amount, (unsigned int) PAGE_CACHE_SIZE - + partial_page); + + + /* If we were asked to read past the end of file, + * end with an empty buffer. */ + if (amount == 0) { + curlun->sense_data = + SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + curlun->info_valid = 1; + bh->inreq->length = 0; + bh->state = BUF_STATE_FULL; + break; + } + + /* Perform the read */ + nread = pread(ums[common->lun].fd, bh->buf, amount, file_offset); + + VLDBG(curlun, "file read %u @ %llu -> %zd\n", amount, + (unsigned long long) file_offset, + nread); + if (nread <= 0) { + const char *err = nread ? strerror(-nread) : "EOF"; + LDBG(curlun, "error in file read: %s\n", err); + nread = 0; + } else if (nread < amount) { + LDBG(curlun, "partial file read: %d/%u\n", + (int) nread, amount); + nread -= (nread & 511); /* Round down to a block */ + } + file_offset += nread; + amount_left -= nread; + common->residue -= nread; + bh->inreq->length = nread; + bh->state = BUF_STATE_FULL; + + /* If an error occurred, report it and its position */ + if (nread < amount) { + curlun->sense_data = SS_UNRECOVERED_READ_ERROR; + curlun->info_valid = 1; + break; + } + + if (amount_left == 0) + break; /* No more left to read */ + + /* Send this buffer and go read some more */ + bh->inreq->zero = 0; + START_TRANSFER_OR(common, bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state) + /* Don't know what to do if + * common->fsg is NULL */ + return -EIO; + common->next_buffhd_to_fill = bh->next; + } + + return -EIO; /* No default reply */ +} + +/*-------------------------------------------------------------------------*/ + +static int do_write(struct fsg_common *common) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + u32 lba; + struct fsg_buffhd *bh; + int get_some_more; + u32 amount_left_to_req, amount_left_to_write; + loff_t usb_offset, file_offset; + unsigned int amount; + unsigned int partial_page; + ssize_t nwritten; + int rc; + + if (curlun->ro) { + curlun->sense_data = SS_WRITE_PROTECTED; + return -EINVAL; + } + + /* Get the starting Logical Block Address and check that it's + * not too big */ + if (common->cmnd[0] == SCSI_WRITE6) + lba = get_unaligned_be24(&common->cmnd[1]); + else { + lba = get_unaligned_be32(&common->cmnd[2]); + + /* We allow DPO (Disable Page Out = don't save data in the + * cache) and FUA (Force Unit Access = write directly to the + * medium). We don't implement DPO; we implement FUA by + * performing synchronous output. */ + if (common->cmnd[1] & ~0x18) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + } + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + + /* Carry out the file writes */ + get_some_more = 1; + file_offset = usb_offset = ((loff_t) lba) << 9; + amount_left_to_req = common->data_size_from_cmnd; + amount_left_to_write = common->data_size_from_cmnd; + + while (amount_left_to_write > 0) { + + /* Queue a request for more data from the host */ + bh = common->next_buffhd_to_fill; + if (bh->state == BUF_STATE_EMPTY && get_some_more) { + + /* Figure out how much we want to get: + * Try to get the remaining amount. + * But don't get more than the buffer size. + * And don't try to go past the end of the file. + * If we're not at a page boundary, + * don't go past the next page. + * If this means getting 0, then we were asked + * to write past the end of file. + * Finally, round down to a block boundary. */ + amount = min(amount_left_to_req, FSG_BUFLEN); + partial_page = usb_offset & (PAGE_CACHE_SIZE - 1); + if (partial_page > 0) + amount = min(amount, + (unsigned int) PAGE_CACHE_SIZE - partial_page); + + if (amount == 0) { + get_some_more = 0; + curlun->sense_data = + SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + curlun->info_valid = 1; + continue; + } + amount -= (amount & 511); + if (amount == 0) { + + /* Why were we were asked to transfer a + * partial block? */ + get_some_more = 0; + continue; + } + + /* Get the next buffer */ + usb_offset += amount; + common->usb_amount_left -= amount; + amount_left_to_req -= amount; + if (amount_left_to_req == 0) + get_some_more = 0; + + /* amount is always divisible by 512, hence by + * the bulk-out maxpacket size */ + bh->outreq->length = amount; + bh->bulk_out_intended_length = amount; + bh->outreq->short_not_ok = 1; + START_TRANSFER_OR(common, bulk_out, bh->outreq, + &bh->outreq_busy, &bh->state) + /* Don't know what to do if + * common->fsg is NULL */ + return -EIO; + common->next_buffhd_to_fill = bh->next; + continue; + } + + /* Write the received data to the backing file */ + bh = common->next_buffhd_to_drain; + if (bh->state == BUF_STATE_EMPTY && !get_some_more) + break; /* We stopped early */ + if (bh->state == BUF_STATE_FULL) { + common->next_buffhd_to_drain = bh->next; + bh->state = BUF_STATE_EMPTY; + + /* Did something go wrong with the transfer? */ + if (bh->outreq->status != 0) { + curlun->sense_data = SS_COMMUNICATION_FAILURE; + curlun->info_valid = 1; + break; + } + + amount = bh->outreq->actual; + + /* Perform the write */ + nwritten = pwrite(ums[common->lun].fd, bh->buf, amount, file_offset); + + VLDBG(curlun, "file write %u @ %llu -> %zd\n", amount, + (unsigned long long) file_offset, + nwritten); + + if (nwritten < 0) { + LDBG(curlun, "error in file write: %pe\n", ERR_PTR(nwritten)); + nwritten = 0; + } else if (nwritten < amount) { + LDBG(curlun, "partial file write: %d/%u\n", + (int) nwritten, amount); + nwritten -= (nwritten & 511); + /* Round down to a block */ + } + file_offset += nwritten; + amount_left_to_write -= nwritten; + common->residue -= nwritten; + + /* If an error occurred, report it and its position */ + if (nwritten < amount) { + pr_warn("nwritten:%zd amount:%u\n", nwritten, + amount); + curlun->sense_data = SS_WRITE_ERROR; + curlun->info_valid = 1; + break; + } + + /* Did the host decide to stop early? */ + if (bh->outreq->actual != bh->outreq->length) { + common->short_packet_received = 1; + break; + } + continue; + } + + /* Wait for something to happen */ + rc = sleep_thread(common); + if (rc) + return rc; + } + + return -EIO; /* No default reply */ +} + +/*-------------------------------------------------------------------------*/ + +static int do_synchronize_cache(struct fsg_common *common) +{ + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int do_verify(struct fsg_common *common) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + u32 lba; + u32 verification_length; + struct fsg_buffhd *bh = common->next_buffhd_to_fill; + loff_t file_offset; + u32 amount_left; + unsigned int amount; + ssize_t nread; + + /* Get the starting Logical Block Address and check that it's + * not too big */ + lba = get_unaligned_be32(&common->cmnd[2]); + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + + /* We allow DPO (Disable Page Out = don't save data in the + * cache) but we don't implement it. */ + if (common->cmnd[1] & ~0x10) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + verification_length = get_unaligned_be16(&common->cmnd[7]); + if (unlikely(verification_length == 0)) + return -EIO; /* No default reply */ + + /* Prepare to carry out the file verify */ + amount_left = verification_length << 9; + file_offset = ((loff_t) lba) << 9; + + /* Write out all the dirty buffers before invalidating them */ + + /* Just try to read the requested blocks */ + while (amount_left > 0) { + + /* Figure out how much we need to read: + * Try to read the remaining amount, but not more than + * the buffer size. + * And don't try to read past the end of the file. + * If this means reading 0 then we were asked to read + * past the end of file. */ + amount = min(amount_left, FSG_BUFLEN); + if (amount == 0) { + curlun->sense_data = + SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + curlun->info_valid = 1; + break; + } + + /* Perform the read */ + nread = pread(ums[common->lun].fd, bh->buf, amount, file_offset); + + VLDBG(curlun, "file read %u @ %llu -> %zd\n", amount, + (unsigned long long) file_offset, + nread); + if (nread <= 0) { + const char *err = nread ? strerror(-nread) : "EOF"; + LDBG(curlun, "error in file read: %s\n", err); + nread = 0; + } else if (nread < amount) { + LDBG(curlun, "partial file verify: %d/%u\n", + (int) nread, amount); + nread -= (nread & 511); /* Round down to a sector */ + } + if (nread == 0) { + curlun->sense_data = SS_UNRECOVERED_READ_ERROR; + curlun->info_valid = 1; + break; + } + file_offset += nread; + amount_left -= nread; + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + static const char vendor_id[] = "Linux "; + u8 *buf = (u8 *) bh->buf; + + if (!curlun) { /* Unsupported LUNs are okay */ + common->bad_lun_okay = 1; + memset(buf, 0, 36); + buf[0] = 0x7f; /* Unsupported, no device-type */ + buf[4] = 31; /* Additional length */ + return 36; + } + + memset(buf, 0, 8); + buf[0] = TYPE_DISK; + buf[1] = curlun->removable ? 0x80 : 0; + buf[2] = 2; /* ANSI SCSI level 2 */ + buf[3] = 2; /* SCSI-2 INQUIRY data format */ + buf[4] = 31; /* Additional length */ + /* No special options */ + sprintf((char *) (buf + 8), "%-8s%-16s%04x", (char*) vendor_id , + ums[common->lun].name, (u16) 0xffff); + + return 36; +} + + +static int do_request_sense(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + u8 *buf = (u8 *) bh->buf; + u32 sd, sdinfo = 0; + int valid; + + /* + * From the SCSI-2 spec., section 7.9 (Unit attention condition): + * + * If a REQUEST SENSE command is received from an initiator + * with a pending unit attention condition (before the target + * generates the contingent allegiance condition), then the + * target shall either: + * a) report any pending sense data and preserve the unit + * attention condition on the logical unit, or, + * b) report the unit attention condition, may discard any + * pending sense data, and clear the unit attention + * condition on the logical unit for that initiator. + * + * FSG normally uses option a); enable this code to use option b). + */ +#if 0 + if (curlun && curlun->unit_attention_data != SS_NO_SENSE) { + curlun->sense_data = curlun->unit_attention_data; + curlun->unit_attention_data = SS_NO_SENSE; + } +#endif + + if (!curlun) { /* Unsupported LUNs are okay */ + common->bad_lun_okay = 1; + sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; + valid = 0; + } else { + sd = curlun->sense_data; + valid = curlun->info_valid << 7; + curlun->sense_data = SS_NO_SENSE; + curlun->info_valid = 0; + } + + memset(buf, 0, 18); + buf[0] = valid | 0x70; /* Valid, current error */ + buf[2] = SK(sd); + put_unaligned_be32(sdinfo, &buf[3]); /* Sense information */ + buf[7] = 18 - 8; /* Additional sense length */ + buf[12] = ASC(sd); + buf[13] = ASCQ(sd); + return 18; +} + +static int do_read_capacity(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + u32 lba = get_unaligned_be32(&common->cmnd[2]); + int pmi = common->cmnd[8]; + u8 *buf = (u8 *) bh->buf; + + /* Check the PMI and LBA fields */ + if (pmi > 1 || (pmi == 0 && lba != 0)) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + put_unaligned_be32(curlun->num_sectors - 1, &buf[0]); + /* Max logical block */ + put_unaligned_be32(512, &buf[4]); /* Block length */ + return 8; +} + +static int do_read_header(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + int msf = common->cmnd[1] & 0x02; + u32 lba = get_unaligned_be32(&common->cmnd[2]); + u8 *buf = (u8 *) bh->buf; + + if (common->cmnd[1] & ~0x02) { /* Mask away MSF */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + + memset(buf, 0, 8); + buf[0] = 0x01; /* 2048 bytes of user data, rest is EC */ + store_cdrom_address(&buf[4], msf, lba); + return 8; +} + + +static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + int msf = common->cmnd[1] & 0x02; + int start_track = common->cmnd[6]; + u8 *buf = (u8 *) bh->buf; + + if ((common->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */ + start_track > 1) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + memset(buf, 0, 20); + buf[1] = (20-2); /* TOC data length */ + buf[2] = 1; /* First track number */ + buf[3] = 1; /* Last track number */ + buf[5] = 0x16; /* Data track, copying allowed */ + buf[6] = 0x01; /* Only track is number 1 */ + store_cdrom_address(&buf[8], msf, 0); + + buf[13] = 0x16; /* Lead-out track is data */ + buf[14] = 0xAA; /* Lead-out track number */ + store_cdrom_address(&buf[16], msf, curlun->num_sectors); + + return 20; +} + +static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + int mscmnd = common->cmnd[0]; + u8 *buf = (u8 *) bh->buf; + u8 *buf0 = buf; + int pc, page_code; + int changeable_values, all_pages; + int valid_page = 0; + int len, limit; + + if ((common->cmnd[1] & ~0x08) != 0) { /* Mask away DBD */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + pc = common->cmnd[2] >> 6; + page_code = common->cmnd[2] & 0x3f; + if (pc == 3) { + curlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED; + return -EINVAL; + } + changeable_values = (pc == 1); + all_pages = (page_code == 0x3f); + + /* Write the mode parameter header. Fixed values are: default + * medium type, no cache control (DPOFUA), and no block descriptors. + * The only variable value is the WriteProtect bit. We will fill in + * the mode data length later. */ + memset(buf, 0, 8); + if (mscmnd == SCSI_MODE_SEN6) { + buf[2] = (curlun->ro ? 0x80 : 0x00); /* WP, DPOFUA */ + buf += 4; + limit = 255; + } else { /* SCSI_MODE_SEN10 */ + buf[3] = (curlun->ro ? 0x80 : 0x00); /* WP, DPOFUA */ + buf += 8; + limit = 65535; /* Should really be FSG_BUFLEN */ + } + + /* No block descriptors */ + + /* The mode pages, in numerical order. The only page we support + * is the Caching page. */ + if (page_code == 0x08 || all_pages) { + valid_page = 1; + buf[0] = 0x08; /* Page code */ + buf[1] = 10; /* Page length */ + memset(buf+2, 0, 10); /* None of the fields are changeable */ + + if (!changeable_values) { + buf[2] = 0x04; /* Write cache enable, */ + /* Read cache not disabled */ + /* No cache retention priorities */ + put_unaligned_be16(0xffff, &buf[4]); + /* Don't disable prefetch */ + /* Minimum prefetch = 0 */ + put_unaligned_be16(0xffff, &buf[8]); + /* Maximum prefetch */ + put_unaligned_be16(0xffff, &buf[10]); + /* Maximum prefetch ceiling */ + } + buf += 12; + } + + /* Check that a valid page was requested and the mode data length + * isn't too long. */ + len = buf - buf0; + if (!valid_page || len > limit) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + /* Store the mode data length */ + if (mscmnd == SCSI_MODE_SEN6) + buf0[0] = len - 1; + else + put_unaligned_be16(len - 2, buf0); + return len; +} + + +static int do_start_stop(struct fsg_common *common) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + + if (!curlun) { + return -EINVAL; + } else if (!curlun->removable) { + curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; + } + + return 0; +} + +static int do_prevent_allow(struct fsg_common *common) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + int prevent; + + if (!curlun->removable) { + curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; + } + + prevent = common->cmnd[4] & 0x01; + if ((common->cmnd[4] & ~0x01) != 0) { /* Mask away Prevent */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + if (curlun->prevent_medium_removal && !prevent) + fsg_lun_fsync_sub(curlun); + curlun->prevent_medium_removal = prevent; + return 0; +} + + +static int do_read_format_capacities(struct fsg_common *common, + struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + u8 *buf = (u8 *) bh->buf; + + buf[0] = buf[1] = buf[2] = 0; + buf[3] = 8; /* Only the Current/Maximum Capacity Descriptor */ + buf += 4; + + put_unaligned_be32(curlun->num_sectors, &buf[0]); + /* Number of blocks */ + put_unaligned_be32(512, &buf[4]); /* Block length */ + buf[4] = 0x02; /* Current capacity */ + return 12; +} + + +static int do_mode_select(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + + /* We don't support MODE SELECT */ + if (curlun) + curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; +} + + +/*-------------------------------------------------------------------------*/ + +static int halt_bulk_in_endpoint(struct fsg_dev *fsg) +{ + int rc; + + rc = fsg_set_halt(fsg, fsg->bulk_in); + if (rc == -EAGAIN) + VDBG(fsg, "delayed bulk-in endpoint halt\n"); + while (rc != 0) { + if (rc != -EAGAIN) { + WARNING(fsg, "usb_ep_set_halt -> %d\n", rc); + rc = 0; + break; + } + + rc = usb_ep_set_halt(fsg->bulk_in); + } + return rc; +} + +static int wedge_bulk_in_endpoint(struct fsg_dev *fsg) +{ + int rc; + + DBG(fsg, "bulk-in set wedge\n"); + rc = 0; /* usb_ep_set_wedge(fsg->bulk_in); */ + if (rc == -EAGAIN) + VDBG(fsg, "delayed bulk-in endpoint wedge\n"); + while (rc != 0) { + if (rc != -EAGAIN) { + WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc); + rc = 0; + break; + } + } + return rc; +} + +static int pad_with_zeros(struct fsg_dev *fsg) +{ + struct fsg_buffhd *bh = fsg->common->next_buffhd_to_fill; + u32 nkeep = bh->inreq->length; + u32 nsend; + int rc; + + bh->state = BUF_STATE_EMPTY; /* For the first iteration */ + fsg->common->usb_amount_left = nkeep + fsg->common->residue; + while (fsg->common->usb_amount_left > 0) { + + /* Wait for the next buffer to be free */ + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(fsg->common); + if (rc) + return rc; + } + + nsend = min(fsg->common->usb_amount_left, FSG_BUFLEN); + memset(bh->buf + nkeep, 0, nsend - nkeep); + bh->inreq->length = nsend; + bh->inreq->zero = 0; + start_transfer(fsg, fsg->bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state); + bh = fsg->common->next_buffhd_to_fill = bh->next; + fsg->common->usb_amount_left -= nsend; + nkeep = 0; + } + return 0; +} + +static int throw_away_data(struct fsg_common *common) +{ + struct fsg_buffhd *bh; + u32 amount; + int rc; + + for (bh = common->next_buffhd_to_drain; + bh->state != BUF_STATE_EMPTY || common->usb_amount_left > 0; + bh = common->next_buffhd_to_drain) { + + /* Throw away the data in a filled buffer */ + if (bh->state == BUF_STATE_FULL) { + bh->state = BUF_STATE_EMPTY; + common->next_buffhd_to_drain = bh->next; + + /* A short packet or an error ends everything */ + if (bh->outreq->actual != bh->outreq->length || + bh->outreq->status != 0) { + raise_exception(common, + FSG_STATE_ABORT_BULK_OUT); + return -EPIPE; + } + continue; + } + + /* Try to submit another request if we need one */ + bh = common->next_buffhd_to_fill; + if (bh->state == BUF_STATE_EMPTY + && common->usb_amount_left > 0) { + amount = min(common->usb_amount_left, FSG_BUFLEN); + + /* amount is always divisible by 512, hence by + * the bulk-out maxpacket size */ + bh->outreq->length = amount; + bh->bulk_out_intended_length = amount; + bh->outreq->short_not_ok = 1; + START_TRANSFER_OR(common, bulk_out, bh->outreq, + &bh->outreq_busy, &bh->state) + /* Don't know what to do if + * common->fsg is NULL */ + return -EIO; + common->next_buffhd_to_fill = bh->next; + common->usb_amount_left -= amount; + continue; + } + + /* Otherwise wait for something to happen */ + rc = sleep_thread(common); + if (rc) + return rc; + } + return 0; +} + + +static int finish_reply(struct fsg_common *common) +{ + struct fsg_buffhd *bh = common->next_buffhd_to_fill; + int rc = 0; + + switch (common->data_dir) { + case DATA_DIR_NONE: + break; /* Nothing to send */ + + /* If we don't know whether the host wants to read or write, + * this must be CB or CBI with an unknown command. We mustn't + * try to send or receive any data. So stall both bulk pipes + * if we can and wait for a reset. */ + case DATA_DIR_UNKNOWN: + if (!common->can_stall) { + /* Nothing */ + } else if (fsg_is_set(common)) { + fsg_set_halt(common->fsg, common->fsg->bulk_out); + rc = halt_bulk_in_endpoint(common->fsg); + } else { + /* Don't know what to do if common->fsg is NULL */ + rc = -EIO; + } + break; + + /* All but the last buffer of data must have already been sent */ + case DATA_DIR_TO_HOST: + if (common->data_size == 0) { + /* Nothing to send */ + + /* If there's no residue, simply send the last buffer */ + } else if (common->residue == 0) { + bh->inreq->zero = 0; + START_TRANSFER_OR(common, bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state) + return -EIO; + common->next_buffhd_to_fill = bh->next; + + /* For Bulk-only, if we're allowed to stall then send the + * short packet and halt the bulk-in endpoint. If we can't + * stall, pad out the remaining data with 0's. */ + } else if (common->can_stall) { + bh->inreq->zero = 1; + START_TRANSFER_OR(common, bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state) + /* Don't know what to do if + * common->fsg is NULL */ + rc = -EIO; + common->next_buffhd_to_fill = bh->next; + if (common->fsg) + rc = halt_bulk_in_endpoint(common->fsg); + } else if (fsg_is_set(common)) { + rc = pad_with_zeros(common->fsg); + } else { + /* Don't know what to do if common->fsg is NULL */ + rc = -EIO; + } + break; + + /* We have processed all we want from the data the host has sent. + * There may still be outstanding bulk-out requests. */ + case DATA_DIR_FROM_HOST: + if (common->residue == 0) { + /* Nothing to receive */ + + /* Did the host stop sending unexpectedly early? */ + } else if (common->short_packet_received) { + raise_exception(common, FSG_STATE_ABORT_BULK_OUT); + rc = -EPIPE; + + /* We haven't processed all the incoming data. Even though + * we may be allowed to stall, doing so would cause a race. + * The controller may already have ACK'ed all the remaining + * bulk-out packets, in which case the host wouldn't see a + * STALL. Not realizing the endpoint was halted, it wouldn't + * clear the halt -- leading to problems later on. */ +#if 0 + } else if (common->can_stall) { + if (fsg_is_set(common)) + fsg_set_halt(common->fsg, + common->fsg->bulk_out); + raise_exception(common, FSG_STATE_ABORT_BULK_OUT); + rc = -EPIPE; +#endif + + /* We can't stall. Read in the excess data and throw it + * all away. */ + } else { + rc = throw_away_data(common); + } + break; + } + return rc; +} + + +static int send_status(struct fsg_common *common) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + struct fsg_buffhd *bh; + struct bulk_cs_wrap *csw; + int rc; + u8 status = US_BULK_STAT_OK; + u32 sd, sdinfo = 0; + + /* Wait for the next buffer to become available */ + bh = common->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(common); + if (rc) + return rc; + } + + if (curlun) + sd = curlun->sense_data; + else if (common->bad_lun_okay) + sd = SS_NO_SENSE; + else + sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; + + if (common->phase_error) { + DBG(common, "sending phase-error status\n"); + status = US_BULK_STAT_PHASE; + sd = SS_INVALID_COMMAND; + } else if (sd != SS_NO_SENSE) { + DBG(common, "sending command-failure status\n"); + status = US_BULK_STAT_FAIL; + VDBG(common, " sense data: SK x%02x, ASC x%02x, ASCQ x%02x;" + " info x%x\n", + SK(sd), ASC(sd), ASCQ(sd), sdinfo); + } + + /* Store and send the Bulk-only CSW */ + csw = (void *)bh->buf; + + csw->Signature = cpu_to_le32(US_BULK_CS_SIGN); + csw->Tag = common->tag; + csw->Residue = cpu_to_le32(common->residue); + csw->Status = status; + + bh->inreq->length = US_BULK_CS_WRAP_LEN; + bh->inreq->zero = 0; + START_TRANSFER_OR(common, bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state) + /* Don't know what to do if common->fsg is NULL */ + return -EIO; + + common->next_buffhd_to_fill = bh->next; + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +/* Check whether the command is properly formed and whether its data size + * and direction agree with the values we already have. */ +static int check_command(struct fsg_common *common, int cmnd_size, + enum data_direction data_dir, unsigned int mask, + int needs_medium, const char *name) +{ + int i; + int lun = common->cmnd[1] >> 5; + static const char dirletter[4] = {'u', 'o', 'i', 'n'}; + char hdlen[20]; + struct fsg_lun *curlun; + + hdlen[0] = 0; + if (common->data_dir != DATA_DIR_UNKNOWN) + sprintf(hdlen, ", H%c=%u", dirletter[(int) common->data_dir], + common->data_size); + VDBG(common, "SCSI command: %s; Dc=%d, D%c=%u; Hc=%d%s\n", + name, cmnd_size, dirletter[(int) data_dir], + common->data_size_from_cmnd, common->cmnd_size, hdlen); + + /* We can't reply at all until we know the correct data direction + * and size. */ + if (common->data_size_from_cmnd == 0) + data_dir = DATA_DIR_NONE; + if (common->data_size < common->data_size_from_cmnd) { + /* Host data size < Device data size is a phase error. + * Carry out the command, but only transfer as much as + * we are allowed. */ + common->data_size_from_cmnd = common->data_size; + common->phase_error = 1; + } + common->residue = common->data_size; + common->usb_amount_left = common->data_size; + + /* Conflicting data directions is a phase error */ + if (common->data_dir != data_dir + && common->data_size_from_cmnd > 0) { + common->phase_error = 1; + return -EINVAL; + } + + /* Verify the length of the command itself */ + if (cmnd_size != common->cmnd_size) { + + /* Special case workaround: There are plenty of buggy SCSI + * implementations. Many have issues with cbw->Length + * field passing a wrong command size. For those cases we + * always try to work around the problem by using the length + * sent by the host side provided it is at least as large + * as the correct command length. + * Examples of such cases would be MS-Windows, which issues + * REQUEST SENSE with cbw->Length == 12 where it should + * be 6, and xbox360 issuing INQUIRY, TEST UNIT READY and + * REQUEST SENSE with cbw->Length == 10 where it should + * be 6 as well. + */ + if (cmnd_size <= common->cmnd_size) { + DBG(common, "%s is buggy! Expected length %d " + "but we got %d\n", name, + cmnd_size, common->cmnd_size); + cmnd_size = common->cmnd_size; + } else { + common->phase_error = 1; + return -EINVAL; + } + } + + /* Check that the LUN values are consistent */ + if (common->lun != lun) + DBG(common, "using LUN %d from CBW, not LUN %d from CDB\n", + common->lun, lun); + + /* Check the LUN */ + if (common->lun < common->nluns) { + curlun = &common->luns[common->lun]; + if (common->cmnd[0] != SCSI_REQ_SENSE) { + curlun->sense_data = SS_NO_SENSE; + curlun->info_valid = 0; + } + } else { + curlun = NULL; + common->bad_lun_okay = 0; + + /* INQUIRY and REQUEST SENSE commands are explicitly allowed + * to use unsupported LUNs; all others may not. */ + if (common->cmnd[0] != SCSI_INQUIRY && + common->cmnd[0] != SCSI_REQ_SENSE) { + DBG(common, "unsupported LUN %d\n", common->lun); + return -EINVAL; + } + } +#if 0 + /* If a unit attention condition exists, only INQUIRY and + * REQUEST SENSE commands are allowed; anything else must fail. */ + if (curlun && curlun->unit_attention_data != SS_NO_SENSE && + common->cmnd[0] != SCSI_INQUIRY && + common->cmnd[0] != SCSI_REQ_SENSE) { + curlun->sense_data = curlun->unit_attention_data; + curlun->unit_attention_data = SS_NO_SENSE; + return -EINVAL; + } +#endif + /* Check that only command bytes listed in the mask are non-zero */ + common->cmnd[1] &= 0x1f; /* Mask away the LUN */ + for (i = 1; i < cmnd_size; ++i) { + if (common->cmnd[i] && !(mask & (1 << i))) { + if (curlun) + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + } + + return 0; +} + +static int do_scsi_command(struct fsg_common *common) +{ + struct fsg_buffhd *bh; + int rc; + int reply = -EINVAL; + int i; + static char unknown[16]; + struct fsg_lun *curlun = &common->luns[common->lun]; + + dump_cdb(common); + + /* Wait for the next buffer to become available for data or status */ + bh = common->next_buffhd_to_fill; + common->next_buffhd_to_drain = bh; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(common); + if (rc) + return rc; + } + common->phase_error = 0; + common->short_packet_received = 0; + + down_read(&common->filesem); /* We're using the backing file */ + switch (common->cmnd[0]) { + + case SCSI_INQUIRY: + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_TO_HOST, + (1<<4), 0, + "INQUIRY"); + if (reply == 0) + reply = do_inquiry(common, bh); + break; + + case SCSI_MODE_SEL6: + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_FROM_HOST, + (1<<1) | (1<<4), 0, + "MODE SELECT(6)"); + if (reply == 0) + reply = do_mode_select(common, bh); + break; + + case SCSI_MODE_SEL10: + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_FROM_HOST, + (1<<1) | (3<<7), 0, + "MODE SELECT(10)"); + if (reply == 0) + reply = do_mode_select(common, bh); + break; + + case SCSI_MODE_SEN6: + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_TO_HOST, + (1<<1) | (1<<2) | (1<<4), 0, + "MODE SENSE(6)"); + if (reply == 0) + reply = do_mode_sense(common, bh); + break; + + case SCSI_MODE_SEN10: + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (1<<1) | (1<<2) | (3<<7), 0, + "MODE SENSE(10)"); + if (reply == 0) + reply = do_mode_sense(common, bh); + break; + + case SCSI_MED_REMOVL: + common->data_size_from_cmnd = 0; + reply = check_command(common, 6, DATA_DIR_NONE, + (1<<4), 0, + "PREVENT-ALLOW MEDIUM REMOVAL"); + if (reply == 0) + reply = do_prevent_allow(common); + break; + + case SCSI_READ6: + i = common->cmnd[4]; + common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + reply = check_command(common, 6, DATA_DIR_TO_HOST, + (7<<1) | (1<<4), 1, + "READ(6)"); + if (reply == 0) + reply = do_read(common); + break; + + case SCSI_READ10: + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]) << 9; + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (1<<1) | (0xf<<2) | (3<<7), 1, + "READ(10)"); + if (reply == 0) + reply = do_read(common); + break; + + case SCSI_READ12: + common->data_size_from_cmnd = + get_unaligned_be32(&common->cmnd[6]) << 9; + reply = check_command(common, 12, DATA_DIR_TO_HOST, + (1<<1) | (0xf<<2) | (0xf<<6), 1, + "READ(12)"); + if (reply == 0) + reply = do_read(common); + break; + + case SCSI_RD_CAPAC: + common->data_size_from_cmnd = 8; + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (0xf<<2) | (1<<8), 1, + "READ CAPACITY"); + if (reply == 0) + reply = do_read_capacity(common, bh); + break; + + case SCSI_RD_HEADER: + if (!common->luns[common->lun].cdrom) + goto unknown_cmnd; + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (3<<7) | (0x1f<<1), 1, + "READ HEADER"); + if (reply == 0) + reply = do_read_header(common, bh); + break; + + case SCSI_RD_TOC: + if (!common->luns[common->lun].cdrom) + goto unknown_cmnd; + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (7<<6) | (1<<1), 1, + "READ TOC"); + if (reply == 0) + reply = do_read_toc(common, bh); + break; + + case SCSI_RD_FMT_CAPAC: + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (3<<7), 1, + "READ FORMAT CAPACITIES"); + if (reply == 0) + reply = do_read_format_capacities(common, bh); + break; + + case SCSI_REQ_SENSE: + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_TO_HOST, + (1<<4), 0, + "REQUEST SENSE"); + if (reply == 0) + reply = do_request_sense(common, bh); + break; + + case SCSI_START_STP: + common->data_size_from_cmnd = 0; + reply = check_command(common, 6, DATA_DIR_NONE, + (1<<1) | (1<<4), 0, + "START-STOP UNIT"); + if (reply == 0) + reply = do_start_stop(common); + break; + + case SCSI_SYNC_CACHE: + common->data_size_from_cmnd = 0; + reply = check_command(common, 10, DATA_DIR_NONE, + (0xf<<2) | (3<<7), 1, + "SYNCHRONIZE CACHE"); + if (reply == 0) + reply = do_synchronize_cache(common); + break; + + case SCSI_TST_U_RDY: + common->data_size_from_cmnd = 0; + reply = check_command(common, 6, DATA_DIR_NONE, + 0, 1, + "TEST UNIT READY"); + break; + + /* Although optional, this command is used by MS-Windows. We + * support a minimal version: BytChk must be 0. */ + case SCSI_VERIFY: + common->data_size_from_cmnd = 0; + reply = check_command(common, 10, DATA_DIR_NONE, + (1<<1) | (0xf<<2) | (3<<7), 1, + "VERIFY"); + if (reply == 0) + reply = do_verify(common); + break; + + case SCSI_WRITE6: + i = common->cmnd[4]; + common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + reply = check_command(common, 6, DATA_DIR_FROM_HOST, + (7<<1) | (1<<4), 1, + "WRITE(6)"); + if (reply == 0) + reply = do_write(common); + break; + + case SCSI_WRITE10: + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]) << 9; + reply = check_command(common, 10, DATA_DIR_FROM_HOST, + (1<<1) | (0xf<<2) | (3<<7), 1, + "WRITE(10)"); + if (reply == 0) + reply = do_write(common); + break; + + case SCSI_WRITE12: + common->data_size_from_cmnd = + get_unaligned_be32(&common->cmnd[6]) << 9; + reply = check_command(common, 12, DATA_DIR_FROM_HOST, + (1<<1) | (0xf<<2) | (0xf<<6), 1, + "WRITE(12)"); + if (reply == 0) + reply = do_write(common); + break; + + /* Some mandatory commands that we recognize but don't implement. + * They don't mean much in this setting. It's left as an exercise + * for anyone interested to implement RESERVE and RELEASE in terms + * of Posix locks. */ + case SCSI_FORMAT: + case SCSI_RELEASE: + case SCSI_RESERVE: + case SCSI_SEND_DIAG: + /* Fall through */ + + default: +unknown_cmnd: + common->data_size_from_cmnd = 0; + sprintf(unknown, "Unknown x%02x", common->cmnd[0]); + reply = check_command(common, common->cmnd_size, + DATA_DIR_UNKNOWN, 0xff, 0, unknown); + if (reply == 0) { + curlun->sense_data = SS_INVALID_COMMAND; + reply = -EINVAL; + } + break; + } + up_read(&common->filesem); + + if (reply == -EPIPE) + return -EPIPE; + + /* Set up the single reply buffer for finish_reply() */ + if (reply == -EINVAL) + reply = 0; /* Error reply length */ + if (reply >= 0 && common->data_dir == DATA_DIR_TO_HOST) { + reply = min((u32) reply, common->data_size_from_cmnd); + bh->inreq->length = reply; + bh->state = BUF_STATE_FULL; + common->residue -= reply; + } /* Otherwise it's already set */ + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + struct usb_request *req = bh->outreq; + struct bulk_cb_wrap *cbw = req->buf; + struct fsg_common *common = fsg->common; + + /* Was this a real packet? Should it be ignored? */ + if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags)) + return -EINVAL; + + /* Is the CBW valid? */ + if (req->actual != US_BULK_CB_WRAP_LEN || + cbw->Signature != cpu_to_le32( + US_BULK_CB_SIGN)) { + DBG(fsg, "invalid CBW: len %u sig 0x%x\n", + req->actual, + le32_to_cpu(cbw->Signature)); + + /* The Bulk-only spec says we MUST stall the IN endpoint + * (6.6.1), so it's unavoidable. It also says we must + * retain this state until the next reset, but there's + * no way to tell the controller driver it should ignore + * Clear-Feature(HALT) requests. + * + * We aren't required to halt the OUT endpoint; instead + * we can simply accept and discard any data received + * until the next reset. */ + wedge_bulk_in_endpoint(fsg); + set_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); + return -EINVAL; + } + + /* Is the CBW meaningful? */ + if (cbw->Lun >= FSG_MAX_LUNS || cbw->Flags & ~US_BULK_FLAG_IN || + cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) { + DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, " + "cmdlen %u\n", + cbw->Lun, cbw->Flags, cbw->Length); + + /* We can do anything we want here, so let's stall the + * bulk pipes if we are allowed to. */ + if (common->can_stall) { + fsg_set_halt(fsg, fsg->bulk_out); + halt_bulk_in_endpoint(fsg); + } + return -EINVAL; + } + + /* Save the command for later */ + common->cmnd_size = cbw->Length; + memcpy(common->cmnd, cbw->CDB, common->cmnd_size); + if (cbw->Flags & US_BULK_FLAG_IN) + common->data_dir = DATA_DIR_TO_HOST; + else + common->data_dir = DATA_DIR_FROM_HOST; + common->data_size = le32_to_cpu(cbw->DataTransferLength); + if (common->data_size == 0) + common->data_dir = DATA_DIR_NONE; + common->lun = cbw->Lun; + common->tag = cbw->Tag; + return 0; +} + + +static int get_next_command(struct fsg_common *common) +{ + struct fsg_buffhd *bh; + int rc = 0; + + /* Wait for the next buffer to become available */ + bh = common->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(common); + if (rc) + return rc; + } + + /* Queue a request to read a Bulk-only CBW */ + set_bulk_out_req_length(common, bh, US_BULK_CB_WRAP_LEN); + bh->outreq->short_not_ok = 1; + START_TRANSFER_OR(common, bulk_out, bh->outreq, + &bh->outreq_busy, &bh->state) + /* Don't know what to do if common->fsg is NULL */ + return -EIO; + + /* We will drain the buffer in software, which means we + * can reuse it for the next filling. No need to advance + * next_buffhd_to_fill. */ + + /* Wait for the CBW to arrive */ + while (bh->state != BUF_STATE_FULL) { + rc = sleep_thread(common); + if (rc) + return rc; + } + + rc = fsg_is_set(common) ? received_cbw(common->fsg, bh) : -EIO; + bh->state = BUF_STATE_EMPTY; + + return rc; +} + + +/*-------------------------------------------------------------------------*/ + +static int enable_endpoint(struct fsg_common *common, struct usb_ep *ep) +{ + int rc; + + ep->driver_data = common; + rc = usb_ep_enable(ep); + if (rc) + ERROR(common, "can't enable %s, result %d\n", ep->name, rc); + return rc; +} + +static int alloc_request(struct fsg_common *common, struct usb_ep *ep, + struct usb_request **preq) +{ + *preq = usb_ep_alloc_request(ep); + if (*preq) + return 0; + ERROR(common, "can't allocate request for %s\n", ep->name); + return -ENOMEM; +} + +/* Reset interface setting and re-init endpoint state (toggle etc). */ +static int do_set_interface(struct fsg_common *common, struct fsg_dev *new_fsg) +{ + struct fsg_dev *fsg; + int i, rc = 0; + + if (common->running) + DBG(common, "reset interface\n"); + +reset: + /* Deallocate the requests */ + if (common->fsg) { + fsg = common->fsg; + + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + struct fsg_buffhd *bh = &common->buffhds[i]; + + if (bh->inreq) { + usb_ep_free_request(fsg->bulk_in, bh->inreq); + bh->inreq = NULL; + } + if (bh->outreq) { + usb_ep_free_request(fsg->bulk_out, bh->outreq); + bh->outreq = NULL; + } + } + + /* Disable the endpoints */ + if (fsg->bulk_in_enabled) { + usb_ep_disable(fsg->bulk_in); + fsg->bulk_in_enabled = 0; + } + if (fsg->bulk_out_enabled) { + usb_ep_disable(fsg->bulk_out); + fsg->bulk_out_enabled = 0; + } + + common->fsg = NULL; + } + + common->running = 0; + if (!new_fsg || rc) + return rc; + + common->fsg = new_fsg; + fsg = common->fsg; + + /* Enable the endpoints */ + rc = config_ep_by_speed(common->gadget, &(fsg->function), fsg->bulk_in); + if (rc) + goto reset; + rc = enable_endpoint(common, fsg->bulk_in); + if (rc) + goto reset; + fsg->bulk_in_enabled = 1; + + rc = config_ep_by_speed(common->gadget, &(fsg->function), + fsg->bulk_out); + if (rc) + goto reset; + rc = enable_endpoint(common, fsg->bulk_out); + if (rc) + goto reset; + fsg->bulk_out_enabled = 1; + common->bulk_out_maxpacket = 512; + clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); + + /* Allocate the requests */ + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + struct fsg_buffhd *bh = &common->buffhds[i]; + + rc = alloc_request(common, fsg->bulk_in, &bh->inreq); + if (rc) + goto reset; + rc = alloc_request(common, fsg->bulk_out, &bh->outreq); + if (rc) + goto reset; + bh->inreq->buf = bh->outreq->buf = bh->buf; + bh->inreq->context = bh->outreq->context = bh; + bh->inreq->complete = bulk_in_complete; + bh->outreq->complete = bulk_out_complete; + } + + common->running = 1; + + return rc; +} + + +/****************************** ALT CONFIGS ******************************/ + + +static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct fsg_dev *fsg = fsg_from_func(f); + fsg->common->new_fsg = fsg; + raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); + return 0; +} + +static void fsg_disable(struct usb_function *f) +{ + struct fsg_dev *fsg = fsg_from_func(f); + fsg->common->new_fsg = NULL; + raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); +} + +/*-------------------------------------------------------------------------*/ + +static void handle_exception(struct fsg_common *common) +{ + int i; + struct fsg_buffhd *bh; + enum fsg_state old_state; + struct fsg_lun *curlun; + unsigned int exception_req_tag; + + /* Cancel all the pending transfers */ + if (common->fsg) { + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + bh = &common->buffhds[i]; + if (bh->inreq_busy) + usb_ep_dequeue(common->fsg->bulk_in, bh->inreq); + if (bh->outreq_busy) + usb_ep_dequeue(common->fsg->bulk_out, + bh->outreq); + } + + /* Wait until everything is idle */ + for (;;) { + int num_active = 0; + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + bh = &common->buffhds[i]; + num_active += bh->inreq_busy + bh->outreq_busy; + } + if (num_active == 0) + break; + if (sleep_thread(common)) + return; + } + + /* Clear out the controller's fifos */ + if (common->fsg->bulk_in_enabled) + usb_ep_fifo_flush(common->fsg->bulk_in); + if (common->fsg->bulk_out_enabled) + usb_ep_fifo_flush(common->fsg->bulk_out); + } + + /* Reset the I/O buffer states and pointers, the SCSI + * state, and the exception. Then invoke the handler. */ + + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + bh = &common->buffhds[i]; + bh->state = BUF_STATE_EMPTY; + } + common->next_buffhd_to_fill = &common->buffhds[0]; + common->next_buffhd_to_drain = &common->buffhds[0]; + exception_req_tag = common->exception_req_tag; + old_state = common->state; + + report_exception("handling", old_state); + + if (old_state == FSG_STATE_ABORT_BULK_OUT) + common->state = FSG_STATE_STATUS_PHASE; + else { + for (i = 0; i < common->nluns; ++i) { + curlun = &common->luns[i]; + curlun->sense_data = SS_NO_SENSE; + curlun->info_valid = 0; + } + common->state = FSG_STATE_IDLE; + } + + /* Carry out any extra actions required for the exception */ + switch (old_state) { + case FSG_STATE_ABORT_BULK_OUT: + send_status(common); + + if (common->state == FSG_STATE_STATUS_PHASE) + common->state = FSG_STATE_IDLE; + break; + + case FSG_STATE_RESET: + /* In case we were forced against our will to halt a + * bulk endpoint, clear the halt now. (The SuperH UDC + * requires this.) */ + if (!fsg_is_set(common)) + break; + if (test_and_clear_bit(IGNORE_BULK_OUT, + &common->fsg->atomic_bitflags)) + usb_ep_clear_halt(common->fsg->bulk_in); + + if (common->ep0_req_tag == exception_req_tag) + ep0_queue(common); /* Complete the status stage */ + + break; + + case FSG_STATE_CONFIG_CHANGE: + do_set_interface(common, common->new_fsg); + break; + + case FSG_STATE_EXIT: + case FSG_STATE_TERMINATED: + do_set_interface(common, NULL); /* Free resources */ + common->state = FSG_STATE_TERMINATED; /* Stop the thread */ + break; + + case FSG_STATE_INTERFACE_CHANGE: + case FSG_STATE_DISCONNECT: + case FSG_STATE_COMMAND_PHASE: + case FSG_STATE_DATA_PHASE: + case FSG_STATE_STATUS_PHASE: + case FSG_STATE_IDLE: + break; + } +} + +/*-------------------------------------------------------------------------*/ + +static void fsg_main_thread(void *fsg_) +{ + struct fsg_dev *fsg = fsg_dev_get(fsg_); + struct fsg_common *common = fsg->common; + struct f_ums_opts *opts = f_ums_opts_get(common->opts); + struct fsg_buffhd *bh; + unsigned i; + int ret = 0; + + + /* The main loop */ + while (common->state != FSG_STATE_TERMINATED) { + if (exception_in_progress(common)) { + handle_exception(common); + continue; + } + + if (!common->running) { + ret = sleep_thread(common); + if (ret) + break; + continue; + } + + ret = get_next_command(common); + if (ret) + continue; + + if (!exception_in_progress(common)) + common->state = FSG_STATE_DATA_PHASE; + + if (do_scsi_command(common) || finish_reply(common)) + continue; + + if (!exception_in_progress(common)) + common->state = FSG_STATE_STATUS_PHASE; + + if (send_status(common)) + continue; + + if (!exception_in_progress(common)) + common->state = FSG_STATE_IDLE; + } + + if (ret && ret != -ERESTARTSYS) + pr_warn("%s: error %pe\n", __func__, ERR_PTR(ret)); + + usb_free_all_descriptors(&fsg->function); + + for (i = 0; i < ums_count; i++) + close(ums[i].fd); + + bh = common->buffhds; + i = FSG_NUM_BUFFERS; + + do { + dma_free(bh->buf); + } while (++bh, --i); + + ums_count = 0; + ums_files = NULL; + + f_ums_opts_put(opts); + fsg_dev_put(fsg); +} + +static void fsg_common_release(struct fsg_common *common); + +static struct fsg_common *fsg_common_setup(struct f_ums_opts *opts) +{ + struct fsg_common *common; + + /* Allocate? */ + common = calloc(sizeof(*common), 1); + if (!common) + return NULL; + + common->ops = NULL; + common->private_data = NULL; + common->opts = opts; + + return common; +} + +static int fsg_common_init(struct fsg_common *common, + struct usb_composite_dev *cdev) +{ + struct usb_gadget *gadget = cdev->gadget; + struct file_list_entry *fentry; + struct fsg_buffhd *bh; + int nluns, i, rc; + + ums_count = 0; + + common->gadget = gadget; + common->ep0 = gadget->ep0; + common->ep0req = cdev->req; + + thread_task = bthread_run(fsg_main_thread, common->fsg, "mass-storage-gadget"); + if (IS_ERR(thread_task)) + return PTR_ERR(thread_task); + + file_list_detect_all(ums_files); + + file_list_for_each_entry(ums_files, fentry) { + unsigned flags = O_RDWR; + struct stat st; + int fd; + + if (fentry->flags) { + pr_err("flags not supported\n"); + rc = -ENOSYS; + goto close; + } + + fd = open(fentry->filename, flags); + if (fd < 0) { + pr_err("open('%s') failed: %pe\n", + fentry->filename, ERR_PTR(fd)); + rc = fd; + goto close; + } + + rc = fstat(fd, &st); + if (rc < 0) { + pr_err("stat('%s') failed: %pe\n", + fentry->filename, ERR_PTR(rc)); + goto close; + } + + if (st.st_size % SECTOR_SIZE != 0) { + pr_err("exporting '%s' failed: invalid block size\n", + fentry->filename); + goto close; + } + + ums[ums_count].fd = fd; + ums[ums_count].num_sectors = st.st_size / SECTOR_SIZE; + + strlcpy(ums[ums_count].name, fentry->name, sizeof(ums[ums_count].name)); + + DBG(common, "LUN %d, %s sector_count %#x\n", + ums_count, fentry->name, ums[ums_count].num_sectors); + + ums_count++; + } + + /* Find out how many LUNs there should be */ + nluns = ums_count; + if (nluns < 1 || nluns > FSG_MAX_LUNS) { + pr_warn("invalid number of LUNs: %u\n", nluns); + rc = -EINVAL; + goto close; + } + + /* Maybe allocate device-global string IDs, and patch descriptors */ + if (fsg_strings[FSG_STRING_INTERFACE].id == 0) { + rc = usb_string_id(cdev); + if (unlikely(rc < 0)) + goto error_release; + fsg_strings[FSG_STRING_INTERFACE].id = rc; + fsg_intf_desc.iInterface = rc; + } + + common->nluns = nluns; + + for (i = 0; i < nluns; i++) { + common->luns[i].removable = 1; + + rc = fsg_lun_open(&common->luns[i], ums[i].num_sectors, ""); + if (rc) + goto error_luns; + } + common->lun = 0; + + /* Data buffers cyclic list */ + bh = common->buffhds; + + i = FSG_NUM_BUFFERS; + goto buffhds_first_it; + do { + bh->next = bh + 1; + ++bh; +buffhds_first_it: + bh->inreq_busy = 0; + bh->outreq_busy = 0; + bh->buf = dma_alloc(FSG_BUFLEN); + if (unlikely(!bh->buf)) { + rc = -ENOMEM; + goto error_release; + } + } while (--i); + bh->next = common->buffhds; + + snprintf(common->inquiry_string, sizeof common->inquiry_string, + "%-8s%-16s%04x", + "Linux ", + "File-Store Gadget", + 0xffff); + + /* Some peripheral controllers are known not to be able to + * halt bulk endpoints correctly. If one of them is present, + * disable stalls. + */ + + /* Information */ + DBG(common, FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n"); + DBG(common, "Number of LUNs=%d\n", common->nluns); + + return 0; + +error_luns: + common->nluns = i + 1; +error_release: + common->state = FSG_STATE_TERMINATED; /* The thread is dead */ + fsg_common_release(common); +close: + for (i = 0; i < ums_count; i++) + close(ums[i].fd); + return rc; +} + +static void fsg_common_release(struct fsg_common *common) +{ + /* If the thread isn't already dead, tell it to exit now */ + if (common->state != FSG_STATE_TERMINATED) { + raise_exception(common, FSG_STATE_EXIT); + } + + bthread_cancel(thread_task); +} + + +static void fsg_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct fsg_dev *fsg = fsg_from_func(f); + + DBG(fsg, "unbind\n"); + + if (fsg->common->fsg == fsg) { + fsg->common->new_fsg = NULL; + raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); + } + + fsg_common_release(fsg->common); +} + +static int fsg_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct fsg_dev *fsg = fsg_from_func(f); + struct usb_gadget *gadget = c->cdev->gadget; + int ret; + struct usb_ep *ep; + unsigned max_burst; + struct fsg_common *common = fsg->common; + + if (!ums_files) { + struct f_ums_opts *opts = container_of(f->fi, struct f_ums_opts, func_inst); + + ums_files = opts->files; + } + + fsg->gadget = gadget; + + DBG(fsg, "bind\n"); + + ret = fsg_common_init(common, c->cdev); + if (ret) + goto remove_ums_files; + + /* New interface */ + ret = usb_interface_id(c, f); + if (ret < 0) + goto fsg_common_release; + fsg_intf_desc.bInterfaceNumber = ret; + fsg->interface_number = ret; + + ret = -EOPNOTSUPP; + + /* Find all the endpoints we will use */ + ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc); + if (!ep) + goto autoconf_fail; + + ep->driver_data = common; /* claim the endpoint */ + fsg->bulk_in = ep; + + ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc); + if (!ep) + goto autoconf_fail; + + ep->driver_data = common; /* claim the endpoint */ + fsg->bulk_out = ep; + + /* Assume endpoint addresses are the same for both speeds */ + fsg_hs_bulk_in_desc.bEndpointAddress = + fsg_fs_bulk_in_desc.bEndpointAddress; + fsg_hs_bulk_out_desc.bEndpointAddress = + fsg_fs_bulk_out_desc.bEndpointAddress; + + /* Calculate bMaxBurst, we know packet size is 1024 */ + max_burst = min_t(unsigned, FSG_BUFLEN / 1024, 15); + + fsg_ss_bulk_in_desc.bEndpointAddress = + fsg_fs_bulk_in_desc.bEndpointAddress; + fsg_ss_bulk_in_comp_desc.bMaxBurst = max_burst; + + fsg_ss_bulk_out_desc.bEndpointAddress = + fsg_fs_bulk_out_desc.bEndpointAddress; + fsg_ss_bulk_out_comp_desc.bMaxBurst = max_burst; + + /* Copy descriptors */ + return usb_assign_descriptors(f, fsg_fs_function, fsg_hs_function, + fsg_ss_function, fsg_ss_function); + +autoconf_fail: + ERROR(fsg, "unable to autoconfigure all endpoints\n"); +fsg_common_release: + fsg_common_release(common); +remove_ums_files: + ums_files = NULL; + + return ret; +} + + +/****************************** ADD FUNCTION ******************************/ + +static struct usb_gadget_strings *fsg_strings_array[] = { + &fsg_stringtab, + NULL, +}; + +static void fsg_free(struct usb_function *f) +{ + struct fsg_dev *fsg; + + fsg = container_of(f, struct fsg_dev, function); + + fsg_dev_put(fsg); +} + +static struct usb_function *fsg_alloc(struct usb_function_instance *fi) +{ + struct f_ums_opts *opts = fsg_opts_from_func_inst(fi); + struct fsg_common *common = opts->common; + struct fsg_dev *fsg; + + fsg = kzalloc(sizeof(*fsg), GFP_KERNEL); + if (!fsg) + return ERR_PTR(-ENOMEM); + + fsg->function.name = FSG_DRIVER_DESC; + fsg->function.strings = fsg_strings_array; + /* descriptors are per-instance copies */ + fsg->function.bind = fsg_bind; + fsg->function.set_alt = fsg_set_alt; + fsg->function.setup = fsg_setup; + fsg->function.disable = fsg_disable; + fsg->function.unbind = fsg_unbind; + fsg->function.free_func = fsg_free; + + fsg->common = common; + common->fsg = fsg_dev_get(fsg); + + return &fsg->function; +} + +static void fsg_free_instance(struct usb_function_instance *fi) +{ + struct f_ums_opts *opts = fsg_opts_from_func_inst(fi); + + f_ums_opts_put(opts); +} + +static struct usb_function_instance *fsg_alloc_inst(void) +{ + struct f_ums_opts *opts; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + + opts->func_inst.free_func_inst = fsg_free_instance; + + opts->common = fsg_common_setup(opts); + if (!opts->common) { + free(opts); + return ERR_PTR(-ENOMEM); + } + + f_ums_opts_get(opts); + + return &opts->func_inst; +} + +DECLARE_USB_FUNCTION_INIT(ums, fsg_alloc_inst, fsg_alloc); diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/function/f_serial.c index 39c44448c4..a768c580ea 100644 --- a/drivers/usb/gadget/f_serial.c +++ b/drivers/usb/gadget/function/f_serial.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * f_serial.c - generic USB serial function driver * * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) * Copyright (C) 2008 by David Brownell * Copyright (C) 2008 by Nokia Corporation - * - * This software is distributed under the terms of the GNU General - * Public License ("GPL") as published by the Free Software Foundation, - * either version 2 of that License or (at your option) any later version. */ #include <common.h> @@ -15,7 +12,6 @@ #include <linux/err.h> #include "u_serial.h" -#include "gadget_chips.h" /* @@ -235,7 +231,7 @@ static int gser_bind(struct usb_configuration *c, struct usb_function *f) gser_ss_out_desc.bEndpointAddress = gser_fs_out_desc.bEndpointAddress; status = usb_assign_descriptors(f, gser_fs_function, gser_hs_function, - gser_ss_function); + gser_ss_function, gser_ss_function); if (status) goto fail; DBG(cdev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n", diff --git a/drivers/usb/gadget/function/storage_common.c b/drivers/usb/gadget/function/storage_common.c new file mode 100644 index 0000000000..60e0994235 --- /dev/null +++ b/drivers/usb/gadget/function/storage_common.c @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * storage_common.c -- Common definitions for mass storage functionality + * + * Copyright (C) 2003-2008 Alan Stern + * Copyeight (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz (m.nazarewicz@samsung.com) + * + * Ported to u-boot: + * Andrzej Pietrasiewicz <andrzej.p@samsung.com> + * + * Code refactoring & cleanup: + * Łukasz Majewski <l.majewski@samsung.com> + */ + +#include "storage_common.h" + +/* + * This file requires the following identifiers used in USB strings to + * be defined (each of type pointer to char): + * - fsg_string_manufacturer -- name of the manufacturer + * - fsg_string_product -- name of the product + * - fsg_string_serial -- product's serial + * - fsg_string_config -- name of the configuration + * - fsg_string_interface -- name of the interface + * The first four are only needed when FSG_DESCRIPTORS_DEVICE_STRINGS + * macro is defined prior to including this file. + */ + +/* There is only one interface. */ + +struct usb_interface_descriptor fsg_intf_desc = { + .bLength = sizeof fsg_intf_desc, + .bDescriptorType = USB_DT_INTERFACE, + + .bNumEndpoints = 2, /* Adjusted during fsg_bind() */ + .bInterfaceClass = USB_CLASS_MASS_STORAGE, + .bInterfaceSubClass = USB_SC_SCSI, /* Adjusted during fsg_bind() */ + .bInterfaceProtocol = USB_PR_BULK, /* Adjusted during fsg_bind() */ + .iInterface = FSG_STRING_INTERFACE, +}; + +/* + * Three full-speed endpoint descriptors: bulk-in, bulk-out, and + * interrupt-in. + */ + +struct usb_endpoint_descriptor fsg_fs_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + /* wMaxPacketSize set by autoconfiguration */ +}; + +struct usb_endpoint_descriptor fsg_fs_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + /* wMaxPacketSize set by autoconfiguration */ +}; + +struct usb_descriptor_header *fsg_fs_function[] = { + (struct usb_descriptor_header *) &fsg_intf_desc, + (struct usb_descriptor_header *) &fsg_fs_bulk_in_desc, + (struct usb_descriptor_header *) &fsg_fs_bulk_out_desc, + NULL, +}; + +/* + * USB 2.0 devices need to expose both high speed and full speed + * descriptors, unless they only run at full speed. + * + * That means alternate endpoint descriptors (bigger packets) + * and a "device qualifier" ... plus more construction options + * for the configuration descriptor. + */ +struct usb_endpoint_descriptor fsg_hs_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +struct usb_endpoint_descriptor fsg_hs_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), + .bInterval = 1, /* NAK every 1 uframe */ +}; + +struct usb_descriptor_header *fsg_hs_function[] = { + (struct usb_descriptor_header *) &fsg_intf_desc, + (struct usb_descriptor_header *) &fsg_hs_bulk_in_desc, + (struct usb_descriptor_header *) &fsg_hs_bulk_out_desc, + NULL, +}; + +struct usb_endpoint_descriptor fsg_ss_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = { + .bLength = sizeof(fsg_ss_bulk_in_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /*.bMaxBurst = DYNAMIC, */ +}; + +struct usb_endpoint_descriptor fsg_ss_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = { + .bLength = sizeof(fsg_ss_bulk_in_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /*.bMaxBurst = DYNAMIC, */ +}; + +struct usb_descriptor_header *fsg_ss_function[] = { + (struct usb_descriptor_header *) &fsg_intf_desc, + (struct usb_descriptor_header *) &fsg_ss_bulk_in_desc, + (struct usb_descriptor_header *) &fsg_ss_bulk_in_comp_desc, + (struct usb_descriptor_header *) &fsg_ss_bulk_out_desc, + (struct usb_descriptor_header *) &fsg_ss_bulk_out_comp_desc, + NULL, +}; +EXPORT_SYMBOL_GPL(fsg_ss_function); + +/* Maxpacket and other transfer characteristics vary by speed. */ +struct usb_endpoint_descriptor * +fsg_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs, + struct usb_endpoint_descriptor *hs) +{ + if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + return hs; + return fs; +} + +/*-------------------------------------------------------------------------*/ + +/* + * If the next two routines are called while the gadget is registered, + * the caller must own fsg->filesem for writing. + */ + +int fsg_lun_open(struct fsg_lun *curlun, unsigned int num_sectors, + const char *filename) +{ + int ro; + + /* R/W if we can, R/O if we must */ + ro = curlun->initially_ro; + + curlun->ro = ro; + curlun->file_length = num_sectors << 9; + curlun->num_sectors = num_sectors; + debug("open backing file: %s\n", filename); + + return 0; +} + +void fsg_lun_close(struct fsg_lun *curlun) +{ +} + +/*-------------------------------------------------------------------------*/ + +/* + * Sync the file data, don't bother with the metadata. + * This code was copied from fs/buffer.c:sys_fdatasync(). + */ +int fsg_lun_fsync_sub(struct fsg_lun *curlun) +{ + return 0; +} + +void store_cdrom_address(u8 *dest, int msf, u32 addr) +{ + if (msf) { + /* Convert to Minutes-Seconds-Frames */ + addr >>= 2; /* Convert to 2048-byte frames */ + addr += 2*75; /* Lead-in occupies 2 seconds */ + dest[3] = addr % 75; /* Frames */ + addr /= 75; + dest[2] = addr % 60; /* Seconds */ + addr /= 60; + dest[1] = addr; /* Minutes */ + dest[0] = 0; /* Reserved */ + } else { + /* Absolute sector */ + put_unaligned_be32(addr, dest); + } +} + +/*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/gadget/function/storage_common.h b/drivers/usb/gadget/function/storage_common.h new file mode 100644 index 0000000000..29afe77685 --- /dev/null +++ b/drivers/usb/gadget/function/storage_common.h @@ -0,0 +1,251 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef USB_STORAGE_COMMON_H +#define USB_STORAGE_COMMON_H + +#include <driver.h> +#include <linux/usb/storage.h> +#include <asm/unaligned.h> +#include <linux/usb/mass_storage.h> + +#ifndef DEBUG +#undef VERBOSE_DEBUG +#undef DUMP_MSGS +#endif /* !DEBUG */ + +#define VLDBG(lun, fmt, args...) dev_vdbg(&(lun)->dev, fmt, ## args) +#define LDBG(lun, fmt, args...) dev_dbg (&(lun)->dev, fmt, ## args) +#define LERROR(lun, fmt, args...) dev_err (&(lun)->dev, fmt, ## args) +#define LWARN(lun, fmt, args...) dev_warn(&(lun)->dev, fmt, ## args) +#define LINFO(lun, fmt, args...) dev_info(&(lun)->dev, fmt, ## args) + +/* + * Keep those macros in sync with those in + * include/linux/usb/composite.h or else GCC will complain. If they + * are identical (the same names of arguments, white spaces in the + * same places) GCC will allow redefinition otherwise (even if some + * white space is removed or added) warning will be issued. + * + * Those macros are needed here because File Storage Gadget does not + * include the composite.h header. For composite gadgets those macros + * are redundant since composite.h is included any way. + * + * One could check whether those macros are already defined (which + * would indicate composite.h had been included) or not (which would + * indicate we were in FSG) but this is not done because a warning is + * desired if definitions here differ from the ones in composite.h. + * + * We want the definitions to match and be the same in File Storage + * Gadget as well as Mass Storage Function (and so composite gadgets + * using MSF). If someone changes them in composite.h it will produce + * a warning in this file when building MSF. + */ + +#define DBG(d, fmt, args...) \ + dev_dbg(&(d)->gadget->dev , fmt , ## args) +#define VDBG(d, fmt, args...) \ + dev_vdbg(&(d)->gadget->dev , fmt , ## args) +#define ERROR(d, fmt, args...) \ + dev_err(&(d)->gadget->dev , fmt , ## args) +#define WARNING(d, fmt, args...) \ + dev_warn(&(d)->gadget->dev , fmt , ## args) +#define INFO(d, fmt, args...) \ + dev_info(&(d)->gadget->dev , fmt , ## args) + +#ifdef DUMP_MSGS + +/* dump_msg(fsg, const char * label, const u8 * buf, unsigned length); */ +# define dump_msg(fsg, label, buf, length) do { \ + if (length < 512) { \ + DBG(fsg, "%s, length %u:\n", label, length); \ + print_hex_dump("", DUMP_PREFIX_OFFSET, \ + 16, 1, buf, length, 0); \ + } \ +} while (0) + +# define dump_cdb(fsg) do { } while (0) + +#else + +# define dump_msg(fsg, /* const char * */ label, \ + /* const u8 * */ buf, /* unsigned */ length) do { } while (0) + +# ifdef VERBOSE_DEBUG + +# define dump_cdb(fsg) \ + print_hex_dump("SCSI CDB: ", DUMP_PREFIX_NONE, \ + 16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0) \ + +# else + +# define dump_cdb(fsg) do { } while (0) + +# endif /* VERBOSE_DEBUG */ + +#endif /* DUMP_MSGS */ + +/* + * Thanks to NetChip Technologies for donating this product ID. + * + * DO NOT REUSE THESE IDs with any other driver!! Ever!! + * Instead: allocate your own, using normal USB-IF procedures. + */ + +#define FSG_VENDOR_ID 0x0525 /* NetChip */ +#define FSG_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */ + +/* Length of a SCSI Command Data Block */ +#define MAX_COMMAND_SIZE 16 + +/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */ +#define SS_NO_SENSE 0 +#define SS_COMMUNICATION_FAILURE 0x040800 +#define SS_INVALID_COMMAND 0x052000 +#define SS_INVALID_FIELD_IN_CDB 0x052400 +#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x052100 +#define SS_LOGICAL_UNIT_NOT_SUPPORTED 0x052500 +#define SS_MEDIUM_NOT_PRESENT 0x023a00 +#define SS_MEDIUM_REMOVAL_PREVENTED 0x055302 +#define SS_NOT_READY_TO_READY_TRANSITION 0x062800 +#define SS_RESET_OCCURRED 0x062900 +#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x053900 +#define SS_UNRECOVERED_READ_ERROR 0x031100 +#define SS_WRITE_ERROR 0x030c02 +#define SS_WRITE_PROTECTED 0x072700 + +#define SK(x) ((u8) ((x) >> 16)) /* Sense Key byte, etc. */ +#define ASC(x) ((u8) ((x) >> 8)) +#define ASCQ(x) ((u8) (x)) + +/*-------------------------------------------------------------------------*/ + +struct fsg_lun { + loff_t file_length; + loff_t num_sectors; + + unsigned int initially_ro:1; + unsigned int ro:1; + unsigned int removable:1; + unsigned int cdrom:1; + unsigned int prevent_medium_removal:1; + unsigned int registered:1; + unsigned int info_valid:1; + unsigned int nofua:1; + + u32 sense_data; + u32 sense_data_info; + u32 unit_attention_data; + + struct device dev; +}; + +#define fsg_lun_is_open(curlun) ((curlun)->filp != NULL) + +/* Big enough to hold our biggest descriptor */ +#define EP0_BUFSIZE 256 +#define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */ + +/* Number of buffers we will use. 2 is enough for double-buffering */ +#define FSG_NUM_BUFFERS 2 + +/* Default size of buffer length. */ +#define FSG_BUFLEN ((u32)131072) + +/* Maximal number of LUNs supported in mass storage function */ +#define FSG_MAX_LUNS 8 + +enum fsg_buffer_state { + BUF_STATE_EMPTY = 0, + BUF_STATE_FULL, + BUF_STATE_BUSY +}; + +/* + * When FSG_BUFFHD_STATIC_BUFFER is defined when this file is included + * the fsg_buffhd structure's buf field will be an array of FSG_BUFLEN + * characters rather then a pointer to void. + */ + +struct fsg_buffhd { + void *buf; + enum fsg_buffer_state state; + struct fsg_buffhd *next; + + /* + * The NetChip 2280 is faster, and handles some protocol faults + * better, if we don't submit any short bulk-out read requests. + * So we will record the intended request length here. + */ + unsigned int bulk_out_intended_length; + + struct usb_request *inreq; + int inreq_busy; + struct usb_request *outreq; + int outreq_busy; +}; + +enum fsg_state { + /* This one isn't used anywhere */ + FSG_STATE_COMMAND_PHASE = -10, + FSG_STATE_DATA_PHASE, + FSG_STATE_STATUS_PHASE, + + FSG_STATE_IDLE = 0, + FSG_STATE_ABORT_BULK_OUT, + FSG_STATE_RESET, + FSG_STATE_INTERFACE_CHANGE, + FSG_STATE_CONFIG_CHANGE, + FSG_STATE_DISCONNECT, + FSG_STATE_EXIT, + FSG_STATE_TERMINATED +}; + +enum data_direction { + DATA_DIR_UNKNOWN = 0, + DATA_DIR_FROM_HOST, + DATA_DIR_TO_HOST, + DATA_DIR_NONE +}; + +/*-------------------------------------------------------------------------*/ + +static inline u32 get_unaligned_be24(u8 *buf) +{ + return 0xffffff & (u32) get_unaligned_be32(buf - 1); +} + +/*-------------------------------------------------------------------------*/ + +enum { + FSG_STRING_INTERFACE +}; + +/*-------------------------------------------------------------------------*/ + +extern struct usb_interface_descriptor fsg_intf_desc; + +extern struct usb_endpoint_descriptor fsg_fs_bulk_in_desc; +extern struct usb_endpoint_descriptor fsg_fs_bulk_out_desc; +extern struct usb_descriptor_header *fsg_fs_function[]; + +extern struct usb_endpoint_descriptor fsg_hs_bulk_in_desc; +extern struct usb_endpoint_descriptor fsg_hs_bulk_out_desc; +extern struct usb_descriptor_header *fsg_hs_function[]; + +extern struct usb_endpoint_descriptor fsg_ss_bulk_in_desc; +extern struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc; +extern struct usb_endpoint_descriptor fsg_ss_bulk_out_desc; +extern struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc; +extern struct usb_descriptor_header *fsg_ss_function[]; + +int fsg_lun_open(struct fsg_lun *curlun, unsigned int num_sectors, + const char *filename); +void fsg_lun_close(struct fsg_lun *curlun); + +struct usb_endpoint_descriptor * +fsg_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs, + struct usb_endpoint_descriptor *hs); +int fsg_lun_fsync_sub(struct fsg_lun *curlun); +void store_cdrom_address(u8 *dest, int msf, u32 addr); + +#endif /* USB_STORAGE_COMMON_H */ diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/function/u_serial.c index 140346e770..ca4e77c5ff 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * u_serial.c - utilities for USB gadget "serial port"/TTY support * @@ -9,17 +10,13 @@ * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com) * Copyright (C) 2000 Peter Berger (pberger@brimson.com) * Copyright (C) 2000 Al Borchers (alborchers@steinerpoint.com) - * - * This software is distributed under the terms of the GNU General - * Public License ("GPL") as published by the Free Software Foundation, - * either version 2 of that License or (at your option) any later version. */ /* #define VERBOSE_DEBUG */ #include <common.h> #include <complete.h> -#include <usb/cdc.h> +#include <linux/usb/cdc.h> #include <kfifo.h> #include <clock.h> #include <linux/err.h> @@ -158,12 +155,13 @@ static void gs_read_complete(struct usb_ep *ep, struct usb_request *req) { struct gs_port *port = ep->driver_data; + list_add_tail(&req->list, &port->read_pool); + port->read_nb_queued--; + if (req->status == -ESHUTDOWN) return; kfifo_put(port->recv_fifo, req->buf, req->actual); - list_add_tail(&req->list, &port->read_pool); - port->read_nb_queued--; gs_start_rx(port); } diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/function/u_serial.h index 72772dabd5..44fcace030 100644 --- a/drivers/usb/gadget/u_serial.h +++ b/drivers/usb/gadget/function/u_serial.h @@ -1,19 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * u_serial.h - interface to USB gadget "serial port"/TTY utilities * * Copyright (C) 2008 David Brownell * Copyright (C) 2008 by Nokia Corporation - * - * This software is distributed under the terms of the GNU General - * Public License ("GPL") as published by the Free Software Foundation, - * either version 2 of that License or (at your option) any later version. */ #ifndef __U_SERIAL_H #define __U_SERIAL_H -#include <usb/composite.h> -#include <usb/cdc.h> +#include <linux/usb/composite.h> +#include <linux/usb/cdc.h> #define MAX_U_SERIAL_PORTS 4 diff --git a/drivers/usb/gadget/functions.c b/drivers/usb/gadget/functions.c index 3b5d4dfc28..7a57915007 100644 --- a/drivers/usb/gadget/functions.c +++ b/drivers/usb/gadget/functions.c @@ -1,7 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> #include <linux/err.h> -#include <usb/composite.h> +#include <linux/usb/composite.h> static LIST_HEAD(func_list); diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h deleted file mode 100644 index c41336f698..0000000000 --- a/drivers/usb/gadget/gadget_chips.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * USB device controllers have lots of quirks. Use these macros in - * gadget drivers or other code that needs to deal with them, and which - * autoconfigures instead of using early binding to the hardware. - * - * This SHOULD eventually work like the ARM mach_is_*() stuff, driven by - * some config file that gets updated as new hardware is supported. - * (And avoiding all runtime comparisons in typical one-choice configs!) - * - * NOTE: some of these controller drivers may not be available yet. - * Some are available on 2.4 kernels; several are available, but not - * yet pushed in the 2.6 mainline tree. - */ - -#ifndef __GADGET_CHIPS_H -#define __GADGET_CHIPS_H - -#include <usb/gadget.h> - -/* - * NOTICE: the entries below are alphabetical and should be kept - * that way. - * - * Always be sure to add new entries to the correct position or - * accept the bashing later. - * - * If you have forgotten the alphabetical order let VIM/EMACS - * do that for you. - */ -#define gadget_is_at91(g) (!strcmp("at91_udc", (g)->name)) -#define gadget_is_goku(g) (!strcmp("goku_udc", (g)->name)) -#define gadget_is_musbhdrc(g) (!strcmp("musb-hdrc", (g)->name)) -#define gadget_is_net2280(g) (!strcmp("net2280", (g)->name)) -#define gadget_is_pxa(g) (!strcmp("pxa25x_udc", (g)->name)) -#define gadget_is_pxa27x(g) (!strcmp("pxa27x_udc", (g)->name)) - -/** - * gadget_supports_altsettings - return true if altsettings work - * @gadget: the gadget in question - */ -static inline bool gadget_supports_altsettings(struct usb_gadget *gadget) -{ - /* PXA 21x/25x/26x has no altsettings at all */ - if (gadget_is_pxa(gadget)) - return false; - - /* PXA 27x and 3xx have *broken* altsetting support */ - if (gadget_is_pxa27x(gadget)) - return false; - - /* Everything else is *presumably* fine ... */ - return true; -} - -#endif /* __GADGET_CHIPS_H */ diff --git a/drivers/usb/gadget/legacy/Makefile b/drivers/usb/gadget/legacy/Makefile new file mode 100644 index 0000000000..5d5382a673 --- /dev/null +++ b/drivers/usb/gadget/legacy/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 + +ccflags-y := -I$(srctree)/drivers/usb/gadget/ +ccflags-y += -I$(srctree)/drivers/usb/gadget/udc/ +ccflags-y += -I$(srctree)/drivers/usb/gadget/function/ + +obj-$(CONFIG_USB_GADGET_SERIAL) += serial.o +obj-$(CONFIG_USB_GADGET) += multi.o diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/legacy/multi.c index d6edfb8cf2..ddb3d4158c 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/legacy/multi.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * multi.c -- Multifunction Composite driver * @@ -5,15 +6,10 @@ * Copyright (C) 2008 Nokia Corporation * Copyright (C) 2009 Samsung Electronics * Author: Michal Nazarewicz (mina86@mina86.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <common.h> -#include <usb/gadget-multi.h> +#include <linux/usb/gadget-multi.h> #include <linux/err.h> #include "u_serial.h" @@ -60,6 +56,8 @@ static struct usb_function_instance *fi_dfu; static struct usb_function *f_dfu; static struct usb_function_instance *fi_fastboot; static struct usb_function *f_fastboot; +static struct usb_function_instance *fi_ums; +static struct usb_function *f_ums; static struct usb_configuration config = { .bConfigurationValue = 1, @@ -127,10 +125,7 @@ static int multi_bind_fastboot(struct usb_composite_dev *cdev) } opts = container_of(fi_fastboot, struct f_fastboot_opts, func_inst); - opts->files = gadget_multi_opts->fastboot_opts.files; - opts->cmd_exec = gadget_multi_opts->fastboot_opts.cmd_exec; - opts->cmd_flash = gadget_multi_opts->fastboot_opts.cmd_flash; - opts->export_bbu = gadget_multi_opts->fastboot_opts.export_bbu; + opts->common = gadget_multi_opts->fastboot_opts; f_fastboot = usb_get_function(fi_fastboot); if (IS_ERR(f_fastboot)) { @@ -142,6 +137,37 @@ static int multi_bind_fastboot(struct usb_composite_dev *cdev) return usb_add_function(&config, f_fastboot); } +static bool fastboot_has_exports(struct f_multi_opts *opts) +{ + return !file_list_empty(opts->fastboot_opts.files) || + opts->fastboot_opts.export_bbu; +} + +static int multi_bind_ums(struct usb_composite_dev *cdev) +{ + int ret; + struct f_ums_opts *opts; + + fi_ums = usb_get_function_instance("ums"); + if (IS_ERR(fi_ums)) { + ret = PTR_ERR(fi_ums); + fi_ums = NULL; + return ret; + } + + opts = container_of(fi_ums, struct f_ums_opts, func_inst); + opts->files = gadget_multi_opts->ums_opts.files; + + f_ums = usb_get_function(fi_ums); + if (IS_ERR(f_ums)) { + ret = PTR_ERR(f_ums); + f_ums = NULL; + return ret; + } + + return usb_add_function(&config, f_ums); +} + static int multi_unbind(struct usb_composite_dev *cdev) { if (gadget_multi_opts->create_acm) { @@ -149,12 +175,17 @@ static int multi_unbind(struct usb_composite_dev *cdev) usb_put_function_instance(fi_acm); } + if (gadget_multi_opts->ums_opts.files) { + usb_put_function(f_ums); + usb_put_function_instance(fi_ums); + } + if (gadget_multi_opts->dfu_opts.files) { usb_put_function(f_dfu); usb_put_function_instance(fi_dfu); } - if (gadget_multi_opts->fastboot_opts.files) { + if (fastboot_has_exports(gadget_multi_opts)) { usb_put_function(f_fastboot); usb_put_function_instance(fi_fastboot); } @@ -182,8 +213,10 @@ static int multi_bind(struct usb_composite_dev *cdev) strings_dev[USB_GADGET_MANUFACTURER_IDX].s = gadget->manufacturer; strings_dev[USB_GADGET_PRODUCT_IDX].s = gadget->productname; + strings_dev[USB_GADGET_SERIAL_IDX].s = gadget->serialnumber; device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; + device_desc.iSerialNumber = strings_dev[USB_GADGET_SERIAL_IDX].id; config.label = strings_dev[STRING_DESCRIPTION_IDX].s; config.iConfiguration = strings_dev[STRING_DESCRIPTION_IDX].id; @@ -192,25 +225,32 @@ static int multi_bind(struct usb_composite_dev *cdev) if (ret) return ret; - if (gadget_multi_opts->fastboot_opts.files) { + if (fastboot_has_exports(gadget_multi_opts)) { printf("%s: creating Fastboot function\n", __func__); ret = multi_bind_fastboot(cdev); if (ret) - goto out; + return ret; } if (gadget_multi_opts->dfu_opts.files) { printf("%s: creating DFU function\n", __func__); ret = multi_bind_dfu(cdev); if (ret) - goto out; + goto unbind_fastboot; + } + + if (gadget_multi_opts->ums_opts.files) { + printf("%s: creating USB Mass Storage function\n", __func__); + ret = multi_bind_ums(cdev); + if (ret) + goto unbind_dfu; } if (gadget_multi_opts->create_acm) { printf("%s: creating ACM function\n", __func__); ret = multi_bind_acm(cdev); if (ret) - goto out; + goto unbind_ums; } usb_ep_autoconfig_reset(cdev->gadget); @@ -218,8 +258,15 @@ static int multi_bind(struct usb_composite_dev *cdev) dev_info(&gadget->dev, DRIVER_DESC "\n"); return 0; -out: - multi_unbind(cdev); +unbind_ums: + if (gadget_multi_opts->ums_opts.files) + usb_put_function_instance(fi_ums); +unbind_dfu: + if (gadget_multi_opts->dfu_opts.files) + usb_put_function_instance(fi_dfu); +unbind_fastboot: + if (fastboot_has_exports(gadget_multi_opts)) + usb_put_function_instance(fi_fastboot); return ret; } @@ -228,7 +275,7 @@ static struct usb_composite_driver multi_driver = { .name = "g_multi", .dev = &device_desc, .strings = dev_strings, - .max_speed = USB_SPEED_HIGH, + .max_speed = USB_SPEED_SUPER, .bind = multi_bind, .unbind = multi_unbind, .needs_serial = 1, @@ -247,10 +294,8 @@ int usb_multi_register(struct f_multi_opts *opts) gadget_multi_opts = opts; ret = usb_composite_probe(&multi_driver); - if (ret) { - usb_composite_unregister(&multi_driver); + if (ret) gadget_multi_opts = NULL; - } return ret; } @@ -267,12 +312,23 @@ void usb_multi_unregister(void) gadget_multi_opts = NULL; } +unsigned usb_multi_count_functions(struct f_multi_opts *opts) +{ + unsigned count = 0; + + count += fastboot_has_exports(opts); + count += !file_list_empty(opts->dfu_opts.files); + count += !file_list_empty(opts->ums_opts.files); + count += opts->create_acm; + + return count; +} + void usb_multi_opts_release(struct f_multi_opts *opts) { - if (opts->fastboot_opts.files) - file_list_free(opts->fastboot_opts.files); - if (opts->dfu_opts.files) - file_list_free(opts->dfu_opts.files); + file_list_free(opts->fastboot_opts.files); + file_list_free(opts->dfu_opts.files); + file_list_free(opts->ums_opts.files); free(opts); } diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/legacy/serial.c index f1d98b7a39..913d174a91 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/legacy/serial.c @@ -1,27 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * serial.c -- USB gadget serial driver * * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) * Copyright (C) 2008 by David Brownell * Copyright (C) 2008 by Nokia Corporation - * - * This software is distributed under the terms of the GNU General - * Public License ("GPL") as published by the Free Software Foundation, - * either version 2 of that License or (at your option) any later version. */ #include <common.h> #include <errno.h> #include <init.h> #include <linux/err.h> -#include <usb/ch9.h> -#include <usb/gadget.h> -#include <usb/composite.h> -#include <usb/usbserial.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/composite.h> +#include <linux/usb/usbserial.h> #include <asm/byteorder.h> #include "u_serial.h" -#include "gadget_chips.h" /* Defines */ @@ -251,8 +247,17 @@ static struct usb_composite_driver gserial_driver = { .unbind = gs_unbind, }; +static bool usb_serial_registered; + int usb_serial_register(struct usb_serial_pdata *pdata) { + int ret; + + if (usb_serial_registered) { + pr_err("USB serial gadget already registered\n"); + return -EBUSY; + } + /* We *could* export two configs; that'd be much cleaner... * but neither of these product IDs was defined that way. */ @@ -277,10 +282,18 @@ int usb_serial_register(struct usb_serial_pdata *pdata) device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC; } - return usb_composite_probe(&gserial_driver); + ret = usb_composite_probe(&gserial_driver); + if (!ret) + usb_serial_registered = true; + + return ret; } void usb_serial_unregister(void) { + if (!usb_serial_registered) + return; + usb_composite_unregister(&gserial_driver); + usb_serial_registered = false; } diff --git a/drivers/usb/gadget/u_os_desc.h b/drivers/usb/gadget/u_os_desc.h new file mode 100644 index 0000000000..5d7d35c8cc --- /dev/null +++ b/drivers/usb/gadget/u_os_desc.h @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * u_os_desc.h + * + * Utility definitions for "OS Descriptors" support + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com> + */ + +#ifndef __U_OS_DESC_H__ +#define __U_OS_DESC_H__ + +#include <asm/unaligned.h> +#include <linux/nls.h> + +#define USB_EXT_PROP_DW_SIZE 0 +#define USB_EXT_PROP_DW_PROPERTY_DATA_TYPE 4 +#define USB_EXT_PROP_W_PROPERTY_NAME_LENGTH 8 +#define USB_EXT_PROP_B_PROPERTY_NAME 10 +#define USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH 10 +#define USB_EXT_PROP_B_PROPERTY_DATA 14 + +#define USB_EXT_PROP_RESERVED 0 +#define USB_EXT_PROP_UNICODE 1 +#define USB_EXT_PROP_UNICODE_ENV 2 +#define USB_EXT_PROP_BINARY 3 +#define USB_EXT_PROP_LE32 4 +#define USB_EXT_PROP_BE32 5 +#define USB_EXT_PROP_UNICODE_LINK 6 +#define USB_EXT_PROP_UNICODE_MULTI 7 + +static inline u8 *__usb_ext_prop_ptr(u8 *buf, size_t offset) +{ + return buf + offset; +} + +static inline u8 *usb_ext_prop_size_ptr(u8 *buf) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_DW_SIZE); +} + +static inline u8 *usb_ext_prop_type_ptr(u8 *buf) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_DW_PROPERTY_DATA_TYPE); +} + +static inline u8 *usb_ext_prop_name_len_ptr(u8 *buf) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_W_PROPERTY_NAME_LENGTH); +} + +static inline u8 *usb_ext_prop_name_ptr(u8 *buf) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_B_PROPERTY_NAME); +} + +static inline u8 *usb_ext_prop_data_len_ptr(u8 *buf, size_t off) +{ + return __usb_ext_prop_ptr(buf, + USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + off); +} + +static inline u8 *usb_ext_prop_data_ptr(u8 *buf, size_t off) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_B_PROPERTY_DATA + off); +} + +static inline void usb_ext_prop_put_size(u8 *buf, int dw_size) +{ + put_unaligned_le32(dw_size, usb_ext_prop_size_ptr(buf)); +} + +static inline void usb_ext_prop_put_type(u8 *buf, int type) +{ + put_unaligned_le32(type, usb_ext_prop_type_ptr(buf)); +} + +static inline int usb_ext_prop_put_name(u8 *buf, const char *name, int pnl) +{ + int result; + + put_unaligned_le16(pnl, usb_ext_prop_name_len_ptr(buf)); + result = utf8s_to_utf16s(name, strlen(name), UTF16_LITTLE_ENDIAN, + (wchar_t *) usb_ext_prop_name_ptr(buf), pnl - 2); + if (result < 0) + return result; + + put_unaligned_le16(0, &buf[USB_EXT_PROP_B_PROPERTY_NAME + pnl - 2]); + + return pnl; +} + +static inline void usb_ext_prop_put_binary(u8 *buf, int pnl, const u8 *data, + int data_len) +{ + put_unaligned_le32(data_len, usb_ext_prop_data_len_ptr(buf, pnl)); + memcpy(usb_ext_prop_data_ptr(buf, pnl), data, data_len); +} + +static inline int usb_ext_prop_put_unicode(u8 *buf, int pnl, const char *string, + int data_len) +{ + int result; + put_unaligned_le32(data_len, usb_ext_prop_data_len_ptr(buf, pnl)); + result = utf8s_to_utf16s(string, data_len >> 1, UTF16_LITTLE_ENDIAN, + (wchar_t *) usb_ext_prop_data_ptr(buf, pnl), + data_len - 2); + if (result < 0) + return result; + + put_unaligned_le16(0, + &buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl + data_len - 2]); + + return data_len; +} + +#endif /* __U_OS_DESC_H__ */ diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c deleted file mode 100644 index 096f05ed48..0000000000 --- a/drivers/usb/gadget/udc-core.c +++ /dev/null @@ -1,417 +0,0 @@ -/** - * udc.c - Core UDC Framework - * - * Copyright (C) 2010 Texas Instruments - * Author: Felipe Balbi <balbi@ti.com> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 of - * the License as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -#define VERBOSE_DEBUG -#include <common.h> -#include <driver.h> -#include <init.h> -#include <poller.h> -#include <usb/ch9.h> -#include <usb/gadget.h> - -/** - * struct usb_udc - describes one usb device controller - * @driver - the gadget driver pointer. For use by the class code - * @dev - the child device to the actual controller - * @gadget - the gadget. For use by the class code - * @list - for use by the udc class driver - * - * This represents the internal data structure which is used by the UDC-class - * to hold information about udc driver and gadget together. - */ -struct usb_udc { - struct usb_gadget_driver *driver; - struct usb_gadget *gadget; - struct device_d dev; - struct list_head list; - struct poller_struct poller; -}; - -static LIST_HEAD(udc_list); - -/* ------------------------------------------------------------------------- */ - -#ifdef CONFIG_KERNEL_HAS_DMA - -int usb_gadget_map_request(struct usb_gadget *gadget, - struct usb_request *req, int is_in) -{ - if (req->length == 0) - return 0; - - if (req->num_sgs) { - int mapped; - - mapped = dma_map_sg(&gadget->dev, req->sg, req->num_sgs, - is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - if (mapped == 0) { - dev_err(&gadget->dev, "failed to map SGs\n"); - return -EFAULT; - } - - req->num_mapped_sgs = mapped; - } else { - req->dma = dma_map_single(&gadget->dev, req->buf, req->length, - is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - - if (dma_mapping_error(&gadget->dev, req->dma)) { - dev_err(&gadget->dev, "failed to map buffer\n"); - return -EFAULT; - } - } - - return 0; -} -EXPORT_SYMBOL_GPL(usb_gadget_map_request); - -void usb_gadget_unmap_request(struct usb_gadget *gadget, - struct usb_request *req, int is_in) -{ - if (req->length == 0) - return; - - if (req->num_mapped_sgs) { - dma_unmap_sg(&gadget->dev, req->sg, req->num_mapped_sgs, - is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - - req->num_mapped_sgs = 0; - } else { - dma_unmap_single(&gadget->dev, req->dma, req->length, - is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - } -} -EXPORT_SYMBOL_GPL(usb_gadget_unmap_request); - -#endif /* CONFIG_HAS_DMA */ - -/* ------------------------------------------------------------------------- */ - -void usb_gadget_set_state(struct usb_gadget *gadget, - enum usb_device_state state) -{ - gadget->state = state; -} -EXPORT_SYMBOL_GPL(usb_gadget_set_state); - -/** - * usb_gadget_udc_reset - notifies the udc core that bus reset occurs - * @gadget: The gadget which bus reset occurs - * @driver: The gadget driver we want to notify - * - * If the udc driver has bus reset handler, it needs to call this when the bus - * reset occurs, it notifies the gadget driver that the bus reset occurs as - * well as updates gadget state. - */ -void usb_gadget_udc_reset(struct usb_gadget *gadget, - struct usb_gadget_driver *driver) -{ - usb_gadget_set_state(gadget, USB_STATE_DEFAULT); -} -EXPORT_SYMBOL_GPL(usb_gadget_udc_reset); -/* ------------------------------------------------------------------------- */ - -/** - * usb_gadget_udc_start - tells usb device controller to start up - * @gadget: The gadget we want to get started - * @driver: The driver we want to bind to @gadget - * - * This call is issued by the UDC Class driver when it's about - * to register a gadget driver to the device controller, before - * calling gadget driver's bind() method. - * - * It allows the controller to be powered off until strictly - * necessary to have it powered on. - * - * Returns zero on success, else negative errno. - */ -static inline int usb_gadget_udc_start(struct usb_gadget *gadget, - struct usb_gadget_driver *driver) -{ - return gadget->ops->udc_start(gadget, driver); -} - -/** - * usb_gadget_udc_stop - tells usb device controller we don't need it anymore - * @gadget: The device we want to stop activity - * @driver: The driver to unbind from @gadget - * - * This call is issued by the UDC Class driver after calling - * gadget driver's unbind() method. - * - * The details are implementation specific, but it can go as - * far as powering off UDC completely and disable its data - * line pullups. - */ -static inline void usb_gadget_udc_stop(struct usb_gadget *gadget, - struct usb_gadget_driver *driver) -{ - gadget->ops->udc_stop(gadget, driver); -} - -int usb_gadget_poll(void) -{ - struct usb_udc *udc; - - list_for_each_entry(udc, &udc_list, list) { - if (udc->gadget->ops->udc_poll) - udc->gadget->ops->udc_poll(udc->gadget); - } - - return 0; -} - -/** - * usb_add_gadget_udc_release - adds a new gadget to the udc class driver list - * @parent: the parent device to this udc. Usually the controller driver's - * device. - * @gadget: the gadget to be added to the list. - * @release: a gadget release function. - * - * Returns zero on success, negative errno otherwise. - */ -int usb_add_gadget_udc_release(struct device_d *parent, struct usb_gadget *gadget, - void (*release)(struct device_d *dev)) -{ - struct usb_udc *udc; - int ret = -ENOMEM; - - udc = kzalloc(sizeof(*udc), GFP_KERNEL); - if (!udc) - goto err1; - - dev_set_name(&gadget->dev, "usbgadget"); - gadget->dev.id = DEVICE_ID_SINGLE; - gadget->dev.parent = parent; - - ret = register_device(&gadget->dev); - if (ret) - goto err2; - - dev_add_param_uint32(&gadget->dev, "product", NULL, NULL, - &gadget->product_id, "0x%04x", NULL); - dev_add_param_uint32(&gadget->dev, "vendor", NULL, NULL, - &gadget->vendor_id, "0x%04x", NULL); - gadget->manufacturer = xstrdup("barebox"); - dev_add_param_string(&gadget->dev, "manufacturer", NULL, NULL, - &gadget->manufacturer, NULL); - gadget->productname = xstrdup(barebox_get_model()); - dev_add_param_string(&gadget->dev, "productname", NULL, NULL, - &gadget->productname, NULL); - - dev_set_name(&udc->dev, "udc"); - udc->dev.id = DEVICE_ID_DYNAMIC; - - udc->gadget = gadget; - - list_add_tail(&udc->list, &udc_list); - - register_device(&udc->dev); - - usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED); - - return 0; -err2: - kfree(udc); - -err1: - return ret; -} -EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release); - -/** - * usb_add_gadget_udc - adds a new gadget to the udc class driver list - * @parent: the parent device to this udc. Usually the controller - * driver's device. - * @gadget: the gadget to be added to the list - * - * Returns zero on success, negative errno otherwise. - */ -int usb_add_gadget_udc(struct device_d *parent, struct usb_gadget *gadget) -{ - return usb_add_gadget_udc_release(parent, gadget, NULL); -} -EXPORT_SYMBOL_GPL(usb_add_gadget_udc); - -static void usb_gadget_remove_driver(struct usb_udc *udc) -{ - dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n", - udc->gadget->name); - - if (udc->gadget->ops->udc_poll) - poller_unregister(&udc->poller); - - usb_gadget_disconnect(udc->gadget); - udc->driver->disconnect(udc->gadget); - udc->driver->unbind(udc->gadget); - usb_gadget_udc_stop(udc->gadget, NULL); - - udc->driver = NULL; - udc->dev.driver = NULL; - udc->gadget->dev.driver = NULL; -} - -/** - * usb_del_gadget_udc - deletes @udc from udc_list - * @gadget: the gadget to be removed. - * - * This, will call usb_gadget_unregister_driver() if - * the @udc is still busy. - */ -void usb_del_gadget_udc(struct usb_gadget *gadget) -{ - struct usb_udc *udc = NULL; - - list_for_each_entry(udc, &udc_list, list) - if (udc->gadget == gadget) - goto found; - - dev_err(gadget->dev.parent, "gadget not registered.\n"); - - return; - -found: - dev_vdbg(gadget->dev.parent, "unregistering gadget\n"); - - list_del(&udc->list); - - if (udc->driver) - usb_gadget_remove_driver(udc); - - unregister_device(&udc->dev); - unregister_device(&gadget->dev); -} -EXPORT_SYMBOL_GPL(usb_del_gadget_udc); - -/* ------------------------------------------------------------------------- */ - -static void udc_poll_driver(struct poller_struct *poller) -{ - struct usb_udc *udc = container_of(poller, struct usb_udc, poller); - - udc->gadget->ops->udc_poll(udc->gadget); -} - -static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver) -{ - int ret; - - dev_dbg(&udc->dev, "registering UDC driver [%s]\n", - driver->function); - - udc->driver = driver; - udc->dev.driver = &driver->driver; - udc->gadget->dev.driver = &driver->driver; - - if (udc->gadget->ops->udc_poll) { - udc->poller.func = udc_poll_driver; - ret = poller_register(&udc->poller); - if (ret) - return ret; - } - - ret = driver->bind(udc->gadget, driver); - if (ret) - goto err1; - - ret = usb_gadget_udc_start(udc->gadget, driver); - if (ret) { - driver->unbind(udc->gadget); - goto err1; - } - usb_gadget_connect(udc->gadget); - - return 0; -err1: - if (udc->gadget->ops->udc_poll) - poller_unregister(&udc->poller); - - if (ret != -EISNAM) - dev_err(&udc->dev, "failed to start %s: %d\n", - udc->driver->function, ret); - udc->driver = NULL; - udc->dev.driver = NULL; - udc->gadget->dev.driver = NULL; - return ret; -} - -int udc_attach_driver(const char *name, struct usb_gadget_driver *driver) -{ - struct usb_udc *udc = NULL; - int ret = -ENODEV; - - list_for_each_entry(udc, &udc_list, list) { - ret = strcmp(name, dev_name(&udc->dev)); - if (!ret) - break; - } - if (ret) { - ret = -ENODEV; - goto out; - } - if (udc->driver) { - ret = -EBUSY; - goto out; - } - ret = udc_bind_to_driver(udc, driver); -out: - return ret; -} -EXPORT_SYMBOL_GPL(udc_attach_driver); - -int usb_gadget_probe_driver(struct usb_gadget_driver *driver) -{ - struct usb_udc *udc = NULL; - int ret; - - if (!driver || !driver->bind || !driver->setup) - return -EINVAL; - - list_for_each_entry(udc, &udc_list, list) { - /* For now we take the first one */ - if (!udc->driver) - goto found; - } - - pr_debug("couldn't find an available UDC\n"); - - return -ENODEV; -found: - ret = udc_bind_to_driver(udc, driver); - - return ret; -} -EXPORT_SYMBOL_GPL(usb_gadget_probe_driver); - -int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) -{ - struct usb_udc *udc = NULL; - int ret = -ENODEV; - - if (!driver || !driver->unbind) - return -EINVAL; - - list_for_each_entry(udc, &udc_list, list) - if (udc->driver == driver) { - usb_gadget_remove_driver(udc); - ret = 0; - break; - } - - return ret; -} -EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver); diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile new file mode 100644 index 0000000000..6e79e80cfa --- /dev/null +++ b/drivers/usb/gadget/udc/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_USB_GADGET) += core.o + +obj-$(CONFIG_USB_GADGET_DRIVER_ARC) += fsl_udc.o +obj-$(CONFIG_USB_GADGET_DRIVER_AT91) += at91_udc.o +obj-$(CONFIG_USB_GADGET_DRIVER_PXA27X) += pxa27x_udc.o diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c index 411464690d..ffd39f489f 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/udc/at91_udc.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * at91_udc -- driver for at91-series USB peripheral controller * * Copyright (C) 2004 by Thomas Rathbone * Copyright (C) 2005 by HP Labs * Copyright (C) 2005 by David Brownell - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #undef VERBOSE_DEBUG @@ -20,8 +16,8 @@ #include <gpio.h> #include <io.h> #include <clock.h> -#include <usb/ch9.h> -#include <usb/gadget.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> #include <of_gpio.h> #include <linux/list.h> @@ -30,11 +26,11 @@ #include <asm/byteorder.h> -#include <mach/hardware.h> -#include <mach/at91sam9261.h> -#include <mach/board.h> -#include <mach/cpu.h> -#include <mach/at91sam9261_matrix.h> +#include <mach/at91/hardware.h> +#include <mach/at91/at91sam9261.h> +#include <mach/at91/board.h> +#include <mach/at91/cpu.h> +#include <mach/at91/at91sam9261_matrix.h> #include "at91_udc.h" @@ -1244,7 +1240,7 @@ static int at91_udc_start(struct usb_gadget *gadget, struct usb_gadget_driver *d return 0; } -static int at91_udc_stop(struct usb_gadget *gadget, struct usb_gadget_driver *driver) +static int at91_udc_stop(struct usb_gadget *gadget) { struct at91_udc *udc = container_of(gadget, struct at91_udc, gadget); @@ -1252,7 +1248,8 @@ static int at91_udc_stop(struct usb_gadget *gadget, struct usb_gadget_driver *dr at91_udp_write(udc, AT91_UDP_IDR, ~0); udc->driver = NULL; - DBG(udc, "unbound from %s\n", driver->function); + DBG(udc, "unbound\n"); + return 0; } @@ -1393,7 +1390,7 @@ static void __init at91udc_of_init(struct at91_udc *udc, struct device_node *np) /*-------------------------------------------------------------------------*/ -static int __init at91udc_probe(struct device_d *dev) +static int __init at91udc_probe(struct device *dev) { struct resource *iores; struct at91_udc *udc = &controller; @@ -1411,12 +1408,12 @@ static int __init at91udc_probe(struct device_d *dev) iclk_name = "udc_clk"; fclk_name = "udpck"; } else { - if (!IS_ENABLED(CONFIG_OFDEVICE) || !dev->device_node) { + if (!IS_ENABLED(CONFIG_OFDEVICE) || !dev->of_node) { dev_err(dev, "no DT and no platform_data\n"); return -ENODEV; } - at91udc_of_init(udc, dev->device_node); + at91udc_of_init(udc, dev->of_node); iclk_name = "pclk"; fclk_name = "hclk"; } @@ -1525,8 +1522,9 @@ static const struct of_device_id at91_udc_dt_ids[] = { { .compatible = "atmel,at91sam9263-udc" }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, at91_udc_dt_ids); -static struct driver_d at91_udc_driver = { +static struct driver at91_udc_driver = { .name = driver_name, .probe = at91udc_probe, .of_compatible = DRV_OF_COMPAT(at91_udc_dt_ids), diff --git a/drivers/usb/gadget/at91_udc.h b/drivers/usb/gadget/udc/at91_udc.h index e592cc54ff..cecaa5b52e 100644 --- a/drivers/usb/gadget/at91_udc.h +++ b/drivers/usb/gadget/udc/at91_udc.h @@ -1,12 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2004 by Thomas Rathbone, HP Labs * Copyright (C) 2005 by Ivan Kokshaysky * Copyright (C) 2006 by SAN People - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef AT91_UDC_H @@ -131,7 +127,7 @@ struct at91_udc { u32 gpio_vbus_val; struct at91_udc_data board; struct clk *iclk, *fclk; - struct device_d *dev; + struct device *dev; void __iomem *udp_baseaddr; int udp_irq; }; diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c new file mode 100644 index 0000000000..e7cfa0d5d8 --- /dev/null +++ b/drivers/usb/gadget/udc/core.c @@ -0,0 +1,1517 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * udc.c - Core UDC Framework + * + * Copyright (C) 2010 Texas Instruments + * Author: Felipe Balbi <balbi@ti.com> + */ + +#define pr_fmt(fmt) "UDC core: " fmt + +#include <common.h> +#include <dma.h> +#include <linux/usb/gadget.h> +#include <linux/usb/ch9.h> + +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/idr.h> +#include <linux/err.h> + +static int gadget_id_numbers; + +static struct bus_type gadget_bus_type; + +/** + * struct usb_udc - describes one usb device controller + * @driver: the gadget driver pointer. For use by the class code + * @dev: the child device to the actual controller + * @gadget: the gadget. For use by the class code + * @list: for use by the udc class driver + * @vbus: for udcs who care about vbus status, this value is real vbus status; + * for udcs who do not care about vbus status, this value is always true + * @started: the UDC's started state. True if the UDC had started. + * + * This represents the internal data structure which is used by the UDC-class + * to hold information about udc driver and gadget together. + */ +struct usb_udc { + struct usb_gadget_driver *driver; + struct usb_gadget *gadget; + struct device dev; + struct list_head list; + bool vbus; + bool started; + struct poller_struct poller; +}; + +static LIST_HEAD(udc_list); + +/* Protects udc_list, udc->driver, driver->is_bound, and related calls */ +static DEFINE_MUTEX(udc_lock); + +/* ------------------------------------------------------------------------- */ + +/** + * usb_ep_set_maxpacket_limit - set maximum packet size limit for endpoint + * @ep:the endpoint being configured + * @maxpacket_limit:value of maximum packet size limit + * + * This function should be used only in UDC drivers to initialize endpoint + * (usually in probe function). + */ +void usb_ep_set_maxpacket_limit(struct usb_ep *ep, + unsigned maxpacket_limit) +{ + ep->maxpacket_limit = maxpacket_limit; + ep->maxpacket = maxpacket_limit; +} +EXPORT_SYMBOL_GPL(usb_ep_set_maxpacket_limit); + +/** + * usb_ep_enable - configure endpoint, making it usable + * @ep:the endpoint being configured. may not be the endpoint named "ep0". + * drivers discover endpoints through the ep_list of a usb_gadget. + * + * When configurations are set, or when interface settings change, the driver + * will enable or disable the relevant endpoints. while it is enabled, an + * endpoint may be used for i/o until the driver receives a disconnect() from + * the host or until the endpoint is disabled. + * + * the ep0 implementation (which calls this routine) must ensure that the + * hardware capabilities of each endpoint match the descriptor provided + * for it. for example, an endpoint named "ep2in-bulk" would be usable + * for interrupt transfers as well as bulk, but it likely couldn't be used + * for iso transfers or for endpoint 14. some endpoints are fully + * configurable, with more generic names like "ep-a". (remember that for + * USB, "in" means "towards the USB host".) + * + * This routine may be called in an atomic (interrupt) context. + * + * returns zero, or a negative error code. + */ +int usb_ep_enable(struct usb_ep *ep) +{ + int ret = 0; + + if (ep->enabled) + goto out; + + /* UDC drivers can't handle endpoints with maxpacket size 0 */ + if (usb_endpoint_maxp(ep->desc) == 0) { + /* + * We should log an error message here, but we can't call + * dev_err() because there's no way to find the gadget + * given only ep. + */ + ret = -EINVAL; + goto out; + } + + ret = ep->ops->enable(ep, ep->desc); + if (ret) + goto out; + + ep->enabled = true; + +out: + return ret; +} +EXPORT_SYMBOL_GPL(usb_ep_enable); + +/** + * usb_ep_disable - endpoint is no longer usable + * @ep:the endpoint being unconfigured. may not be the endpoint named "ep0". + * + * no other task may be using this endpoint when this is called. + * any pending and uncompleted requests will complete with status + * indicating disconnect (-ESHUTDOWN) before this call returns. + * gadget drivers must call usb_ep_enable() again before queueing + * requests to the endpoint. + * + * This routine may be called in an atomic (interrupt) context. + * + * returns zero, or a negative error code. + */ +int usb_ep_disable(struct usb_ep *ep) +{ + int ret = 0; + + if (!ep->enabled) + goto out; + + ret = ep->ops->disable(ep); + if (ret) + goto out; + + ep->enabled = false; + +out: + return ret; +} +EXPORT_SYMBOL_GPL(usb_ep_disable); + +/** + * usb_ep_alloc_request - allocate a request object to use with this endpoint + * @ep:the endpoint to be used with with the request + * @gfp_flags:GFP_* flags to use + * + * Request objects must be allocated with this call, since they normally + * need controller-specific setup and may even need endpoint-specific + * resources such as allocation of DMA descriptors. + * Requests may be submitted with usb_ep_queue(), and receive a single + * completion callback. Free requests with usb_ep_free_request(), when + * they are no longer needed. + * + * Returns the request, or null if one could not be allocated. + */ +struct usb_request *usb_ep_alloc_request(struct usb_ep *ep) +{ + struct usb_request *req = NULL; + + req = ep->ops->alloc_request(ep); + + return req; +} +EXPORT_SYMBOL_GPL(usb_ep_alloc_request); + +/** + * usb_ep_free_request - frees a request object + * @ep:the endpoint associated with the request + * @req:the request being freed + * + * Reverses the effect of usb_ep_alloc_request(). + * Caller guarantees the request is not queued, and that it will + * no longer be requeued (or otherwise used). + */ +void usb_ep_free_request(struct usb_ep *ep, + struct usb_request *req) +{ + ep->ops->free_request(ep, req); +} +EXPORT_SYMBOL_GPL(usb_ep_free_request); + +/** + * usb_ep_queue - queues (submits) an I/O request to an endpoint. + * @ep:the endpoint associated with the request + * @req:the request being submitted + * @gfp_flags: GFP_* flags to use in case the lower level driver couldn't + * pre-allocate all necessary memory with the request. + * + * This tells the device controller to perform the specified request through + * that endpoint (reading or writing a buffer). When the request completes, + * including being canceled by usb_ep_dequeue(), the request's completion + * routine is called to return the request to the driver. Any endpoint + * (except control endpoints like ep0) may have more than one transfer + * request queued; they complete in FIFO order. Once a gadget driver + * submits a request, that request may not be examined or modified until it + * is given back to that driver through the completion callback. + * + * Each request is turned into one or more packets. The controller driver + * never merges adjacent requests into the same packet. OUT transfers + * will sometimes use data that's already buffered in the hardware. + * Drivers can rely on the fact that the first byte of the request's buffer + * always corresponds to the first byte of some USB packet, for both + * IN and OUT transfers. + * + * Bulk endpoints can queue any amount of data; the transfer is packetized + * automatically. The last packet will be short if the request doesn't fill it + * out completely. Zero length packets (ZLPs) should be avoided in portable + * protocols since not all usb hardware can successfully handle zero length + * packets. (ZLPs may be explicitly written, and may be implicitly written if + * the request 'zero' flag is set.) Bulk endpoints may also be used + * for interrupt transfers; but the reverse is not true, and some endpoints + * won't support every interrupt transfer. (Such as 768 byte packets.) + * + * Interrupt-only endpoints are less functional than bulk endpoints, for + * example by not supporting queueing or not handling buffers that are + * larger than the endpoint's maxpacket size. They may also treat data + * toggle differently. + * + * Control endpoints ... after getting a setup() callback, the driver queues + * one response (even if it would be zero length). That enables the + * status ack, after transferring data as specified in the response. Setup + * functions may return negative error codes to generate protocol stalls. + * (Note that some USB device controllers disallow protocol stall responses + * in some cases.) When control responses are deferred (the response is + * written after the setup callback returns), then usb_ep_set_halt() may be + * used on ep0 to trigger protocol stalls. Depending on the controller, + * it may not be possible to trigger a status-stage protocol stall when the + * data stage is over, that is, from within the response's completion + * routine. + * + * For periodic endpoints, like interrupt or isochronous ones, the usb host + * arranges to poll once per interval, and the gadget driver usually will + * have queued some data to transfer at that time. + * + * Note that @req's ->complete() callback must never be called from + * within usb_ep_queue() as that can create deadlock situations. + * + * This routine may be called in interrupt context. + * + * Returns zero, or a negative error code. Endpoints that are not enabled + * report errors; errors will also be + * reported when the usb peripheral is disconnected. + * + * If and only if @req is successfully queued (the return value is zero), + * @req->complete() will be called exactly once, when the Gadget core and + * UDC are finished with the request. When the completion function is called, + * control of the request is returned to the device driver which submitted it. + * The completion handler may then immediately free or reuse @req. + */ +int usb_ep_queue(struct usb_ep *ep, + struct usb_request *req) +{ + int ret = 0; + + if (WARN_ON_ONCE(!ep->enabled && ep->address)) { + ret = -ESHUTDOWN; + goto out; + } + + ret = ep->ops->queue(ep, req); + +out: + return ret; +} +EXPORT_SYMBOL_GPL(usb_ep_queue); + +/** + * usb_ep_dequeue - dequeues (cancels, unlinks) an I/O request from an endpoint + * @ep:the endpoint associated with the request + * @req:the request being canceled + * + * If the request is still active on the endpoint, it is dequeued and + * eventually its completion routine is called (with status -ECONNRESET); + * else a negative error code is returned. This routine is asynchronous, + * that is, it may return before the completion routine runs. + * + * Note that some hardware can't clear out write fifos (to unlink the request + * at the head of the queue) except as part of disconnecting from usb. Such + * restrictions prevent drivers from supporting configuration changes, + * even to configuration zero (a "chapter 9" requirement). + * + * This routine may be called in interrupt context. + */ +int usb_ep_dequeue(struct usb_ep *ep, struct usb_request *req) +{ + int ret; + + ret = ep->ops->dequeue(ep, req); + + return ret; +} +EXPORT_SYMBOL_GPL(usb_ep_dequeue); + +/** + * usb_ep_set_halt - sets the endpoint halt feature. + * @ep: the non-isochronous endpoint being stalled + * + * Use this to stall an endpoint, perhaps as an error report. + * Except for control endpoints, + * the endpoint stays halted (will not stream any data) until the host + * clears this feature; drivers may need to empty the endpoint's request + * queue first, to make sure no inappropriate transfers happen. + * + * Note that while an endpoint CLEAR_FEATURE will be invisible to the + * gadget driver, a SET_INTERFACE will not be. To reset endpoints for the + * current altsetting, see usb_ep_clear_halt(). When switching altsettings, + * it's simplest to use usb_ep_enable() or usb_ep_disable() for the endpoints. + * + * This routine may be called in interrupt context. + * + * Returns zero, or a negative error code. On success, this call sets + * underlying hardware state that blocks data transfers. + * Attempts to halt IN endpoints will fail (returning -EAGAIN) if any + * transfer requests are still queued, or if the controller hardware + * (usually a FIFO) still holds bytes that the host hasn't collected. + */ +int usb_ep_set_halt(struct usb_ep *ep) +{ + int ret; + + ret = ep->ops->set_halt(ep, 1); + + return ret; +} +EXPORT_SYMBOL_GPL(usb_ep_set_halt); + +/** + * usb_ep_clear_halt - clears endpoint halt, and resets toggle + * @ep:the bulk or interrupt endpoint being reset + * + * Use this when responding to the standard usb "set interface" request, + * for endpoints that aren't reconfigured, after clearing any other state + * in the endpoint's i/o queue. + * + * This routine may be called in interrupt context. + * + * Returns zero, or a negative error code. On success, this call clears + * the underlying hardware state reflecting endpoint halt and data toggle. + * Note that some hardware can't support this request (like pxa2xx_udc), + * and accordingly can't correctly implement interface altsettings. + */ +int usb_ep_clear_halt(struct usb_ep *ep) +{ + int ret; + + ret = ep->ops->set_halt(ep, 0); + + return ret; +} +EXPORT_SYMBOL_GPL(usb_ep_clear_halt); + +/** + * usb_ep_set_wedge - sets the halt feature and ignores clear requests + * @ep: the endpoint being wedged + * + * Use this to stall an endpoint and ignore CLEAR_FEATURE(HALT_ENDPOINT) + * requests. If the gadget driver clears the halt status, it will + * automatically unwedge the endpoint. + * + * This routine may be called in interrupt context. + * + * Returns zero on success, else negative errno. + */ +int usb_ep_set_wedge(struct usb_ep *ep) +{ + int ret; + + if (ep->ops->set_wedge) + ret = ep->ops->set_wedge(ep); + else + ret = ep->ops->set_halt(ep, 1); + + return ret; +} +EXPORT_SYMBOL_GPL(usb_ep_set_wedge); + +/** + * usb_ep_fifo_status - returns number of bytes in fifo, or error + * @ep: the endpoint whose fifo status is being checked. + * + * FIFO endpoints may have "unclaimed data" in them in certain cases, + * such as after aborted transfers. Hosts may not have collected all + * the IN data written by the gadget driver (and reported by a request + * completion). The gadget driver may not have collected all the data + * written OUT to it by the host. Drivers that need precise handling for + * fault reporting or recovery may need to use this call. + * + * This routine may be called in interrupt context. + * + * This returns the number of such bytes in the fifo, or a negative + * errno if the endpoint doesn't use a FIFO or doesn't support such + * precise handling. + */ +int usb_ep_fifo_status(struct usb_ep *ep) +{ + int ret; + + if (ep->ops->fifo_status) + ret = ep->ops->fifo_status(ep); + else + ret = -EOPNOTSUPP; + + return ret; +} +EXPORT_SYMBOL_GPL(usb_ep_fifo_status); + +/** + * usb_ep_fifo_flush - flushes contents of a fifo + * @ep: the endpoint whose fifo is being flushed. + * + * This call may be used to flush the "unclaimed data" that may exist in + * an endpoint fifo after abnormal transaction terminations. The call + * must never be used except when endpoint is not being used for any + * protocol translation. + * + * This routine may be called in interrupt context. + */ +void usb_ep_fifo_flush(struct usb_ep *ep) +{ + if (ep->ops->fifo_flush) + ep->ops->fifo_flush(ep); +} +EXPORT_SYMBOL_GPL(usb_ep_fifo_flush); + +/* ------------------------------------------------------------------------- */ + +/** + * usb_gadget_frame_number - returns the current frame number + * @gadget: controller that reports the frame number + * + * Returns the usb frame number, normally eleven bits from a SOF packet, + * or negative errno if this device doesn't support this capability. + */ +int usb_gadget_frame_number(struct usb_gadget *gadget) +{ + int ret; + + ret = gadget->ops->get_frame(gadget); + + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_frame_number); + +/** + * usb_gadget_wakeup - tries to wake up the host connected to this gadget + * @gadget: controller used to wake up the host + * + * Returns zero on success, else negative error code if the hardware + * doesn't support such attempts, or its support has not been enabled + * by the usb host. Drivers must return device descriptors that report + * their ability to support this, or hosts won't enable it. + * + * This may also try to use SRP to wake the host and start enumeration, + * even if OTG isn't otherwise in use. OTG devices may also start + * remote wakeup even when hosts don't explicitly enable it. + */ +int usb_gadget_wakeup(struct usb_gadget *gadget) +{ + int ret = 0; + + if (!gadget->ops->wakeup) { + ret = -EOPNOTSUPP; + goto out; + } + + ret = gadget->ops->wakeup(gadget); + +out: + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_wakeup); + +/** + * usb_gadget_set_selfpowered - sets the device selfpowered feature. + * @gadget:the device being declared as self-powered + * + * this affects the device status reported by the hardware driver + * to reflect that it now has a local power supply. + * + * returns zero on success, else negative errno. + */ +int usb_gadget_set_selfpowered(struct usb_gadget *gadget) +{ + int ret = 0; + + if (!gadget->ops->set_selfpowered) { + ret = -EOPNOTSUPP; + goto out; + } + + ret = gadget->ops->set_selfpowered(gadget, 1); + +out: + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_set_selfpowered); + +/** + * usb_gadget_clear_selfpowered - clear the device selfpowered feature. + * @gadget:the device being declared as bus-powered + * + * this affects the device status reported by the hardware driver. + * some hardware may not support bus-powered operation, in which + * case this feature's value can never change. + * + * returns zero on success, else negative errno. + */ +int usb_gadget_clear_selfpowered(struct usb_gadget *gadget) +{ + int ret = 0; + + if (!gadget->ops->set_selfpowered) { + ret = -EOPNOTSUPP; + goto out; + } + + ret = gadget->ops->set_selfpowered(gadget, 0); + +out: + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_clear_selfpowered); + +/** + * usb_gadget_vbus_connect - Notify controller that VBUS is powered + * @gadget:The device which now has VBUS power. + * Context: can sleep + * + * This call is used by a driver for an external transceiver (or GPIO) + * that detects a VBUS power session starting. Common responses include + * resuming the controller, activating the D+ (or D-) pullup to let the + * host detect that a USB device is attached, and starting to draw power + * (8mA or possibly more, especially after SET_CONFIGURATION). + * + * Returns zero on success, else negative errno. + */ +int usb_gadget_vbus_connect(struct usb_gadget *gadget) +{ + int ret = 0; + + if (!gadget->ops->vbus_session) { + ret = -EOPNOTSUPP; + goto out; + } + + ret = gadget->ops->vbus_session(gadget, 1); + +out: + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_vbus_connect); + +/** + * usb_gadget_vbus_draw - constrain controller's VBUS power usage + * @gadget:The device whose VBUS usage is being described + * @mA:How much current to draw, in milliAmperes. This should be twice + * the value listed in the configuration descriptor bMaxPower field. + * + * This call is used by gadget drivers during SET_CONFIGURATION calls, + * reporting how much power the device may consume. For example, this + * could affect how quickly batteries are recharged. + * + * Returns zero on success, else negative errno. + */ +int usb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA) +{ + int ret = 0; + + if (!gadget->ops->vbus_draw) { + ret = -EOPNOTSUPP; + goto out; + } + + ret = gadget->ops->vbus_draw(gadget, mA); + if (!ret) + gadget->mA = mA; + +out: + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_vbus_draw); + +/** + * usb_gadget_vbus_disconnect - notify controller about VBUS session end + * @gadget:the device whose VBUS supply is being described + * Context: can sleep + * + * This call is used by a driver for an external transceiver (or GPIO) + * that detects a VBUS power session ending. Common responses include + * reversing everything done in usb_gadget_vbus_connect(). + * + * Returns zero on success, else negative errno. + */ +int usb_gadget_vbus_disconnect(struct usb_gadget *gadget) +{ + int ret = 0; + + if (!gadget->ops->vbus_session) { + ret = -EOPNOTSUPP; + goto out; + } + + ret = gadget->ops->vbus_session(gadget, 0); + +out: + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_vbus_disconnect); + +/** + * usb_gadget_connect - software-controlled connect to USB host + * @gadget:the peripheral being connected + * + * Enables the D+ (or potentially D-) pullup. The host will start + * enumerating this gadget when the pullup is active and a VBUS session + * is active (the link is powered). + * + * Returns zero on success, else negative errno. + */ +int usb_gadget_connect(struct usb_gadget *gadget) +{ + int ret = 0; + + if (!gadget->ops->pullup) { + ret = -EOPNOTSUPP; + goto out; + } + + if (gadget->deactivated) { + /* + * If gadget is deactivated we only save new state. + * Gadget will be connected automatically after activation. + */ + gadget->connected = true; + goto out; + } + + ret = gadget->ops->pullup(gadget, 1); + if (!ret) + gadget->connected = 1; + +out: + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_connect); + +/** + * usb_gadget_disconnect - software-controlled disconnect from USB host + * @gadget:the peripheral being disconnected + * + * Disables the D+ (or potentially D-) pullup, which the host may see + * as a disconnect (when a VBUS session is active). Not all systems + * support software pullup controls. + * + * Following a successful disconnect, invoke the ->disconnect() callback + * for the current gadget driver so that UDC drivers don't need to. + * + * Returns zero on success, else negative errno. + */ +int usb_gadget_disconnect(struct usb_gadget *gadget) +{ + int ret = 0; + + if (!gadget->ops->pullup) { + ret = -EOPNOTSUPP; + goto out; + } + + if (!gadget->connected) + goto out; + + if (gadget->deactivated) { + /* + * If gadget is deactivated we only save new state. + * Gadget will stay disconnected after activation. + */ + gadget->connected = false; + goto out; + } + + ret = gadget->ops->pullup(gadget, 0); + if (!ret) + gadget->connected = 0; + + mutex_lock(&udc_lock); + if (gadget->udc->driver) + gadget->udc->driver->disconnect(gadget); + mutex_unlock(&udc_lock); + +out: + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_disconnect); + +/** + * usb_gadget_deactivate - deactivate function which is not ready to work + * @gadget: the peripheral being deactivated + * + * This routine may be used during the gadget driver bind() call to prevent + * the peripheral from ever being visible to the USB host, unless later + * usb_gadget_activate() is called. For example, user mode components may + * need to be activated before the system can talk to hosts. + * + * Returns zero on success, else negative errno. + */ +int usb_gadget_deactivate(struct usb_gadget *gadget) +{ + int ret = 0; + + if (gadget->deactivated) + goto out; + + if (gadget->connected) { + ret = usb_gadget_disconnect(gadget); + if (ret) + goto out; + + /* + * If gadget was being connected before deactivation, we want + * to reconnect it in usb_gadget_activate(). + */ + gadget->connected = true; + } + gadget->deactivated = true; + +out: + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_deactivate); + +/** + * usb_gadget_activate - activate function which is not ready to work + * @gadget: the peripheral being activated + * + * This routine activates gadget which was previously deactivated with + * usb_gadget_deactivate() call. It calls usb_gadget_connect() if needed. + * + * Returns zero on success, else negative errno. + */ +int usb_gadget_activate(struct usb_gadget *gadget) +{ + int ret = 0; + + if (!gadget->deactivated) + goto out; + + gadget->deactivated = false; + + /* + * If gadget has been connected before deactivation, or became connected + * while it was being deactivated, we call usb_gadget_connect(). + */ + if (gadget->connected) + ret = usb_gadget_connect(gadget); + +out: + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_activate); + +/* ------------------------------------------------------------------------- */ + +#ifdef CONFIG_HAS_DMA + +int usb_gadget_map_request_by_dev(struct device *dev, + struct usb_request *req, int is_in) +{ + if (req->length == 0) + return 0; + + req->dma = dma_map_single(dev, req->buf, req->length, + is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + if (dma_mapping_error(dev, req->dma)) { + dev_err(dev, "failed to map buffer\n"); + return -EFAULT; + } + + req->dma_mapped = 1; + + return 0; +} +EXPORT_SYMBOL_GPL(usb_gadget_map_request_by_dev); + +int usb_gadget_map_request(struct usb_gadget *gadget, + struct usb_request *req, int is_in) +{ + return usb_gadget_map_request_by_dev(gadget->dev.parent, req, is_in); +} +EXPORT_SYMBOL_GPL(usb_gadget_map_request); + +void usb_gadget_unmap_request_by_dev(struct device *dev, + struct usb_request *req, int is_in) +{ + if (req->length == 0) + return; + + if (req->dma_mapped) { + dma_unmap_single(dev, req->dma, req->length, + is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + req->dma_mapped = 0; + } +} +EXPORT_SYMBOL_GPL(usb_gadget_unmap_request_by_dev); + +void usb_gadget_unmap_request(struct usb_gadget *gadget, + struct usb_request *req, int is_in) +{ + usb_gadget_unmap_request_by_dev(gadget->dev.parent, req, is_in); +} +EXPORT_SYMBOL_GPL(usb_gadget_unmap_request); + +#endif /* CONFIG_HAS_DMA */ + +/* ------------------------------------------------------------------------- */ + +/** + * usb_gadget_giveback_request - give the request back to the gadget layer + * @ep: the endpoint to be used with with the request + * @req: the request being given back + * + * This is called by device controller drivers in order to return the + * completed request back to the gadget layer. + */ +void usb_gadget_giveback_request(struct usb_ep *ep, + struct usb_request *req) +{ + req->complete(ep, req); +} +EXPORT_SYMBOL_GPL(usb_gadget_giveback_request); + +/* ------------------------------------------------------------------------- */ + +/** + * gadget_find_ep_by_name - returns ep whose name is the same as sting passed + * in second parameter or NULL if searched endpoint not found + * @g: controller to check for quirk + * @name: name of searched endpoint + */ +struct usb_ep *gadget_find_ep_by_name(struct usb_gadget *g, const char *name) +{ + struct usb_ep *ep; + + gadget_for_each_ep(ep, g) { + if (!strcmp(ep->name, name)) + return ep; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(gadget_find_ep_by_name); + +/* ------------------------------------------------------------------------- */ + +int usb_gadget_ep_match_desc(struct usb_gadget *gadget, + struct usb_ep *ep, struct usb_endpoint_descriptor *desc, + struct usb_ss_ep_comp_descriptor *ep_comp) +{ + u8 type; + u16 max; + int num_req_streams = 0; + + /* endpoint already claimed? */ + if (ep->claimed) + return 0; + + type = usb_endpoint_type(desc); + max = usb_endpoint_maxp(desc); + + if (usb_endpoint_dir_in(desc) && !ep->caps.dir_in) + return 0; + if (usb_endpoint_dir_out(desc) && !ep->caps.dir_out) + return 0; + + if (max > ep->maxpacket_limit) + return 0; + + /* "high bandwidth" works only at high speed */ + if (!gadget_is_dualspeed(gadget) && usb_endpoint_maxp_mult(desc) > 1) + return 0; + + switch (type) { + case USB_ENDPOINT_XFER_CONTROL: + /* only support ep0 for portable CONTROL traffic */ + return 0; + case USB_ENDPOINT_XFER_ISOC: + if (!ep->caps.type_iso) + return 0; + /* ISO: limit 1023 bytes full speed, 1024 high/super speed */ + if (!gadget_is_dualspeed(gadget) && max > 1023) + return 0; + break; + case USB_ENDPOINT_XFER_BULK: + if (!ep->caps.type_bulk) + return 0; + if (ep_comp && gadget_is_superspeed(gadget)) { + /* Get the number of required streams from the + * EP companion descriptor and see if the EP + * matches it + */ + num_req_streams = ep_comp->bmAttributes & 0x1f; + if (num_req_streams > ep->max_streams) + return 0; + } + break; + case USB_ENDPOINT_XFER_INT: + /* Bulk endpoints handle interrupt transfers, + * except the toggle-quirky iso-synch kind + */ + if (!ep->caps.type_int && !ep->caps.type_bulk) + return 0; + /* INT: limit 64 bytes full speed, 1024 high/super speed */ + if (!gadget_is_dualspeed(gadget) && max > 64) + return 0; + break; + } + + return 1; +} +EXPORT_SYMBOL_GPL(usb_gadget_ep_match_desc); + +/** + * usb_gadget_check_config - checks if the UDC can support the binded + * configuration + * @gadget: controller to check the USB configuration + * + * Ensure that a UDC is able to support the requested resources by a + * configuration, and that there are no resource limitations, such as + * internal memory allocated to all requested endpoints. + * + * Returns zero on success, else a negative errno. + */ +int usb_gadget_check_config(struct usb_gadget *gadget) +{ + if (gadget->ops->check_config) + return gadget->ops->check_config(gadget); + return 0; +} +EXPORT_SYMBOL_GPL(usb_gadget_check_config); + +/* ------------------------------------------------------------------------- */ + +void usb_gadget_set_state(struct usb_gadget *gadget, + enum usb_device_state state) +{ + gadget->state = state; +} +EXPORT_SYMBOL_GPL(usb_gadget_set_state); + +/* ------------------------------------------------------------------------- */ + +static void usb_udc_connect_control(struct usb_udc *udc) +{ + if (udc->vbus) + usb_gadget_connect(udc->gadget); + else + usb_gadget_disconnect(udc->gadget); +} + +/** + * usb_udc_vbus_handler - updates the udc core vbus status, and try to + * connect or disconnect gadget + * @gadget: The gadget which vbus change occurs + * @status: The vbus status + * + * The udc driver calls it when it wants to connect or disconnect gadget + * according to vbus status. + */ +void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status) +{ + struct usb_udc *udc = gadget->udc; + + if (udc) { + udc->vbus = status; + usb_udc_connect_control(udc); + } +} +EXPORT_SYMBOL_GPL(usb_udc_vbus_handler); + +/** + * usb_gadget_udc_reset - notifies the udc core that bus reset occurs + * @gadget: The gadget which bus reset occurs + * @driver: The gadget driver we want to notify + * + * If the udc driver has bus reset handler, it needs to call this when the bus + * reset occurs, it notifies the gadget driver that the bus reset occurs as + * well as updates gadget state. + */ +void usb_gadget_udc_reset(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + driver->reset(gadget); + usb_gadget_set_state(gadget, USB_STATE_DEFAULT); +} +EXPORT_SYMBOL_GPL(usb_gadget_udc_reset); + +/** + * usb_gadget_udc_start - tells usb device controller to start up + * @udc: The UDC to be started + * + * This call is issued by the UDC Class driver when it's about + * to register a gadget driver to the device controller, before + * calling gadget driver's bind() method. + * + * It allows the controller to be powered off until strictly + * necessary to have it powered on. + * + * Returns zero on success, else negative errno. + */ +static inline int usb_gadget_udc_start(struct usb_udc *udc) +{ + int ret; + + if (udc->started) { + dev_err(&udc->dev, "UDC had already started\n"); + return -EBUSY; + } + + ret = udc->gadget->ops->udc_start(udc->gadget, udc->driver); + if (!ret) + udc->started = true; + + return ret; +} + +/** + * usb_gadget_udc_stop - tells usb device controller we don't need it anymore + * @udc: The UDC to be stopped + * + * This call is issued by the UDC Class driver after calling + * gadget driver's unbind() method. + * + * The details are implementation specific, but it can go as + * far as powering off UDC completely and disable its data + * line pullups. + */ +static inline void usb_gadget_udc_stop(struct usb_udc *udc) +{ + if (!udc->started) { + dev_err(&udc->dev, "UDC had already stopped\n"); + return; + } + + udc->gadget->ops->udc_stop(udc->gadget); + udc->started = false; +} + +/** + * usb_gadget_udc_set_speed - tells usb device controller speed supported by + * current driver + * @udc: The device we want to set maximum speed + * @speed: The maximum speed to allowed to run + * + * This call is issued by the UDC Class driver before calling + * usb_gadget_udc_start() in order to make sure that we don't try to + * connect on speeds the gadget driver doesn't support. + */ +static inline void usb_gadget_udc_set_speed(struct usb_udc *udc, + enum usb_device_speed speed) +{ + struct usb_gadget *gadget = udc->gadget; + enum usb_device_speed s; + + if (speed == USB_SPEED_UNKNOWN) + s = gadget->max_speed; + else + s = min(speed, gadget->max_speed); + + if (s == USB_SPEED_SUPER_PLUS && gadget->ops->udc_set_ssp_rate) + gadget->ops->udc_set_ssp_rate(gadget, gadget->max_ssp_rate); + else if (gadget->ops->udc_set_speed) + gadget->ops->udc_set_speed(gadget, s); +} + +/** + * usb_gadget_enable_async_callbacks - tell usb device controller to enable asynchronous callbacks + * @udc: The UDC which should enable async callbacks + * + * This routine is used when binding gadget drivers. It undoes the effect + * of usb_gadget_disable_async_callbacks(); the UDC driver should enable IRQs + * (if necessary) and resume issuing callbacks. + * + * This routine will always be called in process context. + */ +static inline void usb_gadget_enable_async_callbacks(struct usb_udc *udc) +{ + struct usb_gadget *gadget = udc->gadget; + + if (gadget->ops->udc_async_callbacks) + gadget->ops->udc_async_callbacks(gadget, true); +} + +/** + * usb_gadget_disable_async_callbacks - tell usb device controller to disable asynchronous callbacks + * @udc: The UDC which should disable async callbacks + * + * This routine is used when unbinding gadget drivers. It prevents a race: + * The UDC driver doesn't know when the gadget driver's ->unbind callback + * runs, so unless it is told to disable asynchronous callbacks, it might + * issue a callback (such as ->disconnect) after the unbind has completed. + * + * After this function runs, the UDC driver must suppress all ->suspend, + * ->resume, ->disconnect, ->reset, and ->setup callbacks to the gadget driver + * until async callbacks are again enabled. A simple-minded but effective + * way to accomplish this is to tell the UDC hardware not to generate any + * more IRQs. + * + * Request completion callbacks must still be issued. However, it's okay + * to defer them until the request is cancelled, since the pull-up will be + * turned off during the time period when async callbacks are disabled. + * + * This routine will always be called in process context. + */ +static inline void usb_gadget_disable_async_callbacks(struct usb_udc *udc) +{ + struct usb_gadget *gadget = udc->gadget; + + if (gadget->ops->udc_async_callbacks) + gadget->ops->udc_async_callbacks(gadget, false); +} + +/** + * usb_initialize_gadget - initialize a gadget and its embedded struct device + * @parent: the parent device to this udc. Usually the controller driver's + * device. + * @gadget: the gadget to be initialized. + * @release: a gadget release function. + */ +void usb_initialize_gadget(struct device *parent, struct usb_gadget *gadget, + void (*release)(struct device *dev)) +{ + gadget->dev.parent = parent; + gadget->dev.bus = &gadget_bus_type; +} +EXPORT_SYMBOL_GPL(usb_initialize_gadget); + +/** + * usb_add_gadget - adds a new gadget to the udc class driver list + * @gadget: the gadget to be added to the list. + * + * Returns zero on success, negative errno otherwise. + * Does not do a final usb_put_gadget() if an error occurs. + */ +int usb_add_gadget(struct usb_gadget *gadget) +{ + struct usb_udc *udc; + int ret = -ENOMEM; + + udc = kzalloc(sizeof(*udc), GFP_KERNEL); + if (!udc) + goto error; + + udc->dev.parent = gadget->dev.parent; + ret = dev_set_name(&udc->dev, "usbgadget"); + if (ret) + goto err_put_udc; + + udc->gadget = gadget; + gadget->udc = udc; + + udc->started = false; + + mutex_lock(&udc_lock); + list_add_tail(&udc->list, &udc_list); + mutex_unlock(&udc_lock); + + ret = register_device(&udc->dev); + if (ret) + goto err_unlist_udc; + + usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED); + udc->vbus = true; + + ret = gadget_id_numbers++; + if (ret < 0) + goto err_del_udc; + gadget->id_number = ret; + dev_set_name(&gadget->dev, "gadget"); + gadget->dev.id = ret; + + ret = register_device(&gadget->dev); + if (ret) + goto err_free_id; + + dev_add_param_uint32(&gadget->dev, "product", NULL, NULL, + &gadget->product_id, "0x%04x", NULL); + dev_add_param_uint32(&gadget->dev, "vendor", NULL, NULL, + &gadget->vendor_id, "0x%04x", NULL); + gadget->manufacturer = xstrdup("barebox"); + dev_add_param_string(&gadget->dev, "manufacturer", NULL, NULL, + &gadget->manufacturer, NULL); + gadget->productname = xstrdup(barebox_get_model()); + dev_add_param_string(&gadget->dev, "productname", NULL, NULL, + &gadget->productname, NULL); + gadget->serialnumber = xstrdup(barebox_get_serial_number() ? : "unset"); + dev_add_param_string(&gadget->dev, "serialnumber", NULL, NULL, + &gadget->serialnumber, NULL); + + return 0; + + err_free_id: + err_del_udc: + unregister_device(&udc->dev); + + err_unlist_udc: + mutex_lock(&udc_lock); + list_del(&udc->list); + mutex_unlock(&udc_lock); + + err_put_udc: + error: + return ret; +} +EXPORT_SYMBOL_GPL(usb_add_gadget); + +int usb_gadget_poll(void) +{ + struct usb_udc *udc; + + list_for_each_entry(udc, &udc_list, list) { + if (udc->gadget->ops->udc_poll) + udc->gadget->ops->udc_poll(udc->gadget); + } + + return 0; +} + +/** + * usb_add_gadget_udc_release - adds a new gadget to the udc class driver list + * @parent: the parent device to this udc. Usually the controller driver's + * device. + * @gadget: the gadget to be added to the list. + * @release: a gadget release function. + * + * Returns zero on success, negative errno otherwise. + * Calls the gadget release function in the latter case. + */ +int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, + void (*release)(struct device *dev)) +{ + int ret; + + usb_initialize_gadget(parent, gadget, release); + ret = usb_add_gadget(gadget); + if (ret) + usb_put_gadget(gadget); + return ret; +} +EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release); + +/** + * usb_get_gadget_udc_name - get the name of the first UDC controller + * This functions returns the name of the first UDC controller in the system. + * Please note that this interface is usefull only for legacy drivers which + * assume that there is only one UDC controller in the system and they need to + * get its name before initialization. There is no guarantee that the UDC + * of the returned name will be still available, when gadget driver registers + * itself. + * + * Returns pointer to string with UDC controller name on success, NULL + * otherwise. Caller should kfree() returned string. + */ +char *usb_get_gadget_udc_name(void) +{ + struct usb_udc *udc; + char *name = NULL; + + /* For now we take the first available UDC */ + mutex_lock(&udc_lock); + list_for_each_entry(udc, &udc_list, list) { + if (!udc->driver) { + name = kstrdup(udc->gadget->name, GFP_KERNEL); + break; + } + } + mutex_unlock(&udc_lock); + return name; +} +EXPORT_SYMBOL_GPL(usb_get_gadget_udc_name); + +/** + * usb_add_gadget_udc - adds a new gadget to the udc class driver list + * @parent: the parent device to this udc. Usually the controller + * driver's device. + * @gadget: the gadget to be added to the list + * + * Returns zero on success, negative errno otherwise. + */ +int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) +{ + return usb_add_gadget_udc_release(parent, gadget, NULL); +} +EXPORT_SYMBOL_GPL(usb_add_gadget_udc); + +/** + * usb_del_gadget - deletes a gadget and unregisters its udc + * @gadget: the gadget to be deleted. + * + * This will unbind @gadget, if it is bound. + * It will not do a final usb_put_gadget(). + */ +void usb_del_gadget(struct usb_gadget *gadget) +{ + struct usb_udc *udc = gadget->udc; + + if (!udc) + return; + + dev_vdbg(gadget->dev.parent, "unregistering gadget\n"); + + mutex_lock(&udc_lock); + list_del(&udc->list); + mutex_unlock(&udc_lock); + + unregister_device(&gadget->dev); + unregister_device(&udc->dev); +} +EXPORT_SYMBOL_GPL(usb_del_gadget); + +/** + * usb_del_gadget_udc - unregisters a gadget + * @gadget: the gadget to be unregistered. + * + * Calls usb_del_gadget() and does a final usb_put_gadget(). + */ +void usb_del_gadget_udc(struct usb_gadget *gadget) +{ + usb_del_gadget(gadget); + usb_put_gadget(gadget); +} +EXPORT_SYMBOL_GPL(usb_del_gadget_udc); + +/* ------------------------------------------------------------------------- */ + +static int gadget_match_driver(struct device *dev, struct driver *drv) +{ + struct usb_gadget *gadget = dev_to_usb_gadget(dev); + struct usb_udc *udc = gadget->udc; + struct usb_gadget_driver *driver = container_of(drv, + struct usb_gadget_driver, driver); + + /* If the driver specifies a udc_name, it must match the UDC's name */ + if (driver->udc_name && + strcmp(driver->udc_name, dev_name(&udc->dev)) != 0) + return -1; + + /* If the driver is already bound to a gadget, it doesn't match */ + if (driver->is_bound) + return -1; + + /* Otherwise any gadget driver matches any UDC */ + return 0; +} + +static void udc_poll_driver(struct poller_struct *poller) +{ + struct usb_udc *udc = container_of(poller, struct usb_udc, poller); + + udc->gadget->ops->udc_poll(udc->gadget); +} + +static int gadget_bind_driver(struct device *dev) +{ + struct usb_gadget *gadget = dev_to_usb_gadget(dev); + struct usb_udc *udc = gadget->udc; + struct usb_gadget_driver *driver = container_of(dev->driver, + struct usb_gadget_driver, driver); + int ret = 0; + + mutex_lock(&udc_lock); + if (driver->is_bound) { + mutex_unlock(&udc_lock); + return -ENXIO; /* Driver binds to only one gadget */ + } + driver->is_bound = true; + udc->driver = driver; + mutex_unlock(&udc_lock); + + dev_dbg(&udc->dev, "binding gadget driver [%s]\n", driver->function); + + usb_gadget_udc_set_speed(udc, driver->max_speed); + + if (udc->gadget->ops->udc_poll) { + udc->poller.func = udc_poll_driver; + ret = poller_register(&udc->poller, dev_name(&udc->dev)); + if (ret) + return ret; + } + + ret = driver->bind(udc->gadget, driver); + if (ret) + goto err_bind; + + ret = usb_gadget_udc_start(udc); + if (ret) + goto err_start; + usb_gadget_enable_async_callbacks(udc); + usb_udc_connect_control(udc); + + return 0; + + err_start: + driver->unbind(udc->gadget); + + err_bind: + if (ret != -EISNAM) + dev_err(&udc->dev, "failed to start %s: %d\n", + driver->function, ret); + + if (udc->gadget->ops->udc_poll) + poller_unregister(&udc->poller); + + mutex_lock(&udc_lock); + udc->driver = NULL; + driver->is_bound = false; + mutex_unlock(&udc_lock); + + return ret; +} + +static void gadget_unbind_driver(struct device *dev) +{ + struct usb_gadget *gadget = dev_to_usb_gadget(dev); + struct usb_udc *udc = gadget->udc; + struct usb_gadget_driver *driver = udc->driver; + + dev_dbg(&udc->dev, "unbinding gadget driver [%s]\n", driver->function); + + if (udc->gadget->ops->udc_poll) + poller_unregister(&udc->poller); + + usb_gadget_disconnect(gadget); + usb_gadget_disable_async_callbacks(udc); + udc->driver->unbind(gadget); + usb_gadget_udc_stop(udc); + + mutex_lock(&udc_lock); + driver->is_bound = false; + udc->driver = NULL; + mutex_unlock(&udc_lock); +} + +/* ------------------------------------------------------------------------- */ + +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + int ret; + + if (!driver || !driver->bind || !driver->setup) + return -EINVAL; + + driver->driver.bus = &gadget_bus_type; + ret = register_driver(&driver->driver); + if (ret) { + pr_warn("%s: driver registration failed: %d\n", + driver->function, ret); + return ret; + } + + mutex_lock(&udc_lock); + if (!driver->is_bound) { + if (driver->match_existing_only) { + pr_warn("%s: couldn't find an available UDC or it's busy\n", + driver->function); + ret = -EBUSY; + } else { + pr_info("%s: couldn't find an available UDC\n", + driver->function); + ret = 0; + } + } + mutex_unlock(&udc_lock); + + if (ret) + unregister_driver(&driver->driver); + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_register_driver); + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + if (!driver || !driver->unbind) + return -EINVAL; + + unregister_driver(&driver->driver); + return 0; +} +EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver); + +/* ------------------------------------------------------------------------- */ + +static struct bus_type gadget_bus_type = { + .name = "gadget", + .probe = gadget_bind_driver, + .remove = gadget_unbind_driver, + .match = gadget_match_driver, +}; + +static int usb_udc_init(void) +{ + bus_register(&gadget_bus_type); + + return 0; +} +coredevice_initcall(usb_udc_init); diff --git a/drivers/usb/gadget/fsl_udc.c b/drivers/usb/gadget/udc/fsl_udc.c index a44c7abc01..41de44b30d 100644 --- a/drivers/usb/gadget/fsl_udc.c +++ b/drivers/usb/gadget/udc/fsl_udc.c @@ -1,393 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> -#include <dma.h> #include <errno.h> #include <dma.h> #include <init.h> #include <clock.h> -#include <usb/ch9.h> -#include <usb/gadget.h> -#include <usb/fsl_usb2.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/fsl_usb2.h> #include <io.h> #include <asm/byteorder.h> #include <linux/err.h> - +#include <soc/fsl/fsl_udc.h> #include <asm/mmu.h> -/* ### define USB registers here - */ -#define USB_MAX_CTRL_PAYLOAD 64 -#define USB_DR_SYS_OFFSET 0x400 - - /* USB DR device mode registers (Little Endian) */ -struct usb_dr_device { - /* Capability register */ - u8 res1[256]; - u16 caplength; /* Capability Register Length */ - u16 hciversion; /* Host Controller Interface Version */ - u32 hcsparams; /* Host Controller Structual Parameters */ - u32 hccparams; /* Host Controller Capability Parameters */ - u8 res2[20]; - u32 dciversion; /* Device Controller Interface Version */ - u32 dccparams; /* Device Controller Capability Parameters */ - u8 res3[24]; - /* Operation register */ - u32 usbcmd; /* USB Command Register */ - u32 usbsts; /* USB Status Register */ - u32 usbintr; /* USB Interrupt Enable Register */ - u32 frindex; /* Frame Index Register */ - u8 res4[4]; - u32 deviceaddr; /* Device Address */ - u32 endpointlistaddr; /* Endpoint List Address Register */ - u8 res5[4]; - u32 burstsize; /* Master Interface Data Burst Size Register */ - u32 txttfilltuning; /* Transmit FIFO Tuning Controls Register */ - u8 res6[24]; - u32 configflag; /* Configure Flag Register */ - u32 portsc1; /* Port 1 Status and Control Register */ - u8 res7[28]; - u32 otgsc; /* On-The-Go Status and Control */ - u32 usbmode; /* USB Mode Register */ - u32 endptsetupstat; /* Endpoint Setup Status Register */ - u32 endpointprime; /* Endpoint Initialization Register */ - u32 endptflush; /* Endpoint Flush Register */ - u32 endptstatus; /* Endpoint Status Register */ - u32 endptcomplete; /* Endpoint Complete Register */ - u32 endptctrl[6]; /* Endpoint Control Registers */ -}; - -/* ep0 transfer state */ -#define WAIT_FOR_SETUP 0 -#define DATA_STATE_XMIT 1 -#define DATA_STATE_NEED_ZLP 2 -#define WAIT_FOR_OUT_STATUS 3 -#define DATA_STATE_RECV 4 - -/* Device Controller Capability Parameter register */ -#define DCCPARAMS_DC 0x00000080 -#define DCCPARAMS_DEN_MASK 0x0000001f - -/* Frame Index Register Bit Masks */ -#define USB_FRINDEX_MASKS 0x3fff -/* USB CMD Register Bit Masks */ -#define USB_CMD_RUN_STOP 0x00000001 -#define USB_CMD_CTRL_RESET 0x00000002 -#define USB_CMD_PERIODIC_SCHEDULE_EN 0x00000010 -#define USB_CMD_ASYNC_SCHEDULE_EN 0x00000020 -#define USB_CMD_INT_AA_DOORBELL 0x00000040 -#define USB_CMD_ASP 0x00000300 -#define USB_CMD_ASYNC_SCH_PARK_EN 0x00000800 -#define USB_CMD_SUTW 0x00002000 -#define USB_CMD_ATDTW 0x00004000 -#define USB_CMD_ITC 0x00FF0000 - -/* bit 15,3,2 are frame list size */ -#define USB_CMD_FRAME_SIZE_1024 0x00000000 -#define USB_CMD_FRAME_SIZE_512 0x00000004 -#define USB_CMD_FRAME_SIZE_256 0x00000008 -#define USB_CMD_FRAME_SIZE_128 0x0000000C -#define USB_CMD_FRAME_SIZE_64 0x00008000 -#define USB_CMD_FRAME_SIZE_32 0x00008004 -#define USB_CMD_FRAME_SIZE_16 0x00008008 -#define USB_CMD_FRAME_SIZE_8 0x0000800C - -/* bit 9-8 are async schedule park mode count */ -#define USB_CMD_ASP_00 0x00000000 -#define USB_CMD_ASP_01 0x00000100 -#define USB_CMD_ASP_10 0x00000200 -#define USB_CMD_ASP_11 0x00000300 -#define USB_CMD_ASP_BIT_POS 8 - -/* bit 23-16 are interrupt threshold control */ -#define USB_CMD_ITC_NO_THRESHOLD 0x00000000 -#define USB_CMD_ITC_1_MICRO_FRM 0x00010000 -#define USB_CMD_ITC_2_MICRO_FRM 0x00020000 -#define USB_CMD_ITC_4_MICRO_FRM 0x00040000 -#define USB_CMD_ITC_8_MICRO_FRM 0x00080000 -#define USB_CMD_ITC_16_MICRO_FRM 0x00100000 -#define USB_CMD_ITC_32_MICRO_FRM 0x00200000 -#define USB_CMD_ITC_64_MICRO_FRM 0x00400000 -#define USB_CMD_ITC_BIT_POS 16 - -/* USB STS Register Bit Masks */ -#define USB_STS_INT 0x00000001 -#define USB_STS_ERR 0x00000002 -#define USB_STS_PORT_CHANGE 0x00000004 -#define USB_STS_FRM_LST_ROLL 0x00000008 -#define USB_STS_SYS_ERR 0x00000010 -#define USB_STS_IAA 0x00000020 -#define USB_STS_RESET 0x00000040 -#define USB_STS_SOF 0x00000080 -#define USB_STS_SUSPEND 0x00000100 -#define USB_STS_HC_HALTED 0x00001000 -#define USB_STS_RCL 0x00002000 -#define USB_STS_PERIODIC_SCHEDULE 0x00004000 -#define USB_STS_ASYNC_SCHEDULE 0x00008000 - -/* USB INTR Register Bit Masks */ -#define USB_INTR_INT_EN 0x00000001 -#define USB_INTR_ERR_INT_EN 0x00000002 -#define USB_INTR_PTC_DETECT_EN 0x00000004 -#define USB_INTR_FRM_LST_ROLL_EN 0x00000008 -#define USB_INTR_SYS_ERR_EN 0x00000010 -#define USB_INTR_ASYN_ADV_EN 0x00000020 -#define USB_INTR_RESET_EN 0x00000040 -#define USB_INTR_SOF_EN 0x00000080 -#define USB_INTR_DEVICE_SUSPEND 0x00000100 - -/* Device Address bit masks */ -#define USB_DEVICE_ADDRESS_MASK 0xFE000000 -#define USB_DEVICE_ADDRESS_BIT_POS 25 - -/* endpoint list address bit masks */ -#define USB_EP_LIST_ADDRESS_MASK 0xfffff800 - -/* PORTSCX Register Bit Masks */ -#define PORTSCX_CURRENT_CONNECT_STATUS 0x00000001 -#define PORTSCX_CONNECT_STATUS_CHANGE 0x00000002 -#define PORTSCX_PORT_ENABLE 0x00000004 -#define PORTSCX_PORT_EN_DIS_CHANGE 0x00000008 -#define PORTSCX_OVER_CURRENT_ACT 0x00000010 -#define PORTSCX_OVER_CURRENT_CHG 0x00000020 -#define PORTSCX_PORT_FORCE_RESUME 0x00000040 -#define PORTSCX_PORT_SUSPEND 0x00000080 -#define PORTSCX_PORT_RESET 0x00000100 -#define PORTSCX_LINE_STATUS_BITS 0x00000C00 -#define PORTSCX_PORT_POWER 0x00001000 -#define PORTSCX_PORT_INDICTOR_CTRL 0x0000C000 -#define PORTSCX_PORT_TEST_CTRL 0x000F0000 -#define PORTSCX_WAKE_ON_CONNECT_EN 0x00100000 -#define PORTSCX_WAKE_ON_CONNECT_DIS 0x00200000 -#define PORTSCX_WAKE_ON_OVER_CURRENT 0x00400000 -#define PORTSCX_PHY_LOW_POWER_SPD 0x00800000 -#define PORTSCX_PORT_FORCE_FULL_SPEED 0x01000000 -#define PORTSCX_PORT_SPEED_MASK 0x0C000000 -#define PORTSCX_PORT_WIDTH 0x10000000 -#define PORTSCX_PHY_TYPE_SEL 0xC0000000 - -/* bit 11-10 are line status */ -#define PORTSCX_LINE_STATUS_SE0 0x00000000 -#define PORTSCX_LINE_STATUS_JSTATE 0x00000400 -#define PORTSCX_LINE_STATUS_KSTATE 0x00000800 -#define PORTSCX_LINE_STATUS_UNDEF 0x00000C00 -#define PORTSCX_LINE_STATUS_BIT_POS 10 - -/* bit 15-14 are port indicator control */ -#define PORTSCX_PIC_OFF 0x00000000 -#define PORTSCX_PIC_AMBER 0x00004000 -#define PORTSCX_PIC_GREEN 0x00008000 -#define PORTSCX_PIC_UNDEF 0x0000C000 -#define PORTSCX_PIC_BIT_POS 14 - -/* bit 19-16 are port test control */ -#define PORTSCX_PTC_DISABLE 0x00000000 -#define PORTSCX_PTC_JSTATE 0x00010000 -#define PORTSCX_PTC_KSTATE 0x00020000 -#define PORTSCX_PTC_SEQNAK 0x00030000 -#define PORTSCX_PTC_PACKET 0x00040000 -#define PORTSCX_PTC_FORCE_EN 0x00050000 -#define PORTSCX_PTC_BIT_POS 16 - -/* bit 27-26 are port speed */ -#define PORTSCX_PORT_SPEED_FULL 0x00000000 -#define PORTSCX_PORT_SPEED_LOW 0x04000000 -#define PORTSCX_PORT_SPEED_HIGH 0x08000000 -#define PORTSCX_PORT_SPEED_UNDEF 0x0C000000 -#define PORTSCX_SPEED_BIT_POS 26 - -/* bit 28 is parallel transceiver width for UTMI interface */ -#define PORTSCX_PTW 0x10000000 -#define PORTSCX_PTW_8BIT 0x00000000 -#define PORTSCX_PTW_16BIT 0x10000000 - -/* bit 31-30 are port transceiver select */ -#define PORTSCX_PTS_UTMI 0x00000000 -#define PORTSCX_PTS_ULPI 0x80000000 -#define PORTSCX_PTS_FSLS 0xC0000000 -#define PORTSCX_PTS_BIT_POS 30 - -/* otgsc Register Bit Masks */ -#define OTGSC_CTRL_VUSB_DISCHARGE 0x00000001 -#define OTGSC_CTRL_VUSB_CHARGE 0x00000002 -#define OTGSC_CTRL_OTG_TERM 0x00000008 -#define OTGSC_CTRL_DATA_PULSING 0x00000010 -#define OTGSC_STS_USB_ID 0x00000100 -#define OTGSC_STS_A_VBUS_VALID 0x00000200 -#define OTGSC_STS_A_SESSION_VALID 0x00000400 -#define OTGSC_STS_B_SESSION_VALID 0x00000800 -#define OTGSC_STS_B_SESSION_END 0x00001000 -#define OTGSC_STS_1MS_TOGGLE 0x00002000 -#define OTGSC_STS_DATA_PULSING 0x00004000 -#define OTGSC_INTSTS_USB_ID 0x00010000 -#define OTGSC_INTSTS_A_VBUS_VALID 0x00020000 -#define OTGSC_INTSTS_A_SESSION_VALID 0x00040000 -#define OTGSC_INTSTS_B_SESSION_VALID 0x00080000 -#define OTGSC_INTSTS_B_SESSION_END 0x00100000 -#define OTGSC_INTSTS_1MS 0x00200000 -#define OTGSC_INTSTS_DATA_PULSING 0x00400000 -#define OTGSC_INTR_USB_ID 0x01000000 -#define OTGSC_INTR_A_VBUS_VALID 0x02000000 -#define OTGSC_INTR_A_SESSION_VALID 0x04000000 -#define OTGSC_INTR_B_SESSION_VALID 0x08000000 -#define OTGSC_INTR_B_SESSION_END 0x10000000 -#define OTGSC_INTR_1MS_TIMER 0x20000000 -#define OTGSC_INTR_DATA_PULSING 0x40000000 - -/* USB MODE Register Bit Masks */ -#define USB_MODE_CTRL_MODE_IDLE 0x00000000 -#define USB_MODE_CTRL_MODE_DEVICE 0x00000002 -#define USB_MODE_CTRL_MODE_HOST 0x00000003 -#define USB_MODE_CTRL_MODE_RSV 0x00000001 -#define USB_MODE_CTRL_MODE_MASK 0x00000003 -#define USB_MODE_SETUP_LOCK_OFF 0x00000008 -#define USB_MODE_STREAM_DISABLE 0x00000010 -/* Endpoint Flush Register */ -#define EPFLUSH_TX_OFFSET 0x00010000 -#define EPFLUSH_RX_OFFSET 0x00000000 - -/* Endpoint Setup Status bit masks */ -#define EP_SETUP_STATUS_MASK 0x0000003F -#define EP_SETUP_STATUS_EP0 0x00000001 - -/* ENDPOINTCTRLx Register Bit Masks */ -#define EPCTRL_TX_ENABLE 0x00800000 -#define EPCTRL_TX_DATA_TOGGLE_RST 0x00400000 /* Not EP0 */ -#define EPCTRL_TX_DATA_TOGGLE_INH 0x00200000 /* Not EP0 */ -#define EPCTRL_TX_TYPE 0x000C0000 -#define EPCTRL_TX_DATA_SOURCE 0x00020000 /* Not EP0 */ -#define EPCTRL_TX_EP_STALL 0x00010000 -#define EPCTRL_RX_ENABLE 0x00000080 -#define EPCTRL_RX_DATA_TOGGLE_RST 0x00000040 /* Not EP0 */ -#define EPCTRL_RX_DATA_TOGGLE_INH 0x00000020 /* Not EP0 */ -#define EPCTRL_RX_TYPE 0x0000000C -#define EPCTRL_RX_DATA_SINK 0x00000002 /* Not EP0 */ -#define EPCTRL_RX_EP_STALL 0x00000001 - -/* bit 19-18 and 3-2 are endpoint type */ -#define EPCTRL_EP_TYPE_CONTROL 0 -#define EPCTRL_EP_TYPE_ISO 1 -#define EPCTRL_EP_TYPE_BULK 2 -#define EPCTRL_EP_TYPE_INTERRUPT 3 -#define EPCTRL_TX_EP_TYPE_SHIFT 18 -#define EPCTRL_RX_EP_TYPE_SHIFT 2 - -/* SNOOPn Register Bit Masks */ -#define SNOOP_ADDRESS_MASK 0xFFFFF000 -#define SNOOP_SIZE_ZERO 0x00 /* snooping disable */ -#define SNOOP_SIZE_4KB 0x0B /* 4KB snoop size */ -#define SNOOP_SIZE_8KB 0x0C -#define SNOOP_SIZE_16KB 0x0D -#define SNOOP_SIZE_32KB 0x0E -#define SNOOP_SIZE_64KB 0x0F -#define SNOOP_SIZE_128KB 0x10 -#define SNOOP_SIZE_256KB 0x11 -#define SNOOP_SIZE_512KB 0x12 -#define SNOOP_SIZE_1MB 0x13 -#define SNOOP_SIZE_2MB 0x14 -#define SNOOP_SIZE_4MB 0x15 -#define SNOOP_SIZE_8MB 0x16 -#define SNOOP_SIZE_16MB 0x17 -#define SNOOP_SIZE_32MB 0x18 -#define SNOOP_SIZE_64MB 0x19 -#define SNOOP_SIZE_128MB 0x1A -#define SNOOP_SIZE_256MB 0x1B -#define SNOOP_SIZE_512MB 0x1C -#define SNOOP_SIZE_1GB 0x1D -#define SNOOP_SIZE_2GB 0x1E /* 2GB snoop size */ - -/* pri_ctrl Register Bit Masks */ -#define PRI_CTRL_PRI_LVL1 0x0000000C -#define PRI_CTRL_PRI_LVL0 0x00000003 - -/* si_ctrl Register Bit Masks */ -#define SI_CTRL_ERR_DISABLE 0x00000010 -#define SI_CTRL_IDRC_DISABLE 0x00000008 -#define SI_CTRL_RD_SAFE_EN 0x00000004 -#define SI_CTRL_RD_PREFETCH_DISABLE 0x00000002 -#define SI_CTRL_RD_PREFEFETCH_VAL 0x00000001 - -/* control Register Bit Masks */ -#define USB_CTRL_IOENB 0x00000004 -#define USB_CTRL_ULPI_INT0EN 0x00000001 - -/* Endpoint Queue Head data struct - * Rem: all the variables of qh are LittleEndian Mode - * and NEXT_POINTER_MASK should operate on a LittleEndian, Phy Addr - */ -struct ep_queue_head { - u32 max_pkt_length; /* Mult(31-30) , Zlt(29) , Max Pkt len - and IOS(15) */ - u32 curr_dtd_ptr; /* Current dTD Pointer(31-5) */ - u32 next_dtd_ptr; /* Next dTD Pointer(31-5), T(0) */ - u32 size_ioc_int_sts; /* Total bytes (30-16), IOC (15), - MultO(11-10), STS (7-0) */ - u32 buff_ptr0; /* Buffer pointer Page 0 (31-12) */ - u32 buff_ptr1; /* Buffer pointer Page 1 (31-12) */ - u32 buff_ptr2; /* Buffer pointer Page 2 (31-12) */ - u32 buff_ptr3; /* Buffer pointer Page 3 (31-12) */ - u32 buff_ptr4; /* Buffer pointer Page 4 (31-12) */ - u32 res1; - u8 setup_buffer[8]; /* Setup data 8 bytes */ - u32 res2[4]; -}; - -/* Endpoint Queue Head Bit Masks */ -#define EP_QUEUE_HEAD_MULT_POS 30 -#define EP_QUEUE_HEAD_ZLT_SEL 0x20000000 -#define EP_QUEUE_HEAD_MAX_PKT_LEN_POS 16 -#define EP_QUEUE_HEAD_MAX_PKT_LEN(ep_info) (((ep_info)>>16)&0x07ff) -#define EP_QUEUE_HEAD_IOS 0x00008000 -#define EP_QUEUE_HEAD_NEXT_TERMINATE 0x00000001 -#define EP_QUEUE_HEAD_IOC 0x00008000 -#define EP_QUEUE_HEAD_MULTO 0x00000C00 -#define EP_QUEUE_HEAD_STATUS_HALT 0x00000040 -#define EP_QUEUE_HEAD_STATUS_ACTIVE 0x00000080 -#define EP_QUEUE_CURRENT_OFFSET_MASK 0x00000FFF -#define EP_QUEUE_HEAD_NEXT_POINTER_MASK 0xFFFFFFE0 -#define EP_QUEUE_FRINDEX_MASK 0x000007FF -#define EP_MAX_LENGTH_TRANSFER 0x4000 - -/* Endpoint Transfer Descriptor data struct */ -/* Rem: all the variables of td are LittleEndian Mode */ -struct ep_td_struct { - u32 next_td_ptr; /* Next TD pointer(31-5), T(0) set - indicate invalid */ - u32 size_ioc_sts; /* Total bytes (30-16), IOC (15), - MultO(11-10), STS (7-0) */ - u32 buff_ptr0; /* Buffer pointer Page 0 */ - u32 buff_ptr1; /* Buffer pointer Page 1 */ - u32 buff_ptr2; /* Buffer pointer Page 2 */ - u32 buff_ptr3; /* Buffer pointer Page 3 */ - u32 buff_ptr4; /* Buffer pointer Page 4 */ - u32 res; - /* 32 bytes */ - dma_addr_t td_dma; /* dma address for this td */ - /* virtual address of next td specified in next_td_ptr */ - struct ep_td_struct *next_td_virt; -}; - -/* Endpoint Transfer Descriptor bit Masks */ -#define DTD_NEXT_TERMINATE 0x00000001 -#define DTD_IOC 0x00008000 -#define DTD_STATUS_ACTIVE 0x00000080 -#define DTD_STATUS_HALTED 0x00000040 -#define DTD_STATUS_DATA_BUFF_ERR 0x00000020 -#define DTD_STATUS_TRANSACTION_ERR 0x00000008 -#define DTD_RESERVED_FIELDS 0x80007300 -#define DTD_ADDR_MASK 0xFFFFFFE0 -#define DTD_PACKET_SIZE 0x7FFF0000 -#define DTD_LENGTH_BIT_POS 16 -#define DTD_ERROR_MASK (DTD_STATUS_HALTED | \ - DTD_STATUS_DATA_BUFF_ERR | \ - DTD_STATUS_TRANSACTION_ERR) -/* Alignment requirements; must be a power of two */ -#define DTD_ALIGNMENT 0x20 -#define QH_ALIGNMENT 2048 - -/* Controller dma boundary */ -#define UDC_DMA_BOUNDARY 0x1000 - -/*-------------------------------------------------------------------------*/ - /* ### driver private data */ struct fsl_req { @@ -572,8 +197,8 @@ static void done(struct fsl_ep *ep, struct fsl_req *req, int status) dma_free_coherent(curr_td, 0, sizeof(struct ep_td_struct)); } - dma_sync_single_for_cpu((unsigned long)req->req.buf, req->req.length, - DMA_BIDIRECTIONAL); + dma_sync_single_for_cpu(udc->gadget.dev.parent, (unsigned long)req->req.buf, + req->req.length, DMA_BIDIRECTIONAL); if (status && (status != -ESHUTDOWN)) VDBG("complete %s req %p stat %d len %u/%u", @@ -1128,18 +753,19 @@ out: /* Fill in the dTD structure * @req: request that the transfer belongs to - * @length: return actually data length of the dTD * @dma: return dma address of the dTD * @is_last: return flag if it is the last dTD of the request * return: pointer to the built dTD */ -static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length, +static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, dma_addr_t *dma, int *is_last) { + unsigned length; u32 swap_temp; struct ep_td_struct *dtd; + unsigned long buf; /* how big will this transfer be? */ - *length = min(req->req.length - req->req.actual, + length = min(req->req.length - req->req.actual, (unsigned)EP_MAX_LENGTH_TRANSFER); dtd = dma_alloc_coherent(sizeof(struct ep_td_struct), @@ -1154,18 +780,24 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length, dtd->size_ioc_sts = cpu_to_le32(swap_temp); /* Init all of buffer page pointers */ - swap_temp = (u32) (req->req.buf + req->req.actual); + buf = (unsigned long)req->req.buf; + if (buf > 0xffffffff) { + pr_err("Only 32bit supported\n"); + return NULL; + } + + swap_temp = (u32)(buf + req->req.actual); dtd->buff_ptr0 = cpu_to_le32(swap_temp); dtd->buff_ptr1 = cpu_to_le32(swap_temp + 0x1000); dtd->buff_ptr2 = cpu_to_le32(swap_temp + 0x2000); dtd->buff_ptr3 = cpu_to_le32(swap_temp + 0x3000); dtd->buff_ptr4 = cpu_to_le32(swap_temp + 0x4000); - req->req.actual += *length; + req->req.actual += length; /* zlp is needed if req->req.zero is set */ if (req->req.zero) { - if (*length == 0 || (*length % req->ep->ep.maxpacket) != 0) + if (length == 0 || (length % req->ep->ep.maxpacket) != 0) *is_last = 1; else *is_last = 0; @@ -1177,7 +809,7 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length, if ((*is_last) == 0) VDBG("multi-dtd request!"); /* Fill in the transfer size; set active bit */ - swap_temp = ((*length << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE); + swap_temp = ((length << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE); /* Enable interrupt for the last dtd of a request */ if (*is_last && !req->req.no_interrupt) @@ -1193,14 +825,13 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length, /* Generate dtd chain for a request */ static int fsl_req_to_dtd(struct fsl_req *req) { - unsigned count; int is_last; int is_first =1; struct ep_td_struct *last_dtd = NULL, *dtd; dma_addr_t dma; do { - dtd = fsl_build_dtd(req, &count, &dma, &is_last); + dtd = fsl_build_dtd(req, &dma, &is_last); if (dtd == NULL) return -ENOMEM; @@ -1254,8 +885,8 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req) req->ep = ep; - dma_sync_single_for_device((unsigned long)req->req.buf, req->req.length, - DMA_BIDIRECTIONAL); + dma_sync_single_for_device(udc->gadget.dev.parent, (unsigned long)req->req.buf, + req->req.length, DMA_BIDIRECTIONAL); req->req.status = -EINPROGRESS; req->req.actual = 0; @@ -1321,13 +952,19 @@ static int fsl_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) if (req->queue.next != &ep->queue) { struct ep_queue_head *qh; struct fsl_req *next_req; + unsigned long next_req_head; qh = ep->qh; next_req = list_entry(req->queue.next, struct fsl_req, queue); /* Point the QH to the first TD of next request */ - writel((u32) next_req->head, &qh->curr_dtd_ptr); + next_req_head = (unsigned long)next_req->head; + if (next_req_head > 0xffffffff) { + pr_err("Only 32bit supported\n"); + goto out; + } + writel((u32)next_req_head, &qh->curr_dtd_ptr); } /* The request hasn't been processed, patch up the TD chain */ @@ -1343,7 +980,8 @@ static int fsl_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) done(ep, req, -ECONNRESET); /* Enable EP */ -out: epctrl = readl(&dr_regs->endptctrl[ep_num]); +out: + epctrl = readl(&dr_regs->endptctrl[ep_num]); if (ep_is_in(ep)) epctrl |= EPCTRL_TX_ENABLE; else @@ -2009,7 +1647,7 @@ static int fsl_udc_start(struct usb_gadget *gadget, struct usb_gadget_driver *dr } /* Disconnect from gadget driver */ -static int fsl_udc_stop(struct usb_gadget *gadget, struct usb_gadget_driver *driver) +static int fsl_udc_stop(struct usb_gadget *gadget) { struct fsl_udc *udc = to_fsl_udc(gadget); struct fsl_ep *loop_ep; @@ -2033,7 +1671,7 @@ static int fsl_udc_stop(struct usb_gadget *gadget, struct usb_gadget_driver *dri } static int struct_udc_setup(struct fsl_udc *udc, - struct device_d *dev) + struct device *dev) { struct fsl_usb2_platform_data *pdata = dev->platform_data; size_t size; @@ -2216,7 +1854,7 @@ static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index, return 0; } -struct fsl_udc *ci_udc_register(struct device_d *dev, void __iomem *regs) +struct fsl_udc *ci_udc_register(struct device *dev, void __iomem *regs) { struct fsl_udc *udc_controller; int ret, i; @@ -2298,7 +1936,7 @@ void ci_udc_unregister(struct fsl_udc *udc) free(udc); } -static int fsl_udc_probe(struct device_d *dev) +static int fsl_udc_probe(struct device *dev) { struct fsl_udc *udc; void __iomem *regs = dev_request_mem_region(dev, 0); @@ -2315,14 +1953,14 @@ static int fsl_udc_probe(struct device_d *dev) return 0; } -static void fsl_udc_remove(struct device_d *dev) +static void fsl_udc_remove(struct device *dev) { struct fsl_udc *udc = dev->priv; ci_udc_unregister(udc); } -static struct driver_d fsl_udc_driver = { +static struct driver fsl_udc_driver = { .name = "fsl-udc", .probe = fsl_udc_probe, .remove = fsl_udc_remove, diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index 442c90ca05..20148f4878 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -1,20 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Handles the Intel 27x USB Device Controller (UDC) * * Inspired by original driver by Frank Becker, David Brownell, and others. * Copyright (C) 2008 Robert Jarzmik * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * * Taken from linux-2.6 kernel and adapted to barebox. */ #include <common.h> @@ -24,12 +14,12 @@ #include <gpio.h> #include <init.h> -#include <usb/ch9.h> -#include <usb/gadget.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> #include "pxa27x_udc.h" -#include <mach/udc_pxa2xx.h> -#include <mach/pxa-regs.h> +#include <mach/pxa/udc_pxa2xx.h> +#include <mach/pxa/pxa-regs.h> #define DRIVER_VERSION "2008-04-18" #define DRIVER_DESC "PXA 27x USB Device Controller driver" @@ -341,7 +331,6 @@ static int write_packet(struct pxa_ep *ep, struct pxa27x_request *req, u8 *buf_8; buf = (u32 *)(req->req.buf + req->req.actual); - prefetch(buf); length = min(req->req.length - req->req.actual, max); req->req.actual += length; @@ -880,7 +869,7 @@ static int pxa_udc_vbus_session(struct usb_gadget *_gadget, int is_active) } static int pxa_udc_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver); -static int pxa_udc_stop(struct usb_gadget *gadget, struct usb_gadget_driver *driver); +static int pxa_udc_stop(struct usb_gadget *gadget); static void pxa_udc_gadget_poll(struct usb_gadget *gadget); static const struct usb_gadget_ops pxa_udc_ops = { @@ -893,12 +882,12 @@ static const struct usb_gadget_ops pxa_udc_ops = { .udc_poll = pxa_udc_gadget_poll, }; -static void clk_enable(void) +static void usb_clk_enable(void) { CKEN |= CKEN_USB; } -static void clk_disable(void) +static void usb_clk_disable(void) { CKEN &= ~CKEN_USB; } @@ -912,7 +901,7 @@ static void udc_disable(struct pxa_udc *udc) udc_writel(udc, UDCICR1, 0); udc_clear_mask_UDCCR(udc, UDCCR_UDE); - clk_disable(); + usb_clk_disable(); ep0_idle(udc); udc->gadget.speed = USB_SPEED_UNKNOWN; @@ -957,7 +946,7 @@ static void udc_enable(struct pxa_udc *udc) udc_writel(udc, UDCICR1, 0); udc_clear_mask_UDCCR(udc, UDCCR_UDE); - clk_enable(); + usb_clk_enable(); ep0_idle(udc); udc->gadget.speed = USB_SPEED_FULL; @@ -998,36 +987,26 @@ static int pxa_udc_start(struct usb_gadget *gadget, struct usb_gadget_driver *dr return 0; } -static void stop_activity(struct pxa_udc *udc, struct usb_gadget_driver *driver) +static void stop_activity(struct pxa_udc *udc) { int i; - /* don't disconnect drivers more than once */ - if (udc->gadget.speed == USB_SPEED_UNKNOWN) - driver = NULL; udc->gadget.speed = USB_SPEED_UNKNOWN; for (i = 0; i < NR_USB_ENDPOINTS; i++) pxa_ep_disable(&udc->udc_usb_ep[i].usb_ep); - - if (driver) - driver->disconnect(&udc->gadget); } -static int pxa_udc_stop(struct usb_gadget *gadget, struct usb_gadget_driver *driver) +static int pxa_udc_stop(struct usb_gadget *gadget) { struct pxa_udc *udc = the_controller; if (!udc) return -ENODEV; - if (!driver || driver != udc->driver || !driver->unbind) - return -EINVAL; - stop_activity(udc, driver); + stop_activity(udc); udc_disable(udc); - driver->disconnect(&udc->gadget); - driver->unbind(&udc->gadget); udc->driver = NULL; /* @@ -1359,7 +1338,7 @@ static void irq_udc_reset(struct pxa_udc *udc) if ((udccr & UDCCR_UDA) == 0) { dev_dbg(udc->dev, "USB reset start\n"); - stop_activity(udc, udc->driver); + stop_activity(udc); } udc->gadget.speed = USB_SPEED_FULL; @@ -1380,7 +1359,7 @@ static void pxa_udc_gadget_poll(struct usb_gadget *gadget) if (should_enable_udc(udc)) udc_enable(udc); if (should_disable_udc(udc)) { - stop_activity(udc, udc->driver); + stop_activity(udc); udc_disable(udc); } @@ -1448,7 +1427,7 @@ static struct pxa_udc memory = { } }; -static int __init pxa_udc_probe(struct device_d *dev) +static int __init pxa_udc_probe(struct device *dev) { struct resource *iores; struct pxa_udc *udc = &memory; @@ -1483,7 +1462,7 @@ static int __init pxa_udc_probe(struct device_d *dev) #define pxa27x_clear_otgph() do {} while (0) -static struct driver_d udc_driver = { +static struct driver udc_driver = { .name = "pxa27x-udc", .probe = pxa_udc_probe, }; diff --git a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/udc/pxa27x_udc.h index 554626e9e3..80a93cdcf9 100644 --- a/drivers/usb/gadget/pxa27x_udc.h +++ b/drivers/usb/gadget/udc/pxa27x_udc.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * linux/drivers/usb/gadget/pxa27x_udc.h * Intel PXA27x on-chip full speed USB device controller @@ -5,16 +6,6 @@ * Inspired by original driver by Frank Becker, David Brownell, and others. * Copyright (C) 2008 Robert Jarzmik * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * * Taken from linux-2.6 kernel and adapted to barebox. */ @@ -345,7 +336,7 @@ struct pxa_udc { void __iomem *regs; int irq; struct clk *clk; - struct device_d *dev; + struct device *dev; struct usb_gadget gadget; struct usb_gadget_driver *driver; diff --git a/drivers/usb/gadget/usbstring.c b/drivers/usb/gadget/usbstring.c index 201ff9eafd..58eb28ad1a 100644 --- a/drivers/usb/gadget/usbstring.c +++ b/drivers/usb/gadget/usbstring.c @@ -1,17 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2003 David Brownell - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. */ #include <common.h> #include <errno.h> -#include <usb/ch9.h> -#include <usb/gadget.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> static inline void put_unaligned_le16(u16 val, u8 *p) { @@ -101,7 +97,7 @@ fail: * characters (which are also widely used in C strings). */ int -usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf) +usb_gadget_get_string (const struct usb_gadget_strings *table, int id, u8 *buf) { struct usb_string *s; int len; diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index d2029bc7d7..d38b4dcac4 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config USB_EHCI bool "EHCI driver" depends on HAS_DMA @@ -32,11 +33,3 @@ config USB_XHCI This driver currently only supports virtual USB 2.0 ports, if you plan to use USB 3.0 devices, use a USB 2.0 cable in between. - -config USB_XHCI_PCI - depends on PCI - depends on HAS_DMA - select USB_XHCI - bool "PCI xHCI driver" - help - Enables support for PCI attached xHCI controllers. diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 0478d34272..e55dff4580 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -1,7 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_USB_EHCI) += ehci-hcd.o 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_XHCI) += xhci-hcd.o xhci-hub.o -obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o +obj-$(CONFIG_USB_XHCI) += xhci.o xhci-mem.o xhci-ring.o diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c index 6c88d646c9..f176babfa7 100644 --- a/drivers/usb/host/ehci-atmel.c +++ b/drivers/usb/host/ehci-atmel.c @@ -1,19 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * (C) Copyright 2010 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; version 2 of - * the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <common.h> @@ -21,9 +8,9 @@ #include <linux/err.h> #include <driver.h> #include <init.h> -#include <usb/usb.h> -#include <usb/usb_defs.h> -#include <usb/ehci.h> +#include <linux/usb/usb.h> +#include <linux/usb/usb_defs.h> +#include <linux/usb/ehci.h> #include <errno.h> #include <io.h> @@ -31,7 +18,7 @@ struct atmel_ehci_priv { struct ehci_host *ehci; - struct device_d *dev; + struct device *dev; struct clk *iclk; struct clk *uclk; }; @@ -60,14 +47,7 @@ static void atmel_stop_clock(struct atmel_ehci_priv *atehci) clk_disable(atehci->uclk); } -static int atmel_ehci_detect(struct device_d *dev) -{ - struct atmel_ehci_priv *atehci = dev->priv; - - return ehci_detect(atehci->ehci); -} - -static int atmel_ehci_probe(struct device_d *dev) +static int atmel_ehci_probe(struct device *dev) { int ret; struct resource *iores; @@ -76,12 +56,11 @@ static int atmel_ehci_probe(struct device_d *dev) const char *uclk_name; struct ehci_host *ehci; - uclk_name = (dev->device_node) ? "usb_clk" : "uhpck"; + uclk_name = (dev->of_node) ? "usb_clk" : "uhpck"; atehci = xzalloc(sizeof(*atehci)); atehci->dev = dev; dev->priv = atehci; - dev->detect = atmel_ehci_detect; atehci->iclk = clk_get(dev, "ehci_clk"); if (IS_ERR(atehci->iclk)) { @@ -118,7 +97,7 @@ static int atmel_ehci_probe(struct device_d *dev) return 0; } -static void atmel_ehci_remove(struct device_d *dev) +static void atmel_ehci_remove(struct device *dev) { struct atmel_ehci_priv *atehci = dev->priv; @@ -134,8 +113,9 @@ static const struct of_device_id atmel_ehci_dt_ids[] = { { .compatible = "atmel,at91sam9g45-ehci" }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, atmel_ehci_dt_ids); -static struct driver_d atmel_ehci_driver = { +static struct driver atmel_ehci_driver = { .name = "atmel-ehci", .probe = atmel_ehci_probe, .remove = atmel_ehci_remove, diff --git a/drivers/usb/host/ehci-core.h b/drivers/usb/host/ehci-core.h index 584dd541ad..8f43783f7e 100644 --- a/drivers/usb/host/ehci-core.h +++ b/drivers/usb/host/ehci-core.h @@ -1,18 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /*- * Copyright (c) 2007-2008, Juniper Networks, Inc. * Copyright (c) 2008, Excito Elektronik i Skåne AB * All rights reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2 of - * the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #ifndef USB_EHCI_CORE_H diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index f3be177ceb..7ae3a285a0 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1,26 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0-only /*- * Copyright (c) 2007-2008, Juniper Networks, Inc. * Copyright (c) 2008, Excito Elektronik i Skåne AB * Copyright (c) 2008, Michael Trimarchi <trimarchimichael@yahoo.it> * * All rights reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2 of - * the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ /*#define DEBUG */ #include <common.h> #include <dma.h> #include <asm/byteorder.h> -#include <usb/usb.h> +#include <linux/usb/usb.h> #include <io.h> #include <malloc.h> #include <driver.h> @@ -29,20 +19,24 @@ #include <clock.h> #include <errno.h> #include <of.h> -#include <usb/ehci.h> +#include <linux/usb/ehci.h> #include <linux/err.h> #include <linux/sizes.h> +#include <linux/clk.h> +#include <linux/phy/phy.h> #include "ehci.h" struct ehci_host { int rootdev; - struct device_d *dev; + struct device *dev; struct ehci_hccr *hccr; struct ehci_hcor *hcor; struct usb_host host; struct QH *qh_list; + dma_addr_t qh_list_dma; struct qTD *td; + dma_addr_t td_dma; int portreset; unsigned long flags; @@ -51,7 +45,9 @@ struct ehci_host { void *drvdata; int periodic_schedules; struct QH *periodic_queue; + dma_addr_t periodic_queue_dma; uint32_t *periodic_list; + dma_addr_t periodic_list_dma; }; struct int_queue { @@ -59,9 +55,11 @@ struct int_queue { int queuesize; unsigned long pipe; struct QH *first; + dma_addr_t first_dma; struct QH *current; struct QH *last; struct qTD *tds; + dma_addr_t tds_dma; }; #define to_ehci(ptr) container_of(ptr, struct ehci_host, host) @@ -137,6 +135,38 @@ static struct descriptor { #define ehci_is_TDI() (ehci->flags & EHCI_HAS_TT) +#define EHCI_DMA(dma0, ptr0, ptr) \ + ((dma0) + ((ptr) - (ptr0)) * sizeof(*(ptr))) + +static inline uint32_t ehci_qh_dma(struct ehci_host *ehci, struct QH *qh) +{ + return EHCI_DMA(ehci->qh_list_dma, ehci->qh_list, qh); +} + +static inline uint32_t ehci_td_dma(struct ehci_host *ehci, struct qTD *td) +{ + return EHCI_DMA(ehci->td_dma, ehci->td, td); +} + +static inline uint32_t ehci_int_qh_dma(struct int_queue *intq, struct QH *qh) +{ + return EHCI_DMA(intq->first_dma, intq->first, qh); +} + +static inline uint32_t ehci_int_td_dma(struct int_queue *intq, struct qTD *td) +{ + return EHCI_DMA(intq->tds_dma, intq->tds, td); +} + +static void memzero32(void *ptr, size_t size) +{ + uint32_t *ptr32 = ptr; + int i; + + for (i = 0; i < size / sizeof(uint32_t); i++) + ptr32[i] = 0x0; +} + static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec) { uint32_t result; @@ -202,7 +232,7 @@ static int ehci_td_buffer(struct qTD *td, dma_addr_t addr, size_t sz) } if (idx == buffer_count) { - pr_debug("out of buffer pointers (%u bytes left)\n", sz); + pr_debug("out of buffer pointers (%zu bytes left)\n", sz); return -ENOMEM; } @@ -212,7 +242,7 @@ static int ehci_td_buffer(struct qTD *td, dma_addr_t addr, size_t sz) return 0; } -static int ehci_prepare_qtd(struct device_d *dev, +static int ehci_prepare_qtd(struct device *dev, struct qTD *td, uint32_t token, void *buffer, size_t length, dma_addr_t *buffer_dma, @@ -237,7 +267,7 @@ static int ehci_prepare_qtd(struct device_d *dev, if (ret) return ret; } else { - memset(td->qt_buffer, 0, sizeof(td->qt_buffer)); + memzero32(td->qt_buffer, sizeof(td->qt_buffer)); } return 0; @@ -267,15 +297,17 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, struct usb_host *host = dev->host; struct ehci_host *ehci = to_ehci(host); const bool dir_in = usb_pipein(pipe); - dma_addr_t buffer_dma, req_dma; + dma_addr_t buffer_dma = DMA_ERROR_CODE, req_dma; struct QH *qh = &ehci->qh_list[1]; struct qTD *td; + volatile struct qTD *vtd; uint32_t *tdp; uint32_t endpt, token, usbsts; uint32_t status; uint32_t toggle; bool c; int ret; + uint64_t start, timeout_val; dev_dbg(ehci->dev, "pipe=%lx, buffer=%p, length=%d, req=%p\n", pipe, @@ -318,7 +350,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, qh->qh_endpt2 = cpu_to_hc32(endpt); qh->qh_curtd = 0; qh->qt_token = 0; - memset(qh->qt_buffer, 0, sizeof(qh->qt_buffer)); + memzero32(qh->qt_buffer, sizeof(qh->qt_buffer)); tdp = &qh->qt_next; @@ -337,7 +369,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, dev_dbg(ehci->dev, "unable construct SETUP td\n"); return ret; } - *tdp = cpu_to_hc32((uint32_t) td); + *tdp = cpu_to_hc32(ehci_td_dma(ehci, td)); tdp = &td->qt_next; toggle = 1; @@ -376,7 +408,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, dev_err(ehci->dev, "unable construct DATA td\n"); return ret; } - *tdp = cpu_to_hc32((uint32_t) td); + *tdp = cpu_to_hc32(ehci_td_dma(ehci, td)); tdp = &td->qt_next; } @@ -390,7 +422,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, QT_TOKEN_PID_IN), NULL, 0, NULL, DMA_NONE); - *tdp = cpu_to_hc32((uint32_t)td); + *tdp = cpu_to_hc32(ehci_td_dma(ehci, td)); tdp = &td->qt_next; } @@ -404,13 +436,18 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, return ret; } - ret = handshake(&ehci->hcor->or_usbsts, STS_USBINT, STS_USBINT, - timeout_ms * 1000); - if (ret < 0) { - ehci_enable_async_schedule(ehci, false); - ehci_writel(&qh->qt_token, 0); - return -ETIMEDOUT; - } + /* Wait for TDs to be processed. */ + timeout_val = timeout_ms * MSECOND; + start = get_time_ns(); + vtd = td; + do { + token = hc32_to_cpu(vtd->qt_token); + if (is_timeout_non_interruptible(start, timeout_val)) { + ehci_enable_async_schedule(ehci, false); + ehci_writel(&qh->qt_token, 0); + return -ETIMEDOUT; + } + } while (token & QT_TOKEN_STATUS_ACTIVE); if (req) dma_unmap_single(ehci->dev, req_dma, sizeof(*req), @@ -473,7 +510,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, } #if defined(CONFIG_MACH_EFIKA_MX_SMARTBOOK) && defined(CONFIG_USB_ULPI) -#include <usb/ulpi.h> +#include <linux/usb/ulpi.h> /* * Add support for setting CHRGVBUS to workaround a hardware bug on efika mx/sb * boards. @@ -832,7 +869,7 @@ static int ehci_init(struct usb_host *host) return ret; } - ehci->qh_list[0].qh_link = cpu_to_hc32((uint32_t)&ehci->qh_list[1] | + ehci->qh_list[0].qh_link = cpu_to_hc32(ehci_qh_dma(ehci, &ehci->qh_list[1]) | QH_LINK_TYPE_QH); ehci->qh_list[0].qh_endpt1 = cpu_to_hc32(QH_ENDPT1_H(1) | QH_ENDPT1_EPS(USB_SPEED_HIGH)); @@ -841,12 +878,13 @@ static int ehci_init(struct usb_host *host) ehci->qh_list[0].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); ehci->qh_list[0].qt_token = cpu_to_hc32(QT_TOKEN_STATUS_HALTED); - ehci->qh_list[1].qh_link = cpu_to_hc32((uint32_t)&ehci->qh_list[0] | + ehci->qh_list[1].qh_link = cpu_to_hc32(ehci_qh_dma(ehci, + &ehci->qh_list[0]) | QH_LINK_TYPE_QH); ehci->qh_list[1].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); /* Set async. queue head pointer. */ - ehci_writel(&ehci->hcor->or_asynclistaddr, (uint32_t)ehci->qh_list); + ehci_writel(&ehci->hcor->or_asynclistaddr, (uint32_t)ehci->qh_list_dma); /* * Set up periodic list @@ -854,7 +892,7 @@ static int ehci_init(struct usb_host *host) */ ehci->periodic_schedules = 0; periodic = ehci->periodic_queue; - memset(periodic, 0, sizeof(*periodic)); + memzero32(periodic, sizeof(*periodic)); periodic->qh_link = cpu_to_hc32(QH_LINK_TERMINATE); periodic->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); periodic->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); @@ -876,15 +914,15 @@ static int ehci_init(struct usb_host *host) * PAGE_SIZE less then 4k will break this code. */ ehci->periodic_list = dma_alloc_coherent(1024 * 4, - DMA_ADDRESS_BROKEN); + &ehci->periodic_list_dma); for (i = 0; i < 1024; i++) { - ehci->periodic_list[i] = cpu_to_hc32((unsigned long)periodic + ehci->periodic_list[i] = cpu_to_hc32((unsigned long)ehci->periodic_queue_dma | QH_LINK_TYPE_QH); } /* Set periodic list base address */ ehci_writel(&ehci->hcor->or_periodiclistbase, - (unsigned long)ehci->periodic_list); + (uint32_t)ehci->periodic_list_dma); reg = ehci_readl(&ehci->hccr->cr_hcsparams); descriptor.hub.bNbrPorts = HCS_N_PORTS(reg); @@ -977,7 +1015,10 @@ disable_periodic(struct ehci_host *ehci) return 0; } -#define NEXT_QH(qh) (struct QH *)((unsigned long)hc32_to_cpu((qh)->qh_link) & ~0x1f) +#define NEXT_QH(queue, qh) (struct QH *)( \ + ((unsigned long)hc32_to_cpu((qh)->qh_link) & ~0x1f) - \ + (queue)->first_dma + \ + (unsigned long)(queue)->first) static int enable_periodic(struct ehci_host *ehci) @@ -1042,7 +1083,7 @@ static void ehci_update_endpt2_dev_n_port(struct usb_device *udev, static struct int_queue *ehci_create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, int elementsize, - void *buffer, int interval) + void *buffer, dma_addr_t buffer_dma, int interval) { struct usb_host *host = dev->host; struct ehci_host *ehci = to_ehci(host); @@ -1091,22 +1132,23 @@ static struct int_queue *ehci_create_int_queue(struct usb_device *dev, result->queuesize = queuesize; result->pipe = pipe; result->first = dma_alloc_coherent(sizeof(struct QH) * queuesize, - DMA_ADDRESS_BROKEN); + &result->first_dma); result->current = result->first; result->last = result->first + queuesize - 1; result->tds = dma_alloc_coherent(sizeof(struct qTD) * queuesize, - DMA_ADDRESS_BROKEN); + &result->tds_dma); for (i = 0; i < queuesize; i++) { struct QH *qh = result->first + i; struct qTD *td = result->tds + i; void **buf = &qh->buffer; - qh->qh_link = cpu_to_hc32((unsigned long)(qh+1) | QH_LINK_TYPE_QH); + qh->qh_link = cpu_to_hc32(ehci_int_qh_dma(result, qh + 1) | + QH_LINK_TYPE_QH); if (i == queuesize - 1) qh->qh_link = cpu_to_hc32(QH_LINK_TERMINATE); - qh->qt_next = cpu_to_hc32((unsigned long)td); + qh->qt_next = cpu_to_hc32(ehci_int_td_dma(result, td)); qh->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); qh->qh_endpt1 = cpu_to_hc32((0 << 28) | /* No NAK reload (ehci 4.9) */ @@ -1133,15 +1175,15 @@ static struct int_queue *ehci_create_int_queue(struct usb_device *dev, ((usb_pipein(pipe) ? 1 : 0) << 8) | /* IN/OUT token */ 0x80); /* active */ td->qt_buffer[0] = - cpu_to_hc32((unsigned long)buffer + i * elementsize); + cpu_to_hc32(buffer_dma + i * elementsize); td->qt_buffer[1] = - cpu_to_hc32((td->qt_buffer[0] + 0x1000) & ~0xfff); + cpu_to_hc32((buffer_dma + i * elementsize + 0x1000) & ~0xfff); td->qt_buffer[2] = - cpu_to_hc32((td->qt_buffer[0] + 0x2000) & ~0xfff); + cpu_to_hc32((buffer_dma + i * elementsize + 0x2000) & ~0xfff); td->qt_buffer[3] = - cpu_to_hc32((td->qt_buffer[0] + 0x3000) & ~0xfff); + cpu_to_hc32((buffer_dma + i * elementsize + 0x3000) & ~0xfff); td->qt_buffer[4] = - cpu_to_hc32((td->qt_buffer[0] + 0x4000) & ~0xfff); + cpu_to_hc32((buffer_dma + i * elementsize + 0x4000) & ~0xfff); *buf = buffer + i * elementsize; } @@ -1156,7 +1198,7 @@ static struct int_queue *ehci_create_int_queue(struct usb_device *dev, /* hook up to periodic list */ result->last->qh_link = list->qh_link; - list->qh_link = cpu_to_hc32((unsigned long)result->first | QH_LINK_TYPE_QH); + list->qh_link = cpu_to_hc32(result->first_dma | QH_LINK_TYPE_QH); if (enable_periodic(ehci) < 0) { dev_err(&dev->dev, @@ -1168,8 +1210,10 @@ static struct int_queue *ehci_create_int_queue(struct usb_device *dev, dev_dbg(&dev->dev, "Exit create_int_queue\n"); return result; fail3: - dma_free_coherent(result->tds, 0, sizeof(struct qTD) * queuesize); - dma_free_coherent(result->first, 0, sizeof(struct QH) * queuesize); + dma_free_coherent(result->tds, result->tds_dma, + sizeof(struct qTD) * queuesize); + dma_free_coherent(result->first, result->first_dma, + sizeof(struct QH) * queuesize); free(result); return NULL; } @@ -1226,14 +1270,14 @@ static int ehci_destroy_int_queue(struct usb_device *dev, dev_dbg(&dev->dev, "considering %p, with qh_link %x\n", cur, cur->qh_link); - if (NEXT_QH(cur) == queue->first) { + if (NEXT_QH(queue, cur) == queue->first) { dev_dbg(&dev->dev, "found candidate. removing from chain\n"); cur->qh_link = queue->last->qh_link; result = 0; break; } - cur = NEXT_QH(cur); + cur = NEXT_QH(queue, cur); } if (ehci->periodic_schedules > 0) { @@ -1260,14 +1304,14 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, uint64_t start; void *backbuffer; int result = 0, ret; + dma_addr_t buffer_dma; dev_dbg(ehci->dev, "dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d", dev, pipe, buffer, length, interval); - dma_sync_single_for_device((unsigned long)buffer, length, - DMA_BIDIRECTIONAL); + buffer_dma = dma_map_single(ehci->dev, buffer, length, DMA_BIDIRECTIONAL); - queue = ehci_create_int_queue(dev, pipe, 1, length, buffer, interval); + queue = ehci_create_int_queue(dev, pipe, 1, length, buffer, buffer_dma, interval); if (!queue) return -EINVAL; @@ -1289,8 +1333,7 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, result = -EINVAL; } - dma_sync_single_for_cpu((unsigned long)buffer, length, - DMA_BIDIRECTIONAL); + dma_unmap_single(ehci->dev, buffer_dma, length, DMA_BIDIRECTIONAL); ret = ehci_destroy_int_queue(dev, queue); if (!result) @@ -1298,12 +1341,7 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, return result; } -int ehci_detect(struct ehci_host *ehci) -{ - return usb_host_detect(&ehci->host); -} - -struct ehci_host *ehci_register(struct device_d *dev, struct ehci_data *data) +struct ehci_host *ehci_register(struct device *dev, struct ehci_data *data) { struct usb_host *host; struct ehci_host *ehci; @@ -1326,11 +1364,11 @@ struct ehci_host *ehci_register(struct device_d *dev, struct ehci_data *data) ehci->post_init = data->post_init; ehci->qh_list = dma_alloc_coherent(sizeof(struct QH) * NUM_QH, - DMA_ADDRESS_BROKEN); + &ehci->qh_list_dma); ehci->periodic_queue = dma_alloc_coherent(sizeof(struct QH), - DMA_ADDRESS_BROKEN); + &ehci->periodic_queue_dma); ehci->td = dma_alloc_coherent(sizeof(struct qTD) * NUM_TD, - DMA_ADDRESS_BROKEN); + &ehci->td_dma); host->hw_dev = dev; host->init = ehci_init; @@ -1360,20 +1398,16 @@ void ehci_unregister(struct ehci_host *ehci) free(ehci); } -static int ehci_dev_detect(struct device_d *dev) -{ - struct ehci_host *ehci = dev->priv; - - return ehci_detect(ehci); -} - -static int ehci_probe(struct device_d *dev) +static int ehci_probe(struct device *dev) { struct resource *iores; struct ehci_data data = {}; struct ehci_platform_data *pdata = dev->platform_data; - struct device_node *dn = dev->device_node; + struct device_node *dn = dev->of_node; struct ehci_host *ehci; + struct clk_bulk_data *clks; + int num_clocks, ret; + struct phy *usb2_generic_phy; if (pdata) data.flags = pdata->flags; @@ -1387,6 +1421,27 @@ static int ehci_probe(struct device_d *dev) */ data.flags = EHCI_HAS_TT; + usb2_generic_phy = phy_optional_get(dev, "usb"); + if (IS_ERR(usb2_generic_phy)) + return PTR_ERR(usb2_generic_phy); + + ret = phy_init(usb2_generic_phy); + if (ret) + return ret; + + ret = phy_power_on(usb2_generic_phy); + if (ret) + return ret; + + ret = clk_bulk_get_all(dev, &clks); + if (ret < 0) + return ret; + + num_clocks = ret; + ret = clk_bulk_enable(num_clocks, clks); + if (ret) + return ret; + iores = dev_request_mem_resource(dev, 0); if (IS_ERR(iores)) return PTR_ERR(iores); @@ -1406,12 +1461,11 @@ static int ehci_probe(struct device_d *dev) return PTR_ERR(ehci); dev->priv = ehci; - dev->detect = ehci_dev_detect; return 0; } -static void ehci_remove(struct device_d *dev) +static void ehci_remove(struct device *dev) { struct ehci_host *ehci = dev->priv; @@ -1425,8 +1479,9 @@ static __maybe_unused struct of_device_id ehci_platform_dt_ids[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, ehci_platform_dt_ids); -static struct driver_d ehci_driver = { +static struct driver ehci_driver = { .name = "ehci", .probe = ehci_probe, .remove = ehci_remove, diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index fc5a54f671..e364796a1a 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -1,8 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2010 Michael Grzeschik <mgr@pengutronix.de> - * - * This file is released under the GPLv2 - * */ /* @@ -12,16 +10,16 @@ /*-------------------------------------------------------------------------*/ #include <mfd/twl4030.h> -#include <usb/twl4030.h> -#include <mach/ehci.h> +#include <linux/usb/twl4030.h> +#include <mach/omap/ehci.h> #include <common.h> #include <io.h> #include <clock.h> #include <gpio.h> -#include <mach/omap3-silicon.h> -#include <mach/omap3-clock.h> -#include <mach/cm-regbits-34xx.h> -#include <mach/sys_info.h> +#include <mach/omap/omap3-silicon.h> +#include <mach/omap/omap3-clock.h> +#include <mach/omap/cm-regbits-34xx.h> +#include <mach/omap/sys_info.h> void omap_usb_utmi_init(struct omap_hcd *omap, u8 tll_channel_mask) { diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index e88e37e14c..49196bfc7d 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -1,18 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /*- * Copyright (c) 2007-2008, Juniper Networks, Inc. * Copyright (c) 2008, Michael Trimarchi <trimarchimichael@yahoo.it> * All rights reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2 of - * the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #ifndef USB_EHCI_H @@ -53,7 +43,6 @@ struct ehci_hcor { #define STD_ASS (1 << 15) #define STS_PSS (1 << 14) #define STS_HALT (1 << 12) -#define STS_USBINT BIT(0) uint32_t or_usbintr; uint32_t or_frindex; uint32_t or_ctrldssegment; diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 1013ba39c5..867c0977be 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -1,33 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * (C) Copyright 2010 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; version 2 of - * the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <common.h> #include <linux/clk.h> #include <driver.h> #include <init.h> -#include <usb/usb.h> -#include <usb/usb_defs.h> +#include <linux/usb/usb.h> +#include <linux/usb/usb_defs.h> #include <errno.h> #include <gpio.h> #include <of_gpio.h> #include <io.h> -#include <mach/board.h> +#include <mach/at91/board.h> #include "ohci.h" @@ -36,7 +23,7 @@ struct ohci_at91_priv { - struct device_d *dev; + struct device *dev; struct clk *iclk; struct clk *fclk; struct ohci_regs __iomem *regs; @@ -67,13 +54,13 @@ static void at91_stop_clock(struct ohci_at91_priv *ohci_at91) clk_disable(ohci_at91->iclk); } -static int at91_ohci_probe_dt(struct device_d *dev) +static int at91_ohci_probe_dt(struct device *dev) { u32 ports; int i, ret, gpio; enum of_gpio_flags flags; struct at91_usbh_data *pdata; - struct device_node *np = dev->device_node; + struct device_node *np = dev->of_node; pdata = xzalloc(sizeof(*pdata)); dev->platform_data = pdata; @@ -120,7 +107,7 @@ static int at91_ohci_probe_dt(struct device_d *dev) return 0; } -static int at91_ohci_probe(struct device_d *dev) +static int at91_ohci_probe(struct device *dev) { int ret; struct resource *io; @@ -129,7 +116,7 @@ static int at91_ohci_probe(struct device_d *dev) dev->priv = ohci_at91; ohci_at91->dev = dev; - if (dev->device_node) { + if (dev->of_node) { ret = at91_ohci_probe_dt(dev); if (ret < 0) return ret; @@ -172,7 +159,7 @@ static int at91_ohci_probe(struct device_d *dev) return 0; } -static void at91_ohci_remove(struct device_d *dev) +static void at91_ohci_remove(struct device *dev) { struct at91_usbh_data *pdata = dev->platform_data; struct ohci_at91_priv *ohci_at91 = dev->priv; @@ -207,8 +194,9 @@ static const struct of_device_id at91_ohci_dt_ids[] = { { .compatible = "atmel,at91rm9200-ohci" }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, at91_ohci_dt_ids); -static struct driver_d at91_ohci_driver = { +static struct driver at91_ohci_driver = { .name = "at91_ohci", .probe = at91_ohci_probe, .remove = at91_ohci_remove, diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 924dc8e1f6..ae4c34e818 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * URB OHCI HCD (Host Controller Driver) for USB on the AT91RM9200 and PCI bus. * @@ -16,21 +17,6 @@ * * Modified for the MP2USB by (C) Copyright 2005 Eric Benard * ebenard@eukrea.com - based on s3c24x0's driver - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * */ /* * IMPORTANT NOTES @@ -41,12 +27,11 @@ * to activate workaround for bug #41 or this driver will NOT work! */ #include <common.h> -#include <dma.h> #include <clock.h> #include <dma.h> #include <malloc.h> -#include <usb/usb.h> -#include <usb/usb_defs.h> +#include <linux/usb/usb.h> +#include <linux/usb/usb_defs.h> #include <init.h> #include <errno.h> #include <linux/err.h> @@ -857,7 +842,8 @@ static void td_fill(struct ohci *ohci, unsigned int info, td->hwNextTD = virt_to_phys((void *)m32_swap((unsigned long)td_pt)); - dma_sync_single_for_device((unsigned long)data, len, DMA_BIDIRECTIONAL); + dma_sync_single_for_device(ohci->host.hw_dev, (unsigned long)data, + len, DMA_BIDIRECTIONAL); /* append to queue */ td->ed->hwTailP = td->hwNextTD; @@ -1093,7 +1079,7 @@ static int dl_done_list(struct ohci *ohci) unsigned long ptdphys = virt_to_phys(ptd); struct td *td_list; - dma_sync_single_for_device((unsigned long)ptdphys, + dma_sync_single_for_device(ohci->host.hw_dev, (unsigned long)ptdphys, sizeof(struct td) * NUM_TD, DMA_BIDIRECTIONAL); td_list = dl_reverse_done_list(ohci); @@ -1530,7 +1516,7 @@ static int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *b dev->status = stat; dev->act_len = urb->actual_length; - dma_sync_single_for_cpu((unsigned long)buffer, transfer_len, + dma_sync_single_for_cpu(host->hw_dev, (unsigned long)buffer, transfer_len, DMA_BIDIRECTIONAL); pkt_print(urb, dev, pipe, buffer, transfer_len, @@ -1790,7 +1776,7 @@ static int ohci_init(struct usb_host *host) return 0; } -static int ohci_probe(struct device_d *dev) +static int ohci_probe(struct device *dev) { struct resource *iores; struct usb_host *host; @@ -1825,7 +1811,7 @@ static int ohci_probe(struct device_d *dev) return 0; } -static struct driver_d ohci_driver = { +static struct driver ohci_driver = { .name = "ohci", .probe = ohci_probe, }; diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 9c9b8375ce..7d70bcb719 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * URB OHCI HCD (Host Controller Driver) for USB. * diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c deleted file mode 100644 index 32a6ccd5cd..0000000000 --- a/drivers/usb/host/xhci-hcd.c +++ /dev/null @@ -1,1675 +0,0 @@ -/* - * xHCI HCD driver - * - * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> - * - * Some code borrowed from the Linux xHCI driver - * Author: Sarah Sharp - * Copyright (C) 2008 Intel Corp. - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ -//#define DEBUG -#include <clock.h> -#include <common.h> -#include <dma.h> -#include <init.h> -#include <io.h> -#include <linux/err.h> -#include <usb/usb.h> -#include <usb/xhci.h> - -#include "xhci.h" - - -static struct xhci_input_control_ctx * -xhci_get_input_control_ctx(struct xhci_container_ctx *ctx) -{ - if (ctx->type != XHCI_CTX_TYPE_INPUT) - return NULL; - - return (struct xhci_input_control_ctx *)ctx->bytes; -} - -static struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_hcd *xhci, - struct xhci_container_ctx *ctx) -{ - if (ctx->type == XHCI_CTX_TYPE_DEVICE) - return (struct xhci_slot_ctx *)ctx->bytes; - - return (struct xhci_slot_ctx *) - (ctx->bytes + HCC_CTX_SIZE(xhci->hcc_params)); -} - -static struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_hcd *xhci, - struct xhci_container_ctx *ctx, - unsigned int ep_index) -{ - /* increment ep index by offset of start of ep ctx array */ - ep_index++; - if (ctx->type == XHCI_CTX_TYPE_INPUT) - ep_index++; - - return (struct xhci_ep_ctx *) - (ctx->bytes + (ep_index * HCC_CTX_SIZE(xhci->hcc_params))); -} - -/* - * xHCI ring handling - */ - -static int xhci_ring_is_last_trb(struct xhci_ring *ring, union xhci_trb *trb) -{ - if (ring->type == TYPE_EVENT) - return trb == &ring->trbs[NUM_EVENT_TRBS]; - else - return TRB_TYPE_LINK(le32_to_cpu(trb->link.control)); -} - -static int xhci_ring_increment(struct xhci_ring *ring, bool enqueue) -{ - union xhci_trb **queue = (enqueue) ? &ring->enqueue : &ring->dequeue; - - (*queue)++; - - if (!xhci_ring_is_last_trb(ring, *queue)) - return 0; - - if (ring->type == TYPE_EVENT) { - *queue = &ring->trbs[0]; - ring->cycle_state ^= 1; - } else { - u32 ctrl = le32_to_cpu((*queue)->link.control); - void *p = (void *)(dma_addr_t) - le64_to_cpu((*queue)->link.segment_ptr); - - ctrl = (ctrl & ~TRB_CYCLE) | ring->cycle_state; - (*queue)->link.control = cpu_to_le32(ctrl); - - if (enqueue) - ring->enqueue = p; - else - ring->dequeue = p; - - if (ctrl & LINK_TOGGLE) - ring->cycle_state ^= 1; - } - - return 0; -} - -static int xhci_ring_issue_trb(struct xhci_ring *ring, union xhci_trb *trb) -{ - union xhci_trb *enq = ring->enqueue; - int i; - - /* Pass TRB to hardware */ - trb->generic.field[3] &= ~TRB_CYCLE; - trb->generic.field[3] |= ring->cycle_state; - for (i = 0; i < 4; i++) - enq->generic.field[i] = cpu_to_le32(trb->generic.field[i]); - - xhci_ring_increment(ring, 1); - - return 0; -} - -static void xhci_ring_init(struct xhci_ring *ring, int num_trbs, - enum xhci_ring_type type) -{ - ring->type = type; - ring->cycle_state = 1; - ring->num_trbs = num_trbs; - ring->enqueue = ring->dequeue = &ring->trbs[0]; - - /* Event ring is not linked */ - if (type == TYPE_EVENT) - return; - - ring->trbs[num_trbs-1].link.segment_ptr = - cpu_to_le64((dma_addr_t)&ring->trbs[0]); - ring->trbs[num_trbs-1].link.control = - cpu_to_le32(TRB_TYPE(TRB_LINK) | LINK_TOGGLE); -} - -static struct xhci_ring *xhci_get_endpoint_ring(struct xhci_hcd *xhci) -{ - struct xhci_ring *ring; - - if (list_empty(&xhci->rings_list)) { - dev_err(xhci->dev, "no more endpoint rings available\n"); - return NULL; - } - - ring = list_last_entry(&xhci->rings_list, struct xhci_ring, list); - list_del_init(&ring->list); - - return ring; -} - -static void xhci_put_endpoint_ring(struct xhci_hcd *xhci, struct xhci_ring *ring) -{ - if (!ring) - return; - - memset(ring->trbs, 0, ring->num_trbs * sizeof(union xhci_trb)); - list_add_tail(&ring->list, &xhci->rings_list); -} - -/* - * xhci_get_endpoint_index - Used for passing endpoint bitmasks between the - * core and HCDs. Find the index for an endpoint given its descriptor. - * Use the return value to right shift 1 for the bitmask. - * - * Index = (epnum * 2) + direction - 1, - * where direction = 0 for OUT, 1 for IN. - * For control endpoints, the IN index is used (OUT index is unused), so - * index = (epnum * 2) + direction - 1 = (epnum * 2) + 1 - 1 = (epnum * 2) - */ -static unsigned int xhci_get_endpoint_index(u8 epaddress, u8 epattributes) -{ - u8 epnum = epaddress & USB_ENDPOINT_NUMBER_MASK; - u8 xfer = epattributes & USB_ENDPOINT_XFERTYPE_MASK; - unsigned int index; - - if (xfer == USB_ENDPOINT_XFER_CONTROL) - index = (unsigned int)(epnum * 2); - else - index = (unsigned int)(epnum * 2) + - ((epaddress & USB_DIR_IN) ? 1 : 0) - 1; - - return index; -} - -static u8 xhci_get_endpoint_type(u8 epaddress, u8 epattributes) -{ - int in = epaddress & USB_ENDPOINT_DIR_MASK; - u8 xfer = epattributes & USB_ENDPOINT_XFERTYPE_MASK; - u8 type; - - switch (xfer) { - case USB_ENDPOINT_XFER_CONTROL: - type = CTRL_EP; - break; - case USB_ENDPOINT_XFER_ISOC: - type = (in) ? ISOC_IN_EP : ISOC_OUT_EP; - break; - case USB_ENDPOINT_XFER_BULK: - type = (in) ? BULK_IN_EP : BULK_OUT_EP; - break; - case USB_ENDPOINT_XFER_INT: - type = (in) ? INT_IN_EP : INT_OUT_EP; - break; - } - - return type; -} - -/* - * Convert interval expressed as 2^(bInterval - 1) == interval into - * straight exponent value 2^n == interval. - * - */ -static u32 xhci_parse_exponent_interval(struct usb_device *udev, - struct usb_endpoint_descriptor *ep) -{ - u32 interval; - - interval = clamp_val(ep->bInterval, 1, 16) - 1; - /* - * Full speed isoc endpoints specify interval in frames, - * not microframes. We are using microframes everywhere, - * so adjust accordingly. - */ - if (udev->speed == USB_SPEED_FULL) - interval += 3; /* 1 frame = 2^3 uframes */ - - return interval; -} - -/* - * Convert bInterval expressed in microframes (in 1-255 range) to exponent of - * microframes, rounded down to nearest power of 2. - */ -static u32 xhci_microframes_to_exponent(struct usb_device *udev, - struct usb_endpoint_descriptor *ep, u32 desc_interval, - u32 min_exponent, u32 max_exponent) -{ - u32 interval; - - interval = fls(desc_interval) - 1; - return clamp_val(interval, min_exponent, max_exponent); -} - -static inline u32 xhci_parse_microframe_interval(struct usb_device *udev, - struct usb_endpoint_descriptor *ep) -{ - if (ep->bInterval == 0) - return 0; - return xhci_microframes_to_exponent(udev, ep, ep->bInterval, 0, 15); -} - - -static inline u32 xhci_parse_frame_interval(struct usb_device *udev, - struct usb_endpoint_descriptor *ep) -{ - return xhci_microframes_to_exponent(udev, ep, ep->bInterval * 8, 3, 10); -} - -static u32 xhci_get_endpoint_interval(struct usb_device *udev, - struct usb_endpoint_descriptor *ep) -{ - u8 type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; - u32 interval = 0; - - switch (udev->speed) { - case USB_SPEED_HIGH: - /* Max NAK rate */ - if (type == USB_ENDPOINT_XFER_CONTROL || - type == USB_ENDPOINT_XFER_BULK) { - interval = xhci_parse_microframe_interval(udev, ep); - break; - } - /* Fall through - SS and HS isoc/int have same decoding */ - case USB_SPEED_SUPER: - if (type == USB_ENDPOINT_XFER_ISOC || - type == USB_ENDPOINT_XFER_INT) - interval = xhci_parse_exponent_interval(udev, ep); - break; - case USB_SPEED_FULL: - if (type == USB_ENDPOINT_XFER_ISOC) { - interval = xhci_parse_exponent_interval(udev, ep); - break; - } - /* - * Fall through for interrupt endpoint interval decoding - * since it uses the same rules as low speed interrupt - * endpoints. - */ - case USB_SPEED_LOW: - if (type == USB_ENDPOINT_XFER_ISOC || - type == USB_ENDPOINT_XFER_INT) - interval = xhci_parse_frame_interval(udev, ep); - break; - } - - return interval; -} - -/* The "Mult" field in the endpoint context is only set for SuperSpeed isoc eps. - * High speed endpoint descriptors can define "the number of additional - * transaction opportunities per microframe", but that goes in the Max Burst - * endpoint context field. - */ -static u32 xhci_get_endpoint_mult(struct usb_device *udev, - struct usb_endpoint_descriptor *ep) -{ - u8 type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; - - if (udev->speed != USB_SPEED_SUPER || type != USB_ENDPOINT_XFER_ISOC) - return 0; - /* FIXME: return ss_ep_comp_descriptor.bmAttributes */ - return 0; -} - -/* Return the maximum endpoint service interval time (ESIT) payload. - * Basically, this is the maxpacket size, multiplied by the burst size - * and mult size. - */ -static u32 xhci_get_max_esit_payload(struct usb_device *udev, - struct usb_endpoint_descriptor *ep) -{ - u8 type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; - int max_burst; - int max_packet; - u16 mps; - - /* Only applies for interrupt or isochronous endpoints */ - if (type != USB_ENDPOINT_XFER_INT && type != USB_ENDPOINT_XFER_ISOC) - return 0; - - /* FIXME: return ss_ep_comp_descriptor.wBytesPerInterval */ - if (udev->speed == USB_SPEED_SUPER) - return 0; - - mps = le16_to_cpu(ep->wMaxPacketSize); - max_packet = GET_MAX_PACKET(mps); - max_burst = (mps & 0x1800) >> 11; - /* A 0 in max burst means 1 transfer per ESIT */ - return max_packet * (max_burst + 1); -} - -int xhci_handshake(void __iomem *p, u32 mask, u32 done, int usec) -{ - u32 result; - u64 start; - - start = get_time_ns(); - - while (1) { - result = readl(p) & mask; - if (result == done) - return 0; - if (is_timeout(start, usec * USECOND)) - return -ETIMEDOUT; - } -} - -int xhci_issue_command(struct xhci_hcd *xhci, union xhci_trb *trb) -{ - int ret; - - ret = xhci_ring_issue_trb(&xhci->cmd_ring, trb); - if (ret) - return ret; - - /* Ring the bell */ - writel(DB_VALUE_HOST, &xhci->dba->doorbell[0]); - readl(&xhci->dba->doorbell[0]); - - return 0; -} - -static void xhci_set_event_dequeue(struct xhci_hcd *xhci) -{ - u64 reg64; - - reg64 = xhci_read_64(&xhci->ir_set->erst_dequeue); - reg64 &= ERST_PTR_MASK; - /* - * Don't clear the EHB bit (which is RW1C) because - * there might be more events to service. - */ - reg64 &= ~ERST_EHB; - reg64 |= (dma_addr_t)xhci->event_ring.dequeue & - ~(dma_addr_t)ERST_PTR_MASK; - - /* Update HC event ring dequeue pointer */ - xhci_write_64(reg64, &xhci->ir_set->erst_dequeue); -} - -int xhci_wait_for_event(struct xhci_hcd *xhci, u8 type, union xhci_trb *trb) -{ - while (true) { - union xhci_trb *deq = xhci->event_ring.dequeue; - u8 event_type; - int i, ret; - - ret = xhci_handshake(&deq->event_cmd.flags, - cpu_to_le32(TRB_CYCLE), - cpu_to_le32(xhci->event_ring.cycle_state), - XHCI_CMD_DEFAULT_TIMEOUT / USECOND); - if (ret) { - dev_err(xhci->dev, "Timeout while waiting for event\n"); - return ret; - } - - for (i = 0; i < 4; i++) - trb->generic.field[i] = - le32_to_cpu(deq->generic.field[i]); - - xhci_set_event_dequeue(xhci); - xhci_ring_increment(&xhci->event_ring, 0); - - event_type = TRB_FIELD_TO_TYPE(trb->event_cmd.flags); - - switch (event_type) { - case TRB_PORT_STATUS: - dev_dbg(xhci->dev, "Event PortStatusChange %u\n", - GET_PORT_ID(trb->generic.field[0])); - break; - case TRB_TRANSFER: - dev_dbg(xhci->dev, "Event Transfer %u\n", - GET_COMP_CODE(trb->event_cmd.status)); - ret = -GET_COMP_CODE(trb->event_cmd.status); - if (ret == -COMP_SUCCESS) - ret = 0; - break; - case TRB_COMPLETION: - dev_dbg(xhci->dev, "Event CommandCompletion %u\n", - GET_COMP_CODE(trb->event_cmd.status)); - ret = -GET_COMP_CODE(trb->event_cmd.status); - if (ret == -COMP_SUCCESS) - ret = 0; - break; - default: - dev_err(xhci->dev, "unhandled event %u (%02x) [%08x %08x %08x %08x]\n", - event_type, event_type, - trb->generic.field[0], trb->generic.field[1], - trb->generic.field[2], trb->generic.field[3]); - } - - if (event_type == type) - return ret; - } - return -ENOSYS; -} - -static struct xhci_virtual_device *xhci_find_virtdev(struct xhci_hcd *xhci, - struct usb_device *udev) -{ - struct xhci_virtual_device *vdev; - - list_for_each_entry(vdev, &xhci->vdev_list, list) - if (vdev->udev == udev) - return vdev; - - return NULL; -} - -static struct xhci_container_ctx * -xhci_alloc_container_ctx(struct xhci_hcd *xhci, int type) -{ - struct xhci_container_ctx *ctx; - - if ((type != XHCI_CTX_TYPE_DEVICE) && (type != XHCI_CTX_TYPE_INPUT)) - return NULL; - - ctx = xzalloc(sizeof(*ctx)); - ctx->type = type; - ctx->size = HCC_64BYTE_CONTEXT(xhci->hcc_params) ? 2048 : 1024; - if (type == XHCI_CTX_TYPE_INPUT) - ctx->size += HCC_CTX_SIZE(xhci->hcc_params); - - ctx->bytes = dma_alloc_coherent(ctx->size, &ctx->dma); - if (WARN_ON(!ctx->bytes)) { - kfree(ctx); - return NULL; - } - return ctx; -} - -static void xhci_free_container_ctx(struct xhci_hcd *xhci, - struct xhci_container_ctx *ctx) -{ - if (!ctx) - return; - dma_free_coherent(ctx->bytes, ctx->dma, ctx->size); - kfree(ctx); -} - -static struct xhci_virtual_device *xhci_alloc_virtdev(struct xhci_hcd *xhci, - struct usb_device *udev) -{ - struct xhci_virtual_device *vdev; - - vdev = xzalloc(sizeof(*vdev)); - vdev->udev = udev; - list_add_tail(&vdev->list, &xhci->vdev_list); - - vdev->out_ctx = xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_DEVICE); - vdev->in_ctx = xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_INPUT); - - return vdev; -} - -static void xhci_free_virtdev(struct xhci_virtual_device *vdev) -{ - struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); - int i; - - for (i = 0; i < USB_MAXENDPOINTS; i++) - if (vdev->ep[i]) - xhci_put_endpoint_ring(xhci, vdev->ep[i]); - - list_del(&vdev->list); - xhci_free_container_ctx(xhci, vdev->out_ctx); - xhci_free_container_ctx(xhci, vdev->in_ctx); - free(vdev); -} - -static int xhci_virtdev_issue_transfer(struct xhci_virtual_device *vdev, - u8 ep, union xhci_trb *trb, bool ringbell) -{ - struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); - struct xhci_ring *ring = vdev->ep[ep]; - int ret; - - ret = xhci_ring_issue_trb(ring, trb); - if (ret || !ringbell) - return ret; - - /* Ring the bell */ - writel(DB_VALUE(ep, 0), &xhci->dba->doorbell[vdev->slot_id]); - readl(&xhci->dba->doorbell[vdev->slot_id]); - - return 0; -} - -static void xhci_virtdev_zero_in_ctx(struct xhci_virtual_device *vdev) -{ - struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); - struct xhci_input_control_ctx *in_icc; - struct xhci_slot_ctx *in_slot; - struct xhci_ep_ctx *in_ep; - int i; - - in_icc = xhci_get_input_control_ctx(vdev->in_ctx); - in_slot = xhci_get_slot_ctx(xhci, vdev->in_ctx); - - /* When a device's add flag and drop flag are zero, any subsequent - * configure endpoint command will leave that endpoint's state - * untouched. Make sure we don't leave any old state in the input - * endpoint contexts. - */ - in_icc->drop_flags = 0; - in_icc->add_flags = 0; - in_slot->dev_info &= cpu_to_le32(~LAST_CTX_MASK); - /* Endpoint 0 is always valid */ - in_slot->dev_info |= cpu_to_le32(LAST_CTX(1)); - for (i = 1; i < 31; i++) { - in_ep = xhci_get_ep_ctx(xhci, vdev->in_ctx, i); - - in_ep->ep_info = 0; - in_ep->ep_info2 = 0; - in_ep->deq = 0; - in_ep->tx_info = 0; - } -} - -static void xhci_init_event_cmd_trb(union xhci_trb *trb, - u64 cmd_trb, u32 status, u32 flags) -{ - xhci_write_64(cmd_trb, &trb->event_cmd.cmd_trb); - writel(status, &trb->event_cmd.status); - writel(flags, &trb->event_cmd.flags); -} - -static int xhci_virtdev_disable_slot(struct xhci_virtual_device *vdev) -{ - struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); - union xhci_trb trb; - int ret; - - /* Issue Disable Slot Command */ - xhci_init_event_cmd_trb(&trb, - 0, - 0, - TRB_TYPE(TRB_DISABLE_SLOT) | - SLOT_ID_FOR_TRB(vdev->slot_id)); - xhci_print_trb(xhci, &trb, "Request DisableSlot"); - xhci_issue_command(xhci, &trb); - ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); - xhci_print_trb(xhci, &trb, "Response DisableSlot"); - - /* Clear Device Context Base Address Array */ - xhci->dcbaa[vdev->slot_id] = 0; - - return ret; -} - -static int xhci_virtdev_enable_slot(struct xhci_virtual_device *vdev) -{ - struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); - union xhci_trb trb; - int slot_id; - int ret; - - /* Issue Enable Slot Command */ - xhci_init_event_cmd_trb(&trb, - 0, - 0, - TRB_TYPE(TRB_ENABLE_SLOT)); - xhci_print_trb(xhci, &trb, "Request EnableSlot"); - xhci_issue_command(xhci, &trb); - ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); - xhci_print_trb(xhci, &trb, "Response EnableSlot"); - if (ret) - return ret; - - slot_id = TRB_TO_SLOT_ID(trb.event_cmd.flags); - if (slot_id == 0) { - dev_err(xhci->dev, "EnableSlot returned reserved slot ID 0\n"); - return -EINVAL; - } - - vdev->slot_id = slot_id; - - return 0; -} - -int xhci_virtdev_reset(struct xhci_virtual_device *vdev) -{ - struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); - struct xhci_slot_ctx *out_slot; - union xhci_trb trb; - int ret; - - out_slot = xhci_get_slot_ctx(xhci, vdev->out_ctx); - - /* If device is not setup, there is no point in resetting it */ - if (GET_SLOT_STATE(le32_to_cpu(out_slot->dev_state)) == - SLOT_STATE_DISABLED) - return 0; - - xhci_init_event_cmd_trb(&trb, - 0, - 0, - TRB_TYPE(TRB_RESET_DEV) | - SLOT_ID_FOR_TRB(vdev->slot_id)); - xhci_print_trb(xhci, &trb, "Request Reset"); - xhci_issue_command(xhci, &trb); - ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); - xhci_print_trb(xhci, &trb, "Response Reset"); - - /* - * The Reset Device command can't fail, according to the 0.95/0.96 spec, - * unless we tried to reset a slot ID that wasn't enabled, - * or the device wasn't in the addressed or configured state. - */ - switch (GET_COMP_CODE(trb.event_cmd.status)) { - case COMP_CMD_ABORT: - case COMP_CMD_STOP: - dev_warn(xhci->dev, "Timeout waiting for reset device command\n"); - ret = -ETIMEDOUT; - break; - case COMP_EBADSLT: /* 0.95 completion code for bad slot ID */ - case COMP_CTX_STATE: /* 0.96 completion code for same thing */ - /* Don't treat this as an error. May change my mind later. */ - ret = 0; - case COMP_SUCCESS: - break; - default: - ret = -EINVAL; - } - - return ret; -} - -/* - * Once a hub descriptor is fetched for a device, we need to update the xHC's - * internal data structures for the device. - */ -static int xhci_virtdev_update_hub_device(struct xhci_virtual_device *vdev, - void *buffer, int length) -{ - struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); - struct usb_hub_descriptor *desc = buffer; - struct xhci_input_control_ctx *in_icc; - struct xhci_slot_ctx *in_slot, *out_slot; - union xhci_trb trb; - u32 dev_info, dev_info2, tt_info; - u8 maxchild; - u16 hubchar; - u32 flags; - int ret; - - out_slot = xhci_get_slot_ctx(xhci, vdev->out_ctx); - - /* Need at least first byte of wHubCharacteristics */ - if (length < 4) - return 0; - /* Skip already configured hub device */ - if (out_slot->dev_info & DEV_HUB) - return 0; - - maxchild = desc->bNbrPorts; - hubchar = le16_to_cpu(desc->wHubCharacteristics); - - in_slot = xhci_get_slot_ctx(xhci, vdev->in_ctx); - in_icc = xhci_get_input_control_ctx(vdev->in_ctx); - - /* update slot context */ - memcpy(in_slot, out_slot, sizeof(struct xhci_slot_ctx)); - in_icc->add_flags |= cpu_to_le32(SLOT_FLAG); - in_icc->drop_flags = 0; - in_slot->dev_state = 0; - dev_info = le32_to_cpu(in_slot->dev_info); - dev_info2 = le32_to_cpu(in_slot->dev_info2); - tt_info = le32_to_cpu(in_slot->tt_info); - - dev_info |= DEV_HUB; - /* HS Multi-TT in bDeviceProtocol */ - if (vdev->udev->speed == USB_SPEED_HIGH && - vdev->udev->descriptor->bDeviceProtocol == USB_HUB_PR_HS_MULTI_TT) - dev_info |= DEV_MTT; - if (xhci->hci_version > 0x95) { - dev_info2 |= XHCI_MAX_PORTS(maxchild); - /* Set TT think time - convert from ns to FS bit times. - * 0 = 8 FS bit times, 1 = 16 FS bit times, - * 2 = 24 FS bit times, 3 = 32 FS bit times. - * - * xHCI 1.0: this field shall be 0 if the device is not a - * High-speed hub. - */ - if (xhci->hci_version < 0x100 || - vdev->udev->speed == USB_SPEED_HIGH) { - u32 think_time = (hubchar & HUB_CHAR_TTTT) >> 5; - tt_info |= TT_THINK_TIME(think_time); - } - } - in_slot->dev_info = cpu_to_le32(dev_info); - in_slot->dev_info2 = cpu_to_le32(dev_info2); - in_slot->tt_info = cpu_to_le32(tt_info); - - /* Issue Configure Endpoint or Evaluate Context Command */ - flags = SLOT_ID_FOR_TRB(vdev->slot_id); - if (xhci->hci_version > 0x95) - flags |= TRB_TYPE(TRB_CONFIG_EP); - else - flags |= TRB_TYPE(TRB_EVAL_CONTEXT); - xhci_init_event_cmd_trb(&trb, - vdev->in_ctx->dma, - 0, - flags); - xhci_print_trb(xhci, &trb, "Request ConfigureEndpoint"); - xhci_issue_command(xhci, &trb); - ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); - xhci_print_trb(xhci, &trb, "Response ConfigureEndpoint"); - xhci_virtdev_zero_in_ctx(vdev); - - return ret; -} - -static int xhci_virtdev_update_hub_status(struct xhci_virtual_device *vhub, - int port) -{ - struct xhci_hcd *xhci = to_xhci_hcd(vhub->udev->host); - struct usb_device *udev = vhub->udev->children[port - 1]; - struct xhci_virtual_device *vdev; - - if (!udev) - return 0; - - /* Check if we have a virtual device for it */ - vdev = xhci_find_virtdev(xhci, udev); - if (vdev) - xhci_virtdev_detach(vdev); - - return 0; -} - -static int xhci_virtdev_configure(struct xhci_virtual_device *vdev, int config) -{ - struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); - struct xhci_input_control_ctx *in_icc; - struct xhci_slot_ctx *in_slot; - struct usb_device *udev = vdev->udev; - union xhci_trb trb; - u32 add_flags = 0, last_ctx; - int i, j; - int ret; - - for (i = 0; i < udev->config.no_of_if; i++) { - struct usb_interface *intf = &udev->config.interface[i]; - - for (j = 0; j < intf->no_of_ep; j++) { - struct usb_endpoint_descriptor *ep = &intf->ep_desc[j]; - u8 type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; - u8 eptype = xhci_get_endpoint_type(ep->bEndpointAddress, - ep->bmAttributes); - u8 epi = xhci_get_endpoint_index(ep->bEndpointAddress, - ep->bmAttributes); - struct xhci_ep_ctx *ctx; - u32 mps, interval, mult, esit, max_packet, max_burst; - u32 ep_info, ep_info2, tx_info; - - ctx = xhci_get_ep_ctx(xhci, vdev->in_ctx, epi); - - vdev->ep[epi] = xhci_get_endpoint_ring(xhci); - if (!vdev->ep[epi]) - return -ENOMEM; - /* FIXME: set correct ring type */ - xhci_ring_init(vdev->ep[epi], NUM_TRANSFER_TRBS, - TYPE_BULK); - add_flags |= BIT(epi+1); - - mps = le16_to_cpu(ep->wMaxPacketSize); - interval = xhci_get_endpoint_interval(vdev->udev, ep); - mult = xhci_get_endpoint_mult(vdev->udev, ep); - esit = xhci_get_max_esit_payload(vdev->udev, ep); - max_packet = GET_MAX_PACKET(mps); - max_burst = 0; - - ep_info = EP_INTERVAL(interval) | EP_MULT(mult); - ep_info2 = EP_TYPE(eptype); - if (type == USB_ENDPOINT_XFER_ISOC) - ep_info2 |= ERROR_COUNT(0); - else - ep_info2 |= ERROR_COUNT(3); - - switch (udev->speed) { - case USB_SPEED_SUPER: - /* FIXME: max_burst = ss_ep_comp.bMaxBurst */ - max_burst = 0; - break; - case USB_SPEED_HIGH: - /* Some devices get this wrong */ - if (type == USB_ENDPOINT_XFER_BULK) - max_packet = 512; - if (type == USB_ENDPOINT_XFER_ISOC || - type == USB_ENDPOINT_XFER_INT) - max_burst = (mps & 0x1800) >> 11; - break; - case USB_SPEED_FULL: - case USB_SPEED_LOW: - break; - } - ep_info2 |= MAX_PACKET(max_packet) | MAX_BURST(max_burst); - - tx_info = MAX_ESIT_PAYLOAD_FOR_EP(esit); - switch (type) { - case USB_ENDPOINT_XFER_CONTROL: - tx_info |= AVG_TRB_LENGTH_FOR_EP(8); - break; - case USB_ENDPOINT_XFER_ISOC: - case USB_ENDPOINT_XFER_BULK: - tx_info |= AVG_TRB_LENGTH_FOR_EP(3 * 1024); - break; - case USB_ENDPOINT_XFER_INT: - tx_info |= AVG_TRB_LENGTH_FOR_EP(1 * 1024); - break; - } - - ctx->ep_info = cpu_to_le32(ep_info); - ctx->ep_info2 = cpu_to_le32(ep_info2); - ctx->tx_info = cpu_to_le32(tx_info); - ctx->deq = - cpu_to_le64((dma_addr_t)vdev->ep[epi]->enqueue | - vdev->ep[epi]->cycle_state); - } - } - - last_ctx = fls(add_flags) - 1; - - in_slot = xhci_get_slot_ctx(xhci, vdev->in_ctx); - in_icc = xhci_get_input_control_ctx(vdev->in_ctx); - - /* See section 4.6.6 - A0 = 1; A1 = D0 = D1 = 0 */ - in_icc->add_flags = cpu_to_le32(add_flags); - in_icc->add_flags |= cpu_to_le32(SLOT_FLAG); - in_icc->add_flags &= cpu_to_le32(~EP0_FLAG); - in_icc->drop_flags &= cpu_to_le32(~(SLOT_FLAG | EP0_FLAG)); - - /* Don't issue the command if there's no endpoints to update. */ - if (in_icc->add_flags == cpu_to_le32(SLOT_FLAG) && - in_icc->drop_flags == 0) - return 0; - - in_slot->dev_info &= cpu_to_le32(~LAST_CTX_MASK); - in_slot->dev_info |= cpu_to_le32(LAST_CTX(last_ctx)); - - /* Issue Configure Endpoint Command */ - xhci_init_event_cmd_trb(&trb, - vdev->in_ctx->dma, - 0, - TRB_TYPE(TRB_CONFIG_EP) | - SLOT_ID_FOR_TRB(vdev->slot_id)); - xhci_print_trb(xhci, &trb, "Request ConfigureEndpoint"); - xhci_issue_command(xhci, &trb); - ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); - xhci_print_trb(xhci, &trb, "Response ConfigureEndpoint"); - xhci_virtdev_zero_in_ctx(vdev); - - return ret; -} - -static int xhci_virtdev_deconfigure(struct xhci_virtual_device *vdev) -{ - struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); - union xhci_trb trb; - int ret; - - /* Issue Deconfigure Endpoint Command */ - xhci_init_event_cmd_trb(&trb, - vdev->in_ctx->dma, - 0, - TRB_TYPE(TRB_CONFIG_EP) | TRB_DC | - SLOT_ID_FOR_TRB(vdev->slot_id)); - xhci_print_trb(xhci, &trb, "Request DeconfigureEndpoint"); - xhci_issue_command(xhci, &trb); - ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); - xhci_print_trb(xhci, &trb, "Response DeconfigureEndpoint"); - xhci_virtdev_zero_in_ctx(vdev); - - return ret; -} - -static int xhci_virtdev_init(struct xhci_virtual_device *vdev) -{ - struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); - struct usb_device *top_dev; - struct xhci_slot_ctx *in_slot; - struct xhci_ep_ctx *in_ep; - int max_packets; - u32 route = 0, dev_info, dev_info2, tt_info, ep_info2, tx_info; - bool on_hs_hub = false; - int hs_slot_id = 0; - - in_ep = xhci_get_ep_ctx(xhci, vdev->in_ctx, 0); - in_slot = xhci_get_slot_ctx(xhci, vdev->in_ctx); - - /* - * Find the root hub port this device is under, also determine SlotID - * of possible external HS hub a LS/FS device could be connected to. - */ - for (top_dev = vdev->udev; top_dev->parent && top_dev->parent->parent; - top_dev = top_dev->parent) { - if (top_dev->parent->descriptor->bDeviceClass != USB_CLASS_HUB) - continue; - - route = (route << 4) | (top_dev->portnr & 0xf); - - if (top_dev->parent->speed != USB_SPEED_LOW && - top_dev->parent->speed != USB_SPEED_FULL) { - on_hs_hub = true; - if (!hs_slot_id) { - struct xhci_virtual_device *vhub = - xhci_find_virtdev(xhci, top_dev->parent); - hs_slot_id = vhub->slot_id; - } - } - } - - /* 4.3.3 3) Initalize Input Slot Context */ - dev_info = LAST_CTX(1); - switch (vdev->udev->speed) { - case USB_SPEED_SUPER: - dev_info |= SLOT_SPEED_SS; - max_packets = 512; - break; - case USB_SPEED_HIGH: - dev_info |= SLOT_SPEED_HS; - max_packets = 64; - break; - case USB_SPEED_FULL: - dev_info |= SLOT_SPEED_FS; - max_packets = 64; - break; - case USB_SPEED_LOW: - dev_info |= SLOT_SPEED_LS; - max_packets = 8; - break; - default: - max_packets = 0; - break; - } - dev_info |= route; - dev_info2 = ROOT_HUB_PORT(top_dev->portnr); - tt_info = 0; - - /* Is this a LS/FS device under an external HS hub? */ - if (on_hs_hub && (vdev->udev->speed == USB_SPEED_FULL || - vdev->udev->speed == USB_SPEED_LOW)) { - dev_info |= DEV_MTT; - tt_info |= (top_dev->portnr << 8) | hs_slot_id; - } - - in_slot->dev_info = cpu_to_le32(dev_info); - in_slot->dev_info2 = cpu_to_le32(dev_info2); - in_slot->tt_info = cpu_to_le32(tt_info); - - /* 4.3.3 4) Initalize Transfer Ring */ - vdev->ep[0] = xhci_get_endpoint_ring(xhci); - if (!vdev->ep[0]) - return -ENOMEM; - xhci_ring_init(vdev->ep[0], NUM_TRANSFER_TRBS, TYPE_CTRL); - - /* 4.3.3 5) Initialize Input Control Endpoint 0 Context */ - ep_info2 = EP_TYPE(CTRL_EP) | MAX_BURST(0) | ERROR_COUNT(3); - ep_info2 |= MAX_PACKET(max_packets); - tx_info = AVG_TRB_LENGTH_FOR_EP(8); - in_ep->ep_info2 = cpu_to_le32(ep_info2); - in_ep->tx_info = cpu_to_le32(tx_info); - in_ep->deq = cpu_to_le64((dma_addr_t)vdev->ep[0]->enqueue | - vdev->ep[0]->cycle_state); - - /* 4.3.3 6+7) Initalize and Set Device Context Base Address Array */ - xhci->dcbaa[vdev->slot_id] = cpu_to_le64(vdev->out_ctx->dma); - - return 0; -} - -static int xhci_virtdev_setup(struct xhci_virtual_device *vdev, - enum xhci_setup_dev setup) -{ - struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); - static struct xhci_input_control_ctx *in_icc; - struct xhci_slot_ctx *in_slot; - struct xhci_ep_ctx *in_ep; - union xhci_trb trb; - u32 flags; - int ret; - - in_slot = xhci_get_slot_ctx(xhci, vdev->in_ctx); - in_icc = xhci_get_input_control_ctx(vdev->in_ctx); - - /* - * If this is the first Set Address since device - * plug-in then initialize Slot Context - */ - if (!in_slot->dev_info) - xhci_virtdev_init(vdev); - else { - in_ep = xhci_get_ep_ctx(xhci, vdev->in_ctx, 0); - - /* Otherwise, update Control Ring Dequeue pointer */ - in_ep->deq = cpu_to_le64((dma_addr_t)vdev->ep[0]->enqueue | - vdev->ep[0]->cycle_state); - /* - * FS devices have MaxPacketSize0 of 8 or 64, we start - * with 64. If assumtion was wrong, fix it up here. - */ - if (vdev->udev->speed == USB_SPEED_FULL && - vdev->udev->maxpacketsize == PACKET_SIZE_8) { - u32 info = le32_to_cpu(in_ep->ep_info2); - info &= ~MAX_PACKET_MASK; - info |= MAX_PACKET(8); - in_ep->ep_info2 = cpu_to_le32(info); - } - } - - in_icc->add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG); - in_icc->drop_flags = 0; - - /* Issue Address Device Command */ - flags = TRB_TYPE(TRB_ADDR_DEV) | - SLOT_ID_FOR_TRB(vdev->slot_id); - if (setup == SETUP_CONTEXT_ONLY) - flags |= TRB_BSR; - xhci_init_event_cmd_trb(&trb, - vdev->in_ctx->dma, - 0, - flags); - xhci_print_trb(xhci, &trb, "Request AddressDevice"); - xhci_issue_command(xhci, &trb); - ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); - xhci_print_trb(xhci, &trb, "Response AddressDevice"); - xhci_virtdev_zero_in_ctx(vdev); - - return ret; -} - -static int xhci_virtdev_set_address(struct xhci_virtual_device *vdev) -{ - return xhci_virtdev_setup(vdev, SETUP_CONTEXT_ADDRESS); -} - -static int xhci_virtdev_enable(struct xhci_virtual_device *vdev) -{ - return xhci_virtdev_setup(vdev, SETUP_CONTEXT_ONLY); -} - -static int xhci_virtdev_attach(struct xhci_hcd *xhci, struct usb_device *udev) -{ - struct xhci_virtual_device *vdev; - int ret; - - vdev = xhci_alloc_virtdev(xhci, udev); - if (IS_ERR(vdev)) - return PTR_ERR(vdev); - - ret = xhci_virtdev_enable_slot(vdev); - if (ret) - return ret; - - return xhci_virtdev_enable(vdev); -} - -int xhci_virtdev_detach(struct xhci_virtual_device *vdev) -{ - xhci_virtdev_deconfigure(vdev); - xhci_virtdev_disable_slot(vdev); - xhci_free_virtdev(vdev); - - return 0; -} - -static int xhci_submit_normal(struct usb_device *udev, unsigned long pipe, - void *buffer, int length) -{ - struct usb_host *host = udev->host; - struct xhci_hcd *xhci = to_xhci_hcd(host); - enum dma_data_direction dma_direction; - struct xhci_virtual_device *vdev; - struct xhci_slot_ctx *out_slot; - dma_addr_t buffer_dma; - union xhci_trb trb; - u8 epaddr = usb_pipeendpoint(pipe); - u8 epi; - u32 flags = TRB_TYPE(TRB_NORMAL) | TRB_IOC; - int ret; - - if (usb_pipein(pipe)) { - epaddr |= USB_DIR_IN; - flags |= TRB_ISP; - dma_direction = DMA_FROM_DEVICE; - } else { - epaddr |= USB_DIR_OUT; - dma_direction = DMA_TO_DEVICE; - } - - epi = xhci_get_endpoint_index(epaddr, usb_pipetype(pipe)); - vdev = xhci_find_virtdev(xhci, udev); - if (!vdev) - return -ENODEV; - - out_slot = xhci_get_slot_ctx(xhci, vdev->out_ctx); - - dev_dbg(xhci->dev, "%s udev %p vdev %p slot %u state %u epi %u in_ctx %p out_ctx %p\n", - __func__, udev, vdev, vdev->slot_id, - GET_SLOT_STATE(le32_to_cpu(out_slot->dev_state)), epi, - vdev->in_ctx->bytes, vdev->out_ctx->bytes); - - /* pass ownership of data buffer to device */ - buffer_dma = dma_map_single(xhci->dev, buffer, length, - dma_direction); - if (dma_mapping_error(xhci->dev, buffer_dma)) - return -EFAULT; - - /* Normal TRB */ - /* FIXME: TD remainder */ - xhci_init_event_cmd_trb(&trb, - buffer_dma, - TRB_LEN(length) | TRB_INTR_TARGET(0), - flags); - xhci_print_trb(xhci, &trb, "Request Normal"); - xhci_virtdev_issue_transfer(vdev, epi, &trb, true); - ret = xhci_wait_for_event(xhci, TRB_TRANSFER, &trb); - xhci_print_trb(xhci, &trb, "Response Normal"); - - /* Regain ownership of data buffer from device */ - dma_unmap_single(xhci->dev, buffer_dma, length, - dma_direction); - switch (ret) { - case -COMP_SHORT_TX: - udev->status = 0; - udev->act_len = length - EVENT_TRB_LEN(trb.event_cmd.status); - return 0; - case 0: - udev->status = 0; - udev->act_len = 0; - return 0; - case -ETIMEDOUT: - udev->status = USB_ST_CRC_ERR; - return -1; - default: - return -1; - } -} - -static int xhci_submit_control(struct usb_device *udev, unsigned long pipe, - void *buffer, int length, struct devrequest *req) -{ - struct usb_host *host = udev->host; - struct xhci_hcd *xhci = to_xhci_hcd(host); - struct xhci_virtual_device *vdev; - struct xhci_slot_ctx *out_slot; - dma_addr_t buffer_dma = 0; - union xhci_trb trb; - u16 typeReq = (req->requesttype << 8) | req->request; - u64 field[2]; - u32 flags; - int ret; - - dev_dbg(xhci->dev, "%s req %u (%#x), type %u (%#x), value %u (%#x), index %u (%#x), length %u (%#x)\n", - __func__, req->request, req->request, - req->requesttype, req->requesttype, - le16_to_cpu(req->value), le16_to_cpu(req->value), - le16_to_cpu(req->index), le16_to_cpu(req->index), - le16_to_cpu(req->length), le16_to_cpu(req->length)); - - vdev = xhci_find_virtdev(xhci, udev); - if (!vdev) { - ret = xhci_virtdev_attach(xhci, udev); - if (ret) - return ret; - vdev = xhci_find_virtdev(xhci, udev); - } - if (!vdev) - return -ENODEV; - - out_slot = xhci_get_slot_ctx(xhci, vdev->out_ctx); - - dev_dbg(xhci->dev, "%s udev %p vdev %p slot %u state %u epi %u in_ctx %p out_ctx %p\n", - __func__, udev, vdev, vdev->slot_id, - GET_SLOT_STATE(le32_to_cpu(out_slot->dev_state)), 0, - vdev->in_ctx->bytes, vdev->out_ctx->bytes); - - if (req->request == USB_REQ_SET_ADDRESS) - return xhci_virtdev_set_address(vdev); - if (req->request == USB_REQ_SET_CONFIGURATION) { - ret = xhci_virtdev_configure(vdev, le16_to_cpu(req->value)); - if (ret) - return ret; - } - - if (length > 0) { - /* Pass ownership of data buffer to device */ - buffer_dma = dma_map_single(xhci->dev, buffer, length, - (req->requesttype & USB_DIR_IN) ? - DMA_FROM_DEVICE : DMA_TO_DEVICE); - if (dma_mapping_error(xhci->dev, buffer_dma)) - return -EFAULT; - } - /* Setup TRB */ - field[0] = le16_to_cpu(req->value) << 16 | - req->request << 8 | req->requesttype; - field[1] = le16_to_cpu(req->length) << 16 | - le16_to_cpu(req->index); - flags = TRB_TYPE(TRB_SETUP) | TRB_IDT; - if (xhci->hci_version >= 0x100 && length > 0) { - if (req->requesttype & USB_DIR_IN) - flags |= TRB_TX_TYPE(TRB_DATA_IN); - else - flags |= TRB_TX_TYPE(TRB_DATA_OUT); - } - xhci_init_event_cmd_trb(&trb, - field[1] << 32 | field[0], - TRB_LEN(8) | TRB_INTR_TARGET(0), - flags); - xhci_print_trb(xhci, &trb, "Request Setup "); - xhci_virtdev_issue_transfer(vdev, 0, &trb, false); - - /* Data TRB */ - if (length > 0) { - /* FIXME: TD remainder */ - flags = TRB_TYPE(TRB_DATA) | TRB_IOC; - if (req->requesttype & USB_DIR_IN) - flags |= TRB_ISP | TRB_DIR_IN; - xhci_init_event_cmd_trb(&trb, - buffer_dma, - TRB_LEN(length) | TRB_INTR_TARGET(0), - flags); - xhci_print_trb(xhci, &trb, "Request Data "); - xhci_virtdev_issue_transfer(vdev, 0, &trb, false); - } - - /* Status TRB */ - flags = TRB_TYPE(TRB_STATUS) | TRB_IOC; - if (!(length > 0 && req->requesttype & USB_DIR_IN)) - flags |= TRB_DIR_IN; - xhci_init_event_cmd_trb(&trb, - 0, - TRB_INTR_TARGET(0), - flags); - xhci_print_trb(xhci, &trb, "Request Status"); - xhci_virtdev_issue_transfer(vdev, 0, &trb, true); - - if (length > 0) { - ret = xhci_wait_for_event(xhci, TRB_TRANSFER, &trb); - xhci_print_trb(xhci, &trb, "Response Data "); - if (ret == -COMP_SHORT_TX) - length -= EVENT_TRB_LEN(trb.event_cmd.status); - else if (ret < 0) - goto dma_regain; - } - - ret = xhci_wait_for_event(xhci, TRB_TRANSFER, &trb); - xhci_print_trb(xhci, &trb, "Response Status"); - -dma_regain: - if (length > 0) { - /* Regain ownership of data buffer from device */ - dma_unmap_single(xhci->dev, buffer_dma, length, - (req->requesttype & USB_DIR_IN) ? - DMA_FROM_DEVICE : DMA_TO_DEVICE); - } - - if (ret < 0) - return ret; - - /* - * usb core doesn't notify us about device events on - * external Hubs, track it ourselves. - */ - if (typeReq == GetHubDescriptor) - xhci_virtdev_update_hub_device(vdev, buffer, length); - if (typeReq == ClearPortFeature && - cpu_to_le16(req->value) == USB_PORT_FEAT_C_CONNECTION) - xhci_virtdev_update_hub_status(vdev, le16_to_cpu(req->index)); - - return length; -} - -/* - * xHCI host controller driver - */ - -static void xhci_dma_alloc(struct xhci_hcd *xhci) -{ - size_t sz_sp, sz_spa, sz_dca, sz_cmd, sz_evt, sz_erst, sz_ep; - u64 reg64; - void *p; - int i, num_ep; - - /* Scratchpad buffers: PAGE_SIZE aligned */ - sz_sp = ALIGN(xhci->num_sp * xhci->page_size, xhci->page_size); - /* Device Context Array: 64B aligned */ - sz_dca = ALIGN(xhci->max_slots * sizeof(u64), 64); - /* Command Ring: 64B aligned */ - sz_cmd = ALIGN(NUM_COMMAND_TRBS * sizeof(union xhci_trb), 64); - /* Event Ring: 64B aligned */ - sz_evt = NUM_EVENT_SEGM * - ALIGN(NUM_EVENT_TRBS * sizeof(union xhci_trb), 64); - /* Event Ring Segment Table: 64B aligned */ - sz_erst = ALIGN(NUM_EVENT_SEGM * sizeof(struct xhci_erst_entry), 64); - /* Scratchpad Buffer Array: 64B aligned */ - sz_spa = ALIGN(xhci->num_sp * sizeof(u64), 64); - - xhci->dma_size = sz_sp + sz_spa + sz_dca + sz_cmd + sz_evt + sz_erst; - - /* - * Endpoint Transfer Ring: 16B aligned - * - * We allocate up to MAX_EP_RINGS from the rest of the PAGE - * for virtual devices to pick-up (and return) for endpoint trbs. - */ - sz_ep = ALIGN(NUM_TRANSFER_TRBS * sizeof(union xhci_trb), 16); - - num_ep = PAGE_ALIGN(xhci->dma_size) - - MIN_EP_RINGS * sz_ep - xhci->dma_size; - num_ep /= sz_ep; - num_ep = max(MAX_EP_RINGS, MIN_EP_RINGS + num_ep); - xhci->dma_size += num_ep * sz_ep; - - p = dma_alloc_coherent(xhci->dma_size, DMA_ADDRESS_BROKEN); - - xhci->sp = p; p += sz_sp; - xhci->dcbaa = p; p += sz_dca; - xhci->cmd_ring.trbs = p; p += sz_cmd; - xhci->event_ring.trbs = p; p += sz_evt; - xhci->event_erst = p; p += sz_erst; - xhci->sp_array = p; p += sz_spa; - - xhci->rings = xzalloc(num_ep * sizeof(*xhci->rings)); - for (i = 0; i < num_ep; i++) { - xhci->rings[i].trbs = p; - p += sz_ep; - xhci_put_endpoint_ring(xhci, &xhci->rings[i]); - } - - /* Setup Scratchpad Buffer Array and Base Address in Device Context */ - reg64 = cpu_to_le64((dma_addr_t)xhci->sp); - for (i = 0; i < xhci->num_sp; i++, reg64 += xhci->page_size) - xhci->sp_array[i] = cpu_to_le64(reg64); - if (xhci->num_sp) - xhci->dcbaa[0] = cpu_to_le64((dma_addr_t)xhci->sp_array); - - /* Setup Event Ring Segment Table and Event Ring */ - reg64 = (dma_addr_t)&xhci->event_ring.trbs[0]; - xhci->event_erst[0].seg_addr = cpu_to_le64(reg64); - xhci->event_erst[0].seg_size = cpu_to_le32(NUM_EVENT_TRBS); - xhci_ring_init(&xhci->event_ring, NUM_EVENT_TRBS, TYPE_EVENT); - - /* Setup Command Ring */ - xhci_ring_init(&xhci->cmd_ring, NUM_COMMAND_TRBS, TYPE_COMMAND); -} - -static int xhci_halt(struct xhci_hcd *xhci) -{ - u32 reg = readl(&xhci->op_regs->status); - u32 mask = (u32)~XHCI_IRQS; - - if (!(reg & STS_HALT)) - mask &= ~CMD_RUN; - - /* disable any IRQs and begin halting process */ - reg = readl(&xhci->op_regs->command); - reg &= mask; - writel(reg, &xhci->op_regs->command); - - return xhci_handshake(&xhci->op_regs->status, - STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC); -} - -static int xhci_reset(struct xhci_hcd *xhci) -{ - u32 reg; - int ret; - - reg = readl(&xhci->op_regs->command); - reg |= CMD_RESET; - writel(reg, &xhci->op_regs->command); - - ret = xhci_handshake(&xhci->op_regs->command, - CMD_RESET, 0, 10 * SECOND / USECOND); - if (ret) { - dev_err(xhci->dev, "failed to reset\n"); - return ret; - } - - return 0; -} - -static int xhci_start(struct xhci_hcd *xhci) -{ - u32 reg; - int ret, i; - - reg = readl(&xhci->op_regs->command); - reg |= CMD_RUN; - writel(reg, &xhci->op_regs->command); - - ret = xhci_handshake(&xhci->op_regs->status, - STS_HALT, 0, XHCI_MAX_HALT_USEC); - if (ret) { - dev_err(xhci->dev, "failed to start\n"); - return ret; - } - - /* Ensure ports are powered-off */ - for (i = 0; i < xhci->num_usb_ports; i++) - xhci_hub_port_power(xhci, i, false); - - return 0; -} - -static int xhci_init(struct usb_host *host) -{ - struct xhci_hcd *xhci = to_xhci_hcd(host); - u32 reg; - u64 reg64; - int i, tmp, ret; - - ret = xhci_halt(xhci); - if (ret) - return ret; - - ret = xhci_reset(xhci); - if (ret) - return ret; - - tmp = readl(&xhci->op_regs->page_size); - for (i = 0; i < 16; i++) { - if ((0x1 & tmp) != 0) - break; - tmp >>= 1; - } - if (i < 16) - tmp = (1 << (i+12)); - else - dev_warn(xhci->dev, "unsupported page size %d\n", tmp); - /* Use 4K pages, since that's common and the minimum the HC supports */ - xhci->page_shift = 12; - xhci->page_size = 1 << xhci->page_shift; - - xhci->rootdev = 0; - xhci->num_sp = HCS_MAX_SCRATCHPAD(xhci->hcs_params2); - xhci->max_slots = HCS_MAX_SLOTS(xhci->hcs_params1); - xhci_dma_alloc(xhci); - - ret = xhci_hub_setup_ports(xhci); - if (ret) - return ret; - - /* - * Program the Max Device Slots Enabled (MaxSlotsEn) field in the - * CONFIG register (5.4.7) with the max number of slots HC can handle. - */ - reg = readl(&xhci->op_regs->config_reg); - reg |= (xhci->max_slots & HCS_SLOTS_MASK); - writel(reg, &xhci->op_regs->config_reg); - - /* - * Program the Device Context Base Address Array Pointer (DCBAAP) - * register (5.4.6) with a 64-bit address pointing to where the - * Device Context Base Address Array is located. - */ - xhci_write_64((dma_addr_t)xhci->dcbaa, &xhci->op_regs->dcbaa_ptr); - - /* - * Define the Command Ring Dequeue Pointer by programming the - * Command Ring Control Register (5.4.5) with a 64-bit address - * pointing to the starting address of the first TRB of the Command - * Ring. - */ - reg64 = xhci_read_64(&xhci->op_regs->cmd_ring); - reg64 = (reg64 & (u64)CMD_RING_RSVD_BITS) | - ((dma_addr_t)&xhci->cmd_ring.trbs[0] & - ~(dma_addr_t)CMD_RING_RSVD_BITS) | - xhci->cmd_ring.cycle_state; - xhci_write_64(reg64, &xhci->op_regs->cmd_ring); - - reg = readl(&xhci->cap_regs->db_off) & DBOFF_MASK; - xhci->dba = (void __iomem *)xhci->cap_regs + reg; - xhci->ir_set = &xhci->run_regs->ir_set[0]; - - reg64 = (dma_addr_t)&xhci->event_ring.trbs[0] & - ~(dma_addr_t)CMD_RING_RSVD_BITS; - xhci->event_erst[i].seg_addr = cpu_to_le64(reg64); - xhci->event_erst[i].seg_size = cpu_to_le32(NUM_EVENT_TRBS); - reg = readl(&xhci->ir_set->erst_size) & ~ERST_SIZE_MASK; - writel(reg | NUM_EVENT_SEGM, &xhci->ir_set->erst_size); - xhci_set_event_dequeue(xhci); - - reg64 = xhci_read_64(&xhci->ir_set->erst_base); - reg64 &= ERST_PTR_MASK; - reg64 |= (dma_addr_t)xhci->event_erst & - ~(dma_addr_t)CMD_RING_RSVD_BITS; - xhci_write_64(reg64, &xhci->ir_set->erst_base); - - /* - * Write the USBCMD (5.4.1) to turn the host controller ON via - * setting the Run/Stop (R/S) bit to ‘1’. This operation allows the - * xHC to begin accepting doorbell references. - */ - - return xhci_start(xhci); - - /* - * At this point, the host controller is up and running and the Root - * Hub ports (5.4.8) will begin reporting device connects, etc., - * and system software may begin enumerating devices. - * System software may follow the procedures described in section 4.3, - * to enumerate attached devices. - * - * USB2 (LS/FS/HS) devices require the port reset process to advance - * the port to the Enabled state. Once USB2 ports are Enabled, the port - * is active with SOFs occurring on the port, but the Pipe Schedules - * have not yet been enabled. - * - * SS ports automatically advance to the Enabled state if a successful - * device attach is detected. - */ -} - -static int xhci_submit_bulk_msg(struct usb_device *dev, unsigned long pipe, - void *buffer, int length, int timeout) -{ - return xhci_submit_normal(dev, pipe, buffer, length); -} - -static int xhci_submit_control_msg(struct usb_device *dev, unsigned long pipe, - void *buffer, int length, struct devrequest *setup, int timeout) -{ - struct xhci_hcd *xhci = to_xhci_hcd(dev->host); - - /* Catch Root Hub requests */ - if (usb_pipedevice(pipe) == xhci->rootdev) { - if (xhci->rootdev == 0) - dev->speed = USB_SPEED_HIGH; - return xhci_hub_control(dev, pipe, buffer, length, setup); - } - - return xhci_submit_control(dev, pipe, buffer, length, setup); -} - -static int xhci_submit_int_msg(struct usb_device *dev, unsigned long pipe, - void *buffer, int length, int interval) -{ - struct xhci_hcd *xhci = to_xhci_hcd(dev->host); - - dev_err(xhci->dev, "Interrupt messages not supported\n"); - - return -ENOTSUPP; -} - -static int xhci_detect(struct device_d *dev) -{ - struct xhci_hcd *xhci = dev->priv; - - return usb_host_detect(&xhci->host); -} - -int xhci_register(struct device_d *dev, struct xhci_data *data) -{ - struct usb_host *host; - struct xhci_hcd *xhci; - - xhci = xzalloc(sizeof(*xhci)); - host = &xhci->host; - INIT_LIST_HEAD(&xhci->vdev_list); - INIT_LIST_HEAD(&xhci->rings_list); - xhci->dev = dev; - xhci->cap_regs = data->regs; - xhci->op_regs = (void __iomem *)xhci->cap_regs + - HC_LENGTH(readl(&xhci->cap_regs->hc_capbase)); - xhci->run_regs = (void __iomem *)xhci->cap_regs + - (readl(&xhci->cap_regs->run_regs_off) & RTSOFF_MASK); - /* Cache read-only capability registers */ - xhci->hcs_params1 = readl(&xhci->cap_regs->hcs_params1); - xhci->hcs_params2 = readl(&xhci->cap_regs->hcs_params2); - xhci->hcs_params3 = readl(&xhci->cap_regs->hcs_params3); - xhci->hcc_capbase = readl(&xhci->cap_regs->hc_capbase); - xhci->hci_version = HC_VERSION(xhci->hcc_capbase); - xhci->hcc_params = readl(&xhci->cap_regs->hcc_params); - - host->hw_dev = dev; - host->init = xhci_init; - host->submit_int_msg = xhci_submit_int_msg; - host->submit_control_msg = xhci_submit_control_msg; - host->submit_bulk_msg = xhci_submit_bulk_msg; - - dev->priv = xhci; - dev->detect = xhci_detect; - - usb_register_host(host); - - dev_info(dev, "USB xHCI %x.%02x\n", - xhci->hci_version >> 8, xhci->hci_version & 0xff); - - return 0; -} - -/* - * xHCI platform driver - */ - -static int xhci_probe(struct device_d *dev) -{ - struct resource *iores; - struct xhci_data data = {}; - - iores = dev_request_mem_resource(dev, 0); - if (IS_ERR(iores)) - return PTR_ERR(iores); - data.regs = IOMEM(iores->start); - - return xhci_register(dev, &data); -} - -static void xhci_remove(struct device_d *dev) -{ - struct xhci_hcd *xhci = dev->priv; - xhci_halt(xhci); -} - -static struct driver_d xhci_driver = { - .name = "xHCI", - .probe = xhci_probe, - .remove = xhci_remove, -}; -device_platform_driver(xhci_driver); diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c deleted file mode 100644 index 5ae16f5ca5..0000000000 --- a/drivers/usb/host/xhci-hub.c +++ /dev/null @@ -1,646 +0,0 @@ -/* - * xHCI USB 3.0 Root Hub - * - * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> - * - * This currently does not support any SuperSpeed capabilities. - * - * Some code borrowed from the Linux xHCI driver - * Author: Sarah Sharp - * Copyright (C) 2008 Intel Corp. - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ -//#define DEBUG -#include <clock.h> -#include <common.h> -#include <io.h> -#include <linux/err.h> -#include <usb/usb.h> -#include <usb/xhci.h> - -#include "xhci.h" - -static const struct usb_root_hub_info usb_rh_info = { - .hub = { - .bLength = USB_DT_HUB_NONVAR_SIZE + - ((USB_MAXCHILDREN + 1 + 7) / 8), - .bDescriptorType = USB_DT_HUB, - .bNbrPorts = 0, /* runtime modified */ - .wHubCharacteristics = 0, - .bPwrOn2PwrGood = 10, - .bHubContrCurrent = 0, - .u.hs.DeviceRemovable = {}, - .u.hs.PortPwrCtrlMask = {} - }, - .device = { - .bLength = USB_DT_DEVICE_SIZE, - .bDescriptorType = USB_DT_DEVICE, - .bcdUSB = __constant_cpu_to_le16(0x0002), /* v2.0 */ - .bDeviceClass = USB_CLASS_HUB, - .bDeviceSubClass = 0, - .bDeviceProtocol = USB_HUB_PR_HS_MULTI_TT, - .bMaxPacketSize0 = 64, - .idVendor = 0x0000, - .idProduct = 0x0000, - .bcdDevice = __constant_cpu_to_le16(0x0001), - .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 = 0x81, /* UE_DIR_IN | EHCI_INTR_ENDPT */ - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = __constant_cpu_to_le16((USB_MAXCHILDREN + 1 + 7) / 8), - .bInterval = 255 - } -}; - -static void xhci_setup_common_hub_descriptor(struct xhci_hcd *xhci, - struct usb_hub_descriptor *desc, int ports) -{ - u16 val; - - /* xhci section 5.4.9 says 20ms max */ - desc->bPwrOn2PwrGood = 10; - desc->bHubContrCurrent = 0; - desc->bNbrPorts = xhci->num_usb_ports; - - val = 0; - /* Bits 1:0 - support per-port power switching, or power always on */ - if (HCC_PPC(xhci->hcc_params)) - val |= HUB_CHAR_INDV_PORT_LPSM; - else - val |= HUB_CHAR_NO_LPSM; - /* Bit 2 - root hubs are not part of a compound device */ - /* Bits 4:3 - individual port over current protection */ - val |= HUB_CHAR_INDV_PORT_OCPM; - /* Bits 6:5 - no TTs in root ports */ - /* Bit 7 - no port indicators */ - desc->wHubCharacteristics = cpu_to_le16(val); -} - -static void xhci_setup_usb2_hub_descriptor(struct xhci_hcd *xhci) -{ - struct usb_hub_descriptor *desc = &xhci->usb_info.hub; - __u8 port_removable[(USB_MAXCHILDREN + 1 + 7) / 8]; - int ports; - u32 portsc; - u16 val; - int i; - - ports = xhci->num_usb_ports; - xhci_setup_common_hub_descriptor(xhci, desc, ports); - desc->bDescriptorType = USB_DT_HUB; - val = 1 + (ports / 8); - desc->bLength = USB_DT_HUB_NONVAR_SIZE + 2 * val; - - /* The Device Removable bits are reported on a byte granularity. - * If the port doesn't exist within that byte, the bit is set to 0. - */ - memset(port_removable, 0, sizeof(port_removable)); - for (i = 0; i < ports; i++) { - portsc = readl(xhci->usb_ports[i]); - /* If a device is removable, PORTSC reports a 0, same as in the - * hub descriptor DeviceRemovable bits. - */ - if (portsc & PORT_DEV_REMOVE) - /* This math is hairy because bit 0 of DeviceRemovable - * is reserved, and bit 1 is for port 1, etc. - */ - port_removable[(i + 1) / 8] |= 1 << ((i + 1) % 8); - } - - /* ch11.h defines a hub descriptor that has room for USB_MAXCHILDREN - * ports on it. The USB 2.0 specification says that there are two - * variable length fields at the end of the hub descriptor: - * DeviceRemovable and PortPwrCtrlMask. But since we can have less than - * USB_MAXCHILDREN ports, we may need to use the DeviceRemovable array - * to set PortPwrCtrlMask bits. PortPwrCtrlMask must always be set to - * 0xFF, so we initialize the both arrays (DeviceRemovable and - * PortPwrCtrlMask) to 0xFF. Then we set the DeviceRemovable for each - * set of ports that actually exist. - */ - memset(desc->u.hs.DeviceRemovable, 0xff, - sizeof(desc->u.hs.DeviceRemovable)); - memset(desc->u.hs.PortPwrCtrlMask, 0xff, - sizeof(desc->u.hs.PortPwrCtrlMask)); - - for (i = 0; i < (ports + 1 + 7) / 8; i++) - memset(&desc->u.hs.DeviceRemovable[i], port_removable[i], - sizeof(__u8)); -} - -/* FIXME: usb core does not know about USB_SPEED_SUPER at all */ -static __maybe_unused void xhci_setup_usb3_hub_descriptor(struct xhci_hcd *xhci) -{ - struct usb_hub_descriptor *desc = &xhci->usb_info.hub; - int ports; - u16 port_removable; - u32 portsc; - int i; - - ports = xhci->num_usb_ports; - xhci_setup_common_hub_descriptor(xhci, desc, ports); - desc->bDescriptorType = USB_DT_SS_HUB; - desc->bLength = USB_DT_SS_HUB_SIZE; - /* - * header decode latency should be zero for roothubs, - * see section 4.23.5.2. - */ - desc->u.ss.bHubHdrDecLat = 0; - desc->u.ss.wHubDelay = 0; - port_removable = 0; - /* bit 0 is reserved, bit 1 is for port 1, etc. */ - for (i = 0; i < ports; i++) { - portsc = readl(xhci->usb_ports[i]); - if (portsc & PORT_DEV_REMOVE) - port_removable |= 1 << (i + 1); - } - desc->u.ss.DeviceRemovable = cpu_to_le16(port_removable); -} - -static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, - __le32 __iomem *addr, u8 major_revision, int max_caps) -{ - u32 reg, port_offset, port_count; - int i; - - if (major_revision > 0x03) { - dev_warn(xhci->dev, "Ignoring unknown port speed, Ext Cap %p, rev %02x\n", - addr, major_revision); - return; - } - - /* Port offset and count in the third dword, see section 7.2 */ - reg = readl(addr + 2); - port_offset = XHCI_EXT_PORT_OFF(reg); - port_count = XHCI_EXT_PORT_COUNT(reg); - - /* Port count includes the current port offset */ - if (port_offset == 0 || (port_offset + port_count - 1) > num_ports) - /* WTF? "Valid values are ‘1’ to MaxPorts" */ - return; - - /* cache usb2 port capabilities */ - if (major_revision < 0x03 && xhci->num_ext_caps < max_caps) - xhci->ext_caps[xhci->num_ext_caps++] = reg; - - port_offset--; - for (i = port_offset; i < (port_offset + port_count); i++) { - /* Duplicate entry. Ignore the port if the revisions differ. */ - if (xhci->port_array[i] != 0) { - dev_warn(xhci->dev, "Duplicate port entry, Ext Cap %p, port %u\n", - addr, i); - dev_warn(xhci->dev, "Port was marked as USB %u, duplicated as USB %u\n", - xhci->port_array[i], major_revision); - /* - * Only adjust the roothub port counts if we haven't - * found a similar duplicate. - */ - if (xhci->port_array[i] != major_revision && - xhci->port_array[i] != DUPLICATE_ENTRY) { - xhci->num_usb_ports--; - xhci->port_array[i] = DUPLICATE_ENTRY; - } - continue; - } - xhci->port_array[i] = major_revision; - xhci->num_usb_ports++; - } -} - -int xhci_hub_setup_ports(struct xhci_hcd *xhci) -{ - u32 offset, tmp_offset; - __le32 __iomem *addr, *tmp_addr; - unsigned int num_ports; - int i, cap_count = 0; - - offset = HCC_EXT_CAPS(xhci->hcc_params); - if (offset == 0) { - dev_err(xhci->dev, "No Extended Capability Registers\n"); - return -ENODEV; - } - - addr = &xhci->cap_regs->hc_capbase + offset; - - /* count extended protocol capability entries for later caching */ - tmp_addr = addr; - tmp_offset = offset; - do { - u32 cap_id = readl(tmp_addr); - - if (XHCI_EXT_CAPS_ID(cap_id) == XHCI_EXT_CAPS_PROTOCOL) - cap_count++; - - tmp_offset = XHCI_EXT_CAPS_NEXT(cap_id); - tmp_addr += tmp_offset; - } while (tmp_offset); - - num_ports = HCS_MAX_PORTS(xhci->hcs_params1); - xhci->port_array = xzalloc(num_ports * sizeof(*xhci->port_array)); - xhci->ext_caps = xzalloc(cap_count * sizeof(*xhci->ext_caps)); - - while (1) { - u32 cap_id = readl(addr); - - if (XHCI_EXT_CAPS_ID(cap_id) == XHCI_EXT_CAPS_PROTOCOL) - xhci_add_in_port(xhci, num_ports, addr, - (u8)XHCI_EXT_PORT_MAJOR(cap_id), - cap_count); - offset = XHCI_EXT_CAPS_NEXT(cap_id); - if (!offset || xhci->num_usb_ports == num_ports) - break; - addr += offset; - } - - if (xhci->num_usb_ports == 0) { - dev_err(xhci->dev, "No ports on the roothubs?\n"); - return -ENODEV; - } - - xhci->usb_ports = xzalloc(num_ports * sizeof(*xhci->usb_ports)); - for (i = 0; i < num_ports; i++) - xhci->usb_ports[i] = &xhci->op_regs->port_status_base + - NUM_PORT_REGS * i; - memcpy(&xhci->usb_info, &usb_rh_info, sizeof(usb_rh_info)); - xhci_setup_usb2_hub_descriptor(xhci); - - return 0; -} - -/* - * These bits are Read Only (RO) and should be saved and written to the - * registers: 0, 3, 10:13, 30 - * connect status, over-current status, port speed, and device removable. - * connect status and port speed are also sticky - meaning they're in - * the AUX well and they aren't changed by a hot, warm, or cold reset. - */ -#define XHCI_PORT_RO (PORT_CONNECT | PORT_OC | DEV_SPEED_MASK | \ - PORT_DEV_REMOVE) -/* - * These bits are RW; writing a 0 clears the bit, writing a 1 sets the bit: - * bits 5:8, 9, 14:15, 25:27 - * link state, port power, port indicator state, "wake on" enable state - */ -#define XHCI_PORT_RWS (PORT_PLS_MASK | PORT_POWER | PORT_LED_MASK | \ - PORT_WKCONN_E | PORT_WKDISC_E | PORT_WKOC_E) -/* - * These bits are RW; writing a 1 sets the bit, writing a 0 has no effect: - * bit 4 (port reset) - */ -#define XHCI_PORT_RW1S (PORT_RESET) -/* - * These bits are RW; writing a 1 clears the bit, writing a 0 has no effect: - * bits 1, 17, 18, 19, 20, 21, 22, 23 - * port enable/disable, and - * change bits: connect, PED, warm port reset changed (reserved 0 for USB 2.0), - * over-current, reset, link state, and L1 change - */ -#define XHCI_PORT_RW1CS (PORT_PE | PORT_CSC | PORT_PEC | PORT_WRC | \ - PORT_OCC | PORT_RC | PORT_PLC | PORT_CEC) -/* - * Bit 16 is RW, and writing a '1' to it causes the link state control to be - * latched in - */ -#define XHCI_PORT_RW (PORT_LINK_STROBE) -/* - * These bits are Reserved Zero (RsvdZ) and zero should be written to them: - * bits 2, 24, 28:31 - */ -#define XHCI_PORT_RZ (BIT(2) | BIT(24) | (0xf<<28)) - -/* - * Given a port state, this function returns a value that would result in the - * port being in the same state, if the value was written to the port status - * control register. - * Save Read Only (RO) bits and save read/write bits where - * writing a 0 clears the bit and writing a 1 sets the bit (RWS). - * For all other types (RW1S, RW1CS, RW, and RZ), writing a '0' has no effect. - */ -static u32 inline xhci_port_state_to_neutral(u32 state) -{ - /* Save read-only status and port state */ - return (state & XHCI_PORT_RO) | (state & XHCI_PORT_RWS); -} - -static int xhci_hub_finish_port_detach(struct xhci_hcd *xhci, int port) -{ - struct xhci_virtual_device *vdev, *temp; - union xhci_trb trb; - int ret; - - ret = xhci_wait_for_event(xhci, TRB_PORT_STATUS, &trb); - if (ret) - return ret; - - /* Tear-down any attached virtual devices */ - list_for_each_entry_safe(vdev, temp, &xhci->vdev_list, list) - if (vdev->udev && vdev->udev->portnr == port) - xhci_virtdev_detach(vdev); - - return 0; -} - -static int xhci_hub_finish_port_reset(struct xhci_hcd *xhci, int port) -{ - struct xhci_virtual_device *vdev; - union xhci_trb trb; - int ret; - - ret = xhci_wait_for_event(xhci, TRB_PORT_STATUS, &trb); - if (ret) - return ret; - - /* Reset any attached virtual devices */ - list_for_each_entry(vdev, &xhci->vdev_list, list) - if (vdev->udev && vdev->udev->portnr == port) - xhci_virtdev_reset(vdev); - - return 0; -} - -void xhci_hub_port_power(struct xhci_hcd *xhci, int port, - bool enable) -{ - u32 reg = readl(xhci->usb_ports[port]); - - reg = xhci_port_state_to_neutral(reg); - if (enable) - reg |= PORT_POWER; - else - reg &= ~PORT_POWER; - writel(reg, xhci->usb_ports[port]); -} - -static __maybe_unused int xhci_hub_port_warm_reset(struct xhci_hcd *xhci, int port) -{ - void __iomem *portsc = xhci->usb_ports[port]; - u32 reg; - - reg = xhci_port_state_to_neutral(readl(portsc)); - writel(reg | PORT_WR, portsc); - return xhci_handshake(portsc, PORT_RESET, 0, 10 * SECOND/USECOND); -} - -int xhci_hub_control(struct usb_device *dev, unsigned long pipe, - void *buffer, int length, struct devrequest *req) -{ - struct usb_host *host = dev->host; - struct xhci_hcd *xhci = to_xhci_hcd(host); - struct usb_root_hub_info *info; - __le32 __iomem **port_array; - int max_ports; - void *srcptr = NULL; - u8 tmpbuf[4]; - u16 typeReq; - int len, port, srclen = 0; - u32 reg; - - dev_dbg(xhci->dev, "%s req %u (%#x), type %u (%#x), value %u (%#x), index %u (%#x), length %u (%#x)\n", - __func__, req->request, req->request, - req->requesttype, req->requesttype, - le16_to_cpu(req->value), le16_to_cpu(req->value), - le16_to_cpu(req->index), le16_to_cpu(req->index), - le16_to_cpu(req->length), le16_to_cpu(req->length)); - - info = &xhci->usb_info; - port_array = xhci->usb_ports; - max_ports = xhci->num_usb_ports; - - typeReq = (req->requesttype << 8) | req->request; - switch (typeReq) { - case DeviceRequest | USB_REQ_GET_DESCRIPTOR: - dev_dbg(xhci->dev, "GetDeviceDescriptor %u\n", - le16_to_cpu(req->value) >> 8); - - switch (le16_to_cpu(req->value) >> 8) { - case USB_DT_DEVICE: - srcptr = &info->device; - srclen = info->device.bLength; - break; - case USB_DT_CONFIG: - srcptr = &info->config; - srclen = le16_to_cpu(info->config.wTotalLength); - break; - case USB_DT_STRING: - switch (le16_to_cpu(req->value) & 0xff) { - case 0: /* Language */ - srcptr = "\4\3\1\0"; - srclen = 4; - break; - case 1: /* Vendor: "barebox" */ - srcptr = "\20\3b\0a\0r\0e\0b\0o\0x\0"; - srclen = 16; - break; - case 2: /* Product: "USB 3.0 Root Hub" */ - srcptr = "\42\3U\0S\0B\0 \0\63\0.\0\60\0 \0R\0o\0o\0t\0 \0H\0u\0b"; - srclen = 34; - break; - default: - dev_warn(xhci->dev, "unknown string descriptor %x\n", - le16_to_cpu(req->value) >> 8); - goto unknown; - } - break; - } - break; - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - dev_dbg(xhci->dev, "SetDeviceConfiguration\n"); - /* Nothing to do */ - break; - case DeviceOutRequest | USB_REQ_SET_ADDRESS: - dev_dbg(xhci->dev, "SetDeviceAddress %u\n", - le16_to_cpu(req->value)); - - xhci->rootdev = le16_to_cpu(req->value); - break; - case GetHubDescriptor: - dev_dbg(xhci->dev, "GetHubDescriptor %u\n", - le16_to_cpu(req->value) >> 8); - - switch (le16_to_cpu(req->value) >> 8) { - case USB_DT_HUB: - srcptr = &info->hub; - srclen = info->hub.bLength; - break; - default: - dev_warn(xhci->dev, "unknown descriptor %x\n", - le16_to_cpu(req->value) >> 8); - goto unknown; - } - break; - case GetHubStatus: - dev_dbg(xhci->dev, "GetHubStatus\n"); - - /* No power source, over-current reported per port */ - tmpbuf[0] = 0x00; - tmpbuf[1] = 0x00; - srcptr = tmpbuf; - srclen = 2; - break; - case GetPortStatus: - dev_dbg(xhci->dev, "GetPortStatus %u\n", - le16_to_cpu(req->index)); - - memset(tmpbuf, 0, 4); - - port = le16_to_cpu(req->index); - if (!port || port > max_ports) - goto unknown; - port--; - - /* read PORTSC register */ - reg = readl(port_array[port]); - - if (reg & PORT_CONNECT) { - tmpbuf[0] |= USB_PORT_STAT_CONNECTION; - if (DEV_LOWSPEED(reg)) - tmpbuf[1] |= USB_PORT_STAT_LOW_SPEED >> 8; - else if (DEV_HIGHSPEED(reg)) - tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8; - } - if (reg & PORT_PE) - tmpbuf[0] |= USB_PORT_STAT_ENABLE; - if (reg & PORT_OC) - tmpbuf[0] |= USB_PORT_STAT_OVERCURRENT; - if (reg & PORT_RESET) - tmpbuf[0] |= USB_PORT_STAT_RESET; - /* USB 2.0 only */ - if ((reg & PORT_PLS_MASK) == XDEV_U3 && reg & PORT_POWER) - tmpbuf[0] |= USB_PORT_STAT_SUSPEND; - /* USB 2.0 only */ - if (reg & PORT_POWER) - tmpbuf[1] |= USB_PORT_STAT_POWER >> 8; - if (reg & PORT_CSC) - tmpbuf[2] |= USB_PORT_STAT_C_CONNECTION; - if (reg & PORT_PEC) - tmpbuf[2] |= USB_PORT_STAT_C_ENABLE; - if (reg & PORT_OCC) - tmpbuf[2] |= USB_PORT_STAT_C_OVERCURRENT; - if (reg & PORT_RC) - tmpbuf[2] |= USB_PORT_STAT_C_RESET; - srcptr = tmpbuf; - srclen = 4; - break; - case ClearPortFeature: - dev_dbg(xhci->dev, "ClearPortFeature %u %u\n", - le16_to_cpu(req->index), le16_to_cpu(req->value)); - - port = le16_to_cpu(req->index); - if (!port || port > max_ports) - goto unknown; - port--; - - reg = xhci_port_state_to_neutral(readl(port_array[port])); - - switch (le16_to_cpu(req->value)) { - case USB_PORT_FEAT_ENABLE: - reg &= ~PORT_PE; - break; - case USB_PORT_FEAT_POWER: - reg &= ~PORT_POWER; - break; - case USB_PORT_FEAT_C_CONNECTION: - reg |= PORT_CSC; - break; - case USB_PORT_FEAT_C_ENABLE: - reg |= PORT_PEC; - break; - case USB_PORT_FEAT_C_OVER_CURRENT: - reg |= PORT_OCC; - break; - case USB_PORT_FEAT_C_RESET: - reg |= PORT_RC; - break; - default: - dev_warn(xhci->dev, "unknown feature %u\n", - le16_to_cpu(req->value)); - goto unknown; - } - writel(reg, port_array[port]); - readl(port_array[port]); - - if ((reg & PORT_CONNECT) == 0 && - le16_to_cpu(req->value) == USB_PORT_FEAT_C_CONNECTION) - xhci_hub_finish_port_detach(xhci, port + 1); - - break; - case SetPortFeature: - dev_dbg(xhci->dev, "SetPortFeature %u %u\n", - le16_to_cpu(req->index), le16_to_cpu(req->value)); - - port = le16_to_cpu(req->index); - if (!port || port > max_ports) - goto unknown; - port--; - - reg = xhci_port_state_to_neutral(readl(port_array[port])); - - switch (le16_to_cpu(req->value)) { - case USB_PORT_FEAT_POWER: - reg |= PORT_POWER; - break; - case USB_PORT_FEAT_RESET: - reg |= PORT_RESET; - break; - default: - dev_warn(xhci->dev, "unknown feature %u\n", - le16_to_cpu(req->value)); - goto unknown; - } - writel(reg, port_array[port]); - readl(port_array[port]); - - if (le16_to_cpu(req->value) == USB_PORT_FEAT_RESET) - xhci_hub_finish_port_reset(xhci, port + 1); - - break; - default: - dev_warn(xhci->dev, "unknown root hub request %u (%#x) type %u (%#x)\n", - req->request, req->request, - req->requesttype, req->requesttype); - goto unknown; - } - - len = min3(srclen, (int)le16_to_cpu(req->length), length); - if (srcptr && len) - memcpy(buffer, srcptr, len); - dev->act_len = len; - dev->status = 0; - - return 0; - -unknown: - dev->act_len = 0; - dev->status = USB_ST_STALLED; - return -ENOTSUPP; -} diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c new file mode 100644 index 0000000000..e962bfde3f --- /dev/null +++ b/drivers/usb/host/xhci-mem.c @@ -0,0 +1,866 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * USB HOST XHCI Controller stack + * + * Based on xHCI host controller driver in linux-kernel + * by Sarah Sharp. + * + * Copyright (C) 2008 Intel Corp. + * Author: Sarah Sharp + * + * Copyright (C) 2013 Samsung Electronics Co.Ltd + * Authors: Vivek Gautam <gautam.vivek@samsung.com> + * Vikas Sajjan <vikas.sajjan@samsung.com> + */ + +#include <clock.h> +#include <common.h> +#include <dma.h> +#include <init.h> +#include <io.h> +#include <linux/err.h> +#include <linux/sizes.h> +#include <linux/usb/usb.h> +#include <linux/usb/xhci.h> +#include <asm/unaligned.h> + +#include "xhci.h" + +/* + * The memory handling for the xhci controller is done different + * in barebox than in the original U-Boot driver. + * All device memory is allocated with dma_alloc_coherent(), hence + * xhci_flush_cache()/xhci_inval_cache() can be no-ops. They are + * left here for reference if we ever want to change this behaviour. + * The only exception are the user buffers passed into the driver. These + * are synced with dma_sync_single_for_*() explicitly. + */ + +/** + * flushes the address passed till the length + * + * @param addr pointer to memory region to be flushed + * @param len the length of the cache line to be flushed + * @return none + */ +void xhci_flush_cache(uintptr_t addr, u32 len) +{ + BUG_ON((void *)addr == NULL || len == 0); +} + +/** + * invalidates the address passed till the length + * + * @param addr pointer to memory region to be invalidates + * @param len the length of the cache line to be invalidated + * @return none + */ +void xhci_inval_cache(uintptr_t addr, u32 len) +{ + BUG_ON((void *)addr == NULL || len == 0); +} + +/** + * Free memory allocated with xhci_malloc + * + * @param ptr pointer to memory to be freed + */ +static void xhci_free(struct xhci_ctrl *ctrl, void *ptr) +{ + /* + * These should be freed with dma_free_coherent(), but this + * call needs the size which we don't have here. Let this + * be a no-op for now. This is called in the shutdown path only + * anyway, so loosing memory here won't sum up. + */ + dev_dbg(ctrl->dev, "%s: 0x%p\n", __func__, ptr); +} + +/** + * alloc coherent memory for xhci + * + * @param size size of memory to be allocated + * @return allocates the memory and returns the aligned pointer + */ +static void *xhci_malloc(struct xhci_ctrl *ctrl, unsigned int size, dma_addr_t *dma_addr) +{ + void *ptr; + + ptr = dma_alloc_coherent(size, dma_addr); + if (!ptr) + return NULL; + + dev_dbg(ctrl->dev, "%s: 0x%p (size %d)\n", __func__, ptr, size); + + return ptr; +} + +/** + * frees the "segment" pointer passed + * + * @param ptr pointer to "segement" to be freed + * @return none + */ +static void xhci_segment_free(struct xhci_ctrl *ctrl, struct xhci_segment *seg) +{ + xhci_free(ctrl, seg->trbs); + seg->trbs = NULL; + + free(seg); +} + +/** + * frees the "ring" pointer passed + * + * @param ptr pointer to "ring" to be freed + * @return none + */ +static void xhci_ring_free(struct xhci_ctrl *ctrl, struct xhci_ring *ring) +{ + struct xhci_segment *seg; + struct xhci_segment *first_seg; + + first_seg = ring->first_seg; + seg = first_seg->next; + while (seg != first_seg) { + struct xhci_segment *next = seg->next; + xhci_segment_free(ctrl, seg); + seg = next; + } + xhci_segment_free(ctrl, first_seg); + + free(ring); +} + +/** + * Free the scratchpad buffer array and scratchpad buffers + * + * @ctrl host controller data structure + * @return none + */ +static void xhci_scratchpad_free(struct xhci_ctrl *ctrl) +{ + if (!ctrl->scratchpad) + return; + + ctrl->dcbaa->dev_context_ptrs[0] = 0; + + xhci_free(ctrl, ctrl->scratchpad->scratchpad); + xhci_free(ctrl, ctrl->scratchpad->sp_array); + free(ctrl->scratchpad); + ctrl->scratchpad = NULL; +} + +/** + * frees the "xhci_container_ctx" pointer passed + * + * @param ptr pointer to "xhci_container_ctx" to be freed + * @return none + */ +static void xhci_free_container_ctx(struct xhci_ctrl *ctrl, + struct xhci_container_ctx *ctx) +{ + xhci_free(ctrl, ctx->bytes); + free(ctx); +} + +/** + * frees the virtual devices for "xhci_ctrl" pointer passed + * + * @param ptr pointer to "xhci_ctrl" whose virtual devices are to be freed + * @return none + */ +static void xhci_free_virt_devices(struct xhci_ctrl *ctrl) +{ + int i; + int slot_id; + struct xhci_virt_device *virt_dev; + + /* + * refactored here to loop through all virt_dev + * Slot ID 0 is reserved + */ + for (slot_id = 0; slot_id < MAX_HC_SLOTS; slot_id++) { + virt_dev = ctrl->devs[slot_id]; + if (!virt_dev) + continue; + + ctrl->dcbaa->dev_context_ptrs[slot_id] = 0; + + for (i = 0; i < 31; ++i) + if (virt_dev->eps[i].ring) + xhci_ring_free(ctrl, virt_dev->eps[i].ring); + + if (virt_dev->in_ctx) + xhci_free_container_ctx(ctrl, virt_dev->in_ctx); + if (virt_dev->out_ctx) + xhci_free_container_ctx(ctrl, virt_dev->out_ctx); + + free(virt_dev); + /* make sure we are pointing to NULL */ + ctrl->devs[slot_id] = NULL; + } +} + +/** + * frees all the memory allocated + * + * @param ptr pointer to "xhci_ctrl" to be cleaned up + * @return none + */ +void xhci_cleanup(struct xhci_ctrl *ctrl) +{ + xhci_ring_free(ctrl, ctrl->event_ring); + xhci_ring_free(ctrl, ctrl->cmd_ring); + xhci_scratchpad_free(ctrl); + xhci_free_virt_devices(ctrl); + xhci_free(ctrl, ctrl->erst.entries); + xhci_free(ctrl, ctrl->dcbaa); + free(ctrl->bounce_buffer); +} + +/** + * Make the prev segment point to the next segment. + * Change the last TRB in the prev segment to be a Link TRB which points to the + * address of the next segment. The caller needs to set any Link TRB + * related flags, such as End TRB, Toggle Cycle, and no snoop. + * + * @param prev pointer to the previous segment + * @param next pointer to the next segment + * @param link_trbs flag to indicate whether to link the trbs or NOT + * @return none + */ +static void xhci_link_segments(struct xhci_segment *prev, + struct xhci_segment *next, bool link_trbs) +{ + u32 val; + + if (!prev || !next) + return; + prev->next = next; + if (link_trbs) { + prev->trbs[TRBS_PER_SEGMENT-1].link.segment_ptr = + cpu_to_le64(next->dma); + + /* + * Set the last TRB in the segment to + * have a TRB type ID of Link TRB + */ + val = le32_to_cpu(prev->trbs[TRBS_PER_SEGMENT-1].link.control); + val &= ~TRB_TYPE_BITMASK; + val |= TRB_TYPE(TRB_LINK); + prev->trbs[TRBS_PER_SEGMENT-1].link.control = cpu_to_le32(val); + } +} + +/** + * Initialises the Ring's enqueue,dequeue,enq_seg pointers + * + * @param ring pointer to the RING to be intialised + * @return none + */ +static void xhci_initialize_ring_info(struct xhci_ring *ring) +{ + /* + * The ring is empty, so the enqueue pointer == dequeue pointer + */ + ring->enqueue = ring->first_seg->trbs; + ring->enq_seg = ring->first_seg; + ring->dequeue = ring->enqueue; + ring->deq_seg = ring->first_seg; + + /* + * The ring is initialized to 0. The producer must write 1 to the + * cycle bit to handover ownership of the TRB, so PCS = 1. + * The consumer must compare CCS to the cycle bit to + * check ownership, so CCS = 1. + */ + ring->cycle_state = 1; +} + +/** + * Allocates a generic ring segment from the ring pool, sets the dma address, + * initializes the segment to zero, and sets the private next pointer to NULL. + * Section 4.11.1.1: + * "All components of all Command and Transfer TRBs shall be initialized to '0'" + * + * @param none + * @return pointer to the newly allocated SEGMENT + */ +static struct xhci_segment *xhci_segment_alloc(struct xhci_ctrl *ctrl) +{ + struct xhci_segment *seg; + + seg = xzalloc(sizeof(*seg)); + + seg->trbs = xhci_malloc(ctrl, SEGMENT_SIZE, &seg->dma); + + return seg; +} + +/** + * Create a new ring with zero or more segments. + * TODO: current code only uses one-time-allocated single-segment rings + * of 1KB anyway, so we might as well get rid of all the segment and + * linking code (and maybe increase the size a bit, e.g. 4KB). + * + * + * Link each segment together into a ring. + * Set the end flag and the cycle toggle bit on the last segment. + * See section 4.9.2 and figures 15 and 16 of XHCI spec rev1.0. + * + * @param num_segs number of segments in the ring + * @param link_trbs flag to indicate whether to link the trbs or NOT + * @return pointer to the newly created RING + */ +struct xhci_ring *xhci_ring_alloc(struct xhci_ctrl *ctrl, + unsigned int num_segs, bool link_trbs) +{ + struct xhci_ring *ring; + struct xhci_segment *prev; + + ring = xmalloc(sizeof(*ring)); + + if (num_segs == 0) + return ring; + + ring->first_seg = xhci_segment_alloc(ctrl); + BUG_ON(!ring->first_seg); + + num_segs--; + + prev = ring->first_seg; + while (num_segs > 0) { + struct xhci_segment *next; + + next = xhci_segment_alloc(ctrl); + BUG_ON(!next); + + xhci_link_segments(prev, next, link_trbs); + + prev = next; + num_segs--; + } + xhci_link_segments(prev, ring->first_seg, link_trbs); + if (link_trbs) { + /* See section 4.9.2.1 and 6.4.4.1 */ + prev->trbs[TRBS_PER_SEGMENT-1].link.control |= + cpu_to_le32(LINK_TOGGLE); + } + xhci_initialize_ring_info(ring); + + return ring; +} + +/** + * Set up the scratchpad buffer array and scratchpad buffers + * + * @ctrl host controller data structure + * @return -ENOMEM if buffer allocation fails, 0 on success + */ +static int xhci_scratchpad_alloc(struct xhci_ctrl *ctrl) +{ + struct xhci_hccr *hccr = ctrl->hccr; + struct xhci_hcor *hcor = ctrl->hcor; + struct xhci_scratchpad *scratchpad; + dma_addr_t val_64; + int num_sp; + uint32_t page_size; + void *buf; + int i; + + num_sp = HCS_MAX_SCRATCHPAD(xhci_readl(&hccr->cr_hcsparams2)); + if (!num_sp) + return 0; + + scratchpad = malloc(sizeof(*scratchpad)); + if (!scratchpad) + goto fail_sp; + ctrl->scratchpad = scratchpad; + + scratchpad->sp_array = xhci_malloc(ctrl, num_sp * sizeof(u64), &val_64); + if (!scratchpad->sp_array) + goto fail_sp2; + + ctrl->dcbaa->dev_context_ptrs[0] = cpu_to_le64(val_64); + + xhci_flush_cache((uintptr_t)&ctrl->dcbaa->dev_context_ptrs[0], + sizeof(ctrl->dcbaa->dev_context_ptrs[0])); + + page_size = xhci_readl(&hcor->or_pagesize) & 0xffff; + for (i = 0; i < 16; i++) { + if ((0x1 & page_size) != 0) + break; + page_size = page_size >> 1; + } + BUG_ON(i == 16); + + page_size = 1 << (i + 12); + buf = xhci_malloc(ctrl, num_sp * page_size, &val_64); + if (!buf) + goto fail_sp3; + + xhci_flush_cache((uintptr_t)buf, num_sp * page_size); + + scratchpad->scratchpad = buf; + for (i = 0; i < num_sp; i++) { + scratchpad->sp_array[i] = cpu_to_le64(val_64); + val_64 += page_size; + } + + xhci_flush_cache((uintptr_t)scratchpad->sp_array, + sizeof(u64) * num_sp); + + return 0; + +fail_sp3: + xhci_free(ctrl, scratchpad->sp_array); + +fail_sp2: + xhci_free(ctrl, scratchpad); + ctrl->scratchpad = NULL; + +fail_sp: + return -ENOMEM; +} + +/** + * Allocates the Container context + * + * @param ctrl Host controller data structure + * @param type type of XHCI Container Context + * @return NULL if failed else pointer to the context on success + */ +static struct xhci_container_ctx + *xhci_alloc_container_ctx(struct xhci_ctrl *ctrl, int type) +{ + struct xhci_container_ctx *ctx; + + ctx = xmalloc(sizeof(struct xhci_container_ctx)); + + BUG_ON((type != XHCI_CTX_TYPE_DEVICE) && (type != XHCI_CTX_TYPE_INPUT)); + ctx->type = type; + ctx->size = (MAX_EP_CTX_NUM + 1) * + CTX_SIZE(xhci_readl(&ctrl->hccr->cr_hccparams)); + if (type == XHCI_CTX_TYPE_INPUT) + ctx->size += CTX_SIZE(xhci_readl(&ctrl->hccr->cr_hccparams)); + + ctx->bytes = xhci_malloc(ctrl, ctx->size, &ctx->dma); + + return ctx; +} + +/** + * Allocating virtual device + * + * @param udev pointer to USB deivce structure + * @return 0 on success else -1 on failure + */ +int xhci_alloc_virt_device(struct xhci_ctrl *ctrl, unsigned int slot_id) +{ + u64 byte_64 = 0; + struct xhci_virt_device *virt_dev; + + /* Slot ID 0 is reserved */ + if (ctrl->devs[slot_id]) { + dev_err(ctrl->dev, "Virt dev for slot[%d] already allocated\n", slot_id); + return -EEXIST; + } + + ctrl->devs[slot_id] = malloc(sizeof(struct xhci_virt_device)); + + if (!ctrl->devs[slot_id]) { + dev_err(ctrl->dev, "Failed to allocate virtual device\n"); + return -ENOMEM; + } + + memset(ctrl->devs[slot_id], 0, sizeof(struct xhci_virt_device)); + virt_dev = ctrl->devs[slot_id]; + + /* Allocate the (output) device context that will be used in the HC. */ + virt_dev->out_ctx = xhci_alloc_container_ctx(ctrl, + XHCI_CTX_TYPE_DEVICE); + if (!virt_dev->out_ctx) { + dev_err(ctrl->dev, "Failed to allocate out context for virt dev\n"); + return -ENOMEM; + } + + /* Allocate the (input) device context for address device command */ + virt_dev->in_ctx = xhci_alloc_container_ctx(ctrl, + XHCI_CTX_TYPE_INPUT); + if (!virt_dev->in_ctx) { + dev_err(ctrl->dev, "Failed to allocate in context for virt dev\n"); + return -ENOMEM; + } + + /* Allocate endpoint 0 ring */ + virt_dev->eps[0].ring = xhci_ring_alloc(ctrl, 1, true); + + byte_64 = virt_dev->out_ctx->dma; + + /* Point to output device context in dcbaa. */ + ctrl->dcbaa->dev_context_ptrs[slot_id] = cpu_to_le64(byte_64); + + xhci_flush_cache((uintptr_t)&ctrl->dcbaa->dev_context_ptrs[slot_id], + sizeof(__le64)); + return 0; +} + +/** + * Allocates the necessary data structures + * for XHCI host controller + * + * @param ctrl Host controller data structure + * @param hccr pointer to HOST Controller Control Registers + * @param hcor pointer to HOST Controller Operational Registers + * @return 0 if successful else -1 on failure + */ +int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr, + struct xhci_hcor *hcor) +{ + dma_addr_t dma; + uint64_t val_64; + uint64_t trb_64; + uint32_t val; + uint64_t deq; + int i; + struct xhci_segment *seg; + + /* DCBAA initialization */ + ctrl->dcbaa = xhci_malloc(ctrl, sizeof(struct xhci_device_context_array), + &dma); + ctrl->dcbaa->dma = dma; + + /* Set the pointer in DCBAA register */ + xhci_writeq(&hcor->or_dcbaap, dma); + + /* Command ring control pointer register initialization */ + ctrl->cmd_ring = xhci_ring_alloc(ctrl, 1, true); + + /* Set the address in the Command Ring Control register */ + trb_64 = ctrl->cmd_ring->first_seg->dma; + val_64 = xhci_readq(&hcor->or_crcr); + val_64 = (val_64 & (u64) CMD_RING_RSVD_BITS) | + (trb_64 & (u64) ~CMD_RING_RSVD_BITS) | + ctrl->cmd_ring->cycle_state; + xhci_writeq(&hcor->or_crcr, val_64); + + /* write the address of db register */ + val = xhci_readl(&hccr->cr_dboff); + val &= DBOFF_MASK; + ctrl->dba = (struct xhci_doorbell_array *)((char *)hccr + val); + + /* write the address of runtime register */ + val = xhci_readl(&hccr->cr_rtsoff); + val &= RTSOFF_MASK; + ctrl->run_regs = (struct xhci_run_regs *)((char *)hccr + val); + + /* writting the address of ir_set structure */ + ctrl->ir_set = &ctrl->run_regs->ir_set[0]; + + /* Event ring does not maintain link TRB */ + ctrl->event_ring = xhci_ring_alloc(ctrl, ERST_NUM_SEGS, false); + ctrl->erst.entries = xhci_malloc(ctrl, sizeof(struct xhci_erst_entry) * + ERST_NUM_SEGS, &ctrl->erst.erst_dma_addr); + + ctrl->erst.num_entries = ERST_NUM_SEGS; + + for (val = 0, seg = ctrl->event_ring->first_seg; + val < ERST_NUM_SEGS; + val++) { + struct xhci_erst_entry *entry = &ctrl->erst.entries[val]; + + trb_64 = seg->dma; + entry->seg_addr = cpu_to_le64(trb_64); + entry->seg_size = cpu_to_le32(TRBS_PER_SEGMENT); + entry->rsvd = 0; + seg = seg->next; + } + xhci_flush_cache((uintptr_t)ctrl->erst.entries, + ERST_NUM_SEGS * sizeof(struct xhci_erst_entry)); + + deq = xhci_trb_virt_to_dma(ctrl->event_ring->deq_seg, + ctrl->event_ring->dequeue); + + /* Update HC event ring dequeue pointer */ + xhci_writeq(&ctrl->ir_set->erst_dequeue, + (u64)deq & (u64)~ERST_PTR_MASK); + + /* set ERST count with the number of entries in the segment table */ + val = xhci_readl(&ctrl->ir_set->erst_size); + val &= ERST_SIZE_MASK; + val |= ERST_NUM_SEGS; + xhci_writel(&ctrl->ir_set->erst_size, val); + + /* this is the event ring segment table pointer */ + val_64 = xhci_readq(&ctrl->ir_set->erst_base); + val_64 &= ERST_PTR_MASK; + val_64 |= ctrl->erst.erst_dma_addr & ~ERST_PTR_MASK; + + xhci_writeq(&ctrl->ir_set->erst_base, val_64); + + /* set up the scratchpad buffer array and scratchpad buffers */ + xhci_scratchpad_alloc(ctrl); + + ctrl->bounce_buffer = xmemalign(SZ_64K, SZ_64K); + + /* initializing the virtual devices to NULL */ + for (i = 0; i < MAX_HC_SLOTS; ++i) + ctrl->devs[i] = NULL; + + /* + * Just Zero'ing this register completely, + * or some spurious Device Notification Events + * might screw things here. + */ + xhci_writel(&hcor->or_dnctrl, 0x0); + + return 0; +} + +/** + * Give the input control context for the passed container context + * + * @param ctx pointer to the context + * @return pointer to the Input control context data + */ +struct xhci_input_control_ctx + *xhci_get_input_control_ctx(struct xhci_container_ctx *ctx) +{ + BUG_ON(ctx->type != XHCI_CTX_TYPE_INPUT); + return (struct xhci_input_control_ctx *)ctx->bytes; +} + +/** + * Give the slot context for the passed container context + * + * @param ctrl Host controller data structure + * @param ctx pointer to the context + * @return pointer to the slot control context data + */ +struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_ctrl *ctrl, + struct xhci_container_ctx *ctx) +{ + if (ctx->type == XHCI_CTX_TYPE_DEVICE) + return (struct xhci_slot_ctx *)ctx->bytes; + + return (struct xhci_slot_ctx *) + (ctx->bytes + CTX_SIZE(xhci_readl(&ctrl->hccr->cr_hccparams))); +} + +/** + * Gets the EP context from based on the ep_index + * + * @param ctrl Host controller data structure + * @param ctx context container + * @param ep_index index of the endpoint + * @return pointer to the End point context + */ +struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_ctrl *ctrl, + struct xhci_container_ctx *ctx, + unsigned int ep_index) +{ + /* increment ep index by offset of start of ep ctx array */ + ep_index++; + if (ctx->type == XHCI_CTX_TYPE_INPUT) + ep_index++; + + return (struct xhci_ep_ctx *) + (ctx->bytes + + (ep_index * CTX_SIZE(xhci_readl(&ctrl->hccr->cr_hccparams)))); +} + +/** + * Copy output xhci_ep_ctx to the input xhci_ep_ctx copy. + * Useful when you want to change one particular aspect of the endpoint + * and then issue a configure endpoint command. + * + * @param ctrl Host controller data structure + * @param in_ctx contains the input context + * @param out_ctx contains the input context + * @param ep_index index of the end point + * @return none + */ +void xhci_endpoint_copy(struct xhci_ctrl *ctrl, + struct xhci_container_ctx *in_ctx, + struct xhci_container_ctx *out_ctx, + unsigned int ep_index) +{ + struct xhci_ep_ctx *out_ep_ctx; + struct xhci_ep_ctx *in_ep_ctx; + + out_ep_ctx = xhci_get_ep_ctx(ctrl, out_ctx, ep_index); + in_ep_ctx = xhci_get_ep_ctx(ctrl, in_ctx, ep_index); + + in_ep_ctx->ep_info = out_ep_ctx->ep_info; + in_ep_ctx->ep_info2 = out_ep_ctx->ep_info2; + in_ep_ctx->deq = out_ep_ctx->deq; + in_ep_ctx->tx_info = out_ep_ctx->tx_info; +} + +/** + * Copy output xhci_slot_ctx to the input xhci_slot_ctx. + * Useful when you want to change one particular aspect of the endpoint + * and then issue a configure endpoint command. + * Only the context entries field matters, but + * we'll copy the whole thing anyway. + * + * @param ctrl Host controller data structure + * @param in_ctx contains the inpout context + * @param out_ctx contains the inpout context + * @return none + */ +void xhci_slot_copy(struct xhci_ctrl *ctrl, struct xhci_container_ctx *in_ctx, + struct xhci_container_ctx *out_ctx) +{ + struct xhci_slot_ctx *in_slot_ctx; + struct xhci_slot_ctx *out_slot_ctx; + + in_slot_ctx = xhci_get_slot_ctx(ctrl, in_ctx); + out_slot_ctx = xhci_get_slot_ctx(ctrl, out_ctx); + + in_slot_ctx->dev_info = out_slot_ctx->dev_info; + in_slot_ctx->dev_info2 = out_slot_ctx->dev_info2; + in_slot_ctx->tt_info = out_slot_ctx->tt_info; + in_slot_ctx->dev_state = out_slot_ctx->dev_state; +} + +/** + * Setup an xHCI virtual device for a Set Address command + * + * @param udev pointer to the Device Data Structure + * @return returns negative value on failure else 0 on success + */ +void xhci_setup_addressable_virt_dev(struct xhci_ctrl *ctrl, + struct usb_device *udev, int hop_portnr) +{ + struct xhci_virt_device *virt_dev; + struct xhci_ep_ctx *ep0_ctx; + struct xhci_slot_ctx *slot_ctx; + u32 port_num = 0; + u64 trb_64 = 0; + int slot_id = udev->slot_id; + int speed = udev->speed; + int route = 0; + struct usb_device *dev = udev; + struct usb_hub_device *hub; + + virt_dev = ctrl->devs[slot_id]; + + BUG_ON(!virt_dev); + + /* Extract the EP0 and Slot Ctrl */ + ep0_ctx = xhci_get_ep_ctx(ctrl, virt_dev->in_ctx, 0); + slot_ctx = xhci_get_slot_ctx(ctrl, virt_dev->in_ctx); + + /* Only the control endpoint is valid - one endpoint context */ + slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(1)); + + /* Calculate the route string for this device */ + port_num = dev->portnr; + while (!usb_hub_is_root_hub(dev)) { + /* + * Each hub in the topology is expected to have no more than + * 15 ports in order for the route string of a device to be + * unique. SuperSpeed hubs are restricted to only having 15 + * ports, but FS/LS/HS hubs are not. The xHCI specification + * says that if the port number the device is greater than 15, + * that portion of the route string shall be set to 15. + */ + if (port_num > 15) + port_num = 15; + route |= port_num << (dev->level * 4); + dev = dev->parent; + port_num = dev->portnr; + } + + dev_dbg(&udev->dev, "route string 0x%x\n", route); + + slot_ctx->dev_info |= cpu_to_le32(route); + + switch (speed) { + case USB_SPEED_SUPER: + slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_SS); + break; + case USB_SPEED_HIGH: + slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_HS); + break; + case USB_SPEED_FULL: + slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_FS); + break; + case USB_SPEED_LOW: + slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_LS); + break; + default: + /* Speed was set earlier, this shouldn't happen. */ + BUG(); + } + + /* Set up TT fields to support FS/LS devices */ + if (speed == USB_SPEED_LOW || speed == USB_SPEED_FULL) { + dev = udev; + do { + port_num = dev->portnr; + if (usb_hub_is_root_hub(dev)) + break; + dev = dev->parent; + } while (dev->speed != USB_SPEED_HIGH); + + if (!usb_hub_is_root_hub(dev)) { + hub = dev->hub; + if (hub->tt.multi) + slot_ctx->dev_info |= cpu_to_le32(DEV_MTT); + slot_ctx->tt_info |= cpu_to_le32(TT_PORT(port_num)); + slot_ctx->tt_info |= cpu_to_le32(TT_SLOT(dev->slot_id)); + } + } + + port_num = hop_portnr; + dev_dbg(&udev->dev, "port_num = %d\n", port_num); + + slot_ctx->dev_info2 |= + cpu_to_le32(((port_num & ROOT_HUB_PORT_MASK) << + ROOT_HUB_PORT_SHIFT)); + + /* Step 4 - ring already allocated */ + /* Step 5 */ + ep0_ctx->ep_info2 = cpu_to_le32(EP_TYPE(CTRL_EP)); + dev_dbg(&udev->dev, "SPEED = %d\n", speed); + + switch (speed) { + case USB_SPEED_SUPER: + ep0_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(512)); + dev_dbg(&udev->dev, "Setting Packet size = 512bytes\n"); + break; + case USB_SPEED_HIGH: + /* USB core guesses at a 64-byte max packet first for FS devices */ + case USB_SPEED_FULL: + ep0_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(64)); + dev_dbg(&udev->dev, "Setting Packet size = 64bytes\n"); + break; + case USB_SPEED_LOW: + ep0_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(8)); + dev_dbg(&udev->dev, "Setting Packet size = 8bytes\n"); + break; + default: + /* New speed? */ + BUG(); + } + + /* EP 0 can handle "burst" sizes of 1, so Max Burst Size field is 0 */ + ep0_ctx->ep_info2 |= cpu_to_le32(MAX_BURST(0) | ERROR_COUNT(3)); + + trb_64 = virt_dev->eps[0].ring->first_seg->dma; + ep0_ctx->deq = cpu_to_le64(trb_64 | virt_dev->eps[0].ring->cycle_state); + + /* + * xHCI spec 6.2.3: + * software shall set 'Average TRB Length' to 8 for control endpoints. + */ + ep0_ctx->tx_info = cpu_to_le32(EP_AVG_TRB_LENGTH(8)); + + /* Steps 7 and 8 were done in xhci_alloc_virt_device() */ + + xhci_flush_cache((uintptr_t)ep0_ctx, sizeof(struct xhci_ep_ctx)); + xhci_flush_cache((uintptr_t)slot_ctx, sizeof(struct xhci_slot_ctx)); +} diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c deleted file mode 100644 index 7a9315a0b6..0000000000 --- a/drivers/usb/host/xhci-pci.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * PCI driver for xHCI controllers - * - * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#include <common.h> -#include <init.h> -#include <io.h> -#include <linux/pci.h> -#include <usb/xhci.h> - -static int xhci_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) -{ - struct xhci_data data = {}; - - pci_enable_device(pdev); - pci_set_master(pdev); - - data.regs = pci_iomap(pdev, 0); - - return xhci_register(&pdev->dev, &data); -} - -static DEFINE_PCI_DEVICE_TABLE(xhci_pci_tbl) = { - /* handle any USB 3.0 xHCI controller */ - { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_XHCI, ~0), }, - { }, -}; - -static struct pci_driver xhci_pci_driver = { - .name = "xHCI PCI", - .id_table = xhci_pci_tbl, - .probe = xhci_pci_probe, -}; -device_pci_driver(xhci_pci_driver); diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c new file mode 100644 index 0000000000..691d9c7463 --- /dev/null +++ b/drivers/usb/host/xhci-ring.c @@ -0,0 +1,1078 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * USB HOST XHCI Controller stack + * + * Based on xHCI host controller driver in linux-kernel + * by Sarah Sharp. + * + * Copyright (C) 2008 Intel Corp. + * Author: Sarah Sharp + * + * Copyright (C) 2013 Samsung Electronics Co.Ltd + * Authors: Vivek Gautam <gautam.vivek@samsung.com> + * Vikas Sajjan <vikas.sajjan@samsung.com> + */ +#include <clock.h> +#include <common.h> +#include <dma.h> +#include <init.h> +#include <io.h> +#include <linux/err.h> +#include <linux/sizes.h> +#include <linux/usb/usb.h> +#include <linux/usb/xhci.h> +#include <asm/unaligned.h> + +#include "xhci.h" + +/* + * Returns zero if the TRB isn't in this segment, otherwise it returns the DMA + * address of the TRB. + */ +dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, + union xhci_trb *trb) +{ + unsigned long segment_offset; + + BUG_ON(!seg || !trb || trb < seg->trbs); + + /* offset in TRBs */ + segment_offset = trb - seg->trbs; + BUG_ON(segment_offset >= TRBS_PER_SEGMENT); + + return seg->dma + (segment_offset * sizeof(*trb)); +} + +/** + * Is this TRB a link TRB or was the last TRB the last TRB in this event ring + * segment? I.e. would the updated event TRB pointer step off the end of the + * event seg ? + * + * @param ctrl Host controller data structure + * @param ring pointer to the ring + * @param seg poniter to the segment to which TRB belongs + * @param trb poniter to the ring trb + * @return 1 if this TRB a link TRB else 0 + */ +static int last_trb(struct xhci_ctrl *ctrl, struct xhci_ring *ring, + struct xhci_segment *seg, union xhci_trb *trb) +{ + if (ring == ctrl->event_ring) + return trb == &seg->trbs[TRBS_PER_SEGMENT]; + else + return TRB_TYPE_LINK_LE32(trb->link.control); +} + +/** + * Does this link TRB point to the first segment in a ring, + * or was the previous TRB the last TRB on the last segment in the ERST? + * + * @param ctrl Host controller data structure + * @param ring pointer to the ring + * @param seg poniter to the segment to which TRB belongs + * @param trb poniter to the ring trb + * @return 1 if this TRB is the last TRB on the last segment else 0 + */ +static bool last_trb_on_last_seg(struct xhci_ctrl *ctrl, + struct xhci_ring *ring, + struct xhci_segment *seg, + union xhci_trb *trb) +{ + if (ring == ctrl->event_ring) + return ((trb == &seg->trbs[TRBS_PER_SEGMENT]) && + (seg->next == ring->first_seg)); + else + return le32_to_cpu(trb->link.control) & LINK_TOGGLE; +} + +/** + * See Cycle bit rules. SW is the consumer for the event ring only. + * Don't make a ring full of link TRBs. That would be dumb and this would loop. + * + * If we've just enqueued a TRB that is in the middle of a TD (meaning the + * chain bit is set), then set the chain bit in all the following link TRBs. + * If we've enqueued the last TRB in a TD, make sure the following link TRBs + * have their chain bit cleared (so that each Link TRB is a separate TD). + * + * Section 6.4.4.1 of the 0.95 spec says link TRBs cannot have the chain bit + * set, but other sections talk about dealing with the chain bit set. This was + * fixed in the 0.96 specification errata, but we have to assume that all 0.95 + * xHCI hardware can't handle the chain bit being cleared on a link TRB. + * + * @param ctrl Host controller data structure + * @param ring pointer to the ring + * @param more_trbs_coming flag to indicate whether more trbs + * are expected or NOT. + * Will you enqueue more TRBs before calling + * prepare_ring()? + * @return none + */ +static void inc_enq(struct xhci_ctrl *ctrl, struct xhci_ring *ring, + bool more_trbs_coming) +{ + u32 chain; + union xhci_trb *next; + + chain = le32_to_cpu(ring->enqueue->generic.field[3]) & TRB_CHAIN; + next = ++(ring->enqueue); + + /* + * Update the dequeue pointer further if that was a link TRB or we're at + * the end of an event ring segment (which doesn't have link TRBS) + */ + while (last_trb(ctrl, ring, ring->enq_seg, next)) { + if (ring != ctrl->event_ring) { + /* + * If the caller doesn't plan on enqueueing more + * TDs before ringing the doorbell, then we + * don't want to give the link TRB to the + * hardware just yet. We'll give the link TRB + * back in prepare_ring() just before we enqueue + * the TD at the top of the ring. + */ + if (!chain && !more_trbs_coming) + break; + + /* + * If we're not dealing with 0.95 hardware or + * isoc rings on AMD 0.96 host, + * carry over the chain bit of the previous TRB + * (which may mean the chain bit is cleared). + */ + next->link.control &= cpu_to_le32(~TRB_CHAIN); + next->link.control |= cpu_to_le32(chain); + + next->link.control ^= cpu_to_le32(TRB_CYCLE); + xhci_flush_cache((uintptr_t)next, + sizeof(union xhci_trb)); + } + /* Toggle the cycle bit after the last ring segment. */ + if (last_trb_on_last_seg(ctrl, ring, + ring->enq_seg, next)) + ring->cycle_state = (ring->cycle_state ? 0 : 1); + + ring->enq_seg = ring->enq_seg->next; + ring->enqueue = ring->enq_seg->trbs; + next = ring->enqueue; + } +} + +/** + * See Cycle bit rules. SW is the consumer for the event ring only. + * Don't make a ring full of link TRBs. That would be dumb and this would loop. + * + * @param ctrl Host controller data structure + * @param ring Ring whose Dequeue TRB pointer needs to be incremented. + * return none + */ +static void inc_deq(struct xhci_ctrl *ctrl, struct xhci_ring *ring) +{ + do { + /* + * Update the dequeue pointer further if that was a link TRB or + * we're at the end of an event ring segment (which doesn't have + * link TRBS) + */ + if (last_trb(ctrl, ring, ring->deq_seg, ring->dequeue)) { + if (ring == ctrl->event_ring && + last_trb_on_last_seg(ctrl, ring, + ring->deq_seg, ring->dequeue)) { + ring->cycle_state = (ring->cycle_state ? 0 : 1); + } + ring->deq_seg = ring->deq_seg->next; + ring->dequeue = ring->deq_seg->trbs; + } else { + ring->dequeue++; + } + } while (last_trb(ctrl, ring, ring->deq_seg, ring->dequeue)); +} + +/** + * Generic function for queueing a TRB on a ring. + * The caller must have checked to make sure there's room on the ring. + * + * @param more_trbs_coming: Will you enqueue more TRBs before calling + * prepare_ring()? + * @param ctrl Host controller data structure + * @param ring pointer to the ring + * @param more_trbs_coming flag to indicate whether more trbs + * @param trb_fields pointer to trb field array containing TRB contents + * @return pointer to the enqueued trb + */ +static dma_addr_t queue_trb(struct xhci_ctrl *ctrl, struct xhci_ring *ring, + bool more_trbs_coming, unsigned int *trb_fields) +{ + struct xhci_generic_trb *trb; + dma_addr_t addr; + int i; + + trb = &ring->enqueue->generic; + + for (i = 0; i < 4; i++) + trb->field[i] = cpu_to_le32(trb_fields[i]); + + xhci_flush_cache((uintptr_t)trb, sizeof(struct xhci_generic_trb)); + + addr = xhci_trb_virt_to_dma(ring->enq_seg, (union xhci_trb *)trb); + + inc_enq(ctrl, ring, more_trbs_coming); + + return addr; +} + +/** + * Does various checks on the endpoint ring, and makes it ready + * to queue num_trbs. + * + * @param ctrl Host controller data structure + * @param ep_ring pointer to the EP Transfer Ring + * @param ep_state State of the End Point + * @return error code in case of invalid ep_state, 0 on success + */ +static int prepare_ring(struct xhci_ctrl *ctrl, struct xhci_ring *ep_ring, + u32 ep_state) +{ + union xhci_trb *next = ep_ring->enqueue; + + /* Make sure the endpoint has been added to xHC schedule */ + switch (ep_state) { + case EP_STATE_DISABLED: + /* + * USB core changed config/interfaces without notifying us, + * or hardware is reporting the wrong state. + */ + dev_err(ctrl->dev, "urb submitted to disabled ep\n"); + return -ENOENT; + case EP_STATE_ERROR: + dev_err(ctrl->dev, "waiting for error on ep to be cleared\n"); + return -EINVAL; + case EP_STATE_HALTED: + dev_err(ctrl->dev, "halted endpoint, not queueing URB.\n"); + return -EINVAL; + case EP_STATE_STOPPED: + case EP_STATE_RUNNING: + dev_dbg(ctrl->dev, "EP STATE RUNNING.\n"); + break; + default: + dev_err(ctrl->dev, "unknown endpoint state for ep\n"); + return -EINVAL; + } + + while (last_trb(ctrl, ep_ring, ep_ring->enq_seg, next)) { + /* + * If we're not dealing with 0.95 hardware or isoc rings + * on AMD 0.96 host, clear the chain bit. + */ + next->link.control &= cpu_to_le32(~TRB_CHAIN); + + next->link.control ^= cpu_to_le32(TRB_CYCLE); + + xhci_flush_cache((uintptr_t)next, sizeof(union xhci_trb)); + + /* Toggle the cycle bit after the last ring segment. */ + if (last_trb_on_last_seg(ctrl, ep_ring, + ep_ring->enq_seg, next)) + ep_ring->cycle_state = (ep_ring->cycle_state ? 0 : 1); + ep_ring->enq_seg = ep_ring->enq_seg->next; + ep_ring->enqueue = ep_ring->enq_seg->trbs; + next = ep_ring->enqueue; + } + + return 0; +} + +/** + * Generic function for queueing a command TRB on the command ring. + * Check to make sure there's room on the command ring for one command TRB. + * + * @param ctrl Host controller data structure + * @param ptr Pointer address to write in the first two fields (opt.) + * @param slot_id Slot ID to encode in the flags field (opt.) + * @param ep_index Endpoint index to encode in the flags field (opt.) + * @param cmd Command type to enqueue + * @return none + */ +void xhci_queue_command(struct xhci_ctrl *ctrl, dma_addr_t addr, u32 slot_id, + u32 ep_index, trb_type cmd) +{ + u32 fields[4]; + + BUG_ON(prepare_ring(ctrl, ctrl->cmd_ring, EP_STATE_RUNNING)); + + fields[0] = lower_32_bits(addr); + fields[1] = upper_32_bits(addr); + fields[2] = 0; + fields[3] = TRB_TYPE(cmd) | SLOT_ID_FOR_TRB(slot_id) | + ctrl->cmd_ring->cycle_state; + + /* + * Only 'reset endpoint', 'stop endpoint' and 'set TR dequeue pointer' + * commands need endpoint id encoded. + */ + if (cmd >= TRB_RESET_EP && cmd <= TRB_SET_DEQ) + fields[3] |= EP_ID_FOR_TRB(ep_index); + + queue_trb(ctrl, ctrl->cmd_ring, false, fields); + + /* Ring the command ring doorbell */ + xhci_writel(&ctrl->dba->doorbell[0], DB_VALUE_HOST); +} + +/** + * The TD size is the number of bytes remaining in the TD (including this TRB), + * right shifted by 10. + * It must fit in bits 21:17, so it can't be bigger than 31. + * + * @param remainder remaining packets to be sent + * @return remainder if remainder is less than max else max + */ +static u32 xhci_td_remainder(unsigned int remainder) +{ + u32 max = (1 << (21 - 17 + 1)) - 1; + + if ((remainder >> 10) >= max) + return max << 17; + else + return (remainder >> 10) << 17; +} + +/** + * Finds out the remanining packets to be sent + * + * @param running_total total size sent so far + * @param trb_buff_len length of the TRB Buffer + * @param total_packet_count total packet count + * @param maxpacketsize max packet size of current pipe + * @param num_trbs_left number of TRBs left to be processed + * @return 0 if running_total or trb_buff_len is 0, else remainder + */ +static u32 xhci_v1_0_td_remainder(int running_total, + int trb_buff_len, + unsigned int total_packet_count, + int maxpacketsize, + unsigned int num_trbs_left) +{ + int packets_transferred; + + /* One TRB with a zero-length data packet. */ + if (num_trbs_left == 0 || (running_total == 0 && trb_buff_len == 0)) + return 0; + + /* + * All the TRB queueing functions don't count the current TRB in + * running_total. + */ + packets_transferred = (running_total + trb_buff_len) / maxpacketsize; + + if ((total_packet_count - packets_transferred) > 31) + return 31 << 17; + return (total_packet_count - packets_transferred) << 17; +} + +/** + * Ring the doorbell of the End Point + * + * @param udev pointer to the USB device structure + * @param ep_index index of the endpoint + * @param start_cycle cycle flag of the first TRB + * @param start_trb pionter to the first TRB + * @return none + */ +static void giveback_first_trb(struct usb_device *udev, int ep_index, + int start_cycle, + struct xhci_generic_trb *start_trb) +{ + struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); + + /* + * Pass all the TRBs to the hardware at once and make sure this write + * isn't reordered. + */ + if (start_cycle) + start_trb->field[3] |= cpu_to_le32(start_cycle); + else + start_trb->field[3] &= cpu_to_le32(~TRB_CYCLE); + + xhci_flush_cache((uintptr_t)start_trb, sizeof(struct xhci_generic_trb)); + + /* Ringing EP doorbell here */ + xhci_writel(&ctrl->dba->doorbell[udev->slot_id], + DB_VALUE(ep_index, 0)); + + return; +} + +/**** POLLING mechanism for XHCI ****/ + +/** + * Finalizes a handled event TRB by advancing our dequeue pointer and giving + * the TRB back to the hardware for recycling. Must call this exactly once at + * the end of each event handler, and not touch the TRB again afterwards. + * + * @param ctrl Host controller data structure + * @return none + */ +void xhci_acknowledge_event(struct xhci_ctrl *ctrl) +{ + dma_addr_t deq; + + /* Advance our dequeue pointer to the next event */ + inc_deq(ctrl, ctrl->event_ring); + + /* Inform the hardware */ + deq = xhci_trb_virt_to_dma(ctrl->event_ring->deq_seg, + ctrl->event_ring->dequeue); + xhci_writeq(&ctrl->ir_set->erst_dequeue, deq | ERST_EHB); +} + +/** + * Checks if there is a new event to handle on the event ring. + * + * @param ctrl Host controller data structure + * @return 0 if failure else 1 on success + */ +static int event_ready(struct xhci_ctrl *ctrl) +{ + union xhci_trb *event; + + xhci_inval_cache((uintptr_t)ctrl->event_ring->dequeue, + sizeof(union xhci_trb)); + + event = ctrl->event_ring->dequeue; + + /* Does the HC or OS own the TRB? */ + if ((le32_to_cpu(event->event_cmd.flags) & TRB_CYCLE) != + ctrl->event_ring->cycle_state) + return 0; + + return 1; +} + +/** + * Waits for a specific type of event and returns it. Discards unexpected + * events. Caller *must* call xhci_acknowledge_event() after it is finished + * processing the event, and must not access the returned pointer afterwards. + * + * @param ctrl Host controller data structure + * @param expected TRB type expected from Event TRB + * @return pointer to event trb + */ +union xhci_trb *xhci_wait_for_event(struct xhci_ctrl *ctrl, trb_type expected, + unsigned int timeout_ms) +{ + trb_type type; + uint64_t start = get_time_ns(); + + do { + union xhci_trb *event = ctrl->event_ring->dequeue; + + if (!event_ready(ctrl)) + continue; + + type = TRB_FIELD_TO_TYPE(le32_to_cpu(event->event_cmd.flags)); + if (type == expected || + (expected == TRB_NONE && type != TRB_PORT_STATUS)) + return event; + + if (type == TRB_PORT_STATUS) + /* TODO: remove this once enumeration has been reworked */ + /* + * Port status change events always have a + * successful completion code + */ + BUG_ON(GET_COMP_CODE( + le32_to_cpu(event->generic.field[2])) != + COMP_SUCCESS); + else + dev_dbg(ctrl->dev, "Unexpected XHCI event TRB, skipping... " + "(%08x %08x %08x %08x)\n", + le32_to_cpu(event->generic.field[0]), + le32_to_cpu(event->generic.field[1]), + le32_to_cpu(event->generic.field[2]), + le32_to_cpu(event->generic.field[3])); + + xhci_acknowledge_event(ctrl); + } while (!is_timeout_non_interruptible(start, timeout_ms * MSECOND)); + + if (expected == TRB_TRANSFER) + return NULL; + + dev_warn(ctrl->dev, "XHCI timeout on event type %d...\n", expected); + + return NULL; +} + +/* + * Send reset endpoint command for given endpoint. This recovers from a + * halted endpoint (e.g. due to a stall error). + */ +static void reset_ep(struct usb_device *udev, int ep_index, unsigned int timeout_ms) +{ + struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); + struct xhci_ring *ring = ctrl->devs[udev->slot_id]->eps[ep_index].ring; + union xhci_trb *event; + u64 addr; + u32 field; + + dev_info(&udev->dev, "Resetting EP %d...\n", ep_index); + + xhci_queue_command(ctrl, 0, udev->slot_id, ep_index, TRB_RESET_EP); + event = xhci_wait_for_event(ctrl, TRB_COMPLETION, timeout_ms); + if (!event) + return; + + field = le32_to_cpu(event->trans_event.flags); + BUG_ON(TRB_TO_SLOT_ID(field) != udev->slot_id); + xhci_acknowledge_event(ctrl); + + addr = xhci_trb_virt_to_dma(ring->enq_seg, + (void *)((uintptr_t)ring->enqueue | ring->cycle_state)); + xhci_queue_command(ctrl, addr, udev->slot_id, ep_index, TRB_SET_DEQ); + event = xhci_wait_for_event(ctrl, TRB_COMPLETION, timeout_ms); + if (!event) + return; + + BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)) + != udev->slot_id || GET_COMP_CODE(le32_to_cpu( + event->event_cmd.status)) != COMP_SUCCESS); + xhci_acknowledge_event(ctrl); +} + +/* + * Stops transfer processing for an endpoint and throws away all unprocessed + * TRBs by setting the xHC's dequeue pointer to our enqueue pointer. The next + * xhci_bulk_tx/xhci_ctrl_tx on this enpoint will add new transfers there and + * ring the doorbell, causing this endpoint to start working again. + * (Careful: This will BUG() when there was no transfer in progress. Shouldn't + * happen in practice for current uses and is too complicated to fix right now.) + */ +static void abort_td(struct usb_device *udev, int ep_index) +{ + struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); + struct xhci_ring *ring = ctrl->devs[udev->slot_id]->eps[ep_index].ring; + union xhci_trb *event; + xhci_comp_code comp; + trb_type type; + dma_addr_t addr; + u32 field; + + xhci_queue_command(ctrl, 0, udev->slot_id, ep_index, TRB_STOP_RING); + + event = xhci_wait_for_event(ctrl, TRB_NONE, XHCI_TIMEOUT_DEFAULT); + if (!event) + return; + + type = TRB_FIELD_TO_TYPE(le32_to_cpu(event->event_cmd.flags)); + if (type == TRB_TRANSFER) { + field = le32_to_cpu(event->trans_event.flags); + BUG_ON(TRB_TO_SLOT_ID(field) != udev->slot_id); + BUG_ON(TRB_TO_EP_INDEX(field) != ep_index); + BUG_ON(GET_COMP_CODE(le32_to_cpu(event->trans_event.transfer_len + != COMP_STOP))); + xhci_acknowledge_event(ctrl); + + event = xhci_wait_for_event(ctrl, TRB_COMPLETION, XHCI_TIMEOUT_DEFAULT); + if (!event) + return; + type = TRB_FIELD_TO_TYPE(le32_to_cpu(event->event_cmd.flags)); + + } else { + dev_warn(ctrl->dev, "abort_td: Expected a TRB_TRANSFER TRB first\n"); + } + + comp = GET_COMP_CODE(le32_to_cpu(event->event_cmd.status)); + BUG_ON(type != TRB_COMPLETION || + TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)) + != udev->slot_id || (comp != COMP_SUCCESS && comp + != COMP_CTX_STATE)); + xhci_acknowledge_event(ctrl); + + addr = xhci_trb_virt_to_dma(ring->enq_seg, ring->enqueue); + addr |= ring->cycle_state; + xhci_queue_command(ctrl, addr, udev->slot_id, ep_index, TRB_SET_DEQ); + event = xhci_wait_for_event(ctrl, TRB_COMPLETION, XHCI_TIMEOUT_DEFAULT); + if (!event) + return; + + BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)) + != udev->slot_id || GET_COMP_CODE(le32_to_cpu( + event->event_cmd.status)) != COMP_SUCCESS); + xhci_acknowledge_event(ctrl); +} + +static void record_transfer_result(struct usb_device *udev, + union xhci_trb *event, int length) +{ + udev->act_len = min(length, length - + (int)EVENT_TRB_LEN(le32_to_cpu(event->trans_event.transfer_len))); + + switch (GET_COMP_CODE(le32_to_cpu(event->trans_event.transfer_len))) { + case COMP_SUCCESS: + BUG_ON(udev->act_len != length); + /* fallthrough */ + case COMP_SHORT_TX: + udev->status = 0; + break; + case COMP_STALL: + udev->status = USB_ST_STALLED; + break; + case COMP_DB_ERR: + case COMP_TRB_ERR: + udev->status = USB_ST_BUF_ERR; + break; + case COMP_BABBLE: + udev->status = USB_ST_BABBLE_DET; + break; + default: + udev->status = 0x80; /* USB_ST_TOO_LAZY_TO_MAKE_A_NEW_MACRO */ + } +} + +/**** Bulk and Control transfer methods ****/ +/** + * Queues up the BULK Request + * + * @param udev pointer to the USB device structure + * @param pipe contains the DIR_IN or OUT , devnum + * @param length length of the buffer + * @param buffer buffer to be read/written based on the request + * @return returns 0 if successful else -1 on failure + */ +int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe, + int length, void *buffer, unsigned int timeout_ms) +{ + int num_trbs = 0; + struct xhci_generic_trb *start_trb; + bool first_trb = false; + int start_cycle; + u32 field = 0; + u32 length_field = 0; + struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); + int slot_id = udev->slot_id; + int ep_index; + struct xhci_virt_device *virt_dev; + struct xhci_ep_ctx *ep_ctx; + struct xhci_ring *ring; /* EP transfer ring */ + union xhci_trb *event; + + int running_total, trb_buff_len; + unsigned int total_packet_count; + int maxpacketsize; + u64 addr; + int ret; + u32 trb_fields[4]; + enum dma_data_direction direction; + void *bounce = ctrl->bounce_buffer; + dma_addr_t map; + + /* + * XHCI has the restriction that a single TRB may not cross a 64KiB + * boundary. The U-Boot code we derived this from is somewhat prepared + * for this, but it doesn't work, at least not for the case when a short + * packet is received. For now just limit the maximum buffer length to + * 64KiB and use a 64KiB aligned bounce buffer to make sure we do not + * cross a boundary. + */ + if (length > SZ_64K) + return -EINVAL; + + if (usb_pipein(pipe)) { + direction = DMA_FROM_DEVICE; + } else { + direction = DMA_TO_DEVICE; + memcpy(bounce, buffer, length); + } + + map = addr = dma_map_single(ctrl->host.hw_dev, bounce, length, direction); + + dev_dbg(&udev->dev, "pipe=0x%lx, buffer=%p, length=%d\n", + pipe, buffer, length); + + ep_index = usb_pipe_ep_index(pipe); + virt_dev = ctrl->devs[slot_id]; + + xhci_inval_cache((uintptr_t)virt_dev->out_ctx->bytes, + virt_dev->out_ctx->size); + + ep_ctx = xhci_get_ep_ctx(ctrl, virt_dev->out_ctx, ep_index); + + /* + * If the endpoint was halted due to a prior error, resume it before + * the next transfer. It is the responsibility of the upper layer to + * have dealt with whatever caused the error. + */ + if ((le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK) == EP_STATE_HALTED) + reset_ep(udev, ep_index, timeout_ms); + + ring = virt_dev->eps[ep_index].ring; + /* + * How much data is (potentially) left before the 64KB boundary? + * XHCI Spec puts restriction( TABLE 49 and 6.4.1 section of XHCI Spec) + * that the buffer should not span 64KB boundary. if so + * we send request in more than 1 TRB by chaining them. + */ + running_total = TRB_MAX_BUFF_SIZE - + (lower_32_bits(addr) & (TRB_MAX_BUFF_SIZE - 1)); + trb_buff_len = running_total; + running_total &= TRB_MAX_BUFF_SIZE - 1; + + /* + * If there's some data on this 64KB chunk, or we have to send a + * zero-length transfer, we need at least one TRB + */ + if (running_total != 0 || length == 0) + num_trbs++; + + /* How many more 64KB chunks to transfer, how many more TRBs? */ + while (running_total < length) { + num_trbs++; + running_total += TRB_MAX_BUFF_SIZE; + } + + /* + * XXX: Calling routine prepare_ring() called in place of + * prepare_trasfer() as there in 'Linux' since we are not + * maintaining multiple TDs/transfer at the same time. + */ + ret = prepare_ring(ctrl, ring, + le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK); + if (ret < 0) { + dma_unmap_single(ctrl->host.hw_dev, map, length, direction); + return ret; + } + + /* + * Don't give the first TRB to the hardware (by toggling the cycle bit) + * until we've finished creating all the other TRBs. The ring's cycle + * state may change as we enqueue the other TRBs, so save it too. + */ + start_trb = &ring->enqueue->generic; + start_cycle = ring->cycle_state; + + running_total = 0; + maxpacketsize = usb_maxpacket(udev, pipe); + + total_packet_count = DIV_ROUND_UP(length, maxpacketsize); + + if (trb_buff_len > length) + trb_buff_len = length; + + first_trb = true; + + /* flush the buffer before use */ + xhci_flush_cache((uintptr_t)buffer, length); + + /* Queue the first TRB, even if it's zero-length */ + do { + u32 remainder = 0; + field = 0; + /* Don't change the cycle bit of the first TRB until later */ + if (first_trb) { + first_trb = false; + if (start_cycle == 0) + field |= TRB_CYCLE; + } else { + field |= ring->cycle_state; + } + + /* + * Chain all the TRBs together; clear the chain bit in the last + * TRB to indicate it's the last TRB in the chain. + */ + if (num_trbs > 1) + field |= TRB_CHAIN; + else + field |= TRB_IOC; + + /* Only set interrupt on short packet for IN endpoints */ + if (usb_pipein(pipe)) + field |= TRB_ISP; + + /* Set the TRB length, TD size, and interrupter fields. */ + if (HC_VERSION(xhci_readl(&ctrl->hccr->cr_capbase)) < 0x100) + remainder = xhci_td_remainder(length - running_total); + else + remainder = xhci_v1_0_td_remainder(running_total, + trb_buff_len, + total_packet_count, + maxpacketsize, + num_trbs - 1); + + length_field = (TRB_LEN(trb_buff_len) | + remainder | + TRB_INTR_TARGET(0)); + + trb_fields[0] = lower_32_bits(addr); + trb_fields[1] = upper_32_bits(addr); + trb_fields[2] = length_field; + trb_fields[3] = field | TRB_TYPE(TRB_NORMAL); + + queue_trb(ctrl, ring, (num_trbs > 1), trb_fields); + + --num_trbs; + + running_total += trb_buff_len; + + /* Calculate length for next transfer */ + addr += trb_buff_len; + trb_buff_len = min((length - running_total), TRB_MAX_BUFF_SIZE); + } while (running_total < length); + + giveback_first_trb(udev, ep_index, start_cycle, start_trb); + + event = xhci_wait_for_event(ctrl, TRB_TRANSFER, timeout_ms); + if (!event) { + dev_dbg(&udev->dev, "XHCI bulk transfer timed out, aborting...\n"); + abort_td(udev, ep_index); + udev->status = USB_ST_NAK_REC; /* closest thing to a timeout */ + udev->act_len = 0; + dma_unmap_single(ctrl->host.hw_dev, map, length, direction); + return -ETIMEDOUT; + } + field = le32_to_cpu(event->trans_event.flags); + + if (TRB_TO_SLOT_ID(field) != slot_id) + dev_err(&udev->dev, "Unexpected slot_id %d, expected %d\n", + TRB_TO_SLOT_ID(field), slot_id); + + if (TRB_TO_EP_INDEX(field) != ep_index) + dev_err(&udev->dev, "Unexpected ep_index %d, expected %d\n", + TRB_TO_EP_INDEX(field), ep_index); + + record_transfer_result(udev, event, length); + xhci_acknowledge_event(ctrl); + + dma_unmap_single(ctrl->host.hw_dev, map, length, direction); + + if (usb_pipein(pipe)) + memcpy(buffer, bounce, length); + + return (udev->status != USB_ST_NOT_PROC) ? 0 : -1; +} + +/** + * Queues up the Control Transfer Request + * + * @param udev pointer to the USB device structure + * @param pipe contains the DIR_IN or OUT , devnum + * @param req request type + * @param length length of the buffer + * @param buffer buffer to be read/written based on the request + * @return returns 0 if successful else error code on failure + */ +int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe, + struct devrequest *req, int length, + void *buffer, unsigned int timeout_ms) +{ + int ret; + int start_cycle; + int num_trbs; + u32 field; + u32 length_field; + u64 buf_64 = 0; + struct xhci_generic_trb *start_trb; + struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); + int slot_id = udev->slot_id; + int ep_index; + u32 trb_fields[4]; + struct xhci_virt_device *virt_dev = ctrl->devs[slot_id]; + struct xhci_ring *ep_ring; + union xhci_trb *event; + struct xhci_ep_ctx *ep_ctx; + enum dma_data_direction direction; + dma_addr_t map = 0; + + dev_dbg(&udev->dev, "req=%u (%#x), type=%u (%#x), value=%u (%#x), index=%u\n", + req->request, req->request, + req->requesttype, req->requesttype, + le16_to_cpu(req->value), le16_to_cpu(req->value), + le16_to_cpu(req->index)); + + ep_index = usb_pipe_ep_index(pipe); + + ep_ring = virt_dev->eps[ep_index].ring; + + /* + * Check to see if the max packet size for the default control + * endpoint changed during FS device enumeration + */ + if (udev->speed == USB_SPEED_FULL) { + ret = xhci_check_maxpacket(udev); + if (ret < 0) + return ret; + } + + xhci_inval_cache((uintptr_t)virt_dev->out_ctx->bytes, + virt_dev->out_ctx->size); + + ep_ctx = xhci_get_ep_ctx(ctrl, virt_dev->out_ctx, ep_index); + + /* 1 TRB for setup, 1 for status */ + num_trbs = 2; + /* + * Don't need to check if we need additional event data and normal TRBs, + * since data in control transfers will never get bigger than 16MB + * XXX: can we get a buffer that crosses 64KB boundaries? + */ + + if (length > 0) + num_trbs++; + /* + * XXX: Calling routine prepare_ring() called in place of + * prepare_trasfer() as there in 'Linux' since we are not + * maintaining multiple TDs/transfer at the same time. + */ + ret = prepare_ring(ctrl, ep_ring, + le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK); + + if (ret < 0) + return ret; + + /* + * Don't give the first TRB to the hardware (by toggling the cycle bit) + * until we've finished creating all the other TRBs. The ring's cycle + * state may change as we enqueue the other TRBs, so save it too. + */ + start_trb = &ep_ring->enqueue->generic; + start_cycle = ep_ring->cycle_state; + + dev_dbg(&udev->dev, "start_trb 0x%p, start_cycle %d\n", start_trb, start_cycle); + + /* Queue setup TRB - see section 6.4.1.2.1 */ + /* FIXME better way to translate setup_packet into two u32 fields? */ + field = 0; + field |= TRB_IDT | TRB_TYPE(TRB_SETUP); + if (start_cycle == 0) + field |= 0x1; + + /* xHCI 1.0 6.4.1.2.1: Transfer Type field */ + if (HC_VERSION(xhci_readl(&ctrl->hccr->cr_capbase)) >= 0x100) { + if (length > 0) { + if (req->requesttype & USB_DIR_IN) + field |= TRB_TX_TYPE(TRB_DATA_IN); + else + field |= TRB_TX_TYPE(TRB_DATA_OUT); + } + } + + dev_dbg(&udev->dev, "req->requesttype = %d, req->request = %d," + "le16_to_cpu(req->value) = %d," + "le16_to_cpu(req->index) = %d," + "le16_to_cpu(req->length) = %d\n", + req->requesttype, req->request, le16_to_cpu(req->value), + le16_to_cpu(req->index), le16_to_cpu(req->length)); + + trb_fields[0] = req->requesttype | req->request << 8 | + le16_to_cpu(req->value) << 16; + trb_fields[1] = le16_to_cpu(req->index) | + le16_to_cpu(req->length) << 16; + /* TRB_LEN | (TRB_INTR_TARGET) */ + trb_fields[2] = (TRB_LEN(8) | TRB_INTR_TARGET(0)); + /* Immediate data in pointer */ + trb_fields[3] = field; + queue_trb(ctrl, ep_ring, true, trb_fields); + + /* Re-initializing field to zero */ + field = 0; + /* If there's data, queue data TRBs */ + /* Only set interrupt on short packet for IN endpoints */ + if (usb_pipein(pipe)) + field = TRB_ISP | TRB_TYPE(TRB_DATA); + else + field = TRB_TYPE(TRB_DATA); + + length_field = TRB_LEN(length) | xhci_td_remainder(length) | + ((0 & TRB_INTR_TARGET_MASK) << TRB_INTR_TARGET_SHIFT); + dev_dbg(&udev->dev, "length_field = %d, length = %d," + "xhci_td_remainder(length) = %d , TRB_INTR_TARGET(0) = %d\n", + length_field, TRB_LEN(length), + xhci_td_remainder(length), 0); + + if (req->requesttype & USB_DIR_IN) + direction = DMA_FROM_DEVICE; + else + direction = DMA_TO_DEVICE; + + if (length > 0) { + if (req->requesttype & USB_DIR_IN) + field |= TRB_DIR_IN; + map = buf_64 = dma_map_single(ctrl->host.hw_dev, buffer, length, direction); + + trb_fields[0] = lower_32_bits(buf_64); + trb_fields[1] = upper_32_bits(buf_64); + trb_fields[2] = length_field; + trb_fields[3] = field | ep_ring->cycle_state; + + xhci_flush_cache((uintptr_t)buffer, length); + queue_trb(ctrl, ep_ring, true, trb_fields); + } + + /* + * Queue status TRB - + * see Table 7 and sections 4.11.2.2 and 6.4.1.2.3 + */ + + /* If the device sent data, the status stage is an OUT transfer */ + field = 0; + if (length > 0 && req->requesttype & USB_DIR_IN) + field = 0; + else + field = TRB_DIR_IN; + + trb_fields[0] = 0; + trb_fields[1] = 0; + trb_fields[2] = TRB_INTR_TARGET(0); + /* Event on completion */ + trb_fields[3] = field | TRB_IOC | + TRB_TYPE(TRB_STATUS) | ep_ring->cycle_state; + + queue_trb(ctrl, ep_ring, false, trb_fields); + + giveback_first_trb(udev, ep_index, start_cycle, start_trb); + + event = xhci_wait_for_event(ctrl, TRB_TRANSFER, timeout_ms); + if (!event) + goto abort; + field = le32_to_cpu(event->trans_event.flags); + + if (TRB_TO_SLOT_ID(field) != slot_id) + dev_err(&udev->dev, "Unexpected slot_id %d, expected %d\n", + TRB_TO_SLOT_ID(field), slot_id); + + if (TRB_TO_EP_INDEX(field) != ep_index) + dev_err(&udev->dev, "Unexpected ep_index %d, expected %d\n", + TRB_TO_EP_INDEX(field), ep_index); + + record_transfer_result(udev, event, length); + xhci_acknowledge_event(ctrl); + + /* Invalidate buffer to make it available to usb-core */ + if (length > 0) + dma_unmap_single(ctrl->host.hw_dev, map, length, direction); + + if (udev->status == USB_ST_STALLED) { + reset_ep(udev, ep_index, timeout_ms); + return -EPIPE; + } + + if (GET_COMP_CODE(le32_to_cpu(event->trans_event.transfer_len)) + == COMP_SHORT_TX) { + /* Short data stage, clear up additional status stage event */ + event = xhci_wait_for_event(ctrl, TRB_TRANSFER, timeout_ms); + if (!event) + goto abort; + BUG_ON(TRB_TO_SLOT_ID(field) != slot_id); + BUG_ON(TRB_TO_EP_INDEX(field) != ep_index); + xhci_acknowledge_event(ctrl); + } + + return (udev->status != USB_ST_NOT_PROC) ? 0 : -1; + +abort: + dev_dbg(&udev->dev, "XHCI control transfer timed out, aborting...\n"); + abort_td(udev, ep_index); + udev->status = USB_ST_NAK_REC; + udev->act_len = 0; + dma_unmap_single(ctrl->host.hw_dev, map, length, direction); + return -ETIMEDOUT; +} diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c new file mode 100644 index 0000000000..e7b8344181 --- /dev/null +++ b/drivers/usb/host/xhci.c @@ -0,0 +1,1454 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * USB HOST XHCI Controller stack + * + * Based on xHCI host controller driver in linux-kernel + * by Sarah Sharp. + * + * Copyright (C) 2008 Intel Corp. + * Author: Sarah Sharp + * + * Copyright (C) 2013 Samsung Electronics Co.Ltd + * Authors: Vivek Gautam <gautam.vivek@samsung.com> + * Vikas Sajjan <vikas.sajjan@samsung.com> + */ + +/** + * This file gives the xhci stack for usb3.0 looking into + * xhci specification Rev1.0 (5/21/10). + * The quirk devices support hasn't been given yet. + */ +#include <clock.h> +#include <common.h> +#include <dma.h> +#include <init.h> +#include <io.h> +#include <linux/err.h> +#include <linux/usb/usb.h> +#include <linux/usb/xhci.h> +#include <asm/unaligned.h> + +#include "xhci.h" + +static const 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; + struct usb_ss_ep_comp_descriptor ep_companion; +} __attribute__ ((packed)) descriptor = { + { + 0xc, /* bDescLength */ + 0x2a, /* bDescriptorType: hub descriptor */ + 2, /* bNrPorts -- runtime modified */ + cpu_to_le16(0x8), /* wHubCharacteristics */ + 10, /* bPwrOn2PwrGood */ + 0, /* bHubCntrCurrent */ + { /* Device removable */ + } /* at most 7 ports! XXX */ + }, + { + 0x12, /* bLength */ + 1, /* bDescriptorType: UDESC_DEVICE */ + cpu_to_le16(0x0300), /* bcdUSB: v3.0 */ + 9, /* bDeviceClass: UDCLASS_HUB */ + 0, /* bDeviceSubClass: UDSUBCLASS_HUB */ + 3, /* bDeviceProtocol: UDPROTO_SSHUBSTT */ + 9, /* bMaxPacketSize: 512 bytes 2^9 */ + 0x0000, /* idVendor */ + 0x0000, /* idProduct */ + cpu_to_le16(0x0100), /* bcdDevice */ + 1, /* iManufacturer */ + 2, /* iProduct */ + 0, /* iSerialNumber */ + 1 /* bNumConfigurations: 1 */ + }, + { + 0x9, + 2, /* bDescriptorType: UDESC_CONFIG */ + cpu_to_le16(0x1f), /* includes SS endpoint descriptor */ + 1, /* bNumInterface */ + 1, /* bConfigurationValue */ + 0, /* iConfiguration */ + 0x40, /* bmAttributes: UC_SELF_POWER */ + 0 /* bMaxPower */ + }, + { + 0x9, /* bLength */ + 4, /* bDescriptorType: UDESC_INTERFACE */ + 0, /* bInterfaceNumber */ + 0, /* bAlternateSetting */ + 1, /* bNumEndpoints */ + 9, /* bInterfaceClass: UICLASS_HUB */ + 0, /* bInterfaceSubClass: UISUBCLASS_HUB */ + 0, /* bInterfaceProtocol: UIPROTO_HSHUBSTT */ + 0 /* iInterface */ + }, + { + 0x7, /* bLength */ + 5, /* bDescriptorType: UDESC_ENDPOINT */ + 0x81, /* bEndpointAddress: IN endpoint 1 */ + 3, /* bmAttributes: UE_INTERRUPT */ + 8, /* wMaxPacketSize */ + 255 /* bInterval */ + }, + { + 0x06, /* ss_bLength */ + 0x30, /* ss_bDescriptorType: SS EP Companion */ + 0x00, /* ss_bMaxBurst: allows 1 TX between ACKs */ + /* ss_bmAttributes: 1 packet per service interval */ + 0x00, + /* ss_wBytesPerInterval: 15 bits for max 15 ports */ + cpu_to_le16(0x02), + }, +}; + +struct xhci_ctrl *xhci_get_ctrl(struct usb_device *udev) +{ + struct usb_host *host = udev->host; + + return to_xhci(host); +} + +/** + * Waits for as per specified amount of time + * for the "result" to match with "done" + * + * @param ptr pointer to the register to be read + * @param mask mask for the value read + * @param done value to be campared with result + * @param usec time to wait till + * @return 0 if handshake is success else < 0 on failure + */ +static int handshake(uint32_t volatile *ptr, uint32_t mask, + uint32_t done, int usec) +{ + uint32_t result; + uint64_t start = get_time_ns(); + + while (1) { + result = xhci_readl(ptr); + if (result == ~(uint32_t)0) + return -ENODEV; + result &= mask; + if (result == done) + return 0; + if (is_timeout_non_interruptible(start, usec * 1000)) + return -ETIMEDOUT; + } +} + +/** + * Set the run bit and wait for the host to be running. + * + * @param hcor pointer to host controller operation registers + * @return status of the Handshake + */ +static int xhci_start(struct xhci_ctrl *ctrl) +{ + struct xhci_hcor *hcor = ctrl->hcor; + u32 temp; + int ret; + + dev_dbg(ctrl->dev, "Starting the controller\n"); + + temp = xhci_readl(&hcor->or_usbcmd); + temp |= (CMD_RUN); + xhci_writel(&hcor->or_usbcmd, temp); + + /* + * Wait for the HCHalted Status bit to be 0 to indicate the host is + * running. + */ + ret = handshake(&hcor->or_usbsts, STS_HALT, 0, XHCI_MAX_HALT_USEC); + if (ret) + dev_dbg(ctrl->dev, "Host took too long to start, " + "waited %u microseconds.\n", + XHCI_MAX_HALT_USEC); + return ret; +} + +/** + * Resets the XHCI Controller + * + * @param hcor pointer to host controller operation registers + * @return -EBUSY if XHCI Controller is not halted else status of handshake + */ +static int xhci_reset(struct xhci_ctrl *ctrl) +{ + struct xhci_hcor *hcor = ctrl->hcor; + u32 cmd; + u32 state; + int ret; + + /* Halting the Host first */ + dev_dbg(ctrl->dev, "Halt the HC\n"); + state = xhci_readl(&hcor->or_usbsts) & STS_HALT; + if (!state) { + cmd = xhci_readl(&hcor->or_usbcmd); + cmd &= ~CMD_RUN; + xhci_writel(&hcor->or_usbcmd, cmd); + } + + ret = handshake(&hcor->or_usbsts, + STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC); + if (ret) { + dev_err(ctrl->dev, "Host not halted after %u microseconds.\n", + XHCI_MAX_HALT_USEC); + return -EBUSY; + } + + dev_dbg(ctrl->dev, "Reset the HC\n"); + cmd = xhci_readl(&hcor->or_usbcmd); + cmd |= CMD_RESET; + xhci_writel(&hcor->or_usbcmd, cmd); + + ret = handshake(&hcor->or_usbcmd, CMD_RESET, 0, XHCI_MAX_RESET_USEC); + if (ret) + return ret; + + /* + * xHCI cannot write to any doorbells or operational registers other + * than status until the "Controller Not Ready" flag is cleared. + */ + return handshake(&hcor->or_usbsts, STS_CNR, 0, XHCI_MAX_RESET_USEC); +} + +/** + * Used for passing endpoint bitmasks between the core and HCDs. + * Find the index for an endpoint given its descriptor. + * Use the return value to right shift 1 for the bitmask. + * + * Index = (epnum * 2) + direction - 1, + * where direction = 0 for OUT, 1 for IN. + * For control endpoints, the IN index is used (OUT index is unused), so + * index = (epnum * 2) + direction - 1 = (epnum * 2) + 1 - 1 = (epnum * 2) + * + * @param desc USB enpdoint Descriptor + * @return index of the Endpoint + */ +static unsigned int xhci_get_ep_index(struct usb_endpoint_descriptor *desc) +{ + unsigned int index; + + if (usb_endpoint_xfer_control(desc)) + index = (unsigned int)(usb_endpoint_num(desc) * 2); + else + index = (unsigned int)((usb_endpoint_num(desc) * 2) - + (usb_endpoint_dir_in(desc) ? 0 : 1)); + + return index; +} + +/* + * Convert bInterval expressed in microframes (in 1-255 range) to exponent of + * microframes, rounded down to nearest power of 2. + */ +static unsigned int xhci_microframes_to_exponent(struct usb_device *udev, + unsigned int desc_interval, + unsigned int min_exponent, + unsigned int max_exponent) +{ + unsigned int interval; + + interval = fls(desc_interval) - 1; + interval = clamp_val(interval, min_exponent, max_exponent); + if ((1 << interval) != desc_interval) + dev_dbg(&udev->dev, "rounding interval to %d microframes, "\ + "ep desc says %d microframes\n", + 1 << interval, desc_interval); + + return interval; +} + +static unsigned int xhci_parse_microframe_interval(struct usb_device *udev, + struct usb_endpoint_descriptor *endpt_desc) +{ + if (endpt_desc->bInterval == 0) + return 0; + + return xhci_microframes_to_exponent(udev, endpt_desc->bInterval, 0, 15); +} + +static unsigned int xhci_parse_frame_interval(struct usb_device *udev, + struct usb_endpoint_descriptor *endpt_desc) +{ + return xhci_microframes_to_exponent(udev, endpt_desc->bInterval * 8, 3, 10); +} + +/* + * Convert interval expressed as 2^(bInterval - 1) == interval into + * straight exponent value 2^n == interval. + */ +static unsigned int xhci_parse_exponent_interval(struct usb_device *udev, + struct usb_endpoint_descriptor *endpt_desc) +{ + unsigned int interval; + + interval = clamp_val(endpt_desc->bInterval, 1, 16) - 1; + if (interval != endpt_desc->bInterval - 1) + dev_dbg(&udev->dev, "ep %#x - rounding interval to %d %sframes\n", + endpt_desc->bEndpointAddress, 1 << interval, + udev->speed == USB_SPEED_FULL ? "" : "micro"); + + if (udev->speed == USB_SPEED_FULL) { + /* + * Full speed isoc endpoints specify interval in frames, + * not microframes. We are using microframes everywhere, + * so adjust accordingly. + */ + interval += 3; /* 1 frame = 2^3 uframes */ + } + + return interval; +} + +/* + * Return the polling or NAK interval. + * + * The polling interval is expressed in "microframes". If xHCI's Interval field + * is set to N, it will service the endpoint every 2^(Interval)*125us. + * + * The NAK interval is one NAK per 1 to 255 microframes, or no NAKs if interval + * is set to 0. + */ +static unsigned int xhci_get_endpoint_interval(struct usb_device *udev, + struct usb_endpoint_descriptor *endpt_desc) +{ + unsigned int interval = 0; + + switch (udev->speed) { + case USB_SPEED_HIGH: + /* Max NAK rate */ + if (usb_endpoint_xfer_control(endpt_desc) || + usb_endpoint_xfer_bulk(endpt_desc)) { + interval = xhci_parse_microframe_interval(udev, + endpt_desc); + break; + } + /* Fall through - SS and HS isoc/int have same decoding */ + + case USB_SPEED_SUPER: + if (usb_endpoint_xfer_int(endpt_desc) || + usb_endpoint_xfer_isoc(endpt_desc)) { + interval = xhci_parse_exponent_interval(udev, + endpt_desc); + } + break; + + case USB_SPEED_FULL: + if (usb_endpoint_xfer_isoc(endpt_desc)) { + interval = xhci_parse_exponent_interval(udev, + endpt_desc); + break; + } + /* + * Fall through for interrupt endpoint interval decoding + * since it uses the same rules as low speed interrupt + * endpoints. + */ + + case USB_SPEED_LOW: + if (usb_endpoint_xfer_int(endpt_desc) || + usb_endpoint_xfer_isoc(endpt_desc)) { + interval = xhci_parse_frame_interval(udev, endpt_desc); + } + break; + + default: + BUG(); + } + + return interval; +} + +/* + * The "Mult" field in the endpoint context is only set for SuperSpeed isoc eps. + * High speed endpoint descriptors can define "the number of additional + * transaction opportunities per microframe", but that goes in the Max Burst + * endpoint context field. + */ +static u32 xhci_get_endpoint_mult(struct usb_device *udev, + struct usb_endpoint_descriptor *endpt_desc, + struct usb_ss_ep_comp_descriptor *ss_ep_comp_desc) +{ + if (udev->speed < USB_SPEED_SUPER || + !usb_endpoint_xfer_isoc(endpt_desc)) + return 0; + + return ss_ep_comp_desc->bmAttributes; +} + +static u32 xhci_get_endpoint_max_burst(struct usb_device *udev, + struct usb_endpoint_descriptor *endpt_desc, + struct usb_ss_ep_comp_descriptor *ss_ep_comp_desc) +{ + /* Super speed and Plus have max burst in ep companion desc */ + if (udev->speed >= USB_SPEED_SUPER) + return ss_ep_comp_desc->bMaxBurst; + + if (udev->speed == USB_SPEED_HIGH && + (usb_endpoint_xfer_isoc(endpt_desc) || + usb_endpoint_xfer_int(endpt_desc))) + return usb_endpoint_maxp_mult(endpt_desc) - 1; + + return 0; +} + +/* + * Return the maximum endpoint service interval time (ESIT) payload. + * Basically, this is the maxpacket size, multiplied by the burst size + * and mult size. + */ +static u32 xhci_get_max_esit_payload(struct usb_device *udev, + struct usb_endpoint_descriptor *endpt_desc, + struct usb_ss_ep_comp_descriptor *ss_ep_comp_desc) +{ + int max_burst; + int max_packet; + + /* Only applies for interrupt or isochronous endpoints */ + if (usb_endpoint_xfer_control(endpt_desc) || + usb_endpoint_xfer_bulk(endpt_desc)) + return 0; + + /* SuperSpeed Isoc ep with less than 48k per esit */ + if (udev->speed >= USB_SPEED_SUPER) + return le16_to_cpu(ss_ep_comp_desc->wBytesPerInterval); + + max_packet = usb_endpoint_maxp(endpt_desc); + max_burst = usb_endpoint_maxp_mult(endpt_desc); + + /* A 0 in max burst means 1 transfer per ESIT */ + return max_packet * max_burst; +} + +/** + * Issue a configure endpoint command or evaluate context command + * and wait for it to finish. + * + * @param udev pointer to the Device Data Structure + * @param ctx_change flag to indicate the Context has changed or NOT + * @return 0 on success, -1 on failure + */ +static int xhci_configure_endpoints(struct usb_device *udev, bool ctx_change) +{ + struct xhci_container_ctx *in_ctx; + struct xhci_virt_device *virt_dev; + struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); + union xhci_trb *event; + + virt_dev = ctrl->devs[udev->slot_id]; + in_ctx = virt_dev->in_ctx; + + xhci_flush_cache((uintptr_t)in_ctx->bytes, in_ctx->size); + xhci_queue_command(ctrl, in_ctx->dma, udev->slot_id, 0, + ctx_change ? TRB_EVAL_CONTEXT : TRB_CONFIG_EP); + event = xhci_wait_for_event(ctrl, TRB_COMPLETION, XHCI_TIMEOUT_DEFAULT); + if (!event) + return -ETIMEDOUT; + + BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)) + != udev->slot_id); + + switch (GET_COMP_CODE(le32_to_cpu(event->event_cmd.status))) { + case COMP_SUCCESS: + dev_dbg(&udev->dev, "Successful %s command\n", + ctx_change ? "Evaluate Context" : "Configure Endpoint"); + break; + default: + dev_err(&udev->dev, "%s command returned completion code %d.\n", + ctx_change ? "Evaluate Context" : "Configure Endpoint", + GET_COMP_CODE(le32_to_cpu(event->event_cmd.status))); + return -EINVAL; + } + + xhci_acknowledge_event(ctrl); + + return 0; +} + +/** + * Configure the endpoint, programming the device contexts. + * + * @param udev pointer to the USB device structure + * @return returns the status of the xhci_configure_endpoints + */ +static int xhci_set_configuration(struct usb_device *udev) +{ + struct xhci_container_ctx *in_ctx; + struct xhci_container_ctx *out_ctx; + struct xhci_input_control_ctx *ctrl_ctx; + struct xhci_slot_ctx *slot_ctx; + struct xhci_ep_ctx *ep_ctx[MAX_EP_CTX_NUM]; + int cur_ep; + int max_ep_flag = 0; + int ep_index; + unsigned int dir; + unsigned int ep_type; + struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); + int num_of_ep; + int ep_flag = 0; + u64 trb_64 = 0; + int slot_id = udev->slot_id; + struct xhci_virt_device *virt_dev = ctrl->devs[slot_id]; + struct usb_interface *ifdesc; + u32 max_esit_payload; + unsigned int interval; + unsigned int mult; + unsigned int max_burst; + unsigned int avg_trb_len; + unsigned int err_count = 0; + + out_ctx = virt_dev->out_ctx; + in_ctx = virt_dev->in_ctx; + + num_of_ep = udev->config.interface[0].no_of_ep; + ifdesc = &udev->config.interface[0]; + + ctrl_ctx = xhci_get_input_control_ctx(in_ctx); + /* Initialize the input context control */ + ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG); + ctrl_ctx->drop_flags = 0; + + /* EP_FLAG gives values 1 & 4 for EP1OUT and EP2IN */ + for (cur_ep = 0; cur_ep < num_of_ep; cur_ep++) { + ep_flag = xhci_get_ep_index(&ifdesc->ep_desc[cur_ep]); + ctrl_ctx->add_flags |= cpu_to_le32(1 << (ep_flag + 1)); + if (max_ep_flag < ep_flag) + max_ep_flag = ep_flag; + } + + xhci_inval_cache((uintptr_t)out_ctx->bytes, out_ctx->size); + + /* slot context */ + xhci_slot_copy(ctrl, in_ctx, out_ctx); + slot_ctx = xhci_get_slot_ctx(ctrl, in_ctx); + slot_ctx->dev_info &= ~(cpu_to_le32(LAST_CTX_MASK)); + slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(max_ep_flag + 1) | 0); + + xhci_endpoint_copy(ctrl, in_ctx, out_ctx, 0); + + /* filling up ep contexts */ + for (cur_ep = 0; cur_ep < num_of_ep; cur_ep++) { + struct usb_endpoint_descriptor *endpt_desc = NULL; + struct usb_ss_ep_comp_descriptor *ss_ep_comp_desc = NULL; + + endpt_desc = &ifdesc->ep_desc[cur_ep]; + ss_ep_comp_desc = &ifdesc->ss_ep_comp_desc[cur_ep]; + trb_64 = 0; + + /* + * Get values to fill the endpoint context, mostly from ep + * descriptor. The average TRB buffer lengt for bulk endpoints + * is unclear as we have no clue on scatter gather list entry + * size. For Isoc and Int, set it to max available. + * See xHCI 1.1 spec 4.14.1.1 for details. + */ + max_esit_payload = xhci_get_max_esit_payload(udev, endpt_desc, + ss_ep_comp_desc); + interval = xhci_get_endpoint_interval(udev, endpt_desc); + mult = xhci_get_endpoint_mult(udev, endpt_desc, + ss_ep_comp_desc); + max_burst = xhci_get_endpoint_max_burst(udev, endpt_desc, + ss_ep_comp_desc); + avg_trb_len = max_esit_payload; + + ep_index = xhci_get_ep_index(endpt_desc); + ep_ctx[ep_index] = xhci_get_ep_ctx(ctrl, in_ctx, ep_index); + + /* Allocate the ep rings */ + virt_dev->eps[ep_index].ring = xhci_ring_alloc(ctrl, 1, true); + if (!virt_dev->eps[ep_index].ring) + return -ENOMEM; + + /*NOTE: ep_desc[0] actually represents EP1 and so on */ + dir = (((endpt_desc->bEndpointAddress) & (0x80)) >> 7); + ep_type = (((endpt_desc->bmAttributes) & (0x3)) | (dir << 2)); + + ep_ctx[ep_index]->ep_info = + cpu_to_le32(EP_MAX_ESIT_PAYLOAD_HI(max_esit_payload) | + EP_INTERVAL(interval) | EP_MULT(mult)); + + ep_ctx[ep_index]->ep_info2 = cpu_to_le32(EP_TYPE(ep_type)); + ep_ctx[ep_index]->ep_info2 |= + cpu_to_le32(MAX_PACKET + (get_unaligned(&endpt_desc->wMaxPacketSize))); + + /* Allow 3 retries for everything but isoc, set CErr = 3 */ + if (!usb_endpoint_xfer_isoc(endpt_desc)) + err_count = 3; + ep_ctx[ep_index]->ep_info2 |= + cpu_to_le32(MAX_BURST(max_burst) | + ERROR_COUNT(err_count)); + + trb_64 = xhci_trb_virt_to_dma(virt_dev->eps[ep_index].ring->enq_seg, + virt_dev->eps[ep_index].ring->enqueue); + ep_ctx[ep_index]->deq = cpu_to_le64(trb_64 | + virt_dev->eps[ep_index].ring->cycle_state); + + /* + * xHCI spec 6.2.3: + * 'Average TRB Length' should be 8 for control endpoints. + */ + if (usb_endpoint_xfer_control(endpt_desc)) + avg_trb_len = 8; + ep_ctx[ep_index]->tx_info = + cpu_to_le32(EP_MAX_ESIT_PAYLOAD_LO(max_esit_payload) | + EP_AVG_TRB_LENGTH(avg_trb_len)); + } + + return xhci_configure_endpoints(udev, false); +} + +/** + * Issue an Address Device command (which will issue a SetAddress request to + * the device). + * + * @param udev pointer to the Device Data Structure + * @return 0 if successful else error code on failure + */ +static int xhci_address_device(struct usb_device *udev, int root_portnr) +{ + int ret = 0; + struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); + struct xhci_slot_ctx *slot_ctx; + struct xhci_input_control_ctx *ctrl_ctx; + struct xhci_virt_device *virt_dev; + int slot_id = udev->slot_id; + union xhci_trb *event; + + virt_dev = ctrl->devs[slot_id]; + + /* + * This is the first Set Address since device plug-in + * so setting up the slot context. + */ + dev_dbg(&udev->dev, "Setting up addressable devices %p\n", ctrl->dcbaa); + xhci_setup_addressable_virt_dev(ctrl, udev, root_portnr); + + ctrl_ctx = xhci_get_input_control_ctx(virt_dev->in_ctx); + ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG); + ctrl_ctx->drop_flags = 0; + + xhci_queue_command(ctrl, virt_dev->in_ctx->dma, + slot_id, 0, TRB_ADDR_DEV); + event = xhci_wait_for_event(ctrl, TRB_COMPLETION, XHCI_TIMEOUT_DEFAULT); + if (!event) + return -ETIMEDOUT; + + BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)) != slot_id); + + switch (GET_COMP_CODE(le32_to_cpu(event->event_cmd.status))) { + case COMP_CTX_STATE: + case COMP_EBADSLT: + dev_err(&udev->dev, "Setup ERROR: address device command for slot %d.\n", + slot_id); + ret = -EINVAL; + break; + case COMP_TX_ERR: + dev_err(&udev->dev, "Device not responding to set address.\n"); + ret = -EPROTO; + break; + case COMP_DEV_ERR: + dev_err(&udev->dev, "ERROR: Incompatible device" + "for address device command.\n"); + ret = -ENODEV; + break; + case COMP_SUCCESS: + dev_dbg(&udev->dev, "Successful Address Device command\n"); + udev->status = 0; + break; + default: + dev_err(&udev->dev, "ERROR: unexpected command completion code 0x%x.\n", + GET_COMP_CODE(le32_to_cpu(event->event_cmd.status))); + ret = -EINVAL; + break; + } + + xhci_acknowledge_event(ctrl); + + if (ret < 0) + /* + * TODO: Unsuccessful Address Device command shall leave the + * slot in default state. So, issue Disable Slot command now. + */ + return ret; + + xhci_inval_cache((uintptr_t)virt_dev->out_ctx->bytes, + virt_dev->out_ctx->size); + slot_ctx = xhci_get_slot_ctx(ctrl, virt_dev->out_ctx); + + dev_dbg(&udev->dev, "xHC internal address is: %d\n", + le32_to_cpu(slot_ctx->dev_state) & DEV_ADDR_MASK); + + return 0; +} + +/** + * Issue Enable slot command to the controller to allocate + * device slot and assign the slot id. It fails if the xHC + * ran out of device slots, the Enable Slot command timed out, + * or allocating memory failed. + * + * @param udev pointer to the Device Data Structure + * @return Returns 0 on succes else return error code on failure + */ +static int _xhci_alloc_device(struct usb_device *udev) +{ + struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); + union xhci_trb *event; + int ret; + + /* + * Root hub will be first device to be initailized. + * If this device is root-hub, don't do any xHC related + * stuff. + */ + if (ctrl->rootdev == 0) { + udev->speed = USB_SPEED_SUPER; + return 0; + } + + xhci_queue_command(ctrl, 0, 0, 0, TRB_ENABLE_SLOT); + event = xhci_wait_for_event(ctrl, TRB_COMPLETION, XHCI_TIMEOUT_DEFAULT); + if (!event) + return -ETIMEDOUT; + + BUG_ON(GET_COMP_CODE(le32_to_cpu(event->event_cmd.status)) + != COMP_SUCCESS); + + udev->slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)); + + xhci_acknowledge_event(ctrl); + + ret = xhci_alloc_virt_device(ctrl, udev->slot_id); + if (ret < 0) { + /* + * TODO: Unsuccessful Address Device command shall leave + * the slot in default. So, issue Disable Slot command now. + */ + dev_err(ctrl->dev, "Could not allocate xHCI USB device data structures\n"); + return ret; + } + + return 0; +} + +/* + * Full speed devices may have a max packet size greater than 8 bytes, but the + * USB core doesn't know that until it reads the first 8 bytes of the + * descriptor. If the usb_device's max packet size changes after that point, + * we need to issue an evaluate context command and wait on it. + * + * @param udev pointer to the Device Data Structure + * @return returns the status of the xhci_configure_endpoints + */ +int xhci_check_maxpacket(struct usb_device *udev) +{ + struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); + unsigned int slot_id = udev->slot_id; + int ep_index = 0; /* control endpoint */ + struct xhci_container_ctx *in_ctx; + struct xhci_container_ctx *out_ctx; + struct xhci_input_control_ctx *ctrl_ctx; + struct xhci_ep_ctx *ep_ctx; + int max_packet_size; + int hw_max_packet_size; + int ret = 0; + + out_ctx = ctrl->devs[slot_id]->out_ctx; + xhci_inval_cache((uintptr_t)out_ctx->bytes, out_ctx->size); + + ep_ctx = xhci_get_ep_ctx(ctrl, out_ctx, ep_index); + hw_max_packet_size = MAX_PACKET_DECODED(le32_to_cpu(ep_ctx->ep_info2)); + max_packet_size = udev->epmaxpacketin[0]; + if (hw_max_packet_size != max_packet_size) { + dev_dbg(ctrl->dev, "Max Packet Size for ep 0 changed.\n"); + dev_dbg(ctrl->dev, "Max packet size in usb_device = %d\n", max_packet_size); + dev_dbg(ctrl->dev, "Max packet size in xHCI HW = %d\n", hw_max_packet_size); + dev_dbg(ctrl->dev, "Issuing evaluate context command.\n"); + + /* Set up the modified control endpoint 0 */ + xhci_endpoint_copy(ctrl, ctrl->devs[slot_id]->in_ctx, + ctrl->devs[slot_id]->out_ctx, ep_index); + in_ctx = ctrl->devs[slot_id]->in_ctx; + ep_ctx = xhci_get_ep_ctx(ctrl, in_ctx, ep_index); + ep_ctx->ep_info2 &= cpu_to_le32(~MAX_PACKET(MAX_PACKET_MASK)); + ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet_size)); + + /* + * Set up the input context flags for the command + * FIXME: This won't work if a non-default control endpoint + * changes max packet sizes. + */ + ctrl_ctx = xhci_get_input_control_ctx(in_ctx); + ctrl_ctx->add_flags = cpu_to_le32(EP0_FLAG); + ctrl_ctx->drop_flags = 0; + + ret = xhci_configure_endpoints(udev, true); + } + return ret; +} + +/** + * Clears the Change bits of the Port Status Register + * + * @param wValue request value + * @param wIndex request index + * @param addr address of posrt status register + * @param port_status state of port status register + * @return none + */ +static void xhci_clear_port_change_bit(struct usb_device *udev, u16 wValue, + u16 wIndex, volatile uint32_t *addr, u32 port_status) +{ + char *port_change_bit; + u32 status; + + switch (wValue) { + case USB_PORT_FEAT_C_RESET: + status = PORT_RC; + port_change_bit = "reset"; + break; + case USB_PORT_FEAT_C_CONNECTION: + status = PORT_CSC; + port_change_bit = "connect"; + break; + case USB_PORT_FEAT_C_OVER_CURRENT: + status = PORT_OCC; + port_change_bit = "over-current"; + break; + case USB_PORT_FEAT_C_ENABLE: + status = PORT_PEC; + port_change_bit = "enable/disable"; + break; + case USB_PORT_FEAT_C_SUSPEND: + status = PORT_PLC; + port_change_bit = "suspend/resume"; + break; + default: + /* Should never happen */ + return; + } + + /* Change bits are all write 1 to clear */ + xhci_writel(addr, port_status | status); + + port_status = xhci_readl(addr); + dev_dbg(&udev->dev, "clear port %s change, actual port %d status = 0x%x\n", + port_change_bit, wIndex, port_status); +} + +/** + * Save Read Only (RO) bits and save read/write bits where + * writing a 0 clears the bit and writing a 1 sets the bit (RWS). + * For all other types (RW1S, RW1CS, RW, and RZ), writing a '0' has no effect. + * + * @param state state of the Port Status and Control Regsiter + * @return a value that would result in the port being in the + * same state, if the value was written to the port + * status control register. + */ +static u32 xhci_port_state_to_neutral(u32 state) +{ + /* Save read-only status and port state */ + return (state & XHCI_PORT_RO) | (state & XHCI_PORT_RWS); +} + +/** + * Submits the Requests to the XHCI Host Controller + * + * @param udev pointer to the USB device structure + * @param pipe contains the DIR_IN or OUT , devnum + * @param buffer buffer to be read/written based on the request + * @return returns 0 if successful else -1 on failure + */ +static int xhci_submit_root(struct usb_device *udev, unsigned long pipe, + void *buffer, struct devrequest *req) +{ + uint8_t tmpbuf[4]; + u16 typeReq; + const void *srcptr = NULL; + int len, srclen; + uint32_t reg; + volatile uint32_t *status_reg; + struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); + struct xhci_hccr *hccr = ctrl->hccr; + struct xhci_hcor *hcor = ctrl->hcor; + int max_ports = HCS_MAX_PORTS(xhci_readl(&hccr->cr_hcsparams1)); + + if ((req->requesttype & USB_RT_PORT) && + le16_to_cpu(req->index) > max_ports) { + dev_err(&udev->dev, "The request port(%d) exceeds maximum port number\n", + le16_to_cpu(req->index) - 1); + return -EINVAL; + } + + status_reg = (volatile uint32_t *) + (&hcor->portregs[le16_to_cpu(req->index) - 1].or_portsc); + srclen = 0; + + typeReq = req->request | req->requesttype << 8; + + switch (typeReq) { + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + switch (le16_to_cpu(req->value) >> 8) { + case USB_DT_DEVICE: + dev_dbg(&udev->dev, "USB_DT_DEVICE request\n"); + srcptr = &descriptor.device; + srclen = 0x12; + break; + case USB_DT_CONFIG: + dev_dbg(&udev->dev, "USB_DT_CONFIG config\n"); + srcptr = &descriptor.config; + srclen = 0x19; + break; + case USB_DT_STRING: + dev_dbg(&udev->dev, "USB_DT_STRING config\n"); + switch (le16_to_cpu(req->value) & 0xff) { + case 0: /* Language */ + srcptr = "\4\3\11\4"; + srclen = 4; + break; + case 1: /* Vendor String */ + srcptr = "\16\3U\0-\0B\0o\0o\0t\0"; + srclen = 14; + break; + case 2: /* Product Name */ + srcptr = "\52\3X\0H\0C\0I\0 " + "\0H\0o\0s\0t\0 " + "\0C\0o\0n\0t\0r\0o\0l\0l\0e\0r\0"; + srclen = 42; + break; + default: + dev_err(&udev->dev, "unknown value DT_STRING %x\n", + le16_to_cpu(req->value)); + goto unknown; + } + break; + default: + dev_err(&udev->dev, "unknown value %x\n", le16_to_cpu(req->value)); + goto unknown; + } + break; + case USB_REQ_GET_DESCRIPTOR | ((USB_DIR_IN | USB_RT_HUB) << 8): + switch (le16_to_cpu(req->value) >> 8) { + case USB_DT_HUB: + case USB_DT_SS_HUB: + dev_dbg(&udev->dev, "USB_DT_HUB config\n"); + srcptr = &ctrl->hub_desc; + srclen = 0x8; + break; + default: + dev_err(&udev->dev, "unknown value %x\n", le16_to_cpu(req->value)); + goto unknown; + } + break; + case USB_REQ_SET_ADDRESS | (USB_RECIP_DEVICE << 8): + dev_dbg(&udev->dev, "USB_REQ_SET_ADDRESS\n"); + ctrl->rootdev = le16_to_cpu(req->value); + break; + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + /* Do nothing */ + break; + case USB_REQ_GET_STATUS | ((USB_DIR_IN | USB_RT_HUB) << 8): + tmpbuf[0] = 1; /* USB_STATUS_SELFPOWERED */ + tmpbuf[1] = 0; + srcptr = tmpbuf; + srclen = 2; + break; + case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8): + memset(tmpbuf, 0, 4); + reg = xhci_readl(status_reg); + if (reg & PORT_CONNECT) { + tmpbuf[0] |= USB_PORT_STAT_CONNECTION; + switch (reg & DEV_SPEED_MASK) { + case XDEV_FS: + dev_dbg(&udev->dev, "SPEED = FULLSPEED\n"); + break; + case XDEV_LS: + dev_dbg(&udev->dev, "SPEED = LOWSPEED\n"); + tmpbuf[1] |= USB_PORT_STAT_LOW_SPEED >> 8; + break; + case XDEV_HS: + dev_dbg(&udev->dev, "SPEED = HIGHSPEED\n"); + tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8; + break; + case XDEV_SS: + dev_dbg(&udev->dev, "SPEED = SUPERSPEED\n"); + tmpbuf[1] |= USB_PORT_STAT_SUPER_SPEED >> 8; + break; + } + } + if (reg & PORT_PE) + tmpbuf[0] |= USB_PORT_STAT_ENABLE; + if ((reg & PORT_PLS_MASK) == XDEV_U3) + tmpbuf[0] |= USB_PORT_STAT_SUSPEND; + if (reg & PORT_OC) + tmpbuf[0] |= USB_PORT_STAT_OVERCURRENT; + if (reg & PORT_RESET) + tmpbuf[0] |= USB_PORT_STAT_RESET; + if (reg & PORT_POWER) + /* + * XXX: This Port power bit (for USB 3.0 hub) + * we are faking in USB 2.0 hub port status; + * since there's a change in bit positions in + * two: + * USB 2.0 port status PP is at position[8] + * USB 3.0 port status PP is at position[9] + * So, we are still keeping it at position [8] + */ + tmpbuf[1] |= USB_PORT_STAT_POWER >> 8; + if (reg & PORT_CSC) + tmpbuf[2] |= USB_PORT_STAT_C_CONNECTION; + if (reg & PORT_PEC) + tmpbuf[2] |= USB_PORT_STAT_C_ENABLE; + if (reg & PORT_OCC) + tmpbuf[2] |= USB_PORT_STAT_C_OVERCURRENT; + if (reg & PORT_RC) + tmpbuf[2] |= USB_PORT_STAT_C_RESET; + + srcptr = tmpbuf; + srclen = 4; + break; + case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): + reg = xhci_readl(status_reg); + reg = xhci_port_state_to_neutral(reg); + switch (le16_to_cpu(req->value)) { + case USB_PORT_FEAT_ENABLE: + reg |= PORT_PE; + xhci_writel(status_reg, reg); + break; + case USB_PORT_FEAT_POWER: + reg |= PORT_POWER; + xhci_writel(status_reg, reg); + break; + case USB_PORT_FEAT_RESET: + reg |= PORT_RESET; + xhci_writel(status_reg, reg); + break; + default: + dev_err(&udev->dev, "unknown feature %x\n", le16_to_cpu(req->value)); + goto unknown; + } + break; + case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): + reg = xhci_readl(status_reg); + reg = xhci_port_state_to_neutral(reg); + switch (le16_to_cpu(req->value)) { + case USB_PORT_FEAT_ENABLE: + reg &= ~PORT_PE; + break; + case USB_PORT_FEAT_POWER: + reg &= ~PORT_POWER; + break; + case USB_PORT_FEAT_C_RESET: + case USB_PORT_FEAT_C_CONNECTION: + case USB_PORT_FEAT_C_OVER_CURRENT: + case USB_PORT_FEAT_C_ENABLE: + xhci_clear_port_change_bit(udev, (le16_to_cpu(req->value)), + le16_to_cpu(req->index), + status_reg, reg); + break; + default: + dev_err(&udev->dev, "unknown feature %x\n", le16_to_cpu(req->value)); + goto unknown; + } + xhci_writel(status_reg, reg); + break; + default: + dev_err(&udev->dev, "Unknown request\n"); + goto unknown; + } + + dev_dbg(&udev->dev, "scrlen = %d\n req->length = %d\n", + srclen, le16_to_cpu(req->length)); + + len = min(srclen, (int)le16_to_cpu(req->length)); + + if (srcptr != NULL && len > 0) + memcpy(buffer, srcptr, len); + else + dev_dbg(&udev->dev, "Len is 0\n"); + + udev->act_len = len; + udev->status = 0; + + return 0; + +unknown: + udev->act_len = 0; + udev->status = USB_ST_STALLED; + + return -ENODEV; +} + +/** + * Submits the INT request to XHCI Host cotroller + * + * @param udev pointer to the USB device + * @param pipe contains the DIR_IN or OUT , devnum + * @param buffer buffer to be read/written based on the request + * @param length length of the buffer + * @param interval interval of the interrupt + * @return 0 + */ +static int _xhci_submit_int_msg(struct usb_device *udev, unsigned long pipe, + void *buffer, int length, int interval) +{ + if (usb_pipetype(pipe) != PIPE_INTERRUPT) { + dev_err(&udev->dev, "non-interrupt pipe (type=%lu)", usb_pipetype(pipe)); + return -EINVAL; + } + + /* + * xHCI uses normal TRBs for both bulk and interrupt. When the + * interrupt endpoint is to be serviced, the xHC will consume + * (at most) one TD. A TD (comprised of sg list entries) can + * take several service intervals to transmit. + */ + return xhci_bulk_tx(udev, pipe, length, buffer, 0); +} + +/** + * submit the BULK type of request to the USB Device + * + * @param udev pointer to the USB device + * @param pipe contains the DIR_IN or OUT , devnum + * @param buffer buffer to be read/written based on the request + * @param length length of the buffer + * @return returns 0 if successful else -1 on failure + */ +static int _xhci_submit_bulk_msg(struct usb_device *udev, unsigned long pipe, + void *buffer, int length, int timeout_ms) +{ + if (usb_pipetype(pipe) != PIPE_BULK) { + dev_err(&udev->dev, "non-bulk pipe (type=%lu)", usb_pipetype(pipe)); + return -EINVAL; + } + + return xhci_bulk_tx(udev, pipe, length, buffer, timeout_ms); +} + +/** + * submit the control type of request to the Root hub/Device based on the devnum + * + * @param udev pointer to the USB device + * @param pipe contains the DIR_IN or OUT , devnum + * @param buffer buffer to be read/written based on the request + * @param length length of the buffer + * @param setup Request type + * @param root_portnr Root port number that this device is on + * @return returns 0 if successful else -1 on failure + */ +static int _xhci_submit_control_msg(struct usb_device *udev, unsigned long pipe, + void *buffer, int length, + struct devrequest *setup, int root_portnr, + unsigned int timeout_ms) +{ + struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); + int ret = 0; + + if (usb_pipetype(pipe) != PIPE_CONTROL) { + dev_err(&udev->dev, "non-control pipe (type=%lu)", usb_pipetype(pipe)); + return -EINVAL; + } + + if (usb_pipedevice(pipe) == ctrl->rootdev) + return xhci_submit_root(udev, pipe, buffer, setup); + + if (setup->request == USB_REQ_SET_ADDRESS && + (setup->requesttype & USB_TYPE_MASK) == USB_TYPE_STANDARD) + return xhci_address_device(udev, root_portnr); + + if (setup->request == USB_REQ_SET_CONFIGURATION && + (setup->requesttype & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + ret = xhci_set_configuration(udev); + if (ret) { + dev_err(&udev->dev, "Failed to configure xHCI endpoint\n"); + return ret; + } + } + + return xhci_ctrl_tx(udev, pipe, setup, length, buffer, timeout_ms); +} + +static int xhci_lowlevel_init(struct xhci_ctrl *ctrl) +{ + struct xhci_hccr *hccr; + struct xhci_hcor *hcor; + uint32_t val; + uint32_t val2; + uint32_t reg; + + hccr = ctrl->hccr; + hcor = ctrl->hcor; + /* + * Program the Number of Device Slots Enabled field in the CONFIG + * register with the max value of slots the HC can handle. + */ + val = (xhci_readl(&hccr->cr_hcsparams1) & HCS_SLOTS_MASK); + val2 = xhci_readl(&hcor->or_config); + val |= (val2 & ~HCS_SLOTS_MASK); + xhci_writel(&hcor->or_config, val); + + /* initializing xhci data structures */ + if (xhci_mem_init(ctrl, hccr, hcor) < 0) + return -ENOMEM; + ctrl->hub_desc = descriptor.hub; + + reg = xhci_readl(&hccr->cr_hcsparams1); + ctrl->hub_desc.bNbrPorts = HCS_MAX_PORTS(reg); + + dev_dbg(ctrl->dev, "Register 0x%x NbrPorts %d\n", reg, ctrl->hub_desc.bNbrPorts); + + /* Port Indicators */ + reg = xhci_readl(&hccr->cr_hccparams); + if (HCS_INDICATOR(reg)) + put_unaligned(get_unaligned(&ctrl->hub_desc.wHubCharacteristics) + | 0x80, &ctrl->hub_desc.wHubCharacteristics); + + /* Port Power Control */ + if (HCC_PPC(reg)) + put_unaligned(get_unaligned(&ctrl->hub_desc.wHubCharacteristics) + | 0x01, &ctrl->hub_desc.wHubCharacteristics); + + if (xhci_start(ctrl)) { + xhci_reset(ctrl); + return -ENODEV; + } + + /* Zero'ing IRQ control register and IRQ pending register */ + xhci_writel(&ctrl->ir_set->irq_control, 0x0); + xhci_writel(&ctrl->ir_set->irq_pending, 0x0); + + reg = HC_VERSION(xhci_readl(&hccr->cr_capbase)); + dev_info(ctrl->dev, "USB XHCI %x.%02x\n", reg >> 8, reg & 0xff); + + return 0; +} + +static int xhci_lowlevel_stop(struct xhci_ctrl *ctrl) +{ + u32 temp; + + xhci_reset(ctrl); + + dev_dbg(ctrl->dev, "Disabling event ring interrupts\n"); + + temp = xhci_readl(&ctrl->hcor->or_usbsts); + xhci_writel(&ctrl->hcor->or_usbsts, temp & ~STS_EINT); + temp = xhci_readl(&ctrl->ir_set->irq_pending); + xhci_writel(&ctrl->ir_set->irq_pending, ER_IRQ_DISABLE(temp)); + + return 0; +} + +static int xhci_submit_control_msg(struct usb_device *udev, + unsigned long pipe, void *buffer, int length, + struct devrequest *setup, int timeout_ms) +{ + struct usb_device *dev; + int root_portnr = 0; + + dev = udev; + while (!usb_hub_is_root_hub(dev)) { + root_portnr = dev->portnr; + dev = dev->parent; + } + + return _xhci_submit_control_msg(udev, pipe, buffer, length, setup, + root_portnr, timeout_ms); +} + +static int xhci_submit_bulk_msg(struct usb_device *udev, + unsigned long pipe, void *buffer, int length, + int timeout_ms) +{ + return _xhci_submit_bulk_msg(udev, pipe, buffer, length, timeout_ms); +} + +static int xhci_submit_int_msg(struct usb_device *udev, + unsigned long pipe, void *buffer, int length, + int interval) +{ + return _xhci_submit_int_msg(udev, pipe, buffer, length, interval); +} + +static int xhci_alloc_device(struct usb_device *udev) +{ + return _xhci_alloc_device(udev); +} + +static int xhci_update_hub_device(struct usb_device *udev) +{ + struct usb_host *host = udev->host; + struct xhci_ctrl *ctrl = to_xhci(host); + struct usb_hub_device *hub = udev->hub; + struct xhci_virt_device *virt_dev; + struct xhci_input_control_ctx *ctrl_ctx; + struct xhci_container_ctx *out_ctx; + struct xhci_container_ctx *in_ctx; + struct xhci_slot_ctx *slot_ctx; + int slot_id = udev->slot_id; + unsigned think_time; + + /* Ignore root hubs */ + if (usb_hub_is_root_hub(udev)) + return 0; + + virt_dev = ctrl->devs[slot_id]; + BUG_ON(!virt_dev); + + out_ctx = virt_dev->out_ctx; + in_ctx = virt_dev->in_ctx; + + ctrl_ctx = xhci_get_input_control_ctx(in_ctx); + /* Initialize the input context control */ + ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG); + ctrl_ctx->drop_flags = 0; + + xhci_inval_cache((uintptr_t)out_ctx->bytes, out_ctx->size); + + /* slot context */ + xhci_slot_copy(ctrl, in_ctx, out_ctx); + slot_ctx = xhci_get_slot_ctx(ctrl, in_ctx); + + /* Update hub related fields */ + slot_ctx->dev_info |= cpu_to_le32(DEV_HUB); + /* + * refer to section 6.2.2: MTT should be 0 for full speed hub, + * but it may be already set to 1 when setup an xHCI virtual + * device, so clear it anyway. + */ + if (hub->tt.multi) + slot_ctx->dev_info |= cpu_to_le32(DEV_MTT); + else if (udev->speed == USB_SPEED_FULL) + slot_ctx->dev_info &= cpu_to_le32(~DEV_MTT); + slot_ctx->dev_info2 |= cpu_to_le32(XHCI_MAX_PORTS(udev->maxchild)); + /* + * Set TT think time - convert from ns to FS bit times. + * Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns + * + * 0 = 8 FS bit times, 1 = 16 FS bit times, + * 2 = 24 FS bit times, 3 = 32 FS bit times. + * + * This field shall be 0 if the device is not a high-spped hub. + */ + think_time = hub->tt.think_time; + if (think_time != 0) + think_time = (think_time / 666) - 1; + if (udev->speed == USB_SPEED_HIGH) + slot_ctx->tt_info |= cpu_to_le32(TT_THINK_TIME(think_time)); + slot_ctx->dev_state = 0; + + return xhci_configure_endpoints(udev, false); +} + +static __maybe_unused int xhci_get_max_xfer_size(size_t *size) +{ + /* + * xHCD allocates one segment which includes 64 TRBs for each endpoint + * and the last TRB in this segment is configured as a link TRB to form + * a TRB ring. Each TRB can transfer up to 64K bytes, however data + * buffers referenced by transfer TRBs shall not span 64KB boundaries. + * Hence the maximum number of TRBs we can use in one transfer is 62. + */ + *size = (TRBS_PER_SEGMENT - 2) * TRB_MAX_BUFF_SIZE; + + return 0; +} + +int xhci_register(struct xhci_ctrl *ctrl) +{ + struct usb_host *host; + struct device *dev = ctrl->dev; + int ret; + + dev_dbg(dev, "%s: hccr=%p, hcor=%p\n", __func__, ctrl->hccr, ctrl->hcor); + + host = &ctrl->host; + + /* + * XHCI needs to issue a Address device command to setup + * proper device context structures, before it can interact + * with the device. So a get_descriptor will fail before any + * of that is done for XHCI unlike EHCI. + */ + host->no_desc_before_addr = true; + + /* + * If xHCI doesn't have its own DT node, it'll be a child of a + * physical USB host controller device that should be used for DMA + */ + host->hw_dev = dev_of_node(dev) ? dev : dev->parent; + host->submit_int_msg = xhci_submit_int_msg; + host->submit_control_msg = xhci_submit_control_msg; + host->submit_bulk_msg = xhci_submit_bulk_msg; + host->alloc_device = xhci_alloc_device; + host->update_hub_device = xhci_update_hub_device; + + ret = xhci_reset(ctrl); + if (ret) + goto err; + + ret = xhci_lowlevel_init(ctrl); + if (ret) + goto err; + + usb_register_host(&ctrl->host); + + return 0; +err: + dev_dbg(dev, "%s: failed, ret=%d\n", __func__, ret); + return ret; +} + +int xhci_deregister(struct xhci_ctrl *ctrl) +{ + xhci_lowlevel_stop(ctrl); + xhci_cleanup(ctrl); + + return 0; +} + +/* + * xHCI platform driver + */ + +static int xhci_probe(struct device *dev) +{ + struct resource *iores; + struct xhci_ctrl *ctrl; + + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); + + ctrl = xzalloc(sizeof(*ctrl)); + + ctrl->dev = dev; + ctrl->hccr = IOMEM(iores->start); + ctrl->hcor = (struct xhci_hcor *)((uintptr_t)ctrl->hccr + + HC_LENGTH(xhci_readl(&(ctrl->hccr)->cr_capbase))); + + dev->priv = ctrl; + + return xhci_register(ctrl); +} + +static void xhci_remove(struct device *dev) +{ + struct xhci_ctrl *ctrl = dev->priv; + + xhci_deregister(ctrl); +} + +static struct driver xhci_driver = { + .name = "xHCI", + .probe = xhci_probe, + .remove = xhci_remove, +}; +device_platform_driver(xhci_driver); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 84a14dd1fc..37e8cee843 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1,85 +1,95 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * xHCI USB 3.0 Specification + * USB HOST XHCI Controller * - * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> + * Based on xHCI host controller driver in linux-kernel + * by Sarah Sharp. * - * Some code borrowed from the Linux xHCI driver - * Author: Sarah Sharp - * Copyright (C) 2008 Intel Corp. + * Copyright (C) 2008 Intel Corp. + * Author: Sarah Sharp * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * Copyright (C) 2013 Samsung Electronics Co.Ltd + * Authors: Vivek Gautam <gautam.vivek@samsung.com> + * Vikas Sajjan <vikas.sajjan@samsung.com> */ -#ifndef __XHCI_H -#define __XHCI_H +#ifndef HOST_XHCI_H_ +#define HOST_XHCI_H_ +#include <linux/types.h> +#include <io.h> #include <io-64-nonatomic-lo-hi.h> +#include <linux/list.h> -#define NUM_COMMAND_TRBS 8 -#define NUM_TRANSFER_TRBS 8 -#define NUM_EVENT_SEGM 1 /* only one supported */ -#define NUM_EVENT_TRBS 16 /* minimum 16 TRBS */ -#define MIN_EP_RINGS 3 /* Control + Bulk In/Out */ -#define MAX_EP_RINGS (MIN_EP_RINGS * USB_MAXCHILDREN) - -/* Up to 16 ms to halt an HC */ -#define XHCI_MAX_HALT_USEC (16 * 1000) - -/* Command and Status registers offset from the Operational Registers address */ -#define XHCI_CMD_OFFSET 0x00 -#define XHCI_STS_OFFSET 0x04 -/* HCCPARAMS offset from PCI base address */ -#define XHCI_HCC_PARAMS_OFFSET 0x10 -/* xHCI PCI Configuration Registers */ -#define XHCI_SBRN_OFFSET 0x60 - +#define MAX_EP_CTX_NUM 31 +#define XHCI_ALIGNMENT 64 +/* Generic timeout for XHCI events */ +#define XHCI_TIMEOUT 5000 /* Max number of USB devices for any host controller - limit in section 6.1 */ -#define MAX_HC_SLOTS 256 +#define MAX_HC_SLOTS 256 /* Section 5.3.3 - MaxPorts */ -#define MAX_HC_PORTS 127 +#define MAX_HC_PORTS 255 +/* Up to 32 ms to halt an HC */ +#define XHCI_MAX_HALT_USEC (32*1000) + +#define XHCI_MAX_RESET_USEC (250*1000) + +/* + * These bits are Read Only (RO) and should be saved and written to the + * registers: 0, 3, 10:13, 30 + * connect status, over-current status, port speed, and device removable. + * connect status and port speed are also sticky - meaning they're in + * the AUX well and they aren't changed by a hot, warm, or cold reset. + */ +#define XHCI_PORT_RO ((1 << 0) | (1 << 3) | (0xf << 10) | (1 << 30)) +/* + * These bits are RW; writing a 0 clears the bit, writing a 1 sets the bit: + * bits 5:8, 9, 14:15, 25:27 + * link state, port power, port indicator state, "wake on" enable state + */ +#define XHCI_PORT_RWS ((0xf << 5) | (1 << 9) | (0x3 << 14) | (0x7 << 25)) +/* + * These bits are RW; writing a 1 sets the bit, writing a 0 has no effect: + * bit 4 (port reset) + */ +#define XHCI_PORT_RW1S ((1 << 4)) +/* + * These bits are RW; writing a 1 clears the bit, writing a 0 has no effect: + * bits 1, 17, 18, 19, 20, 21, 22, 23 + * port enable/disable, and + * change bits: connect, PED, + * warm port reset changed (reserved zero for USB 2.0 ports), + * over-current, reset, link state, and L1 change + */ +#define XHCI_PORT_RW1CS ((1 << 1) | (0x7f << 17)) +/* + * Bit 16 is RW, and writing a '1' to it causes the link state control to be + * latched in + */ +#define XHCI_PORT_RW ((1 << 16)) /* - * xHCI register interface. - * This corresponds to the eXtensible Host Controller Interface (xHCI) - * Revision 0.95 specification + * These bits are Reserved Zero (RsvdZ) and zero should be written to them: + * bits 2, 24, 28:31 */ +#define XHCI_PORT_RZ ((1 << 2) | (1 << 24) | (0xf << 28)) -/** - * struct xhci_cap_regs - xHCI Host Controller Capability Registers. - * @hc_capbase: length of the capabilities register and HC version number - * @hcs_params1: HCSPARAMS1 - Structural Parameters 1 - * @hcs_params2: HCSPARAMS2 - Structural Parameters 2 - * @hcs_params3: HCSPARAMS3 - Structural Parameters 3 - * @hcc_params: HCCPARAMS - Capability Parameters - * @db_off: DBOFF - Doorbell array offset - * @run_regs_off: RTSOFF - Runtime register space offset +/* + * XHCI Register Space. */ -struct xhci_cap_regs { - __le32 hc_capbase; - __le32 hcs_params1; - __le32 hcs_params2; - __le32 hcs_params3; - __le32 hcc_params; - __le32 db_off; - __le32 run_regs_off; - /* Reserved up to (CAPLENGTH - 0x1C) */ -}; +struct xhci_hccr { + uint32_t cr_capbase; + uint32_t cr_hcsparams1; + uint32_t cr_hcsparams2; + uint32_t cr_hcsparams3; + uint32_t cr_hccparams; + uint32_t cr_dboff; + uint32_t cr_rtsoff; /* hc_capbase bitmasks */ /* bits 7:0 - how long is the Capabilities register */ -#define HC_LENGTH(p) ((p) & 0x00ff) -/* bits 31:16 */ +#define HC_LENGTH(p) XHCI_HC_LENGTH(p) +/* bits 31:16 */ #define HC_VERSION(p) (((p) >> 16) & 0xffff) /* HCSPARAMS1 - hcs_params1 - bitmasks */ @@ -89,7 +99,9 @@ struct xhci_cap_regs { /* bits 8:18, Max Interrupters */ #define HCS_MAX_INTRS(p) (((p) >> 8) & 0x7ff) /* bits 24:31, Max Ports - max value is 0x7F = 127 ports */ -#define HCS_MAX_PORTS(p) (((p) >> 24) & 0x7f) +#define HCS_MAX_PORTS_SHIFT 24 +#define HCS_MAX_PORTS_MASK (0xff << HCS_MAX_PORTS_SHIFT) +#define HCS_MAX_PORTS(p) (((p) >> 24) & 0xff) /* HCSPARAMS2 - hcs_params2 - bitmasks */ /* bits 0:3, frames or uframes that SW needs to queue transactions @@ -97,9 +109,10 @@ struct xhci_cap_regs { #define HCS_IST(p) (((p) >> 0) & 0xf) /* bits 4:7, max number of Event Ring segments */ #define HCS_ERST_MAX(p) (((p) >> 4) & 0xf) +/* bits 21:25 Hi 5 bits of Scratchpad buffers SW must allocate for the HW */ /* bit 26 Scratchpad restore - for save/restore HW state - not used yet */ -/* bits 27:31 number of Scratchpad buffers SW must allocate for the HW */ -#define HCS_MAX_SCRATCHPAD(p) (((p) >> 27) & 0x1f) +/* bits 27:31 Lo 5 bits of Scratchpad buffers SW must allocate for the HW */ +#define HCS_MAX_SCRATCHPAD(p) ((((p) >> 16) & 0x3e0) | (((p) >> 27) & 0x1f)) /* HCSPARAMS3 - hcs_params3 - bitmasks */ /* bits 0:7, Max U1 to U0 latency for the roothub ports */ @@ -109,141 +122,105 @@ struct xhci_cap_regs { /* HCCPARAMS - hcc_params - bitmasks */ /* true: HC can use 64-bit address pointers */ -#define HCC_64BIT_ADDR(p) ((p) & BIT(0)) +#define HCC_64BIT_ADDR(p) ((p) & (1 << 0)) /* true: HC can do bandwidth negotiation */ -#define HCC_BANDWIDTH_NEG(p) ((p) & BIT(1)) +#define HCC_BANDWIDTH_NEG(p) ((p) & (1 << 1)) /* true: HC uses 64-byte Device Context structures * FIXME 64-byte context structures aren't supported yet. */ -#define HCC_64BYTE_CONTEXT(p) ((p) & BIT(2)) -#define HCC_CTX_SIZE(p) (HCC_64BYTE_CONTEXT(p) ? 64 : 32) +#define HCC_64BYTE_CONTEXT(p) ((p) & (1 << 2)) /* true: HC has port power switches */ -#define HCC_PPC(p) ((p) & BIT(3)) +#define HCC_PPC(p) ((p) & (1 << 3)) /* true: HC has port indicators */ -#define HCC_INDICATOR(p) ((p) & BIT(4)) +#define HCS_INDICATOR(p) ((p) & (1 << 4)) /* true: HC has Light HC Reset Capability */ -#define HCC_LIGHT_RESET(p) ((p) & BIT(5)) +#define HCC_LIGHT_RESET(p) ((p) & (1 << 5)) /* true: HC supports latency tolerance messaging */ -#define HCC_LTC(p) ((p) & BIT(6)) +#define HCC_LTC(p) ((p) & (1 << 6)) /* true: no secondary Stream ID Support */ -#define HCC_NSS(p) ((p) & BIT(7)) +#define HCC_NSS(p) ((p) & (1 << 7)) /* Max size for Primary Stream Arrays - 2^(n+1), where n is bits 12:15 */ #define HCC_MAX_PSA(p) (1 << ((((p) >> 12) & 0xf) + 1)) /* Extended Capabilities pointer from PCI base - section 5.3.6 */ -#define HCC_EXT_CAPS(p) (((p) >> 16) & 0xffff) +#define HCC_EXT_CAPS(p) XHCI_HCC_EXT_CAPS(p) /* db_off bitmask - bits 0:1 reserved */ -#define DBOFF_MASK (~0x3) +#define DBOFF_MASK (~0x3) /* run_regs_off bitmask - bits 0:4 reserved */ -#define RTSOFF_MASK (~0x1f) +#define RTSOFF_MASK (~0x1f) -/* Number of registers per port */ -#define NUM_PORT_REGS 4 +}; -#define PORTSC 0 -#define PORTPMSC 1 -#define PORTLI 2 -#define PORTHLPMC 3 +struct xhci_hcor_port_regs { + volatile uint32_t or_portsc; + volatile uint32_t or_portpmsc; + volatile uint32_t or_portli; + volatile uint32_t reserved_3; +}; -/** - * struct xhci_op_regs - xHCI Host Controller Operational Registers. - * @command: USBCMD - xHC command register - * @status: USBSTS - xHC status register - * @page_size: This indicates the page size that the host controller - * supports. If bit n is set, the HC supports a page size - * of 2^(n+12), up to a 128MB page size. - * 4K is the minimum page size. - * @cmd_ring: CRP - 64-bit Command Ring Pointer - * @dcbaa_ptr: DCBAAP - 64-bit Device Context Base Address Array Pointer - * @config_reg: CONFIG - Configure Register - * @port_status_base: PORTSCn - base address for Port Status and Control - * Each port has a Port Status and Control register, - * followed by a Port Power Management Status and Control - * register, a Port Link Info register, and a reserved - * register. - * @port_power_base: PORTPMSCn - base address for - * Port Power Management Status and Control - * @port_link_base: PORTLIn - base address for Port Link Info (current - * Link PM state and control) for USB 2.1 and USB 3.0 - * devices. - */ -struct xhci_op_regs { - __le32 command; - __le32 status; - __le32 page_size; - __le32 reserved1; - __le32 reserved2; - __le32 dev_notification; - __le64 cmd_ring; - /* rsvd: offset 0x20-2F */ - __le32 reserved3[4]; - __le64 dcbaa_ptr; - __le32 config_reg; - /* rsvd: offset 0x3C-3FF */ - __le32 reserved4[241]; - /* port 1 registers, which serve as a base address for other ports */ - __le32 port_status_base; - __le32 port_power_base; - __le32 port_link_base; - __le32 reserved5; - /* registers for ports 2-255 */ - __le32 reserved6[NUM_PORT_REGS*254]; +struct xhci_hcor { + volatile uint32_t or_usbcmd; + volatile uint32_t or_usbsts; + volatile uint32_t or_pagesize; + volatile uint32_t reserved_0[2]; + volatile uint32_t or_dnctrl; + volatile uint64_t or_crcr; + volatile uint32_t reserved_1[4]; + volatile uint64_t or_dcbaap; + volatile uint32_t or_config; + volatile uint32_t reserved_2[241]; + struct xhci_hcor_port_regs portregs[MAX_HC_PORTS]; }; /* USBCMD - USB command - command bitmasks */ /* start/stop HC execution - do not write unless HC is halted*/ -#define CMD_RUN BIT(0) +#define CMD_RUN XHCI_CMD_RUN /* Reset HC - resets internal HC state machine and all registers (except * PCI config regs). HC does NOT drive a USB reset on the downstream ports. * The xHCI driver must reinitialize the xHC after setting this bit. */ -#define CMD_RESET BIT(1) +#define CMD_RESET (1 << 1) /* Event Interrupt Enable - a '1' allows interrupts from the host controller */ -#define CMD_EIE BIT(2) +#define CMD_EIE XHCI_CMD_EIE /* Host System Error Interrupt Enable - get out-of-band signal for HC errors */ -#define CMD_HSEIE BIT(3) +#define CMD_HSEIE XHCI_CMD_HSEIE /* bits 4:6 are reserved (and should be preserved on writes). */ /* light reset (port status stays unchanged) - reset completed when this is 0 */ -#define CMD_LRESET BIT(7) +#define CMD_LRESET (1 << 7) /* host controller save/restore state. */ -#define CMD_CSS BIT(8) -#define CMD_CRS BIT(9) +#define CMD_CSS (1 << 8) +#define CMD_CRS (1 << 9) /* Enable Wrap Event - '1' means xHC generates an event when MFINDEX wraps. */ -#define CMD_EWE BIT(10) +#define CMD_EWE XHCI_CMD_EWE /* MFINDEX power management - '1' means xHC can stop MFINDEX counter if all root * hubs are in U3 (selective suspend), disconnect, disabled, or powered-off. * '0' means the xHC can power it off if all ports are in the disconnect, * disabled, or powered-off state. */ -#define CMD_PM_INDEX BIT(11) +#define CMD_PM_INDEX (1 << 11) /* bits 12:31 are reserved (and should be preserved on writes). */ -#define XHCI_IRQS (CMD_EIE | CMD_HSEIE | CMD_EWE) - -/* IMAN - Interrupt Management Register */ -#define IMAN_IE BIT(1) -#define IMAN_IP BIT(0) /* USBSTS - USB status - status bitmasks */ /* HC not running - set to 1 when run/stop bit is cleared. */ -#define STS_HALT BIT(0) +#define STS_HALT XHCI_STS_HALT /* serious error, e.g. PCI parity error. The HC will clear the run/stop bit. */ -#define STS_FATAL BIT(2) +#define STS_FATAL (1 << 2) /* event interrupt - clear this prior to clearing any IP flags in IR set*/ -#define STS_EINT BIT(3) +#define STS_EINT (1 << 3) /* port change detect */ -#define STS_PORT BIT(4) +#define STS_PORT (1 << 4) /* bits 5:7 reserved and zeroed */ /* save state status - '1' means xHC is saving state */ -#define STS_SAVE BIT(8) +#define STS_SAVE (1 << 8) /* restore state status - '1' means xHC is restoring state */ -#define STS_RESTORE BIT(9) +#define STS_RESTORE (1 << 9) /* true: save or restore error */ -#define STS_SRE BIT(10) +#define STS_SRE (1 << 10) /* true: Controller Not Ready to accept doorbell or op reg writes after reset */ -#define STS_CNR BIT(11) +#define STS_CNR XHCI_STS_CNR /* true: internal Host Controller Error - SW needs to reset and reinitialize */ -#define STS_HCE BIT(12) +#define STS_HCE (1 << 12) /* bits 13:31 reserved and should be preserved */ /* @@ -251,51 +228,51 @@ struct xhci_op_regs { * Generate a device notification event when the HC sees a transaction with a * notification type that matches a bit set in this bit field. */ -#define DEV_NOTE_MASK (0xffff) -#define ENABLE_DEV_NOTE(x) BIT(x) +#define DEV_NOTE_MASK (0xffff) +#define ENABLE_DEV_NOTE(x) (1 << (x)) /* Most of the device notification types should only be used for debug. * SW does need to pay attention to function wake notifications. */ -#define DEV_NOTE_FWAKE ENABLE_DEV_NOTE(1) +#define DEV_NOTE_FWAKE ENABLE_DEV_NOTE(1) /* CRCR - Command Ring Control Register - cmd_ring bitmasks */ /* bit 0 is the command ring cycle state */ /* stop ring operation after completion of the currently executing command */ -#define CMD_RING_PAUSE BIT(1) +#define CMD_RING_PAUSE (1 << 1) /* stop ring immediately - abort the currently executing command */ -#define CMD_RING_ABORT BIT(2) +#define CMD_RING_ABORT (1 << 2) /* true: command ring is running */ -#define CMD_RING_RUNNING BIT(3) +#define CMD_RING_RUNNING (1 << 3) /* bits 4:5 reserved and should be preserved */ /* Command Ring pointer - bit mask for the lower 32 bits. */ #define CMD_RING_RSVD_BITS (0x3f) /* CONFIG - Configure Register - config_reg bitmasks */ /* bits 0:7 - maximum number of device slots enabled (NumSlotsEn) */ -#define MAX_DEVS(p) ((p) & 0xff) +#define MAX_DEVS(p) ((p) & 0xff) /* bits 8:31 - reserved and should be preserved */ /* PORTSC - Port Status and Control Register - port_status_base bitmasks */ /* true: device connected */ -#define PORT_CONNECT BIT(0) +#define PORT_CONNECT (1 << 0) /* true: port enabled */ -#define PORT_PE BIT(1) +#define PORT_PE (1 << 1) /* bit 2 reserved and zeroed */ /* true: port has an over-current condition */ -#define PORT_OC BIT(3) +#define PORT_OC (1 << 3) /* true: port reset signaling asserted */ -#define PORT_RESET BIT(4) +#define PORT_RESET (1 << 4) /* Port Link State - bits 5:8 * A read gives the current link PM state of the port, * a write with Link State Write Strobe set sets the link state. */ -#define PORT_PLS_MASK (0xf << 5) -#define XDEV_U0 (0x0 << 5) -#define XDEV_U2 (0x2 << 5) -#define XDEV_U3 (0x3 << 5) -#define XDEV_RESUME (0xf << 5) +#define PORT_PLS_MASK (0xf << 5) +#define XDEV_U0 (0x0 << 5) +#define XDEV_U2 (0x2 << 5) +#define XDEV_U3 (0x3 << 5) +#define XDEV_RESUME (0xf << 5) /* true: port has power (see HCC_PPC) */ -#define PORT_POWER BIT(9) +#define PORT_POWER (1 << 9) /* bits 10:13 indicate device speed: * 0 - undefined speed - port hasn't be initialized by a reset yet * 1 - full speed @@ -305,179 +282,153 @@ struct xhci_op_regs { * 5-15 reserved */ #define DEV_SPEED_MASK (0xf << 10) -#define XDEV_FS (0x1 << 10) -#define XDEV_LS (0x2 << 10) -#define XDEV_HS (0x3 << 10) -#define XDEV_SS (0x4 << 10) +#define XDEV_FS (0x1 << 10) +#define XDEV_LS (0x2 << 10) +#define XDEV_HS (0x3 << 10) +#define XDEV_SS (0x4 << 10) #define DEV_UNDEFSPEED(p) (((p) & DEV_SPEED_MASK) == (0x0<<10)) #define DEV_FULLSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_FS) #define DEV_LOWSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_LS) #define DEV_HIGHSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_HS) #define DEV_SUPERSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_SS) /* Bits 20:23 in the Slot Context are the speed for the device */ -#define SLOT_SPEED_FS (XDEV_FS << 10) -#define SLOT_SPEED_LS (XDEV_LS << 10) -#define SLOT_SPEED_HS (XDEV_HS << 10) -#define SLOT_SPEED_SS (XDEV_SS << 10) +#define SLOT_SPEED_FS (XDEV_FS << 10) +#define SLOT_SPEED_LS (XDEV_LS << 10) +#define SLOT_SPEED_HS (XDEV_HS << 10) +#define SLOT_SPEED_SS (XDEV_SS << 10) /* Port Indicator Control */ -#define PORT_LED_OFF (0 << 14) -#define PORT_LED_AMBER (1 << 14) -#define PORT_LED_GREEN (2 << 14) -#define PORT_LED_MASK (3 << 14) +#define PORT_LED_OFF (0 << 14) +#define PORT_LED_AMBER (1 << 14) +#define PORT_LED_GREEN (2 << 14) +#define PORT_LED_MASK (3 << 14) /* Port Link State Write Strobe - set this when changing link state */ -#define PORT_LINK_STROBE BIT(16) +#define PORT_LINK_STROBE (1 << 16) /* true: connect status change */ -#define PORT_CSC BIT(17) +#define PORT_CSC (1 << 17) /* true: port enable change */ -#define PORT_PEC BIT(18) +#define PORT_PEC (1 << 18) /* true: warm reset for a USB 3.0 device is done. A "hot" reset puts the port * into an enabled state, and the device into the default state. A "warm" reset * also resets the link, forcing the device through the link training sequence. * SW can also look at the Port Reset register to see when warm reset is done. */ -#define PORT_WRC BIT(19) +#define PORT_WRC (1 << 19) /* true: over-current change */ -#define PORT_OCC BIT(20) +#define PORT_OCC (1 << 20) /* true: reset change - 1 to 0 transition of PORT_RESET */ -#define PORT_RC BIT(21) +#define PORT_RC (1 << 21) /* port link status change - set on some port link state transitions: - * Transition Reason - * ------------------------------------------------------------------------------ - * - U3 to Resume Wakeup signaling from a device - * - Resume to Recovery to U0 USB 3.0 device resume - * - Resume to U0 USB 2.0 device resume - * - U3 to Recovery to U0 Software resume of USB 3.0 device complete - * - U3 to U0 Software resume of USB 2.0 device complete - * - U2 to U0 L1 resume of USB 2.1 device complete - * - U0 to U0 (???) L1 entry rejection by USB 2.1 device - * - U0 to disabled L1 entry error with USB 2.1 device - * - Any state to inactive Error on USB 3.0 port + * Transition Reason + * -------------------------------------------------------------------------- + * - U3 to Resume Wakeup signaling from a device + * - Resume to Recovery to U0 USB 3.0 device resume + * - Resume to U0 USB 2.0 device resume + * - U3 to Recovery to U0 Software resume of USB 3.0 device complete + * - U3 to U0 Software resume of USB 2.0 device complete + * - U2 to U0 L1 resume of USB 2.1 device complete + * - U0 to U0 (???) L1 entry rejection by USB 2.1 device + * - U0 to disabled L1 entry error with USB 2.1 device + * - Any state to inactive Error on USB 3.0 port */ -#define PORT_PLC BIT(22) +#define PORT_PLC (1 << 22) /* port configure error change - port failed to configure its link partner */ -#define PORT_CEC BIT(23) -/* Cold Attach Status - xHC can set this bit to report device attached during - * Sx state. Warm port reset should be perfomed to clear this bit and move port - * to connected state. - */ -#define PORT_CAS BIT(24) +#define PORT_CEC (1 << 23) +/* bit 24 reserved */ /* wake on connect (enable) */ -#define PORT_WKCONN_E BIT(25) +#define PORT_WKCONN_E (1 << 25) /* wake on disconnect (enable) */ -#define PORT_WKDISC_E BIT(26) +#define PORT_WKDISC_E (1 << 26) /* wake on over-current (enable) */ -#define PORT_WKOC_E BIT(27) +#define PORT_WKOC_E (1 << 27) /* bits 28:29 reserved */ /* true: device is removable - for USB 3.0 roothub emulation */ -#define PORT_DEV_REMOVE BIT(30) +#define PORT_DEV_REMOVE (1 << 30) /* Initiate a warm port reset - complete when PORT_WRC is '1' */ -#define PORT_WR BIT(31) +#define PORT_WR (1 << 31) /* We mark duplicate entries with -1 */ -#define DUPLICATE_ENTRY ((u8)(-1)) +#define DUPLICATE_ENTRY ((u8)(-1)) /* Port Power Management Status and Control - port_power_base bitmasks */ /* Inactivity timer value for transitions into U1, in microseconds. * Timeout can be up to 127us. 0xFF means an infinite timeout. */ #define PORT_U1_TIMEOUT(p) ((p) & 0xff) -#define PORT_U1_TIMEOUT_MASK 0xff /* Inactivity timer value for transitions into U2 */ #define PORT_U2_TIMEOUT(p) (((p) & 0xff) << 8) -#define PORT_U2_TIMEOUT_MASK (0xff << 8) /* Bits 24:31 for port testing */ /* USB2 Protocol PORTSPMSC */ -#define PORT_L1S_MASK 0x7 -#define PORT_L1S_SUCCESS 0x1 -#define PORT_RWE BIT(3) -#define PORT_HIRD(p) (((p) & 0xf) << 4) -#define PORT_HIRD_MASK (0xf << 4) -#define PORT_L1DS_MASK (0xff << 8) -#define PORT_L1DS(p) (((p) & 0xff) << 8) -#define PORT_HLE BIT(16) - -/* USB2 Protocol PORTHLPMC */ -#define PORT_HIRDM(p) ((p) & 3) -#define PORT_L1_TIMEOUT(p) (((p) & 0xff) << 2) -#define PORT_BESLD(p) (((p) & 0xf) << 10) - -/* use 512 microseconds as USB2 LPM L1 default timeout. */ -#define XHCI_L1_TIMEOUT 512 - -/* Set default HIRD/BESL value to 4 (350/400us) for USB2 L1 LPM resume latency. - * Safe to use with mixed HIRD and BESL systems (host and device) and is used - * by other operating systems. - * - * XHCI 1.0 errata 8/14/12 Table 13 notes: - * "Software should choose xHC BESL/BESLD field values that do not violate a - * device's resume latency requirements, - * e.g. not program values > '4' if BLC = '1' and a HIRD device is attached, - * or not program values < '4' if BLC = '0' and a BESL device is attached. - */ -#define XHCI_DEFAULT_BESL 4 +#define PORT_L1S_MASK 7 +#define PORT_L1S_SUCCESS 1 +#define PORT_RWE (1 << 3) +#define PORT_HIRD(p) (((p) & 0xf) << 4) +#define PORT_HIRD_MASK (0xf << 4) +#define PORT_L1DS(p) (((p) & 0xff) << 8) +#define PORT_HLE (1 << 16) /** - * struct xhci_intr_reg - Interrupt Register Set - * @irq_pending: IMAN - Interrupt Management Register. Used to enable - * interrupts and check for pending interrupts. - * @irq_control: IMOD - Interrupt Moderation Register. - * Used to throttle interrupts. - * @erst_size: Number of segments in the Event Ring Segment Table (ERST). - * @erst_base: ERST base address. - * @erst_dequeue: Event ring dequeue pointer. - * - * Each interrupter (defined by a MSI-X vector) has an event ring and an Event - * Ring Segment Table (ERST) associated with it. The event ring is comprised of - * multiple segments of the same size. The HC places events on the ring and - * "updates the Cycle bit in the TRBs to indicate to software the current - * position of the Enqueue Pointer." The HCD (Linux) processes those events and - * updates the dequeue pointer. - */ +* struct xhci_intr_reg - Interrupt Register Set +* @irq_pending: IMAN - Interrupt Management Register. Used to enable +* interrupts and check for pending interrupts. +* @irq_control: IMOD - Interrupt Moderation Register. +* Used to throttle interrupts. +* @erst_size: Number of segments in the + Event Ring Segment Table (ERST). +* @erst_base: ERST base address. +* @erst_dequeue: Event ring dequeue pointer. +* +* Each interrupter (defined by a MSI-X vector) has an event ring and an Event +* Ring Segment Table (ERST) associated with it. +* The event ring is comprised of multiple segments of the same size. +* The HC places events on the ring and "updates the Cycle bit in the TRBs to +* indicate to software the current position of the Enqueue Pointer." +* The HCD (Linux) processes those events and updates the dequeue pointer. +*/ struct xhci_intr_reg { - __le32 irq_pending; - __le32 irq_control; - __le32 erst_size; - __le32 rsvd; - __le64 erst_base; - __le64 erst_dequeue; + volatile __le32 irq_pending; + volatile __le32 irq_control; + volatile __le32 erst_size; + volatile __le32 rsvd; + volatile __le64 erst_base; + volatile __le64 erst_dequeue; }; /* irq_pending bitmasks */ -#define ER_IRQ_PENDING(p) ((p) & 0x1) +#define ER_IRQ_PENDING(p) ((p) & 0x1) /* bits 2:31 need to be preserved */ /* THIS IS BUGGY - FIXME - IP IS WRITE 1 TO CLEAR */ -#define ER_IRQ_CLEAR(p) ((p) & 0xfffffffe) -#define ER_IRQ_ENABLE(p) ((ER_IRQ_CLEAR(p)) | 0x2) -#define ER_IRQ_DISABLE(p) ((ER_IRQ_CLEAR(p)) & ~(0x2)) +#define ER_IRQ_CLEAR(p) ((p) & 0xfffffffe) +#define ER_IRQ_ENABLE(p) ((ER_IRQ_CLEAR(p)) | 0x2) +#define ER_IRQ_DISABLE(p) ((ER_IRQ_CLEAR(p)) & ~(0x2)) /* irq_control bitmasks */ /* Minimum interval between interrupts (in 250ns intervals). The interval * between interrupts will be longer if there are no events on the event ring. * Default is 4000 (1 ms). */ -#define ER_IRQ_INTERVAL_MASK 0xffff +#define ER_IRQ_INTERVAL_MASK (0xffff) /* Counter used to count down the time to the next interrupt - HW use only */ #define ER_IRQ_COUNTER_MASK (0xffff << 16) /* erst_size bitmasks */ /* Preserve bits 16:31 of erst_size */ -#define ERST_SIZE_MASK (0xffff << 16) +#define ERST_SIZE_MASK (0xffff << 16) /* erst_dequeue bitmasks */ /* Dequeue ERST Segment Index (DESI) - Segment number (or alias) * where the current dequeue pointer lies. This is an optional HW hint. */ -#define ERST_DESI_MASK 0x7 +#define ERST_DESI_MASK (0x7) /* Event Handler Busy (EHB) - is the event ring scheduled to be serviced by * a work queue (or delayed service routine)? */ -#define ERST_EHB BIT(3) -#define ERST_PTR_MASK 0xf +#define ERST_EHB (1 << 3) +#define ERST_PTR_MASK (0xf) /** * struct xhci_run_regs - * @microframe_index: MFINDEX - current microframe number + * @microframe_index: MFINDEX - current microframe number * * Section 5.5 Host Controller Runtime Registers: * "Software should read and write these registers using only Dword (32 bit) @@ -499,21 +450,38 @@ struct xhci_run_regs { * Section 5.6 */ struct xhci_doorbell_array { - __le32 doorbell[256]; + volatile __le32 doorbell[256]; }; #define DB_VALUE(ep, stream) ((((ep) + 1) & 0xff) | ((stream) << 16)) #define DB_VALUE_HOST 0x00000000 /** + * struct xhci_protocol_caps + * @revision: major revision, minor revision, capability ID, + * and next capability pointer. + * @name_string: Four ASCII characters to say which spec this xHC + * follows, typically "USB ". + * @port_info: Port offset, count, and protocol-defined information. + */ +struct xhci_protocol_caps { + u32 revision; + u32 name_string; + u32 port_info; +}; + +#define XHCI_EXT_PORT_MAJOR(x) (((x) >> 24) & 0xff) +#define XHCI_EXT_PORT_OFF(x) ((x) & 0xff) +#define XHCI_EXT_PORT_COUNT(x) (((x) >> 8) & 0xff) + +/** * struct xhci_container_ctx * @type: Type of context. Used to calculated offsets to contained contexts. * @size: Size of the context data * @bytes: The raw context data given to HW - * @dma: dma address of the bytes * * Represents either a Device or Input context. Holds a pointer to the raw - * memory used for the context (bytes) and dma address of it (dma). + * memory used for the context (bytes). */ struct xhci_container_ctx { unsigned type; @@ -521,7 +489,6 @@ struct xhci_container_ctx { #define XHCI_CTX_TYPE_INPUT 0x2 int size; - u8 *bytes; dma_addr_t dma; }; @@ -548,29 +515,31 @@ struct xhci_slot_ctx { /* dev_info bitmasks */ /* Route String - 0:19 */ -#define ROUTE_STRING_MASK 0xfffff +#define ROUTE_STRING_MASK (0xfffff) /* Device speed - values defined by PORTSC Device Speed field - 20:23 */ #define DEV_SPEED (0xf << 20) /* bit 24 reserved */ /* Is this LS/FS device connected through a HS hub? - bit 25 */ -#define DEV_MTT BIT(25) +#define DEV_MTT (0x1 << 25) /* Set if the device is a hub - bit 26 */ -#define DEV_HUB BIT(26) +#define DEV_HUB (0x1 << 26) /* Index of the last valid endpoint context in this device context - 27:31 */ #define LAST_CTX_MASK (0x1f << 27) #define LAST_CTX(p) ((p) << 27) #define LAST_CTX_TO_EP_NUM(p) (((p) >> 27) - 1) -#define SLOT_FLAG BIT(0) -#define EP0_FLAG BIT(1) +#define SLOT_FLAG (1 << 0) +#define EP0_FLAG (1 << 1) /* dev_info2 bitmasks */ /* Max Exit Latency (ms) - worst case time to wake up all links in dev path */ -#define MAX_EXIT 0xffff +#define MAX_EXIT (0xffff) /* Root hub port number that is needed to access the USB device */ -#define ROOT_HUB_PORT(p) (((p) & 0xff) << 16) +#define ROOT_HUB_PORT(p) (((p) & 0xff) << 16) +#define ROOT_HUB_PORT_MASK (0xff) +#define ROOT_HUB_PORT_SHIFT (16) #define DEVINFO_TO_ROOT_HUB_PORT(p) (((p) >> 16) & 0xff) /* Maximum number of ports under a hub device */ -#define XHCI_MAX_PORTS(p) (((p) & 0xff) << 24) +#define XHCI_MAX_PORTS(p) (((p) & 0xff) << 24) /* tt_info bitmasks */ /* @@ -578,43 +547,44 @@ struct xhci_slot_ctx { * The Slot ID of the hub that isolates the high speed signaling from * this low or full-speed device. '0' if attached to root hub port. */ -#define TT_SLOT 0xff +#define TT_SLOT(p) (((p) & 0xff) << 0) /* * The number of the downstream facing port of the high-speed hub * '0' if the device is not low or full speed. */ -#define TT_PORT (0xff << 8) +#define TT_PORT(p) (((p) & 0xff) << 8) #define TT_THINK_TIME(p) (((p) & 0x3) << 16) /* dev_state bitmasks */ /* USB device address - assigned by the HC */ -#define DEV_ADDR_MASK 0xff +#define DEV_ADDR_MASK (0xff) /* bits 8:26 reserved */ /* Slot state */ #define SLOT_STATE (0x1f << 27) #define GET_SLOT_STATE(p) (((p) & (0x1f << 27)) >> 27) -#define SLOT_STATE_DISABLED 0x0 +#define SLOT_STATE_DISABLED 0 #define SLOT_STATE_ENABLED SLOT_STATE_DISABLED -#define SLOT_STATE_DEFAULT 0x1 -#define SLOT_STATE_ADDRESSED 0x2 -#define SLOT_STATE_CONFIGURED 0x3 +#define SLOT_STATE_DEFAULT 1 +#define SLOT_STATE_ADDRESSED 2 +#define SLOT_STATE_CONFIGURED 3 /** * struct xhci_ep_ctx * @ep_info: endpoint state, streams, mult, and interval information. * @ep_info2: information on endpoint type, max packet size, max burst size, - * error count, and whether the HC will force an event for all - * transactions. + * error count, and whether the HC will force an event for all + * transactions. * @deq: 64-bit ring dequeue pointer address. If the endpoint only - * defines one stream, this points to the endpoint transfer ring. - * Otherwise, it points to a stream context array, which has a - * ring pointer for each flow. - * @tx_info: Average TRB lengths for the endpoint ring and - * max payload within an Endpoint Service Interval Time (ESIT). + * defines one stream, this points to the endpoint transfer ring. + * Otherwise, it points to a stream context array, which has a + * ring pointer for each flow. + * @tx_info: + * Average TRB lengths for the endpoint ring and + * max payload within an Endpoint Service Interval Time (ESIT). * - * Endpoint Context - section 6.2.1.2. This assumes the HC uses 32-byte context - * structures. If the HC uses 64-byte contexts, there is an additional 32 bytes + * Endpoint Context - section 6.2.1.2.This assumes the HC uses 32-byte context + * structures.If the HC uses 64-byte contexts, there is an additional 32 bytes * reserved at the end of the endpoint context for HC internal use. */ struct xhci_ep_ctx { @@ -636,49 +606,55 @@ struct xhci_ep_ctx { * 4 - TRB error * 5-7 - reserved */ -#define EP_STATE_MASK 0xf -#define EP_STATE_DISABLED 0x0 -#define EP_STATE_RUNNING 0x1 -#define EP_STATE_HALTED 0x2 -#define EP_STATE_STOPPED 0x3 -#define EP_STATE_ERROR 0x4 +#define EP_STATE_MASK (0xf) +#define EP_STATE_DISABLED 0 +#define EP_STATE_RUNNING 1 +#define EP_STATE_HALTED 2 +#define EP_STATE_STOPPED 3 +#define EP_STATE_ERROR 4 /* Mult - Max number of burtst within an interval, in EP companion desc. */ #define EP_MULT(p) (((p) & 0x3) << 8) #define CTX_TO_EP_MULT(p) (((p) >> 8) & 0x3) /* bits 10:14 are Max Primary Streams */ /* bit 15 is Linear Stream Array */ /* Interval - period between requests to an endpoint - 125u increments. */ -#define EP_INTERVAL(p) (((p) & 0xff) << 16) +#define EP_INTERVAL(p) (((p) & 0xff) << 16) #define EP_INTERVAL_TO_UFRAMES(p) (1 << (((p) >> 16) & 0xff)) -#define CTX_TO_EP_INTERVAL(p) (((p) >> 16) & 0xff) -#define EP_MAXPSTREAMS_MASK (0x1f << 10) -#define EP_MAXPSTREAMS(p) (((p) << 10) & EP_MAXPSTREAMS_MASK) +#define CTX_TO_EP_INTERVAL(p) (((p) >> 16) & 0xff) +#define EP_MAXPSTREAMS_MASK (0x1f << 10) +#define EP_MAXPSTREAMS(p) (((p) << 10) & EP_MAXPSTREAMS_MASK) /* Endpoint is set up with a Linear Stream Array (vs. Secondary Stream Array) */ -#define EP_HAS_LSA BIT(15) +#define EP_HAS_LSA (1 << 15) /* ep_info2 bitmasks */ /* * Force Event - generate transfer events for all TRBs for this endpoint * This will tell the HC to ignore the IOC and ISP flags (for debugging only). */ -#define FORCE_EVENT BIT(0) +#define FORCE_EVENT (0x1) #define ERROR_COUNT(p) (((p) & 0x3) << 1) +#define ERROR_COUNT_SHIFT (1) +#define ERROR_COUNT_MASK (0x3) #define CTX_TO_EP_TYPE(p) (((p) >> 3) & 0x7) #define EP_TYPE(p) ((p) << 3) -#define ISOC_OUT_EP 0x1 -#define BULK_OUT_EP 0x2 -#define INT_OUT_EP 0x3 -#define CTRL_EP 0x4 -#define ISOC_IN_EP 0x5 -#define BULK_IN_EP 0x6 -#define INT_IN_EP 0x7 +#define EP_TYPE_SHIFT (3) +#define ISOC_OUT_EP 1 +#define BULK_OUT_EP 2 +#define INT_OUT_EP 3 +#define CTRL_EP 4 +#define ISOC_IN_EP 5 +#define BULK_IN_EP 6 +#define INT_IN_EP 7 /* bit 6 reserved */ /* bit 7 is Host Initiate Disable - for disabling stream selection */ -#define MAX_BURST(p) (((p) & 0xff) << 8) +#define MAX_BURST(p) (((p)&0xff) << 8) +#define MAX_BURST_MASK (0xff) +#define MAX_BURST_SHIFT (8) #define CTX_TO_MAX_BURST(p) (((p) >> 8) & 0xff) -#define MAX_PACKET(p) (((p) & 0xffff) << 16) -#define MAX_PACKET_MASK (0xffff << 16) +#define MAX_PACKET(p) (((p)&0xffff) << 16) +#define MAX_PACKET_MASK (0xffff) #define MAX_PACKET_DECODED(p) (((p) >> 16) & 0xffff) +#define MAX_PACKET_SHIFT (16) /* Get max packet size from ep desc. Bit 10..0 specify the max packet size. * USB2.0 spec 9.6.6. @@ -686,13 +662,14 @@ struct xhci_ep_ctx { #define GET_MAX_PACKET(p) ((p) & 0x7ff) /* tx_info bitmasks */ -#define AVG_TRB_LENGTH_FOR_EP(p) ((p) & 0xffff) -#define MAX_ESIT_PAYLOAD_FOR_EP(p) (((p) & 0xffff) << 16) +#define EP_AVG_TRB_LENGTH(p) ((p) & 0xffff) +#define EP_MAX_ESIT_PAYLOAD_LO(p) (((p) & 0xffff) << 16) +#define EP_MAX_ESIT_PAYLOAD_HI(p) ((((p) >> 16) & 0xff) << 24) #define CTX_TO_MAX_ESIT_PAYLOAD(p) (((p) >> 16) & 0xffff) /* deq bitmasks */ -#define EP_CTX_CYCLE_MASK BIT(0) -#define SCTX_DEQ_MASK (~0xfL) +#define EP_CTX_CYCLE_MASK (1 << 0) + /** * struct xhci_input_control_context @@ -702,102 +679,35 @@ struct xhci_ep_ctx { * @add_context: set the bit of the endpoint context you want to enable */ struct xhci_input_control_ctx { - __le32 drop_flags; - __le32 add_flags; + volatile __le32 drop_flags; + volatile __le32 add_flags; __le32 rsvd2[6]; }; -#define EP_IS_ADDED(ctrl_ctx, i) \ - (le32_to_cpu(ctrl_ctx->add_flags) & (1 << (i + 1))) -#define EP_IS_DROPPED(ctrl_ctx, i) \ - (le32_to_cpu(ctrl_ctx->drop_flags) & (1 << (i + 1))) - -/* drop context bitmasks */ -#define DROP_EP(x) BIT(x) -/* add context bitmasks */ -#define ADD_EP(x) BIT(x) -struct xhci_stream_ctx { - /* 64-bit stream ring address, cycle state, and stream type */ - __le64 stream_ring; - /* offset 0x14 - 0x1f reserved for HC internal use */ - __le32 reserved[2]; -}; - -/* Stream Context Types (section 6.4.1) - bits 3:1 of stream ctx deq ptr */ -#define SCT_FOR_CTX(p) (((p) & 0x7) << 1) -/* Secondary stream array type, dequeue pointer is to a transfer ring */ -#define SCT_SEC_TR 0x0 -/* Primary stream array type, dequeue pointer is to a transfer ring */ -#define SCT_PRI_TR 0x1 -/* Dequeue pointer is for a secondary stream array (SSA) with 8 entries */ -#define SCT_SSA_8 0x2 -#define SCT_SSA_16 0x3 -#define SCT_SSA_32 0x4 -#define SCT_SSA_64 0x5 -#define SCT_SSA_128 0x6 -#define SCT_SSA_256 0x7 - -#define SMALL_STREAM_ARRAY_SIZE 256 -#define MEDIUM_STREAM_ARRAY_SIZE 1024 - -/* "Block" sizes in bytes the hardware uses for different device speeds. - * The logic in this part of the hardware limits the number of bits the hardware - * can use, so must represent bandwidth in a less precise manner to mimic what - * the scheduler hardware computes. - */ -#define FS_BLOCK 1 -#define HS_BLOCK 4 -#define SS_BLOCK 16 -#define DMI_BLOCK 32 - -/* Each device speed has a protocol overhead (CRC, bit stuffing, etc) associated - * with each byte transferred. SuperSpeed devices have an initial overhead to - * set up bursts. These are in blocks, see above. LS overhead has already been - * translated into FS blocks. - */ -#define DMI_OVERHEAD 8 -#define DMI_OVERHEAD_BURST 4 -#define SS_OVERHEAD 8 -#define SS_OVERHEAD_BURST 32 -#define HS_OVERHEAD 26 -#define FS_OVERHEAD 20 -#define LS_OVERHEAD 128 - -/* The TTs need to claim roughly twice as much bandwidth (94 bytes per - * microframe ~= 24Mbps) of the HS bus as the devices can actually use because - * of overhead associated with split transfers crossing microframe boundaries. - * 31 blocks is pure protocol overhead. +/** + * struct xhci_device_context_array + * @dev_context_ptr array of 64-bit DMA addresses for device contexts */ -#define TT_HS_OVERHEAD (31 + 94) -#define TT_DMI_OVERHEAD (25 + 12) - -/* Bandwidth limits in blocks */ -#define FS_BW_LIMIT 1285 -#define TT_BW_LIMIT 1320 -#define HS_BW_LIMIT 1607 -#define SS_BW_LIMIT_IN 3906 -#define DMI_BW_LIMIT_IN 3906 -#define SS_BW_LIMIT_OUT 3906 -#define DMI_BW_LIMIT_OUT 3906 - -/* Percentage of bus bandwidth reserved for non-periodic transfers */ -#define FS_BW_RESERVED 10 -#define HS_BW_RESERVED 20 -#define SS_BW_RESERVED 10 - -enum xhci_overhead_type { - LS_OVERHEAD_TYPE = 0, - FS_OVERHEAD_TYPE, - HS_OVERHEAD_TYPE, +struct xhci_device_context_array { + /* 64-bit device addresses; we only write 32-bit addresses */ + __le64 dev_context_ptrs[MAX_HC_SLOTS]; + /* private xHCD pointers */ + dma_addr_t dma; }; +/* TODO: write function to set the 64-bit device DMA address */ +/* + * TODO: change this to be dynamically sized at HC mem init time since the HC + * might not be able to handle the maximum number of devices possible. + */ + struct xhci_transfer_event { /* 64-bit buffer address, or immediate data */ __le64 buffer; __le32 transfer_len; /* This field is interpreted differently based on the type of TRB */ - __le32 flags; + volatile __le32 flags; }; /* Transfer event TRB length bit mask */ @@ -805,175 +715,180 @@ struct xhci_transfer_event { #define EVENT_TRB_LEN(p) ((p) & 0xffffff) /** Transfer Event bit fields **/ -#define TRB_TO_EP_ID(p) (((p) >> 16) & 0x1f) +#define TRB_TO_EP_ID(p) (((p) >> 16) & 0x1f) /* Completion Code - only applicable for some types of TRBs */ -#define COMP_CODE_MASK (0xff << 24) +#define COMP_CODE_MASK (0xff << 24) +#define COMP_CODE_SHIFT (24) #define GET_COMP_CODE(p) (((p) & COMP_CODE_MASK) >> 24) -#define COMP_SUCCESS 1 -/* Data Buffer Error */ -#define COMP_DB_ERR 2 -/* Babble Detected Error */ -#define COMP_BABBLE 3 -/* USB Transaction Error */ -#define COMP_TX_ERR 4 -/* TRB Error - some TRB field is invalid */ -#define COMP_TRB_ERR 5 -/* Stall Error - USB device is stalled */ -#define COMP_STALL 6 -/* Resource Error - HC doesn't have memory for that device configuration */ -#define COMP_ENOMEM 7 -/* Bandwidth Error - not enough room in schedule for this dev config */ -#define COMP_BW_ERR 8 -/* No Slots Available Error - HC ran out of device slots */ -#define COMP_ENOSLOTS 9 -/* Invalid Stream Type Error */ -#define COMP_STREAM_ERR 10 -/* Slot Not Enabled Error - doorbell rung for disabled device slot */ -#define COMP_EBADSLT 11 -/* Endpoint Not Enabled Error */ -#define COMP_EBADEP 12 -/* Short Packet */ -#define COMP_SHORT_TX 13 -/* Ring Underrun - doorbell rung for an empty isoc OUT ep ring */ -#define COMP_UNDERRUN 14 -/* Ring Overrun - isoc IN ep ring is empty when ep is scheduled to RX */ -#define COMP_OVERRUN 15 -/* Virtual Function Event Ring Full Error */ -#define COMP_VF_FULL 16 -/* Parameter Error - Context parameter is invalid */ -#define COMP_EINVAL 17 -/* Bandwidth Overrun Error - isoc ep exceeded its allocated bandwidth */ -#define COMP_BW_OVER 18 -/* Context State Error - illegal context state transition requested */ -#define COMP_CTX_STATE 19 -/* No Ping Response Error - HC didn't get PING_RESPONSE in time to TX */ -#define COMP_PING_ERR 20 -/* Event Ring is full */ -#define COMP_ER_FULL 21 -/* Incompatible Device Error */ -#define COMP_DEV_ERR 22 -/* Missed Service Error - HC couldn't service an isoc ep within interval */ -#define COMP_MISSED_INT 23 -/* Successfully stopped command ring */ -#define COMP_CMD_STOP 24 -/* Successfully aborted current command and stopped command ring */ -#define COMP_CMD_ABORT 25 -/* Stopped - transfer was terminated by a stop endpoint command */ -#define COMP_STOP 26 -/* Same as COMP_EP_STOPPED, but the transferred length in the event is invalid */ -#define COMP_STOP_INVAL 27 -/* Control Abort Error - Debug Capability - control pipe aborted */ -#define COMP_DBG_ABORT 28 -/* Max Exit Latency Too Large Error */ -#define COMP_MEL_ERR 29 -/* TRB type 30 reserved */ -/* Isoc Buffer Overrun - an isoc IN ep sent more data than could fit in TD */ -#define COMP_BUFF_OVER 31 -/* Event Lost Error - xHC has an "internal event overrun condition" */ -#define COMP_ISSUES 32 -/* Undefined Error - reported when other error codes don't apply */ -#define COMP_UNKNOWN 33 -/* Invalid Stream ID Error */ -#define COMP_STRID_ERR 34 -/* Secondary Bandwidth Error - may be returned by a Configure Endpoint cmd */ -#define COMP_2ND_BW_ERR 35 -/* Split Transaction Error */ -#define COMP_SPLIT_ERR 36 + +typedef enum { + COMP_SUCCESS = 1, + /* Data Buffer Error */ + COMP_DB_ERR, /* 2 */ + /* Babble Detected Error */ + COMP_BABBLE, /* 3 */ + /* USB Transaction Error */ + COMP_TX_ERR, /* 4 */ + /* TRB Error - some TRB field is invalid */ + COMP_TRB_ERR, /* 5 */ + /* Stall Error - USB device is stalled */ + COMP_STALL, /* 6 */ + /* Resource Error - HC doesn't have memory for that device configuration */ + COMP_ENOMEM, /* 7 */ + /* Bandwidth Error - not enough room in schedule for this dev config */ + COMP_BW_ERR, /* 8 */ + /* No Slots Available Error - HC ran out of device slots */ + COMP_ENOSLOTS, /* 9 */ + /* Invalid Stream Type Error */ + COMP_STREAM_ERR, /* 10 */ + /* Slot Not Enabled Error - doorbell rung for disabled device slot */ + COMP_EBADSLT, /* 11 */ + /* Endpoint Not Enabled Error */ + COMP_EBADEP,/* 12 */ + /* Short Packet */ + COMP_SHORT_TX, /* 13 */ + /* Ring Underrun - doorbell rung for an empty isoc OUT ep ring */ + COMP_UNDERRUN, /* 14 */ + /* Ring Overrun - isoc IN ep ring is empty when ep is scheduled to RX */ + COMP_OVERRUN, /* 15 */ + /* Virtual Function Event Ring Full Error */ + COMP_VF_FULL, /* 16 */ + /* Parameter Error - Context parameter is invalid */ + COMP_EINVAL, /* 17 */ + /* Bandwidth Overrun Error - isoc ep exceeded its allocated bandwidth */ + COMP_BW_OVER,/* 18 */ + /* Context State Error - illegal context state transition requested */ + COMP_CTX_STATE,/* 19 */ + /* No Ping Response Error - HC didn't get PING_RESPONSE in time to TX */ + COMP_PING_ERR,/* 20 */ + /* Event Ring is full */ + COMP_ER_FULL,/* 21 */ + /* Incompatible Device Error */ + COMP_DEV_ERR,/* 22 */ + /* Missed Service Error - HC couldn't service an isoc ep within interval */ + COMP_MISSED_INT,/* 23 */ + /* Successfully stopped command ring */ + COMP_CMD_STOP, /* 24 */ + /* Successfully aborted current command and stopped command ring */ + COMP_CMD_ABORT, /* 25 */ + /* Stopped - transfer was terminated by a stop endpoint command */ + COMP_STOP,/* 26 */ + /* Same as COMP_EP_STOPPED, but the transferred length in the event + * is invalid */ + COMP_STOP_INVAL, /* 27*/ + /* Control Abort Error - Debug Capability - control pipe aborted */ + COMP_DBG_ABORT, /* 28 */ + /* Max Exit Latency Too Large Error */ + COMP_MEL_ERR,/* 29 */ + /* TRB type 30 reserved */ + /* Isoc Buffer Overrun - an isoc IN ep sent more data than could fit in TD */ + COMP_BUFF_OVER = 31, + /* Event Lost Error - xHC has an "internal event overrun condition" */ + COMP_ISSUES, /* 32 */ + /* Undefined Error - reported when other error codes don't apply */ + COMP_UNKNOWN, /* 33 */ + /* Invalid Stream ID Error */ + COMP_STRID_ERR, /* 34 */ + /* Secondary Bandwidth Error - may be returned by a Configure Endpoint cmd */ + COMP_2ND_BW_ERR, /* 35 */ + /* Split Transaction Error */ + COMP_SPLIT_ERR /* 36 */ + +} xhci_comp_code; struct xhci_link_trb { /* 64-bit segment pointer*/ - __le64 segment_ptr; - __le32 intr_target; - __le32 control; + volatile __le64 segment_ptr; + volatile __le32 intr_target; + volatile __le32 control; }; /* control bitfields */ -#define LINK_TOGGLE BIT(1) +#define LINK_TOGGLE (0x1 << 1) /* Command completion event TRB */ struct xhci_event_cmd { /* Pointer to command TRB, or the value passed by the event data trb */ - __le64 cmd_trb; - __le32 status; - __le32 flags; + volatile __le64 cmd_trb; + volatile __le32 status; + volatile __le32 flags; }; /* flags bitmasks */ - -/* Address device - disable SetAddress */ -#define TRB_BSR BIT(9) -enum xhci_setup_dev { - SETUP_CONTEXT_ONLY, - SETUP_CONTEXT_ADDRESS, -}; - /* bits 16:23 are the virtual function ID */ /* bits 24:31 are the slot ID */ -#define TRB_TO_SLOT_ID(p) (((p) & (0xff<<24)) >> 24) -#define SLOT_ID_FOR_TRB(p) (((p) & 0xff) << 24) - -/* Configure Endpoint Command TRB - deconfigure */ -#define TRB_DC BIT(9) +#define TRB_TO_SLOT_ID(p) (((p) & (0xff << 24)) >> 24) +#define TRB_TO_SLOT_ID_SHIFT (24) +#define TRB_TO_SLOT_ID_MASK (0xff << TRB_TO_SLOT_ID_SHIFT) +#define SLOT_ID_FOR_TRB(p) (((p) & 0xff) << 24) +#define SLOT_ID_FOR_TRB_MASK (0xff) +#define SLOT_ID_FOR_TRB_SHIFT (24) /* Stop Endpoint TRB - ep_index to endpoint ID for this TRB */ -#define TRB_TO_EP_INDEX(p) ((((p) & (0x1f << 16)) >> 16) - 1) -#define EP_ID_FOR_TRB(p) ((((p) + 1) & 0x1f) << 16) +#define TRB_TO_EP_INDEX(p) ((((p) & (0x1f << 16)) >> 16) - 1) +#define EP_ID_FOR_TRB(p) ((((p) + 1) & 0x1f) << 16) -#define SUSPEND_PORT_FOR_TRB(p) (((p) & 1) << 23) -#define TRB_TO_SUSPEND_PORT(p) (((p) & (1 << 23)) >> 23) -#define LAST_EP_INDEX 30 +#define SUSPEND_PORT_FOR_TRB(p) (((p) & 1) << 23) +#define TRB_TO_SUSPEND_PORT(p) (((p) & (1 << 23)) >> 23) +#define LAST_EP_INDEX 30 + +/* Set TR Dequeue Pointer command TRB fields */ +#define TRB_TO_STREAM_ID(p) ((((p) & (0xffff << 16)) >> 16)) +#define STREAM_ID_FOR_TRB(p) ((((p)) & 0xffff) << 16) -/* Set TR Dequeue Pointer command TRB fields, 6.4.3.9 */ -#define TRB_TO_STREAM_ID(p) ((((p) & (0xffff << 16)) >> 16)) -#define STREAM_ID_FOR_TRB(p) ((((p)) & 0xffff) << 16) -#define SCT_FOR_TRB(p) (((p) << 1) & 0x7) /* Port Status Change Event TRB fields */ /* Port ID - bits 31:24 */ -#define GET_PORT_ID(p) (((p) & (0xff << 24)) >> 24) +#define GET_PORT_ID(p) (((p) & (0xff << 24)) >> 24) +#define PORT_ID_SHIFT (24) +#define PORT_ID_MASK (0xff << PORT_ID_SHIFT) /* Normal TRB fields */ /* transfer_len bitmasks - bits 0:16 */ -#define TRB_LEN(p) ((p) & 0x1ffff) +#define TRB_LEN(p) ((p) & 0x1ffff) +#define TRB_LEN_MASK (0x1ffff) /* Interrupter Target - which MSI-X vector to target the completion event at */ -#define TRB_INTR_TARGET(p) (((p) & 0x3ff) << 22) -#define GET_INTR_TARGET(p) (((p) >> 22) & 0x3ff) -#define TRB_TBC(p) (((p) & 0x3) << 7) -#define TRB_TLBPC(p) (((p) & 0xf) << 16) +#define TRB_INTR_TARGET_SHIFT (22) +#define TRB_INTR_TARGET_MASK (0x3ff) +#define TRB_INTR_TARGET(p) (((p) & 0x3ff) << 22) +#define GET_INTR_TARGET(p) (((p) >> 22) & 0x3ff) +#define TRB_TBC(p) (((p) & 0x3) << 7) +#define TRB_TLBPC(p) (((p) & 0xf) << 16) /* Cycle bit - indicates TRB ownership by HC or HCD */ -#define TRB_CYCLE BIT(0) +#define TRB_CYCLE (1<<0) /* * Force next event data TRB to be evaluated before task switch. * Used to pass OS data back after a TD completes. */ -#define TRB_ENT BIT(1) +#define TRB_ENT (1<<1) /* Interrupt on short packet */ -#define TRB_ISP BIT(2) +#define TRB_ISP (1<<2) /* Set PCIe no snoop attribute */ -#define TRB_NO_SNOOP BIT(3) +#define TRB_NO_SNOOP (1<<3) /* Chain multiple TRBs into a TD */ -#define TRB_CHAIN BIT(4) +#define TRB_CHAIN (1<<4) /* Interrupt on completion */ -#define TRB_IOC BIT(5) +#define TRB_IOC (1<<5) /* The buffer pointer contains immediate data */ -#define TRB_IDT BIT(6) +#define TRB_IDT (1<<6) /* Block Event Interrupt */ -#define TRB_BEI BIT(9) +#define TRB_BEI (1<<9) /* Control transfer TRB specific fields */ -#define TRB_DIR_IN BIT(16) -#define TRB_TX_TYPE(p) ((p) << 16) -#define TRB_DATA_OUT 2 -#define TRB_DATA_IN 3 +#define TRB_DIR_IN (1<<16) +#define TRB_TX_TYPE(p) ((p) << 16) +#define TRB_TX_TYPE_SHIFT (16) +#define TRB_DATA_OUT 2 +#define TRB_DATA_IN 3 /* Isochronous TRB specific fields */ -#define TRB_SIA BIT(31) +#define TRB_SIA (1 << 31) struct xhci_generic_trb { - __le32 field[4]; + volatile __le32 field[4]; }; union xhci_trb { @@ -984,90 +899,93 @@ union xhci_trb { }; /* TRB bit mask */ -#define TRB_TYPE_BITMASK (0xfc00) +#define TRB_TYPE_BITMASK (0xfc00) #define TRB_TYPE(p) ((p) << 10) +#define TRB_TYPE_SHIFT (10) #define TRB_FIELD_TO_TYPE(p) (((p) & TRB_TYPE_BITMASK) >> 10) + /* TRB type IDs */ -/* bulk, interrupt, isoc scatter/gather, and control data stage */ -#define TRB_NORMAL 1 -/* setup stage for control transfers */ -#define TRB_SETUP 2 -/* data stage for control transfers */ -#define TRB_DATA 3 -/* status stage for control transfers */ -#define TRB_STATUS 4 -/* isoc transfers */ -#define TRB_ISOC 5 -/* TRB for linking ring segments */ -#define TRB_LINK 6 -#define TRB_EVENT_DATA 7 -/* Transfer Ring No-op (not for the command ring) */ -#define TRB_TR_NOOP 8 -/* Command TRBs */ -/* Enable Slot Command */ -#define TRB_ENABLE_SLOT 9 -/* Disable Slot Command */ -#define TRB_DISABLE_SLOT 10 -/* Address Device Command */ -#define TRB_ADDR_DEV 11 -/* Configure Endpoint Command */ -#define TRB_CONFIG_EP 12 -/* Evaluate Context Command */ -#define TRB_EVAL_CONTEXT 13 -/* Reset Endpoint Command */ -#define TRB_RESET_EP 14 -/* Stop Transfer Ring Command */ -#define TRB_STOP_RING 15 -/* Set Transfer Ring Dequeue Pointer Command */ -#define TRB_SET_DEQ 16 -/* Reset Device Command */ -#define TRB_RESET_DEV 17 -/* Force Event Command (opt) */ -#define TRB_FORCE_EVENT 18 -/* Negotiate Bandwidth Command (opt) */ -#define TRB_NEG_BANDWIDTH 19 -/* Set Latency Tolerance Value Command (opt) */ -#define TRB_SET_LT 20 -/* Get port bandwidth Command */ -#define TRB_GET_BW 21 -/* Force Header Command - generate a transaction or link management packet */ -#define TRB_FORCE_HEADER 22 -/* No-op Command - not for transfer rings */ -#define TRB_CMD_NOOP 23 -/* TRB IDs 24-31 reserved */ -/* Event TRBS */ -/* Transfer Event */ -#define TRB_TRANSFER 32 -/* Command Completion Event */ -#define TRB_COMPLETION 33 -/* Port Status Change Event */ -#define TRB_PORT_STATUS 34 -/* Bandwidth Request Event (opt) */ -#define TRB_BANDWIDTH_EVENT 35 -/* Doorbell Event (opt) */ -#define TRB_DOORBELL 36 -/* Host Controller Event */ -#define TRB_HC_EVENT 37 -/* Device Notification Event - device sent function wake notification */ -#define TRB_DEV_NOTE 38 -/* MFINDEX Wrap Event - microframe counter wrapped */ -#define TRB_MFINDEX_WRAP 39 -/* TRB IDs 40-47 reserved, 48-63 is vendor-defined */ - -/* Nec vendor-specific command completion event. */ -#define TRB_NEC_CMD_COMP 48 -/* Get NEC firmware revision. */ -#define TRB_NEC_GET_FW 49 +typedef enum { + /* reserved, used as a software sentinel */ + TRB_NONE = 0, + /* bulk, interrupt, isoc scatter/gather, and control data stage */ + TRB_NORMAL = 1, + /* setup stage for control transfers */ + TRB_SETUP, /* 2 */ + /* data stage for control transfers */ + TRB_DATA, /* 3 */ + /* status stage for control transfers */ + TRB_STATUS, /* 4 */ + /* isoc transfers */ + TRB_ISOC, /* 5 */ + /* TRB for linking ring segments */ + TRB_LINK, /* 6 */ + /* TRB for EVENT DATA */ + TRB_EVENT_DATA, /* 7 */ + /* Transfer Ring No-op (not for the command ring) */ + TRB_TR_NOOP, /* 8 */ + /* Command TRBs */ + /* Enable Slot Command */ + TRB_ENABLE_SLOT, /* 9 */ + /* Disable Slot Command */ + TRB_DISABLE_SLOT, /* 10 */ + /* Address Device Command */ + TRB_ADDR_DEV, /* 11 */ + /* Configure Endpoint Command */ + TRB_CONFIG_EP, /* 12 */ + /* Evaluate Context Command */ + TRB_EVAL_CONTEXT, /* 13 */ + /* Reset Endpoint Command */ + TRB_RESET_EP, /* 14 */ + /* Stop Transfer Ring Command */ + TRB_STOP_RING, /* 15 */ + /* Set Transfer Ring Dequeue Pointer Command */ + TRB_SET_DEQ, /* 16 */ + /* Reset Device Command */ + TRB_RESET_DEV, /* 17 */ + /* Force Event Command (opt) */ + TRB_FORCE_EVENT, /* 18 */ + /* Negotiate Bandwidth Command (opt) */ + TRB_NEG_BANDWIDTH, /* 19 */ + /* Set Latency Tolerance Value Command (opt) */ + TRB_SET_LT, /* 20 */ + /* Get port bandwidth Command */ + TRB_GET_BW, /* 21 */ + /* Force Header Command - generate a transaction or link management packet */ + TRB_FORCE_HEADER, /* 22 */ + /* No-op Command - not for transfer rings */ + TRB_CMD_NOOP, /* 23 */ + /* TRB IDs 24-31 reserved */ + /* Event TRBS */ + /* Transfer Event */ + TRB_TRANSFER = 32, + /* Command Completion Event */ + TRB_COMPLETION, /* 33 */ + /* Port Status Change Event */ + TRB_PORT_STATUS, /* 34 */ + /* Bandwidth Request Event (opt) */ + TRB_BANDWIDTH_EVENT, /* 35 */ + /* Doorbell Event (opt) */ + TRB_DOORBELL, /* 36 */ + /* Host Controller Event */ + TRB_HC_EVENT, /* 37 */ + /* Device Notification Event - device sent function wake notification */ + TRB_DEV_NOTE, /* 38 */ + /* MFINDEX Wrap Event - microframe counter wrapped */ + TRB_MFINDEX_WRAP, /* 39 */ + /* TRB IDs 40-47 reserved, 48-63 is vendor-defined */ + /* Nec vendor-specific command completion event. */ + TRB_NEC_CMD_COMP = 48, /* 48 */ + /* Get NEC firmware revision. */ + TRB_NEC_GET_FW, /* 49 */ +} trb_type; #define TRB_TYPE_LINK(x) (((x) & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK)) /* Above, but for __le32 types -- can avoid work by swapping constants: */ -#define TRB_TYPE_LINK_LE32(x) \ - (((x) & cpu_to_le32(TRB_TYPE_BITMASK)) == cpu_to_le32(TRB_TYPE(TRB_LINK))) -#define TRB_TYPE_NOOP_LE32(x) \ - (((x) & cpu_to_le32(TRB_TYPE_BITMASK)) == cpu_to_le32(TRB_TYPE(TRB_TR_NOOP))) - -#define NEC_FW_MINOR(p) (((p) >> 0) & 0xff) -#define NEC_FW_MAJOR(p) (((p) >> 8) & 0xff) +#define TRB_TYPE_LINK_LE32(x) (((x) & cpu_to_le32(TRB_TYPE_BITMASK)) == \ + cpu_to_le32(TRB_TYPE(TRB_LINK))) +#define TRB_TYPE_NOOP_LE32(x) (((x) & cpu_to_le32(TRB_TYPE_BITMASK)) == \ + cpu_to_le32(TRB_TYPE(TRB_TR_NOOP))) /* * TRBS_PER_SEGMENT must be a multiple of 4, @@ -1077,14 +995,36 @@ union xhci_trb { #define TRBS_PER_SEGMENT 64 /* Allow two commands + a link TRB, along with any reserved command TRBs */ #define MAX_RSVD_CMD_TRBS (TRBS_PER_SEGMENT - 3) -#define TRB_SEGMENT_SIZE (TRBS_PER_SEGMENT * 16) -#define TRB_SEGMENT_SHIFT (ilog2(TRB_SEGMENT_SIZE)) +#define SEGMENT_SIZE (TRBS_PER_SEGMENT*16) +/* SEGMENT_SHIFT should be log2(SEGMENT_SIZE). + * Change this if you change TRBS_PER_SEGMENT! + */ +#define SEGMENT_SHIFT 10 /* TRB buffer pointers can't cross 64KB boundaries */ #define TRB_MAX_BUFF_SHIFT 16 #define TRB_MAX_BUFF_SIZE (1 << TRB_MAX_BUFF_SHIFT) -/* xHCI command default timeout value */ -#define XHCI_CMD_DEFAULT_TIMEOUT (5 * SECOND) +struct xhci_segment { + union xhci_trb *trbs; + /* private to HCD */ + struct xhci_segment *next; + dma_addr_t dma; +}; + +struct xhci_ring { + struct xhci_segment *first_seg; + union xhci_trb *enqueue; + struct xhci_segment *enq_seg; + union xhci_trb *dequeue; + struct xhci_segment *deq_seg; + /* + * Write the cycle state into the TRB cycle field to give ownership of + * the TRB to the host controller (if we are the producer), or to check + * if we own the TRB (if we are the consumer). See section 4.9.1. + */ + volatile u32 cycle_state; + unsigned int num_segs; +}; struct xhci_erst_entry { /* 64-bit event ring segment address */ @@ -1094,38 +1034,137 @@ struct xhci_erst_entry { __le32 rsvd; }; +struct xhci_erst { + struct xhci_erst_entry *entries; + unsigned int num_entries; + /* xhci->event_ring keeps track of segment dma addresses */ + dma_addr_t erst_dma_addr; + /* Num entries the ERST can contain */ + unsigned int erst_size; +}; + +struct xhci_scratchpad { + void *scratchpad; + u64 *sp_array; +}; + /* * Each segment table entry is 4*32bits long. 1K seems like an ok size: * (1K bytes * 8bytes/bit) / (4*32 bits) = 64 segment entries in the table, * meaning 64 ring segments. * Initial allocated size of the ERST, in number of entries */ -#define ERST_NUM_SEGS 1 -/* Initial allocated size of the ERST, in number of entries */ -#define ERST_SIZE 64 +#define ERST_NUM_SEGS 1 /* Initial number of event segment rings allocated */ -#define ERST_ENTRIES 1 +#define ERST_ENTRIES 1 +/* Initial allocated size of the ERST, in number of entries */ +#define ERST_SIZE 64 /* Poll every 60 seconds */ -#define POLL_TIMEOUT 60 +#define POLL_TIMEOUT 60 /* Stop endpoint command timeout (secs) for URB cancellation watchdog timer */ #define XHCI_STOP_EP_CMD_TIMEOUT 5 /* XXX: Make these module parameters */ +struct xhci_virt_ep { + struct xhci_ring *ring; + unsigned int ep_state; +#define SET_DEQ_PENDING (1 << 0) +#define EP_HALTED (1 << 1) /* For stall handling */ +#define EP_HALT_PENDING (1 << 2) /* For URB cancellation */ +/* Transitioning the endpoint to using streams, don't enqueue URBs */ +#define EP_GETTING_STREAMS (1 << 3) +#define EP_HAS_STREAMS (1 << 4) +/* Transitioning the endpoint to not using streams, don't enqueue URBs */ +#define EP_GETTING_NO_STREAMS (1 << 5) +}; + +#define CTX_SIZE(_hcc) (HCC_64BYTE_CONTEXT(_hcc) ? 64 : 32) + +struct xhci_virt_device { + struct usb_device *udev; + /* + * Commands to the hardware are passed an "input context" that + * tells the hardware what to change in its data structures. + * The hardware will return changes in an "output context" that + * software must allocate for the hardware. We need to keep + * track of input and output contexts separately because + * these commands might fail and we don't trust the hardware. + */ + struct xhci_container_ctx *out_ctx; + /* Used for addressing devices and configuration changes */ + struct xhci_container_ctx *in_ctx; + /* Rings saved to ensure old alt settings can be re-instated */ +#define XHCI_MAX_RINGS_CACHED 31 + struct xhci_virt_ep eps[31]; +}; + +/* TODO: copied from ehci.h - can be refactored? */ +/* xHCI spec says all registers are little endian */ +static inline unsigned int xhci_readl(uint32_t volatile *regs) +{ + return readl(regs); +} + +static inline void xhci_writel(uint32_t volatile *regs, const unsigned int val) +{ + writel(val, regs); +} + /* - * It can take up to 20 ms to transition from RExit to U0 on the - * Intel Lynx Point LP xHCI host. + * Registers should always be accessed with double word or quad word accesses. + * Some xHCI implementations may support 64-bit address pointers. Registers + * with 64-bit address pointers should be written to with dword accesses by + * writing the low dword first (ptr[0]), then the high dword (ptr[1]) second. + * xHCI implementations that do not support 64-bit address pointers will ignore + * the high dword, and write order is irrelevant. */ -#define XHCI_MAX_REXIT_TIMEOUT (20 * MSECONDS) +static inline u64 xhci_readq(__le64 volatile *regs) +{ +#if BITS_PER_LONG == 64 + return readq(regs); +#else + return lo_hi_readq(regs); +#endif +} -#define XHCI_MAX_EXT_CAPS 50 +static inline void xhci_writeq(__le64 volatile *regs, const u64 val) +{ +#if BITS_PER_LONG == 64 + writeq(val, regs); +#else + lo_hi_writeq(val, regs); +#endif +} + +int xhci_hcd_init(int index, struct xhci_hccr **ret_hccr, + struct xhci_hcor **ret_hcor); +void xhci_hcd_stop(int index); + + +/************************************************************* + EXTENDED CAPABILITY DEFINITIONS +*************************************************************/ +/* HC not running - set to 1 when run/stop bit is cleared. */ +#define XHCI_STS_HALT (1 << 0) + +/* HCCPARAMS offset from PCI base address */ +#define XHCI_HCC_PARAMS_OFFSET 0x10 +/* HCCPARAMS contains the first extended capability pointer */ +#define XHCI_HCC_EXT_CAPS(p) (((p)>>16)&0xffff) -#define XHCI_EXT_PORT_MAJOR(x) (((x) >> 24) & 0xff) -#define XHCI_EXT_PORT_OFF(x) ((x) & 0xff) -#define XHCI_EXT_PORT_COUNT(x) (((x) >> 8) & 0xff) +/* Command and Status registers offset from the Operational Registers address */ +#define XHCI_CMD_OFFSET 0x00 +#define XHCI_STS_OFFSET 0x04 + +#define XHCI_MAX_EXT_CAPS 50 + +/* Capability Register */ +/* bits 7:0 - how long is the Capabilities register */ +#define XHCI_HC_LENGTH(p) (((p) >> 00) & 0x00ff) /* Extended capability register fields */ -#define XHCI_EXT_CAPS_ID(p) (((p)>>0)&0xff) -#define XHCI_EXT_CAPS_NEXT(p) (((p)>>8)&0xff) -#define XHCI_EXT_CAPS_VAL(p) ((p)>>16) +#define XHCI_EXT_CAPS_ID(p) (((p) >> 0) & 0xff) +#define XHCI_EXT_CAPS_NEXT(p) (((p) >> 8) & 0xff) +#define XHCI_EXT_CAPS_VAL(p) ((p) >> 16) /* Extended capability IDs - ID 0 reserved */ #define XHCI_EXT_CAPS_LEGACY 1 #define XHCI_EXT_CAPS_PROTOCOL 2 @@ -1135,148 +1174,124 @@ struct xhci_erst_entry { /* IDs 6-9 reserved */ #define XHCI_EXT_CAPS_DEBUG 10 /* USB Legacy Support Capability - section 7.1.1 */ -#define XHCI_HC_BIOS_OWNED BIT(16) -#define XHCI_HC_OS_OWNED BIT(24) +#define XHCI_HC_BIOS_OWNED (1 << 16) +#define XHCI_HC_OS_OWNED (1 << 24) /* USB Legacy Support Capability - section 7.1.1 */ /* Add this offset, plus the value of xECP in HCCPARAMS to the base address */ -#define XHCI_LEGACY_SUPPORT_OFFSET 0x00 +#define XHCI_LEGACY_SUPPORT_OFFSET (0x00) /* USB Legacy Support Control and Status Register - section 7.1.2 */ /* Add this offset, plus the value of xECP in HCCPARAMS to the base address */ -#define XHCI_LEGACY_CONTROL_OFFSET 0x04 -/* bits 1:3, 5:12, and 17:19 need to be preserved; bits 21:28 should be zero */ -#define XHCI_LEGACY_DISABLE_SMI ((0x7 << 1) + (0xff << 5) + (0x7 << 17)) -#define XHCI_LEGACY_SMI_EVENTS (0x7 << 29) +#define XHCI_LEGACY_CONTROL_OFFSET (0x04) +/* bits 1:2, 5:12, and 17:19 need to be preserved; bits 21:28 should be zero */ +#define XHCI_LEGACY_DISABLE_SMI ((0x3 << 1) + (0xff << 5) + (0x7 << 17)) /* USB 2.0 xHCI 0.96 L1C capability - section 7.2.2.1.3.2 */ -#define XHCI_L1C BIT(16) +#define XHCI_L1C (1 << 16) /* USB 2.0 xHCI 1.0 hardware LMP capability - section 7.2.2.1.3.2 */ -#define XHCI_HLC BIT(19) -#define XHCI_BLC BIT(20) - -/* - * Registers should always be accessed with double word or quad word accesses. - * - * Some xHCI implementations may support 64-bit address pointers. Registers - * with 64-bit address pointers should be written to with dword accesses by - * writing the low dword first (ptr[0]), then the high dword (ptr[1]) second. - * xHCI implementations that do not support 64-bit address pointers will ignore - * the high dword, and write order is irrelevant. - */ -static inline u64 xhci_read_64(__le64 __iomem *regs) -{ - return lo_hi_readq(regs); -} -static inline void xhci_write_64(const u64 val, __le64 __iomem *regs) -{ - lo_hi_writeq(val, regs); -} +#define XHCI_HLC (1 << 19) -/* - * Barebox xHCI housekeeping structs - */ - -enum xhci_ring_type { - TYPE_CTRL = 0, - TYPE_ISOC, - TYPE_BULK, - TYPE_INTR, - TYPE_STREAM, - TYPE_COMMAND, - TYPE_EVENT, -}; +/* command register values to disable interrupts and halt the HC */ +/* start/stop HC execution - do not write unless HC is halted*/ +#define XHCI_CMD_RUN (1 << 0) +/* Event Interrupt Enable - get irq when EINT bit is set in USBSTS register */ +#define XHCI_CMD_EIE (1 << 2) +/* Host System Error Interrupt Enable - get irq when HSEIE bit set in USBSTS */ +#define XHCI_CMD_HSEIE (1 << 3) +/* Enable Wrap Event - '1' means xHC generates an event when MFINDEX wraps. */ +#define XHCI_CMD_EWE (1 << 10) -struct xhci_ring { - struct list_head list; - union xhci_trb *trbs; - union xhci_trb *enqueue; - union xhci_trb *dequeue; - enum xhci_ring_type type; - int num_trbs; - int cycle_state; -}; +#define XHCI_IRQS (XHCI_CMD_EIE | XHCI_CMD_HSEIE | XHCI_CMD_EWE) -struct xhci_virtual_device { - struct list_head list; - struct usb_device *udev; - void *dma; - size_t dma_size; - int slot_id; - struct xhci_ring *ep[USB_MAXENDPOINTS]; - struct xhci_container_ctx *in_ctx; - struct xhci_container_ctx *out_ctx; -}; - -struct usb_root_hub_info { - 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)); +/* true: Controller Not Ready to accept doorbell or op reg writes after reset */ +#define XHCI_STS_CNR (1 << 11) -struct xhci_hcd { +struct xhci_ctrl { struct usb_host host; - struct device_d *dev; - struct xhci_cap_regs __iomem *cap_regs; - struct xhci_op_regs __iomem *op_regs; - struct xhci_run_regs __iomem *run_regs; - struct xhci_doorbell_array __iomem *dba; - struct xhci_intr_reg __iomem *ir_set; - /* Cached register copies of read-only HC data */ - u32 hcs_params1; - u32 hcs_params2; - u32 hcs_params3; - u32 hcc_capbase; - u32 hcc_params; - u16 hci_version; - int max_slots; - int num_sp; - int page_size; - int page_shift; - size_t dma_size; - __le64 *dcbaa; - void *sp; - __le64 *sp_array; - struct xhci_ring cmd_ring; - struct xhci_ring event_ring; - struct xhci_ring *rings; - struct list_head rings_list; - struct xhci_erst_entry *event_erst; - u8 *port_array; + struct device *dev; + struct xhci_hccr *hccr; /* R/O registers, not need for volatile */ + struct xhci_hcor *hcor; + struct xhci_doorbell_array *dba; + struct xhci_run_regs *run_regs; + struct xhci_device_context_array *dcbaa; + struct xhci_ring *event_ring; + struct xhci_ring *cmd_ring; + struct xhci_ring *transfer_ring; + struct xhci_segment *seg; + struct xhci_intr_reg *ir_set; + struct xhci_erst erst; + struct xhci_erst_entry entry[ERST_NUM_SEGS]; + struct xhci_scratchpad *scratchpad; + struct xhci_virt_device *devs[MAX_HC_SLOTS]; + struct usb_hub_descriptor hub_desc; + void *bounce_buffer; int rootdev; - struct list_head vdev_list; - u32 *ext_caps; - unsigned int num_ext_caps; - __le32 __iomem **usb_ports; - unsigned int num_usb_ports; - struct usb_root_hub_info usb_info; }; -#define to_xhci_hcd(_h) \ - container_of(_h, struct xhci_hcd, host) +static inline struct xhci_ctrl *to_xhci(struct usb_host *host) +{ + return container_of(host, struct xhci_ctrl, host); +} -int xhci_handshake(void __iomem *p, u32 mask, u32 done, int usec); +dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, union xhci_trb *trb); +struct xhci_input_control_ctx + *xhci_get_input_control_ctx(struct xhci_container_ctx *ctx); +struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_ctrl *ctrl, + struct xhci_container_ctx *ctx); +struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_ctrl *ctrl, + struct xhci_container_ctx *ctx, + unsigned int ep_index); +void xhci_endpoint_copy(struct xhci_ctrl *ctrl, + struct xhci_container_ctx *in_ctx, + struct xhci_container_ctx *out_ctx, + unsigned int ep_index); +void xhci_slot_copy(struct xhci_ctrl *ctrl, + struct xhci_container_ctx *in_ctx, + struct xhci_container_ctx *out_ctx); +void xhci_setup_addressable_virt_dev(struct xhci_ctrl *ctrl, + struct usb_device *udev, int hop_portnr); +void xhci_queue_command(struct xhci_ctrl *ctrl, dma_addr_t addr, + u32 slot_id, u32 ep_index, trb_type cmd); +void xhci_acknowledge_event(struct xhci_ctrl *ctrl); +#define XHCI_TIMEOUT_DEFAULT 5000 +union xhci_trb *xhci_wait_for_event(struct xhci_ctrl *ctrl, trb_type expected, + unsigned int timeout_ms); +int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe, + int length, void *buffer, unsigned int timeout_ms); +int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe, + struct devrequest *req, int length, void *buffer, unsigned int timeout_ms); +int xhci_check_maxpacket(struct usb_device *udev); +void xhci_flush_cache(uintptr_t addr, u32 type_len); +void xhci_inval_cache(uintptr_t addr, u32 type_len); +void xhci_cleanup(struct xhci_ctrl *ctrl); +struct xhci_ring *xhci_ring_alloc(struct xhci_ctrl *ctrl, unsigned int num_segs, + bool link_trbs); +int xhci_alloc_virt_device(struct xhci_ctrl *ctrl, unsigned int slot_id); +int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr, + struct xhci_hcor *hcor); -int xhci_issue_command(struct xhci_hcd *xhci, union xhci_trb *trb); -int xhci_wait_for_event(struct xhci_hcd *xhci, u8 type, union xhci_trb *trb); +/** + * xhci_deregister() - Unregister an XHCI controller + * + * @dev: Controller device + * @return 0 if registered, -ve on error + */ +int xhci_deregister(struct xhci_ctrl *ctrl); -int xhci_virtdev_reset(struct xhci_virtual_device *vdev); -int xhci_virtdev_detach(struct xhci_virtual_device *vdev); +/** + * xhci_register() - Register a new XHCI controller + * + * @dev: Controller device + * @hccr: Host controller control registers + * @hcor: Not sure what this means + * @return 0 if registered, -ve on error + */ +int xhci_register(struct xhci_ctrl *ctrl); -int xhci_hub_setup_ports(struct xhci_hcd *xhci); -void xhci_hub_port_power(struct xhci_hcd *xhci, int port, bool enable); -int xhci_hub_control(struct usb_device *dev, unsigned long pipe, - void *buffer, int length, struct devrequest *req); +extern struct dm_usb_ops xhci_usb_ops; -static inline void xhci_print_trb(struct xhci_hcd *xhci, - union xhci_trb *trb, const char *desc) -{ - dev_dbg(xhci->dev, "%s [%08x %08x %08x %08x]\n", desc, - trb->generic.field[0], trb->generic.field[1], - trb->generic.field[2], trb->generic.field[3]); -} +struct xhci_ctrl *xhci_get_ctrl(struct usb_device *udev); -#endif +#endif /* HOST_XHCI_H_ */ diff --git a/drivers/usb/imx/Kconfig b/drivers/usb/imx/Kconfig index 05f17fbd2f..2b9ae607bc 100644 --- a/drivers/usb/imx/Kconfig +++ b/drivers/usb/imx/Kconfig @@ -1,7 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only config USB_IMX_CHIPIDEA bool "i.MX USB support (read help)" - depends on ARCH_IMX + depends on ARCH_IMX || ARCH_MXS + select USB_OTGDEV help The Freescale i.MX SoCs have a variant of the chipidea ci13xxx for USB support. Traditionally in barebox this is supported through the @@ -14,8 +16,12 @@ config USB_IMX_CHIPIDEA support to work. It's safe to say yes here. Also select EHCI support for USB host. +config USB_IMX_CHIPIDEA_USBMISC + bool + default y if ARCH_IMX + config USB_IMX_PHY bool - default y if (ARCH_IMX6 || ARCH_VF610) && GENERIC_PHY + default y if (ARCH_IMX6 || ARCH_VF610 || ARCH_MXS) && GENERIC_PHY select STMP_DEVICE select MFD_SYSCON diff --git a/drivers/usb/imx/Makefile b/drivers/usb/imx/Makefile index e15bc711a9..8a70fceb78 100644 --- a/drivers/usb/imx/Makefile +++ b/drivers/usb/imx/Makefile @@ -1,2 +1,4 @@ -obj-$(CONFIG_USB_IMX_CHIPIDEA) += imx-usb-misc.o chipidea-imx.o +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_USB_IMX_CHIPIDEA) += chipidea-imx.o +obj-$(CONFIG_USB_IMX_CHIPIDEA_USBMISC) += imx-usb-misc.o obj-$(CONFIG_USB_IMX_PHY) += imx-usb-phy.o diff --git a/drivers/usb/imx/chipidea-imx.c b/drivers/usb/imx/chipidea-imx.c index 6c60c383f0..c5e6ce61e9 100644 --- a/drivers/usb/imx/chipidea-imx.c +++ b/drivers/usb/imx/chipidea-imx.c @@ -1,15 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2012 Sascha Hauer <s.hauer@pengutronix.de> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. */ #include <common.h> @@ -18,43 +9,47 @@ #include <of.h> #include <errno.h> #include <driver.h> -#include <usb/usb.h> -#include <usb/ehci.h> +#include <linux/usb/usb.h> +#include <linux/usb/ehci.h> #include <regulator.h> -#include <usb/chipidea-imx.h> -#include <usb/phy.h> -#include <usb/ulpi.h> -#include <usb/fsl_usb2.h> +#include <linux/usb/chipidea-imx.h> +#include <linux/usb/phy.h> +#include <linux/usb/ulpi.h> +#include <linux/usb/fsl_usb2.h> #include <linux/err.h> #include <linux/phy/phy.h> #include <linux/clk.h> #define MXC_EHCI_PORTSC_MASK ((0xf << 28) | (1 << 25)) +struct imx_chipidea_data { + bool have_usb_misc; +}; + struct imx_chipidea { - struct device_d *dev; + struct device *dev; void __iomem *base; struct ehci_data data; unsigned long flags; - uint32_t mode; + enum usb_dr_mode mode; int portno; - struct device_d *usbmisc; + struct device *usbmisc; enum usb_phy_interface phymode; struct param_d *param_mode; - int role_registered; struct regulator *vbus; struct phy *phy; struct usb_phy *usbphy; struct clk *clk; struct ehci_host *ehci; struct fsl_udc *udc; + bool have_usb_misc; }; static int imx_chipidea_port_init(void *drvdata) { struct imx_chipidea *ci = drvdata; uint32_t portsc; - int ret; + int ret = 0; if ((ci->flags & MXC_EHCI_PORTSC_MASK) == MXC_EHCI_MODE_ULPI) { dev_dbg(ci->dev, "using ULPI phy\n"); @@ -73,9 +68,11 @@ static int imx_chipidea_port_init(void *drvdata) return ret; } - ret = imx_usbmisc_port_init(ci->usbmisc, ci->portno, ci->flags); - if (ret) - dev_err(ci->dev, "misc init failed: %s\n", strerror(-ret)); + if (ci->have_usb_misc) { + ret = imx_usbmisc_port_init(ci->usbmisc, ci->portno, ci->flags); + if (ret) + dev_err(ci->dev, "misc init failed: %s\n", strerror(-ret)); + } /* PFSC bit is reset by ehci_reset(), thus have to set it not in * probe but here, after ehci_reset() is already called */ @@ -91,11 +88,13 @@ static int imx_chipidea_port_init(void *drvdata) static int imx_chipidea_port_post_init(void *drvdata) { struct imx_chipidea *ci = drvdata; - int ret; + int ret = 0; - ret = imx_usbmisc_port_post_init(ci->usbmisc, ci->portno, ci->flags); - if (ret) - dev_err(ci->dev, "post misc init failed: %s\n", strerror(-ret)); + if (ci->have_usb_misc) { + ret = imx_usbmisc_port_post_init(ci->usbmisc, ci->portno, ci->flags); + if (ret) + dev_err(ci->dev, "post misc init failed: %s\n", strerror(-ret)); + } return ret; } @@ -103,45 +102,35 @@ static int imx_chipidea_port_post_init(void *drvdata) static int imx_chipidea_probe_dt(struct imx_chipidea *ci) { struct of_phandle_args out_args; - enum usb_dr_mode mode; - if (of_parse_phandle_with_args(ci->dev->device_node, "fsl,usbmisc", - "#index-cells", 0, &out_args)) - return -ENODEV; + if (ci->have_usb_misc) { + if (of_parse_phandle_with_args(ci->dev->of_node, "fsl,usbmisc", + "#index-cells", 0, &out_args)) + return -ENODEV; + + ci->usbmisc = of_find_device_by_node(out_args.np); + if (!ci->usbmisc) + return -ENODEV; - ci->usbmisc = of_find_device_by_node(out_args.np); - if (!ci->usbmisc) - return -ENODEV; + ci->portno = out_args.args[0]; + } - ci->portno = out_args.args[0]; ci->flags = MXC_EHCI_MODE_UTMI_8BIT; - mode = of_usb_get_dr_mode(ci->dev->device_node, NULL); + ci->mode = of_usb_get_dr_mode(ci->dev->of_node, NULL); - switch (mode) { - case USB_DR_MODE_HOST: - default: - ci->mode = IMX_USB_MODE_HOST; - break; - case USB_DR_MODE_PERIPHERAL: - ci->mode = IMX_USB_MODE_DEVICE; - break; - case USB_DR_MODE_OTG: - ci->mode = IMX_USB_MODE_OTG; - break; - case USB_DR_MODE_UNKNOWN: + if (ci->mode == USB_DR_MODE_UNKNOWN) { /* * No dr_mode specified. This means it can either be OTG * for port 0 or host mode for the other host-only ports. */ if (ci->portno == 0) - ci->mode = IMX_USB_MODE_OTG; + ci->mode = USB_DR_MODE_OTG; else - ci->mode = IMX_USB_MODE_HOST; - break; + ci->mode = USB_DR_MODE_HOST; } - ci->phymode = of_usb_get_phy_mode(ci->dev->device_node, NULL); + ci->phymode = of_usb_get_phy_mode(ci->dev->of_node, NULL); switch (ci->phymode) { case USBPHY_INTERFACE_MODE_UTMI: ci->flags = MXC_EHCI_MODE_UTMI_8BIT; @@ -162,40 +151,33 @@ static int imx_chipidea_probe_dt(struct imx_chipidea *ci) dev_dbg(ci->dev, "no phy_type setting. Relying on reset default\n"); } - if (of_find_property(ci->dev->device_node, + if (of_find_property(ci->dev->of_node, "disable-over-current", NULL)) ci->flags |= MXC_EHCI_DISABLE_OVERCURRENT; - else if (!of_find_property(ci->dev->device_node, + else if (!of_find_property(ci->dev->of_node, "over-current-active-high", NULL)) ci->flags |= MXC_EHCI_OC_PIN_ACTIVE_LOW; - if (of_usb_get_maximum_speed(ci->dev->device_node, NULL) == + if (of_find_property(ci->dev->of_node, "power-active-high", NULL)) + ci->flags |= MXC_EHCI_PWR_PIN_ACTIVE_HIGH; + + if (of_usb_get_maximum_speed(ci->dev->of_node, NULL) == USB_SPEED_FULL) ci->flags |= MXC_EHCI_PFSC; return 0; } -static int ci_ehci_detect(struct device_d *dev) -{ - struct imx_chipidea *ci = dev->priv; - - return ehci_detect(ci->ehci); -} - -static int ci_register_role(struct imx_chipidea *ci) +static int ci_set_mode(void *ctx, enum usb_dr_mode mode) { + struct imx_chipidea *ci = ctx; int ret; - if (ci->role_registered != IMX_USB_MODE_OTG) - return -EBUSY; - - if (ci->mode == IMX_USB_MODE_HOST) { + if (mode == USB_DR_MODE_HOST) { if (IS_ENABLED(CONFIG_USB_EHCI)) { struct ehci_host *ehci; - ci->role_registered = IMX_USB_MODE_HOST; ret = regulator_enable(ci->vbus); if (ret) return ret; @@ -207,18 +189,15 @@ static int ci_register_role(struct imx_chipidea *ci) } ci->ehci = ehci; - - ci->dev->detect = ci_ehci_detect; } else { dev_err(ci->dev, "Host support not available\n"); return -ENODEV; } } - if (ci->mode == IMX_USB_MODE_DEVICE) { + if (mode == USB_DR_MODE_PERIPHERAL) { if (IS_ENABLED(CONFIG_USB_GADGET_DRIVER_ARC)) { struct fsl_udc *udc; - ci->role_registered = IMX_USB_MODE_DEVICE; udc = ci_udc_register(ci->dev, ci->base); if (IS_ERR(udc)) @@ -234,52 +213,12 @@ static int ci_register_role(struct imx_chipidea *ci) return 0; } -static int ci_set_mode(struct param_d *param, void *priv) -{ - struct imx_chipidea *ci = priv; - - if (ci->role_registered != IMX_USB_MODE_OTG) { - if (ci->role_registered == ci->mode) - return 0; - else - return -EBUSY; - } - - return ci_register_role(ci); -} - -static const char *ci_mode_names[] = { - "host", "peripheral", "otg" -}; - -static struct device_d imx_otg_device = { - .name = "otg", - .id = DEVICE_ID_SINGLE, -}; - -static int ci_register_otg_device(struct imx_chipidea *ci) -{ - int ret; - - if (imx_otg_device.parent) - return -EBUSY; - - imx_otg_device.parent = ci->dev; - - ret = register_device(&imx_otg_device); - if (ret) - return ret; - - ci->param_mode = dev_add_param_enum(&imx_otg_device, "mode", - ci_set_mode, NULL, &ci->mode, - ci_mode_names, ARRAY_SIZE(ci_mode_names), ci); - return 0; -} - -static int imx_chipidea_probe(struct device_d *dev) +static int imx_chipidea_probe(struct device *dev) { struct resource *iores; + struct imx_chipidea_data *imx_data; struct imxusb_platformdata *pdata = dev->platform_data; + char const *phynode_name; int ret; void __iomem *base; struct imx_chipidea *ci; @@ -288,9 +227,12 @@ static int imx_chipidea_probe(struct device_d *dev) ci = xzalloc(sizeof(*ci)); ci->dev = dev; dev->priv = ci; - ci->role_registered = IMX_USB_MODE_OTG; - if (IS_ENABLED(CONFIG_OFDEVICE) && dev->device_node) { + ret = dev_get_drvdata(dev, (const void **)&imx_data); + if (!ret) + ci->have_usb_misc = imx_data->have_usb_misc; + + if (IS_ENABLED(CONFIG_OFDEVICE) && dev->of_node) { ret = imx_chipidea_probe_dt(ci); if (ret) return ret; @@ -306,8 +248,11 @@ static int imx_chipidea_probe(struct device_d *dev) } ci->vbus = regulator_get(dev, "vbus"); - if (IS_ERR(ci->vbus)) + if (IS_ERR(ci->vbus)) { + dev_warn(dev, "Cannot get vbus regulator: %pe (ignoring)\n", + ci->vbus); ci->vbus = NULL; + } /* * Some devices have more than one clock, in this case they are enabled @@ -315,15 +260,23 @@ static int imx_chipidea_probe(struct device_d *dev) * devices which have only one. */ ci->clk = clk_get(dev, NULL); - if (!IS_ERR(ci->clk)) - clk_enable(ci->clk); - if (of_property_read_bool(dev->device_node, "fsl,usbphy")) { - ci->phy = of_phy_get_by_phandle(dev, "fsl,usbphy", 0); + /* Device trees are using both "phys" and "fsl,usbphy". Prefer the + * more modern former one but fall back to the old one. + * + * Code should be removed when all devicetrees are using "phys" */ + if (of_property_read_bool(dev->of_node, "phys")) + phynode_name = "phys"; + else if (of_property_read_bool(dev->of_node, "fsl,usbphy")) + phynode_name = "fsl,usbphy"; + else + phynode_name = NULL; + + if (phynode_name) { + ci->phy = of_phy_get_by_phandle(dev, phynode_name, 0); if (IS_ERR(ci->phy)) { - ret = PTR_ERR(ci->phy); - dev_err(dev, "Cannot get phy: %s\n", strerror(-ret)); - return ret; + dev_err(dev, "Cannot get phy: %pe\n", ci->phy); + return PTR_ERR(ci->phy); } else { ci->usbphy = phy_to_usbphy(ci->phy); if (IS_ERR(ci->usbphy)) @@ -347,6 +300,14 @@ static int imx_chipidea_probe(struct device_d *dev) ci->data.drvdata = ci; ci->data.usbphy = ci->usbphy; + /* + * Enable the clock after we ensured that all resources are available. + * This is crucial since the phy can be missing which and so the + * usb-controller <-> usb-phy communication is only partly initialized. + * This can trigger strange system hangs at least on i.MX8M SoCs. + */ + clk_enable(ci->clk); + if ((ci->flags & MXC_EHCI_PORTSC_MASK) == MXC_EHCI_MODE_HSIC) imx_chipidea_port_init(ci); @@ -361,15 +322,15 @@ static int imx_chipidea_probe(struct device_d *dev) ci->data.hcor = base + 0x140; ci->data.flags = EHCI_HAS_TT; - if (ci->mode == IMX_USB_MODE_OTG) - ret = ci_register_otg_device(ci); + if (ci->mode == USB_DR_MODE_OTG) + ret = usb_register_otg_device(ci->dev, ci_set_mode, ci); else - ret = ci_register_role(ci); + ret = ci_set_mode(ci, ci->mode); return ret; }; -static void imx_chipidea_remove(struct device_d *dev) +static void imx_chipidea_remove(struct device *dev) { struct imx_chipidea *ci = dev->priv; @@ -380,15 +341,31 @@ static void imx_chipidea_remove(struct device_d *dev) ci_udc_unregister(ci->udc); } +static const struct imx_chipidea_data imx_data = { + .have_usb_misc = 1, +}; + +static const struct imx_chipidea_data imx28_data = { + .have_usb_misc = 0, +}; + static __maybe_unused struct of_device_id imx_chipidea_dt_ids[] = { { .compatible = "fsl,imx27-usb", + .data = &imx_data, + }, { + .compatible = "fsl,imx28-usb", + .data = &imx28_data, + }, { + .compatible = "fsl,imx7d-usb", + .data = &imx_data, }, { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, imx_chipidea_dt_ids); -static struct driver_d imx_chipidea_driver = { +static struct driver imx_chipidea_driver = { .name = "imx-usb", .probe = imx_chipidea_probe, .of_compatible = DRV_OF_COMPAT(imx_chipidea_dt_ids), diff --git a/drivers/usb/imx/imx-usb-misc.c b/drivers/usb/imx/imx-usb-misc.c index ef3892c220..bf9583e626 100644 --- a/drivers/usb/imx/imx-usb-misc.c +++ b/drivers/usb/imx/imx-usb-misc.c @@ -1,16 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> * Copyright (C) 2010 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. */ #include <common.h> @@ -18,9 +9,9 @@ #include <io.h> #include <of.h> #include <errno.h> -#include <usb/chipidea-imx.h> -#include <mach/imx6-regs.h> -#include <mach/iomux-mx6.h> +#include <linux/usb/chipidea-imx.h> +#include <mach/imx/imx6-regs.h> +#include <mach/imx/iomux-mx6.h> #define MX25_OTG_SIC_SHIFT 29 #define MX25_OTG_SIC_MASK (0x3 << MX25_OTG_SIC_SHIFT) @@ -353,7 +344,8 @@ static __maybe_unused struct imx_usb_misc_data mx5_data = { #define MX6_USB_CTRL(n) ((n) * 4) #define MX6_USB_CTRL_OVER_CUR_DIS (1 << 7) -#define MX6_USB_CTRL_OVER_CUR_ACT_HIGH (1 << 8) +#define MX6_USB_CTRL_OVER_CUR_ACT_LOW (1 << 8) +#define MX6_USB_CTRL_PWR_POLARITY (1 << 9) static void mx6_hsic_pullup(unsigned long reg, int on) { @@ -378,8 +370,17 @@ static __maybe_unused int mx6_initialize_usb_hw(void __iomem *base, int port, case 0: case 1: val = readl(base + MX6_USB_CTRL(port)); - if (flags & MXC_EHCI_DISABLE_OVERCURRENT) + if (flags & MXC_EHCI_DISABLE_OVERCURRENT) { val |= MX6_USB_CTRL_OVER_CUR_DIS; + } else { + val &= ~MX6_USB_CTRL_OVER_CUR_DIS; + if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW) + val |= MX6_USB_CTRL_OVER_CUR_ACT_LOW; + else + val &= ~MX6_USB_CTRL_OVER_CUR_ACT_LOW; + } + if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) + val |= MX6_USB_CTRL_PWR_POLARITY; writel(val, base + MX6_USB_CTRL(port)); break; case 2: /* HSIC port */ @@ -450,10 +451,12 @@ static int usbmisc_imx7d_init(void __iomem *base, int port, } else { reg &= ~MX6_USB_CTRL_OVER_CUR_DIS; if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW) - reg &= ~MX6_USB_CTRL_OVER_CUR_ACT_HIGH; + reg |= MX6_USB_CTRL_OVER_CUR_ACT_LOW; else - reg |= MX6_USB_CTRL_OVER_CUR_ACT_HIGH; + reg &= ~MX6_USB_CTRL_OVER_CUR_ACT_LOW; } + if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) + reg |= MX6_USB_CTRL_PWR_POLARITY; writel(reg, base); reg = readl(base + MX7D_USBNC_USB_CTRL2); @@ -599,6 +602,16 @@ static __maybe_unused struct of_device_id imx_usbmisc_dt_ids[] = { .data = &mx7_data, }, #endif +#if defined CONFIG_ARCH_IMX8M || defined CONFIG_ARCH_IMX93 + { + .compatible = "fsl,imx8mm-usbmisc", + .data = &mx7_data, + }, + { + .compatible = "fsl,imx8mn-usbmisc", + .data = &mx7_data, + }, +#endif #ifdef CONFIG_ARCH_VF610 { .compatible = "fsl,vf610-usbmisc", @@ -609,8 +622,9 @@ static __maybe_unused struct of_device_id imx_usbmisc_dt_ids[] = { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, imx_usbmisc_dt_ids); -int imx_usbmisc_port_init(struct device_d *dev, int port, unsigned flags) +int imx_usbmisc_port_init(struct device *dev, int port, unsigned flags) { struct imx_usb_misc_priv *usbmisc = dev->priv; @@ -623,7 +637,7 @@ int imx_usbmisc_port_init(struct device_d *dev, int port, unsigned flags) return usbmisc->data->init(usbmisc->base, port, flags); } -int imx_usbmisc_port_post_init(struct device_d *dev, int port, unsigned flags) +int imx_usbmisc_port_post_init(struct device *dev, int port, unsigned flags) { struct imx_usb_misc_priv *usbmisc = dev->priv; @@ -636,7 +650,7 @@ int imx_usbmisc_port_post_init(struct device_d *dev, int port, unsigned flags) return usbmisc->data->post_init(usbmisc->base, port, flags); } -static int imx_usbmisc_probe(struct device_d *dev) +static int imx_usbmisc_probe(struct device *dev) { struct resource *iores; struct imx_usb_misc_data *devtype; @@ -660,17 +674,11 @@ static int imx_usbmisc_probe(struct device_d *dev) return 0; } -static struct driver_d imx_usbmisc_driver = { +static struct driver imx_usbmisc_driver = { .name = "imx-usbmisc", .probe = imx_usbmisc_probe, .id_table = imx_usbmisc_ids, .of_compatible = DRV_OF_COMPAT(imx_usbmisc_dt_ids), }; -static int imx_usbmisc_init(void) -{ - platform_driver_register(&imx_usbmisc_driver); - return 0; -} - -coredevice_initcall(imx_usbmisc_init); +coredevice_platform_driver(imx_usbmisc_driver); diff --git a/drivers/usb/imx/imx-usb-phy.c b/drivers/usb/imx/imx-usb-phy.c index d4562285c0..70bf292f80 100644 --- a/drivers/usb/imx/imx-usb-phy.c +++ b/drivers/usb/imx/imx-usb-phy.c @@ -1,15 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2013 Sascha Hauer <s.hauer@pengutronix.de> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. */ #include <common.h> @@ -19,7 +10,7 @@ #include <errno.h> #include <driver.h> #include <malloc.h> -#include <usb/phy.h> +#include <linux/usb/phy.h> #include <linux/phy/phy.h> #include <linux/clk.h> #include <linux/err.h> @@ -37,9 +28,12 @@ #define BM_USBPHY_CTRL_ENHOSTDISCONDETECT BIT(1) #define ANADIG_USB1_CHRG_DETECT_SET 0x1b4 -#define ANADIG_USB2_CHRG_DETECT_SET 0x214 #define ANADIG_USB1_CHRG_DETECT_EN_B BIT(20) #define ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B BIT(19) +#define ANADIG_USB1_VBUS_DETECT_STAT 0x1c0 +#define ANADIG_USB1_VBUS_DETECT_STAT_VBUS_VALID BIT(3) +#define ANADIG_USB2_CHRG_DETECT_SET 0x214 +#define ANADIG_USB2_VBUS_DETECT_STAT 0x220 struct imx_usbphy { struct usb_phy usb_phy; @@ -49,6 +43,8 @@ struct imx_usbphy { struct clk *clk; struct phy_provider *provider; int port_id; + + unsigned int vbus_valid; }; static int imx_usbphy_phy_init(struct phy *phy) @@ -57,6 +53,7 @@ static int imx_usbphy_phy_init(struct phy *phy) int ret; clk_enable(imxphy->clk); + mdelay(1); ret = stmp_reset_block(imxphy->base + HW_USBPHY_CTRL, false); if (ret) @@ -111,7 +108,7 @@ static int imx_usbphy_notify_disconnect(struct usb_phy *phy, return 0; } -static struct phy *imx_usbphy_xlate(struct device_d *dev, +static struct phy *imx_usbphy_xlate(struct device *dev, struct of_phandle_args *args) { struct imx_usbphy *imxphy = dev->priv; @@ -131,10 +128,25 @@ static const struct phy_ops imx_phy_ops = { .to_usbphy = imx_usbphy_to_usbphy, }; -static int imx_usbphy_probe(struct device_d *dev) +static int imx_usbphy_get_vbus_state(struct param_d *p, void *priv) +{ + struct imx_usbphy *imxphy = priv; + unsigned int reg, val; + + reg = imxphy->port_id ? + ANADIG_USB1_VBUS_DETECT_STAT : + ANADIG_USB2_VBUS_DETECT_STAT; + val = readl(imxphy->anatop + reg); + + imxphy->vbus_valid = !!(val & ANADIG_USB1_VBUS_DETECT_STAT_VBUS_VALID); + + return 0; +} + +static int imx_usbphy_probe(struct device *dev) { struct resource *iores; - struct device_node *np = dev->device_node; + struct device_node *np = dev->of_node; int ret; struct imx_usbphy *imxphy; @@ -153,6 +165,15 @@ static int imx_usbphy_probe(struct device_d *dev) ret = PTR_ERR_OR_ZERO(imxphy->anatop); if (ret) goto err_free; + + /* + * This is useful in case of usb-otg = device. In host case + * it isn't that useful since we are the supplier of the vbus + * signal. + */ + dev_add_param_bool(dev, "vbus_valid", param_set_readonly, + imx_usbphy_get_vbus_state, + &imxphy->vbus_valid, imxphy); } iores = dev_request_mem_resource(dev, 0); @@ -164,7 +185,7 @@ static int imx_usbphy_probe(struct device_d *dev) imxphy->clk = clk_get(dev, NULL); if (IS_ERR(imxphy->clk)) { - dev_err(dev, "could not get clk: %s\n", strerrorp(imxphy->clk)); + dev_err(dev, "could not get clk: %pe\n", imxphy->clk); ret = PTR_ERR(imxphy->clk); goto err_clk; } @@ -174,7 +195,7 @@ static int imx_usbphy_probe(struct device_d *dev) imxphy->usb_phy.dev = dev; imxphy->usb_phy.notify_connect = imx_usbphy_notify_connect; imxphy->usb_phy.notify_disconnect = imx_usbphy_notify_disconnect; - imxphy->phy = phy_create(dev, NULL, &imx_phy_ops, NULL); + imxphy->phy = phy_create(dev, NULL, &imx_phy_ops); if (IS_ERR(imxphy->phy)) { ret = PTR_ERR(imxphy->phy); goto err_clk; @@ -208,15 +229,12 @@ static __maybe_unused struct of_device_id imx_usbphy_dt_ids[] = { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, imx_usbphy_dt_ids); -static struct driver_d imx_usbphy_driver = { +static struct driver imx_usbphy_driver = { .name = "imx-usb-phy", .probe = imx_usbphy_probe, .of_compatible = DRV_OF_COMPAT(imx_usbphy_dt_ids), }; -static int imx_usbphy_init(void) -{ - return platform_driver_register(&imx_usbphy_driver); -} -fs_initcall(imx_usbphy_init); +fs_platform_driver(imx_usbphy_driver); diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 7d6c9da594..fde57fd743 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only # # USB Miscellaneous driver configuration # @@ -6,9 +7,20 @@ comment "USB Miscellaneous drivers" config USB_HUB_USB251XB bool "USB251XB Hub Controller Configuration Driver" depends on I2C + depends on OFDEVICE select NLS help This option enables support for configuration via SMBus of the Microchip USB251x/xBi USB 2.0 Hub Controller series. Configuration parameters may be set in devicetree or platform data. Say Y or M here if you need to configure such a device via SMBus. + +config USB_ONBOARD_HUB + bool "Onboard USB hub support" + depends on OFDEVICE || COMPILE_TEST + help + Say Y here if you want to support discrete onboard USB hubs that + don't require an additional control bus for initialization, but + need some non-trivial form of initialization, such as enabling a + power regulator. An example for such a hub is the Realtek + RTS5411. diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index fb69c454bd..e00f66a5ed 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -1,6 +1,7 @@ -# SPDX-License-Identifier: GPL-2.0 +# SPDX-License-Identifier: GPL-2.0-only # # Makefile for the rest of the USB drivers # (the ones that don't fit into any other categories) # obj-$(CONFIG_USB_HUB_USB251XB) += usb251xb.o +obj-$(CONFIG_USB_ONBOARD_HUB) += onboard_usb_hub.o diff --git a/drivers/usb/misc/onboard_usb_hub.c b/drivers/usb/misc/onboard_usb_hub.c new file mode 100644 index 0000000000..9e94caaa84 --- /dev/null +++ b/drivers/usb/misc/onboard_usb_hub.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for onboard USB hubs + * + * Copyright (c) 2022, Google LLC + */ + +#include <driver.h> +#include <linux/gpio/consumer.h> +#include <init.h> +#include <of.h> +#include <linux/printk.h> +#include <of_device.h> +#include <regulator.h> +#include <linux/usb/usb.h> + +#include "onboard_usb_hub.h" + +void of_usb_host_probe_hubs(struct usb_host *host) +{ + struct device_node *np; + + np = dev_of_node(host->hw_dev); + if (!np) + return; + + of_platform_populate(np, onboard_hub_match, host->hw_dev); +} + +struct onboard_hub { + struct regulator *vdd; + struct device *dev; + const struct onboard_hub_pdata *pdata; + struct gpio_desc *reset_gpio; +}; + +static int onboard_hub_power_on(struct onboard_hub *hub) +{ + int err; + + err = regulator_enable(hub->vdd); + if (err) { + dev_err(hub->dev, "failed to enable regulator: %pe\n", + ERR_PTR(err)); + return err; + } + + udelay(hub->pdata->reset_us); + gpiod_set_value(hub->reset_gpio, 0); + + return 0; +} + +static int onboard_hub_probe(struct device *dev) +{ + struct device_node *peer_node; + struct device *peer_dev; + struct onboard_hub *hub; + + peer_node = of_parse_phandle(dev->of_node, "peer-hub", 0); + if (peer_node) { + peer_dev = of_find_device_by_node(peer_node); + if (peer_dev && peer_dev->priv) + return 0; + } + + hub = xzalloc(sizeof(*hub)); + + hub->pdata = device_get_match_data(dev); + if (!hub->pdata) + return -EINVAL; + + hub->vdd = regulator_get(dev, "vdd"); + if (IS_ERR(hub->vdd)) + return PTR_ERR(hub->vdd); + + hub->reset_gpio = gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(hub->reset_gpio)) + return dev_errp_probe(dev, hub->reset_gpio, + "failed to get reset GPIO\n"); + + hub->dev = dev; + dev->priv = hub; + + return onboard_hub_power_on(hub); +} + +static struct driver onboard_hub_driver = { + .name = "onboard-usb-hub", + .probe = onboard_hub_probe, + .of_compatible = onboard_hub_match, +}; +device_platform_driver(onboard_hub_driver); + +MODULE_AUTHOR("Matthias Kaehlcke <mka@chromium.org>"); +MODULE_DESCRIPTION("Driver for discrete onboard USB hubs"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/misc/onboard_usb_hub.h b/drivers/usb/misc/onboard_usb_hub.h new file mode 100644 index 0000000000..e379ca811a --- /dev/null +++ b/drivers/usb/misc/onboard_usb_hub.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2022, Google LLC + */ + +#ifndef _USB_MISC_ONBOARD_USB_HUB_H +#define _USB_MISC_ONBOARD_USB_HUB_H + +struct onboard_hub_pdata { + unsigned long reset_us; /* reset pulse width in us */ +}; + +static const struct onboard_hub_pdata microchip_usb424_data = { + .reset_us = 1, +}; + +static const struct onboard_hub_pdata realtek_rts5411_data = { + .reset_us = 0, +}; + +static const struct onboard_hub_pdata ti_tusb8041_data = { + .reset_us = 3000, +}; + +static const struct onboard_hub_pdata genesys_gl850g_data = { + .reset_us = 3, +}; + +static const struct onboard_hub_pdata genesys_gl852g_data = { + .reset_us = 50, +}; + +static const struct onboard_hub_pdata vialab_vl817_data = { + .reset_us = 10, +}; + +static const struct of_device_id onboard_hub_match[] = { + { .compatible = "usb424,2514", .data = µchip_usb424_data, }, + { .compatible = "usb424,2517", .data = µchip_usb424_data, }, + { .compatible = "usb451,8140", .data = &ti_tusb8041_data, }, + { .compatible = "usb451,8142", .data = &ti_tusb8041_data, }, + { .compatible = "usb5e3,608", .data = &genesys_gl850g_data, }, + { .compatible = "usb5e3,610", .data = &genesys_gl852g_data, }, + { .compatible = "usb5e3,620", .data = &genesys_gl852g_data, }, + { .compatible = "usbbda,411", .data = &realtek_rts5411_data, }, + { .compatible = "usbbda,5411", .data = &realtek_rts5411_data, }, + { .compatible = "usbbda,414", .data = &realtek_rts5411_data, }, + { .compatible = "usbbda,5414", .data = &realtek_rts5411_data, }, + { .compatible = "usb2109,817", .data = &vialab_vl817_data, }, + { .compatible = "usb2109,2817", .data = &vialab_vl817_data, }, + {} +}; +MODULE_DEVICE_TABLE(of, onboard_hub_match); + +#endif /* _USB_MISC_ONBOARD_USB_HUB_H */ diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c index 10d5aa310b..465d97e779 100644 --- a/drivers/usb/misc/usb251xb.c +++ b/drivers/usb/misc/usb251xb.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Driver for Microchip USB251xB USB 2.0 Hi-Speed Hub Controller * Configuration via SMBus. @@ -116,7 +116,7 @@ #define DRIVER_DESC "Microchip USB 2.0 Hi-Speed Hub Controller" struct usb251xb { - struct device_d *dev; + struct device *dev; struct i2c_client *i2c; u8 skip_config; int gpio_reset; @@ -241,7 +241,7 @@ static void usb251xb_reset(struct usb251xb *hub, int state) static int usb251xb_connect(struct usb251xb *hub) { - struct device_d *dev = hub->dev; + struct device *dev = hub->dev; int err, i; char i2c_wb[USB251XB_I2C_REG_SZ]; @@ -337,8 +337,8 @@ out_err: static int usb251xb_get_ofdata(struct usb251xb *hub, struct usb251xb_data *data) { - struct device_d *dev = hub->dev; - struct device_node *np = dev->device_node; + struct device *dev = hub->dev; + struct device_node *np = dev->of_node; int len, i; u32 port, property_u32 = 0; const u32 *cproperty_u32; @@ -560,7 +560,7 @@ static int usb251xb_get_ofdata(struct usb251xb *hub, */ hub->port_swap = USB251XB_DEF_PORT_SWAP; of_property_for_each_u32(np, "swap-dx-lanes", prop, p, port) { - if ((port >= 0) && (port <= data->port_cnt)) + if (port <= data->port_cnt) hub->port_swap |= BIT(port); } @@ -608,6 +608,7 @@ static const struct of_device_id usb251xb_of_match[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, usb251xb_of_match); #else /* CONFIG_OFDEVICE */ static int usb251xb_get_ofdata(struct usb251xb *hub, struct usb251xb_data *data) @@ -618,8 +619,8 @@ static int usb251xb_get_ofdata(struct usb251xb *hub, static int usb251xb_probe(struct usb251xb *hub) { - struct device_d *dev = hub->dev; - struct device_node *np = dev->device_node; + struct device *dev = hub->dev; + struct device_node *np = dev->of_node; const struct of_device_id *of_id = of_match_device(usb251xb_of_match, dev); int err; @@ -644,7 +645,7 @@ static int usb251xb_probe(struct usb251xb *hub) return 0; } -static int usb251xb_i2c_probe(struct device_d *dev) +static int usb251xb_i2c_probe(struct device *dev) { struct i2c_client *i2c = to_i2c_client(dev); struct usb251xb *hub; @@ -670,7 +671,7 @@ static const struct platform_device_id usb251xb_id[] = { { /* sentinel */ } }; -static struct driver_d usb251xb_i2c_driver = { +static struct driver usb251xb_i2c_driver = { .name = DRIVER_NAME, .probe = usb251xb_i2c_probe, .id_table = usb251xb_id, diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index b795f30275..f99e514790 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config USB_MUSB bool "MUSB support" @@ -6,6 +7,7 @@ if USB_MUSB config USB_MUSB_DSPS tristate select OFDEVICE + select USB_OTGDEV config USB_MUSB_AM335X tristate "AM335x USB support" diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile index 9f9c210300..f60c97004b 100644 --- a/drivers/usb/musb/Makefile +++ b/drivers/usb/musb/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only # # for USB OTG silicon based on Mentor Graphics INVENTRA designs # diff --git a/drivers/usb/musb/am35x-phy-control.h b/drivers/usb/musb/am35x-phy-control.h index c492d421dc..b4eb585e71 100644 --- a/drivers/usb/musb/am35x-phy-control.h +++ b/drivers/usb/musb/am35x-phy-control.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ #ifndef _AM335x_PHY_CONTROL_H_ #define _AM335x_PHY_CONTROL_H_ @@ -16,6 +17,6 @@ static inline void phy_ctrl_wkup(struct phy_control *phy_ctrl, u32 id, bool on) phy_ctrl->phy_wkup(phy_ctrl, id, on); } -struct phy_control *am335x_get_phy_control(struct device_d *dev); +struct phy_control *am335x_get_phy_control(struct device *dev); #endif diff --git a/drivers/usb/musb/musb_am335x.c b/drivers/usb/musb/musb_am335x.c index 2a9167a245..19d780d15b 100644 --- a/drivers/usb/musb/musb_am335x.c +++ b/drivers/usb/musb/musb_am335x.c @@ -1,12 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> #include <init.h> #include <linux/clk.h> -static int am335x_child_probe(struct device_d *dev) +static int am335x_child_probe(struct device *dev) { int ret; - ret = of_platform_populate(dev->device_node, NULL, dev); + ret = of_platform_populate(dev->of_node, NULL, dev); if (ret) return ret; @@ -20,8 +21,9 @@ static __maybe_unused struct of_device_id am335x_child_dt_ids[] = { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, am335x_child_dt_ids); -static struct driver_d am335x_child_driver = { +static struct driver am335x_child_driver = { .name = "am335x_child_probe", .probe = am335x_child_probe, .of_compatible = DRV_OF_COMPAT(am335x_child_dt_ids), diff --git a/drivers/usb/musb/musb_barebox.c b/drivers/usb/musb/musb_barebox.c index b1f38c35ac..81fdd6338f 100644 --- a/drivers/usb/musb/musb_barebox.c +++ b/drivers/usb/musb/musb_barebox.c @@ -1,8 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> #include <init.h> #include <clock.h> -#include <usb/musb.h> -#include <usb/usb.h> +#include <linux/usb/musb.h> +#include <linux/usb/usb.h> #include <linux/types.h> #include <linux/err.h> #include <linux/barebox-wrapper.h> @@ -121,13 +122,6 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, return submit_urb(dev, urb, 100); } -static int musb_detect(struct device_d *dev) -{ - struct musb *musb = dev->priv; - - return usb_host_detect(&musb->host); -} - int musb_register(struct musb *musb) { struct usb_host *host; @@ -139,8 +133,6 @@ int musb_register(struct musb *musb) host->submit_control_msg = submit_control_msg; host->submit_bulk_msg = submit_bulk_msg; - musb->controller->priv = musb; - musb->controller->detect = musb_detect; usb_register_host(host); return 0; diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 4c11e6580c..9c6c4e7bb4 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1,35 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * MUSB OTG driver core code * * Copyright 2005 Mentor Graphics Corporation * Copyright (C) 2005-2006 by Texas Instruments * Copyright (C) 2006-2007 Nokia Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * */ /* @@ -92,8 +67,8 @@ #include <common.h> #include <init.h> #include <clock.h> -#include <usb/musb.h> -#include <usb/usb.h> +#include <linux/usb/musb.h> +#include <linux/usb/usb.h> #include <linux/types.h> #include <linux/err.h> #include <linux/barebox-wrapper.h> @@ -215,8 +190,6 @@ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src) if (unlikely(len == 0)) return; - prefetch((u8 *)src); - dev_dbg(musb->controller, "%cX ep%d fifo %p count %d buf %p\n", 'T', hw_ep->epnum, fifo, len, src); @@ -332,7 +305,6 @@ void musb_load_testpacket(struct musb *musb) static void musb_generic_disable(struct musb *musb) { void __iomem *mbase = musb->mregs; - u16 temp; /* disable interrupts */ musb_writeb(mbase, MUSB_INTRUSBE, 0); @@ -345,9 +317,9 @@ static void musb_generic_disable(struct musb *musb) musb_writeb(mbase, MUSB_DEVCTL, 0); /* flush pending interrupts */ - temp = musb_readb(mbase, MUSB_INTRUSB); - temp = musb_readw(mbase, MUSB_INTRTX); - temp = musb_readw(mbase, MUSB_INTRRX); + (void)musb_readb(mbase, MUSB_INTRUSB); + (void)musb_readw(mbase, MUSB_INTRTX); + (void)musb_readw(mbase, MUSB_INTRRX); } @@ -1136,8 +1108,7 @@ fail2: musb_platform_exit(musb); fail1: - dev_err(musb->controller, - "musb_init_controller failed with status %d\n", status); + dev_err_probe(musb->controller, status, "musb_init_controller failed\n"); musb_free(musb); diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index d9402fcc4a..d954719161 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -1,35 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * MUSB OTG driver defines * * Copyright 2005 Mentor Graphics Corporation * Copyright (C) 2005-2006 by Texas Instruments * Copyright (C) 2006-2007 Nokia Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * */ #ifndef __MUSB_CORE_H__ @@ -37,8 +12,8 @@ #include <poller.h> #include <notifier.h> -#include <usb/usb.h> -#include <usb/phy.h> +#include <linux/usb/usb.h> +#include <linux/usb/phy.h> #include <linux/spinlock.h> struct musb; @@ -333,7 +308,7 @@ struct musb { struct dma_controller *dma_controller; - struct device_d *controller; + struct device *controller; void __iomem *ctrl_base; void __iomem *mregs; @@ -349,7 +324,6 @@ struct musb { u16 int_rx; u16 int_tx; - //struct device_d *phydev; struct usb_host host; struct usb_phy *xceiv; diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h index 97a03cbcf4..75a0977bd1 100644 --- a/drivers/usb/musb/musb_dma.h +++ b/drivers/usb/musb/musb_dma.h @@ -1,35 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * MUSB OTG driver DMA controller abstraction * * Copyright 2005 Mentor Graphics Corporation * Copyright (C) 2005-2006 by Texas Instruments * Copyright (C) 2006-2007 Nokia Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * */ #ifndef __MUSB_DMA_H__ diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 5fe3bcb7cd..97b64302ec 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Texas Instruments DSPS platforms "glue layer" * @@ -7,22 +8,6 @@ * * This file is part of the Inventra Controller Driver for Linux. * - * The Inventra Controller Driver for Linux is free software; you - * can redistribute it and/or modify it under the terms of the GNU - * General Public License version 2 as published by the Free Software - * Foundation. - * - * The Inventra Controller Driver for Linux is distributed in - * the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public - * License for more details. - * - * You should have received a copy of the GNU General Public License - * along with The Inventra Controller Driver for Linux ; if not, - * write to the Free Software Foundation, Inc., 59 Temple Place, - * Suite 330, Boston, MA 02111-1307 USA - * * musb_dsps.c will be a common file for all the TI DSPS platforms * such as dm64x, dm36x, dm35x, da8x, am35x and ti81x. * For now only ti81x is using this and in future davinci.c, am35x.c @@ -32,14 +17,13 @@ #include <common.h> #include <init.h> #include <clock.h> -#include <usb/usb.h> -#include <usb/musb.h> +#include <linux/usb/usb.h> +#include <linux/usb/musb.h> #include <malloc.h> #include <linux/err.h> #include <linux/barebox-wrapper.h> #include "musb_core.h" -#include "phy-am335x.h" static __maybe_unused struct of_device_id musb_dsps_dt_ids[]; @@ -115,7 +99,7 @@ struct dsps_musb_wrapper { * DSPS glue structure. */ struct dsps_glue { - struct device_d *dev; + struct device *dev; void __iomem *base; unsigned long flags; enum musb_mode mode; @@ -124,7 +108,7 @@ struct dsps_glue { const struct dsps_musb_wrapper *wrp; /* wrapper register offsets */ struct poller_async timer; /* otg_workaround timer */ uint64_t last_timer; /* last timer data for each instance */ - struct device_d otg_dev; + struct device otg_dev; uint32_t otgmode; struct musb_hdrc_platform_data pdata; }; @@ -217,10 +201,6 @@ static int dsps_musb_init(struct musb *musb) const struct dsps_musb_wrapper *wrp = glue->wrp; u32 rev, val, mode; - musb->xceiv = am335x_get_usb_phy(); - if (IS_ERR(musb->xceiv)) - return PTR_ERR(musb->xceiv); - /* Returns zero if e.g. not clocked */ rev = dsps_readl(musb->ctrl_base, wrp->revision); if (!rev) @@ -280,11 +260,11 @@ static int get_int_prop(struct device_node *dn, const char *s) return val; } -static int get_musb_port_mode(struct device_d *dev) +static int get_musb_port_mode(struct device *dev) { enum usb_dr_mode mode; - mode = of_usb_get_dr_mode(dev->device_node, NULL); + mode = of_usb_get_dr_mode(dev->of_node, NULL); switch (mode) { case USB_DR_MODE_HOST: return MUSB_PORT_MODE_HOST; @@ -303,57 +283,29 @@ static int get_musb_port_mode(struct device_d *dev) } } -static int dsps_set_mode(struct param_d *param, void *priv) +static int dsps_set_mode(void *ctx, enum usb_dr_mode mode) { - struct dsps_glue *glue = priv; - - if (glue->pdata.mode != MUSB_PORT_MODE_DUAL_ROLE) - return -EBUSY; + struct dsps_glue *glue = ctx; - switch (glue->otgmode) { - case 0: - default: - return -EINVAL; - case 1: + if (mode == USB_DR_MODE_HOST) glue->pdata.mode = MUSB_PORT_MODE_HOST; - break; - case 2: + else if (mode == USB_DR_MODE_PERIPHERAL) glue->pdata.mode = MUSB_PORT_MODE_GADGET; - break; - } + else + return -EINVAL; return musb_init_controller(&glue->musb, &glue->pdata); } -static const char *dsps_mode_names[] = { - "otg", "host", "peripheral" -}; - -static int dsps_register_otg_device(struct dsps_glue *glue) -{ - int ret; - - dev_set_name(&glue->otg_dev, "otg"); - glue->otg_dev.id = DEVICE_ID_DYNAMIC, - glue->otg_dev.parent = glue->dev; - - ret = register_device(&glue->otg_dev); - if (ret) - return ret; - - dev_add_param_enum(&glue->otg_dev, "mode", - dsps_set_mode, NULL, &glue->otgmode, - dsps_mode_names, ARRAY_SIZE(dsps_mode_names), glue); - return 0; -} - -static int dsps_probe(struct device_d *dev) +static int dsps_probe(struct device *dev) { - struct resource *iores; + struct resource *iores[2]; struct musb_hdrc_platform_data *pdata; struct musb_hdrc_config *config; - struct device_node *dn = dev->device_node; + struct device_node *dn = dev->of_node; const struct dsps_musb_wrapper *wrp; + struct device_node *phy_node; + struct device *phy_dev; struct dsps_glue *glue; int ret; @@ -367,6 +319,14 @@ static int dsps_probe(struct device_d *dev) return -ENODEV; } + phy_node = of_parse_phandle(dn, "phys", 0); + if (!phy_node) + return -ENODEV; + + phy_dev = of_find_device_by_node(phy_node); + if (!phy_dev || !phy_dev->priv) + return -EPROBE_DEFER; + /* allocate glue */ glue = kzalloc(sizeof(*glue), GFP_KERNEL); if (!glue) { @@ -379,17 +339,22 @@ static int dsps_probe(struct device_d *dev) pdata = &glue->pdata; - iores = dev_request_mem_resource(dev, 0); - if (IS_ERR(iores)) - return PTR_ERR(iores); - glue->musb.mregs = IOMEM(iores->start); + iores[0] = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores[0])) { + ret = PTR_ERR(iores[0]); + goto free_glue; + } + glue->musb.mregs = IOMEM(iores[0]->start); - iores = dev_request_mem_resource(dev, 1); - if (IS_ERR(iores)) - return PTR_ERR(iores); - glue->musb.ctrl_base = IOMEM(iores->start); + iores[1] = dev_request_mem_resource(dev, 1); + if (IS_ERR(iores[1])) { + ret = PTR_ERR(iores[1]); + goto release_iores0; + } + glue->musb.ctrl_base = IOMEM(iores[1]->start); glue->musb.controller = dev; + glue->musb.xceiv = phy_dev->priv; config = &glue->config; @@ -405,13 +370,26 @@ static int dsps_probe(struct device_d *dev) config->multipoint = of_property_read_bool(dn, "mentor,multipoint"); if (pdata->mode == MUSB_PORT_MODE_DUAL_ROLE) { - ret = dsps_register_otg_device(glue); + ret = usb_register_otg_device(dev, dsps_set_mode, glue); if (ret) - return ret; + goto release_iores1; return 0; } - return musb_init_controller(&glue->musb, pdata); + ret = musb_init_controller(&glue->musb, pdata); + if (ret) + goto release_iores1; + + return 0; + +release_iores1: + release_region(iores[1]); +release_iores0: + release_region(iores[0]); +free_glue: + free(glue); + + return ret; } static const struct dsps_musb_wrapper am33xx_driver_data = { @@ -450,8 +428,9 @@ static __maybe_unused struct of_device_id musb_dsps_dt_ids[] = { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, musb_dsps_dt_ids); -static struct driver_d dsps_usbss_driver = { +static struct driver dsps_usbss_driver = { .name = "musb-dsps", .probe = dsps_probe, .of_compatible = DRV_OF_COMPAT(musb_dsps_dt_ids), diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index fc5cfb13f5..87d6602f74 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * MUSB OTG driver peripheral support * @@ -5,32 +6,6 @@ * Copyright (C) 2005-2006 by Texas Instruments * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2009 MontaVista Software, Inc. <source@mvista.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * */ #include <common.h> @@ -653,15 +628,6 @@ void musb_free_request(struct usb_ep *ep, struct usb_request *req) kfree(to_musb_request(req)); } -static LIST_HEAD(buffers); - -struct free_record { - struct list_head list; - struct device *dev; - unsigned bytes; - dma_addr_t dma; -}; - /* * Context: controller locked, IRQs blocked. */ @@ -1025,8 +991,7 @@ static void musb_gadget_poll(struct usb_gadget *gadget) static int musb_gadget_start(struct usb_gadget *g, struct usb_gadget_driver *driver); -static int musb_gadget_stop(struct usb_gadget *g, - struct usb_gadget_driver *driver); +static int musb_gadget_stop(struct usb_gadget *g); static const struct usb_gadget_ops musb_gadget_operations = { .get_frame = musb_gadget_get_frame, @@ -1201,16 +1166,9 @@ err: return retval; } -static void stop_activity(struct musb *musb, struct usb_gadget_driver *driver) +static void stop_activity(struct musb *musb) { - int i; - struct musb_hw_ep *hw_ep; - - /* don't disconnect if it's not connected */ - if (musb->g.speed == USB_SPEED_UNKNOWN) - driver = NULL; - else - musb->g.speed = USB_SPEED_UNKNOWN; + musb->g.speed = USB_SPEED_UNKNOWN; /* deactivate the hardware */ if (musb->softconnect) { @@ -1218,25 +1176,6 @@ static void stop_activity(struct musb *musb, struct usb_gadget_driver *driver) musb_pullup(musb, 0); } musb_stop(musb); - - /* killing any outstanding requests will quiesce the driver; - * then report disconnect - */ - if (driver) { - for (i = 0, hw_ep = musb->endpoints; - i < musb->nr_endpoints; - i++, hw_ep++) { - musb_ep_select(musb->mregs, i); - if (hw_ep->is_shared_fifo /* || !epnum */) { - nuke(&hw_ep->ep_in, -ESHUTDOWN); - } else { - if (hw_ep->max_packet_sz_tx) - nuke(&hw_ep->ep_in, -ESHUTDOWN); - if (hw_ep->max_packet_sz_rx) - nuke(&hw_ep->ep_out, -ESHUTDOWN); - } - } - } } /* @@ -1245,8 +1184,7 @@ static void stop_activity(struct musb *musb, struct usb_gadget_driver *driver) * * @param driver the gadget driver to unregister */ -static int musb_gadget_stop(struct usb_gadget *g, - struct usb_gadget_driver *driver) +static int musb_gadget_stop(struct usb_gadget *g) { struct musb *musb = gadget_to_musb(g); unsigned long flags; @@ -1260,10 +1198,7 @@ static int musb_gadget_stop(struct usb_gadget *g, (void) musb_gadget_vbus_draw(&musb->g, 0); - stop_activity(musb, driver); - - dev_dbg(musb->controller, "unregistering driver %s\n", - driver ? driver->function : "(removed)"); + stop_activity(musb); musb->is_active = 0; musb->gadget_driver = NULL; diff --git a/drivers/usb/musb/musb_gadget.h b/drivers/usb/musb/musb_gadget.h index 456c165cc7..bbd1a35880 100644 --- a/drivers/usb/musb/musb_gadget.h +++ b/drivers/usb/musb/musb_gadget.h @@ -1,42 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * MUSB OTG driver peripheral defines * * Copyright 2005 Mentor Graphics Corporation * Copyright (C) 2005-2006 by Texas Instruments * Copyright (C) 2006-2007 Nokia Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * */ #ifndef __MUSB_GADGET_H #define __MUSB_GADGET_H #include <linux/list.h> -#include <usb/gadget.h> +#include <linux/usb/gadget.h> #if IS_ENABLED(CONFIG_USB_MUSB_GADGET) extern int musb_g_ep0_irq(struct musb *); diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c index c8f55ac32c..eed02a8581 100644 --- a/drivers/usb/musb/musb_gadget_ep0.c +++ b/drivers/usb/musb/musb_gadget_ep0.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * MUSB OTG peripheral driver ep0 handling * @@ -5,32 +6,6 @@ * Copyright (C) 2005-2006 by Texas Instruments * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2008-2009 MontaVista Software, Inc. <source@mvista.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * */ #include <common.h> diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 68d819af2c..8176accb4f 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * MUSB OTG driver host support * @@ -5,32 +6,6 @@ * Copyright (C) 2005-2006 by Texas Instruments * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2008-2009 MontaVista Software, Inc. <source@mvista.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * */ #include <common.h> @@ -1189,8 +1164,8 @@ void musb_host_rx(struct musb *musb, u8 epnum) pipe = urb->pipe; - dev_dbg(musb->controller, "<== hw %d rxcsr %04x, urb actual %d (+dma %zu)\n", - epnum, rx_csr, urb->actual_length, 0); + dev_dbg(musb->controller, "<== hw %d rxcsr %04x, urb actual %d (+dma 0)\n", + epnum, rx_csr, urb->actual_length); /* check for errors, concurrent stall & unlink is not really * handled yet! */ diff --git a/drivers/usb/musb/musb_host.h b/drivers/usb/musb/musb_host.h index 0937808de8..5585f2e3b5 100644 --- a/drivers/usb/musb/musb_host.h +++ b/drivers/usb/musb/musb_host.h @@ -1,35 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * MUSB OTG driver host defines * * Copyright 2005 Mentor Graphics Corporation * Copyright (C) 2005-2006 by Texas Instruments * Copyright (C) 2006-2007 Nokia Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * */ #ifndef _MUSB_HOST_H @@ -37,7 +12,7 @@ //#include <linux/scatterlist.h> #include <linux/list.h> -#include <usb/usb.h> +#include <linux/usb/usb.h> #include <asm/unaligned.h> /* diff --git a/drivers/usb/musb/musb_io.h b/drivers/usb/musb/musb_io.h index ec474477a1..658bb146fd 100644 --- a/drivers/usb/musb/musb_io.h +++ b/drivers/usb/musb/musb_io.h @@ -1,35 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * MUSB OTG driver register I/O * * Copyright 2005 Mentor Graphics Corporation * Copyright (C) 2005-2006 by Texas Instruments * Copyright (C) 2006-2007 Nokia Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * */ #ifndef __MUSB_LINUX_PLATFORM_ARCH_H__ diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h index 2cb749140b..e843e58b93 100644 --- a/drivers/usb/musb/musb_regs.h +++ b/drivers/usb/musb/musb_regs.h @@ -1,35 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * MUSB OTG driver register defines * * Copyright 2005 Mentor Graphics Corporation * Copyright (C) 2005-2006 by Texas Instruments * Copyright (C) 2006-2007 Nokia Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * */ #ifndef __MUSB_REGS_H__ diff --git a/drivers/usb/musb/phy-am335x-control.c b/drivers/usb/musb/phy-am335x-control.c index c84525ec7e..313c67ef7e 100644 --- a/drivers/usb/musb/phy-am335x-control.c +++ b/drivers/usb/musb/phy-am335x-control.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> #include <init.h> #include <io.h> @@ -7,7 +8,7 @@ #include "am35x-phy-control.h" struct am335x_control_usb { - struct device_d *dev; + struct device *dev; void __iomem *phy_reg; void __iomem *wkup; spinlock_t lock; @@ -101,30 +102,31 @@ static __maybe_unused struct of_device_id omap_control_usb_dt_ids[] = { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, omap_control_usb_dt_ids); -struct phy_control *am335x_get_phy_control(struct device_d *dev) +struct phy_control *am335x_get_phy_control(struct device *dev) { struct device_node *node; struct am335x_control_usb *ctrl_usb; - node = of_parse_phandle(dev->device_node, "ti,ctrl_mod", 0); + node = of_parse_phandle(dev->of_node, "ti,ctrl_mod", 0); if (!node) - return NULL; + return ERR_PTR(-ENOENT); dev = of_find_device_by_node(node); if (!dev) - return NULL; + return ERR_PTR(-EPROBE_DEFER); ctrl_usb = dev->priv; if (!ctrl_usb) - return NULL; + return ERR_PTR(-EPROBE_DEFER); return &ctrl_usb->phy_ctrl; } EXPORT_SYMBOL(am335x_get_phy_control); -static int am335x_control_usb_probe(struct device_d *dev) +static int am335x_control_usb_probe(struct device *dev) { struct resource *iores; /*struct resource *res;*/ @@ -141,13 +143,17 @@ static int am335x_control_usb_probe(struct device_d *dev) ctrl_usb->dev = dev; iores = dev_request_mem_resource(dev, 0); - if (IS_ERR(iores)) - return PTR_ERR(iores); + if (IS_ERR(iores)) { + ret = PTR_ERR(iores); + goto free_ctrl; + } ctrl_usb->phy_reg = IOMEM(iores->start); iores = dev_request_mem_resource(dev, 1); - if (IS_ERR(iores)) - return PTR_ERR(iores); + if (IS_ERR(iores)) { + ret = PTR_ERR(iores); + goto release_resource; + } ctrl_usb->wkup = IOMEM(iores->start); spin_lock_init(&ctrl_usb->lock); @@ -155,9 +161,16 @@ static int am335x_control_usb_probe(struct device_d *dev) dev->priv = ctrl_usb; return 0; + +release_resource: + release_region(iores); +free_ctrl: + free(ctrl_usb); + + return 0; }; -static struct driver_d am335x_control_driver = { +static struct driver am335x_control_driver = { .name = "am335x-control-usb", .probe = am335x_control_usb_probe, .of_compatible = DRV_OF_COMPAT(omap_control_usb_dt_ids), diff --git a/drivers/usb/musb/phy-am335x.c b/drivers/usb/musb/phy-am335x.c index df31255d89..f2a12182e0 100644 --- a/drivers/usb/musb/phy-am335x.c +++ b/drivers/usb/musb/phy-am335x.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> #include <init.h> #include <io.h> @@ -5,7 +6,6 @@ #include <linux/err.h> #include "am35x-phy-control.h" #include "musb_core.h" -#include "phy-am335x.h" struct am335x_usbphy { void __iomem *base; @@ -14,13 +14,6 @@ struct am335x_usbphy { struct usb_phy phy; }; -static struct am335x_usbphy *am_usbphy; - -struct usb_phy *am335x_get_usb_phy(void) -{ - return &am_usbphy->phy; -} - static int am335x_init(struct usb_phy *phy) { struct am335x_usbphy *am_usbphy = container_of(phy, struct am335x_usbphy, phy); @@ -29,8 +22,9 @@ static int am335x_init(struct usb_phy *phy) return 0; } -static int am335x_phy_probe(struct device_d *dev) +static int am335x_phy_probe(struct device *dev) { + struct am335x_usbphy *am_usbphy; struct resource *iores; int ret; @@ -44,22 +38,27 @@ static int am335x_phy_probe(struct device_d *dev) am_usbphy->base = IOMEM(iores->start); am_usbphy->phy_ctrl = am335x_get_phy_control(dev); - if (!am_usbphy->phy_ctrl) - return -ENODEV; + if (IS_ERR(am_usbphy->phy_ctrl)) { + ret = PTR_ERR(am_usbphy->phy_ctrl); + goto err_release; + } - am_usbphy->id = of_alias_get_id(dev->device_node, "phy"); + am_usbphy->id = of_alias_get_id(dev->of_node, "phy"); if (am_usbphy->id < 0) { dev_err(dev, "Missing PHY id: %d\n", am_usbphy->id); - return am_usbphy->id; + ret = am_usbphy->id; + goto err_release; } am_usbphy->phy.init = am335x_init; - dev->priv = am_usbphy; + dev->priv = &am_usbphy->phy; dev_info(dev, "am_usbphy %p enabled\n", &am_usbphy->phy); return 0; +err_release: + release_region(iores); err_free: free(am_usbphy); @@ -73,15 +72,12 @@ static __maybe_unused struct of_device_id am335x_phy_dt_ids[] = { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, am335x_phy_dt_ids); -static struct driver_d am335x_phy_driver = { +static struct driver am335x_phy_driver = { .name = "am335x-phy-driver", .probe = am335x_phy_probe, .of_compatible = DRV_OF_COMPAT(am335x_phy_dt_ids), }; -static int am335x_phy_init(void) -{ - return platform_driver_register(&am335x_phy_driver); -} -fs_initcall(am335x_phy_init); +fs_platform_driver(am335x_phy_driver); diff --git a/drivers/usb/musb/phy-am335x.h b/drivers/usb/musb/phy-am335x.h deleted file mode 100644 index 27da2e3b10..0000000000 --- a/drivers/usb/musb/phy-am335x.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _PHY_AM335x_H_ -#define _PHY_AM335x_H_ - -struct usb_phy *am335x_get_usb_phy(void); - -#endif diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index 2c9fb46e4d..f8e592e89e 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config USB_ULPI bool "ULPI Transceiver support" help @@ -6,3 +7,7 @@ config USB_ULPI config USB_TWL4030 depends on MFD_TWL4030 bool "TWL4030 Transceiver support" + +config USB_OTGDEV + bool + diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile index 465a7f098c..d6cac86cff 100644 --- a/drivers/usb/otg/Makefile +++ b/drivers/usb/otg/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_USB_ULPI) += ulpi.o obj-$(CONFIG_USB_TWL4030) += twl4030.o - +obj-$(CONFIG_USB_OTGDEV) += otgdev.o diff --git a/drivers/usb/otg/otgdev.c b/drivers/usb/otg/otgdev.c new file mode 100644 index 0000000000..5a86263430 --- /dev/null +++ b/drivers/usb/otg/otgdev.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <common.h> +#include <driver.h> +#include <linux/usb/usb.h> + +struct otg_mode { + struct device dev; + unsigned int var_mode; + unsigned int cur_mode; + int (*set_mode_callback)(void *ctx, enum usb_dr_mode mode); + void *ctx; +}; + +static int otg_set_mode(struct param_d *param, void *ctx) +{ + struct otg_mode *otg = ctx; + int ret; + + if (otg->var_mode == USB_DR_MODE_UNKNOWN) + return -EINVAL; + + if (otg->var_mode == otg->cur_mode) + return 0; + + if (otg->cur_mode != USB_DR_MODE_OTG) + return -EBUSY; + + ret = otg->set_mode_callback(otg->ctx, otg->var_mode); + if (ret) + return ret; + + otg->cur_mode = otg->var_mode; + + return 0; +} + +static const char *otg_mode_names[] = { + [USB_DR_MODE_UNKNOWN] = "unknown", + [USB_DR_MODE_HOST] = "host", + [USB_DR_MODE_PERIPHERAL] = "peripheral", + [USB_DR_MODE_OTG] = "otg", +}; + +static int register_otg_device(struct device *dev, struct otg_mode *otg) +{ + struct param_d *param_mode; + int ret; + + ret = register_device(dev); + if (ret) + return ret; + + param_mode = dev_add_param_enum(dev, "mode", + otg_set_mode, NULL, &otg->var_mode, + otg_mode_names, ARRAY_SIZE(otg_mode_names), otg); + + return PTR_ERR_OR_ZERO(param_mode); +} + +struct bus_type otg_bus_type = { + .name = "usbotg" /* "otg" is already taken for the alias */ +}; + +int otg_device_get_mode(struct device *dev) +{ + struct otg_mode *otg; + + if (dev->bus != &otg_bus_type) + return -ENODEV; + + otg = dev->priv; + + return otg->cur_mode; +} + +int usb_register_otg_device(struct device *parent, + int (*set_mode)(void *ctx, enum usb_dr_mode mode), void *ctx) +{ + bool first_otg = list_empty(&otg_bus_type.device_list); + int ret; + struct otg_mode *otg; + + otg = xzalloc(sizeof(*otg)); + otg->dev.priv = otg; + otg->dev.parent = parent; + otg->dev.bus = &otg_bus_type; + otg->dev.id = DEVICE_ID_DYNAMIC; + dev_set_name(&otg->dev, "otg"); + + otg->var_mode = USB_DR_MODE_OTG; + otg->cur_mode = USB_DR_MODE_OTG; + otg->set_mode_callback = set_mode; + otg->ctx = ctx; + + ret = register_otg_device(&otg->dev, otg); + if (ret) + return ret; + + /* register otg.mode as an alias of otg0.mode */ + if (first_otg) + dev_add_alias(&otg->dev, "otg"); + + return 0; +} + +static int otg_bus_init(void) +{ + return bus_register(&otg_bus_type); +} +pure_initcall(otg_bus_init); diff --git a/drivers/usb/otg/twl4030.c b/drivers/usb/otg/twl4030.c index 3668870b9e..5cbf734ded 100644 --- a/drivers/usb/otg/twl4030.c +++ b/drivers/usb/otg/twl4030.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2009 Wind River Systems, Inc. * Tom Rix <Tom.Rix@windriver.com> @@ -20,21 +21,10 @@ * Author: Atin Malaviya (atin.malaviya@gmail.com) * * ------------------------------------------------------------------------ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <mfd/twl4030.h> -#include <usb/twl4030.h> +#include <linux/usb/twl4030.h> #include <clock.h> static int twl4030_usb_write(u8 address, u8 data) diff --git a/drivers/usb/otg/ulpi.c b/drivers/usb/otg/ulpi.c index 9bc432fa86..d231b49b08 100644 --- a/drivers/usb/otg/ulpi.c +++ b/drivers/usb/otg/ulpi.c @@ -1,20 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright 2008 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <common.h> #include <io.h> #include <errno.h> -#include <usb/ulpi.h> +#include <linux/usb/ulpi.h> /* ULPIVIEW register bits */ #define ULPIVW_WU (1 << 31) /* Wakeup */ diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig index b80c039117..efca9874d5 100644 --- a/drivers/usb/storage/Kconfig +++ b/drivers/usb/storage/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config USB_STORAGE tristate "USB Mass Storage support" select DISK diff --git a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile index adf08433d5..8c60f3a2e5 100644 --- a/drivers/usb/storage/Makefile +++ b/drivers/usb/storage/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_USB_STORAGE) += usb-storage.o usb-storage-objs := usb.o transport.o diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 5186508ba6..be3b18dc66 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -1,21 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Most of this source has been derived from the Linux and * U-Boot USB Mass Storage driver implementations. * * Adapted for barebox: * Copyright (c) 2011, AMK Drives & Controls Ltd. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * */ #include <common.h> @@ -60,7 +49,7 @@ static int usb_stor_Bulk_clear_endpt_stall(struct us_data *us, unsigned int pipe /* Determine what the maximum LUN supported is */ int usb_stor_Bulk_max_lun(struct us_data *us) { - struct device_d *dev = &us->pusb_dev->dev; + struct device *dev = &us->pusb_dev->dev; int len, ret = 0; unsigned char *iobuf = dma_alloc(1); @@ -96,40 +85,45 @@ int usb_stor_Bulk_transport(struct us_blk_dev *usb_blkdev, void *data, u32 datalen) { struct us_data *us = usb_blkdev->us; - struct device_d *dev = &us->pusb_dev->dev; - struct bulk_cb_wrap cbw; - struct bulk_cs_wrap csw; + struct device *dev = &us->pusb_dev->dev; + struct bulk_cb_wrap *cbw; + struct bulk_cs_wrap *csw; int actlen, data_actlen; int result; unsigned int residue; unsigned int pipein = usb_rcvbulkpipe(us->pusb_dev, us->recv_bulk_ep); unsigned int pipeout = usb_sndbulkpipe(us->pusb_dev, us->send_bulk_ep); int dir_in = US_DIRECTION(cmd[0]); + int ret = 0; + + cbw = dma_alloc(sizeof(*cbw)); + csw = dma_alloc(sizeof(*csw)); /* set up the command wrapper */ - cbw.Signature = cpu_to_le32(US_BULK_CB_SIGN); - cbw.DataTransferLength = cpu_to_le32(datalen); - cbw.Flags = (dir_in ? US_BULK_FLAG_IN : US_BULK_FLAG_OUT); - cbw.Tag = ++cbw_tag; - cbw.Lun = usb_blkdev->lun; - cbw.Length = cmdlen; + cbw->Signature = cpu_to_le32(US_BULK_CB_SIGN); + cbw->DataTransferLength = cpu_to_le32(datalen); + cbw->Flags = (dir_in ? US_BULK_FLAG_IN : US_BULK_FLAG_OUT); + cbw->Tag = ++cbw_tag; + cbw->Lun = usb_blkdev->lun; + cbw->Length = cmdlen; /* copy the command payload */ - memset(cbw.CDB, 0, sizeof(cbw.CDB)); - memcpy(cbw.CDB, cmd, cbw.Length); + memset(cbw->CDB, 0, sizeof(cbw->CDB)); + memcpy(cbw->CDB, cmd, cbw->Length); /* send it to out endpoint */ dev_dbg(dev, "Bulk Command S 0x%x T 0x%x L %d F %d Trg %d LUN %d CL %d\n", - le32_to_cpu(cbw.Signature), cbw.Tag, - le32_to_cpu(cbw.DataTransferLength), cbw.Flags, - (cbw.Lun >> 4), (cbw.Lun & 0x0F), - cbw.Length); - result = usb_bulk_msg(us->pusb_dev, pipeout, &cbw, US_BULK_CB_WRAP_LEN, + le32_to_cpu(cbw->Signature), cbw->Tag, + le32_to_cpu(cbw->DataTransferLength), cbw->Flags, + (cbw->Lun >> 4), (cbw->Lun & 0x0F), + cbw->Length); + result = usb_bulk_msg(us->pusb_dev, pipeout, cbw, US_BULK_CB_WRAP_LEN, &actlen, USB_BULK_TO); dev_dbg(dev, "Bulk command transfer result=%d\n", result); if (result < 0) { usb_stor_Bulk_reset(us); - return USB_STOR_TRANSPORT_FAILED; + ret = USB_STOR_TRANSPORT_FAILED; + goto fail; } /* DATA STAGE */ @@ -152,13 +146,14 @@ int usb_stor_Bulk_transport(struct us_blk_dev *usb_blkdev, if (result < 0) { dev_dbg(dev, "Device status: %lx\n", us->pusb_dev->status); usb_stor_Bulk_reset(us); - return USB_STOR_TRANSPORT_FAILED; + ret = USB_STOR_TRANSPORT_FAILED; + goto fail; } } /* STATUS phase + error handling */ dev_dbg(dev, "Attempting to get CSW...\n"); - result = usb_bulk_msg(us->pusb_dev, pipein, &csw, US_BULK_CS_WRAP_LEN, + result = usb_bulk_msg(us->pusb_dev, pipein, csw, US_BULK_CS_WRAP_LEN, &actlen, USB_BULK_TO); /* did the endpoint stall? */ @@ -169,7 +164,7 @@ int usb_stor_Bulk_transport(struct us_blk_dev *usb_blkdev, if (result >= 0) { dev_dbg(dev, "Attempting to get CSW...\n"); result = usb_bulk_msg(us->pusb_dev, pipein, - &csw, US_BULK_CS_WRAP_LEN, + csw, US_BULK_CS_WRAP_LEN, &actlen, USB_BULK_TO); } } @@ -177,35 +172,39 @@ int usb_stor_Bulk_transport(struct us_blk_dev *usb_blkdev, if (result < 0) { dev_dbg(dev, "Device status: %lx\n", us->pusb_dev->status); usb_stor_Bulk_reset(us); - return USB_STOR_TRANSPORT_FAILED; + ret = USB_STOR_TRANSPORT_FAILED; + goto fail; } /* check bulk status */ - residue = le32_to_cpu(csw.Residue); + residue = le32_to_cpu(csw->Residue); dev_dbg(dev, "Bulk Status S 0x%x T 0x%x R %u Stat 0x%x\n", - le32_to_cpu(csw.Signature), csw.Tag, residue, csw.Status); - if (csw.Signature != cpu_to_le32(US_BULK_CS_SIGN)) { + le32_to_cpu(csw->Signature), csw->Tag, residue, csw->Status); + if (csw->Signature != cpu_to_le32(US_BULK_CS_SIGN)) { dev_dbg(dev, "Bad CSW signature\n"); usb_stor_Bulk_reset(us); - return USB_STOR_TRANSPORT_FAILED; - } else if (csw.Tag != cbw_tag) { + ret = USB_STOR_TRANSPORT_FAILED; + } else if (csw->Tag != cbw_tag) { dev_dbg(dev, "Mismatching tag\n"); usb_stor_Bulk_reset(us); - return USB_STOR_TRANSPORT_FAILED; - } else if (csw.Status >= US_BULK_STAT_PHASE) { + ret = USB_STOR_TRANSPORT_FAILED; + } else if (csw->Status >= US_BULK_STAT_PHASE) { dev_dbg(dev, "Status >= phase\n"); usb_stor_Bulk_reset(us); - return USB_STOR_TRANSPORT_ERROR; + ret = USB_STOR_TRANSPORT_ERROR; } else if (residue > datalen) { dev_dbg(dev, "residue (%uB) > req data (%uB)\n", residue, datalen); - return USB_STOR_TRANSPORT_FAILED; - } else if (csw.Status == US_BULK_STAT_FAIL) { + ret = USB_STOR_TRANSPORT_FAILED; + } else if (csw->Status == US_BULK_STAT_FAIL) { dev_dbg(dev, "FAILED\n"); - return USB_STOR_TRANSPORT_FAILED; + ret = USB_STOR_TRANSPORT_FAILED; } - return 0; +fail: + dma_free(cbw); + dma_free(csw); + return ret; } @@ -214,7 +213,7 @@ int usb_stor_Bulk_transport(struct us_blk_dev *usb_blkdev, */ int usb_stor_Bulk_reset(struct us_data *us) { - struct device_d *dev = &us->pusb_dev->dev; + struct device *dev = &us->pusb_dev->dev; int result; int result2; unsigned int pipe; diff --git a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h index 22d7dea3f5..5e08ae718a 100644 --- a/drivers/usb/storage/transport.h +++ b/drivers/usb/storage/transport.h @@ -1,21 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Most of this source has been derived from the Linux and * U-Boot USB Mass Storage driver implementations. * * Adapted for barebox: * Copyright (c) 2011, AMK Drives & Controls Ltd. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * */ #ifndef _TRANSPORT_H_ diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index e0ef4f5ef3..cc241e69be 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -1,30 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Most of this source has been derived from the Linux and * U-Boot USB Mass Storage driver implementations. * * Adapted for barebox: * Copyright (c) 2011, AMK Drives & Controls Ltd. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * */ #include <common.h> #include <init.h> #include <malloc.h> +#include <dma.h> #include <errno.h> #include <scsi.h> -#include <usb/usb.h> -#include <usb/usb_defs.h> +#include <linux/usb/usb.h> +#include <linux/usb/usb_defs.h> #include <asm/unaligned.h> @@ -41,10 +31,10 @@ static int usb_stor_request_sense(struct us_blk_dev *usb_blkdev) { struct us_data *us = usb_blkdev->us; - struct device_d *dev = &us->pusb_dev->dev; + struct device *dev = &us->pusb_dev->dev; u8 cmd[6]; const u8 datalen = 18; - u8 *data = xzalloc(datalen); + u8 *data = dma_alloc(datalen); dev_dbg(dev, "SCSI_REQ_SENSE\n"); @@ -55,7 +45,7 @@ static int usb_stor_request_sense(struct us_blk_dev *usb_blkdev) dev_dbg(dev, "Request Sense returned %02X %02X %02X\n", data[2], data[12], data[13]); - free(data); + dma_free(data); return 0; } @@ -84,7 +74,7 @@ static int usb_stor_transport(struct us_blk_dev *usb_blkdev, int retries, int request_sense_delay_ms) { struct us_data *us = usb_blkdev->us; - struct device_d *dev = &us->pusb_dev->dev; + struct device *dev = &us->pusb_dev->dev; int i, ret; for (i = 0; i <= retries; i++) { @@ -111,11 +101,11 @@ static int usb_stor_transport(struct us_blk_dev *usb_blkdev, static int usb_stor_inquiry(struct us_blk_dev *usb_blkdev) { - struct device_d *dev = &usb_blkdev->us->pusb_dev->dev; + struct device *dev = &usb_blkdev->us->pusb_dev->dev; int ret; u8 cmd[6]; const u16 datalen = 36; - u8 *data = xzalloc(datalen); + u8 *data = dma_alloc(datalen); memset(cmd, 0, sizeof(cmd)); cmd[0] = SCSI_INQUIRY; @@ -137,55 +127,129 @@ static int usb_stor_inquiry(struct us_blk_dev *usb_blkdev) // TODO: process and store device info exit: - free(data); + dma_free(data); return ret; } -static int usb_stor_test_unit_ready(struct us_blk_dev *usb_blkdev) +static int usb_stor_test_unit_ready(struct us_blk_dev *usb_blkdev, u64 timeout_ns) { + u64 start; u8 cmd[6]; int ret; memset(cmd, 0, sizeof(cmd)); cmd[0] = SCSI_TST_U_RDY; - ret = usb_stor_transport(usb_blkdev, cmd, sizeof(cmd), NULL, 0, - 10, 100); - if (ret < 0) - return -ENODEV; + start = get_time_ns(); - return 0; + do { + ret = usb_stor_transport(usb_blkdev, cmd, sizeof(cmd), NULL, 0, + 10, 100); + } while (ret < 0 && !is_timeout(start, timeout_ns)); + + return ret ? -ENODEV : 0; } -static int usb_stor_read_capacity(struct us_blk_dev *usb_blkdev, - u32 *last_lba, u32 *block_length) +static int read_capacity_16(struct us_blk_dev *usb_blkdev) { - struct device_d *dev = &usb_blkdev->us->pusb_dev->dev; + struct device *dev = &usb_blkdev->us->pusb_dev->dev; + unsigned char cmd[16]; + const u8 datalen = 32; + u8 *data = dma_alloc(datalen); + int ret; + sector_t lba; + unsigned sector_size; + + memset(cmd, 0, 16); + cmd[0] = SERVICE_ACTION_IN_16; + cmd[1] = SAI_READ_CAPACITY_16; + cmd[13] = datalen; + + ret = usb_stor_transport(usb_blkdev, cmd, sizeof(cmd), data, datalen, + 3, USB_STOR_NO_REQUEST_SENSE); + + if (ret < 0) { + dev_warn(dev, "Read Capacity(16) failed\n"); + goto fail; + } + + /* Note this is logical, not physical sector size */ + sector_size = be32_to_cpup((u32 *)&data[8]); + lba = be64_to_cpup((u64 *)&data[0]); + + dev_dbg(dev, "LBA (16) = 0x%llx w/ sector size = %u\n", + lba, sector_size); + + if ((data[12] & 1) == 1) { + dev_warn(dev, "Protection unsupported\n"); + ret = -ENOTSUPP; + goto fail; + } + + usb_blkdev->blk.blockbits = SECTOR_SHIFT; + usb_blkdev->blk.num_blocks = lba + 1; + + ret = sector_size; +fail: + dma_free(data); + return ret; +} + +static int read_capacity_10(struct us_blk_dev *usb_blkdev) +{ + struct device *dev = &usb_blkdev->us->pusb_dev->dev; + unsigned char cmd[16]; const u32 datalen = 8; - u32 *data = xzalloc(datalen); - u8 cmd[10]; + __be32 *data = dma_alloc(datalen); int ret; + sector_t lba; + unsigned sector_size; memset(cmd, 0, sizeof(cmd)); cmd[0] = SCSI_RD_CAPAC; ret = usb_stor_transport(usb_blkdev, cmd, sizeof(cmd), data, datalen, 3, USB_STOR_NO_REQUEST_SENSE); - if (ret < 0) - goto exit; - dev_dbg(dev, "Read Capacity returns: 0x%x, 0x%x\n", - data[0], data[1]); - *last_lba = be32_to_cpu(data[0]); - *block_length = be32_to_cpu(data[1]); + if (ret < 0) { + dev_warn(dev, "Read Capacity(10) failed\n"); + goto fail; + } -exit: - free(data); + sector_size = be32_to_cpu(data[1]); + lba = be32_to_cpu(data[0]); + + dev_dbg(dev, "LBA (10) = 0x%llx w/ sector size = %u\n", + lba, sector_size); + + if (sector_size != SECTOR_SIZE) + dev_warn(dev, "Support only %d bytes sectors\n", SECTOR_SIZE); + + usb_blkdev->blk.num_blocks = lba + 1; + usb_blkdev->blk.blockbits = SECTOR_SHIFT; + + ret = SECTOR_SIZE; +fail: + dma_free(data); return ret; } +static int usb_stor_io_16(struct us_blk_dev *usb_blkdev, u8 opcode, + sector_t start, u8 *data, u16 blocks) +{ + u8 cmd[16]; + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = opcode; + put_unaligned_be64(start, &cmd[2]); + put_unaligned_be32(blocks, &cmd[10]); + + return usb_stor_transport(usb_blkdev, cmd, sizeof(cmd), data, + blocks * SECTOR_SIZE, 10, 0); +} + static int usb_stor_io_10(struct us_blk_dev *usb_blkdev, u8 opcode, - u32 start, u8 *data, u16 blocks) + sector_t start, u8 *data, u16 blocks) { u8 cmd[10]; @@ -206,37 +270,49 @@ static int usb_stor_io_10(struct us_blk_dev *usb_blkdev, u8 opcode, /* Read / write a chunk of sectors on media */ static int usb_stor_blk_io(struct block_device *disk_dev, - int sector_start, int sector_count, void *buffer, + sector_t sector_start, blkcnt_t sector_count, void *buffer, bool read) { struct us_blk_dev *pblk_dev = container_of(disk_dev, struct us_blk_dev, blk); struct us_data *us = pblk_dev->us; - struct device_d *dev = &us->pusb_dev->dev; + struct device *dev = &us->pusb_dev->dev; + int result; /* ensure unit ready */ dev_dbg(dev, "Testing for unit ready\n"); - if (usb_stor_test_unit_ready(pblk_dev)) { + if (usb_stor_test_unit_ready(pblk_dev, 0)) { dev_dbg(dev, "Device NOT ready\n"); return -EIO; } /* read / write the requested data */ - dev_dbg(dev, "%s %u block(s), starting from %d\n", + dev_dbg(dev, "%s %llu block(s), starting from %llu\n", read ? "Read" : "Write", sector_count, sector_start); while (sector_count > 0) { - unsigned n = min(sector_count, US_MAX_IO_BLK); + u16 n = min_t(blkcnt_t, sector_count, US_MAX_IO_BLK); + + if (disk_dev->num_blocks > 0xffffffff) { + result = usb_stor_io_16(pblk_dev, + read ? SCSI_READ16 : SCSI_WRITE16, + sector_start, + buffer, n); + } else { + + result = usb_stor_io_10(pblk_dev, + read ? SCSI_READ10 : SCSI_WRITE10, + sector_start, + buffer, n); + } - if (usb_stor_io_10(pblk_dev, - read ? SCSI_READ10 : SCSI_WRITE10, - sector_start, - buffer, n)) { - dev_dbg(dev, "I/O error at sector %d\n", sector_start); + if (result) { + dev_dbg(dev, "I/O error at sector %llu\n", sector_start); break; } + sector_start += n; sector_count -= n; buffer += n * SECTOR_SIZE; @@ -247,14 +323,14 @@ static int usb_stor_blk_io(struct block_device *disk_dev, /* Write a chunk of sectors to media */ static int __maybe_unused usb_stor_blk_write(struct block_device *blk, - const void *buffer, int block, int num_blocks) + const void *buffer, sector_t block, blkcnt_t num_blocks) { return usb_stor_blk_io(blk, block, num_blocks, (void *)buffer, false); } /* Read a chunk of sectors from media */ -static int usb_stor_blk_read(struct block_device *blk, void *buffer, int block, - int num_blocks) +static int usb_stor_blk_read(struct block_device *blk, void *buffer, sector_t block, + blkcnt_t num_blocks) { return usb_stor_blk_io(blk, block, num_blocks, buffer, true); } @@ -274,8 +350,7 @@ static struct block_device_ops usb_mass_storage_ops = { static int usb_stor_init_blkdev(struct us_blk_dev *pblk_dev) { struct us_data *us = pblk_dev->us; - struct device_d *dev = &us->pusb_dev->dev; - u32 last_lba = 0, block_length = 0; + struct device *dev = &us->pusb_dev->dev; int result; /* get device info */ @@ -290,7 +365,8 @@ static int usb_stor_init_blkdev(struct us_blk_dev *pblk_dev) /* ensure unit ready */ dev_dbg(dev, "Testing for unit ready\n"); - result = usb_stor_test_unit_ready(pblk_dev); + /* retry a bit longer than usual as some HDDs take longer to spin up */ + result = usb_stor_test_unit_ready(pblk_dev, 10ULL * NSEC_PER_SEC); if (result) { dev_dbg(dev, "Device NOT ready\n"); return result; @@ -299,23 +375,19 @@ static int usb_stor_init_blkdev(struct us_blk_dev *pblk_dev) /* read capacity */ dev_dbg(dev, "Reading capacity\n"); - result = usb_stor_read_capacity(pblk_dev, &last_lba, &block_length); - if (result < 0) { - dev_dbg(dev, "Cannot read device capacity\n"); + result = read_capacity_10(pblk_dev); + if (result < 0) return result; - } - if (last_lba > INT_MAX - 1) { - last_lba = INT_MAX - 1; - dev_warn(dev, - "Limiting device size due to 31 bit contraints\n"); + if (pblk_dev->blk.num_blocks > 0xffffffff) { + result = read_capacity_16(pblk_dev); + if (result < 0) { + dev_notice(dev, "Using 0xffffffff as device size\n"); + pblk_dev->blk.num_blocks = 1 + (sector_t) 0xffffffff; + } } - pblk_dev->blk.num_blocks = last_lba + 1; - if (block_length != SECTOR_SIZE) - pr_warn("Support only %d bytes sectors\n", SECTOR_SIZE); - pblk_dev->blk.blockbits = SECTOR_SHIFT; - dev_dbg(dev, "Capacity = 0x%x, blockshift = 0x%x\n", + dev_dbg(dev, "Capacity = 0x%llx, blockshift = 0x%x\n", pblk_dev->blk.num_blocks, pblk_dev->blk.blockbits); return 0; @@ -324,7 +396,7 @@ static int usb_stor_init_blkdev(struct us_blk_dev *pblk_dev) /* Create and register a disk device for the specified LUN */ static int usb_stor_add_blkdev(struct us_data *us, unsigned char lun) { - struct device_d *dev = &us->pusb_dev->dev; + struct device *dev = &us->pusb_dev->dev; struct us_blk_dev *pblk_dev; int result; @@ -345,10 +417,11 @@ static int usb_stor_add_blkdev(struct us_data *us, unsigned char lun) result = cdev_find_free_index("disk"); if (result == -1) pr_err("Cannot find a free number for the disk node\n"); - pr_info("Using index %d for the new disk\n", result); + dev_info(dev, "registering as disk%d\n", result); pblk_dev->blk.cdev.name = basprintf("disk%d", result); pblk_dev->blk.blockbits = SECTOR_SHIFT; + pblk_dev->blk.type = BLK_TYPE_USB; result = blockdevice_register(&pblk_dev->blk); if (result != 0) { @@ -356,11 +429,6 @@ static int usb_stor_add_blkdev(struct us_data *us, unsigned char lun) goto BadDevice; } - /* create partitions on demand */ - result = parse_partition_table(&pblk_dev->blk); - if (result != 0) - dev_warn(dev, "No partition table found\n"); - list_add_tail(&pblk_dev->list, &us->blk_dev_list); dev_dbg(dev, "USB disk device successfully added\n"); @@ -379,7 +447,7 @@ BadDevice: /* Get the transport settings */ static void get_transport(struct us_data *us) { - struct device_d *dev = &us->pusb_dev->dev; + struct device *dev = &us->pusb_dev->dev; switch (us->protocol) { case US_PR_BULK: us->transport_name = "Bulk"; @@ -394,7 +462,7 @@ static void get_transport(struct us_data *us) /* Get the endpoint settings */ static int get_pipes(struct us_data *us, struct usb_interface *intf) { - struct device_d *dev = &us->pusb_dev->dev; + struct device *dev = &us->pusb_dev->dev; unsigned int i; struct usb_endpoint_descriptor *ep; struct usb_endpoint_descriptor *ep_in = NULL; @@ -435,7 +503,7 @@ static int get_pipes(struct us_data *us, struct usb_interface *intf) /* Scan device's LUNs, registering a disk device for each LUN */ static int usb_stor_scan(struct usb_device *usbdev, struct us_data *us) { - struct device_d *dev = &usbdev->dev; + struct device *dev = &usbdev->dev; unsigned char lun; int num_devs = 0; @@ -459,7 +527,7 @@ static int usb_stor_scan(struct usb_device *usbdev, struct us_data *us) static int usb_stor_probe(struct usb_device *usbdev, const struct usb_device_id *id) { - struct device_d *dev = &usbdev->dev; + struct device *dev = &usbdev->dev; struct us_data *us; int result; int ifno; diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index cd4904f03b..ae16d7b60b 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -1,27 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Most of this source has been derived from the Linux and * U-Boot USB Mass Storage driver implementations. * * Adapted for barebox: * Copyright (c) 2011, AMK Drives & Controls Ltd. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * */ #ifndef _STORAGE_USB_H_ #define _STORAGE_USB_H_ -#include <usb/usb.h> +#include <linux/usb/usb.h> #include <block.h> #include <disks.h> #include <scsi.h> diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig new file mode 100644 index 0000000000..3b32a4e05a --- /dev/null +++ b/drivers/usb/typec/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-only + + +config TYPEC + prompt "Compile USB Type-C framework support" if COMPILE_TEST + bool + +config TYPEC_TUSB320 + tristate "TI TUSB320 Type-C port controller" + depends on I2C + select REGMAP_I2C + select TYPEC + help + Say Y or here if your system has a TI TUSB320 Type-C port controller. diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile new file mode 100644 index 0000000000..456b94afbf --- /dev/null +++ b/drivers/usb/typec/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_TYPEC) += class.o +obj-$(CONFIG_TYPEC_TUSB320) += tusb320.o diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c new file mode 100644 index 0000000000..7f498550f8 --- /dev/null +++ b/drivers/usb/typec/class.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * USB Type-C Connector Class + * + * Copyright (C) 2017, Intel Corporation + * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> + */ + +#include <module.h> +#include <driver.h> +#include <linux/usb/role.h> +#include <linux/usb/typec.h> +#include <linux/usb/typec_altmode.h> +#include <param.h> + +enum typec_param_accessory { + TYPEC_PARAM_ACCESSORY_NONE, + TYPEC_PARAM_ACCESSORY_AUDIO, + TYPEC_PARAM_ACCESSORY_DEBUG, +}; + +struct typec_port { + struct device dev; + const struct typec_operations *ops; + int pwr_role; /* enum typec_role */ + int usb_role; /* enum usb_role role */ + int accessory; /* enum typec_param_accessory */ +}; + +/** + * typec_set_pwr_role - Report power role change + * @port: The USB Type-C Port where the role was changed + * @role: The new data role + * + * This routine is used by the port drivers to report power role changes. + */ +void typec_set_pwr_role(struct typec_port *port, enum typec_role role) +{ + port->pwr_role = role; +} +EXPORT_SYMBOL_GPL(typec_set_pwr_role); + +static inline enum typec_param_accessory typec_mode_to_accessory(int mode) +{ + switch (mode) { + case TYPEC_MODE_AUDIO: + return TYPEC_PARAM_ACCESSORY_AUDIO; + case TYPEC_MODE_DEBUG: + return TYPEC_PARAM_ACCESSORY_DEBUG; + default: + return TYPEC_PARAM_ACCESSORY_NONE; + } +} + +/** + * typec_set_mode - Set mode of operation for USB Type-C connector + * @port: USB Type-C connector + * @mode: Accessory Mode, USB Operation or Safe State + * + * Configure @port for Accessory Mode @mode. This function will configure the + * muxes needed for @mode. + */ +int typec_set_mode(struct typec_port *port, int mode) +{ + port->accessory = typec_mode_to_accessory(mode); + return 0; +} +EXPORT_SYMBOL_GPL(typec_set_mode); + +/** + * typec_set_role - Set USB role for a Type-C connector + * @port: USB Type-C connector + * @role: USB role to be switched to + * + * Set USB role @role for @sw. This is equivalent to Linux + * usb_role_switch_set_role(); + */ +int typec_set_role(struct typec_port *port, enum usb_role role) +{ + port->usb_role = role; + return 0; +} +EXPORT_SYMBOL_GPL(typec_set_role); + +/** + * typec_get_drvdata - Return private driver data pointer + * @port: USB Type-C port + */ +void *typec_get_drvdata(struct typec_port *port) +{ + return port->dev.priv; +} +EXPORT_SYMBOL_GPL(typec_get_drvdata); + +static int typec_register_port_dev(struct typec_port *port, const char *name, int id) +{ + port->dev.id = id; + dev_set_name(&port->dev, name); + + return register_device(&port->dev); +} + +static const char * const usb_role_names[] = { + [USB_ROLE_NONE] = "none", + [USB_ROLE_HOST] = "host", + [USB_ROLE_DEVICE] = "device", +}; + +static const char * const pwr_role_names[] = { + [TYPEC_SINK] = "sink", + [TYPEC_SOURCE] = "source", +}; + +static const char * const accessory_names[] = { + [TYPEC_PARAM_ACCESSORY_NONE] = "none", + [TYPEC_PARAM_ACCESSORY_AUDIO] = "audio", /* analog */ + [TYPEC_PARAM_ACCESSORY_DEBUG] = "debug", +}; + +static int typec_param_update(struct param_d *p, void *priv) +{ + struct typec_port *port = priv; + + return port->ops->poll(port); +} + +/** + * typec_register_port - Register a USB Type-C Port + * @parent: Parent device + * @cap: Description of the port + * + * Registers a device for USB Type-C Port described in @cap. + * + * Returns handle to the port on success or ERR_PTR on failure. + */ +struct typec_port *typec_register_port(struct device *parent, + const struct typec_capability *cap) +{ + struct typec_port *port; + struct device *dev; + const char *alias; + int ret; + + port = kzalloc(sizeof(*port), GFP_KERNEL); + if (!port) + return ERR_PTR(-ENOMEM); + + port->ops = cap->ops; + dev = &port->dev; + dev->parent = parent; + dev->of_node = cap->of_node; + dev->priv = cap->driver_data; + + alias = dev->of_node ? of_alias_get(dev->of_node) : NULL; + if (alias) + ret = typec_register_port_dev(port, alias, DEVICE_ID_SINGLE); + if (!alias || ret) + ret = typec_register_port_dev(port, "typec", DEVICE_ID_DYNAMIC); + + if (ret) + return ERR_PTR(ret); + + of_platform_device_dummy_drv(dev); + if (dev->of_node) + dev->of_node->dev = dev; + + dev_add_param_enum(dev, "usb_role", param_set_readonly, typec_param_update, + &port->usb_role, usb_role_names, + ARRAY_SIZE(usb_role_names), port); + dev_add_param_enum(dev, "pwr_role", param_set_readonly, typec_param_update, + &port->pwr_role, pwr_role_names, + ARRAY_SIZE(pwr_role_names), port); + dev_add_param_enum(dev, "accessory", param_set_readonly, typec_param_update, + &port->accessory, accessory_names, + ARRAY_SIZE(accessory_names), port); + + return port; +} +EXPORT_SYMBOL_GPL(typec_register_port); diff --git a/drivers/usb/typec/tusb320.c b/drivers/usb/typec/tusb320.c new file mode 100644 index 0000000000..90a846b0fb --- /dev/null +++ b/drivers/usb/typec/tusb320.c @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Based on the Linux driver: + * drivers/typec/typec-tusb320.c - TUSB320 typec driver + * + * Copyright (C) 2020 National Instruments Corporation + * Author: Michael Auchter <michael.auchter@ni.com> + */ + +#include <linux/bitfield.h> +#include <i2c/i2c.h> +#include <init.h> +#include <linux/kernel.h> +#include <linux/printk.h> +#include <linux/bitops.h> +#include <module.h> +#include <linux/regmap.h> +#include <linux/usb/typec.h> +#include <linux/usb/typec_altmode.h> + +#define TUSB320_REG8 0x8 +#define TUSB320_REG8_CURRENT_MODE_ADVERTISE GENMASK(7, 6) +#define TUSB320_REG8_CURRENT_MODE_ADVERTISE_USB 0x0 +#define TUSB320_REG8_CURRENT_MODE_ADVERTISE_15A 0x1 +#define TUSB320_REG8_CURRENT_MODE_ADVERTISE_30A 0x2 +#define TUSB320_REG8_CURRENT_MODE_DETECT GENMASK(5, 4) +#define TUSB320_REG8_CURRENT_MODE_DETECT_DEF 0x0 +#define TUSB320_REG8_CURRENT_MODE_DETECT_MED 0x1 +#define TUSB320_REG8_CURRENT_MODE_DETECT_ACC 0x2 +#define TUSB320_REG8_CURRENT_MODE_DETECT_HI 0x3 +#define TUSB320_REG8_ACCESSORY_CONNECTED GENMASK(3, 1) +#define TUSB320_REG8_ACCESSORY_CONNECTED_NONE 0x0 +#define TUSB320_REG8_ACCESSORY_CONNECTED_AUDIO 0x4 +#define TUSB320_REG8_ACCESSORY_CONNECTED_ACHRG 0x5 +#define TUSB320_REG8_ACCESSORY_CONNECTED_DBGDFP 0x6 +#define TUSB320_REG8_ACCESSORY_CONNECTED_DBGUFP 0x7 +#define TUSB320_REG8_ACTIVE_CABLE_DETECTION BIT(0) + +#define TUSB320_REG9 0x9 +#define TUSB320_REG9_ATTACHED_STATE GENMASK(7, 6) +#define TUSB320_REG9_CABLE_DIRECTION BIT(5) +#define TUSB320_REG9_INTERRUPT_STATUS BIT(4) + +enum tusb320_attached_state { + TUSB320_ATTACHED_STATE_NONE, + TUSB320_ATTACHED_STATE_DFP, + TUSB320_ATTACHED_STATE_UFP, + TUSB320_ATTACHED_STATE_ACC, +}; + +struct tusb320_priv { + struct device *dev; + struct regmap *regmap; + struct typec_port *port; + struct typec_capability cap; +}; + +static int tusb320_typec_irq_handler(struct tusb320_priv *priv, u8 reg9) +{ + struct typec_port *port = priv->port; + int typec_mode; + enum typec_role pwr_role; + enum usb_role usb_role; + u8 state, accessory; + int ret, reg8; + + ret = regmap_read(priv->regmap, TUSB320_REG8, ®8); + if (ret) + return ret; + + state = FIELD_GET(TUSB320_REG9_ATTACHED_STATE, reg9); + accessory = FIELD_GET(TUSB320_REG8_ACCESSORY_CONNECTED, reg8); + + switch (state) { + case TUSB320_ATTACHED_STATE_DFP: + typec_mode = TYPEC_MODE_USB2; + usb_role = USB_ROLE_HOST; + pwr_role = TYPEC_SOURCE; + break; + case TUSB320_ATTACHED_STATE_UFP: + typec_mode = TYPEC_MODE_USB2; + usb_role = USB_ROLE_DEVICE; + pwr_role = TYPEC_SINK; + break; + case TUSB320_ATTACHED_STATE_ACC: + /* + * Accessory detected. For debug accessories, just make some + * qualified guesses as to the role for lack of a better option. + */ + if (accessory == TUSB320_REG8_ACCESSORY_CONNECTED_AUDIO || + accessory == TUSB320_REG8_ACCESSORY_CONNECTED_ACHRG) { + typec_mode = TYPEC_MODE_AUDIO; + usb_role = USB_ROLE_NONE; + pwr_role = TYPEC_SINK; + break; + } else if (accessory == + TUSB320_REG8_ACCESSORY_CONNECTED_DBGDFP) { + typec_mode = TYPEC_MODE_DEBUG; + pwr_role = TYPEC_SOURCE; + usb_role = USB_ROLE_HOST; + break; + } else if (accessory == + TUSB320_REG8_ACCESSORY_CONNECTED_DBGUFP) { + typec_mode = TYPEC_MODE_DEBUG; + pwr_role = TYPEC_SINK; + usb_role = USB_ROLE_DEVICE; + break; + } + + dev_warn(priv->dev, "unexpected ACCESSORY_CONNECTED state %d\n", + accessory); + + fallthrough; + default: + typec_mode = TYPEC_MODE_USB2; + usb_role = USB_ROLE_NONE; + pwr_role = TYPEC_SINK; + break; + } + + typec_set_pwr_role(port, pwr_role); + typec_set_mode(port, typec_mode); + typec_set_role(port, usb_role); + + return 0; +} + +static int tusb320_state_update_handler(struct tusb320_priv *priv, + bool force_update) +{ + unsigned int reg; + int ret; + + ret = regmap_read(priv->regmap, TUSB320_REG9, ®); + if (ret) + return ret; + + if (!force_update && !(reg & TUSB320_REG9_INTERRUPT_STATUS)) + return 0; + + ret = tusb320_typec_irq_handler(priv, reg); + + regmap_write(priv->regmap, TUSB320_REG9, reg); + + return ret; +} + +static irqreturn_t tusb320_irq_handler(struct typec_port *port) +{ + struct tusb320_priv *priv = typec_get_drvdata(port); + + return tusb320_state_update_handler(priv, false); +} + +static const struct typec_operations tusb320_typec_ops = { + .poll = tusb320_irq_handler, +}; + +static const struct regmap_config tusb320_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int tusb320_typec_probe(struct i2c_client *client, + struct tusb320_priv *priv) +{ + struct device_node *connector; + + connector = of_get_child_by_name(client->dev.of_node, "connector"); + + priv->cap.driver_data = priv; + priv->cap.ops = &tusb320_typec_ops; + priv->cap.of_node = connector; + + priv->port = typec_register_port(&client->dev, &priv->cap); + + return PTR_ERR_OR_ZERO(priv->port); +} + +static int tusb320_probe(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct tusb320_priv *priv; + int ret; + + priv = xzalloc(sizeof(*priv)); + + priv->dev = &client->dev; + i2c_set_clientdata(client, priv); + + priv->regmap = regmap_init_i2c(client, &tusb320_regmap_config); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + ret = tusb320_typec_probe(client, priv); + if (ret) + return ret; + + /* update initial state */ + tusb320_state_update_handler(priv, true); + + return ret; +} + +static const struct of_device_id tusb320_typec_dt_match[] = { + { .compatible = "ti,tusb320" }, + { .compatible = "ti,tusb320l" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, tusb320_typec_dt_match); + +static struct driver tusb320_typec_driver = { + .name = "typec-tusb320", + .of_match_table = tusb320_typec_dt_match, + .probe = tusb320_probe, +}; +device_i2c_driver(tusb320_typec_driver); + +MODULE_AUTHOR("Michael Auchter <michael.auchter@ni.com>"); +MODULE_DESCRIPTION("TI TUSB320 Type-C driver"); +MODULE_LICENSE("GPL v2"); |