diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 2fa57cd..69030bb 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -42,6 +42,7 @@
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
#include <linux/usb/phy.h>
+#include <asm/unaligned.h>
#include "hw.h"
/*
@@ -958,50 +959,6 @@ enum dwc2_halt_status {
DWC2_HC_XFER_URB_DEQUEUE,
};
-#ifdef CONFIG_MIPS
-/*
- * There are some MIPS machines that can run in either big-endian
- * or little-endian mode and that use the dwc2 register without
- * a byteswap in both ways.
- * Unlike other architectures, MIPS apparently does not require a
- * barrier before the __raw_writel() to synchronize with DMA but does
- * require the barrier after the __raw_writel() to serialize a set of
- * writes. This set of operations was added specifically for MIPS and
- * should only be used there.
- */
-static inline u32 dwc2_readl(struct dwc2_hsotg *hsotg,
- ptrdiff_t reg)
-{
- const void __iomem *addr = hsotg->regs + reg;
- u32 value = __raw_readl(addr);
-
- /*
- * In order to preserve endianness __raw_* operation is used. Therefore
- * a barrier is needed to ensure IO access is not re-ordered across
- * reads or writes
- */
- mb();
- return value;
-}
-
-static inline void dwc2_writel(struct dwc2_hsotg *hsotg, u32 value,
- ptrdiff_t reg)
-{
- const void __iomem *addr = hsotg->regs + reg;
- __raw_writel(value, addr);
-
- /*
- * In order to preserve endianness __raw_* operation is used. Therefore
- * a barrier is needed to ensure IO access is not re-ordered across
- * reads or writes
- */
- mb();
-#ifdef DWC2_LOG_WRITES
- pr_info("INFO:: wrote %08x to %p\n", value, addr);
-#endif
-}
-#else
-/* Normal architectures just use readl/write_be */
static inline u32 dwc2_readl(struct dwc2_hsotg *hsotg,
ptrdiff_t reg)
{@@ -1014,7 +971,8 @@ static inline u32 dwc2_readl(struct dwc2_hsotg *hsotg,
}
-static inline void dwc2_writel(struct dwc2_hsotg *hsotg, u32 value,
+static inline void dwc2_writel(struct dwc2_hsotg *hsotg,
+ const u32 value,
ptrdiff_t reg)
{
void __iomem *addr = hsotg->regs + reg;@@ -1028,7 +986,103 @@ static inline void dwc2_writel(struct dwc2_hsotg *hsotg, u32 value,
pr_info("info:: wrote %08x to %p\n", value, addr);
#endif
}
-#endif
+
+static inline void dwc2_readl_rep(struct dwc2_hsotg *hsotg,
+ ptrdiff_t reg,
+ u32 *buf, const size_t len)
+{
+ void __iomem *addr = hsotg->regs + reg;
+ size_t i, remaining = len & ~4;
+
+ if (hsotg->is_big_endian) {
+ if (likely(IS_ALIGNED(*buf, 0x4))) {
+ for (i = len >> 2; i > 0; i--)
+ *buf++ = ioread32be(addr);
+ } else {
+ /* xfer_buf is not DWORD aligned */
+ for (i = len >> 2; i > 0; i--) {
+ u32 data = ioread32be(addr);
+
+ put_unaligned(data, buf);
+ buf++;
+ }
+ }
+ } else {
+ /* little-endian accessors */
+ if (likely(IS_ALIGNED(*buf, 0x4))) {
+ for (i = len >> 2; i > 0; i--)
+ *buf++ = ioread32(addr);
+
+ } else {
+ /* xfer_buf is not DWORD aligned */
+ for (i = len >> 2; i > 0; i--) {
+ u32 data = ioread32be(addr);
+
+ put_unaligned(data, buf);
+ buf++;
+ }
+ }
+ }
+
+ if (unlikely(remaining)) {
+ u32 data_u32;
+ u8 *buf_u8 = (u8 *) buf;
+ u8 *data_u8 = (u8 *) &data_u32;
+
+ data_u32 = dwc2_readl(hsotg, reg);
+
+ while (remaining--)
+ *buf_u8++ = *data_u8++;
+ }
+}
+
+static inline void dwc2_writel_rep(struct dwc2_hsotg *hsotg,
+ const ptrdiff_t reg,
+ const u32 *buf, const size_t len)
+{
+ void __iomem *addr = hsotg->regs + reg;
+ size_t i, remaining = len & ~4;
+
+ if (hsotg->is_big_endian) {
+ if (likely(IS_ALIGNED(*buf, 0x4))) {
+ for (i = len >> 2; i > 0; i--)
+ iowrite32be(*buf++, addr);
+ } else {
+ /* xfer_buf is not DWORD aligned */
+ for (i = len >> 2; i > 0; i--) {
+ u32 data = get_unaligned(buf);
+
+ iowrite32be(data, addr);
+ buf++;
+ }
+ }
+ } else {
+ /* little-endian accessors */
+ if (likely(IS_ALIGNED(*buf, 0x4))) {
+ for (i = len >> 2; i > 0; i--)
+ iowrite32(*buf++, addr);
+ } else {
+ /* xfer_buf is not DWORD aligned */
+ for (i = len >> 2; i > 0; i--) {
+ u32 data = get_unaligned(buf);
+
+ iowrite32(data, addr);
+ buf++;
+ }
+ }
+ }
+
+ if (unlikely(remaining)) {
+ u32 data_u32;
+ u8 *buf_u8 = (u8 *) buf;
+ u8 *data_u8 = (u8 *) &data_u32;
+
+ while (remaining--)
+ *data_u8++ = *buf_u8++;
+
+ dwc2_writel(hsotg, data_u32, reg);
+ }
+}
extern int dwc2_detect_endiannes(struct dwc2_hsotg *hsotg);
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index 2c687d9..531b30f 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -317,7 +317,7 @@ static int dwc2_hsotg_write_fifo(struct dwc2_hsotg *hsotg,
u32 gnptxsts = dwc2_readl(hsotg, GNPTXSTS);
int buf_pos = hs_req->req.actual;
int to_write = hs_ep->size_loaded;
- void *data;
+ u32 *data;
int can_write;
int pkt_round;
int max_transfer;
@@ -457,10 +457,9 @@ static int dwc2_hsotg_write_fifo(struct dwc2_hsotg *hsotg,
if (periodic)
hs_ep->fifo_load += to_write;
- to_write = DIV_ROUND_UP(to_write, 4);
data = hs_req->req.buf + buf_pos;
- iowrite32_rep(hsotg->regs + EPFIFO(hs_ep->index), data, to_write);
+ dwc2_writel_rep(hsotg, EPFIFO(hs_ep->index), data, to_write);
return (to_write >= can_write) ? -ENOSPC : 0;
}
@@ -1439,12 +1438,11 @@ static void dwc2_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size)
{
struct dwc2_hsotg_ep *hs_ep = hsotg->eps_out[ep_idx];
struct dwc2_hsotg_req *hs_req = hs_ep->req;
- void __iomem *fifo = hsotg->regs + EPFIFO(ep_idx);
+ u32 *data;
int to_read;
int max_req;
int read_ptr;
-
if (!hs_req) {
u32 epctl = dwc2_readl(hsotg, DOEPCTL(ep_idx));
int ptr;@@ -1455,7 +1453,7 @@ static void dwc2_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size)
/* dump the data from the FIFO, we've nothing we can do */
for (ptr = 0; ptr < size; ptr += 4)
- (void)dwc2_readl(hsotg, EPFIFO(ep_idx));
+ (void)__raw_readl(hsotg->regs + EPFIFO(ep_idx));
return;
}
@@ -1479,13 +1477,14 @@ static void dwc2_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size)
hs_ep->total_data += to_read;
hs_req->req.actual += to_read;
- to_read = DIV_ROUND_UP(to_read, 4);
/*
* note, we might over-write the buffer end by 3 bytes depending on
* alignment of the data.
*/
- ioread32_rep(fifo, hs_req->req.buf + read_ptr, to_read);
+ data = hs_req->req.buf + read_ptr;
+
+ dwc2_readl_rep(hsotg, EPFIFO(ep_idx), data, to_read);
}
/**
@@ -3411,7 +3416,6 @@ static void dwc2_hsotg_dump(struct dwc2_hsotg *hsotg)
{
#ifdef DEBUG
struct device *dev = hsotg->dev;
- void __iomem *regs = hsotg->regs;
u32 val;
int idx;
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index dcd6338..8568ff4 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -567,19 +567,10 @@ u32 dwc2_calc_frame_interval(struct dwc2_hsotg *hsotg)
void dwc2_read_packet(struct dwc2_hsotg *hsotg, u8 *dest, u16 bytes)
{
u32 *data_buf = (u32 *)dest;
- int word_count = (bytes + 3) / 4;
- int i;
-
- /*
- * Todo: Account for the case where dest is not dword aligned. This
- * requires reading data from the FIFO into a u32 temp buffer, then
- * moving it into the data buffer.
- */
dev_vdbg(hsotg->dev, "%s(%p,%p,%d)\n", __func__, hsotg, dest, bytes);
- for (i = 0; i < word_count; i++, data_buf++)
- *data_buf = dwc2_readl(hsotg, HCFIFO(0));
+ dwc2_readl_rep(hsotg, HCFIFO(0), data_buf, bytes);
}
/**@@ -1236,10 +1227,8 @@ static void dwc2_set_pid_isoc(struct dwc2_host_chan *chan)
static void dwc2_hc_write_packet(struct dwc2_hsotg *hsotg,
struct dwc2_host_chan *chan)
{
- u32 i;
u32 remaining_count;
u32 byte_count;
- u32 dword_count;
u32 *data_buf = (u32 *)chan->xfer_buf;
if (dbg_hc(chan))@@ -1251,20 +1240,7 @@ static void dwc2_hc_write_packet(struct dwc2_hsotg *hsotg,
else
byte_count = remaining_count;
- dword_count = (byte_count + 3) / 4;
-
- if (((unsigned long)data_buf & 0x3) == 0) {
- /* xfer_buf is DWORD aligned */
- for (i = 0; i < dword_count; i++, data_buf++)
- dwc2_writel(hsotg, *data_buf, HCFIFO(chan->hc_num));
- } else {
- /* xfer_buf is not DWORD aligned */
- for (i = 0; i < dword_count; i++, data_buf++) {
- u32 data = data_buf[0] | data_buf[1] << 8 |
- data_buf[2] << 16 | data_buf[3] << 24;
- dwc2_writel(hsotg, data, HCFIFO(chan->hc_num));
- }
- }
+ dwc2_writel_rep(hsotg, HCFIFO(chan->hc_num), data_buf, byte_count);
chan->xfer_count += byte_count;
chan->xfer_buf += byte_count;