diff --git a/payloads/libpayload/Config.in b/payloads/libpayload/Config.in index 4d4876182e..92c89abeb8 100644 --- a/payloads/libpayload/Config.in +++ b/payloads/libpayload/Config.in @@ -495,6 +495,12 @@ config USB_XHCI Select this option if you want to use USB 3.0 NOTE: This option is not (fully) implemented yet +config USB_XHCI_MTK_QUIRK + bool "Support for USB xHCI controllers on MTK SoC" + depends on USB_XHCI + help + Select this option if you want to use USB 3.0 on MTK platform. + config USB_DWC2 bool "Support for USB DesignWare HCD controllers" depends on USB && !USB_HID diff --git a/payloads/libpayload/drivers/usb/xhci.c b/payloads/libpayload/drivers/usb/xhci.c index 26dcdbe4de..89930e06df 100644 --- a/payloads/libpayload/drivers/usb/xhci.c +++ b/payloads/libpayload/drivers/usb/xhci.c @@ -370,6 +370,8 @@ xhci_reinit (hci_t *controller) xhci->ev_ring_table[0].seg_base_hi = 0; xhci->ev_ring_table[0].seg_size = EVENT_RING_SIZE; + /* pass event ring table to hardware */ + wmb(); /* Initialize primary interrupter */ xhci->hcrreg->intrrs[0].erstsz = 1; xhci_update_event_dq(xhci); @@ -510,6 +512,7 @@ xhci_enqueue_trb(transfer_ring_t *const tr) xhci_spew("Handling LINK pointer\n"); const int tc = TRB_GET(TC, tr->cur); TRB_SET(CH, tr->cur, chain); + wmb(); TRB_SET(C, tr->cur, tr->pcs); tr->cur = phys_to_virt(tr->cur->ptr_low); if (tc) @@ -535,7 +538,7 @@ xhci_enqueue_td(transfer_ring_t *const tr, const int ep, const size_t mps, cur_length = length; packets = 0; length = 0; - } else { + } else if (!IS_ENABLED(CONFIG_LP_XHCI_MTK_QUIRK)) { packets -= (residue + cur_length) / mps; residue = (residue + cur_length) % mps; length -= cur_length; @@ -548,6 +551,18 @@ xhci_enqueue_td(transfer_ring_t *const tr, const int ep, const size_t mps, TRB_SET(TDS, trb, MIN(TRB_MAX_TD_SIZE, packets)); TRB_SET(CH, trb, 1); + if (length && IS_ENABLED(CONFIG_LP_XHCI_MTK_QUIRK)) { + /* + * For MTK's xHCI controller, TDS defines a number of + * packets that remain to be transferred for a TD after + * processing all Max packets in all previous TRBs, that + * means don't include the current TRB's. + */ + packets -= (residue + cur_length) / mps; + residue = (residue + cur_length) % mps; + length -= cur_length; + } + /* Check for first, data stage TRB */ if (!trb_count && ep == 1) { TRB_SET(DIR, trb, dir); diff --git a/payloads/libpayload/drivers/usb/xhci_commands.c b/payloads/libpayload/drivers/usb/xhci_commands.c index 009a69c812..845a34dee7 100644 --- a/payloads/libpayload/drivers/usb/xhci_commands.c +++ b/payloads/libpayload/drivers/usb/xhci_commands.c @@ -47,6 +47,8 @@ xhci_post_command(xhci_t *const xhci) TRB_SET(C, xhci->cr.cur, xhci->cr.pcs); ++xhci->cr.cur; + /* pass command trb to hardware */ + wmb(); /* Ring the doorbell */ xhci->dbreg[0] = 0; diff --git a/payloads/libpayload/drivers/usb/xhci_devconf.c b/payloads/libpayload/drivers/usb/xhci_devconf.c index 012f610287..5b5bb5e800 100644 --- a/payloads/libpayload/drivers/usb/xhci_devconf.c +++ b/payloads/libpayload/drivers/usb/xhci_devconf.c @@ -313,6 +313,16 @@ xhci_finish_ep_config(const endpoint_t *const ep, inputctx_t *const ic) EC_SET(AVRTRB, epctx, avrtrb); EC_SET(MXESIT, epctx, EC_GET(MPS, epctx) * EC_GET(MBS, epctx)); + if (IS_ENABLED(CONFIG_LP_USB_XHCI_MTK_QUIRK)) { + /* The MTK xHCI defines some extra SW parameters which are + * put into reserved DWs in Slot and Endpoint Contexts for + * synchronous endpoints. But for non-isochronous transfers, + * it is enough to set the following two fields to 1, and others + * are set to 0. + */ + EC_SET(BPKTS, epctx, 1); + EC_SET(BBM, epctx, 1); + } return 0; } diff --git a/payloads/libpayload/drivers/usb/xhci_private.h b/payloads/libpayload/drivers/usb/xhci_private.h index 3861858b44..f01a37f842 100644 --- a/payloads/libpayload/drivers/usb/xhci_private.h +++ b/payloads/libpayload/drivers/usb/xhci_private.h @@ -33,6 +33,8 @@ //#define USB_DEBUG #include +#include +#include //#define XHCI_DUMPS #define xhci_debug(fmt, args...) usb_debug("%s: " fmt, __func__, ## args) @@ -242,6 +244,13 @@ typedef volatile struct slotctx { #define EC_MXESIT_FIELD f5 /* MXESIT - Max ESIT Payload */ #define EC_MXESIT_START 16 #define EC_MXESIT_LEN 16 +#define EC_BPKTS_FIELD rsvd[0] /* BPKTS - packets tx in scheduled uframe */ +#define EC_BPKTS_START 0 +#define EC_BPKTS_LEN 6 +#define EC_BBM_FIELD rsvd[0] /* BBM - burst mode for scheduling */ +#define EC_BBM_START 11 +#define EC_BBM_LEN 1 + #define EC_MASK(tok) MASK(EC_##tok##_START, EC_##tok##_LEN) #define EC_GET(tok, ec) (((ec)->EC_##tok##_FIELD & EC_MASK(tok)) \ >> EC_##tok##_START) diff --git a/payloads/libpayload/drivers/usb/xhci_rh.c b/payloads/libpayload/drivers/usb/xhci_rh.c index fa118fe460..bcb01ace98 100644 --- a/payloads/libpayload/drivers/usb/xhci_rh.c +++ b/payloads/libpayload/drivers/usb/xhci_rh.c @@ -119,6 +119,24 @@ xhci_rh_reset_port(usbdev_t *const dev, const int port) return 0; } +static int +xhci_rh_enable_port(usbdev_t *const dev, int port) +{ + if (IS_ENABLED(CONFIG_LP_USB_XHCI_MTK_QUIRK)) { + xhci_t *const xhci = XHCI_INST(dev->controller); + volatile u32 *const portsc = + &xhci->opreg->prs[port - 1].portsc; + + /* + * Before sending commands to a port, the Port Power in + * PORTSC register should be enabled on MTK's xHCI. + */ + *portsc = (*portsc & PORTSC_RW_MASK) | PORTSC_PP; + } + return 0; +} + + 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, @@ -126,7 +144,7 @@ static const generic_hub_ops_t xhci_rh_ops = { .port_in_reset = xhci_rh_port_in_reset, .port_enabled = xhci_rh_port_enabled, .port_speed = xhci_rh_port_speed, - .enable_port = NULL, + .enable_port = xhci_rh_enable_port, .disable_port = NULL, .start_port_reset = NULL, .reset_port = xhci_rh_reset_port,