[PATCH net-next v2 8/8] net: phy: add X-Powers AC200/AC300 EPHY driver

Jagielski, Jedrzej jedrzej.jagielski at intel.com
Thu Aug 6 02:21:26 PDT 2026


From: James Hilliard <james.hilliard1 at gmail.com> 
Sent: Wednesday, August 5, 2026 4:27 AM

>The AC200 and AC300 MDIO link endpoints report the same Clause 22
>identifier and use a compatible link-side paged register layout. Their
>identification registers are inaccessible until a package-specific
>control path powers and configures the block.
>
>Add a common PHY driver which matches the firmware-provided identifier
>only when the node also references one X-Powers control device. Validate
>that reference and the AC300 address relationship, defer until the
>provider is bound, and create a managed device link to protect its
>lifetime and order teardown and power management.
>
>When the optional two-bit SID configuration field is present, verify that
>its package-selector bit agrees with the fixed control reference. Use its
>second bit to select the AC300 low-calibration tuning sequence. Omitting
>the field skips package validation and selects the normal sequence.
>
>Power the control block in reset-default MII mode during PHY probe. Once
>the MAC supplies phydev->interface, pass MII or RMII to the provider
>before the normal PHY soft reset. Program the AC200 link address through
>its provider and let the AC300 provider validate its strapped address.
>
>Apply the common vendor analog initialization, with the package-specific
>normal value and the optional AC300 low-calibration sequence. Support the
>board-selected xMII receive-clock inversion and leave MDI/MDI-X in its
>vendor automatic mode. Disable standard and Intelligent EEE initially,
>then restore phylib's standard EEE policy after later resets.
>
>Power the package control block down with the PHY and restore the vendor
>configuration on resume. If power-off fails, attempt to restore the PHY
>before returning the original suspend error.
>
>Signed-off-by: James Hilliard <james.hilliard1 at gmail.com>
>---
> drivers/net/phy/Kconfig         |   9 +
> drivers/net/phy/Makefile        |   1 +
> drivers/net/phy/xpowers-acx00.c | 544 ++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 554 insertions(+)
>

...

>+static struct device *
>+acx00_ephy_find_supplier(struct device_node *control, bool is_ac300)
>+{
>+	struct platform_device *pdev;
>+	struct mdio_device *mdiodev;
>+
>+	if (is_ac300) {

two approaches are mixed now - specific callbacks and ac200/300 checks,
why not to fully stick to callbacks


>+		mdiodev = of_mdio_find_device(control);
>+		return mdiodev ? &mdiodev->dev : NULL;
>+	}
>+
>+	pdev = of_find_device_by_node(control);
>+	return pdev ? &pdev->dev : NULL;
>+}
>+
>+static int acx00_ephy_get_control(struct phy_device *phydev,
>+				  struct acx00_ephy_priv *priv)
>+{
>+	struct device *dev = &phydev->mdio.dev;
>+	struct acx00_ephy_control *control;
>+	struct device_node *control_node;
>+	struct device *supplier;
>+	struct device_link *link;
>+	const char *compatible;
>+	const char *property;
>+	bool has_ac200;
>+	bool has_ac300;
>+	u32 control_addr;
>+	u32 configuration = 0;
>+	int ret;
>+
>+	has_ac200 = of_property_present(dev->of_node,
>+					"x-powers,ac200-control");
>+	has_ac300 = of_property_present(dev->of_node,
>+					"x-powers,ac300-control");
>+	if (has_ac200 == has_ac300)
>+		return dev_err_probe(dev, -EINVAL,
>+				     "exactly one ACx00 control is required\n");
>+
>+	priv->is_ac300 = has_ac300;

wouldn't enum be a better choice here? easier extending for future
possible ac400,500 etc 
dunno whether planned just thinking

>+	property = has_ac300 ? "x-powers,ac300-control" :
>+			       "x-powers,ac200-control";
>+	compatible = has_ac300 ? "x-powers,ac300-ephy-ctl" :
>+				 "x-powers,ac200-ephy-ctl";
>+	control_node = of_parse_phandle(dev->of_node, property, 0);
>+	if (!control_node)
>+		return dev_err_probe(dev, -EINVAL, "missing %s\n", property);
>+	if (!of_device_is_compatible(control_node, compatible)) {
>+		ret = dev_err_probe(dev, -EINVAL,
>+				    "%s does not reference a %s device\n",
>+				    property, compatible);
>+		goto out_put_node;
>+	}
>+	if (!of_device_is_available(control_node)) {
>+		ret = dev_err_probe(dev, -EINVAL,
>+				    "%s references a disabled device\n",
>+				    property);
>+		goto out_put_node;
>+	}
>+
>+	if (has_ac300) {
>+		ret = of_property_read_u32(control_node, "reg", &control_addr);
>+		if (ret || control_addr != phydev->mdio.addr +
>+					       AC300_EPHY_CONTROL_ADDR_OFFSET) {
>+			ret = dev_err_probe(dev, -EINVAL,
>+					    "AC300 control address does not match reg\n");
>+			goto out_put_node;
>+		}
>+	}




More information about the linux-arm-kernel mailing list