[RFC 7/9] usb: ehci: add big-endian registers support
Antony Pavlov
antonynpavlov at gmail.com
Thu Aug 27 15:24:08 PDT 2015
Signed-off-by: Antony Pavlov <antonynpavlov at gmail.com>
---
drivers/usb/host/Kconfig | 3 +
drivers/usb/host/ehci-hcd.c | 133 ++++++++++++++++++++++----------------------
drivers/usb/host/ehci.h | 42 +++++++++++++-
include/usb/ehci.h | 1 +
4 files changed, 108 insertions(+), 71 deletions(-)
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 54eaf46..23dcf9c 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -2,6 +2,9 @@ config USB_EHCI
bool "EHCI driver"
depends on HAS_DMA
+config USB_EHCI_BIG_ENDIAN_MMIO
+ bool
+
config USB_EHCI_OMAP
depends on ARCH_OMAP3
depends on USB_TWL4030
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 49af642..dd5f503 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -34,22 +34,6 @@
#include "ehci.h"
-struct ehci_priv {
- int rootdev;
- struct device_d *dev;
- struct ehci_hccr *hccr;
- struct ehci_hcor *hcor;
- struct usb_host host;
- struct QH *qh_list;
- struct qTD *td;
- int portreset;
- unsigned long flags;
-
- int (*init)(void *drvdata);
- int (*post_init)(void *drvdata);
- void *drvdata;
-};
-
#define to_ehci(ptr) container_of(ptr, struct ehci_priv, host)
#define NUM_QH 2
@@ -123,7 +107,8 @@ static struct descriptor {
#define ehci_is_TDI() (ehci->flags & EHCI_HAS_TT)
-static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec)
+static int handshake(const struct ehci_priv *ehci,
+ uint32_t *ptr, uint32_t mask, uint32_t done, int usec)
{
uint32_t result;
uint64_t start;
@@ -131,7 +116,7 @@ static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec)
start = get_time_ns();
while (1) {
- result = ehci_readl(ptr);
+ result = ehci_readl(ehci, ptr);
if (result == ~(uint32_t)0)
return -1;
result &= mask;
@@ -149,10 +134,11 @@ static int ehci_reset(struct ehci_priv *ehci)
uint32_t *reg_ptr;
int ret = 0;
- cmd = ehci_readl(&ehci->hcor->or_usbcmd);
+ cmd = ehci_readl(ehci, &ehci->hcor->or_usbcmd);
cmd |= CMD_RESET;
- ehci_writel(&ehci->hcor->or_usbcmd, cmd);
- ret = handshake(&ehci->hcor->or_usbcmd, CMD_RESET, 0, 250 * 1000);
+ ehci_writel(ehci, &ehci->hcor->or_usbcmd, cmd);
+ ret = handshake(ehci, &ehci->hcor->or_usbcmd, CMD_RESET, 0,
+ 250 * 1000);
if (ret < 0) {
dev_err(ehci->dev, "fail to reset\n");
goto out;
@@ -160,9 +146,14 @@ static int ehci_reset(struct ehci_priv *ehci)
if (ehci_is_TDI()) {
reg_ptr = (uint32_t *)((u8 *)ehci->hcor + USBMODE);
- tmp = ehci_readl(reg_ptr);
+ tmp = ehci_readl(ehci, reg_ptr);
tmp |= USBMODE_CM_HC;
- ehci_writel(reg_ptr, tmp);
+ /* FIXME: does not work on AR9331 yet */
+ /*
+ if (ehci_big_endian_mmio(ehci))
+ tmp |= USBMODE_BE;
+ */
+ ehci_writel(ehci, reg_ptr, tmp);
}
out:
return ret;
@@ -333,15 +324,16 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
}
}
- usbsts = ehci_readl(&ehci->hcor->or_usbsts);
- ehci_writel(&ehci->hcor->or_usbsts, (usbsts & 0x3f));
+ usbsts = ehci_readl(ehci, &ehci->hcor->or_usbsts);
+ ehci_writel(ehci, &ehci->hcor->or_usbsts, (usbsts & 0x3f));
/* Enable async. schedule. */
- cmd = ehci_readl(&ehci->hcor->or_usbcmd);
+ cmd = ehci_readl(ehci, &ehci->hcor->or_usbcmd);
cmd |= CMD_ASE;
- ehci_writel(&ehci->hcor->or_usbcmd, cmd);
+ ehci_writel(ehci, &ehci->hcor->or_usbcmd, cmd);
- ret = handshake(&ehci->hcor->or_usbsts, STD_ASS, STD_ASS, 100 * 1000);
+ ret = handshake(ehci, &ehci->hcor->or_usbsts, STD_ASS, STD_ASS,
+ 100 * 1000);
if (ret < 0) {
dev_err(ehci->dev, "fail timeout STD_ASS set\n");
goto fail;
@@ -355,12 +347,13 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
token = hc32_to_cpu(vtd->qt_token);
if (is_timeout(start, timeout_val)) {
/* Disable async schedule. */
- cmd = ehci_readl(&ehci->hcor->or_usbcmd);
+ cmd = ehci_readl(ehci, &ehci->hcor->or_usbcmd);
cmd &= ~CMD_ASE;
- ehci_writel(&ehci->hcor->or_usbcmd, cmd);
+ ehci_writel(ehci, &ehci->hcor->or_usbcmd, cmd);
- ret = handshake(&ehci->hcor->or_usbsts, STD_ASS, 0, 100 * 1000);
- ehci_writel(&qh->qt_token, 0);
+ ret = handshake(ehci, &ehci->hcor->or_usbsts,
+ STD_ASS, 0, 100 * 1000);
+ ehci_writel(ehci, &qh->qt_token, 0);
return -ETIMEDOUT;
}
} while (token & 0x80);
@@ -376,11 +369,11 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
}
/* Disable async schedule. */
- cmd = ehci_readl(&ehci->hcor->or_usbcmd);
+ cmd = ehci_readl(ehci, &ehci->hcor->or_usbcmd);
cmd &= ~CMD_ASE;
- ehci_writel(&ehci->hcor->or_usbcmd, cmd);
+ ehci_writel(ehci, &ehci->hcor->or_usbcmd, cmd);
- ret = handshake(&ehci->hcor->or_usbsts, STD_ASS, 0,
+ ret = handshake(ehci, &ehci->hcor->or_usbsts, STD_ASS, 0,
100 * 1000);
if (ret < 0) {
dev_err(ehci->dev, "fail timeout STD_ASS reset\n");
@@ -420,9 +413,9 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
} else {
dev->act_len = 0;
dev_dbg(ehci->dev, "dev=%u, usbsts=%#x, p[1]=%#x, p[2]=%#x\n",
- dev->devnum, ehci_readl(&ehci->hcor->or_usbsts),
- ehci_readl(&ehci->hcor->or_portsc[0]),
- ehci_readl(&ehci->hcor->or_portsc[1]));
+ dev->devnum, ehci_readl(ehci, &ehci->hcor->or_usbsts),
+ ehci_readl(ehci, &ehci->hcor->or_portsc[0]),
+ ehci_readl(ehci, &ehci->hcor->or_portsc[1]));
}
return (dev->status != USB_ST_NOT_PROC) ? 0 : -1;
@@ -567,7 +560,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
break;
case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8):
memset(tmpbuf, 0, 4);
- reg = ehci_readl(status_reg);
+ reg = ehci_readl(ehci, status_reg);
if (reg & EHCI_PS_CS)
tmpbuf[0] |= USB_PORT_STAT_CONNECTION;
if (reg & EHCI_PS_PE)
@@ -581,8 +574,9 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
int ret;
/* force reset to complete */
reg = reg & ~(EHCI_PS_PR | EHCI_PS_CLEAR);
- ehci_writel(status_reg, reg);
- ret = handshake(status_reg, EHCI_PS_PR, 0, 2 * 1000);
+ ehci_writel(ehci, status_reg, reg);
+ ret = handshake(ehci, status_reg, EHCI_PS_PR, 0,
+ 2 * 1000);
if (!ret)
tmpbuf[0] |= USB_PORT_STAT_RESET;
else
@@ -621,17 +615,17 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
srclen = 4;
break;
case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8):
- reg = ehci_readl(status_reg);
+ reg = ehci_readl(ehci, status_reg);
reg &= ~EHCI_PS_CLEAR;
switch (le16_to_cpu(req->value)) {
case USB_PORT_FEAT_ENABLE:
reg |= EHCI_PS_PE;
- ehci_writel(status_reg, reg);
+ ehci_writel(ehci, status_reg, reg);
break;
case USB_PORT_FEAT_POWER:
- if (HCS_PPC(ehci_readl(&ehci->hccr->cr_hcsparams))) {
+ if (HCS_PPC(ehci_readl(ehci, &ehci->hccr->cr_hcsparams))) {
reg |= EHCI_PS_PP;
- ehci_writel(status_reg, reg);
+ ehci_writel(ehci, status_reg, reg);
}
break;
case USB_PORT_FEAT_RESET:
@@ -642,14 +636,14 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
dev_dbg(ehci->dev, "port %d low speed --> companion\n",
port - 1);
reg |= EHCI_PS_PO;
- ehci_writel(status_reg, reg);
+ ehci_writel(ehci, status_reg, reg);
break;
} else {
int ret;
reg |= EHCI_PS_PR;
reg &= ~EHCI_PS_PE;
- ehci_writel(status_reg, reg);
+ ehci_writel(ehci, status_reg, reg);
/*
* caller must wait, then call GetPortStatus
* usb 2.0 specification say 50 ms resets on
@@ -659,13 +653,13 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
mdelay(50);
ehci->portreset |= 1 << port;
/* terminate the reset */
- ehci_writel(status_reg, reg & ~EHCI_PS_PR);
+ ehci_writel(ehci, status_reg, reg & ~EHCI_PS_PR);
/*
* A host controller must terminate the reset
* and stabilize the state of the port within
* 2 milliseconds
*/
- ret = handshake(status_reg, EHCI_PS_PR, 0,
+ ret = handshake(ehci, status_reg, EHCI_PS_PR, 0,
2 * 1000);
if (!ret)
ehci->portreset |=
@@ -681,10 +675,10 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
goto unknown;
}
/* unblock posted writes */
- (void) ehci_readl(&ehci->hcor->or_usbcmd);
+ (void) ehci_readl(ehci, &ehci->hcor->or_usbcmd);
break;
case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8):
- reg = ehci_readl(status_reg);
+ reg = ehci_readl(ehci, status_reg);
reg &= ~EHCI_PS_CLEAR;
switch (le16_to_cpu(req->value)) {
case USB_PORT_FEAT_ENABLE:
@@ -694,7 +688,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
reg |= EHCI_PS_PEC;
break;
case USB_PORT_FEAT_POWER:
- if (HCS_PPC(ehci_readl(&ehci->hccr->cr_hcsparams)))
+ if (HCS_PPC(ehci_readl(ehci, &ehci->hccr->cr_hcsparams)))
reg &= ~ EHCI_PS_PP;
break;
case USB_PORT_FEAT_C_CONNECTION:
@@ -710,9 +704,9 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
dev_dbg(ehci->dev, "unknown feature %x\n", le16_to_cpu(req->value));
goto unknown;
}
- ehci_writel(status_reg, reg);
+ ehci_writel(ehci, status_reg, reg);
/* unblock posted write */
- (void) ehci_readl(&ehci->hcor->or_usbcmd);
+ (void) ehci_readl(ehci, &ehci->hcor->or_usbcmd);
break;
default:
dev_dbg(ehci->dev, "Unknown request\n");
@@ -743,19 +737,19 @@ unknown:
/* force HC to halt state from unknown (EHCI spec section 2.3) */
static int ehci_halt(struct ehci_priv *ehci)
{
- u32 temp = ehci_readl(&ehci->hcor->or_usbsts);
+ u32 temp = ehci_readl(ehci, &ehci->hcor->or_usbsts);
/* disable any irqs left enabled by previous code */
- ehci_writel(&ehci->hcor->or_usbintr, 0);
+ ehci_writel(ehci, &ehci->hcor->or_usbintr, 0);
if (temp & STS_HALT)
return 0;
- temp = ehci_readl(&ehci->hcor->or_usbcmd);
+ temp = ehci_readl(ehci, &ehci->hcor->or_usbcmd);
temp &= ~CMD_RUN;
- ehci_writel(&ehci->hcor->or_usbcmd, temp);
+ ehci_writel(ehci, &ehci->hcor->or_usbcmd, temp);
- return handshake(&ehci->hcor->or_usbsts,
+ return handshake(ehci, &ehci->hcor->or_usbsts,
STS_HALT, STS_HALT, 16 * 125);
}
@@ -788,10 +782,10 @@ static int ehci_init(struct usb_host *host)
ehci->qh_list->qt_token = cpu_to_hc32(0x40);
/* Set async. queue head pointer. */
- ehci_writel(&ehci->hcor->or_asynclistaddr,
+ ehci_writel(ehci, &ehci->hcor->or_asynclistaddr,
(uint32_t)virt_to_phys(ehci->qh_list));
- reg = ehci_readl(&ehci->hccr->cr_hcsparams);
+ reg = ehci_readl(ehci, &ehci->hccr->cr_hcsparams);
descriptor.hub.bNbrPorts = HCS_N_PORTS(reg);
/* Port Indicators */
@@ -802,21 +796,21 @@ static int ehci_init(struct usb_host *host)
descriptor.hub.wHubCharacteristics |= 0x01;
/* Start the host controller. */
- cmd = ehci_readl(&ehci->hcor->or_usbcmd);
+ cmd = ehci_readl(ehci, &ehci->hcor->or_usbcmd);
/*
* Philips, Intel, and maybe others need CMD_RUN before the
* root hub will detect new devices (why?); NEC doesn't
*/
cmd &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET);
cmd |= CMD_RUN;
- ehci_writel(&ehci->hcor->or_usbcmd, cmd);
+ ehci_writel(ehci, &ehci->hcor->or_usbcmd, cmd);
/* take control over the ports */
- cmd = ehci_readl(&ehci->hcor->or_configflag);
+ cmd = ehci_readl(ehci, &ehci->hcor->or_configflag);
cmd |= FLAG_CF;
- ehci_writel(&ehci->hcor->or_configflag, cmd);
+ ehci_writel(ehci, &ehci->hcor->or_configflag, cmd);
/* unblock posted write */
- cmd = ehci_readl(&ehci->hcor->or_usbcmd);
+ cmd = ehci_readl(ehci, &ehci->hcor->or_usbcmd);
mdelay(5);
ehci->rootdev = 0;
@@ -890,6 +884,9 @@ int ehci_register(struct device_d *dev, struct ehci_data *data)
host = &ehci->host;
dev->priv = ehci;
ehci->flags = data->flags;
+ if (data->flags & EHCI_BE_MMIO)
+ ehci->big_endian_mmio = 1;
+
ehci->hccr = data->hccr;
ehci->dev = dev;
@@ -897,7 +894,7 @@ int ehci_register(struct device_d *dev, struct ehci_data *data)
ehci->hcor = data->hcor;
else
ehci->hcor = (void __iomem *)ehci->hccr +
- HC_LENGTH(ehci_readl(&ehci->hccr->cr_capbase));
+ HC_LENGTH(ehci_readl(ehci, &ehci->hccr->cr_capbase));
ehci->drvdata = data->drvdata;
ehci->init = data->init;
@@ -922,7 +919,7 @@ int ehci_register(struct device_d *dev, struct ehci_data *data)
usb_register_host(host);
- reg = HC_VERSION(ehci_readl(&ehci->hccr->cr_capbase));
+ reg = HC_VERSION(ehci_readl(ehci, &ehci->hccr->cr_capbase));
dev_info(dev, "USB EHCI %x.%02x\n", reg >> 8, reg & 0xff);
return 0;
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index bda669b..e62aca8 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -70,13 +70,49 @@ struct ehci_hcor {
#define USBMODE_CM_HC (3 << 0) /* host controller mode */
#define USBMODE_CM_IDLE (0 << 0) /* idle state */
-static inline void ehci_writel(__u32 __iomem *regs, const unsigned int val)
+struct ehci_priv {
+ int rootdev;
+ struct device_d *dev;
+ struct ehci_hccr *hccr;
+ struct ehci_hcor *hcor;
+ struct usb_host host;
+ struct QH *qh_list;
+ struct qTD *td;
+ int portreset;
+ unsigned long flags;
+ unsigned big_endian_mmio:1;
+
+ int (*init)(void *drvdata);
+ int (*post_init)(void *drvdata);
+ void *drvdata;
+};
+
+#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO
+#define ehci_big_endian_mmio(e) ((e)->big_endian_mmio)
+#else
+#define ehci_big_endian_mmio(e) 0
+#endif
+
+static inline void ehci_writel(const struct ehci_priv *ehci,
+ __u32 __iomem *regs, const unsigned int val)
{
- writel(val, regs);
+ if (IS_ENABLED(CONFIG_USB_EHCI_BIG_ENDIAN_MMIO)) {
+ ehci_big_endian_mmio(ehci) ?
+ iowrite32be(val, regs) :
+ writel(val, regs);
+ } else
+ writel(val, regs);
}
-static inline unsigned int ehci_readl(__u32 __iomem *regs)
+static inline unsigned int ehci_readl(const struct ehci_priv *ehci,
+ __u32 __iomem *regs)
{
+ if (IS_ENABLED(CONFIG_USB_EHCI_BIG_ENDIAN_MMIO)) {
+ return ehci_big_endian_mmio(ehci) ?
+ ioread32be(regs) :
+ readl(regs);
+ }
+
return readl(regs);
}
diff --git a/include/usb/ehci.h b/include/usb/ehci.h
index 93f980d..0089a0c 100644
--- a/include/usb/ehci.h
+++ b/include/usb/ehci.h
@@ -2,6 +2,7 @@
#define __USB_EHCI_H
#define EHCI_HAS_TT (1 << 0)
+#define EHCI_BE_MMIO (1 << 1)
struct ehci_platform_data {
unsigned long flags;
--
2.5.0
More information about the barebox
mailing list