Add support for OHCI controllers and prelimiary support for xHCI (USB3) controllers.
Improve scanning for USB controllers. Limitations: - OHCI doesn't support interrupt transfers yet (ie. no keyboards) - xHCI just does initialization and device attach/detach so far Signed-off-by: Patrick Georgi <patrick@georgi-clan.de> Acked-by: Peter Stuge <peter@stuge.se> git-svn-id: svn://svn.coreboot.org/coreboot/trunk@5691 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
This commit is contained in:
parent
03e54de648
commit
6615ef3bfc
|
@ -224,7 +224,6 @@ config USB_OHCI
|
|||
help
|
||||
Select this option if you are going to use USB 1.1 on an AMD based
|
||||
system.
|
||||
NOTE: This option is not (fully) implemented yet
|
||||
|
||||
config USB_EHCI
|
||||
bool "Support for USB EHCI controllers"
|
||||
|
@ -233,6 +232,13 @@ config USB_EHCI
|
|||
Select this option if you want to use USB 2.0
|
||||
NOTE: This option is not (fully) implemented yet
|
||||
|
||||
config USB_XHCI
|
||||
bool "Support for USB xHCI controllers"
|
||||
depends on USB
|
||||
help
|
||||
Select this option if you want to use USB 3.0
|
||||
NOTE: This option is not (fully) implemented yet
|
||||
|
||||
config USB_HID
|
||||
bool "Support for USB keyboards"
|
||||
depends on USB
|
||||
|
|
|
@ -60,6 +60,10 @@ TARGETS-$(CONFIG_USB) += drivers/usb/quirks.o
|
|||
TARGETS-$(CONFIG_USB_HUB) += drivers/usb/usbhub.o
|
||||
TARGETS-$(CONFIG_USB_UHCI) += drivers/usb/uhci.o
|
||||
TARGETS-$(CONFIG_USB_UHCI) += drivers/usb/uhci_rh.o
|
||||
TARGETS-$(CONFIG_USB_OHCI) += drivers/usb/ohci.o
|
||||
TARGETS-$(CONFIG_USB_OHCI) += drivers/usb/ohci_rh.o
|
||||
TARGETS-$(CONFIG_USB_XHCI) += drivers/usb/xhci.o
|
||||
TARGETS-$(CONFIG_USB_XHCI) += drivers/usb/xhci_rh.o
|
||||
TARGETS-$(CONFIG_USB_HID) += drivers/usb/usbhid.o
|
||||
TARGETS-$(CONFIG_USB_MSC) += drivers/usb/usbmsc.o
|
||||
|
||||
|
|
|
@ -0,0 +1,472 @@
|
|||
/*
|
||||
* This file is part of the libpayload project.
|
||||
*
|
||||
* Copyright (C) 2010 Patrick Georgi
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define USB_DEBUG
|
||||
|
||||
#include <arch/virtual.h>
|
||||
#include <usb/usb.h>
|
||||
#include "ohci_private.h"
|
||||
#include "ohci.h"
|
||||
|
||||
static void ohci_start (hci_t *controller);
|
||||
static void ohci_stop (hci_t *controller);
|
||||
static void ohci_reset (hci_t *controller);
|
||||
static void ohci_shutdown (hci_t *controller);
|
||||
static int ohci_bulk (endpoint_t *ep, int size, u8 *data, int finalize);
|
||||
static int ohci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq,
|
||||
int dalen, u8 *data);
|
||||
static void* ohci_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming);
|
||||
static void ohci_destroy_intr_queue (endpoint_t *ep, void *queue);
|
||||
static u8* ohci_poll_intr_queue (void *queue);
|
||||
|
||||
static void
|
||||
ohci_reset (hci_t *controller)
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef USB_DEBUG
|
||||
/* Section 4.3.3 */
|
||||
static const char *completion_codes[] = {
|
||||
"No error",
|
||||
"CRC",
|
||||
"Bit stuffing",
|
||||
"Data toggle mismatch",
|
||||
"Stall",
|
||||
"Device not responding",
|
||||
"PID check failure",
|
||||
"Unexpected PID",
|
||||
"Data overrun",
|
||||
"Data underrun",
|
||||
"--- (10)",
|
||||
"--- (11)",
|
||||
"Buffer overrun",
|
||||
"Buffer underrun",
|
||||
"Not accessed (14)",
|
||||
"Not accessed (15)"
|
||||
};
|
||||
|
||||
/* Section 4.3.1.2 */
|
||||
static const char *direction[] = {
|
||||
"SETUP",
|
||||
"OUT",
|
||||
"IN",
|
||||
"reserved / from TD"
|
||||
};
|
||||
#endif
|
||||
|
||||
hci_t *
|
||||
ohci_init (pcidev_t addr)
|
||||
{
|
||||
int i;
|
||||
|
||||
hci_t *controller = new_controller ();
|
||||
|
||||
if (!controller)
|
||||
usb_fatal("Could not create USB controller instance.\n");
|
||||
|
||||
controller->instance = malloc (sizeof (ohci_t));
|
||||
if(!controller->instance)
|
||||
usb_fatal("Not enough memory creating USB controller instance.\n");
|
||||
|
||||
controller->start = ohci_start;
|
||||
controller->stop = ohci_stop;
|
||||
controller->reset = ohci_reset;
|
||||
controller->shutdown = ohci_shutdown;
|
||||
controller->bulk = ohci_bulk;
|
||||
controller->control = ohci_control;
|
||||
controller->create_intr_queue = ohci_create_intr_queue;
|
||||
controller->destroy_intr_queue = ohci_destroy_intr_queue;
|
||||
controller->poll_intr_queue = ohci_poll_intr_queue;
|
||||
for (i = 0; i < 128; i++) {
|
||||
controller->devices[i] = 0;
|
||||
}
|
||||
init_device_entry (controller, 0);
|
||||
OHCI_INST (controller)->roothub = controller->devices[0];
|
||||
|
||||
controller->bus_address = addr;
|
||||
controller->reg_base = pci_read_config32 (controller->bus_address, 0x10); // OHCI mandates MMIO, so bit 0 is clear
|
||||
OHCI_INST (controller)->opreg = (opreg_t*)phys_to_virt(controller->reg_base);
|
||||
printf("OHCI Version %x.%x\n", (OHCI_INST (controller)->opreg->HcRevision >> 4) & 0xf, OHCI_INST (controller)->opreg->HcRevision & 0xf);
|
||||
|
||||
if ((OHCI_INST (controller)->opreg->HcControl & HostControllerFunctionalStateMask) == USBReset) {
|
||||
/* cold boot */
|
||||
OHCI_INST (controller)->opreg->HcControl &= ~RemoteWakeupConnected;
|
||||
OHCI_INST (controller)->opreg->HcFmInterval = (11999 * FrameInterval) | ((((11999 - 210)*6)/7) * FSLargestDataPacket);
|
||||
/* TODO: right value for PowerOnToPowerGoodTime ? */
|
||||
OHCI_INST (controller)->opreg->HcRhDescriptorA = NoPowerSwitching | NoOverCurrentProtection | (10 * PowerOnToPowerGoodTime);
|
||||
OHCI_INST (controller)->opreg->HcRhDescriptorB = (0 * DeviceRemovable);
|
||||
udelay(100); /* TODO: reset asserting according to USB spec */
|
||||
} else if ((OHCI_INST (controller)->opreg->HcControl & HostControllerFunctionalStateMask) != USBOperational) {
|
||||
OHCI_INST (controller)->opreg->HcControl = (OHCI_INST (controller)->opreg->HcControl & ~HostControllerFunctionalStateMask) | USBResume;
|
||||
udelay(100); /* TODO: resume time according to USB spec */
|
||||
}
|
||||
int interval = OHCI_INST (controller)->opreg->HcFmInterval;
|
||||
|
||||
td_t *periodic_td = memalign(sizeof(*periodic_td), sizeof(*periodic_td));
|
||||
memset((void*)periodic_td, 0, sizeof(*periodic_td));
|
||||
for (i=0; i<32; i++) OHCI_INST (controller)->hcca->HccaInterruptTable[i] = virt_to_phys(periodic_td);
|
||||
/* TODO: build HCCA data structures */
|
||||
|
||||
OHCI_INST (controller)->opreg->HcCommandStatus = HostControllerReset;
|
||||
udelay (10); /* at most 10us for reset to complete. State must be set to Operational within 2ms (5.1.1.4) */
|
||||
OHCI_INST (controller)->opreg->HcFmInterval = interval;
|
||||
OHCI_INST (controller)->hcca = memalign(256, 256);
|
||||
memset((void*)OHCI_INST (controller)->hcca, 0, 256);
|
||||
|
||||
OHCI_INST (controller)->opreg->HcHCCA = virt_to_phys(OHCI_INST (controller)->hcca);
|
||||
OHCI_INST (controller)->opreg->HcControl &= ~IsochronousEnable; // unused by this driver
|
||||
OHCI_INST (controller)->opreg->HcControl |= BulkListEnable; // always enabled. OHCI still sleeps on BulkListFilled
|
||||
OHCI_INST (controller)->opreg->HcControl |= ControlListEnable; // dito
|
||||
OHCI_INST (controller)->opreg->HcControl |= PeriodicListEnable; // FIXME: setup interrupt data structures and enable all the time
|
||||
// disable everything, contrary to what OHCI spec says in 5.1.1.4, as we don't need IRQs
|
||||
OHCI_INST (controller)->opreg->HcInterruptEnable = 1<<31;
|
||||
OHCI_INST (controller)->opreg->HcInterruptDisable = ~(1<<31);
|
||||
OHCI_INST (controller)->opreg->HcInterruptStatus = ~0;
|
||||
OHCI_INST (controller)->opreg->HcPeriodicStart = (((OHCI_INST (controller)->opreg->HcFmInterval & FrameIntervalMask) / 10) * 9);
|
||||
OHCI_INST (controller)->opreg->HcControl = (OHCI_INST (controller)->opreg->HcControl & ~HostControllerFunctionalStateMask) | USBOperational;
|
||||
|
||||
mdelay(100);
|
||||
|
||||
controller->devices[0]->controller = controller;
|
||||
controller->devices[0]->init = ohci_rh_init;
|
||||
controller->devices[0]->init (controller->devices[0]);
|
||||
ohci_reset (controller);
|
||||
return controller;
|
||||
}
|
||||
|
||||
static void
|
||||
ohci_shutdown (hci_t *controller)
|
||||
{
|
||||
if (controller == 0)
|
||||
return;
|
||||
detach_controller (controller);
|
||||
ohci_stop(controller);
|
||||
OHCI_INST (controller)->roothub->destroy (OHCI_INST (controller)->
|
||||
roothub);
|
||||
free (OHCI_INST (controller));
|
||||
free (controller);
|
||||
}
|
||||
|
||||
static void
|
||||
ohci_start (hci_t *controller)
|
||||
{
|
||||
// TODO: turn on all operation of OHCI, but assume that it's initialized.
|
||||
}
|
||||
|
||||
static void
|
||||
ohci_stop (hci_t *controller)
|
||||
{
|
||||
// TODO: turn off all operation of OHCI
|
||||
}
|
||||
|
||||
static void
|
||||
dump_td(td_t *cur, int level)
|
||||
{
|
||||
#ifdef USB_DEBUG
|
||||
static const char *spaces=" ";
|
||||
const char *spc=spaces+(10-level);
|
||||
#endif
|
||||
debug("%std at %x (%s), condition code: %s\n", spc, cur, direction[cur->direction], completion_codes[cur->condition_code & 0xf]);
|
||||
debug("%s toggle: %x\n", spc, cur->toggle);
|
||||
}
|
||||
|
||||
static int
|
||||
wait_for_ed(usbdev_t *dev, ed_t *head)
|
||||
{
|
||||
td_t *cur;
|
||||
|
||||
/* wait for results */
|
||||
while (((head->head_pointer & ~3) != head->tail_pointer) &&
|
||||
!(head->head_pointer & 1) &&
|
||||
((((td_t*)phys_to_virt(head->head_pointer & ~3))->condition_code & 0xf)>=0xe)) {
|
||||
debug("intst: %x; ctrl: %x; cmdst: %x; head: %x -> %x, tail: %x, condition: %x\n",
|
||||
OHCI_INST(dev->controller)->opreg->HcInterruptStatus,
|
||||
OHCI_INST(dev->controller)->opreg->HcControl,
|
||||
OHCI_INST(dev->controller)->opreg->HcCommandStatus,
|
||||
head->head_pointer,
|
||||
((td_t*)phys_to_virt(head->head_pointer & ~3))->next_td,
|
||||
head->tail_pointer,
|
||||
((td_t*)phys_to_virt(head->head_pointer & ~3))->condition_code);
|
||||
mdelay(1);
|
||||
}
|
||||
if (OHCI_INST(dev->controller)->opreg->HcInterruptStatus & WritebackDoneHead) {
|
||||
debug("done queue:\n");
|
||||
debug("%x, %x\n", OHCI_INST(dev->controller)->hcca->HccaDoneHead, phys_to_virt(OHCI_INST(dev->controller)->hcca->HccaDoneHead));
|
||||
if ((OHCI_INST(dev->controller)->hcca->HccaDoneHead & ~1) == 0) {
|
||||
debug("HcInterruptStatus %x\n", OHCI_INST(dev->controller)->opreg->HcInterruptStatus);
|
||||
}
|
||||
td_t *done_queue = NULL;
|
||||
td_t *done_head = (td_t*)phys_to_virt(OHCI_INST(dev->controller)->hcca->HccaDoneHead);
|
||||
OHCI_INST(dev->controller)->opreg->HcInterruptStatus = WritebackDoneHead;
|
||||
while (1) {
|
||||
td_t *oldnext = (td_t*)phys_to_virt(done_head->next_td);
|
||||
if (oldnext == done_queue) break; /* last element refers to second to last, ie. endless loop */
|
||||
if (oldnext == phys_to_virt(0)) break; /* last element of done list == first element of real list */
|
||||
debug("head is %x, pointing to %x. requeueing to %x\n", done_head, oldnext, done_queue);
|
||||
done_head->next_td = (u32)done_queue;
|
||||
done_queue = done_head;
|
||||
done_head = oldnext;
|
||||
}
|
||||
for (cur = done_queue; cur != 0; cur = (td_t*)cur->next_td) {
|
||||
dump_td(cur, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (head->head_pointer & 1) {
|
||||
debug("HALTED!\n");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ohci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen,
|
||||
unsigned char *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
td_t *cur;
|
||||
|
||||
// pages are specified as 4K in OHCI, so don't use getpagesize()
|
||||
int first_page = (unsigned long)data / 4096;
|
||||
int last_page = (unsigned long)(data+dalen-1)/4096;
|
||||
if (last_page < first_page) last_page = first_page;
|
||||
int pages = (dalen==0)?0:(last_page - first_page + 1);
|
||||
int td_count = (pages+1)/2;
|
||||
|
||||
td_t *tds = memalign(sizeof(td_t), (td_count+3)*sizeof(td_t));
|
||||
memset((void*)tds, 0, (td_count+3)*sizeof(td_t));
|
||||
|
||||
for (i=0; i < td_count + 3; i++) {
|
||||
tds[i].next_td = virt_to_phys(&tds[i+1]);
|
||||
}
|
||||
tds[td_count + 3].next_td = 0;
|
||||
|
||||
tds[0].direction = OHCI_SETUP;
|
||||
tds[0].toggle_from_td = 1;
|
||||
tds[0].toggle = 0;
|
||||
tds[0].error_count = 0;
|
||||
tds[0].delay_interrupt = 7;
|
||||
tds[0].condition_code = 0xf;
|
||||
tds[0].current_buffer_pointer = virt_to_phys(devreq);
|
||||
tds[0].buffer_end = virt_to_phys(devreq + drlen - 1);
|
||||
|
||||
cur = &tds[0];
|
||||
|
||||
while (pages > 0) {
|
||||
cur++;
|
||||
cur->direction = (dir==IN)?OHCI_IN:OHCI_OUT;
|
||||
cur->toggle_from_td = 0;
|
||||
cur->toggle = 1;
|
||||
cur->error_count = 0;
|
||||
cur->delay_interrupt = 7;
|
||||
cur->condition_code = 0xf;
|
||||
cur->current_buffer_pointer = virt_to_phys(data);
|
||||
pages--;
|
||||
int consumed = (4096 - ((unsigned long)data % 4096));
|
||||
if (consumed >= dalen) {
|
||||
// end of data is within same page
|
||||
cur->buffer_end = virt_to_phys(data + dalen - 1);
|
||||
dalen = 0;
|
||||
/* assert(pages == 0); */
|
||||
} else {
|
||||
dalen -= consumed;
|
||||
data += consumed;
|
||||
pages--;
|
||||
int second_page_size = dalen;
|
||||
if (dalen > 4096) {
|
||||
second_page_size = 4096;
|
||||
}
|
||||
cur->buffer_end = virt_to_phys(data + second_page_size - 1);
|
||||
dalen -= second_page_size;
|
||||
data += second_page_size;
|
||||
}
|
||||
}
|
||||
|
||||
cur++;
|
||||
cur->direction = (dir==IN)?OHCI_OUT:OHCI_IN;
|
||||
cur->toggle_from_td = 1;
|
||||
cur->toggle = 1;
|
||||
cur->error_count = 0;
|
||||
cur->delay_interrupt = 7;
|
||||
cur->condition_code = 0xf;
|
||||
cur->current_buffer_pointer = 0;
|
||||
cur->buffer_end = 0;
|
||||
|
||||
/* final dummy TD */
|
||||
cur++;
|
||||
|
||||
/* Data structures */
|
||||
ed_t *head = memalign(sizeof(ed_t), sizeof(ed_t));
|
||||
memset((void*)head, 0, sizeof(*head));
|
||||
head->function_address = dev->address;
|
||||
head->endpoint_number = 0;
|
||||
head->direction = OHCI_FROM_TD;
|
||||
head->lowspeed = dev->speed;
|
||||
head->format = 0;
|
||||
head->maximum_packet_size = dev->endpoints[0].maxpacketsize;
|
||||
head->tail_pointer = virt_to_phys(cur);
|
||||
head->head_pointer = virt_to_phys(tds);
|
||||
head->halted = 0;
|
||||
head->toggle = 0;
|
||||
|
||||
debug("doing control transfer with %x. first_td at %x\n", head->function_address, virt_to_phys(tds));
|
||||
|
||||
/* activate schedule */
|
||||
OHCI_INST(dev->controller)->opreg->HcControlHeadED = virt_to_phys(head);
|
||||
OHCI_INST(dev->controller)->opreg->HcCommandStatus = ControlListFilled;
|
||||
|
||||
int failure = wait_for_ed(dev, head);
|
||||
|
||||
/* free memory */
|
||||
free((void*)tds);
|
||||
free((void*)head);
|
||||
|
||||
return failure;
|
||||
}
|
||||
|
||||
/* finalize == 1: if data is of packet aligned size, add a zero length packet */
|
||||
static int
|
||||
ohci_bulk (endpoint_t *ep, int dalen, u8 *data, int finalize)
|
||||
{
|
||||
int i;
|
||||
debug("bulk: %x bytes from %x, finalize: %x, maxpacketsize: %x\n", dalen, data, finalize, ep->maxpacketsize);
|
||||
|
||||
td_t *cur;
|
||||
|
||||
// pages are specified as 4K in OHCI, so don't use getpagesize()
|
||||
int first_page = (unsigned long)data / 4096;
|
||||
int last_page = (unsigned long)(data+dalen-1)/4096;
|
||||
if (last_page < first_page) last_page = first_page;
|
||||
int pages = (dalen==0)?0:(last_page - first_page + 1);
|
||||
int td_count = (pages+1)/2;
|
||||
|
||||
if (finalize && ((dalen % ep->maxpacketsize) == 0)) {
|
||||
td_count++;
|
||||
}
|
||||
|
||||
td_t *tds = memalign(sizeof(td_t), (td_count+1)*sizeof(td_t));
|
||||
memset((void*)tds, 0, (td_count+1)*sizeof(td_t));
|
||||
|
||||
for (i=0; i < td_count; i++) {
|
||||
tds[i].next_td = virt_to_phys(&tds[i+1]);
|
||||
}
|
||||
|
||||
for (cur = tds; cur->next_td != 0; cur++) {
|
||||
cur->toggle_from_td = 0;
|
||||
cur->error_count = 0;
|
||||
cur->delay_interrupt = 7;
|
||||
cur->condition_code = 0xf;
|
||||
cur->direction = (ep->direction==IN)?OHCI_IN:OHCI_OUT;
|
||||
pages--;
|
||||
if (dalen == 0) {
|
||||
/* magic TD for empty packet transfer */
|
||||
cur->current_buffer_pointer = 0;
|
||||
cur->buffer_end = 0;
|
||||
/* assert((pages == 0) && finalize); */
|
||||
}
|
||||
int consumed = (4096 - ((unsigned long)data % 4096));
|
||||
if (consumed >= dalen) {
|
||||
// end of data is within same page
|
||||
cur->buffer_end = virt_to_phys(data + dalen - 1);
|
||||
dalen = 0;
|
||||
/* assert(pages == finalize); */
|
||||
} else {
|
||||
dalen -= consumed;
|
||||
data += consumed;
|
||||
pages--;
|
||||
int second_page_size = dalen;
|
||||
if (dalen > 4096) {
|
||||
second_page_size = 4096;
|
||||
}
|
||||
cur->buffer_end = virt_to_phys(data + second_page_size - 1);
|
||||
dalen -= second_page_size;
|
||||
data += second_page_size;
|
||||
}
|
||||
}
|
||||
|
||||
/* Data structures */
|
||||
ed_t *head = memalign(sizeof(ed_t), sizeof(ed_t));
|
||||
memset((void*)head, 0, sizeof(*head));
|
||||
head->function_address = ep->dev->address;
|
||||
head->endpoint_number = ep->endpoint & 0xf;
|
||||
head->direction = (ep->direction==IN)?OHCI_IN:OHCI_OUT;
|
||||
head->lowspeed = ep->dev->speed;
|
||||
head->format = 0;
|
||||
head->maximum_packet_size = ep->maxpacketsize;
|
||||
head->tail_pointer = virt_to_phys(cur);
|
||||
head->head_pointer = virt_to_phys(tds);
|
||||
head->halted = 0;
|
||||
head->toggle = ep->toggle;
|
||||
|
||||
debug("doing bulk transfer with %x(%x). first_td at %x, last %x\n", head->function_address, head->endpoint_number, virt_to_phys(tds), virt_to_phys(cur));
|
||||
|
||||
/* activate schedule */
|
||||
OHCI_INST(ep->dev->controller)->opreg->HcBulkHeadED = virt_to_phys(head);
|
||||
OHCI_INST(ep->dev->controller)->opreg->HcCommandStatus = BulkListFilled;
|
||||
|
||||
int failure = wait_for_ed(ep->dev, head);
|
||||
|
||||
ep->toggle = head->toggle;
|
||||
|
||||
/* free memory */
|
||||
free((void*)tds);
|
||||
free((void*)head);
|
||||
|
||||
if (failure) {
|
||||
/* try cleanup */
|
||||
clear_stall(ep);
|
||||
}
|
||||
|
||||
return failure;
|
||||
}
|
||||
|
||||
/* create and hook-up an intr queue into device schedule */
|
||||
static void*
|
||||
ohci_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* remove queue from device schedule, dropping all data that came in */
|
||||
static void
|
||||
ohci_destroy_intr_queue (endpoint_t *ep, void *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*
|
||||
ohci_poll_intr_queue (void *q_)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* This file is part of the libpayload project.
|
||||
*
|
||||
* Copyright (C) 2010 Patrick Georgi
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __OHCI_H
|
||||
#define __OHCI_H
|
||||
|
||||
#include <pci.h>
|
||||
#include <usb/usb.h>
|
||||
|
||||
hci_t *ohci_init (pcidev_t addr);
|
||||
|
||||
void ohci_rh_init (usbdev_t *dev);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,255 @@
|
|||
/*
|
||||
* This file is part of the libpayload project.
|
||||
*
|
||||
* Copyright (C) 2010 Patrick Georgi
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __OHCI_PRIVATE_H
|
||||
#define __OHCI_PRIVATE_H
|
||||
|
||||
#include <pci.h>
|
||||
#include <usb/usb.h>
|
||||
|
||||
#define MASK(startbit, lenbit) (((1<<(lenbit))-1)<<(startbit))
|
||||
|
||||
// FIXME: fake
|
||||
typedef enum { CMD} reg;
|
||||
|
||||
enum {
|
||||
NumberDownstreamPorts = 1<<0,
|
||||
PowerSwitchingMode = 1<<8,
|
||||
NoPowerSwitching = 1<<9,
|
||||
DeviceType = 1<<10,
|
||||
OverCurrentProtectionMode = 1<<11,
|
||||
NoOverCurrentProtection = 1<<12,
|
||||
PowerOnToPowerGoodTime = 1<<24
|
||||
} HcRhDescriptorAReg;
|
||||
|
||||
enum {
|
||||
NumberDownstreamPortsMask = MASK(0, 8),
|
||||
PowerOnToPowerGoodTimeMask = MASK(24, 8)
|
||||
} HcRhDescriptorAMask;
|
||||
|
||||
enum {
|
||||
DeviceRemovable = 1<<0,
|
||||
PortPowerControlMask = 1<<16
|
||||
} HcRhDescriptorBReg;
|
||||
|
||||
enum {
|
||||
CurrentConnectStatus = 1<<0,
|
||||
PortEnableStatus = 1<<1,
|
||||
PortSuspendStatus = 1<<2,
|
||||
PortOverCurrentIndicator = 1<<3,
|
||||
PortResetStatus = 1<<4,
|
||||
PortPowerStatus = 1<<8,
|
||||
LowSpeedDeviceAttached = 1<<9,
|
||||
ConnectStatusChange = 1<<16,
|
||||
PortEnableStatusChange = 1<<17,
|
||||
PortSuspendStatusChange = 1<<18,
|
||||
PortOverCurrentIndicatorChange = 1<<19,
|
||||
PortResetStatusChange = 1<<20
|
||||
} HcRhPortStatusRead;
|
||||
enum {
|
||||
ClearPortEnable = 1<<0,
|
||||
SetPortEnable = 1<<1,
|
||||
SetPortSuspend = 1<<2,
|
||||
ClearSuspendStatus = 1<<3,
|
||||
SetPortReset = 1<<4,
|
||||
SetPortPower = 1<<8,
|
||||
ClearPortPower = 1<<9,
|
||||
} HcRhPortStatusSet;
|
||||
|
||||
enum {
|
||||
LocalPowerStatus = 1<<0,
|
||||
OverCurrentIndicator = 1<<1,
|
||||
DeviceRemoteWakeupEnable = 1<<15,
|
||||
LocalPowerStatusChange = 1<<16,
|
||||
OverCurrentIndicatorChange = 1<<17,
|
||||
ClearRemoteWakeupEnable = 1<<31
|
||||
} HcRhStatusReg;
|
||||
|
||||
enum {
|
||||
FrameInterval = 1<<0,
|
||||
FSLargestDataPacket = 1<<16,
|
||||
FrameIntervalToggle = 1<<31
|
||||
} HcFmIntervalOffset;
|
||||
enum {
|
||||
FrameIntervalMask = MASK(0, 14),
|
||||
FSLargestDataPacketMask = MASK(16, 15),
|
||||
FrameIntervalToggleMask = MASK(31, 1)
|
||||
} HcFmIntervalMask;
|
||||
|
||||
enum {
|
||||
ControlBulkServiceRatio = 1<<0,
|
||||
PeriodicListEnable = 1<<2,
|
||||
IsochronousEnable = 1<<3,
|
||||
ControlListEnable = 1<<4,
|
||||
BulkListEnable = 1<<5,
|
||||
HostControllerFunctionalState = 1<<6,
|
||||
InterruptRouting = 1<<8,
|
||||
RemoteWakeupConnected = 1<<9,
|
||||
RemoteWakeupEnable = 1<<10
|
||||
} HcControlReg;
|
||||
|
||||
enum {
|
||||
ControlBulkServiceRatioMask = MASK(0, 2),
|
||||
HostControllerFunctionalStateMask = MASK(6, 2)
|
||||
} HcControlMask;
|
||||
|
||||
enum {
|
||||
USBReset = 0*HostControllerFunctionalState,
|
||||
USBResume = 1*HostControllerFunctionalState,
|
||||
USBOperational = 2*HostControllerFunctionalState,
|
||||
USBSuspend = 3*HostControllerFunctionalState
|
||||
};
|
||||
|
||||
enum {
|
||||
HostControllerReset = 1<<0,
|
||||
ControlListFilled = 1<<1,
|
||||
BulkListFilled = 1<<2,
|
||||
OwnershipChangeRequest = 1<<3,
|
||||
SchedulingOverrunCount = 1<<16
|
||||
} HcCommandStatusReg;
|
||||
|
||||
enum {
|
||||
SchedulingOverrunCountMask = MASK(16, 2)
|
||||
} HcCommandStatusMask;
|
||||
|
||||
enum {
|
||||
FrameRemaining = 1<<0,
|
||||
FrameRemainingToggle = 1<<31
|
||||
} HcFmRemainingReg;
|
||||
|
||||
enum {
|
||||
SchedulingOverrung = 1<<0,
|
||||
WritebackDoneHead = 1<<1,
|
||||
StartofFrame = 1<<2,
|
||||
ResumeDetected = 1<<3,
|
||||
UnrecoverableError = 1<<4,
|
||||
FrameNumberOverflow = 1<<5,
|
||||
RootHubStatusChange = 1<<6,
|
||||
OwnershipChange = 1<<30
|
||||
} HcInterruptStatusReg;
|
||||
|
||||
typedef struct {
|
||||
// Control and Status Partition
|
||||
volatile u32 HcRevision;
|
||||
volatile u32 HcControl;
|
||||
volatile u32 HcCommandStatus;
|
||||
volatile u32 HcInterruptStatus;
|
||||
volatile u32 HcInterruptEnable;
|
||||
volatile u32 HcInterruptDisable;
|
||||
|
||||
// Memory Pointer Partition
|
||||
volatile u32 HcHCCA;
|
||||
volatile u32 HcPeriodCurrentED;
|
||||
volatile u32 HcControlHeadED;
|
||||
volatile u32 HcControlCurrentED;
|
||||
volatile u32 HcBulkHeadED;
|
||||
volatile u32 HcBulkCurrentED;
|
||||
volatile u32 HcDoneHead;
|
||||
|
||||
// Frame Counter Partition
|
||||
volatile u32 HcFmInterval;
|
||||
volatile u32 HcFmRemaining;
|
||||
volatile u32 HcFmNumber;
|
||||
volatile u32 HcPeriodicStart;
|
||||
volatile u32 HcLSThreshold;
|
||||
|
||||
// Root Hub Partition
|
||||
volatile u32 HcRhDescriptorA;
|
||||
volatile u32 HcRhDescriptorB;
|
||||
volatile u32 HcRhStatus;
|
||||
/* all bits in HcRhPortStatus registers are R/WC, so
|
||||
_DO NOT_ use |= to set the bits,
|
||||
this clears the entire state */
|
||||
volatile u32 HcRhPortStatus[];
|
||||
} __attribute__ ((packed)) opreg_t;
|
||||
|
||||
typedef struct {
|
||||
u32 HccaInterruptTable[32];
|
||||
u16 HccaFrameNumber;
|
||||
u16 HccaPad1;
|
||||
u32 HccaDoneHead;
|
||||
u8 reserved[116]; // pad to 256 byte
|
||||
} __attribute__ ((packed)) hcca_t;
|
||||
|
||||
typedef struct ohci {
|
||||
opreg_t *opreg;
|
||||
hcca_t *hcca;
|
||||
usbdev_t *roothub;
|
||||
} ohci_t;
|
||||
|
||||
typedef enum { OHCI_SETUP=0, OHCI_OUT=1, OHCI_IN=2, OHCI_FROM_TD=3 } ohci_pid_t;
|
||||
|
||||
typedef volatile struct {
|
||||
union {
|
||||
u32 dword0;
|
||||
struct {
|
||||
unsigned long function_address:7;
|
||||
unsigned long endpoint_number:4;
|
||||
unsigned long direction:2;
|
||||
unsigned long lowspeed:1;
|
||||
unsigned long skip:1;
|
||||
unsigned long format:1;
|
||||
unsigned long maximum_packet_size:11;
|
||||
unsigned long:5;
|
||||
} __attribute__ ((packed));
|
||||
};
|
||||
u32 tail_pointer;
|
||||
union {
|
||||
u32 head_pointer;
|
||||
struct {
|
||||
unsigned long halted:1;
|
||||
unsigned long toggle:1;
|
||||
unsigned long:30;
|
||||
} __attribute__ ((packed));
|
||||
};
|
||||
u32 next_ed;
|
||||
} __attribute__ ((packed)) ed_t;
|
||||
|
||||
typedef volatile struct {
|
||||
union {
|
||||
u32 dword0;
|
||||
struct {
|
||||
unsigned long:18;
|
||||
unsigned long buffer_rounding:1;
|
||||
unsigned long direction:2;
|
||||
unsigned long delay_interrupt:3;
|
||||
unsigned long toggle:1;
|
||||
unsigned long toggle_from_td:1;
|
||||
unsigned long error_count:2;
|
||||
unsigned long condition_code:4;
|
||||
} __attribute__ ((packed));
|
||||
};
|
||||
u32 current_buffer_pointer;
|
||||
u32 next_td;
|
||||
u32 buffer_end;
|
||||
} __attribute__ ((packed)) td_t;
|
||||
|
||||
#define OHCI_INST(controller) ((ohci_t*)((controller)->instance))
|
||||
|
||||
#endif
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* This file is part of the libpayload project.
|
||||
*
|
||||
* Copyright (C) 2010 Patrick Georgi
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
//#define USB_DEBUG
|
||||
|
||||
#include <libpayload.h>
|
||||
#include "ohci_private.h"
|
||||
#include "ohci.h"
|
||||
|
||||
typedef struct {
|
||||
int numports;
|
||||
int *port;
|
||||
} rh_inst_t;
|
||||
|
||||
#define RH_INST(dev) ((rh_inst_t*)(dev)->data)
|
||||
|
||||
static void
|
||||
ohci_rh_enable_port (usbdev_t *dev, int port)
|
||||
{
|
||||
if (!(OHCI_INST(dev->controller)->opreg->HcRhPortStatus[port] & CurrentConnectStatus))
|
||||
return;
|
||||
|
||||
OHCI_INST (dev->controller)->opreg->HcRhPortStatus[port] = SetPortEnable; // enable port
|
||||
mdelay(10);
|
||||
while (!(OHCI_INST (dev->controller)->opreg->HcRhPortStatus[port] & PortEnableStatus)) mdelay(1);
|
||||
OHCI_INST (dev->controller)->opreg->HcRhPortStatus[port] = SetPortReset; // reset port
|
||||
while (OHCI_INST (dev->controller)->opreg->HcRhPortStatus[port] & PortResetStatus) mdelay(1);
|
||||
OHCI_INST (dev->controller)->opreg->HcRhPortStatus[port] = PortResetStatusChange;
|
||||
}
|
||||
|
||||
/* disable root hub */
|
||||
static void
|
||||
ohci_rh_disable_port (usbdev_t *dev, int port)
|
||||
{
|
||||
OHCI_INST (dev->controller)->opreg->HcRhPortStatus[port] = ClearPortEnable; // disable port
|
||||
while (OHCI_INST (dev->controller)->opreg->HcRhPortStatus[port] & PortEnableStatus) mdelay(1);
|
||||
}
|
||||
|
||||
static void
|
||||
ohci_rh_scanport (usbdev_t *dev, int port)
|
||||
{
|
||||
if (port >= RH_INST(dev)->numports) {
|
||||
debug("Invalid port %d\n", port);
|
||||
return;
|
||||
}
|
||||
|
||||
/* device registered, and device change logged, so something must have happened */
|
||||
if (RH_INST (dev)->port[port] != -1) {
|
||||
usb_detach_device(dev->controller, RH_INST (dev)->port[port]);
|
||||
RH_INST (dev)->port[port] = -1;
|
||||
}
|
||||
|
||||
/* no device attached
|
||||
previously registered devices are detached, nothing left to do */
|
||||
if (!(OHCI_INST(dev->controller)->opreg->HcRhPortStatus[port] & CurrentConnectStatus))
|
||||
return;
|
||||
|
||||
OHCI_INST (dev->controller)->opreg->HcRhPortStatus[port] = ConnectStatusChange; // clear port state change
|
||||
ohci_rh_enable_port (dev, port);
|
||||
|
||||
mdelay(100); // wait for signal to stabilize
|
||||
|
||||
if (!(OHCI_INST(dev->controller)->opreg->HcRhPortStatus[port] & PortEnableStatus)) {
|
||||
debug ("port enable failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
int speed = (OHCI_INST(dev->controller)->opreg->HcRhPortStatus[port] & LowSpeedDeviceAttached) != 0;
|
||||
RH_INST (dev)->port[port] = usb_attach_device(dev->controller, dev->address, port, speed);
|
||||
}
|
||||
|
||||
static int
|
||||
ohci_rh_report_port_changes (usbdev_t *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!(OHCI_INST (dev->controller)->opreg->HcInterruptStatus & RootHubStatusChange)) return -1;
|
||||
OHCI_INST (dev->controller)->opreg->HcInterruptStatus = RootHubStatusChange;
|
||||
debug("port change\n");
|
||||
|
||||
for (i = 0; i < RH_INST(dev)->numports; i++) {
|
||||
// maybe detach+attach happened between two scans?
|
||||
if (OHCI_INST (dev->controller)->opreg->HcRhPortStatus[i] & ConnectStatusChange) {
|
||||
debug("attachment change on port %d\n", i);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
// no change
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
ohci_rh_destroy (usbdev_t *dev)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < RH_INST (dev)->numports; i++)
|
||||
ohci_rh_disable_port (dev, i);
|
||||
free (RH_INST (dev));
|
||||
}
|
||||
|
||||
static void
|
||||
ohci_rh_poll (usbdev_t *dev)
|
||||
{
|
||||
int port;
|
||||
while ((port = ohci_rh_report_port_changes (dev)) != -1)
|
||||
ohci_rh_scanport (dev, port);
|
||||
}
|
||||
|
||||
void
|
||||
ohci_rh_init (usbdev_t *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
dev->destroy = ohci_rh_destroy;
|
||||
dev->poll = ohci_rh_poll;
|
||||
|
||||
dev->data = malloc (sizeof (rh_inst_t));
|
||||
if (!dev->data)
|
||||
usb_fatal ("Not enough memory for OHCI RH.\n");
|
||||
|
||||
RH_INST (dev)->numports = OHCI_INST (dev->controller)->opreg->HcRhDescriptorA & NumberDownstreamPortsMask;
|
||||
RH_INST (dev)->port = malloc(sizeof(int) * RH_INST (dev)->numports);
|
||||
debug("%d ports registered\n", RH_INST (dev)->numports);
|
||||
|
||||
for (i = 0; i < RH_INST (dev)->numports; i++) {
|
||||
ohci_rh_enable_port (dev, i);
|
||||
RH_INST (dev)->port[i] = -1;
|
||||
}
|
||||
|
||||
/* we can set them here because a root hub _really_ shouldn't
|
||||
appear elsewhere */
|
||||
dev->address = 0;
|
||||
dev->hub = -1;
|
||||
dev->port = -1;
|
||||
|
||||
debug("rh init done\n");
|
||||
}
|
|
@ -30,9 +30,9 @@
|
|||
#include <libpayload-config.h>
|
||||
#include <usb/usb.h>
|
||||
#include "uhci.h"
|
||||
//#include "ohci.h"
|
||||
#include "ohci.h"
|
||||
//#include "ehci.h"
|
||||
//#include "xhci.h"
|
||||
#include "xhci.h"
|
||||
#include <usb/usbdisk.h>
|
||||
|
||||
/**
|
||||
|
@ -68,7 +68,7 @@ usb_controller_initialize (int bus, int dev, int func)
|
|||
pci_command |= PCI_COMMAND_MASTER;
|
||||
pci_write_config32(addr, PCI_COMMAND, pci_command);
|
||||
|
||||
printf ("%02x:%02x.%x %04x:%04x.%d ", 0, dev, func,
|
||||
printf ("%02x:%02x.%x %04x:%04x.%d ", bus, dev, func,
|
||||
pciid >> 16, pciid & 0xFFFF, func);
|
||||
if (prog_if == 0) {
|
||||
printf ("UHCI controller\n");
|
||||
|
@ -81,8 +81,7 @@ usb_controller_initialize (int bus, int dev, int func)
|
|||
if (prog_if == 0x10) {
|
||||
printf ("OHCI controller\n");
|
||||
#ifdef CONFIG_USB_OHCI
|
||||
//ohci_init(addr);
|
||||
printf ("Not supported.\n");
|
||||
ohci_init(addr);
|
||||
#else
|
||||
printf ("Not supported.\n");
|
||||
#endif
|
||||
|
@ -99,10 +98,9 @@ usb_controller_initialize (int bus, int dev, int func)
|
|||
|
||||
}
|
||||
if (prog_if == 0x30) {
|
||||
printf ("XHCI controller\n");
|
||||
printf ("xHCI controller\n");
|
||||
#ifdef CONFIG_USB_XHCI
|
||||
//xhci_init(addr);
|
||||
printf ("Not supported.\n");
|
||||
xhci_init(addr);
|
||||
#else
|
||||
printf ("Not supported.\n");
|
||||
#endif
|
||||
|
@ -128,8 +126,9 @@ usb_initialize (void)
|
|||
*/
|
||||
for (bus = 0; bus < 256; bus++)
|
||||
for (dev = 0; dev < 32; dev++)
|
||||
for (func = 7; func >= 0 ; func--)
|
||||
usb_controller_initialize (bus, dev, func);
|
||||
if (pci_read_config32 (PCI_DEV(bus, dev, 0), 8) >> 16 == 0x0c03)
|
||||
for (func = 7; func >= 0 ; func--)
|
||||
usb_controller_initialize (bus, dev, func);
|
||||
usb_poll();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,262 @@
|
|||
/*
|
||||
* This file is part of the libpayload project.
|
||||
*
|
||||
* Copyright (C) 2010 Patrick Georgi
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define USB_DEBUG
|
||||
|
||||
#include <arch/virtual.h>
|
||||
#include "xhci.h"
|
||||
#include "xhci_private.h"
|
||||
|
||||
static void xhci_start (hci_t *controller);
|
||||
static void xhci_stop (hci_t *controller);
|
||||
static void xhci_reset (hci_t *controller);
|
||||
static void xhci_shutdown (hci_t *controller);
|
||||
static int xhci_bulk (endpoint_t *ep, int size, u8 *data, int finalize);
|
||||
static int xhci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq,
|
||||
int dalen, u8 *data);
|
||||
static void* xhci_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming);
|
||||
static void xhci_destroy_intr_queue (endpoint_t *ep, void *queue);
|
||||
static u8* xhci_poll_intr_queue (void *queue);
|
||||
|
||||
static void
|
||||
xhci_reset (hci_t *controller)
|
||||
{
|
||||
}
|
||||
|
||||
hci_t *
|
||||
xhci_init (pcidev_t addr)
|
||||
{
|
||||
int i;
|
||||
|
||||
hci_t *controller = new_controller ();
|
||||
|
||||
if (!controller)
|
||||
usb_fatal("Could not create USB controller instance.\n");
|
||||
|
||||
controller->instance = malloc (sizeof (xhci_t));
|
||||
if(!controller->instance)
|
||||
usb_fatal("Not enough memory creating USB controller instance.\n");
|
||||
|
||||
controller->start = xhci_start;
|
||||
controller->stop = xhci_stop;
|
||||
controller->reset = xhci_reset;
|
||||
controller->shutdown = xhci_shutdown;
|
||||
controller->bulk = xhci_bulk;
|
||||
controller->control = xhci_control;
|
||||
controller->create_intr_queue = xhci_create_intr_queue;
|
||||
controller->destroy_intr_queue = xhci_destroy_intr_queue;
|
||||
controller->poll_intr_queue = xhci_poll_intr_queue;
|
||||
for (i = 0; i < 128; i++) {
|
||||
controller->devices[i] = 0;
|
||||
}
|
||||
init_device_entry (controller, 0);
|
||||
XHCI_INST (controller)->roothub = controller->devices[0];
|
||||
|
||||
controller->bus_address = addr;
|
||||
controller->reg_base = (u32)phys_to_virt(pci_read_config32 (controller->bus_address, 0x10) & ~0xf);
|
||||
//controller->reg_base = pci_read_config32 (controller->bus_address, 0x14) & ~0xf;
|
||||
if (pci_read_config32 (controller->bus_address, 0x14) > 0) {
|
||||
usb_fatal("We don't do 64bit addressing.\n");
|
||||
}
|
||||
debug("regbase: %lx\n", controller->reg_base);
|
||||
|
||||
XHCI_INST (controller)->capreg = (void*)controller->reg_base;
|
||||
XHCI_INST (controller)->opreg = (void*)(controller->reg_base + XHCI_INST (controller)->capreg->caplength);
|
||||
XHCI_INST (controller)->hcrreg = (void*)(controller->reg_base + XHCI_INST (controller)->capreg->rtsoff);
|
||||
XHCI_INST (controller)->dbreg = (void*)(controller->reg_base + XHCI_INST (controller)->capreg->dboff);
|
||||
debug("caplen: %lx\nrtsoff: %lx\ndboff: %lx\n", XHCI_INST (controller)->capreg->caplength, XHCI_INST (controller)->capreg->rtsoff, XHCI_INST (controller)->capreg->dboff);
|
||||
debug("caplength: %x\n", XHCI_INST (controller)->capreg->caplength);
|
||||
debug("hciversion: %x.%x\n", XHCI_INST (controller)->capreg->hciver_hi, XHCI_INST (controller)->capreg->hciver_lo);
|
||||
if ((XHCI_INST (controller)->capreg->hciversion < 0x96) || (XHCI_INST (controller)->capreg->hciversion > 0x100)) {
|
||||
usb_fatal("Unsupported xHCI version\n");
|
||||
}
|
||||
debug("maxslots: %x\n", XHCI_INST (controller)->capreg->MaxSlots);
|
||||
debug("maxports: %x\n", XHCI_INST (controller)->capreg->MaxPorts);
|
||||
int pagesize = XHCI_INST (controller)->opreg->pagesize << 12;
|
||||
debug("pagesize: %x\n", pagesize);
|
||||
|
||||
XHCI_INST (controller)->dcbaa = memalign(64, (XHCI_INST (controller)->capreg->MaxSlots+1)*sizeof(devctxp_t));
|
||||
memset((void*)XHCI_INST (controller)->dcbaa, 0, (XHCI_INST (controller)->capreg->MaxSlots+1)*sizeof(devctxp_t));
|
||||
|
||||
debug("max scratchpad bufs: %x\n", XHCI_INST (controller)->capreg->Max_Scratchpad_Bufs);
|
||||
if (XHCI_INST (controller)->capreg->Max_Scratchpad_Bufs > 0) {
|
||||
XHCI_INST (controller)->dcbaa->ptr = memalign(64, XHCI_INST (controller)->capreg->Max_Scratchpad_Bufs * 8);
|
||||
}
|
||||
|
||||
XHCI_INST (controller)->opreg->dcbaap_lo = virt_to_phys(XHCI_INST (controller)->dcbaa);
|
||||
XHCI_INST (controller)->opreg->dcbaap_hi = 0;
|
||||
|
||||
printf("waiting for controller to be ready - ");
|
||||
while ((XHCI_INST (controller)->opreg->usbsts & USBSTS_CNR) != 0) mdelay(1);
|
||||
printf("ok.\n");
|
||||
|
||||
debug("ERST Max: %lx -> %lx entries\n", XHCI_INST (controller)->capreg->ERST_Max, 1<<(XHCI_INST (controller)->capreg->ERST_Max));
|
||||
|
||||
// enable all available slots
|
||||
XHCI_INST (controller)->opreg->config = XHCI_INST (controller)->capreg->MaxSlots & CONFIG_MASK_MaxSlotsEn;
|
||||
|
||||
XHCI_INST (controller)->cmd_ring = memalign(64, 16*sizeof(trb_t)); /* TODO: make sure not to cross 64k page boundary */
|
||||
memset((void*)XHCI_INST (controller)->cmd_ring, 0, 16*sizeof(trb_t));
|
||||
|
||||
XHCI_INST (controller)->ev_ring = memalign(64, 16*sizeof(trb_t)); /* TODO: make sure not to cross 64k page boundary */
|
||||
memset((void*)XHCI_INST (controller)->ev_ring, 0, 16*sizeof(trb_t));
|
||||
|
||||
XHCI_INST (controller)->ev_ring_table = memalign(64, sizeof(erst_entry_t));
|
||||
memset((void*)XHCI_INST (controller)->ev_ring_table, 0, sizeof(erst_entry_t));
|
||||
XHCI_INST (controller)->ev_ring_table[0].seg_base_lo = virt_to_phys(XHCI_INST (controller)->ev_ring);
|
||||
XHCI_INST (controller)->ev_ring_table[0].seg_base_hi = 0;
|
||||
XHCI_INST (controller)->ev_ring_table[0].seg_size = 16;
|
||||
|
||||
// init command ring
|
||||
XHCI_INST (controller)->opreg->crcr_lo = virt_to_phys(XHCI_INST (controller)->cmd_ring) | CRCR_RCS;
|
||||
XHCI_INST (controller)->opreg->crcr_hi = 0;
|
||||
XHCI_INST (controller)->cmd_ccs = 1;
|
||||
XHCI_INST (controller)->ev_ccs = 1;
|
||||
|
||||
// init primary interrupter
|
||||
XHCI_INST (controller)->hcrreg->intrrs[0].erstsz = 1;
|
||||
XHCI_INST (controller)->hcrreg->intrrs[0].erdp_lo = virt_to_phys(XHCI_INST (controller)->ev_ring);
|
||||
XHCI_INST (controller)->hcrreg->intrrs[0].erdp_hi = 0;
|
||||
XHCI_INST (controller)->hcrreg->intrrs[0].erstba_lo = virt_to_phys(XHCI_INST (controller)->ev_ring_table);
|
||||
XHCI_INST (controller)->hcrreg->intrrs[0].erstba_hi = 0;
|
||||
|
||||
XHCI_INST (controller)->opreg->usbcmd |= USBCMD_RS; /* start USB controller */
|
||||
XHCI_INST (controller)->dbreg[0] = 0; // and tell controller to consume commands
|
||||
|
||||
/* TODO: TEST */
|
||||
// setup noop command
|
||||
trb_t *cmd = &XHCI_INST (controller)->cmd_ring[0];
|
||||
((u32*)cmd)[3] = 1-XHCI_INST (controller)->cmd_ccs; // disable command descriptor
|
||||
((u32*)cmd)[0] = 0;
|
||||
((u32*)cmd)[1] = 0;
|
||||
((u32*)cmd)[2] = 0;
|
||||
cmd->cmd_No_Op.TRB_Type = TRB_CMD_NOOP;
|
||||
|
||||
// ring the HC doorbell
|
||||
debug("Posting command at %lx\n", virt_to_phys(cmd));
|
||||
cmd->cmd_No_Op.C = XHCI_INST (controller)->cmd_ccs; // enable command
|
||||
XHCI_INST (controller)->dbreg[0] = 0; // and tell controller to consume commands
|
||||
|
||||
// wait for result in event ring
|
||||
trb_t *ev = &XHCI_INST (controller)->ev_ring[0];
|
||||
trb_t *ev1 = &XHCI_INST (controller)->ev_ring[1];
|
||||
while (ev->event_cmd_cmpl.C != XHCI_INST (controller)->ev_ccs) {
|
||||
debug("CRCR: %lx, USBSTS: %lx\n", XHCI_INST (controller)->opreg->crcr_lo, XHCI_INST (controller)->opreg->usbsts);
|
||||
debug("ev0.C %x, ev1.C %x\n", ev->event_cmd_cmpl.C, ev1->event_cmd_cmpl.C);
|
||||
mdelay(100);
|
||||
}
|
||||
debug("command ring is %srunning\n", (XHCI_INST (controller)->opreg->crcr_lo & CRCR_CRR)?"":"not ");
|
||||
switch (ev->event_cmd_cmpl.TRB_Type) {
|
||||
case TRB_EV_CMD_CMPL:
|
||||
debug("Completed command TRB at %lx. Code: %d\n",
|
||||
ev->event_cmd_cmpl.Cmd_TRB_Pointer_lo, ev->event_cmd_cmpl.Completion_Code);
|
||||
break;
|
||||
case TRB_EV_PORTSC:
|
||||
debug("Port Status Change Event. Completion Code: %d\n Port: %d. Ignoring.\n",
|
||||
ev->event_cmd_cmpl.Completion_Code, ev->event_portsc.Port);
|
||||
// we ignore the event as we look for the PORTSC registers instead, at a time when it suits _us_
|
||||
break;
|
||||
default:
|
||||
debug("Unknown event: %d, Completion Code: %d\n", ev->event_cmd_cmpl.TRB_Type, ev->event_cmd_cmpl.Completion_Code);
|
||||
break;
|
||||
}
|
||||
debug("CRCR: %lx, USBSTS: %lx\n", XHCI_INST (controller)->opreg->crcr_lo, XHCI_INST (controller)->opreg->usbsts);
|
||||
debug("ev0.C %x, ev1.C %x, ev1.CC %d\n", ev->event_cmd_cmpl.C, ev1->event_cmd_cmpl.C, ev1->event_cmd_cmpl.Completion_Code);
|
||||
|
||||
controller->devices[0]->controller = controller;
|
||||
controller->devices[0]->init = xhci_rh_init;
|
||||
controller->devices[0]->init (controller->devices[0]);
|
||||
|
||||
xhci_reset (controller);
|
||||
return controller;
|
||||
}
|
||||
|
||||
static void
|
||||
xhci_shutdown (hci_t *controller)
|
||||
{
|
||||
if (controller == 0)
|
||||
return;
|
||||
detach_controller (controller);
|
||||
XHCI_INST (controller)->roothub->destroy (XHCI_INST (controller)->
|
||||
roothub);
|
||||
/* TODO: stop hardware, kill data structures */
|
||||
free (XHCI_INST (controller));
|
||||
free (controller);
|
||||
}
|
||||
|
||||
static void
|
||||
xhci_start (hci_t *controller)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
xhci_stop (hci_t *controller)
|
||||
{
|
||||
}
|
||||
|
||||
static int
|
||||
xhci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen,
|
||||
unsigned char *data)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* finalize == 1: if data is of packet aligned size, add a zero length packet */
|
||||
static int
|
||||
xhci_bulk (endpoint_t *ep, int size, u8 *data, int finalize)
|
||||
{
|
||||
int maxpsize = ep->maxpacketsize;
|
||||
if (maxpsize == 0)
|
||||
usb_fatal ("MaxPacketSize == 0!!!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* create and hook-up an intr queue into device schedule */
|
||||
static void*
|
||||
xhci_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* remove queue from device schedule, dropping all data that came in */
|
||||
static void
|
||||
xhci_destroy_intr_queue (endpoint_t *ep, void *q_)
|
||||
{
|
||||
//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*
|
||||
xhci_poll_intr_queue (void *q_)
|
||||
{
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* This file is part of the libpayload project.
|
||||
*
|
||||
* Copyright (C) 2010 Patrick Georgi
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __XHCI_H
|
||||
#define __XHCI_H
|
||||
|
||||
#include <pci.h>
|
||||
#include <usb/usb.h>
|
||||
|
||||
hci_t *xhci_init (pcidev_t addr);
|
||||
|
||||
void xhci_rh_init (usbdev_t *dev);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,350 @@
|
|||
/*
|
||||
* This file is part of the libpayload project.
|
||||
*
|
||||
* Copyright (C) 2010 Patrick Georgi
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __XHCI_PRIVATE_H
|
||||
#define __XHCI_PRIVATE_H
|
||||
|
||||
#include <usb/usb.h>
|
||||
|
||||
#define MASK(startbit, lenbit) (((1<<(lenbit))-1)<<(startbit))
|
||||
|
||||
typedef volatile union trb {
|
||||
// transfer
|
||||
|
||||
// events
|
||||
#define TRB_EV_CMD_CMPL 33
|
||||
struct {
|
||||
u32 Cmd_TRB_Pointer_lo;
|
||||
u32 Cmd_TRB_Pointer_hi;
|
||||
struct {
|
||||
unsigned long:24;
|
||||
unsigned long Completion_Code:8;
|
||||
} __attribute__ ((packed));
|
||||
struct {
|
||||
unsigned long C:1;
|
||||
unsigned long:9;
|
||||
unsigned long TRB_Type:6;
|
||||
unsigned long VF_ID:8;
|
||||
unsigned long Slot_ID:8;
|
||||
} __attribute__ ((packed));
|
||||
} __attribute__ ((packed)) event_cmd_cmpl;
|
||||
|
||||
#define TRB_EV_PORTSC 34
|
||||
struct {
|
||||
struct {
|
||||
unsigned long:24;
|
||||
unsigned long Port:8;
|
||||
} __attribute__ ((packed));
|
||||
u32 rsvd;
|
||||
struct {
|
||||
unsigned long:24;
|
||||
unsigned long Completion_Code:8;
|
||||
} __attribute__ ((packed));
|
||||
struct {
|
||||
unsigned long C:1;
|
||||
unsigned long:9;
|
||||
unsigned long TRB_Type:6;
|
||||
unsigned long:16;
|
||||
} __attribute__ ((packed));
|
||||
} __attribute__ ((packed)) event_portsc;
|
||||
|
||||
// commands
|
||||
#define TRB_CMD_NOOP 23
|
||||
struct {
|
||||
u32 rsvd[3];
|
||||
struct {
|
||||
unsigned long C:1;
|
||||
unsigned long:9;
|
||||
unsigned long TRB_Type:6;
|
||||
unsigned long:16;
|
||||
} __attribute__ ((packed));
|
||||
} __attribute__ ((packed)) cmd_No_Op;
|
||||
|
||||
// "others"
|
||||
struct {
|
||||
u32 Ring_Segment_Ptr_lo;
|
||||
u32 Ring_Segment_Ptr_hi;
|
||||
struct {
|
||||
unsigned long:22;
|
||||
unsigned long Interrupter_Target;
|
||||
} __attribute__ ((packed));
|
||||
struct {
|
||||
unsigned long C:1;
|
||||
unsigned long TC:1;
|
||||
unsigned long:2;
|
||||
unsigned long CH:1;
|
||||
unsigned long IOC:1;
|
||||
unsigned long:4;
|
||||
unsigned long TRB_Type:6;
|
||||
unsigned long:16;
|
||||
} __attribute__ ((packed));
|
||||
} __attribute__ ((packed)) link;
|
||||
} trb_t;
|
||||
|
||||
typedef struct slotctx {
|
||||
struct {
|
||||
unsigned long Route_String:20;
|
||||
unsigned long Speed:4;
|
||||
unsigned long:1;
|
||||
unsigned long MTT:1;
|
||||
unsigned long Hub:1;
|
||||
unsigned long Context_Entries:5;
|
||||
} __attribute__ ((packed));
|
||||
struct {
|
||||
unsigned long Max_Exit_Latency:16;
|
||||
unsigned long Root_Hub_Port_Number:8;
|
||||
unsigned long Number_of_Ports:8;
|
||||
} __attribute__ ((packed));
|
||||
struct {
|
||||
unsigned long TT_Hub_Slot_ID:8;
|
||||
unsigned long TT_Port_Number:8;
|
||||
unsigned long TTT:2;
|
||||
unsigned long:4;
|
||||
unsigned long Interrupter_Target:10;
|
||||
} __attribute__ ((packed));
|
||||
struct {
|
||||
unsigned long USB_Device_Address:8;
|
||||
unsigned long:19;
|
||||
unsigned long Slot_State:5;
|
||||
} __attribute__ ((packed));
|
||||
u32 rsvd[4];
|
||||
} slotctx_t;
|
||||
|
||||
typedef struct epctx {
|
||||
struct {
|
||||
unsigned long EP_State:3;
|
||||
unsigned long:5;
|
||||
unsigned long Mult:2;
|
||||
unsigned long MaxPStreams:5;
|
||||
unsigned long LSA:1;
|
||||
unsigned long Interval:8;
|
||||
unsigned long:8;
|
||||
} __attribute__ ((packed));
|
||||
struct {
|
||||
unsigned long:1;
|
||||
unsigned long CErr:2;
|
||||
unsigned long EP_Type:3;
|
||||
unsigned long:1;
|
||||
unsigned long HID:1;
|
||||
unsigned long Max_Burst_Size:8;
|
||||
unsigned long Max_Packet_Size:16;
|
||||
} __attribute__ ((packed));
|
||||
union {
|
||||
u32 TR_Dequeue_Pointer_lo;
|
||||
struct {
|
||||
unsigned long DCS:1;
|
||||
unsigned long:3;
|
||||
} __attribute__ ((packed));
|
||||
} __attribute__ ((packed));
|
||||
u32 TR_Dequeue_Pointer_hi;
|
||||
struct {
|
||||
unsigned long Average_TRB_Length:16;
|
||||
unsigned long Max_ESIT_Payload:16;
|
||||
} __attribute__ ((packed));
|
||||
u32 rsvd[3];
|
||||
} epctx_t;
|
||||
|
||||
typedef struct devctx {
|
||||
slotctx_t slot;
|
||||
epctx_t ep0;
|
||||
struct {
|
||||
epctx_t out;
|
||||
epctx_t in;
|
||||
} eps[15];
|
||||
} devctx_t;
|
||||
|
||||
typedef struct devctxp {
|
||||
devctx_t *ptr;
|
||||
void *upper;
|
||||
} devctxp_t;
|
||||
|
||||
typedef struct erst_entry {
|
||||
u32 seg_base_lo;
|
||||
u32 seg_base_hi;
|
||||
u32 seg_size;
|
||||
u32 rsvd;
|
||||
} erst_entry_t;
|
||||
|
||||
typedef struct xhci {
|
||||
/* capreg is read-only, so no need for volatile,
|
||||
and thus 32bit accesses can be assumed. */
|
||||
struct capreg {
|
||||
u8 caplength;
|
||||
u8 res1;
|
||||
union {
|
||||
u16 hciversion;
|
||||
struct {
|
||||
u8 hciver_lo;
|
||||
u8 hciver_hi;
|
||||
} __attribute__ ((packed));
|
||||
} __attribute__ ((packed));
|
||||
union {
|
||||
u32 hcsparams1;
|
||||
struct {
|
||||
unsigned long MaxSlots:7;
|
||||
unsigned long MaxIntrs:11;
|
||||
unsigned long:6;
|
||||
unsigned long MaxPorts:8;
|
||||
} __attribute__ ((packed));
|
||||
} __attribute__ ((packed));
|
||||
union {
|
||||
u32 hcsparams2;
|
||||
struct {
|
||||
unsigned long IST:4;
|
||||
unsigned long ERST_Max:4;
|
||||
unsigned long:18;
|
||||
unsigned long SPR:1;
|
||||
unsigned long Max_Scratchpad_Bufs:5;
|
||||
} __attribute__ ((packed));
|
||||
} __attribute__ ((packed));
|
||||
union {
|
||||
u32 hcsparams3;
|
||||
struct {
|
||||
unsigned long u1latency:8;
|
||||
unsigned long:8;
|
||||
unsigned long u2latency:16;
|
||||
} __attribute__ ((packed));
|
||||
} __attribute__ ((packed));
|
||||
union {
|
||||
u32 hccparams;
|
||||
struct {
|
||||
unsigned long ac64:1;
|
||||
unsigned long bnc:1;
|
||||
unsigned long csz:1;
|
||||
unsigned long ppc:1;
|
||||
unsigned long pind:1;
|
||||
unsigned long lhrc:1;
|
||||
unsigned long ltc:1;
|
||||
unsigned long nss:1;
|
||||
unsigned long:4;
|
||||
unsigned long MaxPSASize:4;
|
||||
unsigned long xECP:16;
|
||||
} __attribute__ ((packed));
|
||||
} __attribute__ ((packed));
|
||||
u32 dboff;
|
||||
u32 rtsoff;
|
||||
} __attribute__ ((packed)) *capreg;
|
||||
|
||||
/* opreg is R/W is most places, so volatile access is necessary.
|
||||
volatile means that the compiler seeks byte writes if possible,
|
||||
making bitfields unusable for MMIO register blocks. Yay C :-( */
|
||||
volatile struct opreg {
|
||||
u32 usbcmd;
|
||||
#define USBCMD_RS 1<<0
|
||||
#define USBCMD_HCRST 1<<1
|
||||
u32 usbsts;
|
||||
#define USBSTS_HCH 1<<0
|
||||
#define USBSTS_HSE 1<<2
|
||||
#define USBSTS_EINT 1<<3
|
||||
#define USBSTS_PCD 1<<4
|
||||
#define USBSTS_CNR 1<<11
|
||||
u32 pagesize;
|
||||
u8 res1[0x13-0x0c+1];
|
||||
u32 dnctrl;
|
||||
u32 crcr_lo;
|
||||
u32 crcr_hi;
|
||||
#define CRCR_RCS 1<<0
|
||||
#define CRCR_CS 1<<1
|
||||
#define CRCR_CA 1<<2
|
||||
#define CRCR_CRR 1<<3
|
||||
u8 res2[0x2f-0x20+1];
|
||||
u32 dcbaap_lo;
|
||||
u32 dcbaap_hi;
|
||||
u32 config;
|
||||
#define CONFIG_MASK_MaxSlotsEn 0xff
|
||||
u8 res3[0x3ff-0x3c+1];
|
||||
struct {
|
||||
u32 portsc;
|
||||
#define PORTSC_CCS 1<<0
|
||||
#define PORTSC_PED 1<<1
|
||||
// BIT 2 rsvdZ
|
||||
#define PORTSC_OCA 1<<3
|
||||
#define PORTSC_PR 1<<4
|
||||
#define PORTSC_PLS 1<<5
|
||||
#define PORTSC_PLS_MASK MASK(5, 4)
|
||||
#define PORTSC_PP 1<<9
|
||||
#define PORTSC_PORT_SPEED 1<<10
|
||||
#define PORTSC_PORT_SPEED_MASK MASK(10, 4)
|
||||
#define PORTSC_PIC 1<<14
|
||||
#define PORTSC_PIC_MASK MASK(14, 2)
|
||||
#define PORTSC_LWS 1<<16
|
||||
#define PORTSC_CSC 1<<17
|
||||
#define PORTSC_PEC 1<<18
|
||||
#define PORTSC_WRC 1<<19
|
||||
#define PORTSC_OCC 1<<20
|
||||
#define PORTSC_PRC 1<<21
|
||||
#define PORTSC_PLC 1<<22
|
||||
#define PORTSC_CEC 1<<23
|
||||
#define PORTSC_CAS 1<<24
|
||||
#define PORTSC_WCE 1<<25
|
||||
#define PORTSC_WDE 1<<26
|
||||
#define PORTSC_WOE 1<<27
|
||||
// BIT 29:28 rsvdZ
|
||||
#define PORTSC_DR 1<<30
|
||||
#define PORTSC_WPR 1<<31
|
||||
#define PORTSC_RW_MASK PORTSC_PR | PORTSC_PLS_MASK | PORTSC_PP | PORTSC_PIC_MASK | PORTSC_LWS | PORTSC_WCE | PORTSC_WDE | PORTSC_WOE
|
||||
u32 portpmsc;
|
||||
u32 portli;
|
||||
u32 res;
|
||||
} __attribute__ ((packed)) prs[];
|
||||
} __attribute__ ((packed)) *opreg;
|
||||
|
||||
/* R/W, volatile, MMIO -> no bitfields */
|
||||
volatile struct hcrreg {
|
||||
u32 mfindex;
|
||||
u8 res1[0x20-0x4];
|
||||
struct {
|
||||
u32 iman;
|
||||
u32 imod;
|
||||
u32 erstsz;
|
||||
u32 res;
|
||||
u32 erstba_lo;
|
||||
u32 erstba_hi;
|
||||
u32 erdp_lo;
|
||||
u32 erdp_hi;
|
||||
} __attribute__ ((packed)) intrrs[]; // up to 1024, but maximum host specific, given in capreg->MaxIntrs
|
||||
} __attribute__ ((packed)) *hcrreg;
|
||||
|
||||
/* R/W, volatile, MMIO -> no bitfields */
|
||||
volatile u32 *dbreg;
|
||||
|
||||
/* R/W, volatile, Memory -> bitfields allowed */
|
||||
volatile devctxp_t *dcbaa;
|
||||
|
||||
trb_t *cmd_ring;
|
||||
trb_t *ev_ring;
|
||||
volatile erst_entry_t *ev_ring_table;
|
||||
int cmd_ccs, ev_ccs;
|
||||
|
||||
usbdev_t *roothub;
|
||||
} xhci_t;
|
||||
|
||||
#define XHCI_INST(controller) ((xhci_t*)((controller)->instance))
|
||||
|
||||
#endif
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* This file is part of the libpayload project.
|
||||
*
|
||||
* Copyright (C) 2010 Patrick Georgi
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define USB_DEBUG
|
||||
|
||||
#include <libpayload.h>
|
||||
#include "xhci_private.h"
|
||||
#include "xhci.h"
|
||||
|
||||
typedef struct {
|
||||
int numports;
|
||||
int *port;
|
||||
} rh_inst_t;
|
||||
|
||||
#define RH_INST(dev) ((rh_inst_t*)(dev)->data)
|
||||
|
||||
static void
|
||||
xhci_rh_enable_port (usbdev_t *dev, int port)
|
||||
{
|
||||
// FIXME: check power situation?
|
||||
// enable slot
|
||||
// attach device context to slot
|
||||
// address device
|
||||
}
|
||||
|
||||
/* disable root hub */
|
||||
static void
|
||||
xhci_rh_disable_port (usbdev_t *dev, int port)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
xhci_rh_scanport (usbdev_t *dev, int port)
|
||||
{
|
||||
// clear CSC
|
||||
int val = XHCI_INST (dev->controller)->opreg->prs[port].portsc;
|
||||
val &= PORTSC_RW_MASK;
|
||||
val |= PORTSC_CSC;
|
||||
XHCI_INST (dev->controller)->opreg->prs[port].portsc = val;
|
||||
|
||||
debug("device attach status on port %x: %x\n", port, XHCI_INST (dev->controller)->opreg->prs[port].portsc & PORTSC_CCS);
|
||||
}
|
||||
|
||||
static int
|
||||
xhci_rh_report_port_changes (usbdev_t *dev)
|
||||
{
|
||||
int i;
|
||||
// no change
|
||||
if (!(XHCI_INST (dev->controller)->opreg->usbsts & USBSTS_PCD))
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < RH_INST (dev)->numports; i++) {
|
||||
if (XHCI_INST (dev->controller)->opreg->prs[i].portsc & PORTSC_CSC) {
|
||||
debug("found connect status change on port %d\n", i);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1; // shouldn't ever happen
|
||||
}
|
||||
|
||||
static void
|
||||
xhci_rh_destroy (usbdev_t *dev)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < RH_INST (dev)->numports; i++)
|
||||
xhci_rh_disable_port (dev, i);
|
||||
free (RH_INST (dev));
|
||||
}
|
||||
|
||||
static void
|
||||
xhci_rh_poll (usbdev_t *dev)
|
||||
{
|
||||
int port;
|
||||
while ((port = xhci_rh_report_port_changes (dev)) != -1)
|
||||
xhci_rh_scanport (dev, port);
|
||||
}
|
||||
|
||||
void
|
||||
xhci_rh_init (usbdev_t *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
dev->destroy = xhci_rh_destroy;
|
||||
dev->poll = xhci_rh_poll;
|
||||
|
||||
dev->data = malloc (sizeof (rh_inst_t));
|
||||
if (!dev->data)
|
||||
usb_fatal ("Not enough memory for XHCI RH.\n");
|
||||
|
||||
RH_INST (dev)->numports = XHCI_INST (dev->controller)->capreg->MaxPorts;
|
||||
RH_INST (dev)->port = malloc(sizeof(int) * RH_INST (dev)->numports);
|
||||
debug("%d ports registered\n", RH_INST (dev)->numports);
|
||||
|
||||
for (i = 0; i < RH_INST (dev)->numports; i++) {
|
||||
xhci_rh_enable_port (dev, i);
|
||||
RH_INST (dev)->port[i] = -1;
|
||||
}
|
||||
|
||||
/* we can set them here because a root hub _really_ shouldn't
|
||||
appear elsewhere */
|
||||
dev->address = 0;
|
||||
dev->hub = -1;
|
||||
dev->port = -1;
|
||||
|
||||
debug("rh init done\n");
|
||||
}
|
Loading…
Reference in New Issue