aboutsummaryrefslogtreecommitdiff
path: root/payloads/libpayload/drivers/usb/xhci_rh.c
diff options
context:
space:
mode:
authorNico Huber <nico.huber@secunet.com>2013-06-13 14:37:15 +0200
committerStefan Reinauer <stefan.reinauer@coreboot.org>2013-06-13 22:21:20 +0200
commit9029265cf5d835f2b87fe7e25124706b59df9394 (patch)
tree0e447c79811b5d526827eb5c414e4e946a2dd424 /payloads/libpayload/drivers/usb/xhci_rh.c
parent5736fab4beb17ea1a04088d1cf16121c57ccf744 (diff)
libpayload: Fill gaps in the xHCI driver
Well, it turned out to be more as some gaps ;) but we finally have xHCI running. It's well tested against a QM77 Ivy Bridge board. We have no SuperSpeed support (yet). On Ivy Bridge, SuperSpeed is not advertised and USB 3 devices will just work at HighSpeed. There are still some bit fields in xhci_private.h, so this might need little more work to run on ARM. Change-Id: I7a2cb3f226d24573659142565db38b13acdc218c Signed-off-by: Nico Huber <nico.huber@secunet.com> Signed-off-by: Patrick Georgi <patrick.georgi@secunet.com> Reviewed-on: http://review.coreboot.org/3452 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
Diffstat (limited to 'payloads/libpayload/drivers/usb/xhci_rh.c')
-rw-r--r--payloads/libpayload/drivers/usb/xhci_rh.c142
1 files changed, 74 insertions, 68 deletions
diff --git a/payloads/libpayload/drivers/usb/xhci_rh.c b/payloads/libpayload/drivers/usb/xhci_rh.c
index 58eb6c328d..e6052be251 100644
--- a/payloads/libpayload/drivers/usb/xhci_rh.c
+++ b/payloads/libpayload/drivers/usb/xhci_rh.c
@@ -1,7 +1,7 @@
/*
* This file is part of the libpayload project.
*
- * Copyright (C) 2010 Patrick Georgi
+ * Copyright (C) 2013 secunet Security Networks AG
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -29,105 +29,111 @@
#define USB_DEBUG
-#include <libpayload.h>
+#include <usb/usb.h>
+#include "generic_hub.h"
#include "xhci_private.h"
#include "xhci.h"
-typedef struct {
- int numports;
- int *port;
-} rh_inst_t;
-
-#define RH_INST(dev) ((rh_inst_t*)(dev)->data)
-
-static void
-xhci_rh_enable_port (usbdev_t *dev, int port)
+static int
+xhci_rh_hub_status_changed(usbdev_t *const dev)
{
- // FIXME: check power situation?
- // enable slot
- // attach device context to slot
- // address device
+ xhci_t *const xhci = XHCI_INST(dev->controller);
+ const int changed = !!(xhci->opreg->usbsts & USBSTS_PCD);
+ if (changed)
+ xhci->opreg->usbsts =
+ (xhci->opreg->usbsts & USBSTS_PRSRV_MASK) | USBSTS_PCD;
+ return changed;
}
-/* disable root hub */
-static void
-xhci_rh_disable_port (usbdev_t *dev, int port)
+static int
+xhci_rh_port_status_changed(usbdev_t *const dev, const int port)
{
+ xhci_t *const xhci = XHCI_INST(dev->controller);
+ volatile u32 *const portsc = &xhci->opreg->prs[port - 1].portsc;
+
+ const int changed = !!(*portsc & PORTSC_CSC);
+ /* always clear all the status change bits */
+ *portsc = (*portsc & PORTSC_RW_MASK) | 0x00ef0000;
+ return changed;
}
-static void
-xhci_rh_scanport (usbdev_t *dev, int port)
+static int
+xhci_rh_port_connected(usbdev_t *const dev, const int port)
{
- // clear CSC
- int val = XHCI_INST (dev->controller)->opreg->prs[port].portsc;
- val &= PORTSC_RW_MASK;
- val |= PORTSC_CSC;
- XHCI_INST (dev->controller)->opreg->prs[port].portsc = val;
+ xhci_t *const xhci = XHCI_INST(dev->controller);
+ volatile u32 *const portsc = &xhci->opreg->prs[port - 1].portsc;
- usb_debug("device attach status on port %x: %x\n", port, XHCI_INST (dev->controller)->opreg->prs[port].portsc & PORTSC_CCS);
+ return *portsc & PORTSC_CCS;
}
static int
-xhci_rh_report_port_changes (usbdev_t *dev)
+xhci_rh_port_in_reset(usbdev_t *const dev, const int port)
{
- int i;
- // no change
- if (!(XHCI_INST (dev->controller)->opreg->usbsts & USBSTS_PCD))
- return -1;
+ xhci_t *const xhci = XHCI_INST(dev->controller);
+ volatile u32 *const portsc = &xhci->opreg->prs[port - 1].portsc;
- for (i = 0; i < RH_INST (dev)->numports; i++) {
- if (XHCI_INST (dev->controller)->opreg->prs[i].portsc & PORTSC_CSC) {
- usb_debug("found connect status change on port %d\n", i);
- return i;
- }
- }
-
- return -1; // shouldn't ever happen
+ return !!(*portsc & PORTSC_PR);
}
-static void
-xhci_rh_destroy (usbdev_t *dev)
+static int
+xhci_rh_port_enabled(usbdev_t *const dev, const int port)
{
- int i;
- for (i = 0; i < RH_INST (dev)->numports; i++)
- xhci_rh_disable_port (dev, i);
- free (RH_INST (dev));
+ xhci_t *const xhci = XHCI_INST(dev->controller);
+ volatile u32 *const portsc = &xhci->opreg->prs[port - 1].portsc;
+
+ return !!(*portsc & PORTSC_PED);
}
-static void
-xhci_rh_poll (usbdev_t *dev)
+static int
+xhci_rh_port_speed(usbdev_t *const dev, const int port)
{
- int port;
- while ((port = xhci_rh_report_port_changes (dev)) != -1)
- xhci_rh_scanport (dev, port);
+ xhci_t *const xhci = XHCI_INST(dev->controller);
+ volatile u32 *const portsc = &xhci->opreg->prs[port - 1].portsc;
+
+ if (*portsc & PORTSC_PED) {
+ return ((*portsc & PORTSC_PORT_SPEED_MASK)
+ >> PORTSC_PORT_SPEED_START)
+ - 1;
+ } else {
+ return -1;
+ }
}
-void
-xhci_rh_init (usbdev_t *dev)
+static int
+xhci_rh_start_port_reset(usbdev_t *const dev, const int port)
{
- int i;
-
- dev->destroy = xhci_rh_destroy;
- dev->poll = xhci_rh_poll;
-
- dev->data = malloc (sizeof (rh_inst_t));
- if (!dev->data)
- fatal("Not enough memory for XHCI RH.\n");
+ xhci_t *const xhci = XHCI_INST(dev->controller);
+ volatile u32 *const portsc = &xhci->opreg->prs[port - 1].portsc;
- RH_INST (dev)->numports = XHCI_INST (dev->controller)->capreg->MaxPorts;
- RH_INST (dev)->port = malloc(sizeof(int) * RH_INST (dev)->numports);
- usb_debug("%d ports registered\n", RH_INST (dev)->numports);
+ *portsc = (*portsc & PORTSC_RW_MASK) | PORTSC_PR;
+ return 0;
+}
- for (i = 0; i < RH_INST (dev)->numports; i++) {
- xhci_rh_enable_port (dev, i);
- RH_INST (dev)->port[i] = -1;
- }
+static const generic_hub_ops_t xhci_rh_ops = {
+ .hub_status_changed = xhci_rh_hub_status_changed,
+ .port_status_changed = xhci_rh_port_status_changed,
+ .port_connected = xhci_rh_port_connected,
+ .port_in_reset = xhci_rh_port_in_reset,
+ .port_enabled = xhci_rh_port_enabled,
+ .port_speed = xhci_rh_port_speed,
+ .enable_port = NULL,
+ .disable_port = NULL,
+ .start_port_reset = xhci_rh_start_port_reset,
+ .reset_port = generic_hub_rh_resetport,
+};
+void
+xhci_rh_init (usbdev_t *dev)
+{
/* we can set them here because a root hub _really_ shouldn't
appear elsewhere */
dev->address = 0;
dev->hub = -1;
dev->port = -1;
- usb_debug("rh init done\n");
+ const int num_ports = /* TODO: maybe we need to read extended caps */
+ (XHCI_INST(dev->controller)->capreg->hcsparams1 >> 24) & 0xff;
+ generic_hub_init(dev, num_ports, &xhci_rh_ops);
+
+ usb_debug("xHCI: root hub init done\n");
}