usb: renesas_usbhs: use transfer counter if IN direction bulk pipe
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Wed, 7 Nov 2012 00:15:09 +0000 (16:15 -0800)
committerFelipe Balbi <balbi@ti.com>
Thu, 8 Nov 2012 13:46:06 +0000 (15:46 +0200)
received data will break if it was bulk pipe and large data size,
because pipe kept BUF PID even though it doesn't have enough buffer.
To avoid this issue, renesas_usbhs can use transfer counter.
Pipe PID will be NAK if it didn't have enough buffer by this patch.

renesas_usbhs has strange address mapping.
Thus, it is difficult to calculate transfer counter setting address.
This patch use fixed table for it.

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
drivers/usb/renesas_usbhs/fifo.c
drivers/usb/renesas_usbhs/pipe.c
drivers/usb/renesas_usbhs/pipe.h

index 77f1adc..72ad375 100644 (file)
@@ -488,6 +488,8 @@ static int usbhsf_pio_try_push(struct usbhs_pkt *pkt, int *is_done)
        usbhs_pipe_data_sequence(pipe, pkt->sequence);
        pkt->sequence = -1; /* -1 sequence will be ignored */
 
+       usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->length);
+
        ret = usbhsf_fifo_select(pipe, fifo, 1);
        if (ret < 0)
                return 0;
@@ -594,6 +596,7 @@ static int usbhsf_prepare_pop(struct usbhs_pkt *pkt, int *is_done)
        usbhs_pipe_data_sequence(pipe, pkt->sequence);
        pkt->sequence = -1; /* -1 sequence will be ignored */
 
+       usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->length);
        usbhs_pipe_enable(pipe);
        usbhsf_rx_irq_ctrl(pipe, 1);
 
@@ -795,6 +798,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_set_trans_count_if_bulk(pipe, pkt->trans);
        usbhsf_dma_start(pipe, fifo);
        dma_async_issue_pending(chan);
 }
index 122526c..7926e1c 100644 (file)
@@ -92,6 +92,82 @@ static void usbhsp_pipe_cfg_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
        __usbhsp_pipe_xxx_set(pipe, DCPCFG, PIPECFG, mask, val);
 }
 
+/*
+ *             PIPEnTRN/PIPEnTRE functions
+ */
+static void usbhsp_pipe_trn_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
+{
+       struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+       struct device *dev = usbhs_priv_to_dev(priv);
+       int num = usbhs_pipe_number(pipe);
+       u16 reg;
+
+       /*
+        * It is impossible to calculate address,
+        * since PIPEnTRN addresses were mapped randomly.
+        */
+#define CASE_PIPExTRN(a)               \
+       case 0x ## a:                   \
+               reg = PIPE ## a ## TRN; \
+               break;
+
+       switch (num) {
+       CASE_PIPExTRN(1);
+       CASE_PIPExTRN(2);
+       CASE_PIPExTRN(3);
+       CASE_PIPExTRN(4);
+       CASE_PIPExTRN(5);
+       CASE_PIPExTRN(B);
+       CASE_PIPExTRN(C);
+       CASE_PIPExTRN(D);
+       CASE_PIPExTRN(E);
+       CASE_PIPExTRN(F);
+       CASE_PIPExTRN(9);
+       CASE_PIPExTRN(A);
+       default:
+               dev_err(dev, "unknown pipe (%d)\n", num);
+               return;
+       }
+       __usbhsp_pipe_xxx_set(pipe, 0, reg, mask, val);
+}
+
+static void usbhsp_pipe_tre_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
+{
+       struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+       struct device *dev = usbhs_priv_to_dev(priv);
+       int num = usbhs_pipe_number(pipe);
+       u16 reg;
+
+       /*
+        * It is impossible to calculate address,
+        * since PIPEnTRE addresses were mapped randomly.
+        */
+#define CASE_PIPExTRE(a)                       \
+       case 0x ## a:                           \
+               reg = PIPE ## a ## TRE;         \
+               break;
+
+       switch (num) {
+       CASE_PIPExTRE(1);
+       CASE_PIPExTRE(2);
+       CASE_PIPExTRE(3);
+       CASE_PIPExTRE(4);
+       CASE_PIPExTRE(5);
+       CASE_PIPExTRE(B);
+       CASE_PIPExTRE(C);
+       CASE_PIPExTRE(D);
+       CASE_PIPExTRE(E);
+       CASE_PIPExTRE(F);
+       CASE_PIPExTRE(9);
+       CASE_PIPExTRE(A);
+       default:
+               dev_err(dev, "unknown pipe (%d)\n", num);
+               return;
+       }
+
+       __usbhsp_pipe_xxx_set(pipe, 0, reg, mask, val);
+}
+
 /*
  *             PIPEBUF
  */
@@ -264,6 +340,31 @@ int usbhs_pipe_is_stall(struct usbhs_pipe *pipe)
        return (int)(pid == PID_STALL10 || pid == PID_STALL11);
 }
 
+void usbhs_pipe_set_trans_count_if_bulk(struct usbhs_pipe *pipe, int len)
+{
+       if (!usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK))
+               return;
+
+       /*
+        * clear and disable transfer counter for IN/OUT pipe
+        */
+       usbhsp_pipe_tre_set(pipe, TRCLR | TRENB, TRCLR);
+
+       /*
+        * Only IN direction bulk pipe can use transfer count.
+        * Without using this function,
+        * received data will break if it was large data size.
+        * see PIPEnTRN/PIPEnTRE for detail
+        */
+       if (usbhs_pipe_is_dir_in(pipe)) {
+               int maxp = usbhs_pipe_get_maxpacket(pipe);
+
+               usbhsp_pipe_trn_set(pipe, 0xffff, DIV_ROUND_UP(len, maxp));
+               usbhsp_pipe_tre_set(pipe, TRENB, TRENB); /* enable */
+       }
+}
+
+
 /*
  *             pipe setup
  */
index 08786c0..01b1820 100644 (file)
@@ -88,6 +88,7 @@ void usbhs_pipe_enable(struct usbhs_pipe *pipe);
 void usbhs_pipe_disable(struct usbhs_pipe *pipe);
 void usbhs_pipe_stall(struct usbhs_pipe *pipe);
 int usbhs_pipe_is_stall(struct usbhs_pipe *pipe);
+void usbhs_pipe_set_trans_count_if_bulk(struct usbhs_pipe *pipe, int len);
 void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo);
 void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 devsel,
                              u16 epnum, u16 maxp);