1ef82a306SJon Loeliger /* 2ef82a306SJon Loeliger * Driver for Vitesse PHYs 3ef82a306SJon Loeliger * 4ef82a306SJon Loeliger * Author: Kriston Carson 5ef82a306SJon Loeliger * 6fddf86fcSAndy Fleming * Copyright (c) 2005, 2009 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 64fddf86fcSAndy Fleming int vsc824x_add_skew(struct phy_device *phydev) 65fddf86fcSAndy Fleming { 66fddf86fcSAndy Fleming int err; 67fddf86fcSAndy Fleming int extcon; 68fddf86fcSAndy Fleming 69fddf86fcSAndy Fleming extcon = phy_read(phydev, MII_VSC8244_EXT_CON1); 70fddf86fcSAndy Fleming 71fddf86fcSAndy Fleming if (extcon < 0) 72fddf86fcSAndy Fleming return extcon; 73fddf86fcSAndy Fleming 74fddf86fcSAndy Fleming extcon &= ~(MII_VSC8244_EXTCON1_TX_SKEW_MASK | 75fddf86fcSAndy Fleming MII_VSC8244_EXTCON1_RX_SKEW_MASK); 76fddf86fcSAndy Fleming 77fddf86fcSAndy Fleming extcon |= (MII_VSC8244_EXTCON1_TX_SKEW | 78fddf86fcSAndy Fleming MII_VSC8244_EXTCON1_RX_SKEW); 79fddf86fcSAndy Fleming 80fddf86fcSAndy Fleming err = phy_write(phydev, MII_VSC8244_EXT_CON1, extcon); 81fddf86fcSAndy Fleming 82fddf86fcSAndy Fleming return err; 83fddf86fcSAndy Fleming } 84fddf86fcSAndy Fleming EXPORT_SYMBOL(vsc824x_add_skew); 85fddf86fcSAndy Fleming 86ef82a306SJon Loeliger static int vsc824x_config_init(struct phy_device *phydev) 87ef82a306SJon Loeliger { 88ef82a306SJon Loeliger int err; 89ef82a306SJon Loeliger 90ef82a306SJon Loeliger err = phy_write(phydev, MII_VSC8244_AUX_CONSTAT, 91ef82a306SJon Loeliger MII_VSC8244_AUXCONSTAT_INIT); 92ef82a306SJon Loeliger if (err < 0) 93ef82a306SJon Loeliger return err; 94ef82a306SJon Loeliger 95af2d940dSAndy Fleming if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) 96fddf86fcSAndy Fleming err = vsc824x_add_skew(phydev); 97af2d940dSAndy Fleming 98ef82a306SJon Loeliger return err; 99ef82a306SJon Loeliger } 100ef82a306SJon Loeliger 101ef82a306SJon Loeliger static int vsc824x_ack_interrupt(struct phy_device *phydev) 102ef82a306SJon Loeliger { 1031d5e83aaSAndy Fleming int err = 0; 1041d5e83aaSAndy Fleming 1051d5e83aaSAndy Fleming /* 1061d5e83aaSAndy Fleming * Don't bother to ACK the interrupts if interrupts 1071d5e83aaSAndy Fleming * are disabled. The 824x cannot clear the interrupts 1081d5e83aaSAndy Fleming * if they are disabled. 1091d5e83aaSAndy Fleming */ 1101d5e83aaSAndy Fleming if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 1111d5e83aaSAndy Fleming err = phy_read(phydev, MII_VSC8244_ISTAT); 112ef82a306SJon Loeliger 113ef82a306SJon Loeliger return (err < 0) ? err : 0; 114ef82a306SJon Loeliger } 115ef82a306SJon Loeliger 11611c6dd2cSTrent Piepho static int vsc82xx_config_intr(struct phy_device *phydev) 117ef82a306SJon Loeliger { 118ef82a306SJon Loeliger int err; 119ef82a306SJon Loeliger 120ef82a306SJon Loeliger if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 121ef82a306SJon Loeliger err = phy_write(phydev, MII_VSC8244_IMASK, 12211c6dd2cSTrent Piepho phydev->drv->phy_id == PHY_ID_VSC8244 ? 12311c6dd2cSTrent Piepho MII_VSC8244_IMASK_MASK : 12411c6dd2cSTrent Piepho MII_VSC8221_IMASK_MASK); 1251d5e83aaSAndy Fleming else { 1261d5e83aaSAndy Fleming /* 1271d5e83aaSAndy Fleming * The Vitesse PHY cannot clear the interrupt 1281d5e83aaSAndy Fleming * once it has disabled them, so we clear them first 1291d5e83aaSAndy Fleming */ 1301d5e83aaSAndy Fleming err = phy_read(phydev, MII_VSC8244_ISTAT); 1311d5e83aaSAndy Fleming 13252cb1c2bSAndy Fleming if (err < 0) 1331d5e83aaSAndy Fleming return err; 1341d5e83aaSAndy Fleming 135ef82a306SJon Loeliger err = phy_write(phydev, MII_VSC8244_IMASK, 0); 1361d5e83aaSAndy Fleming } 1371d5e83aaSAndy Fleming 138ef82a306SJon Loeliger return err; 139ef82a306SJon Loeliger } 140ef82a306SJon Loeliger 141ef82a306SJon Loeliger /* Vitesse 824x */ 142ef82a306SJon Loeliger static struct phy_driver vsc8244_driver = { 14311c6dd2cSTrent Piepho .phy_id = PHY_ID_VSC8244, 144ef82a306SJon Loeliger .name = "Vitesse VSC8244", 145ef82a306SJon Loeliger .phy_id_mask = 0x000fffc0, 146ef82a306SJon Loeliger .features = PHY_GBIT_FEATURES, 147ef82a306SJon Loeliger .flags = PHY_HAS_INTERRUPT, 148ef82a306SJon Loeliger .config_init = &vsc824x_config_init, 149ef82a306SJon Loeliger .config_aneg = &genphy_config_aneg, 150ef82a306SJon Loeliger .read_status = &genphy_read_status, 151ef82a306SJon Loeliger .ack_interrupt = &vsc824x_ack_interrupt, 15211c6dd2cSTrent Piepho .config_intr = &vsc82xx_config_intr, 153ef82a306SJon Loeliger .driver = { .owner = THIS_MODULE,}, 154ef82a306SJon Loeliger }; 155ef82a306SJon Loeliger 15611c6dd2cSTrent Piepho static int vsc8221_config_init(struct phy_device *phydev) 157ef82a306SJon Loeliger { 15811c6dd2cSTrent Piepho int err; 15911c6dd2cSTrent Piepho 16011c6dd2cSTrent Piepho err = phy_write(phydev, MII_VSC8244_AUX_CONSTAT, 16111c6dd2cSTrent Piepho MII_VSC8221_AUXCONSTAT_INIT); 16211c6dd2cSTrent Piepho return err; 16311c6dd2cSTrent Piepho 16411c6dd2cSTrent Piepho /* Perhaps we should set EXT_CON1 based on the interface? 16511c6dd2cSTrent Piepho Options are 802.3Z SerDes or SGMII */ 166ef82a306SJon Loeliger } 167ef82a306SJon Loeliger 16811c6dd2cSTrent Piepho /* Vitesse 8221 */ 16911c6dd2cSTrent Piepho static struct phy_driver vsc8221_driver = { 17011c6dd2cSTrent Piepho .phy_id = PHY_ID_VSC8221, 17111c6dd2cSTrent Piepho .phy_id_mask = 0x000ffff0, 17211c6dd2cSTrent Piepho .name = "Vitesse VSC8221", 17311c6dd2cSTrent Piepho .features = PHY_GBIT_FEATURES, 17411c6dd2cSTrent Piepho .flags = PHY_HAS_INTERRUPT, 17511c6dd2cSTrent Piepho .config_init = &vsc8221_config_init, 17611c6dd2cSTrent Piepho .config_aneg = &genphy_config_aneg, 17711c6dd2cSTrent Piepho .read_status = &genphy_read_status, 17811c6dd2cSTrent Piepho .ack_interrupt = &vsc824x_ack_interrupt, 17911c6dd2cSTrent Piepho .config_intr = &vsc82xx_config_intr, 18011c6dd2cSTrent Piepho .driver = { .owner = THIS_MODULE,}, 18111c6dd2cSTrent Piepho }; 18211c6dd2cSTrent Piepho 18311c6dd2cSTrent Piepho static int __init vsc82xx_init(void) 18411c6dd2cSTrent Piepho { 18511c6dd2cSTrent Piepho int err; 18611c6dd2cSTrent Piepho 18711c6dd2cSTrent Piepho err = phy_driver_register(&vsc8244_driver); 18811c6dd2cSTrent Piepho if (err < 0) 18911c6dd2cSTrent Piepho return err; 19011c6dd2cSTrent Piepho err = phy_driver_register(&vsc8221_driver); 19111c6dd2cSTrent Piepho if (err < 0) 19211c6dd2cSTrent Piepho phy_driver_unregister(&vsc8244_driver); 19311c6dd2cSTrent Piepho return err; 19411c6dd2cSTrent Piepho } 19511c6dd2cSTrent Piepho 19611c6dd2cSTrent Piepho static void __exit vsc82xx_exit(void) 197ef82a306SJon Loeliger { 198ef82a306SJon Loeliger phy_driver_unregister(&vsc8244_driver); 19911c6dd2cSTrent Piepho phy_driver_unregister(&vsc8221_driver); 200ef82a306SJon Loeliger } 201ef82a306SJon Loeliger 20211c6dd2cSTrent Piepho module_init(vsc82xx_init); 20311c6dd2cSTrent Piepho module_exit(vsc82xx_exit); 2044e4f10f6SDavid Woodhouse 205cf93c945SUwe Kleine-König static struct mdio_device_id __maybe_unused vitesse_tbl[] = { 2064e4f10f6SDavid Woodhouse { PHY_ID_VSC8244, 0x000fffc0 }, 2074e4f10f6SDavid Woodhouse { PHY_ID_VSC8221, 0x000ffff0 }, 2084e4f10f6SDavid Woodhouse { } 2094e4f10f6SDavid Woodhouse }; 2104e4f10f6SDavid Woodhouse 2114e4f10f6SDavid Woodhouse MODULE_DEVICE_TABLE(mdio, vitesse_tbl); 212