From aaf098cfeed04595d4c5100ffd39095d79edbf90 Mon Sep 17 00:00:00 2001 From: Michael Trimarchi Date: Fri, 28 Nov 2008 13:20:46 +0100 Subject: USB ehci core support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add USB ehci core support Signed-off-by: Michael Trimarchi Signed-off-by: Remy Böhmer --- drivers/usb/usb_ehci_core.c | 634 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 634 insertions(+) create mode 100644 drivers/usb/usb_ehci_core.c (limited to 'drivers/usb/usb_ehci_core.c') diff --git a/drivers/usb/usb_ehci_core.c b/drivers/usb/usb_ehci_core.c new file mode 100644 index 0000000..765b5dd --- /dev/null +++ b/drivers/usb/usb_ehci_core.c @@ -0,0 +1,634 @@ +/*- + * 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 +#include +#include +#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; + 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"); + 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; +} -- cgit v1.1 From db63299b1dd2894ade542278210bccd046de6435 Mon Sep 17 00:00:00 2001 From: michael Date: Wed, 10 Dec 2008 17:55:19 +0100 Subject: [PATCH] Fix EHCI usb. I start to test on a IXP465 board and I find some errors in the code. This patch fix: - descriptor initizialization (config, interface and endpoint must be one next-to the other when the USB_DT_CONFIG message is send. - FIX little/endian bigendian (introduce the CONFIG_EHCI_DESC_BIG_ENDIAN and the CONFIG_EHCI_MMIO_BIG_ENDIAN) - Introduce the linux version of the usb_config_descriptor and usb_interface descriptor. This descriptor does't contains u-boot extension. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michael Trimarchi Signed-off-by: Remy Böhmer --- drivers/usb/usb_ehci_core.c | 343 ++++++++++++++++++++++++-------------------- 1 file changed, 185 insertions(+), 158 deletions(-) (limited to 'drivers/usb/usb_ehci_core.c') diff --git a/drivers/usb/usb_ehci_core.c b/drivers/usb/usb_ehci_core.c index 765b5dd..869bea3 100644 --- a/drivers/usb/usb_ehci_core.c +++ b/drivers/usb/usb_ehci_core.c @@ -1,6 +1,6 @@ /*- * Copyright (c) 2007-2008, Juniper Networks, Inc. - * Copyright (c) 2008, Excito Elektronik i Skåne AB + * Copyright (c) 2008, Excito Elektronik i SkÃ¥ne AB * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -18,10 +18,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ - +#define DEBUG #include +#include #include #include +#include #include "usb_ehci.h" int rootdev; @@ -31,67 +33,75 @@ 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 */ +static struct descriptor { + struct usb_hub_descriptor hub; + struct usb_device_descriptor device; + struct usb_linux_config_descriptor config; + struct usb_linux_interface_descriptor interface; + struct usb_endpoint_descriptor endpoint; +} __attribute__ ((packed)) descriptor = { + { + 0x8, /* bDescLength */ + 0x29, /* bDescriptorType: hub descriptor */ + 2, /* bNrPorts -- runtime modified */ + 0, /* wHubCharacteristics */ + 0xff, /* bPwrOn2PwrGood */ + 0, /* bHubCntrCurrent */ + {}, /* Device removable */ + {} /* at most 7 ports! XXX */ + }, + { + 0x12, /* 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 */ + }, + { + 0x9, + 2, /* bDescriptorType: UDESC_CONFIG */ + cpu_to_le16(0x19), + 1, /* bNumInterface */ + 1, /* bConfigurationValue */ + 0, /* iConfiguration */ + 0x40, /* bmAttributes: UC_SELF_POWER */ + 0 /* bMaxPower */ + }, + { + 0x9, /* bLength */ + 4, /* bDescriptorType: UDESC_INTERFACE */ + 0, /* bInterfaceNumber */ + 0, /* bAlternateSetting */ + 1, /* bNumEndpoints */ + 9, /* bInterfaceClass: UICLASS_HUB */ + 0, /* bInterfaceSubClass: UISUBCLASS_HUB */ + 0, /* bInterfaceProtocol: UIPROTO_HSHUBSTT */ + 0 /* iInterface */ + }, + { + 0x7, /* bLength */ + 5, /* bDescriptorType: UDESC_ENDPOINT */ + 0x81, /* bEndpointAddress: + * 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_free (void *p, size_t sz) +{ +} static void *ehci_alloc(size_t sz, size_t align) { @@ -107,14 +117,14 @@ static void *ehci_alloc(size_t sz, size_t align) break; case sizeof(struct qTD): if (ntds == 3) { - debug("out of TDs"); + debug("out of TDs\n"); return NULL; } p = &td[ntds]; ntds++; break; default: - debug("unknown allocation size"); + debug("unknown allocation size\n"); return NULL; } @@ -122,10 +132,6 @@ static void *ehci_alloc(size_t sz, size_t align) 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; @@ -134,7 +140,7 @@ static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz) addr = (uint32_t) buf; idx = 0; while (idx < 5) { - td->qt_buffer[idx] = cpu_to_le32(addr); + td->qt_buffer[idx] = cpu_to_hc32(addr); next = (addr + 4096) & ~4095; delta = next - addr; if (delta >= sz) @@ -145,7 +151,7 @@ static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz) } if (idx == 5) { - debug("out of buffer pointers (%u bytes left)", sz); + debug("out of buffer pointers (%u bytes left)\n", sz); return -1; } @@ -163,22 +169,23 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, uint32_t *tdp; uint32_t endpt, token, usbsts; uint32_t c, toggle; + uint32_t cmd; - debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p", dev, pipe, + debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\n", dev, pipe, buffer, length, req); if (req != NULL) - debug("req=%u (%#x), type=%u (%#x), value=%u (%#x), index=%u", + debug("req=%u (%#x), type=%u (%#x), value=%u (%#x), index=%u\n", req->request, req->request, req->requesttype, req->requesttype, le16_to_cpu(req->value), le16_to_cpu(req->value), - le16_to_cpu(req->index), 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"); + debug("unable to allocate QH\n"); return -1; } - qh->qh_link = cpu_to_le32((uint32_t)&qh_list | QH_LINK_TYPE_QH); + qh->qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); c = (usb_pipespeed(pipe) != USB_SPEED_HIGH && usb_pipeendpoint(pipe) == 0) ? 1 : 0; endpt = (8 << 28) | @@ -189,13 +196,13 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, (usb_pipespeed(pipe) << 12) | (usb_pipeendpoint(pipe) << 8) | (0 << 7) | (usb_pipedevice(pipe) << 0); - qh->qh_endpt1 = cpu_to_le32(endpt); + qh->qh_endpt1 = cpu_to_hc32(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); + qh->qh_endpt2 = cpu_to_hc32(endpt); + qh->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + qh->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); td = NULL; tdp = &qh->qh_overlay.qt_next; @@ -206,21 +213,21 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, if (req != NULL) { td = ehci_alloc(sizeof(struct qTD), 32); if (td == NULL) { - debug("unable to allocate SETUP td"); + debug("unable to allocate SETUP td\n"); goto fail; } - td->qt_next = cpu_to_le32(QT_NEXT_TERMINATE); - td->qt_altnext = cpu_to_le32(QT_NEXT_TERMINATE); + td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + td->qt_altnext = cpu_to_hc32(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); + td->qt_token = cpu_to_hc32(token); if (ehci_td_buffer(td, req, sizeof(*req)) != 0) { - debug("unable construct SETUP td"); + debug("unable construct SETUP td\n"); ehci_free(td, sizeof(*td)); goto fail; } - *tdp = cpu_to_le32((uint32_t) td); + *tdp = cpu_to_hc32((uint32_t) td); tdp = &td->qt_next; toggle = 1; } @@ -228,75 +235,80 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, if (length > 0 || req == NULL) { td = ehci_alloc(sizeof(struct qTD), 32); if (td == NULL) { - debug("unable to allocate DATA td"); + debug("unable to allocate DATA td\n"); goto fail; } - td->qt_next = cpu_to_le32(QT_NEXT_TERMINATE); - td->qt_altnext = cpu_to_le32(QT_NEXT_TERMINATE); + td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + td->qt_altnext = cpu_to_hc32(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); + td->qt_token = cpu_to_hc32(token); if (ehci_td_buffer(td, buffer, length) != 0) { - debug("unable construct DATA td"); + debug("unable construct DATA td\n"); ehci_free(td, sizeof(*td)); goto fail; } - *tdp = cpu_to_le32((uint32_t) td); + *tdp = cpu_to_hc32((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"); + debug("unable to allocate ACK td\n"); goto fail; } - td->qt_next = cpu_to_le32(QT_NEXT_TERMINATE); - td->qt_altnext = cpu_to_le32(QT_NEXT_TERMINATE); + td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + td->qt_altnext = cpu_to_hc32(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); + td->qt_token = cpu_to_hc32(token); + *tdp = cpu_to_hc32((uint32_t) td); tdp = &td->qt_next; } - qh_list.qh_link = cpu_to_le32((uint32_t) qh | QH_LINK_TYPE_QH); + qh_list.qh_link = cpu_to_hc32((uint32_t) qh | QH_LINK_TYPE_QH); - usbsts = le32_to_cpu(hcor->or_usbsts); - hcor->or_usbsts = cpu_to_le32(usbsts & 0x3f); + usbsts = ehci_readl(hcor->or_usbsts); + ehci_writel(hcor->or_usbsts, (usbsts & 0x3f)); /* Enable async. schedule. */ - hcor->or_usbcmd |= cpu_to_le32(0x20); - while ((hcor->or_usbsts & cpu_to_le32(0x8000)) == 0) + cmd = ehci_readl(hcor->or_usbcmd); + hcor->or_usbcmd |= CMD_ASE; + ehci_writel(hcor->or_usbcmd, cmd); + + while ((ehci_readl(hcor->or_usbsts) & STD_ASS) == 0) udelay(1); /* Wait for TDs to be processed. */ ts = get_timer(0); vtd = td; do { - token = le32_to_cpu(vtd->qt_token); + token = hc32_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) + cmd = ehci_readl(hcor->or_usbcmd); + cmd &= ~CMD_ASE; + ehci_writel(hcor->or_usbcmd, cmd); + while ((ehci_readl(hcor->or_usbsts) & STD_ASS) != 0) udelay(1); - qh_list.qh_link = cpu_to_le32((uint32_t)&qh_list | QH_LINK_TYPE_QH); + qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); - token = le32_to_cpu(qh->qh_overlay.qt_token); + token = hc32_to_cpu(qh->qh_overlay.qt_token); if (!(token & 0x80)) { - debug("TOKEN=%#x", token); + debug("TOKEN=%#x\n", token); switch (token & 0xfc) { case 0: toggle = token >> 31; @@ -322,20 +334,20 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, 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])); + debug("dev=%u, usbsts=%#x, p[1]=%#x, p[2]=%#x\n", + dev->devnum, ehci_readl(hcor->or_usbsts), + ehci_readl(hcor->or_portsc[0]), + ehci_readl(hcor->or_portsc[1])); } return (dev->status != USB_ST_NOT_PROC) ? 0 : -1; fail: - td = (void *)le32_to_cpu(qh->qh_overlay.qt_next); + td = (void *)hc32_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); + td = (void *)hc32_to_cpu(qh->qh_overlay.qt_next); } ehci_free(qh, sizeof(*qh)); return -1; @@ -351,39 +363,40 @@ static inline int min3(int a, int b, int c) return a; } +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; + void *srcptr = NULL; int len, srclen; uint32_t reg; srclen = 0; - srcptr = NULL; - debug("req=%u (%#x), type=%u (%#x), value=%u, index=%u", + debug("req=%u (%#x), type=%u (%#x), value=%u, index=%u\n", 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) { + switch (le16_to_cpu(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); + debug("USB_DT_DEVICE request\n"); + srcptr = &descriptor.device; + srclen = 0x12; break; case USB_DT_CONFIG: - srcptr = &config; - srclen = sizeof(config) + - sizeof(struct usb_interface_descriptor) + - sizeof(struct usb_hub_descriptor); + debug("USB_DT_CONFIG config\n"); + srcptr = &descriptor.config; + srclen = 0x19; break; case USB_DT_STRING: + debug("USB_DT_STRING config\n"); switch (le16_to_cpu(req->value) & 0xff) { case 0: /* Language */ srcptr = "\4\3\1\0"; @@ -400,29 +413,34 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, srclen = 42; break; default: + debug("unknown value DT_STRING %x\n", + le16_to_cpu(req->value)); goto unknown; } break; default: - debug("unknown value %x", le16_to_cpu(req->value)); + debug("unknown value %x\n", le16_to_cpu(req->value)); goto unknown; } break; case USB_REQ_GET_DESCRIPTOR | ((USB_DIR_IN | USB_RT_HUB) << 8): switch (le16_to_cpu(req->value) >> 8) { case USB_DT_HUB: - srcptr = &hub; - srclen = sizeof(hub); + debug("USB_DT_HUB config\n"); + srcptr = &descriptor.hub; + srclen = 0x8; break; default: - debug("unknown value %x", le16_to_cpu(req->value)); + debug("unknown value %x\n", le16_to_cpu(req->value)); goto unknown; } break; case USB_REQ_SET_ADDRESS | (USB_RECIP_DEVICE << 8): + debug("USB_REQ_SET_ADDRESS\n"); rootdev = le16_to_cpu(req->value); break; case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + debug("USB_REQ_SET_CONFIGURATION\n"); /* Nothing to do */ break; case USB_REQ_GET_STATUS | ((USB_DIR_IN | USB_RT_HUB) << 8): @@ -431,9 +449,9 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, srcptr = tmpbuf; srclen = 2; break; - case DeviceRequest | USB_REQ_GET_STATUS: + case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8): memset(tmpbuf, 0, 4); - reg = le32_to_cpu(hcor->or_portsc[le16_to_cpu(req->index) + reg = ehci_readl(hcor->or_portsc[le16_to_cpu(req->index) - 1]); if (reg & EHCI_PS_CS) tmpbuf[0] |= USB_PORT_STAT_CONNECTION; @@ -460,14 +478,15 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, srcptr = tmpbuf; srclen = 4; break; - case DeviceOutRequest | USB_REQ_SET_FEATURE: - reg = le32_to_cpu(hcor->or_portsc[le16_to_cpu(req->index) - 1]); + case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): + reg = ehci_readl(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: + debug("USB FEAT RESET\n"); if (EHCI_PS_IS_LOWSPEED(reg)) { /* Low speed device, give up ownership. */ reg |= EHCI_PS_PO; @@ -476,8 +495,8 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, /* Start reset sequence. */ reg &= ~EHCI_PS_PE; reg |= EHCI_PS_PR; - hcor->or_portsc[le16_to_cpu(req->index) - 1] = - cpu_to_le32(reg); + ehci_writel(hcor->or_portsc[ + le16_to_cpu(req->index) - 1], reg); /* Wait for reset to complete. */ udelay(500000); /* Terminate reset sequence. */ @@ -486,12 +505,12 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, * manual setting of port enable? */ reg |= EHCI_PS_PE; - hcor->or_portsc[le16_to_cpu(req->index) - 1] = - cpu_to_le32(reg); + ehci_writel(hcor->or_portsc[ + le16_to_cpu(req->index) - 1], reg); /* Wait for HC to complete reset. */ udelay(2000); reg = - le32_to_cpu(hcor->or_portsc[le16_to_cpu(req->index) + ehci_readl(hcor->or_portsc[le16_to_cpu(req->index) - 1]); reg &= ~EHCI_PS_CLEAR; if ((reg & EHCI_PS_PE) == 0) { @@ -503,14 +522,13 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, portreset |= 1 << le16_to_cpu(req->index); break; default: - debug("unknown feature %x", le16_to_cpu(req->value)); + debug("unknown feature %x\n", le16_to_cpu(req->value)); goto unknown; } - hcor->or_portsc[le16_to_cpu(req->index) - 1] = - cpu_to_le32(reg); + ehci_writel(hcor->or_portsc[le16_to_cpu(req->index) - 1], reg); break; - case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: - reg = le32_to_cpu(hcor->or_portsc[le16_to_cpu(req->index) - 1]); + case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): + reg = ehci_readl(hcor->or_portsc[le16_to_cpu(req->index) - 1]); reg &= ~EHCI_PS_CLEAR; switch (le16_to_cpu(req->value)) { case USB_PORT_FEAT_ENABLE: @@ -523,26 +541,29 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, portreset &= ~(1 << le16_to_cpu(req->index)); break; default: - debug("unknown feature %x", le16_to_cpu(req->value)); + debug("unknown feature %x\n", le16_to_cpu(req->value)); goto unknown; } - hcor->or_portsc[le16_to_cpu(req->index) - 1] = - cpu_to_le32(reg); + ehci_writel(hcor->or_portsc[le16_to_cpu(req->index) - 1], reg); break; default: - debug("Unknown request"); + debug("Unknown request\n"); goto unknown; } + wait_ms(1); len = min3(srclen, le16_to_cpu(req->length), length); if (srcptr != NULL && len > 0) memcpy(buffer, srcptr, len); + else + debug("Len is 0\n"); + dev->act_len = len; dev->status = 0; return 0; unknown: - debug("requesttype=%x, request=%x, value=%x, index=%x, length=%x", + debug("requesttype=%x, request=%x, value=%x, index=%x, length=%x\n", req->requesttype, req->request, le16_to_cpu(req->value), le16_to_cpu(req->index), le16_to_cpu(req->length)); @@ -559,34 +580,40 @@ int usb_lowlevel_stop(void) int usb_lowlevel_init(void) { uint32_t reg; + uint32_t cmd; 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); + qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); + qh_list.qh_endpt1 = cpu_to_hc32((1 << 15) | (USB_SPEED_HIGH << 12)); + qh_list.qh_curtd = cpu_to_hc32(QT_NEXT_TERMINATE); + qh_list.qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + qh_list.qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + qh_list.qh_overlay.qt_token = cpu_to_hc32(0x40); /* Set async. queue head pointer. */ - hcor->or_asynclistaddr = cpu_to_le32((uint32_t)&qh_list); + ehci_writel(hcor->or_asynclistaddr, (uint32_t)&qh_list); - reg = le32_to_cpu(hccr->cr_hcsparams); - hub.bNbrPorts = reg & 0xf; + reg = ehci_readl(hccr->cr_hcsparams); + descriptor.hub.bNbrPorts = reg & 0xf; + printf("NbrPorts %x\n", descriptor.hub.bNbrPorts); if (reg & 0x10000) /* Port Indicators */ - hub.wHubCharacteristics |= 0x80; + descriptor.hub.wHubCharacteristics |= 0x80; if (reg & 0x10) /* Port Power Control */ - hub.wHubCharacteristics |= 0x01; + descriptor.hub.wHubCharacteristics |= 0x01; /* take control over the ports */ - hcor->or_configflag |= cpu_to_le32(1); + cmd = ehci_readl(hcor->or_configflag); + cmd |= 1; + ehci_writel(hcor->or_configflag, cmd); /* Start the host controller. */ - hcor->or_usbcmd |= cpu_to_le32(1); + cmd = ehci_readl(hcor->or_configflag); + cmd |= 1; + ehci_writel(hcor->or_usbcmd, cmd); rootdev = 0; -- cgit v1.1 From 51ab142b8b546d5e627b2c8c36d0adae222565f7 Mon Sep 17 00:00:00 2001 From: michael Date: Thu, 11 Dec 2008 13:43:55 +0100 Subject: [PATCH] This patch add varius fix to the ehci. - fix ehci_readl, ehci_writel - introduce new define in ehci.h - introduce the handshake function for waiting on a register - fix usb_ehci_fsl with the new HC_LENGTH macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michael Trimarchi Signed-off-by: Remy Böhmer --- drivers/usb/usb_ehci_core.c | 147 ++++++++++++++++++++++++++++++++------------ 1 file changed, 109 insertions(+), 38 deletions(-) (limited to 'drivers/usb/usb_ehci_core.c') diff --git a/drivers/usb/usb_ehci_core.c b/drivers/usb/usb_ehci_core.c index 869bea3..ec50874 100644 --- a/drivers/usb/usb_ehci_core.c +++ b/drivers/usb/usb_ehci_core.c @@ -99,8 +99,55 @@ static struct descriptor { }, }; -static void ehci_free (void *p, size_t sz) +static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int msec) { + uint32_t result; + do { + result = ehci_readl(ptr); + debug("handshake read reg(%x)=%x\n", (uint32_t)ptr, result); + if (result == ~(uint32_t)0) + return -1; + result &= mask; + if (result == done) + return 0; + wait_ms(1); + msec--; + } while (msec > 0); + return -1; +} + +static void ehci_free(void *p, size_t sz) +{ + +} + +static int ehci_reset(void) +{ + uint32_t cmd; + uint32_t tmp; + uint32_t *reg_ptr; + int ret = 0; + + cmd = ehci_readl(&hcor->or_usbcmd); + cmd |= CMD_RESET; + ehci_writel(&hcor->or_usbcmd, cmd); + ret = handshake(&hcor->or_usbcmd, CMD_RESET, 0, 250); + if (ret < 0) { + printf("EHCI fail to reset\n"); + goto out; + } + +#if defined CONFIG_EHCI_IS_TDI + reg_ptr = (uint32_t *)((u8 *)hcor + USBMODE); + tmp = ehci_readl(reg_ptr); + tmp |= USBMODE_CM_HC; +#if defined CONFIG_EHCI_MMIO_BIG_ENDIAN + tmp |= USBMODE_BE; +#endif + ehci_writel(reg_ptr, tmp); +#endif +out: + return ret; } static void *ehci_alloc(size_t sz, size_t align) @@ -170,6 +217,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, uint32_t endpt, token, usbsts; uint32_t c, toggle; uint32_t cmd; + uint32_t sts; debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\n", dev, pipe, buffer, length, req); @@ -277,16 +325,19 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, qh_list.qh_link = cpu_to_hc32((uint32_t) qh | QH_LINK_TYPE_QH); - usbsts = ehci_readl(hcor->or_usbsts); - ehci_writel(hcor->or_usbsts, (usbsts & 0x3f)); + usbsts = ehci_readl(&hcor->or_usbsts); + ehci_writel(&hcor->or_usbsts, (usbsts & 0x3f)); /* Enable async. schedule. */ - cmd = ehci_readl(hcor->or_usbcmd); - hcor->or_usbcmd |= CMD_ASE; - ehci_writel(hcor->or_usbcmd, cmd); - - while ((ehci_readl(hcor->or_usbsts) & STD_ASS) == 0) - udelay(1); + cmd = ehci_readl(&hcor->or_usbcmd); + cmd |= CMD_ASE; + ehci_writel(&hcor->or_usbcmd, cmd); + + sts = ehci_readl(&hcor->or_usbsts); + while ((sts & STD_ASS) == 0) { + sts = ehci_readl(&hcor->or_usbsts); + udelay(10); + } /* Wait for TDs to be processed. */ ts = get_timer(0); @@ -298,11 +349,15 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, } while (get_timer(ts) < CONFIG_SYS_HZ); /* Disable async schedule. */ - cmd = ehci_readl(hcor->or_usbcmd); + cmd = ehci_readl(&hcor->or_usbcmd); cmd &= ~CMD_ASE; - ehci_writel(hcor->or_usbcmd, cmd); - while ((ehci_readl(hcor->or_usbsts) & STD_ASS) != 0) - udelay(1); + ehci_writel(&hcor->or_usbcmd, cmd); + + sts = ehci_readl(&hcor->or_usbsts); + while ((sts & STD_ASS) != 0) { + sts = ehci_readl(&hcor->or_usbsts); + udelay(10); + } qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); @@ -335,9 +390,9 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, } else { dev->act_len = 0; debug("dev=%u, usbsts=%#x, p[1]=%#x, p[2]=%#x\n", - dev->devnum, ehci_readl(hcor->or_usbsts), - ehci_readl(hcor->or_portsc[0]), - ehci_readl(hcor->or_portsc[1])); + dev->devnum, ehci_readl(&hcor->or_usbsts), + ehci_readl(&hcor->or_portsc[0]), + ehci_readl(&hcor->or_portsc[1])); } return (dev->status != USB_ST_NOT_PROC) ? 0 : -1; @@ -451,7 +506,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, break; case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8): memset(tmpbuf, 0, 4); - reg = ehci_readl(hcor->or_portsc[le16_to_cpu(req->index) + reg = ehci_readl(&hcor->or_portsc[le16_to_cpu(req->index) - 1]); if (reg & EHCI_PS_CS) tmpbuf[0] |= USB_PORT_STAT_CONNECTION; @@ -479,9 +534,12 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, srclen = 4; break; case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): - reg = ehci_readl(hcor->or_portsc[le16_to_cpu(req->index) - 1]); + reg = ehci_readl(&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_POWER: reg |= EHCI_PS_PP; break; @@ -495,22 +553,22 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, /* Start reset sequence. */ reg &= ~EHCI_PS_PE; reg |= EHCI_PS_PR; - ehci_writel(hcor->or_portsc[ + ehci_writel(&hcor->or_portsc[ le16_to_cpu(req->index) - 1], reg); /* Wait for reset to complete. */ - udelay(500000); + wait_ms(500); /* 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; - ehci_writel(hcor->or_portsc[ + ehci_writel(&hcor->or_portsc[ le16_to_cpu(req->index) - 1], reg); /* Wait for HC to complete reset. */ - udelay(2000); + wait_ms(10); reg = - ehci_readl(hcor->or_portsc[le16_to_cpu(req->index) + ehci_readl(&hcor->or_portsc[le16_to_cpu(req->index) - 1]); reg &= ~EHCI_PS_CLEAR; if ((reg & EHCI_PS_PE) == 0) { @@ -525,10 +583,10 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, debug("unknown feature %x\n", le16_to_cpu(req->value)); goto unknown; } - ehci_writel(hcor->or_portsc[le16_to_cpu(req->index) - 1], reg); + ehci_writel(&hcor->or_portsc[le16_to_cpu(req->index) - 1], reg); break; case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): - reg = ehci_readl(hcor->or_portsc[le16_to_cpu(req->index) - 1]); + reg = ehci_readl(&hcor->or_portsc[le16_to_cpu(req->index) - 1]); reg &= ~EHCI_PS_CLEAR; switch (le16_to_cpu(req->value)) { case USB_PORT_FEAT_ENABLE: @@ -537,6 +595,9 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, case USB_PORT_FEAT_C_CONNECTION: reg |= EHCI_PS_CSC; break; + case USB_PORT_FEAT_OVER_CURRENT: + reg |= EHCI_PS_OCC; + break; case USB_PORT_FEAT_C_RESET: portreset &= ~(1 << le16_to_cpu(req->index)); break; @@ -544,7 +605,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, debug("unknown feature %x\n", le16_to_cpu(req->value)); goto unknown; } - ehci_writel(hcor->or_portsc[le16_to_cpu(req->index) - 1], reg); + ehci_writel(&hcor->or_portsc[le16_to_cpu(req->index) - 1], reg); break; default: debug("Unknown request\n"); @@ -585,6 +646,10 @@ int usb_lowlevel_init(void) if (ehci_hcd_init() != 0) return -1; + /* EHCI spec section 4.1 */ + if (ehci_reset() != 0) + return -1; + /* Set head of reclaim list */ memset(&qh_list, 0, sizeof(qh_list)); qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); @@ -595,25 +660,31 @@ int usb_lowlevel_init(void) qh_list.qh_overlay.qt_token = cpu_to_hc32(0x40); /* Set async. queue head pointer. */ - ehci_writel(hcor->or_asynclistaddr, (uint32_t)&qh_list); + ehci_writel(&hcor->or_asynclistaddr, (uint32_t)&qh_list); - reg = ehci_readl(hccr->cr_hcsparams); - descriptor.hub.bNbrPorts = reg & 0xf; - printf("NbrPorts %x\n", descriptor.hub.bNbrPorts); + reg = ehci_readl(&hccr->cr_hcsparams); + descriptor.hub.bNbrPorts = HCS_N_PORTS(reg); + printf("Register %x NbrPorts %d\n", reg, descriptor.hub.bNbrPorts); if (reg & 0x10000) /* Port Indicators */ descriptor.hub.wHubCharacteristics |= 0x80; if (reg & 0x10) /* Port Power Control */ descriptor.hub.wHubCharacteristics |= 0x01; - /* take control over the ports */ - cmd = ehci_readl(hcor->or_configflag); - cmd |= 1; - ehci_writel(hcor->or_configflag, cmd); - /* Start the host controller. */ - cmd = ehci_readl(hcor->or_configflag); - cmd |= 1; - ehci_writel(hcor->or_usbcmd, cmd); + cmd = ehci_readl(&hcor->or_usbcmd); + /* Philips, Intel, and maybe others need CMD_RUN before the + * root hub will detect new devices (why?); NEC doesn't */ + cmd &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); + cmd |= CMD_RUN; + ehci_writel(&hcor->or_usbcmd, cmd); + + /* take control over the ports */ + cmd = ehci_readl(&hcor->or_configflag); + cmd |= FLAG_CF; + ehci_writel(&hcor->or_configflag, cmd); + /* unblock posted writes */ + cmd = ehci_readl(&hcor->or_usbcmd); + wait_ms(5); rootdev = 0; -- cgit v1.1 From c0d722fe7ee1cb452dfd9246419188b3f6d9c4df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Remy=20B=C3=B6hmer?= Date: Sat, 13 Dec 2008 22:51:58 +0100 Subject: EHCI fix code and ixp4xx test. USB ehci configuration parameter: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #define CONFIG_CMD_USB 1 #define CONFIG_USB_STORAGE 1 #define CONFIG_USB_EHCI #define CONFIG_USB_EHCI_IXP4XX 1 #define CONFIG_EHCI_IS_TDI 1 #define CONFIG_EHCI_DESC_BIG_ENDIAN 1 #define CONFIG_EHCI_MMIO_BIG_ENDIAN 1 #define CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS 2 #define CONFIG_LEGACY_USB_INIT_SEQ 1 2 USB Device(s) found scanning bus for storage devices... 0 Storage Device(s) found => usb tree Device Tree: 1 Hub (1.5MBit/s, 0mA) | u-boot EHCI Host Controller | |+-2 Mass Storage (12MBit/s, 100mA) Sony Storage Media 0C07040930296 => Signed-off-by: Michael Trimarchi Signed-off-by: Remy Böhmer --- drivers/usb/usb_ehci_core.c | 127 +++++++++++++++++++++++++++----------------- 1 file changed, 78 insertions(+), 49 deletions(-) (limited to 'drivers/usb/usb_ehci_core.c') diff --git a/drivers/usb/usb_ehci_core.c b/drivers/usb/usb_ehci_core.c index ec50874..07e9a6b 100644 --- a/drivers/usb/usb_ehci_core.c +++ b/drivers/usb/usb_ehci_core.c @@ -1,6 +1,8 @@ /*- * Copyright (c) 2007-2008, Juniper Networks, Inc. * Copyright (c) 2008, Excito Elektronik i SkÃ¥ne AB + * Copyright (c) 2008, Michael Trimarchi + * * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -18,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ -#define DEBUG #include #include #include @@ -99,6 +100,12 @@ static struct descriptor { }, }; +#if defined(CONFIG_EHCI_IS_TDI) +#define ehci_is_TDI() (1) +#else +#define ehci_is_TDI() (0) +#endif + static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int msec) { uint32_t result; @@ -131,17 +138,17 @@ static int ehci_reset(void) cmd = ehci_readl(&hcor->or_usbcmd); cmd |= CMD_RESET; ehci_writel(&hcor->or_usbcmd, cmd); - ret = handshake(&hcor->or_usbcmd, CMD_RESET, 0, 250); + ret = handshake((uint32_t *)&hcor->or_usbcmd, CMD_RESET, 0, 250); if (ret < 0) { printf("EHCI fail to reset\n"); goto out; } -#if defined CONFIG_EHCI_IS_TDI +#if defined(CONFIG_EHCI_IS_TDI) reg_ptr = (uint32_t *)((u8 *)hcor + USBMODE); tmp = ehci_readl(reg_ptr); tmp |= USBMODE_CM_HC; -#if defined CONFIG_EHCI_MMIO_BIG_ENDIAN +#if defined(CONFIG_EHCI_MMIO_BIG_ENDIAN) tmp |= USBMODE_BE; #endif ehci_writel(reg_ptr, tmp); @@ -427,7 +434,15 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, void *srcptr = NULL; int len, srclen; uint32_t reg; + uint32_t *status_reg; + if (le16_to_cpu(req->index) >= CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) { + printf("The request port(%d) is not configured\n", + le16_to_cpu(req->index) - 1); + return -1; + } + status_reg = (uint32_t *)&hcor->or_portsc[ + le16_to_cpu(req->index) - 1]; srclen = 0; debug("req=%u (%#x), type=%u (%#x), value=%u, index=%u\n", @@ -506,8 +521,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, break; case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8): memset(tmpbuf, 0, 4); - reg = ehci_readl(&hcor->or_portsc[le16_to_cpu(req->index) - - 1]); + reg = ehci_readl(status_reg); if (reg & EHCI_PS_CS) tmpbuf[0] |= USB_PORT_STAT_CONNECTION; if (reg & EHCI_PS_PE) @@ -516,8 +530,19 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, 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_PR && + (portreset & (1 << le16_to_cpu(req->index)))) { + int ret; + /* force reset to complete */ + reg = reg & ~(EHCI_PS_PR | EHCI_PS_CLEAR); + ehci_writel(status_reg, reg); + ret = handshake(status_reg, EHCI_PS_PR, 0, 2); + if (!ret) + tmpbuf[0] |= USB_PORT_STAT_RESET; + else + printf("port(%d) reset error\n", + le16_to_cpu(req->index) - 1); + } if (reg & EHCI_PS_PP) tmpbuf[1] |= USB_PORT_STAT_POWER >> 8; tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8; @@ -530,73 +555,71 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, 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 USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): - reg = ehci_readl(&hcor->or_portsc[le16_to_cpu(req->index) - 1]); + reg = ehci_readl(status_reg); reg &= ~EHCI_PS_CLEAR; switch (le16_to_cpu(req->value)) { case USB_PORT_FEAT_ENABLE: reg |= EHCI_PS_PE; + ehci_writel(status_reg, reg); break; case USB_PORT_FEAT_POWER: - reg |= EHCI_PS_PP; + if (HCS_PPC(ehci_readl(&hccr->cr_hcsparams))) { + reg |= EHCI_PS_PP; + ehci_writel(status_reg, reg); + } break; case USB_PORT_FEAT_RESET: - debug("USB FEAT RESET\n"); - if (EHCI_PS_IS_LOWSPEED(reg)) { + if ((reg & (EHCI_PS_PE | EHCI_PS_CS)) == EHCI_PS_CS && + !ehci_is_TDI() && + EHCI_PS_IS_LOWSPEED(reg)) { /* Low speed device, give up ownership. */ + debug("port %d low speed --> companion\n", + req->index - 1); reg |= EHCI_PS_PO; + ehci_writel(status_reg, reg); break; + } else { + reg |= EHCI_PS_PR; + reg &= ~EHCI_PS_PE; + ehci_writel(status_reg, reg); + /* + * caller must wait, then call GetPortStatus + * usb 2.0 specification say 50 ms resets on + * root + */ + wait_ms(50); + portreset |= 1 << le16_to_cpu(req->index); } - /* Start reset sequence. */ - reg &= ~EHCI_PS_PE; - reg |= EHCI_PS_PR; - ehci_writel(&hcor->or_portsc[ - le16_to_cpu(req->index) - 1], reg); - /* Wait for reset to complete. */ - wait_ms(500); - /* 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; - ehci_writel(&hcor->or_portsc[ - le16_to_cpu(req->index) - 1], reg); - /* Wait for HC to complete reset. */ - wait_ms(10); - reg = - ehci_readl(&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\n", le16_to_cpu(req->value)); goto unknown; } - ehci_writel(&hcor->or_portsc[le16_to_cpu(req->index) - 1], reg); + /* unblock posted writes */ + ehci_readl(&hcor->or_usbcmd); break; case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): - reg = ehci_readl(&hcor->or_portsc[le16_to_cpu(req->index) - 1]); - reg &= ~EHCI_PS_CLEAR; + reg = ehci_readl(status_reg); switch (le16_to_cpu(req->value)) { case USB_PORT_FEAT_ENABLE: reg &= ~EHCI_PS_PE; break; + case USB_PORT_FEAT_C_ENABLE: + reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_PE; + break; + case USB_PORT_FEAT_POWER: + if (HCS_PPC(ehci_readl(&hccr->cr_hcsparams))) + reg = reg & ~(EHCI_PS_CLEAR | EHCI_PS_PP); case USB_PORT_FEAT_C_CONNECTION: - reg |= EHCI_PS_CSC; + reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_CSC; break; case USB_PORT_FEAT_OVER_CURRENT: - reg |= EHCI_PS_OCC; + reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_OCC; break; case USB_PORT_FEAT_C_RESET: portreset &= ~(1 << le16_to_cpu(req->index)); @@ -605,7 +628,9 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, debug("unknown feature %x\n", le16_to_cpu(req->value)); goto unknown; } - ehci_writel(&hcor->or_portsc[le16_to_cpu(req->index) - 1], reg); + ehci_writel(status_reg, reg); + /* unblock posted write */ + ehci_readl(&hcor->or_usbcmd); break; default: debug("Unknown request\n"); @@ -665,9 +690,11 @@ int usb_lowlevel_init(void) reg = ehci_readl(&hccr->cr_hcsparams); descriptor.hub.bNbrPorts = HCS_N_PORTS(reg); printf("Register %x NbrPorts %d\n", reg, descriptor.hub.bNbrPorts); - if (reg & 0x10000) /* Port Indicators */ + /* Port Indicators */ + if (HCS_INDICATOR(reg)) descriptor.hub.wHubCharacteristics |= 0x80; - if (reg & 0x10) /* Port Power Control */ + /* Port Power Control */ + if (HCS_PPC(reg)) descriptor.hub.wHubCharacteristics |= 0x01; /* Start the host controller. */ @@ -682,9 +709,11 @@ int usb_lowlevel_init(void) cmd = ehci_readl(&hcor->or_configflag); cmd |= FLAG_CF; ehci_writel(&hcor->or_configflag, cmd); - /* unblock posted writes */ + /* unblock posted write */ cmd = ehci_readl(&hcor->or_usbcmd); wait_ms(5); + reg = HC_VERSION(ehci_readl(&hccr->cr_capbase)); + printf("USB EHCI %x.%02x\n", reg >> 8, reg & 0xff); rootdev = 0; -- cgit v1.1 From 1ed9f9adc88218841dfeb60b9094a5a548bff009 Mon Sep 17 00:00:00 2001 From: Michael Trimarchi Date: Wed, 31 Dec 2008 10:33:22 +0100 Subject: USB ehci remove infinite loop and use handshake function USB ehci code cleanup. Use handshake instead of infinite while loop to check the STD_ASS status Signed-off-by: Michael Trimarchi Signed-off-by: Remy Bohmer --- drivers/usb/usb_ehci_core.c | 49 +++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 24 deletions(-) (limited to 'drivers/usb/usb_ehci_core.c') diff --git a/drivers/usb/usb_ehci_core.c b/drivers/usb/usb_ehci_core.c index 07e9a6b..9736707 100644 --- a/drivers/usb/usb_ehci_core.c +++ b/drivers/usb/usb_ehci_core.c @@ -28,7 +28,7 @@ #include "usb_ehci.h" int rootdev; -struct ehci_hccr *hccr; /* R/O registers, not need for volatile */ +struct ehci_hccr *hccr; /* R/O registers, not need for volatile */ volatile struct ehci_hcor *hcor; static uint16_t portreset; @@ -106,20 +106,19 @@ static struct descriptor { #define ehci_is_TDI() (0) #endif -static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int msec) +static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec) { uint32_t result; do { result = ehci_readl(ptr); - debug("handshake read reg(%x)=%x\n", (uint32_t)ptr, result); if (result == ~(uint32_t)0) return -1; result &= mask; if (result == done) return 0; - wait_ms(1); - msec--; - } while (msec > 0); + udelay(1); + usec--; + } while (usec > 0); return -1; } @@ -138,21 +137,21 @@ static int ehci_reset(void) cmd = ehci_readl(&hcor->or_usbcmd); cmd |= CMD_RESET; ehci_writel(&hcor->or_usbcmd, cmd); - ret = handshake((uint32_t *)&hcor->or_usbcmd, CMD_RESET, 0, 250); + ret = handshake((uint32_t *)&hcor->or_usbcmd, CMD_RESET, 0, 250 * 1000); if (ret < 0) { printf("EHCI fail to reset\n"); goto out; } -#if defined(CONFIG_EHCI_IS_TDI) - reg_ptr = (uint32_t *)((u8 *)hcor + USBMODE); - tmp = ehci_readl(reg_ptr); - tmp |= USBMODE_CM_HC; + if (ehci_is_TDI()) { + reg_ptr = (uint32_t *)((u8 *)hcor + USBMODE); + tmp = ehci_readl(reg_ptr); + tmp |= USBMODE_CM_HC; #if defined(CONFIG_EHCI_MMIO_BIG_ENDIAN) - tmp |= USBMODE_BE; -#endif - ehci_writel(reg_ptr, tmp); + tmp |= USBMODE_BE; #endif + ehci_writel(reg_ptr, tmp); + } out: return ret; } @@ -224,7 +223,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, uint32_t endpt, token, usbsts; uint32_t c, toggle; uint32_t cmd; - uint32_t sts; + int ret = 0; debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\n", dev, pipe, buffer, length, req); @@ -340,10 +339,11 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, cmd |= CMD_ASE; ehci_writel(&hcor->or_usbcmd, cmd); - sts = ehci_readl(&hcor->or_usbsts); - while ((sts & STD_ASS) == 0) { - sts = ehci_readl(&hcor->or_usbsts); - udelay(10); + ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, STD_ASS, + 100 * 1000); + if (ret < 0) { + printf("EHCI fail timeout STD_ASS set\n"); + goto fail; } /* Wait for TDs to be processed. */ @@ -360,10 +360,11 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, cmd &= ~CMD_ASE; ehci_writel(&hcor->or_usbcmd, cmd); - sts = ehci_readl(&hcor->or_usbsts); - while ((sts & STD_ASS) != 0) { - sts = ehci_readl(&hcor->or_usbsts); - udelay(10); + ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, 0, + 100 * 1000); + if (ret < 0) { + printf("EHCI fail timeout STD_ASS reset\n"); + goto fail; } qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); @@ -536,7 +537,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, /* force reset to complete */ reg = reg & ~(EHCI_PS_PR | EHCI_PS_CLEAR); ehci_writel(status_reg, reg); - ret = handshake(status_reg, EHCI_PS_PR, 0, 2); + ret = handshake(status_reg, EHCI_PS_PR, 0, 2 * 1000); if (!ret) tmpbuf[0] |= USB_PORT_STAT_RESET; else -- cgit v1.1 From 597eb28bd9691266b7b804364cda577cdb51d106 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Wed, 21 Jan 2009 17:12:01 +0100 Subject: USB: Fix speed detection on EHCI cntr with root hub transaction translators This patch fixes an issue that the speed of USB devices was not detected correctly on some EHCI controllers. This will be used on the upcoming VCT EHCI support. Signed-off-by: Stefan Roese Signed-off-by: Remy Bohmer --- drivers/usb/usb_ehci_core.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'drivers/usb/usb_ehci_core.c') diff --git a/drivers/usb/usb_ehci_core.c b/drivers/usb/usb_ehci_core.c index 9736707..28962fa 100644 --- a/drivers/usb/usb_ehci_core.c +++ b/drivers/usb/usb_ehci_core.c @@ -546,7 +546,22 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, } if (reg & EHCI_PS_PP) tmpbuf[1] |= USB_PORT_STAT_POWER >> 8; - tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8; + + if (ehci_is_TDI()) { + switch ((reg >> 26) & 3) { + case 0: + break; + case 1: + tmpbuf[1] |= USB_PORT_STAT_LOW_SPEED >> 8; + break; + case 2: + default: + tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8; + break; + } + } else { + tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8; + } if (reg & EHCI_PS_CSC) tmpbuf[2] |= USB_PORT_STAT_C_CONNECTION; -- cgit v1.1 From 832e61418eedfea172bd2fdfd0ea0d199cc70a9d Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Wed, 21 Jan 2009 17:12:10 +0100 Subject: USB: Add config option to call ehci_hcd_init() again after EHCI reset This patch adds the config option CONFIG_EHCI_HCD_INIT_AFTER_RESET to call ehci_hcd_init() again after ehci_reset() is executed. This is needed for the upcoming VCT EHCI support which needs to re-init the hcd part again after the EHCI CMD_RESET is executed. Signed-off-by: Stefan Roese Signed-off-by: Remy Bohmer --- drivers/usb/usb_ehci_core.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/usb/usb_ehci_core.c') diff --git a/drivers/usb/usb_ehci_core.c b/drivers/usb/usb_ehci_core.c index 28962fa..2f38f33 100644 --- a/drivers/usb/usb_ehci_core.c +++ b/drivers/usb/usb_ehci_core.c @@ -691,6 +691,11 @@ int usb_lowlevel_init(void) if (ehci_reset() != 0) return -1; +#if defined(CONFIG_EHCI_HCD_INIT_AFTER_RESET) + if (ehci_hcd_init() != 0) + return -1; +#endif + /* Set head of reclaim list */ memset(&qh_list, 0, sizeof(qh_list)); qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); -- cgit v1.1 From daa2dafb450a8073a4e42fd46cd4e995b208e4cb Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Wed, 21 Jan 2009 17:12:19 +0100 Subject: USB: Add dcache support to the EHCI driver This patch adds routines to handle (flush/invalidate) the dcache for the QH and qTD structures and data buffers. This is needed on platforms using this EHCI support with dcache enabled (like the MIPS VCT board port). Signed-off-by: Stefan Roese Signed-off-by: Remy Bohmer --- drivers/usb/usb_ehci_core.c | 98 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) (limited to 'drivers/usb/usb_ehci_core.c') diff --git a/drivers/usb/usb_ehci_core.c b/drivers/usb/usb_ehci_core.c index 2f38f33..813f64a 100644 --- a/drivers/usb/usb_ehci_core.c +++ b/drivers/usb/usb_ehci_core.c @@ -106,6 +106,99 @@ static struct descriptor { #define ehci_is_TDI() (0) #endif +#if defined(CONFIG_EHCI_DCACHE) +/* + * Routines to handle (flush/invalidate) the dcache for the QH and qTD + * structures and data buffers. This is needed on platforms using this + * EHCI support with dcache enabled. + */ +static void flush_invalidate(u32 addr, int size, int flush) +{ + if (flush) + flush_dcache_range(addr, addr + size); + else + invalidate_dcache_range(addr, addr + size); +} + +static void cache_qtd(struct qTD *qtd, int flush) +{ + u32 *ptr = (u32 *)qtd->qt_buffer[0]; + int len = (qtd->qt_token & 0x7fff0000) >> 16; + + flush_invalidate((u32)qtd, sizeof(struct qTD), flush); + if (ptr && len) + flush_invalidate((u32)ptr, len, flush); +} + + +static inline struct QH *qh_addr(struct QH *qh) +{ + return (struct QH *)((u32)qh & 0xffffffe0); +} + +static void cache_qh(struct QH *qh, int flush) +{ + struct qTD *qtd; + struct qTD *next; + static struct qTD *first_qtd; + + /* + * Walk the QH list and flush/invalidate all entries + */ + while (1) { + flush_invalidate((u32)qh_addr(qh), sizeof(struct QH), flush); + if ((u32)qh & QH_LINK_TYPE_QH) + break; + qh = qh_addr(qh); + qh = (struct QH *)qh->qh_link; + } + qh = qh_addr(qh); + + /* + * Save first qTD pointer, needed for invalidating pass on this QH + */ + if (flush) + first_qtd = qtd = (struct qTD *)(*(u32 *)&qh->qh_overlay & + 0xffffffe0); + else + qtd = first_qtd; + + /* + * Walk the qTD list and flush/invalidate all entries + */ + while (1) { + if (qtd == NULL) + break; + cache_qtd(qtd, flush); + next = (struct qTD *)((u32)qtd->qt_next & 0xffffffe0); + if (next == qtd) + break; + qtd = next; + } +} + +static inline void ehci_flush_dcache(struct QH *qh) +{ + cache_qh(qh, 1); +} + +static inline void ehci_invalidate_dcache(struct QH *qh) +{ + cache_qh(qh, 0); +} +#else /* CONFIG_EHCI_DCACHE */ +/* + * + */ +static inline void ehci_flush_dcache(struct QH *qh) +{ +} + +static inline void ehci_invalidate_dcache(struct QH *qh) +{ +} +#endif /* CONFIG_EHCI_DCACHE */ + static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec) { uint32_t result; @@ -331,6 +424,9 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, qh_list.qh_link = cpu_to_hc32((uint32_t) qh | QH_LINK_TYPE_QH); + /* Flush dcache */ + ehci_flush_dcache(&qh_list); + usbsts = ehci_readl(&hcor->or_usbsts); ehci_writel(&hcor->or_usbsts, (usbsts & 0x3f)); @@ -350,6 +446,8 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, ts = get_timer(0); vtd = td; do { + /* Invalidate dcache */ + ehci_invalidate_dcache(&qh_list); token = hc32_to_cpu(vtd->qt_token); if (!(token & 0x80)) break; -- cgit v1.1