* [PATCH 0/2] phy: nuvoton: Add MA35D1 USB2 OTG PHY driver
@ 2026-06-04 10:12 Joey Lu
2026-06-04 10:12 ` [PATCH 1/2] dt-bindings: phy: nuvoton: Add MA35D1 USB2 OTG PHY binding Joey Lu
2026-06-04 10:12 ` [PATCH 2/2] phy: nuvoton: Add MA35D1 USB2 OTG PHY driver Joey Lu
0 siblings, 2 replies; 7+ messages in thread
From: Joey Lu @ 2026-06-04 10:12 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jacky Huang,
Shan-Chun Hung, linux-phy, devicetree, linux-arm-kernel,
linux-kernel, Joey Lu
The Nuvoton MA35D1 SoC has two USB 2.0 ports:
USB0 is an OTG-capable port. Its physical signals are routed by a
hardware mux to either a DWC2 gadget controller or the EHCI0/OHCI0
host controllers, depending on the USB ID pin state. The DWC2 IP is
device-only in hardware, so all host-mode operation on USB0 is
handled by EHCI0/OHCI0.
USB1 is a dedicated host-only port served by EHCI1/OHCI1.
About this driver:
- Runs the PHY Power-On Reset sequence, with a guard that skips
re-initialization if the PHY is already operational.
- Supports optional resistor calibration trim (nuvoton,rcalcode) and
over-current detect polarity configuration (nuvoton,oc-active-high).
- For PHY0 (USB0) only: registers a USB role switch that reads the
hardware ID pin state from PWRONOTP[16] on every query.
Joey Lu (2):
dt-bindings: phy: nuvoton: Add MA35D1 USB2 OTG PHY binding
phy: nuvoton: Add MA35D1 USB2 OTG PHY driver
.../phy/nuvoton,ma35d1-usb2-phy-otg.yaml | 79 ++++++
drivers/phy/nuvoton/Kconfig | 15 +
drivers/phy/nuvoton/Makefile | 1 +
drivers/phy/nuvoton/phy-ma35d1-otg.c | 264 ++++++++++++++++++
4 files changed, 359 insertions(+)
create mode 100644 Documentation/devicetree/bindings/phy/nuvoton,ma35d1-usb2-phy-otg.yaml
create mode 100644 drivers/phy/nuvoton/phy-ma35d1-otg.c
--
2.43.0
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH 1/2] dt-bindings: phy: nuvoton: Add MA35D1 USB2 OTG PHY binding 2026-06-04 10:12 [PATCH 0/2] phy: nuvoton: Add MA35D1 USB2 OTG PHY driver Joey Lu @ 2026-06-04 10:12 ` Joey Lu 2026-06-08 10:45 ` Krzysztof Kozlowski 2026-06-04 10:12 ` [PATCH 2/2] phy: nuvoton: Add MA35D1 USB2 OTG PHY driver Joey Lu 1 sibling, 1 reply; 7+ messages in thread From: Joey Lu @ 2026-06-04 10:12 UTC (permalink / raw) To: Vinod Koul, Neil Armstrong Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jacky Huang, Shan-Chun Hung, linux-phy, devicetree, linux-arm-kernel, linux-kernel, Joey Lu Add device tree binding documentation for the Nuvoton MA35D1 USB 2.0 OTG PHY driver (nuvoton,ma35d1-usb2-phy-otg). PHY index 0 (USB0) is an OTG port whose signals are routed by a hardware mux to either the DWC2 device controller or the EHCI0/OHCI0 host controllers depending on the USB ID pin state. PHY index 1 (USB1) is a dedicated host-only port. Optional properties allow board-specific resistor calibration trim (nuvoton,rcalcode) and over-current detect polarity configuration (nuvoton,oc-active-high). Signed-off-by: Joey Lu <a0987203069@gmail.com> --- .../phy/nuvoton,ma35d1-usb2-phy-otg.yaml | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/nuvoton,ma35d1-usb2-phy-otg.yaml diff --git a/Documentation/devicetree/bindings/phy/nuvoton,ma35d1-usb2-phy-otg.yaml b/Documentation/devicetree/bindings/phy/nuvoton,ma35d1-usb2-phy-otg.yaml new file mode 100644 index 000000000000..19f074565cc6 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/nuvoton,ma35d1-usb2-phy-otg.yaml @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/nuvoton,ma35d1-usb2-phy-otg.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Nuvoton MA35D1 USB 2.0 host PHY + +maintainers: + - Joey Lu <yclu4@nuvoton.com> + +description: + USB 2.0 PHY driver for the Nuvoton MA35D1 SoC, used by the EHCI and + OHCI host controllers. + + USB0 (PHY index 0) is an OTG port whose physical signals are routed to + either the DWC2 device controller or the EHCI0/OHCI0 host controller by + a hardware mux that follows the USB ID pin. + + USB1 (PHY index 1) is a dedicated host port with no OTG capability. + +properties: + compatible: + const: nuvoton,ma35d1-usb2-phy-otg + + clocks: + maxItems: 1 + + nuvoton,sys: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle to the system management syscon. + - description: PHY instance index. + enum: + - 0 # USB0, OTG port (shared with DWC2 gadget controller) + - 1 # USB1, host-only port + description: + A phandle to the syscon node covering the SYS register block, with + one argument selecting the PHY instance. Index 0 selects the OTG + port PHY (USB0) and index 1 selects the host-only PHY (USB1). + + "#phy-cells": + const: 0 + + nuvoton,rcalcode: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 15 + description: + Resistor calibration trim code written to the RCALCODE field in + USBPMISCR. The 4-bit value adjusts the PHY's internal termination + resistance. When absent the hardware reset default is used. + + nuvoton,oc-active-high: + type: boolean + description: + When present, the over-current detect input from the VBUS power + switch is treated as active-high. The default (property absent) is + active-low. This setting is shared by both USB host ports. + +required: + - compatible + - clocks + - nuvoton,sys + - "#phy-cells" + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/nuvoton,ma35d1-clk.h> + + usb_hphy0: usb-host-phy { + compatible = "nuvoton,ma35d1-usb2-phy-otg"; + clocks = <&clk HUSBH0_GATE>; + nuvoton,sys = <&sys 0>; + #phy-cells = <0>; + }; -- 2.43.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH 1/2] dt-bindings: phy: nuvoton: Add MA35D1 USB2 OTG PHY binding 2026-06-04 10:12 ` [PATCH 1/2] dt-bindings: phy: nuvoton: Add MA35D1 USB2 OTG PHY binding Joey Lu @ 2026-06-08 10:45 ` Krzysztof Kozlowski 2026-06-09 9:15 ` Joey Lu 0 siblings, 1 reply; 7+ messages in thread From: Krzysztof Kozlowski @ 2026-06-08 10:45 UTC (permalink / raw) To: Joey Lu Cc: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jacky Huang, Shan-Chun Hung, linux-phy, devicetree, linux-arm-kernel, linux-kernel On Thu, Jun 04, 2026 at 06:12:19PM +0800, Joey Lu wrote: A nit, subject: drop second/last, redundant "binding". The "dt-bindings" prefix is already stating that these are bindings. See also: https://elixir.bootlin.com/linux/v6.17-rc3/source/Documentation/devicetree/bindings/submitting-patches.rst#L18 > +properties: > + compatible: > + const: nuvoton,ma35d1-usb2-phy-otg > + Where is reg? MMIO is expressed with reg, not via custom phandle. > + clocks: > + maxItems: 1 > + > + nuvoton,sys: > + $ref: /schemas/types.yaml#/definitions/phandle-array > + items: > + - items: > + - description: phandle to the system management syscon. > + - description: PHY instance index. > + enum: > + - 0 # USB0, OTG port (shared with DWC2 gadget controller) > + - 1 # USB1, host-only port > + description: > + A phandle to the syscon node covering the SYS register block, with > + one argument selecting the PHY instance. Index 0 selects the OTG > + port PHY (USB0) and index 1 selects the host-only PHY (USB1). > + > + "#phy-cells": > + const: 0 > + > + nuvoton,rcalcode: > + $ref: /schemas/types.yaml#/definitions/uint32 > + minimum: 0 > + maximum: 15 > + description: > + Resistor calibration trim code written to the RCALCODE field in > + USBPMISCR. The 4-bit value adjusts the PHY's internal termination > + resistance. When absent the hardware reset default is used. > + > + nuvoton,oc-active-high: > + type: boolean > + description: > + When present, the over-current detect input from the VBUS power > + switch is treated as active-high. The default (property absent) is > + active-low. This setting is shared by both USB host ports. > + > +required: > + - compatible > + - clocks > + - nuvoton,sys > + - "#phy-cells" > + > +additionalProperties: false > + > +examples: > + - | > + #include <dt-bindings/clock/nuvoton,ma35d1-clk.h> > + > + usb_hphy0: usb-host-phy { usb-phy And drop unused label. > + compatible = "nuvoton,ma35d1-usb2-phy-otg"; > + clocks = <&clk HUSBH0_GATE>; > + nuvoton,sys = <&sys 0>; > + #phy-cells = <0>; > + }; > -- > 2.43.0 > ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 1/2] dt-bindings: phy: nuvoton: Add MA35D1 USB2 OTG PHY binding 2026-06-08 10:45 ` Krzysztof Kozlowski @ 2026-06-09 9:15 ` Joey Lu 0 siblings, 0 replies; 7+ messages in thread From: Joey Lu @ 2026-06-09 9:15 UTC (permalink / raw) To: Krzysztof Kozlowski Cc: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jacky Huang, Shan-Chun Hung, linux-phy, devicetree, linux-arm-kernel, linux-kernel On 6/8/2026 6:45 PM, Krzysztof Kozlowski wrote: > On Thu, Jun 04, 2026 at 06:12:19PM +0800, Joey Lu wrote: > > A nit, subject: drop second/last, redundant "binding". The > "dt-bindings" prefix is already stating that these are bindings. > See also: > https://elixir.bootlin.com/linux/v6.17-rc3/source/Documentation/devicetree/bindings/submitting-patches.rst#L18 Understood. The trailing "binding" will be dropped from the patch subject line in the next submission. >> +properties: >> + compatible: >> + const: nuvoton,ma35d1-usb2-phy-otg >> + > Where is reg? MMIO is expressed with reg, not via custom phandle. Understood. The PHY node will be restructured as a child of the syscon node, with a `reg = <0x60 0x14>` property covering the writable PHY control registers (USBPMISCR, MISCFCR0) within the parent's address space. The `nuvoton,sys` custom phandle will be removed; the driver will obtain the parent's regmap via `syscon_node_to_regmap(dev->parent->of_node)`, which looks up the global syscon list by `of_node`. PHY instance selection will be expressed via `#phy-cells = <1>`, with 0 for the OTG port (USB0) and 1 for the host-only port (USB1). > >> + clocks: >> + maxItems: 1 >> + >> + nuvoton,sys: >> + $ref: /schemas/types.yaml#/definitions/phandle-array >> + items: >> + - items: >> + - description: phandle to the system management syscon. >> + - description: PHY instance index. >> + enum: >> + - 0 # USB0, OTG port (shared with DWC2 gadget controller) >> + - 1 # USB1, host-only port >> + description: >> + A phandle to the syscon node covering the SYS register block, with >> + one argument selecting the PHY instance. Index 0 selects the OTG >> + port PHY (USB0) and index 1 selects the host-only PHY (USB1). >> + >> + "#phy-cells": >> + const: 0 >> + >> + nuvoton,rcalcode: >> + $ref: /schemas/types.yaml#/definitions/uint32 >> + minimum: 0 >> + maximum: 15 >> + description: >> + Resistor calibration trim code written to the RCALCODE field in >> + USBPMISCR. The 4-bit value adjusts the PHY's internal termination >> + resistance. When absent the hardware reset default is used. >> + >> + nuvoton,oc-active-high: >> + type: boolean >> + description: >> + When present, the over-current detect input from the VBUS power >> + switch is treated as active-high. The default (property absent) is >> + active-low. This setting is shared by both USB host ports. >> + >> +required: >> + - compatible >> + - clocks >> + - nuvoton,sys >> + - "#phy-cells" >> + >> +additionalProperties: false >> + >> +examples: >> + - | >> + #include <dt-bindings/clock/nuvoton,ma35d1-clk.h> >> + >> + usb_hphy0: usb-host-phy { > usb-phy > > And drop unused label. Understood. The node name will be changed to `usb-phy` and the unused label `usb_hphy0:` will be removed. Thanks for the review! >> + compatible = "nuvoton,ma35d1-usb2-phy-otg"; >> + clocks = <&clk HUSBH0_GATE>; >> + nuvoton,sys = <&sys 0>; >> + #phy-cells = <0>; >> + }; >> -- >> 2.43.0 >> ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 2/2] phy: nuvoton: Add MA35D1 USB2 OTG PHY driver 2026-06-04 10:12 [PATCH 0/2] phy: nuvoton: Add MA35D1 USB2 OTG PHY driver Joey Lu 2026-06-04 10:12 ` [PATCH 1/2] dt-bindings: phy: nuvoton: Add MA35D1 USB2 OTG PHY binding Joey Lu @ 2026-06-04 10:12 ` Joey Lu 2026-06-11 11:04 ` Vinod Koul 1 sibling, 1 reply; 7+ messages in thread From: Joey Lu @ 2026-06-04 10:12 UTC (permalink / raw) To: Vinod Koul, Neil Armstrong Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jacky Huang, Shan-Chun Hung, linux-phy, devicetree, linux-arm-kernel, linux-kernel, Joey Lu Add a PHY driver for the USB 2.0 PHYs in the Nuvoton MA35D1 SoC, intended for use with the EHCI and OHCI host controllers. The MA35D1 SoC has two USB ports: - USB0: an OTG port shared between a DWC2 gadget controller and EHCI0/OHCI0 host controllers. A hardware mux automatically routes the physical USB0 signals to the appropriate controller based on the USB ID pin state. The DWC2 IP is device-only in hardware, so host-mode operation on USB0 is handled entirely by EHCI0/OHCI0. - USB1: a dedicated host-only port served by EHCI1/OHCI1. The driver implements: - Power-On Reset sequence with a guard that skips re-initialization if the PHY is already operational. This protects PHY0 when the DWC2 gadget driver has already run its own init before EHCI0 probes. - Optional resistor calibration trim via nuvoton,rcalcode. - Optional over-current detect polarity via nuvoton,oc-active-high. - For PHY0 only: a USB role switch that exposes the hardware ID pin state (PWRONOTP[16]). Signed-off-by: Joey Lu <a0987203069@gmail.com> --- drivers/phy/nuvoton/Kconfig | 15 ++ drivers/phy/nuvoton/Makefile | 1 + drivers/phy/nuvoton/phy-ma35d1-otg.c | 264 +++++++++++++++++++++++++++ 3 files changed, 280 insertions(+) create mode 100644 drivers/phy/nuvoton/phy-ma35d1-otg.c diff --git a/drivers/phy/nuvoton/Kconfig b/drivers/phy/nuvoton/Kconfig index d02cae2db315..5fdd13f841e7 100644 --- a/drivers/phy/nuvoton/Kconfig +++ b/drivers/phy/nuvoton/Kconfig @@ -10,3 +10,18 @@ config PHY_MA35_USB help Enable this to support the USB2.0 PHY on the Nuvoton MA35 series SoCs. + +config PHY_MA35_USB_OTG + tristate "Nuvoton MA35 USB2.0 OTG PHY driver" + depends on ARCH_MA35 || COMPILE_TEST + depends on OF + select GENERIC_PHY + select MFD_SYSCON + select USB_ROLE_SWITCH + help + Enable this to support the USB2.0 OTG PHY on the Nuvoton MA35 + series SoCs. This driver handles PHY initialization for the + EHCI/OHCI host controllers, including per-PHY power-on reset, + resistor calibration trim, and over-current polarity + configuration. For the OTG port (PHY0), it also monitors the + USB ID pin and registers a USB role switch. diff --git a/drivers/phy/nuvoton/Makefile b/drivers/phy/nuvoton/Makefile index 2937e3921898..3ecd76f35d7c 100644 --- a/drivers/phy/nuvoton/Makefile +++ b/drivers/phy/nuvoton/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_PHY_MA35_USB) += phy-ma35d1-usb2.o +obj-$(CONFIG_PHY_MA35_USB_OTG) += phy-ma35d1-otg.o diff --git a/drivers/phy/nuvoton/phy-ma35d1-otg.c b/drivers/phy/nuvoton/phy-ma35d1-otg.c new file mode 100644 index 000000000000..53bc6ddf755e --- /dev/null +++ b/drivers/phy/nuvoton/phy-ma35d1-otg.c @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Nuvoton MA35D1 USB 2.0 OTG PHY driver + * + * PHY0 (USB0) is shared between DWC2 gadget and EHCI0/OHCI0 host + * controllers. The hardware mux switches automatically via the USB + * ID pin. PHY1 (USB1) is host-only. + * + * Copyright (C) 2026 Nuvoton Technology Corp. + */ +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/usb/role.h> + +#define MA35_SYS_PWRONOTP 0x04 +#define PWRONOTP_USBP0ID BIT(16) + +#define MA35_SYS_USBPMISCR 0x60 +#define USBPMISCR_PHY_POR(n) BIT(0 + (n) * 16) +#define USBPMISCR_PHY_SUSPEND(n) BIT(1 + (n) * 16) +#define USBPMISCR_PHY_COMN(n) BIT(2 + (n) * 16) +#define USBPMISCR_PHY_HSTCKSTB(n) BIT(8 + (n) * 16) +#define USBPMISCR_PHY_CK12MSTB(n) BIT(9 + (n) * 16) +/* Mask for control bits (POR, SUSPEND, COMN) of one PHY */ +#define USBPMISCR_PHY_CTL_MASK(n) (0x7 << ((n) * 16)) +/* Host-mode ready: SUSPEND + HSTCKSTB + CK12MSTB */ +#define USBPMISCR_PHY_HOST_READY(n) (USBPMISCR_PHY_SUSPEND(n) | \ + USBPMISCR_PHY_HSTCKSTB(n) | \ + USBPMISCR_PHY_CK12MSTB(n)) +/* RCALCODE: 4-bit resistor trim at bits [15:12] (PHY0) or [31:28] (PHY1) */ +#define USBPMISCR_RCAL_SHIFT(n) (12 + (n) * 16) +#define USBPMISCR_RCAL_MASK(n) GENMASK(USBPMISCR_RCAL_SHIFT(n) + 3, \ + USBPMISCR_RCAL_SHIFT(n)) + +#define MA35_SYS_MISCFCR0 0x70 +/* MISCFCR0[12]: USB host over-current detect polarity (shared, both ports) */ +#define MISCFCR0_UHOVRCURH BIT(12) + +struct ma35_otg_phy { + struct clk *clk; + struct device *dev; + struct regmap *sysreg; + unsigned int phy_idx; + struct usb_role_switch *role_sw; + enum usb_role cur_role; +}; + +static int ma35_otg_phy_init(struct phy *phy) +{ + struct ma35_otg_phy *p = phy_get_drvdata(phy); + unsigned int n = p->phy_idx; + u32 ready_mask = USBPMISCR_PHY_HOST_READY(n); + unsigned int val; + int ret; + + regmap_read(p->sysreg, MA35_SYS_USBPMISCR, &val); + if ((val & ready_mask) == ready_mask) + return 0; + + regmap_update_bits(p->sysreg, MA35_SYS_USBPMISCR, + USBPMISCR_PHY_CTL_MASK(n), + USBPMISCR_PHY_POR(n) | USBPMISCR_PHY_SUSPEND(n)); + msleep(20); + + regmap_update_bits(p->sysreg, MA35_SYS_USBPMISCR, + USBPMISCR_PHY_CTL_MASK(n), + USBPMISCR_PHY_SUSPEND(n)); + + ret = regmap_read_poll_timeout(p->sysreg, MA35_SYS_USBPMISCR, val, + (val & ready_mask) == ready_mask, + 10, 1000); + if (ret) { + dev_err(p->dev, "USB PHY%u clock not stable (USBPMISCR=0x%08x)\n", + n, val); + return ret; + } + + return 0; +} + +static int ma35_otg_phy_power_on(struct phy *phy) +{ + struct ma35_otg_phy *p = phy_get_drvdata(phy); + + return clk_prepare_enable(p->clk); +} + +static int ma35_otg_phy_power_off(struct phy *phy) +{ + struct ma35_otg_phy *p = phy_get_drvdata(phy); + + clk_disable_unprepare(p->clk); + return 0; +} + +static const struct phy_ops ma35_otg_phy_ops = { + .init = ma35_otg_phy_init, + .power_on = ma35_otg_phy_power_on, + .power_off = ma35_otg_phy_power_off, + .owner = THIS_MODULE, +}; + +static enum usb_role ma35_otg_read_id(struct ma35_otg_phy *p) +{ + unsigned int val; + + regmap_read(p->sysreg, MA35_SYS_PWRONOTP, &val); + return (val & PWRONOTP_USBP0ID) ? USB_ROLE_HOST : USB_ROLE_DEVICE; +} + +static int ma35_otg_role_sw_set(struct usb_role_switch *sw, + enum usb_role role) +{ + struct ma35_otg_phy *p = usb_role_switch_get_drvdata(sw); + + p->cur_role = role; + + return 0; +} + +static enum usb_role ma35_otg_role_sw_get(struct usb_role_switch *sw) +{ + struct ma35_otg_phy *p = usb_role_switch_get_drvdata(sw); + + return ma35_otg_read_id(p); +} + +static int ma35_otg_role_switch_init(struct platform_device *pdev, + struct ma35_otg_phy *p) +{ + struct usb_role_switch_desc sw_desc = { }; + + p->cur_role = ma35_otg_read_id(p); + + sw_desc.set = ma35_otg_role_sw_set; + sw_desc.get = ma35_otg_role_sw_get; + sw_desc.allow_userspace_control = true; + sw_desc.driver_data = p; + sw_desc.fwnode = dev_fwnode(&pdev->dev); + + p->role_sw = usb_role_switch_register(&pdev->dev, &sw_desc); + if (IS_ERR(p->role_sw)) + return dev_err_probe(&pdev->dev, PTR_ERR(p->role_sw), + "failed to register role switch\n"); + + return 0; +} + +static void ma35_otg_role_switch_exit(struct ma35_otg_phy *p) +{ + if (!p->role_sw) + return; + + usb_role_switch_unregister(p->role_sw); + p->role_sw = NULL; +} + +static int ma35_otg_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *provider; + struct ma35_otg_phy *p; + unsigned int sys_args[1]; + struct phy *phy; + u32 rcalcode; + int ret; + + p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); + if (!p) + return -ENOMEM; + + p->dev = &pdev->dev; + platform_set_drvdata(pdev, p); + + p->sysreg = syscon_regmap_lookup_by_phandle_args(pdev->dev.of_node, + "nuvoton,sys", + 1, sys_args); + if (IS_ERR(p->sysreg)) + return dev_err_probe(&pdev->dev, PTR_ERR(p->sysreg), + "Failed to get SYS regmap\n"); + + p->phy_idx = sys_args[0]; + + if (p->phy_idx > 1) + return dev_err_probe(&pdev->dev, -EINVAL, + "invalid PHY index %u (must be 0 or 1)\n", + p->phy_idx); + + p->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(p->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(p->clk), + "failed to get PHY clock\n"); + + if (!of_property_read_u32(pdev->dev.of_node, "nuvoton,rcalcode", + &rcalcode)) { + if (rcalcode > 15) + return dev_err_probe(&pdev->dev, -EINVAL, + "rcalcode %u out of range (0-15)\n", + rcalcode); + regmap_update_bits(p->sysreg, MA35_SYS_USBPMISCR, + USBPMISCR_RCAL_MASK(p->phy_idx), + rcalcode << USBPMISCR_RCAL_SHIFT(p->phy_idx)); + } + + if (of_property_read_bool(pdev->dev.of_node, "nuvoton,oc-active-high")) + regmap_update_bits(p->sysreg, MA35_SYS_MISCFCR0, + MISCFCR0_UHOVRCURH, MISCFCR0_UHOVRCURH); + + phy = devm_phy_create(&pdev->dev, pdev->dev.of_node, &ma35_otg_phy_ops); + if (IS_ERR(phy)) + return dev_err_probe(&pdev->dev, PTR_ERR(phy), + "Failed to create PHY\n"); + + phy_set_drvdata(phy, p); + + provider = devm_of_phy_provider_register(&pdev->dev, + of_phy_simple_xlate); + if (IS_ERR(provider)) + return dev_err_probe(&pdev->dev, PTR_ERR(provider), + "Failed to register PHY provider\n"); + + if (p->phy_idx == 0) { + ret = ma35_otg_role_switch_init(pdev, p); + if (ret) + return ret; + } + + return 0; +} + +static void ma35_otg_phy_remove(struct platform_device *pdev) +{ + struct ma35_otg_phy *p = platform_get_drvdata(pdev); + + ma35_otg_role_switch_exit(p); +} + +static const struct of_device_id ma35_otg_phy_of_match[] = { + { .compatible = "nuvoton,ma35d1-usb2-phy-otg" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ma35_otg_phy_of_match); + +static struct platform_driver ma35_otg_phy_driver = { + .probe = ma35_otg_phy_probe, + .remove = ma35_otg_phy_remove, + .driver = { + .name = "ma35d1-usb2-phy-otg", + .of_match_table = ma35_otg_phy_of_match, + }, +}; +module_platform_driver(ma35_otg_phy_driver); + +MODULE_DESCRIPTION("Nuvoton MA35D1 USB 2.0 OTG PHY driver"); +MODULE_AUTHOR("Joey Lu <a0987203069@gmail.com>"); +MODULE_LICENSE("GPL"); -- 2.43.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH 2/2] phy: nuvoton: Add MA35D1 USB2 OTG PHY driver 2026-06-04 10:12 ` [PATCH 2/2] phy: nuvoton: Add MA35D1 USB2 OTG PHY driver Joey Lu @ 2026-06-11 11:04 ` Vinod Koul 2026-06-12 5:53 ` Joey Lu 0 siblings, 1 reply; 7+ messages in thread From: Vinod Koul @ 2026-06-11 11:04 UTC (permalink / raw) To: Joey Lu Cc: Neil Armstrong, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jacky Huang, Shan-Chun Hung, linux-phy, devicetree, linux-arm-kernel, linux-kernel On 04-06-26, 18:12, Joey Lu wrote: > Add a PHY driver for the USB 2.0 PHYs in the Nuvoton MA35D1 SoC, > intended for use with the EHCI and OHCI host controllers. > > The MA35D1 SoC has two USB ports: > > - USB0: an OTG port shared between a DWC2 gadget controller and > EHCI0/OHCI0 host controllers. A hardware mux automatically routes > the physical USB0 signals to the appropriate controller based on the > USB ID pin state. The DWC2 IP is device-only in hardware, > so host-mode operation on USB0 is handled entirely by EHCI0/OHCI0. > > - USB1: a dedicated host-only port served by EHCI1/OHCI1. > > The driver implements: > - Power-On Reset sequence with a guard that skips re-initialization if > the PHY is already operational. This protects PHY0 when the DWC2 > gadget driver has already run its own init before EHCI0 probes. > - Optional resistor calibration trim via nuvoton,rcalcode. > - Optional over-current detect polarity via nuvoton,oc-active-high. > - For PHY0 only: a USB role switch that exposes the hardware ID pin > state (PWRONOTP[16]). > > Signed-off-by: Joey Lu <a0987203069@gmail.com> > --- > drivers/phy/nuvoton/Kconfig | 15 ++ > drivers/phy/nuvoton/Makefile | 1 + > drivers/phy/nuvoton/phy-ma35d1-otg.c | 264 +++++++++++++++++++++++++++ > 3 files changed, 280 insertions(+) > create mode 100644 drivers/phy/nuvoton/phy-ma35d1-otg.c > > diff --git a/drivers/phy/nuvoton/Kconfig b/drivers/phy/nuvoton/Kconfig > index d02cae2db315..5fdd13f841e7 100644 > --- a/drivers/phy/nuvoton/Kconfig > +++ b/drivers/phy/nuvoton/Kconfig > @@ -10,3 +10,18 @@ config PHY_MA35_USB > help > Enable this to support the USB2.0 PHY on the Nuvoton MA35 > series SoCs. > + > +config PHY_MA35_USB_OTG > + tristate "Nuvoton MA35 USB2.0 OTG PHY driver" > + depends on ARCH_MA35 || COMPILE_TEST > + depends on OF > + select GENERIC_PHY > + select MFD_SYSCON > + select USB_ROLE_SWITCH > + help > + Enable this to support the USB2.0 OTG PHY on the Nuvoton MA35 > + series SoCs. This driver handles PHY initialization for the > + EHCI/OHCI host controllers, including per-PHY power-on reset, > + resistor calibration trim, and over-current polarity > + configuration. For the OTG port (PHY0), it also monitors the > + USB ID pin and registers a USB role switch. > diff --git a/drivers/phy/nuvoton/Makefile b/drivers/phy/nuvoton/Makefile > index 2937e3921898..3ecd76f35d7c 100644 > --- a/drivers/phy/nuvoton/Makefile > +++ b/drivers/phy/nuvoton/Makefile > @@ -1,3 +1,4 @@ > # SPDX-License-Identifier: GPL-2.0 > > obj-$(CONFIG_PHY_MA35_USB) += phy-ma35d1-usb2.o > +obj-$(CONFIG_PHY_MA35_USB_OTG) += phy-ma35d1-otg.o Have you considered reusing usb2 driver with a different power_on function? Or handle the differences internally in the driver. There are few similarities in two and some things are different -- ~Vinod ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 2/2] phy: nuvoton: Add MA35D1 USB2 OTG PHY driver 2026-06-11 11:04 ` Vinod Koul @ 2026-06-12 5:53 ` Joey Lu 0 siblings, 0 replies; 7+ messages in thread From: Joey Lu @ 2026-06-12 5:53 UTC (permalink / raw) To: Vinod Koul Cc: Neil Armstrong, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jacky Huang, Shan-Chun Hung, linux-phy, devicetree, linux-arm-kernel, linux-kernel On 6/11/2026 7:04 PM, Vinod Koul wrote: > On 04-06-26, 18:12, Joey Lu wrote: >> Add a PHY driver for the USB 2.0 PHYs in the Nuvoton MA35D1 SoC, >> intended for use with the EHCI and OHCI host controllers. >> >> The MA35D1 SoC has two USB ports: >> >> - USB0: an OTG port shared between a DWC2 gadget controller and >> EHCI0/OHCI0 host controllers. A hardware mux automatically routes >> the physical USB0 signals to the appropriate controller based on the >> USB ID pin state. The DWC2 IP is device-only in hardware, >> so host-mode operation on USB0 is handled entirely by EHCI0/OHCI0. >> >> - USB1: a dedicated host-only port served by EHCI1/OHCI1. >> >> The driver implements: >> - Power-On Reset sequence with a guard that skips re-initialization if >> the PHY is already operational. This protects PHY0 when the DWC2 >> gadget driver has already run its own init before EHCI0 probes. >> - Optional resistor calibration trim via nuvoton,rcalcode. >> - Optional over-current detect polarity via nuvoton,oc-active-high. >> - For PHY0 only: a USB role switch that exposes the hardware ID pin >> state (PWRONOTP[16]). >> >> Signed-off-by: Joey Lu <a0987203069@gmail.com> >> --- >> drivers/phy/nuvoton/Kconfig | 15 ++ >> drivers/phy/nuvoton/Makefile | 1 + >> drivers/phy/nuvoton/phy-ma35d1-otg.c | 264 +++++++++++++++++++++++++++ >> 3 files changed, 280 insertions(+) >> create mode 100644 drivers/phy/nuvoton/phy-ma35d1-otg.c >> >> diff --git a/drivers/phy/nuvoton/Kconfig b/drivers/phy/nuvoton/Kconfig >> index d02cae2db315..5fdd13f841e7 100644 >> --- a/drivers/phy/nuvoton/Kconfig >> +++ b/drivers/phy/nuvoton/Kconfig >> @@ -10,3 +10,18 @@ config PHY_MA35_USB >> help >> Enable this to support the USB2.0 PHY on the Nuvoton MA35 >> series SoCs. >> + >> +config PHY_MA35_USB_OTG >> + tristate "Nuvoton MA35 USB2.0 OTG PHY driver" >> + depends on ARCH_MA35 || COMPILE_TEST >> + depends on OF >> + select GENERIC_PHY >> + select MFD_SYSCON >> + select USB_ROLE_SWITCH >> + help >> + Enable this to support the USB2.0 OTG PHY on the Nuvoton MA35 >> + series SoCs. This driver handles PHY initialization for the >> + EHCI/OHCI host controllers, including per-PHY power-on reset, >> + resistor calibration trim, and over-current polarity >> + configuration. For the OTG port (PHY0), it also monitors the >> + USB ID pin and registers a USB role switch. >> diff --git a/drivers/phy/nuvoton/Makefile b/drivers/phy/nuvoton/Makefile >> index 2937e3921898..3ecd76f35d7c 100644 >> --- a/drivers/phy/nuvoton/Makefile >> +++ b/drivers/phy/nuvoton/Makefile >> @@ -1,3 +1,4 @@ >> # SPDX-License-Identifier: GPL-2.0 >> >> obj-$(CONFIG_PHY_MA35_USB) += phy-ma35d1-usb2.o >> +obj-$(CONFIG_PHY_MA35_USB_OTG) += phy-ma35d1-otg.o > Have you considered reusing usb2 driver with a different power_on > function? Or handle the differences internally in the driver. There are > few similarities in two and some things are different Thank you for the excellent suggestion regarding reusing the existing USB2 driver. After further evaluation and local testing, I verified that it is entirely feasible to reuse the driver. Consequently, I will drop the separate phy-ma35d1-otg.c patch series and submit a new patch set that extends the existing phy-ma35d1-usb2.c mainline driver. In the upcoming patch series, I will expand the driver's capability from a single-port PHY0 peripheral driver to a dual-port manager supporting both PHY0 and PHY1, while integrating OTG features. BR, Joey > ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-06-12 5:53 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-06-04 10:12 [PATCH 0/2] phy: nuvoton: Add MA35D1 USB2 OTG PHY driver Joey Lu 2026-06-04 10:12 ` [PATCH 1/2] dt-bindings: phy: nuvoton: Add MA35D1 USB2 OTG PHY binding Joey Lu 2026-06-08 10:45 ` Krzysztof Kozlowski 2026-06-09 9:15 ` Joey Lu 2026-06-04 10:12 ` [PATCH 2/2] phy: nuvoton: Add MA35D1 USB2 OTG PHY driver Joey Lu 2026-06-11 11:04 ` Vinod Koul 2026-06-12 5:53 ` Joey Lu
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox