[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