libpayload: usb: Support MTK xHCI host controller
1. There is a mis-understanding to calculate the value of TD Size in Normal TRB. For MTK's xHCI controller it 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. 2. To minimize the scheduling effort for synchronous endpoints in xHC, the MTK architecture defines some extra SW scheduling parameters for HW. According to these parameters provided by SW, the xHC can easily decide whether a synchronous endpoint should be scheduled in a specific uFrame. The extra SW scheduling parameters are put into reserved DWs in Slot and Endpoint Context. But in coreboot synchronous transfer can be ignored, so only two fields are set to a default value 1 to support bulk and interrupt transfers, and others are set to zero. 3. For control transfer, it is better to read back doorbell register or add a memory barrier after ringing the doorbell to flush posted write. Otherwise the first command will be aborted on MTK's xHCI controller. 4. Before send commands to a port, the Port Power in PORTSC register should be set to 1 on MTK's xHCI so a hook function of enable_port in generic_hub_ops_t struct is provided. Change-Id: Ie8878b50c048907ebf939b3f6657535a54877fde Signed-off-by: Patrick Georgi <pgeorgi@chromium.org> Original-Commit-Id: 738609c11f16264c6e6429d478b2040cb391fe41 Original-Change-Id: Id9156892699e2e42a166c77fbf6690049abe953b Original-Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com> Original-Reviewed-on: https://chromium-review.googlesource.com/265362 Original-Reviewed-by: Julius Werner <jwerner@chromium.org> Original-Commit-Queue: Yidi Lin <yidi.lin@mediatek.com> Original-Tested-by: Yidi Lin <yidi.lin@mediatek.com> Reviewed-on: http://review.coreboot.org/10389 Reviewed-by: Patrick Georgi <pgeorgi@google.com> Tested-by: build bot (Jenkins)
This commit is contained in:
parent
08e3013490
commit
d42ee150a0
|
@ -495,6 +495,12 @@ config USB_XHCI
|
||||||
Select this option if you want to use USB 3.0
|
Select this option if you want to use USB 3.0
|
||||||
NOTE: This option is not (fully) implemented yet
|
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
|
config USB_DWC2
|
||||||
bool "Support for USB DesignWare HCD controllers"
|
bool "Support for USB DesignWare HCD controllers"
|
||||||
depends on USB && !USB_HID
|
depends on USB && !USB_HID
|
||||||
|
|
|
@ -370,6 +370,8 @@ xhci_reinit (hci_t *controller)
|
||||||
xhci->ev_ring_table[0].seg_base_hi = 0;
|
xhci->ev_ring_table[0].seg_base_hi = 0;
|
||||||
xhci->ev_ring_table[0].seg_size = EVENT_RING_SIZE;
|
xhci->ev_ring_table[0].seg_size = EVENT_RING_SIZE;
|
||||||
|
|
||||||
|
/* pass event ring table to hardware */
|
||||||
|
wmb();
|
||||||
/* Initialize primary interrupter */
|
/* Initialize primary interrupter */
|
||||||
xhci->hcrreg->intrrs[0].erstsz = 1;
|
xhci->hcrreg->intrrs[0].erstsz = 1;
|
||||||
xhci_update_event_dq(xhci);
|
xhci_update_event_dq(xhci);
|
||||||
|
@ -510,6 +512,7 @@ xhci_enqueue_trb(transfer_ring_t *const tr)
|
||||||
xhci_spew("Handling LINK pointer\n");
|
xhci_spew("Handling LINK pointer\n");
|
||||||
const int tc = TRB_GET(TC, tr->cur);
|
const int tc = TRB_GET(TC, tr->cur);
|
||||||
TRB_SET(CH, tr->cur, chain);
|
TRB_SET(CH, tr->cur, chain);
|
||||||
|
wmb();
|
||||||
TRB_SET(C, tr->cur, tr->pcs);
|
TRB_SET(C, tr->cur, tr->pcs);
|
||||||
tr->cur = phys_to_virt(tr->cur->ptr_low);
|
tr->cur = phys_to_virt(tr->cur->ptr_low);
|
||||||
if (tc)
|
if (tc)
|
||||||
|
@ -535,7 +538,7 @@ xhci_enqueue_td(transfer_ring_t *const tr, const int ep, const size_t mps,
|
||||||
cur_length = length;
|
cur_length = length;
|
||||||
packets = 0;
|
packets = 0;
|
||||||
length = 0;
|
length = 0;
|
||||||
} else {
|
} else if (!IS_ENABLED(CONFIG_LP_XHCI_MTK_QUIRK)) {
|
||||||
packets -= (residue + cur_length) / mps;
|
packets -= (residue + cur_length) / mps;
|
||||||
residue = (residue + cur_length) % mps;
|
residue = (residue + cur_length) % mps;
|
||||||
length -= cur_length;
|
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(TDS, trb, MIN(TRB_MAX_TD_SIZE, packets));
|
||||||
TRB_SET(CH, trb, 1);
|
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 */
|
/* Check for first, data stage TRB */
|
||||||
if (!trb_count && ep == 1) {
|
if (!trb_count && ep == 1) {
|
||||||
TRB_SET(DIR, trb, dir);
|
TRB_SET(DIR, trb, dir);
|
||||||
|
|
|
@ -47,6 +47,8 @@ xhci_post_command(xhci_t *const xhci)
|
||||||
TRB_SET(C, xhci->cr.cur, xhci->cr.pcs);
|
TRB_SET(C, xhci->cr.cur, xhci->cr.pcs);
|
||||||
++xhci->cr.cur;
|
++xhci->cr.cur;
|
||||||
|
|
||||||
|
/* pass command trb to hardware */
|
||||||
|
wmb();
|
||||||
/* Ring the doorbell */
|
/* Ring the doorbell */
|
||||||
xhci->dbreg[0] = 0;
|
xhci->dbreg[0] = 0;
|
||||||
|
|
||||||
|
|
|
@ -313,6 +313,16 @@ xhci_finish_ep_config(const endpoint_t *const ep, inputctx_t *const ic)
|
||||||
EC_SET(AVRTRB, epctx, avrtrb);
|
EC_SET(AVRTRB, epctx, avrtrb);
|
||||||
EC_SET(MXESIT, epctx, EC_GET(MPS, epctx) * EC_GET(MBS, epctx));
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,8 @@
|
||||||
|
|
||||||
//#define USB_DEBUG
|
//#define USB_DEBUG
|
||||||
#include <usb/usb.h>
|
#include <usb/usb.h>
|
||||||
|
#include <arch/barrier.h>
|
||||||
|
#include <kconfig.h>
|
||||||
|
|
||||||
//#define XHCI_DUMPS
|
//#define XHCI_DUMPS
|
||||||
#define xhci_debug(fmt, args...) usb_debug("%s: " fmt, __func__, ## args)
|
#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_FIELD f5 /* MXESIT - Max ESIT Payload */
|
||||||
#define EC_MXESIT_START 16
|
#define EC_MXESIT_START 16
|
||||||
#define EC_MXESIT_LEN 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_MASK(tok) MASK(EC_##tok##_START, EC_##tok##_LEN)
|
||||||
#define EC_GET(tok, ec) (((ec)->EC_##tok##_FIELD & EC_MASK(tok)) \
|
#define EC_GET(tok, ec) (((ec)->EC_##tok##_FIELD & EC_MASK(tok)) \
|
||||||
>> EC_##tok##_START)
|
>> EC_##tok##_START)
|
||||||
|
|
|
@ -119,6 +119,24 @@ xhci_rh_reset_port(usbdev_t *const dev, const int port)
|
||||||
return 0;
|
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 = {
|
static const generic_hub_ops_t xhci_rh_ops = {
|
||||||
.hub_status_changed = xhci_rh_hub_status_changed,
|
.hub_status_changed = xhci_rh_hub_status_changed,
|
||||||
.port_status_changed = xhci_rh_port_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_in_reset = xhci_rh_port_in_reset,
|
||||||
.port_enabled = xhci_rh_port_enabled,
|
.port_enabled = xhci_rh_port_enabled,
|
||||||
.port_speed = xhci_rh_port_speed,
|
.port_speed = xhci_rh_port_speed,
|
||||||
.enable_port = NULL,
|
.enable_port = xhci_rh_enable_port,
|
||||||
.disable_port = NULL,
|
.disable_port = NULL,
|
||||||
.start_port_reset = NULL,
|
.start_port_reset = NULL,
|
||||||
.reset_port = xhci_rh_reset_port,
|
.reset_port = xhci_rh_reset_port,
|
||||||
|
|
Loading…
Reference in New Issue