#include <linux/bitfield.h>
#include <linux/etherdevice.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/phy.h>
#define PHY_ID_MXL86110 0xc1335580
#define PHY_ID_MXL86111 0xc1335588
#define MXL86110_EXTD_REG_ADDR_OFFSET 0x1E
#define MXL86110_EXTD_REG_ADDR_DATA 0x1F
#define PHY_IRQ_ENABLE_REG 0x12
#define PHY_IRQ_ENABLE_REG_WOL BIT(6)
#define MXL86111_EXT_SMI_SDS_PHY_REG 0xA000
#define MXL86111_EXT_SMI_SDS_PHYSPACE_MASK BIT(1)
#define MXL86111_EXT_SMI_SDS_PHYFIBER_SPACE (0x1 << 1)
#define MXL86111_EXT_SMI_SDS_PHYUTP_SPACE (0x0 << 1)
#define MXL86111_EXT_SMI_SDS_PHY_AUTO 0xff
#define MXL86110_EXT_SYNCE_CFG_REG 0xA012
#define MXL86110_EXT_SYNCE_CFG_CLK_FRE_SEL BIT(4)
#define MXL86110_EXT_SYNCE_CFG_EN_SYNC_E_DURING_LNKDN BIT(5)
#define MXL86110_EXT_SYNCE_CFG_EN_SYNC_E BIT(6)
#define MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_MASK GENMASK(3, 1)
#define MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_125M_PLL 0
#define MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_25M 4
#define MXL86110_EXT_MAC_ADDR_CFG1 0xA007
#define MXL86110_EXT_MAC_ADDR_CFG2 0xA008
#define MXL86110_EXT_MAC_ADDR_CFG3 0xA009
#define MXL86110_EXT_WOL_CFG_REG 0xA00A
#define MXL86110_WOL_CFG_WOL_MASK BIT(3)
#define MXL86110_EXT_RGMII_CFG1_REG 0xA003
#define MXL86110_EXT_RGMII_CFG1_RX_NO_DELAY (0x0 << 10)
#define MXL86110_EXT_RGMII_CFG1_RX_DELAY_1950PS (0xD << 10)
#define MXL86110_EXT_RGMII_CFG1_RX_DELAY_MASK GENMASK(13, 10)
#define MXL86110_EXT_RGMII_CFG1_TX_1G_DELAY_1950PS (0xD << 0)
#define MXL86110_EXT_RGMII_CFG1_TX_1G_DELAY_MASK GENMASK(3, 0)
#define MXL86110_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_1950PS (0xD << 4)
#define MXL86110_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_MASK GENMASK(7, 4)
#define MXL86110_EXT_RGMII_CFG1_FULL_MASK \
((MXL86110_EXT_RGMII_CFG1_RX_DELAY_MASK) | \
(MXL86110_EXT_RGMII_CFG1_TX_1G_DELAY_MASK) | \
(MXL86110_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_MASK))
#define MXL86110_UTP_EXT_SLEEP_CTRL_REG 0x27
#define MXL86110_UTP_EXT_SLEEP_CTRL_EN_SLEEP_SW_OFF 0
#define MXL86110_UTP_EXT_SLEEP_CTRL_EN_SLEEP_SW_MASK BIT(15)
#define MXL86110_EXT_RGMII_MDIO_CFG 0xA005
#define MXL86110_RGMII_MDIO_CFG_EPA0_MASK GENMASK(6, 6)
#define MXL86110_EXT_RGMII_MDIO_CFG_EBA_MASK GENMASK(5, 5)
#define MXL86110_EXT_RGMII_MDIO_CFG_BA_MASK GENMASK(4, 0)
#define MXL86110_MAX_LEDS 3
#define MXL86110_COM_EXT_LED_GEN_CFG 0xA00B
# define MXL86110_COM_EXT_LED_GEN_CFG_LFM(x) ((BIT(0) | BIT(1)) << (3 * (x)))
# define MXL86110_COM_EXT_LED_GEN_CFG_LFME(x) (BIT(0) << (3 * (x)))
# define MXL86110_COM_EXT_LED_GEN_CFG_LFE(x) (BIT(2) << (3 * (x)))
#define MXL86110_LED0_CFG_REG 0xA00C
#define MXL86110_LED1_CFG_REG 0xA00D
#define MXL86110_LED2_CFG_REG 0xA00E
#define MXL86110_LEDX_CFG_BLINK BIT(13)
#define MXL86110_LEDX_CFG_LINK_UP_FULL_DUPLEX_ON BIT(12)
#define MXL86110_LEDX_CFG_LINK_UP_HALF_DUPLEX_ON BIT(11)
#define MXL86110_LEDX_CFG_LINK_UP_TX_ACT_ON BIT(10)
#define MXL86110_LEDX_CFG_LINK_UP_RX_ACT_ON BIT(9)
#define MXL86110_LEDX_CFG_LINK_UP_TX_ON BIT(8)
#define MXL86110_LEDX_CFG_LINK_UP_RX_ON BIT(7)
#define MXL86110_LEDX_CFG_LINK_UP_1GB_ON BIT(6)
#define MXL86110_LEDX_CFG_LINK_UP_100MB_ON BIT(5)
#define MXL86110_LEDX_CFG_LINK_UP_10MB_ON BIT(4)
#define MXL86110_LEDX_CFG_LINK_UP_COLLISION BIT(3)
#define MXL86110_LEDX_CFG_LINK_UP_1GB_BLINK BIT(2)
#define MXL86110_LEDX_CFG_LINK_UP_100MB_BLINK BIT(1)
#define MXL86110_LEDX_CFG_LINK_UP_10MB_BLINK BIT(0)
#define MXL86110_LED_BLINK_CFG_REG 0xA00F
#define MXL86110_LED_BLINK_CFG_FREQ_MODE1_2HZ 0
#define MXL86110_LED_BLINK_CFG_FREQ_MODE1_4HZ BIT(0)
#define MXL86110_LED_BLINK_CFG_FREQ_MODE1_8HZ BIT(1)
#define MXL86110_LED_BLINK_CFG_FREQ_MODE1_16HZ (BIT(1) | BIT(0))
#define MXL86110_LED_BLINK_CFG_FREQ_MODE2_2HZ 0
#define MXL86110_LED_BLINK_CFG_FREQ_MODE2_4HZ BIT(2)
#define MXL86110_LED_BLINK_CFG_FREQ_MODE2_8HZ BIT(3)
#define MXL86110_LED_BLINK_CFG_FREQ_MODE2_16HZ (BIT(3) | BIT(2))
#define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_50_ON 0
#define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_67_ON (BIT(4))
#define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_75_ON (BIT(5))
#define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_83_ON (BIT(5) | BIT(4))
#define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_50_OFF (BIT(6))
#define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_33_ON (BIT(6) | BIT(4))
#define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_25_ON (BIT(6) | BIT(5))
#define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_17_ON (BIT(6) | BIT(5) | BIT(4))
#define MXL86110_EXT_CHIP_CFG_REG 0xA001
#define MXL86111_EXT_CHIP_CFG_MODE_SEL_MASK GENMASK(2, 0)
#define MXL86111_EXT_CHIP_CFG_MODE_UTP_TO_RGMII 0
#define MXL86111_EXT_CHIP_CFG_MODE_FIBER_TO_RGMII 1
#define MXL86111_EXT_CHIP_CFG_MODE_UTP_FIBER_TO_RGMII 2
#define MXL86111_EXT_CHIP_CFG_MODE_UTP_TO_SGMII 3
#define MXL86111_EXT_CHIP_CFG_MODE_SGPHY_TO_RGMAC 4
#define MXL86111_EXT_CHIP_CFG_MODE_SGMAC_TO_RGPHY 5
#define MXL86111_EXT_CHIP_CFG_MODE_UTP_TO_FIBER_AUTO 6
#define MXL86111_EXT_CHIP_CFG_MODE_UTP_TO_FIBER_FORCE 7
#define MXL86111_EXT_CHIP_CFG_CLDO_MASK GENMASK(5, 4)
#define MXL86111_EXT_CHIP_CFG_CLDO_3V3 0
#define MXL86111_EXT_CHIP_CFG_CLDO_2V5 1
#define MXL86111_EXT_CHIP_CFG_CLDO_1V8_2 2
#define MXL86111_EXT_CHIP_CFG_CLDO_1V8_3 3
#define MXL86111_EXT_CHIP_CFG_CLDO_SHIFT 4
#define MXL86111_EXT_CHIP_CFG_ELDO BIT(6)
#define MXL86110_EXT_CHIP_CFG_RXDLY_ENABLE BIT(8)
#define MXL86110_EXT_CHIP_CFG_SW_RST_N_MODE BIT(15)
#define MXL86111_PHY_STAT_REG 0x11
#define MXL86111_PHY_STAT_SPEED_MASK GENMASK(15, 14)
#define MXL86111_PHY_STAT_SPEED_OFFSET 14
#define MXL86111_PHY_STAT_SPEED_10M 0x0
#define MXL86111_PHY_STAT_SPEED_100M 0x1
#define MXL86111_PHY_STAT_SPEED_1000M 0x2
#define MXL86111_PHY_STAT_DPX_OFFSET 13
#define MXL86111_PHY_STAT_DPX BIT(13)
#define MXL86111_PHY_STAT_LSRT BIT(10)
#define MXL86111_MODE_FIBER 0x1
#define MXL86111_MODE_UTP 0x2
#define MXL86111_MODE_AUTO 0x3
#define MXL86111_SDS_AN_LPA_PAUSE (0x3 << 7)
#define MXL86111_SDS_AN_LPA_ASYM_PAUSE (0x2 << 7)
#define MXL86111_EXT_MISC_CONFIG_REG 0xa006
#define MXL86111_EXT_MISC_CONFIG_FIB_SPEED_SEL BIT(0)
#define MXL86111_EXT_MISC_CONFIG_FIB_SPEED_SEL_1000BX (0x1 << 0)
#define MXL86111_EXT_MISC_CONFIG_FIB_SPEED_SEL_100BX (0x0 << 0)
#define MXL86111_EXT_SDS_LINK_TIMER_CFG2_REG 0xA5
#define MXL86111_EXT_SDS_LINK_TIMER_CFG2_EN_AUTOSEN BIT(15)
#define MII_BMSR_DEFAULT_VAL 0x7949
#define MII_ESTATUS_DEFAULT_VAL 0x2000
#define BMCR_RESET_TIMEOUT 500
#define MXL86111_PL_P1 0x500
static int __mxl86110_write_extended_reg(struct phy_device *phydev,
u16 regnum, u16 val)
{
int ret;
ret = __phy_write(phydev, MXL86110_EXTD_REG_ADDR_OFFSET, regnum);
if (ret < 0)
return ret;
return __phy_write(phydev, MXL86110_EXTD_REG_ADDR_DATA, val);
}
static int __mxl86110_read_extended_reg(struct phy_device *phydev, u16 regnum)
{
int ret;
ret = __phy_write(phydev, MXL86110_EXTD_REG_ADDR_OFFSET, regnum);
if (ret < 0)
return ret;
return __phy_read(phydev, MXL86110_EXTD_REG_ADDR_DATA);
}
static int __mxl86110_modify_extended_reg(struct phy_device *phydev,
u16 regnum, u16 mask, u16 set)
{
int ret;
ret = __phy_write(phydev, MXL86110_EXTD_REG_ADDR_OFFSET, regnum);
if (ret < 0)
return ret;
return __phy_modify(phydev, MXL86110_EXTD_REG_ADDR_DATA, mask, set);
}
static int mxl86110_write_extended_reg(struct phy_device *phydev,
u16 regnum, u16 val)
{
int ret;
phy_lock_mdio_bus(phydev);
ret = __mxl86110_write_extended_reg(phydev, regnum, val);
phy_unlock_mdio_bus(phydev);
return ret;
}
static int mxl86110_read_extended_reg(struct phy_device *phydev, u16 regnum)
{
int ret;
phy_lock_mdio_bus(phydev);
ret = __mxl86110_read_extended_reg(phydev, regnum);
phy_unlock_mdio_bus(phydev);
return ret;
}
static int mxl86110_modify_extended_reg(struct phy_device *phydev,
u16 regnum, u16 mask, u16 set)
{
int ret;
phy_lock_mdio_bus(phydev);
ret = __mxl86110_modify_extended_reg(phydev, regnum, mask, set);
phy_unlock_mdio_bus(phydev);
return ret;
}
static void mxl86110_get_wol(struct phy_device *phydev,
struct ethtool_wolinfo *wol)
{
int val;
wol->supported = WAKE_MAGIC;
wol->wolopts = 0;
val = mxl86110_read_extended_reg(phydev, MXL86110_EXT_WOL_CFG_REG);
if (val >= 0 && (val & MXL86110_WOL_CFG_WOL_MASK))
wol->wolopts |= WAKE_MAGIC;
}
static int mxl86110_set_wol(struct phy_device *phydev,
struct ethtool_wolinfo *wol)
{
struct net_device *netdev;
const unsigned char *mac;
int ret = 0;
phy_lock_mdio_bus(phydev);
if (wol->wolopts & WAKE_MAGIC) {
netdev = phydev->attached_dev;
if (!netdev) {
ret = -ENODEV;
goto out;
}
mac = netdev->dev_addr;
ret = __mxl86110_write_extended_reg(phydev,
MXL86110_EXT_MAC_ADDR_CFG1,
((mac[0] << 8) | mac[1]));
if (ret < 0)
goto out;
ret = __mxl86110_write_extended_reg(phydev,
MXL86110_EXT_MAC_ADDR_CFG2,
((mac[2] << 8) | mac[3]));
if (ret < 0)
goto out;
ret = __mxl86110_write_extended_reg(phydev,
MXL86110_EXT_MAC_ADDR_CFG3,
((mac[4] << 8) | mac[5]));
if (ret < 0)
goto out;
ret = __mxl86110_modify_extended_reg(phydev,
MXL86110_EXT_WOL_CFG_REG,
MXL86110_WOL_CFG_WOL_MASK,
MXL86110_WOL_CFG_WOL_MASK);
if (ret < 0)
goto out;
ret = __phy_modify(phydev, PHY_IRQ_ENABLE_REG, 0,
PHY_IRQ_ENABLE_REG_WOL);
if (ret < 0)
goto out;
phydev_dbg(phydev,
"%s, MAC Addr: %02X:%02X:%02X:%02X:%02X:%02X\n",
__func__,
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
} else {
ret = __mxl86110_modify_extended_reg(phydev,
MXL86110_EXT_WOL_CFG_REG,
MXL86110_WOL_CFG_WOL_MASK,
0);
if (ret < 0)
goto out;
ret = __phy_modify(phydev, PHY_IRQ_ENABLE_REG,
PHY_IRQ_ENABLE_REG_WOL, 0);
}
out:
phy_unlock_mdio_bus(phydev);
return ret;
}
static const unsigned long supported_trgs = (BIT(TRIGGER_NETDEV_LINK_10) |
BIT(TRIGGER_NETDEV_LINK_100) |
BIT(TRIGGER_NETDEV_LINK_1000) |
BIT(TRIGGER_NETDEV_HALF_DUPLEX) |
BIT(TRIGGER_NETDEV_FULL_DUPLEX) |
BIT(TRIGGER_NETDEV_TX) |
BIT(TRIGGER_NETDEV_RX));
static int mxl86110_led_hw_is_supported(struct phy_device *phydev, u8 index,
unsigned long rules)
{
if (index >= MXL86110_MAX_LEDS)
return -EINVAL;
if (rules & ~supported_trgs)
return -EOPNOTSUPP;
return 0;
}
static int mxl86110_led_hw_control_get(struct phy_device *phydev, u8 index,
unsigned long *rules)
{
int val;
if (index >= MXL86110_MAX_LEDS)
return -EINVAL;
val = mxl86110_read_extended_reg(phydev,
MXL86110_LED0_CFG_REG + index);
if (val < 0)
return val;
if (val & MXL86110_LEDX_CFG_LINK_UP_TX_ACT_ON)
*rules |= BIT(TRIGGER_NETDEV_TX);
if (val & MXL86110_LEDX_CFG_LINK_UP_RX_ACT_ON)
*rules |= BIT(TRIGGER_NETDEV_RX);
if (val & MXL86110_LEDX_CFG_LINK_UP_HALF_DUPLEX_ON)
*rules |= BIT(TRIGGER_NETDEV_HALF_DUPLEX);
if (val & MXL86110_LEDX_CFG_LINK_UP_FULL_DUPLEX_ON)
*rules |= BIT(TRIGGER_NETDEV_FULL_DUPLEX);
if (val & MXL86110_LEDX_CFG_LINK_UP_10MB_ON)
*rules |= BIT(TRIGGER_NETDEV_LINK_10);
if (val & MXL86110_LEDX_CFG_LINK_UP_100MB_ON)
*rules |= BIT(TRIGGER_NETDEV_LINK_100);
if (val & MXL86110_LEDX_CFG_LINK_UP_1GB_ON)
*rules |= BIT(TRIGGER_NETDEV_LINK_1000);
return 0;
}
static int mxl86110_led_hw_control_set(struct phy_device *phydev, u8 index,
unsigned long rules)
{
u16 val = 0;
int ret;
if (index >= MXL86110_MAX_LEDS)
return -EINVAL;
if (rules & BIT(TRIGGER_NETDEV_LINK_10))
val |= MXL86110_LEDX_CFG_LINK_UP_10MB_ON;
if (rules & BIT(TRIGGER_NETDEV_LINK_100))
val |= MXL86110_LEDX_CFG_LINK_UP_100MB_ON;
if (rules & BIT(TRIGGER_NETDEV_LINK_1000))
val |= MXL86110_LEDX_CFG_LINK_UP_1GB_ON;
if (rules & BIT(TRIGGER_NETDEV_TX))
val |= MXL86110_LEDX_CFG_LINK_UP_TX_ACT_ON;
if (rules & BIT(TRIGGER_NETDEV_RX))
val |= MXL86110_LEDX_CFG_LINK_UP_RX_ACT_ON;
if (rules & BIT(TRIGGER_NETDEV_HALF_DUPLEX))
val |= MXL86110_LEDX_CFG_LINK_UP_HALF_DUPLEX_ON;
if (rules & BIT(TRIGGER_NETDEV_FULL_DUPLEX))
val |= MXL86110_LEDX_CFG_LINK_UP_FULL_DUPLEX_ON;
if (rules & BIT(TRIGGER_NETDEV_TX) ||
rules & BIT(TRIGGER_NETDEV_RX))
val |= MXL86110_LEDX_CFG_BLINK;
ret = mxl86110_write_extended_reg(phydev,
MXL86110_LED0_CFG_REG + index, val);
if (ret)
return ret;
ret = mxl86110_modify_extended_reg(phydev,
MXL86110_COM_EXT_LED_GEN_CFG,
MXL86110_COM_EXT_LED_GEN_CFG_LFE(index),
0);
return ret;
}
static int mxl86110_led_brightness_set(struct phy_device *phydev,
u8 index, enum led_brightness value)
{
u16 mask, set;
int ret;
if (index >= MXL86110_MAX_LEDS)
return -EINVAL;
set = MXL86110_COM_EXT_LED_GEN_CFG_LFE(index);
mask = MXL86110_COM_EXT_LED_GEN_CFG_LFM(index);
if (value != LED_OFF)
set |= MXL86110_COM_EXT_LED_GEN_CFG_LFME(index);
ret = mxl86110_modify_extended_reg(phydev,
MXL86110_COM_EXT_LED_GEN_CFG,
mask, set);
return ret;
}
static int mxl86110_synce_clk_cfg(struct phy_device *phydev)
{
u16 mask = 0, val = 0;
val = MXL86110_EXT_SYNCE_CFG_EN_SYNC_E |
FIELD_PREP(MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_MASK,
MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_25M);
mask = MXL86110_EXT_SYNCE_CFG_EN_SYNC_E |
MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_MASK |
MXL86110_EXT_SYNCE_CFG_CLK_FRE_SEL;
return __mxl86110_modify_extended_reg(phydev,
MXL86110_EXT_SYNCE_CFG_REG,
mask, val);
}
static int mxl86110_broadcast_cfg(struct phy_device *phydev)
{
return __mxl86110_modify_extended_reg(phydev,
MXL86110_EXT_RGMII_MDIO_CFG,
MXL86110_RGMII_MDIO_CFG_EPA0_MASK,
0);
}
static int mxl86110_enable_led_activity_blink(struct phy_device *phydev)
{
int i, ret = 0;
for (i = 0; i < MXL86110_MAX_LEDS; i++) {
ret = __mxl86110_modify_extended_reg(phydev,
MXL86110_LED0_CFG_REG + i,
0,
MXL86110_LEDX_CFG_BLINK);
if (ret < 0)
break;
}
return ret;
}
static int mxl86110_config_rgmii_delay(struct phy_device *phydev)
{
int ret;
u16 val;
switch (phydev->interface) {
case PHY_INTERFACE_MODE_RGMII:
val = 0;
break;
case PHY_INTERFACE_MODE_RGMII_RXID:
val = MXL86110_EXT_RGMII_CFG1_RX_DELAY_1950PS;
break;
case PHY_INTERFACE_MODE_RGMII_TXID:
val = MXL86110_EXT_RGMII_CFG1_TX_1G_DELAY_1950PS |
MXL86110_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_1950PS;
break;
case PHY_INTERFACE_MODE_RGMII_ID:
val = MXL86110_EXT_RGMII_CFG1_TX_1G_DELAY_1950PS |
MXL86110_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_1950PS |
MXL86110_EXT_RGMII_CFG1_RX_DELAY_1950PS;
break;
default:
ret = -EINVAL;
goto out;
}
ret = __mxl86110_modify_extended_reg(phydev,
MXL86110_EXT_RGMII_CFG1_REG,
MXL86110_EXT_RGMII_CFG1_FULL_MASK,
val);
if (ret < 0)
goto out;
ret = __mxl86110_modify_extended_reg(phydev,
MXL86110_EXT_CHIP_CFG_REG,
MXL86110_EXT_CHIP_CFG_RXDLY_ENABLE,
0);
if (ret < 0)
goto out;
out:
return ret;
}
static int mxl86110_config_init(struct phy_device *phydev)
{
int ret;
phy_lock_mdio_bus(phydev);
ret = mxl86110_synce_clk_cfg(phydev);
if (ret < 0)
goto out;
ret = mxl86110_config_rgmii_delay(phydev);
if (ret < 0)
goto out;
ret = mxl86110_enable_led_activity_blink(phydev);
if (ret < 0)
goto out;
ret = mxl86110_broadcast_cfg(phydev);
out:
phy_unlock_mdio_bus(phydev);
return ret;
}
static int mxl86111_probe(struct phy_device *phydev)
{
int chip_config;
u16 reg_page;
int ret;
chip_config = mxl86110_read_extended_reg(phydev, MXL86110_EXT_CHIP_CFG_REG);
if (chip_config < 0)
return chip_config;
switch (chip_config & MXL86111_EXT_CHIP_CFG_MODE_SEL_MASK) {
case MXL86111_EXT_CHIP_CFG_MODE_UTP_TO_SGMII:
case MXL86111_EXT_CHIP_CFG_MODE_UTP_TO_RGMII:
phydev->port = PORT_TP;
reg_page = MXL86111_EXT_SMI_SDS_PHYUTP_SPACE;
break;
default:
return -EOPNOTSUPP;
}
ret = mxl86110_write_extended_reg(phydev,
MXL86111_EXT_SMI_SDS_PHY_REG,
reg_page);
if (ret < 0)
return ret;
return 0;
}
static int mxl86111_config_init(struct phy_device *phydev)
{
int ret;
phy_lock_mdio_bus(phydev);
ret = mxl86110_synce_clk_cfg(phydev);
if (ret < 0)
goto out;
switch (phydev->interface) {
case PHY_INTERFACE_MODE_100BASEX:
ret = __mxl86110_modify_extended_reg(phydev,
MXL86111_EXT_MISC_CONFIG_REG,
MXL86111_EXT_MISC_CONFIG_FIB_SPEED_SEL,
MXL86111_EXT_MISC_CONFIG_FIB_SPEED_SEL_100BX);
if (ret < 0)
goto out;
break;
case PHY_INTERFACE_MODE_1000BASEX:
case PHY_INTERFACE_MODE_SGMII:
ret = __mxl86110_modify_extended_reg(phydev,
MXL86111_EXT_MISC_CONFIG_REG,
MXL86111_EXT_MISC_CONFIG_FIB_SPEED_SEL,
MXL86111_EXT_MISC_CONFIG_FIB_SPEED_SEL_1000BX);
if (ret < 0)
goto out;
break;
default:
ret = mxl86110_config_rgmii_delay(phydev);
if (ret < 0)
goto out;
ret = __mxl86110_modify_extended_reg(phydev, MXL86110_EXT_RGMII_CFG1_REG,
MXL86110_EXT_RGMII_CFG1_FULL_MASK, ret);
ret = __mxl86110_read_extended_reg(phydev, 0xf);
if (ret < 0)
goto out;
if (ret == MXL86111_PL_P1) {
ret = __mxl86110_read_extended_reg(phydev, MXL86110_EXT_CHIP_CFG_REG);
if (ret < 0)
goto out;
switch (FIELD_GET(MXL86111_EXT_CHIP_CFG_CLDO_MASK, ret)) {
case MXL86111_EXT_CHIP_CFG_CLDO_1V8_3:
case MXL86111_EXT_CHIP_CFG_CLDO_1V8_2:
ret = __mxl86110_write_extended_reg(phydev, 0xa010, 0xabff);
if (ret < 0)
goto out;
break;
default:
break;
}
}
break;
}
ret = mxl86110_enable_led_activity_blink(phydev);
if (ret < 0)
goto out;
ret = mxl86110_broadcast_cfg(phydev);
out:
phy_unlock_mdio_bus(phydev);
return ret;
}
static int mxl86111_read_page(struct phy_device *phydev)
{
int page;
page = __mxl86110_read_extended_reg(phydev, MXL86111_EXT_SMI_SDS_PHY_REG);
if (page < 0)
return page;
return page & MXL86111_EXT_SMI_SDS_PHYSPACE_MASK;
};
static int mxl86111_write_page(struct phy_device *phydev, int page)
{
return __mxl86110_modify_extended_reg(phydev, MXL86111_EXT_SMI_SDS_PHY_REG,
MXL86111_EXT_SMI_SDS_PHYSPACE_MASK, page);
};
static int mxl86111_config_inband(struct phy_device *phydev, unsigned int modes)
{
int ret;
ret = phy_modify_paged(phydev, MXL86111_EXT_SMI_SDS_PHYFIBER_SPACE,
MII_BMCR, BMCR_ANENABLE,
(modes == LINK_INBAND_DISABLE) ? 0 : BMCR_ANENABLE);
if (ret < 0)
goto out;
phy_lock_mdio_bus(phydev);
ret = __mxl86110_modify_extended_reg(phydev, MXL86111_EXT_SDS_LINK_TIMER_CFG2_REG,
MXL86111_EXT_SDS_LINK_TIMER_CFG2_EN_AUTOSEN,
(modes == LINK_INBAND_DISABLE) ? 0 :
MXL86111_EXT_SDS_LINK_TIMER_CFG2_EN_AUTOSEN);
if (ret < 0)
goto out;
ret = __mxl86110_modify_extended_reg(phydev, MXL86110_EXT_CHIP_CFG_REG,
MXL86110_EXT_CHIP_CFG_SW_RST_N_MODE, 0);
if (ret < 0)
goto out;
if (modes != LINK_INBAND_DISABLE) {
__phy_modify(phydev, MII_BMCR, 0, BMCR_PDOWN);
usleep_range(1000, 1050);
__phy_modify(phydev, MII_BMCR, BMCR_PDOWN, 0);
}
out:
phy_unlock_mdio_bus(phydev);
return ret;
}
static unsigned int mxl86111_inband_caps(struct phy_device *phydev,
phy_interface_t interface)
{
switch (interface) {
case PHY_INTERFACE_MODE_100BASEX:
case PHY_INTERFACE_MODE_1000BASEX:
case PHY_INTERFACE_MODE_SGMII:
return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
default:
return 0;
}
}
static struct phy_driver mxl_phy_drvs[] = {
{
PHY_ID_MATCH_EXACT(PHY_ID_MXL86110),
.name = "MXL86110 Gigabit Ethernet",
.config_init = mxl86110_config_init,
.suspend = genphy_suspend,
.resume = genphy_resume,
.soft_reset = genphy_soft_reset,
.get_wol = mxl86110_get_wol,
.set_wol = mxl86110_set_wol,
.led_brightness_set = mxl86110_led_brightness_set,
.led_hw_is_supported = mxl86110_led_hw_is_supported,
.led_hw_control_get = mxl86110_led_hw_control_get,
.led_hw_control_set = mxl86110_led_hw_control_set,
},
{
PHY_ID_MATCH_EXACT(PHY_ID_MXL86111),
.name = "MXL86111 Gigabit Ethernet",
.probe = mxl86111_probe,
.config_init = mxl86111_config_init,
.get_wol = mxl86110_get_wol,
.set_wol = mxl86110_set_wol,
.inband_caps = mxl86111_inband_caps,
.config_inband = mxl86111_config_inband,
.read_page = mxl86111_read_page,
.write_page = mxl86111_write_page,
.led_brightness_set = mxl86110_led_brightness_set,
.led_hw_is_supported = mxl86110_led_hw_is_supported,
.led_hw_control_get = mxl86110_led_hw_control_get,
.led_hw_control_set = mxl86110_led_hw_control_set,
},
};
module_phy_driver(mxl_phy_drvs);
static const struct mdio_device_id __maybe_unused mxl_tbl[] = {
{ PHY_ID_MATCH_EXACT(PHY_ID_MXL86110) },
{ PHY_ID_MATCH_EXACT(PHY_ID_MXL86111) },
{ }
};
MODULE_DEVICE_TABLE(mdio, mxl_tbl);
MODULE_DESCRIPTION("MaxLinear MXL86110/MXL86111 PHY driver");
MODULE_AUTHOR("Stefano Radaelli");
MODULE_LICENSE("GPL");