net: phy: adin: add support MDI/MDIX/Auto-MDI selection
The ADIN PHYs support automatic MDI/MDIX negotiation. By default this is disabled, so this is enabled at `config_init`. This is controlled via the PHY Control 1 register. The supported modes are: 1. Manual MDI 2. Manual MDIX 3. Auto MDIX - prefer MDIX 4. Auto MDIX - prefer MDI The phydev mdix & mdix_ctrl fields include modes 3 & 4 into a single auto-mode. So, the default mode this driver enables is 4 when Auto-MDI mode is used. When detecting MDI/MDIX mode, a combination of the PHY Control 1 register and PHY Status 1 register is used to determine the correct MDI/MDIX mode. If Auto-MDI mode is not set, then the manual MDI/MDIX mode is returned. If Auto-MDI mode is set, then MDIX mode is returned differs from the preferred MDI/MDIX mode. This covers all cases where: 1. MDI preferred & Pair01Swapped == MDIX 2. MDIX preferred & Pair01Swapped == MDI 3. MDI preferred & ! Pair01Swapped == MDIX 4. MDIX preferred & ! Pair01Swapped == MDI The preferred MDI/MDIX mode is not configured via SW, but can be configured via HW pins. Note that the `Pair01Swapped` is the Green-Yellow physical pairs. Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
f1012fb476
commit
b422d1b6f7
|
@ -19,6 +19,10 @@
|
|||
#define ADIN1300_MII_EXT_REG_PTR 0x0010
|
||||
#define ADIN1300_MII_EXT_REG_DATA 0x0011
|
||||
|
||||
#define ADIN1300_PHY_CTRL1 0x0012
|
||||
#define ADIN1300_AUTO_MDI_EN BIT(10)
|
||||
#define ADIN1300_MAN_MDIX_EN BIT(9)
|
||||
|
||||
#define ADIN1300_INT_MASK_REG 0x0018
|
||||
#define ADIN1300_INT_MDIO_SYNC_EN BIT(9)
|
||||
#define ADIN1300_INT_ANEG_STAT_CHNG_EN BIT(8)
|
||||
|
@ -33,6 +37,9 @@
|
|||
(ADIN1300_INT_LINK_STAT_CHNG_EN | ADIN1300_INT_HW_IRQ_EN)
|
||||
#define ADIN1300_INT_STATUS_REG 0x0019
|
||||
|
||||
#define ADIN1300_PHY_STATUS1 0x001a
|
||||
#define ADIN1300_PAIR_01_SWAP BIT(11)
|
||||
|
||||
#define ADIN1300_GE_RGMII_CFG_REG 0xff23
|
||||
#define ADIN1300_GE_RGMII_RX_MSK GENMASK(8, 6)
|
||||
#define ADIN1300_GE_RGMII_RX_SEL(x) \
|
||||
|
@ -206,6 +213,8 @@ static int adin_config_init(struct phy_device *phydev)
|
|||
{
|
||||
int rc;
|
||||
|
||||
phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
|
||||
|
||||
rc = genphy_config_init(phydev);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
@ -269,13 +278,113 @@ static int adin_write_mmd(struct phy_device *phydev, int devad, u16 regnum,
|
|||
return __mdiobus_write(bus, phy_addr, ADIN1300_MII_EXT_REG_DATA, val);
|
||||
}
|
||||
|
||||
static int adin_config_mdix(struct phy_device *phydev)
|
||||
{
|
||||
bool auto_en, mdix_en;
|
||||
int reg;
|
||||
|
||||
mdix_en = false;
|
||||
auto_en = false;
|
||||
switch (phydev->mdix_ctrl) {
|
||||
case ETH_TP_MDI:
|
||||
break;
|
||||
case ETH_TP_MDI_X:
|
||||
mdix_en = true;
|
||||
break;
|
||||
case ETH_TP_MDI_AUTO:
|
||||
auto_en = true;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reg = phy_read(phydev, ADIN1300_PHY_CTRL1);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
if (mdix_en)
|
||||
reg |= ADIN1300_MAN_MDIX_EN;
|
||||
else
|
||||
reg &= ~ADIN1300_MAN_MDIX_EN;
|
||||
|
||||
if (auto_en)
|
||||
reg |= ADIN1300_AUTO_MDI_EN;
|
||||
else
|
||||
reg &= ~ADIN1300_AUTO_MDI_EN;
|
||||
|
||||
return phy_write(phydev, ADIN1300_PHY_CTRL1, reg);
|
||||
}
|
||||
|
||||
static int adin_config_aneg(struct phy_device *phydev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = adin_config_mdix(phydev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return genphy_config_aneg(phydev);
|
||||
}
|
||||
|
||||
static int adin_mdix_update(struct phy_device *phydev)
|
||||
{
|
||||
bool auto_en, mdix_en;
|
||||
bool swapped;
|
||||
int reg;
|
||||
|
||||
reg = phy_read(phydev, ADIN1300_PHY_CTRL1);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
auto_en = !!(reg & ADIN1300_AUTO_MDI_EN);
|
||||
mdix_en = !!(reg & ADIN1300_MAN_MDIX_EN);
|
||||
|
||||
/* If MDI/MDIX is forced, just read it from the control reg */
|
||||
if (!auto_en) {
|
||||
if (mdix_en)
|
||||
phydev->mdix = ETH_TP_MDI_X;
|
||||
else
|
||||
phydev->mdix = ETH_TP_MDI;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Otherwise, we need to deduce it from the PHY status2 reg.
|
||||
* When Auto-MDI is enabled, the ADIN1300_MAN_MDIX_EN bit implies
|
||||
* a preference for MDIX when it is set.
|
||||
*/
|
||||
reg = phy_read(phydev, ADIN1300_PHY_STATUS1);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
swapped = !!(reg & ADIN1300_PAIR_01_SWAP);
|
||||
|
||||
if (mdix_en != swapped)
|
||||
phydev->mdix = ETH_TP_MDI_X;
|
||||
else
|
||||
phydev->mdix = ETH_TP_MDI;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adin_read_status(struct phy_device *phydev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = adin_mdix_update(phydev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return genphy_read_status(phydev);
|
||||
}
|
||||
|
||||
static struct phy_driver adin_driver[] = {
|
||||
{
|
||||
PHY_ID_MATCH_MODEL(PHY_ID_ADIN1200),
|
||||
.name = "ADIN1200",
|
||||
.config_init = adin_config_init,
|
||||
.config_aneg = genphy_config_aneg,
|
||||
.read_status = genphy_read_status,
|
||||
.config_aneg = adin_config_aneg,
|
||||
.read_status = adin_read_status,
|
||||
.ack_interrupt = adin_phy_ack_intr,
|
||||
.config_intr = adin_phy_config_intr,
|
||||
.resume = genphy_resume,
|
||||
|
@ -287,8 +396,8 @@ static struct phy_driver adin_driver[] = {
|
|||
PHY_ID_MATCH_MODEL(PHY_ID_ADIN1300),
|
||||
.name = "ADIN1300",
|
||||
.config_init = adin_config_init,
|
||||
.config_aneg = genphy_config_aneg,
|
||||
.read_status = genphy_read_status,
|
||||
.config_aneg = adin_config_aneg,
|
||||
.read_status = adin_read_status,
|
||||
.ack_interrupt = adin_phy_ack_intr,
|
||||
.config_intr = adin_phy_config_intr,
|
||||
.resume = genphy_resume,
|
||||
|
|
Loading…
Reference in New Issue