net: phy: nxp-tja11xx: Add NXP TJA11xx PHY driver

Add driver for the NXP TJA1100 and TJA1101 PHYs. These PHYs are special
BroadRReach 100BaseT1 PHYs used in automotive.

Signed-off-by: Michael Trimarchi <michael@amarulasolutions.com>
Signed-off-by: Ariel D'Alessandro <ariel.dalessandro@collabora.com>
Reviewed-by: Ramon Fried <rfried.dev@gmail.com>
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 014a4de..d40ce92 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -215,6 +215,11 @@
 	  Enable support for NXP C45 TJA11XX PHYs.
 	  Currently supports only the TJA1103 PHY.
 
+config PHY_NXP_TJA11XX
+	bool "NXP TJA11XX Ethernet PHYs support"
+	help
+	  Currently supports the NXP TJA1100 and TJA1101 PHY.
+
 config PHY_REALTEK
 	bool "Realtek Ethernet PHYs support"
 
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index b28440b..67ca4d3 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -24,6 +24,7 @@
 obj-$(CONFIG_PHY_MESON_GXL) += meson-gxl.o
 obj-$(CONFIG_PHY_NATSEMI) += natsemi.o
 obj-$(CONFIG_PHY_NXP_C45_TJA11XX) += nxp-c45-tja11xx.o
+obj-$(CONFIG_PHY_NXP_TJA11XX) += nxp-tja11xx.o
 obj-$(CONFIG_PHY_REALTEK) += realtek.o
 obj-$(CONFIG_PHY_SMSC) += smsc.o
 obj-$(CONFIG_PHY_TERANETICS) += teranetics.o
diff --git a/drivers/net/phy/nxp-tja11xx.c b/drivers/net/phy/nxp-tja11xx.c
new file mode 100644
index 0000000..30dec5e
--- /dev/null
+++ b/drivers/net/phy/nxp-tja11xx.c
@@ -0,0 +1,277 @@
+// SPDX-License-Identifier: GPL-2.0
+/* NXP TJA1100 BroadRReach PHY driver
+ *
+ * Copyright (C) 2022 Michael Trimarchi <michael@amarulasolutions.com>
+ * Copyright (C) 2022 Ariel D'Alessandro <ariel.dalessandro@collabora.com>
+ * Copyright (C) 2018 Marek Vasut <marex@denx.de>
+ */
+
+#include <common.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <phy.h>
+
+#define PHY_ID_MASK			0xfffffff0
+#define PHY_ID_TJA1100			0x0180dc40
+#define PHY_ID_TJA1101			0x0180dd00
+
+#define MII_ECTRL			17
+#define MII_ECTRL_LINK_CONTROL		BIT(15)
+#define MII_ECTRL_POWER_MODE_MASK	GENMASK(14, 11)
+#define MII_ECTRL_POWER_MODE_NO_CHANGE	(0x0 << 11)
+#define MII_ECTRL_POWER_MODE_NORMAL	(0x3 << 11)
+#define MII_ECTRL_POWER_MODE_STANDBY	(0xc << 11)
+#define MII_ECTRL_CABLE_TEST		BIT(5)
+#define MII_ECTRL_CONFIG_EN		BIT(2)
+#define MII_ECTRL_WAKE_REQUEST		BIT(0)
+
+#define MII_CFG1			18
+#define MII_CFG1_MASTER_SLAVE		BIT(15)
+#define MII_CFG1_AUTO_OP		BIT(14)
+#define MII_CFG1_SLEEP_CONFIRM		BIT(6)
+#define MII_CFG1_LED_MODE_MASK		GENMASK(5, 4)
+#define MII_CFG1_LED_MODE_LINKUP	0
+#define MII_CFG1_LED_ENABLE		BIT(3)
+
+#define MII_CFG2			19
+#define MII_CFG2_SLEEP_REQUEST_TO	GENMASK(1, 0)
+#define MII_CFG2_SLEEP_REQUEST_TO_16MS	0x3
+
+#define MII_INTSRC			21
+#define MII_INTSRC_LINK_FAIL		BIT(10)
+#define MII_INTSRC_LINK_UP		BIT(9)
+#define MII_INTSRC_MASK			(MII_INTSRC_LINK_FAIL | \
+					 MII_INTSRC_LINK_UP)
+#define MII_INTSRC_UV_ERR		BIT(3)
+#define MII_INTSRC_TEMP_ERR		BIT(1)
+
+#define MII_INTEN			22
+#define MII_INTEN_LINK_FAIL		BIT(10)
+#define MII_INTEN_LINK_UP		BIT(9)
+#define MII_INTEN_UV_ERR		BIT(3)
+#define MII_INTEN_TEMP_ERR		BIT(1)
+
+#define MII_COMMSTAT			23
+#define MII_COMMSTAT_LINK_UP		BIT(15)
+#define MII_COMMSTAT_SQI_STATE		GENMASK(7, 5)
+#define MII_COMMSTAT_SQI_MAX		7
+
+#define MII_GENSTAT			24
+#define MII_GENSTAT_PLL_LOCKED		BIT(14)
+
+#define MII_EXTSTAT			25
+#define MII_EXTSTAT_SHORT_DETECT	BIT(8)
+#define MII_EXTSTAT_OPEN_DETECT		BIT(7)
+#define MII_EXTSTAT_POLARITY_DETECT	BIT(6)
+
+#define MII_COMMCFG			27
+#define MII_COMMCFG_AUTO_OP		BIT(15)
+
+static inline int tja11xx_set_bits(struct phy_device *phydev, u32 regnum,
+				   u16 val)
+{
+	return phy_set_bits_mmd(phydev, MDIO_DEVAD_NONE, regnum, val);
+}
+
+static inline int tja11xx_clear_bits(struct phy_device *phydev, u32 regnum,
+				     u16 val)
+{
+	return phy_clear_bits_mmd(phydev, MDIO_DEVAD_NONE, regnum, val);
+}
+
+static inline int tja11xx_read(struct phy_device *phydev, int regnum)
+{
+	return phy_read(phydev, MDIO_DEVAD_NONE, regnum);
+}
+
+static inline int tja11xx_modify(struct phy_device *phydev, int regnum,
+				 u16 mask, u16 set)
+{
+	return phy_modify(phydev, MDIO_DEVAD_NONE, regnum, mask, set);
+}
+
+static int tja11xx_check(struct phy_device *phydev, u8 reg, u16 mask, u16 set)
+{
+	int val;
+
+	return read_poll_timeout(tja11xx_read, val, (val & mask) == set, 150,
+				 30000, phydev, reg);
+}
+
+static int tja11xx_modify_check(struct phy_device *phydev, u8 reg,
+			    u16 mask, u16 set)
+{
+	int ret;
+
+	ret = tja11xx_modify(phydev, reg, mask, set);
+	if (ret)
+		return ret;
+
+	return tja11xx_check(phydev, reg, mask, set);
+}
+
+static int tja11xx_enable_reg_write(struct phy_device *phydev)
+{
+	return tja11xx_set_bits(phydev, MII_ECTRL, MII_ECTRL_CONFIG_EN);
+}
+
+static int tja11xx_enable_link_control(struct phy_device *phydev)
+{
+	return tja11xx_set_bits(phydev, MII_ECTRL, MII_ECTRL_LINK_CONTROL);
+}
+
+static int tja11xx_wakeup(struct phy_device *phydev)
+{
+	int ret;
+
+	ret = tja11xx_read(phydev, MII_ECTRL);
+	if (ret < 0)
+		return ret;
+
+	switch (ret & MII_ECTRL_POWER_MODE_MASK) {
+	case MII_ECTRL_POWER_MODE_NO_CHANGE:
+		break;
+	case MII_ECTRL_POWER_MODE_NORMAL:
+		ret = tja11xx_set_bits(phydev, MII_ECTRL,
+				       MII_ECTRL_WAKE_REQUEST);
+		if (ret)
+			return ret;
+
+		ret = tja11xx_clear_bits(phydev, MII_ECTRL,
+					 MII_ECTRL_WAKE_REQUEST);
+		if (ret)
+			return ret;
+		break;
+	case MII_ECTRL_POWER_MODE_STANDBY:
+		ret = tja11xx_modify_check(phydev, MII_ECTRL,
+					   MII_ECTRL_POWER_MODE_MASK,
+					   MII_ECTRL_POWER_MODE_STANDBY);
+		if (ret)
+			return ret;
+
+		ret = tja11xx_modify(phydev, MII_ECTRL,
+				     MII_ECTRL_POWER_MODE_MASK,
+				     MII_ECTRL_POWER_MODE_NORMAL);
+		if (ret)
+			return ret;
+
+		ret = tja11xx_modify_check(phydev, MII_GENSTAT,
+					   MII_GENSTAT_PLL_LOCKED,
+					   MII_GENSTAT_PLL_LOCKED);
+		if (ret)
+			return ret;
+
+		return tja11xx_enable_link_control(phydev);
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int tja11xx_config_init(struct phy_device *phydev)
+{
+	int ret;
+
+	ret = tja11xx_enable_reg_write(phydev);
+	if (ret)
+		return ret;
+
+	phydev->autoneg = AUTONEG_DISABLE;
+	phydev->speed = SPEED_100;
+	phydev->duplex = DUPLEX_FULL;
+
+	switch (phydev->phy_id & PHY_ID_MASK) {
+	case PHY_ID_TJA1100:
+		ret = tja11xx_modify(phydev, MII_CFG1,
+				     MII_CFG1_AUTO_OP | MII_CFG1_LED_MODE_MASK |
+				     MII_CFG1_LED_ENABLE,
+				     MII_CFG1_AUTO_OP |
+				     MII_CFG1_LED_MODE_LINKUP |
+				     MII_CFG1_LED_ENABLE);
+		if (ret)
+			return ret;
+		break;
+	case PHY_ID_TJA1101:
+		ret = tja11xx_set_bits(phydev, MII_COMMCFG,
+				       MII_COMMCFG_AUTO_OP);
+		if (ret)
+			return ret;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = tja11xx_clear_bits(phydev, MII_CFG1, MII_CFG1_SLEEP_CONFIRM);
+	if (ret)
+		return ret;
+
+	ret = tja11xx_modify(phydev, MII_CFG2, MII_CFG2_SLEEP_REQUEST_TO,
+			     MII_CFG2_SLEEP_REQUEST_TO_16MS);
+	if (ret)
+		return ret;
+
+	ret = tja11xx_wakeup(phydev);
+	if (ret < 0)
+		return ret;
+
+	/* ACK interrupts by reading the status register */
+	ret = tja11xx_read(phydev, MII_INTSRC);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int tja11xx_startup(struct phy_device *phydev)
+{
+	int ret;
+
+	ret = genphy_update_link(phydev);
+	if (ret)
+		return ret;
+
+	ret = tja11xx_read(phydev, MII_CFG1);
+	if (ret < 0)
+		return ret;
+
+	if (phydev->link) {
+		ret = tja11xx_read(phydev, MII_COMMSTAT);
+		if (ret < 0)
+			return ret;
+
+		if (!(ret & MII_COMMSTAT_LINK_UP))
+			phydev->link = 0;
+	}
+
+	return 0;
+}
+
+static struct phy_driver TJA1100_driver = {
+	.name = "NXP TJA1100",
+	.uid = PHY_ID_TJA1100,
+	.mask = PHY_ID_MASK,
+	.features = PHY_BASIC_FEATURES,
+	.config	= &tja11xx_config_init,
+	.startup = &tja11xx_startup,
+	.shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver TJA1101_driver = {
+	.name = "NXP TJA1101",
+	.uid = PHY_ID_TJA1101,
+	.mask = PHY_ID_MASK,
+	.features = PHY_BASIC_FEATURES,
+	.config	= &tja11xx_config_init,
+	.startup = &tja11xx_startup,
+	.shutdown = &genphy_shutdown,
+};
+
+int phy_nxp_tja11xx_init(void)
+{
+	phy_register(&TJA1100_driver);
+	phy_register(&TJA1101_driver);
+
+	return 0;
+}
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 50448cf..3672262 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -532,6 +532,9 @@
 #ifdef CONFIG_NXP_C45_TJA11XX_PHY
 	phy_nxp_c45_tja11xx_init();
 #endif
+#ifdef CONFIG_PHY_NXP_TJA11XX
+	phy_nxp_tja11xx_init();
+#endif
 #ifdef CONFIG_PHY_REALTEK
 	phy_realtek_init();
 #endif