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

"Jagielski, Jedrzej" <[email protected]> Thu, 6 Aug 2026 09:18:31 +0000
Newsgroups dev.linux.lists.mfd,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel,org.kernel.vger.netdev
Message-ID <PH0PR11MB590277BD30A0D65438E4C5CBF0D22@PH0PR11MB5902.namprd11.prod.outlook.com>
From: James Hilliard <[email protected]> 
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 <[email protected]>
>---
> 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 <[email protected]>");
>+MODULE_DESCRIPTION("X-Powers AC200 MFD core driver");
>+MODULE_LICENSE("GPL");
>
>-- 
>2.53.0