mirror of
https://git.yoctoproject.org/meta-ti
synced 2026-07-17 14:57:47 +00:00
536748db58
The complete patchset will get rebased to the tip of the PSP tree soon, but the conflicts take a lot of time to resolve. Signed-off-by: Koen Kooi <koen@dominion.thruhere.net> Signed-off-by: Denys Dmytriyenko <denys@ti.com>
426 lines
14 KiB
Diff
426 lines
14 KiB
Diff
From a4d99c3c1e01b91b69c6939ba3ee708611869402 Mon Sep 17 00:00:00 2001
|
|
From: Ajay Kumar Gupta <ajay.gupta@ti.com>
|
|
Date: Wed, 23 May 2012 15:25:32 +0530
|
|
Subject: [PATCH 10/18] usb: musb: cppi41: enable txfifo empty interrupt logic
|
|
|
|
Fixes the txdma early completion issue as the TxFIFO empty interrupt
|
|
logic generates an interrupt when last byte from TxFIFO is
|
|
transmitted out.
|
|
|
|
The application issues fixed are -
|
|
1) The isochronous gap issue seen due to txdma early completion and
|
|
thus a delay caused in scheduling of txdma workthread which polls
|
|
for txfifo empty status.
|
|
|
|
2) The workthread is rescheduled once again when txfifo is empty to
|
|
make sure any miss of transfer of the data in internal cppififo.
|
|
|
|
3) For non-isochronous tx request uses workthread to poll for txfifo
|
|
empty status if there is possiblity of data in internal cppififo
|
|
other wise the request is completed directly in the tx-dma isr
|
|
function.
|
|
|
|
Signed-off-by: Ravi B <ravibabu@ti.com>
|
|
---
|
|
drivers/usb/musb/cppi41_dma.c | 101 ++++++++++++++++++++++++++++++++++++++--
|
|
drivers/usb/musb/cppi41_dma.h | 7 +++
|
|
drivers/usb/musb/musb_core.h | 16 ++++++
|
|
drivers/usb/musb/musb_gadget.c | 1 +
|
|
drivers/usb/musb/musb_host.c | 1 +
|
|
drivers/usb/musb/ti81xx.c | 51 ++++++++++++++++++++-
|
|
drivers/usb/musb/ti81xx.h | 3 +
|
|
7 files changed, 174 insertions(+), 6 deletions(-)
|
|
|
|
diff --git a/drivers/usb/musb/cppi41_dma.c b/drivers/usb/musb/cppi41_dma.c
|
|
index 4761acd..3665d0f 100644
|
|
--- a/drivers/usb/musb/cppi41_dma.c
|
|
+++ b/drivers/usb/musb/cppi41_dma.c
|
|
@@ -101,6 +101,9 @@ struct cppi41_channel {
|
|
u8 inf_mode;
|
|
u8 tx_complete;
|
|
u8 hb_mult;
|
|
+ u8 txf_complete;
|
|
+ u8 txfifo_intr_enable;
|
|
+ u8 count;
|
|
};
|
|
|
|
/**
|
|
@@ -132,6 +135,7 @@ struct cppi41 {
|
|
u32 teardown_reg_offs; /* USB_TEARDOWN_REG offset */
|
|
u32 bd_size;
|
|
u8 inf_mode;
|
|
+ u8 txfifo_intr_enable; /* txfifo empty interrupt logic */
|
|
};
|
|
|
|
struct usb_cppi41_info usb_cppi41_info[2];
|
|
@@ -573,6 +577,9 @@ static unsigned cppi41_next_tx_segment(struct cppi41_channel *tx_ch)
|
|
u16 q_mgr = cppi_info->q_mgr;
|
|
u16 tx_comp_q = cppi_info->tx_comp_q[tx_ch->ch_num];
|
|
u8 en_bd_intr = cppi->en_bd_intr;
|
|
+ u8 is_isoc = 0;
|
|
+ struct musb_hw_ep *hw_ep = cppi->musb->endpoints + tx_ch->end_pt->epnum;
|
|
+ int xfer_type = hw_ep->xfer_type;
|
|
|
|
/*
|
|
* Tx can use the generic RNDIS mode where we can probably fit this
|
|
@@ -599,6 +606,12 @@ static unsigned cppi41_next_tx_segment(struct cppi41_channel *tx_ch)
|
|
tx_ch->ch_num, tx_ch->dma_mode ? "accelerated" : "transparent",
|
|
pkt_size, num_pds, tx_ch->start_addr + tx_ch->curr_offset, length);
|
|
|
|
+ if (xfer_type == USB_ENDPOINT_XFER_ISOC)
|
|
+ is_isoc = 1;
|
|
+
|
|
+ if (is_isoc && cppi->txfifo_intr_enable && (length <= tx_ch->pkt_size))
|
|
+ tx_ch->txfifo_intr_enable = 1;
|
|
+
|
|
for (n = 0; n < num_pds; n++) {
|
|
struct cppi41_host_pkt_desc *hw_desc;
|
|
|
|
@@ -640,6 +653,14 @@ static unsigned cppi41_next_tx_segment(struct cppi41_channel *tx_ch)
|
|
dev_dbg(musb->controller, "TX PD %p: buf %08x, len %08x, pkt info %08x\n", curr_pd,
|
|
hw_desc->buf_ptr, hw_desc->buf_len, hw_desc->pkt_info);
|
|
|
|
+ /* enable tx fifo empty interrupt */
|
|
+ if (tx_ch->txfifo_intr_enable) {
|
|
+ dev_dbg(musb->controller, "txs(%p %d) enable txFintr\n",
|
|
+ curr_pd, hw_desc->orig_buf_len &
|
|
+ ~CPPI41_PKT_INTR_FLAG);
|
|
+ txfifoempty_int_enable(cppi->musb, curr_pd->ep_num);
|
|
+ }
|
|
+
|
|
cppi41_queue_push(&tx_ch->queue_obj, curr_pd->dma_addr,
|
|
USB_CPPI41_DESC_ALIGN, pkt_size);
|
|
}
|
|
@@ -1211,6 +1232,8 @@ static int cppi41_channel_abort(struct dma_channel *channel)
|
|
csr &= ~MUSB_TXCSR_DMAENAB;
|
|
musb_writew(epio, MUSB_TXCSR, csr);
|
|
|
|
+ cppi_ch->tx_complete = 0;
|
|
+ cppi_ch->txf_complete = 0;
|
|
/* Tear down Tx DMA channel */
|
|
usb_tx_ch_teardown(cppi_ch);
|
|
|
|
@@ -1224,7 +1247,6 @@ static int cppi41_channel_abort(struct dma_channel *channel)
|
|
csr |= MUSB_TXCSR_FLUSHFIFO | MUSB_TXCSR_H_WZC_BITS;
|
|
musb_writew(epio, MUSB_TXCSR, csr);
|
|
musb_writew(epio, MUSB_TXCSR, csr);
|
|
- cppi_ch->tx_complete = 0;
|
|
} else { /* Rx */
|
|
dprintk("Rx channel teardown, cppi_ch = %p\n", cppi_ch);
|
|
|
|
@@ -1331,16 +1353,23 @@ void txdma_completion_work(struct work_struct *data)
|
|
*/
|
|
if (!tx_ch->end_pt) {
|
|
tx_ch->tx_complete = 0;
|
|
+ tx_ch->count = 0;
|
|
continue;
|
|
}
|
|
|
|
epio = tx_ch->end_pt->regs;
|
|
csr = musb_readw(epio, MUSB_TXCSR);
|
|
|
|
- if (csr & (MUSB_TXCSR_TXPKTRDY |
|
|
- MUSB_TXCSR_FIFONOTEMPTY)) {
|
|
+ if (!tx_ch->txfifo_intr_enable &&
|
|
+ (csr & (MUSB_TXCSR_TXPKTRDY |
|
|
+ MUSB_TXCSR_FIFONOTEMPTY))) {
|
|
resched = 1;
|
|
} else {
|
|
+ if (tx_ch->count > 0) {
|
|
+ tx_ch->count--;
|
|
+ resched = 1;
|
|
+ continue;
|
|
+ }
|
|
tx_ch->channel.status =
|
|
MUSB_DMA_STATUS_FREE;
|
|
tx_ch->tx_complete = 0;
|
|
@@ -1363,6 +1392,38 @@ void txdma_completion_work(struct work_struct *data)
|
|
|
|
}
|
|
|
|
+void cppi41_handle_txfifo_intr(struct musb *musb, u16 usbintr)
|
|
+{
|
|
+ struct cppi41 *cppi;
|
|
+ struct cppi41_channel *tx_ch;
|
|
+ int index;
|
|
+
|
|
+ cppi = container_of(musb->dma_controller, struct cppi41, controller);
|
|
+ for (index = 0; (index < USB_CPPI41_NUM_CH) && usbintr; index++) {
|
|
+ if (usbintr & 1) {
|
|
+ tx_ch = &cppi->tx_cppi_ch[index];
|
|
+ if (tx_ch->txf_complete) {
|
|
+ /* disable txfifo empty interupt */
|
|
+ txfifoempty_int_disable(musb, index+1);
|
|
+ tx_ch->txf_complete = 0;
|
|
+ if (!tx_ch->txfifo_intr_enable)
|
|
+ dev_dbg(musb->controller,
|
|
+ "Bug, wrong TxFintr ep%d\n", index+1);
|
|
+ tx_ch->txfifo_intr_enable = 0;
|
|
+
|
|
+ tx_ch->channel.status =
|
|
+ MUSB_DMA_STATUS_FREE;
|
|
+
|
|
+ dev_dbg(musb->controller,
|
|
+ "txc: givback ep%d\n", index+1);
|
|
+ musb_dma_completion(musb, index+1, 1);
|
|
+ }
|
|
+ }
|
|
+ usbintr = usbintr >> 1;
|
|
+ }
|
|
+}
|
|
+EXPORT_SYMBOL(cppi41_handle_txfifo_intr);
|
|
+
|
|
/**
|
|
* cppi41_dma_controller_create -
|
|
* instantiate an object representing DMA controller.
|
|
@@ -1386,6 +1447,7 @@ cppi41_dma_controller_create(struct musb *musb, void __iomem *mregs)
|
|
cppi->controller.channel_abort = cppi41_channel_abort;
|
|
cppi->cppi_info = (struct usb_cppi41_info *)&usb_cppi41_info[musb->id];;
|
|
cppi->en_bd_intr = cppi->cppi_info->bd_intr_ctrl;
|
|
+ cppi->txfifo_intr_enable = musb->txfifo_intr_enable;
|
|
INIT_WORK(&cppi->txdma_work, txdma_completion_work);
|
|
|
|
/*
|
|
@@ -1472,6 +1534,9 @@ static void usb_process_tx_queue(struct cppi41 *cppi, unsigned index)
|
|
(tx_ch->transfer_mode && !tx_ch->zlp_queued))
|
|
cppi41_next_tx_segment(tx_ch);
|
|
else if (tx_ch->channel.actual_len >= tx_ch->length) {
|
|
+ void __iomem *epio;
|
|
+ u16 csr;
|
|
+
|
|
/*
|
|
* We get Tx DMA completion interrupt even when
|
|
* data is still in FIFO and not moved out to
|
|
@@ -1480,8 +1545,34 @@ static void usb_process_tx_queue(struct cppi41 *cppi, unsigned index)
|
|
* USB functionality. So far, we have obsered
|
|
* failure with iperf.
|
|
*/
|
|
- tx_ch->tx_complete = 1;
|
|
- schedule_work(&cppi->txdma_work);
|
|
+ /* wait for tx fifo empty completion interrupt
|
|
+ * if enabled other wise use the workthread
|
|
+ * to poll fifo empty status
|
|
+ */
|
|
+ epio = tx_ch->end_pt->regs;
|
|
+ csr = musb_readw(epio, MUSB_TXCSR);
|
|
+
|
|
+ if (tx_ch->txfifo_intr_enable) {
|
|
+ tx_ch->txf_complete = 1;
|
|
+ dev_dbg(musb->controller,
|
|
+ "wait for TxF-EmptyIntr ep%d\n", ep_num);
|
|
+ } else {
|
|
+ int residue;
|
|
+
|
|
+ residue = tx_ch->channel.actual_len %
|
|
+ tx_ch->pkt_size;
|
|
+
|
|
+ if (tx_ch->pkt_size > 128 && !residue) {
|
|
+ tx_ch->channel.status =
|
|
+ MUSB_DMA_STATUS_FREE;
|
|
+ musb_dma_completion(cppi->musb,
|
|
+ ep_num, 1);
|
|
+ } else {
|
|
+ tx_ch->tx_complete = 1;
|
|
+ tx_ch->count = 1;
|
|
+ schedule_work(&cppi->txdma_work);
|
|
+ }
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
diff --git a/drivers/usb/musb/cppi41_dma.h b/drivers/usb/musb/cppi41_dma.h
|
|
index bedf3bb..053a135 100644
|
|
--- a/drivers/usb/musb/cppi41_dma.h
|
|
+++ b/drivers/usb/musb/cppi41_dma.h
|
|
@@ -58,4 +58,11 @@ extern struct usb_cppi41_info usb_cppi41_info[];
|
|
*/
|
|
void cppi41_completion(struct musb *musb, u32 rx, u32 tx);
|
|
|
|
+/**
|
|
+ * cppi41_handle_txfifo_intr - Handles tx fifo empty interupts
|
|
+ * @musb: the controller
|
|
+ */
|
|
+void cppi41_handle_txfifo_intr(struct musb *musb, u16 usbintr);
|
|
+void txfifoempty_int_enable(struct musb *musb, u8 ep_num);
|
|
+void txfifoempty_int_disable(struct musb *musb, u8 ep_num);
|
|
#endif /* _CPPI41_DMA_H_ */
|
|
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
|
|
index 184c043..40f02ae 100644
|
|
--- a/drivers/usb/musb/musb_core.h
|
|
+++ b/drivers/usb/musb/musb_core.h
|
|
@@ -228,6 +228,8 @@ struct musb_platform_ops {
|
|
void __iomem *);
|
|
void (*dma_controller_destroy)(struct dma_controller *);
|
|
int (*simulate_babble_intr)(struct musb *musb);
|
|
+ void (*txfifoempty_intr_enable)(struct musb *musb, u8 ep_num);
|
|
+ void (*txfifoempty_intr_disable)(struct musb *musb, u8 ep_num);
|
|
};
|
|
|
|
/*
|
|
@@ -278,6 +280,7 @@ struct musb_hw_ep {
|
|
struct musb_ep ep_out; /* RX */
|
|
|
|
u8 prev_toggle; /* Rx */
|
|
+ u8 xfer_type;
|
|
};
|
|
|
|
static inline struct musb_request *next_in_request(struct musb_hw_ep *hw_ep)
|
|
@@ -472,6 +475,7 @@ struct musb {
|
|
u64 *orig_dma_mask;
|
|
#endif
|
|
short fifo_mode;
|
|
+ u8 txfifo_intr_enable;
|
|
};
|
|
|
|
static inline struct musb *gadget_to_musb(struct usb_gadget *g)
|
|
@@ -562,6 +566,18 @@ extern irqreturn_t musb_interrupt(struct musb *);
|
|
|
|
extern void musb_hnp_stop(struct musb *musb);
|
|
|
|
+static inline void txfifoempty_int_enable(struct musb *musb, u8 ep_num)
|
|
+{
|
|
+ if (musb->ops->txfifoempty_intr_enable)
|
|
+ musb->ops->txfifoempty_intr_enable(musb, ep_num);
|
|
+}
|
|
+
|
|
+static inline void txfifoempty_int_disable(struct musb *musb, u8 ep_num)
|
|
+{
|
|
+ if (musb->ops->txfifoempty_intr_disable)
|
|
+ musb->ops->txfifoempty_intr_disable(musb, ep_num);
|
|
+}
|
|
+
|
|
static inline void musb_platform_set_vbus(struct musb *musb, int is_on)
|
|
{
|
|
if (musb->ops->set_vbus)
|
|
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
|
|
index 305d843..47349ca 100644
|
|
--- a/drivers/usb/musb/musb_gadget.c
|
|
+++ b/drivers/usb/musb/musb_gadget.c
|
|
@@ -1020,6 +1020,7 @@ static int musb_gadget_enable(struct usb_ep *ep,
|
|
goto fail;
|
|
}
|
|
musb_ep->type = usb_endpoint_type(desc);
|
|
+ hw_ep->xfer_type = musb_ep->type;
|
|
|
|
/* check direction and (later) maxpacket size against endpoint */
|
|
if (usb_endpoint_num(desc) != epnum)
|
|
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
|
|
index 80d83bd..c3629bd 100644
|
|
--- a/drivers/usb/musb/musb_host.c
|
|
+++ b/drivers/usb/musb/musb_host.c
|
|
@@ -246,6 +246,7 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh)
|
|
/* initialize software qh state */
|
|
qh->offset = 0;
|
|
qh->segsize = 0;
|
|
+ hw_ep->xfer_type = qh->type;
|
|
|
|
/* gather right source of data */
|
|
switch (qh->type) {
|
|
diff --git a/drivers/usb/musb/ti81xx.c b/drivers/usb/musb/ti81xx.c
|
|
index f7b2f62..7e21c25 100644
|
|
--- a/drivers/usb/musb/ti81xx.c
|
|
+++ b/drivers/usb/musb/ti81xx.c
|
|
@@ -568,7 +568,6 @@ int cppi41_enable_sched_rx(void)
|
|
cppi41_dma_sched_tbl_init(0, 0, dma_sched_table, 30);
|
|
return 0;
|
|
}
|
|
-#endif /* CONFIG_USB_TI_CPPI41_DMA */
|
|
|
|
/*
|
|
* Because we don't set CTRL.UINT, it's "important" to:
|
|
@@ -577,6 +576,33 @@ int cppi41_enable_sched_rx(void)
|
|
* - use INTSET/INTCLR instead.
|
|
*/
|
|
|
|
+void txfifoempty_intr_enable(struct musb *musb, u8 ep_num)
|
|
+{
|
|
+ void __iomem *reg_base = musb->ctrl_base;
|
|
+ u32 coremask;
|
|
+
|
|
+ if (musb->txfifo_intr_enable) {
|
|
+ coremask = musb_readl(reg_base, USB_CORE_INTR_SET_REG);
|
|
+ coremask |= (1 << (ep_num + 16));
|
|
+ musb_writel(reg_base, USB_CORE_INTR_SET_REG, coremask);
|
|
+ dev_dbg(musb->controller, "enable txF intr ep%d coremask %x\n",
|
|
+ ep_num, coremask);
|
|
+ }
|
|
+}
|
|
+
|
|
+void txfifoempty_intr_disable(struct musb *musb, u8 ep_num)
|
|
+{
|
|
+ void __iomem *reg_base = musb->ctrl_base;
|
|
+ u32 coremask;
|
|
+
|
|
+ if (musb->txfifo_intr_enable) {
|
|
+ coremask = (1 << (ep_num + 16));
|
|
+ musb_writel(reg_base, USB_CORE_INTR_CLEAR_REG, coremask);
|
|
+ }
|
|
+}
|
|
+
|
|
+#endif /* CONFIG_USB_TI_CPPI41_DMA */
|
|
+
|
|
/**
|
|
* ti81xx_musb_enable - enable interrupts
|
|
*/
|
|
@@ -889,6 +915,18 @@ static irqreturn_t ti81xx_interrupt(int irq, void *hci)
|
|
musb->int_usb = (usbintr & USB_INTR_USB_MASK) >> USB_INTR_USB_SHIFT;
|
|
|
|
dev_dbg(musb->controller, "usbintr (%x) epintr(%x)\n", usbintr, epintr);
|
|
+
|
|
+ if (musb->txfifo_intr_enable && (usbintr & USB_INTR_TXFIFO_MASK)) {
|
|
+#ifdef CONFIG_USB_TI_CPPI41_DMA
|
|
+ dev_dbg(musb->controller,
|
|
+ "TxFIFOIntr %x\n", usbintr >> USB_INTR_TXFIFO_EMPTY);
|
|
+ cppi41_handle_txfifo_intr(musb,
|
|
+ usbintr >> USB_INTR_TXFIFO_EMPTY);
|
|
+ ret = IRQ_HANDLED;
|
|
+#endif
|
|
+ }
|
|
+ usbintr &= ~USB_INTR_TXFIFO_MASK;
|
|
+
|
|
/*
|
|
* DRVVBUS IRQs are the only proxy we have (a very poor one!) for
|
|
* AM3517's missing ID change IRQ. We need an ID change IRQ to
|
|
@@ -1108,6 +1146,13 @@ int ti81xx_musb_init(struct musb *musb)
|
|
/* set musb controller to host mode */
|
|
musb_platform_set_mode(musb, mode);
|
|
|
|
+#ifdef CONFIG_USB_TI_CPPI41_DMA
|
|
+ musb->txfifo_intr_enable = 1;
|
|
+ if (musb->txfifo_intr_enable)
|
|
+ printk(KERN_DEBUG "TxFifo Empty intr enabled\n");
|
|
+ else
|
|
+ printk(KERN_DEBUG "TxFifo Empty intr disabled\n");
|
|
+#endif
|
|
/* enable babble workaround */
|
|
INIT_WORK(&musb->work, evm_deferred_musb_restart);
|
|
musb->enable_babble_work = 0;
|
|
@@ -1184,6 +1229,10 @@ static struct musb_platform_ops ti81xx_ops = {
|
|
.dma_controller_create = cppi41_dma_controller_create,
|
|
.dma_controller_destroy = cppi41_dma_controller_destroy,
|
|
.simulate_babble_intr = musb_simulate_babble,
|
|
+#ifdef CONFIG_USB_TI_CPPI41_DMA
|
|
+ .txfifoempty_intr_enable = txfifoempty_intr_enable,
|
|
+ .txfifoempty_intr_disable = txfifoempty_intr_disable,
|
|
+#endif
|
|
};
|
|
|
|
static void __devexit ti81xx_delete_musb_pdev(struct ti81xx_glue *glue, u8 id)
|
|
diff --git a/drivers/usb/musb/ti81xx.h b/drivers/usb/musb/ti81xx.h
|
|
index e0dbd3e..d173b55 100644
|
|
--- a/drivers/usb/musb/ti81xx.h
|
|
+++ b/drivers/usb/musb/ti81xx.h
|
|
@@ -125,6 +125,9 @@
|
|
/* USB interrupt register bits */
|
|
#define USB_INTR_USB_SHIFT 0
|
|
#define USB_INTR_USB_MASK (0x1ff << USB_INTR_USB_SHIFT) /* 8 Mentor */
|
|
+#define USB_INTR_TXFIFO_MASK (0xffff << 16)
|
|
+#define USB_INTR_TXFIFO_EMPTY 17
|
|
+
|
|
/* interrupts and DRVVBUS interrupt */
|
|
#define USB_INTR_DRVVBUS 0x100
|
|
#define USB_INTR_RX_SHIFT 16
|
|
--
|
|
1.7.7.6
|
|
|