xref: /openbmc/linux/drivers/net/phy/marvell.c (revision 06b334f0)
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>
30fc879f72SAndrew Lunn #include <linux/ethtool_netlink.h>
3100db8189SAndy Fleming #include <linux/phy.h>
322f495c39SBenjamin Herrenschmidt #include <linux/marvell_phy.h>
3369f42be8SHeiner Kallweit #include <linux/bitfield.h>
34cf41a51dSDavid Daney #include <linux/of.h>
3500db8189SAndy Fleming 
36eea3b201SAvinash Kumar #include <linux/io.h>
3700db8189SAndy Fleming #include <asm/irq.h>
38eea3b201SAvinash Kumar #include <linux/uaccess.h>
3900db8189SAndy Fleming 
4027d916d6SDavid Daney #define MII_MARVELL_PHY_PAGE		22
4152295666SAndrew Lunn #define MII_MARVELL_COPPER_PAGE		0x00
4252295666SAndrew Lunn #define MII_MARVELL_FIBER_PAGE		0x01
4352295666SAndrew Lunn #define MII_MARVELL_MSCR_PAGE		0x02
4452295666SAndrew Lunn #define MII_MARVELL_LED_PAGE		0x03
450c9bcc1dSAndrew Lunn #define MII_MARVELL_VCT5_PAGE		0x05
4652295666SAndrew Lunn #define MII_MARVELL_MISC_TEST_PAGE	0x06
47fc879f72SAndrew Lunn #define MII_MARVELL_VCT7_PAGE		0x07
4852295666SAndrew Lunn #define MII_MARVELL_WOL_PAGE		0x11
4927d916d6SDavid Daney 
5000db8189SAndy Fleming #define MII_M1011_IEVENT		0x13
5100db8189SAndy Fleming #define MII_M1011_IEVENT_CLEAR		0x0000
5200db8189SAndy Fleming 
5300db8189SAndy Fleming #define MII_M1011_IMASK			0x12
5400db8189SAndy Fleming #define MII_M1011_IMASK_INIT		0x6400
5500db8189SAndy Fleming #define MII_M1011_IMASK_CLEAR		0x0000
5600db8189SAndy Fleming 
5776884679SAndy Fleming #define MII_M1011_PHY_SCR			0x10
58fecd5e91SAndrew Lunn #define MII_M1011_PHY_SCR_DOWNSHIFT_EN		BIT(11)
59f8d975beSHeiner Kallweit #define MII_M1011_PHY_SCR_DOWNSHIFT_MASK	GENMASK(14, 12)
60a3bdfce7SHeiner Kallweit #define MII_M1011_PHY_SCR_DOWNSHIFT_MAX		8
61fecd5e91SAndrew Lunn #define MII_M1011_PHY_SCR_MDI			(0x0 << 5)
62fecd5e91SAndrew Lunn #define MII_M1011_PHY_SCR_MDI_X			(0x1 << 5)
63fecd5e91SAndrew Lunn #define MII_M1011_PHY_SCR_AUTO_CROSS		(0x3 << 5)
6476884679SAndy Fleming 
65a3bdfce7SHeiner Kallweit #define MII_M1011_PHY_SSR			0x11
66a3bdfce7SHeiner Kallweit #define MII_M1011_PHY_SSR_DOWNSHIFT		BIT(5)
67a3bdfce7SHeiner Kallweit 
6876884679SAndy Fleming #define MII_M1111_PHY_LED_CONTROL	0x18
6976884679SAndy Fleming #define MII_M1111_PHY_LED_DIRECT	0x4100
7076884679SAndy Fleming #define MII_M1111_PHY_LED_COMBINE	0x411c
71895ee682SKim Phillips #define MII_M1111_PHY_EXT_CR		0x14
725c6bc519SHeiner Kallweit #define MII_M1111_PHY_EXT_CR_DOWNSHIFT_MASK	GENMASK(11, 9)
735c6bc519SHeiner Kallweit #define MII_M1111_PHY_EXT_CR_DOWNSHIFT_MAX	8
745c6bc519SHeiner Kallweit #define MII_M1111_PHY_EXT_CR_DOWNSHIFT_EN	BIT(8)
7561111598SAndrew Lunn #define MII_M1111_RGMII_RX_DELAY	BIT(7)
7661111598SAndrew Lunn #define MII_M1111_RGMII_TX_DELAY	BIT(1)
77895ee682SKim Phillips #define MII_M1111_PHY_EXT_SR		0x1b
78be937f1fSAlexandr Smirnov 
79895ee682SKim Phillips #define MII_M1111_HWCFG_MODE_MASK		0xf
80be937f1fSAlexandr Smirnov #define MII_M1111_HWCFG_MODE_FIBER_RGMII	0x3
814117b5beSKapil Juneja #define MII_M1111_HWCFG_MODE_SGMII_NO_CLK	0x4
82865b813aSAndrew Lunn #define MII_M1111_HWCFG_MODE_RTBI		0x7
831887023aSRobert Hancock #define MII_M1111_HWCFG_MODE_COPPER_1000X_AN	0x8
845f8cbc13SLiu Yu-B13201 #define MII_M1111_HWCFG_MODE_COPPER_RTBI	0x9
85865b813aSAndrew Lunn #define MII_M1111_HWCFG_MODE_COPPER_RGMII	0xb
861887023aSRobert Hancock #define MII_M1111_HWCFG_MODE_COPPER_1000X_NOAN	0xc
871887023aSRobert Hancock #define MII_M1111_HWCFG_SERIAL_AN_BYPASS	BIT(12)
88865b813aSAndrew Lunn #define MII_M1111_HWCFG_FIBER_COPPER_RES	BIT(13)
89865b813aSAndrew Lunn #define MII_M1111_HWCFG_FIBER_COPPER_AUTO	BIT(15)
90be937f1fSAlexandr Smirnov 
91c477d044SCyril Chemparathy #define MII_88E1121_PHY_MSCR_REG	21
92c477d044SCyril Chemparathy #define MII_88E1121_PHY_MSCR_RX_DELAY	BIT(5)
93c477d044SCyril Chemparathy #define MII_88E1121_PHY_MSCR_TX_DELAY	BIT(4)
94424ca4c5SRussell King #define MII_88E1121_PHY_MSCR_DELAY_MASK	(BIT(5) | BIT(4))
95c477d044SCyril Chemparathy 
960b04680fSAndrew Lunn #define MII_88E1121_MISC_TEST				0x1a
970b04680fSAndrew Lunn #define MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK	0x1f00
980b04680fSAndrew Lunn #define MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT	8
990b04680fSAndrew Lunn #define MII_88E1510_MISC_TEST_TEMP_IRQ_EN		BIT(7)
1000b04680fSAndrew Lunn #define MII_88E1510_MISC_TEST_TEMP_IRQ			BIT(6)
1010b04680fSAndrew Lunn #define MII_88E1121_MISC_TEST_TEMP_SENSOR_EN		BIT(5)
1020b04680fSAndrew Lunn #define MII_88E1121_MISC_TEST_TEMP_MASK			0x1f
1030b04680fSAndrew Lunn 
1040b04680fSAndrew Lunn #define MII_88E1510_TEMP_SENSOR		0x1b
1050b04680fSAndrew Lunn #define MII_88E1510_TEMP_SENSOR_MASK	0xff
1060b04680fSAndrew Lunn 
10769f42be8SHeiner Kallweit #define MII_88E1540_COPPER_CTRL3	0x1a
10869f42be8SHeiner Kallweit #define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_MASK	GENMASK(11, 10)
10969f42be8SHeiner Kallweit #define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_00MS	0
11069f42be8SHeiner Kallweit #define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_10MS	1
11169f42be8SHeiner Kallweit #define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_20MS	2
11269f42be8SHeiner Kallweit #define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_40MS	3
11369f42be8SHeiner Kallweit #define MII_88E1540_COPPER_CTRL3_FAST_LINK_DOWN		BIT(9)
11469f42be8SHeiner Kallweit 
115fee2d546SAndrew Lunn #define MII_88E6390_MISC_TEST		0x1b
116fee2d546SAndrew Lunn #define MII_88E6390_MISC_TEST_SAMPLE_1S		0
117fee2d546SAndrew Lunn #define MII_88E6390_MISC_TEST_SAMPLE_10MS	BIT(14)
118fee2d546SAndrew Lunn #define MII_88E6390_MISC_TEST_SAMPLE_DISABLE	BIT(15)
119fee2d546SAndrew Lunn #define MII_88E6390_MISC_TEST_SAMPLE_ENABLE	0
120fee2d546SAndrew Lunn #define MII_88E6390_MISC_TEST_SAMPLE_MASK	(0x3 << 14)
121fee2d546SAndrew Lunn 
122fee2d546SAndrew Lunn #define MII_88E6390_TEMP_SENSOR		0x1c
123fee2d546SAndrew Lunn #define MII_88E6390_TEMP_SENSOR_MASK	0xff
124fee2d546SAndrew Lunn #define MII_88E6390_TEMP_SENSOR_SAMPLES 10
125fee2d546SAndrew Lunn 
126337ac9d5SCyril Chemparathy #define MII_88E1318S_PHY_MSCR1_REG	16
127337ac9d5SCyril Chemparathy #define MII_88E1318S_PHY_MSCR1_PAD_ODD	BIT(6)
1283ff1c259SCyril Chemparathy 
1293871c387SMichael Stapelberg /* Copper Specific Interrupt Enable Register */
1303871c387SMichael Stapelberg #define MII_88E1318S_PHY_CSIER				0x12
1313871c387SMichael Stapelberg /* WOL Event Interrupt Enable */
1323871c387SMichael Stapelberg #define MII_88E1318S_PHY_CSIER_WOL_EIE			BIT(7)
1333871c387SMichael Stapelberg 
1343871c387SMichael Stapelberg /* LED Timer Control Register */
1353871c387SMichael Stapelberg #define MII_88E1318S_PHY_LED_TCR			0x12
1363871c387SMichael Stapelberg #define MII_88E1318S_PHY_LED_TCR_FORCE_INT		BIT(15)
1373871c387SMichael Stapelberg #define MII_88E1318S_PHY_LED_TCR_INTn_ENABLE		BIT(7)
1383871c387SMichael Stapelberg #define MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW		BIT(11)
1393871c387SMichael Stapelberg 
1403871c387SMichael Stapelberg /* Magic Packet MAC address registers */
1413871c387SMichael Stapelberg #define MII_88E1318S_PHY_MAGIC_PACKET_WORD2		0x17
1423871c387SMichael Stapelberg #define MII_88E1318S_PHY_MAGIC_PACKET_WORD1		0x18
1433871c387SMichael Stapelberg #define MII_88E1318S_PHY_MAGIC_PACKET_WORD0		0x19
1443871c387SMichael Stapelberg 
1453871c387SMichael Stapelberg #define MII_88E1318S_PHY_WOL_CTRL				0x10
1463871c387SMichael Stapelberg #define MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS		BIT(12)
1473871c387SMichael Stapelberg #define MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE	BIT(14)
1483871c387SMichael Stapelberg 
14907777246SWang Dongsheng #define MII_PHY_LED_CTRL	        16
150140bc929SSergei Poselenov #define MII_88E1121_PHY_LED_DEF		0x0030
15107777246SWang Dongsheng #define MII_88E1510_PHY_LED_DEF		0x1177
152a93f7fe1SJian Shen #define MII_88E1510_PHY_LED0_LINK_LED1_ACTIVE	0x1040
153140bc929SSergei Poselenov 
154be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS		0x11
155be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_1000	0x8000
156be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_100	0x4000
157be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_SPD_MASK	0xc000
158be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_FULLDUPLEX	0x2000
159be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_RESOLVED	0x0800
160be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_LINK	0x0400
161be937f1fSAlexandr Smirnov 
1626b358aedSSebastian Hesselbarth #define MII_88E3016_PHY_SPEC_CTRL	0x10
1636b358aedSSebastian Hesselbarth #define MII_88E3016_DISABLE_SCRAMBLER	0x0200
1646b358aedSSebastian Hesselbarth #define MII_88E3016_AUTO_MDIX_CROSSOVER	0x0030
16576884679SAndy Fleming 
166930b37eeSStefan Roese #define MII_88E1510_GEN_CTRL_REG_1		0x14
167930b37eeSStefan Roese #define MII_88E1510_GEN_CTRL_REG_1_MODE_MASK	0x7
168930b37eeSStefan Roese #define MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII	0x1	/* SGMII to copper */
169930b37eeSStefan Roese #define MII_88E1510_GEN_CTRL_REG_1_RESET	0x8000	/* Soft reset */
170930b37eeSStefan Roese 
1710c9bcc1dSAndrew Lunn #define MII_VCT5_TX_RX_MDI0_COUPLING	0x10
1720c9bcc1dSAndrew Lunn #define MII_VCT5_TX_RX_MDI1_COUPLING	0x11
1730c9bcc1dSAndrew Lunn #define MII_VCT5_TX_RX_MDI2_COUPLING	0x12
1740c9bcc1dSAndrew Lunn #define MII_VCT5_TX_RX_MDI3_COUPLING	0x13
1750c9bcc1dSAndrew Lunn #define MII_VCT5_TX_RX_AMPLITUDE_MASK	0x7f00
1760c9bcc1dSAndrew Lunn #define MII_VCT5_TX_RX_AMPLITUDE_SHIFT	8
1770c9bcc1dSAndrew Lunn #define MII_VCT5_TX_RX_COUPLING_POSITIVE_REFLECTION	BIT(15)
1780c9bcc1dSAndrew Lunn 
1790c9bcc1dSAndrew Lunn #define MII_VCT5_CTRL				0x17
1800c9bcc1dSAndrew Lunn #define MII_VCT5_CTRL_ENABLE				BIT(15)
1810c9bcc1dSAndrew Lunn #define MII_VCT5_CTRL_COMPLETE				BIT(14)
1820c9bcc1dSAndrew Lunn #define MII_VCT5_CTRL_TX_SAME_CHANNEL			(0x0 << 11)
1830c9bcc1dSAndrew Lunn #define MII_VCT5_CTRL_TX0_CHANNEL			(0x4 << 11)
1840c9bcc1dSAndrew Lunn #define MII_VCT5_CTRL_TX1_CHANNEL			(0x5 << 11)
1850c9bcc1dSAndrew Lunn #define MII_VCT5_CTRL_TX2_CHANNEL			(0x6 << 11)
1860c9bcc1dSAndrew Lunn #define MII_VCT5_CTRL_TX3_CHANNEL			(0x7 << 11)
1870c9bcc1dSAndrew Lunn #define MII_VCT5_CTRL_SAMPLES_2				(0x0 << 8)
1880c9bcc1dSAndrew Lunn #define MII_VCT5_CTRL_SAMPLES_4				(0x1 << 8)
1890c9bcc1dSAndrew Lunn #define MII_VCT5_CTRL_SAMPLES_8				(0x2 << 8)
1900c9bcc1dSAndrew Lunn #define MII_VCT5_CTRL_SAMPLES_16			(0x3 << 8)
1910c9bcc1dSAndrew Lunn #define MII_VCT5_CTRL_SAMPLES_32			(0x4 << 8)
1920c9bcc1dSAndrew Lunn #define MII_VCT5_CTRL_SAMPLES_64			(0x5 << 8)
1930c9bcc1dSAndrew Lunn #define MII_VCT5_CTRL_SAMPLES_128			(0x6 << 8)
1940c9bcc1dSAndrew Lunn #define MII_VCT5_CTRL_SAMPLES_DEFAULT			(0x6 << 8)
1950c9bcc1dSAndrew Lunn #define MII_VCT5_CTRL_SAMPLES_256			(0x7 << 8)
1960c9bcc1dSAndrew Lunn #define MII_VCT5_CTRL_SAMPLES_SHIFT			8
1970c9bcc1dSAndrew Lunn #define MII_VCT5_CTRL_MODE_MAXIMUM_PEEK			(0x0 << 6)
1980c9bcc1dSAndrew Lunn #define MII_VCT5_CTRL_MODE_FIRST_LAST_PEEK		(0x1 << 6)
1990c9bcc1dSAndrew Lunn #define MII_VCT5_CTRL_MODE_OFFSET			(0x2 << 6)
2000c9bcc1dSAndrew Lunn #define MII_VCT5_CTRL_SAMPLE_POINT			(0x3 << 6)
2010c9bcc1dSAndrew Lunn #define MII_VCT5_CTRL_PEEK_HYST_DEFAULT			3
2020c9bcc1dSAndrew Lunn 
2030c9bcc1dSAndrew Lunn #define MII_VCT5_SAMPLE_POINT_DISTANCE		0x18
204f2bc8ad3SAndrew Lunn #define MII_VCT5_SAMPLE_POINT_DISTANCE_MAX	511
2050c9bcc1dSAndrew Lunn #define MII_VCT5_TX_PULSE_CTRL			0x1c
2060c9bcc1dSAndrew Lunn #define MII_VCT5_TX_PULSE_CTRL_DONT_WAIT_LINK_DOWN	BIT(12)
2070c9bcc1dSAndrew Lunn #define MII_VCT5_TX_PULSE_CTRL_PULSE_WIDTH_128nS	(0x0 << 10)
2080c9bcc1dSAndrew Lunn #define MII_VCT5_TX_PULSE_CTRL_PULSE_WIDTH_96nS		(0x1 << 10)
2090c9bcc1dSAndrew Lunn #define MII_VCT5_TX_PULSE_CTRL_PULSE_WIDTH_64nS		(0x2 << 10)
2100c9bcc1dSAndrew Lunn #define MII_VCT5_TX_PULSE_CTRL_PULSE_WIDTH_32nS		(0x3 << 10)
2110c9bcc1dSAndrew Lunn #define MII_VCT5_TX_PULSE_CTRL_PULSE_WIDTH_SHIFT	10
2120c9bcc1dSAndrew Lunn #define MII_VCT5_TX_PULSE_CTRL_PULSE_AMPLITUDE_1000mV	(0x0 << 8)
2130c9bcc1dSAndrew Lunn #define MII_VCT5_TX_PULSE_CTRL_PULSE_AMPLITUDE_750mV	(0x1 << 8)
2140c9bcc1dSAndrew Lunn #define MII_VCT5_TX_PULSE_CTRL_PULSE_AMPLITUDE_500mV	(0x2 << 8)
2150c9bcc1dSAndrew Lunn #define MII_VCT5_TX_PULSE_CTRL_PULSE_AMPLITUDE_250mV	(0x3 << 8)
2160c9bcc1dSAndrew Lunn #define MII_VCT5_TX_PULSE_CTRL_PULSE_AMPLITUDE_SHIFT	8
2170c9bcc1dSAndrew Lunn #define MII_VCT5_TX_PULSE_CTRL_MAX_AMP			BIT(7)
2180c9bcc1dSAndrew Lunn #define MII_VCT5_TX_PULSE_CTRL_GT_140m_46_86mV		(0x6 << 0)
2190c9bcc1dSAndrew Lunn 
220db8668a1SAndrew Lunn /* For TDR measurements less than 11 meters, a short pulse should be
221db8668a1SAndrew Lunn  * used.
222db8668a1SAndrew Lunn  */
223db8668a1SAndrew Lunn #define TDR_SHORT_CABLE_LENGTH	11
224db8668a1SAndrew Lunn 
225fc879f72SAndrew Lunn #define MII_VCT7_PAIR_0_DISTANCE	0x10
226fc879f72SAndrew Lunn #define MII_VCT7_PAIR_1_DISTANCE	0x11
227fc879f72SAndrew Lunn #define MII_VCT7_PAIR_2_DISTANCE	0x12
228fc879f72SAndrew Lunn #define MII_VCT7_PAIR_3_DISTANCE	0x13
229fc879f72SAndrew Lunn 
230fc879f72SAndrew Lunn #define MII_VCT7_RESULTS	0x14
231fc879f72SAndrew Lunn #define MII_VCT7_RESULTS_PAIR3_MASK	0xf000
232fc879f72SAndrew Lunn #define MII_VCT7_RESULTS_PAIR2_MASK	0x0f00
233fc879f72SAndrew Lunn #define MII_VCT7_RESULTS_PAIR1_MASK	0x00f0
234fc879f72SAndrew Lunn #define MII_VCT7_RESULTS_PAIR0_MASK	0x000f
235fc879f72SAndrew Lunn #define MII_VCT7_RESULTS_PAIR3_SHIFT	12
236fc879f72SAndrew Lunn #define MII_VCT7_RESULTS_PAIR2_SHIFT	8
237fc879f72SAndrew Lunn #define MII_VCT7_RESULTS_PAIR1_SHIFT	4
238fc879f72SAndrew Lunn #define MII_VCT7_RESULTS_PAIR0_SHIFT	0
239fc879f72SAndrew Lunn #define MII_VCT7_RESULTS_INVALID	0
240fc879f72SAndrew Lunn #define MII_VCT7_RESULTS_OK		1
241fc879f72SAndrew Lunn #define MII_VCT7_RESULTS_OPEN		2
242fc879f72SAndrew Lunn #define MII_VCT7_RESULTS_SAME_SHORT	3
243fc879f72SAndrew Lunn #define MII_VCT7_RESULTS_CROSS_SHORT	4
244fc879f72SAndrew Lunn #define MII_VCT7_RESULTS_BUSY		9
245fc879f72SAndrew Lunn 
246fc879f72SAndrew Lunn #define MII_VCT7_CTRL		0x15
247fc879f72SAndrew Lunn #define MII_VCT7_CTRL_RUN_NOW			BIT(15)
248fc879f72SAndrew Lunn #define MII_VCT7_CTRL_RUN_ANEG			BIT(14)
249fc879f72SAndrew Lunn #define MII_VCT7_CTRL_DISABLE_CROSS		BIT(13)
250fc879f72SAndrew Lunn #define MII_VCT7_CTRL_RUN_AFTER_BREAK_LINK	BIT(12)
251fc879f72SAndrew Lunn #define MII_VCT7_CTRL_IN_PROGRESS		BIT(11)
252fc879f72SAndrew Lunn #define MII_VCT7_CTRL_METERS			BIT(10)
253fc879f72SAndrew Lunn #define MII_VCT7_CTRL_CENTIMETERS		0
254fc879f72SAndrew Lunn 
2556cfb3bccSCharles-Antoine Couret #define LPA_PAUSE_FIBER		0x180
2566cfb3bccSCharles-Antoine Couret #define LPA_PAUSE_ASYM_FIBER	0x100
2576cfb3bccSCharles-Antoine Couret 
2582170fef7SCharles-Antoine Couret #define NB_FIBER_STATS	1
2596cfb3bccSCharles-Antoine Couret 
26000db8189SAndy Fleming MODULE_DESCRIPTION("Marvell PHY driver");
26100db8189SAndy Fleming MODULE_AUTHOR("Andy Fleming");
26200db8189SAndy Fleming MODULE_LICENSE("GPL");
26300db8189SAndy Fleming 
264d2fa47d9SAndrew Lunn struct marvell_hw_stat {
265d2fa47d9SAndrew Lunn 	const char *string;
266d2fa47d9SAndrew Lunn 	u8 page;
267d2fa47d9SAndrew Lunn 	u8 reg;
268d2fa47d9SAndrew Lunn 	u8 bits;
269d2fa47d9SAndrew Lunn };
270d2fa47d9SAndrew Lunn 
271d2fa47d9SAndrew Lunn static struct marvell_hw_stat marvell_hw_stats[] = {
2722170fef7SCharles-Antoine Couret 	{ "phy_receive_errors_copper", 0, 21, 16},
273d2fa47d9SAndrew Lunn 	{ "phy_idle_errors", 0, 10, 8 },
2742170fef7SCharles-Antoine Couret 	{ "phy_receive_errors_fiber", 1, 21, 16},
275d2fa47d9SAndrew Lunn };
276d2fa47d9SAndrew Lunn 
277d2fa47d9SAndrew Lunn struct marvell_priv {
278d2fa47d9SAndrew Lunn 	u64 stats[ARRAY_SIZE(marvell_hw_stats)];
2790b04680fSAndrew Lunn 	char *hwmon_name;
2800b04680fSAndrew Lunn 	struct device *hwmon_dev;
2810c9bcc1dSAndrew Lunn 	bool cable_test_tdr;
282f2bc8ad3SAndrew Lunn 	u32 first;
283f2bc8ad3SAndrew Lunn 	u32 last;
284f2bc8ad3SAndrew Lunn 	u32 step;
285f2bc8ad3SAndrew Lunn 	s8 pair;
286d2fa47d9SAndrew Lunn };
287d2fa47d9SAndrew Lunn 
288424ca4c5SRussell King static int marvell_read_page(struct phy_device *phydev)
2896427bb2dSAndrew Lunn {
290424ca4c5SRussell King 	return __phy_read(phydev, MII_MARVELL_PHY_PAGE);
291424ca4c5SRussell King }
292424ca4c5SRussell King 
293424ca4c5SRussell King static int marvell_write_page(struct phy_device *phydev, int page)
294424ca4c5SRussell King {
295424ca4c5SRussell King 	return __phy_write(phydev, MII_MARVELL_PHY_PAGE, page);
2966427bb2dSAndrew Lunn }
2976427bb2dSAndrew Lunn 
2986427bb2dSAndrew Lunn static int marvell_set_page(struct phy_device *phydev, int page)
2996427bb2dSAndrew Lunn {
3006427bb2dSAndrew Lunn 	return phy_write(phydev, MII_MARVELL_PHY_PAGE, page);
3016427bb2dSAndrew Lunn }
3026427bb2dSAndrew Lunn 
30300db8189SAndy Fleming static int marvell_ack_interrupt(struct phy_device *phydev)
30400db8189SAndy Fleming {
30500db8189SAndy Fleming 	int err;
30600db8189SAndy Fleming 
30700db8189SAndy Fleming 	/* Clear the interrupts by reading the reg */
30800db8189SAndy Fleming 	err = phy_read(phydev, MII_M1011_IEVENT);
30900db8189SAndy Fleming 
31000db8189SAndy Fleming 	if (err < 0)
31100db8189SAndy Fleming 		return err;
31200db8189SAndy Fleming 
31300db8189SAndy Fleming 	return 0;
31400db8189SAndy Fleming }
31500db8189SAndy Fleming 
31600db8189SAndy Fleming static int marvell_config_intr(struct phy_device *phydev)
31700db8189SAndy Fleming {
31800db8189SAndy Fleming 	int err;
31900db8189SAndy Fleming 
3201f6d0f26SIoana Ciornei 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
3211f6d0f26SIoana Ciornei 		err = marvell_ack_interrupt(phydev);
3221f6d0f26SIoana Ciornei 		if (err)
3231f6d0f26SIoana Ciornei 			return err;
3241f6d0f26SIoana Ciornei 
32523beb38fSAndrew Lunn 		err = phy_write(phydev, MII_M1011_IMASK,
32623beb38fSAndrew Lunn 				MII_M1011_IMASK_INIT);
3271f6d0f26SIoana Ciornei 	} else {
32823beb38fSAndrew Lunn 		err = phy_write(phydev, MII_M1011_IMASK,
32923beb38fSAndrew Lunn 				MII_M1011_IMASK_CLEAR);
3301f6d0f26SIoana Ciornei 		if (err)
3311f6d0f26SIoana Ciornei 			return err;
3321f6d0f26SIoana Ciornei 
3331f6d0f26SIoana Ciornei 		err = marvell_ack_interrupt(phydev);
3341f6d0f26SIoana Ciornei 	}
33500db8189SAndy Fleming 
33600db8189SAndy Fleming 	return err;
33700db8189SAndy Fleming }
33800db8189SAndy Fleming 
339a0723b37SIoana Ciornei static irqreturn_t marvell_handle_interrupt(struct phy_device *phydev)
340a0723b37SIoana Ciornei {
341a0723b37SIoana Ciornei 	int irq_status;
342a0723b37SIoana Ciornei 
343a0723b37SIoana Ciornei 	irq_status = phy_read(phydev, MII_M1011_IEVENT);
344a0723b37SIoana Ciornei 	if (irq_status < 0) {
345a0723b37SIoana Ciornei 		phy_error(phydev);
346a0723b37SIoana Ciornei 		return IRQ_NONE;
347a0723b37SIoana Ciornei 	}
348a0723b37SIoana Ciornei 
349a0723b37SIoana Ciornei 	if (!(irq_status & MII_M1011_IMASK_INIT))
350a0723b37SIoana Ciornei 		return IRQ_NONE;
351a0723b37SIoana Ciornei 
352a0723b37SIoana Ciornei 	phy_trigger_machine(phydev);
353a0723b37SIoana Ciornei 
354a0723b37SIoana Ciornei 	return IRQ_HANDLED;
355a0723b37SIoana Ciornei }
356a0723b37SIoana Ciornei 
357239aa55bSDavid Thomson static int marvell_set_polarity(struct phy_device *phydev, int polarity)
358239aa55bSDavid Thomson {
359239aa55bSDavid Thomson 	int reg;
360239aa55bSDavid Thomson 	int err;
361239aa55bSDavid Thomson 	int val;
362239aa55bSDavid Thomson 
363239aa55bSDavid Thomson 	/* get the current settings */
364239aa55bSDavid Thomson 	reg = phy_read(phydev, MII_M1011_PHY_SCR);
365239aa55bSDavid Thomson 	if (reg < 0)
366239aa55bSDavid Thomson 		return reg;
367239aa55bSDavid Thomson 
368239aa55bSDavid Thomson 	val = reg;
369239aa55bSDavid Thomson 	val &= ~MII_M1011_PHY_SCR_AUTO_CROSS;
370239aa55bSDavid Thomson 	switch (polarity) {
371239aa55bSDavid Thomson 	case ETH_TP_MDI:
372239aa55bSDavid Thomson 		val |= MII_M1011_PHY_SCR_MDI;
373239aa55bSDavid Thomson 		break;
374239aa55bSDavid Thomson 	case ETH_TP_MDI_X:
375239aa55bSDavid Thomson 		val |= MII_M1011_PHY_SCR_MDI_X;
376239aa55bSDavid Thomson 		break;
377239aa55bSDavid Thomson 	case ETH_TP_MDI_AUTO:
378239aa55bSDavid Thomson 	case ETH_TP_MDI_INVALID:
379239aa55bSDavid Thomson 	default:
380239aa55bSDavid Thomson 		val |= MII_M1011_PHY_SCR_AUTO_CROSS;
381239aa55bSDavid Thomson 		break;
382239aa55bSDavid Thomson 	}
383239aa55bSDavid Thomson 
384239aa55bSDavid Thomson 	if (val != reg) {
385239aa55bSDavid Thomson 		/* Set the new polarity value in the register */
386239aa55bSDavid Thomson 		err = phy_write(phydev, MII_M1011_PHY_SCR, val);
387239aa55bSDavid Thomson 		if (err)
388239aa55bSDavid Thomson 			return err;
389239aa55bSDavid Thomson 	}
390239aa55bSDavid Thomson 
391d6ab9336SFlorian Fainelli 	return val != reg;
392239aa55bSDavid Thomson }
393239aa55bSDavid Thomson 
39400db8189SAndy Fleming static int marvell_config_aneg(struct phy_device *phydev)
39500db8189SAndy Fleming {
396d6ab9336SFlorian Fainelli 	int changed = 0;
39700db8189SAndy Fleming 	int err;
39800db8189SAndy Fleming 
3994e26c5c3SRaju Lakkaraju 	err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
40076884679SAndy Fleming 	if (err < 0)
40176884679SAndy Fleming 		return err;
40276884679SAndy Fleming 
403d6ab9336SFlorian Fainelli 	changed = err;
404d6ab9336SFlorian Fainelli 
40576884679SAndy Fleming 	err = phy_write(phydev, MII_M1111_PHY_LED_CONTROL,
40676884679SAndy Fleming 			MII_M1111_PHY_LED_DIRECT);
40776884679SAndy Fleming 	if (err < 0)
40876884679SAndy Fleming 		return err;
40900db8189SAndy Fleming 
41000db8189SAndy Fleming 	err = genphy_config_aneg(phydev);
4118ff44985SAnton Vorontsov 	if (err < 0)
41200db8189SAndy Fleming 		return err;
4138ff44985SAnton Vorontsov 
414d6ab9336SFlorian Fainelli 	if (phydev->autoneg != AUTONEG_ENABLE || changed) {
4150c3439bcSAndrew Lunn 		/* A write to speed/duplex bits (that is performed by
4168ff44985SAnton Vorontsov 		 * genphy_config_aneg() call above) must be followed by
4178ff44985SAnton Vorontsov 		 * a software reset. Otherwise, the write has no effect.
4188ff44985SAnton Vorontsov 		 */
41934386344SAndrew Lunn 		err = genphy_soft_reset(phydev);
4208ff44985SAnton Vorontsov 		if (err < 0)
4218ff44985SAnton Vorontsov 			return err;
4228ff44985SAnton Vorontsov 	}
4238ff44985SAnton Vorontsov 
4248ff44985SAnton Vorontsov 	return 0;
42500db8189SAndy Fleming }
42600db8189SAndy Fleming 
427f2899788SAndrew Lunn static int m88e1101_config_aneg(struct phy_device *phydev)
428f2899788SAndrew Lunn {
429f2899788SAndrew Lunn 	int err;
430f2899788SAndrew Lunn 
431f2899788SAndrew Lunn 	/* This Marvell PHY has an errata which requires
432f2899788SAndrew Lunn 	 * that certain registers get written in order
433f2899788SAndrew Lunn 	 * to restart autonegotiation
434f2899788SAndrew Lunn 	 */
43534386344SAndrew Lunn 	err = genphy_soft_reset(phydev);
436f2899788SAndrew Lunn 	if (err < 0)
437f2899788SAndrew Lunn 		return err;
438f2899788SAndrew Lunn 
439f2899788SAndrew Lunn 	err = phy_write(phydev, 0x1d, 0x1f);
440f2899788SAndrew Lunn 	if (err < 0)
441f2899788SAndrew Lunn 		return err;
442f2899788SAndrew Lunn 
443f2899788SAndrew Lunn 	err = phy_write(phydev, 0x1e, 0x200c);
444f2899788SAndrew Lunn 	if (err < 0)
445f2899788SAndrew Lunn 		return err;
446f2899788SAndrew Lunn 
447f2899788SAndrew Lunn 	err = phy_write(phydev, 0x1d, 0x5);
448f2899788SAndrew Lunn 	if (err < 0)
449f2899788SAndrew Lunn 		return err;
450f2899788SAndrew Lunn 
451f2899788SAndrew Lunn 	err = phy_write(phydev, 0x1e, 0);
452f2899788SAndrew Lunn 	if (err < 0)
453f2899788SAndrew Lunn 		return err;
454f2899788SAndrew Lunn 
455f2899788SAndrew Lunn 	err = phy_write(phydev, 0x1e, 0x100);
456f2899788SAndrew Lunn 	if (err < 0)
457f2899788SAndrew Lunn 		return err;
458f2899788SAndrew Lunn 
459f2899788SAndrew Lunn 	return marvell_config_aneg(phydev);
460f2899788SAndrew Lunn }
461f2899788SAndrew Lunn 
4625cd119d9SDan Murphy #if IS_ENABLED(CONFIG_OF_MDIO)
4630c3439bcSAndrew Lunn /* Set and/or override some configuration registers based on the
464cf41a51dSDavid Daney  * marvell,reg-init property stored in the of_node for the phydev.
465cf41a51dSDavid Daney  *
466cf41a51dSDavid Daney  * marvell,reg-init = <reg-page reg mask value>,...;
467cf41a51dSDavid Daney  *
468cf41a51dSDavid Daney  * There may be one or more sets of <reg-page reg mask value>:
469cf41a51dSDavid Daney  *
470cf41a51dSDavid Daney  * reg-page: which register bank to use.
471cf41a51dSDavid Daney  * reg: the register.
472cf41a51dSDavid Daney  * mask: if non-zero, ANDed with existing register value.
473cf41a51dSDavid Daney  * value: ORed with the masked value and written to the regiser.
474cf41a51dSDavid Daney  *
475cf41a51dSDavid Daney  */
476cf41a51dSDavid Daney static int marvell_of_reg_init(struct phy_device *phydev)
477cf41a51dSDavid Daney {
478cf41a51dSDavid Daney 	const __be32 *paddr;
479424ca4c5SRussell King 	int len, i, saved_page, current_page, ret = 0;
480cf41a51dSDavid Daney 
481e5a03bfdSAndrew Lunn 	if (!phydev->mdio.dev.of_node)
482cf41a51dSDavid Daney 		return 0;
483cf41a51dSDavid Daney 
484e5a03bfdSAndrew Lunn 	paddr = of_get_property(phydev->mdio.dev.of_node,
485e5a03bfdSAndrew Lunn 				"marvell,reg-init", &len);
486cf41a51dSDavid Daney 	if (!paddr || len < (4 * sizeof(*paddr)))
487cf41a51dSDavid Daney 		return 0;
488cf41a51dSDavid Daney 
489424ca4c5SRussell King 	saved_page = phy_save_page(phydev);
490cf41a51dSDavid Daney 	if (saved_page < 0)
491424ca4c5SRussell King 		goto err;
492cf41a51dSDavid Daney 	current_page = saved_page;
493cf41a51dSDavid Daney 
494cf41a51dSDavid Daney 	len /= sizeof(*paddr);
495cf41a51dSDavid Daney 	for (i = 0; i < len - 3; i += 4) {
4966427bb2dSAndrew Lunn 		u16 page = be32_to_cpup(paddr + i);
497cf41a51dSDavid Daney 		u16 reg = be32_to_cpup(paddr + i + 1);
498cf41a51dSDavid Daney 		u16 mask = be32_to_cpup(paddr + i + 2);
499cf41a51dSDavid Daney 		u16 val_bits = be32_to_cpup(paddr + i + 3);
500cf41a51dSDavid Daney 		int val;
501cf41a51dSDavid Daney 
5026427bb2dSAndrew Lunn 		if (page != current_page) {
5036427bb2dSAndrew Lunn 			current_page = page;
504424ca4c5SRussell King 			ret = marvell_write_page(phydev, page);
505cf41a51dSDavid Daney 			if (ret < 0)
506cf41a51dSDavid Daney 				goto err;
507cf41a51dSDavid Daney 		}
508cf41a51dSDavid Daney 
509cf41a51dSDavid Daney 		val = 0;
510cf41a51dSDavid Daney 		if (mask) {
511424ca4c5SRussell King 			val = __phy_read(phydev, reg);
512cf41a51dSDavid Daney 			if (val < 0) {
513cf41a51dSDavid Daney 				ret = val;
514cf41a51dSDavid Daney 				goto err;
515cf41a51dSDavid Daney 			}
516cf41a51dSDavid Daney 			val &= mask;
517cf41a51dSDavid Daney 		}
518cf41a51dSDavid Daney 		val |= val_bits;
519cf41a51dSDavid Daney 
520424ca4c5SRussell King 		ret = __phy_write(phydev, reg, val);
521cf41a51dSDavid Daney 		if (ret < 0)
522cf41a51dSDavid Daney 			goto err;
523cf41a51dSDavid Daney 	}
524cf41a51dSDavid Daney err:
525424ca4c5SRussell King 	return phy_restore_page(phydev, saved_page, ret);
526cf41a51dSDavid Daney }
527cf41a51dSDavid Daney #else
528cf41a51dSDavid Daney static int marvell_of_reg_init(struct phy_device *phydev)
529cf41a51dSDavid Daney {
530cf41a51dSDavid Daney 	return 0;
531cf41a51dSDavid Daney }
532cf41a51dSDavid Daney #endif /* CONFIG_OF_MDIO */
533cf41a51dSDavid Daney 
534864dc729SAndrew Lunn static int m88e1121_config_aneg_rgmii_delays(struct phy_device *phydev)
535140bc929SSergei Poselenov {
536424ca4c5SRussell King 	int mscr;
537c477d044SCyril Chemparathy 
538c477d044SCyril Chemparathy 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
539424ca4c5SRussell King 		mscr = MII_88E1121_PHY_MSCR_RX_DELAY |
540424ca4c5SRussell King 		       MII_88E1121_PHY_MSCR_TX_DELAY;
541c477d044SCyril Chemparathy 	else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
542424ca4c5SRussell King 		mscr = MII_88E1121_PHY_MSCR_RX_DELAY;
543c477d044SCyril Chemparathy 	else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
544424ca4c5SRussell King 		mscr = MII_88E1121_PHY_MSCR_TX_DELAY;
545424ca4c5SRussell King 	else
546424ca4c5SRussell King 		mscr = 0;
547c477d044SCyril Chemparathy 
548424ca4c5SRussell King 	return phy_modify_paged(phydev, MII_MARVELL_MSCR_PAGE,
549424ca4c5SRussell King 				MII_88E1121_PHY_MSCR_REG,
550424ca4c5SRussell King 				MII_88E1121_PHY_MSCR_DELAY_MASK, mscr);
551be8c6480SArnaud Patard }
552c477d044SCyril Chemparathy 
553864dc729SAndrew Lunn static int m88e1121_config_aneg(struct phy_device *phydev)
554864dc729SAndrew Lunn {
555d6ab9336SFlorian Fainelli 	int changed = 0;
556864dc729SAndrew Lunn 	int err = 0;
557864dc729SAndrew Lunn 
558864dc729SAndrew Lunn 	if (phy_interface_is_rgmii(phydev)) {
559864dc729SAndrew Lunn 		err = m88e1121_config_aneg_rgmii_delays(phydev);
560fea23fb5SRussell King 		if (err < 0)
561864dc729SAndrew Lunn 			return err;
562864dc729SAndrew Lunn 	}
563140bc929SSergei Poselenov 
564fecd5e91SAndrew Lunn 	err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
565140bc929SSergei Poselenov 	if (err < 0)
566140bc929SSergei Poselenov 		return err;
567140bc929SSergei Poselenov 
568d6ab9336SFlorian Fainelli 	changed = err;
569d6ab9336SFlorian Fainelli 
570d6ab9336SFlorian Fainelli 	err = genphy_config_aneg(phydev);
571d6ab9336SFlorian Fainelli 	if (err < 0)
572d6ab9336SFlorian Fainelli 		return err;
573d6ab9336SFlorian Fainelli 
5744b1bd697SDavid S. Miller 	if (phydev->autoneg != AUTONEG_ENABLE || changed) {
575d6ab9336SFlorian Fainelli 		/* A software reset is used to ensure a "commit" of the
576d6ab9336SFlorian Fainelli 		 * changes is done.
577d6ab9336SFlorian Fainelli 		 */
578d6ab9336SFlorian Fainelli 		err = genphy_soft_reset(phydev);
579d6ab9336SFlorian Fainelli 		if (err < 0)
580d6ab9336SFlorian Fainelli 			return err;
581d6ab9336SFlorian Fainelli 	}
582d6ab9336SFlorian Fainelli 
583d6ab9336SFlorian Fainelli 	return 0;
584140bc929SSergei Poselenov }
585140bc929SSergei Poselenov 
586337ac9d5SCyril Chemparathy static int m88e1318_config_aneg(struct phy_device *phydev)
5873ff1c259SCyril Chemparathy {
588424ca4c5SRussell King 	int err;
5893ff1c259SCyril Chemparathy 
590424ca4c5SRussell King 	err = phy_modify_paged(phydev, MII_MARVELL_MSCR_PAGE,
591424ca4c5SRussell King 			       MII_88E1318S_PHY_MSCR1_REG,
592424ca4c5SRussell King 			       0, MII_88E1318S_PHY_MSCR1_PAD_ODD);
5933ff1c259SCyril Chemparathy 	if (err < 0)
5943ff1c259SCyril Chemparathy 		return err;
5953ff1c259SCyril Chemparathy 
5963ff1c259SCyril Chemparathy 	return m88e1121_config_aneg(phydev);
5973ff1c259SCyril Chemparathy }
5983ff1c259SCyril Chemparathy 
59978301ebeSCharles-Antoine Couret /**
6003c1bcc86SAndrew Lunn  * linkmode_adv_to_fiber_adv_t
6013c1bcc86SAndrew Lunn  * @advertise: the linkmode advertisement settings
60278301ebeSCharles-Antoine Couret  *
6033c1bcc86SAndrew Lunn  * A small helper function that translates linkmode advertisement
6043c1bcc86SAndrew Lunn  * settings to phy autonegotiation advertisements for the MII_ADV
6053c1bcc86SAndrew Lunn  * register for fiber link.
60678301ebeSCharles-Antoine Couret  */
6073c1bcc86SAndrew Lunn static inline u32 linkmode_adv_to_fiber_adv_t(unsigned long *advertise)
60878301ebeSCharles-Antoine Couret {
60978301ebeSCharles-Antoine Couret 	u32 result = 0;
61078301ebeSCharles-Antoine Couret 
6113c1bcc86SAndrew Lunn 	if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, advertise))
61220ecf424SRussell King 		result |= ADVERTISE_1000XHALF;
6133c1bcc86SAndrew Lunn 	if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, advertise))
61420ecf424SRussell King 		result |= ADVERTISE_1000XFULL;
61578301ebeSCharles-Antoine Couret 
6163c1bcc86SAndrew Lunn 	if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, advertise) &&
6173c1bcc86SAndrew Lunn 	    linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertise))
61820ecf424SRussell King 		result |= ADVERTISE_1000XPSE_ASYM;
6193c1bcc86SAndrew Lunn 	else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertise))
62020ecf424SRussell King 		result |= ADVERTISE_1000XPAUSE;
62178301ebeSCharles-Antoine Couret 
62278301ebeSCharles-Antoine Couret 	return result;
62378301ebeSCharles-Antoine Couret }
62478301ebeSCharles-Antoine Couret 
62578301ebeSCharles-Antoine Couret /**
62678301ebeSCharles-Antoine Couret  * marvell_config_aneg_fiber - restart auto-negotiation or write BMCR
62778301ebeSCharles-Antoine Couret  * @phydev: target phy_device struct
62878301ebeSCharles-Antoine Couret  *
62978301ebeSCharles-Antoine Couret  * Description: If auto-negotiation is enabled, we configure the
63078301ebeSCharles-Antoine Couret  *   advertising, and then restart auto-negotiation.  If it is not
63178301ebeSCharles-Antoine Couret  *   enabled, then we write the BMCR. Adapted for fiber link in
63278301ebeSCharles-Antoine Couret  *   some Marvell's devices.
63378301ebeSCharles-Antoine Couret  */
63478301ebeSCharles-Antoine Couret static int marvell_config_aneg_fiber(struct phy_device *phydev)
63578301ebeSCharles-Antoine Couret {
63678301ebeSCharles-Antoine Couret 	int changed = 0;
63778301ebeSCharles-Antoine Couret 	int err;
6389f4bae70SRussell King 	u16 adv;
63978301ebeSCharles-Antoine Couret 
64078301ebeSCharles-Antoine Couret 	if (phydev->autoneg != AUTONEG_ENABLE)
64178301ebeSCharles-Antoine Couret 		return genphy_setup_forced(phydev);
64278301ebeSCharles-Antoine Couret 
64378301ebeSCharles-Antoine Couret 	/* Only allow advertising what this PHY supports */
6443c1bcc86SAndrew Lunn 	linkmode_and(phydev->advertising, phydev->advertising,
6453c1bcc86SAndrew Lunn 		     phydev->supported);
64678301ebeSCharles-Antoine Couret 
6479f4bae70SRussell King 	adv = linkmode_adv_to_fiber_adv_t(phydev->advertising);
6489f4bae70SRussell King 
64978301ebeSCharles-Antoine Couret 	/* Setup fiber advertisement */
6509f4bae70SRussell King 	err = phy_modify_changed(phydev, MII_ADVERTISE,
6519f4bae70SRussell King 				 ADVERTISE_1000XHALF | ADVERTISE_1000XFULL |
6529f4bae70SRussell King 				 ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM,
6539f4bae70SRussell King 				 adv);
65478301ebeSCharles-Antoine Couret 	if (err < 0)
65578301ebeSCharles-Antoine Couret 		return err;
6569f4bae70SRussell King 	if (err > 0)
65778301ebeSCharles-Antoine Couret 		changed = 1;
65878301ebeSCharles-Antoine Couret 
659b5abac2dSRussell King 	return genphy_check_and_restart_aneg(phydev, changed);
66078301ebeSCharles-Antoine Couret }
66178301ebeSCharles-Antoine Couret 
6621887023aSRobert Hancock static int m88e1111_config_aneg(struct phy_device *phydev)
6631887023aSRobert Hancock {
6641887023aSRobert Hancock 	int extsr = phy_read(phydev, MII_M1111_PHY_EXT_SR);
6651887023aSRobert Hancock 	int err;
6661887023aSRobert Hancock 
6671887023aSRobert Hancock 	if (extsr < 0)
6681887023aSRobert Hancock 		return extsr;
6691887023aSRobert Hancock 
6701887023aSRobert Hancock 	/* If not using SGMII or copper 1000BaseX modes, use normal process.
6711887023aSRobert Hancock 	 * Steps below are only required for these modes.
6721887023aSRobert Hancock 	 */
6731887023aSRobert Hancock 	if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
6741887023aSRobert Hancock 	    (extsr & MII_M1111_HWCFG_MODE_MASK) !=
6751887023aSRobert Hancock 	    MII_M1111_HWCFG_MODE_COPPER_1000X_AN)
6761887023aSRobert Hancock 		return marvell_config_aneg(phydev);
6771887023aSRobert Hancock 
6781887023aSRobert Hancock 	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
6791887023aSRobert Hancock 	if (err < 0)
6801887023aSRobert Hancock 		goto error;
6811887023aSRobert Hancock 
6821887023aSRobert Hancock 	/* Configure the copper link first */
6831887023aSRobert Hancock 	err = marvell_config_aneg(phydev);
6841887023aSRobert Hancock 	if (err < 0)
6851887023aSRobert Hancock 		goto error;
6861887023aSRobert Hancock 
6871887023aSRobert Hancock 	/* Then the fiber link */
6881887023aSRobert Hancock 	err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
6891887023aSRobert Hancock 	if (err < 0)
6901887023aSRobert Hancock 		goto error;
6911887023aSRobert Hancock 
692*06b334f0SRobert Hancock 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII)
693*06b334f0SRobert Hancock 		/* Do not touch the fiber advertisement if we're in copper->sgmii mode.
694*06b334f0SRobert Hancock 		 * Just ensure that SGMII-side autonegotiation is enabled.
695*06b334f0SRobert Hancock 		 * If we switched from some other mode to SGMII it may not be.
696*06b334f0SRobert Hancock 		 */
697*06b334f0SRobert Hancock 		err = genphy_check_and_restart_aneg(phydev, false);
698*06b334f0SRobert Hancock 	else
6991887023aSRobert Hancock 		err = marvell_config_aneg_fiber(phydev);
7001887023aSRobert Hancock 	if (err < 0)
7011887023aSRobert Hancock 		goto error;
7021887023aSRobert Hancock 
7031887023aSRobert Hancock 	return marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
7041887023aSRobert Hancock 
7051887023aSRobert Hancock error:
7061887023aSRobert Hancock 	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
7071887023aSRobert Hancock 	return err;
7081887023aSRobert Hancock }
7091887023aSRobert Hancock 
71010e24caaSMichal Simek static int m88e1510_config_aneg(struct phy_device *phydev)
71110e24caaSMichal Simek {
71210e24caaSMichal Simek 	int err;
71310e24caaSMichal Simek 
71452295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
71578301ebeSCharles-Antoine Couret 	if (err < 0)
71678301ebeSCharles-Antoine Couret 		goto error;
71778301ebeSCharles-Antoine Couret 
71878301ebeSCharles-Antoine Couret 	/* Configure the copper link first */
71910e24caaSMichal Simek 	err = m88e1318_config_aneg(phydev);
72010e24caaSMichal Simek 	if (err < 0)
72178301ebeSCharles-Antoine Couret 		goto error;
72210e24caaSMichal Simek 
723de9c4e06SRussell King 	/* Do not touch the fiber page if we're in copper->sgmii mode */
724de9c4e06SRussell King 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII)
725de9c4e06SRussell King 		return 0;
726de9c4e06SRussell King 
72778301ebeSCharles-Antoine Couret 	/* Then the fiber link */
72852295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
72978301ebeSCharles-Antoine Couret 	if (err < 0)
73078301ebeSCharles-Antoine Couret 		goto error;
73178301ebeSCharles-Antoine Couret 
73278301ebeSCharles-Antoine Couret 	err = marvell_config_aneg_fiber(phydev);
73378301ebeSCharles-Antoine Couret 	if (err < 0)
73478301ebeSCharles-Antoine Couret 		goto error;
73578301ebeSCharles-Antoine Couret 
73652295666SAndrew Lunn 	return marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
73778301ebeSCharles-Antoine Couret 
73878301ebeSCharles-Antoine Couret error:
73952295666SAndrew Lunn 	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
74078301ebeSCharles-Antoine Couret 	return err;
74179be1a1cSClemens Gruber }
74279be1a1cSClemens Gruber 
74307777246SWang Dongsheng static void marvell_config_led(struct phy_device *phydev)
74407777246SWang Dongsheng {
74507777246SWang Dongsheng 	u16 def_config;
74607777246SWang Dongsheng 	int err;
74707777246SWang Dongsheng 
74807777246SWang Dongsheng 	switch (MARVELL_PHY_FAMILY_ID(phydev->phy_id)) {
74907777246SWang Dongsheng 	/* Default PHY LED config: LED[0] .. Link, LED[1] .. Activity */
75007777246SWang Dongsheng 	case MARVELL_PHY_FAMILY_ID(MARVELL_PHY_ID_88E1121R):
75107777246SWang Dongsheng 	case MARVELL_PHY_FAMILY_ID(MARVELL_PHY_ID_88E1318S):
75207777246SWang Dongsheng 		def_config = MII_88E1121_PHY_LED_DEF;
75307777246SWang Dongsheng 		break;
75407777246SWang Dongsheng 	/* Default PHY LED config:
75507777246SWang Dongsheng 	 * LED[0] .. 1000Mbps Link
75607777246SWang Dongsheng 	 * LED[1] .. 100Mbps Link
75707777246SWang Dongsheng 	 * LED[2] .. Blink, Activity
75807777246SWang Dongsheng 	 */
75907777246SWang Dongsheng 	case MARVELL_PHY_FAMILY_ID(MARVELL_PHY_ID_88E1510):
760a93f7fe1SJian Shen 		if (phydev->dev_flags & MARVELL_PHY_LED0_LINK_LED1_ACTIVE)
761a93f7fe1SJian Shen 			def_config = MII_88E1510_PHY_LED0_LINK_LED1_ACTIVE;
762a93f7fe1SJian Shen 		else
76307777246SWang Dongsheng 			def_config = MII_88E1510_PHY_LED_DEF;
76407777246SWang Dongsheng 		break;
76507777246SWang Dongsheng 	default:
76607777246SWang Dongsheng 		return;
76707777246SWang Dongsheng 	}
76807777246SWang Dongsheng 
76907777246SWang Dongsheng 	err = phy_write_paged(phydev, MII_MARVELL_LED_PAGE, MII_PHY_LED_CTRL,
77007777246SWang Dongsheng 			      def_config);
77107777246SWang Dongsheng 	if (err < 0)
772ab2a605fSAndrew Lunn 		phydev_warn(phydev, "Fail to config marvell phy LED.\n");
77307777246SWang Dongsheng }
77407777246SWang Dongsheng 
77579be1a1cSClemens Gruber static int marvell_config_init(struct phy_device *phydev)
77679be1a1cSClemens Gruber {
77785bec4bcSBhaskar Chowdhury 	/* Set default LED */
77807777246SWang Dongsheng 	marvell_config_led(phydev);
77907777246SWang Dongsheng 
78079be1a1cSClemens Gruber 	/* Set registers from marvell,reg-init DT property */
78110e24caaSMichal Simek 	return marvell_of_reg_init(phydev);
78210e24caaSMichal Simek }
78310e24caaSMichal Simek 
7846b358aedSSebastian Hesselbarth static int m88e3016_config_init(struct phy_device *phydev)
7856b358aedSSebastian Hesselbarth {
786fea23fb5SRussell King 	int ret;
7876b358aedSSebastian Hesselbarth 
7886b358aedSSebastian Hesselbarth 	/* Enable Scrambler and Auto-Crossover */
789fea23fb5SRussell King 	ret = phy_modify(phydev, MII_88E3016_PHY_SPEC_CTRL,
790f102852fSRussell King 			 MII_88E3016_DISABLE_SCRAMBLER,
791fea23fb5SRussell King 			 MII_88E3016_AUTO_MDIX_CROSSOVER);
792fea23fb5SRussell King 	if (ret < 0)
793fea23fb5SRussell King 		return ret;
7946b358aedSSebastian Hesselbarth 
79579be1a1cSClemens Gruber 	return marvell_config_init(phydev);
7966b358aedSSebastian Hesselbarth }
7976b358aedSSebastian Hesselbarth 
798865b813aSAndrew Lunn static int m88e1111_config_init_hwcfg_mode(struct phy_device *phydev,
799865b813aSAndrew Lunn 					   u16 mode,
800865b813aSAndrew Lunn 					   int fibre_copper_auto)
801865b813aSAndrew Lunn {
802865b813aSAndrew Lunn 	if (fibre_copper_auto)
803fea23fb5SRussell King 		mode |= MII_M1111_HWCFG_FIBER_COPPER_AUTO;
804865b813aSAndrew Lunn 
805fea23fb5SRussell King 	return phy_modify(phydev, MII_M1111_PHY_EXT_SR,
806f102852fSRussell King 			  MII_M1111_HWCFG_MODE_MASK |
807fea23fb5SRussell King 			  MII_M1111_HWCFG_FIBER_COPPER_AUTO |
808f102852fSRussell King 			  MII_M1111_HWCFG_FIBER_COPPER_RES,
809fea23fb5SRussell King 			  mode);
810865b813aSAndrew Lunn }
811865b813aSAndrew Lunn 
81261111598SAndrew Lunn static int m88e1111_config_init_rgmii_delays(struct phy_device *phydev)
813895ee682SKim Phillips {
814fea23fb5SRussell King 	int delay;
815895ee682SKim Phillips 
8169daf5a76SKim Phillips 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
817fea23fb5SRussell King 		delay = MII_M1111_RGMII_RX_DELAY | MII_M1111_RGMII_TX_DELAY;
8189daf5a76SKim Phillips 	} else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
819fea23fb5SRussell King 		delay = MII_M1111_RGMII_RX_DELAY;
8209daf5a76SKim Phillips 	} else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
821fea23fb5SRussell King 		delay = MII_M1111_RGMII_TX_DELAY;
822fea23fb5SRussell King 	} else {
823fea23fb5SRussell King 		delay = 0;
8249daf5a76SKim Phillips 	}
825895ee682SKim Phillips 
826fea23fb5SRussell King 	return phy_modify(phydev, MII_M1111_PHY_EXT_CR,
827f102852fSRussell King 			  MII_M1111_RGMII_RX_DELAY | MII_M1111_RGMII_TX_DELAY,
828fea23fb5SRussell King 			  delay);
82961111598SAndrew Lunn }
83061111598SAndrew Lunn 
83161111598SAndrew Lunn static int m88e1111_config_init_rgmii(struct phy_device *phydev)
83261111598SAndrew Lunn {
83361111598SAndrew Lunn 	int temp;
83461111598SAndrew Lunn 	int err;
83561111598SAndrew Lunn 
83661111598SAndrew Lunn 	err = m88e1111_config_init_rgmii_delays(phydev);
837895ee682SKim Phillips 	if (err < 0)
838895ee682SKim Phillips 		return err;
839895ee682SKim Phillips 
840895ee682SKim Phillips 	temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
841895ee682SKim Phillips 	if (temp < 0)
842895ee682SKim Phillips 		return temp;
843895ee682SKim Phillips 
844895ee682SKim Phillips 	temp &= ~(MII_M1111_HWCFG_MODE_MASK);
845be937f1fSAlexandr Smirnov 
8467239016dSWang Jian 	if (temp & MII_M1111_HWCFG_FIBER_COPPER_RES)
847be937f1fSAlexandr Smirnov 		temp |= MII_M1111_HWCFG_MODE_FIBER_RGMII;
848be937f1fSAlexandr Smirnov 	else
849be937f1fSAlexandr Smirnov 		temp |= MII_M1111_HWCFG_MODE_COPPER_RGMII;
850895ee682SKim Phillips 
851e1dde8dcSAndrew Lunn 	return phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
852895ee682SKim Phillips }
853895ee682SKim Phillips 
854e1dde8dcSAndrew Lunn static int m88e1111_config_init_sgmii(struct phy_device *phydev)
855e1dde8dcSAndrew Lunn {
856e1dde8dcSAndrew Lunn 	int err;
857e1dde8dcSAndrew Lunn 
858865b813aSAndrew Lunn 	err = m88e1111_config_init_hwcfg_mode(
859865b813aSAndrew Lunn 		phydev,
860865b813aSAndrew Lunn 		MII_M1111_HWCFG_MODE_SGMII_NO_CLK,
861865b813aSAndrew Lunn 		MII_M1111_HWCFG_FIBER_COPPER_AUTO);
8624117b5beSKapil Juneja 	if (err < 0)
8634117b5beSKapil Juneja 		return err;
86407151bc9SMadalin Bucur 
86507151bc9SMadalin Bucur 	/* make sure copper is selected */
86652295666SAndrew Lunn 	return marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
8674117b5beSKapil Juneja }
8684117b5beSKapil Juneja 
869e1dde8dcSAndrew Lunn static int m88e1111_config_init_rtbi(struct phy_device *phydev)
870e1dde8dcSAndrew Lunn {
87161111598SAndrew Lunn 	int err;
872e1dde8dcSAndrew Lunn 
87361111598SAndrew Lunn 	err = m88e1111_config_init_rgmii_delays(phydev);
874fea23fb5SRussell King 	if (err < 0)
8755f8cbc13SLiu Yu-B13201 		return err;
8765f8cbc13SLiu Yu-B13201 
877865b813aSAndrew Lunn 	err = m88e1111_config_init_hwcfg_mode(
878865b813aSAndrew Lunn 		phydev,
879865b813aSAndrew Lunn 		MII_M1111_HWCFG_MODE_RTBI,
880865b813aSAndrew Lunn 		MII_M1111_HWCFG_FIBER_COPPER_AUTO);
8815f8cbc13SLiu Yu-B13201 	if (err < 0)
8825f8cbc13SLiu Yu-B13201 		return err;
8835f8cbc13SLiu Yu-B13201 
8845f8cbc13SLiu Yu-B13201 	/* soft reset */
88534386344SAndrew Lunn 	err = genphy_soft_reset(phydev);
8865f8cbc13SLiu Yu-B13201 	if (err < 0)
8875f8cbc13SLiu Yu-B13201 		return err;
888e1dde8dcSAndrew Lunn 
889865b813aSAndrew Lunn 	return m88e1111_config_init_hwcfg_mode(
890865b813aSAndrew Lunn 		phydev,
891865b813aSAndrew Lunn 		MII_M1111_HWCFG_MODE_RTBI,
892865b813aSAndrew Lunn 		MII_M1111_HWCFG_FIBER_COPPER_AUTO);
893e1dde8dcSAndrew Lunn }
894e1dde8dcSAndrew Lunn 
8951887023aSRobert Hancock static int m88e1111_config_init_1000basex(struct phy_device *phydev)
8961887023aSRobert Hancock {
8971887023aSRobert Hancock 	int extsr = phy_read(phydev, MII_M1111_PHY_EXT_SR);
8981887023aSRobert Hancock 	int err, mode;
8991887023aSRobert Hancock 
9001887023aSRobert Hancock 	if (extsr < 0)
9011887023aSRobert Hancock 		return extsr;
9021887023aSRobert Hancock 
9031887023aSRobert Hancock 	/* If using copper mode, ensure 1000BaseX auto-negotiation is enabled */
9041887023aSRobert Hancock 	mode = extsr & MII_M1111_HWCFG_MODE_MASK;
9051887023aSRobert Hancock 	if (mode == MII_M1111_HWCFG_MODE_COPPER_1000X_NOAN) {
9061887023aSRobert Hancock 		err = phy_modify(phydev, MII_M1111_PHY_EXT_SR,
9071887023aSRobert Hancock 				 MII_M1111_HWCFG_MODE_MASK |
9081887023aSRobert Hancock 				 MII_M1111_HWCFG_SERIAL_AN_BYPASS,
9091887023aSRobert Hancock 				 MII_M1111_HWCFG_MODE_COPPER_1000X_AN |
9101887023aSRobert Hancock 				 MII_M1111_HWCFG_SERIAL_AN_BYPASS);
9111887023aSRobert Hancock 		if (err < 0)
9121887023aSRobert Hancock 			return err;
9131887023aSRobert Hancock 	}
9141887023aSRobert Hancock 	return 0;
9151887023aSRobert Hancock }
9161887023aSRobert Hancock 
917e1dde8dcSAndrew Lunn static int m88e1111_config_init(struct phy_device *phydev)
918e1dde8dcSAndrew Lunn {
919e1dde8dcSAndrew Lunn 	int err;
920e1dde8dcSAndrew Lunn 
921e1dde8dcSAndrew Lunn 	if (phy_interface_is_rgmii(phydev)) {
922e1dde8dcSAndrew Lunn 		err = m88e1111_config_init_rgmii(phydev);
923fea23fb5SRussell King 		if (err < 0)
924e1dde8dcSAndrew Lunn 			return err;
925e1dde8dcSAndrew Lunn 	}
926e1dde8dcSAndrew Lunn 
927e1dde8dcSAndrew Lunn 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
928e1dde8dcSAndrew Lunn 		err = m88e1111_config_init_sgmii(phydev);
929e1dde8dcSAndrew Lunn 		if (err < 0)
930e1dde8dcSAndrew Lunn 			return err;
931e1dde8dcSAndrew Lunn 	}
932e1dde8dcSAndrew Lunn 
933e1dde8dcSAndrew Lunn 	if (phydev->interface == PHY_INTERFACE_MODE_RTBI) {
934e1dde8dcSAndrew Lunn 		err = m88e1111_config_init_rtbi(phydev);
9355f8cbc13SLiu Yu-B13201 		if (err < 0)
9365f8cbc13SLiu Yu-B13201 			return err;
9375f8cbc13SLiu Yu-B13201 	}
9385f8cbc13SLiu Yu-B13201 
9391887023aSRobert Hancock 	if (phydev->interface == PHY_INTERFACE_MODE_1000BASEX) {
9401887023aSRobert Hancock 		err = m88e1111_config_init_1000basex(phydev);
9411887023aSRobert Hancock 		if (err < 0)
9421887023aSRobert Hancock 			return err;
9431887023aSRobert Hancock 	}
9441887023aSRobert Hancock 
945cf41a51dSDavid Daney 	err = marvell_of_reg_init(phydev);
946cf41a51dSDavid Daney 	if (err < 0)
947cf41a51dSDavid Daney 		return err;
9485f8cbc13SLiu Yu-B13201 
94934386344SAndrew Lunn 	return genphy_soft_reset(phydev);
950895ee682SKim Phillips }
951895ee682SKim Phillips 
9525c6bc519SHeiner Kallweit static int m88e1111_get_downshift(struct phy_device *phydev, u8 *data)
9535c6bc519SHeiner Kallweit {
9545c6bc519SHeiner Kallweit 	int val, cnt, enable;
9555c6bc519SHeiner Kallweit 
9565c6bc519SHeiner Kallweit 	val = phy_read(phydev, MII_M1111_PHY_EXT_CR);
9575c6bc519SHeiner Kallweit 	if (val < 0)
9585c6bc519SHeiner Kallweit 		return val;
9595c6bc519SHeiner Kallweit 
9605c6bc519SHeiner Kallweit 	enable = FIELD_GET(MII_M1111_PHY_EXT_CR_DOWNSHIFT_EN, val);
9615c6bc519SHeiner Kallweit 	cnt = FIELD_GET(MII_M1111_PHY_EXT_CR_DOWNSHIFT_MASK, val) + 1;
9625c6bc519SHeiner Kallweit 
9635c6bc519SHeiner Kallweit 	*data = enable ? cnt : DOWNSHIFT_DEV_DISABLE;
9645c6bc519SHeiner Kallweit 
9655c6bc519SHeiner Kallweit 	return 0;
9665c6bc519SHeiner Kallweit }
9675c6bc519SHeiner Kallweit 
9685c6bc519SHeiner Kallweit static int m88e1111_set_downshift(struct phy_device *phydev, u8 cnt)
9695c6bc519SHeiner Kallweit {
9705c6bc519SHeiner Kallweit 	int val;
9715c6bc519SHeiner Kallweit 
9725c6bc519SHeiner Kallweit 	if (cnt > MII_M1111_PHY_EXT_CR_DOWNSHIFT_MAX)
9735c6bc519SHeiner Kallweit 		return -E2BIG;
9745c6bc519SHeiner Kallweit 
9755c6bc519SHeiner Kallweit 	if (!cnt)
9765c6bc519SHeiner Kallweit 		return phy_clear_bits(phydev, MII_M1111_PHY_EXT_CR,
9775c6bc519SHeiner Kallweit 				      MII_M1111_PHY_EXT_CR_DOWNSHIFT_EN);
9785c6bc519SHeiner Kallweit 
9795c6bc519SHeiner Kallweit 	val = MII_M1111_PHY_EXT_CR_DOWNSHIFT_EN;
9805c6bc519SHeiner Kallweit 	val |= FIELD_PREP(MII_M1111_PHY_EXT_CR_DOWNSHIFT_MASK, cnt - 1);
9815c6bc519SHeiner Kallweit 
9825c6bc519SHeiner Kallweit 	return phy_modify(phydev, MII_M1111_PHY_EXT_CR,
9835c6bc519SHeiner Kallweit 			  MII_M1111_PHY_EXT_CR_DOWNSHIFT_EN |
9845c6bc519SHeiner Kallweit 			  MII_M1111_PHY_EXT_CR_DOWNSHIFT_MASK,
9855c6bc519SHeiner Kallweit 			  val);
9865c6bc519SHeiner Kallweit }
9875c6bc519SHeiner Kallweit 
9885c6bc519SHeiner Kallweit static int m88e1111_get_tunable(struct phy_device *phydev,
9895c6bc519SHeiner Kallweit 				struct ethtool_tunable *tuna, void *data)
9905c6bc519SHeiner Kallweit {
9915c6bc519SHeiner Kallweit 	switch (tuna->id) {
9925c6bc519SHeiner Kallweit 	case ETHTOOL_PHY_DOWNSHIFT:
9935c6bc519SHeiner Kallweit 		return m88e1111_get_downshift(phydev, data);
9945c6bc519SHeiner Kallweit 	default:
9955c6bc519SHeiner Kallweit 		return -EOPNOTSUPP;
9965c6bc519SHeiner Kallweit 	}
9975c6bc519SHeiner Kallweit }
9985c6bc519SHeiner Kallweit 
9995c6bc519SHeiner Kallweit static int m88e1111_set_tunable(struct phy_device *phydev,
10005c6bc519SHeiner Kallweit 				struct ethtool_tunable *tuna, const void *data)
10015c6bc519SHeiner Kallweit {
10025c6bc519SHeiner Kallweit 	switch (tuna->id) {
10035c6bc519SHeiner Kallweit 	case ETHTOOL_PHY_DOWNSHIFT:
10045c6bc519SHeiner Kallweit 		return m88e1111_set_downshift(phydev, *(const u8 *)data);
10055c6bc519SHeiner Kallweit 	default:
10065c6bc519SHeiner Kallweit 		return -EOPNOTSUPP;
10075c6bc519SHeiner Kallweit 	}
10085c6bc519SHeiner Kallweit }
10095c6bc519SHeiner Kallweit 
1010911af5e1SHeiner Kallweit static int m88e1011_get_downshift(struct phy_device *phydev, u8 *data)
1011a3bdfce7SHeiner Kallweit {
1012a3bdfce7SHeiner Kallweit 	int val, cnt, enable;
1013a3bdfce7SHeiner Kallweit 
1014a3bdfce7SHeiner Kallweit 	val = phy_read(phydev, MII_M1011_PHY_SCR);
1015a3bdfce7SHeiner Kallweit 	if (val < 0)
1016a3bdfce7SHeiner Kallweit 		return val;
1017a3bdfce7SHeiner Kallweit 
1018a3bdfce7SHeiner Kallweit 	enable = FIELD_GET(MII_M1011_PHY_SCR_DOWNSHIFT_EN, val);
1019f8d975beSHeiner Kallweit 	cnt = FIELD_GET(MII_M1011_PHY_SCR_DOWNSHIFT_MASK, val) + 1;
1020a3bdfce7SHeiner Kallweit 
1021a3bdfce7SHeiner Kallweit 	*data = enable ? cnt : DOWNSHIFT_DEV_DISABLE;
1022a3bdfce7SHeiner Kallweit 
1023a3bdfce7SHeiner Kallweit 	return 0;
1024a3bdfce7SHeiner Kallweit }
1025a3bdfce7SHeiner Kallweit 
1026911af5e1SHeiner Kallweit static int m88e1011_set_downshift(struct phy_device *phydev, u8 cnt)
1027a3bdfce7SHeiner Kallweit {
1028a3bdfce7SHeiner Kallweit 	int val;
1029a3bdfce7SHeiner Kallweit 
1030a3bdfce7SHeiner Kallweit 	if (cnt > MII_M1011_PHY_SCR_DOWNSHIFT_MAX)
1031a3bdfce7SHeiner Kallweit 		return -E2BIG;
1032a3bdfce7SHeiner Kallweit 
1033a3bdfce7SHeiner Kallweit 	if (!cnt)
1034a3bdfce7SHeiner Kallweit 		return phy_clear_bits(phydev, MII_M1011_PHY_SCR,
1035a3bdfce7SHeiner Kallweit 				      MII_M1011_PHY_SCR_DOWNSHIFT_EN);
1036a3bdfce7SHeiner Kallweit 
1037a3bdfce7SHeiner Kallweit 	val = MII_M1011_PHY_SCR_DOWNSHIFT_EN;
1038f8d975beSHeiner Kallweit 	val |= FIELD_PREP(MII_M1011_PHY_SCR_DOWNSHIFT_MASK, cnt - 1);
1039a3bdfce7SHeiner Kallweit 
1040a3bdfce7SHeiner Kallweit 	return phy_modify(phydev, MII_M1011_PHY_SCR,
1041a3bdfce7SHeiner Kallweit 			  MII_M1011_PHY_SCR_DOWNSHIFT_EN |
1042f8d975beSHeiner Kallweit 			  MII_M1011_PHY_SCR_DOWNSHIFT_MASK,
1043a3bdfce7SHeiner Kallweit 			  val);
1044a3bdfce7SHeiner Kallweit }
1045a3bdfce7SHeiner Kallweit 
1046911af5e1SHeiner Kallweit static int m88e1011_get_tunable(struct phy_device *phydev,
1047a3bdfce7SHeiner Kallweit 				struct ethtool_tunable *tuna, void *data)
1048a3bdfce7SHeiner Kallweit {
1049a3bdfce7SHeiner Kallweit 	switch (tuna->id) {
1050a3bdfce7SHeiner Kallweit 	case ETHTOOL_PHY_DOWNSHIFT:
1051911af5e1SHeiner Kallweit 		return m88e1011_get_downshift(phydev, data);
1052a3bdfce7SHeiner Kallweit 	default:
1053a3bdfce7SHeiner Kallweit 		return -EOPNOTSUPP;
1054a3bdfce7SHeiner Kallweit 	}
1055a3bdfce7SHeiner Kallweit }
1056a3bdfce7SHeiner Kallweit 
1057911af5e1SHeiner Kallweit static int m88e1011_set_tunable(struct phy_device *phydev,
1058a3bdfce7SHeiner Kallweit 				struct ethtool_tunable *tuna, const void *data)
1059a3bdfce7SHeiner Kallweit {
1060a3bdfce7SHeiner Kallweit 	switch (tuna->id) {
1061a3bdfce7SHeiner Kallweit 	case ETHTOOL_PHY_DOWNSHIFT:
1062911af5e1SHeiner Kallweit 		return m88e1011_set_downshift(phydev, *(const u8 *)data);
1063a3bdfce7SHeiner Kallweit 	default:
1064a3bdfce7SHeiner Kallweit 		return -EOPNOTSUPP;
1065a3bdfce7SHeiner Kallweit 	}
1066a3bdfce7SHeiner Kallweit }
1067a3bdfce7SHeiner Kallweit 
1068e2d861ccSHeiner Kallweit static int m88e1116r_config_init(struct phy_device *phydev)
1069e2d861ccSHeiner Kallweit {
1070e2d861ccSHeiner Kallweit 	int err;
1071e2d861ccSHeiner Kallweit 
1072e2d861ccSHeiner Kallweit 	err = genphy_soft_reset(phydev);
1073e2d861ccSHeiner Kallweit 	if (err < 0)
1074e2d861ccSHeiner Kallweit 		return err;
1075e2d861ccSHeiner Kallweit 
1076e2d861ccSHeiner Kallweit 	msleep(500);
1077e2d861ccSHeiner Kallweit 
1078e2d861ccSHeiner Kallweit 	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
1079e2d861ccSHeiner Kallweit 	if (err < 0)
1080e2d861ccSHeiner Kallweit 		return err;
1081e2d861ccSHeiner Kallweit 
1082e2d861ccSHeiner Kallweit 	err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
1083e2d861ccSHeiner Kallweit 	if (err < 0)
1084e2d861ccSHeiner Kallweit 		return err;
1085e2d861ccSHeiner Kallweit 
1086911af5e1SHeiner Kallweit 	err = m88e1011_set_downshift(phydev, 8);
1087e2d861ccSHeiner Kallweit 	if (err < 0)
1088e2d861ccSHeiner Kallweit 		return err;
1089e2d861ccSHeiner Kallweit 
1090e2d861ccSHeiner Kallweit 	if (phy_interface_is_rgmii(phydev)) {
1091e2d861ccSHeiner Kallweit 		err = m88e1121_config_aneg_rgmii_delays(phydev);
1092e2d861ccSHeiner Kallweit 		if (err < 0)
1093e2d861ccSHeiner Kallweit 			return err;
1094e2d861ccSHeiner Kallweit 	}
1095e2d861ccSHeiner Kallweit 
1096e2d861ccSHeiner Kallweit 	err = genphy_soft_reset(phydev);
1097e2d861ccSHeiner Kallweit 	if (err < 0)
1098e2d861ccSHeiner Kallweit 		return err;
1099e2d861ccSHeiner Kallweit 
1100e2d861ccSHeiner Kallweit 	return marvell_config_init(phydev);
1101e2d861ccSHeiner Kallweit }
1102e2d861ccSHeiner Kallweit 
1103dd9a122aSEsben Haabendal static int m88e1318_config_init(struct phy_device *phydev)
1104dd9a122aSEsben Haabendal {
1105dd9a122aSEsben Haabendal 	if (phy_interrupt_is_valid(phydev)) {
1106dd9a122aSEsben Haabendal 		int err = phy_modify_paged(
1107dd9a122aSEsben Haabendal 			phydev, MII_MARVELL_LED_PAGE,
1108dd9a122aSEsben Haabendal 			MII_88E1318S_PHY_LED_TCR,
1109dd9a122aSEsben Haabendal 			MII_88E1318S_PHY_LED_TCR_FORCE_INT,
1110dd9a122aSEsben Haabendal 			MII_88E1318S_PHY_LED_TCR_INTn_ENABLE |
1111dd9a122aSEsben Haabendal 			MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW);
1112dd9a122aSEsben Haabendal 		if (err < 0)
1113dd9a122aSEsben Haabendal 			return err;
1114dd9a122aSEsben Haabendal 	}
1115dd9a122aSEsben Haabendal 
111607777246SWang Dongsheng 	return marvell_config_init(phydev);
1117dd9a122aSEsben Haabendal }
1118dd9a122aSEsben Haabendal 
1119407353ecSClemens Gruber static int m88e1510_config_init(struct phy_device *phydev)
1120407353ecSClemens Gruber {
1121407353ecSClemens Gruber 	int err;
1122407353ecSClemens Gruber 
1123407353ecSClemens Gruber 	/* SGMII-to-Copper mode initialization */
1124407353ecSClemens Gruber 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
1125407353ecSClemens Gruber 		/* Select page 18 */
11266427bb2dSAndrew Lunn 		err = marvell_set_page(phydev, 18);
1127407353ecSClemens Gruber 		if (err < 0)
1128407353ecSClemens Gruber 			return err;
1129407353ecSClemens Gruber 
1130407353ecSClemens Gruber 		/* In reg 20, write MODE[2:0] = 0x1 (SGMII to Copper) */
1131fea23fb5SRussell King 		err = phy_modify(phydev, MII_88E1510_GEN_CTRL_REG_1,
1132f102852fSRussell King 				 MII_88E1510_GEN_CTRL_REG_1_MODE_MASK,
1133fea23fb5SRussell King 				 MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII);
1134407353ecSClemens Gruber 		if (err < 0)
1135407353ecSClemens Gruber 			return err;
1136407353ecSClemens Gruber 
1137407353ecSClemens Gruber 		/* PHY reset is necessary after changing MODE[2:0] */
1138832913c3SYejune Deng 		err = phy_set_bits(phydev, MII_88E1510_GEN_CTRL_REG_1,
1139fea23fb5SRussell King 				   MII_88E1510_GEN_CTRL_REG_1_RESET);
1140407353ecSClemens Gruber 		if (err < 0)
1141407353ecSClemens Gruber 			return err;
1142407353ecSClemens Gruber 
1143407353ecSClemens Gruber 		/* Reset page selection */
114452295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
1145407353ecSClemens Gruber 		if (err < 0)
1146407353ecSClemens Gruber 			return err;
1147407353ecSClemens Gruber 	}
1148407353ecSClemens Gruber 
1149dd9a122aSEsben Haabendal 	return m88e1318_config_init(phydev);
1150407353ecSClemens Gruber }
1151407353ecSClemens Gruber 
1152605f196eSRon Madrid static int m88e1118_config_aneg(struct phy_device *phydev)
1153605f196eSRon Madrid {
1154605f196eSRon Madrid 	int err;
1155605f196eSRon Madrid 
115634386344SAndrew Lunn 	err = genphy_soft_reset(phydev);
1157605f196eSRon Madrid 	if (err < 0)
1158605f196eSRon Madrid 		return err;
1159605f196eSRon Madrid 
1160fecd5e91SAndrew Lunn 	err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
1161605f196eSRon Madrid 	if (err < 0)
1162605f196eSRon Madrid 		return err;
1163605f196eSRon Madrid 
1164605f196eSRon Madrid 	err = genphy_config_aneg(phydev);
1165605f196eSRon Madrid 	return 0;
1166605f196eSRon Madrid }
1167605f196eSRon Madrid 
1168605f196eSRon Madrid static int m88e1118_config_init(struct phy_device *phydev)
1169605f196eSRon Madrid {
1170605f196eSRon Madrid 	int err;
1171605f196eSRon Madrid 
1172605f196eSRon Madrid 	/* Change address */
117352295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_MSCR_PAGE);
1174605f196eSRon Madrid 	if (err < 0)
1175605f196eSRon Madrid 		return err;
1176605f196eSRon Madrid 
1177605f196eSRon Madrid 	/* Enable 1000 Mbit */
1178605f196eSRon Madrid 	err = phy_write(phydev, 0x15, 0x1070);
1179605f196eSRon Madrid 	if (err < 0)
1180605f196eSRon Madrid 		return err;
1181605f196eSRon Madrid 
1182605f196eSRon Madrid 	/* Change address */
118352295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_LED_PAGE);
1184605f196eSRon Madrid 	if (err < 0)
1185605f196eSRon Madrid 		return err;
1186605f196eSRon Madrid 
1187605f196eSRon Madrid 	/* Adjust LED Control */
11882f495c39SBenjamin Herrenschmidt 	if (phydev->dev_flags & MARVELL_PHY_M1118_DNS323_LEDS)
11892f495c39SBenjamin Herrenschmidt 		err = phy_write(phydev, 0x10, 0x1100);
11902f495c39SBenjamin Herrenschmidt 	else
1191605f196eSRon Madrid 		err = phy_write(phydev, 0x10, 0x021e);
1192605f196eSRon Madrid 	if (err < 0)
1193605f196eSRon Madrid 		return err;
1194605f196eSRon Madrid 
1195cf41a51dSDavid Daney 	err = marvell_of_reg_init(phydev);
1196cf41a51dSDavid Daney 	if (err < 0)
1197cf41a51dSDavid Daney 		return err;
1198cf41a51dSDavid Daney 
1199605f196eSRon Madrid 	/* Reset address */
120052295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
1201605f196eSRon Madrid 	if (err < 0)
1202605f196eSRon Madrid 		return err;
1203605f196eSRon Madrid 
120434386344SAndrew Lunn 	return genphy_soft_reset(phydev);
1205605f196eSRon Madrid }
1206605f196eSRon Madrid 
120790600732SDavid Daney static int m88e1149_config_init(struct phy_device *phydev)
120890600732SDavid Daney {
120990600732SDavid Daney 	int err;
121090600732SDavid Daney 
121190600732SDavid Daney 	/* Change address */
121252295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_MSCR_PAGE);
121390600732SDavid Daney 	if (err < 0)
121490600732SDavid Daney 		return err;
121590600732SDavid Daney 
121690600732SDavid Daney 	/* Enable 1000 Mbit */
121790600732SDavid Daney 	err = phy_write(phydev, 0x15, 0x1048);
121890600732SDavid Daney 	if (err < 0)
121990600732SDavid Daney 		return err;
122090600732SDavid Daney 
1221cf41a51dSDavid Daney 	err = marvell_of_reg_init(phydev);
1222cf41a51dSDavid Daney 	if (err < 0)
1223cf41a51dSDavid Daney 		return err;
1224cf41a51dSDavid Daney 
122590600732SDavid Daney 	/* Reset address */
122652295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
122790600732SDavid Daney 	if (err < 0)
122890600732SDavid Daney 		return err;
122990600732SDavid Daney 
123034386344SAndrew Lunn 	return genphy_soft_reset(phydev);
123190600732SDavid Daney }
123290600732SDavid Daney 
1233e1dde8dcSAndrew Lunn static int m88e1145_config_init_rgmii(struct phy_device *phydev)
123476884679SAndy Fleming {
123576884679SAndy Fleming 	int err;
1236e69d9ed4SAndrew Lunn 
123761111598SAndrew Lunn 	err = m88e1111_config_init_rgmii_delays(phydev);
123876884679SAndy Fleming 	if (err < 0)
123976884679SAndy Fleming 		return err;
124076884679SAndy Fleming 
12412f495c39SBenjamin Herrenschmidt 	if (phydev->dev_flags & MARVELL_PHY_M1145_FLAGS_RESISTANCE) {
124276884679SAndy Fleming 		err = phy_write(phydev, 0x1d, 0x0012);
124376884679SAndy Fleming 		if (err < 0)
124476884679SAndy Fleming 			return err;
124576884679SAndy Fleming 
1246f102852fSRussell King 		err = phy_modify(phydev, 0x1e, 0x0fc0,
1247fea23fb5SRussell King 				 2 << 9 | /* 36 ohm */
1248fea23fb5SRussell King 				 2 << 6); /* 39 ohm */
124976884679SAndy Fleming 		if (err < 0)
125076884679SAndy Fleming 			return err;
125176884679SAndy Fleming 
125276884679SAndy Fleming 		err = phy_write(phydev, 0x1d, 0x3);
125376884679SAndy Fleming 		if (err < 0)
125476884679SAndy Fleming 			return err;
125576884679SAndy Fleming 
125676884679SAndy Fleming 		err = phy_write(phydev, 0x1e, 0x8000);
1257e1dde8dcSAndrew Lunn 	}
125876884679SAndy Fleming 	return err;
125976884679SAndy Fleming }
126076884679SAndy Fleming 
1261e1dde8dcSAndrew Lunn static int m88e1145_config_init_sgmii(struct phy_device *phydev)
1262e1dde8dcSAndrew Lunn {
1263865b813aSAndrew Lunn 	return m88e1111_config_init_hwcfg_mode(
1264865b813aSAndrew Lunn 		phydev, MII_M1111_HWCFG_MODE_SGMII_NO_CLK,
1265865b813aSAndrew Lunn 		MII_M1111_HWCFG_FIBER_COPPER_AUTO);
1266e1dde8dcSAndrew Lunn }
1267e1dde8dcSAndrew Lunn 
1268e1dde8dcSAndrew Lunn static int m88e1145_config_init(struct phy_device *phydev)
1269e1dde8dcSAndrew Lunn {
1270e1dde8dcSAndrew Lunn 	int err;
1271e1dde8dcSAndrew Lunn 
1272e1dde8dcSAndrew Lunn 	/* Take care of errata E0 & E1 */
1273e1dde8dcSAndrew Lunn 	err = phy_write(phydev, 0x1d, 0x001b);
1274e1dde8dcSAndrew Lunn 	if (err < 0)
1275e1dde8dcSAndrew Lunn 		return err;
1276e1dde8dcSAndrew Lunn 
1277e1dde8dcSAndrew Lunn 	err = phy_write(phydev, 0x1e, 0x418f);
1278e1dde8dcSAndrew Lunn 	if (err < 0)
1279e1dde8dcSAndrew Lunn 		return err;
1280e1dde8dcSAndrew Lunn 
1281e1dde8dcSAndrew Lunn 	err = phy_write(phydev, 0x1d, 0x0016);
1282e1dde8dcSAndrew Lunn 	if (err < 0)
1283e1dde8dcSAndrew Lunn 		return err;
1284e1dde8dcSAndrew Lunn 
1285e1dde8dcSAndrew Lunn 	err = phy_write(phydev, 0x1e, 0xa2da);
1286e1dde8dcSAndrew Lunn 	if (err < 0)
1287e1dde8dcSAndrew Lunn 		return err;
1288e1dde8dcSAndrew Lunn 
1289e1dde8dcSAndrew Lunn 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
1290e1dde8dcSAndrew Lunn 		err = m88e1145_config_init_rgmii(phydev);
1291e1dde8dcSAndrew Lunn 		if (err < 0)
1292e1dde8dcSAndrew Lunn 			return err;
1293e1dde8dcSAndrew Lunn 	}
1294e1dde8dcSAndrew Lunn 
1295e1dde8dcSAndrew Lunn 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
1296e1dde8dcSAndrew Lunn 		err = m88e1145_config_init_sgmii(phydev);
1297b0224175SViet Nga Dao 		if (err < 0)
1298b0224175SViet Nga Dao 			return err;
1299b0224175SViet Nga Dao 	}
1300b0224175SViet Nga Dao 
1301cf41a51dSDavid Daney 	err = marvell_of_reg_init(phydev);
1302cf41a51dSDavid Daney 	if (err < 0)
1303cf41a51dSDavid Daney 		return err;
1304cf41a51dSDavid Daney 
130576884679SAndy Fleming 	return 0;
130676884679SAndy Fleming }
130700db8189SAndy Fleming 
130869f42be8SHeiner Kallweit static int m88e1540_get_fld(struct phy_device *phydev, u8 *msecs)
130969f42be8SHeiner Kallweit {
131069f42be8SHeiner Kallweit 	int val;
131169f42be8SHeiner Kallweit 
131269f42be8SHeiner Kallweit 	val = phy_read(phydev, MII_88E1540_COPPER_CTRL3);
131369f42be8SHeiner Kallweit 	if (val < 0)
131469f42be8SHeiner Kallweit 		return val;
131569f42be8SHeiner Kallweit 
131669f42be8SHeiner Kallweit 	if (!(val & MII_88E1540_COPPER_CTRL3_FAST_LINK_DOWN)) {
131769f42be8SHeiner Kallweit 		*msecs = ETHTOOL_PHY_FAST_LINK_DOWN_OFF;
131869f42be8SHeiner Kallweit 		return 0;
131969f42be8SHeiner Kallweit 	}
132069f42be8SHeiner Kallweit 
132169f42be8SHeiner Kallweit 	val = FIELD_GET(MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_MASK, val);
132269f42be8SHeiner Kallweit 
132369f42be8SHeiner Kallweit 	switch (val) {
132469f42be8SHeiner Kallweit 	case MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_00MS:
132569f42be8SHeiner Kallweit 		*msecs = 0;
132669f42be8SHeiner Kallweit 		break;
132769f42be8SHeiner Kallweit 	case MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_10MS:
132869f42be8SHeiner Kallweit 		*msecs = 10;
132969f42be8SHeiner Kallweit 		break;
133069f42be8SHeiner Kallweit 	case MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_20MS:
133169f42be8SHeiner Kallweit 		*msecs = 20;
133269f42be8SHeiner Kallweit 		break;
133369f42be8SHeiner Kallweit 	case MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_40MS:
133469f42be8SHeiner Kallweit 		*msecs = 40;
133569f42be8SHeiner Kallweit 		break;
133669f42be8SHeiner Kallweit 	default:
133769f42be8SHeiner Kallweit 		return -EINVAL;
133869f42be8SHeiner Kallweit 	}
133969f42be8SHeiner Kallweit 
134069f42be8SHeiner Kallweit 	return 0;
134169f42be8SHeiner Kallweit }
134269f42be8SHeiner Kallweit 
134369f42be8SHeiner Kallweit static int m88e1540_set_fld(struct phy_device *phydev, const u8 *msecs)
134469f42be8SHeiner Kallweit {
134569f42be8SHeiner Kallweit 	struct ethtool_eee eee;
134669f42be8SHeiner Kallweit 	int val, ret;
134769f42be8SHeiner Kallweit 
134869f42be8SHeiner Kallweit 	if (*msecs == ETHTOOL_PHY_FAST_LINK_DOWN_OFF)
134969f42be8SHeiner Kallweit 		return phy_clear_bits(phydev, MII_88E1540_COPPER_CTRL3,
135069f42be8SHeiner Kallweit 				      MII_88E1540_COPPER_CTRL3_FAST_LINK_DOWN);
135169f42be8SHeiner Kallweit 
135269f42be8SHeiner Kallweit 	/* According to the Marvell data sheet EEE must be disabled for
135369f42be8SHeiner Kallweit 	 * Fast Link Down detection to work properly
135469f42be8SHeiner Kallweit 	 */
135569f42be8SHeiner Kallweit 	ret = phy_ethtool_get_eee(phydev, &eee);
135669f42be8SHeiner Kallweit 	if (!ret && eee.eee_enabled) {
135769f42be8SHeiner Kallweit 		phydev_warn(phydev, "Fast Link Down detection requires EEE to be disabled!\n");
135869f42be8SHeiner Kallweit 		return -EBUSY;
135969f42be8SHeiner Kallweit 	}
136069f42be8SHeiner Kallweit 
136169f42be8SHeiner Kallweit 	if (*msecs <= 5)
136269f42be8SHeiner Kallweit 		val = MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_00MS;
136369f42be8SHeiner Kallweit 	else if (*msecs <= 15)
136469f42be8SHeiner Kallweit 		val = MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_10MS;
136569f42be8SHeiner Kallweit 	else if (*msecs <= 30)
136669f42be8SHeiner Kallweit 		val = MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_20MS;
136769f42be8SHeiner Kallweit 	else
136869f42be8SHeiner Kallweit 		val = MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_40MS;
136969f42be8SHeiner Kallweit 
137069f42be8SHeiner Kallweit 	val = FIELD_PREP(MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_MASK, val);
137169f42be8SHeiner Kallweit 
137269f42be8SHeiner Kallweit 	ret = phy_modify(phydev, MII_88E1540_COPPER_CTRL3,
137369f42be8SHeiner Kallweit 			 MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_MASK, val);
137469f42be8SHeiner Kallweit 	if (ret)
137569f42be8SHeiner Kallweit 		return ret;
137669f42be8SHeiner Kallweit 
137769f42be8SHeiner Kallweit 	return phy_set_bits(phydev, MII_88E1540_COPPER_CTRL3,
137869f42be8SHeiner Kallweit 			    MII_88E1540_COPPER_CTRL3_FAST_LINK_DOWN);
137969f42be8SHeiner Kallweit }
138069f42be8SHeiner Kallweit 
138169f42be8SHeiner Kallweit static int m88e1540_get_tunable(struct phy_device *phydev,
138269f42be8SHeiner Kallweit 				struct ethtool_tunable *tuna, void *data)
138369f42be8SHeiner Kallweit {
138469f42be8SHeiner Kallweit 	switch (tuna->id) {
138569f42be8SHeiner Kallweit 	case ETHTOOL_PHY_FAST_LINK_DOWN:
138669f42be8SHeiner Kallweit 		return m88e1540_get_fld(phydev, data);
1387a3bdfce7SHeiner Kallweit 	case ETHTOOL_PHY_DOWNSHIFT:
1388911af5e1SHeiner Kallweit 		return m88e1011_get_downshift(phydev, data);
138969f42be8SHeiner Kallweit 	default:
139069f42be8SHeiner Kallweit 		return -EOPNOTSUPP;
139169f42be8SHeiner Kallweit 	}
139269f42be8SHeiner Kallweit }
139369f42be8SHeiner Kallweit 
139469f42be8SHeiner Kallweit static int m88e1540_set_tunable(struct phy_device *phydev,
139569f42be8SHeiner Kallweit 				struct ethtool_tunable *tuna, const void *data)
139669f42be8SHeiner Kallweit {
139769f42be8SHeiner Kallweit 	switch (tuna->id) {
139869f42be8SHeiner Kallweit 	case ETHTOOL_PHY_FAST_LINK_DOWN:
139969f42be8SHeiner Kallweit 		return m88e1540_set_fld(phydev, data);
1400a3bdfce7SHeiner Kallweit 	case ETHTOOL_PHY_DOWNSHIFT:
1401911af5e1SHeiner Kallweit 		return m88e1011_set_downshift(phydev, *(const u8 *)data);
140269f42be8SHeiner Kallweit 	default:
140369f42be8SHeiner Kallweit 		return -EOPNOTSUPP;
140469f42be8SHeiner Kallweit 	}
140569f42be8SHeiner Kallweit }
140669f42be8SHeiner Kallweit 
14078cbcdc1aSAndrew Lunn /* The VOD can be out of specification on link up. Poke an
14088cbcdc1aSAndrew Lunn  * undocumented register, in an undocumented page, with a magic value
14098cbcdc1aSAndrew Lunn  * to fix this.
14108cbcdc1aSAndrew Lunn  */
14118cbcdc1aSAndrew Lunn static int m88e6390_errata(struct phy_device *phydev)
14128cbcdc1aSAndrew Lunn {
14138cbcdc1aSAndrew Lunn 	int err;
14148cbcdc1aSAndrew Lunn 
14158cbcdc1aSAndrew Lunn 	err = phy_write(phydev, MII_BMCR,
14168cbcdc1aSAndrew Lunn 			BMCR_ANENABLE | BMCR_SPEED1000 | BMCR_FULLDPLX);
14178cbcdc1aSAndrew Lunn 	if (err)
14188cbcdc1aSAndrew Lunn 		return err;
14198cbcdc1aSAndrew Lunn 
14208cbcdc1aSAndrew Lunn 	usleep_range(300, 400);
14218cbcdc1aSAndrew Lunn 
14228cbcdc1aSAndrew Lunn 	err = phy_write_paged(phydev, 0xf8, 0x08, 0x36);
14238cbcdc1aSAndrew Lunn 	if (err)
14248cbcdc1aSAndrew Lunn 		return err;
14258cbcdc1aSAndrew Lunn 
14268cbcdc1aSAndrew Lunn 	return genphy_soft_reset(phydev);
14278cbcdc1aSAndrew Lunn }
14288cbcdc1aSAndrew Lunn 
14298cbcdc1aSAndrew Lunn static int m88e6390_config_aneg(struct phy_device *phydev)
14308cbcdc1aSAndrew Lunn {
14318cbcdc1aSAndrew Lunn 	int err;
14328cbcdc1aSAndrew Lunn 
14338cbcdc1aSAndrew Lunn 	err = m88e6390_errata(phydev);
14348cbcdc1aSAndrew Lunn 	if (err)
14358cbcdc1aSAndrew Lunn 		return err;
14368cbcdc1aSAndrew Lunn 
14378cbcdc1aSAndrew Lunn 	return m88e1510_config_aneg(phydev);
14388cbcdc1aSAndrew Lunn }
14398cbcdc1aSAndrew Lunn 
14406cfb3bccSCharles-Antoine Couret /**
1441ab9cb729SAndrew Lunn  * fiber_lpa_mod_linkmode_lpa_t
1442c0ec3c27SAndrew Lunn  * @advertising: the linkmode advertisement settings
14436cfb3bccSCharles-Antoine Couret  * @lpa: value of the MII_LPA register for fiber link
1444be937f1fSAlexandr Smirnov  *
1445ab9cb729SAndrew Lunn  * A small helper function that translates MII_LPA bits to linkmode LP
1446ab9cb729SAndrew Lunn  * advertisement settings. Other bits in advertising are left
1447ab9cb729SAndrew Lunn  * unchanged.
14486cfb3bccSCharles-Antoine Couret  */
1449ab9cb729SAndrew Lunn static void fiber_lpa_mod_linkmode_lpa_t(unsigned long *advertising, u32 lpa)
14506cfb3bccSCharles-Antoine Couret {
1451ab9cb729SAndrew Lunn 	linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
145220ecf424SRussell King 			 advertising, lpa & LPA_1000XHALF);
1453ab9cb729SAndrew Lunn 
1454ab9cb729SAndrew Lunn 	linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
145520ecf424SRussell King 			 advertising, lpa & LPA_1000XFULL);
14566cfb3bccSCharles-Antoine Couret }
14576cfb3bccSCharles-Antoine Couret 
1458e1dde8dcSAndrew Lunn static int marvell_read_status_page_an(struct phy_device *phydev,
1459d2004e27SRussell King 				       int fiber, int status)
1460be937f1fSAlexandr Smirnov {
1461be937f1fSAlexandr Smirnov 	int lpa;
1462fcf1f59aSRussell King 	int err;
1463be937f1fSAlexandr Smirnov 
14643b72f84fSClemens Gruber 	if (!(status & MII_M1011_PHY_STATUS_RESOLVED)) {
14653b72f84fSClemens Gruber 		phydev->link = 0;
14663b72f84fSClemens Gruber 		return 0;
14673b72f84fSClemens Gruber 	}
14683b72f84fSClemens Gruber 
14693b72f84fSClemens Gruber 	if (status & MII_M1011_PHY_STATUS_FULLDUPLEX)
14703b72f84fSClemens Gruber 		phydev->duplex = DUPLEX_FULL;
14713b72f84fSClemens Gruber 	else
14723b72f84fSClemens Gruber 		phydev->duplex = DUPLEX_HALF;
14733b72f84fSClemens Gruber 
14743b72f84fSClemens Gruber 	switch (status & MII_M1011_PHY_STATUS_SPD_MASK) {
14753b72f84fSClemens Gruber 	case MII_M1011_PHY_STATUS_1000:
14763b72f84fSClemens Gruber 		phydev->speed = SPEED_1000;
14773b72f84fSClemens Gruber 		break;
14783b72f84fSClemens Gruber 
14793b72f84fSClemens Gruber 	case MII_M1011_PHY_STATUS_100:
14803b72f84fSClemens Gruber 		phydev->speed = SPEED_100;
14813b72f84fSClemens Gruber 		break;
14823b72f84fSClemens Gruber 
14833b72f84fSClemens Gruber 	default:
14843b72f84fSClemens Gruber 		phydev->speed = SPEED_10;
14853b72f84fSClemens Gruber 		break;
14863b72f84fSClemens Gruber 	}
14873b72f84fSClemens Gruber 
1488fcf1f59aSRussell King 	if (!fiber) {
1489fcf1f59aSRussell King 		err = genphy_read_lpa(phydev);
1490fcf1f59aSRussell King 		if (err < 0)
1491fcf1f59aSRussell King 			return err;
1492fcf1f59aSRussell King 
1493fcf1f59aSRussell King 		phy_resolve_aneg_pause(phydev);
1494fcf1f59aSRussell King 	} else {
1495be937f1fSAlexandr Smirnov 		lpa = phy_read(phydev, MII_LPA);
1496be937f1fSAlexandr Smirnov 		if (lpa < 0)
1497be937f1fSAlexandr Smirnov 			return lpa;
1498be937f1fSAlexandr Smirnov 
14996cfb3bccSCharles-Antoine Couret 		/* The fiber link is only 1000M capable */
1500ab9cb729SAndrew Lunn 		fiber_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa);
15016cfb3bccSCharles-Antoine Couret 
15026cfb3bccSCharles-Antoine Couret 		if (phydev->duplex == DUPLEX_FULL) {
15036cfb3bccSCharles-Antoine Couret 			if (!(lpa & LPA_PAUSE_FIBER)) {
15046cfb3bccSCharles-Antoine Couret 				phydev->pause = 0;
15056cfb3bccSCharles-Antoine Couret 				phydev->asym_pause = 0;
15066cfb3bccSCharles-Antoine Couret 			} else if ((lpa & LPA_PAUSE_ASYM_FIBER)) {
15076cfb3bccSCharles-Antoine Couret 				phydev->pause = 1;
15086cfb3bccSCharles-Antoine Couret 				phydev->asym_pause = 1;
15096cfb3bccSCharles-Antoine Couret 			} else {
15106cfb3bccSCharles-Antoine Couret 				phydev->pause = 1;
15116cfb3bccSCharles-Antoine Couret 				phydev->asym_pause = 0;
15126cfb3bccSCharles-Antoine Couret 			}
15136cfb3bccSCharles-Antoine Couret 		}
15146cfb3bccSCharles-Antoine Couret 	}
1515fcf1f59aSRussell King 
1516e1dde8dcSAndrew Lunn 	return 0;
1517e1dde8dcSAndrew Lunn }
1518e1dde8dcSAndrew Lunn 
1519e1dde8dcSAndrew Lunn /* marvell_read_status_page
1520e1dde8dcSAndrew Lunn  *
1521e1dde8dcSAndrew Lunn  * Description:
1522e1dde8dcSAndrew Lunn  *   Check the link, then figure out the current state
1523e1dde8dcSAndrew Lunn  *   by comparing what we advertise with what the link partner
1524e1dde8dcSAndrew Lunn  *   advertises.  Start by checking the gigabit possibilities,
1525e1dde8dcSAndrew Lunn  *   then move on to 10/100.
1526e1dde8dcSAndrew Lunn  */
1527e1dde8dcSAndrew Lunn static int marvell_read_status_page(struct phy_device *phydev, int page)
1528e1dde8dcSAndrew Lunn {
1529d2004e27SRussell King 	int status;
1530e1dde8dcSAndrew Lunn 	int fiber;
1531e1dde8dcSAndrew Lunn 	int err;
1532e1dde8dcSAndrew Lunn 
1533d2004e27SRussell King 	status = phy_read(phydev, MII_M1011_PHY_STATUS);
1534d2004e27SRussell King 	if (status < 0)
1535d2004e27SRussell King 		return status;
1536d2004e27SRussell King 
1537d2004e27SRussell King 	/* Use the generic register for copper link status,
1538d2004e27SRussell King 	 * and the PHY status register for fiber link status.
1539e1dde8dcSAndrew Lunn 	 */
1540d2004e27SRussell King 	if (page == MII_MARVELL_FIBER_PAGE) {
1541d2004e27SRussell King 		phydev->link = !!(status & MII_M1011_PHY_STATUS_LINK);
1542d2004e27SRussell King 	} else {
1543d2004e27SRussell King 		err = genphy_update_link(phydev);
1544d2004e27SRussell King 		if (err)
1545d2004e27SRussell King 			return err;
1546d2004e27SRussell King 	}
1547d2004e27SRussell King 
154852295666SAndrew Lunn 	if (page == MII_MARVELL_FIBER_PAGE)
1549e1dde8dcSAndrew Lunn 		fiber = 1;
1550e1dde8dcSAndrew Lunn 	else
1551e1dde8dcSAndrew Lunn 		fiber = 0;
1552e1dde8dcSAndrew Lunn 
155398f92831SRussell King 	linkmode_zero(phydev->lp_advertising);
155498f92831SRussell King 	phydev->pause = 0;
155598f92831SRussell King 	phydev->asym_pause = 0;
1556b82cf17fSRussell King 	phydev->speed = SPEED_UNKNOWN;
1557b82cf17fSRussell King 	phydev->duplex = DUPLEX_UNKNOWN;
15584217a64eSMichael Walle 	phydev->port = fiber ? PORT_FIBRE : PORT_TP;
155998f92831SRussell King 
1560e1dde8dcSAndrew Lunn 	if (phydev->autoneg == AUTONEG_ENABLE)
1561d2004e27SRussell King 		err = marvell_read_status_page_an(phydev, fiber, status);
1562e1dde8dcSAndrew Lunn 	else
156398f92831SRussell King 		err = genphy_read_status_fixed(phydev);
1564e1dde8dcSAndrew Lunn 
1565e1dde8dcSAndrew Lunn 	return err;
1566e1dde8dcSAndrew Lunn }
1567e1dde8dcSAndrew Lunn 
15686cfb3bccSCharles-Antoine Couret /* marvell_read_status
15696cfb3bccSCharles-Antoine Couret  *
15706cfb3bccSCharles-Antoine Couret  * Some Marvell's phys have two modes: fiber and copper.
15716cfb3bccSCharles-Antoine Couret  * Both need status checked.
15726cfb3bccSCharles-Antoine Couret  * Description:
15736cfb3bccSCharles-Antoine Couret  *   First, check the fiber link and status.
15746cfb3bccSCharles-Antoine Couret  *   If the fiber link is down, check the copper link and status which
15756cfb3bccSCharles-Antoine Couret  *   will be the default value if both link are down.
15766cfb3bccSCharles-Antoine Couret  */
15776cfb3bccSCharles-Antoine Couret static int marvell_read_status(struct phy_device *phydev)
15786cfb3bccSCharles-Antoine Couret {
15796cfb3bccSCharles-Antoine Couret 	int err;
15806cfb3bccSCharles-Antoine Couret 
15816cfb3bccSCharles-Antoine Couret 	/* Check the fiber mode first */
15823c1bcc86SAndrew Lunn 	if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
15833c1bcc86SAndrew Lunn 			      phydev->supported) &&
1584a13c0652SRussell King 	    phydev->interface != PHY_INTERFACE_MODE_SGMII) {
158552295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
15866cfb3bccSCharles-Antoine Couret 		if (err < 0)
15876cfb3bccSCharles-Antoine Couret 			goto error;
15886cfb3bccSCharles-Antoine Couret 
158952295666SAndrew Lunn 		err = marvell_read_status_page(phydev, MII_MARVELL_FIBER_PAGE);
15906cfb3bccSCharles-Antoine Couret 		if (err < 0)
15916cfb3bccSCharles-Antoine Couret 			goto error;
15926cfb3bccSCharles-Antoine Couret 
15930c3439bcSAndrew Lunn 		/* If the fiber link is up, it is the selected and
15940c3439bcSAndrew Lunn 		 * used link. In this case, we need to stay in the
15950c3439bcSAndrew Lunn 		 * fiber page. Please to be careful about that, avoid
15960c3439bcSAndrew Lunn 		 * to restore Copper page in other functions which
15970c3439bcSAndrew Lunn 		 * could break the behaviour for some fiber phy like
15980c3439bcSAndrew Lunn 		 * 88E1512.
15990c3439bcSAndrew Lunn 		 */
16006cfb3bccSCharles-Antoine Couret 		if (phydev->link)
16016cfb3bccSCharles-Antoine Couret 			return 0;
16026cfb3bccSCharles-Antoine Couret 
16036cfb3bccSCharles-Antoine Couret 		/* If fiber link is down, check and save copper mode state */
160452295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
16056cfb3bccSCharles-Antoine Couret 		if (err < 0)
16066cfb3bccSCharles-Antoine Couret 			goto error;
16076cfb3bccSCharles-Antoine Couret 	}
16086cfb3bccSCharles-Antoine Couret 
160952295666SAndrew Lunn 	return marvell_read_status_page(phydev, MII_MARVELL_COPPER_PAGE);
16106cfb3bccSCharles-Antoine Couret 
16116cfb3bccSCharles-Antoine Couret error:
161252295666SAndrew Lunn 	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
16136cfb3bccSCharles-Antoine Couret 	return err;
16146cfb3bccSCharles-Antoine Couret }
16153758be3dSCharles-Antoine Couret 
16163758be3dSCharles-Antoine Couret /* marvell_suspend
16173758be3dSCharles-Antoine Couret  *
16183758be3dSCharles-Antoine Couret  * Some Marvell's phys have two modes: fiber and copper.
16193758be3dSCharles-Antoine Couret  * Both need to be suspended
16203758be3dSCharles-Antoine Couret  */
16213758be3dSCharles-Antoine Couret static int marvell_suspend(struct phy_device *phydev)
16223758be3dSCharles-Antoine Couret {
16233758be3dSCharles-Antoine Couret 	int err;
16243758be3dSCharles-Antoine Couret 
16253758be3dSCharles-Antoine Couret 	/* Suspend the fiber mode first */
16263c1bcc86SAndrew Lunn 	if (!linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
16273c1bcc86SAndrew Lunn 			       phydev->supported)) {
162852295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
16293758be3dSCharles-Antoine Couret 		if (err < 0)
16303758be3dSCharles-Antoine Couret 			goto error;
16313758be3dSCharles-Antoine Couret 
16323758be3dSCharles-Antoine Couret 		/* With the page set, use the generic suspend */
16333758be3dSCharles-Antoine Couret 		err = genphy_suspend(phydev);
16343758be3dSCharles-Antoine Couret 		if (err < 0)
16353758be3dSCharles-Antoine Couret 			goto error;
16363758be3dSCharles-Antoine Couret 
16373758be3dSCharles-Antoine Couret 		/* Then, the copper link */
163852295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
16393758be3dSCharles-Antoine Couret 		if (err < 0)
16403758be3dSCharles-Antoine Couret 			goto error;
16413758be3dSCharles-Antoine Couret 	}
16423758be3dSCharles-Antoine Couret 
16433758be3dSCharles-Antoine Couret 	/* With the page set, use the generic suspend */
16443758be3dSCharles-Antoine Couret 	return genphy_suspend(phydev);
16453758be3dSCharles-Antoine Couret 
16463758be3dSCharles-Antoine Couret error:
164752295666SAndrew Lunn 	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
16483758be3dSCharles-Antoine Couret 	return err;
16493758be3dSCharles-Antoine Couret }
16503758be3dSCharles-Antoine Couret 
16513758be3dSCharles-Antoine Couret /* marvell_resume
16523758be3dSCharles-Antoine Couret  *
16533758be3dSCharles-Antoine Couret  * Some Marvell's phys have two modes: fiber and copper.
16543758be3dSCharles-Antoine Couret  * Both need to be resumed
16553758be3dSCharles-Antoine Couret  */
16563758be3dSCharles-Antoine Couret static int marvell_resume(struct phy_device *phydev)
16573758be3dSCharles-Antoine Couret {
16583758be3dSCharles-Antoine Couret 	int err;
16593758be3dSCharles-Antoine Couret 
16603758be3dSCharles-Antoine Couret 	/* Resume the fiber mode first */
16613c1bcc86SAndrew Lunn 	if (!linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
16623c1bcc86SAndrew Lunn 			       phydev->supported)) {
166352295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
16643758be3dSCharles-Antoine Couret 		if (err < 0)
16653758be3dSCharles-Antoine Couret 			goto error;
16663758be3dSCharles-Antoine Couret 
16673758be3dSCharles-Antoine Couret 		/* With the page set, use the generic resume */
16683758be3dSCharles-Antoine Couret 		err = genphy_resume(phydev);
16693758be3dSCharles-Antoine Couret 		if (err < 0)
16703758be3dSCharles-Antoine Couret 			goto error;
16713758be3dSCharles-Antoine Couret 
16723758be3dSCharles-Antoine Couret 		/* Then, the copper link */
167352295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
16743758be3dSCharles-Antoine Couret 		if (err < 0)
16753758be3dSCharles-Antoine Couret 			goto error;
16763758be3dSCharles-Antoine Couret 	}
16773758be3dSCharles-Antoine Couret 
16783758be3dSCharles-Antoine Couret 	/* With the page set, use the generic resume */
16793758be3dSCharles-Antoine Couret 	return genphy_resume(phydev);
16803758be3dSCharles-Antoine Couret 
16813758be3dSCharles-Antoine Couret error:
168252295666SAndrew Lunn 	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
16833758be3dSCharles-Antoine Couret 	return err;
16843758be3dSCharles-Antoine Couret }
16853758be3dSCharles-Antoine Couret 
16866b358aedSSebastian Hesselbarth static int marvell_aneg_done(struct phy_device *phydev)
16876b358aedSSebastian Hesselbarth {
16886b358aedSSebastian Hesselbarth 	int retval = phy_read(phydev, MII_M1011_PHY_STATUS);
1689e69d9ed4SAndrew Lunn 
16906b358aedSSebastian Hesselbarth 	return (retval < 0) ? retval : (retval & MII_M1011_PHY_STATUS_RESOLVED);
16916b358aedSSebastian Hesselbarth }
16926b358aedSSebastian Hesselbarth 
169323beb38fSAndrew Lunn static void m88e1318_get_wol(struct phy_device *phydev,
169423beb38fSAndrew Lunn 			     struct ethtool_wolinfo *wol)
16953871c387SMichael Stapelberg {
1696f4f9dcc3SJisheng Zhang 	int ret;
1697424ca4c5SRussell King 
16983871c387SMichael Stapelberg 	wol->supported = WAKE_MAGIC;
16993871c387SMichael Stapelberg 	wol->wolopts = 0;
17003871c387SMichael Stapelberg 
1701f4f9dcc3SJisheng Zhang 	ret = phy_read_paged(phydev, MII_MARVELL_WOL_PAGE,
1702f4f9dcc3SJisheng Zhang 			     MII_88E1318S_PHY_WOL_CTRL);
1703f4f9dcc3SJisheng Zhang 	if (ret >= 0 && ret & MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE)
17043871c387SMichael Stapelberg 		wol->wolopts |= WAKE_MAGIC;
17053871c387SMichael Stapelberg }
17063871c387SMichael Stapelberg 
170723beb38fSAndrew Lunn static int m88e1318_set_wol(struct phy_device *phydev,
170823beb38fSAndrew Lunn 			    struct ethtool_wolinfo *wol)
17093871c387SMichael Stapelberg {
1710424ca4c5SRussell King 	int err = 0, oldpage;
17113871c387SMichael Stapelberg 
1712424ca4c5SRussell King 	oldpage = phy_save_page(phydev);
1713424ca4c5SRussell King 	if (oldpage < 0)
1714424ca4c5SRussell King 		goto error;
17153871c387SMichael Stapelberg 
17163871c387SMichael Stapelberg 	if (wol->wolopts & WAKE_MAGIC) {
17173871c387SMichael Stapelberg 		/* Explicitly switch to page 0x00, just to be sure */
1718424ca4c5SRussell King 		err = marvell_write_page(phydev, MII_MARVELL_COPPER_PAGE);
17193871c387SMichael Stapelberg 		if (err < 0)
1720424ca4c5SRussell King 			goto error;
17213871c387SMichael Stapelberg 
1722b6a930faSJingju Hou 		/* If WOL event happened once, the LED[2] interrupt pin
1723b6a930faSJingju Hou 		 * will not be cleared unless we reading the interrupt status
1724b6a930faSJingju Hou 		 * register. If interrupts are in use, the normal interrupt
1725b6a930faSJingju Hou 		 * handling will clear the WOL event. Clear the WOL event
1726b6a930faSJingju Hou 		 * before enabling it if !phy_interrupt_is_valid()
1727b6a930faSJingju Hou 		 */
1728b6a930faSJingju Hou 		if (!phy_interrupt_is_valid(phydev))
1729e0a7328fSAndrew Lunn 			__phy_read(phydev, MII_M1011_IEVENT);
1730b6a930faSJingju Hou 
17313871c387SMichael Stapelberg 		/* Enable the WOL interrupt */
1732832913c3SYejune Deng 		err = __phy_set_bits(phydev, MII_88E1318S_PHY_CSIER,
1733424ca4c5SRussell King 				     MII_88E1318S_PHY_CSIER_WOL_EIE);
17343871c387SMichael Stapelberg 		if (err < 0)
1735424ca4c5SRussell King 			goto error;
17363871c387SMichael Stapelberg 
1737424ca4c5SRussell King 		err = marvell_write_page(phydev, MII_MARVELL_LED_PAGE);
17383871c387SMichael Stapelberg 		if (err < 0)
1739424ca4c5SRussell King 			goto error;
17403871c387SMichael Stapelberg 
17413871c387SMichael Stapelberg 		/* Setup LED[2] as interrupt pin (active low) */
1742424ca4c5SRussell King 		err = __phy_modify(phydev, MII_88E1318S_PHY_LED_TCR,
1743f102852fSRussell King 				   MII_88E1318S_PHY_LED_TCR_FORCE_INT,
1744424ca4c5SRussell King 				   MII_88E1318S_PHY_LED_TCR_INTn_ENABLE |
1745424ca4c5SRussell King 				   MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW);
17463871c387SMichael Stapelberg 		if (err < 0)
1747424ca4c5SRussell King 			goto error;
17483871c387SMichael Stapelberg 
1749424ca4c5SRussell King 		err = marvell_write_page(phydev, MII_MARVELL_WOL_PAGE);
17503871c387SMichael Stapelberg 		if (err < 0)
1751424ca4c5SRussell King 			goto error;
17523871c387SMichael Stapelberg 
17533871c387SMichael Stapelberg 		/* Store the device address for the magic packet */
1754424ca4c5SRussell King 		err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD2,
17553871c387SMichael Stapelberg 				((phydev->attached_dev->dev_addr[5] << 8) |
17563871c387SMichael Stapelberg 				 phydev->attached_dev->dev_addr[4]));
17573871c387SMichael Stapelberg 		if (err < 0)
1758424ca4c5SRussell King 			goto error;
1759424ca4c5SRussell King 		err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD1,
17603871c387SMichael Stapelberg 				((phydev->attached_dev->dev_addr[3] << 8) |
17613871c387SMichael Stapelberg 				 phydev->attached_dev->dev_addr[2]));
17623871c387SMichael Stapelberg 		if (err < 0)
1763424ca4c5SRussell King 			goto error;
1764424ca4c5SRussell King 		err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD0,
17653871c387SMichael Stapelberg 				((phydev->attached_dev->dev_addr[1] << 8) |
17663871c387SMichael Stapelberg 				 phydev->attached_dev->dev_addr[0]));
17673871c387SMichael Stapelberg 		if (err < 0)
1768424ca4c5SRussell King 			goto error;
17693871c387SMichael Stapelberg 
17703871c387SMichael Stapelberg 		/* Clear WOL status and enable magic packet matching */
1771832913c3SYejune Deng 		err = __phy_set_bits(phydev, MII_88E1318S_PHY_WOL_CTRL,
1772424ca4c5SRussell King 				     MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS |
1773424ca4c5SRussell King 				     MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE);
17743871c387SMichael Stapelberg 		if (err < 0)
1775424ca4c5SRussell King 			goto error;
17763871c387SMichael Stapelberg 	} else {
1777424ca4c5SRussell King 		err = marvell_write_page(phydev, MII_MARVELL_WOL_PAGE);
17783871c387SMichael Stapelberg 		if (err < 0)
1779424ca4c5SRussell King 			goto error;
17803871c387SMichael Stapelberg 
17813871c387SMichael Stapelberg 		/* Clear WOL status and disable magic packet matching */
1782424ca4c5SRussell King 		err = __phy_modify(phydev, MII_88E1318S_PHY_WOL_CTRL,
1783f102852fSRussell King 				   MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE,
1784424ca4c5SRussell King 				   MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS);
17853871c387SMichael Stapelberg 		if (err < 0)
1786424ca4c5SRussell King 			goto error;
17873871c387SMichael Stapelberg 	}
17883871c387SMichael Stapelberg 
1789424ca4c5SRussell King error:
1790424ca4c5SRussell King 	return phy_restore_page(phydev, oldpage, err);
17913871c387SMichael Stapelberg }
17923871c387SMichael Stapelberg 
1793d2fa47d9SAndrew Lunn static int marvell_get_sset_count(struct phy_device *phydev)
1794d2fa47d9SAndrew Lunn {
17953c1bcc86SAndrew Lunn 	if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
17963c1bcc86SAndrew Lunn 			      phydev->supported))
1797d2fa47d9SAndrew Lunn 		return ARRAY_SIZE(marvell_hw_stats);
17982170fef7SCharles-Antoine Couret 	else
17992170fef7SCharles-Antoine Couret 		return ARRAY_SIZE(marvell_hw_stats) - NB_FIBER_STATS;
1800d2fa47d9SAndrew Lunn }
1801d2fa47d9SAndrew Lunn 
1802d2fa47d9SAndrew Lunn static void marvell_get_strings(struct phy_device *phydev, u8 *data)
1803d2fa47d9SAndrew Lunn {
1804fdfdf867SAndrew Lunn 	int count = marvell_get_sset_count(phydev);
1805d2fa47d9SAndrew Lunn 	int i;
1806d2fa47d9SAndrew Lunn 
1807fdfdf867SAndrew Lunn 	for (i = 0; i < count; i++) {
180898409b2bSFlorian Fainelli 		strlcpy(data + i * ETH_GSTRING_LEN,
1809d2fa47d9SAndrew Lunn 			marvell_hw_stats[i].string, ETH_GSTRING_LEN);
1810d2fa47d9SAndrew Lunn 	}
1811d2fa47d9SAndrew Lunn }
1812d2fa47d9SAndrew Lunn 
1813d2fa47d9SAndrew Lunn static u64 marvell_get_stat(struct phy_device *phydev, int i)
1814d2fa47d9SAndrew Lunn {
1815d2fa47d9SAndrew Lunn 	struct marvell_hw_stat stat = marvell_hw_stats[i];
1816d2fa47d9SAndrew Lunn 	struct marvell_priv *priv = phydev->priv;
1817424ca4c5SRussell King 	int val;
1818321b4d4bSAndrew Lunn 	u64 ret;
1819d2fa47d9SAndrew Lunn 
1820424ca4c5SRussell King 	val = phy_read_paged(phydev, stat.page, stat.reg);
1821d2fa47d9SAndrew Lunn 	if (val < 0) {
18226c3442f5SJisheng Zhang 		ret = U64_MAX;
1823d2fa47d9SAndrew Lunn 	} else {
1824d2fa47d9SAndrew Lunn 		val = val & ((1 << stat.bits) - 1);
1825d2fa47d9SAndrew Lunn 		priv->stats[i] += val;
1826321b4d4bSAndrew Lunn 		ret = priv->stats[i];
1827d2fa47d9SAndrew Lunn 	}
1828d2fa47d9SAndrew Lunn 
1829321b4d4bSAndrew Lunn 	return ret;
1830d2fa47d9SAndrew Lunn }
1831d2fa47d9SAndrew Lunn 
1832d2fa47d9SAndrew Lunn static void marvell_get_stats(struct phy_device *phydev,
1833d2fa47d9SAndrew Lunn 			      struct ethtool_stats *stats, u64 *data)
1834d2fa47d9SAndrew Lunn {
1835fdfdf867SAndrew Lunn 	int count = marvell_get_sset_count(phydev);
1836d2fa47d9SAndrew Lunn 	int i;
1837d2fa47d9SAndrew Lunn 
1838fdfdf867SAndrew Lunn 	for (i = 0; i < count; i++)
1839d2fa47d9SAndrew Lunn 		data[i] = marvell_get_stat(phydev, i);
1840d2fa47d9SAndrew Lunn }
1841d2fa47d9SAndrew Lunn 
18420c9bcc1dSAndrew Lunn static int marvell_vct5_wait_complete(struct phy_device *phydev)
18430c9bcc1dSAndrew Lunn {
18440c9bcc1dSAndrew Lunn 	int i;
18450c9bcc1dSAndrew Lunn 	int val;
18460c9bcc1dSAndrew Lunn 
18470c9bcc1dSAndrew Lunn 	for (i = 0; i < 32; i++) {
1848a618e86dSAndrew Lunn 		val = __phy_read(phydev, MII_VCT5_CTRL);
18490c9bcc1dSAndrew Lunn 		if (val < 0)
18500c9bcc1dSAndrew Lunn 			return val;
18510c9bcc1dSAndrew Lunn 
18520c9bcc1dSAndrew Lunn 		if (val & MII_VCT5_CTRL_COMPLETE)
18530c9bcc1dSAndrew Lunn 			return 0;
18540c9bcc1dSAndrew Lunn 	}
18550c9bcc1dSAndrew Lunn 
18560c9bcc1dSAndrew Lunn 	phydev_err(phydev, "Timeout while waiting for cable test to finish\n");
18570c9bcc1dSAndrew Lunn 	return -ETIMEDOUT;
18580c9bcc1dSAndrew Lunn }
18590c9bcc1dSAndrew Lunn 
18600c9bcc1dSAndrew Lunn static int marvell_vct5_amplitude(struct phy_device *phydev, int pair)
18610c9bcc1dSAndrew Lunn {
18620c9bcc1dSAndrew Lunn 	int amplitude;
18630c9bcc1dSAndrew Lunn 	int val;
18640c9bcc1dSAndrew Lunn 	int reg;
18650c9bcc1dSAndrew Lunn 
18660c9bcc1dSAndrew Lunn 	reg = MII_VCT5_TX_RX_MDI0_COUPLING + pair;
1867a618e86dSAndrew Lunn 	val = __phy_read(phydev, reg);
18680c9bcc1dSAndrew Lunn 
18690c9bcc1dSAndrew Lunn 	if (val < 0)
18700c9bcc1dSAndrew Lunn 		return 0;
18710c9bcc1dSAndrew Lunn 
18720c9bcc1dSAndrew Lunn 	amplitude = (val & MII_VCT5_TX_RX_AMPLITUDE_MASK) >>
18730c9bcc1dSAndrew Lunn 		MII_VCT5_TX_RX_AMPLITUDE_SHIFT;
18740c9bcc1dSAndrew Lunn 
18750c9bcc1dSAndrew Lunn 	if (!(val & MII_VCT5_TX_RX_COUPLING_POSITIVE_REFLECTION))
18760c9bcc1dSAndrew Lunn 		amplitude = -amplitude;
18770c9bcc1dSAndrew Lunn 
18780c9bcc1dSAndrew Lunn 	return 1000 * amplitude / 128;
18790c9bcc1dSAndrew Lunn }
18800c9bcc1dSAndrew Lunn 
18810c9bcc1dSAndrew Lunn static u32 marvell_vct5_distance2cm(int distance)
18820c9bcc1dSAndrew Lunn {
18830c9bcc1dSAndrew Lunn 	return distance * 805 / 10;
18840c9bcc1dSAndrew Lunn }
18850c9bcc1dSAndrew Lunn 
1886f2bc8ad3SAndrew Lunn static u32 marvell_vct5_cm2distance(int cm)
18870c9bcc1dSAndrew Lunn {
1888f2bc8ad3SAndrew Lunn 	return cm * 10 / 805;
1889f2bc8ad3SAndrew Lunn }
1890f2bc8ad3SAndrew Lunn 
1891f2bc8ad3SAndrew Lunn static int marvell_vct5_amplitude_distance(struct phy_device *phydev,
1892f2bc8ad3SAndrew Lunn 					   int distance, int pair)
1893f2bc8ad3SAndrew Lunn {
18940c9bcc1dSAndrew Lunn 	u16 reg;
18950c9bcc1dSAndrew Lunn 	int err;
1896f2bc8ad3SAndrew Lunn 	int mV;
1897f2bc8ad3SAndrew Lunn 	int i;
18980c9bcc1dSAndrew Lunn 
1899a618e86dSAndrew Lunn 	err = __phy_write(phydev, MII_VCT5_SAMPLE_POINT_DISTANCE,
19000c9bcc1dSAndrew Lunn 			  distance);
19010c9bcc1dSAndrew Lunn 	if (err)
19020c9bcc1dSAndrew Lunn 		return err;
19030c9bcc1dSAndrew Lunn 
19040c9bcc1dSAndrew Lunn 	reg = MII_VCT5_CTRL_ENABLE |
19050c9bcc1dSAndrew Lunn 		MII_VCT5_CTRL_TX_SAME_CHANNEL |
19060c9bcc1dSAndrew Lunn 		MII_VCT5_CTRL_SAMPLES_DEFAULT |
19070c9bcc1dSAndrew Lunn 		MII_VCT5_CTRL_SAMPLE_POINT |
19080c9bcc1dSAndrew Lunn 		MII_VCT5_CTRL_PEEK_HYST_DEFAULT;
1909a618e86dSAndrew Lunn 	err = __phy_write(phydev, MII_VCT5_CTRL, reg);
19100c9bcc1dSAndrew Lunn 	if (err)
19110c9bcc1dSAndrew Lunn 		return err;
19120c9bcc1dSAndrew Lunn 
19130c9bcc1dSAndrew Lunn 	err = marvell_vct5_wait_complete(phydev);
19140c9bcc1dSAndrew Lunn 	if (err)
19150c9bcc1dSAndrew Lunn 		return err;
19160c9bcc1dSAndrew Lunn 
1917f2bc8ad3SAndrew Lunn 	for (i = 0; i < 4; i++) {
1918f2bc8ad3SAndrew Lunn 		if (pair != PHY_PAIR_ALL && i != pair)
1919f2bc8ad3SAndrew Lunn 			continue;
19200c9bcc1dSAndrew Lunn 
1921f2bc8ad3SAndrew Lunn 		mV = marvell_vct5_amplitude(phydev, i);
1922f2bc8ad3SAndrew Lunn 		ethnl_cable_test_amplitude(phydev, i, mV);
1923f2bc8ad3SAndrew Lunn 	}
19240c9bcc1dSAndrew Lunn 
19250c9bcc1dSAndrew Lunn 	return 0;
19260c9bcc1dSAndrew Lunn }
19270c9bcc1dSAndrew Lunn 
19280c9bcc1dSAndrew Lunn static int marvell_vct5_amplitude_graph(struct phy_device *phydev)
19290c9bcc1dSAndrew Lunn {
1930f2bc8ad3SAndrew Lunn 	struct marvell_priv *priv = phydev->priv;
19310c9bcc1dSAndrew Lunn 	int distance;
1932db8668a1SAndrew Lunn 	u16 width;
1933a618e86dSAndrew Lunn 	int page;
19340c9bcc1dSAndrew Lunn 	int err;
19350c9bcc1dSAndrew Lunn 	u16 reg;
19360c9bcc1dSAndrew Lunn 
1937db8668a1SAndrew Lunn 	if (priv->first <= TDR_SHORT_CABLE_LENGTH)
1938db8668a1SAndrew Lunn 		width = MII_VCT5_TX_PULSE_CTRL_PULSE_WIDTH_32nS;
1939db8668a1SAndrew Lunn 	else
1940db8668a1SAndrew Lunn 		width = MII_VCT5_TX_PULSE_CTRL_PULSE_WIDTH_128nS;
1941db8668a1SAndrew Lunn 
19420c9bcc1dSAndrew Lunn 	reg = MII_VCT5_TX_PULSE_CTRL_GT_140m_46_86mV |
19430c9bcc1dSAndrew Lunn 		MII_VCT5_TX_PULSE_CTRL_DONT_WAIT_LINK_DOWN |
1944db8668a1SAndrew Lunn 		MII_VCT5_TX_PULSE_CTRL_MAX_AMP | width;
19450c9bcc1dSAndrew Lunn 
19460c9bcc1dSAndrew Lunn 	err = phy_write_paged(phydev, MII_MARVELL_VCT5_PAGE,
19470c9bcc1dSAndrew Lunn 			      MII_VCT5_TX_PULSE_CTRL, reg);
19480c9bcc1dSAndrew Lunn 	if (err)
19490c9bcc1dSAndrew Lunn 		return err;
19500c9bcc1dSAndrew Lunn 
1951a618e86dSAndrew Lunn 	/* Reading the TDR data is very MDIO heavy. We need to optimize
1952a618e86dSAndrew Lunn 	 * access to keep the time to a minimum. So lock the bus once,
1953a618e86dSAndrew Lunn 	 * and don't release it until complete. We can then avoid having
1954a618e86dSAndrew Lunn 	 * to change the page for every access, greatly speeding things
1955a618e86dSAndrew Lunn 	 * up.
1956a618e86dSAndrew Lunn 	 */
1957a618e86dSAndrew Lunn 	page = phy_select_page(phydev, MII_MARVELL_VCT5_PAGE);
1958a618e86dSAndrew Lunn 	if (page < 0)
1959830f5ce2SDan Carpenter 		goto restore_page;
1960a618e86dSAndrew Lunn 
1961f2bc8ad3SAndrew Lunn 	for (distance = priv->first;
1962f2bc8ad3SAndrew Lunn 	     distance <= priv->last;
1963f2bc8ad3SAndrew Lunn 	     distance += priv->step) {
1964f2bc8ad3SAndrew Lunn 		err = marvell_vct5_amplitude_distance(phydev, distance,
1965f2bc8ad3SAndrew Lunn 						      priv->pair);
19660c9bcc1dSAndrew Lunn 		if (err)
1967a618e86dSAndrew Lunn 			goto restore_page;
1968db8668a1SAndrew Lunn 
1969db8668a1SAndrew Lunn 		if (distance > TDR_SHORT_CABLE_LENGTH &&
1970db8668a1SAndrew Lunn 		    width == MII_VCT5_TX_PULSE_CTRL_PULSE_WIDTH_32nS) {
1971db8668a1SAndrew Lunn 			width = MII_VCT5_TX_PULSE_CTRL_PULSE_WIDTH_128nS;
1972db8668a1SAndrew Lunn 			reg = MII_VCT5_TX_PULSE_CTRL_GT_140m_46_86mV |
1973db8668a1SAndrew Lunn 				MII_VCT5_TX_PULSE_CTRL_DONT_WAIT_LINK_DOWN |
1974db8668a1SAndrew Lunn 				MII_VCT5_TX_PULSE_CTRL_MAX_AMP | width;
1975db8668a1SAndrew Lunn 			err = __phy_write(phydev, MII_VCT5_TX_PULSE_CTRL, reg);
1976db8668a1SAndrew Lunn 			if (err)
1977db8668a1SAndrew Lunn 				goto restore_page;
1978db8668a1SAndrew Lunn 		}
19790c9bcc1dSAndrew Lunn 	}
19800c9bcc1dSAndrew Lunn 
1981a618e86dSAndrew Lunn restore_page:
1982a618e86dSAndrew Lunn 	return phy_restore_page(phydev, page, err);
19830c9bcc1dSAndrew Lunn }
19840c9bcc1dSAndrew Lunn 
19850c9bcc1dSAndrew Lunn static int marvell_cable_test_start_common(struct phy_device *phydev)
1986fc879f72SAndrew Lunn {
1987fc879f72SAndrew Lunn 	int bmcr, bmsr, ret;
1988fc879f72SAndrew Lunn 
1989fc879f72SAndrew Lunn 	/* If auto-negotiation is enabled, but not complete, the cable
1990fc879f72SAndrew Lunn 	 * test never completes. So disable auto-neg.
1991fc879f72SAndrew Lunn 	 */
1992fc879f72SAndrew Lunn 	bmcr = phy_read(phydev, MII_BMCR);
1993fc879f72SAndrew Lunn 	if (bmcr < 0)
1994fc879f72SAndrew Lunn 		return bmcr;
1995fc879f72SAndrew Lunn 
1996fc879f72SAndrew Lunn 	bmsr = phy_read(phydev, MII_BMSR);
1997fc879f72SAndrew Lunn 
1998fc879f72SAndrew Lunn 	if (bmsr < 0)
1999fc879f72SAndrew Lunn 		return bmsr;
2000fc879f72SAndrew Lunn 
2001fc879f72SAndrew Lunn 	if (bmcr & BMCR_ANENABLE) {
2002832913c3SYejune Deng 		ret =  phy_clear_bits(phydev, MII_BMCR, BMCR_ANENABLE);
2003fc879f72SAndrew Lunn 		if (ret < 0)
2004fc879f72SAndrew Lunn 			return ret;
2005fc879f72SAndrew Lunn 		ret = genphy_soft_reset(phydev);
2006fc879f72SAndrew Lunn 		if (ret < 0)
2007fc879f72SAndrew Lunn 			return ret;
2008fc879f72SAndrew Lunn 	}
2009fc879f72SAndrew Lunn 
2010fc879f72SAndrew Lunn 	/* If the link is up, allow it some time to go down */
2011fc879f72SAndrew Lunn 	if (bmsr & BMSR_LSTATUS)
2012fc879f72SAndrew Lunn 		msleep(1500);
2013fc879f72SAndrew Lunn 
20140c9bcc1dSAndrew Lunn 	return 0;
20150c9bcc1dSAndrew Lunn }
20160c9bcc1dSAndrew Lunn 
20170c9bcc1dSAndrew Lunn static int marvell_vct7_cable_test_start(struct phy_device *phydev)
20180c9bcc1dSAndrew Lunn {
20190c9bcc1dSAndrew Lunn 	struct marvell_priv *priv = phydev->priv;
20200c9bcc1dSAndrew Lunn 	int ret;
20210c9bcc1dSAndrew Lunn 
20220c9bcc1dSAndrew Lunn 	ret = marvell_cable_test_start_common(phydev);
20230c9bcc1dSAndrew Lunn 	if (ret)
20240c9bcc1dSAndrew Lunn 		return ret;
20250c9bcc1dSAndrew Lunn 
20260c9bcc1dSAndrew Lunn 	priv->cable_test_tdr = false;
20270c9bcc1dSAndrew Lunn 
20280c9bcc1dSAndrew Lunn 	/* Reset the VCT5 API control to defaults, otherwise
20290c9bcc1dSAndrew Lunn 	 * VCT7 does not work correctly.
20300c9bcc1dSAndrew Lunn 	 */
20310c9bcc1dSAndrew Lunn 	ret = phy_write_paged(phydev, MII_MARVELL_VCT5_PAGE,
20320c9bcc1dSAndrew Lunn 			      MII_VCT5_CTRL,
20330c9bcc1dSAndrew Lunn 			      MII_VCT5_CTRL_TX_SAME_CHANNEL |
20340c9bcc1dSAndrew Lunn 			      MII_VCT5_CTRL_SAMPLES_DEFAULT |
20350c9bcc1dSAndrew Lunn 			      MII_VCT5_CTRL_MODE_MAXIMUM_PEEK |
20360c9bcc1dSAndrew Lunn 			      MII_VCT5_CTRL_PEEK_HYST_DEFAULT);
20370c9bcc1dSAndrew Lunn 	if (ret)
20380c9bcc1dSAndrew Lunn 		return ret;
20390c9bcc1dSAndrew Lunn 
20400c9bcc1dSAndrew Lunn 	ret = phy_write_paged(phydev, MII_MARVELL_VCT5_PAGE,
20410c9bcc1dSAndrew Lunn 			      MII_VCT5_SAMPLE_POINT_DISTANCE, 0);
20420c9bcc1dSAndrew Lunn 	if (ret)
20430c9bcc1dSAndrew Lunn 		return ret;
20440c9bcc1dSAndrew Lunn 
2045fc879f72SAndrew Lunn 	return phy_write_paged(phydev, MII_MARVELL_VCT7_PAGE,
2046fc879f72SAndrew Lunn 			       MII_VCT7_CTRL,
2047fc879f72SAndrew Lunn 			       MII_VCT7_CTRL_RUN_NOW |
2048fc879f72SAndrew Lunn 			       MII_VCT7_CTRL_CENTIMETERS);
2049fc879f72SAndrew Lunn }
2050fc879f72SAndrew Lunn 
2051f2bc8ad3SAndrew Lunn static int marvell_vct5_cable_test_tdr_start(struct phy_device *phydev,
2052f2bc8ad3SAndrew Lunn 					     const struct phy_tdr_config *cfg)
20530c9bcc1dSAndrew Lunn {
20540c9bcc1dSAndrew Lunn 	struct marvell_priv *priv = phydev->priv;
20550c9bcc1dSAndrew Lunn 	int ret;
20560c9bcc1dSAndrew Lunn 
2057f2bc8ad3SAndrew Lunn 	priv->cable_test_tdr = true;
2058f2bc8ad3SAndrew Lunn 	priv->first = marvell_vct5_cm2distance(cfg->first);
2059f2bc8ad3SAndrew Lunn 	priv->last = marvell_vct5_cm2distance(cfg->last);
2060f2bc8ad3SAndrew Lunn 	priv->step = marvell_vct5_cm2distance(cfg->step);
2061f2bc8ad3SAndrew Lunn 	priv->pair = cfg->pair;
2062f2bc8ad3SAndrew Lunn 
2063f2bc8ad3SAndrew Lunn 	if (priv->first > MII_VCT5_SAMPLE_POINT_DISTANCE_MAX)
2064f2bc8ad3SAndrew Lunn 		return -EINVAL;
2065f2bc8ad3SAndrew Lunn 
2066f2bc8ad3SAndrew Lunn 	if (priv->last > MII_VCT5_SAMPLE_POINT_DISTANCE_MAX)
2067f2bc8ad3SAndrew Lunn 		return -EINVAL;
2068f2bc8ad3SAndrew Lunn 
20690c9bcc1dSAndrew Lunn 	/* Disable  VCT7 */
20700c9bcc1dSAndrew Lunn 	ret = phy_write_paged(phydev, MII_MARVELL_VCT7_PAGE,
20710c9bcc1dSAndrew Lunn 			      MII_VCT7_CTRL, 0);
20720c9bcc1dSAndrew Lunn 	if (ret)
20730c9bcc1dSAndrew Lunn 		return ret;
20740c9bcc1dSAndrew Lunn 
20750c9bcc1dSAndrew Lunn 	ret = marvell_cable_test_start_common(phydev);
20760c9bcc1dSAndrew Lunn 	if (ret)
20770c9bcc1dSAndrew Lunn 		return ret;
20780c9bcc1dSAndrew Lunn 
20790c9bcc1dSAndrew Lunn 	ret = ethnl_cable_test_pulse(phydev, 1000);
20800c9bcc1dSAndrew Lunn 	if (ret)
20810c9bcc1dSAndrew Lunn 		return ret;
20820c9bcc1dSAndrew Lunn 
20830c9bcc1dSAndrew Lunn 	return ethnl_cable_test_step(phydev,
2084f2bc8ad3SAndrew Lunn 				     marvell_vct5_distance2cm(priv->first),
2085f2bc8ad3SAndrew Lunn 				     marvell_vct5_distance2cm(priv->last),
2086f2bc8ad3SAndrew Lunn 				     marvell_vct5_distance2cm(priv->step));
20870c9bcc1dSAndrew Lunn }
20880c9bcc1dSAndrew Lunn 
2089fc879f72SAndrew Lunn static int marvell_vct7_distance_to_length(int distance, bool meter)
2090fc879f72SAndrew Lunn {
2091fc879f72SAndrew Lunn 	if (meter)
2092fc879f72SAndrew Lunn 		distance *= 100;
2093fc879f72SAndrew Lunn 
2094fc879f72SAndrew Lunn 	return distance;
2095fc879f72SAndrew Lunn }
2096fc879f72SAndrew Lunn 
2097fc879f72SAndrew Lunn static bool marvell_vct7_distance_valid(int result)
2098fc879f72SAndrew Lunn {
2099fc879f72SAndrew Lunn 	switch (result) {
2100fc879f72SAndrew Lunn 	case MII_VCT7_RESULTS_OPEN:
2101fc879f72SAndrew Lunn 	case MII_VCT7_RESULTS_SAME_SHORT:
2102fc879f72SAndrew Lunn 	case MII_VCT7_RESULTS_CROSS_SHORT:
2103fc879f72SAndrew Lunn 		return true;
2104fc879f72SAndrew Lunn 	}
2105fc879f72SAndrew Lunn 	return false;
2106fc879f72SAndrew Lunn }
2107fc879f72SAndrew Lunn 
2108fc879f72SAndrew Lunn static int marvell_vct7_report_length(struct phy_device *phydev,
2109fc879f72SAndrew Lunn 				      int pair, bool meter)
2110fc879f72SAndrew Lunn {
2111fc879f72SAndrew Lunn 	int length;
2112fc879f72SAndrew Lunn 	int ret;
2113fc879f72SAndrew Lunn 
2114fc879f72SAndrew Lunn 	ret = phy_read_paged(phydev, MII_MARVELL_VCT7_PAGE,
2115fc879f72SAndrew Lunn 			     MII_VCT7_PAIR_0_DISTANCE + pair);
2116fc879f72SAndrew Lunn 	if (ret < 0)
2117fc879f72SAndrew Lunn 		return ret;
2118fc879f72SAndrew Lunn 
2119fc879f72SAndrew Lunn 	length = marvell_vct7_distance_to_length(ret, meter);
2120fc879f72SAndrew Lunn 
2121fc879f72SAndrew Lunn 	ethnl_cable_test_fault_length(phydev, pair, length);
2122fc879f72SAndrew Lunn 
2123fc879f72SAndrew Lunn 	return 0;
2124fc879f72SAndrew Lunn }
2125fc879f72SAndrew Lunn 
2126fc879f72SAndrew Lunn static int marvell_vct7_cable_test_report_trans(int result)
2127fc879f72SAndrew Lunn {
2128fc879f72SAndrew Lunn 	switch (result) {
2129fc879f72SAndrew Lunn 	case MII_VCT7_RESULTS_OK:
2130fc879f72SAndrew Lunn 		return ETHTOOL_A_CABLE_RESULT_CODE_OK;
2131fc879f72SAndrew Lunn 	case MII_VCT7_RESULTS_OPEN:
2132fc879f72SAndrew Lunn 		return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
2133fc879f72SAndrew Lunn 	case MII_VCT7_RESULTS_SAME_SHORT:
2134fc879f72SAndrew Lunn 		return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
2135fc879f72SAndrew Lunn 	case MII_VCT7_RESULTS_CROSS_SHORT:
2136fc879f72SAndrew Lunn 		return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
2137fc879f72SAndrew Lunn 	default:
2138fc879f72SAndrew Lunn 		return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
2139fc879f72SAndrew Lunn 	}
2140fc879f72SAndrew Lunn }
2141fc879f72SAndrew Lunn 
2142fc879f72SAndrew Lunn static int marvell_vct7_cable_test_report(struct phy_device *phydev)
2143fc879f72SAndrew Lunn {
2144fc879f72SAndrew Lunn 	int pair0, pair1, pair2, pair3;
2145fc879f72SAndrew Lunn 	bool meter;
2146fc879f72SAndrew Lunn 	int ret;
2147fc879f72SAndrew Lunn 
2148fc879f72SAndrew Lunn 	ret = phy_read_paged(phydev, MII_MARVELL_VCT7_PAGE,
2149fc879f72SAndrew Lunn 			     MII_VCT7_RESULTS);
2150fc879f72SAndrew Lunn 	if (ret < 0)
2151fc879f72SAndrew Lunn 		return ret;
2152fc879f72SAndrew Lunn 
2153fc879f72SAndrew Lunn 	pair3 = (ret & MII_VCT7_RESULTS_PAIR3_MASK) >>
2154fc879f72SAndrew Lunn 		MII_VCT7_RESULTS_PAIR3_SHIFT;
2155fc879f72SAndrew Lunn 	pair2 = (ret & MII_VCT7_RESULTS_PAIR2_MASK) >>
2156fc879f72SAndrew Lunn 		MII_VCT7_RESULTS_PAIR2_SHIFT;
2157fc879f72SAndrew Lunn 	pair1 = (ret & MII_VCT7_RESULTS_PAIR1_MASK) >>
2158fc879f72SAndrew Lunn 		MII_VCT7_RESULTS_PAIR1_SHIFT;
2159fc879f72SAndrew Lunn 	pair0 = (ret & MII_VCT7_RESULTS_PAIR0_MASK) >>
2160fc879f72SAndrew Lunn 		MII_VCT7_RESULTS_PAIR0_SHIFT;
2161fc879f72SAndrew Lunn 
2162fc879f72SAndrew Lunn 	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,
2163fc879f72SAndrew Lunn 				marvell_vct7_cable_test_report_trans(pair0));
2164fc879f72SAndrew Lunn 	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_B,
2165fc879f72SAndrew Lunn 				marvell_vct7_cable_test_report_trans(pair1));
2166fc879f72SAndrew Lunn 	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_C,
2167fc879f72SAndrew Lunn 				marvell_vct7_cable_test_report_trans(pair2));
2168fc879f72SAndrew Lunn 	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D,
2169fc879f72SAndrew Lunn 				marvell_vct7_cable_test_report_trans(pair3));
2170fc879f72SAndrew Lunn 
2171fc879f72SAndrew Lunn 	ret = phy_read_paged(phydev, MII_MARVELL_VCT7_PAGE, MII_VCT7_CTRL);
2172fc879f72SAndrew Lunn 	if (ret < 0)
2173fc879f72SAndrew Lunn 		return ret;
2174fc879f72SAndrew Lunn 
2175fc879f72SAndrew Lunn 	meter = ret & MII_VCT7_CTRL_METERS;
2176fc879f72SAndrew Lunn 
2177fc879f72SAndrew Lunn 	if (marvell_vct7_distance_valid(pair0))
2178fc879f72SAndrew Lunn 		marvell_vct7_report_length(phydev, 0, meter);
2179fc879f72SAndrew Lunn 	if (marvell_vct7_distance_valid(pair1))
2180fc879f72SAndrew Lunn 		marvell_vct7_report_length(phydev, 1, meter);
2181fc879f72SAndrew Lunn 	if (marvell_vct7_distance_valid(pair2))
2182fc879f72SAndrew Lunn 		marvell_vct7_report_length(phydev, 2, meter);
2183fc879f72SAndrew Lunn 	if (marvell_vct7_distance_valid(pair3))
2184fc879f72SAndrew Lunn 		marvell_vct7_report_length(phydev, 3, meter);
2185fc879f72SAndrew Lunn 
2186fc879f72SAndrew Lunn 	return 0;
2187fc879f72SAndrew Lunn }
2188fc879f72SAndrew Lunn 
2189fc879f72SAndrew Lunn static int marvell_vct7_cable_test_get_status(struct phy_device *phydev,
2190fc879f72SAndrew Lunn 					      bool *finished)
2191fc879f72SAndrew Lunn {
21920c9bcc1dSAndrew Lunn 	struct marvell_priv *priv = phydev->priv;
2193fc879f72SAndrew Lunn 	int ret;
2194fc879f72SAndrew Lunn 
21950c9bcc1dSAndrew Lunn 	if (priv->cable_test_tdr) {
21960c9bcc1dSAndrew Lunn 		ret = marvell_vct5_amplitude_graph(phydev);
21970c9bcc1dSAndrew Lunn 		*finished = true;
21980c9bcc1dSAndrew Lunn 		return ret;
21990c9bcc1dSAndrew Lunn 	}
22000c9bcc1dSAndrew Lunn 
2201fc879f72SAndrew Lunn 	*finished = false;
2202fc879f72SAndrew Lunn 
2203fc879f72SAndrew Lunn 	ret = phy_read_paged(phydev, MII_MARVELL_VCT7_PAGE,
2204fc879f72SAndrew Lunn 			     MII_VCT7_CTRL);
2205fc879f72SAndrew Lunn 
2206fc879f72SAndrew Lunn 	if (ret < 0)
2207fc879f72SAndrew Lunn 		return ret;
2208fc879f72SAndrew Lunn 
2209fc879f72SAndrew Lunn 	if (!(ret & MII_VCT7_CTRL_IN_PROGRESS)) {
2210fc879f72SAndrew Lunn 		*finished = true;
2211fc879f72SAndrew Lunn 
2212fc879f72SAndrew Lunn 		return marvell_vct7_cable_test_report(phydev);
2213fc879f72SAndrew Lunn 	}
2214fc879f72SAndrew Lunn 
2215fc879f72SAndrew Lunn 	return 0;
2216fc879f72SAndrew Lunn }
2217fc879f72SAndrew Lunn 
22180b04680fSAndrew Lunn #ifdef CONFIG_HWMON
22190b04680fSAndrew Lunn static int m88e1121_get_temp(struct phy_device *phydev, long *temp)
22200b04680fSAndrew Lunn {
2221975b388cSAndrew Lunn 	int oldpage;
2222424ca4c5SRussell King 	int ret = 0;
22230b04680fSAndrew Lunn 	int val;
22240b04680fSAndrew Lunn 
22250b04680fSAndrew Lunn 	*temp = 0;
22260b04680fSAndrew Lunn 
2227424ca4c5SRussell King 	oldpage = phy_select_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
2228424ca4c5SRussell King 	if (oldpage < 0)
2229424ca4c5SRussell King 		goto error;
2230975b388cSAndrew Lunn 
22310b04680fSAndrew Lunn 	/* Enable temperature sensor */
2232424ca4c5SRussell King 	ret = __phy_read(phydev, MII_88E1121_MISC_TEST);
22330b04680fSAndrew Lunn 	if (ret < 0)
22340b04680fSAndrew Lunn 		goto error;
22350b04680fSAndrew Lunn 
2236424ca4c5SRussell King 	ret = __phy_write(phydev, MII_88E1121_MISC_TEST,
22370b04680fSAndrew Lunn 			  ret | MII_88E1121_MISC_TEST_TEMP_SENSOR_EN);
22380b04680fSAndrew Lunn 	if (ret < 0)
22390b04680fSAndrew Lunn 		goto error;
22400b04680fSAndrew Lunn 
22410b04680fSAndrew Lunn 	/* Wait for temperature to stabilize */
22420b04680fSAndrew Lunn 	usleep_range(10000, 12000);
22430b04680fSAndrew Lunn 
2244424ca4c5SRussell King 	val = __phy_read(phydev, MII_88E1121_MISC_TEST);
22450b04680fSAndrew Lunn 	if (val < 0) {
22460b04680fSAndrew Lunn 		ret = val;
22470b04680fSAndrew Lunn 		goto error;
22480b04680fSAndrew Lunn 	}
22490b04680fSAndrew Lunn 
22500b04680fSAndrew Lunn 	/* Disable temperature sensor */
2251424ca4c5SRussell King 	ret = __phy_write(phydev, MII_88E1121_MISC_TEST,
22520b04680fSAndrew Lunn 			  ret & ~MII_88E1121_MISC_TEST_TEMP_SENSOR_EN);
22530b04680fSAndrew Lunn 	if (ret < 0)
22540b04680fSAndrew Lunn 		goto error;
22550b04680fSAndrew Lunn 
22560b04680fSAndrew Lunn 	*temp = ((val & MII_88E1121_MISC_TEST_TEMP_MASK) - 5) * 5000;
22570b04680fSAndrew Lunn 
22580b04680fSAndrew Lunn error:
2259424ca4c5SRussell King 	return phy_restore_page(phydev, oldpage, ret);
22600b04680fSAndrew Lunn }
22610b04680fSAndrew Lunn 
22620b04680fSAndrew Lunn static int m88e1121_hwmon_read(struct device *dev,
22630b04680fSAndrew Lunn 			       enum hwmon_sensor_types type,
22640b04680fSAndrew Lunn 			       u32 attr, int channel, long *temp)
22650b04680fSAndrew Lunn {
22660b04680fSAndrew Lunn 	struct phy_device *phydev = dev_get_drvdata(dev);
22670b04680fSAndrew Lunn 	int err;
22680b04680fSAndrew Lunn 
22690b04680fSAndrew Lunn 	switch (attr) {
22700b04680fSAndrew Lunn 	case hwmon_temp_input:
22710b04680fSAndrew Lunn 		err = m88e1121_get_temp(phydev, temp);
22720b04680fSAndrew Lunn 		break;
22730b04680fSAndrew Lunn 	default:
22740b04680fSAndrew Lunn 		return -EOPNOTSUPP;
22750b04680fSAndrew Lunn 	}
22760b04680fSAndrew Lunn 
22770b04680fSAndrew Lunn 	return err;
22780b04680fSAndrew Lunn }
22790b04680fSAndrew Lunn 
22800b04680fSAndrew Lunn static umode_t m88e1121_hwmon_is_visible(const void *data,
22810b04680fSAndrew Lunn 					 enum hwmon_sensor_types type,
22820b04680fSAndrew Lunn 					 u32 attr, int channel)
22830b04680fSAndrew Lunn {
22840b04680fSAndrew Lunn 	if (type != hwmon_temp)
22850b04680fSAndrew Lunn 		return 0;
22860b04680fSAndrew Lunn 
22870b04680fSAndrew Lunn 	switch (attr) {
22880b04680fSAndrew Lunn 	case hwmon_temp_input:
22890b04680fSAndrew Lunn 		return 0444;
22900b04680fSAndrew Lunn 	default:
22910b04680fSAndrew Lunn 		return 0;
22920b04680fSAndrew Lunn 	}
22930b04680fSAndrew Lunn }
22940b04680fSAndrew Lunn 
22950b04680fSAndrew Lunn static u32 m88e1121_hwmon_chip_config[] = {
22960b04680fSAndrew Lunn 	HWMON_C_REGISTER_TZ,
22970b04680fSAndrew Lunn 	0
22980b04680fSAndrew Lunn };
22990b04680fSAndrew Lunn 
23000b04680fSAndrew Lunn static const struct hwmon_channel_info m88e1121_hwmon_chip = {
23010b04680fSAndrew Lunn 	.type = hwmon_chip,
23020b04680fSAndrew Lunn 	.config = m88e1121_hwmon_chip_config,
23030b04680fSAndrew Lunn };
23040b04680fSAndrew Lunn 
23050b04680fSAndrew Lunn static u32 m88e1121_hwmon_temp_config[] = {
23060b04680fSAndrew Lunn 	HWMON_T_INPUT,
23070b04680fSAndrew Lunn 	0
23080b04680fSAndrew Lunn };
23090b04680fSAndrew Lunn 
23100b04680fSAndrew Lunn static const struct hwmon_channel_info m88e1121_hwmon_temp = {
23110b04680fSAndrew Lunn 	.type = hwmon_temp,
23120b04680fSAndrew Lunn 	.config = m88e1121_hwmon_temp_config,
23130b04680fSAndrew Lunn };
23140b04680fSAndrew Lunn 
23150b04680fSAndrew Lunn static const struct hwmon_channel_info *m88e1121_hwmon_info[] = {
23160b04680fSAndrew Lunn 	&m88e1121_hwmon_chip,
23170b04680fSAndrew Lunn 	&m88e1121_hwmon_temp,
23180b04680fSAndrew Lunn 	NULL
23190b04680fSAndrew Lunn };
23200b04680fSAndrew Lunn 
23210b04680fSAndrew Lunn static const struct hwmon_ops m88e1121_hwmon_hwmon_ops = {
23220b04680fSAndrew Lunn 	.is_visible = m88e1121_hwmon_is_visible,
23230b04680fSAndrew Lunn 	.read = m88e1121_hwmon_read,
23240b04680fSAndrew Lunn };
23250b04680fSAndrew Lunn 
23260b04680fSAndrew Lunn static const struct hwmon_chip_info m88e1121_hwmon_chip_info = {
23270b04680fSAndrew Lunn 	.ops = &m88e1121_hwmon_hwmon_ops,
23280b04680fSAndrew Lunn 	.info = m88e1121_hwmon_info,
23290b04680fSAndrew Lunn };
23300b04680fSAndrew Lunn 
23310b04680fSAndrew Lunn static int m88e1510_get_temp(struct phy_device *phydev, long *temp)
23320b04680fSAndrew Lunn {
23330b04680fSAndrew Lunn 	int ret;
23340b04680fSAndrew Lunn 
23350b04680fSAndrew Lunn 	*temp = 0;
23360b04680fSAndrew Lunn 
2337424ca4c5SRussell King 	ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
2338424ca4c5SRussell King 			     MII_88E1510_TEMP_SENSOR);
23390b04680fSAndrew Lunn 	if (ret < 0)
2340424ca4c5SRussell King 		return ret;
23410b04680fSAndrew Lunn 
23420b04680fSAndrew Lunn 	*temp = ((ret & MII_88E1510_TEMP_SENSOR_MASK) - 25) * 1000;
23430b04680fSAndrew Lunn 
2344424ca4c5SRussell King 	return 0;
23450b04680fSAndrew Lunn }
23460b04680fSAndrew Lunn 
2347f0a45816SColin Ian King static int m88e1510_get_temp_critical(struct phy_device *phydev, long *temp)
23480b04680fSAndrew Lunn {
23490b04680fSAndrew Lunn 	int ret;
23500b04680fSAndrew Lunn 
23510b04680fSAndrew Lunn 	*temp = 0;
23520b04680fSAndrew Lunn 
2353424ca4c5SRussell King 	ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
2354424ca4c5SRussell King 			     MII_88E1121_MISC_TEST);
23550b04680fSAndrew Lunn 	if (ret < 0)
2356424ca4c5SRussell King 		return ret;
23570b04680fSAndrew Lunn 
23580b04680fSAndrew Lunn 	*temp = (((ret & MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK) >>
23590b04680fSAndrew Lunn 		  MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT) * 5) - 25;
23600b04680fSAndrew Lunn 	/* convert to mC */
23610b04680fSAndrew Lunn 	*temp *= 1000;
23620b04680fSAndrew Lunn 
2363424ca4c5SRussell King 	return 0;
23640b04680fSAndrew Lunn }
23650b04680fSAndrew Lunn 
2366f0a45816SColin Ian King static int m88e1510_set_temp_critical(struct phy_device *phydev, long temp)
23670b04680fSAndrew Lunn {
23680b04680fSAndrew Lunn 	temp = temp / 1000;
23690b04680fSAndrew Lunn 	temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
23700b04680fSAndrew Lunn 
2371424ca4c5SRussell King 	return phy_modify_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
2372424ca4c5SRussell King 				MII_88E1121_MISC_TEST,
2373424ca4c5SRussell King 				MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK,
2374424ca4c5SRussell King 				temp << MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT);
23750b04680fSAndrew Lunn }
23760b04680fSAndrew Lunn 
2377f0a45816SColin Ian King static int m88e1510_get_temp_alarm(struct phy_device *phydev, long *alarm)
23780b04680fSAndrew Lunn {
23790b04680fSAndrew Lunn 	int ret;
23800b04680fSAndrew Lunn 
23810b04680fSAndrew Lunn 	*alarm = false;
23820b04680fSAndrew Lunn 
2383424ca4c5SRussell King 	ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
2384424ca4c5SRussell King 			     MII_88E1121_MISC_TEST);
23850b04680fSAndrew Lunn 	if (ret < 0)
2386424ca4c5SRussell King 		return ret;
2387424ca4c5SRussell King 
23880b04680fSAndrew Lunn 	*alarm = !!(ret & MII_88E1510_MISC_TEST_TEMP_IRQ);
23890b04680fSAndrew Lunn 
2390424ca4c5SRussell King 	return 0;
23910b04680fSAndrew Lunn }
23920b04680fSAndrew Lunn 
23930b04680fSAndrew Lunn static int m88e1510_hwmon_read(struct device *dev,
23940b04680fSAndrew Lunn 			       enum hwmon_sensor_types type,
23950b04680fSAndrew Lunn 			       u32 attr, int channel, long *temp)
23960b04680fSAndrew Lunn {
23970b04680fSAndrew Lunn 	struct phy_device *phydev = dev_get_drvdata(dev);
23980b04680fSAndrew Lunn 	int err;
23990b04680fSAndrew Lunn 
24000b04680fSAndrew Lunn 	switch (attr) {
24010b04680fSAndrew Lunn 	case hwmon_temp_input:
24020b04680fSAndrew Lunn 		err = m88e1510_get_temp(phydev, temp);
24030b04680fSAndrew Lunn 		break;
24040b04680fSAndrew Lunn 	case hwmon_temp_crit:
24050b04680fSAndrew Lunn 		err = m88e1510_get_temp_critical(phydev, temp);
24060b04680fSAndrew Lunn 		break;
24070b04680fSAndrew Lunn 	case hwmon_temp_max_alarm:
24080b04680fSAndrew Lunn 		err = m88e1510_get_temp_alarm(phydev, temp);
24090b04680fSAndrew Lunn 		break;
24100b04680fSAndrew Lunn 	default:
24110b04680fSAndrew Lunn 		return -EOPNOTSUPP;
24120b04680fSAndrew Lunn 	}
24130b04680fSAndrew Lunn 
24140b04680fSAndrew Lunn 	return err;
24150b04680fSAndrew Lunn }
24160b04680fSAndrew Lunn 
24170b04680fSAndrew Lunn static int m88e1510_hwmon_write(struct device *dev,
24180b04680fSAndrew Lunn 				enum hwmon_sensor_types type,
24190b04680fSAndrew Lunn 				u32 attr, int channel, long temp)
24200b04680fSAndrew Lunn {
24210b04680fSAndrew Lunn 	struct phy_device *phydev = dev_get_drvdata(dev);
24220b04680fSAndrew Lunn 	int err;
24230b04680fSAndrew Lunn 
24240b04680fSAndrew Lunn 	switch (attr) {
24250b04680fSAndrew Lunn 	case hwmon_temp_crit:
24260b04680fSAndrew Lunn 		err = m88e1510_set_temp_critical(phydev, temp);
24270b04680fSAndrew Lunn 		break;
24280b04680fSAndrew Lunn 	default:
24290b04680fSAndrew Lunn 		return -EOPNOTSUPP;
24300b04680fSAndrew Lunn 	}
24310b04680fSAndrew Lunn 	return err;
24320b04680fSAndrew Lunn }
24330b04680fSAndrew Lunn 
24340b04680fSAndrew Lunn static umode_t m88e1510_hwmon_is_visible(const void *data,
24350b04680fSAndrew Lunn 					 enum hwmon_sensor_types type,
24360b04680fSAndrew Lunn 					 u32 attr, int channel)
24370b04680fSAndrew Lunn {
24380b04680fSAndrew Lunn 	if (type != hwmon_temp)
24390b04680fSAndrew Lunn 		return 0;
24400b04680fSAndrew Lunn 
24410b04680fSAndrew Lunn 	switch (attr) {
24420b04680fSAndrew Lunn 	case hwmon_temp_input:
24430b04680fSAndrew Lunn 	case hwmon_temp_max_alarm:
24440b04680fSAndrew Lunn 		return 0444;
24450b04680fSAndrew Lunn 	case hwmon_temp_crit:
24460b04680fSAndrew Lunn 		return 0644;
24470b04680fSAndrew Lunn 	default:
24480b04680fSAndrew Lunn 		return 0;
24490b04680fSAndrew Lunn 	}
24500b04680fSAndrew Lunn }
24510b04680fSAndrew Lunn 
24520b04680fSAndrew Lunn static u32 m88e1510_hwmon_temp_config[] = {
24530b04680fSAndrew Lunn 	HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_MAX_ALARM,
24540b04680fSAndrew Lunn 	0
24550b04680fSAndrew Lunn };
24560b04680fSAndrew Lunn 
24570b04680fSAndrew Lunn static const struct hwmon_channel_info m88e1510_hwmon_temp = {
24580b04680fSAndrew Lunn 	.type = hwmon_temp,
24590b04680fSAndrew Lunn 	.config = m88e1510_hwmon_temp_config,
24600b04680fSAndrew Lunn };
24610b04680fSAndrew Lunn 
24620b04680fSAndrew Lunn static const struct hwmon_channel_info *m88e1510_hwmon_info[] = {
24630b04680fSAndrew Lunn 	&m88e1121_hwmon_chip,
24640b04680fSAndrew Lunn 	&m88e1510_hwmon_temp,
24650b04680fSAndrew Lunn 	NULL
24660b04680fSAndrew Lunn };
24670b04680fSAndrew Lunn 
24680b04680fSAndrew Lunn static const struct hwmon_ops m88e1510_hwmon_hwmon_ops = {
24690b04680fSAndrew Lunn 	.is_visible = m88e1510_hwmon_is_visible,
24700b04680fSAndrew Lunn 	.read = m88e1510_hwmon_read,
24710b04680fSAndrew Lunn 	.write = m88e1510_hwmon_write,
24720b04680fSAndrew Lunn };
24730b04680fSAndrew Lunn 
24740b04680fSAndrew Lunn static const struct hwmon_chip_info m88e1510_hwmon_chip_info = {
24750b04680fSAndrew Lunn 	.ops = &m88e1510_hwmon_hwmon_ops,
24760b04680fSAndrew Lunn 	.info = m88e1510_hwmon_info,
24770b04680fSAndrew Lunn };
24780b04680fSAndrew Lunn 
2479fee2d546SAndrew Lunn static int m88e6390_get_temp(struct phy_device *phydev, long *temp)
2480fee2d546SAndrew Lunn {
2481fee2d546SAndrew Lunn 	int sum = 0;
2482fee2d546SAndrew Lunn 	int oldpage;
2483fee2d546SAndrew Lunn 	int ret = 0;
2484fee2d546SAndrew Lunn 	int i;
2485fee2d546SAndrew Lunn 
2486fee2d546SAndrew Lunn 	*temp = 0;
2487fee2d546SAndrew Lunn 
2488fee2d546SAndrew Lunn 	oldpage = phy_select_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
2489fee2d546SAndrew Lunn 	if (oldpage < 0)
2490fee2d546SAndrew Lunn 		goto error;
2491fee2d546SAndrew Lunn 
2492fee2d546SAndrew Lunn 	/* Enable temperature sensor */
2493fee2d546SAndrew Lunn 	ret = __phy_read(phydev, MII_88E6390_MISC_TEST);
2494fee2d546SAndrew Lunn 	if (ret < 0)
2495fee2d546SAndrew Lunn 		goto error;
2496fee2d546SAndrew Lunn 
2497fee2d546SAndrew Lunn 	ret = ret & ~MII_88E6390_MISC_TEST_SAMPLE_MASK;
2498fee2d546SAndrew Lunn 	ret |= MII_88E6390_MISC_TEST_SAMPLE_ENABLE |
2499fee2d546SAndrew Lunn 		MII_88E6390_MISC_TEST_SAMPLE_1S;
2500fee2d546SAndrew Lunn 
2501fee2d546SAndrew Lunn 	ret = __phy_write(phydev, MII_88E6390_MISC_TEST, ret);
2502fee2d546SAndrew Lunn 	if (ret < 0)
2503fee2d546SAndrew Lunn 		goto error;
2504fee2d546SAndrew Lunn 
2505fee2d546SAndrew Lunn 	/* Wait for temperature to stabilize */
2506fee2d546SAndrew Lunn 	usleep_range(10000, 12000);
2507fee2d546SAndrew Lunn 
2508fee2d546SAndrew Lunn 	/* Reading the temperature sense has an errata. You need to read
2509fee2d546SAndrew Lunn 	 * a number of times and take an average.
2510fee2d546SAndrew Lunn 	 */
2511fee2d546SAndrew Lunn 	for (i = 0; i < MII_88E6390_TEMP_SENSOR_SAMPLES; i++) {
2512fee2d546SAndrew Lunn 		ret = __phy_read(phydev, MII_88E6390_TEMP_SENSOR);
2513fee2d546SAndrew Lunn 		if (ret < 0)
2514fee2d546SAndrew Lunn 			goto error;
2515fee2d546SAndrew Lunn 		sum += ret & MII_88E6390_TEMP_SENSOR_MASK;
2516fee2d546SAndrew Lunn 	}
2517fee2d546SAndrew Lunn 
2518fee2d546SAndrew Lunn 	sum /= MII_88E6390_TEMP_SENSOR_SAMPLES;
2519fee2d546SAndrew Lunn 	*temp = (sum  - 75) * 1000;
2520fee2d546SAndrew Lunn 
2521fee2d546SAndrew Lunn 	/* Disable temperature sensor */
2522fee2d546SAndrew Lunn 	ret = __phy_read(phydev, MII_88E6390_MISC_TEST);
2523fee2d546SAndrew Lunn 	if (ret < 0)
2524fee2d546SAndrew Lunn 		goto error;
2525fee2d546SAndrew Lunn 
2526fee2d546SAndrew Lunn 	ret = ret & ~MII_88E6390_MISC_TEST_SAMPLE_MASK;
2527fee2d546SAndrew Lunn 	ret |= MII_88E6390_MISC_TEST_SAMPLE_DISABLE;
2528fee2d546SAndrew Lunn 
2529fee2d546SAndrew Lunn 	ret = __phy_write(phydev, MII_88E6390_MISC_TEST, ret);
2530fee2d546SAndrew Lunn 
2531fee2d546SAndrew Lunn error:
2532fee2d546SAndrew Lunn 	phy_restore_page(phydev, oldpage, ret);
2533fee2d546SAndrew Lunn 
2534fee2d546SAndrew Lunn 	return ret;
2535fee2d546SAndrew Lunn }
2536fee2d546SAndrew Lunn 
2537fee2d546SAndrew Lunn static int m88e6390_hwmon_read(struct device *dev,
2538fee2d546SAndrew Lunn 			       enum hwmon_sensor_types type,
2539fee2d546SAndrew Lunn 			       u32 attr, int channel, long *temp)
2540fee2d546SAndrew Lunn {
2541fee2d546SAndrew Lunn 	struct phy_device *phydev = dev_get_drvdata(dev);
2542fee2d546SAndrew Lunn 	int err;
2543fee2d546SAndrew Lunn 
2544fee2d546SAndrew Lunn 	switch (attr) {
2545fee2d546SAndrew Lunn 	case hwmon_temp_input:
2546fee2d546SAndrew Lunn 		err = m88e6390_get_temp(phydev, temp);
2547fee2d546SAndrew Lunn 		break;
2548fee2d546SAndrew Lunn 	default:
2549fee2d546SAndrew Lunn 		return -EOPNOTSUPP;
2550fee2d546SAndrew Lunn 	}
2551fee2d546SAndrew Lunn 
2552fee2d546SAndrew Lunn 	return err;
2553fee2d546SAndrew Lunn }
2554fee2d546SAndrew Lunn 
2555fee2d546SAndrew Lunn static umode_t m88e6390_hwmon_is_visible(const void *data,
2556fee2d546SAndrew Lunn 					 enum hwmon_sensor_types type,
2557fee2d546SAndrew Lunn 					 u32 attr, int channel)
2558fee2d546SAndrew Lunn {
2559fee2d546SAndrew Lunn 	if (type != hwmon_temp)
2560fee2d546SAndrew Lunn 		return 0;
2561fee2d546SAndrew Lunn 
2562fee2d546SAndrew Lunn 	switch (attr) {
2563fee2d546SAndrew Lunn 	case hwmon_temp_input:
2564fee2d546SAndrew Lunn 		return 0444;
2565fee2d546SAndrew Lunn 	default:
2566fee2d546SAndrew Lunn 		return 0;
2567fee2d546SAndrew Lunn 	}
2568fee2d546SAndrew Lunn }
2569fee2d546SAndrew Lunn 
2570fee2d546SAndrew Lunn static u32 m88e6390_hwmon_temp_config[] = {
2571fee2d546SAndrew Lunn 	HWMON_T_INPUT,
2572fee2d546SAndrew Lunn 	0
2573fee2d546SAndrew Lunn };
2574fee2d546SAndrew Lunn 
2575fee2d546SAndrew Lunn static const struct hwmon_channel_info m88e6390_hwmon_temp = {
2576fee2d546SAndrew Lunn 	.type = hwmon_temp,
2577fee2d546SAndrew Lunn 	.config = m88e6390_hwmon_temp_config,
2578fee2d546SAndrew Lunn };
2579fee2d546SAndrew Lunn 
2580fee2d546SAndrew Lunn static const struct hwmon_channel_info *m88e6390_hwmon_info[] = {
2581fee2d546SAndrew Lunn 	&m88e1121_hwmon_chip,
2582fee2d546SAndrew Lunn 	&m88e6390_hwmon_temp,
2583fee2d546SAndrew Lunn 	NULL
2584fee2d546SAndrew Lunn };
2585fee2d546SAndrew Lunn 
2586fee2d546SAndrew Lunn static const struct hwmon_ops m88e6390_hwmon_hwmon_ops = {
2587fee2d546SAndrew Lunn 	.is_visible = m88e6390_hwmon_is_visible,
2588fee2d546SAndrew Lunn 	.read = m88e6390_hwmon_read,
2589fee2d546SAndrew Lunn };
2590fee2d546SAndrew Lunn 
2591fee2d546SAndrew Lunn static const struct hwmon_chip_info m88e6390_hwmon_chip_info = {
2592fee2d546SAndrew Lunn 	.ops = &m88e6390_hwmon_hwmon_ops,
2593fee2d546SAndrew Lunn 	.info = m88e6390_hwmon_info,
2594fee2d546SAndrew Lunn };
2595fee2d546SAndrew Lunn 
25960b04680fSAndrew Lunn static int marvell_hwmon_name(struct phy_device *phydev)
25970b04680fSAndrew Lunn {
25980b04680fSAndrew Lunn 	struct marvell_priv *priv = phydev->priv;
25990b04680fSAndrew Lunn 	struct device *dev = &phydev->mdio.dev;
26000b04680fSAndrew Lunn 	const char *devname = dev_name(dev);
26010b04680fSAndrew Lunn 	size_t len = strlen(devname);
26020b04680fSAndrew Lunn 	int i, j;
26030b04680fSAndrew Lunn 
26040b04680fSAndrew Lunn 	priv->hwmon_name = devm_kzalloc(dev, len, GFP_KERNEL);
26050b04680fSAndrew Lunn 	if (!priv->hwmon_name)
26060b04680fSAndrew Lunn 		return -ENOMEM;
26070b04680fSAndrew Lunn 
26080b04680fSAndrew Lunn 	for (i = j = 0; i < len && devname[i]; i++) {
26090b04680fSAndrew Lunn 		if (isalnum(devname[i]))
26100b04680fSAndrew Lunn 			priv->hwmon_name[j++] = devname[i];
26110b04680fSAndrew Lunn 	}
26120b04680fSAndrew Lunn 
26130b04680fSAndrew Lunn 	return 0;
26140b04680fSAndrew Lunn }
26150b04680fSAndrew Lunn 
26160b04680fSAndrew Lunn static int marvell_hwmon_probe(struct phy_device *phydev,
26170b04680fSAndrew Lunn 			       const struct hwmon_chip_info *chip)
26180b04680fSAndrew Lunn {
26190b04680fSAndrew Lunn 	struct marvell_priv *priv = phydev->priv;
26200b04680fSAndrew Lunn 	struct device *dev = &phydev->mdio.dev;
26210b04680fSAndrew Lunn 	int err;
26220b04680fSAndrew Lunn 
26230b04680fSAndrew Lunn 	err = marvell_hwmon_name(phydev);
26240b04680fSAndrew Lunn 	if (err)
26250b04680fSAndrew Lunn 		return err;
26260b04680fSAndrew Lunn 
26270b04680fSAndrew Lunn 	priv->hwmon_dev = devm_hwmon_device_register_with_info(
26280b04680fSAndrew Lunn 		dev, priv->hwmon_name, phydev, chip, NULL);
26290b04680fSAndrew Lunn 
26300b04680fSAndrew Lunn 	return PTR_ERR_OR_ZERO(priv->hwmon_dev);
26310b04680fSAndrew Lunn }
26320b04680fSAndrew Lunn 
26330b04680fSAndrew Lunn static int m88e1121_hwmon_probe(struct phy_device *phydev)
26340b04680fSAndrew Lunn {
26350b04680fSAndrew Lunn 	return marvell_hwmon_probe(phydev, &m88e1121_hwmon_chip_info);
26360b04680fSAndrew Lunn }
26370b04680fSAndrew Lunn 
26380b04680fSAndrew Lunn static int m88e1510_hwmon_probe(struct phy_device *phydev)
26390b04680fSAndrew Lunn {
26400b04680fSAndrew Lunn 	return marvell_hwmon_probe(phydev, &m88e1510_hwmon_chip_info);
26410b04680fSAndrew Lunn }
2642fee2d546SAndrew Lunn 
2643fee2d546SAndrew Lunn static int m88e6390_hwmon_probe(struct phy_device *phydev)
2644fee2d546SAndrew Lunn {
2645fee2d546SAndrew Lunn 	return marvell_hwmon_probe(phydev, &m88e6390_hwmon_chip_info);
2646fee2d546SAndrew Lunn }
26470b04680fSAndrew Lunn #else
26480b04680fSAndrew Lunn static int m88e1121_hwmon_probe(struct phy_device *phydev)
26490b04680fSAndrew Lunn {
26500b04680fSAndrew Lunn 	return 0;
26510b04680fSAndrew Lunn }
26520b04680fSAndrew Lunn 
26530b04680fSAndrew Lunn static int m88e1510_hwmon_probe(struct phy_device *phydev)
26540b04680fSAndrew Lunn {
26550b04680fSAndrew Lunn 	return 0;
26560b04680fSAndrew Lunn }
2657fee2d546SAndrew Lunn 
2658fee2d546SAndrew Lunn static int m88e6390_hwmon_probe(struct phy_device *phydev)
2659fee2d546SAndrew Lunn {
2660fee2d546SAndrew Lunn 	return 0;
2661fee2d546SAndrew Lunn }
26620b04680fSAndrew Lunn #endif
26630b04680fSAndrew Lunn 
2664d2fa47d9SAndrew Lunn static int marvell_probe(struct phy_device *phydev)
2665d2fa47d9SAndrew Lunn {
2666d2fa47d9SAndrew Lunn 	struct marvell_priv *priv;
2667d2fa47d9SAndrew Lunn 
2668e5a03bfdSAndrew Lunn 	priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
2669d2fa47d9SAndrew Lunn 	if (!priv)
2670d2fa47d9SAndrew Lunn 		return -ENOMEM;
2671d2fa47d9SAndrew Lunn 
2672d2fa47d9SAndrew Lunn 	phydev->priv = priv;
2673d2fa47d9SAndrew Lunn 
2674d2fa47d9SAndrew Lunn 	return 0;
2675d2fa47d9SAndrew Lunn }
2676d2fa47d9SAndrew Lunn 
26770b04680fSAndrew Lunn static int m88e1121_probe(struct phy_device *phydev)
26780b04680fSAndrew Lunn {
26790b04680fSAndrew Lunn 	int err;
26800b04680fSAndrew Lunn 
26810b04680fSAndrew Lunn 	err = marvell_probe(phydev);
26820b04680fSAndrew Lunn 	if (err)
26830b04680fSAndrew Lunn 		return err;
26840b04680fSAndrew Lunn 
26850b04680fSAndrew Lunn 	return m88e1121_hwmon_probe(phydev);
26860b04680fSAndrew Lunn }
26870b04680fSAndrew Lunn 
26880b04680fSAndrew Lunn static int m88e1510_probe(struct phy_device *phydev)
26890b04680fSAndrew Lunn {
26900b04680fSAndrew Lunn 	int err;
26910b04680fSAndrew Lunn 
26920b04680fSAndrew Lunn 	err = marvell_probe(phydev);
26930b04680fSAndrew Lunn 	if (err)
26940b04680fSAndrew Lunn 		return err;
26950b04680fSAndrew Lunn 
26960b04680fSAndrew Lunn 	return m88e1510_hwmon_probe(phydev);
26970b04680fSAndrew Lunn }
26980b04680fSAndrew Lunn 
2699fee2d546SAndrew Lunn static int m88e6390_probe(struct phy_device *phydev)
2700fee2d546SAndrew Lunn {
2701fee2d546SAndrew Lunn 	int err;
2702fee2d546SAndrew Lunn 
2703fee2d546SAndrew Lunn 	err = marvell_probe(phydev);
2704fee2d546SAndrew Lunn 	if (err)
2705fee2d546SAndrew Lunn 		return err;
2706fee2d546SAndrew Lunn 
2707fee2d546SAndrew Lunn 	return m88e6390_hwmon_probe(phydev);
2708fee2d546SAndrew Lunn }
2709fee2d546SAndrew Lunn 
2710e5479239SOlof Johansson static struct phy_driver marvell_drivers[] = {
2711e5479239SOlof Johansson 	{
27122f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1101,
27132f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
271400db8189SAndy Fleming 		.name = "Marvell 88E1101",
2715dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
271618702414SArnd Bergmann 		.probe = marvell_probe,
2717ef0f9545SMaxim Kochetkov 		.config_init = marvell_config_init,
2718ef0f9545SMaxim Kochetkov 		.config_aneg = m88e1101_config_aneg,
2719ef0f9545SMaxim Kochetkov 		.config_intr = marvell_config_intr,
2720a0723b37SIoana Ciornei 		.handle_interrupt = marvell_handle_interrupt,
2721ef0f9545SMaxim Kochetkov 		.resume = genphy_resume,
2722ef0f9545SMaxim Kochetkov 		.suspend = genphy_suspend,
2723424ca4c5SRussell King 		.read_page = marvell_read_page,
2724424ca4c5SRussell King 		.write_page = marvell_write_page,
2725d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2726d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2727d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2728e5479239SOlof Johansson 	},
2729e5479239SOlof Johansson 	{
27302f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1112,
27312f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
273285cfb534SOlof Johansson 		.name = "Marvell 88E1112",
2733dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2734d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
2735ef0f9545SMaxim Kochetkov 		.config_init = m88e1111_config_init,
2736ef0f9545SMaxim Kochetkov 		.config_aneg = marvell_config_aneg,
2737ef0f9545SMaxim Kochetkov 		.config_intr = marvell_config_intr,
2738a0723b37SIoana Ciornei 		.handle_interrupt = marvell_handle_interrupt,
2739ef0f9545SMaxim Kochetkov 		.resume = genphy_resume,
2740ef0f9545SMaxim Kochetkov 		.suspend = genphy_suspend,
2741424ca4c5SRussell King 		.read_page = marvell_read_page,
2742424ca4c5SRussell King 		.write_page = marvell_write_page,
2743d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2744d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2745d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2746262caf47SHeiner Kallweit 		.get_tunable = m88e1011_get_tunable,
2747262caf47SHeiner Kallweit 		.set_tunable = m88e1011_set_tunable,
274885cfb534SOlof Johansson 	},
274985cfb534SOlof Johansson 	{
27502f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1111,
27512f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
275276884679SAndy Fleming 		.name = "Marvell 88E1111",
2753dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2754d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
2755ef0f9545SMaxim Kochetkov 		.config_init = m88e1111_config_init,
27561887023aSRobert Hancock 		.config_aneg = m88e1111_config_aneg,
27571887023aSRobert Hancock 		.read_status = marvell_read_status,
27581887023aSRobert Hancock 		.config_intr = marvell_config_intr,
2759a0723b37SIoana Ciornei 		.handle_interrupt = marvell_handle_interrupt,
27601887023aSRobert Hancock 		.resume = genphy_resume,
27611887023aSRobert Hancock 		.suspend = genphy_suspend,
27621887023aSRobert Hancock 		.read_page = marvell_read_page,
27631887023aSRobert Hancock 		.write_page = marvell_write_page,
27641887023aSRobert Hancock 		.get_sset_count = marvell_get_sset_count,
27651887023aSRobert Hancock 		.get_strings = marvell_get_strings,
27661887023aSRobert Hancock 		.get_stats = marvell_get_stats,
27671887023aSRobert Hancock 		.get_tunable = m88e1111_get_tunable,
27681887023aSRobert Hancock 		.set_tunable = m88e1111_set_tunable,
27691887023aSRobert Hancock 	},
27701887023aSRobert Hancock 	{
27711887023aSRobert Hancock 		.phy_id = MARVELL_PHY_ID_88E1111_FINISAR,
27721887023aSRobert Hancock 		.phy_id_mask = MARVELL_PHY_ID_MASK,
27731887023aSRobert Hancock 		.name = "Marvell 88E1111 (Finisar)",
27741887023aSRobert Hancock 		/* PHY_GBIT_FEATURES */
27751887023aSRobert Hancock 		.probe = marvell_probe,
27761887023aSRobert Hancock 		.config_init = m88e1111_config_init,
27771887023aSRobert Hancock 		.config_aneg = m88e1111_config_aneg,
2778ef0f9545SMaxim Kochetkov 		.read_status = marvell_read_status,
2779ef0f9545SMaxim Kochetkov 		.config_intr = marvell_config_intr,
2780a0723b37SIoana Ciornei 		.handle_interrupt = marvell_handle_interrupt,
2781ef0f9545SMaxim Kochetkov 		.resume = genphy_resume,
2782ef0f9545SMaxim Kochetkov 		.suspend = genphy_suspend,
2783424ca4c5SRussell King 		.read_page = marvell_read_page,
2784424ca4c5SRussell King 		.write_page = marvell_write_page,
2785d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2786d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2787d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
27885c6bc519SHeiner Kallweit 		.get_tunable = m88e1111_get_tunable,
27895c6bc519SHeiner Kallweit 		.set_tunable = m88e1111_set_tunable,
2790e5479239SOlof Johansson 	},
2791e5479239SOlof Johansson 	{
27922f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1118,
27932f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2794605f196eSRon Madrid 		.name = "Marvell 88E1118",
2795dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2796d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
2797ef0f9545SMaxim Kochetkov 		.config_init = m88e1118_config_init,
2798ef0f9545SMaxim Kochetkov 		.config_aneg = m88e1118_config_aneg,
2799ef0f9545SMaxim Kochetkov 		.config_intr = marvell_config_intr,
2800a0723b37SIoana Ciornei 		.handle_interrupt = marvell_handle_interrupt,
2801ef0f9545SMaxim Kochetkov 		.resume = genphy_resume,
2802ef0f9545SMaxim Kochetkov 		.suspend = genphy_suspend,
2803424ca4c5SRussell King 		.read_page = marvell_read_page,
2804424ca4c5SRussell King 		.write_page = marvell_write_page,
2805d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2806d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2807d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2808605f196eSRon Madrid 	},
2809605f196eSRon Madrid 	{
28102f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1121R,
28112f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2812140bc929SSergei Poselenov 		.name = "Marvell 88E1121R",
2813dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2814ef0f9545SMaxim Kochetkov 		.probe = m88e1121_probe,
2815ef0f9545SMaxim Kochetkov 		.config_init = marvell_config_init,
2816ef0f9545SMaxim Kochetkov 		.config_aneg = m88e1121_config_aneg,
2817ef0f9545SMaxim Kochetkov 		.read_status = marvell_read_status,
2818ef0f9545SMaxim Kochetkov 		.config_intr = marvell_config_intr,
2819a0723b37SIoana Ciornei 		.handle_interrupt = marvell_handle_interrupt,
2820ef0f9545SMaxim Kochetkov 		.resume = genphy_resume,
2821ef0f9545SMaxim Kochetkov 		.suspend = genphy_suspend,
2822424ca4c5SRussell King 		.read_page = marvell_read_page,
2823424ca4c5SRussell King 		.write_page = marvell_write_page,
2824d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2825d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2826d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2827911af5e1SHeiner Kallweit 		.get_tunable = m88e1011_get_tunable,
2828911af5e1SHeiner Kallweit 		.set_tunable = m88e1011_set_tunable,
2829140bc929SSergei Poselenov 	},
2830140bc929SSergei Poselenov 	{
2831337ac9d5SCyril Chemparathy 		.phy_id = MARVELL_PHY_ID_88E1318S,
28326ba74014SLinus Torvalds 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2833337ac9d5SCyril Chemparathy 		.name = "Marvell 88E1318S",
2834dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2835d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
2836ef0f9545SMaxim Kochetkov 		.config_init = m88e1318_config_init,
2837ef0f9545SMaxim Kochetkov 		.config_aneg = m88e1318_config_aneg,
2838ef0f9545SMaxim Kochetkov 		.read_status = marvell_read_status,
2839ef0f9545SMaxim Kochetkov 		.config_intr = marvell_config_intr,
2840a0723b37SIoana Ciornei 		.handle_interrupt = marvell_handle_interrupt,
2841ef0f9545SMaxim Kochetkov 		.get_wol = m88e1318_get_wol,
2842ef0f9545SMaxim Kochetkov 		.set_wol = m88e1318_set_wol,
2843ef0f9545SMaxim Kochetkov 		.resume = genphy_resume,
2844ef0f9545SMaxim Kochetkov 		.suspend = genphy_suspend,
2845424ca4c5SRussell King 		.read_page = marvell_read_page,
2846424ca4c5SRussell King 		.write_page = marvell_write_page,
2847d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2848d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2849d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
28503ff1c259SCyril Chemparathy 	},
28513ff1c259SCyril Chemparathy 	{
28522f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1145,
28532f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
285476884679SAndy Fleming 		.name = "Marvell 88E1145",
2855dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2856d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
2857ef0f9545SMaxim Kochetkov 		.config_init = m88e1145_config_init,
2858ef0f9545SMaxim Kochetkov 		.config_aneg = m88e1101_config_aneg,
2859ef0f9545SMaxim Kochetkov 		.config_intr = marvell_config_intr,
2860a0723b37SIoana Ciornei 		.handle_interrupt = marvell_handle_interrupt,
2861ef0f9545SMaxim Kochetkov 		.resume = genphy_resume,
2862ef0f9545SMaxim Kochetkov 		.suspend = genphy_suspend,
2863424ca4c5SRussell King 		.read_page = marvell_read_page,
2864424ca4c5SRussell King 		.write_page = marvell_write_page,
2865d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2866d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2867d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2868a319fb52SHeiner Kallweit 		.get_tunable = m88e1111_get_tunable,
2869a319fb52SHeiner Kallweit 		.set_tunable = m88e1111_set_tunable,
2870ac8c635aSOlof Johansson 	},
2871ac8c635aSOlof Johansson 	{
287290600732SDavid Daney 		.phy_id = MARVELL_PHY_ID_88E1149R,
287390600732SDavid Daney 		.phy_id_mask = MARVELL_PHY_ID_MASK,
287490600732SDavid Daney 		.name = "Marvell 88E1149R",
2875dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2876d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
2877ef0f9545SMaxim Kochetkov 		.config_init = m88e1149_config_init,
2878ef0f9545SMaxim Kochetkov 		.config_aneg = m88e1118_config_aneg,
2879ef0f9545SMaxim Kochetkov 		.config_intr = marvell_config_intr,
2880a0723b37SIoana Ciornei 		.handle_interrupt = marvell_handle_interrupt,
2881ef0f9545SMaxim Kochetkov 		.resume = genphy_resume,
2882ef0f9545SMaxim Kochetkov 		.suspend = genphy_suspend,
2883424ca4c5SRussell King 		.read_page = marvell_read_page,
2884424ca4c5SRussell King 		.write_page = marvell_write_page,
2885d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2886d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2887d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
288890600732SDavid Daney 	},
288990600732SDavid Daney 	{
28902f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1240,
28912f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2892ac8c635aSOlof Johansson 		.name = "Marvell 88E1240",
2893dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2894d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
2895ef0f9545SMaxim Kochetkov 		.config_init = m88e1111_config_init,
2896ef0f9545SMaxim Kochetkov 		.config_aneg = marvell_config_aneg,
2897ef0f9545SMaxim Kochetkov 		.config_intr = marvell_config_intr,
2898a0723b37SIoana Ciornei 		.handle_interrupt = marvell_handle_interrupt,
2899ef0f9545SMaxim Kochetkov 		.resume = genphy_resume,
2900ef0f9545SMaxim Kochetkov 		.suspend = genphy_suspend,
2901424ca4c5SRussell King 		.read_page = marvell_read_page,
2902424ca4c5SRussell King 		.write_page = marvell_write_page,
2903d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2904d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2905d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2906ac8c635aSOlof Johansson 	},
29073da09a51SMichal Simek 	{
29083da09a51SMichal Simek 		.phy_id = MARVELL_PHY_ID_88E1116R,
29093da09a51SMichal Simek 		.phy_id_mask = MARVELL_PHY_ID_MASK,
29103da09a51SMichal Simek 		.name = "Marvell 88E1116R",
2911dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2912d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
2913ef0f9545SMaxim Kochetkov 		.config_init = m88e1116r_config_init,
2914ef0f9545SMaxim Kochetkov 		.config_intr = marvell_config_intr,
2915a0723b37SIoana Ciornei 		.handle_interrupt = marvell_handle_interrupt,
2916ef0f9545SMaxim Kochetkov 		.resume = genphy_resume,
2917ef0f9545SMaxim Kochetkov 		.suspend = genphy_suspend,
2918424ca4c5SRussell King 		.read_page = marvell_read_page,
2919424ca4c5SRussell King 		.write_page = marvell_write_page,
2920d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2921d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2922d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2923262caf47SHeiner Kallweit 		.get_tunable = m88e1011_get_tunable,
2924262caf47SHeiner Kallweit 		.set_tunable = m88e1011_set_tunable,
29253da09a51SMichal Simek 	},
292610e24caaSMichal Simek 	{
292710e24caaSMichal Simek 		.phy_id = MARVELL_PHY_ID_88E1510,
292810e24caaSMichal Simek 		.phy_id_mask = MARVELL_PHY_ID_MASK,
292910e24caaSMichal Simek 		.name = "Marvell 88E1510",
2930719655a1SAndrew Lunn 		.features = PHY_GBIT_FIBRE_FEATURES,
2931fc879f72SAndrew Lunn 		.flags = PHY_POLL_CABLE_TEST,
2932ef0f9545SMaxim Kochetkov 		.probe = m88e1510_probe,
2933ef0f9545SMaxim Kochetkov 		.config_init = m88e1510_config_init,
2934ef0f9545SMaxim Kochetkov 		.config_aneg = m88e1510_config_aneg,
2935ef0f9545SMaxim Kochetkov 		.read_status = marvell_read_status,
2936ef0f9545SMaxim Kochetkov 		.config_intr = marvell_config_intr,
2937a0723b37SIoana Ciornei 		.handle_interrupt = marvell_handle_interrupt,
2938ef0f9545SMaxim Kochetkov 		.get_wol = m88e1318_get_wol,
2939ef0f9545SMaxim Kochetkov 		.set_wol = m88e1318_set_wol,
2940ef0f9545SMaxim Kochetkov 		.resume = marvell_resume,
2941ef0f9545SMaxim Kochetkov 		.suspend = marvell_suspend,
2942424ca4c5SRussell King 		.read_page = marvell_read_page,
2943424ca4c5SRussell King 		.write_page = marvell_write_page,
2944d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2945d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2946d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2947f0f9b4edSLin Yun Sheng 		.set_loopback = genphy_loopback,
2948262caf47SHeiner Kallweit 		.get_tunable = m88e1011_get_tunable,
2949262caf47SHeiner Kallweit 		.set_tunable = m88e1011_set_tunable,
2950fc879f72SAndrew Lunn 		.cable_test_start = marvell_vct7_cable_test_start,
29510c9bcc1dSAndrew Lunn 		.cable_test_tdr_start = marvell_vct5_cable_test_tdr_start,
2952fc879f72SAndrew Lunn 		.cable_test_get_status = marvell_vct7_cable_test_get_status,
295310e24caaSMichal Simek 	},
29546b358aedSSebastian Hesselbarth 	{
2955819ec8e1SAndrew Lunn 		.phy_id = MARVELL_PHY_ID_88E1540,
2956819ec8e1SAndrew Lunn 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2957819ec8e1SAndrew Lunn 		.name = "Marvell 88E1540",
2958dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2959fc879f72SAndrew Lunn 		.flags = PHY_POLL_CABLE_TEST,
296018702414SArnd Bergmann 		.probe = m88e1510_probe,
2961ef0f9545SMaxim Kochetkov 		.config_init = marvell_config_init,
2962ef0f9545SMaxim Kochetkov 		.config_aneg = m88e1510_config_aneg,
2963ef0f9545SMaxim Kochetkov 		.read_status = marvell_read_status,
2964ef0f9545SMaxim Kochetkov 		.config_intr = marvell_config_intr,
2965a0723b37SIoana Ciornei 		.handle_interrupt = marvell_handle_interrupt,
2966ef0f9545SMaxim Kochetkov 		.resume = genphy_resume,
2967ef0f9545SMaxim Kochetkov 		.suspend = genphy_suspend,
2968424ca4c5SRussell King 		.read_page = marvell_read_page,
2969424ca4c5SRussell King 		.write_page = marvell_write_page,
2970d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2971d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2972d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
297369f42be8SHeiner Kallweit 		.get_tunable = m88e1540_get_tunable,
297469f42be8SHeiner Kallweit 		.set_tunable = m88e1540_set_tunable,
2975fc879f72SAndrew Lunn 		.cable_test_start = marvell_vct7_cable_test_start,
29760c9bcc1dSAndrew Lunn 		.cable_test_tdr_start = marvell_vct5_cable_test_tdr_start,
2977fc879f72SAndrew Lunn 		.cable_test_get_status = marvell_vct7_cable_test_get_status,
2978819ec8e1SAndrew Lunn 	},
2979819ec8e1SAndrew Lunn 	{
298060f06fdeSAndrew Lunn 		.phy_id = MARVELL_PHY_ID_88E1545,
298160f06fdeSAndrew Lunn 		.phy_id_mask = MARVELL_PHY_ID_MASK,
298260f06fdeSAndrew Lunn 		.name = "Marvell 88E1545",
298360f06fdeSAndrew Lunn 		.probe = m88e1510_probe,
2984dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2985fc879f72SAndrew Lunn 		.flags = PHY_POLL_CABLE_TEST,
2986ef0f9545SMaxim Kochetkov 		.config_init = marvell_config_init,
2987ef0f9545SMaxim Kochetkov 		.config_aneg = m88e1510_config_aneg,
2988ef0f9545SMaxim Kochetkov 		.read_status = marvell_read_status,
2989ef0f9545SMaxim Kochetkov 		.config_intr = marvell_config_intr,
2990a0723b37SIoana Ciornei 		.handle_interrupt = marvell_handle_interrupt,
2991ef0f9545SMaxim Kochetkov 		.resume = genphy_resume,
2992ef0f9545SMaxim Kochetkov 		.suspend = genphy_suspend,
2993424ca4c5SRussell King 		.read_page = marvell_read_page,
2994424ca4c5SRussell King 		.write_page = marvell_write_page,
299560f06fdeSAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
299660f06fdeSAndrew Lunn 		.get_strings = marvell_get_strings,
299760f06fdeSAndrew Lunn 		.get_stats = marvell_get_stats,
2998262caf47SHeiner Kallweit 		.get_tunable = m88e1540_get_tunable,
2999262caf47SHeiner Kallweit 		.set_tunable = m88e1540_set_tunable,
3000fc879f72SAndrew Lunn 		.cable_test_start = marvell_vct7_cable_test_start,
30010c9bcc1dSAndrew Lunn 		.cable_test_tdr_start = marvell_vct5_cable_test_tdr_start,
3002fc879f72SAndrew Lunn 		.cable_test_get_status = marvell_vct7_cable_test_get_status,
300360f06fdeSAndrew Lunn 	},
300460f06fdeSAndrew Lunn 	{
30056b358aedSSebastian Hesselbarth 		.phy_id = MARVELL_PHY_ID_88E3016,
30066b358aedSSebastian Hesselbarth 		.phy_id_mask = MARVELL_PHY_ID_MASK,
30076b358aedSSebastian Hesselbarth 		.name = "Marvell 88E3016",
3008dcdecdcfSHeiner Kallweit 		/* PHY_BASIC_FEATURES */
3009d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
3010ef0f9545SMaxim Kochetkov 		.config_init = m88e3016_config_init,
3011ef0f9545SMaxim Kochetkov 		.aneg_done = marvell_aneg_done,
3012ef0f9545SMaxim Kochetkov 		.read_status = marvell_read_status,
3013ef0f9545SMaxim Kochetkov 		.config_intr = marvell_config_intr,
3014a0723b37SIoana Ciornei 		.handle_interrupt = marvell_handle_interrupt,
3015ef0f9545SMaxim Kochetkov 		.resume = genphy_resume,
3016ef0f9545SMaxim Kochetkov 		.suspend = genphy_suspend,
3017424ca4c5SRussell King 		.read_page = marvell_read_page,
3018424ca4c5SRussell King 		.write_page = marvell_write_page,
3019d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
3020d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
3021d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
30226b358aedSSebastian Hesselbarth 	},
3023e4cf8a38SAndrew Lunn 	{
3024e4cf8a38SAndrew Lunn 		.phy_id = MARVELL_PHY_ID_88E6390,
3025e4cf8a38SAndrew Lunn 		.phy_id_mask = MARVELL_PHY_ID_MASK,
3026e4cf8a38SAndrew Lunn 		.name = "Marvell 88E6390",
3027dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
3028fc879f72SAndrew Lunn 		.flags = PHY_POLL_CABLE_TEST,
3029fee2d546SAndrew Lunn 		.probe = m88e6390_probe,
3030ef0f9545SMaxim Kochetkov 		.config_init = marvell_config_init,
3031ef0f9545SMaxim Kochetkov 		.config_aneg = m88e6390_config_aneg,
3032ef0f9545SMaxim Kochetkov 		.read_status = marvell_read_status,
3033ef0f9545SMaxim Kochetkov 		.config_intr = marvell_config_intr,
3034a0723b37SIoana Ciornei 		.handle_interrupt = marvell_handle_interrupt,
3035ef0f9545SMaxim Kochetkov 		.resume = genphy_resume,
3036ef0f9545SMaxim Kochetkov 		.suspend = genphy_suspend,
3037424ca4c5SRussell King 		.read_page = marvell_read_page,
3038424ca4c5SRussell King 		.write_page = marvell_write_page,
3039e4cf8a38SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
3040e4cf8a38SAndrew Lunn 		.get_strings = marvell_get_strings,
3041e4cf8a38SAndrew Lunn 		.get_stats = marvell_get_stats,
304269f42be8SHeiner Kallweit 		.get_tunable = m88e1540_get_tunable,
304369f42be8SHeiner Kallweit 		.set_tunable = m88e1540_set_tunable,
3044fc879f72SAndrew Lunn 		.cable_test_start = marvell_vct7_cable_test_start,
30450c9bcc1dSAndrew Lunn 		.cable_test_tdr_start = marvell_vct5_cable_test_tdr_start,
3046fc879f72SAndrew Lunn 		.cable_test_get_status = marvell_vct7_cable_test_get_status,
3047e4cf8a38SAndrew Lunn 	},
3048a602ea86SMaxim Kochetkov 	{
3049a602ea86SMaxim Kochetkov 		.phy_id = MARVELL_PHY_ID_88E1340S,
3050a602ea86SMaxim Kochetkov 		.phy_id_mask = MARVELL_PHY_ID_MASK,
3051a602ea86SMaxim Kochetkov 		.name = "Marvell 88E1340S",
3052a602ea86SMaxim Kochetkov 		.probe = m88e1510_probe,
3053a602ea86SMaxim Kochetkov 		/* PHY_GBIT_FEATURES */
3054a602ea86SMaxim Kochetkov 		.config_init = marvell_config_init,
3055a602ea86SMaxim Kochetkov 		.config_aneg = m88e1510_config_aneg,
3056a602ea86SMaxim Kochetkov 		.read_status = marvell_read_status,
3057a602ea86SMaxim Kochetkov 		.config_intr = marvell_config_intr,
3058a0723b37SIoana Ciornei 		.handle_interrupt = marvell_handle_interrupt,
3059a602ea86SMaxim Kochetkov 		.resume = genphy_resume,
3060a602ea86SMaxim Kochetkov 		.suspend = genphy_suspend,
3061a602ea86SMaxim Kochetkov 		.read_page = marvell_read_page,
3062a602ea86SMaxim Kochetkov 		.write_page = marvell_write_page,
3063a602ea86SMaxim Kochetkov 		.get_sset_count = marvell_get_sset_count,
3064a602ea86SMaxim Kochetkov 		.get_strings = marvell_get_strings,
3065a602ea86SMaxim Kochetkov 		.get_stats = marvell_get_stats,
3066a602ea86SMaxim Kochetkov 		.get_tunable = m88e1540_get_tunable,
3067a602ea86SMaxim Kochetkov 		.set_tunable = m88e1540_set_tunable,
3068a602ea86SMaxim Kochetkov 	},
3069f59babf9SMaxim Kochetkov 	{
3070f59babf9SMaxim Kochetkov 		.phy_id = MARVELL_PHY_ID_88E1548P,
3071f59babf9SMaxim Kochetkov 		.phy_id_mask = MARVELL_PHY_ID_MASK,
3072f59babf9SMaxim Kochetkov 		.name = "Marvell 88E1548P",
3073f59babf9SMaxim Kochetkov 		.probe = m88e1510_probe,
3074f59babf9SMaxim Kochetkov 		.features = PHY_GBIT_FIBRE_FEATURES,
3075f59babf9SMaxim Kochetkov 		.config_init = marvell_config_init,
3076f59babf9SMaxim Kochetkov 		.config_aneg = m88e1510_config_aneg,
3077f59babf9SMaxim Kochetkov 		.read_status = marvell_read_status,
3078f59babf9SMaxim Kochetkov 		.config_intr = marvell_config_intr,
3079a0723b37SIoana Ciornei 		.handle_interrupt = marvell_handle_interrupt,
3080f59babf9SMaxim Kochetkov 		.resume = genphy_resume,
3081f59babf9SMaxim Kochetkov 		.suspend = genphy_suspend,
3082f59babf9SMaxim Kochetkov 		.read_page = marvell_read_page,
3083f59babf9SMaxim Kochetkov 		.write_page = marvell_write_page,
3084f59babf9SMaxim Kochetkov 		.get_sset_count = marvell_get_sset_count,
3085f59babf9SMaxim Kochetkov 		.get_strings = marvell_get_strings,
3086f59babf9SMaxim Kochetkov 		.get_stats = marvell_get_stats,
3087f59babf9SMaxim Kochetkov 		.get_tunable = m88e1540_get_tunable,
3088f59babf9SMaxim Kochetkov 		.set_tunable = m88e1540_set_tunable,
3089f59babf9SMaxim Kochetkov 	},
309076884679SAndy Fleming };
309176884679SAndy Fleming 
309250fd7150SJohan Hovold module_phy_driver(marvell_drivers);
30934e4f10f6SDavid Woodhouse 
3094cf93c945SUwe Kleine-König static struct mdio_device_id __maybe_unused marvell_tbl[] = {
3095f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1101, MARVELL_PHY_ID_MASK },
3096f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1112, MARVELL_PHY_ID_MASK },
3097f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1111, MARVELL_PHY_ID_MASK },
30981887023aSRobert Hancock 	{ MARVELL_PHY_ID_88E1111_FINISAR, MARVELL_PHY_ID_MASK },
3099f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1118, MARVELL_PHY_ID_MASK },
3100f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1121R, MARVELL_PHY_ID_MASK },
3101f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1145, MARVELL_PHY_ID_MASK },
3102f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1149R, MARVELL_PHY_ID_MASK },
3103f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1240, MARVELL_PHY_ID_MASK },
3104f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1318S, MARVELL_PHY_ID_MASK },
31053da09a51SMichal Simek 	{ MARVELL_PHY_ID_88E1116R, MARVELL_PHY_ID_MASK },
310610e24caaSMichal Simek 	{ MARVELL_PHY_ID_88E1510, MARVELL_PHY_ID_MASK },
3107819ec8e1SAndrew Lunn 	{ MARVELL_PHY_ID_88E1540, MARVELL_PHY_ID_MASK },
310860f06fdeSAndrew Lunn 	{ MARVELL_PHY_ID_88E1545, MARVELL_PHY_ID_MASK },
31096b358aedSSebastian Hesselbarth 	{ MARVELL_PHY_ID_88E3016, MARVELL_PHY_ID_MASK },
3110e4cf8a38SAndrew Lunn 	{ MARVELL_PHY_ID_88E6390, MARVELL_PHY_ID_MASK },
3111a602ea86SMaxim Kochetkov 	{ MARVELL_PHY_ID_88E1340S, MARVELL_PHY_ID_MASK },
3112f59babf9SMaxim Kochetkov 	{ MARVELL_PHY_ID_88E1548P, MARVELL_PHY_ID_MASK },
31134e4f10f6SDavid Woodhouse 	{ }
31144e4f10f6SDavid Woodhouse };
31154e4f10f6SDavid Woodhouse 
31164e4f10f6SDavid Woodhouse MODULE_DEVICE_TABLE(mdio, marvell_tbl);
3117