From e73a9891b3a1c9fc0970e0c9dbe2cc47933ad752 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 6 Jun 2011 14:19:03 +0900 Subject: [PATCH] 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 Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/common.c | 2 + drivers/usb/renesas_usbhs/common.h | 35 +++ drivers/usb/renesas_usbhs/fifo.c | 380 +++++++++++++++++++++++++ drivers/usb/renesas_usbhs/fifo.h | 24 ++ drivers/usb/renesas_usbhs/mod_gadget.c | 70 ++++- drivers/usb/renesas_usbhs/pipe.c | 4 +- drivers/usb/renesas_usbhs/pipe.h | 4 +- include/linux/usb/renesas_usbhs.h | 17 ++ 8 files changed, 533 insertions(+), 3 deletions(-) diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index e510b29216b3eb..665259aec87193 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -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 ? */ diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index 06d7239a044d7b..b410463a1212ce 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -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) diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 14baaad20b79b2..2016a2448ccba9 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 @@ 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; } diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h index 94db269f84c2a3..ed6d8e56c13c0f 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 +#include +#include #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 */ diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 89d2b16fbb1081..31d28dc78aa3e7 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -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); diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index c0505876fd8cdf..d0ae846632cdd4 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -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, diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index 484adbed6dfb15..35e100477e55c3 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -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); diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h index 3a7f1d982dd643..8977431259c6c1 100644 --- a/include/linux/usb/renesas_usbhs.h +++ b/include/linux/usb/renesas_usbhs.h @@ -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 */ }; /*