drivers/net/phy: add driver for the onsemi NCN26000 10BASE-T1S PHY
authorPiergiorgio Beruto <piergiorgio.beruto@gmail.com>
Mon, 9 Jan 2023 17:00:38 +0000 (18:00 +0100)
committerDavid S. Miller <davem@davemloft.net>
Wed, 11 Jan 2023 08:35:03 +0000 (08:35 +0000)
This patch adds support for the onsemi NCN26000 10BASE-T1S industrial
Ethernet PHY. The driver supports Point-to-Multipoint operation without
auto-negotiation and with link control handling. The PHY also features
PLCA for improving performance in P2MP mode.

Signed-off-by: Piergiorgio Beruto <piergiorgio.beruto@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
MAINTAINERS
drivers/net/phy/Kconfig
drivers/net/phy/Makefile
drivers/net/phy/ncn26000.c [new file with mode: 0644]

index 8880701..ba2e1fe 100644 (file)
@@ -15584,6 +15584,13 @@ L:     linux-mips@vger.kernel.org
 S:     Maintained
 F:     arch/mips/boot/dts/ralink/omega2p.dts
 
+ONSEMI ETHERNET PHY DRIVERS
+M:     Piergiorgio Beruto <piergiorgio.beruto@gmail.com>
+L:     netdev@vger.kernel.org
+S:     Supported
+W:     http://www.onsemi.com
+F:     drivers/net/phy/ncn*
+
 OP-TEE DRIVER
 M:     Jens Wiklander <jens.wiklander@linaro.org>
 L:     op-tee@lists.trustedfirmware.org
index 1327290..f5df2ed 100644 (file)
@@ -277,6 +277,13 @@ config NXP_TJA11XX_PHY
        help
          Currently supports the NXP TJA1100 and TJA1101 PHY.
 
+config NCN26000_PHY
+       tristate "Onsemi 10BASE-T1S Ethernet PHY"
+       help
+         Adds support for the onsemi 10BASE-T1S Ethernet PHY.
+         Currently supports the NCN26000 10BASE-T1S Industrial PHY
+         with MII interface.
+
 config AT803X_PHY
        tristate "Qualcomm Atheros AR803X PHYs and QCA833x PHYs"
        depends on REGULATOR
index f7138d3..b513806 100644 (file)
@@ -77,6 +77,7 @@ obj-$(CONFIG_MICROCHIP_T1_PHY)        += microchip_t1.o
 obj-$(CONFIG_MICROSEMI_PHY)    += mscc/
 obj-$(CONFIG_MOTORCOMM_PHY)    += motorcomm.o
 obj-$(CONFIG_NATIONAL_PHY)     += national.o
+obj-$(CONFIG_NCN26000_PHY)     += ncn26000.o
 obj-$(CONFIG_NXP_C45_TJA11XX_PHY)      += nxp-c45-tja11xx.o
 obj-$(CONFIG_NXP_TJA11XX_PHY)  += nxp-tja11xx.o
 obj-$(CONFIG_QSEMI_PHY)                += qsemi.o
diff --git a/drivers/net/phy/ncn26000.c b/drivers/net/phy/ncn26000.c
new file mode 100644 (file)
index 0000000..5680584
--- /dev/null
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/*
+ *  Driver for the onsemi 10BASE-T1S NCN26000 PHYs family.
+ *
+ * Copyright 2022 onsemi
+ */
+#include <linux/kernel.h>
+#include <linux/bitfield.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+
+#include "mdio-open-alliance.h"
+
+#define PHY_ID_NCN26000                        0x180FF5A1
+
+#define NCN26000_REG_IRQ_CTL            16
+#define NCN26000_REG_IRQ_STATUS         17
+
+// the NCN26000 maps link_ctrl to BMCR_ANENABLE
+#define NCN26000_BCMR_LINK_CTRL_BIT    BMCR_ANENABLE
+
+// the NCN26000 maps link_status to BMSR_ANEGCOMPLETE
+#define NCN26000_BMSR_LINK_STATUS_BIT  BMSR_ANEGCOMPLETE
+
+#define NCN26000_IRQ_LINKST_BIT                BIT(0)
+#define NCN26000_IRQ_PLCAST_BIT                BIT(1)
+#define NCN26000_IRQ_LJABBER_BIT       BIT(2)
+#define NCN26000_IRQ_RJABBER_BIT       BIT(3)
+#define NCN26000_IRQ_PLCAREC_BIT       BIT(4)
+#define NCN26000_IRQ_PHYSCOL_BIT       BIT(5)
+#define NCN26000_IRQ_RESET_BIT         BIT(15)
+
+#define TO_TMR_DEFAULT                 32
+
+static int ncn26000_config_init(struct phy_device *phydev)
+{
+       /* HW bug workaround: the default value of the PLCA TO_TIMER should be
+        * 32, where the current version of NCN26000 reports 24. This will be
+        * fixed in future PHY versions. For the time being, we force the
+        * correct default here.
+        */
+       return phy_write_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_PLCA_TOTMR,
+                            TO_TMR_DEFAULT);
+}
+
+static int ncn26000_config_aneg(struct phy_device *phydev)
+{
+       /* Note: the NCN26000 supports only P2MP link mode. Therefore, AN is not
+        * supported. However, this function is invoked by phylib to enable the
+        * PHY, regardless of the AN support.
+        */
+       phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
+       phydev->mdix = ETH_TP_MDI;
+
+       // bring up the link
+       return phy_write(phydev, MII_BMCR, NCN26000_BCMR_LINK_CTRL_BIT);
+}
+
+static int ncn26000_read_status(struct phy_device *phydev)
+{
+       /* The NCN26000 reports NCN26000_LINK_STATUS_BIT if the link status of
+        * the PHY is up. It further reports the logical AND of the link status
+        * and the PLCA status in the BMSR_LSTATUS bit.
+        */
+       int ret;
+
+       /* The link state is latched low so that momentary link
+        * drops can be detected. Do not double-read the status
+        * in polling mode to detect such short link drops except
+        * the link was already down.
+        */
+       if (!phy_polling_mode(phydev) || !phydev->link) {
+               ret = phy_read(phydev, MII_BMSR);
+               if (ret < 0)
+                       return ret;
+               else if (ret & NCN26000_BMSR_LINK_STATUS_BIT)
+                       goto upd_link;
+       }
+
+       ret = phy_read(phydev, MII_BMSR);
+       if (ret < 0)
+               return ret;
+
+upd_link:
+       // update link status
+       if (ret & NCN26000_BMSR_LINK_STATUS_BIT) {
+               phydev->link = 1;
+               phydev->pause = 0;
+               phydev->duplex = DUPLEX_HALF;
+               phydev->speed = SPEED_10;
+       } else {
+               phydev->link = 0;
+               phydev->duplex = DUPLEX_UNKNOWN;
+               phydev->speed = SPEED_UNKNOWN;
+       }
+
+       return 0;
+}
+
+static irqreturn_t ncn26000_handle_interrupt(struct phy_device *phydev)
+{
+       int ret;
+
+       // read and aknowledge the IRQ status register
+       ret = phy_read(phydev, NCN26000_REG_IRQ_STATUS);
+
+       // check only link status changes
+       if (ret < 0 || (ret & NCN26000_REG_IRQ_STATUS) == 0)
+               return IRQ_NONE;
+
+       phy_trigger_machine(phydev);
+       return IRQ_HANDLED;
+}
+
+static int ncn26000_config_intr(struct phy_device *phydev)
+{
+       int ret;
+       u16 irqe;
+
+       if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
+               // acknowledge IRQs
+               ret = phy_read(phydev, NCN26000_REG_IRQ_STATUS);
+               if (ret < 0)
+                       return ret;
+
+               // get link status notifications
+               irqe = NCN26000_IRQ_LINKST_BIT;
+       } else {
+               // disable all IRQs
+               irqe = 0;
+       }
+
+       ret = phy_write(phydev, NCN26000_REG_IRQ_CTL, irqe);
+       if (ret != 0)
+               return ret;
+
+       return 0;
+}
+
+static struct phy_driver ncn26000_driver[] = {
+       {
+               PHY_ID_MATCH_MODEL(PHY_ID_NCN26000),
+               .name                   = "NCN26000",
+               .features               = PHY_BASIC_T1S_P2MP_FEATURES,
+               .config_init            = ncn26000_config_init,
+               .config_intr            = ncn26000_config_intr,
+               .config_aneg            = ncn26000_config_aneg,
+               .read_status            = ncn26000_read_status,
+               .handle_interrupt       = ncn26000_handle_interrupt,
+               .get_plca_cfg           = genphy_c45_plca_get_cfg,
+               .set_plca_cfg           = genphy_c45_plca_set_cfg,
+               .get_plca_status        = genphy_c45_plca_get_status,
+               .soft_reset             = genphy_soft_reset,
+       },
+};
+
+module_phy_driver(ncn26000_driver);
+
+static struct mdio_device_id __maybe_unused ncn26000_tbl[] = {
+       { PHY_ID_MATCH_MODEL(PHY_ID_NCN26000) },
+       { }
+};
+
+MODULE_DEVICE_TABLE(mdio, ncn26000_tbl);
+
+MODULE_AUTHOR("Piergiorgio Beruto");
+MODULE_DESCRIPTION("onsemi 10BASE-T1S PHY driver");
+MODULE_LICENSE("Dual BSD/GPL");