From a2f5c9366a42572f9d4aa49174ac117408bf7c12 Mon Sep 17 00:00:00 2001 From: Michael Trimarchi Date: Tue, 12 Apr 2022 10:31:37 -0300 Subject: [PATCH] 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 Signed-off-by: Ariel D'Alessandro Reviewed-by: Ramon Fried --- drivers/net/phy/Kconfig | 5 + drivers/net/phy/Makefile | 1 + drivers/net/phy/nxp-tja11xx.c | 277 ++++++++++++++++++++++++++++++++++ drivers/net/phy/phy.c | 3 + include/phy.h | 1 + 5 files changed, 287 insertions(+) create mode 100644 drivers/net/phy/nxp-tja11xx.c diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 014a4de223..d40ce92cad 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -215,6 +215,11 @@ config PHY_NXP_C45_TJA11XX 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 b28440bc4e..67ca4d3a69 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_PHY_MICREL_KSZ90X1) += micrel_ksz90x1.o 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 0000000000..30dec5e605 --- /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 + * Copyright (C) 2022 Ariel D'Alessandro + * Copyright (C) 2018 Marek Vasut + */ + +#include +#include +#include +#include +#include + +#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 50448cf62c..36722620f0 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -532,6 +532,9 @@ int phy_init(void) #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 diff --git a/include/phy.h b/include/phy.h index 1a30aee542..528839a33d 100644 --- a/include/phy.h +++ b/include/phy.h @@ -556,6 +556,7 @@ int phy_micrel_ksz90x1_init(void); int phy_meson_gxl_init(void); int phy_natsemi_init(void); int phy_nxp_c45_tja11xx_init(void); +int phy_nxp_tja11xx_init(void); int phy_realtek_init(void); int phy_smsc_init(void); int phy_teranetics_init(void); -- 2.39.5