diff options
Diffstat (limited to 'drivers/usb/host/isp1760-hcd.c')
-rw-r--r-- | drivers/usb/host/isp1760-hcd.c | 1638 |
1 files changed, 773 insertions, 865 deletions
diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 7b2e69aa2e98..c9e6e454c625 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -8,6 +8,8 @@ * * (c) 2007 Sebastian Siewior <bigeasy@linutronix.de> * + * (c) 2011 Arvid Brodin <arvid.brodin@enea.com> + * */ #include <linux/module.h> #include <linux/kernel.h> @@ -26,14 +28,18 @@ static struct kmem_cache *qtd_cachep; static struct kmem_cache *qh_cachep; +static struct kmem_cache *urb_listitem_cachep; struct isp1760_hcd { u32 hcs_params; spinlock_t lock; - struct inter_packet_info atl_ints[32]; - struct inter_packet_info int_ints[32]; + struct slotinfo atl_slots[32]; + int atl_done_map; + struct slotinfo int_slots[32]; + int int_done_map; struct memory_chunk memory_pool[BLOCKS]; - u32 atl_queued; + struct list_head controlqhs, bulkqhs, interruptqhs; + int active_ptds; /* periodic schedule support */ #define DEFAULT_I_TDPS 1024 @@ -85,18 +91,34 @@ struct isp1760_qtd { struct list_head qtd_list; struct urb *urb; size_t length; - - /* isp special*/ + size_t actual_length; + + /* QTD_ENQUEUED: waiting for transfer (inactive) */ + /* QTD_PAYLOAD_ALLOC: chip mem has been allocated for payload */ + /* QTD_XFER_STARTED: valid ptd has been written to isp176x - only + interrupt handler may touch this qtd! */ + /* QTD_XFER_COMPLETE: payload has been transferred successfully */ + /* QTD_RETIRE: transfer error/abort qtd */ +#define QTD_ENQUEUED 0 +#define QTD_PAYLOAD_ALLOC 1 +#define QTD_XFER_STARTED 2 +#define QTD_XFER_COMPLETE 3 +#define QTD_RETIRE 4 u32 status; -#define URB_ENQUEUED (1 << 1) }; +/* Queue head, one for each active endpoint */ struct isp1760_qh { - /* first part defined by EHCI spec */ + struct list_head qh_list; struct list_head qtd_list; - u32 toggle; u32 ping; + int slot; +}; + +struct urb_listitem { + struct list_head urb_list; + struct urb *urb; }; /* @@ -272,7 +294,7 @@ static void init_memory(struct isp1760_hcd *priv) payload_addr += priv->memory_pool[curr + i].size; } - BUG_ON(payload_addr - priv->memory_pool[0].start > PAYLOAD_AREA_SIZE); + WARN_ON(payload_addr - priv->memory_pool[0].start > PAYLOAD_AREA_SIZE); } static void alloc_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd) @@ -280,7 +302,7 @@ static void alloc_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd) struct isp1760_hcd *priv = hcd_to_priv(hcd); int i; - BUG_ON(qtd->payload_addr); + WARN_ON(qtd->payload_addr); if (!qtd->length) return; @@ -293,19 +315,6 @@ static void alloc_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd) return; } } - - dev_err(hcd->self.controller, - "%s: Cannot allocate %zu bytes of memory\n" - "Current memory map:\n", - __func__, qtd->length); - for (i = 0; i < BLOCKS; i++) { - dev_err(hcd->self.controller, "Pool %2d size %4d status: %d\n", - i, priv->memory_pool[i].size, - priv->memory_pool[i].free); - } - /* XXX maybe -ENOMEM could be possible */ - BUG(); - return; } static void free_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd) @@ -318,7 +327,7 @@ static void free_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd) for (i = 0; i < BLOCKS; i++) { if (priv->memory_pool[i].start == qtd->payload_addr) { - BUG_ON(priv->memory_pool[i].free); + WARN_ON(priv->memory_pool[i].free); priv->memory_pool[i].free = 1; qtd->payload_addr = 0; return; @@ -327,19 +336,8 @@ static void free_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd) dev_err(hcd->self.controller, "%s: Invalid pointer: %08x\n", __func__, qtd->payload_addr); - BUG(); -} - -static void isp1760_init_regs(struct usb_hcd *hcd) -{ - reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, 0); - reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE); - reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE); - reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE); - - reg_write32(hcd->regs, HC_ATL_PTD_DONEMAP_REG, ~NO_TRANSFER_ACTIVE); - reg_write32(hcd->regs, HC_INT_PTD_DONEMAP_REG, ~NO_TRANSFER_ACTIVE); - reg_write32(hcd->regs, HC_ISO_PTD_DONEMAP_REG, ~NO_TRANSFER_ACTIVE); + WARN_ON(1); + qtd->payload_addr = 0; } static int handshake(struct usb_hcd *hcd, u32 reg, @@ -377,31 +375,27 @@ static int ehci_reset(struct usb_hcd *hcd) return retval; } -static void qh_destroy(struct isp1760_qh *qh) -{ - BUG_ON(!list_empty(&qh->qtd_list)); - kmem_cache_free(qh_cachep, qh); -} - -static struct isp1760_qh *isp1760_qh_alloc(gfp_t flags) +static struct isp1760_qh *qh_alloc(gfp_t flags) { struct isp1760_qh *qh; qh = kmem_cache_zalloc(qh_cachep, flags); if (!qh) - return qh; + return NULL; + INIT_LIST_HEAD(&qh->qh_list); INIT_LIST_HEAD(&qh->qtd_list); + qh->slot = -1; + return qh; } -/* magic numbers that can affect system performance */ -#define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ -#define EHCI_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ -#define EHCI_TUNE_RL_TT 0 -#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ -#define EHCI_TUNE_MULT_TT 1 -#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */ +static void qh_free(struct isp1760_qh *qh) +{ + WARN_ON(!list_empty(&qh->qtd_list)); + WARN_ON(qh->slot > -1); + kmem_cache_free(qh_cachep, qh); +} /* one-time init, only for memory state */ static int priv_init(struct usb_hcd *hcd) @@ -411,6 +405,10 @@ static int priv_init(struct usb_hcd *hcd) spin_lock_init(&priv->lock); + INIT_LIST_HEAD(&priv->interruptqhs); + INIT_LIST_HEAD(&priv->controlqhs); + INIT_LIST_HEAD(&priv->bulkqhs); + /* * hw default: 1K periodic list heads, one per frame. * periodic_size can shrink by USBCMD update if hcc_params allows. @@ -468,7 +466,10 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) } /* pre reset */ - isp1760_init_regs(hcd); + reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, 0); + reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE); + reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE); + reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE); /* reset */ reg_write32(hcd->regs, HC_RESET_REG, SW_RESET_RESET_ALL); @@ -488,12 +489,15 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) 16 : 32, (priv->devflags & ISP1760_FLAG_ANALOG_OC) ? "analog" : "digital"); + /* This is weird: at the first plug-in of a device there seems to be + one packet queued that never gets returned? */ + priv->active_ptds = -1; + /* ATL reset */ reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode | ALL_ATX_RESET); mdelay(10); reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode); - reg_write32(hcd->regs, HC_INTERRUPT_REG, INTERRUPT_ENABLE_MASK); reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, INTERRUPT_ENABLE_MASK); /* @@ -516,14 +520,21 @@ static void isp1760_init_maps(struct usb_hcd *hcd) reg_write32(hcd->regs, HC_ATL_PTD_LASTPTD_REG, 0x80000000); reg_write32(hcd->regs, HC_INT_PTD_LASTPTD_REG, 0x80000000); reg_write32(hcd->regs, HC_ISO_PTD_LASTPTD_REG, 0x00000001); + + reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, 0xffffffff); + reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, 0xffffffff); + reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, 0xffffffff); + + reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, + ATL_BUF_FILL | INT_BUF_FILL); } static void isp1760_enable_interrupts(struct usb_hcd *hcd) { reg_write32(hcd->regs, HC_ATL_IRQ_MASK_AND_REG, 0); - reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, 0); + reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, 0xffffffff); reg_write32(hcd->regs, HC_INT_IRQ_MASK_AND_REG, 0); - reg_write32(hcd->regs, HC_INT_IRQ_MASK_OR_REG, 0); + reg_write32(hcd->regs, HC_INT_IRQ_MASK_OR_REG, 0xffffffff); reg_write32(hcd->regs, HC_ISO_IRQ_MASK_AND_REG, 0); reg_write32(hcd->regs, HC_ISO_IRQ_MASK_OR_REG, 0xffffffff); /* step 23 passed */ @@ -548,8 +559,7 @@ static int isp1760_run(struct usb_hcd *hcd) command |= CMD_RUN; reg_write32(hcd->regs, HC_USBCMD, command); - retval = handshake(hcd, HC_USBCMD, CMD_RUN, CMD_RUN, - 250 * 1000); + retval = handshake(hcd, HC_USBCMD, CMD_RUN, CMD_RUN, 250 * 1000); if (retval) return retval; @@ -598,12 +608,19 @@ static int last_qtd_of_urb(struct isp1760_qtd *qtd, struct isp1760_qh *qh) return (qtd->urb != urb); } -static void transform_into_atl(struct isp1760_qh *qh, +/* magic numbers that can affect system performance */ +#define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ +#define EHCI_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ +#define EHCI_TUNE_RL_TT 0 +#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ +#define EHCI_TUNE_MULT_TT 1 +#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */ + +static void create_ptd_atl(struct isp1760_qh *qh, struct isp1760_qtd *qtd, struct ptd *ptd) { u32 maxpacket; u32 multi; - u32 pid_code; u32 rl = RL_COUNTER; u32 nak = NAK_COUNTER; @@ -616,67 +633,62 @@ static void transform_into_atl(struct isp1760_qh *qh, maxpacket &= 0x7ff; /* DW0 */ - ptd->dw0 = PTD_VALID; - ptd->dw0 |= PTD_LENGTH(qtd->length); - ptd->dw0 |= PTD_MAXPACKET(maxpacket); - ptd->dw0 |= PTD_ENDPOINT(usb_pipeendpoint(qtd->urb->pipe)); + ptd->dw0 = DW0_VALID_BIT; + ptd->dw0 |= TO_DW0_LENGTH(qtd->length); + ptd->dw0 |= TO_DW0_MAXPACKET(maxpacket); + ptd->dw0 |= TO_DW0_ENDPOINT(usb_pipeendpoint(qtd->urb->pipe)); /* DW1 */ ptd->dw1 = usb_pipeendpoint(qtd->urb->pipe) >> 1; - ptd->dw1 |= PTD_DEVICE_ADDR(usb_pipedevice(qtd->urb->pipe)); - - pid_code = qtd->packet_type; - ptd->dw1 |= PTD_PID_TOKEN(pid_code); + ptd->dw1 |= TO_DW1_DEVICE_ADDR(usb_pipedevice(qtd->urb->pipe)); + ptd->dw1 |= TO_DW1_PID_TOKEN(qtd->packet_type); if (usb_pipebulk(qtd->urb->pipe)) - ptd->dw1 |= PTD_TRANS_BULK; + ptd->dw1 |= DW1_TRANS_BULK; else if (usb_pipeint(qtd->urb->pipe)) - ptd->dw1 |= PTD_TRANS_INT; + ptd->dw1 |= DW1_TRANS_INT; if (qtd->urb->dev->speed != USB_SPEED_HIGH) { /* split transaction */ - ptd->dw1 |= PTD_TRANS_SPLIT; + ptd->dw1 |= DW1_TRANS_SPLIT; if (qtd->urb->dev->speed == USB_SPEED_LOW) - ptd->dw1 |= PTD_SE_USB_LOSPEED; + ptd->dw1 |= DW1_SE_USB_LOSPEED; - ptd->dw1 |= PTD_PORT_NUM(qtd->urb->dev->ttport); - ptd->dw1 |= PTD_HUB_NUM(qtd->urb->dev->tt->hub->devnum); + ptd->dw1 |= TO_DW1_PORT_NUM(qtd->urb->dev->ttport); + ptd->dw1 |= TO_DW1_HUB_NUM(qtd->urb->dev->tt->hub->devnum); /* SE bit for Split INT transfers */ if (usb_pipeint(qtd->urb->pipe) && (qtd->urb->dev->speed == USB_SPEED_LOW)) ptd->dw1 |= 2 << 16; - ptd->dw3 = 0; rl = 0; nak = 0; } else { - ptd->dw0 |= PTD_MULTI(multi); + ptd->dw0 |= TO_DW0_MULTI(multi); if (usb_pipecontrol(qtd->urb->pipe) || usb_pipebulk(qtd->urb->pipe)) - ptd->dw3 = qh->ping; - else - ptd->dw3 = 0; + ptd->dw3 |= TO_DW3_PING(qh->ping); } /* DW2 */ ptd->dw2 = 0; - ptd->dw2 |= PTD_DATA_START_ADDR(base_to_chip(qtd->payload_addr)); - ptd->dw2 |= PTD_RL_CNT(rl); - ptd->dw3 |= PTD_NAC_CNT(nak); + ptd->dw2 |= TO_DW2_DATA_START_ADDR(base_to_chip(qtd->payload_addr)); + ptd->dw2 |= TO_DW2_RL(rl); /* DW3 */ - ptd->dw3 |= qh->toggle; + ptd->dw3 |= TO_DW3_NAKCOUNT(nak); + ptd->dw3 |= TO_DW3_DATA_TOGGLE(qh->toggle); if (usb_pipecontrol(qtd->urb->pipe)) { if (qtd->data_buffer == qtd->urb->setup_packet) - ptd->dw3 &= ~PTD_DATA_TOGGLE(1); + ptd->dw3 &= ~TO_DW3_DATA_TOGGLE(1); else if (last_qtd_of_urb(qtd, qh)) - ptd->dw3 |= PTD_DATA_TOGGLE(1); + ptd->dw3 |= TO_DW3_DATA_TOGGLE(1); } - ptd->dw3 |= PTD_ACTIVE; + ptd->dw3 |= DW3_ACTIVE_BIT; /* Cerr */ - ptd->dw3 |= PTD_CERR(ERR_COUNTER); + ptd->dw3 |= TO_DW3_CERR(ERR_COUNTER); } static void transform_add_int(struct isp1760_qh *qh, @@ -731,197 +743,13 @@ static void transform_add_int(struct isp1760_qh *qh, ptd->dw4 = usof; } -static void transform_into_int(struct isp1760_qh *qh, +static void create_ptd_int(struct isp1760_qh *qh, struct isp1760_qtd *qtd, struct ptd *ptd) { - transform_into_atl(qh, qtd, ptd); + create_ptd_atl(qh, qtd, ptd); transform_add_int(qh, qtd, ptd); } -static int qtd_fill(struct isp1760_qtd *qtd, void *databuffer, size_t len, - u32 token) -{ - int count; - - qtd->data_buffer = databuffer; - qtd->packet_type = GET_QTD_TOKEN_TYPE(token); - - if (len > MAX_PAYLOAD_SIZE) - count = MAX_PAYLOAD_SIZE; - else - count = len; - - qtd->length = count; - return count; -} - -static int check_error(struct usb_hcd *hcd, struct ptd *ptd) -{ - int error = 0; - - if (ptd->dw3 & DW3_HALT_BIT) { - error = -EPIPE; - - if (ptd->dw3 & DW3_ERROR_BIT) - pr_err("error bit is set in DW3\n"); - } - - if (ptd->dw3 & DW3_QTD_ACTIVE) { - dev_err(hcd->self.controller, "Transfer active bit is set DW3\n" - "nak counter: %d, rl: %d\n", - (ptd->dw3 >> 19) & 0xf, (ptd->dw2 >> 25) & 0xf); - } - - return error; -} - -static void check_int_err_status(struct usb_hcd *hcd, u32 dw4) -{ - u32 i; - - dw4 >>= 8; - - for (i = 0; i < 8; i++) { - switch (dw4 & 0x7) { - case INT_UNDERRUN: - dev_err(hcd->self.controller, "Underrun (%d)\n", i); - break; - - case INT_EXACT: - dev_err(hcd->self.controller, - "Transaction error (%d)\n", i); - break; - - case INT_BABBLE: - dev_err(hcd->self.controller, "Babble error (%d)\n", i); - break; - } - dw4 >>= 3; - } -} - -static void enqueue_one_qtd(struct usb_hcd *hcd, struct isp1760_qtd *qtd) -{ - if (qtd->length && (qtd->length <= MAX_PAYLOAD_SIZE)) { - switch (qtd->packet_type) { - case IN_PID: - break; - case OUT_PID: - case SETUP_PID: - mem_writes8(hcd->regs, qtd->payload_addr, - qtd->data_buffer, qtd->length); - } - } -} - -static void enqueue_one_atl_qtd(struct usb_hcd *hcd, struct isp1760_qh *qh, - u32 slot, struct isp1760_qtd *qtd) -{ - struct isp1760_hcd *priv = hcd_to_priv(hcd); - struct ptd ptd; - - alloc_mem(hcd, qtd); - transform_into_atl(qh, qtd, &ptd); - ptd_write(hcd->regs, ATL_PTD_OFFSET, slot, &ptd); - enqueue_one_qtd(hcd, qtd); - - priv->atl_ints[slot].qh = qh; - priv->atl_ints[slot].qtd = qtd; - qtd->status |= URB_ENQUEUED; - qtd->status |= slot << 16; -} - -static void enqueue_one_int_qtd(struct usb_hcd *hcd, struct isp1760_qh *qh, - u32 slot, struct isp1760_qtd *qtd) -{ - struct isp1760_hcd *priv = hcd_to_priv(hcd); - struct ptd ptd; - - alloc_mem(hcd, qtd); - transform_into_int(qh, qtd, &ptd); - ptd_write(hcd->regs, INT_PTD_OFFSET, slot, &ptd); - enqueue_one_qtd(hcd, qtd); - - priv->int_ints[slot].qh = qh; - priv->int_ints[slot].qtd = qtd; - qtd->status |= URB_ENQUEUED; - qtd->status |= slot << 16; -} - -static void enqueue_an_ATL_packet(struct usb_hcd *hcd, struct isp1760_qh *qh, - struct isp1760_qtd *qtd) -{ - struct isp1760_hcd *priv = hcd_to_priv(hcd); - u32 skip_map, or_map; - u32 slot; - u32 buffstatus; - - /* - * When this function is called from the interrupt handler to enqueue - * a follow-up packet, the SKIP register gets written and read back - * almost immediately. With ISP1761, this register requires a delay of - * 195ns between a write and subsequent read (see section 15.1.1.3). - */ - mmiowb(); - ndelay(195); - skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG); - - BUG_ON(!skip_map); - slot = __ffs(skip_map); - - enqueue_one_atl_qtd(hcd, qh, slot, qtd); - - or_map = reg_read32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG); - or_map |= (1 << slot); - reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, or_map); - - skip_map &= ~(1 << slot); - reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, skip_map); - - priv->atl_queued++; - if (priv->atl_queued == 2) - reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, - INTERRUPT_ENABLE_SOT_MASK); - - buffstatus = reg_read32(hcd->regs, HC_BUFFER_STATUS_REG); - buffstatus |= ATL_BUFFER; - reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, buffstatus); -} - -static void enqueue_an_INT_packet(struct usb_hcd *hcd, struct isp1760_qh *qh, - struct isp1760_qtd *qtd) -{ - u32 skip_map, or_map; - u32 slot; - u32 buffstatus; - - /* - * When this function is called from the interrupt handler to enqueue - * a follow-up packet, the SKIP register gets written and read back - * almost immediately. With ISP1761, this register requires a delay of - * 195ns between a write and subsequent read (see section 15.1.1.3). - */ - mmiowb(); - ndelay(195); - skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG); - - BUG_ON(!skip_map); - slot = __ffs(skip_map); - - enqueue_one_int_qtd(hcd, qh, slot, qtd); - - or_map = reg_read32(hcd->regs, HC_INT_IRQ_MASK_OR_REG); - or_map |= (1 << slot); - reg_write32(hcd->regs, HC_INT_IRQ_MASK_OR_REG, or_map); - - skip_map &= ~(1 << slot); - reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, skip_map); - - buffstatus = reg_read32(hcd->regs, HC_BUFFER_STATUS_REG); - buffstatus |= INT_BUFFER; - reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, buffstatus); -} - static void isp1760_urb_done(struct usb_hcd *hcd, struct urb *urb) __releases(priv->lock) __acquires(priv->lock) @@ -948,557 +776,654 @@ __acquires(priv->lock) spin_lock(&priv->lock); } -static void isp1760_qtd_free(struct isp1760_qtd *qtd) +static struct isp1760_qtd *qtd_alloc(gfp_t flags, struct urb *urb, + u8 packet_type) { - BUG_ON(qtd->payload_addr); - kmem_cache_free(qtd_cachep, qtd); + struct isp1760_qtd *qtd; + + qtd = kmem_cache_zalloc(qtd_cachep, flags); + if (!qtd) + return NULL; + + INIT_LIST_HEAD(&qtd->qtd_list); + qtd->urb = urb; + qtd->packet_type = packet_type; + qtd->status = QTD_ENQUEUED; + qtd->actual_length = 0; + + return qtd; } -static struct isp1760_qtd *clean_this_qtd(struct isp1760_qtd *qtd, - struct isp1760_qh *qh) +static void qtd_free(struct isp1760_qtd *qtd) { - struct isp1760_qtd *tmp_qtd; - - if (list_is_last(&qtd->qtd_list, &qh->qtd_list)) - tmp_qtd = NULL; - else - tmp_qtd = list_entry(qtd->qtd_list.next, struct isp1760_qtd, - qtd_list); - list_del(&qtd->qtd_list); - isp1760_qtd_free(qtd); - return tmp_qtd; + WARN_ON(qtd->payload_addr); + kmem_cache_free(qtd_cachep, qtd); } -/* - * Remove this QTD from the QH list and free its memory. If this QTD - * isn't the last one than remove also his successor(s). - * Returns the QTD which is part of an new URB and should be enqueued. - */ -static struct isp1760_qtd *clean_up_qtdlist(struct isp1760_qtd *qtd, - struct isp1760_qh *qh) +static void start_bus_transfer(struct usb_hcd *hcd, u32 ptd_offset, int slot, + struct slotinfo *slots, struct isp1760_qtd *qtd, + struct isp1760_qh *qh, struct ptd *ptd) { - struct urb *urb; + struct isp1760_hcd *priv = hcd_to_priv(hcd); + int skip_map; + + WARN_ON((slot < 0) || (slot > 31)); + WARN_ON(qtd->length && !qtd->payload_addr); + WARN_ON(slots[slot].qtd); + WARN_ON(slots[slot].qh); + WARN_ON(qtd->status != QTD_PAYLOAD_ALLOC); + + slots[slot].qtd = qtd; + slots[slot].qh = qh; + qh->slot = slot; + qtd->status = QTD_XFER_STARTED; /* Set this before writing ptd, since + interrupt routine may preempt and expects this value. */ + ptd_write(hcd->regs, ptd_offset, slot, ptd); + priv->active_ptds++; + + /* Make sure done map has not triggered from some unlinked transfer */ + if (ptd_offset == ATL_PTD_OFFSET) { + priv->atl_done_map |= reg_read32(hcd->regs, + HC_ATL_PTD_DONEMAP_REG); + priv->atl_done_map &= ~(1 << qh->slot); - urb = qtd->urb; - do { - qtd = clean_this_qtd(qtd, qh); - } while (qtd && (qtd->urb == urb)); + skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG); + skip_map &= ~(1 << qh->slot); + reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, skip_map); + } else { + priv->int_done_map |= reg_read32(hcd->regs, + HC_INT_PTD_DONEMAP_REG); + priv->int_done_map &= ~(1 << qh->slot); - return qtd; + skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG); + skip_map &= ~(1 << qh->slot); + reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, skip_map); + } } -static void do_atl_int(struct usb_hcd *hcd) +static int is_short_bulk(struct isp1760_qtd *qtd) { - struct isp1760_hcd *priv = hcd_to_priv(hcd); - u32 done_map, skip_map; - struct ptd ptd; - struct urb *urb; - u32 slot; - u32 length; - u32 or_map; - u32 status = -EINVAL; - int error; - struct isp1760_qtd *qtd; - struct isp1760_qh *qh; - u32 rl; - u32 nakcount; + return (usb_pipebulk(qtd->urb->pipe) && + (qtd->actual_length < qtd->length)); +} - done_map = reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG); - skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG); +static void collect_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh, + struct list_head *urb_list) +{ + int last_qtd; + struct isp1760_qtd *qtd, *qtd_next; + struct urb_listitem *urb_listitem; - or_map = reg_read32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG); - or_map &= ~done_map; - reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, or_map); + list_for_each_entry_safe(qtd, qtd_next, &qh->qtd_list, qtd_list) { + if (qtd->status < QTD_XFER_COMPLETE) + break; - while (done_map) { - status = 0; - priv->atl_queued--; + if (list_is_last(&qtd->qtd_list, &qh->qtd_list)) + last_qtd = 1; + else + last_qtd = qtd->urb != qtd_next->urb; + + if ((!last_qtd) && (qtd->status == QTD_RETIRE)) + qtd_next->status = QTD_RETIRE; + + if (qtd->status == QTD_XFER_COMPLETE) { + if (qtd->actual_length) { + switch (qtd->packet_type) { + case IN_PID: + mem_reads8(hcd->regs, qtd->payload_addr, + qtd->data_buffer, + qtd->actual_length); + /* Fall through (?) */ + case OUT_PID: + qtd->urb->actual_length += + qtd->actual_length; + /* Fall through ... */ + case SETUP_PID: + break; + } + } - slot = __ffs(done_map); - done_map &= ~(1 << slot); - skip_map |= (1 << slot); + if (is_short_bulk(qtd)) { + if (qtd->urb->transfer_flags & URB_SHORT_NOT_OK) + qtd->urb->status = -EREMOTEIO; + if (!last_qtd) + qtd_next->status = QTD_RETIRE; + } + } - qtd = priv->atl_ints[slot].qtd; - qh = priv->atl_ints[slot].qh; + if (qtd->payload_addr) + free_mem(hcd, qtd); - if (!qh) { - dev_err(hcd->self.controller, "qh is 0\n"); - continue; + if (last_qtd) { + if ((qtd->status == QTD_RETIRE) && + (qtd->urb->status == -EINPROGRESS)) + qtd->urb->status = -EPIPE; + /* Defer calling of urb_done() since it releases lock */ + urb_listitem = kmem_cache_zalloc(urb_listitem_cachep, + GFP_ATOMIC); + if (unlikely(!urb_listitem)) + break; + urb_listitem->urb = qtd->urb; + list_add_tail(&urb_listitem->urb_list, urb_list); } - ptd_read(hcd->regs, ATL_PTD_OFFSET, slot, &ptd); - rl = (ptd.dw2 >> 25) & 0x0f; - nakcount = (ptd.dw3 >> 19) & 0xf; - - /* Transfer Error, *but* active and no HALT -> reload */ - if ((ptd.dw3 & DW3_ERROR_BIT) && (ptd.dw3 & DW3_QTD_ACTIVE) && - !(ptd.dw3 & DW3_HALT_BIT)) { - - /* according to ppriv code, we have to - * reload this one if trasfered bytes != requested bytes - * else act like everything went smooth.. - * XXX This just doesn't feel right and hasn't - * triggered so far. - */ + list_del(&qtd->qtd_list); + qtd_free(qtd); + } +} - length = PTD_XFERRED_LENGTH(ptd.dw3); - dev_err(hcd->self.controller, - "Should reload now... transferred %d " - "of %zu\n", length, qtd->length); - BUG(); - } +#define ENQUEUE_DEPTH 2 +static void enqueue_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh) +{ + struct isp1760_hcd *priv = hcd_to_priv(hcd); + int ptd_offset; + struct slotinfo *slots; + int curr_slot, free_slot; + int n; + struct ptd ptd; + struct isp1760_qtd *qtd; - if (!nakcount && (ptd.dw3 & DW3_QTD_ACTIVE)) { - u32 buffstatus; + if (unlikely(list_empty(&qh->qtd_list))) { + WARN_ON(1); + return; + } - /* - * NAKs are handled in HW by the chip. Usually if the - * device is not able to send data fast enough. - * This happens mostly on slower hardware. - */ + if (usb_pipeint(list_entry(qh->qtd_list.next, struct isp1760_qtd, + qtd_list)->urb->pipe)) { + ptd_offset = INT_PTD_OFFSET; + slots = priv->int_slots; + } else { + ptd_offset = ATL_PTD_OFFSET; + slots = priv->atl_slots; + } - /* RL counter = ERR counter */ - ptd.dw3 &= ~(0xf << 19); - ptd.dw3 |= rl << 19; - ptd.dw3 &= ~(3 << (55 - 32)); - ptd.dw3 |= ERR_COUNTER << (55 - 32); - - /* - * It is not needed to write skip map back because it - * is unchanged. Just make sure that this entry is - * unskipped once it gets written to the HW. - */ - skip_map &= ~(1 << slot); - or_map = reg_read32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG); - or_map |= 1 << slot; - reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, or_map); + free_slot = -1; + for (curr_slot = 0; curr_slot < 32; curr_slot++) { + if ((free_slot == -1) && (slots[curr_slot].qtd == NULL)) + free_slot = curr_slot; + if (slots[curr_slot].qh == qh) + break; + } - ptd.dw0 |= PTD_VALID; - ptd_write(hcd->regs, ATL_PTD_OFFSET, slot, &ptd); + n = 0; + list_for_each_entry(qtd, &qh->qtd_list, qtd_list) { + if (qtd->status == QTD_ENQUEUED) { + WARN_ON(qtd->payload_addr); + alloc_mem(hcd, qtd); + if ((qtd->length) && (!qtd->payload_addr)) + break; - priv->atl_queued++; - if (priv->atl_queued == 2) - reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, - INTERRUPT_ENABLE_SOT_MASK); + if ((qtd->length) && + ((qtd->packet_type == SETUP_PID) || + (qtd->packet_type == OUT_PID))) { + mem_writes8(hcd->regs, qtd->payload_addr, + qtd->data_buffer, qtd->length); + } - buffstatus = reg_read32(hcd->regs, - HC_BUFFER_STATUS_REG); - buffstatus |= ATL_BUFFER; - reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, - buffstatus); - continue; + qtd->status = QTD_PAYLOAD_ALLOC; } - error = check_error(hcd, &ptd); - if (error) { - status = error; - priv->atl_ints[slot].qh->toggle = 0; - priv->atl_ints[slot].qh->ping = 0; - qtd->urb->status = -EPIPE; - -#if 0 - printk(KERN_ERR "Error in %s().\n", __func__); - printk(KERN_ERR "IN dw0: %08x dw1: %08x dw2: %08x " - "dw3: %08x dw4: %08x dw5: %08x dw6: " - "%08x dw7: %08x\n", - ptd.dw0, ptd.dw1, ptd.dw2, ptd.dw3, - ptd.dw4, ptd.dw5, ptd.dw6, ptd.dw7); -#endif - } else { - priv->atl_ints[slot].qh->toggle = ptd.dw3 & (1 << 25); - priv->atl_ints[slot].qh->ping = ptd.dw3 & (1 << 26); - } + if (qtd->status == QTD_PAYLOAD_ALLOC) { +/* + if ((curr_slot > 31) && (free_slot == -1)) + dev_dbg(hcd->self.controller, "%s: No slot " + "available for transfer\n", __func__); +*/ + /* Start xfer for this endpoint if not already done */ + if ((curr_slot > 31) && (free_slot > -1)) { + if (usb_pipeint(qtd->urb->pipe)) + create_ptd_int(qh, qtd, &ptd); + else + create_ptd_atl(qh, qtd, &ptd); + + start_bus_transfer(hcd, ptd_offset, free_slot, + slots, qtd, qh, &ptd); + curr_slot = free_slot; + } - length = PTD_XFERRED_LENGTH(ptd.dw3); - if (length) { - switch (DW1_GET_PID(ptd.dw1)) { - case IN_PID: - mem_reads8(hcd->regs, qtd->payload_addr, - qtd->data_buffer, length); + n++; + if (n >= ENQUEUE_DEPTH) + break; + } + } +} - case OUT_PID: +void schedule_ptds(struct usb_hcd *hcd) +{ + struct isp1760_hcd *priv; + struct isp1760_qh *qh, *qh_next; + struct list_head *ep_queue; + struct usb_host_endpoint *ep; + LIST_HEAD(urb_list); + struct urb_listitem *urb_listitem, *urb_listitem_next; + + if (!hcd) { + WARN_ON(1); + return; + } - qtd->urb->actual_length += length; + priv = hcd_to_priv(hcd); - case SETUP_PID: - break; + /* + * check finished/retired xfers, transfer payloads, call urb_done() + */ + ep_queue = &priv->interruptqhs; + while (ep_queue) { + list_for_each_entry_safe(qh, qh_next, ep_queue, qh_list) { + ep = list_entry(qh->qtd_list.next, struct isp1760_qtd, + qtd_list)->urb->ep; + collect_qtds(hcd, qh, &urb_list); + if (list_empty(&qh->qtd_list)) { + list_del(&qh->qh_list); + if (ep->hcpriv == NULL) { + /* Endpoint has been disabled, so we + can free the associated queue head. */ + qh_free(qh); + } } } - priv->atl_ints[slot].qtd = NULL; - priv->atl_ints[slot].qh = NULL; - - free_mem(hcd, qtd); + if (ep_queue == &priv->interruptqhs) + ep_queue = &priv->controlqhs; + else if (ep_queue == &priv->controlqhs) + ep_queue = &priv->bulkqhs; + else + ep_queue = NULL; + } - reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, skip_map); + list_for_each_entry_safe(urb_listitem, urb_listitem_next, &urb_list, + urb_list) { + isp1760_urb_done(hcd, urb_listitem->urb); + kmem_cache_free(urb_listitem_cachep, urb_listitem); + } - if (qtd->urb->status == -EPIPE) { - /* HALT was received */ + /* + * Schedule packets for transfer. + * + * According to USB2.0 specification: + * + * 1st prio: interrupt xfers, up to 80 % of bandwidth + * 2nd prio: control xfers + * 3rd prio: bulk xfers + * + * ... but let's use a simpler scheme here (mostly because ISP1761 doc + * is very unclear on how to prioritize traffic): + * + * 1) Enqueue any queued control transfers, as long as payload chip mem + * and PTD ATL slots are available. + * 2) Enqueue any queued INT transfers, as long as payload chip mem + * and PTD INT slots are available. + * 3) Enqueue any queued bulk transfers, as long as payload chip mem + * and PTD ATL slots are available. + * + * Use double buffering (ENQUEUE_DEPTH==2) as a compromise between + * conservation of chip mem and performance. + * + * I'm sure this scheme could be improved upon! + */ + ep_queue = &priv->controlqhs; + while (ep_queue) { + list_for_each_entry_safe(qh, qh_next, ep_queue, qh_list) + enqueue_qtds(hcd, qh); + + if (ep_queue == &priv->controlqhs) + ep_queue = &priv->interruptqhs; + else if (ep_queue == &priv->interruptqhs) + ep_queue = &priv->bulkqhs; + else + ep_queue = NULL; + } +} - urb = qtd->urb; - qtd = clean_up_qtdlist(qtd, qh); - isp1760_urb_done(hcd, urb); +#define PTD_STATE_QTD_DONE 1 +#define PTD_STATE_QTD_RELOAD 2 +#define PTD_STATE_URB_RETIRE 3 - } else if (usb_pipebulk(qtd->urb->pipe) && - (length < qtd->length)) { - /* short BULK received */ +static int check_int_transfer(struct usb_hcd *hcd, struct ptd *ptd, + struct urb *urb) +{ + __dw dw4; + int i; - if (qtd->urb->transfer_flags & URB_SHORT_NOT_OK) { - qtd->urb->status = -EREMOTEIO; - dev_dbg(hcd->self.controller, - "short bulk, %d instead %zu " - "with URB_SHORT_NOT_OK flag.\n", - length, qtd->length); - } + dw4 = ptd->dw4; + dw4 >>= 8; - if (qtd->urb->status == -EINPROGRESS) - qtd->urb->status = 0; + /* FIXME: ISP1761 datasheet does not say what to do with these. Do we + need to handle these errors? Is it done in hardware? */ - urb = qtd->urb; - qtd = clean_up_qtdlist(qtd, qh); - isp1760_urb_done(hcd, urb); + if (ptd->dw3 & DW3_HALT_BIT) { - } else if (last_qtd_of_urb(qtd, qh)) { - /* that was the last qtd of that URB */ + urb->status = -EPROTO; /* Default unknown error */ - if (qtd->urb->status == -EINPROGRESS) - qtd->urb->status = 0; + for (i = 0; i < 8; i++) { + switch (dw4 & 0x7) { + case INT_UNDERRUN: + dev_dbg(hcd->self.controller, "%s: underrun " + "during uFrame %d\n", + __func__, i); + urb->status = -ECOMM; /* Could not write data */ + break; + case INT_EXACT: + dev_dbg(hcd->self.controller, "%s: transaction " + "error during uFrame %d\n", + __func__, i); + urb->status = -EPROTO; /* timeout, bad CRC, PID + error etc. */ + break; + case INT_BABBLE: + dev_dbg(hcd->self.controller, "%s: babble " + "error during uFrame %d\n", + __func__, i); + urb->status = -EOVERFLOW; + break; + } + dw4 >>= 3; + } - urb = qtd->urb; - qtd = clean_up_qtdlist(qtd, qh); - isp1760_urb_done(hcd, urb); + return PTD_STATE_URB_RETIRE; + } - } else { - /* next QTD of this URB */ + return PTD_STATE_QTD_DONE; +} - qtd = clean_this_qtd(qtd, qh); - BUG_ON(!qtd); - } +static int check_atl_transfer(struct usb_hcd *hcd, struct ptd *ptd, + struct urb *urb) +{ + WARN_ON(!ptd); + if (ptd->dw3 & DW3_HALT_BIT) { |