162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2015 Microchip Technology 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include <linux/kernel.h> 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci#include <linux/mii.h> 862306a36Sopenharmony_ci#include <linux/ethtool.h> 962306a36Sopenharmony_ci#include <linux/phy.h> 1062306a36Sopenharmony_ci#include <linux/microchipphy.h> 1162306a36Sopenharmony_ci#include <linux/delay.h> 1262306a36Sopenharmony_ci#include <linux/of.h> 1362306a36Sopenharmony_ci#include <dt-bindings/net/microchip-lan78xx.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define DRIVER_AUTHOR "WOOJUNG HUH <woojung.huh@microchip.com>" 1662306a36Sopenharmony_ci#define DRIVER_DESC "Microchip LAN88XX PHY driver" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistruct lan88xx_priv { 1962306a36Sopenharmony_ci int chip_id; 2062306a36Sopenharmony_ci int chip_rev; 2162306a36Sopenharmony_ci __u32 wolopts; 2262306a36Sopenharmony_ci}; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic int lan88xx_read_page(struct phy_device *phydev) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci return __phy_read(phydev, LAN88XX_EXT_PAGE_ACCESS); 2762306a36Sopenharmony_ci} 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic int lan88xx_write_page(struct phy_device *phydev, int page) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci return __phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, page); 3262306a36Sopenharmony_ci} 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic int lan88xx_phy_config_intr(struct phy_device *phydev) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci int rc; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 3962306a36Sopenharmony_ci /* unmask all source and clear them before enable */ 4062306a36Sopenharmony_ci rc = phy_write(phydev, LAN88XX_INT_MASK, 0x7FFF); 4162306a36Sopenharmony_ci rc = phy_read(phydev, LAN88XX_INT_STS); 4262306a36Sopenharmony_ci rc = phy_write(phydev, LAN88XX_INT_MASK, 4362306a36Sopenharmony_ci LAN88XX_INT_MASK_MDINTPIN_EN_ | 4462306a36Sopenharmony_ci LAN88XX_INT_MASK_LINK_CHANGE_); 4562306a36Sopenharmony_ci } else { 4662306a36Sopenharmony_ci rc = phy_write(phydev, LAN88XX_INT_MASK, 0); 4762306a36Sopenharmony_ci if (rc) 4862306a36Sopenharmony_ci return rc; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci /* Ack interrupts after they have been disabled */ 5162306a36Sopenharmony_ci rc = phy_read(phydev, LAN88XX_INT_STS); 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return rc < 0 ? rc : 0; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic irqreturn_t lan88xx_handle_interrupt(struct phy_device *phydev) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci int irq_status; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci irq_status = phy_read(phydev, LAN88XX_INT_STS); 6262306a36Sopenharmony_ci if (irq_status < 0) { 6362306a36Sopenharmony_ci phy_error(phydev); 6462306a36Sopenharmony_ci return IRQ_NONE; 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (!(irq_status & LAN88XX_INT_STS_LINK_CHANGE_)) 6862306a36Sopenharmony_ci return IRQ_NONE; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci phy_trigger_machine(phydev); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci return IRQ_HANDLED; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int lan88xx_suspend(struct phy_device *phydev) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct lan88xx_priv *priv = phydev->priv; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* do not power down PHY when WOL is enabled */ 8062306a36Sopenharmony_ci if (!priv->wolopts) 8162306a36Sopenharmony_ci genphy_suspend(phydev); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return 0; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic int lan88xx_TR_reg_set(struct phy_device *phydev, u16 regaddr, 8762306a36Sopenharmony_ci u32 data) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci int val, save_page, ret = 0; 9062306a36Sopenharmony_ci u16 buf; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci /* Save current page */ 9362306a36Sopenharmony_ci save_page = phy_save_page(phydev); 9462306a36Sopenharmony_ci if (save_page < 0) { 9562306a36Sopenharmony_ci phydev_warn(phydev, "Failed to get current page\n"); 9662306a36Sopenharmony_ci goto err; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci /* Switch to TR page */ 10062306a36Sopenharmony_ci lan88xx_write_page(phydev, LAN88XX_EXT_PAGE_ACCESS_TR); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci ret = __phy_write(phydev, LAN88XX_EXT_PAGE_TR_LOW_DATA, 10362306a36Sopenharmony_ci (data & 0xFFFF)); 10462306a36Sopenharmony_ci if (ret < 0) { 10562306a36Sopenharmony_ci phydev_warn(phydev, "Failed to write TR low data\n"); 10662306a36Sopenharmony_ci goto err; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci ret = __phy_write(phydev, LAN88XX_EXT_PAGE_TR_HIGH_DATA, 11062306a36Sopenharmony_ci (data & 0x00FF0000) >> 16); 11162306a36Sopenharmony_ci if (ret < 0) { 11262306a36Sopenharmony_ci phydev_warn(phydev, "Failed to write TR high data\n"); 11362306a36Sopenharmony_ci goto err; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci /* Config control bits [15:13] of register */ 11762306a36Sopenharmony_ci buf = (regaddr & ~(0x3 << 13));/* Clr [14:13] to write data in reg */ 11862306a36Sopenharmony_ci buf |= 0x8000; /* Set [15] to Packet transmit */ 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci ret = __phy_write(phydev, LAN88XX_EXT_PAGE_TR_CR, buf); 12162306a36Sopenharmony_ci if (ret < 0) { 12262306a36Sopenharmony_ci phydev_warn(phydev, "Failed to write data in reg\n"); 12362306a36Sopenharmony_ci goto err; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci usleep_range(1000, 2000);/* Wait for Data to be written */ 12762306a36Sopenharmony_ci val = __phy_read(phydev, LAN88XX_EXT_PAGE_TR_CR); 12862306a36Sopenharmony_ci if (!(val & 0x8000)) 12962306a36Sopenharmony_ci phydev_warn(phydev, "TR Register[0x%X] configuration failed\n", 13062306a36Sopenharmony_ci regaddr); 13162306a36Sopenharmony_cierr: 13262306a36Sopenharmony_ci return phy_restore_page(phydev, save_page, ret); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic void lan88xx_config_TR_regs(struct phy_device *phydev) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci int err; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Get access to Channel 0x1, Node 0xF , Register 0x01. 14062306a36Sopenharmony_ci * Write 24-bit value 0x12B00A to register. Setting MrvlTrFix1000Kf, 14162306a36Sopenharmony_ci * MrvlTrFix1000Kp, MasterEnableTR bits. 14262306a36Sopenharmony_ci */ 14362306a36Sopenharmony_ci err = lan88xx_TR_reg_set(phydev, 0x0F82, 0x12B00A); 14462306a36Sopenharmony_ci if (err < 0) 14562306a36Sopenharmony_ci phydev_warn(phydev, "Failed to Set Register[0x0F82]\n"); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci /* Get access to Channel b'10, Node b'1101, Register 0x06. 14862306a36Sopenharmony_ci * Write 24-bit value 0xD2C46F to register. Setting SSTrKf1000Slv, 14962306a36Sopenharmony_ci * SSTrKp1000Mas bits. 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_ci err = lan88xx_TR_reg_set(phydev, 0x168C, 0xD2C46F); 15262306a36Sopenharmony_ci if (err < 0) 15362306a36Sopenharmony_ci phydev_warn(phydev, "Failed to Set Register[0x168C]\n"); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* Get access to Channel b'10, Node b'1111, Register 0x11. 15662306a36Sopenharmony_ci * Write 24-bit value 0x620 to register. Setting rem_upd_done_thresh 15762306a36Sopenharmony_ci * bits 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_ci err = lan88xx_TR_reg_set(phydev, 0x17A2, 0x620); 16062306a36Sopenharmony_ci if (err < 0) 16162306a36Sopenharmony_ci phydev_warn(phydev, "Failed to Set Register[0x17A2]\n"); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* Get access to Channel b'10, Node b'1101, Register 0x10. 16462306a36Sopenharmony_ci * Write 24-bit value 0xEEFFDD to register. Setting 16562306a36Sopenharmony_ci * eee_TrKp1Long_1000, eee_TrKp2Long_1000, eee_TrKp3Long_1000, 16662306a36Sopenharmony_ci * eee_TrKp1Short_1000,eee_TrKp2Short_1000, eee_TrKp3Short_1000 bits. 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_ci err = lan88xx_TR_reg_set(phydev, 0x16A0, 0xEEFFDD); 16962306a36Sopenharmony_ci if (err < 0) 17062306a36Sopenharmony_ci phydev_warn(phydev, "Failed to Set Register[0x16A0]\n"); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* Get access to Channel b'10, Node b'1101, Register 0x13. 17362306a36Sopenharmony_ci * Write 24-bit value 0x071448 to register. Setting 17462306a36Sopenharmony_ci * slv_lpi_tr_tmr_val1, slv_lpi_tr_tmr_val2 bits. 17562306a36Sopenharmony_ci */ 17662306a36Sopenharmony_ci err = lan88xx_TR_reg_set(phydev, 0x16A6, 0x071448); 17762306a36Sopenharmony_ci if (err < 0) 17862306a36Sopenharmony_ci phydev_warn(phydev, "Failed to Set Register[0x16A6]\n"); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* Get access to Channel b'10, Node b'1101, Register 0x12. 18162306a36Sopenharmony_ci * Write 24-bit value 0x13132F to register. Setting 18262306a36Sopenharmony_ci * slv_sigdet_timer_val1, slv_sigdet_timer_val2 bits. 18362306a36Sopenharmony_ci */ 18462306a36Sopenharmony_ci err = lan88xx_TR_reg_set(phydev, 0x16A4, 0x13132F); 18562306a36Sopenharmony_ci if (err < 0) 18662306a36Sopenharmony_ci phydev_warn(phydev, "Failed to Set Register[0x16A4]\n"); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* Get access to Channel b'10, Node b'1101, Register 0x14. 18962306a36Sopenharmony_ci * Write 24-bit value 0x0 to register. Setting eee_3level_delay, 19062306a36Sopenharmony_ci * eee_TrKf_freeze_delay bits. 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_ci err = lan88xx_TR_reg_set(phydev, 0x16A8, 0x0); 19362306a36Sopenharmony_ci if (err < 0) 19462306a36Sopenharmony_ci phydev_warn(phydev, "Failed to Set Register[0x16A8]\n"); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* Get access to Channel b'01, Node b'1111, Register 0x34. 19762306a36Sopenharmony_ci * Write 24-bit value 0x91B06C to register. Setting 19862306a36Sopenharmony_ci * FastMseSearchThreshLong1000, FastMseSearchThreshShort1000, 19962306a36Sopenharmony_ci * FastMseSearchUpdGain1000 bits. 20062306a36Sopenharmony_ci */ 20162306a36Sopenharmony_ci err = lan88xx_TR_reg_set(phydev, 0x0FE8, 0x91B06C); 20262306a36Sopenharmony_ci if (err < 0) 20362306a36Sopenharmony_ci phydev_warn(phydev, "Failed to Set Register[0x0FE8]\n"); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* Get access to Channel b'01, Node b'1111, Register 0x3E. 20662306a36Sopenharmony_ci * Write 24-bit value 0xC0A028 to register. Setting 20762306a36Sopenharmony_ci * FastMseKp2ThreshLong1000, FastMseKp2ThreshShort1000, 20862306a36Sopenharmony_ci * FastMseKp2UpdGain1000, FastMseKp2ExitEn1000 bits. 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_ci err = lan88xx_TR_reg_set(phydev, 0x0FFC, 0xC0A028); 21162306a36Sopenharmony_ci if (err < 0) 21262306a36Sopenharmony_ci phydev_warn(phydev, "Failed to Set Register[0x0FFC]\n"); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* Get access to Channel b'01, Node b'1111, Register 0x35. 21562306a36Sopenharmony_ci * Write 24-bit value 0x041600 to register. Setting 21662306a36Sopenharmony_ci * FastMseSearchPhShNum1000, FastMseSearchClksPerPh1000, 21762306a36Sopenharmony_ci * FastMsePhChangeDelay1000 bits. 21862306a36Sopenharmony_ci */ 21962306a36Sopenharmony_ci err = lan88xx_TR_reg_set(phydev, 0x0FEA, 0x041600); 22062306a36Sopenharmony_ci if (err < 0) 22162306a36Sopenharmony_ci phydev_warn(phydev, "Failed to Set Register[0x0FEA]\n"); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* Get access to Channel b'10, Node b'1101, Register 0x03. 22462306a36Sopenharmony_ci * Write 24-bit value 0x000004 to register. Setting TrFreeze bits. 22562306a36Sopenharmony_ci */ 22662306a36Sopenharmony_ci err = lan88xx_TR_reg_set(phydev, 0x1686, 0x000004); 22762306a36Sopenharmony_ci if (err < 0) 22862306a36Sopenharmony_ci phydev_warn(phydev, "Failed to Set Register[0x1686]\n"); 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic int lan88xx_probe(struct phy_device *phydev) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci struct device *dev = &phydev->mdio.dev; 23462306a36Sopenharmony_ci struct lan88xx_priv *priv; 23562306a36Sopenharmony_ci u32 led_modes[4]; 23662306a36Sopenharmony_ci int len; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 23962306a36Sopenharmony_ci if (!priv) 24062306a36Sopenharmony_ci return -ENOMEM; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci priv->wolopts = 0; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci len = of_property_read_variable_u32_array(dev->of_node, 24562306a36Sopenharmony_ci "microchip,led-modes", 24662306a36Sopenharmony_ci led_modes, 24762306a36Sopenharmony_ci 0, 24862306a36Sopenharmony_ci ARRAY_SIZE(led_modes)); 24962306a36Sopenharmony_ci if (len >= 0) { 25062306a36Sopenharmony_ci u32 reg = 0; 25162306a36Sopenharmony_ci int i; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci for (i = 0; i < len; i++) { 25462306a36Sopenharmony_ci if (led_modes[i] > 15) 25562306a36Sopenharmony_ci return -EINVAL; 25662306a36Sopenharmony_ci reg |= led_modes[i] << (i * 4); 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci for (; i < ARRAY_SIZE(led_modes); i++) 25962306a36Sopenharmony_ci reg |= LAN78XX_FORCE_LED_OFF << (i * 4); 26062306a36Sopenharmony_ci (void)phy_write(phydev, LAN78XX_PHY_LED_MODE_SELECT, reg); 26162306a36Sopenharmony_ci } else if (len == -EOVERFLOW) { 26262306a36Sopenharmony_ci return -EINVAL; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* these values can be used to identify internal PHY */ 26662306a36Sopenharmony_ci priv->chip_id = phy_read_mmd(phydev, 3, LAN88XX_MMD3_CHIP_ID); 26762306a36Sopenharmony_ci priv->chip_rev = phy_read_mmd(phydev, 3, LAN88XX_MMD3_CHIP_REV); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci phydev->priv = priv; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci return 0; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic void lan88xx_remove(struct phy_device *phydev) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci struct device *dev = &phydev->mdio.dev; 27762306a36Sopenharmony_ci struct lan88xx_priv *priv = phydev->priv; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (priv) 28062306a36Sopenharmony_ci devm_kfree(dev, priv); 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic int lan88xx_set_wol(struct phy_device *phydev, 28462306a36Sopenharmony_ci struct ethtool_wolinfo *wol) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci struct lan88xx_priv *priv = phydev->priv; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci priv->wolopts = wol->wolopts; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic void lan88xx_set_mdix(struct phy_device *phydev) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci int buf; 29662306a36Sopenharmony_ci int val; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci switch (phydev->mdix_ctrl) { 29962306a36Sopenharmony_ci case ETH_TP_MDI: 30062306a36Sopenharmony_ci val = LAN88XX_EXT_MODE_CTRL_MDI_; 30162306a36Sopenharmony_ci break; 30262306a36Sopenharmony_ci case ETH_TP_MDI_X: 30362306a36Sopenharmony_ci val = LAN88XX_EXT_MODE_CTRL_MDI_X_; 30462306a36Sopenharmony_ci break; 30562306a36Sopenharmony_ci case ETH_TP_MDI_AUTO: 30662306a36Sopenharmony_ci val = LAN88XX_EXT_MODE_CTRL_AUTO_MDIX_; 30762306a36Sopenharmony_ci break; 30862306a36Sopenharmony_ci default: 30962306a36Sopenharmony_ci return; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, LAN88XX_EXT_PAGE_SPACE_1); 31362306a36Sopenharmony_ci buf = phy_read(phydev, LAN88XX_EXT_MODE_CTRL); 31462306a36Sopenharmony_ci buf &= ~LAN88XX_EXT_MODE_CTRL_MDIX_MASK_; 31562306a36Sopenharmony_ci buf |= val; 31662306a36Sopenharmony_ci phy_write(phydev, LAN88XX_EXT_MODE_CTRL, buf); 31762306a36Sopenharmony_ci phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, LAN88XX_EXT_PAGE_SPACE_0); 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic int lan88xx_config_init(struct phy_device *phydev) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci int val; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /*Zerodetect delay enable */ 32562306a36Sopenharmony_ci val = phy_read_mmd(phydev, MDIO_MMD_PCS, 32662306a36Sopenharmony_ci PHY_ARDENNES_MMD_DEV_3_PHY_CFG); 32762306a36Sopenharmony_ci val |= PHY_ARDENNES_MMD_DEV_3_PHY_CFG_ZD_DLY_EN_; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci phy_write_mmd(phydev, MDIO_MMD_PCS, PHY_ARDENNES_MMD_DEV_3_PHY_CFG, 33062306a36Sopenharmony_ci val); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci /* Config DSP registers */ 33362306a36Sopenharmony_ci lan88xx_config_TR_regs(phydev); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci return 0; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic int lan88xx_config_aneg(struct phy_device *phydev) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci lan88xx_set_mdix(phydev); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci return genphy_config_aneg(phydev); 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic void lan88xx_link_change_notify(struct phy_device *phydev) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci int temp; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* At forced 100 F/H mode, chip may fail to set mode correctly 35062306a36Sopenharmony_ci * when cable is switched between long(~50+m) and short one. 35162306a36Sopenharmony_ci * As workaround, set to 10 before setting to 100 35262306a36Sopenharmony_ci * at forced 100 F/H mode. 35362306a36Sopenharmony_ci */ 35462306a36Sopenharmony_ci if (!phydev->autoneg && phydev->speed == 100) { 35562306a36Sopenharmony_ci /* disable phy interrupt */ 35662306a36Sopenharmony_ci temp = phy_read(phydev, LAN88XX_INT_MASK); 35762306a36Sopenharmony_ci temp &= ~LAN88XX_INT_MASK_MDINTPIN_EN_; 35862306a36Sopenharmony_ci phy_write(phydev, LAN88XX_INT_MASK, temp); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci temp = phy_read(phydev, MII_BMCR); 36162306a36Sopenharmony_ci temp &= ~(BMCR_SPEED100 | BMCR_SPEED1000); 36262306a36Sopenharmony_ci phy_write(phydev, MII_BMCR, temp); /* set to 10 first */ 36362306a36Sopenharmony_ci temp |= BMCR_SPEED100; 36462306a36Sopenharmony_ci phy_write(phydev, MII_BMCR, temp); /* set to 100 later */ 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* clear pending interrupt generated while workaround */ 36762306a36Sopenharmony_ci temp = phy_read(phydev, LAN88XX_INT_STS); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* enable phy interrupt back */ 37062306a36Sopenharmony_ci temp = phy_read(phydev, LAN88XX_INT_MASK); 37162306a36Sopenharmony_ci temp |= LAN88XX_INT_MASK_MDINTPIN_EN_; 37262306a36Sopenharmony_ci phy_write(phydev, LAN88XX_INT_MASK, temp); 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic struct phy_driver microchip_phy_driver[] = { 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci .phy_id = 0x0007c132, 37962306a36Sopenharmony_ci /* This mask (0xfffffff2) is to differentiate from 38062306a36Sopenharmony_ci * LAN8742 (phy_id 0x0007c130 and 0x0007c131) 38162306a36Sopenharmony_ci * and allows future phy_id revisions. 38262306a36Sopenharmony_ci */ 38362306a36Sopenharmony_ci .phy_id_mask = 0xfffffff2, 38462306a36Sopenharmony_ci .name = "Microchip LAN88xx", 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci /* PHY_GBIT_FEATURES */ 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci .probe = lan88xx_probe, 38962306a36Sopenharmony_ci .remove = lan88xx_remove, 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci .config_init = lan88xx_config_init, 39262306a36Sopenharmony_ci .config_aneg = lan88xx_config_aneg, 39362306a36Sopenharmony_ci .link_change_notify = lan88xx_link_change_notify, 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci .config_intr = lan88xx_phy_config_intr, 39662306a36Sopenharmony_ci .handle_interrupt = lan88xx_handle_interrupt, 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci .suspend = lan88xx_suspend, 39962306a36Sopenharmony_ci .resume = genphy_resume, 40062306a36Sopenharmony_ci .set_wol = lan88xx_set_wol, 40162306a36Sopenharmony_ci .read_page = lan88xx_read_page, 40262306a36Sopenharmony_ci .write_page = lan88xx_write_page, 40362306a36Sopenharmony_ci} }; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cimodule_phy_driver(microchip_phy_driver); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic struct mdio_device_id __maybe_unused microchip_tbl[] = { 40862306a36Sopenharmony_ci { 0x0007c132, 0xfffffff2 }, 40962306a36Sopenharmony_ci { } 41062306a36Sopenharmony_ci}; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(mdio, microchip_tbl); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 41562306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 41662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 417