diff options
Diffstat (limited to 'drivers/usb/isp1760/isp1760-hcd.c')
-rw-r--r-- | drivers/usb/isp1760/isp1760-hcd.c | 627 |
1 files changed, 481 insertions, 146 deletions
diff --git a/drivers/usb/isp1760/isp1760-hcd.c b/drivers/usb/isp1760/isp1760-hcd.c index a65f5f917ebe..016a54ea76f4 100644 --- a/drivers/usb/isp1760/isp1760-hcd.c +++ b/drivers/usb/isp1760/isp1760-hcd.c @@ -11,6 +11,8 @@ * * (c) 2011 Arvid Brodin <arvid.brodin@enea.com> * + * Copyright 2021 Linaro, Rui Miguel Silva <rui.silva@linaro.org> + * */ #include <linux/gpio/consumer.h> #include <linux/module.h> @@ -44,6 +46,9 @@ static inline struct isp1760_hcd *hcd_to_priv(struct usb_hcd *hcd) return *(struct isp1760_hcd **)hcd->hcd_priv; } +#define dw_to_le32(x) (cpu_to_le32((__force u32)x)) +#define le32_to_dw(x) ((__force __dw)(le32_to_cpu(x))) + /* urb state*/ #define DELETE_URB (0x0008) #define NO_TRANSFER_ACTIVE (0xffffffff) @@ -60,6 +65,18 @@ struct ptd { __dw dw6; __dw dw7; }; + +struct ptd_le32 { + __le32 dw0; + __le32 dw1; + __le32 dw2; + __le32 dw3; + __le32 dw4; + __le32 dw5; + __le32 dw6; + __le32 dw7; +}; + #define PTD_OFFSET 0x0400 #define ISO_PTD_OFFSET 0x0400 #define INT_PTD_OFFSET 0x0800 @@ -96,7 +113,7 @@ struct ptd { #define TO_DW2_RL(x) TO_DW(((x) << 25)) #define FROM_DW2_RL(x) ((TO_U32(x) >> 25) & 0xf) /* DW3 */ -#define FROM_DW3_NRBYTESTRANSFERRED(x) TO_U32((x) & 0x7fff) +#define FROM_DW3_NRBYTESTRANSFERRED(x) TO_U32((x) & 0x3fff) #define FROM_DW3_SCS_NRBYTESTRANSFERRED(x) TO_U32((x) & 0x07ff) #define TO_DW3_NAKCOUNT(x) TO_DW(((x) << 19)) #define FROM_DW3_NAKCOUNT(x) ((TO_U32(x) >> 19) & 0xf) @@ -123,7 +140,7 @@ struct ptd { /* Errata 1 */ #define RL_COUNTER (0) #define NAK_COUNTER (0) -#define ERR_COUNTER (2) +#define ERR_COUNTER (3) struct isp1760_qtd { u8 packet_type; @@ -165,6 +182,18 @@ struct urb_listitem { struct urb *urb; }; +static const u32 isp1763_hc_portsc1_fields[] = { + [PORT_OWNER] = BIT(13), + [PORT_POWER] = BIT(12), + [PORT_LSTATUS] = BIT(10), + [PORT_RESET] = BIT(8), + [PORT_SUSPEND] = BIT(7), + [PORT_RESUME] = BIT(6), + [PORT_PE] = BIT(2), + [PORT_CSC] = BIT(1), + [PORT_CONNECT] = BIT(0), +}; + /* * Access functions for isp176x registers regmap fields */ @@ -175,10 +204,30 @@ static u32 isp1760_hcd_read(struct usb_hcd *hcd, u32 field) return isp1760_field_read(priv->fields, field); } +/* + * We need, in isp1763, to write directly the values to the portsc1 + * register so it will make the other values to trigger. + */ +static void isp1760_hcd_portsc1_set_clear(struct isp1760_hcd *priv, u32 field, + u32 val) +{ + u32 bit = isp1763_hc_portsc1_fields[field]; + u32 port_status = readl(priv->base + ISP1763_HC_PORTSC1); + + if (val) + writel(port_status | bit, priv->base + ISP1763_HC_PORTSC1); + else + writel(port_status & ~bit, priv->base + ISP1763_HC_PORTSC1); +} + static void isp1760_hcd_write(struct usb_hcd *hcd, u32 field, u32 val) { struct isp1760_hcd *priv = hcd_to_priv(hcd); + if (unlikely(priv->is_isp1763 && + (field >= PORT_OWNER && field <= PORT_CONNECT))) + return isp1760_hcd_portsc1_set_clear(priv, field, val); + isp1760_field_write(priv->fields, field, val); } @@ -192,28 +241,40 @@ static void isp1760_hcd_clear(struct usb_hcd *hcd, u32 field) isp1760_hcd_write(hcd, field, 0); } -static int isp1760_hcd_set_poll_timeout(struct usb_hcd *hcd, u32 field, - u32 timeout_us) +static int isp1760_hcd_set_and_wait(struct usb_hcd *hcd, u32 field, + u32 timeout_us) +{ + struct isp1760_hcd *priv = hcd_to_priv(hcd); + u32 val; + + isp1760_hcd_set(hcd, field); + + return regmap_field_read_poll_timeout(priv->fields[field], val, + val, 10, timeout_us); +} + +static int isp1760_hcd_set_and_wait_swap(struct usb_hcd *hcd, u32 field, + u32 timeout_us) { struct isp1760_hcd *priv = hcd_to_priv(hcd); - unsigned int val; + u32 val; isp1760_hcd_set(hcd, field); - return regmap_field_read_poll_timeout(priv->fields[field], val, 1, 1, - timeout_us); + return regmap_field_read_poll_timeout(priv->fields[field], val, + !val, 10, timeout_us); } -static int isp1760_hcd_clear_poll_timeout(struct usb_hcd *hcd, u32 field, - u32 timeout_us) +static int isp1760_hcd_clear_and_wait(struct usb_hcd *hcd, u32 field, + u32 timeout_us) { struct isp1760_hcd *priv = hcd_to_priv(hcd); - unsigned int val; + u32 val; isp1760_hcd_clear(hcd, field); - return regmap_field_read_poll_timeout(priv->fields[field], val, 0, 1, - timeout_us); + return regmap_field_read_poll_timeout(priv->fields[field], val, + !val, 10, timeout_us); } static bool isp1760_hcd_is_set(struct usb_hcd *hcd, u32 field) @@ -221,12 +282,32 @@ static bool isp1760_hcd_is_set(struct usb_hcd *hcd, u32 field) return !!isp1760_hcd_read(hcd, field); } +static bool isp1760_hcd_ppc_is_set(struct usb_hcd *hcd) +{ + struct isp1760_hcd *priv = hcd_to_priv(hcd); + + if (priv->is_isp1763) + return true; + + return isp1760_hcd_is_set(hcd, HCS_PPC); +} + +static u32 isp1760_hcd_n_ports(struct usb_hcd *hcd) +{ + struct isp1760_hcd *priv = hcd_to_priv(hcd); + + if (priv->is_isp1763) + return 1; + + return isp1760_hcd_read(hcd, HCS_N_PORTS); +} + /* * 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. + * bank optimizations, you should use the more generic mem_read() below. * * For access to ptd memory, use the specialized ptd_read() and ptd_write() * below. @@ -281,19 +362,59 @@ static void bank_reads8(void __iomem *src_base, u32 src_offset, u32 bank_addr, } } -static void mem_reads8(struct usb_hcd *hcd, void __iomem *src_base, - u32 src_offset, void *dst, u32 bytes) +static void isp1760_mem_read(struct usb_hcd *hcd, u32 src_offset, void *dst, + u32 bytes) { + struct isp1760_hcd *priv = hcd_to_priv(hcd); + isp1760_hcd_write(hcd, MEM_BANK_SEL, ISP_BANK_0); isp1760_hcd_write(hcd, MEM_START_ADDR, src_offset); + ndelay(100); - ndelay(90); + bank_reads8(priv->base, src_offset, ISP_BANK_0, dst, bytes); +} - bank_reads8(src_base, src_offset, ISP_BANK_0, dst, bytes); +/* + * ISP1763 does not have the banks direct host controller memory access, + * needs to use the HC_DATA register. Add data read/write according to this, + * and also adjust 16bit access. + */ +static void isp1763_mem_read(struct usb_hcd *hcd, u16 srcaddr, + u16 *dstptr, u32 bytes) +{ + struct isp1760_hcd *priv = hcd_to_priv(hcd); + + /* Write the starting device address to the hcd memory register */ + isp1760_reg_write(priv->regs, ISP1763_HC_MEMORY, srcaddr); + ndelay(100); /* Delay between consecutive access */ + + /* As long there are at least 16-bit to read ... */ + while (bytes >= 2) { + *dstptr = __raw_readw(priv->base + ISP1763_HC_DATA); + bytes -= 2; + dstptr++; + } + + /* If there are no more bytes to read, return */ + if (bytes <= 0) + return; + + *((u8 *)dstptr) = (u8)(readw(priv->base + ISP1763_HC_DATA) & 0xFF); +} + +static void mem_read(struct usb_hcd *hcd, u32 src_offset, __u32 *dst, + u32 bytes) +{ + struct isp1760_hcd *priv = hcd_to_priv(hcd); + + if (!priv->is_isp1763) + return isp1760_mem_read(hcd, src_offset, (u16 *)dst, bytes); + + isp1763_mem_read(hcd, (u16)src_offset, (u16 *)dst, bytes); } -static void mem_writes8(void __iomem *dst_base, u32 dst_offset, - __u32 const *src, u32 bytes) +static void isp1760_mem_write(void __iomem *dst_base, u32 dst_offset, + __u32 const *src, u32 bytes) { __u32 __iomem *dst; @@ -327,33 +448,136 @@ static void mem_writes8(void __iomem *dst_base, u32 dst_offset, __raw_writel(*src, dst); } +static void isp1763_mem_write(struct usb_hcd *hcd, u16 dstaddr, u16 *src, + u32 bytes) +{ + struct isp1760_hcd *priv = hcd_to_priv(hcd); + + /* Write the starting device address to the hcd memory register */ + isp1760_reg_write(priv->regs, ISP1763_HC_MEMORY, dstaddr); + ndelay(100); /* Delay between consecutive access */ + + while (bytes >= 2) { + /* Get and write the data; then adjust the data ptr and len */ + __raw_writew(*src, priv->base + ISP1763_HC_DATA); + bytes -= 2; + src++; + } + + /* If there are no more bytes to process, return */ + if (bytes <= 0) + return; + + /* + * The only way to get here is if there is a single byte left, + * get it and write it to the data reg; + */ + writew(*((u8 *)src), priv->base + ISP1763_HC_DATA); +} + +static void mem_write(struct usb_hcd *hcd, u32 dst_offset, __u32 *src, + u32 bytes) +{ + struct isp1760_hcd *priv = hcd_to_priv(hcd); + + if (!priv->is_isp1763) + return isp1760_mem_write(priv->base, dst_offset, src, bytes); + + isp1763_mem_write(hcd, dst_offset, (u16 *)src, bytes); +} + /* * 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(struct usb_hcd *hcd, void __iomem *base, - u32 ptd_offset, u32 slot, struct ptd *ptd) +static void isp1760_ptd_read(struct usb_hcd *hcd, u32 ptd_offset, u32 slot, + struct ptd *ptd) { + u16 src_offset = ptd_offset + slot * sizeof(*ptd); + struct isp1760_hcd *priv = hcd_to_priv(hcd); + isp1760_hcd_write(hcd, MEM_BANK_SEL, ISP_BANK_0); - isp1760_hcd_write(hcd, MEM_START_ADDR, - ptd_offset + slot * sizeof(*ptd)); + isp1760_hcd_write(hcd, MEM_START_ADDR, src_offset); ndelay(90); - bank_reads8(base, ptd_offset + slot * sizeof(*ptd), ISP_BANK_0, - (void *)ptd, sizeof(*ptd)); + + bank_reads8(priv->base, src_offset, ISP_BANK_0, (void *)ptd, + sizeof(*ptd)); +} + +static void isp1763_ptd_read(struct usb_hcd *hcd, u32 ptd_offset, u32 slot, + struct ptd *ptd) +{ + u16 src_offset = ptd_offset + slot * sizeof(*ptd); + struct ptd_le32 le32_ptd; + + isp1763_mem_read(hcd, src_offset, (u16 *)&le32_ptd, sizeof(le32_ptd)); + /* Normalize the data obtained */ + ptd->dw0 = le32_to_dw(le32_ptd.dw0); + ptd->dw1 = le32_to_dw(le32_ptd.dw1); + ptd->dw2 = le32_to_dw(le32_ptd.dw2); + ptd->dw3 = le32_to_dw(le32_ptd.dw3); + ptd->dw4 = le32_to_dw(le32_ptd.dw4); + ptd->dw5 = le32_to_dw(le32_ptd.dw5); + ptd->dw6 = le32_to_dw(le32_ptd.dw6); + ptd->dw7 = le32_to_dw(le32_ptd.dw7); +} + +static void ptd_read(struct usb_hcd *hcd, u32 ptd_offset, u32 slot, + struct ptd *ptd) +{ + struct isp1760_hcd *priv = hcd_to_priv(hcd); + + if (!priv->is_isp1763) + return isp1760_ptd_read(hcd, ptd_offset, slot, ptd); + + isp1763_ptd_read(hcd, ptd_offset, slot, ptd); +} + +static void isp1763_ptd_write(struct usb_hcd *hcd, u32 ptd_offset, u32 slot, + struct ptd *cpu_ptd) +{ + u16 dst_offset = ptd_offset + slot * sizeof(*cpu_ptd); + struct ptd_le32 ptd; + + ptd.dw0 = dw_to_le32(cpu_ptd->dw0); + ptd.dw1 = dw_to_le32(cpu_ptd->dw1); + ptd.dw2 = dw_to_le32(cpu_ptd->dw2); + ptd.dw3 = dw_to_le32(cpu_ptd->dw3); + ptd.dw4 = dw_to_le32(cpu_ptd->dw4); + ptd.dw5 = dw_to_le32(cpu_ptd->dw5); + ptd.dw6 = dw_to_le32(cpu_ptd->dw6); + ptd.dw7 = dw_to_le32(cpu_ptd->dw7); + + isp1763_mem_write(hcd, dst_offset, (u16 *)&ptd.dw0, + 8 * sizeof(ptd.dw0)); } -static void ptd_write(void __iomem *base, u32 ptd_offset, u32 slot, - struct ptd *ptd) +static void isp1760_ptd_write(void __iomem *base, u32 ptd_offset, u32 slot, + struct ptd *ptd) { - mem_writes8(base, ptd_offset + slot*sizeof(*ptd) + sizeof(ptd->dw0), - (__force u32 *)&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 */ + u32 dst_offset = ptd_offset + slot * sizeof(*ptd); + + /* + * Make sure dw0 gets written last (after other dw's and after payload) + * since it contains the enable bit + */ + isp1760_mem_write(base, dst_offset + sizeof(ptd->dw0), + (__force u32 *)&ptd->dw1, 7 * sizeof(ptd->dw1)); wmb(); - mem_writes8(base, ptd_offset + slot * sizeof(*ptd), - (__force u32 *)&ptd->dw0, sizeof(ptd->dw0)); + isp1760_mem_write(base, dst_offset, (__force u32 *)&ptd->dw0, + sizeof(ptd->dw0)); } +static void ptd_write(struct usb_hcd *hcd, u32 ptd_offset, u32 slot, + struct ptd *ptd) +{ + struct isp1760_hcd *priv = hcd_to_priv(hcd); + + if (!priv->is_isp1763) + return isp1760_ptd_write(priv->base, ptd_offset, slot, ptd); + + isp1763_ptd_write(hcd, ptd_offset, slot, ptd); +} /* memory management of the 60kb on the chip from 0x1000 to 0xffff */ static void init_memory(struct isp1760_hcd *priv) @@ -430,7 +654,7 @@ static int ehci_reset(struct usb_hcd *hcd) hcd->state = HC_STATE_HALT; priv->next_statechange = jiffies; - return isp1760_hcd_set_poll_timeout(hcd, CMD_RESET, 250 * 1000); + return isp1760_hcd_set_and_wait_swap(hcd, CMD_RESET, 250 * 1000); } static struct isp1760_qh *qh_alloc(gfp_t flags) @@ -461,7 +685,6 @@ static int priv_init(struct usb_hcd *hcd) struct isp1760_hcd *priv = hcd_to_priv(hcd); u32 isoc_cache; u32 isoc_thres; - int i; spin_lock_init(&priv->lock); @@ -475,6 +698,11 @@ static int priv_init(struct usb_hcd *hcd) */ priv->periodic_size = DEFAULT_I_TDPS; + if (priv->is_isp1763) { + priv->i_thresh = 2; + return 0; + } + /* controllers may cache some of the periodic schedule ... */ isoc_cache = isp1760_hcd_read(hcd, HCC_ISOC_CACHE); isoc_thres = isp1760_hcd_read(hcd, HCC_ISOC_THRES); @@ -491,16 +719,24 @@ static int priv_init(struct usb_hcd *hcd) static int isp1760_hc_setup(struct usb_hcd *hcd) { struct isp1760_hcd *priv = hcd_to_priv(hcd); + u32 atx_reset; int result; u32 scratch; + u32 pattern; - isp1760_reg_write(priv->regs, ISP176x_HC_SCRATCH, 0xdeadbabe); + if (priv->is_isp1763) + pattern = 0xcafe; + else + pattern = 0xdeadcafe; + + isp1760_hcd_write(hcd, HC_SCRATCH, pattern); /* Change bus pattern */ - scratch = isp1760_reg_read(priv->regs, ISP176x_HC_CHIP_ID); - scratch = isp1760_reg_read(priv->regs, ISP176x_HC_SCRATCH); - if (scratch != 0xdeadbabe) { - dev_err(hcd->self.controller, "Scratch test failed.\n"); + scratch = isp1760_hcd_read(hcd, HC_CHIP_ID_HIGH); + dev_err(hcd->self.controller, "Scratch test 0x%08x\n", scratch); + scratch = isp1760_hcd_read(hcd, HC_SCRATCH); + if (scratch != pattern) { + dev_err(hcd->self.controller, "Scratch test failed. 0x%08x\n", scratch); return -ENODEV; } @@ -512,13 +748,13 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) * the host controller through the EHCI USB Command register. The device * has been reset in core code anyway, so this shouldn't matter. */ - isp1760_reg_write(priv->regs, ISP176x_HC_BUFFER_STATUS, 0); - isp1760_reg_write(priv->regs, ISP176x_HC_ATL_PTD_SKIPMAP, - NO_TRANSFER_ACTIVE); - isp1760_reg_write(priv->regs, ISP176x_HC_INT_PTD_SKIPMAP, - NO_TRANSFER_ACTIVE); - isp1760_reg_write(priv->regs, ISP176x_HC_ISO_PTD_SKIPMAP, - NO_TRANSFER_ACTIVE); + isp1760_hcd_clear(hcd, ISO_BUF_FILL); + isp1760_hcd_clear(hcd, INT_BUF_FILL); + isp1760_hcd_clear(hcd, ATL_BUF_FILL); + + isp1760_hcd_set(hcd, HC_ATL_PTD_SKIPMAP); + isp1760_hcd_set(hcd, HC_INT_PTD_SKIPMAP); + isp1760_hcd_set(hcd, HC_ISO_PTD_SKIPMAP); result = ehci_reset(hcd); if (result) @@ -527,11 +763,26 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) /* Step 11 passed */ /* ATL reset */ - isp1760_hcd_set(hcd, ALL_ATX_RESET); + if (priv->is_isp1763) + atx_reset = SW_RESET_RESET_ATX; + else + atx_reset = ALL_ATX_RESET; + + isp1760_hcd_set(hcd, atx_reset); mdelay(10); - isp1760_hcd_clear(hcd, ALL_ATX_RESET); + isp1760_hcd_clear(hcd, atx_reset); - isp1760_hcd_set(hcd, HC_INT_ENABLE); + if (priv->is_isp1763) { + isp1760_hcd_set(hcd, HW_OTG_DISABLE); + isp1760_hcd_set(hcd, HW_SW_SEL_HC_DC_CLEAR); + isp1760_hcd_set(hcd, HW_HC_2_DIS_CLEAR); + mdelay(10); + + isp1760_hcd_set(hcd, HW_INTF_LOCK); + } + + isp1760_hcd_set(hcd, HC_INT_IRQ_ENABLE); + isp1760_hcd_set(hcd, HC_ATL_IRQ_ENABLE); return priv_init(hcd); } @@ -751,45 +1002,45 @@ static void start_bus_transfer(struct usb_hcd *hcd, u32 ptd_offset, int slot, struct ptd *ptd) { struct isp1760_hcd *priv = hcd_to_priv(hcd); + const struct isp1760_memory_layout *mem = priv->memory_layout; int skip_map; - WARN_ON((slot < 0) || (slot > 31)); + WARN_ON((slot < 0) || (slot > mem->slot_num - 1)); WARN_ON(qtd->length && !qtd->payload_addr); WARN_ON(slots[slot].qtd); WARN_ON(slots[slot].qh); WARN_ON(qtd->status != QTD_PAYLOAD_ALLOC); + if (priv->is_isp1763) + ndelay(100); + /* Make sure done map has not triggered from some unlinked transfer */ if (ptd_offset == ATL_PTD_OFFSET) { - priv->atl_done_map |= isp1760_reg_read(priv->regs, - ISP176x_HC_ATL_PTD_DONEMAP); + skip_map = isp1760_hcd_read(hcd, HC_ATL_PTD_SKIPMAP); + isp1760_hcd_write(hcd, HC_ATL_PTD_SKIPMAP, + skip_map | (1 << slot)); + priv->atl_done_map |= isp1760_hcd_read(hcd, HC_ATL_PTD_DONEMAP); priv->atl_done_map &= ~(1 << slot); } else { - priv->int_done_map |= isp1760_reg_read(priv->regs, - ISP176x_HC_INT_PTD_DONEMAP); + skip_map = isp1760_hcd_read(hcd, HC_INT_PTD_SKIPMAP); + isp1760_hcd_write(hcd, HC_INT_PTD_SKIPMAP, + skip_map | (1 << slot)); + priv->int_done_map |= isp1760_hcd_read(hcd, HC_INT_PTD_DONEMAP); priv->int_done_map &= ~(1 << slot); } + skip_map &= ~(1 << slot); qh->slot = slot; qtd->status = QTD_XFER_STARTED; slots[slot].timestamp = jiffies; slots[slot].qtd = qtd; slots[slot].qh = qh; - ptd_write(priv->base, ptd_offset, slot, ptd); + ptd_write(hcd, ptd_offset, slot, ptd); - if (ptd_offset == ATL_PTD_OFFSET) { - skip_map = isp1760_reg_read(priv->regs, - ISP176x_HC_ATL_PTD_SKIPMAP); - skip_map &= ~(1 << qh->slot); - isp1760_reg_write(priv->regs, ISP176x_HC_ATL_PTD_SKIPMAP, - skip_map); - } else { - skip_map = isp1760_reg_read(priv->regs, - ISP176x_HC_INT_PTD_SKIPMAP); - skip_map &= ~(1 << qh->slot); - isp1760_reg_write(priv->regs, ISP176x_HC_INT_PTD_SKIPMAP, - skip_map); - } + if (ptd_offset == ATL_PTD_OFFSET) + isp1760_hcd_write(hcd, HC_ATL_PTD_SKIPMAP, skip_map); + else + isp1760_hcd_write(hcd, HC_INT_PTD_SKIPMAP, skip_map); } static int is_short_bulk(struct isp1760_qtd *qtd) @@ -801,7 +1052,6 @@ static int is_short_bulk(struct isp1760_qtd *qtd) static void collect_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh, struct list_head *urb_list) { - struct isp1760_hcd *priv = hcd_to_priv(hcd); struct isp1760_qtd *qtd, *qtd_next; struct urb_listitem *urb_listitem; int last_qtd; @@ -819,10 +1069,9 @@ static void collect_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh, if (qtd->actual_length) { switch (qtd->packet_type) { case IN_PID: - mem_reads8(hcd, priv->base, - qtd->payload_addr, - qtd->data_buffer, - qtd->actual_length); + mem_read(hcd, qtd->payload_addr, + qtd->data_buffer, + qtd->actual_length); fallthrough; case OUT_PID: qtd->urb->actual_length += @@ -866,6 +1115,8 @@ static void collect_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh, static void enqueue_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh) { struct isp1760_hcd *priv = hcd_to_priv(hcd); + const struct isp1760_memory_layout *mem = priv->memory_layout; + int slot_num = mem->slot_num; int ptd_offset; struct isp1760_slotinfo *slots; int curr_slot, free_slot; @@ -892,7 +1143,7 @@ static void enqueue_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh) } free_slot = -1; - for (curr_slot = 0; curr_slot < 32; curr_slot++) { + for (curr_slot = 0; curr_slot < slot_num; curr_slot++) { if ((free_slot == -1) && (slots[curr_slot].qtd == NULL)) free_slot = curr_slot; if (slots[curr_slot].qh == qh) @@ -907,11 +1158,10 @@ static void enqueue_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh) if ((qtd->length) && (!qtd->payload_addr)) break; - if ((qtd->length) && - ((qtd->packet_type == SETUP_PID) || - (qtd->packet_type == OUT_PID))) { - mem_writes8(priv->base, qtd->payload_addr, - qtd->data_buffer, qtd->length); + if (qtd->length && (qtd->packet_type == SETUP_PID || + qtd->packet_type == OUT_PID)) { + mem_write(hcd, qtd->payload_addr, + qtd->data_buffer, qtd->length); } qtd->status = QTD_PAYLOAD_ALLOC; @@ -924,7 +1174,7 @@ static void enqueue_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh) "available for transfer\n", __func__); */ /* Start xfer for this endpoint if not already done */ - if ((curr_slot > 31) && (free_slot > -1)) { + if ((curr_slot > slot_num - 1) && (free_slot > -1)) { if (usb_pipeint(qtd->urb->pipe)) create_ptd_int(qh, qtd, &ptd); else @@ -1111,9 +1361,9 @@ static void handle_done_ptds(struct usb_hcd *hcd) int modified; int skip_map; - skip_map = isp1760_reg_read(priv->regs, ISP176x_HC_INT_PTD_SKIPMAP); + skip_map = isp1760_hcd_read(hcd, HC_INT_PTD_SKIPMAP); priv->int_done_map &= ~skip_map; - skip_map = isp1760_reg_read(priv->regs, ISP176x_HC_ATL_PTD_SKIPMAP); + skip_map = isp1760_hcd_read(hcd, HC_ATL_PTD_SKIPMAP); priv->atl_done_map &= ~skip_map; modified = priv->int_done_map || priv->atl_done_map; @@ -1131,7 +1381,7 @@ static void handle_done_ptds(struct usb_hcd *hcd) continue; } ptd_offset = INT_PTD_OFFSET; - ptd_read(hcd, priv->base, INT_PTD_OFFSET, slot, &ptd); + ptd_read(hcd, INT_PTD_OFFSET, slot, &ptd); state = check_int_transfer(hcd, &ptd, slots[slot].qtd->urb); } else { @@ -1146,7 +1396,7 @@ static void handle_done_ptds(struct usb_hcd *hcd) continue; } ptd_offset = ATL_PTD_OFFSET; - ptd_read(hcd, priv->base, ATL_PTD_OFFSET, slot, &ptd); + ptd_read(hcd, ATL_PTD_OFFSET, slot, &ptd); state = check_atl_transfer(hcd, &ptd, slots[slot].qtd->urb); } @@ -1239,27 +1489,30 @@ static void handle_done_ptds(struct usb_hcd *hcd) static irqreturn_t isp1760_irq(struct usb_hcd *hcd) { struct isp1760_hcd *priv = hcd_to_priv(hcd); - u32 imask; irqreturn_t irqret = IRQ_NONE; + u32 int_reg; + u32 imask; spin_lock(&priv->lock); if (!(hcd->state & HC_STATE_RUNNING)) goto leave; - imask = isp1760_reg_read(priv->regs, ISP176x_HC_INTERRUPT); + imask = isp1760_hcd_read(hcd, HC_INTERRUPT); if (unlikely(!imask)) goto leave; - isp1760_reg_write(priv->regs, ISP176x_HC_INTERRUPT, imask); /* Clear */ - priv->int_done_map |= isp1760_reg_read(priv->regs, - ISP176x_HC_INT_PTD_DONEMAP); - priv->atl_done_map |= isp1760_reg_read(priv->regs, - ISP176x_HC_ATL_PTD_DONEMAP); + int_reg = priv->is_isp1763 ? ISP1763_HC_INTERRUPT : + ISP176x_HC_INTERRUPT; + isp1760_reg_write(priv->regs, int_reg, imask); + + priv->int_done_map |= isp1760_hcd_read(hcd, HC_INT_PTD_DONEMAP); + priv->atl_done_map |= isp1760_hcd_read(hcd, HC_ATL_PTD_DONEMAP); handle_done_ptds(hcd); irqret = IRQ_HANDLED; + leave: spin_unlock(&priv->lock); @@ -1300,17 +1553,18 @@ static void errata2_function(struct timer_list *unused) { struct usb_hcd *hcd = errata2_timer_hcd; struct isp1760_hcd *priv = hcd_to_priv(hcd); + const struct isp1760_memory_layout *mem = priv->memory_layout; int slot; struct ptd ptd; unsigned long spinflags; spin_lock_irqsave(&priv->lock, spinflags); - for (slot = 0; slot < 32; slot++) + for (slot = 0; slot < mem->slot_num; slot++) if (priv->atl_slots[slot].qh && time_after(jiffies, priv->atl_slots[slot].timestamp + msecs_to_jiffies(SLOT_TIMEOUT))) { - ptd_read(hcd, priv->base, ATL_PTD_OFFSET, slot, &ptd); + ptd_read(hcd, ATL_PTD_OFFSET, slot, &ptd); if (!FROM_DW0_VALID(ptd.dw0) && !FROM_DW3_ACTIVE(ptd.dw3)) priv->atl_done_map |= 1 << slot; @@ -1325,23 +1579,113 @@ static void errata2_function(struct timer_list *unused) add_timer(&errata2_timer); } +static int isp1763_run(struct usb_hcd *hcd) +{ + struct isp1760_hcd *priv = hcd_to_priv(hcd); + int retval; + u32 chipid_h; + u32 chipid_l; + u32 chip_rev; + u32 ptd_atl_int; + u32 ptd_iso; + + hcd->uses_new_polling = 1; + hcd->state = HC_STATE_RUNNING; + + chipid_h = isp1760_hcd_read(hcd, HC_CHIP_ID_HIGH); + chipid_l = isp1760_hcd_read(hcd, HC_CHIP_ID_LOW); + chip_rev = isp1760_hcd_read(hcd, HC_CHIP_REV); + dev_info(hcd->self.controller, "USB ISP %02x%02x HW rev. %d started\n", + chipid_h, chipid_l, chip_rev); + + isp1760_hcd_clear(hcd, ISO_BUF_FILL); + isp1760_hcd_clear(hcd, INT_BUF_FILL); + isp1760_hcd_clear(hcd, ATL_BUF_FILL); + + isp1760_hcd_set(hcd, HC_ATL_PTD_SKIPMAP); + isp1760_hcd_set(hcd, HC_INT_PTD_SKIPMAP); + isp1760_hcd_set(hcd, HC_ISO_PTD_SKIPMAP); + ndelay(100); + isp1760_hcd_clear(hcd, HC_ATL_PTD_DONEMAP); + isp1760_hcd_clear(hcd, HC_INT_PTD_DONEMAP); + isp1760_hcd_clear(hcd, HC_ISO_PTD_DONEMAP); + + isp1760_hcd_set(hcd, HW_OTG_DISABLE); + isp1760_reg_write(priv->regs, ISP1763_HC_OTG_CTRL_CLEAR, BIT(7)); + isp1760_reg_write(priv->regs, ISP1763_HC_OTG_CTRL_CLEAR, BIT(15)); + mdelay(10); + + isp1760_hcd_set(hcd, HC_INT_IRQ_ENABLE); + isp1760_hcd_set(hcd, HC_ATL_IRQ_ENABLE); + + isp1760_hcd_set(hcd, HW_GLOBAL_INTR_EN); + + isp1760_hcd_clear(hcd, HC_ATL_IRQ_MASK_AND); + isp1760_hcd_clear(hcd, HC_INT_IRQ_MASK_AND); + isp1760_hcd_clear(hcd, HC_ISO_IRQ_MASK_AND); + + isp1760_hcd_set(hcd, HC_ATL_IRQ_MASK_OR); + isp1760_hcd_set(hcd, HC_INT_IRQ_MASK_OR); + isp1760_hcd_set(hcd, HC_ISO_IRQ_MASK_OR); + + ptd_atl_int = 0x8000; + ptd_iso = 0x0001; + + isp1760_hcd_write(hcd, HC_ATL_PTD_LASTPTD, ptd_atl_int); + isp1760_hcd_write(hcd, HC_INT_PTD_LASTPTD, ptd_atl_int); + isp1760_hcd_write(hcd, HC_ISO_PTD_LASTPTD, ptd_iso); + + isp1760_hcd_set(hcd, ATL_BUF_FILL); + isp1760_hcd_set(hcd, INT_BUF_FILL); + + isp1760_hcd_clear(hcd, CMD_LRESET); + isp1760_hcd_clear(hcd, CMD_RESET); + + retval = isp1760_hcd_set_and_wait(hcd, CMD_RUN, 250 * 1000); + if (retval) + return retval; + + down_write(&ehci_cf_port_reset_rwsem); + retval = isp1760_hcd_set_and_wait(hcd, FLAG_CF, 250 * 1000); + up_write(&ehci_cf_port_reset_rwsem); + retval = 0; + if (retval) + return retval; + + return 0; +} + static int isp1760_run(struct usb_hcd *hcd) { struct isp1760_hcd *priv = hcd_to_priv(hcd); int retval; - u32 chipid; + u32 chipid_h; + u32 chipid_l; + u32 chip_rev; + u32 ptd_atl_int; + u32 ptd_iso; + + /* + * ISP1763 have some differences in the setup and order to enable + * the ports, disable otg, setup buffers, and ATL, INT, ISO status. + * So, just handle it a separate sequence. + */ + if (priv->is_isp1763) + return isp1763_run(hcd); hcd->uses_new_polling = 1; hcd->state = HC_STATE_RUNNING; /* Set PTD interrupt AND & OR maps */ - isp1760_reg_write(priv->regs, ISP176x_HC_ATL_IRQ_MASK_AND, 0); - isp1760_reg_write(priv->regs, ISP176x_HC_ATL_IRQ_MASK_OR, 0xffffffff); - isp1760_reg_write(priv->regs, ISP176x_HC_INT_IRQ_MASK_AND, 0); - isp1760_reg_write(priv->regs, ISP176x_HC_INT_IRQ_MASK_OR, 0xffffffff); - isp1760_reg_write(priv->regs, ISP176x_HC_ISO_IRQ_MASK_AND, 0); - isp1760_reg_write(priv->regs, ISP176x_HC_ISO_IRQ_MASK_OR, 0xffffffff); + isp1760_hcd_clear(hcd, HC_ATL_IRQ_MASK_AND); + isp1760_hcd_clear(hcd, HC_INT_IRQ_MASK_AND); + isp1760_hcd_clear(hcd, HC_ISO_IRQ_MASK_AND); + + isp1760_hcd_set(hcd, HC_ATL_IRQ_MASK_OR); + isp1760_hcd_set(hcd, HC_INT_IRQ_MASK_OR); + isp1760_hcd_set(hcd, HC_ISO_IRQ_MASK_OR); + /* step 23 passed */ isp1760_hcd_set(hcd, HW_GLOBAL_INTR_EN); @@ -1349,7 +1693,7 @@ static int isp1760_run(struct usb_hcd *hcd) isp1760_hcd_clear(hcd, CMD_LRESET); isp1760_hcd_clear(hcd, CMD_RESET); - retval = isp1760_hcd_set_poll_timeout(hcd, CMD_RUN, 250 * 1000); + retval = isp1760_hcd_set_and_wait(hcd, CMD_RUN, 250 * 1000); if (retval) return retval; @@ -1360,7 +1704,7 @@ static int isp1760_run(struct usb_hcd *hcd) */ down_write(&ehci_cf_port_reset_rwsem); - retval = isp1760_hcd_set_poll_timeout(hcd, FLAG_CF, 250 * 1000); + retval = isp1760_hcd_set_and_wait(hcd, FLAG_CF, 250 * 1000); up_write(&ehci_cf_port_reset_rwsem); if (retval) return retval; @@ -1370,19 +1714,25 @@ static int isp1760_run(struct usb_hcd *hcd) errata2_timer.expires = jiffies + msecs_to_jiffies(SLOT_CHECK_PERIOD); add_timer(&errata2_timer); - chipid = isp1760_reg_read(priv->regs, ISP176x_HC_CHIP_ID); - dev_info(hcd->self.controller, "USB ISP %04x HW rev. %d started\n", - chipid & 0xffff, chipid >> 16); + chipid_h = isp1760_hcd_read(hcd, HC_CHIP_ID_HIGH); + chipid_l = isp1760_hcd_read(hcd, HC_CHIP_ID_LOW); + chip_rev = isp1760_hcd_read(hcd, HC_CHIP_REV); + dev_info(hcd->self.controller, "USB ISP %02x%02x HW rev. %d started\n", + chipid_h, chipid_l, chip_rev); /* PTD Register Init Part 2, Step 28 */ /* Setup registers controlling PTD checking */ - isp1760_reg_write(priv->regs, ISP176x_HC_ATL_PTD_LASTPTD, 0x80000000); - isp1760_reg_write(priv->regs, ISP176x_HC_INT_PTD_LASTPTD, 0x80000000); - isp1760_reg_write(priv->regs, ISP176x_HC_ISO_PTD_LASTPTD, 0x00000001); - isp1760_reg_write(priv->regs, ISP176x_HC_ATL_PTD_SKIPMAP, 0xffffffff); - isp1760_reg_write(priv->regs, ISP176x_HC_INT_PTD_SKIPMAP, 0xffffffff); - isp1760_reg_write(priv->regs, ISP176x_HC_ISO_PTD_SKIPMAP, 0xffffffff); + ptd_atl_int = 0x80000000; + ptd_iso = 0x00000001; + + isp1760_hcd_write(hcd, HC_ATL_PTD_LASTPTD, ptd_atl_int); + isp1760_hcd_write(hcd, HC_INT_PTD_LASTPTD, ptd_atl_int); + isp1760_hcd_write(hcd, HC_ISO_PTD_LASTPTD, ptd_iso); + + isp1760_hcd_set(hcd, HC_ATL_PTD_SKIPMAP); + isp1760_hcd_set(hcd, HC_INT_PTD_SKIPMAP); + isp1760_hcd_set(hcd, HC_ISO_PTD_SKIPMAP); isp1760_hcd_set(hcd, ATL_BUF_FILL); isp1760_hcd_set(hcd, INT_BUF_FILL); @@ -1623,19 +1973,16 @@ static void kill_transfer(struct usb_hcd *hcd, struct urb *urb, /* We need to forcefully reclaim the slot since some transfers never return, e.g. interrupt transfers and NAKed bulk transfers. */ if (usb_pipecontrol(urb->pipe) || usb_pipebulk(urb->pipe)) { - skip_map = isp1760_reg_read(priv->regs, - ISP176x_HC_ATL_PTD_SKIPMAP); + skip_map = isp1760_hcd_read(hcd, HC_ATL_PTD_SKIPMAP); skip_map |= (1 << qh->slot); - isp1760_reg_write(priv->regs, ISP176x_HC_ATL_PTD_SKIPMAP, - skip_map); + isp1760_hcd_write(hcd, HC_ATL_PTD_SKIPMAP, skip_map); + ndelay(100); priv->atl_slots[qh->slot].qh = NULL; priv->atl_slots[qh->slot].qtd = NULL; } else { - skip_map = isp1760_reg_read(priv->regs, - ISP176x_HC_INT_PTD_SKIPMAP); + skip_map = isp1760_hcd_read(hcd, HC_INT_PTD_SKIPMAP); skip_map |= (1 << qh->slot); - isp1760_reg_write(priv->regs, ISP176x_HC_INT_PTD_SKIPMAP, - skip_map); + isp1760_hcd_write(hcd, HC_INT_PTD_SKIPMAP, skip_map); priv->int_slots[qh->slot].qh = NULL; priv->int_slots[qh->slot].qtd = NULL; } @@ -1791,7 +2138,7 @@ static void isp1760_hub_descriptor(struct isp1760_hcd *priv, int ports; u16 temp; - ports = isp1760_hcd_read(priv->hcd, HCS_N_PORTS); + ports = isp1760_hcd_n_ports(priv->hcd); desc->bDescriptorType = USB_DT_HUB; /* priv 1.0, 2.3.9 says 20ms max */ @@ -1808,7 +2155,7 @@ static void isp1760_hub_descriptor(struct isp1760_hcd *priv, /* per-port overcurrent reporting */ temp = HUB_CHAR_INDV_PORT_OCPM; - if (isp1760_hcd_is_set(priv->hcd, HCS_PPC)) + if (isp1760_hcd_ppc_is_set(priv->hcd)) /* per-port power control */ temp |= HUB_CHAR_INDV_PORT_LPSM; else @@ -1849,7 +2196,7 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq, int retval = 0; int ports; - ports = isp1760_hcd_read(hcd, HCS_N_PORTS); + ports = isp1760_hcd_n_ports(hcd); /* * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR. @@ -1908,7 +2255,7 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq, /* we auto-clear this feature */ break; case USB_PORT_FEAT_POWER: - if (isp1760_hcd_is_set(hcd, HCS_PPC)) + if (isp1760_hcd_ppc_is_set(hcd)) isp1760_hcd_clear(hcd, PORT_POWER); break; case USB_PORT_FEAT_C_CONNECTION: @@ -1923,7 +2270,7 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq, default: goto error; } - isp1760_reg_read(priv->regs, ISP176x_HC_USBCMD); + isp1760_hcd_read(hcd, CMD_RUN); break; case GetHubDescriptor: isp1760_hub_descriptor(priv, (struct usb_hub_descriptor *) @@ -1943,7 +2290,6 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq, if (isp1760_hcd_is_set(hcd, PORT_CSC)) status |= USB_PORT_STAT_C_CONNECTION << 16; - /* whoever resumes must GetPortStatus to complete it!! */ if (isp1760_hcd_is_set(hcd, PORT_RESUME)) { dev_err(hcd->self.controller, "Port resume should be skipped.\n"); @@ -1966,7 +2312,7 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq, /* stop resume signaling */ isp1760_hcd_clear(hcd, PORT_CSC); - retval = isp1760_hcd_clear_poll_timeout(hcd, + retval = isp1760_hcd_clear_and_wait(hcd, PORT_RESUME, 2000); if (retval != 0) { dev_err(hcd->self.controller, @@ -1987,11 +2333,11 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq, /* REVISIT: some hardware needs 550+ usec to clear * this bit; seems too long to spin routinely... */ - retval = isp1760_hcd_clear_poll_timeout(hcd, PORT_RESET, - 750); + retval = isp1760_hcd_clear_and_wait(hcd, PORT_RESET, + 750); if (retval != 0) { dev_err(hcd->self.controller, "port %d reset error %d\n", - wIndex + 1, retval); + wIndex + 1, retval); goto error; } @@ -2039,6 +2385,7 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq, if (!wIndex || wIndex > ports) goto error; wIndex--; + if (isp1760_hcd_is_set(hcd, PORT_OWNER)) break; @@ -2055,7 +2402,7 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq, isp1760_hcd_set(hcd, PORT_SUSPEND); break; case USB_PORT_FEAT_POWER: - if (isp1760_hcd_is_set(hcd, HCS_PPC)) + if (isp1760_hcd_ppc_is_set(hcd)) isp1760_hcd_set(hcd, PORT_POWER); break; case USB_PORT_FEAT_RESET: @@ -2084,7 +2431,6 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq, default: goto error; } - isp1760_reg_read(priv->regs, ISP176x_HC_USBCMD); break; default: @@ -2219,22 +2565,14 @@ int isp1760_hcd_register(struct isp1760_hcd *priv, struct resource *mem, priv->hcd = hcd; - priv->memory_pool = kcalloc(mem_layout->payload_blocks, - sizeof(struct isp1760_memory_chunk), - GFP_KERNEL); - if (!priv->memory_pool) { - ret = -ENOMEM; - goto put_hcd; - } - - priv->atl_slots = kcalloc(mem_layout->ptd_num, + priv->atl_slots = kcalloc(mem_layout->slot_num, sizeof(struct isp1760_slotinfo), GFP_KERNEL); if (!priv->atl_slots) { ret = -ENOMEM; - goto free_mem_pool; + goto put_hcd; } - priv->int_slots = kcalloc(mem_layout->ptd_num, + priv->int_slots = kcalloc(mem_layout->slot_num, sizeof(struct isp1760_slotinfo), GFP_KERNEL); if (!priv->int_slots) { ret = -ENOMEM; @@ -2262,8 +2600,6 @@ free_int_slots: kfree(priv->int_slots); free_atl_slots: kfree(priv->atl_slots); -free_mem_pool: - kfree(priv->memory_pool); put_hcd: usb_put_hcd(hcd); return ret; @@ -2278,5 +2614,4 @@ void isp1760_hcd_unregister(struct isp1760_hcd *priv) usb_put_hcd(priv->hcd); kfree(priv->atl_slots); kfree(priv->int_slots); - kfree(priv->memory_pool); } |