162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Driver for the Texas Instruments DP83869 PHY 362306a36Sopenharmony_ci * Copyright (C) 2019 Texas Instruments Inc. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/ethtool.h> 762306a36Sopenharmony_ci#include <linux/etherdevice.h> 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/mii.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/of.h> 1262306a36Sopenharmony_ci#include <linux/phy.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/bitfield.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <dt-bindings/net/ti-dp83869.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define DP83869_PHY_ID 0x2000a0f1 1962306a36Sopenharmony_ci#define DP83561_PHY_ID 0x2000a1a4 2062306a36Sopenharmony_ci#define DP83869_DEVADDR 0x1f 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define MII_DP83869_PHYCTRL 0x10 2362306a36Sopenharmony_ci#define MII_DP83869_MICR 0x12 2462306a36Sopenharmony_ci#define MII_DP83869_ISR 0x13 2562306a36Sopenharmony_ci#define DP83869_CFG2 0x14 2662306a36Sopenharmony_ci#define DP83869_CTRL 0x1f 2762306a36Sopenharmony_ci#define DP83869_CFG4 0x1e 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* Extended Registers */ 3062306a36Sopenharmony_ci#define DP83869_GEN_CFG3 0x0031 3162306a36Sopenharmony_ci#define DP83869_RGMIICTL 0x0032 3262306a36Sopenharmony_ci#define DP83869_STRAP_STS1 0x006e 3362306a36Sopenharmony_ci#define DP83869_RGMIIDCTL 0x0086 3462306a36Sopenharmony_ci#define DP83869_RXFCFG 0x0134 3562306a36Sopenharmony_ci#define DP83869_RXFPMD1 0x0136 3662306a36Sopenharmony_ci#define DP83869_RXFPMD2 0x0137 3762306a36Sopenharmony_ci#define DP83869_RXFPMD3 0x0138 3862306a36Sopenharmony_ci#define DP83869_RXFSOP1 0x0139 3962306a36Sopenharmony_ci#define DP83869_RXFSOP2 0x013A 4062306a36Sopenharmony_ci#define DP83869_RXFSOP3 0x013B 4162306a36Sopenharmony_ci#define DP83869_IO_MUX_CFG 0x0170 4262306a36Sopenharmony_ci#define DP83869_OP_MODE 0x01df 4362306a36Sopenharmony_ci#define DP83869_FX_CTRL 0x0c00 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define DP83869_SW_RESET BIT(15) 4662306a36Sopenharmony_ci#define DP83869_SW_RESTART BIT(14) 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* MICR Interrupt bits */ 4962306a36Sopenharmony_ci#define MII_DP83869_MICR_AN_ERR_INT_EN BIT(15) 5062306a36Sopenharmony_ci#define MII_DP83869_MICR_SPEED_CHNG_INT_EN BIT(14) 5162306a36Sopenharmony_ci#define MII_DP83869_MICR_DUP_MODE_CHNG_INT_EN BIT(13) 5262306a36Sopenharmony_ci#define MII_DP83869_MICR_PAGE_RXD_INT_EN BIT(12) 5362306a36Sopenharmony_ci#define MII_DP83869_MICR_AUTONEG_COMP_INT_EN BIT(11) 5462306a36Sopenharmony_ci#define MII_DP83869_MICR_LINK_STS_CHNG_INT_EN BIT(10) 5562306a36Sopenharmony_ci#define MII_DP83869_MICR_FALSE_CARRIER_INT_EN BIT(8) 5662306a36Sopenharmony_ci#define MII_DP83869_MICR_SLEEP_MODE_CHNG_INT_EN BIT(4) 5762306a36Sopenharmony_ci#define MII_DP83869_MICR_WOL_INT_EN BIT(3) 5862306a36Sopenharmony_ci#define MII_DP83869_MICR_XGMII_ERR_INT_EN BIT(2) 5962306a36Sopenharmony_ci#define MII_DP83869_MICR_POL_CHNG_INT_EN BIT(1) 6062306a36Sopenharmony_ci#define MII_DP83869_MICR_JABBER_INT_EN BIT(0) 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#define MII_DP83869_BMCR_DEFAULT (BMCR_ANENABLE | \ 6362306a36Sopenharmony_ci BMCR_FULLDPLX | \ 6462306a36Sopenharmony_ci BMCR_SPEED1000) 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define MII_DP83869_FIBER_ADVERTISE (ADVERTISED_FIBRE | \ 6762306a36Sopenharmony_ci ADVERTISED_Pause | \ 6862306a36Sopenharmony_ci ADVERTISED_Asym_Pause) 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* This is the same bit mask as the BMCR so re-use the BMCR default */ 7162306a36Sopenharmony_ci#define DP83869_FX_CTRL_DEFAULT MII_DP83869_BMCR_DEFAULT 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* CFG1 bits */ 7462306a36Sopenharmony_ci#define DP83869_CFG1_DEFAULT (ADVERTISE_1000HALF | \ 7562306a36Sopenharmony_ci ADVERTISE_1000FULL | \ 7662306a36Sopenharmony_ci CTL1000_AS_MASTER) 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* RGMIICTL bits */ 7962306a36Sopenharmony_ci#define DP83869_RGMII_TX_CLK_DELAY_EN BIT(1) 8062306a36Sopenharmony_ci#define DP83869_RGMII_RX_CLK_DELAY_EN BIT(0) 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* RGMIIDCTL */ 8362306a36Sopenharmony_ci#define DP83869_RGMII_CLK_DELAY_SHIFT 4 8462306a36Sopenharmony_ci#define DP83869_CLK_DELAY_DEF 7 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/* STRAP_STS1 bits */ 8762306a36Sopenharmony_ci#define DP83869_STRAP_OP_MODE_MASK GENMASK(2, 0) 8862306a36Sopenharmony_ci#define DP83869_STRAP_STS1_RESERVED BIT(11) 8962306a36Sopenharmony_ci#define DP83869_STRAP_MIRROR_ENABLED BIT(12) 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* PHYCTRL bits */ 9262306a36Sopenharmony_ci#define DP83869_RX_FIFO_SHIFT 12 9362306a36Sopenharmony_ci#define DP83869_TX_FIFO_SHIFT 14 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/* PHY_CTRL lower bytes 0x48 are declared as reserved */ 9662306a36Sopenharmony_ci#define DP83869_PHY_CTRL_DEFAULT 0x48 9762306a36Sopenharmony_ci#define DP83869_PHYCR_FIFO_DEPTH_MASK GENMASK(15, 12) 9862306a36Sopenharmony_ci#define DP83869_PHYCR_RESERVED_MASK BIT(11) 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci/* IO_MUX_CFG bits */ 10162306a36Sopenharmony_ci#define DP83869_IO_MUX_CFG_IO_IMPEDANCE_CTRL 0x1f 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#define DP83869_IO_MUX_CFG_IO_IMPEDANCE_MAX 0x0 10462306a36Sopenharmony_ci#define DP83869_IO_MUX_CFG_IO_IMPEDANCE_MIN 0x1f 10562306a36Sopenharmony_ci#define DP83869_IO_MUX_CFG_CLK_O_SEL_MASK (0x1f << 8) 10662306a36Sopenharmony_ci#define DP83869_IO_MUX_CFG_CLK_O_SEL_SHIFT 8 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/* CFG3 bits */ 10962306a36Sopenharmony_ci#define DP83869_CFG3_PORT_MIRROR_EN BIT(0) 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/* CFG4 bits */ 11262306a36Sopenharmony_ci#define DP83869_INT_OE BIT(7) 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* OP MODE */ 11562306a36Sopenharmony_ci#define DP83869_OP_MODE_MII BIT(5) 11662306a36Sopenharmony_ci#define DP83869_SGMII_RGMII_BRIDGE BIT(6) 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* RXFCFG bits*/ 11962306a36Sopenharmony_ci#define DP83869_WOL_MAGIC_EN BIT(0) 12062306a36Sopenharmony_ci#define DP83869_WOL_PATTERN_EN BIT(1) 12162306a36Sopenharmony_ci#define DP83869_WOL_BCAST_EN BIT(2) 12262306a36Sopenharmony_ci#define DP83869_WOL_UCAST_EN BIT(4) 12362306a36Sopenharmony_ci#define DP83869_WOL_SEC_EN BIT(5) 12462306a36Sopenharmony_ci#define DP83869_WOL_ENH_MAC BIT(7) 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/* CFG2 bits */ 12762306a36Sopenharmony_ci#define DP83869_DOWNSHIFT_EN (BIT(8) | BIT(9)) 12862306a36Sopenharmony_ci#define DP83869_DOWNSHIFT_ATTEMPT_MASK (BIT(10) | BIT(11)) 12962306a36Sopenharmony_ci#define DP83869_DOWNSHIFT_1_COUNT_VAL 0 13062306a36Sopenharmony_ci#define DP83869_DOWNSHIFT_2_COUNT_VAL 1 13162306a36Sopenharmony_ci#define DP83869_DOWNSHIFT_4_COUNT_VAL 2 13262306a36Sopenharmony_ci#define DP83869_DOWNSHIFT_8_COUNT_VAL 3 13362306a36Sopenharmony_ci#define DP83869_DOWNSHIFT_1_COUNT 1 13462306a36Sopenharmony_ci#define DP83869_DOWNSHIFT_2_COUNT 2 13562306a36Sopenharmony_ci#define DP83869_DOWNSHIFT_4_COUNT 4 13662306a36Sopenharmony_ci#define DP83869_DOWNSHIFT_8_COUNT 8 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cienum { 13962306a36Sopenharmony_ci DP83869_PORT_MIRRORING_KEEP, 14062306a36Sopenharmony_ci DP83869_PORT_MIRRORING_EN, 14162306a36Sopenharmony_ci DP83869_PORT_MIRRORING_DIS, 14262306a36Sopenharmony_ci}; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistruct dp83869_private { 14562306a36Sopenharmony_ci int tx_fifo_depth; 14662306a36Sopenharmony_ci int rx_fifo_depth; 14762306a36Sopenharmony_ci s32 rx_int_delay; 14862306a36Sopenharmony_ci s32 tx_int_delay; 14962306a36Sopenharmony_ci int io_impedance; 15062306a36Sopenharmony_ci int port_mirroring; 15162306a36Sopenharmony_ci bool rxctrl_strap_quirk; 15262306a36Sopenharmony_ci int clk_output_sel; 15362306a36Sopenharmony_ci int mode; 15462306a36Sopenharmony_ci}; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic int dp83869_read_status(struct phy_device *phydev) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci struct dp83869_private *dp83869 = phydev->priv; 15962306a36Sopenharmony_ci int ret; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci ret = genphy_read_status(phydev); 16262306a36Sopenharmony_ci if (ret) 16362306a36Sopenharmony_ci return ret; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported)) { 16662306a36Sopenharmony_ci if (phydev->link) { 16762306a36Sopenharmony_ci if (dp83869->mode == DP83869_RGMII_100_BASE) 16862306a36Sopenharmony_ci phydev->speed = SPEED_100; 16962306a36Sopenharmony_ci } else { 17062306a36Sopenharmony_ci phydev->speed = SPEED_UNKNOWN; 17162306a36Sopenharmony_ci phydev->duplex = DUPLEX_UNKNOWN; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return 0; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic int dp83869_ack_interrupt(struct phy_device *phydev) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci int err = phy_read(phydev, MII_DP83869_ISR); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (err < 0) 18362306a36Sopenharmony_ci return err; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return 0; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic int dp83869_config_intr(struct phy_device *phydev) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci int micr_status = 0, err; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 19362306a36Sopenharmony_ci err = dp83869_ack_interrupt(phydev); 19462306a36Sopenharmony_ci if (err) 19562306a36Sopenharmony_ci return err; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci micr_status = phy_read(phydev, MII_DP83869_MICR); 19862306a36Sopenharmony_ci if (micr_status < 0) 19962306a36Sopenharmony_ci return micr_status; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci micr_status |= 20262306a36Sopenharmony_ci (MII_DP83869_MICR_AN_ERR_INT_EN | 20362306a36Sopenharmony_ci MII_DP83869_MICR_SPEED_CHNG_INT_EN | 20462306a36Sopenharmony_ci MII_DP83869_MICR_AUTONEG_COMP_INT_EN | 20562306a36Sopenharmony_ci MII_DP83869_MICR_LINK_STS_CHNG_INT_EN | 20662306a36Sopenharmony_ci MII_DP83869_MICR_DUP_MODE_CHNG_INT_EN | 20762306a36Sopenharmony_ci MII_DP83869_MICR_SLEEP_MODE_CHNG_INT_EN); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci err = phy_write(phydev, MII_DP83869_MICR, micr_status); 21062306a36Sopenharmony_ci } else { 21162306a36Sopenharmony_ci err = phy_write(phydev, MII_DP83869_MICR, micr_status); 21262306a36Sopenharmony_ci if (err) 21362306a36Sopenharmony_ci return err; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci err = dp83869_ack_interrupt(phydev); 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci return err; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic irqreturn_t dp83869_handle_interrupt(struct phy_device *phydev) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci int irq_status, irq_enabled; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci irq_status = phy_read(phydev, MII_DP83869_ISR); 22662306a36Sopenharmony_ci if (irq_status < 0) { 22762306a36Sopenharmony_ci phy_error(phydev); 22862306a36Sopenharmony_ci return IRQ_NONE; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci irq_enabled = phy_read(phydev, MII_DP83869_MICR); 23262306a36Sopenharmony_ci if (irq_enabled < 0) { 23362306a36Sopenharmony_ci phy_error(phydev); 23462306a36Sopenharmony_ci return IRQ_NONE; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (!(irq_status & irq_enabled)) 23862306a36Sopenharmony_ci return IRQ_NONE; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci phy_trigger_machine(phydev); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci return IRQ_HANDLED; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic int dp83869_set_wol(struct phy_device *phydev, 24662306a36Sopenharmony_ci struct ethtool_wolinfo *wol) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci struct net_device *ndev = phydev->attached_dev; 24962306a36Sopenharmony_ci int val_rxcfg, val_micr; 25062306a36Sopenharmony_ci const u8 *mac; 25162306a36Sopenharmony_ci int ret; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci val_rxcfg = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_RXFCFG); 25462306a36Sopenharmony_ci if (val_rxcfg < 0) 25562306a36Sopenharmony_ci return val_rxcfg; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci val_micr = phy_read(phydev, MII_DP83869_MICR); 25862306a36Sopenharmony_ci if (val_micr < 0) 25962306a36Sopenharmony_ci return val_micr; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (wol->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_UCAST | 26262306a36Sopenharmony_ci WAKE_BCAST)) { 26362306a36Sopenharmony_ci val_rxcfg |= DP83869_WOL_ENH_MAC; 26462306a36Sopenharmony_ci val_micr |= MII_DP83869_MICR_WOL_INT_EN; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (wol->wolopts & WAKE_MAGIC || 26762306a36Sopenharmony_ci wol->wolopts & WAKE_MAGICSECURE) { 26862306a36Sopenharmony_ci mac = (const u8 *)ndev->dev_addr; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (!is_valid_ether_addr(mac)) 27162306a36Sopenharmony_ci return -EINVAL; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci ret = phy_write_mmd(phydev, DP83869_DEVADDR, 27462306a36Sopenharmony_ci DP83869_RXFPMD1, 27562306a36Sopenharmony_ci mac[1] << 8 | mac[0]); 27662306a36Sopenharmony_ci if (ret) 27762306a36Sopenharmony_ci return ret; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci ret = phy_write_mmd(phydev, DP83869_DEVADDR, 28062306a36Sopenharmony_ci DP83869_RXFPMD2, 28162306a36Sopenharmony_ci mac[3] << 8 | mac[2]); 28262306a36Sopenharmony_ci if (ret) 28362306a36Sopenharmony_ci return ret; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci ret = phy_write_mmd(phydev, DP83869_DEVADDR, 28662306a36Sopenharmony_ci DP83869_RXFPMD3, 28762306a36Sopenharmony_ci mac[5] << 8 | mac[4]); 28862306a36Sopenharmony_ci if (ret) 28962306a36Sopenharmony_ci return ret; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci val_rxcfg |= DP83869_WOL_MAGIC_EN; 29262306a36Sopenharmony_ci } else { 29362306a36Sopenharmony_ci val_rxcfg &= ~DP83869_WOL_MAGIC_EN; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (wol->wolopts & WAKE_MAGICSECURE) { 29762306a36Sopenharmony_ci ret = phy_write_mmd(phydev, DP83869_DEVADDR, 29862306a36Sopenharmony_ci DP83869_RXFSOP1, 29962306a36Sopenharmony_ci (wol->sopass[1] << 8) | wol->sopass[0]); 30062306a36Sopenharmony_ci if (ret) 30162306a36Sopenharmony_ci return ret; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci ret = phy_write_mmd(phydev, DP83869_DEVADDR, 30462306a36Sopenharmony_ci DP83869_RXFSOP2, 30562306a36Sopenharmony_ci (wol->sopass[3] << 8) | wol->sopass[2]); 30662306a36Sopenharmony_ci if (ret) 30762306a36Sopenharmony_ci return ret; 30862306a36Sopenharmony_ci ret = phy_write_mmd(phydev, DP83869_DEVADDR, 30962306a36Sopenharmony_ci DP83869_RXFSOP3, 31062306a36Sopenharmony_ci (wol->sopass[5] << 8) | wol->sopass[4]); 31162306a36Sopenharmony_ci if (ret) 31262306a36Sopenharmony_ci return ret; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci val_rxcfg |= DP83869_WOL_SEC_EN; 31562306a36Sopenharmony_ci } else { 31662306a36Sopenharmony_ci val_rxcfg &= ~DP83869_WOL_SEC_EN; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (wol->wolopts & WAKE_UCAST) 32062306a36Sopenharmony_ci val_rxcfg |= DP83869_WOL_UCAST_EN; 32162306a36Sopenharmony_ci else 32262306a36Sopenharmony_ci val_rxcfg &= ~DP83869_WOL_UCAST_EN; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (wol->wolopts & WAKE_BCAST) 32562306a36Sopenharmony_ci val_rxcfg |= DP83869_WOL_BCAST_EN; 32662306a36Sopenharmony_ci else 32762306a36Sopenharmony_ci val_rxcfg &= ~DP83869_WOL_BCAST_EN; 32862306a36Sopenharmony_ci } else { 32962306a36Sopenharmony_ci val_rxcfg &= ~DP83869_WOL_ENH_MAC; 33062306a36Sopenharmony_ci val_micr &= ~MII_DP83869_MICR_WOL_INT_EN; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RXFCFG, val_rxcfg); 33462306a36Sopenharmony_ci if (ret) 33562306a36Sopenharmony_ci return ret; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci return phy_write(phydev, MII_DP83869_MICR, val_micr); 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic void dp83869_get_wol(struct phy_device *phydev, 34162306a36Sopenharmony_ci struct ethtool_wolinfo *wol) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci int value, sopass_val; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci wol->supported = (WAKE_UCAST | WAKE_BCAST | WAKE_MAGIC | 34662306a36Sopenharmony_ci WAKE_MAGICSECURE); 34762306a36Sopenharmony_ci wol->wolopts = 0; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci value = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_RXFCFG); 35062306a36Sopenharmony_ci if (value < 0) { 35162306a36Sopenharmony_ci phydev_err(phydev, "Failed to read RX CFG\n"); 35262306a36Sopenharmony_ci return; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (value & DP83869_WOL_UCAST_EN) 35662306a36Sopenharmony_ci wol->wolopts |= WAKE_UCAST; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (value & DP83869_WOL_BCAST_EN) 35962306a36Sopenharmony_ci wol->wolopts |= WAKE_BCAST; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (value & DP83869_WOL_MAGIC_EN) 36262306a36Sopenharmony_ci wol->wolopts |= WAKE_MAGIC; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (value & DP83869_WOL_SEC_EN) { 36562306a36Sopenharmony_ci sopass_val = phy_read_mmd(phydev, DP83869_DEVADDR, 36662306a36Sopenharmony_ci DP83869_RXFSOP1); 36762306a36Sopenharmony_ci if (sopass_val < 0) { 36862306a36Sopenharmony_ci phydev_err(phydev, "Failed to read RX SOP 1\n"); 36962306a36Sopenharmony_ci return; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci wol->sopass[0] = (sopass_val & 0xff); 37362306a36Sopenharmony_ci wol->sopass[1] = (sopass_val >> 8); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci sopass_val = phy_read_mmd(phydev, DP83869_DEVADDR, 37662306a36Sopenharmony_ci DP83869_RXFSOP2); 37762306a36Sopenharmony_ci if (sopass_val < 0) { 37862306a36Sopenharmony_ci phydev_err(phydev, "Failed to read RX SOP 2\n"); 37962306a36Sopenharmony_ci return; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci wol->sopass[2] = (sopass_val & 0xff); 38362306a36Sopenharmony_ci wol->sopass[3] = (sopass_val >> 8); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci sopass_val = phy_read_mmd(phydev, DP83869_DEVADDR, 38662306a36Sopenharmony_ci DP83869_RXFSOP3); 38762306a36Sopenharmony_ci if (sopass_val < 0) { 38862306a36Sopenharmony_ci phydev_err(phydev, "Failed to read RX SOP 3\n"); 38962306a36Sopenharmony_ci return; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci wol->sopass[4] = (sopass_val & 0xff); 39362306a36Sopenharmony_ci wol->sopass[5] = (sopass_val >> 8); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci wol->wolopts |= WAKE_MAGICSECURE; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci if (!(value & DP83869_WOL_ENH_MAC)) 39962306a36Sopenharmony_ci wol->wolopts = 0; 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic int dp83869_get_downshift(struct phy_device *phydev, u8 *data) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci int val, cnt, enable, count; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci val = phy_read(phydev, DP83869_CFG2); 40762306a36Sopenharmony_ci if (val < 0) 40862306a36Sopenharmony_ci return val; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci enable = FIELD_GET(DP83869_DOWNSHIFT_EN, val); 41162306a36Sopenharmony_ci cnt = FIELD_GET(DP83869_DOWNSHIFT_ATTEMPT_MASK, val); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci switch (cnt) { 41462306a36Sopenharmony_ci case DP83869_DOWNSHIFT_1_COUNT_VAL: 41562306a36Sopenharmony_ci count = DP83869_DOWNSHIFT_1_COUNT; 41662306a36Sopenharmony_ci break; 41762306a36Sopenharmony_ci case DP83869_DOWNSHIFT_2_COUNT_VAL: 41862306a36Sopenharmony_ci count = DP83869_DOWNSHIFT_2_COUNT; 41962306a36Sopenharmony_ci break; 42062306a36Sopenharmony_ci case DP83869_DOWNSHIFT_4_COUNT_VAL: 42162306a36Sopenharmony_ci count = DP83869_DOWNSHIFT_4_COUNT; 42262306a36Sopenharmony_ci break; 42362306a36Sopenharmony_ci case DP83869_DOWNSHIFT_8_COUNT_VAL: 42462306a36Sopenharmony_ci count = DP83869_DOWNSHIFT_8_COUNT; 42562306a36Sopenharmony_ci break; 42662306a36Sopenharmony_ci default: 42762306a36Sopenharmony_ci return -EINVAL; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci *data = enable ? count : DOWNSHIFT_DEV_DISABLE; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci return 0; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic int dp83869_set_downshift(struct phy_device *phydev, u8 cnt) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci int val, count; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (cnt > DP83869_DOWNSHIFT_8_COUNT) 44062306a36Sopenharmony_ci return -EINVAL; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (!cnt) 44362306a36Sopenharmony_ci return phy_clear_bits(phydev, DP83869_CFG2, 44462306a36Sopenharmony_ci DP83869_DOWNSHIFT_EN); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci switch (cnt) { 44762306a36Sopenharmony_ci case DP83869_DOWNSHIFT_1_COUNT: 44862306a36Sopenharmony_ci count = DP83869_DOWNSHIFT_1_COUNT_VAL; 44962306a36Sopenharmony_ci break; 45062306a36Sopenharmony_ci case DP83869_DOWNSHIFT_2_COUNT: 45162306a36Sopenharmony_ci count = DP83869_DOWNSHIFT_2_COUNT_VAL; 45262306a36Sopenharmony_ci break; 45362306a36Sopenharmony_ci case DP83869_DOWNSHIFT_4_COUNT: 45462306a36Sopenharmony_ci count = DP83869_DOWNSHIFT_4_COUNT_VAL; 45562306a36Sopenharmony_ci break; 45662306a36Sopenharmony_ci case DP83869_DOWNSHIFT_8_COUNT: 45762306a36Sopenharmony_ci count = DP83869_DOWNSHIFT_8_COUNT_VAL; 45862306a36Sopenharmony_ci break; 45962306a36Sopenharmony_ci default: 46062306a36Sopenharmony_ci phydev_err(phydev, 46162306a36Sopenharmony_ci "Downshift count must be 1, 2, 4 or 8\n"); 46262306a36Sopenharmony_ci return -EINVAL; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci val = DP83869_DOWNSHIFT_EN; 46662306a36Sopenharmony_ci val |= FIELD_PREP(DP83869_DOWNSHIFT_ATTEMPT_MASK, count); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci return phy_modify(phydev, DP83869_CFG2, 46962306a36Sopenharmony_ci DP83869_DOWNSHIFT_EN | DP83869_DOWNSHIFT_ATTEMPT_MASK, 47062306a36Sopenharmony_ci val); 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic int dp83869_get_tunable(struct phy_device *phydev, 47462306a36Sopenharmony_ci struct ethtool_tunable *tuna, void *data) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci switch (tuna->id) { 47762306a36Sopenharmony_ci case ETHTOOL_PHY_DOWNSHIFT: 47862306a36Sopenharmony_ci return dp83869_get_downshift(phydev, data); 47962306a36Sopenharmony_ci default: 48062306a36Sopenharmony_ci return -EOPNOTSUPP; 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic int dp83869_set_tunable(struct phy_device *phydev, 48562306a36Sopenharmony_ci struct ethtool_tunable *tuna, const void *data) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci switch (tuna->id) { 48862306a36Sopenharmony_ci case ETHTOOL_PHY_DOWNSHIFT: 48962306a36Sopenharmony_ci return dp83869_set_downshift(phydev, *(const u8 *)data); 49062306a36Sopenharmony_ci default: 49162306a36Sopenharmony_ci return -EOPNOTSUPP; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic int dp83869_config_port_mirroring(struct phy_device *phydev) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci struct dp83869_private *dp83869 = phydev->priv; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (dp83869->port_mirroring == DP83869_PORT_MIRRORING_EN) 50062306a36Sopenharmony_ci return phy_set_bits_mmd(phydev, DP83869_DEVADDR, 50162306a36Sopenharmony_ci DP83869_GEN_CFG3, 50262306a36Sopenharmony_ci DP83869_CFG3_PORT_MIRROR_EN); 50362306a36Sopenharmony_ci else 50462306a36Sopenharmony_ci return phy_clear_bits_mmd(phydev, DP83869_DEVADDR, 50562306a36Sopenharmony_ci DP83869_GEN_CFG3, 50662306a36Sopenharmony_ci DP83869_CFG3_PORT_MIRROR_EN); 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic int dp83869_set_strapped_mode(struct phy_device *phydev) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci struct dp83869_private *dp83869 = phydev->priv; 51262306a36Sopenharmony_ci int val; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_STRAP_STS1); 51562306a36Sopenharmony_ci if (val < 0) 51662306a36Sopenharmony_ci return val; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci dp83869->mode = val & DP83869_STRAP_OP_MODE_MASK; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci return 0; 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_OF_MDIO) 52462306a36Sopenharmony_cistatic const int dp83869_internal_delay[] = {250, 500, 750, 1000, 1250, 1500, 52562306a36Sopenharmony_ci 1750, 2000, 2250, 2500, 2750, 3000, 52662306a36Sopenharmony_ci 3250, 3500, 3750, 4000}; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_cistatic int dp83869_of_init(struct phy_device *phydev) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci struct dp83869_private *dp83869 = phydev->priv; 53162306a36Sopenharmony_ci struct device *dev = &phydev->mdio.dev; 53262306a36Sopenharmony_ci struct device_node *of_node = dev->of_node; 53362306a36Sopenharmony_ci int delay_size = ARRAY_SIZE(dp83869_internal_delay); 53462306a36Sopenharmony_ci int ret; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if (!of_node) 53762306a36Sopenharmony_ci return -ENODEV; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci dp83869->io_impedance = -EINVAL; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* Optional configuration */ 54262306a36Sopenharmony_ci ret = of_property_read_u32(of_node, "ti,clk-output-sel", 54362306a36Sopenharmony_ci &dp83869->clk_output_sel); 54462306a36Sopenharmony_ci if (ret || dp83869->clk_output_sel > DP83869_CLK_O_SEL_REF_CLK) 54562306a36Sopenharmony_ci dp83869->clk_output_sel = DP83869_CLK_O_SEL_REF_CLK; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci ret = of_property_read_u32(of_node, "ti,op-mode", &dp83869->mode); 54862306a36Sopenharmony_ci if (ret == 0) { 54962306a36Sopenharmony_ci if (dp83869->mode < DP83869_RGMII_COPPER_ETHERNET || 55062306a36Sopenharmony_ci dp83869->mode > DP83869_SGMII_COPPER_ETHERNET) 55162306a36Sopenharmony_ci return -EINVAL; 55262306a36Sopenharmony_ci } else { 55362306a36Sopenharmony_ci ret = dp83869_set_strapped_mode(phydev); 55462306a36Sopenharmony_ci if (ret) 55562306a36Sopenharmony_ci return ret; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (of_property_read_bool(of_node, "ti,max-output-impedance")) 55962306a36Sopenharmony_ci dp83869->io_impedance = DP83869_IO_MUX_CFG_IO_IMPEDANCE_MAX; 56062306a36Sopenharmony_ci else if (of_property_read_bool(of_node, "ti,min-output-impedance")) 56162306a36Sopenharmony_ci dp83869->io_impedance = DP83869_IO_MUX_CFG_IO_IMPEDANCE_MIN; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci if (of_property_read_bool(of_node, "enet-phy-lane-swap")) { 56462306a36Sopenharmony_ci dp83869->port_mirroring = DP83869_PORT_MIRRORING_EN; 56562306a36Sopenharmony_ci } else { 56662306a36Sopenharmony_ci /* If the lane swap is not in the DT then check the straps */ 56762306a36Sopenharmony_ci ret = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_STRAP_STS1); 56862306a36Sopenharmony_ci if (ret < 0) 56962306a36Sopenharmony_ci return ret; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci if (ret & DP83869_STRAP_MIRROR_ENABLED) 57262306a36Sopenharmony_ci dp83869->port_mirroring = DP83869_PORT_MIRRORING_EN; 57362306a36Sopenharmony_ci else 57462306a36Sopenharmony_ci dp83869->port_mirroring = DP83869_PORT_MIRRORING_DIS; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci ret = 0; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (of_property_read_u32(of_node, "rx-fifo-depth", 58062306a36Sopenharmony_ci &dp83869->rx_fifo_depth)) 58162306a36Sopenharmony_ci dp83869->rx_fifo_depth = DP83869_PHYCR_FIFO_DEPTH_4_B_NIB; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci if (of_property_read_u32(of_node, "tx-fifo-depth", 58462306a36Sopenharmony_ci &dp83869->tx_fifo_depth)) 58562306a36Sopenharmony_ci dp83869->tx_fifo_depth = DP83869_PHYCR_FIFO_DEPTH_4_B_NIB; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci dp83869->rx_int_delay = phy_get_internal_delay(phydev, dev, 58862306a36Sopenharmony_ci &dp83869_internal_delay[0], 58962306a36Sopenharmony_ci delay_size, true); 59062306a36Sopenharmony_ci if (dp83869->rx_int_delay < 0) 59162306a36Sopenharmony_ci dp83869->rx_int_delay = DP83869_CLK_DELAY_DEF; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci dp83869->tx_int_delay = phy_get_internal_delay(phydev, dev, 59462306a36Sopenharmony_ci &dp83869_internal_delay[0], 59562306a36Sopenharmony_ci delay_size, false); 59662306a36Sopenharmony_ci if (dp83869->tx_int_delay < 0) 59762306a36Sopenharmony_ci dp83869->tx_int_delay = DP83869_CLK_DELAY_DEF; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci return ret; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci#else 60262306a36Sopenharmony_cistatic int dp83869_of_init(struct phy_device *phydev) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci return dp83869_set_strapped_mode(phydev); 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci#endif /* CONFIG_OF_MDIO */ 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_cistatic int dp83869_configure_rgmii(struct phy_device *phydev, 60962306a36Sopenharmony_ci struct dp83869_private *dp83869) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci int ret = 0, val; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci if (phy_interface_is_rgmii(phydev)) { 61462306a36Sopenharmony_ci val = phy_read(phydev, MII_DP83869_PHYCTRL); 61562306a36Sopenharmony_ci if (val < 0) 61662306a36Sopenharmony_ci return val; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci val &= ~DP83869_PHYCR_FIFO_DEPTH_MASK; 61962306a36Sopenharmony_ci val |= (dp83869->tx_fifo_depth << DP83869_TX_FIFO_SHIFT); 62062306a36Sopenharmony_ci val |= (dp83869->rx_fifo_depth << DP83869_RX_FIFO_SHIFT); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci ret = phy_write(phydev, MII_DP83869_PHYCTRL, val); 62362306a36Sopenharmony_ci if (ret) 62462306a36Sopenharmony_ci return ret; 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci if (dp83869->io_impedance >= 0) 62862306a36Sopenharmony_ci ret = phy_modify_mmd(phydev, DP83869_DEVADDR, 62962306a36Sopenharmony_ci DP83869_IO_MUX_CFG, 63062306a36Sopenharmony_ci DP83869_IO_MUX_CFG_IO_IMPEDANCE_CTRL, 63162306a36Sopenharmony_ci dp83869->io_impedance & 63262306a36Sopenharmony_ci DP83869_IO_MUX_CFG_IO_IMPEDANCE_CTRL); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci return ret; 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cistatic int dp83869_configure_fiber(struct phy_device *phydev, 63862306a36Sopenharmony_ci struct dp83869_private *dp83869) 63962306a36Sopenharmony_ci{ 64062306a36Sopenharmony_ci int bmcr; 64162306a36Sopenharmony_ci int ret; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci /* Only allow advertising what this PHY supports */ 64462306a36Sopenharmony_ci linkmode_and(phydev->advertising, phydev->advertising, 64562306a36Sopenharmony_ci phydev->supported); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported); 64862306a36Sopenharmony_ci linkmode_set_bit(ADVERTISED_FIBRE, phydev->advertising); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci if (dp83869->mode == DP83869_RGMII_1000_BASE) { 65162306a36Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, 65262306a36Sopenharmony_ci phydev->supported); 65362306a36Sopenharmony_ci } else { 65462306a36Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_100baseFX_Full_BIT, 65562306a36Sopenharmony_ci phydev->supported); 65662306a36Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_100baseFX_Half_BIT, 65762306a36Sopenharmony_ci phydev->supported); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci /* Auto neg is not supported in 100base FX mode */ 66062306a36Sopenharmony_ci bmcr = phy_read(phydev, MII_BMCR); 66162306a36Sopenharmony_ci if (bmcr < 0) 66262306a36Sopenharmony_ci return bmcr; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci phydev->autoneg = AUTONEG_DISABLE; 66562306a36Sopenharmony_ci linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported); 66662306a36Sopenharmony_ci linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->advertising); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci if (bmcr & BMCR_ANENABLE) { 66962306a36Sopenharmony_ci ret = phy_modify(phydev, MII_BMCR, BMCR_ANENABLE, 0); 67062306a36Sopenharmony_ci if (ret < 0) 67162306a36Sopenharmony_ci return ret; 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci /* Update advertising from supported */ 67662306a36Sopenharmony_ci linkmode_or(phydev->advertising, phydev->advertising, 67762306a36Sopenharmony_ci phydev->supported); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci return 0; 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic int dp83869_configure_mode(struct phy_device *phydev, 68362306a36Sopenharmony_ci struct dp83869_private *dp83869) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci int phy_ctrl_val; 68662306a36Sopenharmony_ci int ret; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci if (dp83869->mode < DP83869_RGMII_COPPER_ETHERNET || 68962306a36Sopenharmony_ci dp83869->mode > DP83869_SGMII_COPPER_ETHERNET) 69062306a36Sopenharmony_ci return -EINVAL; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci /* Below init sequence for each operational mode is defined in 69362306a36Sopenharmony_ci * section 9.4.8 of the datasheet. 69462306a36Sopenharmony_ci */ 69562306a36Sopenharmony_ci phy_ctrl_val = dp83869->mode; 69662306a36Sopenharmony_ci if (phydev->interface == PHY_INTERFACE_MODE_MII) { 69762306a36Sopenharmony_ci if (dp83869->mode == DP83869_100M_MEDIA_CONVERT || 69862306a36Sopenharmony_ci dp83869->mode == DP83869_RGMII_100_BASE) { 69962306a36Sopenharmony_ci phy_ctrl_val |= DP83869_OP_MODE_MII; 70062306a36Sopenharmony_ci } else { 70162306a36Sopenharmony_ci phydev_err(phydev, "selected op-mode is not valid with MII mode\n"); 70262306a36Sopenharmony_ci return -EINVAL; 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_OP_MODE, 70762306a36Sopenharmony_ci phy_ctrl_val); 70862306a36Sopenharmony_ci if (ret) 70962306a36Sopenharmony_ci return ret; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci ret = phy_write(phydev, MII_BMCR, MII_DP83869_BMCR_DEFAULT); 71262306a36Sopenharmony_ci if (ret) 71362306a36Sopenharmony_ci return ret; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci phy_ctrl_val = (dp83869->rx_fifo_depth << DP83869_RX_FIFO_SHIFT | 71662306a36Sopenharmony_ci dp83869->tx_fifo_depth << DP83869_TX_FIFO_SHIFT | 71762306a36Sopenharmony_ci DP83869_PHY_CTRL_DEFAULT); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci switch (dp83869->mode) { 72062306a36Sopenharmony_ci case DP83869_RGMII_COPPER_ETHERNET: 72162306a36Sopenharmony_ci ret = phy_write(phydev, MII_DP83869_PHYCTRL, 72262306a36Sopenharmony_ci phy_ctrl_val); 72362306a36Sopenharmony_ci if (ret) 72462306a36Sopenharmony_ci return ret; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci ret = phy_write(phydev, MII_CTRL1000, DP83869_CFG1_DEFAULT); 72762306a36Sopenharmony_ci if (ret) 72862306a36Sopenharmony_ci return ret; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci ret = dp83869_configure_rgmii(phydev, dp83869); 73162306a36Sopenharmony_ci if (ret) 73262306a36Sopenharmony_ci return ret; 73362306a36Sopenharmony_ci break; 73462306a36Sopenharmony_ci case DP83869_RGMII_SGMII_BRIDGE: 73562306a36Sopenharmony_ci ret = phy_modify_mmd(phydev, DP83869_DEVADDR, DP83869_OP_MODE, 73662306a36Sopenharmony_ci DP83869_SGMII_RGMII_BRIDGE, 73762306a36Sopenharmony_ci DP83869_SGMII_RGMII_BRIDGE); 73862306a36Sopenharmony_ci if (ret) 73962306a36Sopenharmony_ci return ret; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci ret = phy_write_mmd(phydev, DP83869_DEVADDR, 74262306a36Sopenharmony_ci DP83869_FX_CTRL, DP83869_FX_CTRL_DEFAULT); 74362306a36Sopenharmony_ci if (ret) 74462306a36Sopenharmony_ci return ret; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci break; 74762306a36Sopenharmony_ci case DP83869_1000M_MEDIA_CONVERT: 74862306a36Sopenharmony_ci ret = phy_write(phydev, MII_DP83869_PHYCTRL, 74962306a36Sopenharmony_ci phy_ctrl_val); 75062306a36Sopenharmony_ci if (ret) 75162306a36Sopenharmony_ci return ret; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci ret = phy_write_mmd(phydev, DP83869_DEVADDR, 75462306a36Sopenharmony_ci DP83869_FX_CTRL, DP83869_FX_CTRL_DEFAULT); 75562306a36Sopenharmony_ci if (ret) 75662306a36Sopenharmony_ci return ret; 75762306a36Sopenharmony_ci break; 75862306a36Sopenharmony_ci case DP83869_100M_MEDIA_CONVERT: 75962306a36Sopenharmony_ci ret = phy_write(phydev, MII_DP83869_PHYCTRL, 76062306a36Sopenharmony_ci phy_ctrl_val); 76162306a36Sopenharmony_ci if (ret) 76262306a36Sopenharmony_ci return ret; 76362306a36Sopenharmony_ci break; 76462306a36Sopenharmony_ci case DP83869_SGMII_COPPER_ETHERNET: 76562306a36Sopenharmony_ci ret = phy_write(phydev, MII_DP83869_PHYCTRL, 76662306a36Sopenharmony_ci phy_ctrl_val); 76762306a36Sopenharmony_ci if (ret) 76862306a36Sopenharmony_ci return ret; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci ret = phy_write(phydev, MII_CTRL1000, DP83869_CFG1_DEFAULT); 77162306a36Sopenharmony_ci if (ret) 77262306a36Sopenharmony_ci return ret; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci ret = phy_write_mmd(phydev, DP83869_DEVADDR, 77562306a36Sopenharmony_ci DP83869_FX_CTRL, DP83869_FX_CTRL_DEFAULT); 77662306a36Sopenharmony_ci if (ret) 77762306a36Sopenharmony_ci return ret; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci break; 78062306a36Sopenharmony_ci case DP83869_RGMII_1000_BASE: 78162306a36Sopenharmony_ci case DP83869_RGMII_100_BASE: 78262306a36Sopenharmony_ci ret = dp83869_configure_fiber(phydev, dp83869); 78362306a36Sopenharmony_ci break; 78462306a36Sopenharmony_ci default: 78562306a36Sopenharmony_ci return -EINVAL; 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci return ret; 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_cistatic int dp83869_config_init(struct phy_device *phydev) 79262306a36Sopenharmony_ci{ 79362306a36Sopenharmony_ci struct dp83869_private *dp83869 = phydev->priv; 79462306a36Sopenharmony_ci int ret, val; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci /* Force speed optimization for the PHY even if it strapped */ 79762306a36Sopenharmony_ci ret = phy_modify(phydev, DP83869_CFG2, DP83869_DOWNSHIFT_EN, 79862306a36Sopenharmony_ci DP83869_DOWNSHIFT_EN); 79962306a36Sopenharmony_ci if (ret) 80062306a36Sopenharmony_ci return ret; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci ret = dp83869_configure_mode(phydev, dp83869); 80362306a36Sopenharmony_ci if (ret) 80462306a36Sopenharmony_ci return ret; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci /* Enable Interrupt output INT_OE in CFG4 register */ 80762306a36Sopenharmony_ci if (phy_interrupt_is_valid(phydev)) { 80862306a36Sopenharmony_ci val = phy_read(phydev, DP83869_CFG4); 80962306a36Sopenharmony_ci val |= DP83869_INT_OE; 81062306a36Sopenharmony_ci phy_write(phydev, DP83869_CFG4, val); 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci if (dp83869->port_mirroring != DP83869_PORT_MIRRORING_KEEP) 81462306a36Sopenharmony_ci dp83869_config_port_mirroring(phydev); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci /* Clock output selection if muxing property is set */ 81762306a36Sopenharmony_ci if (dp83869->clk_output_sel != DP83869_CLK_O_SEL_REF_CLK) 81862306a36Sopenharmony_ci ret = phy_modify_mmd(phydev, 81962306a36Sopenharmony_ci DP83869_DEVADDR, DP83869_IO_MUX_CFG, 82062306a36Sopenharmony_ci DP83869_IO_MUX_CFG_CLK_O_SEL_MASK, 82162306a36Sopenharmony_ci dp83869->clk_output_sel << 82262306a36Sopenharmony_ci DP83869_IO_MUX_CFG_CLK_O_SEL_SHIFT); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci if (phy_interface_is_rgmii(phydev)) { 82562306a36Sopenharmony_ci ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIIDCTL, 82662306a36Sopenharmony_ci dp83869->rx_int_delay | 82762306a36Sopenharmony_ci dp83869->tx_int_delay << DP83869_RGMII_CLK_DELAY_SHIFT); 82862306a36Sopenharmony_ci if (ret) 82962306a36Sopenharmony_ci return ret; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIICTL); 83262306a36Sopenharmony_ci val |= (DP83869_RGMII_TX_CLK_DELAY_EN | 83362306a36Sopenharmony_ci DP83869_RGMII_RX_CLK_DELAY_EN); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) 83662306a36Sopenharmony_ci val &= ~(DP83869_RGMII_TX_CLK_DELAY_EN | 83762306a36Sopenharmony_ci DP83869_RGMII_RX_CLK_DELAY_EN); 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) 84062306a36Sopenharmony_ci val &= ~DP83869_RGMII_TX_CLK_DELAY_EN; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) 84362306a36Sopenharmony_ci val &= ~DP83869_RGMII_RX_CLK_DELAY_EN; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIICTL, 84662306a36Sopenharmony_ci val); 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci return ret; 85062306a36Sopenharmony_ci} 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_cistatic int dp83869_probe(struct phy_device *phydev) 85362306a36Sopenharmony_ci{ 85462306a36Sopenharmony_ci struct dp83869_private *dp83869; 85562306a36Sopenharmony_ci int ret; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci dp83869 = devm_kzalloc(&phydev->mdio.dev, sizeof(*dp83869), 85862306a36Sopenharmony_ci GFP_KERNEL); 85962306a36Sopenharmony_ci if (!dp83869) 86062306a36Sopenharmony_ci return -ENOMEM; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci phydev->priv = dp83869; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci ret = dp83869_of_init(phydev); 86562306a36Sopenharmony_ci if (ret) 86662306a36Sopenharmony_ci return ret; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci if (dp83869->mode == DP83869_RGMII_100_BASE || 86962306a36Sopenharmony_ci dp83869->mode == DP83869_RGMII_1000_BASE) 87062306a36Sopenharmony_ci phydev->port = PORT_FIBRE; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci return dp83869_config_init(phydev); 87362306a36Sopenharmony_ci} 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cistatic int dp83869_phy_reset(struct phy_device *phydev) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci int ret; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci ret = phy_write(phydev, DP83869_CTRL, DP83869_SW_RESET); 88062306a36Sopenharmony_ci if (ret < 0) 88162306a36Sopenharmony_ci return ret; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci usleep_range(10, 20); 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci /* Global sw reset sets all registers to default. 88662306a36Sopenharmony_ci * Need to set the registers in the PHY to the right config. 88762306a36Sopenharmony_ci */ 88862306a36Sopenharmony_ci return dp83869_config_init(phydev); 88962306a36Sopenharmony_ci} 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci#define DP83869_PHY_DRIVER(_id, _name) \ 89362306a36Sopenharmony_ci{ \ 89462306a36Sopenharmony_ci PHY_ID_MATCH_MODEL(_id), \ 89562306a36Sopenharmony_ci .name = (_name), \ 89662306a36Sopenharmony_ci .probe = dp83869_probe, \ 89762306a36Sopenharmony_ci .config_init = dp83869_config_init, \ 89862306a36Sopenharmony_ci .soft_reset = dp83869_phy_reset, \ 89962306a36Sopenharmony_ci .config_intr = dp83869_config_intr, \ 90062306a36Sopenharmony_ci .handle_interrupt = dp83869_handle_interrupt, \ 90162306a36Sopenharmony_ci .read_status = dp83869_read_status, \ 90262306a36Sopenharmony_ci .get_tunable = dp83869_get_tunable, \ 90362306a36Sopenharmony_ci .set_tunable = dp83869_set_tunable, \ 90462306a36Sopenharmony_ci .get_wol = dp83869_get_wol, \ 90562306a36Sopenharmony_ci .set_wol = dp83869_set_wol, \ 90662306a36Sopenharmony_ci .suspend = genphy_suspend, \ 90762306a36Sopenharmony_ci .resume = genphy_resume, \ 90862306a36Sopenharmony_ci} 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_cistatic struct phy_driver dp83869_driver[] = { 91162306a36Sopenharmony_ci DP83869_PHY_DRIVER(DP83869_PHY_ID, "TI DP83869"), 91262306a36Sopenharmony_ci DP83869_PHY_DRIVER(DP83561_PHY_ID, "TI DP83561-SP"), 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci}; 91562306a36Sopenharmony_cimodule_phy_driver(dp83869_driver); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_cistatic struct mdio_device_id __maybe_unused dp83869_tbl[] = { 91862306a36Sopenharmony_ci { PHY_ID_MATCH_MODEL(DP83869_PHY_ID) }, 91962306a36Sopenharmony_ci { PHY_ID_MATCH_MODEL(DP83561_PHY_ID) }, 92062306a36Sopenharmony_ci { } 92162306a36Sopenharmony_ci}; 92262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(mdio, dp83869_tbl); 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ciMODULE_DESCRIPTION("Texas Instruments DP83869 PHY driver"); 92562306a36Sopenharmony_ciMODULE_AUTHOR("Dan Murphy <dmurphy@ti.com"); 92662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 927