xref: /openbmc/linux/drivers/net/phy/marvell.c (revision 911af5e1)
1a2443fd1SAndrew Lunn // SPDX-License-Identifier: GPL-2.0+
200db8189SAndy Fleming /*
300db8189SAndy Fleming  * drivers/net/phy/marvell.c
400db8189SAndy Fleming  *
500db8189SAndy Fleming  * Driver for Marvell PHYs
600db8189SAndy Fleming  *
700db8189SAndy Fleming  * Author: Andy Fleming
800db8189SAndy Fleming  *
900db8189SAndy Fleming  * Copyright (c) 2004 Freescale Semiconductor, Inc.
1000db8189SAndy Fleming  *
113871c387SMichael Stapelberg  * Copyright (c) 2013 Michael Stapelberg <michael@stapelberg.de>
1200db8189SAndy Fleming  */
1300db8189SAndy Fleming #include <linux/kernel.h>
1400db8189SAndy Fleming #include <linux/string.h>
150b04680fSAndrew Lunn #include <linux/ctype.h>
1600db8189SAndy Fleming #include <linux/errno.h>
1700db8189SAndy Fleming #include <linux/unistd.h>
180b04680fSAndrew Lunn #include <linux/hwmon.h>
1900db8189SAndy Fleming #include <linux/interrupt.h>
2000db8189SAndy Fleming #include <linux/init.h>
2100db8189SAndy Fleming #include <linux/delay.h>
2200db8189SAndy Fleming #include <linux/netdevice.h>
2300db8189SAndy Fleming #include <linux/etherdevice.h>
2400db8189SAndy Fleming #include <linux/skbuff.h>
2500db8189SAndy Fleming #include <linux/spinlock.h>
2600db8189SAndy Fleming #include <linux/mm.h>
2700db8189SAndy Fleming #include <linux/module.h>
2800db8189SAndy Fleming #include <linux/mii.h>
2900db8189SAndy Fleming #include <linux/ethtool.h>
3000db8189SAndy Fleming #include <linux/phy.h>
312f495c39SBenjamin Herrenschmidt #include <linux/marvell_phy.h>
3269f42be8SHeiner Kallweit #include <linux/bitfield.h>
33cf41a51dSDavid Daney #include <linux/of.h>
3400db8189SAndy Fleming 
35eea3b201SAvinash Kumar #include <linux/io.h>
3600db8189SAndy Fleming #include <asm/irq.h>
37eea3b201SAvinash Kumar #include <linux/uaccess.h>
3800db8189SAndy Fleming 
3927d916d6SDavid Daney #define MII_MARVELL_PHY_PAGE		22
4052295666SAndrew Lunn #define MII_MARVELL_COPPER_PAGE		0x00
4152295666SAndrew Lunn #define MII_MARVELL_FIBER_PAGE		0x01
4252295666SAndrew Lunn #define MII_MARVELL_MSCR_PAGE		0x02
4352295666SAndrew Lunn #define MII_MARVELL_LED_PAGE		0x03
4452295666SAndrew Lunn #define MII_MARVELL_MISC_TEST_PAGE	0x06
4552295666SAndrew Lunn #define MII_MARVELL_WOL_PAGE		0x11
4627d916d6SDavid Daney 
4700db8189SAndy Fleming #define MII_M1011_IEVENT		0x13
4800db8189SAndy Fleming #define MII_M1011_IEVENT_CLEAR		0x0000
4900db8189SAndy Fleming 
5000db8189SAndy Fleming #define MII_M1011_IMASK			0x12
5100db8189SAndy Fleming #define MII_M1011_IMASK_INIT		0x6400
5200db8189SAndy Fleming #define MII_M1011_IMASK_CLEAR		0x0000
5300db8189SAndy Fleming 
5476884679SAndy Fleming #define MII_M1011_PHY_SCR			0x10
55fecd5e91SAndrew Lunn #define MII_M1011_PHY_SCR_DOWNSHIFT_EN		BIT(11)
56f8d975beSHeiner Kallweit #define MII_M1011_PHY_SCR_DOWNSHIFT_MASK	GENMASK(14, 12)
57a3bdfce7SHeiner Kallweit #define MII_M1011_PHY_SCR_DOWNSHIFT_MAX		8
58fecd5e91SAndrew Lunn #define MII_M1011_PHY_SCR_MDI			(0x0 << 5)
59fecd5e91SAndrew Lunn #define MII_M1011_PHY_SCR_MDI_X			(0x1 << 5)
60fecd5e91SAndrew Lunn #define MII_M1011_PHY_SCR_AUTO_CROSS		(0x3 << 5)
6176884679SAndy Fleming 
62a3bdfce7SHeiner Kallweit #define MII_M1011_PHY_SSR			0x11
63a3bdfce7SHeiner Kallweit #define MII_M1011_PHY_SSR_DOWNSHIFT		BIT(5)
64a3bdfce7SHeiner Kallweit 
6576884679SAndy Fleming #define MII_M1111_PHY_LED_CONTROL	0x18
6676884679SAndy Fleming #define MII_M1111_PHY_LED_DIRECT	0x4100
6776884679SAndy Fleming #define MII_M1111_PHY_LED_COMBINE	0x411c
68895ee682SKim Phillips #define MII_M1111_PHY_EXT_CR		0x14
6961111598SAndrew Lunn #define MII_M1111_RGMII_RX_DELAY	BIT(7)
7061111598SAndrew Lunn #define MII_M1111_RGMII_TX_DELAY	BIT(1)
71895ee682SKim Phillips #define MII_M1111_PHY_EXT_SR		0x1b
72be937f1fSAlexandr Smirnov 
73895ee682SKim Phillips #define MII_M1111_HWCFG_MODE_MASK		0xf
74be937f1fSAlexandr Smirnov #define MII_M1111_HWCFG_MODE_FIBER_RGMII	0x3
754117b5beSKapil Juneja #define MII_M1111_HWCFG_MODE_SGMII_NO_CLK	0x4
76865b813aSAndrew Lunn #define MII_M1111_HWCFG_MODE_RTBI		0x7
775f8cbc13SLiu Yu-B13201 #define MII_M1111_HWCFG_MODE_COPPER_RTBI	0x9
78865b813aSAndrew Lunn #define MII_M1111_HWCFG_MODE_COPPER_RGMII	0xb
79865b813aSAndrew Lunn #define MII_M1111_HWCFG_FIBER_COPPER_RES	BIT(13)
80865b813aSAndrew Lunn #define MII_M1111_HWCFG_FIBER_COPPER_AUTO	BIT(15)
81be937f1fSAlexandr Smirnov 
82c477d044SCyril Chemparathy #define MII_88E1121_PHY_MSCR_REG	21
83c477d044SCyril Chemparathy #define MII_88E1121_PHY_MSCR_RX_DELAY	BIT(5)
84c477d044SCyril Chemparathy #define MII_88E1121_PHY_MSCR_TX_DELAY	BIT(4)
85424ca4c5SRussell King #define MII_88E1121_PHY_MSCR_DELAY_MASK	(BIT(5) | BIT(4))
86c477d044SCyril Chemparathy 
870b04680fSAndrew Lunn #define MII_88E1121_MISC_TEST				0x1a
880b04680fSAndrew Lunn #define MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK	0x1f00
890b04680fSAndrew Lunn #define MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT	8
900b04680fSAndrew Lunn #define MII_88E1510_MISC_TEST_TEMP_IRQ_EN		BIT(7)
910b04680fSAndrew Lunn #define MII_88E1510_MISC_TEST_TEMP_IRQ			BIT(6)
920b04680fSAndrew Lunn #define MII_88E1121_MISC_TEST_TEMP_SENSOR_EN		BIT(5)
930b04680fSAndrew Lunn #define MII_88E1121_MISC_TEST_TEMP_MASK			0x1f
940b04680fSAndrew Lunn 
950b04680fSAndrew Lunn #define MII_88E1510_TEMP_SENSOR		0x1b
960b04680fSAndrew Lunn #define MII_88E1510_TEMP_SENSOR_MASK	0xff
970b04680fSAndrew Lunn 
9869f42be8SHeiner Kallweit #define MII_88E1540_COPPER_CTRL3	0x1a
9969f42be8SHeiner Kallweit #define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_MASK	GENMASK(11, 10)
10069f42be8SHeiner Kallweit #define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_00MS	0
10169f42be8SHeiner Kallweit #define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_10MS	1
10269f42be8SHeiner Kallweit #define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_20MS	2
10369f42be8SHeiner Kallweit #define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_40MS	3
10469f42be8SHeiner Kallweit #define MII_88E1540_COPPER_CTRL3_FAST_LINK_DOWN		BIT(9)
10569f42be8SHeiner Kallweit 
106fee2d546SAndrew Lunn #define MII_88E6390_MISC_TEST		0x1b
107fee2d546SAndrew Lunn #define MII_88E6390_MISC_TEST_SAMPLE_1S		0
108fee2d546SAndrew Lunn #define MII_88E6390_MISC_TEST_SAMPLE_10MS	BIT(14)
109fee2d546SAndrew Lunn #define MII_88E6390_MISC_TEST_SAMPLE_DISABLE	BIT(15)
110fee2d546SAndrew Lunn #define MII_88E6390_MISC_TEST_SAMPLE_ENABLE	0
111fee2d546SAndrew Lunn #define MII_88E6390_MISC_TEST_SAMPLE_MASK	(0x3 << 14)
112fee2d546SAndrew Lunn 
113fee2d546SAndrew Lunn #define MII_88E6390_TEMP_SENSOR		0x1c
114fee2d546SAndrew Lunn #define MII_88E6390_TEMP_SENSOR_MASK	0xff
115fee2d546SAndrew Lunn #define MII_88E6390_TEMP_SENSOR_SAMPLES 10
116fee2d546SAndrew Lunn 
117337ac9d5SCyril Chemparathy #define MII_88E1318S_PHY_MSCR1_REG	16
118337ac9d5SCyril Chemparathy #define MII_88E1318S_PHY_MSCR1_PAD_ODD	BIT(6)
1193ff1c259SCyril Chemparathy 
1203871c387SMichael Stapelberg /* Copper Specific Interrupt Enable Register */
1213871c387SMichael Stapelberg #define MII_88E1318S_PHY_CSIER				0x12
1223871c387SMichael Stapelberg /* WOL Event Interrupt Enable */
1233871c387SMichael Stapelberg #define MII_88E1318S_PHY_CSIER_WOL_EIE			BIT(7)
1243871c387SMichael Stapelberg 
1253871c387SMichael Stapelberg /* LED Timer Control Register */
1263871c387SMichael Stapelberg #define MII_88E1318S_PHY_LED_TCR			0x12
1273871c387SMichael Stapelberg #define MII_88E1318S_PHY_LED_TCR_FORCE_INT		BIT(15)
1283871c387SMichael Stapelberg #define MII_88E1318S_PHY_LED_TCR_INTn_ENABLE		BIT(7)
1293871c387SMichael Stapelberg #define MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW		BIT(11)
1303871c387SMichael Stapelberg 
1313871c387SMichael Stapelberg /* Magic Packet MAC address registers */
1323871c387SMichael Stapelberg #define MII_88E1318S_PHY_MAGIC_PACKET_WORD2		0x17
1333871c387SMichael Stapelberg #define MII_88E1318S_PHY_MAGIC_PACKET_WORD1		0x18
1343871c387SMichael Stapelberg #define MII_88E1318S_PHY_MAGIC_PACKET_WORD0		0x19
1353871c387SMichael Stapelberg 
1363871c387SMichael Stapelberg #define MII_88E1318S_PHY_WOL_CTRL				0x10
1373871c387SMichael Stapelberg #define MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS		BIT(12)
1383871c387SMichael Stapelberg #define MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE	BIT(14)
1393871c387SMichael Stapelberg 
14007777246SWang Dongsheng #define MII_PHY_LED_CTRL	        16
141140bc929SSergei Poselenov #define MII_88E1121_PHY_LED_DEF		0x0030
14207777246SWang Dongsheng #define MII_88E1510_PHY_LED_DEF		0x1177
143a93f7fe1SJian Shen #define MII_88E1510_PHY_LED0_LINK_LED1_ACTIVE	0x1040
144140bc929SSergei Poselenov 
145be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS		0x11
146be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_1000	0x8000
147be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_100	0x4000
148be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_SPD_MASK	0xc000
149be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_FULLDUPLEX	0x2000
150be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_RESOLVED	0x0800
151be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_LINK	0x0400
152be937f1fSAlexandr Smirnov 
1536b358aedSSebastian Hesselbarth #define MII_88E3016_PHY_SPEC_CTRL	0x10
1546b358aedSSebastian Hesselbarth #define MII_88E3016_DISABLE_SCRAMBLER	0x0200
1556b358aedSSebastian Hesselbarth #define MII_88E3016_AUTO_MDIX_CROSSOVER	0x0030
15676884679SAndy Fleming 
157930b37eeSStefan Roese #define MII_88E1510_GEN_CTRL_REG_1		0x14
158930b37eeSStefan Roese #define MII_88E1510_GEN_CTRL_REG_1_MODE_MASK	0x7
159930b37eeSStefan Roese #define MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII	0x1	/* SGMII to copper */
160930b37eeSStefan Roese #define MII_88E1510_GEN_CTRL_REG_1_RESET	0x8000	/* Soft reset */
161930b37eeSStefan Roese 
1626cfb3bccSCharles-Antoine Couret #define LPA_FIBER_1000HALF	0x40
1636cfb3bccSCharles-Antoine Couret #define LPA_FIBER_1000FULL	0x20
1646cfb3bccSCharles-Antoine Couret 
1656cfb3bccSCharles-Antoine Couret #define LPA_PAUSE_FIBER		0x180
1666cfb3bccSCharles-Antoine Couret #define LPA_PAUSE_ASYM_FIBER	0x100
1676cfb3bccSCharles-Antoine Couret 
1686cfb3bccSCharles-Antoine Couret #define ADVERTISE_FIBER_1000HALF	0x40
1696cfb3bccSCharles-Antoine Couret #define ADVERTISE_FIBER_1000FULL	0x20
1706cfb3bccSCharles-Antoine Couret 
1716cfb3bccSCharles-Antoine Couret #define ADVERTISE_PAUSE_FIBER		0x180
1726cfb3bccSCharles-Antoine Couret #define ADVERTISE_PAUSE_ASYM_FIBER	0x100
1736cfb3bccSCharles-Antoine Couret 
1746cfb3bccSCharles-Antoine Couret #define REGISTER_LINK_STATUS	0x400
1752170fef7SCharles-Antoine Couret #define NB_FIBER_STATS	1
1766cfb3bccSCharles-Antoine Couret 
17700db8189SAndy Fleming MODULE_DESCRIPTION("Marvell PHY driver");
17800db8189SAndy Fleming MODULE_AUTHOR("Andy Fleming");
17900db8189SAndy Fleming MODULE_LICENSE("GPL");
18000db8189SAndy Fleming 
181d2fa47d9SAndrew Lunn struct marvell_hw_stat {
182d2fa47d9SAndrew Lunn 	const char *string;
183d2fa47d9SAndrew Lunn 	u8 page;
184d2fa47d9SAndrew Lunn 	u8 reg;
185d2fa47d9SAndrew Lunn 	u8 bits;
186d2fa47d9SAndrew Lunn };
187d2fa47d9SAndrew Lunn 
188d2fa47d9SAndrew Lunn static struct marvell_hw_stat marvell_hw_stats[] = {
1892170fef7SCharles-Antoine Couret 	{ "phy_receive_errors_copper", 0, 21, 16},
190d2fa47d9SAndrew Lunn 	{ "phy_idle_errors", 0, 10, 8 },
1912170fef7SCharles-Antoine Couret 	{ "phy_receive_errors_fiber", 1, 21, 16},
192d2fa47d9SAndrew Lunn };
193d2fa47d9SAndrew Lunn 
194d2fa47d9SAndrew Lunn struct marvell_priv {
195d2fa47d9SAndrew Lunn 	u64 stats[ARRAY_SIZE(marvell_hw_stats)];
1960b04680fSAndrew Lunn 	char *hwmon_name;
1970b04680fSAndrew Lunn 	struct device *hwmon_dev;
198d2fa47d9SAndrew Lunn };
199d2fa47d9SAndrew Lunn 
200424ca4c5SRussell King static int marvell_read_page(struct phy_device *phydev)
2016427bb2dSAndrew Lunn {
202424ca4c5SRussell King 	return __phy_read(phydev, MII_MARVELL_PHY_PAGE);
203424ca4c5SRussell King }
204424ca4c5SRussell King 
205424ca4c5SRussell King static int marvell_write_page(struct phy_device *phydev, int page)
206424ca4c5SRussell King {
207424ca4c5SRussell King 	return __phy_write(phydev, MII_MARVELL_PHY_PAGE, page);
2086427bb2dSAndrew Lunn }
2096427bb2dSAndrew Lunn 
2106427bb2dSAndrew Lunn static int marvell_set_page(struct phy_device *phydev, int page)
2116427bb2dSAndrew Lunn {
2126427bb2dSAndrew Lunn 	return phy_write(phydev, MII_MARVELL_PHY_PAGE, page);
2136427bb2dSAndrew Lunn }
2146427bb2dSAndrew Lunn 
21500db8189SAndy Fleming static int marvell_ack_interrupt(struct phy_device *phydev)
21600db8189SAndy Fleming {
21700db8189SAndy Fleming 	int err;
21800db8189SAndy Fleming 
21900db8189SAndy Fleming 	/* Clear the interrupts by reading the reg */
22000db8189SAndy Fleming 	err = phy_read(phydev, MII_M1011_IEVENT);
22100db8189SAndy Fleming 
22200db8189SAndy Fleming 	if (err < 0)
22300db8189SAndy Fleming 		return err;
22400db8189SAndy Fleming 
22500db8189SAndy Fleming 	return 0;
22600db8189SAndy Fleming }
22700db8189SAndy Fleming 
22800db8189SAndy Fleming static int marvell_config_intr(struct phy_device *phydev)
22900db8189SAndy Fleming {
23000db8189SAndy Fleming 	int err;
23100db8189SAndy Fleming 
23200db8189SAndy Fleming 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
23323beb38fSAndrew Lunn 		err = phy_write(phydev, MII_M1011_IMASK,
23423beb38fSAndrew Lunn 				MII_M1011_IMASK_INIT);
23500db8189SAndy Fleming 	else
23623beb38fSAndrew Lunn 		err = phy_write(phydev, MII_M1011_IMASK,
23723beb38fSAndrew Lunn 				MII_M1011_IMASK_CLEAR);
23800db8189SAndy Fleming 
23900db8189SAndy Fleming 	return err;
24000db8189SAndy Fleming }
24100db8189SAndy Fleming 
242239aa55bSDavid Thomson static int marvell_set_polarity(struct phy_device *phydev, int polarity)
243239aa55bSDavid Thomson {
244239aa55bSDavid Thomson 	int reg;
245239aa55bSDavid Thomson 	int err;
246239aa55bSDavid Thomson 	int val;
247239aa55bSDavid Thomson 
248239aa55bSDavid Thomson 	/* get the current settings */
249239aa55bSDavid Thomson 	reg = phy_read(phydev, MII_M1011_PHY_SCR);
250239aa55bSDavid Thomson 	if (reg < 0)
251239aa55bSDavid Thomson 		return reg;
252239aa55bSDavid Thomson 
253239aa55bSDavid Thomson 	val = reg;
254239aa55bSDavid Thomson 	val &= ~MII_M1011_PHY_SCR_AUTO_CROSS;
255239aa55bSDavid Thomson 	switch (polarity) {
256239aa55bSDavid Thomson 	case ETH_TP_MDI:
257239aa55bSDavid Thomson 		val |= MII_M1011_PHY_SCR_MDI;
258239aa55bSDavid Thomson 		break;
259239aa55bSDavid Thomson 	case ETH_TP_MDI_X:
260239aa55bSDavid Thomson 		val |= MII_M1011_PHY_SCR_MDI_X;
261239aa55bSDavid Thomson 		break;
262239aa55bSDavid Thomson 	case ETH_TP_MDI_AUTO:
263239aa55bSDavid Thomson 	case ETH_TP_MDI_INVALID:
264239aa55bSDavid Thomson 	default:
265239aa55bSDavid Thomson 		val |= MII_M1011_PHY_SCR_AUTO_CROSS;
266239aa55bSDavid Thomson 		break;
267239aa55bSDavid Thomson 	}
268239aa55bSDavid Thomson 
269239aa55bSDavid Thomson 	if (val != reg) {
270239aa55bSDavid Thomson 		/* Set the new polarity value in the register */
271239aa55bSDavid Thomson 		err = phy_write(phydev, MII_M1011_PHY_SCR, val);
272239aa55bSDavid Thomson 		if (err)
273239aa55bSDavid Thomson 			return err;
274239aa55bSDavid Thomson 	}
275239aa55bSDavid Thomson 
276d6ab9336SFlorian Fainelli 	return val != reg;
277239aa55bSDavid Thomson }
278239aa55bSDavid Thomson 
27900db8189SAndy Fleming static int marvell_config_aneg(struct phy_device *phydev)
28000db8189SAndy Fleming {
281d6ab9336SFlorian Fainelli 	int changed = 0;
28200db8189SAndy Fleming 	int err;
28300db8189SAndy Fleming 
2844e26c5c3SRaju Lakkaraju 	err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
28576884679SAndy Fleming 	if (err < 0)
28676884679SAndy Fleming 		return err;
28776884679SAndy Fleming 
288d6ab9336SFlorian Fainelli 	changed = err;
289d6ab9336SFlorian Fainelli 
29076884679SAndy Fleming 	err = phy_write(phydev, MII_M1111_PHY_LED_CONTROL,
29176884679SAndy Fleming 			MII_M1111_PHY_LED_DIRECT);
29276884679SAndy Fleming 	if (err < 0)
29376884679SAndy Fleming 		return err;
29400db8189SAndy Fleming 
29500db8189SAndy Fleming 	err = genphy_config_aneg(phydev);
2968ff44985SAnton Vorontsov 	if (err < 0)
29700db8189SAndy Fleming 		return err;
2988ff44985SAnton Vorontsov 
299d6ab9336SFlorian Fainelli 	if (phydev->autoneg != AUTONEG_ENABLE || changed) {
3000c3439bcSAndrew Lunn 		/* A write to speed/duplex bits (that is performed by
3018ff44985SAnton Vorontsov 		 * genphy_config_aneg() call above) must be followed by
3028ff44985SAnton Vorontsov 		 * a software reset. Otherwise, the write has no effect.
3038ff44985SAnton Vorontsov 		 */
30434386344SAndrew Lunn 		err = genphy_soft_reset(phydev);
3058ff44985SAnton Vorontsov 		if (err < 0)
3068ff44985SAnton Vorontsov 			return err;
3078ff44985SAnton Vorontsov 	}
3088ff44985SAnton Vorontsov 
3098ff44985SAnton Vorontsov 	return 0;
31000db8189SAndy Fleming }
31100db8189SAndy Fleming 
312f2899788SAndrew Lunn static int m88e1101_config_aneg(struct phy_device *phydev)
313f2899788SAndrew Lunn {
314f2899788SAndrew Lunn 	int err;
315f2899788SAndrew Lunn 
316f2899788SAndrew Lunn 	/* This Marvell PHY has an errata which requires
317f2899788SAndrew Lunn 	 * that certain registers get written in order
318f2899788SAndrew Lunn 	 * to restart autonegotiation
319f2899788SAndrew Lunn 	 */
32034386344SAndrew Lunn 	err = genphy_soft_reset(phydev);
321f2899788SAndrew Lunn 	if (err < 0)
322f2899788SAndrew Lunn 		return err;
323f2899788SAndrew Lunn 
324f2899788SAndrew Lunn 	err = phy_write(phydev, 0x1d, 0x1f);
325f2899788SAndrew Lunn 	if (err < 0)
326f2899788SAndrew Lunn 		return err;
327f2899788SAndrew Lunn 
328f2899788SAndrew Lunn 	err = phy_write(phydev, 0x1e, 0x200c);
329f2899788SAndrew Lunn 	if (err < 0)
330f2899788SAndrew Lunn 		return err;
331f2899788SAndrew Lunn 
332f2899788SAndrew Lunn 	err = phy_write(phydev, 0x1d, 0x5);
333f2899788SAndrew Lunn 	if (err < 0)
334f2899788SAndrew Lunn 		return err;
335f2899788SAndrew Lunn 
336f2899788SAndrew Lunn 	err = phy_write(phydev, 0x1e, 0);
337f2899788SAndrew Lunn 	if (err < 0)
338f2899788SAndrew Lunn 		return err;
339f2899788SAndrew Lunn 
340f2899788SAndrew Lunn 	err = phy_write(phydev, 0x1e, 0x100);
341f2899788SAndrew Lunn 	if (err < 0)
342f2899788SAndrew Lunn 		return err;
343f2899788SAndrew Lunn 
344f2899788SAndrew Lunn 	return marvell_config_aneg(phydev);
345f2899788SAndrew Lunn }
346f2899788SAndrew Lunn 
347cf41a51dSDavid Daney #ifdef CONFIG_OF_MDIO
3480c3439bcSAndrew Lunn /* Set and/or override some configuration registers based on the
349cf41a51dSDavid Daney  * marvell,reg-init property stored in the of_node for the phydev.
350cf41a51dSDavid Daney  *
351cf41a51dSDavid Daney  * marvell,reg-init = <reg-page reg mask value>,...;
352cf41a51dSDavid Daney  *
353cf41a51dSDavid Daney  * There may be one or more sets of <reg-page reg mask value>:
354cf41a51dSDavid Daney  *
355cf41a51dSDavid Daney  * reg-page: which register bank to use.
356cf41a51dSDavid Daney  * reg: the register.
357cf41a51dSDavid Daney  * mask: if non-zero, ANDed with existing register value.
358cf41a51dSDavid Daney  * value: ORed with the masked value and written to the regiser.
359cf41a51dSDavid Daney  *
360cf41a51dSDavid Daney  */
361cf41a51dSDavid Daney static int marvell_of_reg_init(struct phy_device *phydev)
362cf41a51dSDavid Daney {
363cf41a51dSDavid Daney 	const __be32 *paddr;
364424ca4c5SRussell King 	int len, i, saved_page, current_page, ret = 0;
365cf41a51dSDavid Daney 
366e5a03bfdSAndrew Lunn 	if (!phydev->mdio.dev.of_node)
367cf41a51dSDavid Daney 		return 0;
368cf41a51dSDavid Daney 
369e5a03bfdSAndrew Lunn 	paddr = of_get_property(phydev->mdio.dev.of_node,
370e5a03bfdSAndrew Lunn 				"marvell,reg-init", &len);
371cf41a51dSDavid Daney 	if (!paddr || len < (4 * sizeof(*paddr)))
372cf41a51dSDavid Daney 		return 0;
373cf41a51dSDavid Daney 
374424ca4c5SRussell King 	saved_page = phy_save_page(phydev);
375cf41a51dSDavid Daney 	if (saved_page < 0)
376424ca4c5SRussell King 		goto err;
377cf41a51dSDavid Daney 	current_page = saved_page;
378cf41a51dSDavid Daney 
379cf41a51dSDavid Daney 	len /= sizeof(*paddr);
380cf41a51dSDavid Daney 	for (i = 0; i < len - 3; i += 4) {
3816427bb2dSAndrew Lunn 		u16 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 
3876427bb2dSAndrew Lunn 		if (page != current_page) {
3886427bb2dSAndrew Lunn 			current_page = page;
389424ca4c5SRussell King 			ret = marvell_write_page(phydev, page);
390cf41a51dSDavid Daney 			if (ret < 0)
391cf41a51dSDavid Daney 				goto err;
392cf41a51dSDavid Daney 		}
393cf41a51dSDavid Daney 
394cf41a51dSDavid Daney 		val = 0;
395cf41a51dSDavid Daney 		if (mask) {
396424ca4c5SRussell King 			val = __phy_read(phydev, reg);
397cf41a51dSDavid Daney 			if (val < 0) {
398cf41a51dSDavid Daney 				ret = val;
399cf41a51dSDavid Daney 				goto err;
400cf41a51dSDavid Daney 			}
401cf41a51dSDavid Daney 			val &= mask;
402cf41a51dSDavid Daney 		}
403cf41a51dSDavid Daney 		val |= val_bits;
404cf41a51dSDavid Daney 
405424ca4c5SRussell King 		ret = __phy_write(phydev, reg, val);
406cf41a51dSDavid Daney 		if (ret < 0)
407cf41a51dSDavid Daney 			goto err;
408cf41a51dSDavid Daney 	}
409cf41a51dSDavid Daney err:
410424ca4c5SRussell King 	return phy_restore_page(phydev, saved_page, ret);
411cf41a51dSDavid Daney }
412cf41a51dSDavid Daney #else
413cf41a51dSDavid Daney static int marvell_of_reg_init(struct phy_device *phydev)
414cf41a51dSDavid Daney {
415cf41a51dSDavid Daney 	return 0;
416cf41a51dSDavid Daney }
417cf41a51dSDavid Daney #endif /* CONFIG_OF_MDIO */
418cf41a51dSDavid Daney 
419864dc729SAndrew Lunn static int m88e1121_config_aneg_rgmii_delays(struct phy_device *phydev)
420140bc929SSergei Poselenov {
421424ca4c5SRussell King 	int mscr;
422c477d044SCyril Chemparathy 
423c477d044SCyril Chemparathy 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
424424ca4c5SRussell King 		mscr = MII_88E1121_PHY_MSCR_RX_DELAY |
425424ca4c5SRussell King 		       MII_88E1121_PHY_MSCR_TX_DELAY;
426c477d044SCyril Chemparathy 	else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
427424ca4c5SRussell King 		mscr = MII_88E1121_PHY_MSCR_RX_DELAY;
428c477d044SCyril Chemparathy 	else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
429424ca4c5SRussell King 		mscr = MII_88E1121_PHY_MSCR_TX_DELAY;
430424ca4c5SRussell King 	else
431424ca4c5SRussell King 		mscr = 0;
432c477d044SCyril Chemparathy 
433424ca4c5SRussell King 	return phy_modify_paged(phydev, MII_MARVELL_MSCR_PAGE,
434424ca4c5SRussell King 				MII_88E1121_PHY_MSCR_REG,
435424ca4c5SRussell King 				MII_88E1121_PHY_MSCR_DELAY_MASK, mscr);
436be8c6480SArnaud Patard }
437c477d044SCyril Chemparathy 
438864dc729SAndrew Lunn static int m88e1121_config_aneg(struct phy_device *phydev)
439864dc729SAndrew Lunn {
440d6ab9336SFlorian Fainelli 	int changed = 0;
441864dc729SAndrew Lunn 	int err = 0;
442864dc729SAndrew Lunn 
443864dc729SAndrew Lunn 	if (phy_interface_is_rgmii(phydev)) {
444864dc729SAndrew Lunn 		err = m88e1121_config_aneg_rgmii_delays(phydev);
445fea23fb5SRussell King 		if (err < 0)
446864dc729SAndrew Lunn 			return err;
447864dc729SAndrew Lunn 	}
448140bc929SSergei Poselenov 
449fecd5e91SAndrew Lunn 	err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
450140bc929SSergei Poselenov 	if (err < 0)
451140bc929SSergei Poselenov 		return err;
452140bc929SSergei Poselenov 
453d6ab9336SFlorian Fainelli 	changed = err;
454d6ab9336SFlorian Fainelli 
455d6ab9336SFlorian Fainelli 	err = genphy_config_aneg(phydev);
456d6ab9336SFlorian Fainelli 	if (err < 0)
457d6ab9336SFlorian Fainelli 		return err;
458d6ab9336SFlorian Fainelli 
4594b1bd697SDavid S. Miller 	if (phydev->autoneg != AUTONEG_ENABLE || changed) {
460d6ab9336SFlorian Fainelli 		/* A software reset is used to ensure a "commit" of the
461d6ab9336SFlorian Fainelli 		 * changes is done.
462d6ab9336SFlorian Fainelli 		 */
463d6ab9336SFlorian Fainelli 		err = genphy_soft_reset(phydev);
464d6ab9336SFlorian Fainelli 		if (err < 0)
465d6ab9336SFlorian Fainelli 			return err;
466d6ab9336SFlorian Fainelli 	}
467d6ab9336SFlorian Fainelli 
468d6ab9336SFlorian Fainelli 	return 0;
469140bc929SSergei Poselenov }
470140bc929SSergei Poselenov 
471337ac9d5SCyril Chemparathy static int m88e1318_config_aneg(struct phy_device *phydev)
4723ff1c259SCyril Chemparathy {
473424ca4c5SRussell King 	int err;
4743ff1c259SCyril Chemparathy 
475424ca4c5SRussell King 	err = phy_modify_paged(phydev, MII_MARVELL_MSCR_PAGE,
476424ca4c5SRussell King 			       MII_88E1318S_PHY_MSCR1_REG,
477424ca4c5SRussell King 			       0, MII_88E1318S_PHY_MSCR1_PAD_ODD);
4783ff1c259SCyril Chemparathy 	if (err < 0)
4793ff1c259SCyril Chemparathy 		return err;
4803ff1c259SCyril Chemparathy 
4813ff1c259SCyril Chemparathy 	return m88e1121_config_aneg(phydev);
4823ff1c259SCyril Chemparathy }
4833ff1c259SCyril Chemparathy 
48478301ebeSCharles-Antoine Couret /**
4853c1bcc86SAndrew Lunn  * linkmode_adv_to_fiber_adv_t
4863c1bcc86SAndrew Lunn  * @advertise: the linkmode advertisement settings
48778301ebeSCharles-Antoine Couret  *
4883c1bcc86SAndrew Lunn  * A small helper function that translates linkmode advertisement
4893c1bcc86SAndrew Lunn  * settings to phy autonegotiation advertisements for the MII_ADV
4903c1bcc86SAndrew Lunn  * register for fiber link.
49178301ebeSCharles-Antoine Couret  */
4923c1bcc86SAndrew Lunn static inline u32 linkmode_adv_to_fiber_adv_t(unsigned long *advertise)
49378301ebeSCharles-Antoine Couret {
49478301ebeSCharles-Antoine Couret 	u32 result = 0;
49578301ebeSCharles-Antoine Couret 
4963c1bcc86SAndrew Lunn 	if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, advertise))
49778301ebeSCharles-Antoine Couret 		result |= ADVERTISE_FIBER_1000HALF;
4983c1bcc86SAndrew Lunn 	if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, advertise))
49978301ebeSCharles-Antoine Couret 		result |= ADVERTISE_FIBER_1000FULL;
50078301ebeSCharles-Antoine Couret 
5013c1bcc86SAndrew Lunn 	if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, advertise) &&
5023c1bcc86SAndrew Lunn 	    linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertise))
50378301ebeSCharles-Antoine Couret 		result |= LPA_PAUSE_ASYM_FIBER;
5043c1bcc86SAndrew Lunn 	else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertise))
50578301ebeSCharles-Antoine Couret 		result |= (ADVERTISE_PAUSE_FIBER
50678301ebeSCharles-Antoine Couret 			   & (~ADVERTISE_PAUSE_ASYM_FIBER));
50778301ebeSCharles-Antoine Couret 
50878301ebeSCharles-Antoine Couret 	return result;
50978301ebeSCharles-Antoine Couret }
51078301ebeSCharles-Antoine Couret 
51178301ebeSCharles-Antoine Couret /**
51278301ebeSCharles-Antoine Couret  * marvell_config_aneg_fiber - restart auto-negotiation or write BMCR
51378301ebeSCharles-Antoine Couret  * @phydev: target phy_device struct
51478301ebeSCharles-Antoine Couret  *
51578301ebeSCharles-Antoine Couret  * Description: If auto-negotiation is enabled, we configure the
51678301ebeSCharles-Antoine Couret  *   advertising, and then restart auto-negotiation.  If it is not
51778301ebeSCharles-Antoine Couret  *   enabled, then we write the BMCR. Adapted for fiber link in
51878301ebeSCharles-Antoine Couret  *   some Marvell's devices.
51978301ebeSCharles-Antoine Couret  */
52078301ebeSCharles-Antoine Couret static int marvell_config_aneg_fiber(struct phy_device *phydev)
52178301ebeSCharles-Antoine Couret {
52278301ebeSCharles-Antoine Couret 	int changed = 0;
52378301ebeSCharles-Antoine Couret 	int err;
52478301ebeSCharles-Antoine Couret 	int adv, oldadv;
52578301ebeSCharles-Antoine Couret 
52678301ebeSCharles-Antoine Couret 	if (phydev->autoneg != AUTONEG_ENABLE)
52778301ebeSCharles-Antoine Couret 		return genphy_setup_forced(phydev);
52878301ebeSCharles-Antoine Couret 
52978301ebeSCharles-Antoine Couret 	/* Only allow advertising what this PHY supports */
5303c1bcc86SAndrew Lunn 	linkmode_and(phydev->advertising, phydev->advertising,
5313c1bcc86SAndrew Lunn 		     phydev->supported);
53278301ebeSCharles-Antoine Couret 
53378301ebeSCharles-Antoine Couret 	/* Setup fiber advertisement */
53478301ebeSCharles-Antoine Couret 	adv = phy_read(phydev, MII_ADVERTISE);
53578301ebeSCharles-Antoine Couret 	if (adv < 0)
53678301ebeSCharles-Antoine Couret 		return adv;
53778301ebeSCharles-Antoine Couret 
53878301ebeSCharles-Antoine Couret 	oldadv = adv;
53978301ebeSCharles-Antoine Couret 	adv &= ~(ADVERTISE_FIBER_1000HALF | ADVERTISE_FIBER_1000FULL
54078301ebeSCharles-Antoine Couret 		| LPA_PAUSE_FIBER);
5413c1bcc86SAndrew Lunn 	adv |= linkmode_adv_to_fiber_adv_t(phydev->advertising);
54278301ebeSCharles-Antoine Couret 
54378301ebeSCharles-Antoine Couret 	if (adv != oldadv) {
54478301ebeSCharles-Antoine Couret 		err = phy_write(phydev, MII_ADVERTISE, adv);
54578301ebeSCharles-Antoine Couret 		if (err < 0)
54678301ebeSCharles-Antoine Couret 			return err;
54778301ebeSCharles-Antoine Couret 
54878301ebeSCharles-Antoine Couret 		changed = 1;
54978301ebeSCharles-Antoine Couret 	}
55078301ebeSCharles-Antoine Couret 
55178301ebeSCharles-Antoine Couret 	if (changed == 0) {
55278301ebeSCharles-Antoine Couret 		/* Advertisement hasn't changed, but maybe aneg was never on to
55378301ebeSCharles-Antoine Couret 		 * begin with?	Or maybe phy was isolated?
55478301ebeSCharles-Antoine Couret 		 */
55578301ebeSCharles-Antoine Couret 		int ctl = phy_read(phydev, MII_BMCR);
55678301ebeSCharles-Antoine Couret 
55778301ebeSCharles-Antoine Couret 		if (ctl < 0)
55878301ebeSCharles-Antoine Couret 			return ctl;
55978301ebeSCharles-Antoine Couret 
56078301ebeSCharles-Antoine Couret 		if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE))
56178301ebeSCharles-Antoine Couret 			changed = 1; /* do restart aneg */
56278301ebeSCharles-Antoine Couret 	}
56378301ebeSCharles-Antoine Couret 
56478301ebeSCharles-Antoine Couret 	/* Only restart aneg if we are advertising something different
56578301ebeSCharles-Antoine Couret 	 * than we were before.
56678301ebeSCharles-Antoine Couret 	 */
56778301ebeSCharles-Antoine Couret 	if (changed > 0)
56878301ebeSCharles-Antoine Couret 		changed = genphy_restart_aneg(phydev);
56978301ebeSCharles-Antoine Couret 
57078301ebeSCharles-Antoine Couret 	return changed;
57178301ebeSCharles-Antoine Couret }
57278301ebeSCharles-Antoine Couret 
57310e24caaSMichal Simek static int m88e1510_config_aneg(struct phy_device *phydev)
57410e24caaSMichal Simek {
57510e24caaSMichal Simek 	int err;
57610e24caaSMichal Simek 
57752295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
57878301ebeSCharles-Antoine Couret 	if (err < 0)
57978301ebeSCharles-Antoine Couret 		goto error;
58078301ebeSCharles-Antoine Couret 
58178301ebeSCharles-Antoine Couret 	/* Configure the copper link first */
58210e24caaSMichal Simek 	err = m88e1318_config_aneg(phydev);
58310e24caaSMichal Simek 	if (err < 0)
58478301ebeSCharles-Antoine Couret 		goto error;
58510e24caaSMichal Simek 
586de9c4e06SRussell King 	/* Do not touch the fiber page if we're in copper->sgmii mode */
587de9c4e06SRussell King 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII)
588de9c4e06SRussell King 		return 0;
589de9c4e06SRussell King 
59078301ebeSCharles-Antoine Couret 	/* Then the fiber link */
59152295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
59278301ebeSCharles-Antoine Couret 	if (err < 0)
59378301ebeSCharles-Antoine Couret 		goto error;
59478301ebeSCharles-Antoine Couret 
59578301ebeSCharles-Antoine Couret 	err = marvell_config_aneg_fiber(phydev);
59678301ebeSCharles-Antoine Couret 	if (err < 0)
59778301ebeSCharles-Antoine Couret 		goto error;
59878301ebeSCharles-Antoine Couret 
59952295666SAndrew Lunn 	return marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
60078301ebeSCharles-Antoine Couret 
60178301ebeSCharles-Antoine Couret error:
60252295666SAndrew Lunn 	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
60378301ebeSCharles-Antoine Couret 	return err;
60479be1a1cSClemens Gruber }
60579be1a1cSClemens Gruber 
60607777246SWang Dongsheng static void marvell_config_led(struct phy_device *phydev)
60707777246SWang Dongsheng {
60807777246SWang Dongsheng 	u16 def_config;
60907777246SWang Dongsheng 	int err;
61007777246SWang Dongsheng 
61107777246SWang Dongsheng 	switch (MARVELL_PHY_FAMILY_ID(phydev->phy_id)) {
61207777246SWang Dongsheng 	/* Default PHY LED config: LED[0] .. Link, LED[1] .. Activity */
61307777246SWang Dongsheng 	case MARVELL_PHY_FAMILY_ID(MARVELL_PHY_ID_88E1121R):
61407777246SWang Dongsheng 	case MARVELL_PHY_FAMILY_ID(MARVELL_PHY_ID_88E1318S):
61507777246SWang Dongsheng 		def_config = MII_88E1121_PHY_LED_DEF;
61607777246SWang Dongsheng 		break;
61707777246SWang Dongsheng 	/* Default PHY LED config:
61807777246SWang Dongsheng 	 * LED[0] .. 1000Mbps Link
61907777246SWang Dongsheng 	 * LED[1] .. 100Mbps Link
62007777246SWang Dongsheng 	 * LED[2] .. Blink, Activity
62107777246SWang Dongsheng 	 */
62207777246SWang Dongsheng 	case MARVELL_PHY_FAMILY_ID(MARVELL_PHY_ID_88E1510):
623a93f7fe1SJian Shen 		if (phydev->dev_flags & MARVELL_PHY_LED0_LINK_LED1_ACTIVE)
624a93f7fe1SJian Shen 			def_config = MII_88E1510_PHY_LED0_LINK_LED1_ACTIVE;
625a93f7fe1SJian Shen 		else
62607777246SWang Dongsheng 			def_config = MII_88E1510_PHY_LED_DEF;
62707777246SWang Dongsheng 		break;
62807777246SWang Dongsheng 	default:
62907777246SWang Dongsheng 		return;
63007777246SWang Dongsheng 	}
63107777246SWang Dongsheng 
63207777246SWang Dongsheng 	err = phy_write_paged(phydev, MII_MARVELL_LED_PAGE, MII_PHY_LED_CTRL,
63307777246SWang Dongsheng 			      def_config);
63407777246SWang Dongsheng 	if (err < 0)
635ab2a605fSAndrew Lunn 		phydev_warn(phydev, "Fail to config marvell phy LED.\n");
63607777246SWang Dongsheng }
63707777246SWang Dongsheng 
63879be1a1cSClemens Gruber static int marvell_config_init(struct phy_device *phydev)
63979be1a1cSClemens Gruber {
64007777246SWang Dongsheng 	/* Set defalut LED */
64107777246SWang Dongsheng 	marvell_config_led(phydev);
64207777246SWang Dongsheng 
64379be1a1cSClemens Gruber 	/* Set registers from marvell,reg-init DT property */
64410e24caaSMichal Simek 	return marvell_of_reg_init(phydev);
64510e24caaSMichal Simek }
64610e24caaSMichal Simek 
6476b358aedSSebastian Hesselbarth static int m88e3016_config_init(struct phy_device *phydev)
6486b358aedSSebastian Hesselbarth {
649fea23fb5SRussell King 	int ret;
6506b358aedSSebastian Hesselbarth 
6516b358aedSSebastian Hesselbarth 	/* Enable Scrambler and Auto-Crossover */
652fea23fb5SRussell King 	ret = phy_modify(phydev, MII_88E3016_PHY_SPEC_CTRL,
653f102852fSRussell King 			 MII_88E3016_DISABLE_SCRAMBLER,
654fea23fb5SRussell King 			 MII_88E3016_AUTO_MDIX_CROSSOVER);
655fea23fb5SRussell King 	if (ret < 0)
656fea23fb5SRussell King 		return ret;
6576b358aedSSebastian Hesselbarth 
65879be1a1cSClemens Gruber 	return marvell_config_init(phydev);
6596b358aedSSebastian Hesselbarth }
6606b358aedSSebastian Hesselbarth 
661865b813aSAndrew Lunn static int m88e1111_config_init_hwcfg_mode(struct phy_device *phydev,
662865b813aSAndrew Lunn 					   u16 mode,
663865b813aSAndrew Lunn 					   int fibre_copper_auto)
664865b813aSAndrew Lunn {
665865b813aSAndrew Lunn 	if (fibre_copper_auto)
666fea23fb5SRussell King 		mode |= MII_M1111_HWCFG_FIBER_COPPER_AUTO;
667865b813aSAndrew Lunn 
668fea23fb5SRussell King 	return phy_modify(phydev, MII_M1111_PHY_EXT_SR,
669f102852fSRussell King 			  MII_M1111_HWCFG_MODE_MASK |
670fea23fb5SRussell King 			  MII_M1111_HWCFG_FIBER_COPPER_AUTO |
671f102852fSRussell King 			  MII_M1111_HWCFG_FIBER_COPPER_RES,
672fea23fb5SRussell King 			  mode);
673865b813aSAndrew Lunn }
674865b813aSAndrew Lunn 
67561111598SAndrew Lunn static int m88e1111_config_init_rgmii_delays(struct phy_device *phydev)
676895ee682SKim Phillips {
677fea23fb5SRussell King 	int delay;
678895ee682SKim Phillips 
6799daf5a76SKim Phillips 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
680fea23fb5SRussell King 		delay = MII_M1111_RGMII_RX_DELAY | MII_M1111_RGMII_TX_DELAY;
6819daf5a76SKim Phillips 	} else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
682fea23fb5SRussell King 		delay = MII_M1111_RGMII_RX_DELAY;
6839daf5a76SKim Phillips 	} else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
684fea23fb5SRussell King 		delay = MII_M1111_RGMII_TX_DELAY;
685fea23fb5SRussell King 	} else {
686fea23fb5SRussell King 		delay = 0;
6879daf5a76SKim Phillips 	}
688895ee682SKim Phillips 
689fea23fb5SRussell King 	return phy_modify(phydev, MII_M1111_PHY_EXT_CR,
690f102852fSRussell King 			  MII_M1111_RGMII_RX_DELAY | MII_M1111_RGMII_TX_DELAY,
691fea23fb5SRussell King 			  delay);
69261111598SAndrew Lunn }
69361111598SAndrew Lunn 
69461111598SAndrew Lunn static int m88e1111_config_init_rgmii(struct phy_device *phydev)
69561111598SAndrew Lunn {
69661111598SAndrew Lunn 	int temp;
69761111598SAndrew Lunn 	int err;
69861111598SAndrew Lunn 
69961111598SAndrew Lunn 	err = m88e1111_config_init_rgmii_delays(phydev);
700895ee682SKim Phillips 	if (err < 0)
701895ee682SKim Phillips 		return err;
702895ee682SKim Phillips 
703895ee682SKim Phillips 	temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
704895ee682SKim Phillips 	if (temp < 0)
705895ee682SKim Phillips 		return temp;
706895ee682SKim Phillips 
707895ee682SKim Phillips 	temp &= ~(MII_M1111_HWCFG_MODE_MASK);
708be937f1fSAlexandr Smirnov 
7097239016dSWang Jian 	if (temp & MII_M1111_HWCFG_FIBER_COPPER_RES)
710be937f1fSAlexandr Smirnov 		temp |= MII_M1111_HWCFG_MODE_FIBER_RGMII;
711be937f1fSAlexandr Smirnov 	else
712be937f1fSAlexandr Smirnov 		temp |= MII_M1111_HWCFG_MODE_COPPER_RGMII;
713895ee682SKim Phillips 
714e1dde8dcSAndrew Lunn 	return phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
715895ee682SKim Phillips }
716895ee682SKim Phillips 
717e1dde8dcSAndrew Lunn static int m88e1111_config_init_sgmii(struct phy_device *phydev)
718e1dde8dcSAndrew Lunn {
719e1dde8dcSAndrew Lunn 	int err;
720e1dde8dcSAndrew Lunn 
721865b813aSAndrew Lunn 	err = m88e1111_config_init_hwcfg_mode(
722865b813aSAndrew Lunn 		phydev,
723865b813aSAndrew Lunn 		MII_M1111_HWCFG_MODE_SGMII_NO_CLK,
724865b813aSAndrew Lunn 		MII_M1111_HWCFG_FIBER_COPPER_AUTO);
7254117b5beSKapil Juneja 	if (err < 0)
7264117b5beSKapil Juneja 		return err;
72707151bc9SMadalin Bucur 
72807151bc9SMadalin Bucur 	/* make sure copper is selected */
72952295666SAndrew Lunn 	return marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
7304117b5beSKapil Juneja }
7314117b5beSKapil Juneja 
732e1dde8dcSAndrew Lunn static int m88e1111_config_init_rtbi(struct phy_device *phydev)
733e1dde8dcSAndrew Lunn {
73461111598SAndrew Lunn 	int err;
735e1dde8dcSAndrew Lunn 
73661111598SAndrew Lunn 	err = m88e1111_config_init_rgmii_delays(phydev);
737fea23fb5SRussell King 	if (err < 0)
7385f8cbc13SLiu Yu-B13201 		return err;
7395f8cbc13SLiu Yu-B13201 
740865b813aSAndrew Lunn 	err = m88e1111_config_init_hwcfg_mode(
741865b813aSAndrew Lunn 		phydev,
742865b813aSAndrew Lunn 		MII_M1111_HWCFG_MODE_RTBI,
743865b813aSAndrew Lunn 		MII_M1111_HWCFG_FIBER_COPPER_AUTO);
7445f8cbc13SLiu Yu-B13201 	if (err < 0)
7455f8cbc13SLiu Yu-B13201 		return err;
7465f8cbc13SLiu Yu-B13201 
7475f8cbc13SLiu Yu-B13201 	/* soft reset */
74834386344SAndrew Lunn 	err = genphy_soft_reset(phydev);
7495f8cbc13SLiu Yu-B13201 	if (err < 0)
7505f8cbc13SLiu Yu-B13201 		return err;
751e1dde8dcSAndrew Lunn 
752865b813aSAndrew Lunn 	return m88e1111_config_init_hwcfg_mode(
753865b813aSAndrew Lunn 		phydev,
754865b813aSAndrew Lunn 		MII_M1111_HWCFG_MODE_RTBI,
755865b813aSAndrew Lunn 		MII_M1111_HWCFG_FIBER_COPPER_AUTO);
756e1dde8dcSAndrew Lunn }
757e1dde8dcSAndrew Lunn 
758e1dde8dcSAndrew Lunn static int m88e1111_config_init(struct phy_device *phydev)
759e1dde8dcSAndrew Lunn {
760e1dde8dcSAndrew Lunn 	int err;
761e1dde8dcSAndrew Lunn 
762e1dde8dcSAndrew Lunn 	if (phy_interface_is_rgmii(phydev)) {
763e1dde8dcSAndrew Lunn 		err = m88e1111_config_init_rgmii(phydev);
764fea23fb5SRussell King 		if (err < 0)
765e1dde8dcSAndrew Lunn 			return err;
766e1dde8dcSAndrew Lunn 	}
767e1dde8dcSAndrew Lunn 
768e1dde8dcSAndrew Lunn 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
769e1dde8dcSAndrew Lunn 		err = m88e1111_config_init_sgmii(phydev);
770e1dde8dcSAndrew Lunn 		if (err < 0)
771e1dde8dcSAndrew Lunn 			return err;
772e1dde8dcSAndrew Lunn 	}
773e1dde8dcSAndrew Lunn 
774e1dde8dcSAndrew Lunn 	if (phydev->interface == PHY_INTERFACE_MODE_RTBI) {
775e1dde8dcSAndrew Lunn 		err = m88e1111_config_init_rtbi(phydev);
7765f8cbc13SLiu Yu-B13201 		if (err < 0)
7775f8cbc13SLiu Yu-B13201 			return err;
7785f8cbc13SLiu Yu-B13201 	}
7795f8cbc13SLiu Yu-B13201 
780cf41a51dSDavid Daney 	err = marvell_of_reg_init(phydev);
781cf41a51dSDavid Daney 	if (err < 0)
782cf41a51dSDavid Daney 		return err;
7835f8cbc13SLiu Yu-B13201 
78434386344SAndrew Lunn 	return genphy_soft_reset(phydev);
785895ee682SKim Phillips }
786895ee682SKim Phillips 
787911af5e1SHeiner Kallweit static int m88e1011_get_downshift(struct phy_device *phydev, u8 *data)
788a3bdfce7SHeiner Kallweit {
789a3bdfce7SHeiner Kallweit 	int val, cnt, enable;
790a3bdfce7SHeiner Kallweit 
791a3bdfce7SHeiner Kallweit 	val = phy_read(phydev, MII_M1011_PHY_SCR);
792a3bdfce7SHeiner Kallweit 	if (val < 0)
793a3bdfce7SHeiner Kallweit 		return val;
794a3bdfce7SHeiner Kallweit 
795a3bdfce7SHeiner Kallweit 	enable = FIELD_GET(MII_M1011_PHY_SCR_DOWNSHIFT_EN, val);
796f8d975beSHeiner Kallweit 	cnt = FIELD_GET(MII_M1011_PHY_SCR_DOWNSHIFT_MASK, val) + 1;
797a3bdfce7SHeiner Kallweit 
798a3bdfce7SHeiner Kallweit 	*data = enable ? cnt : DOWNSHIFT_DEV_DISABLE;
799a3bdfce7SHeiner Kallweit 
800a3bdfce7SHeiner Kallweit 	return 0;
801a3bdfce7SHeiner Kallweit }
802a3bdfce7SHeiner Kallweit 
803911af5e1SHeiner Kallweit static int m88e1011_set_downshift(struct phy_device *phydev, u8 cnt)
804a3bdfce7SHeiner Kallweit {
805a3bdfce7SHeiner Kallweit 	int val;
806a3bdfce7SHeiner Kallweit 
807a3bdfce7SHeiner Kallweit 	if (cnt > MII_M1011_PHY_SCR_DOWNSHIFT_MAX)
808a3bdfce7SHeiner Kallweit 		return -E2BIG;
809a3bdfce7SHeiner Kallweit 
810a3bdfce7SHeiner Kallweit 	if (!cnt)
811a3bdfce7SHeiner Kallweit 		return phy_clear_bits(phydev, MII_M1011_PHY_SCR,
812a3bdfce7SHeiner Kallweit 				      MII_M1011_PHY_SCR_DOWNSHIFT_EN);
813a3bdfce7SHeiner Kallweit 
814a3bdfce7SHeiner Kallweit 	val = MII_M1011_PHY_SCR_DOWNSHIFT_EN;
815f8d975beSHeiner Kallweit 	val |= FIELD_PREP(MII_M1011_PHY_SCR_DOWNSHIFT_MASK, cnt - 1);
816a3bdfce7SHeiner Kallweit 
817a3bdfce7SHeiner Kallweit 	return phy_modify(phydev, MII_M1011_PHY_SCR,
818a3bdfce7SHeiner Kallweit 			  MII_M1011_PHY_SCR_DOWNSHIFT_EN |
819f8d975beSHeiner Kallweit 			  MII_M1011_PHY_SCR_DOWNSHIFT_MASK,
820a3bdfce7SHeiner Kallweit 			  val);
821a3bdfce7SHeiner Kallweit }
822a3bdfce7SHeiner Kallweit 
823911af5e1SHeiner Kallweit static int m88e1011_get_tunable(struct phy_device *phydev,
824a3bdfce7SHeiner Kallweit 				struct ethtool_tunable *tuna, void *data)
825a3bdfce7SHeiner Kallweit {
826a3bdfce7SHeiner Kallweit 	switch (tuna->id) {
827a3bdfce7SHeiner Kallweit 	case ETHTOOL_PHY_DOWNSHIFT:
828911af5e1SHeiner Kallweit 		return m88e1011_get_downshift(phydev, data);
829a3bdfce7SHeiner Kallweit 	default:
830a3bdfce7SHeiner Kallweit 		return -EOPNOTSUPP;
831a3bdfce7SHeiner Kallweit 	}
832a3bdfce7SHeiner Kallweit }
833a3bdfce7SHeiner Kallweit 
834911af5e1SHeiner Kallweit static int m88e1011_set_tunable(struct phy_device *phydev,
835a3bdfce7SHeiner Kallweit 				struct ethtool_tunable *tuna, const void *data)
836a3bdfce7SHeiner Kallweit {
837a3bdfce7SHeiner Kallweit 	switch (tuna->id) {
838a3bdfce7SHeiner Kallweit 	case ETHTOOL_PHY_DOWNSHIFT:
839911af5e1SHeiner Kallweit 		return m88e1011_set_downshift(phydev, *(const u8 *)data);
840a3bdfce7SHeiner Kallweit 	default:
841a3bdfce7SHeiner Kallweit 		return -EOPNOTSUPP;
842a3bdfce7SHeiner Kallweit 	}
843a3bdfce7SHeiner Kallweit }
844a3bdfce7SHeiner Kallweit 
845911af5e1SHeiner Kallweit static void m88e1011_link_change_notify(struct phy_device *phydev)
846a3bdfce7SHeiner Kallweit {
847a3bdfce7SHeiner Kallweit 	int status;
848a3bdfce7SHeiner Kallweit 
849a3bdfce7SHeiner Kallweit 	if (phydev->state != PHY_RUNNING)
850a3bdfce7SHeiner Kallweit 		return;
851a3bdfce7SHeiner Kallweit 
852a3bdfce7SHeiner Kallweit 	/* we may be on fiber page currently */
853a3bdfce7SHeiner Kallweit 	status = phy_read_paged(phydev, MII_MARVELL_COPPER_PAGE,
854a3bdfce7SHeiner Kallweit 				MII_M1011_PHY_SSR);
855a3bdfce7SHeiner Kallweit 
856a3bdfce7SHeiner Kallweit 	if (status > 0 && status & MII_M1011_PHY_SSR_DOWNSHIFT)
857a3bdfce7SHeiner Kallweit 		phydev_warn(phydev, "Downshift occurred! Cabling may be defective.\n");
858a3bdfce7SHeiner Kallweit }
859a3bdfce7SHeiner Kallweit 
860e2d861ccSHeiner Kallweit static int m88e1116r_config_init(struct phy_device *phydev)
861e2d861ccSHeiner Kallweit {
862e2d861ccSHeiner Kallweit 	int err;
863e2d861ccSHeiner Kallweit 
864e2d861ccSHeiner Kallweit 	err = genphy_soft_reset(phydev);
865e2d861ccSHeiner Kallweit 	if (err < 0)
866e2d861ccSHeiner Kallweit 		return err;
867e2d861ccSHeiner Kallweit 
868e2d861ccSHeiner Kallweit 	msleep(500);
869e2d861ccSHeiner Kallweit 
870e2d861ccSHeiner Kallweit 	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
871e2d861ccSHeiner Kallweit 	if (err < 0)
872e2d861ccSHeiner Kallweit 		return err;
873e2d861ccSHeiner Kallweit 
874e2d861ccSHeiner Kallweit 	err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
875e2d861ccSHeiner Kallweit 	if (err < 0)
876e2d861ccSHeiner Kallweit 		return err;
877e2d861ccSHeiner Kallweit 
878911af5e1SHeiner Kallweit 	err = m88e1011_set_downshift(phydev, 8);
879e2d861ccSHeiner Kallweit 	if (err < 0)
880e2d861ccSHeiner Kallweit 		return err;
881e2d861ccSHeiner Kallweit 
882e2d861ccSHeiner Kallweit 	if (phy_interface_is_rgmii(phydev)) {
883e2d861ccSHeiner Kallweit 		err = m88e1121_config_aneg_rgmii_delays(phydev);
884e2d861ccSHeiner Kallweit 		if (err < 0)
885e2d861ccSHeiner Kallweit 			return err;
886e2d861ccSHeiner Kallweit 	}
887e2d861ccSHeiner Kallweit 
888e2d861ccSHeiner Kallweit 	err = genphy_soft_reset(phydev);
889e2d861ccSHeiner Kallweit 	if (err < 0)
890e2d861ccSHeiner Kallweit 		return err;
891e2d861ccSHeiner Kallweit 
892e2d861ccSHeiner Kallweit 	return marvell_config_init(phydev);
893e2d861ccSHeiner Kallweit }
894e2d861ccSHeiner Kallweit 
895dd9a122aSEsben Haabendal static int m88e1318_config_init(struct phy_device *phydev)
896dd9a122aSEsben Haabendal {
897dd9a122aSEsben Haabendal 	if (phy_interrupt_is_valid(phydev)) {
898dd9a122aSEsben Haabendal 		int err = phy_modify_paged(
899dd9a122aSEsben Haabendal 			phydev, MII_MARVELL_LED_PAGE,
900dd9a122aSEsben Haabendal 			MII_88E1318S_PHY_LED_TCR,
901dd9a122aSEsben Haabendal 			MII_88E1318S_PHY_LED_TCR_FORCE_INT,
902dd9a122aSEsben Haabendal 			MII_88E1318S_PHY_LED_TCR_INTn_ENABLE |
903dd9a122aSEsben Haabendal 			MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW);
904dd9a122aSEsben Haabendal 		if (err < 0)
905dd9a122aSEsben Haabendal 			return err;
906dd9a122aSEsben Haabendal 	}
907dd9a122aSEsben Haabendal 
90807777246SWang Dongsheng 	return marvell_config_init(phydev);
909dd9a122aSEsben Haabendal }
910dd9a122aSEsben Haabendal 
911407353ecSClemens Gruber static int m88e1510_config_init(struct phy_device *phydev)
912407353ecSClemens Gruber {
913407353ecSClemens Gruber 	int err;
914407353ecSClemens Gruber 
915407353ecSClemens Gruber 	/* SGMII-to-Copper mode initialization */
916407353ecSClemens Gruber 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
917407353ecSClemens Gruber 		/* Select page 18 */
9186427bb2dSAndrew Lunn 		err = marvell_set_page(phydev, 18);
919407353ecSClemens Gruber 		if (err < 0)
920407353ecSClemens Gruber 			return err;
921407353ecSClemens Gruber 
922407353ecSClemens Gruber 		/* In reg 20, write MODE[2:0] = 0x1 (SGMII to Copper) */
923fea23fb5SRussell King 		err = phy_modify(phydev, MII_88E1510_GEN_CTRL_REG_1,
924f102852fSRussell King 				 MII_88E1510_GEN_CTRL_REG_1_MODE_MASK,
925fea23fb5SRussell King 				 MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII);
926407353ecSClemens Gruber 		if (err < 0)
927407353ecSClemens Gruber 			return err;
928407353ecSClemens Gruber 
929407353ecSClemens Gruber 		/* PHY reset is necessary after changing MODE[2:0] */
930fea23fb5SRussell King 		err = phy_modify(phydev, MII_88E1510_GEN_CTRL_REG_1, 0,
931fea23fb5SRussell King 				 MII_88E1510_GEN_CTRL_REG_1_RESET);
932407353ecSClemens Gruber 		if (err < 0)
933407353ecSClemens Gruber 			return err;
934407353ecSClemens Gruber 
935407353ecSClemens Gruber 		/* Reset page selection */
93652295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
937407353ecSClemens Gruber 		if (err < 0)
938407353ecSClemens Gruber 			return err;
939407353ecSClemens Gruber 	}
940407353ecSClemens Gruber 
941dd9a122aSEsben Haabendal 	return m88e1318_config_init(phydev);
942407353ecSClemens Gruber }
943407353ecSClemens Gruber 
944605f196eSRon Madrid static int m88e1118_config_aneg(struct phy_device *phydev)
945605f196eSRon Madrid {
946605f196eSRon Madrid 	int err;
947605f196eSRon Madrid 
94834386344SAndrew Lunn 	err = genphy_soft_reset(phydev);
949605f196eSRon Madrid 	if (err < 0)
950605f196eSRon Madrid 		return err;
951605f196eSRon Madrid 
952fecd5e91SAndrew Lunn 	err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
953605f196eSRon Madrid 	if (err < 0)
954605f196eSRon Madrid 		return err;
955605f196eSRon Madrid 
956605f196eSRon Madrid 	err = genphy_config_aneg(phydev);
957605f196eSRon Madrid 	return 0;
958605f196eSRon Madrid }
959605f196eSRon Madrid 
960605f196eSRon Madrid static int m88e1118_config_init(struct phy_device *phydev)
961605f196eSRon Madrid {
962605f196eSRon Madrid 	int err;
963605f196eSRon Madrid 
964605f196eSRon Madrid 	/* Change address */
96552295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_MSCR_PAGE);
966605f196eSRon Madrid 	if (err < 0)
967605f196eSRon Madrid 		return err;
968605f196eSRon Madrid 
969605f196eSRon Madrid 	/* Enable 1000 Mbit */
970605f196eSRon Madrid 	err = phy_write(phydev, 0x15, 0x1070);
971605f196eSRon Madrid 	if (err < 0)
972605f196eSRon Madrid 		return err;
973605f196eSRon Madrid 
974605f196eSRon Madrid 	/* Change address */
97552295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_LED_PAGE);
976605f196eSRon Madrid 	if (err < 0)
977605f196eSRon Madrid 		return err;
978605f196eSRon Madrid 
979605f196eSRon Madrid 	/* Adjust LED Control */
9802f495c39SBenjamin Herrenschmidt 	if (phydev->dev_flags & MARVELL_PHY_M1118_DNS323_LEDS)
9812f495c39SBenjamin Herrenschmidt 		err = phy_write(phydev, 0x10, 0x1100);
9822f495c39SBenjamin Herrenschmidt 	else
983605f196eSRon Madrid 		err = phy_write(phydev, 0x10, 0x021e);
984605f196eSRon Madrid 	if (err < 0)
985605f196eSRon Madrid 		return err;
986605f196eSRon Madrid 
987cf41a51dSDavid Daney 	err = marvell_of_reg_init(phydev);
988cf41a51dSDavid Daney 	if (err < 0)
989cf41a51dSDavid Daney 		return err;
990cf41a51dSDavid Daney 
991605f196eSRon Madrid 	/* Reset address */
99252295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
993605f196eSRon Madrid 	if (err < 0)
994605f196eSRon Madrid 		return err;
995605f196eSRon Madrid 
99634386344SAndrew Lunn 	return genphy_soft_reset(phydev);
997605f196eSRon Madrid }
998605f196eSRon Madrid 
99990600732SDavid Daney static int m88e1149_config_init(struct phy_device *phydev)
100090600732SDavid Daney {
100190600732SDavid Daney 	int err;
100290600732SDavid Daney 
100390600732SDavid Daney 	/* Change address */
100452295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_MSCR_PAGE);
100590600732SDavid Daney 	if (err < 0)
100690600732SDavid Daney 		return err;
100790600732SDavid Daney 
100890600732SDavid Daney 	/* Enable 1000 Mbit */
100990600732SDavid Daney 	err = phy_write(phydev, 0x15, 0x1048);
101090600732SDavid Daney 	if (err < 0)
101190600732SDavid Daney 		return err;
101290600732SDavid Daney 
1013cf41a51dSDavid Daney 	err = marvell_of_reg_init(phydev);
1014cf41a51dSDavid Daney 	if (err < 0)
1015cf41a51dSDavid Daney 		return err;
1016cf41a51dSDavid Daney 
101790600732SDavid Daney 	/* Reset address */
101852295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
101990600732SDavid Daney 	if (err < 0)
102090600732SDavid Daney 		return err;
102190600732SDavid Daney 
102234386344SAndrew Lunn 	return genphy_soft_reset(phydev);
102390600732SDavid Daney }
102490600732SDavid Daney 
1025e1dde8dcSAndrew Lunn static int m88e1145_config_init_rgmii(struct phy_device *phydev)
102676884679SAndy Fleming {
102776884679SAndy Fleming 	int err;
1028e69d9ed4SAndrew Lunn 
102961111598SAndrew Lunn 	err = m88e1111_config_init_rgmii_delays(phydev);
103076884679SAndy Fleming 	if (err < 0)
103176884679SAndy Fleming 		return err;
103276884679SAndy Fleming 
10332f495c39SBenjamin Herrenschmidt 	if (phydev->dev_flags & MARVELL_PHY_M1145_FLAGS_RESISTANCE) {
103476884679SAndy Fleming 		err = phy_write(phydev, 0x1d, 0x0012);
103576884679SAndy Fleming 		if (err < 0)
103676884679SAndy Fleming 			return err;
103776884679SAndy Fleming 
1038f102852fSRussell King 		err = phy_modify(phydev, 0x1e, 0x0fc0,
1039fea23fb5SRussell King 				 2 << 9 | /* 36 ohm */
1040fea23fb5SRussell King 				 2 << 6); /* 39 ohm */
104176884679SAndy Fleming 		if (err < 0)
104276884679SAndy Fleming 			return err;
104376884679SAndy Fleming 
104476884679SAndy Fleming 		err = phy_write(phydev, 0x1d, 0x3);
104576884679SAndy Fleming 		if (err < 0)
104676884679SAndy Fleming 			return err;
104776884679SAndy Fleming 
104876884679SAndy Fleming 		err = phy_write(phydev, 0x1e, 0x8000);
1049e1dde8dcSAndrew Lunn 	}
105076884679SAndy Fleming 	return err;
105176884679SAndy Fleming }
105276884679SAndy Fleming 
1053e1dde8dcSAndrew Lunn static int m88e1145_config_init_sgmii(struct phy_device *phydev)
1054e1dde8dcSAndrew Lunn {
1055865b813aSAndrew Lunn 	return m88e1111_config_init_hwcfg_mode(
1056865b813aSAndrew Lunn 		phydev, MII_M1111_HWCFG_MODE_SGMII_NO_CLK,
1057865b813aSAndrew Lunn 		MII_M1111_HWCFG_FIBER_COPPER_AUTO);
1058e1dde8dcSAndrew Lunn }
1059e1dde8dcSAndrew Lunn 
1060e1dde8dcSAndrew Lunn static int m88e1145_config_init(struct phy_device *phydev)
1061e1dde8dcSAndrew Lunn {
1062e1dde8dcSAndrew Lunn 	int err;
1063e1dde8dcSAndrew Lunn 
1064e1dde8dcSAndrew Lunn 	/* Take care of errata E0 & E1 */
1065e1dde8dcSAndrew Lunn 	err = phy_write(phydev, 0x1d, 0x001b);
1066e1dde8dcSAndrew Lunn 	if (err < 0)
1067e1dde8dcSAndrew Lunn 		return err;
1068e1dde8dcSAndrew Lunn 
1069e1dde8dcSAndrew Lunn 	err = phy_write(phydev, 0x1e, 0x418f);
1070e1dde8dcSAndrew Lunn 	if (err < 0)
1071e1dde8dcSAndrew Lunn 		return err;
1072e1dde8dcSAndrew Lunn 
1073e1dde8dcSAndrew Lunn 	err = phy_write(phydev, 0x1d, 0x0016);
1074e1dde8dcSAndrew Lunn 	if (err < 0)
1075e1dde8dcSAndrew Lunn 		return err;
1076e1dde8dcSAndrew Lunn 
1077e1dde8dcSAndrew Lunn 	err = phy_write(phydev, 0x1e, 0xa2da);
1078e1dde8dcSAndrew Lunn 	if (err < 0)
1079e1dde8dcSAndrew Lunn 		return err;
1080e1dde8dcSAndrew Lunn 
1081e1dde8dcSAndrew Lunn 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
1082e1dde8dcSAndrew Lunn 		err = m88e1145_config_init_rgmii(phydev);
1083e1dde8dcSAndrew Lunn 		if (err < 0)
1084e1dde8dcSAndrew Lunn 			return err;
1085e1dde8dcSAndrew Lunn 	}
1086e1dde8dcSAndrew Lunn 
1087e1dde8dcSAndrew Lunn 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
1088e1dde8dcSAndrew Lunn 		err = m88e1145_config_init_sgmii(phydev);
1089b0224175SViet Nga Dao 		if (err < 0)
1090b0224175SViet Nga Dao 			return err;
1091b0224175SViet Nga Dao 	}
1092b0224175SViet Nga Dao 
1093cf41a51dSDavid Daney 	err = marvell_of_reg_init(phydev);
1094cf41a51dSDavid Daney 	if (err < 0)
1095cf41a51dSDavid Daney 		return err;
1096cf41a51dSDavid Daney 
109776884679SAndy Fleming 	return 0;
109876884679SAndy Fleming }
109900db8189SAndy Fleming 
110069f42be8SHeiner Kallweit static int m88e1540_get_fld(struct phy_device *phydev, u8 *msecs)
110169f42be8SHeiner Kallweit {
110269f42be8SHeiner Kallweit 	int val;
110369f42be8SHeiner Kallweit 
110469f42be8SHeiner Kallweit 	val = phy_read(phydev, MII_88E1540_COPPER_CTRL3);
110569f42be8SHeiner Kallweit 	if (val < 0)
110669f42be8SHeiner Kallweit 		return val;
110769f42be8SHeiner Kallweit 
110869f42be8SHeiner Kallweit 	if (!(val & MII_88E1540_COPPER_CTRL3_FAST_LINK_DOWN)) {
110969f42be8SHeiner Kallweit 		*msecs = ETHTOOL_PHY_FAST_LINK_DOWN_OFF;
111069f42be8SHeiner Kallweit 		return 0;
111169f42be8SHeiner Kallweit 	}
111269f42be8SHeiner Kallweit 
111369f42be8SHeiner Kallweit 	val = FIELD_GET(MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_MASK, val);
111469f42be8SHeiner Kallweit 
111569f42be8SHeiner Kallweit 	switch (val) {
111669f42be8SHeiner Kallweit 	case MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_00MS:
111769f42be8SHeiner Kallweit 		*msecs = 0;
111869f42be8SHeiner Kallweit 		break;
111969f42be8SHeiner Kallweit 	case MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_10MS:
112069f42be8SHeiner Kallweit 		*msecs = 10;
112169f42be8SHeiner Kallweit 		break;
112269f42be8SHeiner Kallweit 	case MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_20MS:
112369f42be8SHeiner Kallweit 		*msecs = 20;
112469f42be8SHeiner Kallweit 		break;
112569f42be8SHeiner Kallweit 	case MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_40MS:
112669f42be8SHeiner Kallweit 		*msecs = 40;
112769f42be8SHeiner Kallweit 		break;
112869f42be8SHeiner Kallweit 	default:
112969f42be8SHeiner Kallweit 		return -EINVAL;
113069f42be8SHeiner Kallweit 	}
113169f42be8SHeiner Kallweit 
113269f42be8SHeiner Kallweit 	return 0;
113369f42be8SHeiner Kallweit }
113469f42be8SHeiner Kallweit 
113569f42be8SHeiner Kallweit static int m88e1540_set_fld(struct phy_device *phydev, const u8 *msecs)
113669f42be8SHeiner Kallweit {
113769f42be8SHeiner Kallweit 	struct ethtool_eee eee;
113869f42be8SHeiner Kallweit 	int val, ret;
113969f42be8SHeiner Kallweit 
114069f42be8SHeiner Kallweit 	if (*msecs == ETHTOOL_PHY_FAST_LINK_DOWN_OFF)
114169f42be8SHeiner Kallweit 		return phy_clear_bits(phydev, MII_88E1540_COPPER_CTRL3,
114269f42be8SHeiner Kallweit 				      MII_88E1540_COPPER_CTRL3_FAST_LINK_DOWN);
114369f42be8SHeiner Kallweit 
114469f42be8SHeiner Kallweit 	/* According to the Marvell data sheet EEE must be disabled for
114569f42be8SHeiner Kallweit 	 * Fast Link Down detection to work properly
114669f42be8SHeiner Kallweit 	 */
114769f42be8SHeiner Kallweit 	ret = phy_ethtool_get_eee(phydev, &eee);
114869f42be8SHeiner Kallweit 	if (!ret && eee.eee_enabled) {
114969f42be8SHeiner Kallweit 		phydev_warn(phydev, "Fast Link Down detection requires EEE to be disabled!\n");
115069f42be8SHeiner Kallweit 		return -EBUSY;
115169f42be8SHeiner Kallweit 	}
115269f42be8SHeiner Kallweit 
115369f42be8SHeiner Kallweit 	if (*msecs <= 5)
115469f42be8SHeiner Kallweit 		val = MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_00MS;
115569f42be8SHeiner Kallweit 	else if (*msecs <= 15)
115669f42be8SHeiner Kallweit 		val = MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_10MS;
115769f42be8SHeiner Kallweit 	else if (*msecs <= 30)
115869f42be8SHeiner Kallweit 		val = MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_20MS;
115969f42be8SHeiner Kallweit 	else
116069f42be8SHeiner Kallweit 		val = MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_40MS;
116169f42be8SHeiner Kallweit 
116269f42be8SHeiner Kallweit 	val = FIELD_PREP(MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_MASK, val);
116369f42be8SHeiner Kallweit 
116469f42be8SHeiner Kallweit 	ret = phy_modify(phydev, MII_88E1540_COPPER_CTRL3,
116569f42be8SHeiner Kallweit 			 MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_MASK, val);
116669f42be8SHeiner Kallweit 	if (ret)
116769f42be8SHeiner Kallweit 		return ret;
116869f42be8SHeiner Kallweit 
116969f42be8SHeiner Kallweit 	return phy_set_bits(phydev, MII_88E1540_COPPER_CTRL3,
117069f42be8SHeiner Kallweit 			    MII_88E1540_COPPER_CTRL3_FAST_LINK_DOWN);
117169f42be8SHeiner Kallweit }
117269f42be8SHeiner Kallweit 
117369f42be8SHeiner Kallweit static int m88e1540_get_tunable(struct phy_device *phydev,
117469f42be8SHeiner Kallweit 				struct ethtool_tunable *tuna, void *data)
117569f42be8SHeiner Kallweit {
117669f42be8SHeiner Kallweit 	switch (tuna->id) {
117769f42be8SHeiner Kallweit 	case ETHTOOL_PHY_FAST_LINK_DOWN:
117869f42be8SHeiner Kallweit 		return m88e1540_get_fld(phydev, data);
1179a3bdfce7SHeiner Kallweit 	case ETHTOOL_PHY_DOWNSHIFT:
1180911af5e1SHeiner Kallweit 		return m88e1011_get_downshift(phydev, data);
118169f42be8SHeiner Kallweit 	default:
118269f42be8SHeiner Kallweit 		return -EOPNOTSUPP;
118369f42be8SHeiner Kallweit 	}
118469f42be8SHeiner Kallweit }
118569f42be8SHeiner Kallweit 
118669f42be8SHeiner Kallweit static int m88e1540_set_tunable(struct phy_device *phydev,
118769f42be8SHeiner Kallweit 				struct ethtool_tunable *tuna, const void *data)
118869f42be8SHeiner Kallweit {
118969f42be8SHeiner Kallweit 	switch (tuna->id) {
119069f42be8SHeiner Kallweit 	case ETHTOOL_PHY_FAST_LINK_DOWN:
119169f42be8SHeiner Kallweit 		return m88e1540_set_fld(phydev, data);
1192a3bdfce7SHeiner Kallweit 	case ETHTOOL_PHY_DOWNSHIFT:
1193911af5e1SHeiner Kallweit 		return m88e1011_set_downshift(phydev, *(const u8 *)data);
119469f42be8SHeiner Kallweit 	default:
119569f42be8SHeiner Kallweit 		return -EOPNOTSUPP;
119669f42be8SHeiner Kallweit 	}
119769f42be8SHeiner Kallweit }
119869f42be8SHeiner Kallweit 
11998cbcdc1aSAndrew Lunn /* The VOD can be out of specification on link up. Poke an
12008cbcdc1aSAndrew Lunn  * undocumented register, in an undocumented page, with a magic value
12018cbcdc1aSAndrew Lunn  * to fix this.
12028cbcdc1aSAndrew Lunn  */
12038cbcdc1aSAndrew Lunn static int m88e6390_errata(struct phy_device *phydev)
12048cbcdc1aSAndrew Lunn {
12058cbcdc1aSAndrew Lunn 	int err;
12068cbcdc1aSAndrew Lunn 
12078cbcdc1aSAndrew Lunn 	err = phy_write(phydev, MII_BMCR,
12088cbcdc1aSAndrew Lunn 			BMCR_ANENABLE | BMCR_SPEED1000 | BMCR_FULLDPLX);
12098cbcdc1aSAndrew Lunn 	if (err)
12108cbcdc1aSAndrew Lunn 		return err;
12118cbcdc1aSAndrew Lunn 
12128cbcdc1aSAndrew Lunn 	usleep_range(300, 400);
12138cbcdc1aSAndrew Lunn 
12148cbcdc1aSAndrew Lunn 	err = phy_write_paged(phydev, 0xf8, 0x08, 0x36);
12158cbcdc1aSAndrew Lunn 	if (err)
12168cbcdc1aSAndrew Lunn 		return err;
12178cbcdc1aSAndrew Lunn 
12188cbcdc1aSAndrew Lunn 	return genphy_soft_reset(phydev);
12198cbcdc1aSAndrew Lunn }
12208cbcdc1aSAndrew Lunn 
12218cbcdc1aSAndrew Lunn static int m88e6390_config_aneg(struct phy_device *phydev)
12228cbcdc1aSAndrew Lunn {
12238cbcdc1aSAndrew Lunn 	int err;
12248cbcdc1aSAndrew Lunn 
12258cbcdc1aSAndrew Lunn 	err = m88e6390_errata(phydev);
12268cbcdc1aSAndrew Lunn 	if (err)
12278cbcdc1aSAndrew Lunn 		return err;
12288cbcdc1aSAndrew Lunn 
12298cbcdc1aSAndrew Lunn 	return m88e1510_config_aneg(phydev);
12308cbcdc1aSAndrew Lunn }
12318cbcdc1aSAndrew Lunn 
12326cfb3bccSCharles-Antoine Couret /**
1233ab9cb729SAndrew Lunn  * fiber_lpa_mod_linkmode_lpa_t
1234c0ec3c27SAndrew Lunn  * @advertising: the linkmode advertisement settings
12356cfb3bccSCharles-Antoine Couret  * @lpa: value of the MII_LPA register for fiber link
1236be937f1fSAlexandr Smirnov  *
1237ab9cb729SAndrew Lunn  * A small helper function that translates MII_LPA bits to linkmode LP
1238ab9cb729SAndrew Lunn  * advertisement settings. Other bits in advertising are left
1239ab9cb729SAndrew Lunn  * unchanged.
12406cfb3bccSCharles-Antoine Couret  */
1241ab9cb729SAndrew Lunn static void fiber_lpa_mod_linkmode_lpa_t(unsigned long *advertising, u32 lpa)
12426cfb3bccSCharles-Antoine Couret {
1243ab9cb729SAndrew Lunn 	linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
1244ab9cb729SAndrew Lunn 			 advertising, lpa & LPA_FIBER_1000HALF);
1245ab9cb729SAndrew Lunn 
1246ab9cb729SAndrew Lunn 	linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
1247ab9cb729SAndrew Lunn 			 advertising, lpa & LPA_FIBER_1000FULL);
12486cfb3bccSCharles-Antoine Couret }
12496cfb3bccSCharles-Antoine Couret 
12506cfb3bccSCharles-Antoine Couret /**
12516cfb3bccSCharles-Antoine Couret  * marvell_update_link - update link status in real time in @phydev
12526cfb3bccSCharles-Antoine Couret  * @phydev: target phy_device struct
12536cfb3bccSCharles-Antoine Couret  *
12546cfb3bccSCharles-Antoine Couret  * Description: Update the value in phydev->link to reflect the
12556cfb3bccSCharles-Antoine Couret  *   current link value.
12566cfb3bccSCharles-Antoine Couret  */
12576cfb3bccSCharles-Antoine Couret static int marvell_update_link(struct phy_device *phydev, int fiber)
12586cfb3bccSCharles-Antoine Couret {
12596cfb3bccSCharles-Antoine Couret 	int status;
12606cfb3bccSCharles-Antoine Couret 
12616cfb3bccSCharles-Antoine Couret 	/* Use the generic register for copper link, or specific
12620c3439bcSAndrew Lunn 	 * register for fiber case
12630c3439bcSAndrew Lunn 	 */
12646cfb3bccSCharles-Antoine Couret 	if (fiber) {
12656cfb3bccSCharles-Antoine Couret 		status = phy_read(phydev, MII_M1011_PHY_STATUS);
12666cfb3bccSCharles-Antoine Couret 		if (status < 0)
12676cfb3bccSCharles-Antoine Couret 			return status;
12686cfb3bccSCharles-Antoine Couret 
12696cfb3bccSCharles-Antoine Couret 		if ((status & REGISTER_LINK_STATUS) == 0)
12706cfb3bccSCharles-Antoine Couret 			phydev->link = 0;
12716cfb3bccSCharles-Antoine Couret 		else
12726cfb3bccSCharles-Antoine Couret 			phydev->link = 1;
12736cfb3bccSCharles-Antoine Couret 	} else {
12746cfb3bccSCharles-Antoine Couret 		return genphy_update_link(phydev);
12756cfb3bccSCharles-Antoine Couret 	}
12766cfb3bccSCharles-Antoine Couret 
12776cfb3bccSCharles-Antoine Couret 	return 0;
12786cfb3bccSCharles-Antoine Couret }
12796cfb3bccSCharles-Antoine Couret 
1280e1dde8dcSAndrew Lunn static int marvell_read_status_page_an(struct phy_device *phydev,
1281e1dde8dcSAndrew Lunn 				       int fiber)
1282be937f1fSAlexandr Smirnov {
1283e1dde8dcSAndrew Lunn 	int status;
1284be937f1fSAlexandr Smirnov 	int lpa;
1285357cd64cSRussell King 	int lpagb;
1286be937f1fSAlexandr Smirnov 
1287be937f1fSAlexandr Smirnov 	status = phy_read(phydev, MII_M1011_PHY_STATUS);
1288be937f1fSAlexandr Smirnov 	if (status < 0)
1289be937f1fSAlexandr Smirnov 		return status;
1290be937f1fSAlexandr Smirnov 
1291be937f1fSAlexandr Smirnov 	lpa = phy_read(phydev, MII_LPA);
1292be937f1fSAlexandr Smirnov 	if (lpa < 0)
1293be937f1fSAlexandr Smirnov 		return lpa;
1294be937f1fSAlexandr Smirnov 
1295357cd64cSRussell King 	lpagb = phy_read(phydev, MII_STAT1000);
1296357cd64cSRussell King 	if (lpagb < 0)
1297357cd64cSRussell King 		return lpagb;
1298357cd64cSRussell King 
1299be937f1fSAlexandr Smirnov 	if (status & MII_M1011_PHY_STATUS_FULLDUPLEX)
1300be937f1fSAlexandr Smirnov 		phydev->duplex = DUPLEX_FULL;
1301be937f1fSAlexandr Smirnov 	else
1302be937f1fSAlexandr Smirnov 		phydev->duplex = DUPLEX_HALF;
1303be937f1fSAlexandr Smirnov 
1304be937f1fSAlexandr Smirnov 	status = status & MII_M1011_PHY_STATUS_SPD_MASK;
13054f48ed32SAndrew Lunn 	phydev->pause = 0;
13064f48ed32SAndrew Lunn 	phydev->asym_pause = 0;
1307be937f1fSAlexandr Smirnov 
1308be937f1fSAlexandr Smirnov 	switch (status) {
1309be937f1fSAlexandr Smirnov 	case MII_M1011_PHY_STATUS_1000:
1310be937f1fSAlexandr Smirnov 		phydev->speed = SPEED_1000;
1311be937f1fSAlexandr Smirnov 		break;
1312be937f1fSAlexandr Smirnov 
1313be937f1fSAlexandr Smirnov 	case MII_M1011_PHY_STATUS_100:
1314be937f1fSAlexandr Smirnov 		phydev->speed = SPEED_100;
1315be937f1fSAlexandr Smirnov 		break;
1316be937f1fSAlexandr Smirnov 
1317be937f1fSAlexandr Smirnov 	default:
1318be937f1fSAlexandr Smirnov 		phydev->speed = SPEED_10;
1319be937f1fSAlexandr Smirnov 		break;
1320be937f1fSAlexandr Smirnov 	}
1321be937f1fSAlexandr Smirnov 
13226cfb3bccSCharles-Antoine Couret 	if (!fiber) {
1323c0ec3c27SAndrew Lunn 		mii_lpa_to_linkmode_lpa_t(phydev->lp_advertising, lpa);
132478a24df3SAndrew Lunn 		mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, lpagb);
13256cfb3bccSCharles-Antoine Couret 
1326be937f1fSAlexandr Smirnov 		if (phydev->duplex == DUPLEX_FULL) {
1327be937f1fSAlexandr Smirnov 			phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0;
1328be937f1fSAlexandr Smirnov 			phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0;
1329be937f1fSAlexandr Smirnov 		}
1330be937f1fSAlexandr Smirnov 	} else {
13316cfb3bccSCharles-Antoine Couret 		/* The fiber link is only 1000M capable */
1332ab9cb729SAndrew Lunn 		fiber_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa);
13336cfb3bccSCharles-Antoine Couret 
13346cfb3bccSCharles-Antoine Couret 		if (phydev->duplex == DUPLEX_FULL) {
13356cfb3bccSCharles-Antoine Couret 			if (!(lpa & LPA_PAUSE_FIBER)) {
13366cfb3bccSCharles-Antoine Couret 				phydev->pause = 0;
13376cfb3bccSCharles-Antoine Couret 				phydev->asym_pause = 0;
13386cfb3bccSCharles-Antoine Couret 			} else if ((lpa & LPA_PAUSE_ASYM_FIBER)) {
13396cfb3bccSCharles-Antoine Couret 				phydev->pause = 1;
13406cfb3bccSCharles-Antoine Couret 				phydev->asym_pause = 1;
13416cfb3bccSCharles-Antoine Couret 			} else {
13426cfb3bccSCharles-Antoine Couret 				phydev->pause = 1;
13436cfb3bccSCharles-Antoine Couret 				phydev->asym_pause = 0;
13446cfb3bccSCharles-Antoine Couret 			}
13456cfb3bccSCharles-Antoine Couret 		}
13466cfb3bccSCharles-Antoine Couret 	}
1347e1dde8dcSAndrew Lunn 	return 0;
1348e1dde8dcSAndrew Lunn }
1349e1dde8dcSAndrew Lunn 
1350e1dde8dcSAndrew Lunn static int marvell_read_status_page_fixed(struct phy_device *phydev)
1351e1dde8dcSAndrew Lunn {
1352be937f1fSAlexandr Smirnov 	int bmcr = phy_read(phydev, MII_BMCR);
1353be937f1fSAlexandr Smirnov 
1354be937f1fSAlexandr Smirnov 	if (bmcr < 0)
1355be937f1fSAlexandr Smirnov 		return bmcr;
1356be937f1fSAlexandr Smirnov 
1357be937f1fSAlexandr Smirnov 	if (bmcr & BMCR_FULLDPLX)
1358be937f1fSAlexandr Smirnov 		phydev->duplex = DUPLEX_FULL;
1359be937f1fSAlexandr Smirnov 	else
1360be937f1fSAlexandr Smirnov 		phydev->duplex = DUPLEX_HALF;
1361be937f1fSAlexandr Smirnov 
1362be937f1fSAlexandr Smirnov 	if (bmcr & BMCR_SPEED1000)
1363be937f1fSAlexandr Smirnov 		phydev->speed = SPEED_1000;
1364be937f1fSAlexandr Smirnov 	else if (bmcr & BMCR_SPEED100)
1365be937f1fSAlexandr Smirnov 		phydev->speed = SPEED_100;
1366be937f1fSAlexandr Smirnov 	else
1367be937f1fSAlexandr Smirnov 		phydev->speed = SPEED_10;
1368be937f1fSAlexandr Smirnov 
13694f48ed32SAndrew Lunn 	phydev->pause = 0;
13704f48ed32SAndrew Lunn 	phydev->asym_pause = 0;
1371c0ec3c27SAndrew Lunn 	linkmode_zero(phydev->lp_advertising);
1372be937f1fSAlexandr Smirnov 
1373be937f1fSAlexandr Smirnov 	return 0;
1374be937f1fSAlexandr Smirnov }
1375be937f1fSAlexandr Smirnov 
1376e1dde8dcSAndrew Lunn /* marvell_read_status_page
1377e1dde8dcSAndrew Lunn  *
1378e1dde8dcSAndrew Lunn  * Description:
1379e1dde8dcSAndrew Lunn  *   Check the link, then figure out the current state
1380e1dde8dcSAndrew Lunn  *   by comparing what we advertise with what the link partner
1381e1dde8dcSAndrew Lunn  *   advertises.  Start by checking the gigabit possibilities,
1382e1dde8dcSAndrew Lunn  *   then move on to 10/100.
1383e1dde8dcSAndrew Lunn  */
1384e1dde8dcSAndrew Lunn static int marvell_read_status_page(struct phy_device *phydev, int page)
1385e1dde8dcSAndrew Lunn {
1386e1dde8dcSAndrew Lunn 	int fiber;
1387e1dde8dcSAndrew Lunn 	int err;
1388e1dde8dcSAndrew Lunn 
1389e1dde8dcSAndrew Lunn 	/* Detect and update the link, but return if there
1390e1dde8dcSAndrew Lunn 	 * was an error
1391e1dde8dcSAndrew Lunn 	 */
139252295666SAndrew Lunn 	if (page == MII_MARVELL_FIBER_PAGE)
1393e1dde8dcSAndrew Lunn 		fiber = 1;
1394e1dde8dcSAndrew Lunn 	else
1395e1dde8dcSAndrew Lunn 		fiber = 0;
1396e1dde8dcSAndrew Lunn 
1397e1dde8dcSAndrew Lunn 	err = marvell_update_link(phydev, fiber);
1398e1dde8dcSAndrew Lunn 	if (err)
1399e1dde8dcSAndrew Lunn 		return err;
1400e1dde8dcSAndrew Lunn 
1401e1dde8dcSAndrew Lunn 	if (phydev->autoneg == AUTONEG_ENABLE)
1402e1dde8dcSAndrew Lunn 		err = marvell_read_status_page_an(phydev, fiber);
1403e1dde8dcSAndrew Lunn 	else
1404e1dde8dcSAndrew Lunn 		err = marvell_read_status_page_fixed(phydev);
1405e1dde8dcSAndrew Lunn 
1406e1dde8dcSAndrew Lunn 	return err;
1407e1dde8dcSAndrew Lunn }
1408e1dde8dcSAndrew Lunn 
14096cfb3bccSCharles-Antoine Couret /* marvell_read_status
14106cfb3bccSCharles-Antoine Couret  *
14116cfb3bccSCharles-Antoine Couret  * Some Marvell's phys have two modes: fiber and copper.
14126cfb3bccSCharles-Antoine Couret  * Both need status checked.
14136cfb3bccSCharles-Antoine Couret  * Description:
14146cfb3bccSCharles-Antoine Couret  *   First, check the fiber link and status.
14156cfb3bccSCharles-Antoine Couret  *   If the fiber link is down, check the copper link and status which
14166cfb3bccSCharles-Antoine Couret  *   will be the default value if both link are down.
14176cfb3bccSCharles-Antoine Couret  */
14186cfb3bccSCharles-Antoine Couret static int marvell_read_status(struct phy_device *phydev)
14196cfb3bccSCharles-Antoine Couret {
14206cfb3bccSCharles-Antoine Couret 	int err;
14216cfb3bccSCharles-Antoine Couret 
14226cfb3bccSCharles-Antoine Couret 	/* Check the fiber mode first */
14233c1bcc86SAndrew Lunn 	if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
14243c1bcc86SAndrew Lunn 			      phydev->supported) &&
1425a13c0652SRussell King 	    phydev->interface != PHY_INTERFACE_MODE_SGMII) {
142652295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
14276cfb3bccSCharles-Antoine Couret 		if (err < 0)
14286cfb3bccSCharles-Antoine Couret 			goto error;
14296cfb3bccSCharles-Antoine Couret 
143052295666SAndrew Lunn 		err = marvell_read_status_page(phydev, MII_MARVELL_FIBER_PAGE);
14316cfb3bccSCharles-Antoine Couret 		if (err < 0)
14326cfb3bccSCharles-Antoine Couret 			goto error;
14336cfb3bccSCharles-Antoine Couret 
14340c3439bcSAndrew Lunn 		/* If the fiber link is up, it is the selected and
14350c3439bcSAndrew Lunn 		 * used link. In this case, we need to stay in the
14360c3439bcSAndrew Lunn 		 * fiber page. Please to be careful about that, avoid
14370c3439bcSAndrew Lunn 		 * to restore Copper page in other functions which
14380c3439bcSAndrew Lunn 		 * could break the behaviour for some fiber phy like
14390c3439bcSAndrew Lunn 		 * 88E1512.
14400c3439bcSAndrew Lunn 		 */
14416cfb3bccSCharles-Antoine Couret 		if (phydev->link)
14426cfb3bccSCharles-Antoine Couret 			return 0;
14436cfb3bccSCharles-Antoine Couret 
14446cfb3bccSCharles-Antoine Couret 		/* If fiber link is down, check and save copper mode state */
144552295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
14466cfb3bccSCharles-Antoine Couret 		if (err < 0)
14476cfb3bccSCharles-Antoine Couret 			goto error;
14486cfb3bccSCharles-Antoine Couret 	}
14496cfb3bccSCharles-Antoine Couret 
145052295666SAndrew Lunn 	return marvell_read_status_page(phydev, MII_MARVELL_COPPER_PAGE);
14516cfb3bccSCharles-Antoine Couret 
14526cfb3bccSCharles-Antoine Couret error:
145352295666SAndrew Lunn 	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
14546cfb3bccSCharles-Antoine Couret 	return err;
14556cfb3bccSCharles-Antoine Couret }
14563758be3dSCharles-Antoine Couret 
14573758be3dSCharles-Antoine Couret /* marvell_suspend
14583758be3dSCharles-Antoine Couret  *
14593758be3dSCharles-Antoine Couret  * Some Marvell's phys have two modes: fiber and copper.
14603758be3dSCharles-Antoine Couret  * Both need to be suspended
14613758be3dSCharles-Antoine Couret  */
14623758be3dSCharles-Antoine Couret static int marvell_suspend(struct phy_device *phydev)
14633758be3dSCharles-Antoine Couret {
14643758be3dSCharles-Antoine Couret 	int err;
14653758be3dSCharles-Antoine Couret 
14663758be3dSCharles-Antoine Couret 	/* Suspend the fiber mode first */
14673c1bcc86SAndrew Lunn 	if (!linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
14683c1bcc86SAndrew Lunn 			       phydev->supported)) {
146952295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
14703758be3dSCharles-Antoine Couret 		if (err < 0)
14713758be3dSCharles-Antoine Couret 			goto error;
14723758be3dSCharles-Antoine Couret 
14733758be3dSCharles-Antoine Couret 		/* With the page set, use the generic suspend */
14743758be3dSCharles-Antoine Couret 		err = genphy_suspend(phydev);
14753758be3dSCharles-Antoine Couret 		if (err < 0)
14763758be3dSCharles-Antoine Couret 			goto error;
14773758be3dSCharles-Antoine Couret 
14783758be3dSCharles-Antoine Couret 		/* Then, the copper link */
147952295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
14803758be3dSCharles-Antoine Couret 		if (err < 0)
14813758be3dSCharles-Antoine Couret 			goto error;
14823758be3dSCharles-Antoine Couret 	}
14833758be3dSCharles-Antoine Couret 
14843758be3dSCharles-Antoine Couret 	/* With the page set, use the generic suspend */
14853758be3dSCharles-Antoine Couret 	return genphy_suspend(phydev);
14863758be3dSCharles-Antoine Couret 
14873758be3dSCharles-Antoine Couret error:
148852295666SAndrew Lunn 	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
14893758be3dSCharles-Antoine Couret 	return err;
14903758be3dSCharles-Antoine Couret }
14913758be3dSCharles-Antoine Couret 
14923758be3dSCharles-Antoine Couret /* marvell_resume
14933758be3dSCharles-Antoine Couret  *
14943758be3dSCharles-Antoine Couret  * Some Marvell's phys have two modes: fiber and copper.
14953758be3dSCharles-Antoine Couret  * Both need to be resumed
14963758be3dSCharles-Antoine Couret  */
14973758be3dSCharles-Antoine Couret static int marvell_resume(struct phy_device *phydev)
14983758be3dSCharles-Antoine Couret {
14993758be3dSCharles-Antoine Couret 	int err;
15003758be3dSCharles-Antoine Couret 
15013758be3dSCharles-Antoine Couret 	/* Resume the fiber mode first */
15023c1bcc86SAndrew Lunn 	if (!linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
15033c1bcc86SAndrew Lunn 			       phydev->supported)) {
150452295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
15053758be3dSCharles-Antoine Couret 		if (err < 0)
15063758be3dSCharles-Antoine Couret 			goto error;
15073758be3dSCharles-Antoine Couret 
15083758be3dSCharles-Antoine Couret 		/* With the page set, use the generic resume */
15093758be3dSCharles-Antoine Couret 		err = genphy_resume(phydev);
15103758be3dSCharles-Antoine Couret 		if (err < 0)
15113758be3dSCharles-Antoine Couret 			goto error;
15123758be3dSCharles-Antoine Couret 
15133758be3dSCharles-Antoine Couret 		/* Then, the copper link */
151452295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
15153758be3dSCharles-Antoine Couret 		if (err < 0)
15163758be3dSCharles-Antoine Couret 			goto error;
15173758be3dSCharles-Antoine Couret 	}
15183758be3dSCharles-Antoine Couret 
15193758be3dSCharles-Antoine Couret 	/* With the page set, use the generic resume */
15203758be3dSCharles-Antoine Couret 	return genphy_resume(phydev);
15213758be3dSCharles-Antoine Couret 
15223758be3dSCharles-Antoine Couret error:
152352295666SAndrew Lunn 	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
15243758be3dSCharles-Antoine Couret 	return err;
15253758be3dSCharles-Antoine Couret }
15263758be3dSCharles-Antoine Couret 
15276b358aedSSebastian Hesselbarth static int marvell_aneg_done(struct phy_device *phydev)
15286b358aedSSebastian Hesselbarth {
15296b358aedSSebastian Hesselbarth 	int retval = phy_read(phydev, MII_M1011_PHY_STATUS);
1530e69d9ed4SAndrew Lunn 
15316b358aedSSebastian Hesselbarth 	return (retval < 0) ? retval : (retval & MII_M1011_PHY_STATUS_RESOLVED);
15326b358aedSSebastian Hesselbarth }
15336b358aedSSebastian Hesselbarth 
1534dcd07be3SAnatolij Gustschin static int m88e1121_did_interrupt(struct phy_device *phydev)
1535dcd07be3SAnatolij Gustschin {
1536dcd07be3SAnatolij Gustschin 	int imask;
1537dcd07be3SAnatolij Gustschin 
1538dcd07be3SAnatolij Gustschin 	imask = phy_read(phydev, MII_M1011_IEVENT);
1539dcd07be3SAnatolij Gustschin 
1540dcd07be3SAnatolij Gustschin 	if (imask & MII_M1011_IMASK_INIT)
1541dcd07be3SAnatolij Gustschin 		return 1;
1542dcd07be3SAnatolij Gustschin 
1543dcd07be3SAnatolij Gustschin 	return 0;
1544dcd07be3SAnatolij Gustschin }
1545dcd07be3SAnatolij Gustschin 
154623beb38fSAndrew Lunn static void m88e1318_get_wol(struct phy_device *phydev,
154723beb38fSAndrew Lunn 			     struct ethtool_wolinfo *wol)
15483871c387SMichael Stapelberg {
1549424ca4c5SRussell King 	int oldpage, ret = 0;
1550424ca4c5SRussell King 
15513871c387SMichael Stapelberg 	wol->supported = WAKE_MAGIC;
15523871c387SMichael Stapelberg 	wol->wolopts = 0;
15533871c387SMichael Stapelberg 
1554424ca4c5SRussell King 	oldpage = phy_select_page(phydev, MII_MARVELL_WOL_PAGE);
1555424ca4c5SRussell King 	if (oldpage < 0)
1556424ca4c5SRussell King 		goto error;
15573871c387SMichael Stapelberg 
1558424ca4c5SRussell King 	ret = __phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL);
1559424ca4c5SRussell King 	if (ret & MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE)
15603871c387SMichael Stapelberg 		wol->wolopts |= WAKE_MAGIC;
15613871c387SMichael Stapelberg 
1562424ca4c5SRussell King error:
1563424ca4c5SRussell King 	phy_restore_page(phydev, oldpage, ret);
15643871c387SMichael Stapelberg }
15653871c387SMichael Stapelberg 
156623beb38fSAndrew Lunn static int m88e1318_set_wol(struct phy_device *phydev,
156723beb38fSAndrew Lunn 			    struct ethtool_wolinfo *wol)
15683871c387SMichael Stapelberg {
1569424ca4c5SRussell King 	int err = 0, oldpage;
15703871c387SMichael Stapelberg 
1571424ca4c5SRussell King 	oldpage = phy_save_page(phydev);
1572424ca4c5SRussell King 	if (oldpage < 0)
1573424ca4c5SRussell King 		goto error;
15743871c387SMichael Stapelberg 
15753871c387SMichael Stapelberg 	if (wol->wolopts & WAKE_MAGIC) {
15763871c387SMichael Stapelberg 		/* Explicitly switch to page 0x00, just to be sure */
1577424ca4c5SRussell King 		err = marvell_write_page(phydev, MII_MARVELL_COPPER_PAGE);
15783871c387SMichael Stapelberg 		if (err < 0)
1579424ca4c5SRussell King 			goto error;
15803871c387SMichael Stapelberg 
1581b6a930faSJingju Hou 		/* If WOL event happened once, the LED[2] interrupt pin
1582b6a930faSJingju Hou 		 * will not be cleared unless we reading the interrupt status
1583b6a930faSJingju Hou 		 * register. If interrupts are in use, the normal interrupt
1584b6a930faSJingju Hou 		 * handling will clear the WOL event. Clear the WOL event
1585b6a930faSJingju Hou 		 * before enabling it if !phy_interrupt_is_valid()
1586b6a930faSJingju Hou 		 */
1587b6a930faSJingju Hou 		if (!phy_interrupt_is_valid(phydev))
1588e0a7328fSAndrew Lunn 			__phy_read(phydev, MII_M1011_IEVENT);
1589b6a930faSJingju Hou 
15903871c387SMichael Stapelberg 		/* Enable the WOL interrupt */
1591424ca4c5SRussell King 		err = __phy_modify(phydev, MII_88E1318S_PHY_CSIER, 0,
1592424ca4c5SRussell King 				   MII_88E1318S_PHY_CSIER_WOL_EIE);
15933871c387SMichael Stapelberg 		if (err < 0)
1594424ca4c5SRussell King 			goto error;
15953871c387SMichael Stapelberg 
1596424ca4c5SRussell King 		err = marvell_write_page(phydev, MII_MARVELL_LED_PAGE);
15973871c387SMichael Stapelberg 		if (err < 0)
1598424ca4c5SRussell King 			goto error;
15993871c387SMichael Stapelberg 
16003871c387SMichael Stapelberg 		/* Setup LED[2] as interrupt pin (active low) */
1601424ca4c5SRussell King 		err = __phy_modify(phydev, MII_88E1318S_PHY_LED_TCR,
1602f102852fSRussell King 				   MII_88E1318S_PHY_LED_TCR_FORCE_INT,
1603424ca4c5SRussell King 				   MII_88E1318S_PHY_LED_TCR_INTn_ENABLE |
1604424ca4c5SRussell King 				   MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW);
16053871c387SMichael Stapelberg 		if (err < 0)
1606424ca4c5SRussell King 			goto error;
16073871c387SMichael Stapelberg 
1608424ca4c5SRussell King 		err = marvell_write_page(phydev, MII_MARVELL_WOL_PAGE);
16093871c387SMichael Stapelberg 		if (err < 0)
1610424ca4c5SRussell King 			goto error;
16113871c387SMichael Stapelberg 
16123871c387SMichael Stapelberg 		/* Store the device address for the magic packet */
1613424ca4c5SRussell King 		err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD2,
16143871c387SMichael Stapelberg 				((phydev->attached_dev->dev_addr[5] << 8) |
16153871c387SMichael Stapelberg 				 phydev->attached_dev->dev_addr[4]));
16163871c387SMichael Stapelberg 		if (err < 0)
1617424ca4c5SRussell King 			goto error;
1618424ca4c5SRussell King 		err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD1,
16193871c387SMichael Stapelberg 				((phydev->attached_dev->dev_addr[3] << 8) |
16203871c387SMichael Stapelberg 				 phydev->attached_dev->dev_addr[2]));
16213871c387SMichael Stapelberg 		if (err < 0)
1622424ca4c5SRussell King 			goto error;
1623424ca4c5SRussell King 		err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD0,
16243871c387SMichael Stapelberg 				((phydev->attached_dev->dev_addr[1] << 8) |
16253871c387SMichael Stapelberg 				 phydev->attached_dev->dev_addr[0]));
16263871c387SMichael Stapelberg 		if (err < 0)
1627424ca4c5SRussell King 			goto error;
16283871c387SMichael Stapelberg 
16293871c387SMichael Stapelberg 		/* Clear WOL status and enable magic packet matching */
1630424ca4c5SRussell King 		err = __phy_modify(phydev, MII_88E1318S_PHY_WOL_CTRL, 0,
1631424ca4c5SRussell King 				   MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS |
1632424ca4c5SRussell King 				   MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE);
16333871c387SMichael Stapelberg 		if (err < 0)
1634424ca4c5SRussell King 			goto error;
16353871c387SMichael Stapelberg 	} else {
1636424ca4c5SRussell King 		err = marvell_write_page(phydev, MII_MARVELL_WOL_PAGE);
16373871c387SMichael Stapelberg 		if (err < 0)
1638424ca4c5SRussell King 			goto error;
16393871c387SMichael Stapelberg 
16403871c387SMichael Stapelberg 		/* Clear WOL status and disable magic packet matching */
1641424ca4c5SRussell King 		err = __phy_modify(phydev, MII_88E1318S_PHY_WOL_CTRL,
1642f102852fSRussell King 				   MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE,
1643424ca4c5SRussell King 				   MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS);
16443871c387SMichael Stapelberg 		if (err < 0)
1645424ca4c5SRussell King 			goto error;
16463871c387SMichael Stapelberg 	}
16473871c387SMichael Stapelberg 
1648424ca4c5SRussell King error:
1649424ca4c5SRussell King 	return phy_restore_page(phydev, oldpage, err);
16503871c387SMichael Stapelberg }
16513871c387SMichael Stapelberg 
1652d2fa47d9SAndrew Lunn static int marvell_get_sset_count(struct phy_device *phydev)
1653d2fa47d9SAndrew Lunn {
16543c1bcc86SAndrew Lunn 	if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
16553c1bcc86SAndrew Lunn 			      phydev->supported))
1656d2fa47d9SAndrew Lunn 		return ARRAY_SIZE(marvell_hw_stats);
16572170fef7SCharles-Antoine Couret 	else
16582170fef7SCharles-Antoine Couret 		return ARRAY_SIZE(marvell_hw_stats) - NB_FIBER_STATS;
1659d2fa47d9SAndrew Lunn }
1660d2fa47d9SAndrew Lunn 
1661d2fa47d9SAndrew Lunn static void marvell_get_strings(struct phy_device *phydev, u8 *data)
1662d2fa47d9SAndrew Lunn {
1663fdfdf867SAndrew Lunn 	int count = marvell_get_sset_count(phydev);
1664d2fa47d9SAndrew Lunn 	int i;
1665d2fa47d9SAndrew Lunn 
1666fdfdf867SAndrew Lunn 	for (i = 0; i < count; i++) {
166798409b2bSFlorian Fainelli 		strlcpy(data + i * ETH_GSTRING_LEN,
1668d2fa47d9SAndrew Lunn 			marvell_hw_stats[i].string, ETH_GSTRING_LEN);
1669d2fa47d9SAndrew Lunn 	}
1670d2fa47d9SAndrew Lunn }
1671d2fa47d9SAndrew Lunn 
1672d2fa47d9SAndrew Lunn static u64 marvell_get_stat(struct phy_device *phydev, int i)
1673d2fa47d9SAndrew Lunn {
1674d2fa47d9SAndrew Lunn 	struct marvell_hw_stat stat = marvell_hw_stats[i];
1675d2fa47d9SAndrew Lunn 	struct marvell_priv *priv = phydev->priv;
1676424ca4c5SRussell King 	int val;
1677321b4d4bSAndrew Lunn 	u64 ret;
1678d2fa47d9SAndrew Lunn 
1679424ca4c5SRussell King 	val = phy_read_paged(phydev, stat.page, stat.reg);
1680d2fa47d9SAndrew Lunn 	if (val < 0) {
16816c3442f5SJisheng Zhang 		ret = U64_MAX;
1682d2fa47d9SAndrew Lunn 	} else {
1683d2fa47d9SAndrew Lunn 		val = val & ((1 << stat.bits) - 1);
1684d2fa47d9SAndrew Lunn 		priv->stats[i] += val;
1685321b4d4bSAndrew Lunn 		ret = priv->stats[i];
1686d2fa47d9SAndrew Lunn 	}
1687d2fa47d9SAndrew Lunn 
1688321b4d4bSAndrew Lunn 	return ret;
1689d2fa47d9SAndrew Lunn }
1690d2fa47d9SAndrew Lunn 
1691d2fa47d9SAndrew Lunn static void marvell_get_stats(struct phy_device *phydev,
1692d2fa47d9SAndrew Lunn 			      struct ethtool_stats *stats, u64 *data)
1693d2fa47d9SAndrew Lunn {
1694fdfdf867SAndrew Lunn 	int count = marvell_get_sset_count(phydev);
1695d2fa47d9SAndrew Lunn 	int i;
1696d2fa47d9SAndrew Lunn 
1697fdfdf867SAndrew Lunn 	for (i = 0; i < count; i++)
1698d2fa47d9SAndrew Lunn 		data[i] = marvell_get_stat(phydev, i);
1699d2fa47d9SAndrew Lunn }
1700d2fa47d9SAndrew Lunn 
17010b04680fSAndrew Lunn #ifdef CONFIG_HWMON
17020b04680fSAndrew Lunn static int m88e1121_get_temp(struct phy_device *phydev, long *temp)
17030b04680fSAndrew Lunn {
1704975b388cSAndrew Lunn 	int oldpage;
1705424ca4c5SRussell King 	int ret = 0;
17060b04680fSAndrew Lunn 	int val;
17070b04680fSAndrew Lunn 
17080b04680fSAndrew Lunn 	*temp = 0;
17090b04680fSAndrew Lunn 
1710424ca4c5SRussell King 	oldpage = phy_select_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
1711424ca4c5SRussell King 	if (oldpage < 0)
1712424ca4c5SRussell King 		goto error;
1713975b388cSAndrew Lunn 
17140b04680fSAndrew Lunn 	/* Enable temperature sensor */
1715424ca4c5SRussell King 	ret = __phy_read(phydev, MII_88E1121_MISC_TEST);
17160b04680fSAndrew Lunn 	if (ret < 0)
17170b04680fSAndrew Lunn 		goto error;
17180b04680fSAndrew Lunn 
1719424ca4c5SRussell King 	ret = __phy_write(phydev, MII_88E1121_MISC_TEST,
17200b04680fSAndrew Lunn 			  ret | MII_88E1121_MISC_TEST_TEMP_SENSOR_EN);
17210b04680fSAndrew Lunn 	if (ret < 0)
17220b04680fSAndrew Lunn 		goto error;
17230b04680fSAndrew Lunn 
17240b04680fSAndrew Lunn 	/* Wait for temperature to stabilize */
17250b04680fSAndrew Lunn 	usleep_range(10000, 12000);
17260b04680fSAndrew Lunn 
1727424ca4c5SRussell King 	val = __phy_read(phydev, MII_88E1121_MISC_TEST);
17280b04680fSAndrew Lunn 	if (val < 0) {
17290b04680fSAndrew Lunn 		ret = val;
17300b04680fSAndrew Lunn 		goto error;
17310b04680fSAndrew Lunn 	}
17320b04680fSAndrew Lunn 
17330b04680fSAndrew Lunn 	/* Disable temperature sensor */
1734424ca4c5SRussell King 	ret = __phy_write(phydev, MII_88E1121_MISC_TEST,
17350b04680fSAndrew Lunn 			  ret & ~MII_88E1121_MISC_TEST_TEMP_SENSOR_EN);
17360b04680fSAndrew Lunn 	if (ret < 0)
17370b04680fSAndrew Lunn 		goto error;
17380b04680fSAndrew Lunn 
17390b04680fSAndrew Lunn 	*temp = ((val & MII_88E1121_MISC_TEST_TEMP_MASK) - 5) * 5000;
17400b04680fSAndrew Lunn 
17410b04680fSAndrew Lunn error:
1742424ca4c5SRussell King 	return phy_restore_page(phydev, oldpage, ret);
17430b04680fSAndrew Lunn }
17440b04680fSAndrew Lunn 
17450b04680fSAndrew Lunn static int m88e1121_hwmon_read(struct device *dev,
17460b04680fSAndrew Lunn 			       enum hwmon_sensor_types type,
17470b04680fSAndrew Lunn 			       u32 attr, int channel, long *temp)
17480b04680fSAndrew Lunn {
17490b04680fSAndrew Lunn 	struct phy_device *phydev = dev_get_drvdata(dev);
17500b04680fSAndrew Lunn 	int err;
17510b04680fSAndrew Lunn 
17520b04680fSAndrew Lunn 	switch (attr) {
17530b04680fSAndrew Lunn 	case hwmon_temp_input:
17540b04680fSAndrew Lunn 		err = m88e1121_get_temp(phydev, temp);
17550b04680fSAndrew Lunn 		break;
17560b04680fSAndrew Lunn 	default:
17570b04680fSAndrew Lunn 		return -EOPNOTSUPP;
17580b04680fSAndrew Lunn 	}
17590b04680fSAndrew Lunn 
17600b04680fSAndrew Lunn 	return err;
17610b04680fSAndrew Lunn }
17620b04680fSAndrew Lunn 
17630b04680fSAndrew Lunn static umode_t m88e1121_hwmon_is_visible(const void *data,
17640b04680fSAndrew Lunn 					 enum hwmon_sensor_types type,
17650b04680fSAndrew Lunn 					 u32 attr, int channel)
17660b04680fSAndrew Lunn {
17670b04680fSAndrew Lunn 	if (type != hwmon_temp)
17680b04680fSAndrew Lunn 		return 0;
17690b04680fSAndrew Lunn 
17700b04680fSAndrew Lunn 	switch (attr) {
17710b04680fSAndrew Lunn 	case hwmon_temp_input:
17720b04680fSAndrew Lunn 		return 0444;
17730b04680fSAndrew Lunn 	default:
17740b04680fSAndrew Lunn 		return 0;
17750b04680fSAndrew Lunn 	}
17760b04680fSAndrew Lunn }
17770b04680fSAndrew Lunn 
17780b04680fSAndrew Lunn static u32 m88e1121_hwmon_chip_config[] = {
17790b04680fSAndrew Lunn 	HWMON_C_REGISTER_TZ,
17800b04680fSAndrew Lunn 	0
17810b04680fSAndrew Lunn };
17820b04680fSAndrew Lunn 
17830b04680fSAndrew Lunn static const struct hwmon_channel_info m88e1121_hwmon_chip = {
17840b04680fSAndrew Lunn 	.type = hwmon_chip,
17850b04680fSAndrew Lunn 	.config = m88e1121_hwmon_chip_config,
17860b04680fSAndrew Lunn };
17870b04680fSAndrew Lunn 
17880b04680fSAndrew Lunn static u32 m88e1121_hwmon_temp_config[] = {
17890b04680fSAndrew Lunn 	HWMON_T_INPUT,
17900b04680fSAndrew Lunn 	0
17910b04680fSAndrew Lunn };
17920b04680fSAndrew Lunn 
17930b04680fSAndrew Lunn static const struct hwmon_channel_info m88e1121_hwmon_temp = {
17940b04680fSAndrew Lunn 	.type = hwmon_temp,
17950b04680fSAndrew Lunn 	.config = m88e1121_hwmon_temp_config,
17960b04680fSAndrew Lunn };
17970b04680fSAndrew Lunn 
17980b04680fSAndrew Lunn static const struct hwmon_channel_info *m88e1121_hwmon_info[] = {
17990b04680fSAndrew Lunn 	&m88e1121_hwmon_chip,
18000b04680fSAndrew Lunn 	&m88e1121_hwmon_temp,
18010b04680fSAndrew Lunn 	NULL
18020b04680fSAndrew Lunn };
18030b04680fSAndrew Lunn 
18040b04680fSAndrew Lunn static const struct hwmon_ops m88e1121_hwmon_hwmon_ops = {
18050b04680fSAndrew Lunn 	.is_visible = m88e1121_hwmon_is_visible,
18060b04680fSAndrew Lunn 	.read = m88e1121_hwmon_read,
18070b04680fSAndrew Lunn };
18080b04680fSAndrew Lunn 
18090b04680fSAndrew Lunn static const struct hwmon_chip_info m88e1121_hwmon_chip_info = {
18100b04680fSAndrew Lunn 	.ops = &m88e1121_hwmon_hwmon_ops,
18110b04680fSAndrew Lunn 	.info = m88e1121_hwmon_info,
18120b04680fSAndrew Lunn };
18130b04680fSAndrew Lunn 
18140b04680fSAndrew Lunn static int m88e1510_get_temp(struct phy_device *phydev, long *temp)
18150b04680fSAndrew Lunn {
18160b04680fSAndrew Lunn 	int ret;
18170b04680fSAndrew Lunn 
18180b04680fSAndrew Lunn 	*temp = 0;
18190b04680fSAndrew Lunn 
1820424ca4c5SRussell King 	ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
1821424ca4c5SRussell King 			     MII_88E1510_TEMP_SENSOR);
18220b04680fSAndrew Lunn 	if (ret < 0)
1823424ca4c5SRussell King 		return ret;
18240b04680fSAndrew Lunn 
18250b04680fSAndrew Lunn 	*temp = ((ret & MII_88E1510_TEMP_SENSOR_MASK) - 25) * 1000;
18260b04680fSAndrew Lunn 
1827424ca4c5SRussell King 	return 0;
18280b04680fSAndrew Lunn }
18290b04680fSAndrew Lunn 
1830f0a45816SColin Ian King static int m88e1510_get_temp_critical(struct phy_device *phydev, long *temp)
18310b04680fSAndrew Lunn {
18320b04680fSAndrew Lunn 	int ret;
18330b04680fSAndrew Lunn 
18340b04680fSAndrew Lunn 	*temp = 0;
18350b04680fSAndrew Lunn 
1836424ca4c5SRussell King 	ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
1837424ca4c5SRussell King 			     MII_88E1121_MISC_TEST);
18380b04680fSAndrew Lunn 	if (ret < 0)
1839424ca4c5SRussell King 		return ret;
18400b04680fSAndrew Lunn 
18410b04680fSAndrew Lunn 	*temp = (((ret & MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK) >>
18420b04680fSAndrew Lunn 		  MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT) * 5) - 25;
18430b04680fSAndrew Lunn 	/* convert to mC */
18440b04680fSAndrew Lunn 	*temp *= 1000;
18450b04680fSAndrew Lunn 
1846424ca4c5SRussell King 	return 0;
18470b04680fSAndrew Lunn }
18480b04680fSAndrew Lunn 
1849f0a45816SColin Ian King static int m88e1510_set_temp_critical(struct phy_device *phydev, long temp)
18500b04680fSAndrew Lunn {
18510b04680fSAndrew Lunn 	temp = temp / 1000;
18520b04680fSAndrew Lunn 	temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
18530b04680fSAndrew Lunn 
1854424ca4c5SRussell King 	return phy_modify_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
1855424ca4c5SRussell King 				MII_88E1121_MISC_TEST,
1856424ca4c5SRussell King 				MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK,
1857424ca4c5SRussell King 				temp << MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT);
18580b04680fSAndrew Lunn }
18590b04680fSAndrew Lunn 
1860f0a45816SColin Ian King static int m88e1510_get_temp_alarm(struct phy_device *phydev, long *alarm)
18610b04680fSAndrew Lunn {
18620b04680fSAndrew Lunn 	int ret;
18630b04680fSAndrew Lunn 
18640b04680fSAndrew Lunn 	*alarm = false;
18650b04680fSAndrew Lunn 
1866424ca4c5SRussell King 	ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
1867424ca4c5SRussell King 			     MII_88E1121_MISC_TEST);
18680b04680fSAndrew Lunn 	if (ret < 0)
1869424ca4c5SRussell King 		return ret;
1870424ca4c5SRussell King 
18710b04680fSAndrew Lunn 	*alarm = !!(ret & MII_88E1510_MISC_TEST_TEMP_IRQ);
18720b04680fSAndrew Lunn 
1873424ca4c5SRussell King 	return 0;
18740b04680fSAndrew Lunn }
18750b04680fSAndrew Lunn 
18760b04680fSAndrew Lunn static int m88e1510_hwmon_read(struct device *dev,
18770b04680fSAndrew Lunn 			       enum hwmon_sensor_types type,
18780b04680fSAndrew Lunn 			       u32 attr, int channel, long *temp)
18790b04680fSAndrew Lunn {
18800b04680fSAndrew Lunn 	struct phy_device *phydev = dev_get_drvdata(dev);
18810b04680fSAndrew Lunn 	int err;
18820b04680fSAndrew Lunn 
18830b04680fSAndrew Lunn 	switch (attr) {
18840b04680fSAndrew Lunn 	case hwmon_temp_input:
18850b04680fSAndrew Lunn 		err = m88e1510_get_temp(phydev, temp);
18860b04680fSAndrew Lunn 		break;
18870b04680fSAndrew Lunn 	case hwmon_temp_crit:
18880b04680fSAndrew Lunn 		err = m88e1510_get_temp_critical(phydev, temp);
18890b04680fSAndrew Lunn 		break;
18900b04680fSAndrew Lunn 	case hwmon_temp_max_alarm:
18910b04680fSAndrew Lunn 		err = m88e1510_get_temp_alarm(phydev, temp);
18920b04680fSAndrew Lunn 		break;
18930b04680fSAndrew Lunn 	default:
18940b04680fSAndrew Lunn 		return -EOPNOTSUPP;
18950b04680fSAndrew Lunn 	}
18960b04680fSAndrew Lunn 
18970b04680fSAndrew Lunn 	return err;
18980b04680fSAndrew Lunn }
18990b04680fSAndrew Lunn 
19000b04680fSAndrew Lunn static int m88e1510_hwmon_write(struct device *dev,
19010b04680fSAndrew Lunn 				enum hwmon_sensor_types type,
19020b04680fSAndrew Lunn 				u32 attr, int channel, long temp)
19030b04680fSAndrew Lunn {
19040b04680fSAndrew Lunn 	struct phy_device *phydev = dev_get_drvdata(dev);
19050b04680fSAndrew Lunn 	int err;
19060b04680fSAndrew Lunn 
19070b04680fSAndrew Lunn 	switch (attr) {
19080b04680fSAndrew Lunn 	case hwmon_temp_crit:
19090b04680fSAndrew Lunn 		err = m88e1510_set_temp_critical(phydev, temp);
19100b04680fSAndrew Lunn 		break;
19110b04680fSAndrew Lunn 	default:
19120b04680fSAndrew Lunn 		return -EOPNOTSUPP;
19130b04680fSAndrew Lunn 	}
19140b04680fSAndrew Lunn 	return err;
19150b04680fSAndrew Lunn }
19160b04680fSAndrew Lunn 
19170b04680fSAndrew Lunn static umode_t m88e1510_hwmon_is_visible(const void *data,
19180b04680fSAndrew Lunn 					 enum hwmon_sensor_types type,
19190b04680fSAndrew Lunn 					 u32 attr, int channel)
19200b04680fSAndrew Lunn {
19210b04680fSAndrew Lunn 	if (type != hwmon_temp)
19220b04680fSAndrew Lunn 		return 0;
19230b04680fSAndrew Lunn 
19240b04680fSAndrew Lunn 	switch (attr) {
19250b04680fSAndrew Lunn 	case hwmon_temp_input:
19260b04680fSAndrew Lunn 	case hwmon_temp_max_alarm:
19270b04680fSAndrew Lunn 		return 0444;
19280b04680fSAndrew Lunn 	case hwmon_temp_crit:
19290b04680fSAndrew Lunn 		return 0644;
19300b04680fSAndrew Lunn 	default:
19310b04680fSAndrew Lunn 		return 0;
19320b04680fSAndrew Lunn 	}
19330b04680fSAndrew Lunn }
19340b04680fSAndrew Lunn 
19350b04680fSAndrew Lunn static u32 m88e1510_hwmon_temp_config[] = {
19360b04680fSAndrew Lunn 	HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_MAX_ALARM,
19370b04680fSAndrew Lunn 	0
19380b04680fSAndrew Lunn };
19390b04680fSAndrew Lunn 
19400b04680fSAndrew Lunn static const struct hwmon_channel_info m88e1510_hwmon_temp = {
19410b04680fSAndrew Lunn 	.type = hwmon_temp,
19420b04680fSAndrew Lunn 	.config = m88e1510_hwmon_temp_config,
19430b04680fSAndrew Lunn };
19440b04680fSAndrew Lunn 
19450b04680fSAndrew Lunn static const struct hwmon_channel_info *m88e1510_hwmon_info[] = {
19460b04680fSAndrew Lunn 	&m88e1121_hwmon_chip,
19470b04680fSAndrew Lunn 	&m88e1510_hwmon_temp,
19480b04680fSAndrew Lunn 	NULL
19490b04680fSAndrew Lunn };
19500b04680fSAndrew Lunn 
19510b04680fSAndrew Lunn static const struct hwmon_ops m88e1510_hwmon_hwmon_ops = {
19520b04680fSAndrew Lunn 	.is_visible = m88e1510_hwmon_is_visible,
19530b04680fSAndrew Lunn 	.read = m88e1510_hwmon_read,
19540b04680fSAndrew Lunn 	.write = m88e1510_hwmon_write,
19550b04680fSAndrew Lunn };
19560b04680fSAndrew Lunn 
19570b04680fSAndrew Lunn static const struct hwmon_chip_info m88e1510_hwmon_chip_info = {
19580b04680fSAndrew Lunn 	.ops = &m88e1510_hwmon_hwmon_ops,
19590b04680fSAndrew Lunn 	.info = m88e1510_hwmon_info,
19600b04680fSAndrew Lunn };
19610b04680fSAndrew Lunn 
1962fee2d546SAndrew Lunn static int m88e6390_get_temp(struct phy_device *phydev, long *temp)
1963fee2d546SAndrew Lunn {
1964fee2d546SAndrew Lunn 	int sum = 0;
1965fee2d546SAndrew Lunn 	int oldpage;
1966fee2d546SAndrew Lunn 	int ret = 0;
1967fee2d546SAndrew Lunn 	int i;
1968fee2d546SAndrew Lunn 
1969fee2d546SAndrew Lunn 	*temp = 0;
1970fee2d546SAndrew Lunn 
1971fee2d546SAndrew Lunn 	oldpage = phy_select_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
1972fee2d546SAndrew Lunn 	if (oldpage < 0)
1973fee2d546SAndrew Lunn 		goto error;
1974fee2d546SAndrew Lunn 
1975fee2d546SAndrew Lunn 	/* Enable temperature sensor */
1976fee2d546SAndrew Lunn 	ret = __phy_read(phydev, MII_88E6390_MISC_TEST);
1977fee2d546SAndrew Lunn 	if (ret < 0)
1978fee2d546SAndrew Lunn 		goto error;
1979fee2d546SAndrew Lunn 
1980fee2d546SAndrew Lunn 	ret = ret & ~MII_88E6390_MISC_TEST_SAMPLE_MASK;
1981fee2d546SAndrew Lunn 	ret |= MII_88E6390_MISC_TEST_SAMPLE_ENABLE |
1982fee2d546SAndrew Lunn 		MII_88E6390_MISC_TEST_SAMPLE_1S;
1983fee2d546SAndrew Lunn 
1984fee2d546SAndrew Lunn 	ret = __phy_write(phydev, MII_88E6390_MISC_TEST, ret);
1985fee2d546SAndrew Lunn 	if (ret < 0)
1986fee2d546SAndrew Lunn 		goto error;
1987fee2d546SAndrew Lunn 
1988fee2d546SAndrew Lunn 	/* Wait for temperature to stabilize */
1989fee2d546SAndrew Lunn 	usleep_range(10000, 12000);
1990fee2d546SAndrew Lunn 
1991fee2d546SAndrew Lunn 	/* Reading the temperature sense has an errata. You need to read
1992fee2d546SAndrew Lunn 	 * a number of times and take an average.
1993fee2d546SAndrew Lunn 	 */
1994fee2d546SAndrew Lunn 	for (i = 0; i < MII_88E6390_TEMP_SENSOR_SAMPLES; i++) {
1995fee2d546SAndrew Lunn 		ret = __phy_read(phydev, MII_88E6390_TEMP_SENSOR);
1996fee2d546SAndrew Lunn 		if (ret < 0)
1997fee2d546SAndrew Lunn 			goto error;
1998fee2d546SAndrew Lunn 		sum += ret & MII_88E6390_TEMP_SENSOR_MASK;
1999fee2d546SAndrew Lunn 	}
2000fee2d546SAndrew Lunn 
2001fee2d546SAndrew Lunn 	sum /= MII_88E6390_TEMP_SENSOR_SAMPLES;
2002fee2d546SAndrew Lunn 	*temp = (sum  - 75) * 1000;
2003fee2d546SAndrew Lunn 
2004fee2d546SAndrew Lunn 	/* Disable temperature sensor */
2005fee2d546SAndrew Lunn 	ret = __phy_read(phydev, MII_88E6390_MISC_TEST);
2006fee2d546SAndrew Lunn 	if (ret < 0)
2007fee2d546SAndrew Lunn 		goto error;
2008fee2d546SAndrew Lunn 
2009fee2d546SAndrew Lunn 	ret = ret & ~MII_88E6390_MISC_TEST_SAMPLE_MASK;
2010fee2d546SAndrew Lunn 	ret |= MII_88E6390_MISC_TEST_SAMPLE_DISABLE;
2011fee2d546SAndrew Lunn 
2012fee2d546SAndrew Lunn 	ret = __phy_write(phydev, MII_88E6390_MISC_TEST, ret);
2013fee2d546SAndrew Lunn 
2014fee2d546SAndrew Lunn error:
2015fee2d546SAndrew Lunn 	phy_restore_page(phydev, oldpage, ret);
2016fee2d546SAndrew Lunn 
2017fee2d546SAndrew Lunn 	return ret;
2018fee2d546SAndrew Lunn }
2019fee2d546SAndrew Lunn 
2020fee2d546SAndrew Lunn static int m88e6390_hwmon_read(struct device *dev,
2021fee2d546SAndrew Lunn 			       enum hwmon_sensor_types type,
2022fee2d546SAndrew Lunn 			       u32 attr, int channel, long *temp)
2023fee2d546SAndrew Lunn {
2024fee2d546SAndrew Lunn 	struct phy_device *phydev = dev_get_drvdata(dev);
2025fee2d546SAndrew Lunn 	int err;
2026fee2d546SAndrew Lunn 
2027fee2d546SAndrew Lunn 	switch (attr) {
2028fee2d546SAndrew Lunn 	case hwmon_temp_input:
2029fee2d546SAndrew Lunn 		err = m88e6390_get_temp(phydev, temp);
2030fee2d546SAndrew Lunn 		break;
2031fee2d546SAndrew Lunn 	default:
2032fee2d546SAndrew Lunn 		return -EOPNOTSUPP;
2033fee2d546SAndrew Lunn 	}
2034fee2d546SAndrew Lunn 
2035fee2d546SAndrew Lunn 	return err;
2036fee2d546SAndrew Lunn }
2037fee2d546SAndrew Lunn 
2038fee2d546SAndrew Lunn static umode_t m88e6390_hwmon_is_visible(const void *data,
2039fee2d546SAndrew Lunn 					 enum hwmon_sensor_types type,
2040fee2d546SAndrew Lunn 					 u32 attr, int channel)
2041fee2d546SAndrew Lunn {
2042fee2d546SAndrew Lunn 	if (type != hwmon_temp)
2043fee2d546SAndrew Lunn 		return 0;
2044fee2d546SAndrew Lunn 
2045fee2d546SAndrew Lunn 	switch (attr) {
2046fee2d546SAndrew Lunn 	case hwmon_temp_input:
2047fee2d546SAndrew Lunn 		return 0444;
2048fee2d546SAndrew Lunn 	default:
2049fee2d546SAndrew Lunn 		return 0;
2050fee2d546SAndrew Lunn 	}
2051fee2d546SAndrew Lunn }
2052fee2d546SAndrew Lunn 
2053fee2d546SAndrew Lunn static u32 m88e6390_hwmon_temp_config[] = {
2054fee2d546SAndrew Lunn 	HWMON_T_INPUT,
2055fee2d546SAndrew Lunn 	0
2056fee2d546SAndrew Lunn };
2057fee2d546SAndrew Lunn 
2058fee2d546SAndrew Lunn static const struct hwmon_channel_info m88e6390_hwmon_temp = {
2059fee2d546SAndrew Lunn 	.type = hwmon_temp,
2060fee2d546SAndrew Lunn 	.config = m88e6390_hwmon_temp_config,
2061fee2d546SAndrew Lunn };
2062fee2d546SAndrew Lunn 
2063fee2d546SAndrew Lunn static const struct hwmon_channel_info *m88e6390_hwmon_info[] = {
2064fee2d546SAndrew Lunn 	&m88e1121_hwmon_chip,
2065fee2d546SAndrew Lunn 	&m88e6390_hwmon_temp,
2066fee2d546SAndrew Lunn 	NULL
2067fee2d546SAndrew Lunn };
2068fee2d546SAndrew Lunn 
2069fee2d546SAndrew Lunn static const struct hwmon_ops m88e6390_hwmon_hwmon_ops = {
2070fee2d546SAndrew Lunn 	.is_visible = m88e6390_hwmon_is_visible,
2071fee2d546SAndrew Lunn 	.read = m88e6390_hwmon_read,
2072fee2d546SAndrew Lunn };
2073fee2d546SAndrew Lunn 
2074fee2d546SAndrew Lunn static const struct hwmon_chip_info m88e6390_hwmon_chip_info = {
2075fee2d546SAndrew Lunn 	.ops = &m88e6390_hwmon_hwmon_ops,
2076fee2d546SAndrew Lunn 	.info = m88e6390_hwmon_info,
2077fee2d546SAndrew Lunn };
2078fee2d546SAndrew Lunn 
20790b04680fSAndrew Lunn static int marvell_hwmon_name(struct phy_device *phydev)
20800b04680fSAndrew Lunn {
20810b04680fSAndrew Lunn 	struct marvell_priv *priv = phydev->priv;
20820b04680fSAndrew Lunn 	struct device *dev = &phydev->mdio.dev;
20830b04680fSAndrew Lunn 	const char *devname = dev_name(dev);
20840b04680fSAndrew Lunn 	size_t len = strlen(devname);
20850b04680fSAndrew Lunn 	int i, j;
20860b04680fSAndrew Lunn 
20870b04680fSAndrew Lunn 	priv->hwmon_name = devm_kzalloc(dev, len, GFP_KERNEL);
20880b04680fSAndrew Lunn 	if (!priv->hwmon_name)
20890b04680fSAndrew Lunn 		return -ENOMEM;
20900b04680fSAndrew Lunn 
20910b04680fSAndrew Lunn 	for (i = j = 0; i < len && devname[i]; i++) {
20920b04680fSAndrew Lunn 		if (isalnum(devname[i]))
20930b04680fSAndrew Lunn 			priv->hwmon_name[j++] = devname[i];
20940b04680fSAndrew Lunn 	}
20950b04680fSAndrew Lunn 
20960b04680fSAndrew Lunn 	return 0;
20970b04680fSAndrew Lunn }
20980b04680fSAndrew Lunn 
20990b04680fSAndrew Lunn static int marvell_hwmon_probe(struct phy_device *phydev,
21000b04680fSAndrew Lunn 			       const struct hwmon_chip_info *chip)
21010b04680fSAndrew Lunn {
21020b04680fSAndrew Lunn 	struct marvell_priv *priv = phydev->priv;
21030b04680fSAndrew Lunn 	struct device *dev = &phydev->mdio.dev;
21040b04680fSAndrew Lunn 	int err;
21050b04680fSAndrew Lunn 
21060b04680fSAndrew Lunn 	err = marvell_hwmon_name(phydev);
21070b04680fSAndrew Lunn 	if (err)
21080b04680fSAndrew Lunn 		return err;
21090b04680fSAndrew Lunn 
21100b04680fSAndrew Lunn 	priv->hwmon_dev = devm_hwmon_device_register_with_info(
21110b04680fSAndrew Lunn 		dev, priv->hwmon_name, phydev, chip, NULL);
21120b04680fSAndrew Lunn 
21130b04680fSAndrew Lunn 	return PTR_ERR_OR_ZERO(priv->hwmon_dev);
21140b04680fSAndrew Lunn }
21150b04680fSAndrew Lunn 
21160b04680fSAndrew Lunn static int m88e1121_hwmon_probe(struct phy_device *phydev)
21170b04680fSAndrew Lunn {
21180b04680fSAndrew Lunn 	return marvell_hwmon_probe(phydev, &m88e1121_hwmon_chip_info);
21190b04680fSAndrew Lunn }
21200b04680fSAndrew Lunn 
21210b04680fSAndrew Lunn static int m88e1510_hwmon_probe(struct phy_device *phydev)
21220b04680fSAndrew Lunn {
21230b04680fSAndrew Lunn 	return marvell_hwmon_probe(phydev, &m88e1510_hwmon_chip_info);
21240b04680fSAndrew Lunn }
2125fee2d546SAndrew Lunn 
2126fee2d546SAndrew Lunn static int m88e6390_hwmon_probe(struct phy_device *phydev)
2127fee2d546SAndrew Lunn {
2128fee2d546SAndrew Lunn 	return marvell_hwmon_probe(phydev, &m88e6390_hwmon_chip_info);
2129fee2d546SAndrew Lunn }
21300b04680fSAndrew Lunn #else
21310b04680fSAndrew Lunn static int m88e1121_hwmon_probe(struct phy_device *phydev)
21320b04680fSAndrew Lunn {
21330b04680fSAndrew Lunn 	return 0;
21340b04680fSAndrew Lunn }
21350b04680fSAndrew Lunn 
21360b04680fSAndrew Lunn static int m88e1510_hwmon_probe(struct phy_device *phydev)
21370b04680fSAndrew Lunn {
21380b04680fSAndrew Lunn 	return 0;
21390b04680fSAndrew Lunn }
2140fee2d546SAndrew Lunn 
2141fee2d546SAndrew Lunn static int m88e6390_hwmon_probe(struct phy_device *phydev)
2142fee2d546SAndrew Lunn {
2143fee2d546SAndrew Lunn 	return 0;
2144fee2d546SAndrew Lunn }
21450b04680fSAndrew Lunn #endif
21460b04680fSAndrew Lunn 
2147d2fa47d9SAndrew Lunn static int marvell_probe(struct phy_device *phydev)
2148d2fa47d9SAndrew Lunn {
2149d2fa47d9SAndrew Lunn 	struct marvell_priv *priv;
2150d2fa47d9SAndrew Lunn 
2151e5a03bfdSAndrew Lunn 	priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
2152d2fa47d9SAndrew Lunn 	if (!priv)
2153d2fa47d9SAndrew Lunn 		return -ENOMEM;
2154d2fa47d9SAndrew Lunn 
2155d2fa47d9SAndrew Lunn 	phydev->priv = priv;
2156d2fa47d9SAndrew Lunn 
2157d2fa47d9SAndrew Lunn 	return 0;
2158d2fa47d9SAndrew Lunn }
2159d2fa47d9SAndrew Lunn 
21600b04680fSAndrew Lunn static int m88e1121_probe(struct phy_device *phydev)
21610b04680fSAndrew Lunn {
21620b04680fSAndrew Lunn 	int err;
21630b04680fSAndrew Lunn 
21640b04680fSAndrew Lunn 	err = marvell_probe(phydev);
21650b04680fSAndrew Lunn 	if (err)
21660b04680fSAndrew Lunn 		return err;
21670b04680fSAndrew Lunn 
21680b04680fSAndrew Lunn 	return m88e1121_hwmon_probe(phydev);
21690b04680fSAndrew Lunn }
21700b04680fSAndrew Lunn 
21710b04680fSAndrew Lunn static int m88e1510_probe(struct phy_device *phydev)
21720b04680fSAndrew Lunn {
21730b04680fSAndrew Lunn 	int err;
21740b04680fSAndrew Lunn 
21750b04680fSAndrew Lunn 	err = marvell_probe(phydev);
21760b04680fSAndrew Lunn 	if (err)
21770b04680fSAndrew Lunn 		return err;
21780b04680fSAndrew Lunn 
21790b04680fSAndrew Lunn 	return m88e1510_hwmon_probe(phydev);
21800b04680fSAndrew Lunn }
21810b04680fSAndrew Lunn 
2182fee2d546SAndrew Lunn static int m88e6390_probe(struct phy_device *phydev)
2183fee2d546SAndrew Lunn {
2184fee2d546SAndrew Lunn 	int err;
2185fee2d546SAndrew Lunn 
2186fee2d546SAndrew Lunn 	err = marvell_probe(phydev);
2187fee2d546SAndrew Lunn 	if (err)
2188fee2d546SAndrew Lunn 		return err;
2189fee2d546SAndrew Lunn 
2190fee2d546SAndrew Lunn 	return m88e6390_hwmon_probe(phydev);
2191fee2d546SAndrew Lunn }
2192fee2d546SAndrew Lunn 
2193e5479239SOlof Johansson static struct phy_driver marvell_drivers[] = {
2194e5479239SOlof Johansson 	{
21952f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1101,
21962f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
219700db8189SAndy Fleming 		.name = "Marvell 88E1101",
2198dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
219918702414SArnd Bergmann 		.probe = marvell_probe,
220079be1a1cSClemens Gruber 		.config_init = &marvell_config_init,
2201f2899788SAndrew Lunn 		.config_aneg = &m88e1101_config_aneg,
220200db8189SAndy Fleming 		.ack_interrupt = &marvell_ack_interrupt,
220300db8189SAndy Fleming 		.config_intr = &marvell_config_intr,
22040898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
22050898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2206424ca4c5SRussell King 		.read_page = marvell_read_page,
2207424ca4c5SRussell King 		.write_page = marvell_write_page,
2208d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2209d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2210d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2211e5479239SOlof Johansson 	},
2212e5479239SOlof Johansson 	{
22132f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1112,
22142f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
221585cfb534SOlof Johansson 		.name = "Marvell 88E1112",
2216dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2217d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
221885cfb534SOlof Johansson 		.config_init = &m88e1111_config_init,
221985cfb534SOlof Johansson 		.config_aneg = &marvell_config_aneg,
222085cfb534SOlof Johansson 		.ack_interrupt = &marvell_ack_interrupt,
222185cfb534SOlof Johansson 		.config_intr = &marvell_config_intr,
22220898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
22230898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2224424ca4c5SRussell King 		.read_page = marvell_read_page,
2225424ca4c5SRussell King 		.write_page = marvell_write_page,
2226d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2227d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2228d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
222985cfb534SOlof Johansson 	},
223085cfb534SOlof Johansson 	{
22312f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1111,
22322f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
223376884679SAndy Fleming 		.name = "Marvell 88E1111",
2234dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2235d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
2236e5479239SOlof Johansson 		.config_init = &m88e1111_config_init,
2237d6ab9336SFlorian Fainelli 		.config_aneg = &marvell_config_aneg,
2238be937f1fSAlexandr Smirnov 		.read_status = &marvell_read_status,
223976884679SAndy Fleming 		.ack_interrupt = &marvell_ack_interrupt,
224076884679SAndy Fleming 		.config_intr = &marvell_config_intr,
22410898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
22420898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2243424ca4c5SRussell King 		.read_page = marvell_read_page,
2244424ca4c5SRussell King 		.write_page = marvell_write_page,
2245d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2246d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2247d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2248e5479239SOlof Johansson 	},
2249e5479239SOlof Johansson 	{
22502f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1118,
22512f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2252605f196eSRon Madrid 		.name = "Marvell 88E1118",
2253dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2254d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
2255605f196eSRon Madrid 		.config_init = &m88e1118_config_init,
2256605f196eSRon Madrid 		.config_aneg = &m88e1118_config_aneg,
2257605f196eSRon Madrid 		.ack_interrupt = &marvell_ack_interrupt,
2258605f196eSRon Madrid 		.config_intr = &marvell_config_intr,
22590898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
22600898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2261424ca4c5SRussell King 		.read_page = marvell_read_page,
2262424ca4c5SRussell King 		.write_page = marvell_write_page,
2263d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2264d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2265d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2266605f196eSRon Madrid 	},
2267605f196eSRon Madrid 	{
22682f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1121R,
22692f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2270140bc929SSergei Poselenov 		.name = "Marvell 88E1121R",
2271dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
227218702414SArnd Bergmann 		.probe = &m88e1121_probe,
227307777246SWang Dongsheng 		.config_init = &marvell_config_init,
2274140bc929SSergei Poselenov 		.config_aneg = &m88e1121_config_aneg,
2275140bc929SSergei Poselenov 		.read_status = &marvell_read_status,
2276140bc929SSergei Poselenov 		.ack_interrupt = &marvell_ack_interrupt,
2277140bc929SSergei Poselenov 		.config_intr = &marvell_config_intr,
2278dcd07be3SAnatolij Gustschin 		.did_interrupt = &m88e1121_did_interrupt,
22790898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
22800898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2281424ca4c5SRussell King 		.read_page = marvell_read_page,
2282424ca4c5SRussell King 		.write_page = marvell_write_page,
2283d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2284d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2285d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2286911af5e1SHeiner Kallweit 		.get_tunable = m88e1011_get_tunable,
2287911af5e1SHeiner Kallweit 		.set_tunable = m88e1011_set_tunable,
2288911af5e1SHeiner Kallweit 		.link_change_notify = m88e1011_link_change_notify,
2289140bc929SSergei Poselenov 	},
2290140bc929SSergei Poselenov 	{
2291337ac9d5SCyril Chemparathy 		.phy_id = MARVELL_PHY_ID_88E1318S,
22926ba74014SLinus Torvalds 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2293337ac9d5SCyril Chemparathy 		.name = "Marvell 88E1318S",
2294dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2295d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
2296dd9a122aSEsben Haabendal 		.config_init = &m88e1318_config_init,
2297337ac9d5SCyril Chemparathy 		.config_aneg = &m88e1318_config_aneg,
22983ff1c259SCyril Chemparathy 		.read_status = &marvell_read_status,
22993ff1c259SCyril Chemparathy 		.ack_interrupt = &marvell_ack_interrupt,
23003ff1c259SCyril Chemparathy 		.config_intr = &marvell_config_intr,
23013ff1c259SCyril Chemparathy 		.did_interrupt = &m88e1121_did_interrupt,
23023871c387SMichael Stapelberg 		.get_wol = &m88e1318_get_wol,
23033871c387SMichael Stapelberg 		.set_wol = &m88e1318_set_wol,
23040898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
23050898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2306424ca4c5SRussell King 		.read_page = marvell_read_page,
2307424ca4c5SRussell King 		.write_page = marvell_write_page,
2308d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2309d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2310d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
23113ff1c259SCyril Chemparathy 	},
23123ff1c259SCyril Chemparathy 	{
23132f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1145,
23142f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
231576884679SAndy Fleming 		.name = "Marvell 88E1145",
2316dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2317d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
231876884679SAndy Fleming 		.config_init = &m88e1145_config_init,
2319c505873eSZhao Qiang 		.config_aneg = &m88e1101_config_aneg,
232076884679SAndy Fleming 		.read_status = &genphy_read_status,
232176884679SAndy Fleming 		.ack_interrupt = &marvell_ack_interrupt,
232276884679SAndy Fleming 		.config_intr = &marvell_config_intr,
23230898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
23240898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2325424ca4c5SRussell King 		.read_page = marvell_read_page,
2326424ca4c5SRussell King 		.write_page = marvell_write_page,
2327d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2328d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2329d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2330ac8c635aSOlof Johansson 	},
2331ac8c635aSOlof Johansson 	{
233290600732SDavid Daney 		.phy_id = MARVELL_PHY_ID_88E1149R,
233390600732SDavid Daney 		.phy_id_mask = MARVELL_PHY_ID_MASK,
233490600732SDavid Daney 		.name = "Marvell 88E1149R",
2335dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2336d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
233790600732SDavid Daney 		.config_init = &m88e1149_config_init,
233890600732SDavid Daney 		.config_aneg = &m88e1118_config_aneg,
233990600732SDavid Daney 		.ack_interrupt = &marvell_ack_interrupt,
234090600732SDavid Daney 		.config_intr = &marvell_config_intr,
23410898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
23420898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2343424ca4c5SRussell King 		.read_page = marvell_read_page,
2344424ca4c5SRussell King 		.write_page = marvell_write_page,
2345d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2346d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2347d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
234890600732SDavid Daney 	},
234990600732SDavid Daney 	{
23502f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1240,
23512f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2352ac8c635aSOlof Johansson 		.name = "Marvell 88E1240",
2353dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2354d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
2355ac8c635aSOlof Johansson 		.config_init = &m88e1111_config_init,
2356ac8c635aSOlof Johansson 		.config_aneg = &marvell_config_aneg,
2357ac8c635aSOlof Johansson 		.ack_interrupt = &marvell_ack_interrupt,
2358ac8c635aSOlof Johansson 		.config_intr = &marvell_config_intr,
23590898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
23600898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2361424ca4c5SRussell King 		.read_page = marvell_read_page,
2362424ca4c5SRussell King 		.write_page = marvell_write_page,
2363d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2364d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2365d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2366ac8c635aSOlof Johansson 	},
23673da09a51SMichal Simek 	{
23683da09a51SMichal Simek 		.phy_id = MARVELL_PHY_ID_88E1116R,
23693da09a51SMichal Simek 		.phy_id_mask = MARVELL_PHY_ID_MASK,
23703da09a51SMichal Simek 		.name = "Marvell 88E1116R",
2371dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2372d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
23733da09a51SMichal Simek 		.config_init = &m88e1116r_config_init,
23743da09a51SMichal Simek 		.ack_interrupt = &marvell_ack_interrupt,
23753da09a51SMichal Simek 		.config_intr = &marvell_config_intr,
23760898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
23770898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2378424ca4c5SRussell King 		.read_page = marvell_read_page,
2379424ca4c5SRussell King 		.write_page = marvell_write_page,
2380d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2381d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2382d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
23833da09a51SMichal Simek 	},
238410e24caaSMichal Simek 	{
238510e24caaSMichal Simek 		.phy_id = MARVELL_PHY_ID_88E1510,
238610e24caaSMichal Simek 		.phy_id_mask = MARVELL_PHY_ID_MASK,
238710e24caaSMichal Simek 		.name = "Marvell 88E1510",
2388719655a1SAndrew Lunn 		.features = PHY_GBIT_FIBRE_FEATURES,
23890b04680fSAndrew Lunn 		.probe = &m88e1510_probe,
2390930b37eeSStefan Roese 		.config_init = &m88e1510_config_init,
239110e24caaSMichal Simek 		.config_aneg = &m88e1510_config_aneg,
239210e24caaSMichal Simek 		.read_status = &marvell_read_status,
239310e24caaSMichal Simek 		.ack_interrupt = &marvell_ack_interrupt,
239410e24caaSMichal Simek 		.config_intr = &marvell_config_intr,
239510e24caaSMichal Simek 		.did_interrupt = &m88e1121_did_interrupt,
2396f39aac7eSJingju Hou 		.get_wol = &m88e1318_get_wol,
2397f39aac7eSJingju Hou 		.set_wol = &m88e1318_set_wol,
23983758be3dSCharles-Antoine Couret 		.resume = &marvell_resume,
23993758be3dSCharles-Antoine Couret 		.suspend = &marvell_suspend,
2400424ca4c5SRussell King 		.read_page = marvell_read_page,
2401424ca4c5SRussell King 		.write_page = marvell_write_page,
2402d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2403d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2404d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2405f0f9b4edSLin Yun Sheng 		.set_loopback = genphy_loopback,
240610e24caaSMichal Simek 	},
24076b358aedSSebastian Hesselbarth 	{
2408819ec8e1SAndrew Lunn 		.phy_id = MARVELL_PHY_ID_88E1540,
2409819ec8e1SAndrew Lunn 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2410819ec8e1SAndrew Lunn 		.name = "Marvell 88E1540",
2411dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
241218702414SArnd Bergmann 		.probe = m88e1510_probe,
241379be1a1cSClemens Gruber 		.config_init = &marvell_config_init,
2414819ec8e1SAndrew Lunn 		.config_aneg = &m88e1510_config_aneg,
2415819ec8e1SAndrew Lunn 		.read_status = &marvell_read_status,
2416819ec8e1SAndrew Lunn 		.ack_interrupt = &marvell_ack_interrupt,
2417819ec8e1SAndrew Lunn 		.config_intr = &marvell_config_intr,
2418819ec8e1SAndrew Lunn 		.did_interrupt = &m88e1121_did_interrupt,
2419819ec8e1SAndrew Lunn 		.resume = &genphy_resume,
2420819ec8e1SAndrew Lunn 		.suspend = &genphy_suspend,
2421424ca4c5SRussell King 		.read_page = marvell_read_page,
2422424ca4c5SRussell King 		.write_page = marvell_write_page,
2423d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2424d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2425d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
242669f42be8SHeiner Kallweit 		.get_tunable = m88e1540_get_tunable,
242769f42be8SHeiner Kallweit 		.set_tunable = m88e1540_set_tunable,
2428911af5e1SHeiner Kallweit 		.link_change_notify = m88e1011_link_change_notify,
2429819ec8e1SAndrew Lunn 	},
2430819ec8e1SAndrew Lunn 	{
243160f06fdeSAndrew Lunn 		.phy_id = MARVELL_PHY_ID_88E1545,
243260f06fdeSAndrew Lunn 		.phy_id_mask = MARVELL_PHY_ID_MASK,
243360f06fdeSAndrew Lunn 		.name = "Marvell 88E1545",
243460f06fdeSAndrew Lunn 		.probe = m88e1510_probe,
2435dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
243660f06fdeSAndrew Lunn 		.config_init = &marvell_config_init,
243760f06fdeSAndrew Lunn 		.config_aneg = &m88e1510_config_aneg,
243860f06fdeSAndrew Lunn 		.read_status = &marvell_read_status,
243960f06fdeSAndrew Lunn 		.ack_interrupt = &marvell_ack_interrupt,
244060f06fdeSAndrew Lunn 		.config_intr = &marvell_config_intr,
244160f06fdeSAndrew Lunn 		.did_interrupt = &m88e1121_did_interrupt,
244260f06fdeSAndrew Lunn 		.resume = &genphy_resume,
244360f06fdeSAndrew Lunn 		.suspend = &genphy_suspend,
2444424ca4c5SRussell King 		.read_page = marvell_read_page,
2445424ca4c5SRussell King 		.write_page = marvell_write_page,
244660f06fdeSAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
244760f06fdeSAndrew Lunn 		.get_strings = marvell_get_strings,
244860f06fdeSAndrew Lunn 		.get_stats = marvell_get_stats,
244960f06fdeSAndrew Lunn 	},
245060f06fdeSAndrew Lunn 	{
24516b358aedSSebastian Hesselbarth 		.phy_id = MARVELL_PHY_ID_88E3016,
24526b358aedSSebastian Hesselbarth 		.phy_id_mask = MARVELL_PHY_ID_MASK,
24536b358aedSSebastian Hesselbarth 		.name = "Marvell 88E3016",
2454dcdecdcfSHeiner Kallweit 		/* PHY_BASIC_FEATURES */
2455d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
24566b358aedSSebastian Hesselbarth 		.config_init = &m88e3016_config_init,
24576b358aedSSebastian Hesselbarth 		.aneg_done = &marvell_aneg_done,
24586b358aedSSebastian Hesselbarth 		.read_status = &marvell_read_status,
24596b358aedSSebastian Hesselbarth 		.ack_interrupt = &marvell_ack_interrupt,
24606b358aedSSebastian Hesselbarth 		.config_intr = &marvell_config_intr,
24616b358aedSSebastian Hesselbarth 		.did_interrupt = &m88e1121_did_interrupt,
24626b358aedSSebastian Hesselbarth 		.resume = &genphy_resume,
24636b358aedSSebastian Hesselbarth 		.suspend = &genphy_suspend,
2464424ca4c5SRussell King 		.read_page = marvell_read_page,
2465424ca4c5SRussell King 		.write_page = marvell_write_page,
2466d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2467d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2468d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
24696b358aedSSebastian Hesselbarth 	},
2470e4cf8a38SAndrew Lunn 	{
2471e4cf8a38SAndrew Lunn 		.phy_id = MARVELL_PHY_ID_88E6390,
2472e4cf8a38SAndrew Lunn 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2473e4cf8a38SAndrew Lunn 		.name = "Marvell 88E6390",
2474dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2475fee2d546SAndrew Lunn 		.probe = m88e6390_probe,
2476e4cf8a38SAndrew Lunn 		.config_init = &marvell_config_init,
24778cbcdc1aSAndrew Lunn 		.config_aneg = &m88e6390_config_aneg,
2478e4cf8a38SAndrew Lunn 		.read_status = &marvell_read_status,
2479e4cf8a38SAndrew Lunn 		.ack_interrupt = &marvell_ack_interrupt,
2480e4cf8a38SAndrew Lunn 		.config_intr = &marvell_config_intr,
2481e4cf8a38SAndrew Lunn 		.did_interrupt = &m88e1121_did_interrupt,
2482e4cf8a38SAndrew Lunn 		.resume = &genphy_resume,
2483e4cf8a38SAndrew Lunn 		.suspend = &genphy_suspend,
2484424ca4c5SRussell King 		.read_page = marvell_read_page,
2485424ca4c5SRussell King 		.write_page = marvell_write_page,
2486e4cf8a38SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2487e4cf8a38SAndrew Lunn 		.get_strings = marvell_get_strings,
2488e4cf8a38SAndrew Lunn 		.get_stats = marvell_get_stats,
248969f42be8SHeiner Kallweit 		.get_tunable = m88e1540_get_tunable,
249069f42be8SHeiner Kallweit 		.set_tunable = m88e1540_set_tunable,
2491911af5e1SHeiner Kallweit 		.link_change_notify = m88e1011_link_change_notify,
2492e4cf8a38SAndrew Lunn 	},
249376884679SAndy Fleming };
249476884679SAndy Fleming 
249550fd7150SJohan Hovold module_phy_driver(marvell_drivers);
24964e4f10f6SDavid Woodhouse 
2497cf93c945SUwe Kleine-König static struct mdio_device_id __maybe_unused marvell_tbl[] = {
2498f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1101, MARVELL_PHY_ID_MASK },
2499f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1112, MARVELL_PHY_ID_MASK },
2500f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1111, MARVELL_PHY_ID_MASK },
2501f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1118, MARVELL_PHY_ID_MASK },
2502f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1121R, MARVELL_PHY_ID_MASK },
2503f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1145, MARVELL_PHY_ID_MASK },
2504f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1149R, MARVELL_PHY_ID_MASK },
2505f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1240, MARVELL_PHY_ID_MASK },
2506f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1318S, MARVELL_PHY_ID_MASK },
25073da09a51SMichal Simek 	{ MARVELL_PHY_ID_88E1116R, MARVELL_PHY_ID_MASK },
250810e24caaSMichal Simek 	{ MARVELL_PHY_ID_88E1510, MARVELL_PHY_ID_MASK },
2509819ec8e1SAndrew Lunn 	{ MARVELL_PHY_ID_88E1540, MARVELL_PHY_ID_MASK },
251060f06fdeSAndrew Lunn 	{ MARVELL_PHY_ID_88E1545, MARVELL_PHY_ID_MASK },
25116b358aedSSebastian Hesselbarth 	{ MARVELL_PHY_ID_88E3016, MARVELL_PHY_ID_MASK },
2512e4cf8a38SAndrew Lunn 	{ MARVELL_PHY_ID_88E6390, MARVELL_PHY_ID_MASK },
25134e4f10f6SDavid Woodhouse 	{ }
25144e4f10f6SDavid Woodhouse };
25154e4f10f6SDavid Woodhouse 
25164e4f10f6SDavid Woodhouse MODULE_DEVICE_TABLE(mdio, marvell_tbl);
2517