Commit 613707eb authored by David S. Miller's avatar David S. Miller

Merge branch 'add-ti-dp83td510-support'

Oleksij Rempel says:

====================
add ti dp83td510 support

changes v4:
- dp83td510: remove unused variables
- s/base1/baset1
- s/genphy_c45_baset1_read_master_slave/genphy_c45_pma_baset1_read_master_slave

changes v3:
- export reusable code snippets and make use of it in the dp83td510
  driver

changes v2:
- rewrite the driver reduce usage of common code and to reduce amount of
  quirks.
- add genphy_c45_baset1_an_config_aneg fix
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 5dd6da25 165cd04f
...@@ -342,6 +342,12 @@ config DP83869_PHY ...@@ -342,6 +342,12 @@ config DP83869_PHY
Currently supports the DP83869 PHY. This PHY supports copper and Currently supports the DP83869 PHY. This PHY supports copper and
fiber connections. fiber connections.
config DP83TD510_PHY
tristate "Texas Instruments DP83TD510 Ethernet 10Base-T1L PHY"
help
Support for the DP83TD510 Ethernet 10Base-T1L PHY. This PHY supports
a 10M single pair Ethernet connection for up to 1000 meter cable.
config VITESSE_PHY config VITESSE_PHY
tristate "Vitesse PHYs" tristate "Vitesse PHYs"
help help
......
...@@ -57,6 +57,7 @@ obj-$(CONFIG_DP83848_PHY) += dp83848.o ...@@ -57,6 +57,7 @@ obj-$(CONFIG_DP83848_PHY) += dp83848.o
obj-$(CONFIG_DP83867_PHY) += dp83867.o obj-$(CONFIG_DP83867_PHY) += dp83867.o
obj-$(CONFIG_DP83869_PHY) += dp83869.o obj-$(CONFIG_DP83869_PHY) += dp83869.o
obj-$(CONFIG_DP83TC811_PHY) += dp83tc811.o obj-$(CONFIG_DP83TC811_PHY) += dp83tc811.o
obj-$(CONFIG_DP83TD510_PHY) += dp83td510.o
obj-$(CONFIG_FIXED_PHY) += fixed_phy.o obj-$(CONFIG_FIXED_PHY) += fixed_phy.o
obj-$(CONFIG_ICPLUS_PHY) += icplus.o obj-$(CONFIG_ICPLUS_PHY) += icplus.o
obj-$(CONFIG_INTEL_XWAY_PHY) += intel-xway.o obj-$(CONFIG_INTEL_XWAY_PHY) += intel-xway.o
......
// SPDX-License-Identifier: GPL-2.0
/* Driver for the Texas Instruments DP83TD510 PHY
* Copyright (c) 2022 Pengutronix, Oleksij Rempel <kernel@pengutronix.de>
*/
#include <linux/bitfield.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/phy.h>
#define DP83TD510E_PHY_ID 0x20000181
/* MDIO_MMD_VEND2 registers */
#define DP83TD510E_PHY_STS 0x10
#define DP83TD510E_STS_MII_INT BIT(7)
#define DP83TD510E_LINK_STATUS BIT(0)
#define DP83TD510E_GEN_CFG 0x11
#define DP83TD510E_GENCFG_INT_POLARITY BIT(3)
#define DP83TD510E_GENCFG_INT_EN BIT(1)
#define DP83TD510E_GENCFG_INT_OE BIT(0)
#define DP83TD510E_INTERRUPT_REG_1 0x12
#define DP83TD510E_INT1_LINK BIT(13)
#define DP83TD510E_INT1_LINK_EN BIT(5)
#define DP83TD510E_AN_STAT_1 0x60c
#define DP83TD510E_MASTER_SLAVE_RESOL_FAIL BIT(15)
static int dp83td510_config_intr(struct phy_device *phydev)
{
int ret;
if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
/* Clear any pending interrupts */
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PHY_STS,
0x0);
if (ret)
return ret;
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
DP83TD510E_INTERRUPT_REG_1,
DP83TD510E_INT1_LINK_EN);
if (ret)
return ret;
ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
DP83TD510E_GEN_CFG,
DP83TD510E_GENCFG_INT_POLARITY |
DP83TD510E_GENCFG_INT_EN |
DP83TD510E_GENCFG_INT_OE);
} else {
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
DP83TD510E_INTERRUPT_REG_1, 0x0);
if (ret)
return ret;
ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2,
DP83TD510E_GEN_CFG,
DP83TD510E_GENCFG_INT_EN);
if (ret)
return ret;
/* Clear any pending interrupts */
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PHY_STS,
0x0);
}
return ret;
}
static irqreturn_t dp83td510_handle_interrupt(struct phy_device *phydev)
{
int ret;
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PHY_STS);
if (ret < 0) {
phy_error(phydev);
return IRQ_NONE;
} else if (!(ret & DP83TD510E_STS_MII_INT)) {
return IRQ_NONE;
}
/* Read the current enabled interrupts */
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_INTERRUPT_REG_1);
if (ret < 0) {
phy_error(phydev);
return IRQ_NONE;
} else if (!(ret & DP83TD510E_INT1_LINK_EN) ||
!(ret & DP83TD510E_INT1_LINK)) {
return IRQ_NONE;
}
phy_trigger_machine(phydev);
return IRQ_HANDLED;
}
static int dp83td510_read_status(struct phy_device *phydev)
{
u16 phy_sts;
int ret;
phydev->speed = SPEED_UNKNOWN;
phydev->duplex = DUPLEX_UNKNOWN;
phydev->pause = 0;
phydev->asym_pause = 0;
linkmode_zero(phydev->lp_advertising);
phy_sts = phy_read(phydev, DP83TD510E_PHY_STS);
phydev->link = !!(phy_sts & DP83TD510E_LINK_STATUS);
if (phydev->link) {
/* This PHY supports only one link mode: 10BaseT1L_Full */
phydev->duplex = DUPLEX_FULL;
phydev->speed = SPEED_10;
if (phydev->autoneg == AUTONEG_ENABLE) {
ret = genphy_c45_read_lpa(phydev);
if (ret)
return ret;
phy_resolve_aneg_linkmode(phydev);
}
}
if (phydev->autoneg == AUTONEG_ENABLE) {
ret = genphy_c45_baset1_read_status(phydev);
if (ret < 0)
return ret;
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2,
DP83TD510E_AN_STAT_1);
if (ret < 0)
return ret;
if (ret & DP83TD510E_MASTER_SLAVE_RESOL_FAIL)
phydev->master_slave_state = MASTER_SLAVE_STATE_ERR;
} else {
return genphy_c45_pma_baset1_read_master_slave(phydev);
}
return 0;
}
static int dp83td510_config_aneg(struct phy_device *phydev)
{
bool changed = false;
int ret;
ret = genphy_c45_pma_baset1_setup_master_slave(phydev);
if (ret < 0)
return ret;
if (phydev->autoneg == AUTONEG_DISABLE)
return genphy_c45_an_disable_aneg(phydev);
ret = genphy_c45_an_config_aneg(phydev);
if (ret < 0)
return ret;
if (ret > 0)
changed = true;
return genphy_c45_check_and_restart_aneg(phydev, changed);
}
static int dp83td510_get_features(struct phy_device *phydev)
{
/* This PHY can't respond on MDIO bus if no RMII clock is enabled.
* In case RMII mode is used (most meaningful mode for this PHY) and
* the PHY do not have own XTAL, and CLK providing MAC is not probed,
* we won't be able to read all needed ability registers.
* So provide it manually.
*/
linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
phydev->supported);
return 0;
}
static struct phy_driver dp83td510_driver[] = {
{
PHY_ID_MATCH_MODEL(DP83TD510E_PHY_ID),
.name = "TI DP83TD510E",
.config_aneg = dp83td510_config_aneg,
.read_status = dp83td510_read_status,
.get_features = dp83td510_get_features,
.config_intr = dp83td510_config_intr,
.handle_interrupt = dp83td510_handle_interrupt,
.suspend = genphy_suspend,
.resume = genphy_resume,
} };
module_phy_driver(dp83td510_driver);
static struct mdio_device_id __maybe_unused dp83td510_tbl[] = {
{ PHY_ID_MATCH_MODEL(DP83TD510E_PHY_ID) },
{ }
};
MODULE_DEVICE_TABLE(mdio, dp83td510_tbl);
MODULE_DESCRIPTION("Texas Instruments DP83TD510E PHY driver");
MODULE_AUTHOR("Oleksij Rempel <kernel@pengutronix.de>");
MODULE_LICENSE("GPL v2");
...@@ -70,6 +70,36 @@ int genphy_c45_pma_suspend(struct phy_device *phydev) ...@@ -70,6 +70,36 @@ int genphy_c45_pma_suspend(struct phy_device *phydev)
} }
EXPORT_SYMBOL_GPL(genphy_c45_pma_suspend); EXPORT_SYMBOL_GPL(genphy_c45_pma_suspend);
/**
* genphy_c45_pma_baset1_setup_master_slave - configures forced master/slave
* role of BaseT1 devices.
* @phydev: target phy_device struct
*/
int genphy_c45_pma_baset1_setup_master_slave(struct phy_device *phydev)
{
int ctl = 0;
switch (phydev->master_slave_set) {
case MASTER_SLAVE_CFG_MASTER_PREFERRED:
case MASTER_SLAVE_CFG_MASTER_FORCE:
ctl = MDIO_PMA_PMD_BT1_CTRL_CFG_MST;
break;
case MASTER_SLAVE_CFG_SLAVE_FORCE:
case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
break;
case MASTER_SLAVE_CFG_UNKNOWN:
case MASTER_SLAVE_CFG_UNSUPPORTED:
return 0;
default:
phydev_warn(phydev, "Unsupported Master/Slave mode\n");
return -EOPNOTSUPP;
}
return phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_PMD_BT1_CTRL,
MDIO_PMA_PMD_BT1_CTRL_CFG_MST, ctl);
}
EXPORT_SYMBOL_GPL(genphy_c45_pma_baset1_setup_master_slave);
/** /**
* genphy_c45_pma_setup_forced - configures a forced speed * genphy_c45_pma_setup_forced - configures a forced speed
* @phydev: target phy_device struct * @phydev: target phy_device struct
...@@ -141,25 +171,7 @@ int genphy_c45_pma_setup_forced(struct phy_device *phydev) ...@@ -141,25 +171,7 @@ int genphy_c45_pma_setup_forced(struct phy_device *phydev)
return ret; return ret;
if (genphy_c45_baset1_able(phydev)) { if (genphy_c45_baset1_able(phydev)) {
int ctl = 0; ret = genphy_c45_pma_baset1_setup_master_slave(phydev);
switch (phydev->master_slave_set) {
case MASTER_SLAVE_CFG_MASTER_PREFERRED:
case MASTER_SLAVE_CFG_MASTER_FORCE:
ctl = MDIO_PMA_PMD_BT1_CTRL_CFG_MST;
break;
case MASTER_SLAVE_CFG_SLAVE_FORCE:
case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
case MASTER_SLAVE_CFG_UNKNOWN:
case MASTER_SLAVE_CFG_UNSUPPORTED:
break;
default:
phydev_warn(phydev, "Unsupported Master/Slave mode\n");
return -EOPNOTSUPP;
}
ret = phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_PMD_BT1_CTRL,
MDIO_PMA_PMD_BT1_CTRL_CFG_MST, ctl);
if (ret < 0) if (ret < 0)
return ret; return ret;
} }
...@@ -191,8 +203,12 @@ static int genphy_c45_baset1_an_config_aneg(struct phy_device *phydev) ...@@ -191,8 +203,12 @@ static int genphy_c45_baset1_an_config_aneg(struct phy_device *phydev)
case MASTER_SLAVE_CFG_MASTER_PREFERRED: case MASTER_SLAVE_CFG_MASTER_PREFERRED:
case MASTER_SLAVE_CFG_SLAVE_PREFERRED: case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
break; break;
case MASTER_SLAVE_CFG_UNKNOWN:
case MASTER_SLAVE_CFG_UNSUPPORTED:
return 0;
default: default:
break; phydev_warn(phydev, "Unsupported Master/Slave mode\n");
return -EOPNOTSUPP;
} }
switch (phydev->master_slave_set) { switch (phydev->master_slave_set) {
...@@ -534,6 +550,34 @@ int genphy_c45_read_lpa(struct phy_device *phydev) ...@@ -534,6 +550,34 @@ int genphy_c45_read_lpa(struct phy_device *phydev)
} }
EXPORT_SYMBOL_GPL(genphy_c45_read_lpa); EXPORT_SYMBOL_GPL(genphy_c45_read_lpa);
/**
* genphy_c45_pma_baset1_read_master_slave - read forced master/slave
* configuration
* @phydev: target phy_device struct
*/
int genphy_c45_pma_baset1_read_master_slave(struct phy_device *phydev)
{
int val;
phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
phydev->master_slave_get = MASTER_SLAVE_CFG_UNKNOWN;
val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_PMD_BT1_CTRL);
if (val < 0)
return val;
if (val & MDIO_PMA_PMD_BT1_CTRL_CFG_MST) {
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;
}
EXPORT_SYMBOL_GPL(genphy_c45_pma_baset1_read_master_slave);
/** /**
* genphy_c45_read_pma - read link speed etc from PMA * genphy_c45_read_pma - read link speed etc from PMA
* @phydev: target phy_device struct * @phydev: target phy_device struct
...@@ -575,14 +619,9 @@ int genphy_c45_read_pma(struct phy_device *phydev) ...@@ -575,14 +619,9 @@ int genphy_c45_read_pma(struct phy_device *phydev)
phydev->duplex = DUPLEX_FULL; phydev->duplex = DUPLEX_FULL;
if (genphy_c45_baset1_able(phydev)) { if (genphy_c45_baset1_able(phydev)) {
val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_PMD_BT1_CTRL); val = genphy_c45_pma_baset1_read_master_slave(phydev);
if (val < 0) if (val < 0)
return val; return val;
if (MDIO_PMA_PMD_BT1_CTRL_CFG_MST)
phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER;
else
phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE;
} }
return 0; return 0;
...@@ -746,7 +785,7 @@ EXPORT_SYMBOL_GPL(genphy_c45_pma_read_abilities); ...@@ -746,7 +785,7 @@ EXPORT_SYMBOL_GPL(genphy_c45_pma_read_abilities);
* is forced or not, it is read from BASE-T1 AN advertisement * is forced or not, it is read from BASE-T1 AN advertisement
* register 7.514. * register 7.514.
*/ */
static int genphy_c45_baset1_read_status(struct phy_device *phydev) int genphy_c45_baset1_read_status(struct phy_device *phydev)
{ {
int ret; int ret;
int cfg; int cfg;
...@@ -776,6 +815,7 @@ static int genphy_c45_baset1_read_status(struct phy_device *phydev) ...@@ -776,6 +815,7 @@ static int genphy_c45_baset1_read_status(struct phy_device *phydev)
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(genphy_c45_baset1_read_status);
/** /**
* genphy_c45_read_status - read PHY status * genphy_c45_read_status - read PHY status
......
...@@ -1614,11 +1614,14 @@ int genphy_c45_read_link(struct phy_device *phydev); ...@@ -1614,11 +1614,14 @@ int genphy_c45_read_link(struct phy_device *phydev);
int genphy_c45_read_lpa(struct phy_device *phydev); int genphy_c45_read_lpa(struct phy_device *phydev);
int genphy_c45_read_pma(struct phy_device *phydev); int genphy_c45_read_pma(struct phy_device *phydev);
int genphy_c45_pma_setup_forced(struct phy_device *phydev); int genphy_c45_pma_setup_forced(struct phy_device *phydev);
int genphy_c45_pma_baset1_setup_master_slave(struct phy_device *phydev);
int genphy_c45_an_config_aneg(struct phy_device *phydev); int genphy_c45_an_config_aneg(struct phy_device *phydev);
int genphy_c45_an_disable_aneg(struct phy_device *phydev); int genphy_c45_an_disable_aneg(struct phy_device *phydev);
int genphy_c45_read_mdix(struct phy_device *phydev); int genphy_c45_read_mdix(struct phy_device *phydev);
int genphy_c45_pma_read_abilities(struct phy_device *phydev); int genphy_c45_pma_read_abilities(struct phy_device *phydev);
int genphy_c45_pma_baset1_read_master_slave(struct phy_device *phydev);
int genphy_c45_read_status(struct phy_device *phydev); int genphy_c45_read_status(struct phy_device *phydev);
int genphy_c45_baset1_read_status(struct phy_device *phydev);
int genphy_c45_config_aneg(struct phy_device *phydev); int genphy_c45_config_aneg(struct phy_device *phydev);
int genphy_c45_loopback(struct phy_device *phydev, bool enable); int genphy_c45_loopback(struct phy_device *phydev, bool enable);
int genphy_c45_pma_resume(struct phy_device *phydev); int genphy_c45_pma_resume(struct phy_device *phydev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment