diff options
author | Wolfgang Denk <wd@castor.denx.de> | 2005-07-21 11:57:57 +0200 |
---|---|---|
committer | Wolfgang Denk <wd@castor.denx.de> | 2005-07-21 11:57:57 +0200 |
commit | 9c998aa83148c75cd734a18958218926313bd54c (patch) | |
tree | c6d523801214c37337d3995dd1c405a7c329b37d /cpu | |
parent | f530187dbd69b0534e84b553f9a9803b16ed4999 (diff) |
Fix low-level OHCI transfers for ARM920t and MPC5xxx
A new, Windows compatible init sequence was also backported from Linux 2.6,
but disabled with #undef NEW_INIT_SEQ as it wouldn't change the behaviour
of the memopry sticks we tested. Maybe it's not relevant for mass storage
devices. For recerence, see file common/usb.c, function usb_new_device(),
section #ifdef NEW_INIT_SEQ.
Diffstat (limited to 'cpu')
-rw-r--r-- | cpu/arm920t/s3c24x0/usb_ohci.c | 68 | ||||
-rw-r--r-- | cpu/arm920t/s3c24x0/usb_ohci.h | 2 | ||||
-rw-r--r-- | cpu/mpc5xxx/usb_ohci.c | 58 | ||||
-rw-r--r-- | cpu/mpc5xxx/usb_ohci.h | 1 |
4 files changed, 113 insertions, 16 deletions
diff --git a/cpu/arm920t/s3c24x0/usb_ohci.c b/cpu/arm920t/s3c24x0/usb_ohci.c index c5dac27680e..fa6abeb546b 100644 --- a/cpu/arm920t/s3c24x0/usb_ohci.c +++ b/cpu/arm920t/s3c24x0/usb_ohci.c @@ -94,6 +94,8 @@ urb_priv_t urb_priv; int got_rhsc; /* device which was disconnected */ struct usb_device *devgone; +/* flag guarding URB transation */ +int urb_finished = 0; /*-------------------------------------------------------------------------*/ @@ -398,6 +400,16 @@ int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer, return -1; } + /* if we have an unfinished URB from previous transaction let's + * fail and scream as quickly as possible so as not to corrupt + * further communication */ + if (!urb_finished) { + err("sohci_submit_job: URB NOT FINISHED"); + return -1; + } + /* we're about to begin a new transaction here so mark the URB unfinished */ + urb_finished = 0; + /* every endpoint has a ed, locate and fill it */ if (!(ed = ep_add_ed (dev, pipe))) { err("sohci_submit_job: ENOMEM"); @@ -658,7 +670,6 @@ static void td_fill (ohci_t *ohci, unsigned int info, else td->hwBE = 0; td->hwNextTD = m32_swap (td_pt); - td->hwPSW [0] = m16_swap (((__u32)data & 0x0FFF) | 0xE000); /* append to queue */ td->ed->hwTailP = td->hwNextTD; @@ -793,6 +804,7 @@ static td_t * dl_reverse_done_list (ohci_t *ohci) td_rev = td_list; td_list_hc = m32_swap (td_list->hwNextTD) & 0xfffffff0; } + return td_list; } @@ -826,6 +838,17 @@ static int dl_done_list (ohci_t *ohci, td_t *td_list) 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) { + if ((ed->state & (ED_OPER | ED_UNLINK))) + urb_finished = 1; + else + dbg("dl_done_list: strange.., ED state %x, ed->state\n"); + } else + dbg("dl_done_list: processing TD %x, len %x\n", lurb_priv->td_cnt, + lurb_priv->length); + if (ed->state != ED_NEW) { edHeadP = m32_swap (ed->hwHeadP) & 0xfffffff0; edTailP = m32_swap (ed->hwTailP); @@ -1197,6 +1220,8 @@ pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe)); return stat; } + + /*-------------------------------------------------------------------------*/ /* common code for handling submit messages - used for all but root hub */ @@ -1245,22 +1270,41 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, for (;;) { /* check whether the controller is done */ stat = hc_interrupt(); + if (stat < 0) { stat = USB_ST_CRC_ERR; break; } - if (stat >= 0 && stat != 0xff) { + + /* 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)) { /* 0xff is returned for an SF-interrupt */ break; } + if (--timeout) { wait_ms(1); + if (!urb_finished) + dbg("\%"); + } else { err("CTL:TIMEOUT "); + dbg("submit_common_msg: TO status %x\n", stat); stat = USB_ST_CRC_ERR; + urb_finished = 1; break; } } + +#if 0 /* we got an Root Hub Status Change interrupt */ if (got_rhsc) { #ifdef DEBUG @@ -1282,6 +1326,7 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, devgone = dev; } } +#endif dev->status = stat; dev->act_len = transfer_len; @@ -1457,16 +1502,26 @@ hc_interrupt (void) int ints; int stat = -1; - if ((ohci->hcca->done_head != 0) && !(m32_swap (ohci->hcca->done_head) & 0x01)) { + if ((ohci->hcca->done_head != 0) && + !(m32_swap (ohci->hcca->done_head) & 0x01)) { + ints = OHCI_INTR_WDH; - } else { - ints = readl (®s->intrstatus); + + } else if ((ints = readl (®s->intrstatus)) == ~(u32)0) { + ohci->disabled++; + err ("%s device removed!", ohci->slot_name); + return -1; + + } else if ((ints &= readl (®s->intrenable)) == 0) { + dbg("hc_interrupt: returning..\n"); + return 0xff; } /* dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no)); */ if (ints & OHCI_INTR_RHSC) { got_rhsc = 1; + stat = 0xff; } if (ints & OHCI_INTR_UE) { @@ -1490,6 +1545,7 @@ hc_interrupt (void) if (ints & OHCI_INTR_WDH) { wait_ms(1); + writel (OHCI_INTR_WDH, ®s->intrdisable); stat = dl_done_list (&gohci, dl_reverse_done_list (&gohci)); writel (OHCI_INTR_WDH, ®s->intrenable); @@ -1610,6 +1666,8 @@ int usb_lowlevel_init(void) wait_ms(1); #endif ohci_inited = 1; + urb_finished = 1; + return 0; } diff --git a/cpu/arm920t/s3c24x0/usb_ohci.h b/cpu/arm920t/s3c24x0/usb_ohci.h index fab0e65a381..5e9a0fdfc4e 100644 --- a/cpu/arm920t/s3c24x0/usb_ohci.h +++ b/cpu/arm920t/s3c24x0/usb_ohci.h @@ -30,7 +30,6 @@ static int cc_to_error[16] = { }; /* ED States */ - #define ED_NEW 0x00 #define ED_UNLINK 0x01 #define ED_OPER 0x02 @@ -104,7 +103,6 @@ struct td { __u32 hwNextTD; /* Next TD Pointer */ __u32 hwBE; /* Memory Buffer End Pointer */ - __u16 hwPSW[MAXPSW]; __u8 unused; __u8 index; struct ed *ed; diff --git a/cpu/mpc5xxx/usb_ohci.c b/cpu/mpc5xxx/usb_ohci.c index 88068262069..2f19d7e9220 100644 --- a/cpu/mpc5xxx/usb_ohci.c +++ b/cpu/mpc5xxx/usb_ohci.c @@ -98,6 +98,8 @@ urb_priv_t urb_priv; int got_rhsc; /* device which was disconnected */ struct usb_device *devgone; +/* flag guarding URB transation */ +int urb_finished = 0; /*-------------------------------------------------------------------------*/ @@ -402,6 +404,16 @@ int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer, return -1; } + /* if we have an unfinished URB from previous transaction let's + * fail and scream as quickly as possible so as not to corrupt + * further communication */ + if (!urb_finished) { + err("sohci_submit_job: URB NOT FINISHED"); + return -1; + } + /* we're about to begin a new transaction here so mark the URB unfinished */ + urb_finished = 0; + /* every endpoint has a ed, locate and fill it */ if (!(ed = ep_add_ed (dev, pipe))) { err("sohci_submit_job: ENOMEM"); @@ -664,7 +676,6 @@ static void td_fill (ohci_t *ohci, unsigned int info, else td->hwBE = 0; td->hwNextTD = ohci_cpu_to_le32 ((unsigned long)td_pt); - td->hwPSW [0] = ohci_cpu_to_le16 (((__u32)data & 0x0FFF) | 0xE000); /* append to queue */ td->ed->hwTailP = td->hwNextTD; @@ -673,7 +684,6 @@ static void td_fill (ohci_t *ohci, unsigned int info, /*-------------------------------------------------------------------------*/ /* prepare all TDs of a transfer */ - static void td_submit_job (struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len, struct devrequest *setup, urb_priv_t *urb, int interval) { @@ -813,7 +823,7 @@ 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; + int stat = 0; /* urb_t *urb; */ urb_priv_t *lurb_priv; __u32 tdINFO, edHeadP, edTailP; @@ -835,6 +845,7 @@ static int dl_done_list (ohci_t *ohci, td_t *td_list) && (lurb_priv->state != URB_DEL)) { dbg("ConditionCode %#x", cc); stat = cc_to_error[cc]; + urb_finished = 1; } } @@ -1250,18 +1261,35 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, stat = USB_ST_CRC_ERR; break; } - if (stat >= 0 && stat < 0xff) { + + /* 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)) { /* 0xff is returned for an SF-interrupt */ break; } + if (--timeout) { wait_ms(1); + if (!urb_finished) + dbg("\%"); + } else { err("CTL:TIMEOUT "); + dbg("submit_common_msg: TO status %x\n", stat); stat = USB_ST_CRC_ERR; + urb_finished = 1; break; } } +#if 0 /* we got an Root Hub Status Change interrupt */ if (got_rhsc) { #ifdef DEBUG @@ -1283,6 +1311,7 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, devgone = dev; } } +#endif dev->status = stat; dev->act_len = transfer_len; @@ -1454,17 +1483,27 @@ hc_interrupt (void) 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)) { - if ((ohci->hcca->done_head != 0) && !(ohci_cpu_to_le32 (ohci->hcca->done_head) & 0x01)) { - ints = OHCI_INTR_WDH; - } else { - ints = readl (®s->intrstatus); + ints = OHCI_INTR_WDH; + + } else if ((ints = readl (®s->intrstatus)) == ~(u32)0) { + ohci->disabled++; + err ("%s device removed!", ohci->slot_name); + return -1; + + } else if ((ints &= readl (®s->intrenable)) == 0) { + dbg("hc_interrupt: returning..\n"); + return 0xff; } /* dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no)); */ if (ints & OHCI_INTR_RHSC) { got_rhsc = 1; + stat = 0xff; } if (ints & OHCI_INTR_UE) { @@ -1499,6 +1538,7 @@ hc_interrupt (void) /* 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; + wait_ms(1); writel (OHCI_INTR_SF, ®s->intrdisable); if (ohci->ed_rm_list[frame] != NULL) writel (OHCI_INTR_SF, ®s->intrenable); @@ -1589,6 +1629,8 @@ int usb_lowlevel_init(void) ohci_dump (&gohci, 1); #endif ohci_inited = 1; + urb_finished = 1; + return 0; } diff --git a/cpu/mpc5xxx/usb_ohci.h b/cpu/mpc5xxx/usb_ohci.h index 11b361af044..884f1d5e519 100644 --- a/cpu/mpc5xxx/usb_ohci.h +++ b/cpu/mpc5xxx/usb_ohci.h @@ -104,7 +104,6 @@ struct td { __u32 hwNextTD; /* Next TD Pointer */ __u32 hwBE; /* Memory Buffer End Pointer */ - __u16 hwPSW[MAXPSW]; __u8 unused; __u8 index; struct ed *ed; |