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>
This commit is contained in:
parent
087baf80ec
commit
a2f5c9366a
@ -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"
|
||||
|
||||
|
@ -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
|
||||
|
277
drivers/net/phy/nxp-tja11xx.c
Normal file
277
drivers/net/phy/nxp-tja11xx.c
Normal file
@ -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;
|
||||
}
|
@ -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
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user