[PATCH v2 1/5] usb: ehci: support non-standard port status registers
From: Linus Walleij
Date: Wed Sep 02 2026 - 02:12:59 EST
Some EHCI implementations place the port status registers outside the
standard operational register layout.
Add a per-controller port status base and use a helper for all PORTSC
accesses, while preserving the standard location by default.
Suggested-by: Daniel Palmer <daniel@xxxxxxxxx>
Assisted-by: LLM
Signed-off-by: Linus Walleij <linusw@xxxxxxxxxx>
---
drivers/usb/host/ehci-dbg.c | 3 +--
drivers/usb/host/ehci-hcd.c | 7 +++----
drivers/usb/host/ehci-hub.c | 35 +++++++++++++++++------------------
drivers/usb/host/ehci.h | 12 ++++++++++++
4 files changed, 33 insertions(+), 24 deletions(-)
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
index 3cf743c81eb4..51ec6ea43158 100644
--- a/drivers/usb/host/ehci-dbg.c
+++ b/drivers/usb/host/ehci-dbg.c
@@ -869,8 +869,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
for (i = 1; i <= HCS_N_PORTS(ehci->hcs_params); i++) {
temp = dbg_port_buf(scratch, sizeof(scratch), label, i,
- ehci_readl(ehci,
- &ehci->regs->port_status[i - 1]));
+ ehci_readl(ehci, ehci_portsc(ehci, i - 1)));
temp = scnprintf(next, size, fmt, temp, scratch);
size -= temp;
next += temp;
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 3c46bb18c7f3..19cc7611d86d 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -325,7 +325,7 @@ static void ehci_turn_off_all_ports(struct ehci_hcd *ehci)
ehci_port_power(ehci, port, false);
spin_lock_irq(&ehci->lock);
ehci_writel(ehci, PORT_RWC_BITS,
- &ehci->regs->port_status[port]);
+ ehci_portsc(ehci, port));
}
}
@@ -811,8 +811,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
/* leverage per-port change bits feature */
if (!(ppcd & (1 << i)))
continue;
- pstatus = ehci_readl(ehci,
- &ehci->regs->port_status[i]);
+ pstatus = ehci_readl(ehci, ehci_portsc(ehci, i));
if (pstatus & PORT_OWNER)
continue;
@@ -1109,7 +1108,7 @@ static void ehci_remove_device(struct usb_hcd *hcd, struct usb_device *udev)
/* Clear wakeup signal locked in zhaoxin platform when device plug in. */
static void ehci_zx_wakeup_clear(struct ehci_hcd *ehci)
{
- u32 __iomem *reg = &ehci->regs->port_status[4];
+ u32 __iomem *reg = ehci_portsc(ehci, 4);
u32 t1 = ehci_readl(ehci, reg);
t1 &= (u32)~0xf0000;
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 1aee392e8492..77d0df41f45f 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -55,7 +55,7 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci)
port = HCS_N_PORTS(ehci->hcs_params);
while (port--) {
if (test_bit(port, &ehci->owned_ports)) {
- reg = &ehci->regs->port_status[port];
+ reg = ehci_portsc(ehci, port);
status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
if (!(status & PORT_POWER))
ehci_port_power(ehci, port, true);
@@ -69,7 +69,7 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci)
port = HCS_N_PORTS(ehci->hcs_params);
while (port--) {
if (test_bit(port, &ehci->owned_ports)) {
- reg = &ehci->regs->port_status[port];
+ reg = ehci_portsc(ehci, port);
status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
/* Port already owned by companion? */
@@ -106,7 +106,7 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci)
* but if something went wrong the port must not
* remain enabled.
*/
- reg = &ehci->regs->port_status[port];
+ reg = ehci_portsc(ehci, port);
status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
if (status & PORT_OWNER)
ehci_writel(ehci, status | PORT_CSC, reg);
@@ -137,7 +137,7 @@ static int ehci_port_change(struct ehci_hcd *ehci)
*/
while (i--)
- if (ehci_readl(ehci, &ehci->regs->port_status[i]) & PORT_CSC)
+ if (ehci_readl(ehci, ehci_portsc(ehci, i)) & PORT_CSC)
return 1;
return 0;
@@ -175,7 +175,7 @@ void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
port = HCS_N_PORTS(ehci->hcs_params);
while (port--) {
- u32 __iomem *reg = &ehci->regs->port_status[port];
+ u32 __iomem *reg = ehci_portsc(ehci, port);
u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
u32 t2 = t1 & ~PORT_WAKE_BITS;
@@ -254,7 +254,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
fs_idle_delay = false;
port = HCS_N_PORTS(ehci->hcs_params);
while (port--) {
- u32 __iomem *reg = &ehci->regs->port_status [port];
+ u32 __iomem *reg = ehci_portsc(ehci, port);
u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
u32 t2 = t1 & ~PORT_WAKE_BITS;
@@ -426,7 +426,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
*/
i = HCS_N_PORTS(ehci->hcs_params);
while (i--) {
- temp = ehci_readl(ehci, &ehci->regs->port_status[i]);
+ temp = ehci_readl(ehci, ehci_portsc(ehci, i));
if ((temp & PORT_PE) &&
!(temp & (PORT_SUSPEND | PORT_RESUME))) {
ehci_dbg(ehci, "Port status(0x%x) is wrong\n", temp);
@@ -463,14 +463,14 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
/* manually resume the ports we suspended during bus_suspend() */
i = HCS_N_PORTS (ehci->hcs_params);
while (i--) {
- temp = ehci_readl(ehci, &ehci->regs->port_status [i]);
+ temp = ehci_readl(ehci, ehci_portsc(ehci, i));
temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
if (test_bit(i, &ehci->bus_suspended) &&
(temp & PORT_SUSPEND)) {
temp |= PORT_RESUME;
set_bit(i, &resume_needed);
}
- ehci_writel(ehci, temp, &ehci->regs->port_status [i]);
+ ehci_writel(ehci, temp, ehci_portsc(ehci, i));
}
/*
@@ -487,10 +487,10 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
i = HCS_N_PORTS (ehci->hcs_params);
while (i--) {
- temp = ehci_readl(ehci, &ehci->regs->port_status [i]);
+ temp = ehci_readl(ehci, ehci_portsc(ehci, i));
if (test_bit(i, &resume_needed)) {
temp &= ~(PORT_RWC_BITS | PORT_SUSPEND | PORT_RESUME);
- ehci_writel(ehci, temp, &ehci->regs->port_status [i]);
+ ehci_writel(ehci, temp, ehci_portsc(ehci, i));
}
}
@@ -540,7 +540,7 @@ static void set_owner(struct ehci_hcd *ehci, int portnum, int new_owner)
u32 port_status;
int try;
- status_reg = &ehci->regs->port_status[portnum];
+ status_reg = ehci_portsc(ehci, portnum);
/*
* The controller won't set the OWNER bit if the port is
@@ -661,7 +661,7 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
for (i = 0; i < ports; i++) {
/* leverage per-port change bits feature */
if (ppcd & (1 << i))
- temp = ehci_readl(ehci, &ehci->regs->port_status[i]);
+ temp = ehci_readl(ehci, ehci_portsc(ehci, i));
else
temp = 0;
@@ -753,7 +753,7 @@ int ehci_hub_control(
temp = (wIndex - 1) & 0xff;
if (temp >= HCS_N_PORTS_MAX)
temp = 0;
- status_reg = &ehci->regs->port_status[temp];
+ status_reg = ehci_portsc(ehci, temp);
hostpc_reg = &ehci->regs->hostpc[temp];
/*
@@ -1149,8 +1149,7 @@ int ehci_hub_control(
/* Put all enabled ports into suspend */
while (ports--) {
- u32 __iomem *sreg =
- &ehci->regs->port_status[ports];
+ u32 __iomem *sreg = ehci_portsc(ehci, ports);
temp = ehci_readl(ehci, sreg) & ~PORT_RWC_BITS;
if (temp & PORT_PE)
@@ -1200,14 +1199,14 @@ static int ehci_port_handed_over(struct usb_hcd *hcd, int portnum)
if (ehci_is_TDI(ehci))
return 0;
- reg = &ehci->regs->port_status[portnum - 1];
+ reg = ehci_portsc(ehci, portnum - 1);
return ehci_readl(ehci, reg) & PORT_OWNER;
}
static int ehci_port_power(struct ehci_hcd *ehci, int portnum, bool enable)
{
struct usb_hcd *hcd = ehci_to_hcd(ehci);
- u32 __iomem *status_reg = &ehci->regs->port_status[portnum];
+ u32 __iomem *status_reg = ehci_portsc(ehci, portnum);
u32 temp = ehci_readl(ehci, status_reg) & ~PORT_RWC_BITS;
if (enable)
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index d7a3c8d13f6b..765338c2eb88 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -123,6 +123,7 @@ struct ehci_hcd { /* one per controller */
struct ehci_caps __iomem *caps;
struct ehci_regs __iomem *regs;
struct ehci_dbg_port __iomem *debug;
+ u32 __iomem *port_status;
__u32 hcs_params; /* cached register copy */
spinlock_t lock;
@@ -278,6 +279,17 @@ static inline struct usb_hcd *ehci_to_hcd(struct ehci_hcd *ehci)
/*-------------------------------------------------------------------------*/
+static inline u32 __iomem *ehci_portsc(struct ehci_hcd *ehci,
+ unsigned int port)
+{
+ if (ehci->port_status)
+ return ehci->port_status + port;
+
+ return &ehci->regs->port_status[port];
+}
+
+/*-------------------------------------------------------------------------*/
+
#define QTD_NEXT(ehci, dma) cpu_to_hc32(ehci, (u32)dma)
/*
--
2.55.0