[PATCH net-next v3 0/4] net: phy: add X-Powers AC200/AC300 EPHY support

James Hilliard posted 4 patches 1 month, 3 weeks ago
There is a newer version of this series
.../devicetree/bindings/mfd/x-powers,ac200.yaml    |  69 +++
.../bindings/net/x-powers,acx00-ephy-package.yaml  | 208 ++++++++
drivers/mfd/Kconfig                                |  11 +
drivers/mfd/Makefile                               |   1 +
drivers/mfd/ac200.c                                | 184 +++++++
drivers/net/phy/Kconfig                            |  11 +
drivers/net/phy/Makefile                           |   3 +
drivers/net/phy/xpowers-acx00-ac200.c              | 374 ++++++++++++++
drivers/net/phy/xpowers-acx00-ac300.c              | 415 ++++++++++++++++
drivers/net/phy/xpowers-acx00-main.c               | 536 +++++++++++++++++++++
drivers/net/phy/xpowers-acx00.h                    |  28 ++
11 files changed, 1840 insertions(+)
[PATCH net-next v3 0/4] net: phy: add X-Powers AC200/AC300 EPHY support
Posted by James Hilliard 1 month, 3 weeks ago
The AC200 and AC300 contain compatible Fast Ethernet link PHYs which
report the same Clause 22 identifier and use the same link-side register
layout. The link endpoint is inaccessible until package-specific control
registers have powered and configured it.

Version 2 represented those control ranges as separate devices. Following
review, this revision instead models each variant as a standard Ethernet
PHY package with one forced-ID link-PHY child. The package reg value is the
link address. AC300's control range is a fixed package-relative offset of
16 and is accessed with the PHY package helpers; it has no separate DT node
or MDIO driver. AC200 references its I2C MFD because the corresponding
control registers reside in that multi-function device.

Fixed hardware uses an AC200- or AC300-specific package compatible. Systems
which can contain either package use the ACx00 package compatible and one
packed SID configuration field. Bits 3 through 0 carry the analog
calibration, bit 8 selects AC300, and bit 9 selects its low-calibration
tuning. The driver chooses the backend before acquiring any backend-specific
resource, so an AC300 system does not instantiate or access the AC200 I2C
device.

One xpowers-acx00 PHY module binds the link child, joins the parent package
and runs the selected AC200 or AC300 backend. Only the link PHY registers a
driver. The backend source files are linked into the same module and merely
keep the I2C and MDIO implementations separate. Thus the PHY driver owns the
complete Ethernet PHY while the AC200 MFD continues to own the shared
mixed-signal chip and its regmap.

The series contains no generic MDIO reconfiguration. It has no hard
CONFIG_OF_DYNAMIC dependency: fixed descriptions work when their provider
path is already enabled. When CONFIG_OF_DYNAMIC is available, the AC200
backend can activate an explicitly marked fail-needs-probe I2C/MFD path
after the packed field selects AC200. The AC300 path leaves that candidate
disabled.

The common link implementation performs the vendor analog initialization,
supports MII and RMII, preserves automatic MDI/MDI-X, and restores package
state across suspend and resume. It preserves standard MAC-managed EEE
advertisement while disabling only the vendor PHY-autonomous Intelligent
EEE mode.

The four patches add the minimal AC200 MFD binding and regmap provider,
then the AC200/AC300 PHY-package binding and combined PHY driver. Board
Device Trees and optional PHY features remain outside this initial series.

The AC200 portions build on earlier work by Jernej Skrabec and Andre
Przywara:

  https://github.com/jernejsk/linux-1/commits/ac200-v4

Public AC200 and AC300 documentation is linked from:

  https://linux-sunxi.org/AC200

Validation completed for this revision:

  - arm64 defconfig vmlinux and module builds with W=1;
  - x86_64 allmodconfig object builds with W=1;
  - a built-in AC300-only configuration with I2C disabled;
  - dt_binding_check for both new schemas; and
  - strict checkpatch checks for all new source files.

Hardware-tested on an H616 board containing AC300. The generic package
driver read the packed SID field as 0x106, selected AC300 without enabling
the AC200 I2C path, accessed the control range at package base plus 16,
bound the link PHY at address 0, applied RMII mode and negotiated a 100 Mbps
full-duplex link. Bidirectional network traffic and cold-boot testing
passed. The AC200 backend is build-tested but was not runtime-tested in
this revision.

Assisted-by: OpenAI Codex (gpt-5.6-sol, max)
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
Changes v2 -> v3:
  - model AC200 and AC300 as standard Ethernet PHY packages
  - remove the standalone AC200 and AC300 control bindings and drivers
  - put the common link implementation and both private backends in one
    xpowers-acx00 module
  - access the AC300 control range at package base plus 16 with PHY package
    helpers
  - access AC200 package registers through its referenced MFD regmap and a
    managed device link
  - add an ACx00 package compatible which selects the backend from one
    packed SID configuration field
  - combine calibration, package selection and AC300 tuning into that field
  - optionally activate only a selected fail-needs-probe AC200 path when
    CONFIG_OF_DYNAMIC is available
  - preserve standard MAC-managed EEE advertisement and disable only the
    PHY-autonomous Intelligent EEE mode
  - reduce the series from eight patches to four
  - Link to v2:
    https://patch.msgid.link/20260804-submit-acx00-of-dynamic-v1-v2-0-3eef49ff1d8c@gmail.com

---
James Hilliard (4):
      dt-bindings: mfd: x-powers: add AC200
      mfd: add X-Powers AC200 support
      dt-bindings: net: x-powers: add AC200/AC300 EPHY packages
      net: phy: add X-Powers AC200/AC300 EPHY driver

 .../devicetree/bindings/mfd/x-powers,ac200.yaml    |  69 +++
 .../bindings/net/x-powers,acx00-ephy-package.yaml  | 208 ++++++++
 drivers/mfd/Kconfig                                |  11 +
 drivers/mfd/Makefile                               |   1 +
 drivers/mfd/ac200.c                                | 184 +++++++
 drivers/net/phy/Kconfig                            |  11 +
 drivers/net/phy/Makefile                           |   3 +
 drivers/net/phy/xpowers-acx00-ac200.c              | 374 ++++++++++++++
 drivers/net/phy/xpowers-acx00-ac300.c              | 415 ++++++++++++++++
 drivers/net/phy/xpowers-acx00-main.c               | 536 +++++++++++++++++++++
 drivers/net/phy/xpowers-acx00.h                    |  28 ++
 11 files changed, 1840 insertions(+)
---
base-commit: a23b36233d4103def55dc8cf65698106d0bd1e62
change-id: 20260802-submit-acx00-of-dynamic-v1-94a0dc15f282

Best regards,
--  
James Hilliard <james.hilliard1@gmail.com>
RE: [PATCH net-next v3 0/4] net: phy: add X-Powers AC200/AC300 EPHY support
Posted by Jagielski, Jedrzej 1 month, 3 weeks ago
From: James Hilliard <james.hilliard1@gmail.com> 
Sent: Wednesday, August 5, 2026 4:27 AM

>The X-Powers AC200 is a mixed-signal companion IC with a paged register
>map accessed over I2C.
>
>Enable the package supplies and input clock, prevent the clock rate from
>changing, and apply the vendor settling delays around common reset.
>Initialize the paged regmap, report the chip and package revision, and
>instantiate the Ethernet PHY control child when firmware describes it.
>
>Cache only the common page selector. Individual function resets can
>invalidate other registers without regmap's knowledge, so all functional
>registers remain volatile.
>
>The AC200 and its children cannot initiate DMA. Mark the parent as
>DMA-incapable before adding the child. Register the common-reset action
>before the MFD child so managed teardown removes the child before
>resetting its parent, and also reset the chip during system shutdown.
>
>Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
>---
> drivers/mfd/Kconfig  |  12 +++
> drivers/mfd/Makefile |   1 +
> drivers/mfd/ac200.c  | 207 +++++++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 220 insertions(+)
>
>diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>index 763ce6a34782..3360c9b86be8 100644
>--- a/drivers/mfd/Kconfig
>+++ b/drivers/mfd/Kconfig
>@@ -205,6 +205,18 @@ config MFD_AC100
> 	  This driver include only the core APIs. You have to select individual
> 	  components like codecs or RTC under the corresponding menus.
> 
>+config MFD_AC200
>+	tristate "X-Powers AC200"
>+	depends on I2C
>+	depends on OF
>+	select MFD_CORE
>+	select REGMAP_I2C
>+	help
>+	  Support for the X-Powers AC200 mixed-signal companion IC. The AC200
>+	  contains audio, video, RTC and Fast Ethernet PHY functions and is
>+	  co-packaged with some Allwinner H6 and H616 SoCs. This driver provides
>+	  the shared register access used by the individual function drivers.
>+
> config MFD_AXP20X
> 	tristate
> 	select MFD_CORE
>diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
>index dd4bb7e77c33..890e76a9ad00 100644
>--- a/drivers/mfd/Makefile
>+++ b/drivers/mfd/Makefile
>@@ -150,6 +150,7 @@ obj-$(CONFIG_MFD_DA9052_SPI)	+= da9052-spi.o
> obj-$(CONFIG_MFD_DA9052_I2C)	+= da9052-i2c.o
> 
> obj-$(CONFIG_MFD_AC100)		+= ac100.o
>+obj-$(CONFIG_MFD_AC200)		+= ac200.o
> obj-$(CONFIG_MFD_AXP20X)	+= axp20x.o
> obj-$(CONFIG_MFD_AXP20X_I2C)	+= axp20x-i2c.o
> obj-$(CONFIG_MFD_AXP20X_RSB)	+= axp20x-rsb.o
>diff --git a/drivers/mfd/ac200.c b/drivers/mfd/ac200.c
>new file mode 100644

...

>+static void ac200_disable_action(void *data)
>+{
>+	ac200_disable(data);
>+}

Hi James

any particular reason why ac200_disable cannot be called direcrly?
couldn't find anywhere extending this later in the series

>+
>+static int ac200_probe(struct i2c_client *client)
>+{
>+	struct device *dev = &client->dev;
>+	struct device_node *ephy_node __free(device_node) = NULL;
>+	struct ac200 *ac200;
>+	struct clk *clk;
>+	unsigned int version;
>+	int ret;

i believe it would be nice to stick to RCT
especially for netdev targeted pacthes 

>+
>+	ac200 = devm_kzalloc(dev, sizeof(*ac200), GFP_KERNEL);
>+	if (!ac200)
>+		return -ENOMEM;
>+
>+	ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ac200_supplies),
>+					     ac200_supplies);
>+	if (ret)
>+		return dev_err_probe(dev, ret, "failed to enable supplies\n");
>+
>+	clk = devm_clk_get_enabled(dev, NULL);
>+	if (IS_ERR(clk))
>+		return dev_err_probe(dev, PTR_ERR(clk),
>+				     "failed to enable input clock\n");
>+
>+	ret = devm_clk_rate_exclusive_get(dev, clk);
>+	if (ret)
>+		return dev_err_probe(dev, ret, "failed to lock clock rate\n");
>+
>+	ac200->regmap = devm_regmap_init_i2c(client, &ac200_regmap_config);
>+	if (IS_ERR(ac200->regmap))
>+		return dev_err_probe(dev, PTR_ERR(ac200->regmap),
>+				     "failed to initialize regmap\n");
>+
>+	i2c_set_clientdata(client, ac200);
>+
>+	/*
>+	 * No minimum delay is documented. Match the vendor driver's 40 ms delay
>+	 * before its first AC200 register access after enabling the input clock.
>+	 */
>+	msleep(40);
>+
>+	ret = regmap_read(ac200->regmap, AC200_SYS_VERSION_REG, &version);
>+	if (ret)
>+		return dev_err_probe(dev, ret,
>+				     "failed to read chip version\n");
>+
>+	dev_info(dev, "AC200 revision %#lx in package %lu\n",
>+		 FIELD_GET(AC200_SYS_VERSION_CHIP_MASK, version),
>+		 FIELD_GET(AC200_SYS_VERSION_PACKAGE_MASK, version));
>+
>+	/* Run after the MFD children have been removed. */
>+	ret = devm_add_action_or_reset(dev, ac200_disable_action, ac200);
>+	if (ret)
>+		return ret;
>+
>+	ret = regmap_write(ac200->regmap, AC200_SYS_CONTROL_REG, 0);
>+	if (ret)
>+		return ret;
>+
>+	ret = regmap_write(ac200->regmap, AC200_SYS_CONTROL_REG,
>+			   AC200_SYS_CONTROL_CHIP_RESET_DEASSERT);
>+	if (ret)
>+		return ret;
>+
>+	/* Match the settling interval used by the vendor initialization. */
>+	usleep_range(1000, 2000);
>+
>+	/* Neither the AC200 nor its child devices can perform DMA. */
>+	dev->coherent_dma_mask = 0;
>+	dev->dma_mask = &dev->coherent_dma_mask;
>+	ephy_node = of_get_compatible_child(dev->of_node,
>+					    "x-powers,ac200-ephy-ctl");
>+	if (!ephy_node)
>+		return 0;
>+
>+	ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, ac200_cells,
>+				   ARRAY_SIZE(ac200_cells), NULL, 0, NULL);
>+	if (ret)
>+		return dev_err_probe(dev, ret, "failed to add MFD devices\n");
>+
>+	return 0;
>+}
>+
>+static void ac200_shutdown(struct i2c_client *client)
>+{
>+	struct ac200 *ac200 = i2c_get_clientdata(client);
>+
>+	ac200_disable(ac200);
>+}
>+
>+static const struct of_device_id ac200_of_match[] = {
>+	{ .compatible = "x-powers,ac200" },
>+	{ }
>+};
>+MODULE_DEVICE_TABLE(of, ac200_of_match);
>+
>+static const struct i2c_device_id ac200_i2c_ids[] = {
>+	{ "ac200" },
>+	{ }
>+};
>+MODULE_DEVICE_TABLE(i2c, ac200_i2c_ids);
>+
>+static struct i2c_driver ac200_driver = {
>+	.driver = {
>+		.name = "ac200",
>+		.of_match_table = ac200_of_match,
>+	},
>+	.probe = ac200_probe,
>+	.shutdown = ac200_shutdown,
>+	.id_table = ac200_i2c_ids,
>+};
>+module_i2c_driver(ac200_driver);
>+
>+MODULE_AUTHOR("James Hilliard <james.hilliard1@gmail.com>");
>+MODULE_DESCRIPTION("X-Powers AC200 MFD core driver");
>+MODULE_LICENSE("GPL");
>
>-- 
>2.53.0