usb: renesas_usbhs: add DMAEngine support
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>
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index e510b29..665259a 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -304,6 +304,8 @@
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 ? */
diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h
index 06d7239..b410463 100644
--- a/drivers/usb/renesas_usbhs/common.h
+++ b/drivers/usb/renesas_usbhs/common.h
@@ -36,6 +36,12 @@
#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 @@
#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 @@
#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 @@
#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)
diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c
index 14baaad..2016a24 100644
--- a/drivers/usb/renesas_usbhs/fifo.c
+++ b/drivers/usb/renesas_usbhs/fifo.c
@@ -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 @@
void usbhs_pkt_init(struct usbhs_pkt *pkt)
{
+ pkt->dma = DMA_ADDR_INVALID;
INIT_LIST_HEAD(&pkt->node);
}
@@ -136,6 +139,9 @@
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;
@@ -508,6 +514,330 @@
};
/*
+ * 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
*/
static int usbhsf_irq_empty(struct usbhs_priv *priv,
@@ -570,6 +900,19 @@
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 @@
{
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 @@
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 @@
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 @@
/* 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;
}
diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h
index 94db269..ed6d8e5 100644
--- a/drivers/usb/renesas_usbhs/fifo.h
+++ b/drivers/usb/renesas_usbhs/fifo.h
@@ -17,18 +17,33 @@
#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 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_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 @@
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 @@
#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 */
diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c
index 89d2b16..31d28dc 100644
--- a/drivers/usb/renesas_usbhs/mod_gadget.c
+++ b/drivers/usb/renesas_usbhs/mod_gadget.c
@@ -160,6 +160,71 @@
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 @@
usbhs_pkt_init(usbhsg_ureq_to_pkt(ureq));
+ ureq->req.dma = DMA_ADDR_INVALID;
+
return &ureq->req;
}
@@ -569,7 +636,8 @@
* 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);
diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c
index c050587..d0ae846 100644
--- a/drivers/usb/renesas_usbhs/pipe.c
+++ b/drivers/usb/renesas_usbhs/pipe.c
@@ -532,7 +532,8 @@
}
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 @@
}
info->done = done;
+ info->dma_map_ctrl = dma_map_ctrl;
}
struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv,
diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h
index 484adbe..35e1004 100644
--- a/drivers/usb/renesas_usbhs/pipe.h
+++ b/drivers/usb/renesas_usbhs/pipe.h
@@ -44,6 +44,7 @@
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 @@
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);