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