101db923eSDan Murphy // SPDX-License-Identifier: GPL-2.0 201db923eSDan Murphy /* Driver for the Texas Instruments DP83869 PHY 301db923eSDan Murphy * Copyright (C) 2019 Texas Instruments Inc. 401db923eSDan Murphy */ 501db923eSDan Murphy 601db923eSDan Murphy #include <linux/ethtool.h> 749fc2301SDan Murphy #include <linux/etherdevice.h> 801db923eSDan Murphy #include <linux/kernel.h> 901db923eSDan Murphy #include <linux/mii.h> 1001db923eSDan Murphy #include <linux/module.h> 1101db923eSDan Murphy #include <linux/of.h> 1201db923eSDan Murphy #include <linux/phy.h> 1301db923eSDan Murphy #include <linux/delay.h> 14811ac400SDan Murphy #include <linux/bitfield.h> 1501db923eSDan Murphy 1601db923eSDan Murphy #include <dt-bindings/net/ti-dp83869.h> 1701db923eSDan Murphy 1801db923eSDan Murphy #define DP83869_PHY_ID 0x2000a0f1 1901db923eSDan Murphy #define DP83869_DEVADDR 0x1f 2001db923eSDan Murphy 2101db923eSDan Murphy #define MII_DP83869_PHYCTRL 0x10 2201db923eSDan Murphy #define MII_DP83869_MICR 0x12 2301db923eSDan Murphy #define MII_DP83869_ISR 0x13 24811ac400SDan Murphy #define DP83869_CFG2 0x14 2501db923eSDan Murphy #define DP83869_CTRL 0x1f 2601db923eSDan Murphy #define DP83869_CFG4 0x1e 2701db923eSDan Murphy 2801db923eSDan Murphy /* Extended Registers */ 2901db923eSDan Murphy #define DP83869_GEN_CFG3 0x0031 3001db923eSDan Murphy #define DP83869_RGMIICTL 0x0032 3101db923eSDan Murphy #define DP83869_STRAP_STS1 0x006e 3201db923eSDan Murphy #define DP83869_RGMIIDCTL 0x0086 3349fc2301SDan Murphy #define DP83869_RXFCFG 0x0134 3449fc2301SDan Murphy #define DP83869_RXFPMD1 0x0136 3549fc2301SDan Murphy #define DP83869_RXFPMD2 0x0137 3649fc2301SDan Murphy #define DP83869_RXFPMD3 0x0138 3749fc2301SDan Murphy #define DP83869_RXFSOP1 0x0139 3849fc2301SDan Murphy #define DP83869_RXFSOP2 0x013A 3949fc2301SDan Murphy #define DP83869_RXFSOP3 0x013B 4001db923eSDan Murphy #define DP83869_IO_MUX_CFG 0x0170 4101db923eSDan Murphy #define DP83869_OP_MODE 0x01df 4201db923eSDan Murphy #define DP83869_FX_CTRL 0x0c00 4301db923eSDan Murphy 4401db923eSDan Murphy #define DP83869_SW_RESET BIT(15) 4501db923eSDan Murphy #define DP83869_SW_RESTART BIT(14) 4601db923eSDan Murphy 4701db923eSDan Murphy /* MICR Interrupt bits */ 4801db923eSDan Murphy #define MII_DP83869_MICR_AN_ERR_INT_EN BIT(15) 4901db923eSDan Murphy #define MII_DP83869_MICR_SPEED_CHNG_INT_EN BIT(14) 5001db923eSDan Murphy #define MII_DP83869_MICR_DUP_MODE_CHNG_INT_EN BIT(13) 5101db923eSDan Murphy #define MII_DP83869_MICR_PAGE_RXD_INT_EN BIT(12) 5201db923eSDan Murphy #define MII_DP83869_MICR_AUTONEG_COMP_INT_EN BIT(11) 5301db923eSDan Murphy #define MII_DP83869_MICR_LINK_STS_CHNG_INT_EN BIT(10) 5401db923eSDan Murphy #define MII_DP83869_MICR_FALSE_CARRIER_INT_EN BIT(8) 5501db923eSDan Murphy #define MII_DP83869_MICR_SLEEP_MODE_CHNG_INT_EN BIT(4) 5601db923eSDan Murphy #define MII_DP83869_MICR_WOL_INT_EN BIT(3) 5701db923eSDan Murphy #define MII_DP83869_MICR_XGMII_ERR_INT_EN BIT(2) 5801db923eSDan Murphy #define MII_DP83869_MICR_POL_CHNG_INT_EN BIT(1) 5901db923eSDan Murphy #define MII_DP83869_MICR_JABBER_INT_EN BIT(0) 6001db923eSDan Murphy 6101db923eSDan Murphy #define MII_DP83869_BMCR_DEFAULT (BMCR_ANENABLE | \ 6201db923eSDan Murphy BMCR_FULLDPLX | \ 6301db923eSDan Murphy BMCR_SPEED1000) 6401db923eSDan Murphy 65a29de52bSDan Murphy #define MII_DP83869_FIBER_ADVERTISE (ADVERTISED_FIBRE | \ 66a29de52bSDan Murphy ADVERTISED_Pause | \ 67a29de52bSDan Murphy ADVERTISED_Asym_Pause) 68a29de52bSDan Murphy 6901db923eSDan Murphy /* This is the same bit mask as the BMCR so re-use the BMCR default */ 7001db923eSDan Murphy #define DP83869_FX_CTRL_DEFAULT MII_DP83869_BMCR_DEFAULT 7101db923eSDan Murphy 7201db923eSDan Murphy /* CFG1 bits */ 7301db923eSDan Murphy #define DP83869_CFG1_DEFAULT (ADVERTISE_1000HALF | \ 7401db923eSDan Murphy ADVERTISE_1000FULL | \ 7501db923eSDan Murphy CTL1000_AS_MASTER) 7601db923eSDan Murphy 7701db923eSDan Murphy /* RGMIICTL bits */ 7801db923eSDan Murphy #define DP83869_RGMII_TX_CLK_DELAY_EN BIT(1) 7901db923eSDan Murphy #define DP83869_RGMII_RX_CLK_DELAY_EN BIT(0) 8001db923eSDan Murphy 81736b25afSDan Murphy /* RGMIIDCTL */ 82736b25afSDan Murphy #define DP83869_RGMII_CLK_DELAY_SHIFT 4 83736b25afSDan Murphy #define DP83869_CLK_DELAY_DEF 7 84736b25afSDan Murphy 8501db923eSDan Murphy /* STRAP_STS1 bits */ 860eaf8ccfSDan Murphy #define DP83869_STRAP_OP_MODE_MASK GENMASK(2, 0) 8701db923eSDan Murphy #define DP83869_STRAP_STS1_RESERVED BIT(11) 88c4566aecSDan Murphy #define DP83869_STRAP_MIRROR_ENABLED BIT(12) 8901db923eSDan Murphy 9001db923eSDan Murphy /* PHYCTRL bits */ 9101db923eSDan Murphy #define DP83869_RX_FIFO_SHIFT 12 9201db923eSDan Murphy #define DP83869_TX_FIFO_SHIFT 14 9301db923eSDan Murphy 9401db923eSDan Murphy /* PHY_CTRL lower bytes 0x48 are declared as reserved */ 9501db923eSDan Murphy #define DP83869_PHY_CTRL_DEFAULT 0x48 9601db923eSDan Murphy #define DP83869_PHYCR_FIFO_DEPTH_MASK GENMASK(15, 12) 9701db923eSDan Murphy #define DP83869_PHYCR_RESERVED_MASK BIT(11) 9801db923eSDan Murphy 9901db923eSDan Murphy /* IO_MUX_CFG bits */ 10001db923eSDan Murphy #define DP83869_IO_MUX_CFG_IO_IMPEDANCE_CTRL 0x1f 10101db923eSDan Murphy 10201db923eSDan Murphy #define DP83869_IO_MUX_CFG_IO_IMPEDANCE_MAX 0x0 10301db923eSDan Murphy #define DP83869_IO_MUX_CFG_IO_IMPEDANCE_MIN 0x1f 10401db923eSDan Murphy #define DP83869_IO_MUX_CFG_CLK_O_SEL_MASK (0x1f << 8) 10501db923eSDan Murphy #define DP83869_IO_MUX_CFG_CLK_O_SEL_SHIFT 8 10601db923eSDan Murphy 10701db923eSDan Murphy /* CFG3 bits */ 10801db923eSDan Murphy #define DP83869_CFG3_PORT_MIRROR_EN BIT(0) 10901db923eSDan Murphy 11001db923eSDan Murphy /* CFG4 bits */ 11101db923eSDan Murphy #define DP83869_INT_OE BIT(7) 11201db923eSDan Murphy 11301db923eSDan Murphy /* OP MODE */ 11401db923eSDan Murphy #define DP83869_OP_MODE_MII BIT(5) 11501db923eSDan Murphy #define DP83869_SGMII_RGMII_BRIDGE BIT(6) 11601db923eSDan Murphy 11749fc2301SDan Murphy /* RXFCFG bits*/ 11849fc2301SDan Murphy #define DP83869_WOL_MAGIC_EN BIT(0) 11949fc2301SDan Murphy #define DP83869_WOL_PATTERN_EN BIT(1) 12049fc2301SDan Murphy #define DP83869_WOL_BCAST_EN BIT(2) 12149fc2301SDan Murphy #define DP83869_WOL_UCAST_EN BIT(4) 12249fc2301SDan Murphy #define DP83869_WOL_SEC_EN BIT(5) 12349fc2301SDan Murphy #define DP83869_WOL_ENH_MAC BIT(7) 12449fc2301SDan Murphy 125811ac400SDan Murphy /* CFG2 bits */ 126811ac400SDan Murphy #define DP83869_DOWNSHIFT_EN (BIT(8) | BIT(9)) 127811ac400SDan Murphy #define DP83869_DOWNSHIFT_ATTEMPT_MASK (BIT(10) | BIT(11)) 128811ac400SDan Murphy #define DP83869_DOWNSHIFT_1_COUNT_VAL 0 129811ac400SDan Murphy #define DP83869_DOWNSHIFT_2_COUNT_VAL 1 130811ac400SDan Murphy #define DP83869_DOWNSHIFT_4_COUNT_VAL 2 131811ac400SDan Murphy #define DP83869_DOWNSHIFT_8_COUNT_VAL 3 132811ac400SDan Murphy #define DP83869_DOWNSHIFT_1_COUNT 1 133811ac400SDan Murphy #define DP83869_DOWNSHIFT_2_COUNT 2 134811ac400SDan Murphy #define DP83869_DOWNSHIFT_4_COUNT 4 135811ac400SDan Murphy #define DP83869_DOWNSHIFT_8_COUNT 8 136811ac400SDan Murphy 13701db923eSDan Murphy enum { 13801db923eSDan Murphy DP83869_PORT_MIRRORING_KEEP, 13901db923eSDan Murphy DP83869_PORT_MIRRORING_EN, 14001db923eSDan Murphy DP83869_PORT_MIRRORING_DIS, 14101db923eSDan Murphy }; 14201db923eSDan Murphy 14301db923eSDan Murphy struct dp83869_private { 14401db923eSDan Murphy int tx_fifo_depth; 14501db923eSDan Murphy int rx_fifo_depth; 146736b25afSDan Murphy s32 rx_int_delay; 147736b25afSDan Murphy s32 tx_int_delay; 14801db923eSDan Murphy int io_impedance; 14901db923eSDan Murphy int port_mirroring; 15001db923eSDan Murphy bool rxctrl_strap_quirk; 15101db923eSDan Murphy int clk_output_sel; 15201db923eSDan Murphy int mode; 15301db923eSDan Murphy }; 15401db923eSDan Murphy 155a29de52bSDan Murphy static int dp83869_read_status(struct phy_device *phydev) 156a29de52bSDan Murphy { 157a29de52bSDan Murphy struct dp83869_private *dp83869 = phydev->priv; 158a29de52bSDan Murphy int ret; 159a29de52bSDan Murphy 160a29de52bSDan Murphy ret = genphy_read_status(phydev); 161a29de52bSDan Murphy if (ret) 162a29de52bSDan Murphy return ret; 163a29de52bSDan Murphy 164a29de52bSDan Murphy if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported)) { 165a29de52bSDan Murphy if (phydev->link) { 166a29de52bSDan Murphy if (dp83869->mode == DP83869_RGMII_100_BASE) 167a29de52bSDan Murphy phydev->speed = SPEED_100; 168a29de52bSDan Murphy } else { 169a29de52bSDan Murphy phydev->speed = SPEED_UNKNOWN; 170a29de52bSDan Murphy phydev->duplex = DUPLEX_UNKNOWN; 171a29de52bSDan Murphy } 172a29de52bSDan Murphy } 173a29de52bSDan Murphy 174a29de52bSDan Murphy return 0; 175a29de52bSDan Murphy } 176a29de52bSDan Murphy 17701db923eSDan Murphy static int dp83869_ack_interrupt(struct phy_device *phydev) 17801db923eSDan Murphy { 17901db923eSDan Murphy int err = phy_read(phydev, MII_DP83869_ISR); 18001db923eSDan Murphy 18101db923eSDan Murphy if (err < 0) 18201db923eSDan Murphy return err; 18301db923eSDan Murphy 18401db923eSDan Murphy return 0; 18501db923eSDan Murphy } 18601db923eSDan Murphy 18701db923eSDan Murphy static int dp83869_config_intr(struct phy_device *phydev) 18801db923eSDan Murphy { 189aa2d603aSIoana Ciornei int micr_status = 0, err; 19001db923eSDan Murphy 19101db923eSDan Murphy if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 192aa2d603aSIoana Ciornei err = dp83869_ack_interrupt(phydev); 193aa2d603aSIoana Ciornei if (err) 194aa2d603aSIoana Ciornei return err; 195aa2d603aSIoana Ciornei 19601db923eSDan Murphy micr_status = phy_read(phydev, MII_DP83869_MICR); 19701db923eSDan Murphy if (micr_status < 0) 19801db923eSDan Murphy return micr_status; 19901db923eSDan Murphy 20001db923eSDan Murphy micr_status |= 20101db923eSDan Murphy (MII_DP83869_MICR_AN_ERR_INT_EN | 20201db923eSDan Murphy MII_DP83869_MICR_SPEED_CHNG_INT_EN | 20301db923eSDan Murphy MII_DP83869_MICR_AUTONEG_COMP_INT_EN | 20401db923eSDan Murphy MII_DP83869_MICR_LINK_STS_CHNG_INT_EN | 20501db923eSDan Murphy MII_DP83869_MICR_DUP_MODE_CHNG_INT_EN | 20601db923eSDan Murphy MII_DP83869_MICR_SLEEP_MODE_CHNG_INT_EN); 20701db923eSDan Murphy 208aa2d603aSIoana Ciornei err = phy_write(phydev, MII_DP83869_MICR, micr_status); 209aa2d603aSIoana Ciornei } else { 210aa2d603aSIoana Ciornei err = phy_write(phydev, MII_DP83869_MICR, micr_status); 211aa2d603aSIoana Ciornei if (err) 212aa2d603aSIoana Ciornei return err; 213aa2d603aSIoana Ciornei 214aa2d603aSIoana Ciornei err = dp83869_ack_interrupt(phydev); 21501db923eSDan Murphy } 21601db923eSDan Murphy 217aa2d603aSIoana Ciornei return err; 21801db923eSDan Murphy } 21901db923eSDan Murphy 2201d1ae3c6SIoana Ciornei static irqreturn_t dp83869_handle_interrupt(struct phy_device *phydev) 2211d1ae3c6SIoana Ciornei { 2221d1ae3c6SIoana Ciornei int irq_status, irq_enabled; 2231d1ae3c6SIoana Ciornei 2241d1ae3c6SIoana Ciornei irq_status = phy_read(phydev, MII_DP83869_ISR); 2251d1ae3c6SIoana Ciornei if (irq_status < 0) { 2261d1ae3c6SIoana Ciornei phy_error(phydev); 2271d1ae3c6SIoana Ciornei return IRQ_NONE; 2281d1ae3c6SIoana Ciornei } 2291d1ae3c6SIoana Ciornei 2301d1ae3c6SIoana Ciornei irq_enabled = phy_read(phydev, MII_DP83869_MICR); 2311d1ae3c6SIoana Ciornei if (irq_enabled < 0) { 2321d1ae3c6SIoana Ciornei phy_error(phydev); 2331d1ae3c6SIoana Ciornei return IRQ_NONE; 2341d1ae3c6SIoana Ciornei } 2351d1ae3c6SIoana Ciornei 2361d1ae3c6SIoana Ciornei if (!(irq_status & irq_enabled)) 2371d1ae3c6SIoana Ciornei return IRQ_NONE; 2381d1ae3c6SIoana Ciornei 2391d1ae3c6SIoana Ciornei phy_trigger_machine(phydev); 2401d1ae3c6SIoana Ciornei 2411d1ae3c6SIoana Ciornei return IRQ_HANDLED; 2421d1ae3c6SIoana Ciornei } 2431d1ae3c6SIoana Ciornei 24449fc2301SDan Murphy static int dp83869_set_wol(struct phy_device *phydev, 24549fc2301SDan Murphy struct ethtool_wolinfo *wol) 24649fc2301SDan Murphy { 24749fc2301SDan Murphy struct net_device *ndev = phydev->attached_dev; 24849fc2301SDan Murphy int val_rxcfg, val_micr; 24949fc2301SDan Murphy u8 *mac; 25049fc2301SDan Murphy int ret; 25149fc2301SDan Murphy 25249fc2301SDan Murphy val_rxcfg = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_RXFCFG); 25349fc2301SDan Murphy if (val_rxcfg < 0) 25449fc2301SDan Murphy return val_rxcfg; 25549fc2301SDan Murphy 25649fc2301SDan Murphy val_micr = phy_read(phydev, MII_DP83869_MICR); 25749fc2301SDan Murphy if (val_micr < 0) 25849fc2301SDan Murphy return val_micr; 25949fc2301SDan Murphy 26049fc2301SDan Murphy if (wol->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_UCAST | 26149fc2301SDan Murphy WAKE_BCAST)) { 26249fc2301SDan Murphy val_rxcfg |= DP83869_WOL_ENH_MAC; 26349fc2301SDan Murphy val_micr |= MII_DP83869_MICR_WOL_INT_EN; 26449fc2301SDan Murphy 26549fc2301SDan Murphy if (wol->wolopts & WAKE_MAGIC || 26649fc2301SDan Murphy wol->wolopts & WAKE_MAGICSECURE) { 26749fc2301SDan Murphy mac = (u8 *)ndev->dev_addr; 26849fc2301SDan Murphy 26949fc2301SDan Murphy if (!is_valid_ether_addr(mac)) 27049fc2301SDan Murphy return -EINVAL; 27149fc2301SDan Murphy 27249fc2301SDan Murphy ret = phy_write_mmd(phydev, DP83869_DEVADDR, 27349fc2301SDan Murphy DP83869_RXFPMD1, 27449fc2301SDan Murphy mac[1] << 8 | mac[0]); 27549fc2301SDan Murphy if (ret) 27649fc2301SDan Murphy return ret; 27749fc2301SDan Murphy 27849fc2301SDan Murphy ret = phy_write_mmd(phydev, DP83869_DEVADDR, 27949fc2301SDan Murphy DP83869_RXFPMD2, 28049fc2301SDan Murphy mac[3] << 8 | mac[2]); 28149fc2301SDan Murphy if (ret) 28249fc2301SDan Murphy return ret; 28349fc2301SDan Murphy 28449fc2301SDan Murphy ret = phy_write_mmd(phydev, DP83869_DEVADDR, 28549fc2301SDan Murphy DP83869_RXFPMD3, 28649fc2301SDan Murphy mac[5] << 8 | mac[4]); 28749fc2301SDan Murphy if (ret) 28849fc2301SDan Murphy return ret; 28949fc2301SDan Murphy 29049fc2301SDan Murphy val_rxcfg |= DP83869_WOL_MAGIC_EN; 29149fc2301SDan Murphy } else { 29249fc2301SDan Murphy val_rxcfg &= ~DP83869_WOL_MAGIC_EN; 29349fc2301SDan Murphy } 29449fc2301SDan Murphy 29549fc2301SDan Murphy if (wol->wolopts & WAKE_MAGICSECURE) { 29649fc2301SDan Murphy ret = phy_write_mmd(phydev, DP83869_DEVADDR, 29749fc2301SDan Murphy DP83869_RXFSOP1, 29849fc2301SDan Murphy (wol->sopass[1] << 8) | wol->sopass[0]); 29949fc2301SDan Murphy if (ret) 30049fc2301SDan Murphy return ret; 30149fc2301SDan Murphy 30249fc2301SDan Murphy ret = phy_write_mmd(phydev, DP83869_DEVADDR, 30349fc2301SDan Murphy DP83869_RXFSOP2, 30449fc2301SDan Murphy (wol->sopass[3] << 8) | wol->sopass[2]); 30549fc2301SDan Murphy if (ret) 30649fc2301SDan Murphy return ret; 30749fc2301SDan Murphy ret = phy_write_mmd(phydev, DP83869_DEVADDR, 30849fc2301SDan Murphy DP83869_RXFSOP3, 30949fc2301SDan Murphy (wol->sopass[5] << 8) | wol->sopass[4]); 31049fc2301SDan Murphy if (ret) 31149fc2301SDan Murphy return ret; 31249fc2301SDan Murphy 31349fc2301SDan Murphy val_rxcfg |= DP83869_WOL_SEC_EN; 31449fc2301SDan Murphy } else { 31549fc2301SDan Murphy val_rxcfg &= ~DP83869_WOL_SEC_EN; 31649fc2301SDan Murphy } 31749fc2301SDan Murphy 31849fc2301SDan Murphy if (wol->wolopts & WAKE_UCAST) 31949fc2301SDan Murphy val_rxcfg |= DP83869_WOL_UCAST_EN; 32049fc2301SDan Murphy else 32149fc2301SDan Murphy val_rxcfg &= ~DP83869_WOL_UCAST_EN; 32249fc2301SDan Murphy 32349fc2301SDan Murphy if (wol->wolopts & WAKE_BCAST) 32449fc2301SDan Murphy val_rxcfg |= DP83869_WOL_BCAST_EN; 32549fc2301SDan Murphy else 32649fc2301SDan Murphy val_rxcfg &= ~DP83869_WOL_BCAST_EN; 32749fc2301SDan Murphy } else { 32849fc2301SDan Murphy val_rxcfg &= ~DP83869_WOL_ENH_MAC; 32949fc2301SDan Murphy val_micr &= ~MII_DP83869_MICR_WOL_INT_EN; 33049fc2301SDan Murphy } 33149fc2301SDan Murphy 33249fc2301SDan Murphy ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RXFCFG, val_rxcfg); 33349fc2301SDan Murphy if (ret) 33449fc2301SDan Murphy return ret; 33549fc2301SDan Murphy 33649fc2301SDan Murphy return phy_write(phydev, MII_DP83869_MICR, val_micr); 33749fc2301SDan Murphy } 33849fc2301SDan Murphy 33949fc2301SDan Murphy static void dp83869_get_wol(struct phy_device *phydev, 34049fc2301SDan Murphy struct ethtool_wolinfo *wol) 34149fc2301SDan Murphy { 342e275d49aSColin Ian King int value, sopass_val; 34349fc2301SDan Murphy 34449fc2301SDan Murphy wol->supported = (WAKE_UCAST | WAKE_BCAST | WAKE_MAGIC | 34549fc2301SDan Murphy WAKE_MAGICSECURE); 34649fc2301SDan Murphy wol->wolopts = 0; 34749fc2301SDan Murphy 34849fc2301SDan Murphy value = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_RXFCFG); 34949fc2301SDan Murphy if (value < 0) { 35049fc2301SDan Murphy phydev_err(phydev, "Failed to read RX CFG\n"); 35149fc2301SDan Murphy return; 35249fc2301SDan Murphy } 35349fc2301SDan Murphy 35449fc2301SDan Murphy if (value & DP83869_WOL_UCAST_EN) 35549fc2301SDan Murphy wol->wolopts |= WAKE_UCAST; 35649fc2301SDan Murphy 35749fc2301SDan Murphy if (value & DP83869_WOL_BCAST_EN) 35849fc2301SDan Murphy wol->wolopts |= WAKE_BCAST; 35949fc2301SDan Murphy 36049fc2301SDan Murphy if (value & DP83869_WOL_MAGIC_EN) 36149fc2301SDan Murphy wol->wolopts |= WAKE_MAGIC; 36249fc2301SDan Murphy 36349fc2301SDan Murphy if (value & DP83869_WOL_SEC_EN) { 36449fc2301SDan Murphy sopass_val = phy_read_mmd(phydev, DP83869_DEVADDR, 36549fc2301SDan Murphy DP83869_RXFSOP1); 36649fc2301SDan Murphy if (sopass_val < 0) { 36749fc2301SDan Murphy phydev_err(phydev, "Failed to read RX SOP 1\n"); 36849fc2301SDan Murphy return; 36949fc2301SDan Murphy } 37049fc2301SDan Murphy 37149fc2301SDan Murphy wol->sopass[0] = (sopass_val & 0xff); 37249fc2301SDan Murphy wol->sopass[1] = (sopass_val >> 8); 37349fc2301SDan Murphy 37449fc2301SDan Murphy sopass_val = phy_read_mmd(phydev, DP83869_DEVADDR, 37549fc2301SDan Murphy DP83869_RXFSOP2); 37649fc2301SDan Murphy if (sopass_val < 0) { 37749fc2301SDan Murphy phydev_err(phydev, "Failed to read RX SOP 2\n"); 37849fc2301SDan Murphy return; 37949fc2301SDan Murphy } 38049fc2301SDan Murphy 38149fc2301SDan Murphy wol->sopass[2] = (sopass_val & 0xff); 38249fc2301SDan Murphy wol->sopass[3] = (sopass_val >> 8); 38349fc2301SDan Murphy 38449fc2301SDan Murphy sopass_val = phy_read_mmd(phydev, DP83869_DEVADDR, 38549fc2301SDan Murphy DP83869_RXFSOP3); 38649fc2301SDan Murphy if (sopass_val < 0) { 38749fc2301SDan Murphy phydev_err(phydev, "Failed to read RX SOP 3\n"); 38849fc2301SDan Murphy return; 38949fc2301SDan Murphy } 39049fc2301SDan Murphy 39149fc2301SDan Murphy wol->sopass[4] = (sopass_val & 0xff); 39249fc2301SDan Murphy wol->sopass[5] = (sopass_val >> 8); 39349fc2301SDan Murphy 39449fc2301SDan Murphy wol->wolopts |= WAKE_MAGICSECURE; 39549fc2301SDan Murphy } 39649fc2301SDan Murphy 39749fc2301SDan Murphy if (!(value & DP83869_WOL_ENH_MAC)) 39849fc2301SDan Murphy wol->wolopts = 0; 39949fc2301SDan Murphy } 40049fc2301SDan Murphy 401811ac400SDan Murphy static int dp83869_get_downshift(struct phy_device *phydev, u8 *data) 402811ac400SDan Murphy { 403811ac400SDan Murphy int val, cnt, enable, count; 404811ac400SDan Murphy 405811ac400SDan Murphy val = phy_read(phydev, DP83869_CFG2); 406811ac400SDan Murphy if (val < 0) 407811ac400SDan Murphy return val; 408811ac400SDan Murphy 409811ac400SDan Murphy enable = FIELD_GET(DP83869_DOWNSHIFT_EN, val); 410811ac400SDan Murphy cnt = FIELD_GET(DP83869_DOWNSHIFT_ATTEMPT_MASK, val); 411811ac400SDan Murphy 412811ac400SDan Murphy switch (cnt) { 413811ac400SDan Murphy case DP83869_DOWNSHIFT_1_COUNT_VAL: 414811ac400SDan Murphy count = DP83869_DOWNSHIFT_1_COUNT; 415811ac400SDan Murphy break; 416811ac400SDan Murphy case DP83869_DOWNSHIFT_2_COUNT_VAL: 417811ac400SDan Murphy count = DP83869_DOWNSHIFT_2_COUNT; 418811ac400SDan Murphy break; 419811ac400SDan Murphy case DP83869_DOWNSHIFT_4_COUNT_VAL: 420811ac400SDan Murphy count = DP83869_DOWNSHIFT_4_COUNT; 421811ac400SDan Murphy break; 422811ac400SDan Murphy case DP83869_DOWNSHIFT_8_COUNT_VAL: 423811ac400SDan Murphy count = DP83869_DOWNSHIFT_8_COUNT; 424811ac400SDan Murphy break; 425811ac400SDan Murphy default: 426811ac400SDan Murphy return -EINVAL; 427811ac400SDan Murphy } 428811ac400SDan Murphy 429811ac400SDan Murphy *data = enable ? count : DOWNSHIFT_DEV_DISABLE; 430811ac400SDan Murphy 431811ac400SDan Murphy return 0; 432811ac400SDan Murphy } 433811ac400SDan Murphy 434811ac400SDan Murphy static int dp83869_set_downshift(struct phy_device *phydev, u8 cnt) 435811ac400SDan Murphy { 436811ac400SDan Murphy int val, count; 437811ac400SDan Murphy 438811ac400SDan Murphy if (cnt > DP83869_DOWNSHIFT_8_COUNT) 439811ac400SDan Murphy return -EINVAL; 440811ac400SDan Murphy 441811ac400SDan Murphy if (!cnt) 442811ac400SDan Murphy return phy_clear_bits(phydev, DP83869_CFG2, 443811ac400SDan Murphy DP83869_DOWNSHIFT_EN); 444811ac400SDan Murphy 445811ac400SDan Murphy switch (cnt) { 446811ac400SDan Murphy case DP83869_DOWNSHIFT_1_COUNT: 447811ac400SDan Murphy count = DP83869_DOWNSHIFT_1_COUNT_VAL; 448811ac400SDan Murphy break; 449811ac400SDan Murphy case DP83869_DOWNSHIFT_2_COUNT: 450811ac400SDan Murphy count = DP83869_DOWNSHIFT_2_COUNT_VAL; 451811ac400SDan Murphy break; 452811ac400SDan Murphy case DP83869_DOWNSHIFT_4_COUNT: 453811ac400SDan Murphy count = DP83869_DOWNSHIFT_4_COUNT_VAL; 454811ac400SDan Murphy break; 455811ac400SDan Murphy case DP83869_DOWNSHIFT_8_COUNT: 456811ac400SDan Murphy count = DP83869_DOWNSHIFT_8_COUNT_VAL; 457811ac400SDan Murphy break; 458811ac400SDan Murphy default: 459811ac400SDan Murphy phydev_err(phydev, 460811ac400SDan Murphy "Downshift count must be 1, 2, 4 or 8\n"); 461811ac400SDan Murphy return -EINVAL; 462811ac400SDan Murphy } 463811ac400SDan Murphy 464811ac400SDan Murphy val = DP83869_DOWNSHIFT_EN; 465811ac400SDan Murphy val |= FIELD_PREP(DP83869_DOWNSHIFT_ATTEMPT_MASK, count); 466811ac400SDan Murphy 467811ac400SDan Murphy return phy_modify(phydev, DP83869_CFG2, 468811ac400SDan Murphy DP83869_DOWNSHIFT_EN | DP83869_DOWNSHIFT_ATTEMPT_MASK, 469811ac400SDan Murphy val); 470811ac400SDan Murphy } 471811ac400SDan Murphy 472811ac400SDan Murphy static int dp83869_get_tunable(struct phy_device *phydev, 473811ac400SDan Murphy struct ethtool_tunable *tuna, void *data) 474811ac400SDan Murphy { 475811ac400SDan Murphy switch (tuna->id) { 476811ac400SDan Murphy case ETHTOOL_PHY_DOWNSHIFT: 477811ac400SDan Murphy return dp83869_get_downshift(phydev, data); 478811ac400SDan Murphy default: 479811ac400SDan Murphy return -EOPNOTSUPP; 480811ac400SDan Murphy } 481811ac400SDan Murphy } 482811ac400SDan Murphy 483811ac400SDan Murphy static int dp83869_set_tunable(struct phy_device *phydev, 484811ac400SDan Murphy struct ethtool_tunable *tuna, const void *data) 485811ac400SDan Murphy { 486811ac400SDan Murphy switch (tuna->id) { 487811ac400SDan Murphy case ETHTOOL_PHY_DOWNSHIFT: 488811ac400SDan Murphy return dp83869_set_downshift(phydev, *(const u8 *)data); 489811ac400SDan Murphy default: 490811ac400SDan Murphy return -EOPNOTSUPP; 491811ac400SDan Murphy } 492811ac400SDan Murphy } 493811ac400SDan Murphy 49401db923eSDan Murphy static int dp83869_config_port_mirroring(struct phy_device *phydev) 49501db923eSDan Murphy { 49601db923eSDan Murphy struct dp83869_private *dp83869 = phydev->priv; 49701db923eSDan Murphy 49801db923eSDan Murphy if (dp83869->port_mirroring == DP83869_PORT_MIRRORING_EN) 499786c4a53SDan Murphy return phy_set_bits_mmd(phydev, DP83869_DEVADDR, 500786c4a53SDan Murphy DP83869_GEN_CFG3, 50101db923eSDan Murphy DP83869_CFG3_PORT_MIRROR_EN); 50201db923eSDan Murphy else 503786c4a53SDan Murphy return phy_clear_bits_mmd(phydev, DP83869_DEVADDR, 504786c4a53SDan Murphy DP83869_GEN_CFG3, 50501db923eSDan Murphy DP83869_CFG3_PORT_MIRROR_EN); 50601db923eSDan Murphy } 50701db923eSDan Murphy 5080eaf8ccfSDan Murphy static int dp83869_set_strapped_mode(struct phy_device *phydev) 5090eaf8ccfSDan Murphy { 5100eaf8ccfSDan Murphy struct dp83869_private *dp83869 = phydev->priv; 5110eaf8ccfSDan Murphy int val; 5120eaf8ccfSDan Murphy 5130eaf8ccfSDan Murphy val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_STRAP_STS1); 5140eaf8ccfSDan Murphy if (val < 0) 5150eaf8ccfSDan Murphy return val; 5160eaf8ccfSDan Murphy 5170eaf8ccfSDan Murphy dp83869->mode = val & DP83869_STRAP_OP_MODE_MASK; 5180eaf8ccfSDan Murphy 5190eaf8ccfSDan Murphy return 0; 5200eaf8ccfSDan Murphy } 5210eaf8ccfSDan Murphy 522e9293c98SDan Murphy #if IS_ENABLED(CONFIG_OF_MDIO) 523736b25afSDan Murphy static const int dp83869_internal_delay[] = {250, 500, 750, 1000, 1250, 1500, 524736b25afSDan Murphy 1750, 2000, 2250, 2500, 2750, 3000, 525736b25afSDan Murphy 3250, 3500, 3750, 4000}; 526736b25afSDan Murphy 52701db923eSDan Murphy static int dp83869_of_init(struct phy_device *phydev) 52801db923eSDan Murphy { 52901db923eSDan Murphy struct dp83869_private *dp83869 = phydev->priv; 53001db923eSDan Murphy struct device *dev = &phydev->mdio.dev; 53101db923eSDan Murphy struct device_node *of_node = dev->of_node; 532736b25afSDan Murphy int delay_size = ARRAY_SIZE(dp83869_internal_delay); 53301db923eSDan Murphy int ret; 53401db923eSDan Murphy 53501db923eSDan Murphy if (!of_node) 53601db923eSDan Murphy return -ENODEV; 53701db923eSDan Murphy 53801db923eSDan Murphy dp83869->io_impedance = -EINVAL; 53901db923eSDan Murphy 54001db923eSDan Murphy /* Optional configuration */ 54101db923eSDan Murphy ret = of_property_read_u32(of_node, "ti,clk-output-sel", 54201db923eSDan Murphy &dp83869->clk_output_sel); 54301db923eSDan Murphy if (ret || dp83869->clk_output_sel > DP83869_CLK_O_SEL_REF_CLK) 54401db923eSDan Murphy dp83869->clk_output_sel = DP83869_CLK_O_SEL_REF_CLK; 54501db923eSDan Murphy 54601db923eSDan Murphy ret = of_property_read_u32(of_node, "ti,op-mode", &dp83869->mode); 54701db923eSDan Murphy if (ret == 0) { 54801db923eSDan Murphy if (dp83869->mode < DP83869_RGMII_COPPER_ETHERNET || 54901db923eSDan Murphy dp83869->mode > DP83869_SGMII_COPPER_ETHERNET) 55001db923eSDan Murphy return -EINVAL; 5510eaf8ccfSDan Murphy } else { 5520eaf8ccfSDan Murphy ret = dp83869_set_strapped_mode(phydev); 5530eaf8ccfSDan Murphy if (ret) 5540eaf8ccfSDan Murphy return ret; 55501db923eSDan Murphy } 55601db923eSDan Murphy 55701db923eSDan Murphy if (of_property_read_bool(of_node, "ti,max-output-impedance")) 55801db923eSDan Murphy dp83869->io_impedance = DP83869_IO_MUX_CFG_IO_IMPEDANCE_MAX; 55901db923eSDan Murphy else if (of_property_read_bool(of_node, "ti,min-output-impedance")) 56001db923eSDan Murphy dp83869->io_impedance = DP83869_IO_MUX_CFG_IO_IMPEDANCE_MIN; 56101db923eSDan Murphy 562c4566aecSDan Murphy if (of_property_read_bool(of_node, "enet-phy-lane-swap")) { 563c4566aecSDan Murphy dp83869->port_mirroring = DP83869_PORT_MIRRORING_EN; 564c4566aecSDan Murphy } else { 565c4566aecSDan Murphy /* If the lane swap is not in the DT then check the straps */ 566c4566aecSDan Murphy ret = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_STRAP_STS1); 567c4566aecSDan Murphy if (ret < 0) 568c4566aecSDan Murphy return ret; 5694e2905adSDan Murphy 570c4566aecSDan Murphy if (ret & DP83869_STRAP_MIRROR_ENABLED) 57101db923eSDan Murphy dp83869->port_mirroring = DP83869_PORT_MIRRORING_EN; 57201db923eSDan Murphy else 57301db923eSDan Murphy dp83869->port_mirroring = DP83869_PORT_MIRRORING_DIS; 5744e2905adSDan Murphy 5754e2905adSDan Murphy ret = 0; 576c4566aecSDan Murphy } 57701db923eSDan Murphy 57801db923eSDan Murphy if (of_property_read_u32(of_node, "rx-fifo-depth", 57901db923eSDan Murphy &dp83869->rx_fifo_depth)) 58001db923eSDan Murphy dp83869->rx_fifo_depth = DP83869_PHYCR_FIFO_DEPTH_4_B_NIB; 58101db923eSDan Murphy 58201db923eSDan Murphy if (of_property_read_u32(of_node, "tx-fifo-depth", 58301db923eSDan Murphy &dp83869->tx_fifo_depth)) 58401db923eSDan Murphy dp83869->tx_fifo_depth = DP83869_PHYCR_FIFO_DEPTH_4_B_NIB; 58501db923eSDan Murphy 586736b25afSDan Murphy dp83869->rx_int_delay = phy_get_internal_delay(phydev, dev, 587736b25afSDan Murphy &dp83869_internal_delay[0], 588736b25afSDan Murphy delay_size, true); 589736b25afSDan Murphy if (dp83869->rx_int_delay < 0) 590736b25afSDan Murphy dp83869->rx_int_delay = 591736b25afSDan Murphy dp83869_internal_delay[DP83869_CLK_DELAY_DEF]; 592736b25afSDan Murphy 593736b25afSDan Murphy dp83869->tx_int_delay = phy_get_internal_delay(phydev, dev, 594736b25afSDan Murphy &dp83869_internal_delay[0], 595736b25afSDan Murphy delay_size, false); 596736b25afSDan Murphy if (dp83869->tx_int_delay < 0) 597736b25afSDan Murphy dp83869->tx_int_delay = 598736b25afSDan Murphy dp83869_internal_delay[DP83869_CLK_DELAY_DEF]; 599736b25afSDan Murphy 600786c4a53SDan Murphy return ret; 60101db923eSDan Murphy } 60201db923eSDan Murphy #else 60301db923eSDan Murphy static int dp83869_of_init(struct phy_device *phydev) 60401db923eSDan Murphy { 6050eaf8ccfSDan Murphy return dp83869_set_strapped_mode(phydev); 60601db923eSDan Murphy } 60701db923eSDan Murphy #endif /* CONFIG_OF_MDIO */ 60801db923eSDan Murphy 60901db923eSDan Murphy static int dp83869_configure_rgmii(struct phy_device *phydev, 61001db923eSDan Murphy struct dp83869_private *dp83869) 61101db923eSDan Murphy { 612786c4a53SDan Murphy int ret = 0, val; 61301db923eSDan Murphy 61401db923eSDan Murphy if (phy_interface_is_rgmii(phydev)) { 61501db923eSDan Murphy val = phy_read(phydev, MII_DP83869_PHYCTRL); 61601db923eSDan Murphy if (val < 0) 61701db923eSDan Murphy return val; 61801db923eSDan Murphy 61901db923eSDan Murphy val &= ~DP83869_PHYCR_FIFO_DEPTH_MASK; 62001db923eSDan Murphy val |= (dp83869->tx_fifo_depth << DP83869_TX_FIFO_SHIFT); 62101db923eSDan Murphy val |= (dp83869->rx_fifo_depth << DP83869_RX_FIFO_SHIFT); 62201db923eSDan Murphy 62301db923eSDan Murphy ret = phy_write(phydev, MII_DP83869_PHYCTRL, val); 62401db923eSDan Murphy if (ret) 62501db923eSDan Murphy return ret; 62601db923eSDan Murphy } 62701db923eSDan Murphy 62801db923eSDan Murphy if (dp83869->io_impedance >= 0) 629786c4a53SDan Murphy ret = phy_modify_mmd(phydev, DP83869_DEVADDR, 63001db923eSDan Murphy DP83869_IO_MUX_CFG, 63101db923eSDan Murphy DP83869_IO_MUX_CFG_IO_IMPEDANCE_CTRL, 63201db923eSDan Murphy dp83869->io_impedance & 63301db923eSDan Murphy DP83869_IO_MUX_CFG_IO_IMPEDANCE_CTRL); 63401db923eSDan Murphy 635786c4a53SDan Murphy return ret; 63601db923eSDan Murphy } 63701db923eSDan Murphy 638a29de52bSDan Murphy static int dp83869_configure_fiber(struct phy_device *phydev, 639a29de52bSDan Murphy struct dp83869_private *dp83869) 640a29de52bSDan Murphy { 641a29de52bSDan Murphy int bmcr; 642a29de52bSDan Murphy int ret; 643a29de52bSDan Murphy 644a29de52bSDan Murphy /* Only allow advertising what this PHY supports */ 645a29de52bSDan Murphy linkmode_and(phydev->advertising, phydev->advertising, 646a29de52bSDan Murphy phydev->supported); 647a29de52bSDan Murphy 648a29de52bSDan Murphy linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported); 649a29de52bSDan Murphy linkmode_set_bit(ADVERTISED_FIBRE, phydev->advertising); 650a29de52bSDan Murphy 651a29de52bSDan Murphy if (dp83869->mode == DP83869_RGMII_1000_BASE) { 652a29de52bSDan Murphy linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, 653a29de52bSDan Murphy phydev->supported); 654a29de52bSDan Murphy } else { 655a29de52bSDan Murphy linkmode_set_bit(ETHTOOL_LINK_MODE_100baseFX_Full_BIT, 656a29de52bSDan Murphy phydev->supported); 657a29de52bSDan Murphy linkmode_set_bit(ETHTOOL_LINK_MODE_100baseFX_Half_BIT, 658a29de52bSDan Murphy phydev->supported); 659a29de52bSDan Murphy 660a29de52bSDan Murphy /* Auto neg is not supported in 100base FX mode */ 661a29de52bSDan Murphy bmcr = phy_read(phydev, MII_BMCR); 662a29de52bSDan Murphy if (bmcr < 0) 663a29de52bSDan Murphy return bmcr; 664a29de52bSDan Murphy 665a29de52bSDan Murphy phydev->autoneg = AUTONEG_DISABLE; 666a29de52bSDan Murphy linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported); 667a29de52bSDan Murphy linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->advertising); 668a29de52bSDan Murphy 669a29de52bSDan Murphy if (bmcr & BMCR_ANENABLE) { 670a29de52bSDan Murphy ret = phy_modify(phydev, MII_BMCR, BMCR_ANENABLE, 0); 671a29de52bSDan Murphy if (ret < 0) 672a29de52bSDan Murphy return ret; 673a29de52bSDan Murphy } 674a29de52bSDan Murphy } 675a29de52bSDan Murphy 676a29de52bSDan Murphy /* Update advertising from supported */ 677a29de52bSDan Murphy linkmode_or(phydev->advertising, phydev->advertising, 678a29de52bSDan Murphy phydev->supported); 679a29de52bSDan Murphy 680a29de52bSDan Murphy return 0; 681a29de52bSDan Murphy } 682a29de52bSDan Murphy 68301db923eSDan Murphy static int dp83869_configure_mode(struct phy_device *phydev, 68401db923eSDan Murphy struct dp83869_private *dp83869) 68501db923eSDan Murphy { 68601db923eSDan Murphy int phy_ctrl_val; 68701db923eSDan Murphy int ret; 68801db923eSDan Murphy 68901db923eSDan Murphy if (dp83869->mode < DP83869_RGMII_COPPER_ETHERNET || 69001db923eSDan Murphy dp83869->mode > DP83869_SGMII_COPPER_ETHERNET) 69101db923eSDan Murphy return -EINVAL; 69201db923eSDan Murphy 69301db923eSDan Murphy /* Below init sequence for each operational mode is defined in 69401db923eSDan Murphy * section 9.4.8 of the datasheet. 69501db923eSDan Murphy */ 69601db923eSDan Murphy ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_OP_MODE, 69701db923eSDan Murphy dp83869->mode); 69801db923eSDan Murphy if (ret) 69901db923eSDan Murphy return ret; 70001db923eSDan Murphy 70101db923eSDan Murphy ret = phy_write(phydev, MII_BMCR, MII_DP83869_BMCR_DEFAULT); 70201db923eSDan Murphy if (ret) 70301db923eSDan Murphy return ret; 70401db923eSDan Murphy 70501db923eSDan Murphy phy_ctrl_val = (dp83869->rx_fifo_depth << DP83869_RX_FIFO_SHIFT | 70601db923eSDan Murphy dp83869->tx_fifo_depth << DP83869_TX_FIFO_SHIFT | 70701db923eSDan Murphy DP83869_PHY_CTRL_DEFAULT); 70801db923eSDan Murphy 70901db923eSDan Murphy switch (dp83869->mode) { 71001db923eSDan Murphy case DP83869_RGMII_COPPER_ETHERNET: 71101db923eSDan Murphy ret = phy_write(phydev, MII_DP83869_PHYCTRL, 71201db923eSDan Murphy phy_ctrl_val); 71301db923eSDan Murphy if (ret) 71401db923eSDan Murphy return ret; 71501db923eSDan Murphy 71601db923eSDan Murphy ret = phy_write(phydev, MII_CTRL1000, DP83869_CFG1_DEFAULT); 71701db923eSDan Murphy if (ret) 71801db923eSDan Murphy return ret; 71901db923eSDan Murphy 72001db923eSDan Murphy ret = dp83869_configure_rgmii(phydev, dp83869); 72101db923eSDan Murphy if (ret) 72201db923eSDan Murphy return ret; 72301db923eSDan Murphy break; 72401db923eSDan Murphy case DP83869_RGMII_SGMII_BRIDGE: 725786c4a53SDan Murphy ret = phy_modify_mmd(phydev, DP83869_DEVADDR, DP83869_OP_MODE, 72601db923eSDan Murphy DP83869_SGMII_RGMII_BRIDGE, 72701db923eSDan Murphy DP83869_SGMII_RGMII_BRIDGE); 728786c4a53SDan Murphy if (ret) 729786c4a53SDan Murphy return ret; 73001db923eSDan Murphy 73101db923eSDan Murphy ret = phy_write_mmd(phydev, DP83869_DEVADDR, 73201db923eSDan Murphy DP83869_FX_CTRL, DP83869_FX_CTRL_DEFAULT); 73301db923eSDan Murphy if (ret) 73401db923eSDan Murphy return ret; 73501db923eSDan Murphy 73601db923eSDan Murphy break; 73701db923eSDan Murphy case DP83869_1000M_MEDIA_CONVERT: 73801db923eSDan Murphy ret = phy_write(phydev, MII_DP83869_PHYCTRL, 73901db923eSDan Murphy phy_ctrl_val); 74001db923eSDan Murphy if (ret) 74101db923eSDan Murphy return ret; 74201db923eSDan Murphy 74301db923eSDan Murphy ret = phy_write_mmd(phydev, DP83869_DEVADDR, 74401db923eSDan Murphy DP83869_FX_CTRL, DP83869_FX_CTRL_DEFAULT); 74501db923eSDan Murphy if (ret) 74601db923eSDan Murphy return ret; 74701db923eSDan Murphy break; 74801db923eSDan Murphy case DP83869_100M_MEDIA_CONVERT: 74901db923eSDan Murphy ret = phy_write(phydev, MII_DP83869_PHYCTRL, 75001db923eSDan Murphy phy_ctrl_val); 75101db923eSDan Murphy if (ret) 75201db923eSDan Murphy return ret; 75301db923eSDan Murphy break; 75401db923eSDan Murphy case DP83869_SGMII_COPPER_ETHERNET: 75501db923eSDan Murphy ret = phy_write(phydev, MII_DP83869_PHYCTRL, 75601db923eSDan Murphy phy_ctrl_val); 75701db923eSDan Murphy if (ret) 75801db923eSDan Murphy return ret; 75901db923eSDan Murphy 76001db923eSDan Murphy ret = phy_write(phydev, MII_CTRL1000, DP83869_CFG1_DEFAULT); 76101db923eSDan Murphy if (ret) 76201db923eSDan Murphy return ret; 76301db923eSDan Murphy 76401db923eSDan Murphy ret = phy_write_mmd(phydev, DP83869_DEVADDR, 76501db923eSDan Murphy DP83869_FX_CTRL, DP83869_FX_CTRL_DEFAULT); 76601db923eSDan Murphy if (ret) 76701db923eSDan Murphy return ret; 76801db923eSDan Murphy 76901db923eSDan Murphy break; 77001db923eSDan Murphy case DP83869_RGMII_1000_BASE: 77101db923eSDan Murphy case DP83869_RGMII_100_BASE: 772a29de52bSDan Murphy ret = dp83869_configure_fiber(phydev, dp83869); 77301db923eSDan Murphy break; 77401db923eSDan Murphy default: 77501db923eSDan Murphy return -EINVAL; 7763a5f494dSzhengbin } 77701db923eSDan Murphy 778786c4a53SDan Murphy return ret; 77901db923eSDan Murphy } 78001db923eSDan Murphy 78101db923eSDan Murphy static int dp83869_config_init(struct phy_device *phydev) 78201db923eSDan Murphy { 78301db923eSDan Murphy struct dp83869_private *dp83869 = phydev->priv; 78401db923eSDan Murphy int ret, val; 78501db923eSDan Murphy 786811ac400SDan Murphy /* Force speed optimization for the PHY even if it strapped */ 787811ac400SDan Murphy ret = phy_modify(phydev, DP83869_CFG2, DP83869_DOWNSHIFT_EN, 788811ac400SDan Murphy DP83869_DOWNSHIFT_EN); 789811ac400SDan Murphy if (ret) 790811ac400SDan Murphy return ret; 791811ac400SDan Murphy 79201db923eSDan Murphy ret = dp83869_configure_mode(phydev, dp83869); 79301db923eSDan Murphy if (ret) 79401db923eSDan Murphy return ret; 79501db923eSDan Murphy 79601db923eSDan Murphy /* Enable Interrupt output INT_OE in CFG4 register */ 79701db923eSDan Murphy if (phy_interrupt_is_valid(phydev)) { 79801db923eSDan Murphy val = phy_read(phydev, DP83869_CFG4); 79901db923eSDan Murphy val |= DP83869_INT_OE; 80001db923eSDan Murphy phy_write(phydev, DP83869_CFG4, val); 80101db923eSDan Murphy } 80201db923eSDan Murphy 80301db923eSDan Murphy if (dp83869->port_mirroring != DP83869_PORT_MIRRORING_KEEP) 80401db923eSDan Murphy dp83869_config_port_mirroring(phydev); 80501db923eSDan Murphy 80601db923eSDan Murphy /* Clock output selection if muxing property is set */ 80701db923eSDan Murphy if (dp83869->clk_output_sel != DP83869_CLK_O_SEL_REF_CLK) 808786c4a53SDan Murphy ret = phy_modify_mmd(phydev, 809786c4a53SDan Murphy DP83869_DEVADDR, DP83869_IO_MUX_CFG, 81001db923eSDan Murphy DP83869_IO_MUX_CFG_CLK_O_SEL_MASK, 81101db923eSDan Murphy dp83869->clk_output_sel << 81201db923eSDan Murphy DP83869_IO_MUX_CFG_CLK_O_SEL_SHIFT); 81301db923eSDan Murphy 814736b25afSDan Murphy if (phy_interface_is_rgmii(phydev)) { 815736b25afSDan Murphy ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIIDCTL, 816736b25afSDan Murphy dp83869->rx_int_delay | 817736b25afSDan Murphy dp83869->tx_int_delay << DP83869_RGMII_CLK_DELAY_SHIFT); 818736b25afSDan Murphy if (ret) 819736b25afSDan Murphy return ret; 820736b25afSDan Murphy 821736b25afSDan Murphy val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIICTL); 822736b25afSDan Murphy val |= (DP83869_RGMII_TX_CLK_DELAY_EN | 823736b25afSDan Murphy DP83869_RGMII_RX_CLK_DELAY_EN); 824736b25afSDan Murphy 8252e1ec861SDaniel Gorsulowski if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) 8262e1ec861SDaniel Gorsulowski val &= ~(DP83869_RGMII_TX_CLK_DELAY_EN | 8272e1ec861SDaniel Gorsulowski DP83869_RGMII_RX_CLK_DELAY_EN); 8282e1ec861SDaniel Gorsulowski 829736b25afSDan Murphy if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) 8302e1ec861SDaniel Gorsulowski val &= ~DP83869_RGMII_TX_CLK_DELAY_EN; 831736b25afSDan Murphy 832736b25afSDan Murphy if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) 8332e1ec861SDaniel Gorsulowski val &= ~DP83869_RGMII_RX_CLK_DELAY_EN; 834736b25afSDan Murphy 835736b25afSDan Murphy ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIICTL, 836736b25afSDan Murphy val); 837736b25afSDan Murphy } 838736b25afSDan Murphy 839786c4a53SDan Murphy return ret; 84001db923eSDan Murphy } 84101db923eSDan Murphy 84201db923eSDan Murphy static int dp83869_probe(struct phy_device *phydev) 84301db923eSDan Murphy { 84401db923eSDan Murphy struct dp83869_private *dp83869; 84501db923eSDan Murphy int ret; 84601db923eSDan Murphy 84701db923eSDan Murphy dp83869 = devm_kzalloc(&phydev->mdio.dev, sizeof(*dp83869), 84801db923eSDan Murphy GFP_KERNEL); 84901db923eSDan Murphy if (!dp83869) 85001db923eSDan Murphy return -ENOMEM; 85101db923eSDan Murphy 85201db923eSDan Murphy phydev->priv = dp83869; 85301db923eSDan Murphy 85401db923eSDan Murphy ret = dp83869_of_init(phydev); 85501db923eSDan Murphy if (ret) 85601db923eSDan Murphy return ret; 85701db923eSDan Murphy 858*4217a64eSMichael Walle if (dp83869->mode == DP83869_RGMII_100_BASE || 859*4217a64eSMichael Walle dp83869->mode == DP83869_RGMII_1000_BASE) 860*4217a64eSMichael Walle phydev->port = PORT_FIBRE; 861*4217a64eSMichael Walle 86201db923eSDan Murphy return dp83869_config_init(phydev); 86301db923eSDan Murphy } 86401db923eSDan Murphy 86501db923eSDan Murphy static int dp83869_phy_reset(struct phy_device *phydev) 86601db923eSDan Murphy { 86701db923eSDan Murphy int ret; 86801db923eSDan Murphy 86901db923eSDan Murphy ret = phy_write(phydev, DP83869_CTRL, DP83869_SW_RESET); 87001db923eSDan Murphy if (ret < 0) 87101db923eSDan Murphy return ret; 87201db923eSDan Murphy 87301db923eSDan Murphy usleep_range(10, 20); 87401db923eSDan Murphy 87501db923eSDan Murphy /* Global sw reset sets all registers to default. 87601db923eSDan Murphy * Need to set the registers in the PHY to the right config. 87701db923eSDan Murphy */ 87801db923eSDan Murphy return dp83869_config_init(phydev); 87901db923eSDan Murphy } 88001db923eSDan Murphy 88101db923eSDan Murphy static struct phy_driver dp83869_driver[] = { 88201db923eSDan Murphy { 88301db923eSDan Murphy PHY_ID_MATCH_MODEL(DP83869_PHY_ID), 88401db923eSDan Murphy .name = "TI DP83869", 88501db923eSDan Murphy 88601db923eSDan Murphy .probe = dp83869_probe, 88701db923eSDan Murphy .config_init = dp83869_config_init, 88801db923eSDan Murphy .soft_reset = dp83869_phy_reset, 88901db923eSDan Murphy 89001db923eSDan Murphy /* IRQ related */ 89101db923eSDan Murphy .config_intr = dp83869_config_intr, 8921d1ae3c6SIoana Ciornei .handle_interrupt = dp83869_handle_interrupt, 893a29de52bSDan Murphy .read_status = dp83869_read_status, 89401db923eSDan Murphy 895811ac400SDan Murphy .get_tunable = dp83869_get_tunable, 896811ac400SDan Murphy .set_tunable = dp83869_set_tunable, 897811ac400SDan Murphy 89849fc2301SDan Murphy .get_wol = dp83869_get_wol, 89949fc2301SDan Murphy .set_wol = dp83869_set_wol, 90049fc2301SDan Murphy 90101db923eSDan Murphy .suspend = genphy_suspend, 90201db923eSDan Murphy .resume = genphy_resume, 90301db923eSDan Murphy }, 90401db923eSDan Murphy }; 90501db923eSDan Murphy module_phy_driver(dp83869_driver); 90601db923eSDan Murphy 90701db923eSDan Murphy static struct mdio_device_id __maybe_unused dp83869_tbl[] = { 90801db923eSDan Murphy { PHY_ID_MATCH_MODEL(DP83869_PHY_ID) }, 90901db923eSDan Murphy { } 91001db923eSDan Murphy }; 91101db923eSDan Murphy MODULE_DEVICE_TABLE(mdio, dp83869_tbl); 91201db923eSDan Murphy 91301db923eSDan Murphy MODULE_DESCRIPTION("Texas Instruments DP83869 PHY driver"); 91401db923eSDan Murphy MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com"); 91501db923eSDan Murphy MODULE_LICENSE("GPL v2"); 916