[U-Boot-Users] Patch: Add USB OHCI support for MPC8220 - part 2
TsiChung Liew
Tsi-Chung.Liew at freescale.com
Fri May 11 04:54:52 CEST 2007
- USB Ohci support
Regards,
TsiChung Liew
Signed-off by: TsiChung Liew<Tsi-Chung.Liew at freescale.com>
+/*-------------------------------------------------------------------------*/
+
+/* td done list */
+static int dl_done_list(ohci_t * ohci, td_t * td_list)
+{
+ td_t *td_list_next = NULL;
+ ed_t *ed;
+ int cc = 0;
+ int stat = 0xff;
+ /* urb_t *urb; */
+ urb_priv_t *lurb_priv;
+ __u32 tdINFO, edHeadP, edTailP;
+
+ while (td_list) {
+ td_list_next = td_list->next_dl_td;
+
+ lurb_priv = &urb_priv;
+ tdINFO = ohci_cpu_to_le32(td_list->hwINFO);
+
+ ed = td_list->ed;
+
+ dl_transfer_length(td_list);
+
+ /* error code of transfer */
+ cc = TD_CC_GET(tdINFO);
+ if (++(lurb_priv->td_cnt) == lurb_priv->length) {
+ if ((ed->state & (ED_OPER | ED_UNLINK))
+ && (lurb_priv->state != URB_DEL)) {
+ dbg("ConditionCode %#x", cc);
+ stat = cc_to_error[cc];
+ }
+ }
+
+ if (ed->state != ED_NEW) {
+ edHeadP = ohci_cpu_to_le32(ed->hwHeadP) & 0xfffffff0;
+ edTailP = ohci_cpu_to_le32(ed->hwTailP);
+
+ /* unlink eds if they are not busy */
+ if ((edHeadP == edTailP) && (ed->state == ED_OPER))
+ ep_unlink(ohci, ed);
+ }
+
+ td_list = td_list_next;
+ }
+ 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 */
+ 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]);}
+#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])
+#endif
+#define RD_RH_STAT roothub_status(&gohci)
+#define RD_RH_PORTSTAT roothub_portstatus(&gohci,wIndex-1)
+
+/* 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);
+ ndp = (temp & RH_A_NDP);
+
+ for (i = 0; i < ndp; i++) {
+ temp = roothub_portstatus(controller, i);
+ /* 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)
+{
+ void *data = buffer;
+ 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
+ urb_priv.actual_length = 0;
+ pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)",
+ usb_pipein(pipe));
+#endif
+ if ((pipe & PIPE_INTERRUPT) == PIPE_INTERRUPT) {
+ info("Root-Hub submit IRQ: NOT implemented");
+ return 0;
+ }
+
+ bmRType_bReq = cmd->requesttype | (cmd->request << 8);
+ wValue = m16_swap(cmd->value);
+ wIndex = m16_swap(cmd->index);
+ wLength = m16_swap(cmd->length);
+
+ 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 = m16_swap(1);
+ OK(2);
+ case RH_GET_STATUS | RH_INTERFACE:
+ *(__u16 *) data_buf = m16_swap(0);
+ OK(2);
+ case RH_GET_STATUS | RH_ENDPOINT:
+ *(__u16 *) data_buf = m16_swap(0);
+ OK(2);
+ case RH_GET_STATUS | RH_CLASS:
+ *(__u32 *) data_buf = m32_swap(RD_RH_STAT &
+ ~(RH_HS_CRWE | RH_HS_DRWE));
+ OK(4);
+ case RH_GET_STATUS | RH_OTHER | RH_CLASS:
+ *(__u32 *) data_buf = m32_swap(RD_RH_PORTSTAT);
+ OK(4);
+
+ case RH_CLEAR_FEATURE | RH_ENDPOINT:
+ switch (wValue) {
+ case (RH_ENDPOINT_STALL):
+ OK(0);
+ }
+ 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);
+ }
+ 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);
+ }
+ 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);
+ 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);
+ }
+ break;
+
+ case RH_SET_ADDRESS:
+ gohci.rh.devnum = wValue;
+ OK(0);
+
+ 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) {
+ len = min_t(unsigned int,
+ leni,
+ min_t(unsigned int,
+ sizeof(root_hub_str_index0),
+ wLength));
+ data_buf = root_hub_str_index0;
+ OK(len);
+ }
+
+ 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;
+ }
+ break;
+
+ case RH_GET_DESCRIPTOR | RH_CLASS:
+ {
+ __u32 temp = roothub_a(&gohci);
+
+ data_buf[0] = 9; /* min length; */
+ data_buf[1] = 0x29;
+ data_buf[2] = temp & RH_A_NDP;
+ 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? */
+ data_buf[3] |= 0x8;
+
+ /* corresponds to data_buf[4-7] */
+ datab[1] = 0;
+ data_buf[5] = (temp & RH_A_POTPGT) >> 24;
+ temp = roothub_b(&gohci);
+ 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));
+ OK(len);
+ }
+
+ case RH_GET_CONFIGURATION:
+ *(__u8 *) data_buf = 0x01;
+ OK(1);
+
+ case RH_SET_CONFIGURATION:
+ WR_RH_STAT(0x10000);
+ OK(0);
+
+ default:
+ dbg("unsupported root hub command");
+ stat = USB_ST_STALLED;
+ }
+
+#ifdef DEBUG
+ ohci_dump_roothub(&gohci, 1);
+#endif
+
+ len = min_t(int, len, leni);
+
+ if (data != data_buf)
+ memcpy(data, data_buf, len);
+
+ dev->act_len = len;
+ dev->status = stat;
+
+#ifdef DEBUG
+ if (transfer_len)
+ urb_priv.actual_length = transfer_len;
+ pkt_print(dev, pipe, buffer, transfer_len, cmd, "RET(rh)",
+ 0 /*usb_pipein(pipe) */ );
+#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;
+
+ /* device pulled? Shortcut the action. */
+ if (devgone == dev) {
+ dev->status = USB_ST_CRC_ERR;
+ return 0;
+ }
+#ifdef DEBUG
+ urb_priv.actual_length = 0;
+ pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB",
+ usb_pipein(pipe));
+#endif
+ if (!maxsize) {
+ err("submit_common_message: pipesize for pipe %lx is zero",
+ pipe);
+ return -1;
+ }
+
+ if (sohci_submit_job(dev, pipe, buffer, transfer_len, setup, interval)
<
+ 0) {
+ err("sohci_submit_job failed");
+ return -1;
+ }
+
+ /* allow more time for a BULK device to react - some are slow */
+#define BULK_TO 5000 /* timeout in milliseconds */
+ if (usb_pipetype(pipe) == PIPE_BULK)
+ 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;
+ }
+
+ if (stat >= 0 && stat < 0xff) {
+ /* 0xff is returned for an SF-interrupt */
+ break;
+ }
+
+ if (--timeout)
+ wait_ms(1);
+ else {
+ err("CTL:TIMEOUT ");
+ stat = USB_ST_CRC_ERR;
+ break;
+ }
+ }
+
+ /* we got an Root Hub Status Change interrupt */
+ if (got_rhsc) {
+#ifdef DEBUG
+ ohci_dump_roothub(&gohci, 1);
+#endif
+ got_rhsc = 0;
+ /* abuse timeout */
+ timeout = rh_check_port_status(&gohci);
+ if (timeout >= 0) {
+ /*
+ * XXX
+ * This is potentially dangerous because it assumes
+ * that only one device is ever plugged in!
+ */
+ devgone = dev;
+ }
+ }
+
+ dev->status = stat;
+ dev->act_len = transfer_len;
+
+#ifdef DEBUG
+ pkt_print(dev, pipe, buffer, transfer_len, setup, "RET(ctlr)",
+ usb_pipein(pipe));
+#endif
+
+ /* free TDs in urb_priv */
+ urb_free_priv(&urb_priv);
+ 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
+ urb_priv.actual_length = 0;
+ pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB",
+ usb_pipein(pipe));
+#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 -1;
+}
+
+/*-------------------------------------------------------------------------*
+ * HC functions
+
*-------------------------------------------------------------------------*/
+
+/* reset the HC and BUS */
+
+static int hc_reset(ohci_t * ohci)
+{
+ int timeout = 30;
+ int smm_timeout = 50; /* 0,5 sec */
+
+ if (readl(&ohci->regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC
*/
+ writel(OHCI_OCR, &ohci->regs->cmdstatus); /* request ownership */
+ info("USB HC TakeOver from SMM");
+
+ while (readl(&ohci->regs->control) & OHCI_CTRL_IR) {
+ wait_ms(10);
+ if (--smm_timeout == 0) {
+ err("USB HC TakeOver failed!");
+ return -1;
+ }
+ }
+ }
+
+ /* Disable HC interrupts */
+ writel(OHCI_INTR_MIE, &ohci->regs->intrdisable);
+
+ dbg("USB HC reset_hc usb-%s: ctrl = 0x%X ;",
+ ohci->slot_name, readl(&ohci->regs->control));
+
+ /* Reset USB (needed by some controllers) */
+ ohci->hc_control = 0;
+ writel(ohci->hc_control, &ohci->regs->control);
+
+ /* HC Reset requires max 10 us delay */
+ writel(OHCI_HCR, &ohci->regs->cmdstatus);
+
+ while ((readl(&ohci->regs->cmdstatus) & OHCI_HCR) != 0) {
+ if (--timeout == 0) {
+ err("USB HC reset timed out!");
+ return -1;
+ }
+ udelay(1);
+ }
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Start an OHCI controller, set the BUS operational
+ * enable interrupts
+ * connect the virtual root hub */
+
+static int hc_start(ohci_t * ohci)
+{
+ __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);
+
+ writel((__u32) ohci->hcca, &ohci->regs->hcca); /* a reset clears this
*/
+
+ fminterval = 0x2edf;
+ writel((fminterval * 9) / 10, &ohci->regs->periodicstart);
+ fminterval |= ((((fminterval - 210) * 6) / 7) << 16);
+ writel(fminterval, &ohci->regs->fminterval);
+ writel(0x628, &ohci->regs->lsthresh);
+
+ /* start controller operations */
+ ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
+ ohci->disabled = 0;
+ writel(ohci->hc_control, &ohci->regs->control);
+
+ /* 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);
+ /* clear all interrupts */
+ mask &= ~OHCI_INTR_MIE;
+ writel(mask, &ohci->regs->intrstatus);
+ /* 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);
+
+#ifdef OHCI_USE_NPS
+ /* required for AMD-756 and some Mac platforms */
+ writel((roothub_a(ohci) | RH_A_NPS) & ~RH_A_PSM,
+ &ohci->regs->roothub.a);
+ writel(RH_HS_LPSC, &ohci->regs->roothub.status);
+#endif /* OHCI_USE_NPS */
+
+#define mdelay(n) ({unsigned long msec=(n); while (msec--) udelay
(1000);})
+ /* POTPGT delay is bits 24-31, in 2 ms units. */
+ mdelay((roothub_a(ohci) >> 23) & 0x1fe);
+
+ /* connect the virtual root hub */
+ ohci->rh.devnum = 0;
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* an interrupt happens */
+
+static int hc_interrupt(void)
+{
+ ohci_t *ohci = &gohci;
+ struct ohci_regs *regs = ohci->regs;
+ int ints;
+ int stat = -1;
+
+ if ((ohci->hcca->done_head != 0)
+ && !(ohci_cpu_to_le32(ohci->hcca->done_head) & 0x01))
+ ints = OHCI_INTR_WDH;
+ else
+ ints = readl(®s->intrstatus);
+
+ /* dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca-
>frame_no)); */
+
+ if (ints & OHCI_INTR_RHSC)
+ got_rhsc = 1;
+
+ if (ints & OHCI_INTR_UE) {
+ ohci->disabled++;
+ err("OHCI Unrecoverable Error, controller usb-%s disabled",
+ ohci->slot_name);
+ /* e.g. due to PCI Master/Target Abort */
+
+#ifdef DEBUG
+ ohci_dump(ohci, 1);
+#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... */
+ hc_reset(ohci);
+ return -1;
+ }
+
+ if (ints & OHCI_INTR_WDH) {
+ writel(OHCI_INTR_WDH, ®s->intrdisable);
+ stat = dl_done_list(&gohci, dl_reverse_done_list(&gohci));
+ writel(OHCI_INTR_WDH, ®s->intrenable);
+ }
+
+ if (ints & OHCI_INTR_SO) {
+ dbg("USB Schedule overrun\n");
+ writel(OHCI_INTR_SO, ®s->intrenable);
+ stat = -1;
+ }
+
+ /* FIXME: this assumes SOF (1/ms) interrupts don't get lost... */
+ if (ints & OHCI_INTR_SF) {
+ unsigned int frame = ohci_cpu_to_le16(ohci->hcca->frame_no) & 1;
+ writel(OHCI_INTR_SF, ®s->intrdisable);
+
+ if (ohci->ed_rm_list[frame] != NULL)
+ writel(OHCI_INTR_SF, ®s->intrenable);
+
+ stat = 0xff;
+ }
+
+ writel(ints, ®s->intrstatus);
+ return stat;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*-------------------------------------------------------------------------*/
+
+/* De-allocate all resources.. */
+
+static void hc_release_ohci(ohci_t * ohci)
+{
+ dbg("USB HC release ohci usb-%s", ohci->slot_name);
+
+ if (!ohci->disabled)
+ hc_reset(ohci);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * low level initalisation routine, called from usb.c
+ */
+static char ohci_inited = 0;
+
+int usb_lowlevel_init(void)
+{
+ /* Set the USB VBus */
+ *(vu_long *) (CFG_FPGA_BASE + 0x08) |= 0x00000060;
+
+ memset(&gohci, 0, sizeof(ohci_t));
+ memset(&urb_priv, 0, sizeof(urb_priv_t));
+
+ /* 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));
+
+ gohci.disabled = 1;
+ gohci.sleeping = 0;
+ gohci.irq = -1;
+ gohci.regs = (struct ohci_regs *)MMAP_USBH;
+
+ gohci.flags = 0;
+ gohci.slot_name = "mpc8220";
+
+ if (hc_reset(&gohci) < 0) {
+ hc_release_ohci(&gohci);
+ return -1;
+ }
+
+ if (hc_start(&gohci) < 0) {
+ err("can't start usb-%s", gohci.slot_name);
+ hc_release_ohci(&gohci);
+ return -1;
+ }
+#ifdef DEBUG
+ ohci_dump(&gohci, 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 ? */
+ hc_reset(&gohci);
+ return 0;
+}
+
+#endif /* CONFIG_USB_OHCI */
More information about the U-Boot
mailing list