RE: [PATCH net-next v2 8/8] net: phy: add X-Powers AC200/AC300 EPHY driver
"Jagielski, Jedrzej" <[email protected]> Thu, 6 Aug 2026 09:21:26 +0000
| Newsgroups | dev.linux.lists.mfd,org.infradead.lists.linux-arm-kernel,org.infradead.lists.linux-rockchip,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel,org.kernel.vger.netdev |
|---|---|
| Message-ID | <PH0PR11MB5902F56C72ED0C31020B277CF0D22@PH0PR11MB5902.namprd11.prod.outlook.com> |
From: James Hilliard <[email protected]> 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 <[email protected]> >--- > 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; >+ } >+ }