diff mbox

[4/4] usb: renesas_usbhs: add support for USB-DMAC

Message ID 1423469774-6532-5-git-send-email-yoshihiro.shimoda.uh@renesas.com (mailing list archive)
State Superseded
Delegated to: Geert Uytterhoeven
Headers show

Commit Message

Yoshihiro Shimoda Feb. 9, 2015, 8:16 a.m. UTC
Some Renesas SoCs have the USB-DMAC. It is able to terminate transfers
when a short packet is received, even if less bytes than the transfer
counter size have been received. Also, it is able to send a short
packet even if the packet size is not multiples of 8bytes.

Since the previous code has used the interruption of USBHS controller
when receiving packets even if this driver has used a dmac, a lot of
interruptions has happened. This patch will reduce such interruptions.

This patch allows to use the USB-DMAC on R-Car H2 and M2.

Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
---
 drivers/usb/renesas_usbhs/common.c |   19 +++++
 drivers/usb/renesas_usbhs/common.h |    7 ++
 drivers/usb/renesas_usbhs/fifo.c   |  164 ++++++++++++++++++++++++++++++++++--
 drivers/usb/renesas_usbhs/fifo.h   |    1 +
 drivers/usb/renesas_usbhs/pipe.c   |   39 +++++++++
 drivers/usb/renesas_usbhs/pipe.h   |    1 +
 include/linux/usb/renesas_usbhs.h  |    2 +
 7 files changed, 227 insertions(+), 6 deletions(-)

Comments

Geert Uytterhoeven Feb. 10, 2015, 10:45 a.m. UTC | #1
Hi Shimoda-san,

On Mon, Feb 9, 2015 at 9:16 AM, Yoshihiro Shimoda
<yoshihiro.shimoda.uh@renesas.com> wrote:
> Some Renesas SoCs have the USB-DMAC. It is able to terminate transfers
> when a short packet is received, even if less bytes than the transfer
> counter size have been received. Also, it is able to send a short
> packet even if the packet size is not multiples of 8bytes.
>
> Since the previous code has used the interruption of USBHS controller
> when receiving packets even if this driver has used a dmac, a lot of
> interruptions has happened. This patch will reduce such interruptions.
>
> This patch allows to use the USB-DMAC on R-Car H2 and M2.
>
> Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>

> --- a/drivers/usb/renesas_usbhs/common.c
> +++ b/drivers/usb/renesas_usbhs/common.c

> @@ -487,6 +497,15 @@ static struct renesas_usbhs_platform_info *usbhs_parse_dt(struct device *dev)
>         if (gpio > 0)
>                 dparam->enable_gpio = gpio;
>
> +       switch (dparam->type) {
> +       case USBHS_TYPE_R8A7790:
> +       case USBHS_TYPE_R8A7791:
> +               dparam->has_usb_dmac = 1;
> +               break;
> +       default:
> +               break;
> +       }
> +
>         return info;
>  }

>  struct usbhs_priv *usbhs_pdev_to_priv(struct platform_device *pdev);
> diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c
> index 3b77a1b..1e7dc6e 100644
> --- a/drivers/usb/renesas_usbhs/fifo.c
> +++ b/drivers/usb/renesas_usbhs/fifo.c

> @@ -847,10 +849,13 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done)
>             usbhs_pipe_is_dcp(pipe))
>                 goto usbhsf_pio_prepare_push;
>
> -       if (len & 0x7) /* 8byte alignment */
> +       /* default: 8byte alignment */
> +       if (!usbhs_get_dparam(priv, has_usb_dmac) && len & 0x7)
>                 goto usbhsf_pio_prepare_push;

So the has_usb_dmac flags indicates that DMA addresses are not limited to
8-byte alignment.

Can't this be handled by looking at a dma_mask, as set by the DMAC?

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds
--
To unsubscribe from this list: send the line "unsubscribe linux-sh" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Yoshihiro Shimoda Feb. 12, 2015, 2:32 a.m. UTC | #2
SGkgR2VlcnQtc2FuLA0KDQo+IEhpIFNoaW1vZGEtc2FuLA0KPiANCj4gT24gTW9uLCBGZWIgOSwg
MjAxNSBhdCA5OjE2IEFNLCBZb3NoaWhpcm8gU2hpbW9kYQ0KPiA8eW9zaGloaXJvLnNoaW1vZGEu
dWhAcmVuZXNhcy5jb20+IHdyb3RlOg0KPiA+IFNvbWUgUmVuZXNhcyBTb0NzIGhhdmUgdGhlIFVT
Qi1ETUFDLiBJdCBpcyBhYmxlIHRvIHRlcm1pbmF0ZSB0cmFuc2ZlcnMNCj4gPiB3aGVuIGEgc2hv
cnQgcGFja2V0IGlzIHJlY2VpdmVkLCBldmVuIGlmIGxlc3MgYnl0ZXMgdGhhbiB0aGUgdHJhbnNm
ZXINCj4gPiBjb3VudGVyIHNpemUgaGF2ZSBiZWVuIHJlY2VpdmVkLiBBbHNvLCBpdCBpcyBhYmxl
IHRvIHNlbmQgYSBzaG9ydA0KPiA+IHBhY2tldCBldmVuIGlmIHRoZSBwYWNrZXQgc2l6ZSBpcyBu
b3QgbXVsdGlwbGVzIG9mIDhieXRlcy4NCj4gPg0KPiA+IFNpbmNlIHRoZSBwcmV2aW91cyBjb2Rl
IGhhcyB1c2VkIHRoZSBpbnRlcnJ1cHRpb24gb2YgVVNCSFMgY29udHJvbGxlcg0KPiA+IHdoZW4g
cmVjZWl2aW5nIHBhY2tldHMgZXZlbiBpZiB0aGlzIGRyaXZlciBoYXMgdXNlZCBhIGRtYWMsIGEg
bG90IG9mDQo+ID4gaW50ZXJydXB0aW9ucyBoYXMgaGFwcGVuZWQuIFRoaXMgcGF0Y2ggd2lsbCBy
ZWR1Y2Ugc3VjaCBpbnRlcnJ1cHRpb25zLg0KPiA+DQo+ID4gVGhpcyBwYXRjaCBhbGxvd3MgdG8g
dXNlIHRoZSBVU0ItRE1BQyBvbiBSLUNhciBIMiBhbmQgTTIuDQo+ID4NCj4gPiBTaWduZWQtb2Zm
LWJ5OiBZb3NoaWhpcm8gU2hpbW9kYSA8eW9zaGloaXJvLnNoaW1vZGEudWhAcmVuZXNhcy5jb20+
DQo+IA0KPiA+IC0tLSBhL2RyaXZlcnMvdXNiL3JlbmVzYXNfdXNiaHMvY29tbW9uLmMNCj4gPiAr
KysgYi9kcml2ZXJzL3VzYi9yZW5lc2FzX3VzYmhzL2NvbW1vbi5jDQo+IA0KPiA+IEBAIC00ODcs
NiArNDk3LDE1IEBAIHN0YXRpYyBzdHJ1Y3QgcmVuZXNhc191c2Joc19wbGF0Zm9ybV9pbmZvICp1
c2Joc19wYXJzZV9kdChzdHJ1Y3QgZGV2aWNlICpkZXYpDQo+ID4gICAgICAgICBpZiAoZ3BpbyA+
IDApDQo+ID4gICAgICAgICAgICAgICAgIGRwYXJhbS0+ZW5hYmxlX2dwaW8gPSBncGlvOw0KPiA+
DQo+ID4gKyAgICAgICBzd2l0Y2ggKGRwYXJhbS0+dHlwZSkgew0KPiA+ICsgICAgICAgY2FzZSBV
U0JIU19UWVBFX1I4QTc3OTA6DQo+ID4gKyAgICAgICBjYXNlIFVTQkhTX1RZUEVfUjhBNzc5MToN
Cj4gPiArICAgICAgICAgICAgICAgZHBhcmFtLT5oYXNfdXNiX2RtYWMgPSAxOw0KPiA+ICsgICAg
ICAgICAgICAgICBicmVhazsNCj4gPiArICAgICAgIGRlZmF1bHQ6DQo+ID4gKyAgICAgICAgICAg
ICAgIGJyZWFrOw0KPiA+ICsgICAgICAgfQ0KPiA+ICsNCj4gPiAgICAgICAgIHJldHVybiBpbmZv
Ow0KPiA+ICB9DQo+IA0KPiA+ICBzdHJ1Y3QgdXNiaHNfcHJpdiAqdXNiaHNfcGRldl90b19wcml2
KHN0cnVjdCBwbGF0Zm9ybV9kZXZpY2UgKnBkZXYpOw0KPiA+IGRpZmYgLS1naXQgYS9kcml2ZXJz
L3VzYi9yZW5lc2FzX3VzYmhzL2ZpZm8uYyBiL2RyaXZlcnMvdXNiL3JlbmVzYXNfdXNiaHMvZmlm
by5jDQo+ID4gaW5kZXggM2I3N2ExYi4uMWU3ZGM2ZSAxMDA2NDQNCj4gPiAtLS0gYS9kcml2ZXJz
L3VzYi9yZW5lc2FzX3VzYmhzL2ZpZm8uYw0KPiA+ICsrKyBiL2RyaXZlcnMvdXNiL3JlbmVzYXNf
dXNiaHMvZmlmby5jDQo+IA0KPiA+IEBAIC04NDcsMTAgKzg0OSwxMyBAQCBzdGF0aWMgaW50IHVz
YmhzZl9kbWFfcHJlcGFyZV9wdXNoKHN0cnVjdCB1c2Joc19wa3QgKnBrdCwgaW50ICppc19kb25l
KQ0KPiA+ICAgICAgICAgICAgIHVzYmhzX3BpcGVfaXNfZGNwKHBpcGUpKQ0KPiA+ICAgICAgICAg
ICAgICAgICBnb3RvIHVzYmhzZl9waW9fcHJlcGFyZV9wdXNoOw0KPiA+DQo+ID4gLSAgICAgICBp
ZiAobGVuICYgMHg3KSAvKiA4Ynl0ZSBhbGlnbm1lbnQgKi8NCj4gPiArICAgICAgIC8qIGRlZmF1
bHQ6IDhieXRlIGFsaWdubWVudCAqLw0KPiA+ICsgICAgICAgaWYgKCF1c2Joc19nZXRfZHBhcmFt
KHByaXYsIGhhc191c2JfZG1hYykgJiYgbGVuICYgMHg3KQ0KPiA+ICAgICAgICAgICAgICAgICBn
b3RvIHVzYmhzZl9waW9fcHJlcGFyZV9wdXNoOw0KPiANCj4gU28gdGhlIGhhc191c2JfZG1hYyBm
bGFncyBpbmRpY2F0ZXMgdGhhdCBETUEgYWRkcmVzc2VzIGFyZSBub3QgbGltaXRlZCB0bw0KPiA4
LWJ5dGUgYWxpZ25tZW50Lg0KPiANCj4gQ2FuJ3QgdGhpcyBiZSBoYW5kbGVkIGJ5IGxvb2tpbmcg
YXQgYSBkbWFfbWFzaywgYXMgc2V0IGJ5IHRoZSBETUFDPw0KDQpPcHBzLCB0aGUgY29tbWVudCAi
OGJ5dGUgYWxpZ25tZW50IiBpcyB3cm9uZy4NCkkgd2lsbCBmaXggdGhlIGNvbW1lbnQuDQoNClRo
ZSBVU0ItRE1BQyBjYW4gc2VuZCBhIHBhY2tldCB0aGF0IGl0IGlzIG5vdCBtdWx0aXBsZXMgb2Yg
OC1ieXRlcy4NClRoZSBVU0ItRE1BQyBuZWVkcyAzMi1ieXRlcyBhbGlnbm1lbnQgYnkgdGhlIGZv
bGxvd2luZyBjb2RlLg0KDQotCWlmICgodWludHB0cl90KShwa3QtPmJ1ZiArIHBrdC0+YWN0dWFs
KSAmIDB4NykgLyogOGJ5dGUgYWxpZ25tZW50ICovDQorCWFsaWduX21hc2sgPSB1c2Joc19nZXRf
ZHBhcmFtKHByaXYsIGhhc191c2JfZG1hYykgPw0KKwkJCQkJVVNCSFNfVVNCX0RNQUNfWEZFUl9T
SVpFIC0gMSA6IDB4NzsNCisJaWYgKCh1aW50cHRyX3QpKHBrdC0+YnVmICsgcGt0LT5hY3R1YWwp
ICYgYWxpZ25fbWFzaykNCg0KVGhlIHByZXZpb3VzIGNvZGUgd2lsbCB1c2UgYSBkbWFjIGlmIGEg
cGFja2V0IHNpemUgaXMgbXVsdGlwbGVzIG9mIDgtYnl0ZXMgYW5kDQppdCBpcyA4LWJ5dGVzIGFs
aWdubWVudC4NCg0KQmVzdCByZWdhcmRzLA0KWW9zaGloaXJvIFNoaW1vZGENCg0KPiBHcntvZXRq
ZSxlZXRpbmd9cywNCj4gDQo+ICAgICAgICAgICAgICAgICAgICAgICAgIEdlZXJ0DQo+IA0KPiAt
LQ0KPiBHZWVydCBVeXR0ZXJob2V2ZW4gLS0gVGhlcmUncyBsb3RzIG9mIExpbnV4IGJleW9uZCBp
YTMyIC0tIGdlZXJ0QGxpbnV4LW02OGsub3JnDQo+IA0KPiBJbiBwZXJzb25hbCBjb252ZXJzYXRp
b25zIHdpdGggdGVjaG5pY2FsIHBlb3BsZSwgSSBjYWxsIG15c2VsZiBhIGhhY2tlci4gQnV0DQo+
IHdoZW4gSSdtIHRhbGtpbmcgdG8gam91cm5hbGlzdHMgSSBqdXN0IHNheSAicHJvZ3JhbW1lciIg
b3Igc29tZXRoaW5nIGxpa2UgdGhhdC4NCj4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAtLSBMaW51cyBUb3J2YWxkcw0K
--
To unsubscribe from this list: send the line "unsubscribe linux-sh" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index 4cf77d3..0f7e850 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -276,6 +276,16 @@  int usbhs_set_device_config(struct usbhs_priv *priv, int devnum,
 }
 
 /*
+ *		interrupt functions
+ */
+void usbhs_xxxsts_clear(struct usbhs_priv *priv, u16 sts_reg, u16 bit)
+{
+	u16 pipe_mask = (u16)GENMASK(usbhs_get_dparam(priv, pipe_size), 0);
+
+	usbhs_write(priv, sts_reg, ~(1 << bit) & pipe_mask);
+}
+
+/*
  *		local functions
  */
 static void usbhsc_set_buswait(struct usbhs_priv *priv)
@@ -487,6 +497,15 @@  static struct renesas_usbhs_platform_info *usbhs_parse_dt(struct device *dev)
 	if (gpio > 0)
 		dparam->enable_gpio = gpio;
 
+	switch (dparam->type) {
+	case USBHS_TYPE_R8A7790:
+	case USBHS_TYPE_R8A7791:
+		dparam->has_usb_dmac = 1;
+		break;
+	default:
+		break;
+	}
+
 	return info;
 }
 
diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h
index fc96e92..8c5fc12 100644
--- a/drivers/usb/renesas_usbhs/common.h
+++ b/drivers/usb/renesas_usbhs/common.h
@@ -193,6 +193,7 @@  struct usbhs_priv;
 #define TYPE_BULK	(1 << 14)
 #define TYPE_INT	(2 << 14)
 #define TYPE_ISO	(3 << 14)
+#define BFRE		(1 << 10)	/* BRDY Interrupt Operation Spec. */
 #define DBLB		(1 << 9)	/* Double Buffer Mode */
 #define SHTNAK		(1 << 7)	/* Pipe Disable in Transfer End */
 #define DIR_OUT		(1 << 4)	/* Transfer Direction */
@@ -216,6 +217,7 @@  struct usbhs_priv;
 #define	ACLRM		(1 << 9)	/* Buffer Auto-Clear Mode */
 #define SQCLR		(1 << 8)	/* Toggle Bit Clear */
 #define SQSET		(1 << 7)	/* Toggle Bit Set */
+#define SQMON		(1 << 6)	/* Toggle Bit Check */
 #define PBUSY		(1 << 5)	/* Pipe Busy */
 #define PID_MASK	(0x3)		/* Response PID */
 #define  PID_NAK	0
@@ -324,6 +326,11 @@  int usbhs_set_device_config(struct usbhs_priv *priv, int devnum, u16 upphub,
 			   u16 hubport, u16 speed);
 
 /*
+ * interrupt functions
+ */
+void usbhs_xxxsts_clear(struct usbhs_priv *priv, u16 sts_reg, u16 bit);
+
+/*
  * data
  */
 struct usbhs_priv *usbhs_pdev_to_priv(struct platform_device *pdev);
diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c
index 3b77a1b..1e7dc6e 100644
--- a/drivers/usb/renesas_usbhs/fifo.c
+++ b/drivers/usb/renesas_usbhs/fifo.c
@@ -813,7 +813,8 @@  static void xfer_work(struct work_struct *work)
 	desc->callback		= usbhsf_dma_complete;
 	desc->callback_param	= pipe;
 
-	if (dmaengine_submit(desc) < 0) {
+	pkt->cookie = dmaengine_submit(desc);
+	if (pkt->cookie < 0) {
 		dev_err(dev, "Failed to submit dma descriptor\n");
 		return;
 	}
@@ -838,6 +839,7 @@  static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done)
 	struct usbhs_fifo *fifo;
 	int len = pkt->length - pkt->actual;
 	int ret;
+	uintptr_t align_mask;
 
 	if (usbhs_pipe_is_busy(pipe))
 		return 0;
@@ -847,10 +849,13 @@  static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done)
 	    usbhs_pipe_is_dcp(pipe))
 		goto usbhsf_pio_prepare_push;
 
-	if (len & 0x7) /* 8byte alignment */
+	/* default: 8byte alignment */
+	if (!usbhs_get_dparam(priv, has_usb_dmac) && len & 0x7)
 		goto usbhsf_pio_prepare_push;
 
-	if ((uintptr_t)(pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */
+	align_mask = usbhs_get_dparam(priv, has_usb_dmac) ?
+					USBHS_USB_DMAC_XFER_SIZE - 1 : 0x7;
+	if ((uintptr_t)(pkt->buf + pkt->actual) & align_mask)
 		goto usbhsf_pio_prepare_push;
 
 	/* return at this time if the pipe is running */
@@ -924,7 +929,85 @@  struct usbhs_pkt_handle usbhs_fifo_dma_push_handler = {
 /*
  *		DMA pop handler
  */
-static int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done)
+
+static int usbhsf_dma_prepare_pop_with_rx_irq(struct usbhs_pkt *pkt,
+					      int *is_done)
+{
+	return usbhsf_prepare_pop(pkt, is_done);
+}
+
+static int usbhsf_dma_prepare_pop_with_usb_dmac(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 ret;
+
+	if (usbhs_pipe_is_busy(pipe))
+		return 0;
+
+	/* use PIO if packet is less than pio_dma_border or pipe is DCP */
+	if ((pkt->length < usbhs_get_dparam(priv, pio_dma_border)) ||
+	    usbhs_pipe_is_dcp(pipe))
+		goto usbhsf_pio_prepare_pop;
+
+	fifo = usbhsf_get_dma_fifo(priv, pkt);
+	if (!fifo)
+		goto usbhsf_pio_prepare_pop;
+
+	if ((uintptr_t)pkt->buf & (USBHS_USB_DMAC_XFER_SIZE - 1))
+		goto usbhsf_pio_prepare_pop;
+
+	usbhs_pipe_config_change_bfre(pipe, 1);
+
+	ret = usbhsf_fifo_select(pipe, fifo, 0);
+	if (ret < 0)
+		goto usbhsf_pio_prepare_pop;
+
+	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 = pkt->length;
+
+	INIT_WORK(&pkt->work, xfer_work);
+	schedule_work(&pkt->work);
+
+	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;
+	usbhs_pipe_config_change_bfre(pipe, 0);
+
+	return pkt->handler->prepare(pkt, is_done);
+}
+
+static int usbhsf_dma_prepare_pop(struct usbhs_pkt *pkt, int *is_done)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pkt->pipe);
+
+	if (usbhs_get_dparam(priv, has_usb_dmac))
+		return usbhsf_dma_prepare_pop_with_usb_dmac(pkt, is_done);
+	else
+		return usbhsf_dma_prepare_pop_with_rx_irq(pkt, is_done);
+}
+
+static int usbhsf_dma_try_pop_with_rx_irq(struct usbhs_pkt *pkt, int *is_done)
 {
 	struct usbhs_pipe *pipe = pkt->pipe;
 	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
@@ -993,7 +1076,16 @@  usbhsf_pio_prepare_pop:
 	return pkt->handler->try_run(pkt, is_done);
 }
 
-static int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done)
+static int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pkt->pipe);
+
+	BUG_ON(usbhs_get_dparam(priv, has_usb_dmac));
+
+	return usbhsf_dma_try_pop_with_rx_irq(pkt, is_done);
+}
+
+static int usbhsf_dma_pop_done_with_rx_irq(struct usbhs_pkt *pkt, int *is_done)
 {
 	struct usbhs_pipe *pipe = pkt->pipe;
 	int maxp = usbhs_pipe_get_maxpacket(pipe);
@@ -1017,8 +1109,68 @@  static int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done)
 	return 0;
 }
 
+static size_t usbhs_dma_calc_received_size(struct usbhs_pkt *pkt,
+					   struct dma_chan *chan, int dtln)
+{
+	struct usbhs_pipe *pipe = pkt->pipe;
+	struct dma_tx_state state;
+	size_t received_size;
+	int maxp = usbhs_pipe_get_maxpacket(pipe);
+
+	dmaengine_tx_status(chan, pkt->cookie, &state);
+	received_size = pkt->length - state.residue;
+
+	if (dtln) {
+		received_size -= USBHS_USB_DMAC_XFER_SIZE;
+		received_size &= ~(maxp - 1);
+		received_size += dtln;
+	}
+
+	return received_size;
+}
+
+static int usbhsf_dma_pop_done_with_usb_dmac(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 = usbhs_pipe_to_fifo(pipe);
+	struct dma_chan *chan = usbhsf_dma_chan_get(fifo, pkt);
+	int rcv_len;
+
+	/*
+	 * Since the driver disables rx_irq in DMA mode, the interrupt handler
+	 * cannot the BRDYSTS. So, the function clears it here because the
+	 * driver may use PIO mode next time.
+	 */
+	usbhs_xxxsts_clear(priv, BRDYSTS, usbhs_pipe_number(pipe));
+
+	rcv_len = usbhsf_fifo_rcv_len(priv, fifo);
+	usbhsf_fifo_clear(pipe, fifo);
+	pkt->actual = usbhs_dma_calc_received_size(pkt, chan, rcv_len);
+
+	usbhsf_dma_stop(pipe, fifo);
+	usbhsf_dma_unmap(pkt);
+	usbhsf_fifo_unselect(pipe, pipe->fifo);
+
+	/* The driver can assume the rx transaction is always "done" */
+	*is_done = 1;
+
+	return 0;
+}
+
+static int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pkt->pipe);
+
+	if (usbhs_get_dparam(priv, has_usb_dmac))
+		return usbhsf_dma_pop_done_with_usb_dmac(pkt, is_done);
+	else
+		return usbhsf_dma_pop_done_with_rx_irq(pkt, is_done);
+}
+
 struct usbhs_pkt_handle usbhs_fifo_dma_pop_handler = {
-	.prepare	= usbhsf_prepare_pop,
+	.prepare	= usbhsf_dma_prepare_pop,
 	.try_run	= usbhsf_dma_try_pop,
 	.dma_done	= usbhsf_dma_pop_done
 };
diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h
index f07037c1..04d3f8a 100644
--- a/drivers/usb/renesas_usbhs/fifo.h
+++ b/drivers/usb/renesas_usbhs/fifo.h
@@ -58,6 +58,7 @@  struct usbhs_pkt {
 		     struct usbhs_pkt *pkt);
 	struct work_struct work;
 	dma_addr_t dma;
+	dma_cookie_t cookie;
 	void *buf;
 	int length;
 	int trans;
diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c
index 007f45a..4f9c335 100644
--- a/drivers/usb/renesas_usbhs/pipe.c
+++ b/drivers/usb/renesas_usbhs/pipe.c
@@ -84,6 +84,17 @@  static void __usbhsp_pipe_xxx_set(struct usbhs_pipe *pipe,
 		usbhs_bset(priv, pipe_reg, mask, val);
 }
 
+static u16 __usbhsp_pipe_xxx_get(struct usbhs_pipe *pipe,
+				 u16 dcp_reg, u16 pipe_reg)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+
+	if (usbhs_pipe_is_dcp(pipe))
+		return usbhs_read(priv, dcp_reg);
+	else
+		return usbhs_read(priv, pipe_reg);
+}
+
 /*
  *		DCPCFG/PIPECFG functions
  */
@@ -92,6 +103,11 @@  static void usbhsp_pipe_cfg_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
 	__usbhsp_pipe_xxx_set(pipe, DCPCFG, PIPECFG, mask, val);
 }
 
+static u16 usbhsp_pipe_cfg_get(struct usbhs_pipe *pipe)
+{
+	return __usbhsp_pipe_xxx_get(pipe, DCPCFG, PIPECFG);
+}
+
 /*
  *		PIPEnTRN/PIPEnTRE functions
  */
@@ -616,6 +632,11 @@  void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int sequence)
 	usbhsp_pipectrl_set(pipe, mask, val);
 }
 
+static int usbhs_pipe_get_data_sequence(struct usbhs_pipe *pipe)
+{
+	return !!(usbhsp_pipectrl_get(pipe) & SQMON);
+}
+
 void usbhs_pipe_clear(struct usbhs_pipe *pipe)
 {
 	if (usbhs_pipe_is_dcp(pipe)) {
@@ -626,6 +647,24 @@  void usbhs_pipe_clear(struct usbhs_pipe *pipe)
 	}
 }
 
+void usbhs_pipe_config_change_bfre(struct usbhs_pipe *pipe, int enable)
+{
+	int sequence;
+
+	if (usbhs_pipe_is_dcp(pipe))
+		return;
+
+	usbhsp_pipe_select(pipe);
+	/* check if the driver needs to change the BFRE value */
+	if (!(enable ^ !!(usbhsp_pipe_cfg_get(pipe) & BFRE)))
+		return;
+
+	sequence = usbhs_pipe_get_data_sequence(pipe);
+	usbhsp_pipe_cfg_set(pipe, BFRE, enable ? BFRE : 0);
+	usbhs_pipe_clear(pipe);
+	usbhs_pipe_data_sequence(pipe, sequence);
+}
+
 static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type)
 {
 	struct usbhs_pipe *pos, *pipe;
diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h
index d24a059..b0bc7b6 100644
--- a/drivers/usb/renesas_usbhs/pipe.h
+++ b/drivers/usb/renesas_usbhs/pipe.h
@@ -97,6 +97,7 @@  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);
+void usbhs_pipe_config_change_bfre(struct usbhs_pipe *pipe, int enable);
 
 #define usbhs_pipe_sequence_data0(pipe)	usbhs_pipe_data_sequence(pipe, 0)
 #define usbhs_pipe_sequence_data1(pipe)	usbhs_pipe_data_sequence(pipe, 1)
diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h
index 9fd9e48..f06529c 100644
--- a/include/linux/usb/renesas_usbhs.h
+++ b/include/linux/usb/renesas_usbhs.h
@@ -165,6 +165,8 @@  struct renesas_usbhs_driver_param {
 	 */
 	u32 has_otg:1; /* for controlling PWEN/EXTLP */
 	u32 has_sudmac:1; /* for SUDMAC */
+	u32 has_usb_dmac:1; /* for USB-DMAC */
+#define USBHS_USB_DMAC_XFER_SIZE	32	/* hardcode the xfer size */
 };
 
 #define USBHS_TYPE_R8A7790 1