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 634117b5beSKapil Juneja #define MII_M1111_HWCFG_MODE_SGMII_NO_CLK 0x4 6476884679SAndy Fleming 6500db8189SAndy Fleming MODULE_DESCRIPTION("Marvell PHY driver"); 6600db8189SAndy Fleming MODULE_AUTHOR("Andy Fleming"); 6700db8189SAndy Fleming MODULE_LICENSE("GPL"); 6800db8189SAndy Fleming 6900db8189SAndy Fleming static int marvell_ack_interrupt(struct phy_device *phydev) 7000db8189SAndy Fleming { 7100db8189SAndy Fleming int err; 7200db8189SAndy Fleming 7300db8189SAndy Fleming /* Clear the interrupts by reading the reg */ 7400db8189SAndy Fleming err = phy_read(phydev, MII_M1011_IEVENT); 7500db8189SAndy Fleming 7600db8189SAndy Fleming if (err < 0) 7700db8189SAndy Fleming return err; 7800db8189SAndy Fleming 7900db8189SAndy Fleming return 0; 8000db8189SAndy Fleming } 8100db8189SAndy Fleming 8200db8189SAndy Fleming static int marvell_config_intr(struct phy_device *phydev) 8300db8189SAndy Fleming { 8400db8189SAndy Fleming int err; 8500db8189SAndy Fleming 8600db8189SAndy Fleming if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 8700db8189SAndy Fleming err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_INIT); 8800db8189SAndy Fleming else 8900db8189SAndy Fleming err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR); 9000db8189SAndy Fleming 9100db8189SAndy Fleming return err; 9200db8189SAndy Fleming } 9300db8189SAndy Fleming 9400db8189SAndy Fleming static int marvell_config_aneg(struct phy_device *phydev) 9500db8189SAndy Fleming { 9600db8189SAndy Fleming int err; 9700db8189SAndy Fleming 9800db8189SAndy Fleming /* The Marvell PHY has an errata which requires 9900db8189SAndy Fleming * that certain registers get written in order 10000db8189SAndy Fleming * to restart autonegotiation */ 10100db8189SAndy Fleming err = phy_write(phydev, MII_BMCR, BMCR_RESET); 10200db8189SAndy Fleming 10300db8189SAndy Fleming if (err < 0) 10400db8189SAndy Fleming return err; 10500db8189SAndy Fleming 10600db8189SAndy Fleming err = phy_write(phydev, 0x1d, 0x1f); 10700db8189SAndy Fleming if (err < 0) 10800db8189SAndy Fleming return err; 10900db8189SAndy Fleming 11000db8189SAndy Fleming err = phy_write(phydev, 0x1e, 0x200c); 11100db8189SAndy Fleming if (err < 0) 11200db8189SAndy Fleming return err; 11300db8189SAndy Fleming 11400db8189SAndy Fleming err = phy_write(phydev, 0x1d, 0x5); 11500db8189SAndy Fleming if (err < 0) 11600db8189SAndy Fleming return err; 11700db8189SAndy Fleming 11800db8189SAndy Fleming err = phy_write(phydev, 0x1e, 0); 11900db8189SAndy Fleming if (err < 0) 12000db8189SAndy Fleming return err; 12100db8189SAndy Fleming 12200db8189SAndy Fleming err = phy_write(phydev, 0x1e, 0x100); 12300db8189SAndy Fleming if (err < 0) 12400db8189SAndy Fleming return err; 12500db8189SAndy Fleming 12676884679SAndy Fleming err = phy_write(phydev, MII_M1011_PHY_SCR, 12776884679SAndy Fleming MII_M1011_PHY_SCR_AUTO_CROSS); 12876884679SAndy Fleming if (err < 0) 12976884679SAndy Fleming return err; 13076884679SAndy Fleming 13176884679SAndy Fleming err = phy_write(phydev, MII_M1111_PHY_LED_CONTROL, 13276884679SAndy Fleming MII_M1111_PHY_LED_DIRECT); 13376884679SAndy Fleming if (err < 0) 13476884679SAndy Fleming return err; 13500db8189SAndy Fleming 13600db8189SAndy Fleming err = genphy_config_aneg(phydev); 13700db8189SAndy Fleming 13800db8189SAndy Fleming return err; 13900db8189SAndy Fleming } 14000db8189SAndy Fleming 141895ee682SKim Phillips static int m88e1111_config_init(struct phy_device *phydev) 142895ee682SKim Phillips { 143895ee682SKim Phillips int err; 144895ee682SKim Phillips 145895ee682SKim Phillips if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) || 146895ee682SKim Phillips (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)) { 147895ee682SKim Phillips int temp; 148895ee682SKim Phillips 149895ee682SKim Phillips if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) { 150895ee682SKim Phillips temp = phy_read(phydev, MII_M1111_PHY_EXT_CR); 151895ee682SKim Phillips if (temp < 0) 152895ee682SKim Phillips return temp; 153895ee682SKim Phillips 154895ee682SKim Phillips temp |= (MII_M1111_RX_DELAY | MII_M1111_TX_DELAY); 155895ee682SKim Phillips 156895ee682SKim Phillips err = phy_write(phydev, MII_M1111_PHY_EXT_CR, temp); 157895ee682SKim Phillips if (err < 0) 158895ee682SKim Phillips return err; 159895ee682SKim Phillips } 160895ee682SKim Phillips 161895ee682SKim Phillips temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); 162895ee682SKim Phillips if (temp < 0) 163895ee682SKim Phillips return temp; 164895ee682SKim Phillips 165895ee682SKim Phillips temp &= ~(MII_M1111_HWCFG_MODE_MASK); 166895ee682SKim Phillips temp |= MII_M1111_HWCFG_MODE_RGMII; 167895ee682SKim Phillips 168895ee682SKim Phillips err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); 169895ee682SKim Phillips if (err < 0) 170895ee682SKim Phillips return err; 171895ee682SKim Phillips } 172895ee682SKim Phillips 1734117b5beSKapil Juneja if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { 1744117b5beSKapil Juneja int temp; 1754117b5beSKapil Juneja 1764117b5beSKapil Juneja temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); 1774117b5beSKapil Juneja if (temp < 0) 1784117b5beSKapil Juneja return temp; 1794117b5beSKapil Juneja 1804117b5beSKapil Juneja temp &= ~(MII_M1111_HWCFG_MODE_MASK); 1814117b5beSKapil Juneja temp |= MII_M1111_HWCFG_MODE_SGMII_NO_CLK; 1824117b5beSKapil Juneja 1834117b5beSKapil Juneja err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); 1844117b5beSKapil Juneja if (err < 0) 1854117b5beSKapil Juneja return err; 1864117b5beSKapil Juneja } 1874117b5beSKapil Juneja 188895ee682SKim Phillips err = phy_write(phydev, MII_BMCR, BMCR_RESET); 189895ee682SKim Phillips if (err < 0) 190895ee682SKim Phillips return err; 191895ee682SKim Phillips 192895ee682SKim Phillips return 0; 193895ee682SKim Phillips } 194895ee682SKim Phillips 19576884679SAndy Fleming static int m88e1145_config_init(struct phy_device *phydev) 19676884679SAndy Fleming { 19776884679SAndy Fleming int err; 19876884679SAndy Fleming 19976884679SAndy Fleming /* Take care of errata E0 & E1 */ 20076884679SAndy Fleming err = phy_write(phydev, 0x1d, 0x001b); 20176884679SAndy Fleming if (err < 0) 20276884679SAndy Fleming return err; 20376884679SAndy Fleming 20476884679SAndy Fleming err = phy_write(phydev, 0x1e, 0x418f); 20576884679SAndy Fleming if (err < 0) 20676884679SAndy Fleming return err; 20776884679SAndy Fleming 20876884679SAndy Fleming err = phy_write(phydev, 0x1d, 0x0016); 20976884679SAndy Fleming if (err < 0) 21076884679SAndy Fleming return err; 21176884679SAndy Fleming 21276884679SAndy Fleming err = phy_write(phydev, 0x1e, 0xa2da); 21376884679SAndy Fleming if (err < 0) 21476884679SAndy Fleming return err; 21576884679SAndy Fleming 216895ee682SKim Phillips if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) { 21776884679SAndy Fleming int temp = phy_read(phydev, MII_M1145_PHY_EXT_CR); 21876884679SAndy Fleming if (temp < 0) 21976884679SAndy Fleming return temp; 22076884679SAndy Fleming 22176884679SAndy Fleming temp |= (MII_M1145_RGMII_RX_DELAY | MII_M1145_RGMII_TX_DELAY); 22276884679SAndy Fleming 22376884679SAndy Fleming err = phy_write(phydev, MII_M1145_PHY_EXT_CR, temp); 22476884679SAndy Fleming if (err < 0) 22576884679SAndy Fleming return err; 22676884679SAndy Fleming 22776884679SAndy Fleming if (phydev->dev_flags & M1145_DEV_FLAGS_RESISTANCE) { 22876884679SAndy Fleming err = phy_write(phydev, 0x1d, 0x0012); 22976884679SAndy Fleming if (err < 0) 23076884679SAndy Fleming return err; 23176884679SAndy Fleming 23276884679SAndy Fleming temp = phy_read(phydev, 0x1e); 23376884679SAndy Fleming if (temp < 0) 23476884679SAndy Fleming return temp; 23576884679SAndy Fleming 23676884679SAndy Fleming temp &= 0xf03f; 23776884679SAndy Fleming temp |= 2 << 9; /* 36 ohm */ 23876884679SAndy Fleming temp |= 2 << 6; /* 39 ohm */ 23976884679SAndy Fleming 24076884679SAndy Fleming err = phy_write(phydev, 0x1e, temp); 24176884679SAndy Fleming if (err < 0) 24276884679SAndy Fleming return err; 24376884679SAndy Fleming 24476884679SAndy Fleming err = phy_write(phydev, 0x1d, 0x3); 24576884679SAndy Fleming if (err < 0) 24676884679SAndy Fleming return err; 24776884679SAndy Fleming 24876884679SAndy Fleming err = phy_write(phydev, 0x1e, 0x8000); 24976884679SAndy Fleming if (err < 0) 25076884679SAndy Fleming return err; 25176884679SAndy Fleming } 25276884679SAndy Fleming } 25376884679SAndy Fleming 25476884679SAndy Fleming return 0; 25576884679SAndy Fleming } 25600db8189SAndy Fleming 257e5479239SOlof Johansson static struct phy_driver marvell_drivers[] = { 258e5479239SOlof Johansson { 25976884679SAndy Fleming .phy_id = 0x01410c60, 26076884679SAndy Fleming .phy_id_mask = 0xfffffff0, 26100db8189SAndy Fleming .name = "Marvell 88E1101", 26200db8189SAndy Fleming .features = PHY_GBIT_FEATURES, 26300db8189SAndy Fleming .flags = PHY_HAS_INTERRUPT, 26400db8189SAndy Fleming .config_aneg = &marvell_config_aneg, 26500db8189SAndy Fleming .read_status = &genphy_read_status, 26600db8189SAndy Fleming .ack_interrupt = &marvell_ack_interrupt, 26700db8189SAndy Fleming .config_intr = &marvell_config_intr, 268ac8c635aSOlof Johansson .driver = { .owner = THIS_MODULE }, 269e5479239SOlof Johansson }, 270e5479239SOlof Johansson { 27185cfb534SOlof Johansson .phy_id = 0x01410c90, 27285cfb534SOlof Johansson .phy_id_mask = 0xfffffff0, 27385cfb534SOlof Johansson .name = "Marvell 88E1112", 27485cfb534SOlof Johansson .features = PHY_GBIT_FEATURES, 27585cfb534SOlof Johansson .flags = PHY_HAS_INTERRUPT, 27685cfb534SOlof Johansson .config_init = &m88e1111_config_init, 27785cfb534SOlof Johansson .config_aneg = &marvell_config_aneg, 27885cfb534SOlof Johansson .read_status = &genphy_read_status, 27985cfb534SOlof Johansson .ack_interrupt = &marvell_ack_interrupt, 28085cfb534SOlof Johansson .config_intr = &marvell_config_intr, 281ac8c635aSOlof Johansson .driver = { .owner = THIS_MODULE }, 28285cfb534SOlof Johansson }, 28385cfb534SOlof Johansson { 28476884679SAndy Fleming .phy_id = 0x01410cc0, 28576884679SAndy Fleming .phy_id_mask = 0xfffffff0, 28676884679SAndy Fleming .name = "Marvell 88E1111", 28776884679SAndy Fleming .features = PHY_GBIT_FEATURES, 28876884679SAndy Fleming .flags = PHY_HAS_INTERRUPT, 289e5479239SOlof Johansson .config_init = &m88e1111_config_init, 29076884679SAndy Fleming .config_aneg = &marvell_config_aneg, 29176884679SAndy Fleming .read_status = &genphy_read_status, 29276884679SAndy Fleming .ack_interrupt = &marvell_ack_interrupt, 29376884679SAndy Fleming .config_intr = &marvell_config_intr, 294ac8c635aSOlof Johansson .driver = { .owner = THIS_MODULE }, 295e5479239SOlof Johansson }, 296e5479239SOlof Johansson { 29776884679SAndy Fleming .phy_id = 0x01410cd0, 29876884679SAndy Fleming .phy_id_mask = 0xfffffff0, 29976884679SAndy Fleming .name = "Marvell 88E1145", 30076884679SAndy Fleming .features = PHY_GBIT_FEATURES, 30176884679SAndy Fleming .flags = PHY_HAS_INTERRUPT, 30276884679SAndy Fleming .config_init = &m88e1145_config_init, 30376884679SAndy Fleming .config_aneg = &marvell_config_aneg, 30476884679SAndy Fleming .read_status = &genphy_read_status, 30576884679SAndy Fleming .ack_interrupt = &marvell_ack_interrupt, 30676884679SAndy Fleming .config_intr = &marvell_config_intr, 307ac8c635aSOlof Johansson .driver = { .owner = THIS_MODULE }, 308ac8c635aSOlof Johansson }, 309ac8c635aSOlof Johansson { 310ac8c635aSOlof Johansson .phy_id = 0x01410e30, 311ac8c635aSOlof Johansson .phy_id_mask = 0xfffffff0, 312ac8c635aSOlof Johansson .name = "Marvell 88E1240", 313ac8c635aSOlof Johansson .features = PHY_GBIT_FEATURES, 314ac8c635aSOlof Johansson .flags = PHY_HAS_INTERRUPT, 315ac8c635aSOlof Johansson .config_init = &m88e1111_config_init, 316ac8c635aSOlof Johansson .config_aneg = &marvell_config_aneg, 317ac8c635aSOlof Johansson .read_status = &genphy_read_status, 318ac8c635aSOlof Johansson .ack_interrupt = &marvell_ack_interrupt, 319ac8c635aSOlof Johansson .config_intr = &marvell_config_intr, 320ac8c635aSOlof Johansson .driver = { .owner = THIS_MODULE }, 321ac8c635aSOlof Johansson }, 32276884679SAndy Fleming }; 32376884679SAndy Fleming 32400db8189SAndy Fleming static int __init marvell_init(void) 32500db8189SAndy Fleming { 32676884679SAndy Fleming int ret; 327e5479239SOlof Johansson int i; 32876884679SAndy Fleming 329e5479239SOlof Johansson for (i = 0; i < ARRAY_SIZE(marvell_drivers); i++) { 330e5479239SOlof Johansson ret = phy_driver_register(&marvell_drivers[i]); 331e5479239SOlof Johansson 332e5479239SOlof Johansson if (ret) { 333e5479239SOlof Johansson while (i-- > 0) 334e5479239SOlof Johansson phy_driver_unregister(&marvell_drivers[i]); 33576884679SAndy Fleming return ret; 336e5479239SOlof Johansson } 337e5479239SOlof Johansson } 33876884679SAndy Fleming 33976884679SAndy Fleming return 0; 34000db8189SAndy Fleming } 34100db8189SAndy Fleming 34200db8189SAndy Fleming static void __exit marvell_exit(void) 34300db8189SAndy Fleming { 344e5479239SOlof Johansson int i; 345e5479239SOlof Johansson 346e5479239SOlof Johansson for (i = 0; i < ARRAY_SIZE(marvell_drivers); i++) 347e5479239SOlof Johansson phy_driver_unregister(&marvell_drivers[i]); 34800db8189SAndy Fleming } 34900db8189SAndy Fleming 35000db8189SAndy Fleming module_init(marvell_init); 35100db8189SAndy Fleming module_exit(marvell_exit); 352