1
0
mirror of https://git.yoctoproject.org/meta-ti synced 2026-07-17 14:57:47 +00:00
Files
meta-ti/recipes-kernel/linux/linux-ti33x-psp-3.2/psp/0010-usb-musb-cppi41-enable-txfifo-empty-interrupt-logic.patch
Koen Kooi 536748db58 linux-ti33x-psp 3.2: backport PM and USB fixes from PSP
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>
2012-06-11 16:59:07 -04:00

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