mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: "Jagielski, Jedrzej" <jedrzej.jagielski@intel.com>
To: James Hilliard <james.hilliard1@gmail.com>,
	Andrew Lunn <andrew+netdev@lunn.ch>,
	"David S. Miller" <davem@davemloft.net>,
	"Eric Dumazet" <edumazet@google.com>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
	Rob Herring <robh@kernel.org>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>, Lee Jones <lee@kernel.org>,
	Andrew Lunn <andrew@lunn.ch>,
	Heiner Kallweit <hkallweit1@gmail.com>,
	Russell King <linux@armlinux.org.uk>,
	Heiko Stuebner <heiko@sntech.de>
Cc: "netdev@vger.kernel.org" <netdev@vger.kernel.org>,
	"devicetree@vger.kernel.org" <devicetree@vger.kernel.org>,
	"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
	"mfd@lists.linux.dev" <mfd@lists.linux.dev>,
	"linux-arm-kernel@lists.infradead.org"
	<linux-arm-kernel@lists.infradead.org>,
	"linux-rockchip@lists.infradead.org"
	<linux-rockchip@lists.infradead.org>
Subject: RE: [PATCH net-next v2 4/8] net: phy: add X-Powers AC200 EPHY control driver
Date: Thu, 6 Aug 2026 09:19:34 +0000	[thread overview]
Message-ID: <PH0PR11MB5902DF2CE7FBA22964F76BC5F0D22@PH0PR11MB5902.namprd11.prod.outlook.com> (raw)
In-Reply-To: <20260804-submit-acx00-of-dynamic-v1-v2-4-3eef49ff1d8c@gmail.com>

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

>The AC200 Fast Ethernet PHY needs package registers in the parent I2C
>regmap to be configured before its Clause 22 endpoint becomes usable.
>
>Add a control driver which obtains the calibration value, applies the
>vendor offset, selects the documented 24 or 27 MHz input clock, programs
>the link PHY address and performs the required reset, clock and shutdown
>sequence. Use the optional SoC SID cell when supplied and otherwise read
>the AC200's internal calibration eFuse.
>
>Early PHY creation occurs before a MAC has attached, so use reset-default
>MII initially. A separate operation later applies phydev->interface; when
>the block is already powered it changes only RMII_SEL before the normal
>PHY soft reset.
>
>Expose serialized, idempotent power and interface operations to the
>common PHY driver. Enable only the MII I/O pads in this basic driver;
>dedicated LED outputs remain disabled until a later LED patch describes
>and manages them.
>
>Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
>---
> drivers/net/phy/Kconfig             |   9 ++
> drivers/net/phy/Makefile            |   1 +
> drivers/net/phy/xpowers-ac200-ctl.c | 294 ++++++++++++++++++++++++++++++++++++
> drivers/net/phy/xpowers-acx00.h     |  15 ++
> 4 files changed, 319 insertions(+)
>
>diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
>index a29d3fed8a05..6119f4de880d 100644
>--- a/drivers/net/phy/Kconfig
>+++ b/drivers/net/phy/Kconfig
>@@ -475,6 +475,15 @@ config VITESSE_PHY
> 	help
> 	  Currently supports the vsc8244
> 
>+config XPOWERS_AC200_PHY_CTL
>+	tristate "X-Powers AC200 Ethernet PHY control"
>+	depends on MFD_AC200
>+	help
>+	  Enable the control driver for the Fast Ethernet PHY function in
>+	  the X-Powers AC200 mixed-signal companion IC. It programs the PHY
>+	  address, interface mode, calibration and I/O controls through the
>+	  parent AC200 I2C regmap.
>+
> config XILINX_GMII2RGMII
> 	tristate "Xilinx GMII2RGMII converter driver"
> 	help
>diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
>index e23df5e836e9..f81854fc9f12 100644
>--- a/drivers/net/phy/Makefile
>+++ b/drivers/net/phy/Makefile
>@@ -100,4 +100,5 @@ obj-$(CONFIG_SMSC_PHY)		+= smsc.o
> obj-$(CONFIG_STE10XP)		+= ste10Xp.o
> obj-$(CONFIG_TERANETICS_PHY)	+= teranetics.o
> obj-$(CONFIG_VITESSE_PHY)	+= vitesse.o
>+obj-$(CONFIG_XPOWERS_AC200_PHY_CTL) += xpowers-ac200-ctl.o
> obj-$(CONFIG_XILINX_GMII2RGMII) += xilinx_gmii2rgmii.o
>diff --git a/drivers/net/phy/xpowers-ac200-ctl.c b/drivers/net/phy/xpowers-ac200-ctl.c
>new file mode 100644
>index 000000000000..c69e6a8c71a4
>--- /dev/null
>+++ b/drivers/net/phy/xpowers-ac200-ctl.c
>@@ -0,0 +1,294 @@
>+// SPDX-License-Identifier: GPL-2.0-only
>+/*
>+ * X-Powers AC200 Ethernet PHY control driver
>+ *
>+ * Copyright (c) 2022 Arm Ltd. (Andre Przywara <andre.przywara@arm.com>)
>+ * Copyright (C) 2026 James Hilliard <james.hilliard1@gmail.com>

Quite unclear for me what's Andre's role here
if he worked on that shouldn't he be mentioned in the
commit msg anyhow?

>+ */
>+
>+#include <linux/bitfield.h>
>+#include <linux/clk.h>
>+#include <linux/delay.h>
>+#include <linux/module.h>
>+#include <linux/mutex.h>
>+#include <linux/nvmem-consumer.h>
>+#include <linux/phy.h>
>+#include <linux/platform_device.h>
>+#include <linux/property.h>
>+#include <linux/regmap.h>
>+
>+#include "xpowers-acx00.h"
>+
>+#define AC200_EPHY_BPS_EFFUSE_OFFSET	3
>+
>+#define AC200_SYS_EPHY_CTL0_REG			0x0014
>+#define AC200_EPHY_RESET_DEASSERT		BIT(0)
>+#define AC200_EPHY_SYSCLK_ENABLE			BIT(1)
>+
>+#define AC200_SYS_EPHY_CTL1_REG			0x0016
>+#define AC200_EPHY_MII_IO_ENABLE			BIT(0)
>+
>+/* AC200-internal copy of the Ethernet PHY calibration eFuse. */
>+#define AC200_EFUSE_EPHY_REG			0x8004
>+
>+#define AC200_EPHY_CTL_REG			0x6000
>+#define AC200_EPHY_SHUTDOWN			BIT(0)
>+#define AC200_EPHY_CLK_SEL_24_MHZ		BIT(2)
>+#define AC200_EPHY_PHY_ADDR_MASK			GENMASK(8, 4)
>+#define AC200_EPHY_RMII_SEL			BIT(11)
>+#define AC200_EPHY_BPS_EFFUSE_MASK		GENMASK(15, 12)
>+
>+struct ac200_ephy_ctl {
>+	struct acx00_ephy_control control;
>+	struct regmap *regmap;
>+	struct mutex lock; /* Serializes power sequencing and state. */
>+	u16 ephy_ctl;
>+	unsigned int phy_addr;
>+	phy_interface_t interface;
>+	bool powered;
>+};
>+
>+static u16 ac200_ephy_ctl_config(const struct ac200_ephy_ctl *priv)
>+{
>+	return priv->ephy_ctl |
>+		(priv->interface == PHY_INTERFACE_MODE_RMII ?
>+		 AC200_EPHY_RMII_SEL : 0) |
>+		FIELD_PREP(AC200_EPHY_PHY_ADDR_MASK, priv->phy_addr);
>+}
>+
>+static int ac200_ephy_ctl_power_off_locked(struct ac200_ephy_ctl *priv)
>+{
>+	int err;
>+	int ret;
>+
>+	if (!priv->powered)
>+		return 0;
>+
>+	ret = regmap_write(priv->regmap, AC200_EPHY_CTL_REG,
>+			   ac200_ephy_ctl_config(priv) | AC200_EPHY_SHUTDOWN);

how about creating wrapper logging which register write failed?
currently there's no info at the end what's not ideal when debugging
especially as the're called in a sequence

>+	err = regmap_write(priv->regmap, AC200_SYS_EPHY_CTL1_REG, 0);
>+	if (!ret)
>+		ret = err;
>+	err = regmap_write(priv->regmap, AC200_SYS_EPHY_CTL0_REG, 0);
>+	if (!ret)
>+		ret = err;
>+
>+	priv->powered = false;
>+
>+	return ret;
>+}
>+
>+static int ac200_ephy_ctl_power_off(struct acx00_ephy_control *control)
>+{
>+	struct ac200_ephy_ctl *priv =
>+		container_of(control, struct ac200_ephy_ctl, control);
>+	int ret;
>+
>+	mutex_lock(&priv->lock);
>+	ret = ac200_ephy_ctl_power_off_locked(priv);
>+	mutex_unlock(&priv->lock);
>+
>+	return ret;
>+}
>+
>+static int
>+ac200_ephy_ctl_set_interface(struct acx00_ephy_control *control,
>+			     phy_interface_t interface)
>+{
>+	struct ac200_ephy_ctl *priv =
>+		container_of(control, struct ac200_ephy_ctl, control);
>+	u16 value;
>+	int ret = 0;
>+
>+	switch (interface) {
>+	case PHY_INTERFACE_MODE_MII:
>+		value = 0;
>+		break;
>+	case PHY_INTERFACE_MODE_RMII:
>+		value = AC200_EPHY_RMII_SEL;
>+		break;
>+	default:
>+		return -EINVAL;
>+	}

'value' can be moved into priv->powered branch since
it's used only there

>+
>+	mutex_lock(&priv->lock);
>+	if (priv->interface == interface)
>+		goto out_unlock;
>+
>+	if (priv->powered)
>+		ret = regmap_update_bits(priv->regmap, AC200_EPHY_CTL_REG,
>+					 AC200_EPHY_RMII_SEL, value);
>+	if (!ret)
>+		priv->interface = interface;
>+
>+out_unlock:
>+	mutex_unlock(&priv->lock);
>+
>+	return ret;
>+}
>+
>+static int ac200_ephy_ctl_power_on(struct acx00_ephy_control *control,
>+				   unsigned int phy_addr)
>+{
>+	struct ac200_ephy_ctl *priv =
>+		container_of(control, struct ac200_ephy_ctl, control);
>+	u16 ephy_ctl;
>+	int ret;
>+
>+	if (phy_addr > FIELD_MAX(AC200_EPHY_PHY_ADDR_MASK))
>+		return -EINVAL;
>+
>+	mutex_lock(&priv->lock);
>+	if (priv->powered && priv->phy_addr == phy_addr) {
>+		ret = 0;
>+		goto out_unlock;
>+	}
>+	if (priv->powered) {
>+		ret = ac200_ephy_ctl_power_off_locked(priv);
>+		if (ret)
>+			goto out_unlock;
>+	}
>+	priv->phy_addr = phy_addr;
>+
>+	ephy_ctl = ac200_ephy_ctl_config(priv);
>+
>+	/* Start from a disabled state before applying the configuration. */
>+	ret = regmap_write(priv->regmap, AC200_SYS_EPHY_CTL0_REG, 0);
>+	if (ret)
>+		goto err_disable;
>+
>+	ret = regmap_write(priv->regmap, AC200_SYS_EPHY_CTL1_REG,
>+			   AC200_EPHY_MII_IO_ENABLE);
>+	if (ret)
>+		goto err_disable;
>+
>+	ret = regmap_write(priv->regmap, AC200_EPHY_CTL_REG,
>+			   ephy_ctl | AC200_EPHY_SHUTDOWN);
>+	if (ret)
>+		goto err_disable;
>+
>+	ret = regmap_write(priv->regmap, AC200_SYS_EPHY_CTL0_REG,
>+			   AC200_EPHY_RESET_DEASSERT |
>+			   AC200_EPHY_SYSCLK_ENABLE);
>+	if (ret)
>+		goto err_disable;
>+

what does the value comes from?

>+	fsleep(10000);
>+
>+	ret = regmap_write(priv->regmap, AC200_EPHY_CTL_REG, ephy_ctl);
>+	if (ret)
>+		goto err_disable;
>+
>+	priv->powered = true;
>+	goto out_unlock;
>+
>+err_disable:
>+	/* Attempt every step of the shutdown sequence after a partial start. */
>+	priv->powered = true;
>+	ac200_ephy_ctl_power_off_locked(priv);
>+out_unlock:
>+	mutex_unlock(&priv->lock);
>+
>+	return ret;
>+}
>+
>+static int ac200_ephy_ctl_probe(struct platform_device *pdev)
>+{
>+	struct device *dev = &pdev->dev;
>+	struct ac200_ephy_ctl *priv;
>+	unsigned long clk_rate;
>+	unsigned int calibration;
>+	u8 nvmem_calibration;
>+	u8 bps_effuse_code;
>+	struct clk *clk;
>+	int ret;
>+
>+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
>+	if (!priv)
>+		return -ENOMEM;
>+	mutex_init(&priv->lock);
>+
>+	priv->regmap = dev_get_regmap(dev->parent, NULL);
>+	if (!priv->regmap)
>+		return dev_err_probe(dev, -EPROBE_DEFER,
>+				     "parent regmap is not ready\n");
>+
>+	if (device_property_present(dev, "nvmem-cells")) {
>+		ret = nvmem_cell_read_u8(dev, "calibration",
>+					 &nvmem_calibration);
>+		if (ret)
>+			return dev_err_probe(dev, ret,
>+					     "failed to read calibration data\n");
>+		calibration = nvmem_calibration;
>+	} else {
>+		ret = regmap_read(priv->regmap, AC200_EFUSE_EPHY_REG,
>+				  &calibration);
>+		if (ret)
>+			return dev_err_probe(dev, ret,
>+					     "failed to read on-chip calibration data\n");
>+	}
>+
>+	/* The vendor driver supplies no transfer function beyond this offset. */
>+	bps_effuse_code = (calibration + AC200_EPHY_BPS_EFFUSE_OFFSET) &
>+			   FIELD_MAX(AC200_EPHY_BPS_EFFUSE_MASK);
>+	priv->ephy_ctl =
>+		FIELD_PREP(AC200_EPHY_BPS_EFFUSE_MASK, bps_effuse_code);
>+	/* EPHY_MODE and BIST_CLK_EN stay clear for normal operation. */
>+
>+	clk = clk_get(dev->parent, NULL);
>+	if (IS_ERR(clk))
>+		return dev_err_probe(dev, PTR_ERR(clk),
>+				     "failed to get input clock\n");
>+
>+	clk_rate = clk_get_rate(clk);
>+	clk_put(clk);
>+
>+	switch (clk_rate) {
>+	case 24000000:
>+		priv->ephy_ctl |= AC200_EPHY_CLK_SEL_24_MHZ;
>+		break;
>+	case 27000000:
>+		break;
>+	default:
>+		return dev_err_probe(dev, -EINVAL,
>+				     "unsupported input clock rate %lu Hz\n",
>+				     clk_rate);
>+	}
>+
>+	priv->control.power_on = ac200_ephy_ctl_power_on;
>+	priv->control.power_off = ac200_ephy_ctl_power_off;
>+	priv->control.set_interface = ac200_ephy_ctl_set_interface;
>+	/* MII is the reset default used until the MAC supplies its interface. */
>+	priv->interface = PHY_INTERFACE_MODE_MII;
>+	platform_set_drvdata(pdev, &priv->control);
>+
>+	return 0;
>+}
>+
>+static void ac200_ephy_ctl_remove(struct platform_device *pdev)
>+{
>+	struct acx00_ephy_control *control = platform_get_drvdata(pdev);
>+
>+	control->power_off(control);
>+}
>+
>+static const struct of_device_id ac200_ephy_ctl_of_match[] = {
>+	{ .compatible = "x-powers,ac200-ephy-ctl" },
>+	{ }
>+};
>+MODULE_DEVICE_TABLE(of, ac200_ephy_ctl_of_match);
>+
>+static struct platform_driver ac200_ephy_ctl_driver = {
>+	.probe = ac200_ephy_ctl_probe,
>+	.remove = ac200_ephy_ctl_remove,
>+	.shutdown = ac200_ephy_ctl_remove,
>+	.driver = {
>+		.name = "ac200-ephy-ctl",
>+		.of_match_table = ac200_ephy_ctl_of_match,
>+	},
>+};
>+module_platform_driver(ac200_ephy_ctl_driver);
>+
>+MODULE_AUTHOR("James Hilliard <james.hilliard1@gmail.com>");
>+MODULE_DESCRIPTION("X-Powers AC200 Ethernet PHY control driver");
>+MODULE_LICENSE("GPL");
>diff --git a/drivers/net/phy/xpowers-acx00.h b/drivers/net/phy/xpowers-acx00.h
>new file mode 100644
>index 000000000000..482ba6faba6e
>--- /dev/null
>+++ b/drivers/net/phy/xpowers-acx00.h
>@@ -0,0 +1,15 @@
>+/* SPDX-License-Identifier: GPL-2.0-only */
>+#ifndef __DRIVERS_NET_PHY_XPOWERS_ACX00_H
>+#define __DRIVERS_NET_PHY_XPOWERS_ACX00_H
>+
>+#include <linux/phy.h>
>+
>+struct acx00_ephy_control {
>+	int (*power_on)(struct acx00_ephy_control *control,
>+			unsigned int phy_addr);
>+	int (*power_off)(struct acx00_ephy_control *control);
>+	int (*set_interface)(struct acx00_ephy_control *control,
>+			     phy_interface_t interface);
>+};
>+
>+#endif
>
>-- 
>2.53.0



  reply	other threads:[~2026-08-06  9:19 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-05  2:26 [PATCH net-next v2 0/8] net: phy: add X-Powers AC200/AC300 EPHY support James Hilliard
2026-08-05  2:26 ` [PATCH net-next v2 1/8] dt-bindings: net: x-powers: add AC200 EPHY control James Hilliard
2026-08-05  2:27 ` [PATCH net-next v2 2/8] dt-bindings: mfd: x-powers: add AC200 James Hilliard
2026-08-05  2:27 ` [PATCH net-next v2 3/8] mfd: add X-Powers AC200 support James Hilliard
2026-08-05  2:27 ` [PATCH net-next v2 4/8] net: phy: add X-Powers AC200 EPHY control driver James Hilliard
2026-08-06  9:19   ` Jagielski, Jedrzej [this message]
2026-08-05  2:27 ` [PATCH net-next v2 5/8] dt-bindings: net: x-powers: add AC300 EPHY control James Hilliard
2026-08-05  3:38   ` Rob Herring (Arm)
2026-08-05 14:17   ` Rob Herring
2026-08-06  1:36     ` James Hilliard
2026-08-05  2:27 ` [PATCH net-next v2 6/8] net: phy: add X-Powers AC300 EPHY control driver James Hilliard
2026-08-05 12:22   ` Andrew Lunn
2026-08-06  9:20   ` Jagielski, Jedrzej
2026-08-05  2:27 ` [PATCH net-next v2 7/8] dt-bindings: net: x-powers: add AC200/AC300 EPHY James Hilliard
2026-08-05  2:27 ` [PATCH net-next v2 8/8] net: phy: add X-Powers AC200/AC300 EPHY driver James Hilliard
2026-08-05 12:37   ` Andrew Lunn
2026-08-06  1:28     ` James Hilliard
2026-08-06  9:21       ` Jagielski, Jedrzej

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=PH0PR11MB5902DF2CE7FBA22964F76BC5F0D22@PH0PR11MB5902.namprd11.prod.outlook.com \
    --to=jedrzej.jagielski@intel.com \
    --cc=andrew+netdev@lunn.ch \
    --cc=andrew@lunn.ch \
    --cc=conor+dt@kernel.org \
    --cc=davem@davemloft.net \
    --cc=devicetree@vger.kernel.org \
    --cc=edumazet@google.com \
    --cc=heiko@sntech.de \
    --cc=hkallweit1@gmail.com \
    --cc=james.hilliard1@gmail.com \
    --cc=krzk+dt@kernel.org \
    --cc=kuba@kernel.org \
    --cc=lee@kernel.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-rockchip@lists.infradead.org \
    --cc=linux@armlinux.org.uk \
    --cc=mfd@lists.linux.dev \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=robh@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®