usb: cdnsp: add support for eUSB2v2 port

The Cadence CDNSP controller optionally supports eUSB2 (embedded USB2)
port. While this port type operates logically like high-speed USB 2.0,
it utilizes a different physical layer signaling.

This patch:
- Extends the port detection logic to recognize the eUSB2 protocol.
- Tracks the eUSB2 port offset in the cdnsp_device structure.
- Ensures that eUSB2 ports are correctly handled during Link State
  transitions, specifically forcing L0 when LPM is capable, similar
  to standard USB 2.0 ports.

Signed-off-by: Pawel Laszczak <pawell@cadence.com>
Acked-by: Peter Chen <peter.chen@kernel.org>
Link: https://patch.msgid.link/20260420-eusb2v2_upstream-v2-1-9883645e2ede@cadence.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Pawel Laszczak
2026-04-20 12:23:57 +02:00
committed by Greg Kroah-Hartman
parent 9a71ac5089
commit e68fddb47a
4 changed files with 90 additions and 42 deletions

View File

@@ -124,20 +124,28 @@ void cdnsp_set_link_state(struct cdnsp_device *pdev,
}
static void cdnsp_disable_port(struct cdnsp_device *pdev,
__le32 __iomem *port_regs)
struct cdnsp_port *port)
{
u32 temp = cdnsp_port_state_to_neutral(readl(port_regs));
u32 temp;
writel(temp | PORT_PED, port_regs);
if (!port->exist)
return;
temp = cdnsp_port_state_to_neutral(readl(&port->regs->portsc));
writel(temp | PORT_PED, &port->regs->portsc);
}
static void cdnsp_clear_port_change_bit(struct cdnsp_device *pdev,
__le32 __iomem *port_regs)
struct cdnsp_port *port)
{
u32 portsc = readl(port_regs);
u32 portsc;
if (!port->exist)
return;
portsc = readl(&port->regs->portsc);
writel(cdnsp_port_state_to_neutral(portsc) |
(portsc & PORT_CHANGE_BITS), port_regs);
(portsc & PORT_CHANGE_BITS), &port->regs->portsc);
}
static void cdnsp_set_apb_timeout_value(struct cdnsp_device *pdev)
@@ -944,7 +952,7 @@ void cdnsp_set_usb2_hardware_lpm(struct cdnsp_device *pdev,
struct usb_request *req,
int enable)
{
if (pdev->active_port != &pdev->usb2_port || !pdev->gadget.lpm_capable)
if (pdev->active_port == &pdev->usb3_port || !pdev->gadget.lpm_capable)
return;
trace_cdnsp_lpm(enable);
@@ -1310,20 +1318,26 @@ static int cdnsp_run(struct cdnsp_device *pdev,
break;
}
if (speed >= USB_SPEED_SUPER) {
if (pdev->usb3_port.exist && speed >= USB_SPEED_SUPER) {
writel(temp, &pdev->port3x_regs->mode_addr);
cdnsp_set_link_state(pdev, &pdev->usb3_port.regs->portsc,
XDEV_RXDETECT);
} else {
cdnsp_disable_port(pdev, &pdev->usb3_port.regs->portsc);
cdnsp_disable_port(pdev, &pdev->usb3_port);
}
cdnsp_set_link_state(pdev, &pdev->usb2_port.regs->portsc,
XDEV_RXDETECT);
if (pdev->usb2_port.exist) {
cdnsp_set_link_state(pdev, &pdev->usb2_port.regs->portsc,
XDEV_RXDETECT);
writel(PORT_REG6_L1_L0_HW_EN | fs_speed, &pdev->port20_regs->port_reg6);
}
if (pdev->eusb_port.exist)
cdnsp_set_link_state(pdev, &pdev->eusb_port.regs->portsc,
XDEV_RXDETECT);
cdnsp_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
writel(PORT_REG6_L1_L0_HW_EN | fs_speed, &pdev->port20_regs->port_reg6);
ret = cdnsp_start(pdev);
if (ret) {
@@ -1469,8 +1483,10 @@ static void cdnsp_stop(struct cdnsp_device *pdev)
cdnsp_ep_dequeue(&pdev->eps[0], req);
}
cdnsp_disable_port(pdev, &pdev->usb2_port.regs->portsc);
cdnsp_disable_port(pdev, &pdev->usb3_port.regs->portsc);
cdnsp_disable_port(pdev, &pdev->usb2_port);
cdnsp_disable_port(pdev, &pdev->usb3_port);
cdnsp_disable_port(pdev, &pdev->eusb_port);
cdnsp_disable_slot(pdev);
cdnsp_halt(pdev);
@@ -1479,8 +1495,9 @@ static void cdnsp_stop(struct cdnsp_device *pdev)
temp = readl(&pdev->ir_set->irq_pending);
writel(IMAN_IE_CLEAR(temp), &pdev->ir_set->irq_pending);
cdnsp_clear_port_change_bit(pdev, &pdev->usb2_port.regs->portsc);
cdnsp_clear_port_change_bit(pdev, &pdev->usb3_port.regs->portsc);
cdnsp_clear_port_change_bit(pdev, &pdev->usb2_port);
cdnsp_clear_port_change_bit(pdev, &pdev->eusb_port);
cdnsp_clear_port_change_bit(pdev, &pdev->usb3_port);
/* Clear interrupt line */
temp = readl(&pdev->ir_set->irq_pending);

View File

@@ -1474,6 +1474,7 @@ struct cdnsp_device {
unsigned int link_state;
struct cdnsp_port usb2_port;
struct cdnsp_port eusb_port;
struct cdnsp_port usb3_port;
struct cdnsp_port *active_port;
u16 test_mode;

View File

@@ -1088,11 +1088,9 @@ void cdnsp_mem_cleanup(struct cdnsp_device *pdev)
pdev->dcbaa, pdev->dcbaa->dma);
pdev->dcbaa = NULL;
pdev->usb2_port.exist = 0;
pdev->usb3_port.exist = 0;
pdev->usb2_port.port_num = 0;
pdev->usb3_port.port_num = 0;
memset(&pdev->usb2_port, 0, sizeof(struct cdnsp_port));
memset(&pdev->eusb_port, 0, sizeof(struct cdnsp_port));
memset(&pdev->usb3_port, 0, sizeof(struct cdnsp_port));
pdev->active_port = NULL;
}
@@ -1133,6 +1131,18 @@ static void cdnsp_add_in_port(struct cdnsp_device *pdev,
port_offset = CDNSP_EXT_PORT_OFF(temp);
port_count = CDNSP_EXT_PORT_COUNT(temp);
if (port == &pdev->eusb_port) {
/*
* If controller has usb2 + eusb port then eusb is as
* second port
*/
if (port_count == 2)
port_offset++;
if (port_count == 1 && pdev->usb2_port.exist)
return;
}
trace_cdnsp_port_info(addr, port_offset, port_count, port->maj_rev);
port->port_num = port_offset;
@@ -1152,13 +1162,10 @@ static int cdnsp_setup_port_arrays(struct cdnsp_device *pdev)
base = &pdev->cap_regs->hc_capbase;
offset = cdnsp_find_next_ext_cap(base, 0,
EXT_CAP_CFG_DEV_20PORT_CAP_ID);
pdev->port20_regs = base + offset;
offset = cdnsp_find_next_ext_cap(base, 0, D_XEC_CFG_3XPORT_CAP);
pdev->port3x_regs = base + offset;
if (offset)
pdev->port20_regs = base + offset;
offset = 0;
base = &pdev->cap_regs->hc_capbase;
/* Driver expects max 2 extended protocol capability. */
for (i = 0; i < 2; i++) {
@@ -1173,26 +1180,46 @@ static int cdnsp_setup_port_arrays(struct cdnsp_device *pdev)
cdnsp_add_in_port(pdev, &pdev->usb3_port,
base + offset);
if (CDNSP_EXT_PORT_MAJOR(temp) == 0x02 &&
!pdev->usb2_port.port_num)
cdnsp_add_in_port(pdev, &pdev->usb2_port,
base + offset);
if (CDNSP_EXT_PORT_MAJOR(temp) == 0x02) {
if (!pdev->usb2_port.port_num && pdev->port20_regs)
cdnsp_add_in_port(pdev, &pdev->usb2_port,
base + offset);
if (!pdev->eusb_port.port_num)
cdnsp_add_in_port(pdev, &pdev->eusb_port,
base + offset);
}
}
if (!pdev->usb2_port.exist || !pdev->usb3_port.exist) {
dev_err(pdev->dev, "Error: Only one port detected\n");
if (!pdev->usb2_port.exist && !pdev->eusb_port.exist &&
!pdev->usb3_port.exist) {
dev_err(pdev->dev, "Error: No port detected\n");
return -ENODEV;
}
trace_cdnsp_init("Found USB 2.0 ports and USB 3.0 ports.");
if (pdev->usb2_port.exist) {
pdev->usb2_port.regs = (struct cdnsp_port_regs __iomem *)
(&pdev->op_regs->port_reg_base + NUM_PORT_REGS *
(pdev->usb2_port.port_num - 1));
trace_cdnsp_init("Found USB 2.0 port.");
}
pdev->usb2_port.regs = (struct cdnsp_port_regs __iomem *)
(&pdev->op_regs->port_reg_base + NUM_PORT_REGS *
(pdev->usb2_port.port_num - 1));
if (pdev->eusb_port.exist) {
pdev->eusb_port.regs = (struct cdnsp_port_regs __iomem *)
(&pdev->op_regs->port_reg_base + NUM_PORT_REGS *
(pdev->eusb_port.port_num - 1));
trace_cdnsp_init("Found eUSB 2.0 port.");
}
pdev->usb3_port.regs = (struct cdnsp_port_regs __iomem *)
(&pdev->op_regs->port_reg_base + NUM_PORT_REGS *
(pdev->usb3_port.port_num - 1));
if (pdev->usb3_port.exist) {
offset = cdnsp_find_next_ext_cap(base, 0, D_XEC_CFG_3XPORT_CAP);
pdev->port3x_regs = base + offset;
pdev->usb3_port.regs = (struct cdnsp_port_regs __iomem *)
(&pdev->op_regs->port_reg_base + NUM_PORT_REGS *
(pdev->usb3_port.port_num - 1));
trace_cdnsp_init("Found USB 3.x port.");
}
return 0;
}

View File

@@ -259,7 +259,7 @@ static bool cdnsp_room_on_ring(struct cdnsp_device *pdev,
*/
static void cdnsp_force_l0_go(struct cdnsp_device *pdev)
{
if (pdev->active_port == &pdev->usb2_port && pdev->gadget.lpm_capable)
if (pdev->active_port != &pdev->usb3_port && pdev->gadget.lpm_capable)
cdnsp_set_link_state(pdev, &pdev->active_port->regs->portsc, XDEV_U0);
}
@@ -763,6 +763,8 @@ static int cdnsp_update_port_id(struct cdnsp_device *pdev, u32 port_id)
if (port_id == pdev->usb2_port.port_num) {
port = &pdev->usb2_port;
} else if (port_id == pdev->eusb_port.port_num) {
port = &pdev->eusb_port;
} else if (port_id == pdev->usb3_port.port_num) {
port = &pdev->usb3_port;
} else {
@@ -779,7 +781,8 @@ static int cdnsp_update_port_id(struct cdnsp_device *pdev, u32 port_id)
cdnsp_enable_slot(pdev);
}
if (port_id == pdev->usb2_port.port_num)
if ((pdev->usb2_port.exist && port_id == pdev->usb2_port.port_num) ||
(pdev->eusb_port.exist && port_id == pdev->eusb_port.port_num))
cdnsp_set_usb2_hardware_lpm(pdev, NULL, 1);
else
writel(PORT_U1_TIMEOUT(1) | PORT_U2_TIMEOUT(1),
@@ -808,7 +811,7 @@ static void cdnsp_handle_port_status(struct cdnsp_device *pdev,
port_regs = pdev->active_port->regs;
if (port_id == pdev->usb2_port.port_num)
if (port_id == pdev->usb2_port.port_num || port_id == pdev->eusb_port.port_num)
port2 = true;
new_event: