xref: /openbmc/linux/drivers/net/phy/marvell.c (revision f0a45816)
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
59239aa55bSDavid Thomson #define MII_M1011_PHY_SCR_MDI		0x0000
60239aa55bSDavid Thomson #define MII_M1011_PHY_SCR_MDI_X		0x0020
6176884679SAndy Fleming #define MII_M1011_PHY_SCR_AUTO_CROSS	0x0060
6276884679SAndy Fleming 
63b0224175SViet Nga Dao #define MII_M1145_PHY_EXT_SR		0x1b
6476884679SAndy Fleming #define MII_M1145_PHY_EXT_CR		0x14
6576884679SAndy Fleming #define MII_M1145_RGMII_RX_DELAY	0x0080
6676884679SAndy Fleming #define MII_M1145_RGMII_TX_DELAY	0x0002
67b0224175SViet Nga Dao #define MII_M1145_HWCFG_MODE_SGMII_NO_CLK	0x4
68b0224175SViet Nga Dao #define MII_M1145_HWCFG_MODE_MASK		0xf
69b0224175SViet Nga Dao #define MII_M1145_HWCFG_FIBER_COPPER_AUTO	0x8000
7076884679SAndy Fleming 
7199d881f9SVince Bridgers #define MII_M1145_HWCFG_MODE_SGMII_NO_CLK	0x4
7299d881f9SVince Bridgers #define MII_M1145_HWCFG_MODE_MASK		0xf
7399d881f9SVince Bridgers #define MII_M1145_HWCFG_FIBER_COPPER_AUTO	0x8000
7499d881f9SVince Bridgers 
7576884679SAndy Fleming #define MII_M1111_PHY_LED_CONTROL	0x18
7676884679SAndy Fleming #define MII_M1111_PHY_LED_DIRECT	0x4100
7776884679SAndy Fleming #define MII_M1111_PHY_LED_COMBINE	0x411c
78895ee682SKim Phillips #define MII_M1111_PHY_EXT_CR		0x14
79895ee682SKim Phillips #define MII_M1111_RX_DELAY		0x80
80895ee682SKim Phillips #define MII_M1111_TX_DELAY		0x2
81895ee682SKim Phillips #define MII_M1111_PHY_EXT_SR		0x1b
82be937f1fSAlexandr Smirnov 
83895ee682SKim Phillips #define MII_M1111_HWCFG_MODE_MASK		0xf
84be937f1fSAlexandr Smirnov #define MII_M1111_HWCFG_MODE_COPPER_RGMII	0xb
85be937f1fSAlexandr Smirnov #define MII_M1111_HWCFG_MODE_FIBER_RGMII	0x3
864117b5beSKapil Juneja #define MII_M1111_HWCFG_MODE_SGMII_NO_CLK	0x4
875f8cbc13SLiu Yu-B13201 #define MII_M1111_HWCFG_MODE_COPPER_RTBI	0x9
88be937f1fSAlexandr Smirnov #define MII_M1111_HWCFG_FIBER_COPPER_AUTO	0x8000
89be937f1fSAlexandr Smirnov #define MII_M1111_HWCFG_FIBER_COPPER_RES	0x2000
90be937f1fSAlexandr Smirnov 
91c477d044SCyril Chemparathy #define MII_88E1121_PHY_MSCR_REG	21
92c477d044SCyril Chemparathy #define MII_88E1121_PHY_MSCR_RX_DELAY	BIT(5)
93c477d044SCyril Chemparathy #define MII_88E1121_PHY_MSCR_TX_DELAY	BIT(4)
94c477d044SCyril Chemparathy #define MII_88E1121_PHY_MSCR_DELAY_MASK	(~(0x3 << 4))
95c477d044SCyril Chemparathy 
960b04680fSAndrew Lunn #define MII_88E1121_MISC_TEST				0x1a
970b04680fSAndrew Lunn #define MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK	0x1f00
980b04680fSAndrew Lunn #define MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT	8
990b04680fSAndrew Lunn #define MII_88E1510_MISC_TEST_TEMP_IRQ_EN		BIT(7)
1000b04680fSAndrew Lunn #define MII_88E1510_MISC_TEST_TEMP_IRQ			BIT(6)
1010b04680fSAndrew Lunn #define MII_88E1121_MISC_TEST_TEMP_SENSOR_EN		BIT(5)
1020b04680fSAndrew Lunn #define MII_88E1121_MISC_TEST_TEMP_MASK			0x1f
1030b04680fSAndrew Lunn 
1040b04680fSAndrew Lunn #define MII_88E1510_TEMP_SENSOR		0x1b
1050b04680fSAndrew Lunn #define MII_88E1510_TEMP_SENSOR_MASK	0xff
1060b04680fSAndrew Lunn 
107337ac9d5SCyril Chemparathy #define MII_88E1318S_PHY_MSCR1_REG	16
108337ac9d5SCyril Chemparathy #define MII_88E1318S_PHY_MSCR1_PAD_ODD	BIT(6)
1093ff1c259SCyril Chemparathy 
1103871c387SMichael Stapelberg /* Copper Specific Interrupt Enable Register */
1113871c387SMichael Stapelberg #define MII_88E1318S_PHY_CSIER                              0x12
1123871c387SMichael Stapelberg /* WOL Event Interrupt Enable */
1133871c387SMichael Stapelberg #define MII_88E1318S_PHY_CSIER_WOL_EIE                      BIT(7)
1143871c387SMichael Stapelberg 
1153871c387SMichael Stapelberg /* LED Timer Control Register */
1163871c387SMichael Stapelberg #define MII_88E1318S_PHY_LED_TCR                            0x12
1173871c387SMichael Stapelberg #define MII_88E1318S_PHY_LED_TCR_FORCE_INT                  BIT(15)
1183871c387SMichael Stapelberg #define MII_88E1318S_PHY_LED_TCR_INTn_ENABLE                BIT(7)
1193871c387SMichael Stapelberg #define MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW             BIT(11)
1203871c387SMichael Stapelberg 
1213871c387SMichael Stapelberg /* Magic Packet MAC address registers */
1223871c387SMichael Stapelberg #define MII_88E1318S_PHY_MAGIC_PACKET_WORD2                 0x17
1233871c387SMichael Stapelberg #define MII_88E1318S_PHY_MAGIC_PACKET_WORD1                 0x18
1243871c387SMichael Stapelberg #define MII_88E1318S_PHY_MAGIC_PACKET_WORD0                 0x19
1253871c387SMichael Stapelberg 
1263871c387SMichael Stapelberg #define MII_88E1318S_PHY_WOL_CTRL                           0x10
1273871c387SMichael Stapelberg #define MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS          BIT(12)
1283871c387SMichael Stapelberg #define MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE BIT(14)
1293871c387SMichael Stapelberg 
130140bc929SSergei Poselenov #define MII_88E1121_PHY_LED_CTRL	16
131140bc929SSergei Poselenov #define MII_88E1121_PHY_LED_DEF		0x0030
132140bc929SSergei Poselenov 
133be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS		0x11
134be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_1000	0x8000
135be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_100	0x4000
136be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_SPD_MASK	0xc000
137be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_FULLDUPLEX	0x2000
138be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_RESOLVED	0x0800
139be937f1fSAlexandr Smirnov #define MII_M1011_PHY_STATUS_LINK	0x0400
140be937f1fSAlexandr Smirnov 
1413da09a51SMichal Simek #define MII_M1116R_CONTROL_REG_MAC	21
1423da09a51SMichal Simek 
1436b358aedSSebastian Hesselbarth #define MII_88E3016_PHY_SPEC_CTRL	0x10
1446b358aedSSebastian Hesselbarth #define MII_88E3016_DISABLE_SCRAMBLER	0x0200
1456b358aedSSebastian Hesselbarth #define MII_88E3016_AUTO_MDIX_CROSSOVER	0x0030
14676884679SAndy Fleming 
147930b37eeSStefan Roese #define MII_88E1510_GEN_CTRL_REG_1		0x14
148930b37eeSStefan Roese #define MII_88E1510_GEN_CTRL_REG_1_MODE_MASK	0x7
149930b37eeSStefan Roese #define MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII	0x1	/* SGMII to copper */
150930b37eeSStefan Roese #define MII_88E1510_GEN_CTRL_REG_1_RESET	0x8000	/* Soft reset */
151930b37eeSStefan Roese 
1526cfb3bccSCharles-Antoine Couret #define LPA_FIBER_1000HALF	0x40
1536cfb3bccSCharles-Antoine Couret #define LPA_FIBER_1000FULL	0x20
1546cfb3bccSCharles-Antoine Couret 
1556cfb3bccSCharles-Antoine Couret #define LPA_PAUSE_FIBER	0x180
1566cfb3bccSCharles-Antoine Couret #define LPA_PAUSE_ASYM_FIBER	0x100
1576cfb3bccSCharles-Antoine Couret 
1586cfb3bccSCharles-Antoine Couret #define ADVERTISE_FIBER_1000HALF	0x40
1596cfb3bccSCharles-Antoine Couret #define ADVERTISE_FIBER_1000FULL	0x20
1606cfb3bccSCharles-Antoine Couret 
1616cfb3bccSCharles-Antoine Couret #define ADVERTISE_PAUSE_FIBER		0x180
1626cfb3bccSCharles-Antoine Couret #define ADVERTISE_PAUSE_ASYM_FIBER	0x100
1636cfb3bccSCharles-Antoine Couret 
1646cfb3bccSCharles-Antoine Couret #define REGISTER_LINK_STATUS	0x400
1652170fef7SCharles-Antoine Couret #define NB_FIBER_STATS	1
1666cfb3bccSCharles-Antoine Couret 
16700db8189SAndy Fleming MODULE_DESCRIPTION("Marvell PHY driver");
16800db8189SAndy Fleming MODULE_AUTHOR("Andy Fleming");
16900db8189SAndy Fleming MODULE_LICENSE("GPL");
17000db8189SAndy Fleming 
171d2fa47d9SAndrew Lunn struct marvell_hw_stat {
172d2fa47d9SAndrew Lunn 	const char *string;
173d2fa47d9SAndrew Lunn 	u8 page;
174d2fa47d9SAndrew Lunn 	u8 reg;
175d2fa47d9SAndrew Lunn 	u8 bits;
176d2fa47d9SAndrew Lunn };
177d2fa47d9SAndrew Lunn 
178d2fa47d9SAndrew Lunn static struct marvell_hw_stat marvell_hw_stats[] = {
1792170fef7SCharles-Antoine Couret 	{ "phy_receive_errors_copper", 0, 21, 16},
180d2fa47d9SAndrew Lunn 	{ "phy_idle_errors", 0, 10, 8 },
1812170fef7SCharles-Antoine Couret 	{ "phy_receive_errors_fiber", 1, 21, 16},
182d2fa47d9SAndrew Lunn };
183d2fa47d9SAndrew Lunn 
184d2fa47d9SAndrew Lunn struct marvell_priv {
185d2fa47d9SAndrew Lunn 	u64 stats[ARRAY_SIZE(marvell_hw_stats)];
1860b04680fSAndrew Lunn 	char *hwmon_name;
1870b04680fSAndrew Lunn 	struct device *hwmon_dev;
188d2fa47d9SAndrew Lunn };
189d2fa47d9SAndrew Lunn 
1906427bb2dSAndrew Lunn static int marvell_get_page(struct phy_device *phydev)
1916427bb2dSAndrew Lunn {
1926427bb2dSAndrew Lunn 	return phy_read(phydev, MII_MARVELL_PHY_PAGE);
1936427bb2dSAndrew Lunn }
1946427bb2dSAndrew Lunn 
1956427bb2dSAndrew Lunn static int marvell_set_page(struct phy_device *phydev, int page)
1966427bb2dSAndrew Lunn {
1976427bb2dSAndrew Lunn 	return phy_write(phydev, MII_MARVELL_PHY_PAGE, page);
1986427bb2dSAndrew Lunn }
1996427bb2dSAndrew Lunn 
20053798328SAndrew Lunn static int marvell_get_set_page(struct phy_device *phydev, int page)
20153798328SAndrew Lunn {
20253798328SAndrew Lunn 	int oldpage = marvell_get_page(phydev);
20353798328SAndrew Lunn 
20453798328SAndrew Lunn 	if (oldpage < 0)
20553798328SAndrew Lunn 		return oldpage;
20653798328SAndrew Lunn 
20753798328SAndrew Lunn 	if (page != oldpage)
20853798328SAndrew Lunn 		return marvell_set_page(phydev, page);
20953798328SAndrew Lunn 
21053798328SAndrew Lunn 	return 0;
21153798328SAndrew Lunn }
21253798328SAndrew Lunn 
21300db8189SAndy Fleming static int marvell_ack_interrupt(struct phy_device *phydev)
21400db8189SAndy Fleming {
21500db8189SAndy Fleming 	int err;
21600db8189SAndy Fleming 
21700db8189SAndy Fleming 	/* Clear the interrupts by reading the reg */
21800db8189SAndy Fleming 	err = phy_read(phydev, MII_M1011_IEVENT);
21900db8189SAndy Fleming 
22000db8189SAndy Fleming 	if (err < 0)
22100db8189SAndy Fleming 		return err;
22200db8189SAndy Fleming 
22300db8189SAndy Fleming 	return 0;
22400db8189SAndy Fleming }
22500db8189SAndy Fleming 
22600db8189SAndy Fleming static int marvell_config_intr(struct phy_device *phydev)
22700db8189SAndy Fleming {
22800db8189SAndy Fleming 	int err;
22900db8189SAndy Fleming 
23000db8189SAndy Fleming 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
23123beb38fSAndrew Lunn 		err = phy_write(phydev, MII_M1011_IMASK,
23223beb38fSAndrew Lunn 				MII_M1011_IMASK_INIT);
23300db8189SAndy Fleming 	else
23423beb38fSAndrew Lunn 		err = phy_write(phydev, MII_M1011_IMASK,
23523beb38fSAndrew Lunn 				MII_M1011_IMASK_CLEAR);
23600db8189SAndy Fleming 
23700db8189SAndy Fleming 	return err;
23800db8189SAndy Fleming }
23900db8189SAndy Fleming 
240239aa55bSDavid Thomson static int marvell_set_polarity(struct phy_device *phydev, int polarity)
241239aa55bSDavid Thomson {
242239aa55bSDavid Thomson 	int reg;
243239aa55bSDavid Thomson 	int err;
244239aa55bSDavid Thomson 	int val;
245239aa55bSDavid Thomson 
246239aa55bSDavid Thomson 	/* get the current settings */
247239aa55bSDavid Thomson 	reg = phy_read(phydev, MII_M1011_PHY_SCR);
248239aa55bSDavid Thomson 	if (reg < 0)
249239aa55bSDavid Thomson 		return reg;
250239aa55bSDavid Thomson 
251239aa55bSDavid Thomson 	val = reg;
252239aa55bSDavid Thomson 	val &= ~MII_M1011_PHY_SCR_AUTO_CROSS;
253239aa55bSDavid Thomson 	switch (polarity) {
254239aa55bSDavid Thomson 	case ETH_TP_MDI:
255239aa55bSDavid Thomson 		val |= MII_M1011_PHY_SCR_MDI;
256239aa55bSDavid Thomson 		break;
257239aa55bSDavid Thomson 	case ETH_TP_MDI_X:
258239aa55bSDavid Thomson 		val |= MII_M1011_PHY_SCR_MDI_X;
259239aa55bSDavid Thomson 		break;
260239aa55bSDavid Thomson 	case ETH_TP_MDI_AUTO:
261239aa55bSDavid Thomson 	case ETH_TP_MDI_INVALID:
262239aa55bSDavid Thomson 	default:
263239aa55bSDavid Thomson 		val |= MII_M1011_PHY_SCR_AUTO_CROSS;
264239aa55bSDavid Thomson 		break;
265239aa55bSDavid Thomson 	}
266239aa55bSDavid Thomson 
267239aa55bSDavid Thomson 	if (val != reg) {
268239aa55bSDavid Thomson 		/* Set the new polarity value in the register */
269239aa55bSDavid Thomson 		err = phy_write(phydev, MII_M1011_PHY_SCR, val);
270239aa55bSDavid Thomson 		if (err)
271239aa55bSDavid Thomson 			return err;
272239aa55bSDavid Thomson 	}
273239aa55bSDavid Thomson 
274239aa55bSDavid Thomson 	return 0;
275239aa55bSDavid Thomson }
276239aa55bSDavid Thomson 
27700db8189SAndy Fleming static int marvell_config_aneg(struct phy_device *phydev)
27800db8189SAndy Fleming {
27900db8189SAndy Fleming 	int err;
28000db8189SAndy Fleming 
2814e26c5c3SRaju Lakkaraju 	err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
28276884679SAndy Fleming 	if (err < 0)
28376884679SAndy Fleming 		return err;
28476884679SAndy Fleming 
28576884679SAndy Fleming 	err = phy_write(phydev, MII_M1111_PHY_LED_CONTROL,
28676884679SAndy Fleming 			MII_M1111_PHY_LED_DIRECT);
28776884679SAndy Fleming 	if (err < 0)
28876884679SAndy Fleming 		return err;
28900db8189SAndy Fleming 
29000db8189SAndy Fleming 	err = genphy_config_aneg(phydev);
2918ff44985SAnton Vorontsov 	if (err < 0)
29200db8189SAndy Fleming 		return err;
2938ff44985SAnton Vorontsov 
2948ff44985SAnton Vorontsov 	if (phydev->autoneg != AUTONEG_ENABLE) {
2958ff44985SAnton Vorontsov 		int bmcr;
2968ff44985SAnton Vorontsov 
2970c3439bcSAndrew Lunn 		/* A write to speed/duplex bits (that is performed by
2988ff44985SAnton Vorontsov 		 * genphy_config_aneg() call above) must be followed by
2998ff44985SAnton Vorontsov 		 * a software reset. Otherwise, the write has no effect.
3008ff44985SAnton Vorontsov 		 */
3018ff44985SAnton Vorontsov 		bmcr = phy_read(phydev, MII_BMCR);
3028ff44985SAnton Vorontsov 		if (bmcr < 0)
3038ff44985SAnton Vorontsov 			return bmcr;
3048ff44985SAnton Vorontsov 
3058ff44985SAnton Vorontsov 		err = phy_write(phydev, MII_BMCR, bmcr | BMCR_RESET);
3068ff44985SAnton Vorontsov 		if (err < 0)
3078ff44985SAnton Vorontsov 			return err;
3088ff44985SAnton Vorontsov 	}
3098ff44985SAnton Vorontsov 
3108ff44985SAnton Vorontsov 	return 0;
31100db8189SAndy Fleming }
31200db8189SAndy Fleming 
313f2899788SAndrew Lunn static int m88e1101_config_aneg(struct phy_device *phydev)
314f2899788SAndrew Lunn {
315f2899788SAndrew Lunn 	int err;
316f2899788SAndrew Lunn 
317f2899788SAndrew Lunn 	/* This Marvell PHY has an errata which requires
318f2899788SAndrew Lunn 	 * that certain registers get written in order
319f2899788SAndrew Lunn 	 * to restart autonegotiation
320f2899788SAndrew Lunn 	 */
321f2899788SAndrew Lunn 	err = phy_write(phydev, MII_BMCR, BMCR_RESET);
322f2899788SAndrew Lunn 
323f2899788SAndrew Lunn 	if (err < 0)
324f2899788SAndrew Lunn 		return err;
325f2899788SAndrew Lunn 
326f2899788SAndrew Lunn 	err = phy_write(phydev, 0x1d, 0x1f);
327f2899788SAndrew Lunn 	if (err < 0)
328f2899788SAndrew Lunn 		return err;
329f2899788SAndrew Lunn 
330f2899788SAndrew Lunn 	err = phy_write(phydev, 0x1e, 0x200c);
331f2899788SAndrew Lunn 	if (err < 0)
332f2899788SAndrew Lunn 		return err;
333f2899788SAndrew Lunn 
334f2899788SAndrew Lunn 	err = phy_write(phydev, 0x1d, 0x5);
335f2899788SAndrew Lunn 	if (err < 0)
336f2899788SAndrew Lunn 		return err;
337f2899788SAndrew Lunn 
338f2899788SAndrew Lunn 	err = phy_write(phydev, 0x1e, 0);
339f2899788SAndrew Lunn 	if (err < 0)
340f2899788SAndrew Lunn 		return err;
341f2899788SAndrew Lunn 
342f2899788SAndrew Lunn 	err = phy_write(phydev, 0x1e, 0x100);
343f2899788SAndrew Lunn 	if (err < 0)
344f2899788SAndrew Lunn 		return err;
345f2899788SAndrew Lunn 
346f2899788SAndrew Lunn 	return marvell_config_aneg(phydev);
347f2899788SAndrew Lunn }
348f2899788SAndrew Lunn 
3493ec0a0f1SHarini Katakam static int m88e1111_config_aneg(struct phy_device *phydev)
3503ec0a0f1SHarini Katakam {
3513ec0a0f1SHarini Katakam 	int err;
3523ec0a0f1SHarini Katakam 
3533ec0a0f1SHarini Katakam 	/* The Marvell PHY has an errata which requires
3543ec0a0f1SHarini Katakam 	 * that certain registers get written in order
3553ec0a0f1SHarini Katakam 	 * to restart autonegotiation
3563ec0a0f1SHarini Katakam 	 */
3573ec0a0f1SHarini Katakam 	err = phy_write(phydev, MII_BMCR, BMCR_RESET);
3583ec0a0f1SHarini Katakam 
3594e26c5c3SRaju Lakkaraju 	err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
3603ec0a0f1SHarini Katakam 	if (err < 0)
3613ec0a0f1SHarini Katakam 		return err;
3623ec0a0f1SHarini Katakam 
3633ec0a0f1SHarini Katakam 	err = phy_write(phydev, MII_M1111_PHY_LED_CONTROL,
3643ec0a0f1SHarini Katakam 			MII_M1111_PHY_LED_DIRECT);
3653ec0a0f1SHarini Katakam 	if (err < 0)
3663ec0a0f1SHarini Katakam 		return err;
3673ec0a0f1SHarini Katakam 
3683ec0a0f1SHarini Katakam 	err = genphy_config_aneg(phydev);
3693ec0a0f1SHarini Katakam 	if (err < 0)
3703ec0a0f1SHarini Katakam 		return err;
3713ec0a0f1SHarini Katakam 
3723ec0a0f1SHarini Katakam 	if (phydev->autoneg != AUTONEG_ENABLE) {
3733ec0a0f1SHarini Katakam 		int bmcr;
3743ec0a0f1SHarini Katakam 
3753ec0a0f1SHarini Katakam 		/* A write to speed/duplex bits (that is performed by
3763ec0a0f1SHarini Katakam 		 * genphy_config_aneg() call above) must be followed by
3773ec0a0f1SHarini Katakam 		 * a software reset. Otherwise, the write has no effect.
3783ec0a0f1SHarini Katakam 		 */
3793ec0a0f1SHarini Katakam 		bmcr = phy_read(phydev, MII_BMCR);
3803ec0a0f1SHarini Katakam 		if (bmcr < 0)
3813ec0a0f1SHarini Katakam 			return bmcr;
3823ec0a0f1SHarini Katakam 
3833ec0a0f1SHarini Katakam 		err = phy_write(phydev, MII_BMCR, bmcr | BMCR_RESET);
3843ec0a0f1SHarini Katakam 		if (err < 0)
3853ec0a0f1SHarini Katakam 			return err;
3863ec0a0f1SHarini Katakam 	}
3873ec0a0f1SHarini Katakam 
3883ec0a0f1SHarini Katakam 	return 0;
3893ec0a0f1SHarini Katakam }
3903ec0a0f1SHarini Katakam 
391cf41a51dSDavid Daney #ifdef CONFIG_OF_MDIO
3920c3439bcSAndrew Lunn /* Set and/or override some configuration registers based on the
393cf41a51dSDavid Daney  * marvell,reg-init property stored in the of_node for the phydev.
394cf41a51dSDavid Daney  *
395cf41a51dSDavid Daney  * marvell,reg-init = <reg-page reg mask value>,...;
396cf41a51dSDavid Daney  *
397cf41a51dSDavid Daney  * There may be one or more sets of <reg-page reg mask value>:
398cf41a51dSDavid Daney  *
399cf41a51dSDavid Daney  * reg-page: which register bank to use.
400cf41a51dSDavid Daney  * reg: the register.
401cf41a51dSDavid Daney  * mask: if non-zero, ANDed with existing register value.
402cf41a51dSDavid Daney  * value: ORed with the masked value and written to the regiser.
403cf41a51dSDavid Daney  *
404cf41a51dSDavid Daney  */
405cf41a51dSDavid Daney static int marvell_of_reg_init(struct phy_device *phydev)
406cf41a51dSDavid Daney {
407cf41a51dSDavid Daney 	const __be32 *paddr;
408b5718b5aSUwe Kleine-König 	int len, i, saved_page, current_page, ret;
409cf41a51dSDavid Daney 
410e5a03bfdSAndrew Lunn 	if (!phydev->mdio.dev.of_node)
411cf41a51dSDavid Daney 		return 0;
412cf41a51dSDavid Daney 
413e5a03bfdSAndrew Lunn 	paddr = of_get_property(phydev->mdio.dev.of_node,
414e5a03bfdSAndrew Lunn 				"marvell,reg-init", &len);
415cf41a51dSDavid Daney 	if (!paddr || len < (4 * sizeof(*paddr)))
416cf41a51dSDavid Daney 		return 0;
417cf41a51dSDavid Daney 
4186427bb2dSAndrew Lunn 	saved_page = marvell_get_page(phydev);
419cf41a51dSDavid Daney 	if (saved_page < 0)
420cf41a51dSDavid Daney 		return saved_page;
421cf41a51dSDavid Daney 	current_page = saved_page;
422cf41a51dSDavid Daney 
423cf41a51dSDavid Daney 	ret = 0;
424cf41a51dSDavid Daney 	len /= sizeof(*paddr);
425cf41a51dSDavid Daney 	for (i = 0; i < len - 3; i += 4) {
4266427bb2dSAndrew Lunn 		u16 page = be32_to_cpup(paddr + i);
427cf41a51dSDavid Daney 		u16 reg = be32_to_cpup(paddr + i + 1);
428cf41a51dSDavid Daney 		u16 mask = be32_to_cpup(paddr + i + 2);
429cf41a51dSDavid Daney 		u16 val_bits = be32_to_cpup(paddr + i + 3);
430cf41a51dSDavid Daney 		int val;
431cf41a51dSDavid Daney 
4326427bb2dSAndrew Lunn 		if (page != current_page) {
4336427bb2dSAndrew Lunn 			current_page = page;
4346427bb2dSAndrew Lunn 			ret = marvell_set_page(phydev, page);
435cf41a51dSDavid Daney 			if (ret < 0)
436cf41a51dSDavid Daney 				goto err;
437cf41a51dSDavid Daney 		}
438cf41a51dSDavid Daney 
439cf41a51dSDavid Daney 		val = 0;
440cf41a51dSDavid Daney 		if (mask) {
441cf41a51dSDavid Daney 			val = phy_read(phydev, reg);
442cf41a51dSDavid Daney 			if (val < 0) {
443cf41a51dSDavid Daney 				ret = val;
444cf41a51dSDavid Daney 				goto err;
445cf41a51dSDavid Daney 			}
446cf41a51dSDavid Daney 			val &= mask;
447cf41a51dSDavid Daney 		}
448cf41a51dSDavid Daney 		val |= val_bits;
449cf41a51dSDavid Daney 
450cf41a51dSDavid Daney 		ret = phy_write(phydev, reg, val);
451cf41a51dSDavid Daney 		if (ret < 0)
452cf41a51dSDavid Daney 			goto err;
453cf41a51dSDavid Daney 	}
454cf41a51dSDavid Daney err:
455b5718b5aSUwe Kleine-König 	if (current_page != saved_page) {
4566427bb2dSAndrew Lunn 		i = marvell_set_page(phydev, saved_page);
457cf41a51dSDavid Daney 		if (ret == 0)
458cf41a51dSDavid Daney 			ret = i;
459cf41a51dSDavid Daney 	}
460cf41a51dSDavid Daney 	return ret;
461cf41a51dSDavid Daney }
462cf41a51dSDavid Daney #else
463cf41a51dSDavid Daney static int marvell_of_reg_init(struct phy_device *phydev)
464cf41a51dSDavid Daney {
465cf41a51dSDavid Daney 	return 0;
466cf41a51dSDavid Daney }
467cf41a51dSDavid Daney #endif /* CONFIG_OF_MDIO */
468cf41a51dSDavid Daney 
469140bc929SSergei Poselenov static int m88e1121_config_aneg(struct phy_device *phydev)
470140bc929SSergei Poselenov {
471c477d044SCyril Chemparathy 	int err, oldpage, mscr;
472c477d044SCyril Chemparathy 
47352295666SAndrew Lunn 	oldpage = marvell_get_set_page(phydev, MII_MARVELL_MSCR_PAGE);
47453798328SAndrew Lunn 	if (oldpage < 0)
47553798328SAndrew Lunn 		return oldpage;
476be8c6480SArnaud Patard 
47732a64161SFlorian Fainelli 	if (phy_interface_is_rgmii(phydev)) {
478c477d044SCyril Chemparathy 		mscr = phy_read(phydev, MII_88E1121_PHY_MSCR_REG) &
479c477d044SCyril Chemparathy 			MII_88E1121_PHY_MSCR_DELAY_MASK;
480c477d044SCyril Chemparathy 
481c477d044SCyril Chemparathy 		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
482c477d044SCyril Chemparathy 			mscr |= (MII_88E1121_PHY_MSCR_RX_DELAY |
483c477d044SCyril Chemparathy 				 MII_88E1121_PHY_MSCR_TX_DELAY);
484c477d044SCyril Chemparathy 		else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
485c477d044SCyril Chemparathy 			mscr |= MII_88E1121_PHY_MSCR_RX_DELAY;
486c477d044SCyril Chemparathy 		else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
487c477d044SCyril Chemparathy 			mscr |= MII_88E1121_PHY_MSCR_TX_DELAY;
488c477d044SCyril Chemparathy 
489c477d044SCyril Chemparathy 		err = phy_write(phydev, MII_88E1121_PHY_MSCR_REG, mscr);
490c477d044SCyril Chemparathy 		if (err < 0)
491c477d044SCyril Chemparathy 			return err;
492be8c6480SArnaud Patard 	}
493c477d044SCyril Chemparathy 
4946427bb2dSAndrew Lunn 	marvell_set_page(phydev, oldpage);
495140bc929SSergei Poselenov 
496140bc929SSergei Poselenov 	err = phy_write(phydev, MII_BMCR, BMCR_RESET);
497140bc929SSergei Poselenov 	if (err < 0)
498140bc929SSergei Poselenov 		return err;
499140bc929SSergei Poselenov 
500140bc929SSergei Poselenov 	err = phy_write(phydev, MII_M1011_PHY_SCR,
501140bc929SSergei Poselenov 			MII_M1011_PHY_SCR_AUTO_CROSS);
502140bc929SSergei Poselenov 	if (err < 0)
503140bc929SSergei Poselenov 		return err;
504140bc929SSergei Poselenov 
505fdecf36fSClemens Gruber 	return genphy_config_aneg(phydev);
506140bc929SSergei Poselenov }
507140bc929SSergei Poselenov 
508337ac9d5SCyril Chemparathy static int m88e1318_config_aneg(struct phy_device *phydev)
5093ff1c259SCyril Chemparathy {
5103ff1c259SCyril Chemparathy 	int err, oldpage, mscr;
5113ff1c259SCyril Chemparathy 
51252295666SAndrew Lunn 	oldpage = marvell_get_set_page(phydev, MII_MARVELL_MSCR_PAGE);
51353798328SAndrew Lunn 	if (oldpage < 0)
51453798328SAndrew Lunn 		return oldpage;
5153ff1c259SCyril Chemparathy 
516337ac9d5SCyril Chemparathy 	mscr = phy_read(phydev, MII_88E1318S_PHY_MSCR1_REG);
517337ac9d5SCyril Chemparathy 	mscr |= MII_88E1318S_PHY_MSCR1_PAD_ODD;
5183ff1c259SCyril Chemparathy 
519337ac9d5SCyril Chemparathy 	err = phy_write(phydev, MII_88E1318S_PHY_MSCR1_REG, mscr);
5203ff1c259SCyril Chemparathy 	if (err < 0)
5213ff1c259SCyril Chemparathy 		return err;
5223ff1c259SCyril Chemparathy 
5236427bb2dSAndrew Lunn 	err = marvell_set_page(phydev, oldpage);
5243ff1c259SCyril Chemparathy 	if (err < 0)
5253ff1c259SCyril Chemparathy 		return err;
5263ff1c259SCyril Chemparathy 
5273ff1c259SCyril Chemparathy 	return m88e1121_config_aneg(phydev);
5283ff1c259SCyril Chemparathy }
5293ff1c259SCyril Chemparathy 
53078301ebeSCharles-Antoine Couret /**
53178301ebeSCharles-Antoine Couret  * ethtool_adv_to_fiber_adv_t
53278301ebeSCharles-Antoine Couret  * @ethadv: the ethtool advertisement settings
53378301ebeSCharles-Antoine Couret  *
53478301ebeSCharles-Antoine Couret  * A small helper function that translates ethtool advertisement
53578301ebeSCharles-Antoine Couret  * settings to phy autonegotiation advertisements for the
53678301ebeSCharles-Antoine Couret  * MII_ADV register for fiber link.
53778301ebeSCharles-Antoine Couret  */
53878301ebeSCharles-Antoine Couret static inline u32 ethtool_adv_to_fiber_adv_t(u32 ethadv)
53978301ebeSCharles-Antoine Couret {
54078301ebeSCharles-Antoine Couret 	u32 result = 0;
54178301ebeSCharles-Antoine Couret 
54278301ebeSCharles-Antoine Couret 	if (ethadv & ADVERTISED_1000baseT_Half)
54378301ebeSCharles-Antoine Couret 		result |= ADVERTISE_FIBER_1000HALF;
54478301ebeSCharles-Antoine Couret 	if (ethadv & ADVERTISED_1000baseT_Full)
54578301ebeSCharles-Antoine Couret 		result |= ADVERTISE_FIBER_1000FULL;
54678301ebeSCharles-Antoine Couret 
54778301ebeSCharles-Antoine Couret 	if ((ethadv & ADVERTISE_PAUSE_ASYM) && (ethadv & ADVERTISE_PAUSE_CAP))
54878301ebeSCharles-Antoine Couret 		result |= LPA_PAUSE_ASYM_FIBER;
54978301ebeSCharles-Antoine Couret 	else if (ethadv & ADVERTISE_PAUSE_CAP)
55078301ebeSCharles-Antoine Couret 		result |= (ADVERTISE_PAUSE_FIBER
55178301ebeSCharles-Antoine Couret 			   & (~ADVERTISE_PAUSE_ASYM_FIBER));
55278301ebeSCharles-Antoine Couret 
55378301ebeSCharles-Antoine Couret 	return result;
55478301ebeSCharles-Antoine Couret }
55578301ebeSCharles-Antoine Couret 
55678301ebeSCharles-Antoine Couret /**
55778301ebeSCharles-Antoine Couret  * marvell_config_aneg_fiber - restart auto-negotiation or write BMCR
55878301ebeSCharles-Antoine Couret  * @phydev: target phy_device struct
55978301ebeSCharles-Antoine Couret  *
56078301ebeSCharles-Antoine Couret  * Description: If auto-negotiation is enabled, we configure the
56178301ebeSCharles-Antoine Couret  *   advertising, and then restart auto-negotiation.  If it is not
56278301ebeSCharles-Antoine Couret  *   enabled, then we write the BMCR. Adapted for fiber link in
56378301ebeSCharles-Antoine Couret  *   some Marvell's devices.
56478301ebeSCharles-Antoine Couret  */
56578301ebeSCharles-Antoine Couret static int marvell_config_aneg_fiber(struct phy_device *phydev)
56678301ebeSCharles-Antoine Couret {
56778301ebeSCharles-Antoine Couret 	int changed = 0;
56878301ebeSCharles-Antoine Couret 	int err;
56978301ebeSCharles-Antoine Couret 	int adv, oldadv;
57078301ebeSCharles-Antoine Couret 	u32 advertise;
57178301ebeSCharles-Antoine Couret 
57278301ebeSCharles-Antoine Couret 	if (phydev->autoneg != AUTONEG_ENABLE)
57378301ebeSCharles-Antoine Couret 		return genphy_setup_forced(phydev);
57478301ebeSCharles-Antoine Couret 
57578301ebeSCharles-Antoine Couret 	/* Only allow advertising what this PHY supports */
57678301ebeSCharles-Antoine Couret 	phydev->advertising &= phydev->supported;
57778301ebeSCharles-Antoine Couret 	advertise = phydev->advertising;
57878301ebeSCharles-Antoine Couret 
57978301ebeSCharles-Antoine Couret 	/* Setup fiber advertisement */
58078301ebeSCharles-Antoine Couret 	adv = phy_read(phydev, MII_ADVERTISE);
58178301ebeSCharles-Antoine Couret 	if (adv < 0)
58278301ebeSCharles-Antoine Couret 		return adv;
58378301ebeSCharles-Antoine Couret 
58478301ebeSCharles-Antoine Couret 	oldadv = adv;
58578301ebeSCharles-Antoine Couret 	adv &= ~(ADVERTISE_FIBER_1000HALF | ADVERTISE_FIBER_1000FULL
58678301ebeSCharles-Antoine Couret 		| LPA_PAUSE_FIBER);
58778301ebeSCharles-Antoine Couret 	adv |= ethtool_adv_to_fiber_adv_t(advertise);
58878301ebeSCharles-Antoine Couret 
58978301ebeSCharles-Antoine Couret 	if (adv != oldadv) {
59078301ebeSCharles-Antoine Couret 		err = phy_write(phydev, MII_ADVERTISE, adv);
59178301ebeSCharles-Antoine Couret 		if (err < 0)
59278301ebeSCharles-Antoine Couret 			return err;
59378301ebeSCharles-Antoine Couret 
59478301ebeSCharles-Antoine Couret 		changed = 1;
59578301ebeSCharles-Antoine Couret 	}
59678301ebeSCharles-Antoine Couret 
59778301ebeSCharles-Antoine Couret 	if (changed == 0) {
59878301ebeSCharles-Antoine Couret 		/* Advertisement hasn't changed, but maybe aneg was never on to
59978301ebeSCharles-Antoine Couret 		 * begin with?  Or maybe phy was isolated?
60078301ebeSCharles-Antoine Couret 		 */
60178301ebeSCharles-Antoine Couret 		int ctl = phy_read(phydev, MII_BMCR);
60278301ebeSCharles-Antoine Couret 
60378301ebeSCharles-Antoine Couret 		if (ctl < 0)
60478301ebeSCharles-Antoine Couret 			return ctl;
60578301ebeSCharles-Antoine Couret 
60678301ebeSCharles-Antoine Couret 		if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE))
60778301ebeSCharles-Antoine Couret 			changed = 1; /* do restart aneg */
60878301ebeSCharles-Antoine Couret 	}
60978301ebeSCharles-Antoine Couret 
61078301ebeSCharles-Antoine Couret 	/* Only restart aneg if we are advertising something different
61178301ebeSCharles-Antoine Couret 	 * than we were before.
61278301ebeSCharles-Antoine Couret 	 */
61378301ebeSCharles-Antoine Couret 	if (changed > 0)
61478301ebeSCharles-Antoine Couret 		changed = genphy_restart_aneg(phydev);
61578301ebeSCharles-Antoine Couret 
61678301ebeSCharles-Antoine Couret 	return changed;
61778301ebeSCharles-Antoine Couret }
61878301ebeSCharles-Antoine Couret 
61910e24caaSMichal Simek static int m88e1510_config_aneg(struct phy_device *phydev)
62010e24caaSMichal Simek {
62110e24caaSMichal Simek 	int err;
62210e24caaSMichal Simek 
62352295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
62478301ebeSCharles-Antoine Couret 	if (err < 0)
62578301ebeSCharles-Antoine Couret 		goto error;
62678301ebeSCharles-Antoine Couret 
62778301ebeSCharles-Antoine Couret 	/* Configure the copper link first */
62810e24caaSMichal Simek 	err = m88e1318_config_aneg(phydev);
62910e24caaSMichal Simek 	if (err < 0)
63078301ebeSCharles-Antoine Couret 		goto error;
63110e24caaSMichal Simek 
63278301ebeSCharles-Antoine Couret 	/* Then the fiber link */
63352295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
63478301ebeSCharles-Antoine Couret 	if (err < 0)
63578301ebeSCharles-Antoine Couret 		goto error;
63678301ebeSCharles-Antoine Couret 
63778301ebeSCharles-Antoine Couret 	err = marvell_config_aneg_fiber(phydev);
63878301ebeSCharles-Antoine Couret 	if (err < 0)
63978301ebeSCharles-Antoine Couret 		goto error;
64078301ebeSCharles-Antoine Couret 
64152295666SAndrew Lunn 	return marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
64278301ebeSCharles-Antoine Couret 
64378301ebeSCharles-Antoine Couret error:
64452295666SAndrew Lunn 	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
64578301ebeSCharles-Antoine Couret 	return err;
64679be1a1cSClemens Gruber }
64779be1a1cSClemens Gruber 
64879be1a1cSClemens Gruber static int marvell_config_init(struct phy_device *phydev)
64979be1a1cSClemens Gruber {
65079be1a1cSClemens Gruber 	/* Set registers from marvell,reg-init DT property */
65110e24caaSMichal Simek 	return marvell_of_reg_init(phydev);
65210e24caaSMichal Simek }
65310e24caaSMichal Simek 
6543da09a51SMichal Simek static int m88e1116r_config_init(struct phy_device *phydev)
6553da09a51SMichal Simek {
6563da09a51SMichal Simek 	int temp;
6573da09a51SMichal Simek 	int err;
6583da09a51SMichal Simek 
6593da09a51SMichal Simek 	temp = phy_read(phydev, MII_BMCR);
6603da09a51SMichal Simek 	temp |= BMCR_RESET;
6613da09a51SMichal Simek 	err = phy_write(phydev, MII_BMCR, temp);
6623da09a51SMichal Simek 	if (err < 0)
6633da09a51SMichal Simek 		return err;
6643da09a51SMichal Simek 
6653da09a51SMichal Simek 	mdelay(500);
6663da09a51SMichal Simek 
66752295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
6683da09a51SMichal Simek 	if (err < 0)
6693da09a51SMichal Simek 		return err;
6703da09a51SMichal Simek 
6713da09a51SMichal Simek 	temp = phy_read(phydev, MII_M1011_PHY_SCR);
6723da09a51SMichal Simek 	temp |= (7 << 12);	/* max number of gigabit attempts */
6733da09a51SMichal Simek 	temp |= (1 << 11);	/* enable downshift */
6743da09a51SMichal Simek 	temp |= MII_M1011_PHY_SCR_AUTO_CROSS;
6753da09a51SMichal Simek 	err = phy_write(phydev, MII_M1011_PHY_SCR, temp);
6763da09a51SMichal Simek 	if (err < 0)
6773da09a51SMichal Simek 		return err;
6783da09a51SMichal Simek 
67952295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_MSCR_PAGE);
6803da09a51SMichal Simek 	if (err < 0)
6813da09a51SMichal Simek 		return err;
6823da09a51SMichal Simek 	temp = phy_read(phydev, MII_M1116R_CONTROL_REG_MAC);
6833da09a51SMichal Simek 	temp |= (1 << 5);
6843da09a51SMichal Simek 	temp |= (1 << 4);
6853da09a51SMichal Simek 	err = phy_write(phydev, MII_M1116R_CONTROL_REG_MAC, temp);
6863da09a51SMichal Simek 	if (err < 0)
6873da09a51SMichal Simek 		return err;
68852295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
6893da09a51SMichal Simek 	if (err < 0)
6903da09a51SMichal Simek 		return err;
6913da09a51SMichal Simek 
6923da09a51SMichal Simek 	temp = phy_read(phydev, MII_BMCR);
6933da09a51SMichal Simek 	temp |= BMCR_RESET;
6943da09a51SMichal Simek 	err = phy_write(phydev, MII_BMCR, temp);
6953da09a51SMichal Simek 	if (err < 0)
6963da09a51SMichal Simek 		return err;
6973da09a51SMichal Simek 
6983da09a51SMichal Simek 	mdelay(500);
6993da09a51SMichal Simek 
70079be1a1cSClemens Gruber 	return marvell_config_init(phydev);
7013da09a51SMichal Simek }
7023da09a51SMichal Simek 
7036b358aedSSebastian Hesselbarth static int m88e3016_config_init(struct phy_device *phydev)
7046b358aedSSebastian Hesselbarth {
7056b358aedSSebastian Hesselbarth 	int reg;
7066b358aedSSebastian Hesselbarth 
7076b358aedSSebastian Hesselbarth 	/* Enable Scrambler and Auto-Crossover */
7086b358aedSSebastian Hesselbarth 	reg = phy_read(phydev, MII_88E3016_PHY_SPEC_CTRL);
7096b358aedSSebastian Hesselbarth 	if (reg < 0)
7106b358aedSSebastian Hesselbarth 		return reg;
7116b358aedSSebastian Hesselbarth 
7126b358aedSSebastian Hesselbarth 	reg &= ~MII_88E3016_DISABLE_SCRAMBLER;
7136b358aedSSebastian Hesselbarth 	reg |= MII_88E3016_AUTO_MDIX_CROSSOVER;
7146b358aedSSebastian Hesselbarth 
7156b358aedSSebastian Hesselbarth 	reg = phy_write(phydev, MII_88E3016_PHY_SPEC_CTRL, reg);
7166b358aedSSebastian Hesselbarth 	if (reg < 0)
7176b358aedSSebastian Hesselbarth 		return reg;
7186b358aedSSebastian Hesselbarth 
71979be1a1cSClemens Gruber 	return marvell_config_init(phydev);
7206b358aedSSebastian Hesselbarth }
7216b358aedSSebastian Hesselbarth 
722e1dde8dcSAndrew Lunn static int m88e1111_config_init_rgmii(struct phy_device *phydev)
723895ee682SKim Phillips {
724895ee682SKim Phillips 	int err;
725be937f1fSAlexandr Smirnov 	int temp;
726be937f1fSAlexandr Smirnov 
727895ee682SKim Phillips 	temp = phy_read(phydev, MII_M1111_PHY_EXT_CR);
728895ee682SKim Phillips 	if (temp < 0)
729895ee682SKim Phillips 		return temp;
730895ee682SKim Phillips 
7319daf5a76SKim Phillips 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
732895ee682SKim Phillips 		temp |= (MII_M1111_RX_DELAY | MII_M1111_TX_DELAY);
7339daf5a76SKim Phillips 	} else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
7349daf5a76SKim Phillips 		temp &= ~MII_M1111_TX_DELAY;
7359daf5a76SKim Phillips 		temp |= MII_M1111_RX_DELAY;
7369daf5a76SKim Phillips 	} else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
7379daf5a76SKim Phillips 		temp &= ~MII_M1111_RX_DELAY;
7389daf5a76SKim Phillips 		temp |= MII_M1111_TX_DELAY;
7399daf5a76SKim Phillips 	}
740895ee682SKim Phillips 
741895ee682SKim Phillips 	err = phy_write(phydev, MII_M1111_PHY_EXT_CR, temp);
742895ee682SKim Phillips 	if (err < 0)
743895ee682SKim Phillips 		return err;
744895ee682SKim Phillips 
745895ee682SKim Phillips 	temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
746895ee682SKim Phillips 	if (temp < 0)
747895ee682SKim Phillips 		return temp;
748895ee682SKim Phillips 
749895ee682SKim Phillips 	temp &= ~(MII_M1111_HWCFG_MODE_MASK);
750be937f1fSAlexandr Smirnov 
7517239016dSWang Jian 	if (temp & MII_M1111_HWCFG_FIBER_COPPER_RES)
752be937f1fSAlexandr Smirnov 		temp |= MII_M1111_HWCFG_MODE_FIBER_RGMII;
753be937f1fSAlexandr Smirnov 	else
754be937f1fSAlexandr Smirnov 		temp |= MII_M1111_HWCFG_MODE_COPPER_RGMII;
755895ee682SKim Phillips 
756e1dde8dcSAndrew Lunn 	return phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
757895ee682SKim Phillips }
758895ee682SKim Phillips 
759e1dde8dcSAndrew Lunn static int m88e1111_config_init_sgmii(struct phy_device *phydev)
760e1dde8dcSAndrew Lunn {
761e1dde8dcSAndrew Lunn 	int err;
762e1dde8dcSAndrew Lunn 	int temp;
763e1dde8dcSAndrew Lunn 
7644117b5beSKapil Juneja 	temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
7654117b5beSKapil Juneja 	if (temp < 0)
7664117b5beSKapil Juneja 		return temp;
7674117b5beSKapil Juneja 
7684117b5beSKapil Juneja 	temp &= ~(MII_M1111_HWCFG_MODE_MASK);
7694117b5beSKapil Juneja 	temp |= MII_M1111_HWCFG_MODE_SGMII_NO_CLK;
77032d0c1e1SHaiying Wang 	temp |= MII_M1111_HWCFG_FIBER_COPPER_AUTO;
7714117b5beSKapil Juneja 
7724117b5beSKapil Juneja 	err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
7734117b5beSKapil Juneja 	if (err < 0)
7744117b5beSKapil Juneja 		return err;
77507151bc9SMadalin Bucur 
77607151bc9SMadalin Bucur 	/* make sure copper is selected */
77752295666SAndrew Lunn 	return marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
7784117b5beSKapil Juneja }
7794117b5beSKapil Juneja 
780e1dde8dcSAndrew Lunn static int m88e1111_config_init_rtbi(struct phy_device *phydev)
781e1dde8dcSAndrew Lunn {
782e1dde8dcSAndrew Lunn 	int err;
783e1dde8dcSAndrew Lunn 	int temp;
784e1dde8dcSAndrew Lunn 
7855f8cbc13SLiu Yu-B13201 	temp = phy_read(phydev, MII_M1111_PHY_EXT_CR);
7865f8cbc13SLiu Yu-B13201 	if (temp < 0)
7875f8cbc13SLiu Yu-B13201 		return temp;
788e1dde8dcSAndrew Lunn 
7895f8cbc13SLiu Yu-B13201 	temp |= (MII_M1111_RX_DELAY | MII_M1111_TX_DELAY);
7905f8cbc13SLiu Yu-B13201 	err = phy_write(phydev, MII_M1111_PHY_EXT_CR, temp);
7915f8cbc13SLiu Yu-B13201 	if (err < 0)
7925f8cbc13SLiu Yu-B13201 		return err;
7935f8cbc13SLiu Yu-B13201 
7945f8cbc13SLiu Yu-B13201 	temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
7955f8cbc13SLiu Yu-B13201 	if (temp < 0)
7965f8cbc13SLiu Yu-B13201 		return temp;
797e1dde8dcSAndrew Lunn 
798e1dde8dcSAndrew Lunn 	temp &= ~(MII_M1111_HWCFG_MODE_MASK |
799e1dde8dcSAndrew Lunn 		  MII_M1111_HWCFG_FIBER_COPPER_RES);
8005f8cbc13SLiu Yu-B13201 	temp |= 0x7 | MII_M1111_HWCFG_FIBER_COPPER_AUTO;
801e1dde8dcSAndrew Lunn 
8025f8cbc13SLiu Yu-B13201 	err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
8035f8cbc13SLiu Yu-B13201 	if (err < 0)
8045f8cbc13SLiu Yu-B13201 		return err;
8055f8cbc13SLiu Yu-B13201 
8065f8cbc13SLiu Yu-B13201 	/* soft reset */
8075f8cbc13SLiu Yu-B13201 	err = phy_write(phydev, MII_BMCR, BMCR_RESET);
8085f8cbc13SLiu Yu-B13201 	if (err < 0)
8095f8cbc13SLiu Yu-B13201 		return err;
810e1dde8dcSAndrew Lunn 
8115f8cbc13SLiu Yu-B13201 	do
8125f8cbc13SLiu Yu-B13201 		temp = phy_read(phydev, MII_BMCR);
8135f8cbc13SLiu Yu-B13201 	while (temp & BMCR_RESET);
8145f8cbc13SLiu Yu-B13201 
8155f8cbc13SLiu Yu-B13201 	temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
8165f8cbc13SLiu Yu-B13201 	if (temp < 0)
8175f8cbc13SLiu Yu-B13201 		return temp;
818e1dde8dcSAndrew Lunn 
819e1dde8dcSAndrew Lunn 	temp &= ~(MII_M1111_HWCFG_MODE_MASK |
820e1dde8dcSAndrew Lunn 		  MII_M1111_HWCFG_FIBER_COPPER_RES);
821e1dde8dcSAndrew Lunn 	temp |= MII_M1111_HWCFG_MODE_COPPER_RTBI |
822e1dde8dcSAndrew Lunn 		MII_M1111_HWCFG_FIBER_COPPER_AUTO;
823e1dde8dcSAndrew Lunn 
824e1dde8dcSAndrew Lunn 	return phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
825e1dde8dcSAndrew Lunn }
826e1dde8dcSAndrew Lunn 
827e1dde8dcSAndrew Lunn static int m88e1111_config_init(struct phy_device *phydev)
828e1dde8dcSAndrew Lunn {
829e1dde8dcSAndrew Lunn 	int err;
830e1dde8dcSAndrew Lunn 
831e1dde8dcSAndrew Lunn 	if (phy_interface_is_rgmii(phydev)) {
832e1dde8dcSAndrew Lunn 		err = m88e1111_config_init_rgmii(phydev);
833e1dde8dcSAndrew Lunn 		if (err)
834e1dde8dcSAndrew Lunn 			return err;
835e1dde8dcSAndrew Lunn 	}
836e1dde8dcSAndrew Lunn 
837e1dde8dcSAndrew Lunn 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
838e1dde8dcSAndrew Lunn 		err = m88e1111_config_init_sgmii(phydev);
839e1dde8dcSAndrew Lunn 		if (err < 0)
840e1dde8dcSAndrew Lunn 			return err;
841e1dde8dcSAndrew Lunn 	}
842e1dde8dcSAndrew Lunn 
843e1dde8dcSAndrew Lunn 	if (phydev->interface == PHY_INTERFACE_MODE_RTBI) {
844e1dde8dcSAndrew Lunn 		err = m88e1111_config_init_rtbi(phydev);
8455f8cbc13SLiu Yu-B13201 		if (err < 0)
8465f8cbc13SLiu Yu-B13201 			return err;
8475f8cbc13SLiu Yu-B13201 	}
8485f8cbc13SLiu Yu-B13201 
849cf41a51dSDavid Daney 	err = marvell_of_reg_init(phydev);
850cf41a51dSDavid Daney 	if (err < 0)
851cf41a51dSDavid Daney 		return err;
8525f8cbc13SLiu Yu-B13201 
853cc90cb3bSSrinivas Kandagatla 	return phy_write(phydev, MII_BMCR, BMCR_RESET);
854895ee682SKim Phillips }
855895ee682SKim Phillips 
856fdecf36fSClemens Gruber static int m88e1121_config_init(struct phy_device *phydev)
857fdecf36fSClemens Gruber {
858fdecf36fSClemens Gruber 	int err, oldpage;
859fdecf36fSClemens Gruber 
86052295666SAndrew Lunn 	oldpage = marvell_get_set_page(phydev, MII_MARVELL_LED_PAGE);
86153798328SAndrew Lunn 	if (oldpage < 0)
86253798328SAndrew Lunn 		return oldpage;
863fdecf36fSClemens Gruber 
864fdecf36fSClemens Gruber 	/* Default PHY LED config: LED[0] .. Link, LED[1] .. Activity */
865fdecf36fSClemens Gruber 	err = phy_write(phydev, MII_88E1121_PHY_LED_CTRL,
866fdecf36fSClemens Gruber 			MII_88E1121_PHY_LED_DEF);
867fdecf36fSClemens Gruber 	if (err < 0)
868fdecf36fSClemens Gruber 		return err;
869fdecf36fSClemens Gruber 
8706427bb2dSAndrew Lunn 	marvell_set_page(phydev, oldpage);
871fdecf36fSClemens Gruber 
872fdecf36fSClemens Gruber 	/* Set marvell,reg-init configuration from device tree */
873fdecf36fSClemens Gruber 	return marvell_config_init(phydev);
874fdecf36fSClemens Gruber }
875fdecf36fSClemens Gruber 
876407353ecSClemens Gruber static int m88e1510_config_init(struct phy_device *phydev)
877407353ecSClemens Gruber {
878407353ecSClemens Gruber 	int err;
879407353ecSClemens Gruber 	int temp;
880407353ecSClemens Gruber 
881407353ecSClemens Gruber 	/* SGMII-to-Copper mode initialization */
882407353ecSClemens Gruber 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
883407353ecSClemens Gruber 		/* Select page 18 */
8846427bb2dSAndrew Lunn 		err = marvell_set_page(phydev, 18);
885407353ecSClemens Gruber 		if (err < 0)
886407353ecSClemens Gruber 			return err;
887407353ecSClemens Gruber 
888407353ecSClemens Gruber 		/* In reg 20, write MODE[2:0] = 0x1 (SGMII to Copper) */
889407353ecSClemens Gruber 		temp = phy_read(phydev, MII_88E1510_GEN_CTRL_REG_1);
890407353ecSClemens Gruber 		temp &= ~MII_88E1510_GEN_CTRL_REG_1_MODE_MASK;
891407353ecSClemens Gruber 		temp |= MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII;
892407353ecSClemens Gruber 		err = phy_write(phydev, MII_88E1510_GEN_CTRL_REG_1, temp);
893407353ecSClemens Gruber 		if (err < 0)
894407353ecSClemens Gruber 			return err;
895407353ecSClemens Gruber 
896407353ecSClemens Gruber 		/* PHY reset is necessary after changing MODE[2:0] */
897407353ecSClemens Gruber 		temp |= MII_88E1510_GEN_CTRL_REG_1_RESET;
898407353ecSClemens Gruber 		err = phy_write(phydev, MII_88E1510_GEN_CTRL_REG_1, temp);
899407353ecSClemens Gruber 		if (err < 0)
900407353ecSClemens Gruber 			return err;
901407353ecSClemens Gruber 
902407353ecSClemens Gruber 		/* Reset page selection */
90352295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
904407353ecSClemens Gruber 		if (err < 0)
905407353ecSClemens Gruber 			return err;
906407353ecSClemens Gruber 	}
907407353ecSClemens Gruber 
908fdecf36fSClemens Gruber 	return m88e1121_config_init(phydev);
909407353ecSClemens Gruber }
910407353ecSClemens Gruber 
911605f196eSRon Madrid static int m88e1118_config_aneg(struct phy_device *phydev)
912605f196eSRon Madrid {
913605f196eSRon Madrid 	int err;
914605f196eSRon Madrid 
915605f196eSRon Madrid 	err = phy_write(phydev, MII_BMCR, BMCR_RESET);
916605f196eSRon Madrid 	if (err < 0)
917605f196eSRon Madrid 		return err;
918605f196eSRon Madrid 
919605f196eSRon Madrid 	err = phy_write(phydev, MII_M1011_PHY_SCR,
920605f196eSRon Madrid 			MII_M1011_PHY_SCR_AUTO_CROSS);
921605f196eSRon Madrid 	if (err < 0)
922605f196eSRon Madrid 		return err;
923605f196eSRon Madrid 
924605f196eSRon Madrid 	err = genphy_config_aneg(phydev);
925605f196eSRon Madrid 	return 0;
926605f196eSRon Madrid }
927605f196eSRon Madrid 
928605f196eSRon Madrid static int m88e1118_config_init(struct phy_device *phydev)
929605f196eSRon Madrid {
930605f196eSRon Madrid 	int err;
931605f196eSRon Madrid 
932605f196eSRon Madrid 	/* Change address */
93352295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_MSCR_PAGE);
934605f196eSRon Madrid 	if (err < 0)
935605f196eSRon Madrid 		return err;
936605f196eSRon Madrid 
937605f196eSRon Madrid 	/* Enable 1000 Mbit */
938605f196eSRon Madrid 	err = phy_write(phydev, 0x15, 0x1070);
939605f196eSRon Madrid 	if (err < 0)
940605f196eSRon Madrid 		return err;
941605f196eSRon Madrid 
942605f196eSRon Madrid 	/* Change address */
94352295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_LED_PAGE);
944605f196eSRon Madrid 	if (err < 0)
945605f196eSRon Madrid 		return err;
946605f196eSRon Madrid 
947605f196eSRon Madrid 	/* Adjust LED Control */
9482f495c39SBenjamin Herrenschmidt 	if (phydev->dev_flags & MARVELL_PHY_M1118_DNS323_LEDS)
9492f495c39SBenjamin Herrenschmidt 		err = phy_write(phydev, 0x10, 0x1100);
9502f495c39SBenjamin Herrenschmidt 	else
951605f196eSRon Madrid 		err = phy_write(phydev, 0x10, 0x021e);
952605f196eSRon Madrid 	if (err < 0)
953605f196eSRon Madrid 		return err;
954605f196eSRon Madrid 
955cf41a51dSDavid Daney 	err = marvell_of_reg_init(phydev);
956cf41a51dSDavid Daney 	if (err < 0)
957cf41a51dSDavid Daney 		return err;
958cf41a51dSDavid Daney 
959605f196eSRon Madrid 	/* Reset address */
96052295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
961605f196eSRon Madrid 	if (err < 0)
962605f196eSRon Madrid 		return err;
963605f196eSRon Madrid 
964cc90cb3bSSrinivas Kandagatla 	return phy_write(phydev, MII_BMCR, BMCR_RESET);
965605f196eSRon Madrid }
966605f196eSRon Madrid 
96790600732SDavid Daney static int m88e1149_config_init(struct phy_device *phydev)
96890600732SDavid Daney {
96990600732SDavid Daney 	int err;
97090600732SDavid Daney 
97190600732SDavid Daney 	/* Change address */
97252295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_MSCR_PAGE);
97390600732SDavid Daney 	if (err < 0)
97490600732SDavid Daney 		return err;
97590600732SDavid Daney 
97690600732SDavid Daney 	/* Enable 1000 Mbit */
97790600732SDavid Daney 	err = phy_write(phydev, 0x15, 0x1048);
97890600732SDavid Daney 	if (err < 0)
97990600732SDavid Daney 		return err;
98090600732SDavid Daney 
981cf41a51dSDavid Daney 	err = marvell_of_reg_init(phydev);
982cf41a51dSDavid Daney 	if (err < 0)
983cf41a51dSDavid Daney 		return err;
984cf41a51dSDavid Daney 
98590600732SDavid Daney 	/* Reset address */
98652295666SAndrew Lunn 	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
98790600732SDavid Daney 	if (err < 0)
98890600732SDavid Daney 		return err;
98990600732SDavid Daney 
990cc90cb3bSSrinivas Kandagatla 	return phy_write(phydev, MII_BMCR, BMCR_RESET);
99190600732SDavid Daney }
99290600732SDavid Daney 
993e1dde8dcSAndrew Lunn static int m88e1145_config_init_rgmii(struct phy_device *phydev)
99476884679SAndy Fleming {
99576884679SAndy Fleming 	int err;
99676884679SAndy Fleming 	int temp = phy_read(phydev, MII_M1145_PHY_EXT_CR);
997e69d9ed4SAndrew Lunn 
99876884679SAndy Fleming 	if (temp < 0)
99976884679SAndy Fleming 		return temp;
100076884679SAndy Fleming 
100176884679SAndy Fleming 	temp |= (MII_M1145_RGMII_RX_DELAY | MII_M1145_RGMII_TX_DELAY);
100276884679SAndy Fleming 
100376884679SAndy Fleming 	err = phy_write(phydev, MII_M1145_PHY_EXT_CR, temp);
100476884679SAndy Fleming 	if (err < 0)
100576884679SAndy Fleming 		return err;
100676884679SAndy Fleming 
10072f495c39SBenjamin Herrenschmidt 	if (phydev->dev_flags & MARVELL_PHY_M1145_FLAGS_RESISTANCE) {
100876884679SAndy Fleming 		err = phy_write(phydev, 0x1d, 0x0012);
100976884679SAndy Fleming 		if (err < 0)
101076884679SAndy Fleming 			return err;
101176884679SAndy Fleming 
101276884679SAndy Fleming 		temp = phy_read(phydev, 0x1e);
101376884679SAndy Fleming 		if (temp < 0)
101476884679SAndy Fleming 			return temp;
101576884679SAndy Fleming 
101676884679SAndy Fleming 		temp &= 0xf03f;
101776884679SAndy Fleming 		temp |= 2 << 9;	/* 36 ohm */
101876884679SAndy Fleming 		temp |= 2 << 6;	/* 39 ohm */
101976884679SAndy Fleming 
102076884679SAndy Fleming 		err = phy_write(phydev, 0x1e, temp);
102176884679SAndy Fleming 		if (err < 0)
102276884679SAndy Fleming 			return err;
102376884679SAndy Fleming 
102476884679SAndy Fleming 		err = phy_write(phydev, 0x1d, 0x3);
102576884679SAndy Fleming 		if (err < 0)
102676884679SAndy Fleming 			return err;
102776884679SAndy Fleming 
102876884679SAndy Fleming 		err = phy_write(phydev, 0x1e, 0x8000);
1029e1dde8dcSAndrew Lunn 	}
103076884679SAndy Fleming 	return err;
103176884679SAndy Fleming }
103276884679SAndy Fleming 
1033e1dde8dcSAndrew Lunn static int m88e1145_config_init_sgmii(struct phy_device *phydev)
1034e1dde8dcSAndrew Lunn {
1035e1dde8dcSAndrew Lunn 	int temp = phy_read(phydev, MII_M1145_PHY_EXT_SR);
1036e1dde8dcSAndrew Lunn 
1037b0224175SViet Nga Dao 	if (temp < 0)
1038b0224175SViet Nga Dao 		return temp;
1039b0224175SViet Nga Dao 
104099d881f9SVince Bridgers 	temp &= ~MII_M1145_HWCFG_MODE_MASK;
1041b0224175SViet Nga Dao 	temp |= MII_M1145_HWCFG_MODE_SGMII_NO_CLK;
1042b0224175SViet Nga Dao 	temp |= MII_M1145_HWCFG_FIBER_COPPER_AUTO;
1043b0224175SViet Nga Dao 
1044e1dde8dcSAndrew Lunn 	return phy_write(phydev, MII_M1145_PHY_EXT_SR, temp);
1045e1dde8dcSAndrew Lunn }
1046e1dde8dcSAndrew Lunn 
1047e1dde8dcSAndrew Lunn static int m88e1145_config_init(struct phy_device *phydev)
1048e1dde8dcSAndrew Lunn {
1049e1dde8dcSAndrew Lunn 	int err;
1050e1dde8dcSAndrew Lunn 
1051e1dde8dcSAndrew Lunn 	/* Take care of errata E0 & E1 */
1052e1dde8dcSAndrew Lunn 	err = phy_write(phydev, 0x1d, 0x001b);
1053e1dde8dcSAndrew Lunn 	if (err < 0)
1054e1dde8dcSAndrew Lunn 		return err;
1055e1dde8dcSAndrew Lunn 
1056e1dde8dcSAndrew Lunn 	err = phy_write(phydev, 0x1e, 0x418f);
1057e1dde8dcSAndrew Lunn 	if (err < 0)
1058e1dde8dcSAndrew Lunn 		return err;
1059e1dde8dcSAndrew Lunn 
1060e1dde8dcSAndrew Lunn 	err = phy_write(phydev, 0x1d, 0x0016);
1061e1dde8dcSAndrew Lunn 	if (err < 0)
1062e1dde8dcSAndrew Lunn 		return err;
1063e1dde8dcSAndrew Lunn 
1064e1dde8dcSAndrew Lunn 	err = phy_write(phydev, 0x1e, 0xa2da);
1065e1dde8dcSAndrew Lunn 	if (err < 0)
1066e1dde8dcSAndrew Lunn 		return err;
1067e1dde8dcSAndrew Lunn 
1068e1dde8dcSAndrew Lunn 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
1069e1dde8dcSAndrew Lunn 		err = m88e1145_config_init_rgmii(phydev);
1070e1dde8dcSAndrew Lunn 		if (err < 0)
1071e1dde8dcSAndrew Lunn 			return err;
1072e1dde8dcSAndrew Lunn 	}
1073e1dde8dcSAndrew Lunn 
1074e1dde8dcSAndrew Lunn 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
1075e1dde8dcSAndrew Lunn 		err = m88e1145_config_init_sgmii(phydev);
1076b0224175SViet Nga Dao 		if (err < 0)
1077b0224175SViet Nga Dao 			return err;
1078b0224175SViet Nga Dao 	}
1079b0224175SViet Nga Dao 
1080cf41a51dSDavid Daney 	err = marvell_of_reg_init(phydev);
1081cf41a51dSDavid Daney 	if (err < 0)
1082cf41a51dSDavid Daney 		return err;
1083cf41a51dSDavid Daney 
108476884679SAndy Fleming 	return 0;
108576884679SAndy Fleming }
108600db8189SAndy Fleming 
10876cfb3bccSCharles-Antoine Couret /**
10886cfb3bccSCharles-Antoine Couret  * fiber_lpa_to_ethtool_lpa_t
10896cfb3bccSCharles-Antoine Couret  * @lpa: value of the MII_LPA register for fiber link
1090be937f1fSAlexandr Smirnov  *
10916cfb3bccSCharles-Antoine Couret  * A small helper function that translates MII_LPA
10926cfb3bccSCharles-Antoine Couret  * bits to ethtool LP advertisement settings.
10936cfb3bccSCharles-Antoine Couret  */
10946cfb3bccSCharles-Antoine Couret static u32 fiber_lpa_to_ethtool_lpa_t(u32 lpa)
10956cfb3bccSCharles-Antoine Couret {
10966cfb3bccSCharles-Antoine Couret 	u32 result = 0;
10976cfb3bccSCharles-Antoine Couret 
10986cfb3bccSCharles-Antoine Couret 	if (lpa & LPA_FIBER_1000HALF)
10996cfb3bccSCharles-Antoine Couret 		result |= ADVERTISED_1000baseT_Half;
11006cfb3bccSCharles-Antoine Couret 	if (lpa & LPA_FIBER_1000FULL)
11016cfb3bccSCharles-Antoine Couret 		result |= ADVERTISED_1000baseT_Full;
11026cfb3bccSCharles-Antoine Couret 
11036cfb3bccSCharles-Antoine Couret 	return result;
11046cfb3bccSCharles-Antoine Couret }
11056cfb3bccSCharles-Antoine Couret 
11066cfb3bccSCharles-Antoine Couret /**
11076cfb3bccSCharles-Antoine Couret  * marvell_update_link - update link status in real time in @phydev
11086cfb3bccSCharles-Antoine Couret  * @phydev: target phy_device struct
11096cfb3bccSCharles-Antoine Couret  *
11106cfb3bccSCharles-Antoine Couret  * Description: Update the value in phydev->link to reflect the
11116cfb3bccSCharles-Antoine Couret  *   current link value.
11126cfb3bccSCharles-Antoine Couret  */
11136cfb3bccSCharles-Antoine Couret static int marvell_update_link(struct phy_device *phydev, int fiber)
11146cfb3bccSCharles-Antoine Couret {
11156cfb3bccSCharles-Antoine Couret 	int status;
11166cfb3bccSCharles-Antoine Couret 
11176cfb3bccSCharles-Antoine Couret 	/* Use the generic register for copper link, or specific
11180c3439bcSAndrew Lunn 	 * register for fiber case
11190c3439bcSAndrew Lunn 	 */
11206cfb3bccSCharles-Antoine Couret 	if (fiber) {
11216cfb3bccSCharles-Antoine Couret 		status = phy_read(phydev, MII_M1011_PHY_STATUS);
11226cfb3bccSCharles-Antoine Couret 		if (status < 0)
11236cfb3bccSCharles-Antoine Couret 			return status;
11246cfb3bccSCharles-Antoine Couret 
11256cfb3bccSCharles-Antoine Couret 		if ((status & REGISTER_LINK_STATUS) == 0)
11266cfb3bccSCharles-Antoine Couret 			phydev->link = 0;
11276cfb3bccSCharles-Antoine Couret 		else
11286cfb3bccSCharles-Antoine Couret 			phydev->link = 1;
11296cfb3bccSCharles-Antoine Couret 	} else {
11306cfb3bccSCharles-Antoine Couret 		return genphy_update_link(phydev);
11316cfb3bccSCharles-Antoine Couret 	}
11326cfb3bccSCharles-Antoine Couret 
11336cfb3bccSCharles-Antoine Couret 	return 0;
11346cfb3bccSCharles-Antoine Couret }
11356cfb3bccSCharles-Antoine Couret 
1136e1dde8dcSAndrew Lunn static int marvell_read_status_page_an(struct phy_device *phydev,
1137e1dde8dcSAndrew Lunn 				       int fiber)
1138be937f1fSAlexandr Smirnov {
1139e1dde8dcSAndrew Lunn 	int status;
1140be937f1fSAlexandr Smirnov 	int lpa;
1141357cd64cSRussell King 	int lpagb;
1142e1dde8dcSAndrew Lunn 	int adv;
1143be937f1fSAlexandr Smirnov 
1144be937f1fSAlexandr Smirnov 	status = phy_read(phydev, MII_M1011_PHY_STATUS);
1145be937f1fSAlexandr Smirnov 	if (status < 0)
1146be937f1fSAlexandr Smirnov 		return status;
1147be937f1fSAlexandr Smirnov 
1148be937f1fSAlexandr Smirnov 	lpa = phy_read(phydev, MII_LPA);
1149be937f1fSAlexandr Smirnov 	if (lpa < 0)
1150be937f1fSAlexandr Smirnov 		return lpa;
1151be937f1fSAlexandr Smirnov 
1152357cd64cSRussell King 	lpagb = phy_read(phydev, MII_STAT1000);
1153357cd64cSRussell King 	if (lpagb < 0)
1154357cd64cSRussell King 		return lpagb;
1155357cd64cSRussell King 
1156be937f1fSAlexandr Smirnov 	adv = phy_read(phydev, MII_ADVERTISE);
1157be937f1fSAlexandr Smirnov 	if (adv < 0)
1158be937f1fSAlexandr Smirnov 		return adv;
1159be937f1fSAlexandr Smirnov 
1160be937f1fSAlexandr Smirnov 	lpa &= adv;
1161be937f1fSAlexandr Smirnov 
1162be937f1fSAlexandr Smirnov 	if (status & MII_M1011_PHY_STATUS_FULLDUPLEX)
1163be937f1fSAlexandr Smirnov 		phydev->duplex = DUPLEX_FULL;
1164be937f1fSAlexandr Smirnov 	else
1165be937f1fSAlexandr Smirnov 		phydev->duplex = DUPLEX_HALF;
1166be937f1fSAlexandr Smirnov 
1167be937f1fSAlexandr Smirnov 	status = status & MII_M1011_PHY_STATUS_SPD_MASK;
11684f48ed32SAndrew Lunn 	phydev->pause = 0;
11694f48ed32SAndrew Lunn 	phydev->asym_pause = 0;
1170be937f1fSAlexandr Smirnov 
1171be937f1fSAlexandr Smirnov 	switch (status) {
1172be937f1fSAlexandr Smirnov 	case MII_M1011_PHY_STATUS_1000:
1173be937f1fSAlexandr Smirnov 		phydev->speed = SPEED_1000;
1174be937f1fSAlexandr Smirnov 		break;
1175be937f1fSAlexandr Smirnov 
1176be937f1fSAlexandr Smirnov 	case MII_M1011_PHY_STATUS_100:
1177be937f1fSAlexandr Smirnov 		phydev->speed = SPEED_100;
1178be937f1fSAlexandr Smirnov 		break;
1179be937f1fSAlexandr Smirnov 
1180be937f1fSAlexandr Smirnov 	default:
1181be937f1fSAlexandr Smirnov 		phydev->speed = SPEED_10;
1182be937f1fSAlexandr Smirnov 		break;
1183be937f1fSAlexandr Smirnov 	}
1184be937f1fSAlexandr Smirnov 
11856cfb3bccSCharles-Antoine Couret 	if (!fiber) {
1186e1dde8dcSAndrew Lunn 		phydev->lp_advertising =
1187e1dde8dcSAndrew Lunn 			mii_stat1000_to_ethtool_lpa_t(lpagb) |
11886cfb3bccSCharles-Antoine Couret 			mii_lpa_to_ethtool_lpa_t(lpa);
11896cfb3bccSCharles-Antoine Couret 
1190be937f1fSAlexandr Smirnov 		if (phydev->duplex == DUPLEX_FULL) {
1191be937f1fSAlexandr Smirnov 			phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0;
1192be937f1fSAlexandr Smirnov 			phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0;
1193be937f1fSAlexandr Smirnov 		}
1194be937f1fSAlexandr Smirnov 	} else {
11956cfb3bccSCharles-Antoine Couret 		/* The fiber link is only 1000M capable */
11966cfb3bccSCharles-Antoine Couret 		phydev->lp_advertising = fiber_lpa_to_ethtool_lpa_t(lpa);
11976cfb3bccSCharles-Antoine Couret 
11986cfb3bccSCharles-Antoine Couret 		if (phydev->duplex == DUPLEX_FULL) {
11996cfb3bccSCharles-Antoine Couret 			if (!(lpa & LPA_PAUSE_FIBER)) {
12006cfb3bccSCharles-Antoine Couret 				phydev->pause = 0;
12016cfb3bccSCharles-Antoine Couret 				phydev->asym_pause = 0;
12026cfb3bccSCharles-Antoine Couret 			} else if ((lpa & LPA_PAUSE_ASYM_FIBER)) {
12036cfb3bccSCharles-Antoine Couret 				phydev->pause = 1;
12046cfb3bccSCharles-Antoine Couret 				phydev->asym_pause = 1;
12056cfb3bccSCharles-Antoine Couret 			} else {
12066cfb3bccSCharles-Antoine Couret 				phydev->pause = 1;
12076cfb3bccSCharles-Antoine Couret 				phydev->asym_pause = 0;
12086cfb3bccSCharles-Antoine Couret 			}
12096cfb3bccSCharles-Antoine Couret 		}
12106cfb3bccSCharles-Antoine Couret 	}
1211e1dde8dcSAndrew Lunn 	return 0;
1212e1dde8dcSAndrew Lunn }
1213e1dde8dcSAndrew Lunn 
1214e1dde8dcSAndrew Lunn static int marvell_read_status_page_fixed(struct phy_device *phydev)
1215e1dde8dcSAndrew Lunn {
1216be937f1fSAlexandr Smirnov 	int bmcr = phy_read(phydev, MII_BMCR);
1217be937f1fSAlexandr Smirnov 
1218be937f1fSAlexandr Smirnov 	if (bmcr < 0)
1219be937f1fSAlexandr Smirnov 		return bmcr;
1220be937f1fSAlexandr Smirnov 
1221be937f1fSAlexandr Smirnov 	if (bmcr & BMCR_FULLDPLX)
1222be937f1fSAlexandr Smirnov 		phydev->duplex = DUPLEX_FULL;
1223be937f1fSAlexandr Smirnov 	else
1224be937f1fSAlexandr Smirnov 		phydev->duplex = DUPLEX_HALF;
1225be937f1fSAlexandr Smirnov 
1226be937f1fSAlexandr Smirnov 	if (bmcr & BMCR_SPEED1000)
1227be937f1fSAlexandr Smirnov 		phydev->speed = SPEED_1000;
1228be937f1fSAlexandr Smirnov 	else if (bmcr & BMCR_SPEED100)
1229be937f1fSAlexandr Smirnov 		phydev->speed = SPEED_100;
1230be937f1fSAlexandr Smirnov 	else
1231be937f1fSAlexandr Smirnov 		phydev->speed = SPEED_10;
1232be937f1fSAlexandr Smirnov 
12334f48ed32SAndrew Lunn 	phydev->pause = 0;
12344f48ed32SAndrew Lunn 	phydev->asym_pause = 0;
1235357cd64cSRussell King 	phydev->lp_advertising = 0;
1236be937f1fSAlexandr Smirnov 
1237be937f1fSAlexandr Smirnov 	return 0;
1238be937f1fSAlexandr Smirnov }
1239be937f1fSAlexandr Smirnov 
1240e1dde8dcSAndrew Lunn /* marvell_read_status_page
1241e1dde8dcSAndrew Lunn  *
1242e1dde8dcSAndrew Lunn  * Description:
1243e1dde8dcSAndrew Lunn  *   Check the link, then figure out the current state
1244e1dde8dcSAndrew Lunn  *   by comparing what we advertise with what the link partner
1245e1dde8dcSAndrew Lunn  *   advertises.  Start by checking the gigabit possibilities,
1246e1dde8dcSAndrew Lunn  *   then move on to 10/100.
1247e1dde8dcSAndrew Lunn  */
1248e1dde8dcSAndrew Lunn static int marvell_read_status_page(struct phy_device *phydev, int page)
1249e1dde8dcSAndrew Lunn {
1250e1dde8dcSAndrew Lunn 	int fiber;
1251e1dde8dcSAndrew Lunn 	int err;
1252e1dde8dcSAndrew Lunn 
1253e1dde8dcSAndrew Lunn 	/* Detect and update the link, but return if there
1254e1dde8dcSAndrew Lunn 	 * was an error
1255e1dde8dcSAndrew Lunn 	 */
125652295666SAndrew Lunn 	if (page == MII_MARVELL_FIBER_PAGE)
1257e1dde8dcSAndrew Lunn 		fiber = 1;
1258e1dde8dcSAndrew Lunn 	else
1259e1dde8dcSAndrew Lunn 		fiber = 0;
1260e1dde8dcSAndrew Lunn 
1261e1dde8dcSAndrew Lunn 	err = marvell_update_link(phydev, fiber);
1262e1dde8dcSAndrew Lunn 	if (err)
1263e1dde8dcSAndrew Lunn 		return err;
1264e1dde8dcSAndrew Lunn 
1265e1dde8dcSAndrew Lunn 	if (phydev->autoneg == AUTONEG_ENABLE)
1266e1dde8dcSAndrew Lunn 		err = marvell_read_status_page_an(phydev, fiber);
1267e1dde8dcSAndrew Lunn 	else
1268e1dde8dcSAndrew Lunn 		err = marvell_read_status_page_fixed(phydev);
1269e1dde8dcSAndrew Lunn 
1270e1dde8dcSAndrew Lunn 	return err;
1271e1dde8dcSAndrew Lunn }
1272e1dde8dcSAndrew Lunn 
12736cfb3bccSCharles-Antoine Couret /* marvell_read_status
12746cfb3bccSCharles-Antoine Couret  *
12756cfb3bccSCharles-Antoine Couret  * Some Marvell's phys have two modes: fiber and copper.
12766cfb3bccSCharles-Antoine Couret  * Both need status checked.
12776cfb3bccSCharles-Antoine Couret  * Description:
12786cfb3bccSCharles-Antoine Couret  *   First, check the fiber link and status.
12796cfb3bccSCharles-Antoine Couret  *   If the fiber link is down, check the copper link and status which
12806cfb3bccSCharles-Antoine Couret  *   will be the default value if both link are down.
12816cfb3bccSCharles-Antoine Couret  */
12826cfb3bccSCharles-Antoine Couret static int marvell_read_status(struct phy_device *phydev)
12836cfb3bccSCharles-Antoine Couret {
12846cfb3bccSCharles-Antoine Couret 	int err;
12856cfb3bccSCharles-Antoine Couret 
12866cfb3bccSCharles-Antoine Couret 	/* Check the fiber mode first */
1287a13c0652SRussell King 	if (phydev->supported & SUPPORTED_FIBRE &&
1288a13c0652SRussell King 	    phydev->interface != PHY_INTERFACE_MODE_SGMII) {
128952295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
12906cfb3bccSCharles-Antoine Couret 		if (err < 0)
12916cfb3bccSCharles-Antoine Couret 			goto error;
12926cfb3bccSCharles-Antoine Couret 
129352295666SAndrew Lunn 		err = marvell_read_status_page(phydev, MII_MARVELL_FIBER_PAGE);
12946cfb3bccSCharles-Antoine Couret 		if (err < 0)
12956cfb3bccSCharles-Antoine Couret 			goto error;
12966cfb3bccSCharles-Antoine Couret 
12970c3439bcSAndrew Lunn 		/* If the fiber link is up, it is the selected and
12980c3439bcSAndrew Lunn 		 * used link. In this case, we need to stay in the
12990c3439bcSAndrew Lunn 		 * fiber page. Please to be careful about that, avoid
13000c3439bcSAndrew Lunn 		 * to restore Copper page in other functions which
13010c3439bcSAndrew Lunn 		 * could break the behaviour for some fiber phy like
13020c3439bcSAndrew Lunn 		 * 88E1512.
13030c3439bcSAndrew Lunn 		 */
13046cfb3bccSCharles-Antoine Couret 		if (phydev->link)
13056cfb3bccSCharles-Antoine Couret 			return 0;
13066cfb3bccSCharles-Antoine Couret 
13076cfb3bccSCharles-Antoine Couret 		/* If fiber link is down, check and save copper mode state */
130852295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
13096cfb3bccSCharles-Antoine Couret 		if (err < 0)
13106cfb3bccSCharles-Antoine Couret 			goto error;
13116cfb3bccSCharles-Antoine Couret 	}
13126cfb3bccSCharles-Antoine Couret 
131352295666SAndrew Lunn 	return marvell_read_status_page(phydev, MII_MARVELL_COPPER_PAGE);
13146cfb3bccSCharles-Antoine Couret 
13156cfb3bccSCharles-Antoine Couret error:
131652295666SAndrew Lunn 	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
13176cfb3bccSCharles-Antoine Couret 	return err;
13186cfb3bccSCharles-Antoine Couret }
13193758be3dSCharles-Antoine Couret 
13203758be3dSCharles-Antoine Couret /* marvell_suspend
13213758be3dSCharles-Antoine Couret  *
13223758be3dSCharles-Antoine Couret  * Some Marvell's phys have two modes: fiber and copper.
13233758be3dSCharles-Antoine Couret  * Both need to be suspended
13243758be3dSCharles-Antoine Couret  */
13253758be3dSCharles-Antoine Couret static int marvell_suspend(struct phy_device *phydev)
13263758be3dSCharles-Antoine Couret {
13273758be3dSCharles-Antoine Couret 	int err;
13283758be3dSCharles-Antoine Couret 
13293758be3dSCharles-Antoine Couret 	/* Suspend the fiber mode first */
13303758be3dSCharles-Antoine Couret 	if (!(phydev->supported & SUPPORTED_FIBRE)) {
133152295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
13323758be3dSCharles-Antoine Couret 		if (err < 0)
13333758be3dSCharles-Antoine Couret 			goto error;
13343758be3dSCharles-Antoine Couret 
13353758be3dSCharles-Antoine Couret 		/* With the page set, use the generic suspend */
13363758be3dSCharles-Antoine Couret 		err = genphy_suspend(phydev);
13373758be3dSCharles-Antoine Couret 		if (err < 0)
13383758be3dSCharles-Antoine Couret 			goto error;
13393758be3dSCharles-Antoine Couret 
13403758be3dSCharles-Antoine Couret 		/* Then, the copper link */
134152295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
13423758be3dSCharles-Antoine Couret 		if (err < 0)
13433758be3dSCharles-Antoine Couret 			goto error;
13443758be3dSCharles-Antoine Couret 	}
13453758be3dSCharles-Antoine Couret 
13463758be3dSCharles-Antoine Couret 	/* With the page set, use the generic suspend */
13473758be3dSCharles-Antoine Couret 	return genphy_suspend(phydev);
13483758be3dSCharles-Antoine Couret 
13493758be3dSCharles-Antoine Couret error:
135052295666SAndrew Lunn 	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
13513758be3dSCharles-Antoine Couret 	return err;
13523758be3dSCharles-Antoine Couret }
13533758be3dSCharles-Antoine Couret 
13543758be3dSCharles-Antoine Couret /* marvell_resume
13553758be3dSCharles-Antoine Couret  *
13563758be3dSCharles-Antoine Couret  * Some Marvell's phys have two modes: fiber and copper.
13573758be3dSCharles-Antoine Couret  * Both need to be resumed
13583758be3dSCharles-Antoine Couret  */
13593758be3dSCharles-Antoine Couret static int marvell_resume(struct phy_device *phydev)
13603758be3dSCharles-Antoine Couret {
13613758be3dSCharles-Antoine Couret 	int err;
13623758be3dSCharles-Antoine Couret 
13633758be3dSCharles-Antoine Couret 	/* Resume the fiber mode first */
13643758be3dSCharles-Antoine Couret 	if (!(phydev->supported & SUPPORTED_FIBRE)) {
136552295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
13663758be3dSCharles-Antoine Couret 		if (err < 0)
13673758be3dSCharles-Antoine Couret 			goto error;
13683758be3dSCharles-Antoine Couret 
13693758be3dSCharles-Antoine Couret 		/* With the page set, use the generic resume */
13703758be3dSCharles-Antoine Couret 		err = genphy_resume(phydev);
13713758be3dSCharles-Antoine Couret 		if (err < 0)
13723758be3dSCharles-Antoine Couret 			goto error;
13733758be3dSCharles-Antoine Couret 
13743758be3dSCharles-Antoine Couret 		/* Then, the copper link */
137552295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
13763758be3dSCharles-Antoine Couret 		if (err < 0)
13773758be3dSCharles-Antoine Couret 			goto error;
13783758be3dSCharles-Antoine Couret 	}
13793758be3dSCharles-Antoine Couret 
13803758be3dSCharles-Antoine Couret 	/* With the page set, use the generic resume */
13813758be3dSCharles-Antoine Couret 	return genphy_resume(phydev);
13823758be3dSCharles-Antoine Couret 
13833758be3dSCharles-Antoine Couret error:
138452295666SAndrew Lunn 	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
13853758be3dSCharles-Antoine Couret 	return err;
13863758be3dSCharles-Antoine Couret }
13873758be3dSCharles-Antoine Couret 
13886b358aedSSebastian Hesselbarth static int marvell_aneg_done(struct phy_device *phydev)
13896b358aedSSebastian Hesselbarth {
13906b358aedSSebastian Hesselbarth 	int retval = phy_read(phydev, MII_M1011_PHY_STATUS);
1391e69d9ed4SAndrew Lunn 
13926b358aedSSebastian Hesselbarth 	return (retval < 0) ? retval : (retval & MII_M1011_PHY_STATUS_RESOLVED);
13936b358aedSSebastian Hesselbarth }
13946b358aedSSebastian Hesselbarth 
1395dcd07be3SAnatolij Gustschin static int m88e1121_did_interrupt(struct phy_device *phydev)
1396dcd07be3SAnatolij Gustschin {
1397dcd07be3SAnatolij Gustschin 	int imask;
1398dcd07be3SAnatolij Gustschin 
1399dcd07be3SAnatolij Gustschin 	imask = phy_read(phydev, MII_M1011_IEVENT);
1400dcd07be3SAnatolij Gustschin 
1401dcd07be3SAnatolij Gustschin 	if (imask & MII_M1011_IMASK_INIT)
1402dcd07be3SAnatolij Gustschin 		return 1;
1403dcd07be3SAnatolij Gustschin 
1404dcd07be3SAnatolij Gustschin 	return 0;
1405dcd07be3SAnatolij Gustschin }
1406dcd07be3SAnatolij Gustschin 
140723beb38fSAndrew Lunn static void m88e1318_get_wol(struct phy_device *phydev,
140823beb38fSAndrew Lunn 			     struct ethtool_wolinfo *wol)
14093871c387SMichael Stapelberg {
14103871c387SMichael Stapelberg 	wol->supported = WAKE_MAGIC;
14113871c387SMichael Stapelberg 	wol->wolopts = 0;
14123871c387SMichael Stapelberg 
141352295666SAndrew Lunn 	if (marvell_set_page(phydev, MII_MARVELL_WOL_PAGE) < 0)
14143871c387SMichael Stapelberg 		return;
14153871c387SMichael Stapelberg 
14163871c387SMichael Stapelberg 	if (phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL) &
14173871c387SMichael Stapelberg 	    MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE)
14183871c387SMichael Stapelberg 		wol->wolopts |= WAKE_MAGIC;
14193871c387SMichael Stapelberg 
142052295666SAndrew Lunn 	if (marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE) < 0)
14213871c387SMichael Stapelberg 		return;
14223871c387SMichael Stapelberg }
14233871c387SMichael Stapelberg 
142423beb38fSAndrew Lunn static int m88e1318_set_wol(struct phy_device *phydev,
142523beb38fSAndrew Lunn 			    struct ethtool_wolinfo *wol)
14263871c387SMichael Stapelberg {
14273871c387SMichael Stapelberg 	int err, oldpage, temp;
14283871c387SMichael Stapelberg 
14296427bb2dSAndrew Lunn 	oldpage = marvell_get_page(phydev);
14303871c387SMichael Stapelberg 
14313871c387SMichael Stapelberg 	if (wol->wolopts & WAKE_MAGIC) {
14323871c387SMichael Stapelberg 		/* Explicitly switch to page 0x00, just to be sure */
143352295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
14343871c387SMichael Stapelberg 		if (err < 0)
14353871c387SMichael Stapelberg 			return err;
14363871c387SMichael Stapelberg 
14373871c387SMichael Stapelberg 		/* Enable the WOL interrupt */
14383871c387SMichael Stapelberg 		temp = phy_read(phydev, MII_88E1318S_PHY_CSIER);
14393871c387SMichael Stapelberg 		temp |= MII_88E1318S_PHY_CSIER_WOL_EIE;
14403871c387SMichael Stapelberg 		err = phy_write(phydev, MII_88E1318S_PHY_CSIER, temp);
14413871c387SMichael Stapelberg 		if (err < 0)
14423871c387SMichael Stapelberg 			return err;
14433871c387SMichael Stapelberg 
144452295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_LED_PAGE);
14453871c387SMichael Stapelberg 		if (err < 0)
14463871c387SMichael Stapelberg 			return err;
14473871c387SMichael Stapelberg 
14483871c387SMichael Stapelberg 		/* Setup LED[2] as interrupt pin (active low) */
14493871c387SMichael Stapelberg 		temp = phy_read(phydev, MII_88E1318S_PHY_LED_TCR);
14503871c387SMichael Stapelberg 		temp &= ~MII_88E1318S_PHY_LED_TCR_FORCE_INT;
14513871c387SMichael Stapelberg 		temp |= MII_88E1318S_PHY_LED_TCR_INTn_ENABLE;
14523871c387SMichael Stapelberg 		temp |= MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW;
14533871c387SMichael Stapelberg 		err = phy_write(phydev, MII_88E1318S_PHY_LED_TCR, temp);
14543871c387SMichael Stapelberg 		if (err < 0)
14553871c387SMichael Stapelberg 			return err;
14563871c387SMichael Stapelberg 
145752295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_WOL_PAGE);
14583871c387SMichael Stapelberg 		if (err < 0)
14593871c387SMichael Stapelberg 			return err;
14603871c387SMichael Stapelberg 
14613871c387SMichael Stapelberg 		/* Store the device address for the magic packet */
14623871c387SMichael Stapelberg 		err = phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD2,
14633871c387SMichael Stapelberg 				((phydev->attached_dev->dev_addr[5] << 8) |
14643871c387SMichael Stapelberg 				 phydev->attached_dev->dev_addr[4]));
14653871c387SMichael Stapelberg 		if (err < 0)
14663871c387SMichael Stapelberg 			return err;
14673871c387SMichael Stapelberg 		err = phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD1,
14683871c387SMichael Stapelberg 				((phydev->attached_dev->dev_addr[3] << 8) |
14693871c387SMichael Stapelberg 				 phydev->attached_dev->dev_addr[2]));
14703871c387SMichael Stapelberg 		if (err < 0)
14713871c387SMichael Stapelberg 			return err;
14723871c387SMichael Stapelberg 		err = phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD0,
14733871c387SMichael Stapelberg 				((phydev->attached_dev->dev_addr[1] << 8) |
14743871c387SMichael Stapelberg 				 phydev->attached_dev->dev_addr[0]));
14753871c387SMichael Stapelberg 		if (err < 0)
14763871c387SMichael Stapelberg 			return err;
14773871c387SMichael Stapelberg 
14783871c387SMichael Stapelberg 		/* Clear WOL status and enable magic packet matching */
14793871c387SMichael Stapelberg 		temp = phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL);
14803871c387SMichael Stapelberg 		temp |= MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS;
14813871c387SMichael Stapelberg 		temp |= MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE;
14823871c387SMichael Stapelberg 		err = phy_write(phydev, MII_88E1318S_PHY_WOL_CTRL, temp);
14833871c387SMichael Stapelberg 		if (err < 0)
14843871c387SMichael Stapelberg 			return err;
14853871c387SMichael Stapelberg 	} else {
148652295666SAndrew Lunn 		err = marvell_set_page(phydev, MII_MARVELL_WOL_PAGE);
14873871c387SMichael Stapelberg 		if (err < 0)
14883871c387SMichael Stapelberg 			return err;
14893871c387SMichael Stapelberg 
14903871c387SMichael Stapelberg 		/* Clear WOL status and disable magic packet matching */
14913871c387SMichael Stapelberg 		temp = phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL);
14923871c387SMichael Stapelberg 		temp |= MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS;
14933871c387SMichael Stapelberg 		temp &= ~MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE;
14943871c387SMichael Stapelberg 		err = phy_write(phydev, MII_88E1318S_PHY_WOL_CTRL, temp);
14953871c387SMichael Stapelberg 		if (err < 0)
14963871c387SMichael Stapelberg 			return err;
14973871c387SMichael Stapelberg 	}
14983871c387SMichael Stapelberg 
14996427bb2dSAndrew Lunn 	err = marvell_set_page(phydev, oldpage);
15003871c387SMichael Stapelberg 	if (err < 0)
15013871c387SMichael Stapelberg 		return err;
15023871c387SMichael Stapelberg 
15033871c387SMichael Stapelberg 	return 0;
15043871c387SMichael Stapelberg }
15053871c387SMichael Stapelberg 
1506d2fa47d9SAndrew Lunn static int marvell_get_sset_count(struct phy_device *phydev)
1507d2fa47d9SAndrew Lunn {
15082170fef7SCharles-Antoine Couret 	if (phydev->supported & SUPPORTED_FIBRE)
1509d2fa47d9SAndrew Lunn 		return ARRAY_SIZE(marvell_hw_stats);
15102170fef7SCharles-Antoine Couret 	else
15112170fef7SCharles-Antoine Couret 		return ARRAY_SIZE(marvell_hw_stats) - NB_FIBER_STATS;
1512d2fa47d9SAndrew Lunn }
1513d2fa47d9SAndrew Lunn 
1514d2fa47d9SAndrew Lunn static void marvell_get_strings(struct phy_device *phydev, u8 *data)
1515d2fa47d9SAndrew Lunn {
1516d2fa47d9SAndrew Lunn 	int i;
1517d2fa47d9SAndrew Lunn 
1518d2fa47d9SAndrew Lunn 	for (i = 0; i < ARRAY_SIZE(marvell_hw_stats); i++) {
1519d2fa47d9SAndrew Lunn 		memcpy(data + i * ETH_GSTRING_LEN,
1520d2fa47d9SAndrew Lunn 		       marvell_hw_stats[i].string, ETH_GSTRING_LEN);
1521d2fa47d9SAndrew Lunn 	}
1522d2fa47d9SAndrew Lunn }
1523d2fa47d9SAndrew Lunn 
1524d2fa47d9SAndrew Lunn #ifndef UINT64_MAX
1525d2fa47d9SAndrew Lunn #define UINT64_MAX              (u64)(~((u64)0))
1526d2fa47d9SAndrew Lunn #endif
1527d2fa47d9SAndrew Lunn static u64 marvell_get_stat(struct phy_device *phydev, int i)
1528d2fa47d9SAndrew Lunn {
1529d2fa47d9SAndrew Lunn 	struct marvell_hw_stat stat = marvell_hw_stats[i];
1530d2fa47d9SAndrew Lunn 	struct marvell_priv *priv = phydev->priv;
153153798328SAndrew Lunn 	int oldpage, val;
1532321b4d4bSAndrew Lunn 	u64 ret;
1533d2fa47d9SAndrew Lunn 
153453798328SAndrew Lunn 	oldpage = marvell_get_set_page(phydev, stat.page);
153553798328SAndrew Lunn 	if (oldpage < 0)
1536d2fa47d9SAndrew Lunn 		return UINT64_MAX;
1537d2fa47d9SAndrew Lunn 
1538d2fa47d9SAndrew Lunn 	val = phy_read(phydev, stat.reg);
1539d2fa47d9SAndrew Lunn 	if (val < 0) {
1540321b4d4bSAndrew Lunn 		ret = UINT64_MAX;
1541d2fa47d9SAndrew Lunn 	} else {
1542d2fa47d9SAndrew Lunn 		val = val & ((1 << stat.bits) - 1);
1543d2fa47d9SAndrew Lunn 		priv->stats[i] += val;
1544321b4d4bSAndrew Lunn 		ret = priv->stats[i];
1545d2fa47d9SAndrew Lunn 	}
1546d2fa47d9SAndrew Lunn 
15476427bb2dSAndrew Lunn 	marvell_set_page(phydev, oldpage);
1548d2fa47d9SAndrew Lunn 
1549321b4d4bSAndrew Lunn 	return ret;
1550d2fa47d9SAndrew Lunn }
1551d2fa47d9SAndrew Lunn 
1552d2fa47d9SAndrew Lunn static void marvell_get_stats(struct phy_device *phydev,
1553d2fa47d9SAndrew Lunn 			      struct ethtool_stats *stats, u64 *data)
1554d2fa47d9SAndrew Lunn {
1555d2fa47d9SAndrew Lunn 	int i;
1556d2fa47d9SAndrew Lunn 
1557d2fa47d9SAndrew Lunn 	for (i = 0; i < ARRAY_SIZE(marvell_hw_stats); i++)
1558d2fa47d9SAndrew Lunn 		data[i] = marvell_get_stat(phydev, i);
1559d2fa47d9SAndrew Lunn }
1560d2fa47d9SAndrew Lunn 
15610b04680fSAndrew Lunn #ifdef CONFIG_HWMON
15620b04680fSAndrew Lunn static int m88e1121_get_temp(struct phy_device *phydev, long *temp)
15630b04680fSAndrew Lunn {
1564975b388cSAndrew Lunn 	int oldpage;
15650b04680fSAndrew Lunn 	int ret;
15660b04680fSAndrew Lunn 	int val;
15670b04680fSAndrew Lunn 
15680b04680fSAndrew Lunn 	*temp = 0;
15690b04680fSAndrew Lunn 
15700b04680fSAndrew Lunn 	mutex_lock(&phydev->lock);
15710b04680fSAndrew Lunn 
157252295666SAndrew Lunn 	oldpage = marvell_get_set_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
1573975b388cSAndrew Lunn 	if (oldpage < 0) {
1574975b388cSAndrew Lunn 		mutex_unlock(&phydev->lock);
1575975b388cSAndrew Lunn 		return oldpage;
1576975b388cSAndrew Lunn 	}
1577975b388cSAndrew Lunn 
15780b04680fSAndrew Lunn 	/* Enable temperature sensor */
15790b04680fSAndrew Lunn 	ret = phy_read(phydev, MII_88E1121_MISC_TEST);
15800b04680fSAndrew Lunn 	if (ret < 0)
15810b04680fSAndrew Lunn 		goto error;
15820b04680fSAndrew Lunn 
15830b04680fSAndrew Lunn 	ret = phy_write(phydev, MII_88E1121_MISC_TEST,
15840b04680fSAndrew Lunn 			ret | MII_88E1121_MISC_TEST_TEMP_SENSOR_EN);
15850b04680fSAndrew Lunn 	if (ret < 0)
15860b04680fSAndrew Lunn 		goto error;
15870b04680fSAndrew Lunn 
15880b04680fSAndrew Lunn 	/* Wait for temperature to stabilize */
15890b04680fSAndrew Lunn 	usleep_range(10000, 12000);
15900b04680fSAndrew Lunn 
15910b04680fSAndrew Lunn 	val = phy_read(phydev, MII_88E1121_MISC_TEST);
15920b04680fSAndrew Lunn 	if (val < 0) {
15930b04680fSAndrew Lunn 		ret = val;
15940b04680fSAndrew Lunn 		goto error;
15950b04680fSAndrew Lunn 	}
15960b04680fSAndrew Lunn 
15970b04680fSAndrew Lunn 	/* Disable temperature sensor */
15980b04680fSAndrew Lunn 	ret = phy_write(phydev, MII_88E1121_MISC_TEST,
15990b04680fSAndrew Lunn 			ret & ~MII_88E1121_MISC_TEST_TEMP_SENSOR_EN);
16000b04680fSAndrew Lunn 	if (ret < 0)
16010b04680fSAndrew Lunn 		goto error;
16020b04680fSAndrew Lunn 
16030b04680fSAndrew Lunn 	*temp = ((val & MII_88E1121_MISC_TEST_TEMP_MASK) - 5) * 5000;
16040b04680fSAndrew Lunn 
16050b04680fSAndrew Lunn error:
1606975b388cSAndrew Lunn 	marvell_set_page(phydev, oldpage);
16070b04680fSAndrew Lunn 	mutex_unlock(&phydev->lock);
16080b04680fSAndrew Lunn 
16090b04680fSAndrew Lunn 	return ret;
16100b04680fSAndrew Lunn }
16110b04680fSAndrew Lunn 
16120b04680fSAndrew Lunn static int m88e1121_hwmon_read(struct device *dev,
16130b04680fSAndrew Lunn 			       enum hwmon_sensor_types type,
16140b04680fSAndrew Lunn 			       u32 attr, int channel, long *temp)
16150b04680fSAndrew Lunn {
16160b04680fSAndrew Lunn 	struct phy_device *phydev = dev_get_drvdata(dev);
16170b04680fSAndrew Lunn 	int err;
16180b04680fSAndrew Lunn 
16190b04680fSAndrew Lunn 	switch (attr) {
16200b04680fSAndrew Lunn 	case hwmon_temp_input:
16210b04680fSAndrew Lunn 		err = m88e1121_get_temp(phydev, temp);
16220b04680fSAndrew Lunn 		break;
16230b04680fSAndrew Lunn 	default:
16240b04680fSAndrew Lunn 		return -EOPNOTSUPP;
16250b04680fSAndrew Lunn 	}
16260b04680fSAndrew Lunn 
16270b04680fSAndrew Lunn 	return err;
16280b04680fSAndrew Lunn }
16290b04680fSAndrew Lunn 
16300b04680fSAndrew Lunn static umode_t m88e1121_hwmon_is_visible(const void *data,
16310b04680fSAndrew Lunn 					 enum hwmon_sensor_types type,
16320b04680fSAndrew Lunn 					 u32 attr, int channel)
16330b04680fSAndrew Lunn {
16340b04680fSAndrew Lunn 	if (type != hwmon_temp)
16350b04680fSAndrew Lunn 		return 0;
16360b04680fSAndrew Lunn 
16370b04680fSAndrew Lunn 	switch (attr) {
16380b04680fSAndrew Lunn 	case hwmon_temp_input:
16390b04680fSAndrew Lunn 		return 0444;
16400b04680fSAndrew Lunn 	default:
16410b04680fSAndrew Lunn 		return 0;
16420b04680fSAndrew Lunn 	}
16430b04680fSAndrew Lunn }
16440b04680fSAndrew Lunn 
16450b04680fSAndrew Lunn static u32 m88e1121_hwmon_chip_config[] = {
16460b04680fSAndrew Lunn 	HWMON_C_REGISTER_TZ,
16470b04680fSAndrew Lunn 	0
16480b04680fSAndrew Lunn };
16490b04680fSAndrew Lunn 
16500b04680fSAndrew Lunn static const struct hwmon_channel_info m88e1121_hwmon_chip = {
16510b04680fSAndrew Lunn 	.type = hwmon_chip,
16520b04680fSAndrew Lunn 	.config = m88e1121_hwmon_chip_config,
16530b04680fSAndrew Lunn };
16540b04680fSAndrew Lunn 
16550b04680fSAndrew Lunn static u32 m88e1121_hwmon_temp_config[] = {
16560b04680fSAndrew Lunn 	HWMON_T_INPUT,
16570b04680fSAndrew Lunn 	0
16580b04680fSAndrew Lunn };
16590b04680fSAndrew Lunn 
16600b04680fSAndrew Lunn static const struct hwmon_channel_info m88e1121_hwmon_temp = {
16610b04680fSAndrew Lunn 	.type = hwmon_temp,
16620b04680fSAndrew Lunn 	.config = m88e1121_hwmon_temp_config,
16630b04680fSAndrew Lunn };
16640b04680fSAndrew Lunn 
16650b04680fSAndrew Lunn static const struct hwmon_channel_info *m88e1121_hwmon_info[] = {
16660b04680fSAndrew Lunn 	&m88e1121_hwmon_chip,
16670b04680fSAndrew Lunn 	&m88e1121_hwmon_temp,
16680b04680fSAndrew Lunn 	NULL
16690b04680fSAndrew Lunn };
16700b04680fSAndrew Lunn 
16710b04680fSAndrew Lunn static const struct hwmon_ops m88e1121_hwmon_hwmon_ops = {
16720b04680fSAndrew Lunn 	.is_visible = m88e1121_hwmon_is_visible,
16730b04680fSAndrew Lunn 	.read = m88e1121_hwmon_read,
16740b04680fSAndrew Lunn };
16750b04680fSAndrew Lunn 
16760b04680fSAndrew Lunn static const struct hwmon_chip_info m88e1121_hwmon_chip_info = {
16770b04680fSAndrew Lunn 	.ops = &m88e1121_hwmon_hwmon_ops,
16780b04680fSAndrew Lunn 	.info = m88e1121_hwmon_info,
16790b04680fSAndrew Lunn };
16800b04680fSAndrew Lunn 
16810b04680fSAndrew Lunn static int m88e1510_get_temp(struct phy_device *phydev, long *temp)
16820b04680fSAndrew Lunn {
1683975b388cSAndrew Lunn 	int oldpage;
16840b04680fSAndrew Lunn 	int ret;
16850b04680fSAndrew Lunn 
16860b04680fSAndrew Lunn 	*temp = 0;
16870b04680fSAndrew Lunn 
16880b04680fSAndrew Lunn 	mutex_lock(&phydev->lock);
16890b04680fSAndrew Lunn 
169052295666SAndrew Lunn 	oldpage = marvell_get_set_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
1691975b388cSAndrew Lunn 	if (oldpage < 0) {
1692975b388cSAndrew Lunn 		mutex_unlock(&phydev->lock);
1693975b388cSAndrew Lunn 		return oldpage;
1694975b388cSAndrew Lunn 	}
1695975b388cSAndrew Lunn 
16960b04680fSAndrew Lunn 	ret = phy_read(phydev, MII_88E1510_TEMP_SENSOR);
16970b04680fSAndrew Lunn 	if (ret < 0)
16980b04680fSAndrew Lunn 		goto error;
16990b04680fSAndrew Lunn 
17000b04680fSAndrew Lunn 	*temp = ((ret & MII_88E1510_TEMP_SENSOR_MASK) - 25) * 1000;
17010b04680fSAndrew Lunn 
17020b04680fSAndrew Lunn error:
1703975b388cSAndrew Lunn 	marvell_set_page(phydev, oldpage);
17040b04680fSAndrew Lunn 	mutex_unlock(&phydev->lock);
17050b04680fSAndrew Lunn 
17060b04680fSAndrew Lunn 	return ret;
17070b04680fSAndrew Lunn }
17080b04680fSAndrew Lunn 
1709f0a45816SColin Ian King static int m88e1510_get_temp_critical(struct phy_device *phydev, long *temp)
17100b04680fSAndrew Lunn {
1711975b388cSAndrew Lunn 	int oldpage;
17120b04680fSAndrew Lunn 	int ret;
17130b04680fSAndrew Lunn 
17140b04680fSAndrew Lunn 	*temp = 0;
17150b04680fSAndrew Lunn 
17160b04680fSAndrew Lunn 	mutex_lock(&phydev->lock);
171753798328SAndrew Lunn 
171852295666SAndrew Lunn 	oldpage = marvell_get_set_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
1719975b388cSAndrew Lunn 	if (oldpage < 0) {
1720975b388cSAndrew Lunn 		mutex_unlock(&phydev->lock);
1721975b388cSAndrew Lunn 		return oldpage;
1722975b388cSAndrew Lunn 	}
17230b04680fSAndrew Lunn 
17240b04680fSAndrew Lunn 	ret = phy_read(phydev, MII_88E1121_MISC_TEST);
17250b04680fSAndrew Lunn 	if (ret < 0)
17260b04680fSAndrew Lunn 		goto error;
17270b04680fSAndrew Lunn 
17280b04680fSAndrew Lunn 	*temp = (((ret & MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK) >>
17290b04680fSAndrew Lunn 		  MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT) * 5) - 25;
17300b04680fSAndrew Lunn 	/* convert to mC */
17310b04680fSAndrew Lunn 	*temp *= 1000;
17320b04680fSAndrew Lunn 
17330b04680fSAndrew Lunn error:
1734975b388cSAndrew Lunn 	marvell_set_page(phydev, oldpage);
17350b04680fSAndrew Lunn 	mutex_unlock(&phydev->lock);
17360b04680fSAndrew Lunn 
17370b04680fSAndrew Lunn 	return ret;
17380b04680fSAndrew Lunn }
17390b04680fSAndrew Lunn 
1740f0a45816SColin Ian King static int m88e1510_set_temp_critical(struct phy_device *phydev, long temp)
17410b04680fSAndrew Lunn {
1742975b388cSAndrew Lunn 	int oldpage;
17430b04680fSAndrew Lunn 	int ret;
17440b04680fSAndrew Lunn 
17450b04680fSAndrew Lunn 	mutex_lock(&phydev->lock);
17460b04680fSAndrew Lunn 
174752295666SAndrew Lunn 	oldpage = marvell_get_set_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
1748975b388cSAndrew Lunn 	if (oldpage < 0) {
1749975b388cSAndrew Lunn 		mutex_unlock(&phydev->lock);
1750975b388cSAndrew Lunn 		return oldpage;
1751975b388cSAndrew Lunn 	}
1752975b388cSAndrew Lunn 
17530b04680fSAndrew Lunn 	ret = phy_read(phydev, MII_88E1121_MISC_TEST);
17540b04680fSAndrew Lunn 	if (ret < 0)
17550b04680fSAndrew Lunn 		goto error;
17560b04680fSAndrew Lunn 
17570b04680fSAndrew Lunn 	temp = temp / 1000;
17580b04680fSAndrew Lunn 	temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
17590b04680fSAndrew Lunn 	ret = phy_write(phydev, MII_88E1121_MISC_TEST,
17600b04680fSAndrew Lunn 			(ret & ~MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK) |
17610b04680fSAndrew Lunn 			(temp << MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT));
17620b04680fSAndrew Lunn 
17630b04680fSAndrew Lunn error:
1764975b388cSAndrew Lunn 	marvell_set_page(phydev, oldpage);
17650b04680fSAndrew Lunn 	mutex_unlock(&phydev->lock);
17660b04680fSAndrew Lunn 
17670b04680fSAndrew Lunn 	return ret;
17680b04680fSAndrew Lunn }
17690b04680fSAndrew Lunn 
1770f0a45816SColin Ian King static int m88e1510_get_temp_alarm(struct phy_device *phydev, long *alarm)
17710b04680fSAndrew Lunn {
1772975b388cSAndrew Lunn 	int oldpage;
17730b04680fSAndrew Lunn 	int ret;
17740b04680fSAndrew Lunn 
17750b04680fSAndrew Lunn 	*alarm = false;
17760b04680fSAndrew Lunn 
17770b04680fSAndrew Lunn 	mutex_lock(&phydev->lock);
17780b04680fSAndrew Lunn 
177952295666SAndrew Lunn 	oldpage = marvell_get_set_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
1780975b388cSAndrew Lunn 	if (oldpage < 0) {
1781975b388cSAndrew Lunn 		mutex_unlock(&phydev->lock);
1782975b388cSAndrew Lunn 		return oldpage;
1783975b388cSAndrew Lunn 	}
1784975b388cSAndrew Lunn 
17850b04680fSAndrew Lunn 	ret = phy_read(phydev, MII_88E1121_MISC_TEST);
17860b04680fSAndrew Lunn 	if (ret < 0)
17870b04680fSAndrew Lunn 		goto error;
17880b04680fSAndrew Lunn 	*alarm = !!(ret & MII_88E1510_MISC_TEST_TEMP_IRQ);
17890b04680fSAndrew Lunn 
17900b04680fSAndrew Lunn error:
1791975b388cSAndrew Lunn 	marvell_set_page(phydev, oldpage);
17920b04680fSAndrew Lunn 	mutex_unlock(&phydev->lock);
17930b04680fSAndrew Lunn 
17940b04680fSAndrew Lunn 	return ret;
17950b04680fSAndrew Lunn }
17960b04680fSAndrew Lunn 
17970b04680fSAndrew Lunn static int m88e1510_hwmon_read(struct device *dev,
17980b04680fSAndrew Lunn 			       enum hwmon_sensor_types type,
17990b04680fSAndrew Lunn 			       u32 attr, int channel, long *temp)
18000b04680fSAndrew Lunn {
18010b04680fSAndrew Lunn 	struct phy_device *phydev = dev_get_drvdata(dev);
18020b04680fSAndrew Lunn 	int err;
18030b04680fSAndrew Lunn 
18040b04680fSAndrew Lunn 	switch (attr) {
18050b04680fSAndrew Lunn 	case hwmon_temp_input:
18060b04680fSAndrew Lunn 		err = m88e1510_get_temp(phydev, temp);
18070b04680fSAndrew Lunn 		break;
18080b04680fSAndrew Lunn 	case hwmon_temp_crit:
18090b04680fSAndrew Lunn 		err = m88e1510_get_temp_critical(phydev, temp);
18100b04680fSAndrew Lunn 		break;
18110b04680fSAndrew Lunn 	case hwmon_temp_max_alarm:
18120b04680fSAndrew Lunn 		err = m88e1510_get_temp_alarm(phydev, temp);
18130b04680fSAndrew Lunn 		break;
18140b04680fSAndrew Lunn 	default:
18150b04680fSAndrew Lunn 		return -EOPNOTSUPP;
18160b04680fSAndrew Lunn 	}
18170b04680fSAndrew Lunn 
18180b04680fSAndrew Lunn 	return err;
18190b04680fSAndrew Lunn }
18200b04680fSAndrew Lunn 
18210b04680fSAndrew Lunn static int m88e1510_hwmon_write(struct device *dev,
18220b04680fSAndrew Lunn 				enum hwmon_sensor_types type,
18230b04680fSAndrew Lunn 				u32 attr, int channel, long temp)
18240b04680fSAndrew Lunn {
18250b04680fSAndrew Lunn 	struct phy_device *phydev = dev_get_drvdata(dev);
18260b04680fSAndrew Lunn 	int err;
18270b04680fSAndrew Lunn 
18280b04680fSAndrew Lunn 	switch (attr) {
18290b04680fSAndrew Lunn 	case hwmon_temp_crit:
18300b04680fSAndrew Lunn 		err = m88e1510_set_temp_critical(phydev, temp);
18310b04680fSAndrew Lunn 		break;
18320b04680fSAndrew Lunn 	default:
18330b04680fSAndrew Lunn 		return -EOPNOTSUPP;
18340b04680fSAndrew Lunn 	}
18350b04680fSAndrew Lunn 	return err;
18360b04680fSAndrew Lunn }
18370b04680fSAndrew Lunn 
18380b04680fSAndrew Lunn static umode_t m88e1510_hwmon_is_visible(const void *data,
18390b04680fSAndrew Lunn 					 enum hwmon_sensor_types type,
18400b04680fSAndrew Lunn 					 u32 attr, int channel)
18410b04680fSAndrew Lunn {
18420b04680fSAndrew Lunn 	if (type != hwmon_temp)
18430b04680fSAndrew Lunn 		return 0;
18440b04680fSAndrew Lunn 
18450b04680fSAndrew Lunn 	switch (attr) {
18460b04680fSAndrew Lunn 	case hwmon_temp_input:
18470b04680fSAndrew Lunn 	case hwmon_temp_max_alarm:
18480b04680fSAndrew Lunn 		return 0444;
18490b04680fSAndrew Lunn 	case hwmon_temp_crit:
18500b04680fSAndrew Lunn 		return 0644;
18510b04680fSAndrew Lunn 	default:
18520b04680fSAndrew Lunn 		return 0;
18530b04680fSAndrew Lunn 	}
18540b04680fSAndrew Lunn }
18550b04680fSAndrew Lunn 
18560b04680fSAndrew Lunn static u32 m88e1510_hwmon_temp_config[] = {
18570b04680fSAndrew Lunn 	HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_MAX_ALARM,
18580b04680fSAndrew Lunn 	0
18590b04680fSAndrew Lunn };
18600b04680fSAndrew Lunn 
18610b04680fSAndrew Lunn static const struct hwmon_channel_info m88e1510_hwmon_temp = {
18620b04680fSAndrew Lunn 	.type = hwmon_temp,
18630b04680fSAndrew Lunn 	.config = m88e1510_hwmon_temp_config,
18640b04680fSAndrew Lunn };
18650b04680fSAndrew Lunn 
18660b04680fSAndrew Lunn static const struct hwmon_channel_info *m88e1510_hwmon_info[] = {
18670b04680fSAndrew Lunn 	&m88e1121_hwmon_chip,
18680b04680fSAndrew Lunn 	&m88e1510_hwmon_temp,
18690b04680fSAndrew Lunn 	NULL
18700b04680fSAndrew Lunn };
18710b04680fSAndrew Lunn 
18720b04680fSAndrew Lunn static const struct hwmon_ops m88e1510_hwmon_hwmon_ops = {
18730b04680fSAndrew Lunn 	.is_visible = m88e1510_hwmon_is_visible,
18740b04680fSAndrew Lunn 	.read = m88e1510_hwmon_read,
18750b04680fSAndrew Lunn 	.write = m88e1510_hwmon_write,
18760b04680fSAndrew Lunn };
18770b04680fSAndrew Lunn 
18780b04680fSAndrew Lunn static const struct hwmon_chip_info m88e1510_hwmon_chip_info = {
18790b04680fSAndrew Lunn 	.ops = &m88e1510_hwmon_hwmon_ops,
18800b04680fSAndrew Lunn 	.info = m88e1510_hwmon_info,
18810b04680fSAndrew Lunn };
18820b04680fSAndrew Lunn 
18830b04680fSAndrew Lunn static int marvell_hwmon_name(struct phy_device *phydev)
18840b04680fSAndrew Lunn {
18850b04680fSAndrew Lunn 	struct marvell_priv *priv = phydev->priv;
18860b04680fSAndrew Lunn 	struct device *dev = &phydev->mdio.dev;
18870b04680fSAndrew Lunn 	const char *devname = dev_name(dev);
18880b04680fSAndrew Lunn 	size_t len = strlen(devname);
18890b04680fSAndrew Lunn 	int i, j;
18900b04680fSAndrew Lunn 
18910b04680fSAndrew Lunn 	priv->hwmon_name = devm_kzalloc(dev, len, GFP_KERNEL);
18920b04680fSAndrew Lunn 	if (!priv->hwmon_name)
18930b04680fSAndrew Lunn 		return -ENOMEM;
18940b04680fSAndrew Lunn 
18950b04680fSAndrew Lunn 	for (i = j = 0; i < len && devname[i]; i++) {
18960b04680fSAndrew Lunn 		if (isalnum(devname[i]))
18970b04680fSAndrew Lunn 			priv->hwmon_name[j++] = devname[i];
18980b04680fSAndrew Lunn 	}
18990b04680fSAndrew Lunn 
19000b04680fSAndrew Lunn 	return 0;
19010b04680fSAndrew Lunn }
19020b04680fSAndrew Lunn 
19030b04680fSAndrew Lunn static int marvell_hwmon_probe(struct phy_device *phydev,
19040b04680fSAndrew Lunn 			       const struct hwmon_chip_info *chip)
19050b04680fSAndrew Lunn {
19060b04680fSAndrew Lunn 	struct marvell_priv *priv = phydev->priv;
19070b04680fSAndrew Lunn 	struct device *dev = &phydev->mdio.dev;
19080b04680fSAndrew Lunn 	int err;
19090b04680fSAndrew Lunn 
19100b04680fSAndrew Lunn 	err = marvell_hwmon_name(phydev);
19110b04680fSAndrew Lunn 	if (err)
19120b04680fSAndrew Lunn 		return err;
19130b04680fSAndrew Lunn 
19140b04680fSAndrew Lunn 	priv->hwmon_dev = devm_hwmon_device_register_with_info(
19150b04680fSAndrew Lunn 		dev, priv->hwmon_name, phydev, chip, NULL);
19160b04680fSAndrew Lunn 
19170b04680fSAndrew Lunn 	return PTR_ERR_OR_ZERO(priv->hwmon_dev);
19180b04680fSAndrew Lunn }
19190b04680fSAndrew Lunn 
19200b04680fSAndrew Lunn static int m88e1121_hwmon_probe(struct phy_device *phydev)
19210b04680fSAndrew Lunn {
19220b04680fSAndrew Lunn 	return marvell_hwmon_probe(phydev, &m88e1121_hwmon_chip_info);
19230b04680fSAndrew Lunn }
19240b04680fSAndrew Lunn 
19250b04680fSAndrew Lunn static int m88e1510_hwmon_probe(struct phy_device *phydev)
19260b04680fSAndrew Lunn {
19270b04680fSAndrew Lunn 	return marvell_hwmon_probe(phydev, &m88e1510_hwmon_chip_info);
19280b04680fSAndrew Lunn }
19290b04680fSAndrew Lunn #else
19300b04680fSAndrew Lunn static int m88e1121_hwmon_probe(struct phy_device *phydev)
19310b04680fSAndrew Lunn {
19320b04680fSAndrew Lunn 	return 0;
19330b04680fSAndrew Lunn }
19340b04680fSAndrew Lunn 
19350b04680fSAndrew Lunn static int m88e1510_hwmon_probe(struct phy_device *phydev)
19360b04680fSAndrew Lunn {
19370b04680fSAndrew Lunn 	return 0;
19380b04680fSAndrew Lunn }
19390b04680fSAndrew Lunn #endif
19400b04680fSAndrew Lunn 
1941d2fa47d9SAndrew Lunn static int marvell_probe(struct phy_device *phydev)
1942d2fa47d9SAndrew Lunn {
1943d2fa47d9SAndrew Lunn 	struct marvell_priv *priv;
1944d2fa47d9SAndrew Lunn 
1945e5a03bfdSAndrew Lunn 	priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
1946d2fa47d9SAndrew Lunn 	if (!priv)
1947d2fa47d9SAndrew Lunn 		return -ENOMEM;
1948d2fa47d9SAndrew Lunn 
1949d2fa47d9SAndrew Lunn 	phydev->priv = priv;
1950d2fa47d9SAndrew Lunn 
1951d2fa47d9SAndrew Lunn 	return 0;
1952d2fa47d9SAndrew Lunn }
1953d2fa47d9SAndrew Lunn 
19540b04680fSAndrew Lunn static int m88e1121_probe(struct phy_device *phydev)
19550b04680fSAndrew Lunn {
19560b04680fSAndrew Lunn 	int err;
19570b04680fSAndrew Lunn 
19580b04680fSAndrew Lunn 	err = marvell_probe(phydev);
19590b04680fSAndrew Lunn 	if (err)
19600b04680fSAndrew Lunn 		return err;
19610b04680fSAndrew Lunn 
19620b04680fSAndrew Lunn 	return m88e1121_hwmon_probe(phydev);
19630b04680fSAndrew Lunn }
19640b04680fSAndrew Lunn 
19650b04680fSAndrew Lunn static int m88e1510_probe(struct phy_device *phydev)
19660b04680fSAndrew Lunn {
19670b04680fSAndrew Lunn 	int err;
19680b04680fSAndrew Lunn 
19690b04680fSAndrew Lunn 	err = marvell_probe(phydev);
19700b04680fSAndrew Lunn 	if (err)
19710b04680fSAndrew Lunn 		return err;
19720b04680fSAndrew Lunn 
19730b04680fSAndrew Lunn 	return m88e1510_hwmon_probe(phydev);
19740b04680fSAndrew Lunn }
19750b04680fSAndrew Lunn 
1976e5479239SOlof Johansson static struct phy_driver marvell_drivers[] = {
1977e5479239SOlof Johansson 	{
19782f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1101,
19792f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
198000db8189SAndy Fleming 		.name = "Marvell 88E1101",
198100db8189SAndy Fleming 		.features = PHY_GBIT_FEATURES,
198200db8189SAndy Fleming 		.flags = PHY_HAS_INTERRUPT,
198318702414SArnd Bergmann 		.probe = marvell_probe,
198479be1a1cSClemens Gruber 		.config_init = &marvell_config_init,
1985f2899788SAndrew Lunn 		.config_aneg = &m88e1101_config_aneg,
198600db8189SAndy Fleming 		.read_status = &genphy_read_status,
198700db8189SAndy Fleming 		.ack_interrupt = &marvell_ack_interrupt,
198800db8189SAndy Fleming 		.config_intr = &marvell_config_intr,
19890898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
19900898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
1991d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
1992d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
1993d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
1994e5479239SOlof Johansson 	},
1995e5479239SOlof Johansson 	{
19962f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1112,
19972f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
199885cfb534SOlof Johansson 		.name = "Marvell 88E1112",
199985cfb534SOlof Johansson 		.features = PHY_GBIT_FEATURES,
200085cfb534SOlof Johansson 		.flags = PHY_HAS_INTERRUPT,
2001d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
200285cfb534SOlof Johansson 		.config_init = &m88e1111_config_init,
200385cfb534SOlof Johansson 		.config_aneg = &marvell_config_aneg,
200485cfb534SOlof Johansson 		.read_status = &genphy_read_status,
200585cfb534SOlof Johansson 		.ack_interrupt = &marvell_ack_interrupt,
200685cfb534SOlof Johansson 		.config_intr = &marvell_config_intr,
20070898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
20080898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2009d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2010d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2011d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
201285cfb534SOlof Johansson 	},
201385cfb534SOlof Johansson 	{
20142f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1111,
20152f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
201676884679SAndy Fleming 		.name = "Marvell 88E1111",
201776884679SAndy Fleming 		.features = PHY_GBIT_FEATURES,
201876884679SAndy Fleming 		.flags = PHY_HAS_INTERRUPT,
2019d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
2020e5479239SOlof Johansson 		.config_init = &m88e1111_config_init,
20213ec0a0f1SHarini Katakam 		.config_aneg = &m88e1111_config_aneg,
2022be937f1fSAlexandr Smirnov 		.read_status = &marvell_read_status,
202376884679SAndy Fleming 		.ack_interrupt = &marvell_ack_interrupt,
202476884679SAndy Fleming 		.config_intr = &marvell_config_intr,
20250898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
20260898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2027d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2028d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2029d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2030e5479239SOlof Johansson 	},
2031e5479239SOlof Johansson 	{
20322f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1118,
20332f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2034605f196eSRon Madrid 		.name = "Marvell 88E1118",
2035605f196eSRon Madrid 		.features = PHY_GBIT_FEATURES,
2036605f196eSRon Madrid 		.flags = PHY_HAS_INTERRUPT,
2037d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
2038605f196eSRon Madrid 		.config_init = &m88e1118_config_init,
2039605f196eSRon Madrid 		.config_aneg = &m88e1118_config_aneg,
2040605f196eSRon Madrid 		.read_status = &genphy_read_status,
2041605f196eSRon Madrid 		.ack_interrupt = &marvell_ack_interrupt,
2042605f196eSRon Madrid 		.config_intr = &marvell_config_intr,
20430898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
20440898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2045d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2046d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2047d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2048605f196eSRon Madrid 	},
2049605f196eSRon Madrid 	{
20502f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1121R,
20512f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2052140bc929SSergei Poselenov 		.name = "Marvell 88E1121R",
2053140bc929SSergei Poselenov 		.features = PHY_GBIT_FEATURES,
2054140bc929SSergei Poselenov 		.flags = PHY_HAS_INTERRUPT,
205518702414SArnd Bergmann 		.probe = &m88e1121_probe,
2056fdecf36fSClemens Gruber 		.config_init = &m88e1121_config_init,
2057140bc929SSergei Poselenov 		.config_aneg = &m88e1121_config_aneg,
2058140bc929SSergei Poselenov 		.read_status = &marvell_read_status,
2059140bc929SSergei Poselenov 		.ack_interrupt = &marvell_ack_interrupt,
2060140bc929SSergei Poselenov 		.config_intr = &marvell_config_intr,
2061dcd07be3SAnatolij Gustschin 		.did_interrupt = &m88e1121_did_interrupt,
20620898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
20630898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2064d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2065d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2066d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2067140bc929SSergei Poselenov 	},
2068140bc929SSergei Poselenov 	{
2069337ac9d5SCyril Chemparathy 		.phy_id = MARVELL_PHY_ID_88E1318S,
20706ba74014SLinus Torvalds 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2071337ac9d5SCyril Chemparathy 		.name = "Marvell 88E1318S",
20723ff1c259SCyril Chemparathy 		.features = PHY_GBIT_FEATURES,
20733ff1c259SCyril Chemparathy 		.flags = PHY_HAS_INTERRUPT,
2074d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
2075fdecf36fSClemens Gruber 		.config_init = &m88e1121_config_init,
2076337ac9d5SCyril Chemparathy 		.config_aneg = &m88e1318_config_aneg,
20773ff1c259SCyril Chemparathy 		.read_status = &marvell_read_status,
20783ff1c259SCyril Chemparathy 		.ack_interrupt = &marvell_ack_interrupt,
20793ff1c259SCyril Chemparathy 		.config_intr = &marvell_config_intr,
20803ff1c259SCyril Chemparathy 		.did_interrupt = &m88e1121_did_interrupt,
20813871c387SMichael Stapelberg 		.get_wol = &m88e1318_get_wol,
20823871c387SMichael Stapelberg 		.set_wol = &m88e1318_set_wol,
20830898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
20840898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2085d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2086d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2087d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
20883ff1c259SCyril Chemparathy 	},
20893ff1c259SCyril Chemparathy 	{
20902f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1145,
20912f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
209276884679SAndy Fleming 		.name = "Marvell 88E1145",
209376884679SAndy Fleming 		.features = PHY_GBIT_FEATURES,
209476884679SAndy Fleming 		.flags = PHY_HAS_INTERRUPT,
2095d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
209676884679SAndy Fleming 		.config_init = &m88e1145_config_init,
209776884679SAndy Fleming 		.config_aneg = &marvell_config_aneg,
209876884679SAndy Fleming 		.read_status = &genphy_read_status,
209976884679SAndy Fleming 		.ack_interrupt = &marvell_ack_interrupt,
210076884679SAndy Fleming 		.config_intr = &marvell_config_intr,
21010898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
21020898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2103d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2104d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2105d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2106ac8c635aSOlof Johansson 	},
2107ac8c635aSOlof Johansson 	{
210890600732SDavid Daney 		.phy_id = MARVELL_PHY_ID_88E1149R,
210990600732SDavid Daney 		.phy_id_mask = MARVELL_PHY_ID_MASK,
211090600732SDavid Daney 		.name = "Marvell 88E1149R",
211190600732SDavid Daney 		.features = PHY_GBIT_FEATURES,
211290600732SDavid Daney 		.flags = PHY_HAS_INTERRUPT,
2113d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
211490600732SDavid Daney 		.config_init = &m88e1149_config_init,
211590600732SDavid Daney 		.config_aneg = &m88e1118_config_aneg,
211690600732SDavid Daney 		.read_status = &genphy_read_status,
211790600732SDavid Daney 		.ack_interrupt = &marvell_ack_interrupt,
211890600732SDavid Daney 		.config_intr = &marvell_config_intr,
21190898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
21200898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2121d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2122d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2123d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
212490600732SDavid Daney 	},
212590600732SDavid Daney 	{
21262f495c39SBenjamin Herrenschmidt 		.phy_id = MARVELL_PHY_ID_88E1240,
21272f495c39SBenjamin Herrenschmidt 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2128ac8c635aSOlof Johansson 		.name = "Marvell 88E1240",
2129ac8c635aSOlof Johansson 		.features = PHY_GBIT_FEATURES,
2130ac8c635aSOlof Johansson 		.flags = PHY_HAS_INTERRUPT,
2131d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
2132ac8c635aSOlof Johansson 		.config_init = &m88e1111_config_init,
2133ac8c635aSOlof Johansson 		.config_aneg = &marvell_config_aneg,
2134ac8c635aSOlof Johansson 		.read_status = &genphy_read_status,
2135ac8c635aSOlof Johansson 		.ack_interrupt = &marvell_ack_interrupt,
2136ac8c635aSOlof Johansson 		.config_intr = &marvell_config_intr,
21370898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
21380898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2139d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2140d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2141d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2142ac8c635aSOlof Johansson 	},
21433da09a51SMichal Simek 	{
21443da09a51SMichal Simek 		.phy_id = MARVELL_PHY_ID_88E1116R,
21453da09a51SMichal Simek 		.phy_id_mask = MARVELL_PHY_ID_MASK,
21463da09a51SMichal Simek 		.name = "Marvell 88E1116R",
21473da09a51SMichal Simek 		.features = PHY_GBIT_FEATURES,
21483da09a51SMichal Simek 		.flags = PHY_HAS_INTERRUPT,
2149d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
21503da09a51SMichal Simek 		.config_init = &m88e1116r_config_init,
21513da09a51SMichal Simek 		.config_aneg = &genphy_config_aneg,
21523da09a51SMichal Simek 		.read_status = &genphy_read_status,
21533da09a51SMichal Simek 		.ack_interrupt = &marvell_ack_interrupt,
21543da09a51SMichal Simek 		.config_intr = &marvell_config_intr,
21550898b448SSebastian Hesselbarth 		.resume = &genphy_resume,
21560898b448SSebastian Hesselbarth 		.suspend = &genphy_suspend,
2157d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2158d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2159d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
21603da09a51SMichal Simek 	},
216110e24caaSMichal Simek 	{
216210e24caaSMichal Simek 		.phy_id = MARVELL_PHY_ID_88E1510,
216310e24caaSMichal Simek 		.phy_id_mask = MARVELL_PHY_ID_MASK,
216410e24caaSMichal Simek 		.name = "Marvell 88E1510",
21656cfb3bccSCharles-Antoine Couret 		.features = PHY_GBIT_FEATURES | SUPPORTED_FIBRE,
216618702414SArnd Bergmann 		.flags = PHY_HAS_INTERRUPT,
21670b04680fSAndrew Lunn 		.probe = &m88e1510_probe,
2168930b37eeSStefan Roese 		.config_init = &m88e1510_config_init,
216910e24caaSMichal Simek 		.config_aneg = &m88e1510_config_aneg,
217010e24caaSMichal Simek 		.read_status = &marvell_read_status,
217110e24caaSMichal Simek 		.ack_interrupt = &marvell_ack_interrupt,
217210e24caaSMichal Simek 		.config_intr = &marvell_config_intr,
217310e24caaSMichal Simek 		.did_interrupt = &m88e1121_did_interrupt,
2174f39aac7eSJingju Hou 		.get_wol = &m88e1318_get_wol,
2175f39aac7eSJingju Hou 		.set_wol = &m88e1318_set_wol,
21763758be3dSCharles-Antoine Couret 		.resume = &marvell_resume,
21773758be3dSCharles-Antoine Couret 		.suspend = &marvell_suspend,
2178d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2179d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2180d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
218110e24caaSMichal Simek 	},
21826b358aedSSebastian Hesselbarth 	{
2183819ec8e1SAndrew Lunn 		.phy_id = MARVELL_PHY_ID_88E1540,
2184819ec8e1SAndrew Lunn 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2185819ec8e1SAndrew Lunn 		.name = "Marvell 88E1540",
2186819ec8e1SAndrew Lunn 		.features = PHY_GBIT_FEATURES,
2187819ec8e1SAndrew Lunn 		.flags = PHY_HAS_INTERRUPT,
218818702414SArnd Bergmann 		.probe = m88e1510_probe,
218979be1a1cSClemens Gruber 		.config_init = &marvell_config_init,
2190819ec8e1SAndrew Lunn 		.config_aneg = &m88e1510_config_aneg,
2191819ec8e1SAndrew Lunn 		.read_status = &marvell_read_status,
2192819ec8e1SAndrew Lunn 		.ack_interrupt = &marvell_ack_interrupt,
2193819ec8e1SAndrew Lunn 		.config_intr = &marvell_config_intr,
2194819ec8e1SAndrew Lunn 		.did_interrupt = &m88e1121_did_interrupt,
2195819ec8e1SAndrew Lunn 		.resume = &genphy_resume,
2196819ec8e1SAndrew Lunn 		.suspend = &genphy_suspend,
2197d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2198d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2199d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
2200819ec8e1SAndrew Lunn 	},
2201819ec8e1SAndrew Lunn 	{
220260f06fdeSAndrew Lunn 		.phy_id = MARVELL_PHY_ID_88E1545,
220360f06fdeSAndrew Lunn 		.phy_id_mask = MARVELL_PHY_ID_MASK,
220460f06fdeSAndrew Lunn 		.name = "Marvell 88E1545",
220560f06fdeSAndrew Lunn 		.probe = m88e1510_probe,
220660f06fdeSAndrew Lunn 		.features = PHY_GBIT_FEATURES,
220760f06fdeSAndrew Lunn 		.flags = PHY_HAS_INTERRUPT,
220860f06fdeSAndrew Lunn 		.config_init = &marvell_config_init,
220960f06fdeSAndrew Lunn 		.config_aneg = &m88e1510_config_aneg,
221060f06fdeSAndrew Lunn 		.read_status = &marvell_read_status,
221160f06fdeSAndrew Lunn 		.ack_interrupt = &marvell_ack_interrupt,
221260f06fdeSAndrew Lunn 		.config_intr = &marvell_config_intr,
221360f06fdeSAndrew Lunn 		.did_interrupt = &m88e1121_did_interrupt,
221460f06fdeSAndrew Lunn 		.resume = &genphy_resume,
221560f06fdeSAndrew Lunn 		.suspend = &genphy_suspend,
221660f06fdeSAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
221760f06fdeSAndrew Lunn 		.get_strings = marvell_get_strings,
221860f06fdeSAndrew Lunn 		.get_stats = marvell_get_stats,
221960f06fdeSAndrew Lunn 	},
222060f06fdeSAndrew Lunn 	{
22216b358aedSSebastian Hesselbarth 		.phy_id = MARVELL_PHY_ID_88E3016,
22226b358aedSSebastian Hesselbarth 		.phy_id_mask = MARVELL_PHY_ID_MASK,
22236b358aedSSebastian Hesselbarth 		.name = "Marvell 88E3016",
22246b358aedSSebastian Hesselbarth 		.features = PHY_BASIC_FEATURES,
22256b358aedSSebastian Hesselbarth 		.flags = PHY_HAS_INTERRUPT,
2226d2fa47d9SAndrew Lunn 		.probe = marvell_probe,
22276b358aedSSebastian Hesselbarth 		.config_aneg = &genphy_config_aneg,
22286b358aedSSebastian Hesselbarth 		.config_init = &m88e3016_config_init,
22296b358aedSSebastian Hesselbarth 		.aneg_done = &marvell_aneg_done,
22306b358aedSSebastian Hesselbarth 		.read_status = &marvell_read_status,
22316b358aedSSebastian Hesselbarth 		.ack_interrupt = &marvell_ack_interrupt,
22326b358aedSSebastian Hesselbarth 		.config_intr = &marvell_config_intr,
22336b358aedSSebastian Hesselbarth 		.did_interrupt = &m88e1121_did_interrupt,
22346b358aedSSebastian Hesselbarth 		.resume = &genphy_resume,
22356b358aedSSebastian Hesselbarth 		.suspend = &genphy_suspend,
2236d2fa47d9SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2237d2fa47d9SAndrew Lunn 		.get_strings = marvell_get_strings,
2238d2fa47d9SAndrew Lunn 		.get_stats = marvell_get_stats,
22396b358aedSSebastian Hesselbarth 	},
2240e4cf8a38SAndrew Lunn 	{
2241e4cf8a38SAndrew Lunn 		.phy_id = MARVELL_PHY_ID_88E6390,
2242e4cf8a38SAndrew Lunn 		.phy_id_mask = MARVELL_PHY_ID_MASK,
2243e4cf8a38SAndrew Lunn 		.name = "Marvell 88E6390",
2244e4cf8a38SAndrew Lunn 		.features = PHY_GBIT_FEATURES,
2245e4cf8a38SAndrew Lunn 		.flags = PHY_HAS_INTERRUPT,
2246e4cf8a38SAndrew Lunn 		.probe = m88e1510_probe,
2247e4cf8a38SAndrew Lunn 		.config_init = &marvell_config_init,
2248e4cf8a38SAndrew Lunn 		.config_aneg = &m88e1510_config_aneg,
2249e4cf8a38SAndrew Lunn 		.read_status = &marvell_read_status,
2250e4cf8a38SAndrew Lunn 		.ack_interrupt = &marvell_ack_interrupt,
2251e4cf8a38SAndrew Lunn 		.config_intr = &marvell_config_intr,
2252e4cf8a38SAndrew Lunn 		.did_interrupt = &m88e1121_did_interrupt,
2253e4cf8a38SAndrew Lunn 		.resume = &genphy_resume,
2254e4cf8a38SAndrew Lunn 		.suspend = &genphy_suspend,
2255e4cf8a38SAndrew Lunn 		.get_sset_count = marvell_get_sset_count,
2256e4cf8a38SAndrew Lunn 		.get_strings = marvell_get_strings,
2257e4cf8a38SAndrew Lunn 		.get_stats = marvell_get_stats,
2258e4cf8a38SAndrew Lunn 	},
225976884679SAndy Fleming };
226076884679SAndy Fleming 
226150fd7150SJohan Hovold module_phy_driver(marvell_drivers);
22624e4f10f6SDavid Woodhouse 
2263cf93c945SUwe Kleine-König static struct mdio_device_id __maybe_unused marvell_tbl[] = {
2264f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1101, MARVELL_PHY_ID_MASK },
2265f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1112, MARVELL_PHY_ID_MASK },
2266f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1111, MARVELL_PHY_ID_MASK },
2267f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1118, MARVELL_PHY_ID_MASK },
2268f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1121R, MARVELL_PHY_ID_MASK },
2269f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1145, MARVELL_PHY_ID_MASK },
2270f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1149R, MARVELL_PHY_ID_MASK },
2271f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1240, MARVELL_PHY_ID_MASK },
2272f5e1cabfSMichal Simek 	{ MARVELL_PHY_ID_88E1318S, MARVELL_PHY_ID_MASK },
22733da09a51SMichal Simek 	{ MARVELL_PHY_ID_88E1116R, MARVELL_PHY_ID_MASK },
227410e24caaSMichal Simek 	{ MARVELL_PHY_ID_88E1510, MARVELL_PHY_ID_MASK },
2275819ec8e1SAndrew Lunn 	{ MARVELL_PHY_ID_88E1540, MARVELL_PHY_ID_MASK },
227660f06fdeSAndrew Lunn 	{ MARVELL_PHY_ID_88E1545, MARVELL_PHY_ID_MASK },
22776b358aedSSebastian Hesselbarth 	{ MARVELL_PHY_ID_88E3016, MARVELL_PHY_ID_MASK },
2278e4cf8a38SAndrew Lunn 	{ MARVELL_PHY_ID_88E6390, MARVELL_PHY_ID_MASK },
22794e4f10f6SDavid Woodhouse 	{ }
22804e4f10f6SDavid Woodhouse };
22814e4f10f6SDavid Woodhouse 
22824e4f10f6SDavid Woodhouse MODULE_DEVICE_TABLE(mdio, marvell_tbl);
2283