diff options
Diffstat (limited to 'drivers/usb/host/isp1760-hcd.c')
-rw-r--r-- | drivers/usb/host/isp1760-hcd.c | 1395 |
1 files changed, 650 insertions, 745 deletions
diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index bdba8c5d844..f50e84ac570 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -33,6 +33,7 @@ struct isp1760_hcd { struct inter_packet_info atl_ints[32]; struct inter_packet_info int_ints[32]; struct memory_chunk memory_pool[BLOCKS]; + u32 atl_queued; /* periodic schedule support */ #define DEFAULT_I_TDPS 1024 @@ -47,10 +48,6 @@ static inline struct isp1760_hcd *hcd_to_priv(struct usb_hcd *hcd) { return (struct isp1760_hcd *) (hcd->hcd_priv); } -static inline struct usb_hcd *priv_to_hcd(struct isp1760_hcd *priv) -{ - return container_of((void *) priv, struct usb_hcd, hcd_priv); -} /* Section 2.2 Host Controller Capability Registers */ #define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */ @@ -80,11 +77,10 @@ static inline struct usb_hcd *priv_to_hcd(struct isp1760_hcd *priv) #define PORT_RWC_BITS (PORT_CSC) struct isp1760_qtd { - struct isp1760_qtd *hw_next; u8 packet_type; - u8 toggle; - void *data_buffer; + u32 payload_addr; + /* the rest is HCD-private */ struct list_head qtd_list; struct urb *urb; @@ -92,205 +88,267 @@ struct isp1760_qtd { /* isp special*/ u32 status; -#define URB_COMPLETE_NOTIFY (1 << 0) #define URB_ENQUEUED (1 << 1) -#define URB_TYPE_ATL (1 << 2) -#define URB_TYPE_INT (1 << 3) }; struct isp1760_qh { /* first part defined by EHCI spec */ struct list_head qtd_list; - struct isp1760_hcd *priv; - - /* periodic schedule info */ - unsigned short period; /* polling interval */ - struct usb_device *dev; u32 toggle; u32 ping; }; -#define ehci_port_speed(priv, portsc) USB_PORT_STAT_HIGH_SPEED - -static unsigned int isp1760_readl(__u32 __iomem *regs) +/* + * Access functions for isp176x registers (addresses 0..0x03FF). + */ +static u32 reg_read32(void __iomem *base, u32 reg) { - return readl(regs); + return readl(base + reg); } -static void isp1760_writel(const unsigned int val, __u32 __iomem *regs) +static void reg_write32(void __iomem *base, u32 reg, u32 val) { - writel(val, regs); + writel(val, base + reg); } /* - * The next two copy via MMIO data to/from the device. memcpy_{to|from}io() + * Access functions for isp176x memory (offset >= 0x0400). + * + * bank_reads8() reads memory locations prefetched by an earlier write to + * HC_MEMORY_REG (see isp176x datasheet). Unless you want to do fancy multi- + * bank optimizations, you should use the more generic mem_reads8() below. + * + * For access to ptd memory, use the specialized ptd_read() and ptd_write() + * below. + * + * These functions copy via MMIO data to/from the device. memcpy_{to|from}io() * doesn't quite work because some people have to enforce 32-bit access */ -static void priv_read_copy(struct isp1760_hcd *priv, u32 *src, - __u32 __iomem *dst, u32 len) +static void bank_reads8(void __iomem *src_base, u32 src_offset, u32 bank_addr, + __u32 *dst, u32 bytes) { + __u32 __iomem *src; u32 val; - u8 *buff8; + __u8 *src_byteptr; + __u8 *dst_byteptr; - if (!src) { - printk(KERN_ERR "ERROR: buffer: %p len: %d\n", src, len); - return; - } + src = src_base + (bank_addr | src_offset); - while (len >= 4) { - *src = __raw_readl(dst); - len -= 4; - src++; - dst++; + if (src_offset < PAYLOAD_OFFSET) { + while (bytes >= 4) { + *dst = le32_to_cpu(__raw_readl(src)); + bytes -= 4; + src++; + dst++; + } + } else { + while (bytes >= 4) { + *dst = __raw_readl(src); + bytes -= 4; + src++; + dst++; + } } - if (!len) + if (!bytes) return; /* in case we have 3, 2 or 1 by left. The dst buffer may not be fully * allocated. */ - val = isp1760_readl(dst); - - buff8 = (u8 *)src; - while (len) { - - *buff8 = val; - val >>= 8; - len--; - buff8++; + if (src_offset < PAYLOAD_OFFSET) + val = le32_to_cpu(__raw_readl(src)); + else + val = __raw_readl(src); + + dst_byteptr = (void *) dst; + src_byteptr = (void *) &val; + while (bytes > 0) { + *dst_byteptr = *src_byteptr; + dst_byteptr++; + src_byteptr++; + bytes--; } } -static void priv_write_copy(const struct isp1760_hcd *priv, const u32 *src, - __u32 __iomem *dst, u32 len) +static void mem_reads8(void __iomem *src_base, u32 src_offset, void *dst, + u32 bytes) { - while (len >= 4) { - __raw_writel(*src, dst); - len -= 4; - src++; - dst++; + reg_write32(src_base, HC_MEMORY_REG, src_offset + ISP_BANK(0)); + ndelay(90); + bank_reads8(src_base, src_offset, ISP_BANK(0), dst, bytes); +} + +static void mem_writes8(void __iomem *dst_base, u32 dst_offset, + __u32 const *src, u32 bytes) +{ + __u32 __iomem *dst; + + dst = dst_base + dst_offset; + + if (dst_offset < PAYLOAD_OFFSET) { + while (bytes >= 4) { + __raw_writel(cpu_to_le32(*src), dst); + bytes -= 4; + src++; + dst++; + } + } else { + while (bytes >= 4) { + __raw_writel(*src, dst); + bytes -= 4; + src++; + dst++; + } } - if (!len) + if (!bytes) return; - /* in case we have 3, 2 or 1 by left. The buffer is allocated and the - * extra bytes should not be read by the HW + /* in case we have 3, 2 or 1 bytes left. The buffer is allocated and the + * extra bytes should not be read by the HW. */ - __raw_writel(*src, dst); + if (dst_offset < PAYLOAD_OFFSET) + __raw_writel(cpu_to_le32(*src), dst); + else + __raw_writel(*src, dst); +} + +/* + * Read and write ptds. 'ptd_offset' should be one of ISO_PTD_OFFSET, + * INT_PTD_OFFSET, and ATL_PTD_OFFSET. 'slot' should be less than 32. + */ +static void ptd_read(void __iomem *base, u32 ptd_offset, u32 slot, + struct ptd *ptd) +{ + reg_write32(base, HC_MEMORY_REG, + ISP_BANK(0) + ptd_offset + slot*sizeof(*ptd)); + ndelay(90); + bank_reads8(base, ptd_offset + slot*sizeof(*ptd), ISP_BANK(0), + (void *) ptd, sizeof(*ptd)); +} + +static void ptd_write(void __iomem *base, u32 ptd_offset, u32 slot, + struct ptd *ptd) +{ + mem_writes8(base, ptd_offset + slot*sizeof(*ptd) + sizeof(ptd->dw0), + &ptd->dw1, 7*sizeof(ptd->dw1)); + /* Make sure dw0 gets written last (after other dw's and after payload) + since it contains the enable bit */ + wmb(); + mem_writes8(base, ptd_offset + slot*sizeof(*ptd), &ptd->dw0, + sizeof(ptd->dw0)); } + /* memory management of the 60kb on the chip from 0x1000 to 0xffff */ static void init_memory(struct isp1760_hcd *priv) { - int i; - u32 payload; + int i, curr; + u32 payload_addr; - payload = 0x1000; + payload_addr = PAYLOAD_OFFSET; for (i = 0; i < BLOCK_1_NUM; i++) { - priv->memory_pool[i].start = payload; + priv->memory_pool[i].start = payload_addr; priv->memory_pool[i].size = BLOCK_1_SIZE; priv->memory_pool[i].free = 1; - payload += priv->memory_pool[i].size; + payload_addr += priv->memory_pool[i].size; } - - for (i = BLOCK_1_NUM; i < BLOCK_1_NUM + BLOCK_2_NUM; i++) { - priv->memory_pool[i].start = payload; - priv->memory_pool[i].size = BLOCK_2_SIZE; - priv->memory_pool[i].free = 1; - payload += priv->memory_pool[i].size; + curr = i; + for (i = 0; i < BLOCK_2_NUM; i++) { + priv->memory_pool[curr + i].start = payload_addr; + priv->memory_pool[curr + i].size = BLOCK_2_SIZE; + priv->memory_pool[curr + i].free = 1; + payload_addr += priv->memory_pool[curr + i].size; } - - for (i = BLOCK_1_NUM + BLOCK_2_NUM; i < BLOCKS; i++) { - priv->memory_pool[i].start = payload; - priv->memory_pool[i].size = BLOCK_3_SIZE; - priv->memory_pool[i].free = 1; - payload += priv->memory_pool[i].size; + curr = i; + for (i = 0; i < BLOCK_3_NUM; i++) { + priv->memory_pool[curr + i].start = payload_addr; + priv->memory_pool[curr + i].size = BLOCK_3_SIZE; + priv->memory_pool[curr + i].free = 1; + payload_addr += priv->memory_pool[curr + i].size; } - BUG_ON(payload - priv->memory_pool[i - 1].size > PAYLOAD_SIZE); + BUG_ON(payload_addr - priv->memory_pool[0].start > PAYLOAD_AREA_SIZE); } -static u32 alloc_mem(struct isp1760_hcd *priv, u32 size) +static void alloc_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd) { + struct isp1760_hcd *priv = hcd_to_priv(hcd); int i; - if (!size) - return ISP1760_NULL_POINTER; + BUG_ON(qtd->payload_addr); + + if (!qtd->length) + return; for (i = 0; i < BLOCKS; i++) { - if (priv->memory_pool[i].size >= size && + if (priv->memory_pool[i].size >= qtd->length && priv->memory_pool[i].free) { - priv->memory_pool[i].free = 0; - return priv->memory_pool[i].start; + qtd->payload_addr = priv->memory_pool[i].start; + return; } } - printk(KERN_ERR "ISP1760 MEM: can not allocate %d bytes of memory\n", - size); - printk(KERN_ERR "Current memory map:\n"); + dev_err(hcd->self.controller, + "%s: Can not allocate %lu bytes of memory\n" + "Current memory map:\n", + __func__, qtd->length); for (i = 0; i < BLOCKS; i++) { - printk(KERN_ERR "Pool %2d size %4d status: %d\n", + 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 0; + return; } -static void free_mem(struct isp1760_hcd *priv, u32 mem) +static void free_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd) { + struct isp1760_hcd *priv = hcd_to_priv(hcd); int i; - if (mem == ISP1760_NULL_POINTER) + if (!qtd->payload_addr) return; for (i = 0; i < BLOCKS; i++) { - if (priv->memory_pool[i].start == mem) { - + if (priv->memory_pool[i].start == qtd->payload_addr) { BUG_ON(priv->memory_pool[i].free); - priv->memory_pool[i].free = 1; - return ; + qtd->payload_addr = 0; + return; } } - printk(KERN_ERR "Trying to free not-here-allocated memory :%08x\n", - mem); + dev_err(hcd->self.controller, "%s: Invalid pointer: %08x\n", + __func__, qtd->payload_addr); BUG(); } static void isp1760_init_regs(struct usb_hcd *hcd) { - isp1760_writel(0, hcd->regs + HC_BUFFER_STATUS_REG); - isp1760_writel(NO_TRANSFER_ACTIVE, hcd->regs + - HC_ATL_PTD_SKIPMAP_REG); - isp1760_writel(NO_TRANSFER_ACTIVE, hcd->regs + - HC_INT_PTD_SKIPMAP_REG); - isp1760_writel(NO_TRANSFER_ACTIVE, hcd->regs + - HC_ISO_PTD_SKIPMAP_REG); - - isp1760_writel(~NO_TRANSFER_ACTIVE, hcd->regs + - HC_ATL_PTD_DONEMAP_REG); - isp1760_writel(~NO_TRANSFER_ACTIVE, hcd->regs + - HC_INT_PTD_DONEMAP_REG); - isp1760_writel(~NO_TRANSFER_ACTIVE, hcd->regs + - HC_ISO_PTD_DONEMAP_REG); + 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); } -static int handshake(struct isp1760_hcd *priv, void __iomem *ptr, +static int handshake(struct usb_hcd *hcd, u32 reg, u32 mask, u32 done, int usec) { u32 result; do { - result = isp1760_readl(ptr); + result = reg_read32(hcd->regs, reg); if (result == ~0) return -ENODEV; result &= mask; @@ -303,17 +361,18 @@ static int handshake(struct isp1760_hcd *priv, void __iomem *ptr, } /* reset a non-running (STS_HALT == 1) controller */ -static int ehci_reset(struct isp1760_hcd *priv) +static int ehci_reset(struct usb_hcd *hcd) { int retval; - struct usb_hcd *hcd = priv_to_hcd(priv); - u32 command = isp1760_readl(hcd->regs + HC_USBCMD); + struct isp1760_hcd *priv = hcd_to_priv(hcd); + + u32 command = reg_read32(hcd->regs, HC_USBCMD); command |= CMD_RESET; - isp1760_writel(command, hcd->regs + HC_USBCMD); + reg_write32(hcd->regs, HC_USBCMD, command); hcd->state = HC_STATE_HALT; priv->next_statechange = jiffies; - retval = handshake(priv, hcd->regs + HC_USBCMD, + retval = handshake(hcd, HC_USBCMD, CMD_RESET, 0, 250 * 1000); return retval; } @@ -324,8 +383,7 @@ static void qh_destroy(struct isp1760_qh *qh) kmem_cache_free(qh_cachep, qh); } -static struct isp1760_qh *isp1760_qh_alloc(struct isp1760_hcd *priv, - gfp_t flags) +static struct isp1760_qh *isp1760_qh_alloc(gfp_t flags) { struct isp1760_qh *qh; @@ -334,7 +392,6 @@ static struct isp1760_qh *isp1760_qh_alloc(struct isp1760_hcd *priv, return qh; INIT_LIST_HEAD(&qh->qtd_list); - qh->priv = priv; return qh; } @@ -361,7 +418,7 @@ static int priv_init(struct usb_hcd *hcd) priv->periodic_size = DEFAULT_I_TDPS; /* controllers may cache some of the periodic schedule ... */ - hcc_params = isp1760_readl(hcd->regs + HC_HCCPARAMS); + hcc_params = reg_read32(hcd->regs, HC_HCCPARAMS); /* full frame cache */ if (HCC_ISOC_CACHE(hcc_params)) priv->i_thresh = 8; @@ -398,15 +455,15 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) * Write it twice to ensure correct upper bits if switching * to 16-bit mode. */ - isp1760_writel(hwmode, hcd->regs + HC_HW_MODE_CTRL); - isp1760_writel(hwmode, hcd->regs + HC_HW_MODE_CTRL); + reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode); + reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode); - isp1760_writel(0xdeadbabe, hcd->regs + HC_SCRATCH_REG); + reg_write32(hcd->regs, HC_SCRATCH_REG, 0xdeadbabe); /* Change bus pattern */ - scratch = isp1760_readl(hcd->regs + HC_CHIP_ID_REG); - scratch = isp1760_readl(hcd->regs + HC_SCRATCH_REG); + scratch = reg_read32(hcd->regs, HC_CHIP_ID_REG); + scratch = reg_read32(hcd->regs, HC_SCRATCH_REG); if (scratch != 0xdeadbabe) { - printk(KERN_ERR "ISP1760: Scratch test failed.\n"); + dev_err(hcd->self.controller, "Scratch test failed.\n"); return -ENODEV; } @@ -414,30 +471,30 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) isp1760_init_regs(hcd); /* reset */ - isp1760_writel(SW_RESET_RESET_ALL, hcd->regs + HC_RESET_REG); + reg_write32(hcd->regs, HC_RESET_REG, SW_RESET_RESET_ALL); mdelay(100); - isp1760_writel(SW_RESET_RESET_HC, hcd->regs + HC_RESET_REG); + reg_write32(hcd->regs, HC_RESET_REG, SW_RESET_RESET_HC); mdelay(100); - result = ehci_reset(priv); + result = ehci_reset(hcd); if (result) return result; /* Step 11 passed */ - isp1760_info(priv, "bus width: %d, oc: %s\n", + dev_info(hcd->self.controller, "bus width: %d, oc: %s\n", (priv->devflags & ISP1760_FLAG_BUS_WIDTH_16) ? 16 : 32, (priv->devflags & ISP1760_FLAG_ANALOG_OC) ? "analog" : "digital"); /* ATL reset */ - isp1760_writel(hwmode | ALL_ATX_RESET, hcd->regs + HC_HW_MODE_CTRL); + reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode | ALL_ATX_RESET); mdelay(10); - isp1760_writel(hwmode, hcd->regs + HC_HW_MODE_CTRL); + reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode); - isp1760_writel(INTERRUPT_ENABLE_MASK, hcd->regs + HC_INTERRUPT_REG); - isp1760_writel(INTERRUPT_ENABLE_MASK, hcd->regs + HC_INTERRUPT_ENABLE); + reg_write32(hcd->regs, HC_INTERRUPT_REG, INTERRUPT_ENABLE_MASK); + reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, INTERRUPT_ENABLE_MASK); /* * PORT 1 Control register of the ISP1760 is the OTG control @@ -445,11 +502,10 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) * support in this driver, we use port 1 as a "normal" USB host port on * both chips. */ - isp1760_writel(PORT1_POWER | PORT1_INIT2, - hcd->regs + HC_PORT1_CTRL); + reg_write32(hcd->regs, HC_PORT1_CTRL, PORT1_POWER | PORT1_INIT2); mdelay(10); - priv->hcs_params = isp1760_readl(hcd->regs + HC_HCSPARAMS); + priv->hcs_params = reg_read32(hcd->regs, HC_HCSPARAMS); return priv_init(hcd); } @@ -457,25 +513,24 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) static void isp1760_init_maps(struct usb_hcd *hcd) { /*set last maps, for iso its only 1, else 32 tds bitmap*/ - isp1760_writel(0x80000000, hcd->regs + HC_ATL_PTD_LASTPTD_REG); - isp1760_writel(0x80000000, hcd->regs + HC_INT_PTD_LASTPTD_REG); - isp1760_writel(0x00000001, hcd->regs + HC_ISO_PTD_LASTPTD_REG); + 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); } static void isp1760_enable_interrupts(struct usb_hcd *hcd) { - isp1760_writel(0, hcd->regs + HC_ATL_IRQ_MASK_AND_REG); - isp1760_writel(0, hcd->regs + HC_ATL_IRQ_MASK_OR_REG); - isp1760_writel(0, hcd->regs + HC_INT_IRQ_MASK_AND_REG); - isp1760_writel(0, hcd->regs + HC_INT_IRQ_MASK_OR_REG); - isp1760_writel(0, hcd->regs + HC_ISO_IRQ_MASK_AND_REG); - isp1760_writel(0xffffffff, hcd->regs + HC_ISO_IRQ_MASK_OR_REG); + 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_INT_IRQ_MASK_AND_REG, 0); + reg_write32(hcd->regs, HC_INT_IRQ_MASK_OR_REG, 0); + 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 */ } static int isp1760_run(struct usb_hcd *hcd) { - struct isp1760_hcd *priv = hcd_to_priv(hcd); int retval; u32 temp; u32 command; @@ -485,15 +540,15 @@ static int isp1760_run(struct usb_hcd *hcd) hcd->state = HC_STATE_RUNNING; isp1760_enable_interrupts(hcd); - temp = isp1760_readl(hcd->regs + HC_HW_MODE_CTRL); - isp1760_writel(temp | HW_GLOBAL_INTR_EN, hcd->regs + HC_HW_MODE_CTRL); + temp = reg_read32(hcd->regs, HC_HW_MODE_CTRL); + reg_write32(hcd->regs, HC_HW_MODE_CTRL, temp | HW_GLOBAL_INTR_EN); - command = isp1760_readl(hcd->regs + HC_USBCMD); + command = reg_read32(hcd->regs, HC_USBCMD); command &= ~(CMD_LRESET|CMD_RESET); command |= CMD_RUN; - isp1760_writel(command, hcd->regs + HC_USBCMD); + reg_write32(hcd->regs, HC_USBCMD, command); - retval = handshake(priv, hcd->regs + HC_USBCMD, CMD_RUN, CMD_RUN, + retval = handshake(hcd, HC_USBCMD, CMD_RUN, CMD_RUN, 250 * 1000); if (retval) return retval; @@ -504,17 +559,16 @@ static int isp1760_run(struct usb_hcd *hcd) * the semaphore while doing so. */ down_write(&ehci_cf_port_reset_rwsem); - isp1760_writel(FLAG_CF, hcd->regs + HC_CONFIGFLAG); + reg_write32(hcd->regs, HC_CONFIGFLAG, FLAG_CF); - retval = handshake(priv, hcd->regs + HC_CONFIGFLAG, FLAG_CF, FLAG_CF, - 250 * 1000); + retval = handshake(hcd, HC_CONFIGFLAG, FLAG_CF, FLAG_CF, 250 * 1000); up_write(&ehci_cf_port_reset_rwsem); if (retval) return retval; - chipid = isp1760_readl(hcd->regs + HC_CHIP_ID_REG); - isp1760_info(priv, "USB ISP %04x HW rev. %d started\n", chipid & 0xffff, - chipid >> 16); + chipid = reg_read32(hcd->regs, HC_CHIP_ID_REG); + dev_info(hcd->self.controller, "USB ISP %04x HW rev. %d started\n", + chipid & 0xffff, chipid >> 16); /* PTD Register Init Part 2, Step 28 */ /* enable INTs */ @@ -532,160 +586,156 @@ static u32 base_to_chip(u32 base) return ((base - 0x400) >> 3); } -static void transform_into_atl(struct isp1760_hcd *priv, struct isp1760_qh *qh, - struct isp1760_qtd *qtd, struct urb *urb, - u32 payload, struct ptd *ptd) +static int last_qtd_of_urb(struct isp1760_qtd *qtd, struct isp1760_qh *qh) +{ + struct urb *urb; + + if (list_is_last(&qtd->qtd_list, &qh->qtd_list)) + return 1; + + urb = qtd->urb; + qtd = list_entry(qtd->qtd_list.next, typeof(*qtd), qtd_list); + return (qtd->urb != urb); +} + +static void transform_into_atl(struct isp1760_qh *qh, + struct isp1760_qtd *qtd, struct ptd *ptd) { - u32 dw0; - u32 dw1; - u32 dw2; - u32 dw3; u32 maxpacket; u32 multi; u32 pid_code; u32 rl = RL_COUNTER; u32 nak = NAK_COUNTER; + memset(ptd, 0, sizeof(*ptd)); + /* according to 3.6.2, max packet len can not be > 0x400 */ - maxpacket = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); + maxpacket = usb_maxpacket(qtd->urb->dev, qtd->urb->pipe, + usb_pipeout(qtd->urb->pipe)); multi = 1 + ((maxpacket >> 11) & 0x3); maxpacket &= 0x7ff; /* DW0 */ - dw0 = PTD_VALID; - dw0 |= PTD_LENGTH(qtd->length); - dw0 |= PTD_MAXPACKET(maxpacket); - dw0 |= PTD_ENDPOINT(usb_pipeendpoint(urb->pipe)); - dw1 = usb_pipeendpoint(urb->pipe) >> 1; + ptd->dw0 = PTD_VALID; + ptd->dw0 |= PTD_LENGTH(qtd->length); + ptd->dw0 |= PTD_MAXPACKET(maxpacket); + ptd->dw0 |= PTD_ENDPOINT(usb_pipeendpoint(qtd->urb->pipe)); /* DW1 */ - dw1 |= PTD_DEVICE_ADDR(usb_pipedevice(urb->pipe)); + ptd->dw1 = usb_pipeendpoint(qtd->urb->pipe) >> 1; + ptd->dw1 |= PTD_DEVICE_ADDR(usb_pipedevice(qtd->urb->pipe)); pid_code = qtd->packet_type; - dw1 |= PTD_PID_TOKEN(pid_code); + ptd->dw1 |= PTD_PID_TOKEN(pid_code); - if (usb_pipebulk(urb->pipe)) - dw1 |= PTD_TRANS_BULK; - else if (usb_pipeint(urb->pipe)) - dw1 |= PTD_TRANS_INT; + if (usb_pipebulk(qtd->urb->pipe)) + ptd->dw1 |= PTD_TRANS_BULK; + else if (usb_pipeint(qtd->urb->pipe)) + ptd->dw1 |= PTD_TRANS_INT; - if (urb->dev->speed != USB_SPEED_HIGH) { + if (qtd->urb->dev->speed != USB_SPEED_HIGH) { /* split transaction */ - dw1 |= PTD_TRANS_SPLIT; - if (urb->dev->speed == USB_SPEED_LOW) - dw1 |= PTD_SE_USB_LOSPEED; + ptd->dw1 |= PTD_TRANS_SPLIT; + if (qtd->urb->dev->speed == USB_SPEED_LOW) + ptd->dw1 |= PTD_SE_USB_LOSPEED; - dw1 |= PTD_PORT_NUM(urb->dev->ttport); - dw1 |= PTD_HUB_NUM(urb->dev->tt->hub->devnum); + ptd->dw1 |= PTD_PORT_NUM(qtd->urb->dev->ttport); + ptd->dw1 |= PTD_HUB_NUM(qtd->urb->dev->tt->hub->devnum); /* SE bit for Split INT transfers */ - if (usb_pipeint(urb->pipe) && - (urb->dev->speed == USB_SPEED_LOW)) - dw1 |= 2 << 16; + if (usb_pipeint(qtd->urb->pipe) && + (qtd->urb->dev->speed == USB_SPEED_LOW)) + ptd->dw1 |= 2 << 16; - dw3 = 0; + ptd->dw3 = 0; rl = 0; nak = 0; } else { - dw0 |= PTD_MULTI(multi); - if (usb_pipecontrol(urb->pipe) || usb_pipebulk(urb->pipe)) - dw3 = qh->ping; + ptd->dw0 |= PTD_MULTI(multi); + if (usb_pipecontrol(qtd->urb->pipe) || + usb_pipebulk(qtd->urb->pipe)) + ptd->dw3 = qh->ping; else - dw3 = 0; + ptd->dw3 = 0; } /* DW2 */ - dw2 = 0; - dw2 |= PTD_DATA_START_ADDR(base_to_chip(payload)); - dw2 |= PTD_RL_CNT(rl); - dw3 |= PTD_NAC_CNT(nak); + 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); /* DW3 */ - if (usb_pipecontrol(urb->pipe)) - dw3 |= PTD_DATA_TOGGLE(qtd->toggle); - else - dw3 |= qh->toggle; - + ptd->dw3 |= qh->toggle; + if (usb_pipecontrol(qtd->urb->pipe)) { + if (qtd->data_buffer == qtd->urb->setup_packet) + ptd->dw3 &= ~PTD_DATA_TOGGLE(1); + else if (last_qtd_of_urb(qtd, qh)) + ptd->dw3 |= PTD_DATA_TOGGLE(1); + } - dw3 |= PTD_ACTIVE; + ptd->dw3 |= PTD_ACTIVE; /* Cerr */ - dw3 |= PTD_CERR(ERR_COUNTER); - - memset(ptd, 0, sizeof(*ptd)); - - ptd->dw0 = cpu_to_le32(dw0); - ptd->dw1 = cpu_to_le32(dw1); - ptd->dw2 = cpu_to_le32(dw2); - ptd->dw3 = cpu_to_le32(dw3); + ptd->dw3 |= PTD_CERR(ERR_COUNTER); } -static void transform_add_int(struct isp1760_hcd *priv, struct isp1760_qh *qh, - struct isp1760_qtd *qtd, struct urb *urb, - u32 payload, struct ptd *ptd) +static void transform_add_int(struct isp1760_qh *qh, + struct isp1760_qtd *qtd, struct ptd *ptd) { - u32 maxpacket; - u32 multi; - u32 numberofusofs; - u32 i; - u32 usofmask, usof; + u32 usof; u32 period; - maxpacket = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); - multi = 1 + ((maxpacket >> 11) & 0x3); - maxpacket &= 0x7ff; - /* length of the data per uframe */ - maxpacket = multi * maxpacket; - - numberofusofs = urb->transfer_buffer_length / maxpacket; - if (urb->transfer_buffer_length % maxpacket) - numberofusofs += 1; - - usofmask = 1; - usof = 0; - for (i = 0; i < numberofusofs; i++) { - usof |= usofmask; - usofmask <<= 1; - } - - if (urb->dev->speed != USB_SPEED_HIGH) { - /* split */ - ptd->dw5 = cpu_to_le32(0x1c); + /* + * Most of this is guessing. ISP1761 datasheet is quite unclear, and + * the algorithm from the original Philips driver code, which was + * pretty much used in this driver before as well, is quite horrendous + * and, i believe, incorrect. The code below follows the datasheet and + * USB2.0 spec as far as I can tell, and plug/unplug seems to be much + * more reliable this way (fingers crossed...). + */ - if (qh->period >= 32) - period = qh->period / 2; + if (qtd->urb->dev->speed == USB_SPEED_HIGH) { + /* urb->interval is in units of microframes (1/8 ms) */ + period = qtd->urb->interval >> 3; + + if (qtd->urb->interval > 4) + usof = 0x01; /* One bit set => + interval 1 ms * uFrame-match */ + else if (qtd->urb->interval > 2) + usof = 0x22; /* Two bits set => interval 1/2 ms */ + else if (qtd->urb->interval > 1) + usof = 0x55; /* Four bits set => interval 1/4 ms */ else - period = qh->period; - + usof = 0xff; /* All bits set => interval 1/8 ms */ } else { + /* urb->interval is in units of frames (1 ms) */ + period = qtd->urb->interval; + usof = 0x0f; /* Execute Start Split on any of the + four first uFrames */ - if (qh->period >= 8) - period = qh->period/8; - else - period = qh->period; - - if (period >= 32) - period = 16; - - if (qh->period >= 8) { - /* millisecond period */ - period = (period << 3); - } else { - /* usof based tranmsfers */ - /* minimum 4 usofs */ - usof = 0x11; - } + /* + * First 8 bits in dw5 is uSCS and "specifies which uSOF the + * complete split needs to be sent. Valid only for IN." Also, + * "All bits can be set to one for every transfer." (p 82, + * ISP1761 data sheet.) 0x1c is from Philips driver. Where did + * that number come from? 0xff seems to work fine... + */ + /* ptd->dw5 = 0x1c; */ + ptd->dw5 = 0xff; /* Execute Complete Split on any uFrame */ } - ptd->dw2 |= cpu_to_le32(period); - ptd->dw4 = cpu_to_le32(usof); + period = period >> 1;/* Ensure equal or shorter period than requested */ + period &= 0xf8; /* Mask off too large values and lowest unused 3 bits */ + + ptd->dw2 |= period; + ptd->dw4 = usof; } -static void transform_into_int(struct isp1760_hcd *priv, struct isp1760_qh *qh, - struct isp1760_qtd *qtd, struct urb *urb, - u32 payload, struct ptd *ptd) +static void transform_into_int(struct isp1760_qh *qh, + struct isp1760_qtd *qtd, struct ptd *ptd) { - transform_into_atl(priv, qh, qtd, urb, payload, ptd); - transform_add_int(priv, qh, qtd, urb, payload, ptd); + transform_into_atl(qh, qtd, ptd); + transform_add_int(qh, qtd, ptd); } static int qtd_fill(struct isp1760_qtd *qtd, void *databuffer, size_t len, @@ -695,10 +745,9 @@ static int qtd_fill(struct isp1760_qtd *qtd, void *databuffer, size_t len, qtd->data_buffer = databuffer; qtd->packet_type = GET_QTD_TOKEN_TYPE(token); - qtd->toggle = GET_DATA_TOGGLE(token); - if (len > HC_ATL_PL_SIZE) - count = HC_ATL_PL_SIZE; + if (len > MAX_PAYLOAD_SIZE) + count = MAX_PAYLOAD_SIZE; else count = len; @@ -706,29 +755,27 @@ static int qtd_fill(struct isp1760_qtd *qtd, void *databuffer, size_t len, return count; } -static int check_error(struct ptd *ptd) +static int check_error(struct usb_hcd *hcd, struct ptd *ptd) { int error = 0; - u32 dw3; - dw3 = le32_to_cpu(ptd->dw3); - if (dw3 & DW3_HALT_BIT) { + if (ptd->dw3 & DW3_HALT_BIT) { error = -EPIPE; - if (dw3 & DW3_ERROR_BIT) + if (ptd->dw3 & DW3_ERROR_BIT) pr_err("error bit is set in DW3\n"); } - if (dw3 & DW3_QTD_ACTIVE) { - printk(KERN_ERR "transfer active bit is set DW3\n"); - printk(KERN_ERR "nak counter: %d, rl: %d\n", (dw3 >> 19) & 0xf, - (le32_to_cpu(ptd->dw2) >> 25) & 0xf); + 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(u32 dw4) +static void check_int_err_status(struct usb_hcd *hcd, u32 dw4) { u32 i; @@ -737,79 +784,67 @@ static void check_int_err_status(u32 dw4) for (i = 0; i < 8; i++) { switch (dw4 & 0x7) { case INT_UNDERRUN: - printk(KERN_ERR "ERROR: under run , %d\n", i); + dev_err(hcd->self.controller, "Underrun (%d)\n", i); break; case INT_EXACT: - printk(KERN_ERR "ERROR: transaction error, %d\n", i); + dev_err(hcd->self.controller, + "Transaction error (%d)\n", i); break; case INT_BABBLE: - printk(KERN_ERR "ERROR: babble error, %d\n", i); + dev_err(hcd->self.controller, "Babble error (%d)\n", i); break; } dw4 >>= 3; } } -static void enqueue_one_qtd(struct isp1760_qtd *qtd, struct isp1760_hcd *priv, - u32 payload) +static void enqueue_one_qtd(struct usb_hcd *hcd, struct isp1760_qtd *qtd) { - u32 token; - struct usb_hcd *hcd = priv_to_hcd(priv); - - token = qtd->packet_type; - - if (qtd->length && (qtd->length <= HC_ATL_PL_SIZE)) { - switch (token) { + if (qtd->length && (qtd->length <= MAX_PAYLOAD_SIZE)) { + switch (qtd->packet_type) { case IN_PID: break; case OUT_PID: case SETUP_PID: - priv_write_copy(priv, qtd->data_buffer, - hcd->regs + payload, - qtd->length); + mem_writes8(hcd->regs, qtd->payload_addr, + qtd->data_buffer, qtd->length); } } } -static void enqueue_one_atl_qtd(u32 atl_regs, u32 payload, - struct isp1760_hcd *priv, struct isp1760_qh *qh, - struct urb *urb, u32 slot, struct isp1760_qtd *qtd) +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; - struct usb_hcd *hcd = priv_to_hcd(priv); - transform_into_atl(priv, qh, qtd, urb, payload, &ptd); - priv_write_copy(priv, (u32 *)&ptd, hcd->regs + atl_regs, sizeof(ptd)); - enqueue_one_qtd(qtd, priv, payload); + 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].urb = urb; priv->atl_ints[slot].qh = qh; priv->atl_ints[slot].qtd = qtd; - priv->atl_ints[slot].data_buffer = qtd->data_buffer; - priv->atl_ints[slot].payload = payload; - qtd->status |= URB_ENQUEUED | URB_TYPE_ATL; + qtd->status |= URB_ENQUEUED; qtd->status |= slot << 16; } -static void enqueue_one_int_qtd(u32 int_regs, u32 payload, - struct isp1760_hcd *priv, struct isp1760_qh *qh, - struct urb *urb, u32 slot, struct isp1760_qtd *qtd) +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; - struct usb_hcd *hcd = priv_to_hcd(priv); - transform_into_int(priv, qh, qtd, urb, payload, &ptd); - priv_write_copy(priv, (u32 *)&ptd, hcd->regs + int_regs, sizeof(ptd)); - enqueue_one_qtd(qtd, priv, payload); + 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].urb = urb; priv->int_ints[slot].qh = qh; priv->int_ints[slot].qtd = qtd; - priv->int_ints[slot].data_buffer = qtd->data_buffer; - priv->int_ints[slot].payload = payload; - qtd->status |= URB_ENQUEUED | URB_TYPE_INT; + qtd->status |= URB_ENQUEUED; qtd->status |= slot << 16; } @@ -818,9 +853,7 @@ static void enqueue_an_ATL_packet(struct usb_hcd *hcd, struct isp1760_qh *qh, { struct isp1760_hcd *priv = hcd_to_priv(hcd); u32 skip_map, or_map; - u32 queue_entry; u32 slot; - u32 atl_regs, payload; u32 buffstatus; /* @@ -831,38 +864,35 @@ static void enqueue_an_ATL_packet(struct usb_hcd *hcd, struct isp1760_qh *qh, */ mmiowb(); ndelay(195); - skip_map = isp1760_readl(hcd->regs + HC_ATL_PTD_SKIPMAP_REG); + skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG); BUG_ON(!skip_map); slot = __ffs(skip_map); - queue_entry = 1 << slot; - - atl_regs = ATL_REGS_OFFSET + slot * sizeof(struct ptd); - payload = alloc_mem(priv, qtd->length); + enqueue_one_atl_qtd(hcd, qh, slot, qtd); - enqueue_one_atl_qtd(atl_regs, payload, priv, qh, qtd->urb, 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); - or_map = isp1760_readl(hcd->regs + HC_ATL_IRQ_MASK_OR_REG); - or_map |= queue_entry; - isp1760_writel(or_map, hcd->regs + HC_ATL_IRQ_MASK_OR_REG); + skip_map &= ~(1 << slot); + reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, skip_map); - skip_map &= ~queue_entry; - isp1760_writel(skip_map, hcd->regs + HC_ATL_PTD_SKIPMAP_REG); + priv->atl_queued++; + if (priv->atl_queued == 2) + reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, + INTERRUPT_ENABLE_SOT_MASK); - buffstatus = isp1760_readl(hcd->regs + HC_BUFFER_STATUS_REG); + buffstatus = reg_read32(hcd->regs, HC_BUFFER_STATUS_REG); buffstatus |= ATL_BUFFER; - isp1760_writel(buffstatus, hcd->regs + HC_BUFFER_STATUS_REG); + 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) { - struct isp1760_hcd *priv = hcd_to_priv(hcd); u32 skip_map, or_map; - u32 queue_entry; u32 slot; - u32 int_regs, payload; u32 buffstatus; /* @@ -873,37 +903,34 @@ static void enqueue_an_INT_packet(struct usb_hcd *hcd, struct isp1760_qh *qh, */ mmiowb(); ndelay(195); - skip_map = isp1760_readl(hcd->regs + HC_INT_PTD_SKIPMAP_REG); + skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG); BUG_ON(!skip_map); slot = __ffs(skip_map); - queue_entry = 1 << slot; - |