xref: /openbmc/linux/drivers/net/phy/marvell.c (revision dd9a122a)
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 
831dd9a122aSEsben Haabendal static int m88e1318_config_init(struct phy_device *phydev)
832dd9a122aSEsben Haabendal {
833dd9a122aSEsben Haabendal 	if (phy_interrupt_is_valid(phydev)) {
834dd9a122aSEsben Haabendal 		int err = phy_modify_paged(
835dd9a122aSEsben Haabendal 			phydev, MII_MARVELL_LED_PAGE,
836dd9a122aSEsben Haabendal 			MII_88E1318S_PHY_LED_TCR,
837dd9a122aSEsben Haabendal 			MII_88E1318S_PHY_LED_TCR_FORCE_INT,
838dd9a122aSEsben Haabendal 			MII_88E1318S_PHY_LED_TCR_INTn_ENABLE |
839dd9a122aSEsben Haabendal 			MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW);
840dd9a122aSEsben Haabendal 		if (err < 0)
841dd9a122aSEsben Haabendal 			return err;
842dd9a122aSEsben Haabendal 	}
843dd9a122aSEsben Haabendal 
844dd9a122aSEsben Haabendal 	return m88e1121_config_init(phydev);
845dd9a122aSEsben Haabendal }
846dd9a122aSEsben Haabendal 
847407353ecSClemens Gruber static int m88e1510_config_init(struct phy_device *phydev)
848407353ecSClemens Gruber {
849407353ecSClemens Gruber 	int err;
850407353ecSClemens Gruber 
851407353ecSClemens Gruber 	/* SGMII-to-Copper mode initialization */
852407353ecSClemens Gruber 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
8536623c0fbSRussell King 		u32 pause;
8546623c0fbSRussell King 
855407353ecSClemens Gruber 		/* Select page 18 */
8566427bb2dSAndrew Lunn 		err = marvell_set_page(phydev, 18);
857407353ecSClemens Gruber 		if (err < 0)
858407353ecSClemens Gruber 			return err;
859407353ecSClemens Gruber 
860407353ecSClemens Gruber 		/* In reg 20, write MODE[2:0] = 0x1 (SGMII to Copper) */
861fea23fb5SRussell King 		err = phy_modify(phydev, MII_88E1510_GEN_CTRL_REG_1,
862f102852fSRussell King 				 MII_88E1510_GEN_CTRL_REG_1_MODE_MASK,
863fea23fb5SRussell King 				 MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII);
864407353ecSClemens Gruber 		if (err < 0)
865407353ecSClemens Gruber 			return err;
866407353ecSClemens Gruber 
867407353ecSClemens Gruber 		/* PHY reset is necessary after changing MODE[2:0] */
868fea23fb5SRussell King 		err = phy_modify(phydev, MII_88E1510_GEN_CTRL_REG_1, 0,
869fea23fb5SRussell King 				 MII_88E1510_GEN_CTRL_REG_1_RESET);
870407353ecSClemens Gruber 		if (err < 0)
871407353ecSClemens Gruber 			return err;
872407353ecSClemens Gruber 
873407353ecSClemens Gruber 		/* Reset page selection */
87452295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
875407353ecSClemens Gruber 		if (err < 0)
876407353ecSClemens Gruber 			return err;
8776623c0fbSRussell King 
8786623c0fbSRussell King 		/* There appears to be a bug in the 88e1512 when used in
879cc1122b0SColin Ian King 		 * SGMII to copper mode, where the AN advertisement register
8806623c0fbSRussell King 		 * clears the pause bits each time a negotiation occurs.
8816623c0fbSRussell King 		 * This means we can never be truely sure what was advertised,
8826623c0fbSRussell King 		 * so disable Pause support.
8836623c0fbSRussell King 		 */
8846623c0fbSRussell King 		pause = SUPPORTED_Pause | SUPPORTED_Asym_Pause;
8856623c0fbSRussell King 		phydev->supported &= ~pause;
8866623c0fbSRussell King 		phydev->advertising &= ~pause;
887407353ecSClemens Gruber 	}
888407353ecSClemens Gruber 
889dd9a122aSEsben Haabendal 	return m88e1318_config_init(phydev);
890407353ecSClemens Gruber }
891407353ecSClemens Gruber 
892605f196eSRon Madrid static int m88e1118_config_aneg(struct phy_device *phydev)
893605f196eSRon Madrid {
894605f196eSRon Madrid 	int err;
895605f196eSRon Madrid 
89634386344SAndrew Lunn 	err = genphy_soft_reset(phydev);
897605f196eSRon Madrid 	if (err < 0)
898605f196eSRon Madrid 		return err;
899605f196eSRon Madrid 
900fecd5e91SAndrew Lunn 	err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
901605f196eSRon Madrid 	if (err < 0)
902605f196eSRon Madrid 		return err;
903605f196eSRon Madrid 
904605f196eSRon Madrid 	err = genphy_config_aneg(phydev);
905605f196eSRon Madrid 	return 0;
906605f196eSRon Madrid }
907605f196eSRon Madrid 
908605f196eSRon Madrid static int m88e1118_config_init(struct phy_device *phydev)
909605f196eSRon Madrid {
910605f196eSRon Madrid 	int err;
911605f196eSRon Madrid 
912605f196eSRon Madrid 	/* Change address */
91352295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_MSCR_PAGE);
914605f196eSRon Madrid 	if (err < 0)
915605f196eSRon Madrid 		return err;
916605f196eSRon Madrid 
917605f196eSRon Madrid 	/* Enable 1000 Mbit */
918605f196eSRon Madrid 	err = phy_write(phydev, 0x15, 0x1070);
919605f196eSRon Madrid 	if (err < 0)
920605f196eSRon Madrid 		return err;
921605f196eSRon Madrid 
922605f196eSRon Madrid 	/* Change address */
92352295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_LED_PAGE);
924605f196eSRon Madrid 	if (err < 0)
925605f196eSRon Madrid 		return err;
926605f196eSRon Madrid 
927605f196eSRon Madrid 	/* Adjust LED Control */
9282f495c39SBenjamin Herrenschmidt 	if (phydev->dev_flags & MARVELL_PHY_M1118_DNS323_LEDS)
9292f495c39SBenjamin Herrenschmidt 		err = phy_write(phydev, 0x10, 0x1100);
9302f495c39SBenjamin Herrenschmidt 	else
931605f196eSRon Madrid 		err = phy_write(phydev, 0x10, 0x021e);
932605f196eSRon Madrid 	if (err < 0)
933605f196eSRon Madrid 		return err;
934605f196eSRon Madrid 
935cf41a51dSDavid Daney 	err = marvell_of_reg_init(phydev);
936cf41a51dSDavid Daney 	if (err < 0)
937cf41a51dSDavid Daney 		return err;
938cf41a51dSDavid Daney 
939605f196eSRon Madrid 	/* Reset address */
94052295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
941605f196eSRon Madrid 	if (err < 0)
942605f196eSRon Madrid 		return err;
943605f196eSRon Madrid 
94434386344SAndrew Lunn 	return genphy_soft_reset(phydev);
945605f196eSRon Madrid }
946605f196eSRon Madrid 
94790600732SDavid Daney static int m88e1149_config_init(struct phy_device *phydev)
94890600732SDavid Daney {
94990600732SDavid Daney 	int err;
95090600732SDavid Daney 
95190600732SDavid Daney 	/* Change address */
95252295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_MSCR_PAGE);
95390600732SDavid Daney 	if (err < 0)
95490600732SDavid Daney 		return err;
95590600732SDavid Daney 
95690600732SDavid Daney 	/* Enable 1000 Mbit */
95790600732SDavid Daney 	err = phy_write(phydev, 0x15, 0x1048);
95890600732SDavid Daney 	if (err < 0)
95990600732SDavid Daney 		return err;
96090600732SDavid Daney 
961cf41a51dSDavid Daney 	err = marvell_of_reg_init(phydev);
962cf41a51dSDavid Daney 	if (err < 0)
963cf41a51dSDavid Daney 		return err;
964cf41a51dSDavid Daney 
96590600732SDavid Daney 	/* Reset address */
96652295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
96790600732SDavid Daney 	if (err < 0)
96890600732SDavid Daney 		return err;
96990600732SDavid Daney 
97034386344SAndrew Lunn 	return genphy_soft_reset(phydev);
97190600732SDavid Daney }
97290600732SDavid Daney 
973e1dde8dcSAndrew Lunn static int m88e1145_config_init_rgmii(struct phy_device *phydev)
97476884679SAndy Fleming {
97576884679SAndy Fleming 	int err;
976e69d9ed4SAndrew Lunn 
97761111598SAndrew Lunn 	err = m88e1111_config_init_rgmii_delays(phydev);
97876884679SAndy Fleming 	if (err < 0)
97976884679SAndy Fleming 		return err;
98076884679SAndy Fleming 
9812f495c39SBenjamin Herrenschmidt 	if (phydev->dev_flags & MARVELL_PHY_M1145_FLAGS_RESISTANCE) {
98276884679SAndy Fleming 		err = phy_write(phydev, 0x1d, 0x0012);
98376884679SAndy Fleming 		if (err < 0)
98476884679SAndy Fleming 			return err;
98576884679SAndy Fleming 
986f102852fSRussell King 		err = phy_modify(phydev, 0x1e, 0x0fc0,
987fea23fb5SRussell King 				 2 << 9 | /* 36 ohm */
988fea23fb5SRussell King 				 2 << 6); /* 39 ohm */
98976884679SAndy Fleming 		if (err < 0)
99076884679SAndy Fleming 			return err;
99176884679SAndy Fleming 
99276884679SAndy Fleming 		err = phy_write(phydev, 0x1d, 0x3);
99376884679SAndy Fleming 		if (err < 0)
99476884679SAndy Fleming 			return err;
99576884679SAndy Fleming 
99676884679SAndy Fleming 		err = phy_write(phydev, 0x1e, 0x8000);
997e1dde8dcSAndrew Lunn 	}
99876884679SAndy Fleming 	return err;
99976884679SAndy Fleming }
100076884679SAndy Fleming 
1001e1dde8dcSAndrew Lunn static int m88e1145_config_init_sgmii(struct phy_device *phydev)
1002e1dde8dcSAndrew Lunn {
1003865b813aSAndrew Lunn 	return m88e1111_config_init_hwcfg_mode(
1004865b813aSAndrew Lunn 		phydev, MII_M1111_HWCFG_MODE_SGMII_NO_CLK,
1005865b813aSAndrew Lunn 		MII_M1111_HWCFG_FIBER_COPPER_AUTO);
1006e1dde8dcSAndrew Lunn }
1007e1dde8dcSAndrew Lunn 
1008e1dde8dcSAndrew Lunn static int m88e1145_config_init(struct phy_device *phydev)
1009e1dde8dcSAndrew Lunn {
1010e1dde8dcSAndrew Lunn 	int err;
1011e1dde8dcSAndrew Lunn 
1012e1dde8dcSAndrew Lunn 	/* Take care of errata E0 & E1 */
1013e1dde8dcSAndrew Lunn 	err = phy_write(phydev, 0x1d, 0x001b);
1014e1dde8dcSAndrew Lunn 	if (err < 0)
1015e1dde8dcSAndrew Lunn 		return err;
1016e1dde8dcSAndrew Lunn 
1017e1dde8dcSAndrew Lunn 	err = phy_write(phydev, 0x1e, 0x418f);
1018e1dde8dcSAndrew Lunn 	if (err < 0)
1019e1dde8dcSAndrew Lunn 		return err;
1020e1dde8dcSAndrew Lunn 
1021e1dde8dcSAndrew Lunn 	err = phy_write(phydev, 0x1d, 0x0016);
1022e1dde8dcSAndrew Lunn 	if (err < 0)
1023e1dde8dcSAndrew Lunn 		return err;
1024e1dde8dcSAndrew Lunn 
1025e1dde8dcSAndrew Lunn 	err = phy_write(phydev, 0x1e, 0xa2da);
1026e1dde8dcSAndrew Lunn 	if (err < 0)
1027e1dde8dcSAndrew Lunn 		return err;
1028e1dde8dcSAndrew Lunn 
1029e1dde8dcSAndrew Lunn 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
1030e1dde8dcSAndrew Lunn 		err = m88e1145_config_init_rgmii(phydev);
1031e1dde8dcSAndrew Lunn 		if (err < 0)
1032e1dde8dcSAndrew Lunn 			return err;
1033e1dde8dcSAndrew Lunn 	}
1034e1dde8dcSAndrew Lunn 
1035e1dde8dcSAndrew Lunn 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
1036e1dde8dcSAndrew Lunn 		err = m88e1145_config_init_sgmii(phydev);
1037b0224175SViet Nga Dao 		if (err < 0)
1038b0224175SViet Nga Dao 			return err;
1039b0224175SViet Nga Dao 	}
1040b0224175SViet Nga Dao 
1041cf41a51dSDavid Daney 	err = marvell_of_reg_init(phydev);
1042cf41a51dSDavid Daney 	if (err < 0)
1043cf41a51dSDavid Daney 		return err;
1044cf41a51dSDavid Daney 
104576884679SAndy Fleming 	return 0;
104676884679SAndy Fleming }
104700db8189SAndy Fleming 
10486cfb3bccSCharles-Antoine Couret /**
10496cfb3bccSCharles-Antoine Couret  * fiber_lpa_to_ethtool_lpa_t
10506cfb3bccSCharles-Antoine Couret  * @lpa: value of the MII_LPA register for fiber link
1051be937f1fSAlexandr Smirnov  *
10526cfb3bccSCharles-Antoine Couret  * A small helper function that translates MII_LPA
10536cfb3bccSCharles-Antoine Couret  * bits to ethtool LP advertisement settings.
10546cfb3bccSCharles-Antoine Couret  */
10556cfb3bccSCharles-Antoine Couret static u32 fiber_lpa_to_ethtool_lpa_t(u32 lpa)
10566cfb3bccSCharles-Antoine Couret {
10576cfb3bccSCharles-Antoine Couret 	u32 result = 0;
10586cfb3bccSCharles-Antoine Couret 
10596cfb3bccSCharles-Antoine Couret 	if (lpa & LPA_FIBER_1000HALF)
10606cfb3bccSCharles-Antoine Couret 		result |= ADVERTISED_1000baseT_Half;
10616cfb3bccSCharles-Antoine Couret 	if (lpa & LPA_FIBER_1000FULL)
10626cfb3bccSCharles-Antoine Couret 		result |= ADVERTISED_1000baseT_Full;
10636cfb3bccSCharles-Antoine Couret 
10646cfb3bccSCharles-Antoine Couret 	return result;
10656cfb3bccSCharles-Antoine Couret }
10666cfb3bccSCharles-Antoine Couret 
10676cfb3bccSCharles-Antoine Couret /**
10686cfb3bccSCharles-Antoine Couret  * marvell_update_link - update link status in real time in @phydev
10696cfb3bccSCharles-Antoine Couret  * @phydev: target phy_device struct
10706cfb3bccSCharles-Antoine Couret  *
10716cfb3bccSCharles-Antoine Couret  * Description: Update the value in phydev->link to reflect the
10726cfb3bccSCharles-Antoine Couret  *   current link value.
10736cfb3bccSCharles-Antoine Couret  */
10746cfb3bccSCharles-Antoine Couret static int marvell_update_link(struct phy_device *phydev, int fiber)
10756cfb3bccSCharles-Antoine Couret {
10766cfb3bccSCharles-Antoine Couret 	int status;
10776cfb3bccSCharles-Antoine Couret 
10786cfb3bccSCharles-Antoine Couret 	/* Use the generic register for copper link, or specific
10790c3439bcSAndrew Lunn 	 * register for fiber case
10800c3439bcSAndrew Lunn 	 */
10816cfb3bccSCharles-Antoine Couret 	if (fiber) {
10826cfb3bccSCharles-Antoine Couret 		status = phy_read(phydev, MII_M1011_PHY_STATUS);
10836cfb3bccSCharles-Antoine Couret 		if (status < 0)
10846cfb3bccSCharles-Antoine Couret 			return status;
10856cfb3bccSCharles-Antoine Couret 
10866cfb3bccSCharles-Antoine Couret 		if ((status & REGISTER_LINK_STATUS) == 0)
10876cfb3bccSCharles-Antoine Couret 			phydev->link = 0;
10886cfb3bccSCharles-Antoine Couret 		else
10896cfb3bccSCharles-Antoine Couret 			phydev->link = 1;
10906cfb3bccSCharles-Antoine Couret 	} else {
10916cfb3bccSCharles-Antoine Couret 		return genphy_update_link(phydev);
10926cfb3bccSCharles-Antoine Couret 	}
10936cfb3bccSCharles-Antoine Couret 
10946cfb3bccSCharles-Antoine Couret 	return 0;
10956cfb3bccSCharles-Antoine Couret }
10966cfb3bccSCharles-Antoine Couret 
1097e1dde8dcSAndrew Lunn static int marvell_read_status_page_an(struct phy_device *phydev,
1098e1dde8dcSAndrew Lunn 				       int fiber)
1099be937f1fSAlexandr Smirnov {
1100e1dde8dcSAndrew Lunn 	int status;
1101be937f1fSAlexandr Smirnov 	int lpa;
1102357cd64cSRussell King 	int lpagb;
1103be937f1fSAlexandr Smirnov 
1104be937f1fSAlexandr Smirnov 	status = phy_read(phydev, MII_M1011_PHY_STATUS);
1105be937f1fSAlexandr Smirnov 	if (status < 0)
1106be937f1fSAlexandr Smirnov 		return status;
1107be937f1fSAlexandr Smirnov 
1108be937f1fSAlexandr Smirnov 	lpa = phy_read(phydev, MII_LPA);
1109be937f1fSAlexandr Smirnov 	if (lpa < 0)
1110be937f1fSAlexandr Smirnov 		return lpa;
1111be937f1fSAlexandr Smirnov 
1112357cd64cSRussell King 	lpagb = phy_read(phydev, MII_STAT1000);
1113357cd64cSRussell King 	if (lpagb < 0)
1114357cd64cSRussell King 		return lpagb;
1115357cd64cSRussell King 
1116be937f1fSAlexandr Smirnov 	if (status & MII_M1011_PHY_STATUS_FULLDUPLEX)
1117be937f1fSAlexandr Smirnov 		phydev->duplex = DUPLEX_FULL;
1118be937f1fSAlexandr Smirnov 	else
1119be937f1fSAlexandr Smirnov 		phydev->duplex = DUPLEX_HALF;
1120be937f1fSAlexandr Smirnov 
1121be937f1fSAlexandr Smirnov 	status = status & MII_M1011_PHY_STATUS_SPD_MASK;
11224f48ed32SAndrew Lunn 	phydev->pause = 0;
11234f48ed32SAndrew Lunn 	phydev->asym_pause = 0;
1124be937f1fSAlexandr Smirnov 
1125be937f1fSAlexandr Smirnov 	switch (status) {
1126be937f1fSAlexandr Smirnov 	case MII_M1011_PHY_STATUS_1000:
1127be937f1fSAlexandr Smirnov 		phydev->speed = SPEED_1000;
1128be937f1fSAlexandr Smirnov 		break;
1129be937f1fSAlexandr Smirnov 
1130be937f1fSAlexandr Smirnov 	case MII_M1011_PHY_STATUS_100:
1131be937f1fSAlexandr Smirnov 		phydev->speed = SPEED_100;
1132be937f1fSAlexandr Smirnov 		break;
1133be937f1fSAlexandr Smirnov 
1134be937f1fSAlexandr Smirnov 	default:
1135be937f1fSAlexandr Smirnov 		phydev->speed = SPEED_10;
1136be937f1fSAlexandr Smirnov 		break;
1137be937f1fSAlexandr Smirnov 	}
1138be937f1fSAlexandr Smirnov 
11396cfb3bccSCharles-Antoine Couret 	if (!fiber) {
1140e1dde8dcSAndrew Lunn 		phydev->lp_advertising =
1141e1dde8dcSAndrew Lunn 			mii_stat1000_to_ethtool_lpa_t(lpagb) |
11426cfb3bccSCharles-Antoine Couret 			mii_lpa_to_ethtool_lpa_t(lpa);
11436cfb3bccSCharles-Antoine Couret 
1144be937f1fSAlexandr Smirnov 		if (phydev->duplex == DUPLEX_FULL) {
1145be937f1fSAlexandr Smirnov 			phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0;
1146be937f1fSAlexandr Smirnov 			phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0;
1147be937f1fSAlexandr Smirnov 		}
1148be937f1fSAlexandr Smirnov 	} else {
11496cfb3bccSCharles-Antoine Couret 		/* The fiber link is only 1000M capable */
11506cfb3bccSCharles-Antoine Couret 		phydev->lp_advertising = fiber_lpa_to_ethtool_lpa_t(lpa);
11516cfb3bccSCharles-Antoine Couret 
11526cfb3bccSCharles-Antoine Couret 		if (phydev->duplex == DUPLEX_FULL) {
11536cfb3bccSCharles-Antoine Couret 			if (!(lpa & LPA_PAUSE_FIBER)) {
11546cfb3bccSCharles-Antoine Couret 				phydev->pause = 0;
11556cfb3bccSCharles-Antoine Couret 				phydev->asym_pause = 0;
11566cfb3bccSCharles-Antoine Couret 			} else if ((lpa & LPA_PAUSE_ASYM_FIBER)) {
11576cfb3bccSCharles-Antoine Couret 				phydev->pause = 1;
11586cfb3bccSCharles-Antoine Couret 				phydev->asym_pause = 1;
11596cfb3bccSCharles-Antoine Couret 			} else {
11606cfb3bccSCharles-Antoine Couret 				phydev->pause = 1;
11616cfb3bccSCharles-Antoine Couret 				phydev->asym_pause = 0;
11626cfb3bccSCharles-Antoine Couret 			}
11636cfb3bccSCharles-Antoine Couret 		}
11646cfb3bccSCharles-Antoine Couret 	}
1165e1dde8dcSAndrew Lunn 	return 0;
1166e1dde8dcSAndrew Lunn }
1167e1dde8dcSAndrew Lunn 
1168e1dde8dcSAndrew Lunn static int marvell_read_status_page_fixed(struct phy_device *phydev)
1169e1dde8dcSAndrew Lunn {
1170be937f1fSAlexandr Smirnov 	int bmcr = phy_read(phydev, MII_BMCR);
1171be937f1fSAlexandr Smirnov 
1172be937f1fSAlexandr Smirnov 	if (bmcr < 0)
1173be937f1fSAlexandr Smirnov 		return bmcr;
1174be937f1fSAlexandr Smirnov 
1175be937f1fSAlexandr Smirnov 	if (bmcr & BMCR_FULLDPLX)
1176be937f1fSAlexandr Smirnov 		phydev->duplex = DUPLEX_FULL;
1177be937f1fSAlexandr Smirnov 	else
1178be937f1fSAlexandr Smirnov 		phydev->duplex = DUPLEX_HALF;
1179be937f1fSAlexandr Smirnov 
1180be937f1fSAlexandr Smirnov 	if (bmcr & BMCR_SPEED1000)
1181be937f1fSAlexandr Smirnov 		phydev->speed = SPEED_1000;
1182be937f1fSAlexandr Smirnov 	else if (bmcr & BMCR_SPEED100)
1183be937f1fSAlexandr Smirnov 		phydev->speed = SPEED_100;
1184be937f1fSAlexandr Smirnov 	else
1185be937f1fSAlexandr Smirnov 		phydev->speed = SPEED_10;
1186be937f1fSAlexandr Smirnov 
11874f48ed32SAndrew Lunn 	phydev->pause = 0;
11884f48ed32SAndrew Lunn 	phydev->asym_pause = 0;
1189357cd64cSRussell King 	phydev->lp_advertising = 0;
1190be937f1fSAlexandr Smirnov 
1191be937f1fSAlexandr Smirnov 	return 0;
1192be937f1fSAlexandr Smirnov }
1193be937f1fSAlexandr Smirnov 
1194e1dde8dcSAndrew Lunn /* marvell_read_status_page
1195e1dde8dcSAndrew Lunn  *
1196e1dde8dcSAndrew Lunn  * Description:
1197e1dde8dcSAndrew Lunn  *   Check the link, then figure out the current state
1198e1dde8dcSAndrew Lunn  *   by comparing what we advertise with what the link partner
1199e1dde8dcSAndrew Lunn  *   advertises.  Start by checking the gigabit possibilities,
1200e1dde8dcSAndrew Lunn  *   then move on to 10/100.
1201e1dde8dcSAndrew Lunn  */
1202e1dde8dcSAndrew Lunn static int marvell_read_status_page(struct phy_device *phydev, int page)
1203e1dde8dcSAndrew Lunn {
1204e1dde8dcSAndrew Lunn 	int fiber;
1205e1dde8dcSAndrew Lunn 	int err;
1206e1dde8dcSAndrew Lunn 
1207e1dde8dcSAndrew Lunn 	/* Detect and update the link, but return if there
1208e1dde8dcSAndrew Lunn 	 * was an error
1209e1dde8dcSAndrew Lunn 	 */
121052295666SAndrew Lunn 	if (page == MII_MARVELL_FIBER_PAGE)
1211e1dde8dcSAndrew Lunn 		fiber = 1;
1212e1dde8dcSAndrew Lunn 	else
1213e1dde8dcSAndrew Lunn 		fiber = 0;
1214e1dde8dcSAndrew Lunn 
1215e1dde8dcSAndrew Lunn 	err = marvell_update_link(phydev, fiber);
1216e1dde8dcSAndrew Lunn 	if (err)
1217e1dde8dcSAndrew Lunn 		return err;
1218e1dde8dcSAndrew Lunn 
1219e1dde8dcSAndrew Lunn 	if (phydev->autoneg == AUTONEG_ENABLE)
1220e1dde8dcSAndrew Lunn 		err = marvell_read_status_page_an(phydev, fiber);
1221e1dde8dcSAndrew Lunn 	else
1222e1dde8dcSAndrew Lunn 		err = marvell_read_status_page_fixed(phydev);
1223e1dde8dcSAndrew Lunn 
1224e1dde8dcSAndrew Lunn 	return err;
1225e1dde8dcSAndrew Lunn }
1226e1dde8dcSAndrew Lunn 
12276cfb3bccSCharles-Antoine Couret /* marvell_read_status
12286cfb3bccSCharles-Antoine Couret  *
12296cfb3bccSCharles-Antoine Couret  * Some Marvell's phys have two modes: fiber and copper.
12306cfb3bccSCharles-Antoine Couret  * Both need status checked.
12316cfb3bccSCharles-Antoine Couret  * Description:
12326cfb3bccSCharles-Antoine Couret  *   First, check the fiber link and status.
12336cfb3bccSCharles-Antoine Couret  *   If the fiber link is down, check the copper link and status which
12346cfb3bccSCharles-Antoine Couret  *   will be the default value if both link are down.
12356cfb3bccSCharles-Antoine Couret  */
12366cfb3bccSCharles-Antoine Couret static int marvell_read_status(struct phy_device *phydev)
12376cfb3bccSCharles-Antoine Couret {
12386cfb3bccSCharles-Antoine Couret 	int err;
12396cfb3bccSCharles-Antoine Couret 
12406cfb3bccSCharles-Antoine Couret 	/* Check the fiber mode first */
1241a13c0652SRussell King 	if (phydev->supported & SUPPORTED_FIBRE &&
1242a13c0652SRussell King 	    phydev->interface != PHY_INTERFACE_MODE_SGMII) {
124352295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
12446cfb3bccSCharles-Antoine Couret 		if (err < 0)
12456cfb3bccSCharles-Antoine Couret 			goto error;
12466cfb3bccSCharles-Antoine Couret 
124752295666SAndrew Lunn 		err = marvell_read_status_page(phydev, MII_MARVELL_FIBER_PAGE);
12486cfb3bccSCharles-Antoine Couret 		if (err < 0)
12496cfb3bccSCharles-Antoine Couret 			goto error;
12506cfb3bccSCharles-Antoine Couret 
12510c3439bcSAndrew Lunn 		/* If the fiber link is up, it is the selected and
12520c3439bcSAndrew Lunn 		 * used link. In this case, we need to stay in the
12530c3439bcSAndrew Lunn 		 * fiber page. Please to be careful about that, avoid
12540c3439bcSAndrew Lunn 		 * to restore Copper page in other functions which
12550c3439bcSAndrew Lunn 		 * could break the behaviour for some fiber phy like
12560c3439bcSAndrew Lunn 		 * 88E1512.
12570c3439bcSAndrew Lunn 		 */
12586cfb3bccSCharles-Antoine Couret 		if (phydev->link)
12596cfb3bccSCharles-Antoine Couret 			return 0;
12606cfb3bccSCharles-Antoine Couret 
12616cfb3bccSCharles-Antoine Couret 		/* If fiber link is down, check and save copper mode state */
126252295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
12636cfb3bccSCharles-Antoine Couret 		if (err < 0)
12646cfb3bccSCharles-Antoine Couret 			goto error;
12656cfb3bccSCharles-Antoine Couret 	}
12666cfb3bccSCharles-Antoine Couret 
126752295666SAndrew Lunn 	return marvell_read_status_page(phydev, MII_MARVELL_COPPER_PAGE);
12686cfb3bccSCharles-Antoine Couret 
12696cfb3bccSCharles-Antoine Couret error:
127052295666SAndrew Lunn 	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
12716cfb3bccSCharles-Antoine Couret 	return err;
12726cfb3bccSCharles-Antoine Couret }
12733758be3dSCharles-Antoine Couret 
12743758be3dSCharles-Antoine Couret /* marvell_suspend
12753758be3dSCharles-Antoine Couret  *
12763758be3dSCharles-Antoine Couret  * Some Marvell's phys have two modes: fiber and copper.
12773758be3dSCharles-Antoine Couret  * Both need to be suspended
12783758be3dSCharles-Antoine Couret  */
12793758be3dSCharles-Antoine Couret static int marvell_suspend(struct phy_device *phydev)
12803758be3dSCharles-Antoine Couret {
12813758be3dSCharles-Antoine Couret 	int err;
12823758be3dSCharles-Antoine Couret 
12833758be3dSCharles-Antoine Couret 	/* Suspend the fiber mode first */
12843758be3dSCharles-Antoine Couret 	if (!(phydev->supported & SUPPORTED_FIBRE)) {
128552295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
12863758be3dSCharles-Antoine Couret 		if (err < 0)
12873758be3dSCharles-Antoine Couret 			goto error;
12883758be3dSCharles-Antoine Couret 
12893758be3dSCharles-Antoine Couret 		/* With the page set, use the generic suspend */
12903758be3dSCharles-Antoine Couret 		err = genphy_suspend(phydev);
12913758be3dSCharles-Antoine Couret 		if (err < 0)
12923758be3dSCharles-Antoine Couret 			goto error;
12933758be3dSCharles-Antoine Couret 
12943758be3dSCharles-Antoine Couret 		/* Then, the copper link */
129552295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
12963758be3dSCharles-Antoine Couret 		if (err < 0)
12973758be3dSCharles-Antoine Couret 			goto error;
12983758be3dSCharles-Antoine Couret 	}
12993758be3dSCharles-Antoine Couret 
13003758be3dSCharles-Antoine Couret 	/* With the page set, use the generic suspend */
13013758be3dSCharles-Antoine Couret 	return genphy_suspend(phydev);
13023758be3dSCharles-Antoine Couret 
13033758be3dSCharles-Antoine Couret error:
130452295666SAndrew Lunn 	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
13053758be3dSCharles-Antoine Couret 	return err;
13063758be3dSCharles-Antoine Couret }
13073758be3dSCharles-Antoine Couret 
13083758be3dSCharles-Antoine Couret /* marvell_resume
13093758be3dSCharles-Antoine Couret  *
13103758be3dSCharles-Antoine Couret  * Some Marvell's phys have two modes: fiber and copper.
13113758be3dSCharles-Antoine Couret  * Both need to be resumed
13123758be3dSCharles-Antoine Couret  */
13133758be3dSCharles-Antoine Couret static int marvell_resume(struct phy_device *phydev)
13143758be3dSCharles-Antoine Couret {
13153758be3dSCharles-Antoine Couret 	int err;
13163758be3dSCharles-Antoine Couret 
13173758be3dSCharles-Antoine Couret 	/* Resume the fiber mode first */
13183758be3dSCharles-Antoine Couret 	if (!(phydev->supported & SUPPORTED_FIBRE)) {
131952295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
13203758be3dSCharles-Antoine Couret 		if (err < 0)
13213758be3dSCharles-Antoine Couret 			goto error;
13223758be3dSCharles-Antoine Couret 
13233758be3dSCharles-Antoine Couret 		/* With the page set, use the generic resume */
13243758be3dSCharles-Antoine Couret 		err = genphy_resume(phydev);
13253758be3dSCharles-Antoine Couret 		if (err < 0)
13263758be3dSCharles-Antoine Couret 			goto error;
13273758be3dSCharles-Antoine Couret 
13283758be3dSCharles-Antoine Couret 		/* Then, the copper link */
132952295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
13303758be3dSCharles-Antoine Couret 		if (err < 0)
13313758be3dSCharles-Antoine Couret 			goto error;
13323758be3dSCharles-Antoine Couret 	}
13333758be3dSCharles-Antoine Couret 
13343758be3dSCharles-Antoine Couret 	/* With the page set, use the generic resume */
13353758be3dSCharles-Antoine Couret 	return genphy_resume(phydev);
13363758be3dSCharles-Antoine Couret 
13373758be3dSCharles-Antoine Couret error:
133852295666SAndrew Lunn 	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
13393758be3dSCharles-Antoine Couret 	return err;
13403758be3dSCharles-Antoine Couret }
13413758be3dSCharles-Antoine Couret 
13426b358aedSSebastian Hesselbarth static int marvell_aneg_done(struct phy_device *phydev)
13436b358aedSSebastian Hesselbarth {
13446b358aedSSebastian Hesselbarth 	int retval = phy_read(phydev, MII_M1011_PHY_STATUS);
1345e69d9ed4SAndrew Lunn 
13466b358aedSSebastian Hesselbarth 	return (retval < 0) ? retval : (retval & MII_M1011_PHY_STATUS_RESOLVED);
13476b358aedSSebastian Hesselbarth }
13486b358aedSSebastian Hesselbarth 
1349dcd07be3SAnatolij Gustschin static int m88e1121_did_interrupt(struct phy_device *phydev)
1350dcd07be3SAnatolij Gustschin {
1351dcd07be3SAnatolij Gustschin 	int imask;
1352dcd07be3SAnatolij Gustschin 
1353dcd07be3SAnatolij Gustschin 	imask = phy_read(phydev, MII_M1011_IEVENT);
1354dcd07be3SAnatolij Gustschin 
1355dcd07be3SAnatolij Gustschin 	if (imask & MII_M1011_IMASK_INIT)
1356dcd07be3SAnatolij Gustschin 		return 1;
1357dcd07be3SAnatolij Gustschin 
1358dcd07be3SAnatolij Gustschin 	return 0;
1359dcd07be3SAnatolij Gustschin }
1360dcd07be3SAnatolij Gustschin 
136123beb38fSAndrew Lunn static void m88e1318_get_wol(struct phy_device *phydev,
136223beb38fSAndrew Lunn 			     struct ethtool_wolinfo *wol)
13633871c387SMichael Stapelberg {
1364424ca4c5SRussell King 	int oldpage, ret = 0;
1365424ca4c5SRussell King 
13663871c387SMichael Stapelberg 	wol->supported = WAKE_MAGIC;
13673871c387SMichael Stapelberg 	wol->wolopts = 0;
13683871c387SMichael Stapelberg 
1369424ca4c5SRussell King 	oldpage = phy_select_page(phydev, MII_MARVELL_WOL_PAGE);
1370424ca4c5SRussell King 	if (oldpage < 0)
1371424ca4c5SRussell King 		goto error;
13723871c387SMichael Stapelberg 
1373424ca4c5SRussell King 	ret = __phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL);
1374424ca4c5SRussell King 	if (ret & MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE)
13753871c387SMichael Stapelberg 		wol->wolopts |= WAKE_MAGIC;
13763871c387SMichael Stapelberg 
1377424ca4c5SRussell King error:
1378424ca4c5SRussell King 	phy_restore_page(phydev, oldpage, ret);
13793871c387SMichael Stapelberg }
13803871c387SMichael Stapelberg 
138123beb38fSAndrew Lunn static int m88e1318_set_wol(struct phy_device *phydev,
138223beb38fSAndrew Lunn 			    struct ethtool_wolinfo *wol)
13833871c387SMichael Stapelberg {
1384424ca4c5SRussell King 	int err = 0, oldpage;
13853871c387SMichael Stapelberg 
1386424ca4c5SRussell King 	oldpage = phy_save_page(phydev);
1387424ca4c5SRussell King 	if (oldpage < 0)
1388424ca4c5SRussell King 		goto error;
13893871c387SMichael Stapelberg 
13903871c387SMichael Stapelberg 	if (wol->wolopts & WAKE_MAGIC) {
13913871c387SMichael Stapelberg 		/* Explicitly switch to page 0x00, just to be sure */
1392424ca4c5SRussell King 		err = marvell_write_page(phydev, MII_MARVELL_COPPER_PAGE);
13933871c387SMichael Stapelberg 		if (err < 0)
1394424ca4c5SRussell King 			goto error;
13953871c387SMichael Stapelberg 
13963871c387SMichael Stapelberg 		/* Enable the WOL interrupt */
1397424ca4c5SRussell King 		err = __phy_modify(phydev, MII_88E1318S_PHY_CSIER, 0,
1398424ca4c5SRussell King 				   MII_88E1318S_PHY_CSIER_WOL_EIE);
13993871c387SMichael Stapelberg 		if (err < 0)
1400424ca4c5SRussell King 			goto error;
14013871c387SMichael Stapelberg 
1402424ca4c5SRussell King 		err = marvell_write_page(phydev, MII_MARVELL_LED_PAGE);
14033871c387SMichael Stapelberg 		if (err < 0)
1404424ca4c5SRussell King 			goto error;
14053871c387SMichael Stapelberg 
14063871c387SMichael Stapelberg 		/* Setup LED[2] as interrupt pin (active low) */
1407424ca4c5SRussell King 		err = __phy_modify(phydev, MII_88E1318S_PHY_LED_TCR,
1408f102852fSRussell King 				   MII_88E1318S_PHY_LED_TCR_FORCE_INT,
1409424ca4c5SRussell King 				   MII_88E1318S_PHY_LED_TCR_INTn_ENABLE |
1410424ca4c5SRussell King 				   MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW);
14113871c387SMichael Stapelberg 		if (err < 0)
1412424ca4c5SRussell King 			goto error;
14133871c387SMichael Stapelberg 
1414424ca4c5SRussell King 		err = marvell_write_page(phydev, MII_MARVELL_WOL_PAGE);
14153871c387SMichael Stapelberg 		if (err < 0)
1416424ca4c5SRussell King 			goto error;
14173871c387SMichael Stapelberg 
14183871c387SMichael Stapelberg 		/* Store the device address for the magic packet */
1419424ca4c5SRussell King 		err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD2,
14203871c387SMichael Stapelberg 				((phydev->attached_dev->dev_addr[5] << 8) |
14213871c387SMichael Stapelberg 				 phydev->attached_dev->dev_addr[4]));
14223871c387SMichael Stapelberg 		if (err < 0)
1423424ca4c5SRussell King 			goto error;
1424424ca4c5SRussell King 		err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD1,
14253871c387SMichael Stapelberg 				((phydev->attached_dev->dev_addr[3] << 8) |
14263871c387SMichael Stapelberg 				 phydev->attached_dev->dev_addr[2]));
14273871c387SMichael Stapelberg 		if (err < 0)
1428424ca4c5SRussell King 			goto error;
1429424ca4c5SRussell King 		err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD0,
14303871c387SMichael Stapelberg 				((phydev->attached_dev->dev_addr[1] << 8) |
14313871c387SMichael Stapelberg 				 phydev->attached_dev->dev_addr[0]));
14323871c387SMichael Stapelberg 		if (err < 0)
1433424ca4c5SRussell King 			goto error;
14343871c387SMichael Stapelberg 
14353871c387SMichael Stapelberg 		/* Clear WOL status and enable magic packet matching */
1436424ca4c5SRussell King 		err = __phy_modify(phydev, MII_88E1318S_PHY_WOL_CTRL, 0,
1437424ca4c5SRussell King 				   MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS |
1438424ca4c5SRussell King 				   MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE);
14393871c387SMichael Stapelberg 		if (err < 0)
1440424ca4c5SRussell King 			goto error;
14413871c387SMichael Stapelberg 	} else {
1442424ca4c5SRussell King 		err = marvell_write_page(phydev, MII_MARVELL_WOL_PAGE);
14433871c387SMichael Stapelberg 		if (err < 0)
1444424ca4c5SRussell King 			goto error;
14453871c387SMichael Stapelberg 
14463871c387SMichael Stapelberg 		/* Clear WOL status and disable magic packet matching */
1447424ca4c5SRussell King 		err = __phy_modify(phydev, MII_88E1318S_PHY_WOL_CTRL,
1448f102852fSRussell King 				   MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE,
1449424ca4c5SRussell King 				   MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS);
14503871c387SMichael Stapelberg 		if (err < 0)
1451424ca4c5SRussell King 			goto error;
14523871c387SMichael Stapelberg 	}
14533871c387SMichael Stapelberg 
1454424ca4c5SRussell King error:
1455424ca4c5SRussell King 	return phy_restore_page(phydev, oldpage, err);
14563871c387SMichael Stapelberg }
14573871c387SMichael Stapelberg 
1458d2fa47d9SAndrew Lunn static int marvell_get_sset_count(struct phy_device *phydev)
1459d2fa47d9SAndrew Lunn {
14602170fef7SCharles-Antoine Couret 	if (phydev->supported & SUPPORTED_FIBRE)
1461d2fa47d9SAndrew Lunn 		return ARRAY_SIZE(marvell_hw_stats);
14622170fef7SCharles-Antoine Couret 	else
14632170fef7SCharles-Antoine Couret 		return ARRAY_SIZE(marvell_hw_stats) - NB_FIBER_STATS;
1464d2fa47d9SAndrew Lunn }
1465d2fa47d9SAndrew Lunn 
1466d2fa47d9SAndrew Lunn static void marvell_get_strings(struct phy_device *phydev, u8 *data)
1467d2fa47d9SAndrew Lunn {
1468d2fa47d9SAndrew Lunn 	int i;
1469d2fa47d9SAndrew Lunn 
1470d2fa47d9SAndrew Lunn 	for (i = 0; i < ARRAY_SIZE(marvell_hw_stats); i++) {
147198409b2bSFlorian Fainelli 		strlcpy(data + i * ETH_GSTRING_LEN,
1472d2fa47d9SAndrew Lunn 			marvell_hw_stats[i].string, ETH_GSTRING_LEN);
1473d2fa47d9SAndrew Lunn 	}
1474d2fa47d9SAndrew Lunn }
1475d2fa47d9SAndrew Lunn 
1476d2fa47d9SAndrew Lunn #ifndef UINT64_MAX
1477d2fa47d9SAndrew Lunn #define UINT64_MAX		(u64)(~((u64)0))
1478d2fa47d9SAndrew Lunn #endif
1479d2fa47d9SAndrew Lunn static u64 marvell_get_stat(struct phy_device *phydev, int i)
1480d2fa47d9SAndrew Lunn {
1481d2fa47d9SAndrew Lunn 	struct marvell_hw_stat stat = marvell_hw_stats[i];
1482d2fa47d9SAndrew Lunn 	struct marvell_priv *priv = phydev->priv;
1483424ca4c5SRussell King 	int val;
1484321b4d4bSAndrew Lunn 	u64 ret;
1485d2fa47d9SAndrew Lunn 
1486424ca4c5SRussell King 	val = phy_read_paged(phydev, stat.page, stat.reg);
1487d2fa47d9SAndrew Lunn 	if (val < 0) {
1488321b4d4bSAndrew Lunn 		ret = UINT64_MAX;
1489d2fa47d9SAndrew Lunn 	} else {
1490d2fa47d9SAndrew Lunn 		val = val & ((1 << stat.bits) - 1);
1491d2fa47d9SAndrew Lunn 		priv->stats[i] += val;
1492321b4d4bSAndrew Lunn 		ret = priv->stats[i];
1493d2fa47d9SAndrew Lunn 	}
1494d2fa47d9SAndrew Lunn 
1495321b4d4bSAndrew Lunn 	return ret;
1496d2fa47d9SAndrew Lunn }
1497d2fa47d9SAndrew Lunn 
1498d2fa47d9SAndrew Lunn static void marvell_get_stats(struct phy_device *phydev,
1499d2fa47d9SAndrew Lunn 			      struct ethtool_stats *stats, u64 *data)
1500d2fa47d9SAndrew Lunn {
1501d2fa47d9SAndrew Lunn 	int i;
1502d2fa47d9SAndrew Lunn 
1503d2fa47d9SAndrew Lunn 	for (i = 0; i < ARRAY_SIZE(marvell_hw_stats); i++)
1504d2fa47d9SAndrew Lunn 		data[i] = marvell_get_stat(phydev, i);
1505d2fa47d9SAndrew Lunn }
1506d2fa47d9SAndrew Lunn 
15070b04680fSAndrew Lunn #ifdef CONFIG_HWMON
15080b04680fSAndrew Lunn static int m88e1121_get_temp(struct phy_device *phydev, long *temp)
15090b04680fSAndrew Lunn {
1510975b388cSAndrew Lunn 	int oldpage;
1511424ca4c5SRussell King 	int ret = 0;
15120b04680fSAndrew Lunn 	int val;
15130b04680fSAndrew Lunn 
15140b04680fSAndrew Lunn 	*temp = 0;
15150b04680fSAndrew Lunn 
1516424ca4c5SRussell King 	oldpage = phy_select_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
1517424ca4c5SRussell King 	if (oldpage < 0)
1518424ca4c5SRussell King 		goto error;
1519975b388cSAndrew Lunn 
15200b04680fSAndrew Lunn 	/* Enable temperature sensor */
1521424ca4c5SRussell King 	ret = __phy_read(phydev, MII_88E1121_MISC_TEST);
15220b04680fSAndrew Lunn 	if (ret < 0)
15230b04680fSAndrew Lunn 		goto error;
15240b04680fSAndrew Lunn 
1525424ca4c5SRussell King 	ret = __phy_write(phydev, MII_88E1121_MISC_TEST,
15260b04680fSAndrew Lunn 			  ret | MII_88E1121_MISC_TEST_TEMP_SENSOR_EN);
15270b04680fSAndrew Lunn 	if (ret < 0)
15280b04680fSAndrew Lunn 		goto error;
15290b04680fSAndrew Lunn 
15300b04680fSAndrew Lunn 	/* Wait for temperature to stabilize */
15310b04680fSAndrew Lunn 	usleep_range(10000, 12000);
15320b04680fSAndrew Lunn 
1533424ca4c5SRussell King 	val = __phy_read(phydev, MII_88E1121_MISC_TEST);
15340b04680fSAndrew Lunn 	if (val < 0) {
15350b04680fSAndrew Lunn 		ret = val;
15360b04680fSAndrew Lunn 		goto error;
15370b04680fSAndrew Lunn 	}
15380b04680fSAndrew Lunn 
15390b04680fSAndrew Lunn 	/* Disable temperature sensor */
1540424ca4c5SRussell King 	ret = __phy_write(phydev, MII_88E1121_MISC_TEST,
15410b04680fSAndrew Lunn 			  ret & ~MII_88E1121_MISC_TEST_TEMP_SENSOR_EN);
15420b04680fSAndrew Lunn 	if (ret < 0)
15430b04680fSAndrew Lunn 		goto error;
15440b04680fSAndrew Lunn 
15450b04680fSAndrew Lunn 	*temp = ((val & MII_88E1121_MISC_TEST_TEMP_MASK) - 5) * 5000;
15460b04680fSAndrew Lunn 
15470b04680fSAndrew Lunn error:
1548424ca4c5SRussell King 	return phy_restore_page(phydev, oldpage, ret);
15490b04680fSAndrew Lunn }
15500b04680fSAndrew Lunn 
15510b04680fSAndrew Lunn static int m88e1121_hwmon_read(struct device *dev,
15520b04680fSAndrew Lunn 			       enum hwmon_sensor_types type,
15530b04680fSAndrew Lunn 			       u32 attr, int channel, long *temp)
15540b04680fSAndrew Lunn {
15550b04680fSAndrew Lunn 	struct phy_device *phydev = dev_get_drvdata(dev);
15560b04680fSAndrew Lunn 	int err;
15570b04680fSAndrew Lunn 
15580b04680fSAndrew Lunn 	switch (attr) {
15590b04680fSAndrew Lunn 	case hwmon_temp_input:
15600b04680fSAndrew Lunn 		err = m88e1121_get_temp(phydev, temp);
15610b04680fSAndrew Lunn 		break;
15620b04680fSAndrew Lunn 	default:
15630b04680fSAndrew Lunn 		return -EOPNOTSUPP;
15640b04680fSAndrew Lunn 	}
15650b04680fSAndrew Lunn 
15660b04680fSAndrew Lunn 	return err;
15670b04680fSAndrew Lunn }
15680b04680fSAndrew Lunn 
15690b04680fSAndrew Lunn static umode_t m88e1121_hwmon_is_visible(const void *data,
15700b04680fSAndrew Lunn 					 enum hwmon_sensor_types type,
15710b04680fSAndrew Lunn 					 u32 attr, int channel)
15720b04680fSAndrew Lunn {
15730b04680fSAndrew Lunn 	if (type != hwmon_temp)
15740b04680fSAndrew Lunn 		return 0;
15750b04680fSAndrew Lunn 
15760b04680fSAndrew Lunn 	switch (attr) {
15770b04680fSAndrew Lunn 	case hwmon_temp_input:
15780b04680fSAndrew Lunn 		return 0444;
15790b04680fSAndrew Lunn 	default:
15800b04680fSAndrew Lunn 		return 0;
15810b04680fSAndrew Lunn 	}
15820b04680fSAndrew Lunn }
15830b04680fSAndrew Lunn 
15840b04680fSAndrew Lunn static u32 m88e1121_hwmon_chip_config[] = {
15850b04680fSAndrew Lunn 	HWMON_C_REGISTER_TZ,
15860b04680fSAndrew Lunn 	0
15870b04680fSAndrew Lunn };
15880b04680fSAndrew Lunn 
15890b04680fSAndrew Lunn static const struct hwmon_channel_info m88e1121_hwmon_chip = {
15900b04680fSAndrew Lunn 	.type = hwmon_chip,
15910b04680fSAndrew Lunn 	.config = m88e1121_hwmon_chip_config,
15920b04680fSAndrew Lunn };
15930b04680fSAndrew Lunn 
15940b04680fSAndrew Lunn static u32 m88e1121_hwmon_temp_config[] = {
15950b04680fSAndrew Lunn 	HWMON_T_INPUT,
15960b04680fSAndrew Lunn 	0
15970b04680fSAndrew Lunn };
15980b04680fSAndrew Lunn 
15990b04680fSAndrew Lunn static const struct hwmon_channel_info m88e1121_hwmon_temp = {
16000b04680fSAndrew Lunn 	.type = hwmon_temp,
16010b04680fSAndrew Lunn 	.config = m88e1121_hwmon_temp_config,
16020b04680fSAndrew Lunn };
16030b04680fSAndrew Lunn 
16040b04680fSAndrew Lunn static const struct hwmon_channel_info *m88e1121_hwmon_info[] = {
16050b04680fSAndrew Lunn 	&m88e1121_hwmon_chip,
16060b04680fSAndrew Lunn 	&m88e1121_hwmon_temp,
16070b04680fSAndrew Lunn 	NULL
16080b04680fSAndrew Lunn };
16090b04680fSAndrew Lunn 
16100b04680fSAndrew Lunn static const struct hwmon_ops m88e1121_hwmon_hwmon_ops = {
16110b04680fSAndrew Lunn 	.is_visible = m88e1121_hwmon_is_visible,
16120b04680fSAndrew Lunn 	.read = m88e1121_hwmon_read,
16130b04680fSAndrew Lunn };
16140b04680fSAndrew Lunn 
16150b04680fSAndrew Lunn static const struct hwmon_chip_info m88e1121_hwmon_chip_info = {
16160b04680fSAndrew Lunn 	.ops = &m88e1121_hwmon_hwmon_ops,
16170b04680fSAndrew Lunn 	.info = m88e1121_hwmon_info,
16180b04680fSAndrew Lunn };
16190b04680fSAndrew Lunn 
16200b04680fSAndrew Lunn static int m88e1510_get_temp(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_88E1510_TEMP_SENSOR);
16280b04680fSAndrew Lunn 	if (ret < 0)
1629424ca4c5SRussell King 		return ret;
16300b04680fSAndrew Lunn 
16310b04680fSAndrew Lunn 	*temp = ((ret & MII_88E1510_TEMP_SENSOR_MASK) - 25) * 1000;
16320b04680fSAndrew Lunn 
1633424ca4c5SRussell King 	return 0;
16340b04680fSAndrew Lunn }
16350b04680fSAndrew Lunn 
1636f0a45816SColin Ian King static int m88e1510_get_temp_critical(struct phy_device *phydev, long *temp)
16370b04680fSAndrew Lunn {
16380b04680fSAndrew Lunn 	int ret;
16390b04680fSAndrew Lunn 
16400b04680fSAndrew Lunn 	*temp = 0;
16410b04680fSAndrew Lunn 
1642424ca4c5SRussell King 	ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
1643424ca4c5SRussell King 			     MII_88E1121_MISC_TEST);
16440b04680fSAndrew Lunn 	if (ret < 0)
1645424ca4c5SRussell King 		return ret;
16460b04680fSAndrew Lunn 
16470b04680fSAndrew Lunn 	*temp = (((ret & MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK) >>
16480b04680fSAndrew Lunn 		  MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT) * 5) - 25;
16490b04680fSAndrew Lunn 	/* convert to mC */
16500b04680fSAndrew Lunn 	*temp *= 1000;
16510b04680fSAndrew Lunn 
1652424ca4c5SRussell King 	return 0;
16530b04680fSAndrew Lunn }
16540b04680fSAndrew Lunn 
1655f0a45816SColin Ian King static int m88e1510_set_temp_critical(struct phy_device *phydev, long temp)
16560b04680fSAndrew Lunn {
16570b04680fSAndrew Lunn 	temp = temp / 1000;
16580b04680fSAndrew Lunn 	temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
16590b04680fSAndrew Lunn 
1660424ca4c5SRussell King 	return phy_modify_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
1661424ca4c5SRussell King 				MII_88E1121_MISC_TEST,
1662424ca4c5SRussell King 				MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK,
1663424ca4c5SRussell King 				temp << MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT);
16640b04680fSAndrew Lunn }
16650b04680fSAndrew Lunn 
1666f0a45816SColin Ian King static int m88e1510_get_temp_alarm(struct phy_device *phydev, long *alarm)
16670b04680fSAndrew Lunn {
16680b04680fSAndrew Lunn 	int ret;
16690b04680fSAndrew Lunn 
16700b04680fSAndrew Lunn 	*alarm = false;
16710b04680fSAndrew Lunn 
1672424ca4c5SRussell King 	ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
1673424ca4c5SRussell King 			     MII_88E1121_MISC_TEST);
16740b04680fSAndrew Lunn 	if (ret < 0)
1675424ca4c5SRussell King 		return ret;
1676424ca4c5SRussell King 
16770b04680fSAndrew Lunn 	*alarm = !!(ret & MII_88E1510_MISC_TEST_TEMP_IRQ);
16780b04680fSAndrew Lunn 
1679424ca4c5SRussell King 	return 0;
16800b04680fSAndrew Lunn }
16810b04680fSAndrew Lunn 
16820b04680fSAndrew Lunn static int m88e1510_hwmon_read(struct device *dev,
16830b04680fSAndrew Lunn 			       enum hwmon_sensor_types type,
16840b04680fSAndrew Lunn 			       u32 attr, int channel, long *temp)
16850b04680fSAndrew Lunn {
16860b04680fSAndrew Lunn 	struct phy_device *phydev = dev_get_drvdata(dev);
16870b04680fSAndrew Lunn 	int err;
16880b04680fSAndrew Lunn 
16890b04680fSAndrew Lunn 	switch (attr) {
16900b04680fSAndrew Lunn 	case hwmon_temp_input:
16910b04680fSAndrew Lunn 		err = m88e1510_get_temp(phydev, temp);
16920b04680fSAndrew Lunn 		break;
16930b04680fSAndrew Lunn 	case hwmon_temp_crit:
16940b04680fSAndrew Lunn 		err = m88e1510_get_temp_critical(phydev, temp);
16950b04680fSAndrew Lunn 		break;
16960b04680fSAndrew Lunn 	case hwmon_temp_max_alarm:
16970b04680fSAndrew Lunn 		err = m88e1510_get_temp_alarm(phydev, temp);
16980b04680fSAndrew Lunn 		break;
16990b04680fSAndrew Lunn 	default:
17000b04680fSAndrew Lunn 		return -EOPNOTSUPP;
17010b04680fSAndrew Lunn 	}
17020b04680fSAndrew Lunn 
17030b04680fSAndrew Lunn 	return err;
17040b04680fSAndrew Lunn }
17050b04680fSAndrew Lunn 
17060b04680fSAndrew Lunn static int m88e1510_hwmon_write(struct device *dev,
17070b04680fSAndrew Lunn 				enum hwmon_sensor_types type,
17080b04680fSAndrew Lunn 				u32 attr, int channel, long temp)
17090b04680fSAndrew Lunn {
17100b04680fSAndrew Lunn 	struct phy_device *phydev = dev_get_drvdata(dev);
17110b04680fSAndrew Lunn 	int err;
17120b04680fSAndrew Lunn 
17130b04680fSAndrew Lunn 	switch (attr) {
17140b04680fSAndrew Lunn 	case hwmon_temp_crit:
17150b04680fSAndrew Lunn 		err = m88e1510_set_temp_critical(phydev, temp);
17160b04680fSAndrew Lunn 		break;
17170b04680fSAndrew Lunn 	default:
17180b04680fSAndrew Lunn 		return -EOPNOTSUPP;
17190b04680fSAndrew Lunn 	}
17200b04680fSAndrew Lunn 	return err;
17210b04680fSAndrew Lunn }
17220b04680fSAndrew Lunn 
17230b04680fSAndrew Lunn static umode_t m88e1510_hwmon_is_visible(const void *data,
17240b04680fSAndrew Lunn 					 enum hwmon_sensor_types type,
17250b04680fSAndrew Lunn 					 u32 attr, int channel)
17260b04680fSAndrew Lunn {
17270b04680fSAndrew Lunn 	if (type != hwmon_temp)
17280b04680fSAndrew Lunn 		return 0;
17290b04680fSAndrew Lunn 
17300b04680fSAndrew Lunn 	switch (attr) {
17310b04680fSAndrew Lunn 	case hwmon_temp_input:
17320b04680fSAndrew Lunn 	case hwmon_temp_max_alarm:
17330b04680fSAndrew Lunn 		return 0444;
17340b04680fSAndrew Lunn 	case hwmon_temp_crit:
17350b04680fSAndrew Lunn 		return 0644;
17360b04680fSAndrew Lunn 	default:
17370b04680fSAndrew Lunn 		return 0;
17380b04680fSAndrew Lunn 	}
17390b04680fSAndrew Lunn }
17400b04680fSAndrew Lunn 
17410b04680fSAndrew Lunn static u32 m88e1510_hwmon_temp_config[] = {
17420b04680fSAndrew Lunn 	HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_MAX_ALARM,
17430b04680fSAndrew Lunn 	0
17440b04680fSAndrew Lunn };
17450b04680fSAndrew Lunn 
17460b04680fSAndrew Lunn static const struct hwmon_channel_info m88e1510_hwmon_temp = {
17470b04680fSAndrew Lunn 	.type = hwmon_temp,
17480b04680fSAndrew Lunn 	.config = m88e1510_hwmon_temp_config,
17490b04680fSAndrew Lunn };
17500b04680fSAndrew Lunn 
17510b04680fSAndrew Lunn static const struct hwmon_channel_info *m88e1510_hwmon_info[] = {
17520b04680fSAndrew Lunn 	&m88e1121_hwmon_chip,
17530b04680fSAndrew Lunn 	&m88e1510_hwmon_temp,
17540b04680fSAndrew Lunn 	NULL
17550b04680fSAndrew Lunn };
17560b04680fSAndrew Lunn 
17570b04680fSAndrew Lunn static const struct hwmon_ops m88e1510_hwmon_hwmon_ops = {
17580b04680fSAndrew Lunn 	.is_visible = m88e1510_hwmon_is_visible,
17590b04680fSAndrew Lunn 	.read = m88e1510_hwmon_read,
17600b04680fSAndrew Lunn 	.write = m88e1510_hwmon_write,
17610b04680fSAndrew Lunn };
17620b04680fSAndrew Lunn 
17630b04680fSAndrew Lunn static const struct hwmon_chip_info m88e1510_hwmon_chip_info = {
17640b04680fSAndrew Lunn 	.ops = &m88e1510_hwmon_hwmon_ops,
17650b04680fSAndrew Lunn 	.info = m88e1510_hwmon_info,
17660b04680fSAndrew Lunn };
17670b04680fSAndrew Lunn 
1768fee2d546SAndrew Lunn static int m88e6390_get_temp(struct phy_device *phydev, long *temp)
1769fee2d546SAndrew Lunn {
1770fee2d546SAndrew Lunn 	int sum = 0;
1771fee2d546SAndrew Lunn 	int oldpage;
1772fee2d546SAndrew Lunn 	int ret = 0;
1773fee2d546SAndrew Lunn 	int i;
1774fee2d546SAndrew Lunn 
1775fee2d546SAndrew Lunn 	*temp = 0;
1776fee2d546SAndrew Lunn 
1777fee2d546SAndrew Lunn 	oldpage = phy_select_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
1778fee2d546SAndrew Lunn 	if (oldpage < 0)
1779fee2d546SAndrew Lunn 		goto error;
1780fee2d546SAndrew Lunn 
1781fee2d546SAndrew Lunn 	/* Enable temperature sensor */
1782fee2d546SAndrew Lunn 	ret = __phy_read(phydev, MII_88E6390_MISC_TEST);
1783fee2d546SAndrew Lunn 	if (ret < 0)
1784fee2d546SAndrew Lunn 		goto error;
1785fee2d546SAndrew Lunn 
1786fee2d546SAndrew Lunn 	ret = ret & ~MII_88E6390_MISC_TEST_SAMPLE_MASK;
1787fee2d546SAndrew Lunn 	ret |= MII_88E6390_MISC_TEST_SAMPLE_ENABLE |
1788fee2d546SAndrew Lunn 		MII_88E6390_MISC_TEST_SAMPLE_1S;
1789fee2d546SAndrew Lunn 
1790fee2d546SAndrew Lunn 	ret = __phy_write(phydev, MII_88E6390_MISC_TEST, ret);
1791fee2d546SAndrew Lunn 	if (ret < 0)
1792fee2d546SAndrew Lunn 		goto error;
1793fee2d546SAndrew Lunn 
1794fee2d546SAndrew Lunn 	/* Wait for temperature to stabilize */
1795fee2d546SAndrew Lunn 	usleep_range(10000, 12000);
1796fee2d546SAndrew Lunn 
1797fee2d546SAndrew Lunn 	/* Reading the temperature sense has an errata. You need to read
1798fee2d546SAndrew Lunn 	 * a number of times and take an average.
1799fee2d546SAndrew Lunn 	 */
1800fee2d546SAndrew Lunn 	for (i = 0; i < MII_88E6390_TEMP_SENSOR_SAMPLES; i++) {
1801fee2d546SAndrew Lunn 		ret = __phy_read(phydev, MII_88E6390_TEMP_SENSOR);
1802fee2d546SAndrew Lunn 		if (ret < 0)
1803fee2d546SAndrew Lunn 			goto error;
1804fee2d546SAndrew Lunn 		sum += ret & MII_88E6390_TEMP_SENSOR_MASK;
1805fee2d546SAndrew Lunn 	}
1806fee2d546SAndrew Lunn 
1807fee2d546SAndrew Lunn 	sum /= MII_88E6390_TEMP_SENSOR_SAMPLES;
1808fee2d546SAndrew Lunn 	*temp = (sum  - 75) * 1000;
1809fee2d546SAndrew Lunn 
1810fee2d546SAndrew Lunn 	/* Disable temperature sensor */
1811fee2d546SAndrew Lunn 	ret = __phy_read(phydev, MII_88E6390_MISC_TEST);
1812fee2d546SAndrew Lunn 	if (ret < 0)
1813fee2d546SAndrew Lunn 		goto error;
1814fee2d546SAndrew Lunn 
1815fee2d546SAndrew Lunn 	ret = ret & ~MII_88E6390_MISC_TEST_SAMPLE_MASK;
1816fee2d546SAndrew Lunn 	ret |= MII_88E6390_MISC_TEST_SAMPLE_DISABLE;
1817fee2d546SAndrew Lunn 
1818fee2d546SAndrew Lunn 	ret = __phy_write(phydev, MII_88E6390_MISC_TEST, ret);
1819fee2d546SAndrew Lunn 
1820fee2d546SAndrew Lunn error:
1821fee2d546SAndrew Lunn 	phy_restore_page(phydev, oldpage, ret);
1822fee2d546SAndrew Lunn 
1823fee2d546SAndrew Lunn 	return ret;
1824fee2d546SAndrew Lunn }
1825fee2d546SAndrew Lunn 
1826fee2d546SAndrew Lunn static int m88e6390_hwmon_read(struct device *dev,
1827fee2d546SAndrew Lunn 			       enum hwmon_sensor_types type,
1828fee2d546SAndrew Lunn 			       u32 attr, int channel, long *temp)
1829fee2d546SAndrew Lunn {
1830fee2d546SAndrew Lunn 	struct phy_device *phydev = dev_get_drvdata(dev);
1831fee2d546SAndrew Lunn 	int err;
1832fee2d546SAndrew Lunn 
1833fee2d546SAndrew Lunn 	switch (attr) {
1834fee2d546SAndrew Lunn 	case hwmon_temp_input:
1835fee2d546SAndrew Lunn 		err = m88e6390_get_temp(phydev, temp);
1836fee2d546SAndrew Lunn 		break;
1837fee2d546SAndrew Lunn 	default:
1838fee2d546SAndrew Lunn 		return -EOPNOTSUPP;
1839fee2d546SAndrew Lunn 	}
1840fee2d546SAndrew Lunn 
1841fee2d546SAndrew Lunn 	return err;
1842fee2d546SAndrew Lunn }
1843fee2d546SAndrew Lunn 
1844fee2d546SAndrew Lunn static umode_t m88e6390_hwmon_is_visible(const void *data,
1845fee2d546SAndrew Lunn 					 enum hwmon_sensor_types type,
1846fee2d546SAndrew Lunn 					 u32 attr, int channel)
1847fee2d546SAndrew Lunn {
1848fee2d546SAndrew Lunn 	if (type != hwmon_temp)
1849fee2d546SAndrew Lunn 		return 0;
1850fee2d546SAndrew Lunn 
1851fee2d546SAndrew Lunn 	switch (attr) {
1852fee2d546SAndrew Lunn 	case hwmon_temp_input:
1853fee2d546SAndrew Lunn 		return 0444;
1854fee2d546SAndrew Lunn 	default:
1855fee2d546SAndrew Lunn 		return 0;
1856fee2d546SAndrew Lunn 	}
1857fee2d546SAndrew Lunn }
1858fee2d546SAndrew Lunn 
1859fee2d546SAndrew Lunn static u32 m88e6390_hwmon_temp_config[] = {
1860fee2d546SAndrew Lunn 	HWMON_T_INPUT,
1861fee2d546SAndrew Lunn 	0
1862fee2d546SAndrew Lunn };
1863fee2d546SAndrew Lunn 
1864fee2d546SAndrew Lunn static const struct hwmon_channel_info m88e6390_hwmon_temp = {
1865fee2d546SAndrew Lunn 	.type = hwmon_temp,
1866fee2d546SAndrew Lunn 	.config = m88e6390_hwmon_temp_config,
1867fee2d546SAndrew Lunn };
1868fee2d546SAndrew Lunn 
1869fee2d546SAndrew Lunn static const struct hwmon_channel_info *m88e6390_hwmon_info[] = {
1870fee2d546SAndrew Lunn 	&m88e1121_hwmon_chip,
1871fee2d546SAndrew Lunn 	&m88e6390_hwmon_temp,
1872fee2d546SAndrew Lunn 	NULL
1873fee2d546SAndrew Lunn };
1874fee2d546SAndrew Lunn 
1875fee2d546SAndrew Lunn static const struct hwmon_ops m88e6390_hwmon_hwmon_ops = {
1876fee2d546SAndrew Lunn 	.is_visible = m88e6390_hwmon_is_visible,
1877fee2d546SAndrew Lunn 	.read = m88e6390_hwmon_read,
1878fee2d546SAndrew Lunn };
1879fee2d546SAndrew Lunn 
1880fee2d546SAndrew Lunn static const struct hwmon_chip_info m88e6390_hwmon_chip_info = {
1881fee2d546SAndrew Lunn 	.ops = &m88e6390_hwmon_hwmon_ops,
1882fee2d546SAndrew Lunn 	.info = m88e6390_hwmon_info,
1883fee2d546SAndrew Lunn };
1884fee2d546SAndrew Lunn 
18850b04680fSAndrew Lunn static int marvell_hwmon_name(struct phy_device *phydev)
18860b04680fSAndrew Lunn {
18870b04680fSAndrew Lunn 	struct marvell_priv *priv = phydev->priv;
18880b04680fSAndrew Lunn 	struct device *dev = &phydev->mdio.dev;
18890b04680fSAndrew Lunn 	const char *devname = dev_name(dev);
18900b04680fSAndrew Lunn 	size_t len = strlen(devname);
18910b04680fSAndrew Lunn 	int i, j;
18920b04680fSAndrew Lunn 
18930b04680fSAndrew Lunn 	priv->hwmon_name = devm_kzalloc(dev, len, GFP_KERNEL);
18940b04680fSAndrew Lunn 	if (!priv->hwmon_name)
18950b04680fSAndrew Lunn 		return -ENOMEM;
18960b04680fSAndrew Lunn 
18970b04680fSAndrew Lunn 	for (i = j = 0; i < len && devname[i]; i++) {
18980b04680fSAndrew Lunn 		if (isalnum(devname[i]))
18990b04680fSAndrew Lunn 			priv->hwmon_name[j++] = devname[i];
19000b04680fSAndrew Lunn 	}
19010b04680fSAndrew Lunn 
19020b04680fSAndrew Lunn 	return 0;
19030b04680fSAndrew Lunn }
19040b04680fSAndrew Lunn 
19050b04680fSAndrew Lunn static int marvell_hwmon_probe(struct phy_device *phydev,
19060b04680fSAndrew Lunn 			       const struct hwmon_chip_info *chip)
19070b04680fSAndrew Lunn {
19080b04680fSAndrew Lunn 	struct marvell_priv *priv = phydev->priv;
19090b04680fSAndrew Lunn 	struct device *dev = &phydev->mdio.dev;
19100b04680fSAndrew Lunn 	int err;
19110b04680fSAndrew Lunn 
19120b04680fSAndrew Lunn 	err = marvell_hwmon_name(phydev);
19130b04680fSAndrew Lunn 	if (err)
19140b04680fSAndrew Lunn 		return err;
19150b04680fSAndrew Lunn 
19160b04680fSAndrew Lunn 	priv->hwmon_dev = devm_hwmon_device_register_with_info(
19170b04680fSAndrew Lunn 		dev, priv->hwmon_name, phydev, chip, NULL);
19180b04680fSAndrew Lunn 
19190b04680fSAndrew Lunn 	return PTR_ERR_OR_ZERO(priv->hwmon_dev);
19200b04680fSAndrew Lunn }
19210b04680fSAndrew Lunn 
19220b04680fSAndrew Lunn static int m88e1121_hwmon_probe(struct phy_device *phydev)
19230b04680fSAndrew Lunn {
19240b04680fSAndrew Lunn 	return marvell_hwmon_probe(phydev, &m88e1121_hwmon_chip_info);
19250b04680fSAndrew Lunn }
19260b04680fSAndrew Lunn 
19270b04680fSAndrew Lunn static int m88e1510_hwmon_probe(struct phy_device *phydev)
19280b04680fSAndrew Lunn {
19290b04680fSAndrew Lunn 	return marvell_hwmon_probe(phydev, &m88e1510_hwmon_chip_info);
19300b04680fSAndrew Lunn }
1931fee2d546SAndrew Lunn 
1932fee2d546SAndrew Lunn static int m88e6390_hwmon_probe(struct phy_device *phydev)
1933fee2d546SAndrew Lunn {
1934fee2d546SAndrew Lunn 	return marvell_hwmon_probe(phydev, &m88e6390_hwmon_chip_info);
1935fee2d546SAndrew Lunn }
19360b04680fSAndrew Lunn #else
19370b04680fSAndrew Lunn static int m88e1121_hwmon_probe(struct phy_device *phydev)
19380b04680fSAndrew Lunn {
19390b04680fSAndrew Lunn 	return 0;
19400b04680fSAndrew Lunn }
19410b04680fSAndrew Lunn 
19420b04680fSAndrew Lunn static int m88e1510_hwmon_probe(struct phy_device *phydev)
19430b04680fSAndrew Lunn {
19440b04680fSAndrew Lunn 	return 0;
19450b04680fSAndrew Lunn }
1946fee2d546SAndrew Lunn 
1947fee2d546SAndrew Lunn static int m88e6390_hwmon_probe(struct phy_device *phydev)
1948fee2d546SAndrew Lunn {
1949fee2d546SAndrew Lunn 	return 0;
1950fee2d546SAndrew Lunn }
19510b04680fSAndrew Lunn #endif
19520b04680fSAndrew Lunn 
1953d2fa47d9SAndrew Lunn static int marvell_probe(struct phy_device *phydev)
1954d2fa47d9SAndrew Lunn {
1955d2fa47d9SAndrew Lunn 	struct marvell_priv *priv;
1956d2fa47d9SAndrew Lunn 
1957e5a03bfdSAndrew Lunn 	priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
1958d2fa47d9SAndrew Lunn 	if (!priv)
1959d2fa47d9SAndrew Lunn 		return -ENOMEM;
1960d2fa47d9SAndrew Lunn 
1961d2fa47d9SAndrew Lunn 	phydev->priv = priv;
1962d2fa47d9SAndrew Lunn 
1963d2fa47d9SAndrew Lunn 	return 0;
1964d2fa47d9SAndrew Lunn }
1965d2fa47d9SAndrew Lunn 
19660b04680fSAndrew Lunn static int m88e1121_probe(struct phy_device *phydev)
19670b04680fSAndrew Lunn {
19680b04680fSAndrew Lunn 	int err;
19690b04680fSAndrew Lunn 
19700b04680fSAndrew Lunn 	err = marvell_probe(phydev);
19710b04680fSAndrew Lunn 	if (err)
19720b04680fSAndrew Lunn 		return err;
19730b04680fSAndrew Lunn 
19740b04680fSAndrew Lunn 	return m88e1121_hwmon_probe(phydev);
19750b04680fSAndrew Lunn }
19760b04680fSAndrew Lunn 
19770b04680fSAndrew Lunn static int m88e1510_probe(struct phy_device *phydev)
19780b04680fSAndrew Lunn {
19790b04680fSAndrew Lunn 	int err;
19800b04680fSAndrew Lunn 
19810b04680fSAndrew Lunn 	err = marvell_probe(phydev);
19820b04680fSAndrew Lunn 	if (err)
19830b04680fSAndrew Lunn 		return err;
19840b04680fSAndrew Lunn 
19850b04680fSAndrew Lunn 	return m88e1510_hwmon_probe(phydev);
19860b04680fSAndrew Lunn }
19870b04680fSAndrew Lunn 
1988fee2d546SAndrew Lunn static int m88e6390_probe(struct phy_device *phydev)
1989fee2d546SAndrew Lunn {
1990fee2d546SAndrew Lunn 	int err;
1991fee2d546SAndrew Lunn 
1992fee2d546SAndrew Lunn 	err = marvell_probe(phydev);
1993fee2d546SAndrew Lunn 	if (err)
1994fee2d546SAndrew Lunn 		return err;
1995fee2d546SAndrew Lunn 
1996fee2d546SAndrew Lunn 	return m88e6390_hwmon_probe(phydev);
1997fee2d546SAndrew Lunn }
1998fee2d546SAndrew Lunn 
1999e5479239SOlof Johansson static struct phy_driver marvell_drivers[] = {
2000e5479239SOlof Johansson 	{
20012f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1101,
20022f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
200300db8189SAndy Fleming 		.name = "Marvell 88E1101",
200400db8189SAndy Fleming 		.features = PHY_GBIT_FEATURES,
200500db8189SAndy Fleming 		.flags = PHY_HAS_INTERRUPT,
200618702414SArnd Bergmann 		.probe = marvell_probe,
200779be1a1cSClemens Gruber 		.config_init = &marvell_config_init,
2008f2899788SAndrew Lunn 		.config_aneg = &m88e1101_config_aneg,
200900db8189SAndy Fleming 		.ack_interrupt = &marvell_ack_interrupt,
201000db8189SAndy Fleming 		.config_intr = &marvell_config_intr,
20110898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
20120898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2013424ca4c5SRussell King 		.read_page = marvell_read_page,
2014424ca4c5SRussell King 		.write_page = marvell_write_page,
2015d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2016d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2017d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2018e5479239SOlof Johansson 	},
2019e5479239SOlof Johansson 	{
20202f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1112,
20212f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
202285cfb534SOlof Johansson 		.name = "Marvell 88E1112",
202385cfb534SOlof Johansson 		.features = PHY_GBIT_FEATURES,
202485cfb534SOlof Johansson 		.flags = PHY_HAS_INTERRUPT,
2025d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
202685cfb534SOlof Johansson 		.config_init = &m88e1111_config_init,
202785cfb534SOlof Johansson 		.config_aneg = &marvell_config_aneg,
202885cfb534SOlof Johansson 		.ack_interrupt = &marvell_ack_interrupt,
202985cfb534SOlof Johansson 		.config_intr = &marvell_config_intr,
20300898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
20310898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2032424ca4c5SRussell King 		.read_page = marvell_read_page,
2033424ca4c5SRussell King 		.write_page = marvell_write_page,
2034d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2035d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2036d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
203785cfb534SOlof Johansson 	},
203885cfb534SOlof Johansson 	{
20392f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1111,
20402f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
204176884679SAndy Fleming 		.name = "Marvell 88E1111",
204276884679SAndy Fleming 		.features = PHY_GBIT_FEATURES,
204376884679SAndy Fleming 		.flags = PHY_HAS_INTERRUPT,
2044d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
2045e5479239SOlof Johansson 		.config_init = &m88e1111_config_init,
20463ec0a0f1SHarini Katakam 		.config_aneg = &m88e1111_config_aneg,
2047be937f1fSAlexandr Smirnov 		.read_status = &marvell_read_status,
204876884679SAndy Fleming 		.ack_interrupt = &marvell_ack_interrupt,
204976884679SAndy Fleming 		.config_intr = &marvell_config_intr,
20500898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
20510898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2052424ca4c5SRussell King 		.read_page = marvell_read_page,
2053424ca4c5SRussell King 		.write_page = marvell_write_page,
2054d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2055d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2056d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2057e5479239SOlof Johansson 	},
2058e5479239SOlof Johansson 	{
20592f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1118,
20602f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2061605f196eSRon Madrid 		.name = "Marvell 88E1118",
2062605f196eSRon Madrid 		.features = PHY_GBIT_FEATURES,
2063605f196eSRon Madrid 		.flags = PHY_HAS_INTERRUPT,
2064d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
2065605f196eSRon Madrid 		.config_init = &m88e1118_config_init,
2066605f196eSRon Madrid 		.config_aneg = &m88e1118_config_aneg,
2067605f196eSRon Madrid 		.ack_interrupt = &marvell_ack_interrupt,
2068605f196eSRon Madrid 		.config_intr = &marvell_config_intr,
20690898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
20700898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2071424ca4c5SRussell King 		.read_page = marvell_read_page,
2072424ca4c5SRussell King 		.write_page = marvell_write_page,
2073d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2074d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2075d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2076605f196eSRon Madrid 	},
2077605f196eSRon Madrid 	{
20782f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1121R,
20792f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2080140bc929SSergei Poselenov 		.name = "Marvell 88E1121R",
2081140bc929SSergei Poselenov 		.features = PHY_GBIT_FEATURES,
2082140bc929SSergei Poselenov 		.flags = PHY_HAS_INTERRUPT,
208318702414SArnd Bergmann 		.probe = &m88e1121_probe,
2084fdecf36fSClemens Gruber 		.config_init = &m88e1121_config_init,
2085140bc929SSergei Poselenov 		.config_aneg = &m88e1121_config_aneg,
2086140bc929SSergei Poselenov 		.read_status = &marvell_read_status,
2087140bc929SSergei Poselenov 		.ack_interrupt = &marvell_ack_interrupt,
2088140bc929SSergei Poselenov 		.config_intr = &marvell_config_intr,
2089dcd07be3SAnatolij Gustschin 		.did_interrupt = &m88e1121_did_interrupt,
20900898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
20910898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2092424ca4c5SRussell King 		.read_page = marvell_read_page,
2093424ca4c5SRussell King 		.write_page = marvell_write_page,
2094d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2095d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2096d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2097140bc929SSergei Poselenov 	},
2098140bc929SSergei Poselenov 	{
2099337ac9d5SCyril Chemparathy 		.phy_id = MARVELL_PHY_ID_88E1318S,
21006ba74014SLinus Torvalds 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2101337ac9d5SCyril Chemparathy 		.name = "Marvell 88E1318S",
21023ff1c259SCyril Chemparathy 		.features = PHY_GBIT_FEATURES,
21033ff1c259SCyril Chemparathy 		.flags = PHY_HAS_INTERRUPT,
2104d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
2105dd9a122aSEsben Haabendal 		.config_init = &m88e1318_config_init,
2106337ac9d5SCyril Chemparathy 		.config_aneg = &m88e1318_config_aneg,
21073ff1c259SCyril Chemparathy 		.read_status = &marvell_read_status,
21083ff1c259SCyril Chemparathy 		.ack_interrupt = &marvell_ack_interrupt,
21093ff1c259SCyril Chemparathy 		.config_intr = &marvell_config_intr,
21103ff1c259SCyril Chemparathy 		.did_interrupt = &m88e1121_did_interrupt,
21113871c387SMichael Stapelberg 		.get_wol = &m88e1318_get_wol,
21123871c387SMichael Stapelberg 		.set_wol = &m88e1318_set_wol,
21130898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
21140898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2115424ca4c5SRussell King 		.read_page = marvell_read_page,
2116424ca4c5SRussell King 		.write_page = marvell_write_page,
2117d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2118d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2119d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
21203ff1c259SCyril Chemparathy 	},
21213ff1c259SCyril Chemparathy 	{
21222f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1145,
21232f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
212476884679SAndy Fleming 		.name = "Marvell 88E1145",
212576884679SAndy Fleming 		.features = PHY_GBIT_FEATURES,
212676884679SAndy Fleming 		.flags = PHY_HAS_INTERRUPT,
2127d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
212876884679SAndy Fleming 		.config_init = &m88e1145_config_init,
2129c505873eSZhao Qiang 		.config_aneg = &m88e1101_config_aneg,
213076884679SAndy Fleming 		.read_status = &genphy_read_status,
213176884679SAndy Fleming 		.ack_interrupt = &marvell_ack_interrupt,
213276884679SAndy Fleming 		.config_intr = &marvell_config_intr,
21330898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
21340898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2135424ca4c5SRussell King 		.read_page = marvell_read_page,
2136424ca4c5SRussell King 		.write_page = marvell_write_page,
2137d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2138d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2139d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2140ac8c635aSOlof Johansson 	},
2141ac8c635aSOlof Johansson 	{
214290600732SDavid Daney 		.phy_id = MARVELL_PHY_ID_88E1149R,
214390600732SDavid Daney 		.phy_id_mask = MARVELL_PHY_ID_MASK,
214490600732SDavid Daney 		.name = "Marvell 88E1149R",
214590600732SDavid Daney 		.features = PHY_GBIT_FEATURES,
214690600732SDavid Daney 		.flags = PHY_HAS_INTERRUPT,
2147d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
214890600732SDavid Daney 		.config_init = &m88e1149_config_init,
214990600732SDavid Daney 		.config_aneg = &m88e1118_config_aneg,
215090600732SDavid Daney 		.ack_interrupt = &marvell_ack_interrupt,
215190600732SDavid Daney 		.config_intr = &marvell_config_intr,
21520898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
21530898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2154424ca4c5SRussell King 		.read_page = marvell_read_page,
2155424ca4c5SRussell King 		.write_page = marvell_write_page,
2156d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2157d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2158d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
215990600732SDavid Daney 	},
216090600732SDavid Daney 	{
21612f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1240,
21622f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2163ac8c635aSOlof Johansson 		.name = "Marvell 88E1240",
2164ac8c635aSOlof Johansson 		.features = PHY_GBIT_FEATURES,
2165ac8c635aSOlof Johansson 		.flags = PHY_HAS_INTERRUPT,
2166d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
2167ac8c635aSOlof Johansson 		.config_init = &m88e1111_config_init,
2168ac8c635aSOlof Johansson 		.config_aneg = &marvell_config_aneg,
2169ac8c635aSOlof Johansson 		.ack_interrupt = &marvell_ack_interrupt,
2170ac8c635aSOlof Johansson 		.config_intr = &marvell_config_intr,
21710898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
21720898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2173424ca4c5SRussell King 		.read_page = marvell_read_page,
2174424ca4c5SRussell King 		.write_page = marvell_write_page,
2175d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2176d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2177d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2178ac8c635aSOlof Johansson 	},
21793da09a51SMichal Simek 	{
21803da09a51SMichal Simek 		.phy_id = MARVELL_PHY_ID_88E1116R,
21813da09a51SMichal Simek 		.phy_id_mask = MARVELL_PHY_ID_MASK,
21823da09a51SMichal Simek 		.name = "Marvell 88E1116R",
21833da09a51SMichal Simek 		.features = PHY_GBIT_FEATURES,
21843da09a51SMichal Simek 		.flags = PHY_HAS_INTERRUPT,
2185d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
21863da09a51SMichal Simek 		.config_init = &m88e1116r_config_init,
21873da09a51SMichal Simek 		.ack_interrupt = &marvell_ack_interrupt,
21883da09a51SMichal Simek 		.config_intr = &marvell_config_intr,
21890898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
21900898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2191424ca4c5SRussell King 		.read_page = marvell_read_page,
2192424ca4c5SRussell King 		.write_page = marvell_write_page,
2193d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2194d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2195d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
21963da09a51SMichal Simek 	},
219710e24caaSMichal Simek 	{
219810e24caaSMichal Simek 		.phy_id = MARVELL_PHY_ID_88E1510,
219910e24caaSMichal Simek 		.phy_id_mask = MARVELL_PHY_ID_MASK,
220010e24caaSMichal Simek 		.name = "Marvell 88E1510",
22016cfb3bccSCharles-Antoine Couret 		.features = PHY_GBIT_FEATURES | SUPPORTED_FIBRE,
220218702414SArnd Bergmann 		.flags = PHY_HAS_INTERRUPT,
22030b04680fSAndrew Lunn 		.probe = &m88e1510_probe,
2204930b37eeSStefan Roese 		.config_init = &m88e1510_config_init,
220510e24caaSMichal Simek 		.config_aneg = &m88e1510_config_aneg,
220610e24caaSMichal Simek 		.read_status = &marvell_read_status,
220710e24caaSMichal Simek 		.ack_interrupt = &marvell_ack_interrupt,
220810e24caaSMichal Simek 		.config_intr = &marvell_config_intr,
220910e24caaSMichal Simek 		.did_interrupt = &m88e1121_did_interrupt,
2210f39aac7eSJingju Hou 		.get_wol = &m88e1318_get_wol,
2211f39aac7eSJingju Hou 		.set_wol = &m88e1318_set_wol,
22123758be3dSCharles-Antoine Couret 		.resume = &marvell_resume,
22133758be3dSCharles-Antoine Couret 		.suspend = &marvell_suspend,
2214424ca4c5SRussell King 		.read_page = marvell_read_page,
2215424ca4c5SRussell King 		.write_page = marvell_write_page,
2216d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2217d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2218d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2219f0f9b4edSLin Yun Sheng 		.set_loopback = genphy_loopback,
222010e24caaSMichal Simek 	},
22216b358aedSSebastian Hesselbarth 	{
2222819ec8e1SAndrew Lunn 		.phy_id = MARVELL_PHY_ID_88E1540,
2223819ec8e1SAndrew Lunn 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2224819ec8e1SAndrew Lunn 		.name = "Marvell 88E1540",
2225819ec8e1SAndrew Lunn 		.features = PHY_GBIT_FEATURES,
2226819ec8e1SAndrew Lunn 		.flags = PHY_HAS_INTERRUPT,
222718702414SArnd Bergmann 		.probe = m88e1510_probe,
222879be1a1cSClemens Gruber 		.config_init = &marvell_config_init,
2229819ec8e1SAndrew Lunn 		.config_aneg = &m88e1510_config_aneg,
2230819ec8e1SAndrew Lunn 		.read_status = &marvell_read_status,
2231819ec8e1SAndrew Lunn 		.ack_interrupt = &marvell_ack_interrupt,
2232819ec8e1SAndrew Lunn 		.config_intr = &marvell_config_intr,
2233819ec8e1SAndrew Lunn 		.did_interrupt = &m88e1121_did_interrupt,
2234819ec8e1SAndrew Lunn 		.resume = &genphy_resume,
2235819ec8e1SAndrew Lunn 		.suspend = &genphy_suspend,
2236424ca4c5SRussell King 		.read_page = marvell_read_page,
2237424ca4c5SRussell King 		.write_page = marvell_write_page,
2238d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2239d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2240d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2241819ec8e1SAndrew Lunn 	},
2242819ec8e1SAndrew Lunn 	{
224360f06fdeSAndrew Lunn 		.phy_id = MARVELL_PHY_ID_88E1545,
224460f06fdeSAndrew Lunn 		.phy_id_mask = MARVELL_PHY_ID_MASK,
224560f06fdeSAndrew Lunn 		.name = "Marvell 88E1545",
224660f06fdeSAndrew Lunn 		.probe = m88e1510_probe,
224760f06fdeSAndrew Lunn 		.features = PHY_GBIT_FEATURES,
224860f06fdeSAndrew Lunn 		.flags = PHY_HAS_INTERRUPT,
224960f06fdeSAndrew Lunn 		.config_init = &marvell_config_init,
225060f06fdeSAndrew Lunn 		.config_aneg = &m88e1510_config_aneg,
225160f06fdeSAndrew Lunn 		.read_status = &marvell_read_status,
225260f06fdeSAndrew Lunn 		.ack_interrupt = &marvell_ack_interrupt,
225360f06fdeSAndrew Lunn 		.config_intr = &marvell_config_intr,
225460f06fdeSAndrew Lunn 		.did_interrupt = &m88e1121_did_interrupt,
225560f06fdeSAndrew Lunn 		.resume = &genphy_resume,
225660f06fdeSAndrew Lunn 		.suspend = &genphy_suspend,
2257424ca4c5SRussell King 		.read_page = marvell_read_page,
2258424ca4c5SRussell King 		.write_page = marvell_write_page,
225960f06fdeSAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
226060f06fdeSAndrew Lunn 		.get_strings = marvell_get_strings,
226160f06fdeSAndrew Lunn 		.get_stats = marvell_get_stats,
226260f06fdeSAndrew Lunn 	},
226360f06fdeSAndrew Lunn 	{
22646b358aedSSebastian Hesselbarth 		.phy_id = MARVELL_PHY_ID_88E3016,
22656b358aedSSebastian Hesselbarth 		.phy_id_mask = MARVELL_PHY_ID_MASK,
22666b358aedSSebastian Hesselbarth 		.name = "Marvell 88E3016",
22676b358aedSSebastian Hesselbarth 		.features = PHY_BASIC_FEATURES,
22686b358aedSSebastian Hesselbarth 		.flags = PHY_HAS_INTERRUPT,
2269d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
22706b358aedSSebastian Hesselbarth 		.config_init = &m88e3016_config_init,
22716b358aedSSebastian Hesselbarth 		.aneg_done = &marvell_aneg_done,
22726b358aedSSebastian Hesselbarth 		.read_status = &marvell_read_status,
22736b358aedSSebastian Hesselbarth 		.ack_interrupt = &marvell_ack_interrupt,
22746b358aedSSebastian Hesselbarth 		.config_intr = &marvell_config_intr,
22756b358aedSSebastian Hesselbarth 		.did_interrupt = &m88e1121_did_interrupt,
22766b358aedSSebastian Hesselbarth 		.resume = &genphy_resume,
22776b358aedSSebastian Hesselbarth 		.suspend = &genphy_suspend,
2278424ca4c5SRussell King 		.read_page = marvell_read_page,
2279424ca4c5SRussell King 		.write_page = marvell_write_page,
2280d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2281d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2282d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
22836b358aedSSebastian Hesselbarth 	},
2284e4cf8a38SAndrew Lunn 	{
2285e4cf8a38SAndrew Lunn 		.phy_id = MARVELL_PHY_ID_88E6390,
2286e4cf8a38SAndrew Lunn 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2287e4cf8a38SAndrew Lunn 		.name = "Marvell 88E6390",
2288e4cf8a38SAndrew Lunn 		.features = PHY_GBIT_FEATURES,
2289e4cf8a38SAndrew Lunn 		.flags = PHY_HAS_INTERRUPT,
2290fee2d546SAndrew Lunn 		.probe = m88e6390_probe,
2291e4cf8a38SAndrew Lunn 		.config_init = &marvell_config_init,
2292e4cf8a38SAndrew Lunn 		.config_aneg = &m88e1510_config_aneg,
2293e4cf8a38SAndrew Lunn 		.read_status = &marvell_read_status,
2294e4cf8a38SAndrew Lunn 		.ack_interrupt = &marvell_ack_interrupt,
2295e4cf8a38SAndrew Lunn 		.config_intr = &marvell_config_intr,
2296e4cf8a38SAndrew Lunn 		.did_interrupt = &m88e1121_did_interrupt,
2297e4cf8a38SAndrew Lunn 		.resume = &genphy_resume,
2298e4cf8a38SAndrew Lunn 		.suspend = &genphy_suspend,
2299424ca4c5SRussell King 		.read_page = marvell_read_page,
2300424ca4c5SRussell King 		.write_page = marvell_write_page,
2301e4cf8a38SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2302e4cf8a38SAndrew Lunn 		.get_strings = marvell_get_strings,
2303e4cf8a38SAndrew Lunn 		.get_stats = marvell_get_stats,
2304e4cf8a38SAndrew Lunn 	},
230576884679SAndy Fleming };
230676884679SAndy Fleming 
230750fd7150SJohan Hovold module_phy_driver(marvell_drivers);
23084e4f10f6SDavid Woodhouse 
2309cf93c945SUwe Kleine-König static struct mdio_device_id __maybe_unused marvell_tbl[] = {
2310f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1101, MARVELL_PHY_ID_MASK },
2311f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1112, MARVELL_PHY_ID_MASK },
2312f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1111, MARVELL_PHY_ID_MASK },
2313f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1118, MARVELL_PHY_ID_MASK },
2314f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1121R, MARVELL_PHY_ID_MASK },
2315f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1145, MARVELL_PHY_ID_MASK },
2316f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1149R, MARVELL_PHY_ID_MASK },
2317f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1240, MARVELL_PHY_ID_MASK },
2318f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1318S, MARVELL_PHY_ID_MASK },
23193da09a51SMichal Simek 	{ MARVELL_PHY_ID_88E1116R, MARVELL_PHY_ID_MASK },
232010e24caaSMichal Simek 	{ MARVELL_PHY_ID_88E1510, MARVELL_PHY_ID_MASK },
2321819ec8e1SAndrew Lunn 	{ MARVELL_PHY_ID_88E1540, MARVELL_PHY_ID_MASK },
232260f06fdeSAndrew Lunn 	{ MARVELL_PHY_ID_88E1545, MARVELL_PHY_ID_MASK },
23236b358aedSSebastian Hesselbarth 	{ MARVELL_PHY_ID_88E3016, MARVELL_PHY_ID_MASK },
2324e4cf8a38SAndrew Lunn 	{ MARVELL_PHY_ID_88E6390, MARVELL_PHY_ID_MASK },
23254e4f10f6SDavid Woodhouse 	{ }
23264e4f10f6SDavid Woodhouse };
23274e4f10f6SDavid Woodhouse 
23284e4f10f6SDavid Woodhouse MODULE_DEVICE_TABLE(mdio, marvell_tbl);
2329