17ecca2a4SBenjamin Herrenschmidt // SPDX-License-Identifier: GPL-2.0+
27ecca2a4SBenjamin Herrenschmidt /*
37ecca2a4SBenjamin Herrenschmidt * aspeed-vhub -- Driver for Aspeed SoC "vHub" USB gadget
47ecca2a4SBenjamin Herrenschmidt *
57ecca2a4SBenjamin Herrenschmidt * hub.c - virtual hub handling
67ecca2a4SBenjamin Herrenschmidt *
77ecca2a4SBenjamin Herrenschmidt * Copyright 2017 IBM Corporation
87ecca2a4SBenjamin Herrenschmidt */
97ecca2a4SBenjamin Herrenschmidt
107ecca2a4SBenjamin Herrenschmidt #include <linux/kernel.h>
117ecca2a4SBenjamin Herrenschmidt #include <linux/module.h>
127ecca2a4SBenjamin Herrenschmidt #include <linux/platform_device.h>
137ecca2a4SBenjamin Herrenschmidt #include <linux/delay.h>
147ecca2a4SBenjamin Herrenschmidt #include <linux/ioport.h>
157ecca2a4SBenjamin Herrenschmidt #include <linux/slab.h>
167ecca2a4SBenjamin Herrenschmidt #include <linux/errno.h>
177ecca2a4SBenjamin Herrenschmidt #include <linux/list.h>
187ecca2a4SBenjamin Herrenschmidt #include <linux/interrupt.h>
197ecca2a4SBenjamin Herrenschmidt #include <linux/proc_fs.h>
207ecca2a4SBenjamin Herrenschmidt #include <linux/prefetch.h>
217ecca2a4SBenjamin Herrenschmidt #include <linux/clk.h>
227ecca2a4SBenjamin Herrenschmidt #include <linux/usb/gadget.h>
237ecca2a4SBenjamin Herrenschmidt #include <linux/of.h>
247ecca2a4SBenjamin Herrenschmidt #include <linux/regmap.h>
257ecca2a4SBenjamin Herrenschmidt #include <linux/dma-mapping.h>
267ecca2a4SBenjamin Herrenschmidt #include <linux/bcd.h>
277ecca2a4SBenjamin Herrenschmidt #include <linux/version.h>
287ecca2a4SBenjamin Herrenschmidt #include <linux/usb.h>
297ecca2a4SBenjamin Herrenschmidt #include <linux/usb/hcd.h>
307ecca2a4SBenjamin Herrenschmidt
317ecca2a4SBenjamin Herrenschmidt #include "vhub.h"
327ecca2a4SBenjamin Herrenschmidt
337ecca2a4SBenjamin Herrenschmidt /* usb 2.0 hub device descriptor
347ecca2a4SBenjamin Herrenschmidt *
357ecca2a4SBenjamin Herrenschmidt * A few things we may want to improve here:
367ecca2a4SBenjamin Herrenschmidt *
377ecca2a4SBenjamin Herrenschmidt * - We may need to indicate TT support
387ecca2a4SBenjamin Herrenschmidt * - We may need a device qualifier descriptor
397ecca2a4SBenjamin Herrenschmidt * as devices can pretend to be usb1 or 2
407ecca2a4SBenjamin Herrenschmidt * - Make vid/did overridable
417ecca2a4SBenjamin Herrenschmidt * - make it look like usb1 if usb1 mode forced
427ecca2a4SBenjamin Herrenschmidt */
4388a68672SSasha Levin #define KERNEL_REL bin2bcd(LINUX_VERSION_MAJOR)
4488a68672SSasha Levin #define KERNEL_VER bin2bcd(LINUX_VERSION_PATCHLEVEL)
457ecca2a4SBenjamin Herrenschmidt
467ecca2a4SBenjamin Herrenschmidt enum {
475cc0710fSTao Ren AST_VHUB_STR_INDEX_MAX = 4,
487ecca2a4SBenjamin Herrenschmidt AST_VHUB_STR_MANUF = 3,
497ecca2a4SBenjamin Herrenschmidt AST_VHUB_STR_PRODUCT = 2,
507ecca2a4SBenjamin Herrenschmidt AST_VHUB_STR_SERIAL = 1,
517ecca2a4SBenjamin Herrenschmidt };
527ecca2a4SBenjamin Herrenschmidt
537ecca2a4SBenjamin Herrenschmidt static const struct usb_device_descriptor ast_vhub_dev_desc = {
547ecca2a4SBenjamin Herrenschmidt .bLength = USB_DT_DEVICE_SIZE,
557ecca2a4SBenjamin Herrenschmidt .bDescriptorType = USB_DT_DEVICE,
567ecca2a4SBenjamin Herrenschmidt .bcdUSB = cpu_to_le16(0x0200),
577ecca2a4SBenjamin Herrenschmidt .bDeviceClass = USB_CLASS_HUB,
587ecca2a4SBenjamin Herrenschmidt .bDeviceSubClass = 0,
597ecca2a4SBenjamin Herrenschmidt .bDeviceProtocol = 1,
607ecca2a4SBenjamin Herrenschmidt .bMaxPacketSize0 = 64,
617ecca2a4SBenjamin Herrenschmidt .idVendor = cpu_to_le16(0x1d6b),
627ecca2a4SBenjamin Herrenschmidt .idProduct = cpu_to_le16(0x0107),
637ecca2a4SBenjamin Herrenschmidt .bcdDevice = cpu_to_le16(0x0100),
647ecca2a4SBenjamin Herrenschmidt .iManufacturer = AST_VHUB_STR_MANUF,
657ecca2a4SBenjamin Herrenschmidt .iProduct = AST_VHUB_STR_PRODUCT,
667ecca2a4SBenjamin Herrenschmidt .iSerialNumber = AST_VHUB_STR_SERIAL,
677ecca2a4SBenjamin Herrenschmidt .bNumConfigurations = 1,
687ecca2a4SBenjamin Herrenschmidt };
697ecca2a4SBenjamin Herrenschmidt
70347f3f54SNeal Liu static const struct usb_qualifier_descriptor ast_vhub_qual_desc = {
71347f3f54SNeal Liu .bLength = 0xA,
72347f3f54SNeal Liu .bDescriptorType = USB_DT_DEVICE_QUALIFIER,
73347f3f54SNeal Liu .bcdUSB = cpu_to_le16(0x0200),
74347f3f54SNeal Liu .bDeviceClass = USB_CLASS_HUB,
75347f3f54SNeal Liu .bDeviceSubClass = 0,
76347f3f54SNeal Liu .bDeviceProtocol = 0,
77347f3f54SNeal Liu .bMaxPacketSize0 = 64,
78347f3f54SNeal Liu .bNumConfigurations = 1,
79347f3f54SNeal Liu .bRESERVED = 0,
80347f3f54SNeal Liu };
81347f3f54SNeal Liu
827ecca2a4SBenjamin Herrenschmidt /*
837ecca2a4SBenjamin Herrenschmidt * Configuration descriptor: same comments as above
847ecca2a4SBenjamin Herrenschmidt * regarding handling USB1 mode.
857ecca2a4SBenjamin Herrenschmidt */
867ecca2a4SBenjamin Herrenschmidt
877ecca2a4SBenjamin Herrenschmidt /*
887ecca2a4SBenjamin Herrenschmidt * We don't use sizeof() as Linux definition of
897ecca2a4SBenjamin Herrenschmidt * struct usb_endpoint_descriptor contains 2
907ecca2a4SBenjamin Herrenschmidt * extra bytes
917ecca2a4SBenjamin Herrenschmidt */
927ecca2a4SBenjamin Herrenschmidt #define AST_VHUB_CONF_DESC_SIZE (USB_DT_CONFIG_SIZE + \
937ecca2a4SBenjamin Herrenschmidt USB_DT_INTERFACE_SIZE + \
947ecca2a4SBenjamin Herrenschmidt USB_DT_ENDPOINT_SIZE)
957ecca2a4SBenjamin Herrenschmidt
966dbf05fcSTao Ren static const struct ast_vhub_full_cdesc ast_vhub_conf_desc = {
977ecca2a4SBenjamin Herrenschmidt .cfg = {
987ecca2a4SBenjamin Herrenschmidt .bLength = USB_DT_CONFIG_SIZE,
997ecca2a4SBenjamin Herrenschmidt .bDescriptorType = USB_DT_CONFIG,
1007ecca2a4SBenjamin Herrenschmidt .wTotalLength = cpu_to_le16(AST_VHUB_CONF_DESC_SIZE),
1017ecca2a4SBenjamin Herrenschmidt .bNumInterfaces = 1,
1027ecca2a4SBenjamin Herrenschmidt .bConfigurationValue = 1,
1037ecca2a4SBenjamin Herrenschmidt .iConfiguration = 0,
1047ecca2a4SBenjamin Herrenschmidt .bmAttributes = USB_CONFIG_ATT_ONE |
1057ecca2a4SBenjamin Herrenschmidt USB_CONFIG_ATT_SELFPOWER |
1067ecca2a4SBenjamin Herrenschmidt USB_CONFIG_ATT_WAKEUP,
1077ecca2a4SBenjamin Herrenschmidt .bMaxPower = 0,
1087ecca2a4SBenjamin Herrenschmidt },
1097ecca2a4SBenjamin Herrenschmidt .intf = {
1107ecca2a4SBenjamin Herrenschmidt .bLength = USB_DT_INTERFACE_SIZE,
1117ecca2a4SBenjamin Herrenschmidt .bDescriptorType = USB_DT_INTERFACE,
1127ecca2a4SBenjamin Herrenschmidt .bInterfaceNumber = 0,
1137ecca2a4SBenjamin Herrenschmidt .bAlternateSetting = 0,
1147ecca2a4SBenjamin Herrenschmidt .bNumEndpoints = 1,
1157ecca2a4SBenjamin Herrenschmidt .bInterfaceClass = USB_CLASS_HUB,
1167ecca2a4SBenjamin Herrenschmidt .bInterfaceSubClass = 0,
1177ecca2a4SBenjamin Herrenschmidt .bInterfaceProtocol = 0,
1187ecca2a4SBenjamin Herrenschmidt .iInterface = 0,
1197ecca2a4SBenjamin Herrenschmidt },
1207ecca2a4SBenjamin Herrenschmidt .ep = {
1217ecca2a4SBenjamin Herrenschmidt .bLength = USB_DT_ENDPOINT_SIZE,
1227ecca2a4SBenjamin Herrenschmidt .bDescriptorType = USB_DT_ENDPOINT,
1237ecca2a4SBenjamin Herrenschmidt .bEndpointAddress = 0x81,
1247ecca2a4SBenjamin Herrenschmidt .bmAttributes = USB_ENDPOINT_XFER_INT,
1257ecca2a4SBenjamin Herrenschmidt .wMaxPacketSize = cpu_to_le16(1),
1267ecca2a4SBenjamin Herrenschmidt .bInterval = 0x0c,
1277ecca2a4SBenjamin Herrenschmidt },
1287ecca2a4SBenjamin Herrenschmidt };
1297ecca2a4SBenjamin Herrenschmidt
1307ecca2a4SBenjamin Herrenschmidt #define AST_VHUB_HUB_DESC_SIZE (USB_DT_HUB_NONVAR_SIZE + 2)
1317ecca2a4SBenjamin Herrenschmidt
1327ecca2a4SBenjamin Herrenschmidt static const struct usb_hub_descriptor ast_vhub_hub_desc = {
1337ecca2a4SBenjamin Herrenschmidt .bDescLength = AST_VHUB_HUB_DESC_SIZE,
1347ecca2a4SBenjamin Herrenschmidt .bDescriptorType = USB_DT_HUB,
1357ecca2a4SBenjamin Herrenschmidt .bNbrPorts = AST_VHUB_NUM_PORTS,
1367ecca2a4SBenjamin Herrenschmidt .wHubCharacteristics = cpu_to_le16(HUB_CHAR_NO_LPSM),
1377ecca2a4SBenjamin Herrenschmidt .bPwrOn2PwrGood = 10,
1387ecca2a4SBenjamin Herrenschmidt .bHubContrCurrent = 0,
1397ecca2a4SBenjamin Herrenschmidt .u.hs.DeviceRemovable[0] = 0,
1407ecca2a4SBenjamin Herrenschmidt .u.hs.DeviceRemovable[1] = 0xff,
1417ecca2a4SBenjamin Herrenschmidt };
1427ecca2a4SBenjamin Herrenschmidt
1437ecca2a4SBenjamin Herrenschmidt /*
1447ecca2a4SBenjamin Herrenschmidt * These strings converted to UTF-16 must be smaller than
1457ecca2a4SBenjamin Herrenschmidt * our EP0 buffer.
1467ecca2a4SBenjamin Herrenschmidt */
1477ecca2a4SBenjamin Herrenschmidt static const struct usb_string ast_vhub_str_array[] = {
1487ecca2a4SBenjamin Herrenschmidt {
1497ecca2a4SBenjamin Herrenschmidt .id = AST_VHUB_STR_SERIAL,
1507ecca2a4SBenjamin Herrenschmidt .s = "00000000"
1517ecca2a4SBenjamin Herrenschmidt },
1527ecca2a4SBenjamin Herrenschmidt {
1537ecca2a4SBenjamin Herrenschmidt .id = AST_VHUB_STR_PRODUCT,
1547ecca2a4SBenjamin Herrenschmidt .s = "USB Virtual Hub"
1557ecca2a4SBenjamin Herrenschmidt },
1567ecca2a4SBenjamin Herrenschmidt {
1577ecca2a4SBenjamin Herrenschmidt .id = AST_VHUB_STR_MANUF,
1587ecca2a4SBenjamin Herrenschmidt .s = "Aspeed"
1597ecca2a4SBenjamin Herrenschmidt },
1607ecca2a4SBenjamin Herrenschmidt { }
1617ecca2a4SBenjamin Herrenschmidt };
1627ecca2a4SBenjamin Herrenschmidt
1637ecca2a4SBenjamin Herrenschmidt static const struct usb_gadget_strings ast_vhub_strings = {
1647ecca2a4SBenjamin Herrenschmidt .language = 0x0409,
1657ecca2a4SBenjamin Herrenschmidt .strings = (struct usb_string *)ast_vhub_str_array
1667ecca2a4SBenjamin Herrenschmidt };
1677ecca2a4SBenjamin Herrenschmidt
ast_vhub_hub_dev_status(struct ast_vhub_ep * ep,u16 wIndex,u16 wValue)1687ecca2a4SBenjamin Herrenschmidt static int ast_vhub_hub_dev_status(struct ast_vhub_ep *ep,
1697ecca2a4SBenjamin Herrenschmidt u16 wIndex, u16 wValue)
1707ecca2a4SBenjamin Herrenschmidt {
1717ecca2a4SBenjamin Herrenschmidt u8 st0;
1727ecca2a4SBenjamin Herrenschmidt
1737ecca2a4SBenjamin Herrenschmidt EPDBG(ep, "GET_STATUS(dev)\n");
1747ecca2a4SBenjamin Herrenschmidt
1757ecca2a4SBenjamin Herrenschmidt /*
1767ecca2a4SBenjamin Herrenschmidt * Mark it as self-powered, I doubt the BMC is powered off
1777ecca2a4SBenjamin Herrenschmidt * the USB bus ...
1787ecca2a4SBenjamin Herrenschmidt */
1797ecca2a4SBenjamin Herrenschmidt st0 = 1 << USB_DEVICE_SELF_POWERED;
1807ecca2a4SBenjamin Herrenschmidt
1817ecca2a4SBenjamin Herrenschmidt /*
1827ecca2a4SBenjamin Herrenschmidt * Need to double check how remote wakeup actually works
1837ecca2a4SBenjamin Herrenschmidt * on that chip and what triggers it.
1847ecca2a4SBenjamin Herrenschmidt */
1857ecca2a4SBenjamin Herrenschmidt if (ep->vhub->wakeup_en)
1867ecca2a4SBenjamin Herrenschmidt st0 |= 1 << USB_DEVICE_REMOTE_WAKEUP;
1877ecca2a4SBenjamin Herrenschmidt
1887ecca2a4SBenjamin Herrenschmidt return ast_vhub_simple_reply(ep, st0, 0);
1897ecca2a4SBenjamin Herrenschmidt }
1907ecca2a4SBenjamin Herrenschmidt
ast_vhub_hub_ep_status(struct ast_vhub_ep * ep,u16 wIndex,u16 wValue)1917ecca2a4SBenjamin Herrenschmidt static int ast_vhub_hub_ep_status(struct ast_vhub_ep *ep,
1927ecca2a4SBenjamin Herrenschmidt u16 wIndex, u16 wValue)
1937ecca2a4SBenjamin Herrenschmidt {
1947ecca2a4SBenjamin Herrenschmidt int ep_num;
1957ecca2a4SBenjamin Herrenschmidt u8 st0 = 0;
1967ecca2a4SBenjamin Herrenschmidt
1977ecca2a4SBenjamin Herrenschmidt ep_num = wIndex & USB_ENDPOINT_NUMBER_MASK;
1987ecca2a4SBenjamin Herrenschmidt EPDBG(ep, "GET_STATUS(ep%d)\n", ep_num);
1997ecca2a4SBenjamin Herrenschmidt
2007ecca2a4SBenjamin Herrenschmidt /* On the hub we have only EP 0 and 1 */
2017ecca2a4SBenjamin Herrenschmidt if (ep_num == 1) {
2027ecca2a4SBenjamin Herrenschmidt if (ep->vhub->ep1_stalled)
2037ecca2a4SBenjamin Herrenschmidt st0 |= 1 << USB_ENDPOINT_HALT;
2047ecca2a4SBenjamin Herrenschmidt } else if (ep_num != 0)
2057ecca2a4SBenjamin Herrenschmidt return std_req_stall;
2067ecca2a4SBenjamin Herrenschmidt
2077ecca2a4SBenjamin Herrenschmidt return ast_vhub_simple_reply(ep, st0, 0);
2087ecca2a4SBenjamin Herrenschmidt }
2097ecca2a4SBenjamin Herrenschmidt
ast_vhub_hub_dev_feature(struct ast_vhub_ep * ep,u16 wIndex,u16 wValue,bool is_set)2107ecca2a4SBenjamin Herrenschmidt static int ast_vhub_hub_dev_feature(struct ast_vhub_ep *ep,
2117ecca2a4SBenjamin Herrenschmidt u16 wIndex, u16 wValue,
2127ecca2a4SBenjamin Herrenschmidt bool is_set)
2137ecca2a4SBenjamin Herrenschmidt {
214aa9c2219SNeal Liu u32 val;
215aa9c2219SNeal Liu
2167ecca2a4SBenjamin Herrenschmidt EPDBG(ep, "%s_FEATURE(dev val=%02x)\n",
2177ecca2a4SBenjamin Herrenschmidt is_set ? "SET" : "CLEAR", wValue);
2187ecca2a4SBenjamin Herrenschmidt
219aa9c2219SNeal Liu if (wValue == USB_DEVICE_REMOTE_WAKEUP) {
2207ecca2a4SBenjamin Herrenschmidt ep->vhub->wakeup_en = is_set;
2217ecca2a4SBenjamin Herrenschmidt EPDBG(ep, "Hub remote wakeup %s\n",
2227ecca2a4SBenjamin Herrenschmidt is_set ? "enabled" : "disabled");
223aa9c2219SNeal Liu return std_req_complete;
224aa9c2219SNeal Liu }
225aa9c2219SNeal Liu
226aa9c2219SNeal Liu if (wValue == USB_DEVICE_TEST_MODE) {
227aa9c2219SNeal Liu val = readl(ep->vhub->regs + AST_VHUB_CTRL);
228aa9c2219SNeal Liu val &= ~GENMASK(10, 8);
229aa9c2219SNeal Liu val |= VHUB_CTRL_SET_TEST_MODE((wIndex >> 8) & 0x7);
230aa9c2219SNeal Liu writel(val, ep->vhub->regs + AST_VHUB_CTRL);
2317ecca2a4SBenjamin Herrenschmidt
2327ecca2a4SBenjamin Herrenschmidt return std_req_complete;
2337ecca2a4SBenjamin Herrenschmidt }
2347ecca2a4SBenjamin Herrenschmidt
235aa9c2219SNeal Liu return std_req_stall;
236aa9c2219SNeal Liu }
237aa9c2219SNeal Liu
ast_vhub_hub_ep_feature(struct ast_vhub_ep * ep,u16 wIndex,u16 wValue,bool is_set)2387ecca2a4SBenjamin Herrenschmidt static int ast_vhub_hub_ep_feature(struct ast_vhub_ep *ep,
2397ecca2a4SBenjamin Herrenschmidt u16 wIndex, u16 wValue,
2407ecca2a4SBenjamin Herrenschmidt bool is_set)
2417ecca2a4SBenjamin Herrenschmidt {
2427ecca2a4SBenjamin Herrenschmidt int ep_num;
2437ecca2a4SBenjamin Herrenschmidt u32 reg;
2447ecca2a4SBenjamin Herrenschmidt
2457ecca2a4SBenjamin Herrenschmidt ep_num = wIndex & USB_ENDPOINT_NUMBER_MASK;
2467ecca2a4SBenjamin Herrenschmidt EPDBG(ep, "%s_FEATURE(ep%d val=%02x)\n",
2477ecca2a4SBenjamin Herrenschmidt is_set ? "SET" : "CLEAR", ep_num, wValue);
2487ecca2a4SBenjamin Herrenschmidt
2497ecca2a4SBenjamin Herrenschmidt if (ep_num > 1)
2507ecca2a4SBenjamin Herrenschmidt return std_req_stall;
2517ecca2a4SBenjamin Herrenschmidt if (wValue != USB_ENDPOINT_HALT)
2527ecca2a4SBenjamin Herrenschmidt return std_req_stall;
2537ecca2a4SBenjamin Herrenschmidt if (ep_num == 0)
2547ecca2a4SBenjamin Herrenschmidt return std_req_complete;
2557ecca2a4SBenjamin Herrenschmidt
2567ecca2a4SBenjamin Herrenschmidt EPDBG(ep, "%s stall on EP 1\n",
2577ecca2a4SBenjamin Herrenschmidt is_set ? "setting" : "clearing");
2587ecca2a4SBenjamin Herrenschmidt
2597ecca2a4SBenjamin Herrenschmidt ep->vhub->ep1_stalled = is_set;
2607ecca2a4SBenjamin Herrenschmidt reg = readl(ep->vhub->regs + AST_VHUB_EP1_CTRL);
2617ecca2a4SBenjamin Herrenschmidt if (is_set) {
2627ecca2a4SBenjamin Herrenschmidt reg |= VHUB_EP1_CTRL_STALL;
2637ecca2a4SBenjamin Herrenschmidt } else {
2647ecca2a4SBenjamin Herrenschmidt reg &= ~VHUB_EP1_CTRL_STALL;
2657ecca2a4SBenjamin Herrenschmidt reg |= VHUB_EP1_CTRL_RESET_TOGGLE;
2667ecca2a4SBenjamin Herrenschmidt }
2677ecca2a4SBenjamin Herrenschmidt writel(reg, ep->vhub->regs + AST_VHUB_EP1_CTRL);
2687ecca2a4SBenjamin Herrenschmidt
2697ecca2a4SBenjamin Herrenschmidt return std_req_complete;
2707ecca2a4SBenjamin Herrenschmidt }
2717ecca2a4SBenjamin Herrenschmidt
ast_vhub_rep_desc(struct ast_vhub_ep * ep,u8 desc_type,u16 len)2727ecca2a4SBenjamin Herrenschmidt static int ast_vhub_rep_desc(struct ast_vhub_ep *ep,
2737ecca2a4SBenjamin Herrenschmidt u8 desc_type, u16 len)
2747ecca2a4SBenjamin Herrenschmidt {
2757ecca2a4SBenjamin Herrenschmidt size_t dsize;
2766dbf05fcSTao Ren struct ast_vhub *vhub = ep->vhub;
2777ecca2a4SBenjamin Herrenschmidt
2787ecca2a4SBenjamin Herrenschmidt EPDBG(ep, "GET_DESCRIPTOR(type:%d)\n", desc_type);
2797ecca2a4SBenjamin Herrenschmidt
2807ecca2a4SBenjamin Herrenschmidt /*
2817ecca2a4SBenjamin Herrenschmidt * Copy first to EP buffer and send from there, so
2827ecca2a4SBenjamin Herrenschmidt * we can do some in-place patching if needed. We know
2837ecca2a4SBenjamin Herrenschmidt * the EP buffer is big enough but ensure that doesn't
2847ecca2a4SBenjamin Herrenschmidt * change. We do that now rather than later after we
2857ecca2a4SBenjamin Herrenschmidt * have checked sizes etc... to avoid a gcc bug where
2867ecca2a4SBenjamin Herrenschmidt * it thinks len is constant and barfs about read
2877ecca2a4SBenjamin Herrenschmidt * overflows in memcpy.
2887ecca2a4SBenjamin Herrenschmidt */
2897ecca2a4SBenjamin Herrenschmidt switch(desc_type) {
2907ecca2a4SBenjamin Herrenschmidt case USB_DT_DEVICE:
2917ecca2a4SBenjamin Herrenschmidt dsize = USB_DT_DEVICE_SIZE;
2926dbf05fcSTao Ren memcpy(ep->buf, &vhub->vhub_dev_desc, dsize);
2936dbf05fcSTao Ren BUILD_BUG_ON(dsize > sizeof(vhub->vhub_dev_desc));
2947ecca2a4SBenjamin Herrenschmidt BUILD_BUG_ON(USB_DT_DEVICE_SIZE >= AST_VHUB_EP0_MAX_PACKET);
2957ecca2a4SBenjamin Herrenschmidt break;
296347f3f54SNeal Liu case USB_DT_OTHER_SPEED_CONFIG:
2977ecca2a4SBenjamin Herrenschmidt case USB_DT_CONFIG:
2987ecca2a4SBenjamin Herrenschmidt dsize = AST_VHUB_CONF_DESC_SIZE;
2996dbf05fcSTao Ren memcpy(ep->buf, &vhub->vhub_conf_desc, dsize);
300347f3f54SNeal Liu ((u8 *)ep->buf)[1] = desc_type;
3016dbf05fcSTao Ren BUILD_BUG_ON(dsize > sizeof(vhub->vhub_conf_desc));
3027ecca2a4SBenjamin Herrenschmidt BUILD_BUG_ON(AST_VHUB_CONF_DESC_SIZE >= AST_VHUB_EP0_MAX_PACKET);
3037ecca2a4SBenjamin Herrenschmidt break;
3047ecca2a4SBenjamin Herrenschmidt case USB_DT_HUB:
3057ecca2a4SBenjamin Herrenschmidt dsize = AST_VHUB_HUB_DESC_SIZE;
3066dbf05fcSTao Ren memcpy(ep->buf, &vhub->vhub_hub_desc, dsize);
3076dbf05fcSTao Ren BUILD_BUG_ON(dsize > sizeof(vhub->vhub_hub_desc));
3087ecca2a4SBenjamin Herrenschmidt BUILD_BUG_ON(AST_VHUB_HUB_DESC_SIZE >= AST_VHUB_EP0_MAX_PACKET);
3097ecca2a4SBenjamin Herrenschmidt break;
310347f3f54SNeal Liu case USB_DT_DEVICE_QUALIFIER:
311347f3f54SNeal Liu dsize = sizeof(vhub->vhub_qual_desc);
312347f3f54SNeal Liu memcpy(ep->buf, &vhub->vhub_qual_desc, dsize);
313347f3f54SNeal Liu break;
3147ecca2a4SBenjamin Herrenschmidt default:
3157ecca2a4SBenjamin Herrenschmidt return std_req_stall;
3167ecca2a4SBenjamin Herrenschmidt }
3177ecca2a4SBenjamin Herrenschmidt
3187ecca2a4SBenjamin Herrenschmidt /* Crop requested length */
3197ecca2a4SBenjamin Herrenschmidt if (len > dsize)
3207ecca2a4SBenjamin Herrenschmidt len = dsize;
3217ecca2a4SBenjamin Herrenschmidt
3227ecca2a4SBenjamin Herrenschmidt /* Shoot it from the EP buffer */
3237ecca2a4SBenjamin Herrenschmidt return ast_vhub_reply(ep, NULL, len);
3247ecca2a4SBenjamin Herrenschmidt }
3257ecca2a4SBenjamin Herrenschmidt
3265cc0710fSTao Ren static struct usb_gadget_strings*
ast_vhub_str_of_container(struct usb_gadget_string_container * container)3275cc0710fSTao Ren ast_vhub_str_of_container(struct usb_gadget_string_container *container)
3285cc0710fSTao Ren {
3295cc0710fSTao Ren return (struct usb_gadget_strings *)container->stash;
3305cc0710fSTao Ren }
3315cc0710fSTao Ren
ast_vhub_collect_languages(struct ast_vhub * vhub,void * buf,size_t size)3325cc0710fSTao Ren static int ast_vhub_collect_languages(struct ast_vhub *vhub, void *buf,
3335cc0710fSTao Ren size_t size)
3345cc0710fSTao Ren {
3355cc0710fSTao Ren int rc, hdr_len, nlangs, max_langs;
3365cc0710fSTao Ren struct usb_gadget_strings *lang_str;
3375cc0710fSTao Ren struct usb_gadget_string_container *container;
3385cc0710fSTao Ren struct usb_string_descriptor *sdesc = buf;
3395cc0710fSTao Ren
3405cc0710fSTao Ren nlangs = 0;
3415cc0710fSTao Ren hdr_len = sizeof(struct usb_descriptor_header);
3425cc0710fSTao Ren max_langs = (size - hdr_len) / sizeof(sdesc->wData[0]);
3435cc0710fSTao Ren list_for_each_entry(container, &vhub->vhub_str_desc, list) {
3445cc0710fSTao Ren if (nlangs >= max_langs)
3455cc0710fSTao Ren break;
3465cc0710fSTao Ren
3475cc0710fSTao Ren lang_str = ast_vhub_str_of_container(container);
3485cc0710fSTao Ren sdesc->wData[nlangs++] = cpu_to_le16(lang_str->language);
3495cc0710fSTao Ren }
3505cc0710fSTao Ren
3515cc0710fSTao Ren rc = hdr_len + nlangs * sizeof(sdesc->wData[0]);
3525cc0710fSTao Ren sdesc->bLength = rc;
3535cc0710fSTao Ren sdesc->bDescriptorType = USB_DT_STRING;
3545cc0710fSTao Ren
3555cc0710fSTao Ren return rc;
3565cc0710fSTao Ren }
3575cc0710fSTao Ren
ast_vhub_lookup_string(struct ast_vhub * vhub,u16 lang_id)3585cc0710fSTao Ren static struct usb_gadget_strings *ast_vhub_lookup_string(struct ast_vhub *vhub,
3595cc0710fSTao Ren u16 lang_id)
3605cc0710fSTao Ren {
3615cc0710fSTao Ren struct usb_gadget_strings *lang_str;
3625cc0710fSTao Ren struct usb_gadget_string_container *container;
3635cc0710fSTao Ren
3645cc0710fSTao Ren list_for_each_entry(container, &vhub->vhub_str_desc, list) {
3655cc0710fSTao Ren lang_str = ast_vhub_str_of_container(container);
3665cc0710fSTao Ren if (lang_str->language == lang_id)
3675cc0710fSTao Ren return lang_str;
3685cc0710fSTao Ren }
3695cc0710fSTao Ren
3705cc0710fSTao Ren return NULL;
3715cc0710fSTao Ren }
3725cc0710fSTao Ren
ast_vhub_rep_string(struct ast_vhub_ep * ep,u8 string_id,u16 lang_id,u16 len)3737ecca2a4SBenjamin Herrenschmidt static int ast_vhub_rep_string(struct ast_vhub_ep *ep,
3747ecca2a4SBenjamin Herrenschmidt u8 string_id, u16 lang_id,
3757ecca2a4SBenjamin Herrenschmidt u16 len)
3767ecca2a4SBenjamin Herrenschmidt {
3775cc0710fSTao Ren int rc;
3785cc0710fSTao Ren u8 buf[256];
3795cc0710fSTao Ren struct ast_vhub *vhub = ep->vhub;
3805cc0710fSTao Ren struct usb_gadget_strings *lang_str;
3817ecca2a4SBenjamin Herrenschmidt
3825cc0710fSTao Ren if (string_id == 0) {
3835cc0710fSTao Ren rc = ast_vhub_collect_languages(vhub, buf, sizeof(buf));
3845cc0710fSTao Ren } else {
3855cc0710fSTao Ren lang_str = ast_vhub_lookup_string(vhub, lang_id);
3865cc0710fSTao Ren if (!lang_str)
3875cc0710fSTao Ren return std_req_stall;
3887ecca2a4SBenjamin Herrenschmidt
3895cc0710fSTao Ren rc = usb_gadget_get_string(lang_str, string_id, buf);
3905cc0710fSTao Ren }
3915cc0710fSTao Ren
3925cc0710fSTao Ren if (rc < 0 || rc >= AST_VHUB_EP0_MAX_PACKET)
3937ecca2a4SBenjamin Herrenschmidt return std_req_stall;
3947ecca2a4SBenjamin Herrenschmidt
3957ecca2a4SBenjamin Herrenschmidt /* Shoot it from the EP buffer */
3965cc0710fSTao Ren memcpy(ep->buf, buf, rc);
3977ecca2a4SBenjamin Herrenschmidt return ast_vhub_reply(ep, NULL, min_t(u16, rc, len));
3987ecca2a4SBenjamin Herrenschmidt }
3997ecca2a4SBenjamin Herrenschmidt
ast_vhub_std_hub_request(struct ast_vhub_ep * ep,struct usb_ctrlrequest * crq)4007ecca2a4SBenjamin Herrenschmidt enum std_req_rc ast_vhub_std_hub_request(struct ast_vhub_ep *ep,
4017ecca2a4SBenjamin Herrenschmidt struct usb_ctrlrequest *crq)
4027ecca2a4SBenjamin Herrenschmidt {
4037ecca2a4SBenjamin Herrenschmidt struct ast_vhub *vhub = ep->vhub;
4047ecca2a4SBenjamin Herrenschmidt u16 wValue, wIndex, wLength;
4057ecca2a4SBenjamin Herrenschmidt
4067ecca2a4SBenjamin Herrenschmidt wValue = le16_to_cpu(crq->wValue);
4077ecca2a4SBenjamin Herrenschmidt wIndex = le16_to_cpu(crq->wIndex);
4087ecca2a4SBenjamin Herrenschmidt wLength = le16_to_cpu(crq->wLength);
4097ecca2a4SBenjamin Herrenschmidt
4107ecca2a4SBenjamin Herrenschmidt /* First packet, grab speed */
4117ecca2a4SBenjamin Herrenschmidt if (vhub->speed == USB_SPEED_UNKNOWN) {
4127ecca2a4SBenjamin Herrenschmidt u32 ustat = readl(vhub->regs + AST_VHUB_USBSTS);
4137ecca2a4SBenjamin Herrenschmidt if (ustat & VHUB_USBSTS_HISPEED)
4147ecca2a4SBenjamin Herrenschmidt vhub->speed = USB_SPEED_HIGH;
4157ecca2a4SBenjamin Herrenschmidt else
4167ecca2a4SBenjamin Herrenschmidt vhub->speed = USB_SPEED_FULL;
4177ecca2a4SBenjamin Herrenschmidt UDCDBG(vhub, "USB status=%08x speed=%s\n", ustat,
4187ecca2a4SBenjamin Herrenschmidt vhub->speed == USB_SPEED_HIGH ? "high" : "full");
4197ecca2a4SBenjamin Herrenschmidt }
4207ecca2a4SBenjamin Herrenschmidt
4217ecca2a4SBenjamin Herrenschmidt switch ((crq->bRequestType << 8) | crq->bRequest) {
4227ecca2a4SBenjamin Herrenschmidt /* SET_ADDRESS */
4237ecca2a4SBenjamin Herrenschmidt case DeviceOutRequest | USB_REQ_SET_ADDRESS:
4247ecca2a4SBenjamin Herrenschmidt EPDBG(ep, "SET_ADDRESS: Got address %x\n", wValue);
4257ecca2a4SBenjamin Herrenschmidt writel(wValue, vhub->regs + AST_VHUB_CONF);
4267ecca2a4SBenjamin Herrenschmidt return std_req_complete;
4277ecca2a4SBenjamin Herrenschmidt
4287ecca2a4SBenjamin Herrenschmidt /* GET_STATUS */
4297ecca2a4SBenjamin Herrenschmidt case DeviceRequest | USB_REQ_GET_STATUS:
4307ecca2a4SBenjamin Herrenschmidt return ast_vhub_hub_dev_status(ep, wIndex, wValue);
4317ecca2a4SBenjamin Herrenschmidt case InterfaceRequest | USB_REQ_GET_STATUS:
4327ecca2a4SBenjamin Herrenschmidt return ast_vhub_simple_reply(ep, 0, 0);
4337ecca2a4SBenjamin Herrenschmidt case EndpointRequest | USB_REQ_GET_STATUS:
4347ecca2a4SBenjamin Herrenschmidt return ast_vhub_hub_ep_status(ep, wIndex, wValue);
4357ecca2a4SBenjamin Herrenschmidt
4367ecca2a4SBenjamin Herrenschmidt /* SET/CLEAR_FEATURE */
4377ecca2a4SBenjamin Herrenschmidt case DeviceOutRequest | USB_REQ_SET_FEATURE:
4387ecca2a4SBenjamin Herrenschmidt return ast_vhub_hub_dev_feature(ep, wIndex, wValue, true);
4397ecca2a4SBenjamin Herrenschmidt case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
4407ecca2a4SBenjamin Herrenschmidt return ast_vhub_hub_dev_feature(ep, wIndex, wValue, false);
4417ecca2a4SBenjamin Herrenschmidt case EndpointOutRequest | USB_REQ_SET_FEATURE:
4427ecca2a4SBenjamin Herrenschmidt return ast_vhub_hub_ep_feature(ep, wIndex, wValue, true);
4437ecca2a4SBenjamin Herrenschmidt case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
4447ecca2a4SBenjamin Herrenschmidt return ast_vhub_hub_ep_feature(ep, wIndex, wValue, false);
4457ecca2a4SBenjamin Herrenschmidt
4467ecca2a4SBenjamin Herrenschmidt /* GET/SET_CONFIGURATION */
4477ecca2a4SBenjamin Herrenschmidt case DeviceRequest | USB_REQ_GET_CONFIGURATION:
4487ecca2a4SBenjamin Herrenschmidt return ast_vhub_simple_reply(ep, 1);
4497ecca2a4SBenjamin Herrenschmidt case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
4507ecca2a4SBenjamin Herrenschmidt if (wValue != 1)
4517ecca2a4SBenjamin Herrenschmidt return std_req_stall;
4527ecca2a4SBenjamin Herrenschmidt return std_req_complete;
4537ecca2a4SBenjamin Herrenschmidt
4547ecca2a4SBenjamin Herrenschmidt /* GET_DESCRIPTOR */
4557ecca2a4SBenjamin Herrenschmidt case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
4567ecca2a4SBenjamin Herrenschmidt switch (wValue >> 8) {
4577ecca2a4SBenjamin Herrenschmidt case USB_DT_DEVICE:
4587ecca2a4SBenjamin Herrenschmidt case USB_DT_CONFIG:
459347f3f54SNeal Liu case USB_DT_DEVICE_QUALIFIER:
460347f3f54SNeal Liu case USB_DT_OTHER_SPEED_CONFIG:
4617ecca2a4SBenjamin Herrenschmidt return ast_vhub_rep_desc(ep, wValue >> 8,
4627ecca2a4SBenjamin Herrenschmidt wLength);
4637ecca2a4SBenjamin Herrenschmidt case USB_DT_STRING:
4647ecca2a4SBenjamin Herrenschmidt return ast_vhub_rep_string(ep, wValue & 0xff,
4657ecca2a4SBenjamin Herrenschmidt wIndex, wLength);
4667ecca2a4SBenjamin Herrenschmidt }
4677ecca2a4SBenjamin Herrenschmidt return std_req_stall;
4687ecca2a4SBenjamin Herrenschmidt
4697ecca2a4SBenjamin Herrenschmidt /* GET/SET_INTERFACE */
4707ecca2a4SBenjamin Herrenschmidt case DeviceRequest | USB_REQ_GET_INTERFACE:
4717ecca2a4SBenjamin Herrenschmidt return ast_vhub_simple_reply(ep, 0);
4727ecca2a4SBenjamin Herrenschmidt case DeviceOutRequest | USB_REQ_SET_INTERFACE:
4737ecca2a4SBenjamin Herrenschmidt if (wValue != 0 || wIndex != 0)
4747ecca2a4SBenjamin Herrenschmidt return std_req_stall;
4757ecca2a4SBenjamin Herrenschmidt return std_req_complete;
4767ecca2a4SBenjamin Herrenschmidt }
4777ecca2a4SBenjamin Herrenschmidt return std_req_stall;
4787ecca2a4SBenjamin Herrenschmidt }
4797ecca2a4SBenjamin Herrenschmidt
ast_vhub_update_hub_ep1(struct ast_vhub * vhub,unsigned int port)4807ecca2a4SBenjamin Herrenschmidt static void ast_vhub_update_hub_ep1(struct ast_vhub *vhub,
4817ecca2a4SBenjamin Herrenschmidt unsigned int port)
4827ecca2a4SBenjamin Herrenschmidt {
4837ecca2a4SBenjamin Herrenschmidt /* Update HW EP1 response */
4847ecca2a4SBenjamin Herrenschmidt u32 reg = readl(vhub->regs + AST_VHUB_EP1_STS_CHG);
4857ecca2a4SBenjamin Herrenschmidt u32 pmask = (1 << (port + 1));
4867ecca2a4SBenjamin Herrenschmidt if (vhub->ports[port].change)
4877ecca2a4SBenjamin Herrenschmidt reg |= pmask;
4887ecca2a4SBenjamin Herrenschmidt else
4897ecca2a4SBenjamin Herrenschmidt reg &= ~pmask;
4907ecca2a4SBenjamin Herrenschmidt writel(reg, vhub->regs + AST_VHUB_EP1_STS_CHG);
4917ecca2a4SBenjamin Herrenschmidt }
4927ecca2a4SBenjamin Herrenschmidt
ast_vhub_change_port_stat(struct ast_vhub * vhub,unsigned int port,u16 clr_flags,u16 set_flags,bool set_c)4937ecca2a4SBenjamin Herrenschmidt static void ast_vhub_change_port_stat(struct ast_vhub *vhub,
4947ecca2a4SBenjamin Herrenschmidt unsigned int port,
4957ecca2a4SBenjamin Herrenschmidt u16 clr_flags,
4967ecca2a4SBenjamin Herrenschmidt u16 set_flags,
4977ecca2a4SBenjamin Herrenschmidt bool set_c)
4987ecca2a4SBenjamin Herrenschmidt {
4997ecca2a4SBenjamin Herrenschmidt struct ast_vhub_port *p = &vhub->ports[port];
5007ecca2a4SBenjamin Herrenschmidt u16 prev;
5017ecca2a4SBenjamin Herrenschmidt
5027ecca2a4SBenjamin Herrenschmidt /* Update port status */
5037ecca2a4SBenjamin Herrenschmidt prev = p->status;
5047ecca2a4SBenjamin Herrenschmidt p->status = (prev & ~clr_flags) | set_flags;
5057ecca2a4SBenjamin Herrenschmidt DDBG(&p->dev, "port %d status %04x -> %04x (C=%d)\n",
5067ecca2a4SBenjamin Herrenschmidt port + 1, prev, p->status, set_c);
5077ecca2a4SBenjamin Herrenschmidt
5087ecca2a4SBenjamin Herrenschmidt /* Update change bits if needed */
5097ecca2a4SBenjamin Herrenschmidt if (set_c) {
5107ecca2a4SBenjamin Herrenschmidt u16 chg = p->status ^ prev;
5117ecca2a4SBenjamin Herrenschmidt
5127ecca2a4SBenjamin Herrenschmidt /* Only these are relevant for change */
5137ecca2a4SBenjamin Herrenschmidt chg &= USB_PORT_STAT_C_CONNECTION |
5147ecca2a4SBenjamin Herrenschmidt USB_PORT_STAT_C_ENABLE |
5157ecca2a4SBenjamin Herrenschmidt USB_PORT_STAT_C_SUSPEND |
5167ecca2a4SBenjamin Herrenschmidt USB_PORT_STAT_C_OVERCURRENT |
5177ecca2a4SBenjamin Herrenschmidt USB_PORT_STAT_C_RESET |
5187ecca2a4SBenjamin Herrenschmidt USB_PORT_STAT_C_L1;
5197ecca2a4SBenjamin Herrenschmidt
5209fbbeb4eSBenjamin Herrenschmidt /*
5219fbbeb4eSBenjamin Herrenschmidt * We only set USB_PORT_STAT_C_ENABLE if we are disabling
5229fbbeb4eSBenjamin Herrenschmidt * the port as per USB spec, otherwise MacOS gets upset
5239fbbeb4eSBenjamin Herrenschmidt */
5249fbbeb4eSBenjamin Herrenschmidt if (p->status & USB_PORT_STAT_ENABLE)
5259fbbeb4eSBenjamin Herrenschmidt chg &= ~USB_PORT_STAT_C_ENABLE;
5269fbbeb4eSBenjamin Herrenschmidt
5279fbbeb4eSBenjamin Herrenschmidt p->change = chg;
5287ecca2a4SBenjamin Herrenschmidt ast_vhub_update_hub_ep1(vhub, port);
5297ecca2a4SBenjamin Herrenschmidt }
5307ecca2a4SBenjamin Herrenschmidt }
5317ecca2a4SBenjamin Herrenschmidt
ast_vhub_send_host_wakeup(struct ast_vhub * vhub)5327ecca2a4SBenjamin Herrenschmidt static void ast_vhub_send_host_wakeup(struct ast_vhub *vhub)
5337ecca2a4SBenjamin Herrenschmidt {
5347ecca2a4SBenjamin Herrenschmidt u32 reg = readl(vhub->regs + AST_VHUB_CTRL);
5357ecca2a4SBenjamin Herrenschmidt UDCDBG(vhub, "Waking up host !\n");
5367ecca2a4SBenjamin Herrenschmidt reg |= VHUB_CTRL_MANUAL_REMOTE_WAKEUP;
5377ecca2a4SBenjamin Herrenschmidt writel(reg, vhub->regs + AST_VHUB_CTRL);
5387ecca2a4SBenjamin Herrenschmidt }
5397ecca2a4SBenjamin Herrenschmidt
ast_vhub_device_connect(struct ast_vhub * vhub,unsigned int port,bool on)5407ecca2a4SBenjamin Herrenschmidt void ast_vhub_device_connect(struct ast_vhub *vhub,
5417ecca2a4SBenjamin Herrenschmidt unsigned int port, bool on)
5427ecca2a4SBenjamin Herrenschmidt {
5437ecca2a4SBenjamin Herrenschmidt if (on)
5447ecca2a4SBenjamin Herrenschmidt ast_vhub_change_port_stat(vhub, port, 0,
5457ecca2a4SBenjamin Herrenschmidt USB_PORT_STAT_CONNECTION, true);
5467ecca2a4SBenjamin Herrenschmidt else
5477ecca2a4SBenjamin Herrenschmidt ast_vhub_change_port_stat(vhub, port,
5487ecca2a4SBenjamin Herrenschmidt USB_PORT_STAT_CONNECTION |
5497ecca2a4SBenjamin Herrenschmidt USB_PORT_STAT_ENABLE,
5507ecca2a4SBenjamin Herrenschmidt 0, true);
5517ecca2a4SBenjamin Herrenschmidt
5527ecca2a4SBenjamin Herrenschmidt /*
5537ecca2a4SBenjamin Herrenschmidt * If the hub is set to wakup the host on connection events
5547ecca2a4SBenjamin Herrenschmidt * then send a wakeup.
5557ecca2a4SBenjamin Herrenschmidt */
5567ecca2a4SBenjamin Herrenschmidt if (vhub->wakeup_en)
5577ecca2a4SBenjamin Herrenschmidt ast_vhub_send_host_wakeup(vhub);
5587ecca2a4SBenjamin Herrenschmidt }
5597ecca2a4SBenjamin Herrenschmidt
ast_vhub_wake_work(struct work_struct * work)5607ecca2a4SBenjamin Herrenschmidt static void ast_vhub_wake_work(struct work_struct *work)
5617ecca2a4SBenjamin Herrenschmidt {
5627ecca2a4SBenjamin Herrenschmidt struct ast_vhub *vhub = container_of(work,
5637ecca2a4SBenjamin Herrenschmidt struct ast_vhub,
5647ecca2a4SBenjamin Herrenschmidt wake_work);
5657ecca2a4SBenjamin Herrenschmidt unsigned long flags;
5667ecca2a4SBenjamin Herrenschmidt unsigned int i;
5677ecca2a4SBenjamin Herrenschmidt
5687ecca2a4SBenjamin Herrenschmidt /*
5697ecca2a4SBenjamin Herrenschmidt * Wake all sleeping ports. If a port is suspended by
5707ecca2a4SBenjamin Herrenschmidt * the host suspend (without explicit state suspend),
5717ecca2a4SBenjamin Herrenschmidt * we let the normal host wake path deal with it later.
5727ecca2a4SBenjamin Herrenschmidt */
5737ecca2a4SBenjamin Herrenschmidt spin_lock_irqsave(&vhub->lock, flags);
574487bc828STao Ren for (i = 0; i < vhub->max_ports; i++) {
5757ecca2a4SBenjamin Herrenschmidt struct ast_vhub_port *p = &vhub->ports[i];
5767ecca2a4SBenjamin Herrenschmidt
5777ecca2a4SBenjamin Herrenschmidt if (!(p->status & USB_PORT_STAT_SUSPEND))
5787ecca2a4SBenjamin Herrenschmidt continue;
5797ecca2a4SBenjamin Herrenschmidt ast_vhub_change_port_stat(vhub, i,
5807ecca2a4SBenjamin Herrenschmidt USB_PORT_STAT_SUSPEND,
5817ecca2a4SBenjamin Herrenschmidt 0, true);
5827ecca2a4SBenjamin Herrenschmidt ast_vhub_dev_resume(&p->dev);
5837ecca2a4SBenjamin Herrenschmidt }
5847ecca2a4SBenjamin Herrenschmidt ast_vhub_send_host_wakeup(vhub);
5857ecca2a4SBenjamin Herrenschmidt spin_unlock_irqrestore(&vhub->lock, flags);
5867ecca2a4SBenjamin Herrenschmidt }
5877ecca2a4SBenjamin Herrenschmidt
ast_vhub_hub_wake_all(struct ast_vhub * vhub)5887ecca2a4SBenjamin Herrenschmidt void ast_vhub_hub_wake_all(struct ast_vhub *vhub)
5897ecca2a4SBenjamin Herrenschmidt {
5907ecca2a4SBenjamin Herrenschmidt /*
5917ecca2a4SBenjamin Herrenschmidt * A device is trying to wake the world, because this
5927ecca2a4SBenjamin Herrenschmidt * can recurse into the device, we break the call chain
5937ecca2a4SBenjamin Herrenschmidt * using a work queue
5947ecca2a4SBenjamin Herrenschmidt */
5957ecca2a4SBenjamin Herrenschmidt schedule_work(&vhub->wake_work);
5967ecca2a4SBenjamin Herrenschmidt }
5977ecca2a4SBenjamin Herrenschmidt
ast_vhub_port_reset(struct ast_vhub * vhub,u8 port)5987ecca2a4SBenjamin Herrenschmidt static void ast_vhub_port_reset(struct ast_vhub *vhub, u8 port)
5997ecca2a4SBenjamin Herrenschmidt {
6007ecca2a4SBenjamin Herrenschmidt struct ast_vhub_port *p = &vhub->ports[port];
6017ecca2a4SBenjamin Herrenschmidt u16 set, clr, speed;
6027ecca2a4SBenjamin Herrenschmidt
6037ecca2a4SBenjamin Herrenschmidt /* First mark disabled */
6047ecca2a4SBenjamin Herrenschmidt ast_vhub_change_port_stat(vhub, port,
6057ecca2a4SBenjamin Herrenschmidt USB_PORT_STAT_ENABLE |
6067ecca2a4SBenjamin Herrenschmidt USB_PORT_STAT_SUSPEND,
6077ecca2a4SBenjamin Herrenschmidt USB_PORT_STAT_RESET,
6087ecca2a4SBenjamin Herrenschmidt false);
6097ecca2a4SBenjamin Herrenschmidt
6107ecca2a4SBenjamin Herrenschmidt if (!p->dev.driver)
6117ecca2a4SBenjamin Herrenschmidt return;
6127ecca2a4SBenjamin Herrenschmidt
6137ecca2a4SBenjamin Herrenschmidt /*
6147ecca2a4SBenjamin Herrenschmidt * This will either "start" the port or reset the
6157ecca2a4SBenjamin Herrenschmidt * device if already started...
6167ecca2a4SBenjamin Herrenschmidt */
6177ecca2a4SBenjamin Herrenschmidt ast_vhub_dev_reset(&p->dev);
6187ecca2a4SBenjamin Herrenschmidt
6197ecca2a4SBenjamin Herrenschmidt /* Grab the right speed */
6207ecca2a4SBenjamin Herrenschmidt speed = p->dev.driver->max_speed;
6217ecca2a4SBenjamin Herrenschmidt if (speed == USB_SPEED_UNKNOWN || speed > vhub->speed)
6227ecca2a4SBenjamin Herrenschmidt speed = vhub->speed;
6237ecca2a4SBenjamin Herrenschmidt
6247ecca2a4SBenjamin Herrenschmidt switch (speed) {
6257ecca2a4SBenjamin Herrenschmidt case USB_SPEED_LOW:
6267ecca2a4SBenjamin Herrenschmidt set = USB_PORT_STAT_LOW_SPEED;
6277ecca2a4SBenjamin Herrenschmidt clr = USB_PORT_STAT_HIGH_SPEED;
6287ecca2a4SBenjamin Herrenschmidt break;
6297ecca2a4SBenjamin Herrenschmidt case USB_SPEED_FULL:
6307ecca2a4SBenjamin Herrenschmidt set = 0;
6317ecca2a4SBenjamin Herrenschmidt clr = USB_PORT_STAT_LOW_SPEED |
6327ecca2a4SBenjamin Herrenschmidt USB_PORT_STAT_HIGH_SPEED;
6337ecca2a4SBenjamin Herrenschmidt break;
6347ecca2a4SBenjamin Herrenschmidt case USB_SPEED_HIGH:
6357ecca2a4SBenjamin Herrenschmidt set = USB_PORT_STAT_HIGH_SPEED;
6367ecca2a4SBenjamin Herrenschmidt clr = USB_PORT_STAT_LOW_SPEED;
6377ecca2a4SBenjamin Herrenschmidt break;
6387ecca2a4SBenjamin Herrenschmidt default:
6397ecca2a4SBenjamin Herrenschmidt UDCDBG(vhub, "Unsupported speed %d when"
6407ecca2a4SBenjamin Herrenschmidt " connecting device\n",
6417ecca2a4SBenjamin Herrenschmidt speed);
6427ecca2a4SBenjamin Herrenschmidt return;
6437ecca2a4SBenjamin Herrenschmidt }
6447ecca2a4SBenjamin Herrenschmidt clr |= USB_PORT_STAT_RESET;
6457ecca2a4SBenjamin Herrenschmidt set |= USB_PORT_STAT_ENABLE;
6467ecca2a4SBenjamin Herrenschmidt
6477ecca2a4SBenjamin Herrenschmidt /* This should ideally be delayed ... */
6487ecca2a4SBenjamin Herrenschmidt ast_vhub_change_port_stat(vhub, port, clr, set, true);
6497ecca2a4SBenjamin Herrenschmidt }
6507ecca2a4SBenjamin Herrenschmidt
ast_vhub_set_port_feature(struct ast_vhub_ep * ep,u8 port,u16 feat)6517ecca2a4SBenjamin Herrenschmidt static enum std_req_rc ast_vhub_set_port_feature(struct ast_vhub_ep *ep,
6527ecca2a4SBenjamin Herrenschmidt u8 port, u16 feat)
6537ecca2a4SBenjamin Herrenschmidt {
6547ecca2a4SBenjamin Herrenschmidt struct ast_vhub *vhub = ep->vhub;
6557ecca2a4SBenjamin Herrenschmidt struct ast_vhub_port *p;
6567ecca2a4SBenjamin Herrenschmidt
657487bc828STao Ren if (port == 0 || port > vhub->max_ports)
6587ecca2a4SBenjamin Herrenschmidt return std_req_stall;
6597ecca2a4SBenjamin Herrenschmidt port--;
6607ecca2a4SBenjamin Herrenschmidt p = &vhub->ports[port];
6617ecca2a4SBenjamin Herrenschmidt
6627ecca2a4SBenjamin Herrenschmidt switch(feat) {
6637ecca2a4SBenjamin Herrenschmidt case USB_PORT_FEAT_SUSPEND:
6647ecca2a4SBenjamin Herrenschmidt if (!(p->status & USB_PORT_STAT_ENABLE))
6657ecca2a4SBenjamin Herrenschmidt return std_req_complete;
6667ecca2a4SBenjamin Herrenschmidt ast_vhub_change_port_stat(vhub, port,
6677ecca2a4SBenjamin Herrenschmidt 0, USB_PORT_STAT_SUSPEND,
6687ecca2a4SBenjamin Herrenschmidt false);
6697ecca2a4SBenjamin Herrenschmidt ast_vhub_dev_suspend(&p->dev);
6707ecca2a4SBenjamin Herrenschmidt return std_req_complete;
6717ecca2a4SBenjamin Herrenschmidt case USB_PORT_FEAT_RESET:
6727ecca2a4SBenjamin Herrenschmidt EPDBG(ep, "Port reset !\n");
6737ecca2a4SBenjamin Herrenschmidt ast_vhub_port_reset(vhub, port);
6747ecca2a4SBenjamin Herrenschmidt return std_req_complete;
6757ecca2a4SBenjamin Herrenschmidt case USB_PORT_FEAT_POWER:
6767ecca2a4SBenjamin Herrenschmidt /*
6777ecca2a4SBenjamin Herrenschmidt * On Power-on, we mark the connected flag changed,
6787ecca2a4SBenjamin Herrenschmidt * if there's a connected device, some hosts will
6797ecca2a4SBenjamin Herrenschmidt * otherwise fail to detect it.
6807ecca2a4SBenjamin Herrenschmidt */
6817ecca2a4SBenjamin Herrenschmidt if (p->status & USB_PORT_STAT_CONNECTION) {
6827ecca2a4SBenjamin Herrenschmidt p->change |= USB_PORT_STAT_C_CONNECTION;
6837ecca2a4SBenjamin Herrenschmidt ast_vhub_update_hub_ep1(vhub, port);
6847ecca2a4SBenjamin Herrenschmidt }
6857ecca2a4SBenjamin Herrenschmidt return std_req_complete;
6867ecca2a4SBenjamin Herrenschmidt case USB_PORT_FEAT_TEST:
6877ecca2a4SBenjamin Herrenschmidt case USB_PORT_FEAT_INDICATOR:
6887ecca2a4SBenjamin Herrenschmidt /* We don't do anything with these */
6897ecca2a4SBenjamin Herrenschmidt return std_req_complete;
6907ecca2a4SBenjamin Herrenschmidt }
6917ecca2a4SBenjamin Herrenschmidt return std_req_stall;
6927ecca2a4SBenjamin Herrenschmidt }
6937ecca2a4SBenjamin Herrenschmidt
ast_vhub_clr_port_feature(struct ast_vhub_ep * ep,u8 port,u16 feat)6947ecca2a4SBenjamin Herrenschmidt static enum std_req_rc ast_vhub_clr_port_feature(struct ast_vhub_ep *ep,
6957ecca2a4SBenjamin Herrenschmidt u8 port, u16 feat)
6967ecca2a4SBenjamin Herrenschmidt {
6977ecca2a4SBenjamin Herrenschmidt struct ast_vhub *vhub = ep->vhub;
6987ecca2a4SBenjamin Herrenschmidt struct ast_vhub_port *p;
6997ecca2a4SBenjamin Herrenschmidt
700487bc828STao Ren if (port == 0 || port > vhub->max_ports)
7017ecca2a4SBenjamin Herrenschmidt return std_req_stall;
7027ecca2a4SBenjamin Herrenschmidt port--;
7037ecca2a4SBenjamin Herrenschmidt p = &vhub->ports[port];
7047ecca2a4SBenjamin Herrenschmidt
7057ecca2a4SBenjamin Herrenschmidt switch(feat) {
7067ecca2a4SBenjamin Herrenschmidt case USB_PORT_FEAT_ENABLE:
7077ecca2a4SBenjamin Herrenschmidt ast_vhub_change_port_stat(vhub, port,
7087ecca2a4SBenjamin Herrenschmidt USB_PORT_STAT_ENABLE |
7097ecca2a4SBenjamin Herrenschmidt USB_PORT_STAT_SUSPEND, 0,
7107ecca2a4SBenjamin Herrenschmidt false);
7117ecca2a4SBenjamin Herrenschmidt ast_vhub_dev_suspend(&p->dev);
7127ecca2a4SBenjamin Herrenschmidt return std_req_complete;
7137ecca2a4SBenjamin Herrenschmidt case USB_PORT_FEAT_SUSPEND:
7147ecca2a4SBenjamin Herrenschmidt if (!(p->status & USB_PORT_STAT_SUSPEND))
7157ecca2a4SBenjamin Herrenschmidt return std_req_complete;
7167ecca2a4SBenjamin Herrenschmidt ast_vhub_change_port_stat(vhub, port,
7177ecca2a4SBenjamin Herrenschmidt USB_PORT_STAT_SUSPEND, 0,
7187ecca2a4SBenjamin Herrenschmidt false);
7197ecca2a4SBenjamin Herrenschmidt ast_vhub_dev_resume(&p->dev);
7207ecca2a4SBenjamin Herrenschmidt return std_req_complete;
7217ecca2a4SBenjamin Herrenschmidt case USB_PORT_FEAT_POWER:
7227ecca2a4SBenjamin Herrenschmidt /* We don't do power control */
7237ecca2a4SBenjamin Herrenschmidt return std_req_complete;
7247ecca2a4SBenjamin Herrenschmidt case USB_PORT_FEAT_INDICATOR:
7257ecca2a4SBenjamin Herrenschmidt /* We don't have indicators */
7267ecca2a4SBenjamin Herrenschmidt return std_req_complete;
7277ecca2a4SBenjamin Herrenschmidt case USB_PORT_FEAT_C_CONNECTION:
7287ecca2a4SBenjamin Herrenschmidt case USB_PORT_FEAT_C_ENABLE:
7297ecca2a4SBenjamin Herrenschmidt case USB_PORT_FEAT_C_SUSPEND:
7307ecca2a4SBenjamin Herrenschmidt case USB_PORT_FEAT_C_OVER_CURRENT:
7317ecca2a4SBenjamin Herrenschmidt case USB_PORT_FEAT_C_RESET:
7327ecca2a4SBenjamin Herrenschmidt /* Clear state-change feature */
7337ecca2a4SBenjamin Herrenschmidt p->change &= ~(1u << (feat - 16));
7347ecca2a4SBenjamin Herrenschmidt ast_vhub_update_hub_ep1(vhub, port);
7357ecca2a4SBenjamin Herrenschmidt return std_req_complete;
7367ecca2a4SBenjamin Herrenschmidt }
7377ecca2a4SBenjamin Herrenschmidt return std_req_stall;
7387ecca2a4SBenjamin Herrenschmidt }
7397ecca2a4SBenjamin Herrenschmidt
ast_vhub_get_port_stat(struct ast_vhub_ep * ep,u8 port)7407ecca2a4SBenjamin Herrenschmidt static enum std_req_rc ast_vhub_get_port_stat(struct ast_vhub_ep *ep,
7417ecca2a4SBenjamin Herrenschmidt u8 port)
7427ecca2a4SBenjamin Herrenschmidt {
7437ecca2a4SBenjamin Herrenschmidt struct ast_vhub *vhub = ep->vhub;
7447ecca2a4SBenjamin Herrenschmidt u16 stat, chg;
7457ecca2a4SBenjamin Herrenschmidt
746487bc828STao Ren if (port == 0 || port > vhub->max_ports)
7477ecca2a4SBenjamin Herrenschmidt return std_req_stall;
7487ecca2a4SBenjamin Herrenschmidt port--;
7497ecca2a4SBenjamin Herrenschmidt
7507ecca2a4SBenjamin Herrenschmidt stat = vhub->ports[port].status;
7517ecca2a4SBenjamin Herrenschmidt chg = vhub->ports[port].change;
7527ecca2a4SBenjamin Herrenschmidt
7537ecca2a4SBenjamin Herrenschmidt /* We always have power */
7547ecca2a4SBenjamin Herrenschmidt stat |= USB_PORT_STAT_POWER;
7557ecca2a4SBenjamin Herrenschmidt
7567ecca2a4SBenjamin Herrenschmidt EPDBG(ep, " port status=%04x change=%04x\n", stat, chg);
7577ecca2a4SBenjamin Herrenschmidt
7587ecca2a4SBenjamin Herrenschmidt return ast_vhub_simple_reply(ep,
7597ecca2a4SBenjamin Herrenschmidt stat & 0xff,
7607ecca2a4SBenjamin Herrenschmidt stat >> 8,
7617ecca2a4SBenjamin Herrenschmidt chg & 0xff,
7627ecca2a4SBenjamin Herrenschmidt chg >> 8);
7637ecca2a4SBenjamin Herrenschmidt }
7647ecca2a4SBenjamin Herrenschmidt
ast_vhub_class_hub_request(struct ast_vhub_ep * ep,struct usb_ctrlrequest * crq)7657ecca2a4SBenjamin Herrenschmidt enum std_req_rc ast_vhub_class_hub_request(struct ast_vhub_ep *ep,
7667ecca2a4SBenjamin Herrenschmidt struct usb_ctrlrequest *crq)
7677ecca2a4SBenjamin Herrenschmidt {
7687ecca2a4SBenjamin Herrenschmidt u16 wValue, wIndex, wLength;
7697ecca2a4SBenjamin Herrenschmidt
7707ecca2a4SBenjamin Herrenschmidt wValue = le16_to_cpu(crq->wValue);
7717ecca2a4SBenjamin Herrenschmidt wIndex = le16_to_cpu(crq->wIndex);
7727ecca2a4SBenjamin Herrenschmidt wLength = le16_to_cpu(crq->wLength);
7737ecca2a4SBenjamin Herrenschmidt
7747ecca2a4SBenjamin Herrenschmidt switch ((crq->bRequestType << 8) | crq->bRequest) {
7757ecca2a4SBenjamin Herrenschmidt case GetHubStatus:
7767ecca2a4SBenjamin Herrenschmidt EPDBG(ep, "GetHubStatus\n");
7777ecca2a4SBenjamin Herrenschmidt return ast_vhub_simple_reply(ep, 0, 0, 0, 0);
7787ecca2a4SBenjamin Herrenschmidt case GetPortStatus:
7797ecca2a4SBenjamin Herrenschmidt EPDBG(ep, "GetPortStatus(%d)\n", wIndex & 0xff);
7807ecca2a4SBenjamin Herrenschmidt return ast_vhub_get_port_stat(ep, wIndex & 0xf);
7817ecca2a4SBenjamin Herrenschmidt case GetHubDescriptor:
7827ecca2a4SBenjamin Herrenschmidt if (wValue != (USB_DT_HUB << 8))
7837ecca2a4SBenjamin Herrenschmidt return std_req_stall;
7847ecca2a4SBenjamin Herrenschmidt EPDBG(ep, "GetHubDescriptor(%d)\n", wIndex & 0xff);
7857ecca2a4SBenjamin Herrenschmidt return ast_vhub_rep_desc(ep, USB_DT_HUB, wLength);
7867ecca2a4SBenjamin Herrenschmidt case SetHubFeature:
7877ecca2a4SBenjamin Herrenschmidt case ClearHubFeature:
7887ecca2a4SBenjamin Herrenschmidt EPDBG(ep, "Get/SetHubFeature(%d)\n", wValue);
7897ecca2a4SBenjamin Herrenschmidt /* No feature, just complete the requests */
7907ecca2a4SBenjamin Herrenschmidt if (wValue == C_HUB_LOCAL_POWER ||
7917ecca2a4SBenjamin Herrenschmidt wValue == C_HUB_OVER_CURRENT)
7927ecca2a4SBenjamin Herrenschmidt return std_req_complete;
7937ecca2a4SBenjamin Herrenschmidt return std_req_stall;
7947ecca2a4SBenjamin Herrenschmidt case SetPortFeature:
7957ecca2a4SBenjamin Herrenschmidt EPDBG(ep, "SetPortFeature(%d,%d)\n", wIndex & 0xf, wValue);
7967ecca2a4SBenjamin Herrenschmidt return ast_vhub_set_port_feature(ep, wIndex & 0xf, wValue);
7977ecca2a4SBenjamin Herrenschmidt case ClearPortFeature:
7987ecca2a4SBenjamin Herrenschmidt EPDBG(ep, "ClearPortFeature(%d,%d)\n", wIndex & 0xf, wValue);
7997ecca2a4SBenjamin Herrenschmidt return ast_vhub_clr_port_feature(ep, wIndex & 0xf, wValue);
800aa31332fSBenjamin Herrenschmidt case ClearTTBuffer:
801aa31332fSBenjamin Herrenschmidt case ResetTT:
802aa31332fSBenjamin Herrenschmidt case StopTT:
803aa31332fSBenjamin Herrenschmidt return std_req_complete;
804aa31332fSBenjamin Herrenschmidt case GetTTState:
805aa31332fSBenjamin Herrenschmidt return ast_vhub_simple_reply(ep, 0, 0, 0, 0);
8067ecca2a4SBenjamin Herrenschmidt default:
8077ecca2a4SBenjamin Herrenschmidt EPDBG(ep, "Unknown class request\n");
8087ecca2a4SBenjamin Herrenschmidt }
8097ecca2a4SBenjamin Herrenschmidt return std_req_stall;
8107ecca2a4SBenjamin Herrenschmidt }
8117ecca2a4SBenjamin Herrenschmidt
ast_vhub_hub_suspend(struct ast_vhub * vhub)8127ecca2a4SBenjamin Herrenschmidt void ast_vhub_hub_suspend(struct ast_vhub *vhub)
8137ecca2a4SBenjamin Herrenschmidt {
8147ecca2a4SBenjamin Herrenschmidt unsigned int i;
8157ecca2a4SBenjamin Herrenschmidt
8167ecca2a4SBenjamin Herrenschmidt UDCDBG(vhub, "USB bus suspend\n");
8177ecca2a4SBenjamin Herrenschmidt
8187ecca2a4SBenjamin Herrenschmidt if (vhub->suspended)
8197ecca2a4SBenjamin Herrenschmidt return;
8207ecca2a4SBenjamin Herrenschmidt
8217ecca2a4SBenjamin Herrenschmidt vhub->suspended = true;
8227ecca2a4SBenjamin Herrenschmidt
8237ecca2a4SBenjamin Herrenschmidt /*
8247ecca2a4SBenjamin Herrenschmidt * Forward to unsuspended ports without changing
8257ecca2a4SBenjamin Herrenschmidt * their connection status.
8267ecca2a4SBenjamin Herrenschmidt */
827487bc828STao Ren for (i = 0; i < vhub->max_ports; i++) {
8287ecca2a4SBenjamin Herrenschmidt struct ast_vhub_port *p = &vhub->ports[i];
8297ecca2a4SBenjamin Herrenschmidt
8307ecca2a4SBenjamin Herrenschmidt if (!(p->status & USB_PORT_STAT_SUSPEND))
8317ecca2a4SBenjamin Herrenschmidt ast_vhub_dev_suspend(&p->dev);
8327ecca2a4SBenjamin Herrenschmidt }
8337ecca2a4SBenjamin Herrenschmidt }
8347ecca2a4SBenjamin Herrenschmidt
ast_vhub_hub_resume(struct ast_vhub * vhub)8357ecca2a4SBenjamin Herrenschmidt void ast_vhub_hub_resume(struct ast_vhub *vhub)
8367ecca2a4SBenjamin Herrenschmidt {
8377ecca2a4SBenjamin Herrenschmidt unsigned int i;
8387ecca2a4SBenjamin Herrenschmidt
8397ecca2a4SBenjamin Herrenschmidt UDCDBG(vhub, "USB bus resume\n");
8407ecca2a4SBenjamin Herrenschmidt
8417ecca2a4SBenjamin Herrenschmidt if (!vhub->suspended)
8427ecca2a4SBenjamin Herrenschmidt return;
8437ecca2a4SBenjamin Herrenschmidt
8447ecca2a4SBenjamin Herrenschmidt vhub->suspended = false;
8457ecca2a4SBenjamin Herrenschmidt
8467ecca2a4SBenjamin Herrenschmidt /*
8477ecca2a4SBenjamin Herrenschmidt * Forward to unsuspended ports without changing
8487ecca2a4SBenjamin Herrenschmidt * their connection status.
8497ecca2a4SBenjamin Herrenschmidt */
850487bc828STao Ren for (i = 0; i < vhub->max_ports; i++) {
8517ecca2a4SBenjamin Herrenschmidt struct ast_vhub_port *p = &vhub->ports[i];
8527ecca2a4SBenjamin Herrenschmidt
8537ecca2a4SBenjamin Herrenschmidt if (!(p->status & USB_PORT_STAT_SUSPEND))
8547ecca2a4SBenjamin Herrenschmidt ast_vhub_dev_resume(&p->dev);
8557ecca2a4SBenjamin Herrenschmidt }
8567ecca2a4SBenjamin Herrenschmidt }
8577ecca2a4SBenjamin Herrenschmidt
ast_vhub_hub_reset(struct ast_vhub * vhub)8587ecca2a4SBenjamin Herrenschmidt void ast_vhub_hub_reset(struct ast_vhub *vhub)
8597ecca2a4SBenjamin Herrenschmidt {
8607ecca2a4SBenjamin Herrenschmidt unsigned int i;
8617ecca2a4SBenjamin Herrenschmidt
8627ecca2a4SBenjamin Herrenschmidt UDCDBG(vhub, "USB bus reset\n");
8637ecca2a4SBenjamin Herrenschmidt
8647ecca2a4SBenjamin Herrenschmidt /*
8657ecca2a4SBenjamin Herrenschmidt * Is the speed known ? If not we don't care, we aren't
8667ecca2a4SBenjamin Herrenschmidt * initialized yet and ports haven't been enabled.
8677ecca2a4SBenjamin Herrenschmidt */
8687ecca2a4SBenjamin Herrenschmidt if (vhub->speed == USB_SPEED_UNKNOWN)
8697ecca2a4SBenjamin Herrenschmidt return;
8707ecca2a4SBenjamin Herrenschmidt
8717ecca2a4SBenjamin Herrenschmidt /* We aren't suspended anymore obviously */
8727ecca2a4SBenjamin Herrenschmidt vhub->suspended = false;
8737ecca2a4SBenjamin Herrenschmidt
8747ecca2a4SBenjamin Herrenschmidt /* No speed set */
8757ecca2a4SBenjamin Herrenschmidt vhub->speed = USB_SPEED_UNKNOWN;
8767ecca2a4SBenjamin Herrenschmidt
8777ecca2a4SBenjamin Herrenschmidt /* Wakeup not enabled anymore */
8787ecca2a4SBenjamin Herrenschmidt vhub->wakeup_en = false;
8797ecca2a4SBenjamin Herrenschmidt
8807ecca2a4SBenjamin Herrenschmidt /*
8817ecca2a4SBenjamin Herrenschmidt * Clear all port status, disable gadgets and "suspend"
8827ecca2a4SBenjamin Herrenschmidt * them. They will be woken up by a port reset.
8837ecca2a4SBenjamin Herrenschmidt */
884487bc828STao Ren for (i = 0; i < vhub->max_ports; i++) {
8857ecca2a4SBenjamin Herrenschmidt struct ast_vhub_port *p = &vhub->ports[i];
8867ecca2a4SBenjamin Herrenschmidt
8877ecca2a4SBenjamin Herrenschmidt /* Only keep the connected flag */
8887ecca2a4SBenjamin Herrenschmidt p->status &= USB_PORT_STAT_CONNECTION;
8897ecca2a4SBenjamin Herrenschmidt p->change = 0;
8907ecca2a4SBenjamin Herrenschmidt
8917ecca2a4SBenjamin Herrenschmidt /* Suspend the gadget if any */
8927ecca2a4SBenjamin Herrenschmidt ast_vhub_dev_suspend(&p->dev);
8937ecca2a4SBenjamin Herrenschmidt }
8947ecca2a4SBenjamin Herrenschmidt
8957ecca2a4SBenjamin Herrenschmidt /* Cleanup HW */
8967ecca2a4SBenjamin Herrenschmidt writel(0, vhub->regs + AST_VHUB_CONF);
8977ecca2a4SBenjamin Herrenschmidt writel(0, vhub->regs + AST_VHUB_EP0_CTRL);
8987ecca2a4SBenjamin Herrenschmidt writel(VHUB_EP1_CTRL_RESET_TOGGLE |
8997ecca2a4SBenjamin Herrenschmidt VHUB_EP1_CTRL_ENABLE,
9007ecca2a4SBenjamin Herrenschmidt vhub->regs + AST_VHUB_EP1_CTRL);
9017ecca2a4SBenjamin Herrenschmidt writel(0, vhub->regs + AST_VHUB_EP1_STS_CHG);
9027ecca2a4SBenjamin Herrenschmidt }
9037ecca2a4SBenjamin Herrenschmidt
ast_vhub_of_parse_dev_desc(struct ast_vhub * vhub,const struct device_node * vhub_np)9042e596d88STao Ren static void ast_vhub_of_parse_dev_desc(struct ast_vhub *vhub,
9052e596d88STao Ren const struct device_node *vhub_np)
9062e596d88STao Ren {
9072e596d88STao Ren u16 id;
9082e596d88STao Ren u32 data;
9092e596d88STao Ren
9102e596d88STao Ren if (!of_property_read_u32(vhub_np, "vhub-vendor-id", &data)) {
9112e596d88STao Ren id = (u16)data;
9122e596d88STao Ren vhub->vhub_dev_desc.idVendor = cpu_to_le16(id);
9132e596d88STao Ren }
9142e596d88STao Ren if (!of_property_read_u32(vhub_np, "vhub-product-id", &data)) {
9152e596d88STao Ren id = (u16)data;
9162e596d88STao Ren vhub->vhub_dev_desc.idProduct = cpu_to_le16(id);
9172e596d88STao Ren }
9182e596d88STao Ren if (!of_property_read_u32(vhub_np, "vhub-device-revision", &data)) {
9192e596d88STao Ren id = (u16)data;
9202e596d88STao Ren vhub->vhub_dev_desc.bcdDevice = cpu_to_le16(id);
9212e596d88STao Ren }
9222e596d88STao Ren }
9232e596d88STao Ren
ast_vhub_fixup_usb1_dev_desc(struct ast_vhub * vhub)92491786aa0STao Ren static void ast_vhub_fixup_usb1_dev_desc(struct ast_vhub *vhub)
92591786aa0STao Ren {
92691786aa0STao Ren vhub->vhub_dev_desc.bcdUSB = cpu_to_le16(0x0100);
92791786aa0STao Ren vhub->vhub_dev_desc.bDeviceProtocol = 0;
92891786aa0STao Ren }
92991786aa0STao Ren
9305cc0710fSTao Ren static struct usb_gadget_string_container*
ast_vhub_str_container_alloc(struct ast_vhub * vhub)9315cc0710fSTao Ren ast_vhub_str_container_alloc(struct ast_vhub *vhub)
9326dbf05fcSTao Ren {
9335cc0710fSTao Ren unsigned int size;
9345cc0710fSTao Ren struct usb_string *str_array;
9355cc0710fSTao Ren struct usb_gadget_strings *lang_str;
9365cc0710fSTao Ren struct usb_gadget_string_container *container;
9375cc0710fSTao Ren
9385cc0710fSTao Ren size = sizeof(*container);
9395cc0710fSTao Ren size += sizeof(struct usb_gadget_strings);
9405cc0710fSTao Ren size += sizeof(struct usb_string) * AST_VHUB_STR_INDEX_MAX;
9415cc0710fSTao Ren container = devm_kzalloc(&vhub->pdev->dev, size, GFP_KERNEL);
9425cc0710fSTao Ren if (!container)
9435cc0710fSTao Ren return ERR_PTR(-ENOMEM);
9445cc0710fSTao Ren
9455cc0710fSTao Ren lang_str = ast_vhub_str_of_container(container);
9465cc0710fSTao Ren str_array = (struct usb_string *)(lang_str + 1);
9475cc0710fSTao Ren lang_str->strings = str_array;
9485cc0710fSTao Ren return container;
9495cc0710fSTao Ren }
9505cc0710fSTao Ren
ast_vhub_str_deep_copy(struct usb_gadget_strings * dest,const struct usb_gadget_strings * src)9515cc0710fSTao Ren static void ast_vhub_str_deep_copy(struct usb_gadget_strings *dest,
9525cc0710fSTao Ren const struct usb_gadget_strings *src)
9535cc0710fSTao Ren {
9545cc0710fSTao Ren struct usb_string *src_array = src->strings;
9555cc0710fSTao Ren struct usb_string *dest_array = dest->strings;
9565cc0710fSTao Ren
9575cc0710fSTao Ren dest->language = src->language;
9585cc0710fSTao Ren if (src_array && dest_array) {
9595cc0710fSTao Ren do {
9605cc0710fSTao Ren *dest_array = *src_array;
9615cc0710fSTao Ren dest_array++;
9625cc0710fSTao Ren src_array++;
9635cc0710fSTao Ren } while (src_array->s);
9645cc0710fSTao Ren }
9655cc0710fSTao Ren }
9665cc0710fSTao Ren
ast_vhub_str_alloc_add(struct ast_vhub * vhub,const struct usb_gadget_strings * src_str)9675cc0710fSTao Ren static int ast_vhub_str_alloc_add(struct ast_vhub *vhub,
9685cc0710fSTao Ren const struct usb_gadget_strings *src_str)
9695cc0710fSTao Ren {
9705cc0710fSTao Ren struct usb_gadget_strings *dest_str;
9715cc0710fSTao Ren struct usb_gadget_string_container *container;
9725cc0710fSTao Ren
9735cc0710fSTao Ren container = ast_vhub_str_container_alloc(vhub);
9745cc0710fSTao Ren if (IS_ERR(container))
9755cc0710fSTao Ren return PTR_ERR(container);
9765cc0710fSTao Ren
9775cc0710fSTao Ren dest_str = ast_vhub_str_of_container(container);
9785cc0710fSTao Ren ast_vhub_str_deep_copy(dest_str, src_str);
9795cc0710fSTao Ren list_add_tail(&container->list, &vhub->vhub_str_desc);
9805cc0710fSTao Ren
9815cc0710fSTao Ren return 0;
9825cc0710fSTao Ren }
9835cc0710fSTao Ren
98430d2617fSTao Ren static const struct {
98530d2617fSTao Ren const char *name;
98630d2617fSTao Ren u8 id;
98730d2617fSTao Ren } str_id_map[] = {
98830d2617fSTao Ren {"manufacturer", AST_VHUB_STR_MANUF},
98930d2617fSTao Ren {"product", AST_VHUB_STR_PRODUCT},
99030d2617fSTao Ren {"serial-number", AST_VHUB_STR_SERIAL},
99130d2617fSTao Ren {},
99230d2617fSTao Ren };
99330d2617fSTao Ren
ast_vhub_of_parse_str_desc(struct ast_vhub * vhub,const struct device_node * desc_np)99430d2617fSTao Ren static int ast_vhub_of_parse_str_desc(struct ast_vhub *vhub,
99530d2617fSTao Ren const struct device_node *desc_np)
99630d2617fSTao Ren {
99730d2617fSTao Ren u32 langid;
99830d2617fSTao Ren int ret = 0;
99930d2617fSTao Ren int i, offset;
100030d2617fSTao Ren const char *str;
100130d2617fSTao Ren struct device_node *child;
100230d2617fSTao Ren struct usb_string str_array[AST_VHUB_STR_INDEX_MAX];
100330d2617fSTao Ren struct usb_gadget_strings lang_str = {
100430d2617fSTao Ren .strings = (struct usb_string *)str_array,
100530d2617fSTao Ren };
100630d2617fSTao Ren
100730d2617fSTao Ren for_each_child_of_node(desc_np, child) {
100830d2617fSTao Ren if (of_property_read_u32(child, "reg", &langid))
100930d2617fSTao Ren continue; /* no language identifier specified */
101030d2617fSTao Ren
101130d2617fSTao Ren if (!usb_validate_langid(langid))
101230d2617fSTao Ren continue; /* invalid language identifier */
101330d2617fSTao Ren
101430d2617fSTao Ren lang_str.language = langid;
101530d2617fSTao Ren for (i = offset = 0; str_id_map[i].name; i++) {
101630d2617fSTao Ren str = of_get_property(child, str_id_map[i].name, NULL);
101730d2617fSTao Ren if (str) {
101830d2617fSTao Ren str_array[offset].s = str;
101930d2617fSTao Ren str_array[offset].id = str_id_map[i].id;
102030d2617fSTao Ren offset++;
102130d2617fSTao Ren }
102230d2617fSTao Ren }
102330d2617fSTao Ren str_array[offset].id = 0;
102430d2617fSTao Ren str_array[offset].s = NULL;
102530d2617fSTao Ren
102630d2617fSTao Ren ret = ast_vhub_str_alloc_add(vhub, &lang_str);
1027a55a9a4cSkernel test robot if (ret) {
1028a55a9a4cSkernel test robot of_node_put(child);
102930d2617fSTao Ren break;
103030d2617fSTao Ren }
1031a55a9a4cSkernel test robot }
103230d2617fSTao Ren
103330d2617fSTao Ren return ret;
103430d2617fSTao Ren }
103530d2617fSTao Ren
ast_vhub_init_desc(struct ast_vhub * vhub)10365cc0710fSTao Ren static int ast_vhub_init_desc(struct ast_vhub *vhub)
10375cc0710fSTao Ren {
10385cc0710fSTao Ren int ret;
103930d2617fSTao Ren struct device_node *desc_np;
104030d2617fSTao Ren const struct device_node *vhub_np = vhub->pdev->dev.of_node;
10415cc0710fSTao Ren
10426dbf05fcSTao Ren /* Initialize vhub Device Descriptor. */
10436dbf05fcSTao Ren memcpy(&vhub->vhub_dev_desc, &ast_vhub_dev_desc,
10446dbf05fcSTao Ren sizeof(vhub->vhub_dev_desc));
10452e596d88STao Ren ast_vhub_of_parse_dev_desc(vhub, vhub_np);
104691786aa0STao Ren if (vhub->force_usb1)
104791786aa0STao Ren ast_vhub_fixup_usb1_dev_desc(vhub);
10486dbf05fcSTao Ren
10496dbf05fcSTao Ren /* Initialize vhub Configuration Descriptor. */
10506dbf05fcSTao Ren memcpy(&vhub->vhub_conf_desc, &ast_vhub_conf_desc,
10516dbf05fcSTao Ren sizeof(vhub->vhub_conf_desc));
10526dbf05fcSTao Ren
10536dbf05fcSTao Ren /* Initialize vhub Hub Descriptor. */
10546dbf05fcSTao Ren memcpy(&vhub->vhub_hub_desc, &ast_vhub_hub_desc,
10556dbf05fcSTao Ren sizeof(vhub->vhub_hub_desc));
1056487bc828STao Ren vhub->vhub_hub_desc.bNbrPorts = vhub->max_ports;
10576dbf05fcSTao Ren
10586dbf05fcSTao Ren /* Initialize vhub String Descriptors. */
10595cc0710fSTao Ren INIT_LIST_HEAD(&vhub->vhub_str_desc);
106030d2617fSTao Ren desc_np = of_get_child_by_name(vhub_np, "vhub-strings");
1061*220fafb4SLiang He if (desc_np) {
106230d2617fSTao Ren ret = ast_vhub_of_parse_str_desc(vhub, desc_np);
1063*220fafb4SLiang He of_node_put(desc_np);
1064*220fafb4SLiang He }
106530d2617fSTao Ren else
10665cc0710fSTao Ren ret = ast_vhub_str_alloc_add(vhub, &ast_vhub_strings);
10675cc0710fSTao Ren
1068347f3f54SNeal Liu /* Initialize vhub Qualifier Descriptor. */
1069347f3f54SNeal Liu memcpy(&vhub->vhub_qual_desc, &ast_vhub_qual_desc,
1070347f3f54SNeal Liu sizeof(vhub->vhub_qual_desc));
1071347f3f54SNeal Liu
10725cc0710fSTao Ren return ret;
10736dbf05fcSTao Ren }
10746dbf05fcSTao Ren
ast_vhub_init_hub(struct ast_vhub * vhub)10755cc0710fSTao Ren int ast_vhub_init_hub(struct ast_vhub *vhub)
10767ecca2a4SBenjamin Herrenschmidt {
10777ecca2a4SBenjamin Herrenschmidt vhub->speed = USB_SPEED_UNKNOWN;
10787ecca2a4SBenjamin Herrenschmidt INIT_WORK(&vhub->wake_work, ast_vhub_wake_work);
10796dbf05fcSTao Ren
10805cc0710fSTao Ren return ast_vhub_init_desc(vhub);
10817ecca2a4SBenjamin Herrenschmidt }
1082