[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20241021130209.15660-5-ansuelsmth@gmail.com>
Date: Mon, 21 Oct 2024 15:01:59 +0200
From: Christian Marangi <ansuelsmth@...il.com>
To: Christian Marangi <ansuelsmth@...il.com>,
Andrew Lunn <andrew@...n.ch>,
Florian Fainelli <f.fainelli@...il.com>,
Vladimir Oltean <olteanv@...il.com>,
"David S. Miller" <davem@...emloft.net>,
Eric Dumazet <edumazet@...gle.com>,
Jakub Kicinski <kuba@...nel.org>,
Paolo Abeni <pabeni@...hat.com>,
Rob Herring <robh@...nel.org>,
Krzysztof Kozlowski <krzk+dt@...nel.org>,
Conor Dooley <conor+dt@...nel.org>,
Heiner Kallweit <hkallweit1@...il.com>,
Russell King <linux@...linux.org.uk>,
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 RFC PATCH 4/4] net: phy: Add Airoha AN8855 Internal Switch Gigabit PHY
Add support for Airoha AN8855 Internal Switch Gigabit PHY.
This is a simple PHY driver to configure and calibrate the PHY for the
AN8855 Switch.
Signed-off-by: Christian Marangi <ansuelsmth@...il.com>
---
MAINTAINERS | 1 +
drivers/net/phy/Kconfig | 5 +
drivers/net/phy/Makefile | 1 +
drivers/net/phy/air_an8855.c | 187 +++++++++++++++++++++++++++++++++++
4 files changed, 194 insertions(+)
create mode 100644 drivers/net/phy/air_an8855.c
diff --git a/MAINTAINERS b/MAINTAINERS
index e3077d9feee2..cf34add2a0bb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -726,6 +726,7 @@ S: Maintained
F: Documentation/devicetree/bindings/net/dsa/airoha,an8855.yaml
F: drivers/net/dsa/an8855.c
F: drivers/net/dsa/an8855.h
+F: drivers/net/phy/air_an8855.c
AIROHA ETHERNET DRIVER
M: Lorenzo Bianconi <lorenzo@...nel.org>
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index ee3ea0b56d48..1d474038ea7f 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -79,6 +79,11 @@ config SFP
comment "MII PHY device drivers"
+config AIR_AN8855_PHY
+ tristate "Airoha AN8855 Internal Gigabit PHY"
+ help
+ Currently supports the internal Airoha AN8855 Switch PHY.
+
config AIR_EN8811H_PHY
tristate "Airoha EN8811H 2.5 Gigabit PHY"
help
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 90f886844381..baba7894785b 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -35,6 +35,7 @@ obj-y += $(sfp-obj-y) $(sfp-obj-m)
obj-$(CONFIG_ADIN_PHY) += adin.o
obj-$(CONFIG_ADIN1100_PHY) += adin1100.o
+obj-$(CONFIG_AIR_AN8855_PHY) += air_an8855.o
obj-$(CONFIG_AIR_EN8811H_PHY) += air_en8811h.o
obj-$(CONFIG_AMD_PHY) += amd.o
obj-$(CONFIG_AMCC_QT2025_PHY) += qt2025.o
diff --git a/drivers/net/phy/air_an8855.c b/drivers/net/phy/air_an8855.c
new file mode 100644
index 000000000000..d40b0b17a2e4
--- /dev/null
+++ b/drivers/net/phy/air_an8855.c
@@ -0,0 +1,187 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <linux/phy.h>
+#include <linux/module.h>
+#include <linux/bitfield.h>
+
+#define AN8855_PHY_PAGE_CTRL 0x1f
+#define AN8855_PHY_NORMAL_PAGE 0x0
+#define AN8855_PHY_EXT_PAGE 0x1
+
+#define AN8855_PHY_EXT_REG_14 0x14
+#define AN8855_PHY_EN_DOWN_SHFIT BIT(4)
+
+/* R50 Calibration regs in MDIO_MMD_VEND1 */
+#define AN8855_PHY_R500HM_RSEL_TX_AB 0x174
+#define AN8855_PHY_R50OHM_RSEL_TX_A_EN BIT(15)
+#define AN8855_PHY_R50OHM_RSEL_TX_A GENMASK(14, 8)
+#define AN8855_PHY_R50OHM_RSEL_TX_B_EN BIT(7)
+#define AN8855_PHY_R50OHM_RSEL_TX_B GENMASK(6, 0)
+#define AN8855_PHY_R500HM_RSEL_TX_CD 0x175
+#define AN8855_PHY_R50OHM_RSEL_TX_C_EN BIT(15)
+#define AN8855_PHY_R50OHM_RSEL_TX_C GENMASK(14, 8)
+#define AN8855_PHY_R50OHM_RSEL_TX_D_EN BIT(7)
+#define AN8855_PHY_R50OHM_RSEL_TX_D GENMASK(6, 0)
+
+/* PHY TX PAIR DELAY SELECT Register */
+#define PHY_TX_PAIR_DLY_SEL_GBE 0x013
+/* PHY ADC Register */
+#define PHY_RXADC_CTRL 0x0d8
+#define PHY_RXADC_REV_0 0x0d9
+#define PHY_RXADC_REV_1 0x0da
+
+#define AN8855_PHY_ID 0xc0ff0410
+
+static int an8855_config_init(struct phy_device *phydev)
+{
+ u8 calibration_data[4];
+ int ret;
+
+ memcpy(calibration_data, &phydev->dev_flags, sizeof(u32));
+
+ /* Enable HW auto downshift */
+ ret = phy_write(phydev, AN8855_PHY_PAGE_CTRL, AN8855_PHY_EXT_PAGE);
+ if (ret)
+ return ret;
+ ret = phy_set_bits(phydev, AN8855_PHY_EXT_REG_14,
+ AN8855_PHY_EN_DOWN_SHFIT);
+ if (ret)
+ return ret;
+ ret = phy_write(phydev, AN8855_PHY_PAGE_CTRL, AN8855_PHY_NORMAL_PAGE);
+ if (ret)
+ return ret;
+
+ /* Enable Asymmetric Pause Capability */
+ ret = phy_set_bits(phydev, MII_ADVERTISE, ADVERTISE_PAUSE_ASYM);
+ if (ret)
+ return ret;
+
+ /* Disable EEE */
+ ret = phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0);
+ if (ret)
+ return ret;
+
+ /* Apply calibration values, if needed. */
+ if (phydev->dev_flags) {
+ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, AN8855_PHY_R500HM_RSEL_TX_AB,
+ AN8855_PHY_R50OHM_RSEL_TX_A | AN8855_PHY_R50OHM_RSEL_TX_B,
+ FIELD_PREP(AN8855_PHY_R50OHM_RSEL_TX_A, calibration_data[0]) |
+ FIELD_PREP(AN8855_PHY_R50OHM_RSEL_TX_B, calibration_data[1]));
+ if (ret)
+ return ret;
+ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, AN8855_PHY_R500HM_RSEL_TX_CD,
+ AN8855_PHY_R50OHM_RSEL_TX_C | AN8855_PHY_R50OHM_RSEL_TX_D,
+ FIELD_PREP(AN8855_PHY_R50OHM_RSEL_TX_C, calibration_data[2]) |
+ FIELD_PREP(AN8855_PHY_R50OHM_RSEL_TX_D, calibration_data[3]));
+ if (ret)
+ return ret;
+ }
+
+ /* Apply values to decude signal noise */
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, PHY_TX_PAIR_DLY_SEL_GBE, 0x4040);
+ if (ret)
+ return ret;
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, PHY_RXADC_CTRL, 0x1010);
+ if (ret)
+ return ret;
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, PHY_RXADC_REV_0, 0x100);
+ if (ret)
+ return ret;
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, PHY_RXADC_REV_1, 0x100);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int an8855_get_downshift(struct phy_device *phydev, u8 *data)
+{
+ int val;
+ int ret;
+
+ ret = phy_write(phydev, AN8855_PHY_PAGE_CTRL, AN8855_PHY_EXT_PAGE);
+ if (ret)
+ return ret;
+
+ val = phy_read(phydev, AN8855_PHY_EXT_REG_14);
+ *data = val & AN8855_PHY_EXT_REG_14 ? DOWNSHIFT_DEV_DEFAULT_COUNT :
+ DOWNSHIFT_DEV_DISABLE;
+
+ ret = phy_write(phydev, AN8855_PHY_PAGE_CTRL, AN8855_PHY_NORMAL_PAGE);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int an8855_set_downshift(struct phy_device *phydev, u8 cnt)
+{
+ int ret;
+
+ ret = phy_write(phydev, AN8855_PHY_PAGE_CTRL, AN8855_PHY_EXT_PAGE);
+ if (ret)
+ return ret;
+
+ if (cnt != DOWNSHIFT_DEV_DISABLE) {
+ ret = phy_set_bits(phydev, AN8855_PHY_EXT_REG_14,
+ AN8855_PHY_EN_DOWN_SHFIT);
+ if (ret)
+ return ret;
+ } else {
+ ret = phy_clear_bits(phydev, AN8855_PHY_EXT_REG_14,
+ AN8855_PHY_EN_DOWN_SHFIT);
+ if (ret)
+ return ret;
+ }
+
+ return phy_write(phydev, AN8855_PHY_PAGE_CTRL, AN8855_PHY_NORMAL_PAGE);
+}
+
+static int an8855_get_tunable(struct phy_device *phydev,
+ struct ethtool_tunable *tuna, void *data)
+{
+ switch (tuna->id) {
+ case ETHTOOL_PHY_DOWNSHIFT:
+ return an8855_get_downshift(phydev, data);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int an8855_set_tunable(struct phy_device *phydev,
+ struct ethtool_tunable *tuna, const void *data)
+{
+ switch (tuna->id) {
+ case ETHTOOL_PHY_DOWNSHIFT:
+ return an8855_set_downshift(phydev, *(const u8 *)data);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static struct phy_driver An8855_driver[] = {
+{
+ PHY_ID_MATCH_EXACT(AN8855_PHY_ID),
+ .name = "Airoha AN8855 internal PHY",
+ /* PHY_GBIT_FEATURES */
+ .flags = PHY_IS_INTERNAL,
+ .config_init = an8855_config_init,
+ .soft_reset = genphy_soft_reset,
+ .get_tunable = an8855_get_tunable,
+ .set_tunable = an8855_set_tunable,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+}, };
+
+module_phy_driver(An8855_driver);
+
+static struct mdio_device_id __maybe_unused An8855_tbl[] = {
+ { PHY_ID_MATCH_EXACT(AN8855_PHY_ID) },
+ { }
+};
+
+MODULE_DEVICE_TABLE(mdio, An8855_tbl);
+
+MODULE_DESCRIPTION("Airoha AN8855 PHY driver");
+MODULE_AUTHOR("Christian Marangi <ansuelsmth@...il.com>");
+MODULE_LICENSE("GPL");
--
2.45.2
Powered by blists - more mailing lists