summaryrefslogtreecommitdiff
path: root/payloads/libpayload/drivers/usb
diff options
context:
space:
mode:
Diffstat (limited to 'payloads/libpayload/drivers/usb')
-rw-r--r--payloads/libpayload/drivers/usb/xhci.c17
-rw-r--r--payloads/libpayload/drivers/usb/xhci_commands.c2
-rw-r--r--payloads/libpayload/drivers/usb/xhci_devconf.c10
-rw-r--r--payloads/libpayload/drivers/usb/xhci_private.h9
-rw-r--r--payloads/libpayload/drivers/usb/xhci_rh.c20
5 files changed, 56 insertions, 2 deletions
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 <usb/usb.h>
+#include <arch/barrier.h>
+#include <kconfig.h>
//#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,