100db8189SAndy Fleming /* 200db8189SAndy Fleming * drivers/net/phy/marvell.c 300db8189SAndy Fleming * 400db8189SAndy Fleming * Driver for Marvell PHYs 500db8189SAndy Fleming * 600db8189SAndy Fleming * Author: Andy Fleming 700db8189SAndy Fleming * 800db8189SAndy Fleming * Copyright (c) 2004 Freescale Semiconductor, Inc. 900db8189SAndy Fleming * 1000db8189SAndy Fleming * This program is free software; you can redistribute it and/or modify it 1100db8189SAndy Fleming * under the terms of the GNU General Public License as published by the 1200db8189SAndy Fleming * Free Software Foundation; either version 2 of the License, or (at your 1300db8189SAndy Fleming * option) any later version. 1400db8189SAndy Fleming * 1500db8189SAndy Fleming */ 1600db8189SAndy Fleming #include <linux/kernel.h> 1700db8189SAndy Fleming #include <linux/string.h> 1800db8189SAndy Fleming #include <linux/errno.h> 1900db8189SAndy Fleming #include <linux/unistd.h> 2000db8189SAndy Fleming #include <linux/slab.h> 2100db8189SAndy Fleming #include <linux/interrupt.h> 2200db8189SAndy Fleming #include <linux/init.h> 2300db8189SAndy Fleming #include <linux/delay.h> 2400db8189SAndy Fleming #include <linux/netdevice.h> 2500db8189SAndy Fleming #include <linux/etherdevice.h> 2600db8189SAndy Fleming #include <linux/skbuff.h> 2700db8189SAndy Fleming #include <linux/spinlock.h> 2800db8189SAndy Fleming #include <linux/mm.h> 2900db8189SAndy Fleming #include <linux/module.h> 3000db8189SAndy Fleming #include <linux/mii.h> 3100db8189SAndy Fleming #include <linux/ethtool.h> 3200db8189SAndy Fleming #include <linux/phy.h> 3300db8189SAndy Fleming 3400db8189SAndy Fleming #include <asm/io.h> 3500db8189SAndy Fleming #include <asm/irq.h> 3600db8189SAndy Fleming #include <asm/uaccess.h> 3700db8189SAndy Fleming 3800db8189SAndy Fleming #define MII_M1011_IEVENT 0x13 3900db8189SAndy Fleming #define MII_M1011_IEVENT_CLEAR 0x0000 4000db8189SAndy Fleming 4100db8189SAndy Fleming #define MII_M1011_IMASK 0x12 4200db8189SAndy Fleming #define MII_M1011_IMASK_INIT 0x6400 4300db8189SAndy Fleming #define MII_M1011_IMASK_CLEAR 0x0000 4400db8189SAndy Fleming 4576884679SAndy Fleming #define MII_M1011_PHY_SCR 0x10 4676884679SAndy Fleming #define MII_M1011_PHY_SCR_AUTO_CROSS 0x0060 4776884679SAndy Fleming 4876884679SAndy Fleming #define MII_M1145_PHY_EXT_CR 0x14 4976884679SAndy Fleming #define MII_M1145_RGMII_RX_DELAY 0x0080 5076884679SAndy Fleming #define MII_M1145_RGMII_TX_DELAY 0x0002 5176884679SAndy Fleming 5276884679SAndy Fleming #define M1145_DEV_FLAGS_RESISTANCE 0x00000001 5376884679SAndy Fleming 5476884679SAndy Fleming #define MII_M1111_PHY_LED_CONTROL 0x18 5576884679SAndy Fleming #define MII_M1111_PHY_LED_DIRECT 0x4100 5676884679SAndy Fleming #define MII_M1111_PHY_LED_COMBINE 0x411c 57895ee682SKim Phillips #define MII_M1111_PHY_EXT_CR 0x14 58895ee682SKim Phillips #define MII_M1111_RX_DELAY 0x80 59895ee682SKim Phillips #define MII_M1111_TX_DELAY 0x2 60895ee682SKim Phillips #define MII_M1111_PHY_EXT_SR 0x1b 61895ee682SKim Phillips #define MII_M1111_HWCFG_MODE_MASK 0xf 62895ee682SKim Phillips #define MII_M1111_HWCFG_MODE_RGMII 0xb 6376884679SAndy Fleming 6400db8189SAndy Fleming MODULE_DESCRIPTION("Marvell PHY driver"); 6500db8189SAndy Fleming MODULE_AUTHOR("Andy Fleming"); 6600db8189SAndy Fleming MODULE_LICENSE("GPL"); 6700db8189SAndy Fleming 6800db8189SAndy Fleming static int marvell_ack_interrupt(struct phy_device *phydev) 6900db8189SAndy Fleming { 7000db8189SAndy Fleming int err; 7100db8189SAndy Fleming 7200db8189SAndy Fleming /* Clear the interrupts by reading the reg */ 7300db8189SAndy Fleming err = phy_read(phydev, MII_M1011_IEVENT); 7400db8189SAndy Fleming 7500db8189SAndy Fleming if (err < 0) 7600db8189SAndy Fleming return err; 7700db8189SAndy Fleming 7800db8189SAndy Fleming return 0; 7900db8189SAndy Fleming } 8000db8189SAndy Fleming 8100db8189SAndy Fleming static int marvell_config_intr(struct phy_device *phydev) 8200db8189SAndy Fleming { 8300db8189SAndy Fleming int err; 8400db8189SAndy Fleming 8500db8189SAndy Fleming if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 8600db8189SAndy Fleming err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_INIT); 8700db8189SAndy Fleming else 8800db8189SAndy Fleming err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR); 8900db8189SAndy Fleming 9000db8189SAndy Fleming return err; 9100db8189SAndy Fleming } 9200db8189SAndy Fleming 9300db8189SAndy Fleming static int marvell_config_aneg(struct phy_device *phydev) 9400db8189SAndy Fleming { 9500db8189SAndy Fleming int err; 9600db8189SAndy Fleming 9700db8189SAndy Fleming /* The Marvell PHY has an errata which requires 9800db8189SAndy Fleming * that certain registers get written in order 9900db8189SAndy Fleming * to restart autonegotiation */ 10000db8189SAndy Fleming err = phy_write(phydev, MII_BMCR, BMCR_RESET); 10100db8189SAndy Fleming 10200db8189SAndy Fleming if (err < 0) 10300db8189SAndy Fleming return err; 10400db8189SAndy Fleming 10500db8189SAndy Fleming err = phy_write(phydev, 0x1d, 0x1f); 10600db8189SAndy Fleming if (err < 0) 10700db8189SAndy Fleming return err; 10800db8189SAndy Fleming 10900db8189SAndy Fleming err = phy_write(phydev, 0x1e, 0x200c); 11000db8189SAndy Fleming if (err < 0) 11100db8189SAndy Fleming return err; 11200db8189SAndy Fleming 11300db8189SAndy Fleming err = phy_write(phydev, 0x1d, 0x5); 11400db8189SAndy Fleming if (err < 0) 11500db8189SAndy Fleming return err; 11600db8189SAndy Fleming 11700db8189SAndy Fleming err = phy_write(phydev, 0x1e, 0); 11800db8189SAndy Fleming if (err < 0) 11900db8189SAndy Fleming return err; 12000db8189SAndy Fleming 12100db8189SAndy Fleming err = phy_write(phydev, 0x1e, 0x100); 12200db8189SAndy Fleming if (err < 0) 12300db8189SAndy Fleming return err; 12400db8189SAndy Fleming 12576884679SAndy Fleming err = phy_write(phydev, MII_M1011_PHY_SCR, 12676884679SAndy Fleming MII_M1011_PHY_SCR_AUTO_CROSS); 12776884679SAndy Fleming if (err < 0) 12876884679SAndy Fleming return err; 12976884679SAndy Fleming 13076884679SAndy Fleming err = phy_write(phydev, MII_M1111_PHY_LED_CONTROL, 13176884679SAndy Fleming MII_M1111_PHY_LED_DIRECT); 13276884679SAndy Fleming if (err < 0) 13376884679SAndy Fleming return err; 13400db8189SAndy Fleming 13500db8189SAndy Fleming err = genphy_config_aneg(phydev); 13600db8189SAndy Fleming 13700db8189SAndy Fleming return err; 13800db8189SAndy Fleming } 13900db8189SAndy Fleming 140895ee682SKim Phillips static int m88e1111_config_init(struct phy_device *phydev) 141895ee682SKim Phillips { 142895ee682SKim Phillips int err; 143895ee682SKim Phillips 144895ee682SKim Phillips if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) || 145895ee682SKim Phillips (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)) { 146895ee682SKim Phillips int temp; 147895ee682SKim Phillips 148895ee682SKim Phillips if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) { 149895ee682SKim Phillips temp = phy_read(phydev, MII_M1111_PHY_EXT_CR); 150895ee682SKim Phillips if (temp < 0) 151895ee682SKim Phillips return temp; 152895ee682SKim Phillips 153895ee682SKim Phillips temp |= (MII_M1111_RX_DELAY | MII_M1111_TX_DELAY); 154895ee682SKim Phillips 155895ee682SKim Phillips err = phy_write(phydev, MII_M1111_PHY_EXT_CR, temp); 156895ee682SKim Phillips if (err < 0) 157895ee682SKim Phillips return err; 158895ee682SKim Phillips } 159895ee682SKim Phillips 160895ee682SKim Phillips temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); 161895ee682SKim Phillips if (temp < 0) 162895ee682SKim Phillips return temp; 163895ee682SKim Phillips 164895ee682SKim Phillips temp &= ~(MII_M1111_HWCFG_MODE_MASK); 165895ee682SKim Phillips temp |= MII_M1111_HWCFG_MODE_RGMII; 166895ee682SKim Phillips 167895ee682SKim Phillips err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); 168895ee682SKim Phillips if (err < 0) 169895ee682SKim Phillips return err; 170895ee682SKim Phillips } 171895ee682SKim Phillips 172895ee682SKim Phillips err = phy_write(phydev, MII_BMCR, BMCR_RESET); 173895ee682SKim Phillips if (err < 0) 174895ee682SKim Phillips return err; 175895ee682SKim Phillips 176895ee682SKim Phillips return 0; 177895ee682SKim Phillips } 178895ee682SKim Phillips 17976884679SAndy Fleming static int m88e1145_config_init(struct phy_device *phydev) 18076884679SAndy Fleming { 18176884679SAndy Fleming int err; 18276884679SAndy Fleming 18376884679SAndy Fleming /* Take care of errata E0 & E1 */ 18476884679SAndy Fleming err = phy_write(phydev, 0x1d, 0x001b); 18576884679SAndy Fleming if (err < 0) 18676884679SAndy Fleming return err; 18776884679SAndy Fleming 18876884679SAndy Fleming err = phy_write(phydev, 0x1e, 0x418f); 18976884679SAndy Fleming if (err < 0) 19076884679SAndy Fleming return err; 19176884679SAndy Fleming 19276884679SAndy Fleming err = phy_write(phydev, 0x1d, 0x0016); 19376884679SAndy Fleming if (err < 0) 19476884679SAndy Fleming return err; 19576884679SAndy Fleming 19676884679SAndy Fleming err = phy_write(phydev, 0x1e, 0xa2da); 19776884679SAndy Fleming if (err < 0) 19876884679SAndy Fleming return err; 19976884679SAndy Fleming 200895ee682SKim Phillips if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) { 20176884679SAndy Fleming int temp = phy_read(phydev, MII_M1145_PHY_EXT_CR); 20276884679SAndy Fleming if (temp < 0) 20376884679SAndy Fleming return temp; 20476884679SAndy Fleming 20576884679SAndy Fleming temp |= (MII_M1145_RGMII_RX_DELAY | MII_M1145_RGMII_TX_DELAY); 20676884679SAndy Fleming 20776884679SAndy Fleming err = phy_write(phydev, MII_M1145_PHY_EXT_CR, temp); 20876884679SAndy Fleming if (err < 0) 20976884679SAndy Fleming return err; 21076884679SAndy Fleming 21176884679SAndy Fleming if (phydev->dev_flags & M1145_DEV_FLAGS_RESISTANCE) { 21276884679SAndy Fleming err = phy_write(phydev, 0x1d, 0x0012); 21376884679SAndy Fleming if (err < 0) 21476884679SAndy Fleming return err; 21576884679SAndy Fleming 21676884679SAndy Fleming temp = phy_read(phydev, 0x1e); 21776884679SAndy Fleming if (temp < 0) 21876884679SAndy Fleming return temp; 21976884679SAndy Fleming 22076884679SAndy Fleming temp &= 0xf03f; 22176884679SAndy Fleming temp |= 2 << 9; /* 36 ohm */ 22276884679SAndy Fleming temp |= 2 << 6; /* 39 ohm */ 22376884679SAndy Fleming 22476884679SAndy Fleming err = phy_write(phydev, 0x1e, temp); 22576884679SAndy Fleming if (err < 0) 22676884679SAndy Fleming return err; 22776884679SAndy Fleming 22876884679SAndy Fleming err = phy_write(phydev, 0x1d, 0x3); 22976884679SAndy Fleming if (err < 0) 23076884679SAndy Fleming return err; 23176884679SAndy Fleming 23276884679SAndy Fleming err = phy_write(phydev, 0x1e, 0x8000); 23376884679SAndy Fleming if (err < 0) 23476884679SAndy Fleming return err; 23576884679SAndy Fleming } 23676884679SAndy Fleming } 23776884679SAndy Fleming 23876884679SAndy Fleming return 0; 23976884679SAndy Fleming } 24000db8189SAndy Fleming 24100db8189SAndy Fleming static struct phy_driver m88e1101_driver = { 24276884679SAndy Fleming .phy_id = 0x01410c60, 24376884679SAndy Fleming .phy_id_mask = 0xfffffff0, 24400db8189SAndy Fleming .name = "Marvell 88E1101", 24500db8189SAndy Fleming .features = PHY_GBIT_FEATURES, 24600db8189SAndy Fleming .flags = PHY_HAS_INTERRUPT, 24700db8189SAndy Fleming .config_aneg = &marvell_config_aneg, 24800db8189SAndy Fleming .read_status = &genphy_read_status, 24900db8189SAndy Fleming .ack_interrupt = &marvell_ack_interrupt, 25000db8189SAndy Fleming .config_intr = &marvell_config_intr, 25100db8189SAndy Fleming .driver = {.owner = THIS_MODULE,}, 25200db8189SAndy Fleming }; 25300db8189SAndy Fleming 254895ee682SKim Phillips static struct phy_driver m88e1111_driver = { 25576884679SAndy Fleming .phy_id = 0x01410cc0, 25676884679SAndy Fleming .phy_id_mask = 0xfffffff0, 25776884679SAndy Fleming .name = "Marvell 88E1111", 25876884679SAndy Fleming .features = PHY_GBIT_FEATURES, 25976884679SAndy Fleming .flags = PHY_HAS_INTERRUPT, 26076884679SAndy Fleming .config_aneg = &marvell_config_aneg, 26176884679SAndy Fleming .read_status = &genphy_read_status, 26276884679SAndy Fleming .ack_interrupt = &marvell_ack_interrupt, 26376884679SAndy Fleming .config_intr = &marvell_config_intr, 264895ee682SKim Phillips .config_init = &m88e1111_config_init, 26576884679SAndy Fleming .driver = {.owner = THIS_MODULE,}, 26676884679SAndy Fleming }; 26776884679SAndy Fleming 26876884679SAndy Fleming static struct phy_driver m88e1145_driver = { 26976884679SAndy Fleming .phy_id = 0x01410cd0, 27076884679SAndy Fleming .phy_id_mask = 0xfffffff0, 27176884679SAndy Fleming .name = "Marvell 88E1145", 27276884679SAndy Fleming .features = PHY_GBIT_FEATURES, 27376884679SAndy Fleming .flags = PHY_HAS_INTERRUPT, 27476884679SAndy Fleming .config_init = &m88e1145_config_init, 27576884679SAndy Fleming .config_aneg = &marvell_config_aneg, 27676884679SAndy Fleming .read_status = &genphy_read_status, 27776884679SAndy Fleming .ack_interrupt = &marvell_ack_interrupt, 27876884679SAndy Fleming .config_intr = &marvell_config_intr, 27976884679SAndy Fleming .driver = {.owner = THIS_MODULE,}, 28076884679SAndy Fleming }; 28176884679SAndy Fleming 28200db8189SAndy Fleming static int __init marvell_init(void) 28300db8189SAndy Fleming { 28476884679SAndy Fleming int ret; 28576884679SAndy Fleming 28676884679SAndy Fleming ret = phy_driver_register(&m88e1101_driver); 28776884679SAndy Fleming if (ret) 28876884679SAndy Fleming return ret; 28976884679SAndy Fleming 290895ee682SKim Phillips ret = phy_driver_register(&m88e1111_driver); 29176884679SAndy Fleming if (ret) 292895ee682SKim Phillips goto err1111; 29376884679SAndy Fleming 29476884679SAndy Fleming ret = phy_driver_register(&m88e1145_driver); 29576884679SAndy Fleming if (ret) 29676884679SAndy Fleming goto err1145; 29776884679SAndy Fleming 29876884679SAndy Fleming return 0; 29976884679SAndy Fleming 30076884679SAndy Fleming err1145: 301895ee682SKim Phillips phy_driver_unregister(&m88e1111_driver); 302895ee682SKim Phillips err1111: 30376884679SAndy Fleming phy_driver_unregister(&m88e1101_driver); 30476884679SAndy Fleming return ret; 30500db8189SAndy Fleming } 30600db8189SAndy Fleming 30700db8189SAndy Fleming static void __exit marvell_exit(void) 30800db8189SAndy Fleming { 30900db8189SAndy Fleming phy_driver_unregister(&m88e1101_driver); 310895ee682SKim Phillips phy_driver_unregister(&m88e1111_driver); 31176884679SAndy Fleming phy_driver_unregister(&m88e1145_driver); 31200db8189SAndy Fleming } 31300db8189SAndy Fleming 31400db8189SAndy Fleming module_init(marvell_init); 31500db8189SAndy Fleming module_exit(marvell_exit); 316