#include "Project.h"
#include "usb_ohci.h"
// we need more TDs than EDs
#define NUM_TD 64
// +1 so we can align the storage
__align(32) td_t gtd[NUM_TD+1];
// pointers to aligned storage
td_t *ptd;
// global ohci_t
static ohci_t gohci;
// this must be aligned to a 256 byte boundary
__align(256) struct ohci_hcca ghcca[1];
// a pointer to the aligned storage
struct ohci_hcca *phcca;
// this allocates EDs for all possible endpoints
__align(16) struct ohci_device ohci_dev;
// urb_priv
urb_priv_t urb_priv;
// RHSC flag
INT got_rhsc;
// device which was disconnected
struct usb_device *devgone;
// flag guarding URB transation
INT urb_finished = 0;
static int cc_to_error[16] = {
// mapping of the OHCI CC status to error codes
0, // No Error
USB_ST_CRC_ERR, // CRC Error
USB_ST_BIT_ERR, // Bit Stuff
USB_ST_CRC_ERR, // Data Togg
USB_ST_STALLED, // Stall
-1, // DevNotResp
USB_ST_BIT_ERR, // PIDCheck
USB_ST_BIT_ERR, // UnExpPID
USB_ST_BUF_ERR, // DataOver
USB_ST_BUF_ERR, // DataUnder
-1, // reservd
-1, // reservd
USB_ST_BUF_ERR, // BufferOver
USB_ST_BUF_ERR, // BuffUnder
-1, // Not Access
-1 // Not Access
};
// TDs ...
static __inline struct td *
td_alloc (struct usb_device *usb_dev)
{
INT i;
struct td *td;
td = NULL;
for (i = 0; i < NUM_TD; i++)
{
if (ptd[i].usb_dev == NULL)
{
td = &ptd[i];
td->usb_dev = usb_dev;
break;
}
}
return td;
}
static __inline void
ed_free (struct ed *ed)
{
ed->usb_dev = NULL;
}
// -------------------------------------------------------------------------
// AMD-756 (D2 rev) reports corrupt register contents in some cases.
// The erratum (#4) description is incorrect. AMD's workaround waits
// till some bits (mostly reserved) are clear; ok for all revs.
#define OHCI_QUIRK_AMD756 0xabcd
static UINT32 roothub_a (struct ohci *hc)
{
UINT32 temp = readl (&hc->regs->roothub.a);
UINT32 mask = 0xfc0fe000;
if (hc->flags & OHCI_QUIRK_AMD756)
while (temp & mask)
temp = readl (&hc->regs->roothub.a);
return temp;
}
static __inline UINT32 roothub_b (struct ohci *hc)
{
return readl (&hc->regs->roothub.b);
}
static __inline UINT32 roothub_status (struct ohci *hc)
{
return readl (&hc->regs->roothub.status);
}
static UINT32 roothub_portstatus (struct ohci *hc, INT i)
{
UINT32 temp = readl (&hc->regs->roothub.portstatus[i]);
UINT32 mask = 0xffe0fce0;
if (hc->flags & OHCI_QUIRK_AMD756)
while (temp & mask)
temp = readl (&hc->regs->roothub.portstatus[i]);
return temp;
}
// forward declaration
static INT hc_interrupt (void);
static void
td_submit_job (struct usb_device * dev, UINT32 pipe, void * buffer,
INT transfer_len, struct devrequest * setup, urb_priv_t * urb, INT interval);
// -------------------------------------------------------------------------
// URB support functions
//-------------------------------------------------------------------------
// free HCD-private data associated with this URB
static void urb_free_priv (urb_priv_t * urb)
{
INT i;
INT last;
struct td * td;
last = urb->length - 1;
if (last >= 0) {
for (i = 0; i <= last; i++) {
td = urb->td[i];
if (td) {
td->usb_dev = NULL;
urb->td[i] = NULL;
}
}
}
}
// -------------------------------------------------------------------------
#ifdef DEBUG
static INT sohci_get_current_frame_number (struct usb_device * dev);
// debug| print the main components of an URB
// small: 0) header + data packets 1) just header
static void pkt_print (struct usb_device * dev, UINT32 pipe, void * buffer,
INT transfer_len, struct devrequest * setup, char * str, INT small)
{
/*
urb_priv_t * purb = &urb_priv;
TRACE("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,len:%d/%d stat:%#lx\r\n",
str,
sohci_get_current_frame_number (dev),
usb_pipedevice (pipe),
usb_pipeendpoint (pipe),
usb_pipeout (pipe)? 'O': 'I',
usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"):
(usb_pipecontrol (pipe)? "CTRL": "BULK"),
purb->actual_length,
transfer_len, dev->status);
#ifdef OHCI_VERBOSE_DEBUG
if (!small) {
INT i, len;
if (usb_pipecontrol (pipe)) {
TRACE(__FILE__ ": cmd(8):\r\n");
for (i = 0; i < 8 ; i++)
TRACE(" %02x", ((UINT8 *) setup) [i]);
TRACE("\r\n");
}
if (transfer_len > 0 && buffer) {
TRACE(__FILE__ ": data(%d/%d):\r\n",
purb->actual_length,
transfer_len);
len = usb_pipeout (pipe)?
transfer_len: purb->actual_length;
for (i = 0; i < 16 && i < len; i++)
TRACE(" %02x", ((UINT8 *) buffer) [i]);
TRACE("%s\r\n", i < len? "...": "");
}
}
#endif
*/
}
// just for debugging; prints non-empty branches of the INT ed tree inclusive iso eds
void ep_print_int_eds (ohci_t *ohci, char * str) {
INT i, j;
UINT32 * ed_p;
for (i= 0; i < 32; i++) {
j = 5;
ed_p = &(ohci->hcca->int_table [i]);
if (*ed_p == 0)
continue;
TRACE(__FILE__ ": %s branch INT %2d(%2x):\r\n", str, i, i);
while (*ed_p != 0 && j--) {
ed_t *ed = (ed_t *)m32_swap(ed_p);
TRACE(" ed: %4x;", ed->hwINFO);
ed_p = &ed->hwNextED;
}
TRACE("\r\n");
}
}
static void ohci_dump_intr_mask (char *label, UINT32 mask)
{
TRACE("%s: 0x%08x%s%s%s%s%s%s%s%s%s\r\n",
label,
mask,
(mask & OHCI_INTR_MIE) ? " MIE" : "",
(mask & OHCI_INTR_OC) ? " OC" : "",
(mask & OHCI_INTR_RHSC) ? " RHSC" : "",
(mask & OHCI_INTR_FNO) ? " FNO" : "",
(mask & OHCI_INTR_UE) ? " UE" : "",
(mask & OHCI_INTR_RD) ? " RD" : "",
(mask & OHCI_INTR_SF) ? " SF" : "",
(mask & OHCI_INTR_WDH) ? " WDH" : "",
(mask & OHCI_INTR_SO) ? " SO" : ""
);
}
static void maybe_print_eds (char *label, UINT32 value)
{
ed_t *edp = (ed_t *)value;
if (value) {
TRACE ("%s %08x\r\n", label, value);
TRACE ("%08x\r\n", edp->hwINFO);
TRACE ("%08x\r\n", edp->hwTailP);
TRACE ("%08x\r\n", edp->hwHeadP);
TRACE ("%08x\r\n", edp->hwNextED);
}
}
static char * hcfs2string (INT state)
{
switch (state) {
case OHCI_USB_RESET: return "reset";
case OHCI_USB_RESUME: return "resume";
case OHCI_USB_OPER: return "operational";
case OHCI_USB_SUSPEND: return "suspend";
}
return "?";
}
// dump control and status registers
static void ohci_dump_status (ohci_t *controller)
{
struct ohci_regs *regs = controller->regs;
UINT32 temp;
temp = readl (®s->revision) & 0xff;
if (temp != 0x10)
TRACE ("spec %d.%d\r\n", (temp >> 4), (temp & 0x0f));
temp = readl (®s->control);
TRACE ("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d\r\n", temp,
(temp & OHCI_CTRL_RWE) ? " RWE" : "",
(temp & OHCI_CTRL_RWC) ? " RWC" : "",
(temp & OHCI_CTRL_IR) ? " IR" : "",
hcfs2string (temp & OHCI_CTRL_HCFS),
(temp & OHCI_CTRL_BLE) ? " BLE" : "",
(temp & OHCI_CTRL_CLE) ? " CLE" : "",
(temp & OHCI_CTRL_IE) ? " IE" : "",
(temp & OHCI_CTRL_PLE) ? " PLE" : "",
temp & OHCI_CTRL_CBSR
);
temp = readl (®s->cmdstatus);
TRACE ("cmdstatus: 0x%08x SOC=%d%s%s%s%s\r\n", temp,
(temp & OHCI_SOC) >> 16,
(temp & OHCI_OCR) ? " OCR" : "",
(temp & OHCI_BLF) ? " BLF" : "",
(temp & OHCI_CLF) ? " CLF" : "",
(temp & OHCI_HCR) ? " HCR" : ""
);
ohci_dump_intr_mask ("intrstatus", readl (®s->intrstatus));
ohci_dump_intr_mask ("intrenable", readl (®s->intrenable));
maybe_print_eds ("ed_periodcurrent", readl (®s->ed_periodcurrent));
maybe_print_eds ("ed_controlhead", readl (®s->ed_controlhead));
maybe_print_eds ("ed_controlcurrent", readl (®s->ed_controlcurrent));
maybe_print_eds ("ed_bulkhead", readl (®s->ed_bulkhead));
maybe_print_eds ("ed_bulkcurrent", readl (®s->ed_bulkcurrent));
maybe_print_eds (