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/Makefile | 1 + drivers/usb/usb_ehci.h | 121 +++++++++ drivers/usb/usb_ehci_core.c | 634 ++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/usb_ehci_core.h | 29 ++ 4 files changed, 785 insertions(+) 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 (limited to 'drivers') diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 856f51a..fbc6521 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -28,6 +28,7 @@ LIB := $(obj)libusb.a # core COBJS-y += usbdcore.o COBJS-$(CONFIG_USB_OHCI_NEW) += usb_ohci.o +COBJS-$(CONFIG_USB_EHCI) += usb_ehci_core.o # host COBJS-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o diff --git a/drivers/usb/usb_ehci.h b/drivers/usb/usb_ehci.h new file mode 100644 index 0000000..ebffb44 --- /dev/null +++ b/drivers/usb/usb_ehci.h @@ -0,0 +1,121 @@ +/*- + * 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_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) + +#define EHCI_PS_IS_LOWSPEED(x) (((x) & EHCI_PS_LS) == 0x00000400) + +/* + * 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..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; +} diff --git a/drivers/usb/usb_ehci_core.h b/drivers/usb/usb_ehci_core.h new file mode 100644 index 0000000..88e91b9 --- /dev/null +++ b/drivers/usb/usb_ehci_core.h @@ -0,0 +1,29 @@ +/*- + * Copyright (c) 2007-2008, Juniper Networks, Inc. + * Copyright (c) 2008, Excito Elektronik i Skåne AB + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef USB_EHCI_CORE_H +#define USB_EHCI_CORE_H + +extern int rootdev; +extern struct ehci_hccr *hccr; +extern volatile struct ehci_hcor *hcor; + +#endif -- cgit v1.1 From 6b92487dcf9afe83a3570153d66940fdb293be76 Mon Sep 17 00:00:00 2001 From: Michael Trimarchi Date: Fri, 28 Nov 2008 13:22:09 +0100 Subject: USB ehci freescale support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add USB ehci freescale support Signed-off-by: Michael Trimarchi Signed-off-by: Remy Böhmer --- drivers/usb/Makefile | 1 + drivers/usb/usb_ehci_fsl.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/usb_ehci_fsl.h | 86 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 186 insertions(+) create mode 100644 drivers/usb/usb_ehci_fsl.c create mode 100644 drivers/usb/usb_ehci_fsl.h (limited to 'drivers') diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index fbc6521..6ba154b 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -34,6 +34,7 @@ COBJS-$(CONFIG_USB_EHCI) += usb_ehci_core.o COBJS-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o COBJS-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o COBJS-$(CONFIG_USB_SL811HS) += sl811_usb.o +COBJS-$(CONFIG_USB_EHCI_FSL) += usb_ehci_fsl.o # device ifdef CONFIG_USB_DEVICE diff --git a/drivers/usb/usb_ehci_fsl.c b/drivers/usb/usb_ehci_fsl.c new file mode 100644 index 0000000..9ca6c56 --- /dev/null +++ b/drivers/usb/usb_ehci_fsl.c @@ -0,0 +1,99 @@ +/* + * (C) Copyright 2008, Excito Elektronik i Sk=E5ne AB + * + * Author: Tor Krill tor@excito.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + +#include "usb_ehci.h" +#include "usb_ehci_fsl.h" +#include "usb_ehci_core.h" + +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + * + * Excerpts from linux ehci fsl driver. + */ +int ehci_hcd_init(void) +{ + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + uint32_t addr, temp; + + addr = (uint32_t)&(im->usb[0]); + hccr = (struct ehci_hccr *)(addr + FSL_SKIP_PCI); + hcor = (struct ehci_hcor *)((uint32_t) hccr + hccr->cr_caplength); + + /* Configure clock */ + clrsetbits_be32(&(im->clk.sccr), MPC83XX_SCCR_USB_MASK, + MPC83XX_SCCR_USB_DRCM_11); + + /* Confgure interface. */ + temp = in_be32((void *)(addr + FSL_SOC_USB_CTRL)); + out_be32((void *)(addr + FSL_SOC_USB_CTRL), temp + | REFSEL_16MHZ | UTMI_PHY_EN); + + /* Wait for clock to stabilize */ + do { + temp = in_be32((void *)(addr + FSL_SOC_USB_CTRL)); + udelay(1000); + } while (!(temp & PHY_CLK_VALID)); + + /* Set to Host mode */ + temp = in_le32((void *)(addr + FSL_SOC_USB_USBMODE)); + out_le32((void *)(addr + FSL_SOC_USB_USBMODE), temp | CM_HOST); + + out_be32((void *)(addr + FSL_SOC_USB_SNOOP1), SNOOP_SIZE_2GB); + out_be32((void *)(addr + FSL_SOC_USB_SNOOP2), + 0x80000000 | SNOOP_SIZE_2GB); + + /* Init phy */ + /* TODO: handle different phys? */ + out_le32(&(hcor->or_portsc[0]), PORT_PTS_UTMI); + + /* Enable interface. */ + temp = in_be32((void *)(addr + FSL_SOC_USB_CTRL)); + out_be32((void *)(addr + FSL_SOC_USB_CTRL), temp | USB_EN); + + out_be32((void *)(addr + FSL_SOC_USB_PRICTRL), 0x0000000c); + out_be32((void *)(addr + FSL_SOC_USB_AGECNTTHRSH), 0x00000040); + out_be32((void *)(addr + FSL_SOC_USB_SICTRL), 0x00000001); + + /* Enable interface. */ + temp = in_be32((void *)(addr + FSL_SOC_USB_CTRL)); + out_be32((void *)(addr + FSL_SOC_USB_CTRL), temp | USB_EN); + + temp = in_le32((void *)(addr + FSL_SOC_USB_USBMODE)); + + return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(void) +{ + return 0; +} diff --git a/drivers/usb/usb_ehci_fsl.h b/drivers/usb/usb_ehci_fsl.h new file mode 100644 index 0000000..c429af1 --- /dev/null +++ b/drivers/usb/usb_ehci_fsl.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2005 freescale semiconductor + * Copyright (c) 2005 MontaVista Software + * Copyright (c) 2008 Excito Elektronik i Sk=E5ne AB + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef _EHCI_FSL_H +#define _EHCI_FSL_H + +/* Global offsets */ +#define FSL_SKIP_PCI 0x100 + +/* offsets for the non-ehci registers in the FSL SOC USB controller */ +#define FSL_SOC_USB_ULPIVP 0x170 +#define FSL_SOC_USB_PORTSC1 0x184 +#define PORT_PTS_MSK (3 << 30) +#define PORT_PTS_UTMI (0 << 30) +#define PORT_PTS_ULPI (2 << 30) +#define PORT_PTS_SERIAL (3 << 30) +#define PORT_PTS_PTW (1 << 28) + +/* USBMODE Register bits */ +#define CM_IDLE (0 << 0) +#define CM_RESERVED (1 << 0) +#define CM_DEVICE (2 << 0) +#define CM_HOST (3 << 0) +#define USBMODE_RESERVED_2 (0 << 2) +#define SLOM (1 << 3) +#define SDIS (1 << 4) + +/* CONTROL Register bits */ +#define ULPI_INT_EN (1 << 0) +#define WU_INT_EN (1 << 1) +#define USB_EN (1 << 2) +#define LSF_EN (1 << 3) +#define KEEP_OTG_ON (1 << 4) +#define OTG_PORT (1 << 5) +#define REFSEL_12MHZ (0 << 6) +#define REFSEL_16MHZ (1 << 6) +#define REFSEL_48MHZ (2 << 6) +#define PLL_RESET (1 << 8) +#define UTMI_PHY_EN (1 << 9) +#define PHY_CLK_SEL_UTMI (0 << 10) +#define PHY_CLK_SEL_ULPI (1 << 10) +#define CLKIN_SEL_USB_CLK (0 << 11) +#define CLKIN_SEL_USB_CLK2 (1 << 11) +#define CLKIN_SEL_SYS_CLK (2 << 11) +#define CLKIN_SEL_SYS_CLK2 (3 << 11) +#define RESERVED_18 (0 << 13) +#define RESERVED_17 (0 << 14) +#define RESERVED_16 (0 << 15) +#define WU_INT (1 << 16) +#define PHY_CLK_VALID (1 << 17) + +#define FSL_SOC_USB_PORTSC2 0x188 +#define FSL_SOC_USB_USBMODE 0x1a8 +#define FSL_SOC_USB_SNOOP1 0x400 /* NOTE: big-endian */ +#define FSL_SOC_USB_SNOOP2 0x404 /* NOTE: big-endian */ +#define FSL_SOC_USB_AGECNTTHRSH 0x408 /* NOTE: big-endian */ +#define FSL_SOC_USB_PRICTRL 0x40c /* NOTE: big-endian */ +#define FSL_SOC_USB_SICTRL 0x410 /* NOTE: big-endian */ +#define FSL_SOC_USB_CTRL 0x500 /* NOTE: big-endian */ +#define SNOOP_SIZE_2GB 0x1e + +/* System Clock Control Register */ +#define MPC83XX_SCCR_USB_MASK 0x00f00000 +#define MPC83XX_SCCR_USB_DRCM_11 0x00300000 +#define MPC83XX_SCCR_USB_DRCM_01 0x00100000 +#define MPC83XX_SCCR_USB_DRCM_10 0x00200000 + +#endif /* _EHCI_FSL_H */ -- 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.h | 50 ++++++- drivers/usb/usb_ehci_core.c | 343 ++++++++++++++++++++++++-------------------- drivers/usb/usb_ehci_core.h | 2 +- 3 files changed, 234 insertions(+), 161 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/usb_ehci.h b/drivers/usb/usb_ehci.h index ebffb44..3e7a2ab 100644 --- a/drivers/usb/usb_ehci.h +++ b/drivers/usb/usb_ehci.h @@ -24,6 +24,7 @@ /* (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) @@ -32,6 +33,7 @@ #define EndpointRequest \ ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8) + #define EndpointOutRequest \ ((USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8) @@ -39,8 +41,9 @@ * Register Space. */ struct ehci_hccr { - uint8_t cr_caplength; - uint16_t cr_hciversion; + uint32_t cr_capbase; +#define HC_LENGTH(p) (((p) >> 0) & 0x00ff) +#define HC_VERSION(p) (((p) >> 16) & 0xffff) uint32_t cr_hcsparams; uint32_t cr_hccparams; uint8_t cr_hcsp_portrt[8]; @@ -48,7 +51,9 @@ struct ehci_hccr { struct ehci_hcor { uint32_t or_usbcmd; +#define CMD_ASE (1 << 5) uint32_t or_usbsts; +#define STD_ASS (1 << 15) uint32_t or_usbintr; uint32_t or_frindex; uint32_t or_ctrldssegment; @@ -60,6 +65,47 @@ struct ehci_hcor { uint32_t or_systune; }; +/* Interface descriptor */ +struct usb_linux_interface_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned char bInterfaceNumber; + unsigned char bAlternateSetting; + unsigned char bNumEndpoints; + unsigned char bInterfaceClass; + unsigned char bInterfaceSubClass; + unsigned char bInterfaceProtocol; + unsigned char iInterface; +} __attribute__ ((packed)); + +/* Configuration descriptor information.. */ +struct usb_linux_config_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned short wTotalLength; + unsigned char bNumInterfaces; + unsigned char bConfigurationValue; + unsigned char iConfiguration; + unsigned char bmAttributes; + unsigned char MaxPower; +} __attribute__ ((packed)); + +#if defined CONFIG_EHCI_DESC_BIG_ENDIAN +#define ehci_readl(x) (x) +#define ehci_writel(a, b) (a) = (b) +#else +#define ehci_readl(x) cpu_to_le32((x)) +#define ehci_writel(a, b) (a) = cpu_to_le32((b)) +#endif + +#if defined CONFIG_EHCI_MMIO_BIG_ENDIAN +#define hc32_to_cpu(x) be32_to_cpu((x)) +#define cpu_to_hc32(x) cpu_to_be32((x)) +#else +#define hc32_to_cpu(x) le32_to_cpu((x)) +#define cpu_to_hc32(x) cpu_to_le32((x)) +#endif + #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 */ 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; diff --git a/drivers/usb/usb_ehci_core.h b/drivers/usb/usb_ehci_core.h index 88e91b9..39e5c5e 100644 --- a/drivers/usb/usb_ehci_core.h +++ b/drivers/usb/usb_ehci_core.h @@ -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 -- 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.h | 27 ++++++-- drivers/usb/usb_ehci_core.c | 147 ++++++++++++++++++++++++++++++++------------ drivers/usb/usb_ehci_fsl.c | 3 +- 3 files changed, 133 insertions(+), 44 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/usb_ehci.h b/drivers/usb/usb_ehci.h index 3e7a2ab..90b137a 100644 --- a/drivers/usb/usb_ehci.h +++ b/drivers/usb/usb_ehci.h @@ -45,15 +45,24 @@ struct ehci_hccr { #define HC_LENGTH(p) (((p) >> 0) & 0x00ff) #define HC_VERSION(p) (((p) >> 16) & 0xffff) uint32_t cr_hcsparams; +#define HCS_N_PORTS(p) (((p) >> 0) & 0xf) uint32_t cr_hccparams; uint8_t cr_hcsp_portrt[8]; }; struct ehci_hcor { uint32_t or_usbcmd; -#define CMD_ASE (1 << 5) +#define CMD_PARK (1 << 11) /* enable "park" */ +#define CMD_PARK_CNT(c) (((c) >> 8) & 3) /* how many transfers to park */ +#define CMD_ASE (1 << 5) /* async schedule enable */ +#define CMD_LRESET (1 << 7) /* partial reset */ +#define CMD_IAAD (1 << 5) /* "doorbell" interrupt */ +#define CMD_PSE (1 << 4) /* periodic schedule enable */ +#define CMD_RESET (1 << 1) /* reset HC not bus */ +#define CMD_RUN (1 << 0) /* start/stop HC */ uint32_t or_usbsts; #define STD_ASS (1 << 15) +#define STS_HALT (1 << 12) uint32_t or_usbintr; uint32_t or_frindex; uint32_t or_ctrldssegment; @@ -61,10 +70,17 @@ struct ehci_hcor { uint32_t or_asynclistaddr; uint32_t _reserved_[9]; uint32_t or_configflag; +#define FLAG_CF (1 << 0) /* true: we'll support "high speed" */ uint32_t or_portsc[2]; uint32_t or_systune; }; +#define USBMODE 0x68 /* USB Device mode */ +#define USBMODE_SDIS (1 << 3) /* Stream disable */ +#define USBMODE_BE (1 << 2) /* BE/LE endiannes select */ +#define USBMODE_CM_HC (3 << 0) /* host controller mode */ +#define USBMODE_CM_IDLE (0 << 0) /* idle state */ + /* Interface descriptor */ struct usb_linux_interface_descriptor { unsigned char bLength; @@ -91,11 +107,12 @@ struct usb_linux_config_descriptor { } __attribute__ ((packed)); #if defined CONFIG_EHCI_DESC_BIG_ENDIAN -#define ehci_readl(x) (x) -#define ehci_writel(a, b) (a) = (b) +#define ehci_readl(x) (*((volatile u32 *)(x))) +#define ehci_writel(a, b) (*((volatile u32 *)(a)) = ((volatile u32)b)) #else -#define ehci_readl(x) cpu_to_le32((x)) -#define ehci_writel(a, b) (a) = cpu_to_le32((b)) +#define ehci_readl(x) cpu_to_le32((*((volatile u32 *)(x)))) +#define ehci_writel(a, b) (*((volatile u32 *)(a)) = \ + cpu_to_le32(((volatile u32)b))) #endif #if defined CONFIG_EHCI_MMIO_BIG_ENDIAN 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; diff --git a/drivers/usb/usb_ehci_fsl.c b/drivers/usb/usb_ehci_fsl.c index 9ca6c56..81d5d21 100644 --- a/drivers/usb/usb_ehci_fsl.c +++ b/drivers/usb/usb_ehci_fsl.c @@ -43,7 +43,8 @@ int ehci_hcd_init(void) addr = (uint32_t)&(im->usb[0]); hccr = (struct ehci_hccr *)(addr + FSL_SKIP_PCI); - hcor = (struct ehci_hcor *)((uint32_t) hccr + hccr->cr_caplength); + hcor = (struct ehci_hcor *)((uint32_t) hccr + + HC_LENGTH(ehci_readl(&hccr->cr_capbase))); /* Configure clock */ clrsetbits_be32(&(im->clk.sccr), MPC83XX_SCCR_USB_MASK, -- 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.h | 51 ++++++++++-------- drivers/usb/usb_ehci_core.c | 127 +++++++++++++++++++++++++++----------------- 2 files changed, 106 insertions(+), 72 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/usb_ehci.h b/drivers/usb/usb_ehci.h index 90b137a..b72498b 100644 --- a/drivers/usb/usb_ehci.h +++ b/drivers/usb/usb_ehci.h @@ -1,5 +1,6 @@ /*- * Copyright (c) 2007-2008, Juniper Networks, Inc. + * Copyright (c) 2008, Michael Trimarchi * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -21,6 +22,10 @@ #ifndef USB_EHCI_H #define USB_EHCI_H +#if !defined(CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) +#define CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS 2 +#endif + /* (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) @@ -45,10 +50,12 @@ struct ehci_hccr { #define HC_LENGTH(p) (((p) >> 0) & 0x00ff) #define HC_VERSION(p) (((p) >> 16) & 0xffff) uint32_t cr_hcsparams; +#define HCS_PPC(p) ((p) & (1 << 4)) +#define HCS_INDICATOR(p) ((p) & (1 << 16)) /* Port indicators */ #define HCS_N_PORTS(p) (((p) >> 0) & 0xf) uint32_t cr_hccparams; uint8_t cr_hcsp_portrt[8]; -}; +} __attribute__ ((packed)); struct ehci_hcor { uint32_t or_usbcmd; @@ -71,9 +78,9 @@ struct ehci_hcor { uint32_t _reserved_[9]; uint32_t or_configflag; #define FLAG_CF (1 << 0) /* true: we'll support "high speed" */ - uint32_t or_portsc[2]; + uint32_t or_portsc[CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS]; uint32_t or_systune; -}; +} __attribute__ ((packed)); #define USBMODE 0x68 /* USB Device mode */ #define USBMODE_SDIS (1 << 3) /* Stream disable */ @@ -123,26 +130,24 @@ struct usb_linux_config_descriptor { #define cpu_to_hc32(x) cpu_to_le32((x)) #endif -#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_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_WKOC_E (1 << 22) /* RW wake on over current */ +#define EHCI_PS_WKDSCNNT_E (1 << 21) /* RW wake on disconnect */ +#define EHCI_PS_WKCNNT_E (1 << 20) /* RW wake on connect */ +#define EHCI_PS_PO (1 << 13) /* RW port owner */ +#define EHCI_PS_PP (1 << 12) /* RW,RO port power */ +#define EHCI_PS_LS (3 << 10) /* RO line status */ +#define EHCI_PS_PR (1 << 8) /* RW port reset */ +#define EHCI_PS_SUSP (1 << 7) /* RW suspend */ +#define EHCI_PS_FPR (1 << 6) /* RW force port resume */ +#define EHCI_PS_OCC (1 << 5) /* RWC over current change */ +#define EHCI_PS_OCA (1 << 4) /* RO over current active */ +#define EHCI_PS_PEC (1 << 3) /* RWC port enable change */ +#define EHCI_PS_PE (1 << 2) /* RW port enable */ +#define EHCI_PS_CSC (1 << 1) /* RWC connect status change */ +#define EHCI_PS_CS (1 << 0) /* RO connect status */ #define EHCI_PS_CLEAR (EHCI_PS_OCC | EHCI_PS_PEC | EHCI_PS_CSC) -#define EHCI_PS_IS_LOWSPEED(x) (((x) & EHCI_PS_LS) == 0x00000400) +#define EHCI_PS_IS_LOWSPEED(x) (((x) & EHCI_PS_LS) == (1 << 10)) /* * Schedule Interface Space. @@ -177,8 +182,8 @@ struct QH { struct qTD qh_overlay; }; -/* Low level intit functions */ - +/* Low level init 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 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 8fea2914ac974029b65926ef8247d908f84d202d Mon Sep 17 00:00:00 2001 From: Michael Trimarchi Date: Wed, 31 Dec 2008 10:32:41 +0100 Subject: Add initial support for USB ehci pci Add USB ehci pci support. This patch doesn't include any pci_ids and it is not tested on real hardware. Signed-off-by: Michael Trimarchi Signed-off-by: Remy Bohmer --- drivers/usb/Makefile | 1 + drivers/usb/usb_ehci_pci.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 drivers/usb/usb_ehci_pci.c (limited to 'drivers') diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 6ba154b..26189b6 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -35,6 +35,7 @@ COBJS-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o COBJS-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o COBJS-$(CONFIG_USB_SL811HS) += sl811_usb.o COBJS-$(CONFIG_USB_EHCI_FSL) += usb_ehci_fsl.o +COBJS-$(CONFIG_USB_EHCI_PCI) += usb_ehci_pci.o # device ifdef CONFIG_USB_DEVICE diff --git a/drivers/usb/usb_ehci_pci.c b/drivers/usb/usb_ehci_pci.c new file mode 100644 index 0000000..3e7143c --- /dev/null +++ b/drivers/usb/usb_ehci_pci.c @@ -0,0 +1,64 @@ +/*- + * Copyright (c) 2007-2008, Juniper Networks, Inc. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include "usb_ehci.h" +#include "usb_ehci_core.h" + +#ifdef CONFIG_PCI_EHCI_DEVICE +static struct pci_device_id ehci_pci_ids[] = { + /* Please add supported PCI EHCI controller ids here */ + {0, 0} +}; +#endif + +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + */ +int ehci_hcd_init(void) +{ + pci_dev_t pdev; + uint32_t addr; + + pdev = pci_find_devices(ehci_pci_ids, CONFIG_PCI_EHCI_DEVICE); + if (pdev == -1) { + printf("EHCI host controller not found\n"); + return -1; + } + + pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &addr); + hccr = (struct ehci_hccr *)addr; + hcor = (struct ehci_hcor *)((uint32_t) hccr + + HC_LENGTH(ehci_readl(&hccr->cr_capbase))); + + return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(void) +{ + return 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') 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 7b6e31eb17e3ff76238a60803fc531517d516223 Mon Sep 17 00:00:00 2001 From: Michael Trimarchi Date: Wed, 31 Dec 2008 10:33:56 +0100 Subject: USB ehci ixp4xx support Add USB ehci ixp4xx host controller. Test on ixdp465 board. Signed-off-by: Michael Trimarchi Signed-off-by: Remy Bohmer --- drivers/usb/Makefile | 1 + drivers/usb/usb_ehci_ixp.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 drivers/usb/usb_ehci_ixp.c (limited to 'drivers') diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 26189b6..0f94963 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -36,6 +36,7 @@ COBJS-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o COBJS-$(CONFIG_USB_SL811HS) += sl811_usb.o COBJS-$(CONFIG_USB_EHCI_FSL) += usb_ehci_fsl.o COBJS-$(CONFIG_USB_EHCI_PCI) += usb_ehci_pci.o +COBJS-$(CONFIG_USB_EHCI_IXP4XX) += usb_ehci_ixp.o # device ifdef CONFIG_USB_DEVICE diff --git a/drivers/usb/usb_ehci_ixp.c b/drivers/usb/usb_ehci_ixp.c new file mode 100644 index 0000000..25c18c1 --- /dev/null +++ b/drivers/usb/usb_ehci_ixp.c @@ -0,0 +1,49 @@ +/* + * (C) Copyright 2008, Michael Trimarchi + * + * Author: Michael Trimarchi + * This code is based on ehci freescale driver + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#include +#include +#include "usb_ehci.h" +#include "usb_ehci_core.h" +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + */ +int ehci_hcd_init(void) +{ + hccr = (struct ehci_hccr *)(0xcd000100); + hcor = (struct ehci_hcor *)((uint32_t) hccr + + HC_LENGTH(ehci_readl(&hccr->cr_capbase))); + + printf("IXP4XX init hccr %x and hcor %x hc_length %d\n", + (uint32_t)hccr, (uint32_t)hcor, + (uint32_t)HC_LENGTH(ehci_readl(&hccr->cr_capbase))); + return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(void) +{ + return 0; +} -- cgit v1.1 From a142896934c755e679ba87e227a8e449f39b0012 Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Sun, 4 Jan 2009 09:41:03 +0530 Subject: usb : musb : Adding host controller driver for Mentor USB controller Adding Mentor USB core functionality and Mentor USB Host controller functionality for Mentor USB OTG controller (musbhdrc). Signed-off-by: Ravi Babu Signed-off-by: Swaminathan S Signed-off-by: Thomas Abraham Signed-off-by: Ajay Kumar Gupta Signed-off-by: Remy Bohmer --- drivers/usb/Makefile | 1 + drivers/usb/musb_core.c | 141 +++++++++ drivers/usb/musb_core.h | 317 +++++++++++++++++++ drivers/usb/musb_hcd.c | 792 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/musb_hcd.h | 51 ++++ 5 files changed, 1302 insertions(+) create mode 100644 drivers/usb/musb_core.c create mode 100644 drivers/usb/musb_core.h create mode 100644 drivers/usb/musb_hcd.c create mode 100644 drivers/usb/musb_hcd.h (limited to 'drivers') diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 0f94963..098d7b6 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -37,6 +37,7 @@ COBJS-$(CONFIG_USB_SL811HS) += sl811_usb.o COBJS-$(CONFIG_USB_EHCI_FSL) += usb_ehci_fsl.o COBJS-$(CONFIG_USB_EHCI_PCI) += usb_ehci_pci.o COBJS-$(CONFIG_USB_EHCI_IXP4XX) += usb_ehci_ixp.o +COBJS-$(CONFIG_MUSB_HCD) += musb_hcd.o musb_core.o # device ifdef CONFIG_USB_DEVICE diff --git a/drivers/usb/musb_core.c b/drivers/usb/musb_core.c new file mode 100644 index 0000000..ec57fc8 --- /dev/null +++ b/drivers/usb/musb_core.c @@ -0,0 +1,141 @@ +/* + * Mentor USB OTG Core functionality common for both Host and Device + * functionality. + * + * Copyright (c) 2008 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + */ + +#include + +#include "musb_core.h" +struct musb_regs *musbr; + +/* + * program the mentor core to start (enable interrupts, dma, etc.) + */ +void musb_start(void) +{ + u8 devctl; + + /* disable all interrupts */ + writew(0, &musbr->intrtxe); + writew(0, &musbr->intrrxe); + writeb(0, &musbr->intrusbe); + writeb(0, &musbr->testmode); + + /* put into basic highspeed mode and start session */ + writeb(MUSB_POWER_HSENAB, &musbr->power); +#if defined(CONFIG_MUSB_HCD) + devctl = readb(&musbr->devctl); + writeb(devctl | MUSB_DEVCTL_SESSION, &musbr->devctl); +#endif +} + +/* + * This function configures the endpoint configuration. The musb hcd or musb + * device implementation can use this function to configure the endpoints + * and set the FIFO sizes. Note: The summation of FIFO sizes of all endpoints + * should not be more than the available FIFO size. + * + * epinfo - Pointer to EP configuration table + * cnt - Number of entries in the EP conf table. + */ +void musb_configure_ep(struct musb_epinfo *epinfo, u8 cnt) +{ + u16 csr; + u16 fifoaddr = 64; /* First 64 bytes of FIFO reserved for EP0 */ + u32 fifosize; + u8 idx; + + while (cnt--) { + /* prepare fifosize to write to register */ + fifosize = epinfo->epsize >> 3; + idx = ffs(fifosize) - 1; + + writeb(epinfo->epnum, &musbr->index); + if (epinfo->epdir) { + /* Configure fifo size and fifo base address */ + writeb(idx, &musbr->txfifosz); + writew(fifoaddr >> 3, &musbr->txfifoadd); +#if defined(CONFIG_MUSB_HCD) + /* clear the data toggle bit */ + csr = readw(&musbr->txcsr); + writew(csr | MUSB_TXCSR_CLRDATATOG, &musbr->txcsr); +#endif + /* Flush fifo if required */ + if (csr & MUSB_TXCSR_TXPKTRDY) + writew(csr | MUSB_TXCSR_FLUSHFIFO, + &musbr->txcsr); + } else { + /* Configure fifo size and fifo base address */ + writeb(idx, &musbr->rxfifosz); + writew(fifoaddr >> 3, &musbr->rxfifoadd); +#if defined(CONFIG_MUSB_HCD) + /* clear the data toggle bit */ + csr = readw(&musbr->rxcsr); + writew(csr | MUSB_RXCSR_CLRDATATOG, &musbr->rxcsr); +#endif + /* Flush fifo if required */ + if (csr & MUSB_RXCSR_RXPKTRDY) + writew(csr | MUSB_RXCSR_FLUSHFIFO, + &musbr->rxcsr); + } + fifoaddr += epinfo->epsize; + epinfo++; + } +} + +/* + * This function writes data to endpoint fifo + * + * ep - endpoint number + * length - number of bytes to write to FIFO + * fifo_data - Pointer to data buffer that contains the data to write + */ +void write_fifo(u8 ep, u32 length, void *fifo_data) +{ + u8 *data = (u8 *)fifo_data; + + /* select the endpoint index */ + writeb(ep, &musbr->index); + + /* write the data to the fifo */ + while (length--) + writeb(*data++, &musbr->fifox[ep]); +} + +/* + * This function reads data from endpoint fifo + * + * ep - endpoint number + * length - number of bytes to read from FIFO + * fifo_data - pointer to data buffer into which data is read + */ +void read_fifo(u8 ep, u32 length, void *fifo_data) +{ + u8 *data = (u8 *)fifo_data; + + /* select the endpoint index */ + writeb(ep, &musbr->index); + + /* read the data to the fifo */ + while (length--) + *data++ = readb(&musbr->fifox[ep]); +} diff --git a/drivers/usb/musb_core.h b/drivers/usb/musb_core.h new file mode 100644 index 0000000..9f5ebe7 --- /dev/null +++ b/drivers/usb/musb_core.h @@ -0,0 +1,317 @@ +/****************************************************************** + * Copyright 2008 Mentor Graphics Corporation + * Copyright (C) 2008 by Texas Instruments + * + * This file is part of the Inventra Controller Driver for Linux. + * + * The Inventra Controller Driver for Linux is free software; you + * can redistribute it and/or modify it under the terms of the GNU + * General Public License version 2 as published by the Free Software + * Foundation. + * + * The Inventra Controller Driver for Linux 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 The Inventra Controller Driver for Linux ; if not, + * write to the Free Software Foundation, Inc., 59 Temple Place, + * Suite 330, Boston, MA 02111-1307 USA + * + * ANY DOWNLOAD, USE, REPRODUCTION, MODIFICATION OR DISTRIBUTION + * OF THIS DRIVER INDICATES YOUR COMPLETE AND UNCONDITIONAL ACCEPTANCE + * OF THOSE TERMS.THIS DRIVER IS PROVIDED "AS IS" AND MENTOR GRAPHICS + * MAKES NO WARRANTIES, EXPRESS OR IMPLIED, RELATED TO THIS DRIVER. + * MENTOR GRAPHICS SPECIFICALLY DISCLAIMS ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY; FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT. MENTOR GRAPHICS DOES NOT PROVIDE SUPPORT + * SERVICES OR UPDATES FOR THIS DRIVER, EVEN IF YOU ARE A MENTOR + * GRAPHICS SUPPORT CUSTOMER. + ******************************************************************/ + +#ifndef __MUSB_HDRC_DEFS_H__ +#define __MUSB_HDRC_DEFS_H__ + +#include +#include +#include + +#define MUSB_EP0_FIFOSIZE 64 /* This is non-configurable */ + +/* Mentor USB core register overlay structure */ +struct musb_regs { + /* common registers */ + u8 faddr; + u8 power; + u16 intrtx; + u16 intrrx; + u16 intrtxe; + u16 intrrxe; + u8 intrusb; + u8 intrusbe; + u16 frame; + u8 index; + u8 testmode; + /* indexed registers */ + u16 txmaxp; + u16 txcsr; + u16 rxmaxp; + u16 rxcsr; + u16 rxcount; + u8 txtype; + u8 txinterval; + u8 rxtype; + u8 rxinterval; + u8 reserved0; + u8 fifosize; + /* fifo */ + u32 fifox[16]; + /* OTG, dynamic FIFO, version & vendor registers */ + u8 devctl; + u8 reserved1; + u8 txfifosz; + u8 rxfifosz; + u16 txfifoadd; + u16 rxfifoadd; + u32 vcontrol; + u16 hwvers; + u16 reserved2[5]; + u8 epinfo; + u8 raminfo; + u8 linkinfo; + u8 vplen; + u8 hseof1; + u8 fseof1; + u8 lseof1; + u8 reserved3; + /* target address registers */ + struct musb_tar_regs { + u8 txfuncaddr; + u8 reserved0; + u8 txhubaddr; + u8 txhubport; + u8 rxfuncaddr; + u8 reserved1; + u8 rxhubaddr; + u8 rxhubport; + } tar[16]; +} __attribute((aligned(32))); + +/* + * MUSB Register bits + */ + +/* POWER */ +#define MUSB_POWER_ISOUPDATE 0x80 +#define MUSB_POWER_SOFTCONN 0x40 +#define MUSB_POWER_HSENAB 0x20 +#define MUSB_POWER_HSMODE 0x10 +#define MUSB_POWER_RESET 0x08 +#define MUSB_POWER_RESUME 0x04 +#define MUSB_POWER_SUSPENDM 0x02 +#define MUSB_POWER_ENSUSPEND 0x01 +#define MUSB_POWER_HSMODE_SHIFT 4 + +/* INTRUSB */ +#define MUSB_INTR_SUSPEND 0x01 +#define MUSB_INTR_RESUME 0x02 +#define MUSB_INTR_RESET 0x04 +#define MUSB_INTR_BABBLE 0x04 +#define MUSB_INTR_SOF 0x08 +#define MUSB_INTR_CONNECT 0x10 +#define MUSB_INTR_DISCONNECT 0x20 +#define MUSB_INTR_SESSREQ 0x40 +#define MUSB_INTR_VBUSERROR 0x80 /* For SESSION end */ + +/* DEVCTL */ +#define MUSB_DEVCTL_BDEVICE 0x80 +#define MUSB_DEVCTL_FSDEV 0x40 +#define MUSB_DEVCTL_LSDEV 0x20 +#define MUSB_DEVCTL_VBUS 0x18 +#define MUSB_DEVCTL_VBUS_SHIFT 3 +#define MUSB_DEVCTL_HM 0x04 +#define MUSB_DEVCTL_HR 0x02 +#define MUSB_DEVCTL_SESSION 0x01 + +/* TESTMODE */ +#define MUSB_TEST_FORCE_HOST 0x80 +#define MUSB_TEST_FIFO_ACCESS 0x40 +#define MUSB_TEST_FORCE_FS 0x20 +#define MUSB_TEST_FORCE_HS 0x10 +#define MUSB_TEST_PACKET 0x08 +#define MUSB_TEST_K 0x04 +#define MUSB_TEST_J 0x02 +#define MUSB_TEST_SE0_NAK 0x01 + +/* Allocate for double-packet buffering (effectively doubles assigned _SIZE) */ +#define MUSB_FIFOSZ_DPB 0x10 +/* Allocation size (8, 16, 32, ... 4096) */ +#define MUSB_FIFOSZ_SIZE 0x0f + +/* CSR0 */ +#define MUSB_CSR0_FLUSHFIFO 0x0100 +#define MUSB_CSR0_TXPKTRDY 0x0002 +#define MUSB_CSR0_RXPKTRDY 0x0001 + +/* CSR0 in Peripheral mode */ +#define MUSB_CSR0_P_SVDSETUPEND 0x0080 +#define MUSB_CSR0_P_SVDRXPKTRDY 0x0040 +#define MUSB_CSR0_P_SENDSTALL 0x0020 +#define MUSB_CSR0_P_SETUPEND 0x0010 +#define MUSB_CSR0_P_DATAEND 0x0008 +#define MUSB_CSR0_P_SENTSTALL 0x0004 + +/* CSR0 in Host mode */ +#define MUSB_CSR0_H_DIS_PING 0x0800 +#define MUSB_CSR0_H_WR_DATATOGGLE 0x0400 /* Set to allow setting: */ +#define MUSB_CSR0_H_DATATOGGLE 0x0200 /* Data toggle control */ +#define MUSB_CSR0_H_NAKTIMEOUT 0x0080 +#define MUSB_CSR0_H_STATUSPKT 0x0040 +#define MUSB_CSR0_H_REQPKT 0x0020 +#define MUSB_CSR0_H_ERROR 0x0010 +#define MUSB_CSR0_H_SETUPPKT 0x0008 +#define MUSB_CSR0_H_RXSTALL 0x0004 + +/* CSR0 bits to avoid zeroing (write zero clears, write 1 ignored) */ +#define MUSB_CSR0_P_WZC_BITS \ + (MUSB_CSR0_P_SENTSTALL) +#define MUSB_CSR0_H_WZC_BITS \ + (MUSB_CSR0_H_NAKTIMEOUT | MUSB_CSR0_H_RXSTALL \ + | MUSB_CSR0_RXPKTRDY) + +/* TxType/RxType */ +#define MUSB_TYPE_SPEED 0xc0 +#define MUSB_TYPE_SPEED_SHIFT 6 +#define MUSB_TYPE_SPEED_HIGH 1 +#define MUSB_TYPE_SPEED_FULL 2 +#define MUSB_TYPE_SPEED_LOW 3 +#define MUSB_TYPE_PROTO 0x30 /* Implicitly zero for ep0 */ +#define MUSB_TYPE_PROTO_SHIFT 4 +#define MUSB_TYPE_REMOTE_END 0xf /* Implicitly zero for ep0 */ +#define MUSB_TYPE_PROTO_BULK 2 +#define MUSB_TYPE_PROTO_INTR 3 + +/* CONFIGDATA */ +#define MUSB_CONFIGDATA_MPRXE 0x80 /* Auto bulk pkt combining */ +#define MUSB_CONFIGDATA_MPTXE 0x40 /* Auto bulk pkt splitting */ +#define MUSB_CONFIGDATA_BIGENDIAN 0x20 +#define MUSB_CONFIGDATA_HBRXE 0x10 /* HB-ISO for RX */ +#define MUSB_CONFIGDATA_HBTXE 0x08 /* HB-ISO for TX */ +#define MUSB_CONFIGDATA_DYNFIFO 0x04 /* Dynamic FIFO sizing */ +#define MUSB_CONFIGDATA_SOFTCONE 0x02 /* SoftConnect */ +#define MUSB_CONFIGDATA_UTMIDW 0x01 /* Data width 0/1 => 8/16bits */ + +/* TXCSR in Peripheral and Host mode */ +#define MUSB_TXCSR_AUTOSET 0x8000 +#define MUSB_TXCSR_MODE 0x2000 +#define MUSB_TXCSR_DMAENAB 0x1000 +#define MUSB_TXCSR_FRCDATATOG 0x0800 +#define MUSB_TXCSR_DMAMODE 0x0400 +#define MUSB_TXCSR_CLRDATATOG 0x0040 +#define MUSB_TXCSR_FLUSHFIFO 0x0008 +#define MUSB_TXCSR_FIFONOTEMPTY 0x0002 +#define MUSB_TXCSR_TXPKTRDY 0x0001 + +/* TXCSR in Peripheral mode */ +#define MUSB_TXCSR_P_ISO 0x4000 +#define MUSB_TXCSR_P_INCOMPTX 0x0080 +#define MUSB_TXCSR_P_SENTSTALL 0x0020 +#define MUSB_TXCSR_P_SENDSTALL 0x0010 +#define MUSB_TXCSR_P_UNDERRUN 0x0004 + +/* TXCSR in Host mode */ +#define MUSB_TXCSR_H_WR_DATATOGGLE 0x0200 +#define MUSB_TXCSR_H_DATATOGGLE 0x0100 +#define MUSB_TXCSR_H_NAKTIMEOUT 0x0080 +#define MUSB_TXCSR_H_RXSTALL 0x0020 +#define MUSB_TXCSR_H_ERROR 0x0004 +#define MUSB_TXCSR_H_DATATOGGLE_SHIFT 8 + +/* TXCSR bits to avoid zeroing (write zero clears, write 1 ignored) */ +#define MUSB_TXCSR_P_WZC_BITS \ + (MUSB_TXCSR_P_INCOMPTX | MUSB_TXCSR_P_SENTSTALL \ + | MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_FIFONOTEMPTY) +#define MUSB_TXCSR_H_WZC_BITS \ + (MUSB_TXCSR_H_NAKTIMEOUT | MUSB_TXCSR_H_RXSTALL \ + | MUSB_TXCSR_H_ERROR | MUSB_TXCSR_FIFONOTEMPTY) + +/* RXCSR in Peripheral and Host mode */ +#define MUSB_RXCSR_AUTOCLEAR 0x8000 +#define MUSB_RXCSR_DMAENAB 0x2000 +#define MUSB_RXCSR_DISNYET 0x1000 +#define MUSB_RXCSR_PID_ERR 0x1000 +#define MUSB_RXCSR_DMAMODE 0x0800 +#define MUSB_RXCSR_INCOMPRX 0x0100 +#define MUSB_RXCSR_CLRDATATOG 0x0080 +#define MUSB_RXCSR_FLUSHFIFO 0x0010 +#define MUSB_RXCSR_DATAERROR 0x0008 +#define MUSB_RXCSR_FIFOFULL 0x0002 +#define MUSB_RXCSR_RXPKTRDY 0x0001 + +/* RXCSR in Peripheral mode */ +#define MUSB_RXCSR_P_ISO 0x4000 +#define MUSB_RXCSR_P_SENTSTALL 0x0040 +#define MUSB_RXCSR_P_SENDSTALL 0x0020 +#define MUSB_RXCSR_P_OVERRUN 0x0004 + +/* RXCSR in Host mode */ +#define MUSB_RXCSR_H_AUTOREQ 0x4000 +#define MUSB_RXCSR_H_WR_DATATOGGLE 0x0400 +#define MUSB_RXCSR_H_DATATOGGLE 0x0200 +#define MUSB_RXCSR_H_RXSTALL 0x0040 +#define MUSB_RXCSR_H_REQPKT 0x0020 +#define MUSB_RXCSR_H_ERROR 0x0004 +#define MUSB_S_RXCSR_H_DATATOGGLE 9 + +/* RXCSR bits to avoid zeroing (write zero clears, write 1 ignored) */ +#define MUSB_RXCSR_P_WZC_BITS \ + (MUSB_RXCSR_P_SENTSTALL | MUSB_RXCSR_P_OVERRUN \ + | MUSB_RXCSR_RXPKTRDY) +#define MUSB_RXCSR_H_WZC_BITS \ + (MUSB_RXCSR_H_RXSTALL | MUSB_RXCSR_H_ERROR \ + | MUSB_RXCSR_DATAERROR | MUSB_RXCSR_RXPKTRDY) + +/* HUBADDR */ +#define MUSB_HUBADDR_MULTI_TT 0x80 + +/* Endpoint configuration information. Note: The value of endpoint fifo size + * element should be either 8,16,32,64,128,256,512,1024,2048 or 4096. Other + * values are not supported + */ +struct musb_epinfo { + u8 epnum; /* endpoint number */ + u8 epdir; /* endpoint direction */ + u16 epsize; /* endpoint FIFO size */ +}; + +/* + * Platform specific MUSB configuration. Any platform using the musb + * functionality should create one instance of this structure in the + * platform specific file. + */ +struct musb_config { + struct musb_regs *regs; + u32 timeout; + u8 musb_speed; +}; + +/* externally defined data */ +extern struct musb_config musb_cfg; +extern struct musb_regs *musbr; + +/* exported functions */ +extern void musb_start(void); +extern void musb_configure_ep(struct musb_epinfo *epinfo, u8 cnt); +extern void write_fifo(u8 ep, u32 length, void *fifo_data); +extern void read_fifo(u8 ep, u32 length, void *fifo_data); + +/* extern functions */ +extern inline void musb_writew(u32 offset, u16 value); +extern inline void musb_writeb(u32 offset, u8 value); +extern inline u16 musb_readw(u32 offset); +extern inline u8 musb_readb(u32 offset); + +#endif /* __MUSB_HDRC_DEFS_H__ */ + diff --git a/drivers/usb/musb_hcd.c b/drivers/usb/musb_hcd.c new file mode 100644 index 0000000..352a0d4 --- /dev/null +++ b/drivers/usb/musb_hcd.c @@ -0,0 +1,792 @@ +/* + * Mentor USB OTG Core host controller driver. + * + * Copyright (c) 2008 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + */ + +#include +#include "musb_hcd.h" + +/* MSC control transfers */ +#define USB_MSC_BBB_RESET 0xFF +#define USB_MSC_BBB_GET_MAX_LUN 0xFE + +/* Endpoint configuration information */ +static struct musb_epinfo epinfo[3] = { + {MUSB_BULK_EP, 1, 512}, /* EP1 - Bluk Out - 512 Bytes */ + {MUSB_BULK_EP, 0, 512}, /* EP1 - Bluk In - 512 Bytes */ + {MUSB_INTR_EP, 0, 64} /* EP2 - Interrupt IN - 64 Bytes */ +}; + +/* + * This function writes the data toggle value. + */ +static void write_toggle(struct usb_device *dev, u8 ep, u8 dir_out) +{ + u16 toggle = usb_gettoggle(dev, ep, dir_out); + u16 csr; + + if (dir_out) { + if (!toggle) + writew(MUSB_TXCSR_CLRDATATOG, &musbr->txcsr); + else { + csr = readw(&musbr->txcsr); + csr |= MUSB_TXCSR_H_WR_DATATOGGLE; + writew(csr, &musbr->txcsr); + csr |= (toggle << MUSB_TXCSR_H_DATATOGGLE_SHIFT); + writew(csr, &musbr->txcsr); + } + } else { + if (!toggle) + writew(MUSB_RXCSR_CLRDATATOG, &musbr->rxcsr); + else { + csr = readw(&musbr->rxcsr); + csr |= MUSB_RXCSR_H_WR_DATATOGGLE; + writew(csr, &musbr->rxcsr); + csr |= (toggle << MUSB_S_RXCSR_H_DATATOGGLE); + writew(csr, &musbr->rxcsr); + } + } +} + +/* + * This function checks if RxStall has occured on the endpoint. If a RxStall + * has occured, the RxStall is cleared and 1 is returned. If RxStall has + * not occured, 0 is returned. + */ +static u8 check_stall(u8 ep, u8 dir_out) +{ + u16 csr; + + /* For endpoint 0 */ + if (!ep) { + csr = readw(&musbr->txcsr); + if (csr & MUSB_CSR0_H_RXSTALL) { + csr &= ~MUSB_CSR0_H_RXSTALL; + writew(csr, &musbr->txcsr); + return 1; + } + } else { /* For non-ep0 */ + if (dir_out) { /* is it tx ep */ + csr = readw(&musbr->txcsr); + if (csr & MUSB_TXCSR_H_RXSTALL) { + csr &= ~MUSB_TXCSR_H_RXSTALL; + writew(csr, &musbr->txcsr); + return 1; + } + } else { /* is it rx ep */ + csr = readw(&musbr->rxcsr); + if (csr & MUSB_RXCSR_H_RXSTALL) { + csr &= ~MUSB_RXCSR_H_RXSTALL; + writew(csr, &musbr->rxcsr); + return 1; + } + } + } + return 0; +} + +/* + * waits until ep0 is ready. Returns 0 if ep is ready, -1 for timeout + * error and -2 for stall. + */ +static int wait_until_ep0_ready(struct usb_device *dev, u32 bit_mask) +{ + u16 csr; + int result = 1; + + while (result > 0) { + csr = readw(&musbr->txcsr); + if (csr & MUSB_CSR0_H_ERROR) { + csr &= ~MUSB_CSR0_H_ERROR; + writew(csr, &musbr->txcsr); + dev->status = USB_ST_CRC_ERR; + result = -1; + break; + } + + switch (bit_mask) { + case MUSB_CSR0_TXPKTRDY: + if (!(csr & MUSB_CSR0_TXPKTRDY)) { + if (check_stall(MUSB_CONTROL_EP, 0)) { + dev->status = USB_ST_STALLED; + result = -2; + } else + result = 0; + } + break; + + case MUSB_CSR0_RXPKTRDY: + if (check_stall(MUSB_CONTROL_EP, 0)) { + dev->status = USB_ST_STALLED; + result = -2; + } else + if (csr & MUSB_CSR0_RXPKTRDY) + result = 0; + break; + + case MUSB_CSR0_H_REQPKT: + if (!(csr & MUSB_CSR0_H_REQPKT)) { + if (check_stall(MUSB_CONTROL_EP, 0)) { + dev->status = USB_ST_STALLED; + result = -2; + } else + result = 0; + } + break; + } + } + return result; +} + +/* + * waits until tx ep is ready. Returns 1 when ep is ready and 0 on error. + */ +static u8 wait_until_txep_ready(struct usb_device *dev, u8 ep) +{ + u16 csr; + + do { + if (check_stall(ep, 1)) { + dev->status = USB_ST_STALLED; + return 0; + } + + csr = readw(&musbr->txcsr); + if (csr & MUSB_TXCSR_H_ERROR) { + dev->status = USB_ST_CRC_ERR; + return 0; + } + } while (csr & MUSB_TXCSR_TXPKTRDY); + return 1; +} + +/* + * waits until rx ep is ready. Returns 1 when ep is ready and 0 on error. + */ +static u8 wait_until_rxep_ready(struct usb_device *dev, u8 ep) +{ + u16 csr; + + do { + if (check_stall(ep, 0)) { + dev->status = USB_ST_STALLED; + return 0; + } + + csr = readw(&musbr->rxcsr); + if (csr & MUSB_RXCSR_H_ERROR) { + dev->status = USB_ST_CRC_ERR; + return 0; + } + } while (!(csr & MUSB_RXCSR_RXPKTRDY)); + return 1; +} + +/* + * This function performs the setup phase of the control transfer + */ +static int ctrlreq_setup_phase(struct usb_device *dev, struct devrequest *setup) +{ + int result; + u16 csr; + + /* write the control request to ep0 fifo */ + write_fifo(MUSB_CONTROL_EP, sizeof(struct devrequest), (void *)setup); + + /* enable transfer of setup packet */ + csr = readw(&musbr->txcsr); + csr |= (MUSB_CSR0_TXPKTRDY|MUSB_CSR0_H_SETUPPKT); + writew(csr, &musbr->txcsr); + + /* wait until the setup packet is transmitted */ + result = wait_until_ep0_ready(dev, MUSB_CSR0_TXPKTRDY); + dev->act_len = 0; + return result; +} + +/* + * This function handles the control transfer in data phase + */ +static int ctrlreq_in_data_phase(struct usb_device *dev, u32 len, void *buffer) +{ + u16 csr; + u32 rxlen = 0; + u32 nextlen = 0; + u8 maxpktsize = (1 << dev->maxpacketsize) * 8; + u8 *rxbuff = (u8 *)buffer; + u8 rxedlength; + int result; + + while (rxlen < len) { + /* Determine the next read length */ + nextlen = ((len-rxlen) > maxpktsize) ? maxpktsize : (len-rxlen); + + /* Set the ReqPkt bit */ + csr = readw(&musbr->txcsr); + writew(csr | MUSB_CSR0_H_REQPKT, &musbr->txcsr); + result = wait_until_ep0_ready(dev, MUSB_CSR0_RXPKTRDY); + if (result < 0) + return result; + + /* Actual number of bytes received by usb */ + rxedlength = readb(&musbr->rxcount); + + /* Read the data from the RxFIFO */ + read_fifo(MUSB_CONTROL_EP, rxedlength, &rxbuff[rxlen]); + + /* Clear the RxPktRdy Bit */ + csr = readw(&musbr->txcsr); + csr &= ~MUSB_CSR0_RXPKTRDY; + writew(csr, &musbr->txcsr); + + /* short packet? */ + if (rxedlength != nextlen) { + dev->act_len += rxedlength; + break; + } + rxlen += nextlen; + dev->act_len = rxlen; + } + return 0; +} + +/* + * This function handles the control transfer out data phase + */ +static int ctrlreq_out_data_phase(struct usb_device *dev, u32 len, void *buffer) +{ + u16 csr; + u32 txlen = 0; + u32 nextlen = 0; + u8 maxpktsize = (1 << dev->maxpacketsize) * 8; + u8 *txbuff = (u8 *)buffer; + int result = 0; + + while (txlen < len) { + /* Determine the next write length */ + nextlen = ((len-txlen) > maxpktsize) ? maxpktsize : (len-txlen); + + /* Load the data to send in FIFO */ + write_fifo(MUSB_CONTROL_EP, txlen, &txbuff[txlen]); + + /* Set TXPKTRDY bit */ + csr = readw(&musbr->txcsr); + writew(csr | MUSB_CSR0_H_DIS_PING | MUSB_CSR0_TXPKTRDY, + &musbr->txcsr); + result = wait_until_ep0_ready(dev, MUSB_CSR0_TXPKTRDY); + if (result < 0) + break; + + txlen += nextlen; + dev->act_len = txlen; + } + return result; +} + +/* + * This function handles the control transfer out status phase + */ +static int ctrlreq_out_status_phase(struct usb_device *dev) +{ + u16 csr; + int result; + + /* Set the StatusPkt bit */ + csr = readw(&musbr->txcsr); + csr |= (MUSB_CSR0_H_DIS_PING | MUSB_CSR0_TXPKTRDY | + MUSB_CSR0_H_STATUSPKT); + writew(csr, &musbr->txcsr); + + /* Wait until TXPKTRDY bit is cleared */ + result = wait_until_ep0_ready(dev, MUSB_CSR0_TXPKTRDY); + return result; +} + +/* + * This function handles the control transfer in status phase + */ +static int ctrlreq_in_status_phase(struct usb_device *dev) +{ + u16 csr; + int result; + + /* Set the StatusPkt bit and ReqPkt bit */ + csr = MUSB_CSR0_H_DIS_PING | MUSB_CSR0_H_REQPKT | MUSB_CSR0_H_STATUSPKT; + writew(csr, &musbr->txcsr); + result = wait_until_ep0_ready(dev, MUSB_CSR0_H_REQPKT); + + /* clear StatusPkt bit and RxPktRdy bit */ + csr = readw(&musbr->txcsr); + csr &= ~(MUSB_CSR0_RXPKTRDY | MUSB_CSR0_H_STATUSPKT); + writew(csr, &musbr->txcsr); + return result; +} + +/* + * determines the speed of the device (High/Full/Slow) + */ +static u8 get_dev_speed(struct usb_device *dev) +{ + return (dev->speed & USB_SPEED_HIGH) ? MUSB_TYPE_SPEED_HIGH : + ((dev->speed & USB_SPEED_LOW) ? MUSB_TYPE_SPEED_LOW : + MUSB_TYPE_SPEED_FULL); +} + +/* + * configure the hub address and the port address. + */ +static void config_hub_port(struct usb_device *dev, u8 ep) +{ + u8 chid; + u8 hub; + + /* Find out the nearest parent which is high speed */ + while (dev->parent->parent != NULL) + if (get_dev_speed(dev->parent) != MUSB_TYPE_SPEED_HIGH) + dev = dev->parent; + else + break; + + /* determine the port address at that hub */ + hub = dev->parent->devnum; + for (chid = 0; chid < USB_MAXCHILDREN; chid++) + if (dev->parent->children[chid] == dev) + break; + + /* configure the hub address and the port address */ + writeb(hub, &musbr->tar[ep].txhubaddr); + writeb((chid + 1), &musbr->tar[ep].txhubport); + writeb(hub, &musbr->tar[ep].rxhubaddr); + writeb((chid + 1), &musbr->tar[ep].rxhubport); +} + +/* + * do a control transfer + */ +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len, struct devrequest *setup) +{ + int devnum = usb_pipedevice(pipe); + u16 csr; + u8 devspeed; + + /* select control endpoint */ + writeb(MUSB_CONTROL_EP, &musbr->index); + csr = readw(&musbr->txcsr); + + /* target addr and (for multipoint) hub addr/port */ + writeb(devnum, &musbr->tar[MUSB_CONTROL_EP].txfuncaddr); + writeb(devnum, &musbr->tar[MUSB_CONTROL_EP].rxfuncaddr); + + /* configure the hub address and the port number as required */ + devspeed = get_dev_speed(dev); + if ((musb_ishighspeed()) && (dev->parent != NULL) && + (devspeed != MUSB_TYPE_SPEED_HIGH)) { + config_hub_port(dev, MUSB_CONTROL_EP); + writeb(devspeed << 6, &musbr->txtype); + } else { + writeb(musb_cfg.musb_speed << 6, &musbr->txtype); + writeb(0, &musbr->tar[MUSB_CONTROL_EP].txhubaddr); + writeb(0, &musbr->tar[MUSB_CONTROL_EP].txhubport); + writeb(0, &musbr->tar[MUSB_CONTROL_EP].rxhubaddr); + writeb(0, &musbr->tar[MUSB_CONTROL_EP].rxhubport); + } + + /* Control transfer setup phase */ + if (ctrlreq_setup_phase(dev, setup) < 0) + return 0; + + switch (setup->request) { + case USB_REQ_GET_DESCRIPTOR: + case USB_REQ_GET_CONFIGURATION: + case USB_REQ_GET_INTERFACE: + case USB_REQ_GET_STATUS: + case USB_MSC_BBB_GET_MAX_LUN: + /* control transfer in-data-phase */ + if (ctrlreq_in_data_phase(dev, len, buffer) < 0) + return 0; + /* control transfer out-status-phase */ + if (ctrlreq_out_status_phase(dev) < 0) + return 0; + break; + + case USB_REQ_SET_ADDRESS: + case USB_REQ_SET_CONFIGURATION: + case USB_REQ_SET_FEATURE: + case USB_REQ_SET_INTERFACE: + case USB_REQ_CLEAR_FEATURE: + case USB_MSC_BBB_RESET: + /* control transfer in status phase */ + if (ctrlreq_in_status_phase(dev) < 0) + return 0; + break; + + case USB_REQ_SET_DESCRIPTOR: + /* control transfer out data phase */ + if (ctrlreq_out_data_phase(dev, len, buffer) < 0) + return 0; + /* control transfer in status phase */ + if (ctrlreq_in_status_phase(dev) < 0) + return 0; + break; + + default: + /* unhandled control transfer */ + return -1; + } + + dev->status = 0; + dev->act_len = len; + return len; +} + +/* + * do a bulk transfer + */ +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int len) +{ + int dir_out = usb_pipeout(pipe); + int ep = usb_pipeendpoint(pipe); + int devnum = usb_pipedevice(pipe); + u8 type; + u16 csr; + u32 txlen = 0; + u32 nextlen = 0; + u8 devspeed; + + /* select bulk endpoint */ + writeb(MUSB_BULK_EP, &musbr->index); + + /* write the address of the device */ + if (dir_out) + writeb(devnum, &musbr->tar[MUSB_BULK_EP].txfuncaddr); + else + writeb(devnum, &musbr->tar[MUSB_BULK_EP].rxfuncaddr); + + /* configure the hub address and the port number as required */ + devspeed = get_dev_speed(dev); + if ((musb_ishighspeed()) && (dev->parent != NULL) && + (devspeed != MUSB_TYPE_SPEED_HIGH)) { + /* + * MUSB is in high speed and the destination device is full + * speed device. So configure the hub address and port + * address registers. + */ + config_hub_port(dev, MUSB_BULK_EP); + } else { + if (dir_out) { + writeb(0, &musbr->tar[MUSB_BULK_EP].txhubaddr); + writeb(0, &musbr->tar[MUSB_BULK_EP].txhubport); + } else { + writeb(0, &musbr->tar[MUSB_BULK_EP].rxhubaddr); + writeb(0, &musbr->tar[MUSB_BULK_EP].rxhubport); + } + devspeed = musb_cfg.musb_speed; + } + + /* Write the saved toggle bit value */ + write_toggle(dev, ep, dir_out); + + if (dir_out) { /* bulk-out transfer */ + /* Program the TxType register */ + type = (devspeed << MUSB_TYPE_SPEED_SHIFT) | + (MUSB_TYPE_PROTO_BULK << MUSB_TYPE_PROTO_SHIFT) | + (ep & MUSB_TYPE_REMOTE_END); + writeb(type, &musbr->txtype); + + /* Write maximum packet size to the TxMaxp register */ + writew(dev->epmaxpacketout[ep], &musbr->txmaxp); + while (txlen < len) { + nextlen = ((len-txlen) < dev->epmaxpacketout[ep]) ? + (len-txlen) : dev->epmaxpacketout[ep]; + + /* Write the data to the FIFO */ + write_fifo(MUSB_BULK_EP, nextlen, + (void *)(((u8 *)buffer) + txlen)); + + /* Set the TxPktRdy bit */ + csr = readw(&musbr->txcsr); + writew(csr | MUSB_TXCSR_TXPKTRDY, &musbr->txcsr); + + /* Wait until the TxPktRdy bit is cleared */ + if (!wait_until_txep_ready(dev, MUSB_BULK_EP)) { + readw(&musbr->txcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_TXCSR_H_DATATOGGLE_SHIFT) & 1); + dev->act_len = txlen; + return 0; + } + txlen += nextlen; + } + + /* Keep a copy of the data toggle bit */ + csr = readw(&musbr->txcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_TXCSR_H_DATATOGGLE_SHIFT) & 1); + } else { /* bulk-in transfer */ + /* Write the saved toggle bit value */ + write_toggle(dev, ep, dir_out); + + /* Program the RxType register */ + type = (devspeed << MUSB_TYPE_SPEED_SHIFT) | + (MUSB_TYPE_PROTO_BULK << MUSB_TYPE_PROTO_SHIFT) | + (ep & MUSB_TYPE_REMOTE_END); + writeb(type, &musbr->rxtype); + + /* Write the maximum packet size to the RxMaxp register */ + writew(dev->epmaxpacketin[ep], &musbr->rxmaxp); + while (txlen < len) { + nextlen = ((len-txlen) < dev->epmaxpacketin[ep]) ? + (len-txlen) : dev->epmaxpacketin[ep]; + + /* Set the ReqPkt bit */ + writew(MUSB_RXCSR_H_REQPKT, &musbr->rxcsr); + + /* Wait until the RxPktRdy bit is set */ + if (!wait_until_rxep_ready(dev, MUSB_BULK_EP)) { + csr = readw(&musbr->rxcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1); + csr &= ~MUSB_RXCSR_RXPKTRDY; + writew(csr, &musbr->rxcsr); + dev->act_len = txlen; + return 0; + } + + /* Read the data from the FIFO */ + read_fifo(MUSB_BULK_EP, nextlen, + (void *)(((u8 *)buffer) + txlen)); + + /* Clear the RxPktRdy bit */ + csr = readw(&musbr->rxcsr); + csr &= ~MUSB_RXCSR_RXPKTRDY; + writew(csr, &musbr->rxcsr); + txlen += nextlen; + } + + /* Keep a copy of the data toggle bit */ + csr = readw(&musbr->rxcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1); + } + + /* bulk transfer is complete */ + dev->status = 0; + dev->act_len = len; + return 0; +} + +/* + * This function initializes the usb controller module. + */ +int usb_lowlevel_init(void) +{ + u8 power; + u32 timeout; + + if (musb_platform_init() == -1) + return -1; + + /* Configure all the endpoint FIFO's and start usb controller */ + musbr = musb_cfg.regs; + musb_configure_ep(&epinfo[0], + sizeof(epinfo) / sizeof(struct musb_epinfo)); + musb_start(); + + /* + * Wait until musb is enabled in host mode with a timeout. There + * should be a usb device connected. + */ + timeout = musb_cfg.timeout; + while (timeout--) + if (readb(&musbr->devctl) & MUSB_DEVCTL_HM) + break; + + /* if musb core is not in host mode, then return */ + if (!timeout) + return -1; + + /* start usb bus reset */ + power = readb(&musbr->power); + writeb(power | MUSB_POWER_RESET, &musbr->power); + + /* After initiating a usb reset, wait for about 20ms to 30ms */ + udelay(30000); + + /* stop usb bus reset */ + power = readb(&musbr->power); + power &= ~MUSB_POWER_RESET; + writeb(power, &musbr->power); + + /* Determine if the connected device is a high/full/low speed device */ + musb_cfg.musb_speed = (readb(&musbr->power) & MUSB_POWER_HSMODE) ? + MUSB_TYPE_SPEED_HIGH : + ((readb(&musbr->devctl) & MUSB_DEVCTL_FSDEV) ? + MUSB_TYPE_SPEED_FULL : MUSB_TYPE_SPEED_LOW); + return 0; +} + +/* + * This function stops the operation of the davinci usb module. + */ +int usb_lowlevel_stop(void) +{ + /* Reset the USB module */ + musb_platform_deinit(); + writeb(0, &musbr->devctl); + return 0; +} + +/* + * This function supports usb interrupt transfers. Currently, usb interrupt + * transfers are not supported. + */ +int submit_int_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int len, int interval) +{ + int dir_out = usb_pipeout(pipe); + int ep = usb_pipeendpoint(pipe); + int devnum = usb_pipedevice(pipe); + u8 type; + u16 csr; + u32 txlen = 0; + u32 nextlen = 0; + u8 devspeed; + + /* select interrupt endpoint */ + writeb(MUSB_INTR_EP, &musbr->index); + + /* write the address of the device */ + if (dir_out) + writeb(devnum, &musbr->tar[MUSB_INTR_EP].txfuncaddr); + else + writeb(devnum, &musbr->tar[MUSB_INTR_EP].rxfuncaddr); + + /* configure the hub address and the port number as required */ + devspeed = get_dev_speed(dev); + if ((musb_ishighspeed()) && (dev->parent != NULL) && + (devspeed != MUSB_TYPE_SPEED_HIGH)) { + /* + * MUSB is in high speed and the destination device is full + * speed device. So configure the hub address and port + * address registers. + */ + config_hub_port(dev, MUSB_INTR_EP); + } else { + if (dir_out) { + writeb(0, &musbr->tar[MUSB_INTR_EP].txhubaddr); + writeb(0, &musbr->tar[MUSB_INTR_EP].txhubport); + } else { + writeb(0, &musbr->tar[MUSB_INTR_EP].rxhubaddr); + writeb(0, &musbr->tar[MUSB_INTR_EP].rxhubport); + } + devspeed = musb_cfg.musb_speed; + } + + /* Write the saved toggle bit value */ + write_toggle(dev, ep, dir_out); + + if (!dir_out) { /* intrrupt-in transfer */ + /* Write the saved toggle bit value */ + write_toggle(dev, ep, dir_out); + writeb(interval, &musbr->rxinterval); + + /* Program the RxType register */ + type = (devspeed << MUSB_TYPE_SPEED_SHIFT) | + (MUSB_TYPE_PROTO_INTR << MUSB_TYPE_PROTO_SHIFT) | + (ep & MUSB_TYPE_REMOTE_END); + writeb(type, &musbr->rxtype); + + /* Write the maximum packet size to the RxMaxp register */ + writew(dev->epmaxpacketin[ep], &musbr->rxmaxp); + + while (txlen < len) { + nextlen = ((len-txlen) < dev->epmaxpacketin[ep]) ? + (len-txlen) : dev->epmaxpacketin[ep]; + + /* Set the ReqPkt bit */ + writew(MUSB_RXCSR_H_REQPKT, &musbr->rxcsr); + + /* Wait until the RxPktRdy bit is set */ + if (!wait_until_rxep_ready(dev, MUSB_INTR_EP)) { + csr = readw(&musbr->rxcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1); + csr &= ~MUSB_RXCSR_RXPKTRDY; + writew(csr, &musbr->rxcsr); + dev->act_len = txlen; + return 0; + } + + /* Read the data from the FIFO */ + read_fifo(MUSB_INTR_EP, nextlen, + (void *)(((u8 *)buffer) + txlen)); + + /* Clear the RxPktRdy bit */ + csr = readw(&musbr->rxcsr); + csr &= ~MUSB_RXCSR_RXPKTRDY; + writew(csr, &musbr->rxcsr); + txlen += nextlen; + } + + /* Keep a copy of the data toggle bit */ + csr = readw(&musbr->rxcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1); + } + + /* interrupt transfer is complete */ + dev->irq_status = 0; + dev->irq_act_len = len; + dev->irq_handle(dev); + dev->status = 0; + dev->act_len = len; + return 0; +} + + +#ifdef CONFIG_SYS_USB_EVENT_POLL +/* + * This function polls for USB keyboard data. + */ +void usb_event_poll() +{ + device_t *dev; + struct usb_device *usb_kbd_dev; + struct usb_interface_descriptor *iface; + struct usb_endpoint_descriptor *ep; + int pipe; + int maxp; + + /* Get the pointer to USB Keyboard device pointer */ + dev = device_get_by_name("usbkbd"); + usb_kbd_dev = (struct usb_device *)dev->priv; + iface = &usb_kbd_dev->config.if_desc[0]; + ep = &iface->ep_desc[0]; + pipe = usb_rcvintpipe(usb_kbd_dev, ep->bEndpointAddress); + + /* Submit a interrupt transfer request */ + maxp = usb_maxpacket(usb_kbd_dev, pipe); + usb_submit_int_msg(usb_kbd_dev, pipe, &new[0], + maxp > 8 ? 8 : maxp, ep->bInterval); +} +#endif /* CONFIG_SYS_USB_EVENT_POLL */ diff --git a/drivers/usb/musb_hcd.h b/drivers/usb/musb_hcd.h new file mode 100644 index 0000000..bb83311 --- /dev/null +++ b/drivers/usb/musb_hcd.h @@ -0,0 +1,51 @@ +/* + * Mentor USB OTG Core host controller driver. + * + * Copyright (c) 2008 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + */ + +#ifndef __MUSB_HCD_H__ +#define __MUSB_HCD_H__ + +#include "musb_core.h" +#ifdef CONFIG_USB_KEYBOARD +#include +extern unsigned char new[]; +#endif + +/* This defines the endpoint number used for control transfers */ +#define MUSB_CONTROL_EP 0 + +/* This defines the endpoint number used for bulk transfer */ +#define MUSB_BULK_EP 1 + +/* This defines the endpoint number used for interrupt transfer */ +#define MUSB_INTR_EP 2 + +/* Determine the operating speed of MUSB core */ +#define musb_ishighspeed() \ + ((readb(&musbr->power) & MUSB_POWER_HSMODE) \ + >> MUSB_POWER_HSMODE_SHIFT) + +/* extern functions */ +extern int musb_platform_init(void); +extern void musb_platform_deinit(void); + +#endif /* __MUSB_HCD_H__ */ -- cgit v1.1 From e142e9f35f8ec61e74bf8019428b003f5070c33b Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Sun, 4 Jan 2009 09:41:13 +0530 Subject: usb : musb : Adding DM6446 (TI DaVinci) platform specific USB support Adding DM6446 (TI DaVinci) platform specific USB functionality for USB Phy and VBUS initialization. Signed-off-by: Ravi Babu Signed-off-by: Swaminathan S Signed-off-by: Thomas Abraham Signed-off-by: Ajay Kumar Gupta Signed-off-by: Remy Bohmer --- drivers/usb/Makefile | 1 + drivers/usb/davinci_usb.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/davinci_usb.h | 87 +++++++++++++++++++++++++++++++++++++ 3 files changed, 194 insertions(+) create mode 100644 drivers/usb/davinci_usb.c create mode 100644 drivers/usb/davinci_usb.h (limited to 'drivers') diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 098d7b6..37dcf24 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -38,6 +38,7 @@ COBJS-$(CONFIG_USB_EHCI_FSL) += usb_ehci_fsl.o COBJS-$(CONFIG_USB_EHCI_PCI) += usb_ehci_pci.o COBJS-$(CONFIG_USB_EHCI_IXP4XX) += usb_ehci_ixp.o COBJS-$(CONFIG_MUSB_HCD) += musb_hcd.o musb_core.o +COBJS-$(CONFIG_USB_DAVINCI) += davinci_usb.o # device ifdef CONFIG_USB_DEVICE diff --git a/drivers/usb/davinci_usb.c b/drivers/usb/davinci_usb.c new file mode 100644 index 0000000..e66f660 --- /dev/null +++ b/drivers/usb/davinci_usb.c @@ -0,0 +1,106 @@ +/* + * TI's Davinci platform specific USB wrapper functions. + * + * Copyright (c) 2008 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + */ + +#include +#include +#include "davinci_usb.h" + +/* MUSB platform configuration */ +struct musb_config musb_cfg = { + (struct musb_regs *)MENTOR_USB0_BASE, + DAVINCI_USB_TIMEOUT, + 0 +}; + +/* MUSB module register overlay */ +struct davinci_usb_regs *dregs; + +/* + * Enable the USB phy + */ +static u8 phy_on(void) +{ + u32 timeout; + + /* Wait until the USB phy is turned on */ + writel(USBPHY_SESNDEN | USBPHY_VBDTCTEN, USBPHY_CTL_PADDR); + timeout = musb_cfg.timeout; + while (timeout--) + if (readl(USBPHY_CTL_PADDR) & USBPHY_PHYCLKGD) + return 1; + + /* USB phy was not turned on */ + return 0; +} + +/* + * Disable the USB phy + */ +static void phy_off(void) +{ + /* powerdown the on-chip PHY and its oscillator */ + writel(USBPHY_OSCPDWN | USBPHY_PHYPDWN, USBPHY_CTL_PADDR); +} + +/* + * This function performs Davinci platform specific initialization for usb0. + */ +int musb_platform_init(void) +{ + u32 revision; + + /* enable USB VBUS */ + enable_vbus(); + + /* start the on-chip USB phy and its pll */ + if (!phy_on()) + return -1; + + /* reset the controller */ + dregs = (struct davinci_usb_regs *)DAVINCI_USB0_BASE; + writel(1, &dregs->ctrlr); + udelay(5000); + + /* Returns zero if e.g. not clocked */ + revision = readl(&dregs->version); + if (!revision) + return -1; + + /* Disable all interrupts */ + writel(DAVINCI_USB_USBINT_MASK | DAVINCI_USB_RXINT_MASK | + DAVINCI_USB_TXINT_MASK , &dregs->intmsksetr); + return 0; +} + +/* + * This function performs Davinci platform specific deinitialization for usb0. + */ +void musb_platform_deinit(void) +{ + /* Turn of the phy */ + phy_off(); + + /* flush any interrupts */ + writel(DAVINCI_USB_USBINT_MASK | DAVINCI_USB_TXINT_MASK | + DAVINCI_USB_RXINT_MASK , &dregs->intclrr); +} diff --git a/drivers/usb/davinci_usb.h b/drivers/usb/davinci_usb.h new file mode 100644 index 0000000..d270861 --- /dev/null +++ b/drivers/usb/davinci_usb.h @@ -0,0 +1,87 @@ +/* + * TI's Davinci platform specific USB wrapper functions. + * + * Copyright (c) 2008 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + */ + +#ifndef __DAVINCI_USB_H__ +#define __DAVINCI_USB_H__ + +#include +#include "musb_core.h" + +/* Base address of DAVINCI usb0 wrapper */ +#define DAVINCI_USB0_BASE 0x01C64000 + +/* Base address of DAVINCI musb core */ +#define MENTOR_USB0_BASE (DAVINCI_USB0_BASE+0x400) + +/* + * Davinci platform USB wrapper register overlay. Note: Only the required + * registers are included in this structure. It can be expanded as required. + */ +struct davinci_usb_regs { + u32 version; + u32 ctrlr; + u32 reserved[0x20]; + u32 intclrr; + u32 intmskr; + u32 intmsksetr; +}; + +#define DAVINCI_USB_TX_ENDPTS_MASK 0x1f /* ep0 + 4 tx */ +#define DAVINCI_USB_RX_ENDPTS_MASK 0x1e /* 4 rx */ +#define DAVINCI_USB_USBINT_SHIFT 16 +#define DAVINCI_USB_TXINT_SHIFT 0 +#define DAVINCI_USB_RXINT_SHIFT 8 +#define DAVINCI_INTR_DRVVBUS 0x0100 + +#define DAVINCI_USB_USBINT_MASK 0x01ff0000 /* 8 Mentor, DRVVBUS */ +#define DAVINCI_USB_TXINT_MASK \ + (DAVINCI_USB_TX_ENDPTS_MASK << DAVINCI_USB_TXINT_SHIFT) +#define DAVINCI_USB_RXINT_MASK \ + (DAVINCI_USB_RX_ENDPTS_MASK << DAVINCI_USB_RXINT_SHIFT) +#define MGC_BUSCTL_OFFSET(_bEnd, _bOffset) \ + (0x80 + (8*(_bEnd)) + (_bOffset)) + +/* Integrated highspeed/otg PHY */ +#define USBPHY_CTL_PADDR (DAVINCI_SYSTEM_MODULE_BASE + 0x34) +#define USBPHY_PHYCLKGD (1 << 8) +#define USBPHY_SESNDEN (1 << 7) /* v(sess_end) comparator */ +#define USBPHY_VBDTCTEN (1 << 6) /* v(bus) comparator */ +#define USBPHY_PHYPLLON (1 << 4) /* override pll suspend */ +#define USBPHY_CLKO1SEL (1 << 3) +#define USBPHY_OSCPDWN (1 << 2) +#define USBPHY_PHYPDWN (1 << 0) + +/* Timeout for Davinci USB module */ +#define DAVINCI_USB_TIMEOUT 0x3FFFFFF + +/* IO Expander I2C address and VBUS enable mask */ +#define IOEXP_I2C_ADDR 0x3A +#define IOEXP_VBUSEN_MASK 1 + +/* extern functions */ +extern void lpsc_on(unsigned int id); +extern int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len); +extern int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len); +extern void enable_vbus(void); +#endif /* __DAVINCI_USB_H__ */ + -- 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') 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') 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 4e0ea0efc1e501186aca8577a4042fc6fa641602 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Wed, 21 Jan 2009 17:12:28 +0100 Subject: USB: Add EHCI support for VCT EHCI controller Signed-off-by: Stefan Roese Signed-off-by: Remy Bohmer --- drivers/usb/Makefile | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 37dcf24..b306a65 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -39,6 +39,7 @@ COBJS-$(CONFIG_USB_EHCI_PCI) += usb_ehci_pci.o COBJS-$(CONFIG_USB_EHCI_IXP4XX) += usb_ehci_ixp.o COBJS-$(CONFIG_MUSB_HCD) += musb_hcd.o musb_core.o COBJS-$(CONFIG_USB_DAVINCI) += davinci_usb.o +COBJS-$(CONFIG_USB_EHCI_VCT) += usb_ehci_vct.o # device ifdef CONFIG_USB_DEVICE -- 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.h | 5 +++ drivers/usb/usb_ehci_core.c | 98 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/usb_ehci.h b/drivers/usb/usb_ehci.h index b72498b..b3c1d5d 100644 --- a/drivers/usb/usb_ehci.h +++ b/drivers/usb/usb_ehci.h @@ -180,6 +180,11 @@ struct QH { uint32_t qh_endpt2; uint32_t qh_curtd; struct qTD qh_overlay; + /* + * Add dummy fill value to make the size of this struct + * aligned to 32 bytes + */ + uint8_t fill[16]; }; /* Low level init functions */ 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