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:
Patrick Georgi
2010-08-13 09:18:58 +00:00
committed by Patrick Georgi
parent 03e54de648
commit 6615ef3bfc
11 changed files with 1736 additions and 11 deletions

View File

@ -224,7 +224,6 @@ config USB_OHCI
help help
Select this option if you are going to use USB 1.1 on an AMD based Select this option if you are going to use USB 1.1 on an AMD based
system. system.
NOTE: This option is not (fully) implemented yet
config USB_EHCI config USB_EHCI
bool "Support for USB EHCI controllers" bool "Support for USB EHCI controllers"
@ -233,6 +232,13 @@ config USB_EHCI
Select this option if you want to use USB 2.0 Select this option if you want to use USB 2.0
NOTE: This option is not (fully) implemented yet 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 config USB_HID
bool "Support for USB keyboards" bool "Support for USB keyboards"
depends on USB depends on USB

View File

@ -60,6 +60,10 @@ TARGETS-$(CONFIG_USB) += drivers/usb/quirks.o
TARGETS-$(CONFIG_USB_HUB) += drivers/usb/usbhub.o TARGETS-$(CONFIG_USB_HUB) += drivers/usb/usbhub.o
TARGETS-$(CONFIG_USB_UHCI) += drivers/usb/uhci.o TARGETS-$(CONFIG_USB_UHCI) += drivers/usb/uhci.o
TARGETS-$(CONFIG_USB_UHCI) += drivers/usb/uhci_rh.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_HID) += drivers/usb/usbhid.o
TARGETS-$(CONFIG_USB_MSC) += drivers/usb/usbmsc.o TARGETS-$(CONFIG_USB_MSC) += drivers/usb/usbmsc.o

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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");
}

View File

@ -30,9 +30,9 @@
#include <libpayload-config.h> #include <libpayload-config.h>
#include <usb/usb.h> #include <usb/usb.h>
#include "uhci.h" #include "uhci.h"
//#include "ohci.h" #include "ohci.h"
//#include "ehci.h" //#include "ehci.h"
//#include "xhci.h" #include "xhci.h"
#include <usb/usbdisk.h> #include <usb/usbdisk.h>
/** /**
@ -68,7 +68,7 @@ usb_controller_initialize (int bus, int dev, int func)
pci_command |= PCI_COMMAND_MASTER; pci_command |= PCI_COMMAND_MASTER;
pci_write_config32(addr, PCI_COMMAND, pci_command); 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); pciid >> 16, pciid & 0xFFFF, func);
if (prog_if == 0) { if (prog_if == 0) {
printf ("UHCI controller\n"); printf ("UHCI controller\n");
@ -81,8 +81,7 @@ usb_controller_initialize (int bus, int dev, int func)
if (prog_if == 0x10) { if (prog_if == 0x10) {
printf ("OHCI controller\n"); printf ("OHCI controller\n");
#ifdef CONFIG_USB_OHCI #ifdef CONFIG_USB_OHCI
//ohci_init(addr); ohci_init(addr);
printf ("Not supported.\n");
#else #else
printf ("Not supported.\n"); printf ("Not supported.\n");
#endif #endif
@ -99,10 +98,9 @@ usb_controller_initialize (int bus, int dev, int func)
} }
if (prog_if == 0x30) { if (prog_if == 0x30) {
printf ("XHCI controller\n"); printf ("xHCI controller\n");
#ifdef CONFIG_USB_XHCI #ifdef CONFIG_USB_XHCI
//xhci_init(addr); xhci_init(addr);
printf ("Not supported.\n");
#else #else
printf ("Not supported.\n"); printf ("Not supported.\n");
#endif #endif
@ -128,8 +126,9 @@ usb_initialize (void)
*/ */
for (bus = 0; bus < 256; bus++) for (bus = 0; bus < 256; bus++)
for (dev = 0; dev < 32; dev++) for (dev = 0; dev < 32; dev++)
for (func = 7; func >= 0 ; func--) if (pci_read_config32 (PCI_DEV(bus, dev, 0), 8) >> 16 == 0x0c03)
usb_controller_initialize (bus, dev, func); for (func = 7; func >= 0 ; func--)
usb_controller_initialize (bus, dev, func);
usb_poll(); usb_poll();
return 0; return 0;
} }

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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");
}