xref: /openbmc/linux/drivers/net/phy/marvell.c (revision 98409b2b)
100db8189SAndy Fleming /*
200db8189SAndy Fleming  * drivers/net/phy/marvell.c
300db8189SAndy Fleming  *
400db8189SAndy Fleming  * Driver for Marvell PHYs
500db8189SAndy Fleming  *
600db8189SAndy Fleming  * Author: Andy Fleming
700db8189SAndy Fleming  *
800db8189SAndy Fleming  * Copyright (c) 2004 Freescale Semiconductor, Inc.
900db8189SAndy Fleming  *
103871c387SMichael Stapelberg  * Copyright (c) 2013 Michael Stapelberg <michael@stapelberg.de>
113871c387SMichael Stapelberg  *
1200db8189SAndy Fleming  * This program is free software; you can redistribute  it and/or modify it
1300db8189SAndy Fleming  * under  the terms of  the GNU General  Public License as published by the
1400db8189SAndy Fleming  * Free Software Foundation;  either version 2 of the  License, or (at your
1500db8189SAndy Fleming  * option) any later version.
1600db8189SAndy Fleming  *
1700db8189SAndy Fleming  */
1800db8189SAndy Fleming #include <linux/kernel.h>
1900db8189SAndy Fleming #include <linux/string.h>
200b04680fSAndrew Lunn #include <linux/ctype.h>
2100db8189SAndy Fleming #include <linux/errno.h>
2200db8189SAndy Fleming #include <linux/unistd.h>
230b04680fSAndrew Lunn #include <linux/hwmon.h>
2400db8189SAndy Fleming #include <linux/interrupt.h>
2500db8189SAndy Fleming #include <linux/init.h>
2600db8189SAndy Fleming #include <linux/delay.h>
2700db8189SAndy Fleming #include <linux/netdevice.h>
2800db8189SAndy Fleming #include <linux/etherdevice.h>
2900db8189SAndy Fleming #include <linux/skbuff.h>
3000db8189SAndy Fleming #include <linux/spinlock.h>
3100db8189SAndy Fleming #include <linux/mm.h>
3200db8189SAndy Fleming #include <linux/module.h>
3300db8189SAndy Fleming #include <linux/mii.h>
3400db8189SAndy Fleming #include <linux/ethtool.h>
3500db8189SAndy Fleming #include <linux/phy.h>
362f495c39SBenjamin Herrenschmidt #include <linux/marvell_phy.h>
37cf41a51dSDavid Daney #include <linux/of.h>
3800db8189SAndy Fleming 
39eea3b201SAvinash Kumar #include <linux/io.h>
4000db8189SAndy Fleming #include <asm/irq.h>
41eea3b201SAvinash Kumar #include <linux/uaccess.h>
4200db8189SAndy Fleming 
4327d916d6SDavid Daney #define MII_MARVELL_PHY_PAGE		22
4452295666SAndrew Lunn #define MII_MARVELL_COPPER_PAGE		0x00
4552295666SAndrew Lunn #define MII_MARVELL_FIBER_PAGE		0x01
4652295666SAndrew Lunn #define MII_MARVELL_MSCR_PAGE		0x02
4752295666SAndrew Lunn #define MII_MARVELL_LED_PAGE		0x03
4852295666SAndrew Lunn #define MII_MARVELL_MISC_TEST_PAGE	0x06
4952295666SAndrew Lunn #define MII_MARVELL_WOL_PAGE		0x11
5027d916d6SDavid Daney 
5100db8189SAndy Fleming #define MII_M1011_IEVENT		0x13
5200db8189SAndy Fleming #define MII_M1011_IEVENT_CLEAR		0x0000
5300db8189SAndy Fleming 
5400db8189SAndy Fleming #define MII_M1011_IMASK			0x12
5500db8189SAndy Fleming #define MII_M1011_IMASK_INIT		0x6400
5600db8189SAndy Fleming #define MII_M1011_IMASK_CLEAR		0x0000
5700db8189SAndy Fleming 
5876884679SAndy Fleming #define MII_M1011_PHY_SCR			0x10
59fecd5e91SAndrew Lunn #define MII_M1011_PHY_SCR_DOWNSHIFT_EN		BIT(11)
60fecd5e91SAndrew Lunn #define MII_M1011_PHY_SCR_DOWNSHIFT_SHIFT	12
616ef05eb7SAndrew Lunn #define MII_M1011_PHY_SRC_DOWNSHIFT_MASK	0x7800
62fecd5e91SAndrew Lunn #define MII_M1011_PHY_SCR_MDI			(0x0 << 5)
63fecd5e91SAndrew Lunn #define MII_M1011_PHY_SCR_MDI_X			(0x1 << 5)
64fecd5e91SAndrew Lunn #define MII_M1011_PHY_SCR_AUTO_CROSS		(0x3 << 5)
6576884679SAndy Fleming 
6676884679SAndy Fleming #define MII_M1111_PHY_LED_CONTROL	0x18
6776884679SAndy Fleming #define MII_M1111_PHY_LED_DIRECT	0x4100
6876884679SAndy Fleming #define MII_M1111_PHY_LED_COMBINE	0x411c
69895ee682SKim Phillips #define MII_M1111_PHY_EXT_CR		0x14
7061111598SAndrew Lunn #define MII_M1111_RGMII_RX_DELAY	BIT(7)
7161111598SAndrew Lunn #define MII_M1111_RGMII_TX_DELAY	BIT(1)
72895ee682SKim Phillips #define MII_M1111_PHY_EXT_SR		0x1b
73be937f1fSAlexandr Smirnov 
74895ee682SKim Phillips #define MII_M1111_HWCFG_MODE_MASK		0xf
75be937f1fSAlexandr Smirnov #define MII_M1111_HWCFG_MODE_FIBER_RGMII	0x3
764117b5beSKapil Juneja #define MII_M1111_HWCFG_MODE_SGMII_NO_CLK	0x4
77865b813aSAndrew Lunn #define MII_M1111_HWCFG_MODE_RTBI		0x7
785f8cbc13SLiu Yu-B13201 #define MII_M1111_HWCFG_MODE_COPPER_RTBI	0x9
79865b813aSAndrew Lunn #define MII_M1111_HWCFG_MODE_COPPER_RGMII	0xb
80865b813aSAndrew Lunn #define MII_M1111_HWCFG_FIBER_COPPER_RES	BIT(13)
81865b813aSAndrew Lunn #define MII_M1111_HWCFG_FIBER_COPPER_AUTO	BIT(15)
82be937f1fSAlexandr Smirnov 
83c477d044SCyril Chemparathy #define MII_88E1121_PHY_MSCR_REG	21
84c477d044SCyril Chemparathy #define MII_88E1121_PHY_MSCR_RX_DELAY	BIT(5)
85c477d044SCyril Chemparathy #define MII_88E1121_PHY_MSCR_TX_DELAY	BIT(4)
86424ca4c5SRussell King #define MII_88E1121_PHY_MSCR_DELAY_MASK	(BIT(5) | BIT(4))
87c477d044SCyril Chemparathy 
880b04680fSAndrew Lunn #define MII_88E1121_MISC_TEST				0x1a
890b04680fSAndrew Lunn #define MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK	0x1f00
900b04680fSAndrew Lunn #define MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT	8
910b04680fSAndrew Lunn #define MII_88E1510_MISC_TEST_TEMP_IRQ_EN		BIT(7)
920b04680fSAndrew Lunn #define MII_88E1510_MISC_TEST_TEMP_IRQ			BIT(6)
930b04680fSAndrew Lunn #define MII_88E1121_MISC_TEST_TEMP_SENSOR_EN		BIT(5)
940b04680fSAndrew Lunn #define MII_88E1121_MISC_TEST_TEMP_MASK			0x1f
950b04680fSAndrew Lunn 
960b04680fSAndrew Lunn #define MII_88E1510_TEMP_SENSOR		0x1b
970b04680fSAndrew Lunn #define MII_88E1510_TEMP_SENSOR_MASK	0xff
980b04680fSAndrew Lunn 
99fee2d546SAndrew Lunn #define MII_88E6390_MISC_TEST		0x1b
100fee2d546SAndrew Lunn #define MII_88E6390_MISC_TEST_SAMPLE_1S		0
101fee2d546SAndrew Lunn #define MII_88E6390_MISC_TEST_SAMPLE_10MS	BIT(14)
102fee2d546SAndrew Lunn #define MII_88E6390_MISC_TEST_SAMPLE_DISABLE	BIT(15)
103fee2d546SAndrew Lunn #define MII_88E6390_MISC_TEST_SAMPLE_ENABLE	0
104fee2d546SAndrew Lunn #define MII_88E6390_MISC_TEST_SAMPLE_MASK	(0x3 << 14)
105fee2d546SAndrew Lunn 
106fee2d546SAndrew Lunn #define MII_88E6390_TEMP_SENSOR		0x1c
107fee2d546SAndrew Lunn #define MII_88E6390_TEMP_SENSOR_MASK	0xff
108fee2d546SAndrew Lunn #define MII_88E6390_TEMP_SENSOR_SAMPLES 10
109fee2d546SAndrew Lunn 
110337ac9d5SCyril Chemparathy #define MII_88E1318S_PHY_MSCR1_REG	16
111337ac9d5SCyril Chemparathy #define MII_88E1318S_PHY_MSCR1_PAD_ODD	BIT(6)
1123ff1c259SCyril Chemparathy 
1133871c387SMichael Stapelberg /* Copper Specific Interrupt Enable Register */
1143871c387SMichael Stapelberg #define MII_88E1318S_PHY_CSIER				0x12
1153871c387SMichael Stapelberg /* WOL Event Interrupt Enable */
1163871c387SMichael Stapelberg #define MII_88E1318S_PHY_CSIER_WOL_EIE			BIT(7)
1173871c387SMichael Stapelberg 
1183871c387SMichael Stapelberg /* LED Timer Control Register */
1193871c387SMichael Stapelberg #define MII_88E1318S_PHY_LED_TCR			0x12
1203871c387SMichael Stapelberg #define MII_88E1318S_PHY_LED_TCR_FORCE_INT		BIT(15)
1213871c387SMichael Stapelberg #define MII_88E1318S_PHY_LED_TCR_INTn_ENABLE		BIT(7)
1223871c387SMichael Stapelberg #define MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW		BIT(11)
1233871c387SMichael Stapelberg 
1243871c387SMichael Stapelberg /* Magic Packet MAC address registers */
1253871c387SMichael Stapelberg #define MII_88E1318S_PHY_MAGIC_PACKET_WORD2		0x17
1263871c387SMichael Stapelberg #define MII_88E1318S_PHY_MAGIC_PACKET_WORD1		0x18
1273871c387SMichael Stapelberg #define MII_88E1318S_PHY_MAGIC_PACKET_WORD0		0x19
1283871c387SMichael Stapelberg 
1293871c387SMichael Stapelberg #define MII_88E1318S_PHY_WOL_CTRL				0x10
1303871c387SMichael Stapelberg #define MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS		BIT(12)
1313871c387SMichael Stapelberg #define MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE	BIT(14)
1323871c387SMichael Stapelberg 
133140bc929SSergei Poselenov #define MII_88E1121_PHY_LED_CTRL	16
134140bc929SSergei Poselenov #define MII_88E1121_PHY_LED_DEF		0x0030
135140bc929SSergei Poselenov 
136be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS		0x11
137be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_1000	0x8000
138be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_100	0x4000
139be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_SPD_MASK	0xc000
140be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_FULLDUPLEX	0x2000
141be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_RESOLVED	0x0800
142be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_LINK	0x0400
143be937f1fSAlexandr Smirnov 
1446b358aedSSebastian Hesselbarth #define MII_88E3016_PHY_SPEC_CTRL	0x10
1456b358aedSSebastian Hesselbarth #define MII_88E3016_DISABLE_SCRAMBLER	0x0200
1466b358aedSSebastian Hesselbarth #define MII_88E3016_AUTO_MDIX_CROSSOVER	0x0030
14776884679SAndy Fleming 
148930b37eeSStefan Roese #define MII_88E1510_GEN_CTRL_REG_1		0x14
149930b37eeSStefan Roese #define MII_88E1510_GEN_CTRL_REG_1_MODE_MASK	0x7
150930b37eeSStefan Roese #define MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII	0x1	/* SGMII to copper */
151930b37eeSStefan Roese #define MII_88E1510_GEN_CTRL_REG_1_RESET	0x8000	/* Soft reset */
152930b37eeSStefan Roese 
1536cfb3bccSCharles-Antoine Couret #define LPA_FIBER_1000HALF	0x40
1546cfb3bccSCharles-Antoine Couret #define LPA_FIBER_1000FULL	0x20
1556cfb3bccSCharles-Antoine Couret 
1566cfb3bccSCharles-Antoine Couret #define LPA_PAUSE_FIBER		0x180
1576cfb3bccSCharles-Antoine Couret #define LPA_PAUSE_ASYM_FIBER	0x100
1586cfb3bccSCharles-Antoine Couret 
1596cfb3bccSCharles-Antoine Couret #define ADVERTISE_FIBER_1000HALF	0x40
1606cfb3bccSCharles-Antoine Couret #define ADVERTISE_FIBER_1000FULL	0x20
1616cfb3bccSCharles-Antoine Couret 
1626cfb3bccSCharles-Antoine Couret #define ADVERTISE_PAUSE_FIBER		0x180
1636cfb3bccSCharles-Antoine Couret #define ADVERTISE_PAUSE_ASYM_FIBER	0x100
1646cfb3bccSCharles-Antoine Couret 
1656cfb3bccSCharles-Antoine Couret #define REGISTER_LINK_STATUS	0x400
1662170fef7SCharles-Antoine Couret #define NB_FIBER_STATS	1
1676cfb3bccSCharles-Antoine Couret 
16800db8189SAndy Fleming MODULE_DESCRIPTION("Marvell PHY driver");
16900db8189SAndy Fleming MODULE_AUTHOR("Andy Fleming");
17000db8189SAndy Fleming MODULE_LICENSE("GPL");
17100db8189SAndy Fleming 
172d2fa47d9SAndrew Lunn struct marvell_hw_stat {
173d2fa47d9SAndrew Lunn 	const char *string;
174d2fa47d9SAndrew Lunn 	u8 page;
175d2fa47d9SAndrew Lunn 	u8 reg;
176d2fa47d9SAndrew Lunn 	u8 bits;
177d2fa47d9SAndrew Lunn };
178d2fa47d9SAndrew Lunn 
179d2fa47d9SAndrew Lunn static struct marvell_hw_stat marvell_hw_stats[] = {
1802170fef7SCharles-Antoine Couret 	{ "phy_receive_errors_copper", 0, 21, 16},
181d2fa47d9SAndrew Lunn 	{ "phy_idle_errors", 0, 10, 8 },
1822170fef7SCharles-Antoine Couret 	{ "phy_receive_errors_fiber", 1, 21, 16},
183d2fa47d9SAndrew Lunn };
184d2fa47d9SAndrew Lunn 
185d2fa47d9SAndrew Lunn struct marvell_priv {
186d2fa47d9SAndrew Lunn 	u64 stats[ARRAY_SIZE(marvell_hw_stats)];
1870b04680fSAndrew Lunn 	char *hwmon_name;
1880b04680fSAndrew Lunn 	struct device *hwmon_dev;
189d2fa47d9SAndrew Lunn };
190d2fa47d9SAndrew Lunn 
191424ca4c5SRussell King static int marvell_read_page(struct phy_device *phydev)
1926427bb2dSAndrew Lunn {
193424ca4c5SRussell King 	return __phy_read(phydev, MII_MARVELL_PHY_PAGE);
194424ca4c5SRussell King }
195424ca4c5SRussell King 
196424ca4c5SRussell King static int marvell_write_page(struct phy_device *phydev, int page)
197424ca4c5SRussell King {
198424ca4c5SRussell King 	return __phy_write(phydev, MII_MARVELL_PHY_PAGE, page);
1996427bb2dSAndrew Lunn }
2006427bb2dSAndrew Lunn 
2016427bb2dSAndrew Lunn static int marvell_set_page(struct phy_device *phydev, int page)
2026427bb2dSAndrew Lunn {
2036427bb2dSAndrew Lunn 	return phy_write(phydev, MII_MARVELL_PHY_PAGE, page);
2046427bb2dSAndrew Lunn }
2056427bb2dSAndrew Lunn 
20600db8189SAndy Fleming static int marvell_ack_interrupt(struct phy_device *phydev)
20700db8189SAndy Fleming {
20800db8189SAndy Fleming 	int err;
20900db8189SAndy Fleming 
21000db8189SAndy Fleming 	/* Clear the interrupts by reading the reg */
21100db8189SAndy Fleming 	err = phy_read(phydev, MII_M1011_IEVENT);
21200db8189SAndy Fleming 
21300db8189SAndy Fleming 	if (err < 0)
21400db8189SAndy Fleming 		return err;
21500db8189SAndy Fleming 
21600db8189SAndy Fleming 	return 0;
21700db8189SAndy Fleming }
21800db8189SAndy Fleming 
21900db8189SAndy Fleming static int marvell_config_intr(struct phy_device *phydev)
22000db8189SAndy Fleming {
22100db8189SAndy Fleming 	int err;
22200db8189SAndy Fleming 
22300db8189SAndy Fleming 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
22423beb38fSAndrew Lunn 		err = phy_write(phydev, MII_M1011_IMASK,
22523beb38fSAndrew Lunn 				MII_M1011_IMASK_INIT);
22600db8189SAndy Fleming 	else
22723beb38fSAndrew Lunn 		err = phy_write(phydev, MII_M1011_IMASK,
22823beb38fSAndrew Lunn 				MII_M1011_IMASK_CLEAR);
22900db8189SAndy Fleming 
23000db8189SAndy Fleming 	return err;
23100db8189SAndy Fleming }
23200db8189SAndy Fleming 
233239aa55bSDavid Thomson static int marvell_set_polarity(struct phy_device *phydev, int polarity)
234239aa55bSDavid Thomson {
235239aa55bSDavid Thomson 	int reg;
236239aa55bSDavid Thomson 	int err;
237239aa55bSDavid Thomson 	int val;
238239aa55bSDavid Thomson 
239239aa55bSDavid Thomson 	/* get the current settings */
240239aa55bSDavid Thomson 	reg = phy_read(phydev, MII_M1011_PHY_SCR);
241239aa55bSDavid Thomson 	if (reg < 0)
242239aa55bSDavid Thomson 		return reg;
243239aa55bSDavid Thomson 
244239aa55bSDavid Thomson 	val = reg;
245239aa55bSDavid Thomson 	val &= ~MII_M1011_PHY_SCR_AUTO_CROSS;
246239aa55bSDavid Thomson 	switch (polarity) {
247239aa55bSDavid Thomson 	case ETH_TP_MDI:
248239aa55bSDavid Thomson 		val |= MII_M1011_PHY_SCR_MDI;
249239aa55bSDavid Thomson 		break;
250239aa55bSDavid Thomson 	case ETH_TP_MDI_X:
251239aa55bSDavid Thomson 		val |= MII_M1011_PHY_SCR_MDI_X;
252239aa55bSDavid Thomson 		break;
253239aa55bSDavid Thomson 	case ETH_TP_MDI_AUTO:
254239aa55bSDavid Thomson 	case ETH_TP_MDI_INVALID:
255239aa55bSDavid Thomson 	default:
256239aa55bSDavid Thomson 		val |= MII_M1011_PHY_SCR_AUTO_CROSS;
257239aa55bSDavid Thomson 		break;
258239aa55bSDavid Thomson 	}
259239aa55bSDavid Thomson 
260239aa55bSDavid Thomson 	if (val != reg) {
261239aa55bSDavid Thomson 		/* Set the new polarity value in the register */
262239aa55bSDavid Thomson 		err = phy_write(phydev, MII_M1011_PHY_SCR, val);
263239aa55bSDavid Thomson 		if (err)
264239aa55bSDavid Thomson 			return err;
265239aa55bSDavid Thomson 	}
266239aa55bSDavid Thomson 
267239aa55bSDavid Thomson 	return 0;
268239aa55bSDavid Thomson }
269239aa55bSDavid Thomson 
2706ef05eb7SAndrew Lunn static int marvell_set_downshift(struct phy_device *phydev, bool enable,
2716ef05eb7SAndrew Lunn 				 u8 retries)
2726ef05eb7SAndrew Lunn {
2736ef05eb7SAndrew Lunn 	int reg;
2746ef05eb7SAndrew Lunn 
2756ef05eb7SAndrew Lunn 	reg = phy_read(phydev, MII_M1011_PHY_SCR);
2766ef05eb7SAndrew Lunn 	if (reg < 0)
2776ef05eb7SAndrew Lunn 		return reg;
2786ef05eb7SAndrew Lunn 
2796ef05eb7SAndrew Lunn 	reg &= MII_M1011_PHY_SRC_DOWNSHIFT_MASK;
2806ef05eb7SAndrew Lunn 	reg |= ((retries - 1) << MII_M1011_PHY_SCR_DOWNSHIFT_SHIFT);
2816ef05eb7SAndrew Lunn 	if (enable)
2826ef05eb7SAndrew Lunn 		reg |= MII_M1011_PHY_SCR_DOWNSHIFT_EN;
2836ef05eb7SAndrew Lunn 
2846ef05eb7SAndrew Lunn 	return phy_write(phydev, MII_M1011_PHY_SCR, reg);
2856ef05eb7SAndrew Lunn }
2866ef05eb7SAndrew Lunn 
28700db8189SAndy Fleming static int marvell_config_aneg(struct phy_device *phydev)
28800db8189SAndy Fleming {
28900db8189SAndy Fleming 	int err;
29000db8189SAndy Fleming 
2914e26c5c3SRaju Lakkaraju 	err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
29276884679SAndy Fleming 	if (err < 0)
29376884679SAndy Fleming 		return err;
29476884679SAndy Fleming 
29576884679SAndy Fleming 	err = phy_write(phydev, MII_M1111_PHY_LED_CONTROL,
29676884679SAndy Fleming 			MII_M1111_PHY_LED_DIRECT);
29776884679SAndy Fleming 	if (err < 0)
29876884679SAndy Fleming 		return err;
29900db8189SAndy Fleming 
30000db8189SAndy Fleming 	err = genphy_config_aneg(phydev);
3018ff44985SAnton Vorontsov 	if (err < 0)
30200db8189SAndy Fleming 		return err;
3038ff44985SAnton Vorontsov 
3048ff44985SAnton Vorontsov 	if (phydev->autoneg != AUTONEG_ENABLE) {
3050c3439bcSAndrew Lunn 		/* A write to speed/duplex bits (that is performed by
3068ff44985SAnton Vorontsov 		 * genphy_config_aneg() call above) must be followed by
3078ff44985SAnton Vorontsov 		 * a software reset. Otherwise, the write has no effect.
3088ff44985SAnton Vorontsov 		 */
30934386344SAndrew Lunn 		err = genphy_soft_reset(phydev);
3108ff44985SAnton Vorontsov 		if (err < 0)
3118ff44985SAnton Vorontsov 			return err;
3128ff44985SAnton Vorontsov 	}
3138ff44985SAnton Vorontsov 
3148ff44985SAnton Vorontsov 	return 0;
31500db8189SAndy Fleming }
31600db8189SAndy Fleming 
317f2899788SAndrew Lunn static int m88e1101_config_aneg(struct phy_device *phydev)
318f2899788SAndrew Lunn {
319f2899788SAndrew Lunn 	int err;
320f2899788SAndrew Lunn 
321f2899788SAndrew Lunn 	/* This Marvell PHY has an errata which requires
322f2899788SAndrew Lunn 	 * that certain registers get written in order
323f2899788SAndrew Lunn 	 * to restart autonegotiation
324f2899788SAndrew Lunn 	 */
32534386344SAndrew Lunn 	err = genphy_soft_reset(phydev);
326f2899788SAndrew Lunn 	if (err < 0)
327f2899788SAndrew Lunn 		return err;
328f2899788SAndrew Lunn 
329f2899788SAndrew Lunn 	err = phy_write(phydev, 0x1d, 0x1f);
330f2899788SAndrew Lunn 	if (err < 0)
331f2899788SAndrew Lunn 		return err;
332f2899788SAndrew Lunn 
333f2899788SAndrew Lunn 	err = phy_write(phydev, 0x1e, 0x200c);
334f2899788SAndrew Lunn 	if (err < 0)
335f2899788SAndrew Lunn 		return err;
336f2899788SAndrew Lunn 
337f2899788SAndrew Lunn 	err = phy_write(phydev, 0x1d, 0x5);
338f2899788SAndrew Lunn 	if (err < 0)
339f2899788SAndrew Lunn 		return err;
340f2899788SAndrew Lunn 
341f2899788SAndrew Lunn 	err = phy_write(phydev, 0x1e, 0);
342f2899788SAndrew Lunn 	if (err < 0)
343f2899788SAndrew Lunn 		return err;
344f2899788SAndrew Lunn 
345f2899788SAndrew Lunn 	err = phy_write(phydev, 0x1e, 0x100);
346f2899788SAndrew Lunn 	if (err < 0)
347f2899788SAndrew Lunn 		return err;
348f2899788SAndrew Lunn 
349f2899788SAndrew Lunn 	return marvell_config_aneg(phydev);
350f2899788SAndrew Lunn }
351f2899788SAndrew Lunn 
3523ec0a0f1SHarini Katakam static int m88e1111_config_aneg(struct phy_device *phydev)
3533ec0a0f1SHarini Katakam {
3543ec0a0f1SHarini Katakam 	int err;
3553ec0a0f1SHarini Katakam 
3563ec0a0f1SHarini Katakam 	/* The Marvell PHY has an errata which requires
3573ec0a0f1SHarini Katakam 	 * that certain registers get written in order
3583ec0a0f1SHarini Katakam 	 * to restart autonegotiation
3593ec0a0f1SHarini Katakam 	 */
36034386344SAndrew Lunn 	err = genphy_soft_reset(phydev);
3613ec0a0f1SHarini Katakam 
3624e26c5c3SRaju Lakkaraju 	err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
3633ec0a0f1SHarini Katakam 	if (err < 0)
3643ec0a0f1SHarini Katakam 		return err;
3653ec0a0f1SHarini Katakam 
3663ec0a0f1SHarini Katakam 	err = phy_write(phydev, MII_M1111_PHY_LED_CONTROL,
3673ec0a0f1SHarini Katakam 			MII_M1111_PHY_LED_DIRECT);
3683ec0a0f1SHarini Katakam 	if (err < 0)
3693ec0a0f1SHarini Katakam 		return err;
3703ec0a0f1SHarini Katakam 
3713ec0a0f1SHarini Katakam 	err = genphy_config_aneg(phydev);
3723ec0a0f1SHarini Katakam 	if (err < 0)
3733ec0a0f1SHarini Katakam 		return err;
3743ec0a0f1SHarini Katakam 
3753ec0a0f1SHarini Katakam 	if (phydev->autoneg != AUTONEG_ENABLE) {
3763ec0a0f1SHarini Katakam 		/* A write to speed/duplex bits (that is performed by
3773ec0a0f1SHarini Katakam 		 * genphy_config_aneg() call above) must be followed by
3783ec0a0f1SHarini Katakam 		 * a software reset. Otherwise, the write has no effect.
3793ec0a0f1SHarini Katakam 		 */
38034386344SAndrew Lunn 		err = genphy_soft_reset(phydev);
3813ec0a0f1SHarini Katakam 		if (err < 0)
3823ec0a0f1SHarini Katakam 			return err;
3833ec0a0f1SHarini Katakam 	}
3843ec0a0f1SHarini Katakam 
3853ec0a0f1SHarini Katakam 	return 0;
3863ec0a0f1SHarini Katakam }
3873ec0a0f1SHarini Katakam 
388cf41a51dSDavid Daney #ifdef CONFIG_OF_MDIO
3890c3439bcSAndrew Lunn /* Set and/or override some configuration registers based on the
390cf41a51dSDavid Daney  * marvell,reg-init property stored in the of_node for the phydev.
391cf41a51dSDavid Daney  *
392cf41a51dSDavid Daney  * marvell,reg-init = <reg-page reg mask value>,...;
393cf41a51dSDavid Daney  *
394cf41a51dSDavid Daney  * There may be one or more sets of <reg-page reg mask value>:
395cf41a51dSDavid Daney  *
396cf41a51dSDavid Daney  * reg-page: which register bank to use.
397cf41a51dSDavid Daney  * reg: the register.
398cf41a51dSDavid Daney  * mask: if non-zero, ANDed with existing register value.
399cf41a51dSDavid Daney  * value: ORed with the masked value and written to the regiser.
400cf41a51dSDavid Daney  *
401cf41a51dSDavid Daney  */
402cf41a51dSDavid Daney static int marvell_of_reg_init(struct phy_device *phydev)
403cf41a51dSDavid Daney {
404cf41a51dSDavid Daney 	const __be32 *paddr;
405424ca4c5SRussell King 	int len, i, saved_page, current_page, ret = 0;
406cf41a51dSDavid Daney 
407e5a03bfdSAndrew Lunn 	if (!phydev->mdio.dev.of_node)
408cf41a51dSDavid Daney 		return 0;
409cf41a51dSDavid Daney 
410e5a03bfdSAndrew Lunn 	paddr = of_get_property(phydev->mdio.dev.of_node,
411e5a03bfdSAndrew Lunn 				"marvell,reg-init", &len);
412cf41a51dSDavid Daney 	if (!paddr || len < (4 * sizeof(*paddr)))
413cf41a51dSDavid Daney 		return 0;
414cf41a51dSDavid Daney 
415424ca4c5SRussell King 	saved_page = phy_save_page(phydev);
416cf41a51dSDavid Daney 	if (saved_page < 0)
417424ca4c5SRussell King 		goto err;
418cf41a51dSDavid Daney 	current_page = saved_page;
419cf41a51dSDavid Daney 
420cf41a51dSDavid Daney 	len /= sizeof(*paddr);
421cf41a51dSDavid Daney 	for (i = 0; i < len - 3; i += 4) {
4226427bb2dSAndrew Lunn 		u16 page = be32_to_cpup(paddr + i);
423cf41a51dSDavid Daney 		u16 reg = be32_to_cpup(paddr + i + 1);
424cf41a51dSDavid Daney 		u16 mask = be32_to_cpup(paddr + i + 2);
425cf41a51dSDavid Daney 		u16 val_bits = be32_to_cpup(paddr + i + 3);
426cf41a51dSDavid Daney 		int val;
427cf41a51dSDavid Daney 
4286427bb2dSAndrew Lunn 		if (page != current_page) {
4296427bb2dSAndrew Lunn 			current_page = page;
430424ca4c5SRussell King 			ret = marvell_write_page(phydev, page);
431cf41a51dSDavid Daney 			if (ret < 0)
432cf41a51dSDavid Daney 				goto err;
433cf41a51dSDavid Daney 		}
434cf41a51dSDavid Daney 
435cf41a51dSDavid Daney 		val = 0;
436cf41a51dSDavid Daney 		if (mask) {
437424ca4c5SRussell King 			val = __phy_read(phydev, reg);
438cf41a51dSDavid Daney 			if (val < 0) {
439cf41a51dSDavid Daney 				ret = val;
440cf41a51dSDavid Daney 				goto err;
441cf41a51dSDavid Daney 			}
442cf41a51dSDavid Daney 			val &= mask;
443cf41a51dSDavid Daney 		}
444cf41a51dSDavid Daney 		val |= val_bits;
445cf41a51dSDavid Daney 
446424ca4c5SRussell King 		ret = __phy_write(phydev, reg, val);
447cf41a51dSDavid Daney 		if (ret < 0)
448cf41a51dSDavid Daney 			goto err;
449cf41a51dSDavid Daney 	}
450cf41a51dSDavid Daney err:
451424ca4c5SRussell King 	return phy_restore_page(phydev, saved_page, ret);
452cf41a51dSDavid Daney }
453cf41a51dSDavid Daney #else
454cf41a51dSDavid Daney static int marvell_of_reg_init(struct phy_device *phydev)
455cf41a51dSDavid Daney {
456cf41a51dSDavid Daney 	return 0;
457cf41a51dSDavid Daney }
458cf41a51dSDavid Daney #endif /* CONFIG_OF_MDIO */
459cf41a51dSDavid Daney 
460864dc729SAndrew Lunn static int m88e1121_config_aneg_rgmii_delays(struct phy_device *phydev)
461140bc929SSergei Poselenov {
462424ca4c5SRussell King 	int mscr;
463c477d044SCyril Chemparathy 
464c477d044SCyril Chemparathy 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
465424ca4c5SRussell King 		mscr = MII_88E1121_PHY_MSCR_RX_DELAY |
466424ca4c5SRussell King 		       MII_88E1121_PHY_MSCR_TX_DELAY;
467c477d044SCyril Chemparathy 	else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
468424ca4c5SRussell King 		mscr = MII_88E1121_PHY_MSCR_RX_DELAY;
469c477d044SCyril Chemparathy 	else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
470424ca4c5SRussell King 		mscr = MII_88E1121_PHY_MSCR_TX_DELAY;
471424ca4c5SRussell King 	else
472424ca4c5SRussell King 		mscr = 0;
473c477d044SCyril Chemparathy 
474424ca4c5SRussell King 	return phy_modify_paged(phydev, MII_MARVELL_MSCR_PAGE,
475424ca4c5SRussell King 				MII_88E1121_PHY_MSCR_REG,
476424ca4c5SRussell King 				MII_88E1121_PHY_MSCR_DELAY_MASK, mscr);
477be8c6480SArnaud Patard }
478c477d044SCyril Chemparathy 
479864dc729SAndrew Lunn static int m88e1121_config_aneg(struct phy_device *phydev)
480864dc729SAndrew Lunn {
481864dc729SAndrew Lunn 	int err = 0;
482864dc729SAndrew Lunn 
483864dc729SAndrew Lunn 	if (phy_interface_is_rgmii(phydev)) {
484864dc729SAndrew Lunn 		err = m88e1121_config_aneg_rgmii_delays(phydev);
485fea23fb5SRussell King 		if (err < 0)
486864dc729SAndrew Lunn 			return err;
487864dc729SAndrew Lunn 	}
488140bc929SSergei Poselenov 
48934386344SAndrew Lunn 	err = genphy_soft_reset(phydev);
490140bc929SSergei Poselenov 	if (err < 0)
491140bc929SSergei Poselenov 		return err;
492140bc929SSergei Poselenov 
493fecd5e91SAndrew Lunn 	err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
494140bc929SSergei Poselenov 	if (err < 0)
495140bc929SSergei Poselenov 		return err;
496140bc929SSergei Poselenov 
497fdecf36fSClemens Gruber 	return genphy_config_aneg(phydev);
498140bc929SSergei Poselenov }
499140bc929SSergei Poselenov 
500337ac9d5SCyril Chemparathy static int m88e1318_config_aneg(struct phy_device *phydev)
5013ff1c259SCyril Chemparathy {
502424ca4c5SRussell King 	int err;
5033ff1c259SCyril Chemparathy 
504424ca4c5SRussell King 	err = phy_modify_paged(phydev, MII_MARVELL_MSCR_PAGE,
505424ca4c5SRussell King 			       MII_88E1318S_PHY_MSCR1_REG,
506424ca4c5SRussell King 			       0, MII_88E1318S_PHY_MSCR1_PAD_ODD);
5073ff1c259SCyril Chemparathy 	if (err < 0)
5083ff1c259SCyril Chemparathy 		return err;
5093ff1c259SCyril Chemparathy 
5103ff1c259SCyril Chemparathy 	return m88e1121_config_aneg(phydev);
5113ff1c259SCyril Chemparathy }
5123ff1c259SCyril Chemparathy 
51378301ebeSCharles-Antoine Couret /**
51478301ebeSCharles-Antoine Couret  * ethtool_adv_to_fiber_adv_t
51578301ebeSCharles-Antoine Couret  * @ethadv: the ethtool advertisement settings
51678301ebeSCharles-Antoine Couret  *
51778301ebeSCharles-Antoine Couret  * A small helper function that translates ethtool advertisement
51878301ebeSCharles-Antoine Couret  * settings to phy autonegotiation advertisements for the
51978301ebeSCharles-Antoine Couret  * MII_ADV register for fiber link.
52078301ebeSCharles-Antoine Couret  */
52178301ebeSCharles-Antoine Couret static inline u32 ethtool_adv_to_fiber_adv_t(u32 ethadv)
52278301ebeSCharles-Antoine Couret {
52378301ebeSCharles-Antoine Couret 	u32 result = 0;
52478301ebeSCharles-Antoine Couret 
52578301ebeSCharles-Antoine Couret 	if (ethadv & ADVERTISED_1000baseT_Half)
52678301ebeSCharles-Antoine Couret 		result |= ADVERTISE_FIBER_1000HALF;
52778301ebeSCharles-Antoine Couret 	if (ethadv & ADVERTISED_1000baseT_Full)
52878301ebeSCharles-Antoine Couret 		result |= ADVERTISE_FIBER_1000FULL;
52978301ebeSCharles-Antoine Couret 
53078301ebeSCharles-Antoine Couret 	if ((ethadv & ADVERTISE_PAUSE_ASYM) && (ethadv & ADVERTISE_PAUSE_CAP))
53178301ebeSCharles-Antoine Couret 		result |= LPA_PAUSE_ASYM_FIBER;
53278301ebeSCharles-Antoine Couret 	else if (ethadv & ADVERTISE_PAUSE_CAP)
53378301ebeSCharles-Antoine Couret 		result |= (ADVERTISE_PAUSE_FIBER
53478301ebeSCharles-Antoine Couret 			   & (~ADVERTISE_PAUSE_ASYM_FIBER));
53578301ebeSCharles-Antoine Couret 
53678301ebeSCharles-Antoine Couret 	return result;
53778301ebeSCharles-Antoine Couret }
53878301ebeSCharles-Antoine Couret 
53978301ebeSCharles-Antoine Couret /**
54078301ebeSCharles-Antoine Couret  * marvell_config_aneg_fiber - restart auto-negotiation or write BMCR
54178301ebeSCharles-Antoine Couret  * @phydev: target phy_device struct
54278301ebeSCharles-Antoine Couret  *
54378301ebeSCharles-Antoine Couret  * Description: If auto-negotiation is enabled, we configure the
54478301ebeSCharles-Antoine Couret  *   advertising, and then restart auto-negotiation.  If it is not
54578301ebeSCharles-Antoine Couret  *   enabled, then we write the BMCR. Adapted for fiber link in
54678301ebeSCharles-Antoine Couret  *   some Marvell's devices.
54778301ebeSCharles-Antoine Couret  */
54878301ebeSCharles-Antoine Couret static int marvell_config_aneg_fiber(struct phy_device *phydev)
54978301ebeSCharles-Antoine Couret {
55078301ebeSCharles-Antoine Couret 	int changed = 0;
55178301ebeSCharles-Antoine Couret 	int err;
55278301ebeSCharles-Antoine Couret 	int adv, oldadv;
55378301ebeSCharles-Antoine Couret 	u32 advertise;
55478301ebeSCharles-Antoine Couret 
55578301ebeSCharles-Antoine Couret 	if (phydev->autoneg != AUTONEG_ENABLE)
55678301ebeSCharles-Antoine Couret 		return genphy_setup_forced(phydev);
55778301ebeSCharles-Antoine Couret 
55878301ebeSCharles-Antoine Couret 	/* Only allow advertising what this PHY supports */
55978301ebeSCharles-Antoine Couret 	phydev->advertising &= phydev->supported;
56078301ebeSCharles-Antoine Couret 	advertise = phydev->advertising;
56178301ebeSCharles-Antoine Couret 
56278301ebeSCharles-Antoine Couret 	/* Setup fiber advertisement */
56378301ebeSCharles-Antoine Couret 	adv = phy_read(phydev, MII_ADVERTISE);
56478301ebeSCharles-Antoine Couret 	if (adv < 0)
56578301ebeSCharles-Antoine Couret 		return adv;
56678301ebeSCharles-Antoine Couret 
56778301ebeSCharles-Antoine Couret 	oldadv = adv;
56878301ebeSCharles-Antoine Couret 	adv &= ~(ADVERTISE_FIBER_1000HALF | ADVERTISE_FIBER_1000FULL
56978301ebeSCharles-Antoine Couret 		| LPA_PAUSE_FIBER);
57078301ebeSCharles-Antoine Couret 	adv |= ethtool_adv_to_fiber_adv_t(advertise);
57178301ebeSCharles-Antoine Couret 
57278301ebeSCharles-Antoine Couret 	if (adv != oldadv) {
57378301ebeSCharles-Antoine Couret 		err = phy_write(phydev, MII_ADVERTISE, adv);
57478301ebeSCharles-Antoine Couret 		if (err < 0)
57578301ebeSCharles-Antoine Couret 			return err;
57678301ebeSCharles-Antoine Couret 
57778301ebeSCharles-Antoine Couret 		changed = 1;
57878301ebeSCharles-Antoine Couret 	}
57978301ebeSCharles-Antoine Couret 
58078301ebeSCharles-Antoine Couret 	if (changed == 0) {
58178301ebeSCharles-Antoine Couret 		/* Advertisement hasn't changed, but maybe aneg was never on to
58278301ebeSCharles-Antoine Couret 		 * begin with?	Or maybe phy was isolated?
58378301ebeSCharles-Antoine Couret 		 */
58478301ebeSCharles-Antoine Couret 		int ctl = phy_read(phydev, MII_BMCR);
58578301ebeSCharles-Antoine Couret 
58678301ebeSCharles-Antoine Couret 		if (ctl < 0)
58778301ebeSCharles-Antoine Couret 			return ctl;
58878301ebeSCharles-Antoine Couret 
58978301ebeSCharles-Antoine Couret 		if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE))
59078301ebeSCharles-Antoine Couret 			changed = 1; /* do restart aneg */
59178301ebeSCharles-Antoine Couret 	}
59278301ebeSCharles-Antoine Couret 
59378301ebeSCharles-Antoine Couret 	/* Only restart aneg if we are advertising something different
59478301ebeSCharles-Antoine Couret 	 * than we were before.
59578301ebeSCharles-Antoine Couret 	 */
59678301ebeSCharles-Antoine Couret 	if (changed > 0)
59778301ebeSCharles-Antoine Couret 		changed = genphy_restart_aneg(phydev);
59878301ebeSCharles-Antoine Couret 
59978301ebeSCharles-Antoine Couret 	return changed;
60078301ebeSCharles-Antoine Couret }
60178301ebeSCharles-Antoine Couret 
60210e24caaSMichal Simek static int m88e1510_config_aneg(struct phy_device *phydev)
60310e24caaSMichal Simek {
60410e24caaSMichal Simek 	int err;
60510e24caaSMichal Simek 
60652295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
60778301ebeSCharles-Antoine Couret 	if (err < 0)
60878301ebeSCharles-Antoine Couret 		goto error;
60978301ebeSCharles-Antoine Couret 
61078301ebeSCharles-Antoine Couret 	/* Configure the copper link first */
61110e24caaSMichal Simek 	err = m88e1318_config_aneg(phydev);
61210e24caaSMichal Simek 	if (err < 0)
61378301ebeSCharles-Antoine Couret 		goto error;
61410e24caaSMichal Simek 
615de9c4e06SRussell King 	/* Do not touch the fiber page if we're in copper->sgmii mode */
616de9c4e06SRussell King 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII)
617de9c4e06SRussell King 		return 0;
618de9c4e06SRussell King 
61978301ebeSCharles-Antoine Couret 	/* Then the fiber link */
62052295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
62178301ebeSCharles-Antoine Couret 	if (err < 0)
62278301ebeSCharles-Antoine Couret 		goto error;
62378301ebeSCharles-Antoine Couret 
62478301ebeSCharles-Antoine Couret 	err = marvell_config_aneg_fiber(phydev);
62578301ebeSCharles-Antoine Couret 	if (err < 0)
62678301ebeSCharles-Antoine Couret 		goto error;
62778301ebeSCharles-Antoine Couret 
62852295666SAndrew Lunn 	return marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
62978301ebeSCharles-Antoine Couret 
63078301ebeSCharles-Antoine Couret error:
63152295666SAndrew Lunn 	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
63278301ebeSCharles-Antoine Couret 	return err;
63379be1a1cSClemens Gruber }
63479be1a1cSClemens Gruber 
63579be1a1cSClemens Gruber static int marvell_config_init(struct phy_device *phydev)
63679be1a1cSClemens Gruber {
63779be1a1cSClemens Gruber 	/* Set registers from marvell,reg-init DT property */
63810e24caaSMichal Simek 	return marvell_of_reg_init(phydev);
63910e24caaSMichal Simek }
64010e24caaSMichal Simek 
6413da09a51SMichal Simek static int m88e1116r_config_init(struct phy_device *phydev)
6423da09a51SMichal Simek {
6433da09a51SMichal Simek 	int err;
6443da09a51SMichal Simek 
64534386344SAndrew Lunn 	err = genphy_soft_reset(phydev);
6463da09a51SMichal Simek 	if (err < 0)
6473da09a51SMichal Simek 		return err;
6483da09a51SMichal Simek 
6493da09a51SMichal Simek 	mdelay(500);
6503da09a51SMichal Simek 
65152295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
6523da09a51SMichal Simek 	if (err < 0)
6533da09a51SMichal Simek 		return err;
6543da09a51SMichal Simek 
655fecd5e91SAndrew Lunn 	err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
656fecd5e91SAndrew Lunn 	if (err < 0)
657fecd5e91SAndrew Lunn 		return err;
658fecd5e91SAndrew Lunn 
6596ef05eb7SAndrew Lunn 	err = marvell_set_downshift(phydev, true, 8);
6603da09a51SMichal Simek 	if (err < 0)
6613da09a51SMichal Simek 		return err;
6623da09a51SMichal Simek 
66314fc0abaSAndrew Lunn 	if (phy_interface_is_rgmii(phydev)) {
664864dc729SAndrew Lunn 		err = m88e1121_config_aneg_rgmii_delays(phydev);
6653da09a51SMichal Simek 		if (err < 0)
6663da09a51SMichal Simek 			return err;
66714fc0abaSAndrew Lunn 	}
6683da09a51SMichal Simek 
66934386344SAndrew Lunn 	err = genphy_soft_reset(phydev);
6703da09a51SMichal Simek 	if (err < 0)
6713da09a51SMichal Simek 		return err;
6723da09a51SMichal Simek 
67379be1a1cSClemens Gruber 	return marvell_config_init(phydev);
6743da09a51SMichal Simek }
6753da09a51SMichal Simek 
6766b358aedSSebastian Hesselbarth static int m88e3016_config_init(struct phy_device *phydev)
6776b358aedSSebastian Hesselbarth {
678fea23fb5SRussell King 	int ret;
6796b358aedSSebastian Hesselbarth 
6806b358aedSSebastian Hesselbarth 	/* Enable Scrambler and Auto-Crossover */
681fea23fb5SRussell King 	ret = phy_modify(phydev, MII_88E3016_PHY_SPEC_CTRL,
682f102852fSRussell King 			 MII_88E3016_DISABLE_SCRAMBLER,
683fea23fb5SRussell King 			 MII_88E3016_AUTO_MDIX_CROSSOVER);
684fea23fb5SRussell King 	if (ret < 0)
685fea23fb5SRussell King 		return ret;
6866b358aedSSebastian Hesselbarth 
68779be1a1cSClemens Gruber 	return marvell_config_init(phydev);
6886b358aedSSebastian Hesselbarth }
6896b358aedSSebastian Hesselbarth 
690865b813aSAndrew Lunn static int m88e1111_config_init_hwcfg_mode(struct phy_device *phydev,
691865b813aSAndrew Lunn 					   u16 mode,
692865b813aSAndrew Lunn 					   int fibre_copper_auto)
693865b813aSAndrew Lunn {
694865b813aSAndrew Lunn 	if (fibre_copper_auto)
695fea23fb5SRussell King 		mode |= MII_M1111_HWCFG_FIBER_COPPER_AUTO;
696865b813aSAndrew Lunn 
697fea23fb5SRussell King 	return phy_modify(phydev, MII_M1111_PHY_EXT_SR,
698f102852fSRussell King 			  MII_M1111_HWCFG_MODE_MASK |
699fea23fb5SRussell King 			  MII_M1111_HWCFG_FIBER_COPPER_AUTO |
700f102852fSRussell King 			  MII_M1111_HWCFG_FIBER_COPPER_RES,
701fea23fb5SRussell King 			  mode);
702865b813aSAndrew Lunn }
703865b813aSAndrew Lunn 
70461111598SAndrew Lunn static int m88e1111_config_init_rgmii_delays(struct phy_device *phydev)
705895ee682SKim Phillips {
706fea23fb5SRussell King 	int delay;
707895ee682SKim Phillips 
7089daf5a76SKim Phillips 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
709fea23fb5SRussell King 		delay = MII_M1111_RGMII_RX_DELAY | MII_M1111_RGMII_TX_DELAY;
7109daf5a76SKim Phillips 	} else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
711fea23fb5SRussell King 		delay = MII_M1111_RGMII_RX_DELAY;
7129daf5a76SKim Phillips 	} else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
713fea23fb5SRussell King 		delay = MII_M1111_RGMII_TX_DELAY;
714fea23fb5SRussell King 	} else {
715fea23fb5SRussell King 		delay = 0;
7169daf5a76SKim Phillips 	}
717895ee682SKim Phillips 
718fea23fb5SRussell King 	return phy_modify(phydev, MII_M1111_PHY_EXT_CR,
719f102852fSRussell King 			  MII_M1111_RGMII_RX_DELAY | MII_M1111_RGMII_TX_DELAY,
720fea23fb5SRussell King 			  delay);
72161111598SAndrew Lunn }
72261111598SAndrew Lunn 
72361111598SAndrew Lunn static int m88e1111_config_init_rgmii(struct phy_device *phydev)
72461111598SAndrew Lunn {
72561111598SAndrew Lunn 	int temp;
72661111598SAndrew Lunn 	int err;
72761111598SAndrew Lunn 
72861111598SAndrew Lunn 	err = m88e1111_config_init_rgmii_delays(phydev);
729895ee682SKim Phillips 	if (err < 0)
730895ee682SKim Phillips 		return err;
731895ee682SKim Phillips 
732895ee682SKim Phillips 	temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
733895ee682SKim Phillips 	if (temp < 0)
734895ee682SKim Phillips 		return temp;
735895ee682SKim Phillips 
736895ee682SKim Phillips 	temp &= ~(MII_M1111_HWCFG_MODE_MASK);
737be937f1fSAlexandr Smirnov 
7387239016dSWang Jian 	if (temp & MII_M1111_HWCFG_FIBER_COPPER_RES)
739be937f1fSAlexandr Smirnov 		temp |= MII_M1111_HWCFG_MODE_FIBER_RGMII;
740be937f1fSAlexandr Smirnov 	else
741be937f1fSAlexandr Smirnov 		temp |= MII_M1111_HWCFG_MODE_COPPER_RGMII;
742895ee682SKim Phillips 
743e1dde8dcSAndrew Lunn 	return phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
744895ee682SKim Phillips }
745895ee682SKim Phillips 
746e1dde8dcSAndrew Lunn static int m88e1111_config_init_sgmii(struct phy_device *phydev)
747e1dde8dcSAndrew Lunn {
748e1dde8dcSAndrew Lunn 	int err;
749e1dde8dcSAndrew Lunn 
750865b813aSAndrew Lunn 	err = m88e1111_config_init_hwcfg_mode(
751865b813aSAndrew Lunn 		phydev,
752865b813aSAndrew Lunn 		MII_M1111_HWCFG_MODE_SGMII_NO_CLK,
753865b813aSAndrew Lunn 		MII_M1111_HWCFG_FIBER_COPPER_AUTO);
7544117b5beSKapil Juneja 	if (err < 0)
7554117b5beSKapil Juneja 		return err;
75607151bc9SMadalin Bucur 
75707151bc9SMadalin Bucur 	/* make sure copper is selected */
75852295666SAndrew Lunn 	return marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
7594117b5beSKapil Juneja }
7604117b5beSKapil Juneja 
761e1dde8dcSAndrew Lunn static int m88e1111_config_init_rtbi(struct phy_device *phydev)
762e1dde8dcSAndrew Lunn {
76361111598SAndrew Lunn 	int err;
764e1dde8dcSAndrew Lunn 
76561111598SAndrew Lunn 	err = m88e1111_config_init_rgmii_delays(phydev);
766fea23fb5SRussell King 	if (err < 0)
7675f8cbc13SLiu Yu-B13201 		return err;
7685f8cbc13SLiu Yu-B13201 
769865b813aSAndrew Lunn 	err = m88e1111_config_init_hwcfg_mode(
770865b813aSAndrew Lunn 		phydev,
771865b813aSAndrew Lunn 		MII_M1111_HWCFG_MODE_RTBI,
772865b813aSAndrew Lunn 		MII_M1111_HWCFG_FIBER_COPPER_AUTO);
7735f8cbc13SLiu Yu-B13201 	if (err < 0)
7745f8cbc13SLiu Yu-B13201 		return err;
7755f8cbc13SLiu Yu-B13201 
7765f8cbc13SLiu Yu-B13201 	/* soft reset */
77734386344SAndrew Lunn 	err = genphy_soft_reset(phydev);
7785f8cbc13SLiu Yu-B13201 	if (err < 0)
7795f8cbc13SLiu Yu-B13201 		return err;
780e1dde8dcSAndrew Lunn 
781865b813aSAndrew Lunn 	return m88e1111_config_init_hwcfg_mode(
782865b813aSAndrew Lunn 		phydev,
783865b813aSAndrew Lunn 		MII_M1111_HWCFG_MODE_RTBI,
784865b813aSAndrew Lunn 		MII_M1111_HWCFG_FIBER_COPPER_AUTO);
785e1dde8dcSAndrew Lunn }
786e1dde8dcSAndrew Lunn 
787e1dde8dcSAndrew Lunn static int m88e1111_config_init(struct phy_device *phydev)
788e1dde8dcSAndrew Lunn {
789e1dde8dcSAndrew Lunn 	int err;
790e1dde8dcSAndrew Lunn 
791e1dde8dcSAndrew Lunn 	if (phy_interface_is_rgmii(phydev)) {
792e1dde8dcSAndrew Lunn 		err = m88e1111_config_init_rgmii(phydev);
793fea23fb5SRussell King 		if (err < 0)
794e1dde8dcSAndrew Lunn 			return err;
795e1dde8dcSAndrew Lunn 	}
796e1dde8dcSAndrew Lunn 
797e1dde8dcSAndrew Lunn 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
798e1dde8dcSAndrew Lunn 		err = m88e1111_config_init_sgmii(phydev);
799e1dde8dcSAndrew Lunn 		if (err < 0)
800e1dde8dcSAndrew Lunn 			return err;
801e1dde8dcSAndrew Lunn 	}
802e1dde8dcSAndrew Lunn 
803e1dde8dcSAndrew Lunn 	if (phydev->interface == PHY_INTERFACE_MODE_RTBI) {
804e1dde8dcSAndrew Lunn 		err = m88e1111_config_init_rtbi(phydev);
8055f8cbc13SLiu Yu-B13201 		if (err < 0)
8065f8cbc13SLiu Yu-B13201 			return err;
8075f8cbc13SLiu Yu-B13201 	}
8085f8cbc13SLiu Yu-B13201 
809cf41a51dSDavid Daney 	err = marvell_of_reg_init(phydev);
810cf41a51dSDavid Daney 	if (err < 0)
811cf41a51dSDavid Daney 		return err;
8125f8cbc13SLiu Yu-B13201 
81334386344SAndrew Lunn 	return genphy_soft_reset(phydev);
814895ee682SKim Phillips }
815895ee682SKim Phillips 
816fdecf36fSClemens Gruber static int m88e1121_config_init(struct phy_device *phydev)
817fdecf36fSClemens Gruber {
818424ca4c5SRussell King 	int err;
819fdecf36fSClemens Gruber 
820fdecf36fSClemens Gruber 	/* Default PHY LED config: LED[0] .. Link, LED[1] .. Activity */
821424ca4c5SRussell King 	err = phy_write_paged(phydev, MII_MARVELL_LED_PAGE,
822424ca4c5SRussell King 			      MII_88E1121_PHY_LED_CTRL,
823fdecf36fSClemens Gruber 			      MII_88E1121_PHY_LED_DEF);
824fdecf36fSClemens Gruber 	if (err < 0)
825fdecf36fSClemens Gruber 		return err;
826fdecf36fSClemens Gruber 
827fdecf36fSClemens Gruber 	/* Set marvell,reg-init configuration from device tree */
828fdecf36fSClemens Gruber 	return marvell_config_init(phydev);
829fdecf36fSClemens Gruber }
830fdecf36fSClemens Gruber 
831407353ecSClemens Gruber static int m88e1510_config_init(struct phy_device *phydev)
832407353ecSClemens Gruber {
833407353ecSClemens Gruber 	int err;
834407353ecSClemens Gruber 
835407353ecSClemens Gruber 	/* SGMII-to-Copper mode initialization */
836407353ecSClemens Gruber 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
8376623c0fbSRussell King 		u32 pause;
8386623c0fbSRussell King 
839407353ecSClemens Gruber 		/* Select page 18 */
8406427bb2dSAndrew Lunn 		err = marvell_set_page(phydev, 18);
841407353ecSClemens Gruber 		if (err < 0)
842407353ecSClemens Gruber 			return err;
843407353ecSClemens Gruber 
844407353ecSClemens Gruber 		/* In reg 20, write MODE[2:0] = 0x1 (SGMII to Copper) */
845fea23fb5SRussell King 		err = phy_modify(phydev, MII_88E1510_GEN_CTRL_REG_1,
846f102852fSRussell King 				 MII_88E1510_GEN_CTRL_REG_1_MODE_MASK,
847fea23fb5SRussell King 				 MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII);
848407353ecSClemens Gruber 		if (err < 0)
849407353ecSClemens Gruber 			return err;
850407353ecSClemens Gruber 
851407353ecSClemens Gruber 		/* PHY reset is necessary after changing MODE[2:0] */
852fea23fb5SRussell King 		err = phy_modify(phydev, MII_88E1510_GEN_CTRL_REG_1, 0,
853fea23fb5SRussell King 				 MII_88E1510_GEN_CTRL_REG_1_RESET);
854407353ecSClemens Gruber 		if (err < 0)
855407353ecSClemens Gruber 			return err;
856407353ecSClemens Gruber 
857407353ecSClemens Gruber 		/* Reset page selection */
85852295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
859407353ecSClemens Gruber 		if (err < 0)
860407353ecSClemens Gruber 			return err;
8616623c0fbSRussell King 
8626623c0fbSRussell King 		/* There appears to be a bug in the 88e1512 when used in
8636623c0fbSRussell King 		 * SGMII to copper mode, where the AN advertisment register
8646623c0fbSRussell King 		 * clears the pause bits each time a negotiation occurs.
8656623c0fbSRussell King 		 * This means we can never be truely sure what was advertised,
8666623c0fbSRussell King 		 * so disable Pause support.
8676623c0fbSRussell King 		 */
8686623c0fbSRussell King 		pause = SUPPORTED_Pause | SUPPORTED_Asym_Pause;
8696623c0fbSRussell King 		phydev->supported &= ~pause;
8706623c0fbSRussell King 		phydev->advertising &= ~pause;
871407353ecSClemens Gruber 	}
872407353ecSClemens Gruber 
873fdecf36fSClemens Gruber 	return m88e1121_config_init(phydev);
874407353ecSClemens Gruber }
875407353ecSClemens Gruber 
876605f196eSRon Madrid static int m88e1118_config_aneg(struct phy_device *phydev)
877605f196eSRon Madrid {
878605f196eSRon Madrid 	int err;
879605f196eSRon Madrid 
88034386344SAndrew Lunn 	err = genphy_soft_reset(phydev);
881605f196eSRon Madrid 	if (err < 0)
882605f196eSRon Madrid 		return err;
883605f196eSRon Madrid 
884fecd5e91SAndrew Lunn 	err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
885605f196eSRon Madrid 	if (err < 0)
886605f196eSRon Madrid 		return err;
887605f196eSRon Madrid 
888605f196eSRon Madrid 	err = genphy_config_aneg(phydev);
889605f196eSRon Madrid 	return 0;
890605f196eSRon Madrid }
891605f196eSRon Madrid 
892605f196eSRon Madrid static int m88e1118_config_init(struct phy_device *phydev)
893605f196eSRon Madrid {
894605f196eSRon Madrid 	int err;
895605f196eSRon Madrid 
896605f196eSRon Madrid 	/* Change address */
89752295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_MSCR_PAGE);
898605f196eSRon Madrid 	if (err < 0)
899605f196eSRon Madrid 		return err;
900605f196eSRon Madrid 
901605f196eSRon Madrid 	/* Enable 1000 Mbit */
902605f196eSRon Madrid 	err = phy_write(phydev, 0x15, 0x1070);
903605f196eSRon Madrid 	if (err < 0)
904605f196eSRon Madrid 		return err;
905605f196eSRon Madrid 
906605f196eSRon Madrid 	/* Change address */
90752295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_LED_PAGE);
908605f196eSRon Madrid 	if (err < 0)
909605f196eSRon Madrid 		return err;
910605f196eSRon Madrid 
911605f196eSRon Madrid 	/* Adjust LED Control */
9122f495c39SBenjamin Herrenschmidt 	if (phydev->dev_flags & MARVELL_PHY_M1118_DNS323_LEDS)
9132f495c39SBenjamin Herrenschmidt 		err = phy_write(phydev, 0x10, 0x1100);
9142f495c39SBenjamin Herrenschmidt 	else
915605f196eSRon Madrid 		err = phy_write(phydev, 0x10, 0x021e);
916605f196eSRon Madrid 	if (err < 0)
917605f196eSRon Madrid 		return err;
918605f196eSRon Madrid 
919cf41a51dSDavid Daney 	err = marvell_of_reg_init(phydev);
920cf41a51dSDavid Daney 	if (err < 0)
921cf41a51dSDavid Daney 		return err;
922cf41a51dSDavid Daney 
923605f196eSRon Madrid 	/* Reset address */
92452295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
925605f196eSRon Madrid 	if (err < 0)
926605f196eSRon Madrid 		return err;
927605f196eSRon Madrid 
92834386344SAndrew Lunn 	return genphy_soft_reset(phydev);
929605f196eSRon Madrid }
930605f196eSRon Madrid 
93190600732SDavid Daney static int m88e1149_config_init(struct phy_device *phydev)
93290600732SDavid Daney {
93390600732SDavid Daney 	int err;
93490600732SDavid Daney 
93590600732SDavid Daney 	/* Change address */
93652295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_MSCR_PAGE);
93790600732SDavid Daney 	if (err < 0)
93890600732SDavid Daney 		return err;
93990600732SDavid Daney 
94090600732SDavid Daney 	/* Enable 1000 Mbit */
94190600732SDavid Daney 	err = phy_write(phydev, 0x15, 0x1048);
94290600732SDavid Daney 	if (err < 0)
94390600732SDavid Daney 		return err;
94490600732SDavid Daney 
945cf41a51dSDavid Daney 	err = marvell_of_reg_init(phydev);
946cf41a51dSDavid Daney 	if (err < 0)
947cf41a51dSDavid Daney 		return err;
948cf41a51dSDavid Daney 
94990600732SDavid Daney 	/* Reset address */
95052295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
95190600732SDavid Daney 	if (err < 0)
95290600732SDavid Daney 		return err;
95390600732SDavid Daney 
95434386344SAndrew Lunn 	return genphy_soft_reset(phydev);
95590600732SDavid Daney }
95690600732SDavid Daney 
957e1dde8dcSAndrew Lunn static int m88e1145_config_init_rgmii(struct phy_device *phydev)
95876884679SAndy Fleming {
95976884679SAndy Fleming 	int err;
960e69d9ed4SAndrew Lunn 
96161111598SAndrew Lunn 	err = m88e1111_config_init_rgmii_delays(phydev);
96276884679SAndy Fleming 	if (err < 0)
96376884679SAndy Fleming 		return err;
96476884679SAndy Fleming 
9652f495c39SBenjamin Herrenschmidt 	if (phydev->dev_flags & MARVELL_PHY_M1145_FLAGS_RESISTANCE) {
96676884679SAndy Fleming 		err = phy_write(phydev, 0x1d, 0x0012);
96776884679SAndy Fleming 		if (err < 0)
96876884679SAndy Fleming 			return err;
96976884679SAndy Fleming 
970f102852fSRussell King 		err = phy_modify(phydev, 0x1e, 0x0fc0,
971fea23fb5SRussell King 				 2 << 9 | /* 36 ohm */
972fea23fb5SRussell King 				 2 << 6); /* 39 ohm */
97376884679SAndy Fleming 		if (err < 0)
97476884679SAndy Fleming 			return err;
97576884679SAndy Fleming 
97676884679SAndy Fleming 		err = phy_write(phydev, 0x1d, 0x3);
97776884679SAndy Fleming 		if (err < 0)
97876884679SAndy Fleming 			return err;
97976884679SAndy Fleming 
98076884679SAndy Fleming 		err = phy_write(phydev, 0x1e, 0x8000);
981e1dde8dcSAndrew Lunn 	}
98276884679SAndy Fleming 	return err;
98376884679SAndy Fleming }
98476884679SAndy Fleming 
985e1dde8dcSAndrew Lunn static int m88e1145_config_init_sgmii(struct phy_device *phydev)
986e1dde8dcSAndrew Lunn {
987865b813aSAndrew Lunn 	return m88e1111_config_init_hwcfg_mode(
988865b813aSAndrew Lunn 		phydev, MII_M1111_HWCFG_MODE_SGMII_NO_CLK,
989865b813aSAndrew Lunn 		MII_M1111_HWCFG_FIBER_COPPER_AUTO);
990e1dde8dcSAndrew Lunn }
991e1dde8dcSAndrew Lunn 
992e1dde8dcSAndrew Lunn static int m88e1145_config_init(struct phy_device *phydev)
993e1dde8dcSAndrew Lunn {
994e1dde8dcSAndrew Lunn 	int err;
995e1dde8dcSAndrew Lunn 
996e1dde8dcSAndrew Lunn 	/* Take care of errata E0 & E1 */
997e1dde8dcSAndrew Lunn 	err = phy_write(phydev, 0x1d, 0x001b);
998e1dde8dcSAndrew Lunn 	if (err < 0)
999e1dde8dcSAndrew Lunn 		return err;
1000e1dde8dcSAndrew Lunn 
1001e1dde8dcSAndrew Lunn 	err = phy_write(phydev, 0x1e, 0x418f);
1002e1dde8dcSAndrew Lunn 	if (err < 0)
1003e1dde8dcSAndrew Lunn 		return err;
1004e1dde8dcSAndrew Lunn 
1005e1dde8dcSAndrew Lunn 	err = phy_write(phydev, 0x1d, 0x0016);
1006e1dde8dcSAndrew Lunn 	if (err < 0)
1007e1dde8dcSAndrew Lunn 		return err;
1008e1dde8dcSAndrew Lunn 
1009e1dde8dcSAndrew Lunn 	err = phy_write(phydev, 0x1e, 0xa2da);
1010e1dde8dcSAndrew Lunn 	if (err < 0)
1011e1dde8dcSAndrew Lunn 		return err;
1012e1dde8dcSAndrew Lunn 
1013e1dde8dcSAndrew Lunn 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
1014e1dde8dcSAndrew Lunn 		err = m88e1145_config_init_rgmii(phydev);
1015e1dde8dcSAndrew Lunn 		if (err < 0)
1016e1dde8dcSAndrew Lunn 			return err;
1017e1dde8dcSAndrew Lunn 	}
1018e1dde8dcSAndrew Lunn 
1019e1dde8dcSAndrew Lunn 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
1020e1dde8dcSAndrew Lunn 		err = m88e1145_config_init_sgmii(phydev);
1021b0224175SViet Nga Dao 		if (err < 0)
1022b0224175SViet Nga Dao 			return err;
1023b0224175SViet Nga Dao 	}
1024b0224175SViet Nga Dao 
1025cf41a51dSDavid Daney 	err = marvell_of_reg_init(phydev);
1026cf41a51dSDavid Daney 	if (err < 0)
1027cf41a51dSDavid Daney 		return err;
1028cf41a51dSDavid Daney 
102976884679SAndy Fleming 	return 0;
103076884679SAndy Fleming }
103100db8189SAndy Fleming 
10326cfb3bccSCharles-Antoine Couret /**
10336cfb3bccSCharles-Antoine Couret  * fiber_lpa_to_ethtool_lpa_t
10346cfb3bccSCharles-Antoine Couret  * @lpa: value of the MII_LPA register for fiber link
1035be937f1fSAlexandr Smirnov  *
10366cfb3bccSCharles-Antoine Couret  * A small helper function that translates MII_LPA
10376cfb3bccSCharles-Antoine Couret  * bits to ethtool LP advertisement settings.
10386cfb3bccSCharles-Antoine Couret  */
10396cfb3bccSCharles-Antoine Couret static u32 fiber_lpa_to_ethtool_lpa_t(u32 lpa)
10406cfb3bccSCharles-Antoine Couret {
10416cfb3bccSCharles-Antoine Couret 	u32 result = 0;
10426cfb3bccSCharles-Antoine Couret 
10436cfb3bccSCharles-Antoine Couret 	if (lpa & LPA_FIBER_1000HALF)
10446cfb3bccSCharles-Antoine Couret 		result |= ADVERTISED_1000baseT_Half;
10456cfb3bccSCharles-Antoine Couret 	if (lpa & LPA_FIBER_1000FULL)
10466cfb3bccSCharles-Antoine Couret 		result |= ADVERTISED_1000baseT_Full;
10476cfb3bccSCharles-Antoine Couret 
10486cfb3bccSCharles-Antoine Couret 	return result;
10496cfb3bccSCharles-Antoine Couret }
10506cfb3bccSCharles-Antoine Couret 
10516cfb3bccSCharles-Antoine Couret /**
10526cfb3bccSCharles-Antoine Couret  * marvell_update_link - update link status in real time in @phydev
10536cfb3bccSCharles-Antoine Couret  * @phydev: target phy_device struct
10546cfb3bccSCharles-Antoine Couret  *
10556cfb3bccSCharles-Antoine Couret  * Description: Update the value in phydev->link to reflect the
10566cfb3bccSCharles-Antoine Couret  *   current link value.
10576cfb3bccSCharles-Antoine Couret  */
10586cfb3bccSCharles-Antoine Couret static int marvell_update_link(struct phy_device *phydev, int fiber)
10596cfb3bccSCharles-Antoine Couret {
10606cfb3bccSCharles-Antoine Couret 	int status;
10616cfb3bccSCharles-Antoine Couret 
10626cfb3bccSCharles-Antoine Couret 	/* Use the generic register for copper link, or specific
10630c3439bcSAndrew Lunn 	 * register for fiber case
10640c3439bcSAndrew Lunn 	 */
10656cfb3bccSCharles-Antoine Couret 	if (fiber) {
10666cfb3bccSCharles-Antoine Couret 		status = phy_read(phydev, MII_M1011_PHY_STATUS);
10676cfb3bccSCharles-Antoine Couret 		if (status < 0)
10686cfb3bccSCharles-Antoine Couret 			return status;
10696cfb3bccSCharles-Antoine Couret 
10706cfb3bccSCharles-Antoine Couret 		if ((status & REGISTER_LINK_STATUS) == 0)
10716cfb3bccSCharles-Antoine Couret 			phydev->link = 0;
10726cfb3bccSCharles-Antoine Couret 		else
10736cfb3bccSCharles-Antoine Couret 			phydev->link = 1;
10746cfb3bccSCharles-Antoine Couret 	} else {
10756cfb3bccSCharles-Antoine Couret 		return genphy_update_link(phydev);
10766cfb3bccSCharles-Antoine Couret 	}
10776cfb3bccSCharles-Antoine Couret 
10786cfb3bccSCharles-Antoine Couret 	return 0;
10796cfb3bccSCharles-Antoine Couret }
10806cfb3bccSCharles-Antoine Couret 
1081e1dde8dcSAndrew Lunn static int marvell_read_status_page_an(struct phy_device *phydev,
1082e1dde8dcSAndrew Lunn 				       int fiber)
1083be937f1fSAlexandr Smirnov {
1084e1dde8dcSAndrew Lunn 	int status;
1085be937f1fSAlexandr Smirnov 	int lpa;
1086357cd64cSRussell King 	int lpagb;
1087be937f1fSAlexandr Smirnov 
1088be937f1fSAlexandr Smirnov 	status = phy_read(phydev, MII_M1011_PHY_STATUS);
1089be937f1fSAlexandr Smirnov 	if (status < 0)
1090be937f1fSAlexandr Smirnov 		return status;
1091be937f1fSAlexandr Smirnov 
1092be937f1fSAlexandr Smirnov 	lpa = phy_read(phydev, MII_LPA);
1093be937f1fSAlexandr Smirnov 	if (lpa < 0)
1094be937f1fSAlexandr Smirnov 		return lpa;
1095be937f1fSAlexandr Smirnov 
1096357cd64cSRussell King 	lpagb = phy_read(phydev, MII_STAT1000);
1097357cd64cSRussell King 	if (lpagb < 0)
1098357cd64cSRussell King 		return lpagb;
1099357cd64cSRussell King 
1100be937f1fSAlexandr Smirnov 	if (status & MII_M1011_PHY_STATUS_FULLDUPLEX)
1101be937f1fSAlexandr Smirnov 		phydev->duplex = DUPLEX_FULL;
1102be937f1fSAlexandr Smirnov 	else
1103be937f1fSAlexandr Smirnov 		phydev->duplex = DUPLEX_HALF;
1104be937f1fSAlexandr Smirnov 
1105be937f1fSAlexandr Smirnov 	status = status & MII_M1011_PHY_STATUS_SPD_MASK;
11064f48ed32SAndrew Lunn 	phydev->pause = 0;
11074f48ed32SAndrew Lunn 	phydev->asym_pause = 0;
1108be937f1fSAlexandr Smirnov 
1109be937f1fSAlexandr Smirnov 	switch (status) {
1110be937f1fSAlexandr Smirnov 	case MII_M1011_PHY_STATUS_1000:
1111be937f1fSAlexandr Smirnov 		phydev->speed = SPEED_1000;
1112be937f1fSAlexandr Smirnov 		break;
1113be937f1fSAlexandr Smirnov 
1114be937f1fSAlexandr Smirnov 	case MII_M1011_PHY_STATUS_100:
1115be937f1fSAlexandr Smirnov 		phydev->speed = SPEED_100;
1116be937f1fSAlexandr Smirnov 		break;
1117be937f1fSAlexandr Smirnov 
1118be937f1fSAlexandr Smirnov 	default:
1119be937f1fSAlexandr Smirnov 		phydev->speed = SPEED_10;
1120be937f1fSAlexandr Smirnov 		break;
1121be937f1fSAlexandr Smirnov 	}
1122be937f1fSAlexandr Smirnov 
11236cfb3bccSCharles-Antoine Couret 	if (!fiber) {
1124e1dde8dcSAndrew Lunn 		phydev->lp_advertising =
1125e1dde8dcSAndrew Lunn 			mii_stat1000_to_ethtool_lpa_t(lpagb) |
11266cfb3bccSCharles-Antoine Couret 			mii_lpa_to_ethtool_lpa_t(lpa);
11276cfb3bccSCharles-Antoine Couret 
1128be937f1fSAlexandr Smirnov 		if (phydev->duplex == DUPLEX_FULL) {
1129be937f1fSAlexandr Smirnov 			phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0;
1130be937f1fSAlexandr Smirnov 			phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0;
1131be937f1fSAlexandr Smirnov 		}
1132be937f1fSAlexandr Smirnov 	} else {
11336cfb3bccSCharles-Antoine Couret 		/* The fiber link is only 1000M capable */
11346cfb3bccSCharles-Antoine Couret 		phydev->lp_advertising = fiber_lpa_to_ethtool_lpa_t(lpa);
11356cfb3bccSCharles-Antoine Couret 
11366cfb3bccSCharles-Antoine Couret 		if (phydev->duplex == DUPLEX_FULL) {
11376cfb3bccSCharles-Antoine Couret 			if (!(lpa & LPA_PAUSE_FIBER)) {
11386cfb3bccSCharles-Antoine Couret 				phydev->pause = 0;
11396cfb3bccSCharles-Antoine Couret 				phydev->asym_pause = 0;
11406cfb3bccSCharles-Antoine Couret 			} else if ((lpa & LPA_PAUSE_ASYM_FIBER)) {
11416cfb3bccSCharles-Antoine Couret 				phydev->pause = 1;
11426cfb3bccSCharles-Antoine Couret 				phydev->asym_pause = 1;
11436cfb3bccSCharles-Antoine Couret 			} else {
11446cfb3bccSCharles-Antoine Couret 				phydev->pause = 1;
11456cfb3bccSCharles-Antoine Couret 				phydev->asym_pause = 0;
11466cfb3bccSCharles-Antoine Couret 			}
11476cfb3bccSCharles-Antoine Couret 		}
11486cfb3bccSCharles-Antoine Couret 	}
1149e1dde8dcSAndrew Lunn 	return 0;
1150e1dde8dcSAndrew Lunn }
1151e1dde8dcSAndrew Lunn 
1152e1dde8dcSAndrew Lunn static int marvell_read_status_page_fixed(struct phy_device *phydev)
1153e1dde8dcSAndrew Lunn {
1154be937f1fSAlexandr Smirnov 	int bmcr = phy_read(phydev, MII_BMCR);
1155be937f1fSAlexandr Smirnov 
1156be937f1fSAlexandr Smirnov 	if (bmcr < 0)
1157be937f1fSAlexandr Smirnov 		return bmcr;
1158be937f1fSAlexandr Smirnov 
1159be937f1fSAlexandr Smirnov 	if (bmcr & BMCR_FULLDPLX)
1160be937f1fSAlexandr Smirnov 		phydev->duplex = DUPLEX_FULL;
1161be937f1fSAlexandr Smirnov 	else
1162be937f1fSAlexandr Smirnov 		phydev->duplex = DUPLEX_HALF;
1163be937f1fSAlexandr Smirnov 
1164be937f1fSAlexandr Smirnov 	if (bmcr & BMCR_SPEED1000)
1165be937f1fSAlexandr Smirnov 		phydev->speed = SPEED_1000;
1166be937f1fSAlexandr Smirnov 	else if (bmcr & BMCR_SPEED100)
1167be937f1fSAlexandr Smirnov 		phydev->speed = SPEED_100;
1168be937f1fSAlexandr Smirnov 	else
1169be937f1fSAlexandr Smirnov 		phydev->speed = SPEED_10;
1170be937f1fSAlexandr Smirnov 
11714f48ed32SAndrew Lunn 	phydev->pause = 0;
11724f48ed32SAndrew Lunn 	phydev->asym_pause = 0;
1173357cd64cSRussell King 	phydev->lp_advertising = 0;
1174be937f1fSAlexandr Smirnov 
1175be937f1fSAlexandr Smirnov 	return 0;
1176be937f1fSAlexandr Smirnov }
1177be937f1fSAlexandr Smirnov 
1178e1dde8dcSAndrew Lunn /* marvell_read_status_page
1179e1dde8dcSAndrew Lunn  *
1180e1dde8dcSAndrew Lunn  * Description:
1181e1dde8dcSAndrew Lunn  *   Check the link, then figure out the current state
1182e1dde8dcSAndrew Lunn  *   by comparing what we advertise with what the link partner
1183e1dde8dcSAndrew Lunn  *   advertises.  Start by checking the gigabit possibilities,
1184e1dde8dcSAndrew Lunn  *   then move on to 10/100.
1185e1dde8dcSAndrew Lunn  */
1186e1dde8dcSAndrew Lunn static int marvell_read_status_page(struct phy_device *phydev, int page)
1187e1dde8dcSAndrew Lunn {
1188e1dde8dcSAndrew Lunn 	int fiber;
1189e1dde8dcSAndrew Lunn 	int err;
1190e1dde8dcSAndrew Lunn 
1191e1dde8dcSAndrew Lunn 	/* Detect and update the link, but return if there
1192e1dde8dcSAndrew Lunn 	 * was an error
1193e1dde8dcSAndrew Lunn 	 */
119452295666SAndrew Lunn 	if (page == MII_MARVELL_FIBER_PAGE)
1195e1dde8dcSAndrew Lunn 		fiber = 1;
1196e1dde8dcSAndrew Lunn 	else
1197e1dde8dcSAndrew Lunn 		fiber = 0;
1198e1dde8dcSAndrew Lunn 
1199e1dde8dcSAndrew Lunn 	err = marvell_update_link(phydev, fiber);
1200e1dde8dcSAndrew Lunn 	if (err)
1201e1dde8dcSAndrew Lunn 		return err;
1202e1dde8dcSAndrew Lunn 
1203e1dde8dcSAndrew Lunn 	if (phydev->autoneg == AUTONEG_ENABLE)
1204e1dde8dcSAndrew Lunn 		err = marvell_read_status_page_an(phydev, fiber);
1205e1dde8dcSAndrew Lunn 	else
1206e1dde8dcSAndrew Lunn 		err = marvell_read_status_page_fixed(phydev);
1207e1dde8dcSAndrew Lunn 
1208e1dde8dcSAndrew Lunn 	return err;
1209e1dde8dcSAndrew Lunn }
1210e1dde8dcSAndrew Lunn 
12116cfb3bccSCharles-Antoine Couret /* marvell_read_status
12126cfb3bccSCharles-Antoine Couret  *
12136cfb3bccSCharles-Antoine Couret  * Some Marvell's phys have two modes: fiber and copper.
12146cfb3bccSCharles-Antoine Couret  * Both need status checked.
12156cfb3bccSCharles-Antoine Couret  * Description:
12166cfb3bccSCharles-Antoine Couret  *   First, check the fiber link and status.
12176cfb3bccSCharles-Antoine Couret  *   If the fiber link is down, check the copper link and status which
12186cfb3bccSCharles-Antoine Couret  *   will be the default value if both link are down.
12196cfb3bccSCharles-Antoine Couret  */
12206cfb3bccSCharles-Antoine Couret static int marvell_read_status(struct phy_device *phydev)
12216cfb3bccSCharles-Antoine Couret {
12226cfb3bccSCharles-Antoine Couret 	int err;
12236cfb3bccSCharles-Antoine Couret 
12246cfb3bccSCharles-Antoine Couret 	/* Check the fiber mode first */
1225a13c0652SRussell King 	if (phydev->supported & SUPPORTED_FIBRE &&
1226a13c0652SRussell King 	    phydev->interface != PHY_INTERFACE_MODE_SGMII) {
122752295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
12286cfb3bccSCharles-Antoine Couret 		if (err < 0)
12296cfb3bccSCharles-Antoine Couret 			goto error;
12306cfb3bccSCharles-Antoine Couret 
123152295666SAndrew Lunn 		err = marvell_read_status_page(phydev, MII_MARVELL_FIBER_PAGE);
12326cfb3bccSCharles-Antoine Couret 		if (err < 0)
12336cfb3bccSCharles-Antoine Couret 			goto error;
12346cfb3bccSCharles-Antoine Couret 
12350c3439bcSAndrew Lunn 		/* If the fiber link is up, it is the selected and
12360c3439bcSAndrew Lunn 		 * used link. In this case, we need to stay in the
12370c3439bcSAndrew Lunn 		 * fiber page. Please to be careful about that, avoid
12380c3439bcSAndrew Lunn 		 * to restore Copper page in other functions which
12390c3439bcSAndrew Lunn 		 * could break the behaviour for some fiber phy like
12400c3439bcSAndrew Lunn 		 * 88E1512.
12410c3439bcSAndrew Lunn 		 */
12426cfb3bccSCharles-Antoine Couret 		if (phydev->link)
12436cfb3bccSCharles-Antoine Couret 			return 0;
12446cfb3bccSCharles-Antoine Couret 
12456cfb3bccSCharles-Antoine Couret 		/* If fiber link is down, check and save copper mode state */
124652295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
12476cfb3bccSCharles-Antoine Couret 		if (err < 0)
12486cfb3bccSCharles-Antoine Couret 			goto error;
12496cfb3bccSCharles-Antoine Couret 	}
12506cfb3bccSCharles-Antoine Couret 
125152295666SAndrew Lunn 	return marvell_read_status_page(phydev, MII_MARVELL_COPPER_PAGE);
12526cfb3bccSCharles-Antoine Couret 
12536cfb3bccSCharles-Antoine Couret error:
125452295666SAndrew Lunn 	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
12556cfb3bccSCharles-Antoine Couret 	return err;
12566cfb3bccSCharles-Antoine Couret }
12573758be3dSCharles-Antoine Couret 
12583758be3dSCharles-Antoine Couret /* marvell_suspend
12593758be3dSCharles-Antoine Couret  *
12603758be3dSCharles-Antoine Couret  * Some Marvell's phys have two modes: fiber and copper.
12613758be3dSCharles-Antoine Couret  * Both need to be suspended
12623758be3dSCharles-Antoine Couret  */
12633758be3dSCharles-Antoine Couret static int marvell_suspend(struct phy_device *phydev)
12643758be3dSCharles-Antoine Couret {
12653758be3dSCharles-Antoine Couret 	int err;
12663758be3dSCharles-Antoine Couret 
12673758be3dSCharles-Antoine Couret 	/* Suspend the fiber mode first */
12683758be3dSCharles-Antoine Couret 	if (!(phydev->supported & SUPPORTED_FIBRE)) {
126952295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
12703758be3dSCharles-Antoine Couret 		if (err < 0)
12713758be3dSCharles-Antoine Couret 			goto error;
12723758be3dSCharles-Antoine Couret 
12733758be3dSCharles-Antoine Couret 		/* With the page set, use the generic suspend */
12743758be3dSCharles-Antoine Couret 		err = genphy_suspend(phydev);
12753758be3dSCharles-Antoine Couret 		if (err < 0)
12763758be3dSCharles-Antoine Couret 			goto error;
12773758be3dSCharles-Antoine Couret 
12783758be3dSCharles-Antoine Couret 		/* Then, the copper link */
127952295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
12803758be3dSCharles-Antoine Couret 		if (err < 0)
12813758be3dSCharles-Antoine Couret 			goto error;
12823758be3dSCharles-Antoine Couret 	}
12833758be3dSCharles-Antoine Couret 
12843758be3dSCharles-Antoine Couret 	/* With the page set, use the generic suspend */
12853758be3dSCharles-Antoine Couret 	return genphy_suspend(phydev);
12863758be3dSCharles-Antoine Couret 
12873758be3dSCharles-Antoine Couret error:
128852295666SAndrew Lunn 	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
12893758be3dSCharles-Antoine Couret 	return err;
12903758be3dSCharles-Antoine Couret }
12913758be3dSCharles-Antoine Couret 
12923758be3dSCharles-Antoine Couret /* marvell_resume
12933758be3dSCharles-Antoine Couret  *
12943758be3dSCharles-Antoine Couret  * Some Marvell's phys have two modes: fiber and copper.
12953758be3dSCharles-Antoine Couret  * Both need to be resumed
12963758be3dSCharles-Antoine Couret  */
12973758be3dSCharles-Antoine Couret static int marvell_resume(struct phy_device *phydev)
12983758be3dSCharles-Antoine Couret {
12993758be3dSCharles-Antoine Couret 	int err;
13003758be3dSCharles-Antoine Couret 
13013758be3dSCharles-Antoine Couret 	/* Resume the fiber mode first */
13023758be3dSCharles-Antoine Couret 	if (!(phydev->supported & SUPPORTED_FIBRE)) {
130352295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
13043758be3dSCharles-Antoine Couret 		if (err < 0)
13053758be3dSCharles-Antoine Couret 			goto error;
13063758be3dSCharles-Antoine Couret 
13073758be3dSCharles-Antoine Couret 		/* With the page set, use the generic resume */
13083758be3dSCharles-Antoine Couret 		err = genphy_resume(phydev);
13093758be3dSCharles-Antoine Couret 		if (err < 0)
13103758be3dSCharles-Antoine Couret 			goto error;
13113758be3dSCharles-Antoine Couret 
13123758be3dSCharles-Antoine Couret 		/* Then, the copper link */
131352295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
13143758be3dSCharles-Antoine Couret 		if (err < 0)
13153758be3dSCharles-Antoine Couret 			goto error;
13163758be3dSCharles-Antoine Couret 	}
13173758be3dSCharles-Antoine Couret 
13183758be3dSCharles-Antoine Couret 	/* With the page set, use the generic resume */
13193758be3dSCharles-Antoine Couret 	return genphy_resume(phydev);
13203758be3dSCharles-Antoine Couret 
13213758be3dSCharles-Antoine Couret error:
132252295666SAndrew Lunn 	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
13233758be3dSCharles-Antoine Couret 	return err;
13243758be3dSCharles-Antoine Couret }
13253758be3dSCharles-Antoine Couret 
13266b358aedSSebastian Hesselbarth static int marvell_aneg_done(struct phy_device *phydev)
13276b358aedSSebastian Hesselbarth {
13286b358aedSSebastian Hesselbarth 	int retval = phy_read(phydev, MII_M1011_PHY_STATUS);
1329e69d9ed4SAndrew Lunn 
13306b358aedSSebastian Hesselbarth 	return (retval < 0) ? retval : (retval & MII_M1011_PHY_STATUS_RESOLVED);
13316b358aedSSebastian Hesselbarth }
13326b358aedSSebastian Hesselbarth 
1333dcd07be3SAnatolij Gustschin static int m88e1121_did_interrupt(struct phy_device *phydev)
1334dcd07be3SAnatolij Gustschin {
1335dcd07be3SAnatolij Gustschin 	int imask;
1336dcd07be3SAnatolij Gustschin 
1337dcd07be3SAnatolij Gustschin 	imask = phy_read(phydev, MII_M1011_IEVENT);
1338dcd07be3SAnatolij Gustschin 
1339dcd07be3SAnatolij Gustschin 	if (imask & MII_M1011_IMASK_INIT)
1340dcd07be3SAnatolij Gustschin 		return 1;
1341dcd07be3SAnatolij Gustschin 
1342dcd07be3SAnatolij Gustschin 	return 0;
1343dcd07be3SAnatolij Gustschin }
1344dcd07be3SAnatolij Gustschin 
134523beb38fSAndrew Lunn static void m88e1318_get_wol(struct phy_device *phydev,
134623beb38fSAndrew Lunn 			     struct ethtool_wolinfo *wol)
13473871c387SMichael Stapelberg {
1348424ca4c5SRussell King 	int oldpage, ret = 0;
1349424ca4c5SRussell King 
13503871c387SMichael Stapelberg 	wol->supported = WAKE_MAGIC;
13513871c387SMichael Stapelberg 	wol->wolopts = 0;
13523871c387SMichael Stapelberg 
1353424ca4c5SRussell King 	oldpage = phy_select_page(phydev, MII_MARVELL_WOL_PAGE);
1354424ca4c5SRussell King 	if (oldpage < 0)
1355424ca4c5SRussell King 		goto error;
13563871c387SMichael Stapelberg 
1357424ca4c5SRussell King 	ret = __phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL);
1358424ca4c5SRussell King 	if (ret & MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE)
13593871c387SMichael Stapelberg 		wol->wolopts |= WAKE_MAGIC;
13603871c387SMichael Stapelberg 
1361424ca4c5SRussell King error:
1362424ca4c5SRussell King 	phy_restore_page(phydev, oldpage, ret);
13633871c387SMichael Stapelberg }
13643871c387SMichael Stapelberg 
136523beb38fSAndrew Lunn static int m88e1318_set_wol(struct phy_device *phydev,
136623beb38fSAndrew Lunn 			    struct ethtool_wolinfo *wol)
13673871c387SMichael Stapelberg {
1368424ca4c5SRussell King 	int err = 0, oldpage;
13693871c387SMichael Stapelberg 
1370424ca4c5SRussell King 	oldpage = phy_save_page(phydev);
1371424ca4c5SRussell King 	if (oldpage < 0)
1372424ca4c5SRussell King 		goto error;
13733871c387SMichael Stapelberg 
13743871c387SMichael Stapelberg 	if (wol->wolopts & WAKE_MAGIC) {
13753871c387SMichael Stapelberg 		/* Explicitly switch to page 0x00, just to be sure */
1376424ca4c5SRussell King 		err = marvell_write_page(phydev, MII_MARVELL_COPPER_PAGE);
13773871c387SMichael Stapelberg 		if (err < 0)
1378424ca4c5SRussell King 			goto error;
13793871c387SMichael Stapelberg 
13803871c387SMichael Stapelberg 		/* Enable the WOL interrupt */
1381424ca4c5SRussell King 		err = __phy_modify(phydev, MII_88E1318S_PHY_CSIER, 0,
1382424ca4c5SRussell King 				   MII_88E1318S_PHY_CSIER_WOL_EIE);
13833871c387SMichael Stapelberg 		if (err < 0)
1384424ca4c5SRussell King 			goto error;
13853871c387SMichael Stapelberg 
1386424ca4c5SRussell King 		err = marvell_write_page(phydev, MII_MARVELL_LED_PAGE);
13873871c387SMichael Stapelberg 		if (err < 0)
1388424ca4c5SRussell King 			goto error;
13893871c387SMichael Stapelberg 
13903871c387SMichael Stapelberg 		/* Setup LED[2] as interrupt pin (active low) */
1391424ca4c5SRussell King 		err = __phy_modify(phydev, MII_88E1318S_PHY_LED_TCR,
1392f102852fSRussell King 				   MII_88E1318S_PHY_LED_TCR_FORCE_INT,
1393424ca4c5SRussell King 				   MII_88E1318S_PHY_LED_TCR_INTn_ENABLE |
1394424ca4c5SRussell King 				   MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW);
13953871c387SMichael Stapelberg 		if (err < 0)
1396424ca4c5SRussell King 			goto error;
13973871c387SMichael Stapelberg 
1398424ca4c5SRussell King 		err = marvell_write_page(phydev, MII_MARVELL_WOL_PAGE);
13993871c387SMichael Stapelberg 		if (err < 0)
1400424ca4c5SRussell King 			goto error;
14013871c387SMichael Stapelberg 
14023871c387SMichael Stapelberg 		/* Store the device address for the magic packet */
1403424ca4c5SRussell King 		err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD2,
14043871c387SMichael Stapelberg 				((phydev->attached_dev->dev_addr[5] << 8) |
14053871c387SMichael Stapelberg 				 phydev->attached_dev->dev_addr[4]));
14063871c387SMichael Stapelberg 		if (err < 0)
1407424ca4c5SRussell King 			goto error;
1408424ca4c5SRussell King 		err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD1,
14093871c387SMichael Stapelberg 				((phydev->attached_dev->dev_addr[3] << 8) |
14103871c387SMichael Stapelberg 				 phydev->attached_dev->dev_addr[2]));
14113871c387SMichael Stapelberg 		if (err < 0)
1412424ca4c5SRussell King 			goto error;
1413424ca4c5SRussell King 		err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD0,
14143871c387SMichael Stapelberg 				((phydev->attached_dev->dev_addr[1] << 8) |
14153871c387SMichael Stapelberg 				 phydev->attached_dev->dev_addr[0]));
14163871c387SMichael Stapelberg 		if (err < 0)
1417424ca4c5SRussell King 			goto error;
14183871c387SMichael Stapelberg 
14193871c387SMichael Stapelberg 		/* Clear WOL status and enable magic packet matching */
1420424ca4c5SRussell King 		err = __phy_modify(phydev, MII_88E1318S_PHY_WOL_CTRL, 0,
1421424ca4c5SRussell King 				   MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS |
1422424ca4c5SRussell King 				   MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE);
14233871c387SMichael Stapelberg 		if (err < 0)
1424424ca4c5SRussell King 			goto error;
14253871c387SMichael Stapelberg 	} else {
1426424ca4c5SRussell King 		err = marvell_write_page(phydev, MII_MARVELL_WOL_PAGE);
14273871c387SMichael Stapelberg 		if (err < 0)
1428424ca4c5SRussell King 			goto error;
14293871c387SMichael Stapelberg 
14303871c387SMichael Stapelberg 		/* Clear WOL status and disable magic packet matching */
1431424ca4c5SRussell King 		err = __phy_modify(phydev, MII_88E1318S_PHY_WOL_CTRL,
1432f102852fSRussell King 				   MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE,
1433424ca4c5SRussell King 				   MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS);
14343871c387SMichael Stapelberg 		if (err < 0)
1435424ca4c5SRussell King 			goto error;
14363871c387SMichael Stapelberg 	}
14373871c387SMichael Stapelberg 
1438424ca4c5SRussell King error:
1439424ca4c5SRussell King 	return phy_restore_page(phydev, oldpage, err);
14403871c387SMichael Stapelberg }
14413871c387SMichael Stapelberg 
1442d2fa47d9SAndrew Lunn static int marvell_get_sset_count(struct phy_device *phydev)
1443d2fa47d9SAndrew Lunn {
14442170fef7SCharles-Antoine Couret 	if (phydev->supported & SUPPORTED_FIBRE)
1445d2fa47d9SAndrew Lunn 		return ARRAY_SIZE(marvell_hw_stats);
14462170fef7SCharles-Antoine Couret 	else
14472170fef7SCharles-Antoine Couret 		return ARRAY_SIZE(marvell_hw_stats) - NB_FIBER_STATS;
1448d2fa47d9SAndrew Lunn }
1449d2fa47d9SAndrew Lunn 
1450d2fa47d9SAndrew Lunn static void marvell_get_strings(struct phy_device *phydev, u8 *data)
1451d2fa47d9SAndrew Lunn {
1452d2fa47d9SAndrew Lunn 	int i;
1453d2fa47d9SAndrew Lunn 
1454d2fa47d9SAndrew Lunn 	for (i = 0; i < ARRAY_SIZE(marvell_hw_stats); i++) {
145598409b2bSFlorian Fainelli 		strlcpy(data + i * ETH_GSTRING_LEN,
1456d2fa47d9SAndrew Lunn 			marvell_hw_stats[i].string, ETH_GSTRING_LEN);
1457d2fa47d9SAndrew Lunn 	}
1458d2fa47d9SAndrew Lunn }
1459d2fa47d9SAndrew Lunn 
1460d2fa47d9SAndrew Lunn #ifndef UINT64_MAX
1461d2fa47d9SAndrew Lunn #define UINT64_MAX		(u64)(~((u64)0))
1462d2fa47d9SAndrew Lunn #endif
1463d2fa47d9SAndrew Lunn static u64 marvell_get_stat(struct phy_device *phydev, int i)
1464d2fa47d9SAndrew Lunn {
1465d2fa47d9SAndrew Lunn 	struct marvell_hw_stat stat = marvell_hw_stats[i];
1466d2fa47d9SAndrew Lunn 	struct marvell_priv *priv = phydev->priv;
1467424ca4c5SRussell King 	int val;
1468321b4d4bSAndrew Lunn 	u64 ret;
1469d2fa47d9SAndrew Lunn 
1470424ca4c5SRussell King 	val = phy_read_paged(phydev, stat.page, stat.reg);
1471d2fa47d9SAndrew Lunn 	if (val < 0) {
1472321b4d4bSAndrew Lunn 		ret = UINT64_MAX;
1473d2fa47d9SAndrew Lunn 	} else {
1474d2fa47d9SAndrew Lunn 		val = val & ((1 << stat.bits) - 1);
1475d2fa47d9SAndrew Lunn 		priv->stats[i] += val;
1476321b4d4bSAndrew Lunn 		ret = priv->stats[i];
1477d2fa47d9SAndrew Lunn 	}
1478d2fa47d9SAndrew Lunn 
1479321b4d4bSAndrew Lunn 	return ret;
1480d2fa47d9SAndrew Lunn }
1481d2fa47d9SAndrew Lunn 
1482d2fa47d9SAndrew Lunn static void marvell_get_stats(struct phy_device *phydev,
1483d2fa47d9SAndrew Lunn 			      struct ethtool_stats *stats, u64 *data)
1484d2fa47d9SAndrew Lunn {
1485d2fa47d9SAndrew Lunn 	int i;
1486d2fa47d9SAndrew Lunn 
1487d2fa47d9SAndrew Lunn 	for (i = 0; i < ARRAY_SIZE(marvell_hw_stats); i++)
1488d2fa47d9SAndrew Lunn 		data[i] = marvell_get_stat(phydev, i);
1489d2fa47d9SAndrew Lunn }
1490d2fa47d9SAndrew Lunn 
14910b04680fSAndrew Lunn #ifdef CONFIG_HWMON
14920b04680fSAndrew Lunn static int m88e1121_get_temp(struct phy_device *phydev, long *temp)
14930b04680fSAndrew Lunn {
1494975b388cSAndrew Lunn 	int oldpage;
1495424ca4c5SRussell King 	int ret = 0;
14960b04680fSAndrew Lunn 	int val;
14970b04680fSAndrew Lunn 
14980b04680fSAndrew Lunn 	*temp = 0;
14990b04680fSAndrew Lunn 
1500424ca4c5SRussell King 	oldpage = phy_select_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
1501424ca4c5SRussell King 	if (oldpage < 0)
1502424ca4c5SRussell King 		goto error;
1503975b388cSAndrew Lunn 
15040b04680fSAndrew Lunn 	/* Enable temperature sensor */
1505424ca4c5SRussell King 	ret = __phy_read(phydev, MII_88E1121_MISC_TEST);
15060b04680fSAndrew Lunn 	if (ret < 0)
15070b04680fSAndrew Lunn 		goto error;
15080b04680fSAndrew Lunn 
1509424ca4c5SRussell King 	ret = __phy_write(phydev, MII_88E1121_MISC_TEST,
15100b04680fSAndrew Lunn 			  ret | MII_88E1121_MISC_TEST_TEMP_SENSOR_EN);
15110b04680fSAndrew Lunn 	if (ret < 0)
15120b04680fSAndrew Lunn 		goto error;
15130b04680fSAndrew Lunn 
15140b04680fSAndrew Lunn 	/* Wait for temperature to stabilize */
15150b04680fSAndrew Lunn 	usleep_range(10000, 12000);
15160b04680fSAndrew Lunn 
1517424ca4c5SRussell King 	val = __phy_read(phydev, MII_88E1121_MISC_TEST);
15180b04680fSAndrew Lunn 	if (val < 0) {
15190b04680fSAndrew Lunn 		ret = val;
15200b04680fSAndrew Lunn 		goto error;
15210b04680fSAndrew Lunn 	}
15220b04680fSAndrew Lunn 
15230b04680fSAndrew Lunn 	/* Disable temperature sensor */
1524424ca4c5SRussell King 	ret = __phy_write(phydev, MII_88E1121_MISC_TEST,
15250b04680fSAndrew Lunn 			  ret & ~MII_88E1121_MISC_TEST_TEMP_SENSOR_EN);
15260b04680fSAndrew Lunn 	if (ret < 0)
15270b04680fSAndrew Lunn 		goto error;
15280b04680fSAndrew Lunn 
15290b04680fSAndrew Lunn 	*temp = ((val & MII_88E1121_MISC_TEST_TEMP_MASK) - 5) * 5000;
15300b04680fSAndrew Lunn 
15310b04680fSAndrew Lunn error:
1532424ca4c5SRussell King 	return phy_restore_page(phydev, oldpage, ret);
15330b04680fSAndrew Lunn }
15340b04680fSAndrew Lunn 
15350b04680fSAndrew Lunn static int m88e1121_hwmon_read(struct device *dev,
15360b04680fSAndrew Lunn 			       enum hwmon_sensor_types type,
15370b04680fSAndrew Lunn 			       u32 attr, int channel, long *temp)
15380b04680fSAndrew Lunn {
15390b04680fSAndrew Lunn 	struct phy_device *phydev = dev_get_drvdata(dev);
15400b04680fSAndrew Lunn 	int err;
15410b04680fSAndrew Lunn 
15420b04680fSAndrew Lunn 	switch (attr) {
15430b04680fSAndrew Lunn 	case hwmon_temp_input:
15440b04680fSAndrew Lunn 		err = m88e1121_get_temp(phydev, temp);
15450b04680fSAndrew Lunn 		break;
15460b04680fSAndrew Lunn 	default:
15470b04680fSAndrew Lunn 		return -EOPNOTSUPP;
15480b04680fSAndrew Lunn 	}
15490b04680fSAndrew Lunn 
15500b04680fSAndrew Lunn 	return err;
15510b04680fSAndrew Lunn }
15520b04680fSAndrew Lunn 
15530b04680fSAndrew Lunn static umode_t m88e1121_hwmon_is_visible(const void *data,
15540b04680fSAndrew Lunn 					 enum hwmon_sensor_types type,
15550b04680fSAndrew Lunn 					 u32 attr, int channel)
15560b04680fSAndrew Lunn {
15570b04680fSAndrew Lunn 	if (type != hwmon_temp)
15580b04680fSAndrew Lunn 		return 0;
15590b04680fSAndrew Lunn 
15600b04680fSAndrew Lunn 	switch (attr) {
15610b04680fSAndrew Lunn 	case hwmon_temp_input:
15620b04680fSAndrew Lunn 		return 0444;
15630b04680fSAndrew Lunn 	default:
15640b04680fSAndrew Lunn 		return 0;
15650b04680fSAndrew Lunn 	}
15660b04680fSAndrew Lunn }
15670b04680fSAndrew Lunn 
15680b04680fSAndrew Lunn static u32 m88e1121_hwmon_chip_config[] = {
15690b04680fSAndrew Lunn 	HWMON_C_REGISTER_TZ,
15700b04680fSAndrew Lunn 	0
15710b04680fSAndrew Lunn };
15720b04680fSAndrew Lunn 
15730b04680fSAndrew Lunn static const struct hwmon_channel_info m88e1121_hwmon_chip = {
15740b04680fSAndrew Lunn 	.type = hwmon_chip,
15750b04680fSAndrew Lunn 	.config = m88e1121_hwmon_chip_config,
15760b04680fSAndrew Lunn };
15770b04680fSAndrew Lunn 
15780b04680fSAndrew Lunn static u32 m88e1121_hwmon_temp_config[] = {
15790b04680fSAndrew Lunn 	HWMON_T_INPUT,
15800b04680fSAndrew Lunn 	0
15810b04680fSAndrew Lunn };
15820b04680fSAndrew Lunn 
15830b04680fSAndrew Lunn static const struct hwmon_channel_info m88e1121_hwmon_temp = {
15840b04680fSAndrew Lunn 	.type = hwmon_temp,
15850b04680fSAndrew Lunn 	.config = m88e1121_hwmon_temp_config,
15860b04680fSAndrew Lunn };
15870b04680fSAndrew Lunn 
15880b04680fSAndrew Lunn static const struct hwmon_channel_info *m88e1121_hwmon_info[] = {
15890b04680fSAndrew Lunn 	&m88e1121_hwmon_chip,
15900b04680fSAndrew Lunn 	&m88e1121_hwmon_temp,
15910b04680fSAndrew Lunn 	NULL
15920b04680fSAndrew Lunn };
15930b04680fSAndrew Lunn 
15940b04680fSAndrew Lunn static const struct hwmon_ops m88e1121_hwmon_hwmon_ops = {
15950b04680fSAndrew Lunn 	.is_visible = m88e1121_hwmon_is_visible,
15960b04680fSAndrew Lunn 	.read = m88e1121_hwmon_read,
15970b04680fSAndrew Lunn };
15980b04680fSAndrew Lunn 
15990b04680fSAndrew Lunn static const struct hwmon_chip_info m88e1121_hwmon_chip_info = {
16000b04680fSAndrew Lunn 	.ops = &m88e1121_hwmon_hwmon_ops,
16010b04680fSAndrew Lunn 	.info = m88e1121_hwmon_info,
16020b04680fSAndrew Lunn };
16030b04680fSAndrew Lunn 
16040b04680fSAndrew Lunn static int m88e1510_get_temp(struct phy_device *phydev, long *temp)
16050b04680fSAndrew Lunn {
16060b04680fSAndrew Lunn 	int ret;
16070b04680fSAndrew Lunn 
16080b04680fSAndrew Lunn 	*temp = 0;
16090b04680fSAndrew Lunn 
1610424ca4c5SRussell King 	ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
1611424ca4c5SRussell King 			     MII_88E1510_TEMP_SENSOR);
16120b04680fSAndrew Lunn 	if (ret < 0)
1613424ca4c5SRussell King 		return ret;
16140b04680fSAndrew Lunn 
16150b04680fSAndrew Lunn 	*temp = ((ret & MII_88E1510_TEMP_SENSOR_MASK) - 25) * 1000;
16160b04680fSAndrew Lunn 
1617424ca4c5SRussell King 	return 0;
16180b04680fSAndrew Lunn }
16190b04680fSAndrew Lunn 
1620f0a45816SColin Ian King static int m88e1510_get_temp_critical(struct phy_device *phydev, long *temp)
16210b04680fSAndrew Lunn {
16220b04680fSAndrew Lunn 	int ret;
16230b04680fSAndrew Lunn 
16240b04680fSAndrew Lunn 	*temp = 0;
16250b04680fSAndrew Lunn 
1626424ca4c5SRussell King 	ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
1627424ca4c5SRussell King 			     MII_88E1121_MISC_TEST);
16280b04680fSAndrew Lunn 	if (ret < 0)
1629424ca4c5SRussell King 		return ret;
16300b04680fSAndrew Lunn 
16310b04680fSAndrew Lunn 	*temp = (((ret & MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK) >>
16320b04680fSAndrew Lunn 		  MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT) * 5) - 25;
16330b04680fSAndrew Lunn 	/* convert to mC */
16340b04680fSAndrew Lunn 	*temp *= 1000;
16350b04680fSAndrew Lunn 
1636424ca4c5SRussell King 	return 0;
16370b04680fSAndrew Lunn }
16380b04680fSAndrew Lunn 
1639f0a45816SColin Ian King static int m88e1510_set_temp_critical(struct phy_device *phydev, long temp)
16400b04680fSAndrew Lunn {
16410b04680fSAndrew Lunn 	temp = temp / 1000;
16420b04680fSAndrew Lunn 	temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
16430b04680fSAndrew Lunn 
1644424ca4c5SRussell King 	return phy_modify_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
1645424ca4c5SRussell King 				MII_88E1121_MISC_TEST,
1646424ca4c5SRussell King 				MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK,
1647424ca4c5SRussell King 				temp << MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT);
16480b04680fSAndrew Lunn }
16490b04680fSAndrew Lunn 
1650f0a45816SColin Ian King static int m88e1510_get_temp_alarm(struct phy_device *phydev, long *alarm)
16510b04680fSAndrew Lunn {
16520b04680fSAndrew Lunn 	int ret;
16530b04680fSAndrew Lunn 
16540b04680fSAndrew Lunn 	*alarm = false;
16550b04680fSAndrew Lunn 
1656424ca4c5SRussell King 	ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
1657424ca4c5SRussell King 			     MII_88E1121_MISC_TEST);
16580b04680fSAndrew Lunn 	if (ret < 0)
1659424ca4c5SRussell King 		return ret;
1660424ca4c5SRussell King 
16610b04680fSAndrew Lunn 	*alarm = !!(ret & MII_88E1510_MISC_TEST_TEMP_IRQ);
16620b04680fSAndrew Lunn 
1663424ca4c5SRussell King 	return 0;
16640b04680fSAndrew Lunn }
16650b04680fSAndrew Lunn 
16660b04680fSAndrew Lunn static int m88e1510_hwmon_read(struct device *dev,
16670b04680fSAndrew Lunn 			       enum hwmon_sensor_types type,
16680b04680fSAndrew Lunn 			       u32 attr, int channel, long *temp)
16690b04680fSAndrew Lunn {
16700b04680fSAndrew Lunn 	struct phy_device *phydev = dev_get_drvdata(dev);
16710b04680fSAndrew Lunn 	int err;
16720b04680fSAndrew Lunn 
16730b04680fSAndrew Lunn 	switch (attr) {
16740b04680fSAndrew Lunn 	case hwmon_temp_input:
16750b04680fSAndrew Lunn 		err = m88e1510_get_temp(phydev, temp);
16760b04680fSAndrew Lunn 		break;
16770b04680fSAndrew Lunn 	case hwmon_temp_crit:
16780b04680fSAndrew Lunn 		err = m88e1510_get_temp_critical(phydev, temp);
16790b04680fSAndrew Lunn 		break;
16800b04680fSAndrew Lunn 	case hwmon_temp_max_alarm:
16810b04680fSAndrew Lunn 		err = m88e1510_get_temp_alarm(phydev, temp);
16820b04680fSAndrew Lunn 		break;
16830b04680fSAndrew Lunn 	default:
16840b04680fSAndrew Lunn 		return -EOPNOTSUPP;
16850b04680fSAndrew Lunn 	}
16860b04680fSAndrew Lunn 
16870b04680fSAndrew Lunn 	return err;
16880b04680fSAndrew Lunn }
16890b04680fSAndrew Lunn 
16900b04680fSAndrew Lunn static int m88e1510_hwmon_write(struct device *dev,
16910b04680fSAndrew Lunn 				enum hwmon_sensor_types type,
16920b04680fSAndrew Lunn 				u32 attr, int channel, long temp)
16930b04680fSAndrew Lunn {
16940b04680fSAndrew Lunn 	struct phy_device *phydev = dev_get_drvdata(dev);
16950b04680fSAndrew Lunn 	int err;
16960b04680fSAndrew Lunn 
16970b04680fSAndrew Lunn 	switch (attr) {
16980b04680fSAndrew Lunn 	case hwmon_temp_crit:
16990b04680fSAndrew Lunn 		err = m88e1510_set_temp_critical(phydev, temp);
17000b04680fSAndrew Lunn 		break;
17010b04680fSAndrew Lunn 	default:
17020b04680fSAndrew Lunn 		return -EOPNOTSUPP;
17030b04680fSAndrew Lunn 	}
17040b04680fSAndrew Lunn 	return err;
17050b04680fSAndrew Lunn }
17060b04680fSAndrew Lunn 
17070b04680fSAndrew Lunn static umode_t m88e1510_hwmon_is_visible(const void *data,
17080b04680fSAndrew Lunn 					 enum hwmon_sensor_types type,
17090b04680fSAndrew Lunn 					 u32 attr, int channel)
17100b04680fSAndrew Lunn {
17110b04680fSAndrew Lunn 	if (type != hwmon_temp)
17120b04680fSAndrew Lunn 		return 0;
17130b04680fSAndrew Lunn 
17140b04680fSAndrew Lunn 	switch (attr) {
17150b04680fSAndrew Lunn 	case hwmon_temp_input:
17160b04680fSAndrew Lunn 	case hwmon_temp_max_alarm:
17170b04680fSAndrew Lunn 		return 0444;
17180b04680fSAndrew Lunn 	case hwmon_temp_crit:
17190b04680fSAndrew Lunn 		return 0644;
17200b04680fSAndrew Lunn 	default:
17210b04680fSAndrew Lunn 		return 0;
17220b04680fSAndrew Lunn 	}
17230b04680fSAndrew Lunn }
17240b04680fSAndrew Lunn 
17250b04680fSAndrew Lunn static u32 m88e1510_hwmon_temp_config[] = {
17260b04680fSAndrew Lunn 	HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_MAX_ALARM,
17270b04680fSAndrew Lunn 	0
17280b04680fSAndrew Lunn };
17290b04680fSAndrew Lunn 
17300b04680fSAndrew Lunn static const struct hwmon_channel_info m88e1510_hwmon_temp = {
17310b04680fSAndrew Lunn 	.type = hwmon_temp,
17320b04680fSAndrew Lunn 	.config = m88e1510_hwmon_temp_config,
17330b04680fSAndrew Lunn };
17340b04680fSAndrew Lunn 
17350b04680fSAndrew Lunn static const struct hwmon_channel_info *m88e1510_hwmon_info[] = {
17360b04680fSAndrew Lunn 	&m88e1121_hwmon_chip,
17370b04680fSAndrew Lunn 	&m88e1510_hwmon_temp,
17380b04680fSAndrew Lunn 	NULL
17390b04680fSAndrew Lunn };
17400b04680fSAndrew Lunn 
17410b04680fSAndrew Lunn static const struct hwmon_ops m88e1510_hwmon_hwmon_ops = {
17420b04680fSAndrew Lunn 	.is_visible = m88e1510_hwmon_is_visible,
17430b04680fSAndrew Lunn 	.read = m88e1510_hwmon_read,
17440b04680fSAndrew Lunn 	.write = m88e1510_hwmon_write,
17450b04680fSAndrew Lunn };
17460b04680fSAndrew Lunn 
17470b04680fSAndrew Lunn static const struct hwmon_chip_info m88e1510_hwmon_chip_info = {
17480b04680fSAndrew Lunn 	.ops = &m88e1510_hwmon_hwmon_ops,
17490b04680fSAndrew Lunn 	.info = m88e1510_hwmon_info,
17500b04680fSAndrew Lunn };
17510b04680fSAndrew Lunn 
1752fee2d546SAndrew Lunn static int m88e6390_get_temp(struct phy_device *phydev, long *temp)
1753fee2d546SAndrew Lunn {
1754fee2d546SAndrew Lunn 	int sum = 0;
1755fee2d546SAndrew Lunn 	int oldpage;
1756fee2d546SAndrew Lunn 	int ret = 0;
1757fee2d546SAndrew Lunn 	int i;
1758fee2d546SAndrew Lunn 
1759fee2d546SAndrew Lunn 	*temp = 0;
1760fee2d546SAndrew Lunn 
1761fee2d546SAndrew Lunn 	oldpage = phy_select_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
1762fee2d546SAndrew Lunn 	if (oldpage < 0)
1763fee2d546SAndrew Lunn 		goto error;
1764fee2d546SAndrew Lunn 
1765fee2d546SAndrew Lunn 	/* Enable temperature sensor */
1766fee2d546SAndrew Lunn 	ret = __phy_read(phydev, MII_88E6390_MISC_TEST);
1767fee2d546SAndrew Lunn 	if (ret < 0)
1768fee2d546SAndrew Lunn 		goto error;
1769fee2d546SAndrew Lunn 
1770fee2d546SAndrew Lunn 	ret = ret & ~MII_88E6390_MISC_TEST_SAMPLE_MASK;
1771fee2d546SAndrew Lunn 	ret |= MII_88E6390_MISC_TEST_SAMPLE_ENABLE |
1772fee2d546SAndrew Lunn 		MII_88E6390_MISC_TEST_SAMPLE_1S;
1773fee2d546SAndrew Lunn 
1774fee2d546SAndrew Lunn 	ret = __phy_write(phydev, MII_88E6390_MISC_TEST, ret);
1775fee2d546SAndrew Lunn 	if (ret < 0)
1776fee2d546SAndrew Lunn 		goto error;
1777fee2d546SAndrew Lunn 
1778fee2d546SAndrew Lunn 	/* Wait for temperature to stabilize */
1779fee2d546SAndrew Lunn 	usleep_range(10000, 12000);
1780fee2d546SAndrew Lunn 
1781fee2d546SAndrew Lunn 	/* Reading the temperature sense has an errata. You need to read
1782fee2d546SAndrew Lunn 	 * a number of times and take an average.
1783fee2d546SAndrew Lunn 	 */
1784fee2d546SAndrew Lunn 	for (i = 0; i < MII_88E6390_TEMP_SENSOR_SAMPLES; i++) {
1785fee2d546SAndrew Lunn 		ret = __phy_read(phydev, MII_88E6390_TEMP_SENSOR);
1786fee2d546SAndrew Lunn 		if (ret < 0)
1787fee2d546SAndrew Lunn 			goto error;
1788fee2d546SAndrew Lunn 		sum += ret & MII_88E6390_TEMP_SENSOR_MASK;
1789fee2d546SAndrew Lunn 	}
1790fee2d546SAndrew Lunn 
1791fee2d546SAndrew Lunn 	sum /= MII_88E6390_TEMP_SENSOR_SAMPLES;
1792fee2d546SAndrew Lunn 	*temp = (sum  - 75) * 1000;
1793fee2d546SAndrew Lunn 
1794fee2d546SAndrew Lunn 	/* Disable temperature sensor */
1795fee2d546SAndrew Lunn 	ret = __phy_read(phydev, MII_88E6390_MISC_TEST);
1796fee2d546SAndrew Lunn 	if (ret < 0)
1797fee2d546SAndrew Lunn 		goto error;
1798fee2d546SAndrew Lunn 
1799fee2d546SAndrew Lunn 	ret = ret & ~MII_88E6390_MISC_TEST_SAMPLE_MASK;
1800fee2d546SAndrew Lunn 	ret |= MII_88E6390_MISC_TEST_SAMPLE_DISABLE;
1801fee2d546SAndrew Lunn 
1802fee2d546SAndrew Lunn 	ret = __phy_write(phydev, MII_88E6390_MISC_TEST, ret);
1803fee2d546SAndrew Lunn 
1804fee2d546SAndrew Lunn error:
1805fee2d546SAndrew Lunn 	phy_restore_page(phydev, oldpage, ret);
1806fee2d546SAndrew Lunn 
1807fee2d546SAndrew Lunn 	return ret;
1808fee2d546SAndrew Lunn }
1809fee2d546SAndrew Lunn 
1810fee2d546SAndrew Lunn static int m88e6390_hwmon_read(struct device *dev,
1811fee2d546SAndrew Lunn 			       enum hwmon_sensor_types type,
1812fee2d546SAndrew Lunn 			       u32 attr, int channel, long *temp)
1813fee2d546SAndrew Lunn {
1814fee2d546SAndrew Lunn 	struct phy_device *phydev = dev_get_drvdata(dev);
1815fee2d546SAndrew Lunn 	int err;
1816fee2d546SAndrew Lunn 
1817fee2d546SAndrew Lunn 	switch (attr) {
1818fee2d546SAndrew Lunn 	case hwmon_temp_input:
1819fee2d546SAndrew Lunn 		err = m88e6390_get_temp(phydev, temp);
1820fee2d546SAndrew Lunn 		break;
1821fee2d546SAndrew Lunn 	default:
1822fee2d546SAndrew Lunn 		return -EOPNOTSUPP;
1823fee2d546SAndrew Lunn 	}
1824fee2d546SAndrew Lunn 
1825fee2d546SAndrew Lunn 	return err;
1826fee2d546SAndrew Lunn }
1827fee2d546SAndrew Lunn 
1828fee2d546SAndrew Lunn static umode_t m88e6390_hwmon_is_visible(const void *data,
1829fee2d546SAndrew Lunn 					 enum hwmon_sensor_types type,
1830fee2d546SAndrew Lunn 					 u32 attr, int channel)
1831fee2d546SAndrew Lunn {
1832fee2d546SAndrew Lunn 	if (type != hwmon_temp)
1833fee2d546SAndrew Lunn 		return 0;
1834fee2d546SAndrew Lunn 
1835fee2d546SAndrew Lunn 	switch (attr) {
1836fee2d546SAndrew Lunn 	case hwmon_temp_input:
1837fee2d546SAndrew Lunn 		return 0444;
1838fee2d546SAndrew Lunn 	default:
1839fee2d546SAndrew Lunn 		return 0;
1840fee2d546SAndrew Lunn 	}
1841fee2d546SAndrew Lunn }
1842fee2d546SAndrew Lunn 
1843fee2d546SAndrew Lunn static u32 m88e6390_hwmon_temp_config[] = {
1844fee2d546SAndrew Lunn 	HWMON_T_INPUT,
1845fee2d546SAndrew Lunn 	0
1846fee2d546SAndrew Lunn };
1847fee2d546SAndrew Lunn 
1848fee2d546SAndrew Lunn static const struct hwmon_channel_info m88e6390_hwmon_temp = {
1849fee2d546SAndrew Lunn 	.type = hwmon_temp,
1850fee2d546SAndrew Lunn 	.config = m88e6390_hwmon_temp_config,
1851fee2d546SAndrew Lunn };
1852fee2d546SAndrew Lunn 
1853fee2d546SAndrew Lunn static const struct hwmon_channel_info *m88e6390_hwmon_info[] = {
1854fee2d546SAndrew Lunn 	&m88e1121_hwmon_chip,
1855fee2d546SAndrew Lunn 	&m88e6390_hwmon_temp,
1856fee2d546SAndrew Lunn 	NULL
1857fee2d546SAndrew Lunn };
1858fee2d546SAndrew Lunn 
1859fee2d546SAndrew Lunn static const struct hwmon_ops m88e6390_hwmon_hwmon_ops = {
1860fee2d546SAndrew Lunn 	.is_visible = m88e6390_hwmon_is_visible,
1861fee2d546SAndrew Lunn 	.read = m88e6390_hwmon_read,
1862fee2d546SAndrew Lunn };
1863fee2d546SAndrew Lunn 
1864fee2d546SAndrew Lunn static const struct hwmon_chip_info m88e6390_hwmon_chip_info = {
1865fee2d546SAndrew Lunn 	.ops = &m88e6390_hwmon_hwmon_ops,
1866fee2d546SAndrew Lunn 	.info = m88e6390_hwmon_info,
1867fee2d546SAndrew Lunn };
1868fee2d546SAndrew Lunn 
18690b04680fSAndrew Lunn static int marvell_hwmon_name(struct phy_device *phydev)
18700b04680fSAndrew Lunn {
18710b04680fSAndrew Lunn 	struct marvell_priv *priv = phydev->priv;
18720b04680fSAndrew Lunn 	struct device *dev = &phydev->mdio.dev;
18730b04680fSAndrew Lunn 	const char *devname = dev_name(dev);
18740b04680fSAndrew Lunn 	size_t len = strlen(devname);
18750b04680fSAndrew Lunn 	int i, j;
18760b04680fSAndrew Lunn 
18770b04680fSAndrew Lunn 	priv->hwmon_name = devm_kzalloc(dev, len, GFP_KERNEL);
18780b04680fSAndrew Lunn 	if (!priv->hwmon_name)
18790b04680fSAndrew Lunn 		return -ENOMEM;
18800b04680fSAndrew Lunn 
18810b04680fSAndrew Lunn 	for (i = j = 0; i < len && devname[i]; i++) {
18820b04680fSAndrew Lunn 		if (isalnum(devname[i]))
18830b04680fSAndrew Lunn 			priv->hwmon_name[j++] = devname[i];
18840b04680fSAndrew Lunn 	}
18850b04680fSAndrew Lunn 
18860b04680fSAndrew Lunn 	return 0;
18870b04680fSAndrew Lunn }
18880b04680fSAndrew Lunn 
18890b04680fSAndrew Lunn static int marvell_hwmon_probe(struct phy_device *phydev,
18900b04680fSAndrew Lunn 			       const struct hwmon_chip_info *chip)
18910b04680fSAndrew Lunn {
18920b04680fSAndrew Lunn 	struct marvell_priv *priv = phydev->priv;
18930b04680fSAndrew Lunn 	struct device *dev = &phydev->mdio.dev;
18940b04680fSAndrew Lunn 	int err;
18950b04680fSAndrew Lunn 
18960b04680fSAndrew Lunn 	err = marvell_hwmon_name(phydev);
18970b04680fSAndrew Lunn 	if (err)
18980b04680fSAndrew Lunn 		return err;
18990b04680fSAndrew Lunn 
19000b04680fSAndrew Lunn 	priv->hwmon_dev = devm_hwmon_device_register_with_info(
19010b04680fSAndrew Lunn 		dev, priv->hwmon_name, phydev, chip, NULL);
19020b04680fSAndrew Lunn 
19030b04680fSAndrew Lunn 	return PTR_ERR_OR_ZERO(priv->hwmon_dev);
19040b04680fSAndrew Lunn }
19050b04680fSAndrew Lunn 
19060b04680fSAndrew Lunn static int m88e1121_hwmon_probe(struct phy_device *phydev)
19070b04680fSAndrew Lunn {
19080b04680fSAndrew Lunn 	return marvell_hwmon_probe(phydev, &m88e1121_hwmon_chip_info);
19090b04680fSAndrew Lunn }
19100b04680fSAndrew Lunn 
19110b04680fSAndrew Lunn static int m88e1510_hwmon_probe(struct phy_device *phydev)
19120b04680fSAndrew Lunn {
19130b04680fSAndrew Lunn 	return marvell_hwmon_probe(phydev, &m88e1510_hwmon_chip_info);
19140b04680fSAndrew Lunn }
1915fee2d546SAndrew Lunn 
1916fee2d546SAndrew Lunn static int m88e6390_hwmon_probe(struct phy_device *phydev)
1917fee2d546SAndrew Lunn {
1918fee2d546SAndrew Lunn 	return marvell_hwmon_probe(phydev, &m88e6390_hwmon_chip_info);
1919fee2d546SAndrew Lunn }
19200b04680fSAndrew Lunn #else
19210b04680fSAndrew Lunn static int m88e1121_hwmon_probe(struct phy_device *phydev)
19220b04680fSAndrew Lunn {
19230b04680fSAndrew Lunn 	return 0;
19240b04680fSAndrew Lunn }
19250b04680fSAndrew Lunn 
19260b04680fSAndrew Lunn static int m88e1510_hwmon_probe(struct phy_device *phydev)
19270b04680fSAndrew Lunn {
19280b04680fSAndrew Lunn 	return 0;
19290b04680fSAndrew Lunn }
1930fee2d546SAndrew Lunn 
1931fee2d546SAndrew Lunn static int m88e6390_hwmon_probe(struct phy_device *phydev)
1932fee2d546SAndrew Lunn {
1933fee2d546SAndrew Lunn 	return 0;
1934fee2d546SAndrew Lunn }
19350b04680fSAndrew Lunn #endif
19360b04680fSAndrew Lunn 
1937d2fa47d9SAndrew Lunn static int marvell_probe(struct phy_device *phydev)
1938d2fa47d9SAndrew Lunn {
1939d2fa47d9SAndrew Lunn 	struct marvell_priv *priv;
1940d2fa47d9SAndrew Lunn 
1941e5a03bfdSAndrew Lunn 	priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
1942d2fa47d9SAndrew Lunn 	if (!priv)
1943d2fa47d9SAndrew Lunn 		return -ENOMEM;
1944d2fa47d9SAndrew Lunn 
1945d2fa47d9SAndrew Lunn 	phydev->priv = priv;
1946d2fa47d9SAndrew Lunn 
1947d2fa47d9SAndrew Lunn 	return 0;
1948d2fa47d9SAndrew Lunn }
1949d2fa47d9SAndrew Lunn 
19500b04680fSAndrew Lunn static int m88e1121_probe(struct phy_device *phydev)
19510b04680fSAndrew Lunn {
19520b04680fSAndrew Lunn 	int err;
19530b04680fSAndrew Lunn 
19540b04680fSAndrew Lunn 	err = marvell_probe(phydev);
19550b04680fSAndrew Lunn 	if (err)
19560b04680fSAndrew Lunn 		return err;
19570b04680fSAndrew Lunn 
19580b04680fSAndrew Lunn 	return m88e1121_hwmon_probe(phydev);
19590b04680fSAndrew Lunn }
19600b04680fSAndrew Lunn 
19610b04680fSAndrew Lunn static int m88e1510_probe(struct phy_device *phydev)
19620b04680fSAndrew Lunn {
19630b04680fSAndrew Lunn 	int err;
19640b04680fSAndrew Lunn 
19650b04680fSAndrew Lunn 	err = marvell_probe(phydev);
19660b04680fSAndrew Lunn 	if (err)
19670b04680fSAndrew Lunn 		return err;
19680b04680fSAndrew Lunn 
19690b04680fSAndrew Lunn 	return m88e1510_hwmon_probe(phydev);
19700b04680fSAndrew Lunn }
19710b04680fSAndrew Lunn 
1972fee2d546SAndrew Lunn static int m88e6390_probe(struct phy_device *phydev)
1973fee2d546SAndrew Lunn {
1974fee2d546SAndrew Lunn 	int err;
1975fee2d546SAndrew Lunn 
1976fee2d546SAndrew Lunn 	err = marvell_probe(phydev);
1977fee2d546SAndrew Lunn 	if (err)
1978fee2d546SAndrew Lunn 		return err;
1979fee2d546SAndrew Lunn 
1980fee2d546SAndrew Lunn 	return m88e6390_hwmon_probe(phydev);
1981fee2d546SAndrew Lunn }
1982fee2d546SAndrew Lunn 
1983e5479239SOlof Johansson static struct phy_driver marvell_drivers[] = {
1984e5479239SOlof Johansson 	{
19852f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1101,
19862f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
198700db8189SAndy Fleming 		.name = "Marvell 88E1101",
198800db8189SAndy Fleming 		.features = PHY_GBIT_FEATURES,
198900db8189SAndy Fleming 		.flags = PHY_HAS_INTERRUPT,
199018702414SArnd Bergmann 		.probe = marvell_probe,
199179be1a1cSClemens Gruber 		.config_init = &marvell_config_init,
1992f2899788SAndrew Lunn 		.config_aneg = &m88e1101_config_aneg,
199300db8189SAndy Fleming 		.ack_interrupt = &marvell_ack_interrupt,
199400db8189SAndy Fleming 		.config_intr = &marvell_config_intr,
19950898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
19960898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
1997424ca4c5SRussell King 		.read_page = marvell_read_page,
1998424ca4c5SRussell King 		.write_page = marvell_write_page,
1999d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2000d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2001d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2002e5479239SOlof Johansson 	},
2003e5479239SOlof Johansson 	{
20042f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1112,
20052f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
200685cfb534SOlof Johansson 		.name = "Marvell 88E1112",
200785cfb534SOlof Johansson 		.features = PHY_GBIT_FEATURES,
200885cfb534SOlof Johansson 		.flags = PHY_HAS_INTERRUPT,
2009d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
201085cfb534SOlof Johansson 		.config_init = &m88e1111_config_init,
201185cfb534SOlof Johansson 		.config_aneg = &marvell_config_aneg,
201285cfb534SOlof Johansson 		.ack_interrupt = &marvell_ack_interrupt,
201385cfb534SOlof Johansson 		.config_intr = &marvell_config_intr,
20140898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
20150898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2016424ca4c5SRussell King 		.read_page = marvell_read_page,
2017424ca4c5SRussell King 		.write_page = marvell_write_page,
2018d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2019d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2020d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
202185cfb534SOlof Johansson 	},
202285cfb534SOlof Johansson 	{
20232f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1111,
20242f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
202576884679SAndy Fleming 		.name = "Marvell 88E1111",
202676884679SAndy Fleming 		.features = PHY_GBIT_FEATURES,
202776884679SAndy Fleming 		.flags = PHY_HAS_INTERRUPT,
2028d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
2029e5479239SOlof Johansson 		.config_init = &m88e1111_config_init,
20303ec0a0f1SHarini Katakam 		.config_aneg = &m88e1111_config_aneg,
2031be937f1fSAlexandr Smirnov 		.read_status = &marvell_read_status,
203276884679SAndy Fleming 		.ack_interrupt = &marvell_ack_interrupt,
203376884679SAndy Fleming 		.config_intr = &marvell_config_intr,
20340898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
20350898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2036424ca4c5SRussell King 		.read_page = marvell_read_page,
2037424ca4c5SRussell King 		.write_page = marvell_write_page,
2038d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2039d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2040d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2041e5479239SOlof Johansson 	},
2042e5479239SOlof Johansson 	{
20432f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1118,
20442f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2045605f196eSRon Madrid 		.name = "Marvell 88E1118",
2046605f196eSRon Madrid 		.features = PHY_GBIT_FEATURES,
2047605f196eSRon Madrid 		.flags = PHY_HAS_INTERRUPT,
2048d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
2049605f196eSRon Madrid 		.config_init = &m88e1118_config_init,
2050605f196eSRon Madrid 		.config_aneg = &m88e1118_config_aneg,
2051605f196eSRon Madrid 		.ack_interrupt = &marvell_ack_interrupt,
2052605f196eSRon Madrid 		.config_intr = &marvell_config_intr,
20530898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
20540898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2055424ca4c5SRussell King 		.read_page = marvell_read_page,
2056424ca4c5SRussell King 		.write_page = marvell_write_page,
2057d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2058d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2059d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2060605f196eSRon Madrid 	},
2061605f196eSRon Madrid 	{
20622f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1121R,
20632f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2064140bc929SSergei Poselenov 		.name = "Marvell 88E1121R",
2065140bc929SSergei Poselenov 		.features = PHY_GBIT_FEATURES,
2066140bc929SSergei Poselenov 		.flags = PHY_HAS_INTERRUPT,
206718702414SArnd Bergmann 		.probe = &m88e1121_probe,
2068fdecf36fSClemens Gruber 		.config_init = &m88e1121_config_init,
2069140bc929SSergei Poselenov 		.config_aneg = &m88e1121_config_aneg,
2070140bc929SSergei Poselenov 		.read_status = &marvell_read_status,
2071140bc929SSergei Poselenov 		.ack_interrupt = &marvell_ack_interrupt,
2072140bc929SSergei Poselenov 		.config_intr = &marvell_config_intr,
2073dcd07be3SAnatolij Gustschin 		.did_interrupt = &m88e1121_did_interrupt,
20740898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
20750898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2076424ca4c5SRussell King 		.read_page = marvell_read_page,
2077424ca4c5SRussell King 		.write_page = marvell_write_page,
2078d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2079d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2080d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2081140bc929SSergei Poselenov 	},
2082140bc929SSergei Poselenov 	{
2083337ac9d5SCyril Chemparathy 		.phy_id = MARVELL_PHY_ID_88E1318S,
20846ba74014SLinus Torvalds 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2085337ac9d5SCyril Chemparathy 		.name = "Marvell 88E1318S",
20863ff1c259SCyril Chemparathy 		.features = PHY_GBIT_FEATURES,
20873ff1c259SCyril Chemparathy 		.flags = PHY_HAS_INTERRUPT,
2088d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
2089fdecf36fSClemens Gruber 		.config_init = &m88e1121_config_init,
2090337ac9d5SCyril Chemparathy 		.config_aneg = &m88e1318_config_aneg,
20913ff1c259SCyril Chemparathy 		.read_status = &marvell_read_status,
20923ff1c259SCyril Chemparathy 		.ack_interrupt = &marvell_ack_interrupt,
20933ff1c259SCyril Chemparathy 		.config_intr = &marvell_config_intr,
20943ff1c259SCyril Chemparathy 		.did_interrupt = &m88e1121_did_interrupt,
20953871c387SMichael Stapelberg 		.get_wol = &m88e1318_get_wol,
20963871c387SMichael Stapelberg 		.set_wol = &m88e1318_set_wol,
20970898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
20980898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2099424ca4c5SRussell King 		.read_page = marvell_read_page,
2100424ca4c5SRussell King 		.write_page = marvell_write_page,
2101d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2102d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2103d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
21043ff1c259SCyril Chemparathy 	},
21053ff1c259SCyril Chemparathy 	{
21062f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1145,
21072f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
210876884679SAndy Fleming 		.name = "Marvell 88E1145",
210976884679SAndy Fleming 		.features = PHY_GBIT_FEATURES,
211076884679SAndy Fleming 		.flags = PHY_HAS_INTERRUPT,
2111d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
211276884679SAndy Fleming 		.config_init = &m88e1145_config_init,
2113c505873eSZhao Qiang 		.config_aneg = &m88e1101_config_aneg,
211476884679SAndy Fleming 		.read_status = &genphy_read_status,
211576884679SAndy Fleming 		.ack_interrupt = &marvell_ack_interrupt,
211676884679SAndy Fleming 		.config_intr = &marvell_config_intr,
21170898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
21180898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2119424ca4c5SRussell King 		.read_page = marvell_read_page,
2120424ca4c5SRussell King 		.write_page = marvell_write_page,
2121d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2122d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2123d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2124ac8c635aSOlof Johansson 	},
2125ac8c635aSOlof Johansson 	{
212690600732SDavid Daney 		.phy_id = MARVELL_PHY_ID_88E1149R,
212790600732SDavid Daney 		.phy_id_mask = MARVELL_PHY_ID_MASK,
212890600732SDavid Daney 		.name = "Marvell 88E1149R",
212990600732SDavid Daney 		.features = PHY_GBIT_FEATURES,
213090600732SDavid Daney 		.flags = PHY_HAS_INTERRUPT,
2131d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
213290600732SDavid Daney 		.config_init = &m88e1149_config_init,
213390600732SDavid Daney 		.config_aneg = &m88e1118_config_aneg,
213490600732SDavid Daney 		.ack_interrupt = &marvell_ack_interrupt,
213590600732SDavid Daney 		.config_intr = &marvell_config_intr,
21360898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
21370898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2138424ca4c5SRussell King 		.read_page = marvell_read_page,
2139424ca4c5SRussell King 		.write_page = marvell_write_page,
2140d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2141d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2142d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
214390600732SDavid Daney 	},
214490600732SDavid Daney 	{
21452f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1240,
21462f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2147ac8c635aSOlof Johansson 		.name = "Marvell 88E1240",
2148ac8c635aSOlof Johansson 		.features = PHY_GBIT_FEATURES,
2149ac8c635aSOlof Johansson 		.flags = PHY_HAS_INTERRUPT,
2150d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
2151ac8c635aSOlof Johansson 		.config_init = &m88e1111_config_init,
2152ac8c635aSOlof Johansson 		.config_aneg = &marvell_config_aneg,
2153ac8c635aSOlof Johansson 		.ack_interrupt = &marvell_ack_interrupt,
2154ac8c635aSOlof Johansson 		.config_intr = &marvell_config_intr,
21550898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
21560898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2157424ca4c5SRussell King 		.read_page = marvell_read_page,
2158424ca4c5SRussell King 		.write_page = marvell_write_page,
2159d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2160d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2161d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2162ac8c635aSOlof Johansson 	},
21633da09a51SMichal Simek 	{
21643da09a51SMichal Simek 		.phy_id = MARVELL_PHY_ID_88E1116R,
21653da09a51SMichal Simek 		.phy_id_mask = MARVELL_PHY_ID_MASK,
21663da09a51SMichal Simek 		.name = "Marvell 88E1116R",
21673da09a51SMichal Simek 		.features = PHY_GBIT_FEATURES,
21683da09a51SMichal Simek 		.flags = PHY_HAS_INTERRUPT,
2169d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
21703da09a51SMichal Simek 		.config_init = &m88e1116r_config_init,
21713da09a51SMichal Simek 		.ack_interrupt = &marvell_ack_interrupt,
21723da09a51SMichal Simek 		.config_intr = &marvell_config_intr,
21730898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
21740898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2175424ca4c5SRussell King 		.read_page = marvell_read_page,
2176424ca4c5SRussell King 		.write_page = marvell_write_page,
2177d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2178d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2179d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
21803da09a51SMichal Simek 	},
218110e24caaSMichal Simek 	{
218210e24caaSMichal Simek 		.phy_id = MARVELL_PHY_ID_88E1510,
218310e24caaSMichal Simek 		.phy_id_mask = MARVELL_PHY_ID_MASK,
218410e24caaSMichal Simek 		.name = "Marvell 88E1510",
21856cfb3bccSCharles-Antoine Couret 		.features = PHY_GBIT_FEATURES | SUPPORTED_FIBRE,
218618702414SArnd Bergmann 		.flags = PHY_HAS_INTERRUPT,
21870b04680fSAndrew Lunn 		.probe = &m88e1510_probe,
2188930b37eeSStefan Roese 		.config_init = &m88e1510_config_init,
218910e24caaSMichal Simek 		.config_aneg = &m88e1510_config_aneg,
219010e24caaSMichal Simek 		.read_status = &marvell_read_status,
219110e24caaSMichal Simek 		.ack_interrupt = &marvell_ack_interrupt,
219210e24caaSMichal Simek 		.config_intr = &marvell_config_intr,
219310e24caaSMichal Simek 		.did_interrupt = &m88e1121_did_interrupt,
2194f39aac7eSJingju Hou 		.get_wol = &m88e1318_get_wol,
2195f39aac7eSJingju Hou 		.set_wol = &m88e1318_set_wol,
21963758be3dSCharles-Antoine Couret 		.resume = &marvell_resume,
21973758be3dSCharles-Antoine Couret 		.suspend = &marvell_suspend,
2198424ca4c5SRussell King 		.read_page = marvell_read_page,
2199424ca4c5SRussell King 		.write_page = marvell_write_page,
2200d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2201d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2202d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2203f0f9b4edSLin Yun Sheng 		.set_loopback = genphy_loopback,
220410e24caaSMichal Simek 	},
22056b358aedSSebastian Hesselbarth 	{
2206819ec8e1SAndrew Lunn 		.phy_id = MARVELL_PHY_ID_88E1540,
2207819ec8e1SAndrew Lunn 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2208819ec8e1SAndrew Lunn 		.name = "Marvell 88E1540",
2209819ec8e1SAndrew Lunn 		.features = PHY_GBIT_FEATURES,
2210819ec8e1SAndrew Lunn 		.flags = PHY_HAS_INTERRUPT,
221118702414SArnd Bergmann 		.probe = m88e1510_probe,
221279be1a1cSClemens Gruber 		.config_init = &marvell_config_init,
2213819ec8e1SAndrew Lunn 		.config_aneg = &m88e1510_config_aneg,
2214819ec8e1SAndrew Lunn 		.read_status = &marvell_read_status,
2215819ec8e1SAndrew Lunn 		.ack_interrupt = &marvell_ack_interrupt,
2216819ec8e1SAndrew Lunn 		.config_intr = &marvell_config_intr,
2217819ec8e1SAndrew Lunn 		.did_interrupt = &m88e1121_did_interrupt,
2218819ec8e1SAndrew Lunn 		.resume = &genphy_resume,
2219819ec8e1SAndrew Lunn 		.suspend = &genphy_suspend,
2220424ca4c5SRussell King 		.read_page = marvell_read_page,
2221424ca4c5SRussell King 		.write_page = marvell_write_page,
2222d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2223d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2224d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2225819ec8e1SAndrew Lunn 	},
2226819ec8e1SAndrew Lunn 	{
222760f06fdeSAndrew Lunn 		.phy_id = MARVELL_PHY_ID_88E1545,
222860f06fdeSAndrew Lunn 		.phy_id_mask = MARVELL_PHY_ID_MASK,
222960f06fdeSAndrew Lunn 		.name = "Marvell 88E1545",
223060f06fdeSAndrew Lunn 		.probe = m88e1510_probe,
223160f06fdeSAndrew Lunn 		.features = PHY_GBIT_FEATURES,
223260f06fdeSAndrew Lunn 		.flags = PHY_HAS_INTERRUPT,
223360f06fdeSAndrew Lunn 		.config_init = &marvell_config_init,
223460f06fdeSAndrew Lunn 		.config_aneg = &m88e1510_config_aneg,
223560f06fdeSAndrew Lunn 		.read_status = &marvell_read_status,
223660f06fdeSAndrew Lunn 		.ack_interrupt = &marvell_ack_interrupt,
223760f06fdeSAndrew Lunn 		.config_intr = &marvell_config_intr,
223860f06fdeSAndrew Lunn 		.did_interrupt = &m88e1121_did_interrupt,
223960f06fdeSAndrew Lunn 		.resume = &genphy_resume,
224060f06fdeSAndrew Lunn 		.suspend = &genphy_suspend,
2241424ca4c5SRussell King 		.read_page = marvell_read_page,
2242424ca4c5SRussell King 		.write_page = marvell_write_page,
224360f06fdeSAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
224460f06fdeSAndrew Lunn 		.get_strings = marvell_get_strings,
224560f06fdeSAndrew Lunn 		.get_stats = marvell_get_stats,
224660f06fdeSAndrew Lunn 	},
224760f06fdeSAndrew Lunn 	{
22486b358aedSSebastian Hesselbarth 		.phy_id = MARVELL_PHY_ID_88E3016,
22496b358aedSSebastian Hesselbarth 		.phy_id_mask = MARVELL_PHY_ID_MASK,
22506b358aedSSebastian Hesselbarth 		.name = "Marvell 88E3016",
22516b358aedSSebastian Hesselbarth 		.features = PHY_BASIC_FEATURES,
22526b358aedSSebastian Hesselbarth 		.flags = PHY_HAS_INTERRUPT,
2253d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
22546b358aedSSebastian Hesselbarth 		.config_init = &m88e3016_config_init,
22556b358aedSSebastian Hesselbarth 		.aneg_done = &marvell_aneg_done,
22566b358aedSSebastian Hesselbarth 		.read_status = &marvell_read_status,
22576b358aedSSebastian Hesselbarth 		.ack_interrupt = &marvell_ack_interrupt,
22586b358aedSSebastian Hesselbarth 		.config_intr = &marvell_config_intr,
22596b358aedSSebastian Hesselbarth 		.did_interrupt = &m88e1121_did_interrupt,
22606b358aedSSebastian Hesselbarth 		.resume = &genphy_resume,
22616b358aedSSebastian Hesselbarth 		.suspend = &genphy_suspend,
2262424ca4c5SRussell King 		.read_page = marvell_read_page,
2263424ca4c5SRussell King 		.write_page = marvell_write_page,
2264d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2265d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2266d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
22676b358aedSSebastian Hesselbarth 	},
2268e4cf8a38SAndrew Lunn 	{
2269e4cf8a38SAndrew Lunn 		.phy_id = MARVELL_PHY_ID_88E6390,
2270e4cf8a38SAndrew Lunn 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2271e4cf8a38SAndrew Lunn 		.name = "Marvell 88E6390",
2272e4cf8a38SAndrew Lunn 		.features = PHY_GBIT_FEATURES,
2273e4cf8a38SAndrew Lunn 		.flags = PHY_HAS_INTERRUPT,
2274fee2d546SAndrew Lunn 		.probe = m88e6390_probe,
2275e4cf8a38SAndrew Lunn 		.config_init = &marvell_config_init,
2276e4cf8a38SAndrew Lunn 		.config_aneg = &m88e1510_config_aneg,
2277e4cf8a38SAndrew Lunn 		.read_status = &marvell_read_status,
2278e4cf8a38SAndrew Lunn 		.ack_interrupt = &marvell_ack_interrupt,
2279e4cf8a38SAndrew Lunn 		.config_intr = &marvell_config_intr,
2280e4cf8a38SAndrew Lunn 		.did_interrupt = &m88e1121_did_interrupt,
2281e4cf8a38SAndrew Lunn 		.resume = &genphy_resume,
2282e4cf8a38SAndrew Lunn 		.suspend = &genphy_suspend,
2283424ca4c5SRussell King 		.read_page = marvell_read_page,
2284424ca4c5SRussell King 		.write_page = marvell_write_page,
2285e4cf8a38SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2286e4cf8a38SAndrew Lunn 		.get_strings = marvell_get_strings,
2287e4cf8a38SAndrew Lunn 		.get_stats = marvell_get_stats,
2288e4cf8a38SAndrew Lunn 	},
228976884679SAndy Fleming };
229076884679SAndy Fleming 
229150fd7150SJohan Hovold module_phy_driver(marvell_drivers);
22924e4f10f6SDavid Woodhouse 
2293cf93c945SUwe Kleine-König static struct mdio_device_id __maybe_unused marvell_tbl[] = {
2294f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1101, MARVELL_PHY_ID_MASK },
2295f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1112, MARVELL_PHY_ID_MASK },
2296f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1111, MARVELL_PHY_ID_MASK },
2297f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1118, MARVELL_PHY_ID_MASK },
2298f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1121R, MARVELL_PHY_ID_MASK },
2299f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1145, MARVELL_PHY_ID_MASK },
2300f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1149R, MARVELL_PHY_ID_MASK },
2301f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1240, MARVELL_PHY_ID_MASK },
2302f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1318S, MARVELL_PHY_ID_MASK },
23033da09a51SMichal Simek 	{ MARVELL_PHY_ID_88E1116R, MARVELL_PHY_ID_MASK },
230410e24caaSMichal Simek 	{ MARVELL_PHY_ID_88E1510, MARVELL_PHY_ID_MASK },
2305819ec8e1SAndrew Lunn 	{ MARVELL_PHY_ID_88E1540, MARVELL_PHY_ID_MASK },
230660f06fdeSAndrew Lunn 	{ MARVELL_PHY_ID_88E1545, MARVELL_PHY_ID_MASK },
23076b358aedSSebastian Hesselbarth 	{ MARVELL_PHY_ID_88E3016, MARVELL_PHY_ID_MASK },
2308e4cf8a38SAndrew Lunn 	{ MARVELL_PHY_ID_88E6390, MARVELL_PHY_ID_MASK },
23094e4f10f6SDavid Woodhouse 	{ }
23104e4f10f6SDavid Woodhouse };
23114e4f10f6SDavid Woodhouse 
23124e4f10f6SDavid Woodhouse MODULE_DEVICE_TABLE(mdio, marvell_tbl);
2313