xref: /openbmc/linux/drivers/net/phy/marvell.c (revision 6cfb3bcc)
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 
1416cfb3bccSCharles-Antoine Couret #define LPA_FIBER_1000HALF	0x40
1426cfb3bccSCharles-Antoine Couret #define LPA_FIBER_1000FULL	0x20
1436cfb3bccSCharles-Antoine Couret 
1446cfb3bccSCharles-Antoine Couret #define LPA_PAUSE_FIBER	0x180
1456cfb3bccSCharles-Antoine Couret #define LPA_PAUSE_ASYM_FIBER	0x100
1466cfb3bccSCharles-Antoine Couret 
1476cfb3bccSCharles-Antoine Couret #define ADVERTISE_FIBER_1000HALF	0x40
1486cfb3bccSCharles-Antoine Couret #define ADVERTISE_FIBER_1000FULL	0x20
1496cfb3bccSCharles-Antoine Couret 
1506cfb3bccSCharles-Antoine Couret #define ADVERTISE_PAUSE_FIBER		0x180
1516cfb3bccSCharles-Antoine Couret #define ADVERTISE_PAUSE_ASYM_FIBER	0x100
1526cfb3bccSCharles-Antoine Couret 
1536cfb3bccSCharles-Antoine Couret #define REGISTER_LINK_STATUS	0x400
1546cfb3bccSCharles-Antoine Couret 
15500db8189SAndy Fleming MODULE_DESCRIPTION("Marvell PHY driver");
15600db8189SAndy Fleming MODULE_AUTHOR("Andy Fleming");
15700db8189SAndy Fleming MODULE_LICENSE("GPL");
15800db8189SAndy Fleming 
159d2fa47d9SAndrew Lunn struct marvell_hw_stat {
160d2fa47d9SAndrew Lunn 	const char *string;
161d2fa47d9SAndrew Lunn 	u8 page;
162d2fa47d9SAndrew Lunn 	u8 reg;
163d2fa47d9SAndrew Lunn 	u8 bits;
164d2fa47d9SAndrew Lunn };
165d2fa47d9SAndrew Lunn 
166d2fa47d9SAndrew Lunn static struct marvell_hw_stat marvell_hw_stats[] = {
167d2fa47d9SAndrew Lunn 	{ "phy_receive_errors", 0, 21, 16},
168d2fa47d9SAndrew Lunn 	{ "phy_idle_errors", 0, 10, 8 },
169d2fa47d9SAndrew Lunn };
170d2fa47d9SAndrew Lunn 
171d2fa47d9SAndrew Lunn struct marvell_priv {
172d2fa47d9SAndrew Lunn 	u64 stats[ARRAY_SIZE(marvell_hw_stats)];
173d2fa47d9SAndrew Lunn };
174d2fa47d9SAndrew Lunn 
17500db8189SAndy Fleming static int marvell_ack_interrupt(struct phy_device *phydev)
17600db8189SAndy Fleming {
17700db8189SAndy Fleming 	int err;
17800db8189SAndy Fleming 
17900db8189SAndy Fleming 	/* Clear the interrupts by reading the reg */
18000db8189SAndy Fleming 	err = phy_read(phydev, MII_M1011_IEVENT);
18100db8189SAndy Fleming 
18200db8189SAndy Fleming 	if (err < 0)
18300db8189SAndy Fleming 		return err;
18400db8189SAndy Fleming 
18500db8189SAndy Fleming 	return 0;
18600db8189SAndy Fleming }
18700db8189SAndy Fleming 
18800db8189SAndy Fleming static int marvell_config_intr(struct phy_device *phydev)
18900db8189SAndy Fleming {
19000db8189SAndy Fleming 	int err;
19100db8189SAndy Fleming 
19200db8189SAndy Fleming 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
19300db8189SAndy Fleming 		err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_INIT);
19400db8189SAndy Fleming 	else
19500db8189SAndy Fleming 		err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR);
19600db8189SAndy Fleming 
19700db8189SAndy Fleming 	return err;
19800db8189SAndy Fleming }
19900db8189SAndy Fleming 
200239aa55bSDavid Thomson static int marvell_set_polarity(struct phy_device *phydev, int polarity)
201239aa55bSDavid Thomson {
202239aa55bSDavid Thomson 	int reg;
203239aa55bSDavid Thomson 	int err;
204239aa55bSDavid Thomson 	int val;
205239aa55bSDavid Thomson 
206239aa55bSDavid Thomson 	/* get the current settings */
207239aa55bSDavid Thomson 	reg = phy_read(phydev, MII_M1011_PHY_SCR);
208239aa55bSDavid Thomson 	if (reg < 0)
209239aa55bSDavid Thomson 		return reg;
210239aa55bSDavid Thomson 
211239aa55bSDavid Thomson 	val = reg;
212239aa55bSDavid Thomson 	val &= ~MII_M1011_PHY_SCR_AUTO_CROSS;
213239aa55bSDavid Thomson 	switch (polarity) {
214239aa55bSDavid Thomson 	case ETH_TP_MDI:
215239aa55bSDavid Thomson 		val |= MII_M1011_PHY_SCR_MDI;
216239aa55bSDavid Thomson 		break;
217239aa55bSDavid Thomson 	case ETH_TP_MDI_X:
218239aa55bSDavid Thomson 		val |= MII_M1011_PHY_SCR_MDI_X;
219239aa55bSDavid Thomson 		break;
220239aa55bSDavid Thomson 	case ETH_TP_MDI_AUTO:
221239aa55bSDavid Thomson 	case ETH_TP_MDI_INVALID:
222239aa55bSDavid Thomson 	default:
223239aa55bSDavid Thomson 		val |= MII_M1011_PHY_SCR_AUTO_CROSS;
224239aa55bSDavid Thomson 		break;
225239aa55bSDavid Thomson 	}
226239aa55bSDavid Thomson 
227239aa55bSDavid Thomson 	if (val != reg) {
228239aa55bSDavid Thomson 		/* Set the new polarity value in the register */
229239aa55bSDavid Thomson 		err = phy_write(phydev, MII_M1011_PHY_SCR, val);
230239aa55bSDavid Thomson 		if (err)
231239aa55bSDavid Thomson 			return err;
232239aa55bSDavid Thomson 	}
233239aa55bSDavid Thomson 
234239aa55bSDavid Thomson 	return 0;
235239aa55bSDavid Thomson }
236239aa55bSDavid Thomson 
23700db8189SAndy Fleming static int marvell_config_aneg(struct phy_device *phydev)
23800db8189SAndy Fleming {
23900db8189SAndy Fleming 	int err;
24000db8189SAndy Fleming 
24100db8189SAndy Fleming 	/* The Marvell PHY has an errata which requires
24200db8189SAndy Fleming 	 * that certain registers get written in order
24300db8189SAndy Fleming 	 * to restart autonegotiation */
24400db8189SAndy Fleming 	err = phy_write(phydev, MII_BMCR, BMCR_RESET);
24500db8189SAndy Fleming 
24600db8189SAndy Fleming 	if (err < 0)
24700db8189SAndy Fleming 		return err;
24800db8189SAndy Fleming 
24900db8189SAndy Fleming 	err = phy_write(phydev, 0x1d, 0x1f);
25000db8189SAndy Fleming 	if (err < 0)
25100db8189SAndy Fleming 		return err;
25200db8189SAndy Fleming 
25300db8189SAndy Fleming 	err = phy_write(phydev, 0x1e, 0x200c);
25400db8189SAndy Fleming 	if (err < 0)
25500db8189SAndy Fleming 		return err;
25600db8189SAndy Fleming 
25700db8189SAndy Fleming 	err = phy_write(phydev, 0x1d, 0x5);
25800db8189SAndy Fleming 	if (err < 0)
25900db8189SAndy Fleming 		return err;
26000db8189SAndy Fleming 
26100db8189SAndy Fleming 	err = phy_write(phydev, 0x1e, 0);
26200db8189SAndy Fleming 	if (err < 0)
26300db8189SAndy Fleming 		return err;
26400db8189SAndy Fleming 
26500db8189SAndy Fleming 	err = phy_write(phydev, 0x1e, 0x100);
26600db8189SAndy Fleming 	if (err < 0)
26700db8189SAndy Fleming 		return err;
26800db8189SAndy Fleming 
269239aa55bSDavid Thomson 	err = marvell_set_polarity(phydev, phydev->mdix);
27076884679SAndy Fleming 	if (err < 0)
27176884679SAndy Fleming 		return err;
27276884679SAndy Fleming 
27376884679SAndy Fleming 	err = phy_write(phydev, MII_M1111_PHY_LED_CONTROL,
27476884679SAndy Fleming 			MII_M1111_PHY_LED_DIRECT);
27576884679SAndy Fleming 	if (err < 0)
27676884679SAndy Fleming 		return err;
27700db8189SAndy Fleming 
27800db8189SAndy Fleming 	err = genphy_config_aneg(phydev);
2798ff44985SAnton Vorontsov 	if (err < 0)
28000db8189SAndy Fleming 		return err;
2818ff44985SAnton Vorontsov 
2828ff44985SAnton Vorontsov 	if (phydev->autoneg != AUTONEG_ENABLE) {
2838ff44985SAnton Vorontsov 		int bmcr;
2848ff44985SAnton Vorontsov 
2858ff44985SAnton Vorontsov 		/*
2868ff44985SAnton Vorontsov 		 * A write to speed/duplex bits (that is performed by
2878ff44985SAnton Vorontsov 		 * genphy_config_aneg() call above) must be followed by
2888ff44985SAnton Vorontsov 		 * a software reset. Otherwise, the write has no effect.
2898ff44985SAnton Vorontsov 		 */
2908ff44985SAnton Vorontsov 		bmcr = phy_read(phydev, MII_BMCR);
2918ff44985SAnton Vorontsov 		if (bmcr < 0)
2928ff44985SAnton Vorontsov 			return bmcr;
2938ff44985SAnton Vorontsov 
2948ff44985SAnton Vorontsov 		err = phy_write(phydev, MII_BMCR, bmcr | BMCR_RESET);
2958ff44985SAnton Vorontsov 		if (err < 0)
2968ff44985SAnton Vorontsov 			return err;
2978ff44985SAnton Vorontsov 	}
2988ff44985SAnton Vorontsov 
2998ff44985SAnton Vorontsov 	return 0;
30000db8189SAndy Fleming }
30100db8189SAndy Fleming 
3023ec0a0f1SHarini Katakam static int m88e1111_config_aneg(struct phy_device *phydev)
3033ec0a0f1SHarini Katakam {
3043ec0a0f1SHarini Katakam 	int err;
3053ec0a0f1SHarini Katakam 
3063ec0a0f1SHarini Katakam 	/* The Marvell PHY has an errata which requires
3073ec0a0f1SHarini Katakam 	 * that certain registers get written in order
3083ec0a0f1SHarini Katakam 	 * to restart autonegotiation
3093ec0a0f1SHarini Katakam 	 */
3103ec0a0f1SHarini Katakam 	err = phy_write(phydev, MII_BMCR, BMCR_RESET);
3113ec0a0f1SHarini Katakam 
3123ec0a0f1SHarini Katakam 	err = marvell_set_polarity(phydev, phydev->mdix);
3133ec0a0f1SHarini Katakam 	if (err < 0)
3143ec0a0f1SHarini Katakam 		return err;
3153ec0a0f1SHarini Katakam 
3163ec0a0f1SHarini Katakam 	err = phy_write(phydev, MII_M1111_PHY_LED_CONTROL,
3173ec0a0f1SHarini Katakam 			MII_M1111_PHY_LED_DIRECT);
3183ec0a0f1SHarini Katakam 	if (err < 0)
3193ec0a0f1SHarini Katakam 		return err;
3203ec0a0f1SHarini Katakam 
3213ec0a0f1SHarini Katakam 	err = genphy_config_aneg(phydev);
3223ec0a0f1SHarini Katakam 	if (err < 0)
3233ec0a0f1SHarini Katakam 		return err;
3243ec0a0f1SHarini Katakam 
3253ec0a0f1SHarini Katakam 	if (phydev->autoneg != AUTONEG_ENABLE) {
3263ec0a0f1SHarini Katakam 		int bmcr;
3273ec0a0f1SHarini Katakam 
3283ec0a0f1SHarini Katakam 		/* A write to speed/duplex bits (that is performed by
3293ec0a0f1SHarini Katakam 		 * genphy_config_aneg() call above) must be followed by
3303ec0a0f1SHarini Katakam 		 * a software reset. Otherwise, the write has no effect.
3313ec0a0f1SHarini Katakam 		 */
3323ec0a0f1SHarini Katakam 		bmcr = phy_read(phydev, MII_BMCR);
3333ec0a0f1SHarini Katakam 		if (bmcr < 0)
3343ec0a0f1SHarini Katakam 			return bmcr;
3353ec0a0f1SHarini Katakam 
3363ec0a0f1SHarini Katakam 		err = phy_write(phydev, MII_BMCR, bmcr | BMCR_RESET);
3373ec0a0f1SHarini Katakam 		if (err < 0)
3383ec0a0f1SHarini Katakam 			return err;
3393ec0a0f1SHarini Katakam 	}
3403ec0a0f1SHarini Katakam 
3413ec0a0f1SHarini Katakam 	return 0;
3423ec0a0f1SHarini Katakam }
3433ec0a0f1SHarini Katakam 
344cf41a51dSDavid Daney #ifdef CONFIG_OF_MDIO
345cf41a51dSDavid Daney /*
346cf41a51dSDavid Daney  * Set and/or override some configuration registers based on the
347cf41a51dSDavid Daney  * marvell,reg-init property stored in the of_node for the phydev.
348cf41a51dSDavid Daney  *
349cf41a51dSDavid Daney  * marvell,reg-init = <reg-page reg mask value>,...;
350cf41a51dSDavid Daney  *
351cf41a51dSDavid Daney  * There may be one or more sets of <reg-page reg mask value>:
352cf41a51dSDavid Daney  *
353cf41a51dSDavid Daney  * reg-page: which register bank to use.
354cf41a51dSDavid Daney  * reg: the register.
355cf41a51dSDavid Daney  * mask: if non-zero, ANDed with existing register value.
356cf41a51dSDavid Daney  * value: ORed with the masked value and written to the regiser.
357cf41a51dSDavid Daney  *
358cf41a51dSDavid Daney  */
359cf41a51dSDavid Daney static int marvell_of_reg_init(struct phy_device *phydev)
360cf41a51dSDavid Daney {
361cf41a51dSDavid Daney 	const __be32 *paddr;
362cf41a51dSDavid Daney 	int len, i, saved_page, current_page, page_changed, ret;
363cf41a51dSDavid Daney 
364e5a03bfdSAndrew Lunn 	if (!phydev->mdio.dev.of_node)
365cf41a51dSDavid Daney 		return 0;
366cf41a51dSDavid Daney 
367e5a03bfdSAndrew Lunn 	paddr = of_get_property(phydev->mdio.dev.of_node,
368e5a03bfdSAndrew Lunn 				"marvell,reg-init", &len);
369cf41a51dSDavid Daney 	if (!paddr || len < (4 * sizeof(*paddr)))
370cf41a51dSDavid Daney 		return 0;
371cf41a51dSDavid Daney 
372cf41a51dSDavid Daney 	saved_page = phy_read(phydev, MII_MARVELL_PHY_PAGE);
373cf41a51dSDavid Daney 	if (saved_page < 0)
374cf41a51dSDavid Daney 		return saved_page;
375cf41a51dSDavid Daney 	page_changed = 0;
376cf41a51dSDavid Daney 	current_page = saved_page;
377cf41a51dSDavid Daney 
378cf41a51dSDavid Daney 	ret = 0;
379cf41a51dSDavid Daney 	len /= sizeof(*paddr);
380cf41a51dSDavid Daney 	for (i = 0; i < len - 3; i += 4) {
381cf41a51dSDavid Daney 		u16 reg_page = be32_to_cpup(paddr + i);
382cf41a51dSDavid Daney 		u16 reg = be32_to_cpup(paddr + i + 1);
383cf41a51dSDavid Daney 		u16 mask = be32_to_cpup(paddr + i + 2);
384cf41a51dSDavid Daney 		u16 val_bits = be32_to_cpup(paddr + i + 3);
385cf41a51dSDavid Daney 		int val;
386cf41a51dSDavid Daney 
387cf41a51dSDavid Daney 		if (reg_page != current_page) {
388cf41a51dSDavid Daney 			current_page = reg_page;
389cf41a51dSDavid Daney 			page_changed = 1;
390cf41a51dSDavid Daney 			ret = phy_write(phydev, MII_MARVELL_PHY_PAGE, reg_page);
391cf41a51dSDavid Daney 			if (ret < 0)
392cf41a51dSDavid Daney 				goto err;
393cf41a51dSDavid Daney 		}
394cf41a51dSDavid Daney 
395cf41a51dSDavid Daney 		val = 0;
396cf41a51dSDavid Daney 		if (mask) {
397cf41a51dSDavid Daney 			val = phy_read(phydev, reg);
398cf41a51dSDavid Daney 			if (val < 0) {
399cf41a51dSDavid Daney 				ret = val;
400cf41a51dSDavid Daney 				goto err;
401cf41a51dSDavid Daney 			}
402cf41a51dSDavid Daney 			val &= mask;
403cf41a51dSDavid Daney 		}
404cf41a51dSDavid Daney 		val |= val_bits;
405cf41a51dSDavid Daney 
406cf41a51dSDavid Daney 		ret = phy_write(phydev, reg, val);
407cf41a51dSDavid Daney 		if (ret < 0)
408cf41a51dSDavid Daney 			goto err;
409cf41a51dSDavid Daney 
410cf41a51dSDavid Daney 	}
411cf41a51dSDavid Daney err:
412cf41a51dSDavid Daney 	if (page_changed) {
413cf41a51dSDavid Daney 		i = phy_write(phydev, MII_MARVELL_PHY_PAGE, saved_page);
414cf41a51dSDavid Daney 		if (ret == 0)
415cf41a51dSDavid Daney 			ret = i;
416cf41a51dSDavid Daney 	}
417cf41a51dSDavid Daney 	return ret;
418cf41a51dSDavid Daney }
419cf41a51dSDavid Daney #else
420cf41a51dSDavid Daney static int marvell_of_reg_init(struct phy_device *phydev)
421cf41a51dSDavid Daney {
422cf41a51dSDavid Daney 	return 0;
423cf41a51dSDavid Daney }
424cf41a51dSDavid Daney #endif /* CONFIG_OF_MDIO */
425cf41a51dSDavid Daney 
426140bc929SSergei Poselenov static int m88e1121_config_aneg(struct phy_device *phydev)
427140bc929SSergei Poselenov {
428c477d044SCyril Chemparathy 	int err, oldpage, mscr;
429c477d044SCyril Chemparathy 
43027d916d6SDavid Daney 	oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE);
431c477d044SCyril Chemparathy 
43227d916d6SDavid Daney 	err = phy_write(phydev, MII_MARVELL_PHY_PAGE,
433c477d044SCyril Chemparathy 			MII_88E1121_PHY_MSCR_PAGE);
434c477d044SCyril Chemparathy 	if (err < 0)
435c477d044SCyril Chemparathy 		return err;
436be8c6480SArnaud Patard 
43732a64161SFlorian Fainelli 	if (phy_interface_is_rgmii(phydev)) {
438be8c6480SArnaud Patard 
439c477d044SCyril Chemparathy 		mscr = phy_read(phydev, MII_88E1121_PHY_MSCR_REG) &
440c477d044SCyril Chemparathy 			MII_88E1121_PHY_MSCR_DELAY_MASK;
441c477d044SCyril Chemparathy 
442c477d044SCyril Chemparathy 		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
443c477d044SCyril Chemparathy 			mscr |= (MII_88E1121_PHY_MSCR_RX_DELAY |
444c477d044SCyril Chemparathy 				 MII_88E1121_PHY_MSCR_TX_DELAY);
445c477d044SCyril Chemparathy 		else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
446c477d044SCyril Chemparathy 			mscr |= MII_88E1121_PHY_MSCR_RX_DELAY;
447c477d044SCyril Chemparathy 		else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
448c477d044SCyril Chemparathy 			mscr |= MII_88E1121_PHY_MSCR_TX_DELAY;
449c477d044SCyril Chemparathy 
450c477d044SCyril Chemparathy 		err = phy_write(phydev, MII_88E1121_PHY_MSCR_REG, mscr);
451c477d044SCyril Chemparathy 		if (err < 0)
452c477d044SCyril Chemparathy 			return err;
453be8c6480SArnaud Patard 	}
454c477d044SCyril Chemparathy 
45527d916d6SDavid Daney 	phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage);
456140bc929SSergei Poselenov 
457140bc929SSergei Poselenov 	err = phy_write(phydev, MII_BMCR, BMCR_RESET);
458140bc929SSergei Poselenov 	if (err < 0)
459140bc929SSergei Poselenov 		return err;
460140bc929SSergei Poselenov 
461140bc929SSergei Poselenov 	err = phy_write(phydev, MII_M1011_PHY_SCR,
462140bc929SSergei Poselenov 			MII_M1011_PHY_SCR_AUTO_CROSS);
463140bc929SSergei Poselenov 	if (err < 0)
464140bc929SSergei Poselenov 		return err;
465140bc929SSergei Poselenov 
466fdecf36fSClemens Gruber 	return genphy_config_aneg(phydev);
467140bc929SSergei Poselenov }
468140bc929SSergei Poselenov 
469337ac9d5SCyril Chemparathy static int m88e1318_config_aneg(struct phy_device *phydev)
4703ff1c259SCyril Chemparathy {
4713ff1c259SCyril Chemparathy 	int err, oldpage, mscr;
4723ff1c259SCyril Chemparathy 
47327d916d6SDavid Daney 	oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE);
4743ff1c259SCyril Chemparathy 
47527d916d6SDavid Daney 	err = phy_write(phydev, MII_MARVELL_PHY_PAGE,
4763ff1c259SCyril Chemparathy 			MII_88E1121_PHY_MSCR_PAGE);
4773ff1c259SCyril Chemparathy 	if (err < 0)
4783ff1c259SCyril Chemparathy 		return err;
4793ff1c259SCyril Chemparathy 
480337ac9d5SCyril Chemparathy 	mscr = phy_read(phydev, MII_88E1318S_PHY_MSCR1_REG);
481337ac9d5SCyril Chemparathy 	mscr |= MII_88E1318S_PHY_MSCR1_PAD_ODD;
4823ff1c259SCyril Chemparathy 
483337ac9d5SCyril Chemparathy 	err = phy_write(phydev, MII_88E1318S_PHY_MSCR1_REG, mscr);
4843ff1c259SCyril Chemparathy 	if (err < 0)
4853ff1c259SCyril Chemparathy 		return err;
4863ff1c259SCyril Chemparathy 
48727d916d6SDavid Daney 	err = phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage);
4883ff1c259SCyril Chemparathy 	if (err < 0)
4893ff1c259SCyril Chemparathy 		return err;
4903ff1c259SCyril Chemparathy 
4913ff1c259SCyril Chemparathy 	return m88e1121_config_aneg(phydev);
4923ff1c259SCyril Chemparathy }
4933ff1c259SCyril Chemparathy 
49410e24caaSMichal Simek static int m88e1510_config_aneg(struct phy_device *phydev)
49510e24caaSMichal Simek {
49610e24caaSMichal Simek 	int err;
49710e24caaSMichal Simek 
49810e24caaSMichal Simek 	err = m88e1318_config_aneg(phydev);
49910e24caaSMichal Simek 	if (err < 0)
50010e24caaSMichal Simek 		return err;
50110e24caaSMichal Simek 
50279be1a1cSClemens Gruber 	return 0;
50379be1a1cSClemens Gruber }
50479be1a1cSClemens Gruber 
50579be1a1cSClemens Gruber static int marvell_config_init(struct phy_device *phydev)
50679be1a1cSClemens Gruber {
50779be1a1cSClemens Gruber 	/* Set registers from marvell,reg-init DT property */
50810e24caaSMichal Simek 	return marvell_of_reg_init(phydev);
50910e24caaSMichal Simek }
51010e24caaSMichal Simek 
5113da09a51SMichal Simek static int m88e1116r_config_init(struct phy_device *phydev)
5123da09a51SMichal Simek {
5133da09a51SMichal Simek 	int temp;
5143da09a51SMichal Simek 	int err;
5153da09a51SMichal Simek 
5163da09a51SMichal Simek 	temp = phy_read(phydev, MII_BMCR);
5173da09a51SMichal Simek 	temp |= BMCR_RESET;
5183da09a51SMichal Simek 	err = phy_write(phydev, MII_BMCR, temp);
5193da09a51SMichal Simek 	if (err < 0)
5203da09a51SMichal Simek 		return err;
5213da09a51SMichal Simek 
5223da09a51SMichal Simek 	mdelay(500);
5233da09a51SMichal Simek 
5243da09a51SMichal Simek 	err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0);
5253da09a51SMichal Simek 	if (err < 0)
5263da09a51SMichal Simek 		return err;
5273da09a51SMichal Simek 
5283da09a51SMichal Simek 	temp = phy_read(phydev, MII_M1011_PHY_SCR);
5293da09a51SMichal Simek 	temp |= (7 << 12);	/* max number of gigabit attempts */
5303da09a51SMichal Simek 	temp |= (1 << 11);	/* enable downshift */
5313da09a51SMichal Simek 	temp |= MII_M1011_PHY_SCR_AUTO_CROSS;
5323da09a51SMichal Simek 	err = phy_write(phydev, MII_M1011_PHY_SCR, temp);
5333da09a51SMichal Simek 	if (err < 0)
5343da09a51SMichal Simek 		return err;
5353da09a51SMichal Simek 
5363da09a51SMichal Simek 	err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 2);
5373da09a51SMichal Simek 	if (err < 0)
5383da09a51SMichal Simek 		return err;
5393da09a51SMichal Simek 	temp = phy_read(phydev, MII_M1116R_CONTROL_REG_MAC);
5403da09a51SMichal Simek 	temp |= (1 << 5);
5413da09a51SMichal Simek 	temp |= (1 << 4);
5423da09a51SMichal Simek 	err = phy_write(phydev, MII_M1116R_CONTROL_REG_MAC, temp);
5433da09a51SMichal Simek 	if (err < 0)
5443da09a51SMichal Simek 		return err;
5453da09a51SMichal Simek 	err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0);
5463da09a51SMichal Simek 	if (err < 0)
5473da09a51SMichal Simek 		return err;
5483da09a51SMichal Simek 
5493da09a51SMichal Simek 	temp = phy_read(phydev, MII_BMCR);
5503da09a51SMichal Simek 	temp |= BMCR_RESET;
5513da09a51SMichal Simek 	err = phy_write(phydev, MII_BMCR, temp);
5523da09a51SMichal Simek 	if (err < 0)
5533da09a51SMichal Simek 		return err;
5543da09a51SMichal Simek 
5553da09a51SMichal Simek 	mdelay(500);
5563da09a51SMichal Simek 
55779be1a1cSClemens Gruber 	return marvell_config_init(phydev);
5583da09a51SMichal Simek }
5593da09a51SMichal Simek 
5606b358aedSSebastian Hesselbarth static int m88e3016_config_init(struct phy_device *phydev)
5616b358aedSSebastian Hesselbarth {
5626b358aedSSebastian Hesselbarth 	int reg;
5636b358aedSSebastian Hesselbarth 
5646b358aedSSebastian Hesselbarth 	/* Enable Scrambler and Auto-Crossover */
5656b358aedSSebastian Hesselbarth 	reg = phy_read(phydev, MII_88E3016_PHY_SPEC_CTRL);
5666b358aedSSebastian Hesselbarth 	if (reg < 0)
5676b358aedSSebastian Hesselbarth 		return reg;
5686b358aedSSebastian Hesselbarth 
5696b358aedSSebastian Hesselbarth 	reg &= ~MII_88E3016_DISABLE_SCRAMBLER;
5706b358aedSSebastian Hesselbarth 	reg |= MII_88E3016_AUTO_MDIX_CROSSOVER;
5716b358aedSSebastian Hesselbarth 
5726b358aedSSebastian Hesselbarth 	reg = phy_write(phydev, MII_88E3016_PHY_SPEC_CTRL, reg);
5736b358aedSSebastian Hesselbarth 	if (reg < 0)
5746b358aedSSebastian Hesselbarth 		return reg;
5756b358aedSSebastian Hesselbarth 
57679be1a1cSClemens Gruber 	return marvell_config_init(phydev);
5776b358aedSSebastian Hesselbarth }
5786b358aedSSebastian Hesselbarth 
579895ee682SKim Phillips static int m88e1111_config_init(struct phy_device *phydev)
580895ee682SKim Phillips {
581895ee682SKim Phillips 	int err;
582be937f1fSAlexandr Smirnov 	int temp;
583be937f1fSAlexandr Smirnov 
58432a64161SFlorian Fainelli 	if (phy_interface_is_rgmii(phydev)) {
585895ee682SKim Phillips 
586895ee682SKim Phillips 		temp = phy_read(phydev, MII_M1111_PHY_EXT_CR);
587895ee682SKim Phillips 		if (temp < 0)
588895ee682SKim Phillips 			return temp;
589895ee682SKim Phillips 
5909daf5a76SKim Phillips 		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
591895ee682SKim Phillips 			temp |= (MII_M1111_RX_DELAY | MII_M1111_TX_DELAY);
5929daf5a76SKim Phillips 		} else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
5939daf5a76SKim Phillips 			temp &= ~MII_M1111_TX_DELAY;
5949daf5a76SKim Phillips 			temp |= MII_M1111_RX_DELAY;
5959daf5a76SKim Phillips 		} else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
5969daf5a76SKim Phillips 			temp &= ~MII_M1111_RX_DELAY;
5979daf5a76SKim Phillips 			temp |= MII_M1111_TX_DELAY;
5989daf5a76SKim Phillips 		}
599895ee682SKim Phillips 
600895ee682SKim Phillips 		err = phy_write(phydev, MII_M1111_PHY_EXT_CR, temp);
601895ee682SKim Phillips 		if (err < 0)
602895ee682SKim Phillips 			return err;
603895ee682SKim Phillips 
604895ee682SKim Phillips 		temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
605895ee682SKim Phillips 		if (temp < 0)
606895ee682SKim Phillips 			return temp;
607895ee682SKim Phillips 
608895ee682SKim Phillips 		temp &= ~(MII_M1111_HWCFG_MODE_MASK);
609be937f1fSAlexandr Smirnov 
6107239016dSWang Jian 		if (temp & MII_M1111_HWCFG_FIBER_COPPER_RES)
611be937f1fSAlexandr Smirnov 			temp |= MII_M1111_HWCFG_MODE_FIBER_RGMII;
612be937f1fSAlexandr Smirnov 		else
613be937f1fSAlexandr Smirnov 			temp |= MII_M1111_HWCFG_MODE_COPPER_RGMII;
614895ee682SKim Phillips 
615895ee682SKim Phillips 		err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
616895ee682SKim Phillips 		if (err < 0)
617895ee682SKim Phillips 			return err;
618895ee682SKim Phillips 	}
619895ee682SKim Phillips 
6204117b5beSKapil Juneja 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
6214117b5beSKapil Juneja 		temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
6224117b5beSKapil Juneja 		if (temp < 0)
6234117b5beSKapil Juneja 			return temp;
6244117b5beSKapil Juneja 
6254117b5beSKapil Juneja 		temp &= ~(MII_M1111_HWCFG_MODE_MASK);
6264117b5beSKapil Juneja 		temp |= MII_M1111_HWCFG_MODE_SGMII_NO_CLK;
62732d0c1e1SHaiying Wang 		temp |= MII_M1111_HWCFG_FIBER_COPPER_AUTO;
6284117b5beSKapil Juneja 
6294117b5beSKapil Juneja 		err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
6304117b5beSKapil Juneja 		if (err < 0)
6314117b5beSKapil Juneja 			return err;
63207151bc9SMadalin Bucur 
63307151bc9SMadalin Bucur 		/* make sure copper is selected */
63407151bc9SMadalin Bucur 		err = phy_read(phydev, MII_M1145_PHY_EXT_ADDR_PAGE);
63507151bc9SMadalin Bucur 		if (err < 0)
63607151bc9SMadalin Bucur 			return err;
63707151bc9SMadalin Bucur 
63807151bc9SMadalin Bucur 		err = phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE,
63907151bc9SMadalin Bucur 				err & (~0xff));
64007151bc9SMadalin Bucur 		if (err < 0)
64107151bc9SMadalin Bucur 			return err;
6424117b5beSKapil Juneja 	}
6434117b5beSKapil Juneja 
6445f8cbc13SLiu Yu-B13201 	if (phydev->interface == PHY_INTERFACE_MODE_RTBI) {
6455f8cbc13SLiu Yu-B13201 		temp = phy_read(phydev, MII_M1111_PHY_EXT_CR);
6465f8cbc13SLiu Yu-B13201 		if (temp < 0)
6475f8cbc13SLiu Yu-B13201 			return temp;
6485f8cbc13SLiu Yu-B13201 		temp |= (MII_M1111_RX_DELAY | MII_M1111_TX_DELAY);
6495f8cbc13SLiu Yu-B13201 		err = phy_write(phydev, MII_M1111_PHY_EXT_CR, temp);
6505f8cbc13SLiu Yu-B13201 		if (err < 0)
6515f8cbc13SLiu Yu-B13201 			return err;
6525f8cbc13SLiu Yu-B13201 
6535f8cbc13SLiu Yu-B13201 		temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
6545f8cbc13SLiu Yu-B13201 		if (temp < 0)
6555f8cbc13SLiu Yu-B13201 			return temp;
6565f8cbc13SLiu Yu-B13201 		temp &= ~(MII_M1111_HWCFG_MODE_MASK | MII_M1111_HWCFG_FIBER_COPPER_RES);
6575f8cbc13SLiu Yu-B13201 		temp |= 0x7 | MII_M1111_HWCFG_FIBER_COPPER_AUTO;
6585f8cbc13SLiu Yu-B13201 		err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
6595f8cbc13SLiu Yu-B13201 		if (err < 0)
6605f8cbc13SLiu Yu-B13201 			return err;
6615f8cbc13SLiu Yu-B13201 
6625f8cbc13SLiu Yu-B13201 		/* soft reset */
6635f8cbc13SLiu Yu-B13201 		err = phy_write(phydev, MII_BMCR, BMCR_RESET);
6645f8cbc13SLiu Yu-B13201 		if (err < 0)
6655f8cbc13SLiu Yu-B13201 			return err;
6665f8cbc13SLiu Yu-B13201 		do
6675f8cbc13SLiu Yu-B13201 			temp = phy_read(phydev, MII_BMCR);
6685f8cbc13SLiu Yu-B13201 		while (temp & BMCR_RESET);
6695f8cbc13SLiu Yu-B13201 
6705f8cbc13SLiu Yu-B13201 		temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
6715f8cbc13SLiu Yu-B13201 		if (temp < 0)
6725f8cbc13SLiu Yu-B13201 			return temp;
6735f8cbc13SLiu Yu-B13201 		temp &= ~(MII_M1111_HWCFG_MODE_MASK | MII_M1111_HWCFG_FIBER_COPPER_RES);
6745f8cbc13SLiu Yu-B13201 		temp |= MII_M1111_HWCFG_MODE_COPPER_RTBI | MII_M1111_HWCFG_FIBER_COPPER_AUTO;
6755f8cbc13SLiu Yu-B13201 		err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
6765f8cbc13SLiu Yu-B13201 		if (err < 0)
6775f8cbc13SLiu Yu-B13201 			return err;
6785f8cbc13SLiu Yu-B13201 	}
6795f8cbc13SLiu Yu-B13201 
680cf41a51dSDavid Daney 	err = marvell_of_reg_init(phydev);
681cf41a51dSDavid Daney 	if (err < 0)
682cf41a51dSDavid Daney 		return err;
6835f8cbc13SLiu Yu-B13201 
684cc90cb3bSSrinivas Kandagatla 	return phy_write(phydev, MII_BMCR, BMCR_RESET);
685895ee682SKim Phillips }
686895ee682SKim Phillips 
687fdecf36fSClemens Gruber static int m88e1121_config_init(struct phy_device *phydev)
688fdecf36fSClemens Gruber {
689fdecf36fSClemens Gruber 	int err, oldpage;
690fdecf36fSClemens Gruber 
691fdecf36fSClemens Gruber 	oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE);
692fdecf36fSClemens Gruber 
693fdecf36fSClemens Gruber 	err = phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_88E1121_PHY_LED_PAGE);
694fdecf36fSClemens Gruber 	if (err < 0)
695fdecf36fSClemens Gruber 		return err;
696fdecf36fSClemens Gruber 
697fdecf36fSClemens Gruber 	/* Default PHY LED config: LED[0] .. Link, LED[1] .. Activity */
698fdecf36fSClemens Gruber 	err = phy_write(phydev, MII_88E1121_PHY_LED_CTRL,
699fdecf36fSClemens Gruber 			MII_88E1121_PHY_LED_DEF);
700fdecf36fSClemens Gruber 	if (err < 0)
701fdecf36fSClemens Gruber 		return err;
702fdecf36fSClemens Gruber 
703fdecf36fSClemens Gruber 	phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage);
704fdecf36fSClemens Gruber 
705fdecf36fSClemens Gruber 	/* Set marvell,reg-init configuration from device tree */
706fdecf36fSClemens Gruber 	return marvell_config_init(phydev);
707fdecf36fSClemens Gruber }
708fdecf36fSClemens Gruber 
709407353ecSClemens Gruber static int m88e1510_config_init(struct phy_device *phydev)
710407353ecSClemens Gruber {
711407353ecSClemens Gruber 	int err;
712407353ecSClemens Gruber 	int temp;
713407353ecSClemens Gruber 
714407353ecSClemens Gruber 	/* SGMII-to-Copper mode initialization */
715407353ecSClemens Gruber 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
716407353ecSClemens Gruber 		/* Select page 18 */
717407353ecSClemens Gruber 		err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 18);
718407353ecSClemens Gruber 		if (err < 0)
719407353ecSClemens Gruber 			return err;
720407353ecSClemens Gruber 
721407353ecSClemens Gruber 		/* In reg 20, write MODE[2:0] = 0x1 (SGMII to Copper) */
722407353ecSClemens Gruber 		temp = phy_read(phydev, MII_88E1510_GEN_CTRL_REG_1);
723407353ecSClemens Gruber 		temp &= ~MII_88E1510_GEN_CTRL_REG_1_MODE_MASK;
724407353ecSClemens Gruber 		temp |= MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII;
725407353ecSClemens Gruber 		err = phy_write(phydev, MII_88E1510_GEN_CTRL_REG_1, temp);
726407353ecSClemens Gruber 		if (err < 0)
727407353ecSClemens Gruber 			return err;
728407353ecSClemens Gruber 
729407353ecSClemens Gruber 		/* PHY reset is necessary after changing MODE[2:0] */
730407353ecSClemens Gruber 		temp |= MII_88E1510_GEN_CTRL_REG_1_RESET;
731407353ecSClemens Gruber 		err = phy_write(phydev, MII_88E1510_GEN_CTRL_REG_1, temp);
732407353ecSClemens Gruber 		if (err < 0)
733407353ecSClemens Gruber 			return err;
734407353ecSClemens Gruber 
735407353ecSClemens Gruber 		/* Reset page selection */
736407353ecSClemens Gruber 		err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0);
737407353ecSClemens Gruber 		if (err < 0)
738407353ecSClemens Gruber 			return err;
739407353ecSClemens Gruber 	}
740407353ecSClemens Gruber 
741fdecf36fSClemens Gruber 	return m88e1121_config_init(phydev);
742407353ecSClemens Gruber }
743407353ecSClemens Gruber 
744605f196eSRon Madrid static int m88e1118_config_aneg(struct phy_device *phydev)
745605f196eSRon Madrid {
746605f196eSRon Madrid 	int err;
747605f196eSRon Madrid 
748605f196eSRon Madrid 	err = phy_write(phydev, MII_BMCR, BMCR_RESET);
749605f196eSRon Madrid 	if (err < 0)
750605f196eSRon Madrid 		return err;
751605f196eSRon Madrid 
752605f196eSRon Madrid 	err = phy_write(phydev, MII_M1011_PHY_SCR,
753605f196eSRon Madrid 			MII_M1011_PHY_SCR_AUTO_CROSS);
754605f196eSRon Madrid 	if (err < 0)
755605f196eSRon Madrid 		return err;
756605f196eSRon Madrid 
757605f196eSRon Madrid 	err = genphy_config_aneg(phydev);
758605f196eSRon Madrid 	return 0;
759605f196eSRon Madrid }
760605f196eSRon Madrid 
761605f196eSRon Madrid static int m88e1118_config_init(struct phy_device *phydev)
762605f196eSRon Madrid {
763605f196eSRon Madrid 	int err;
764605f196eSRon Madrid 
765605f196eSRon Madrid 	/* Change address */
76627d916d6SDavid Daney 	err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0002);
767605f196eSRon Madrid 	if (err < 0)
768605f196eSRon Madrid 		return err;
769605f196eSRon Madrid 
770605f196eSRon Madrid 	/* Enable 1000 Mbit */
771605f196eSRon Madrid 	err = phy_write(phydev, 0x15, 0x1070);
772605f196eSRon Madrid 	if (err < 0)
773605f196eSRon Madrid 		return err;
774605f196eSRon Madrid 
775605f196eSRon Madrid 	/* Change address */
77627d916d6SDavid Daney 	err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0003);
777605f196eSRon Madrid 	if (err < 0)
778605f196eSRon Madrid 		return err;
779605f196eSRon Madrid 
780605f196eSRon Madrid 	/* Adjust LED Control */
7812f495c39SBenjamin Herrenschmidt 	if (phydev->dev_flags & MARVELL_PHY_M1118_DNS323_LEDS)
7822f495c39SBenjamin Herrenschmidt 		err = phy_write(phydev, 0x10, 0x1100);
7832f495c39SBenjamin Herrenschmidt 	else
784605f196eSRon Madrid 		err = phy_write(phydev, 0x10, 0x021e);
785605f196eSRon Madrid 	if (err < 0)
786605f196eSRon Madrid 		return err;
787605f196eSRon Madrid 
788cf41a51dSDavid Daney 	err = marvell_of_reg_init(phydev);
789cf41a51dSDavid Daney 	if (err < 0)
790cf41a51dSDavid Daney 		return err;
791cf41a51dSDavid Daney 
792605f196eSRon Madrid 	/* Reset address */
79327d916d6SDavid Daney 	err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0);
794605f196eSRon Madrid 	if (err < 0)
795605f196eSRon Madrid 		return err;
796605f196eSRon Madrid 
797cc90cb3bSSrinivas Kandagatla 	return phy_write(phydev, MII_BMCR, BMCR_RESET);
798605f196eSRon Madrid }
799605f196eSRon Madrid 
80090600732SDavid Daney static int m88e1149_config_init(struct phy_device *phydev)
80190600732SDavid Daney {
80290600732SDavid Daney 	int err;
80390600732SDavid Daney 
80490600732SDavid Daney 	/* Change address */
80590600732SDavid Daney 	err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0002);
80690600732SDavid Daney 	if (err < 0)
80790600732SDavid Daney 		return err;
80890600732SDavid Daney 
80990600732SDavid Daney 	/* Enable 1000 Mbit */
81090600732SDavid Daney 	err = phy_write(phydev, 0x15, 0x1048);
81190600732SDavid Daney 	if (err < 0)
81290600732SDavid Daney 		return err;
81390600732SDavid Daney 
814cf41a51dSDavid Daney 	err = marvell_of_reg_init(phydev);
815cf41a51dSDavid Daney 	if (err < 0)
816cf41a51dSDavid Daney 		return err;
817cf41a51dSDavid Daney 
81890600732SDavid Daney 	/* Reset address */
81990600732SDavid Daney 	err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0);
82090600732SDavid Daney 	if (err < 0)
82190600732SDavid Daney 		return err;
82290600732SDavid Daney 
823cc90cb3bSSrinivas Kandagatla 	return phy_write(phydev, MII_BMCR, BMCR_RESET);
82490600732SDavid Daney }
82590600732SDavid Daney 
82676884679SAndy Fleming static int m88e1145_config_init(struct phy_device *phydev)
82776884679SAndy Fleming {
82876884679SAndy Fleming 	int err;
829b0224175SViet Nga Dao 	int temp;
83076884679SAndy Fleming 
83176884679SAndy Fleming 	/* Take care of errata E0 & E1 */
83276884679SAndy Fleming 	err = phy_write(phydev, 0x1d, 0x001b);
83376884679SAndy Fleming 	if (err < 0)
83476884679SAndy Fleming 		return err;
83576884679SAndy Fleming 
83676884679SAndy Fleming 	err = phy_write(phydev, 0x1e, 0x418f);
83776884679SAndy Fleming 	if (err < 0)
83876884679SAndy Fleming 		return err;
83976884679SAndy Fleming 
84076884679SAndy Fleming 	err = phy_write(phydev, 0x1d, 0x0016);
84176884679SAndy Fleming 	if (err < 0)
84276884679SAndy Fleming 		return err;
84376884679SAndy Fleming 
84476884679SAndy Fleming 	err = phy_write(phydev, 0x1e, 0xa2da);
84576884679SAndy Fleming 	if (err < 0)
84676884679SAndy Fleming 		return err;
84776884679SAndy Fleming 
848895ee682SKim Phillips 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
84976884679SAndy Fleming 		int temp = phy_read(phydev, MII_M1145_PHY_EXT_CR);
85076884679SAndy Fleming 		if (temp < 0)
85176884679SAndy Fleming 			return temp;
85276884679SAndy Fleming 
85376884679SAndy Fleming 		temp |= (MII_M1145_RGMII_RX_DELAY | MII_M1145_RGMII_TX_DELAY);
85476884679SAndy Fleming 
85576884679SAndy Fleming 		err = phy_write(phydev, MII_M1145_PHY_EXT_CR, temp);
85676884679SAndy Fleming 		if (err < 0)
85776884679SAndy Fleming 			return err;
85876884679SAndy Fleming 
8592f495c39SBenjamin Herrenschmidt 		if (phydev->dev_flags & MARVELL_PHY_M1145_FLAGS_RESISTANCE) {
86076884679SAndy Fleming 			err = phy_write(phydev, 0x1d, 0x0012);
86176884679SAndy Fleming 			if (err < 0)
86276884679SAndy Fleming 				return err;
86376884679SAndy Fleming 
86476884679SAndy Fleming 			temp = phy_read(phydev, 0x1e);
86576884679SAndy Fleming 			if (temp < 0)
86676884679SAndy Fleming 				return temp;
86776884679SAndy Fleming 
86876884679SAndy Fleming 			temp &= 0xf03f;
86976884679SAndy Fleming 			temp |= 2 << 9;	/* 36 ohm */
87076884679SAndy Fleming 			temp |= 2 << 6;	/* 39 ohm */
87176884679SAndy Fleming 
87276884679SAndy Fleming 			err = phy_write(phydev, 0x1e, temp);
87376884679SAndy Fleming 			if (err < 0)
87476884679SAndy Fleming 				return err;
87576884679SAndy Fleming 
87676884679SAndy Fleming 			err = phy_write(phydev, 0x1d, 0x3);
87776884679SAndy Fleming 			if (err < 0)
87876884679SAndy Fleming 				return err;
87976884679SAndy Fleming 
88076884679SAndy Fleming 			err = phy_write(phydev, 0x1e, 0x8000);
88176884679SAndy Fleming 			if (err < 0)
88276884679SAndy Fleming 				return err;
88376884679SAndy Fleming 		}
88476884679SAndy Fleming 	}
88576884679SAndy Fleming 
886b0224175SViet Nga Dao 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
887b0224175SViet Nga Dao 		temp = phy_read(phydev, MII_M1145_PHY_EXT_SR);
888b0224175SViet Nga Dao 		if (temp < 0)
889b0224175SViet Nga Dao 			return temp;
890b0224175SViet Nga Dao 
89199d881f9SVince Bridgers 		temp &= ~MII_M1145_HWCFG_MODE_MASK;
892b0224175SViet Nga Dao 		temp |= MII_M1145_HWCFG_MODE_SGMII_NO_CLK;
893b0224175SViet Nga Dao 		temp |= MII_M1145_HWCFG_FIBER_COPPER_AUTO;
894b0224175SViet Nga Dao 
895b0224175SViet Nga Dao 		err = phy_write(phydev, MII_M1145_PHY_EXT_SR, temp);
896b0224175SViet Nga Dao 		if (err < 0)
897b0224175SViet Nga Dao 			return err;
898b0224175SViet Nga Dao 	}
899b0224175SViet Nga Dao 
900cf41a51dSDavid Daney 	err = marvell_of_reg_init(phydev);
901cf41a51dSDavid Daney 	if (err < 0)
902cf41a51dSDavid Daney 		return err;
903cf41a51dSDavid Daney 
90476884679SAndy Fleming 	return 0;
90576884679SAndy Fleming }
90600db8189SAndy Fleming 
9076cfb3bccSCharles-Antoine Couret /**
9086cfb3bccSCharles-Antoine Couret  * fiber_lpa_to_ethtool_lpa_t
9096cfb3bccSCharles-Antoine Couret  * @lpa: value of the MII_LPA register for fiber link
910be937f1fSAlexandr Smirnov  *
9116cfb3bccSCharles-Antoine Couret  * A small helper function that translates MII_LPA
9126cfb3bccSCharles-Antoine Couret  * bits to ethtool LP advertisement settings.
9136cfb3bccSCharles-Antoine Couret  */
9146cfb3bccSCharles-Antoine Couret static u32 fiber_lpa_to_ethtool_lpa_t(u32 lpa)
9156cfb3bccSCharles-Antoine Couret {
9166cfb3bccSCharles-Antoine Couret 	u32 result = 0;
9176cfb3bccSCharles-Antoine Couret 
9186cfb3bccSCharles-Antoine Couret 	if (lpa & LPA_FIBER_1000HALF)
9196cfb3bccSCharles-Antoine Couret 		result |= ADVERTISED_1000baseT_Half;
9206cfb3bccSCharles-Antoine Couret 	if (lpa & LPA_FIBER_1000FULL)
9216cfb3bccSCharles-Antoine Couret 		result |= ADVERTISED_1000baseT_Full;
9226cfb3bccSCharles-Antoine Couret 
9236cfb3bccSCharles-Antoine Couret 	return result;
9246cfb3bccSCharles-Antoine Couret }
9256cfb3bccSCharles-Antoine Couret 
9266cfb3bccSCharles-Antoine Couret /**
9276cfb3bccSCharles-Antoine Couret  * marvell_update_link - update link status in real time in @phydev
9286cfb3bccSCharles-Antoine Couret  * @phydev: target phy_device struct
9296cfb3bccSCharles-Antoine Couret  *
9306cfb3bccSCharles-Antoine Couret  * Description: Update the value in phydev->link to reflect the
9316cfb3bccSCharles-Antoine Couret  *   current link value.
9326cfb3bccSCharles-Antoine Couret  */
9336cfb3bccSCharles-Antoine Couret static int marvell_update_link(struct phy_device *phydev, int fiber)
9346cfb3bccSCharles-Antoine Couret {
9356cfb3bccSCharles-Antoine Couret 	int status;
9366cfb3bccSCharles-Antoine Couret 
9376cfb3bccSCharles-Antoine Couret 	/* Use the generic register for copper link, or specific
9386cfb3bccSCharles-Antoine Couret 	 * register for fiber case */
9396cfb3bccSCharles-Antoine Couret 	if (fiber) {
9406cfb3bccSCharles-Antoine Couret 		status = phy_read(phydev, MII_M1011_PHY_STATUS);
9416cfb3bccSCharles-Antoine Couret 		if (status < 0)
9426cfb3bccSCharles-Antoine Couret 			return status;
9436cfb3bccSCharles-Antoine Couret 
9446cfb3bccSCharles-Antoine Couret 		if ((status & REGISTER_LINK_STATUS) == 0)
9456cfb3bccSCharles-Antoine Couret 			phydev->link = 0;
9466cfb3bccSCharles-Antoine Couret 		else
9476cfb3bccSCharles-Antoine Couret 			phydev->link = 1;
9486cfb3bccSCharles-Antoine Couret 	} else {
9496cfb3bccSCharles-Antoine Couret 		return genphy_update_link(phydev);
9506cfb3bccSCharles-Antoine Couret 	}
9516cfb3bccSCharles-Antoine Couret 
9526cfb3bccSCharles-Antoine Couret 	return 0;
9536cfb3bccSCharles-Antoine Couret }
9546cfb3bccSCharles-Antoine Couret 
9556cfb3bccSCharles-Antoine Couret /* marvell_read_status_page
9566cfb3bccSCharles-Antoine Couret  *
957be937f1fSAlexandr Smirnov  * Description:
958be937f1fSAlexandr Smirnov  *   Check the link, then figure out the current state
959be937f1fSAlexandr Smirnov  *   by comparing what we advertise with what the link partner
960be937f1fSAlexandr Smirnov  *   advertises.  Start by checking the gigabit possibilities,
961be937f1fSAlexandr Smirnov  *   then move on to 10/100.
962be937f1fSAlexandr Smirnov  */
9636cfb3bccSCharles-Antoine Couret static int marvell_read_status_page(struct phy_device *phydev, int page)
964be937f1fSAlexandr Smirnov {
965be937f1fSAlexandr Smirnov 	int adv;
966be937f1fSAlexandr Smirnov 	int err;
967be937f1fSAlexandr Smirnov 	int lpa;
968357cd64cSRussell King 	int lpagb;
969be937f1fSAlexandr Smirnov 	int status = 0;
9706cfb3bccSCharles-Antoine Couret 	int fiber;
971be937f1fSAlexandr Smirnov 
9726cfb3bccSCharles-Antoine Couret 	/* Detect and update the link, but return if there
973be937f1fSAlexandr Smirnov 	 * was an error */
9746cfb3bccSCharles-Antoine Couret 	if (page == MII_M1111_FIBER)
9756cfb3bccSCharles-Antoine Couret 		fiber = 1;
9766cfb3bccSCharles-Antoine Couret 	else
9776cfb3bccSCharles-Antoine Couret 		fiber = 0;
9786cfb3bccSCharles-Antoine Couret 
9796cfb3bccSCharles-Antoine Couret 	err = marvell_update_link(phydev, fiber);
980be937f1fSAlexandr Smirnov 	if (err)
981be937f1fSAlexandr Smirnov 		return err;
982be937f1fSAlexandr Smirnov 
983be937f1fSAlexandr Smirnov 	if (AUTONEG_ENABLE == phydev->autoneg) {
984be937f1fSAlexandr Smirnov 		status = phy_read(phydev, MII_M1011_PHY_STATUS);
985be937f1fSAlexandr Smirnov 		if (status < 0)
986be937f1fSAlexandr Smirnov 			return status;
987be937f1fSAlexandr Smirnov 
988be937f1fSAlexandr Smirnov 		lpa = phy_read(phydev, MII_LPA);
989be937f1fSAlexandr Smirnov 		if (lpa < 0)
990be937f1fSAlexandr Smirnov 			return lpa;
991be937f1fSAlexandr Smirnov 
992357cd64cSRussell King 		lpagb = phy_read(phydev, MII_STAT1000);
993357cd64cSRussell King 		if (lpagb < 0)
994357cd64cSRussell King 			return lpagb;
995357cd64cSRussell King 
996be937f1fSAlexandr Smirnov 		adv = phy_read(phydev, MII_ADVERTISE);
997be937f1fSAlexandr Smirnov 		if (adv < 0)
998be937f1fSAlexandr Smirnov 			return adv;
999be937f1fSAlexandr Smirnov 
1000be937f1fSAlexandr Smirnov 		lpa &= adv;
1001be937f1fSAlexandr Smirnov 
1002be937f1fSAlexandr Smirnov 		if (status & MII_M1011_PHY_STATUS_FULLDUPLEX)
1003be937f1fSAlexandr Smirnov 			phydev->duplex = DUPLEX_FULL;
1004be937f1fSAlexandr Smirnov 		else
1005be937f1fSAlexandr Smirnov 			phydev->duplex = DUPLEX_HALF;
1006be937f1fSAlexandr Smirnov 
1007be937f1fSAlexandr Smirnov 		status = status & MII_M1011_PHY_STATUS_SPD_MASK;
1008be937f1fSAlexandr Smirnov 		phydev->pause = phydev->asym_pause = 0;
1009be937f1fSAlexandr Smirnov 
1010be937f1fSAlexandr Smirnov 		switch (status) {
1011be937f1fSAlexandr Smirnov 		case MII_M1011_PHY_STATUS_1000:
1012be937f1fSAlexandr Smirnov 			phydev->speed = SPEED_1000;
1013be937f1fSAlexandr Smirnov 			break;
1014be937f1fSAlexandr Smirnov 
1015be937f1fSAlexandr Smirnov 		case MII_M1011_PHY_STATUS_100:
1016be937f1fSAlexandr Smirnov 			phydev->speed = SPEED_100;
1017be937f1fSAlexandr Smirnov 			break;
1018be937f1fSAlexandr Smirnov 
1019be937f1fSAlexandr Smirnov 		default:
1020be937f1fSAlexandr Smirnov 			phydev->speed = SPEED_10;
1021be937f1fSAlexandr Smirnov 			break;
1022be937f1fSAlexandr Smirnov 		}
1023be937f1fSAlexandr Smirnov 
10246cfb3bccSCharles-Antoine Couret 		if (!fiber) {
10256cfb3bccSCharles-Antoine Couret 			phydev->lp_advertising = mii_stat1000_to_ethtool_lpa_t(lpagb) |
10266cfb3bccSCharles-Antoine Couret 					 mii_lpa_to_ethtool_lpa_t(lpa);
10276cfb3bccSCharles-Antoine Couret 
1028be937f1fSAlexandr Smirnov 			if (phydev->duplex == DUPLEX_FULL) {
1029be937f1fSAlexandr Smirnov 				phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0;
1030be937f1fSAlexandr Smirnov 				phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0;
1031be937f1fSAlexandr Smirnov 			}
1032be937f1fSAlexandr Smirnov 		} else {
10336cfb3bccSCharles-Antoine Couret 			/* The fiber link is only 1000M capable */
10346cfb3bccSCharles-Antoine Couret 			phydev->lp_advertising = fiber_lpa_to_ethtool_lpa_t(lpa);
10356cfb3bccSCharles-Antoine Couret 
10366cfb3bccSCharles-Antoine Couret 			if (phydev->duplex == DUPLEX_FULL) {
10376cfb3bccSCharles-Antoine Couret 				if (!(lpa & LPA_PAUSE_FIBER)) {
10386cfb3bccSCharles-Antoine Couret 					phydev->pause = 0;
10396cfb3bccSCharles-Antoine Couret 					phydev->asym_pause = 0;
10406cfb3bccSCharles-Antoine Couret 				} else if ((lpa & LPA_PAUSE_ASYM_FIBER)) {
10416cfb3bccSCharles-Antoine Couret 					phydev->pause = 1;
10426cfb3bccSCharles-Antoine Couret 					phydev->asym_pause = 1;
10436cfb3bccSCharles-Antoine Couret 				} else {
10446cfb3bccSCharles-Antoine Couret 					phydev->pause = 1;
10456cfb3bccSCharles-Antoine Couret 					phydev->asym_pause = 0;
10466cfb3bccSCharles-Antoine Couret 				}
10476cfb3bccSCharles-Antoine Couret 			}
10486cfb3bccSCharles-Antoine Couret 		}
10496cfb3bccSCharles-Antoine Couret 	} else {
1050be937f1fSAlexandr Smirnov 		int bmcr = phy_read(phydev, MII_BMCR);
1051be937f1fSAlexandr Smirnov 
1052be937f1fSAlexandr Smirnov 		if (bmcr < 0)
1053be937f1fSAlexandr Smirnov 			return bmcr;
1054be937f1fSAlexandr Smirnov 
1055be937f1fSAlexandr Smirnov 		if (bmcr & BMCR_FULLDPLX)
1056be937f1fSAlexandr Smirnov 			phydev->duplex = DUPLEX_FULL;
1057be937f1fSAlexandr Smirnov 		else
1058be937f1fSAlexandr Smirnov 			phydev->duplex = DUPLEX_HALF;
1059be937f1fSAlexandr Smirnov 
1060be937f1fSAlexandr Smirnov 		if (bmcr & BMCR_SPEED1000)
1061be937f1fSAlexandr Smirnov 			phydev->speed = SPEED_1000;
1062be937f1fSAlexandr Smirnov 		else if (bmcr & BMCR_SPEED100)
1063be937f1fSAlexandr Smirnov 			phydev->speed = SPEED_100;
1064be937f1fSAlexandr Smirnov 		else
1065be937f1fSAlexandr Smirnov 			phydev->speed = SPEED_10;
1066be937f1fSAlexandr Smirnov 
1067be937f1fSAlexandr Smirnov 		phydev->pause = phydev->asym_pause = 0;
1068357cd64cSRussell King 		phydev->lp_advertising = 0;
1069be937f1fSAlexandr Smirnov 	}
1070be937f1fSAlexandr Smirnov 
1071be937f1fSAlexandr Smirnov 	return 0;
1072be937f1fSAlexandr Smirnov }
1073be937f1fSAlexandr Smirnov 
10746cfb3bccSCharles-Antoine Couret /* marvell_read_status
10756cfb3bccSCharles-Antoine Couret  *
10766cfb3bccSCharles-Antoine Couret  * Some Marvell's phys have two modes: fiber and copper.
10776cfb3bccSCharles-Antoine Couret  * Both need status checked.
10786cfb3bccSCharles-Antoine Couret  * Description:
10796cfb3bccSCharles-Antoine Couret  *   First, check the fiber link and status.
10806cfb3bccSCharles-Antoine Couret  *   If the fiber link is down, check the copper link and status which
10816cfb3bccSCharles-Antoine Couret  *   will be the default value if both link are down.
10826cfb3bccSCharles-Antoine Couret  */
10836cfb3bccSCharles-Antoine Couret static int marvell_read_status(struct phy_device *phydev)
10846cfb3bccSCharles-Antoine Couret {
10856cfb3bccSCharles-Antoine Couret 	int err;
10866cfb3bccSCharles-Antoine Couret 
10876cfb3bccSCharles-Antoine Couret 	/* Check the fiber mode first */
10886cfb3bccSCharles-Antoine Couret 	if (phydev->supported & SUPPORTED_FIBRE) {
10896cfb3bccSCharles-Antoine Couret 		err = phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_FIBER);
10906cfb3bccSCharles-Antoine Couret 		if (err < 0)
10916cfb3bccSCharles-Antoine Couret 			goto error;
10926cfb3bccSCharles-Antoine Couret 
10936cfb3bccSCharles-Antoine Couret 		err = marvell_read_status_page(phydev, MII_M1111_FIBER);
10946cfb3bccSCharles-Antoine Couret 		if (err < 0)
10956cfb3bccSCharles-Antoine Couret 			goto error;
10966cfb3bccSCharles-Antoine Couret 
10976cfb3bccSCharles-Antoine Couret 		/* If the fiber link is up, it is the selected and used link.
10986cfb3bccSCharles-Antoine Couret 		 * In this case, we need to stay in the fiber page.
10996cfb3bccSCharles-Antoine Couret 		 * Please to be careful about that, avoid to restore Copper page
11006cfb3bccSCharles-Antoine Couret 		 * in other functions which could break the behaviour
11016cfb3bccSCharles-Antoine Couret 		 * for some fiber phy like 88E1512.
11026cfb3bccSCharles-Antoine Couret 		 * */
11036cfb3bccSCharles-Antoine Couret 		if (phydev->link)
11046cfb3bccSCharles-Antoine Couret 			return 0;
11056cfb3bccSCharles-Antoine Couret 
11066cfb3bccSCharles-Antoine Couret 		/* If fiber link is down, check and save copper mode state */
11076cfb3bccSCharles-Antoine Couret 		err = phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_COPPER);
11086cfb3bccSCharles-Antoine Couret 		if (err < 0)
11096cfb3bccSCharles-Antoine Couret 			goto error;
11106cfb3bccSCharles-Antoine Couret 	}
11116cfb3bccSCharles-Antoine Couret 
11126cfb3bccSCharles-Antoine Couret 	return marvell_read_status_page(phydev, MII_M1111_COPPER);
11136cfb3bccSCharles-Antoine Couret 
11146cfb3bccSCharles-Antoine Couret error:
11156cfb3bccSCharles-Antoine Couret 	phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_COPPER);
11166cfb3bccSCharles-Antoine Couret 	return err;
11176cfb3bccSCharles-Antoine Couret }
11186b358aedSSebastian Hesselbarth static int marvell_aneg_done(struct phy_device *phydev)
11196b358aedSSebastian Hesselbarth {
11206b358aedSSebastian Hesselbarth 	int retval = phy_read(phydev, MII_M1011_PHY_STATUS);
11216b358aedSSebastian Hesselbarth 	return (retval < 0) ? retval : (retval & MII_M1011_PHY_STATUS_RESOLVED);
11226b358aedSSebastian Hesselbarth }
11236b358aedSSebastian Hesselbarth 
1124dcd07be3SAnatolij Gustschin static int m88e1121_did_interrupt(struct phy_device *phydev)
1125dcd07be3SAnatolij Gustschin {
1126dcd07be3SAnatolij Gustschin 	int imask;
1127dcd07be3SAnatolij Gustschin 
1128dcd07be3SAnatolij Gustschin 	imask = phy_read(phydev, MII_M1011_IEVENT);
1129dcd07be3SAnatolij Gustschin 
1130dcd07be3SAnatolij Gustschin 	if (imask & MII_M1011_IMASK_INIT)
1131dcd07be3SAnatolij Gustschin 		return 1;
1132dcd07be3SAnatolij Gustschin 
1133dcd07be3SAnatolij Gustschin 	return 0;
1134dcd07be3SAnatolij Gustschin }
1135dcd07be3SAnatolij Gustschin 
11363871c387SMichael Stapelberg static void m88e1318_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
11373871c387SMichael Stapelberg {
11383871c387SMichael Stapelberg 	wol->supported = WAKE_MAGIC;
11393871c387SMichael Stapelberg 	wol->wolopts = 0;
11403871c387SMichael Stapelberg 
11413871c387SMichael Stapelberg 	if (phy_write(phydev, MII_MARVELL_PHY_PAGE,
11423871c387SMichael Stapelberg 		      MII_88E1318S_PHY_WOL_PAGE) < 0)
11433871c387SMichael Stapelberg 		return;
11443871c387SMichael Stapelberg 
11453871c387SMichael Stapelberg 	if (phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL) &
11463871c387SMichael Stapelberg 	    MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE)
11473871c387SMichael Stapelberg 		wol->wolopts |= WAKE_MAGIC;
11483871c387SMichael Stapelberg 
11493871c387SMichael Stapelberg 	if (phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x00) < 0)
11503871c387SMichael Stapelberg 		return;
11513871c387SMichael Stapelberg }
11523871c387SMichael Stapelberg 
11533871c387SMichael Stapelberg static int m88e1318_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
11543871c387SMichael Stapelberg {
11553871c387SMichael Stapelberg 	int err, oldpage, temp;
11563871c387SMichael Stapelberg 
11573871c387SMichael Stapelberg 	oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE);
11583871c387SMichael Stapelberg 
11593871c387SMichael Stapelberg 	if (wol->wolopts & WAKE_MAGIC) {
11603871c387SMichael Stapelberg 		/* Explicitly switch to page 0x00, just to be sure */
11613871c387SMichael Stapelberg 		err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x00);
11623871c387SMichael Stapelberg 		if (err < 0)
11633871c387SMichael Stapelberg 			return err;
11643871c387SMichael Stapelberg 
11653871c387SMichael Stapelberg 		/* Enable the WOL interrupt */
11663871c387SMichael Stapelberg 		temp = phy_read(phydev, MII_88E1318S_PHY_CSIER);
11673871c387SMichael Stapelberg 		temp |= MII_88E1318S_PHY_CSIER_WOL_EIE;
11683871c387SMichael Stapelberg 		err = phy_write(phydev, MII_88E1318S_PHY_CSIER, temp);
11693871c387SMichael Stapelberg 		if (err < 0)
11703871c387SMichael Stapelberg 			return err;
11713871c387SMichael Stapelberg 
11723871c387SMichael Stapelberg 		err = phy_write(phydev, MII_MARVELL_PHY_PAGE,
11733871c387SMichael Stapelberg 				MII_88E1318S_PHY_LED_PAGE);
11743871c387SMichael Stapelberg 		if (err < 0)
11753871c387SMichael Stapelberg 			return err;
11763871c387SMichael Stapelberg 
11773871c387SMichael Stapelberg 		/* Setup LED[2] as interrupt pin (active low) */
11783871c387SMichael Stapelberg 		temp = phy_read(phydev, MII_88E1318S_PHY_LED_TCR);
11793871c387SMichael Stapelberg 		temp &= ~MII_88E1318S_PHY_LED_TCR_FORCE_INT;
11803871c387SMichael Stapelberg 		temp |= MII_88E1318S_PHY_LED_TCR_INTn_ENABLE;
11813871c387SMichael Stapelberg 		temp |= MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW;
11823871c387SMichael Stapelberg 		err = phy_write(phydev, MII_88E1318S_PHY_LED_TCR, temp);
11833871c387SMichael Stapelberg 		if (err < 0)
11843871c387SMichael Stapelberg 			return err;
11853871c387SMichael Stapelberg 
11863871c387SMichael Stapelberg 		err = phy_write(phydev, MII_MARVELL_PHY_PAGE,
11873871c387SMichael Stapelberg 				MII_88E1318S_PHY_WOL_PAGE);
11883871c387SMichael Stapelberg 		if (err < 0)
11893871c387SMichael Stapelberg 			return err;
11903871c387SMichael Stapelberg 
11913871c387SMichael Stapelberg 		/* Store the device address for the magic packet */
11923871c387SMichael Stapelberg 		err = phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD2,
11933871c387SMichael Stapelberg 				((phydev->attached_dev->dev_addr[5] << 8) |
11943871c387SMichael Stapelberg 				 phydev->attached_dev->dev_addr[4]));
11953871c387SMichael Stapelberg 		if (err < 0)
11963871c387SMichael Stapelberg 			return err;
11973871c387SMichael Stapelberg 		err = phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD1,
11983871c387SMichael Stapelberg 				((phydev->attached_dev->dev_addr[3] << 8) |
11993871c387SMichael Stapelberg 				 phydev->attached_dev->dev_addr[2]));
12003871c387SMichael Stapelberg 		if (err < 0)
12013871c387SMichael Stapelberg 			return err;
12023871c387SMichael Stapelberg 		err = phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD0,
12033871c387SMichael Stapelberg 				((phydev->attached_dev->dev_addr[1] << 8) |
12043871c387SMichael Stapelberg 				 phydev->attached_dev->dev_addr[0]));
12053871c387SMichael Stapelberg 		if (err < 0)
12063871c387SMichael Stapelberg 			return err;
12073871c387SMichael Stapelberg 
12083871c387SMichael Stapelberg 		/* Clear WOL status and enable magic packet matching */
12093871c387SMichael Stapelberg 		temp = phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL);
12103871c387SMichael Stapelberg 		temp |= MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS;
12113871c387SMichael Stapelberg 		temp |= MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE;
12123871c387SMichael Stapelberg 		err = phy_write(phydev, MII_88E1318S_PHY_WOL_CTRL, temp);
12133871c387SMichael Stapelberg 		if (err < 0)
12143871c387SMichael Stapelberg 			return err;
12153871c387SMichael Stapelberg 	} else {
12163871c387SMichael Stapelberg 		err = phy_write(phydev, MII_MARVELL_PHY_PAGE,
12173871c387SMichael Stapelberg 				MII_88E1318S_PHY_WOL_PAGE);
12183871c387SMichael Stapelberg 		if (err < 0)
12193871c387SMichael Stapelberg 			return err;
12203871c387SMichael Stapelberg 
12213871c387SMichael Stapelberg 		/* Clear WOL status and disable magic packet matching */
12223871c387SMichael Stapelberg 		temp = phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL);
12233871c387SMichael Stapelberg 		temp |= MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS;
12243871c387SMichael Stapelberg 		temp &= ~MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE;
12253871c387SMichael Stapelberg 		err = phy_write(phydev, MII_88E1318S_PHY_WOL_CTRL, temp);
12263871c387SMichael Stapelberg 		if (err < 0)
12273871c387SMichael Stapelberg 			return err;
12283871c387SMichael Stapelberg 	}
12293871c387SMichael Stapelberg 
12303871c387SMichael Stapelberg 	err = phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage);
12313871c387SMichael Stapelberg 	if (err < 0)
12323871c387SMichael Stapelberg 		return err;
12333871c387SMichael Stapelberg 
12343871c387SMichael Stapelberg 	return 0;
12353871c387SMichael Stapelberg }
12363871c387SMichael Stapelberg 
1237d2fa47d9SAndrew Lunn static int marvell_get_sset_count(struct phy_device *phydev)
1238d2fa47d9SAndrew Lunn {
1239d2fa47d9SAndrew Lunn 	return ARRAY_SIZE(marvell_hw_stats);
1240d2fa47d9SAndrew Lunn }
1241d2fa47d9SAndrew Lunn 
1242d2fa47d9SAndrew Lunn static void marvell_get_strings(struct phy_device *phydev, u8 *data)
1243d2fa47d9SAndrew Lunn {
1244d2fa47d9SAndrew Lunn 	int i;
1245d2fa47d9SAndrew Lunn 
1246d2fa47d9SAndrew Lunn 	for (i = 0; i < ARRAY_SIZE(marvell_hw_stats); i++) {
1247d2fa47d9SAndrew Lunn 		memcpy(data + i * ETH_GSTRING_LEN,
1248d2fa47d9SAndrew Lunn 		       marvell_hw_stats[i].string, ETH_GSTRING_LEN);
1249d2fa47d9SAndrew Lunn 	}
1250d2fa47d9SAndrew Lunn }
1251d2fa47d9SAndrew Lunn 
1252d2fa47d9SAndrew Lunn #ifndef UINT64_MAX
1253d2fa47d9SAndrew Lunn #define UINT64_MAX              (u64)(~((u64)0))
1254d2fa47d9SAndrew Lunn #endif
1255d2fa47d9SAndrew Lunn static u64 marvell_get_stat(struct phy_device *phydev, int i)
1256d2fa47d9SAndrew Lunn {
1257d2fa47d9SAndrew Lunn 	struct marvell_hw_stat stat = marvell_hw_stats[i];
1258d2fa47d9SAndrew Lunn 	struct marvell_priv *priv = phydev->priv;
1259321b4d4bSAndrew Lunn 	int err, oldpage, val;
1260321b4d4bSAndrew Lunn 	u64 ret;
1261d2fa47d9SAndrew Lunn 
1262d2fa47d9SAndrew Lunn 	oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE);
1263d2fa47d9SAndrew Lunn 	err = phy_write(phydev, MII_MARVELL_PHY_PAGE,
1264d2fa47d9SAndrew Lunn 			stat.page);
1265d2fa47d9SAndrew Lunn 	if (err < 0)
1266d2fa47d9SAndrew Lunn 		return UINT64_MAX;
1267d2fa47d9SAndrew Lunn 
1268d2fa47d9SAndrew Lunn 	val = phy_read(phydev, stat.reg);
1269d2fa47d9SAndrew Lunn 	if (val < 0) {
1270321b4d4bSAndrew Lunn 		ret = UINT64_MAX;
1271d2fa47d9SAndrew Lunn 	} else {
1272d2fa47d9SAndrew Lunn 		val = val & ((1 << stat.bits) - 1);
1273d2fa47d9SAndrew Lunn 		priv->stats[i] += val;
1274321b4d4bSAndrew Lunn 		ret = priv->stats[i];
1275d2fa47d9SAndrew Lunn 	}
1276d2fa47d9SAndrew Lunn 
1277d2fa47d9SAndrew Lunn 	phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage);
1278d2fa47d9SAndrew Lunn 
1279321b4d4bSAndrew Lunn 	return ret;
1280d2fa47d9SAndrew Lunn }
1281d2fa47d9SAndrew Lunn 
1282d2fa47d9SAndrew Lunn static void marvell_get_stats(struct phy_device *phydev,
1283d2fa47d9SAndrew Lunn 			      struct ethtool_stats *stats, u64 *data)
1284d2fa47d9SAndrew Lunn {
1285d2fa47d9SAndrew Lunn 	int i;
1286d2fa47d9SAndrew Lunn 
1287d2fa47d9SAndrew Lunn 	for (i = 0; i < ARRAY_SIZE(marvell_hw_stats); i++)
1288d2fa47d9SAndrew Lunn 		data[i] = marvell_get_stat(phydev, i);
1289d2fa47d9SAndrew Lunn }
1290d2fa47d9SAndrew Lunn 
1291d2fa47d9SAndrew Lunn static int marvell_probe(struct phy_device *phydev)
1292d2fa47d9SAndrew Lunn {
1293d2fa47d9SAndrew Lunn 	struct marvell_priv *priv;
1294d2fa47d9SAndrew Lunn 
1295e5a03bfdSAndrew Lunn 	priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
1296d2fa47d9SAndrew Lunn 	if (!priv)
1297d2fa47d9SAndrew Lunn 		return -ENOMEM;
1298d2fa47d9SAndrew Lunn 
1299d2fa47d9SAndrew Lunn 	phydev->priv = priv;
1300d2fa47d9SAndrew Lunn 
1301d2fa47d9SAndrew Lunn 	return 0;
1302d2fa47d9SAndrew Lunn }
1303d2fa47d9SAndrew Lunn 
1304e5479239SOlof Johansson static struct phy_driver marvell_drivers[] = {
1305e5479239SOlof Johansson 	{
13062f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1101,
13072f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
130800db8189SAndy Fleming 		.name = "Marvell 88E1101",
130900db8189SAndy Fleming 		.features = PHY_GBIT_FEATURES,
1310d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
131100db8189SAndy Fleming 		.flags = PHY_HAS_INTERRUPT,
131279be1a1cSClemens Gruber 		.config_init = &marvell_config_init,
131300db8189SAndy Fleming 		.config_aneg = &marvell_config_aneg,
131400db8189SAndy Fleming 		.read_status = &genphy_read_status,
131500db8189SAndy Fleming 		.ack_interrupt = &marvell_ack_interrupt,
131600db8189SAndy Fleming 		.config_intr = &marvell_config_intr,
13170898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
13180898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
1319d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
1320d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
1321d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
1322e5479239SOlof Johansson 	},
1323e5479239SOlof Johansson 	{
13242f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1112,
13252f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
132685cfb534SOlof Johansson 		.name = "Marvell 88E1112",
132785cfb534SOlof Johansson 		.features = PHY_GBIT_FEATURES,
132885cfb534SOlof Johansson 		.flags = PHY_HAS_INTERRUPT,
1329d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
133085cfb534SOlof Johansson 		.config_init = &m88e1111_config_init,
133185cfb534SOlof Johansson 		.config_aneg = &marvell_config_aneg,
133285cfb534SOlof Johansson 		.read_status = &genphy_read_status,
133385cfb534SOlof Johansson 		.ack_interrupt = &marvell_ack_interrupt,
133485cfb534SOlof Johansson 		.config_intr = &marvell_config_intr,
13350898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
13360898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
1337d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
1338d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
1339d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
134085cfb534SOlof Johansson 	},
134185cfb534SOlof Johansson 	{
13422f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1111,
13432f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
134476884679SAndy Fleming 		.name = "Marvell 88E1111",
134576884679SAndy Fleming 		.features = PHY_GBIT_FEATURES,
134676884679SAndy Fleming 		.flags = PHY_HAS_INTERRUPT,
1347d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
1348e5479239SOlof Johansson 		.config_init = &m88e1111_config_init,
13493ec0a0f1SHarini Katakam 		.config_aneg = &m88e1111_config_aneg,
1350be937f1fSAlexandr Smirnov 		.read_status = &marvell_read_status,
135176884679SAndy Fleming 		.ack_interrupt = &marvell_ack_interrupt,
135276884679SAndy Fleming 		.config_intr = &marvell_config_intr,
13530898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
13540898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
1355d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
1356d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
1357d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
1358e5479239SOlof Johansson 	},
1359e5479239SOlof Johansson 	{
13602f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1118,
13612f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
1362605f196eSRon Madrid 		.name = "Marvell 88E1118",
1363605f196eSRon Madrid 		.features = PHY_GBIT_FEATURES,
1364605f196eSRon Madrid 		.flags = PHY_HAS_INTERRUPT,
1365d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
1366605f196eSRon Madrid 		.config_init = &m88e1118_config_init,
1367605f196eSRon Madrid 		.config_aneg = &m88e1118_config_aneg,
1368605f196eSRon Madrid 		.read_status = &genphy_read_status,
1369605f196eSRon Madrid 		.ack_interrupt = &marvell_ack_interrupt,
1370605f196eSRon Madrid 		.config_intr = &marvell_config_intr,
13710898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
13720898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
1373d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
1374d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
1375d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
1376605f196eSRon Madrid 	},
1377605f196eSRon Madrid 	{
13782f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1121R,
13792f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
1380140bc929SSergei Poselenov 		.name = "Marvell 88E1121R",
1381140bc929SSergei Poselenov 		.features = PHY_GBIT_FEATURES,
1382140bc929SSergei Poselenov 		.flags = PHY_HAS_INTERRUPT,
1383d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
1384fdecf36fSClemens Gruber 		.config_init = &m88e1121_config_init,
1385140bc929SSergei Poselenov 		.config_aneg = &m88e1121_config_aneg,
1386140bc929SSergei Poselenov 		.read_status = &marvell_read_status,
1387140bc929SSergei Poselenov 		.ack_interrupt = &marvell_ack_interrupt,
1388140bc929SSergei Poselenov 		.config_intr = &marvell_config_intr,
1389dcd07be3SAnatolij Gustschin 		.did_interrupt = &m88e1121_did_interrupt,
13900898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
13910898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
1392d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
1393d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
1394d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
1395140bc929SSergei Poselenov 	},
1396140bc929SSergei Poselenov 	{
1397337ac9d5SCyril Chemparathy 		.phy_id = MARVELL_PHY_ID_88E1318S,
13986ba74014SLinus Torvalds 		.phy_id_mask = MARVELL_PHY_ID_MASK,
1399337ac9d5SCyril Chemparathy 		.name = "Marvell 88E1318S",
14003ff1c259SCyril Chemparathy 		.features = PHY_GBIT_FEATURES,
14013ff1c259SCyril Chemparathy 		.flags = PHY_HAS_INTERRUPT,
1402d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
1403fdecf36fSClemens Gruber 		.config_init = &m88e1121_config_init,
1404337ac9d5SCyril Chemparathy 		.config_aneg = &m88e1318_config_aneg,
14053ff1c259SCyril Chemparathy 		.read_status = &marvell_read_status,
14063ff1c259SCyril Chemparathy 		.ack_interrupt = &marvell_ack_interrupt,
14073ff1c259SCyril Chemparathy 		.config_intr = &marvell_config_intr,
14083ff1c259SCyril Chemparathy 		.did_interrupt = &m88e1121_did_interrupt,
14093871c387SMichael Stapelberg 		.get_wol = &m88e1318_get_wol,
14103871c387SMichael Stapelberg 		.set_wol = &m88e1318_set_wol,
14110898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
14120898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
1413d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
1414d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
1415d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
14163ff1c259SCyril Chemparathy 	},
14173ff1c259SCyril Chemparathy 	{
14182f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1145,
14192f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
142076884679SAndy Fleming 		.name = "Marvell 88E1145",
142176884679SAndy Fleming 		.features = PHY_GBIT_FEATURES,
142276884679SAndy Fleming 		.flags = PHY_HAS_INTERRUPT,
1423d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
142476884679SAndy Fleming 		.config_init = &m88e1145_config_init,
142576884679SAndy Fleming 		.config_aneg = &marvell_config_aneg,
142676884679SAndy Fleming 		.read_status = &genphy_read_status,
142776884679SAndy Fleming 		.ack_interrupt = &marvell_ack_interrupt,
142876884679SAndy Fleming 		.config_intr = &marvell_config_intr,
14290898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
14300898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
1431d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
1432d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
1433d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
1434ac8c635aSOlof Johansson 	},
1435ac8c635aSOlof Johansson 	{
143690600732SDavid Daney 		.phy_id = MARVELL_PHY_ID_88E1149R,
143790600732SDavid Daney 		.phy_id_mask = MARVELL_PHY_ID_MASK,
143890600732SDavid Daney 		.name = "Marvell 88E1149R",
143990600732SDavid Daney 		.features = PHY_GBIT_FEATURES,
144090600732SDavid Daney 		.flags = PHY_HAS_INTERRUPT,
1441d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
144290600732SDavid Daney 		.config_init = &m88e1149_config_init,
144390600732SDavid Daney 		.config_aneg = &m88e1118_config_aneg,
144490600732SDavid Daney 		.read_status = &genphy_read_status,
144590600732SDavid Daney 		.ack_interrupt = &marvell_ack_interrupt,
144690600732SDavid Daney 		.config_intr = &marvell_config_intr,
14470898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
14480898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
1449d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
1450d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
1451d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
145290600732SDavid Daney 	},
145390600732SDavid Daney 	{
14542f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1240,
14552f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
1456ac8c635aSOlof Johansson 		.name = "Marvell 88E1240",
1457ac8c635aSOlof Johansson 		.features = PHY_GBIT_FEATURES,
1458ac8c635aSOlof Johansson 		.flags = PHY_HAS_INTERRUPT,
1459d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
1460ac8c635aSOlof Johansson 		.config_init = &m88e1111_config_init,
1461ac8c635aSOlof Johansson 		.config_aneg = &marvell_config_aneg,
1462ac8c635aSOlof Johansson 		.read_status = &genphy_read_status,
1463ac8c635aSOlof Johansson 		.ack_interrupt = &marvell_ack_interrupt,
1464ac8c635aSOlof Johansson 		.config_intr = &marvell_config_intr,
14650898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
14660898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
1467d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
1468d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
1469d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
1470ac8c635aSOlof Johansson 	},
14713da09a51SMichal Simek 	{
14723da09a51SMichal Simek 		.phy_id = MARVELL_PHY_ID_88E1116R,
14733da09a51SMichal Simek 		.phy_id_mask = MARVELL_PHY_ID_MASK,
14743da09a51SMichal Simek 		.name = "Marvell 88E1116R",
14753da09a51SMichal Simek 		.features = PHY_GBIT_FEATURES,
14763da09a51SMichal Simek 		.flags = PHY_HAS_INTERRUPT,
1477d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
14783da09a51SMichal Simek 		.config_init = &m88e1116r_config_init,
14793da09a51SMichal Simek 		.config_aneg = &genphy_config_aneg,
14803da09a51SMichal Simek 		.read_status = &genphy_read_status,
14813da09a51SMichal Simek 		.ack_interrupt = &marvell_ack_interrupt,
14823da09a51SMichal Simek 		.config_intr = &marvell_config_intr,
14830898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
14840898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
1485d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
1486d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
1487d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
14883da09a51SMichal Simek 	},
148910e24caaSMichal Simek 	{
149010e24caaSMichal Simek 		.phy_id = MARVELL_PHY_ID_88E1510,
149110e24caaSMichal Simek 		.phy_id_mask = MARVELL_PHY_ID_MASK,
149210e24caaSMichal Simek 		.name = "Marvell 88E1510",
14936cfb3bccSCharles-Antoine Couret 		.features = PHY_GBIT_FEATURES | SUPPORTED_FIBRE,
149410e24caaSMichal Simek 		.flags = PHY_HAS_INTERRUPT,
1495d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
1496930b37eeSStefan Roese 		.config_init = &m88e1510_config_init,
149710e24caaSMichal Simek 		.config_aneg = &m88e1510_config_aneg,
149810e24caaSMichal Simek 		.read_status = &marvell_read_status,
149910e24caaSMichal Simek 		.ack_interrupt = &marvell_ack_interrupt,
150010e24caaSMichal Simek 		.config_intr = &marvell_config_intr,
150110e24caaSMichal Simek 		.did_interrupt = &m88e1121_did_interrupt,
15020898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
15030898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
1504d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
1505d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
1506d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
150710e24caaSMichal Simek 	},
15086b358aedSSebastian Hesselbarth 	{
1509819ec8e1SAndrew Lunn 		.phy_id = MARVELL_PHY_ID_88E1540,
1510819ec8e1SAndrew Lunn 		.phy_id_mask = MARVELL_PHY_ID_MASK,
1511819ec8e1SAndrew Lunn 		.name = "Marvell 88E1540",
1512819ec8e1SAndrew Lunn 		.features = PHY_GBIT_FEATURES,
1513819ec8e1SAndrew Lunn 		.flags = PHY_HAS_INTERRUPT,
1514d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
151579be1a1cSClemens Gruber 		.config_init = &marvell_config_init,
1516819ec8e1SAndrew Lunn 		.config_aneg = &m88e1510_config_aneg,
1517819ec8e1SAndrew Lunn 		.read_status = &marvell_read_status,
1518819ec8e1SAndrew Lunn 		.ack_interrupt = &marvell_ack_interrupt,
1519819ec8e1SAndrew Lunn 		.config_intr = &marvell_config_intr,
1520819ec8e1SAndrew Lunn 		.did_interrupt = &m88e1121_did_interrupt,
1521819ec8e1SAndrew Lunn 		.resume = &genphy_resume,
1522819ec8e1SAndrew Lunn 		.suspend = &genphy_suspend,
1523d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
1524d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
1525d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
1526819ec8e1SAndrew Lunn 	},
1527819ec8e1SAndrew Lunn 	{
15286b358aedSSebastian Hesselbarth 		.phy_id = MARVELL_PHY_ID_88E3016,
15296b358aedSSebastian Hesselbarth 		.phy_id_mask = MARVELL_PHY_ID_MASK,
15306b358aedSSebastian Hesselbarth 		.name = "Marvell 88E3016",
15316b358aedSSebastian Hesselbarth 		.features = PHY_BASIC_FEATURES,
15326b358aedSSebastian Hesselbarth 		.flags = PHY_HAS_INTERRUPT,
1533d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
15346b358aedSSebastian Hesselbarth 		.config_aneg = &genphy_config_aneg,
15356b358aedSSebastian Hesselbarth 		.config_init = &m88e3016_config_init,
15366b358aedSSebastian Hesselbarth 		.aneg_done = &marvell_aneg_done,
15376b358aedSSebastian Hesselbarth 		.read_status = &marvell_read_status,
15386b358aedSSebastian Hesselbarth 		.ack_interrupt = &marvell_ack_interrupt,
15396b358aedSSebastian Hesselbarth 		.config_intr = &marvell_config_intr,
15406b358aedSSebastian Hesselbarth 		.did_interrupt = &m88e1121_did_interrupt,
15416b358aedSSebastian Hesselbarth 		.resume = &genphy_resume,
15426b358aedSSebastian Hesselbarth 		.suspend = &genphy_suspend,
1543d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
1544d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
1545d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
15466b358aedSSebastian Hesselbarth 	},
154776884679SAndy Fleming };
154876884679SAndy Fleming 
154950fd7150SJohan Hovold module_phy_driver(marvell_drivers);
15504e4f10f6SDavid Woodhouse 
1551cf93c945SUwe Kleine-König static struct mdio_device_id __maybe_unused marvell_tbl[] = {
1552f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1101, MARVELL_PHY_ID_MASK },
1553f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1112, MARVELL_PHY_ID_MASK },
1554f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1111, MARVELL_PHY_ID_MASK },
1555f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1118, MARVELL_PHY_ID_MASK },
1556f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1121R, MARVELL_PHY_ID_MASK },
1557f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1145, MARVELL_PHY_ID_MASK },
1558f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1149R, MARVELL_PHY_ID_MASK },
1559f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1240, MARVELL_PHY_ID_MASK },
1560f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1318S, MARVELL_PHY_ID_MASK },
15613da09a51SMichal Simek 	{ MARVELL_PHY_ID_88E1116R, MARVELL_PHY_ID_MASK },
156210e24caaSMichal Simek 	{ MARVELL_PHY_ID_88E1510, MARVELL_PHY_ID_MASK },
1563819ec8e1SAndrew Lunn 	{ MARVELL_PHY_ID_88E1540, MARVELL_PHY_ID_MASK },
15646b358aedSSebastian Hesselbarth 	{ MARVELL_PHY_ID_88E3016, MARVELL_PHY_ID_MASK },
15654e4f10f6SDavid Woodhouse 	{ }
15664e4f10f6SDavid Woodhouse };
15674e4f10f6SDavid Woodhouse 
15684e4f10f6SDavid Woodhouse MODULE_DEVICE_TABLE(mdio, marvell_tbl);
1569