summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/device/Kconfig5
-rw-r--r--src/device/Makefile.inc2
-rw-r--r--src/device/xhci.c86
-rw-r--r--src/include/device/xhci.h57
4 files changed, 150 insertions, 0 deletions
diff --git a/src/device/Kconfig b/src/device/Kconfig
index 79ce77d66e..f7f4b90c5e 100644
--- a/src/device/Kconfig
+++ b/src/device/Kconfig
@@ -811,4 +811,9 @@ config RESOURCE_ALLOCATOR_V4
ranges for allocating resources. This allows allocation of resources
above 4G boundary as well.
+config XHCI_UTILS
+ def_bool n
+ help
+ Provides xHCI utility functions.
+
endmenu
diff --git a/src/device/Makefile.inc b/src/device/Makefile.inc
index 2e62d4284d..2fae44ab24 100644
--- a/src/device/Makefile.inc
+++ b/src/device/Makefile.inc
@@ -62,3 +62,5 @@ ramstage-y += mmio.c
ramstage-y += resource_allocator_common.c
ramstage-$(CONFIG_RESOURCE_ALLOCATOR_V3) += resource_allocator_v3.c
ramstage-$(CONFIG_RESOURCE_ALLOCATOR_V4) += resource_allocator_v4.c
+
+ramstage-$(CONFIG_XHCI_UTILS) += xhci.c
diff --git a/src/device/xhci.c b/src/device/xhci.c
new file mode 100644
index 0000000000..ce1c1b2124
--- /dev/null
+++ b/src/device/xhci.c
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <device/xhci.h>
+#include <console/console.h>
+#include <device/pci_def.h>
+#include <arch/mmio.h>
+
+union xhci_ext_caps_header {
+ uint32_t val;
+ struct {
+ uint32_t cap_id : 8;
+ uint32_t next_ptr : 8;
+ uint32_t reserved : 16;
+ };
+};
+
+enum cb_err xhci_for_each_ext_cap(const struct device *device, void *context,
+ void (*callback)(void *context,
+ const struct xhci_ext_cap *cap))
+{
+ struct resource *res;
+ uint32_t *ext_cap_ptr;
+ uint32_t ext_caps_word_offset;
+ union xhci_ext_caps_header header;
+ struct xhci_ext_cap cap;
+
+ if (!device || !callback)
+ return CB_ERR_ARG;
+
+ res = probe_resource(device, PCI_BASE_ADDRESS_0);
+ if (!res) {
+ printk(BIOS_ERR, "%s: Unable to find BAR resource for %s\n", __func__,
+ dev_path(device));
+ return CB_ERR;
+ }
+
+ if (!(res->flags & IORESOURCE_ASSIGNED)) {
+ printk(BIOS_ERR, "%s: BAR is is not assigned\n", __func__);
+ return CB_ERR;
+ }
+
+ if (res->limit > 0xFFFFFFFF) {
+ printk(BIOS_ERR, "%s: 64-bit BAR is not supported\n", __func__);
+ return CB_ERR;
+ }
+
+ ext_caps_word_offset = read16(res2mmio(res, XHCI_HCCPARAMS1_XECP, 0));
+
+ if (!ext_caps_word_offset) {
+ printk(BIOS_ERR, "%s: No extended capabilities defined\n", __func__);
+ return CB_ERR;
+ }
+
+ ext_cap_ptr = res2mmio(res, ext_caps_word_offset << 2, 0);
+
+ while ((uintptr_t)ext_cap_ptr < (uintptr_t)res->limit) {
+ header.val = read32(ext_cap_ptr);
+
+ cap.cap_id = header.cap_id;
+
+ if (header.cap_id == XHCI_ECP_CAP_ID_SUPP) {
+ cap.supported_protocol.reg0 = header.val;
+ cap.supported_protocol.reg1 = read32(ext_cap_ptr + 1);
+ cap.supported_protocol.reg2 = read32(ext_cap_ptr + 2);
+ }
+
+ callback(context, &cap);
+
+ if (!header.next_ptr)
+ break;
+
+ ext_cap_ptr += header.next_ptr;
+ }
+
+ return CB_SUCCESS;
+}
+
+void xhci_print_supported_protocol(const struct xhci_supported_protocol *supported_protocol)
+{
+ printk(BIOS_DEBUG, "xHCI Supported Protocol:\n");
+ printk(BIOS_DEBUG, " Major: %#x, Minor: %#x, Protocol: '%.*s'\n",
+ supported_protocol->major_rev, supported_protocol->minor_rev,
+ (int)sizeof(supported_protocol->name), supported_protocol->name);
+ printk(BIOS_DEBUG, " Port Offset: %d, Port Count: %d\n",
+ supported_protocol->port_offset, supported_protocol->port_count);
+}
diff --git a/src/include/device/xhci.h b/src/include/device/xhci.h
new file mode 100644
index 0000000000..25f950be2e
--- /dev/null
+++ b/src/include/device/xhci.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef __DEVICE_XHCI_H__
+#define __DEVICE_XHCI_H__
+
+#include <stdint.h>
+#include <device/device.h>
+#include <commonlib/bsd/cb_err.h>
+
+#define XHCI_HCCPARAMS1_XECP 0x12
+
+#define XHCI_ECP_CAP_ID_LEGACY 1
+#define XHCI_ECP_CAP_ID_SUPP 2
+
+struct xhci_supported_protocol {
+ union {
+ uint32_t reg0;
+ struct {
+ uint32_t cap_id : 8;
+ uint32_t next_ptr : 8;
+ uint32_t minor_rev : 8;
+ uint32_t major_rev : 8;
+ };
+ };
+ union {
+ uint32_t reg1;
+ char name[4];
+ };
+ union {
+ uint32_t reg2;
+ struct {
+ uint32_t port_offset : 8;
+ uint32_t port_count : 8;
+ uint32_t reserved : 12;
+ uint32_t protocol_speed_id_count : 4;
+ };
+ };
+};
+
+struct xhci_ext_cap {
+ uint32_t cap_id;
+ /* cap_id is used to select the correct struct in the union. */
+ union {
+ struct xhci_supported_protocol supported_protocol;
+ };
+};
+
+/**
+ * Iterates over the xHCI Extended Capabilities List.
+ */
+enum cb_err xhci_for_each_ext_cap(const struct device *device, void *context,
+ void (*callback)(void *context,
+ const struct xhci_ext_cap *cap));
+
+void xhci_print_supported_protocol(const struct xhci_supported_protocol *supported_protocol);
+
+#endif /* __DEVICE_XHCI_H__ */