162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * drivers/net/phy/smsc.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Driver for SMSC PHYs 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Herbert Valerio Riedel 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Copyright (c) 2006 Herbert Valerio Riedel <hvr@gnu.org> 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Support added for SMSC LAN8187 and LAN8700 by steve.glendinning@shawell.net 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/clk.h> 1662306a36Sopenharmony_ci#include <linux/kernel.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/mii.h> 1962306a36Sopenharmony_ci#include <linux/ethtool.h> 2062306a36Sopenharmony_ci#include <linux/of.h> 2162306a36Sopenharmony_ci#include <linux/phy.h> 2262306a36Sopenharmony_ci#include <linux/netdevice.h> 2362306a36Sopenharmony_ci#include <linux/crc16.h> 2462306a36Sopenharmony_ci#include <linux/etherdevice.h> 2562306a36Sopenharmony_ci#include <linux/smscphy.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* Vendor-specific PHY Definitions */ 2862306a36Sopenharmony_ci/* EDPD NLP / crossover time configuration */ 2962306a36Sopenharmony_ci#define PHY_EDPD_CONFIG 16 3062306a36Sopenharmony_ci#define PHY_EDPD_CONFIG_EXT_CROSSOVER_ 0x0001 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* Control/Status Indication Register */ 3362306a36Sopenharmony_ci#define SPECIAL_CTRL_STS 27 3462306a36Sopenharmony_ci#define SPECIAL_CTRL_STS_OVRRD_AMDIX_ 0x8000 3562306a36Sopenharmony_ci#define SPECIAL_CTRL_STS_AMDIX_ENABLE_ 0x4000 3662306a36Sopenharmony_ci#define SPECIAL_CTRL_STS_AMDIX_STATE_ 0x2000 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define EDPD_MAX_WAIT_DFLT_MS 640 3962306a36Sopenharmony_ci/* interval between phylib state machine runs in ms */ 4062306a36Sopenharmony_ci#define PHY_STATE_MACH_MS 1000 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistruct smsc_hw_stat { 4362306a36Sopenharmony_ci const char *string; 4462306a36Sopenharmony_ci u8 reg; 4562306a36Sopenharmony_ci u8 bits; 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic struct smsc_hw_stat smsc_hw_stats[] = { 4962306a36Sopenharmony_ci { "phy_symbol_errors", 26, 16}, 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistruct smsc_phy_priv { 5362306a36Sopenharmony_ci unsigned int edpd_enable:1; 5462306a36Sopenharmony_ci unsigned int edpd_mode_set_by_user:1; 5562306a36Sopenharmony_ci unsigned int edpd_max_wait_ms; 5662306a36Sopenharmony_ci bool wol_arp; 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic int smsc_phy_ack_interrupt(struct phy_device *phydev) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci int rc = phy_read(phydev, MII_LAN83C185_ISF); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci return rc < 0 ? rc : 0; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ciint smsc_phy_config_intr(struct phy_device *phydev) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci int rc; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 7162306a36Sopenharmony_ci rc = smsc_phy_ack_interrupt(phydev); 7262306a36Sopenharmony_ci if (rc) 7362306a36Sopenharmony_ci return rc; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci rc = phy_write(phydev, MII_LAN83C185_IM, 7662306a36Sopenharmony_ci MII_LAN83C185_ISF_INT_PHYLIB_EVENTS); 7762306a36Sopenharmony_ci } else { 7862306a36Sopenharmony_ci rc = phy_write(phydev, MII_LAN83C185_IM, 0); 7962306a36Sopenharmony_ci if (rc) 8062306a36Sopenharmony_ci return rc; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci rc = smsc_phy_ack_interrupt(phydev); 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return rc < 0 ? rc : 0; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(smsc_phy_config_intr); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic int smsc_phy_config_edpd(struct phy_device *phydev) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct smsc_phy_priv *priv = phydev->priv; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (priv->edpd_enable) 9462306a36Sopenharmony_ci return phy_set_bits(phydev, MII_LAN83C185_CTRL_STATUS, 9562306a36Sopenharmony_ci MII_LAN83C185_EDPWRDOWN); 9662306a36Sopenharmony_ci else 9762306a36Sopenharmony_ci return phy_clear_bits(phydev, MII_LAN83C185_CTRL_STATUS, 9862306a36Sopenharmony_ci MII_LAN83C185_EDPWRDOWN); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ciirqreturn_t smsc_phy_handle_interrupt(struct phy_device *phydev) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci int irq_status; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci irq_status = phy_read(phydev, MII_LAN83C185_ISF); 10662306a36Sopenharmony_ci if (irq_status < 0) { 10762306a36Sopenharmony_ci if (irq_status != -ENODEV) 10862306a36Sopenharmony_ci phy_error(phydev); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci return IRQ_NONE; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (!(irq_status & MII_LAN83C185_ISF_INT_PHYLIB_EVENTS)) 11462306a36Sopenharmony_ci return IRQ_NONE; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci phy_trigger_machine(phydev); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci return IRQ_HANDLED; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(smsc_phy_handle_interrupt); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ciint smsc_phy_config_init(struct phy_device *phydev) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct smsc_phy_priv *priv = phydev->priv; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (!priv) 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* don't use EDPD in irq mode except overridden by user */ 13062306a36Sopenharmony_ci if (!priv->edpd_mode_set_by_user && phydev->irq != PHY_POLL) 13162306a36Sopenharmony_ci priv->edpd_enable = false; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci return smsc_phy_config_edpd(phydev); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(smsc_phy_config_init); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic int smsc_phy_reset(struct phy_device *phydev) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci int rc = phy_read(phydev, MII_LAN83C185_SPECIAL_MODES); 14062306a36Sopenharmony_ci if (rc < 0) 14162306a36Sopenharmony_ci return rc; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* If the SMSC PHY is in power down mode, then set it 14462306a36Sopenharmony_ci * in all capable mode before using it. 14562306a36Sopenharmony_ci */ 14662306a36Sopenharmony_ci if ((rc & MII_LAN83C185_MODE_MASK) == MII_LAN83C185_MODE_POWERDOWN) { 14762306a36Sopenharmony_ci /* set "all capable" mode */ 14862306a36Sopenharmony_ci rc |= MII_LAN83C185_MODE_ALL; 14962306a36Sopenharmony_ci phy_write(phydev, MII_LAN83C185_SPECIAL_MODES, rc); 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* reset the phy */ 15362306a36Sopenharmony_ci return genphy_soft_reset(phydev); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic int lan87xx_config_aneg(struct phy_device *phydev) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci int rc; 15962306a36Sopenharmony_ci int val; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci switch (phydev->mdix_ctrl) { 16262306a36Sopenharmony_ci case ETH_TP_MDI: 16362306a36Sopenharmony_ci val = SPECIAL_CTRL_STS_OVRRD_AMDIX_; 16462306a36Sopenharmony_ci break; 16562306a36Sopenharmony_ci case ETH_TP_MDI_X: 16662306a36Sopenharmony_ci val = SPECIAL_CTRL_STS_OVRRD_AMDIX_ | 16762306a36Sopenharmony_ci SPECIAL_CTRL_STS_AMDIX_STATE_; 16862306a36Sopenharmony_ci break; 16962306a36Sopenharmony_ci case ETH_TP_MDI_AUTO: 17062306a36Sopenharmony_ci val = SPECIAL_CTRL_STS_AMDIX_ENABLE_; 17162306a36Sopenharmony_ci break; 17262306a36Sopenharmony_ci default: 17362306a36Sopenharmony_ci return genphy_config_aneg(phydev); 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci rc = phy_read(phydev, SPECIAL_CTRL_STS); 17762306a36Sopenharmony_ci if (rc < 0) 17862306a36Sopenharmony_ci return rc; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci rc &= ~(SPECIAL_CTRL_STS_OVRRD_AMDIX_ | 18162306a36Sopenharmony_ci SPECIAL_CTRL_STS_AMDIX_ENABLE_ | 18262306a36Sopenharmony_ci SPECIAL_CTRL_STS_AMDIX_STATE_); 18362306a36Sopenharmony_ci rc |= val; 18462306a36Sopenharmony_ci phy_write(phydev, SPECIAL_CTRL_STS, rc); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci phydev->mdix = phydev->mdix_ctrl; 18762306a36Sopenharmony_ci return genphy_config_aneg(phydev); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic int lan95xx_config_aneg_ext(struct phy_device *phydev) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci if (phydev->phy_id == 0x0007c0f0) { /* LAN9500A or LAN9505A */ 19362306a36Sopenharmony_ci /* Extend Manual AutoMDIX timer */ 19462306a36Sopenharmony_ci int rc = phy_set_bits(phydev, PHY_EDPD_CONFIG, 19562306a36Sopenharmony_ci PHY_EDPD_CONFIG_EXT_CROSSOVER_); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (rc < 0) 19862306a36Sopenharmony_ci return rc; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci return lan87xx_config_aneg(phydev); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci/* 20562306a36Sopenharmony_ci * The LAN87xx suffers from rare absence of the ENERGYON-bit when Ethernet cable 20662306a36Sopenharmony_ci * plugs in while LAN87xx is in Energy Detect Power-Down mode. This leads to 20762306a36Sopenharmony_ci * unstable detection of plugging in Ethernet cable. 20862306a36Sopenharmony_ci * This workaround disables Energy Detect Power-Down mode and waiting for 20962306a36Sopenharmony_ci * response on link pulses to detect presence of plugged Ethernet cable. 21062306a36Sopenharmony_ci * The Energy Detect Power-Down mode is enabled again in the end of procedure to 21162306a36Sopenharmony_ci * save approximately 220 mW of power if cable is unplugged. 21262306a36Sopenharmony_ci * The workaround is only applicable to poll mode. Energy Detect Power-Down may 21362306a36Sopenharmony_ci * not be used in interrupt mode lest link change detection becomes unreliable. 21462306a36Sopenharmony_ci */ 21562306a36Sopenharmony_ciint lan87xx_read_status(struct phy_device *phydev) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct smsc_phy_priv *priv = phydev->priv; 21862306a36Sopenharmony_ci int err; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci err = genphy_read_status(phydev); 22162306a36Sopenharmony_ci if (err) 22262306a36Sopenharmony_ci return err; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (!phydev->link && priv && priv->edpd_enable && 22562306a36Sopenharmony_ci priv->edpd_max_wait_ms) { 22662306a36Sopenharmony_ci unsigned int max_wait = priv->edpd_max_wait_ms * 1000; 22762306a36Sopenharmony_ci int rc; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* Disable EDPD to wake up PHY */ 23062306a36Sopenharmony_ci rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); 23162306a36Sopenharmony_ci if (rc < 0) 23262306a36Sopenharmony_ci return rc; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, 23562306a36Sopenharmony_ci rc & ~MII_LAN83C185_EDPWRDOWN); 23662306a36Sopenharmony_ci if (rc < 0) 23762306a36Sopenharmony_ci return rc; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* Wait max 640 ms to detect energy and the timeout is not 24062306a36Sopenharmony_ci * an actual error. 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_ci read_poll_timeout(phy_read, rc, 24362306a36Sopenharmony_ci rc & MII_LAN83C185_ENERGYON || rc < 0, 24462306a36Sopenharmony_ci 10000, max_wait, true, phydev, 24562306a36Sopenharmony_ci MII_LAN83C185_CTRL_STATUS); 24662306a36Sopenharmony_ci if (rc < 0) 24762306a36Sopenharmony_ci return rc; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci /* Re-enable EDPD */ 25062306a36Sopenharmony_ci rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); 25162306a36Sopenharmony_ci if (rc < 0) 25262306a36Sopenharmony_ci return rc; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, 25562306a36Sopenharmony_ci rc | MII_LAN83C185_EDPWRDOWN); 25662306a36Sopenharmony_ci if (rc < 0) 25762306a36Sopenharmony_ci return rc; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci return err; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lan87xx_read_status); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic int lan874x_phy_config_init(struct phy_device *phydev) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci u16 val; 26762306a36Sopenharmony_ci int rc; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* Setup LED2/nINT/nPME pin to function as nPME. May need user option 27062306a36Sopenharmony_ci * to use LED1/nINT/nPME. 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_ci val = MII_LAN874X_PHY_PME2_SET; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* The bits MII_LAN874X_PHY_WOL_PFDA_FR, MII_LAN874X_PHY_WOL_WUFR, 27562306a36Sopenharmony_ci * MII_LAN874X_PHY_WOL_MPR, and MII_LAN874X_PHY_WOL_BCAST_FR need to 27662306a36Sopenharmony_ci * be cleared to de-assert PME signal after a WoL event happens, but 27762306a36Sopenharmony_ci * using PME auto clear gets around that. 27862306a36Sopenharmony_ci */ 27962306a36Sopenharmony_ci val |= MII_LAN874X_PHY_PME_SELF_CLEAR; 28062306a36Sopenharmony_ci rc = phy_write_mmd(phydev, MDIO_MMD_PCS, MII_LAN874X_PHY_MMD_WOL_WUCSR, 28162306a36Sopenharmony_ci val); 28262306a36Sopenharmony_ci if (rc < 0) 28362306a36Sopenharmony_ci return rc; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* set nPME self clear delay time */ 28662306a36Sopenharmony_ci rc = phy_write_mmd(phydev, MDIO_MMD_PCS, MII_LAN874X_PHY_MMD_MCFGR, 28762306a36Sopenharmony_ci MII_LAN874X_PHY_PME_SELF_CLEAR_DELAY); 28862306a36Sopenharmony_ci if (rc < 0) 28962306a36Sopenharmony_ci return rc; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci return smsc_phy_config_init(phydev); 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic void lan874x_get_wol(struct phy_device *phydev, 29562306a36Sopenharmony_ci struct ethtool_wolinfo *wol) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci struct smsc_phy_priv *priv = phydev->priv; 29862306a36Sopenharmony_ci int rc; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci wol->supported = (WAKE_UCAST | WAKE_BCAST | WAKE_MAGIC | 30162306a36Sopenharmony_ci WAKE_ARP | WAKE_MCAST); 30262306a36Sopenharmony_ci wol->wolopts = 0; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci rc = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_LAN874X_PHY_MMD_WOL_WUCSR); 30562306a36Sopenharmony_ci if (rc < 0) 30662306a36Sopenharmony_ci return; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (rc & MII_LAN874X_PHY_WOL_PFDAEN) 30962306a36Sopenharmony_ci wol->wolopts |= WAKE_UCAST; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (rc & MII_LAN874X_PHY_WOL_BCSTEN) 31262306a36Sopenharmony_ci wol->wolopts |= WAKE_BCAST; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (rc & MII_LAN874X_PHY_WOL_MPEN) 31562306a36Sopenharmony_ci wol->wolopts |= WAKE_MAGIC; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (rc & MII_LAN874X_PHY_WOL_WUEN) { 31862306a36Sopenharmony_ci if (priv->wol_arp) 31962306a36Sopenharmony_ci wol->wolopts |= WAKE_ARP; 32062306a36Sopenharmony_ci else 32162306a36Sopenharmony_ci wol->wolopts |= WAKE_MCAST; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic u16 smsc_crc16(const u8 *buffer, size_t len) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci return bitrev16(crc16(0xFFFF, buffer, len)); 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic int lan874x_chk_wol_pattern(const u8 pattern[], const u16 *mask, 33162306a36Sopenharmony_ci u8 len, u8 *data, u8 *datalen) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci size_t i, j, k; 33462306a36Sopenharmony_ci int ret = 0; 33562306a36Sopenharmony_ci u16 bits; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* Pattern filtering can match up to 128 bytes of frame data. There 33862306a36Sopenharmony_ci * are 8 registers to program the 16-bit masks, where each bit means 33962306a36Sopenharmony_ci * the byte will be compared. The frame data will then go through a 34062306a36Sopenharmony_ci * CRC16 calculation for hardware comparison. This helper function 34162306a36Sopenharmony_ci * makes sure only relevant frame data are included in this 34262306a36Sopenharmony_ci * calculation. It provides a warning when the masks and expected 34362306a36Sopenharmony_ci * data size do not match. 34462306a36Sopenharmony_ci */ 34562306a36Sopenharmony_ci i = 0; 34662306a36Sopenharmony_ci k = 0; 34762306a36Sopenharmony_ci while (len > 0) { 34862306a36Sopenharmony_ci bits = *mask; 34962306a36Sopenharmony_ci for (j = 0; j < 16; j++, i++, len--) { 35062306a36Sopenharmony_ci /* No more pattern. */ 35162306a36Sopenharmony_ci if (!len) { 35262306a36Sopenharmony_ci /* The rest of bitmap is not empty. */ 35362306a36Sopenharmony_ci if (bits) 35462306a36Sopenharmony_ci ret = i + 1; 35562306a36Sopenharmony_ci break; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci if (bits & 1) 35862306a36Sopenharmony_ci data[k++] = pattern[i]; 35962306a36Sopenharmony_ci bits >>= 1; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci mask++; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci *datalen = k; 36462306a36Sopenharmony_ci return ret; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic int lan874x_set_wol_pattern(struct phy_device *phydev, u16 val, 36862306a36Sopenharmony_ci const u8 data[], u8 datalen, 36962306a36Sopenharmony_ci const u16 *mask, u8 masklen) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci u16 crc, reg; 37262306a36Sopenharmony_ci int rc; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* Starting pattern offset is set before calling this function. */ 37562306a36Sopenharmony_ci val |= MII_LAN874X_PHY_WOL_FILTER_EN; 37662306a36Sopenharmony_ci rc = phy_write_mmd(phydev, MDIO_MMD_PCS, 37762306a36Sopenharmony_ci MII_LAN874X_PHY_MMD_WOL_WUF_CFGA, val); 37862306a36Sopenharmony_ci if (rc < 0) 37962306a36Sopenharmony_ci return rc; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci crc = smsc_crc16(data, datalen); 38262306a36Sopenharmony_ci rc = phy_write_mmd(phydev, MDIO_MMD_PCS, 38362306a36Sopenharmony_ci MII_LAN874X_PHY_MMD_WOL_WUF_CFGB, crc); 38462306a36Sopenharmony_ci if (rc < 0) 38562306a36Sopenharmony_ci return rc; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci masklen = (masklen + 15) & ~0xf; 38862306a36Sopenharmony_ci reg = MII_LAN874X_PHY_MMD_WOL_WUF_MASK7; 38962306a36Sopenharmony_ci while (masklen >= 16) { 39062306a36Sopenharmony_ci rc = phy_write_mmd(phydev, MDIO_MMD_PCS, reg, *mask); 39162306a36Sopenharmony_ci if (rc < 0) 39262306a36Sopenharmony_ci return rc; 39362306a36Sopenharmony_ci reg--; 39462306a36Sopenharmony_ci mask++; 39562306a36Sopenharmony_ci masklen -= 16; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* Clear out the rest of mask registers. */ 39962306a36Sopenharmony_ci while (reg != MII_LAN874X_PHY_MMD_WOL_WUF_MASK0) { 40062306a36Sopenharmony_ci phy_write_mmd(phydev, MDIO_MMD_PCS, reg, 0); 40162306a36Sopenharmony_ci reg--; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci return rc; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic int lan874x_set_wol(struct phy_device *phydev, 40762306a36Sopenharmony_ci struct ethtool_wolinfo *wol) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci struct net_device *ndev = phydev->attached_dev; 41062306a36Sopenharmony_ci struct smsc_phy_priv *priv = phydev->priv; 41162306a36Sopenharmony_ci u16 val, val_wucsr; 41262306a36Sopenharmony_ci u8 data[128]; 41362306a36Sopenharmony_ci u8 datalen; 41462306a36Sopenharmony_ci int rc; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci /* lan874x has only one WoL filter pattern */ 41762306a36Sopenharmony_ci if ((wol->wolopts & (WAKE_ARP | WAKE_MCAST)) == 41862306a36Sopenharmony_ci (WAKE_ARP | WAKE_MCAST)) { 41962306a36Sopenharmony_ci phydev_info(phydev, 42062306a36Sopenharmony_ci "lan874x WoL supports one of ARP|MCAST at a time\n"); 42162306a36Sopenharmony_ci return -EOPNOTSUPP; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci rc = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_LAN874X_PHY_MMD_WOL_WUCSR); 42562306a36Sopenharmony_ci if (rc < 0) 42662306a36Sopenharmony_ci return rc; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci val_wucsr = rc; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (wol->wolopts & WAKE_UCAST) 43162306a36Sopenharmony_ci val_wucsr |= MII_LAN874X_PHY_WOL_PFDAEN; 43262306a36Sopenharmony_ci else 43362306a36Sopenharmony_ci val_wucsr &= ~MII_LAN874X_PHY_WOL_PFDAEN; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (wol->wolopts & WAKE_BCAST) 43662306a36Sopenharmony_ci val_wucsr |= MII_LAN874X_PHY_WOL_BCSTEN; 43762306a36Sopenharmony_ci else 43862306a36Sopenharmony_ci val_wucsr &= ~MII_LAN874X_PHY_WOL_BCSTEN; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (wol->wolopts & WAKE_MAGIC) 44162306a36Sopenharmony_ci val_wucsr |= MII_LAN874X_PHY_WOL_MPEN; 44262306a36Sopenharmony_ci else 44362306a36Sopenharmony_ci val_wucsr &= ~MII_LAN874X_PHY_WOL_MPEN; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* Need to use pattern matching */ 44662306a36Sopenharmony_ci if (wol->wolopts & (WAKE_ARP | WAKE_MCAST)) 44762306a36Sopenharmony_ci val_wucsr |= MII_LAN874X_PHY_WOL_WUEN; 44862306a36Sopenharmony_ci else 44962306a36Sopenharmony_ci val_wucsr &= ~MII_LAN874X_PHY_WOL_WUEN; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci if (wol->wolopts & WAKE_ARP) { 45262306a36Sopenharmony_ci const u8 pattern[2] = { 0x08, 0x06 }; 45362306a36Sopenharmony_ci const u16 mask[1] = { 0x0003 }; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci rc = lan874x_chk_wol_pattern(pattern, mask, 2, data, 45662306a36Sopenharmony_ci &datalen); 45762306a36Sopenharmony_ci if (rc) 45862306a36Sopenharmony_ci phydev_dbg(phydev, "pattern not valid at %d\n", rc); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* Need to match broadcast destination address and provided 46162306a36Sopenharmony_ci * data pattern at offset 12. 46262306a36Sopenharmony_ci */ 46362306a36Sopenharmony_ci val = 12 | MII_LAN874X_PHY_WOL_FILTER_BCSTEN; 46462306a36Sopenharmony_ci rc = lan874x_set_wol_pattern(phydev, val, data, datalen, mask, 46562306a36Sopenharmony_ci 2); 46662306a36Sopenharmony_ci if (rc < 0) 46762306a36Sopenharmony_ci return rc; 46862306a36Sopenharmony_ci priv->wol_arp = true; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (wol->wolopts & WAKE_MCAST) { 47262306a36Sopenharmony_ci /* Need to match multicast destination address. */ 47362306a36Sopenharmony_ci val = MII_LAN874X_PHY_WOL_FILTER_MCASTTEN; 47462306a36Sopenharmony_ci rc = lan874x_set_wol_pattern(phydev, val, data, 0, NULL, 0); 47562306a36Sopenharmony_ci if (rc < 0) 47662306a36Sopenharmony_ci return rc; 47762306a36Sopenharmony_ci priv->wol_arp = false; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (wol->wolopts & (WAKE_MAGIC | WAKE_UCAST)) { 48162306a36Sopenharmony_ci const u8 *mac = (const u8 *)ndev->dev_addr; 48262306a36Sopenharmony_ci int i, reg; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci reg = MII_LAN874X_PHY_MMD_WOL_RX_ADDRC; 48562306a36Sopenharmony_ci for (i = 0; i < 6; i += 2, reg--) { 48662306a36Sopenharmony_ci rc = phy_write_mmd(phydev, MDIO_MMD_PCS, reg, 48762306a36Sopenharmony_ci ((mac[i + 1] << 8) | mac[i])); 48862306a36Sopenharmony_ci if (rc < 0) 48962306a36Sopenharmony_ci return rc; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci rc = phy_write_mmd(phydev, MDIO_MMD_PCS, MII_LAN874X_PHY_MMD_WOL_WUCSR, 49462306a36Sopenharmony_ci val_wucsr); 49562306a36Sopenharmony_ci if (rc < 0) 49662306a36Sopenharmony_ci return rc; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci return 0; 49962306a36Sopenharmony_ci} 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cistatic int smsc_get_sset_count(struct phy_device *phydev) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci return ARRAY_SIZE(smsc_hw_stats); 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistatic void smsc_get_strings(struct phy_device *phydev, u8 *data) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci int i; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(smsc_hw_stats); i++) { 51162306a36Sopenharmony_ci strncpy(data + i * ETH_GSTRING_LEN, 51262306a36Sopenharmony_ci smsc_hw_stats[i].string, ETH_GSTRING_LEN); 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic u64 smsc_get_stat(struct phy_device *phydev, int i) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci struct smsc_hw_stat stat = smsc_hw_stats[i]; 51962306a36Sopenharmony_ci int val; 52062306a36Sopenharmony_ci u64 ret; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci val = phy_read(phydev, stat.reg); 52362306a36Sopenharmony_ci if (val < 0) 52462306a36Sopenharmony_ci ret = U64_MAX; 52562306a36Sopenharmony_ci else 52662306a36Sopenharmony_ci ret = val; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci return ret; 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic void smsc_get_stats(struct phy_device *phydev, 53262306a36Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci int i; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(smsc_hw_stats); i++) 53762306a36Sopenharmony_ci data[i] = smsc_get_stat(phydev, i); 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cistatic int smsc_phy_get_edpd(struct phy_device *phydev, u16 *edpd) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci struct smsc_phy_priv *priv = phydev->priv; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (!priv) 54562306a36Sopenharmony_ci return -EOPNOTSUPP; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (!priv->edpd_enable) 54862306a36Sopenharmony_ci *edpd = ETHTOOL_PHY_EDPD_DISABLE; 54962306a36Sopenharmony_ci else if (!priv->edpd_max_wait_ms) 55062306a36Sopenharmony_ci *edpd = ETHTOOL_PHY_EDPD_NO_TX; 55162306a36Sopenharmony_ci else 55262306a36Sopenharmony_ci *edpd = PHY_STATE_MACH_MS + priv->edpd_max_wait_ms; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci return 0; 55562306a36Sopenharmony_ci} 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cistatic int smsc_phy_set_edpd(struct phy_device *phydev, u16 edpd) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci struct smsc_phy_priv *priv = phydev->priv; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci if (!priv) 56262306a36Sopenharmony_ci return -EOPNOTSUPP; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci switch (edpd) { 56562306a36Sopenharmony_ci case ETHTOOL_PHY_EDPD_DISABLE: 56662306a36Sopenharmony_ci priv->edpd_enable = false; 56762306a36Sopenharmony_ci break; 56862306a36Sopenharmony_ci case ETHTOOL_PHY_EDPD_NO_TX: 56962306a36Sopenharmony_ci priv->edpd_enable = true; 57062306a36Sopenharmony_ci priv->edpd_max_wait_ms = 0; 57162306a36Sopenharmony_ci break; 57262306a36Sopenharmony_ci case ETHTOOL_PHY_EDPD_DFLT_TX_MSECS: 57362306a36Sopenharmony_ci edpd = PHY_STATE_MACH_MS + EDPD_MAX_WAIT_DFLT_MS; 57462306a36Sopenharmony_ci fallthrough; 57562306a36Sopenharmony_ci default: 57662306a36Sopenharmony_ci if (phydev->irq != PHY_POLL) 57762306a36Sopenharmony_ci return -EOPNOTSUPP; 57862306a36Sopenharmony_ci if (edpd < PHY_STATE_MACH_MS || edpd > PHY_STATE_MACH_MS + 1000) 57962306a36Sopenharmony_ci return -EINVAL; 58062306a36Sopenharmony_ci priv->edpd_enable = true; 58162306a36Sopenharmony_ci priv->edpd_max_wait_ms = edpd - PHY_STATE_MACH_MS; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci priv->edpd_mode_set_by_user = true; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci return smsc_phy_config_edpd(phydev); 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ciint smsc_phy_get_tunable(struct phy_device *phydev, 59062306a36Sopenharmony_ci struct ethtool_tunable *tuna, void *data) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci switch (tuna->id) { 59362306a36Sopenharmony_ci case ETHTOOL_PHY_EDPD: 59462306a36Sopenharmony_ci return smsc_phy_get_edpd(phydev, data); 59562306a36Sopenharmony_ci default: 59662306a36Sopenharmony_ci return -EOPNOTSUPP; 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(smsc_phy_get_tunable); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ciint smsc_phy_set_tunable(struct phy_device *phydev, 60262306a36Sopenharmony_ci struct ethtool_tunable *tuna, const void *data) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci switch (tuna->id) { 60562306a36Sopenharmony_ci case ETHTOOL_PHY_EDPD: 60662306a36Sopenharmony_ci return smsc_phy_set_edpd(phydev, *(u16 *)data); 60762306a36Sopenharmony_ci default: 60862306a36Sopenharmony_ci return -EOPNOTSUPP; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(smsc_phy_set_tunable); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ciint smsc_phy_probe(struct phy_device *phydev) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci struct device *dev = &phydev->mdio.dev; 61662306a36Sopenharmony_ci struct smsc_phy_priv *priv; 61762306a36Sopenharmony_ci struct clk *refclk; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 62062306a36Sopenharmony_ci if (!priv) 62162306a36Sopenharmony_ci return -ENOMEM; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci priv->edpd_enable = true; 62462306a36Sopenharmony_ci priv->edpd_max_wait_ms = EDPD_MAX_WAIT_DFLT_MS; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci if (device_property_present(dev, "smsc,disable-energy-detect")) 62762306a36Sopenharmony_ci priv->edpd_enable = false; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci phydev->priv = priv; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci /* Make clk optional to keep DTB backward compatibility. */ 63262306a36Sopenharmony_ci refclk = devm_clk_get_optional_enabled(dev, NULL); 63362306a36Sopenharmony_ci if (IS_ERR(refclk)) 63462306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(refclk), 63562306a36Sopenharmony_ci "Failed to request clock\n"); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci return clk_set_rate(refclk, 50 * 1000 * 1000); 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(smsc_phy_probe); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistatic struct phy_driver smsc_phy_driver[] = { 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci .phy_id = 0x0007c0a0, /* OUI=0x00800f, Model#=0x0a */ 64462306a36Sopenharmony_ci .phy_id_mask = 0xfffffff0, 64562306a36Sopenharmony_ci .name = "SMSC LAN83C185", 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci /* PHY_BASIC_FEATURES */ 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci .probe = smsc_phy_probe, 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci /* basic functions */ 65262306a36Sopenharmony_ci .config_init = smsc_phy_config_init, 65362306a36Sopenharmony_ci .soft_reset = smsc_phy_reset, 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci /* IRQ related */ 65662306a36Sopenharmony_ci .config_intr = smsc_phy_config_intr, 65762306a36Sopenharmony_ci .handle_interrupt = smsc_phy_handle_interrupt, 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci .suspend = genphy_suspend, 66062306a36Sopenharmony_ci .resume = genphy_resume, 66162306a36Sopenharmony_ci}, { 66262306a36Sopenharmony_ci .phy_id = 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */ 66362306a36Sopenharmony_ci .phy_id_mask = 0xfffffff0, 66462306a36Sopenharmony_ci .name = "SMSC LAN8187", 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci /* PHY_BASIC_FEATURES */ 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci .probe = smsc_phy_probe, 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci /* basic functions */ 67162306a36Sopenharmony_ci .config_init = smsc_phy_config_init, 67262306a36Sopenharmony_ci .soft_reset = smsc_phy_reset, 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci /* IRQ related */ 67562306a36Sopenharmony_ci .config_intr = smsc_phy_config_intr, 67662306a36Sopenharmony_ci .handle_interrupt = smsc_phy_handle_interrupt, 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci /* Statistics */ 67962306a36Sopenharmony_ci .get_sset_count = smsc_get_sset_count, 68062306a36Sopenharmony_ci .get_strings = smsc_get_strings, 68162306a36Sopenharmony_ci .get_stats = smsc_get_stats, 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci .suspend = genphy_suspend, 68462306a36Sopenharmony_ci .resume = genphy_resume, 68562306a36Sopenharmony_ci}, { 68662306a36Sopenharmony_ci /* This covers internal PHY (phy_id: 0x0007C0C3) for 68762306a36Sopenharmony_ci * LAN9500 (PID: 0x9500), LAN9514 (PID: 0xec00), LAN9505 (PID: 0x9505) 68862306a36Sopenharmony_ci */ 68962306a36Sopenharmony_ci .phy_id = 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */ 69062306a36Sopenharmony_ci .phy_id_mask = 0xfffffff0, 69162306a36Sopenharmony_ci .name = "SMSC LAN8700", 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci /* PHY_BASIC_FEATURES */ 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci .probe = smsc_phy_probe, 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci /* basic functions */ 69862306a36Sopenharmony_ci .read_status = lan87xx_read_status, 69962306a36Sopenharmony_ci .config_init = smsc_phy_config_init, 70062306a36Sopenharmony_ci .soft_reset = smsc_phy_reset, 70162306a36Sopenharmony_ci .config_aneg = lan87xx_config_aneg, 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci /* IRQ related */ 70462306a36Sopenharmony_ci .config_intr = smsc_phy_config_intr, 70562306a36Sopenharmony_ci .handle_interrupt = smsc_phy_handle_interrupt, 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci /* Statistics */ 70862306a36Sopenharmony_ci .get_sset_count = smsc_get_sset_count, 70962306a36Sopenharmony_ci .get_strings = smsc_get_strings, 71062306a36Sopenharmony_ci .get_stats = smsc_get_stats, 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci .get_tunable = smsc_phy_get_tunable, 71362306a36Sopenharmony_ci .set_tunable = smsc_phy_set_tunable, 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci .suspend = genphy_suspend, 71662306a36Sopenharmony_ci .resume = genphy_resume, 71762306a36Sopenharmony_ci}, { 71862306a36Sopenharmony_ci .phy_id = 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */ 71962306a36Sopenharmony_ci .phy_id_mask = 0xfffffff0, 72062306a36Sopenharmony_ci .name = "SMSC LAN911x Internal PHY", 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci /* PHY_BASIC_FEATURES */ 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci .probe = smsc_phy_probe, 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci /* IRQ related */ 72762306a36Sopenharmony_ci .config_intr = smsc_phy_config_intr, 72862306a36Sopenharmony_ci .handle_interrupt = smsc_phy_handle_interrupt, 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci .suspend = genphy_suspend, 73162306a36Sopenharmony_ci .resume = genphy_resume, 73262306a36Sopenharmony_ci}, { 73362306a36Sopenharmony_ci /* This covers internal PHY (phy_id: 0x0007C0F0) for 73462306a36Sopenharmony_ci * LAN9500A (PID: 0x9E00), LAN9505A (PID: 0x9E01) 73562306a36Sopenharmony_ci */ 73662306a36Sopenharmony_ci .phy_id = 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */ 73762306a36Sopenharmony_ci .phy_id_mask = 0xfffffff0, 73862306a36Sopenharmony_ci .name = "SMSC LAN8710/LAN8720", 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci /* PHY_BASIC_FEATURES */ 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci .probe = smsc_phy_probe, 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci /* basic functions */ 74562306a36Sopenharmony_ci .read_status = lan87xx_read_status, 74662306a36Sopenharmony_ci .config_init = smsc_phy_config_init, 74762306a36Sopenharmony_ci .soft_reset = smsc_phy_reset, 74862306a36Sopenharmony_ci .config_aneg = lan95xx_config_aneg_ext, 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci /* IRQ related */ 75162306a36Sopenharmony_ci .config_intr = smsc_phy_config_intr, 75262306a36Sopenharmony_ci .handle_interrupt = smsc_phy_handle_interrupt, 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci /* Statistics */ 75562306a36Sopenharmony_ci .get_sset_count = smsc_get_sset_count, 75662306a36Sopenharmony_ci .get_strings = smsc_get_strings, 75762306a36Sopenharmony_ci .get_stats = smsc_get_stats, 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci .get_tunable = smsc_phy_get_tunable, 76062306a36Sopenharmony_ci .set_tunable = smsc_phy_set_tunable, 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci .suspend = genphy_suspend, 76362306a36Sopenharmony_ci .resume = genphy_resume, 76462306a36Sopenharmony_ci}, { 76562306a36Sopenharmony_ci .phy_id = 0x0007c110, 76662306a36Sopenharmony_ci .phy_id_mask = 0xfffffff0, 76762306a36Sopenharmony_ci .name = "SMSC LAN8740", 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* PHY_BASIC_FEATURES */ 77062306a36Sopenharmony_ci .flags = PHY_RST_AFTER_CLK_EN, 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci .probe = smsc_phy_probe, 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci /* basic functions */ 77562306a36Sopenharmony_ci .read_status = lan87xx_read_status, 77662306a36Sopenharmony_ci .config_init = lan874x_phy_config_init, 77762306a36Sopenharmony_ci .soft_reset = smsc_phy_reset, 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci /* IRQ related */ 78062306a36Sopenharmony_ci .config_intr = smsc_phy_config_intr, 78162306a36Sopenharmony_ci .handle_interrupt = smsc_phy_handle_interrupt, 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci /* Statistics */ 78462306a36Sopenharmony_ci .get_sset_count = smsc_get_sset_count, 78562306a36Sopenharmony_ci .get_strings = smsc_get_strings, 78662306a36Sopenharmony_ci .get_stats = smsc_get_stats, 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci .get_tunable = smsc_phy_get_tunable, 78962306a36Sopenharmony_ci .set_tunable = smsc_phy_set_tunable, 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci /* WoL */ 79262306a36Sopenharmony_ci .set_wol = lan874x_set_wol, 79362306a36Sopenharmony_ci .get_wol = lan874x_get_wol, 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci .suspend = genphy_suspend, 79662306a36Sopenharmony_ci .resume = genphy_resume, 79762306a36Sopenharmony_ci}, { 79862306a36Sopenharmony_ci .phy_id = 0x0007c130, /* 0x0007c130 and 0x0007c131 */ 79962306a36Sopenharmony_ci /* This mask (0xfffffff2) is to differentiate from 80062306a36Sopenharmony_ci * LAN88xx (phy_id 0x0007c132) 80162306a36Sopenharmony_ci * and allows future phy_id revisions. 80262306a36Sopenharmony_ci */ 80362306a36Sopenharmony_ci .phy_id_mask = 0xfffffff2, 80462306a36Sopenharmony_ci .name = "Microchip LAN8742", 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci /* PHY_BASIC_FEATURES */ 80762306a36Sopenharmony_ci .flags = PHY_RST_AFTER_CLK_EN, 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci .probe = smsc_phy_probe, 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci /* basic functions */ 81262306a36Sopenharmony_ci .read_status = lan87xx_read_status, 81362306a36Sopenharmony_ci .config_init = lan874x_phy_config_init, 81462306a36Sopenharmony_ci .soft_reset = smsc_phy_reset, 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci /* IRQ related */ 81762306a36Sopenharmony_ci .config_intr = smsc_phy_config_intr, 81862306a36Sopenharmony_ci .handle_interrupt = smsc_phy_handle_interrupt, 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci /* Statistics */ 82162306a36Sopenharmony_ci .get_sset_count = smsc_get_sset_count, 82262306a36Sopenharmony_ci .get_strings = smsc_get_strings, 82362306a36Sopenharmony_ci .get_stats = smsc_get_stats, 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci .get_tunable = smsc_phy_get_tunable, 82662306a36Sopenharmony_ci .set_tunable = smsc_phy_set_tunable, 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci /* WoL */ 82962306a36Sopenharmony_ci .set_wol = lan874x_set_wol, 83062306a36Sopenharmony_ci .get_wol = lan874x_get_wol, 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci .suspend = genphy_suspend, 83362306a36Sopenharmony_ci .resume = genphy_resume, 83462306a36Sopenharmony_ci} }; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_cimodule_phy_driver(smsc_phy_driver); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ciMODULE_DESCRIPTION("SMSC PHY driver"); 83962306a36Sopenharmony_ciMODULE_AUTHOR("Herbert Valerio Riedel"); 84062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_cistatic struct mdio_device_id __maybe_unused smsc_tbl[] = { 84362306a36Sopenharmony_ci { 0x0007c0a0, 0xfffffff0 }, 84462306a36Sopenharmony_ci { 0x0007c0b0, 0xfffffff0 }, 84562306a36Sopenharmony_ci { 0x0007c0c0, 0xfffffff0 }, 84662306a36Sopenharmony_ci { 0x0007c0d0, 0xfffffff0 }, 84762306a36Sopenharmony_ci { 0x0007c0f0, 0xfffffff0 }, 84862306a36Sopenharmony_ci { 0x0007c110, 0xfffffff0 }, 84962306a36Sopenharmony_ci { 0x0007c130, 0xfffffff2 }, 85062306a36Sopenharmony_ci { } 85162306a36Sopenharmony_ci}; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(mdio, smsc_tbl); 854