[coreboot] New patch to review for coreboot: 0d93130 libpayload: Add dummy queue heads to EHCI interrupt frame list

Nico Huber (nico.huber@secunet.com) gerrit at coreboot.org
Wed Jun 20 17:36:42 CEST 2012


Nico Huber (nico.huber at secunet.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/1124

-gerrit

commit 0d93130883dd16165344d56447c29ee5913a5695
Author: Nico Huber <nico.huber at secunet.com>
Date:   Thu Jun 14 13:27:39 2012 +0200

    libpayload: Add dummy queue heads to EHCI interrupt frame list
    
    This introduces a dummy queue head in the interrupt frame list of the
    EHCI host controller. It's a workaround for broken controllers which
    follow pointers from this list even if the terminate bit is set.
    Fortunately, they do honor the bit in queue heads and having an empty
    QH in the list doesn't violate the standard.
    
    The linux kernel has a similar workaround for AMD SB700, SB800, and
    Hudson-2/3 platforms. We observed this bug with an AMD SB600.
    
    Change-Id: Ibbb66dea5fddc89c7995a24d746bedf6bfa887be
    Signed-off-by: Nico Huber <nico.huber at secunet.com>
---
 payloads/libpayload/drivers/usb/ehci.c         |   24 +++++++++++++++++++++---
 payloads/libpayload/drivers/usb/ehci_private.h |    1 +
 2 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/payloads/libpayload/drivers/usb/ehci.c b/payloads/libpayload/drivers/usb/ehci.c
index d99901a..8843876 100644
--- a/payloads/libpayload/drivers/usb/ehci.c
+++ b/payloads/libpayload/drivers/usb/ehci.c
@@ -83,6 +83,9 @@ static void ehci_shutdown (hci_t *controller)
 	/* Free periodic frame list */
 	free(phys_to_virt(EHCI_INST(controller)->operation->periodiclistbase));
 
+	/* Free dummy QH */
+	free(EHCI_INST(controller)->dummy_qh);
+
 	EHCI_INST(controller)->operation->configflag = 0;
 }
 
@@ -521,9 +524,11 @@ static void *ehci_create_intr_queue(
 	int nothing_placed = 1;
 	u32 *const ps = (u32 *)phys_to_virt(EHCI_INST(ep->dev->controller)
 						->operation->periodiclistbase);
+	const u32 dummy_ptr = virt_to_phys(EHCI_INST(
+				ep->dev->controller)->dummy_qh) | PS_TYPE_QH;
 	for (i = 0; i < 1024; i += reqtiming) {
 		/* advance to the next free position */
-		while ((i < 1024) && !(ps[i] & PS_TERMINATE)) ++i;
+		while ((i < 1024) && (ps[i] != dummy_ptr)) ++i;
 		if (i < 1024) {
 			ps[i] =	virt_to_phys(&intrq->qh) | PS_TYPE_QH;
 			nothing_placed = 0;
@@ -547,9 +552,11 @@ static void ehci_destroy_intr_queue(endpoint_t *const ep, void *const queue)
 	int i;
 	u32 *const ps = (u32 *)phys_to_virt(EHCI_INST(
 			ep->dev->controller)->operation->periodiclistbase);
+	const u32 dummy_ptr = virt_to_phys(EHCI_INST(
+				ep->dev->controller)->dummy_qh) | PS_TYPE_QH;
 	for (i = 0; i < 1024; ++i) {
 		if ((ps[i] & PS_PTR_MASK) == virt_to_phys(&intrq->qh))
-			ps[i] = PS_TERMINATE;
+			ps[i] = dummy_ptr;
 	}
 
 	/* wait 1ms for frame to end */
@@ -660,8 +667,19 @@ ehci_init (pcidev_t addr)
 	u32 *const periodic_list = (u32 *)memalign(4096, 1024 * sizeof(u32));
 	if (!periodic_list)
 		fatal("Not enough memory creating EHCI periodic frame list.\n");
+
+	/*
+	 * Insert dummy QH in periodic frame list
+	 * This helps with broken host controllers
+	 * and doesn't violate the standard.
+	 */
+	EHCI_INST(controller)->dummy_qh = (ehci_qh_t *)memalign(32, sizeof(ehci_qh_t));
+	memset(EHCI_INST(controller)->dummy_qh, 0,
+		sizeof(*EHCI_INST(controller)->dummy_qh));
+	EHCI_INST(controller)->dummy_qh->horiz_link_ptr = QH_TERMINATE;
 	for (i = 0; i < 1024; ++i)
-		periodic_list[i] = PS_TERMINATE;
+		periodic_list[i] = virt_to_phys(EHCI_INST(controller)->dummy_qh)
+				   | PS_TYPE_QH;
 
 	/* Make sure periodic schedule is disabled */
 	ehci_set_periodic_schedule(EHCI_INST(controller), 0);
diff --git a/payloads/libpayload/drivers/usb/ehci_private.h b/payloads/libpayload/drivers/usb/ehci_private.h
index a97336b..3276e23 100644
--- a/payloads/libpayload/drivers/usb/ehci_private.h
+++ b/payloads/libpayload/drivers/usb/ehci_private.h
@@ -132,6 +132,7 @@ typedef volatile struct {
 typedef struct ehci {
 	hc_cap_t *capabilities;
 	hc_op_t *operation;
+	ehci_qh_t *dummy_qh;
 } ehci_t;
 
 #define PS_TERMINATE 1




More information about the coreboot mailing list