- reduced memory requirements a lot (from >100kb/controller to
560bytes/controller) - no need for the client of libpayload to implement usbdisk_{create,remove}, just because USB was compiled in. - usb hub support compiles, and works for some trivial cases (no device detach, trivial power management) - usb keyboard support works in qemu, though there are reports that it doesn't work on real hardware yet. - usb keyboard is integrated in both libc-getchar() and curses, if CONFIG_USB_HID is enabled Signed-off-by: Patrick Georgi <patrick.georgi@coresystems.de> Acked-by: Jordan Crouse <jordan.crouse@amd.com> git-svn-id: svn://svn.coreboot.org/coreboot/trunk@3662 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
This commit is contained in:
@@ -40,6 +40,9 @@ static int uhci_packet (usbdev_t *dev, int endp, int pid, int toggle,
|
||||
static int uhci_bulk (endpoint_t *ep, int size, u8 *data, int finalize);
|
||||
static int uhci_control (usbdev_t *dev, pid_t dir, int drlen, void *devreq,
|
||||
int dalen, u8 *data);
|
||||
static void* uhci_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming);
|
||||
static void uhci_destroy_intr_queue (endpoint_t *ep, void *queue);
|
||||
static u8* uhci_poll_intr_queue (void *queue);
|
||||
|
||||
#if 0
|
||||
/* dump uhci */
|
||||
@@ -119,7 +122,14 @@ uhci_init (pcidev_t addr)
|
||||
controller->packet = uhci_packet;
|
||||
controller->bulk = uhci_bulk;
|
||||
controller->control = uhci_control;
|
||||
UHCI_INST (controller)->roothub = &(controller->devices[0]);
|
||||
controller->create_intr_queue = uhci_create_intr_queue;
|
||||
controller->destroy_intr_queue = uhci_destroy_intr_queue;
|
||||
controller->poll_intr_queue = uhci_poll_intr_queue;
|
||||
for (i = 1; i < 128; i++) {
|
||||
controller->devices[i] = 0;
|
||||
}
|
||||
init_device_entry (controller, 0);
|
||||
UHCI_INST (controller)->roothub = controller->devices[0];
|
||||
|
||||
controller->bus_address = addr;
|
||||
controller->reg_base = pci_read_config32 (controller->bus_address, 0x20) & ~1; /* ~1 clears the register type indicator that is set to 1 for IO space */
|
||||
@@ -134,10 +144,27 @@ uhci_init (pcidev_t addr)
|
||||
memset (UHCI_INST (controller)->framelistptr, 0,
|
||||
1024 * sizeof (flistp_t));
|
||||
|
||||
/* According to the *BSD UHCI code, this one is needed on some
|
||||
PIIX chips, because otherwise they misbehave. It must be
|
||||
added to the last chain.
|
||||
|
||||
FIXME: this leaks, if the driver should ever be reinited
|
||||
for some reason. Not a problem now.
|
||||
*/
|
||||
td_t *antiberserk = memalign(16, sizeof(td_t));
|
||||
memset(antiberserk, 0, sizeof(td_t));
|
||||
|
||||
UHCI_INST (controller)->qh_prei = memalign (16, sizeof (qh_t));
|
||||
UHCI_INST (controller)->qh_intr = memalign (16, sizeof (qh_t));
|
||||
UHCI_INST (controller)->qh_data = memalign (16, sizeof (qh_t));
|
||||
UHCI_INST (controller)->qh_last = memalign (16, sizeof (qh_t));
|
||||
|
||||
UHCI_INST (controller)->qh_prei->headlinkptr.ptr =
|
||||
virt_to_phys (UHCI_INST (controller)->qh_intr);
|
||||
UHCI_INST (controller)->qh_prei->headlinkptr.queue_head = 1;
|
||||
UHCI_INST (controller)->qh_prei->elementlinkptr.ptr = 0;
|
||||
UHCI_INST (controller)->qh_prei->elementlinkptr.terminate = 1;
|
||||
|
||||
UHCI_INST (controller)->qh_intr->headlinkptr.ptr =
|
||||
virt_to_phys (UHCI_INST (controller)->qh_data);
|
||||
UHCI_INST (controller)->qh_intr->headlinkptr.queue_head = 1;
|
||||
@@ -150,23 +177,20 @@ uhci_init (pcidev_t addr)
|
||||
UHCI_INST (controller)->qh_data->elementlinkptr.ptr = 0;
|
||||
UHCI_INST (controller)->qh_data->elementlinkptr.terminate = 1;
|
||||
|
||||
UHCI_INST (controller)->qh_last->headlinkptr.ptr = 0;
|
||||
UHCI_INST (controller)->qh_last->headlinkptr.ptr = virt_to_phys (UHCI_INST (controller)->qh_data);
|
||||
UHCI_INST (controller)->qh_last->headlinkptr.terminate = 1;
|
||||
UHCI_INST (controller)->qh_last->elementlinkptr.ptr = 0;
|
||||
UHCI_INST (controller)->qh_last->elementlinkptr.ptr = virt_to_phys (antiberserk);
|
||||
UHCI_INST (controller)->qh_last->elementlinkptr.terminate = 1;
|
||||
|
||||
for (i = 0; i < 1024; i++) {
|
||||
UHCI_INST (controller)->framelistptr[i].ptr =
|
||||
virt_to_phys (UHCI_INST (controller)->qh_intr);
|
||||
virt_to_phys (UHCI_INST (controller)->qh_prei);
|
||||
UHCI_INST (controller)->framelistptr[i].terminate = 0;
|
||||
UHCI_INST (controller)->framelistptr[i].queue_head = 1;
|
||||
}
|
||||
for (i = 1; i < 128; i++) {
|
||||
init_device_entry (controller, i);
|
||||
}
|
||||
controller->devices[0].controller = controller;
|
||||
controller->devices[0].init = uhci_rh_init;
|
||||
controller->devices[0].init (&controller->devices[0]);
|
||||
controller->devices[0]->controller = controller;
|
||||
controller->devices[0]->init = uhci_rh_init;
|
||||
controller->devices[0]->init (controller->devices[0]);
|
||||
uhci_reset (controller);
|
||||
return controller;
|
||||
}
|
||||
@@ -181,6 +205,7 @@ uhci_shutdown (hci_t *controller)
|
||||
roothub);
|
||||
uhci_reg_mask16 (controller, USBCMD, 0, 0); // stop work
|
||||
free (UHCI_INST (controller)->framelistptr);
|
||||
free (UHCI_INST (controller)->qh_prei);
|
||||
free (UHCI_INST (controller)->qh_intr);
|
||||
free (UHCI_INST (controller)->qh_data);
|
||||
free (UHCI_INST (controller)->qh_last);
|
||||
@@ -205,12 +230,12 @@ uhci_stop (hci_t *controller)
|
||||
static td_t *
|
||||
wait_for_completed_qh (hci_t *controller, qh_t *qh)
|
||||
{
|
||||
int timeout = 1000; /* max 30 ms. */
|
||||
int timeout = 1000000; /* max 30 ms. */
|
||||
void *current = GET_TD (qh->elementlinkptr.ptr);
|
||||
while ((qh->elementlinkptr.terminate == 0) && (timeout-- > 0)) {
|
||||
if (current != GET_TD (qh->elementlinkptr.ptr)) {
|
||||
current = GET_TD (qh->elementlinkptr.ptr);
|
||||
timeout = 1000;
|
||||
timeout = 1000000;
|
||||
}
|
||||
uhci_reg_mask16 (controller, USBSTS, ~0, 0); // clear resettable registers
|
||||
udelay (30);
|
||||
@@ -449,6 +474,130 @@ uhci_bulk (endpoint_t *ep, int size, u8 *data, int finalize)
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
qh_t *qh;
|
||||
td_t *tds;
|
||||
td_t *last_td;
|
||||
u8 *data;
|
||||
int lastread;
|
||||
int total;
|
||||
int reqsize;
|
||||
} intr_q;
|
||||
|
||||
/* create and hook-up an intr queue into device schedule */
|
||||
static void*
|
||||
uhci_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming)
|
||||
{
|
||||
u8 *data = malloc(reqsize*reqcount);
|
||||
td_t *tds = memalign(16, sizeof(td_t) * reqcount);
|
||||
qh_t *qh = memalign(16, sizeof(qh_t));
|
||||
|
||||
qh->elementlinkptr.ptr = virt_to_phys(tds);
|
||||
qh->elementlinkptr.terminate = 0;
|
||||
|
||||
intr_q *q = malloc(sizeof(intr_q));
|
||||
q->qh = qh;
|
||||
q->tds = tds;
|
||||
q->data = data;
|
||||
q->lastread = 0;
|
||||
q->total = reqcount;
|
||||
q->reqsize = reqsize;
|
||||
q->last_td = &tds[reqcount - 1];
|
||||
|
||||
memset (tds, 0, sizeof (td_t) * reqcount);
|
||||
int i;
|
||||
for (i = 0; i < reqcount; i++) {
|
||||
tds[i].ptr = virt_to_phys (&tds[i + 1]);
|
||||
tds[i].terminate = 0;
|
||||
tds[i].queue_head = 0;
|
||||
tds[i].depth_first = 0;
|
||||
|
||||
tds[i].pid = ep->direction;
|
||||
tds[i].dev_addr = ep->dev->address;
|
||||
tds[i].endp = ep->endpoint & 0xf;
|
||||
tds[i].maxlen = maxlen (reqsize);
|
||||
tds[i].counter = 0;
|
||||
tds[i].data_toggle = ep->toggle & 1;
|
||||
tds[i].lowspeed = ep->dev->lowspeed;
|
||||
tds[i].bufptr = virt_to_phys (data);
|
||||
tds[i].status_active = 1;
|
||||
ep->toggle ^= 1;
|
||||
data += reqsize;
|
||||
}
|
||||
tds[reqcount - 1].ptr = 0;
|
||||
tds[reqcount - 1].terminate = 1;
|
||||
tds[reqcount - 1].queue_head = 0;
|
||||
tds[reqcount - 1].depth_first = 0;
|
||||
for (i = reqtiming; i < 1024; i += reqtiming) {
|
||||
/* FIXME: wrap in another qh, one for each occurance of the qh in the framelist */
|
||||
qh->headlinkptr.ptr = UHCI_INST (ep->dev->controller)->framelistptr[i].ptr;
|
||||
qh->headlinkptr.terminate = 0;
|
||||
UHCI_INST (ep->dev->controller)->framelistptr[i].ptr = virt_to_phys(qh);
|
||||
UHCI_INST (ep->dev->controller)->framelistptr[i].terminate = 0;
|
||||
UHCI_INST (ep->dev->controller)->framelistptr[i].queue_head = 1;
|
||||
}
|
||||
return q;
|
||||
}
|
||||
|
||||
/* remove queue from device schedule, dropping all data that came in */
|
||||
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);
|
||||
int i;
|
||||
for (i=0; i<1024; i++) {
|
||||
u32 oldptr = 0;
|
||||
u32 ptr = UHCI_INST (ep->dev->controller)->framelistptr[i].ptr;
|
||||
while (ptr != end) {
|
||||
if (((qh_t*)phys_to_virt(ptr))->elementlinkptr.ptr == val) {
|
||||
((qh_t*)phys_to_virt(oldptr))->headlinkptr.ptr = ((qh_t*)phys_to_virt(ptr))->headlinkptr.ptr;
|
||||
free(phys_to_virt(ptr));
|
||||
break;
|
||||
}
|
||||
oldptr = ptr;
|
||||
ptr = ((qh_t*)phys_to_virt(ptr))->headlinkptr.ptr;
|
||||
}
|
||||
}
|
||||
free(q->data);
|
||||
free(q->tds);
|
||||
free(q->qh);
|
||||
free(q);
|
||||
}
|
||||
|
||||
/* read one intr-packet from queue, if available. extend the queue for new input.
|
||||
return NULL if nothing new available.
|
||||
Recommended use: while (data=poll_intr_queue(q)) process(data);
|
||||
*/
|
||||
static u8*
|
||||
uhci_poll_intr_queue (void *q_)
|
||||
{
|
||||
intr_q *q = (intr_q*)q_;
|
||||
if (q->tds[q->lastread].status_active == 0) {
|
||||
/* FIXME: handle errors */
|
||||
int current = q->lastread;
|
||||
int previous;
|
||||
if (q->lastread == 0) {
|
||||
previous = q->total - 1;
|
||||
} else {
|
||||
previous = q->lastread - 1;
|
||||
}
|
||||
q->tds[previous].status = 0;
|
||||
q->tds[previous].ptr = 0;
|
||||
q->tds[previous].terminate = 1;
|
||||
if (q->last_td != &q->tds[previous]) {
|
||||
q->last_td->ptr = virt_to_phys(&q->tds[previous]);
|
||||
q->last_td->terminate = 0;
|
||||
q->last_td = &q->tds[previous];
|
||||
}
|
||||
q->tds[previous].status_active = 1;
|
||||
q->lastread = (q->lastread + 1) % q->total;
|
||||
return &q->data[current*q->reqsize];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
uhci_reg_write32 (hci_t *ctrl, usbreg reg, u32 value)
|
||||
{
|
||||
|
Reference in New Issue
Block a user