162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * drivers/net/phy/marvell.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Driver for Marvell PHYs
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Andy Fleming
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Copyright (c) 2004 Freescale Semiconductor, Inc.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Copyright (c) 2013 Michael Stapelberg <michael@stapelberg.de>
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/string.h>
1562306a36Sopenharmony_ci#include <linux/ctype.h>
1662306a36Sopenharmony_ci#include <linux/errno.h>
1762306a36Sopenharmony_ci#include <linux/unistd.h>
1862306a36Sopenharmony_ci#include <linux/hwmon.h>
1962306a36Sopenharmony_ci#include <linux/interrupt.h>
2062306a36Sopenharmony_ci#include <linux/init.h>
2162306a36Sopenharmony_ci#include <linux/delay.h>
2262306a36Sopenharmony_ci#include <linux/netdevice.h>
2362306a36Sopenharmony_ci#include <linux/etherdevice.h>
2462306a36Sopenharmony_ci#include <linux/skbuff.h>
2562306a36Sopenharmony_ci#include <linux/spinlock.h>
2662306a36Sopenharmony_ci#include <linux/mm.h>
2762306a36Sopenharmony_ci#include <linux/module.h>
2862306a36Sopenharmony_ci#include <linux/mii.h>
2962306a36Sopenharmony_ci#include <linux/ethtool.h>
3062306a36Sopenharmony_ci#include <linux/ethtool_netlink.h>
3162306a36Sopenharmony_ci#include <linux/phy.h>
3262306a36Sopenharmony_ci#include <linux/marvell_phy.h>
3362306a36Sopenharmony_ci#include <linux/bitfield.h>
3462306a36Sopenharmony_ci#include <linux/of.h>
3562306a36Sopenharmony_ci#include <linux/sfp.h>
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#include <linux/io.h>
3862306a36Sopenharmony_ci#include <asm/irq.h>
3962306a36Sopenharmony_ci#include <linux/uaccess.h>
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define MII_MARVELL_PHY_PAGE		22
4262306a36Sopenharmony_ci#define MII_MARVELL_COPPER_PAGE		0x00
4362306a36Sopenharmony_ci#define MII_MARVELL_FIBER_PAGE		0x01
4462306a36Sopenharmony_ci#define MII_MARVELL_MSCR_PAGE		0x02
4562306a36Sopenharmony_ci#define MII_MARVELL_LED_PAGE		0x03
4662306a36Sopenharmony_ci#define MII_MARVELL_VCT5_PAGE		0x05
4762306a36Sopenharmony_ci#define MII_MARVELL_MISC_TEST_PAGE	0x06
4862306a36Sopenharmony_ci#define MII_MARVELL_VCT7_PAGE		0x07
4962306a36Sopenharmony_ci#define MII_MARVELL_WOL_PAGE		0x11
5062306a36Sopenharmony_ci#define MII_MARVELL_MODE_PAGE		0x12
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define MII_M1011_IEVENT		0x13
5362306a36Sopenharmony_ci#define MII_M1011_IEVENT_CLEAR		0x0000
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#define MII_M1011_IMASK			0x12
5662306a36Sopenharmony_ci#define MII_M1011_IMASK_INIT		0x6400
5762306a36Sopenharmony_ci#define MII_M1011_IMASK_CLEAR		0x0000
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#define MII_M1011_PHY_SCR			0x10
6062306a36Sopenharmony_ci#define MII_M1011_PHY_SCR_DOWNSHIFT_EN		BIT(11)
6162306a36Sopenharmony_ci#define MII_M1011_PHY_SCR_DOWNSHIFT_MASK	GENMASK(14, 12)
6262306a36Sopenharmony_ci#define MII_M1011_PHY_SCR_DOWNSHIFT_MAX		8
6362306a36Sopenharmony_ci#define MII_M1011_PHY_SCR_MDI			(0x0 << 5)
6462306a36Sopenharmony_ci#define MII_M1011_PHY_SCR_MDI_X			(0x1 << 5)
6562306a36Sopenharmony_ci#define MII_M1011_PHY_SCR_AUTO_CROSS		(0x3 << 5)
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci#define MII_M1011_PHY_SSR			0x11
6862306a36Sopenharmony_ci#define MII_M1011_PHY_SSR_DOWNSHIFT		BIT(5)
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci#define MII_M1111_PHY_LED_CONTROL	0x18
7162306a36Sopenharmony_ci#define MII_M1111_PHY_LED_DIRECT	0x4100
7262306a36Sopenharmony_ci#define MII_M1111_PHY_LED_COMBINE	0x411c
7362306a36Sopenharmony_ci#define MII_M1111_PHY_EXT_CR		0x14
7462306a36Sopenharmony_ci#define MII_M1111_PHY_EXT_CR_DOWNSHIFT_MASK	GENMASK(11, 9)
7562306a36Sopenharmony_ci#define MII_M1111_PHY_EXT_CR_DOWNSHIFT_MAX	8
7662306a36Sopenharmony_ci#define MII_M1111_PHY_EXT_CR_DOWNSHIFT_EN	BIT(8)
7762306a36Sopenharmony_ci#define MII_M1111_RGMII_RX_DELAY	BIT(7)
7862306a36Sopenharmony_ci#define MII_M1111_RGMII_TX_DELAY	BIT(1)
7962306a36Sopenharmony_ci#define MII_M1111_PHY_EXT_SR		0x1b
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci#define MII_M1111_HWCFG_MODE_MASK		0xf
8262306a36Sopenharmony_ci#define MII_M1111_HWCFG_MODE_FIBER_RGMII	0x3
8362306a36Sopenharmony_ci#define MII_M1111_HWCFG_MODE_SGMII_NO_CLK	0x4
8462306a36Sopenharmony_ci#define MII_M1111_HWCFG_MODE_RTBI		0x7
8562306a36Sopenharmony_ci#define MII_M1111_HWCFG_MODE_COPPER_1000X_AN	0x8
8662306a36Sopenharmony_ci#define MII_M1111_HWCFG_MODE_COPPER_RTBI	0x9
8762306a36Sopenharmony_ci#define MII_M1111_HWCFG_MODE_COPPER_RGMII	0xb
8862306a36Sopenharmony_ci#define MII_M1111_HWCFG_MODE_COPPER_1000X_NOAN	0xc
8962306a36Sopenharmony_ci#define MII_M1111_HWCFG_SERIAL_AN_BYPASS	BIT(12)
9062306a36Sopenharmony_ci#define MII_M1111_HWCFG_FIBER_COPPER_RES	BIT(13)
9162306a36Sopenharmony_ci#define MII_M1111_HWCFG_FIBER_COPPER_AUTO	BIT(15)
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci#define MII_88E1121_PHY_MSCR_REG	21
9462306a36Sopenharmony_ci#define MII_88E1121_PHY_MSCR_RX_DELAY	BIT(5)
9562306a36Sopenharmony_ci#define MII_88E1121_PHY_MSCR_TX_DELAY	BIT(4)
9662306a36Sopenharmony_ci#define MII_88E1121_PHY_MSCR_DELAY_MASK	(BIT(5) | BIT(4))
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci#define MII_88E1121_MISC_TEST				0x1a
9962306a36Sopenharmony_ci#define MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK	0x1f00
10062306a36Sopenharmony_ci#define MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT	8
10162306a36Sopenharmony_ci#define MII_88E1510_MISC_TEST_TEMP_IRQ_EN		BIT(7)
10262306a36Sopenharmony_ci#define MII_88E1510_MISC_TEST_TEMP_IRQ			BIT(6)
10362306a36Sopenharmony_ci#define MII_88E1121_MISC_TEST_TEMP_SENSOR_EN		BIT(5)
10462306a36Sopenharmony_ci#define MII_88E1121_MISC_TEST_TEMP_MASK			0x1f
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci#define MII_88E1510_TEMP_SENSOR		0x1b
10762306a36Sopenharmony_ci#define MII_88E1510_TEMP_SENSOR_MASK	0xff
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci#define MII_88E1540_COPPER_CTRL3	0x1a
11062306a36Sopenharmony_ci#define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_MASK	GENMASK(11, 10)
11162306a36Sopenharmony_ci#define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_00MS	0
11262306a36Sopenharmony_ci#define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_10MS	1
11362306a36Sopenharmony_ci#define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_20MS	2
11462306a36Sopenharmony_ci#define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_40MS	3
11562306a36Sopenharmony_ci#define MII_88E1540_COPPER_CTRL3_FAST_LINK_DOWN		BIT(9)
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci#define MII_88E6390_MISC_TEST		0x1b
11862306a36Sopenharmony_ci#define MII_88E6390_MISC_TEST_TEMP_SENSOR_ENABLE_SAMPLE_1S	(0x0 << 14)
11962306a36Sopenharmony_ci#define MII_88E6390_MISC_TEST_TEMP_SENSOR_ENABLE		(0x1 << 14)
12062306a36Sopenharmony_ci#define MII_88E6390_MISC_TEST_TEMP_SENSOR_ENABLE_ONESHOT	(0x2 << 14)
12162306a36Sopenharmony_ci#define MII_88E6390_MISC_TEST_TEMP_SENSOR_DISABLE		(0x3 << 14)
12262306a36Sopenharmony_ci#define MII_88E6390_MISC_TEST_TEMP_SENSOR_MASK			(0x3 << 14)
12362306a36Sopenharmony_ci#define MII_88E6393_MISC_TEST_SAMPLES_2048	(0x0 << 11)
12462306a36Sopenharmony_ci#define MII_88E6393_MISC_TEST_SAMPLES_4096	(0x1 << 11)
12562306a36Sopenharmony_ci#define MII_88E6393_MISC_TEST_SAMPLES_8192	(0x2 << 11)
12662306a36Sopenharmony_ci#define MII_88E6393_MISC_TEST_SAMPLES_16384	(0x3 << 11)
12762306a36Sopenharmony_ci#define MII_88E6393_MISC_TEST_SAMPLES_MASK	(0x3 << 11)
12862306a36Sopenharmony_ci#define MII_88E6393_MISC_TEST_RATE_2_3MS	(0x5 << 8)
12962306a36Sopenharmony_ci#define MII_88E6393_MISC_TEST_RATE_6_4MS	(0x6 << 8)
13062306a36Sopenharmony_ci#define MII_88E6393_MISC_TEST_RATE_11_9MS	(0x7 << 8)
13162306a36Sopenharmony_ci#define MII_88E6393_MISC_TEST_RATE_MASK		(0x7 << 8)
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci#define MII_88E6390_TEMP_SENSOR		0x1c
13462306a36Sopenharmony_ci#define MII_88E6393_TEMP_SENSOR_THRESHOLD_MASK	0xff00
13562306a36Sopenharmony_ci#define MII_88E6393_TEMP_SENSOR_THRESHOLD_SHIFT	8
13662306a36Sopenharmony_ci#define MII_88E6390_TEMP_SENSOR_MASK		0xff
13762306a36Sopenharmony_ci#define MII_88E6390_TEMP_SENSOR_SAMPLES		10
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci#define MII_88E1318S_PHY_MSCR1_REG	16
14062306a36Sopenharmony_ci#define MII_88E1318S_PHY_MSCR1_PAD_ODD	BIT(6)
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci/* Copper Specific Interrupt Enable Register */
14362306a36Sopenharmony_ci#define MII_88E1318S_PHY_CSIER				0x12
14462306a36Sopenharmony_ci/* WOL Event Interrupt Enable */
14562306a36Sopenharmony_ci#define MII_88E1318S_PHY_CSIER_WOL_EIE			BIT(7)
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci#define MII_88E1318S_PHY_LED_FUNC		0x10
14862306a36Sopenharmony_ci#define MII_88E1318S_PHY_LED_FUNC_OFF		(0x8)
14962306a36Sopenharmony_ci#define MII_88E1318S_PHY_LED_FUNC_ON		(0x9)
15062306a36Sopenharmony_ci#define MII_88E1318S_PHY_LED_FUNC_HI_Z		(0xa)
15162306a36Sopenharmony_ci#define MII_88E1318S_PHY_LED_FUNC_BLINK		(0xb)
15262306a36Sopenharmony_ci#define MII_88E1318S_PHY_LED_TCR		0x12
15362306a36Sopenharmony_ci#define MII_88E1318S_PHY_LED_TCR_FORCE_INT	BIT(15)
15462306a36Sopenharmony_ci#define MII_88E1318S_PHY_LED_TCR_INTn_ENABLE	BIT(7)
15562306a36Sopenharmony_ci#define MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW	BIT(11)
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci/* Magic Packet MAC address registers */
15862306a36Sopenharmony_ci#define MII_88E1318S_PHY_MAGIC_PACKET_WORD2		0x17
15962306a36Sopenharmony_ci#define MII_88E1318S_PHY_MAGIC_PACKET_WORD1		0x18
16062306a36Sopenharmony_ci#define MII_88E1318S_PHY_MAGIC_PACKET_WORD0		0x19
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci#define MII_88E1318S_PHY_WOL_CTRL				0x10
16362306a36Sopenharmony_ci#define MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS		BIT(12)
16462306a36Sopenharmony_ci#define MII_88E1318S_PHY_WOL_CTRL_LINK_UP_ENABLE		BIT(13)
16562306a36Sopenharmony_ci#define MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE	BIT(14)
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci#define MII_PHY_LED_CTRL	        16
16862306a36Sopenharmony_ci#define MII_88E1121_PHY_LED_DEF		0x0030
16962306a36Sopenharmony_ci#define MII_88E1510_PHY_LED_DEF		0x1177
17062306a36Sopenharmony_ci#define MII_88E1510_PHY_LED0_LINK_LED1_ACTIVE	0x1040
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci#define MII_M1011_PHY_STATUS		0x11
17362306a36Sopenharmony_ci#define MII_M1011_PHY_STATUS_1000	0x8000
17462306a36Sopenharmony_ci#define MII_M1011_PHY_STATUS_100	0x4000
17562306a36Sopenharmony_ci#define MII_M1011_PHY_STATUS_SPD_MASK	0xc000
17662306a36Sopenharmony_ci#define MII_M1011_PHY_STATUS_FULLDUPLEX	0x2000
17762306a36Sopenharmony_ci#define MII_M1011_PHY_STATUS_RESOLVED	0x0800
17862306a36Sopenharmony_ci#define MII_M1011_PHY_STATUS_LINK	0x0400
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci#define MII_88E3016_PHY_SPEC_CTRL	0x10
18162306a36Sopenharmony_ci#define MII_88E3016_DISABLE_SCRAMBLER	0x0200
18262306a36Sopenharmony_ci#define MII_88E3016_AUTO_MDIX_CROSSOVER	0x0030
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci#define MII_88E1510_GEN_CTRL_REG_1		0x14
18562306a36Sopenharmony_ci#define MII_88E1510_GEN_CTRL_REG_1_MODE_MASK	0x7
18662306a36Sopenharmony_ci#define MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII	0x0	/* RGMII to copper */
18762306a36Sopenharmony_ci#define MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII	0x1	/* SGMII to copper */
18862306a36Sopenharmony_ci/* RGMII to 1000BASE-X */
18962306a36Sopenharmony_ci#define MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII_1000X	0x2
19062306a36Sopenharmony_ci/* RGMII to 100BASE-FX */
19162306a36Sopenharmony_ci#define MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII_100FX	0x3
19262306a36Sopenharmony_ci/* RGMII to SGMII */
19362306a36Sopenharmony_ci#define MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII_SGMII	0x4
19462306a36Sopenharmony_ci#define MII_88E1510_GEN_CTRL_REG_1_RESET	0x8000	/* Soft reset */
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci#define MII_88E1510_MSCR_2		0x15
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci#define MII_VCT5_TX_RX_MDI0_COUPLING	0x10
19962306a36Sopenharmony_ci#define MII_VCT5_TX_RX_MDI1_COUPLING	0x11
20062306a36Sopenharmony_ci#define MII_VCT5_TX_RX_MDI2_COUPLING	0x12
20162306a36Sopenharmony_ci#define MII_VCT5_TX_RX_MDI3_COUPLING	0x13
20262306a36Sopenharmony_ci#define MII_VCT5_TX_RX_AMPLITUDE_MASK	0x7f00
20362306a36Sopenharmony_ci#define MII_VCT5_TX_RX_AMPLITUDE_SHIFT	8
20462306a36Sopenharmony_ci#define MII_VCT5_TX_RX_COUPLING_POSITIVE_REFLECTION	BIT(15)
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci#define MII_VCT5_CTRL				0x17
20762306a36Sopenharmony_ci#define MII_VCT5_CTRL_ENABLE				BIT(15)
20862306a36Sopenharmony_ci#define MII_VCT5_CTRL_COMPLETE				BIT(14)
20962306a36Sopenharmony_ci#define MII_VCT5_CTRL_TX_SAME_CHANNEL			(0x0 << 11)
21062306a36Sopenharmony_ci#define MII_VCT5_CTRL_TX0_CHANNEL			(0x4 << 11)
21162306a36Sopenharmony_ci#define MII_VCT5_CTRL_TX1_CHANNEL			(0x5 << 11)
21262306a36Sopenharmony_ci#define MII_VCT5_CTRL_TX2_CHANNEL			(0x6 << 11)
21362306a36Sopenharmony_ci#define MII_VCT5_CTRL_TX3_CHANNEL			(0x7 << 11)
21462306a36Sopenharmony_ci#define MII_VCT5_CTRL_SAMPLES_2				(0x0 << 8)
21562306a36Sopenharmony_ci#define MII_VCT5_CTRL_SAMPLES_4				(0x1 << 8)
21662306a36Sopenharmony_ci#define MII_VCT5_CTRL_SAMPLES_8				(0x2 << 8)
21762306a36Sopenharmony_ci#define MII_VCT5_CTRL_SAMPLES_16			(0x3 << 8)
21862306a36Sopenharmony_ci#define MII_VCT5_CTRL_SAMPLES_32			(0x4 << 8)
21962306a36Sopenharmony_ci#define MII_VCT5_CTRL_SAMPLES_64			(0x5 << 8)
22062306a36Sopenharmony_ci#define MII_VCT5_CTRL_SAMPLES_128			(0x6 << 8)
22162306a36Sopenharmony_ci#define MII_VCT5_CTRL_SAMPLES_DEFAULT			(0x6 << 8)
22262306a36Sopenharmony_ci#define MII_VCT5_CTRL_SAMPLES_256			(0x7 << 8)
22362306a36Sopenharmony_ci#define MII_VCT5_CTRL_SAMPLES_SHIFT			8
22462306a36Sopenharmony_ci#define MII_VCT5_CTRL_MODE_MAXIMUM_PEEK			(0x0 << 6)
22562306a36Sopenharmony_ci#define MII_VCT5_CTRL_MODE_FIRST_LAST_PEEK		(0x1 << 6)
22662306a36Sopenharmony_ci#define MII_VCT5_CTRL_MODE_OFFSET			(0x2 << 6)
22762306a36Sopenharmony_ci#define MII_VCT5_CTRL_SAMPLE_POINT			(0x3 << 6)
22862306a36Sopenharmony_ci#define MII_VCT5_CTRL_PEEK_HYST_DEFAULT			3
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci#define MII_VCT5_SAMPLE_POINT_DISTANCE		0x18
23162306a36Sopenharmony_ci#define MII_VCT5_SAMPLE_POINT_DISTANCE_MAX	511
23262306a36Sopenharmony_ci#define MII_VCT5_TX_PULSE_CTRL			0x1c
23362306a36Sopenharmony_ci#define MII_VCT5_TX_PULSE_CTRL_DONT_WAIT_LINK_DOWN	BIT(12)
23462306a36Sopenharmony_ci#define MII_VCT5_TX_PULSE_CTRL_PULSE_WIDTH_128nS	(0x0 << 10)
23562306a36Sopenharmony_ci#define MII_VCT5_TX_PULSE_CTRL_PULSE_WIDTH_96nS		(0x1 << 10)
23662306a36Sopenharmony_ci#define MII_VCT5_TX_PULSE_CTRL_PULSE_WIDTH_64nS		(0x2 << 10)
23762306a36Sopenharmony_ci#define MII_VCT5_TX_PULSE_CTRL_PULSE_WIDTH_32nS		(0x3 << 10)
23862306a36Sopenharmony_ci#define MII_VCT5_TX_PULSE_CTRL_PULSE_WIDTH_SHIFT	10
23962306a36Sopenharmony_ci#define MII_VCT5_TX_PULSE_CTRL_PULSE_AMPLITUDE_1000mV	(0x0 << 8)
24062306a36Sopenharmony_ci#define MII_VCT5_TX_PULSE_CTRL_PULSE_AMPLITUDE_750mV	(0x1 << 8)
24162306a36Sopenharmony_ci#define MII_VCT5_TX_PULSE_CTRL_PULSE_AMPLITUDE_500mV	(0x2 << 8)
24262306a36Sopenharmony_ci#define MII_VCT5_TX_PULSE_CTRL_PULSE_AMPLITUDE_250mV	(0x3 << 8)
24362306a36Sopenharmony_ci#define MII_VCT5_TX_PULSE_CTRL_PULSE_AMPLITUDE_SHIFT	8
24462306a36Sopenharmony_ci#define MII_VCT5_TX_PULSE_CTRL_MAX_AMP			BIT(7)
24562306a36Sopenharmony_ci#define MII_VCT5_TX_PULSE_CTRL_GT_140m_46_86mV		(0x6 << 0)
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci/* For TDR measurements less than 11 meters, a short pulse should be
24862306a36Sopenharmony_ci * used.
24962306a36Sopenharmony_ci */
25062306a36Sopenharmony_ci#define TDR_SHORT_CABLE_LENGTH	11
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci#define MII_VCT7_PAIR_0_DISTANCE	0x10
25362306a36Sopenharmony_ci#define MII_VCT7_PAIR_1_DISTANCE	0x11
25462306a36Sopenharmony_ci#define MII_VCT7_PAIR_2_DISTANCE	0x12
25562306a36Sopenharmony_ci#define MII_VCT7_PAIR_3_DISTANCE	0x13
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci#define MII_VCT7_RESULTS	0x14
25862306a36Sopenharmony_ci#define MII_VCT7_RESULTS_PAIR3_MASK	0xf000
25962306a36Sopenharmony_ci#define MII_VCT7_RESULTS_PAIR2_MASK	0x0f00
26062306a36Sopenharmony_ci#define MII_VCT7_RESULTS_PAIR1_MASK	0x00f0
26162306a36Sopenharmony_ci#define MII_VCT7_RESULTS_PAIR0_MASK	0x000f
26262306a36Sopenharmony_ci#define MII_VCT7_RESULTS_PAIR3_SHIFT	12
26362306a36Sopenharmony_ci#define MII_VCT7_RESULTS_PAIR2_SHIFT	8
26462306a36Sopenharmony_ci#define MII_VCT7_RESULTS_PAIR1_SHIFT	4
26562306a36Sopenharmony_ci#define MII_VCT7_RESULTS_PAIR0_SHIFT	0
26662306a36Sopenharmony_ci#define MII_VCT7_RESULTS_INVALID	0
26762306a36Sopenharmony_ci#define MII_VCT7_RESULTS_OK		1
26862306a36Sopenharmony_ci#define MII_VCT7_RESULTS_OPEN		2
26962306a36Sopenharmony_ci#define MII_VCT7_RESULTS_SAME_SHORT	3
27062306a36Sopenharmony_ci#define MII_VCT7_RESULTS_CROSS_SHORT	4
27162306a36Sopenharmony_ci#define MII_VCT7_RESULTS_BUSY		9
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci#define MII_VCT7_CTRL		0x15
27462306a36Sopenharmony_ci#define MII_VCT7_CTRL_RUN_NOW			BIT(15)
27562306a36Sopenharmony_ci#define MII_VCT7_CTRL_RUN_ANEG			BIT(14)
27662306a36Sopenharmony_ci#define MII_VCT7_CTRL_DISABLE_CROSS		BIT(13)
27762306a36Sopenharmony_ci#define MII_VCT7_CTRL_RUN_AFTER_BREAK_LINK	BIT(12)
27862306a36Sopenharmony_ci#define MII_VCT7_CTRL_IN_PROGRESS		BIT(11)
27962306a36Sopenharmony_ci#define MII_VCT7_CTRL_METERS			BIT(10)
28062306a36Sopenharmony_ci#define MII_VCT7_CTRL_CENTIMETERS		0
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci#define LPA_PAUSE_FIBER		0x180
28362306a36Sopenharmony_ci#define LPA_PAUSE_ASYM_FIBER	0x100
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci#define NB_FIBER_STATS	1
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ciMODULE_DESCRIPTION("Marvell PHY driver");
28862306a36Sopenharmony_ciMODULE_AUTHOR("Andy Fleming");
28962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cistruct marvell_hw_stat {
29262306a36Sopenharmony_ci	const char *string;
29362306a36Sopenharmony_ci	u8 page;
29462306a36Sopenharmony_ci	u8 reg;
29562306a36Sopenharmony_ci	u8 bits;
29662306a36Sopenharmony_ci};
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic struct marvell_hw_stat marvell_hw_stats[] = {
29962306a36Sopenharmony_ci	{ "phy_receive_errors_copper", 0, 21, 16},
30062306a36Sopenharmony_ci	{ "phy_idle_errors", 0, 10, 8 },
30162306a36Sopenharmony_ci	{ "phy_receive_errors_fiber", 1, 21, 16},
30262306a36Sopenharmony_ci};
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistruct marvell_priv {
30562306a36Sopenharmony_ci	u64 stats[ARRAY_SIZE(marvell_hw_stats)];
30662306a36Sopenharmony_ci	char *hwmon_name;
30762306a36Sopenharmony_ci	struct device *hwmon_dev;
30862306a36Sopenharmony_ci	bool cable_test_tdr;
30962306a36Sopenharmony_ci	u32 first;
31062306a36Sopenharmony_ci	u32 last;
31162306a36Sopenharmony_ci	u32 step;
31262306a36Sopenharmony_ci	s8 pair;
31362306a36Sopenharmony_ci};
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic int marvell_read_page(struct phy_device *phydev)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	return __phy_read(phydev, MII_MARVELL_PHY_PAGE);
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic int marvell_write_page(struct phy_device *phydev, int page)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	return __phy_write(phydev, MII_MARVELL_PHY_PAGE, page);
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic int marvell_set_page(struct phy_device *phydev, int page)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	return phy_write(phydev, MII_MARVELL_PHY_PAGE, page);
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic int marvell_ack_interrupt(struct phy_device *phydev)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	int err;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	/* Clear the interrupts by reading the reg */
33562306a36Sopenharmony_ci	err = phy_read(phydev, MII_M1011_IEVENT);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	if (err < 0)
33862306a36Sopenharmony_ci		return err;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	return 0;
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic int marvell_config_intr(struct phy_device *phydev)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	int err;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
34862306a36Sopenharmony_ci		err = marvell_ack_interrupt(phydev);
34962306a36Sopenharmony_ci		if (err)
35062306a36Sopenharmony_ci			return err;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci		err = phy_write(phydev, MII_M1011_IMASK,
35362306a36Sopenharmony_ci				MII_M1011_IMASK_INIT);
35462306a36Sopenharmony_ci	} else {
35562306a36Sopenharmony_ci		err = phy_write(phydev, MII_M1011_IMASK,
35662306a36Sopenharmony_ci				MII_M1011_IMASK_CLEAR);
35762306a36Sopenharmony_ci		if (err)
35862306a36Sopenharmony_ci			return err;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci		err = marvell_ack_interrupt(phydev);
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	return err;
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic irqreturn_t marvell_handle_interrupt(struct phy_device *phydev)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	int irq_status;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	irq_status = phy_read(phydev, MII_M1011_IEVENT);
37162306a36Sopenharmony_ci	if (irq_status < 0) {
37262306a36Sopenharmony_ci		phy_error(phydev);
37362306a36Sopenharmony_ci		return IRQ_NONE;
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	if (!(irq_status & MII_M1011_IMASK_INIT))
37762306a36Sopenharmony_ci		return IRQ_NONE;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	phy_trigger_machine(phydev);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	return IRQ_HANDLED;
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cistatic int marvell_set_polarity(struct phy_device *phydev, int polarity)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	u16 val;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	switch (polarity) {
38962306a36Sopenharmony_ci	case ETH_TP_MDI:
39062306a36Sopenharmony_ci		val = MII_M1011_PHY_SCR_MDI;
39162306a36Sopenharmony_ci		break;
39262306a36Sopenharmony_ci	case ETH_TP_MDI_X:
39362306a36Sopenharmony_ci		val = MII_M1011_PHY_SCR_MDI_X;
39462306a36Sopenharmony_ci		break;
39562306a36Sopenharmony_ci	case ETH_TP_MDI_AUTO:
39662306a36Sopenharmony_ci	case ETH_TP_MDI_INVALID:
39762306a36Sopenharmony_ci	default:
39862306a36Sopenharmony_ci		val = MII_M1011_PHY_SCR_AUTO_CROSS;
39962306a36Sopenharmony_ci		break;
40062306a36Sopenharmony_ci	}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	return phy_modify_changed(phydev, MII_M1011_PHY_SCR,
40362306a36Sopenharmony_ci				  MII_M1011_PHY_SCR_AUTO_CROSS, val);
40462306a36Sopenharmony_ci}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_cistatic int marvell_config_aneg(struct phy_device *phydev)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	int changed = 0;
40962306a36Sopenharmony_ci	int err;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
41262306a36Sopenharmony_ci	if (err < 0)
41362306a36Sopenharmony_ci		return err;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	changed = err;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	err = phy_write(phydev, MII_M1111_PHY_LED_CONTROL,
41862306a36Sopenharmony_ci			MII_M1111_PHY_LED_DIRECT);
41962306a36Sopenharmony_ci	if (err < 0)
42062306a36Sopenharmony_ci		return err;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	err = genphy_config_aneg(phydev);
42362306a36Sopenharmony_ci	if (err < 0)
42462306a36Sopenharmony_ci		return err;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	if (phydev->autoneg != AUTONEG_ENABLE || changed) {
42762306a36Sopenharmony_ci		/* A write to speed/duplex bits (that is performed by
42862306a36Sopenharmony_ci		 * genphy_config_aneg() call above) must be followed by
42962306a36Sopenharmony_ci		 * a software reset. Otherwise, the write has no effect.
43062306a36Sopenharmony_ci		 */
43162306a36Sopenharmony_ci		err = genphy_soft_reset(phydev);
43262306a36Sopenharmony_ci		if (err < 0)
43362306a36Sopenharmony_ci			return err;
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	return 0;
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic int m88e1101_config_aneg(struct phy_device *phydev)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	int err;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	/* This Marvell PHY has an errata which requires
44462306a36Sopenharmony_ci	 * that certain registers get written in order
44562306a36Sopenharmony_ci	 * to restart autonegotiation
44662306a36Sopenharmony_ci	 */
44762306a36Sopenharmony_ci	err = genphy_soft_reset(phydev);
44862306a36Sopenharmony_ci	if (err < 0)
44962306a36Sopenharmony_ci		return err;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	err = phy_write(phydev, 0x1d, 0x1f);
45262306a36Sopenharmony_ci	if (err < 0)
45362306a36Sopenharmony_ci		return err;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	err = phy_write(phydev, 0x1e, 0x200c);
45662306a36Sopenharmony_ci	if (err < 0)
45762306a36Sopenharmony_ci		return err;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	err = phy_write(phydev, 0x1d, 0x5);
46062306a36Sopenharmony_ci	if (err < 0)
46162306a36Sopenharmony_ci		return err;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	err = phy_write(phydev, 0x1e, 0);
46462306a36Sopenharmony_ci	if (err < 0)
46562306a36Sopenharmony_ci		return err;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	err = phy_write(phydev, 0x1e, 0x100);
46862306a36Sopenharmony_ci	if (err < 0)
46962306a36Sopenharmony_ci		return err;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	return marvell_config_aneg(phydev);
47262306a36Sopenharmony_ci}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_OF_MDIO)
47562306a36Sopenharmony_ci/* Set and/or override some configuration registers based on the
47662306a36Sopenharmony_ci * marvell,reg-init property stored in the of_node for the phydev.
47762306a36Sopenharmony_ci *
47862306a36Sopenharmony_ci * marvell,reg-init = <reg-page reg mask value>,...;
47962306a36Sopenharmony_ci *
48062306a36Sopenharmony_ci * There may be one or more sets of <reg-page reg mask value>:
48162306a36Sopenharmony_ci *
48262306a36Sopenharmony_ci * reg-page: which register bank to use.
48362306a36Sopenharmony_ci * reg: the register.
48462306a36Sopenharmony_ci * mask: if non-zero, ANDed with existing register value.
48562306a36Sopenharmony_ci * value: ORed with the masked value and written to the regiser.
48662306a36Sopenharmony_ci *
48762306a36Sopenharmony_ci */
48862306a36Sopenharmony_cistatic int marvell_of_reg_init(struct phy_device *phydev)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	const __be32 *paddr;
49162306a36Sopenharmony_ci	int len, i, saved_page, current_page, ret = 0;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	if (!phydev->mdio.dev.of_node)
49462306a36Sopenharmony_ci		return 0;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	paddr = of_get_property(phydev->mdio.dev.of_node,
49762306a36Sopenharmony_ci				"marvell,reg-init", &len);
49862306a36Sopenharmony_ci	if (!paddr || len < (4 * sizeof(*paddr)))
49962306a36Sopenharmony_ci		return 0;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	saved_page = phy_save_page(phydev);
50262306a36Sopenharmony_ci	if (saved_page < 0)
50362306a36Sopenharmony_ci		goto err;
50462306a36Sopenharmony_ci	current_page = saved_page;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	len /= sizeof(*paddr);
50762306a36Sopenharmony_ci	for (i = 0; i < len - 3; i += 4) {
50862306a36Sopenharmony_ci		u16 page = be32_to_cpup(paddr + i);
50962306a36Sopenharmony_ci		u16 reg = be32_to_cpup(paddr + i + 1);
51062306a36Sopenharmony_ci		u16 mask = be32_to_cpup(paddr + i + 2);
51162306a36Sopenharmony_ci		u16 val_bits = be32_to_cpup(paddr + i + 3);
51262306a36Sopenharmony_ci		int val;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci		if (page != current_page) {
51562306a36Sopenharmony_ci			current_page = page;
51662306a36Sopenharmony_ci			ret = marvell_write_page(phydev, page);
51762306a36Sopenharmony_ci			if (ret < 0)
51862306a36Sopenharmony_ci				goto err;
51962306a36Sopenharmony_ci		}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci		val = 0;
52262306a36Sopenharmony_ci		if (mask) {
52362306a36Sopenharmony_ci			val = __phy_read(phydev, reg);
52462306a36Sopenharmony_ci			if (val < 0) {
52562306a36Sopenharmony_ci				ret = val;
52662306a36Sopenharmony_ci				goto err;
52762306a36Sopenharmony_ci			}
52862306a36Sopenharmony_ci			val &= mask;
52962306a36Sopenharmony_ci		}
53062306a36Sopenharmony_ci		val |= val_bits;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci		ret = __phy_write(phydev, reg, val);
53362306a36Sopenharmony_ci		if (ret < 0)
53462306a36Sopenharmony_ci			goto err;
53562306a36Sopenharmony_ci	}
53662306a36Sopenharmony_cierr:
53762306a36Sopenharmony_ci	return phy_restore_page(phydev, saved_page, ret);
53862306a36Sopenharmony_ci}
53962306a36Sopenharmony_ci#else
54062306a36Sopenharmony_cistatic int marvell_of_reg_init(struct phy_device *phydev)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	return 0;
54362306a36Sopenharmony_ci}
54462306a36Sopenharmony_ci#endif /* CONFIG_OF_MDIO */
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_cistatic int m88e1121_config_aneg_rgmii_delays(struct phy_device *phydev)
54762306a36Sopenharmony_ci{
54862306a36Sopenharmony_ci	int mscr;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
55162306a36Sopenharmony_ci		mscr = MII_88E1121_PHY_MSCR_RX_DELAY |
55262306a36Sopenharmony_ci		       MII_88E1121_PHY_MSCR_TX_DELAY;
55362306a36Sopenharmony_ci	else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
55462306a36Sopenharmony_ci		mscr = MII_88E1121_PHY_MSCR_RX_DELAY;
55562306a36Sopenharmony_ci	else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
55662306a36Sopenharmony_ci		mscr = MII_88E1121_PHY_MSCR_TX_DELAY;
55762306a36Sopenharmony_ci	else
55862306a36Sopenharmony_ci		mscr = 0;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	return phy_modify_paged_changed(phydev, MII_MARVELL_MSCR_PAGE,
56162306a36Sopenharmony_ci					MII_88E1121_PHY_MSCR_REG,
56262306a36Sopenharmony_ci					MII_88E1121_PHY_MSCR_DELAY_MASK, mscr);
56362306a36Sopenharmony_ci}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_cistatic int m88e1121_config_aneg(struct phy_device *phydev)
56662306a36Sopenharmony_ci{
56762306a36Sopenharmony_ci	int changed = 0;
56862306a36Sopenharmony_ci	int err = 0;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	if (phy_interface_is_rgmii(phydev)) {
57162306a36Sopenharmony_ci		err = m88e1121_config_aneg_rgmii_delays(phydev);
57262306a36Sopenharmony_ci		if (err < 0)
57362306a36Sopenharmony_ci			return err;
57462306a36Sopenharmony_ci	}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	changed = err;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
57962306a36Sopenharmony_ci	if (err < 0)
58062306a36Sopenharmony_ci		return err;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	changed |= err;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	err = genphy_config_aneg(phydev);
58562306a36Sopenharmony_ci	if (err < 0)
58662306a36Sopenharmony_ci		return err;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	if (phydev->autoneg != AUTONEG_ENABLE || changed) {
58962306a36Sopenharmony_ci		/* A software reset is used to ensure a "commit" of the
59062306a36Sopenharmony_ci		 * changes is done.
59162306a36Sopenharmony_ci		 */
59262306a36Sopenharmony_ci		err = genphy_soft_reset(phydev);
59362306a36Sopenharmony_ci		if (err < 0)
59462306a36Sopenharmony_ci			return err;
59562306a36Sopenharmony_ci	}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	return 0;
59862306a36Sopenharmony_ci}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_cistatic int m88e1318_config_aneg(struct phy_device *phydev)
60162306a36Sopenharmony_ci{
60262306a36Sopenharmony_ci	int err;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	err = phy_modify_paged(phydev, MII_MARVELL_MSCR_PAGE,
60562306a36Sopenharmony_ci			       MII_88E1318S_PHY_MSCR1_REG,
60662306a36Sopenharmony_ci			       0, MII_88E1318S_PHY_MSCR1_PAD_ODD);
60762306a36Sopenharmony_ci	if (err < 0)
60862306a36Sopenharmony_ci		return err;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	return m88e1121_config_aneg(phydev);
61162306a36Sopenharmony_ci}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci/**
61462306a36Sopenharmony_ci * linkmode_adv_to_fiber_adv_t
61562306a36Sopenharmony_ci * @advertise: the linkmode advertisement settings
61662306a36Sopenharmony_ci *
61762306a36Sopenharmony_ci * A small helper function that translates linkmode advertisement
61862306a36Sopenharmony_ci * settings to phy autonegotiation advertisements for the MII_ADV
61962306a36Sopenharmony_ci * register for fiber link.
62062306a36Sopenharmony_ci */
62162306a36Sopenharmony_cistatic inline u32 linkmode_adv_to_fiber_adv_t(unsigned long *advertise)
62262306a36Sopenharmony_ci{
62362306a36Sopenharmony_ci	u32 result = 0;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, advertise))
62662306a36Sopenharmony_ci		result |= ADVERTISE_1000XHALF;
62762306a36Sopenharmony_ci	if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, advertise))
62862306a36Sopenharmony_ci		result |= ADVERTISE_1000XFULL;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, advertise) &&
63162306a36Sopenharmony_ci	    linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertise))
63262306a36Sopenharmony_ci		result |= ADVERTISE_1000XPSE_ASYM;
63362306a36Sopenharmony_ci	else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertise))
63462306a36Sopenharmony_ci		result |= ADVERTISE_1000XPAUSE;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	return result;
63762306a36Sopenharmony_ci}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci/**
64062306a36Sopenharmony_ci * marvell_config_aneg_fiber - restart auto-negotiation or write BMCR
64162306a36Sopenharmony_ci * @phydev: target phy_device struct
64262306a36Sopenharmony_ci *
64362306a36Sopenharmony_ci * Description: If auto-negotiation is enabled, we configure the
64462306a36Sopenharmony_ci *   advertising, and then restart auto-negotiation.  If it is not
64562306a36Sopenharmony_ci *   enabled, then we write the BMCR. Adapted for fiber link in
64662306a36Sopenharmony_ci *   some Marvell's devices.
64762306a36Sopenharmony_ci */
64862306a36Sopenharmony_cistatic int marvell_config_aneg_fiber(struct phy_device *phydev)
64962306a36Sopenharmony_ci{
65062306a36Sopenharmony_ci	int changed = 0;
65162306a36Sopenharmony_ci	int err;
65262306a36Sopenharmony_ci	u16 adv;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	if (phydev->autoneg != AUTONEG_ENABLE)
65562306a36Sopenharmony_ci		return genphy_setup_forced(phydev);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	/* Only allow advertising what this PHY supports */
65862306a36Sopenharmony_ci	linkmode_and(phydev->advertising, phydev->advertising,
65962306a36Sopenharmony_ci		     phydev->supported);
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	adv = linkmode_adv_to_fiber_adv_t(phydev->advertising);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	/* Setup fiber advertisement */
66462306a36Sopenharmony_ci	err = phy_modify_changed(phydev, MII_ADVERTISE,
66562306a36Sopenharmony_ci				 ADVERTISE_1000XHALF | ADVERTISE_1000XFULL |
66662306a36Sopenharmony_ci				 ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM,
66762306a36Sopenharmony_ci				 adv);
66862306a36Sopenharmony_ci	if (err < 0)
66962306a36Sopenharmony_ci		return err;
67062306a36Sopenharmony_ci	if (err > 0)
67162306a36Sopenharmony_ci		changed = 1;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	return genphy_check_and_restart_aneg(phydev, changed);
67462306a36Sopenharmony_ci}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_cistatic int m88e1111_config_aneg(struct phy_device *phydev)
67762306a36Sopenharmony_ci{
67862306a36Sopenharmony_ci	int extsr = phy_read(phydev, MII_M1111_PHY_EXT_SR);
67962306a36Sopenharmony_ci	int err;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	if (extsr < 0)
68262306a36Sopenharmony_ci		return extsr;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	/* If not using SGMII or copper 1000BaseX modes, use normal process.
68562306a36Sopenharmony_ci	 * Steps below are only required for these modes.
68662306a36Sopenharmony_ci	 */
68762306a36Sopenharmony_ci	if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
68862306a36Sopenharmony_ci	    (extsr & MII_M1111_HWCFG_MODE_MASK) !=
68962306a36Sopenharmony_ci	    MII_M1111_HWCFG_MODE_COPPER_1000X_AN)
69062306a36Sopenharmony_ci		return marvell_config_aneg(phydev);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
69362306a36Sopenharmony_ci	if (err < 0)
69462306a36Sopenharmony_ci		goto error;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	/* Configure the copper link first */
69762306a36Sopenharmony_ci	err = marvell_config_aneg(phydev);
69862306a36Sopenharmony_ci	if (err < 0)
69962306a36Sopenharmony_ci		goto error;
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	/* Then the fiber link */
70262306a36Sopenharmony_ci	err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
70362306a36Sopenharmony_ci	if (err < 0)
70462306a36Sopenharmony_ci		goto error;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	if (phydev->interface == PHY_INTERFACE_MODE_SGMII)
70762306a36Sopenharmony_ci		/* Do not touch the fiber advertisement if we're in copper->sgmii mode.
70862306a36Sopenharmony_ci		 * Just ensure that SGMII-side autonegotiation is enabled.
70962306a36Sopenharmony_ci		 * If we switched from some other mode to SGMII it may not be.
71062306a36Sopenharmony_ci		 */
71162306a36Sopenharmony_ci		err = genphy_check_and_restart_aneg(phydev, false);
71262306a36Sopenharmony_ci	else
71362306a36Sopenharmony_ci		err = marvell_config_aneg_fiber(phydev);
71462306a36Sopenharmony_ci	if (err < 0)
71562306a36Sopenharmony_ci		goto error;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	return marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_cierror:
72062306a36Sopenharmony_ci	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
72162306a36Sopenharmony_ci	return err;
72262306a36Sopenharmony_ci}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_cistatic int m88e1510_config_aneg(struct phy_device *phydev)
72562306a36Sopenharmony_ci{
72662306a36Sopenharmony_ci	int err;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
72962306a36Sopenharmony_ci	if (err < 0)
73062306a36Sopenharmony_ci		goto error;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	/* Configure the copper link first */
73362306a36Sopenharmony_ci	err = m88e1318_config_aneg(phydev);
73462306a36Sopenharmony_ci	if (err < 0)
73562306a36Sopenharmony_ci		goto error;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	/* Do not touch the fiber page if we're in copper->sgmii mode */
73862306a36Sopenharmony_ci	if (phydev->interface == PHY_INTERFACE_MODE_SGMII)
73962306a36Sopenharmony_ci		return 0;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	/* Then the fiber link */
74262306a36Sopenharmony_ci	err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
74362306a36Sopenharmony_ci	if (err < 0)
74462306a36Sopenharmony_ci		goto error;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	err = marvell_config_aneg_fiber(phydev);
74762306a36Sopenharmony_ci	if (err < 0)
74862306a36Sopenharmony_ci		goto error;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	return marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_cierror:
75362306a36Sopenharmony_ci	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
75462306a36Sopenharmony_ci	return err;
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_cistatic void marvell_config_led(struct phy_device *phydev)
75862306a36Sopenharmony_ci{
75962306a36Sopenharmony_ci	u16 def_config;
76062306a36Sopenharmony_ci	int err;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	switch (MARVELL_PHY_FAMILY_ID(phydev->phy_id)) {
76362306a36Sopenharmony_ci	/* Default PHY LED config: LED[0] .. Link, LED[1] .. Activity */
76462306a36Sopenharmony_ci	case MARVELL_PHY_FAMILY_ID(MARVELL_PHY_ID_88E1121R):
76562306a36Sopenharmony_ci	case MARVELL_PHY_FAMILY_ID(MARVELL_PHY_ID_88E1318S):
76662306a36Sopenharmony_ci		def_config = MII_88E1121_PHY_LED_DEF;
76762306a36Sopenharmony_ci		break;
76862306a36Sopenharmony_ci	/* Default PHY LED config:
76962306a36Sopenharmony_ci	 * LED[0] .. 1000Mbps Link
77062306a36Sopenharmony_ci	 * LED[1] .. 100Mbps Link
77162306a36Sopenharmony_ci	 * LED[2] .. Blink, Activity
77262306a36Sopenharmony_ci	 */
77362306a36Sopenharmony_ci	case MARVELL_PHY_FAMILY_ID(MARVELL_PHY_ID_88E1510):
77462306a36Sopenharmony_ci		if (phydev->dev_flags & MARVELL_PHY_LED0_LINK_LED1_ACTIVE)
77562306a36Sopenharmony_ci			def_config = MII_88E1510_PHY_LED0_LINK_LED1_ACTIVE;
77662306a36Sopenharmony_ci		else
77762306a36Sopenharmony_ci			def_config = MII_88E1510_PHY_LED_DEF;
77862306a36Sopenharmony_ci		break;
77962306a36Sopenharmony_ci	default:
78062306a36Sopenharmony_ci		return;
78162306a36Sopenharmony_ci	}
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	err = phy_write_paged(phydev, MII_MARVELL_LED_PAGE, MII_PHY_LED_CTRL,
78462306a36Sopenharmony_ci			      def_config);
78562306a36Sopenharmony_ci	if (err < 0)
78662306a36Sopenharmony_ci		phydev_warn(phydev, "Fail to config marvell phy LED.\n");
78762306a36Sopenharmony_ci}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_cistatic int marvell_config_init(struct phy_device *phydev)
79062306a36Sopenharmony_ci{
79162306a36Sopenharmony_ci	/* Set default LED */
79262306a36Sopenharmony_ci	marvell_config_led(phydev);
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	/* Set registers from marvell,reg-init DT property */
79562306a36Sopenharmony_ci	return marvell_of_reg_init(phydev);
79662306a36Sopenharmony_ci}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_cistatic int m88e3016_config_init(struct phy_device *phydev)
79962306a36Sopenharmony_ci{
80062306a36Sopenharmony_ci	int ret;
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	/* Enable Scrambler and Auto-Crossover */
80362306a36Sopenharmony_ci	ret = phy_modify(phydev, MII_88E3016_PHY_SPEC_CTRL,
80462306a36Sopenharmony_ci			 MII_88E3016_DISABLE_SCRAMBLER,
80562306a36Sopenharmony_ci			 MII_88E3016_AUTO_MDIX_CROSSOVER);
80662306a36Sopenharmony_ci	if (ret < 0)
80762306a36Sopenharmony_ci		return ret;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	return marvell_config_init(phydev);
81062306a36Sopenharmony_ci}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_cistatic int m88e1111_config_init_hwcfg_mode(struct phy_device *phydev,
81362306a36Sopenharmony_ci					   u16 mode,
81462306a36Sopenharmony_ci					   int fibre_copper_auto)
81562306a36Sopenharmony_ci{
81662306a36Sopenharmony_ci	if (fibre_copper_auto)
81762306a36Sopenharmony_ci		mode |= MII_M1111_HWCFG_FIBER_COPPER_AUTO;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	return phy_modify(phydev, MII_M1111_PHY_EXT_SR,
82062306a36Sopenharmony_ci			  MII_M1111_HWCFG_MODE_MASK |
82162306a36Sopenharmony_ci			  MII_M1111_HWCFG_FIBER_COPPER_AUTO |
82262306a36Sopenharmony_ci			  MII_M1111_HWCFG_FIBER_COPPER_RES,
82362306a36Sopenharmony_ci			  mode);
82462306a36Sopenharmony_ci}
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_cistatic int m88e1111_config_init_rgmii_delays(struct phy_device *phydev)
82762306a36Sopenharmony_ci{
82862306a36Sopenharmony_ci	int delay;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	switch (phydev->interface) {
83162306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII_ID:
83262306a36Sopenharmony_ci		delay = MII_M1111_RGMII_RX_DELAY | MII_M1111_RGMII_TX_DELAY;
83362306a36Sopenharmony_ci		break;
83462306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII_RXID:
83562306a36Sopenharmony_ci		delay = MII_M1111_RGMII_RX_DELAY;
83662306a36Sopenharmony_ci		break;
83762306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII_TXID:
83862306a36Sopenharmony_ci		delay = MII_M1111_RGMII_TX_DELAY;
83962306a36Sopenharmony_ci		break;
84062306a36Sopenharmony_ci	default:
84162306a36Sopenharmony_ci		delay = 0;
84262306a36Sopenharmony_ci		break;
84362306a36Sopenharmony_ci	}
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	return phy_modify(phydev, MII_M1111_PHY_EXT_CR,
84662306a36Sopenharmony_ci			  MII_M1111_RGMII_RX_DELAY | MII_M1111_RGMII_TX_DELAY,
84762306a36Sopenharmony_ci			  delay);
84862306a36Sopenharmony_ci}
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_cistatic int m88e1111_config_init_rgmii(struct phy_device *phydev)
85162306a36Sopenharmony_ci{
85262306a36Sopenharmony_ci	int temp;
85362306a36Sopenharmony_ci	int err;
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	err = m88e1111_config_init_rgmii_delays(phydev);
85662306a36Sopenharmony_ci	if (err < 0)
85762306a36Sopenharmony_ci		return err;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
86062306a36Sopenharmony_ci	if (temp < 0)
86162306a36Sopenharmony_ci		return temp;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	temp &= ~(MII_M1111_HWCFG_MODE_MASK);
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	if (temp & MII_M1111_HWCFG_FIBER_COPPER_RES)
86662306a36Sopenharmony_ci		temp |= MII_M1111_HWCFG_MODE_FIBER_RGMII;
86762306a36Sopenharmony_ci	else
86862306a36Sopenharmony_ci		temp |= MII_M1111_HWCFG_MODE_COPPER_RGMII;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	return phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
87162306a36Sopenharmony_ci}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_cistatic int m88e1111_config_init_sgmii(struct phy_device *phydev)
87462306a36Sopenharmony_ci{
87562306a36Sopenharmony_ci	int err;
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	err = m88e1111_config_init_hwcfg_mode(
87862306a36Sopenharmony_ci		phydev,
87962306a36Sopenharmony_ci		MII_M1111_HWCFG_MODE_SGMII_NO_CLK,
88062306a36Sopenharmony_ci		MII_M1111_HWCFG_FIBER_COPPER_AUTO);
88162306a36Sopenharmony_ci	if (err < 0)
88262306a36Sopenharmony_ci		return err;
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	/* make sure copper is selected */
88562306a36Sopenharmony_ci	return marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
88662306a36Sopenharmony_ci}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_cistatic int m88e1111_config_init_rtbi(struct phy_device *phydev)
88962306a36Sopenharmony_ci{
89062306a36Sopenharmony_ci	int err;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	err = m88e1111_config_init_rgmii_delays(phydev);
89362306a36Sopenharmony_ci	if (err < 0)
89462306a36Sopenharmony_ci		return err;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	err = m88e1111_config_init_hwcfg_mode(
89762306a36Sopenharmony_ci		phydev,
89862306a36Sopenharmony_ci		MII_M1111_HWCFG_MODE_RTBI,
89962306a36Sopenharmony_ci		MII_M1111_HWCFG_FIBER_COPPER_AUTO);
90062306a36Sopenharmony_ci	if (err < 0)
90162306a36Sopenharmony_ci		return err;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	/* soft reset */
90462306a36Sopenharmony_ci	err = genphy_soft_reset(phydev);
90562306a36Sopenharmony_ci	if (err < 0)
90662306a36Sopenharmony_ci		return err;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	return m88e1111_config_init_hwcfg_mode(
90962306a36Sopenharmony_ci		phydev,
91062306a36Sopenharmony_ci		MII_M1111_HWCFG_MODE_RTBI,
91162306a36Sopenharmony_ci		MII_M1111_HWCFG_FIBER_COPPER_AUTO);
91262306a36Sopenharmony_ci}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_cistatic int m88e1111_config_init_1000basex(struct phy_device *phydev)
91562306a36Sopenharmony_ci{
91662306a36Sopenharmony_ci	int extsr = phy_read(phydev, MII_M1111_PHY_EXT_SR);
91762306a36Sopenharmony_ci	int err, mode;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	if (extsr < 0)
92062306a36Sopenharmony_ci		return extsr;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	/* If using copper mode, ensure 1000BaseX auto-negotiation is enabled */
92362306a36Sopenharmony_ci	mode = extsr & MII_M1111_HWCFG_MODE_MASK;
92462306a36Sopenharmony_ci	if (mode == MII_M1111_HWCFG_MODE_COPPER_1000X_NOAN) {
92562306a36Sopenharmony_ci		err = phy_modify(phydev, MII_M1111_PHY_EXT_SR,
92662306a36Sopenharmony_ci				 MII_M1111_HWCFG_MODE_MASK |
92762306a36Sopenharmony_ci				 MII_M1111_HWCFG_SERIAL_AN_BYPASS,
92862306a36Sopenharmony_ci				 MII_M1111_HWCFG_MODE_COPPER_1000X_AN |
92962306a36Sopenharmony_ci				 MII_M1111_HWCFG_SERIAL_AN_BYPASS);
93062306a36Sopenharmony_ci		if (err < 0)
93162306a36Sopenharmony_ci			return err;
93262306a36Sopenharmony_ci	}
93362306a36Sopenharmony_ci	return 0;
93462306a36Sopenharmony_ci}
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_cistatic int m88e1111_config_init(struct phy_device *phydev)
93762306a36Sopenharmony_ci{
93862306a36Sopenharmony_ci	int err;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	if (phy_interface_is_rgmii(phydev)) {
94162306a36Sopenharmony_ci		err = m88e1111_config_init_rgmii(phydev);
94262306a36Sopenharmony_ci		if (err < 0)
94362306a36Sopenharmony_ci			return err;
94462306a36Sopenharmony_ci	}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
94762306a36Sopenharmony_ci		err = m88e1111_config_init_sgmii(phydev);
94862306a36Sopenharmony_ci		if (err < 0)
94962306a36Sopenharmony_ci			return err;
95062306a36Sopenharmony_ci	}
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	if (phydev->interface == PHY_INTERFACE_MODE_RTBI) {
95362306a36Sopenharmony_ci		err = m88e1111_config_init_rtbi(phydev);
95462306a36Sopenharmony_ci		if (err < 0)
95562306a36Sopenharmony_ci			return err;
95662306a36Sopenharmony_ci	}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	if (phydev->interface == PHY_INTERFACE_MODE_1000BASEX) {
95962306a36Sopenharmony_ci		err = m88e1111_config_init_1000basex(phydev);
96062306a36Sopenharmony_ci		if (err < 0)
96162306a36Sopenharmony_ci			return err;
96262306a36Sopenharmony_ci	}
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	err = marvell_of_reg_init(phydev);
96562306a36Sopenharmony_ci	if (err < 0)
96662306a36Sopenharmony_ci		return err;
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	err = genphy_soft_reset(phydev);
96962306a36Sopenharmony_ci	if (err < 0)
97062306a36Sopenharmony_ci		return err;
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
97362306a36Sopenharmony_ci		/* If the HWCFG_MODE was changed from another mode (such as
97462306a36Sopenharmony_ci		 * 1000BaseX) to SGMII, the state of the support bits may have
97562306a36Sopenharmony_ci		 * also changed now that the PHY has been reset.
97662306a36Sopenharmony_ci		 * Update the PHY abilities accordingly.
97762306a36Sopenharmony_ci		 */
97862306a36Sopenharmony_ci		err = genphy_read_abilities(phydev);
97962306a36Sopenharmony_ci		linkmode_or(phydev->advertising, phydev->advertising,
98062306a36Sopenharmony_ci			    phydev->supported);
98162306a36Sopenharmony_ci	}
98262306a36Sopenharmony_ci	return err;
98362306a36Sopenharmony_ci}
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_cistatic int m88e1111_get_downshift(struct phy_device *phydev, u8 *data)
98662306a36Sopenharmony_ci{
98762306a36Sopenharmony_ci	int val, cnt, enable;
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	val = phy_read(phydev, MII_M1111_PHY_EXT_CR);
99062306a36Sopenharmony_ci	if (val < 0)
99162306a36Sopenharmony_ci		return val;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	enable = FIELD_GET(MII_M1111_PHY_EXT_CR_DOWNSHIFT_EN, val);
99462306a36Sopenharmony_ci	cnt = FIELD_GET(MII_M1111_PHY_EXT_CR_DOWNSHIFT_MASK, val) + 1;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	*data = enable ? cnt : DOWNSHIFT_DEV_DISABLE;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	return 0;
99962306a36Sopenharmony_ci}
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_cistatic int m88e1111_set_downshift(struct phy_device *phydev, u8 cnt)
100262306a36Sopenharmony_ci{
100362306a36Sopenharmony_ci	int val, err;
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	if (cnt > MII_M1111_PHY_EXT_CR_DOWNSHIFT_MAX)
100662306a36Sopenharmony_ci		return -E2BIG;
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	if (!cnt) {
100962306a36Sopenharmony_ci		err = phy_clear_bits(phydev, MII_M1111_PHY_EXT_CR,
101062306a36Sopenharmony_ci				     MII_M1111_PHY_EXT_CR_DOWNSHIFT_EN);
101162306a36Sopenharmony_ci	} else {
101262306a36Sopenharmony_ci		val = MII_M1111_PHY_EXT_CR_DOWNSHIFT_EN;
101362306a36Sopenharmony_ci		val |= FIELD_PREP(MII_M1111_PHY_EXT_CR_DOWNSHIFT_MASK, cnt - 1);
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci		err = phy_modify(phydev, MII_M1111_PHY_EXT_CR,
101662306a36Sopenharmony_ci				 MII_M1111_PHY_EXT_CR_DOWNSHIFT_EN |
101762306a36Sopenharmony_ci				 MII_M1111_PHY_EXT_CR_DOWNSHIFT_MASK,
101862306a36Sopenharmony_ci				 val);
101962306a36Sopenharmony_ci	}
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	if (err < 0)
102262306a36Sopenharmony_ci		return err;
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	return genphy_soft_reset(phydev);
102562306a36Sopenharmony_ci}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_cistatic int m88e1111_get_tunable(struct phy_device *phydev,
102862306a36Sopenharmony_ci				struct ethtool_tunable *tuna, void *data)
102962306a36Sopenharmony_ci{
103062306a36Sopenharmony_ci	switch (tuna->id) {
103162306a36Sopenharmony_ci	case ETHTOOL_PHY_DOWNSHIFT:
103262306a36Sopenharmony_ci		return m88e1111_get_downshift(phydev, data);
103362306a36Sopenharmony_ci	default:
103462306a36Sopenharmony_ci		return -EOPNOTSUPP;
103562306a36Sopenharmony_ci	}
103662306a36Sopenharmony_ci}
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_cistatic int m88e1111_set_tunable(struct phy_device *phydev,
103962306a36Sopenharmony_ci				struct ethtool_tunable *tuna, const void *data)
104062306a36Sopenharmony_ci{
104162306a36Sopenharmony_ci	switch (tuna->id) {
104262306a36Sopenharmony_ci	case ETHTOOL_PHY_DOWNSHIFT:
104362306a36Sopenharmony_ci		return m88e1111_set_downshift(phydev, *(const u8 *)data);
104462306a36Sopenharmony_ci	default:
104562306a36Sopenharmony_ci		return -EOPNOTSUPP;
104662306a36Sopenharmony_ci	}
104762306a36Sopenharmony_ci}
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_cistatic int m88e1011_get_downshift(struct phy_device *phydev, u8 *data)
105062306a36Sopenharmony_ci{
105162306a36Sopenharmony_ci	int val, cnt, enable;
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	val = phy_read(phydev, MII_M1011_PHY_SCR);
105462306a36Sopenharmony_ci	if (val < 0)
105562306a36Sopenharmony_ci		return val;
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	enable = FIELD_GET(MII_M1011_PHY_SCR_DOWNSHIFT_EN, val);
105862306a36Sopenharmony_ci	cnt = FIELD_GET(MII_M1011_PHY_SCR_DOWNSHIFT_MASK, val) + 1;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	*data = enable ? cnt : DOWNSHIFT_DEV_DISABLE;
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	return 0;
106362306a36Sopenharmony_ci}
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_cistatic int m88e1011_set_downshift(struct phy_device *phydev, u8 cnt)
106662306a36Sopenharmony_ci{
106762306a36Sopenharmony_ci	int val, err;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	if (cnt > MII_M1011_PHY_SCR_DOWNSHIFT_MAX)
107062306a36Sopenharmony_ci		return -E2BIG;
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	if (!cnt) {
107362306a36Sopenharmony_ci		err = phy_clear_bits(phydev, MII_M1011_PHY_SCR,
107462306a36Sopenharmony_ci				     MII_M1011_PHY_SCR_DOWNSHIFT_EN);
107562306a36Sopenharmony_ci	} else {
107662306a36Sopenharmony_ci		val = MII_M1011_PHY_SCR_DOWNSHIFT_EN;
107762306a36Sopenharmony_ci		val |= FIELD_PREP(MII_M1011_PHY_SCR_DOWNSHIFT_MASK, cnt - 1);
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci		err = phy_modify(phydev, MII_M1011_PHY_SCR,
108062306a36Sopenharmony_ci				 MII_M1011_PHY_SCR_DOWNSHIFT_EN |
108162306a36Sopenharmony_ci				 MII_M1011_PHY_SCR_DOWNSHIFT_MASK,
108262306a36Sopenharmony_ci				 val);
108362306a36Sopenharmony_ci	}
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	if (err < 0)
108662306a36Sopenharmony_ci		return err;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	return genphy_soft_reset(phydev);
108962306a36Sopenharmony_ci}
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_cistatic int m88e1011_get_tunable(struct phy_device *phydev,
109262306a36Sopenharmony_ci				struct ethtool_tunable *tuna, void *data)
109362306a36Sopenharmony_ci{
109462306a36Sopenharmony_ci	switch (tuna->id) {
109562306a36Sopenharmony_ci	case ETHTOOL_PHY_DOWNSHIFT:
109662306a36Sopenharmony_ci		return m88e1011_get_downshift(phydev, data);
109762306a36Sopenharmony_ci	default:
109862306a36Sopenharmony_ci		return -EOPNOTSUPP;
109962306a36Sopenharmony_ci	}
110062306a36Sopenharmony_ci}
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_cistatic int m88e1011_set_tunable(struct phy_device *phydev,
110362306a36Sopenharmony_ci				struct ethtool_tunable *tuna, const void *data)
110462306a36Sopenharmony_ci{
110562306a36Sopenharmony_ci	switch (tuna->id) {
110662306a36Sopenharmony_ci	case ETHTOOL_PHY_DOWNSHIFT:
110762306a36Sopenharmony_ci		return m88e1011_set_downshift(phydev, *(const u8 *)data);
110862306a36Sopenharmony_ci	default:
110962306a36Sopenharmony_ci		return -EOPNOTSUPP;
111062306a36Sopenharmony_ci	}
111162306a36Sopenharmony_ci}
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_cistatic int m88e1112_config_init(struct phy_device *phydev)
111462306a36Sopenharmony_ci{
111562306a36Sopenharmony_ci	int err;
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	err = m88e1011_set_downshift(phydev, 3);
111862306a36Sopenharmony_ci	if (err < 0)
111962306a36Sopenharmony_ci		return err;
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	return m88e1111_config_init(phydev);
112262306a36Sopenharmony_ci}
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_cistatic int m88e1111gbe_config_init(struct phy_device *phydev)
112562306a36Sopenharmony_ci{
112662306a36Sopenharmony_ci	int err;
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	err = m88e1111_set_downshift(phydev, 3);
112962306a36Sopenharmony_ci	if (err < 0)
113062306a36Sopenharmony_ci		return err;
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	return m88e1111_config_init(phydev);
113362306a36Sopenharmony_ci}
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_cistatic int marvell_1011gbe_config_init(struct phy_device *phydev)
113662306a36Sopenharmony_ci{
113762306a36Sopenharmony_ci	int err;
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	err = m88e1011_set_downshift(phydev, 3);
114062306a36Sopenharmony_ci	if (err < 0)
114162306a36Sopenharmony_ci		return err;
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	return marvell_config_init(phydev);
114462306a36Sopenharmony_ci}
114562306a36Sopenharmony_cistatic int m88e1116r_config_init(struct phy_device *phydev)
114662306a36Sopenharmony_ci{
114762306a36Sopenharmony_ci	int err;
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	err = genphy_soft_reset(phydev);
115062306a36Sopenharmony_ci	if (err < 0)
115162306a36Sopenharmony_ci		return err;
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	msleep(500);
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
115662306a36Sopenharmony_ci	if (err < 0)
115762306a36Sopenharmony_ci		return err;
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
116062306a36Sopenharmony_ci	if (err < 0)
116162306a36Sopenharmony_ci		return err;
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	err = m88e1011_set_downshift(phydev, 8);
116462306a36Sopenharmony_ci	if (err < 0)
116562306a36Sopenharmony_ci		return err;
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	if (phy_interface_is_rgmii(phydev)) {
116862306a36Sopenharmony_ci		err = m88e1121_config_aneg_rgmii_delays(phydev);
116962306a36Sopenharmony_ci		if (err < 0)
117062306a36Sopenharmony_ci			return err;
117162306a36Sopenharmony_ci	}
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	err = genphy_soft_reset(phydev);
117462306a36Sopenharmony_ci	if (err < 0)
117562306a36Sopenharmony_ci		return err;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	return marvell_config_init(phydev);
117862306a36Sopenharmony_ci}
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_cistatic int m88e1318_config_init(struct phy_device *phydev)
118162306a36Sopenharmony_ci{
118262306a36Sopenharmony_ci	if (phy_interrupt_is_valid(phydev)) {
118362306a36Sopenharmony_ci		int err = phy_modify_paged(
118462306a36Sopenharmony_ci			phydev, MII_MARVELL_LED_PAGE,
118562306a36Sopenharmony_ci			MII_88E1318S_PHY_LED_TCR,
118662306a36Sopenharmony_ci			MII_88E1318S_PHY_LED_TCR_FORCE_INT,
118762306a36Sopenharmony_ci			MII_88E1318S_PHY_LED_TCR_INTn_ENABLE |
118862306a36Sopenharmony_ci			MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW);
118962306a36Sopenharmony_ci		if (err < 0)
119062306a36Sopenharmony_ci			return err;
119162306a36Sopenharmony_ci	}
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	return marvell_config_init(phydev);
119462306a36Sopenharmony_ci}
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_cistatic int m88e1510_config_init(struct phy_device *phydev)
119762306a36Sopenharmony_ci{
119862306a36Sopenharmony_ci	static const struct {
119962306a36Sopenharmony_ci		u16 reg17, reg16;
120062306a36Sopenharmony_ci	} errata_vals[] = {
120162306a36Sopenharmony_ci		{ 0x214b, 0x2144 },
120262306a36Sopenharmony_ci		{ 0x0c28, 0x2146 },
120362306a36Sopenharmony_ci		{ 0xb233, 0x214d },
120462306a36Sopenharmony_ci		{ 0xcc0c, 0x2159 },
120562306a36Sopenharmony_ci	};
120662306a36Sopenharmony_ci	int err;
120762306a36Sopenharmony_ci	int i;
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	/* As per Marvell Release Notes - Alaska 88E1510/88E1518/88E1512/
121062306a36Sopenharmony_ci	 * 88E1514 Rev A0, Errata Section 5.1:
121162306a36Sopenharmony_ci	 * If EEE is intended to be used, the following register writes
121262306a36Sopenharmony_ci	 * must be done once after every hardware reset.
121362306a36Sopenharmony_ci	 */
121462306a36Sopenharmony_ci	err = marvell_set_page(phydev, 0x00FF);
121562306a36Sopenharmony_ci	if (err < 0)
121662306a36Sopenharmony_ci		return err;
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(errata_vals); ++i) {
121962306a36Sopenharmony_ci		err = phy_write(phydev, 17, errata_vals[i].reg17);
122062306a36Sopenharmony_ci		if (err)
122162306a36Sopenharmony_ci			return err;
122262306a36Sopenharmony_ci		err = phy_write(phydev, 16, errata_vals[i].reg16);
122362306a36Sopenharmony_ci		if (err)
122462306a36Sopenharmony_ci			return err;
122562306a36Sopenharmony_ci	}
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	err = marvell_set_page(phydev, 0x00FB);
122862306a36Sopenharmony_ci	if (err < 0)
122962306a36Sopenharmony_ci		return err;
123062306a36Sopenharmony_ci	err = phy_write(phydev, 07, 0xC00D);
123162306a36Sopenharmony_ci	if (err < 0)
123262306a36Sopenharmony_ci		return err;
123362306a36Sopenharmony_ci	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
123462306a36Sopenharmony_ci	if (err < 0)
123562306a36Sopenharmony_ci		return err;
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	/* SGMII-to-Copper mode initialization */
123862306a36Sopenharmony_ci	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
123962306a36Sopenharmony_ci		/* Select page 18 */
124062306a36Sopenharmony_ci		err = marvell_set_page(phydev, 18);
124162306a36Sopenharmony_ci		if (err < 0)
124262306a36Sopenharmony_ci			return err;
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci		/* In reg 20, write MODE[2:0] = 0x1 (SGMII to Copper) */
124562306a36Sopenharmony_ci		err = phy_modify(phydev, MII_88E1510_GEN_CTRL_REG_1,
124662306a36Sopenharmony_ci				 MII_88E1510_GEN_CTRL_REG_1_MODE_MASK,
124762306a36Sopenharmony_ci				 MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII);
124862306a36Sopenharmony_ci		if (err < 0)
124962306a36Sopenharmony_ci			return err;
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci		/* PHY reset is necessary after changing MODE[2:0] */
125262306a36Sopenharmony_ci		err = phy_set_bits(phydev, MII_88E1510_GEN_CTRL_REG_1,
125362306a36Sopenharmony_ci				   MII_88E1510_GEN_CTRL_REG_1_RESET);
125462306a36Sopenharmony_ci		if (err < 0)
125562306a36Sopenharmony_ci			return err;
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci		/* Reset page selection */
125862306a36Sopenharmony_ci		err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
125962306a36Sopenharmony_ci		if (err < 0)
126062306a36Sopenharmony_ci			return err;
126162306a36Sopenharmony_ci	}
126262306a36Sopenharmony_ci	err = m88e1011_set_downshift(phydev, 3);
126362306a36Sopenharmony_ci	if (err < 0)
126462306a36Sopenharmony_ci		return err;
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	return m88e1318_config_init(phydev);
126762306a36Sopenharmony_ci}
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_cistatic int m88e1118_config_aneg(struct phy_device *phydev)
127062306a36Sopenharmony_ci{
127162306a36Sopenharmony_ci	int err;
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
127462306a36Sopenharmony_ci	if (err < 0)
127562306a36Sopenharmony_ci		return err;
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	err = genphy_config_aneg(phydev);
127862306a36Sopenharmony_ci	if (err < 0)
127962306a36Sopenharmony_ci		return err;
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	return genphy_soft_reset(phydev);
128262306a36Sopenharmony_ci}
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_cistatic int m88e1118_config_init(struct phy_device *phydev)
128562306a36Sopenharmony_ci{
128662306a36Sopenharmony_ci	u16 leds;
128762306a36Sopenharmony_ci	int err;
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	/* Enable 1000 Mbit */
129062306a36Sopenharmony_ci	err = phy_write_paged(phydev, MII_MARVELL_MSCR_PAGE,
129162306a36Sopenharmony_ci			      MII_88E1121_PHY_MSCR_REG, 0x1070);
129262306a36Sopenharmony_ci	if (err < 0)
129362306a36Sopenharmony_ci		return err;
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	if (phy_interface_is_rgmii(phydev)) {
129662306a36Sopenharmony_ci		err = m88e1121_config_aneg_rgmii_delays(phydev);
129762306a36Sopenharmony_ci		if (err < 0)
129862306a36Sopenharmony_ci			return err;
129962306a36Sopenharmony_ci	}
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	/* Adjust LED Control */
130262306a36Sopenharmony_ci	if (phydev->dev_flags & MARVELL_PHY_M1118_DNS323_LEDS)
130362306a36Sopenharmony_ci		leds = 0x1100;
130462306a36Sopenharmony_ci	else
130562306a36Sopenharmony_ci		leds = 0x021e;
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	err = phy_write_paged(phydev, MII_MARVELL_LED_PAGE, 0x10, leds);
130862306a36Sopenharmony_ci	if (err < 0)
130962306a36Sopenharmony_ci		return err;
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	err = marvell_of_reg_init(phydev);
131262306a36Sopenharmony_ci	if (err < 0)
131362306a36Sopenharmony_ci		return err;
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	/* Reset page register */
131662306a36Sopenharmony_ci	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
131762306a36Sopenharmony_ci	if (err < 0)
131862306a36Sopenharmony_ci		return err;
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	return genphy_soft_reset(phydev);
132162306a36Sopenharmony_ci}
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_cistatic int m88e1149_config_init(struct phy_device *phydev)
132462306a36Sopenharmony_ci{
132562306a36Sopenharmony_ci	int err;
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	/* Change address */
132862306a36Sopenharmony_ci	err = marvell_set_page(phydev, MII_MARVELL_MSCR_PAGE);
132962306a36Sopenharmony_ci	if (err < 0)
133062306a36Sopenharmony_ci		return err;
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	/* Enable 1000 Mbit */
133362306a36Sopenharmony_ci	err = phy_write(phydev, 0x15, 0x1048);
133462306a36Sopenharmony_ci	if (err < 0)
133562306a36Sopenharmony_ci		return err;
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	err = marvell_of_reg_init(phydev);
133862306a36Sopenharmony_ci	if (err < 0)
133962306a36Sopenharmony_ci		return err;
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	/* Reset address */
134262306a36Sopenharmony_ci	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
134362306a36Sopenharmony_ci	if (err < 0)
134462306a36Sopenharmony_ci		return err;
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	return genphy_soft_reset(phydev);
134762306a36Sopenharmony_ci}
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_cistatic int m88e1145_config_init_rgmii(struct phy_device *phydev)
135062306a36Sopenharmony_ci{
135162306a36Sopenharmony_ci	int err;
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	err = m88e1111_config_init_rgmii_delays(phydev);
135462306a36Sopenharmony_ci	if (err < 0)
135562306a36Sopenharmony_ci		return err;
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	if (phydev->dev_flags & MARVELL_PHY_M1145_FLAGS_RESISTANCE) {
135862306a36Sopenharmony_ci		err = phy_write(phydev, 0x1d, 0x0012);
135962306a36Sopenharmony_ci		if (err < 0)
136062306a36Sopenharmony_ci			return err;
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci		err = phy_modify(phydev, 0x1e, 0x0fc0,
136362306a36Sopenharmony_ci				 2 << 9 | /* 36 ohm */
136462306a36Sopenharmony_ci				 2 << 6); /* 39 ohm */
136562306a36Sopenharmony_ci		if (err < 0)
136662306a36Sopenharmony_ci			return err;
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci		err = phy_write(phydev, 0x1d, 0x3);
136962306a36Sopenharmony_ci		if (err < 0)
137062306a36Sopenharmony_ci			return err;
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci		err = phy_write(phydev, 0x1e, 0x8000);
137362306a36Sopenharmony_ci	}
137462306a36Sopenharmony_ci	return err;
137562306a36Sopenharmony_ci}
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_cistatic int m88e1145_config_init_sgmii(struct phy_device *phydev)
137862306a36Sopenharmony_ci{
137962306a36Sopenharmony_ci	return m88e1111_config_init_hwcfg_mode(
138062306a36Sopenharmony_ci		phydev, MII_M1111_HWCFG_MODE_SGMII_NO_CLK,
138162306a36Sopenharmony_ci		MII_M1111_HWCFG_FIBER_COPPER_AUTO);
138262306a36Sopenharmony_ci}
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_cistatic int m88e1145_config_init(struct phy_device *phydev)
138562306a36Sopenharmony_ci{
138662306a36Sopenharmony_ci	int err;
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_ci	/* Take care of errata E0 & E1 */
138962306a36Sopenharmony_ci	err = phy_write(phydev, 0x1d, 0x001b);
139062306a36Sopenharmony_ci	if (err < 0)
139162306a36Sopenharmony_ci		return err;
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	err = phy_write(phydev, 0x1e, 0x418f);
139462306a36Sopenharmony_ci	if (err < 0)
139562306a36Sopenharmony_ci		return err;
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	err = phy_write(phydev, 0x1d, 0x0016);
139862306a36Sopenharmony_ci	if (err < 0)
139962306a36Sopenharmony_ci		return err;
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	err = phy_write(phydev, 0x1e, 0xa2da);
140262306a36Sopenharmony_ci	if (err < 0)
140362306a36Sopenharmony_ci		return err;
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
140662306a36Sopenharmony_ci		err = m88e1145_config_init_rgmii(phydev);
140762306a36Sopenharmony_ci		if (err < 0)
140862306a36Sopenharmony_ci			return err;
140962306a36Sopenharmony_ci	}
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
141262306a36Sopenharmony_ci		err = m88e1145_config_init_sgmii(phydev);
141362306a36Sopenharmony_ci		if (err < 0)
141462306a36Sopenharmony_ci			return err;
141562306a36Sopenharmony_ci	}
141662306a36Sopenharmony_ci	err = m88e1111_set_downshift(phydev, 3);
141762306a36Sopenharmony_ci	if (err < 0)
141862306a36Sopenharmony_ci		return err;
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	err = marvell_of_reg_init(phydev);
142162306a36Sopenharmony_ci	if (err < 0)
142262306a36Sopenharmony_ci		return err;
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci	return 0;
142562306a36Sopenharmony_ci}
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_cistatic int m88e1540_get_fld(struct phy_device *phydev, u8 *msecs)
142862306a36Sopenharmony_ci{
142962306a36Sopenharmony_ci	int val;
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	val = phy_read(phydev, MII_88E1540_COPPER_CTRL3);
143262306a36Sopenharmony_ci	if (val < 0)
143362306a36Sopenharmony_ci		return val;
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	if (!(val & MII_88E1540_COPPER_CTRL3_FAST_LINK_DOWN)) {
143662306a36Sopenharmony_ci		*msecs = ETHTOOL_PHY_FAST_LINK_DOWN_OFF;
143762306a36Sopenharmony_ci		return 0;
143862306a36Sopenharmony_ci	}
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	val = FIELD_GET(MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_MASK, val);
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	switch (val) {
144362306a36Sopenharmony_ci	case MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_00MS:
144462306a36Sopenharmony_ci		*msecs = 0;
144562306a36Sopenharmony_ci		break;
144662306a36Sopenharmony_ci	case MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_10MS:
144762306a36Sopenharmony_ci		*msecs = 10;
144862306a36Sopenharmony_ci		break;
144962306a36Sopenharmony_ci	case MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_20MS:
145062306a36Sopenharmony_ci		*msecs = 20;
145162306a36Sopenharmony_ci		break;
145262306a36Sopenharmony_ci	case MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_40MS:
145362306a36Sopenharmony_ci		*msecs = 40;
145462306a36Sopenharmony_ci		break;
145562306a36Sopenharmony_ci	default:
145662306a36Sopenharmony_ci		return -EINVAL;
145762306a36Sopenharmony_ci	}
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	return 0;
146062306a36Sopenharmony_ci}
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_cistatic int m88e1540_set_fld(struct phy_device *phydev, const u8 *msecs)
146362306a36Sopenharmony_ci{
146462306a36Sopenharmony_ci	struct ethtool_eee eee;
146562306a36Sopenharmony_ci	int val, ret;
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	if (*msecs == ETHTOOL_PHY_FAST_LINK_DOWN_OFF)
146862306a36Sopenharmony_ci		return phy_clear_bits(phydev, MII_88E1540_COPPER_CTRL3,
146962306a36Sopenharmony_ci				      MII_88E1540_COPPER_CTRL3_FAST_LINK_DOWN);
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	/* According to the Marvell data sheet EEE must be disabled for
147262306a36Sopenharmony_ci	 * Fast Link Down detection to work properly
147362306a36Sopenharmony_ci	 */
147462306a36Sopenharmony_ci	ret = genphy_c45_ethtool_get_eee(phydev, &eee);
147562306a36Sopenharmony_ci	if (!ret && eee.eee_enabled) {
147662306a36Sopenharmony_ci		phydev_warn(phydev, "Fast Link Down detection requires EEE to be disabled!\n");
147762306a36Sopenharmony_ci		return -EBUSY;
147862306a36Sopenharmony_ci	}
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	if (*msecs <= 5)
148162306a36Sopenharmony_ci		val = MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_00MS;
148262306a36Sopenharmony_ci	else if (*msecs <= 15)
148362306a36Sopenharmony_ci		val = MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_10MS;
148462306a36Sopenharmony_ci	else if (*msecs <= 30)
148562306a36Sopenharmony_ci		val = MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_20MS;
148662306a36Sopenharmony_ci	else
148762306a36Sopenharmony_ci		val = MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_40MS;
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	val = FIELD_PREP(MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_MASK, val);
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci	ret = phy_modify(phydev, MII_88E1540_COPPER_CTRL3,
149262306a36Sopenharmony_ci			 MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_MASK, val);
149362306a36Sopenharmony_ci	if (ret)
149462306a36Sopenharmony_ci		return ret;
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	return phy_set_bits(phydev, MII_88E1540_COPPER_CTRL3,
149762306a36Sopenharmony_ci			    MII_88E1540_COPPER_CTRL3_FAST_LINK_DOWN);
149862306a36Sopenharmony_ci}
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_cistatic int m88e1540_get_tunable(struct phy_device *phydev,
150162306a36Sopenharmony_ci				struct ethtool_tunable *tuna, void *data)
150262306a36Sopenharmony_ci{
150362306a36Sopenharmony_ci	switch (tuna->id) {
150462306a36Sopenharmony_ci	case ETHTOOL_PHY_FAST_LINK_DOWN:
150562306a36Sopenharmony_ci		return m88e1540_get_fld(phydev, data);
150662306a36Sopenharmony_ci	case ETHTOOL_PHY_DOWNSHIFT:
150762306a36Sopenharmony_ci		return m88e1011_get_downshift(phydev, data);
150862306a36Sopenharmony_ci	default:
150962306a36Sopenharmony_ci		return -EOPNOTSUPP;
151062306a36Sopenharmony_ci	}
151162306a36Sopenharmony_ci}
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_cistatic int m88e1540_set_tunable(struct phy_device *phydev,
151462306a36Sopenharmony_ci				struct ethtool_tunable *tuna, const void *data)
151562306a36Sopenharmony_ci{
151662306a36Sopenharmony_ci	switch (tuna->id) {
151762306a36Sopenharmony_ci	case ETHTOOL_PHY_FAST_LINK_DOWN:
151862306a36Sopenharmony_ci		return m88e1540_set_fld(phydev, data);
151962306a36Sopenharmony_ci	case ETHTOOL_PHY_DOWNSHIFT:
152062306a36Sopenharmony_ci		return m88e1011_set_downshift(phydev, *(const u8 *)data);
152162306a36Sopenharmony_ci	default:
152262306a36Sopenharmony_ci		return -EOPNOTSUPP;
152362306a36Sopenharmony_ci	}
152462306a36Sopenharmony_ci}
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci/* The VOD can be out of specification on link up. Poke an
152762306a36Sopenharmony_ci * undocumented register, in an undocumented page, with a magic value
152862306a36Sopenharmony_ci * to fix this.
152962306a36Sopenharmony_ci */
153062306a36Sopenharmony_cistatic int m88e6390_errata(struct phy_device *phydev)
153162306a36Sopenharmony_ci{
153262306a36Sopenharmony_ci	int err;
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	err = phy_write(phydev, MII_BMCR,
153562306a36Sopenharmony_ci			BMCR_ANENABLE | BMCR_SPEED1000 | BMCR_FULLDPLX);
153662306a36Sopenharmony_ci	if (err)
153762306a36Sopenharmony_ci		return err;
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci	usleep_range(300, 400);
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	err = phy_write_paged(phydev, 0xf8, 0x08, 0x36);
154262306a36Sopenharmony_ci	if (err)
154362306a36Sopenharmony_ci		return err;
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci	return genphy_soft_reset(phydev);
154662306a36Sopenharmony_ci}
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_cistatic int m88e6390_config_aneg(struct phy_device *phydev)
154962306a36Sopenharmony_ci{
155062306a36Sopenharmony_ci	int err;
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	err = m88e6390_errata(phydev);
155362306a36Sopenharmony_ci	if (err)
155462306a36Sopenharmony_ci		return err;
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	return m88e1510_config_aneg(phydev);
155762306a36Sopenharmony_ci}
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci/**
156062306a36Sopenharmony_ci * fiber_lpa_mod_linkmode_lpa_t
156162306a36Sopenharmony_ci * @advertising: the linkmode advertisement settings
156262306a36Sopenharmony_ci * @lpa: value of the MII_LPA register for fiber link
156362306a36Sopenharmony_ci *
156462306a36Sopenharmony_ci * A small helper function that translates MII_LPA bits to linkmode LP
156562306a36Sopenharmony_ci * advertisement settings. Other bits in advertising are left
156662306a36Sopenharmony_ci * unchanged.
156762306a36Sopenharmony_ci */
156862306a36Sopenharmony_cistatic void fiber_lpa_mod_linkmode_lpa_t(unsigned long *advertising, u32 lpa)
156962306a36Sopenharmony_ci{
157062306a36Sopenharmony_ci	linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
157162306a36Sopenharmony_ci			 advertising, lpa & LPA_1000XHALF);
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci	linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
157462306a36Sopenharmony_ci			 advertising, lpa & LPA_1000XFULL);
157562306a36Sopenharmony_ci}
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_cistatic int marvell_read_status_page_an(struct phy_device *phydev,
157862306a36Sopenharmony_ci				       int fiber, int status)
157962306a36Sopenharmony_ci{
158062306a36Sopenharmony_ci	int lpa;
158162306a36Sopenharmony_ci	int err;
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	if (!(status & MII_M1011_PHY_STATUS_RESOLVED)) {
158462306a36Sopenharmony_ci		phydev->link = 0;
158562306a36Sopenharmony_ci		return 0;
158662306a36Sopenharmony_ci	}
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci	if (status & MII_M1011_PHY_STATUS_FULLDUPLEX)
158962306a36Sopenharmony_ci		phydev->duplex = DUPLEX_FULL;
159062306a36Sopenharmony_ci	else
159162306a36Sopenharmony_ci		phydev->duplex = DUPLEX_HALF;
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci	switch (status & MII_M1011_PHY_STATUS_SPD_MASK) {
159462306a36Sopenharmony_ci	case MII_M1011_PHY_STATUS_1000:
159562306a36Sopenharmony_ci		phydev->speed = SPEED_1000;
159662306a36Sopenharmony_ci		break;
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci	case MII_M1011_PHY_STATUS_100:
159962306a36Sopenharmony_ci		phydev->speed = SPEED_100;
160062306a36Sopenharmony_ci		break;
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci	default:
160362306a36Sopenharmony_ci		phydev->speed = SPEED_10;
160462306a36Sopenharmony_ci		break;
160562306a36Sopenharmony_ci	}
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci	if (!fiber) {
160862306a36Sopenharmony_ci		err = genphy_read_lpa(phydev);
160962306a36Sopenharmony_ci		if (err < 0)
161062306a36Sopenharmony_ci			return err;
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci		phy_resolve_aneg_pause(phydev);
161362306a36Sopenharmony_ci	} else {
161462306a36Sopenharmony_ci		lpa = phy_read(phydev, MII_LPA);
161562306a36Sopenharmony_ci		if (lpa < 0)
161662306a36Sopenharmony_ci			return lpa;
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci		/* The fiber link is only 1000M capable */
161962306a36Sopenharmony_ci		fiber_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa);
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci		if (phydev->duplex == DUPLEX_FULL) {
162262306a36Sopenharmony_ci			if (!(lpa & LPA_PAUSE_FIBER)) {
162362306a36Sopenharmony_ci				phydev->pause = 0;
162462306a36Sopenharmony_ci				phydev->asym_pause = 0;
162562306a36Sopenharmony_ci			} else if ((lpa & LPA_PAUSE_ASYM_FIBER)) {
162662306a36Sopenharmony_ci				phydev->pause = 1;
162762306a36Sopenharmony_ci				phydev->asym_pause = 1;
162862306a36Sopenharmony_ci			} else {
162962306a36Sopenharmony_ci				phydev->pause = 1;
163062306a36Sopenharmony_ci				phydev->asym_pause = 0;
163162306a36Sopenharmony_ci			}
163262306a36Sopenharmony_ci		}
163362306a36Sopenharmony_ci	}
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	return 0;
163662306a36Sopenharmony_ci}
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci/* marvell_read_status_page
163962306a36Sopenharmony_ci *
164062306a36Sopenharmony_ci * Description:
164162306a36Sopenharmony_ci *   Check the link, then figure out the current state
164262306a36Sopenharmony_ci *   by comparing what we advertise with what the link partner
164362306a36Sopenharmony_ci *   advertises.  Start by checking the gigabit possibilities,
164462306a36Sopenharmony_ci *   then move on to 10/100.
164562306a36Sopenharmony_ci */
164662306a36Sopenharmony_cistatic int marvell_read_status_page(struct phy_device *phydev, int page)
164762306a36Sopenharmony_ci{
164862306a36Sopenharmony_ci	int status;
164962306a36Sopenharmony_ci	int fiber;
165062306a36Sopenharmony_ci	int err;
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci	status = phy_read(phydev, MII_M1011_PHY_STATUS);
165362306a36Sopenharmony_ci	if (status < 0)
165462306a36Sopenharmony_ci		return status;
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci	/* Use the generic register for copper link status,
165762306a36Sopenharmony_ci	 * and the PHY status register for fiber link status.
165862306a36Sopenharmony_ci	 */
165962306a36Sopenharmony_ci	if (page == MII_MARVELL_FIBER_PAGE) {
166062306a36Sopenharmony_ci		phydev->link = !!(status & MII_M1011_PHY_STATUS_LINK);
166162306a36Sopenharmony_ci	} else {
166262306a36Sopenharmony_ci		err = genphy_update_link(phydev);
166362306a36Sopenharmony_ci		if (err)
166462306a36Sopenharmony_ci			return err;
166562306a36Sopenharmony_ci	}
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci	if (page == MII_MARVELL_FIBER_PAGE)
166862306a36Sopenharmony_ci		fiber = 1;
166962306a36Sopenharmony_ci	else
167062306a36Sopenharmony_ci		fiber = 0;
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	linkmode_zero(phydev->lp_advertising);
167362306a36Sopenharmony_ci	phydev->pause = 0;
167462306a36Sopenharmony_ci	phydev->asym_pause = 0;
167562306a36Sopenharmony_ci	phydev->speed = SPEED_UNKNOWN;
167662306a36Sopenharmony_ci	phydev->duplex = DUPLEX_UNKNOWN;
167762306a36Sopenharmony_ci	phydev->port = fiber ? PORT_FIBRE : PORT_TP;
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_ci	if (phydev->autoneg == AUTONEG_ENABLE)
168062306a36Sopenharmony_ci		err = marvell_read_status_page_an(phydev, fiber, status);
168162306a36Sopenharmony_ci	else
168262306a36Sopenharmony_ci		err = genphy_read_status_fixed(phydev);
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ci	return err;
168562306a36Sopenharmony_ci}
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_ci/* marvell_read_status
168862306a36Sopenharmony_ci *
168962306a36Sopenharmony_ci * Some Marvell's phys have two modes: fiber and copper.
169062306a36Sopenharmony_ci * Both need status checked.
169162306a36Sopenharmony_ci * Description:
169262306a36Sopenharmony_ci *   First, check the fiber link and status.
169362306a36Sopenharmony_ci *   If the fiber link is down, check the copper link and status which
169462306a36Sopenharmony_ci *   will be the default value if both link are down.
169562306a36Sopenharmony_ci */
169662306a36Sopenharmony_cistatic int marvell_read_status(struct phy_device *phydev)
169762306a36Sopenharmony_ci{
169862306a36Sopenharmony_ci	int err;
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci	/* Check the fiber mode first */
170162306a36Sopenharmony_ci	if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
170262306a36Sopenharmony_ci			      phydev->supported) &&
170362306a36Sopenharmony_ci	    phydev->interface != PHY_INTERFACE_MODE_SGMII) {
170462306a36Sopenharmony_ci		err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
170562306a36Sopenharmony_ci		if (err < 0)
170662306a36Sopenharmony_ci			goto error;
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci		err = marvell_read_status_page(phydev, MII_MARVELL_FIBER_PAGE);
170962306a36Sopenharmony_ci		if (err < 0)
171062306a36Sopenharmony_ci			goto error;
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci		/* If the fiber link is up, it is the selected and
171362306a36Sopenharmony_ci		 * used link. In this case, we need to stay in the
171462306a36Sopenharmony_ci		 * fiber page. Please to be careful about that, avoid
171562306a36Sopenharmony_ci		 * to restore Copper page in other functions which
171662306a36Sopenharmony_ci		 * could break the behaviour for some fiber phy like
171762306a36Sopenharmony_ci		 * 88E1512.
171862306a36Sopenharmony_ci		 */
171962306a36Sopenharmony_ci		if (phydev->link)
172062306a36Sopenharmony_ci			return 0;
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci		/* If fiber link is down, check and save copper mode state */
172362306a36Sopenharmony_ci		err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
172462306a36Sopenharmony_ci		if (err < 0)
172562306a36Sopenharmony_ci			goto error;
172662306a36Sopenharmony_ci	}
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci	return marvell_read_status_page(phydev, MII_MARVELL_COPPER_PAGE);
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_cierror:
173162306a36Sopenharmony_ci	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
173262306a36Sopenharmony_ci	return err;
173362306a36Sopenharmony_ci}
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci/* marvell_suspend
173662306a36Sopenharmony_ci *
173762306a36Sopenharmony_ci * Some Marvell's phys have two modes: fiber and copper.
173862306a36Sopenharmony_ci * Both need to be suspended
173962306a36Sopenharmony_ci */
174062306a36Sopenharmony_cistatic int marvell_suspend(struct phy_device *phydev)
174162306a36Sopenharmony_ci{
174262306a36Sopenharmony_ci	int err;
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_ci	/* Suspend the fiber mode first */
174562306a36Sopenharmony_ci	if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
174662306a36Sopenharmony_ci			      phydev->supported)) {
174762306a36Sopenharmony_ci		err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
174862306a36Sopenharmony_ci		if (err < 0)
174962306a36Sopenharmony_ci			goto error;
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci		/* With the page set, use the generic suspend */
175262306a36Sopenharmony_ci		err = genphy_suspend(phydev);
175362306a36Sopenharmony_ci		if (err < 0)
175462306a36Sopenharmony_ci			goto error;
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci		/* Then, the copper link */
175762306a36Sopenharmony_ci		err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
175862306a36Sopenharmony_ci		if (err < 0)
175962306a36Sopenharmony_ci			goto error;
176062306a36Sopenharmony_ci	}
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci	/* With the page set, use the generic suspend */
176362306a36Sopenharmony_ci	return genphy_suspend(phydev);
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_cierror:
176662306a36Sopenharmony_ci	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
176762306a36Sopenharmony_ci	return err;
176862306a36Sopenharmony_ci}
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_ci/* marvell_resume
177162306a36Sopenharmony_ci *
177262306a36Sopenharmony_ci * Some Marvell's phys have two modes: fiber and copper.
177362306a36Sopenharmony_ci * Both need to be resumed
177462306a36Sopenharmony_ci */
177562306a36Sopenharmony_cistatic int marvell_resume(struct phy_device *phydev)
177662306a36Sopenharmony_ci{
177762306a36Sopenharmony_ci	int err;
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci	/* Resume the fiber mode first */
178062306a36Sopenharmony_ci	if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
178162306a36Sopenharmony_ci			      phydev->supported)) {
178262306a36Sopenharmony_ci		err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
178362306a36Sopenharmony_ci		if (err < 0)
178462306a36Sopenharmony_ci			goto error;
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci		/* With the page set, use the generic resume */
178762306a36Sopenharmony_ci		err = genphy_resume(phydev);
178862306a36Sopenharmony_ci		if (err < 0)
178962306a36Sopenharmony_ci			goto error;
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_ci		/* Then, the copper link */
179262306a36Sopenharmony_ci		err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
179362306a36Sopenharmony_ci		if (err < 0)
179462306a36Sopenharmony_ci			goto error;
179562306a36Sopenharmony_ci	}
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_ci	/* With the page set, use the generic resume */
179862306a36Sopenharmony_ci	return genphy_resume(phydev);
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_cierror:
180162306a36Sopenharmony_ci	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
180262306a36Sopenharmony_ci	return err;
180362306a36Sopenharmony_ci}
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_cistatic int marvell_aneg_done(struct phy_device *phydev)
180662306a36Sopenharmony_ci{
180762306a36Sopenharmony_ci	int retval = phy_read(phydev, MII_M1011_PHY_STATUS);
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci	return (retval < 0) ? retval : (retval & MII_M1011_PHY_STATUS_RESOLVED);
181062306a36Sopenharmony_ci}
181162306a36Sopenharmony_ci
181262306a36Sopenharmony_cistatic void m88e1318_get_wol(struct phy_device *phydev,
181362306a36Sopenharmony_ci			     struct ethtool_wolinfo *wol)
181462306a36Sopenharmony_ci{
181562306a36Sopenharmony_ci	int ret;
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci	wol->supported = WAKE_MAGIC | WAKE_PHY;
181862306a36Sopenharmony_ci	wol->wolopts = 0;
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ci	ret = phy_read_paged(phydev, MII_MARVELL_WOL_PAGE,
182162306a36Sopenharmony_ci			     MII_88E1318S_PHY_WOL_CTRL);
182262306a36Sopenharmony_ci	if (ret < 0)
182362306a36Sopenharmony_ci		return;
182462306a36Sopenharmony_ci
182562306a36Sopenharmony_ci	if (ret & MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE)
182662306a36Sopenharmony_ci		wol->wolopts |= WAKE_MAGIC;
182762306a36Sopenharmony_ci
182862306a36Sopenharmony_ci	if (ret & MII_88E1318S_PHY_WOL_CTRL_LINK_UP_ENABLE)
182962306a36Sopenharmony_ci		wol->wolopts |= WAKE_PHY;
183062306a36Sopenharmony_ci}
183162306a36Sopenharmony_ci
183262306a36Sopenharmony_cistatic int m88e1318_set_wol(struct phy_device *phydev,
183362306a36Sopenharmony_ci			    struct ethtool_wolinfo *wol)
183462306a36Sopenharmony_ci{
183562306a36Sopenharmony_ci	int err = 0, oldpage;
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	oldpage = phy_save_page(phydev);
183862306a36Sopenharmony_ci	if (oldpage < 0)
183962306a36Sopenharmony_ci		goto error;
184062306a36Sopenharmony_ci
184162306a36Sopenharmony_ci	if (wol->wolopts & (WAKE_MAGIC | WAKE_PHY)) {
184262306a36Sopenharmony_ci		/* Explicitly switch to page 0x00, just to be sure */
184362306a36Sopenharmony_ci		err = marvell_write_page(phydev, MII_MARVELL_COPPER_PAGE);
184462306a36Sopenharmony_ci		if (err < 0)
184562306a36Sopenharmony_ci			goto error;
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_ci		/* If WOL event happened once, the LED[2] interrupt pin
184862306a36Sopenharmony_ci		 * will not be cleared unless we reading the interrupt status
184962306a36Sopenharmony_ci		 * register. If interrupts are in use, the normal interrupt
185062306a36Sopenharmony_ci		 * handling will clear the WOL event. Clear the WOL event
185162306a36Sopenharmony_ci		 * before enabling it if !phy_interrupt_is_valid()
185262306a36Sopenharmony_ci		 */
185362306a36Sopenharmony_ci		if (!phy_interrupt_is_valid(phydev))
185462306a36Sopenharmony_ci			__phy_read(phydev, MII_M1011_IEVENT);
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci		/* Enable the WOL interrupt */
185762306a36Sopenharmony_ci		err = __phy_set_bits(phydev, MII_88E1318S_PHY_CSIER,
185862306a36Sopenharmony_ci				     MII_88E1318S_PHY_CSIER_WOL_EIE);
185962306a36Sopenharmony_ci		if (err < 0)
186062306a36Sopenharmony_ci			goto error;
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci		err = marvell_write_page(phydev, MII_MARVELL_LED_PAGE);
186362306a36Sopenharmony_ci		if (err < 0)
186462306a36Sopenharmony_ci			goto error;
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci		/* Setup LED[2] as interrupt pin (active low) */
186762306a36Sopenharmony_ci		err = __phy_modify(phydev, MII_88E1318S_PHY_LED_TCR,
186862306a36Sopenharmony_ci				   MII_88E1318S_PHY_LED_TCR_FORCE_INT,
186962306a36Sopenharmony_ci				   MII_88E1318S_PHY_LED_TCR_INTn_ENABLE |
187062306a36Sopenharmony_ci				   MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW);
187162306a36Sopenharmony_ci		if (err < 0)
187262306a36Sopenharmony_ci			goto error;
187362306a36Sopenharmony_ci	}
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_ci	if (wol->wolopts & WAKE_MAGIC) {
187662306a36Sopenharmony_ci		err = marvell_write_page(phydev, MII_MARVELL_WOL_PAGE);
187762306a36Sopenharmony_ci		if (err < 0)
187862306a36Sopenharmony_ci			goto error;
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_ci		/* Store the device address for the magic packet */
188162306a36Sopenharmony_ci		err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD2,
188262306a36Sopenharmony_ci				((phydev->attached_dev->dev_addr[5] << 8) |
188362306a36Sopenharmony_ci				 phydev->attached_dev->dev_addr[4]));
188462306a36Sopenharmony_ci		if (err < 0)
188562306a36Sopenharmony_ci			goto error;
188662306a36Sopenharmony_ci		err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD1,
188762306a36Sopenharmony_ci				((phydev->attached_dev->dev_addr[3] << 8) |
188862306a36Sopenharmony_ci				 phydev->attached_dev->dev_addr[2]));
188962306a36Sopenharmony_ci		if (err < 0)
189062306a36Sopenharmony_ci			goto error;
189162306a36Sopenharmony_ci		err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD0,
189262306a36Sopenharmony_ci				((phydev->attached_dev->dev_addr[1] << 8) |
189362306a36Sopenharmony_ci				 phydev->attached_dev->dev_addr[0]));
189462306a36Sopenharmony_ci		if (err < 0)
189562306a36Sopenharmony_ci			goto error;
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_ci		/* Clear WOL status and enable magic packet matching */
189862306a36Sopenharmony_ci		err = __phy_set_bits(phydev, MII_88E1318S_PHY_WOL_CTRL,
189962306a36Sopenharmony_ci				     MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS |
190062306a36Sopenharmony_ci				     MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE);
190162306a36Sopenharmony_ci		if (err < 0)
190262306a36Sopenharmony_ci			goto error;
190362306a36Sopenharmony_ci	} else {
190462306a36Sopenharmony_ci		err = marvell_write_page(phydev, MII_MARVELL_WOL_PAGE);
190562306a36Sopenharmony_ci		if (err < 0)
190662306a36Sopenharmony_ci			goto error;
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_ci		/* Clear WOL status and disable magic packet matching */
190962306a36Sopenharmony_ci		err = __phy_modify(phydev, MII_88E1318S_PHY_WOL_CTRL,
191062306a36Sopenharmony_ci				   MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE,
191162306a36Sopenharmony_ci				   MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS);
191262306a36Sopenharmony_ci		if (err < 0)
191362306a36Sopenharmony_ci			goto error;
191462306a36Sopenharmony_ci	}
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_ci	if (wol->wolopts & WAKE_PHY) {
191762306a36Sopenharmony_ci		err = marvell_write_page(phydev, MII_MARVELL_WOL_PAGE);
191862306a36Sopenharmony_ci		if (err < 0)
191962306a36Sopenharmony_ci			goto error;
192062306a36Sopenharmony_ci
192162306a36Sopenharmony_ci		/* Clear WOL status and enable link up event */
192262306a36Sopenharmony_ci		err = __phy_modify(phydev, MII_88E1318S_PHY_WOL_CTRL, 0,
192362306a36Sopenharmony_ci				   MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS |
192462306a36Sopenharmony_ci				   MII_88E1318S_PHY_WOL_CTRL_LINK_UP_ENABLE);
192562306a36Sopenharmony_ci		if (err < 0)
192662306a36Sopenharmony_ci			goto error;
192762306a36Sopenharmony_ci	} else {
192862306a36Sopenharmony_ci		err = marvell_write_page(phydev, MII_MARVELL_WOL_PAGE);
192962306a36Sopenharmony_ci		if (err < 0)
193062306a36Sopenharmony_ci			goto error;
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_ci		/* Clear WOL status and disable link up event */
193362306a36Sopenharmony_ci		err = __phy_modify(phydev, MII_88E1318S_PHY_WOL_CTRL,
193462306a36Sopenharmony_ci				   MII_88E1318S_PHY_WOL_CTRL_LINK_UP_ENABLE,
193562306a36Sopenharmony_ci				   MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS);
193662306a36Sopenharmony_ci		if (err < 0)
193762306a36Sopenharmony_ci			goto error;
193862306a36Sopenharmony_ci	}
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_cierror:
194162306a36Sopenharmony_ci	return phy_restore_page(phydev, oldpage, err);
194262306a36Sopenharmony_ci}
194362306a36Sopenharmony_ci
194462306a36Sopenharmony_cistatic int marvell_get_sset_count(struct phy_device *phydev)
194562306a36Sopenharmony_ci{
194662306a36Sopenharmony_ci	if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
194762306a36Sopenharmony_ci			      phydev->supported))
194862306a36Sopenharmony_ci		return ARRAY_SIZE(marvell_hw_stats);
194962306a36Sopenharmony_ci	else
195062306a36Sopenharmony_ci		return ARRAY_SIZE(marvell_hw_stats) - NB_FIBER_STATS;
195162306a36Sopenharmony_ci}
195262306a36Sopenharmony_ci
195362306a36Sopenharmony_cistatic void marvell_get_strings(struct phy_device *phydev, u8 *data)
195462306a36Sopenharmony_ci{
195562306a36Sopenharmony_ci	int count = marvell_get_sset_count(phydev);
195662306a36Sopenharmony_ci	int i;
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
195962306a36Sopenharmony_ci		strscpy(data + i * ETH_GSTRING_LEN,
196062306a36Sopenharmony_ci			marvell_hw_stats[i].string, ETH_GSTRING_LEN);
196162306a36Sopenharmony_ci	}
196262306a36Sopenharmony_ci}
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_cistatic u64 marvell_get_stat(struct phy_device *phydev, int i)
196562306a36Sopenharmony_ci{
196662306a36Sopenharmony_ci	struct marvell_hw_stat stat = marvell_hw_stats[i];
196762306a36Sopenharmony_ci	struct marvell_priv *priv = phydev->priv;
196862306a36Sopenharmony_ci	int val;
196962306a36Sopenharmony_ci	u64 ret;
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ci	val = phy_read_paged(phydev, stat.page, stat.reg);
197262306a36Sopenharmony_ci	if (val < 0) {
197362306a36Sopenharmony_ci		ret = U64_MAX;
197462306a36Sopenharmony_ci	} else {
197562306a36Sopenharmony_ci		val = val & ((1 << stat.bits) - 1);
197662306a36Sopenharmony_ci		priv->stats[i] += val;
197762306a36Sopenharmony_ci		ret = priv->stats[i];
197862306a36Sopenharmony_ci	}
197962306a36Sopenharmony_ci
198062306a36Sopenharmony_ci	return ret;
198162306a36Sopenharmony_ci}
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_cistatic void marvell_get_stats(struct phy_device *phydev,
198462306a36Sopenharmony_ci			      struct ethtool_stats *stats, u64 *data)
198562306a36Sopenharmony_ci{
198662306a36Sopenharmony_ci	int count = marvell_get_sset_count(phydev);
198762306a36Sopenharmony_ci	int i;
198862306a36Sopenharmony_ci
198962306a36Sopenharmony_ci	for (i = 0; i < count; i++)
199062306a36Sopenharmony_ci		data[i] = marvell_get_stat(phydev, i);
199162306a36Sopenharmony_ci}
199262306a36Sopenharmony_ci
199362306a36Sopenharmony_cistatic int m88e1510_loopback(struct phy_device *phydev, bool enable)
199462306a36Sopenharmony_ci{
199562306a36Sopenharmony_ci	int err;
199662306a36Sopenharmony_ci
199762306a36Sopenharmony_ci	if (enable) {
199862306a36Sopenharmony_ci		u16 bmcr_ctl, mscr2_ctl = 0;
199962306a36Sopenharmony_ci
200062306a36Sopenharmony_ci		bmcr_ctl = mii_bmcr_encode_fixed(phydev->speed, phydev->duplex);
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_ci		err = phy_write(phydev, MII_BMCR, bmcr_ctl);
200362306a36Sopenharmony_ci		if (err < 0)
200462306a36Sopenharmony_ci			return err;
200562306a36Sopenharmony_ci
200662306a36Sopenharmony_ci		if (phydev->speed == SPEED_1000)
200762306a36Sopenharmony_ci			mscr2_ctl = BMCR_SPEED1000;
200862306a36Sopenharmony_ci		else if (phydev->speed == SPEED_100)
200962306a36Sopenharmony_ci			mscr2_ctl = BMCR_SPEED100;
201062306a36Sopenharmony_ci
201162306a36Sopenharmony_ci		err = phy_modify_paged(phydev, MII_MARVELL_MSCR_PAGE,
201262306a36Sopenharmony_ci				       MII_88E1510_MSCR_2, BMCR_SPEED1000 |
201362306a36Sopenharmony_ci				       BMCR_SPEED100, mscr2_ctl);
201462306a36Sopenharmony_ci		if (err < 0)
201562306a36Sopenharmony_ci			return err;
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_ci		/* Need soft reset to have speed configuration takes effect */
201862306a36Sopenharmony_ci		err = genphy_soft_reset(phydev);
201962306a36Sopenharmony_ci		if (err < 0)
202062306a36Sopenharmony_ci			return err;
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_ci		err = phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK,
202362306a36Sopenharmony_ci				 BMCR_LOOPBACK);
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_ci		if (!err) {
202662306a36Sopenharmony_ci			/* It takes some time for PHY device to switch
202762306a36Sopenharmony_ci			 * into/out-of loopback mode.
202862306a36Sopenharmony_ci			 */
202962306a36Sopenharmony_ci			msleep(1000);
203062306a36Sopenharmony_ci		}
203162306a36Sopenharmony_ci		return err;
203262306a36Sopenharmony_ci	} else {
203362306a36Sopenharmony_ci		err = phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK, 0);
203462306a36Sopenharmony_ci		if (err < 0)
203562306a36Sopenharmony_ci			return err;
203662306a36Sopenharmony_ci
203762306a36Sopenharmony_ci		return phy_config_aneg(phydev);
203862306a36Sopenharmony_ci	}
203962306a36Sopenharmony_ci}
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_cistatic int marvell_vct5_wait_complete(struct phy_device *phydev)
204262306a36Sopenharmony_ci{
204362306a36Sopenharmony_ci	int i;
204462306a36Sopenharmony_ci	int val;
204562306a36Sopenharmony_ci
204662306a36Sopenharmony_ci	for (i = 0; i < 32; i++) {
204762306a36Sopenharmony_ci		val = __phy_read(phydev, MII_VCT5_CTRL);
204862306a36Sopenharmony_ci		if (val < 0)
204962306a36Sopenharmony_ci			return val;
205062306a36Sopenharmony_ci
205162306a36Sopenharmony_ci		if (val & MII_VCT5_CTRL_COMPLETE)
205262306a36Sopenharmony_ci			return 0;
205362306a36Sopenharmony_ci	}
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ci	phydev_err(phydev, "Timeout while waiting for cable test to finish\n");
205662306a36Sopenharmony_ci	return -ETIMEDOUT;
205762306a36Sopenharmony_ci}
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_cistatic int marvell_vct5_amplitude(struct phy_device *phydev, int pair)
206062306a36Sopenharmony_ci{
206162306a36Sopenharmony_ci	int amplitude;
206262306a36Sopenharmony_ci	int val;
206362306a36Sopenharmony_ci	int reg;
206462306a36Sopenharmony_ci
206562306a36Sopenharmony_ci	reg = MII_VCT5_TX_RX_MDI0_COUPLING + pair;
206662306a36Sopenharmony_ci	val = __phy_read(phydev, reg);
206762306a36Sopenharmony_ci
206862306a36Sopenharmony_ci	if (val < 0)
206962306a36Sopenharmony_ci		return 0;
207062306a36Sopenharmony_ci
207162306a36Sopenharmony_ci	amplitude = (val & MII_VCT5_TX_RX_AMPLITUDE_MASK) >>
207262306a36Sopenharmony_ci		MII_VCT5_TX_RX_AMPLITUDE_SHIFT;
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_ci	if (!(val & MII_VCT5_TX_RX_COUPLING_POSITIVE_REFLECTION))
207562306a36Sopenharmony_ci		amplitude = -amplitude;
207662306a36Sopenharmony_ci
207762306a36Sopenharmony_ci	return 1000 * amplitude / 128;
207862306a36Sopenharmony_ci}
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_cistatic u32 marvell_vct5_distance2cm(int distance)
208162306a36Sopenharmony_ci{
208262306a36Sopenharmony_ci	return distance * 805 / 10;
208362306a36Sopenharmony_ci}
208462306a36Sopenharmony_ci
208562306a36Sopenharmony_cistatic u32 marvell_vct5_cm2distance(int cm)
208662306a36Sopenharmony_ci{
208762306a36Sopenharmony_ci	return cm * 10 / 805;
208862306a36Sopenharmony_ci}
208962306a36Sopenharmony_ci
209062306a36Sopenharmony_cistatic int marvell_vct5_amplitude_distance(struct phy_device *phydev,
209162306a36Sopenharmony_ci					   int distance, int pair)
209262306a36Sopenharmony_ci{
209362306a36Sopenharmony_ci	u16 reg;
209462306a36Sopenharmony_ci	int err;
209562306a36Sopenharmony_ci	int mV;
209662306a36Sopenharmony_ci	int i;
209762306a36Sopenharmony_ci
209862306a36Sopenharmony_ci	err = __phy_write(phydev, MII_VCT5_SAMPLE_POINT_DISTANCE,
209962306a36Sopenharmony_ci			  distance);
210062306a36Sopenharmony_ci	if (err)
210162306a36Sopenharmony_ci		return err;
210262306a36Sopenharmony_ci
210362306a36Sopenharmony_ci	reg = MII_VCT5_CTRL_ENABLE |
210462306a36Sopenharmony_ci		MII_VCT5_CTRL_TX_SAME_CHANNEL |
210562306a36Sopenharmony_ci		MII_VCT5_CTRL_SAMPLES_DEFAULT |
210662306a36Sopenharmony_ci		MII_VCT5_CTRL_SAMPLE_POINT |
210762306a36Sopenharmony_ci		MII_VCT5_CTRL_PEEK_HYST_DEFAULT;
210862306a36Sopenharmony_ci	err = __phy_write(phydev, MII_VCT5_CTRL, reg);
210962306a36Sopenharmony_ci	if (err)
211062306a36Sopenharmony_ci		return err;
211162306a36Sopenharmony_ci
211262306a36Sopenharmony_ci	err = marvell_vct5_wait_complete(phydev);
211362306a36Sopenharmony_ci	if (err)
211462306a36Sopenharmony_ci		return err;
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_ci	for (i = 0; i < 4; i++) {
211762306a36Sopenharmony_ci		if (pair != PHY_PAIR_ALL && i != pair)
211862306a36Sopenharmony_ci			continue;
211962306a36Sopenharmony_ci
212062306a36Sopenharmony_ci		mV = marvell_vct5_amplitude(phydev, i);
212162306a36Sopenharmony_ci		ethnl_cable_test_amplitude(phydev, i, mV);
212262306a36Sopenharmony_ci	}
212362306a36Sopenharmony_ci
212462306a36Sopenharmony_ci	return 0;
212562306a36Sopenharmony_ci}
212662306a36Sopenharmony_ci
212762306a36Sopenharmony_cistatic int marvell_vct5_amplitude_graph(struct phy_device *phydev)
212862306a36Sopenharmony_ci{
212962306a36Sopenharmony_ci	struct marvell_priv *priv = phydev->priv;
213062306a36Sopenharmony_ci	int distance;
213162306a36Sopenharmony_ci	u16 width;
213262306a36Sopenharmony_ci	int page;
213362306a36Sopenharmony_ci	int err;
213462306a36Sopenharmony_ci	u16 reg;
213562306a36Sopenharmony_ci
213662306a36Sopenharmony_ci	if (priv->first <= TDR_SHORT_CABLE_LENGTH)
213762306a36Sopenharmony_ci		width = MII_VCT5_TX_PULSE_CTRL_PULSE_WIDTH_32nS;
213862306a36Sopenharmony_ci	else
213962306a36Sopenharmony_ci		width = MII_VCT5_TX_PULSE_CTRL_PULSE_WIDTH_128nS;
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_ci	reg = MII_VCT5_TX_PULSE_CTRL_GT_140m_46_86mV |
214262306a36Sopenharmony_ci		MII_VCT5_TX_PULSE_CTRL_DONT_WAIT_LINK_DOWN |
214362306a36Sopenharmony_ci		MII_VCT5_TX_PULSE_CTRL_MAX_AMP | width;
214462306a36Sopenharmony_ci
214562306a36Sopenharmony_ci	err = phy_write_paged(phydev, MII_MARVELL_VCT5_PAGE,
214662306a36Sopenharmony_ci			      MII_VCT5_TX_PULSE_CTRL, reg);
214762306a36Sopenharmony_ci	if (err)
214862306a36Sopenharmony_ci		return err;
214962306a36Sopenharmony_ci
215062306a36Sopenharmony_ci	/* Reading the TDR data is very MDIO heavy. We need to optimize
215162306a36Sopenharmony_ci	 * access to keep the time to a minimum. So lock the bus once,
215262306a36Sopenharmony_ci	 * and don't release it until complete. We can then avoid having
215362306a36Sopenharmony_ci	 * to change the page for every access, greatly speeding things
215462306a36Sopenharmony_ci	 * up.
215562306a36Sopenharmony_ci	 */
215662306a36Sopenharmony_ci	page = phy_select_page(phydev, MII_MARVELL_VCT5_PAGE);
215762306a36Sopenharmony_ci	if (page < 0)
215862306a36Sopenharmony_ci		goto restore_page;
215962306a36Sopenharmony_ci
216062306a36Sopenharmony_ci	for (distance = priv->first;
216162306a36Sopenharmony_ci	     distance <= priv->last;
216262306a36Sopenharmony_ci	     distance += priv->step) {
216362306a36Sopenharmony_ci		err = marvell_vct5_amplitude_distance(phydev, distance,
216462306a36Sopenharmony_ci						      priv->pair);
216562306a36Sopenharmony_ci		if (err)
216662306a36Sopenharmony_ci			goto restore_page;
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_ci		if (distance > TDR_SHORT_CABLE_LENGTH &&
216962306a36Sopenharmony_ci		    width == MII_VCT5_TX_PULSE_CTRL_PULSE_WIDTH_32nS) {
217062306a36Sopenharmony_ci			width = MII_VCT5_TX_PULSE_CTRL_PULSE_WIDTH_128nS;
217162306a36Sopenharmony_ci			reg = MII_VCT5_TX_PULSE_CTRL_GT_140m_46_86mV |
217262306a36Sopenharmony_ci				MII_VCT5_TX_PULSE_CTRL_DONT_WAIT_LINK_DOWN |
217362306a36Sopenharmony_ci				MII_VCT5_TX_PULSE_CTRL_MAX_AMP | width;
217462306a36Sopenharmony_ci			err = __phy_write(phydev, MII_VCT5_TX_PULSE_CTRL, reg);
217562306a36Sopenharmony_ci			if (err)
217662306a36Sopenharmony_ci				goto restore_page;
217762306a36Sopenharmony_ci		}
217862306a36Sopenharmony_ci	}
217962306a36Sopenharmony_ci
218062306a36Sopenharmony_cirestore_page:
218162306a36Sopenharmony_ci	return phy_restore_page(phydev, page, err);
218262306a36Sopenharmony_ci}
218362306a36Sopenharmony_ci
218462306a36Sopenharmony_cistatic int marvell_cable_test_start_common(struct phy_device *phydev)
218562306a36Sopenharmony_ci{
218662306a36Sopenharmony_ci	int bmcr, bmsr, ret;
218762306a36Sopenharmony_ci
218862306a36Sopenharmony_ci	/* If auto-negotiation is enabled, but not complete, the cable
218962306a36Sopenharmony_ci	 * test never completes. So disable auto-neg.
219062306a36Sopenharmony_ci	 */
219162306a36Sopenharmony_ci	bmcr = phy_read(phydev, MII_BMCR);
219262306a36Sopenharmony_ci	if (bmcr < 0)
219362306a36Sopenharmony_ci		return bmcr;
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_ci	bmsr = phy_read(phydev, MII_BMSR);
219662306a36Sopenharmony_ci
219762306a36Sopenharmony_ci	if (bmsr < 0)
219862306a36Sopenharmony_ci		return bmsr;
219962306a36Sopenharmony_ci
220062306a36Sopenharmony_ci	if (bmcr & BMCR_ANENABLE) {
220162306a36Sopenharmony_ci		ret =  phy_clear_bits(phydev, MII_BMCR, BMCR_ANENABLE);
220262306a36Sopenharmony_ci		if (ret < 0)
220362306a36Sopenharmony_ci			return ret;
220462306a36Sopenharmony_ci		ret = genphy_soft_reset(phydev);
220562306a36Sopenharmony_ci		if (ret < 0)
220662306a36Sopenharmony_ci			return ret;
220762306a36Sopenharmony_ci	}
220862306a36Sopenharmony_ci
220962306a36Sopenharmony_ci	/* If the link is up, allow it some time to go down */
221062306a36Sopenharmony_ci	if (bmsr & BMSR_LSTATUS)
221162306a36Sopenharmony_ci		msleep(1500);
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_ci	return 0;
221462306a36Sopenharmony_ci}
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_cistatic int marvell_vct7_cable_test_start(struct phy_device *phydev)
221762306a36Sopenharmony_ci{
221862306a36Sopenharmony_ci	struct marvell_priv *priv = phydev->priv;
221962306a36Sopenharmony_ci	int ret;
222062306a36Sopenharmony_ci
222162306a36Sopenharmony_ci	ret = marvell_cable_test_start_common(phydev);
222262306a36Sopenharmony_ci	if (ret)
222362306a36Sopenharmony_ci		return ret;
222462306a36Sopenharmony_ci
222562306a36Sopenharmony_ci	priv->cable_test_tdr = false;
222662306a36Sopenharmony_ci
222762306a36Sopenharmony_ci	/* Reset the VCT5 API control to defaults, otherwise
222862306a36Sopenharmony_ci	 * VCT7 does not work correctly.
222962306a36Sopenharmony_ci	 */
223062306a36Sopenharmony_ci	ret = phy_write_paged(phydev, MII_MARVELL_VCT5_PAGE,
223162306a36Sopenharmony_ci			      MII_VCT5_CTRL,
223262306a36Sopenharmony_ci			      MII_VCT5_CTRL_TX_SAME_CHANNEL |
223362306a36Sopenharmony_ci			      MII_VCT5_CTRL_SAMPLES_DEFAULT |
223462306a36Sopenharmony_ci			      MII_VCT5_CTRL_MODE_MAXIMUM_PEEK |
223562306a36Sopenharmony_ci			      MII_VCT5_CTRL_PEEK_HYST_DEFAULT);
223662306a36Sopenharmony_ci	if (ret)
223762306a36Sopenharmony_ci		return ret;
223862306a36Sopenharmony_ci
223962306a36Sopenharmony_ci	ret = phy_write_paged(phydev, MII_MARVELL_VCT5_PAGE,
224062306a36Sopenharmony_ci			      MII_VCT5_SAMPLE_POINT_DISTANCE, 0);
224162306a36Sopenharmony_ci	if (ret)
224262306a36Sopenharmony_ci		return ret;
224362306a36Sopenharmony_ci
224462306a36Sopenharmony_ci	return phy_write_paged(phydev, MII_MARVELL_VCT7_PAGE,
224562306a36Sopenharmony_ci			       MII_VCT7_CTRL,
224662306a36Sopenharmony_ci			       MII_VCT7_CTRL_RUN_NOW |
224762306a36Sopenharmony_ci			       MII_VCT7_CTRL_CENTIMETERS);
224862306a36Sopenharmony_ci}
224962306a36Sopenharmony_ci
225062306a36Sopenharmony_cistatic int marvell_vct5_cable_test_tdr_start(struct phy_device *phydev,
225162306a36Sopenharmony_ci					     const struct phy_tdr_config *cfg)
225262306a36Sopenharmony_ci{
225362306a36Sopenharmony_ci	struct marvell_priv *priv = phydev->priv;
225462306a36Sopenharmony_ci	int ret;
225562306a36Sopenharmony_ci
225662306a36Sopenharmony_ci	priv->cable_test_tdr = true;
225762306a36Sopenharmony_ci	priv->first = marvell_vct5_cm2distance(cfg->first);
225862306a36Sopenharmony_ci	priv->last = marvell_vct5_cm2distance(cfg->last);
225962306a36Sopenharmony_ci	priv->step = marvell_vct5_cm2distance(cfg->step);
226062306a36Sopenharmony_ci	priv->pair = cfg->pair;
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_ci	if (priv->first > MII_VCT5_SAMPLE_POINT_DISTANCE_MAX)
226362306a36Sopenharmony_ci		return -EINVAL;
226462306a36Sopenharmony_ci
226562306a36Sopenharmony_ci	if (priv->last > MII_VCT5_SAMPLE_POINT_DISTANCE_MAX)
226662306a36Sopenharmony_ci		return -EINVAL;
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_ci	/* Disable  VCT7 */
226962306a36Sopenharmony_ci	ret = phy_write_paged(phydev, MII_MARVELL_VCT7_PAGE,
227062306a36Sopenharmony_ci			      MII_VCT7_CTRL, 0);
227162306a36Sopenharmony_ci	if (ret)
227262306a36Sopenharmony_ci		return ret;
227362306a36Sopenharmony_ci
227462306a36Sopenharmony_ci	ret = marvell_cable_test_start_common(phydev);
227562306a36Sopenharmony_ci	if (ret)
227662306a36Sopenharmony_ci		return ret;
227762306a36Sopenharmony_ci
227862306a36Sopenharmony_ci	ret = ethnl_cable_test_pulse(phydev, 1000);
227962306a36Sopenharmony_ci	if (ret)
228062306a36Sopenharmony_ci		return ret;
228162306a36Sopenharmony_ci
228262306a36Sopenharmony_ci	return ethnl_cable_test_step(phydev,
228362306a36Sopenharmony_ci				     marvell_vct5_distance2cm(priv->first),
228462306a36Sopenharmony_ci				     marvell_vct5_distance2cm(priv->last),
228562306a36Sopenharmony_ci				     marvell_vct5_distance2cm(priv->step));
228662306a36Sopenharmony_ci}
228762306a36Sopenharmony_ci
228862306a36Sopenharmony_cistatic int marvell_vct7_distance_to_length(int distance, bool meter)
228962306a36Sopenharmony_ci{
229062306a36Sopenharmony_ci	if (meter)
229162306a36Sopenharmony_ci		distance *= 100;
229262306a36Sopenharmony_ci
229362306a36Sopenharmony_ci	return distance;
229462306a36Sopenharmony_ci}
229562306a36Sopenharmony_ci
229662306a36Sopenharmony_cistatic bool marvell_vct7_distance_valid(int result)
229762306a36Sopenharmony_ci{
229862306a36Sopenharmony_ci	switch (result) {
229962306a36Sopenharmony_ci	case MII_VCT7_RESULTS_OPEN:
230062306a36Sopenharmony_ci	case MII_VCT7_RESULTS_SAME_SHORT:
230162306a36Sopenharmony_ci	case MII_VCT7_RESULTS_CROSS_SHORT:
230262306a36Sopenharmony_ci		return true;
230362306a36Sopenharmony_ci	}
230462306a36Sopenharmony_ci	return false;
230562306a36Sopenharmony_ci}
230662306a36Sopenharmony_ci
230762306a36Sopenharmony_cistatic int marvell_vct7_report_length(struct phy_device *phydev,
230862306a36Sopenharmony_ci				      int pair, bool meter)
230962306a36Sopenharmony_ci{
231062306a36Sopenharmony_ci	int length;
231162306a36Sopenharmony_ci	int ret;
231262306a36Sopenharmony_ci
231362306a36Sopenharmony_ci	ret = phy_read_paged(phydev, MII_MARVELL_VCT7_PAGE,
231462306a36Sopenharmony_ci			     MII_VCT7_PAIR_0_DISTANCE + pair);
231562306a36Sopenharmony_ci	if (ret < 0)
231662306a36Sopenharmony_ci		return ret;
231762306a36Sopenharmony_ci
231862306a36Sopenharmony_ci	length = marvell_vct7_distance_to_length(ret, meter);
231962306a36Sopenharmony_ci
232062306a36Sopenharmony_ci	ethnl_cable_test_fault_length(phydev, pair, length);
232162306a36Sopenharmony_ci
232262306a36Sopenharmony_ci	return 0;
232362306a36Sopenharmony_ci}
232462306a36Sopenharmony_ci
232562306a36Sopenharmony_cistatic int marvell_vct7_cable_test_report_trans(int result)
232662306a36Sopenharmony_ci{
232762306a36Sopenharmony_ci	switch (result) {
232862306a36Sopenharmony_ci	case MII_VCT7_RESULTS_OK:
232962306a36Sopenharmony_ci		return ETHTOOL_A_CABLE_RESULT_CODE_OK;
233062306a36Sopenharmony_ci	case MII_VCT7_RESULTS_OPEN:
233162306a36Sopenharmony_ci		return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
233262306a36Sopenharmony_ci	case MII_VCT7_RESULTS_SAME_SHORT:
233362306a36Sopenharmony_ci		return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
233462306a36Sopenharmony_ci	case MII_VCT7_RESULTS_CROSS_SHORT:
233562306a36Sopenharmony_ci		return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
233662306a36Sopenharmony_ci	default:
233762306a36Sopenharmony_ci		return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
233862306a36Sopenharmony_ci	}
233962306a36Sopenharmony_ci}
234062306a36Sopenharmony_ci
234162306a36Sopenharmony_cistatic int marvell_vct7_cable_test_report(struct phy_device *phydev)
234262306a36Sopenharmony_ci{
234362306a36Sopenharmony_ci	int pair0, pair1, pair2, pair3;
234462306a36Sopenharmony_ci	bool meter;
234562306a36Sopenharmony_ci	int ret;
234662306a36Sopenharmony_ci
234762306a36Sopenharmony_ci	ret = phy_read_paged(phydev, MII_MARVELL_VCT7_PAGE,
234862306a36Sopenharmony_ci			     MII_VCT7_RESULTS);
234962306a36Sopenharmony_ci	if (ret < 0)
235062306a36Sopenharmony_ci		return ret;
235162306a36Sopenharmony_ci
235262306a36Sopenharmony_ci	pair3 = (ret & MII_VCT7_RESULTS_PAIR3_MASK) >>
235362306a36Sopenharmony_ci		MII_VCT7_RESULTS_PAIR3_SHIFT;
235462306a36Sopenharmony_ci	pair2 = (ret & MII_VCT7_RESULTS_PAIR2_MASK) >>
235562306a36Sopenharmony_ci		MII_VCT7_RESULTS_PAIR2_SHIFT;
235662306a36Sopenharmony_ci	pair1 = (ret & MII_VCT7_RESULTS_PAIR1_MASK) >>
235762306a36Sopenharmony_ci		MII_VCT7_RESULTS_PAIR1_SHIFT;
235862306a36Sopenharmony_ci	pair0 = (ret & MII_VCT7_RESULTS_PAIR0_MASK) >>
235962306a36Sopenharmony_ci		MII_VCT7_RESULTS_PAIR0_SHIFT;
236062306a36Sopenharmony_ci
236162306a36Sopenharmony_ci	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,
236262306a36Sopenharmony_ci				marvell_vct7_cable_test_report_trans(pair0));
236362306a36Sopenharmony_ci	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_B,
236462306a36Sopenharmony_ci				marvell_vct7_cable_test_report_trans(pair1));
236562306a36Sopenharmony_ci	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_C,
236662306a36Sopenharmony_ci				marvell_vct7_cable_test_report_trans(pair2));
236762306a36Sopenharmony_ci	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D,
236862306a36Sopenharmony_ci				marvell_vct7_cable_test_report_trans(pair3));
236962306a36Sopenharmony_ci
237062306a36Sopenharmony_ci	ret = phy_read_paged(phydev, MII_MARVELL_VCT7_PAGE, MII_VCT7_CTRL);
237162306a36Sopenharmony_ci	if (ret < 0)
237262306a36Sopenharmony_ci		return ret;
237362306a36Sopenharmony_ci
237462306a36Sopenharmony_ci	meter = ret & MII_VCT7_CTRL_METERS;
237562306a36Sopenharmony_ci
237662306a36Sopenharmony_ci	if (marvell_vct7_distance_valid(pair0))
237762306a36Sopenharmony_ci		marvell_vct7_report_length(phydev, 0, meter);
237862306a36Sopenharmony_ci	if (marvell_vct7_distance_valid(pair1))
237962306a36Sopenharmony_ci		marvell_vct7_report_length(phydev, 1, meter);
238062306a36Sopenharmony_ci	if (marvell_vct7_distance_valid(pair2))
238162306a36Sopenharmony_ci		marvell_vct7_report_length(phydev, 2, meter);
238262306a36Sopenharmony_ci	if (marvell_vct7_distance_valid(pair3))
238362306a36Sopenharmony_ci		marvell_vct7_report_length(phydev, 3, meter);
238462306a36Sopenharmony_ci
238562306a36Sopenharmony_ci	return 0;
238662306a36Sopenharmony_ci}
238762306a36Sopenharmony_ci
238862306a36Sopenharmony_cistatic int marvell_vct7_cable_test_get_status(struct phy_device *phydev,
238962306a36Sopenharmony_ci					      bool *finished)
239062306a36Sopenharmony_ci{
239162306a36Sopenharmony_ci	struct marvell_priv *priv = phydev->priv;
239262306a36Sopenharmony_ci	int ret;
239362306a36Sopenharmony_ci
239462306a36Sopenharmony_ci	if (priv->cable_test_tdr) {
239562306a36Sopenharmony_ci		ret = marvell_vct5_amplitude_graph(phydev);
239662306a36Sopenharmony_ci		*finished = true;
239762306a36Sopenharmony_ci		return ret;
239862306a36Sopenharmony_ci	}
239962306a36Sopenharmony_ci
240062306a36Sopenharmony_ci	*finished = false;
240162306a36Sopenharmony_ci
240262306a36Sopenharmony_ci	ret = phy_read_paged(phydev, MII_MARVELL_VCT7_PAGE,
240362306a36Sopenharmony_ci			     MII_VCT7_CTRL);
240462306a36Sopenharmony_ci
240562306a36Sopenharmony_ci	if (ret < 0)
240662306a36Sopenharmony_ci		return ret;
240762306a36Sopenharmony_ci
240862306a36Sopenharmony_ci	if (!(ret & MII_VCT7_CTRL_IN_PROGRESS)) {
240962306a36Sopenharmony_ci		*finished = true;
241062306a36Sopenharmony_ci
241162306a36Sopenharmony_ci		return marvell_vct7_cable_test_report(phydev);
241262306a36Sopenharmony_ci	}
241362306a36Sopenharmony_ci
241462306a36Sopenharmony_ci	return 0;
241562306a36Sopenharmony_ci}
241662306a36Sopenharmony_ci
241762306a36Sopenharmony_ci#ifdef CONFIG_HWMON
241862306a36Sopenharmony_cistruct marvell_hwmon_ops {
241962306a36Sopenharmony_ci	int (*config)(struct phy_device *phydev);
242062306a36Sopenharmony_ci	int (*get_temp)(struct phy_device *phydev, long *temp);
242162306a36Sopenharmony_ci	int (*get_temp_critical)(struct phy_device *phydev, long *temp);
242262306a36Sopenharmony_ci	int (*set_temp_critical)(struct phy_device *phydev, long temp);
242362306a36Sopenharmony_ci	int (*get_temp_alarm)(struct phy_device *phydev, long *alarm);
242462306a36Sopenharmony_ci};
242562306a36Sopenharmony_ci
242662306a36Sopenharmony_cistatic const struct marvell_hwmon_ops *
242762306a36Sopenharmony_cito_marvell_hwmon_ops(const struct phy_device *phydev)
242862306a36Sopenharmony_ci{
242962306a36Sopenharmony_ci	return phydev->drv->driver_data;
243062306a36Sopenharmony_ci}
243162306a36Sopenharmony_ci
243262306a36Sopenharmony_cistatic int m88e1121_get_temp(struct phy_device *phydev, long *temp)
243362306a36Sopenharmony_ci{
243462306a36Sopenharmony_ci	int oldpage;
243562306a36Sopenharmony_ci	int ret = 0;
243662306a36Sopenharmony_ci	int val;
243762306a36Sopenharmony_ci
243862306a36Sopenharmony_ci	*temp = 0;
243962306a36Sopenharmony_ci
244062306a36Sopenharmony_ci	oldpage = phy_select_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
244162306a36Sopenharmony_ci	if (oldpage < 0)
244262306a36Sopenharmony_ci		goto error;
244362306a36Sopenharmony_ci
244462306a36Sopenharmony_ci	/* Enable temperature sensor */
244562306a36Sopenharmony_ci	ret = __phy_read(phydev, MII_88E1121_MISC_TEST);
244662306a36Sopenharmony_ci	if (ret < 0)
244762306a36Sopenharmony_ci		goto error;
244862306a36Sopenharmony_ci
244962306a36Sopenharmony_ci	ret = __phy_write(phydev, MII_88E1121_MISC_TEST,
245062306a36Sopenharmony_ci			  ret | MII_88E1121_MISC_TEST_TEMP_SENSOR_EN);
245162306a36Sopenharmony_ci	if (ret < 0)
245262306a36Sopenharmony_ci		goto error;
245362306a36Sopenharmony_ci
245462306a36Sopenharmony_ci	/* Wait for temperature to stabilize */
245562306a36Sopenharmony_ci	usleep_range(10000, 12000);
245662306a36Sopenharmony_ci
245762306a36Sopenharmony_ci	val = __phy_read(phydev, MII_88E1121_MISC_TEST);
245862306a36Sopenharmony_ci	if (val < 0) {
245962306a36Sopenharmony_ci		ret = val;
246062306a36Sopenharmony_ci		goto error;
246162306a36Sopenharmony_ci	}
246262306a36Sopenharmony_ci
246362306a36Sopenharmony_ci	/* Disable temperature sensor */
246462306a36Sopenharmony_ci	ret = __phy_write(phydev, MII_88E1121_MISC_TEST,
246562306a36Sopenharmony_ci			  ret & ~MII_88E1121_MISC_TEST_TEMP_SENSOR_EN);
246662306a36Sopenharmony_ci	if (ret < 0)
246762306a36Sopenharmony_ci		goto error;
246862306a36Sopenharmony_ci
246962306a36Sopenharmony_ci	*temp = ((val & MII_88E1121_MISC_TEST_TEMP_MASK) - 5) * 5000;
247062306a36Sopenharmony_ci
247162306a36Sopenharmony_cierror:
247262306a36Sopenharmony_ci	return phy_restore_page(phydev, oldpage, ret);
247362306a36Sopenharmony_ci}
247462306a36Sopenharmony_ci
247562306a36Sopenharmony_cistatic int m88e1510_get_temp(struct phy_device *phydev, long *temp)
247662306a36Sopenharmony_ci{
247762306a36Sopenharmony_ci	int ret;
247862306a36Sopenharmony_ci
247962306a36Sopenharmony_ci	*temp = 0;
248062306a36Sopenharmony_ci
248162306a36Sopenharmony_ci	ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
248262306a36Sopenharmony_ci			     MII_88E1510_TEMP_SENSOR);
248362306a36Sopenharmony_ci	if (ret < 0)
248462306a36Sopenharmony_ci		return ret;
248562306a36Sopenharmony_ci
248662306a36Sopenharmony_ci	*temp = ((ret & MII_88E1510_TEMP_SENSOR_MASK) - 25) * 1000;
248762306a36Sopenharmony_ci
248862306a36Sopenharmony_ci	return 0;
248962306a36Sopenharmony_ci}
249062306a36Sopenharmony_ci
249162306a36Sopenharmony_cistatic int m88e1510_get_temp_critical(struct phy_device *phydev, long *temp)
249262306a36Sopenharmony_ci{
249362306a36Sopenharmony_ci	int ret;
249462306a36Sopenharmony_ci
249562306a36Sopenharmony_ci	*temp = 0;
249662306a36Sopenharmony_ci
249762306a36Sopenharmony_ci	ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
249862306a36Sopenharmony_ci			     MII_88E1121_MISC_TEST);
249962306a36Sopenharmony_ci	if (ret < 0)
250062306a36Sopenharmony_ci		return ret;
250162306a36Sopenharmony_ci
250262306a36Sopenharmony_ci	*temp = (((ret & MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK) >>
250362306a36Sopenharmony_ci		  MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT) * 5) - 25;
250462306a36Sopenharmony_ci	/* convert to mC */
250562306a36Sopenharmony_ci	*temp *= 1000;
250662306a36Sopenharmony_ci
250762306a36Sopenharmony_ci	return 0;
250862306a36Sopenharmony_ci}
250962306a36Sopenharmony_ci
251062306a36Sopenharmony_cistatic int m88e1510_set_temp_critical(struct phy_device *phydev, long temp)
251162306a36Sopenharmony_ci{
251262306a36Sopenharmony_ci	temp = temp / 1000;
251362306a36Sopenharmony_ci	temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
251462306a36Sopenharmony_ci
251562306a36Sopenharmony_ci	return phy_modify_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
251662306a36Sopenharmony_ci				MII_88E1121_MISC_TEST,
251762306a36Sopenharmony_ci				MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK,
251862306a36Sopenharmony_ci				temp << MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT);
251962306a36Sopenharmony_ci}
252062306a36Sopenharmony_ci
252162306a36Sopenharmony_cistatic int m88e1510_get_temp_alarm(struct phy_device *phydev, long *alarm)
252262306a36Sopenharmony_ci{
252362306a36Sopenharmony_ci	int ret;
252462306a36Sopenharmony_ci
252562306a36Sopenharmony_ci	*alarm = false;
252662306a36Sopenharmony_ci
252762306a36Sopenharmony_ci	ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
252862306a36Sopenharmony_ci			     MII_88E1121_MISC_TEST);
252962306a36Sopenharmony_ci	if (ret < 0)
253062306a36Sopenharmony_ci		return ret;
253162306a36Sopenharmony_ci
253262306a36Sopenharmony_ci	*alarm = !!(ret & MII_88E1510_MISC_TEST_TEMP_IRQ);
253362306a36Sopenharmony_ci
253462306a36Sopenharmony_ci	return 0;
253562306a36Sopenharmony_ci}
253662306a36Sopenharmony_ci
253762306a36Sopenharmony_cistatic int m88e6390_get_temp(struct phy_device *phydev, long *temp)
253862306a36Sopenharmony_ci{
253962306a36Sopenharmony_ci	int sum = 0;
254062306a36Sopenharmony_ci	int oldpage;
254162306a36Sopenharmony_ci	int ret = 0;
254262306a36Sopenharmony_ci	int i;
254362306a36Sopenharmony_ci
254462306a36Sopenharmony_ci	*temp = 0;
254562306a36Sopenharmony_ci
254662306a36Sopenharmony_ci	oldpage = phy_select_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
254762306a36Sopenharmony_ci	if (oldpage < 0)
254862306a36Sopenharmony_ci		goto error;
254962306a36Sopenharmony_ci
255062306a36Sopenharmony_ci	/* Enable temperature sensor */
255162306a36Sopenharmony_ci	ret = __phy_read(phydev, MII_88E6390_MISC_TEST);
255262306a36Sopenharmony_ci	if (ret < 0)
255362306a36Sopenharmony_ci		goto error;
255462306a36Sopenharmony_ci
255562306a36Sopenharmony_ci	ret &= ~MII_88E6390_MISC_TEST_TEMP_SENSOR_MASK;
255662306a36Sopenharmony_ci	ret |= MII_88E6390_MISC_TEST_TEMP_SENSOR_ENABLE_SAMPLE_1S;
255762306a36Sopenharmony_ci
255862306a36Sopenharmony_ci	ret = __phy_write(phydev, MII_88E6390_MISC_TEST, ret);
255962306a36Sopenharmony_ci	if (ret < 0)
256062306a36Sopenharmony_ci		goto error;
256162306a36Sopenharmony_ci
256262306a36Sopenharmony_ci	/* Wait for temperature to stabilize */
256362306a36Sopenharmony_ci	usleep_range(10000, 12000);
256462306a36Sopenharmony_ci
256562306a36Sopenharmony_ci	/* Reading the temperature sense has an errata. You need to read
256662306a36Sopenharmony_ci	 * a number of times and take an average.
256762306a36Sopenharmony_ci	 */
256862306a36Sopenharmony_ci	for (i = 0; i < MII_88E6390_TEMP_SENSOR_SAMPLES; i++) {
256962306a36Sopenharmony_ci		ret = __phy_read(phydev, MII_88E6390_TEMP_SENSOR);
257062306a36Sopenharmony_ci		if (ret < 0)
257162306a36Sopenharmony_ci			goto error;
257262306a36Sopenharmony_ci		sum += ret & MII_88E6390_TEMP_SENSOR_MASK;
257362306a36Sopenharmony_ci	}
257462306a36Sopenharmony_ci
257562306a36Sopenharmony_ci	sum /= MII_88E6390_TEMP_SENSOR_SAMPLES;
257662306a36Sopenharmony_ci	*temp = (sum  - 75) * 1000;
257762306a36Sopenharmony_ci
257862306a36Sopenharmony_ci	/* Disable temperature sensor */
257962306a36Sopenharmony_ci	ret = __phy_read(phydev, MII_88E6390_MISC_TEST);
258062306a36Sopenharmony_ci	if (ret < 0)
258162306a36Sopenharmony_ci		goto error;
258262306a36Sopenharmony_ci
258362306a36Sopenharmony_ci	ret = ret & ~MII_88E6390_MISC_TEST_TEMP_SENSOR_MASK;
258462306a36Sopenharmony_ci	ret |= MII_88E6390_MISC_TEST_TEMP_SENSOR_DISABLE;
258562306a36Sopenharmony_ci
258662306a36Sopenharmony_ci	ret = __phy_write(phydev, MII_88E6390_MISC_TEST, ret);
258762306a36Sopenharmony_ci
258862306a36Sopenharmony_cierror:
258962306a36Sopenharmony_ci	phy_restore_page(phydev, oldpage, ret);
259062306a36Sopenharmony_ci
259162306a36Sopenharmony_ci	return ret;
259262306a36Sopenharmony_ci}
259362306a36Sopenharmony_ci
259462306a36Sopenharmony_cistatic int m88e6393_get_temp(struct phy_device *phydev, long *temp)
259562306a36Sopenharmony_ci{
259662306a36Sopenharmony_ci	int err;
259762306a36Sopenharmony_ci
259862306a36Sopenharmony_ci	err = m88e1510_get_temp(phydev, temp);
259962306a36Sopenharmony_ci
260062306a36Sopenharmony_ci	/* 88E1510 measures T + 25, while the PHY on 88E6393X switch
260162306a36Sopenharmony_ci	 * T + 75, so we have to subtract another 50
260262306a36Sopenharmony_ci	 */
260362306a36Sopenharmony_ci	*temp -= 50000;
260462306a36Sopenharmony_ci
260562306a36Sopenharmony_ci	return err;
260662306a36Sopenharmony_ci}
260762306a36Sopenharmony_ci
260862306a36Sopenharmony_cistatic int m88e6393_get_temp_critical(struct phy_device *phydev, long *temp)
260962306a36Sopenharmony_ci{
261062306a36Sopenharmony_ci	int ret;
261162306a36Sopenharmony_ci
261262306a36Sopenharmony_ci	*temp = 0;
261362306a36Sopenharmony_ci
261462306a36Sopenharmony_ci	ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
261562306a36Sopenharmony_ci			     MII_88E6390_TEMP_SENSOR);
261662306a36Sopenharmony_ci	if (ret < 0)
261762306a36Sopenharmony_ci		return ret;
261862306a36Sopenharmony_ci
261962306a36Sopenharmony_ci	*temp = (((ret & MII_88E6393_TEMP_SENSOR_THRESHOLD_MASK) >>
262062306a36Sopenharmony_ci		  MII_88E6393_TEMP_SENSOR_THRESHOLD_SHIFT) - 75) * 1000;
262162306a36Sopenharmony_ci
262262306a36Sopenharmony_ci	return 0;
262362306a36Sopenharmony_ci}
262462306a36Sopenharmony_ci
262562306a36Sopenharmony_cistatic int m88e6393_set_temp_critical(struct phy_device *phydev, long temp)
262662306a36Sopenharmony_ci{
262762306a36Sopenharmony_ci	temp = (temp / 1000) + 75;
262862306a36Sopenharmony_ci
262962306a36Sopenharmony_ci	return phy_modify_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
263062306a36Sopenharmony_ci				MII_88E6390_TEMP_SENSOR,
263162306a36Sopenharmony_ci				MII_88E6393_TEMP_SENSOR_THRESHOLD_MASK,
263262306a36Sopenharmony_ci				temp << MII_88E6393_TEMP_SENSOR_THRESHOLD_SHIFT);
263362306a36Sopenharmony_ci}
263462306a36Sopenharmony_ci
263562306a36Sopenharmony_cistatic int m88e6393_hwmon_config(struct phy_device *phydev)
263662306a36Sopenharmony_ci{
263762306a36Sopenharmony_ci	int err;
263862306a36Sopenharmony_ci
263962306a36Sopenharmony_ci	err = m88e6393_set_temp_critical(phydev, 100000);
264062306a36Sopenharmony_ci	if (err)
264162306a36Sopenharmony_ci		return err;
264262306a36Sopenharmony_ci
264362306a36Sopenharmony_ci	return phy_modify_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
264462306a36Sopenharmony_ci				MII_88E6390_MISC_TEST,
264562306a36Sopenharmony_ci				MII_88E6390_MISC_TEST_TEMP_SENSOR_MASK |
264662306a36Sopenharmony_ci				MII_88E6393_MISC_TEST_SAMPLES_MASK |
264762306a36Sopenharmony_ci				MII_88E6393_MISC_TEST_RATE_MASK,
264862306a36Sopenharmony_ci				MII_88E6390_MISC_TEST_TEMP_SENSOR_ENABLE |
264962306a36Sopenharmony_ci				MII_88E6393_MISC_TEST_SAMPLES_2048 |
265062306a36Sopenharmony_ci				MII_88E6393_MISC_TEST_RATE_2_3MS);
265162306a36Sopenharmony_ci}
265262306a36Sopenharmony_ci
265362306a36Sopenharmony_cistatic int marvell_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
265462306a36Sopenharmony_ci			      u32 attr, int channel, long *temp)
265562306a36Sopenharmony_ci{
265662306a36Sopenharmony_ci	struct phy_device *phydev = dev_get_drvdata(dev);
265762306a36Sopenharmony_ci	const struct marvell_hwmon_ops *ops = to_marvell_hwmon_ops(phydev);
265862306a36Sopenharmony_ci	int err = -EOPNOTSUPP;
265962306a36Sopenharmony_ci
266062306a36Sopenharmony_ci	switch (attr) {
266162306a36Sopenharmony_ci	case hwmon_temp_input:
266262306a36Sopenharmony_ci		if (ops->get_temp)
266362306a36Sopenharmony_ci			err = ops->get_temp(phydev, temp);
266462306a36Sopenharmony_ci		break;
266562306a36Sopenharmony_ci	case hwmon_temp_crit:
266662306a36Sopenharmony_ci		if (ops->get_temp_critical)
266762306a36Sopenharmony_ci			err = ops->get_temp_critical(phydev, temp);
266862306a36Sopenharmony_ci		break;
266962306a36Sopenharmony_ci	case hwmon_temp_max_alarm:
267062306a36Sopenharmony_ci		if (ops->get_temp_alarm)
267162306a36Sopenharmony_ci			err = ops->get_temp_alarm(phydev, temp);
267262306a36Sopenharmony_ci		break;
267362306a36Sopenharmony_ci	}
267462306a36Sopenharmony_ci
267562306a36Sopenharmony_ci	return err;
267662306a36Sopenharmony_ci}
267762306a36Sopenharmony_ci
267862306a36Sopenharmony_cistatic int marvell_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
267962306a36Sopenharmony_ci			       u32 attr, int channel, long temp)
268062306a36Sopenharmony_ci{
268162306a36Sopenharmony_ci	struct phy_device *phydev = dev_get_drvdata(dev);
268262306a36Sopenharmony_ci	const struct marvell_hwmon_ops *ops = to_marvell_hwmon_ops(phydev);
268362306a36Sopenharmony_ci	int err = -EOPNOTSUPP;
268462306a36Sopenharmony_ci
268562306a36Sopenharmony_ci	switch (attr) {
268662306a36Sopenharmony_ci	case hwmon_temp_crit:
268762306a36Sopenharmony_ci		if (ops->set_temp_critical)
268862306a36Sopenharmony_ci			err = ops->set_temp_critical(phydev, temp);
268962306a36Sopenharmony_ci		break;
269062306a36Sopenharmony_ci	}
269162306a36Sopenharmony_ci
269262306a36Sopenharmony_ci	return err;
269362306a36Sopenharmony_ci}
269462306a36Sopenharmony_ci
269562306a36Sopenharmony_cistatic umode_t marvell_hwmon_is_visible(const void *data,
269662306a36Sopenharmony_ci					enum hwmon_sensor_types type,
269762306a36Sopenharmony_ci					u32 attr, int channel)
269862306a36Sopenharmony_ci{
269962306a36Sopenharmony_ci	const struct phy_device *phydev = data;
270062306a36Sopenharmony_ci	const struct marvell_hwmon_ops *ops = to_marvell_hwmon_ops(phydev);
270162306a36Sopenharmony_ci
270262306a36Sopenharmony_ci	if (type != hwmon_temp)
270362306a36Sopenharmony_ci		return 0;
270462306a36Sopenharmony_ci
270562306a36Sopenharmony_ci	switch (attr) {
270662306a36Sopenharmony_ci	case hwmon_temp_input:
270762306a36Sopenharmony_ci		return ops->get_temp ? 0444 : 0;
270862306a36Sopenharmony_ci	case hwmon_temp_max_alarm:
270962306a36Sopenharmony_ci		return ops->get_temp_alarm ? 0444 : 0;
271062306a36Sopenharmony_ci	case hwmon_temp_crit:
271162306a36Sopenharmony_ci		return (ops->get_temp_critical ? 0444 : 0) |
271262306a36Sopenharmony_ci		       (ops->set_temp_critical ? 0200 : 0);
271362306a36Sopenharmony_ci	default:
271462306a36Sopenharmony_ci		return 0;
271562306a36Sopenharmony_ci	}
271662306a36Sopenharmony_ci}
271762306a36Sopenharmony_ci
271862306a36Sopenharmony_cistatic u32 marvell_hwmon_chip_config[] = {
271962306a36Sopenharmony_ci	HWMON_C_REGISTER_TZ,
272062306a36Sopenharmony_ci	0
272162306a36Sopenharmony_ci};
272262306a36Sopenharmony_ci
272362306a36Sopenharmony_cistatic const struct hwmon_channel_info marvell_hwmon_chip = {
272462306a36Sopenharmony_ci	.type = hwmon_chip,
272562306a36Sopenharmony_ci	.config = marvell_hwmon_chip_config,
272662306a36Sopenharmony_ci};
272762306a36Sopenharmony_ci
272862306a36Sopenharmony_ci/* we can define HWMON_T_CRIT and HWMON_T_MAX_ALARM even though these are not
272962306a36Sopenharmony_ci * defined for all PHYs, because the hwmon code checks whether the attributes
273062306a36Sopenharmony_ci * exists via the .is_visible method
273162306a36Sopenharmony_ci */
273262306a36Sopenharmony_cistatic u32 marvell_hwmon_temp_config[] = {
273362306a36Sopenharmony_ci	HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_MAX_ALARM,
273462306a36Sopenharmony_ci	0
273562306a36Sopenharmony_ci};
273662306a36Sopenharmony_ci
273762306a36Sopenharmony_cistatic const struct hwmon_channel_info marvell_hwmon_temp = {
273862306a36Sopenharmony_ci	.type = hwmon_temp,
273962306a36Sopenharmony_ci	.config = marvell_hwmon_temp_config,
274062306a36Sopenharmony_ci};
274162306a36Sopenharmony_ci
274262306a36Sopenharmony_cistatic const struct hwmon_channel_info * const marvell_hwmon_info[] = {
274362306a36Sopenharmony_ci	&marvell_hwmon_chip,
274462306a36Sopenharmony_ci	&marvell_hwmon_temp,
274562306a36Sopenharmony_ci	NULL
274662306a36Sopenharmony_ci};
274762306a36Sopenharmony_ci
274862306a36Sopenharmony_cistatic const struct hwmon_ops marvell_hwmon_hwmon_ops = {
274962306a36Sopenharmony_ci	.is_visible = marvell_hwmon_is_visible,
275062306a36Sopenharmony_ci	.read = marvell_hwmon_read,
275162306a36Sopenharmony_ci	.write = marvell_hwmon_write,
275262306a36Sopenharmony_ci};
275362306a36Sopenharmony_ci
275462306a36Sopenharmony_cistatic const struct hwmon_chip_info marvell_hwmon_chip_info = {
275562306a36Sopenharmony_ci	.ops = &marvell_hwmon_hwmon_ops,
275662306a36Sopenharmony_ci	.info = marvell_hwmon_info,
275762306a36Sopenharmony_ci};
275862306a36Sopenharmony_ci
275962306a36Sopenharmony_cistatic int marvell_hwmon_name(struct phy_device *phydev)
276062306a36Sopenharmony_ci{
276162306a36Sopenharmony_ci	struct marvell_priv *priv = phydev->priv;
276262306a36Sopenharmony_ci	struct device *dev = &phydev->mdio.dev;
276362306a36Sopenharmony_ci	const char *devname = dev_name(dev);
276462306a36Sopenharmony_ci	size_t len = strlen(devname);
276562306a36Sopenharmony_ci	int i, j;
276662306a36Sopenharmony_ci
276762306a36Sopenharmony_ci	priv->hwmon_name = devm_kzalloc(dev, len, GFP_KERNEL);
276862306a36Sopenharmony_ci	if (!priv->hwmon_name)
276962306a36Sopenharmony_ci		return -ENOMEM;
277062306a36Sopenharmony_ci
277162306a36Sopenharmony_ci	for (i = j = 0; i < len && devname[i]; i++) {
277262306a36Sopenharmony_ci		if (isalnum(devname[i]))
277362306a36Sopenharmony_ci			priv->hwmon_name[j++] = devname[i];
277462306a36Sopenharmony_ci	}
277562306a36Sopenharmony_ci
277662306a36Sopenharmony_ci	return 0;
277762306a36Sopenharmony_ci}
277862306a36Sopenharmony_ci
277962306a36Sopenharmony_cistatic int marvell_hwmon_probe(struct phy_device *phydev)
278062306a36Sopenharmony_ci{
278162306a36Sopenharmony_ci	const struct marvell_hwmon_ops *ops = to_marvell_hwmon_ops(phydev);
278262306a36Sopenharmony_ci	struct marvell_priv *priv = phydev->priv;
278362306a36Sopenharmony_ci	struct device *dev = &phydev->mdio.dev;
278462306a36Sopenharmony_ci	int err;
278562306a36Sopenharmony_ci
278662306a36Sopenharmony_ci	if (!ops)
278762306a36Sopenharmony_ci		return 0;
278862306a36Sopenharmony_ci
278962306a36Sopenharmony_ci	err = marvell_hwmon_name(phydev);
279062306a36Sopenharmony_ci	if (err)
279162306a36Sopenharmony_ci		return err;
279262306a36Sopenharmony_ci
279362306a36Sopenharmony_ci	priv->hwmon_dev = devm_hwmon_device_register_with_info(
279462306a36Sopenharmony_ci		dev, priv->hwmon_name, phydev, &marvell_hwmon_chip_info, NULL);
279562306a36Sopenharmony_ci	if (IS_ERR(priv->hwmon_dev))
279662306a36Sopenharmony_ci		return PTR_ERR(priv->hwmon_dev);
279762306a36Sopenharmony_ci
279862306a36Sopenharmony_ci	if (ops->config)
279962306a36Sopenharmony_ci		err = ops->config(phydev);
280062306a36Sopenharmony_ci
280162306a36Sopenharmony_ci	return err;
280262306a36Sopenharmony_ci}
280362306a36Sopenharmony_ci
280462306a36Sopenharmony_cistatic const struct marvell_hwmon_ops m88e1121_hwmon_ops = {
280562306a36Sopenharmony_ci	.get_temp = m88e1121_get_temp,
280662306a36Sopenharmony_ci};
280762306a36Sopenharmony_ci
280862306a36Sopenharmony_cistatic const struct marvell_hwmon_ops m88e1510_hwmon_ops = {
280962306a36Sopenharmony_ci	.get_temp = m88e1510_get_temp,
281062306a36Sopenharmony_ci	.get_temp_critical = m88e1510_get_temp_critical,
281162306a36Sopenharmony_ci	.set_temp_critical = m88e1510_set_temp_critical,
281262306a36Sopenharmony_ci	.get_temp_alarm = m88e1510_get_temp_alarm,
281362306a36Sopenharmony_ci};
281462306a36Sopenharmony_ci
281562306a36Sopenharmony_cistatic const struct marvell_hwmon_ops m88e6390_hwmon_ops = {
281662306a36Sopenharmony_ci	.get_temp = m88e6390_get_temp,
281762306a36Sopenharmony_ci};
281862306a36Sopenharmony_ci
281962306a36Sopenharmony_cistatic const struct marvell_hwmon_ops m88e6393_hwmon_ops = {
282062306a36Sopenharmony_ci	.config = m88e6393_hwmon_config,
282162306a36Sopenharmony_ci	.get_temp = m88e6393_get_temp,
282262306a36Sopenharmony_ci	.get_temp_critical = m88e6393_get_temp_critical,
282362306a36Sopenharmony_ci	.set_temp_critical = m88e6393_set_temp_critical,
282462306a36Sopenharmony_ci	.get_temp_alarm = m88e1510_get_temp_alarm,
282562306a36Sopenharmony_ci};
282662306a36Sopenharmony_ci
282762306a36Sopenharmony_ci#define DEF_MARVELL_HWMON_OPS(s) (&(s))
282862306a36Sopenharmony_ci
282962306a36Sopenharmony_ci#else
283062306a36Sopenharmony_ci
283162306a36Sopenharmony_ci#define DEF_MARVELL_HWMON_OPS(s) NULL
283262306a36Sopenharmony_ci
283362306a36Sopenharmony_cistatic int marvell_hwmon_probe(struct phy_device *phydev)
283462306a36Sopenharmony_ci{
283562306a36Sopenharmony_ci	return 0;
283662306a36Sopenharmony_ci}
283762306a36Sopenharmony_ci#endif
283862306a36Sopenharmony_ci
283962306a36Sopenharmony_cistatic int m88e1318_led_brightness_set(struct phy_device *phydev,
284062306a36Sopenharmony_ci				       u8 index, enum led_brightness value)
284162306a36Sopenharmony_ci{
284262306a36Sopenharmony_ci	int reg;
284362306a36Sopenharmony_ci
284462306a36Sopenharmony_ci	reg = phy_read_paged(phydev, MII_MARVELL_LED_PAGE,
284562306a36Sopenharmony_ci			     MII_88E1318S_PHY_LED_FUNC);
284662306a36Sopenharmony_ci	if (reg < 0)
284762306a36Sopenharmony_ci		return reg;
284862306a36Sopenharmony_ci
284962306a36Sopenharmony_ci	switch (index) {
285062306a36Sopenharmony_ci	case 0:
285162306a36Sopenharmony_ci	case 1:
285262306a36Sopenharmony_ci	case 2:
285362306a36Sopenharmony_ci		reg &= ~(0xf << (4 * index));
285462306a36Sopenharmony_ci		if (value == LED_OFF)
285562306a36Sopenharmony_ci			reg |= MII_88E1318S_PHY_LED_FUNC_OFF << (4 * index);
285662306a36Sopenharmony_ci		else
285762306a36Sopenharmony_ci			reg |= MII_88E1318S_PHY_LED_FUNC_ON << (4 * index);
285862306a36Sopenharmony_ci		break;
285962306a36Sopenharmony_ci	default:
286062306a36Sopenharmony_ci		return -EINVAL;
286162306a36Sopenharmony_ci	}
286262306a36Sopenharmony_ci
286362306a36Sopenharmony_ci	return phy_write_paged(phydev, MII_MARVELL_LED_PAGE,
286462306a36Sopenharmony_ci			       MII_88E1318S_PHY_LED_FUNC, reg);
286562306a36Sopenharmony_ci}
286662306a36Sopenharmony_ci
286762306a36Sopenharmony_cistatic int m88e1318_led_blink_set(struct phy_device *phydev, u8 index,
286862306a36Sopenharmony_ci				  unsigned long *delay_on,
286962306a36Sopenharmony_ci				  unsigned long *delay_off)
287062306a36Sopenharmony_ci{
287162306a36Sopenharmony_ci	int reg;
287262306a36Sopenharmony_ci
287362306a36Sopenharmony_ci	reg = phy_read_paged(phydev, MII_MARVELL_LED_PAGE,
287462306a36Sopenharmony_ci			     MII_88E1318S_PHY_LED_FUNC);
287562306a36Sopenharmony_ci	if (reg < 0)
287662306a36Sopenharmony_ci		return reg;
287762306a36Sopenharmony_ci
287862306a36Sopenharmony_ci	switch (index) {
287962306a36Sopenharmony_ci	case 0:
288062306a36Sopenharmony_ci	case 1:
288162306a36Sopenharmony_ci	case 2:
288262306a36Sopenharmony_ci		reg &= ~(0xf << (4 * index));
288362306a36Sopenharmony_ci		reg |= MII_88E1318S_PHY_LED_FUNC_BLINK << (4 * index);
288462306a36Sopenharmony_ci		/* Reset default is 84ms */
288562306a36Sopenharmony_ci		*delay_on = 84 / 2;
288662306a36Sopenharmony_ci		*delay_off = 84 / 2;
288762306a36Sopenharmony_ci		break;
288862306a36Sopenharmony_ci	default:
288962306a36Sopenharmony_ci		return -EINVAL;
289062306a36Sopenharmony_ci	}
289162306a36Sopenharmony_ci
289262306a36Sopenharmony_ci	return phy_write_paged(phydev, MII_MARVELL_LED_PAGE,
289362306a36Sopenharmony_ci			       MII_88E1318S_PHY_LED_FUNC, reg);
289462306a36Sopenharmony_ci}
289562306a36Sopenharmony_ci
289662306a36Sopenharmony_cistruct marvell_led_rules {
289762306a36Sopenharmony_ci	int mode;
289862306a36Sopenharmony_ci	unsigned long rules;
289962306a36Sopenharmony_ci};
290062306a36Sopenharmony_ci
290162306a36Sopenharmony_cistatic const struct marvell_led_rules marvell_led0[] = {
290262306a36Sopenharmony_ci	{
290362306a36Sopenharmony_ci		.mode = 0,
290462306a36Sopenharmony_ci		.rules = BIT(TRIGGER_NETDEV_LINK),
290562306a36Sopenharmony_ci	},
290662306a36Sopenharmony_ci	{
290762306a36Sopenharmony_ci		.mode = 1,
290862306a36Sopenharmony_ci		.rules = (BIT(TRIGGER_NETDEV_LINK) |
290962306a36Sopenharmony_ci			  BIT(TRIGGER_NETDEV_RX) |
291062306a36Sopenharmony_ci			  BIT(TRIGGER_NETDEV_TX)),
291162306a36Sopenharmony_ci	},
291262306a36Sopenharmony_ci	{
291362306a36Sopenharmony_ci		.mode = 3,
291462306a36Sopenharmony_ci		.rules = (BIT(TRIGGER_NETDEV_RX) |
291562306a36Sopenharmony_ci			  BIT(TRIGGER_NETDEV_TX)),
291662306a36Sopenharmony_ci	},
291762306a36Sopenharmony_ci	{
291862306a36Sopenharmony_ci		.mode = 4,
291962306a36Sopenharmony_ci		.rules = (BIT(TRIGGER_NETDEV_RX) |
292062306a36Sopenharmony_ci			  BIT(TRIGGER_NETDEV_TX)),
292162306a36Sopenharmony_ci	},
292262306a36Sopenharmony_ci	{
292362306a36Sopenharmony_ci		.mode = 5,
292462306a36Sopenharmony_ci		.rules = BIT(TRIGGER_NETDEV_TX),
292562306a36Sopenharmony_ci	},
292662306a36Sopenharmony_ci	{
292762306a36Sopenharmony_ci		.mode = 6,
292862306a36Sopenharmony_ci		.rules = BIT(TRIGGER_NETDEV_LINK),
292962306a36Sopenharmony_ci	},
293062306a36Sopenharmony_ci	{
293162306a36Sopenharmony_ci		.mode = 7,
293262306a36Sopenharmony_ci		.rules = BIT(TRIGGER_NETDEV_LINK_1000),
293362306a36Sopenharmony_ci	},
293462306a36Sopenharmony_ci	{
293562306a36Sopenharmony_ci		.mode = 8,
293662306a36Sopenharmony_ci		.rules = 0,
293762306a36Sopenharmony_ci	},
293862306a36Sopenharmony_ci};
293962306a36Sopenharmony_ci
294062306a36Sopenharmony_cistatic const struct marvell_led_rules marvell_led1[] = {
294162306a36Sopenharmony_ci	{
294262306a36Sopenharmony_ci		.mode = 1,
294362306a36Sopenharmony_ci		.rules = (BIT(TRIGGER_NETDEV_LINK) |
294462306a36Sopenharmony_ci			  BIT(TRIGGER_NETDEV_RX) |
294562306a36Sopenharmony_ci			  BIT(TRIGGER_NETDEV_TX)),
294662306a36Sopenharmony_ci	},
294762306a36Sopenharmony_ci	{
294862306a36Sopenharmony_ci		.mode = 2,
294962306a36Sopenharmony_ci		.rules = (BIT(TRIGGER_NETDEV_LINK) |
295062306a36Sopenharmony_ci			  BIT(TRIGGER_NETDEV_RX)),
295162306a36Sopenharmony_ci	},
295262306a36Sopenharmony_ci	{
295362306a36Sopenharmony_ci		.mode = 3,
295462306a36Sopenharmony_ci		.rules = (BIT(TRIGGER_NETDEV_RX) |
295562306a36Sopenharmony_ci			  BIT(TRIGGER_NETDEV_TX)),
295662306a36Sopenharmony_ci	},
295762306a36Sopenharmony_ci	{
295862306a36Sopenharmony_ci		.mode = 4,
295962306a36Sopenharmony_ci		.rules = (BIT(TRIGGER_NETDEV_RX) |
296062306a36Sopenharmony_ci			  BIT(TRIGGER_NETDEV_TX)),
296162306a36Sopenharmony_ci	},
296262306a36Sopenharmony_ci	{
296362306a36Sopenharmony_ci		.mode = 6,
296462306a36Sopenharmony_ci		.rules = (BIT(TRIGGER_NETDEV_LINK_100) |
296562306a36Sopenharmony_ci			  BIT(TRIGGER_NETDEV_LINK_1000)),
296662306a36Sopenharmony_ci	},
296762306a36Sopenharmony_ci	{
296862306a36Sopenharmony_ci		.mode = 7,
296962306a36Sopenharmony_ci		.rules = BIT(TRIGGER_NETDEV_LINK_100),
297062306a36Sopenharmony_ci	},
297162306a36Sopenharmony_ci	{
297262306a36Sopenharmony_ci		.mode = 8,
297362306a36Sopenharmony_ci		.rules = 0,
297462306a36Sopenharmony_ci	},
297562306a36Sopenharmony_ci};
297662306a36Sopenharmony_ci
297762306a36Sopenharmony_cistatic const struct marvell_led_rules marvell_led2[] = {
297862306a36Sopenharmony_ci	{
297962306a36Sopenharmony_ci		.mode = 0,
298062306a36Sopenharmony_ci		.rules = BIT(TRIGGER_NETDEV_LINK),
298162306a36Sopenharmony_ci	},
298262306a36Sopenharmony_ci	{
298362306a36Sopenharmony_ci		.mode = 1,
298462306a36Sopenharmony_ci		.rules = (BIT(TRIGGER_NETDEV_LINK) |
298562306a36Sopenharmony_ci			  BIT(TRIGGER_NETDEV_RX) |
298662306a36Sopenharmony_ci			  BIT(TRIGGER_NETDEV_TX)),
298762306a36Sopenharmony_ci	},
298862306a36Sopenharmony_ci	{
298962306a36Sopenharmony_ci		.mode = 3,
299062306a36Sopenharmony_ci		.rules = (BIT(TRIGGER_NETDEV_RX) |
299162306a36Sopenharmony_ci			  BIT(TRIGGER_NETDEV_TX)),
299262306a36Sopenharmony_ci	},
299362306a36Sopenharmony_ci	{
299462306a36Sopenharmony_ci		.mode = 4,
299562306a36Sopenharmony_ci		.rules = (BIT(TRIGGER_NETDEV_RX) |
299662306a36Sopenharmony_ci			  BIT(TRIGGER_NETDEV_TX)),
299762306a36Sopenharmony_ci	},
299862306a36Sopenharmony_ci	{
299962306a36Sopenharmony_ci		.mode = 5,
300062306a36Sopenharmony_ci		.rules = BIT(TRIGGER_NETDEV_TX),
300162306a36Sopenharmony_ci	},
300262306a36Sopenharmony_ci	{
300362306a36Sopenharmony_ci		.mode = 6,
300462306a36Sopenharmony_ci		.rules = (BIT(TRIGGER_NETDEV_LINK_10) |
300562306a36Sopenharmony_ci			  BIT(TRIGGER_NETDEV_LINK_1000)),
300662306a36Sopenharmony_ci	},
300762306a36Sopenharmony_ci	{
300862306a36Sopenharmony_ci		.mode = 7,
300962306a36Sopenharmony_ci		.rules = BIT(TRIGGER_NETDEV_LINK_10),
301062306a36Sopenharmony_ci	},
301162306a36Sopenharmony_ci	{
301262306a36Sopenharmony_ci		.mode = 8,
301362306a36Sopenharmony_ci		.rules = 0,
301462306a36Sopenharmony_ci	},
301562306a36Sopenharmony_ci};
301662306a36Sopenharmony_ci
301762306a36Sopenharmony_cistatic int marvell_find_led_mode(unsigned long rules,
301862306a36Sopenharmony_ci				 const struct marvell_led_rules *marvell_rules,
301962306a36Sopenharmony_ci				 int count,
302062306a36Sopenharmony_ci				 int *mode)
302162306a36Sopenharmony_ci{
302262306a36Sopenharmony_ci	int i;
302362306a36Sopenharmony_ci
302462306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
302562306a36Sopenharmony_ci		if (marvell_rules[i].rules == rules) {
302662306a36Sopenharmony_ci			*mode = marvell_rules[i].mode;
302762306a36Sopenharmony_ci			return 0;
302862306a36Sopenharmony_ci		}
302962306a36Sopenharmony_ci	}
303062306a36Sopenharmony_ci	return -EOPNOTSUPP;
303162306a36Sopenharmony_ci}
303262306a36Sopenharmony_ci
303362306a36Sopenharmony_cistatic int marvell_get_led_mode(u8 index, unsigned long rules, int *mode)
303462306a36Sopenharmony_ci{
303562306a36Sopenharmony_ci	int ret;
303662306a36Sopenharmony_ci
303762306a36Sopenharmony_ci	switch (index) {
303862306a36Sopenharmony_ci	case 0:
303962306a36Sopenharmony_ci		ret = marvell_find_led_mode(rules, marvell_led0,
304062306a36Sopenharmony_ci					    ARRAY_SIZE(marvell_led0), mode);
304162306a36Sopenharmony_ci		break;
304262306a36Sopenharmony_ci	case 1:
304362306a36Sopenharmony_ci		ret = marvell_find_led_mode(rules, marvell_led1,
304462306a36Sopenharmony_ci					    ARRAY_SIZE(marvell_led1), mode);
304562306a36Sopenharmony_ci		break;
304662306a36Sopenharmony_ci	case 2:
304762306a36Sopenharmony_ci		ret = marvell_find_led_mode(rules, marvell_led2,
304862306a36Sopenharmony_ci					    ARRAY_SIZE(marvell_led2), mode);
304962306a36Sopenharmony_ci		break;
305062306a36Sopenharmony_ci	default:
305162306a36Sopenharmony_ci		ret = -EINVAL;
305262306a36Sopenharmony_ci	}
305362306a36Sopenharmony_ci
305462306a36Sopenharmony_ci	return ret;
305562306a36Sopenharmony_ci}
305662306a36Sopenharmony_ci
305762306a36Sopenharmony_cistatic int marvell_find_led_rules(unsigned long *rules,
305862306a36Sopenharmony_ci				  const struct marvell_led_rules *marvell_rules,
305962306a36Sopenharmony_ci				  int count,
306062306a36Sopenharmony_ci				  int mode)
306162306a36Sopenharmony_ci{
306262306a36Sopenharmony_ci	int i;
306362306a36Sopenharmony_ci
306462306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
306562306a36Sopenharmony_ci		if (marvell_rules[i].mode == mode) {
306662306a36Sopenharmony_ci			*rules = marvell_rules[i].rules;
306762306a36Sopenharmony_ci			return 0;
306862306a36Sopenharmony_ci		}
306962306a36Sopenharmony_ci	}
307062306a36Sopenharmony_ci	return -EOPNOTSUPP;
307162306a36Sopenharmony_ci}
307262306a36Sopenharmony_ci
307362306a36Sopenharmony_cistatic int marvell_get_led_rules(u8 index, unsigned long *rules, int mode)
307462306a36Sopenharmony_ci{
307562306a36Sopenharmony_ci	int ret;
307662306a36Sopenharmony_ci
307762306a36Sopenharmony_ci	switch (index) {
307862306a36Sopenharmony_ci	case 0:
307962306a36Sopenharmony_ci		ret = marvell_find_led_rules(rules, marvell_led0,
308062306a36Sopenharmony_ci					     ARRAY_SIZE(marvell_led0), mode);
308162306a36Sopenharmony_ci		break;
308262306a36Sopenharmony_ci	case 1:
308362306a36Sopenharmony_ci		ret = marvell_find_led_rules(rules, marvell_led1,
308462306a36Sopenharmony_ci					     ARRAY_SIZE(marvell_led1), mode);
308562306a36Sopenharmony_ci		break;
308662306a36Sopenharmony_ci	case 2:
308762306a36Sopenharmony_ci		ret = marvell_find_led_rules(rules, marvell_led2,
308862306a36Sopenharmony_ci					     ARRAY_SIZE(marvell_led2), mode);
308962306a36Sopenharmony_ci		break;
309062306a36Sopenharmony_ci	default:
309162306a36Sopenharmony_ci		ret = -EOPNOTSUPP;
309262306a36Sopenharmony_ci	}
309362306a36Sopenharmony_ci
309462306a36Sopenharmony_ci	return ret;
309562306a36Sopenharmony_ci}
309662306a36Sopenharmony_ci
309762306a36Sopenharmony_cistatic int m88e1318_led_hw_is_supported(struct phy_device *phydev, u8 index,
309862306a36Sopenharmony_ci					unsigned long rules)
309962306a36Sopenharmony_ci{
310062306a36Sopenharmony_ci	int mode, ret;
310162306a36Sopenharmony_ci
310262306a36Sopenharmony_ci	switch (index) {
310362306a36Sopenharmony_ci	case 0:
310462306a36Sopenharmony_ci	case 1:
310562306a36Sopenharmony_ci	case 2:
310662306a36Sopenharmony_ci		ret = marvell_get_led_mode(index, rules, &mode);
310762306a36Sopenharmony_ci		break;
310862306a36Sopenharmony_ci	default:
310962306a36Sopenharmony_ci		ret = -EINVAL;
311062306a36Sopenharmony_ci	}
311162306a36Sopenharmony_ci
311262306a36Sopenharmony_ci	return ret;
311362306a36Sopenharmony_ci}
311462306a36Sopenharmony_ci
311562306a36Sopenharmony_cistatic int m88e1318_led_hw_control_set(struct phy_device *phydev, u8 index,
311662306a36Sopenharmony_ci				       unsigned long rules)
311762306a36Sopenharmony_ci{
311862306a36Sopenharmony_ci	int mode, ret, reg;
311962306a36Sopenharmony_ci
312062306a36Sopenharmony_ci	switch (index) {
312162306a36Sopenharmony_ci	case 0:
312262306a36Sopenharmony_ci	case 1:
312362306a36Sopenharmony_ci	case 2:
312462306a36Sopenharmony_ci		ret = marvell_get_led_mode(index, rules, &mode);
312562306a36Sopenharmony_ci		break;
312662306a36Sopenharmony_ci	default:
312762306a36Sopenharmony_ci		ret = -EINVAL;
312862306a36Sopenharmony_ci	}
312962306a36Sopenharmony_ci
313062306a36Sopenharmony_ci	if (ret < 0)
313162306a36Sopenharmony_ci		return ret;
313262306a36Sopenharmony_ci
313362306a36Sopenharmony_ci	reg = phy_read_paged(phydev, MII_MARVELL_LED_PAGE,
313462306a36Sopenharmony_ci			     MII_88E1318S_PHY_LED_FUNC);
313562306a36Sopenharmony_ci	if (reg < 0)
313662306a36Sopenharmony_ci		return reg;
313762306a36Sopenharmony_ci
313862306a36Sopenharmony_ci	reg &= ~(0xf << (4 * index));
313962306a36Sopenharmony_ci	reg |= mode << (4 * index);
314062306a36Sopenharmony_ci	return phy_write_paged(phydev, MII_MARVELL_LED_PAGE,
314162306a36Sopenharmony_ci			       MII_88E1318S_PHY_LED_FUNC, reg);
314262306a36Sopenharmony_ci}
314362306a36Sopenharmony_ci
314462306a36Sopenharmony_cistatic int m88e1318_led_hw_control_get(struct phy_device *phydev, u8 index,
314562306a36Sopenharmony_ci				       unsigned long *rules)
314662306a36Sopenharmony_ci{
314762306a36Sopenharmony_ci	int mode, reg;
314862306a36Sopenharmony_ci
314962306a36Sopenharmony_ci	if (index > 2)
315062306a36Sopenharmony_ci		return -EINVAL;
315162306a36Sopenharmony_ci
315262306a36Sopenharmony_ci	reg = phy_read_paged(phydev, MII_MARVELL_LED_PAGE,
315362306a36Sopenharmony_ci			     MII_88E1318S_PHY_LED_FUNC);
315462306a36Sopenharmony_ci	if (reg < 0)
315562306a36Sopenharmony_ci		return reg;
315662306a36Sopenharmony_ci
315762306a36Sopenharmony_ci	mode = (reg >> (4 * index)) & 0xf;
315862306a36Sopenharmony_ci
315962306a36Sopenharmony_ci	return marvell_get_led_rules(index, rules, mode);
316062306a36Sopenharmony_ci}
316162306a36Sopenharmony_ci
316262306a36Sopenharmony_cistatic int marvell_probe(struct phy_device *phydev)
316362306a36Sopenharmony_ci{
316462306a36Sopenharmony_ci	struct marvell_priv *priv;
316562306a36Sopenharmony_ci
316662306a36Sopenharmony_ci	priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
316762306a36Sopenharmony_ci	if (!priv)
316862306a36Sopenharmony_ci		return -ENOMEM;
316962306a36Sopenharmony_ci
317062306a36Sopenharmony_ci	phydev->priv = priv;
317162306a36Sopenharmony_ci
317262306a36Sopenharmony_ci	return marvell_hwmon_probe(phydev);
317362306a36Sopenharmony_ci}
317462306a36Sopenharmony_ci
317562306a36Sopenharmony_cistatic int m88e1510_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
317662306a36Sopenharmony_ci{
317762306a36Sopenharmony_ci	DECLARE_PHY_INTERFACE_MASK(interfaces);
317862306a36Sopenharmony_ci	struct phy_device *phydev = upstream;
317962306a36Sopenharmony_ci	phy_interface_t interface;
318062306a36Sopenharmony_ci	struct device *dev;
318162306a36Sopenharmony_ci	int oldpage;
318262306a36Sopenharmony_ci	int ret = 0;
318362306a36Sopenharmony_ci	u16 mode;
318462306a36Sopenharmony_ci
318562306a36Sopenharmony_ci	__ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0, };
318662306a36Sopenharmony_ci
318762306a36Sopenharmony_ci	dev = &phydev->mdio.dev;
318862306a36Sopenharmony_ci
318962306a36Sopenharmony_ci	sfp_parse_support(phydev->sfp_bus, id, supported, interfaces);
319062306a36Sopenharmony_ci	interface = sfp_select_interface(phydev->sfp_bus, supported);
319162306a36Sopenharmony_ci
319262306a36Sopenharmony_ci	dev_info(dev, "%s SFP module inserted\n", phy_modes(interface));
319362306a36Sopenharmony_ci
319462306a36Sopenharmony_ci	switch (interface) {
319562306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_1000BASEX:
319662306a36Sopenharmony_ci		mode = MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII_1000X;
319762306a36Sopenharmony_ci
319862306a36Sopenharmony_ci		break;
319962306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_100BASEX:
320062306a36Sopenharmony_ci		mode = MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII_100FX;
320162306a36Sopenharmony_ci
320262306a36Sopenharmony_ci		break;
320362306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_SGMII:
320462306a36Sopenharmony_ci		mode = MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII_SGMII;
320562306a36Sopenharmony_ci
320662306a36Sopenharmony_ci		break;
320762306a36Sopenharmony_ci	default:
320862306a36Sopenharmony_ci		dev_err(dev, "Incompatible SFP module inserted\n");
320962306a36Sopenharmony_ci
321062306a36Sopenharmony_ci		return -EINVAL;
321162306a36Sopenharmony_ci	}
321262306a36Sopenharmony_ci
321362306a36Sopenharmony_ci	oldpage = phy_select_page(phydev, MII_MARVELL_MODE_PAGE);
321462306a36Sopenharmony_ci	if (oldpage < 0)
321562306a36Sopenharmony_ci		goto error;
321662306a36Sopenharmony_ci
321762306a36Sopenharmony_ci	ret = __phy_modify(phydev, MII_88E1510_GEN_CTRL_REG_1,
321862306a36Sopenharmony_ci			   MII_88E1510_GEN_CTRL_REG_1_MODE_MASK, mode);
321962306a36Sopenharmony_ci	if (ret < 0)
322062306a36Sopenharmony_ci		goto error;
322162306a36Sopenharmony_ci
322262306a36Sopenharmony_ci	ret = __phy_set_bits(phydev, MII_88E1510_GEN_CTRL_REG_1,
322362306a36Sopenharmony_ci			     MII_88E1510_GEN_CTRL_REG_1_RESET);
322462306a36Sopenharmony_ci
322562306a36Sopenharmony_cierror:
322662306a36Sopenharmony_ci	return phy_restore_page(phydev, oldpage, ret);
322762306a36Sopenharmony_ci}
322862306a36Sopenharmony_ci
322962306a36Sopenharmony_cistatic void m88e1510_sfp_remove(void *upstream)
323062306a36Sopenharmony_ci{
323162306a36Sopenharmony_ci	struct phy_device *phydev = upstream;
323262306a36Sopenharmony_ci	int oldpage;
323362306a36Sopenharmony_ci	int ret = 0;
323462306a36Sopenharmony_ci
323562306a36Sopenharmony_ci	oldpage = phy_select_page(phydev, MII_MARVELL_MODE_PAGE);
323662306a36Sopenharmony_ci	if (oldpage < 0)
323762306a36Sopenharmony_ci		goto error;
323862306a36Sopenharmony_ci
323962306a36Sopenharmony_ci	ret = __phy_modify(phydev, MII_88E1510_GEN_CTRL_REG_1,
324062306a36Sopenharmony_ci			   MII_88E1510_GEN_CTRL_REG_1_MODE_MASK,
324162306a36Sopenharmony_ci			   MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII);
324262306a36Sopenharmony_ci	if (ret < 0)
324362306a36Sopenharmony_ci		goto error;
324462306a36Sopenharmony_ci
324562306a36Sopenharmony_ci	ret = __phy_set_bits(phydev, MII_88E1510_GEN_CTRL_REG_1,
324662306a36Sopenharmony_ci			     MII_88E1510_GEN_CTRL_REG_1_RESET);
324762306a36Sopenharmony_ci
324862306a36Sopenharmony_cierror:
324962306a36Sopenharmony_ci	phy_restore_page(phydev, oldpage, ret);
325062306a36Sopenharmony_ci}
325162306a36Sopenharmony_ci
325262306a36Sopenharmony_cistatic const struct sfp_upstream_ops m88e1510_sfp_ops = {
325362306a36Sopenharmony_ci	.module_insert = m88e1510_sfp_insert,
325462306a36Sopenharmony_ci	.module_remove = m88e1510_sfp_remove,
325562306a36Sopenharmony_ci	.attach = phy_sfp_attach,
325662306a36Sopenharmony_ci	.detach = phy_sfp_detach,
325762306a36Sopenharmony_ci};
325862306a36Sopenharmony_ci
325962306a36Sopenharmony_cistatic int m88e1510_probe(struct phy_device *phydev)
326062306a36Sopenharmony_ci{
326162306a36Sopenharmony_ci	int err;
326262306a36Sopenharmony_ci
326362306a36Sopenharmony_ci	err = marvell_probe(phydev);
326462306a36Sopenharmony_ci	if (err)
326562306a36Sopenharmony_ci		return err;
326662306a36Sopenharmony_ci
326762306a36Sopenharmony_ci	return phy_sfp_probe(phydev, &m88e1510_sfp_ops);
326862306a36Sopenharmony_ci}
326962306a36Sopenharmony_ci
327062306a36Sopenharmony_cistatic struct phy_driver marvell_drivers[] = {
327162306a36Sopenharmony_ci	{
327262306a36Sopenharmony_ci		.phy_id = MARVELL_PHY_ID_88E1101,
327362306a36Sopenharmony_ci		.phy_id_mask = MARVELL_PHY_ID_MASK,
327462306a36Sopenharmony_ci		.name = "Marvell 88E1101",
327562306a36Sopenharmony_ci		/* PHY_GBIT_FEATURES */
327662306a36Sopenharmony_ci		.probe = marvell_probe,
327762306a36Sopenharmony_ci		.config_init = marvell_config_init,
327862306a36Sopenharmony_ci		.config_aneg = m88e1101_config_aneg,
327962306a36Sopenharmony_ci		.config_intr = marvell_config_intr,
328062306a36Sopenharmony_ci		.handle_interrupt = marvell_handle_interrupt,
328162306a36Sopenharmony_ci		.resume = genphy_resume,
328262306a36Sopenharmony_ci		.suspend = genphy_suspend,
328362306a36Sopenharmony_ci		.read_page = marvell_read_page,
328462306a36Sopenharmony_ci		.write_page = marvell_write_page,
328562306a36Sopenharmony_ci		.get_sset_count = marvell_get_sset_count,
328662306a36Sopenharmony_ci		.get_strings = marvell_get_strings,
328762306a36Sopenharmony_ci		.get_stats = marvell_get_stats,
328862306a36Sopenharmony_ci	},
328962306a36Sopenharmony_ci	{
329062306a36Sopenharmony_ci		.phy_id = MARVELL_PHY_ID_88E1112,
329162306a36Sopenharmony_ci		.phy_id_mask = MARVELL_PHY_ID_MASK,
329262306a36Sopenharmony_ci		.name = "Marvell 88E1112",
329362306a36Sopenharmony_ci		/* PHY_GBIT_FEATURES */
329462306a36Sopenharmony_ci		.probe = marvell_probe,
329562306a36Sopenharmony_ci		.config_init = m88e1112_config_init,
329662306a36Sopenharmony_ci		.config_aneg = marvell_config_aneg,
329762306a36Sopenharmony_ci		.config_intr = marvell_config_intr,
329862306a36Sopenharmony_ci		.handle_interrupt = marvell_handle_interrupt,
329962306a36Sopenharmony_ci		.resume = genphy_resume,
330062306a36Sopenharmony_ci		.suspend = genphy_suspend,
330162306a36Sopenharmony_ci		.read_page = marvell_read_page,
330262306a36Sopenharmony_ci		.write_page = marvell_write_page,
330362306a36Sopenharmony_ci		.get_sset_count = marvell_get_sset_count,
330462306a36Sopenharmony_ci		.get_strings = marvell_get_strings,
330562306a36Sopenharmony_ci		.get_stats = marvell_get_stats,
330662306a36Sopenharmony_ci		.get_tunable = m88e1011_get_tunable,
330762306a36Sopenharmony_ci		.set_tunable = m88e1011_set_tunable,
330862306a36Sopenharmony_ci	},
330962306a36Sopenharmony_ci	{
331062306a36Sopenharmony_ci		.phy_id = MARVELL_PHY_ID_88E1111,
331162306a36Sopenharmony_ci		.phy_id_mask = MARVELL_PHY_ID_MASK,
331262306a36Sopenharmony_ci		.name = "Marvell 88E1111",
331362306a36Sopenharmony_ci		/* PHY_GBIT_FEATURES */
331462306a36Sopenharmony_ci		.probe = marvell_probe,
331562306a36Sopenharmony_ci		.config_init = m88e1111gbe_config_init,
331662306a36Sopenharmony_ci		.config_aneg = m88e1111_config_aneg,
331762306a36Sopenharmony_ci		.read_status = marvell_read_status,
331862306a36Sopenharmony_ci		.config_intr = marvell_config_intr,
331962306a36Sopenharmony_ci		.handle_interrupt = marvell_handle_interrupt,
332062306a36Sopenharmony_ci		.resume = genphy_resume,
332162306a36Sopenharmony_ci		.suspend = genphy_suspend,
332262306a36Sopenharmony_ci		.read_page = marvell_read_page,
332362306a36Sopenharmony_ci		.write_page = marvell_write_page,
332462306a36Sopenharmony_ci		.get_sset_count = marvell_get_sset_count,
332562306a36Sopenharmony_ci		.get_strings = marvell_get_strings,
332662306a36Sopenharmony_ci		.get_stats = marvell_get_stats,
332762306a36Sopenharmony_ci		.get_tunable = m88e1111_get_tunable,
332862306a36Sopenharmony_ci		.set_tunable = m88e1111_set_tunable,
332962306a36Sopenharmony_ci	},
333062306a36Sopenharmony_ci	{
333162306a36Sopenharmony_ci		.phy_id = MARVELL_PHY_ID_88E1111_FINISAR,
333262306a36Sopenharmony_ci		.phy_id_mask = MARVELL_PHY_ID_MASK,
333362306a36Sopenharmony_ci		.name = "Marvell 88E1111 (Finisar)",
333462306a36Sopenharmony_ci		/* PHY_GBIT_FEATURES */
333562306a36Sopenharmony_ci		.probe = marvell_probe,
333662306a36Sopenharmony_ci		.config_init = m88e1111gbe_config_init,
333762306a36Sopenharmony_ci		.config_aneg = m88e1111_config_aneg,
333862306a36Sopenharmony_ci		.read_status = marvell_read_status,
333962306a36Sopenharmony_ci		.config_intr = marvell_config_intr,
334062306a36Sopenharmony_ci		.handle_interrupt = marvell_handle_interrupt,
334162306a36Sopenharmony_ci		.resume = genphy_resume,
334262306a36Sopenharmony_ci		.suspend = genphy_suspend,
334362306a36Sopenharmony_ci		.read_page = marvell_read_page,
334462306a36Sopenharmony_ci		.write_page = marvell_write_page,
334562306a36Sopenharmony_ci		.get_sset_count = marvell_get_sset_count,
334662306a36Sopenharmony_ci		.get_strings = marvell_get_strings,
334762306a36Sopenharmony_ci		.get_stats = marvell_get_stats,
334862306a36Sopenharmony_ci		.get_tunable = m88e1111_get_tunable,
334962306a36Sopenharmony_ci		.set_tunable = m88e1111_set_tunable,
335062306a36Sopenharmony_ci	},
335162306a36Sopenharmony_ci	{
335262306a36Sopenharmony_ci		.phy_id = MARVELL_PHY_ID_88E1118,
335362306a36Sopenharmony_ci		.phy_id_mask = MARVELL_PHY_ID_MASK,
335462306a36Sopenharmony_ci		.name = "Marvell 88E1118",
335562306a36Sopenharmony_ci		/* PHY_GBIT_FEATURES */
335662306a36Sopenharmony_ci		.probe = marvell_probe,
335762306a36Sopenharmony_ci		.config_init = m88e1118_config_init,
335862306a36Sopenharmony_ci		.config_aneg = m88e1118_config_aneg,
335962306a36Sopenharmony_ci		.config_intr = marvell_config_intr,
336062306a36Sopenharmony_ci		.handle_interrupt = marvell_handle_interrupt,
336162306a36Sopenharmony_ci		.resume = genphy_resume,
336262306a36Sopenharmony_ci		.suspend = genphy_suspend,
336362306a36Sopenharmony_ci		.read_page = marvell_read_page,
336462306a36Sopenharmony_ci		.write_page = marvell_write_page,
336562306a36Sopenharmony_ci		.get_sset_count = marvell_get_sset_count,
336662306a36Sopenharmony_ci		.get_strings = marvell_get_strings,
336762306a36Sopenharmony_ci		.get_stats = marvell_get_stats,
336862306a36Sopenharmony_ci	},
336962306a36Sopenharmony_ci	{
337062306a36Sopenharmony_ci		.phy_id = MARVELL_PHY_ID_88E1121R,
337162306a36Sopenharmony_ci		.phy_id_mask = MARVELL_PHY_ID_MASK,
337262306a36Sopenharmony_ci		.name = "Marvell 88E1121R",
337362306a36Sopenharmony_ci		.driver_data = DEF_MARVELL_HWMON_OPS(m88e1121_hwmon_ops),
337462306a36Sopenharmony_ci		/* PHY_GBIT_FEATURES */
337562306a36Sopenharmony_ci		.probe = marvell_probe,
337662306a36Sopenharmony_ci		.config_init = marvell_1011gbe_config_init,
337762306a36Sopenharmony_ci		.config_aneg = m88e1121_config_aneg,
337862306a36Sopenharmony_ci		.read_status = marvell_read_status,
337962306a36Sopenharmony_ci		.config_intr = marvell_config_intr,
338062306a36Sopenharmony_ci		.handle_interrupt = marvell_handle_interrupt,
338162306a36Sopenharmony_ci		.resume = genphy_resume,
338262306a36Sopenharmony_ci		.suspend = genphy_suspend,
338362306a36Sopenharmony_ci		.read_page = marvell_read_page,
338462306a36Sopenharmony_ci		.write_page = marvell_write_page,
338562306a36Sopenharmony_ci		.get_sset_count = marvell_get_sset_count,
338662306a36Sopenharmony_ci		.get_strings = marvell_get_strings,
338762306a36Sopenharmony_ci		.get_stats = marvell_get_stats,
338862306a36Sopenharmony_ci		.get_tunable = m88e1011_get_tunable,
338962306a36Sopenharmony_ci		.set_tunable = m88e1011_set_tunable,
339062306a36Sopenharmony_ci	},
339162306a36Sopenharmony_ci	{
339262306a36Sopenharmony_ci		.phy_id = MARVELL_PHY_ID_88E1318S,
339362306a36Sopenharmony_ci		.phy_id_mask = MARVELL_PHY_ID_MASK,
339462306a36Sopenharmony_ci		.name = "Marvell 88E1318S",
339562306a36Sopenharmony_ci		/* PHY_GBIT_FEATURES */
339662306a36Sopenharmony_ci		.probe = marvell_probe,
339762306a36Sopenharmony_ci		.config_init = m88e1318_config_init,
339862306a36Sopenharmony_ci		.config_aneg = m88e1318_config_aneg,
339962306a36Sopenharmony_ci		.read_status = marvell_read_status,
340062306a36Sopenharmony_ci		.config_intr = marvell_config_intr,
340162306a36Sopenharmony_ci		.handle_interrupt = marvell_handle_interrupt,
340262306a36Sopenharmony_ci		.get_wol = m88e1318_get_wol,
340362306a36Sopenharmony_ci		.set_wol = m88e1318_set_wol,
340462306a36Sopenharmony_ci		.resume = genphy_resume,
340562306a36Sopenharmony_ci		.suspend = genphy_suspend,
340662306a36Sopenharmony_ci		.read_page = marvell_read_page,
340762306a36Sopenharmony_ci		.write_page = marvell_write_page,
340862306a36Sopenharmony_ci		.get_sset_count = marvell_get_sset_count,
340962306a36Sopenharmony_ci		.get_strings = marvell_get_strings,
341062306a36Sopenharmony_ci		.get_stats = marvell_get_stats,
341162306a36Sopenharmony_ci		.led_brightness_set = m88e1318_led_brightness_set,
341262306a36Sopenharmony_ci		.led_blink_set = m88e1318_led_blink_set,
341362306a36Sopenharmony_ci		.led_hw_is_supported = m88e1318_led_hw_is_supported,
341462306a36Sopenharmony_ci		.led_hw_control_set = m88e1318_led_hw_control_set,
341562306a36Sopenharmony_ci		.led_hw_control_get = m88e1318_led_hw_control_get,
341662306a36Sopenharmony_ci	},
341762306a36Sopenharmony_ci	{
341862306a36Sopenharmony_ci		.phy_id = MARVELL_PHY_ID_88E1145,
341962306a36Sopenharmony_ci		.phy_id_mask = MARVELL_PHY_ID_MASK,
342062306a36Sopenharmony_ci		.name = "Marvell 88E1145",
342162306a36Sopenharmony_ci		/* PHY_GBIT_FEATURES */
342262306a36Sopenharmony_ci		.probe = marvell_probe,
342362306a36Sopenharmony_ci		.config_init = m88e1145_config_init,
342462306a36Sopenharmony_ci		.config_aneg = m88e1101_config_aneg,
342562306a36Sopenharmony_ci		.config_intr = marvell_config_intr,
342662306a36Sopenharmony_ci		.handle_interrupt = marvell_handle_interrupt,
342762306a36Sopenharmony_ci		.resume = genphy_resume,
342862306a36Sopenharmony_ci		.suspend = genphy_suspend,
342962306a36Sopenharmony_ci		.read_page = marvell_read_page,
343062306a36Sopenharmony_ci		.write_page = marvell_write_page,
343162306a36Sopenharmony_ci		.get_sset_count = marvell_get_sset_count,
343262306a36Sopenharmony_ci		.get_strings = marvell_get_strings,
343362306a36Sopenharmony_ci		.get_stats = marvell_get_stats,
343462306a36Sopenharmony_ci		.get_tunable = m88e1111_get_tunable,
343562306a36Sopenharmony_ci		.set_tunable = m88e1111_set_tunable,
343662306a36Sopenharmony_ci	},
343762306a36Sopenharmony_ci	{
343862306a36Sopenharmony_ci		.phy_id = MARVELL_PHY_ID_88E1149R,
343962306a36Sopenharmony_ci		.phy_id_mask = MARVELL_PHY_ID_MASK,
344062306a36Sopenharmony_ci		.name = "Marvell 88E1149R",
344162306a36Sopenharmony_ci		/* PHY_GBIT_FEATURES */
344262306a36Sopenharmony_ci		.probe = marvell_probe,
344362306a36Sopenharmony_ci		.config_init = m88e1149_config_init,
344462306a36Sopenharmony_ci		.config_aneg = m88e1118_config_aneg,
344562306a36Sopenharmony_ci		.config_intr = marvell_config_intr,
344662306a36Sopenharmony_ci		.handle_interrupt = marvell_handle_interrupt,
344762306a36Sopenharmony_ci		.resume = genphy_resume,
344862306a36Sopenharmony_ci		.suspend = genphy_suspend,
344962306a36Sopenharmony_ci		.read_page = marvell_read_page,
345062306a36Sopenharmony_ci		.write_page = marvell_write_page,
345162306a36Sopenharmony_ci		.get_sset_count = marvell_get_sset_count,
345262306a36Sopenharmony_ci		.get_strings = marvell_get_strings,
345362306a36Sopenharmony_ci		.get_stats = marvell_get_stats,
345462306a36Sopenharmony_ci	},
345562306a36Sopenharmony_ci	{
345662306a36Sopenharmony_ci		.phy_id = MARVELL_PHY_ID_88E1240,
345762306a36Sopenharmony_ci		.phy_id_mask = MARVELL_PHY_ID_MASK,
345862306a36Sopenharmony_ci		.name = "Marvell 88E1240",
345962306a36Sopenharmony_ci		/* PHY_GBIT_FEATURES */
346062306a36Sopenharmony_ci		.probe = marvell_probe,
346162306a36Sopenharmony_ci		.config_init = m88e1112_config_init,
346262306a36Sopenharmony_ci		.config_aneg = marvell_config_aneg,
346362306a36Sopenharmony_ci		.config_intr = marvell_config_intr,
346462306a36Sopenharmony_ci		.handle_interrupt = marvell_handle_interrupt,
346562306a36Sopenharmony_ci		.resume = genphy_resume,
346662306a36Sopenharmony_ci		.suspend = genphy_suspend,
346762306a36Sopenharmony_ci		.read_page = marvell_read_page,
346862306a36Sopenharmony_ci		.write_page = marvell_write_page,
346962306a36Sopenharmony_ci		.get_sset_count = marvell_get_sset_count,
347062306a36Sopenharmony_ci		.get_strings = marvell_get_strings,
347162306a36Sopenharmony_ci		.get_stats = marvell_get_stats,
347262306a36Sopenharmony_ci		.get_tunable = m88e1011_get_tunable,
347362306a36Sopenharmony_ci		.set_tunable = m88e1011_set_tunable,
347462306a36Sopenharmony_ci	},
347562306a36Sopenharmony_ci	{
347662306a36Sopenharmony_ci		.phy_id = MARVELL_PHY_ID_88E1116R,
347762306a36Sopenharmony_ci		.phy_id_mask = MARVELL_PHY_ID_MASK,
347862306a36Sopenharmony_ci		.name = "Marvell 88E1116R",
347962306a36Sopenharmony_ci		/* PHY_GBIT_FEATURES */
348062306a36Sopenharmony_ci		.probe = marvell_probe,
348162306a36Sopenharmony_ci		.config_init = m88e1116r_config_init,
348262306a36Sopenharmony_ci		.config_intr = marvell_config_intr,
348362306a36Sopenharmony_ci		.handle_interrupt = marvell_handle_interrupt,
348462306a36Sopenharmony_ci		.resume = genphy_resume,
348562306a36Sopenharmony_ci		.suspend = genphy_suspend,
348662306a36Sopenharmony_ci		.read_page = marvell_read_page,
348762306a36Sopenharmony_ci		.write_page = marvell_write_page,
348862306a36Sopenharmony_ci		.get_sset_count = marvell_get_sset_count,
348962306a36Sopenharmony_ci		.get_strings = marvell_get_strings,
349062306a36Sopenharmony_ci		.get_stats = marvell_get_stats,
349162306a36Sopenharmony_ci		.get_tunable = m88e1011_get_tunable,
349262306a36Sopenharmony_ci		.set_tunable = m88e1011_set_tunable,
349362306a36Sopenharmony_ci	},
349462306a36Sopenharmony_ci	{
349562306a36Sopenharmony_ci		.phy_id = MARVELL_PHY_ID_88E1510,
349662306a36Sopenharmony_ci		.phy_id_mask = MARVELL_PHY_ID_MASK,
349762306a36Sopenharmony_ci		.name = "Marvell 88E1510",
349862306a36Sopenharmony_ci		.driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops),
349962306a36Sopenharmony_ci		.features = PHY_GBIT_FIBRE_FEATURES,
350062306a36Sopenharmony_ci		.flags = PHY_POLL_CABLE_TEST,
350162306a36Sopenharmony_ci		.probe = m88e1510_probe,
350262306a36Sopenharmony_ci		.config_init = m88e1510_config_init,
350362306a36Sopenharmony_ci		.config_aneg = m88e1510_config_aneg,
350462306a36Sopenharmony_ci		.read_status = marvell_read_status,
350562306a36Sopenharmony_ci		.config_intr = marvell_config_intr,
350662306a36Sopenharmony_ci		.handle_interrupt = marvell_handle_interrupt,
350762306a36Sopenharmony_ci		.get_wol = m88e1318_get_wol,
350862306a36Sopenharmony_ci		.set_wol = m88e1318_set_wol,
350962306a36Sopenharmony_ci		.resume = marvell_resume,
351062306a36Sopenharmony_ci		.suspend = marvell_suspend,
351162306a36Sopenharmony_ci		.read_page = marvell_read_page,
351262306a36Sopenharmony_ci		.write_page = marvell_write_page,
351362306a36Sopenharmony_ci		.get_sset_count = marvell_get_sset_count,
351462306a36Sopenharmony_ci		.get_strings = marvell_get_strings,
351562306a36Sopenharmony_ci		.get_stats = marvell_get_stats,
351662306a36Sopenharmony_ci		.set_loopback = m88e1510_loopback,
351762306a36Sopenharmony_ci		.get_tunable = m88e1011_get_tunable,
351862306a36Sopenharmony_ci		.set_tunable = m88e1011_set_tunable,
351962306a36Sopenharmony_ci		.cable_test_start = marvell_vct7_cable_test_start,
352062306a36Sopenharmony_ci		.cable_test_tdr_start = marvell_vct5_cable_test_tdr_start,
352162306a36Sopenharmony_ci		.cable_test_get_status = marvell_vct7_cable_test_get_status,
352262306a36Sopenharmony_ci		.led_brightness_set = m88e1318_led_brightness_set,
352362306a36Sopenharmony_ci		.led_blink_set = m88e1318_led_blink_set,
352462306a36Sopenharmony_ci		.led_hw_is_supported = m88e1318_led_hw_is_supported,
352562306a36Sopenharmony_ci		.led_hw_control_set = m88e1318_led_hw_control_set,
352662306a36Sopenharmony_ci		.led_hw_control_get = m88e1318_led_hw_control_get,
352762306a36Sopenharmony_ci	},
352862306a36Sopenharmony_ci	{
352962306a36Sopenharmony_ci		.phy_id = MARVELL_PHY_ID_88E1540,
353062306a36Sopenharmony_ci		.phy_id_mask = MARVELL_PHY_ID_MASK,
353162306a36Sopenharmony_ci		.name = "Marvell 88E1540",
353262306a36Sopenharmony_ci		.driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops),
353362306a36Sopenharmony_ci		/* PHY_GBIT_FEATURES */
353462306a36Sopenharmony_ci		.flags = PHY_POLL_CABLE_TEST,
353562306a36Sopenharmony_ci		.probe = marvell_probe,
353662306a36Sopenharmony_ci		.config_init = marvell_1011gbe_config_init,
353762306a36Sopenharmony_ci		.config_aneg = m88e1510_config_aneg,
353862306a36Sopenharmony_ci		.read_status = marvell_read_status,
353962306a36Sopenharmony_ci		.config_intr = marvell_config_intr,
354062306a36Sopenharmony_ci		.handle_interrupt = marvell_handle_interrupt,
354162306a36Sopenharmony_ci		.resume = genphy_resume,
354262306a36Sopenharmony_ci		.suspend = genphy_suspend,
354362306a36Sopenharmony_ci		.read_page = marvell_read_page,
354462306a36Sopenharmony_ci		.write_page = marvell_write_page,
354562306a36Sopenharmony_ci		.get_sset_count = marvell_get_sset_count,
354662306a36Sopenharmony_ci		.get_strings = marvell_get_strings,
354762306a36Sopenharmony_ci		.get_stats = marvell_get_stats,
354862306a36Sopenharmony_ci		.get_tunable = m88e1540_get_tunable,
354962306a36Sopenharmony_ci		.set_tunable = m88e1540_set_tunable,
355062306a36Sopenharmony_ci		.cable_test_start = marvell_vct7_cable_test_start,
355162306a36Sopenharmony_ci		.cable_test_tdr_start = marvell_vct5_cable_test_tdr_start,
355262306a36Sopenharmony_ci		.cable_test_get_status = marvell_vct7_cable_test_get_status,
355362306a36Sopenharmony_ci		.led_brightness_set = m88e1318_led_brightness_set,
355462306a36Sopenharmony_ci		.led_blink_set = m88e1318_led_blink_set,
355562306a36Sopenharmony_ci		.led_hw_is_supported = m88e1318_led_hw_is_supported,
355662306a36Sopenharmony_ci		.led_hw_control_set = m88e1318_led_hw_control_set,
355762306a36Sopenharmony_ci		.led_hw_control_get = m88e1318_led_hw_control_get,
355862306a36Sopenharmony_ci	},
355962306a36Sopenharmony_ci	{
356062306a36Sopenharmony_ci		.phy_id = MARVELL_PHY_ID_88E1545,
356162306a36Sopenharmony_ci		.phy_id_mask = MARVELL_PHY_ID_MASK,
356262306a36Sopenharmony_ci		.name = "Marvell 88E1545",
356362306a36Sopenharmony_ci		.driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops),
356462306a36Sopenharmony_ci		.probe = marvell_probe,
356562306a36Sopenharmony_ci		/* PHY_GBIT_FEATURES */
356662306a36Sopenharmony_ci		.flags = PHY_POLL_CABLE_TEST,
356762306a36Sopenharmony_ci		.config_init = marvell_1011gbe_config_init,
356862306a36Sopenharmony_ci		.config_aneg = m88e1510_config_aneg,
356962306a36Sopenharmony_ci		.read_status = marvell_read_status,
357062306a36Sopenharmony_ci		.config_intr = marvell_config_intr,
357162306a36Sopenharmony_ci		.handle_interrupt = marvell_handle_interrupt,
357262306a36Sopenharmony_ci		.resume = genphy_resume,
357362306a36Sopenharmony_ci		.suspend = genphy_suspend,
357462306a36Sopenharmony_ci		.read_page = marvell_read_page,
357562306a36Sopenharmony_ci		.write_page = marvell_write_page,
357662306a36Sopenharmony_ci		.get_sset_count = marvell_get_sset_count,
357762306a36Sopenharmony_ci		.get_strings = marvell_get_strings,
357862306a36Sopenharmony_ci		.get_stats = marvell_get_stats,
357962306a36Sopenharmony_ci		.get_tunable = m88e1540_get_tunable,
358062306a36Sopenharmony_ci		.set_tunable = m88e1540_set_tunable,
358162306a36Sopenharmony_ci		.cable_test_start = marvell_vct7_cable_test_start,
358262306a36Sopenharmony_ci		.cable_test_tdr_start = marvell_vct5_cable_test_tdr_start,
358362306a36Sopenharmony_ci		.cable_test_get_status = marvell_vct7_cable_test_get_status,
358462306a36Sopenharmony_ci		.led_brightness_set = m88e1318_led_brightness_set,
358562306a36Sopenharmony_ci		.led_blink_set = m88e1318_led_blink_set,
358662306a36Sopenharmony_ci		.led_hw_is_supported = m88e1318_led_hw_is_supported,
358762306a36Sopenharmony_ci		.led_hw_control_set = m88e1318_led_hw_control_set,
358862306a36Sopenharmony_ci		.led_hw_control_get = m88e1318_led_hw_control_get,
358962306a36Sopenharmony_ci	},
359062306a36Sopenharmony_ci	{
359162306a36Sopenharmony_ci		.phy_id = MARVELL_PHY_ID_88E3016,
359262306a36Sopenharmony_ci		.phy_id_mask = MARVELL_PHY_ID_MASK,
359362306a36Sopenharmony_ci		.name = "Marvell 88E3016",
359462306a36Sopenharmony_ci		/* PHY_BASIC_FEATURES */
359562306a36Sopenharmony_ci		.probe = marvell_probe,
359662306a36Sopenharmony_ci		.config_init = m88e3016_config_init,
359762306a36Sopenharmony_ci		.aneg_done = marvell_aneg_done,
359862306a36Sopenharmony_ci		.read_status = marvell_read_status,
359962306a36Sopenharmony_ci		.config_intr = marvell_config_intr,
360062306a36Sopenharmony_ci		.handle_interrupt = marvell_handle_interrupt,
360162306a36Sopenharmony_ci		.resume = genphy_resume,
360262306a36Sopenharmony_ci		.suspend = genphy_suspend,
360362306a36Sopenharmony_ci		.read_page = marvell_read_page,
360462306a36Sopenharmony_ci		.write_page = marvell_write_page,
360562306a36Sopenharmony_ci		.get_sset_count = marvell_get_sset_count,
360662306a36Sopenharmony_ci		.get_strings = marvell_get_strings,
360762306a36Sopenharmony_ci		.get_stats = marvell_get_stats,
360862306a36Sopenharmony_ci	},
360962306a36Sopenharmony_ci	{
361062306a36Sopenharmony_ci		.phy_id = MARVELL_PHY_ID_88E6341_FAMILY,
361162306a36Sopenharmony_ci		.phy_id_mask = MARVELL_PHY_ID_MASK,
361262306a36Sopenharmony_ci		.name = "Marvell 88E6341 Family",
361362306a36Sopenharmony_ci		.driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops),
361462306a36Sopenharmony_ci		/* PHY_GBIT_FEATURES */
361562306a36Sopenharmony_ci		.flags = PHY_POLL_CABLE_TEST,
361662306a36Sopenharmony_ci		.probe = marvell_probe,
361762306a36Sopenharmony_ci		.config_init = marvell_1011gbe_config_init,
361862306a36Sopenharmony_ci		.config_aneg = m88e6390_config_aneg,
361962306a36Sopenharmony_ci		.read_status = marvell_read_status,
362062306a36Sopenharmony_ci		.config_intr = marvell_config_intr,
362162306a36Sopenharmony_ci		.handle_interrupt = marvell_handle_interrupt,
362262306a36Sopenharmony_ci		.resume = genphy_resume,
362362306a36Sopenharmony_ci		.suspend = genphy_suspend,
362462306a36Sopenharmony_ci		.read_page = marvell_read_page,
362562306a36Sopenharmony_ci		.write_page = marvell_write_page,
362662306a36Sopenharmony_ci		.get_sset_count = marvell_get_sset_count,
362762306a36Sopenharmony_ci		.get_strings = marvell_get_strings,
362862306a36Sopenharmony_ci		.get_stats = marvell_get_stats,
362962306a36Sopenharmony_ci		.get_tunable = m88e1540_get_tunable,
363062306a36Sopenharmony_ci		.set_tunable = m88e1540_set_tunable,
363162306a36Sopenharmony_ci		.cable_test_start = marvell_vct7_cable_test_start,
363262306a36Sopenharmony_ci		.cable_test_tdr_start = marvell_vct5_cable_test_tdr_start,
363362306a36Sopenharmony_ci		.cable_test_get_status = marvell_vct7_cable_test_get_status,
363462306a36Sopenharmony_ci	},
363562306a36Sopenharmony_ci	{
363662306a36Sopenharmony_ci		.phy_id = MARVELL_PHY_ID_88E6390_FAMILY,
363762306a36Sopenharmony_ci		.phy_id_mask = MARVELL_PHY_ID_MASK,
363862306a36Sopenharmony_ci		.name = "Marvell 88E6390 Family",
363962306a36Sopenharmony_ci		.driver_data = DEF_MARVELL_HWMON_OPS(m88e6390_hwmon_ops),
364062306a36Sopenharmony_ci		/* PHY_GBIT_FEATURES */
364162306a36Sopenharmony_ci		.flags = PHY_POLL_CABLE_TEST,
364262306a36Sopenharmony_ci		.probe = marvell_probe,
364362306a36Sopenharmony_ci		.config_init = marvell_1011gbe_config_init,
364462306a36Sopenharmony_ci		.config_aneg = m88e6390_config_aneg,
364562306a36Sopenharmony_ci		.read_status = marvell_read_status,
364662306a36Sopenharmony_ci		.config_intr = marvell_config_intr,
364762306a36Sopenharmony_ci		.handle_interrupt = marvell_handle_interrupt,
364862306a36Sopenharmony_ci		.resume = genphy_resume,
364962306a36Sopenharmony_ci		.suspend = genphy_suspend,
365062306a36Sopenharmony_ci		.read_page = marvell_read_page,
365162306a36Sopenharmony_ci		.write_page = marvell_write_page,
365262306a36Sopenharmony_ci		.get_sset_count = marvell_get_sset_count,
365362306a36Sopenharmony_ci		.get_strings = marvell_get_strings,
365462306a36Sopenharmony_ci		.get_stats = marvell_get_stats,
365562306a36Sopenharmony_ci		.get_tunable = m88e1540_get_tunable,
365662306a36Sopenharmony_ci		.set_tunable = m88e1540_set_tunable,
365762306a36Sopenharmony_ci		.cable_test_start = marvell_vct7_cable_test_start,
365862306a36Sopenharmony_ci		.cable_test_tdr_start = marvell_vct5_cable_test_tdr_start,
365962306a36Sopenharmony_ci		.cable_test_get_status = marvell_vct7_cable_test_get_status,
366062306a36Sopenharmony_ci	},
366162306a36Sopenharmony_ci	{
366262306a36Sopenharmony_ci		.phy_id = MARVELL_PHY_ID_88E6393_FAMILY,
366362306a36Sopenharmony_ci		.phy_id_mask = MARVELL_PHY_ID_MASK,
366462306a36Sopenharmony_ci		.name = "Marvell 88E6393 Family",
366562306a36Sopenharmony_ci		.driver_data = DEF_MARVELL_HWMON_OPS(m88e6393_hwmon_ops),
366662306a36Sopenharmony_ci		/* PHY_GBIT_FEATURES */
366762306a36Sopenharmony_ci		.flags = PHY_POLL_CABLE_TEST,
366862306a36Sopenharmony_ci		.probe = marvell_probe,
366962306a36Sopenharmony_ci		.config_init = marvell_1011gbe_config_init,
367062306a36Sopenharmony_ci		.config_aneg = m88e1510_config_aneg,
367162306a36Sopenharmony_ci		.read_status = marvell_read_status,
367262306a36Sopenharmony_ci		.config_intr = marvell_config_intr,
367362306a36Sopenharmony_ci		.handle_interrupt = marvell_handle_interrupt,
367462306a36Sopenharmony_ci		.resume = genphy_resume,
367562306a36Sopenharmony_ci		.suspend = genphy_suspend,
367662306a36Sopenharmony_ci		.read_page = marvell_read_page,
367762306a36Sopenharmony_ci		.write_page = marvell_write_page,
367862306a36Sopenharmony_ci		.get_sset_count = marvell_get_sset_count,
367962306a36Sopenharmony_ci		.get_strings = marvell_get_strings,
368062306a36Sopenharmony_ci		.get_stats = marvell_get_stats,
368162306a36Sopenharmony_ci		.get_tunable = m88e1540_get_tunable,
368262306a36Sopenharmony_ci		.set_tunable = m88e1540_set_tunable,
368362306a36Sopenharmony_ci		.cable_test_start = marvell_vct7_cable_test_start,
368462306a36Sopenharmony_ci		.cable_test_tdr_start = marvell_vct5_cable_test_tdr_start,
368562306a36Sopenharmony_ci		.cable_test_get_status = marvell_vct7_cable_test_get_status,
368662306a36Sopenharmony_ci	},
368762306a36Sopenharmony_ci	{
368862306a36Sopenharmony_ci		.phy_id = MARVELL_PHY_ID_88E1340S,
368962306a36Sopenharmony_ci		.phy_id_mask = MARVELL_PHY_ID_MASK,
369062306a36Sopenharmony_ci		.name = "Marvell 88E1340S",
369162306a36Sopenharmony_ci		.driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops),
369262306a36Sopenharmony_ci		.probe = marvell_probe,
369362306a36Sopenharmony_ci		/* PHY_GBIT_FEATURES */
369462306a36Sopenharmony_ci		.config_init = marvell_1011gbe_config_init,
369562306a36Sopenharmony_ci		.config_aneg = m88e1510_config_aneg,
369662306a36Sopenharmony_ci		.read_status = marvell_read_status,
369762306a36Sopenharmony_ci		.config_intr = marvell_config_intr,
369862306a36Sopenharmony_ci		.handle_interrupt = marvell_handle_interrupt,
369962306a36Sopenharmony_ci		.resume = genphy_resume,
370062306a36Sopenharmony_ci		.suspend = genphy_suspend,
370162306a36Sopenharmony_ci		.read_page = marvell_read_page,
370262306a36Sopenharmony_ci		.write_page = marvell_write_page,
370362306a36Sopenharmony_ci		.get_sset_count = marvell_get_sset_count,
370462306a36Sopenharmony_ci		.get_strings = marvell_get_strings,
370562306a36Sopenharmony_ci		.get_stats = marvell_get_stats,
370662306a36Sopenharmony_ci		.get_tunable = m88e1540_get_tunable,
370762306a36Sopenharmony_ci		.set_tunable = m88e1540_set_tunable,
370862306a36Sopenharmony_ci	},
370962306a36Sopenharmony_ci	{
371062306a36Sopenharmony_ci		.phy_id = MARVELL_PHY_ID_88E1548P,
371162306a36Sopenharmony_ci		.phy_id_mask = MARVELL_PHY_ID_MASK,
371262306a36Sopenharmony_ci		.name = "Marvell 88E1548P",
371362306a36Sopenharmony_ci		.driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops),
371462306a36Sopenharmony_ci		.probe = marvell_probe,
371562306a36Sopenharmony_ci		.features = PHY_GBIT_FIBRE_FEATURES,
371662306a36Sopenharmony_ci		.config_init = marvell_1011gbe_config_init,
371762306a36Sopenharmony_ci		.config_aneg = m88e1510_config_aneg,
371862306a36Sopenharmony_ci		.read_status = marvell_read_status,
371962306a36Sopenharmony_ci		.config_intr = marvell_config_intr,
372062306a36Sopenharmony_ci		.handle_interrupt = marvell_handle_interrupt,
372162306a36Sopenharmony_ci		.resume = genphy_resume,
372262306a36Sopenharmony_ci		.suspend = genphy_suspend,
372362306a36Sopenharmony_ci		.read_page = marvell_read_page,
372462306a36Sopenharmony_ci		.write_page = marvell_write_page,
372562306a36Sopenharmony_ci		.get_sset_count = marvell_get_sset_count,
372662306a36Sopenharmony_ci		.get_strings = marvell_get_strings,
372762306a36Sopenharmony_ci		.get_stats = marvell_get_stats,
372862306a36Sopenharmony_ci		.get_tunable = m88e1540_get_tunable,
372962306a36Sopenharmony_ci		.set_tunable = m88e1540_set_tunable,
373062306a36Sopenharmony_ci		.led_brightness_set = m88e1318_led_brightness_set,
373162306a36Sopenharmony_ci		.led_blink_set = m88e1318_led_blink_set,
373262306a36Sopenharmony_ci		.led_hw_is_supported = m88e1318_led_hw_is_supported,
373362306a36Sopenharmony_ci		.led_hw_control_set = m88e1318_led_hw_control_set,
373462306a36Sopenharmony_ci		.led_hw_control_get = m88e1318_led_hw_control_get,
373562306a36Sopenharmony_ci	},
373662306a36Sopenharmony_ci};
373762306a36Sopenharmony_ci
373862306a36Sopenharmony_cimodule_phy_driver(marvell_drivers);
373962306a36Sopenharmony_ci
374062306a36Sopenharmony_cistatic struct mdio_device_id __maybe_unused marvell_tbl[] = {
374162306a36Sopenharmony_ci	{ MARVELL_PHY_ID_88E1101, MARVELL_PHY_ID_MASK },
374262306a36Sopenharmony_ci	{ MARVELL_PHY_ID_88E1112, MARVELL_PHY_ID_MASK },
374362306a36Sopenharmony_ci	{ MARVELL_PHY_ID_88E1111, MARVELL_PHY_ID_MASK },
374462306a36Sopenharmony_ci	{ MARVELL_PHY_ID_88E1111_FINISAR, MARVELL_PHY_ID_MASK },
374562306a36Sopenharmony_ci	{ MARVELL_PHY_ID_88E1118, MARVELL_PHY_ID_MASK },
374662306a36Sopenharmony_ci	{ MARVELL_PHY_ID_88E1121R, MARVELL_PHY_ID_MASK },
374762306a36Sopenharmony_ci	{ MARVELL_PHY_ID_88E1145, MARVELL_PHY_ID_MASK },
374862306a36Sopenharmony_ci	{ MARVELL_PHY_ID_88E1149R, MARVELL_PHY_ID_MASK },
374962306a36Sopenharmony_ci	{ MARVELL_PHY_ID_88E1240, MARVELL_PHY_ID_MASK },
375062306a36Sopenharmony_ci	{ MARVELL_PHY_ID_88E1318S, MARVELL_PHY_ID_MASK },
375162306a36Sopenharmony_ci	{ MARVELL_PHY_ID_88E1116R, MARVELL_PHY_ID_MASK },
375262306a36Sopenharmony_ci	{ MARVELL_PHY_ID_88E1510, MARVELL_PHY_ID_MASK },
375362306a36Sopenharmony_ci	{ MARVELL_PHY_ID_88E1540, MARVELL_PHY_ID_MASK },
375462306a36Sopenharmony_ci	{ MARVELL_PHY_ID_88E1545, MARVELL_PHY_ID_MASK },
375562306a36Sopenharmony_ci	{ MARVELL_PHY_ID_88E3016, MARVELL_PHY_ID_MASK },
375662306a36Sopenharmony_ci	{ MARVELL_PHY_ID_88E6341_FAMILY, MARVELL_PHY_ID_MASK },
375762306a36Sopenharmony_ci	{ MARVELL_PHY_ID_88E6390_FAMILY, MARVELL_PHY_ID_MASK },
375862306a36Sopenharmony_ci	{ MARVELL_PHY_ID_88E6393_FAMILY, MARVELL_PHY_ID_MASK },
375962306a36Sopenharmony_ci	{ MARVELL_PHY_ID_88E1340S, MARVELL_PHY_ID_MASK },
376062306a36Sopenharmony_ci	{ MARVELL_PHY_ID_88E1548P, MARVELL_PHY_ID_MASK },
376162306a36Sopenharmony_ci	{ }
376262306a36Sopenharmony_ci};
376362306a36Sopenharmony_ci
376462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(mdio, marvell_tbl);
3765