lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250626212321.28114-7-ansuelsmth@gmail.com>
Date: Thu, 26 Jun 2025 23:23:05 +0200
From: Christian Marangi <ansuelsmth@...il.com>
To: Christian Marangi <ansuelsmth@...il.com>,
	Lee Jones <lee@...nel.org>,
	Rob Herring <robh@...nel.org>,
	Krzysztof Kozlowski <krzk+dt@...nel.org>,
	Conor Dooley <conor+dt@...nel.org>,
	Andrew Lunn <andrew+netdev@...n.ch>,
	"David S. Miller" <davem@...emloft.net>,
	Eric Dumazet <edumazet@...gle.com>,
	Jakub Kicinski <kuba@...nel.org>,
	Paolo Abeni <pabeni@...hat.com>,
	Vladimir Oltean <olteanv@...il.com>,
	Srinivas Kandagatla <srini@...nel.org>,
	Heiner Kallweit <hkallweit1@...il.com>,
	Russell King <linux@...linux.org.uk>,
	"Chester A. Unal" <chester.a.unal@...nc9.com>,
	Daniel Golle <daniel@...rotopia.org>,
	DENG Qingfang <dqfext@...il.com>,
	Sean Wang <sean.wang@...iatek.com>,
	Simon Horman <horms@...nel.org>,
	Matthias Brugger <matthias.bgg@...il.com>,
	AngeloGioacchino Del Regno <angelogioacchino.delregno@...labora.com>,
	linux-arm-kernel@...ts.infradead.org,
	linux-mediatek@...ts.infradead.org,
	netdev@...r.kernel.org,
	devicetree@...r.kernel.org,
	linux-kernel@...r.kernel.org
Subject: [net-next PATCH v15 06/12] net: mdio: Add Airoha AN8855 Switch MDIO PBUS

Add Airoha AN8855 MDIO PBUS driver that permits to access the internal
Gigabit PHY from the Switch register.

This have the benefits of exposing direct access to CL45 address and
Vendor MDIO pages via specific Switch registers.

Additional info are present in a long explaination in the MDIO driver
and also finding from Reverse-Engineering the implementation.

This requires the upper Switch MFD to be probed and init to actually
work as it does make use of regmap.

Signed-off-by: Christian Marangi <ansuelsmth@...il.com>
---
 drivers/net/mdio/Kconfig       |  10 ++
 drivers/net/mdio/Makefile      |   1 +
 drivers/net/mdio/mdio-an8855.c | 262 +++++++++++++++++++++++++++++++++
 include/linux/dsa/an8855.h     |  18 +++
 4 files changed, 291 insertions(+)
 create mode 100644 drivers/net/mdio/mdio-an8855.c
 create mode 100644 include/linux/dsa/an8855.h

diff --git a/drivers/net/mdio/Kconfig b/drivers/net/mdio/Kconfig
index 7db40aaa079d..7f8f0b5caa42 100644
--- a/drivers/net/mdio/Kconfig
+++ b/drivers/net/mdio/Kconfig
@@ -42,6 +42,16 @@ config MDIO_XGENE
 	  This module provides a driver for the MDIO busses found in the
 	  APM X-Gene SoC's.
 
+config MDIO_AN8855
+	tristate "Airoha AN8855 Switch MDIO bus controller"
+	depends on MFD_AIROHA_AN8855
+	depends on OF_MDIO
+	select MDIO_REGMAP
+	help
+	  This module provides a driver for the Airoha AN8855 Switch
+	  that requires a MDIO passtrough as switch address is shared
+	  with the internal PHYs and requires additional page handling.
+
 config MDIO_ASPEED
 	tristate "ASPEED MDIO bus controller"
 	depends on ARCH_ASPEED || COMPILE_TEST
diff --git a/drivers/net/mdio/Makefile b/drivers/net/mdio/Makefile
index c23778e73890..2b9edddf3911 100644
--- a/drivers/net/mdio/Makefile
+++ b/drivers/net/mdio/Makefile
@@ -5,6 +5,7 @@ obj-$(CONFIG_ACPI_MDIO)		+= acpi_mdio.o
 obj-$(CONFIG_FWNODE_MDIO)	+= fwnode_mdio.o
 obj-$(CONFIG_OF_MDIO)		+= of_mdio.o
 
+obj-$(CONFIG_MDIO_AN8855)		+= mdio-an8855.o
 obj-$(CONFIG_MDIO_ASPEED)		+= mdio-aspeed.o
 obj-$(CONFIG_MDIO_BCM_IPROC)		+= mdio-bcm-iproc.o
 obj-$(CONFIG_MDIO_BCM_UNIMAC)		+= mdio-bcm-unimac.o
diff --git a/drivers/net/mdio/mdio-an8855.c b/drivers/net/mdio/mdio-an8855.c
new file mode 100644
index 000000000000..990cf683b470
--- /dev/null
+++ b/drivers/net/mdio/mdio-an8855.c
@@ -0,0 +1,262 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * MDIO PBUS driver for Airoha AN8855 Switch
+ *
+ * Author: Christian Marangi <ansuelsmth@...il.com>
+ *
+ */
+
+#include <linux/dsa/an8855.h>
+#include <linux/module.h>
+#include <linux/of_mdio.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+/* AN8855 permit to access the internal GPHY via the PBUS
+ * interface.
+ *
+ * Some piece of this comes from Reverse-Enginnering
+ * by applying the value on the Switch and observing
+ * it by reading the raw value on the MDIO BUS.
+ *
+ * The CL22 address are shifted by 4 left
+ * The CL44 address need to be multiplied by 4 (and
+ * no shift)
+ *
+ * The GPHY have additional configuration (like auto
+ * downshift) at PAGE 1 in the EXT register.
+ * It was discovered that it's possible to access
+ * PAGE x address by increasing them by 2 on setting
+ * the value in the related mask.
+ * The PHY have these custom/vendor register
+ * always starting at 0x10.
+ * From MDIO bus, for address 0x0 to 0xf, PHY
+ * always report PAGE 0 values.
+ * From PBUS, on setting the PAGE value, 0x0 to 0xf
+ * always report 0.
+ *
+ * (it can also be notice that PBUS does NOT change the
+ *  page on accessing these custom/vendor register)
+ *
+ * Comparison examples:
+ *	(PORT 0 PAGE 1)		  |	(PORT 0 PAGE 2)
+ *  PBUS ADDR	  VALUE	    MDIO  |  PBUS ADDR	  VALUE	    MDIO
+ * 0xa0803000: 0x00000000  0x1840 | 0xa0804000: 0x00000000  0x1840
+ * 0xa0803010: 0x00000000  0x7949 | 0xa0804010: 0x00000000  0x7949
+ * 0xa0803020: 0x00000000  0xc0ff | 0xa0804020: 0x00000000  0xc0ff
+ * 0xa0803030: 0x00000000  0x0410 | 0xa0804030: 0x00000000  0x0410
+ * 0xa0803040: 0x00000000  0x0de1 | 0xa0804040: 0x00000000  0x0de1
+ * 0xa0803050: 0x00000000  0x0000 | 0xa0804050: 0x00000000  0x0000
+ * 0xa0803060: 0x00000000  0x0004 | 0xa0804060: 0x00000000  0x0004
+ * 0xa0803070: 0x00000000  0x2001 | 0xa0804070: 0x00000000  0x2001
+ * 0xa0803080: 0x00000000  0x0000 | 0xa0804080: 0x00000000  0x0000
+ * 0xa0803090: 0x00000000  0x0200 | 0xa0804090: 0x00000000  0x0200
+ * 0xa08030a0: 0x00000000  0x4000 | 0xa08040a0: 0x00000000  0x4000
+ * 0xa08030b0: 0x00000000  0x0000 | 0xa08040b0: 0x00000000  0x0000
+ * 0xa08030c0: 0x00000000  0x0000 | 0xa08040c0: 0x00000000  0x0000
+ * 0xa08030d0: 0x00000000  0x0000 | 0xa08040d0: 0x00000000  0x0000
+ * 0xa08030e0: 0x00000000  0x0000 | 0xa08040e0: 0x00000000  0x0000
+ * 0xa08030f0: 0x00000000  0x2000 | 0xa08040f0: 0x00000000  0x2000
+ * 0xa0803100: 0x00000000  0x0000 | 0xa0804100: 0x00000000  0x0000
+ * 0xa0803110: 0x00000000  0x0000 | 0xa0804110: 0x0000030f  0x030f
+ * 0xa0803120: 0x00000000  0x0000 | 0xa0804120: 0x00000000  0x0000
+ * 0xa0803130: 0x00000030  0x0030 | 0xa0804130: 0x00000000  0x0000
+ * 0xa0803140: 0x00003a14  0x3a14 | 0xa0804140: 0x00000000  0x0000
+ * 0xa0803150: 0x00000101  0x0101 | 0xa0804150: 0x00000000  0x0000
+ * 0xa0803160: 0x00000001  0x0001 | 0xa0804160: 0x00000000  0x0000
+ * 0xa0803170: 0x00000800  0x0800 | 0xa0804170: 0x000001ff  0x01ff
+ * 0xa0803180: 0x00000000  0x0000 | 0xa0804180: 0x0000ff1f  0xff1f
+ * 0xa0803190: 0x0000001f  0x001f | 0xa0804190: 0x000083ff  0x83ff
+ * 0xa08031a0: 0x00000000  0x0000 | 0xa08041a0: 0x00000000  0x0000
+ * 0xa08031b0: 0x00000000  0x0000 | 0xa08041b0: 0x00000000  0x0000
+ * 0xa08031c0: 0x00003001  0x3001 | 0xa08041c0: 0x00000000  0x0000
+ * 0xa08031d0: 0x00000000  0x0000 | 0xa08041d0: 0x00000000  0x0000
+ * 0xa08031e0: 0x00000000  0x0000 | 0xa08041e0: 0x00000000  0x0000
+ * 0xa08031f0: 0x00000000  0x0001 | 0xa08041f0: 0x00000000  0x0002
+ *
+ * Using the PBUS permits to have consistent access
+ * to Switch and PHY without having to relay on checking
+ * pages.
+ *
+ * It does also permit to cut on CL45 access and PAGE1
+ * access as the PBUS expose direct register for them.
+ *
+ * The base address is 0xa0800000 and can be seen as
+ * bitmap to derive each specific address.
+ *
+ * Example:
+ *   PORT 1 ADDR 0x2 --> 0xa1800020
+ *			    ^    ^
+ *			    |    ADDR
+ *			    PORT
+ *   PORT 2 DEVAD 1 ADDR 0x2 --> 0xa2840008
+ *				    ^ ^   ^
+ *				    | |   ADDR (*4)
+ *				    | DEVAD
+ *				    PORT
+ *   PORT 3 PAGE 1 ADDR 0x14 --> 0xa3803140
+ *				    ^  ^^^
+ *				    |  |ADDR
+ *				    |  PAGE (+2)
+ *				    PORT
+ *
+ * It's worth mention that trying to read more than the
+ * expected PHY address cause the PBUS to ""crash"" and
+ * the Switch to lock out (requiring a reset).
+ * Validation of the port value is put to prevent this
+ * problem.
+ */
+
+struct an8855_mdio_priv {
+	struct regmap *regmap;
+	u32 base_addr;
+	u8 next_page;
+};
+
+static int an8855_mdio_read(struct mii_bus *bus, int addr, int regnum)
+{
+	struct an8855_mdio_priv *priv = bus->priv;
+	u32 pbus_addr = AN8855_GPHY_ACCESS;
+	u32 port = addr - priv->base_addr;
+	u32 val;
+	int ret;
+
+	/* Signal invalid address for mdio tools */
+	if (port >= AN8855_NUM_PHY_PORT)
+		return 0xffff;
+
+	pbus_addr |= FIELD_PREP(AN8855_GPHY_PORT, port);
+	pbus_addr |= FIELD_PREP(AN8855_CL22_ADDR, regnum);
+	if (priv->next_page)
+		pbus_addr |= FIELD_PREP(AN8855_PAGE_SELECT,
+					priv->next_page + 2);
+
+	ret = regmap_read(priv->regmap, pbus_addr, &val);
+	if (ret)
+		return ret;
+
+	return val & 0xffff;
+}
+
+static int an8855_mdio_write(struct mii_bus *bus, int addr, int regnum,
+			     u16 value)
+{
+	struct an8855_mdio_priv *priv = bus->priv;
+	u32 pbus_addr = AN8855_GPHY_ACCESS;
+	u32 port = addr - priv->base_addr;
+
+	if (port >= AN8855_NUM_PHY_PORT)
+		return -EINVAL;
+
+	/* When PHY ask to change page, skip writing it and
+	 * save it for the next read/write.
+	 */
+	if (regnum == AN8855_PHY_SELECT_PAGE) {
+		priv->next_page = value;
+		return 0;
+	}
+
+	pbus_addr |= FIELD_PREP(AN8855_GPHY_PORT, port);
+	pbus_addr |= FIELD_PREP(AN8855_CL22_ADDR, regnum);
+	if (priv->next_page)
+		pbus_addr |= FIELD_PREP(AN8855_PAGE_SELECT,
+					priv->next_page + 2);
+
+	return regmap_write(priv->regmap, pbus_addr, value);
+}
+
+static int an8855_mdio_cl45_read(struct mii_bus *bus, int addr, int devnum,
+				 int regnum)
+{
+	struct an8855_mdio_priv *priv = bus->priv;
+	u32 pbus_addr = AN8855_GPHY_ACCESS;
+	u32 port = addr - priv->base_addr;
+	u32 val;
+	int ret;
+
+	/* Signal invalid address for mdio tools */
+	if (port >= AN8855_NUM_PHY_PORT)
+		return 0xffff;
+
+	pbus_addr |= FIELD_PREP(AN8855_GPHY_PORT, port);
+	pbus_addr |= FIELD_PREP(AN8855_DEVAD_ADDR, devnum);
+	pbus_addr |= FIELD_PREP(AN8855_CL45_ADDR, regnum * 4);
+
+	ret = regmap_read(priv->regmap, pbus_addr, &val);
+	if (ret)
+		return ret;
+
+	return val & 0xffff;
+}
+
+static int an8855_mdio_cl45_write(struct mii_bus *bus, int addr, int devnum,
+				  int regnum, u16 value)
+{
+	struct an8855_mdio_priv *priv = bus->priv;
+	u32 pbus_addr = AN8855_GPHY_ACCESS;
+	u32 port = addr - priv->base_addr;
+
+	if (port >= AN8855_NUM_PHY_PORT)
+		return -EINVAL;
+
+	pbus_addr |= FIELD_PREP(AN8855_GPHY_PORT, port);
+	pbus_addr |= FIELD_PREP(AN8855_DEVAD_ADDR, devnum);
+	pbus_addr |= FIELD_PREP(AN8855_CL45_ADDR, regnum * 4);
+
+	return regmap_write(priv->regmap, pbus_addr, value);
+}
+
+static int an8855_mdio_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct an8855_mdio_priv *priv;
+	struct mii_bus *bus;
+	int ret;
+
+	bus = devm_mdiobus_alloc_size(dev, sizeof(*priv));
+	if (!bus)
+		return -ENOMEM;
+
+	priv = bus->priv;
+	priv->regmap = dev_get_regmap(dev->parent, NULL);
+	if (!priv->regmap)
+		return -ENOENT;
+
+	ret = of_property_read_u32(dev->parent->of_node, "reg",
+				   &priv->base_addr);
+	if (ret)
+		return -EINVAL;
+
+	bus->name = "an8855_mdio_bus";
+	snprintf(bus->id, MII_BUS_ID_SIZE, "%s-gphy-pbus", dev_name(dev));
+	bus->parent = dev;
+	bus->read = an8855_mdio_read;
+	bus->write = an8855_mdio_write;
+	bus->read_c45 = an8855_mdio_cl45_read;
+	bus->write_c45 = an8855_mdio_cl45_write;
+
+	ret = devm_of_mdiobus_register(dev, bus, dev->of_node);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to register MDIO bus\n");
+
+	return 0;
+}
+
+static const struct of_device_id an8855_mdio_of_match[] = {
+	{ .compatible = "airoha,an8855-mdio", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, an8855_mdio_of_match);
+
+static struct platform_driver an8855_mdio_driver = {
+	.probe	= an8855_mdio_probe,
+	.driver = {
+		.name = "an8855-mdio",
+		.of_match_table = an8855_mdio_of_match,
+	},
+};
+module_platform_driver(an8855_mdio_driver);
+
+MODULE_AUTHOR("Christian Marangi <ansuelsmth@...il.com>");
+MODULE_DESCRIPTION("Driver for AN8855 MDIO passthrough");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/dsa/an8855.h b/include/linux/dsa/an8855.h
new file mode 100644
index 000000000000..32ec29b3abb0
--- /dev/null
+++ b/include/linux/dsa/an8855.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _NET_AN8855_H
+#define _NET_AN8855_H
+
+#define AN8855_GPHY_ACCESS		0xa0800000
+#define	  AN8855_GPHY_PORT		GENMASK(26, 24)
+#define	  AN8855_DEVAD_ADDR		GENMASK(23, 18)
+#define	  AN8855_PAGE_SELECT		GENMASK(14, 12)
+#define	  AN8855_ADDR			GENMASK(11, 0)
+#define	    AN8855_CL45_ADDR		AN8855_ADDR
+#define	    AN8855_CL22_ADDR		GENMASK(8, 4)
+
+#define AN8855_PHY_SELECT_PAGE		0x1f
+
+#define AN8855_NUM_PHY_PORT		5
+
+#endif /* _NET_AN8855_H */
-- 
2.48.1


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ