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 * 103871c387SMichael Stapelberg * Copyright (c) 2013 Michael Stapelberg <michael@stapelberg.de> 113871c387SMichael Stapelberg * 1200db8189SAndy Fleming * This program is free software; you can redistribute it and/or modify it 1300db8189SAndy Fleming * under the terms of the GNU General Public License as published by the 1400db8189SAndy Fleming * Free Software Foundation; either version 2 of the License, or (at your 1500db8189SAndy Fleming * option) any later version. 1600db8189SAndy Fleming * 1700db8189SAndy Fleming */ 1800db8189SAndy Fleming #include <linux/kernel.h> 1900db8189SAndy Fleming #include <linux/string.h> 2000db8189SAndy Fleming #include <linux/errno.h> 2100db8189SAndy Fleming #include <linux/unistd.h> 2200db8189SAndy Fleming #include <linux/interrupt.h> 2300db8189SAndy Fleming #include <linux/init.h> 2400db8189SAndy Fleming #include <linux/delay.h> 2500db8189SAndy Fleming #include <linux/netdevice.h> 2600db8189SAndy Fleming #include <linux/etherdevice.h> 2700db8189SAndy Fleming #include <linux/skbuff.h> 2800db8189SAndy Fleming #include <linux/spinlock.h> 2900db8189SAndy Fleming #include <linux/mm.h> 3000db8189SAndy Fleming #include <linux/module.h> 3100db8189SAndy Fleming #include <linux/mii.h> 3200db8189SAndy Fleming #include <linux/ethtool.h> 3300db8189SAndy Fleming #include <linux/phy.h> 342f495c39SBenjamin Herrenschmidt #include <linux/marvell_phy.h> 35cf41a51dSDavid Daney #include <linux/of.h> 3600db8189SAndy Fleming 37eea3b201SAvinash Kumar #include <linux/io.h> 3800db8189SAndy Fleming #include <asm/irq.h> 39eea3b201SAvinash Kumar #include <linux/uaccess.h> 4000db8189SAndy Fleming 4127d916d6SDavid Daney #define MII_MARVELL_PHY_PAGE 22 4227d916d6SDavid Daney 4300db8189SAndy Fleming #define MII_M1011_IEVENT 0x13 4400db8189SAndy Fleming #define MII_M1011_IEVENT_CLEAR 0x0000 4500db8189SAndy Fleming 4600db8189SAndy Fleming #define MII_M1011_IMASK 0x12 4700db8189SAndy Fleming #define MII_M1011_IMASK_INIT 0x6400 4800db8189SAndy Fleming #define MII_M1011_IMASK_CLEAR 0x0000 4900db8189SAndy Fleming 5076884679SAndy Fleming #define MII_M1011_PHY_SCR 0x10 51239aa55bSDavid Thomson #define MII_M1011_PHY_SCR_MDI 0x0000 52239aa55bSDavid Thomson #define MII_M1011_PHY_SCR_MDI_X 0x0020 5376884679SAndy Fleming #define MII_M1011_PHY_SCR_AUTO_CROSS 0x0060 5476884679SAndy Fleming 5507151bc9SMadalin Bucur #define MII_M1145_PHY_EXT_ADDR_PAGE 0x16 56b0224175SViet Nga Dao #define MII_M1145_PHY_EXT_SR 0x1b 5776884679SAndy Fleming #define MII_M1145_PHY_EXT_CR 0x14 5876884679SAndy Fleming #define MII_M1145_RGMII_RX_DELAY 0x0080 5976884679SAndy Fleming #define MII_M1145_RGMII_TX_DELAY 0x0002 60b0224175SViet Nga Dao #define MII_M1145_HWCFG_MODE_SGMII_NO_CLK 0x4 61b0224175SViet Nga Dao #define MII_M1145_HWCFG_MODE_MASK 0xf 62b0224175SViet Nga Dao #define MII_M1145_HWCFG_FIBER_COPPER_AUTO 0x8000 6376884679SAndy Fleming 6499d881f9SVince Bridgers #define MII_M1145_HWCFG_MODE_SGMII_NO_CLK 0x4 6599d881f9SVince Bridgers #define MII_M1145_HWCFG_MODE_MASK 0xf 6699d881f9SVince Bridgers #define MII_M1145_HWCFG_FIBER_COPPER_AUTO 0x8000 6799d881f9SVince Bridgers 6876884679SAndy Fleming #define MII_M1111_PHY_LED_CONTROL 0x18 6976884679SAndy Fleming #define MII_M1111_PHY_LED_DIRECT 0x4100 7076884679SAndy Fleming #define MII_M1111_PHY_LED_COMBINE 0x411c 71895ee682SKim Phillips #define MII_M1111_PHY_EXT_CR 0x14 72895ee682SKim Phillips #define MII_M1111_RX_DELAY 0x80 73895ee682SKim Phillips #define MII_M1111_TX_DELAY 0x2 74895ee682SKim Phillips #define MII_M1111_PHY_EXT_SR 0x1b 75be937f1fSAlexandr Smirnov 76895ee682SKim Phillips #define MII_M1111_HWCFG_MODE_MASK 0xf 77be937f1fSAlexandr Smirnov #define MII_M1111_HWCFG_MODE_COPPER_RGMII 0xb 78be937f1fSAlexandr Smirnov #define MII_M1111_HWCFG_MODE_FIBER_RGMII 0x3 794117b5beSKapil Juneja #define MII_M1111_HWCFG_MODE_SGMII_NO_CLK 0x4 805f8cbc13SLiu Yu-B13201 #define MII_M1111_HWCFG_MODE_COPPER_RTBI 0x9 81be937f1fSAlexandr Smirnov #define MII_M1111_HWCFG_FIBER_COPPER_AUTO 0x8000 82be937f1fSAlexandr Smirnov #define MII_M1111_HWCFG_FIBER_COPPER_RES 0x2000 83be937f1fSAlexandr Smirnov 84be937f1fSAlexandr Smirnov #define MII_M1111_COPPER 0 85be937f1fSAlexandr Smirnov #define MII_M1111_FIBER 1 86be937f1fSAlexandr Smirnov 87c477d044SCyril Chemparathy #define MII_88E1121_PHY_MSCR_PAGE 2 88c477d044SCyril Chemparathy #define MII_88E1121_PHY_MSCR_REG 21 89c477d044SCyril Chemparathy #define MII_88E1121_PHY_MSCR_RX_DELAY BIT(5) 90c477d044SCyril Chemparathy #define MII_88E1121_PHY_MSCR_TX_DELAY BIT(4) 91c477d044SCyril Chemparathy #define MII_88E1121_PHY_MSCR_DELAY_MASK (~(0x3 << 4)) 92c477d044SCyril Chemparathy 93337ac9d5SCyril Chemparathy #define MII_88E1318S_PHY_MSCR1_REG 16 94337ac9d5SCyril Chemparathy #define MII_88E1318S_PHY_MSCR1_PAD_ODD BIT(6) 953ff1c259SCyril Chemparathy 963871c387SMichael Stapelberg /* Copper Specific Interrupt Enable Register */ 973871c387SMichael Stapelberg #define MII_88E1318S_PHY_CSIER 0x12 983871c387SMichael Stapelberg /* WOL Event Interrupt Enable */ 993871c387SMichael Stapelberg #define MII_88E1318S_PHY_CSIER_WOL_EIE BIT(7) 1003871c387SMichael Stapelberg 1013871c387SMichael Stapelberg /* LED Timer Control Register */ 1023871c387SMichael Stapelberg #define MII_88E1318S_PHY_LED_PAGE 0x03 1033871c387SMichael Stapelberg #define MII_88E1318S_PHY_LED_TCR 0x12 1043871c387SMichael Stapelberg #define MII_88E1318S_PHY_LED_TCR_FORCE_INT BIT(15) 1053871c387SMichael Stapelberg #define MII_88E1318S_PHY_LED_TCR_INTn_ENABLE BIT(7) 1063871c387SMichael Stapelberg #define MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW BIT(11) 1073871c387SMichael Stapelberg 1083871c387SMichael Stapelberg /* Magic Packet MAC address registers */ 1093871c387SMichael Stapelberg #define MII_88E1318S_PHY_MAGIC_PACKET_WORD2 0x17 1103871c387SMichael Stapelberg #define MII_88E1318S_PHY_MAGIC_PACKET_WORD1 0x18 1113871c387SMichael Stapelberg #define MII_88E1318S_PHY_MAGIC_PACKET_WORD0 0x19 1123871c387SMichael Stapelberg 1133871c387SMichael Stapelberg #define MII_88E1318S_PHY_WOL_PAGE 0x11 1143871c387SMichael Stapelberg #define MII_88E1318S_PHY_WOL_CTRL 0x10 1153871c387SMichael Stapelberg #define MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS BIT(12) 1163871c387SMichael Stapelberg #define MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE BIT(14) 1173871c387SMichael Stapelberg 118140bc929SSergei Poselenov #define MII_88E1121_PHY_LED_CTRL 16 119140bc929SSergei Poselenov #define MII_88E1121_PHY_LED_PAGE 3 120140bc929SSergei Poselenov #define MII_88E1121_PHY_LED_DEF 0x0030 121140bc929SSergei Poselenov 122be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS 0x11 123be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_1000 0x8000 124be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_100 0x4000 125be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_SPD_MASK 0xc000 126be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_FULLDUPLEX 0x2000 127be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_RESOLVED 0x0800 128be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_LINK 0x0400 129be937f1fSAlexandr Smirnov 1303da09a51SMichal Simek #define MII_M1116R_CONTROL_REG_MAC 21 1313da09a51SMichal Simek 1326b358aedSSebastian Hesselbarth #define MII_88E3016_PHY_SPEC_CTRL 0x10 1336b358aedSSebastian Hesselbarth #define MII_88E3016_DISABLE_SCRAMBLER 0x0200 1346b358aedSSebastian Hesselbarth #define MII_88E3016_AUTO_MDIX_CROSSOVER 0x0030 13576884679SAndy Fleming 136930b37eeSStefan Roese #define MII_88E1510_GEN_CTRL_REG_1 0x14 137930b37eeSStefan Roese #define MII_88E1510_GEN_CTRL_REG_1_MODE_MASK 0x7 138930b37eeSStefan Roese #define MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII 0x1 /* SGMII to copper */ 139930b37eeSStefan Roese #define MII_88E1510_GEN_CTRL_REG_1_RESET 0x8000 /* Soft reset */ 140930b37eeSStefan Roese 14100db8189SAndy Fleming MODULE_DESCRIPTION("Marvell PHY driver"); 14200db8189SAndy Fleming MODULE_AUTHOR("Andy Fleming"); 14300db8189SAndy Fleming MODULE_LICENSE("GPL"); 14400db8189SAndy Fleming 145d2fa47d9SAndrew Lunn struct marvell_hw_stat { 146d2fa47d9SAndrew Lunn const char *string; 147d2fa47d9SAndrew Lunn u8 page; 148d2fa47d9SAndrew Lunn u8 reg; 149d2fa47d9SAndrew Lunn u8 bits; 150d2fa47d9SAndrew Lunn }; 151d2fa47d9SAndrew Lunn 152d2fa47d9SAndrew Lunn static struct marvell_hw_stat marvell_hw_stats[] = { 153d2fa47d9SAndrew Lunn { "phy_receive_errors", 0, 21, 16}, 154d2fa47d9SAndrew Lunn { "phy_idle_errors", 0, 10, 8 }, 155d2fa47d9SAndrew Lunn }; 156d2fa47d9SAndrew Lunn 157d2fa47d9SAndrew Lunn struct marvell_priv { 158d2fa47d9SAndrew Lunn u64 stats[ARRAY_SIZE(marvell_hw_stats)]; 159d2fa47d9SAndrew Lunn }; 160d2fa47d9SAndrew Lunn 16100db8189SAndy Fleming static int marvell_ack_interrupt(struct phy_device *phydev) 16200db8189SAndy Fleming { 16300db8189SAndy Fleming int err; 16400db8189SAndy Fleming 16500db8189SAndy Fleming /* Clear the interrupts by reading the reg */ 16600db8189SAndy Fleming err = phy_read(phydev, MII_M1011_IEVENT); 16700db8189SAndy Fleming 16800db8189SAndy Fleming if (err < 0) 16900db8189SAndy Fleming return err; 17000db8189SAndy Fleming 17100db8189SAndy Fleming return 0; 17200db8189SAndy Fleming } 17300db8189SAndy Fleming 17400db8189SAndy Fleming static int marvell_config_intr(struct phy_device *phydev) 17500db8189SAndy Fleming { 17600db8189SAndy Fleming int err; 17700db8189SAndy Fleming 17800db8189SAndy Fleming if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 17900db8189SAndy Fleming err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_INIT); 18000db8189SAndy Fleming else 18100db8189SAndy Fleming err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR); 18200db8189SAndy Fleming 18300db8189SAndy Fleming return err; 18400db8189SAndy Fleming } 18500db8189SAndy Fleming 186239aa55bSDavid Thomson static int marvell_set_polarity(struct phy_device *phydev, int polarity) 187239aa55bSDavid Thomson { 188239aa55bSDavid Thomson int reg; 189239aa55bSDavid Thomson int err; 190239aa55bSDavid Thomson int val; 191239aa55bSDavid Thomson 192239aa55bSDavid Thomson /* get the current settings */ 193239aa55bSDavid Thomson reg = phy_read(phydev, MII_M1011_PHY_SCR); 194239aa55bSDavid Thomson if (reg < 0) 195239aa55bSDavid Thomson return reg; 196239aa55bSDavid Thomson 197239aa55bSDavid Thomson val = reg; 198239aa55bSDavid Thomson val &= ~MII_M1011_PHY_SCR_AUTO_CROSS; 199239aa55bSDavid Thomson switch (polarity) { 200239aa55bSDavid Thomson case ETH_TP_MDI: 201239aa55bSDavid Thomson val |= MII_M1011_PHY_SCR_MDI; 202239aa55bSDavid Thomson break; 203239aa55bSDavid Thomson case ETH_TP_MDI_X: 204239aa55bSDavid Thomson val |= MII_M1011_PHY_SCR_MDI_X; 205239aa55bSDavid Thomson break; 206239aa55bSDavid Thomson case ETH_TP_MDI_AUTO: 207239aa55bSDavid Thomson case ETH_TP_MDI_INVALID: 208239aa55bSDavid Thomson default: 209239aa55bSDavid Thomson val |= MII_M1011_PHY_SCR_AUTO_CROSS; 210239aa55bSDavid Thomson break; 211239aa55bSDavid Thomson } 212239aa55bSDavid Thomson 213239aa55bSDavid Thomson if (val != reg) { 214239aa55bSDavid Thomson /* Set the new polarity value in the register */ 215239aa55bSDavid Thomson err = phy_write(phydev, MII_M1011_PHY_SCR, val); 216239aa55bSDavid Thomson if (err) 217239aa55bSDavid Thomson return err; 218239aa55bSDavid Thomson } 219239aa55bSDavid Thomson 220239aa55bSDavid Thomson return 0; 221239aa55bSDavid Thomson } 222239aa55bSDavid Thomson 22300db8189SAndy Fleming static int marvell_config_aneg(struct phy_device *phydev) 22400db8189SAndy Fleming { 22500db8189SAndy Fleming int err; 22600db8189SAndy Fleming 22700db8189SAndy Fleming /* The Marvell PHY has an errata which requires 22800db8189SAndy Fleming * that certain registers get written in order 22900db8189SAndy Fleming * to restart autonegotiation */ 23000db8189SAndy Fleming err = phy_write(phydev, MII_BMCR, BMCR_RESET); 23100db8189SAndy Fleming 23200db8189SAndy Fleming if (err < 0) 23300db8189SAndy Fleming return err; 23400db8189SAndy Fleming 23500db8189SAndy Fleming err = phy_write(phydev, 0x1d, 0x1f); 23600db8189SAndy Fleming if (err < 0) 23700db8189SAndy Fleming return err; 23800db8189SAndy Fleming 23900db8189SAndy Fleming err = phy_write(phydev, 0x1e, 0x200c); 24000db8189SAndy Fleming if (err < 0) 24100db8189SAndy Fleming return err; 24200db8189SAndy Fleming 24300db8189SAndy Fleming err = phy_write(phydev, 0x1d, 0x5); 24400db8189SAndy Fleming if (err < 0) 24500db8189SAndy Fleming return err; 24600db8189SAndy Fleming 24700db8189SAndy Fleming err = phy_write(phydev, 0x1e, 0); 24800db8189SAndy Fleming if (err < 0) 24900db8189SAndy Fleming return err; 25000db8189SAndy Fleming 25100db8189SAndy Fleming err = phy_write(phydev, 0x1e, 0x100); 25200db8189SAndy Fleming if (err < 0) 25300db8189SAndy Fleming return err; 25400db8189SAndy Fleming 255239aa55bSDavid Thomson err = marvell_set_polarity(phydev, phydev->mdix); 25676884679SAndy Fleming if (err < 0) 25776884679SAndy Fleming return err; 25876884679SAndy Fleming 25976884679SAndy Fleming err = phy_write(phydev, MII_M1111_PHY_LED_CONTROL, 26076884679SAndy Fleming MII_M1111_PHY_LED_DIRECT); 26176884679SAndy Fleming if (err < 0) 26276884679SAndy Fleming return err; 26300db8189SAndy Fleming 26400db8189SAndy Fleming err = genphy_config_aneg(phydev); 2658ff44985SAnton Vorontsov if (err < 0) 26600db8189SAndy Fleming return err; 2678ff44985SAnton Vorontsov 2688ff44985SAnton Vorontsov if (phydev->autoneg != AUTONEG_ENABLE) { 2698ff44985SAnton Vorontsov int bmcr; 2708ff44985SAnton Vorontsov 2718ff44985SAnton Vorontsov /* 2728ff44985SAnton Vorontsov * A write to speed/duplex bits (that is performed by 2738ff44985SAnton Vorontsov * genphy_config_aneg() call above) must be followed by 2748ff44985SAnton Vorontsov * a software reset. Otherwise, the write has no effect. 2758ff44985SAnton Vorontsov */ 2768ff44985SAnton Vorontsov bmcr = phy_read(phydev, MII_BMCR); 2778ff44985SAnton Vorontsov if (bmcr < 0) 2788ff44985SAnton Vorontsov return bmcr; 2798ff44985SAnton Vorontsov 2808ff44985SAnton Vorontsov err = phy_write(phydev, MII_BMCR, bmcr | BMCR_RESET); 2818ff44985SAnton Vorontsov if (err < 0) 2828ff44985SAnton Vorontsov return err; 2838ff44985SAnton Vorontsov } 2848ff44985SAnton Vorontsov 2858ff44985SAnton Vorontsov return 0; 28600db8189SAndy Fleming } 28700db8189SAndy Fleming 288cf41a51dSDavid Daney #ifdef CONFIG_OF_MDIO 289cf41a51dSDavid Daney /* 290cf41a51dSDavid Daney * Set and/or override some configuration registers based on the 291cf41a51dSDavid Daney * marvell,reg-init property stored in the of_node for the phydev. 292cf41a51dSDavid Daney * 293cf41a51dSDavid Daney * marvell,reg-init = <reg-page reg mask value>,...; 294cf41a51dSDavid Daney * 295cf41a51dSDavid Daney * There may be one or more sets of <reg-page reg mask value>: 296cf41a51dSDavid Daney * 297cf41a51dSDavid Daney * reg-page: which register bank to use. 298cf41a51dSDavid Daney * reg: the register. 299cf41a51dSDavid Daney * mask: if non-zero, ANDed with existing register value. 300cf41a51dSDavid Daney * value: ORed with the masked value and written to the regiser. 301cf41a51dSDavid Daney * 302cf41a51dSDavid Daney */ 303cf41a51dSDavid Daney static int marvell_of_reg_init(struct phy_device *phydev) 304cf41a51dSDavid Daney { 305cf41a51dSDavid Daney const __be32 *paddr; 306cf41a51dSDavid Daney int len, i, saved_page, current_page, page_changed, ret; 307cf41a51dSDavid Daney 308e5a03bfdSAndrew Lunn if (!phydev->mdio.dev.of_node) 309cf41a51dSDavid Daney return 0; 310cf41a51dSDavid Daney 311e5a03bfdSAndrew Lunn paddr = of_get_property(phydev->mdio.dev.of_node, 312e5a03bfdSAndrew Lunn "marvell,reg-init", &len); 313cf41a51dSDavid Daney if (!paddr || len < (4 * sizeof(*paddr))) 314cf41a51dSDavid Daney return 0; 315cf41a51dSDavid Daney 316cf41a51dSDavid Daney saved_page = phy_read(phydev, MII_MARVELL_PHY_PAGE); 317cf41a51dSDavid Daney if (saved_page < 0) 318cf41a51dSDavid Daney return saved_page; 319cf41a51dSDavid Daney page_changed = 0; 320cf41a51dSDavid Daney current_page = saved_page; 321cf41a51dSDavid Daney 322cf41a51dSDavid Daney ret = 0; 323cf41a51dSDavid Daney len /= sizeof(*paddr); 324cf41a51dSDavid Daney for (i = 0; i < len - 3; i += 4) { 325cf41a51dSDavid Daney u16 reg_page = be32_to_cpup(paddr + i); 326cf41a51dSDavid Daney u16 reg = be32_to_cpup(paddr + i + 1); 327cf41a51dSDavid Daney u16 mask = be32_to_cpup(paddr + i + 2); 328cf41a51dSDavid Daney u16 val_bits = be32_to_cpup(paddr + i + 3); 329cf41a51dSDavid Daney int val; 330cf41a51dSDavid Daney 331cf41a51dSDavid Daney if (reg_page != current_page) { 332cf41a51dSDavid Daney current_page = reg_page; 333cf41a51dSDavid Daney page_changed = 1; 334cf41a51dSDavid Daney ret = phy_write(phydev, MII_MARVELL_PHY_PAGE, reg_page); 335cf41a51dSDavid Daney if (ret < 0) 336cf41a51dSDavid Daney goto err; 337cf41a51dSDavid Daney } 338cf41a51dSDavid Daney 339cf41a51dSDavid Daney val = 0; 340cf41a51dSDavid Daney if (mask) { 341cf41a51dSDavid Daney val = phy_read(phydev, reg); 342cf41a51dSDavid Daney if (val < 0) { 343cf41a51dSDavid Daney ret = val; 344cf41a51dSDavid Daney goto err; 345cf41a51dSDavid Daney } 346cf41a51dSDavid Daney val &= mask; 347cf41a51dSDavid Daney } 348cf41a51dSDavid Daney val |= val_bits; 349cf41a51dSDavid Daney 350cf41a51dSDavid Daney ret = phy_write(phydev, reg, val); 351cf41a51dSDavid Daney if (ret < 0) 352cf41a51dSDavid Daney goto err; 353cf41a51dSDavid Daney 354cf41a51dSDavid Daney } 355cf41a51dSDavid Daney err: 356cf41a51dSDavid Daney if (page_changed) { 357cf41a51dSDavid Daney i = phy_write(phydev, MII_MARVELL_PHY_PAGE, saved_page); 358cf41a51dSDavid Daney if (ret == 0) 359cf41a51dSDavid Daney ret = i; 360cf41a51dSDavid Daney } 361cf41a51dSDavid Daney return ret; 362cf41a51dSDavid Daney } 363cf41a51dSDavid Daney #else 364cf41a51dSDavid Daney static int marvell_of_reg_init(struct phy_device *phydev) 365cf41a51dSDavid Daney { 366cf41a51dSDavid Daney return 0; 367cf41a51dSDavid Daney } 368cf41a51dSDavid Daney #endif /* CONFIG_OF_MDIO */ 369cf41a51dSDavid Daney 370140bc929SSergei Poselenov static int m88e1121_config_aneg(struct phy_device *phydev) 371140bc929SSergei Poselenov { 372c477d044SCyril Chemparathy int err, oldpage, mscr; 373c477d044SCyril Chemparathy 37427d916d6SDavid Daney oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE); 375c477d044SCyril Chemparathy 37627d916d6SDavid Daney err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 377c477d044SCyril Chemparathy MII_88E1121_PHY_MSCR_PAGE); 378c477d044SCyril Chemparathy if (err < 0) 379c477d044SCyril Chemparathy return err; 380be8c6480SArnaud Patard 38132a64161SFlorian Fainelli if (phy_interface_is_rgmii(phydev)) { 382be8c6480SArnaud Patard 383c477d044SCyril Chemparathy mscr = phy_read(phydev, MII_88E1121_PHY_MSCR_REG) & 384c477d044SCyril Chemparathy MII_88E1121_PHY_MSCR_DELAY_MASK; 385c477d044SCyril Chemparathy 386c477d044SCyril Chemparathy if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) 387c477d044SCyril Chemparathy mscr |= (MII_88E1121_PHY_MSCR_RX_DELAY | 388c477d044SCyril Chemparathy MII_88E1121_PHY_MSCR_TX_DELAY); 389c477d044SCyril Chemparathy else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) 390c477d044SCyril Chemparathy mscr |= MII_88E1121_PHY_MSCR_RX_DELAY; 391c477d044SCyril Chemparathy else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) 392c477d044SCyril Chemparathy mscr |= MII_88E1121_PHY_MSCR_TX_DELAY; 393c477d044SCyril Chemparathy 394c477d044SCyril Chemparathy err = phy_write(phydev, MII_88E1121_PHY_MSCR_REG, mscr); 395c477d044SCyril Chemparathy if (err < 0) 396c477d044SCyril Chemparathy return err; 397be8c6480SArnaud Patard } 398c477d044SCyril Chemparathy 39927d916d6SDavid Daney phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage); 400140bc929SSergei Poselenov 401140bc929SSergei Poselenov err = phy_write(phydev, MII_BMCR, BMCR_RESET); 402140bc929SSergei Poselenov if (err < 0) 403140bc929SSergei Poselenov return err; 404140bc929SSergei Poselenov 405140bc929SSergei Poselenov err = phy_write(phydev, MII_M1011_PHY_SCR, 406140bc929SSergei Poselenov MII_M1011_PHY_SCR_AUTO_CROSS); 407140bc929SSergei Poselenov if (err < 0) 408140bc929SSergei Poselenov return err; 409140bc929SSergei Poselenov 410fdecf36fSClemens Gruber return genphy_config_aneg(phydev); 411140bc929SSergei Poselenov } 412140bc929SSergei Poselenov 413337ac9d5SCyril Chemparathy static int m88e1318_config_aneg(struct phy_device *phydev) 4143ff1c259SCyril Chemparathy { 4153ff1c259SCyril Chemparathy int err, oldpage, mscr; 4163ff1c259SCyril Chemparathy 41727d916d6SDavid Daney oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE); 4183ff1c259SCyril Chemparathy 41927d916d6SDavid Daney err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 4203ff1c259SCyril Chemparathy MII_88E1121_PHY_MSCR_PAGE); 4213ff1c259SCyril Chemparathy if (err < 0) 4223ff1c259SCyril Chemparathy return err; 4233ff1c259SCyril Chemparathy 424337ac9d5SCyril Chemparathy mscr = phy_read(phydev, MII_88E1318S_PHY_MSCR1_REG); 425337ac9d5SCyril Chemparathy mscr |= MII_88E1318S_PHY_MSCR1_PAD_ODD; 4263ff1c259SCyril Chemparathy 427337ac9d5SCyril Chemparathy err = phy_write(phydev, MII_88E1318S_PHY_MSCR1_REG, mscr); 4283ff1c259SCyril Chemparathy if (err < 0) 4293ff1c259SCyril Chemparathy return err; 4303ff1c259SCyril Chemparathy 43127d916d6SDavid Daney err = phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage); 4323ff1c259SCyril Chemparathy if (err < 0) 4333ff1c259SCyril Chemparathy return err; 4343ff1c259SCyril Chemparathy 4353ff1c259SCyril Chemparathy return m88e1121_config_aneg(phydev); 4363ff1c259SCyril Chemparathy } 4373ff1c259SCyril Chemparathy 43810e24caaSMichal Simek static int m88e1510_config_aneg(struct phy_device *phydev) 43910e24caaSMichal Simek { 44010e24caaSMichal Simek int err; 44110e24caaSMichal Simek 44210e24caaSMichal Simek err = m88e1318_config_aneg(phydev); 44310e24caaSMichal Simek if (err < 0) 44410e24caaSMichal Simek return err; 44510e24caaSMichal Simek 44679be1a1cSClemens Gruber return 0; 44779be1a1cSClemens Gruber } 44879be1a1cSClemens Gruber 44979be1a1cSClemens Gruber static int marvell_config_init(struct phy_device *phydev) 45079be1a1cSClemens Gruber { 45179be1a1cSClemens Gruber /* Set registers from marvell,reg-init DT property */ 45210e24caaSMichal Simek return marvell_of_reg_init(phydev); 45310e24caaSMichal Simek } 45410e24caaSMichal Simek 4553da09a51SMichal Simek static int m88e1116r_config_init(struct phy_device *phydev) 4563da09a51SMichal Simek { 4573da09a51SMichal Simek int temp; 4583da09a51SMichal Simek int err; 4593da09a51SMichal Simek 4603da09a51SMichal Simek temp = phy_read(phydev, MII_BMCR); 4613da09a51SMichal Simek temp |= BMCR_RESET; 4623da09a51SMichal Simek err = phy_write(phydev, MII_BMCR, temp); 4633da09a51SMichal Simek if (err < 0) 4643da09a51SMichal Simek return err; 4653da09a51SMichal Simek 4663da09a51SMichal Simek mdelay(500); 4673da09a51SMichal Simek 4683da09a51SMichal Simek err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0); 4693da09a51SMichal Simek if (err < 0) 4703da09a51SMichal Simek return err; 4713da09a51SMichal Simek 4723da09a51SMichal Simek temp = phy_read(phydev, MII_M1011_PHY_SCR); 4733da09a51SMichal Simek temp |= (7 << 12); /* max number of gigabit attempts */ 4743da09a51SMichal Simek temp |= (1 << 11); /* enable downshift */ 4753da09a51SMichal Simek temp |= MII_M1011_PHY_SCR_AUTO_CROSS; 4763da09a51SMichal Simek err = phy_write(phydev, MII_M1011_PHY_SCR, temp); 4773da09a51SMichal Simek if (err < 0) 4783da09a51SMichal Simek return err; 4793da09a51SMichal Simek 4803da09a51SMichal Simek err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 2); 4813da09a51SMichal Simek if (err < 0) 4823da09a51SMichal Simek return err; 4833da09a51SMichal Simek temp = phy_read(phydev, MII_M1116R_CONTROL_REG_MAC); 4843da09a51SMichal Simek temp |= (1 << 5); 4853da09a51SMichal Simek temp |= (1 << 4); 4863da09a51SMichal Simek err = phy_write(phydev, MII_M1116R_CONTROL_REG_MAC, temp); 4873da09a51SMichal Simek if (err < 0) 4883da09a51SMichal Simek return err; 4893da09a51SMichal Simek err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0); 4903da09a51SMichal Simek if (err < 0) 4913da09a51SMichal Simek return err; 4923da09a51SMichal Simek 4933da09a51SMichal Simek temp = phy_read(phydev, MII_BMCR); 4943da09a51SMichal Simek temp |= BMCR_RESET; 4953da09a51SMichal Simek err = phy_write(phydev, MII_BMCR, temp); 4963da09a51SMichal Simek if (err < 0) 4973da09a51SMichal Simek return err; 4983da09a51SMichal Simek 4993da09a51SMichal Simek mdelay(500); 5003da09a51SMichal Simek 50179be1a1cSClemens Gruber return marvell_config_init(phydev); 5023da09a51SMichal Simek } 5033da09a51SMichal Simek 5046b358aedSSebastian Hesselbarth static int m88e3016_config_init(struct phy_device *phydev) 5056b358aedSSebastian Hesselbarth { 5066b358aedSSebastian Hesselbarth int reg; 5076b358aedSSebastian Hesselbarth 5086b358aedSSebastian Hesselbarth /* Enable Scrambler and Auto-Crossover */ 5096b358aedSSebastian Hesselbarth reg = phy_read(phydev, MII_88E3016_PHY_SPEC_CTRL); 5106b358aedSSebastian Hesselbarth if (reg < 0) 5116b358aedSSebastian Hesselbarth return reg; 5126b358aedSSebastian Hesselbarth 5136b358aedSSebastian Hesselbarth reg &= ~MII_88E3016_DISABLE_SCRAMBLER; 5146b358aedSSebastian Hesselbarth reg |= MII_88E3016_AUTO_MDIX_CROSSOVER; 5156b358aedSSebastian Hesselbarth 5166b358aedSSebastian Hesselbarth reg = phy_write(phydev, MII_88E3016_PHY_SPEC_CTRL, reg); 5176b358aedSSebastian Hesselbarth if (reg < 0) 5186b358aedSSebastian Hesselbarth return reg; 5196b358aedSSebastian Hesselbarth 52079be1a1cSClemens Gruber return marvell_config_init(phydev); 5216b358aedSSebastian Hesselbarth } 5226b358aedSSebastian Hesselbarth 523895ee682SKim Phillips static int m88e1111_config_init(struct phy_device *phydev) 524895ee682SKim Phillips { 525895ee682SKim Phillips int err; 526be937f1fSAlexandr Smirnov int temp; 527be937f1fSAlexandr Smirnov 52832a64161SFlorian Fainelli if (phy_interface_is_rgmii(phydev)) { 529895ee682SKim Phillips 530895ee682SKim Phillips temp = phy_read(phydev, MII_M1111_PHY_EXT_CR); 531895ee682SKim Phillips if (temp < 0) 532895ee682SKim Phillips return temp; 533895ee682SKim Phillips 5349daf5a76SKim Phillips if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) { 535895ee682SKim Phillips temp |= (MII_M1111_RX_DELAY | MII_M1111_TX_DELAY); 5369daf5a76SKim Phillips } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { 5379daf5a76SKim Phillips temp &= ~MII_M1111_TX_DELAY; 5389daf5a76SKim Phillips temp |= MII_M1111_RX_DELAY; 5399daf5a76SKim Phillips } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { 5409daf5a76SKim Phillips temp &= ~MII_M1111_RX_DELAY; 5419daf5a76SKim Phillips temp |= MII_M1111_TX_DELAY; 5429daf5a76SKim Phillips } 543895ee682SKim Phillips 544895ee682SKim Phillips err = phy_write(phydev, MII_M1111_PHY_EXT_CR, temp); 545895ee682SKim Phillips if (err < 0) 546895ee682SKim Phillips return err; 547895ee682SKim Phillips 548895ee682SKim Phillips temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); 549895ee682SKim Phillips if (temp < 0) 550895ee682SKim Phillips return temp; 551895ee682SKim Phillips 552895ee682SKim Phillips temp &= ~(MII_M1111_HWCFG_MODE_MASK); 553be937f1fSAlexandr Smirnov 5547239016dSWang Jian if (temp & MII_M1111_HWCFG_FIBER_COPPER_RES) 555be937f1fSAlexandr Smirnov temp |= MII_M1111_HWCFG_MODE_FIBER_RGMII; 556be937f1fSAlexandr Smirnov else 557be937f1fSAlexandr Smirnov temp |= MII_M1111_HWCFG_MODE_COPPER_RGMII; 558895ee682SKim Phillips 559895ee682SKim Phillips err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); 560895ee682SKim Phillips if (err < 0) 561895ee682SKim Phillips return err; 562895ee682SKim Phillips } 563895ee682SKim Phillips 5644117b5beSKapil Juneja if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { 5654117b5beSKapil Juneja temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); 5664117b5beSKapil Juneja if (temp < 0) 5674117b5beSKapil Juneja return temp; 5684117b5beSKapil Juneja 5694117b5beSKapil Juneja temp &= ~(MII_M1111_HWCFG_MODE_MASK); 5704117b5beSKapil Juneja temp |= MII_M1111_HWCFG_MODE_SGMII_NO_CLK; 57132d0c1e1SHaiying Wang temp |= MII_M1111_HWCFG_FIBER_COPPER_AUTO; 5724117b5beSKapil Juneja 5734117b5beSKapil Juneja err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); 5744117b5beSKapil Juneja if (err < 0) 5754117b5beSKapil Juneja return err; 57607151bc9SMadalin Bucur 57707151bc9SMadalin Bucur /* make sure copper is selected */ 57807151bc9SMadalin Bucur err = phy_read(phydev, MII_M1145_PHY_EXT_ADDR_PAGE); 57907151bc9SMadalin Bucur if (err < 0) 58007151bc9SMadalin Bucur return err; 58107151bc9SMadalin Bucur 58207151bc9SMadalin Bucur err = phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 58307151bc9SMadalin Bucur err & (~0xff)); 58407151bc9SMadalin Bucur if (err < 0) 58507151bc9SMadalin Bucur return err; 5864117b5beSKapil Juneja } 5874117b5beSKapil Juneja 5885f8cbc13SLiu Yu-B13201 if (phydev->interface == PHY_INTERFACE_MODE_RTBI) { 5895f8cbc13SLiu Yu-B13201 temp = phy_read(phydev, MII_M1111_PHY_EXT_CR); 5905f8cbc13SLiu Yu-B13201 if (temp < 0) 5915f8cbc13SLiu Yu-B13201 return temp; 5925f8cbc13SLiu Yu-B13201 temp |= (MII_M1111_RX_DELAY | MII_M1111_TX_DELAY); 5935f8cbc13SLiu Yu-B13201 err = phy_write(phydev, MII_M1111_PHY_EXT_CR, temp); 5945f8cbc13SLiu Yu-B13201 if (err < 0) 5955f8cbc13SLiu Yu-B13201 return err; 5965f8cbc13SLiu Yu-B13201 5975f8cbc13SLiu Yu-B13201 temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); 5985f8cbc13SLiu Yu-B13201 if (temp < 0) 5995f8cbc13SLiu Yu-B13201 return temp; 6005f8cbc13SLiu Yu-B13201 temp &= ~(MII_M1111_HWCFG_MODE_MASK | MII_M1111_HWCFG_FIBER_COPPER_RES); 6015f8cbc13SLiu Yu-B13201 temp |= 0x7 | MII_M1111_HWCFG_FIBER_COPPER_AUTO; 6025f8cbc13SLiu Yu-B13201 err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); 6035f8cbc13SLiu Yu-B13201 if (err < 0) 6045f8cbc13SLiu Yu-B13201 return err; 6055f8cbc13SLiu Yu-B13201 6065f8cbc13SLiu Yu-B13201 /* soft reset */ 6075f8cbc13SLiu Yu-B13201 err = phy_write(phydev, MII_BMCR, BMCR_RESET); 6085f8cbc13SLiu Yu-B13201 if (err < 0) 6095f8cbc13SLiu Yu-B13201 return err; 6105f8cbc13SLiu Yu-B13201 do 6115f8cbc13SLiu Yu-B13201 temp = phy_read(phydev, MII_BMCR); 6125f8cbc13SLiu Yu-B13201 while (temp & BMCR_RESET); 6135f8cbc13SLiu Yu-B13201 6145f8cbc13SLiu Yu-B13201 temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); 6155f8cbc13SLiu Yu-B13201 if (temp < 0) 6165f8cbc13SLiu Yu-B13201 return temp; 6175f8cbc13SLiu Yu-B13201 temp &= ~(MII_M1111_HWCFG_MODE_MASK | MII_M1111_HWCFG_FIBER_COPPER_RES); 6185f8cbc13SLiu Yu-B13201 temp |= MII_M1111_HWCFG_MODE_COPPER_RTBI | MII_M1111_HWCFG_FIBER_COPPER_AUTO; 6195f8cbc13SLiu Yu-B13201 err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); 6205f8cbc13SLiu Yu-B13201 if (err < 0) 6215f8cbc13SLiu Yu-B13201 return err; 6225f8cbc13SLiu Yu-B13201 } 6235f8cbc13SLiu Yu-B13201 624cf41a51dSDavid Daney err = marvell_of_reg_init(phydev); 625cf41a51dSDavid Daney if (err < 0) 626cf41a51dSDavid Daney return err; 6275f8cbc13SLiu Yu-B13201 628cc90cb3bSSrinivas Kandagatla return phy_write(phydev, MII_BMCR, BMCR_RESET); 629895ee682SKim Phillips } 630895ee682SKim Phillips 631fdecf36fSClemens Gruber static int m88e1121_config_init(struct phy_device *phydev) 632fdecf36fSClemens Gruber { 633fdecf36fSClemens Gruber int err, oldpage; 634fdecf36fSClemens Gruber 635fdecf36fSClemens Gruber oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE); 636fdecf36fSClemens Gruber 637fdecf36fSClemens Gruber err = phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_88E1121_PHY_LED_PAGE); 638fdecf36fSClemens Gruber if (err < 0) 639fdecf36fSClemens Gruber return err; 640fdecf36fSClemens Gruber 641fdecf36fSClemens Gruber /* Default PHY LED config: LED[0] .. Link, LED[1] .. Activity */ 642fdecf36fSClemens Gruber err = phy_write(phydev, MII_88E1121_PHY_LED_CTRL, 643fdecf36fSClemens Gruber MII_88E1121_PHY_LED_DEF); 644fdecf36fSClemens Gruber if (err < 0) 645fdecf36fSClemens Gruber return err; 646fdecf36fSClemens Gruber 647fdecf36fSClemens Gruber phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage); 648fdecf36fSClemens Gruber 649fdecf36fSClemens Gruber /* Set marvell,reg-init configuration from device tree */ 650fdecf36fSClemens Gruber return marvell_config_init(phydev); 651fdecf36fSClemens Gruber } 652fdecf36fSClemens Gruber 653407353ecSClemens Gruber static int m88e1510_config_init(struct phy_device *phydev) 654407353ecSClemens Gruber { 655407353ecSClemens Gruber int err; 656407353ecSClemens Gruber int temp; 657407353ecSClemens Gruber 658407353ecSClemens Gruber /* SGMII-to-Copper mode initialization */ 659407353ecSClemens Gruber if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { 660407353ecSClemens Gruber /* Select page 18 */ 661407353ecSClemens Gruber err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 18); 662407353ecSClemens Gruber if (err < 0) 663407353ecSClemens Gruber return err; 664407353ecSClemens Gruber 665407353ecSClemens Gruber /* In reg 20, write MODE[2:0] = 0x1 (SGMII to Copper) */ 666407353ecSClemens Gruber temp = phy_read(phydev, MII_88E1510_GEN_CTRL_REG_1); 667407353ecSClemens Gruber temp &= ~MII_88E1510_GEN_CTRL_REG_1_MODE_MASK; 668407353ecSClemens Gruber temp |= MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII; 669407353ecSClemens Gruber err = phy_write(phydev, MII_88E1510_GEN_CTRL_REG_1, temp); 670407353ecSClemens Gruber if (err < 0) 671407353ecSClemens Gruber return err; 672407353ecSClemens Gruber 673407353ecSClemens Gruber /* PHY reset is necessary after changing MODE[2:0] */ 674407353ecSClemens Gruber temp |= MII_88E1510_GEN_CTRL_REG_1_RESET; 675407353ecSClemens Gruber err = phy_write(phydev, MII_88E1510_GEN_CTRL_REG_1, temp); 676407353ecSClemens Gruber if (err < 0) 677407353ecSClemens Gruber return err; 678407353ecSClemens Gruber 679407353ecSClemens Gruber /* Reset page selection */ 680407353ecSClemens Gruber err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0); 681407353ecSClemens Gruber if (err < 0) 682407353ecSClemens Gruber return err; 683407353ecSClemens Gruber } 684407353ecSClemens Gruber 685fdecf36fSClemens Gruber return m88e1121_config_init(phydev); 686407353ecSClemens Gruber } 687407353ecSClemens Gruber 688605f196eSRon Madrid static int m88e1118_config_aneg(struct phy_device *phydev) 689605f196eSRon Madrid { 690605f196eSRon Madrid int err; 691605f196eSRon Madrid 692605f196eSRon Madrid err = phy_write(phydev, MII_BMCR, BMCR_RESET); 693605f196eSRon Madrid if (err < 0) 694605f196eSRon Madrid return err; 695605f196eSRon Madrid 696605f196eSRon Madrid err = phy_write(phydev, MII_M1011_PHY_SCR, 697605f196eSRon Madrid MII_M1011_PHY_SCR_AUTO_CROSS); 698605f196eSRon Madrid if (err < 0) 699605f196eSRon Madrid return err; 700605f196eSRon Madrid 701605f196eSRon Madrid err = genphy_config_aneg(phydev); 702605f196eSRon Madrid return 0; 703605f196eSRon Madrid } 704605f196eSRon Madrid 705605f196eSRon Madrid static int m88e1118_config_init(struct phy_device *phydev) 706605f196eSRon Madrid { 707605f196eSRon Madrid int err; 708605f196eSRon Madrid 709605f196eSRon Madrid /* Change address */ 71027d916d6SDavid Daney err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0002); 711605f196eSRon Madrid if (err < 0) 712605f196eSRon Madrid return err; 713605f196eSRon Madrid 714605f196eSRon Madrid /* Enable 1000 Mbit */ 715605f196eSRon Madrid err = phy_write(phydev, 0x15, 0x1070); 716605f196eSRon Madrid if (err < 0) 717605f196eSRon Madrid return err; 718605f196eSRon Madrid 719605f196eSRon Madrid /* Change address */ 72027d916d6SDavid Daney err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0003); 721605f196eSRon Madrid if (err < 0) 722605f196eSRon Madrid return err; 723605f196eSRon Madrid 724605f196eSRon Madrid /* Adjust LED Control */ 7252f495c39SBenjamin Herrenschmidt if (phydev->dev_flags & MARVELL_PHY_M1118_DNS323_LEDS) 7262f495c39SBenjamin Herrenschmidt err = phy_write(phydev, 0x10, 0x1100); 7272f495c39SBenjamin Herrenschmidt else 728605f196eSRon Madrid err = phy_write(phydev, 0x10, 0x021e); 729605f196eSRon Madrid if (err < 0) 730605f196eSRon Madrid return err; 731605f196eSRon Madrid 732cf41a51dSDavid Daney err = marvell_of_reg_init(phydev); 733cf41a51dSDavid Daney if (err < 0) 734cf41a51dSDavid Daney return err; 735cf41a51dSDavid Daney 736605f196eSRon Madrid /* Reset address */ 73727d916d6SDavid Daney err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0); 738605f196eSRon Madrid if (err < 0) 739605f196eSRon Madrid return err; 740605f196eSRon Madrid 741cc90cb3bSSrinivas Kandagatla return phy_write(phydev, MII_BMCR, BMCR_RESET); 742605f196eSRon Madrid } 743605f196eSRon Madrid 74490600732SDavid Daney static int m88e1149_config_init(struct phy_device *phydev) 74590600732SDavid Daney { 74690600732SDavid Daney int err; 74790600732SDavid Daney 74890600732SDavid Daney /* Change address */ 74990600732SDavid Daney err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0002); 75090600732SDavid Daney if (err < 0) 75190600732SDavid Daney return err; 75290600732SDavid Daney 75390600732SDavid Daney /* Enable 1000 Mbit */ 75490600732SDavid Daney err = phy_write(phydev, 0x15, 0x1048); 75590600732SDavid Daney if (err < 0) 75690600732SDavid Daney return err; 75790600732SDavid Daney 758cf41a51dSDavid Daney err = marvell_of_reg_init(phydev); 759cf41a51dSDavid Daney if (err < 0) 760cf41a51dSDavid Daney return err; 761cf41a51dSDavid Daney 76290600732SDavid Daney /* Reset address */ 76390600732SDavid Daney err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0); 76490600732SDavid Daney if (err < 0) 76590600732SDavid Daney return err; 76690600732SDavid Daney 767cc90cb3bSSrinivas Kandagatla return phy_write(phydev, MII_BMCR, BMCR_RESET); 76890600732SDavid Daney } 76990600732SDavid Daney 77076884679SAndy Fleming static int m88e1145_config_init(struct phy_device *phydev) 77176884679SAndy Fleming { 77276884679SAndy Fleming int err; 773b0224175SViet Nga Dao int temp; 77476884679SAndy Fleming 77576884679SAndy Fleming /* Take care of errata E0 & E1 */ 77676884679SAndy Fleming err = phy_write(phydev, 0x1d, 0x001b); 77776884679SAndy Fleming if (err < 0) 77876884679SAndy Fleming return err; 77976884679SAndy Fleming 78076884679SAndy Fleming err = phy_write(phydev, 0x1e, 0x418f); 78176884679SAndy Fleming if (err < 0) 78276884679SAndy Fleming return err; 78376884679SAndy Fleming 78476884679SAndy Fleming err = phy_write(phydev, 0x1d, 0x0016); 78576884679SAndy Fleming if (err < 0) 78676884679SAndy Fleming return err; 78776884679SAndy Fleming 78876884679SAndy Fleming err = phy_write(phydev, 0x1e, 0xa2da); 78976884679SAndy Fleming if (err < 0) 79076884679SAndy Fleming return err; 79176884679SAndy Fleming 792895ee682SKim Phillips if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) { 79376884679SAndy Fleming int temp = phy_read(phydev, MII_M1145_PHY_EXT_CR); 79476884679SAndy Fleming if (temp < 0) 79576884679SAndy Fleming return temp; 79676884679SAndy Fleming 79776884679SAndy Fleming temp |= (MII_M1145_RGMII_RX_DELAY | MII_M1145_RGMII_TX_DELAY); 79876884679SAndy Fleming 79976884679SAndy Fleming err = phy_write(phydev, MII_M1145_PHY_EXT_CR, temp); 80076884679SAndy Fleming if (err < 0) 80176884679SAndy Fleming return err; 80276884679SAndy Fleming 8032f495c39SBenjamin Herrenschmidt if (phydev->dev_flags & MARVELL_PHY_M1145_FLAGS_RESISTANCE) { 80476884679SAndy Fleming err = phy_write(phydev, 0x1d, 0x0012); 80576884679SAndy Fleming if (err < 0) 80676884679SAndy Fleming return err; 80776884679SAndy Fleming 80876884679SAndy Fleming temp = phy_read(phydev, 0x1e); 80976884679SAndy Fleming if (temp < 0) 81076884679SAndy Fleming return temp; 81176884679SAndy Fleming 81276884679SAndy Fleming temp &= 0xf03f; 81376884679SAndy Fleming temp |= 2 << 9; /* 36 ohm */ 81476884679SAndy Fleming temp |= 2 << 6; /* 39 ohm */ 81576884679SAndy Fleming 81676884679SAndy Fleming err = phy_write(phydev, 0x1e, temp); 81776884679SAndy Fleming if (err < 0) 81876884679SAndy Fleming return err; 81976884679SAndy Fleming 82076884679SAndy Fleming err = phy_write(phydev, 0x1d, 0x3); 82176884679SAndy Fleming if (err < 0) 82276884679SAndy Fleming return err; 82376884679SAndy Fleming 82476884679SAndy Fleming err = phy_write(phydev, 0x1e, 0x8000); 82576884679SAndy Fleming if (err < 0) 82676884679SAndy Fleming return err; 82776884679SAndy Fleming } 82876884679SAndy Fleming } 82976884679SAndy Fleming 830b0224175SViet Nga Dao if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { 831b0224175SViet Nga Dao temp = phy_read(phydev, MII_M1145_PHY_EXT_SR); 832b0224175SViet Nga Dao if (temp < 0) 833b0224175SViet Nga Dao return temp; 834b0224175SViet Nga Dao 83599d881f9SVince Bridgers temp &= ~MII_M1145_HWCFG_MODE_MASK; 836b0224175SViet Nga Dao temp |= MII_M1145_HWCFG_MODE_SGMII_NO_CLK; 837b0224175SViet Nga Dao temp |= MII_M1145_HWCFG_FIBER_COPPER_AUTO; 838b0224175SViet Nga Dao 839b0224175SViet Nga Dao err = phy_write(phydev, MII_M1145_PHY_EXT_SR, temp); 840b0224175SViet Nga Dao if (err < 0) 841b0224175SViet Nga Dao return err; 842b0224175SViet Nga Dao } 843b0224175SViet Nga Dao 844cf41a51dSDavid Daney err = marvell_of_reg_init(phydev); 845cf41a51dSDavid Daney if (err < 0) 846cf41a51dSDavid Daney return err; 847cf41a51dSDavid Daney 84876884679SAndy Fleming return 0; 84976884679SAndy Fleming } 85000db8189SAndy Fleming 851be937f1fSAlexandr Smirnov /* marvell_read_status 852be937f1fSAlexandr Smirnov * 853be937f1fSAlexandr Smirnov * Generic status code does not detect Fiber correctly! 854be937f1fSAlexandr Smirnov * Description: 855be937f1fSAlexandr Smirnov * Check the link, then figure out the current state 856be937f1fSAlexandr Smirnov * by comparing what we advertise with what the link partner 857be937f1fSAlexandr Smirnov * advertises. Start by checking the gigabit possibilities, 858be937f1fSAlexandr Smirnov * then move on to 10/100. 859be937f1fSAlexandr Smirnov */ 860be937f1fSAlexandr Smirnov static int marvell_read_status(struct phy_device *phydev) 861be937f1fSAlexandr Smirnov { 862be937f1fSAlexandr Smirnov int adv; 863be937f1fSAlexandr Smirnov int err; 864be937f1fSAlexandr Smirnov int lpa; 865357cd64cSRussell King int lpagb; 866be937f1fSAlexandr Smirnov int status = 0; 867be937f1fSAlexandr Smirnov 868be937f1fSAlexandr Smirnov /* Update the link, but return if there 869be937f1fSAlexandr Smirnov * was an error */ 870be937f1fSAlexandr Smirnov err = genphy_update_link(phydev); 871be937f1fSAlexandr Smirnov if (err) 872be937f1fSAlexandr Smirnov return err; 873be937f1fSAlexandr Smirnov 874be937f1fSAlexandr Smirnov if (AUTONEG_ENABLE == phydev->autoneg) { 875be937f1fSAlexandr Smirnov status = phy_read(phydev, MII_M1011_PHY_STATUS); 876be937f1fSAlexandr Smirnov if (status < 0) 877be937f1fSAlexandr Smirnov return status; 878be937f1fSAlexandr Smirnov 879be937f1fSAlexandr Smirnov lpa = phy_read(phydev, MII_LPA); 880be937f1fSAlexandr Smirnov if (lpa < 0) 881be937f1fSAlexandr Smirnov return lpa; 882be937f1fSAlexandr Smirnov 883357cd64cSRussell King lpagb = phy_read(phydev, MII_STAT1000); 884357cd64cSRussell King if (lpagb < 0) 885357cd64cSRussell King return lpagb; 886357cd64cSRussell King 887be937f1fSAlexandr Smirnov adv = phy_read(phydev, MII_ADVERTISE); 888be937f1fSAlexandr Smirnov if (adv < 0) 889be937f1fSAlexandr Smirnov return adv; 890be937f1fSAlexandr Smirnov 891357cd64cSRussell King phydev->lp_advertising = mii_stat1000_to_ethtool_lpa_t(lpagb) | 892357cd64cSRussell King mii_lpa_to_ethtool_lpa_t(lpa); 893357cd64cSRussell King 894be937f1fSAlexandr Smirnov lpa &= adv; 895be937f1fSAlexandr Smirnov 896be937f1fSAlexandr Smirnov if (status & MII_M1011_PHY_STATUS_FULLDUPLEX) 897be937f1fSAlexandr Smirnov phydev->duplex = DUPLEX_FULL; 898be937f1fSAlexandr Smirnov else 899be937f1fSAlexandr Smirnov phydev->duplex = DUPLEX_HALF; 900be937f1fSAlexandr Smirnov 901be937f1fSAlexandr Smirnov status = status & MII_M1011_PHY_STATUS_SPD_MASK; 902be937f1fSAlexandr Smirnov phydev->pause = phydev->asym_pause = 0; 903be937f1fSAlexandr Smirnov 904be937f1fSAlexandr Smirnov switch (status) { 905be937f1fSAlexandr Smirnov case MII_M1011_PHY_STATUS_1000: 906be937f1fSAlexandr Smirnov phydev->speed = SPEED_1000; 907be937f1fSAlexandr Smirnov break; 908be937f1fSAlexandr Smirnov 909be937f1fSAlexandr Smirnov case MII_M1011_PHY_STATUS_100: 910be937f1fSAlexandr Smirnov phydev->speed = SPEED_100; 911be937f1fSAlexandr Smirnov break; 912be937f1fSAlexandr Smirnov 913be937f1fSAlexandr Smirnov default: 914be937f1fSAlexandr Smirnov phydev->speed = SPEED_10; 915be937f1fSAlexandr Smirnov break; 916be937f1fSAlexandr Smirnov } 917be937f1fSAlexandr Smirnov 918be937f1fSAlexandr Smirnov if (phydev->duplex == DUPLEX_FULL) { 919be937f1fSAlexandr Smirnov phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0; 920be937f1fSAlexandr Smirnov phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0; 921be937f1fSAlexandr Smirnov } 922be937f1fSAlexandr Smirnov } else { 923be937f1fSAlexandr Smirnov int bmcr = phy_read(phydev, MII_BMCR); 924be937f1fSAlexandr Smirnov 925be937f1fSAlexandr Smirnov if (bmcr < 0) 926be937f1fSAlexandr Smirnov return bmcr; 927be937f1fSAlexandr Smirnov 928be937f1fSAlexandr Smirnov if (bmcr & BMCR_FULLDPLX) 929be937f1fSAlexandr Smirnov phydev->duplex = DUPLEX_FULL; 930be937f1fSAlexandr Smirnov else 931be937f1fSAlexandr Smirnov phydev->duplex = DUPLEX_HALF; 932be937f1fSAlexandr Smirnov 933be937f1fSAlexandr Smirnov if (bmcr & BMCR_SPEED1000) 934be937f1fSAlexandr Smirnov phydev->speed = SPEED_1000; 935be937f1fSAlexandr Smirnov else if (bmcr & BMCR_SPEED100) 936be937f1fSAlexandr Smirnov phydev->speed = SPEED_100; 937be937f1fSAlexandr Smirnov else 938be937f1fSAlexandr Smirnov phydev->speed = SPEED_10; 939be937f1fSAlexandr Smirnov 940be937f1fSAlexandr Smirnov phydev->pause = phydev->asym_pause = 0; 941357cd64cSRussell King phydev->lp_advertising = 0; 942be937f1fSAlexandr Smirnov } 943be937f1fSAlexandr Smirnov 944be937f1fSAlexandr Smirnov return 0; 945be937f1fSAlexandr Smirnov } 946be937f1fSAlexandr Smirnov 9476b358aedSSebastian Hesselbarth static int marvell_aneg_done(struct phy_device *phydev) 9486b358aedSSebastian Hesselbarth { 9496b358aedSSebastian Hesselbarth int retval = phy_read(phydev, MII_M1011_PHY_STATUS); 9506b358aedSSebastian Hesselbarth return (retval < 0) ? retval : (retval & MII_M1011_PHY_STATUS_RESOLVED); 9516b358aedSSebastian Hesselbarth } 9526b358aedSSebastian Hesselbarth 953dcd07be3SAnatolij Gustschin static int m88e1121_did_interrupt(struct phy_device *phydev) 954dcd07be3SAnatolij Gustschin { 955dcd07be3SAnatolij Gustschin int imask; 956dcd07be3SAnatolij Gustschin 957dcd07be3SAnatolij Gustschin imask = phy_read(phydev, MII_M1011_IEVENT); 958dcd07be3SAnatolij Gustschin 959dcd07be3SAnatolij Gustschin if (imask & MII_M1011_IMASK_INIT) 960dcd07be3SAnatolij Gustschin return 1; 961dcd07be3SAnatolij Gustschin 962dcd07be3SAnatolij Gustschin return 0; 963dcd07be3SAnatolij Gustschin } 964dcd07be3SAnatolij Gustschin 9653871c387SMichael Stapelberg static void m88e1318_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) 9663871c387SMichael Stapelberg { 9673871c387SMichael Stapelberg wol->supported = WAKE_MAGIC; 9683871c387SMichael Stapelberg wol->wolopts = 0; 9693871c387SMichael Stapelberg 9703871c387SMichael Stapelberg if (phy_write(phydev, MII_MARVELL_PHY_PAGE, 9713871c387SMichael Stapelberg MII_88E1318S_PHY_WOL_PAGE) < 0) 9723871c387SMichael Stapelberg return; 9733871c387SMichael Stapelberg 9743871c387SMichael Stapelberg if (phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL) & 9753871c387SMichael Stapelberg MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE) 9763871c387SMichael Stapelberg wol->wolopts |= WAKE_MAGIC; 9773871c387SMichael Stapelberg 9783871c387SMichael Stapelberg if (phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x00) < 0) 9793871c387SMichael Stapelberg return; 9803871c387SMichael Stapelberg } 9813871c387SMichael Stapelberg 9823871c387SMichael Stapelberg static int m88e1318_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) 9833871c387SMichael Stapelberg { 9843871c387SMichael Stapelberg int err, oldpage, temp; 9853871c387SMichael Stapelberg 9863871c387SMichael Stapelberg oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE); 9873871c387SMichael Stapelberg 9883871c387SMichael Stapelberg if (wol->wolopts & WAKE_MAGIC) { 9893871c387SMichael Stapelberg /* Explicitly switch to page 0x00, just to be sure */ 9903871c387SMichael Stapelberg err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x00); 9913871c387SMichael Stapelberg if (err < 0) 9923871c387SMichael Stapelberg return err; 9933871c387SMichael Stapelberg 9943871c387SMichael Stapelberg /* Enable the WOL interrupt */ 9953871c387SMichael Stapelberg temp = phy_read(phydev, MII_88E1318S_PHY_CSIER); 9963871c387SMichael Stapelberg temp |= MII_88E1318S_PHY_CSIER_WOL_EIE; 9973871c387SMichael Stapelberg err = phy_write(phydev, MII_88E1318S_PHY_CSIER, temp); 9983871c387SMichael Stapelberg if (err < 0) 9993871c387SMichael Stapelberg return err; 10003871c387SMichael Stapelberg 10013871c387SMichael Stapelberg err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 10023871c387SMichael Stapelberg MII_88E1318S_PHY_LED_PAGE); 10033871c387SMichael Stapelberg if (err < 0) 10043871c387SMichael Stapelberg return err; 10053871c387SMichael Stapelberg 10063871c387SMichael Stapelberg /* Setup LED[2] as interrupt pin (active low) */ 10073871c387SMichael Stapelberg temp = phy_read(phydev, MII_88E1318S_PHY_LED_TCR); 10083871c387SMichael Stapelberg temp &= ~MII_88E1318S_PHY_LED_TCR_FORCE_INT; 10093871c387SMichael Stapelberg temp |= MII_88E1318S_PHY_LED_TCR_INTn_ENABLE; 10103871c387SMichael Stapelberg temp |= MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW; 10113871c387SMichael Stapelberg err = phy_write(phydev, MII_88E1318S_PHY_LED_TCR, temp); 10123871c387SMichael Stapelberg if (err < 0) 10133871c387SMichael Stapelberg return err; 10143871c387SMichael Stapelberg 10153871c387SMichael Stapelberg err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 10163871c387SMichael Stapelberg MII_88E1318S_PHY_WOL_PAGE); 10173871c387SMichael Stapelberg if (err < 0) 10183871c387SMichael Stapelberg return err; 10193871c387SMichael Stapelberg 10203871c387SMichael Stapelberg /* Store the device address for the magic packet */ 10213871c387SMichael Stapelberg err = phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD2, 10223871c387SMichael Stapelberg ((phydev->attached_dev->dev_addr[5] << 8) | 10233871c387SMichael Stapelberg phydev->attached_dev->dev_addr[4])); 10243871c387SMichael Stapelberg if (err < 0) 10253871c387SMichael Stapelberg return err; 10263871c387SMichael Stapelberg err = phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD1, 10273871c387SMichael Stapelberg ((phydev->attached_dev->dev_addr[3] << 8) | 10283871c387SMichael Stapelberg phydev->attached_dev->dev_addr[2])); 10293871c387SMichael Stapelberg if (err < 0) 10303871c387SMichael Stapelberg return err; 10313871c387SMichael Stapelberg err = phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD0, 10323871c387SMichael Stapelberg ((phydev->attached_dev->dev_addr[1] << 8) | 10333871c387SMichael Stapelberg phydev->attached_dev->dev_addr[0])); 10343871c387SMichael Stapelberg if (err < 0) 10353871c387SMichael Stapelberg return err; 10363871c387SMichael Stapelberg 10373871c387SMichael Stapelberg /* Clear WOL status and enable magic packet matching */ 10383871c387SMichael Stapelberg temp = phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL); 10393871c387SMichael Stapelberg temp |= MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS; 10403871c387SMichael Stapelberg temp |= MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE; 10413871c387SMichael Stapelberg err = phy_write(phydev, MII_88E1318S_PHY_WOL_CTRL, temp); 10423871c387SMichael Stapelberg if (err < 0) 10433871c387SMichael Stapelberg return err; 10443871c387SMichael Stapelberg } else { 10453871c387SMichael Stapelberg err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 10463871c387SMichael Stapelberg MII_88E1318S_PHY_WOL_PAGE); 10473871c387SMichael Stapelberg if (err < 0) 10483871c387SMichael Stapelberg return err; 10493871c387SMichael Stapelberg 10503871c387SMichael Stapelberg /* Clear WOL status and disable magic packet matching */ 10513871c387SMichael Stapelberg temp = phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL); 10523871c387SMichael Stapelberg temp |= MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS; 10533871c387SMichael Stapelberg temp &= ~MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE; 10543871c387SMichael Stapelberg err = phy_write(phydev, MII_88E1318S_PHY_WOL_CTRL, temp); 10553871c387SMichael Stapelberg if (err < 0) 10563871c387SMichael Stapelberg return err; 10573871c387SMichael Stapelberg } 10583871c387SMichael Stapelberg 10593871c387SMichael Stapelberg err = phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage); 10603871c387SMichael Stapelberg if (err < 0) 10613871c387SMichael Stapelberg return err; 10623871c387SMichael Stapelberg 10633871c387SMichael Stapelberg return 0; 10643871c387SMichael Stapelberg } 10653871c387SMichael Stapelberg 1066d2fa47d9SAndrew Lunn static int marvell_get_sset_count(struct phy_device *phydev) 1067d2fa47d9SAndrew Lunn { 1068d2fa47d9SAndrew Lunn return ARRAY_SIZE(marvell_hw_stats); 1069d2fa47d9SAndrew Lunn } 1070d2fa47d9SAndrew Lunn 1071d2fa47d9SAndrew Lunn static void marvell_get_strings(struct phy_device *phydev, u8 *data) 1072d2fa47d9SAndrew Lunn { 1073d2fa47d9SAndrew Lunn int i; 1074d2fa47d9SAndrew Lunn 1075d2fa47d9SAndrew Lunn for (i = 0; i < ARRAY_SIZE(marvell_hw_stats); i++) { 1076d2fa47d9SAndrew Lunn memcpy(data + i * ETH_GSTRING_LEN, 1077d2fa47d9SAndrew Lunn marvell_hw_stats[i].string, ETH_GSTRING_LEN); 1078d2fa47d9SAndrew Lunn } 1079d2fa47d9SAndrew Lunn } 1080d2fa47d9SAndrew Lunn 1081d2fa47d9SAndrew Lunn #ifndef UINT64_MAX 1082d2fa47d9SAndrew Lunn #define UINT64_MAX (u64)(~((u64)0)) 1083d2fa47d9SAndrew Lunn #endif 1084d2fa47d9SAndrew Lunn static u64 marvell_get_stat(struct phy_device *phydev, int i) 1085d2fa47d9SAndrew Lunn { 1086d2fa47d9SAndrew Lunn struct marvell_hw_stat stat = marvell_hw_stats[i]; 1087d2fa47d9SAndrew Lunn struct marvell_priv *priv = phydev->priv; 1088321b4d4bSAndrew Lunn int err, oldpage, val; 1089321b4d4bSAndrew Lunn u64 ret; 1090d2fa47d9SAndrew Lunn 1091d2fa47d9SAndrew Lunn oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE); 1092d2fa47d9SAndrew Lunn err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 1093d2fa47d9SAndrew Lunn stat.page); 1094d2fa47d9SAndrew Lunn if (err < 0) 1095d2fa47d9SAndrew Lunn return UINT64_MAX; 1096d2fa47d9SAndrew Lunn 1097d2fa47d9SAndrew Lunn val = phy_read(phydev, stat.reg); 1098d2fa47d9SAndrew Lunn if (val < 0) { 1099321b4d4bSAndrew Lunn ret = UINT64_MAX; 1100d2fa47d9SAndrew Lunn } else { 1101d2fa47d9SAndrew Lunn val = val & ((1 << stat.bits) - 1); 1102d2fa47d9SAndrew Lunn priv->stats[i] += val; 1103321b4d4bSAndrew Lunn ret = priv->stats[i]; 1104d2fa47d9SAndrew Lunn } 1105d2fa47d9SAndrew Lunn 1106d2fa47d9SAndrew Lunn phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage); 1107d2fa47d9SAndrew Lunn 1108321b4d4bSAndrew Lunn return ret; 1109d2fa47d9SAndrew Lunn } 1110d2fa47d9SAndrew Lunn 1111d2fa47d9SAndrew Lunn static void marvell_get_stats(struct phy_device *phydev, 1112d2fa47d9SAndrew Lunn struct ethtool_stats *stats, u64 *data) 1113d2fa47d9SAndrew Lunn { 1114d2fa47d9SAndrew Lunn int i; 1115d2fa47d9SAndrew Lunn 1116d2fa47d9SAndrew Lunn for (i = 0; i < ARRAY_SIZE(marvell_hw_stats); i++) 1117d2fa47d9SAndrew Lunn data[i] = marvell_get_stat(phydev, i); 1118d2fa47d9SAndrew Lunn } 1119d2fa47d9SAndrew Lunn 1120d2fa47d9SAndrew Lunn static int marvell_probe(struct phy_device *phydev) 1121d2fa47d9SAndrew Lunn { 1122d2fa47d9SAndrew Lunn struct marvell_priv *priv; 1123d2fa47d9SAndrew Lunn 1124e5a03bfdSAndrew Lunn priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); 1125d2fa47d9SAndrew Lunn if (!priv) 1126d2fa47d9SAndrew Lunn return -ENOMEM; 1127d2fa47d9SAndrew Lunn 1128d2fa47d9SAndrew Lunn phydev->priv = priv; 1129d2fa47d9SAndrew Lunn 1130d2fa47d9SAndrew Lunn return 0; 1131d2fa47d9SAndrew Lunn } 1132d2fa47d9SAndrew Lunn 1133e5479239SOlof Johansson static struct phy_driver marvell_drivers[] = { 1134e5479239SOlof Johansson { 11352f495c39SBenjamin Herrenschmidt .phy_id = MARVELL_PHY_ID_88E1101, 11362f495c39SBenjamin Herrenschmidt .phy_id_mask = MARVELL_PHY_ID_MASK, 113700db8189SAndy Fleming .name = "Marvell 88E1101", 113800db8189SAndy Fleming .features = PHY_GBIT_FEATURES, 1139d2fa47d9SAndrew Lunn .probe = marvell_probe, 114000db8189SAndy Fleming .flags = PHY_HAS_INTERRUPT, 114179be1a1cSClemens Gruber .config_init = &marvell_config_init, 114200db8189SAndy Fleming .config_aneg = &marvell_config_aneg, 114300db8189SAndy Fleming .read_status = &genphy_read_status, 114400db8189SAndy Fleming .ack_interrupt = &marvell_ack_interrupt, 114500db8189SAndy Fleming .config_intr = &marvell_config_intr, 11460898b448SSebastian Hesselbarth .resume = &genphy_resume, 11470898b448SSebastian Hesselbarth .suspend = &genphy_suspend, 1148d2fa47d9SAndrew Lunn .get_sset_count = marvell_get_sset_count, 1149d2fa47d9SAndrew Lunn .get_strings = marvell_get_strings, 1150d2fa47d9SAndrew Lunn .get_stats = marvell_get_stats, 1151e5479239SOlof Johansson }, 1152e5479239SOlof Johansson { 11532f495c39SBenjamin Herrenschmidt .phy_id = MARVELL_PHY_ID_88E1112, 11542f495c39SBenjamin Herrenschmidt .phy_id_mask = MARVELL_PHY_ID_MASK, 115585cfb534SOlof Johansson .name = "Marvell 88E1112", 115685cfb534SOlof Johansson .features = PHY_GBIT_FEATURES, 115785cfb534SOlof Johansson .flags = PHY_HAS_INTERRUPT, 1158d2fa47d9SAndrew Lunn .probe = marvell_probe, 115985cfb534SOlof Johansson .config_init = &m88e1111_config_init, 116085cfb534SOlof Johansson .config_aneg = &marvell_config_aneg, 116185cfb534SOlof Johansson .read_status = &genphy_read_status, 116285cfb534SOlof Johansson .ack_interrupt = &marvell_ack_interrupt, 116385cfb534SOlof Johansson .config_intr = &marvell_config_intr, 11640898b448SSebastian Hesselbarth .resume = &genphy_resume, 11650898b448SSebastian Hesselbarth .suspend = &genphy_suspend, 1166d2fa47d9SAndrew Lunn .get_sset_count = marvell_get_sset_count, 1167d2fa47d9SAndrew Lunn .get_strings = marvell_get_strings, 1168d2fa47d9SAndrew Lunn .get_stats = marvell_get_stats, 116985cfb534SOlof Johansson }, 117085cfb534SOlof Johansson { 11712f495c39SBenjamin Herrenschmidt .phy_id = MARVELL_PHY_ID_88E1111, 11722f495c39SBenjamin Herrenschmidt .phy_id_mask = MARVELL_PHY_ID_MASK, 117376884679SAndy Fleming .name = "Marvell 88E1111", 117476884679SAndy Fleming .features = PHY_GBIT_FEATURES, 117576884679SAndy Fleming .flags = PHY_HAS_INTERRUPT, 1176d2fa47d9SAndrew Lunn .probe = marvell_probe, 1177e5479239SOlof Johansson .config_init = &m88e1111_config_init, 117876884679SAndy Fleming .config_aneg = &marvell_config_aneg, 1179be937f1fSAlexandr Smirnov .read_status = &marvell_read_status, 118076884679SAndy Fleming .ack_interrupt = &marvell_ack_interrupt, 118176884679SAndy Fleming .config_intr = &marvell_config_intr, 11820898b448SSebastian Hesselbarth .resume = &genphy_resume, 11830898b448SSebastian Hesselbarth .suspend = &genphy_suspend, 1184d2fa47d9SAndrew Lunn .get_sset_count = marvell_get_sset_count, 1185d2fa47d9SAndrew Lunn .get_strings = marvell_get_strings, 1186d2fa47d9SAndrew Lunn .get_stats = marvell_get_stats, 1187e5479239SOlof Johansson }, 1188e5479239SOlof Johansson { 11892f495c39SBenjamin Herrenschmidt .phy_id = MARVELL_PHY_ID_88E1118, 11902f495c39SBenjamin Herrenschmidt .phy_id_mask = MARVELL_PHY_ID_MASK, 1191605f196eSRon Madrid .name = "Marvell 88E1118", 1192605f196eSRon Madrid .features = PHY_GBIT_FEATURES, 1193605f196eSRon Madrid .flags = PHY_HAS_INTERRUPT, 1194d2fa47d9SAndrew Lunn .probe = marvell_probe, 1195605f196eSRon Madrid .config_init = &m88e1118_config_init, 1196605f196eSRon Madrid .config_aneg = &m88e1118_config_aneg, 1197605f196eSRon Madrid .read_status = &genphy_read_status, 1198605f196eSRon Madrid .ack_interrupt = &marvell_ack_interrupt, 1199605f196eSRon Madrid .config_intr = &marvell_config_intr, 12000898b448SSebastian Hesselbarth .resume = &genphy_resume, 12010898b448SSebastian Hesselbarth .suspend = &genphy_suspend, 1202d2fa47d9SAndrew Lunn .get_sset_count = marvell_get_sset_count, 1203d2fa47d9SAndrew Lunn .get_strings = marvell_get_strings, 1204d2fa47d9SAndrew Lunn .get_stats = marvell_get_stats, 1205605f196eSRon Madrid }, 1206605f196eSRon Madrid { 12072f495c39SBenjamin Herrenschmidt .phy_id = MARVELL_PHY_ID_88E1121R, 12082f495c39SBenjamin Herrenschmidt .phy_id_mask = MARVELL_PHY_ID_MASK, 1209140bc929SSergei Poselenov .name = "Marvell 88E1121R", 1210140bc929SSergei Poselenov .features = PHY_GBIT_FEATURES, 1211140bc929SSergei Poselenov .flags = PHY_HAS_INTERRUPT, 1212d2fa47d9SAndrew Lunn .probe = marvell_probe, 1213fdecf36fSClemens Gruber .config_init = &m88e1121_config_init, 1214140bc929SSergei Poselenov .config_aneg = &m88e1121_config_aneg, 1215140bc929SSergei Poselenov .read_status = &marvell_read_status, 1216140bc929SSergei Poselenov .ack_interrupt = &marvell_ack_interrupt, 1217140bc929SSergei Poselenov .config_intr = &marvell_config_intr, 1218dcd07be3SAnatolij Gustschin .did_interrupt = &m88e1121_did_interrupt, 12190898b448SSebastian Hesselbarth .resume = &genphy_resume, 12200898b448SSebastian Hesselbarth .suspend = &genphy_suspend, 1221d2fa47d9SAndrew Lunn .get_sset_count = marvell_get_sset_count, 1222d2fa47d9SAndrew Lunn .get_strings = marvell_get_strings, 1223d2fa47d9SAndrew Lunn .get_stats = marvell_get_stats, 1224140bc929SSergei Poselenov }, 1225140bc929SSergei Poselenov { 1226337ac9d5SCyril Chemparathy .phy_id = MARVELL_PHY_ID_88E1318S, 12276ba74014SLinus Torvalds .phy_id_mask = MARVELL_PHY_ID_MASK, 1228337ac9d5SCyril Chemparathy .name = "Marvell 88E1318S", 12293ff1c259SCyril Chemparathy .features = PHY_GBIT_FEATURES, 12303ff1c259SCyril Chemparathy .flags = PHY_HAS_INTERRUPT, 1231d2fa47d9SAndrew Lunn .probe = marvell_probe, 1232fdecf36fSClemens Gruber .config_init = &m88e1121_config_init, 1233337ac9d5SCyril Chemparathy .config_aneg = &m88e1318_config_aneg, 12343ff1c259SCyril Chemparathy .read_status = &marvell_read_status, 12353ff1c259SCyril Chemparathy .ack_interrupt = &marvell_ack_interrupt, 12363ff1c259SCyril Chemparathy .config_intr = &marvell_config_intr, 12373ff1c259SCyril Chemparathy .did_interrupt = &m88e1121_did_interrupt, 12383871c387SMichael Stapelberg .get_wol = &m88e1318_get_wol, 12393871c387SMichael Stapelberg .set_wol = &m88e1318_set_wol, 12400898b448SSebastian Hesselbarth .resume = &genphy_resume, 12410898b448SSebastian Hesselbarth .suspend = &genphy_suspend, 1242d2fa47d9SAndrew Lunn .get_sset_count = marvell_get_sset_count, 1243d2fa47d9SAndrew Lunn .get_strings = marvell_get_strings, 1244d2fa47d9SAndrew Lunn .get_stats = marvell_get_stats, 12453ff1c259SCyril Chemparathy }, 12463ff1c259SCyril Chemparathy { 12472f495c39SBenjamin Herrenschmidt .phy_id = MARVELL_PHY_ID_88E1145, 12482f495c39SBenjamin Herrenschmidt .phy_id_mask = MARVELL_PHY_ID_MASK, 124976884679SAndy Fleming .name = "Marvell 88E1145", 125076884679SAndy Fleming .features = PHY_GBIT_FEATURES, 125176884679SAndy Fleming .flags = PHY_HAS_INTERRUPT, 1252d2fa47d9SAndrew Lunn .probe = marvell_probe, 125376884679SAndy Fleming .config_init = &m88e1145_config_init, 125476884679SAndy Fleming .config_aneg = &marvell_config_aneg, 125576884679SAndy Fleming .read_status = &genphy_read_status, 125676884679SAndy Fleming .ack_interrupt = &marvell_ack_interrupt, 125776884679SAndy Fleming .config_intr = &marvell_config_intr, 12580898b448SSebastian Hesselbarth .resume = &genphy_resume, 12590898b448SSebastian Hesselbarth .suspend = &genphy_suspend, 1260d2fa47d9SAndrew Lunn .get_sset_count = marvell_get_sset_count, 1261d2fa47d9SAndrew Lunn .get_strings = marvell_get_strings, 1262d2fa47d9SAndrew Lunn .get_stats = marvell_get_stats, 1263ac8c635aSOlof Johansson }, 1264ac8c635aSOlof Johansson { 126590600732SDavid Daney .phy_id = MARVELL_PHY_ID_88E1149R, 126690600732SDavid Daney .phy_id_mask = MARVELL_PHY_ID_MASK, 126790600732SDavid Daney .name = "Marvell 88E1149R", 126890600732SDavid Daney .features = PHY_GBIT_FEATURES, 126990600732SDavid Daney .flags = PHY_HAS_INTERRUPT, 1270d2fa47d9SAndrew Lunn .probe = marvell_probe, 127190600732SDavid Daney .config_init = &m88e1149_config_init, 127290600732SDavid Daney .config_aneg = &m88e1118_config_aneg, 127390600732SDavid Daney .read_status = &genphy_read_status, 127490600732SDavid Daney .ack_interrupt = &marvell_ack_interrupt, 127590600732SDavid Daney .config_intr = &marvell_config_intr, 12760898b448SSebastian Hesselbarth .resume = &genphy_resume, 12770898b448SSebastian Hesselbarth .suspend = &genphy_suspend, 1278d2fa47d9SAndrew Lunn .get_sset_count = marvell_get_sset_count, 1279d2fa47d9SAndrew Lunn .get_strings = marvell_get_strings, 1280d2fa47d9SAndrew Lunn .get_stats = marvell_get_stats, 128190600732SDavid Daney }, 128290600732SDavid Daney { 12832f495c39SBenjamin Herrenschmidt .phy_id = MARVELL_PHY_ID_88E1240, 12842f495c39SBenjamin Herrenschmidt .phy_id_mask = MARVELL_PHY_ID_MASK, 1285ac8c635aSOlof Johansson .name = "Marvell 88E1240", 1286ac8c635aSOlof Johansson .features = PHY_GBIT_FEATURES, 1287ac8c635aSOlof Johansson .flags = PHY_HAS_INTERRUPT, 1288d2fa47d9SAndrew Lunn .probe = marvell_probe, 1289ac8c635aSOlof Johansson .config_init = &m88e1111_config_init, 1290ac8c635aSOlof Johansson .config_aneg = &marvell_config_aneg, 1291ac8c635aSOlof Johansson .read_status = &genphy_read_status, 1292ac8c635aSOlof Johansson .ack_interrupt = &marvell_ack_interrupt, 1293ac8c635aSOlof Johansson .config_intr = &marvell_config_intr, 12940898b448SSebastian Hesselbarth .resume = &genphy_resume, 12950898b448SSebastian Hesselbarth .suspend = &genphy_suspend, 1296d2fa47d9SAndrew Lunn .get_sset_count = marvell_get_sset_count, 1297d2fa47d9SAndrew Lunn .get_strings = marvell_get_strings, 1298d2fa47d9SAndrew Lunn .get_stats = marvell_get_stats, 1299ac8c635aSOlof Johansson }, 13003da09a51SMichal Simek { 13013da09a51SMichal Simek .phy_id = MARVELL_PHY_ID_88E1116R, 13023da09a51SMichal Simek .phy_id_mask = MARVELL_PHY_ID_MASK, 13033da09a51SMichal Simek .name = "Marvell 88E1116R", 13043da09a51SMichal Simek .features = PHY_GBIT_FEATURES, 13053da09a51SMichal Simek .flags = PHY_HAS_INTERRUPT, 1306d2fa47d9SAndrew Lunn .probe = marvell_probe, 13073da09a51SMichal Simek .config_init = &m88e1116r_config_init, 13083da09a51SMichal Simek .config_aneg = &genphy_config_aneg, 13093da09a51SMichal Simek .read_status = &genphy_read_status, 13103da09a51SMichal Simek .ack_interrupt = &marvell_ack_interrupt, 13113da09a51SMichal Simek .config_intr = &marvell_config_intr, 13120898b448SSebastian Hesselbarth .resume = &genphy_resume, 13130898b448SSebastian Hesselbarth .suspend = &genphy_suspend, 1314d2fa47d9SAndrew Lunn .get_sset_count = marvell_get_sset_count, 1315d2fa47d9SAndrew Lunn .get_strings = marvell_get_strings, 1316d2fa47d9SAndrew Lunn .get_stats = marvell_get_stats, 13173da09a51SMichal Simek }, 131810e24caaSMichal Simek { 131910e24caaSMichal Simek .phy_id = MARVELL_PHY_ID_88E1510, 132010e24caaSMichal Simek .phy_id_mask = MARVELL_PHY_ID_MASK, 132110e24caaSMichal Simek .name = "Marvell 88E1510", 132210e24caaSMichal Simek .features = PHY_GBIT_FEATURES, 132310e24caaSMichal Simek .flags = PHY_HAS_INTERRUPT, 1324d2fa47d9SAndrew Lunn .probe = marvell_probe, 1325930b37eeSStefan Roese .config_init = &m88e1510_config_init, 132610e24caaSMichal Simek .config_aneg = &m88e1510_config_aneg, 132710e24caaSMichal Simek .read_status = &marvell_read_status, 132810e24caaSMichal Simek .ack_interrupt = &marvell_ack_interrupt, 132910e24caaSMichal Simek .config_intr = &marvell_config_intr, 133010e24caaSMichal Simek .did_interrupt = &m88e1121_did_interrupt, 13310898b448SSebastian Hesselbarth .resume = &genphy_resume, 13320898b448SSebastian Hesselbarth .suspend = &genphy_suspend, 1333d2fa47d9SAndrew Lunn .get_sset_count = marvell_get_sset_count, 1334d2fa47d9SAndrew Lunn .get_strings = marvell_get_strings, 1335d2fa47d9SAndrew Lunn .get_stats = marvell_get_stats, 133610e24caaSMichal Simek }, 13376b358aedSSebastian Hesselbarth { 1338819ec8e1SAndrew Lunn .phy_id = MARVELL_PHY_ID_88E1540, 1339819ec8e1SAndrew Lunn .phy_id_mask = MARVELL_PHY_ID_MASK, 1340819ec8e1SAndrew Lunn .name = "Marvell 88E1540", 1341819ec8e1SAndrew Lunn .features = PHY_GBIT_FEATURES, 1342819ec8e1SAndrew Lunn .flags = PHY_HAS_INTERRUPT, 1343d2fa47d9SAndrew Lunn .probe = marvell_probe, 134479be1a1cSClemens Gruber .config_init = &marvell_config_init, 1345819ec8e1SAndrew Lunn .config_aneg = &m88e1510_config_aneg, 1346819ec8e1SAndrew Lunn .read_status = &marvell_read_status, 1347819ec8e1SAndrew Lunn .ack_interrupt = &marvell_ack_interrupt, 1348819ec8e1SAndrew Lunn .config_intr = &marvell_config_intr, 1349819ec8e1SAndrew Lunn .did_interrupt = &m88e1121_did_interrupt, 1350819ec8e1SAndrew Lunn .resume = &genphy_resume, 1351819ec8e1SAndrew Lunn .suspend = &genphy_suspend, 1352d2fa47d9SAndrew Lunn .get_sset_count = marvell_get_sset_count, 1353d2fa47d9SAndrew Lunn .get_strings = marvell_get_strings, 1354d2fa47d9SAndrew Lunn .get_stats = marvell_get_stats, 1355819ec8e1SAndrew Lunn }, 1356819ec8e1SAndrew Lunn { 13576b358aedSSebastian Hesselbarth .phy_id = MARVELL_PHY_ID_88E3016, 13586b358aedSSebastian Hesselbarth .phy_id_mask = MARVELL_PHY_ID_MASK, 13596b358aedSSebastian Hesselbarth .name = "Marvell 88E3016", 13606b358aedSSebastian Hesselbarth .features = PHY_BASIC_FEATURES, 13616b358aedSSebastian Hesselbarth .flags = PHY_HAS_INTERRUPT, 1362d2fa47d9SAndrew Lunn .probe = marvell_probe, 13636b358aedSSebastian Hesselbarth .config_aneg = &genphy_config_aneg, 13646b358aedSSebastian Hesselbarth .config_init = &m88e3016_config_init, 13656b358aedSSebastian Hesselbarth .aneg_done = &marvell_aneg_done, 13666b358aedSSebastian Hesselbarth .read_status = &marvell_read_status, 13676b358aedSSebastian Hesselbarth .ack_interrupt = &marvell_ack_interrupt, 13686b358aedSSebastian Hesselbarth .config_intr = &marvell_config_intr, 13696b358aedSSebastian Hesselbarth .did_interrupt = &m88e1121_did_interrupt, 13706b358aedSSebastian Hesselbarth .resume = &genphy_resume, 13716b358aedSSebastian Hesselbarth .suspend = &genphy_suspend, 1372d2fa47d9SAndrew Lunn .get_sset_count = marvell_get_sset_count, 1373d2fa47d9SAndrew Lunn .get_strings = marvell_get_strings, 1374d2fa47d9SAndrew Lunn .get_stats = marvell_get_stats, 13756b358aedSSebastian Hesselbarth }, 137676884679SAndy Fleming }; 137776884679SAndy Fleming 137850fd7150SJohan Hovold module_phy_driver(marvell_drivers); 13794e4f10f6SDavid Woodhouse 1380cf93c945SUwe Kleine-König static struct mdio_device_id __maybe_unused marvell_tbl[] = { 1381f5e1cabfSMichal Simek { MARVELL_PHY_ID_88E1101, MARVELL_PHY_ID_MASK }, 1382f5e1cabfSMichal Simek { MARVELL_PHY_ID_88E1112, MARVELL_PHY_ID_MASK }, 1383f5e1cabfSMichal Simek { MARVELL_PHY_ID_88E1111, MARVELL_PHY_ID_MASK }, 1384f5e1cabfSMichal Simek { MARVELL_PHY_ID_88E1118, MARVELL_PHY_ID_MASK }, 1385f5e1cabfSMichal Simek { MARVELL_PHY_ID_88E1121R, MARVELL_PHY_ID_MASK }, 1386f5e1cabfSMichal Simek { MARVELL_PHY_ID_88E1145, MARVELL_PHY_ID_MASK }, 1387f5e1cabfSMichal Simek { MARVELL_PHY_ID_88E1149R, MARVELL_PHY_ID_MASK }, 1388f5e1cabfSMichal Simek { MARVELL_PHY_ID_88E1240, MARVELL_PHY_ID_MASK }, 1389f5e1cabfSMichal Simek { MARVELL_PHY_ID_88E1318S, MARVELL_PHY_ID_MASK }, 13903da09a51SMichal Simek { MARVELL_PHY_ID_88E1116R, MARVELL_PHY_ID_MASK }, 139110e24caaSMichal Simek { MARVELL_PHY_ID_88E1510, MARVELL_PHY_ID_MASK }, 1392819ec8e1SAndrew Lunn { MARVELL_PHY_ID_88E1540, MARVELL_PHY_ID_MASK }, 13936b358aedSSebastian Hesselbarth { MARVELL_PHY_ID_88E3016, MARVELL_PHY_ID_MASK }, 13944e4f10f6SDavid Woodhouse { } 13954e4f10f6SDavid Woodhouse }; 13964e4f10f6SDavid Woodhouse 13974e4f10f6SDavid Woodhouse MODULE_DEVICE_TABLE(mdio, marvell_tbl); 1398