|
|
@@ -108,19 +108,45 @@ static struct usbhs_pkt *__usbhsf_pkt_get(struct usbhs_pipe *pipe)
|
|
|
return list_first_entry(&pipe->list, struct usbhs_pkt, node);
|
|
|
}
|
|
|
|
|
|
+static void usbhsf_fifo_clear(struct usbhs_pipe *pipe,
|
|
|
+ struct usbhs_fifo *fifo);
|
|
|
+static void usbhsf_fifo_unselect(struct usbhs_pipe *pipe,
|
|
|
+ struct usbhs_fifo *fifo);
|
|
|
+static struct dma_chan *usbhsf_dma_chan_get(struct usbhs_fifo *fifo,
|
|
|
+ struct usbhs_pkt *pkt);
|
|
|
+#define usbhsf_dma_map(p) __usbhsf_dma_map_ctrl(p, 1)
|
|
|
+#define usbhsf_dma_unmap(p) __usbhsf_dma_map_ctrl(p, 0)
|
|
|
+static int __usbhsf_dma_map_ctrl(struct usbhs_pkt *pkt, int map);
|
|
|
struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt)
|
|
|
{
|
|
|
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
|
|
|
+ struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe);
|
|
|
unsigned long flags;
|
|
|
|
|
|
/******************** spin lock ********************/
|
|
|
usbhs_lock(priv, flags);
|
|
|
|
|
|
+ usbhs_pipe_disable(pipe);
|
|
|
+
|
|
|
if (!pkt)
|
|
|
pkt = __usbhsf_pkt_get(pipe);
|
|
|
|
|
|
- if (pkt)
|
|
|
+ if (pkt) {
|
|
|
+ struct dma_chan *chan = NULL;
|
|
|
+
|
|
|
+ if (fifo)
|
|
|
+ chan = usbhsf_dma_chan_get(fifo, pkt);
|
|
|
+ if (chan) {
|
|
|
+ dmaengine_terminate_all(chan);
|
|
|
+ usbhsf_fifo_clear(pipe, fifo);
|
|
|
+ usbhsf_dma_unmap(pkt);
|
|
|
+ }
|
|
|
+
|
|
|
__usbhsf_pkt_del(pkt);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (fifo)
|
|
|
+ usbhsf_fifo_unselect(pipe, fifo);
|
|
|
|
|
|
usbhs_unlock(priv, flags);
|
|
|
/******************** spin unlock ******************/
|
|
|
@@ -544,6 +570,7 @@ static int usbhsf_pio_try_push(struct usbhs_pkt *pkt, int *is_done)
|
|
|
usbhsf_send_terminator(pipe, fifo);
|
|
|
|
|
|
usbhsf_tx_irq_ctrl(pipe, !*is_done);
|
|
|
+ usbhs_pipe_running(pipe, !*is_done);
|
|
|
usbhs_pipe_enable(pipe);
|
|
|
|
|
|
dev_dbg(dev, " send %d (%d/ %d/ %d/ %d)\n",
|
|
|
@@ -570,12 +597,21 @@ usbhs_fifo_write_busy:
|
|
|
* retry in interrupt
|
|
|
*/
|
|
|
usbhsf_tx_irq_ctrl(pipe, 1);
|
|
|
+ usbhs_pipe_running(pipe, 1);
|
|
|
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+static int usbhsf_pio_prepare_push(struct usbhs_pkt *pkt, int *is_done)
|
|
|
+{
|
|
|
+ if (usbhs_pipe_is_running(pkt->pipe))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ return usbhsf_pio_try_push(pkt, is_done);
|
|
|
+}
|
|
|
+
|
|
|
struct usbhs_pkt_handle usbhs_fifo_pio_push_handler = {
|
|
|
- .prepare = usbhsf_pio_try_push,
|
|
|
+ .prepare = usbhsf_pio_prepare_push,
|
|
|
.try_run = usbhsf_pio_try_push,
|
|
|
};
|
|
|
|
|
|
@@ -589,6 +625,9 @@ static int usbhsf_prepare_pop(struct usbhs_pkt *pkt, int *is_done)
|
|
|
if (usbhs_pipe_is_busy(pipe))
|
|
|
return 0;
|
|
|
|
|
|
+ if (usbhs_pipe_is_running(pipe))
|
|
|
+ return 0;
|
|
|
+
|
|
|
/*
|
|
|
* pipe enable to prepare packet receive
|
|
|
*/
|
|
|
@@ -597,6 +636,7 @@ static int usbhsf_prepare_pop(struct usbhs_pkt *pkt, int *is_done)
|
|
|
|
|
|
usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->length);
|
|
|
usbhs_pipe_enable(pipe);
|
|
|
+ usbhs_pipe_running(pipe, 1);
|
|
|
usbhsf_rx_irq_ctrl(pipe, 1);
|
|
|
|
|
|
return 0;
|
|
|
@@ -642,6 +682,7 @@ static int usbhsf_pio_try_pop(struct usbhs_pkt *pkt, int *is_done)
|
|
|
(total_len < maxp)) { /* short packet */
|
|
|
*is_done = 1;
|
|
|
usbhsf_rx_irq_ctrl(pipe, 0);
|
|
|
+ usbhs_pipe_running(pipe, 0);
|
|
|
usbhs_pipe_disable(pipe); /* disable pipe first */
|
|
|
}
|
|
|
|
|
|
@@ -763,8 +804,6 @@ static void __usbhsf_dma_ctrl(struct usbhs_pipe *pipe,
|
|
|
usbhs_bset(priv, fifo->sel, DREQE, dreqe);
|
|
|
}
|
|
|
|
|
|
-#define usbhsf_dma_map(p) __usbhsf_dma_map_ctrl(p, 1)
|
|
|
-#define usbhsf_dma_unmap(p) __usbhsf_dma_map_ctrl(p, 0)
|
|
|
static int __usbhsf_dma_map_ctrl(struct usbhs_pkt *pkt, int map)
|
|
|
{
|
|
|
struct usbhs_pipe *pipe = pkt->pipe;
|
|
|
@@ -805,6 +844,7 @@ static void xfer_work(struct work_struct *work)
|
|
|
dev_dbg(dev, " %s %d (%d/ %d)\n",
|
|
|
fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero);
|
|
|
|
|
|
+ usbhs_pipe_running(pipe, 1);
|
|
|
usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans);
|
|
|
usbhs_pipe_enable(pipe);
|
|
|
usbhsf_dma_start(pipe, fifo);
|
|
|
@@ -836,6 +876,10 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done)
|
|
|
if ((uintptr_t)(pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */
|
|
|
goto usbhsf_pio_prepare_push;
|
|
|
|
|
|
+ /* return at this time if the pipe is running */
|
|
|
+ if (usbhs_pipe_is_running(pipe))
|
|
|
+ return 0;
|
|
|
+
|
|
|
/* get enable DMA fifo */
|
|
|
fifo = usbhsf_get_dma_fifo(priv, pkt);
|
|
|
if (!fifo)
|
|
|
@@ -869,15 +913,29 @@ usbhsf_pio_prepare_push:
|
|
|
static int usbhsf_dma_push_done(struct usbhs_pkt *pkt, int *is_done)
|
|
|
{
|
|
|
struct usbhs_pipe *pipe = pkt->pipe;
|
|
|
+ int is_short = pkt->trans % usbhs_pipe_get_maxpacket(pipe);
|
|
|
+
|
|
|
+ pkt->actual += pkt->trans;
|
|
|
|
|
|
- pkt->actual = pkt->trans;
|
|
|
+ if (pkt->actual < pkt->length)
|
|
|
+ *is_done = 0; /* there are remainder data */
|
|
|
+ else if (is_short)
|
|
|
+ *is_done = 1; /* short packet */
|
|
|
+ else
|
|
|
+ *is_done = !pkt->zero; /* send zero packet? */
|
|
|
|
|
|
- *is_done = !pkt->zero; /* send zero packet ? */
|
|
|
+ usbhs_pipe_running(pipe, !*is_done);
|
|
|
|
|
|
usbhsf_dma_stop(pipe, pipe->fifo);
|
|
|
usbhsf_dma_unmap(pkt);
|
|
|
usbhsf_fifo_unselect(pipe, pipe->fifo);
|
|
|
|
|
|
+ if (!*is_done) {
|
|
|
+ /* change handler to PIO */
|
|
|
+ pkt->handler = &usbhs_fifo_pio_push_handler;
|
|
|
+ return pkt->handler->try_run(pkt, is_done);
|
|
|
+ }
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
@@ -972,8 +1030,10 @@ static int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done)
|
|
|
if ((pkt->actual == pkt->length) || /* receive all data */
|
|
|
(pkt->trans < maxp)) { /* short packet */
|
|
|
*is_done = 1;
|
|
|
+ usbhs_pipe_running(pipe, 0);
|
|
|
} else {
|
|
|
/* re-enable */
|
|
|
+ usbhs_pipe_running(pipe, 0);
|
|
|
usbhsf_prepare_pop(pkt, is_done);
|
|
|
}
|
|
|
|