162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Driver for the Texas Instruments DP83822, DP83825 and DP83826 PHYs. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2017 Texas Instruments Inc. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/ethtool.h> 862306a36Sopenharmony_ci#include <linux/etherdevice.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/mii.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/of.h> 1362306a36Sopenharmony_ci#include <linux/phy.h> 1462306a36Sopenharmony_ci#include <linux/netdevice.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define DP83822_PHY_ID 0x2000a240 1762306a36Sopenharmony_ci#define DP83825S_PHY_ID 0x2000a140 1862306a36Sopenharmony_ci#define DP83825I_PHY_ID 0x2000a150 1962306a36Sopenharmony_ci#define DP83825CM_PHY_ID 0x2000a160 2062306a36Sopenharmony_ci#define DP83825CS_PHY_ID 0x2000a170 2162306a36Sopenharmony_ci#define DP83826C_PHY_ID 0x2000a130 2262306a36Sopenharmony_ci#define DP83826NC_PHY_ID 0x2000a110 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define DP83822_DEVADDR 0x1f 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define MII_DP83822_CTRL_2 0x0a 2762306a36Sopenharmony_ci#define MII_DP83822_PHYSTS 0x10 2862306a36Sopenharmony_ci#define MII_DP83822_PHYSCR 0x11 2962306a36Sopenharmony_ci#define MII_DP83822_MISR1 0x12 3062306a36Sopenharmony_ci#define MII_DP83822_MISR2 0x13 3162306a36Sopenharmony_ci#define MII_DP83822_FCSCR 0x14 3262306a36Sopenharmony_ci#define MII_DP83822_RCSR 0x17 3362306a36Sopenharmony_ci#define MII_DP83822_RESET_CTRL 0x1f 3462306a36Sopenharmony_ci#define MII_DP83822_GENCFG 0x465 3562306a36Sopenharmony_ci#define MII_DP83822_SOR1 0x467 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* GENCFG */ 3862306a36Sopenharmony_ci#define DP83822_SIG_DET_LOW BIT(0) 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* Control Register 2 bits */ 4162306a36Sopenharmony_ci#define DP83822_FX_ENABLE BIT(14) 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define DP83822_HW_RESET BIT(15) 4462306a36Sopenharmony_ci#define DP83822_SW_RESET BIT(14) 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* PHY STS bits */ 4762306a36Sopenharmony_ci#define DP83822_PHYSTS_DUPLEX BIT(2) 4862306a36Sopenharmony_ci#define DP83822_PHYSTS_10 BIT(1) 4962306a36Sopenharmony_ci#define DP83822_PHYSTS_LINK BIT(0) 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* PHYSCR Register Fields */ 5262306a36Sopenharmony_ci#define DP83822_PHYSCR_INT_OE BIT(0) /* Interrupt Output Enable */ 5362306a36Sopenharmony_ci#define DP83822_PHYSCR_INTEN BIT(1) /* Interrupt Enable */ 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* MISR1 bits */ 5662306a36Sopenharmony_ci#define DP83822_RX_ERR_HF_INT_EN BIT(0) 5762306a36Sopenharmony_ci#define DP83822_FALSE_CARRIER_HF_INT_EN BIT(1) 5862306a36Sopenharmony_ci#define DP83822_ANEG_COMPLETE_INT_EN BIT(2) 5962306a36Sopenharmony_ci#define DP83822_DUP_MODE_CHANGE_INT_EN BIT(3) 6062306a36Sopenharmony_ci#define DP83822_SPEED_CHANGED_INT_EN BIT(4) 6162306a36Sopenharmony_ci#define DP83822_LINK_STAT_INT_EN BIT(5) 6262306a36Sopenharmony_ci#define DP83822_ENERGY_DET_INT_EN BIT(6) 6362306a36Sopenharmony_ci#define DP83822_LINK_QUAL_INT_EN BIT(7) 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* MISR2 bits */ 6662306a36Sopenharmony_ci#define DP83822_JABBER_DET_INT_EN BIT(0) 6762306a36Sopenharmony_ci#define DP83822_WOL_PKT_INT_EN BIT(1) 6862306a36Sopenharmony_ci#define DP83822_SLEEP_MODE_INT_EN BIT(2) 6962306a36Sopenharmony_ci#define DP83822_MDI_XOVER_INT_EN BIT(3) 7062306a36Sopenharmony_ci#define DP83822_LB_FIFO_INT_EN BIT(4) 7162306a36Sopenharmony_ci#define DP83822_PAGE_RX_INT_EN BIT(5) 7262306a36Sopenharmony_ci#define DP83822_ANEG_ERR_INT_EN BIT(6) 7362306a36Sopenharmony_ci#define DP83822_EEE_ERROR_CHANGE_INT_EN BIT(7) 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/* INT_STAT1 bits */ 7662306a36Sopenharmony_ci#define DP83822_WOL_INT_EN BIT(4) 7762306a36Sopenharmony_ci#define DP83822_WOL_INT_STAT BIT(12) 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#define MII_DP83822_RXSOP1 0x04a5 8062306a36Sopenharmony_ci#define MII_DP83822_RXSOP2 0x04a6 8162306a36Sopenharmony_ci#define MII_DP83822_RXSOP3 0x04a7 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/* WoL Registers */ 8462306a36Sopenharmony_ci#define MII_DP83822_WOL_CFG 0x04a0 8562306a36Sopenharmony_ci#define MII_DP83822_WOL_STAT 0x04a1 8662306a36Sopenharmony_ci#define MII_DP83822_WOL_DA1 0x04a2 8762306a36Sopenharmony_ci#define MII_DP83822_WOL_DA2 0x04a3 8862306a36Sopenharmony_ci#define MII_DP83822_WOL_DA3 0x04a4 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* WoL bits */ 9162306a36Sopenharmony_ci#define DP83822_WOL_MAGIC_EN BIT(0) 9262306a36Sopenharmony_ci#define DP83822_WOL_SECURE_ON BIT(5) 9362306a36Sopenharmony_ci#define DP83822_WOL_EN BIT(7) 9462306a36Sopenharmony_ci#define DP83822_WOL_INDICATION_SEL BIT(8) 9562306a36Sopenharmony_ci#define DP83822_WOL_CLR_INDICATION BIT(11) 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* RCSR bits */ 9862306a36Sopenharmony_ci#define DP83822_RGMII_MODE_EN BIT(9) 9962306a36Sopenharmony_ci#define DP83822_RX_CLK_SHIFT BIT(12) 10062306a36Sopenharmony_ci#define DP83822_TX_CLK_SHIFT BIT(11) 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* SOR1 mode */ 10362306a36Sopenharmony_ci#define DP83822_STRAP_MODE1 0 10462306a36Sopenharmony_ci#define DP83822_STRAP_MODE2 BIT(0) 10562306a36Sopenharmony_ci#define DP83822_STRAP_MODE3 BIT(1) 10662306a36Sopenharmony_ci#define DP83822_STRAP_MODE4 GENMASK(1, 0) 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci#define DP83822_COL_STRAP_MASK GENMASK(11, 10) 10962306a36Sopenharmony_ci#define DP83822_COL_SHIFT 10 11062306a36Sopenharmony_ci#define DP83822_RX_ER_STR_MASK GENMASK(9, 8) 11162306a36Sopenharmony_ci#define DP83822_RX_ER_SHIFT 8 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci#define MII_DP83822_FIBER_ADVERTISE (ADVERTISED_TP | ADVERTISED_MII | \ 11462306a36Sopenharmony_ci ADVERTISED_FIBRE | \ 11562306a36Sopenharmony_ci ADVERTISED_Pause | ADVERTISED_Asym_Pause) 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistruct dp83822_private { 11862306a36Sopenharmony_ci bool fx_signal_det_low; 11962306a36Sopenharmony_ci int fx_enabled; 12062306a36Sopenharmony_ci u16 fx_sd_enable; 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic int dp83822_set_wol(struct phy_device *phydev, 12462306a36Sopenharmony_ci struct ethtool_wolinfo *wol) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct net_device *ndev = phydev->attached_dev; 12762306a36Sopenharmony_ci u16 value; 12862306a36Sopenharmony_ci const u8 *mac; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (wol->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) { 13162306a36Sopenharmony_ci mac = (const u8 *)ndev->dev_addr; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (!is_valid_ether_addr(mac)) 13462306a36Sopenharmony_ci return -EINVAL; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* MAC addresses start with byte 5, but stored in mac[0]. 13762306a36Sopenharmony_ci * 822 PHYs store bytes 4|5, 2|3, 0|1 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_ci phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_DA1, 14062306a36Sopenharmony_ci (mac[1] << 8) | mac[0]); 14162306a36Sopenharmony_ci phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_DA2, 14262306a36Sopenharmony_ci (mac[3] << 8) | mac[2]); 14362306a36Sopenharmony_ci phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_DA3, 14462306a36Sopenharmony_ci (mac[5] << 8) | mac[4]); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci value = phy_read_mmd(phydev, DP83822_DEVADDR, 14762306a36Sopenharmony_ci MII_DP83822_WOL_CFG); 14862306a36Sopenharmony_ci if (wol->wolopts & WAKE_MAGIC) 14962306a36Sopenharmony_ci value |= DP83822_WOL_MAGIC_EN; 15062306a36Sopenharmony_ci else 15162306a36Sopenharmony_ci value &= ~DP83822_WOL_MAGIC_EN; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (wol->wolopts & WAKE_MAGICSECURE) { 15462306a36Sopenharmony_ci phy_write_mmd(phydev, DP83822_DEVADDR, 15562306a36Sopenharmony_ci MII_DP83822_RXSOP1, 15662306a36Sopenharmony_ci (wol->sopass[1] << 8) | wol->sopass[0]); 15762306a36Sopenharmony_ci phy_write_mmd(phydev, DP83822_DEVADDR, 15862306a36Sopenharmony_ci MII_DP83822_RXSOP2, 15962306a36Sopenharmony_ci (wol->sopass[3] << 8) | wol->sopass[2]); 16062306a36Sopenharmony_ci phy_write_mmd(phydev, DP83822_DEVADDR, 16162306a36Sopenharmony_ci MII_DP83822_RXSOP3, 16262306a36Sopenharmony_ci (wol->sopass[5] << 8) | wol->sopass[4]); 16362306a36Sopenharmony_ci value |= DP83822_WOL_SECURE_ON; 16462306a36Sopenharmony_ci } else { 16562306a36Sopenharmony_ci value &= ~DP83822_WOL_SECURE_ON; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* Clear any pending WoL interrupt */ 16962306a36Sopenharmony_ci phy_read(phydev, MII_DP83822_MISR2); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci value |= DP83822_WOL_EN | DP83822_WOL_INDICATION_SEL | 17262306a36Sopenharmony_ci DP83822_WOL_CLR_INDICATION; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci return phy_write_mmd(phydev, DP83822_DEVADDR, 17562306a36Sopenharmony_ci MII_DP83822_WOL_CFG, value); 17662306a36Sopenharmony_ci } else { 17762306a36Sopenharmony_ci return phy_clear_bits_mmd(phydev, DP83822_DEVADDR, 17862306a36Sopenharmony_ci MII_DP83822_WOL_CFG, DP83822_WOL_EN); 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic void dp83822_get_wol(struct phy_device *phydev, 18362306a36Sopenharmony_ci struct ethtool_wolinfo *wol) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci int value; 18662306a36Sopenharmony_ci u16 sopass_val; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci wol->supported = (WAKE_MAGIC | WAKE_MAGICSECURE); 18962306a36Sopenharmony_ci wol->wolopts = 0; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (value & DP83822_WOL_MAGIC_EN) 19462306a36Sopenharmony_ci wol->wolopts |= WAKE_MAGIC; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (value & DP83822_WOL_SECURE_ON) { 19762306a36Sopenharmony_ci sopass_val = phy_read_mmd(phydev, DP83822_DEVADDR, 19862306a36Sopenharmony_ci MII_DP83822_RXSOP1); 19962306a36Sopenharmony_ci wol->sopass[0] = (sopass_val & 0xff); 20062306a36Sopenharmony_ci wol->sopass[1] = (sopass_val >> 8); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci sopass_val = phy_read_mmd(phydev, DP83822_DEVADDR, 20362306a36Sopenharmony_ci MII_DP83822_RXSOP2); 20462306a36Sopenharmony_ci wol->sopass[2] = (sopass_val & 0xff); 20562306a36Sopenharmony_ci wol->sopass[3] = (sopass_val >> 8); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci sopass_val = phy_read_mmd(phydev, DP83822_DEVADDR, 20862306a36Sopenharmony_ci MII_DP83822_RXSOP3); 20962306a36Sopenharmony_ci wol->sopass[4] = (sopass_val & 0xff); 21062306a36Sopenharmony_ci wol->sopass[5] = (sopass_val >> 8); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci wol->wolopts |= WAKE_MAGICSECURE; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* WoL is not enabled so set wolopts to 0 */ 21662306a36Sopenharmony_ci if (!(value & DP83822_WOL_EN)) 21762306a36Sopenharmony_ci wol->wolopts = 0; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic int dp83822_config_intr(struct phy_device *phydev) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci struct dp83822_private *dp83822 = phydev->priv; 22362306a36Sopenharmony_ci int misr_status; 22462306a36Sopenharmony_ci int physcr_status; 22562306a36Sopenharmony_ci int err; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 22862306a36Sopenharmony_ci misr_status = phy_read(phydev, MII_DP83822_MISR1); 22962306a36Sopenharmony_ci if (misr_status < 0) 23062306a36Sopenharmony_ci return misr_status; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci misr_status |= (DP83822_LINK_STAT_INT_EN | 23362306a36Sopenharmony_ci DP83822_ENERGY_DET_INT_EN | 23462306a36Sopenharmony_ci DP83822_LINK_QUAL_INT_EN); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci /* Private data pointer is NULL on DP83825/26 */ 23762306a36Sopenharmony_ci if (!dp83822 || !dp83822->fx_enabled) 23862306a36Sopenharmony_ci misr_status |= DP83822_ANEG_COMPLETE_INT_EN | 23962306a36Sopenharmony_ci DP83822_DUP_MODE_CHANGE_INT_EN | 24062306a36Sopenharmony_ci DP83822_SPEED_CHANGED_INT_EN; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci err = phy_write(phydev, MII_DP83822_MISR1, misr_status); 24462306a36Sopenharmony_ci if (err < 0) 24562306a36Sopenharmony_ci return err; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci misr_status = phy_read(phydev, MII_DP83822_MISR2); 24862306a36Sopenharmony_ci if (misr_status < 0) 24962306a36Sopenharmony_ci return misr_status; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci misr_status |= (DP83822_JABBER_DET_INT_EN | 25262306a36Sopenharmony_ci DP83822_SLEEP_MODE_INT_EN | 25362306a36Sopenharmony_ci DP83822_LB_FIFO_INT_EN | 25462306a36Sopenharmony_ci DP83822_PAGE_RX_INT_EN | 25562306a36Sopenharmony_ci DP83822_EEE_ERROR_CHANGE_INT_EN); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* Private data pointer is NULL on DP83825/26 */ 25862306a36Sopenharmony_ci if (!dp83822 || !dp83822->fx_enabled) 25962306a36Sopenharmony_ci misr_status |= DP83822_ANEG_ERR_INT_EN | 26062306a36Sopenharmony_ci DP83822_WOL_PKT_INT_EN; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci err = phy_write(phydev, MII_DP83822_MISR2, misr_status); 26362306a36Sopenharmony_ci if (err < 0) 26462306a36Sopenharmony_ci return err; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci physcr_status = phy_read(phydev, MII_DP83822_PHYSCR); 26762306a36Sopenharmony_ci if (physcr_status < 0) 26862306a36Sopenharmony_ci return physcr_status; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci physcr_status |= DP83822_PHYSCR_INT_OE | DP83822_PHYSCR_INTEN; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci } else { 27362306a36Sopenharmony_ci err = phy_write(phydev, MII_DP83822_MISR1, 0); 27462306a36Sopenharmony_ci if (err < 0) 27562306a36Sopenharmony_ci return err; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci err = phy_write(phydev, MII_DP83822_MISR2, 0); 27862306a36Sopenharmony_ci if (err < 0) 27962306a36Sopenharmony_ci return err; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci physcr_status = phy_read(phydev, MII_DP83822_PHYSCR); 28262306a36Sopenharmony_ci if (physcr_status < 0) 28362306a36Sopenharmony_ci return physcr_status; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci physcr_status &= ~DP83822_PHYSCR_INTEN; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci return phy_write(phydev, MII_DP83822_PHYSCR, physcr_status); 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic irqreturn_t dp83822_handle_interrupt(struct phy_device *phydev) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci bool trigger_machine = false; 29462306a36Sopenharmony_ci int irq_status; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* The MISR1 and MISR2 registers are holding the interrupt status in 29762306a36Sopenharmony_ci * the upper half (15:8), while the lower half (7:0) is used for 29862306a36Sopenharmony_ci * controlling the interrupt enable state of those individual interrupt 29962306a36Sopenharmony_ci * sources. To determine the possible interrupt sources, just read the 30062306a36Sopenharmony_ci * MISR* register and use it directly to know which interrupts have 30162306a36Sopenharmony_ci * been enabled previously or not. 30262306a36Sopenharmony_ci */ 30362306a36Sopenharmony_ci irq_status = phy_read(phydev, MII_DP83822_MISR1); 30462306a36Sopenharmony_ci if (irq_status < 0) { 30562306a36Sopenharmony_ci phy_error(phydev); 30662306a36Sopenharmony_ci return IRQ_NONE; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci if (irq_status & ((irq_status & GENMASK(7, 0)) << 8)) 30962306a36Sopenharmony_ci trigger_machine = true; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci irq_status = phy_read(phydev, MII_DP83822_MISR2); 31262306a36Sopenharmony_ci if (irq_status < 0) { 31362306a36Sopenharmony_ci phy_error(phydev); 31462306a36Sopenharmony_ci return IRQ_NONE; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci if (irq_status & ((irq_status & GENMASK(7, 0)) << 8)) 31762306a36Sopenharmony_ci trigger_machine = true; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (!trigger_machine) 32062306a36Sopenharmony_ci return IRQ_NONE; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci phy_trigger_machine(phydev); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci return IRQ_HANDLED; 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic int dp8382x_disable_wol(struct phy_device *phydev) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci return phy_clear_bits_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG, 33062306a36Sopenharmony_ci DP83822_WOL_EN | DP83822_WOL_MAGIC_EN | 33162306a36Sopenharmony_ci DP83822_WOL_SECURE_ON); 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic int dp83822_read_status(struct phy_device *phydev) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct dp83822_private *dp83822 = phydev->priv; 33762306a36Sopenharmony_ci int status = phy_read(phydev, MII_DP83822_PHYSTS); 33862306a36Sopenharmony_ci int ctrl2; 33962306a36Sopenharmony_ci int ret; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (dp83822->fx_enabled) { 34262306a36Sopenharmony_ci if (status & DP83822_PHYSTS_LINK) { 34362306a36Sopenharmony_ci phydev->speed = SPEED_UNKNOWN; 34462306a36Sopenharmony_ci phydev->duplex = DUPLEX_UNKNOWN; 34562306a36Sopenharmony_ci } else { 34662306a36Sopenharmony_ci ctrl2 = phy_read(phydev, MII_DP83822_CTRL_2); 34762306a36Sopenharmony_ci if (ctrl2 < 0) 34862306a36Sopenharmony_ci return ctrl2; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (!(ctrl2 & DP83822_FX_ENABLE)) { 35162306a36Sopenharmony_ci ret = phy_write(phydev, MII_DP83822_CTRL_2, 35262306a36Sopenharmony_ci DP83822_FX_ENABLE | ctrl2); 35362306a36Sopenharmony_ci if (ret < 0) 35462306a36Sopenharmony_ci return ret; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci ret = genphy_read_status(phydev); 36062306a36Sopenharmony_ci if (ret) 36162306a36Sopenharmony_ci return ret; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (status < 0) 36462306a36Sopenharmony_ci return status; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (status & DP83822_PHYSTS_DUPLEX) 36762306a36Sopenharmony_ci phydev->duplex = DUPLEX_FULL; 36862306a36Sopenharmony_ci else 36962306a36Sopenharmony_ci phydev->duplex = DUPLEX_HALF; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (status & DP83822_PHYSTS_10) 37262306a36Sopenharmony_ci phydev->speed = SPEED_10; 37362306a36Sopenharmony_ci else 37462306a36Sopenharmony_ci phydev->speed = SPEED_100; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci return 0; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic int dp83822_config_init(struct phy_device *phydev) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci struct dp83822_private *dp83822 = phydev->priv; 38262306a36Sopenharmony_ci struct device *dev = &phydev->mdio.dev; 38362306a36Sopenharmony_ci int rgmii_delay = 0; 38462306a36Sopenharmony_ci s32 rx_int_delay; 38562306a36Sopenharmony_ci s32 tx_int_delay; 38662306a36Sopenharmony_ci int err = 0; 38762306a36Sopenharmony_ci int bmcr; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (phy_interface_is_rgmii(phydev)) { 39062306a36Sopenharmony_ci rx_int_delay = phy_get_internal_delay(phydev, dev, NULL, 0, 39162306a36Sopenharmony_ci true); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci /* Set DP83822_RX_CLK_SHIFT to enable rx clk internal delay */ 39462306a36Sopenharmony_ci if (rx_int_delay > 0) 39562306a36Sopenharmony_ci rgmii_delay |= DP83822_RX_CLK_SHIFT; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci tx_int_delay = phy_get_internal_delay(phydev, dev, NULL, 0, 39862306a36Sopenharmony_ci false); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci /* Set DP83822_TX_CLK_SHIFT to disable tx clk internal delay */ 40162306a36Sopenharmony_ci if (tx_int_delay <= 0) 40262306a36Sopenharmony_ci rgmii_delay |= DP83822_TX_CLK_SHIFT; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci err = phy_modify_mmd(phydev, DP83822_DEVADDR, MII_DP83822_RCSR, 40562306a36Sopenharmony_ci DP83822_RX_CLK_SHIFT | DP83822_TX_CLK_SHIFT, rgmii_delay); 40662306a36Sopenharmony_ci if (err) 40762306a36Sopenharmony_ci return err; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci err = phy_set_bits_mmd(phydev, DP83822_DEVADDR, 41062306a36Sopenharmony_ci MII_DP83822_RCSR, DP83822_RGMII_MODE_EN); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (err) 41362306a36Sopenharmony_ci return err; 41462306a36Sopenharmony_ci } else { 41562306a36Sopenharmony_ci err = phy_clear_bits_mmd(phydev, DP83822_DEVADDR, 41662306a36Sopenharmony_ci MII_DP83822_RCSR, DP83822_RGMII_MODE_EN); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (err) 41962306a36Sopenharmony_ci return err; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci if (dp83822->fx_enabled) { 42362306a36Sopenharmony_ci err = phy_modify(phydev, MII_DP83822_CTRL_2, 42462306a36Sopenharmony_ci DP83822_FX_ENABLE, 1); 42562306a36Sopenharmony_ci if (err < 0) 42662306a36Sopenharmony_ci return err; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci /* Only allow advertising what this PHY supports */ 42962306a36Sopenharmony_ci linkmode_and(phydev->advertising, phydev->advertising, 43062306a36Sopenharmony_ci phydev->supported); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, 43362306a36Sopenharmony_ci phydev->supported); 43462306a36Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, 43562306a36Sopenharmony_ci phydev->advertising); 43662306a36Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_100baseFX_Full_BIT, 43762306a36Sopenharmony_ci phydev->supported); 43862306a36Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_100baseFX_Half_BIT, 43962306a36Sopenharmony_ci phydev->supported); 44062306a36Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_100baseFX_Full_BIT, 44162306a36Sopenharmony_ci phydev->advertising); 44262306a36Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_100baseFX_Half_BIT, 44362306a36Sopenharmony_ci phydev->advertising); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* Auto neg is not supported in fiber mode */ 44662306a36Sopenharmony_ci bmcr = phy_read(phydev, MII_BMCR); 44762306a36Sopenharmony_ci if (bmcr < 0) 44862306a36Sopenharmony_ci return bmcr; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (bmcr & BMCR_ANENABLE) { 45162306a36Sopenharmony_ci err = phy_modify(phydev, MII_BMCR, BMCR_ANENABLE, 0); 45262306a36Sopenharmony_ci if (err < 0) 45362306a36Sopenharmony_ci return err; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci phydev->autoneg = AUTONEG_DISABLE; 45662306a36Sopenharmony_ci linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, 45762306a36Sopenharmony_ci phydev->supported); 45862306a36Sopenharmony_ci linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, 45962306a36Sopenharmony_ci phydev->advertising); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* Setup fiber advertisement */ 46262306a36Sopenharmony_ci err = phy_modify_changed(phydev, MII_ADVERTISE, 46362306a36Sopenharmony_ci MII_DP83822_FIBER_ADVERTISE, 46462306a36Sopenharmony_ci MII_DP83822_FIBER_ADVERTISE); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (err < 0) 46762306a36Sopenharmony_ci return err; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (dp83822->fx_signal_det_low) { 47062306a36Sopenharmony_ci err = phy_set_bits_mmd(phydev, DP83822_DEVADDR, 47162306a36Sopenharmony_ci MII_DP83822_GENCFG, 47262306a36Sopenharmony_ci DP83822_SIG_DET_LOW); 47362306a36Sopenharmony_ci if (err) 47462306a36Sopenharmony_ci return err; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci return dp8382x_disable_wol(phydev); 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic int dp8382x_config_init(struct phy_device *phydev) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci return dp8382x_disable_wol(phydev); 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic int dp83822_phy_reset(struct phy_device *phydev) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci int err; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci err = phy_write(phydev, MII_DP83822_RESET_CTRL, DP83822_SW_RESET); 49062306a36Sopenharmony_ci if (err < 0) 49162306a36Sopenharmony_ci return err; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci return phydev->drv->config_init(phydev); 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci#ifdef CONFIG_OF_MDIO 49762306a36Sopenharmony_cistatic int dp83822_of_init(struct phy_device *phydev) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci struct dp83822_private *dp83822 = phydev->priv; 50062306a36Sopenharmony_ci struct device *dev = &phydev->mdio.dev; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci /* Signal detection for the PHY is only enabled if the FX_EN and the 50362306a36Sopenharmony_ci * SD_EN pins are strapped. Signal detection can only enabled if FX_EN 50462306a36Sopenharmony_ci * is strapped otherwise signal detection is disabled for the PHY. 50562306a36Sopenharmony_ci */ 50662306a36Sopenharmony_ci if (dp83822->fx_enabled && dp83822->fx_sd_enable) 50762306a36Sopenharmony_ci dp83822->fx_signal_det_low = device_property_present(dev, 50862306a36Sopenharmony_ci "ti,link-loss-low"); 50962306a36Sopenharmony_ci if (!dp83822->fx_enabled) 51062306a36Sopenharmony_ci dp83822->fx_enabled = device_property_present(dev, 51162306a36Sopenharmony_ci "ti,fiber-mode"); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci return 0; 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci#else 51662306a36Sopenharmony_cistatic int dp83822_of_init(struct phy_device *phydev) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci return 0; 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci#endif /* CONFIG_OF_MDIO */ 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic int dp83822_read_straps(struct phy_device *phydev) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci struct dp83822_private *dp83822 = phydev->priv; 52562306a36Sopenharmony_ci int fx_enabled, fx_sd_enable; 52662306a36Sopenharmony_ci int val; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci val = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_SOR1); 52962306a36Sopenharmony_ci if (val < 0) 53062306a36Sopenharmony_ci return val; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci phydev_dbg(phydev, "SOR1 strap register: 0x%04x\n", val); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci fx_enabled = (val & DP83822_COL_STRAP_MASK) >> DP83822_COL_SHIFT; 53562306a36Sopenharmony_ci if (fx_enabled == DP83822_STRAP_MODE2 || 53662306a36Sopenharmony_ci fx_enabled == DP83822_STRAP_MODE3) 53762306a36Sopenharmony_ci dp83822->fx_enabled = 1; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (dp83822->fx_enabled) { 54062306a36Sopenharmony_ci fx_sd_enable = (val & DP83822_RX_ER_STR_MASK) >> DP83822_RX_ER_SHIFT; 54162306a36Sopenharmony_ci if (fx_sd_enable == DP83822_STRAP_MODE3 || 54262306a36Sopenharmony_ci fx_sd_enable == DP83822_STRAP_MODE4) 54362306a36Sopenharmony_ci dp83822->fx_sd_enable = 1; 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci return 0; 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_cistatic int dp83822_probe(struct phy_device *phydev) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci struct dp83822_private *dp83822; 55262306a36Sopenharmony_ci int ret; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci dp83822 = devm_kzalloc(&phydev->mdio.dev, sizeof(*dp83822), 55562306a36Sopenharmony_ci GFP_KERNEL); 55662306a36Sopenharmony_ci if (!dp83822) 55762306a36Sopenharmony_ci return -ENOMEM; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci phydev->priv = dp83822; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci ret = dp83822_read_straps(phydev); 56262306a36Sopenharmony_ci if (ret) 56362306a36Sopenharmony_ci return ret; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci dp83822_of_init(phydev); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci if (dp83822->fx_enabled) 56862306a36Sopenharmony_ci phydev->port = PORT_FIBRE; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci return 0; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic int dp83822_suspend(struct phy_device *phydev) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci int value; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (!(value & DP83822_WOL_EN)) 58062306a36Sopenharmony_ci genphy_suspend(phydev); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci return 0; 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic int dp83822_resume(struct phy_device *phydev) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci int value; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci genphy_resume(phydev); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG, value | 59462306a36Sopenharmony_ci DP83822_WOL_CLR_INDICATION); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci return 0; 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci#define DP83822_PHY_DRIVER(_id, _name) \ 60062306a36Sopenharmony_ci { \ 60162306a36Sopenharmony_ci PHY_ID_MATCH_MODEL(_id), \ 60262306a36Sopenharmony_ci .name = (_name), \ 60362306a36Sopenharmony_ci /* PHY_BASIC_FEATURES */ \ 60462306a36Sopenharmony_ci .probe = dp83822_probe, \ 60562306a36Sopenharmony_ci .soft_reset = dp83822_phy_reset, \ 60662306a36Sopenharmony_ci .config_init = dp83822_config_init, \ 60762306a36Sopenharmony_ci .read_status = dp83822_read_status, \ 60862306a36Sopenharmony_ci .get_wol = dp83822_get_wol, \ 60962306a36Sopenharmony_ci .set_wol = dp83822_set_wol, \ 61062306a36Sopenharmony_ci .config_intr = dp83822_config_intr, \ 61162306a36Sopenharmony_ci .handle_interrupt = dp83822_handle_interrupt, \ 61262306a36Sopenharmony_ci .suspend = dp83822_suspend, \ 61362306a36Sopenharmony_ci .resume = dp83822_resume, \ 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci#define DP8382X_PHY_DRIVER(_id, _name) \ 61762306a36Sopenharmony_ci { \ 61862306a36Sopenharmony_ci PHY_ID_MATCH_MODEL(_id), \ 61962306a36Sopenharmony_ci .name = (_name), \ 62062306a36Sopenharmony_ci /* PHY_BASIC_FEATURES */ \ 62162306a36Sopenharmony_ci .soft_reset = dp83822_phy_reset, \ 62262306a36Sopenharmony_ci .config_init = dp8382x_config_init, \ 62362306a36Sopenharmony_ci .get_wol = dp83822_get_wol, \ 62462306a36Sopenharmony_ci .set_wol = dp83822_set_wol, \ 62562306a36Sopenharmony_ci .config_intr = dp83822_config_intr, \ 62662306a36Sopenharmony_ci .handle_interrupt = dp83822_handle_interrupt, \ 62762306a36Sopenharmony_ci .suspend = dp83822_suspend, \ 62862306a36Sopenharmony_ci .resume = dp83822_resume, \ 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cistatic struct phy_driver dp83822_driver[] = { 63262306a36Sopenharmony_ci DP83822_PHY_DRIVER(DP83822_PHY_ID, "TI DP83822"), 63362306a36Sopenharmony_ci DP8382X_PHY_DRIVER(DP83825I_PHY_ID, "TI DP83825I"), 63462306a36Sopenharmony_ci DP8382X_PHY_DRIVER(DP83826C_PHY_ID, "TI DP83826C"), 63562306a36Sopenharmony_ci DP8382X_PHY_DRIVER(DP83826NC_PHY_ID, "TI DP83826NC"), 63662306a36Sopenharmony_ci DP8382X_PHY_DRIVER(DP83825S_PHY_ID, "TI DP83825S"), 63762306a36Sopenharmony_ci DP8382X_PHY_DRIVER(DP83825CM_PHY_ID, "TI DP83825M"), 63862306a36Sopenharmony_ci DP8382X_PHY_DRIVER(DP83825CS_PHY_ID, "TI DP83825CS"), 63962306a36Sopenharmony_ci}; 64062306a36Sopenharmony_cimodule_phy_driver(dp83822_driver); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_cistatic struct mdio_device_id __maybe_unused dp83822_tbl[] = { 64362306a36Sopenharmony_ci { DP83822_PHY_ID, 0xfffffff0 }, 64462306a36Sopenharmony_ci { DP83825I_PHY_ID, 0xfffffff0 }, 64562306a36Sopenharmony_ci { DP83826C_PHY_ID, 0xfffffff0 }, 64662306a36Sopenharmony_ci { DP83826NC_PHY_ID, 0xfffffff0 }, 64762306a36Sopenharmony_ci { DP83825S_PHY_ID, 0xfffffff0 }, 64862306a36Sopenharmony_ci { DP83825CM_PHY_ID, 0xfffffff0 }, 64962306a36Sopenharmony_ci { DP83825CS_PHY_ID, 0xfffffff0 }, 65062306a36Sopenharmony_ci { }, 65162306a36Sopenharmony_ci}; 65262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(mdio, dp83822_tbl); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ciMODULE_DESCRIPTION("Texas Instruments DP83822 PHY driver"); 65562306a36Sopenharmony_ciMODULE_AUTHOR("Dan Murphy <dmurphy@ti.com"); 65662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 657