2008-09-02 18:06:22 +02:00
|
|
|
/*
|
|
|
|
* This file is part of the libpayload project.
|
|
|
|
*
|
2010-03-25 23:17:36 +01:00
|
|
|
* Copyright (C) 2008-2010 coresystems GmbH
|
2008-09-02 18:06:22 +02:00
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
|
|
* derived from this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
2010-03-25 23:17:36 +01:00
|
|
|
//#define USB_DEBUG
|
|
|
|
|
2010-06-07 15:58:17 +02:00
|
|
|
#include <arch/virtual.h>
|
2008-09-11 19:29:00 +02:00
|
|
|
#include <usb/usb.h>
|
2008-09-02 18:06:22 +02:00
|
|
|
#include "uhci.h"
|
2010-06-07 15:58:17 +02:00
|
|
|
#include "uhci_private.h"
|
2008-09-02 18:06:22 +02:00
|
|
|
|
|
|
|
static void uhci_start (hci_t *controller);
|
|
|
|
static void uhci_stop (hci_t *controller);
|
|
|
|
static void uhci_reset (hci_t *controller);
|
|
|
|
static void uhci_shutdown (hci_t *controller);
|
|
|
|
static int uhci_bulk (endpoint_t *ep, int size, u8 *data, int finalize);
|
2010-06-07 15:58:17 +02:00
|
|
|
static int uhci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq,
|
2008-09-02 18:06:22 +02:00
|
|
|
int dalen, u8 *data);
|
2008-10-16 21:20:51 +02:00
|
|
|
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);
|
2008-09-02 18:06:22 +02:00
|
|
|
|
|
|
|
#if 0
|
|
|
|
/* dump uhci */
|
|
|
|
static void
|
|
|
|
uhci_dump (hci_t *controller)
|
|
|
|
{
|
2012-11-01 23:44:10 +01:00
|
|
|
usb_debug ("dump:\nUSBCMD: %x\n", uhci_reg_read16 (controller, USBCMD));
|
|
|
|
usb_debug ("USBSTS: %x\n", uhci_reg_read16 (controller, USBSTS));
|
|
|
|
usb_debug ("USBINTR: %x\n", uhci_reg_read16 (controller, USBINTR));
|
|
|
|
usb_debug ("FRNUM: %x\n", uhci_reg_read16 (controller, FRNUM));
|
|
|
|
usb_debug ("FLBASEADD: %x\n", uhci_reg_read32 (controller, FLBASEADD));
|
|
|
|
usb_debug ("SOFMOD: %x\n", uhci_reg_read8 (controller, SOFMOD));
|
|
|
|
usb_debug ("PORTSC1: %x\n", uhci_reg_read16 (controller, PORTSC1));
|
|
|
|
usb_debug ("PORTSC2: %x\n", uhci_reg_read16 (controller, PORTSC2));
|
2008-09-02 18:06:22 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void
|
|
|
|
td_dump (td_t *td)
|
|
|
|
{
|
2009-04-30 18:46:12 +02:00
|
|
|
char td_value[3];
|
2011-04-21 18:57:16 +02:00
|
|
|
const char *td_type;
|
2011-11-24 11:55:46 +01:00
|
|
|
switch (td->token & TD_PID_MASK) {
|
2010-06-07 15:58:17 +02:00
|
|
|
case UHCI_SETUP:
|
2009-04-30 18:46:12 +02:00
|
|
|
td_type="SETUP";
|
|
|
|
break;
|
2010-06-07 15:58:17 +02:00
|
|
|
case UHCI_IN:
|
2009-04-30 18:46:12 +02:00
|
|
|
td_type="IN";
|
|
|
|
break;
|
2010-06-07 15:58:17 +02:00
|
|
|
case UHCI_OUT:
|
2009-04-30 18:46:12 +02:00
|
|
|
td_type="OUT";
|
|
|
|
break;
|
|
|
|
default:
|
2011-11-24 11:55:46 +01:00
|
|
|
sprintf(td_value, "%x", td->token & TD_PID_MASK);
|
2009-04-30 18:46:12 +02:00
|
|
|
td_type=td_value;
|
|
|
|
}
|
2012-11-01 23:44:10 +01:00
|
|
|
usb_debug ("%s packet (at %lx) to %x.%x failed\n", td_type,
|
2011-11-24 11:55:46 +01:00
|
|
|
virt_to_phys (td), (td->token & TD_DEVADDR_MASK) >> TD_DEVADDR_SHIFT,
|
|
|
|
(td->token & TD_EP_MASK) >> TD_EP_SHIFT);
|
2012-11-01 23:44:10 +01:00
|
|
|
usb_debug ("td (counter at %x) returns: ", td->ctrlsts >> TD_COUNTER_SHIFT);
|
|
|
|
usb_debug (" bitstuff err: %x, ", !!(td->ctrlsts & TD_STATUS_BITSTUFF_ERR));
|
|
|
|
usb_debug (" CRC err: %x, ", !!(td->ctrlsts & TD_STATUS_CRC_ERR));
|
|
|
|
usb_debug (" NAK rcvd: %x, ", !!(td->ctrlsts & TD_STATUS_NAK_RCVD));
|
|
|
|
usb_debug (" Babble: %x, ", !!(td->ctrlsts & TD_STATUS_BABBLE));
|
|
|
|
usb_debug (" Data Buffer err: %x, ", !!(td->ctrlsts & TD_STATUS_DATABUF_ERR));
|
|
|
|
usb_debug (" Stalled: %x, ", !!(td->ctrlsts & TD_STATUS_STALLED));
|
|
|
|
usb_debug (" Active: %x\n", !!(td->ctrlsts & TD_STATUS_ACTIVE));
|
2011-11-24 11:55:46 +01:00
|
|
|
if (td->ctrlsts & TD_STATUS_BABBLE)
|
2012-11-01 23:44:10 +01:00
|
|
|
usb_debug (" Babble because of %s\n",
|
2011-11-24 11:55:46 +01:00
|
|
|
(td->ctrlsts & TD_STATUS_BITSTUFF_ERR) ? "host" : "device");
|
|
|
|
if (td->ctrlsts & TD_STATUS_ACTIVE)
|
2012-11-01 23:44:10 +01:00
|
|
|
usb_debug (" still active - timeout?\n");
|
2008-09-02 18:06:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
uhci_reset (hci_t *controller)
|
|
|
|
{
|
|
|
|
/* reset */
|
2012-05-21 14:23:03 +02:00
|
|
|
uhci_reg_write16 (controller, USBCMD, 4); /* Global Reset */
|
|
|
|
mdelay (50); /* uhci spec 2.1.1: at least 10ms */
|
2008-09-02 18:06:22 +02:00
|
|
|
uhci_reg_write16 (controller, USBCMD, 0);
|
|
|
|
mdelay (10);
|
2012-05-21 14:23:03 +02:00
|
|
|
uhci_reg_write16 (controller, USBCMD, 2); /* Host Controller Reset */
|
|
|
|
/* wait for controller to finish reset */
|
|
|
|
/* TOTEST: how long to wait? 100ms for now */
|
|
|
|
int timeout = 200; /* time out after 200 * 500us == 100ms */
|
|
|
|
while (((uhci_reg_read16 (controller, USBCMD) & 2) != 0) && timeout--)
|
|
|
|
udelay (500);
|
|
|
|
if (timeout < 0)
|
2012-11-01 23:44:10 +01:00
|
|
|
usb_debug ("Warning: uhci: host controller reset timed out.\n");
|
2008-09-02 18:06:22 +02:00
|
|
|
|
|
|
|
uhci_reg_write32 (controller, FLBASEADD,
|
|
|
|
(u32) virt_to_phys (UHCI_INST (controller)->
|
|
|
|
framelistptr));
|
2012-11-01 23:44:10 +01:00
|
|
|
//usb_debug ("framelist at %p\n",UHCI_INST(controller)->framelistptr);
|
2008-09-02 18:06:22 +02:00
|
|
|
|
|
|
|
/* disable irqs */
|
|
|
|
uhci_reg_write16 (controller, USBINTR, 0);
|
|
|
|
|
|
|
|
/* reset framelist index */
|
|
|
|
uhci_reg_write16 (controller, FRNUM, 0);
|
|
|
|
|
libpayload: remove uhci_reg_maskX
Not that good an idea to start with.
Coccinelle patch:
@@
@@
-void
(
-uhci_reg_mask8
|
-uhci_reg_mask16
|
-uhci_reg_mask32
)
- (...) { ... }
@@
@@
-void
(
-uhci_reg_mask8
|
-uhci_reg_mask16
|
-uhci_reg_mask32
)
- (...);
@@
expression ctrl, reg, ormask;
@@
-uhci_reg_mask32 (ctrl, reg, ~0, ormask)
+uhci_reg_write32 (ctrl, reg, uhci_reg_read32 (ctrl, reg) | ormask)
@@
expression ctrl, reg, ormask;
@@
-uhci_reg_mask16 (ctrl, reg, ~0, ormask)
+uhci_reg_write16 (ctrl, reg, uhci_reg_read16 (ctrl, reg) | ormask)
@@
expression ctrl, reg, ormask;
@@
-uhci_reg_mask8 (ctrl, reg, ~0, ormask)
+uhci_reg_write8 (ctrl, reg, uhci_reg_read8 (ctrl, reg) | ormask)
@@
expression ctrl, reg, andmask;
@@
-uhci_reg_mask32 (ctrl, reg, andmask, 0)
+uhci_reg_write32 (ctrl, reg, uhci_reg_read32 (ctrl, reg) & andmask)
@@
expression ctrl, reg, andmask;
@@
-uhci_reg_mask16 (ctrl, reg, andmask, 0)
+uhci_reg_write16 (ctrl, reg, uhci_reg_read16 (ctrl, reg) & andmask)
@@
expression ctrl, reg, andmask;
@@
-uhci_reg_mask16 (ctrl, reg, andmask, 0)
+uhci_reg_write16 (ctrl, reg, uhci_reg_read16 (ctrl, reg) & andmask)
Change-Id: Id0eb8327293831e54249d43fd06d50963c793699
Signed-off-by: Patrick Georgi <patrick.georgi@secunet.com>
Reviewed-on: http://review.coreboot.org/477
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2011-11-18 14:44:16 +01:00
|
|
|
uhci_reg_write16(controller, USBCMD,
|
|
|
|
uhci_reg_read16(controller, USBCMD) | 0xc0); // max packets, configure flag
|
2008-09-02 18:06:22 +02:00
|
|
|
|
|
|
|
uhci_start (controller);
|
|
|
|
}
|
|
|
|
|
|
|
|
hci_t *
|
|
|
|
uhci_init (pcidev_t addr)
|
|
|
|
{
|
|
|
|
int i;
|
2010-03-25 23:17:36 +01:00
|
|
|
u16 reg16;
|
|
|
|
|
2008-09-02 18:06:22 +02:00
|
|
|
hci_t *controller = new_controller ();
|
|
|
|
|
2009-07-31 13:39:55 +02:00
|
|
|
if (!controller)
|
2011-11-04 11:50:03 +01:00
|
|
|
fatal("Could not create USB controller instance.\n");
|
2009-07-31 13:39:55 +02:00
|
|
|
|
2008-09-02 18:06:22 +02:00
|
|
|
controller->instance = malloc (sizeof (uhci_t));
|
2009-07-31 13:39:55 +02:00
|
|
|
if(!controller->instance)
|
2011-11-04 11:50:03 +01:00
|
|
|
fatal("Not enough memory creating USB controller instance.\n");
|
2009-07-31 13:39:55 +02:00
|
|
|
|
2012-06-28 06:30:15 +02:00
|
|
|
controller->type = UHCI;
|
|
|
|
|
2008-09-02 18:06:22 +02:00
|
|
|
controller->start = uhci_start;
|
|
|
|
controller->stop = uhci_stop;
|
|
|
|
controller->reset = uhci_reset;
|
|
|
|
controller->shutdown = uhci_shutdown;
|
|
|
|
controller->bulk = uhci_bulk;
|
|
|
|
controller->control = uhci_control;
|
2008-10-16 21:20:51 +02:00
|
|
|
controller->create_intr_queue = uhci_create_intr_queue;
|
|
|
|
controller->destroy_intr_queue = uhci_destroy_intr_queue;
|
|
|
|
controller->poll_intr_queue = uhci_poll_intr_queue;
|
2009-07-18 17:17:40 +02:00
|
|
|
for (i = 0; i < 128; i++) {
|
2008-10-16 21:20:51 +02:00
|
|
|
controller->devices[i] = 0;
|
|
|
|
}
|
|
|
|
init_device_entry (controller, 0);
|
|
|
|
UHCI_INST (controller)->roothub = controller->devices[0];
|
2008-09-02 18:06:22 +02:00
|
|
|
|
|
|
|
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 */
|
|
|
|
|
|
|
|
/* kill legacy support handler */
|
|
|
|
uhci_stop (controller);
|
|
|
|
mdelay (1);
|
|
|
|
uhci_reg_write16 (controller, USBSTS, 0x3f);
|
2010-03-25 23:17:36 +01:00
|
|
|
reg16 = pci_read_config16(controller->bus_address, 0xc0);
|
|
|
|
reg16 &= 0xdf80;
|
|
|
|
pci_write_config16 (controller->bus_address, 0xc0, reg16);
|
2008-09-02 18:06:22 +02:00
|
|
|
|
2012-09-20 10:24:01 +02:00
|
|
|
UHCI_INST (controller)->framelistptr = memalign (0x1000, 1024 * sizeof (flistp_t)); /* 4kb aligned to 4kb */
|
2009-07-31 13:39:55 +02:00
|
|
|
if (! UHCI_INST (controller)->framelistptr)
|
2011-11-04 11:50:03 +01:00
|
|
|
fatal("Not enough memory for USB frame list pointer.\n");
|
2009-07-31 13:39:55 +02:00
|
|
|
|
2008-09-02 18:06:22 +02:00
|
|
|
memset (UHCI_INST (controller)->framelistptr, 0,
|
|
|
|
1024 * sizeof (flistp_t));
|
|
|
|
|
2008-10-16 21:20:51 +02:00
|
|
|
/* 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));
|
2009-07-31 13:39:55 +02:00
|
|
|
if (!antiberserk)
|
2011-11-04 11:50:03 +01:00
|
|
|
fatal("Not enough memory for chipset workaround.\n");
|
2008-10-16 21:20:51 +02:00
|
|
|
memset(antiberserk, 0, sizeof(td_t));
|
|
|
|
|
|
|
|
UHCI_INST (controller)->qh_prei = memalign (16, sizeof (qh_t));
|
2008-09-02 18:06:22 +02:00
|
|
|
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));
|
|
|
|
|
2009-07-31 13:39:55 +02:00
|
|
|
if (! UHCI_INST (controller)->qh_prei ||
|
|
|
|
! UHCI_INST (controller)->qh_intr ||
|
|
|
|
! UHCI_INST (controller)->qh_data ||
|
|
|
|
! UHCI_INST (controller)->qh_last)
|
2011-11-04 11:50:03 +01:00
|
|
|
fatal("Not enough memory for USB controller queues.\n");
|
2009-07-31 13:39:55 +02:00
|
|
|
|
2011-11-24 11:55:46 +01:00
|
|
|
UHCI_INST (controller)->qh_prei->headlinkptr =
|
|
|
|
virt_to_phys (UHCI_INST (controller)->qh_intr) | FLISTP_QH;
|
|
|
|
UHCI_INST (controller)->qh_prei->elementlinkptr = 0 | FLISTP_TERMINATE;
|
|
|
|
|
|
|
|
UHCI_INST (controller)->qh_intr->headlinkptr =
|
|
|
|
virt_to_phys (UHCI_INST (controller)->qh_data) | FLISTP_QH;
|
|
|
|
UHCI_INST (controller)->qh_intr->elementlinkptr = 0 | FLISTP_TERMINATE;
|
|
|
|
|
|
|
|
UHCI_INST (controller)->qh_data->headlinkptr =
|
|
|
|
virt_to_phys (UHCI_INST (controller)->qh_last) | FLISTP_QH;
|
|
|
|
UHCI_INST (controller)->qh_data->elementlinkptr = 0 | FLISTP_TERMINATE;
|
|
|
|
|
|
|
|
UHCI_INST (controller)->qh_last->headlinkptr = virt_to_phys (UHCI_INST (controller)->qh_data) | FLISTP_TERMINATE;
|
|
|
|
UHCI_INST (controller)->qh_last->elementlinkptr = virt_to_phys (antiberserk) | FLISTP_TERMINATE;
|
2008-09-02 18:06:22 +02:00
|
|
|
|
|
|
|
for (i = 0; i < 1024; i++) {
|
2011-11-24 11:55:46 +01:00
|
|
|
UHCI_INST (controller)->framelistptr[i] =
|
|
|
|
virt_to_phys (UHCI_INST (controller)->qh_prei) | FLISTP_QH;
|
2008-09-02 18:06:22 +02:00
|
|
|
}
|
2008-10-16 21:20:51 +02:00
|
|
|
controller->devices[0]->controller = controller;
|
|
|
|
controller->devices[0]->init = uhci_rh_init;
|
|
|
|
controller->devices[0]->init (controller->devices[0]);
|
2008-09-02 18:06:22 +02:00
|
|
|
uhci_reset (controller);
|
|
|
|
return controller;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
uhci_shutdown (hci_t *controller)
|
|
|
|
{
|
|
|
|
if (controller == 0)
|
|
|
|
return;
|
|
|
|
detach_controller (controller);
|
|
|
|
UHCI_INST (controller)->roothub->destroy (UHCI_INST (controller)->
|
|
|
|
roothub);
|
libpayload: remove uhci_reg_maskX
Not that good an idea to start with.
Coccinelle patch:
@@
@@
-void
(
-uhci_reg_mask8
|
-uhci_reg_mask16
|
-uhci_reg_mask32
)
- (...) { ... }
@@
@@
-void
(
-uhci_reg_mask8
|
-uhci_reg_mask16
|
-uhci_reg_mask32
)
- (...);
@@
expression ctrl, reg, ormask;
@@
-uhci_reg_mask32 (ctrl, reg, ~0, ormask)
+uhci_reg_write32 (ctrl, reg, uhci_reg_read32 (ctrl, reg) | ormask)
@@
expression ctrl, reg, ormask;
@@
-uhci_reg_mask16 (ctrl, reg, ~0, ormask)
+uhci_reg_write16 (ctrl, reg, uhci_reg_read16 (ctrl, reg) | ormask)
@@
expression ctrl, reg, ormask;
@@
-uhci_reg_mask8 (ctrl, reg, ~0, ormask)
+uhci_reg_write8 (ctrl, reg, uhci_reg_read8 (ctrl, reg) | ormask)
@@
expression ctrl, reg, andmask;
@@
-uhci_reg_mask32 (ctrl, reg, andmask, 0)
+uhci_reg_write32 (ctrl, reg, uhci_reg_read32 (ctrl, reg) & andmask)
@@
expression ctrl, reg, andmask;
@@
-uhci_reg_mask16 (ctrl, reg, andmask, 0)
+uhci_reg_write16 (ctrl, reg, uhci_reg_read16 (ctrl, reg) & andmask)
@@
expression ctrl, reg, andmask;
@@
-uhci_reg_mask16 (ctrl, reg, andmask, 0)
+uhci_reg_write16 (ctrl, reg, uhci_reg_read16 (ctrl, reg) & andmask)
Change-Id: Id0eb8327293831e54249d43fd06d50963c793699
Signed-off-by: Patrick Georgi <patrick.georgi@secunet.com>
Reviewed-on: http://review.coreboot.org/477
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2011-11-18 14:44:16 +01:00
|
|
|
uhci_reg_write16(controller, USBCMD,
|
|
|
|
uhci_reg_read16(controller, USBCMD) & 0); // stop work
|
2008-09-02 18:06:22 +02:00
|
|
|
free (UHCI_INST (controller)->framelistptr);
|
2008-10-16 21:20:51 +02:00
|
|
|
free (UHCI_INST (controller)->qh_prei);
|
2008-09-02 18:06:22 +02:00
|
|
|
free (UHCI_INST (controller)->qh_intr);
|
|
|
|
free (UHCI_INST (controller)->qh_data);
|
|
|
|
free (UHCI_INST (controller)->qh_last);
|
|
|
|
free (UHCI_INST (controller));
|
|
|
|
free (controller);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
uhci_start (hci_t *controller)
|
|
|
|
{
|
libpayload: remove uhci_reg_maskX
Not that good an idea to start with.
Coccinelle patch:
@@
@@
-void
(
-uhci_reg_mask8
|
-uhci_reg_mask16
|
-uhci_reg_mask32
)
- (...) { ... }
@@
@@
-void
(
-uhci_reg_mask8
|
-uhci_reg_mask16
|
-uhci_reg_mask32
)
- (...);
@@
expression ctrl, reg, ormask;
@@
-uhci_reg_mask32 (ctrl, reg, ~0, ormask)
+uhci_reg_write32 (ctrl, reg, uhci_reg_read32 (ctrl, reg) | ormask)
@@
expression ctrl, reg, ormask;
@@
-uhci_reg_mask16 (ctrl, reg, ~0, ormask)
+uhci_reg_write16 (ctrl, reg, uhci_reg_read16 (ctrl, reg) | ormask)
@@
expression ctrl, reg, ormask;
@@
-uhci_reg_mask8 (ctrl, reg, ~0, ormask)
+uhci_reg_write8 (ctrl, reg, uhci_reg_read8 (ctrl, reg) | ormask)
@@
expression ctrl, reg, andmask;
@@
-uhci_reg_mask32 (ctrl, reg, andmask, 0)
+uhci_reg_write32 (ctrl, reg, uhci_reg_read32 (ctrl, reg) & andmask)
@@
expression ctrl, reg, andmask;
@@
-uhci_reg_mask16 (ctrl, reg, andmask, 0)
+uhci_reg_write16 (ctrl, reg, uhci_reg_read16 (ctrl, reg) & andmask)
@@
expression ctrl, reg, andmask;
@@
-uhci_reg_mask16 (ctrl, reg, andmask, 0)
+uhci_reg_write16 (ctrl, reg, uhci_reg_read16 (ctrl, reg) & andmask)
Change-Id: Id0eb8327293831e54249d43fd06d50963c793699
Signed-off-by: Patrick Georgi <patrick.georgi@secunet.com>
Reviewed-on: http://review.coreboot.org/477
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2011-11-18 14:44:16 +01:00
|
|
|
uhci_reg_write16(controller, USBCMD,
|
|
|
|
uhci_reg_read16(controller, USBCMD) | 1); // start work on schedule
|
2008-09-02 18:06:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
uhci_stop (hci_t *controller)
|
|
|
|
{
|
libpayload: remove uhci_reg_maskX
Not that good an idea to start with.
Coccinelle patch:
@@
@@
-void
(
-uhci_reg_mask8
|
-uhci_reg_mask16
|
-uhci_reg_mask32
)
- (...) { ... }
@@
@@
-void
(
-uhci_reg_mask8
|
-uhci_reg_mask16
|
-uhci_reg_mask32
)
- (...);
@@
expression ctrl, reg, ormask;
@@
-uhci_reg_mask32 (ctrl, reg, ~0, ormask)
+uhci_reg_write32 (ctrl, reg, uhci_reg_read32 (ctrl, reg) | ormask)
@@
expression ctrl, reg, ormask;
@@
-uhci_reg_mask16 (ctrl, reg, ~0, ormask)
+uhci_reg_write16 (ctrl, reg, uhci_reg_read16 (ctrl, reg) | ormask)
@@
expression ctrl, reg, ormask;
@@
-uhci_reg_mask8 (ctrl, reg, ~0, ormask)
+uhci_reg_write8 (ctrl, reg, uhci_reg_read8 (ctrl, reg) | ormask)
@@
expression ctrl, reg, andmask;
@@
-uhci_reg_mask32 (ctrl, reg, andmask, 0)
+uhci_reg_write32 (ctrl, reg, uhci_reg_read32 (ctrl, reg) & andmask)
@@
expression ctrl, reg, andmask;
@@
-uhci_reg_mask16 (ctrl, reg, andmask, 0)
+uhci_reg_write16 (ctrl, reg, uhci_reg_read16 (ctrl, reg) & andmask)
@@
expression ctrl, reg, andmask;
@@
-uhci_reg_mask16 (ctrl, reg, andmask, 0)
+uhci_reg_write16 (ctrl, reg, uhci_reg_read16 (ctrl, reg) & andmask)
Change-Id: Id0eb8327293831e54249d43fd06d50963c793699
Signed-off-by: Patrick Georgi <patrick.georgi@secunet.com>
Reviewed-on: http://review.coreboot.org/477
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2011-11-18 14:44:16 +01:00
|
|
|
uhci_reg_write16(controller, USBCMD,
|
|
|
|
uhci_reg_read16(controller, USBCMD) & ~1); // stop work on schedule
|
2008-09-02 18:06:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#define GET_TD(x) ((void*)(((unsigned int)(x))&~0xf))
|
|
|
|
|
|
|
|
static td_t *
|
|
|
|
wait_for_completed_qh (hci_t *controller, qh_t *qh)
|
|
|
|
{
|
2012-05-29 16:19:19 +02:00
|
|
|
int timeout = 1000; /* max 30 ms. */
|
2011-11-24 11:55:46 +01:00
|
|
|
void *current = GET_TD (qh->elementlinkptr);
|
|
|
|
while (((qh->elementlinkptr & FLISTP_TERMINATE) == 0) && (timeout-- > 0)) {
|
|
|
|
if (current != GET_TD (qh->elementlinkptr)) {
|
|
|
|
current = GET_TD (qh->elementlinkptr);
|
2012-05-29 16:19:19 +02:00
|
|
|
timeout = 1000;
|
2008-09-02 18:06:22 +02:00
|
|
|
}
|
libpayload: remove uhci_reg_maskX
Not that good an idea to start with.
Coccinelle patch:
@@
@@
-void
(
-uhci_reg_mask8
|
-uhci_reg_mask16
|
-uhci_reg_mask32
)
- (...) { ... }
@@
@@
-void
(
-uhci_reg_mask8
|
-uhci_reg_mask16
|
-uhci_reg_mask32
)
- (...);
@@
expression ctrl, reg, ormask;
@@
-uhci_reg_mask32 (ctrl, reg, ~0, ormask)
+uhci_reg_write32 (ctrl, reg, uhci_reg_read32 (ctrl, reg) | ormask)
@@
expression ctrl, reg, ormask;
@@
-uhci_reg_mask16 (ctrl, reg, ~0, ormask)
+uhci_reg_write16 (ctrl, reg, uhci_reg_read16 (ctrl, reg) | ormask)
@@
expression ctrl, reg, ormask;
@@
-uhci_reg_mask8 (ctrl, reg, ~0, ormask)
+uhci_reg_write8 (ctrl, reg, uhci_reg_read8 (ctrl, reg) | ormask)
@@
expression ctrl, reg, andmask;
@@
-uhci_reg_mask32 (ctrl, reg, andmask, 0)
+uhci_reg_write32 (ctrl, reg, uhci_reg_read32 (ctrl, reg) & andmask)
@@
expression ctrl, reg, andmask;
@@
-uhci_reg_mask16 (ctrl, reg, andmask, 0)
+uhci_reg_write16 (ctrl, reg, uhci_reg_read16 (ctrl, reg) & andmask)
@@
expression ctrl, reg, andmask;
@@
-uhci_reg_mask16 (ctrl, reg, andmask, 0)
+uhci_reg_write16 (ctrl, reg, uhci_reg_read16 (ctrl, reg) & andmask)
Change-Id: Id0eb8327293831e54249d43fd06d50963c793699
Signed-off-by: Patrick Georgi <patrick.georgi@secunet.com>
Reviewed-on: http://review.coreboot.org/477
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2011-11-18 14:44:16 +01:00
|
|
|
uhci_reg_write16(controller, USBSTS,
|
|
|
|
uhci_reg_read16(controller, USBSTS) | 0); // clear resettable registers
|
2008-09-02 18:06:22 +02:00
|
|
|
udelay (30);
|
|
|
|
}
|
2011-11-24 11:55:46 +01:00
|
|
|
return (GET_TD (qh->elementlinkptr) ==
|
|
|
|
0) ? 0 : GET_TD (phys_to_virt (qh->elementlinkptr));
|
2008-09-02 18:06:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
maxlen (int size)
|
|
|
|
{
|
|
|
|
return (size - 1) & 0x7ff;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
min (int a, int b)
|
|
|
|
{
|
|
|
|
if (a < b)
|
|
|
|
return a;
|
|
|
|
else
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2010-06-07 15:58:17 +02:00
|
|
|
uhci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen,
|
2008-09-02 18:06:22 +02:00
|
|
|
unsigned char *data)
|
|
|
|
{
|
|
|
|
int endp = 0; /* this is control: always 0 */
|
|
|
|
int mlen = dev->endpoints[0].maxpacketsize;
|
|
|
|
int count = (2 + (dalen + mlen - 1) / mlen);
|
|
|
|
unsigned short req = ((unsigned short *) devreq)[0];
|
|
|
|
int i;
|
|
|
|
td_t *tds = memalign (16, sizeof (td_t) * count);
|
|
|
|
memset (tds, 0, sizeof (td_t) * count);
|
|
|
|
count--; /* to compensate for 0-indexed array */
|
|
|
|
for (i = 0; i < count; i++) {
|
2011-11-24 11:55:46 +01:00
|
|
|
tds[i].ptr = virt_to_phys (&tds[i + 1]) | TD_DEPTH_FIRST;
|
2008-09-02 18:06:22 +02:00
|
|
|
}
|
2011-11-24 11:55:46 +01:00
|
|
|
tds[count].ptr = 0 | TD_DEPTH_FIRST | TD_TERMINATE;
|
|
|
|
|
|
|
|
tds[0].token = UHCI_SETUP |
|
|
|
|
dev->address << TD_DEVADDR_SHIFT |
|
|
|
|
endp << TD_EP_SHIFT |
|
|
|
|
TD_TOGGLE_DATA0 |
|
|
|
|
maxlen(drlen) << TD_MAXLEN_SHIFT;
|
2008-09-02 18:06:22 +02:00
|
|
|
tds[0].bufptr = virt_to_phys (devreq);
|
2011-11-24 11:55:46 +01:00
|
|
|
tds[0].ctrlsts = (3 << TD_COUNTER_SHIFT) |
|
|
|
|
(dev->speed?TD_LOWSPEED:0) |
|
|
|
|
TD_STATUS_ACTIVE;
|
2008-09-02 18:06:22 +02:00
|
|
|
|
|
|
|
int toggle = 1;
|
|
|
|
for (i = 1; i < count; i++) {
|
2010-06-07 15:58:17 +02:00
|
|
|
switch (dir) {
|
2011-11-24 11:55:46 +01:00
|
|
|
case SETUP: tds[i].token = UHCI_SETUP; break;
|
|
|
|
case IN: tds[i].token = UHCI_IN; break;
|
|
|
|
case OUT: tds[i].token = UHCI_OUT; break;
|
2010-06-07 15:58:17 +02:00
|
|
|
}
|
2011-11-24 11:55:46 +01:00
|
|
|
tds[i].token |= dev->address << TD_DEVADDR_SHIFT |
|
|
|
|
endp << TD_EP_SHIFT |
|
|
|
|
maxlen (min (mlen, dalen)) << TD_MAXLEN_SHIFT |
|
|
|
|
toggle << TD_TOGGLE_SHIFT;
|
2008-09-02 18:06:22 +02:00
|
|
|
tds[i].bufptr = virt_to_phys (data);
|
2011-11-24 11:55:46 +01:00
|
|
|
tds[i].ctrlsts = (3 << TD_COUNTER_SHIFT) |
|
|
|
|
(dev->speed?TD_LOWSPEED:0) |
|
|
|
|
TD_STATUS_ACTIVE;
|
2008-09-02 18:06:22 +02:00
|
|
|
toggle ^= 1;
|
|
|
|
dalen -= mlen;
|
|
|
|
data += mlen;
|
|
|
|
}
|
|
|
|
|
2012-05-16 15:04:27 +02:00
|
|
|
tds[count].token = ((dir == OUT) ? UHCI_IN : UHCI_OUT) |
|
2011-11-24 11:55:46 +01:00
|
|
|
dev->address << TD_DEVADDR_SHIFT |
|
|
|
|
endp << TD_EP_SHIFT |
|
|
|
|
maxlen(0) << TD_MAXLEN_SHIFT |
|
|
|
|
TD_TOGGLE_DATA1;
|
2010-03-25 23:17:36 +01:00
|
|
|
tds[count].bufptr = 0;
|
2012-05-16 15:04:27 +02:00
|
|
|
tds[count].ctrlsts = (0 << TD_COUNTER_SHIFT) | /* as Linux 2.4.10 does */
|
2011-11-24 11:55:46 +01:00
|
|
|
(dev->speed?TD_LOWSPEED:0) |
|
|
|
|
TD_STATUS_ACTIVE;
|
|
|
|
UHCI_INST (dev->controller)->qh_data->elementlinkptr =
|
|
|
|
virt_to_phys (tds) & ~(FLISTP_QH | FLISTP_TERMINATE);
|
2008-09-02 18:06:22 +02:00
|
|
|
td_t *td = wait_for_completed_qh (dev->controller,
|
|
|
|
UHCI_INST (dev->controller)->
|
|
|
|
qh_data);
|
|
|
|
int result;
|
|
|
|
if (td == 0) {
|
|
|
|
result = 0;
|
|
|
|
} else {
|
2012-11-01 23:44:10 +01:00
|
|
|
usb_debug ("control packet, req %x\n", req);
|
2008-09-02 18:06:22 +02:00
|
|
|
td_dump (td);
|
|
|
|
result = 1;
|
|
|
|
}
|
|
|
|
free (tds);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static td_t *
|
|
|
|
create_schedule (int numpackets)
|
|
|
|
{
|
|
|
|
if (numpackets == 0)
|
|
|
|
return 0;
|
|
|
|
td_t *tds = memalign (16, sizeof (td_t) * numpackets);
|
|
|
|
memset (tds, 0, sizeof (td_t) * numpackets);
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < numpackets; i++) {
|
2011-11-24 11:55:46 +01:00
|
|
|
tds[i].ptr = virt_to_phys (&tds[i + 1]) | TD_DEPTH_FIRST;
|
2008-09-02 18:06:22 +02:00
|
|
|
}
|
2011-11-24 11:55:46 +01:00
|
|
|
tds[numpackets - 1].ptr = 0 | TD_TERMINATE;
|
2008-09-02 18:06:22 +02:00
|
|
|
return tds;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
fill_schedule (td_t *td, endpoint_t *ep, int length, unsigned char *data,
|
|
|
|
int *toggle)
|
|
|
|
{
|
2010-06-11 16:25:40 +02:00
|
|
|
switch (ep->direction) {
|
2011-11-24 11:55:46 +01:00
|
|
|
case IN: td->token = UHCI_IN; break;
|
|
|
|
case OUT: td->token = UHCI_OUT; break;
|
|
|
|
case SETUP: td->token = UHCI_SETUP; break;
|
2010-06-11 16:25:40 +02:00
|
|
|
}
|
2011-11-24 11:55:46 +01:00
|
|
|
td->token |= ep->dev->address << TD_DEVADDR_SHIFT |
|
|
|
|
(ep->endpoint & 0xf) << TD_EP_SHIFT |
|
|
|
|
maxlen (length) << TD_MAXLEN_SHIFT |
|
|
|
|
(*toggle & 1) << TD_TOGGLE_SHIFT;
|
2008-09-02 18:06:22 +02:00
|
|
|
td->bufptr = virt_to_phys (data);
|
2011-11-24 11:55:46 +01:00
|
|
|
td->ctrlsts = ((ep->direction == SETUP?3:0) << TD_COUNTER_SHIFT) |
|
2012-05-16 15:04:27 +02:00
|
|
|
(ep->dev->speed?TD_LOWSPEED:0) |
|
2011-11-24 11:55:46 +01:00
|
|
|
TD_STATUS_ACTIVE;
|
2008-09-02 18:06:22 +02:00
|
|
|
*toggle ^= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
run_schedule (usbdev_t *dev, td_t *td)
|
|
|
|
{
|
2011-11-24 11:55:46 +01:00
|
|
|
UHCI_INST (dev->controller)->qh_data->elementlinkptr =
|
2012-10-01 22:04:29 +02:00
|
|
|
virt_to_phys (td) & ~(FLISTP_QH | FLISTP_TERMINATE);
|
2008-09-02 18:06:22 +02:00
|
|
|
td = wait_for_completed_qh (dev->controller,
|
|
|
|
UHCI_INST (dev->controller)->qh_data);
|
|
|
|
if (td == 0) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
td_dump (td);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* finalize == 1: if data is of packet aligned size, add a zero length packet */
|
|
|
|
static int
|
|
|
|
uhci_bulk (endpoint_t *ep, int size, u8 *data, int finalize)
|
|
|
|
{
|
|
|
|
int maxpsize = ep->maxpacketsize;
|
|
|
|
if (maxpsize == 0)
|
2011-11-04 11:50:03 +01:00
|
|
|
fatal("MaxPacketSize == 0!!!");
|
2012-10-03 08:23:56 +02:00
|
|
|
int numpackets = (size + maxpsize - 1) / maxpsize;
|
|
|
|
if (finalize && ((size % maxpsize) == 0)) {
|
|
|
|
numpackets++;
|
|
|
|
}
|
2008-09-02 18:06:22 +02:00
|
|
|
if (numpackets == 0)
|
|
|
|
return 0;
|
|
|
|
td_t *tds = create_schedule (numpackets);
|
|
|
|
int i = 0, toggle = ep->toggle;
|
|
|
|
while ((size > 0) || ((size == 0) && (finalize != 0))) {
|
|
|
|
fill_schedule (&tds[i], ep, min (size, maxpsize), data,
|
|
|
|
&toggle);
|
|
|
|
i++;
|
|
|
|
data += maxpsize;
|
|
|
|
size -= maxpsize;
|
|
|
|
}
|
|
|
|
if (run_schedule (ep->dev, tds) == 1) {
|
2012-11-01 23:44:10 +01:00
|
|
|
usb_debug("Stalled. Trying to clean up.\n");
|
2008-09-02 18:06:22 +02:00
|
|
|
clear_stall (ep);
|
|
|
|
free (tds);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
ep->toggle = toggle;
|
|
|
|
free (tds);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-10-16 21:20:51 +02:00
|
|
|
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));
|
|
|
|
|
2009-07-31 13:39:55 +02:00
|
|
|
if (!data || !tds || !qh)
|
2011-11-04 11:50:03 +01:00
|
|
|
fatal("Not enough memory to create USB intr queue prerequisites.\n");
|
2009-07-31 13:39:55 +02:00
|
|
|
|
2011-11-24 11:55:46 +01:00
|
|
|
qh->elementlinkptr = virt_to_phys(tds);
|
2008-10-16 21:20:51 +02:00
|
|
|
|
|
|
|
intr_q *q = malloc(sizeof(intr_q));
|
2009-07-31 13:39:55 +02:00
|
|
|
if (!q)
|
2011-11-04 11:50:03 +01:00
|
|
|
fatal("Not enough memory to create USB intr queue.\n");
|
2008-10-16 21:20:51 +02:00
|
|
|
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]);
|
|
|
|
|
2010-06-11 16:25:40 +02:00
|
|
|
switch (ep->direction) {
|
2011-11-24 11:55:46 +01:00
|
|
|
case IN: tds[i].token = UHCI_IN; break;
|
|
|
|
case OUT: tds[i].token = UHCI_OUT; break;
|
|
|
|
case SETUP: tds[i].token = UHCI_SETUP; break;
|
2010-06-11 16:25:40 +02:00
|
|
|
}
|
2011-11-24 11:55:46 +01:00
|
|
|
tds[i].token |= ep->dev->address << TD_DEVADDR_SHIFT |
|
|
|
|
(ep->endpoint & 0xf) << TD_EP_SHIFT |
|
|
|
|
maxlen (reqsize) << TD_MAXLEN_SHIFT |
|
|
|
|
(ep->toggle & 1) << TD_TOGGLE_SHIFT;
|
2008-10-16 21:20:51 +02:00
|
|
|
tds[i].bufptr = virt_to_phys (data);
|
2011-11-24 11:55:46 +01:00
|
|
|
tds[i].ctrlsts = (0 << TD_COUNTER_SHIFT) |
|
2012-05-16 15:04:27 +02:00
|
|
|
(ep->dev->speed?TD_LOWSPEED:0) |
|
2011-11-24 11:55:46 +01:00
|
|
|
TD_STATUS_ACTIVE;
|
2008-10-16 21:20:51 +02:00
|
|
|
ep->toggle ^= 1;
|
|
|
|
data += reqsize;
|
|
|
|
}
|
2011-11-24 11:55:46 +01:00
|
|
|
tds[reqcount - 1].ptr = 0 | TD_TERMINATE;
|
2008-10-16 21:20:51 +02:00
|
|
|
for (i = reqtiming; i < 1024; i += reqtiming) {
|
|
|
|
/* FIXME: wrap in another qh, one for each occurance of the qh in the framelist */
|
2011-11-24 11:55:46 +01:00
|
|
|
qh->headlinkptr = UHCI_INST (ep->dev->controller)->framelistptr[i] & ~FLISTP_TERMINATE;
|
|
|
|
UHCI_INST (ep->dev->controller)->framelistptr[i] = virt_to_phys(qh) | FLISTP_QH;
|
2008-10-16 21:20:51 +02:00
|
|
|
}
|
|
|
|
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;
|
2011-11-24 11:55:46 +01:00
|
|
|
u32 ptr = UHCI_INST (ep->dev->controller)->framelistptr[i];
|
2008-10-16 21:20:51 +02:00
|
|
|
while (ptr != end) {
|
2011-11-24 11:55:46 +01:00
|
|
|
if (((qh_t*)phys_to_virt(ptr))->elementlinkptr == val) {
|
|
|
|
((qh_t*)phys_to_virt(oldptr))->headlinkptr = ((qh_t*)phys_to_virt(ptr))->headlinkptr;
|
2008-10-16 21:20:51 +02:00
|
|
|
free(phys_to_virt(ptr));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
oldptr = ptr;
|
2011-11-24 11:55:46 +01:00
|
|
|
ptr = ((qh_t*)phys_to_virt(ptr))->headlinkptr;
|
2008-10-16 21:20:51 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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_;
|
2011-11-24 11:55:46 +01:00
|
|
|
if ((q->tds[q->lastread].ctrlsts & TD_STATUS_ACTIVE) == 0) {
|
2008-10-16 21:20:51 +02:00
|
|
|
/* FIXME: handle errors */
|
|
|
|
int current = q->lastread;
|
|
|
|
int previous;
|
|
|
|
if (q->lastread == 0) {
|
|
|
|
previous = q->total - 1;
|
|
|
|
} else {
|
|
|
|
previous = q->lastread - 1;
|
|
|
|
}
|
2011-11-24 11:55:46 +01:00
|
|
|
q->tds[previous].ctrlsts &= ~TD_STATUS_MASK;
|
|
|
|
q->tds[previous].ptr = 0 | TD_TERMINATE;
|
2008-10-16 21:20:51 +02:00
|
|
|
if (q->last_td != &q->tds[previous]) {
|
2011-11-24 11:55:46 +01:00
|
|
|
q->last_td->ptr = virt_to_phys(&q->tds[previous]) & ~TD_TERMINATE;
|
2008-10-16 21:20:51 +02:00
|
|
|
q->last_td = &q->tds[previous];
|
|
|
|
}
|
2011-11-24 11:55:46 +01:00
|
|
|
q->tds[previous].ctrlsts |= TD_STATUS_ACTIVE;
|
2008-10-16 21:20:51 +02:00
|
|
|
q->lastread = (q->lastread + 1) % q->total;
|
|
|
|
return &q->data[current*q->reqsize];
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-09-02 18:06:22 +02:00
|
|
|
void
|
|
|
|
uhci_reg_write32 (hci_t *ctrl, usbreg reg, u32 value)
|
|
|
|
{
|
|
|
|
outl (value, ctrl->reg_base + reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
u32
|
|
|
|
uhci_reg_read32 (hci_t *ctrl, usbreg reg)
|
|
|
|
{
|
|
|
|
return inl (ctrl->reg_base + reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
uhci_reg_write16 (hci_t *ctrl, usbreg reg, u16 value)
|
|
|
|
{
|
|
|
|
outw (value, ctrl->reg_base + reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
u16
|
|
|
|
uhci_reg_read16 (hci_t *ctrl, usbreg reg)
|
|
|
|
{
|
|
|
|
return inw (ctrl->reg_base + reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
uhci_reg_write8 (hci_t *ctrl, usbreg reg, u8 value)
|
|
|
|
{
|
|
|
|
outb (value, ctrl->reg_base + reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
u8
|
|
|
|
uhci_reg_read8 (hci_t *ctrl, usbreg reg)
|
|
|
|
{
|
|
|
|
return inb (ctrl->reg_base + reg);
|
|
|
|
}
|