[U-Boot-Users] [PATCH 1/4][RFC] Add initial high speed support to USB code.

Add high speed support to USB code. Extracted from Juniper Networks patch.
I know that the mergewindow is closed but wanted to get feedback on these patches if possible.
Signed-off-by: Tor Krill tor@excito.com --- common/cmd_usb.c | 3 ++- common/usb.c | 30 +++++++++++++++++++++++++----- include/usb.h | 16 +++++++++------- include/usb_defs.h | 10 ++++++++++ 4 files changed, 46 insertions(+), 13 deletions(-)
diff --git a/common/cmd_usb.c b/common/cmd_usb.c index 9be86b8..03282f6 100644 --- a/common/cmd_usb.c +++ b/common/cmd_usb.c @@ -276,7 +276,8 @@ void usb_show_tree_graph(struct usb_device *dev,char *pre) pre[index++]= has_child ? '|' : ' '; pre[index]=0; printf(" %s (%s, %dmA)\n",usb_get_class_desc(dev->config.if_desc[0].bInterfaceClass), - dev->slow ? "1.5MBit/s" : "12MBit/s",dev->config.MaxPower * 2); + (dev->speed == USB_SPEED_LOW) ? "1.5MBit/s" : (dev->speed == USB_SPEED_FULL) + ? "12MBit/s" : "480MBit/s", dev->config.MaxPower * 2); if (strlen(dev->mf) || strlen(dev->prod) || strlen(dev->serial)) diff --git a/common/usb.c b/common/usb.c index a0107dc..44f35bc 100644 --- a/common/usb.c +++ b/common/usb.c @@ -793,6 +793,11 @@ int usb_new_device(struct usb_device *dev) case 16: dev->maxpacketsize = 1; break; case 32: dev->maxpacketsize = 2; break; case 64: dev->maxpacketsize = 3; break; + case 512: dev->maxpacketsize = 6; break; + default: + printf("XXX bMaxPacketSize0 unsupported (%u)\n", + dev->descriptor.bMaxPacketSize0); + break; } dev->devnum = addr;
@@ -981,8 +986,10 @@ static int hub_port_reset(struct usb_device *dev, int port, } portstatus = le16_to_cpu(portsts.wPortStatus); portchange = le16_to_cpu(portsts.wPortChange); - USB_HUB_PRINTF("portstatus %x, change %x, %s\n", portstatus ,portchange, - portstatus&(1<<USB_PORT_FEAT_LOWSPEED) ? "Low Speed" : "High Speed"); + USB_HUB_PRINTF("portstatus %x, change %x, %s Speed\n", portstatus ,portchange, + portstatus&(1<<USB_PORT_FEAT_LOWSPEED) ? "Low" : + portstatus&(1<<USB_PORT_FEAT_HIGHSPEED) ? "High" : + "Full"); USB_HUB_PRINTF("STAT_C_CONNECTION = %d STAT_CONNECTION = %d USB_PORT_STAT_ENABLE %d\n", (portchange & USB_PORT_STAT_C_CONNECTION) ? 1 : 0, (portstatus & USB_PORT_STAT_CONNECTION) ? 1 : 0, @@ -1026,8 +1033,10 @@ void usb_hub_port_connect_change(struct usb_device *dev, int port)
portstatus = le16_to_cpu(portsts.wPortStatus); portchange = le16_to_cpu(portsts.wPortChange); - USB_HUB_PRINTF("portstatus %x, change %x, %s\n", portstatus, portchange, - portstatus&(1<<USB_PORT_FEAT_LOWSPEED) ? "Low Speed" : "High Speed"); + USB_HUB_PRINTF("portstatus %x, change %x, %s Speed\n", portstatus, portchange, + portstatus&(1<<USB_PORT_FEAT_LOWSPEED) ? "Low" : + portstatus&(1<<USB_PORT_FEAT_HIGHSPEED) ? "High" : + "Full");
/* Clear the connection change status */ usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_CONNECTION); @@ -1052,10 +1061,21 @@ void usb_hub_port_connect_change(struct usb_device *dev, int port)
/* Allocate a new device struct for it */ usb=usb_alloc_new_device(); - usb->slow = (portstatus & USB_PORT_STAT_LOW_SPEED) ? 1 : 0; + switch (portstatus & USB_PORT_STAT_SPEED) { + case 0: + usb->speed = USB_SPEED_FULL; + break; + case USB_PORT_STAT_LOW_SPEED: + usb->speed = USB_SPEED_LOW; + break; + case USB_PORT_STAT_HIGH_SPEED: + usb->speed = USB_SPEED_HIGH; + break; + }
dev->children[port] = usb; usb->parent=dev; + usb->portnr = port + 1; /* Run it through the hoops (find a driver, etc) */ if (usb_new_device(usb)) { /* Woops, disable the port */ diff --git a/include/usb.h b/include/usb.h index 5a6ffdd..410f9cf 100644 --- a/include/usb.h +++ b/include/usb.h @@ -132,7 +132,7 @@ struct usb_config_descriptor {
struct usb_device { int devnum; /* Device number on USB bus */ - int slow; /* Slow device? */ + int speed; /* full/low/high */ char mf[32]; /* manufacturer */ char prod[32]; /* product */ char serial[32]; /* serial number */ @@ -161,6 +161,7 @@ struct usb_device { unsigned long status; int act_len; /* transfered bytes */ int maxchild; /* Number of ports if hub */ + int portnr; struct usb_device *parent; struct usb_device *children[USB_MAXCHILDREN]; }; @@ -171,7 +172,7 @@ struct usb_device {
#if defined(CONFIG_USB_UHCI) || defined(CONFIG_USB_OHCI) || \ defined(CONFIG_USB_OHCI_NEW) || defined (CONFIG_USB_SL811HS) || \ - defined(CONFIG_USB_ISP116X_HCD) + defined(CONFIG_USB_ISP116X_HCD) || defined (CONFIG_USB_EHCI)
int usb_lowlevel_init(void); int usb_lowlevel_stop(void); @@ -264,7 +265,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate); * - endpoint number (4 bits) * - current Data0/1 state (1 bit) * - direction (1 bit) - * - speed (1 bit) + * - speed (2 bits) * - max packet size (2 bits: 8, 16, 32 or 64) * - pipe type (2 bits: control, interrupt, bulk, isochronous) * @@ -280,7 +281,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate); * - device: bits 8-14 * - endpoint: bits 15-18 * - Data0/1: bit 19 - * - speed: bit 26 (0 = Full, 1 = Low Speed) + * - speed: bits 26-27 (0 = Full, 1 = Low, 2 = High) * - pipe type: bits 30-31 (00 = isochronous, 01 = interrupt, 10 = control, 11 = bulk) * * Why? Because it's arbitrary, and whatever encoding we select is really @@ -290,8 +291,8 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate); */ /* Create various pipes... */ #define create_pipe(dev,endpoint) \ - (((dev)->devnum << 8) | (endpoint << 15) | ((dev)->slow << 26) | (dev)->maxpacketsize) -#define default_pipe(dev) ((dev)->slow <<26) + (((dev)->devnum << 8) | (endpoint << 15) | ((dev)->speed << 26) | (dev)->maxpacketsize) +#define default_pipe(dev) ((dev)->speed << 26)
#define usb_sndctrlpipe(dev,endpoint) ((PIPE_CONTROL << 30) | create_pipe(dev,endpoint)) #define usb_rcvctrlpipe(dev,endpoint) ((PIPE_CONTROL << 30) | create_pipe(dev,endpoint) | USB_DIR_IN) @@ -323,7 +324,8 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate); #define usb_pipe_endpdev(pipe) (((pipe) >> 8) & 0x7ff) #define usb_pipeendpoint(pipe) (((pipe) >> 15) & 0xf) #define usb_pipedata(pipe) (((pipe) >> 19) & 1) -#define usb_pipeslow(pipe) (((pipe) >> 26) & 1) +#define usb_pipespeed(pipe) (((pipe) >> 26) & 3) +#define usb_pipeslow(pipe) (usb_pipespeed(pipe) == USB_SPEED_LOW) #define usb_pipetype(pipe) (((pipe) >> 30) & 3) #define usb_pipeisoc(pipe) (usb_pipetype((pipe)) == PIPE_ISOCHRONOUS) #define usb_pipeint(pipe) (usb_pipetype((pipe)) == PIPE_INTERRUPT) diff --git a/include/usb_defs.h b/include/usb_defs.h index 353019f..8032e57 100644 --- a/include/usb_defs.h +++ b/include/usb_defs.h @@ -80,6 +80,12 @@ #define USB_DIR_OUT 0 #define USB_DIR_IN 0x80
+/* USB device speeds */ +#define USB_SPEED_FULL 0x0 /* 12Mbps */ +#define USB_SPEED_LOW 0x1 /* 1.5Mbps */ +#define USB_SPEED_HIGH 0x2 /* 480Mbps */ +#define USB_SPEED_RESERVED 0x3 + /* Descriptor types */ #define USB_DT_DEVICE 0x01 #define USB_DT_CONFIG 0x02 @@ -202,6 +208,7 @@ #define USB_PORT_FEAT_RESET 4 #define USB_PORT_FEAT_POWER 8 #define USB_PORT_FEAT_LOWSPEED 9 +#define USB_PORT_FEAT_HIGHSPEED 10 #define USB_PORT_FEAT_C_CONNECTION 16 #define USB_PORT_FEAT_C_ENABLE 17 #define USB_PORT_FEAT_C_SUSPEND 18 @@ -216,6 +223,9 @@ #define USB_PORT_STAT_RESET 0x0010 #define USB_PORT_STAT_POWER 0x0100 #define USB_PORT_STAT_LOW_SPEED 0x0200 +#define USB_PORT_STAT_HIGH_SPEED 0x0400 /* support for EHCI */ +#define USB_PORT_STAT_SPEED \ + (USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_HIGH_SPEED)
/* wPortChange bits */ #define USB_PORT_STAT_C_CONNECTION 0x0001

Add basic functionality for ehci bulk and control messages
Signed-off-by: Tor Krill tor@excito.com --- drivers/usb/Makefile | 3 + drivers/usb/usb_ehci.h | 106 +++++++ drivers/usb/usb_ehci_core.c | 633 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/usb_ehci_core.h | 29 ++ 4 files changed, 771 insertions(+), 0 deletions(-) create mode 100644 drivers/usb/usb_ehci.h create mode 100644 drivers/usb/usb_ehci_core.c create mode 100644 drivers/usb/usb_ehci_core.h
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 252b00e..838c52c 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -32,6 +32,9 @@ COBJS-y += usbdcore.o COBJS-y += usbdcore_ep0.o COBJS-y += usbdcore_mpc8xx.o COBJS-y += usbdcore_omap1510.o +COBJS-$(CONFIG_USB_EHCI) += usb_ehci_core.o +COBJS-$(CONFIG_USB_EHCI_FSL) += usb_ehci_fsl.o +COBJS-$(CONFIG_USB_EHCI_PCI) += usb_ehci_pci.o
COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/usb/usb_ehci.h b/drivers/usb/usb_ehci.h new file mode 100644 index 0000000..0695038 --- /dev/null +++ b/drivers/usb/usb_ehci.h @@ -0,0 +1,106 @@ +/*- + * Copyright (c) 2007-2008, Juniper Networks, Inc. + * 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. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef USB_EHCI_H +#define USB_EHCI_H + +/* + * Register Space. + */ +struct ehci_hccr { + uint8_t cr_caplength; + uint16_t cr_hciversion; + uint32_t cr_hcsparams; + uint32_t cr_hccparams; + uint8_t cr_hcsp_portrt[8]; +}; + +struct ehci_hcor { + uint32_t or_usbcmd; + uint32_t or_usbsts; + uint32_t or_usbintr; + uint32_t or_frindex; + uint32_t or_ctrldssegment; + uint32_t or_periodiclistbase; + uint32_t or_asynclistaddr; + uint32_t _reserved_[9]; + uint32_t or_configflag; + uint32_t or_portsc[2]; + uint32_t or_systune; +}; + +#define EHCI_PS_WKOC_E 0x00400000 /* RW wake on over current */ +#define EHCI_PS_WKDSCNNT_E 0x00200000 /* RW wake on disconnect */ +#define EHCI_PS_WKCNNT_E 0x00100000 /* RW wake on connect */ +#define EHCI_PS_PTC 0x000f0000 /* RW port test control */ +#define EHCI_PS_PIC 0x0000c000 /* RW port indicator control */ +#define EHCI_PS_PO 0x00002000 /* RW port owner */ +#define EHCI_PS_PP 0x00001000 /* RW,RO port power */ +#define EHCI_PS_LS 0x00000c00 /* RO line status */ +#define EHCI_PS_IS_LOWSPEED(x) (((x) & EHCI_PS_LS) == 0x00000400) +#define EHCI_PS_PR 0x00000100 /* RW port reset */ +#define EHCI_PS_SUSP 0x00000080 /* RW suspend */ +#define EHCI_PS_FPR 0x00000040 /* RW force port resume */ +#define EHCI_PS_OCC 0x00000020 /* RWC over current change */ +#define EHCI_PS_OCA 0x00000010 /* RO over current active */ +#define EHCI_PS_PEC 0x00000008 /* RWC port enable change */ +#define EHCI_PS_PE 0x00000004 /* RW port enable */ +#define EHCI_PS_CSC 0x00000002 /* RWC connect status change */ +#define EHCI_PS_CS 0x00000001 /* RO connect status */ +#define EHCI_PS_CLEAR (EHCI_PS_OCC|EHCI_PS_PEC|EHCI_PS_CSC) + +/* + * Schedule Interface Space. + * + * IMPORTANT: Software must ensure that no interface data structure + * reachable by the EHCI host controller spans a 4K page boundary! + * + * Periodic transfers (i.e. isochronous and interrupt transfers) are + * not supported. + */ + +/* Queue Element Transfer Descriptor (qTD). */ +struct qTD { + uint32_t qt_next; +#define QT_NEXT_TERMINATE 1 + uint32_t qt_altnext; + uint32_t qt_token; + uint32_t qt_buffer[5]; +}; + +/* Queue Head (QH). */ +struct QH { + uint32_t qh_link; +#define QH_LINK_TERMINATE 1 +#define QH_LINK_TYPE_ITD 0 +#define QH_LINK_TYPE_QH 2 +#define QH_LINK_TYPE_SITD 4 +#define QH_LINK_TYPE_FSTN 6 + uint32_t qh_endpt1; + uint32_t qh_endpt2; + uint32_t qh_curtd; + struct qTD qh_overlay; +}; + +/* Low level intit functions */ + +int ehci_hcd_init (void); +int ehci_hcd_stop (void); +#endif /* USB_EHCI_H */ diff --git a/drivers/usb/usb_ehci_core.c b/drivers/usb/usb_ehci_core.c new file mode 100644 index 0000000..dfffb01 --- /dev/null +++ b/drivers/usb/usb_ehci_core.c @@ -0,0 +1,633 @@ +/*- + * 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. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <usb.h> +#include <asm/io.h> +#include "usb_ehci.h" + +int rootdev; +struct ehci_hccr *hccr; /* R/O registers, not need for volatile */ +volatile struct ehci_hcor *hcor; + +static uint16_t portreset; +static struct QH qh_list __attribute__ ((aligned (32))); + +static struct { + uint8_t hub[8]; + uint8_t device[18]; + uint8_t config[9]; + uint8_t interface[9]; + uint8_t endpoint[7]; +} descr = { + { /* HUB */ + sizeof (descr.hub), /* bDescLength */ + 0x29, /* bDescriptorType: hub descriptor */ + 2, /* bNrPorts -- runtime modified */ + 0, 0, /* wHubCharacteristics -- runtime modified */ + 0xff, /* bPwrOn2PwrGood */ + 0, /* bHubCntrCurrent */ + 0 /* DeviceRemovable XXX at most 7 ports! XXX */ + } + , { /* DEVICE */ + sizeof (descr.device), /* bLength */ + 1, /* bDescriptorType: UDESC_DEVICE */ + 0x00, 0x02, /* bcdUSB: v2.0 */ + 9, /* bDeviceClass: UDCLASS_HUB */ + 0, /* bDeviceSubClass: UDSUBCLASS_HUB */ + 1, /* bDeviceProtocol: UDPROTO_HSHUBSTT */ + 64, /* bMaxPacketSize: 64 bytes */ + 0x00, 0x00, /* idVendor */ + 0x00, 0x00, /* idProduct */ + 0x00, 0x01, /* bcdDevice */ + 1, /* iManufacturer */ + 2, /* iProduct */ + 0, /* iSerialNumber */ + 1 /* bNumConfigurations: 1 */ + } + , { /* CONFIG */ + sizeof (descr.config), /* bLength */ + 2, /* bDescriptorType: UDESC_CONFIG */ + sizeof (descr.config) + sizeof (descr.interface) + + sizeof (descr.endpoint), 0, + /* wTotalLength */ + 1, /* bNumInterface */ + 1, /* bConfigurationValue */ + 0, /* iConfiguration */ + 0x40, /* bmAttributes: UC_SELF_POWERED */ + 0 /* bMaxPower */ + } + , { /* INTERFACE */ + sizeof (descr.interface), /* 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 */ + } + , { /* ENDPOINT */ + sizeof (descr.endpoint), /* bLength */ + 5, /* bDescriptorType: UDESC_ENDPOINT */ + 0x81, /* bEndpointAddress: UE_DIR_IN | EHCI_INTR_ENDPT */ + 3, /* bmAttributes: UE_INTERRUPT */ + 8, 0, /* wMaxPacketSize */ + 255 /* bInterval */ + } +}; + +static void *ehci_alloc (size_t sz, size_t align) +{ + static struct QH qh __attribute__ ((aligned (32))); + static struct qTD td[3] __attribute__ ((aligned (32))); + static int ntds = 0; + void *p; + + switch (sz) { + case sizeof (struct QH): + p = &qh; + ntds = 0; + break; + case sizeof (struct qTD): + if (ntds == 3) { + debug ("out of TDs"); + return (NULL); + } + p = &td[ntds]; + ntds++; + break; + default: + debug ("unknown allocation size"); + return (NULL); + } + + memset (p, sz, 0); + return (p); +} + +static void ehci_free (void *p, size_t sz) +{ +} + +static int ehci_td_buffer (struct qTD *td, void *buf, size_t sz) +{ + uint32_t addr, delta, next; + int idx; + + addr = (uint32_t) buf; + idx = 0; + while (idx < 5) { + td->qt_buffer[idx] = cpu_to_le32 (addr); + next = (addr + 4096) & ~4095; + delta = next - addr; + if (delta >= sz) + break; + sz -= delta; + addr = next; + idx++; + } + + if (idx == 5) { + debug ("out of buffer pointers (%u bytes left)", sz); + return (-1); + } + + return (0); +} + +static int +ehci_submit_async (struct usb_device *dev, unsigned long pipe, void *buffer, + int length, struct devrequest *req) +{ + struct QH *qh; + struct qTD *td; + volatile struct qTD *vtd; + unsigned long ts; + uint32_t *tdp; + uint32_t endpt, token, usbsts; + uint32_t c, toggle; + + debug ("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p", dev, pipe, + buffer, length, req); + if (req != NULL) + debug ("req=%u (%#x), type=%u (%#x), value=%u (%#x), index=%u", + 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)); + + qh = ehci_alloc (sizeof (struct QH), 32); + if (qh == NULL) { + debug ("unable to allocate QH"); + return (-1); + } + qh->qh_link = cpu_to_le32 ((uint32_t) & qh_list | QH_LINK_TYPE_QH); + c = (usb_pipespeed (pipe) != USB_SPEED_HIGH && + usb_pipeendpoint (pipe) == 0) ? 1 : 0; + endpt = (8 << 28) | + (c << 27) | + (usb_maxpacket (dev, pipe) << 16) | + (0 << 15) | + (1 << 14) | + (usb_pipespeed (pipe) << 12) | + (usb_pipeendpoint (pipe) << 8) | + (0 << 7) | (usb_pipedevice (pipe) << 0); + qh->qh_endpt1 = cpu_to_le32 (endpt); + endpt = (1 << 30) | + (dev->portnr << 23) | + (dev->parent->devnum << 16) | (0 << 8) | (0 << 0); + qh->qh_endpt2 = cpu_to_le32 (endpt); + qh->qh_overlay.qt_next = cpu_to_le32 (QT_NEXT_TERMINATE); + qh->qh_overlay.qt_altnext = cpu_to_le32 (QT_NEXT_TERMINATE); + + td = NULL; + tdp = &qh->qh_overlay.qt_next; + + toggle = + usb_gettoggle (dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)); + + if (req != NULL) { + td = ehci_alloc (sizeof (struct qTD), 32); + if (td == NULL) { + debug ("unable to allocate SETUP td"); + goto fail; + } + td->qt_next = cpu_to_le32 (QT_NEXT_TERMINATE); + td->qt_altnext = cpu_to_le32 (QT_NEXT_TERMINATE); + token = (0 << 31) | + (sizeof (*req) << 16) | + (0 << 15) | (0 << 12) | (3 << 10) | (2 << 8) | (0x80 << 0); + td->qt_token = cpu_to_le32 (token); + if (ehci_td_buffer (td, req, sizeof (*req)) != 0) { + debug ("unable construct SETUP td"); + ehci_free (td, sizeof (*td)); + goto fail; + } + *tdp = cpu_to_le32 ((uint32_t) td); + tdp = &td->qt_next; + toggle = 1; + } + + if (length > 0 || req == NULL) { + td = ehci_alloc (sizeof (struct qTD), 32); + if (td == NULL) { + debug ("unable to allocate DATA td"); + goto fail; + } + td->qt_next = cpu_to_le32 (QT_NEXT_TERMINATE); + td->qt_altnext = cpu_to_le32 (QT_NEXT_TERMINATE); + token = (toggle << 31) | + (length << 16) | + ((req == NULL ? 1 : 0) << 15) | + (0 << 12) | + (3 << 10) | + ((usb_pipein (pipe) ? 1 : 0) << 8) | (0x80 << 0); + td->qt_token = cpu_to_le32 (token); + if (ehci_td_buffer (td, buffer, length) != 0) { + debug ("unable construct DATA td"); + ehci_free (td, sizeof (*td)); + goto fail; + } + *tdp = cpu_to_le32 ((uint32_t) td); + tdp = &td->qt_next; + } + + if (req != NULL) { + td = ehci_alloc (sizeof (struct qTD), 32); + if (td == NULL) { + debug ("unable to allocate ACK td"); + goto fail; + } + td->qt_next = cpu_to_le32 (QT_NEXT_TERMINATE); + td->qt_altnext = cpu_to_le32 (QT_NEXT_TERMINATE); + token = (toggle << 31) | + (0 << 16) | + (1 << 15) | + (0 << 12) | + (3 << 10) | + ((usb_pipein (pipe) ? 0 : 1) << 8) | (0x80 << 0); + td->qt_token = cpu_to_le32 (token); + *tdp = cpu_to_le32 ((uint32_t) td); + tdp = &td->qt_next; + } + + qh_list.qh_link = cpu_to_le32 ((uint32_t) qh | QH_LINK_TYPE_QH); + + usbsts = le32_to_cpu (hcor->or_usbsts); + hcor->or_usbsts = cpu_to_le32 (usbsts & 0x3f); + + /* Enable async. schedule. */ + hcor->or_usbcmd |= cpu_to_le32 (0x20); + while ((hcor->or_usbsts & cpu_to_le32 (0x8000)) == 0) + udelay (1); + + /* Wait for TDs to be processed. */ + ts = get_timer (0); + vtd = td; + do { + token = le32_to_cpu (vtd->qt_token); + if (!(token & 0x80)) + break; + } while (get_timer (ts) < CFG_HZ); + + /* Disable async schedule. */ + hcor->or_usbcmd &= ~cpu_to_le32 (0x20); + while ((hcor->or_usbsts & cpu_to_le32 (0x8000)) != 0) + udelay (1); + + qh_list.qh_link = cpu_to_le32 ((uint32_t) & qh_list | QH_LINK_TYPE_QH); + + token = le32_to_cpu (qh->qh_overlay.qt_token); + if (!(token & 0x80)) { + // debug ("TOKEN=%#x", token); + switch (token & 0xfc) { + case 0: + toggle = token >> 31; + usb_settoggle (dev, usb_pipeendpoint (pipe), + usb_pipeout (pipe), toggle); + dev->status = 0; + break; + case 0x40: + dev->status = USB_ST_STALLED; + break; + case 0xa0: + case 0x20: + dev->status = USB_ST_BUF_ERR; + break; + case 0x50: + case 0x10: + dev->status = USB_ST_BABBLE_DET; + break; + default: + dev->status = USB_ST_CRC_ERR; + break; + } + dev->act_len = length - ((token >> 16) & 0x7fff); + } else { + dev->act_len = 0; + debug ("dev=%u, usbsts=%#x, p[1]=%#x, p[2]=%#x", + dev->devnum, le32_to_cpu (hcor->or_usbsts), + le32_to_cpu (hcor->or_portsc[0]), + le32_to_cpu (hcor->or_portsc[1])); + } + + return ((dev->status != USB_ST_NOT_PROC) ? 0 : -1); + + fail: + td = (void *)le32_to_cpu (qh->qh_overlay.qt_next); + while (td != (void *)QT_NEXT_TERMINATE) { + qh->qh_overlay.qt_next = td->qt_next; + ehci_free (td, sizeof (*td)); + td = (void *)le32_to_cpu (qh->qh_overlay.qt_next); + } + ehci_free (qh, sizeof (*qh)); + return (-1); +} + +static __inline int min3 (int a, int b, int c) +{ + + if (b < a) + a = b; + if (c < a) + a = c; + return (a); +} + +static int +ehci_submit_root (struct usb_device *dev, unsigned long pipe, void *buffer, + int length, struct devrequest *req) +{ + uint8_t tmpbuf[4]; + void *srcptr; + int len, srclen; + uint32_t reg; + + srclen = 0; + srcptr = NULL; + + debug ("req=%u (%#x), type=%u (%#x), value=%u, index=%u", + req->request, req->request, + req->requesttype, req->requesttype, + le16_to_cpu (req->value), le16_to_cpu (req->index)); + +#define C(a,b) (((b) << 8) | (a)) + + switch (C (req->request, req->requesttype)) { + case C (USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RECIP_DEVICE): + switch (le16_to_cpu (req->value) >> 8) { + case USB_DT_DEVICE: + srcptr = descr.device; + srclen = sizeof (descr.device); + break; + case USB_DT_CONFIG: + srcptr = descr.config; + srclen = sizeof (descr.config) + + sizeof (descr.interface) + sizeof (descr.endpoint); + 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 */ + srcptr = "\16\3u\0-\0b\0o\0o\0t\0"; + srclen = 14; + break; + case 2: /* Product */ + srcptr = "\52\3E\0H\0C\0I\0 \0H\0o\0s\0t\0 \0C\0o\0n\0t\0r\0o\0l\0l\0e\0r\0"; + srclen = 42; + break; + default: + goto unknown; + } + break; + default: + debug ("unknown value %x", le16_to_cpu (req->value)); + goto unknown; + } + break; + case C (USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB): + switch (le16_to_cpu (req->value) >> 8) { + case USB_DT_HUB: + srcptr = descr.hub; + srclen = sizeof (descr.hub); + break; + default: + debug ("unknown value %x", le16_to_cpu (req->value)); + goto unknown; + } + break; + case C (USB_REQ_SET_ADDRESS, USB_RECIP_DEVICE): + rootdev = le16_to_cpu (req->value); + break; + case C (USB_REQ_SET_CONFIGURATION, USB_RECIP_DEVICE): + /* Nothing to do */ + break; + case C (USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB): + tmpbuf[0] = 1; /* USB_STATUS_SELFPOWERED */ + tmpbuf[1] = 0; + srcptr = tmpbuf; + srclen = 2; + break; + case C (USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT): + memset (tmpbuf, 0, 4); + reg = le32_to_cpu (hcor->or_portsc[le16_to_cpu (req->index) - 1]); + if (reg & EHCI_PS_CS) + tmpbuf[0] |= USB_PORT_STAT_CONNECTION; + if (reg & EHCI_PS_PE) + tmpbuf[0] |= USB_PORT_STAT_ENABLE; + if (reg & EHCI_PS_SUSP) + tmpbuf[0] |= USB_PORT_STAT_SUSPEND; + if (reg & EHCI_PS_OCA) + tmpbuf[0] |= USB_PORT_STAT_OVERCURRENT; + if (reg & EHCI_PS_PR) + tmpbuf[0] |= USB_PORT_STAT_RESET; + if (reg & EHCI_PS_PP) + tmpbuf[1] |= USB_PORT_STAT_POWER >> 8; + tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8; + + if (reg & EHCI_PS_CSC) + tmpbuf[2] |= USB_PORT_STAT_C_CONNECTION; + if (reg & EHCI_PS_PEC) + tmpbuf[2] |= USB_PORT_STAT_C_ENABLE; + if (reg & EHCI_PS_OCC) + tmpbuf[2] |= USB_PORT_STAT_C_OVERCURRENT; + if (portreset & (1 << le16_to_cpu (req->index))) + tmpbuf[2] |= USB_PORT_STAT_C_RESET; + srcptr = tmpbuf; + srclen = 4; + break; + case C (USB_REQ_SET_FEATURE, USB_DIR_OUT | USB_RT_PORT): + reg = le32_to_cpu (hcor->or_portsc[le16_to_cpu (req->index) - 1]); + reg &= ~EHCI_PS_CLEAR; + switch (le16_to_cpu (req->value)) { + case USB_PORT_FEAT_POWER: + reg |= EHCI_PS_PP; + break; + case USB_PORT_FEAT_RESET: + if (EHCI_PS_IS_LOWSPEED (reg)) { + /* Low speed device, give up ownership. */ + reg |= EHCI_PS_PO; + break; + } + /* Start reset sequence. */ + reg &= ~EHCI_PS_PE; + reg |= EHCI_PS_PR; + hcor->or_portsc[le16_to_cpu (req->index) - 1] = + cpu_to_le32 (reg); + /* Wait for reset to complete. */ + udelay (500000); + /* Terminate reset sequence. */ + reg &= ~EHCI_PS_PR; + /* TODO: is it only fsl chip that requires this + * manual setting of port enable? + */ + reg |= EHCI_PS_PE; + hcor->or_portsc[le16_to_cpu (req->index) - 1] = + cpu_to_le32 (reg); + /* Wait for HC to complete reset. */ + udelay (2000); + reg = + le32_to_cpu (hcor->or_portsc[le16_to_cpu (req->index) - 1]); + reg &= ~EHCI_PS_CLEAR; + if ((reg & EHCI_PS_PE) == 0) { + /* Not a high speed device, give up ownership. */ + reg |= EHCI_PS_PO; + break; + } + portreset |= 1 << le16_to_cpu (req->index); + break; + default: + debug ("unknown feature %x", le16_to_cpu (req->value)); + goto unknown; + } + hcor->or_portsc[le16_to_cpu (req->index) - 1] = cpu_to_le32 (reg); + break; + case C (USB_REQ_CLEAR_FEATURE, USB_DIR_OUT | USB_RT_PORT): + reg = le32_to_cpu (hcor->or_portsc[le16_to_cpu (req->index) - 1]); + reg &= ~EHCI_PS_CLEAR; + switch (le16_to_cpu (req->value)) { + case USB_PORT_FEAT_ENABLE: + reg &= ~EHCI_PS_PE; + break; + case USB_PORT_FEAT_C_CONNECTION: + reg |= EHCI_PS_CSC; + break; + case USB_PORT_FEAT_C_RESET: + portreset &= ~(1 << le16_to_cpu (req->index)); + break; + default: + debug ("unknown feature %x", le16_to_cpu (req->value)); + goto unknown; + } + hcor->or_portsc[le16_to_cpu (req->index) - 1] = cpu_to_le32 (reg); + break; + default: + debug ("Unknown request %x", + C (req->request, req->requesttype)); + goto unknown; + } + +#undef C + + len = min3 (srclen, le16_to_cpu (req->length), length); + if (srcptr != NULL && len > 0) + memcpy (buffer, srcptr, len); + dev->act_len = len; + dev->status = 0; + return (0); + + unknown: + debug ("requesttype=%x, request=%x, value=%x, index=%x, length=%x", + req->requesttype, req->request, le16_to_cpu (req->value), + le16_to_cpu (req->index), le16_to_cpu (req->length)); + + dev->act_len = 0; + dev->status = USB_ST_STALLED; + return (-1); +} + +int usb_lowlevel_stop (void) +{ + return ehci_hcd_stop (); +} + +int usb_lowlevel_init (void) +{ + uint32_t reg; + + if (ehci_hcd_init () != 0) { + return -1; + } + + /* Set head of reclaim list */ + memset (&qh_list, 0, sizeof (qh_list)); + qh_list.qh_link = cpu_to_le32 ((uint32_t) & qh_list | QH_LINK_TYPE_QH); + qh_list.qh_endpt1 = cpu_to_le32 ((1 << 15) | (USB_SPEED_HIGH << 12)); + qh_list.qh_curtd = cpu_to_le32 (QT_NEXT_TERMINATE); + qh_list.qh_overlay.qt_next = cpu_to_le32 (QT_NEXT_TERMINATE); + qh_list.qh_overlay.qt_altnext = cpu_to_le32 (QT_NEXT_TERMINATE); + qh_list.qh_overlay.qt_token = cpu_to_le32 (0x40); + + /* Set async. queue head pointer. */ + hcor->or_asynclistaddr = cpu_to_le32 ((uint32_t) & qh_list); + + reg = le32_to_cpu (hccr->cr_hcsparams); + descr.hub[2] = reg & 0xf; + if (reg & 0x10000) /* Port Indicators */ + descr.hub[3] |= 0x80; + if (reg & 0x10) /* Port Power Control */ + descr.hub[3] |= 0x01; + + /* take control over the ports */ + hcor->or_configflag |= cpu_to_le32 (1); + + /* Start the host controller. */ + hcor->or_usbcmd |= cpu_to_le32 (1); + + rootdev = 0; + + return 0; +} + +int +submit_bulk_msg (struct usb_device *dev, unsigned long pipe, void *buffer, + int length) +{ + + if (usb_pipetype (pipe) != PIPE_BULK) { + debug ("non-bulk pipe (type=%lu)", usb_pipetype (pipe)); + return (-1); + } + return (ehci_submit_async (dev, pipe, buffer, length, NULL)); +} + +int +submit_control_msg (struct usb_device *dev, unsigned long pipe, void *buffer, + int length, struct devrequest *setup) +{ + + if (usb_pipetype (pipe) != PIPE_CONTROL) { + debug ("non-control pipe (type=%lu)", usb_pipetype (pipe)); + return (-1); + } + + if (usb_pipedevice (pipe) == rootdev) { + if (rootdev == 0) + dev->speed = USB_SPEED_HIGH; + return (ehci_submit_root (dev, pipe, buffer, length, setup)); + } + return (ehci_submit_async (dev, pipe, buffer, length, setup)); +} + +int +submit_int_msg (struct usb_device *dev, unsigned long pipe, void *buffer, + int length, int interval) +{ + + debug ("dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d", dev, pipe, + buffer, length, interval); + return (-1); +} diff --git a/drivers/usb/usb_ehci_core.h b/drivers/usb/usb_ehci_core.h new file mode 100644 index 0000000..88e91b9 --- /dev/null +++ b/drivers/usb/usb_ehci_core.h @@ -0,0 +1,29 @@ +/*- + * 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. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef USB_EHCI_CORE_H +#define USB_EHCI_CORE_H + +extern int rootdev; +extern struct ehci_hccr *hccr; +extern volatile struct ehci_hcor *hcor; + +#endif

Add hcd driver for Freescale 8313 and possibly others. Supports built in UTMI PHY and has a hardcoded clock setting.
Signed-off-by: Tor Krill tor@excito.com --- drivers/usb/usb_ehci_fsl.c | 99 ++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/usb_ehci_fsl.h | 82 ++++++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+), 0 deletions(-) create mode 100644 drivers/usb/usb_ehci_fsl.c create mode 100644 drivers/usb/usb_ehci_fsl.h
diff --git a/drivers/usb/usb_ehci_fsl.c b/drivers/usb/usb_ehci_fsl.c new file mode 100644 index 0000000..6c03773 --- /dev/null +++ b/drivers/usb/usb_ehci_fsl.c @@ -0,0 +1,99 @@ +/* + * (C) Copyright 2008, Excito Elektronik i Skåne AB + * + * Author: Tor Krill tor@excito.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. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <pci.h> +#include <usb.h> +#include <mpc83xx.h> +#include <asm/io.h> +#include <asm/bitops.h> + +#include "usb_ehci.h" +#include "usb_ehci_fsl.h" +#include "usb_ehci_core.h" + +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + * + * Excerpts from linux ehci fsl driver. + */ +int ehci_hcd_init (void) +{ + volatile immap_t *im = (immap_t *) CFG_IMMR; + uint32_t addr, temp; + + addr = (uint32_t) & (im->usb[0]); + hccr = (struct ehci_hccr *)(addr + FSL_SKIP_PCI); + hcor = (struct ehci_hcor *)((uint32_t) hccr + hccr->cr_caplength); + + /* Configure clock */ + clrsetbits_be32 (&(im->clk.sccr), MPC83XX_SCCR_USB_MASK, + MPC83XX_SCCR_USB_DRCM_11); + + /* Confgure interface. */ + temp = in_be32 ((void *)(addr + FSL_SOC_USB_CTRL)); + out_be32 ((void *)(addr + FSL_SOC_USB_CTRL), temp + | REFSEL_16MHZ | UTMI_PHY_EN); + + /* Wait for clock to stabilize */ + do { + temp = in_be32 ((void *)(addr + FSL_SOC_USB_CTRL)); + udelay (1000); + } while (!(temp & PHY_CLK_VALID)); + + /* Set to Host mode */ + temp = in_le32 ((void *)(addr + FSL_SOC_USB_USBMODE)); + out_le32 ((void *)(addr + FSL_SOC_USB_USBMODE), temp | CM_HOST); + + out_be32 ((void *)(addr + FSL_SOC_USB_SNOOP1), SNOOP_SIZE_2GB); + out_be32 ((void *)(addr + FSL_SOC_USB_SNOOP2), + 0x80000000 | SNOOP_SIZE_2GB); + + /* Init phy */ + /* TODO: handle different phys? */ + out_le32 (&(hcor->or_portsc[0]), PORT_PTS_UTMI); + + /* Enable interface. */ + temp = in_be32 ((void *)(addr + FSL_SOC_USB_CTRL)); + out_be32 ((void *)(addr + FSL_SOC_USB_CTRL), temp | USB_EN); + + out_be32 ((void *)(addr + FSL_SOC_USB_PRICTRL), 0x0000000c); + out_be32 ((void *)(addr + FSL_SOC_USB_AGECNTTHRSH), 0x00000040); + out_be32 ((void *)(addr + FSL_SOC_USB_SICTRL), 0x00000001); + + /* Enable interface. */ + temp = in_be32 ((void *)(addr + FSL_SOC_USB_CTRL)); + out_be32 ((void *)(addr + FSL_SOC_USB_CTRL), temp | USB_EN); + + temp = in_le32 ((void *)(addr + FSL_SOC_USB_USBMODE)); + + return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop (void) +{ + return 0; +} diff --git a/drivers/usb/usb_ehci_fsl.h b/drivers/usb/usb_ehci_fsl.h new file mode 100644 index 0000000..dd7ee2e --- /dev/null +++ b/drivers/usb/usb_ehci_fsl.h @@ -0,0 +1,82 @@ +/* Copyright (c) 2005 freescale semiconductor + * Copyright (c) 2005 MontaVista Software + * Copyright (c) 2008 Excito Elektronik i Skåne AB + * + * 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. + * + * 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. + */ +#ifndef _EHCI_FSL_H +#define _EHCI_FSL_H +/* Global offsets */ +#define FSL_SKIP_PCI 0x100 + +/* offsets for the non-ehci registers in the FSL SOC USB controller */ +#define FSL_SOC_USB_ULPIVP 0x170 +#define FSL_SOC_USB_PORTSC1 0x184 +#define PORT_PTS_MSK (3<<30) +#define PORT_PTS_UTMI (0<<30) +#define PORT_PTS_ULPI (2<<30) +#define PORT_PTS_SERIAL (3<<30) +#define PORT_PTS_PTW (1<<28) + +/* USBMODE Register bits */ +#define CM_IDLE (0<<0) +#define CM_RESERVED (1<<0) +#define CM_DEVICE (2<<0) +#define CM_HOST (3<<0) +#define USBMODE_RESERVED_2 (0<<2) +#define SLOM (1<<3) +#define SDIS (1<<4) + +/* CONTROL Register bits */ +#define ULPI_INT_EN (1<<0) +#define WU_INT_EN (1<<1) +#define USB_EN (1<<2) +#define LSF_EN (1<<3) +#define KEEP_OTG_ON (1<<4) +#define OTG_PORT (1<<5) +#define REFSEL_12MHZ (0<<6) +#define REFSEL_16MHZ (1<<6) +#define REFSEL_48MHZ (2<<6) +#define PLL_RESET (1<<8) +#define UTMI_PHY_EN (1<<9) +#define PHY_CLK_SEL_UTMI (0<<10) +#define PHY_CLK_SEL_ULPI (1<<10) +#define CLKIN_SEL_USB_CLK (0<<11) +#define CLKIN_SEL_USB_CLK2 (1<<11) +#define CLKIN_SEL_SYS_CLK (2<<11) +#define CLKIN_SEL_SYS_CLK2 (3<<11) +#define RESERVED_18 (0<<13) +#define RESERVED_17 (0<<14) +#define RESERVED_16 (0<<15) +#define WU_INT (1<<16) +#define PHY_CLK_VALID (1<<17) + +#define FSL_SOC_USB_PORTSC2 0x188 +#define FSL_SOC_USB_USBMODE 0x1a8 +#define FSL_SOC_USB_SNOOP1 0x400 /* NOTE: big-endian */ +#define FSL_SOC_USB_SNOOP2 0x404 /* NOTE: big-endian */ +#define FSL_SOC_USB_AGECNTTHRSH 0x408 /* NOTE: big-endian */ +#define FSL_SOC_USB_PRICTRL 0x40c /* NOTE: big-endian */ +#define FSL_SOC_USB_SICTRL 0x410 /* NOTE: big-endian */ +#define FSL_SOC_USB_CTRL 0x500 /* NOTE: big-endian */ +#define SNOOP_SIZE_2GB 0x1e + +/* System Clock Control Register */ +#define MPC83XX_SCCR_USB_MASK 0x00f00000 +#define MPC83XX_SCCR_USB_DRCM_11 0x00300000 +#define MPC83XX_SCCR_USB_DRCM_01 0x00100000 +#define MPC83XX_SCCR_USB_DRCM_10 0x00200000 + +#endif /* _EHCI_FSL_H */

Readds Juniper Networks original Philips PCI driver. (Untested)
Signed-off-by: Tor Krill tor@excito.com --- drivers/usb/usb_ehci_pci.c | 135 ++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/usb_ehci_pci.h | 37 ++++++++++++ 2 files changed, 172 insertions(+), 0 deletions(-) create mode 100644 drivers/usb/usb_ehci_pci.c create mode 100644 drivers/usb/usb_ehci_pci.h
diff --git a/drivers/usb/usb_ehci_pci.c b/drivers/usb/usb_ehci_pci.c new file mode 100644 index 0000000..d652122 --- /dev/null +++ b/drivers/usb/usb_ehci_pci.c @@ -0,0 +1,135 @@ +/*- + * Copyright (c) 2007-2008, Juniper Networks, Inc. + * 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. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <pci.h> +#include <usb.h> +#include "usb_ehci.h" +#include "usb_ehci_pci.h" +#include "usb_ehci_core.h" + +#ifdef EHCI_DEBUG +static void dump_pci_reg (pci_dev_t dev, int ofs) +{ + uint32_t reg; + + pci_read_config_dword (dev, ofs, ®); + printf ("\t0x%02x: %08x\n", ofs, reg); +} + +static void dump_pci (int enh, pci_dev_t dev) +{ + int ofs; + + debug ("\n%s", (enh) ? "EHCI" : "OHCI"); + for (ofs = 0; ofs < 0x44; ofs += 4) + dump_pci_reg (dev, ofs); + if (enh) + dump_pci_reg (dev, 0x60); + dump_pci_reg (dev, 0xdc); + dump_pci_reg (dev, 0xe0); + if (enh) { + dump_pci_reg (dev, 0xe4); + dump_pci_reg (dev, 0xe8); + } +} + +static void dump_regs (void) +{ + + debug ("usbcmd=%#x, usbsts=%#x, usbintr=%#x,\n\tfrindex=%#x, " + "ctrldssegment=%#x, periodiclistbase=%#x,\n\tasynclistaddr=%#x, " + "configflag=%#x,\n\tportsc[1]=%#x, portsc[2]=%#x, systune=%#x", + swap_32 (hcor->or_usbcmd), swap_32 (hcor->or_usbsts), + swap_32 (hcor->or_usbintr), swap_32 (hcor->or_frindex), + swap_32 (hcor->or_ctrldssegment), + swap_32 (hcor->or_periodiclistbase), + swap_32 (hcor->or_asynclistaddr), swap_32 (hcor->or_configflag), + swap_32 (hcor->or_portsc[0]), swap_32 (hcor->or_portsc[1]), + swap_32 (hcor->or_systune)); +} + +static void dump_TD (struct qTD *td) +{ + + debug ("%p: qt_next=%#x, qt_altnext=%#x, qt_token=%#x, " + "qt_buffer={%#x,%#x,%#x,%#x,%#x}", td, swap_32 (td->qt_next), + swap_32 (td->qt_altnext), swap_32 (td->qt_token), + swap_32 (td->qt_buffer[0]), swap_32 (td->qt_buffer[1]), + swap_32 (td->qt_buffer[2]), swap_32 (td->qt_buffer[3]), + swap_32 (td->qt_buffer[4])); +} + +static void dump_QH (struct QH *qh) +{ + + debug ("%p: qh_link=%#x, qh_endpt1=%#x, qh_endpt2=%#x, qh_curtd=%#x", + qh, swap_32 (qh->qh_link), swap_32 (qh->qh_endpt1), + swap_32 (qh->qh_endpt2), swap_32 (qh->qh_curtd)); + dump_TD (&qh->qh_overlay); +} +#endif + +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + */ +int ehci_hcd_init (void) +{ + pci_dev_t dev; + uint32_t addr; + + dev = pci_find_device (0x1131, 0x1561, 0); + if (dev != -1) { + volatile uint32_t *hcreg; + + pci_read_config_dword (dev, PCI_BASE_ADDRESS_0, &addr); + hcreg = (uint32_t *) (addr + 8); + *hcreg = swap_32 (1); + udelay (100); + } + + dev = pci_find_device (0x1131, 0x1562, 0); + if (dev == -1) { + printf ("EHCI host controller not found\n"); + return (-1); + } + + pci_read_config_dword (dev, EHCI_PCICS_USBBASE, &addr); + hccr = (void *)addr; + + addr += hccr->cr_caplength; + hcor = (void *)addr; + + /* Latte/Espresso USB hardware bug workaround */ + hcor->or_systune |= swap_32 (3); + + return (0); +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop (void) +{ + + return (0); +} diff --git a/drivers/usb/usb_ehci_pci.h b/drivers/usb/usb_ehci_pci.h new file mode 100644 index 0000000..6a4d16e --- /dev/null +++ b/drivers/usb/usb_ehci_pci.h @@ -0,0 +1,37 @@ +/*- + * Copyright (c) 2007-2008, Juniper Networks, Inc. + * 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. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * PCI Configuration Space. + */ +#define EHCI_PCICS_BASEC PCI_CLASS_CODE +#define EHCI_PCICS_SCC PCI_CLASS_SUB_CODE +#define EHCI_PCICS_PI PCI_CLASS_PROG +#define EHCI_PCICS_USBBASE PCI_BASE_ADDRESS_0 +#define EHCI_PCICS_SBRN 0x60 +#define EHCI_PCICS_FLADJ 0x61 +#define EHCI_PCICS_PORTWAKECAP 0x62 + +#define EHCI_PCICS_USBLEGSUP +#define EHCI_PCICS_USBLEGCTLSTS + +#define EHCI_PCI_BASEC 0x0c /* Serial Bus Controller. */ +#define EHCI_PCI_SCC 0x03 /* USB Host Controller. */ +#define EHCI_PCI_PI 0x20 /* USB 2.0 Host Controller. */

Hi, Tor Krill wrote:
+static uint16_t portreset; +static struct QH qh_list __attribute__ ((aligned (32)));
+static struct {
- uint8_t hub[8];
- uint8_t device[18];
- uint8_t config[9];
- uint8_t interface[9];
- uint8_t endpoint[7];
+} descr = {
- { /* HUB */
sizeof (descr.hub), /* bDescLength */
0x29, /* bDescriptorType: hub descriptor */
2, /* bNrPorts -- runtime modified */
0, 0, /* wHubCharacteristics -- runtime modified */
0xff, /* bPwrOn2PwrGood */
0, /* bHubCntrCurrent */
0 /* DeviceRemovable XXX at most 7 ports! XXX */
- }
- , { /* DEVICE */
sizeof (descr.device), /* bLength */
1, /* bDescriptorType: UDESC_DEVICE */
0x00, 0x02, /* bcdUSB: v2.0 */
9, /* bDeviceClass: UDCLASS_HUB */
0, /* bDeviceSubClass: UDSUBCLASS_HUB */
1, /* bDeviceProtocol: UDPROTO_HSHUBSTT */
64, /* bMaxPacketSize: 64 bytes */
0x00, 0x00, /* idVendor */
0x00, 0x00, /* idProduct */
0x00, 0x01, /* bcdDevice */
1, /* iManufacturer */
2, /* iProduct */
0, /* iSerialNumber */
1 /* bNumConfigurations: 1 */
- }
- , { /* CONFIG */
sizeof (descr.config), /* bLength */
2, /* bDescriptorType: UDESC_CONFIG */
sizeof (descr.config) + sizeof (descr.interface) +
sizeof (descr.endpoint), 0,
/* wTotalLength */
1, /* bNumInterface */
1, /* bConfigurationValue */
0, /* iConfiguration */
0x40, /* bmAttributes: UC_SELF_POWERED */
0 /* bMaxPower */
- }
- , { /* INTERFACE */
sizeof (descr.interface), /* 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 */
- }
- , { /* ENDPOINT */
sizeof (descr.endpoint), /* bLength */
5, /* bDescriptorType: UDESC_ENDPOINT */
0x81, /* bEndpointAddress: UE_DIR_IN | EHCI_INTR_ENDPT */
3, /* bmAttributes: UE_INTERRUPT */
8, 0, /* wMaxPacketSize */
255 /* bInterval */
- }
+};
I prefer using:
struct usb_device_descriptor device = { sizeof(struct usb_device_descriptor), /* bLength */ 1, /* bDescriptorType: UDESC_DEVICE */ 0x0002, /* bcdUSB: v2.0 */ 9, /* bDeviceClass: UDCLASS_HUB */ 0, /* bDeviceSubClass: UDSUBCLASS_HUB */ 1, /* bDeviceProtocol: UDPROTO_HSHUBSTT */ 64, /* bMaxPacketSize: 64 bytes */ 0x0000, /* idVendor */ 0x0000, /* idProduct */ 0x0001, /* bcdDevice */ 1, /* iManufacturer */ 2, /* iProduct */ 0, /* iSerialNumber */ 1 /* bNumConfigurations: 1 */ };
struct usb_interface_descriptor interface = { sizeof(struct usb_interface_descriptor), /* 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 */ };
struct usb_endpoint_descriptor endpoint = { sizeof(struct usb_endpoint_descriptor), /* bLength */ 5, /* bDescriptorType: UDESC_ENDPOINT */ 0x81, /* bEndpointAddress: UE_DIR_IN | EHCI_INTR_ENDPT */ 3, /* bmAttributes: UE_INTERRUPT */ 8, 0, /* wMaxPacketSize */ 255 /* bInterval */
};
struct usb_hub_descriptor hub = { sizeof(struct usb_hub_descriptor), /* bDescLength */ 0x29, /* bDescriptorType: hub descriptor */ 2, /* bNrPorts -- runtime modified */ 0, 0, /* wHubCharacteristics */ 0xff, /* bPwrOn2PwrGood */ {}, /* bHubCntrCurrent */ {} /* at most 7 ports! XXX */ };
Why don't use somenthing like this? (see up)
+static int +ehci_submit_root (struct usb_device *dev, unsigned long pipe, void *buffer,
int length, struct devrequest *req)
+{
- uint8_t tmpbuf[4];
- void *srcptr;
- int len, srclen;
- uint32_t reg;
- srclen = 0;
- srcptr = NULL;
- debug ("req=%u (%#x), type=%u (%#x), value=%u, index=%u",
req->request, req->request,
req->requesttype, req->requesttype,
le16_to_cpu (req->value), le16_to_cpu (req->index));
+#define C(a,b) (((b) << 8) | (a))
- switch (C (req->request, req->requesttype)) {
- case C (USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RECIP_DEVICE):
switch (le16_to_cpu (req->value) >> 8) {
case USB_DT_DEVICE:
srcptr = descr.device;
srclen = sizeof (descr.device);
break;
case USB_DT_CONFIG:
srcptr = descr.config;
srclen = sizeof (descr.config) +
sizeof (descr.interface) + sizeof (descr.endpoint);
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 */
srcptr = "\16\3u\0-\0b\0o\0o\0t\0";
srclen = 14;
break;
case 2: /* Product */
srcptr = "\52\3E\0H\0C\0I\0 \0H\0o\0s\0t\0 \0C\0o\0n\0t\0r\0o\0l\0l\0e\0r\0";
srclen = 42;
break;
default:
goto unknown;
}
break;
default:
debug ("unknown value %x", le16_to_cpu (req->value));
goto unknown;
}
break;
- case C (USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB):
switch (le16_to_cpu (req->value) >> 8) {
case USB_DT_HUB:
srcptr = descr.hub;
srclen = sizeof (descr.hub);
break;
default:
debug ("unknown value %x", le16_to_cpu (req->value));
goto unknown;
}
break;
- case C (USB_REQ_SET_ADDRESS, USB_RECIP_DEVICE):
rootdev = le16_to_cpu (req->value);
break;
- case C (USB_REQ_SET_CONFIGURATION, USB_RECIP_DEVICE):
/* Nothing to do */
break;
- case C (USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB):
tmpbuf[0] = 1; /* USB_STATUS_SELFPOWERED */
tmpbuf[1] = 0;
srcptr = tmpbuf;
srclen = 2;
break;
- case C (USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT):
memset (tmpbuf, 0, 4);
reg = le32_to_cpu (hcor->or_portsc[le16_to_cpu (req->index) - 1]);
if (reg & EHCI_PS_CS)
tmpbuf[0] |= USB_PORT_STAT_CONNECTION;
if (reg & EHCI_PS_PE)
tmpbuf[0] |= USB_PORT_STAT_ENABLE;
if (reg & EHCI_PS_SUSP)
tmpbuf[0] |= USB_PORT_STAT_SUSPEND;
if (reg & EHCI_PS_OCA)
tmpbuf[0] |= USB_PORT_STAT_OVERCURRENT;
if (reg & EHCI_PS_PR)
tmpbuf[0] |= USB_PORT_STAT_RESET;
if (reg & EHCI_PS_PP)
tmpbuf[1] |= USB_PORT_STAT_POWER >> 8;
tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8;
if (reg & EHCI_PS_CSC)
tmpbuf[2] |= USB_PORT_STAT_C_CONNECTION;
if (reg & EHCI_PS_PEC)
tmpbuf[2] |= USB_PORT_STAT_C_ENABLE;
if (reg & EHCI_PS_OCC)
tmpbuf[2] |= USB_PORT_STAT_C_OVERCURRENT;
if (portreset & (1 << le16_to_cpu (req->index)))
tmpbuf[2] |= USB_PORT_STAT_C_RESET;
srcptr = tmpbuf;
srclen = 4;
break;
- case C (USB_REQ_SET_FEATURE, USB_DIR_OUT | USB_RT_PORT):
reg = le32_to_cpu (hcor->or_portsc[le16_to_cpu (req->index) - 1]);
reg &= ~EHCI_PS_CLEAR;
switch (le16_to_cpu (req->value)) {
case USB_PORT_FEAT_POWER:
reg |= EHCI_PS_PP;
break;
case USB_PORT_FEAT_RESET:
if (EHCI_PS_IS_LOWSPEED (reg)) {
/* Low speed device, give up ownership. */
reg |= EHCI_PS_PO;
break;
}
/* Start reset sequence. */
reg &= ~EHCI_PS_PE;
reg |= EHCI_PS_PR;
hcor->or_portsc[le16_to_cpu (req->index) - 1] =
cpu_to_le32 (reg);
/* Wait for reset to complete. */
udelay (500000);
/* Terminate reset sequence. */
reg &= ~EHCI_PS_PR;
/* TODO: is it only fsl chip that requires this
* manual setting of port enable?
*/
reg |= EHCI_PS_PE;
hcor->or_portsc[le16_to_cpu (req->index) - 1] =
cpu_to_le32 (reg);
/* Wait for HC to complete reset. */
udelay (2000);
reg =
le32_to_cpu (hcor->or_portsc[le16_to_cpu (req->index) - 1]);
reg &= ~EHCI_PS_CLEAR;
if ((reg & EHCI_PS_PE) == 0) {
/* Not a high speed device, give up ownership. */
reg |= EHCI_PS_PO;
break;
}
portreset |= 1 << le16_to_cpu (req->index);
break;
default:
debug ("unknown feature %x", le16_to_cpu (req->value));
goto unknown;
}
hcor->or_portsc[le16_to_cpu (req->index) - 1] = cpu_to_le32 (reg);
break;
- case C (USB_REQ_CLEAR_FEATURE, USB_DIR_OUT | USB_RT_PORT):
reg = le32_to_cpu (hcor->or_portsc[le16_to_cpu (req->index) - 1]);
reg &= ~EHCI_PS_CLEAR;
switch (le16_to_cpu (req->value)) {
case USB_PORT_FEAT_ENABLE:
reg &= ~EHCI_PS_PE;
break;
case USB_PORT_FEAT_C_CONNECTION:
reg |= EHCI_PS_CSC;
break;
case USB_PORT_FEAT_C_RESET:
portreset &= ~(1 << le16_to_cpu (req->index));
break;
default:
debug ("unknown feature %x", le16_to_cpu (req->value));
goto unknown;
}
hcor->or_portsc[le16_to_cpu (req->index) - 1] = cpu_to_le32 (reg);
break;
- default:
debug ("Unknown request %x",
C (req->request, req->requesttype));
goto unknown;
- }
+#undef C
- len = min3 (srclen, le16_to_cpu (req->length), length);
- if (srcptr != NULL && len > 0)
memcpy (buffer, srcptr, len);
- dev->act_len = len;
- dev->status = 0;
- return (0);
unknown:
- debug ("requesttype=%x, request=%x, value=%x, index=%x, length=%x",
req->requesttype, req->request, le16_to_cpu (req->value),
le16_to_cpu (req->index), le16_to_cpu (req->length));
- dev->act_len = 0;
- dev->status = USB_ST_STALLED;
- return (-1);
+}
This code can be change according with this one.
static int ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, int length, struct devrequest *req) { uint8_t tmpbuf[4]; u16 typeReq; void *srcptr; int len, srclen; uint32_t reg;
srclen = 0; srcptr = NULL;
DBG("req=%u (%#x), type=%u (%#x), value=%u, index=%u", req->request, req->request, req->requesttype, req->requesttype, swap_16(req->value), swap_16(req->index));
typeReq = req->request << 8 | req->requesttype;
switch (typeReq) {
case DeviceRequest | USB_REQ_GET_DESCRIPTOR: switch(swap_16(req->value) >> 8) { case USB_DT_DEVICE: srcptr = &device; srclen = sizeof(struct usb_device_descriptor); break; case USB_DT_CONFIG: srcptr = &config; srclen = sizeof(config) + sizeof(struct usb_interface_descriptor) + sizeof(struct usb_hub_descriptor); break; case USB_DT_STRING: switch (swap_16(req->value) & 0xff) { case 0: /* Language */ srcptr = "\4\3\1\0"; srclen = 4; break; case 1: /* Vendor */ srcptr = "\20\3P\0h\0i\0l\0i\0p\0s\0"; srclen = 16; break; case 2: /* Product */ srcptr = "\12\3E\0H\0C\0I\0"; srclen = 10; break; default: goto unknown; } break; default: DBG("unknown value %x", swap_16(req->value)); goto unknown; } break; case USB_REQ_GET_DESCRIPTOR | (( USB_DIR_IN | USB_RT_HUB) << 8): switch (swap_16(req->value) >> 8) { case USB_DT_HUB: srcptr = &hub; srclen = sizeof(hub); break; default: DBG("unknown value %x", swap_16(req->value)); goto unknown; } break; case USB_REQ_SET_ADDRESS | (USB_RECIP_DEVICE << 8): rootdev = swap_16(req->value); break; case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: /* Nothing to do */ 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 DeviceRequest | USB_REQ_GET_STATUS: memset(tmpbuf, 0, 4); reg = swap_32(hcor->or_portsc[swap_16(req->index) - 1]); if (reg & EHCI_PS_CS) tmpbuf[0] |= USB_PORT_STAT_CONNECTION; if (reg & EHCI_PS_PE) tmpbuf[0] |= USB_PORT_STAT_ENABLE; if (reg & EHCI_PS_SUSP) tmpbuf[0] |= USB_PORT_STAT_SUSPEND; if (reg & EHCI_PS_OCA) tmpbuf[0] |= USB_PORT_STAT_OVERCURRENT; if (reg & EHCI_PS_PR) tmpbuf[0] |= USB_PORT_STAT_RESET; if (reg & EHCI_PS_PP) ...
All the struct are defined in u-boot and you can reuse that one, I think.
Regards Michael

Hi Tor,
Tor Krill tor@excito.com writes:
Add high speed support to USB code. Extracted from Juniper Networks patch.
I know that the mergewindow is closed but wanted to get feedback on these patches if possible.
Thanks for the patches! I just got back from holiday, so it'll take a couple of days for reviewing.
Thank you for your patience.
Viele Grüße / Best regards
Markus Klotzbücher
-- DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: +49-8142-66989-0 Fax: +49-8142-66989-80 Email: office@denx.de

Hi Tor,
Tor Krill tor@excito.com writes:
Add high speed support to USB code. Extracted from Juniper Networks patch.
I know that the mergewindow is closed but wanted to get feedback on these patches if possible.
Thanks again for fixing this up. AFAIK patches look fine, only two things:
- please address the issues pointed out by Michael, especially removal of the "C" macro.
- Secondly it would be nice if we could have a short README.ehci or similar which explains what works and what not, and what needs to be done to add further board/HC support.
Thank you in advance.
Best regards
Markus Klotzbücher
-- DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: +49-8142-66989-0 Fax: +49-8142-66989-80 Email: office@denx.de

On 7/10/2008, "Markus Klotzbücher" mk@denx.de wrote:
Hi Tor,
Hi Markus,
Tor Krill tor@excito.com writes:
Add high speed support to USB code. Extracted from Juniper Networks patch.
I know that the mergewindow is closed but wanted to get feedback on these patches if possible.
Thanks again for fixing this up. AFAIK patches look fine, only two things:
- please address the issues pointed out by Michael, especially removal
of the "C" macro.
- Secondly it would be nice if we could have a short README.ehci or
similar which explains what works and what not, and what needs to be done to add further board/HC support.
Thank you in advance.
Thank you for reviewing. I will try fix things up and resubmit later.
Best Regards,
/Tor

Hi Tor,
On Thu, Jul 10, 2008 at 05:22:18PM +0200, Tor Krill wrote:
On 7/10/2008, "Markus Klotzbücher" mk@denx.de wrote:
Tor Krill tor@excito.com writes:
Add high speed support to USB code. Extracted from Juniper Networks patch.
I know that the mergewindow is closed but wanted to get feedback on these patches if possible.
Thanks again for fixing this up. AFAIK patches look fine, only two things:
- please address the issues pointed out by Michael, especially removal
of the "C" macro.
- Secondly it would be nice if we could have a short README.ehci or
similar which explains what works and what not, and what needs to be done to add further board/HC support.
Thank you in advance.
Thank you for reviewing. I will try fix things up and resubmit later.
Any news about this? I'd very much like to get your patches merged...
Best regards Markus Klotzbücher
participants (3)
-
Markus Klotzbücher
-
michael
-
Tor Krill