Patrick Georgi has submitted this change. ( https://review.coreboot.org/c/coreboot/+/39838 )
Change subject: lp/drivers/usb: Work around QEMU XHCI register issue ......................................................................
lp/drivers/usb: Work around QEMU XHCI register issue
The QEMU XHCI controller does not support byte/word reads from the capability register and it expects dword reads only.
In order to make this work move the access of the capability register fields to use macros instead of a packed struct bitfield.
This issue was filed upstream: https://bugs.launchpad.net/qemu/+bug/1693050
The original fix attempt in 2012 was not effective: https://github.com/qemu/qemu/commit/6ee021d41078844df60a3a466e3829a3e82776f3
With this change the controller is detected properly by the libpayload USB drivers.
Change-Id: I048ed14921a4c9c0620c10b315b42476b6e5c512 Signed-off-by: Duncan Laurie dlaurie@google.com Reviewed-on: https://review.coreboot.org/c/coreboot/+/39838 Tested-by: build bot (Jenkins) no-reply@coreboot.org Reviewed-by: Nico Huber nico.h@gmx.de Reviewed-by: Angel Pons th3fanbus@gmail.com --- M payloads/libpayload/drivers/usb/xhci.c M payloads/libpayload/drivers/usb/xhci_private.h M payloads/libpayload/drivers/usb/xhci_rh.c 3 files changed, 81 insertions(+), 78 deletions(-)
Approvals: build bot (Jenkins): Verified Nico Huber: Looks good to me, approved Angel Pons: Looks good to me, approved
diff --git a/payloads/libpayload/drivers/usb/xhci.c b/payloads/libpayload/drivers/usb/xhci.c index 0a69c51..2f61f86 100644 --- a/payloads/libpayload/drivers/usb/xhci.c +++ b/payloads/libpayload/drivers/usb/xhci.c @@ -185,26 +185,27 @@ goto _free_xhci; }
- xhci->capreg = phys_to_virt(physical_bar); - xhci->opreg = ((void *)xhci->capreg) + xhci->capreg->caplength; - xhci->hcrreg = ((void *)xhci->capreg) + xhci->capreg->rtsoff; - xhci->dbreg = ((void *)xhci->capreg) + xhci->capreg->dboff; + memcpy(&xhci->capreg, phys_to_virt(physical_bar), sizeof(xhci->capreg)); + xhci->opreg = phys_to_virt(physical_bar) + CAP_GET(CAPLEN, xhci->capreg); + xhci->hcrreg = phys_to_virt(physical_bar) + xhci->capreg.rtsoff; + xhci->dbreg = phys_to_virt(physical_bar) + xhci->capreg.dboff; + xhci_debug("regbase: 0x%"PRIx32"\n", physical_bar); - xhci_debug("caplen: 0x%"PRIx32"\n", xhci->capreg->caplength); - xhci_debug("rtsoff: 0x%"PRIx32"\n", xhci->capreg->rtsoff); - xhci_debug("dboff: 0x%"PRIx32"\n", xhci->capreg->dboff); + xhci_debug("caplen: 0x%"PRIx32"\n", CAP_GET(CAPLEN, xhci->capreg)); + xhci_debug("rtsoff: 0x%"PRIx32"\n", xhci->capreg.rtsoff); + xhci_debug("dboff: 0x%"PRIx32"\n", xhci->capreg.dboff);
xhci_debug("hciversion: %"PRIx8".%"PRIx8"\n", - xhci->capreg->hciver_hi, xhci->capreg->hciver_lo); - if ((xhci->capreg->hciversion < 0x96) || - (xhci->capreg->hciversion > 0x110)) { + CAP_GET(CAPVER_HI, xhci->capreg), CAP_GET(CAPVER_LO, xhci->capreg)); + if ((CAP_GET(CAPVER, xhci->capreg) < 0x96) || + (CAP_GET(CAPVER, xhci->capreg) > 0x110)) { xhci_debug("Unsupported xHCI version\n"); goto _free_xhci; }
xhci_debug("context size: %dB\n", CTXSIZE(xhci)); - xhci_debug("maxslots: 0x%02lx\n", xhci->capreg->MaxSlots); - xhci_debug("maxports: 0x%02lx\n", xhci->capreg->MaxPorts); + xhci_debug("maxslots: 0x%02lx\n", CAP_GET(MAXSLOTS, xhci->capreg)); + xhci_debug("maxports: 0x%02lx\n", CAP_GET(MAXPORTS, xhci->capreg)); const unsigned pagesize = xhci->opreg->pagesize << 12; xhci_debug("pagesize: 0x%04x\n", pagesize);
@@ -213,7 +214,8 @@ * structures at first and can still chicken out easily if we run out * of memory. */ - xhci->max_slots_en = xhci->capreg->MaxSlots & CONFIG_LP_MASK_MaxSlotsEn; + xhci->max_slots_en = CAP_GET(MAXSLOTS, xhci->capreg) & + CONFIG_LP_MASK_MaxSlotsEn; xhci->dcbaa = xhci_align(64, (xhci->max_slots_en + 1) * sizeof(u64)); xhci->dev = malloc((xhci->max_slots_en + 1) * sizeof(*xhci->dev)); if (!xhci->dcbaa || !xhci->dev) { @@ -227,8 +229,9 @@ * Let dcbaa[0] point to another array of pointers, sp_ptrs. * The pointers therein point to scratchpad buffers (pages). */ - const size_t max_sp_bufs = xhci->capreg->Max_Scratchpad_Bufs_Hi << 5 | - xhci->capreg->Max_Scratchpad_Bufs_Lo; + const size_t max_sp_bufs = + CAP_GET(MAX_SCRATCH_BUFS_HI, xhci->capreg) << 5 | + CAP_GET(MAX_SCRATCH_BUFS_LO, xhci->capreg); xhci_debug("max scratchpad bufs: 0x%zx\n", max_sp_bufs); if (max_sp_bufs) { const size_t sp_ptrs_size = max_sp_bufs * sizeof(u64); @@ -376,7 +379,8 @@ xhci_debug("event ring @%p (0x%08x)\n", xhci->er.ring, virt_to_phys(xhci->er.ring)); xhci_debug("ERST Max: 0x%lx -> 0x%lx entries\n", - xhci->capreg->ERST_Max, 1 << xhci->capreg->ERST_Max); + CAP_GET(ERST_MAX, xhci->capreg), + 1 << CAP_GET(ERST_MAX, xhci->capreg)); memset((void*)xhci->ev_ring_table, 0x00, sizeof(erst_entry_t)); xhci->ev_ring_table[0].seg_base_lo = virt_to_phys(xhci->er.ring); xhci->ev_ring_table[0].seg_base_hi = 0; @@ -432,8 +436,9 @@ #endif
if (xhci->sp_ptrs) { - size_t max_sp_bufs = xhci->capreg->Max_Scratchpad_Bufs_Hi << 5 | - xhci->capreg->Max_Scratchpad_Bufs_Lo; + const size_t max_sp_bufs = + CAP_GET(MAX_SCRATCH_BUFS_HI, xhci->capreg) << 5 | + CAP_GET(MAX_SCRATCH_BUFS_LO, xhci->capreg); for (i = 0; i < max_sp_bufs; ++i) { if (xhci->sp_ptrs[i]) free(phys_to_virt(xhci->sp_ptrs[i])); diff --git a/payloads/libpayload/drivers/usb/xhci_private.h b/payloads/libpayload/drivers/usb/xhci_private.h index ab1dfa9..65c3fdd 100644 --- a/payloads/libpayload/drivers/usb/xhci_private.h +++ b/payloads/libpayload/drivers/usb/xhci_private.h @@ -274,7 +274,6 @@ } epctx_t;
#define NUM_EPS 32 -#define CTXSIZE(xhci) ((xhci)->capreg->csz ? 64 : 32)
typedef union devctx { /* set of pointers, so we can dynamically adjust Slot/EP context size */ @@ -321,66 +320,65 @@ u32 rsvd; } erst_entry_t;
+#define CAP_CAPLEN_FIELD hciparams +#define CAP_CAPLEN_START 0 +#define CAP_CAPLEN_LEN 8 +#define CAP_CAPVER_FIELD hciparams +#define CAP_CAPVER_START 16 +#define CAP_CAPVER_LEN 16 +#define CAP_CAPVER_HI_FIELD hciparams +#define CAP_CAPVER_HI_START 24 +#define CAP_CAPVER_HI_LEN 8 +#define CAP_CAPVER_LO_FIELD hciparams +#define CAP_CAPVER_LO_START 16 +#define CAP_CAPVER_LO_LEN 8 +#define CAP_MAXSLOTS_FIELD hcsparams1 +#define CAP_MAXSLOTS_START 0 +#define CAP_MAXSLOTS_LEN 7 +#define CAP_MAXINTRS_FIELD hcsparams1 +#define CAP_MAXINTRS_START 7 +#define CAP_MAXINTRS_LEN 11 +#define CAP_MAXPORTS_FIELD hcsparams1 +#define CAP_MAXPORTS_START 24 +#define CAP_MAXPORTS_LEN 8 +#define CAP_IST_FIELD hcsparams2 +#define CAP_IST_START 0 +#define CAP_IST_LEN 4 +#define CAP_ERST_MAX_FIELD hcsparams2 +#define CAP_ERST_MAX_START 4 +#define CAP_ERST_MAX_LEN 4 +#define CAP_MAX_SCRATCH_BUFS_HI_FIELD hcsparams2 +#define CAP_MAX_SCRATCH_BUFS_HI_START 21 +#define CAP_MAX_SCRATCH_BUFS_HI_LEN 5 +#define CAP_MAX_SCRATCH_BUFS_LO_FIELD hcsparams2 +#define CAP_MAX_SCRATCH_BUFS_LO_START 27 +#define CAP_MAX_SCRATCH_BUFS_LO_LEN 5 +#define CAP_U1_LATENCY_FIELD hcsparams3 +#define CAP_U1_LATENCY_START 0 +#define CAP_U1_LATENCY_LEN 8 +#define CAP_U2_LATENCY_FIELD hcsparams3 +#define CAP_U2_LATENCY_START 16 +#define CAP_U2_LATENCY_LEN 16 +#define CAP_CSZ_FIELD hccparams +#define CAP_CSZ_START 2 +#define CAP_CSZ_LEN 1 + +#define CAP_MASK(tok) MASK(CAP_##tok##_START, CAP_##tok##_LEN) +#define CAP_GET(tok, cap) (((cap).CAP_##tok##_FIELD & CAP_MASK(tok)) \ + >> CAP_##tok##_START) + +#define CTXSIZE(xhci) (CAP_GET(CSZ, (xhci)->capreg) ? 64 : 32) + typedef struct xhci { - /* capreg is read-only, so no need for volatile, - and thus 32bit accesses can be assumed. */ struct capreg { - u8 caplength; /* 0x00 */ - u8 res1; /* 0x01 */ - union { /* 0x02 */ - u16 hciversion; - struct { - u8 hciver_lo; - u8 hciver_hi; - } __packed; - } __packed; - union { /* 0x04 */ - u32 hcsparams1; - struct { - unsigned long MaxSlots:7; - unsigned long MaxIntrs:11; - unsigned long:6; - unsigned long MaxPorts:8; - } __packed; - } __packed; - union { /* 0x08 */ - u32 hcsparams2; - struct { - unsigned long IST:4; - unsigned long ERST_Max:4; - unsigned long:13; - unsigned long Max_Scratchpad_Bufs_Hi:5; - unsigned long SPR:1; - unsigned long Max_Scratchpad_Bufs_Lo:5; - } __packed; - } __packed; - union { /* 0x0C */ - u32 hcsparams3; - struct { - unsigned long u1latency:8; - unsigned long:8; - unsigned long u2latency:16; - } __packed; - } __packed; - union { /* 0x10 */ - u32 hccparams; - struct { - unsigned long ac64:1; - unsigned long bnc:1; - unsigned long csz:1; - unsigned long ppc:1; - unsigned long pind:1; - unsigned long lhrc:1; - unsigned long ltc:1; - unsigned long nss:1; - unsigned long:4; - unsigned long MaxPSASize:4; - unsigned long xECP:16; - } __packed; - } __packed; - u32 dboff; /* 0x14 */ - u32 rtsoff; /* 0x18 */ - } __packed *capreg; + u32 hciparams; + u32 hcsparams1; + u32 hcsparams2; + u32 hcsparams3; + u32 hccparams; + u32 dboff; + u32 rtsoff; + } __packed capreg;
/* opreg is R/W is most places, so volatile access is necessary. volatile means that the compiler seeks byte writes if possible, diff --git a/payloads/libpayload/drivers/usb/xhci_rh.c b/payloads/libpayload/drivers/usb/xhci_rh.c index 453fa5b..865b9ac 100644 --- a/payloads/libpayload/drivers/usb/xhci_rh.c +++ b/payloads/libpayload/drivers/usb/xhci_rh.c @@ -160,7 +160,7 @@ dev->port = -1;
const int num_ports = /* TODO: maybe we need to read extended caps */ - (XHCI_INST(dev->controller)->capreg->hcsparams1 >> 24) & 0xff; + CAP_GET(MAXPORTS, XHCI_INST(dev->controller)->capreg); generic_hub_init(dev, num_ports, &xhci_rh_ops);
usb_debug("xHCI: root hub init done\n");