aboutsummaryrefslogtreecommitdiff
path: root/payloads/libpayload/drivers/usb/uhci_rh.c
diff options
context:
space:
mode:
authorPatrick Georgi <patrick.georgi@coresystems.de>2008-09-02 16:06:22 +0000
committerPatrick Georgi <patrick.georgi@coresystems.de>2008-09-02 16:06:22 +0000
commitd21f68bbd588f46c23066eb8d227b51e4823de41 (patch)
tree1c9c724d292b8de59599d98e8549177329953a12 /payloads/libpayload/drivers/usb/uhci_rh.c
parent5ccfa1ac7994888f57c60b80fff80e350ab3f16c (diff)
This patch adds USB capabilities to libpayload. It requires some
memalign implementation (eg. the one I sent yesterday). Features: - UHCI controller driver - UHCI root hub driver - USB MSC (Mass Storage Class) driver - skeleton of a USB HID driver (requires better interrupt transfer handling, which is TODO) - skeleton of a USB hub driver (needs several blank spots filled in, eg. power management. Again: TODO) OHCI and EHCI are not supported, though OHCI support should be rather easy as the stack provides reasonable abstractions (or so I hope). EHCI will probably be more complicated. Isochronous transfers (eg. webcams, audio stuff, ...) are not supported. They can be, but I doubt we'll have a reason for that in the boot environment. The MSC driver was tested against a couple of USB flash drives, and should be reasonably tolerant by now. But I probably underestimate the amount of bugs present in USB flash drives, so feedback is welcome. Signed-off-by: Patrick Georgi <patrick.georgi@coresystems.de> Acked-by: Jordan Crouse <jordan.crouse@amd.com> git-svn-id: svn://svn.coreboot.org/coreboot/trunk@3560 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
Diffstat (limited to 'payloads/libpayload/drivers/usb/uhci_rh.c')
-rw-r--r--payloads/libpayload/drivers/usb/uhci_rh.c182
1 files changed, 182 insertions, 0 deletions
diff --git a/payloads/libpayload/drivers/usb/uhci_rh.c b/payloads/libpayload/drivers/usb/uhci_rh.c
new file mode 100644
index 0000000000..e703e53f21
--- /dev/null
+++ b/payloads/libpayload/drivers/usb/uhci_rh.c
@@ -0,0 +1,182 @@
+/*
+ * This file is part of the libpayload project.
+ *
+ * Copyright (C) 2008 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 "uhci.h"
+
+typedef struct {
+ int port[2];
+} rh_inst_t;
+
+#define RH_INST(dev) ((rh_inst_t*)(dev)->data)
+
+static void
+uhci_rh_enable_port (usbdev_t *dev, int port)
+{
+ u16 value;
+ hci_t *controller = dev->controller;
+ if (port == 1)
+ port = PORTSC1;
+ else
+ port = PORTSC2;
+ uhci_reg_mask16 (controller, port, ~(1 << 12), 0); /* wakeup */
+
+ uhci_reg_mask16 (controller, port, ~0, 1 << 9); /* reset */
+ mdelay (30); // >10ms
+ uhci_reg_mask16 (controller, port, ~(1 << 9), 0);
+ mdelay (1); // >5.3us per spec, <3ms because some devices make trouble
+
+ uhci_reg_mask16 (controller, port, ~0, 1 << 2); /* enable */
+ do {
+ value = uhci_reg_read16 (controller, port);
+ mdelay (1);
+ } while (((value & (1 << 2)) == 0) && (value & 0x01));
+}
+
+/* disable root hub */
+static void
+uhci_rh_disable_port (usbdev_t *dev, int port)
+{
+ hci_t *controller = dev->controller;
+ port = PORTSC2;
+ if (port == 1)
+ port = PORTSC1;
+ uhci_reg_mask16 (controller, port, ~4, 0);
+ int value;
+ do {
+ value = uhci_reg_read16 (controller, port);
+ mdelay (1);
+ } while ((value & (1 << 2)) != 0);
+}
+
+static void
+uhci_rh_scanport (usbdev_t *dev, int port)
+{
+ int portsc, offset;
+ if (port == 1) {
+ portsc = PORTSC1;
+ offset = 0;
+ } else if (port == 2) {
+ portsc = PORTSC2;
+ offset = 1;
+ } else
+ return;
+ int devno = RH_INST (dev)->port[offset];
+ if (devno != -1) {
+ dev->controller->devices[devno].destroy (&dev->controller->
+ devices[devno]);
+ init_device_entry (dev->controller, devno);
+ RH_INST (dev)->port[offset] = -1;
+ }
+ uhci_reg_mask16 (dev->controller, portsc, ~0, (1 << 3) | (1 << 2)); // clear port state change, enable port
+
+ if ((uhci_reg_read16 (dev->controller, portsc) & 1) != 0) {
+ int newdev;
+ usbdev_t *newdev_t;
+ // device attached
+
+ uhci_rh_disable_port (dev, port);
+ uhci_rh_enable_port (dev, port);
+
+ int lowspeed =
+ (uhci_reg_read16 (dev->controller, portsc) >> 8) & 1;
+ printf ("%sspeed device\n", (lowspeed == 1) ? "low" : "full");
+
+ newdev = set_address (dev->controller, lowspeed);
+ if (newdev == -1)
+ return;
+ newdev_t = &dev->controller->devices[newdev];
+ RH_INST (dev)->port[offset] = newdev;
+ newdev_t->address = newdev;
+ newdev_t->hub = dev->address;
+ newdev_t->port = portsc;
+ // determine responsible driver
+ newdev_t->init (newdev_t);
+ }
+}
+
+static int
+uhci_rh_report_port_changes (usbdev_t *dev)
+{
+ int stored, real;
+
+ stored = (RH_INST (dev)->port[0] == -1);
+ real = ((uhci_reg_read16 (dev->controller, PORTSC1) & 1) == 0);
+ if (stored != real)
+ return 1;
+
+ stored = (RH_INST (dev)->port[1] == -1);
+ real = ((uhci_reg_read16 (dev->controller, PORTSC2) & 1) == 0);
+ if (stored != real)
+ return 2;
+
+// maybe detach+attach happened between two scans?
+ if ((uhci_reg_read16 (dev->controller, PORTSC1) & 2) > 0)
+ return 1;
+ if ((uhci_reg_read16 (dev->controller, PORTSC2) & 2) > 0)
+ return 2;
+
+// no change
+ return -1;
+}
+
+static void
+uhci_rh_destroy (usbdev_t *dev)
+{
+ uhci_rh_disable_port (dev, 1);
+ uhci_rh_disable_port (dev, 2);
+ free (RH_INST (dev));
+}
+
+static void
+uhci_rh_poll (usbdev_t *dev)
+{
+ int port;
+ while ((port = uhci_rh_report_port_changes (dev)) != -1)
+ uhci_rh_scanport (dev, port);
+}
+
+void
+uhci_rh_init (usbdev_t *dev)
+{
+ dev->destroy = uhci_rh_destroy;
+ dev->poll = uhci_rh_poll;
+
+ uhci_rh_enable_port (dev, 1);
+ uhci_rh_enable_port (dev, 2);
+ dev->data = malloc (sizeof (rh_inst_t));
+ RH_INST (dev)->port[0] = -1;
+ RH_INST (dev)->port[1] = -1;
+
+ /* we can set them here because a root hub _really_ shouldn't
+ appear elsewhere */
+ dev->address = 0;
+ dev->hub = -1;
+ dev->port = -1;
+}