Index: uspace/drv/bus/usb/xhci/debug.c
===================================================================
--- uspace/drv/bus/usb/xhci/debug.c	(revision e4d7363ab09538c786991a26952f1695de85df2c)
+++ uspace/drv/bus/usb/xhci/debug.c	(revision 91ca111603271f1e8653a31f4d8a6120eed9f0cc)
@@ -35,4 +35,5 @@
 
 #include <inttypes.h>
+#include <byteorder.h>
 #include <usb/debug.h>
 
@@ -60,5 +61,5 @@
  * Dumps all capability registers.
  */
-void xhci_dump_cap_regs(xhci_cap_regs_t *cap)
+void xhci_dump_cap_regs(const xhci_cap_regs_t *cap)
 {
 	usb_log_debug2("Capabilities:");
@@ -99,5 +100,5 @@
 }
 
-void xhci_dump_port(xhci_port_regs_t *port)
+void xhci_dump_port(const xhci_port_regs_t *port)
 {
 	DUMP_REG(port, XHCI_PORT_CCS);
@@ -124,5 +125,5 @@
 }
 
-void xhci_dump_state(xhci_hc_t *hc)
+void xhci_dump_state(const xhci_hc_t *hc)
 {
 	usb_log_debug2("Operational registers:");
@@ -171,5 +172,5 @@
 }
 
-void xhci_dump_ports(xhci_hc_t *hc)
+void xhci_dump_ports(const xhci_hc_t *hc)
 {
 	const size_t num_ports = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_PORTS);
@@ -230,7 +231,71 @@
 }
 
-void xhci_dump_trb(xhci_trb_t *trb)
+void xhci_dump_trb(const xhci_trb_t *trb)
 {
 	usb_log_debug2("TRB(%p): type %s, cycle %u", trb, xhci_trb_str_type(TRB_TYPE(*trb)), TRB_CYCLE(*trb));
+}
+
+static const char *ec_ids [] = {
+	[0] = "<empty>",
+#define EC(t) [XHCI_EC_##t] = #t
+	EC(USB_LEGACY),
+	EC(SUPPORTED_PROTOCOL),
+	EC(EXTENDED_POWER_MANAGEMENT),
+	EC(IOV),
+	EC(MSI),
+	EC(LOCALMEM),
+	EC(DEBUG),
+	EC(MSIX),
+#undef EC
+	[XHCI_EC_MAX] = NULL
+};
+
+const char *xhci_ec_str_id(unsigned id)
+{
+	static char buf [20];
+
+	if (id < XHCI_EC_MAX && ec_ids[id] != NULL)
+		return ec_ids[id];
+
+	snprintf(buf, sizeof(buf), "<unknown (%u)>", id);
+	return buf;
+}
+
+static void xhci_dump_psi(const xhci_psi_t *psi)
+{
+	static const char speed_exp [] = " KMG";
+	static const char *psi_types [] = { "", " rsvd", " RX", " TX" };
+	
+	usb_log_debug("Speed %u%s: %5u %cb/s, %s",
+	    XHCI_REG_RD(psi, XHCI_PSI_PSIV),
+	    psi_types[XHCI_REG_RD(psi, XHCI_PSI_PLT)],
+	    XHCI_REG_RD(psi, XHCI_PSI_PSIM),
+	    speed_exp[XHCI_REG_RD(psi, XHCI_PSI_PSIE)],
+	    XHCI_REG_RD(psi, XHCI_PSI_PFD) ? "full-duplex" : "");
+}
+
+void xhci_dump_extcap(const xhci_extcap_t *ec)
+{
+	xhci_sp_name_t name;
+	unsigned ports_from, ports_to;
+
+	unsigned id = XHCI_REG_RD(ec, XHCI_EC_CAP_ID);
+	usb_log_debug("Extended capability %s", xhci_ec_str_id(id));
+
+	switch (id) {
+		case XHCI_EC_SUPPORTED_PROTOCOL:
+			name.packed = host2uint32_t_le(XHCI_REG_RD(ec, XHCI_EC_SP_NAME));
+			ports_from = XHCI_REG_RD(ec, XHCI_EC_SP_CP_OFF);
+			ports_to = ports_from + XHCI_REG_RD(ec, XHCI_EC_SP_CP_OFF) - 1;
+			usb_log_debug("\tProtocol %4s%u.%u, ports %u-%u", name.str,
+			    XHCI_REG_RD(ec, XHCI_EC_SP_MAJOR),
+			    XHCI_REG_RD(ec, XHCI_EC_SP_MINOR),
+			    ports_from, ports_to);
+
+			unsigned psic = XHCI_REG_RD(ec, XHCI_EC_SP_PSIC);
+			for (unsigned i = 0; i < psic; i++)
+				xhci_dump_psi(xhci_extcap_psi(ec, i));
+			break;
+	}
 }
 
Index: uspace/drv/bus/usb/xhci/debug.h
===================================================================
--- uspace/drv/bus/usb/xhci/debug.h	(revision e4d7363ab09538c786991a26952f1695de85df2c)
+++ uspace/drv/bus/usb/xhci/debug.h	(revision 91ca111603271f1e8653a31f4d8a6120eed9f0cc)
@@ -37,16 +37,24 @@
 #define XHCI_DEBUG_H
 
+/**
+ * As the debug header is likely to be included in every file, avoid including
+ * all headers of xhci to support "include what you use".
+ */
 struct xhci_hc;
 struct xhci_cap_regs;
 struct xhci_port_regs;
 struct xhci_trb;
+struct xhci_extcap;
 
 void xhci_dump_cap_regs(const struct xhci_cap_regs *);
-void xhci_dump_port(struct xhci_port_regs *);
-void xhci_dump_state(struct xhci_hc *);
-void xhci_dump_ports(struct xhci_hc *);
+void xhci_dump_port(const struct xhci_port_regs *);
+void xhci_dump_state(const struct xhci_hc *);
+void xhci_dump_ports(const struct xhci_hc *);
 
 const char *xhci_trb_str_type(unsigned);
-void xhci_dump_trb(struct xhci_trb *trb);
+void xhci_dump_trb(const struct xhci_trb *trb);
+
+const char *xhci_ec_str_id(unsigned);
+void xhci_dump_extcap(const struct xhci_extcap *);
 
 #endif
Index: uspace/drv/bus/usb/xhci/hc.c
===================================================================
--- uspace/drv/bus/usb/xhci/hc.c	(revision e4d7363ab09538c786991a26952f1695de85df2c)
+++ uspace/drv/bus/usb/xhci/hc.c	(revision 91ca111603271f1e8653a31f4d8a6120eed9f0cc)
@@ -69,4 +69,77 @@
 };
 
+/**
+ * Default USB Speed ID mapping: Table 157
+ */
+#define PSI_TO_BPS(psie, psim) (((uint64_t) psim) << (10 * psie))
+#define PORT_SPEED(psie, psim) { \
+	.rx_bps = PSI_TO_BPS(psie, psim), \
+	.tx_bps = PSI_TO_BPS(psie, psim) \
+}
+static const xhci_port_speed_t ps_default_full  = PORT_SPEED(2, 12);
+static const xhci_port_speed_t ps_default_low   = PORT_SPEED(1, 1500);
+static const xhci_port_speed_t ps_default_high  = PORT_SPEED(2, 480);
+static const xhci_port_speed_t ps_default_super = PORT_SPEED(3, 5);
+
+/**
+ * Walk the list of extended capabilities.
+ */
+static int hc_parse_ec(xhci_hc_t *hc)
+{
+	unsigned psic, major;
+
+	for (xhci_extcap_t *ec = hc->xecp; ec; ec = xhci_extcap_next(ec)) {
+		xhci_dump_extcap(ec);
+		switch (XHCI_REG_RD(ec, XHCI_EC_CAP_ID)) {
+		case XHCI_EC_USB_LEGACY:
+			assert(hc->legsup == NULL);
+			hc->legsup = (xhci_legsup_t *) ec;
+			break;
+		case XHCI_EC_SUPPORTED_PROTOCOL:
+			psic = XHCI_REG_RD(ec, XHCI_EC_SP_PSIC);
+			major = XHCI_REG_RD(ec, XHCI_EC_SP_MAJOR);
+
+			// "Implied" speed
+			if (psic == 0) {
+				/*
+				 * According to section 7.2.2.1.2, only USB 2.0
+				 * and USB 3.0 can have psic == 0. So we
+				 * blindly assume the name == "USB " and minor
+				 * == 0.
+				 */
+				if (major == 2) {
+					hc->speeds[1] = ps_default_full;
+					hc->speeds[2] = ps_default_low;
+					hc->speeds[3] = ps_default_high;
+				} else if (major == 3) {
+					hc->speeds[4] = ps_default_super;
+				} else {
+					return EINVAL;
+				}
+
+				usb_log_debug2("Implied speed of USB %u set up.", major);
+			} else {
+				for (unsigned i = 0; i < psic; i++) {
+					xhci_psi_t *psi = xhci_extcap_psi(ec, i);
+					unsigned sim = XHCI_REG_RD(psi, XHCI_PSI_PSIM);
+					unsigned psiv = XHCI_REG_RD(psi, XHCI_PSI_PSIV);
+					unsigned psie = XHCI_REG_RD(psi, XHCI_PSI_PSIE);
+					unsigned psim = XHCI_REG_RD(psi, XHCI_PSI_PSIM);
+
+					uint64_t bps = PSI_TO_BPS(psie, psim);
+
+					if (sim == XHCI_PSI_PLT_SYMM || sim == XHCI_PSI_PLT_RX)
+						hc->speeds[psiv].rx_bps = bps;
+					if (sim == XHCI_PSI_PLT_SYMM || sim == XHCI_PSI_PLT_TX) {
+						hc->speeds[psiv].tx_bps = bps;
+						usb_log_debug2("Speed %u set up for bps %" PRIu64 " / %" PRIu64 ".", psiv, hc->speeds[psiv].rx_bps, hc->speeds[psiv].tx_bps);
+					}
+				}
+			}
+		}
+	}
+	return EOK;
+}
+
 int hc_init_mmio(xhci_hc_t *hc, const hw_res_list_parsed_t *hw_res)
 {
@@ -90,8 +163,13 @@
 		return err;
 
+	hc->base = base;
 	hc->cap_regs = (xhci_cap_regs_t *)  base;
 	hc->op_regs  = (xhci_op_regs_t *)  (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_LENGTH));
 	hc->rt_regs  = (xhci_rt_regs_t *)  (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_RTSOFF));
 	hc->db_arry  = (xhci_doorbell_t *) (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_DBOFF));
+
+	uintptr_t xec_offset = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_XECP) * sizeof(xhci_dword_t);
+	if (xec_offset > 0)
+		hc->xecp = (xhci_extcap_t *) (base + xec_offset);
 
 	usb_log_debug2("Initialized MMIO reg areas:");
@@ -105,4 +183,9 @@
 	hc->ac64 = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_AC64);
 	hc->max_slots = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_SLOTS);
+
+	if ((err = hc_parse_ec(hc))) {
+		pio_disable(hc->base, RNGSZ(hc->mmio_range));
+		return err;
+	}
 
 	return EOK;
@@ -151,6 +234,4 @@
 	}
 
-	addr_range_t mmio_range = hw_res->mem_ranges.ranges[0];
-
 	code->ranges = malloc(sizeof(irq_pio_range_t));
 	if (code->ranges == NULL)
@@ -165,6 +246,6 @@
 	code->rangecount = 1;
 	code->ranges[0] = (irq_pio_range_t) {
-	    .base = RNGABS(mmio_range),
-	    .size = RNGSZ(mmio_range),
+	    .base = RNGABS(hc->mmio_range),
+	    .size = RNGSZ(hc->mmio_range),
 	};
 
@@ -172,5 +253,5 @@
 	memcpy(code->cmds, irq_commands, sizeof(irq_commands));
 
-	void *intr0_iman = RNGABSPTR(mmio_range) + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_RTSOFF) + offsetof(xhci_rt_regs_t, ir[0]);
+	void *intr0_iman = RNGABSPTR(hc->mmio_range) + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_RTSOFF) + offsetof(xhci_rt_regs_t, ir[0]);
 	code->cmds[0].addr = intr0_iman;
 	code->cmds[3].addr = intr0_iman;
@@ -182,6 +263,13 @@
 int hc_claim(xhci_hc_t *hc, ddf_dev_t *dev)
 {
-	// TODO: impl
-	return EOK;
+	/* No legacy support capability, the controller is solely for us */
+	if (!hc->legsup)
+		return EOK;
+
+	/*
+	 * TODO: Implement handoff from BIOS, section 4.22.1
+	 * QEMU does not support this, so we have to test on real HW.
+	 */
+	return ENOTSUP;
 }
 
@@ -346,4 +434,5 @@
 	xhci_trb_ring_fini(&hc->command_ring);
 	xhci_event_ring_fini(&hc->event_ring);
+	pio_disable(hc->base, RNGSZ(hc->mmio_range));
 	usb_log_info("HC(%p): Finalized.", hc);
 }
Index: uspace/drv/bus/usb/xhci/hc.h
===================================================================
--- uspace/drv/bus/usb/xhci/hc.h	(revision e4d7363ab09538c786991a26952f1695de85df2c)
+++ uspace/drv/bus/usb/xhci/hc.h	(revision 91ca111603271f1e8653a31f4d8a6120eed9f0cc)
@@ -39,20 +39,33 @@
 #include "trb_ring.h"
 
+/**
+ * xHCI lets the controller define speeds of ports it controls.
+ */
+typedef struct xhci_port_speed {
+	uint64_t rx_bps, tx_bps;
+} xhci_port_speed_t;
+
 typedef struct xhci_hc {
+	/* MMIO range */
+	addr_range_t mmio_range;
+	void *base;
+
+	/* Mapped register sets */
 	xhci_cap_regs_t *cap_regs;
 	xhci_op_regs_t *op_regs;
 	xhci_rt_regs_t *rt_regs;
 	xhci_doorbell_t *db_arry;
+	xhci_extcap_t *xecp;		/**< First extended capability */
+	xhci_legsup_t *legsup;		/**< Legacy support capability */
 
-	addr_range_t mmio_range;
-
+	/* Structures in allocated memory */
 	xhci_trb_ring_t command_ring;
 	xhci_event_ring_t event_ring;
-
 	xhci_device_ctx_t *dcbaa;
 
+	/* Cached capabilities */
+	xhci_port_speed_t speeds [16];
 	unsigned max_slots;
 	bool ac64;
-
 } xhci_hc_t;
 
Index: uspace/drv/bus/usb/xhci/hw_struct/regs.h
===================================================================
--- uspace/drv/bus/usb/xhci/hw_struct/regs.h	(revision e4d7363ab09538c786991a26952f1695de85df2c)
+++ uspace/drv/bus/usb/xhci/hw_struct/regs.h	(revision 91ca111603271f1e8653a31f4d8a6120eed9f0cc)
@@ -483,4 +483,97 @@
 typedef ioport32_t xhci_doorbell_t;
 
+enum xhci_plt {
+	XHCI_PSI_PLT_SYMM,
+	XHCI_PSI_PLT_RSVD,
+	XHCI_PSI_PLT_RX,
+	XHCI_PSI_PLT_TX
+};
+
+/**
+ * Protocol speed ID: section 7.2.1
+ */
+typedef struct xhci_psi {
+        xhci_dword_t psi;
+} xhci_psi_t;
+
+#define XHCI_PSI_PSIV    psi, 32, RANGE,  3,  0
+#define XHCI_PSI_PSIE    psi, 32, RANGE,  5,  4
+#define XHCI_PSI_PLT     psi, 32, RANGE,  7,  6
+#define XHCI_PSI_PFD     psi, 32,  FLAG,  8
+#define XHCI_PSI_PSIM    psi, 32, RANGE, 31, 16
+
+enum xhci_extcap_type {
+	XHCI_EC_RESERVED = 0,
+	XHCI_EC_USB_LEGACY,
+	XHCI_EC_SUPPORTED_PROTOCOL,
+	XHCI_EC_EXTENDED_POWER_MANAGEMENT,
+	XHCI_EC_IOV,
+	XHCI_EC_MSI,
+	XHCI_EC_LOCALMEM,
+	XHCI_EC_DEBUG = 10,
+	XHCI_EC_MSIX = 17,
+	XHCI_EC_MAX = 255
+};
+
+/**
+ * xHCI Extended Capability: section 7
+ */
+typedef struct xhci_extcap {
+	xhci_dword_t header;
+	xhci_dword_t cap_specific[];
+} xhci_extcap_t;
+
+#define XHCI_EC_CAP_ID                           header, 32, RANGE,  7,  0
+#define XHCI_EC_SIZE                             header, 32, RANGE, 15,  8
+
+/* Supported protocol */
+#define XHCI_EC_SP_MINOR                         header, 32, RANGE, 23, 16
+#define XHCI_EC_SP_MAJOR                         header, 32, RANGE, 31, 24
+#define XHCI_EC_SP_NAME                 cap_specific[0], 32, FIELD
+#define XHCI_EC_SP_CP_OFF               cap_specific[1], 32, RANGE,  7,  0
+#define XHCI_EC_SP_CP_COUNT             cap_specific[1], 32, RANGE, 15,  8
+#define XHCI_EC_SP_PSIC                 cap_specific[1], 32, RANGE, 31, 28
+#define XHCI_EC_SP_SLOT_TYPE            cap_specific[2], 32, RANGE,  4,  0
+
+typedef union {
+	char str [4];
+	uint32_t packed;
+} xhci_sp_name_t;
+
+static inline xhci_extcap_t *xhci_extcap_next(const xhci_extcap_t *cur)
+{
+	unsigned dword_offset = XHCI_REG_RD(cur, XHCI_EC_SIZE);
+	if (!dword_offset)
+		return NULL;
+	return (xhci_extcap_t *) (((xhci_dword_t *) cur) + dword_offset);
+}
+
+static inline xhci_psi_t *xhci_extcap_psi(const xhci_extcap_t *ec, unsigned psid)
+{
+	assert(XHCI_REG_RD(ec, XHCI_EC_CAP_ID) == XHCI_EC_SUPPORTED_PROTOCOL);
+	assert(XHCI_REG_RD(ec, XHCI_EC_SP_PSIC) > psid);
+
+	unsigned dword_offset = 4 + psid;
+	return (xhci_psi_t *) (((xhci_dword_t *) ec) + dword_offset);
+}
+
+/**
+ * USB Legacy Support: section 7.1
+ *
+ * Legacy support have an exception from dword-access, because it needs to be
+ * byte-accessed.
+ */
+typedef struct xhci_extcap_legsup {
+	ioport8_t cap_id;
+	ioport8_t size;			/**< Next Capability Pointer */
+	ioport8_t sem_bios;
+	ioport8_t sem_os;
+
+	xhci_dword_t usblegctlsts;	/**< USB Legacy Support Control/Status - RW for BIOS, RO for OS */
+} xhci_legsup_t;
+
+#define XHCI_LEGSUP_SEM_BIOS	sem_bios, 8, FLAG, 0
+#define XHCI_LEGSUP_SEM_OS	sem_os, 8, FLAG, 0
+
 #endif
 /**
Index: uspace/lib/usb/include/usb/usb.h
===================================================================
--- uspace/lib/usb/include/usb/usb.h	(revision e4d7363ab09538c786991a26952f1695de85df2c)
+++ uspace/lib/usb/include/usb/usb.h	(revision 91ca111603271f1e8653a31f4d8a6120eed9f0cc)
@@ -80,5 +80,5 @@
 	/** USB 2.0 high speed (480Mbits/s). */
 	USB_SPEED_HIGH,
-	/** USB 3.0 super speed (480Mbits/s). */
+	/** USB 3.0 super speed (5Gbits/s). */
 	USB_SPEED_SUPER,
 	/** Psuedo-speed serving as a boundary. */
