Newer
Older
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
cc = TD_CC_GET(m32_swap(td_list->hwINFO));
if (cc) {
err(" USB-error: %s (%x)", cc_to_string[cc], cc);
if (*phwHeadP & m32_swap(0x1)) {
if (lurb_priv &&
((td_list->index + 1) < urb_len)) {
*phwHeadP =
(lurb_priv->td[urb_len - 1]->hwNextTD &\
m32_swap(0xfffffff0)) |
(*phwHeadP & m32_swap(0x2));
lurb_priv->td_cnt += urb_len -
td_list->index - 1;
} else
*phwHeadP &= m32_swap(0xfffffff2);
}
#ifdef CONFIG_MPC5200
td_list->hwNextTD = 0;
#endif
}
}
Markus Klotzbuecher
committed
/* replies to the request have to be on a FIFO basis so
* we reverse the reversed done-list */
static td_t *dl_reverse_done_list(ohci_t *ohci)
Markus Klotzbuecher
committed
{
__u32 td_list_hc;
td_t *td_rev = NULL;
td_t *td_list = NULL;
td_list_hc = m32_swap(ohci->hcca->done_head) & 0xfffffff0;
Markus Klotzbuecher
committed
ohci->hcca->done_head = 0;
while (td_list_hc) {
td_list = (td_t *)td_list_hc;
Markus Klotzbuecher
committed
td_list->next_dl_td = td_rev;
td_rev = td_list;
td_list_hc = m32_swap(td_list->hwNextTD) & 0xfffffff0;
Markus Klotzbuecher
committed
}
return td_list;
}
/*-------------------------------------------------------------------------*/
Markus Klotzbuecher
committed
/*-------------------------------------------------------------------------*/
static void finish_urb(ohci_t *ohci, urb_priv_t *urb, int status)
{
if ((status & (ED_OPER | ED_UNLINK)) && (urb->state != URB_DEL))
urb->finished = sohci_return_job(ohci, urb);
else
dbg("finish_urb: strange.., ED state %x, \n", status);
}
/*
* Used to take back a TD from the host controller. This would normally be
* called from within dl_done_list, however it may be called directly if the
* HC no longer sees the TD and it has not appeared on the donelist (after
* two frames). This bug has been observed on ZF Micro systems.
*/
static int takeback_td(ohci_t *ohci, td_t *td_list)
Markus Klotzbuecher
committed
{
ed_t *ed;
Markus Klotzbuecher
committed
int stat = 0;
/* urb_t *urb; */
urb_priv_t *lurb_priv;
__u32 tdINFO, edHeadP, edTailP;
tdINFO = m32_swap(td_list->hwINFO);
Markus Klotzbuecher
committed
ed = td_list->ed;
lurb_priv = ed->purb;
Markus Klotzbuecher
committed
dl_transfer_length(td_list);
Markus Klotzbuecher
committed
Markus Klotzbuecher
committed
/* error code of transfer */
cc = TD_CC_GET(tdINFO);
if (cc) {
err("USB-error: %s (%x)", cc_to_string[cc], cc);
stat = cc_to_error[cc];
}
/* see if this done list makes for all TD's of current URB,
* and mark the URB finished if so */
if (lurb_priv->td_cnt == lurb_priv->length)
finish_urb(ohci, lurb_priv, ed->state);
dbg("dl_done_list: processing TD %x, len %x\n",
lurb_priv->td_cnt, lurb_priv->length);
if (ed->state != ED_NEW && (!usb_pipeint(lurb_priv->pipe))) {
edHeadP = m32_swap(ed->hwHeadP) & 0xfffffff0;
edTailP = m32_swap(ed->hwTailP);
/* unlink eds if they are not busy */
if ((edHeadP == edTailP) && (ed->state == ED_OPER))
ep_unlink(ohci, ed);
}
return stat;
}
Markus Klotzbuecher
committed
static int dl_done_list(ohci_t *ohci)
{
int stat = 0;
td_t *td_list = dl_reverse_done_list(ohci);
while (td_list) {
td_t *td_next = td_list->next_dl_td;
stat = takeback_td(ohci, td_list);
td_list = td_next;
Markus Klotzbuecher
committed
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
}
return stat;
}
/*-------------------------------------------------------------------------*
* Virtual Root Hub
*-------------------------------------------------------------------------*/
/* Device descriptor */
static __u8 root_hub_dev_des[] =
{
0x12, /* __u8 bLength; */
0x01, /* __u8 bDescriptorType; Device */
0x10, /* __u16 bcdUSB; v1.1 */
0x01,
0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
0x00, /* __u8 bDeviceSubClass; */
0x00, /* __u8 bDeviceProtocol; */
0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
0x00, /* __u16 idVendor; */
0x00,
0x00, /* __u16 idProduct; */
0x00,
0x00, /* __u16 bcdDevice; */
0x00,
0x00, /* __u8 iManufacturer; */
0x01, /* __u8 iProduct; */
0x00, /* __u8 iSerialNumber; */
0x01 /* __u8 bNumConfigurations; */
};
/* Configuration descriptor */
static __u8 root_hub_config_des[] =
{
0x09, /* __u8 bLength; */
0x02, /* __u8 bDescriptorType; Configuration */
0x19, /* __u16 wTotalLength; */
0x00,
0x01, /* __u8 bNumInterfaces; */
0x01, /* __u8 bConfigurationValue; */
0x00, /* __u8 iConfiguration; */
0x40, /* __u8 bmAttributes;
Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */
Markus Klotzbuecher
committed
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
0x00, /* __u8 MaxPower; */
/* interface */
0x09, /* __u8 if_bLength; */
0x04, /* __u8 if_bDescriptorType; Interface */
0x00, /* __u8 if_bInterfaceNumber; */
0x00, /* __u8 if_bAlternateSetting; */
0x01, /* __u8 if_bNumEndpoints; */
0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */
0x00, /* __u8 if_bInterfaceSubClass; */
0x00, /* __u8 if_bInterfaceProtocol; */
0x00, /* __u8 if_iInterface; */
/* endpoint */
0x07, /* __u8 ep_bLength; */
0x05, /* __u8 ep_bDescriptorType; Endpoint */
0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
0x03, /* __u8 ep_bmAttributes; Interrupt */
0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */
0x00,
0xff /* __u8 ep_bInterval; 255 ms */
};
static unsigned char root_hub_str_index0[] =
{
0x04, /* __u8 bLength; */
0x03, /* __u8 bDescriptorType; String-descriptor */
0x09, /* __u8 lang ID */
0x04, /* __u8 lang ID */
};
static unsigned char root_hub_str_index1[] =
{
28, /* __u8 bLength; */
0x03, /* __u8 bDescriptorType; String-descriptor */
'O', /* __u8 Unicode */
0, /* __u8 Unicode */
'H', /* __u8 Unicode */
0, /* __u8 Unicode */
'C', /* __u8 Unicode */
0, /* __u8 Unicode */
'I', /* __u8 Unicode */
0, /* __u8 Unicode */
' ', /* __u8 Unicode */
0, /* __u8 Unicode */
'R', /* __u8 Unicode */
0, /* __u8 Unicode */
'o', /* __u8 Unicode */
0, /* __u8 Unicode */
'o', /* __u8 Unicode */
0, /* __u8 Unicode */
't', /* __u8 Unicode */
0, /* __u8 Unicode */
' ', /* __u8 Unicode */
0, /* __u8 Unicode */
'H', /* __u8 Unicode */
0, /* __u8 Unicode */
'u', /* __u8 Unicode */
0, /* __u8 Unicode */
'b', /* __u8 Unicode */
0, /* __u8 Unicode */
};
/* Hub class-specific descriptor is constructed dynamically */
/*-------------------------------------------------------------------------*/
#define OK(x) len = (x); break
#ifdef DEBUG
#define WR_RH_STAT(x) {info("WR:status %#8x", (x)); writel((x), \
&gohci.regs->roothub.status); }
#define WR_RH_PORTSTAT(x) {info("WR:portstatus[%d] %#8x", wIndex-1, \
(x)); writel((x), &gohci.regs->roothub.portstatus[wIndex-1]); }
Markus Klotzbuecher
committed
#else
#define WR_RH_STAT(x) writel((x), &gohci.regs->roothub.status)
#define WR_RH_PORTSTAT(x) writel((x), \
&gohci.regs->roothub.portstatus[wIndex-1])
Markus Klotzbuecher
committed
#endif
#define RD_RH_STAT roothub_status(&gohci)
#define RD_RH_PORTSTAT roothub_portstatus(&gohci, wIndex-1)
Markus Klotzbuecher
committed
/* request to virtual root hub */
int rh_check_port_status(ohci_t *controller)
{
__u32 temp, ndp, i;
int res;
res = -1;
temp = roothub_a(controller);
Markus Klotzbuecher
committed
ndp = (temp & RH_A_NDP);
#ifdef CONFIG_AT91C_PQFP_UHPBUG
ndp = (ndp == 2) ? 1:0;
#endif
for (i = 0; i < ndp; i++) {
temp = roothub_portstatus(controller, i);
Markus Klotzbuecher
committed
/* check for a device disconnect */
if (((temp & (RH_PS_PESC | RH_PS_CSC)) ==
(RH_PS_PESC | RH_PS_CSC)) &&
((temp & RH_PS_CCS) == 0)) {
res = i;
break;
}
}
return res;
}
static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe,
void *buffer, int transfer_len, struct devrequest *cmd)
{
Markus Klotzbuecher
committed
int leni = transfer_len;
int len = 0;
int stat = 0;
__u32 datab[4];
__u8 *data_buf = (__u8 *)datab;
__u16 bmRType_bReq;
__u16 wValue;
__u16 wIndex;
__u16 wLength;
#ifdef DEBUG
pkt_print(NULL, dev, pipe, buffer, transfer_len,
cmd, "SUB(rh)", usb_pipein(pipe));
Markus Klotzbuecher
committed
#else
wait_ms(1);
#endif
Markus Klotzbuecher
committed
info("Root-Hub submit IRQ: NOT implemented");
return 0;
}
bmRType_bReq = cmd->requesttype | (cmd->request << 8);
wValue = le16_to_cpu(cmd->value);
wIndex = le16_to_cpu(cmd->index);
wLength = le16_to_cpu(cmd->length);
Markus Klotzbuecher
committed
info("Root-Hub: adr: %2x cmd(%1x): %08x %04x %04x %04x",
dev->devnum, 8, bmRType_bReq, wValue, wIndex, wLength);
switch (bmRType_bReq) {
/* Request Destination:
without flags: Device,
RH_INTERFACE: interface,
RH_ENDPOINT: endpoint,
RH_CLASS means HUB here,
RH_OTHER | RH_CLASS almost ever means HUB_PORT here
*/
case RH_GET_STATUS:
*(__u16 *) data_buf = cpu_to_le16(1);
OK(2);
Markus Klotzbuecher
committed
case RH_GET_STATUS | RH_INTERFACE:
*(__u16 *) data_buf = cpu_to_le16(0);
OK(2);
Markus Klotzbuecher
committed
case RH_GET_STATUS | RH_ENDPOINT:
*(__u16 *) data_buf = cpu_to_le16(0);
OK(2);
Markus Klotzbuecher
committed
case RH_GET_STATUS | RH_CLASS:
*(__u32 *) data_buf = cpu_to_le32(
Markus Klotzbuecher
committed
RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE));
Markus Klotzbuecher
committed
case RH_GET_STATUS | RH_OTHER | RH_CLASS:
*(__u32 *) data_buf = cpu_to_le32(RD_RH_PORTSTAT);
OK(4);
Markus Klotzbuecher
committed
case RH_CLEAR_FEATURE | RH_ENDPOINT:
switch (wValue) {
case (RH_ENDPOINT_STALL):
OK(0);
Markus Klotzbuecher
committed
}
break;
case RH_CLEAR_FEATURE | RH_CLASS:
switch (wValue) {
case RH_C_HUB_LOCAL_POWER:
OK(0);
case (RH_C_HUB_OVER_CURRENT):
WR_RH_STAT(RH_HS_OCIC);
OK(0);
Markus Klotzbuecher
committed
}
break;
case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
switch (wValue) {
case (RH_PORT_ENABLE): WR_RH_PORTSTAT(RH_PS_CCS); OK(0);
case (RH_PORT_SUSPEND): WR_RH_PORTSTAT(RH_PS_POCI); OK(0);
case (RH_PORT_POWER): WR_RH_PORTSTAT(RH_PS_LSDA); OK(0);
case (RH_C_PORT_CONNECTION): WR_RH_PORTSTAT(RH_PS_CSC); OK(0);
case (RH_C_PORT_ENABLE): WR_RH_PORTSTAT(RH_PS_PESC); OK(0);
case (RH_C_PORT_SUSPEND): WR_RH_PORTSTAT(RH_PS_PSSC); OK(0);
case (RH_C_PORT_OVER_CURRENT):WR_RH_PORTSTAT(RH_PS_OCIC); OK(0);
case (RH_C_PORT_RESET): WR_RH_PORTSTAT(RH_PS_PRSC); OK(0);
Markus Klotzbuecher
committed
}
break;
case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
switch (wValue) {
case (RH_PORT_SUSPEND):
WR_RH_PORTSTAT(RH_PS_PSS); OK(0);
case (RH_PORT_RESET): /* BUG IN HUP CODE *********/
if (RD_RH_PORTSTAT & RH_PS_CCS)
WR_RH_PORTSTAT(RH_PS_PRS);
OK(0);
case (RH_PORT_POWER):
WR_RH_PORTSTAT(RH_PS_PPS);
wait_ms(100);
OK(0);
case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/
if (RD_RH_PORTSTAT & RH_PS_CCS)
WR_RH_PORTSTAT(RH_PS_PES);
OK(0);
Markus Klotzbuecher
committed
}
break;
case RH_SET_ADDRESS:
gohci.rh.devnum = wValue;
OK(0);
Markus Klotzbuecher
committed
case RH_GET_DESCRIPTOR:
switch ((wValue & 0xff00) >> 8) {
case (0x01): /* device descriptor */
len = min_t(unsigned int,
leni,
min_t(unsigned int,
sizeof(root_hub_dev_des),
wLength));
data_buf = root_hub_dev_des; OK(len);
case (0x02): /* configuration descriptor */
len = min_t(unsigned int,
leni,
min_t(unsigned int,
sizeof(root_hub_config_des),
wLength));
data_buf = root_hub_config_des; OK(len);
case (0x03): /* string descriptors */
if (wValue == 0x0300) {
Markus Klotzbuecher
committed
len = min_t(unsigned int,
leni,
min_t(unsigned int,
sizeof(root_hub_str_index0),
wLength));
data_buf = root_hub_str_index0;
OK(len);
Markus Klotzbuecher
committed
}
if (wValue == 0x0301) {
len = min_t(unsigned int,
leni,
min_t(unsigned int,
sizeof(root_hub_str_index1),
wLength));
data_buf = root_hub_str_index1;
OK(len);
}
default:
stat = USB_ST_STALLED;
Markus Klotzbuecher
committed
}
break;
case RH_GET_DESCRIPTOR | RH_CLASS:
{
__u32 temp = roothub_a(&gohci);
Markus Klotzbuecher
committed
data_buf [0] = 9; /* min length; */
data_buf [1] = 0x29;
data_buf [2] = temp & RH_A_NDP;
#ifdef CONFIG_AT91C_PQFP_UHPBUG
data_buf [2] = (data_buf [2] == 2) ? 1:0;
#endif
data_buf [3] = 0;
if (temp & RH_A_PSM) /* per-port power switching? */
data_buf [3] |= 0x1;
if (temp & RH_A_NOCP) /* no overcurrent reporting? */
data_buf [3] |= 0x10;
else if (temp & RH_A_OCPM)/* per-port overcurrent reporting? */
Markus Klotzbuecher
committed
data_buf [3] |= 0x8;
/* corresponds to data_buf[4-7] */
datab [1] = 0;
data_buf [5] = (temp & RH_A_POTPGT) >> 24;
Markus Klotzbuecher
committed
data_buf [7] = temp & RH_B_DR;
if (data_buf [2] < 7) {
data_buf [8] = 0xff;
} else {
data_buf [0] += 2;
data_buf [8] = (temp & RH_B_DR) >> 8;
data_buf [10] = data_buf [9] = 0xff;
}
len = min_t(unsigned int, leni,
min_t(unsigned int, data_buf [0], wLength));
Markus Klotzbuecher
committed
}
case RH_GET_CONFIGURATION: *(__u8 *) data_buf = 0x01; OK(1);
Markus Klotzbuecher
committed
case RH_SET_CONFIGURATION: WR_RH_STAT(0x10000); OK(0);
Markus Klotzbuecher
committed
default:
dbg("unsupported root hub command");
Markus Klotzbuecher
committed
stat = USB_ST_STALLED;
}
#ifdef DEBUG
ohci_dump_roothub(&gohci, 1);
Markus Klotzbuecher
committed
#else
wait_ms(1);
#endif
len = min_t(int, len, leni);
if (data != data_buf)
memcpy(data, data_buf, len);
Markus Klotzbuecher
committed
dev->act_len = len;
dev->status = stat;
#ifdef DEBUG
pkt_print(NULL, dev, pipe, buffer,
transfer_len, cmd, "RET(rh)", 0/*usb_pipein(pipe)*/);
Markus Klotzbuecher
committed
#else
wait_ms(1);
#endif
return stat;
}
/*-------------------------------------------------------------------------*/
/* common code for handling submit messages - used for all but root hub */
/* accesses. */
int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
int transfer_len, struct devrequest *setup, int interval)
{
int stat = 0;
int maxsize = usb_maxpacket(dev, pipe);
int timeout;
urb_priv_t *urb;
urb = malloc(sizeof(urb_priv_t));
memset(urb, 0, sizeof(urb_priv_t));
urb->dev = dev;
urb->pipe = pipe;
urb->transfer_buffer = buffer;
urb->transfer_buffer_length = transfer_len;
urb->interval = interval;
Markus Klotzbuecher
committed
/* device pulled? Shortcut the action. */
if (devgone == dev) {
dev->status = USB_ST_CRC_ERR;
return 0;
}
#ifdef DEBUG
urb->actual_length = 0;
pkt_print(urb, dev, pipe, buffer, transfer_len,
setup, "SUB", usb_pipein(pipe));
Markus Klotzbuecher
committed
#else
wait_ms(1);
#endif
if (!maxsize) {
err("submit_common_message: pipesize for pipe %lx is zero",
pipe);
return -1;
}
if (sohci_submit_job(urb, setup) < 0) {
Markus Klotzbuecher
committed
err("sohci_submit_job failed");
return -1;
}
#if 0
Markus Klotzbuecher
committed
wait_ms(10);
/* ohci_dump_status(&gohci); */
#endif
Markus Klotzbuecher
committed
/* allow more time for a BULK device to react - some are slow */
#define BULK_TO 5000 /* timeout in milliseconds */
Markus Klotzbuecher
committed
timeout = BULK_TO;
else
timeout = 100;
/* wait for it to complete */
for (;;) {
/* check whether the controller is done */
stat = hc_interrupt();
if (stat < 0) {
stat = USB_ST_CRC_ERR;
break;
}
/* NOTE: since we are not interrupt driven in U-Boot and always
* handle only one URB at a time, we cannot assume the
* transaction finished on the first successful return from
* hc_interrupt().. unless the flag for current URB is set,
* meaning that all TD's to/from device got actually
* transferred and processed. If the current URB is not
* finished we need to re-iterate this loop so as
* hc_interrupt() gets called again as there needs to be some
* more TD's to process still */
if ((stat >= 0) && (stat != 0xff) && (urb->finished)) {
Markus Klotzbuecher
committed
/* 0xff is returned for an SF-interrupt */
break;
}
Markus Klotzbuecher
committed
if (--timeout) {
wait_ms(1);
if (!urb->finished)
Markus Klotzbuecher
committed
} else {
err("CTL:TIMEOUT ");
dbg("submit_common_msg: TO status %x\n", stat);
Markus Klotzbuecher
committed
stat = USB_ST_CRC_ERR;
break;
}
}
dev->status = stat;
dev->act_len = transfer_len;
#ifdef DEBUG
pkt_print(urb, dev, pipe, buffer, transfer_len,
setup, "RET(ctlr)", usb_pipein(pipe));
Markus Klotzbuecher
committed
#else
wait_ms(1);
#endif
/* free TDs in urb_priv */
Markus Klotzbuecher
committed
return 0;
}
/* submit routines called from usb.c */
int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
int transfer_len)
{
info("submit_bulk_msg");
return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, 0);
}
int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
int transfer_len, struct devrequest *setup)
{
int maxsize = usb_maxpacket(dev, pipe);
info("submit_control_msg");
#ifdef DEBUG
pkt_print(NULL, dev, pipe, buffer, transfer_len,
setup, "SUB", usb_pipein(pipe));
Markus Klotzbuecher
committed
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
#else
wait_ms(1);
#endif
if (!maxsize) {
err("submit_control_message: pipesize for pipe %lx is zero",
pipe);
return -1;
}
if (((pipe >> 8) & 0x7f) == gohci.rh.devnum) {
gohci.rh.dev = dev;
/* root hub - redirect */
return ohci_submit_rh_msg(dev, pipe, buffer, transfer_len,
setup);
}
return submit_common_msg(dev, pipe, buffer, transfer_len, setup, 0);
}
int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
int transfer_len, int interval)
{
info("submit_int_msg");
return submit_common_msg(dev, pipe, buffer, transfer_len, NULL,
interval);
Markus Klotzbuecher
committed
}
/*-------------------------------------------------------------------------*
* HC functions
*-------------------------------------------------------------------------*/
/* reset the HC and BUS */
static int hc_reset(ohci_t *ohci)
Markus Klotzbuecher
committed
{
#ifdef CONFIG_PCI_EHCI_DEVNO
pci_dev_t pdev;
#endif
Markus Klotzbuecher
committed
int timeout = 30;
int smm_timeout = 50; /* 0,5 sec */
dbg("%s\n", __FUNCTION__);
#ifdef CONFIG_PCI_EHCI_DEVNO
/*
* Some multi-function controllers (e.g. ISP1562) allow root hub
* resetting via EHCI registers only.
*/
pdev = pci_find_devices(ehci_pci_ids, CONFIG_PCI_EHCI_DEVNO);
if (pdev != -1) {
u32 base;
int timeout = 1000;
pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &base);
writel(readl(base + EHCI_USBCMD_OFF) | EHCI_USBCMD_HCRESET,
base + EHCI_USBCMD_OFF);
while (readl(base + EHCI_USBCMD_OFF) & EHCI_USBCMD_HCRESET) {
if (timeout-- <= 0) {
printf("USB RootHub reset timed out!");
break;
}
udelay(1);
}
} else
printf("No EHCI func at %d index!\n", CONFIG_PCI_EHCI_DEVNO);
#endif
if (readl(&ohci->regs->control) & OHCI_CTRL_IR) {
/* SMM owns the HC */
writel(OHCI_OCR, &ohci->regs->cmdstatus);/* request ownership */
Markus Klotzbuecher
committed
info("USB HC TakeOver from SMM");
while (readl(&ohci->regs->control) & OHCI_CTRL_IR) {
wait_ms(10);
Markus Klotzbuecher
committed
if (--smm_timeout == 0) {
err("USB HC TakeOver failed!");
return -1;
}
}
}
/* Disable HC interrupts */
writel(OHCI_INTR_MIE, &ohci->regs->intrdisable);
Markus Klotzbuecher
committed
dbg("USB HC reset_hc usb-%s: ctrl = 0x%X ;\n",
ohci->slot_name,
readl(&ohci->regs->control));
/* Reset USB (needed by some controllers) */
Markus Klotzbuecher
committed
ohci->hc_control = 0;
writel(ohci->hc_control, &ohci->regs->control);
Markus Klotzbuecher
committed
/* HC Reset requires max 10 us delay */
writel(OHCI_HCR, &ohci->regs->cmdstatus);
while ((readl(&ohci->regs->cmdstatus) & OHCI_HCR) != 0) {
Markus Klotzbuecher
committed
if (--timeout == 0) {
err("USB HC reset timed out!");
return -1;
}
Markus Klotzbuecher
committed
}
return 0;
}
/*-------------------------------------------------------------------------*/
/* Start an OHCI controller, set the BUS operational
* enable interrupts
* connect the virtual root hub */
static int hc_start(ohci_t *ohci)
Markus Klotzbuecher
committed
{
__u32 mask;
unsigned int fminterval;
ohci->disabled = 1;
/* Tell the controller where the control and bulk lists are
* The lists are empty now. */
writel(0, &ohci->regs->ed_controlhead);
writel(0, &ohci->regs->ed_bulkhead);
Markus Klotzbuecher
committed
writel((__u32)ohci->hcca, &ohci->regs->hcca); /* a reset clears this */
Markus Klotzbuecher
committed
fminterval = 0x2edf;
writel((fminterval * 9) / 10, &ohci->regs->periodicstart);
Markus Klotzbuecher
committed
fminterval |= ((((fminterval - 210) * 6) / 7) << 16);
writel(fminterval, &ohci->regs->fminterval);
writel(0x628, &ohci->regs->lsthresh);
Markus Klotzbuecher
committed
/* start controller operations */
ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
ohci->disabled = 0;
writel(ohci->hc_control, &ohci->regs->control);
Markus Klotzbuecher
committed
/* disable all interrupts */
mask = (OHCI_INTR_SO | OHCI_INTR_WDH | OHCI_INTR_SF | OHCI_INTR_RD |
OHCI_INTR_UE | OHCI_INTR_FNO | OHCI_INTR_RHSC |
OHCI_INTR_OC | OHCI_INTR_MIE);
writel(mask, &ohci->regs->intrdisable);
Markus Klotzbuecher
committed
/* clear all interrupts */
mask &= ~OHCI_INTR_MIE;
writel(mask, &ohci->regs->intrstatus);
Markus Klotzbuecher
committed
/* Choose the interrupts we care about now - but w/o MIE */
mask = OHCI_INTR_RHSC | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO;
writel(mask, &ohci->regs->intrenable);
Markus Klotzbuecher
committed
#ifdef OHCI_USE_NPS
/* required for AMD-756 and some Mac platforms */
writel((roothub_a(ohci) | RH_A_NPS) & ~RH_A_PSM,
Markus Klotzbuecher
committed
&ohci->regs->roothub.a);
writel(RH_HS_LPSC, &ohci->regs->roothub.status);
Markus Klotzbuecher
committed
#endif /* OHCI_USE_NPS */
#define mdelay(n) ({unsigned long msec = (n); while (msec--) udelay(1000); })
Markus Klotzbuecher
committed
/* POTPGT delay is bits 24-31, in 2 ms units. */
mdelay((roothub_a(ohci) >> 23) & 0x1fe);
Markus Klotzbuecher
committed
/* connect the virtual root hub */
ohci->rh.devnum = 0;
return 0;
}
/*-------------------------------------------------------------------------*/
/* Poll USB interrupt. */
void usb_event_poll(void)
{
hc_interrupt();
}
Markus Klotzbuecher
committed
/* an interrupt happens */
static int hc_interrupt(void)
Markus Klotzbuecher
committed
{
ohci_t *ohci = &gohci;
struct ohci_regs *regs = ohci->regs;
int ints;
int stat = -1;
if ((ohci->hcca->done_head != 0) &&
!(m32_swap(ohci->hcca->done_head) & 0x01)) {
ints = OHCI_INTR_WDH;
} else {
ints = readl(®s->intrstatus);
if (ints == ~(u32)0) {
ohci->disabled++;
err("%s device removed!", ohci->slot_name);
return -1;
} else {
ints &= readl(®s->intrenable);
if (ints == 0) {
dbg("hc_interrupt: returning..\n");
return 0xff;
}
}
/* dbg("Interrupt: %x frame: %x", ints,
le16_to_cpu(ohci->hcca->frame_no)); */
Markus Klotzbuecher
committed
Markus Klotzbuecher
committed
if (ints & OHCI_INTR_UE) {
ohci->disabled++;
err("OHCI Unrecoverable Error, controller usb-%s disabled",
Markus Klotzbuecher
committed
ohci->slot_name);
/* e.g. due to PCI Master/Target Abort */
#ifdef DEBUG
Markus Klotzbuecher
committed
#else
Markus Klotzbuecher
committed
#endif
/* FIXME: be optimistic, hope that bug won't repeat often. */
/* Make some non-interrupt context restart the controller. */
/* Count and limit the retries though; either hardware or */
/* software errors can go forever... */
Markus Klotzbuecher
committed
return -1;
}
if (ints & OHCI_INTR_WDH) {
wait_ms(1);
writel(OHCI_INTR_WDH, ®s->intrdisable);
(void)readl(®s->intrdisable); /* flush */
stat = dl_done_list(&gohci);
writel(OHCI_INTR_WDH, ®s->intrenable);
(void)readl(®s->intrdisable); /* flush */
Markus Klotzbuecher
committed
}
if (ints & OHCI_INTR_SO) {
dbg("USB Schedule overrun\n");
writel(OHCI_INTR_SO, ®s->intrenable);
Markus Klotzbuecher
committed
stat = -1;
}
/* FIXME: this assumes SOF (1/ms) interrupts don't get lost... */
if (ints & OHCI_INTR_SF) {
unsigned int frame = m16_swap(ohci->hcca->frame_no) & 1;
Markus Klotzbuecher
committed
wait_ms(1);
writel(OHCI_INTR_SF, ®s->intrdisable);
Markus Klotzbuecher
committed
if (ohci->ed_rm_list[frame] != NULL)
writel(OHCI_INTR_SF, ®s->intrenable);
Markus Klotzbuecher
committed
stat = 0xff;
}
writel(ints, ®s->intrstatus);
Markus Klotzbuecher
committed
return stat;
}
/*-------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*/
/* De-allocate all resources.. */
static void hc_release_ohci(ohci_t *ohci)
Markus Klotzbuecher
committed
{
dbg("USB HC release ohci usb-%s", ohci->slot_name);
Markus Klotzbuecher
committed
if (!ohci->disabled)
Markus Klotzbuecher
committed
}
/*-------------------------------------------------------------------------*/
/*
* low level initalisation routine, called from usb.c
*/
static char ohci_inited = 0;
int usb_lowlevel_init(void)
{
#ifdef CONFIG_PCI_OHCI
pci_dev_t pdev;
#endif
Markus Klotzbuecher
committed
#ifdef CONFIG_SYS_USB_OHCI_CPU_INIT
Markus Klotzbuecher
committed
/* cpu dependant init */
Markus Klotzbuecher
committed
return -1;
Markus Klotzbuecher
committed
#endif
Markus Klotzbuecher
committed
#ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT
Markus Klotzbuecher
committed
/* board dependant init */
Markus Klotzbuecher
committed
return -1;
#endif
memset(&gohci, 0, sizeof(ohci_t));
Markus Klotzbuecher
committed
/* align the storage */
if ((__u32)&ghcca[0] & 0xff) {
err("HCCA not aligned!!");
return -1;
}
phcca = &ghcca[0];
info("aligned ghcca %p", phcca);
memset(&ohci_dev, 0, sizeof(struct ohci_device));
if ((__u32)&ohci_dev.ed[0] & 0x7) {
err("EDs not aligned!!");
return -1;
}
memset(gtd, 0, sizeof(td_t) * (NUM_TD + 1));
if ((__u32)gtd & 0x7) {
err("TDs not aligned!!");
return -1;
}
ptd = gtd;
gohci.hcca = phcca;
memset(phcca, 0, sizeof(struct ohci_hcca));
Markus Klotzbuecher
committed
gohci.disabled = 1;
gohci.sleeping = 0;
gohci.irq = -1;
#ifdef CONFIG_PCI_OHCI
pdev = pci_find_devices(ohci_pci_ids, CONFIG_PCI_OHCI_DEVNO);
if (pdev != -1) {
u16 vid, did;
u32 base;
pci_read_config_word(pdev, PCI_VENDOR_ID, &vid);
pci_read_config_word(pdev, PCI_DEVICE_ID, &did);
printf("OHCI pci controller (%04x, %04x) found @(%d:%d:%d)\n",
vid, did, (pdev >> 16) & 0xff,
(pdev >> 11) & 0x1f, (pdev >> 8) & 0x7);
pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &base);
printf("OHCI regs address 0x%08x\n", base);
gohci.regs = (struct ohci_regs *)base;
} else
return -1;
#else
gohci.regs = (struct ohci_regs *)CONFIG_SYS_USB_OHCI_REGS_BASE;
Markus Klotzbuecher
committed
gohci.flags = 0;
gohci.slot_name = CONFIG_SYS_USB_OHCI_SLOT_NAME;
Markus Klotzbuecher
committed
if (hc_reset (&gohci) < 0) {
hc_release_ohci (&gohci);
err ("can't reset usb-%s", gohci.slot_name);
#ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT
Markus Klotzbuecher
committed
/* board dependant cleanup */
usb_board_init_fail();
Markus Klotzbuecher
committed
#endif
#ifdef CONFIG_SYS_USB_OHCI_CPU_INIT
Markus Klotzbuecher
committed
/* cpu dependant cleanup */
usb_cpu_init_fail();
Markus Klotzbuecher
committed
#endif
Markus Klotzbuecher
committed
return -1;
}
if (hc_start(&gohci) < 0) {
err("can't start usb-%s", gohci.slot_name);
hc_release_ohci(&gohci);
Markus Klotzbuecher
committed
/* Initialization failed */
#ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT
Markus Klotzbuecher
committed
/* board dependant cleanup */
usb_board_stop();
#endif
#ifdef CONFIG_SYS_USB_OHCI_CPU_INIT
Markus Klotzbuecher
committed
/* cpu dependant cleanup */
usb_cpu_stop();
#endif
Markus Klotzbuecher
committed
return -1;
}
#ifdef DEBUG
Markus Klotzbuecher
committed
#else
wait_ms(1);
#endif
ohci_inited = 1;
return 0;
}
int usb_lowlevel_stop(void)
{
/* this gets called really early - before the controller has */
/* even been initialized! */
if (!ohci_inited)
return 0;
/* TODO release any interrupts, etc. */
/* call hc_release_ohci() here ? */
Markus Klotzbuecher
committed
#ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT