From: Stefan Eichenberger <eichest@gmail.com>
To: netdev@vger.kernel.org, andrew@lunn.ch, hkallweit1@gmail.com,
linux@armlinux.org.uk
Cc: davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
pabeni@redhat.com, francesco.dolcini@toradex.com
Subject: [PATCH v1 2/2] net: phy: marvell-88q2xxx: add driver for the Marvell 88Q2110 PHY
Date: Mon, 3 Jul 2023 14:44:40 +0200 [thread overview]
Message-ID: <20230703124440.391970-3-eichest@gmail.com> (raw)
In-Reply-To: <20230703124440.391970-1-eichest@gmail.com>
Add a driver for the Marvell 88Q2110. This driver is minimalistic, but
already allows to detect the link, switch between 100BASE-T1 and
1000BASE-T1 and switch between master and slave mode. Autonegotiation
supported by the PHY is not yet implemented.
Signed-off-by: Stefan Eichenberger <eichest@gmail.com>
---
drivers/net/phy/Kconfig | 6 +
drivers/net/phy/Makefile | 1 +
drivers/net/phy/marvell-88q2xxx.c | 217 ++++++++++++++++++++++++++++++
3 files changed, 224 insertions(+)
create mode 100644 drivers/net/phy/marvell-88q2xxx.c
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 93b8efc792273..2913b145d5406 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -223,6 +223,12 @@ config MARVELL_88X2222_PHY
Support for the Marvell 88X2222 Dual-port Multi-speed Ethernet
Transceiver.
+config MARVELL_88Q2XXX_PHY
+ tristate "Marvell 88Q2XXX PHY"
+ help
+ Support for the Marvell Automotive 88Q2XXX 100/1000BASE-T1 Ethernet
+ PHYs.
+
config MAXLINEAR_GPHY
tristate "Maxlinear Ethernet PHYs"
select POLYNOMIAL if HWMON
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index f289ab16a1dab..15d1908fd5cb7 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_LXT_PHY) += lxt.o
obj-$(CONFIG_MARVELL_10G_PHY) += marvell10g.o
obj-$(CONFIG_MARVELL_PHY) += marvell.o
obj-$(CONFIG_MARVELL_88X2222_PHY) += marvell-88x2222.o
+obj-$(CONFIG_MARVELL_88Q2XXX_PHY) += marvell-88q2xxx.o
obj-$(CONFIG_MAXLINEAR_GPHY) += mxl-gpy.o
obj-$(CONFIG_MEDIATEK_GE_PHY) += mediatek-ge.o
obj-$(CONFIG_MESON_GXL_PHY) += meson-gxl.o
diff --git a/drivers/net/phy/marvell-88q2xxx.c b/drivers/net/phy/marvell-88q2xxx.c
new file mode 100644
index 0000000000000..637697e9f19fa
--- /dev/null
+++ b/drivers/net/phy/marvell-88q2xxx.c
@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Marvell 88Q2XXX automotive 100BASE-T1/1000BASE-T1 PHY driver
+ */
+
+#include <linux/marvell_phy.h>
+#include <linux/phy.h>
+#include <linux/ethtool_netlink.h>
+
+#define MARVELL_PHY_ID_88Q2110 0x002b0981
+
+static int mv88q2xxx_soft_reset(struct phy_device *phydev)
+{
+ phy_write_mmd(phydev, 3, 0x0900, 0x8000);
+
+ return 0;
+}
+
+static void init_fast_ethernet(struct phy_device *phydev)
+{
+ u16 value = phy_read_mmd(phydev, 1, 0x0834);
+
+ value = value & 0xFFF0;
+
+ phy_write_mmd(phydev, 1, 0x0834, value);
+}
+
+static void init_gbit_ethernet(struct phy_device *phydev)
+{
+ u16 value = phy_read_mmd(phydev, 1, 0x0834);
+
+ value = (value & 0xFFF0) | 0x0001;
+
+ phy_write_mmd(phydev, 1, 0x0834, value);
+}
+
+static int setup_master_slave(struct phy_device *phydev)
+{
+ u16 reg_data = phy_read_mmd(phydev, 1, 0x0834);
+
+ switch (phydev->master_slave_set) {
+ case MASTER_SLAVE_CFG_MASTER_FORCE:
+ case MASTER_SLAVE_CFG_MASTER_PREFERRED:
+ reg_data |= 0x4000;
+ break;
+ case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
+ case MASTER_SLAVE_CFG_SLAVE_FORCE:
+ reg_data &= ~0x4000;
+ break;
+ case MASTER_SLAVE_CFG_UNKNOWN:
+ case MASTER_SLAVE_CFG_UNSUPPORTED:
+ return 0;
+ default:
+ phydev_warn(phydev, "Unsupported Master/Slave mode\n");
+ return -EOPNOTSUPP;
+ }
+
+ phy_write_mmd(phydev, 1, 0x0834, reg_data);
+
+ return 0;
+}
+
+static int mv88q2xxx_config_aneg(struct phy_device *phydev)
+{
+ int ret;
+
+ if (phydev->speed == SPEED_100)
+ init_fast_ethernet(phydev);
+ else if (phydev->speed == SPEED_1000)
+ init_gbit_ethernet(phydev);
+
+ ret = setup_master_slave(phydev);
+ if (ret)
+ return ret;
+
+ mv88q2xxx_soft_reset(phydev);
+
+ return 0;
+}
+
+static int mv88q2xxx_config_init(struct phy_device *phydev)
+{
+ return mv88q2xxx_config_aneg(phydev);
+}
+
+static int get_speed(struct phy_device *phydev)
+{
+ u16 value = 0;
+
+ if (phydev->autoneg)
+ value = (phy_read_mmd(phydev, 7, 0x801a) & 0x4000) >> 14;
+ else
+ value = (phy_read_mmd(phydev, 1, 0x0834) & 0x1);
+
+ return value ? SPEED_1000 : SPEED_100;
+}
+
+static int check_link(struct phy_device *phydev)
+{
+ u16 ret1, ret2;
+
+ if (phydev->speed == SPEED_1000) {
+ ret1 = phy_read_mmd(phydev, 3, 0x0901);
+ ret1 = phy_read_mmd(phydev, 3, 0x0901);
+ ret2 = phy_read_mmd(phydev, 7, 0x8001);
+ } else {
+ ret1 = phy_read_mmd(phydev, 3, 0x8109);
+ ret2 = phy_read_mmd(phydev, 3, 0x8108);
+ }
+
+ return (0x0 != (ret1 & 0x0004)) && (0x0 != (ret2 & 0x3000)) ? 1 : 0;
+}
+
+static int read_master_slave(struct phy_device *phydev)
+{
+ int reg;
+
+ phydev->master_slave_get = MASTER_SLAVE_CFG_UNKNOWN;
+ phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
+
+ reg = phy_read_mmd(phydev, 7, 0x8001);
+ if (reg & (1 << 14)) {
+ phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_FORCE;
+ phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER;
+ } else {
+ phydev->master_slave_get = MASTER_SLAVE_CFG_SLAVE_FORCE;
+ phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE;
+ }
+
+ return 0;
+}
+
+static int mv88q2xxx_read_status(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = genphy_update_link(phydev);
+ if (ret)
+ return ret;
+
+ phydev->link = check_link(phydev);
+ phydev->speed = get_speed(phydev);
+
+ ret = read_master_slave(phydev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int mv88q2xxx_probe(struct phy_device *phydev)
+{
+ if (!phydev->is_c45)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int mv88q2xxxx_cable_test_start(struct phy_device *phydev)
+{
+ return 0;
+}
+
+static int mv88q2xxxx_cable_test_get_status(struct phy_device *phydev,
+ bool *finished)
+{
+ ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,
+ ETHTOOL_A_CABLE_RESULT_CODE_OK);
+ return 0;
+}
+
+static int mv88q2xxxx_get_sqi(struct phy_device *phydev)
+{
+ u16 value;
+
+ if (phydev->speed == SPEED_100)
+ value = (phy_read_mmd(phydev, 3, 0x8230) >> 12) & 0x0F;
+ else
+ value = phy_read_mmd(phydev, 3, 0xfc88) & 0x0F;
+
+ return value;
+}
+
+static int mv88q2xxxx_get_sqi_max(struct phy_device *phydev)
+{
+ return 15;
+}
+
+static struct phy_driver mv88q2xxx_driver[] = {
+ {
+ .phy_id = MARVELL_PHY_ID_88Q2110,
+ .phy_id_mask = MARVELL_PHY_ID_MASK,
+ .features = PHY_GBIT_T1_FEATURES,
+ .name = "mv88q2110",
+ .probe = mv88q2xxx_probe,
+ .soft_reset = mv88q2xxx_soft_reset,
+ .config_init = mv88q2xxx_config_init,
+ .read_status = mv88q2xxx_read_status,
+ .config_aneg = mv88q2xxx_config_aneg,
+ .cable_test_start = mv88q2xxxx_cable_test_start,
+ .cable_test_get_status = mv88q2xxxx_cable_test_get_status,
+ .set_loopback = genphy_c45_loopback,
+ .get_sqi = mv88q2xxxx_get_sqi,
+ .get_sqi_max = mv88q2xxxx_get_sqi_max,
+ },
+};
+
+module_phy_driver(mv88q2xxx_driver);
+
+static struct mdio_device_id __maybe_unused mv88q2xxx_tbl[] = {
+ { MARVELL_PHY_ID_88Q2110, MARVELL_PHY_ID_MASK },
+ { /*sentinel*/ },
+};
+MODULE_DEVICE_TABLE(mdio, mv88q2xxx_tbl);
+
+MODULE_DESCRIPTION("Marvell Automotive 100BASE-T1/1000BASE-T1 Ethernet PHY driver (MV88Q2xxx)");
+MODULE_LICENSE("GPL");
--
2.39.2
next prev parent reply other threads:[~2023-07-03 12:44 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-07-03 12:44 [PATCH v1 0/2] Add a driver for the Marvell 88Q2110 PHY Stefan Eichenberger
2023-07-03 12:44 ` [PATCH v1 1/2] net: phy: add the link modes for 1000BASE-T1 Ethernet PHY Stefan Eichenberger
2023-07-03 12:44 ` Stefan Eichenberger [this message]
2023-07-03 13:46 ` [PATCH v1 2/2] net: phy: marvell-88q2xxx: add driver for the Marvell 88Q2110 PHY Andrew Lunn
2023-07-03 15:17 ` Francesco Dolcini
2023-07-03 15:27 ` Francesco Dolcini
2023-07-13 6:31 ` Russell King (Oracle)
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=20230703124440.391970-3-eichest@gmail.com \
--to=eichest@gmail.com \
--cc=andrew@lunn.ch \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=francesco.dolcini@toradex.com \
--cc=hkallweit1@gmail.com \
--cc=kuba@kernel.org \
--cc=linux@armlinux.org.uk \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
/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;
as well as URLs for NNTP newsgroup(s).