1ef82a306SJon Loeliger /* 2ef82a306SJon Loeliger * Driver for Vitesse PHYs 3ef82a306SJon Loeliger * 4ef82a306SJon Loeliger * Author: Kriston Carson 5ef82a306SJon Loeliger * 6ef82a306SJon Loeliger * Copyright (c) 2005 Freescale Semiconductor, Inc. 7ef82a306SJon Loeliger * 8ef82a306SJon Loeliger * This program is free software; you can redistribute it and/or modify it 9ef82a306SJon Loeliger * under the terms of the GNU General Public License as published by the 10ef82a306SJon Loeliger * Free Software Foundation; either version 2 of the License, or (at your 11ef82a306SJon Loeliger * option) any later version. 12ef82a306SJon Loeliger * 13ef82a306SJon Loeliger */ 14ef82a306SJon Loeliger 15ef82a306SJon Loeliger #include <linux/kernel.h> 16ef82a306SJon Loeliger #include <linux/module.h> 17ef82a306SJon Loeliger #include <linux/mii.h> 18ef82a306SJon Loeliger #include <linux/ethtool.h> 19ef82a306SJon Loeliger #include <linux/phy.h> 20ef82a306SJon Loeliger 21ef82a306SJon Loeliger /* Vitesse Extended Control Register 1 */ 22ef82a306SJon Loeliger #define MII_VSC8244_EXT_CON1 0x17 23ef82a306SJon Loeliger #define MII_VSC8244_EXTCON1_INIT 0x0000 24af2d940dSAndy Fleming #define MII_VSC8244_EXTCON1_TX_SKEW_MASK 0x0c00 25af2d940dSAndy Fleming #define MII_VSC8244_EXTCON1_RX_SKEW_MASK 0x0300 26af2d940dSAndy Fleming #define MII_VSC8244_EXTCON1_TX_SKEW 0x0800 27af2d940dSAndy Fleming #define MII_VSC8244_EXTCON1_RX_SKEW 0x0200 28ef82a306SJon Loeliger 29ef82a306SJon Loeliger /* Vitesse Interrupt Mask Register */ 30ef82a306SJon Loeliger #define MII_VSC8244_IMASK 0x19 31ef82a306SJon Loeliger #define MII_VSC8244_IMASK_IEN 0x8000 32ef82a306SJon Loeliger #define MII_VSC8244_IMASK_SPEED 0x4000 33ef82a306SJon Loeliger #define MII_VSC8244_IMASK_LINK 0x2000 34ef82a306SJon Loeliger #define MII_VSC8244_IMASK_DUPLEX 0x1000 35ef82a306SJon Loeliger #define MII_VSC8244_IMASK_MASK 0xf000 36ef82a306SJon Loeliger 3711c6dd2cSTrent Piepho #define MII_VSC8221_IMASK_MASK 0xa000 3811c6dd2cSTrent Piepho 39ef82a306SJon Loeliger /* Vitesse Interrupt Status Register */ 40ef82a306SJon Loeliger #define MII_VSC8244_ISTAT 0x1a 41ef82a306SJon Loeliger #define MII_VSC8244_ISTAT_STATUS 0x8000 42ef82a306SJon Loeliger #define MII_VSC8244_ISTAT_SPEED 0x4000 43ef82a306SJon Loeliger #define MII_VSC8244_ISTAT_LINK 0x2000 44ef82a306SJon Loeliger #define MII_VSC8244_ISTAT_DUPLEX 0x1000 45ef82a306SJon Loeliger 46ef82a306SJon Loeliger /* Vitesse Auxiliary Control/Status Register */ 47ef82a306SJon Loeliger #define MII_VSC8244_AUX_CONSTAT 0x1c 48af2d940dSAndy Fleming #define MII_VSC8244_AUXCONSTAT_INIT 0x0000 49ef82a306SJon Loeliger #define MII_VSC8244_AUXCONSTAT_DUPLEX 0x0020 50ef82a306SJon Loeliger #define MII_VSC8244_AUXCONSTAT_SPEED 0x0018 51ef82a306SJon Loeliger #define MII_VSC8244_AUXCONSTAT_GBIT 0x0010 52ef82a306SJon Loeliger #define MII_VSC8244_AUXCONSTAT_100 0x0008 53ef82a306SJon Loeliger 5411c6dd2cSTrent Piepho #define MII_VSC8221_AUXCONSTAT_INIT 0x0004 /* need to set this bit? */ 5511c6dd2cSTrent Piepho #define MII_VSC8221_AUXCONSTAT_RESERVED 0x0004 5611c6dd2cSTrent Piepho 5711c6dd2cSTrent Piepho #define PHY_ID_VSC8244 0x000fc6c0 5811c6dd2cSTrent Piepho #define PHY_ID_VSC8221 0x000fc550 5911c6dd2cSTrent Piepho 60ef82a306SJon Loeliger MODULE_DESCRIPTION("Vitesse PHY driver"); 61ef82a306SJon Loeliger MODULE_AUTHOR("Kriston Carson"); 62ef82a306SJon Loeliger MODULE_LICENSE("GPL"); 63ef82a306SJon Loeliger 64ef82a306SJon Loeliger static int vsc824x_config_init(struct phy_device *phydev) 65ef82a306SJon Loeliger { 66af2d940dSAndy Fleming int extcon; 67ef82a306SJon Loeliger int err; 68ef82a306SJon Loeliger 69ef82a306SJon Loeliger err = phy_write(phydev, MII_VSC8244_AUX_CONSTAT, 70ef82a306SJon Loeliger MII_VSC8244_AUXCONSTAT_INIT); 71ef82a306SJon Loeliger if (err < 0) 72ef82a306SJon Loeliger return err; 73ef82a306SJon Loeliger 74af2d940dSAndy Fleming extcon = phy_read(phydev, MII_VSC8244_EXT_CON1); 75af2d940dSAndy Fleming 76af2d940dSAndy Fleming if (extcon < 0) 77af2d940dSAndy Fleming return err; 78af2d940dSAndy Fleming 79af2d940dSAndy Fleming extcon &= ~(MII_VSC8244_EXTCON1_TX_SKEW_MASK | 80af2d940dSAndy Fleming MII_VSC8244_EXTCON1_RX_SKEW_MASK); 81af2d940dSAndy Fleming 82af2d940dSAndy Fleming if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) 83af2d940dSAndy Fleming extcon |= (MII_VSC8244_EXTCON1_TX_SKEW | 84af2d940dSAndy Fleming MII_VSC8244_EXTCON1_RX_SKEW); 85af2d940dSAndy Fleming 86af2d940dSAndy Fleming err = phy_write(phydev, MII_VSC8244_EXT_CON1, extcon); 87af2d940dSAndy Fleming 88ef82a306SJon Loeliger return err; 89ef82a306SJon Loeliger } 90ef82a306SJon Loeliger 91ef82a306SJon Loeliger static int vsc824x_ack_interrupt(struct phy_device *phydev) 92ef82a306SJon Loeliger { 931d5e83aaSAndy Fleming int err = 0; 941d5e83aaSAndy Fleming 951d5e83aaSAndy Fleming /* 961d5e83aaSAndy Fleming * Don't bother to ACK the interrupts if interrupts 971d5e83aaSAndy Fleming * are disabled. The 824x cannot clear the interrupts 981d5e83aaSAndy Fleming * if they are disabled. 991d5e83aaSAndy Fleming */ 1001d5e83aaSAndy Fleming if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 1011d5e83aaSAndy Fleming err = phy_read(phydev, MII_VSC8244_ISTAT); 102ef82a306SJon Loeliger 103ef82a306SJon Loeliger return (err < 0) ? err : 0; 104ef82a306SJon Loeliger } 105ef82a306SJon Loeliger 10611c6dd2cSTrent Piepho static int vsc82xx_config_intr(struct phy_device *phydev) 107ef82a306SJon Loeliger { 108ef82a306SJon Loeliger int err; 109ef82a306SJon Loeliger 110ef82a306SJon Loeliger if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 111ef82a306SJon Loeliger err = phy_write(phydev, MII_VSC8244_IMASK, 11211c6dd2cSTrent Piepho phydev->drv->phy_id == PHY_ID_VSC8244 ? 11311c6dd2cSTrent Piepho MII_VSC8244_IMASK_MASK : 11411c6dd2cSTrent Piepho MII_VSC8221_IMASK_MASK); 1151d5e83aaSAndy Fleming else { 1161d5e83aaSAndy Fleming /* 1171d5e83aaSAndy Fleming * The Vitesse PHY cannot clear the interrupt 1181d5e83aaSAndy Fleming * once it has disabled them, so we clear them first 1191d5e83aaSAndy Fleming */ 1201d5e83aaSAndy Fleming err = phy_read(phydev, MII_VSC8244_ISTAT); 1211d5e83aaSAndy Fleming 12252cb1c2bSAndy Fleming if (err < 0) 1231d5e83aaSAndy Fleming return err; 1241d5e83aaSAndy Fleming 125ef82a306SJon Loeliger err = phy_write(phydev, MII_VSC8244_IMASK, 0); 1261d5e83aaSAndy Fleming } 1271d5e83aaSAndy Fleming 128ef82a306SJon Loeliger return err; 129ef82a306SJon Loeliger } 130ef82a306SJon Loeliger 131ef82a306SJon Loeliger /* Vitesse 824x */ 132ef82a306SJon Loeliger static struct phy_driver vsc8244_driver = { 13311c6dd2cSTrent Piepho .phy_id = PHY_ID_VSC8244, 134ef82a306SJon Loeliger .name = "Vitesse VSC8244", 135ef82a306SJon Loeliger .phy_id_mask = 0x000fffc0, 136ef82a306SJon Loeliger .features = PHY_GBIT_FEATURES, 137ef82a306SJon Loeliger .flags = PHY_HAS_INTERRUPT, 138ef82a306SJon Loeliger .config_init = &vsc824x_config_init, 139ef82a306SJon Loeliger .config_aneg = &genphy_config_aneg, 140ef82a306SJon Loeliger .read_status = &genphy_read_status, 141ef82a306SJon Loeliger .ack_interrupt = &vsc824x_ack_interrupt, 14211c6dd2cSTrent Piepho .config_intr = &vsc82xx_config_intr, 143ef82a306SJon Loeliger .driver = { .owner = THIS_MODULE,}, 144ef82a306SJon Loeliger }; 145ef82a306SJon Loeliger 14611c6dd2cSTrent Piepho static int vsc8221_config_init(struct phy_device *phydev) 147ef82a306SJon Loeliger { 14811c6dd2cSTrent Piepho int err; 14911c6dd2cSTrent Piepho 15011c6dd2cSTrent Piepho err = phy_write(phydev, MII_VSC8244_AUX_CONSTAT, 15111c6dd2cSTrent Piepho MII_VSC8221_AUXCONSTAT_INIT); 15211c6dd2cSTrent Piepho return err; 15311c6dd2cSTrent Piepho 15411c6dd2cSTrent Piepho /* Perhaps we should set EXT_CON1 based on the interface? 15511c6dd2cSTrent Piepho Options are 802.3Z SerDes or SGMII */ 156ef82a306SJon Loeliger } 157ef82a306SJon Loeliger 15811c6dd2cSTrent Piepho /* Vitesse 8221 */ 15911c6dd2cSTrent Piepho static struct phy_driver vsc8221_driver = { 16011c6dd2cSTrent Piepho .phy_id = PHY_ID_VSC8221, 16111c6dd2cSTrent Piepho .phy_id_mask = 0x000ffff0, 16211c6dd2cSTrent Piepho .name = "Vitesse VSC8221", 16311c6dd2cSTrent Piepho .features = PHY_GBIT_FEATURES, 16411c6dd2cSTrent Piepho .flags = PHY_HAS_INTERRUPT, 16511c6dd2cSTrent Piepho .config_init = &vsc8221_config_init, 16611c6dd2cSTrent Piepho .config_aneg = &genphy_config_aneg, 16711c6dd2cSTrent Piepho .read_status = &genphy_read_status, 16811c6dd2cSTrent Piepho .ack_interrupt = &vsc824x_ack_interrupt, 16911c6dd2cSTrent Piepho .config_intr = &vsc82xx_config_intr, 17011c6dd2cSTrent Piepho .driver = { .owner = THIS_MODULE,}, 17111c6dd2cSTrent Piepho }; 17211c6dd2cSTrent Piepho 17311c6dd2cSTrent Piepho static int __init vsc82xx_init(void) 17411c6dd2cSTrent Piepho { 17511c6dd2cSTrent Piepho int err; 17611c6dd2cSTrent Piepho 17711c6dd2cSTrent Piepho err = phy_driver_register(&vsc8244_driver); 17811c6dd2cSTrent Piepho if (err < 0) 17911c6dd2cSTrent Piepho return err; 18011c6dd2cSTrent Piepho err = phy_driver_register(&vsc8221_driver); 18111c6dd2cSTrent Piepho if (err < 0) 18211c6dd2cSTrent Piepho phy_driver_unregister(&vsc8244_driver); 18311c6dd2cSTrent Piepho return err; 18411c6dd2cSTrent Piepho } 18511c6dd2cSTrent Piepho 18611c6dd2cSTrent Piepho static void __exit vsc82xx_exit(void) 187ef82a306SJon Loeliger { 188ef82a306SJon Loeliger phy_driver_unregister(&vsc8244_driver); 18911c6dd2cSTrent Piepho phy_driver_unregister(&vsc8221_driver); 190ef82a306SJon Loeliger } 191ef82a306SJon Loeliger 19211c6dd2cSTrent Piepho module_init(vsc82xx_init); 19311c6dd2cSTrent Piepho module_exit(vsc82xx_exit); 1944e4f10f6SDavid Woodhouse 195cf93c945SUwe Kleine-König static struct mdio_device_id __maybe_unused vitesse_tbl[] = { 1964e4f10f6SDavid Woodhouse { PHY_ID_VSC8244, 0x000fffc0 }, 1974e4f10f6SDavid Woodhouse { PHY_ID_VSC8221, 0x000ffff0 }, 1984e4f10f6SDavid Woodhouse { } 1994e4f10f6SDavid Woodhouse }; 2004e4f10f6SDavid Woodhouse 2014e4f10f6SDavid Woodhouse MODULE_DEVICE_TABLE(mdio, vitesse_tbl); 202