[U-Boot] [PATCH] usb: musb: add virtual root hub control support

Mike Frysinger vapier at gentoo.org
Sat Jan 9 22:53:54 CET 2010


From: Bryan Wu <bryan.wu at analog.com>

For MUSB devices that do not support multipoint (hubs), we have to emulate
a root hub so that we can support core operations like resetting ports.

Signed-off-by: Bryan Wu <bryan.wu at analog.com>
Signed-off-by: Cliff Cai <cliff.cai at analog.com>
Signed-off-by: Mike Frysinger <vapier at gentoo.org>
---
 drivers/usb/musb/musb_hcd.c |  429 ++++++++++++++++++++++++++++++++++++++++++-
 drivers/usb/musb/musb_hcd.h |   55 ++++++
 2 files changed, 482 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/musb/musb_hcd.c b/drivers/usb/musb/musb_hcd.c
index adb8c38..dd2aa7f 100644
--- a/drivers/usb/musb/musb_hcd.c
+++ b/drivers/usb/musb/musb_hcd.c
@@ -35,6 +35,106 @@ static struct musb_epinfo epinfo[3] = {
 	{MUSB_INTR_EP, 0, 64}   /* EP2 - Interrupt IN - 64 Bytes */
 };
 
+/* --- Virtual Root Hub ---------------------------------------------------- */
+#ifdef MUSB_NO_MULTIPOINT
+static int rh_devnum;
+static u32 port_status;
+
+/* Device descriptor */
+static u8 root_hub_dev_des[] = {
+	0x12,			/*  __u8  bLength; */
+	0x01,			/*  __u8  bDescriptorType; Device */
+	0x00,			/*  __u16 bcdUSB; v1.1 */
+	0x02,
+	0x09,			/*  __u8  bDeviceClass; HUB_CLASSCODE */
+	0x00,			/*  __u8  bDeviceSubClass; */
+	0x00,			/*  __u8  bDeviceProtocol; */
+	0x08,			/*  __u8  bMaxPacketSize0; 8 Bytes */
+	0x00,			/*  __u16 idVendor; */
+	0x00,
+	0x00,			/*  __u16 idProduct; */
+	0x00,
+	0x00,			/*  __u16 bcdDevice; */
+	0x00,
+	0x00,			/*  __u8  iManufacturer; */
+	0x01,			/*  __u8  iProduct; */
+	0x00,			/*  __u8  iSerialNumber; */
+	0x01			/*  __u8  bNumConfigurations; */
+};
+
+/* Configuration descriptor */
+static u8 root_hub_config_des[] = {
+	0x09,			/*  __u8  bLength; */
+	0x02,			/*  __u8  bDescriptorType; Configuration */
+	0x19,			/*  __u16 wTotalLength; */
+	0x00,
+	0x01,			/*  __u8  bNumInterfaces; */
+	0x01,			/*  __u8  bConfigurationValue; */
+	0x00,			/*  __u8  iConfiguration; */
+	0x40,			/*  __u8  bmAttributes;
+				   Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */
+	0x00,			/*  __u8  MaxPower; */
+
+	/* interface */
+	0x09,			/*  __u8  if_bLength; */
+	0x04,			/*  __u8  if_bDescriptorType; Interface */
+	0x00,			/*  __u8  if_bInterfaceNumber; */
+	0x00,			/*  __u8  if_bAlternateSetting; */
+	0x01,			/*  __u8  if_bNumEndpoints; */
+	0x09,			/*  __u8  if_bInterfaceClass; HUB_CLASSCODE */
+	0x00,			/*  __u8  if_bInterfaceSubClass; */
+	0x00,			/*  __u8  if_bInterfaceProtocol; */
+	0x00,			/*  __u8  if_iInterface; */
+
+	/* endpoint */
+	0x07,			/*  __u8  ep_bLength; */
+	0x05,			/*  __u8  ep_bDescriptorType; Endpoint */
+	0x81,			/*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
+	0x03,			/*  __u8  ep_bmAttributes; Interrupt */
+	0x00,			/*  __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */
+	0x02,
+	0xff			/*  __u8  ep_bInterval; 255 ms */
+};
+
+static unsigned char root_hub_str_index0[] = {
+	0x04,			/*  __u8  bLength; */
+	0x03,			/*  __u8  bDescriptorType; String-descriptor */
+	0x09,			/*  __u8  lang ID */
+	0x04,			/*  __u8  lang ID */
+};
+
+static unsigned char root_hub_str_index1[] = {
+	0x1c,			/*  __u8  bLength; */
+	0x03,			/*  __u8  bDescriptorType; String-descriptor */
+	'M',			/*  __u8  Unicode */
+	0,			/*  __u8  Unicode */
+	'U',			/*  __u8  Unicode */
+	0,			/*  __u8  Unicode */
+	'S',			/*  __u8  Unicode */
+	0,			/*  __u8  Unicode */
+	'B',			/*  __u8  Unicode */
+	0,			/*  __u8  Unicode */
+	' ',			/*  __u8  Unicode */
+	0,			/*  __u8  Unicode */
+	'R',			/*  __u8  Unicode */
+	0,			/*  __u8  Unicode */
+	'o',			/*  __u8  Unicode */
+	0,			/*  __u8  Unicode */
+	'o',			/*  __u8  Unicode */
+	0,			/*  __u8  Unicode */
+	't',			/*  __u8  Unicode */
+	0,			/*  __u8  Unicode */
+	' ',			/*  __u8  Unicode */
+	0,			/*  __u8  Unicode */
+	'H',			/*  __u8  Unicode */
+	0,			/*  __u8  Unicode */
+	'u',			/*  __u8  Unicode */
+	0,			/*  __u8  Unicode */
+	'b',			/*  __u8  Unicode */
+	0,			/*  __u8  Unicode */
+};
+#endif
+
 /*
  * This function writes the data toggle value.
  */
@@ -411,18 +511,341 @@ static void config_hub_port(struct usb_device *dev, u8 ep)
 #endif
 }
 
+#ifdef MUSB_NO_MULTIPOINT
+
+static void musb_port_reset(int do_reset)
+{
+	u8 power = readb(&musbr->power);
+
+	if (do_reset) {
+		power &= 0xf0;
+		writeb(power | MUSB_POWER_RESET, &musbr->power);
+		port_status |= USB_PORT_STAT_RESET;
+		port_status &= ~USB_PORT_STAT_ENABLE;
+		udelay(30000);
+	} else {
+		writeb(power & ~MUSB_POWER_RESET, &musbr->power);
+
+		power = readb(&musbr->power);
+		if (power & MUSB_POWER_HSMODE)
+			port_status |= USB_PORT_STAT_HIGH_SPEED;
+
+		port_status &= ~(USB_PORT_STAT_RESET | (USB_PORT_STAT_C_CONNECTION << 16));
+		port_status |= USB_PORT_STAT_ENABLE
+			| (USB_PORT_STAT_C_RESET << 16)
+			| (USB_PORT_STAT_C_ENABLE << 16);
+	}
+}
+
+/*
+ * root hub control
+ */
+static int musb_submit_rh_msg(struct usb_device *dev, unsigned long pipe,
+			      void *buffer, int transfer_len,
+			      struct devrequest *cmd)
+{
+	int leni = transfer_len;
+	int len = 0;
+	int stat = 0;
+	u32 datab[4];
+	u8 *data_buf = (u8 *) datab;
+	u16 bmRType_bReq;
+	u16 wValue;
+	u16 wIndex;
+	u16 wLength;
+	u16 int_usb;
+
+	if ((pipe & PIPE_INTERRUPT) == PIPE_INTERRUPT) {
+		debug("Root-Hub submit IRQ: NOT implemented\n");
+		return 0;
+	}
+
+	bmRType_bReq = cmd->requesttype | (cmd->request << 8);
+	wValue = swap_16(cmd->value);
+	wIndex = swap_16(cmd->index);
+	wLength = swap_16(cmd->length);
+
+	debug("--- HUB ----------------------------------------\n");
+	debug("submit rh urb, req=%x val=%#x index=%#x len=%d\n",
+	    bmRType_bReq, wValue, wIndex, wLength);
+	debug("------------------------------------------------\n");
+
+	switch (bmRType_bReq) {
+	case RH_GET_STATUS:
+		debug("RH_GET_STATUS\n");
+
+		*(__u16 *) data_buf = swap_16(1);
+		len = 2;
+		break;
+
+	case RH_GET_STATUS | RH_INTERFACE:
+		debug("RH_GET_STATUS | RH_INTERFACE\n");
+
+		*(__u16 *) data_buf = swap_16(0);
+		len = 2;
+		break;
+
+	case RH_GET_STATUS | RH_ENDPOINT:
+		debug("RH_GET_STATUS | RH_ENDPOINT\n");
+
+		*(__u16 *) data_buf = swap_16(0);
+		len = 2;
+		break;
+
+	case RH_GET_STATUS | RH_CLASS:
+		debug("RH_GET_STATUS | RH_CLASS\n");
+
+		*(__u32 *) data_buf = swap_32(0);
+		len = 4;
+		break;
+
+	case RH_GET_STATUS | RH_OTHER | RH_CLASS:
+		debug("RH_GET_STATUS | RH_OTHER | RH_CLASS\n");
+
+		int_usb = readw(&musbr->intrusb);
+		if (int_usb & MUSB_INTR_CONNECT) {
+			port_status |= USB_PORT_STAT_CONNECTION
+				| (USB_PORT_STAT_C_CONNECTION << 16);
+			port_status |= USB_PORT_STAT_HIGH_SPEED
+				| USB_PORT_STAT_ENABLE;
+		}
+
+		if (port_status & USB_PORT_STAT_RESET)
+			musb_port_reset(0);
+
+		*(__u32 *) data_buf = swap_32(port_status);
+		len = 4;
+		break;
+
+	case RH_CLEAR_FEATURE | RH_ENDPOINT:
+		debug("RH_CLEAR_FEATURE | RH_ENDPOINT\n");
+
+		switch (wValue) {
+		case RH_ENDPOINT_STALL:
+			debug("C_HUB_ENDPOINT_STALL\n");
+			len = 0;
+			break;
+		}
+		port_status &= ~(1 << wValue);
+		break;
+
+	case RH_CLEAR_FEATURE | RH_CLASS:
+		debug("RH_CLEAR_FEATURE | RH_CLASS\n");
+
+		switch (wValue) {
+		case RH_C_HUB_LOCAL_POWER:
+			debug("C_HUB_LOCAL_POWER\n");
+			len = 0;
+			break;
+
+		case RH_C_HUB_OVER_CURRENT:
+			debug("C_HUB_OVER_CURRENT\n");
+			len = 0;
+			break;
+		}
+		port_status &= ~(1 << wValue);
+		break;
+
+	case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
+		debug("RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS\n");
+
+		switch (wValue) {
+		case RH_PORT_ENABLE:
+			len = 0;
+			break;
+
+		case RH_PORT_SUSPEND:
+			len = 0;
+			break;
+
+		case RH_PORT_POWER:
+			len = 0;
+			break;
+
+		case RH_C_PORT_CONNECTION:
+			len = 0;
+			break;
+
+		case RH_C_PORT_ENABLE:
+			len = 0;
+			break;
+
+		case RH_C_PORT_SUSPEND:
+			len = 0;
+			break;
+
+		case RH_C_PORT_OVER_CURRENT:
+			len = 0;
+			break;
+
+		case RH_C_PORT_RESET:
+			len = 0;
+			break;
+
+		default:
+			debug("invalid wValue\n");
+			stat = USB_ST_STALLED;
+		}
+
+		port_status &= ~(1 << wValue);
+		break;
+
+	case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
+		debug("RH_SET_FEATURE | RH_OTHER | RH_CLASS\n");
+
+		switch (wValue) {
+		case RH_PORT_SUSPEND:
+			len = 0;
+			break;
+
+		case RH_PORT_RESET:
+			musb_port_reset(1);
+			len = 0;
+			break;
+
+		case RH_PORT_POWER:
+			len = 0;
+			break;
+
+		case RH_PORT_ENABLE:
+			len = 0;
+			break;
+
+		default:
+			debug("invalid wValue\n");
+			stat = USB_ST_STALLED;
+		}
+
+		port_status |= 1 << wValue;
+		break;
+
+	case RH_SET_ADDRESS:
+		debug("RH_SET_ADDRESS\n");
+
+		rh_devnum = wValue;
+		len = 0;
+		break;
+
+	case RH_GET_DESCRIPTOR:
+		debug("RH_GET_DESCRIPTOR: %x, %d\n", wValue, wLength);
+
+		switch (wValue) {
+		case (USB_DT_DEVICE << 8):	/* device descriptor */
+			len = min_t(unsigned int,
+				    leni, min_t(unsigned int,
+						sizeof(root_hub_dev_des),
+						wLength));
+			data_buf = root_hub_dev_des;
+			break;
+
+		case (USB_DT_CONFIG << 8):	/* configuration descriptor */
+			len = min_t(unsigned int,
+				    leni, min_t(unsigned int,
+						sizeof(root_hub_config_des),
+						wLength));
+			data_buf = root_hub_config_des;
+			break;
+
+		case ((USB_DT_STRING << 8) | 0x00):	/* string 0 descriptors */
+			len = min_t(unsigned int,
+				    leni, min_t(unsigned int,
+						sizeof(root_hub_str_index0),
+						wLength));
+			data_buf = root_hub_str_index0;
+			break;
+
+		case ((USB_DT_STRING << 8) | 0x01):	/* string 1 descriptors */
+			len = min_t(unsigned int,
+				    leni, min_t(unsigned int,
+						sizeof(root_hub_str_index1),
+						wLength));
+			data_buf = root_hub_str_index1;
+			break;
+
+		default:
+			debug("invalid wValue\n");
+			stat = USB_ST_STALLED;
+		}
+
+		break;
+
+	case RH_GET_DESCRIPTOR | RH_CLASS:
+		debug("RH_GET_DESCRIPTOR | RH_CLASS\n");
+
+		data_buf[0] = 0x09;	/* min length; */
+		data_buf[1] = 0x29;
+		data_buf[2] = 0x1;	/* 1 port */
+		data_buf[3] = 0x01;	/* per-port power switching */
+		data_buf[3] |= 0x10;	/* no overcurrent reporting */
+
+		/* Corresponds to data_buf[4-7] */
+		data_buf[4] = 0;
+		data_buf[5] = 5;
+		data_buf[6] = 0;
+		data_buf[7] = 0x02;
+		data_buf[8] = 0xff;
+
+		len = min_t(unsigned int, leni,
+			    min_t(unsigned int, data_buf[0], wLength));
+		break;
+
+	case RH_GET_CONFIGURATION:
+		debug("RH_GET_CONFIGURATION\n");
+
+		*(__u8 *) data_buf = 0x01;
+		len = 1;
+		break;
+
+	case RH_SET_CONFIGURATION:
+		debug("RH_SET_CONFIGURATION\n");
+
+		len = 0;
+		break;
+
+	default:
+		debug("*** *** *** unsupported root hub command *** *** ***\n");
+		stat = USB_ST_STALLED;
+	}
+
+	len = min_t(int, len, leni);
+	if (buffer != data_buf)
+		memcpy(buffer, data_buf, len);
+
+	dev->act_len = len;
+	dev->status = stat;
+	debug("dev act_len %d, status %d\n", dev->act_len, dev->status);
+
+	return stat;
+}
+
+static void musb_rh_init(void)
+{
+	rh_devnum = 0;
+	port_status = 0;
+}
+
+#else
+
+static void musb_rh_init(void) {}
+
+#endif
+
 /*
  * do a control transfer
  */
 int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
 			int len, struct devrequest *setup)
 {
-#ifndef MUSB_NO_MULTIPOINT
 	int devnum = usb_pipedevice(pipe);
-#endif
 	u16 csr;
 	u8  devspeed;
 
+#ifdef MUSB_NO_MULTIPOINT
+	/* Control message is for the HUB? */
+	if (devnum == rh_devnum)
+		return musb_submit_rh_msg(dev, pipe, buffer, len, setup);
+#endif
+
 	/* select control endpoint */
 	writeb(MUSB_CONTROL_EP, &musbr->index);
 	csr = readw(&musbr->txcsr);
@@ -649,6 +1072,8 @@ int usb_lowlevel_init(void)
 	u8  power;
 	u32 timeout;
 
+	musb_rh_init();
+
 	if (musb_platform_init() == -1)
 		return -1;
 
diff --git a/drivers/usb/musb/musb_hcd.h b/drivers/usb/musb/musb_hcd.h
index a437985..dde7d37 100644
--- a/drivers/usb/musb/musb_hcd.h
+++ b/drivers/usb/musb/musb_hcd.h
@@ -50,6 +50,61 @@ extern unsigned char new[];
 	((readb(&musbr->power) & MUSB_POWER_HSMODE) \
 		>> MUSB_POWER_HSMODE_SHIFT)
 
+#define min_t(type, x, y)	\
+	({ type __x = (x); type __y = (y); __x < __y ? __x : __y; })
+
+/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */
+
+/* destination of request */
+#define RH_INTERFACE		   0x01
+#define RH_ENDPOINT		   0x02
+#define RH_OTHER		   0x03
+
+#define RH_CLASS		   0x20
+#define RH_VENDOR		   0x40
+
+/* Requests: bRequest << 8 | bmRequestType */
+#define RH_GET_STATUS		0x0080
+#define RH_CLEAR_FEATURE	0x0100
+#define RH_SET_FEATURE		0x0300
+#define RH_SET_ADDRESS		0x0500
+#define RH_GET_DESCRIPTOR	0x0680
+#define RH_SET_DESCRIPTOR	0x0700
+#define RH_GET_CONFIGURATION	0x0880
+#define RH_SET_CONFIGURATION	0x0900
+#define RH_GET_STATE		0x0280
+#define RH_GET_INTERFACE	0x0A80
+#define RH_SET_INTERFACE	0x0B00
+#define RH_SYNC_FRAME		0x0C80
+/* Our Vendor Specific Request */
+#define RH_SET_EP		0x2000
+
+/* Hub port features */
+#define RH_PORT_CONNECTION	   0x00
+#define RH_PORT_ENABLE		   0x01
+#define RH_PORT_SUSPEND		   0x02
+#define RH_PORT_OVER_CURRENT	   0x03
+#define RH_PORT_RESET		   0x04
+#define RH_PORT_POWER		   0x08
+#define RH_PORT_LOW_SPEED	   0x09
+
+#define RH_C_PORT_CONNECTION	   0x10
+#define RH_C_PORT_ENABLE	   0x11
+#define RH_C_PORT_SUSPEND	   0x12
+#define RH_C_PORT_OVER_CURRENT	   0x13
+#define RH_C_PORT_RESET		   0x14
+
+/* Hub features */
+#define RH_C_HUB_LOCAL_POWER	   0x00
+#define RH_C_HUB_OVER_CURRENT	   0x01
+
+#define RH_DEVICE_REMOTE_WAKEUP	   0x00
+#define RH_ENDPOINT_STALL	   0x01
+
+#define RH_ACK			   0x01
+#define RH_REQ_ERR		   -1
+#define RH_NACK			   0x00
+
 /* extern functions */
 extern int musb_platform_init(void);
 extern void musb_platform_deinit(void);
-- 
1.6.6



More information about the U-Boot mailing list