xref: /openbmc/linux/drivers/net/phy/marvell.c (revision fc879f72)
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
4552295666SAndrew Lunn #define MII_MARVELL_MISC_TEST_PAGE	0x06
46fc879f72SAndrew Lunn #define MII_MARVELL_VCT7_PAGE		0x07
4752295666SAndrew Lunn #define MII_MARVELL_WOL_PAGE		0x11
4827d916d6SDavid Daney 
4900db8189SAndy Fleming #define MII_M1011_IEVENT		0x13
5000db8189SAndy Fleming #define MII_M1011_IEVENT_CLEAR		0x0000
5100db8189SAndy Fleming 
5200db8189SAndy Fleming #define MII_M1011_IMASK			0x12
5300db8189SAndy Fleming #define MII_M1011_IMASK_INIT		0x6400
5400db8189SAndy Fleming #define MII_M1011_IMASK_CLEAR		0x0000
5500db8189SAndy Fleming 
5676884679SAndy Fleming #define MII_M1011_PHY_SCR			0x10
57fecd5e91SAndrew Lunn #define MII_M1011_PHY_SCR_DOWNSHIFT_EN		BIT(11)
58f8d975beSHeiner Kallweit #define MII_M1011_PHY_SCR_DOWNSHIFT_MASK	GENMASK(14, 12)
59a3bdfce7SHeiner Kallweit #define MII_M1011_PHY_SCR_DOWNSHIFT_MAX		8
60fecd5e91SAndrew Lunn #define MII_M1011_PHY_SCR_MDI			(0x0 << 5)
61fecd5e91SAndrew Lunn #define MII_M1011_PHY_SCR_MDI_X			(0x1 << 5)
62fecd5e91SAndrew Lunn #define MII_M1011_PHY_SCR_AUTO_CROSS		(0x3 << 5)
6376884679SAndy Fleming 
64a3bdfce7SHeiner Kallweit #define MII_M1011_PHY_SSR			0x11
65a3bdfce7SHeiner Kallweit #define MII_M1011_PHY_SSR_DOWNSHIFT		BIT(5)
66a3bdfce7SHeiner Kallweit 
6776884679SAndy Fleming #define MII_M1111_PHY_LED_CONTROL	0x18
6876884679SAndy Fleming #define MII_M1111_PHY_LED_DIRECT	0x4100
6976884679SAndy Fleming #define MII_M1111_PHY_LED_COMBINE	0x411c
70895ee682SKim Phillips #define MII_M1111_PHY_EXT_CR		0x14
715c6bc519SHeiner Kallweit #define MII_M1111_PHY_EXT_CR_DOWNSHIFT_MASK	GENMASK(11, 9)
725c6bc519SHeiner Kallweit #define MII_M1111_PHY_EXT_CR_DOWNSHIFT_MAX	8
735c6bc519SHeiner Kallweit #define MII_M1111_PHY_EXT_CR_DOWNSHIFT_EN	BIT(8)
7461111598SAndrew Lunn #define MII_M1111_RGMII_RX_DELAY	BIT(7)
7561111598SAndrew Lunn #define MII_M1111_RGMII_TX_DELAY	BIT(1)
76895ee682SKim Phillips #define MII_M1111_PHY_EXT_SR		0x1b
77be937f1fSAlexandr Smirnov 
78895ee682SKim Phillips #define MII_M1111_HWCFG_MODE_MASK		0xf
79be937f1fSAlexandr Smirnov #define MII_M1111_HWCFG_MODE_FIBER_RGMII	0x3
804117b5beSKapil Juneja #define MII_M1111_HWCFG_MODE_SGMII_NO_CLK	0x4
81865b813aSAndrew Lunn #define MII_M1111_HWCFG_MODE_RTBI		0x7
825f8cbc13SLiu Yu-B13201 #define MII_M1111_HWCFG_MODE_COPPER_RTBI	0x9
83865b813aSAndrew Lunn #define MII_M1111_HWCFG_MODE_COPPER_RGMII	0xb
84865b813aSAndrew Lunn #define MII_M1111_HWCFG_FIBER_COPPER_RES	BIT(13)
85865b813aSAndrew Lunn #define MII_M1111_HWCFG_FIBER_COPPER_AUTO	BIT(15)
86be937f1fSAlexandr Smirnov 
87c477d044SCyril Chemparathy #define MII_88E1121_PHY_MSCR_REG	21
88c477d044SCyril Chemparathy #define MII_88E1121_PHY_MSCR_RX_DELAY	BIT(5)
89c477d044SCyril Chemparathy #define MII_88E1121_PHY_MSCR_TX_DELAY	BIT(4)
90424ca4c5SRussell King #define MII_88E1121_PHY_MSCR_DELAY_MASK	(BIT(5) | BIT(4))
91c477d044SCyril Chemparathy 
920b04680fSAndrew Lunn #define MII_88E1121_MISC_TEST				0x1a
930b04680fSAndrew Lunn #define MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK	0x1f00
940b04680fSAndrew Lunn #define MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT	8
950b04680fSAndrew Lunn #define MII_88E1510_MISC_TEST_TEMP_IRQ_EN		BIT(7)
960b04680fSAndrew Lunn #define MII_88E1510_MISC_TEST_TEMP_IRQ			BIT(6)
970b04680fSAndrew Lunn #define MII_88E1121_MISC_TEST_TEMP_SENSOR_EN		BIT(5)
980b04680fSAndrew Lunn #define MII_88E1121_MISC_TEST_TEMP_MASK			0x1f
990b04680fSAndrew Lunn 
1000b04680fSAndrew Lunn #define MII_88E1510_TEMP_SENSOR		0x1b
1010b04680fSAndrew Lunn #define MII_88E1510_TEMP_SENSOR_MASK	0xff
1020b04680fSAndrew Lunn 
10369f42be8SHeiner Kallweit #define MII_88E1540_COPPER_CTRL3	0x1a
10469f42be8SHeiner Kallweit #define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_MASK	GENMASK(11, 10)
10569f42be8SHeiner Kallweit #define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_00MS	0
10669f42be8SHeiner Kallweit #define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_10MS	1
10769f42be8SHeiner Kallweit #define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_20MS	2
10869f42be8SHeiner Kallweit #define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_40MS	3
10969f42be8SHeiner Kallweit #define MII_88E1540_COPPER_CTRL3_FAST_LINK_DOWN		BIT(9)
11069f42be8SHeiner Kallweit 
111fee2d546SAndrew Lunn #define MII_88E6390_MISC_TEST		0x1b
112fee2d546SAndrew Lunn #define MII_88E6390_MISC_TEST_SAMPLE_1S		0
113fee2d546SAndrew Lunn #define MII_88E6390_MISC_TEST_SAMPLE_10MS	BIT(14)
114fee2d546SAndrew Lunn #define MII_88E6390_MISC_TEST_SAMPLE_DISABLE	BIT(15)
115fee2d546SAndrew Lunn #define MII_88E6390_MISC_TEST_SAMPLE_ENABLE	0
116fee2d546SAndrew Lunn #define MII_88E6390_MISC_TEST_SAMPLE_MASK	(0x3 << 14)
117fee2d546SAndrew Lunn 
118fee2d546SAndrew Lunn #define MII_88E6390_TEMP_SENSOR		0x1c
119fee2d546SAndrew Lunn #define MII_88E6390_TEMP_SENSOR_MASK	0xff
120fee2d546SAndrew Lunn #define MII_88E6390_TEMP_SENSOR_SAMPLES 10
121fee2d546SAndrew Lunn 
122337ac9d5SCyril Chemparathy #define MII_88E1318S_PHY_MSCR1_REG	16
123337ac9d5SCyril Chemparathy #define MII_88E1318S_PHY_MSCR1_PAD_ODD	BIT(6)
1243ff1c259SCyril Chemparathy 
1253871c387SMichael Stapelberg /* Copper Specific Interrupt Enable Register */
1263871c387SMichael Stapelberg #define MII_88E1318S_PHY_CSIER				0x12
1273871c387SMichael Stapelberg /* WOL Event Interrupt Enable */
1283871c387SMichael Stapelberg #define MII_88E1318S_PHY_CSIER_WOL_EIE			BIT(7)
1293871c387SMichael Stapelberg 
1303871c387SMichael Stapelberg /* LED Timer Control Register */
1313871c387SMichael Stapelberg #define MII_88E1318S_PHY_LED_TCR			0x12
1323871c387SMichael Stapelberg #define MII_88E1318S_PHY_LED_TCR_FORCE_INT		BIT(15)
1333871c387SMichael Stapelberg #define MII_88E1318S_PHY_LED_TCR_INTn_ENABLE		BIT(7)
1343871c387SMichael Stapelberg #define MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW		BIT(11)
1353871c387SMichael Stapelberg 
1363871c387SMichael Stapelberg /* Magic Packet MAC address registers */
1373871c387SMichael Stapelberg #define MII_88E1318S_PHY_MAGIC_PACKET_WORD2		0x17
1383871c387SMichael Stapelberg #define MII_88E1318S_PHY_MAGIC_PACKET_WORD1		0x18
1393871c387SMichael Stapelberg #define MII_88E1318S_PHY_MAGIC_PACKET_WORD0		0x19
1403871c387SMichael Stapelberg 
1413871c387SMichael Stapelberg #define MII_88E1318S_PHY_WOL_CTRL				0x10
1423871c387SMichael Stapelberg #define MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS		BIT(12)
1433871c387SMichael Stapelberg #define MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE	BIT(14)
1443871c387SMichael Stapelberg 
14507777246SWang Dongsheng #define MII_PHY_LED_CTRL	        16
146140bc929SSergei Poselenov #define MII_88E1121_PHY_LED_DEF		0x0030
14707777246SWang Dongsheng #define MII_88E1510_PHY_LED_DEF		0x1177
148a93f7fe1SJian Shen #define MII_88E1510_PHY_LED0_LINK_LED1_ACTIVE	0x1040
149140bc929SSergei Poselenov 
150be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS		0x11
151be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_1000	0x8000
152be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_100	0x4000
153be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_SPD_MASK	0xc000
154be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_FULLDUPLEX	0x2000
155be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_RESOLVED	0x0800
156be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_LINK	0x0400
157be937f1fSAlexandr Smirnov 
1586b358aedSSebastian Hesselbarth #define MII_88E3016_PHY_SPEC_CTRL	0x10
1596b358aedSSebastian Hesselbarth #define MII_88E3016_DISABLE_SCRAMBLER	0x0200
1606b358aedSSebastian Hesselbarth #define MII_88E3016_AUTO_MDIX_CROSSOVER	0x0030
16176884679SAndy Fleming 
162930b37eeSStefan Roese #define MII_88E1510_GEN_CTRL_REG_1		0x14
163930b37eeSStefan Roese #define MII_88E1510_GEN_CTRL_REG_1_MODE_MASK	0x7
164930b37eeSStefan Roese #define MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII	0x1	/* SGMII to copper */
165930b37eeSStefan Roese #define MII_88E1510_GEN_CTRL_REG_1_RESET	0x8000	/* Soft reset */
166930b37eeSStefan Roese 
167fc879f72SAndrew Lunn #define MII_VCT7_PAIR_0_DISTANCE	0x10
168fc879f72SAndrew Lunn #define MII_VCT7_PAIR_1_DISTANCE	0x11
169fc879f72SAndrew Lunn #define MII_VCT7_PAIR_2_DISTANCE	0x12
170fc879f72SAndrew Lunn #define MII_VCT7_PAIR_3_DISTANCE	0x13
171fc879f72SAndrew Lunn 
172fc879f72SAndrew Lunn #define MII_VCT7_RESULTS	0x14
173fc879f72SAndrew Lunn #define MII_VCT7_RESULTS_PAIR3_MASK	0xf000
174fc879f72SAndrew Lunn #define MII_VCT7_RESULTS_PAIR2_MASK	0x0f00
175fc879f72SAndrew Lunn #define MII_VCT7_RESULTS_PAIR1_MASK	0x00f0
176fc879f72SAndrew Lunn #define MII_VCT7_RESULTS_PAIR0_MASK	0x000f
177fc879f72SAndrew Lunn #define MII_VCT7_RESULTS_PAIR3_SHIFT	12
178fc879f72SAndrew Lunn #define MII_VCT7_RESULTS_PAIR2_SHIFT	8
179fc879f72SAndrew Lunn #define MII_VCT7_RESULTS_PAIR1_SHIFT	4
180fc879f72SAndrew Lunn #define MII_VCT7_RESULTS_PAIR0_SHIFT	0
181fc879f72SAndrew Lunn #define MII_VCT7_RESULTS_INVALID	0
182fc879f72SAndrew Lunn #define MII_VCT7_RESULTS_OK		1
183fc879f72SAndrew Lunn #define MII_VCT7_RESULTS_OPEN		2
184fc879f72SAndrew Lunn #define MII_VCT7_RESULTS_SAME_SHORT	3
185fc879f72SAndrew Lunn #define MII_VCT7_RESULTS_CROSS_SHORT	4
186fc879f72SAndrew Lunn #define MII_VCT7_RESULTS_BUSY		9
187fc879f72SAndrew Lunn 
188fc879f72SAndrew Lunn #define MII_VCT7_CTRL		0x15
189fc879f72SAndrew Lunn #define MII_VCT7_CTRL_RUN_NOW			BIT(15)
190fc879f72SAndrew Lunn #define MII_VCT7_CTRL_RUN_ANEG			BIT(14)
191fc879f72SAndrew Lunn #define MII_VCT7_CTRL_DISABLE_CROSS		BIT(13)
192fc879f72SAndrew Lunn #define MII_VCT7_CTRL_RUN_AFTER_BREAK_LINK	BIT(12)
193fc879f72SAndrew Lunn #define MII_VCT7_CTRL_IN_PROGRESS		BIT(11)
194fc879f72SAndrew Lunn #define MII_VCT7_CTRL_METERS			BIT(10)
195fc879f72SAndrew Lunn #define MII_VCT7_CTRL_CENTIMETERS		0
196fc879f72SAndrew Lunn 
1976cfb3bccSCharles-Antoine Couret #define LPA_PAUSE_FIBER		0x180
1986cfb3bccSCharles-Antoine Couret #define LPA_PAUSE_ASYM_FIBER	0x100
1996cfb3bccSCharles-Antoine Couret 
2002170fef7SCharles-Antoine Couret #define NB_FIBER_STATS	1
2016cfb3bccSCharles-Antoine Couret 
20200db8189SAndy Fleming MODULE_DESCRIPTION("Marvell PHY driver");
20300db8189SAndy Fleming MODULE_AUTHOR("Andy Fleming");
20400db8189SAndy Fleming MODULE_LICENSE("GPL");
20500db8189SAndy Fleming 
206d2fa47d9SAndrew Lunn struct marvell_hw_stat {
207d2fa47d9SAndrew Lunn 	const char *string;
208d2fa47d9SAndrew Lunn 	u8 page;
209d2fa47d9SAndrew Lunn 	u8 reg;
210d2fa47d9SAndrew Lunn 	u8 bits;
211d2fa47d9SAndrew Lunn };
212d2fa47d9SAndrew Lunn 
213d2fa47d9SAndrew Lunn static struct marvell_hw_stat marvell_hw_stats[] = {
2142170fef7SCharles-Antoine Couret 	{ "phy_receive_errors_copper", 0, 21, 16},
215d2fa47d9SAndrew Lunn 	{ "phy_idle_errors", 0, 10, 8 },
2162170fef7SCharles-Antoine Couret 	{ "phy_receive_errors_fiber", 1, 21, 16},
217d2fa47d9SAndrew Lunn };
218d2fa47d9SAndrew Lunn 
219d2fa47d9SAndrew Lunn struct marvell_priv {
220d2fa47d9SAndrew Lunn 	u64 stats[ARRAY_SIZE(marvell_hw_stats)];
2210b04680fSAndrew Lunn 	char *hwmon_name;
2220b04680fSAndrew Lunn 	struct device *hwmon_dev;
223d2fa47d9SAndrew Lunn };
224d2fa47d9SAndrew Lunn 
225424ca4c5SRussell King static int marvell_read_page(struct phy_device *phydev)
2266427bb2dSAndrew Lunn {
227424ca4c5SRussell King 	return __phy_read(phydev, MII_MARVELL_PHY_PAGE);
228424ca4c5SRussell King }
229424ca4c5SRussell King 
230424ca4c5SRussell King static int marvell_write_page(struct phy_device *phydev, int page)
231424ca4c5SRussell King {
232424ca4c5SRussell King 	return __phy_write(phydev, MII_MARVELL_PHY_PAGE, page);
2336427bb2dSAndrew Lunn }
2346427bb2dSAndrew Lunn 
2356427bb2dSAndrew Lunn static int marvell_set_page(struct phy_device *phydev, int page)
2366427bb2dSAndrew Lunn {
2376427bb2dSAndrew Lunn 	return phy_write(phydev, MII_MARVELL_PHY_PAGE, page);
2386427bb2dSAndrew Lunn }
2396427bb2dSAndrew Lunn 
24000db8189SAndy Fleming static int marvell_ack_interrupt(struct phy_device *phydev)
24100db8189SAndy Fleming {
24200db8189SAndy Fleming 	int err;
24300db8189SAndy Fleming 
24400db8189SAndy Fleming 	/* Clear the interrupts by reading the reg */
24500db8189SAndy Fleming 	err = phy_read(phydev, MII_M1011_IEVENT);
24600db8189SAndy Fleming 
24700db8189SAndy Fleming 	if (err < 0)
24800db8189SAndy Fleming 		return err;
24900db8189SAndy Fleming 
25000db8189SAndy Fleming 	return 0;
25100db8189SAndy Fleming }
25200db8189SAndy Fleming 
25300db8189SAndy Fleming static int marvell_config_intr(struct phy_device *phydev)
25400db8189SAndy Fleming {
25500db8189SAndy Fleming 	int err;
25600db8189SAndy Fleming 
25700db8189SAndy Fleming 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
25823beb38fSAndrew Lunn 		err = phy_write(phydev, MII_M1011_IMASK,
25923beb38fSAndrew Lunn 				MII_M1011_IMASK_INIT);
26000db8189SAndy Fleming 	else
26123beb38fSAndrew Lunn 		err = phy_write(phydev, MII_M1011_IMASK,
26223beb38fSAndrew Lunn 				MII_M1011_IMASK_CLEAR);
26300db8189SAndy Fleming 
26400db8189SAndy Fleming 	return err;
26500db8189SAndy Fleming }
26600db8189SAndy Fleming 
267239aa55bSDavid Thomson static int marvell_set_polarity(struct phy_device *phydev, int polarity)
268239aa55bSDavid Thomson {
269239aa55bSDavid Thomson 	int reg;
270239aa55bSDavid Thomson 	int err;
271239aa55bSDavid Thomson 	int val;
272239aa55bSDavid Thomson 
273239aa55bSDavid Thomson 	/* get the current settings */
274239aa55bSDavid Thomson 	reg = phy_read(phydev, MII_M1011_PHY_SCR);
275239aa55bSDavid Thomson 	if (reg < 0)
276239aa55bSDavid Thomson 		return reg;
277239aa55bSDavid Thomson 
278239aa55bSDavid Thomson 	val = reg;
279239aa55bSDavid Thomson 	val &= ~MII_M1011_PHY_SCR_AUTO_CROSS;
280239aa55bSDavid Thomson 	switch (polarity) {
281239aa55bSDavid Thomson 	case ETH_TP_MDI:
282239aa55bSDavid Thomson 		val |= MII_M1011_PHY_SCR_MDI;
283239aa55bSDavid Thomson 		break;
284239aa55bSDavid Thomson 	case ETH_TP_MDI_X:
285239aa55bSDavid Thomson 		val |= MII_M1011_PHY_SCR_MDI_X;
286239aa55bSDavid Thomson 		break;
287239aa55bSDavid Thomson 	case ETH_TP_MDI_AUTO:
288239aa55bSDavid Thomson 	case ETH_TP_MDI_INVALID:
289239aa55bSDavid Thomson 	default:
290239aa55bSDavid Thomson 		val |= MII_M1011_PHY_SCR_AUTO_CROSS;
291239aa55bSDavid Thomson 		break;
292239aa55bSDavid Thomson 	}
293239aa55bSDavid Thomson 
294239aa55bSDavid Thomson 	if (val != reg) {
295239aa55bSDavid Thomson 		/* Set the new polarity value in the register */
296239aa55bSDavid Thomson 		err = phy_write(phydev, MII_M1011_PHY_SCR, val);
297239aa55bSDavid Thomson 		if (err)
298239aa55bSDavid Thomson 			return err;
299239aa55bSDavid Thomson 	}
300239aa55bSDavid Thomson 
301d6ab9336SFlorian Fainelli 	return val != reg;
302239aa55bSDavid Thomson }
303239aa55bSDavid Thomson 
30400db8189SAndy Fleming static int marvell_config_aneg(struct phy_device *phydev)
30500db8189SAndy Fleming {
306d6ab9336SFlorian Fainelli 	int changed = 0;
30700db8189SAndy Fleming 	int err;
30800db8189SAndy Fleming 
3094e26c5c3SRaju Lakkaraju 	err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
31076884679SAndy Fleming 	if (err < 0)
31176884679SAndy Fleming 		return err;
31276884679SAndy Fleming 
313d6ab9336SFlorian Fainelli 	changed = err;
314d6ab9336SFlorian Fainelli 
31576884679SAndy Fleming 	err = phy_write(phydev, MII_M1111_PHY_LED_CONTROL,
31676884679SAndy Fleming 			MII_M1111_PHY_LED_DIRECT);
31776884679SAndy Fleming 	if (err < 0)
31876884679SAndy Fleming 		return err;
31900db8189SAndy Fleming 
32000db8189SAndy Fleming 	err = genphy_config_aneg(phydev);
3218ff44985SAnton Vorontsov 	if (err < 0)
32200db8189SAndy Fleming 		return err;
3238ff44985SAnton Vorontsov 
324d6ab9336SFlorian Fainelli 	if (phydev->autoneg != AUTONEG_ENABLE || changed) {
3250c3439bcSAndrew Lunn 		/* A write to speed/duplex bits (that is performed by
3268ff44985SAnton Vorontsov 		 * genphy_config_aneg() call above) must be followed by
3278ff44985SAnton Vorontsov 		 * a software reset. Otherwise, the write has no effect.
3288ff44985SAnton Vorontsov 		 */
32934386344SAndrew Lunn 		err = genphy_soft_reset(phydev);
3308ff44985SAnton Vorontsov 		if (err < 0)
3318ff44985SAnton Vorontsov 			return err;
3328ff44985SAnton Vorontsov 	}
3338ff44985SAnton Vorontsov 
3348ff44985SAnton Vorontsov 	return 0;
33500db8189SAndy Fleming }
33600db8189SAndy Fleming 
337f2899788SAndrew Lunn static int m88e1101_config_aneg(struct phy_device *phydev)
338f2899788SAndrew Lunn {
339f2899788SAndrew Lunn 	int err;
340f2899788SAndrew Lunn 
341f2899788SAndrew Lunn 	/* This Marvell PHY has an errata which requires
342f2899788SAndrew Lunn 	 * that certain registers get written in order
343f2899788SAndrew Lunn 	 * to restart autonegotiation
344f2899788SAndrew Lunn 	 */
34534386344SAndrew Lunn 	err = genphy_soft_reset(phydev);
346f2899788SAndrew Lunn 	if (err < 0)
347f2899788SAndrew Lunn 		return err;
348f2899788SAndrew Lunn 
349f2899788SAndrew Lunn 	err = phy_write(phydev, 0x1d, 0x1f);
350f2899788SAndrew Lunn 	if (err < 0)
351f2899788SAndrew Lunn 		return err;
352f2899788SAndrew Lunn 
353f2899788SAndrew Lunn 	err = phy_write(phydev, 0x1e, 0x200c);
354f2899788SAndrew Lunn 	if (err < 0)
355f2899788SAndrew Lunn 		return err;
356f2899788SAndrew Lunn 
357f2899788SAndrew Lunn 	err = phy_write(phydev, 0x1d, 0x5);
358f2899788SAndrew Lunn 	if (err < 0)
359f2899788SAndrew Lunn 		return err;
360f2899788SAndrew Lunn 
361f2899788SAndrew Lunn 	err = phy_write(phydev, 0x1e, 0);
362f2899788SAndrew Lunn 	if (err < 0)
363f2899788SAndrew Lunn 		return err;
364f2899788SAndrew Lunn 
365f2899788SAndrew Lunn 	err = phy_write(phydev, 0x1e, 0x100);
366f2899788SAndrew Lunn 	if (err < 0)
367f2899788SAndrew Lunn 		return err;
368f2899788SAndrew Lunn 
369f2899788SAndrew Lunn 	return marvell_config_aneg(phydev);
370f2899788SAndrew Lunn }
371f2899788SAndrew Lunn 
372cf41a51dSDavid Daney #ifdef CONFIG_OF_MDIO
3730c3439bcSAndrew Lunn /* Set and/or override some configuration registers based on the
374cf41a51dSDavid Daney  * marvell,reg-init property stored in the of_node for the phydev.
375cf41a51dSDavid Daney  *
376cf41a51dSDavid Daney  * marvell,reg-init = <reg-page reg mask value>,...;
377cf41a51dSDavid Daney  *
378cf41a51dSDavid Daney  * There may be one or more sets of <reg-page reg mask value>:
379cf41a51dSDavid Daney  *
380cf41a51dSDavid Daney  * reg-page: which register bank to use.
381cf41a51dSDavid Daney  * reg: the register.
382cf41a51dSDavid Daney  * mask: if non-zero, ANDed with existing register value.
383cf41a51dSDavid Daney  * value: ORed with the masked value and written to the regiser.
384cf41a51dSDavid Daney  *
385cf41a51dSDavid Daney  */
386cf41a51dSDavid Daney static int marvell_of_reg_init(struct phy_device *phydev)
387cf41a51dSDavid Daney {
388cf41a51dSDavid Daney 	const __be32 *paddr;
389424ca4c5SRussell King 	int len, i, saved_page, current_page, ret = 0;
390cf41a51dSDavid Daney 
391e5a03bfdSAndrew Lunn 	if (!phydev->mdio.dev.of_node)
392cf41a51dSDavid Daney 		return 0;
393cf41a51dSDavid Daney 
394e5a03bfdSAndrew Lunn 	paddr = of_get_property(phydev->mdio.dev.of_node,
395e5a03bfdSAndrew Lunn 				"marvell,reg-init", &len);
396cf41a51dSDavid Daney 	if (!paddr || len < (4 * sizeof(*paddr)))
397cf41a51dSDavid Daney 		return 0;
398cf41a51dSDavid Daney 
399424ca4c5SRussell King 	saved_page = phy_save_page(phydev);
400cf41a51dSDavid Daney 	if (saved_page < 0)
401424ca4c5SRussell King 		goto err;
402cf41a51dSDavid Daney 	current_page = saved_page;
403cf41a51dSDavid Daney 
404cf41a51dSDavid Daney 	len /= sizeof(*paddr);
405cf41a51dSDavid Daney 	for (i = 0; i < len - 3; i += 4) {
4066427bb2dSAndrew Lunn 		u16 page = be32_to_cpup(paddr + i);
407cf41a51dSDavid Daney 		u16 reg = be32_to_cpup(paddr + i + 1);
408cf41a51dSDavid Daney 		u16 mask = be32_to_cpup(paddr + i + 2);
409cf41a51dSDavid Daney 		u16 val_bits = be32_to_cpup(paddr + i + 3);
410cf41a51dSDavid Daney 		int val;
411cf41a51dSDavid Daney 
4126427bb2dSAndrew Lunn 		if (page != current_page) {
4136427bb2dSAndrew Lunn 			current_page = page;
414424ca4c5SRussell King 			ret = marvell_write_page(phydev, page);
415cf41a51dSDavid Daney 			if (ret < 0)
416cf41a51dSDavid Daney 				goto err;
417cf41a51dSDavid Daney 		}
418cf41a51dSDavid Daney 
419cf41a51dSDavid Daney 		val = 0;
420cf41a51dSDavid Daney 		if (mask) {
421424ca4c5SRussell King 			val = __phy_read(phydev, reg);
422cf41a51dSDavid Daney 			if (val < 0) {
423cf41a51dSDavid Daney 				ret = val;
424cf41a51dSDavid Daney 				goto err;
425cf41a51dSDavid Daney 			}
426cf41a51dSDavid Daney 			val &= mask;
427cf41a51dSDavid Daney 		}
428cf41a51dSDavid Daney 		val |= val_bits;
429cf41a51dSDavid Daney 
430424ca4c5SRussell King 		ret = __phy_write(phydev, reg, val);
431cf41a51dSDavid Daney 		if (ret < 0)
432cf41a51dSDavid Daney 			goto err;
433cf41a51dSDavid Daney 	}
434cf41a51dSDavid Daney err:
435424ca4c5SRussell King 	return phy_restore_page(phydev, saved_page, ret);
436cf41a51dSDavid Daney }
437cf41a51dSDavid Daney #else
438cf41a51dSDavid Daney static int marvell_of_reg_init(struct phy_device *phydev)
439cf41a51dSDavid Daney {
440cf41a51dSDavid Daney 	return 0;
441cf41a51dSDavid Daney }
442cf41a51dSDavid Daney #endif /* CONFIG_OF_MDIO */
443cf41a51dSDavid Daney 
444864dc729SAndrew Lunn static int m88e1121_config_aneg_rgmii_delays(struct phy_device *phydev)
445140bc929SSergei Poselenov {
446424ca4c5SRussell King 	int mscr;
447c477d044SCyril Chemparathy 
448c477d044SCyril Chemparathy 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
449424ca4c5SRussell King 		mscr = MII_88E1121_PHY_MSCR_RX_DELAY |
450424ca4c5SRussell King 		       MII_88E1121_PHY_MSCR_TX_DELAY;
451c477d044SCyril Chemparathy 	else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
452424ca4c5SRussell King 		mscr = MII_88E1121_PHY_MSCR_RX_DELAY;
453c477d044SCyril Chemparathy 	else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
454424ca4c5SRussell King 		mscr = MII_88E1121_PHY_MSCR_TX_DELAY;
455424ca4c5SRussell King 	else
456424ca4c5SRussell King 		mscr = 0;
457c477d044SCyril Chemparathy 
458424ca4c5SRussell King 	return phy_modify_paged(phydev, MII_MARVELL_MSCR_PAGE,
459424ca4c5SRussell King 				MII_88E1121_PHY_MSCR_REG,
460424ca4c5SRussell King 				MII_88E1121_PHY_MSCR_DELAY_MASK, mscr);
461be8c6480SArnaud Patard }
462c477d044SCyril Chemparathy 
463864dc729SAndrew Lunn static int m88e1121_config_aneg(struct phy_device *phydev)
464864dc729SAndrew Lunn {
465d6ab9336SFlorian Fainelli 	int changed = 0;
466864dc729SAndrew Lunn 	int err = 0;
467864dc729SAndrew Lunn 
468864dc729SAndrew Lunn 	if (phy_interface_is_rgmii(phydev)) {
469864dc729SAndrew Lunn 		err = m88e1121_config_aneg_rgmii_delays(phydev);
470fea23fb5SRussell King 		if (err < 0)
471864dc729SAndrew Lunn 			return err;
472864dc729SAndrew Lunn 	}
473140bc929SSergei Poselenov 
474fecd5e91SAndrew Lunn 	err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
475140bc929SSergei Poselenov 	if (err < 0)
476140bc929SSergei Poselenov 		return err;
477140bc929SSergei Poselenov 
478d6ab9336SFlorian Fainelli 	changed = err;
479d6ab9336SFlorian Fainelli 
480d6ab9336SFlorian Fainelli 	err = genphy_config_aneg(phydev);
481d6ab9336SFlorian Fainelli 	if (err < 0)
482d6ab9336SFlorian Fainelli 		return err;
483d6ab9336SFlorian Fainelli 
4844b1bd697SDavid S. Miller 	if (phydev->autoneg != AUTONEG_ENABLE || changed) {
485d6ab9336SFlorian Fainelli 		/* A software reset is used to ensure a "commit" of the
486d6ab9336SFlorian Fainelli 		 * changes is done.
487d6ab9336SFlorian Fainelli 		 */
488d6ab9336SFlorian Fainelli 		err = genphy_soft_reset(phydev);
489d6ab9336SFlorian Fainelli 		if (err < 0)
490d6ab9336SFlorian Fainelli 			return err;
491d6ab9336SFlorian Fainelli 	}
492d6ab9336SFlorian Fainelli 
493d6ab9336SFlorian Fainelli 	return 0;
494140bc929SSergei Poselenov }
495140bc929SSergei Poselenov 
496337ac9d5SCyril Chemparathy static int m88e1318_config_aneg(struct phy_device *phydev)
4973ff1c259SCyril Chemparathy {
498424ca4c5SRussell King 	int err;
4993ff1c259SCyril Chemparathy 
500424ca4c5SRussell King 	err = phy_modify_paged(phydev, MII_MARVELL_MSCR_PAGE,
501424ca4c5SRussell King 			       MII_88E1318S_PHY_MSCR1_REG,
502424ca4c5SRussell King 			       0, MII_88E1318S_PHY_MSCR1_PAD_ODD);
5033ff1c259SCyril Chemparathy 	if (err < 0)
5043ff1c259SCyril Chemparathy 		return err;
5053ff1c259SCyril Chemparathy 
5063ff1c259SCyril Chemparathy 	return m88e1121_config_aneg(phydev);
5073ff1c259SCyril Chemparathy }
5083ff1c259SCyril Chemparathy 
50978301ebeSCharles-Antoine Couret /**
5103c1bcc86SAndrew Lunn  * linkmode_adv_to_fiber_adv_t
5113c1bcc86SAndrew Lunn  * @advertise: the linkmode advertisement settings
51278301ebeSCharles-Antoine Couret  *
5133c1bcc86SAndrew Lunn  * A small helper function that translates linkmode advertisement
5143c1bcc86SAndrew Lunn  * settings to phy autonegotiation advertisements for the MII_ADV
5153c1bcc86SAndrew Lunn  * register for fiber link.
51678301ebeSCharles-Antoine Couret  */
5173c1bcc86SAndrew Lunn static inline u32 linkmode_adv_to_fiber_adv_t(unsigned long *advertise)
51878301ebeSCharles-Antoine Couret {
51978301ebeSCharles-Antoine Couret 	u32 result = 0;
52078301ebeSCharles-Antoine Couret 
5213c1bcc86SAndrew Lunn 	if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, advertise))
52220ecf424SRussell King 		result |= ADVERTISE_1000XHALF;
5233c1bcc86SAndrew Lunn 	if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, advertise))
52420ecf424SRussell King 		result |= ADVERTISE_1000XFULL;
52578301ebeSCharles-Antoine Couret 
5263c1bcc86SAndrew Lunn 	if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, advertise) &&
5273c1bcc86SAndrew Lunn 	    linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertise))
52820ecf424SRussell King 		result |= ADVERTISE_1000XPSE_ASYM;
5293c1bcc86SAndrew Lunn 	else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertise))
53020ecf424SRussell King 		result |= ADVERTISE_1000XPAUSE;
53178301ebeSCharles-Antoine Couret 
53278301ebeSCharles-Antoine Couret 	return result;
53378301ebeSCharles-Antoine Couret }
53478301ebeSCharles-Antoine Couret 
53578301ebeSCharles-Antoine Couret /**
53678301ebeSCharles-Antoine Couret  * marvell_config_aneg_fiber - restart auto-negotiation or write BMCR
53778301ebeSCharles-Antoine Couret  * @phydev: target phy_device struct
53878301ebeSCharles-Antoine Couret  *
53978301ebeSCharles-Antoine Couret  * Description: If auto-negotiation is enabled, we configure the
54078301ebeSCharles-Antoine Couret  *   advertising, and then restart auto-negotiation.  If it is not
54178301ebeSCharles-Antoine Couret  *   enabled, then we write the BMCR. Adapted for fiber link in
54278301ebeSCharles-Antoine Couret  *   some Marvell's devices.
54378301ebeSCharles-Antoine Couret  */
54478301ebeSCharles-Antoine Couret static int marvell_config_aneg_fiber(struct phy_device *phydev)
54578301ebeSCharles-Antoine Couret {
54678301ebeSCharles-Antoine Couret 	int changed = 0;
54778301ebeSCharles-Antoine Couret 	int err;
5489f4bae70SRussell King 	u16 adv;
54978301ebeSCharles-Antoine Couret 
55078301ebeSCharles-Antoine Couret 	if (phydev->autoneg != AUTONEG_ENABLE)
55178301ebeSCharles-Antoine Couret 		return genphy_setup_forced(phydev);
55278301ebeSCharles-Antoine Couret 
55378301ebeSCharles-Antoine Couret 	/* Only allow advertising what this PHY supports */
5543c1bcc86SAndrew Lunn 	linkmode_and(phydev->advertising, phydev->advertising,
5553c1bcc86SAndrew Lunn 		     phydev->supported);
55678301ebeSCharles-Antoine Couret 
5579f4bae70SRussell King 	adv = linkmode_adv_to_fiber_adv_t(phydev->advertising);
5589f4bae70SRussell King 
55978301ebeSCharles-Antoine Couret 	/* Setup fiber advertisement */
5609f4bae70SRussell King 	err = phy_modify_changed(phydev, MII_ADVERTISE,
5619f4bae70SRussell King 				 ADVERTISE_1000XHALF | ADVERTISE_1000XFULL |
5629f4bae70SRussell King 				 ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM,
5639f4bae70SRussell King 				 adv);
56478301ebeSCharles-Antoine Couret 	if (err < 0)
56578301ebeSCharles-Antoine Couret 		return err;
5669f4bae70SRussell King 	if (err > 0)
56778301ebeSCharles-Antoine Couret 		changed = 1;
56878301ebeSCharles-Antoine Couret 
569b5abac2dSRussell King 	return genphy_check_and_restart_aneg(phydev, changed);
57078301ebeSCharles-Antoine Couret }
57178301ebeSCharles-Antoine Couret 
57210e24caaSMichal Simek static int m88e1510_config_aneg(struct phy_device *phydev)
57310e24caaSMichal Simek {
57410e24caaSMichal Simek 	int err;
57510e24caaSMichal Simek 
57652295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
57778301ebeSCharles-Antoine Couret 	if (err < 0)
57878301ebeSCharles-Antoine Couret 		goto error;
57978301ebeSCharles-Antoine Couret 
58078301ebeSCharles-Antoine Couret 	/* Configure the copper link first */
58110e24caaSMichal Simek 	err = m88e1318_config_aneg(phydev);
58210e24caaSMichal Simek 	if (err < 0)
58378301ebeSCharles-Antoine Couret 		goto error;
58410e24caaSMichal Simek 
585de9c4e06SRussell King 	/* Do not touch the fiber page if we're in copper->sgmii mode */
586de9c4e06SRussell King 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII)
587de9c4e06SRussell King 		return 0;
588de9c4e06SRussell King 
58978301ebeSCharles-Antoine Couret 	/* Then the fiber link */
59052295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
59178301ebeSCharles-Antoine Couret 	if (err < 0)
59278301ebeSCharles-Antoine Couret 		goto error;
59378301ebeSCharles-Antoine Couret 
59478301ebeSCharles-Antoine Couret 	err = marvell_config_aneg_fiber(phydev);
59578301ebeSCharles-Antoine Couret 	if (err < 0)
59678301ebeSCharles-Antoine Couret 		goto error;
59778301ebeSCharles-Antoine Couret 
59852295666SAndrew Lunn 	return marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
59978301ebeSCharles-Antoine Couret 
60078301ebeSCharles-Antoine Couret error:
60152295666SAndrew Lunn 	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
60278301ebeSCharles-Antoine Couret 	return err;
60379be1a1cSClemens Gruber }
60479be1a1cSClemens Gruber 
60507777246SWang Dongsheng static void marvell_config_led(struct phy_device *phydev)
60607777246SWang Dongsheng {
60707777246SWang Dongsheng 	u16 def_config;
60807777246SWang Dongsheng 	int err;
60907777246SWang Dongsheng 
61007777246SWang Dongsheng 	switch (MARVELL_PHY_FAMILY_ID(phydev->phy_id)) {
61107777246SWang Dongsheng 	/* Default PHY LED config: LED[0] .. Link, LED[1] .. Activity */
61207777246SWang Dongsheng 	case MARVELL_PHY_FAMILY_ID(MARVELL_PHY_ID_88E1121R):
61307777246SWang Dongsheng 	case MARVELL_PHY_FAMILY_ID(MARVELL_PHY_ID_88E1318S):
61407777246SWang Dongsheng 		def_config = MII_88E1121_PHY_LED_DEF;
61507777246SWang Dongsheng 		break;
61607777246SWang Dongsheng 	/* Default PHY LED config:
61707777246SWang Dongsheng 	 * LED[0] .. 1000Mbps Link
61807777246SWang Dongsheng 	 * LED[1] .. 100Mbps Link
61907777246SWang Dongsheng 	 * LED[2] .. Blink, Activity
62007777246SWang Dongsheng 	 */
62107777246SWang Dongsheng 	case MARVELL_PHY_FAMILY_ID(MARVELL_PHY_ID_88E1510):
622a93f7fe1SJian Shen 		if (phydev->dev_flags & MARVELL_PHY_LED0_LINK_LED1_ACTIVE)
623a93f7fe1SJian Shen 			def_config = MII_88E1510_PHY_LED0_LINK_LED1_ACTIVE;
624a93f7fe1SJian Shen 		else
62507777246SWang Dongsheng 			def_config = MII_88E1510_PHY_LED_DEF;
62607777246SWang Dongsheng 		break;
62707777246SWang Dongsheng 	default:
62807777246SWang Dongsheng 		return;
62907777246SWang Dongsheng 	}
63007777246SWang Dongsheng 
63107777246SWang Dongsheng 	err = phy_write_paged(phydev, MII_MARVELL_LED_PAGE, MII_PHY_LED_CTRL,
63207777246SWang Dongsheng 			      def_config);
63307777246SWang Dongsheng 	if (err < 0)
634ab2a605fSAndrew Lunn 		phydev_warn(phydev, "Fail to config marvell phy LED.\n");
63507777246SWang Dongsheng }
63607777246SWang Dongsheng 
63779be1a1cSClemens Gruber static int marvell_config_init(struct phy_device *phydev)
63879be1a1cSClemens Gruber {
63907777246SWang Dongsheng 	/* Set defalut LED */
64007777246SWang Dongsheng 	marvell_config_led(phydev);
64107777246SWang Dongsheng 
64279be1a1cSClemens Gruber 	/* Set registers from marvell,reg-init DT property */
64310e24caaSMichal Simek 	return marvell_of_reg_init(phydev);
64410e24caaSMichal Simek }
64510e24caaSMichal Simek 
6466b358aedSSebastian Hesselbarth static int m88e3016_config_init(struct phy_device *phydev)
6476b358aedSSebastian Hesselbarth {
648fea23fb5SRussell King 	int ret;
6496b358aedSSebastian Hesselbarth 
6506b358aedSSebastian Hesselbarth 	/* Enable Scrambler and Auto-Crossover */
651fea23fb5SRussell King 	ret = phy_modify(phydev, MII_88E3016_PHY_SPEC_CTRL,
652f102852fSRussell King 			 MII_88E3016_DISABLE_SCRAMBLER,
653fea23fb5SRussell King 			 MII_88E3016_AUTO_MDIX_CROSSOVER);
654fea23fb5SRussell King 	if (ret < 0)
655fea23fb5SRussell King 		return ret;
6566b358aedSSebastian Hesselbarth 
65779be1a1cSClemens Gruber 	return marvell_config_init(phydev);
6586b358aedSSebastian Hesselbarth }
6596b358aedSSebastian Hesselbarth 
660865b813aSAndrew Lunn static int m88e1111_config_init_hwcfg_mode(struct phy_device *phydev,
661865b813aSAndrew Lunn 					   u16 mode,
662865b813aSAndrew Lunn 					   int fibre_copper_auto)
663865b813aSAndrew Lunn {
664865b813aSAndrew Lunn 	if (fibre_copper_auto)
665fea23fb5SRussell King 		mode |= MII_M1111_HWCFG_FIBER_COPPER_AUTO;
666865b813aSAndrew Lunn 
667fea23fb5SRussell King 	return phy_modify(phydev, MII_M1111_PHY_EXT_SR,
668f102852fSRussell King 			  MII_M1111_HWCFG_MODE_MASK |
669fea23fb5SRussell King 			  MII_M1111_HWCFG_FIBER_COPPER_AUTO |
670f102852fSRussell King 			  MII_M1111_HWCFG_FIBER_COPPER_RES,
671fea23fb5SRussell King 			  mode);
672865b813aSAndrew Lunn }
673865b813aSAndrew Lunn 
67461111598SAndrew Lunn static int m88e1111_config_init_rgmii_delays(struct phy_device *phydev)
675895ee682SKim Phillips {
676fea23fb5SRussell King 	int delay;
677895ee682SKim Phillips 
6789daf5a76SKim Phillips 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
679fea23fb5SRussell King 		delay = MII_M1111_RGMII_RX_DELAY | MII_M1111_RGMII_TX_DELAY;
6809daf5a76SKim Phillips 	} else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
681fea23fb5SRussell King 		delay = MII_M1111_RGMII_RX_DELAY;
6829daf5a76SKim Phillips 	} else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
683fea23fb5SRussell King 		delay = MII_M1111_RGMII_TX_DELAY;
684fea23fb5SRussell King 	} else {
685fea23fb5SRussell King 		delay = 0;
6869daf5a76SKim Phillips 	}
687895ee682SKim Phillips 
688fea23fb5SRussell King 	return phy_modify(phydev, MII_M1111_PHY_EXT_CR,
689f102852fSRussell King 			  MII_M1111_RGMII_RX_DELAY | MII_M1111_RGMII_TX_DELAY,
690fea23fb5SRussell King 			  delay);
69161111598SAndrew Lunn }
69261111598SAndrew Lunn 
69361111598SAndrew Lunn static int m88e1111_config_init_rgmii(struct phy_device *phydev)
69461111598SAndrew Lunn {
69561111598SAndrew Lunn 	int temp;
69661111598SAndrew Lunn 	int err;
69761111598SAndrew Lunn 
69861111598SAndrew Lunn 	err = m88e1111_config_init_rgmii_delays(phydev);
699895ee682SKim Phillips 	if (err < 0)
700895ee682SKim Phillips 		return err;
701895ee682SKim Phillips 
702895ee682SKim Phillips 	temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
703895ee682SKim Phillips 	if (temp < 0)
704895ee682SKim Phillips 		return temp;
705895ee682SKim Phillips 
706895ee682SKim Phillips 	temp &= ~(MII_M1111_HWCFG_MODE_MASK);
707be937f1fSAlexandr Smirnov 
7087239016dSWang Jian 	if (temp & MII_M1111_HWCFG_FIBER_COPPER_RES)
709be937f1fSAlexandr Smirnov 		temp |= MII_M1111_HWCFG_MODE_FIBER_RGMII;
710be937f1fSAlexandr Smirnov 	else
711be937f1fSAlexandr Smirnov 		temp |= MII_M1111_HWCFG_MODE_COPPER_RGMII;
712895ee682SKim Phillips 
713e1dde8dcSAndrew Lunn 	return phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
714895ee682SKim Phillips }
715895ee682SKim Phillips 
716e1dde8dcSAndrew Lunn static int m88e1111_config_init_sgmii(struct phy_device *phydev)
717e1dde8dcSAndrew Lunn {
718e1dde8dcSAndrew Lunn 	int err;
719e1dde8dcSAndrew Lunn 
720865b813aSAndrew Lunn 	err = m88e1111_config_init_hwcfg_mode(
721865b813aSAndrew Lunn 		phydev,
722865b813aSAndrew Lunn 		MII_M1111_HWCFG_MODE_SGMII_NO_CLK,
723865b813aSAndrew Lunn 		MII_M1111_HWCFG_FIBER_COPPER_AUTO);
7244117b5beSKapil Juneja 	if (err < 0)
7254117b5beSKapil Juneja 		return err;
72607151bc9SMadalin Bucur 
72707151bc9SMadalin Bucur 	/* make sure copper is selected */
72852295666SAndrew Lunn 	return marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
7294117b5beSKapil Juneja }
7304117b5beSKapil Juneja 
731e1dde8dcSAndrew Lunn static int m88e1111_config_init_rtbi(struct phy_device *phydev)
732e1dde8dcSAndrew Lunn {
73361111598SAndrew Lunn 	int err;
734e1dde8dcSAndrew Lunn 
73561111598SAndrew Lunn 	err = m88e1111_config_init_rgmii_delays(phydev);
736fea23fb5SRussell King 	if (err < 0)
7375f8cbc13SLiu Yu-B13201 		return err;
7385f8cbc13SLiu Yu-B13201 
739865b813aSAndrew Lunn 	err = m88e1111_config_init_hwcfg_mode(
740865b813aSAndrew Lunn 		phydev,
741865b813aSAndrew Lunn 		MII_M1111_HWCFG_MODE_RTBI,
742865b813aSAndrew Lunn 		MII_M1111_HWCFG_FIBER_COPPER_AUTO);
7435f8cbc13SLiu Yu-B13201 	if (err < 0)
7445f8cbc13SLiu Yu-B13201 		return err;
7455f8cbc13SLiu Yu-B13201 
7465f8cbc13SLiu Yu-B13201 	/* soft reset */
74734386344SAndrew Lunn 	err = genphy_soft_reset(phydev);
7485f8cbc13SLiu Yu-B13201 	if (err < 0)
7495f8cbc13SLiu Yu-B13201 		return err;
750e1dde8dcSAndrew Lunn 
751865b813aSAndrew Lunn 	return m88e1111_config_init_hwcfg_mode(
752865b813aSAndrew Lunn 		phydev,
753865b813aSAndrew Lunn 		MII_M1111_HWCFG_MODE_RTBI,
754865b813aSAndrew Lunn 		MII_M1111_HWCFG_FIBER_COPPER_AUTO);
755e1dde8dcSAndrew Lunn }
756e1dde8dcSAndrew Lunn 
757e1dde8dcSAndrew Lunn static int m88e1111_config_init(struct phy_device *phydev)
758e1dde8dcSAndrew Lunn {
759e1dde8dcSAndrew Lunn 	int err;
760e1dde8dcSAndrew Lunn 
761e1dde8dcSAndrew Lunn 	if (phy_interface_is_rgmii(phydev)) {
762e1dde8dcSAndrew Lunn 		err = m88e1111_config_init_rgmii(phydev);
763fea23fb5SRussell King 		if (err < 0)
764e1dde8dcSAndrew Lunn 			return err;
765e1dde8dcSAndrew Lunn 	}
766e1dde8dcSAndrew Lunn 
767e1dde8dcSAndrew Lunn 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
768e1dde8dcSAndrew Lunn 		err = m88e1111_config_init_sgmii(phydev);
769e1dde8dcSAndrew Lunn 		if (err < 0)
770e1dde8dcSAndrew Lunn 			return err;
771e1dde8dcSAndrew Lunn 	}
772e1dde8dcSAndrew Lunn 
773e1dde8dcSAndrew Lunn 	if (phydev->interface == PHY_INTERFACE_MODE_RTBI) {
774e1dde8dcSAndrew Lunn 		err = m88e1111_config_init_rtbi(phydev);
7755f8cbc13SLiu Yu-B13201 		if (err < 0)
7765f8cbc13SLiu Yu-B13201 			return err;
7775f8cbc13SLiu Yu-B13201 	}
7785f8cbc13SLiu Yu-B13201 
779cf41a51dSDavid Daney 	err = marvell_of_reg_init(phydev);
780cf41a51dSDavid Daney 	if (err < 0)
781cf41a51dSDavid Daney 		return err;
7825f8cbc13SLiu Yu-B13201 
78334386344SAndrew Lunn 	return genphy_soft_reset(phydev);
784895ee682SKim Phillips }
785895ee682SKim Phillips 
7865c6bc519SHeiner Kallweit static int m88e1111_get_downshift(struct phy_device *phydev, u8 *data)
7875c6bc519SHeiner Kallweit {
7885c6bc519SHeiner Kallweit 	int val, cnt, enable;
7895c6bc519SHeiner Kallweit 
7905c6bc519SHeiner Kallweit 	val = phy_read(phydev, MII_M1111_PHY_EXT_CR);
7915c6bc519SHeiner Kallweit 	if (val < 0)
7925c6bc519SHeiner Kallweit 		return val;
7935c6bc519SHeiner Kallweit 
7945c6bc519SHeiner Kallweit 	enable = FIELD_GET(MII_M1111_PHY_EXT_CR_DOWNSHIFT_EN, val);
7955c6bc519SHeiner Kallweit 	cnt = FIELD_GET(MII_M1111_PHY_EXT_CR_DOWNSHIFT_MASK, val) + 1;
7965c6bc519SHeiner Kallweit 
7975c6bc519SHeiner Kallweit 	*data = enable ? cnt : DOWNSHIFT_DEV_DISABLE;
7985c6bc519SHeiner Kallweit 
7995c6bc519SHeiner Kallweit 	return 0;
8005c6bc519SHeiner Kallweit }
8015c6bc519SHeiner Kallweit 
8025c6bc519SHeiner Kallweit static int m88e1111_set_downshift(struct phy_device *phydev, u8 cnt)
8035c6bc519SHeiner Kallweit {
8045c6bc519SHeiner Kallweit 	int val;
8055c6bc519SHeiner Kallweit 
8065c6bc519SHeiner Kallweit 	if (cnt > MII_M1111_PHY_EXT_CR_DOWNSHIFT_MAX)
8075c6bc519SHeiner Kallweit 		return -E2BIG;
8085c6bc519SHeiner Kallweit 
8095c6bc519SHeiner Kallweit 	if (!cnt)
8105c6bc519SHeiner Kallweit 		return phy_clear_bits(phydev, MII_M1111_PHY_EXT_CR,
8115c6bc519SHeiner Kallweit 				      MII_M1111_PHY_EXT_CR_DOWNSHIFT_EN);
8125c6bc519SHeiner Kallweit 
8135c6bc519SHeiner Kallweit 	val = MII_M1111_PHY_EXT_CR_DOWNSHIFT_EN;
8145c6bc519SHeiner Kallweit 	val |= FIELD_PREP(MII_M1111_PHY_EXT_CR_DOWNSHIFT_MASK, cnt - 1);
8155c6bc519SHeiner Kallweit 
8165c6bc519SHeiner Kallweit 	return phy_modify(phydev, MII_M1111_PHY_EXT_CR,
8175c6bc519SHeiner Kallweit 			  MII_M1111_PHY_EXT_CR_DOWNSHIFT_EN |
8185c6bc519SHeiner Kallweit 			  MII_M1111_PHY_EXT_CR_DOWNSHIFT_MASK,
8195c6bc519SHeiner Kallweit 			  val);
8205c6bc519SHeiner Kallweit }
8215c6bc519SHeiner Kallweit 
8225c6bc519SHeiner Kallweit static int m88e1111_get_tunable(struct phy_device *phydev,
8235c6bc519SHeiner Kallweit 				struct ethtool_tunable *tuna, void *data)
8245c6bc519SHeiner Kallweit {
8255c6bc519SHeiner Kallweit 	switch (tuna->id) {
8265c6bc519SHeiner Kallweit 	case ETHTOOL_PHY_DOWNSHIFT:
8275c6bc519SHeiner Kallweit 		return m88e1111_get_downshift(phydev, data);
8285c6bc519SHeiner Kallweit 	default:
8295c6bc519SHeiner Kallweit 		return -EOPNOTSUPP;
8305c6bc519SHeiner Kallweit 	}
8315c6bc519SHeiner Kallweit }
8325c6bc519SHeiner Kallweit 
8335c6bc519SHeiner Kallweit static int m88e1111_set_tunable(struct phy_device *phydev,
8345c6bc519SHeiner Kallweit 				struct ethtool_tunable *tuna, const void *data)
8355c6bc519SHeiner Kallweit {
8365c6bc519SHeiner Kallweit 	switch (tuna->id) {
8375c6bc519SHeiner Kallweit 	case ETHTOOL_PHY_DOWNSHIFT:
8385c6bc519SHeiner Kallweit 		return m88e1111_set_downshift(phydev, *(const u8 *)data);
8395c6bc519SHeiner Kallweit 	default:
8405c6bc519SHeiner Kallweit 		return -EOPNOTSUPP;
8415c6bc519SHeiner Kallweit 	}
8425c6bc519SHeiner Kallweit }
8435c6bc519SHeiner Kallweit 
844911af5e1SHeiner Kallweit static int m88e1011_get_downshift(struct phy_device *phydev, u8 *data)
845a3bdfce7SHeiner Kallweit {
846a3bdfce7SHeiner Kallweit 	int val, cnt, enable;
847a3bdfce7SHeiner Kallweit 
848a3bdfce7SHeiner Kallweit 	val = phy_read(phydev, MII_M1011_PHY_SCR);
849a3bdfce7SHeiner Kallweit 	if (val < 0)
850a3bdfce7SHeiner Kallweit 		return val;
851a3bdfce7SHeiner Kallweit 
852a3bdfce7SHeiner Kallweit 	enable = FIELD_GET(MII_M1011_PHY_SCR_DOWNSHIFT_EN, val);
853f8d975beSHeiner Kallweit 	cnt = FIELD_GET(MII_M1011_PHY_SCR_DOWNSHIFT_MASK, val) + 1;
854a3bdfce7SHeiner Kallweit 
855a3bdfce7SHeiner Kallweit 	*data = enable ? cnt : DOWNSHIFT_DEV_DISABLE;
856a3bdfce7SHeiner Kallweit 
857a3bdfce7SHeiner Kallweit 	return 0;
858a3bdfce7SHeiner Kallweit }
859a3bdfce7SHeiner Kallweit 
860911af5e1SHeiner Kallweit static int m88e1011_set_downshift(struct phy_device *phydev, u8 cnt)
861a3bdfce7SHeiner Kallweit {
862a3bdfce7SHeiner Kallweit 	int val;
863a3bdfce7SHeiner Kallweit 
864a3bdfce7SHeiner Kallweit 	if (cnt > MII_M1011_PHY_SCR_DOWNSHIFT_MAX)
865a3bdfce7SHeiner Kallweit 		return -E2BIG;
866a3bdfce7SHeiner Kallweit 
867a3bdfce7SHeiner Kallweit 	if (!cnt)
868a3bdfce7SHeiner Kallweit 		return phy_clear_bits(phydev, MII_M1011_PHY_SCR,
869a3bdfce7SHeiner Kallweit 				      MII_M1011_PHY_SCR_DOWNSHIFT_EN);
870a3bdfce7SHeiner Kallweit 
871a3bdfce7SHeiner Kallweit 	val = MII_M1011_PHY_SCR_DOWNSHIFT_EN;
872f8d975beSHeiner Kallweit 	val |= FIELD_PREP(MII_M1011_PHY_SCR_DOWNSHIFT_MASK, cnt - 1);
873a3bdfce7SHeiner Kallweit 
874a3bdfce7SHeiner Kallweit 	return phy_modify(phydev, MII_M1011_PHY_SCR,
875a3bdfce7SHeiner Kallweit 			  MII_M1011_PHY_SCR_DOWNSHIFT_EN |
876f8d975beSHeiner Kallweit 			  MII_M1011_PHY_SCR_DOWNSHIFT_MASK,
877a3bdfce7SHeiner Kallweit 			  val);
878a3bdfce7SHeiner Kallweit }
879a3bdfce7SHeiner Kallweit 
880911af5e1SHeiner Kallweit static int m88e1011_get_tunable(struct phy_device *phydev,
881a3bdfce7SHeiner Kallweit 				struct ethtool_tunable *tuna, void *data)
882a3bdfce7SHeiner Kallweit {
883a3bdfce7SHeiner Kallweit 	switch (tuna->id) {
884a3bdfce7SHeiner Kallweit 	case ETHTOOL_PHY_DOWNSHIFT:
885911af5e1SHeiner Kallweit 		return m88e1011_get_downshift(phydev, data);
886a3bdfce7SHeiner Kallweit 	default:
887a3bdfce7SHeiner Kallweit 		return -EOPNOTSUPP;
888a3bdfce7SHeiner Kallweit 	}
889a3bdfce7SHeiner Kallweit }
890a3bdfce7SHeiner Kallweit 
891911af5e1SHeiner Kallweit static int m88e1011_set_tunable(struct phy_device *phydev,
892a3bdfce7SHeiner Kallweit 				struct ethtool_tunable *tuna, const void *data)
893a3bdfce7SHeiner Kallweit {
894a3bdfce7SHeiner Kallweit 	switch (tuna->id) {
895a3bdfce7SHeiner Kallweit 	case ETHTOOL_PHY_DOWNSHIFT:
896911af5e1SHeiner Kallweit 		return m88e1011_set_downshift(phydev, *(const u8 *)data);
897a3bdfce7SHeiner Kallweit 	default:
898a3bdfce7SHeiner Kallweit 		return -EOPNOTSUPP;
899a3bdfce7SHeiner Kallweit 	}
900a3bdfce7SHeiner Kallweit }
901a3bdfce7SHeiner Kallweit 
902e2d861ccSHeiner Kallweit static int m88e1116r_config_init(struct phy_device *phydev)
903e2d861ccSHeiner Kallweit {
904e2d861ccSHeiner Kallweit 	int err;
905e2d861ccSHeiner Kallweit 
906e2d861ccSHeiner Kallweit 	err = genphy_soft_reset(phydev);
907e2d861ccSHeiner Kallweit 	if (err < 0)
908e2d861ccSHeiner Kallweit 		return err;
909e2d861ccSHeiner Kallweit 
910e2d861ccSHeiner Kallweit 	msleep(500);
911e2d861ccSHeiner Kallweit 
912e2d861ccSHeiner Kallweit 	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
913e2d861ccSHeiner Kallweit 	if (err < 0)
914e2d861ccSHeiner Kallweit 		return err;
915e2d861ccSHeiner Kallweit 
916e2d861ccSHeiner Kallweit 	err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
917e2d861ccSHeiner Kallweit 	if (err < 0)
918e2d861ccSHeiner Kallweit 		return err;
919e2d861ccSHeiner Kallweit 
920911af5e1SHeiner Kallweit 	err = m88e1011_set_downshift(phydev, 8);
921e2d861ccSHeiner Kallweit 	if (err < 0)
922e2d861ccSHeiner Kallweit 		return err;
923e2d861ccSHeiner Kallweit 
924e2d861ccSHeiner Kallweit 	if (phy_interface_is_rgmii(phydev)) {
925e2d861ccSHeiner Kallweit 		err = m88e1121_config_aneg_rgmii_delays(phydev);
926e2d861ccSHeiner Kallweit 		if (err < 0)
927e2d861ccSHeiner Kallweit 			return err;
928e2d861ccSHeiner Kallweit 	}
929e2d861ccSHeiner Kallweit 
930e2d861ccSHeiner Kallweit 	err = genphy_soft_reset(phydev);
931e2d861ccSHeiner Kallweit 	if (err < 0)
932e2d861ccSHeiner Kallweit 		return err;
933e2d861ccSHeiner Kallweit 
934e2d861ccSHeiner Kallweit 	return marvell_config_init(phydev);
935e2d861ccSHeiner Kallweit }
936e2d861ccSHeiner Kallweit 
937dd9a122aSEsben Haabendal static int m88e1318_config_init(struct phy_device *phydev)
938dd9a122aSEsben Haabendal {
939dd9a122aSEsben Haabendal 	if (phy_interrupt_is_valid(phydev)) {
940dd9a122aSEsben Haabendal 		int err = phy_modify_paged(
941dd9a122aSEsben Haabendal 			phydev, MII_MARVELL_LED_PAGE,
942dd9a122aSEsben Haabendal 			MII_88E1318S_PHY_LED_TCR,
943dd9a122aSEsben Haabendal 			MII_88E1318S_PHY_LED_TCR_FORCE_INT,
944dd9a122aSEsben Haabendal 			MII_88E1318S_PHY_LED_TCR_INTn_ENABLE |
945dd9a122aSEsben Haabendal 			MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW);
946dd9a122aSEsben Haabendal 		if (err < 0)
947dd9a122aSEsben Haabendal 			return err;
948dd9a122aSEsben Haabendal 	}
949dd9a122aSEsben Haabendal 
95007777246SWang Dongsheng 	return marvell_config_init(phydev);
951dd9a122aSEsben Haabendal }
952dd9a122aSEsben Haabendal 
953407353ecSClemens Gruber static int m88e1510_config_init(struct phy_device *phydev)
954407353ecSClemens Gruber {
955407353ecSClemens Gruber 	int err;
956407353ecSClemens Gruber 
957407353ecSClemens Gruber 	/* SGMII-to-Copper mode initialization */
958407353ecSClemens Gruber 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
959407353ecSClemens Gruber 		/* Select page 18 */
9606427bb2dSAndrew Lunn 		err = marvell_set_page(phydev, 18);
961407353ecSClemens Gruber 		if (err < 0)
962407353ecSClemens Gruber 			return err;
963407353ecSClemens Gruber 
964407353ecSClemens Gruber 		/* In reg 20, write MODE[2:0] = 0x1 (SGMII to Copper) */
965fea23fb5SRussell King 		err = phy_modify(phydev, MII_88E1510_GEN_CTRL_REG_1,
966f102852fSRussell King 				 MII_88E1510_GEN_CTRL_REG_1_MODE_MASK,
967fea23fb5SRussell King 				 MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII);
968407353ecSClemens Gruber 		if (err < 0)
969407353ecSClemens Gruber 			return err;
970407353ecSClemens Gruber 
971407353ecSClemens Gruber 		/* PHY reset is necessary after changing MODE[2:0] */
972fea23fb5SRussell King 		err = phy_modify(phydev, MII_88E1510_GEN_CTRL_REG_1, 0,
973fea23fb5SRussell King 				 MII_88E1510_GEN_CTRL_REG_1_RESET);
974407353ecSClemens Gruber 		if (err < 0)
975407353ecSClemens Gruber 			return err;
976407353ecSClemens Gruber 
977407353ecSClemens Gruber 		/* Reset page selection */
97852295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
979407353ecSClemens Gruber 		if (err < 0)
980407353ecSClemens Gruber 			return err;
981407353ecSClemens Gruber 	}
982407353ecSClemens Gruber 
983dd9a122aSEsben Haabendal 	return m88e1318_config_init(phydev);
984407353ecSClemens Gruber }
985407353ecSClemens Gruber 
986605f196eSRon Madrid static int m88e1118_config_aneg(struct phy_device *phydev)
987605f196eSRon Madrid {
988605f196eSRon Madrid 	int err;
989605f196eSRon Madrid 
99034386344SAndrew Lunn 	err = genphy_soft_reset(phydev);
991605f196eSRon Madrid 	if (err < 0)
992605f196eSRon Madrid 		return err;
993605f196eSRon Madrid 
994fecd5e91SAndrew Lunn 	err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
995605f196eSRon Madrid 	if (err < 0)
996605f196eSRon Madrid 		return err;
997605f196eSRon Madrid 
998605f196eSRon Madrid 	err = genphy_config_aneg(phydev);
999605f196eSRon Madrid 	return 0;
1000605f196eSRon Madrid }
1001605f196eSRon Madrid 
1002605f196eSRon Madrid static int m88e1118_config_init(struct phy_device *phydev)
1003605f196eSRon Madrid {
1004605f196eSRon Madrid 	int err;
1005605f196eSRon Madrid 
1006605f196eSRon Madrid 	/* Change address */
100752295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_MSCR_PAGE);
1008605f196eSRon Madrid 	if (err < 0)
1009605f196eSRon Madrid 		return err;
1010605f196eSRon Madrid 
1011605f196eSRon Madrid 	/* Enable 1000 Mbit */
1012605f196eSRon Madrid 	err = phy_write(phydev, 0x15, 0x1070);
1013605f196eSRon Madrid 	if (err < 0)
1014605f196eSRon Madrid 		return err;
1015605f196eSRon Madrid 
1016605f196eSRon Madrid 	/* Change address */
101752295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_LED_PAGE);
1018605f196eSRon Madrid 	if (err < 0)
1019605f196eSRon Madrid 		return err;
1020605f196eSRon Madrid 
1021605f196eSRon Madrid 	/* Adjust LED Control */
10222f495c39SBenjamin Herrenschmidt 	if (phydev->dev_flags & MARVELL_PHY_M1118_DNS323_LEDS)
10232f495c39SBenjamin Herrenschmidt 		err = phy_write(phydev, 0x10, 0x1100);
10242f495c39SBenjamin Herrenschmidt 	else
1025605f196eSRon Madrid 		err = phy_write(phydev, 0x10, 0x021e);
1026605f196eSRon Madrid 	if (err < 0)
1027605f196eSRon Madrid 		return err;
1028605f196eSRon Madrid 
1029cf41a51dSDavid Daney 	err = marvell_of_reg_init(phydev);
1030cf41a51dSDavid Daney 	if (err < 0)
1031cf41a51dSDavid Daney 		return err;
1032cf41a51dSDavid Daney 
1033605f196eSRon Madrid 	/* Reset address */
103452295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
1035605f196eSRon Madrid 	if (err < 0)
1036605f196eSRon Madrid 		return err;
1037605f196eSRon Madrid 
103834386344SAndrew Lunn 	return genphy_soft_reset(phydev);
1039605f196eSRon Madrid }
1040605f196eSRon Madrid 
104190600732SDavid Daney static int m88e1149_config_init(struct phy_device *phydev)
104290600732SDavid Daney {
104390600732SDavid Daney 	int err;
104490600732SDavid Daney 
104590600732SDavid Daney 	/* Change address */
104652295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_MSCR_PAGE);
104790600732SDavid Daney 	if (err < 0)
104890600732SDavid Daney 		return err;
104990600732SDavid Daney 
105090600732SDavid Daney 	/* Enable 1000 Mbit */
105190600732SDavid Daney 	err = phy_write(phydev, 0x15, 0x1048);
105290600732SDavid Daney 	if (err < 0)
105390600732SDavid Daney 		return err;
105490600732SDavid Daney 
1055cf41a51dSDavid Daney 	err = marvell_of_reg_init(phydev);
1056cf41a51dSDavid Daney 	if (err < 0)
1057cf41a51dSDavid Daney 		return err;
1058cf41a51dSDavid Daney 
105990600732SDavid Daney 	/* Reset address */
106052295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
106190600732SDavid Daney 	if (err < 0)
106290600732SDavid Daney 		return err;
106390600732SDavid Daney 
106434386344SAndrew Lunn 	return genphy_soft_reset(phydev);
106590600732SDavid Daney }
106690600732SDavid Daney 
1067e1dde8dcSAndrew Lunn static int m88e1145_config_init_rgmii(struct phy_device *phydev)
106876884679SAndy Fleming {
106976884679SAndy Fleming 	int err;
1070e69d9ed4SAndrew Lunn 
107161111598SAndrew Lunn 	err = m88e1111_config_init_rgmii_delays(phydev);
107276884679SAndy Fleming 	if (err < 0)
107376884679SAndy Fleming 		return err;
107476884679SAndy Fleming 
10752f495c39SBenjamin Herrenschmidt 	if (phydev->dev_flags & MARVELL_PHY_M1145_FLAGS_RESISTANCE) {
107676884679SAndy Fleming 		err = phy_write(phydev, 0x1d, 0x0012);
107776884679SAndy Fleming 		if (err < 0)
107876884679SAndy Fleming 			return err;
107976884679SAndy Fleming 
1080f102852fSRussell King 		err = phy_modify(phydev, 0x1e, 0x0fc0,
1081fea23fb5SRussell King 				 2 << 9 | /* 36 ohm */
1082fea23fb5SRussell King 				 2 << 6); /* 39 ohm */
108376884679SAndy Fleming 		if (err < 0)
108476884679SAndy Fleming 			return err;
108576884679SAndy Fleming 
108676884679SAndy Fleming 		err = phy_write(phydev, 0x1d, 0x3);
108776884679SAndy Fleming 		if (err < 0)
108876884679SAndy Fleming 			return err;
108976884679SAndy Fleming 
109076884679SAndy Fleming 		err = phy_write(phydev, 0x1e, 0x8000);
1091e1dde8dcSAndrew Lunn 	}
109276884679SAndy Fleming 	return err;
109376884679SAndy Fleming }
109476884679SAndy Fleming 
1095e1dde8dcSAndrew Lunn static int m88e1145_config_init_sgmii(struct phy_device *phydev)
1096e1dde8dcSAndrew Lunn {
1097865b813aSAndrew Lunn 	return m88e1111_config_init_hwcfg_mode(
1098865b813aSAndrew Lunn 		phydev, MII_M1111_HWCFG_MODE_SGMII_NO_CLK,
1099865b813aSAndrew Lunn 		MII_M1111_HWCFG_FIBER_COPPER_AUTO);
1100e1dde8dcSAndrew Lunn }
1101e1dde8dcSAndrew Lunn 
1102e1dde8dcSAndrew Lunn static int m88e1145_config_init(struct phy_device *phydev)
1103e1dde8dcSAndrew Lunn {
1104e1dde8dcSAndrew Lunn 	int err;
1105e1dde8dcSAndrew Lunn 
1106e1dde8dcSAndrew Lunn 	/* Take care of errata E0 & E1 */
1107e1dde8dcSAndrew Lunn 	err = phy_write(phydev, 0x1d, 0x001b);
1108e1dde8dcSAndrew Lunn 	if (err < 0)
1109e1dde8dcSAndrew Lunn 		return err;
1110e1dde8dcSAndrew Lunn 
1111e1dde8dcSAndrew Lunn 	err = phy_write(phydev, 0x1e, 0x418f);
1112e1dde8dcSAndrew Lunn 	if (err < 0)
1113e1dde8dcSAndrew Lunn 		return err;
1114e1dde8dcSAndrew Lunn 
1115e1dde8dcSAndrew Lunn 	err = phy_write(phydev, 0x1d, 0x0016);
1116e1dde8dcSAndrew Lunn 	if (err < 0)
1117e1dde8dcSAndrew Lunn 		return err;
1118e1dde8dcSAndrew Lunn 
1119e1dde8dcSAndrew Lunn 	err = phy_write(phydev, 0x1e, 0xa2da);
1120e1dde8dcSAndrew Lunn 	if (err < 0)
1121e1dde8dcSAndrew Lunn 		return err;
1122e1dde8dcSAndrew Lunn 
1123e1dde8dcSAndrew Lunn 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
1124e1dde8dcSAndrew Lunn 		err = m88e1145_config_init_rgmii(phydev);
1125e1dde8dcSAndrew Lunn 		if (err < 0)
1126e1dde8dcSAndrew Lunn 			return err;
1127e1dde8dcSAndrew Lunn 	}
1128e1dde8dcSAndrew Lunn 
1129e1dde8dcSAndrew Lunn 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
1130e1dde8dcSAndrew Lunn 		err = m88e1145_config_init_sgmii(phydev);
1131b0224175SViet Nga Dao 		if (err < 0)
1132b0224175SViet Nga Dao 			return err;
1133b0224175SViet Nga Dao 	}
1134b0224175SViet Nga Dao 
1135cf41a51dSDavid Daney 	err = marvell_of_reg_init(phydev);
1136cf41a51dSDavid Daney 	if (err < 0)
1137cf41a51dSDavid Daney 		return err;
1138cf41a51dSDavid Daney 
113976884679SAndy Fleming 	return 0;
114076884679SAndy Fleming }
114100db8189SAndy Fleming 
114269f42be8SHeiner Kallweit static int m88e1540_get_fld(struct phy_device *phydev, u8 *msecs)
114369f42be8SHeiner Kallweit {
114469f42be8SHeiner Kallweit 	int val;
114569f42be8SHeiner Kallweit 
114669f42be8SHeiner Kallweit 	val = phy_read(phydev, MII_88E1540_COPPER_CTRL3);
114769f42be8SHeiner Kallweit 	if (val < 0)
114869f42be8SHeiner Kallweit 		return val;
114969f42be8SHeiner Kallweit 
115069f42be8SHeiner Kallweit 	if (!(val & MII_88E1540_COPPER_CTRL3_FAST_LINK_DOWN)) {
115169f42be8SHeiner Kallweit 		*msecs = ETHTOOL_PHY_FAST_LINK_DOWN_OFF;
115269f42be8SHeiner Kallweit 		return 0;
115369f42be8SHeiner Kallweit 	}
115469f42be8SHeiner Kallweit 
115569f42be8SHeiner Kallweit 	val = FIELD_GET(MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_MASK, val);
115669f42be8SHeiner Kallweit 
115769f42be8SHeiner Kallweit 	switch (val) {
115869f42be8SHeiner Kallweit 	case MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_00MS:
115969f42be8SHeiner Kallweit 		*msecs = 0;
116069f42be8SHeiner Kallweit 		break;
116169f42be8SHeiner Kallweit 	case MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_10MS:
116269f42be8SHeiner Kallweit 		*msecs = 10;
116369f42be8SHeiner Kallweit 		break;
116469f42be8SHeiner Kallweit 	case MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_20MS:
116569f42be8SHeiner Kallweit 		*msecs = 20;
116669f42be8SHeiner Kallweit 		break;
116769f42be8SHeiner Kallweit 	case MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_40MS:
116869f42be8SHeiner Kallweit 		*msecs = 40;
116969f42be8SHeiner Kallweit 		break;
117069f42be8SHeiner Kallweit 	default:
117169f42be8SHeiner Kallweit 		return -EINVAL;
117269f42be8SHeiner Kallweit 	}
117369f42be8SHeiner Kallweit 
117469f42be8SHeiner Kallweit 	return 0;
117569f42be8SHeiner Kallweit }
117669f42be8SHeiner Kallweit 
117769f42be8SHeiner Kallweit static int m88e1540_set_fld(struct phy_device *phydev, const u8 *msecs)
117869f42be8SHeiner Kallweit {
117969f42be8SHeiner Kallweit 	struct ethtool_eee eee;
118069f42be8SHeiner Kallweit 	int val, ret;
118169f42be8SHeiner Kallweit 
118269f42be8SHeiner Kallweit 	if (*msecs == ETHTOOL_PHY_FAST_LINK_DOWN_OFF)
118369f42be8SHeiner Kallweit 		return phy_clear_bits(phydev, MII_88E1540_COPPER_CTRL3,
118469f42be8SHeiner Kallweit 				      MII_88E1540_COPPER_CTRL3_FAST_LINK_DOWN);
118569f42be8SHeiner Kallweit 
118669f42be8SHeiner Kallweit 	/* According to the Marvell data sheet EEE must be disabled for
118769f42be8SHeiner Kallweit 	 * Fast Link Down detection to work properly
118869f42be8SHeiner Kallweit 	 */
118969f42be8SHeiner Kallweit 	ret = phy_ethtool_get_eee(phydev, &eee);
119069f42be8SHeiner Kallweit 	if (!ret && eee.eee_enabled) {
119169f42be8SHeiner Kallweit 		phydev_warn(phydev, "Fast Link Down detection requires EEE to be disabled!\n");
119269f42be8SHeiner Kallweit 		return -EBUSY;
119369f42be8SHeiner Kallweit 	}
119469f42be8SHeiner Kallweit 
119569f42be8SHeiner Kallweit 	if (*msecs <= 5)
119669f42be8SHeiner Kallweit 		val = MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_00MS;
119769f42be8SHeiner Kallweit 	else if (*msecs <= 15)
119869f42be8SHeiner Kallweit 		val = MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_10MS;
119969f42be8SHeiner Kallweit 	else if (*msecs <= 30)
120069f42be8SHeiner Kallweit 		val = MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_20MS;
120169f42be8SHeiner Kallweit 	else
120269f42be8SHeiner Kallweit 		val = MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_40MS;
120369f42be8SHeiner Kallweit 
120469f42be8SHeiner Kallweit 	val = FIELD_PREP(MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_MASK, val);
120569f42be8SHeiner Kallweit 
120669f42be8SHeiner Kallweit 	ret = phy_modify(phydev, MII_88E1540_COPPER_CTRL3,
120769f42be8SHeiner Kallweit 			 MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_MASK, val);
120869f42be8SHeiner Kallweit 	if (ret)
120969f42be8SHeiner Kallweit 		return ret;
121069f42be8SHeiner Kallweit 
121169f42be8SHeiner Kallweit 	return phy_set_bits(phydev, MII_88E1540_COPPER_CTRL3,
121269f42be8SHeiner Kallweit 			    MII_88E1540_COPPER_CTRL3_FAST_LINK_DOWN);
121369f42be8SHeiner Kallweit }
121469f42be8SHeiner Kallweit 
121569f42be8SHeiner Kallweit static int m88e1540_get_tunable(struct phy_device *phydev,
121669f42be8SHeiner Kallweit 				struct ethtool_tunable *tuna, void *data)
121769f42be8SHeiner Kallweit {
121869f42be8SHeiner Kallweit 	switch (tuna->id) {
121969f42be8SHeiner Kallweit 	case ETHTOOL_PHY_FAST_LINK_DOWN:
122069f42be8SHeiner Kallweit 		return m88e1540_get_fld(phydev, data);
1221a3bdfce7SHeiner Kallweit 	case ETHTOOL_PHY_DOWNSHIFT:
1222911af5e1SHeiner Kallweit 		return m88e1011_get_downshift(phydev, data);
122369f42be8SHeiner Kallweit 	default:
122469f42be8SHeiner Kallweit 		return -EOPNOTSUPP;
122569f42be8SHeiner Kallweit 	}
122669f42be8SHeiner Kallweit }
122769f42be8SHeiner Kallweit 
122869f42be8SHeiner Kallweit static int m88e1540_set_tunable(struct phy_device *phydev,
122969f42be8SHeiner Kallweit 				struct ethtool_tunable *tuna, const void *data)
123069f42be8SHeiner Kallweit {
123169f42be8SHeiner Kallweit 	switch (tuna->id) {
123269f42be8SHeiner Kallweit 	case ETHTOOL_PHY_FAST_LINK_DOWN:
123369f42be8SHeiner Kallweit 		return m88e1540_set_fld(phydev, data);
1234a3bdfce7SHeiner Kallweit 	case ETHTOOL_PHY_DOWNSHIFT:
1235911af5e1SHeiner Kallweit 		return m88e1011_set_downshift(phydev, *(const u8 *)data);
123669f42be8SHeiner Kallweit 	default:
123769f42be8SHeiner Kallweit 		return -EOPNOTSUPP;
123869f42be8SHeiner Kallweit 	}
123969f42be8SHeiner Kallweit }
124069f42be8SHeiner Kallweit 
12418cbcdc1aSAndrew Lunn /* The VOD can be out of specification on link up. Poke an
12428cbcdc1aSAndrew Lunn  * undocumented register, in an undocumented page, with a magic value
12438cbcdc1aSAndrew Lunn  * to fix this.
12448cbcdc1aSAndrew Lunn  */
12458cbcdc1aSAndrew Lunn static int m88e6390_errata(struct phy_device *phydev)
12468cbcdc1aSAndrew Lunn {
12478cbcdc1aSAndrew Lunn 	int err;
12488cbcdc1aSAndrew Lunn 
12498cbcdc1aSAndrew Lunn 	err = phy_write(phydev, MII_BMCR,
12508cbcdc1aSAndrew Lunn 			BMCR_ANENABLE | BMCR_SPEED1000 | BMCR_FULLDPLX);
12518cbcdc1aSAndrew Lunn 	if (err)
12528cbcdc1aSAndrew Lunn 		return err;
12538cbcdc1aSAndrew Lunn 
12548cbcdc1aSAndrew Lunn 	usleep_range(300, 400);
12558cbcdc1aSAndrew Lunn 
12568cbcdc1aSAndrew Lunn 	err = phy_write_paged(phydev, 0xf8, 0x08, 0x36);
12578cbcdc1aSAndrew Lunn 	if (err)
12588cbcdc1aSAndrew Lunn 		return err;
12598cbcdc1aSAndrew Lunn 
12608cbcdc1aSAndrew Lunn 	return genphy_soft_reset(phydev);
12618cbcdc1aSAndrew Lunn }
12628cbcdc1aSAndrew Lunn 
12638cbcdc1aSAndrew Lunn static int m88e6390_config_aneg(struct phy_device *phydev)
12648cbcdc1aSAndrew Lunn {
12658cbcdc1aSAndrew Lunn 	int err;
12668cbcdc1aSAndrew Lunn 
12678cbcdc1aSAndrew Lunn 	err = m88e6390_errata(phydev);
12688cbcdc1aSAndrew Lunn 	if (err)
12698cbcdc1aSAndrew Lunn 		return err;
12708cbcdc1aSAndrew Lunn 
12718cbcdc1aSAndrew Lunn 	return m88e1510_config_aneg(phydev);
12728cbcdc1aSAndrew Lunn }
12738cbcdc1aSAndrew Lunn 
12746cfb3bccSCharles-Antoine Couret /**
1275ab9cb729SAndrew Lunn  * fiber_lpa_mod_linkmode_lpa_t
1276c0ec3c27SAndrew Lunn  * @advertising: the linkmode advertisement settings
12776cfb3bccSCharles-Antoine Couret  * @lpa: value of the MII_LPA register for fiber link
1278be937f1fSAlexandr Smirnov  *
1279ab9cb729SAndrew Lunn  * A small helper function that translates MII_LPA bits to linkmode LP
1280ab9cb729SAndrew Lunn  * advertisement settings. Other bits in advertising are left
1281ab9cb729SAndrew Lunn  * unchanged.
12826cfb3bccSCharles-Antoine Couret  */
1283ab9cb729SAndrew Lunn static void fiber_lpa_mod_linkmode_lpa_t(unsigned long *advertising, u32 lpa)
12846cfb3bccSCharles-Antoine Couret {
1285ab9cb729SAndrew Lunn 	linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
128620ecf424SRussell King 			 advertising, lpa & LPA_1000XHALF);
1287ab9cb729SAndrew Lunn 
1288ab9cb729SAndrew Lunn 	linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
128920ecf424SRussell King 			 advertising, lpa & LPA_1000XFULL);
12906cfb3bccSCharles-Antoine Couret }
12916cfb3bccSCharles-Antoine Couret 
1292e1dde8dcSAndrew Lunn static int marvell_read_status_page_an(struct phy_device *phydev,
1293d2004e27SRussell King 				       int fiber, int status)
1294be937f1fSAlexandr Smirnov {
1295be937f1fSAlexandr Smirnov 	int lpa;
1296fcf1f59aSRussell King 	int err;
1297be937f1fSAlexandr Smirnov 
12983b72f84fSClemens Gruber 	if (!(status & MII_M1011_PHY_STATUS_RESOLVED)) {
12993b72f84fSClemens Gruber 		phydev->link = 0;
13003b72f84fSClemens Gruber 		return 0;
13013b72f84fSClemens Gruber 	}
13023b72f84fSClemens Gruber 
13033b72f84fSClemens Gruber 	if (status & MII_M1011_PHY_STATUS_FULLDUPLEX)
13043b72f84fSClemens Gruber 		phydev->duplex = DUPLEX_FULL;
13053b72f84fSClemens Gruber 	else
13063b72f84fSClemens Gruber 		phydev->duplex = DUPLEX_HALF;
13073b72f84fSClemens Gruber 
13083b72f84fSClemens Gruber 	switch (status & MII_M1011_PHY_STATUS_SPD_MASK) {
13093b72f84fSClemens Gruber 	case MII_M1011_PHY_STATUS_1000:
13103b72f84fSClemens Gruber 		phydev->speed = SPEED_1000;
13113b72f84fSClemens Gruber 		break;
13123b72f84fSClemens Gruber 
13133b72f84fSClemens Gruber 	case MII_M1011_PHY_STATUS_100:
13143b72f84fSClemens Gruber 		phydev->speed = SPEED_100;
13153b72f84fSClemens Gruber 		break;
13163b72f84fSClemens Gruber 
13173b72f84fSClemens Gruber 	default:
13183b72f84fSClemens Gruber 		phydev->speed = SPEED_10;
13193b72f84fSClemens Gruber 		break;
13203b72f84fSClemens Gruber 	}
13213b72f84fSClemens Gruber 
1322fcf1f59aSRussell King 	if (!fiber) {
1323fcf1f59aSRussell King 		err = genphy_read_lpa(phydev);
1324fcf1f59aSRussell King 		if (err < 0)
1325fcf1f59aSRussell King 			return err;
1326fcf1f59aSRussell King 
1327fcf1f59aSRussell King 		phy_resolve_aneg_pause(phydev);
1328fcf1f59aSRussell King 	} else {
1329be937f1fSAlexandr Smirnov 		lpa = phy_read(phydev, MII_LPA);
1330be937f1fSAlexandr Smirnov 		if (lpa < 0)
1331be937f1fSAlexandr Smirnov 			return lpa;
1332be937f1fSAlexandr Smirnov 
13336cfb3bccSCharles-Antoine Couret 		/* The fiber link is only 1000M capable */
1334ab9cb729SAndrew Lunn 		fiber_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa);
13356cfb3bccSCharles-Antoine Couret 
13366cfb3bccSCharles-Antoine Couret 		if (phydev->duplex == DUPLEX_FULL) {
13376cfb3bccSCharles-Antoine Couret 			if (!(lpa & LPA_PAUSE_FIBER)) {
13386cfb3bccSCharles-Antoine Couret 				phydev->pause = 0;
13396cfb3bccSCharles-Antoine Couret 				phydev->asym_pause = 0;
13406cfb3bccSCharles-Antoine Couret 			} else if ((lpa & LPA_PAUSE_ASYM_FIBER)) {
13416cfb3bccSCharles-Antoine Couret 				phydev->pause = 1;
13426cfb3bccSCharles-Antoine Couret 				phydev->asym_pause = 1;
13436cfb3bccSCharles-Antoine Couret 			} else {
13446cfb3bccSCharles-Antoine Couret 				phydev->pause = 1;
13456cfb3bccSCharles-Antoine Couret 				phydev->asym_pause = 0;
13466cfb3bccSCharles-Antoine Couret 			}
13476cfb3bccSCharles-Antoine Couret 		}
13486cfb3bccSCharles-Antoine Couret 	}
1349fcf1f59aSRussell King 
1350e1dde8dcSAndrew Lunn 	return 0;
1351e1dde8dcSAndrew Lunn }
1352e1dde8dcSAndrew Lunn 
1353e1dde8dcSAndrew Lunn /* marvell_read_status_page
1354e1dde8dcSAndrew Lunn  *
1355e1dde8dcSAndrew Lunn  * Description:
1356e1dde8dcSAndrew Lunn  *   Check the link, then figure out the current state
1357e1dde8dcSAndrew Lunn  *   by comparing what we advertise with what the link partner
1358e1dde8dcSAndrew Lunn  *   advertises.  Start by checking the gigabit possibilities,
1359e1dde8dcSAndrew Lunn  *   then move on to 10/100.
1360e1dde8dcSAndrew Lunn  */
1361e1dde8dcSAndrew Lunn static int marvell_read_status_page(struct phy_device *phydev, int page)
1362e1dde8dcSAndrew Lunn {
1363d2004e27SRussell King 	int status;
1364e1dde8dcSAndrew Lunn 	int fiber;
1365e1dde8dcSAndrew Lunn 	int err;
1366e1dde8dcSAndrew Lunn 
1367d2004e27SRussell King 	status = phy_read(phydev, MII_M1011_PHY_STATUS);
1368d2004e27SRussell King 	if (status < 0)
1369d2004e27SRussell King 		return status;
1370d2004e27SRussell King 
1371d2004e27SRussell King 	/* Use the generic register for copper link status,
1372d2004e27SRussell King 	 * and the PHY status register for fiber link status.
1373e1dde8dcSAndrew Lunn 	 */
1374d2004e27SRussell King 	if (page == MII_MARVELL_FIBER_PAGE) {
1375d2004e27SRussell King 		phydev->link = !!(status & MII_M1011_PHY_STATUS_LINK);
1376d2004e27SRussell King 	} else {
1377d2004e27SRussell King 		err = genphy_update_link(phydev);
1378d2004e27SRussell King 		if (err)
1379d2004e27SRussell King 			return err;
1380d2004e27SRussell King 	}
1381d2004e27SRussell King 
138252295666SAndrew Lunn 	if (page == MII_MARVELL_FIBER_PAGE)
1383e1dde8dcSAndrew Lunn 		fiber = 1;
1384e1dde8dcSAndrew Lunn 	else
1385e1dde8dcSAndrew Lunn 		fiber = 0;
1386e1dde8dcSAndrew Lunn 
138798f92831SRussell King 	linkmode_zero(phydev->lp_advertising);
138898f92831SRussell King 	phydev->pause = 0;
138998f92831SRussell King 	phydev->asym_pause = 0;
1390b82cf17fSRussell King 	phydev->speed = SPEED_UNKNOWN;
1391b82cf17fSRussell King 	phydev->duplex = DUPLEX_UNKNOWN;
139298f92831SRussell King 
1393e1dde8dcSAndrew Lunn 	if (phydev->autoneg == AUTONEG_ENABLE)
1394d2004e27SRussell King 		err = marvell_read_status_page_an(phydev, fiber, status);
1395e1dde8dcSAndrew Lunn 	else
139698f92831SRussell King 		err = genphy_read_status_fixed(phydev);
1397e1dde8dcSAndrew Lunn 
1398e1dde8dcSAndrew Lunn 	return err;
1399e1dde8dcSAndrew Lunn }
1400e1dde8dcSAndrew Lunn 
14016cfb3bccSCharles-Antoine Couret /* marvell_read_status
14026cfb3bccSCharles-Antoine Couret  *
14036cfb3bccSCharles-Antoine Couret  * Some Marvell's phys have two modes: fiber and copper.
14046cfb3bccSCharles-Antoine Couret  * Both need status checked.
14056cfb3bccSCharles-Antoine Couret  * Description:
14066cfb3bccSCharles-Antoine Couret  *   First, check the fiber link and status.
14076cfb3bccSCharles-Antoine Couret  *   If the fiber link is down, check the copper link and status which
14086cfb3bccSCharles-Antoine Couret  *   will be the default value if both link are down.
14096cfb3bccSCharles-Antoine Couret  */
14106cfb3bccSCharles-Antoine Couret static int marvell_read_status(struct phy_device *phydev)
14116cfb3bccSCharles-Antoine Couret {
14126cfb3bccSCharles-Antoine Couret 	int err;
14136cfb3bccSCharles-Antoine Couret 
14146cfb3bccSCharles-Antoine Couret 	/* Check the fiber mode first */
14153c1bcc86SAndrew Lunn 	if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
14163c1bcc86SAndrew Lunn 			      phydev->supported) &&
1417a13c0652SRussell King 	    phydev->interface != PHY_INTERFACE_MODE_SGMII) {
141852295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
14196cfb3bccSCharles-Antoine Couret 		if (err < 0)
14206cfb3bccSCharles-Antoine Couret 			goto error;
14216cfb3bccSCharles-Antoine Couret 
142252295666SAndrew Lunn 		err = marvell_read_status_page(phydev, MII_MARVELL_FIBER_PAGE);
14236cfb3bccSCharles-Antoine Couret 		if (err < 0)
14246cfb3bccSCharles-Antoine Couret 			goto error;
14256cfb3bccSCharles-Antoine Couret 
14260c3439bcSAndrew Lunn 		/* If the fiber link is up, it is the selected and
14270c3439bcSAndrew Lunn 		 * used link. In this case, we need to stay in the
14280c3439bcSAndrew Lunn 		 * fiber page. Please to be careful about that, avoid
14290c3439bcSAndrew Lunn 		 * to restore Copper page in other functions which
14300c3439bcSAndrew Lunn 		 * could break the behaviour for some fiber phy like
14310c3439bcSAndrew Lunn 		 * 88E1512.
14320c3439bcSAndrew Lunn 		 */
14336cfb3bccSCharles-Antoine Couret 		if (phydev->link)
14346cfb3bccSCharles-Antoine Couret 			return 0;
14356cfb3bccSCharles-Antoine Couret 
14366cfb3bccSCharles-Antoine Couret 		/* If fiber link is down, check and save copper mode state */
143752295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
14386cfb3bccSCharles-Antoine Couret 		if (err < 0)
14396cfb3bccSCharles-Antoine Couret 			goto error;
14406cfb3bccSCharles-Antoine Couret 	}
14416cfb3bccSCharles-Antoine Couret 
144252295666SAndrew Lunn 	return marvell_read_status_page(phydev, MII_MARVELL_COPPER_PAGE);
14436cfb3bccSCharles-Antoine Couret 
14446cfb3bccSCharles-Antoine Couret error:
144552295666SAndrew Lunn 	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
14466cfb3bccSCharles-Antoine Couret 	return err;
14476cfb3bccSCharles-Antoine Couret }
14483758be3dSCharles-Antoine Couret 
14493758be3dSCharles-Antoine Couret /* marvell_suspend
14503758be3dSCharles-Antoine Couret  *
14513758be3dSCharles-Antoine Couret  * Some Marvell's phys have two modes: fiber and copper.
14523758be3dSCharles-Antoine Couret  * Both need to be suspended
14533758be3dSCharles-Antoine Couret  */
14543758be3dSCharles-Antoine Couret static int marvell_suspend(struct phy_device *phydev)
14553758be3dSCharles-Antoine Couret {
14563758be3dSCharles-Antoine Couret 	int err;
14573758be3dSCharles-Antoine Couret 
14583758be3dSCharles-Antoine Couret 	/* Suspend the fiber mode first */
14593c1bcc86SAndrew Lunn 	if (!linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
14603c1bcc86SAndrew Lunn 			       phydev->supported)) {
146152295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
14623758be3dSCharles-Antoine Couret 		if (err < 0)
14633758be3dSCharles-Antoine Couret 			goto error;
14643758be3dSCharles-Antoine Couret 
14653758be3dSCharles-Antoine Couret 		/* With the page set, use the generic suspend */
14663758be3dSCharles-Antoine Couret 		err = genphy_suspend(phydev);
14673758be3dSCharles-Antoine Couret 		if (err < 0)
14683758be3dSCharles-Antoine Couret 			goto error;
14693758be3dSCharles-Antoine Couret 
14703758be3dSCharles-Antoine Couret 		/* Then, the copper link */
147152295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
14723758be3dSCharles-Antoine Couret 		if (err < 0)
14733758be3dSCharles-Antoine Couret 			goto error;
14743758be3dSCharles-Antoine Couret 	}
14753758be3dSCharles-Antoine Couret 
14763758be3dSCharles-Antoine Couret 	/* With the page set, use the generic suspend */
14773758be3dSCharles-Antoine Couret 	return genphy_suspend(phydev);
14783758be3dSCharles-Antoine Couret 
14793758be3dSCharles-Antoine Couret error:
148052295666SAndrew Lunn 	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
14813758be3dSCharles-Antoine Couret 	return err;
14823758be3dSCharles-Antoine Couret }
14833758be3dSCharles-Antoine Couret 
14843758be3dSCharles-Antoine Couret /* marvell_resume
14853758be3dSCharles-Antoine Couret  *
14863758be3dSCharles-Antoine Couret  * Some Marvell's phys have two modes: fiber and copper.
14873758be3dSCharles-Antoine Couret  * Both need to be resumed
14883758be3dSCharles-Antoine Couret  */
14893758be3dSCharles-Antoine Couret static int marvell_resume(struct phy_device *phydev)
14903758be3dSCharles-Antoine Couret {
14913758be3dSCharles-Antoine Couret 	int err;
14923758be3dSCharles-Antoine Couret 
14933758be3dSCharles-Antoine Couret 	/* Resume the fiber mode first */
14943c1bcc86SAndrew Lunn 	if (!linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
14953c1bcc86SAndrew Lunn 			       phydev->supported)) {
149652295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
14973758be3dSCharles-Antoine Couret 		if (err < 0)
14983758be3dSCharles-Antoine Couret 			goto error;
14993758be3dSCharles-Antoine Couret 
15003758be3dSCharles-Antoine Couret 		/* With the page set, use the generic resume */
15013758be3dSCharles-Antoine Couret 		err = genphy_resume(phydev);
15023758be3dSCharles-Antoine Couret 		if (err < 0)
15033758be3dSCharles-Antoine Couret 			goto error;
15043758be3dSCharles-Antoine Couret 
15053758be3dSCharles-Antoine Couret 		/* Then, the copper link */
150652295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
15073758be3dSCharles-Antoine Couret 		if (err < 0)
15083758be3dSCharles-Antoine Couret 			goto error;
15093758be3dSCharles-Antoine Couret 	}
15103758be3dSCharles-Antoine Couret 
15113758be3dSCharles-Antoine Couret 	/* With the page set, use the generic resume */
15123758be3dSCharles-Antoine Couret 	return genphy_resume(phydev);
15133758be3dSCharles-Antoine Couret 
15143758be3dSCharles-Antoine Couret error:
151552295666SAndrew Lunn 	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
15163758be3dSCharles-Antoine Couret 	return err;
15173758be3dSCharles-Antoine Couret }
15183758be3dSCharles-Antoine Couret 
15196b358aedSSebastian Hesselbarth static int marvell_aneg_done(struct phy_device *phydev)
15206b358aedSSebastian Hesselbarth {
15216b358aedSSebastian Hesselbarth 	int retval = phy_read(phydev, MII_M1011_PHY_STATUS);
1522e69d9ed4SAndrew Lunn 
15236b358aedSSebastian Hesselbarth 	return (retval < 0) ? retval : (retval & MII_M1011_PHY_STATUS_RESOLVED);
15246b358aedSSebastian Hesselbarth }
15256b358aedSSebastian Hesselbarth 
1526dcd07be3SAnatolij Gustschin static int m88e1121_did_interrupt(struct phy_device *phydev)
1527dcd07be3SAnatolij Gustschin {
1528dcd07be3SAnatolij Gustschin 	int imask;
1529dcd07be3SAnatolij Gustschin 
1530dcd07be3SAnatolij Gustschin 	imask = phy_read(phydev, MII_M1011_IEVENT);
1531dcd07be3SAnatolij Gustschin 
1532dcd07be3SAnatolij Gustschin 	if (imask & MII_M1011_IMASK_INIT)
1533dcd07be3SAnatolij Gustschin 		return 1;
1534dcd07be3SAnatolij Gustschin 
1535dcd07be3SAnatolij Gustschin 	return 0;
1536dcd07be3SAnatolij Gustschin }
1537dcd07be3SAnatolij Gustschin 
153823beb38fSAndrew Lunn static void m88e1318_get_wol(struct phy_device *phydev,
153923beb38fSAndrew Lunn 			     struct ethtool_wolinfo *wol)
15403871c387SMichael Stapelberg {
1541424ca4c5SRussell King 	int oldpage, ret = 0;
1542424ca4c5SRussell King 
15433871c387SMichael Stapelberg 	wol->supported = WAKE_MAGIC;
15443871c387SMichael Stapelberg 	wol->wolopts = 0;
15453871c387SMichael Stapelberg 
1546424ca4c5SRussell King 	oldpage = phy_select_page(phydev, MII_MARVELL_WOL_PAGE);
1547424ca4c5SRussell King 	if (oldpage < 0)
1548424ca4c5SRussell King 		goto error;
15493871c387SMichael Stapelberg 
1550424ca4c5SRussell King 	ret = __phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL);
1551424ca4c5SRussell King 	if (ret & MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE)
15523871c387SMichael Stapelberg 		wol->wolopts |= WAKE_MAGIC;
15533871c387SMichael Stapelberg 
1554424ca4c5SRussell King error:
1555424ca4c5SRussell King 	phy_restore_page(phydev, oldpage, ret);
15563871c387SMichael Stapelberg }
15573871c387SMichael Stapelberg 
155823beb38fSAndrew Lunn static int m88e1318_set_wol(struct phy_device *phydev,
155923beb38fSAndrew Lunn 			    struct ethtool_wolinfo *wol)
15603871c387SMichael Stapelberg {
1561424ca4c5SRussell King 	int err = 0, oldpage;
15623871c387SMichael Stapelberg 
1563424ca4c5SRussell King 	oldpage = phy_save_page(phydev);
1564424ca4c5SRussell King 	if (oldpage < 0)
1565424ca4c5SRussell King 		goto error;
15663871c387SMichael Stapelberg 
15673871c387SMichael Stapelberg 	if (wol->wolopts & WAKE_MAGIC) {
15683871c387SMichael Stapelberg 		/* Explicitly switch to page 0x00, just to be sure */
1569424ca4c5SRussell King 		err = marvell_write_page(phydev, MII_MARVELL_COPPER_PAGE);
15703871c387SMichael Stapelberg 		if (err < 0)
1571424ca4c5SRussell King 			goto error;
15723871c387SMichael Stapelberg 
1573b6a930faSJingju Hou 		/* If WOL event happened once, the LED[2] interrupt pin
1574b6a930faSJingju Hou 		 * will not be cleared unless we reading the interrupt status
1575b6a930faSJingju Hou 		 * register. If interrupts are in use, the normal interrupt
1576b6a930faSJingju Hou 		 * handling will clear the WOL event. Clear the WOL event
1577b6a930faSJingju Hou 		 * before enabling it if !phy_interrupt_is_valid()
1578b6a930faSJingju Hou 		 */
1579b6a930faSJingju Hou 		if (!phy_interrupt_is_valid(phydev))
1580e0a7328fSAndrew Lunn 			__phy_read(phydev, MII_M1011_IEVENT);
1581b6a930faSJingju Hou 
15823871c387SMichael Stapelberg 		/* Enable the WOL interrupt */
1583424ca4c5SRussell King 		err = __phy_modify(phydev, MII_88E1318S_PHY_CSIER, 0,
1584424ca4c5SRussell King 				   MII_88E1318S_PHY_CSIER_WOL_EIE);
15853871c387SMichael Stapelberg 		if (err < 0)
1586424ca4c5SRussell King 			goto error;
15873871c387SMichael Stapelberg 
1588424ca4c5SRussell King 		err = marvell_write_page(phydev, MII_MARVELL_LED_PAGE);
15893871c387SMichael Stapelberg 		if (err < 0)
1590424ca4c5SRussell King 			goto error;
15913871c387SMichael Stapelberg 
15923871c387SMichael Stapelberg 		/* Setup LED[2] as interrupt pin (active low) */
1593424ca4c5SRussell King 		err = __phy_modify(phydev, MII_88E1318S_PHY_LED_TCR,
1594f102852fSRussell King 				   MII_88E1318S_PHY_LED_TCR_FORCE_INT,
1595424ca4c5SRussell King 				   MII_88E1318S_PHY_LED_TCR_INTn_ENABLE |
1596424ca4c5SRussell King 				   MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW);
15973871c387SMichael Stapelberg 		if (err < 0)
1598424ca4c5SRussell King 			goto error;
15993871c387SMichael Stapelberg 
1600424ca4c5SRussell King 		err = marvell_write_page(phydev, MII_MARVELL_WOL_PAGE);
16013871c387SMichael Stapelberg 		if (err < 0)
1602424ca4c5SRussell King 			goto error;
16033871c387SMichael Stapelberg 
16043871c387SMichael Stapelberg 		/* Store the device address for the magic packet */
1605424ca4c5SRussell King 		err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD2,
16063871c387SMichael Stapelberg 				((phydev->attached_dev->dev_addr[5] << 8) |
16073871c387SMichael Stapelberg 				 phydev->attached_dev->dev_addr[4]));
16083871c387SMichael Stapelberg 		if (err < 0)
1609424ca4c5SRussell King 			goto error;
1610424ca4c5SRussell King 		err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD1,
16113871c387SMichael Stapelberg 				((phydev->attached_dev->dev_addr[3] << 8) |
16123871c387SMichael Stapelberg 				 phydev->attached_dev->dev_addr[2]));
16133871c387SMichael Stapelberg 		if (err < 0)
1614424ca4c5SRussell King 			goto error;
1615424ca4c5SRussell King 		err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD0,
16163871c387SMichael Stapelberg 				((phydev->attached_dev->dev_addr[1] << 8) |
16173871c387SMichael Stapelberg 				 phydev->attached_dev->dev_addr[0]));
16183871c387SMichael Stapelberg 		if (err < 0)
1619424ca4c5SRussell King 			goto error;
16203871c387SMichael Stapelberg 
16213871c387SMichael Stapelberg 		/* Clear WOL status and enable magic packet matching */
1622424ca4c5SRussell King 		err = __phy_modify(phydev, MII_88E1318S_PHY_WOL_CTRL, 0,
1623424ca4c5SRussell King 				   MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS |
1624424ca4c5SRussell King 				   MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE);
16253871c387SMichael Stapelberg 		if (err < 0)
1626424ca4c5SRussell King 			goto error;
16273871c387SMichael Stapelberg 	} else {
1628424ca4c5SRussell King 		err = marvell_write_page(phydev, MII_MARVELL_WOL_PAGE);
16293871c387SMichael Stapelberg 		if (err < 0)
1630424ca4c5SRussell King 			goto error;
16313871c387SMichael Stapelberg 
16323871c387SMichael Stapelberg 		/* Clear WOL status and disable magic packet matching */
1633424ca4c5SRussell King 		err = __phy_modify(phydev, MII_88E1318S_PHY_WOL_CTRL,
1634f102852fSRussell King 				   MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE,
1635424ca4c5SRussell King 				   MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS);
16363871c387SMichael Stapelberg 		if (err < 0)
1637424ca4c5SRussell King 			goto error;
16383871c387SMichael Stapelberg 	}
16393871c387SMichael Stapelberg 
1640424ca4c5SRussell King error:
1641424ca4c5SRussell King 	return phy_restore_page(phydev, oldpage, err);
16423871c387SMichael Stapelberg }
16433871c387SMichael Stapelberg 
1644d2fa47d9SAndrew Lunn static int marvell_get_sset_count(struct phy_device *phydev)
1645d2fa47d9SAndrew Lunn {
16463c1bcc86SAndrew Lunn 	if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
16473c1bcc86SAndrew Lunn 			      phydev->supported))
1648d2fa47d9SAndrew Lunn 		return ARRAY_SIZE(marvell_hw_stats);
16492170fef7SCharles-Antoine Couret 	else
16502170fef7SCharles-Antoine Couret 		return ARRAY_SIZE(marvell_hw_stats) - NB_FIBER_STATS;
1651d2fa47d9SAndrew Lunn }
1652d2fa47d9SAndrew Lunn 
1653d2fa47d9SAndrew Lunn static void marvell_get_strings(struct phy_device *phydev, u8 *data)
1654d2fa47d9SAndrew Lunn {
1655fdfdf867SAndrew Lunn 	int count = marvell_get_sset_count(phydev);
1656d2fa47d9SAndrew Lunn 	int i;
1657d2fa47d9SAndrew Lunn 
1658fdfdf867SAndrew Lunn 	for (i = 0; i < count; i++) {
165998409b2bSFlorian Fainelli 		strlcpy(data + i * ETH_GSTRING_LEN,
1660d2fa47d9SAndrew Lunn 			marvell_hw_stats[i].string, ETH_GSTRING_LEN);
1661d2fa47d9SAndrew Lunn 	}
1662d2fa47d9SAndrew Lunn }
1663d2fa47d9SAndrew Lunn 
1664d2fa47d9SAndrew Lunn static u64 marvell_get_stat(struct phy_device *phydev, int i)
1665d2fa47d9SAndrew Lunn {
1666d2fa47d9SAndrew Lunn 	struct marvell_hw_stat stat = marvell_hw_stats[i];
1667d2fa47d9SAndrew Lunn 	struct marvell_priv *priv = phydev->priv;
1668424ca4c5SRussell King 	int val;
1669321b4d4bSAndrew Lunn 	u64 ret;
1670d2fa47d9SAndrew Lunn 
1671424ca4c5SRussell King 	val = phy_read_paged(phydev, stat.page, stat.reg);
1672d2fa47d9SAndrew Lunn 	if (val < 0) {
16736c3442f5SJisheng Zhang 		ret = U64_MAX;
1674d2fa47d9SAndrew Lunn 	} else {
1675d2fa47d9SAndrew Lunn 		val = val & ((1 << stat.bits) - 1);
1676d2fa47d9SAndrew Lunn 		priv->stats[i] += val;
1677321b4d4bSAndrew Lunn 		ret = priv->stats[i];
1678d2fa47d9SAndrew Lunn 	}
1679d2fa47d9SAndrew Lunn 
1680321b4d4bSAndrew Lunn 	return ret;
1681d2fa47d9SAndrew Lunn }
1682d2fa47d9SAndrew Lunn 
1683d2fa47d9SAndrew Lunn static void marvell_get_stats(struct phy_device *phydev,
1684d2fa47d9SAndrew Lunn 			      struct ethtool_stats *stats, u64 *data)
1685d2fa47d9SAndrew Lunn {
1686fdfdf867SAndrew Lunn 	int count = marvell_get_sset_count(phydev);
1687d2fa47d9SAndrew Lunn 	int i;
1688d2fa47d9SAndrew Lunn 
1689fdfdf867SAndrew Lunn 	for (i = 0; i < count; i++)
1690d2fa47d9SAndrew Lunn 		data[i] = marvell_get_stat(phydev, i);
1691d2fa47d9SAndrew Lunn }
1692d2fa47d9SAndrew Lunn 
1693fc879f72SAndrew Lunn static int marvell_vct7_cable_test_start(struct phy_device *phydev)
1694fc879f72SAndrew Lunn {
1695fc879f72SAndrew Lunn 	int bmcr, bmsr, ret;
1696fc879f72SAndrew Lunn 
1697fc879f72SAndrew Lunn 	/* If auto-negotiation is enabled, but not complete, the cable
1698fc879f72SAndrew Lunn 	 * test never completes. So disable auto-neg.
1699fc879f72SAndrew Lunn 	 */
1700fc879f72SAndrew Lunn 	bmcr = phy_read(phydev, MII_BMCR);
1701fc879f72SAndrew Lunn 	if (bmcr < 0)
1702fc879f72SAndrew Lunn 		return bmcr;
1703fc879f72SAndrew Lunn 
1704fc879f72SAndrew Lunn 	bmsr = phy_read(phydev, MII_BMSR);
1705fc879f72SAndrew Lunn 
1706fc879f72SAndrew Lunn 	if (bmsr < 0)
1707fc879f72SAndrew Lunn 		return bmsr;
1708fc879f72SAndrew Lunn 
1709fc879f72SAndrew Lunn 	if (bmcr & BMCR_ANENABLE) {
1710fc879f72SAndrew Lunn 		ret =  phy_modify(phydev, MII_BMCR, BMCR_ANENABLE, 0);
1711fc879f72SAndrew Lunn 		if (ret < 0)
1712fc879f72SAndrew Lunn 			return ret;
1713fc879f72SAndrew Lunn 		ret = genphy_soft_reset(phydev);
1714fc879f72SAndrew Lunn 		if (ret < 0)
1715fc879f72SAndrew Lunn 			return ret;
1716fc879f72SAndrew Lunn 	}
1717fc879f72SAndrew Lunn 
1718fc879f72SAndrew Lunn 	/* If the link is up, allow it some time to go down */
1719fc879f72SAndrew Lunn 	if (bmsr & BMSR_LSTATUS)
1720fc879f72SAndrew Lunn 		msleep(1500);
1721fc879f72SAndrew Lunn 
1722fc879f72SAndrew Lunn 	return phy_write_paged(phydev, MII_MARVELL_VCT7_PAGE,
1723fc879f72SAndrew Lunn 			       MII_VCT7_CTRL,
1724fc879f72SAndrew Lunn 			       MII_VCT7_CTRL_RUN_NOW |
1725fc879f72SAndrew Lunn 			       MII_VCT7_CTRL_CENTIMETERS);
1726fc879f72SAndrew Lunn }
1727fc879f72SAndrew Lunn 
1728fc879f72SAndrew Lunn static int marvell_vct7_distance_to_length(int distance, bool meter)
1729fc879f72SAndrew Lunn {
1730fc879f72SAndrew Lunn 	if (meter)
1731fc879f72SAndrew Lunn 		distance *= 100;
1732fc879f72SAndrew Lunn 
1733fc879f72SAndrew Lunn 	return distance;
1734fc879f72SAndrew Lunn }
1735fc879f72SAndrew Lunn 
1736fc879f72SAndrew Lunn static bool marvell_vct7_distance_valid(int result)
1737fc879f72SAndrew Lunn {
1738fc879f72SAndrew Lunn 	switch (result) {
1739fc879f72SAndrew Lunn 	case MII_VCT7_RESULTS_OPEN:
1740fc879f72SAndrew Lunn 	case MII_VCT7_RESULTS_SAME_SHORT:
1741fc879f72SAndrew Lunn 	case MII_VCT7_RESULTS_CROSS_SHORT:
1742fc879f72SAndrew Lunn 		return true;
1743fc879f72SAndrew Lunn 	}
1744fc879f72SAndrew Lunn 	return false;
1745fc879f72SAndrew Lunn }
1746fc879f72SAndrew Lunn 
1747fc879f72SAndrew Lunn static int marvell_vct7_report_length(struct phy_device *phydev,
1748fc879f72SAndrew Lunn 				      int pair, bool meter)
1749fc879f72SAndrew Lunn {
1750fc879f72SAndrew Lunn 	int length;
1751fc879f72SAndrew Lunn 	int ret;
1752fc879f72SAndrew Lunn 
1753fc879f72SAndrew Lunn 	ret = phy_read_paged(phydev, MII_MARVELL_VCT7_PAGE,
1754fc879f72SAndrew Lunn 			     MII_VCT7_PAIR_0_DISTANCE + pair);
1755fc879f72SAndrew Lunn 	if (ret < 0)
1756fc879f72SAndrew Lunn 		return ret;
1757fc879f72SAndrew Lunn 
1758fc879f72SAndrew Lunn 	length = marvell_vct7_distance_to_length(ret, meter);
1759fc879f72SAndrew Lunn 
1760fc879f72SAndrew Lunn 	ethnl_cable_test_fault_length(phydev, pair, length);
1761fc879f72SAndrew Lunn 
1762fc879f72SAndrew Lunn 	return 0;
1763fc879f72SAndrew Lunn }
1764fc879f72SAndrew Lunn 
1765fc879f72SAndrew Lunn static int marvell_vct7_cable_test_report_trans(int result)
1766fc879f72SAndrew Lunn {
1767fc879f72SAndrew Lunn 	switch (result) {
1768fc879f72SAndrew Lunn 	case MII_VCT7_RESULTS_OK:
1769fc879f72SAndrew Lunn 		return ETHTOOL_A_CABLE_RESULT_CODE_OK;
1770fc879f72SAndrew Lunn 	case MII_VCT7_RESULTS_OPEN:
1771fc879f72SAndrew Lunn 		return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
1772fc879f72SAndrew Lunn 	case MII_VCT7_RESULTS_SAME_SHORT:
1773fc879f72SAndrew Lunn 		return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
1774fc879f72SAndrew Lunn 	case MII_VCT7_RESULTS_CROSS_SHORT:
1775fc879f72SAndrew Lunn 		return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
1776fc879f72SAndrew Lunn 	default:
1777fc879f72SAndrew Lunn 		return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
1778fc879f72SAndrew Lunn 	}
1779fc879f72SAndrew Lunn }
1780fc879f72SAndrew Lunn 
1781fc879f72SAndrew Lunn static int marvell_vct7_cable_test_report(struct phy_device *phydev)
1782fc879f72SAndrew Lunn {
1783fc879f72SAndrew Lunn 	int pair0, pair1, pair2, pair3;
1784fc879f72SAndrew Lunn 	bool meter;
1785fc879f72SAndrew Lunn 	int ret;
1786fc879f72SAndrew Lunn 
1787fc879f72SAndrew Lunn 	ret = phy_read_paged(phydev, MII_MARVELL_VCT7_PAGE,
1788fc879f72SAndrew Lunn 			     MII_VCT7_RESULTS);
1789fc879f72SAndrew Lunn 	if (ret < 0)
1790fc879f72SAndrew Lunn 		return ret;
1791fc879f72SAndrew Lunn 
1792fc879f72SAndrew Lunn 	pair3 = (ret & MII_VCT7_RESULTS_PAIR3_MASK) >>
1793fc879f72SAndrew Lunn 		MII_VCT7_RESULTS_PAIR3_SHIFT;
1794fc879f72SAndrew Lunn 	pair2 = (ret & MII_VCT7_RESULTS_PAIR2_MASK) >>
1795fc879f72SAndrew Lunn 		MII_VCT7_RESULTS_PAIR2_SHIFT;
1796fc879f72SAndrew Lunn 	pair1 = (ret & MII_VCT7_RESULTS_PAIR1_MASK) >>
1797fc879f72SAndrew Lunn 		MII_VCT7_RESULTS_PAIR1_SHIFT;
1798fc879f72SAndrew Lunn 	pair0 = (ret & MII_VCT7_RESULTS_PAIR0_MASK) >>
1799fc879f72SAndrew Lunn 		MII_VCT7_RESULTS_PAIR0_SHIFT;
1800fc879f72SAndrew Lunn 
1801fc879f72SAndrew Lunn 	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,
1802fc879f72SAndrew Lunn 				marvell_vct7_cable_test_report_trans(pair0));
1803fc879f72SAndrew Lunn 	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_B,
1804fc879f72SAndrew Lunn 				marvell_vct7_cable_test_report_trans(pair1));
1805fc879f72SAndrew Lunn 	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_C,
1806fc879f72SAndrew Lunn 				marvell_vct7_cable_test_report_trans(pair2));
1807fc879f72SAndrew Lunn 	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D,
1808fc879f72SAndrew Lunn 				marvell_vct7_cable_test_report_trans(pair3));
1809fc879f72SAndrew Lunn 
1810fc879f72SAndrew Lunn 	ret = phy_read_paged(phydev, MII_MARVELL_VCT7_PAGE, MII_VCT7_CTRL);
1811fc879f72SAndrew Lunn 	if (ret < 0)
1812fc879f72SAndrew Lunn 		return ret;
1813fc879f72SAndrew Lunn 
1814fc879f72SAndrew Lunn 	meter = ret & MII_VCT7_CTRL_METERS;
1815fc879f72SAndrew Lunn 
1816fc879f72SAndrew Lunn 	if (marvell_vct7_distance_valid(pair0))
1817fc879f72SAndrew Lunn 		marvell_vct7_report_length(phydev, 0, meter);
1818fc879f72SAndrew Lunn 	if (marvell_vct7_distance_valid(pair1))
1819fc879f72SAndrew Lunn 		marvell_vct7_report_length(phydev, 1, meter);
1820fc879f72SAndrew Lunn 	if (marvell_vct7_distance_valid(pair2))
1821fc879f72SAndrew Lunn 		marvell_vct7_report_length(phydev, 2, meter);
1822fc879f72SAndrew Lunn 	if (marvell_vct7_distance_valid(pair3))
1823fc879f72SAndrew Lunn 		marvell_vct7_report_length(phydev, 3, meter);
1824fc879f72SAndrew Lunn 
1825fc879f72SAndrew Lunn 	return 0;
1826fc879f72SAndrew Lunn }
1827fc879f72SAndrew Lunn 
1828fc879f72SAndrew Lunn static int marvell_vct7_cable_test_get_status(struct phy_device *phydev,
1829fc879f72SAndrew Lunn 					      bool *finished)
1830fc879f72SAndrew Lunn {
1831fc879f72SAndrew Lunn 	int ret;
1832fc879f72SAndrew Lunn 
1833fc879f72SAndrew Lunn 	*finished = false;
1834fc879f72SAndrew Lunn 
1835fc879f72SAndrew Lunn 	ret = phy_read_paged(phydev, MII_MARVELL_VCT7_PAGE,
1836fc879f72SAndrew Lunn 			     MII_VCT7_CTRL);
1837fc879f72SAndrew Lunn 
1838fc879f72SAndrew Lunn 	if (ret < 0)
1839fc879f72SAndrew Lunn 		return ret;
1840fc879f72SAndrew Lunn 
1841fc879f72SAndrew Lunn 	if (!(ret & MII_VCT7_CTRL_IN_PROGRESS)) {
1842fc879f72SAndrew Lunn 		*finished = true;
1843fc879f72SAndrew Lunn 
1844fc879f72SAndrew Lunn 		return marvell_vct7_cable_test_report(phydev);
1845fc879f72SAndrew Lunn 	}
1846fc879f72SAndrew Lunn 
1847fc879f72SAndrew Lunn 	return 0;
1848fc879f72SAndrew Lunn }
1849fc879f72SAndrew Lunn 
18500b04680fSAndrew Lunn #ifdef CONFIG_HWMON
18510b04680fSAndrew Lunn static int m88e1121_get_temp(struct phy_device *phydev, long *temp)
18520b04680fSAndrew Lunn {
1853975b388cSAndrew Lunn 	int oldpage;
1854424ca4c5SRussell King 	int ret = 0;
18550b04680fSAndrew Lunn 	int val;
18560b04680fSAndrew Lunn 
18570b04680fSAndrew Lunn 	*temp = 0;
18580b04680fSAndrew Lunn 
1859424ca4c5SRussell King 	oldpage = phy_select_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
1860424ca4c5SRussell King 	if (oldpage < 0)
1861424ca4c5SRussell King 		goto error;
1862975b388cSAndrew Lunn 
18630b04680fSAndrew Lunn 	/* Enable temperature sensor */
1864424ca4c5SRussell King 	ret = __phy_read(phydev, MII_88E1121_MISC_TEST);
18650b04680fSAndrew Lunn 	if (ret < 0)
18660b04680fSAndrew Lunn 		goto error;
18670b04680fSAndrew Lunn 
1868424ca4c5SRussell King 	ret = __phy_write(phydev, MII_88E1121_MISC_TEST,
18690b04680fSAndrew Lunn 			  ret | MII_88E1121_MISC_TEST_TEMP_SENSOR_EN);
18700b04680fSAndrew Lunn 	if (ret < 0)
18710b04680fSAndrew Lunn 		goto error;
18720b04680fSAndrew Lunn 
18730b04680fSAndrew Lunn 	/* Wait for temperature to stabilize */
18740b04680fSAndrew Lunn 	usleep_range(10000, 12000);
18750b04680fSAndrew Lunn 
1876424ca4c5SRussell King 	val = __phy_read(phydev, MII_88E1121_MISC_TEST);
18770b04680fSAndrew Lunn 	if (val < 0) {
18780b04680fSAndrew Lunn 		ret = val;
18790b04680fSAndrew Lunn 		goto error;
18800b04680fSAndrew Lunn 	}
18810b04680fSAndrew Lunn 
18820b04680fSAndrew Lunn 	/* Disable temperature sensor */
1883424ca4c5SRussell King 	ret = __phy_write(phydev, MII_88E1121_MISC_TEST,
18840b04680fSAndrew Lunn 			  ret & ~MII_88E1121_MISC_TEST_TEMP_SENSOR_EN);
18850b04680fSAndrew Lunn 	if (ret < 0)
18860b04680fSAndrew Lunn 		goto error;
18870b04680fSAndrew Lunn 
18880b04680fSAndrew Lunn 	*temp = ((val & MII_88E1121_MISC_TEST_TEMP_MASK) - 5) * 5000;
18890b04680fSAndrew Lunn 
18900b04680fSAndrew Lunn error:
1891424ca4c5SRussell King 	return phy_restore_page(phydev, oldpage, ret);
18920b04680fSAndrew Lunn }
18930b04680fSAndrew Lunn 
18940b04680fSAndrew Lunn static int m88e1121_hwmon_read(struct device *dev,
18950b04680fSAndrew Lunn 			       enum hwmon_sensor_types type,
18960b04680fSAndrew Lunn 			       u32 attr, int channel, long *temp)
18970b04680fSAndrew Lunn {
18980b04680fSAndrew Lunn 	struct phy_device *phydev = dev_get_drvdata(dev);
18990b04680fSAndrew Lunn 	int err;
19000b04680fSAndrew Lunn 
19010b04680fSAndrew Lunn 	switch (attr) {
19020b04680fSAndrew Lunn 	case hwmon_temp_input:
19030b04680fSAndrew Lunn 		err = m88e1121_get_temp(phydev, temp);
19040b04680fSAndrew Lunn 		break;
19050b04680fSAndrew Lunn 	default:
19060b04680fSAndrew Lunn 		return -EOPNOTSUPP;
19070b04680fSAndrew Lunn 	}
19080b04680fSAndrew Lunn 
19090b04680fSAndrew Lunn 	return err;
19100b04680fSAndrew Lunn }
19110b04680fSAndrew Lunn 
19120b04680fSAndrew Lunn static umode_t m88e1121_hwmon_is_visible(const void *data,
19130b04680fSAndrew Lunn 					 enum hwmon_sensor_types type,
19140b04680fSAndrew Lunn 					 u32 attr, int channel)
19150b04680fSAndrew Lunn {
19160b04680fSAndrew Lunn 	if (type != hwmon_temp)
19170b04680fSAndrew Lunn 		return 0;
19180b04680fSAndrew Lunn 
19190b04680fSAndrew Lunn 	switch (attr) {
19200b04680fSAndrew Lunn 	case hwmon_temp_input:
19210b04680fSAndrew Lunn 		return 0444;
19220b04680fSAndrew Lunn 	default:
19230b04680fSAndrew Lunn 		return 0;
19240b04680fSAndrew Lunn 	}
19250b04680fSAndrew Lunn }
19260b04680fSAndrew Lunn 
19270b04680fSAndrew Lunn static u32 m88e1121_hwmon_chip_config[] = {
19280b04680fSAndrew Lunn 	HWMON_C_REGISTER_TZ,
19290b04680fSAndrew Lunn 	0
19300b04680fSAndrew Lunn };
19310b04680fSAndrew Lunn 
19320b04680fSAndrew Lunn static const struct hwmon_channel_info m88e1121_hwmon_chip = {
19330b04680fSAndrew Lunn 	.type = hwmon_chip,
19340b04680fSAndrew Lunn 	.config = m88e1121_hwmon_chip_config,
19350b04680fSAndrew Lunn };
19360b04680fSAndrew Lunn 
19370b04680fSAndrew Lunn static u32 m88e1121_hwmon_temp_config[] = {
19380b04680fSAndrew Lunn 	HWMON_T_INPUT,
19390b04680fSAndrew Lunn 	0
19400b04680fSAndrew Lunn };
19410b04680fSAndrew Lunn 
19420b04680fSAndrew Lunn static const struct hwmon_channel_info m88e1121_hwmon_temp = {
19430b04680fSAndrew Lunn 	.type = hwmon_temp,
19440b04680fSAndrew Lunn 	.config = m88e1121_hwmon_temp_config,
19450b04680fSAndrew Lunn };
19460b04680fSAndrew Lunn 
19470b04680fSAndrew Lunn static const struct hwmon_channel_info *m88e1121_hwmon_info[] = {
19480b04680fSAndrew Lunn 	&m88e1121_hwmon_chip,
19490b04680fSAndrew Lunn 	&m88e1121_hwmon_temp,
19500b04680fSAndrew Lunn 	NULL
19510b04680fSAndrew Lunn };
19520b04680fSAndrew Lunn 
19530b04680fSAndrew Lunn static const struct hwmon_ops m88e1121_hwmon_hwmon_ops = {
19540b04680fSAndrew Lunn 	.is_visible = m88e1121_hwmon_is_visible,
19550b04680fSAndrew Lunn 	.read = m88e1121_hwmon_read,
19560b04680fSAndrew Lunn };
19570b04680fSAndrew Lunn 
19580b04680fSAndrew Lunn static const struct hwmon_chip_info m88e1121_hwmon_chip_info = {
19590b04680fSAndrew Lunn 	.ops = &m88e1121_hwmon_hwmon_ops,
19600b04680fSAndrew Lunn 	.info = m88e1121_hwmon_info,
19610b04680fSAndrew Lunn };
19620b04680fSAndrew Lunn 
19630b04680fSAndrew Lunn static int m88e1510_get_temp(struct phy_device *phydev, long *temp)
19640b04680fSAndrew Lunn {
19650b04680fSAndrew Lunn 	int ret;
19660b04680fSAndrew Lunn 
19670b04680fSAndrew Lunn 	*temp = 0;
19680b04680fSAndrew Lunn 
1969424ca4c5SRussell King 	ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
1970424ca4c5SRussell King 			     MII_88E1510_TEMP_SENSOR);
19710b04680fSAndrew Lunn 	if (ret < 0)
1972424ca4c5SRussell King 		return ret;
19730b04680fSAndrew Lunn 
19740b04680fSAndrew Lunn 	*temp = ((ret & MII_88E1510_TEMP_SENSOR_MASK) - 25) * 1000;
19750b04680fSAndrew Lunn 
1976424ca4c5SRussell King 	return 0;
19770b04680fSAndrew Lunn }
19780b04680fSAndrew Lunn 
1979f0a45816SColin Ian King static int m88e1510_get_temp_critical(struct phy_device *phydev, long *temp)
19800b04680fSAndrew Lunn {
19810b04680fSAndrew Lunn 	int ret;
19820b04680fSAndrew Lunn 
19830b04680fSAndrew Lunn 	*temp = 0;
19840b04680fSAndrew Lunn 
1985424ca4c5SRussell King 	ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
1986424ca4c5SRussell King 			     MII_88E1121_MISC_TEST);
19870b04680fSAndrew Lunn 	if (ret < 0)
1988424ca4c5SRussell King 		return ret;
19890b04680fSAndrew Lunn 
19900b04680fSAndrew Lunn 	*temp = (((ret & MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK) >>
19910b04680fSAndrew Lunn 		  MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT) * 5) - 25;
19920b04680fSAndrew Lunn 	/* convert to mC */
19930b04680fSAndrew Lunn 	*temp *= 1000;
19940b04680fSAndrew Lunn 
1995424ca4c5SRussell King 	return 0;
19960b04680fSAndrew Lunn }
19970b04680fSAndrew Lunn 
1998f0a45816SColin Ian King static int m88e1510_set_temp_critical(struct phy_device *phydev, long temp)
19990b04680fSAndrew Lunn {
20000b04680fSAndrew Lunn 	temp = temp / 1000;
20010b04680fSAndrew Lunn 	temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
20020b04680fSAndrew Lunn 
2003424ca4c5SRussell King 	return phy_modify_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
2004424ca4c5SRussell King 				MII_88E1121_MISC_TEST,
2005424ca4c5SRussell King 				MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK,
2006424ca4c5SRussell King 				temp << MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT);
20070b04680fSAndrew Lunn }
20080b04680fSAndrew Lunn 
2009f0a45816SColin Ian King static int m88e1510_get_temp_alarm(struct phy_device *phydev, long *alarm)
20100b04680fSAndrew Lunn {
20110b04680fSAndrew Lunn 	int ret;
20120b04680fSAndrew Lunn 
20130b04680fSAndrew Lunn 	*alarm = false;
20140b04680fSAndrew Lunn 
2015424ca4c5SRussell King 	ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
2016424ca4c5SRussell King 			     MII_88E1121_MISC_TEST);
20170b04680fSAndrew Lunn 	if (ret < 0)
2018424ca4c5SRussell King 		return ret;
2019424ca4c5SRussell King 
20200b04680fSAndrew Lunn 	*alarm = !!(ret & MII_88E1510_MISC_TEST_TEMP_IRQ);
20210b04680fSAndrew Lunn 
2022424ca4c5SRussell King 	return 0;
20230b04680fSAndrew Lunn }
20240b04680fSAndrew Lunn 
20250b04680fSAndrew Lunn static int m88e1510_hwmon_read(struct device *dev,
20260b04680fSAndrew Lunn 			       enum hwmon_sensor_types type,
20270b04680fSAndrew Lunn 			       u32 attr, int channel, long *temp)
20280b04680fSAndrew Lunn {
20290b04680fSAndrew Lunn 	struct phy_device *phydev = dev_get_drvdata(dev);
20300b04680fSAndrew Lunn 	int err;
20310b04680fSAndrew Lunn 
20320b04680fSAndrew Lunn 	switch (attr) {
20330b04680fSAndrew Lunn 	case hwmon_temp_input:
20340b04680fSAndrew Lunn 		err = m88e1510_get_temp(phydev, temp);
20350b04680fSAndrew Lunn 		break;
20360b04680fSAndrew Lunn 	case hwmon_temp_crit:
20370b04680fSAndrew Lunn 		err = m88e1510_get_temp_critical(phydev, temp);
20380b04680fSAndrew Lunn 		break;
20390b04680fSAndrew Lunn 	case hwmon_temp_max_alarm:
20400b04680fSAndrew Lunn 		err = m88e1510_get_temp_alarm(phydev, temp);
20410b04680fSAndrew Lunn 		break;
20420b04680fSAndrew Lunn 	default:
20430b04680fSAndrew Lunn 		return -EOPNOTSUPP;
20440b04680fSAndrew Lunn 	}
20450b04680fSAndrew Lunn 
20460b04680fSAndrew Lunn 	return err;
20470b04680fSAndrew Lunn }
20480b04680fSAndrew Lunn 
20490b04680fSAndrew Lunn static int m88e1510_hwmon_write(struct device *dev,
20500b04680fSAndrew Lunn 				enum hwmon_sensor_types type,
20510b04680fSAndrew Lunn 				u32 attr, int channel, long temp)
20520b04680fSAndrew Lunn {
20530b04680fSAndrew Lunn 	struct phy_device *phydev = dev_get_drvdata(dev);
20540b04680fSAndrew Lunn 	int err;
20550b04680fSAndrew Lunn 
20560b04680fSAndrew Lunn 	switch (attr) {
20570b04680fSAndrew Lunn 	case hwmon_temp_crit:
20580b04680fSAndrew Lunn 		err = m88e1510_set_temp_critical(phydev, temp);
20590b04680fSAndrew Lunn 		break;
20600b04680fSAndrew Lunn 	default:
20610b04680fSAndrew Lunn 		return -EOPNOTSUPP;
20620b04680fSAndrew Lunn 	}
20630b04680fSAndrew Lunn 	return err;
20640b04680fSAndrew Lunn }
20650b04680fSAndrew Lunn 
20660b04680fSAndrew Lunn static umode_t m88e1510_hwmon_is_visible(const void *data,
20670b04680fSAndrew Lunn 					 enum hwmon_sensor_types type,
20680b04680fSAndrew Lunn 					 u32 attr, int channel)
20690b04680fSAndrew Lunn {
20700b04680fSAndrew Lunn 	if (type != hwmon_temp)
20710b04680fSAndrew Lunn 		return 0;
20720b04680fSAndrew Lunn 
20730b04680fSAndrew Lunn 	switch (attr) {
20740b04680fSAndrew Lunn 	case hwmon_temp_input:
20750b04680fSAndrew Lunn 	case hwmon_temp_max_alarm:
20760b04680fSAndrew Lunn 		return 0444;
20770b04680fSAndrew Lunn 	case hwmon_temp_crit:
20780b04680fSAndrew Lunn 		return 0644;
20790b04680fSAndrew Lunn 	default:
20800b04680fSAndrew Lunn 		return 0;
20810b04680fSAndrew Lunn 	}
20820b04680fSAndrew Lunn }
20830b04680fSAndrew Lunn 
20840b04680fSAndrew Lunn static u32 m88e1510_hwmon_temp_config[] = {
20850b04680fSAndrew Lunn 	HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_MAX_ALARM,
20860b04680fSAndrew Lunn 	0
20870b04680fSAndrew Lunn };
20880b04680fSAndrew Lunn 
20890b04680fSAndrew Lunn static const struct hwmon_channel_info m88e1510_hwmon_temp = {
20900b04680fSAndrew Lunn 	.type = hwmon_temp,
20910b04680fSAndrew Lunn 	.config = m88e1510_hwmon_temp_config,
20920b04680fSAndrew Lunn };
20930b04680fSAndrew Lunn 
20940b04680fSAndrew Lunn static const struct hwmon_channel_info *m88e1510_hwmon_info[] = {
20950b04680fSAndrew Lunn 	&m88e1121_hwmon_chip,
20960b04680fSAndrew Lunn 	&m88e1510_hwmon_temp,
20970b04680fSAndrew Lunn 	NULL
20980b04680fSAndrew Lunn };
20990b04680fSAndrew Lunn 
21000b04680fSAndrew Lunn static const struct hwmon_ops m88e1510_hwmon_hwmon_ops = {
21010b04680fSAndrew Lunn 	.is_visible = m88e1510_hwmon_is_visible,
21020b04680fSAndrew Lunn 	.read = m88e1510_hwmon_read,
21030b04680fSAndrew Lunn 	.write = m88e1510_hwmon_write,
21040b04680fSAndrew Lunn };
21050b04680fSAndrew Lunn 
21060b04680fSAndrew Lunn static const struct hwmon_chip_info m88e1510_hwmon_chip_info = {
21070b04680fSAndrew Lunn 	.ops = &m88e1510_hwmon_hwmon_ops,
21080b04680fSAndrew Lunn 	.info = m88e1510_hwmon_info,
21090b04680fSAndrew Lunn };
21100b04680fSAndrew Lunn 
2111fee2d546SAndrew Lunn static int m88e6390_get_temp(struct phy_device *phydev, long *temp)
2112fee2d546SAndrew Lunn {
2113fee2d546SAndrew Lunn 	int sum = 0;
2114fee2d546SAndrew Lunn 	int oldpage;
2115fee2d546SAndrew Lunn 	int ret = 0;
2116fee2d546SAndrew Lunn 	int i;
2117fee2d546SAndrew Lunn 
2118fee2d546SAndrew Lunn 	*temp = 0;
2119fee2d546SAndrew Lunn 
2120fee2d546SAndrew Lunn 	oldpage = phy_select_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
2121fee2d546SAndrew Lunn 	if (oldpage < 0)
2122fee2d546SAndrew Lunn 		goto error;
2123fee2d546SAndrew Lunn 
2124fee2d546SAndrew Lunn 	/* Enable temperature sensor */
2125fee2d546SAndrew Lunn 	ret = __phy_read(phydev, MII_88E6390_MISC_TEST);
2126fee2d546SAndrew Lunn 	if (ret < 0)
2127fee2d546SAndrew Lunn 		goto error;
2128fee2d546SAndrew Lunn 
2129fee2d546SAndrew Lunn 	ret = ret & ~MII_88E6390_MISC_TEST_SAMPLE_MASK;
2130fee2d546SAndrew Lunn 	ret |= MII_88E6390_MISC_TEST_SAMPLE_ENABLE |
2131fee2d546SAndrew Lunn 		MII_88E6390_MISC_TEST_SAMPLE_1S;
2132fee2d546SAndrew Lunn 
2133fee2d546SAndrew Lunn 	ret = __phy_write(phydev, MII_88E6390_MISC_TEST, ret);
2134fee2d546SAndrew Lunn 	if (ret < 0)
2135fee2d546SAndrew Lunn 		goto error;
2136fee2d546SAndrew Lunn 
2137fee2d546SAndrew Lunn 	/* Wait for temperature to stabilize */
2138fee2d546SAndrew Lunn 	usleep_range(10000, 12000);
2139fee2d546SAndrew Lunn 
2140fee2d546SAndrew Lunn 	/* Reading the temperature sense has an errata. You need to read
2141fee2d546SAndrew Lunn 	 * a number of times and take an average.
2142fee2d546SAndrew Lunn 	 */
2143fee2d546SAndrew Lunn 	for (i = 0; i < MII_88E6390_TEMP_SENSOR_SAMPLES; i++) {
2144fee2d546SAndrew Lunn 		ret = __phy_read(phydev, MII_88E6390_TEMP_SENSOR);
2145fee2d546SAndrew Lunn 		if (ret < 0)
2146fee2d546SAndrew Lunn 			goto error;
2147fee2d546SAndrew Lunn 		sum += ret & MII_88E6390_TEMP_SENSOR_MASK;
2148fee2d546SAndrew Lunn 	}
2149fee2d546SAndrew Lunn 
2150fee2d546SAndrew Lunn 	sum /= MII_88E6390_TEMP_SENSOR_SAMPLES;
2151fee2d546SAndrew Lunn 	*temp = (sum  - 75) * 1000;
2152fee2d546SAndrew Lunn 
2153fee2d546SAndrew Lunn 	/* Disable temperature sensor */
2154fee2d546SAndrew Lunn 	ret = __phy_read(phydev, MII_88E6390_MISC_TEST);
2155fee2d546SAndrew Lunn 	if (ret < 0)
2156fee2d546SAndrew Lunn 		goto error;
2157fee2d546SAndrew Lunn 
2158fee2d546SAndrew Lunn 	ret = ret & ~MII_88E6390_MISC_TEST_SAMPLE_MASK;
2159fee2d546SAndrew Lunn 	ret |= MII_88E6390_MISC_TEST_SAMPLE_DISABLE;
2160fee2d546SAndrew Lunn 
2161fee2d546SAndrew Lunn 	ret = __phy_write(phydev, MII_88E6390_MISC_TEST, ret);
2162fee2d546SAndrew Lunn 
2163fee2d546SAndrew Lunn error:
2164fee2d546SAndrew Lunn 	phy_restore_page(phydev, oldpage, ret);
2165fee2d546SAndrew Lunn 
2166fee2d546SAndrew Lunn 	return ret;
2167fee2d546SAndrew Lunn }
2168fee2d546SAndrew Lunn 
2169fee2d546SAndrew Lunn static int m88e6390_hwmon_read(struct device *dev,
2170fee2d546SAndrew Lunn 			       enum hwmon_sensor_types type,
2171fee2d546SAndrew Lunn 			       u32 attr, int channel, long *temp)
2172fee2d546SAndrew Lunn {
2173fee2d546SAndrew Lunn 	struct phy_device *phydev = dev_get_drvdata(dev);
2174fee2d546SAndrew Lunn 	int err;
2175fee2d546SAndrew Lunn 
2176fee2d546SAndrew Lunn 	switch (attr) {
2177fee2d546SAndrew Lunn 	case hwmon_temp_input:
2178fee2d546SAndrew Lunn 		err = m88e6390_get_temp(phydev, temp);
2179fee2d546SAndrew Lunn 		break;
2180fee2d546SAndrew Lunn 	default:
2181fee2d546SAndrew Lunn 		return -EOPNOTSUPP;
2182fee2d546SAndrew Lunn 	}
2183fee2d546SAndrew Lunn 
2184fee2d546SAndrew Lunn 	return err;
2185fee2d546SAndrew Lunn }
2186fee2d546SAndrew Lunn 
2187fee2d546SAndrew Lunn static umode_t m88e6390_hwmon_is_visible(const void *data,
2188fee2d546SAndrew Lunn 					 enum hwmon_sensor_types type,
2189fee2d546SAndrew Lunn 					 u32 attr, int channel)
2190fee2d546SAndrew Lunn {
2191fee2d546SAndrew Lunn 	if (type != hwmon_temp)
2192fee2d546SAndrew Lunn 		return 0;
2193fee2d546SAndrew Lunn 
2194fee2d546SAndrew Lunn 	switch (attr) {
2195fee2d546SAndrew Lunn 	case hwmon_temp_input:
2196fee2d546SAndrew Lunn 		return 0444;
2197fee2d546SAndrew Lunn 	default:
2198fee2d546SAndrew Lunn 		return 0;
2199fee2d546SAndrew Lunn 	}
2200fee2d546SAndrew Lunn }
2201fee2d546SAndrew Lunn 
2202fee2d546SAndrew Lunn static u32 m88e6390_hwmon_temp_config[] = {
2203fee2d546SAndrew Lunn 	HWMON_T_INPUT,
2204fee2d546SAndrew Lunn 	0
2205fee2d546SAndrew Lunn };
2206fee2d546SAndrew Lunn 
2207fee2d546SAndrew Lunn static const struct hwmon_channel_info m88e6390_hwmon_temp = {
2208fee2d546SAndrew Lunn 	.type = hwmon_temp,
2209fee2d546SAndrew Lunn 	.config = m88e6390_hwmon_temp_config,
2210fee2d546SAndrew Lunn };
2211fee2d546SAndrew Lunn 
2212fee2d546SAndrew Lunn static const struct hwmon_channel_info *m88e6390_hwmon_info[] = {
2213fee2d546SAndrew Lunn 	&m88e1121_hwmon_chip,
2214fee2d546SAndrew Lunn 	&m88e6390_hwmon_temp,
2215fee2d546SAndrew Lunn 	NULL
2216fee2d546SAndrew Lunn };
2217fee2d546SAndrew Lunn 
2218fee2d546SAndrew Lunn static const struct hwmon_ops m88e6390_hwmon_hwmon_ops = {
2219fee2d546SAndrew Lunn 	.is_visible = m88e6390_hwmon_is_visible,
2220fee2d546SAndrew Lunn 	.read = m88e6390_hwmon_read,
2221fee2d546SAndrew Lunn };
2222fee2d546SAndrew Lunn 
2223fee2d546SAndrew Lunn static const struct hwmon_chip_info m88e6390_hwmon_chip_info = {
2224fee2d546SAndrew Lunn 	.ops = &m88e6390_hwmon_hwmon_ops,
2225fee2d546SAndrew Lunn 	.info = m88e6390_hwmon_info,
2226fee2d546SAndrew Lunn };
2227fee2d546SAndrew Lunn 
22280b04680fSAndrew Lunn static int marvell_hwmon_name(struct phy_device *phydev)
22290b04680fSAndrew Lunn {
22300b04680fSAndrew Lunn 	struct marvell_priv *priv = phydev->priv;
22310b04680fSAndrew Lunn 	struct device *dev = &phydev->mdio.dev;
22320b04680fSAndrew Lunn 	const char *devname = dev_name(dev);
22330b04680fSAndrew Lunn 	size_t len = strlen(devname);
22340b04680fSAndrew Lunn 	int i, j;
22350b04680fSAndrew Lunn 
22360b04680fSAndrew Lunn 	priv->hwmon_name = devm_kzalloc(dev, len, GFP_KERNEL);
22370b04680fSAndrew Lunn 	if (!priv->hwmon_name)
22380b04680fSAndrew Lunn 		return -ENOMEM;
22390b04680fSAndrew Lunn 
22400b04680fSAndrew Lunn 	for (i = j = 0; i < len && devname[i]; i++) {
22410b04680fSAndrew Lunn 		if (isalnum(devname[i]))
22420b04680fSAndrew Lunn 			priv->hwmon_name[j++] = devname[i];
22430b04680fSAndrew Lunn 	}
22440b04680fSAndrew Lunn 
22450b04680fSAndrew Lunn 	return 0;
22460b04680fSAndrew Lunn }
22470b04680fSAndrew Lunn 
22480b04680fSAndrew Lunn static int marvell_hwmon_probe(struct phy_device *phydev,
22490b04680fSAndrew Lunn 			       const struct hwmon_chip_info *chip)
22500b04680fSAndrew Lunn {
22510b04680fSAndrew Lunn 	struct marvell_priv *priv = phydev->priv;
22520b04680fSAndrew Lunn 	struct device *dev = &phydev->mdio.dev;
22530b04680fSAndrew Lunn 	int err;
22540b04680fSAndrew Lunn 
22550b04680fSAndrew Lunn 	err = marvell_hwmon_name(phydev);
22560b04680fSAndrew Lunn 	if (err)
22570b04680fSAndrew Lunn 		return err;
22580b04680fSAndrew Lunn 
22590b04680fSAndrew Lunn 	priv->hwmon_dev = devm_hwmon_device_register_with_info(
22600b04680fSAndrew Lunn 		dev, priv->hwmon_name, phydev, chip, NULL);
22610b04680fSAndrew Lunn 
22620b04680fSAndrew Lunn 	return PTR_ERR_OR_ZERO(priv->hwmon_dev);
22630b04680fSAndrew Lunn }
22640b04680fSAndrew Lunn 
22650b04680fSAndrew Lunn static int m88e1121_hwmon_probe(struct phy_device *phydev)
22660b04680fSAndrew Lunn {
22670b04680fSAndrew Lunn 	return marvell_hwmon_probe(phydev, &m88e1121_hwmon_chip_info);
22680b04680fSAndrew Lunn }
22690b04680fSAndrew Lunn 
22700b04680fSAndrew Lunn static int m88e1510_hwmon_probe(struct phy_device *phydev)
22710b04680fSAndrew Lunn {
22720b04680fSAndrew Lunn 	return marvell_hwmon_probe(phydev, &m88e1510_hwmon_chip_info);
22730b04680fSAndrew Lunn }
2274fee2d546SAndrew Lunn 
2275fee2d546SAndrew Lunn static int m88e6390_hwmon_probe(struct phy_device *phydev)
2276fee2d546SAndrew Lunn {
2277fee2d546SAndrew Lunn 	return marvell_hwmon_probe(phydev, &m88e6390_hwmon_chip_info);
2278fee2d546SAndrew Lunn }
22790b04680fSAndrew Lunn #else
22800b04680fSAndrew Lunn static int m88e1121_hwmon_probe(struct phy_device *phydev)
22810b04680fSAndrew Lunn {
22820b04680fSAndrew Lunn 	return 0;
22830b04680fSAndrew Lunn }
22840b04680fSAndrew Lunn 
22850b04680fSAndrew Lunn static int m88e1510_hwmon_probe(struct phy_device *phydev)
22860b04680fSAndrew Lunn {
22870b04680fSAndrew Lunn 	return 0;
22880b04680fSAndrew Lunn }
2289fee2d546SAndrew Lunn 
2290fee2d546SAndrew Lunn static int m88e6390_hwmon_probe(struct phy_device *phydev)
2291fee2d546SAndrew Lunn {
2292fee2d546SAndrew Lunn 	return 0;
2293fee2d546SAndrew Lunn }
22940b04680fSAndrew Lunn #endif
22950b04680fSAndrew Lunn 
2296d2fa47d9SAndrew Lunn static int marvell_probe(struct phy_device *phydev)
2297d2fa47d9SAndrew Lunn {
2298d2fa47d9SAndrew Lunn 	struct marvell_priv *priv;
2299d2fa47d9SAndrew Lunn 
2300e5a03bfdSAndrew Lunn 	priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
2301d2fa47d9SAndrew Lunn 	if (!priv)
2302d2fa47d9SAndrew Lunn 		return -ENOMEM;
2303d2fa47d9SAndrew Lunn 
2304d2fa47d9SAndrew Lunn 	phydev->priv = priv;
2305d2fa47d9SAndrew Lunn 
2306d2fa47d9SAndrew Lunn 	return 0;
2307d2fa47d9SAndrew Lunn }
2308d2fa47d9SAndrew Lunn 
23090b04680fSAndrew Lunn static int m88e1121_probe(struct phy_device *phydev)
23100b04680fSAndrew Lunn {
23110b04680fSAndrew Lunn 	int err;
23120b04680fSAndrew Lunn 
23130b04680fSAndrew Lunn 	err = marvell_probe(phydev);
23140b04680fSAndrew Lunn 	if (err)
23150b04680fSAndrew Lunn 		return err;
23160b04680fSAndrew Lunn 
23170b04680fSAndrew Lunn 	return m88e1121_hwmon_probe(phydev);
23180b04680fSAndrew Lunn }
23190b04680fSAndrew Lunn 
23200b04680fSAndrew Lunn static int m88e1510_probe(struct phy_device *phydev)
23210b04680fSAndrew Lunn {
23220b04680fSAndrew Lunn 	int err;
23230b04680fSAndrew Lunn 
23240b04680fSAndrew Lunn 	err = marvell_probe(phydev);
23250b04680fSAndrew Lunn 	if (err)
23260b04680fSAndrew Lunn 		return err;
23270b04680fSAndrew Lunn 
23280b04680fSAndrew Lunn 	return m88e1510_hwmon_probe(phydev);
23290b04680fSAndrew Lunn }
23300b04680fSAndrew Lunn 
2331fee2d546SAndrew Lunn static int m88e6390_probe(struct phy_device *phydev)
2332fee2d546SAndrew Lunn {
2333fee2d546SAndrew Lunn 	int err;
2334fee2d546SAndrew Lunn 
2335fee2d546SAndrew Lunn 	err = marvell_probe(phydev);
2336fee2d546SAndrew Lunn 	if (err)
2337fee2d546SAndrew Lunn 		return err;
2338fee2d546SAndrew Lunn 
2339fee2d546SAndrew Lunn 	return m88e6390_hwmon_probe(phydev);
2340fee2d546SAndrew Lunn }
2341fee2d546SAndrew Lunn 
2342e5479239SOlof Johansson static struct phy_driver marvell_drivers[] = {
2343e5479239SOlof Johansson 	{
23442f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1101,
23452f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
234600db8189SAndy Fleming 		.name = "Marvell 88E1101",
2347dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
234818702414SArnd Bergmann 		.probe = marvell_probe,
234979be1a1cSClemens Gruber 		.config_init = &marvell_config_init,
2350f2899788SAndrew Lunn 		.config_aneg = &m88e1101_config_aneg,
235100db8189SAndy Fleming 		.ack_interrupt = &marvell_ack_interrupt,
235200db8189SAndy Fleming 		.config_intr = &marvell_config_intr,
23530898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
23540898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2355424ca4c5SRussell King 		.read_page = marvell_read_page,
2356424ca4c5SRussell King 		.write_page = marvell_write_page,
2357d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2358d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2359d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2360e5479239SOlof Johansson 	},
2361e5479239SOlof Johansson 	{
23622f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1112,
23632f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
236485cfb534SOlof Johansson 		.name = "Marvell 88E1112",
2365dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2366d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
236785cfb534SOlof Johansson 		.config_init = &m88e1111_config_init,
236885cfb534SOlof Johansson 		.config_aneg = &marvell_config_aneg,
236985cfb534SOlof Johansson 		.ack_interrupt = &marvell_ack_interrupt,
237085cfb534SOlof Johansson 		.config_intr = &marvell_config_intr,
23710898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
23720898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2373424ca4c5SRussell King 		.read_page = marvell_read_page,
2374424ca4c5SRussell King 		.write_page = marvell_write_page,
2375d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2376d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2377d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2378262caf47SHeiner Kallweit 		.get_tunable = m88e1011_get_tunable,
2379262caf47SHeiner Kallweit 		.set_tunable = m88e1011_set_tunable,
238085cfb534SOlof Johansson 	},
238185cfb534SOlof Johansson 	{
23822f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1111,
23832f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
238476884679SAndy Fleming 		.name = "Marvell 88E1111",
2385dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2386d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
2387e5479239SOlof Johansson 		.config_init = &m88e1111_config_init,
2388d6ab9336SFlorian Fainelli 		.config_aneg = &marvell_config_aneg,
2389be937f1fSAlexandr Smirnov 		.read_status = &marvell_read_status,
239076884679SAndy Fleming 		.ack_interrupt = &marvell_ack_interrupt,
239176884679SAndy Fleming 		.config_intr = &marvell_config_intr,
23920898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
23930898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2394424ca4c5SRussell King 		.read_page = marvell_read_page,
2395424ca4c5SRussell King 		.write_page = marvell_write_page,
2396d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2397d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2398d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
23995c6bc519SHeiner Kallweit 		.get_tunable = m88e1111_get_tunable,
24005c6bc519SHeiner Kallweit 		.set_tunable = m88e1111_set_tunable,
2401e5479239SOlof Johansson 	},
2402e5479239SOlof Johansson 	{
24032f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1118,
24042f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2405605f196eSRon Madrid 		.name = "Marvell 88E1118",
2406dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2407d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
2408605f196eSRon Madrid 		.config_init = &m88e1118_config_init,
2409605f196eSRon Madrid 		.config_aneg = &m88e1118_config_aneg,
2410605f196eSRon Madrid 		.ack_interrupt = &marvell_ack_interrupt,
2411605f196eSRon Madrid 		.config_intr = &marvell_config_intr,
24120898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
24130898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2414424ca4c5SRussell King 		.read_page = marvell_read_page,
2415424ca4c5SRussell King 		.write_page = marvell_write_page,
2416d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2417d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2418d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2419605f196eSRon Madrid 	},
2420605f196eSRon Madrid 	{
24212f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1121R,
24222f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2423140bc929SSergei Poselenov 		.name = "Marvell 88E1121R",
2424dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
242518702414SArnd Bergmann 		.probe = &m88e1121_probe,
242607777246SWang Dongsheng 		.config_init = &marvell_config_init,
2427140bc929SSergei Poselenov 		.config_aneg = &m88e1121_config_aneg,
2428140bc929SSergei Poselenov 		.read_status = &marvell_read_status,
2429140bc929SSergei Poselenov 		.ack_interrupt = &marvell_ack_interrupt,
2430140bc929SSergei Poselenov 		.config_intr = &marvell_config_intr,
2431dcd07be3SAnatolij Gustschin 		.did_interrupt = &m88e1121_did_interrupt,
24320898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
24330898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2434424ca4c5SRussell King 		.read_page = marvell_read_page,
2435424ca4c5SRussell King 		.write_page = marvell_write_page,
2436d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2437d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2438d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2439911af5e1SHeiner Kallweit 		.get_tunable = m88e1011_get_tunable,
2440911af5e1SHeiner Kallweit 		.set_tunable = m88e1011_set_tunable,
2441140bc929SSergei Poselenov 	},
2442140bc929SSergei Poselenov 	{
2443337ac9d5SCyril Chemparathy 		.phy_id = MARVELL_PHY_ID_88E1318S,
24446ba74014SLinus Torvalds 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2445337ac9d5SCyril Chemparathy 		.name = "Marvell 88E1318S",
2446dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2447d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
2448dd9a122aSEsben Haabendal 		.config_init = &m88e1318_config_init,
2449337ac9d5SCyril Chemparathy 		.config_aneg = &m88e1318_config_aneg,
24503ff1c259SCyril Chemparathy 		.read_status = &marvell_read_status,
24513ff1c259SCyril Chemparathy 		.ack_interrupt = &marvell_ack_interrupt,
24523ff1c259SCyril Chemparathy 		.config_intr = &marvell_config_intr,
24533ff1c259SCyril Chemparathy 		.did_interrupt = &m88e1121_did_interrupt,
24543871c387SMichael Stapelberg 		.get_wol = &m88e1318_get_wol,
24553871c387SMichael Stapelberg 		.set_wol = &m88e1318_set_wol,
24560898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
24570898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2458424ca4c5SRussell King 		.read_page = marvell_read_page,
2459424ca4c5SRussell King 		.write_page = marvell_write_page,
2460d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2461d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2462d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
24633ff1c259SCyril Chemparathy 	},
24643ff1c259SCyril Chemparathy 	{
24652f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1145,
24662f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
246776884679SAndy Fleming 		.name = "Marvell 88E1145",
2468dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2469d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
247076884679SAndy Fleming 		.config_init = &m88e1145_config_init,
2471c505873eSZhao Qiang 		.config_aneg = &m88e1101_config_aneg,
247276884679SAndy Fleming 		.read_status = &genphy_read_status,
247376884679SAndy Fleming 		.ack_interrupt = &marvell_ack_interrupt,
247476884679SAndy Fleming 		.config_intr = &marvell_config_intr,
24750898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
24760898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2477424ca4c5SRussell King 		.read_page = marvell_read_page,
2478424ca4c5SRussell King 		.write_page = marvell_write_page,
2479d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2480d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2481d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2482a319fb52SHeiner Kallweit 		.get_tunable = m88e1111_get_tunable,
2483a319fb52SHeiner Kallweit 		.set_tunable = m88e1111_set_tunable,
2484ac8c635aSOlof Johansson 	},
2485ac8c635aSOlof Johansson 	{
248690600732SDavid Daney 		.phy_id = MARVELL_PHY_ID_88E1149R,
248790600732SDavid Daney 		.phy_id_mask = MARVELL_PHY_ID_MASK,
248890600732SDavid Daney 		.name = "Marvell 88E1149R",
2489dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2490d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
249190600732SDavid Daney 		.config_init = &m88e1149_config_init,
249290600732SDavid Daney 		.config_aneg = &m88e1118_config_aneg,
249390600732SDavid Daney 		.ack_interrupt = &marvell_ack_interrupt,
249490600732SDavid Daney 		.config_intr = &marvell_config_intr,
24950898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
24960898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2497424ca4c5SRussell King 		.read_page = marvell_read_page,
2498424ca4c5SRussell King 		.write_page = marvell_write_page,
2499d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2500d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2501d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
250290600732SDavid Daney 	},
250390600732SDavid Daney 	{
25042f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1240,
25052f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2506ac8c635aSOlof Johansson 		.name = "Marvell 88E1240",
2507dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2508d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
2509ac8c635aSOlof Johansson 		.config_init = &m88e1111_config_init,
2510ac8c635aSOlof Johansson 		.config_aneg = &marvell_config_aneg,
2511ac8c635aSOlof Johansson 		.ack_interrupt = &marvell_ack_interrupt,
2512ac8c635aSOlof Johansson 		.config_intr = &marvell_config_intr,
25130898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
25140898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2515424ca4c5SRussell King 		.read_page = marvell_read_page,
2516424ca4c5SRussell King 		.write_page = marvell_write_page,
2517d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2518d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2519d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2520ac8c635aSOlof Johansson 	},
25213da09a51SMichal Simek 	{
25223da09a51SMichal Simek 		.phy_id = MARVELL_PHY_ID_88E1116R,
25233da09a51SMichal Simek 		.phy_id_mask = MARVELL_PHY_ID_MASK,
25243da09a51SMichal Simek 		.name = "Marvell 88E1116R",
2525dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2526d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
25273da09a51SMichal Simek 		.config_init = &m88e1116r_config_init,
25283da09a51SMichal Simek 		.ack_interrupt = &marvell_ack_interrupt,
25293da09a51SMichal Simek 		.config_intr = &marvell_config_intr,
25300898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
25310898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2532424ca4c5SRussell King 		.read_page = marvell_read_page,
2533424ca4c5SRussell King 		.write_page = marvell_write_page,
2534d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2535d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2536d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2537262caf47SHeiner Kallweit 		.get_tunable = m88e1011_get_tunable,
2538262caf47SHeiner Kallweit 		.set_tunable = m88e1011_set_tunable,
25393da09a51SMichal Simek 	},
254010e24caaSMichal Simek 	{
254110e24caaSMichal Simek 		.phy_id = MARVELL_PHY_ID_88E1510,
254210e24caaSMichal Simek 		.phy_id_mask = MARVELL_PHY_ID_MASK,
254310e24caaSMichal Simek 		.name = "Marvell 88E1510",
2544719655a1SAndrew Lunn 		.features = PHY_GBIT_FIBRE_FEATURES,
2545fc879f72SAndrew Lunn 		.flags = PHY_POLL_CABLE_TEST,
25460b04680fSAndrew Lunn 		.probe = &m88e1510_probe,
2547930b37eeSStefan Roese 		.config_init = &m88e1510_config_init,
254810e24caaSMichal Simek 		.config_aneg = &m88e1510_config_aneg,
254910e24caaSMichal Simek 		.read_status = &marvell_read_status,
255010e24caaSMichal Simek 		.ack_interrupt = &marvell_ack_interrupt,
255110e24caaSMichal Simek 		.config_intr = &marvell_config_intr,
255210e24caaSMichal Simek 		.did_interrupt = &m88e1121_did_interrupt,
2553f39aac7eSJingju Hou 		.get_wol = &m88e1318_get_wol,
2554f39aac7eSJingju Hou 		.set_wol = &m88e1318_set_wol,
25553758be3dSCharles-Antoine Couret 		.resume = &marvell_resume,
25563758be3dSCharles-Antoine Couret 		.suspend = &marvell_suspend,
2557424ca4c5SRussell King 		.read_page = marvell_read_page,
2558424ca4c5SRussell King 		.write_page = marvell_write_page,
2559d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2560d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2561d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2562f0f9b4edSLin Yun Sheng 		.set_loopback = genphy_loopback,
2563262caf47SHeiner Kallweit 		.get_tunable = m88e1011_get_tunable,
2564262caf47SHeiner Kallweit 		.set_tunable = m88e1011_set_tunable,
2565fc879f72SAndrew Lunn 		.cable_test_start = marvell_vct7_cable_test_start,
2566fc879f72SAndrew Lunn 		.cable_test_get_status = marvell_vct7_cable_test_get_status,
256710e24caaSMichal Simek 	},
25686b358aedSSebastian Hesselbarth 	{
2569819ec8e1SAndrew Lunn 		.phy_id = MARVELL_PHY_ID_88E1540,
2570819ec8e1SAndrew Lunn 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2571819ec8e1SAndrew Lunn 		.name = "Marvell 88E1540",
2572dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2573fc879f72SAndrew Lunn 		.flags = PHY_POLL_CABLE_TEST,
257418702414SArnd Bergmann 		.probe = m88e1510_probe,
257579be1a1cSClemens Gruber 		.config_init = &marvell_config_init,
2576819ec8e1SAndrew Lunn 		.config_aneg = &m88e1510_config_aneg,
2577819ec8e1SAndrew Lunn 		.read_status = &marvell_read_status,
2578819ec8e1SAndrew Lunn 		.ack_interrupt = &marvell_ack_interrupt,
2579819ec8e1SAndrew Lunn 		.config_intr = &marvell_config_intr,
2580819ec8e1SAndrew Lunn 		.did_interrupt = &m88e1121_did_interrupt,
2581819ec8e1SAndrew Lunn 		.resume = &genphy_resume,
2582819ec8e1SAndrew Lunn 		.suspend = &genphy_suspend,
2583424ca4c5SRussell King 		.read_page = marvell_read_page,
2584424ca4c5SRussell King 		.write_page = marvell_write_page,
2585d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2586d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2587d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
258869f42be8SHeiner Kallweit 		.get_tunable = m88e1540_get_tunable,
258969f42be8SHeiner Kallweit 		.set_tunable = m88e1540_set_tunable,
2590fc879f72SAndrew Lunn 		.cable_test_start = marvell_vct7_cable_test_start,
2591fc879f72SAndrew Lunn 		.cable_test_get_status = marvell_vct7_cable_test_get_status,
2592819ec8e1SAndrew Lunn 	},
2593819ec8e1SAndrew Lunn 	{
259460f06fdeSAndrew Lunn 		.phy_id = MARVELL_PHY_ID_88E1545,
259560f06fdeSAndrew Lunn 		.phy_id_mask = MARVELL_PHY_ID_MASK,
259660f06fdeSAndrew Lunn 		.name = "Marvell 88E1545",
259760f06fdeSAndrew Lunn 		.probe = m88e1510_probe,
2598dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2599fc879f72SAndrew Lunn 		.flags = PHY_POLL_CABLE_TEST,
260060f06fdeSAndrew Lunn 		.config_init = &marvell_config_init,
260160f06fdeSAndrew Lunn 		.config_aneg = &m88e1510_config_aneg,
260260f06fdeSAndrew Lunn 		.read_status = &marvell_read_status,
260360f06fdeSAndrew Lunn 		.ack_interrupt = &marvell_ack_interrupt,
260460f06fdeSAndrew Lunn 		.config_intr = &marvell_config_intr,
260560f06fdeSAndrew Lunn 		.did_interrupt = &m88e1121_did_interrupt,
260660f06fdeSAndrew Lunn 		.resume = &genphy_resume,
260760f06fdeSAndrew Lunn 		.suspend = &genphy_suspend,
2608424ca4c5SRussell King 		.read_page = marvell_read_page,
2609424ca4c5SRussell King 		.write_page = marvell_write_page,
261060f06fdeSAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
261160f06fdeSAndrew Lunn 		.get_strings = marvell_get_strings,
261260f06fdeSAndrew Lunn 		.get_stats = marvell_get_stats,
2613262caf47SHeiner Kallweit 		.get_tunable = m88e1540_get_tunable,
2614262caf47SHeiner Kallweit 		.set_tunable = m88e1540_set_tunable,
2615fc879f72SAndrew Lunn 		.cable_test_start = marvell_vct7_cable_test_start,
2616fc879f72SAndrew Lunn 		.cable_test_get_status = marvell_vct7_cable_test_get_status,
261760f06fdeSAndrew Lunn 	},
261860f06fdeSAndrew Lunn 	{
26196b358aedSSebastian Hesselbarth 		.phy_id = MARVELL_PHY_ID_88E3016,
26206b358aedSSebastian Hesselbarth 		.phy_id_mask = MARVELL_PHY_ID_MASK,
26216b358aedSSebastian Hesselbarth 		.name = "Marvell 88E3016",
2622dcdecdcfSHeiner Kallweit 		/* PHY_BASIC_FEATURES */
2623d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
26246b358aedSSebastian Hesselbarth 		.config_init = &m88e3016_config_init,
26256b358aedSSebastian Hesselbarth 		.aneg_done = &marvell_aneg_done,
26266b358aedSSebastian Hesselbarth 		.read_status = &marvell_read_status,
26276b358aedSSebastian Hesselbarth 		.ack_interrupt = &marvell_ack_interrupt,
26286b358aedSSebastian Hesselbarth 		.config_intr = &marvell_config_intr,
26296b358aedSSebastian Hesselbarth 		.did_interrupt = &m88e1121_did_interrupt,
26306b358aedSSebastian Hesselbarth 		.resume = &genphy_resume,
26316b358aedSSebastian Hesselbarth 		.suspend = &genphy_suspend,
2632424ca4c5SRussell King 		.read_page = marvell_read_page,
2633424ca4c5SRussell King 		.write_page = marvell_write_page,
2634d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2635d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2636d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
26376b358aedSSebastian Hesselbarth 	},
2638e4cf8a38SAndrew Lunn 	{
2639e4cf8a38SAndrew Lunn 		.phy_id = MARVELL_PHY_ID_88E6390,
2640e4cf8a38SAndrew Lunn 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2641e4cf8a38SAndrew Lunn 		.name = "Marvell 88E6390",
2642dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
2643fc879f72SAndrew Lunn 		.flags = PHY_POLL_CABLE_TEST,
2644fee2d546SAndrew Lunn 		.probe = m88e6390_probe,
2645e4cf8a38SAndrew Lunn 		.config_init = &marvell_config_init,
26468cbcdc1aSAndrew Lunn 		.config_aneg = &m88e6390_config_aneg,
2647e4cf8a38SAndrew Lunn 		.read_status = &marvell_read_status,
2648e4cf8a38SAndrew Lunn 		.ack_interrupt = &marvell_ack_interrupt,
2649e4cf8a38SAndrew Lunn 		.config_intr = &marvell_config_intr,
2650e4cf8a38SAndrew Lunn 		.did_interrupt = &m88e1121_did_interrupt,
2651e4cf8a38SAndrew Lunn 		.resume = &genphy_resume,
2652e4cf8a38SAndrew Lunn 		.suspend = &genphy_suspend,
2653424ca4c5SRussell King 		.read_page = marvell_read_page,
2654424ca4c5SRussell King 		.write_page = marvell_write_page,
2655e4cf8a38SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2656e4cf8a38SAndrew Lunn 		.get_strings = marvell_get_strings,
2657e4cf8a38SAndrew Lunn 		.get_stats = marvell_get_stats,
265869f42be8SHeiner Kallweit 		.get_tunable = m88e1540_get_tunable,
265969f42be8SHeiner Kallweit 		.set_tunable = m88e1540_set_tunable,
2660fc879f72SAndrew Lunn 		.cable_test_start = marvell_vct7_cable_test_start,
2661fc879f72SAndrew Lunn 		.cable_test_get_status = marvell_vct7_cable_test_get_status,
2662e4cf8a38SAndrew Lunn 	},
266376884679SAndy Fleming };
266476884679SAndy Fleming 
266550fd7150SJohan Hovold module_phy_driver(marvell_drivers);
26664e4f10f6SDavid Woodhouse 
2667cf93c945SUwe Kleine-König static struct mdio_device_id __maybe_unused marvell_tbl[] = {
2668f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1101, MARVELL_PHY_ID_MASK },
2669f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1112, MARVELL_PHY_ID_MASK },
2670f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1111, MARVELL_PHY_ID_MASK },
2671f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1118, MARVELL_PHY_ID_MASK },
2672f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1121R, MARVELL_PHY_ID_MASK },
2673f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1145, MARVELL_PHY_ID_MASK },
2674f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1149R, MARVELL_PHY_ID_MASK },
2675f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1240, MARVELL_PHY_ID_MASK },
2676f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1318S, MARVELL_PHY_ID_MASK },
26773da09a51SMichal Simek 	{ MARVELL_PHY_ID_88E1116R, MARVELL_PHY_ID_MASK },
267810e24caaSMichal Simek 	{ MARVELL_PHY_ID_88E1510, MARVELL_PHY_ID_MASK },
2679819ec8e1SAndrew Lunn 	{ MARVELL_PHY_ID_88E1540, MARVELL_PHY_ID_MASK },
268060f06fdeSAndrew Lunn 	{ MARVELL_PHY_ID_88E1545, MARVELL_PHY_ID_MASK },
26816b358aedSSebastian Hesselbarth 	{ MARVELL_PHY_ID_88E3016, MARVELL_PHY_ID_MASK },
2682e4cf8a38SAndrew Lunn 	{ MARVELL_PHY_ID_88E6390, MARVELL_PHY_ID_MASK },
26834e4f10f6SDavid Woodhouse 	{ }
26844e4f10f6SDavid Woodhouse };
26854e4f10f6SDavid Woodhouse 
26864e4f10f6SDavid Woodhouse MODULE_DEVICE_TABLE(mdio, marvell_tbl);
2687