1*b2908a98SStefano Radaelli // SPDX-License-Identifier: GPL-2.0+ 2*b2908a98SStefano Radaelli /* 3*b2908a98SStefano Radaelli * PHY driver for Maxlinear MXL86110 4*b2908a98SStefano Radaelli * 5*b2908a98SStefano Radaelli * Copyright 2023 MaxLinear Inc. 6*b2908a98SStefano Radaelli * 7*b2908a98SStefano Radaelli */ 8*b2908a98SStefano Radaelli 9*b2908a98SStefano Radaelli #include <linux/bitfield.h> 10*b2908a98SStefano Radaelli #include <linux/etherdevice.h> 11*b2908a98SStefano Radaelli #include <linux/kernel.h> 12*b2908a98SStefano Radaelli #include <linux/module.h> 13*b2908a98SStefano Radaelli #include <linux/of.h> 14*b2908a98SStefano Radaelli #include <linux/phy.h> 15*b2908a98SStefano Radaelli 16*b2908a98SStefano Radaelli /* PHY ID */ 17*b2908a98SStefano Radaelli #define PHY_ID_MXL86110 0xc1335580 18*b2908a98SStefano Radaelli 19*b2908a98SStefano Radaelli /* required to access extended registers */ 20*b2908a98SStefano Radaelli #define MXL86110_EXTD_REG_ADDR_OFFSET 0x1E 21*b2908a98SStefano Radaelli #define MXL86110_EXTD_REG_ADDR_DATA 0x1F 22*b2908a98SStefano Radaelli #define PHY_IRQ_ENABLE_REG 0x12 23*b2908a98SStefano Radaelli #define PHY_IRQ_ENABLE_REG_WOL BIT(6) 24*b2908a98SStefano Radaelli 25*b2908a98SStefano Radaelli /* SyncE Configuration Register - COM_EXT SYNCE_CFG */ 26*b2908a98SStefano Radaelli #define MXL86110_EXT_SYNCE_CFG_REG 0xA012 27*b2908a98SStefano Radaelli #define MXL86110_EXT_SYNCE_CFG_CLK_FRE_SEL BIT(4) 28*b2908a98SStefano Radaelli #define MXL86110_EXT_SYNCE_CFG_EN_SYNC_E_DURING_LNKDN BIT(5) 29*b2908a98SStefano Radaelli #define MXL86110_EXT_SYNCE_CFG_EN_SYNC_E BIT(6) 30*b2908a98SStefano Radaelli #define MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_MASK GENMASK(3, 1) 31*b2908a98SStefano Radaelli #define MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_125M_PLL 0 32*b2908a98SStefano Radaelli #define MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_25M 4 33*b2908a98SStefano Radaelli 34*b2908a98SStefano Radaelli /* MAC Address registers */ 35*b2908a98SStefano Radaelli #define MXL86110_EXT_MAC_ADDR_CFG1 0xA007 36*b2908a98SStefano Radaelli #define MXL86110_EXT_MAC_ADDR_CFG2 0xA008 37*b2908a98SStefano Radaelli #define MXL86110_EXT_MAC_ADDR_CFG3 0xA009 38*b2908a98SStefano Radaelli 39*b2908a98SStefano Radaelli #define MXL86110_EXT_WOL_CFG_REG 0xA00A 40*b2908a98SStefano Radaelli #define MXL86110_WOL_CFG_WOL_MASK BIT(3) 41*b2908a98SStefano Radaelli 42*b2908a98SStefano Radaelli /* RGMII register */ 43*b2908a98SStefano Radaelli #define MXL86110_EXT_RGMII_CFG1_REG 0xA003 44*b2908a98SStefano Radaelli /* delay can be adjusted in steps of about 150ps */ 45*b2908a98SStefano Radaelli #define MXL86110_EXT_RGMII_CFG1_RX_NO_DELAY (0x0 << 10) 46*b2908a98SStefano Radaelli /* Closest value to 2000 ps */ 47*b2908a98SStefano Radaelli #define MXL86110_EXT_RGMII_CFG1_RX_DELAY_1950PS (0xD << 10) 48*b2908a98SStefano Radaelli #define MXL86110_EXT_RGMII_CFG1_RX_DELAY_MASK GENMASK(13, 10) 49*b2908a98SStefano Radaelli 50*b2908a98SStefano Radaelli #define MXL86110_EXT_RGMII_CFG1_TX_1G_DELAY_1950PS (0xD << 0) 51*b2908a98SStefano Radaelli #define MXL86110_EXT_RGMII_CFG1_TX_1G_DELAY_MASK GENMASK(3, 0) 52*b2908a98SStefano Radaelli 53*b2908a98SStefano Radaelli #define MXL86110_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_1950PS (0xD << 4) 54*b2908a98SStefano Radaelli #define MXL86110_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_MASK GENMASK(7, 4) 55*b2908a98SStefano Radaelli 56*b2908a98SStefano Radaelli #define MXL86110_EXT_RGMII_CFG1_FULL_MASK \ 57*b2908a98SStefano Radaelli ((MXL86110_EXT_RGMII_CFG1_RX_DELAY_MASK) | \ 58*b2908a98SStefano Radaelli (MXL86110_EXT_RGMII_CFG1_TX_1G_DELAY_MASK) | \ 59*b2908a98SStefano Radaelli (MXL86110_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_MASK)) 60*b2908a98SStefano Radaelli 61*b2908a98SStefano Radaelli /* EXT Sleep Control register */ 62*b2908a98SStefano Radaelli #define MXL86110_UTP_EXT_SLEEP_CTRL_REG 0x27 63*b2908a98SStefano Radaelli #define MXL86110_UTP_EXT_SLEEP_CTRL_EN_SLEEP_SW_OFF 0 64*b2908a98SStefano Radaelli #define MXL86110_UTP_EXT_SLEEP_CTRL_EN_SLEEP_SW_MASK BIT(15) 65*b2908a98SStefano Radaelli 66*b2908a98SStefano Radaelli /* RGMII In-Band Status and MDIO Configuration Register */ 67*b2908a98SStefano Radaelli #define MXL86110_EXT_RGMII_MDIO_CFG 0xA005 68*b2908a98SStefano Radaelli #define MXL86110_RGMII_MDIO_CFG_EPA0_MASK GENMASK(6, 6) 69*b2908a98SStefano Radaelli #define MXL86110_EXT_RGMII_MDIO_CFG_EBA_MASK GENMASK(5, 5) 70*b2908a98SStefano Radaelli #define MXL86110_EXT_RGMII_MDIO_CFG_BA_MASK GENMASK(4, 0) 71*b2908a98SStefano Radaelli 72*b2908a98SStefano Radaelli #define MXL86110_MAX_LEDS 3 73*b2908a98SStefano Radaelli /* LED registers and defines */ 74*b2908a98SStefano Radaelli #define MXL86110_LED0_CFG_REG 0xA00C 75*b2908a98SStefano Radaelli #define MXL86110_LED1_CFG_REG 0xA00D 76*b2908a98SStefano Radaelli #define MXL86110_LED2_CFG_REG 0xA00E 77*b2908a98SStefano Radaelli 78*b2908a98SStefano Radaelli #define MXL86110_LEDX_CFG_BLINK BIT(13) 79*b2908a98SStefano Radaelli #define MXL86110_LEDX_CFG_LINK_UP_FULL_DUPLEX_ON BIT(12) 80*b2908a98SStefano Radaelli #define MXL86110_LEDX_CFG_LINK_UP_HALF_DUPLEX_ON BIT(11) 81*b2908a98SStefano Radaelli #define MXL86110_LEDX_CFG_LINK_UP_TX_ACT_ON BIT(10) 82*b2908a98SStefano Radaelli #define MXL86110_LEDX_CFG_LINK_UP_RX_ACT_ON BIT(9) 83*b2908a98SStefano Radaelli #define MXL86110_LEDX_CFG_LINK_UP_TX_ON BIT(8) 84*b2908a98SStefano Radaelli #define MXL86110_LEDX_CFG_LINK_UP_RX_ON BIT(7) 85*b2908a98SStefano Radaelli #define MXL86110_LEDX_CFG_LINK_UP_1GB_ON BIT(6) 86*b2908a98SStefano Radaelli #define MXL86110_LEDX_CFG_LINK_UP_100MB_ON BIT(5) 87*b2908a98SStefano Radaelli #define MXL86110_LEDX_CFG_LINK_UP_10MB_ON BIT(4) 88*b2908a98SStefano Radaelli #define MXL86110_LEDX_CFG_LINK_UP_COLLISION BIT(3) 89*b2908a98SStefano Radaelli #define MXL86110_LEDX_CFG_LINK_UP_1GB_BLINK BIT(2) 90*b2908a98SStefano Radaelli #define MXL86110_LEDX_CFG_LINK_UP_100MB_BLINK BIT(1) 91*b2908a98SStefano Radaelli #define MXL86110_LEDX_CFG_LINK_UP_10MB_BLINK BIT(0) 92*b2908a98SStefano Radaelli 93*b2908a98SStefano Radaelli #define MXL86110_LED_BLINK_CFG_REG 0xA00F 94*b2908a98SStefano Radaelli #define MXL86110_LED_BLINK_CFG_FREQ_MODE1_2HZ 0 95*b2908a98SStefano Radaelli #define MXL86110_LED_BLINK_CFG_FREQ_MODE1_4HZ BIT(0) 96*b2908a98SStefano Radaelli #define MXL86110_LED_BLINK_CFG_FREQ_MODE1_8HZ BIT(1) 97*b2908a98SStefano Radaelli #define MXL86110_LED_BLINK_CFG_FREQ_MODE1_16HZ (BIT(1) | BIT(0)) 98*b2908a98SStefano Radaelli #define MXL86110_LED_BLINK_CFG_FREQ_MODE2_2HZ 0 99*b2908a98SStefano Radaelli #define MXL86110_LED_BLINK_CFG_FREQ_MODE2_4HZ BIT(2) 100*b2908a98SStefano Radaelli #define MXL86110_LED_BLINK_CFG_FREQ_MODE2_8HZ BIT(3) 101*b2908a98SStefano Radaelli #define MXL86110_LED_BLINK_CFG_FREQ_MODE2_16HZ (BIT(3) | BIT(2)) 102*b2908a98SStefano Radaelli #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_50_ON 0 103*b2908a98SStefano Radaelli #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_67_ON (BIT(4)) 104*b2908a98SStefano Radaelli #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_75_ON (BIT(5)) 105*b2908a98SStefano Radaelli #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_83_ON (BIT(5) | BIT(4)) 106*b2908a98SStefano Radaelli #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_50_OFF (BIT(6)) 107*b2908a98SStefano Radaelli #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_33_ON (BIT(6) | BIT(4)) 108*b2908a98SStefano Radaelli #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_25_ON (BIT(6) | BIT(5)) 109*b2908a98SStefano Radaelli #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_17_ON (BIT(6) | BIT(5) | BIT(4)) 110*b2908a98SStefano Radaelli 111*b2908a98SStefano Radaelli /* Chip Configuration Register - COM_EXT_CHIP_CFG */ 112*b2908a98SStefano Radaelli #define MXL86110_EXT_CHIP_CFG_REG 0xA001 113*b2908a98SStefano Radaelli #define MXL86110_EXT_CHIP_CFG_RXDLY_ENABLE BIT(8) 114*b2908a98SStefano Radaelli #define MXL86110_EXT_CHIP_CFG_SW_RST_N_MODE BIT(15) 115*b2908a98SStefano Radaelli 116*b2908a98SStefano Radaelli /** 117*b2908a98SStefano Radaelli * __mxl86110_write_extended_reg() - write to a PHY's extended register 118*b2908a98SStefano Radaelli * @phydev: pointer to the PHY device structure 119*b2908a98SStefano Radaelli * @regnum: register number to write 120*b2908a98SStefano Radaelli * @val: value to write to @regnum 121*b2908a98SStefano Radaelli * 122*b2908a98SStefano Radaelli * Unlocked version of mxl86110_write_extended_reg 123*b2908a98SStefano Radaelli * 124*b2908a98SStefano Radaelli * Note: This function assumes the caller already holds the MDIO bus lock 125*b2908a98SStefano Radaelli * or otherwise has exclusive access to the PHY. 126*b2908a98SStefano Radaelli * 127*b2908a98SStefano Radaelli * Return: 0 or negative error code 128*b2908a98SStefano Radaelli */ 129*b2908a98SStefano Radaelli static int __mxl86110_write_extended_reg(struct phy_device *phydev, 130*b2908a98SStefano Radaelli u16 regnum, u16 val) 131*b2908a98SStefano Radaelli { 132*b2908a98SStefano Radaelli int ret; 133*b2908a98SStefano Radaelli 134*b2908a98SStefano Radaelli ret = __phy_write(phydev, MXL86110_EXTD_REG_ADDR_OFFSET, regnum); 135*b2908a98SStefano Radaelli if (ret < 0) 136*b2908a98SStefano Radaelli return ret; 137*b2908a98SStefano Radaelli 138*b2908a98SStefano Radaelli return __phy_write(phydev, MXL86110_EXTD_REG_ADDR_DATA, val); 139*b2908a98SStefano Radaelli } 140*b2908a98SStefano Radaelli 141*b2908a98SStefano Radaelli /** 142*b2908a98SStefano Radaelli * __mxl86110_read_extended_reg - Read a PHY's extended register 143*b2908a98SStefano Radaelli * @phydev: pointer to the PHY device structure 144*b2908a98SStefano Radaelli * @regnum: extended register number to read (address written to reg 30) 145*b2908a98SStefano Radaelli * 146*b2908a98SStefano Radaelli * Unlocked version of mxl86110_read_extended_reg 147*b2908a98SStefano Radaelli * 148*b2908a98SStefano Radaelli * Reads the content of a PHY extended register using the MaxLinear 149*b2908a98SStefano Radaelli * 2-step access mechanism: write the register address to reg 30 (0x1E), 150*b2908a98SStefano Radaelli * then read the value from reg 31 (0x1F). 151*b2908a98SStefano Radaelli * 152*b2908a98SStefano Radaelli * Note: This function assumes the caller already holds the MDIO bus lock 153*b2908a98SStefano Radaelli * or otherwise has exclusive access to the PHY. 154*b2908a98SStefano Radaelli * 155*b2908a98SStefano Radaelli * Return: 16-bit register value on success, or negative errno code on failure. 156*b2908a98SStefano Radaelli */ 157*b2908a98SStefano Radaelli static int __mxl86110_read_extended_reg(struct phy_device *phydev, u16 regnum) 158*b2908a98SStefano Radaelli { 159*b2908a98SStefano Radaelli int ret; 160*b2908a98SStefano Radaelli 161*b2908a98SStefano Radaelli ret = __phy_write(phydev, MXL86110_EXTD_REG_ADDR_OFFSET, regnum); 162*b2908a98SStefano Radaelli if (ret < 0) 163*b2908a98SStefano Radaelli return ret; 164*b2908a98SStefano Radaelli return __phy_read(phydev, MXL86110_EXTD_REG_ADDR_DATA); 165*b2908a98SStefano Radaelli } 166*b2908a98SStefano Radaelli 167*b2908a98SStefano Radaelli /** 168*b2908a98SStefano Radaelli * __mxl86110_modify_extended_reg() - modify bits of a PHY's extended register 169*b2908a98SStefano Radaelli * @phydev: pointer to the PHY device structure 170*b2908a98SStefano Radaelli * @regnum: register number to write 171*b2908a98SStefano Radaelli * @mask: bit mask of bits to clear 172*b2908a98SStefano Radaelli * @set: bit mask of bits to set 173*b2908a98SStefano Radaelli * 174*b2908a98SStefano Radaelli * Note: register value = (old register value & ~mask) | set. 175*b2908a98SStefano Radaelli * This function assumes the caller already holds the MDIO bus lock 176*b2908a98SStefano Radaelli * or otherwise has exclusive access to the PHY. 177*b2908a98SStefano Radaelli * 178*b2908a98SStefano Radaelli * Return: 0 or negative error code 179*b2908a98SStefano Radaelli */ 180*b2908a98SStefano Radaelli static int __mxl86110_modify_extended_reg(struct phy_device *phydev, 181*b2908a98SStefano Radaelli u16 regnum, u16 mask, u16 set) 182*b2908a98SStefano Radaelli { 183*b2908a98SStefano Radaelli int ret; 184*b2908a98SStefano Radaelli 185*b2908a98SStefano Radaelli ret = __phy_write(phydev, MXL86110_EXTD_REG_ADDR_OFFSET, regnum); 186*b2908a98SStefano Radaelli if (ret < 0) 187*b2908a98SStefano Radaelli return ret; 188*b2908a98SStefano Radaelli 189*b2908a98SStefano Radaelli return __phy_modify(phydev, MXL86110_EXTD_REG_ADDR_DATA, mask, set); 190*b2908a98SStefano Radaelli } 191*b2908a98SStefano Radaelli 192*b2908a98SStefano Radaelli /** 193*b2908a98SStefano Radaelli * mxl86110_write_extended_reg() - Write to a PHY's extended register 194*b2908a98SStefano Radaelli * @phydev: pointer to the PHY device structure 195*b2908a98SStefano Radaelli * @regnum: register number to write 196*b2908a98SStefano Radaelli * @val: value to write to @regnum 197*b2908a98SStefano Radaelli * 198*b2908a98SStefano Radaelli * This function writes to an extended register of the PHY using the 199*b2908a98SStefano Radaelli * MaxLinear two-step access method (reg 0x1E/0x1F). It handles acquiring 200*b2908a98SStefano Radaelli * and releasing the MDIO bus lock internally. 201*b2908a98SStefano Radaelli * 202*b2908a98SStefano Radaelli * Return: 0 or negative error code 203*b2908a98SStefano Radaelli */ 204*b2908a98SStefano Radaelli static int mxl86110_write_extended_reg(struct phy_device *phydev, 205*b2908a98SStefano Radaelli u16 regnum, u16 val) 206*b2908a98SStefano Radaelli { 207*b2908a98SStefano Radaelli int ret; 208*b2908a98SStefano Radaelli 209*b2908a98SStefano Radaelli phy_lock_mdio_bus(phydev); 210*b2908a98SStefano Radaelli ret = __mxl86110_write_extended_reg(phydev, regnum, val); 211*b2908a98SStefano Radaelli phy_unlock_mdio_bus(phydev); 212*b2908a98SStefano Radaelli 213*b2908a98SStefano Radaelli return ret; 214*b2908a98SStefano Radaelli } 215*b2908a98SStefano Radaelli 216*b2908a98SStefano Radaelli /** 217*b2908a98SStefano Radaelli * mxl86110_read_extended_reg() - Read a PHY's extended register 218*b2908a98SStefano Radaelli * @phydev: pointer to the PHY device structure 219*b2908a98SStefano Radaelli * @regnum: extended register number to read 220*b2908a98SStefano Radaelli * 221*b2908a98SStefano Radaelli * This function reads from an extended register of the PHY using the 222*b2908a98SStefano Radaelli * MaxLinear two-step access method (reg 0x1E/0x1F). It handles acquiring 223*b2908a98SStefano Radaelli * and releasing the MDIO bus lock internally. 224*b2908a98SStefano Radaelli * 225*b2908a98SStefano Radaelli * Return: 16-bit register value on success, or negative errno code on failure 226*b2908a98SStefano Radaelli */ 227*b2908a98SStefano Radaelli static int mxl86110_read_extended_reg(struct phy_device *phydev, u16 regnum) 228*b2908a98SStefano Radaelli { 229*b2908a98SStefano Radaelli int ret; 230*b2908a98SStefano Radaelli 231*b2908a98SStefano Radaelli phy_lock_mdio_bus(phydev); 232*b2908a98SStefano Radaelli ret = __mxl86110_read_extended_reg(phydev, regnum); 233*b2908a98SStefano Radaelli phy_unlock_mdio_bus(phydev); 234*b2908a98SStefano Radaelli 235*b2908a98SStefano Radaelli return ret; 236*b2908a98SStefano Radaelli } 237*b2908a98SStefano Radaelli 238*b2908a98SStefano Radaelli /** 239*b2908a98SStefano Radaelli * mxl86110_get_wol() - report if wake-on-lan is enabled 240*b2908a98SStefano Radaelli * @phydev: pointer to the phy_device 241*b2908a98SStefano Radaelli * @wol: a pointer to a &struct ethtool_wolinfo 242*b2908a98SStefano Radaelli */ 243*b2908a98SStefano Radaelli static void mxl86110_get_wol(struct phy_device *phydev, 244*b2908a98SStefano Radaelli struct ethtool_wolinfo *wol) 245*b2908a98SStefano Radaelli { 246*b2908a98SStefano Radaelli int val; 247*b2908a98SStefano Radaelli 248*b2908a98SStefano Radaelli wol->supported = WAKE_MAGIC; 249*b2908a98SStefano Radaelli wol->wolopts = 0; 250*b2908a98SStefano Radaelli val = mxl86110_read_extended_reg(phydev, MXL86110_EXT_WOL_CFG_REG); 251*b2908a98SStefano Radaelli if (val >= 0 && (val & MXL86110_WOL_CFG_WOL_MASK)) 252*b2908a98SStefano Radaelli wol->wolopts |= WAKE_MAGIC; 253*b2908a98SStefano Radaelli } 254*b2908a98SStefano Radaelli 255*b2908a98SStefano Radaelli /** 256*b2908a98SStefano Radaelli * mxl86110_set_wol() - enable/disable wake-on-lan 257*b2908a98SStefano Radaelli * @phydev: pointer to the phy_device 258*b2908a98SStefano Radaelli * @wol: a pointer to a &struct ethtool_wolinfo 259*b2908a98SStefano Radaelli * 260*b2908a98SStefano Radaelli * Configures the WOL Magic Packet MAC 261*b2908a98SStefano Radaelli * 262*b2908a98SStefano Radaelli * Return: 0 or negative errno code 263*b2908a98SStefano Radaelli */ 264*b2908a98SStefano Radaelli static int mxl86110_set_wol(struct phy_device *phydev, 265*b2908a98SStefano Radaelli struct ethtool_wolinfo *wol) 266*b2908a98SStefano Radaelli { 267*b2908a98SStefano Radaelli struct net_device *netdev; 268*b2908a98SStefano Radaelli const unsigned char *mac; 269*b2908a98SStefano Radaelli int ret = 0; 270*b2908a98SStefano Radaelli 271*b2908a98SStefano Radaelli phy_lock_mdio_bus(phydev); 272*b2908a98SStefano Radaelli 273*b2908a98SStefano Radaelli if (wol->wolopts & WAKE_MAGIC) { 274*b2908a98SStefano Radaelli netdev = phydev->attached_dev; 275*b2908a98SStefano Radaelli if (!netdev) { 276*b2908a98SStefano Radaelli ret = -ENODEV; 277*b2908a98SStefano Radaelli goto out; 278*b2908a98SStefano Radaelli } 279*b2908a98SStefano Radaelli 280*b2908a98SStefano Radaelli /* Configure the MAC address of the WOL magic packet */ 281*b2908a98SStefano Radaelli mac = netdev->dev_addr; 282*b2908a98SStefano Radaelli ret = __mxl86110_write_extended_reg(phydev, 283*b2908a98SStefano Radaelli MXL86110_EXT_MAC_ADDR_CFG1, 284*b2908a98SStefano Radaelli ((mac[0] << 8) | mac[1])); 285*b2908a98SStefano Radaelli if (ret < 0) 286*b2908a98SStefano Radaelli goto out; 287*b2908a98SStefano Radaelli 288*b2908a98SStefano Radaelli ret = __mxl86110_write_extended_reg(phydev, 289*b2908a98SStefano Radaelli MXL86110_EXT_MAC_ADDR_CFG2, 290*b2908a98SStefano Radaelli ((mac[2] << 8) | mac[3])); 291*b2908a98SStefano Radaelli if (ret < 0) 292*b2908a98SStefano Radaelli goto out; 293*b2908a98SStefano Radaelli 294*b2908a98SStefano Radaelli ret = __mxl86110_write_extended_reg(phydev, 295*b2908a98SStefano Radaelli MXL86110_EXT_MAC_ADDR_CFG3, 296*b2908a98SStefano Radaelli ((mac[4] << 8) | mac[5])); 297*b2908a98SStefano Radaelli if (ret < 0) 298*b2908a98SStefano Radaelli goto out; 299*b2908a98SStefano Radaelli 300*b2908a98SStefano Radaelli ret = __mxl86110_modify_extended_reg(phydev, 301*b2908a98SStefano Radaelli MXL86110_EXT_WOL_CFG_REG, 302*b2908a98SStefano Radaelli MXL86110_WOL_CFG_WOL_MASK, 303*b2908a98SStefano Radaelli MXL86110_WOL_CFG_WOL_MASK); 304*b2908a98SStefano Radaelli if (ret < 0) 305*b2908a98SStefano Radaelli goto out; 306*b2908a98SStefano Radaelli 307*b2908a98SStefano Radaelli /* Enables Wake-on-LAN interrupt in the PHY. */ 308*b2908a98SStefano Radaelli ret = __phy_modify(phydev, PHY_IRQ_ENABLE_REG, 0, 309*b2908a98SStefano Radaelli PHY_IRQ_ENABLE_REG_WOL); 310*b2908a98SStefano Radaelli if (ret < 0) 311*b2908a98SStefano Radaelli goto out; 312*b2908a98SStefano Radaelli 313*b2908a98SStefano Radaelli phydev_dbg(phydev, 314*b2908a98SStefano Radaelli "%s, MAC Addr: %02X:%02X:%02X:%02X:%02X:%02X\n", 315*b2908a98SStefano Radaelli __func__, 316*b2908a98SStefano Radaelli mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 317*b2908a98SStefano Radaelli } else { 318*b2908a98SStefano Radaelli ret = __mxl86110_modify_extended_reg(phydev, 319*b2908a98SStefano Radaelli MXL86110_EXT_WOL_CFG_REG, 320*b2908a98SStefano Radaelli MXL86110_WOL_CFG_WOL_MASK, 321*b2908a98SStefano Radaelli 0); 322*b2908a98SStefano Radaelli if (ret < 0) 323*b2908a98SStefano Radaelli goto out; 324*b2908a98SStefano Radaelli 325*b2908a98SStefano Radaelli /* Disables Wake-on-LAN interrupt in the PHY. */ 326*b2908a98SStefano Radaelli ret = __phy_modify(phydev, PHY_IRQ_ENABLE_REG, 327*b2908a98SStefano Radaelli PHY_IRQ_ENABLE_REG_WOL, 0); 328*b2908a98SStefano Radaelli } 329*b2908a98SStefano Radaelli 330*b2908a98SStefano Radaelli out: 331*b2908a98SStefano Radaelli phy_unlock_mdio_bus(phydev); 332*b2908a98SStefano Radaelli return ret; 333*b2908a98SStefano Radaelli } 334*b2908a98SStefano Radaelli 335*b2908a98SStefano Radaelli static const unsigned long supported_trgs = (BIT(TRIGGER_NETDEV_LINK_10) | 336*b2908a98SStefano Radaelli BIT(TRIGGER_NETDEV_LINK_100) | 337*b2908a98SStefano Radaelli BIT(TRIGGER_NETDEV_LINK_1000) | 338*b2908a98SStefano Radaelli BIT(TRIGGER_NETDEV_HALF_DUPLEX) | 339*b2908a98SStefano Radaelli BIT(TRIGGER_NETDEV_FULL_DUPLEX) | 340*b2908a98SStefano Radaelli BIT(TRIGGER_NETDEV_TX) | 341*b2908a98SStefano Radaelli BIT(TRIGGER_NETDEV_RX)); 342*b2908a98SStefano Radaelli 343*b2908a98SStefano Radaelli static int mxl86110_led_hw_is_supported(struct phy_device *phydev, u8 index, 344*b2908a98SStefano Radaelli unsigned long rules) 345*b2908a98SStefano Radaelli { 346*b2908a98SStefano Radaelli if (index >= MXL86110_MAX_LEDS) 347*b2908a98SStefano Radaelli return -EINVAL; 348*b2908a98SStefano Radaelli 349*b2908a98SStefano Radaelli /* All combinations of the supported triggers are allowed */ 350*b2908a98SStefano Radaelli if (rules & ~supported_trgs) 351*b2908a98SStefano Radaelli return -EOPNOTSUPP; 352*b2908a98SStefano Radaelli 353*b2908a98SStefano Radaelli return 0; 354*b2908a98SStefano Radaelli } 355*b2908a98SStefano Radaelli 356*b2908a98SStefano Radaelli static int mxl86110_led_hw_control_get(struct phy_device *phydev, u8 index, 357*b2908a98SStefano Radaelli unsigned long *rules) 358*b2908a98SStefano Radaelli { 359*b2908a98SStefano Radaelli int val; 360*b2908a98SStefano Radaelli 361*b2908a98SStefano Radaelli if (index >= MXL86110_MAX_LEDS) 362*b2908a98SStefano Radaelli return -EINVAL; 363*b2908a98SStefano Radaelli 364*b2908a98SStefano Radaelli val = mxl86110_read_extended_reg(phydev, 365*b2908a98SStefano Radaelli MXL86110_LED0_CFG_REG + index); 366*b2908a98SStefano Radaelli if (val < 0) 367*b2908a98SStefano Radaelli return val; 368*b2908a98SStefano Radaelli 369*b2908a98SStefano Radaelli if (val & MXL86110_LEDX_CFG_LINK_UP_TX_ACT_ON) 370*b2908a98SStefano Radaelli *rules |= BIT(TRIGGER_NETDEV_TX); 371*b2908a98SStefano Radaelli 372*b2908a98SStefano Radaelli if (val & MXL86110_LEDX_CFG_LINK_UP_RX_ACT_ON) 373*b2908a98SStefano Radaelli *rules |= BIT(TRIGGER_NETDEV_RX); 374*b2908a98SStefano Radaelli 375*b2908a98SStefano Radaelli if (val & MXL86110_LEDX_CFG_LINK_UP_HALF_DUPLEX_ON) 376*b2908a98SStefano Radaelli *rules |= BIT(TRIGGER_NETDEV_HALF_DUPLEX); 377*b2908a98SStefano Radaelli 378*b2908a98SStefano Radaelli if (val & MXL86110_LEDX_CFG_LINK_UP_FULL_DUPLEX_ON) 379*b2908a98SStefano Radaelli *rules |= BIT(TRIGGER_NETDEV_FULL_DUPLEX); 380*b2908a98SStefano Radaelli 381*b2908a98SStefano Radaelli if (val & MXL86110_LEDX_CFG_LINK_UP_10MB_ON) 382*b2908a98SStefano Radaelli *rules |= BIT(TRIGGER_NETDEV_LINK_10); 383*b2908a98SStefano Radaelli 384*b2908a98SStefano Radaelli if (val & MXL86110_LEDX_CFG_LINK_UP_100MB_ON) 385*b2908a98SStefano Radaelli *rules |= BIT(TRIGGER_NETDEV_LINK_100); 386*b2908a98SStefano Radaelli 387*b2908a98SStefano Radaelli if (val & MXL86110_LEDX_CFG_LINK_UP_1GB_ON) 388*b2908a98SStefano Radaelli *rules |= BIT(TRIGGER_NETDEV_LINK_1000); 389*b2908a98SStefano Radaelli 390*b2908a98SStefano Radaelli return 0; 391*b2908a98SStefano Radaelli } 392*b2908a98SStefano Radaelli 393*b2908a98SStefano Radaelli static int mxl86110_led_hw_control_set(struct phy_device *phydev, u8 index, 394*b2908a98SStefano Radaelli unsigned long rules) 395*b2908a98SStefano Radaelli { 396*b2908a98SStefano Radaelli u16 val = 0; 397*b2908a98SStefano Radaelli 398*b2908a98SStefano Radaelli if (index >= MXL86110_MAX_LEDS) 399*b2908a98SStefano Radaelli return -EINVAL; 400*b2908a98SStefano Radaelli 401*b2908a98SStefano Radaelli if (rules & BIT(TRIGGER_NETDEV_LINK_10)) 402*b2908a98SStefano Radaelli val |= MXL86110_LEDX_CFG_LINK_UP_10MB_ON; 403*b2908a98SStefano Radaelli 404*b2908a98SStefano Radaelli if (rules & BIT(TRIGGER_NETDEV_LINK_100)) 405*b2908a98SStefano Radaelli val |= MXL86110_LEDX_CFG_LINK_UP_100MB_ON; 406*b2908a98SStefano Radaelli 407*b2908a98SStefano Radaelli if (rules & BIT(TRIGGER_NETDEV_LINK_1000)) 408*b2908a98SStefano Radaelli val |= MXL86110_LEDX_CFG_LINK_UP_1GB_ON; 409*b2908a98SStefano Radaelli 410*b2908a98SStefano Radaelli if (rules & BIT(TRIGGER_NETDEV_TX)) 411*b2908a98SStefano Radaelli val |= MXL86110_LEDX_CFG_LINK_UP_TX_ACT_ON; 412*b2908a98SStefano Radaelli 413*b2908a98SStefano Radaelli if (rules & BIT(TRIGGER_NETDEV_RX)) 414*b2908a98SStefano Radaelli val |= MXL86110_LEDX_CFG_LINK_UP_RX_ACT_ON; 415*b2908a98SStefano Radaelli 416*b2908a98SStefano Radaelli if (rules & BIT(TRIGGER_NETDEV_HALF_DUPLEX)) 417*b2908a98SStefano Radaelli val |= MXL86110_LEDX_CFG_LINK_UP_HALF_DUPLEX_ON; 418*b2908a98SStefano Radaelli 419*b2908a98SStefano Radaelli if (rules & BIT(TRIGGER_NETDEV_FULL_DUPLEX)) 420*b2908a98SStefano Radaelli val |= MXL86110_LEDX_CFG_LINK_UP_FULL_DUPLEX_ON; 421*b2908a98SStefano Radaelli 422*b2908a98SStefano Radaelli if (rules & BIT(TRIGGER_NETDEV_TX) || 423*b2908a98SStefano Radaelli rules & BIT(TRIGGER_NETDEV_RX)) 424*b2908a98SStefano Radaelli val |= MXL86110_LEDX_CFG_BLINK; 425*b2908a98SStefano Radaelli 426*b2908a98SStefano Radaelli return mxl86110_write_extended_reg(phydev, 427*b2908a98SStefano Radaelli MXL86110_LED0_CFG_REG + index, val); 428*b2908a98SStefano Radaelli } 429*b2908a98SStefano Radaelli 430*b2908a98SStefano Radaelli /** 431*b2908a98SStefano Radaelli * mxl86110_synce_clk_cfg() - applies syncE/clk output configuration 432*b2908a98SStefano Radaelli * @phydev: pointer to the phy_device 433*b2908a98SStefano Radaelli * 434*b2908a98SStefano Radaelli * Note: This function assumes the caller already holds the MDIO bus lock 435*b2908a98SStefano Radaelli * or otherwise has exclusive access to the PHY. 436*b2908a98SStefano Radaelli * 437*b2908a98SStefano Radaelli * Return: 0 or negative errno code 438*b2908a98SStefano Radaelli */ 439*b2908a98SStefano Radaelli static int mxl86110_synce_clk_cfg(struct phy_device *phydev) 440*b2908a98SStefano Radaelli { 441*b2908a98SStefano Radaelli u16 mask = 0, val = 0; 442*b2908a98SStefano Radaelli 443*b2908a98SStefano Radaelli /* 444*b2908a98SStefano Radaelli * Configures the clock output to its default 445*b2908a98SStefano Radaelli * setting as per the datasheet. 446*b2908a98SStefano Radaelli * This results in a 25MHz clock output being selected in the 447*b2908a98SStefano Radaelli * COM_EXT_SYNCE_CFG register for SyncE configuration. 448*b2908a98SStefano Radaelli */ 449*b2908a98SStefano Radaelli val = MXL86110_EXT_SYNCE_CFG_EN_SYNC_E | 450*b2908a98SStefano Radaelli FIELD_PREP(MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_MASK, 451*b2908a98SStefano Radaelli MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_25M); 452*b2908a98SStefano Radaelli mask = MXL86110_EXT_SYNCE_CFG_EN_SYNC_E | 453*b2908a98SStefano Radaelli MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_MASK | 454*b2908a98SStefano Radaelli MXL86110_EXT_SYNCE_CFG_CLK_FRE_SEL; 455*b2908a98SStefano Radaelli 456*b2908a98SStefano Radaelli /* Write clock output configuration */ 457*b2908a98SStefano Radaelli return __mxl86110_modify_extended_reg(phydev, 458*b2908a98SStefano Radaelli MXL86110_EXT_SYNCE_CFG_REG, 459*b2908a98SStefano Radaelli mask, val); 460*b2908a98SStefano Radaelli } 461*b2908a98SStefano Radaelli 462*b2908a98SStefano Radaelli /** 463*b2908a98SStefano Radaelli * mxl86110_broadcast_cfg - Configure MDIO broadcast setting for PHY 464*b2908a98SStefano Radaelli * @phydev: Pointer to the PHY device structure 465*b2908a98SStefano Radaelli * 466*b2908a98SStefano Radaelli * This function configures the MDIO broadcast behavior of the MxL86110 PHY. 467*b2908a98SStefano Radaelli * Currently, broadcast mode is explicitly disabled by clearing the EPA0 bit 468*b2908a98SStefano Radaelli * in the RGMII_MDIO_CFG extended register. 469*b2908a98SStefano Radaelli * 470*b2908a98SStefano Radaelli * Note: This function assumes the caller already holds the MDIO bus lock 471*b2908a98SStefano Radaelli * or otherwise has exclusive access to the PHY. 472*b2908a98SStefano Radaelli * 473*b2908a98SStefano Radaelli * Return: 0 on success or a negative errno code on failure. 474*b2908a98SStefano Radaelli */ 475*b2908a98SStefano Radaelli static int mxl86110_broadcast_cfg(struct phy_device *phydev) 476*b2908a98SStefano Radaelli { 477*b2908a98SStefano Radaelli return __mxl86110_modify_extended_reg(phydev, 478*b2908a98SStefano Radaelli MXL86110_EXT_RGMII_MDIO_CFG, 479*b2908a98SStefano Radaelli MXL86110_RGMII_MDIO_CFG_EPA0_MASK, 480*b2908a98SStefano Radaelli 0); 481*b2908a98SStefano Radaelli } 482*b2908a98SStefano Radaelli 483*b2908a98SStefano Radaelli /** 484*b2908a98SStefano Radaelli * mxl86110_enable_led_activity_blink - Enable LEDs activity blink on PHY 485*b2908a98SStefano Radaelli * @phydev: Pointer to the PHY device structure 486*b2908a98SStefano Radaelli * 487*b2908a98SStefano Radaelli * Configure all PHY LEDs to blink on traffic activity regardless of whether 488*b2908a98SStefano Radaelli * they are ON or OFF. This behavior allows each LED to serve as a pure activity 489*b2908a98SStefano Radaelli * indicator, independently of its use as a link status indicator. 490*b2908a98SStefano Radaelli * 491*b2908a98SStefano Radaelli * By default, each LED blinks only when it is also in the ON state. 492*b2908a98SStefano Radaelli * This function modifies the appropriate registers (LABx fields) 493*b2908a98SStefano Radaelli * to enable blinking even when the LEDs are OFF, to allow the LED to be used 494*b2908a98SStefano Radaelli * as a traffic indicator without requiring it to also serve 495*b2908a98SStefano Radaelli * as a link status LED. 496*b2908a98SStefano Radaelli * 497*b2908a98SStefano Radaelli * Note: Any further LED customization can be performed via the 498*b2908a98SStefano Radaelli * /sys/class/leds interface; the functions led_hw_is_supported, 499*b2908a98SStefano Radaelli * led_hw_control_get, and led_hw_control_set are used 500*b2908a98SStefano Radaelli * to support this mechanism. 501*b2908a98SStefano Radaelli * 502*b2908a98SStefano Radaelli * This function assumes the caller already holds the MDIO bus lock 503*b2908a98SStefano Radaelli * or otherwise has exclusive access to the PHY. 504*b2908a98SStefano Radaelli * 505*b2908a98SStefano Radaelli * Return: 0 on success or a negative errno code on failure. 506*b2908a98SStefano Radaelli */ 507*b2908a98SStefano Radaelli static int mxl86110_enable_led_activity_blink(struct phy_device *phydev) 508*b2908a98SStefano Radaelli { 509*b2908a98SStefano Radaelli int i, ret = 0; 510*b2908a98SStefano Radaelli 511*b2908a98SStefano Radaelli for (i = 0; i < MXL86110_MAX_LEDS; i++) { 512*b2908a98SStefano Radaelli ret = __mxl86110_modify_extended_reg(phydev, 513*b2908a98SStefano Radaelli MXL86110_LED0_CFG_REG + i, 514*b2908a98SStefano Radaelli 0, 515*b2908a98SStefano Radaelli MXL86110_LEDX_CFG_BLINK); 516*b2908a98SStefano Radaelli if (ret < 0) 517*b2908a98SStefano Radaelli break; 518*b2908a98SStefano Radaelli } 519*b2908a98SStefano Radaelli 520*b2908a98SStefano Radaelli return ret; 521*b2908a98SStefano Radaelli } 522*b2908a98SStefano Radaelli 523*b2908a98SStefano Radaelli /** 524*b2908a98SStefano Radaelli * mxl86110_config_init() - initialize the PHY 525*b2908a98SStefano Radaelli * @phydev: pointer to the phy_device 526*b2908a98SStefano Radaelli * 527*b2908a98SStefano Radaelli * Return: 0 or negative errno code 528*b2908a98SStefano Radaelli */ 529*b2908a98SStefano Radaelli static int mxl86110_config_init(struct phy_device *phydev) 530*b2908a98SStefano Radaelli { 531*b2908a98SStefano Radaelli u16 val = 0; 532*b2908a98SStefano Radaelli int ret; 533*b2908a98SStefano Radaelli 534*b2908a98SStefano Radaelli phy_lock_mdio_bus(phydev); 535*b2908a98SStefano Radaelli 536*b2908a98SStefano Radaelli /* configure syncE / clk output */ 537*b2908a98SStefano Radaelli ret = mxl86110_synce_clk_cfg(phydev); 538*b2908a98SStefano Radaelli if (ret < 0) 539*b2908a98SStefano Radaelli goto out; 540*b2908a98SStefano Radaelli 541*b2908a98SStefano Radaelli switch (phydev->interface) { 542*b2908a98SStefano Radaelli case PHY_INTERFACE_MODE_RGMII: 543*b2908a98SStefano Radaelli val = 0; 544*b2908a98SStefano Radaelli break; 545*b2908a98SStefano Radaelli case PHY_INTERFACE_MODE_RGMII_RXID: 546*b2908a98SStefano Radaelli val = MXL86110_EXT_RGMII_CFG1_RX_DELAY_1950PS; 547*b2908a98SStefano Radaelli break; 548*b2908a98SStefano Radaelli case PHY_INTERFACE_MODE_RGMII_TXID: 549*b2908a98SStefano Radaelli val = MXL86110_EXT_RGMII_CFG1_TX_1G_DELAY_1950PS | 550*b2908a98SStefano Radaelli MXL86110_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_1950PS; 551*b2908a98SStefano Radaelli break; 552*b2908a98SStefano Radaelli case PHY_INTERFACE_MODE_RGMII_ID: 553*b2908a98SStefano Radaelli val = MXL86110_EXT_RGMII_CFG1_TX_1G_DELAY_1950PS | 554*b2908a98SStefano Radaelli MXL86110_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_1950PS | 555*b2908a98SStefano Radaelli MXL86110_EXT_RGMII_CFG1_RX_DELAY_1950PS; 556*b2908a98SStefano Radaelli break; 557*b2908a98SStefano Radaelli default: 558*b2908a98SStefano Radaelli ret = -EINVAL; 559*b2908a98SStefano Radaelli goto out; 560*b2908a98SStefano Radaelli } 561*b2908a98SStefano Radaelli 562*b2908a98SStefano Radaelli ret = __mxl86110_modify_extended_reg(phydev, 563*b2908a98SStefano Radaelli MXL86110_EXT_RGMII_CFG1_REG, 564*b2908a98SStefano Radaelli MXL86110_EXT_RGMII_CFG1_FULL_MASK, 565*b2908a98SStefano Radaelli val); 566*b2908a98SStefano Radaelli if (ret < 0) 567*b2908a98SStefano Radaelli goto out; 568*b2908a98SStefano Radaelli 569*b2908a98SStefano Radaelli /* Configure RXDLY (RGMII Rx Clock Delay) to disable 570*b2908a98SStefano Radaelli * the default additional delay value on RX_CLK 571*b2908a98SStefano Radaelli * (2 ns for 125 MHz, 8 ns for 25 MHz/2.5 MHz) 572*b2908a98SStefano Radaelli * and use just the digital one selected before 573*b2908a98SStefano Radaelli */ 574*b2908a98SStefano Radaelli ret = __mxl86110_modify_extended_reg(phydev, 575*b2908a98SStefano Radaelli MXL86110_EXT_CHIP_CFG_REG, 576*b2908a98SStefano Radaelli MXL86110_EXT_CHIP_CFG_RXDLY_ENABLE, 577*b2908a98SStefano Radaelli 0); 578*b2908a98SStefano Radaelli if (ret < 0) 579*b2908a98SStefano Radaelli goto out; 580*b2908a98SStefano Radaelli 581*b2908a98SStefano Radaelli ret = mxl86110_enable_led_activity_blink(phydev); 582*b2908a98SStefano Radaelli if (ret < 0) 583*b2908a98SStefano Radaelli goto out; 584*b2908a98SStefano Radaelli 585*b2908a98SStefano Radaelli ret = mxl86110_broadcast_cfg(phydev); 586*b2908a98SStefano Radaelli 587*b2908a98SStefano Radaelli out: 588*b2908a98SStefano Radaelli phy_unlock_mdio_bus(phydev); 589*b2908a98SStefano Radaelli return ret; 590*b2908a98SStefano Radaelli } 591*b2908a98SStefano Radaelli 592*b2908a98SStefano Radaelli static struct phy_driver mxl_phy_drvs[] = { 593*b2908a98SStefano Radaelli { 594*b2908a98SStefano Radaelli PHY_ID_MATCH_EXACT(PHY_ID_MXL86110), 595*b2908a98SStefano Radaelli .name = "MXL86110 Gigabit Ethernet", 596*b2908a98SStefano Radaelli .config_init = mxl86110_config_init, 597*b2908a98SStefano Radaelli .get_wol = mxl86110_get_wol, 598*b2908a98SStefano Radaelli .set_wol = mxl86110_set_wol, 599*b2908a98SStefano Radaelli .led_hw_is_supported = mxl86110_led_hw_is_supported, 600*b2908a98SStefano Radaelli .led_hw_control_get = mxl86110_led_hw_control_get, 601*b2908a98SStefano Radaelli .led_hw_control_set = mxl86110_led_hw_control_set, 602*b2908a98SStefano Radaelli }, 603*b2908a98SStefano Radaelli }; 604*b2908a98SStefano Radaelli 605*b2908a98SStefano Radaelli module_phy_driver(mxl_phy_drvs); 606*b2908a98SStefano Radaelli 607*b2908a98SStefano Radaelli static const struct mdio_device_id __maybe_unused mxl_tbl[] = { 608*b2908a98SStefano Radaelli { PHY_ID_MATCH_EXACT(PHY_ID_MXL86110) }, 609*b2908a98SStefano Radaelli { } 610*b2908a98SStefano Radaelli }; 611*b2908a98SStefano Radaelli 612*b2908a98SStefano Radaelli MODULE_DEVICE_TABLE(mdio, mxl_tbl); 613*b2908a98SStefano Radaelli 614*b2908a98SStefano Radaelli MODULE_DESCRIPTION("MaxLinear MXL86110 PHY driver"); 615*b2908a98SStefano Radaelli MODULE_AUTHOR("Stefano Radaelli"); 616*b2908a98SStefano Radaelli MODULE_LICENSE("GPL"); 617