summaryrefslogtreecommitdiff
path: root/payloads/libpayload/drivers/usb
diff options
context:
space:
mode:
authorNico Huber <nico.huber@secunet.com>2012-11-20 17:27:46 +0100
committerPatrick Georgi <patrick@georgi-clan.de>2012-11-24 08:43:21 +0100
commitce407e470d36bd446e7e1f02884c97a98423d5c4 (patch)
tree989ff1e649743b60b08224c7c88704bf9097dada /payloads/libpayload/drivers/usb
parentcd587f110bec55ff24a73a775a8c757332715f3f (diff)
libpayload: Implement correct interrupt-queue linking for UHCI
The linking of interrupt queues into UHCI controller's framelist (in uhci_create_intr_queue()) was incomplete. The implementation of uhci_destroy_intr_queue() was even worse, looking like it wanted to clean up more than uhci_create_intr_queue() did. This patch follows the simple approach that we used for OHCI and EHCI: Each slot in the framelist holds only one interrupt queue. Therefore, we have to look for free slots each time we want to link an interrupt queue into the framelist. In return, we have a much simpler structured framelist. With this, USB devices using interrupt transfers (e.g. keyboards) can be detached cleanly from UHCI controllers. Also, more than one of such devices can be attached without further risk. Change-Id: I07b81a3b6f2cb3ff69515c973b3ae6321ad969aa Signed-off-by: Nico Huber <nico.huber@secunet.com> Reviewed-on: http://review.coreboot.org/1897 Tested-by: build bot (Jenkins) Reviewed-by: Patrick Georgi <patrick@georgi-clan.de>
Diffstat (limited to 'payloads/libpayload/drivers/usb')
-rw-r--r--payloads/libpayload/drivers/usb/uhci.c49
1 files changed, 30 insertions, 19 deletions
diff --git a/payloads/libpayload/drivers/usb/uhci.c b/payloads/libpayload/drivers/usb/uhci.c
index 2eea375bcc..bfa53f6049 100644
--- a/payloads/libpayload/drivers/usb/uhci.c
+++ b/payloads/libpayload/drivers/usb/uhci.c
@@ -517,11 +517,27 @@ uhci_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming
data += reqsize;
}
tds[reqcount - 1].ptr = 0 | TD_TERMINATE;
- for (i = reqtiming; i < 1024; i += reqtiming) {
- /* FIXME: wrap in another qh, one for each occurance of the qh in the framelist */
- qh->headlinkptr = UHCI_INST (ep->dev->controller)->framelistptr[i] & ~FLISTP_TERMINATE;
- UHCI_INST (ep->dev->controller)->framelistptr[i] = virt_to_phys(qh) | FLISTP_QH;
+
+ /* insert QH into framelist */
+ uhci_t *const uhcic = UHCI_INST(ep->dev->controller);
+ const u32 def_ptr = virt_to_phys(uhcic->qh_prei) | FLISTP_QH;
+ int nothing_placed = 1;
+ qh->headlinkptr = def_ptr;
+ for (i = 0; i < 1024; i += reqtiming) {
+ /* advance to the next free position */
+ while ((i < 1024) && (uhcic->framelistptr[i] != def_ptr)) ++i;
+ if (i < 1024) {
+ uhcic->framelistptr[i] = virt_to_phys(qh) | FLISTP_QH;
+ nothing_placed = 0;
+ }
}
+ if (nothing_placed) {
+ printf("Error: Failed to place UHCI interrupt queue "
+ "head into framelist: no space left\n");
+ uhci_destroy_intr_queue(ep, q);
+ return NULL;
+ }
+
return q;
}
@@ -529,23 +545,18 @@ uhci_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming
static void
uhci_destroy_intr_queue (endpoint_t *ep, void *q_)
{
- intr_q *q = (intr_q*)q_;
- u32 val = virt_to_phys (q->qh);
- u32 end = virt_to_phys (UHCI_INST (ep->dev->controller)->qh_intr);
+ intr_q *const q = (intr_q*)q_;
+
+ /* remove QH from framelist */
+ uhci_t *const uhcic = UHCI_INST(ep->dev->controller);
+ const u32 qh_ptr = virt_to_phys(q->qh) | FLISTP_QH;
+ const u32 def_ptr = virt_to_phys(uhcic->qh_prei) | FLISTP_QH;
int i;
- for (i=0; i<1024; i++) {
- u32 oldptr = 0;
- u32 ptr = UHCI_INST (ep->dev->controller)->framelistptr[i];
- while (ptr != end) {
- if (((qh_t*)phys_to_virt(ptr))->elementlinkptr == val) {
- ((qh_t*)phys_to_virt(oldptr))->headlinkptr = ((qh_t*)phys_to_virt(ptr))->headlinkptr;
- free(phys_to_virt(ptr));
- break;
- }
- oldptr = ptr;
- ptr = ((qh_t*)phys_to_virt(ptr))->headlinkptr;
- }
+ for (i = 0; i < 1024; ++i) {
+ if (uhcic->framelistptr[i] == qh_ptr)
+ uhcic->framelistptr[i] = def_ptr;
}
+
free(q->data);
free(q->tds);
free(q->qh);