aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Georgi <patrick.georgi@coresystems.de>2010-09-25 17:01:13 +0000
committerPatrick Georgi <patrick.georgi@coresystems.de>2010-09-25 17:01:13 +0000
commit7f43dc1060adfaf82b7b58be9a26dc714d686f5a (patch)
treea9300e20a41cb82860707c961985c7e8c289ba3f
parent5df4168db81d6a113922af66034c6e32f85adb08 (diff)
Add an EHCI driver to libpayload's USB stack.
Interrupt transfer support is missing (ie. no keyboard), bulk and control transfers work (ie. mass storage). Signed-off-by: Patrick Georgi <patrick.georgi@coresystems.de> Acked-by: Peter Stuge <peter@stuge.se> git-svn-id: svn://svn.coreboot.org/coreboot/trunk@5845 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
-rw-r--r--payloads/libpayload/Config.in1
-rw-r--r--payloads/libpayload/configs/defconfig7
-rw-r--r--payloads/libpayload/drivers/Makefile.inc2
-rw-r--r--payloads/libpayload/drivers/usb/ehci.c359
-rw-r--r--payloads/libpayload/drivers/usb/ehci.h40
-rw-r--r--payloads/libpayload/drivers/usb/ehci_private.h266
-rw-r--r--payloads/libpayload/drivers/usb/ehci_rh.c145
-rw-r--r--payloads/libpayload/drivers/usb/usbinit.c5
8 files changed, 818 insertions, 7 deletions
diff --git a/payloads/libpayload/Config.in b/payloads/libpayload/Config.in
index 5ac35faeca..0a9265d733 100644
--- a/payloads/libpayload/Config.in
+++ b/payloads/libpayload/Config.in
@@ -230,7 +230,6 @@ config USB_EHCI
depends on USB
help
Select this option if you want to use USB 2.0
- NOTE: This option is not (fully) implemented yet
config USB_XHCI
bool "Support for USB xHCI controllers"
diff --git a/payloads/libpayload/configs/defconfig b/payloads/libpayload/configs/defconfig
index d7704bc743..77b71b7205 100644
--- a/payloads/libpayload/configs/defconfig
+++ b/payloads/libpayload/configs/defconfig
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
# libpayload version: 0.2.0
-# Fri Mar 26 12:45:27 2010
+# Sat Sep 25 14:01:26 2010
#
#
@@ -49,8 +49,9 @@ CONFIG_NVRAM=y
CONFIG_SPEAKER=y
CONFIG_USB=y
CONFIG_USB_UHCI=y
-# CONFIG_USB_OHCI is not set
-# CONFIG_USB_EHCI is not set
+CONFIG_USB_OHCI=y
+CONFIG_USB_EHCI=y
+CONFIG_USB_XHCI=y
CONFIG_USB_HID=y
CONFIG_USB_HUB=y
CONFIG_USB_MSC=y
diff --git a/payloads/libpayload/drivers/Makefile.inc b/payloads/libpayload/drivers/Makefile.inc
index 134119fce4..e9496a60ed 100644
--- a/payloads/libpayload/drivers/Makefile.inc
+++ b/payloads/libpayload/drivers/Makefile.inc
@@ -62,6 +62,8 @@ TARGETS-$(CONFIG_USB_UHCI) += drivers/usb/uhci.o
TARGETS-$(CONFIG_USB_UHCI) += drivers/usb/uhci_rh.o
TARGETS-$(CONFIG_USB_OHCI) += drivers/usb/ohci.o
TARGETS-$(CONFIG_USB_OHCI) += drivers/usb/ohci_rh.o
+TARGETS-$(CONFIG_USB_EHCI) += drivers/usb/ehci.o
+TARGETS-$(CONFIG_USB_EHCI) += drivers/usb/ehci_rh.o
TARGETS-$(CONFIG_USB_XHCI) += drivers/usb/xhci.o
TARGETS-$(CONFIG_USB_XHCI) += drivers/usb/xhci_rh.o
TARGETS-$(CONFIG_USB_HID) += drivers/usb/usbhid.o
diff --git a/payloads/libpayload/drivers/usb/ehci.c b/payloads/libpayload/drivers/usb/ehci.c
new file mode 100644
index 0000000000..93cc00ced1
--- /dev/null
+++ b/payloads/libpayload/drivers/usb/ehci.c
@@ -0,0 +1,359 @@
+/*
+ * This file is part of the libpayload project.
+ *
+ * Copyright (C) 2010 coresystems GmbH
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <libpayload.h>
+#include "ehci.h"
+#include "ehci_private.h"
+
+static void dump_td(u32 addr) {
+ qtd_t *td = phys_to_virt(addr);
+ printf("td at phys(%x): active: %x, halted: %x, data_buf_err: %x\n babble: %x, xact_err: %x, missed_mframe: %x\n splitxstate: %x, perr: %x\n\n",
+ addr, td->active, td->halted, td->data_buf_err, td->babble, td->xact_err, td->missed_mframe, td->splitxstate, td->perr);
+ printf("- cerr: %x, total_len: %x\n\n", td->cerr, td->total_len);
+}
+
+static void ehci_start (hci_t *controller)
+{
+ EHCI_INST(controller)->operation->rs = 1;
+}
+
+static void ehci_stop (hci_t *controller)
+{
+ EHCI_INST(controller)->operation->rs = 0;
+}
+
+static void ehci_reset (hci_t *controller)
+{
+
+}
+
+static void ehci_shutdown (hci_t *controller)
+{
+
+}
+
+enum { EHCI_OUT=0, EHCI_IN=1, EHCI_SETUP=2 };
+
+/* returns handled bytes */
+int fill_td(qtd_t *td, void* data, int datalen) {
+ u32 total_len = 0;
+ u32 page_minus_1 = 0;
+
+ u32 start = virt_to_phys(data);
+ u32 page = start & ~4095;
+ u32 offset = start & 4095;
+ u32 page_len = 4096 - offset;
+
+ td->c_page = 0;
+ td->bufptr0 = page;
+ td->cur_off = offset;
+
+ if (datalen <= page_len) {
+ total_len = datalen;
+ } else {
+ datalen -= page_len;
+ total_len += page_len;
+
+ do {
+ /* we have a continguous mapping between virtual and physical memory */
+ page += 4096;
+
+ td->bufptrs[page_minus_1] = page;
+ if (datalen <= 4096) {
+ total_len += datalen;
+ break;
+ }
+ page_minus_1++;
+ datalen -= 4096;
+ total_len += 4096;
+ } while (page_minus_1<4);
+ }
+ td->total_len = total_len;
+ return total_len;
+}
+
+/* free up data structures */
+void free_qh_and_tds(ehci_qh_t *qh, qtd_t *cur) {
+ qtd_t *next;
+ while (cur) {
+ next = (qtd_t*)phys_to_virt(cur->next_qtd & ~31);
+ free(cur);
+ cur = next;
+ }
+ free(qh);
+}
+
+int wait_for_tds(qtd_t *head) {
+ int result = 0;
+ qtd_t *cur = head;
+ while (1) {
+ if (0) dump_td(virt_to_phys(cur));
+ while (cur->active && !cur->halted) udelay(60);
+ if (cur->halted) {
+ printf("ERROR with packet\n");
+ dump_td(virt_to_phys(cur));
+ printf("-----------------\n");
+ return 1;
+ }
+ if (cur->next_qtd & 1) {
+ return 0;
+ }
+ if (0) dump_td(virt_to_phys(cur));
+ /* helps debugging the TD chain */
+ if (0) printf("\nmoving from %x to %x\n", cur, phys_to_virt(cur->next_qtd));
+ cur = phys_to_virt(cur->next_qtd);
+ }
+ return result;
+}
+
+static int ehci_bulk (endpoint_t *ep, int size, u8 *data, int finalize)
+{
+ int result = 0;
+ int endp = ep->endpoint & 0xf;
+ int pid = (ep->direction==IN)?EHCI_IN:EHCI_OUT;
+
+ qtd_t *head = memalign(32, sizeof(qtd_t));
+ qtd_t *cur = head;
+ while (1) {
+ memset(cur, 0, sizeof(qtd_t));
+ cur->active = 1;
+ cur->pid = pid;
+ cur->cerr = 0;
+ u32 chunk = fill_td(cur, data, size);
+ size -= chunk;
+ data += chunk;
+
+ cur->alt_terminate = 1;
+ if (size == 0) {
+ cur->next_qtd = virt_to_phys(0);
+ cur->terminate = 1;
+ break;
+ } else {
+ qtd_t *next = memalign(32, sizeof(qtd_t));
+ cur->next_qtd = virt_to_phys(next);
+ cur = next;
+ }
+ }
+
+ /* create QH */
+ ehci_qh_t *qh = memalign(32, sizeof(ehci_qh_t));
+ memset(qh, 0, sizeof(ehci_qh_t));
+ qh->horiz_link_ptr = virt_to_phys(qh);
+ qh->type = 1; // FIXME: proper symbols for type. this is QH
+ qh->addr = ep->dev->address;
+ qh->ep = endp;
+ qh->eps = ep->dev->speed;
+ qh->dtc = 0;
+ qh->reclaim_head = 1;
+ qh->max_packet_len = ep->maxpacketsize;
+ qh->nak_cnt_reload = 0;
+ qh->pipe_multiplier = 3;
+
+ qh->td.next_qtd = virt_to_phys(head);
+ qh->td.dt = ep->toggle;
+ head->dt = ep->toggle;
+
+ /* hook up QH */
+ EHCI_INST(ep->dev->controller)->operation->asynclistaddr = virt_to_phys(qh);
+
+ /* start async schedule */
+ EHCI_INST(ep->dev->controller)->operation->async_sched_enable = 1;
+ while (!EHCI_INST(ep->dev->controller)->operation->async_sched_status) ; /* wait */
+
+ /* wait for result */
+ result = wait_for_tds(head);
+
+ /* disable async schedule */
+ EHCI_INST(ep->dev->controller)->operation->async_sched_enable = 0;
+ while (EHCI_INST(ep->dev->controller)->operation->async_sched_status) ; /* wait */
+
+ ep->toggle = cur->dt;
+
+ free_qh_and_tds(qh, head);
+ return result;
+}
+
+
+/* FIXME: Handle control transfers as 3 QHs, so the 2nd stage can be >0x4000 bytes */
+static int ehci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq,
+ int dalen, u8 *data)
+{
+ int endp = 0; // this is control. always 0 (for now)
+ int toggle = 0;
+ int mlen = dev->endpoints[0].maxpacketsize;
+ int result = 0;
+
+ /* create qTDs */
+ qtd_t *head = memalign(32, sizeof(qtd_t));
+ qtd_t *cur = head;
+ memset(cur, 0, sizeof(qtd_t));
+ cur->active = 1;
+ cur->dt = toggle;
+ cur->pid = EHCI_SETUP;
+ cur->cerr = 3;
+ if (fill_td(cur, devreq, drlen) != drlen) {
+ printf("ERROR: couldn't send the entire device request\n");
+ }
+ qtd_t *next = memalign(32, sizeof(qtd_t));
+ cur->next_qtd = virt_to_phys(next);
+ cur->alt_terminate = 1;
+
+ /* FIXME: We're limited to 16-20K (depending on alignment) for payload for now.
+ * Figure out, how toggle can be set sensibly in this scenario */
+ if (dalen > 0) {
+ toggle ^= 1;
+ cur = next;
+ memset(cur, 0, sizeof(qtd_t));
+ cur->active = 1;
+ cur->dt = toggle;
+ cur->pid = (dir == OUT)?EHCI_OUT:EHCI_IN;
+ cur->cerr = 3;
+ if (fill_td(cur, data, dalen) != dalen) {
+ printf("ERROR: couldn't send the entire control payload\n");
+ }
+ next = memalign(32, sizeof(qtd_t));
+ cur->next_qtd = virt_to_phys(next);
+ cur->alt_terminate = 1;
+ }
+
+ toggle = 1;
+ cur = next;
+ memset(cur, 0, sizeof(qtd_t));
+ cur->active = 1;
+ cur->dt = toggle;
+ cur->pid = (dir == OUT)?EHCI_IN:EHCI_OUT;
+ cur->cerr = 0;
+ fill_td(cur, NULL, 0);
+ cur->next_qtd = virt_to_phys(0);
+ cur->terminate = 1;
+ cur->alt_terminate = 1;
+
+ /* create QH */
+ ehci_qh_t *qh = memalign(32, sizeof(ehci_qh_t));
+ memset(qh, 0, sizeof(ehci_qh_t));
+ qh->horiz_link_ptr = virt_to_phys(qh);
+ qh->type = 1; // FIXME: proper symbols for type. this is QH
+ qh->addr = dev->address;
+ qh->ep = endp;
+ qh->eps = dev->speed;
+ qh->dtc = 1; /* Take data toggle from TD, as control transfers are special */
+ qh->reclaim_head = 1;
+ qh->max_packet_len = mlen;
+ qh->non_hs_control_ep = 0; // no support for non-HS devices at this time
+ qh->nak_cnt_reload = 0;
+ qh->pipe_multiplier = 3;
+ qh->td.next_qtd = virt_to_phys(head);
+
+ /* hook up QH */
+ EHCI_INST(dev->controller)->operation->asynclistaddr = virt_to_phys(qh);
+
+ /* start async schedule */
+ EHCI_INST(dev->controller)->operation->async_sched_enable = 1;
+ while (!EHCI_INST(dev->controller)->operation->async_sched_status) ; /* wait */
+
+ result = wait_for_tds(head);
+
+ /* disable async schedule */
+ EHCI_INST(dev->controller)->operation->async_sched_enable = 0;
+ while (EHCI_INST(dev->controller)->operation->async_sched_status) ; /* wait */
+
+ free_qh_and_tds(qh, head);
+ return result;
+}
+
+static void* ehci_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming)
+{
+ return NULL;
+}
+
+static void ehci_destroy_intr_queue (endpoint_t *ep, void *queue)
+{
+}
+
+static u8* ehci_poll_intr_queue (void *queue)
+{
+ return NULL;
+}
+
+hci_t *
+ehci_init (pcidev_t addr)
+{
+ int i;
+ hci_t *controller = new_controller ();
+
+ if (!controller)
+ usb_fatal("Could not create USB controller instance.\n");
+
+ controller->instance = malloc (sizeof (ehci_t));
+ if(!controller->instance)
+ usb_fatal("Not enough memory creating USB controller instance.\n");
+
+#define PCI_COMMAND 4
+#define PCI_COMMAND_IO 1
+#define PCI_COMMAND_MEMORY 2
+#define PCI_COMMAND_MASTER 4
+
+ u32 pci_command = pci_read_config32(addr, PCI_COMMAND);
+ pci_command = (pci_command | PCI_COMMAND_MEMORY) & ~PCI_COMMAND_IO ;
+ pci_write_config32(addr, PCI_COMMAND, pci_command);
+
+ controller->start = ehci_start;
+ controller->stop = ehci_stop;
+ controller->reset = ehci_reset;
+ controller->shutdown = ehci_shutdown;
+ controller->bulk = ehci_bulk;
+ controller->control = ehci_control;
+ controller->create_intr_queue = ehci_create_intr_queue;
+ controller->destroy_intr_queue = ehci_destroy_intr_queue;
+ controller->poll_intr_queue = ehci_poll_intr_queue;
+ for (i = 0; i < 128; i++) {
+ controller->devices[i] = 0;
+ }
+ init_device_entry (controller, 0);
+
+ EHCI_INST(controller)->capabilities = phys_to_virt(pci_read_config32(addr, USBBASE));
+ EHCI_INST(controller)->operation = (hc_op_t *)(phys_to_virt(pci_read_config32(addr, USBBASE)) + EHCI_INST(controller)->capabilities->caplength);
+
+ /* default value for frame length adjust */
+ pci_write_config8(addr, FLADJ, FLADJ_framelength(60000));
+
+ /* Enable operation of controller */
+ controller->start(controller);
+
+ /* take over all ports. USB1 should be blind now */
+ EHCI_INST(controller)->operation->configflag = 1;
+
+ /* TODO lots of stuff missing */
+
+ controller->devices[0]->controller = controller;
+ controller->devices[0]->init = ehci_rh_init;
+ controller->devices[0]->init (controller->devices[0]);
+
+ return controller;
+}
diff --git a/payloads/libpayload/drivers/usb/ehci.h b/payloads/libpayload/drivers/usb/ehci.h
new file mode 100644
index 0000000000..c66643c702
--- /dev/null
+++ b/payloads/libpayload/drivers/usb/ehci.h
@@ -0,0 +1,40 @@
+/*
+ * This file is part of the libpayload project.
+ *
+ * Copyright (C) 2010 coresystems GmbH
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __EHCI_H
+#define __EHCI_H
+
+#include <pci.h>
+#include <usb/usb.h>
+
+hci_t *ehci_init (pcidev_t addr);
+
+void ehci_rh_init (usbdev_t *dev);
+
+#endif
diff --git a/payloads/libpayload/drivers/usb/ehci_private.h b/payloads/libpayload/drivers/usb/ehci_private.h
new file mode 100644
index 0000000000..e63a81db13
--- /dev/null
+++ b/payloads/libpayload/drivers/usb/ehci_private.h
@@ -0,0 +1,266 @@
+/*
+ * This file is part of the libpayload project.
+ *
+ * Copyright (C) 2010 coresystems GmbH
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __EHCI_PRIVATE_H
+#define __EHCI_PRIVATE_H
+
+#include <pci.h>
+#include <usb/usb.h>
+
+#define USBBASE 0x10
+#define FLADJ 0x61
+#define FLADJ_framelength(x) (((x)-59488)/16)
+
+typedef union {
+ u32 val;
+ volatile struct {
+ unsigned long current_conn_status:1;
+ unsigned long conn_status_change:1;
+ unsigned long port_enable:1;
+ unsigned long port_enable_change:1;
+ unsigned long overcurrent:1;
+ unsigned long overcurrent_change:1;
+ unsigned long force_port_resume:1;
+ unsigned long suspend:1;
+ unsigned long port_reset:1;
+ unsigned long:1;
+ unsigned long line_status:2;
+ unsigned long pp:1;
+ unsigned long port_owner:1;
+ unsigned long port_indicator_control:2;
+ unsigned long port_test_control:4;
+ unsigned long wake_on_connect_en:1;
+ unsigned long wake_on_disconnect_en:1;
+ unsigned long wake_on_overcurrent_en:1;
+ unsigned long:9;
+ } __attribute__ ((packed));
+} __attribute__ ((packed)) portsc_t;
+
+typedef struct {
+ u8 caplength;
+ u8 res1;
+ u16 hciversion;
+ union {
+ u32 hcsparams;
+ struct {
+ unsigned long n_ports:4;
+ unsigned long ppc:1;
+ unsigned long:2;
+ unsigned long port_routing_rules:1;
+ unsigned long n_pcc:4;
+ unsigned long n_cc:4;
+ unsigned long p_indicator:1;
+ unsigned long:3;
+ unsigned long debug_port_number:4;
+ unsigned long:8;
+ } __attribute__ ((packed));
+ };
+ union {
+ u32 hccparams;
+ struct {
+ unsigned long cap_64b_addr:1;
+ unsigned long cap_prog_framelist_size:1;
+ unsigned long cap_async_park:1;
+ unsigned long:1;
+ unsigned long isoc_sched_threshold:4;
+ unsigned long eecp:8;
+ unsigned long:16;
+ } __attribute__ ((packed));
+ };
+ union {
+ u64 hcsp_portroute;
+ struct {
+ unsigned long portroute0:4;
+ unsigned long portroute1:4;
+ unsigned long portroute2:4;
+ unsigned long portroute3:4;
+ unsigned long portroute4:4;
+ unsigned long portroute5:4;
+ unsigned long portroute6:4;
+ unsigned long portroute7:4;
+ unsigned long portroute8:4;
+ unsigned long portroute9:4;
+ unsigned long portroute10:4;
+ unsigned long portroute11:4;
+ unsigned long portroute12:4;
+ unsigned long portroute13:4;
+ unsigned long portroute14:4;
+ unsigned long portroute15:4;
+ unsigned long:4;
+ } __attribute__ ((packed));
+ };
+} __attribute__ ((packed)) hc_cap_t;
+
+typedef struct {
+ union {
+ u32 usbcmd;
+ volatile struct {
+ unsigned long rs:1;
+ unsigned long hcreset:1;
+ unsigned long frame_list_size:2;
+ unsigned long periodic_sched_enable:1;
+ unsigned long async_sched_enable:1;
+ unsigned long irq_on_async_advance_doorbell:1;
+ unsigned long light_hc_reset:1;
+ unsigned long async_sched_park_mode_count:2;
+ unsigned long:1;
+ unsigned long async_sched_park_mode_enable:1;
+ unsigned long:4;
+ unsigned long irq_threshold_count:8;
+ unsigned long:8;
+ } __attribute__ ((packed));
+ };
+ union {
+ u32 usbsts;
+ struct {
+ unsigned long usbint:1;
+ unsigned long usberrint:1;
+ unsigned long port_change_detect:1;
+ unsigned long frame_list_rollover:1;
+ unsigned long host_system_error:1;
+ unsigned long irq_on_async_advance:1;
+ unsigned long:6;
+ unsigned long hchalted:1;
+ unsigned long reclamation:1;
+ unsigned long periodic_sched_status:1;
+ unsigned long async_sched_status:1;
+ unsigned long:16;
+ } __attribute__ ((packed));
+ };
+ union {
+ u32 usbintr;
+ struct {
+ unsigned long en_usb_irq:1;
+ unsigned long en_usb_err_irq:1;
+ unsigned long en_port_change_irq:1;
+ unsigned long en_frame_list_rollover_irq:1;
+ unsigned long en_host_system_error_irq:1;
+ unsigned long en_irq_on_async_advance:1;
+ unsigned long:26;
+ } __attribute__ ((packed));
+ };
+ u32 frindex;
+ u32 ctrldssegment;
+ u32 periodiclistbase;
+ u32 asynclistaddr;
+ u8 res1[0x3f-0x1c];
+ u32 configflag;
+ portsc_t portsc[0];
+} hc_op_t;
+
+typedef struct {
+ union {
+ u32 next_qtd;
+ struct {
+ unsigned long terminate:1;
+ unsigned long:4;
+ unsigned long:27;
+ } __attribute__ ((packed));
+ };
+ union {
+ u32 alt_next_qtd;
+ struct {
+ unsigned long alt_terminate:1;
+ unsigned long:4;
+ unsigned long:27;
+ } __attribute__ ((packed));
+ };
+ struct {
+ union {
+ volatile u8 status;
+ struct {
+ volatile unsigned long perr:1;
+ volatile unsigned long splitxstate:1;
+ volatile unsigned long missed_mframe:1;
+ volatile unsigned long xact_err:1;
+ volatile unsigned long babble:1;
+ volatile unsigned long data_buf_err:1;
+ volatile unsigned long halted:1;
+ volatile unsigned long active:1;
+ } __attribute__ ((packed));
+ };
+ unsigned long pid:2;
+ volatile unsigned long cerr:2;
+ volatile unsigned long c_page:3;
+ unsigned long ioc:1;
+ volatile unsigned long total_len:15;
+ volatile unsigned long dt:1;
+ } __attribute__ ((packed));
+ union {
+ u32 bufptr0;
+ struct {
+ volatile unsigned long cur_off:12;
+ unsigned long:20;
+ } __attribute__ ((packed));
+ };
+ u32 bufptrs[4];
+ u32 bufptrs64[5];
+} __attribute__ ((packed)) qtd_t;
+
+typedef struct {
+ union {
+ u32 horiz_link_ptr;
+ struct {
+ unsigned long terminate:1;
+ unsigned long type:2;
+ unsigned long:1;
+ unsigned long:28;
+ } __attribute__ ((packed));
+ };
+ struct {
+ unsigned long addr:7;
+ unsigned long inactivate:1;
+ unsigned long ep:4;
+ unsigned long eps:2;
+ unsigned long dtc:1;
+ unsigned long reclaim_head:1;
+ unsigned long max_packet_len:11;
+ unsigned long non_hs_control_ep:1;
+ unsigned long nak_cnt_reload:4;
+ } __attribute__ ((packed));
+ struct {
+ unsigned long irq_sched_mask:8;
+ unsigned long split_compl_mask:8;
+ unsigned long hub_addr:7;
+ unsigned long port_num:7;
+ unsigned long pipe_multiplier:2;
+ } __attribute__ ((packed));
+ volatile u32 current_td_ptr;
+ volatile qtd_t td;
+} ehci_qh_t;
+
+typedef struct ehci {
+ hc_cap_t *capabilities;
+ hc_op_t *operation;
+} ehci_t;
+
+
+#define EHCI_INST(controller) ((ehci_t*)((controller)->instance))
+
+#endif
diff --git a/payloads/libpayload/drivers/usb/ehci_rh.c b/payloads/libpayload/drivers/usb/ehci_rh.c
new file mode 100644
index 0000000000..5eaeeb690e
--- /dev/null
+++ b/payloads/libpayload/drivers/usb/ehci_rh.c
@@ -0,0 +1,145 @@
+/*
+ * This file is part of the libpayload project.
+ *
+ * Copyright (C) 2010 coresystems GmbH
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <libpayload.h>
+#include "ehci.h"
+#include "ehci_private.h"
+
+typedef struct {
+ int n_ports;
+ /* typical C, n_ports is the number
+ * of ports, while ports[] spans [0,n_ports-1],
+ * even though the spec counts from 1.
+ */
+ volatile portsc_t *ports;
+ int *devices;
+} rh_inst_t;
+
+#define RH_INST(dev) ((rh_inst_t*)(dev)->data)
+
+static void
+ehci_rh_destroy (usbdev_t *dev)
+{
+ free (RH_INST(dev)->devices);
+ free (RH_INST(dev));
+}
+
+static void
+ehci_rh_hand_over_port (usbdev_t *dev, int port)
+{
+ volatile portsc_t *p = &(RH_INST(dev)->ports[port]);
+
+ printf("giving up port %x, it's USB1\n", port+1);
+
+ /* Lowspeed device. Hand over to companion */
+ p->port_owner = 1;
+ do {} while (!p->conn_status_change);
+ /* RW/C register, so clear it by writing 1 */
+ p->conn_status_change = 1;
+ return;
+}
+
+static void
+ehci_rh_scanport (usbdev_t *dev, int port)
+{
+ volatile portsc_t *p = &(RH_INST(dev)->ports[port]);
+ if (RH_INST(dev)->devices[port]!=-1) {
+ printf("Unregister device at port %x\n", port+1);
+ usb_detach_device(dev->controller, RH_INST(dev)->devices[port]);
+ RH_INST(dev)->devices[port]=-1;
+ }
+ /* device connected, handle */
+ if (p->current_conn_status) {
+ mdelay(100);
+ if (p->line_status == 0x1) {
+ ehci_rh_hand_over_port(dev, port);
+ return;
+ }
+ p->port_enable = 0;
+ p->port_reset = 1;
+ mdelay(50);
+ p->port_reset = 0;
+ /* Wait for flag change to finish. The controller might take a while */
+ while (p->port_reset) ;
+ if (!p->port_enable) {
+ ehci_rh_hand_over_port(dev, port);
+ return;
+ }
+ printf("port %x hosts a USB2 device\n", port+1);
+ RH_INST(dev)->devices[port] = usb_attach_device(dev->controller, dev->address, port, 2);
+ }
+ /* RW/C register, so clear it by writing 1 */
+ p->conn_status_change = 1;
+}
+
+static int
+ehci_rh_report_port_changes (usbdev_t *dev)
+{
+ int i;
+ for (i=0; i<RH_INST(dev)->n_ports; i++) {
+ if (RH_INST(dev)->ports[i].conn_status_change)
+ return i;
+ }
+ return -1;
+}
+
+static void
+ehci_rh_poll (usbdev_t *dev)
+{
+ int port;
+ while ((port = ehci_rh_report_port_changes (dev)) != -1)
+ ehci_rh_scanport (dev, port);
+}
+
+
+void
+ehci_rh_init (usbdev_t *dev)
+{
+ int i;
+
+ dev->destroy = ehci_rh_destroy;
+ dev->poll = ehci_rh_poll;
+
+ dev->data = malloc(sizeof(rh_inst_t));
+
+ RH_INST(dev)->n_ports = EHCI_INST(dev->controller)->capabilities->n_ports;
+ RH_INST(dev)->ports = EHCI_INST(dev->controller)->operation->portsc;
+
+ printf("root hub has %x ports\n", RH_INST(dev)->n_ports);
+
+ RH_INST(dev)->devices = malloc(RH_INST(dev)->n_ports * sizeof(int));
+ for (i=0; i < RH_INST(dev)->n_ports; i++) {
+ RH_INST(dev)->devices[i] = -1;
+ RH_INST(dev)->ports[i].pp = 1;
+ }
+
+ dev->address = 0;
+ dev->hub = -1;
+ dev->port = -1;
+}
diff --git a/payloads/libpayload/drivers/usb/usbinit.c b/payloads/libpayload/drivers/usb/usbinit.c
index 50dbf97470..f0d5a60d1e 100644
--- a/payloads/libpayload/drivers/usb/usbinit.c
+++ b/payloads/libpayload/drivers/usb/usbinit.c
@@ -31,7 +31,7 @@
#include <usb/usb.h>
#include "uhci.h"
#include "ohci.h"
-//#include "ehci.h"
+#include "ehci.h"
#include "xhci.h"
#include <usb/usbdisk.h>
@@ -90,8 +90,7 @@ usb_controller_initialize (int bus, int dev, int func)
if (prog_if == 0x20) {
printf ("EHCI controller\n");
#ifdef CONFIG_USB_EHCI
- //ehci_init(addr);
- printf ("Not supported.\n");
+ ehci_init(addr);
#else
printf ("Not supported.\n");
#endif