[U-Boot] [RFC PATCH 0/1] Add ehci core
Remy Bohmer
linux at bohmer.net
Tue Nov 25 12:31:53 CET 2008
Hello Michael,
Thanks for inlining these patches... But you forgot at least a
Signed-off-by line...
And sorry... I have to mention that the code is also not very clean
related to Coding rules (see attached)
Can you please clean it up and make sure the build problems Markus
mentioned this morning are solved?
(Coding Style logging is generated by the Linux checkpatch.pl script)
Remy
2008/11/25 Michael Trimarchi <trimarchi at gandalf.sssup.it>:
> Add ehci core functionality
>
> ---
> common/cmd_usb.c | 3 +-
> drivers/usb/usb_ehci.h | 120 ++++++++
> drivers/usb/usb_ehci_core.c | 633 +++++++++++++++++++++++++++++++++++++++++++
> drivers/usb/usb_ehci_core.h | 29 ++
> include/usb.h | 15 +-
> include/usb_defs.h | 10 +
> 6 files changed, 803 insertions(+), 7 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/common/cmd_usb.c b/common/cmd_usb.c
> index 99e551f..532df37 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/drivers/usb/usb_ehci.h b/drivers/usb/usb_ehci.h
> new file mode 100644
> index 0000000..821082a
> --- /dev/null
> +++ b/drivers/usb/usb_ehci.h
> @@ -0,0 +1,120 @@
> +/*-
> + * 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
> +
> +/* (shifted) direction/type/recipient from the USB 2.0 spec, table 9.2 */
> +#define DeviceRequest \
> + ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
> +#define DeviceOutRequest \
> + ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
> +
> +#define InterfaceRequest \
> + ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
> +
> +#define EndpointRequest \
> + ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
> +#define EndpointOutRequest \
> + ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
> +
> +/*
> + * 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..ef947d5
> --- /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)));
> +
> +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_config_descriptor config = {
> + sizeof(struct usb_config_descriptor),
> + 2, /* bDescriptorType: UDESC_CONFIG */
> + sizeof(struct usb_config_descriptor) +
> + sizeof(struct usb_interface_descriptor) +
> + sizeof(struct usb_endpoint_descriptor),
> + 0,
> + 1, /* bNumInterface */
> + 1, /* bConfigurationValue */
> + 0, /* iConfiguration */
> + 0x40, /* bmAttributes: UC_SELF_POWER */
> + 0 /* bMaxPower */
> +};
> +
> +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 */
> +};
> +
> +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) < CONFIG_SYS_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);
> +}
> +
> +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;
> +
> + 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));
> +
> + typeReq = req->request << 8 | req->requesttype;
> +
> + switch (typeReq) {
> + case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
> + switch (le16_to_cpu (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 (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 USB_REQ_GET_DESCRIPTOR | ((USB_DIR_IN | USB_RT_HUB) << 8):
> + switch (le16_to_cpu (req->value) >> 8) {
> + case USB_DT_HUB:
> + srcptr = &hub;
> + srclen = sizeof(hub);
> + break;
> + default:
> + debug ("unknown value %x", le16_to_cpu (req->value));
> + goto unknown;
> + }
> + break;
> + case USB_REQ_SET_ADDRESS | (USB_RECIP_DEVICE << 8):
> + rootdev = le16_to_cpu (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 = 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 DeviceOutRequest | USB_REQ_SET_FEATURE:
> + 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 DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
> + 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;
> + }
> +
> + 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);
> + hub.bNbrPorts = reg & 0xf;
> + if (reg & 0x10000) /* Port Indicators */
> + hub.wHubCharacteristics |= 0x80;
> + if (reg & 0x10) /* Port Power Control */
> + hub.wHubCharacteristics |= 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..39e5c5e
> --- /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
> diff --git a/include/usb.h b/include/usb.h
> index 9a2e72c..840e657 100644
> --- a/include/usb.h
> +++ b/include/usb.h
> @@ -139,7 +139,7 @@ enum {
>
> 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 */
> @@ -171,6 +171,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];
> };
> @@ -180,6 +181,7 @@ struct usb_device {
> */
>
> #if defined(CONFIG_USB_UHCI) || defined(CONFIG_USB_OHCI) || \
> + defined(CONFIG_USB_EHCI) || \
> defined(CONFIG_USB_OHCI_NEW) || defined (CONFIG_USB_SL811HS) || \
> defined(CONFIG_USB_ISP116X_HCD) || defined(CONFIG_USB_R8A66597_HCD)
>
> @@ -274,7 +276,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)
> *
> @@ -290,7 +292,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
> @@ -300,8 +302,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)
> @@ -333,7 +335,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
> --
> 1.5.6.5
>
>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: ehci-uboot-checkpatch.log
Type: application/octet-stream
Size: 33430 bytes
Desc: not available
Url : http://lists.denx.de/pipermail/u-boot/attachments/20081125/b8898ffb/attachment-0001.obj
More information about the U-Boot
mailing list