usb: renesas_usbhs: add DMAEngine support
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Mon, 6 Jun 2011 05:19:03 +0000 (14:19 +0900)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 7 Jun 2011 16:10:10 +0000 (09:10 -0700)
USB DMA was installed on "normal DMAC" when SH7724 or older SuperH,
but the "USB-DMAC" was prepared on recent SuperH.
These 2 DMAC have a little bit different behavior.

This patch add DMAEngine code for "normal DMAC",
but it is still using PIO fifo.
The DMA fifo will be formally supported in the future.

You can enable DMA fifo by local fixup
usbhs_fifo_pio_push_handler -> usbhs_fifo_dma_push_handler
usbhs_fifo_pio_pop_handler  -> usbhs_fifo_dma_pop_handler
on usbhsg_ep_enable.

This DMAEngine was tested by g_file_storage on SH7724 Ecovec board

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/renesas_usbhs/common.c
drivers/usb/renesas_usbhs/common.h
drivers/usb/renesas_usbhs/fifo.c
drivers/usb/renesas_usbhs/fifo.h
drivers/usb/renesas_usbhs/mod_gadget.c
drivers/usb/renesas_usbhs/pipe.c
drivers/usb/renesas_usbhs/pipe.h
include/linux/usb/renesas_usbhs.h

index e510b29216b3ebd40ee6f444c3aefbe57f05d317..665259aec87193568c5ddf5ff6f004f45e12a671 100644 (file)
@@ -304,6 +304,8 @@ static int __devinit usbhs_probe(struct platform_device *pdev)
                priv->dparam->pipe_type = usbhsc_default_pipe_type;
                priv->dparam->pipe_size = ARRAY_SIZE(usbhsc_default_pipe_type);
        }
+       if (!priv->dparam->pio_dma_border)
+               priv->dparam->pio_dma_border = 64; /* 64byte */
 
        /* FIXME */
        /* runtime power control ? */
index 06d7239a044d7b0f2dd8ebfa78f8b08d976e374a..b410463a1212ce859bae0a930105dabbf8647e26 100644 (file)
@@ -36,6 +36,12 @@ struct usbhs_priv;
 #define CFIFO          0x0014
 #define CFIFOSEL       0x0020
 #define CFIFOCTR       0x0022
+#define D0FIFO         0x0100
+#define D0FIFOSEL      0x0028
+#define D0FIFOCTR      0x002A
+#define D1FIFO         0x0120
+#define D1FIFOSEL      0x002C
+#define D1FIFOCTR      0x002E
 #define INTENB0                0x0030
 #define INTENB1                0x0032
 #define BRDYENB                0x0036
@@ -60,6 +66,30 @@ struct usbhs_priv;
 #define PIPEMAXP       0x006C
 #define PIPEPERI       0x006E
 #define PIPEnCTR       0x0070
+#define PIPE1TRE       0x0090
+#define PIPE1TRN       0x0092
+#define PIPE2TRE       0x0094
+#define PIPE2TRN       0x0096
+#define PIPE3TRE       0x0098
+#define PIPE3TRN       0x009A
+#define PIPE4TRE       0x009C
+#define PIPE4TRN       0x009E
+#define PIPE5TRE       0x00A0
+#define PIPE5TRN       0x00A2
+#define PIPEBTRE       0x00A4
+#define PIPEBTRN       0x00A6
+#define PIPECTRE       0x00A8
+#define PIPECTRN       0x00AA
+#define PIPEDTRE       0x00AC
+#define PIPEDTRN       0x00AE
+#define PIPEETRE       0x00B0
+#define PIPEETRN       0x00B2
+#define PIPEFTRE       0x00B4
+#define PIPEFTRN       0x00B6
+#define PIPE9TRE       0x00B8
+#define PIPE9TRN       0x00BA
+#define PIPEATRE       0x00BC
+#define PIPEATRN       0x00BE
 
 /* SYSCFG */
 #define SCKE   (1 << 10)       /* USB Module Clock Enable */
@@ -78,6 +108,7 @@ struct usbhs_priv;
 #define  RHST_HIGH_SPEED 3     /* High-speed connection */
 
 /* CFIFOSEL */
+#define DREQE  (1 << 12)       /* DMA Transfer Request Enable */
 #define MBW_32 (0x2 << 10)     /* CFIFO Port Access Bit Width */
 
 /* CFIFOCTR */
@@ -164,6 +195,10 @@ struct usbhs_priv;
 
 #define CCPL           (1 << 2)        /* Control Transfer End Enable */
 
+/* PIPEnTRE */
+#define TRENB          (1 << 9)        /* Transaction Counter Enable */
+#define TRCLR          (1 << 8)        /* Transaction Counter Clear */
+
 /* FRMNUM */
 #define FRNM_MASK      (0x7FF)
 
index 14baaad20b79b2ae7cc7b0d62720c7e69d5d39bc..2016a2448ccba9d90e49eb0bf4d929a73c087c1b 100644 (file)
@@ -20,6 +20,8 @@
 #include "./pipe.h"
 
 #define usbhsf_get_cfifo(p)    (&((p)->fifo_info.cfifo))
+#define usbhsf_get_d0fifo(p)   (&((p)->fifo_info.d0fifo))
+#define usbhsf_get_d1fifo(p)   (&((p)->fifo_info.d1fifo))
 
 #define usbhsf_fifo_is_busy(f) ((f)->pipe) /* see usbhs_pipe_select_fifo */
 
@@ -43,6 +45,7 @@ static struct usbhs_pkt_handle usbhsf_null_handler = {
 
 void usbhs_pkt_init(struct usbhs_pkt *pkt)
 {
+       pkt->dma = DMA_ADDR_INVALID;
        INIT_LIST_HEAD(&pkt->node);
 }
 
@@ -136,6 +139,9 @@ int __usbhs_pkt_handler(struct usbhs_pipe *pipe, int type)
        case USBHSF_PKT_TRY_RUN:
                func = pkt->handler->try_run;
                break;
+       case USBHSF_PKT_DMA_DONE:
+               func = pkt->handler->dma_done;
+               break;
        default:
                dev_err(dev, "unknown pkt hander\n");
                goto __usbhs_pkt_handler_end;
@@ -507,6 +513,330 @@ struct usbhs_pkt_handle usbhs_ctrl_stage_end_handler = {
        .try_run = usbhsf_ctrl_stage_end,
 };
 
+/*
+ *             DMA fifo functions
+ */
+static struct dma_chan *usbhsf_dma_chan_get(struct usbhs_fifo *fifo,
+                                           struct usbhs_pkt *pkt)
+{
+       if (&usbhs_fifo_dma_push_handler == pkt->handler)
+               return fifo->tx_chan;
+
+       if (&usbhs_fifo_dma_pop_handler == pkt->handler)
+               return fifo->rx_chan;
+
+       return NULL;
+}
+
+static struct usbhs_fifo *usbhsf_get_dma_fifo(struct usbhs_priv *priv,
+                                             struct usbhs_pkt *pkt)
+{
+       struct usbhs_fifo *fifo;
+
+       /* DMA :: D0FIFO */
+       fifo = usbhsf_get_d0fifo(priv);
+       if (usbhsf_dma_chan_get(fifo, pkt) &&
+           !usbhsf_fifo_is_busy(fifo))
+               return fifo;
+
+       /* DMA :: D1FIFO */
+       fifo = usbhsf_get_d1fifo(priv);
+       if (usbhsf_dma_chan_get(fifo, pkt) &&
+           !usbhsf_fifo_is_busy(fifo))
+               return fifo;
+
+       return NULL;
+}
+
+#define usbhsf_dma_start(p, f) __usbhsf_dma_ctrl(p, f, DREQE)
+#define usbhsf_dma_stop(p, f)  __usbhsf_dma_ctrl(p, f, 0)
+static void __usbhsf_dma_ctrl(struct usbhs_pipe *pipe,
+                             struct usbhs_fifo *fifo,
+                             u16 dreqe)
+{
+       struct usbhs_priv *priv = usbhs_pipe_to_priv(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;
+       struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+       struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv);
+
+       return info->dma_map_ctrl(pkt, map);
+}
+
+static void usbhsf_dma_complete(void *arg);
+static void usbhsf_dma_prepare_tasklet(unsigned long data)
+{
+       struct usbhs_pkt *pkt = (struct usbhs_pkt *)data;
+       struct usbhs_pipe *pipe = pkt->pipe;
+       struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe);
+       struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+       struct scatterlist sg;
+       struct dma_async_tx_descriptor *desc;
+       struct dma_chan *chan = usbhsf_dma_chan_get(fifo, pkt);
+       struct device *dev = usbhs_priv_to_dev(priv);
+       enum dma_data_direction dir;
+       dma_cookie_t cookie;
+
+       dir = usbhs_pipe_is_dir_in(pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+       sg_init_table(&sg, 1);
+       sg_set_page(&sg, virt_to_page(pkt->dma),
+                   pkt->length, offset_in_page(pkt->dma));
+       sg_dma_address(&sg) = pkt->dma + pkt->actual;
+       sg_dma_len(&sg) = pkt->trans;
+
+       desc = chan->device->device_prep_slave_sg(chan, &sg, 1, dir,
+                                                 DMA_PREP_INTERRUPT |
+                                                 DMA_CTRL_ACK);
+       if (!desc)
+               return;
+
+       desc->callback          = usbhsf_dma_complete;
+       desc->callback_param    = pipe;
+
+       cookie = desc->tx_submit(desc);
+       if (cookie < 0) {
+               dev_err(dev, "Failed to submit dma descriptor\n");
+               return;
+       }
+
+       dev_dbg(dev, "  %s %d (%d/ %d)\n",
+               fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero);
+
+       usbhsf_dma_start(pipe, fifo);
+       dma_async_issue_pending(chan);
+}
+
+static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done)
+{
+       struct usbhs_pipe *pipe = pkt->pipe;
+       struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+       struct usbhs_fifo *fifo;
+       int len = pkt->length - pkt->actual;
+       int ret;
+
+       if (usbhs_pipe_is_busy(pipe))
+               return 0;
+
+       /* use PIO if packet is less than pio_dma_border or pipe is DCP */
+       if ((len < usbhs_get_dparam(priv, pio_dma_border)) ||
+           usbhs_pipe_is_dcp(pipe))
+               goto usbhsf_pio_prepare_push;
+
+       if (len % 4) /* 32bit alignment */
+               goto usbhsf_pio_prepare_push;
+
+       /* get enable DMA fifo */
+       fifo = usbhsf_get_dma_fifo(priv, pkt);
+       if (!fifo)
+               goto usbhsf_pio_prepare_push;
+
+       if (usbhsf_dma_map(pkt) < 0)
+               goto usbhsf_pio_prepare_push;
+
+       ret = usbhsf_fifo_select(pipe, fifo, 0);
+       if (ret < 0)
+               goto usbhsf_pio_prepare_push_unmap;
+
+       pkt->trans = len;
+
+       tasklet_init(&fifo->tasklet,
+                    usbhsf_dma_prepare_tasklet,
+                    (unsigned long)pkt);
+
+       tasklet_schedule(&fifo->tasklet);
+
+       return 0;
+
+usbhsf_pio_prepare_push_unmap:
+       usbhsf_dma_unmap(pkt);
+usbhsf_pio_prepare_push:
+       /*
+        * change handler to PIO
+        */
+       pkt->handler = &usbhs_fifo_pio_push_handler;
+
+       return pkt->handler->prepare(pkt, is_done);
+}
+
+static int usbhsf_dma_push_done(struct usbhs_pkt *pkt, int *is_done)
+{
+       struct usbhs_pipe *pipe = pkt->pipe;
+
+       pkt->actual = pkt->trans;
+
+       *is_done = !pkt->zero;  /* send zero packet ? */
+
+       usbhsf_dma_stop(pipe, pipe->fifo);
+       usbhsf_dma_unmap(pkt);
+       usbhsf_fifo_unselect(pipe, pipe->fifo);
+
+       return 0;
+}
+
+struct usbhs_pkt_handle usbhs_fifo_dma_push_handler = {
+       .prepare        = usbhsf_dma_prepare_push,
+       .dma_done       = usbhsf_dma_push_done,
+};
+
+static int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done)
+{
+       struct usbhs_pipe *pipe = pkt->pipe;
+       struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+       struct usbhs_fifo *fifo;
+       int len, ret;
+
+       if (usbhs_pipe_is_busy(pipe))
+               return 0;
+
+       if (usbhs_pipe_is_dcp(pipe))
+               goto usbhsf_pio_prepare_pop;
+
+       /* get enable DMA fifo */
+       fifo = usbhsf_get_dma_fifo(priv, pkt);
+       if (!fifo)
+               goto usbhsf_pio_prepare_pop;
+
+       ret = usbhsf_fifo_select(pipe, fifo, 0);
+       if (ret < 0)
+               goto usbhsf_pio_prepare_pop;
+
+       /* use PIO if packet is less than pio_dma_border */
+       len = usbhsf_fifo_rcv_len(priv, fifo);
+       len = min(pkt->length - pkt->actual, len);
+       if (len % 4) /* 32bit alignment */
+               goto usbhsf_pio_prepare_pop_unselect;
+
+       if (len < usbhs_get_dparam(priv, pio_dma_border))
+               goto usbhsf_pio_prepare_pop_unselect;
+
+       ret = usbhsf_fifo_barrier(priv, fifo);
+       if (ret < 0)
+               goto usbhsf_pio_prepare_pop_unselect;
+
+       if (usbhsf_dma_map(pkt) < 0)
+               goto usbhsf_pio_prepare_pop_unselect;
+
+       /* DMA */
+
+       /*
+        * usbhs_fifo_dma_pop_handler :: prepare
+        * enabled irq to come here.
+        * but it is no longer needed for DMA. disable it.
+        */
+       usbhsf_rx_irq_ctrl(pipe, 0);
+
+       pkt->trans = len;
+
+       tasklet_init(&fifo->tasklet,
+                    usbhsf_dma_prepare_tasklet,
+                    (unsigned long)pkt);
+
+       tasklet_schedule(&fifo->tasklet);
+
+       return 0;
+
+usbhsf_pio_prepare_pop_unselect:
+       usbhsf_fifo_unselect(pipe, fifo);
+usbhsf_pio_prepare_pop:
+
+       /*
+        * change handler to PIO
+        */
+       pkt->handler = &usbhs_fifo_pio_pop_handler;
+
+       return pkt->handler->try_run(pkt, is_done);
+}
+
+static int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done)
+{
+       struct usbhs_pipe *pipe = pkt->pipe;
+       int maxp = usbhs_pipe_get_maxpacket(pipe);
+
+       usbhsf_dma_stop(pipe, pipe->fifo);
+       usbhsf_dma_unmap(pkt);
+       usbhsf_fifo_unselect(pipe, pipe->fifo);
+
+       pkt->actual += pkt->trans;
+
+       if ((pkt->actual == pkt->length) ||     /* receive all data */
+           (pkt->trans < maxp)) {              /* short packet */
+               *is_done = 1;
+       } else {
+               /* re-enable */
+               usbhsf_prepare_pop(pkt, is_done);
+       }
+
+       return 0;
+}
+
+struct usbhs_pkt_handle usbhs_fifo_dma_pop_handler = {
+       .prepare        = usbhsf_prepare_pop,
+       .try_run        = usbhsf_dma_try_pop,
+       .dma_done       = usbhsf_dma_pop_done
+};
+
+/*
+ *             DMA setting
+ */
+static bool usbhsf_dma_filter(struct dma_chan *chan, void *param)
+{
+       struct sh_dmae_slave *slave = param;
+
+       /*
+        * FIXME
+        *
+        * usbhs doesn't recognize id = 0 as valid DMA
+        */
+       if (0 == slave->slave_id)
+               return false;
+
+       chan->private = slave;
+
+       return true;
+}
+
+static void usbhsf_dma_quit(struct usbhs_priv *priv, struct usbhs_fifo *fifo)
+{
+       if (fifo->tx_chan)
+               dma_release_channel(fifo->tx_chan);
+       if (fifo->rx_chan)
+               dma_release_channel(fifo->rx_chan);
+
+       fifo->tx_chan = NULL;
+       fifo->rx_chan = NULL;
+}
+
+static void usbhsf_dma_init(struct usbhs_priv *priv,
+                           struct usbhs_fifo *fifo)
+{
+       struct device *dev = usbhs_priv_to_dev(priv);
+       dma_cap_mask_t mask;
+
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_SLAVE, mask);
+       fifo->tx_chan = dma_request_channel(mask, usbhsf_dma_filter,
+                                           &fifo->tx_slave);
+
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_SLAVE, mask);
+       fifo->rx_chan = dma_request_channel(mask, usbhsf_dma_filter,
+                                           &fifo->rx_slave);
+
+       if (fifo->tx_chan || fifo->rx_chan)
+               dev_info(dev, "enable DMAEngine (%s%s%s)\n",
+                        fifo->name,
+                        fifo->tx_chan ? "[TX]" : "    ",
+                        fifo->rx_chan ? "[RX]" : "    ");
+}
+
 /*
  *             irq functions
  */
@@ -570,6 +900,19 @@ static int usbhsf_irq_ready(struct usbhs_priv *priv,
        return 0;
 }
 
+static void usbhsf_dma_complete(void *arg)
+{
+       struct usbhs_pipe *pipe = arg;
+       struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+       struct device *dev = usbhs_priv_to_dev(priv);
+       int ret;
+
+       ret = usbhs_pkt_dmadone(pipe);
+       if (ret < 0)
+               dev_err(dev, "dma_complete run_error %d : %d\n",
+                       usbhs_pipe_number(pipe), ret);
+}
+
 /*
  *             fifo init
  */
@@ -577,6 +920,8 @@ void usbhs_fifo_init(struct usbhs_priv *priv)
 {
        struct usbhs_mod *mod = usbhs_mod_get_current(priv);
        struct usbhs_fifo *cfifo = usbhsf_get_cfifo(priv);
+       struct usbhs_fifo *d0fifo = usbhsf_get_d0fifo(priv);
+       struct usbhs_fifo *d1fifo = usbhsf_get_d1fifo(priv);
 
        mod->irq_empty          = usbhsf_irq_empty;
        mod->irq_ready          = usbhsf_irq_ready;
@@ -584,6 +929,19 @@ void usbhs_fifo_init(struct usbhs_priv *priv)
        mod->irq_brdysts        = 0;
 
        cfifo->pipe     = NULL;
+       cfifo->tx_chan  = NULL;
+       cfifo->rx_chan  = NULL;
+
+       d0fifo->pipe    = NULL;
+       d0fifo->tx_chan = NULL;
+       d0fifo->rx_chan = NULL;
+
+       d1fifo->pipe    = NULL;
+       d1fifo->tx_chan = NULL;
+       d1fifo->rx_chan = NULL;
+
+       usbhsf_dma_init(priv, usbhsf_get_d0fifo(priv));
+       usbhsf_dma_init(priv, usbhsf_get_d1fifo(priv));
 }
 
 void usbhs_fifo_quit(struct usbhs_priv *priv)
@@ -594,6 +952,9 @@ void usbhs_fifo_quit(struct usbhs_priv *priv)
        mod->irq_ready          = NULL;
        mod->irq_bempsts        = 0;
        mod->irq_brdysts        = 0;
+
+       usbhsf_dma_quit(priv, usbhsf_get_d0fifo(priv));
+       usbhsf_dma_quit(priv, usbhsf_get_d1fifo(priv));
 }
 
 int usbhs_fifo_probe(struct usbhs_priv *priv)
@@ -602,10 +963,29 @@ int usbhs_fifo_probe(struct usbhs_priv *priv)
 
        /* CFIFO */
        fifo = usbhsf_get_cfifo(priv);
+       fifo->name      = "CFIFO";
        fifo->port      = CFIFO;
        fifo->sel       = CFIFOSEL;
        fifo->ctr       = CFIFOCTR;
 
+       /* D0FIFO */
+       fifo = usbhsf_get_d0fifo(priv);
+       fifo->name      = "D0FIFO";
+       fifo->port      = D0FIFO;
+       fifo->sel       = D0FIFOSEL;
+       fifo->ctr       = D0FIFOCTR;
+       fifo->tx_slave.slave_id = usbhs_get_dparam(priv, d0_tx_id);
+       fifo->rx_slave.slave_id = usbhs_get_dparam(priv, d0_rx_id);
+
+       /* D1FIFO */
+       fifo = usbhsf_get_d1fifo(priv);
+       fifo->name      = "D1FIFO";
+       fifo->port      = D1FIFO;
+       fifo->sel       = D1FIFOSEL;
+       fifo->ctr       = D1FIFOCTR;
+       fifo->tx_slave.slave_id = usbhs_get_dparam(priv, d1_tx_id);
+       fifo->rx_slave.slave_id = usbhs_get_dparam(priv, d1_rx_id);
+
        return 0;
 }
 
index 94db269f84c2a3295f4d10f8328e59ead27ea7a4..ed6d8e56c13c0f241f0381945c3420d316f063dc 100644 (file)
 #ifndef RENESAS_USB_FIFO_H
 #define RENESAS_USB_FIFO_H
 
+#include <linux/interrupt.h>
+#include <linux/sh_dma.h>
+#include <asm/dma.h>
 #include "pipe.h"
 
+#define        DMA_ADDR_INVALID        (~(dma_addr_t)0)
+
 struct usbhs_fifo {
+       char *name;
        u32 port;       /* xFIFO */
        u32 sel;        /* xFIFOSEL */
        u32 ctr;        /* xFIFOCTR */
 
        struct usbhs_pipe       *pipe;
+       struct tasklet_struct   tasklet;
+
+       struct dma_chan         *tx_chan;
+       struct dma_chan         *rx_chan;
+
+       struct sh_dmae_slave    tx_slave;
+       struct sh_dmae_slave    rx_slave;
 };
 
 struct usbhs_fifo_info {
        struct usbhs_fifo cfifo;
+       struct usbhs_fifo d0fifo;
+       struct usbhs_fifo d1fifo;
 };
 
 struct usbhs_pkt_handle;
@@ -36,8 +51,10 @@ struct usbhs_pkt {
        struct list_head node;
        struct usbhs_pipe *pipe;
        struct usbhs_pkt_handle *handler;
+       dma_addr_t dma;
        void *buf;
        int length;
+       int trans;
        int actual;
        int zero;
 };
@@ -45,6 +62,7 @@ struct usbhs_pkt {
 struct usbhs_pkt_handle {
        int (*prepare)(struct usbhs_pkt *pkt, int *is_done);
        int (*try_run)(struct usbhs_pkt *pkt, int *is_done);
+       int (*dma_done)(struct usbhs_pkt *pkt, int *is_done);
 };
 
 /*
@@ -61,12 +79,17 @@ void usbhs_fifo_quit(struct usbhs_priv *priv);
 enum {
        USBHSF_PKT_PREPARE,
        USBHSF_PKT_TRY_RUN,
+       USBHSF_PKT_DMA_DONE,
 };
 
 extern struct usbhs_pkt_handle usbhs_fifo_pio_push_handler;
 extern struct usbhs_pkt_handle usbhs_fifo_pio_pop_handler;
 extern struct usbhs_pkt_handle usbhs_ctrl_stage_end_handler;
 
+extern struct usbhs_pkt_handle usbhs_fifo_dma_push_handler;
+extern struct usbhs_pkt_handle usbhs_fifo_dma_pop_handler;
+
+
 void usbhs_pkt_init(struct usbhs_pkt *pkt);
 void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt,
                    struct usbhs_pkt_handle *handler,
@@ -76,5 +99,6 @@ int __usbhs_pkt_handler(struct usbhs_pipe *pipe, int type);
 
 #define usbhs_pkt_start(p)     __usbhs_pkt_handler(p, USBHSF_PKT_PREPARE)
 #define usbhs_pkt_run(p)       __usbhs_pkt_handler(p, USBHSF_PKT_TRY_RUN)
+#define usbhs_pkt_dmadone(p)   __usbhs_pkt_handler(p, USBHSF_PKT_DMA_DONE)
 
 #endif /* RENESAS_USB_FIFO_H */
index 89d2b16fbb108149174e316f6b4733612779968a..31d28dc78aa3e79276e0bec4a47ca313e8537376 100644 (file)
@@ -160,6 +160,71 @@ static void usbhsg_queue_done(struct usbhs_pkt *pkt)
        usbhsg_queue_pop(uep, ureq, 0);
 }
 
+static int usbhsg_dma_map(struct device *dev,
+                         struct usbhs_pkt *pkt,
+                         enum dma_data_direction dir)
+{
+       struct usbhsg_request *ureq = usbhsg_pkt_to_ureq(pkt);
+       struct usb_request *req = &ureq->req;
+
+       if (pkt->dma != DMA_ADDR_INVALID) {
+               dev_err(dev, "dma is already mapped\n");
+               return -EIO;
+       }
+
+       if (req->dma == DMA_ADDR_INVALID) {
+               pkt->dma = dma_map_single(dev, pkt->buf, pkt->length, dir);
+       } else {
+               dma_sync_single_for_device(dev, req->dma, req->length, dir);
+               pkt->dma = req->dma;
+       }
+
+       if (dma_mapping_error(dev, pkt->dma)) {
+               dev_err(dev, "dma mapping error %x\n", pkt->dma);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int usbhsg_dma_unmap(struct device *dev,
+                           struct usbhs_pkt *pkt,
+                           enum dma_data_direction dir)
+{
+       struct usbhsg_request *ureq = usbhsg_pkt_to_ureq(pkt);
+       struct usb_request *req = &ureq->req;
+
+       if (pkt->dma == DMA_ADDR_INVALID) {
+               dev_err(dev, "dma is not mapped\n");
+               return -EIO;
+       }
+
+       if (req->dma == DMA_ADDR_INVALID)
+               dma_unmap_single(dev, pkt->dma, pkt->length, dir);
+       else
+               dma_sync_single_for_cpu(dev, req->dma, req->length, dir);
+
+       pkt->dma = DMA_ADDR_INVALID;
+
+       return 0;
+}
+
+static int usbhsg_dma_map_ctrl(struct usbhs_pkt *pkt, int map)
+{
+       struct usbhs_pipe *pipe = pkt->pipe;
+       struct usbhsg_uep *uep = usbhsg_pipe_to_uep(pipe);
+       struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+       struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+       enum dma_data_direction dir;
+
+       dir = usbhs_pipe_is_dir_in(pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+       if (map)
+               return usbhsg_dma_map(dev, pkt, dir);
+       else
+               return usbhsg_dma_unmap(dev, pkt, dir);
+}
+
 /*
  *             USB_TYPE_STANDARD / clear feature functions
  */
@@ -434,6 +499,8 @@ static struct usb_request *usbhsg_ep_alloc_request(struct usb_ep *ep,
 
        usbhs_pkt_init(usbhsg_ureq_to_pkt(ureq));
 
+       ureq->req.dma = DMA_ADDR_INVALID;
+
        return &ureq->req;
 }
 
@@ -569,7 +636,8 @@ static int usbhsg_try_start(struct usbhs_priv *priv, u32 status)
         * pipe initialize and enable DCP
         */
        usbhs_pipe_init(priv,
-                       usbhsg_queue_done);
+                       usbhsg_queue_done,
+                       usbhsg_dma_map_ctrl);
        usbhs_fifo_init(priv);
        usbhsg_uep_init(gpriv);
 
index c0505876fd8cdfaac8c78bde4eefeb9c8afad6a3..d0ae846632cdd48218df45a90d88cbe87202ba80 100644 (file)
@@ -532,7 +532,8 @@ static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type)
 }
 
 void usbhs_pipe_init(struct usbhs_priv *priv,
-                    void (*done)(struct usbhs_pkt *pkt))
+                    void (*done)(struct usbhs_pkt *pkt),
+                    int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map))
 {
        struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv);
        struct device *dev = usbhs_priv_to_dev(priv);
@@ -572,6 +573,7 @@ void usbhs_pipe_init(struct usbhs_priv *priv,
        }
 
        info->done = done;
+       info->dma_map_ctrl = dma_map_ctrl;
 }
 
 struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv,
index 484adbed6dfb157443ae7f200948fdce69cfa02c..35e100477e55c3622eeed1e4dc57600a707de340 100644 (file)
@@ -44,6 +44,7 @@ struct usbhs_pipe_info {
        int bufnmb_last;        /* FIXME : driver needs good allocator */
 
        void (*done)(struct usbhs_pkt *pkt);
+       int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map);
 };
 
 /*
@@ -82,7 +83,8 @@ void usbhs_pipe_remove(struct usbhs_priv *priv);
 int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe);
 int usbhs_pipe_is_dir_host(struct usbhs_pipe *pipe);
 void usbhs_pipe_init(struct usbhs_priv *priv,
-                    void (*done)(struct usbhs_pkt *pkt));
+                    void (*done)(struct usbhs_pkt *pkt),
+                    int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map));
 int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe);
 void usbhs_pipe_clear_sequence(struct usbhs_pipe *pipe);
 int usbhs_pipe_is_accessible(struct usbhs_pipe *pipe);
index 3a7f1d982dd6436aec9c8748d6ace583158b111f..8977431259c6c12660647590bb7ae5f8724e610d 100644 (file)
@@ -110,6 +110,23 @@ struct renesas_usbhs_driver_param {
         * delay time from notify_hotplug callback
         */
        int detection_delay;
+
+       /*
+        * option:
+        *
+        * dma id for dmaengine
+        */
+       int d0_tx_id;
+       int d0_rx_id;
+       int d1_tx_id;
+       int d1_rx_id;
+
+       /*
+        * option:
+        *
+        * pio <--> dma border.
+        */
+       int pio_dma_border; /* default is 64byte */
 };
 
 /*