aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/host/isp1760-hcd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host/isp1760-hcd.c')
-rw-r--r--drivers/usb/host/isp1760-hcd.c1854
1 files changed, 941 insertions, 913 deletions
diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c
index 795345ad45e..51a0ae9cdd1 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>
@@ -19,21 +21,33 @@
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/mm.h>
+#include <linux/timer.h>
#include <asm/unaligned.h>
#include <asm/cacheflush.h>
+#include <linux/gpio.h>
#include "isp1760-hcd.h"
static struct kmem_cache *qtd_cachep;
static struct kmem_cache *qh_cachep;
+static struct kmem_cache *urb_listitem_cachep;
+
+enum queue_head_types {
+ QH_CONTROL,
+ QH_BULK,
+ QH_INTERRUPT,
+ QH_END
+};
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 qh_list[QH_END];
/* periodic schedule support */
#define DEFAULT_I_TDPS 1024
@@ -42,6 +56,8 @@ struct isp1760_hcd {
unsigned long reset_done;
unsigned long next_statechange;
unsigned int devflags;
+
+ int rst_gpio;
};
static inline struct isp1760_hcd *hcd_to_priv(struct usb_hcd *hcd)
@@ -85,18 +101,35 @@ 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;
+ int tt_buffer_dirty; /* See USB2.0 spec section 11.17.5 */
+};
+
+struct urb_listitem {
+ struct list_head urb_list;
+ struct urb *urb;
};
/*
@@ -272,7 +305,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 +313,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 +326,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 +338,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 +347,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,40 +386,40 @@ 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)
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
u32 hcc_params;
+ int i;
spin_lock_init(&priv->lock);
+ for (i = 0; i < QH_END; i++)
+ INIT_LIST_HEAD(&priv->qh_list[i]);
+
/*
* hw default: 1K periodic list heads, one per frame.
* periodic_size can shrink by USBCMD update if hcc_params allows.
@@ -434,6 +443,18 @@ static int isp1760_hc_setup(struct usb_hcd *hcd)
int result;
u32 scratch, hwmode;
+ /* low-level chip reset */
+ if (gpio_is_valid(priv->rst_gpio)) {
+ unsigned int rst_lvl;
+
+ rst_lvl = (priv->devflags &
+ ISP1760_FLAG_RESET_ACTIVE_HIGH) ? 1 : 0;
+
+ gpio_set_value(priv->rst_gpio, rst_lvl);
+ mdelay(50);
+ gpio_set_value(priv->rst_gpio, !rst_lvl);
+ }
+
/* Setup HW Mode Control: This assumes a level active-low interrupt */
hwmode = HW_DATA_BUS_32BIT;
@@ -468,7 +489,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);
@@ -493,7 +517,6 @@ static int isp1760_hc_setup(struct usb_hcd *hcd)
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);
/*
@@ -510,77 +533,6 @@ static int isp1760_hc_setup(struct usb_hcd *hcd)
return priv_init(hcd);
}
-static void isp1760_init_maps(struct usb_hcd *hcd)
-{
- /*set last maps, for iso its only 1, else 32 tds bitmap*/
- 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)
-{
- 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)
-{
- int retval;
- u32 temp;
- u32 command;
- u32 chipid;
-
- hcd->uses_new_polling = 1;
-
- hcd->state = HC_STATE_RUNNING;
- isp1760_enable_interrupts(hcd);
- temp = reg_read32(hcd->regs, HC_HW_MODE_CTRL);
- reg_write32(hcd->regs, HC_HW_MODE_CTRL, temp | HW_GLOBAL_INTR_EN);
-
- command = reg_read32(hcd->regs, HC_USBCMD);
- command &= ~(CMD_LRESET|CMD_RESET);
- command |= CMD_RUN;
- reg_write32(hcd->regs, HC_USBCMD, command);
-
- retval = handshake(hcd, HC_USBCMD, CMD_RUN, CMD_RUN,
- 250 * 1000);
- if (retval)
- return retval;
-
- /*
- * XXX
- * Spec says to write FLAG_CF as last config action, priv code grabs
- * the semaphore while doing so.
- */
- down_write(&ehci_cf_port_reset_rwsem);
- reg_write32(hcd->regs, HC_CONFIGFLAG, FLAG_CF);
-
- retval = handshake(hcd, HC_CONFIGFLAG, FLAG_CF, FLAG_CF, 250 * 1000);
- up_write(&ehci_cf_port_reset_rwsem);
- if (retval)
- return retval;
-
- 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 */
- isp1760_init_maps(hcd);
-
- /* GRR this is run-once init(), being done every time the HC starts.
- * So long as they're part of class devices, we can't do it init()
- * since the class device isn't created that early.
- */
- return 0;
-}
-
static u32 base_to_chip(u32 base)
{
return ((base - 0x400) >> 3);
@@ -598,12 +550,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 +575,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 +685,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 +718,765 @@ __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);
+
+ /* 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 << slot);
+ } else {
+ priv->int_done_map |= reg_read32(hcd->regs,
+ HC_INT_PTD_DONEMAP_REG);
+ priv->int_done_map &= ~(1 << slot);
+ }
- urb = qtd->urb;
- do {
- qtd = clean_this_qtd(qtd, qh);
- } while (qtd && (qtd->urb == urb));
+ qh->slot = slot;
+ qtd->status = QTD_XFER_STARTED;
+ slots[slot].timestamp = jiffies;
+ slots[slot].qtd = qtd;
+ slots[slot].qh = qh;
+ ptd_write(hcd->regs, ptd_offset, slot, ptd);
- return qtd;
+ if (ptd_offset == ATL_PTD_OFFSET) {
+ 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 {
+ 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--;
+ last_qtd = last_qtd_of_urb(qtd, qh);
+
+ 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; /* Try again on next call */
+ 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;
+ list_del(&qtd->qtd_list);
+ qtd_free(qtd);
+ }
+}
- /* 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.
- */
+#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;
- length = PTD_XFERRED_LENGTH(ptd.dw3);
- dev_err(hcd->self.controller,
- "Should reload now... transferred %d "
- "of %zu\n", length, qtd->length);
- BUG();
- }
+ if (unlikely(list_empty(&qh->qtd_list))) {
+ WARN_ON(1);
+ return;
+ }
- if (!nakcount && (ptd.dw3 & DW3_QTD_ACTIVE)) {
- u32 buffstatus;
+ /* Make sure this endpoint's TT buffer is clean before queueing ptds */
+ if (qh->tt_buffer_dirty)
+ 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);
-
- ptd.dw0 |= PTD_VALID;
- ptd_write(hcd->regs, ATL_PTD_OFFSET, slot, &ptd);
-
- 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);
- continue;
- }
+ 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;
+ }
- 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);
- }
+ 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;
- 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);
+ 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);
+ }
- case OUT_PID:
+ qtd->status = QTD_PAYLOAD_ALLOC;
+ }
- qtd->urb->actual_length += length;
+ 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;
+ }
- case SETUP_PID:
+ n++;
+ if (n >= ENQUEUE_DEPTH)
break;
- }
}
+ }
+}
- priv->atl_ints[slot].qtd = NULL;
- priv->atl_ints[slot].qh = NULL;
-
- free_mem(hcd, qtd);
+static void schedule_ptds(struct usb_hcd *hcd)
+{
+ struct isp1760_hcd *priv;
+ struct isp1760_qh *qh, *qh_next;
+ struct list_head *ep_queue;
+ LIST_HEAD(urb_list);
+ struct urb_listitem *urb_listitem, *urb_listitem_next;
+ int i;
- reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, skip_map);
+ if (!hcd) {
+ WARN_ON(1);
+ return;
+ }
- if (qtd->urb->status == -EPIPE) {
- /* HALT was received */
+ priv = hcd_to_priv(hcd);
- urb = qtd->urb;
- qtd = clean_up_qtdlist(qtd, qh);
- isp1760_urb_done(hcd, urb);
+ /*
+ * check finished/retired xfers, transfer payloads, call urb_done()
+ */
+ for (i = 0; i < QH_END; i++) {
+ ep_queue = &priv->qh_list[i];
+ list_for_each_entry_safe(qh, qh_next, ep_queue, qh_list) {
+ collect_qtds(hcd, qh, &urb_list);
+ if (list_empty(&qh->qtd_list))
+ list_del(&qh->qh_list);
+ }
+ }
- } else if (usb_pipebulk(qtd->urb->pipe) &&
- (length < qtd->length)) {
- /* short BULK received */
+ 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->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);
- }
+ /*
+ * 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!
+ */
+ for (i = 0; i < QH_END; i++) {
+ ep_queue = &priv->qh_list[i];
+ list_for_each_entry_safe(qh, qh_next, ep_queue, qh_list)
+ enqueue_qtds(hcd, qh);
+ }
+}
- if (qtd->urb->status == -EINPROGRESS)
- qtd->urb->status = 0;
+#define PTD_STATE_QTD_DONE 1
+#define PTD_STATE_QTD_RELOAD 2
+#define PTD_STATE_URB_RETIRE 3
- urb = qtd->urb;
- qtd = clean_up_qtdlist(qtd, qh);
- isp1760_urb_done(hcd, urb);
+static int check_int_transfer(struct usb_hcd *hcd, struct ptd *ptd,
+ struct urb *urb)
+{
+ __dw dw4;
+ int i;
- } else if (last_qtd_of_urb(qtd, qh)) {
- /* that was the last qtd of that URB */
+ 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 {
- /* next QTD of this URB */
+ urb->status = -EPROTO; /* Default unknown error */
- qtd = clean_this_qtd(qtd, qh);
- BUG_ON(!qtd);
+ 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;
}
- if (qtd)
- enqueue_an_ATL_packet(hcd, qh, qtd);
+ return PTD_STATE_URB_RETIRE;
+ }
+
+ return PTD_STATE_QTD_DONE;
+}
- skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG);
+static int check_atl_transfer(struct usb_hcd *hcd, struct ptd *ptd,
+ struct urb *urb)
+{
+ WARN_ON(!ptd);
+ if (ptd->dw3 & DW3_HALT_BIT) {
+ if (ptd->dw3 & DW3_BABBLE_BIT)
+ urb->status = -EOVERFLOW;
+ else if (FROM_DW3_CERR(ptd->dw3))
+ urb->status = -EPIPE; /* Stall */
+ else if (ptd->dw3 & DW3_ERROR_BIT)
+ urb->status = -EPROTO; /* XactErr */
+ else
+ urb->status = -EPROTO; /* Unknown */
+/*
+ dev_dbg(hcd->self.controller, "%s: ptd error:\n"
+ " dw0: %08x dw1: %08x dw2: %08x dw3: %08x\n"
+ " dw4: %08x dw5: %08x dw6: %08x dw7: %08x\n",
+ __func__,
+ ptd->dw0, ptd->dw1, ptd->dw2, ptd->dw3,
+ ptd->dw4, ptd->dw5, ptd->dw6, ptd->dw7);
+*/
+ return PTD_STATE_URB_RETIRE;
+ }
+
+ if ((ptd->dw3 & DW3_ERROR_BIT) && (ptd->dw3 & DW3_ACTIVE_BIT)) {
+ /* Transfer Error, *but* active and no HALT -> reload */
+ dev_dbg(hcd->self.controller, "PID error; reloading ptd\n");
+ return PTD_STATE_QTD_RELOAD;
}
- if (priv->atl_queued <= 1)
- reg_write32(hcd->regs, HC_INTERRUPT_ENABLE,
- INTERRUPT_ENABLE_MASK);
+
+ if (!FROM_DW3_NAKCOUNT(ptd->dw3) && (ptd->dw3 & DW3_ACTIVE_BIT)) {
+ /*
+ * 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.
+ */
+ return PTD_STATE_QTD_RELOAD;
+ }
+
+ return PTD_STATE_QTD_DONE;
}
-static void do_intl_int(struct usb_hcd *hcd)
+static void handle_done_ptds(struct usb_hcd *hcd)
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
- u32 done_map, skip_map;
struct ptd ptd;
- struct urb *urb;
- u32 length;
- u32 or_map;
- int error;
- u32 slot;
- struct isp1760_qtd *qtd;
struct isp1760_qh *qh;
+ int slot;
+ int state;
+ struct slotinfo *slots;
+ u32 ptd_offset;
+ struct isp1760_qtd *qtd;
+ int modified;
+ int skip_map;
- done_map = reg_read32(hcd->regs, HC_INT_PTD_DONEMAP_REG);
skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG);
+ priv->int_done_map &= ~skip_map;
+ skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG);
+ priv->atl_done_map &= ~skip_map;
+
+ modified = priv->int_done_map || priv->atl_done_map;
+
+ while (priv->int_done_map || priv->atl_done_map) {
+ if (priv->int_done_map) {
+ /* INT ptd */
+ slot = __ffs(priv->int_done_map);
+ priv->int_done_map &= ~(1 << slot);
+ slots = priv->int_slots;
+ /* This should not trigger, and could be removed if
+ noone have any problems with it triggering: */
+ if (!slots[slot].qh) {
+ WARN_ON(1);
+ continue;
+ }
+ ptd_offset = INT_PTD_OFFSET;
+ ptd_read(hcd->regs, INT_PTD_OFFSET, slot, &ptd);
+ state = check_int_transfer(hcd, &ptd,
+ slots[slot].qtd->urb);
+ } else {
+ /* ATL ptd */
+ slot = __ffs(priv->atl_done_map);
+ priv->atl_done_map &= ~(1 << slot);
+ slots = priv->atl_slots;
+ /* This should not trigger, and could be removed if
+ noone have any problems with it triggering: */
+ if (!slots[slot].qh) {
+ WARN_ON(1);
+ continue;
+ }
+ ptd_offset = ATL_PTD_OFFSET;
+ ptd_read(hcd->regs, ATL_PTD_OFFSET, slot, &ptd);
+ state = check_atl_transfer(hcd, &ptd,
+ slots[slot].qtd->urb);
+ }
- or_map = reg_read32(hcd->regs, HC_INT_IRQ_MASK_OR_REG);
- or_map &= ~done_map;
- reg_write32(hcd->regs, HC_INT_IRQ_MASK_OR_REG, or_map);
+ qtd = slots[slot].qtd;
+ slots[slot].qtd = NULL;
+ qh = slots[slot].qh;
+ slots[slot].qh = NULL;
+ qh->slot = -1;
+
+ WARN_ON(qtd->status != QTD_XFER_STARTED);
+
+ switch (state) {
+ case PTD_STATE_QTD_DONE:
+ if ((usb_pipeint(qtd->urb->pipe)) &&
+ (qtd->urb->dev->speed != USB_SPEED_HIGH))
+ qtd->actual_length =
+ FROM_DW3_SCS_NRBYTESTRANSFERRED(ptd.dw3);
+ else
+ qtd->actual_length =
+ FROM_DW3_NRBYTESTRANSFERRED(ptd.dw3);
+
+ qtd->status = QTD_XFER_COMPLETE;
+ if (list_is_last(&qtd->qtd_list, &qh->qtd_list) ||
+ is_short_bulk(qtd))
+ qtd = NULL;
+ else
+ qtd = list_entry(qtd->qtd_list.next,
+ typeof(*qtd), qtd_list);
+
+ qh->toggle = FROM_DW3_DATA_TOGGLE(ptd.dw3);
+ qh->ping = FROM_DW3_PING(ptd.dw3);
+ break;
- while (done_map) {
- slot = __ffs(done_map);
- done_map &= ~(1 << slot);
- skip_map |= (1 << slot);
+ case PTD_STATE_QTD_RELOAD: /* QTD_RETRY, for atls only */
+ qtd->status = QTD_PAYLOAD_ALLOC;
+ ptd.dw0 |= DW0_VALID_BIT;
+ /* RL counter = ERR counter */
+ ptd.dw3 &= ~TO_DW3_NAKCOUNT(0xf);
+ ptd.dw3 |= TO_DW3_NAKCOUNT(FROM_DW2_RL(ptd.dw2));
+ ptd.dw3 &= ~TO_DW3_CERR(3);
+ ptd.dw3 |= TO_DW3_CERR(ERR_COUNTER);
+ qh->toggle = FROM_DW3_DATA_TOGGLE(ptd.dw3);
+ qh->ping = FROM_DW3_PING(ptd.dw3);
+ break;
- qtd = priv->int_ints[slot].qtd;
- qh = priv->int_ints[slot].qh;
+ case PTD_STATE_URB_RETIRE:
+ qtd->status = QTD_RETIRE;
+ if ((qtd->urb->dev->speed != USB_SPEED_HIGH) &&
+ (qtd->urb->status != -EPIPE) &&
+ (qtd->urb->status != -EREMOTEIO)) {
+ qh->tt_buffer_dirty = 1;
+ if (usb_hub_clear_tt_buffer(qtd->urb))
+ /* Clear failed; let's hope things work
+ anyway */
+ qh->tt_buffer_dirty = 0;
+ }
+ qtd = NULL;
+ qh->toggle = 0;
+ qh->ping = 0;
+ break;
- if (!qh) {
- dev_err(hcd->self.controller, "(INT) qh is 0\n");
+ default:
+ WARN_ON(1);
continue;
}
- ptd_read(hcd->regs, INT_PTD_OFFSET, slot, &ptd);
- check_int_err_status(hcd, ptd.dw4);
-
- error = check_error(hcd, &ptd);
- if (error) {
-#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
- qtd->urb->status = -EPIPE;
- priv->int_ints[slot].qh->toggle = 0;
- priv->int_ints[slot].qh->ping = 0;
+ if (qtd && (qtd->status == QTD_PAYLOAD_ALLOC)) {
+ if (slots == priv->int_slots) {
+ if (state == PTD_STATE_QTD_RELOAD)
+ dev_err(hcd->self.controller,
+ "%s: PTD_STATE_QTD_RELOAD on "
+ "interrupt packet\n", __func__);
+ if (state != PTD_STATE_QTD_RELOAD)
+ create_ptd_int(qh, qtd, &ptd);
+ } else {
+ if (state != PTD_STATE_QTD_RELOAD)
+ create_ptd_atl(qh, qtd, &ptd);
+ }
- } else {
- priv->int_ints[slot].qh->toggle = ptd.dw3 & (1 << 25);
- priv->int_ints[slot].qh->ping = ptd.dw3 & (1 << 26);
+ start_bus_transfer(hcd, ptd_offset, slot, slots, qtd,
+ qh, &ptd);
}
+ }
- if (qtd->urb->dev->speed != USB_SPEED_HIGH)
- length = PTD_XFERRED_LENGTH_LO(ptd.dw3);
- else
- 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);
- case OUT_PID:
-
- qtd->urb->actual_length += length;
+ if (modified)
+ schedule_ptds(hcd);
+}
- case SETUP_PID:
- break;
- }
- }
+static irqreturn_t isp1760_irq(struct usb_hcd *hcd)
+{
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
+ u32 imask;
+ irqreturn_t irqret = IRQ_NONE;
- priv->int_ints[slot].qtd = NULL;
- priv->int_ints[slot].qh = NULL;
+ spin_lock(&priv->lock);
- reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, skip_map);
- free_mem(hcd, qtd);
+ if (!(hcd->state & HC_STATE_RUNNING))
+ goto leave;
- if (qtd->urb->status == -EPIPE) {
- /* HALT received */
+ imask = reg_read32(hcd->regs, HC_INTERRUPT_REG);
+ if (unlikely(!imask))
+ goto leave;
+ reg_write32(hcd->regs, HC_INTERRUPT_REG, imask); /* Clear */
- urb = qtd->urb;
- qtd = clean_up_qtdlist(qtd, qh);
- isp1760_urb_done(hcd, urb);
+ priv->int_done_map |= reg_read32(hcd->regs, HC_INT_PTD_DONEMAP_REG);
+ priv->atl_done_map |= reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG);
- } else if (last_qtd_of_urb(qtd, qh)) {
+ handle_done_ptds(hcd);
- if (qtd->urb->status == -EINPROGRESS)
- qtd->urb->status = 0;
+ irqret = IRQ_HANDLED;
+leave:
+ spin_unlock(&priv->lock);
- urb = qtd->urb;
- qtd = clean_up_qtdlist(qtd, qh);
- isp1760_urb_done(hcd, urb);
+ return irqret;
+}
- } else {
- /* next QTD of this URB */
+/*
+ * Workaround for problem described in chip errata 2:
+ *
+ * Sometimes interrupts are not generated when ATL (not INT?) completion occurs.
+ * One solution suggested in the errata is to use SOF interrupts _instead_of_
+ * ATL done interrupts (the "instead of" might be important since it seems
+ * enabling ATL interrupts also causes the chip to sometimes - rarely - "forget"
+ * to set the PTD's done bit in addition to not generating an interrupt!).
+ *
+ * So if we use SOF + ATL interrupts, we sometimes get stale PTDs since their
+ * done bit is not being set. This is bad - it blocks the endpoint until reboot.
+ *
+ * If we use SOF interrupts only, we get latency between ptd completion and the
+ * actual handling. This is very noticeable in testusb runs which takes several
+ * minutes longer without ATL interrupts.
+ *
+ * A better solution is to run the code below every SLOT_CHECK_PERIOD ms. If it
+ * finds active ATL slots which are older than SLOT_TIMEOUT ms, it checks the
+ * slot's ACTIVE and VALID bits. If these are not set, the ptd is considered
+ * completed and its done map bit is set.
+ *
+ * The values of SLOT_TIMEOUT and SLOT_CHECK_PERIOD have been arbitrarily chosen
+ * not to cause too much lag when this HW bug occurs, while still hopefully
+ * ensuring that the check does not falsely trigger.
+ */
+#define SLOT_TIMEOUT 300
+#define SLOT_CHECK_PERIOD 200
+static struct timer_list errata2_timer;
- qtd = clean_this_qtd(qtd, qh);
- BUG_ON(!qtd);
+static void errata2_function(unsigned long data)
+{
+ struct usb_hcd *hcd = (struct usb_hcd *) data;
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
+ int slot;
+ struct ptd ptd;
+ unsigned long spinflags;
+
+ spin_lock_irqsave(&priv->lock, spinflags);
+
+ for (slot = 0; slot < 32; slot++)
+ if (priv->atl_slots[slot].qh && time_after(jiffies,
+ priv->atl_slots[slot].timestamp +
+ SLOT_TIMEOUT * HZ / 1000)) {
+ ptd_read(hcd->regs, ATL_PTD_OFFSET, slot, &ptd);
+ if (!FROM_DW0_VALID(ptd.dw0) &&
+ !FROM_DW3_ACTIVE(ptd.dw3))
+ priv->atl_done_map |= 1 << slot;
}
- if (qtd)
- enqueue_an_INT_packet(hcd, qh, qtd);
+ if (priv->atl_done_map)
+ handle_done_ptds(hcd);
- skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG);
- }
+ spin_unlock_irqrestore(&priv->lock, spinflags);
+
+ errata2_timer.expires = jiffies + SLOT_CHECK_PERIOD * HZ / 1000;
+ add_timer(&errata2_timer);
}
-static struct isp1760_qh *qh_make(struct usb_hcd *hcd, struct urb *urb,
- gfp_t flags)
+static int isp1760_run(struct usb_hcd *hcd)
{
- struct isp1760_qh *qh;
- int is_input, type;
-
- qh = isp1760_qh_alloc(flags);
- if (!qh)
- return qh;
+ int retval;
+ u32 temp;
+ u32 command;
+ u32 chipid;
- /*
- * init endpoint/device data for this QH
- */
- is_input = usb_pipein(urb->pipe);
- type = usb_pipetype(urb->pipe);
+ hcd->uses_new_polling = 1;
- if (!usb_pipecontrol(urb->pipe))
- usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), !is_input,
- 1);
- return qh;
-}
+ hcd->state = HC_STATE_RUNNING;
-/*
- * For control/bulk/interrupt, return QH with these TDs appended.
- * Allocates and initializes the QH if necessary.
- * Returns null if it can't allocate a QH it needs to.
- * If the QH has TDs (urbs) already, that's great.
- */
-static struct isp1760_qh *qh_append_tds(struct usb_hcd *hcd,
- struct urb *urb, struct list_head *qtd_list, int epnum,
- void **ptr)
-{
- struct isp1760_qh *qh;
+ /* Set PTD interrupt AND & OR maps */
+ reg_write32(hcd->regs, HC_ATL_IRQ_MASK_AND_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, 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 */
- qh = (struct isp1760_qh *)*ptr;
- if (!qh) {
- /* can't sleep here, we have priv->lock... */
- qh = qh_make(hcd, urb, GFP_ATOMIC);
- if (!qh)
- return qh;
- *ptr = qh;
- }
+ temp = reg_read32(hcd->regs, HC_HW_MODE_CTRL);
+ reg_write32(hcd->regs, HC_HW_MODE_CTRL, temp | HW_GLOBAL_INTR_EN);
- list_splice(qtd_list, qh->qtd_list.prev);
+ command = reg_read32(hcd->regs, HC_USBCMD);
+ command &= ~(CMD_LRESET|CMD_RESET);
+ command |= CMD_RUN;
+ reg_write32(hcd->regs, HC_USBCMD, command);
- return qh;
-}
+ retval = handshake(hcd, HC_USBCMD, CMD_RUN, CMD_RUN, 250 * 1000);
+ if (retval)
+ return retval;
-static void qtd_list_free(struct urb *urb, struct list_head *qtd_list)
-{
- struct list_head *entry, *temp;
+ /*
+ * XXX
+ * Spec says to write FLAG_CF as last config action, priv code grabs
+ * the semaphore while doing so.
+ */
+ down_write(&ehci_cf_port_reset_rwsem);
+ reg_write32(hcd->regs, HC_CONFIGFLAG, FLAG_CF);
- list_for_each_safe(entry, temp, qtd_list) {
- struct isp1760_qtd *qtd;
+ retval = handshake(hcd, HC_CONFIGFLAG, FLAG_CF, FLAG_CF, 250 * 1000);
+ up_write(&ehci_cf_port_reset_rwsem);
+ if (retval)
+ return retval;
- qtd = list_entry(entry, struct isp1760_qtd, qtd_list);
- list_del(&qtd->qtd_list);
- isp1760_qtd_free(qtd);
- }
-}
+ init_timer(&errata2_timer);
+ errata2_timer.function = errata2_function;
+ errata2_timer.data = (unsigned long) hcd;
+ errata2_timer.expires = jiffies + SLOT_CHECK_PERIOD * HZ / 1000;
+ add_timer(&errata2_timer);
-static int isp1760_prepare_enqueue(struct usb_hcd *hcd, struct urb *urb,
- struct list_head *qtd_list, gfp_t mem_flags, packet_enqueue *p)
-{
- struct isp1760_hcd *priv = hcd_to_priv(hcd);
- struct isp1760_qtd *qtd;
- int epnum;
- unsigned long flags;
- struct isp1760_qh *qh = NULL;
- int rc;
- int qh_busy;
+ 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);
- qtd = list_entry(qtd_list->next, struct isp1760_qtd, qtd_list);
- epnum = urb->ep->desc.bEndpointAddress;
+ /* PTD Register Init Part 2, Step 28 */
- spin_lock_irqsave(&priv->lock, flags);
- if (!HCD_HW_ACCESSIBLE(hcd)) {
- rc = -ESHUTDOWN;
- goto done;
- }
- rc = usb_hcd_link_urb_to_ep(hcd, urb);
- if (rc)
- goto done;
+ /* Setup registers controlling PTD checking */
+ 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);
- qh = urb->ep->hcpriv;
- if (qh)
- qh_busy = !list_empty(&qh->qtd_list);
- else
- qh_busy = 0;
+ /* GRR this is run-once init(), being done every time the HC starts.
+ * So long as they're part of class devices, we can't do it init()
+ * since the class device isn't created that early.
+ */
+ return 0;
+}
- qh = qh_append_tds(hcd, urb, qtd_list, epnum, &urb->ep->hcpriv);
- if (!qh) {
- usb_hcd_unlink_urb_from_ep(hcd, urb);
- rc = -ENOMEM;
- goto done;
- }
+static int qtd_fill(struct isp1760_qtd *qtd, void *databuffer, size_t len)
+{
+ qtd->data_buffer = databuffer;
- if (!qh_busy)
- p(hcd, qh, qtd);
+ if (len > MAX_PAYLOAD_SIZE)
+ len = MAX_PAYLOAD_SIZE;
+ qtd->length = len;
-done:
- spin_unlock_irqrestore(&priv->lock, flags);
- if (!qh)
- qtd_list_free(urb, qtd_list);
- return rc;
+ return qtd->length;
}
-static struct isp1760_qtd *isp1760_qtd_alloc(gfp_t flags)
+static void qtd_list_free(struct list_head *qtd_list)
{
- struct isp1760_qtd *qtd;
+ struct isp1760_qtd *qtd, *qtd_next;
- qtd = kmem_cache_zalloc(qtd_cachep, flags);
- if (qtd)
- INIT_LIST_HEAD(&qtd->qtd_list);
-
- return qtd;
+ list_for_each_entry_safe(qtd, qtd_next, qtd_list, qtd_list) {
+ list_del(&qtd->qtd_list);
+ qtd_free(qtd);
+ }
}
/*
- * create a list of filled qtds for this URB; won't link into qh.
+ * Packetize urb->transfer_buffer into list of packets of size wMaxPacketSize.
+ * Also calculate the PID type (SETUP/IN/OUT) for each packet.
*/
#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff)
-static struct list_head *qh_urb_transaction(struct usb_hcd *hcd,
+static void packetize_urb(struct usb_hcd *hcd,
struct urb *urb, struct list_head *head, gfp_t flags)
{
struct isp1760_qtd *qtd;
void *buf;
- int len, maxpacket;
- int is_input;
- u32 token;
+ int len, maxpacketsize;
+ u8 packet_type;
/*
* URBs map to sequences of QTDs: one logical transaction
*/
- qtd = isp1760_qtd_alloc(flags);
- if (!qtd)
- return NULL;
- list_add_tail(&qtd->qtd_list, head);
- qtd->urb = urb;
- urb->status = -EINPROGRESS;
+ if (!urb->transfer_buffer && urb->transfer_buffer_length) {
+ /* XXX This looks like usb storage / SCSI bug */
+ dev_err(hcd->self.controller,
+ "buf is null, dma is %08lx len is %d\n",
+ (long unsigned)urb->transfer_dma,
+ urb->transfer_buffer_length);
+ WARN_ON(1);
+ }
- token = 0;
- /* for split transactions, SplitXState initialized to zero */
+ if (usb_pipein(urb->pipe))
+ packet_type = IN_PID;
+ else
+ packet_type = OUT_PID;
- len = urb->transfer_buffer_length;
- is_input = usb_pipein(urb->pipe);
if (usb_pipecontrol(urb->pipe)) {
- /* SETUP pid */
- qtd_fill(qtd, urb->setup_packet,
- sizeof(struct usb_ctrlrequest),
- token | SETUP_PID);
-
- /* ... and always at least one more pid */
- qtd = isp1760_qtd_alloc(flags);
+ qtd = qtd_alloc(flags, urb, SETUP_PID);
if (!qtd)
goto cleanup;
- qtd->urb = urb;
+ qtd_fill(qtd, urb->setup_packet, sizeof(struct usb_ctrlrequest));
list_add_tail(&qtd->qtd_list, head);
/* for zero length DATA stages, STATUS is always IN */
- if (len == 0)
- token |= IN_PID;
+ if (urb->transfer_buffer_length == 0)
+ packet_type = IN_PID;
}
- /*
- * data transfer stage: buffer setup
- */
- buf = urb->transfer_buffer;
-
- if (is_input)
- token |= IN_PID;
- else
- token |= OUT_PID;
-
- maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe, !is_input));
+ maxpacketsize = max_packet(usb_maxpacket(urb->dev, urb->pipe,
+ usb_pipeout(urb->pipe)));
/*
* buffer gets wrapped in one or more qtds;
* last one may be "short" (including zero len)
* and may serve as a control status ack
*/
+ buf = urb->transfer_buffer;
+ len = urb->transfer_buffer_length;
+
for (;;) {
int this_qtd_len;
- if (!buf && len) {
- /* XXX This looks like usb storage / SCSI bug */
- dev_err(hcd->self.controller, "buf is null, dma is %08lx len is %d\n",
- (long unsigned)urb->transfer_dma, len);
- WARN_ON(1);
- }
+ qtd = qtd_alloc(flags, urb, packet_type);
+ if (!qtd)
+ goto cleanup;
+ this_qtd_len = qtd_fill(qtd, buf, len);
+ list_add_tail(&qtd->qtd_list, head);
- this_qtd_len = qtd_fill(qtd, buf, len, token);
len -= this_qtd_len;
buf += this_qtd_len;
if (len <= 0)
break;
-
- qtd = isp1760_qtd_alloc(flags);
- if (!qtd)
- goto cleanup;
- qtd->urb = urb;
- list_add_tail(&qtd->qtd_list, head);
}
/*
@@ -1510,183 +1488,247 @@ static struct list_head *qh_urb_transaction(struct usb_hcd *hcd,
if (usb_pipecontrol(urb->pipe)) {
one_more = 1;
- /* "in" <--> "out" */
- token ^= IN_PID;
+ if (packet_type == IN_PID)
+ packet_type = OUT_PID;
+ else
+ packet_type = IN_PID;
} else if (usb_pipebulk(urb->pipe)
&& (urb->transfer_flags & URB_ZERO_PACKET)
- && !(urb->transfer_buffer_length % maxpacket)) {
+ && !(urb->transfer_buffer_length %
+ maxpacketsize)) {
one_more = 1;
}
if (one_more) {
- qtd = isp1760_qtd_alloc(flags);
+ qtd = qtd_alloc(flags, urb, packet_type);
if (!qtd)
goto cleanup;
- qtd->urb = urb;
- list_add_tail(&qtd->qtd_list, head);
/* never any data in such packets */
- qtd_fill(qtd, NULL, 0, token);
+ qtd_fill(qtd, NULL, 0);
+ list_add_tail(&qtd->qtd_list, head);
}
}
- qtd->status = 0;
- return head;
+ return;
cleanup:
- qtd_list_free(urb, head);
- return NULL;
+ qtd_list_free(head);
}
static int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
gfp_t mem_flags)
{
- struct list_head qtd_list;
- packet_enqueue *pe;
-
- INIT_LIST_HEAD(&qtd_list);
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
+ struct list_head *ep_queue;
+ struct isp1760_qh *qh, *qhit;
+ unsigned long spinflags;
+ LIST_HEAD(new_qtds);
+ int retval;
+ int qh_in_queue;
switch (usb_pipetype(urb->pipe)) {
case PIPE_CONTROL:
+ ep_queue = &priv->qh_list[QH_CONTROL];
+ break;
case PIPE_BULK:
- if (!qh_urb_transaction(hcd, urb, &qtd_list, mem_flags))
- return -ENOMEM;
- pe = enqueue_an_ATL_packet;
+ ep_queue = &priv->qh_list[QH_BULK];
break;
-
case PIPE_INTERRUPT:
- if (!qh_urb_transaction(hcd, urb, &qtd_list, mem_flags))
- return -ENOMEM;
- pe = enqueue_an_INT_packet;
+ if (urb->interval < 0)
+ return -EINVAL;
+ /* FIXME: Check bandwidth */
+ ep_queue = &priv->qh_list[QH_INTERRUPT];
break;
-
case PIPE_ISOCHRONOUS:
- dev_err(hcd->self.controller, "PIPE_ISOCHRONOUS ain't supported\n");
+ dev_err(hcd->self.controller, "%s: isochronous USB packets "
+ "not yet supported\n",
+ __func__);
+ return -EPIPE;
default:
+ dev_err(hcd->self.controller, "%s: unknown pipe type\n",
+ __func__);
return -EPIPE;
}
- return isp1760_prepare_enqueue(hcd, urb, &qtd_list, mem_flags, pe);
+ if (usb_pipein(urb->pipe))
+ urb->actual_length = 0;
+
+ packetize_urb(hcd, urb, &new_qtds, mem_flags);
+ if (list_empty(&new_qtds))
+ return -ENOMEM;
+
+ retval = 0;
+ spin_lock_irqsave(&priv->lock, spinflags);
+
+ if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ retval = -ESHUTDOWN;
+ qtd_list_free(&new_qtds);
+ goto out;
+ }
+ retval = usb_hcd_link_urb_to_ep(hcd, urb);
+ if (retval) {
+ qtd_list_free(&new_qtds);
+ goto out;
+ }
+
+ qh = urb->ep->hcpriv;
+ if (qh) {
+ qh_in_queue = 0;
+ list_for_each_entry(qhit, ep_queue, qh_list) {
+ if (qhit == qh) {
+ qh_in_queue = 1;
+ break;
+ }
+ }
+ if (!qh_in_queue)
+ list_add_tail(&qh->qh_list, ep_queue);
+ } else {
+ qh = qh_alloc(GFP_ATOMIC);
+ if (!qh) {
+ retval = -ENOMEM;
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+ qtd_list_free(&new_qtds);
+ goto out;
+ }
+ list_add_tail(&qh->qh_list, ep_queue);
+ urb->ep->hcpriv = qh;
+ }
+
+ list_splice_tail(&new_qtds, &qh->qtd_list);
+ schedule_ptds(hcd);
+
+out:
+ spin_unlock_irqrestore(&priv->lock, spinflags);
+ return retval;
}
-static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+static void kill_transfer(struct usb_hcd *hcd, struct urb *urb,
+ struct isp1760_qh *qh)
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
- struct inter_packet_info *ints;
- u32 i;
- u32 reg_base, or_reg, skip_reg;
- unsigned long flags;
- struct ptd ptd;
- packet_enqueue *pe;
-
- switch (usb_pipetype(urb->pipe)) {
- case PIPE_ISOCHRONOUS:
- return -EPIPE;
- break;
+ int skip_map;
- case PIPE_INTERRUPT:
- ints = priv->int_ints;
- reg_base = INT_PTD_OFFSET;
- or_reg = HC_INT_IRQ_MASK_OR_REG;
- skip_reg = HC_INT_PTD_SKIPMAP_REG;
- pe = enqueue_an_INT_packet;
- break;
+ WARN_ON(qh->slot == -1);
- default:
- ints = priv->atl_ints;
- reg_base = ATL_PTD_OFFSET;
- or_reg = HC_ATL_IRQ_MASK_OR_REG;
- skip_reg = HC_ATL_PTD_SKIPMAP_REG;
- pe = enqueue_an_ATL_packet;
- break;
+ /* 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 = 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);
+ priv->atl_slots[qh->slot].qh = NULL;
+ priv->atl_slots[qh->slot].qtd = NULL;
+ } else {
+ 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);
+ priv->int_slots[qh->slot].qh = NULL;
+ priv->int_slots[qh->slot].qtd = NULL;
}
- memset(&ptd, 0, sizeof(ptd));
- spin_lock_irqsave(&priv->lock, flags);
+ qh->slot = -1;
+}
- for (i = 0; i < 32; i++) {
- if (!ints[i].qh)
- continue;
- BUG_ON(!ints[i].qtd);
+/*
+ * Retire the qtds beginning at 'qtd' and belonging all to the same urb, killing
+ * any active transfer belonging to the urb in the process.
+ */
+static void dequeue_urb_from_qtd(struct usb_hcd *hcd, struct isp1760_qh *qh,
+ struct isp1760_qtd *qtd)
+{
+ struct urb *urb;
+ int urb_was_running;
- if (ints[i].qtd->urb == urb) {
- u32 skip_map;
- u32 or_map;
- struct isp1760_qtd *qtd;
- struct isp1760_qh *qh;
+ urb = qtd->urb;
+ urb_was_running = 0;
+ list_for_each_entry_from(qtd, &qh->qtd_list, qtd_list) {
+ if (qtd->urb != urb)
+ break;
- skip_map = reg_read32(hcd->regs, skip_reg);
- skip_map |= 1 << i;
- reg_write32(hcd->regs, skip_reg, skip_map);
+ if (qtd->status >= QTD_XFER_STARTED)
+ urb_was_running = 1;
+ if (last_qtd_of_urb(qtd, qh) &&
+ (qtd->status >= QTD_XFER_COMPLETE))
+ urb_was_running = 0;
- or_map = reg_read32(hcd->regs, or_reg);
- or_map &= ~(1 << i);
- reg_write32(hcd->regs, or_reg, or_map);
+ if (qtd->status == QTD_XFER_STARTED)
+ kill_transfer(hcd, urb, qh);
+ qtd->status = QTD_RETIRE;
+ }
- ptd_write(hcd->regs, reg_base, i, &ptd);
+ if ((urb->dev->speed != USB_SPEED_HIGH) && urb_was_running) {
+ qh->tt_buffer_dirty = 1;
+ if (usb_hub_clear_tt_buffer(urb))
+ /* Clear failed; let's hope things work anyway */
+ qh->tt_buffer_dirty = 0;
+ }
+}
- qtd = ints[i].qtd;
- qh = ints[i].qh;
+static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
+ int status)
+{
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
+ unsigned long spinflags;
+ struct isp1760_qh *qh;
+ struct isp1760_qtd *qtd;
+ int retval = 0;
- free_mem(hcd, qtd);
- qtd = clean_up_qtdlist(qtd, qh);
+ spin_lock_irqsave(&priv->lock, spinflags);
+ retval = usb_hcd_check_unlink_urb(hcd, urb, status);
+ if (retval)
+ goto out;
- ints[i].qh = NULL;
- ints[i].qtd = NULL;
+ qh = urb->ep->hcpriv;
+ if (!qh) {
+ retval = -EINVAL;
+ goto out;
+ }
- isp1760_urb_done(hcd, urb);
- if (qtd)
- pe(hcd, qh, qtd);
+ list_for_each_entry(qtd, &qh->qtd_list, qtd_list)
+ if (qtd->urb == urb) {
+ dequeue_urb_from_qtd(hcd, qh, qtd);
+ list_move(&qtd->qtd_list, &qh->qtd_list);
break;
-
- } else {
- struct isp1760_qtd *qtd;
-
- list_for_each_entry(qtd, &ints[i].qtd->qtd_list,
- qtd_list) {
- if (qtd->urb == urb) {
- clean_up_qtdlist(qtd, ints[i].qh);
- isp1760_urb_done(hcd, urb);
- qtd = NULL;
- break;
- }
- }
-
- /* We found the urb before the last slot */
- if (!qtd)
- break;
}
- }
- spin_unlock_irqrestore(&priv->lock, flags);
- return 0;
+ urb->status = status;
+ schedule_ptds(hcd);
+
+out:
+ spin_unlock_irqrestore(&priv->lock, spinflags);
+ return retval;
}
-static irqreturn_t isp1760_irq(struct usb_hcd *hcd)
+static void isp1760_endpoint_disable(struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep)
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
- u32 imask;
- irqreturn_t irqret = IRQ_NONE;
+ unsigned long spinflags;
+ struct isp1760_qh *qh, *qh_iter;
+ int i;
- spin_lock(&priv->lock);
+ spin_lock_irqsave(&priv->lock, spinflags);
- if (!(hcd->state & HC_STATE_RUNNING))
- goto leave;
+ qh = ep->hcpriv;
+ if (!qh)
+ goto out;
- imask = reg_read32(hcd->regs, HC_INTERRUPT_REG);
- if (unlikely(!imask))
- goto leave;
+ WARN_ON(!list_empty(&qh->qtd_list));
- reg_write32(hcd->regs, HC_INTERRUPT_REG, imask);
- if (imask & (HC_ATL_INT | HC_SOT_INT))
- do_atl_int(hcd);
+ for (i = 0; i < QH_END; i++)
+ list_for_each_entry(qh_iter, &priv->qh_list[i], qh_list)
+ if (qh_iter == qh) {
+ list_del(&qh_iter->qh_list);
+ i = QH_END;
+ break;
+ }
+ qh_free(qh);
+ ep->hcpriv = NULL;
- if (imask & HC_INTL_INT)
- do_intl_int(hcd);
+ schedule_ptds(hcd);
- irqret = IRQ_HANDLED;
-leave:
- spin_unlock(&priv->lock);
- return irqret;
+out:
+ spin_unlock_irqrestore(&priv->lock, spinflags);
}
static int isp1760_hub_status_data(struct usb_hcd *hcd, char *buf)
@@ -1697,7 +1739,7 @@ static int isp1760_hub_status_data(struct usb_hcd *hcd, char *buf)
int retval = 1;
unsigned long flags;
- /* if !USB_SUSPEND, root hub timers won't get shut down ... */
+ /* if !PM_RUNTIME, root hub timers won't get shut down ... */
if (!HC_IS_RUNNING(hcd->state))
return 0;
@@ -1777,7 +1819,7 @@ static int check_reset_complete(struct usb_hcd *hcd, int index,
/* if reset finished and it's still not enabled -- handoff */
if (!(port_status & PORT_PE)) {
- dev_err(hcd->self.controller,
+ dev_info(hcd->self.controller,
"port %d full speed --> companion\n",
index + 1);
@@ -1786,7 +1828,7 @@ static int check_reset_complete(struct usb_hcd *hcd, int index,
reg_write32(hcd->regs, HC_PORTSC1, port_status);
} else
- dev_err(hcd->self.controller, "port %d high speed\n",
+ dev_info(hcd->self.controller, "port %d high speed\n",
index + 1);
return port_status;
@@ -2058,51 +2100,6 @@ error:
return retval;
}
-static void isp1760_endpoint_disable(struct usb_hcd *hcd,
- struct usb_host_endpoint *ep)
-{
- struct isp1760_hcd *priv = hcd_to_priv(hcd);
- struct isp1760_qh *qh;
- struct isp1760_qtd *qtd;
- unsigned long flags;
-
- spin_lock_irqsave(&priv->lock, flags);
- qh = ep->hcpriv;
- if (!qh)
- goto out;
-
- ep->hcpriv = NULL;
- do {
- /* more than entry might get removed */
- if (list_empty(&qh->qtd_list))
- break;
-
- qtd = list_first_entry(&qh->qtd_list, struct isp1760_qtd,
- qtd_list);
-
- if (qtd->status & URB_ENQUEUED) {
- spin_unlock_irqrestore(&priv->lock, flags);
- isp1760_urb_dequeue(hcd, qtd->urb, -ECONNRESET);
- spin_lock_irqsave(&priv->lock, flags);
- } else {
- struct urb *urb;
-
- urb = qtd->urb;
- clean_up_qtdlist(qtd, qh);
- urb->status = -ECONNRESET;
- isp1760_urb_done(hcd, urb);
- }
- } while (1);
-
- qh_destroy(qh);
- /* remove requests and leak them.
- * ATL are pretty fast done, INT could take a while...
- * The latter shoule be removed
- */
-out:
- spin_unlock_irqrestore(&priv->lock, flags);
-}
-
static int isp1760_get_frame(struct usb_hcd *hcd)
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
@@ -2117,6 +2114,8 @@ static void isp1760_stop(struct usb_hcd *hcd)
struct isp1760_hcd *priv = hcd_to_priv(hcd);
u32 temp;
+ del_timer(&errata2_timer);
+
isp1760_hub_control(hcd, ClearPortFeature, USB_PORT_FEAT_POWER, 1,
NULL, 0);
mdelay(20);
@@ -2144,6 +2143,23 @@ static void isp1760_shutdown(struct usb_hcd *hcd)
reg_write32(hcd->regs, HC_USBCMD, command);
}
+static void isp1760_clear_tt_buffer_complete(struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep)
+{
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
+ struct isp1760_qh *qh = ep->hcpriv;
+ unsigned long spinflags;
+
+ if (!qh)
+ return;
+
+ spin_lock_irqsave(&priv->lock, spinflags);
+ qh->tt_buffer_dirty = 0;
+ schedule_ptds(hcd);
+ spin_unlock_irqrestore(&priv->lock, spinflags);
+}
+
+
static const struct hc_driver isp1760_hc_driver = {
.description = "isp1760-hcd",
.product_desc = "NXP ISP1760 USB Host Controller",
@@ -2160,10 +2176,18 @@ static const struct hc_driver isp1760_hc_driver = {
.get_frame_number = isp1760_get_frame,
.hub_status_data = isp1760_hub_status_data,
.hub_control = isp1760_hub_control,
+ .clear_tt_buffer_complete = isp1760_clear_tt_buffer_complete,
};
int __init init_kmem_once(void)
{
+ urb_listitem_cachep = kmem_cache_create("isp1760_urb_listitem",
+ sizeof(struct urb_listitem), 0, SLAB_TEMPORARY |
+ SLAB_MEM_SPREAD, NULL);
+
+ if (!urb_listitem_cachep)
+ return -ENOMEM;
+
qtd_cachep = kmem_cache_create("isp1760_qtd",
sizeof(struct isp1760_qtd), 0, SLAB_TEMPORARY |
SLAB_MEM_SPREAD, NULL);
@@ -2186,10 +2210,12 @@ void deinit_kmem_cache(void)
{
kmem_cache_destroy(qtd_cachep);
kmem_cache_destroy(qh_cachep);
+ kmem_cache_destroy(urb_listitem_cachep);
}
struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len,
int irq, unsigned long irqflags,
+ int rst_gpio,
struct device *dev, const char *busname,
unsigned int devflags)
{
@@ -2209,6 +2235,7 @@ struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len,
priv = hcd_to_priv(hcd);
priv->devflags = devflags;
+ priv->rst_gpio = rst_gpio;
init_memory(priv);
hcd->regs = ioremap(res_start, res_len);
if (!hcd->regs) {
@@ -2223,6 +2250,7 @@ struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len,
ret = usb_add_hcd(hcd, irq, irqflags);
if (ret)
goto err_unmap;
+ device_wakeup_enable(hcd->self.controller);
return hcd;