1792aec47SWoojung.Huh@microchip.com /* 2792aec47SWoojung.Huh@microchip.com * Copyright (C) 2015 Microchip Technology 3792aec47SWoojung.Huh@microchip.com * 4792aec47SWoojung.Huh@microchip.com * This program is free software; you can redistribute it and/or 5792aec47SWoojung.Huh@microchip.com * modify it under the terms of the GNU General Public License 6792aec47SWoojung.Huh@microchip.com * as published by the Free Software Foundation; either version 2 7792aec47SWoojung.Huh@microchip.com * of the License, or (at your option) any later version. 8792aec47SWoojung.Huh@microchip.com * 9792aec47SWoojung.Huh@microchip.com * This program is distributed in the hope that it will be useful, 10792aec47SWoojung.Huh@microchip.com * but WITHOUT ANY WARRANTY; without even the implied warranty of 11792aec47SWoojung.Huh@microchip.com * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12792aec47SWoojung.Huh@microchip.com * GNU General Public License for more details. 13792aec47SWoojung.Huh@microchip.com * 14792aec47SWoojung.Huh@microchip.com * You should have received a copy of the GNU General Public License 15792aec47SWoojung.Huh@microchip.com * along with this program; if not, see <http://www.gnu.org/licenses/>. 16792aec47SWoojung.Huh@microchip.com */ 17792aec47SWoojung.Huh@microchip.com #include <linux/kernel.h> 18792aec47SWoojung.Huh@microchip.com #include <linux/module.h> 19792aec47SWoojung.Huh@microchip.com #include <linux/mii.h> 20792aec47SWoojung.Huh@microchip.com #include <linux/ethtool.h> 21792aec47SWoojung.Huh@microchip.com #include <linux/phy.h> 22792aec47SWoojung.Huh@microchip.com #include <linux/microchipphy.h> 23792aec47SWoojung.Huh@microchip.com 24792aec47SWoojung.Huh@microchip.com #define DRIVER_AUTHOR "WOOJUNG HUH <woojung.huh@microchip.com>" 25792aec47SWoojung.Huh@microchip.com #define DRIVER_DESC "Microchip LAN88XX PHY driver" 26792aec47SWoojung.Huh@microchip.com 27792aec47SWoojung.Huh@microchip.com struct lan88xx_priv { 28792aec47SWoojung.Huh@microchip.com int chip_id; 29792aec47SWoojung.Huh@microchip.com int chip_rev; 30792aec47SWoojung.Huh@microchip.com __u32 wolopts; 31792aec47SWoojung.Huh@microchip.com }; 32792aec47SWoojung.Huh@microchip.com 33792aec47SWoojung.Huh@microchip.com static int lan88xx_phy_config_intr(struct phy_device *phydev) 34792aec47SWoojung.Huh@microchip.com { 35792aec47SWoojung.Huh@microchip.com int rc; 36792aec47SWoojung.Huh@microchip.com 37792aec47SWoojung.Huh@microchip.com if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 38792aec47SWoojung.Huh@microchip.com /* unmask all source and clear them before enable */ 39792aec47SWoojung.Huh@microchip.com rc = phy_write(phydev, LAN88XX_INT_MASK, 0x7FFF); 40792aec47SWoojung.Huh@microchip.com rc = phy_read(phydev, LAN88XX_INT_STS); 41792aec47SWoojung.Huh@microchip.com rc = phy_write(phydev, LAN88XX_INT_MASK, 42792aec47SWoojung.Huh@microchip.com LAN88XX_INT_MASK_MDINTPIN_EN_ | 43792aec47SWoojung.Huh@microchip.com LAN88XX_INT_MASK_LINK_CHANGE_); 44792aec47SWoojung.Huh@microchip.com } else { 45792aec47SWoojung.Huh@microchip.com rc = phy_write(phydev, LAN88XX_INT_MASK, 0); 46792aec47SWoojung.Huh@microchip.com } 47792aec47SWoojung.Huh@microchip.com 48792aec47SWoojung.Huh@microchip.com return rc < 0 ? rc : 0; 49792aec47SWoojung.Huh@microchip.com } 50792aec47SWoojung.Huh@microchip.com 51792aec47SWoojung.Huh@microchip.com static int lan88xx_phy_ack_interrupt(struct phy_device *phydev) 52792aec47SWoojung.Huh@microchip.com { 53792aec47SWoojung.Huh@microchip.com int rc = phy_read(phydev, LAN88XX_INT_STS); 54792aec47SWoojung.Huh@microchip.com 55792aec47SWoojung.Huh@microchip.com return rc < 0 ? rc : 0; 56792aec47SWoojung.Huh@microchip.com } 57792aec47SWoojung.Huh@microchip.com 5899408030SBaoyou Xie static int lan88xx_suspend(struct phy_device *phydev) 59792aec47SWoojung.Huh@microchip.com { 60792aec47SWoojung.Huh@microchip.com struct lan88xx_priv *priv = phydev->priv; 61792aec47SWoojung.Huh@microchip.com 62792aec47SWoojung.Huh@microchip.com /* do not power down PHY when WOL is enabled */ 63792aec47SWoojung.Huh@microchip.com if (!priv->wolopts) 64792aec47SWoojung.Huh@microchip.com genphy_suspend(phydev); 65792aec47SWoojung.Huh@microchip.com 66792aec47SWoojung.Huh@microchip.com return 0; 67792aec47SWoojung.Huh@microchip.com } 68792aec47SWoojung.Huh@microchip.com 69792aec47SWoojung.Huh@microchip.com static int lan88xx_probe(struct phy_device *phydev) 70792aec47SWoojung.Huh@microchip.com { 71e5a03bfdSAndrew Lunn struct device *dev = &phydev->mdio.dev; 72792aec47SWoojung.Huh@microchip.com struct lan88xx_priv *priv; 73792aec47SWoojung.Huh@microchip.com 74792aec47SWoojung.Huh@microchip.com priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 75792aec47SWoojung.Huh@microchip.com if (!priv) 76792aec47SWoojung.Huh@microchip.com return -ENOMEM; 77792aec47SWoojung.Huh@microchip.com 78792aec47SWoojung.Huh@microchip.com priv->wolopts = 0; 79792aec47SWoojung.Huh@microchip.com 80792aec47SWoojung.Huh@microchip.com /* these values can be used to identify internal PHY */ 81a6d99fcdSRussell King priv->chip_id = phy_read_mmd(phydev, 3, LAN88XX_MMD3_CHIP_ID); 82a6d99fcdSRussell King priv->chip_rev = phy_read_mmd(phydev, 3, LAN88XX_MMD3_CHIP_REV); 83792aec47SWoojung.Huh@microchip.com 84792aec47SWoojung.Huh@microchip.com phydev->priv = priv; 85792aec47SWoojung.Huh@microchip.com 86792aec47SWoojung.Huh@microchip.com return 0; 87792aec47SWoojung.Huh@microchip.com } 88792aec47SWoojung.Huh@microchip.com 89792aec47SWoojung.Huh@microchip.com static void lan88xx_remove(struct phy_device *phydev) 90792aec47SWoojung.Huh@microchip.com { 91e5a03bfdSAndrew Lunn struct device *dev = &phydev->mdio.dev; 92792aec47SWoojung.Huh@microchip.com struct lan88xx_priv *priv = phydev->priv; 93792aec47SWoojung.Huh@microchip.com 94792aec47SWoojung.Huh@microchip.com if (priv) 95792aec47SWoojung.Huh@microchip.com devm_kfree(dev, priv); 96792aec47SWoojung.Huh@microchip.com } 97792aec47SWoojung.Huh@microchip.com 98792aec47SWoojung.Huh@microchip.com static int lan88xx_set_wol(struct phy_device *phydev, 99792aec47SWoojung.Huh@microchip.com struct ethtool_wolinfo *wol) 100792aec47SWoojung.Huh@microchip.com { 101792aec47SWoojung.Huh@microchip.com struct lan88xx_priv *priv = phydev->priv; 102792aec47SWoojung.Huh@microchip.com 103792aec47SWoojung.Huh@microchip.com priv->wolopts = wol->wolopts; 104792aec47SWoojung.Huh@microchip.com 105792aec47SWoojung.Huh@microchip.com return 0; 106792aec47SWoojung.Huh@microchip.com } 107792aec47SWoojung.Huh@microchip.com 108f6e3ef3eSWoojung Huh static void lan88xx_set_mdix(struct phy_device *phydev) 109f6e3ef3eSWoojung Huh { 110f6e3ef3eSWoojung Huh int buf; 111f6e3ef3eSWoojung Huh int val; 112f6e3ef3eSWoojung Huh 1134e26c5c3SRaju Lakkaraju switch (phydev->mdix_ctrl) { 114f6e3ef3eSWoojung Huh case ETH_TP_MDI: 115f6e3ef3eSWoojung Huh val = LAN88XX_EXT_MODE_CTRL_MDI_; 116f6e3ef3eSWoojung Huh break; 117f6e3ef3eSWoojung Huh case ETH_TP_MDI_X: 118f6e3ef3eSWoojung Huh val = LAN88XX_EXT_MODE_CTRL_MDI_X_; 119f6e3ef3eSWoojung Huh break; 120f6e3ef3eSWoojung Huh case ETH_TP_MDI_AUTO: 121f6e3ef3eSWoojung Huh val = LAN88XX_EXT_MODE_CTRL_AUTO_MDIX_; 122f6e3ef3eSWoojung Huh break; 123f6e3ef3eSWoojung Huh default: 124f6e3ef3eSWoojung Huh return; 125f6e3ef3eSWoojung Huh } 126f6e3ef3eSWoojung Huh 127f6e3ef3eSWoojung Huh phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, LAN88XX_EXT_PAGE_SPACE_1); 128f6e3ef3eSWoojung Huh buf = phy_read(phydev, LAN88XX_EXT_MODE_CTRL); 129f6e3ef3eSWoojung Huh buf &= ~LAN88XX_EXT_MODE_CTRL_MDIX_MASK_; 130f6e3ef3eSWoojung Huh buf |= val; 131f6e3ef3eSWoojung Huh phy_write(phydev, LAN88XX_EXT_MODE_CTRL, buf); 132f6e3ef3eSWoojung Huh phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, LAN88XX_EXT_PAGE_SPACE_0); 133f6e3ef3eSWoojung Huh } 134f6e3ef3eSWoojung Huh 135f6e3ef3eSWoojung Huh static int lan88xx_config_aneg(struct phy_device *phydev) 136f6e3ef3eSWoojung Huh { 137f6e3ef3eSWoojung Huh lan88xx_set_mdix(phydev); 138f6e3ef3eSWoojung Huh 139f6e3ef3eSWoojung Huh return genphy_config_aneg(phydev); 140f6e3ef3eSWoojung Huh } 141f6e3ef3eSWoojung Huh 142792aec47SWoojung.Huh@microchip.com static struct phy_driver microchip_phy_driver[] = { 143792aec47SWoojung.Huh@microchip.com { 144792aec47SWoojung.Huh@microchip.com .phy_id = 0x0007c130, 145792aec47SWoojung.Huh@microchip.com .phy_id_mask = 0xfffffff0, 146792aec47SWoojung.Huh@microchip.com .name = "Microchip LAN88xx", 147792aec47SWoojung.Huh@microchip.com 148529ed127STimur Tabi .features = PHY_GBIT_FEATURES, 149792aec47SWoojung.Huh@microchip.com .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 150792aec47SWoojung.Huh@microchip.com 151792aec47SWoojung.Huh@microchip.com .probe = lan88xx_probe, 152792aec47SWoojung.Huh@microchip.com .remove = lan88xx_remove, 153792aec47SWoojung.Huh@microchip.com 154792aec47SWoojung.Huh@microchip.com .config_init = genphy_config_init, 155f6e3ef3eSWoojung Huh .config_aneg = lan88xx_config_aneg, 156792aec47SWoojung.Huh@microchip.com .read_status = genphy_read_status, 157792aec47SWoojung.Huh@microchip.com 158792aec47SWoojung.Huh@microchip.com .ack_interrupt = lan88xx_phy_ack_interrupt, 159792aec47SWoojung.Huh@microchip.com .config_intr = lan88xx_phy_config_intr, 160792aec47SWoojung.Huh@microchip.com 161792aec47SWoojung.Huh@microchip.com .suspend = lan88xx_suspend, 162792aec47SWoojung.Huh@microchip.com .resume = genphy_resume, 163792aec47SWoojung.Huh@microchip.com .set_wol = lan88xx_set_wol, 164792aec47SWoojung.Huh@microchip.com } }; 165792aec47SWoojung.Huh@microchip.com 166792aec47SWoojung.Huh@microchip.com module_phy_driver(microchip_phy_driver); 167792aec47SWoojung.Huh@microchip.com 168792aec47SWoojung.Huh@microchip.com static struct mdio_device_id __maybe_unused microchip_tbl[] = { 169792aec47SWoojung.Huh@microchip.com { 0x0007c130, 0xfffffff0 }, 170792aec47SWoojung.Huh@microchip.com { } 171792aec47SWoojung.Huh@microchip.com }; 172792aec47SWoojung.Huh@microchip.com 173792aec47SWoojung.Huh@microchip.com MODULE_DEVICE_TABLE(mdio, microchip_tbl); 174792aec47SWoojung.Huh@microchip.com 175792aec47SWoojung.Huh@microchip.com MODULE_AUTHOR(DRIVER_AUTHOR); 176792aec47SWoojung.Huh@microchip.com MODULE_DESCRIPTION(DRIVER_DESC); 177792aec47SWoojung.Huh@microchip.com MODULE_LICENSE("GPL"); 178