* [PATCH v4 0/3] usb: dwc3: xilinx: Add Versal2 MMI USB 3.2 controller support
@ 2026-10-20 11:26 Radhey Shyam Pandey
2026-10-20 11:26 ` [PATCH v4 1/3] dt-bindings: usb: amd,versal2-mmi-dwc3: Add MMI USB support on Versal Gen2 platform Radhey Shyam Pandey
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Radhey Shyam Pandey @ 2026-10-20 11:26 UTC (permalink / raw)
To: gregkh, robh, krzk+dt, conor+dt, Thinh.Nguyen, michal.simek, p.zabel
Cc: linux-usb, devicetree, linux-kernel, linux-arm-kernel,
Radhey Shyam Pandey
This series introduces support for the Multimedia Integrated (MMI) USB
3.2 Dual-Role Device (DRD) controller on Xilinx Versal2 platforms.
The controller supports SSP(10-Gbps), SuperSpeed, high-speed, full-speed
and low-speed operation modes.
The driver is required for the clock, reset and platform specific
initialization (coherency/TX_DEEMPH etc). In this initial version typec
reversibility is not implemented and it is assumed that USB3 PHY TCA mux
programming is done by MMI configuration data object (CDOs) and TI PD
controller is configured using external tiva programmer on VEK385
evaluation board.
This series depends on:
https://lore.kernel.org/all/20260928120015.3152461-1-radhey.shyam.pandey@amd.com [1]
https://lore.kernel.org/all/20260928134215.3194328-1-radhey.shyam.pandey@amd.com [2]
Changes for v4:
- Switch Versal Gen2 MMI to the flat probe model: embed struct dwc3 in the
glue private data and call dwc3_core_probe() directly, rather than
instantiating a child snps,dwc3 node.
- Binding: drop the wrapper parent address space and make reg required, as
the MMI integration has no glue register space of its own.
- Replace dwc3_xlnx_config with dwc3_xlnx_platdata {init, teardown, flat}
and split probe into common, flat and legacy paths. ZynqMP and Versal
behaviour is unchanged.
- Pass GSBUSCFG0.REQINFO and the Gen2 TX de-emphasis value through
struct dwc3_properties instead of software node properties.
- Acquire the USB3 PHY and reset in dwc3_xlnx_init_versal2(), undone by
devm_add_action_or_reset(); drop the explicit assert delay now that
phy_init() sits between assert and deassert.
Changes for v3:
- Return -ENODEV from probe if device_get_match_data() is NULL.
- Replace map_resource with no_mem_map (Versal2 MMI only; others default
MMIO map).
- Add PHY warm-reset assert delay.
- Loop all num_usb3_ports with one de-emphasis value.
- Set default as DWC3_LCSR_TX_DEEMPH_UNSPECIFIED default and only set
snps,lcsr-tx-deemph when not unspecified.
Changes for v2:
- DT binding: fix MHz spacing (SI convention), reorder description
before $ref in xlnx,usb-syscon, restore zynqmp-dwc3 example and add
versal2-mmi-dwc3 example, fix node name for no-reg case, use 1/1
address/size configuration and lowercase hex in syscon offsets.
- Split config struct refactoring (device_get_match_data,dwc3_xlnx_config)
into a separate preparatory patch.
- Fix error message capitalization to lowercase per kernel convention.
- Rename property snps,lcsr_tx_deemph to snps,lcsr-tx-deemph (hyphens).
- Fix double space in comment and missing blank line in core.h.
- Use platform data instead of of_device_is_compatible() check for
deemphasis support.
Link: https://lore.kernel.org/all/20260429173050.1772377-1-radhey.shyam.pandey@amd.com [v3]
Link: https://lore.kernel.org/all/20260330190304.1841593-1-radhey.shyam.pandey@amd.com [v2]
Link: https://lore.kernel.org/all/20251119193036.2666877-1-radhey.shyam.pandey@amd.com [v1]
Radhey Shyam Pandey (3):
dt-bindings: usb: amd,versal2-mmi-dwc3: Add MMI USB support on Versal
Gen2 platform
usb: dwc3: xilinx: Add Versal2 MMI USB 3.2 controller support with
flat probe model
usb: dwc3: xilinx: Add support to program MMI USB TX deemphasis
.../bindings/usb/amd,versal2-mmi-dwc3.yaml | 92 +++++
drivers/usb/dwc3/core.c | 12 +
drivers/usb/dwc3/core.h | 8 +
drivers/usb/dwc3/dwc3-generic-plat.c | 11 +-
drivers/usb/dwc3/dwc3-xilinx.c | 333 ++++++++++++++++--
drivers/usb/dwc3/glue.h | 4 +
6 files changed, 424 insertions(+), 36 deletions(-)
create mode 100644 Documentation/devicetree/bindings/usb/amd,versal2-mmi-dwc3.yaml
--
2.43.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v4 1/3] dt-bindings: usb: amd,versal2-mmi-dwc3: Add MMI USB support on Versal Gen2 platform
2026-10-20 11:26 [PATCH v4 0/3] usb: dwc3: xilinx: Add Versal2 MMI USB 3.2 controller support Radhey Shyam Pandey
@ 2026-10-20 11:26 ` Radhey Shyam Pandey
2026-10-20 18:17 ` Rob Herring (Arm)
2026-10-20 11:26 ` [PATCH v4 2/3] usb: dwc3: xilinx: Add Versal2 MMI USB 3.2 controller support with flat probe model Radhey Shyam Pandey
2026-10-20 11:26 ` [PATCH v4 3/3] usb: dwc3: xilinx: Add support to program MMI USB TX deemphasis Radhey Shyam Pandey
2 siblings, 1 reply; 6+ messages in thread
From: Radhey Shyam Pandey @ 2026-10-20 11:26 UTC (permalink / raw)
To: gregkh, robh, krzk+dt, conor+dt, Thinh.Nguyen, michal.simek, p.zabel
Cc: linux-usb, devicetree, linux-kernel, linux-arm-kernel,
Radhey Shyam Pandey
The Versal Gen 2 platform includes two distinct USB controllers. The
LPD controller is a USB 2.0 instance and reuses the existing
xlnx,versal-dwc3 binding. The second is the Multimedia Integrated (MMI)
USB 3.2 Gen 2x1 Dual-Role (DRD) controller, which natively supports USB
3.2 host and device operation over Type-C in a Gen 2x1 configuration
with a maximum data rate of 10 Gbps per direction.
Introduce a new compatible in <vendor>,<soc>-<subsystem>-<ip> format to
uniquely distinguish the MMI USB controller.
Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
---
Changes for v4:
- Switch to flat mode and rephrase commit description.
Changes for v3:
- Modify commit description to explain that there are two different
USB controllers and difference between versal2-dwc3 and
versal2-mmi-dwc3. Suggested by Krzysztof.
Changes for v2:
- Add blank line after compatible as suggested by Krzysztof.
- Retain the mmi suffix in the compatible string, as this USB 3.2 Gen2
IP from Synopsys is part of the dedicated Multimedia Interface. The
Versal Gen2 platform also includes a separate USB 2.0 controller,
and the mmi suffix uniquely distinguishes between the two USB
controllers. MMI is an independent subsystem particularly targeted for
deployment in Multi-Media related applications. The MMI block include
following submodules: UDH: USB3.2 Gen 2x1 Dual Role Device, DisplayPort
Transmit Controller, Security Module (ESM) for DisplayPort and HDMI
Controllers, DP AUX-I2C PHY.
- For MMI USB define parent address space i.e UDH block.
- Fix inconsistent MHz spacing to use SI convention with spaces.
- Move description before $ref and items in xlnx,usb-syscon property.
- Restore original zynqmp-dwc3 example, add new versal2-mmi-dwc3 example.
- Use 'usb' node name (without unit address) for versal2 example since
it has no reg property.
- Use 1/1 address/size configuration in versal2 example, use lowercase
hex in syscon offsets.
---
.../bindings/usb/amd,versal2-mmi-dwc3.yaml | 92 +++++++++++++++++++
1 file changed, 92 insertions(+)
create mode 100644 Documentation/devicetree/bindings/usb/amd,versal2-mmi-dwc3.yaml
diff --git a/Documentation/devicetree/bindings/usb/amd,versal2-mmi-dwc3.yaml b/Documentation/devicetree/bindings/usb/amd,versal2-mmi-dwc3.yaml
new file mode 100644
index 000000000000..f71fccd95b2a
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/amd,versal2-mmi-dwc3.yaml
@@ -0,0 +1,92 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/usb/amd,versal2-mmi-dwc3.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: AMD Versal Gen 2 MMI SuperSpeedPlus DWC3 USB controller
+
+maintainers:
+ - Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+
+description:
+ AMD Versal Gen 2 MMI (Multimedia Integrated) USB 3.2 controller.
+ The MMI subsystem integrates a DesignWare USB 3.2 host/device controller
+ with platform-specific clock, reset, and power domain management. It
+ supports USB 3.2 Gen 2x1 SuperSpeedPlus operation. SuperSpeedPlus
+ operation uses an external combo PHY shared with DisplayPort, and cable
+ orientation and role management are handled by a USB Type-C PD
+ controller.
+
+allOf:
+ - $ref: snps,dwc3-common.yaml#
+
+properties:
+ compatible:
+ const: amd,versal2-mmi-dwc3
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: SoC bus clock, has to be >= 156.25 MHz in SSP mode,
+ >= 125 MHz for SS operation and >= 60 MHz for HS operation.
+ - description: Reference clock, generates ITP when the USB2 PHY is
+ suspended.
+
+ clock-names:
+ items:
+ - const: bus_early
+ - const: ref
+
+ dma-coherent: true
+
+ interrupts:
+ maxItems: 1
+
+ phys:
+ minItems: 1
+ maxItems: 2
+
+ phy-names:
+ minItems: 1
+ maxItems: 2
+ items:
+ enum:
+ - usb2-phy
+ - usb3-phy
+
+ power-domains:
+ maxItems: 1
+
+ resets:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - interrupts
+ - power-domains
+ - resets
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ usb@edec0000 {
+ compatible = "amd,versal2-mmi-dwc3";
+ reg = <0xedec0000 0x10000>;
+ clocks = <&usb_bus_clk>, <&usb_ref_clk>;
+ clock-names = "bus_early", "ref";
+ dma-coherent;
+ dr_mode = "host";
+ interrupts = <GIC_SPI 191 IRQ_TYPE_LEVEL_HIGH>;
+ power-domains = <&scmi_devpd 0>;
+ resets = <&scmi_reset 0>;
+ };
--
2.43.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v4 2/3] usb: dwc3: xilinx: Add Versal2 MMI USB 3.2 controller support with flat probe model
2026-10-20 11:26 [PATCH v4 0/3] usb: dwc3: xilinx: Add Versal2 MMI USB 3.2 controller support Radhey Shyam Pandey
2026-10-20 11:26 ` [PATCH v4 1/3] dt-bindings: usb: amd,versal2-mmi-dwc3: Add MMI USB support on Versal Gen2 platform Radhey Shyam Pandey
@ 2026-10-20 11:26 ` Radhey Shyam Pandey
2026-11-04 1:44 ` Thinh Nguyen
2026-10-20 11:26 ` [PATCH v4 3/3] usb: dwc3: xilinx: Add support to program MMI USB TX deemphasis Radhey Shyam Pandey
2 siblings, 1 reply; 6+ messages in thread
From: Radhey Shyam Pandey @ 2026-10-20 11:26 UTC (permalink / raw)
To: gregkh, robh, krzk+dt, conor+dt, Thinh.Nguyen, michal.simek, p.zabel
Cc: linux-usb, devicetree, linux-kernel, linux-arm-kernel,
Radhey Shyam Pandey
The Versal Gen2 MMI USB 3.2 DRD controller integrates the Synopsys DWC3
core using a single flat amd,versal2-mmi-dwc3 DT node. Unlike ZynqMP and
Versal platforms, it has no wrapper register space and does not instantiate
a child snps,dwc3 device.
Add support for this integration model by introducing a flat probe path
that embeds struct dwc3 in the glue driver's private data and passes the
controller MMIO resource directly to dwc3_core_probe(). The core is
configured with ignore_clocks_and_resets, as clock and reset management
remain under glue driver control.
Rename the existing probe path to dwc3_xlnx_probe_legacy() and select
between legacy and flat modes through platform data, preserving existing
behaviour for ZynqMP and Versal platforms.
Introduce a flat platform flag to handle differences in probe, remove and
PM flows. The flat path invokes DWC3 core PM and teardown callbacks
directly, while the legacy path continues to use the child device model.
Add Versal Gen2-specific reset and PHY initialization, and pass the
platform specific GSBUSCFG0.REQINFO value through struct
dwc3_properties, the mechanism the flat model provides for core
properties supplied by the glue driver.
In this initial version Type-C reversibility is not implemented. USB3 PHY
TCA mux programming is assumed to be done by MMI configuration data
objects (CDOs), and the TI PD controller is configured using an external
Tiva programmer on the VEK385 evaluation board.
Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
---
Changes for v4:
- Switch to the flat probe model: embed struct dwc3 in struct dwc3_xlnx and
call dwc3_core_probe() directly, instead of the v3 wrapper that only
skipped the glue ioremap via no_mem_map.
- Replace dwc3_xlnx_config with dwc3_xlnx_platdata {init, teardown, flat} and
split probe into common, flat and legacy paths.
- Acquire the USB3 PHY and the mandatory (non-optional) reset in
dwc3_xlnx_init_versal2(), undone by a devm_add_action_or_reset() teardown;
drop the udelay() now that phy_init() sits between assert and deassert.
- Pass GSBUSCFG0.REQINFO through struct dwc3_properties rather than a
software node, and set ignore_clocks_and_resets.
Changes for v3:
- Remove mention of xlnx,usb-syscon phandle from version history.
- Rename map_resource to no_mem_map.
- Add assert delay.
- Rephrase commit description.
Changes for v2:
- Split config struct refactoring into separate patch (2/4).
- Remove unused regmap/syscon fields and parsing code; defer to
patch that first consumes them.
- Fix error message capitalization to lowercase ("reset", "deassert").
---
drivers/usb/dwc3/dwc3-xilinx.c | 329 +++++++++++++++++++++++++++++----
1 file changed, 294 insertions(+), 35 deletions(-)
diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c
index db3a9204ab64..5c7431d711bf 100644
--- a/drivers/usb/dwc3/dwc3-xilinx.c
+++ b/drivers/usb/dwc3/dwc3-xilinx.c
@@ -25,6 +25,9 @@
#include <linux/phy/phy.h>
+#include "core.h"
+#include "glue.h"
+
/* USB phy reset mask register */
#define XLNX_USB_PHY_RST_EN 0x001C
#define XLNX_PHY_RST_MASK 0x1
@@ -42,18 +45,45 @@
#define XLNX_USB_FPD_POWER_PRSNT 0x80
#define FPD_POWER_PRSNT_OPTION BIT(0)
+struct dwc3_xlnx;
+
+struct dwc3_xlnx_platdata {
+ int (*init)(struct dwc3_xlnx *priv_data);
+ void (*teardown)(void *data);
+ bool flat;
+};
+
struct dwc3_xlnx {
int num_clocks;
struct clk_bulk_data *clks;
struct device *dev;
void __iomem *regs;
- int (*pltfm_init)(struct dwc3_xlnx *data);
+ const struct dwc3_xlnx_platdata *plat;
+ struct dwc3 dwc;
struct phy *usb3_phy;
struct reset_control *usb_crst;
struct reset_control *usb_hibrst;
struct reset_control *usb_apbrst;
};
+#define to_dwc3_xlnx(d) container_of((d), struct dwc3_xlnx, dwc)
+
+/*
+ * The flat model hands the glue device over to the DWC3 core, which stores
+ * its own context in the driver data. The legacy model keeps the glue
+ * context there. Use the match data, which is available either way, to tell
+ * the two apart.
+ */
+static struct dwc3_xlnx *dwc3_xlnx_get_priv(struct device *dev)
+{
+ const struct dwc3_xlnx_platdata *plat = device_get_match_data(dev);
+
+ if (plat && plat->flat)
+ return to_dwc3_xlnx(dev_get_drvdata(dev));
+
+ return dev_get_drvdata(dev);
+}
+
static void dwc3_xlnx_mask_phy_rst(struct dwc3_xlnx *priv_data, bool mask)
{
u32 reg;
@@ -113,6 +143,59 @@ static int dwc3_xlnx_init_versal(struct dwc3_xlnx *priv_data)
return 0;
}
+static void dwc3_xlnx_versal2_teardown(void *data)
+{
+ struct dwc3_xlnx *priv_data = data;
+
+ phy_power_off(priv_data->usb3_phy);
+ reset_control_assert(priv_data->usb_crst);
+ phy_exit(priv_data->usb3_phy);
+}
+
+static int dwc3_xlnx_init_versal2(struct dwc3_xlnx *priv_data)
+{
+ struct device *dev = priv_data->dev;
+ int ret;
+
+ priv_data->usb3_phy = devm_phy_optional_get(dev, "usb3-phy");
+ if (IS_ERR(priv_data->usb3_phy))
+ return dev_err_probe(dev, PTR_ERR(priv_data->usb3_phy),
+ "failed to get USB3 PHY\n");
+
+ priv_data->usb_crst = devm_reset_control_get_exclusive(dev, NULL);
+ if (IS_ERR(priv_data->usb_crst))
+ return dev_err_probe(dev, PTR_ERR(priv_data->usb_crst),
+ "failed to get reset signal\n");
+
+ ret = reset_control_assert(priv_data->usb_crst);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to assert reset\n");
+
+ ret = phy_init(priv_data->usb3_phy);
+ if (ret < 0)
+ return ret;
+
+ ret = reset_control_deassert(priv_data->usb_crst);
+ if (ret < 0) {
+ dev_err_probe(dev, ret, "failed to deassert reset\n");
+ goto err_phy_exit;
+ }
+
+ ret = phy_power_on(priv_data->usb3_phy);
+ if (ret < 0)
+ goto err_crst_assert;
+
+ return devm_add_action_or_reset(dev, dwc3_xlnx_versal2_teardown,
+ priv_data);
+
+err_crst_assert:
+ reset_control_assert(priv_data->usb_crst);
+err_phy_exit:
+ phy_exit(priv_data->usb3_phy);
+
+ return ret;
+}
+
static void dwc3_xlnx_zynqmp_teardown(void *data)
{
struct dwc3_xlnx *priv_data = data;
@@ -142,8 +225,10 @@ static void dwc3_xlnx_zynqmp_teardown(void *data)
static void dwc3_xlnx_release_teardown(struct device *dev,
struct dwc3_xlnx *priv_data)
{
- if (devm_is_action_added(dev, dwc3_xlnx_zynqmp_teardown, priv_data))
- devm_release_action(dev, dwc3_xlnx_zynqmp_teardown, priv_data);
+ void (*teardown)(void *data) = priv_data->plat->teardown;
+
+ if (teardown && devm_is_action_added(dev, teardown, priv_data))
+ devm_release_action(dev, teardown, priv_data);
}
static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
@@ -288,14 +373,33 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
return ret;
}
+static const struct dwc3_xlnx_platdata zynqmp_platdata = {
+ .init = dwc3_xlnx_init_zynqmp,
+ .teardown = dwc3_xlnx_zynqmp_teardown,
+};
+
+static const struct dwc3_xlnx_platdata versal_platdata = {
+ .init = dwc3_xlnx_init_versal,
+};
+
+static const struct dwc3_xlnx_platdata versal2_platdata = {
+ .init = dwc3_xlnx_init_versal2,
+ .teardown = dwc3_xlnx_versal2_teardown,
+ .flat = true,
+};
+
static const struct of_device_id dwc3_xlnx_of_match[] = {
{
.compatible = "xlnx,zynqmp-dwc3",
- .data = &dwc3_xlnx_init_zynqmp,
+ .data = &zynqmp_platdata,
},
{
.compatible = "xlnx,versal-dwc3",
- .data = &dwc3_xlnx_init_versal,
+ .data = &versal_platdata,
+ },
+ {
+ .compatible = "amd,versal2-mmi-dwc3",
+ .data = &versal2_platdata,
},
{ /* Sentinel */ }
};
@@ -326,33 +430,44 @@ static int dwc3_set_swnode(struct device *dev)
return ret;
}
-static int dwc3_xlnx_probe(struct platform_device *pdev)
+/*
+ * Steps both probe models share: claim the glue context, map the wrapper
+ * registers where there are any, turn the clocks on and run the platform
+ * init. The models differ only in what they do with the core afterwards.
+ */
+static int dwc3_xlnx_probe_common(struct platform_device *pdev,
+ struct dwc3_xlnx **priv)
{
- struct dwc3_xlnx *priv_data;
- struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
- void __iomem *regs;
- int ret;
+ const struct dwc3_xlnx_platdata *plat;
+ struct dwc3_xlnx *priv_data;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ plat = device_get_match_data(dev);
+ if (!plat || !plat->init)
+ return dev_err_probe(dev, -ENODEV,
+ "missing platform match data\n");
priv_data = devm_kzalloc(dev, sizeof(*priv_data), GFP_KERNEL);
if (!priv_data)
return -ENOMEM;
- regs = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(regs))
- return dev_err_probe(dev, PTR_ERR(regs), "failed to map registers\n");
-
- priv_data->pltfm_init = device_get_match_data(dev);
- if (!priv_data->pltfm_init)
- return dev_err_probe(dev, -ENODEV,
- "missing platform init handler\n");
-
- priv_data->regs = regs;
+ priv_data->plat = plat;
priv_data->dev = dev;
-
platform_set_drvdata(pdev, priv_data);
- ret = devm_clk_bulk_get_all(priv_data->dev, &priv_data->clks);
+ /*
+ * Only the wrapper models have glue registers of their own. The flat
+ * model hands the controller resource straight to the DWC3 core.
+ */
+ if (!plat->flat) {
+ priv_data->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv_data->regs))
+ return dev_err_probe(dev, PTR_ERR(priv_data->regs),
+ "failed to map registers\n");
+ }
+
+ ret = devm_clk_bulk_get_all(dev, &priv_data->clks);
if (ret < 0)
return ret;
@@ -362,10 +477,66 @@ static int dwc3_xlnx_probe(struct platform_device *pdev)
if (ret)
return ret;
- ret = priv_data->pltfm_init(priv_data);
+ ret = plat->init(priv_data);
+ if (ret) {
+ /*
+ * Registering the teardown is the last thing init does, so a
+ * failure here means it either never registered or has already
+ * run. Only the clocks are left to undo.
+ */
+ clk_bulk_disable_unprepare(priv_data->num_clocks,
+ priv_data->clks);
+ return ret;
+ }
+
+ *priv = priv_data;
+
+ return 0;
+}
+
+static int dwc3_xlnx_probe_flat(struct platform_device *pdev,
+ struct dwc3_xlnx *priv_data)
+{
+ struct dwc3_probe_data probe_data = {};
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ int ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -ENODEV;
+ goto err_clk_put;
+ }
+
+ priv_data->dwc.dev = dev;
+ probe_data.dwc = &priv_data->dwc;
+ probe_data.res = res;
+ probe_data.ignore_clocks_and_resets = true;
+ probe_data.properties = DWC3_DEFAULT_PROPERTIES;
+
+ if (of_dma_is_coherent(dev->of_node))
+ probe_data.properties.gsbuscfg0_reqinfo = 0xf0f0;
+
+ ret = dwc3_core_probe(&probe_data);
if (ret)
goto err_clk_put;
+ return 0;
+
+err_clk_put:
+ dwc3_xlnx_release_teardown(dev, priv_data);
+ clk_bulk_disable_unprepare(priv_data->num_clocks, priv_data->clks);
+
+ return ret;
+}
+
+static int dwc3_xlnx_probe_legacy(struct platform_device *pdev,
+ struct dwc3_xlnx *priv_data)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ int ret;
+
ret = dwc3_set_swnode(dev);
if (ret)
goto err_clk_put;
@@ -397,12 +568,30 @@ static int dwc3_xlnx_probe(struct platform_device *pdev)
return ret;
}
+static int dwc3_xlnx_probe(struct platform_device *pdev)
+{
+ struct dwc3_xlnx *priv_data;
+ int ret;
+
+ ret = dwc3_xlnx_probe_common(pdev, &priv_data);
+ if (ret)
+ return ret;
+
+ if (priv_data->plat->flat)
+ return dwc3_xlnx_probe_flat(pdev, priv_data);
+
+ return dwc3_xlnx_probe_legacy(pdev, priv_data);
+}
+
static void dwc3_xlnx_remove(struct platform_device *pdev)
{
- struct dwc3_xlnx *priv_data = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
+ struct dwc3_xlnx *priv_data = dwc3_xlnx_get_priv(dev);
- of_platform_depopulate(dev);
+ if (priv_data->plat->flat)
+ dwc3_core_remove(&priv_data->dwc);
+ else
+ of_platform_depopulate(dev);
/*
* This is also what makes .shutdown behave the same as .remove:
@@ -414,13 +603,24 @@ static void dwc3_xlnx_remove(struct platform_device *pdev)
clk_bulk_disable_unprepare(priv_data->num_clocks, priv_data->clks);
priv_data->num_clocks = 0;
+ if (priv_data->plat->flat)
+ return;
+
pm_runtime_put_noidle(dev);
pm_runtime_set_suspended(dev);
}
static int __maybe_unused dwc3_xlnx_runtime_suspend(struct device *dev)
{
- struct dwc3_xlnx *priv_data = dev_get_drvdata(dev);
+ struct dwc3_xlnx *priv_data = dwc3_xlnx_get_priv(dev);
+ int ret;
+
+ /* Quiesce the core while its register interface is still clocked. */
+ if (priv_data->plat->flat) {
+ ret = dwc3_runtime_suspend(&priv_data->dwc);
+ if (ret)
+ return ret;
+ }
clk_bulk_disable(priv_data->num_clocks, priv_data->clks);
@@ -429,13 +629,29 @@ static int __maybe_unused dwc3_xlnx_runtime_suspend(struct device *dev)
static int __maybe_unused dwc3_xlnx_runtime_resume(struct device *dev)
{
- struct dwc3_xlnx *priv_data = dev_get_drvdata(dev);
+ struct dwc3_xlnx *priv_data = dwc3_xlnx_get_priv(dev);
+ int ret;
+
+ ret = clk_bulk_enable(priv_data->num_clocks, priv_data->clks);
+ if (ret)
+ return ret;
- return clk_bulk_enable(priv_data->num_clocks, priv_data->clks);
+ if (priv_data->plat->flat) {
+ ret = dwc3_runtime_resume(&priv_data->dwc);
+ if (ret)
+ clk_bulk_disable(priv_data->num_clocks, priv_data->clks);
+ }
+
+ return ret;
}
static int __maybe_unused dwc3_xlnx_runtime_idle(struct device *dev)
{
+ struct dwc3_xlnx *priv_data = dwc3_xlnx_get_priv(dev);
+
+ if (priv_data->plat->flat)
+ return dwc3_runtime_idle(&priv_data->dwc);
+
pm_runtime_autosuspend(dev);
return 0;
@@ -443,12 +659,19 @@ static int __maybe_unused dwc3_xlnx_runtime_idle(struct device *dev)
static int __maybe_unused dwc3_xlnx_suspend(struct device *dev)
{
- struct dwc3_xlnx *priv_data = dev_get_drvdata(dev);
+ struct dwc3_xlnx *priv_data = dwc3_xlnx_get_priv(dev);
int ret;
+ /* Quiesce the core while its register interface is still clocked. */
+ if (priv_data->plat->flat) {
+ ret = dwc3_pm_suspend(&priv_data->dwc);
+ if (ret)
+ return ret;
+ }
+
ret = phy_power_off(priv_data->usb3_phy);
if (ret < 0)
- return ret;
+ goto err_core_resume;
ret = phy_exit(priv_data->usb3_phy);
if (ret < 0)
@@ -462,13 +685,16 @@ static int __maybe_unused dwc3_xlnx_suspend(struct device *dev)
err_phy_power_on:
if (phy_power_on(priv_data->usb3_phy))
dev_err(dev, "failed to restore PHY power after suspend error\n");
+err_core_resume:
+ if (priv_data->plat->flat)
+ dwc3_pm_resume(&priv_data->dwc);
return ret;
}
static int __maybe_unused dwc3_xlnx_resume(struct device *dev)
{
- struct dwc3_xlnx *priv_data = dev_get_drvdata(dev);
+ struct dwc3_xlnx *priv_data = dwc3_xlnx_get_priv(dev);
int ret;
ret = clk_bulk_enable(priv_data->num_clocks, priv_data->clks);
@@ -480,23 +706,56 @@ static int __maybe_unused dwc3_xlnx_resume(struct device *dev)
goto err_clk_disable;
ret = phy_power_on(priv_data->usb3_phy);
- if (ret < 0) {
- phy_exit(priv_data->usb3_phy);
- goto err_clk_disable;
+ if (ret < 0)
+ goto err_phy_exit;
+
+ if (priv_data->plat->flat) {
+ ret = dwc3_pm_resume(&priv_data->dwc);
+ if (ret)
+ goto err_phy_power_off;
}
return 0;
+err_phy_power_off:
+ phy_power_off(priv_data->usb3_phy);
+err_phy_exit:
+ phy_exit(priv_data->usb3_phy);
err_clk_disable:
clk_bulk_disable(priv_data->num_clocks, priv_data->clks);
return ret;
}
+#ifdef CONFIG_PM_SLEEP
+static void dwc3_xlnx_complete(struct device *dev)
+{
+ struct dwc3_xlnx *priv_data = dwc3_xlnx_get_priv(dev);
+
+ if (priv_data->plat->flat)
+ dwc3_pm_complete(&priv_data->dwc);
+}
+
+static int dwc3_xlnx_prepare(struct device *dev)
+{
+ struct dwc3_xlnx *priv_data = dwc3_xlnx_get_priv(dev);
+
+ if (priv_data->plat->flat)
+ return dwc3_pm_prepare(&priv_data->dwc);
+
+ return 0;
+}
+#else
+#define dwc3_xlnx_complete NULL
+#define dwc3_xlnx_prepare NULL
+#endif /* CONFIG_PM_SLEEP */
+
static const struct dev_pm_ops dwc3_xlnx_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(dwc3_xlnx_suspend, dwc3_xlnx_resume)
SET_RUNTIME_PM_OPS(dwc3_xlnx_runtime_suspend,
dwc3_xlnx_runtime_resume, dwc3_xlnx_runtime_idle)
+ .complete = dwc3_xlnx_complete,
+ .prepare = dwc3_xlnx_prepare,
};
static struct platform_driver dwc3_xlnx_driver = {
--
2.43.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v4 3/3] usb: dwc3: xilinx: Add support to program MMI USB TX deemphasis
2026-10-20 11:26 [PATCH v4 0/3] usb: dwc3: xilinx: Add Versal2 MMI USB 3.2 controller support Radhey Shyam Pandey
2026-10-20 11:26 ` [PATCH v4 1/3] dt-bindings: usb: amd,versal2-mmi-dwc3: Add MMI USB support on Versal Gen2 platform Radhey Shyam Pandey
2026-10-20 11:26 ` [PATCH v4 2/3] usb: dwc3: xilinx: Add Versal2 MMI USB 3.2 controller support with flat probe model Radhey Shyam Pandey
@ 2026-10-20 11:26 ` Radhey Shyam Pandey
2 siblings, 0 replies; 6+ messages in thread
From: Radhey Shyam Pandey @ 2026-10-20 11:26 UTC (permalink / raw)
To: gregkh, robh, krzk+dt, conor+dt, Thinh.Nguyen, michal.simek, p.zabel
Cc: linux-usb, devicetree, linux-kernel, linux-arm-kernel,
Radhey Shyam Pandey
Introduces support for programming the 18-bit TX deemphasis value that
drives the pipe_TxDeemph signal, as defined in the PIPE4 specification.
The value is supplied by the glue driver through struct dwc3_properties,
which the flat model provides for core properties the glue knows at probe
time, and is programmed into LCSR_TX_DEEMPH during SoC bus configuration.
The configured value is recommended by Synopsys and is intended for
standard (non-compliance) operation. These Gen2 equalization settings
have been validated through both internal and external compliance
testing. By applying this setting, the stability of USB 3.2 enumeration
is improved and now SuperSpeedPlus devices are consistently recognized as
USB 3.2 Gen2 by the MMI USB Host controller.
Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
---
Changes for v4:
- Pass the value through struct dwc3_properties instead of the
snps,lcsr-tx-deemph software node property; the flat model hands core
properties to dwc3_core_probe() directly.
- Drop the per-port DWC3_LCSR_TX_DEEMPH(n) macro and the loop over
num_usb3_ports in favour of a plain offset, as MMI has a single USB3
port.
- Only program the register on DWC31 and DWC32, where the LCSR space
exists.
- Add the UNSPECIFIED sentinel to DWC3_DEFAULT_PROPERTIES and spell out
both sentinels in fsl_ls1028_dwc3 to avoid -Woverride-init.
Changes for v3:
- Define DWC3_LCSR_TX_DEEMPH(n) and multiport handling. Thinh: Please
review on this offset calculation as MMI USB IP support single usb3
port.
- Default set the tx_deemph to the DWC3_LCSR_TX_DEEMPH_UNSPECIFIED.
Changes for v2:
- Don't use compatible check for deemphasis programming.
- Rename property "snps,lcsr_tx_deemph" to "snps,lcsr-tx-deemph"
(hyphens per kernel convention).
- Fix double space in LCSR_TX_DEEMPH register comment.
- Add blank line between register offset define and "Bit fields" section.
---
drivers/usb/dwc3/core.c | 12 ++++++++++++
drivers/usb/dwc3/core.h | 8 ++++++++
drivers/usb/dwc3/dwc3-generic-plat.c | 11 ++++++++++-
drivers/usb/dwc3/dwc3-xilinx.c | 4 ++++
drivers/usb/dwc3/glue.h | 4 ++++
5 files changed, 38 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index fd5c2cd36c59..4e29056a76d9 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -642,6 +642,17 @@ static void dwc3_config_soc_bus(struct dwc3 *dwc)
reg |= DWC3_GSBUSCFG0_REQINFO(dwc->gsbuscfg0_reqinfo);
dwc3_writel(dwc, DWC3_GSBUSCFG0, reg);
}
+
+ /* The LCSR register space only exists on DWC31 and DWC32 cores. */
+ if (dwc->csr_tx_deemph_field_1 != DWC3_LCSR_TX_DEEMPH_UNSPECIFIED &&
+ (DWC3_IP_IS(DWC31) || DWC3_IP_IS(DWC32))) {
+ u32 reg;
+
+ reg = dwc3_readl(dwc, DWC3_LCSR_TX_DEEMPH);
+ reg &= ~DWC3_LCSR_TX_DEEMPH_MASK(~0);
+ reg |= DWC3_LCSR_TX_DEEMPH_MASK(dwc->csr_tx_deemph_field_1);
+ dwc3_writel(dwc, DWC3_LCSR_TX_DEEMPH, reg);
+ }
}
static int dwc3_core_ulpi_init(struct dwc3 *dwc)
@@ -1695,6 +1706,7 @@ static void dwc3_get_software_properties(struct dwc3 *dwc,
dwc->needs_full_reinit = true;
dwc->gsbuscfg0_reqinfo = DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED;
+ dwc->csr_tx_deemph_field_1 = properties->csr_tx_deemph_field_1;
if (properties->gsbuscfg0_reqinfo !=
DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED) {
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index f3dabb37f806..032f801954fa 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -182,6 +182,8 @@
#define DWC3_LLUCTL(n) (0xd024 + ((n) * 0x80))
+#define DWC3_LCSR_TX_DEEMPH 0xd060
+
/* Bit fields */
/* Global SoC Bus Configuration INCRx Register 0 */
@@ -199,6 +201,10 @@
#define DWC3_GSBUSCFG0_REQINFO(n) (((n) & 0xffff) << 16)
#define DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED 0xffffffff
+/* LCSR_TX_DEEMPH Register: setting TX deemphasis used in normal operation in gen2 */
+#define DWC3_LCSR_TX_DEEMPH_MASK(n) ((n) & 0x3ffff)
+#define DWC3_LCSR_TX_DEEMPH_UNSPECIFIED 0xffffffff
+
/* Global Debug LSP MUX Select */
#define DWC3_GDBGLSPMUX_ENDBC BIT(15) /* Host only */
#define DWC3_GDBGLSPMUX_HOSTSELECT(n) ((n) & 0x3fff)
@@ -1195,6 +1201,7 @@ struct dwc3_glue_ops {
* @gsbuscfg0_reqinfo: store GSBUSCFG0.DATRDREQINFO, DESRDREQINFO,
* DATWRREQINFO, and DESWRREQINFO value passed from
* glue driver.
+ * @csr_tx_deemph_field_1: stores TX deemphasis used in Gen2 operation.
* @wakeup_pending_funcs: Indicates whether any interface has requested for
* function wakeup in bitmap format where bit position
* represents interface_id.
@@ -1447,6 +1454,7 @@ struct dwc3 {
int num_ep_resized;
struct dentry *debug_root;
u32 gsbuscfg0_reqinfo;
+ u32 csr_tx_deemph_field_1;
u32 wakeup_pending_funcs;
};
diff --git a/drivers/usb/dwc3/dwc3-generic-plat.c b/drivers/usb/dwc3/dwc3-generic-plat.c
index ca69ac0eb07c..4c2eba2a0a3f 100644
--- a/drivers/usb/dwc3/dwc3-generic-plat.c
+++ b/drivers/usb/dwc3/dwc3-generic-plat.c
@@ -223,7 +223,16 @@ static const struct dwc3_generic_config spacemit_k1_dwc3 = {
};
static const struct dwc3_generic_config fsl_ls1028_dwc3 = {
- .properties.gsbuscfg0_reqinfo = 0x2222,
+ /*
+ * Spell out every unspecified sentinel. A designated initializer
+ * cannot override a single member of DWC3_DEFAULT_PROPERTIES without
+ * tripping -Woverride-init, and a partially initialized struct would
+ * request a value of zero for the members left out.
+ */
+ .properties = {
+ .gsbuscfg0_reqinfo = 0x2222,
+ .csr_tx_deemph_field_1 = DWC3_LCSR_TX_DEEMPH_UNSPECIFIED,
+ },
};
static const struct dwc3_generic_config eic7700_dwc3 = {
diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c
index 5c7431d711bf..70f28ed16faa 100644
--- a/drivers/usb/dwc3/dwc3-xilinx.c
+++ b/drivers/usb/dwc3/dwc3-xilinx.c
@@ -45,6 +45,8 @@
#define XLNX_USB_FPD_POWER_PRSNT 0x80
#define FPD_POWER_PRSNT_OPTION BIT(0)
+#define XLNX_MMI_USB_TX_DEEMPH_DEF 0x8c45
+
struct dwc3_xlnx;
struct dwc3_xlnx_platdata {
@@ -517,6 +519,8 @@ static int dwc3_xlnx_probe_flat(struct platform_device *pdev,
if (of_dma_is_coherent(dev->of_node))
probe_data.properties.gsbuscfg0_reqinfo = 0xf0f0;
+ probe_data.properties.csr_tx_deemph_field_1 = XLNX_MMI_USB_TX_DEEMPH_DEF;
+
ret = dwc3_core_probe(&probe_data);
if (ret)
goto err_clk_put;
diff --git a/drivers/usb/dwc3/glue.h b/drivers/usb/dwc3/glue.h
index d738e1739ae0..8fa7f0f696a8 100644
--- a/drivers/usb/dwc3/glue.h
+++ b/drivers/usb/dwc3/glue.h
@@ -12,16 +12,20 @@
/**
* dwc3_properties: DWC3 core properties
* @gsbuscfg0_reqinfo: Value to be programmed in the GSBUSCFG0.REQINFO field
+ * @csr_tx_deemph_field_1: Value to be programmed in the LCSR_TX_DEEMPH
+ * register, setting the TX deemphasis used in Gen2
* @needs_full_reinit: indicate the controller may not remain power during system
* pm and need full initialization
*/
struct dwc3_properties {
u32 gsbuscfg0_reqinfo;
+ u32 csr_tx_deemph_field_1;
unsigned needs_full_reinit:1;
};
#define DWC3_DEFAULT_PROPERTIES ((struct dwc3_properties){ \
.gsbuscfg0_reqinfo = DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED, \
+ .csr_tx_deemph_field_1 = DWC3_LCSR_TX_DEEMPH_UNSPECIFIED, \
})
/**
--
2.43.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v4 1/3] dt-bindings: usb: amd,versal2-mmi-dwc3: Add MMI USB support on Versal Gen2 platform
2026-10-20 11:26 ` [PATCH v4 1/3] dt-bindings: usb: amd,versal2-mmi-dwc3: Add MMI USB support on Versal Gen2 platform Radhey Shyam Pandey
@ 2026-10-20 18:17 ` Rob Herring (Arm)
0 siblings, 0 replies; 6+ messages in thread
From: Rob Herring (Arm) @ 2026-10-20 18:17 UTC (permalink / raw)
To: Radhey Shyam Pandey
Cc: krzk+dt, linux-usb, linux-kernel, michal.simek, Thinh.Nguyen,
conor+dt, p.zabel, devicetree, linux-arm-kernel, gregkh
On Tue, 20 Oct 2026 16:56:33 +0530, Radhey Shyam Pandey wrote:
> The Versal Gen 2 platform includes two distinct USB controllers. The
> LPD controller is a USB 2.0 instance and reuses the existing
> xlnx,versal-dwc3 binding. The second is the Multimedia Integrated (MMI)
> USB 3.2 Gen 2x1 Dual-Role (DRD) controller, which natively supports USB
> 3.2 host and device operation over Type-C in a Gen 2x1 configuration
> with a maximum data rate of 10 Gbps per direction.
>
> Introduce a new compatible in <vendor>,<soc>-<subsystem>-<ip> format to
> uniquely distinguish the MMI USB controller.
>
> Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
> ---
> Changes for v4:
> - Switch to flat mode and rephrase commit description.
>
> Changes for v3:
> - Modify commit description to explain that there are two different
> USB controllers and difference between versal2-dwc3 and
> versal2-mmi-dwc3. Suggested by Krzysztof.
>
> Changes for v2:
> - Add blank line after compatible as suggested by Krzysztof.
> - Retain the mmi suffix in the compatible string, as this USB 3.2 Gen2
> IP from Synopsys is part of the dedicated Multimedia Interface. The
> Versal Gen2 platform also includes a separate USB 2.0 controller,
> and the mmi suffix uniquely distinguishes between the two USB
> controllers. MMI is an independent subsystem particularly targeted for
> deployment in Multi-Media related applications. The MMI block include
> following submodules: UDH: USB3.2 Gen 2x1 Dual Role Device, DisplayPort
> Transmit Controller, Security Module (ESM) for DisplayPort and HDMI
> Controllers, DP AUX-I2C PHY.
> - For MMI USB define parent address space i.e UDH block.
> - Fix inconsistent MHz spacing to use SI convention with spaces.
> - Move description before $ref and items in xlnx,usb-syscon property.
> - Restore original zynqmp-dwc3 example, add new versal2-mmi-dwc3 example.
> - Use 'usb' node name (without unit address) for versal2 example since
> it has no reg property.
> - Use 1/1 address/size configuration in versal2 example, use lowercase
> hex in syscon offsets.
> ---
> .../bindings/usb/amd,versal2-mmi-dwc3.yaml | 92 +++++++++++++++++++
> 1 file changed, 92 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/usb/amd,versal2-mmi-dwc3.yaml
>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v4 2/3] usb: dwc3: xilinx: Add Versal2 MMI USB 3.2 controller support with flat probe model
2026-10-20 11:26 ` [PATCH v4 2/3] usb: dwc3: xilinx: Add Versal2 MMI USB 3.2 controller support with flat probe model Radhey Shyam Pandey
@ 2026-11-04 1:44 ` Thinh Nguyen
0 siblings, 0 replies; 6+ messages in thread
From: Thinh Nguyen @ 2026-11-04 1:44 UTC (permalink / raw)
To: Radhey Shyam Pandey
Cc: gregkh, robh, krzk+dt, conor+dt, Thinh Nguyen, michal.simek,
p.zabel, linux-usb, devicetree, linux-kernel, linux-arm-kernel
Hi,
On Tue, Oct 20, 2026, Radhey Shyam Pandey wrote:
....
This doesn't apply on Greg's usb-testing branch. Please rebase and
resend.
Thanks,
Thinh
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-11-04 1:45 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-20 11:26 [PATCH v4 0/3] usb: dwc3: xilinx: Add Versal2 MMI USB 3.2 controller support Radhey Shyam Pandey
2026-10-20 11:26 ` [PATCH v4 1/3] dt-bindings: usb: amd,versal2-mmi-dwc3: Add MMI USB support on Versal Gen2 platform Radhey Shyam Pandey
2026-10-20 18:17 ` Rob Herring (Arm)
2026-10-20 11:26 ` [PATCH v4 2/3] usb: dwc3: xilinx: Add Versal2 MMI USB 3.2 controller support with flat probe model Radhey Shyam Pandey
2026-11-04 1:44 ` Thinh Nguyen
2026-10-20 11:26 ` [PATCH v4 3/3] usb: dwc3: xilinx: Add support to program MMI USB TX deemphasis Radhey Shyam Pandey
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®