xref: /openbmc/linux/drivers/net/phy/adin.c (revision 65d7be09)
19c102981SAlexandru Ardelean // SPDX-License-Identifier: GPL-2.0+
29c102981SAlexandru Ardelean /**
39c102981SAlexandru Ardelean  *  Driver for Analog Devices Industrial Ethernet PHYs
49c102981SAlexandru Ardelean  *
59c102981SAlexandru Ardelean  * Copyright 2019 Analog Devices Inc.
69c102981SAlexandru Ardelean  */
79c102981SAlexandru Ardelean #include <linux/kernel.h>
8c83e6163SAlexandru Ardelean #include <linux/bitfield.h>
9fa5bd9c5SAlexandru Ardelean #include <linux/delay.h>
109c102981SAlexandru Ardelean #include <linux/errno.h>
119c102981SAlexandru Ardelean #include <linux/init.h>
129c102981SAlexandru Ardelean #include <linux/module.h>
139c102981SAlexandru Ardelean #include <linux/mii.h>
149c102981SAlexandru Ardelean #include <linux/phy.h>
15c83e6163SAlexandru Ardelean #include <linux/property.h>
169c102981SAlexandru Ardelean 
179c102981SAlexandru Ardelean #define PHY_ID_ADIN1200				0x0283bc20
189c102981SAlexandru Ardelean #define PHY_ID_ADIN1300				0x0283bc30
199c102981SAlexandru Ardelean 
203e32d020SAlexandru Ardelean #define ADIN1300_MII_EXT_REG_PTR		0x0010
213e32d020SAlexandru Ardelean #define ADIN1300_MII_EXT_REG_DATA		0x0011
223e32d020SAlexandru Ardelean 
23b422d1b6SAlexandru Ardelean #define ADIN1300_PHY_CTRL1			0x0012
24b422d1b6SAlexandru Ardelean #define   ADIN1300_AUTO_MDI_EN			BIT(10)
25b422d1b6SAlexandru Ardelean #define   ADIN1300_MAN_MDIX_EN			BIT(9)
26b422d1b6SAlexandru Ardelean 
279fe0b8d6SAlexandru Ardelean #define ADIN1300_RX_ERR_CNT			0x0014
289fe0b8d6SAlexandru Ardelean 
2965d7be09SAlexandru Ardelean #define ADIN1300_PHY_CTRL_STATUS2		0x0015
3065d7be09SAlexandru Ardelean #define   ADIN1300_NRG_PD_EN			BIT(3)
3165d7be09SAlexandru Ardelean #define   ADIN1300_NRG_PD_TX_EN			BIT(2)
3265d7be09SAlexandru Ardelean #define   ADIN1300_NRG_PD_STATUS		BIT(1)
3365d7be09SAlexandru Ardelean 
342d99b584SAlexandru Ardelean #define ADIN1300_PHY_CTRL2			0x0016
352d99b584SAlexandru Ardelean #define   ADIN1300_DOWNSPEED_AN_100_EN		BIT(11)
362d99b584SAlexandru Ardelean #define   ADIN1300_DOWNSPEED_AN_10_EN		BIT(10)
372d99b584SAlexandru Ardelean #define   ADIN1300_GROUP_MDIO_EN		BIT(6)
382d99b584SAlexandru Ardelean #define   ADIN1300_DOWNSPEEDS_EN	\
392d99b584SAlexandru Ardelean 	(ADIN1300_DOWNSPEED_AN_100_EN | ADIN1300_DOWNSPEED_AN_10_EN)
402d99b584SAlexandru Ardelean 
412d99b584SAlexandru Ardelean #define ADIN1300_PHY_CTRL3			0x0017
422d99b584SAlexandru Ardelean #define   ADIN1300_LINKING_EN			BIT(13)
432d99b584SAlexandru Ardelean #define   ADIN1300_DOWNSPEED_RETRIES_MSK	GENMASK(12, 10)
442d99b584SAlexandru Ardelean 
45fb44b8d6SAlexandru Ardelean #define ADIN1300_INT_MASK_REG			0x0018
46fb44b8d6SAlexandru Ardelean #define   ADIN1300_INT_MDIO_SYNC_EN		BIT(9)
47fb44b8d6SAlexandru Ardelean #define   ADIN1300_INT_ANEG_STAT_CHNG_EN	BIT(8)
48fb44b8d6SAlexandru Ardelean #define   ADIN1300_INT_ANEG_PAGE_RX_EN		BIT(6)
49fb44b8d6SAlexandru Ardelean #define   ADIN1300_INT_IDLE_ERR_CNT_EN		BIT(5)
50fb44b8d6SAlexandru Ardelean #define   ADIN1300_INT_MAC_FIFO_OU_EN		BIT(4)
51fb44b8d6SAlexandru Ardelean #define   ADIN1300_INT_RX_STAT_CHNG_EN		BIT(3)
52fb44b8d6SAlexandru Ardelean #define   ADIN1300_INT_LINK_STAT_CHNG_EN	BIT(2)
53fb44b8d6SAlexandru Ardelean #define   ADIN1300_INT_SPEED_CHNG_EN		BIT(1)
54fb44b8d6SAlexandru Ardelean #define   ADIN1300_INT_HW_IRQ_EN		BIT(0)
55fb44b8d6SAlexandru Ardelean #define ADIN1300_INT_MASK_EN	\
56fb44b8d6SAlexandru Ardelean 	(ADIN1300_INT_LINK_STAT_CHNG_EN | ADIN1300_INT_HW_IRQ_EN)
57fb44b8d6SAlexandru Ardelean #define ADIN1300_INT_STATUS_REG			0x0019
58fb44b8d6SAlexandru Ardelean 
59b422d1b6SAlexandru Ardelean #define ADIN1300_PHY_STATUS1			0x001a
60b422d1b6SAlexandru Ardelean #define   ADIN1300_PAIR_01_SWAP			BIT(11)
61b422d1b6SAlexandru Ardelean 
62c6aa697cSAlexandru Ardelean /* EEE register addresses, accessible via Clause 22 access using
63c6aa697cSAlexandru Ardelean  * ADIN1300_MII_EXT_REG_PTR & ADIN1300_MII_EXT_REG_DATA.
64c6aa697cSAlexandru Ardelean  * The bit-fields are the same as specified by IEEE for EEE.
65c6aa697cSAlexandru Ardelean  */
66c6aa697cSAlexandru Ardelean #define ADIN1300_EEE_CAP_REG			0x8000
67c6aa697cSAlexandru Ardelean #define ADIN1300_EEE_ADV_REG			0x8001
68c6aa697cSAlexandru Ardelean #define ADIN1300_EEE_LPABLE_REG			0x8002
69c6aa697cSAlexandru Ardelean #define ADIN1300_CLOCK_STOP_REG			0x9400
70c6aa697cSAlexandru Ardelean #define ADIN1300_LPI_WAKE_ERR_CNT_REG		0xa000
71c6aa697cSAlexandru Ardelean 
72fa5bd9c5SAlexandru Ardelean #define ADIN1300_GE_SOFT_RESET_REG		0xff0c
73fa5bd9c5SAlexandru Ardelean #define   ADIN1300_GE_SOFT_RESET		BIT(0)
74fa5bd9c5SAlexandru Ardelean 
75d6200c8fSAlexandru Ardelean #define ADIN1300_GE_RGMII_CFG_REG		0xff23
76c83e6163SAlexandru Ardelean #define   ADIN1300_GE_RGMII_RX_MSK		GENMASK(8, 6)
77c83e6163SAlexandru Ardelean #define   ADIN1300_GE_RGMII_RX_SEL(x)		\
78c83e6163SAlexandru Ardelean 		FIELD_PREP(ADIN1300_GE_RGMII_RX_MSK, x)
79c83e6163SAlexandru Ardelean #define   ADIN1300_GE_RGMII_GTX_MSK		GENMASK(5, 3)
80c83e6163SAlexandru Ardelean #define   ADIN1300_GE_RGMII_GTX_SEL(x)		\
81c83e6163SAlexandru Ardelean 		FIELD_PREP(ADIN1300_GE_RGMII_GTX_MSK, x)
82d6200c8fSAlexandru Ardelean #define   ADIN1300_GE_RGMII_RXID_EN		BIT(2)
83d6200c8fSAlexandru Ardelean #define   ADIN1300_GE_RGMII_TXID_EN		BIT(1)
84d6200c8fSAlexandru Ardelean #define   ADIN1300_GE_RGMII_EN			BIT(0)
85d6200c8fSAlexandru Ardelean 
86c83e6163SAlexandru Ardelean /* RGMII internal delay settings for rx and tx for ADIN1300 */
87c83e6163SAlexandru Ardelean #define ADIN1300_RGMII_1_60_NS			0x0001
88c83e6163SAlexandru Ardelean #define ADIN1300_RGMII_1_80_NS			0x0002
89c83e6163SAlexandru Ardelean #define	ADIN1300_RGMII_2_00_NS			0x0000
90c83e6163SAlexandru Ardelean #define	ADIN1300_RGMII_2_20_NS			0x0006
91c83e6163SAlexandru Ardelean #define	ADIN1300_RGMII_2_40_NS			0x0007
92c83e6163SAlexandru Ardelean 
93d6200c8fSAlexandru Ardelean #define ADIN1300_GE_RMII_CFG_REG		0xff24
94f1012fb4SAlexandru Ardelean #define   ADIN1300_GE_RMII_FIFO_DEPTH_MSK	GENMASK(6, 4)
95f1012fb4SAlexandru Ardelean #define   ADIN1300_GE_RMII_FIFO_DEPTH_SEL(x)	\
96f1012fb4SAlexandru Ardelean 		FIELD_PREP(ADIN1300_GE_RMII_FIFO_DEPTH_MSK, x)
97d6200c8fSAlexandru Ardelean #define   ADIN1300_GE_RMII_EN			BIT(0)
98d6200c8fSAlexandru Ardelean 
99f1012fb4SAlexandru Ardelean /* RMII fifo depth values */
100f1012fb4SAlexandru Ardelean #define ADIN1300_RMII_4_BITS			0x0000
101f1012fb4SAlexandru Ardelean #define ADIN1300_RMII_8_BITS			0x0001
102f1012fb4SAlexandru Ardelean #define ADIN1300_RMII_12_BITS			0x0002
103f1012fb4SAlexandru Ardelean #define ADIN1300_RMII_16_BITS			0x0003
104f1012fb4SAlexandru Ardelean #define ADIN1300_RMII_20_BITS			0x0004
105f1012fb4SAlexandru Ardelean #define ADIN1300_RMII_24_BITS			0x0005
106f1012fb4SAlexandru Ardelean 
107c83e6163SAlexandru Ardelean /**
108c83e6163SAlexandru Ardelean  * struct adin_cfg_reg_map - map a config value to aregister value
109c83e6163SAlexandru Ardelean  * @cfg		value in device configuration
110c83e6163SAlexandru Ardelean  * @reg		value in the register
111c83e6163SAlexandru Ardelean  */
112c83e6163SAlexandru Ardelean struct adin_cfg_reg_map {
113c83e6163SAlexandru Ardelean 	int cfg;
114c83e6163SAlexandru Ardelean 	int reg;
115c83e6163SAlexandru Ardelean };
116c83e6163SAlexandru Ardelean 
117c83e6163SAlexandru Ardelean static const struct adin_cfg_reg_map adin_rgmii_delays[] = {
118c83e6163SAlexandru Ardelean 	{ 1600, ADIN1300_RGMII_1_60_NS },
119c83e6163SAlexandru Ardelean 	{ 1800, ADIN1300_RGMII_1_80_NS },
120c83e6163SAlexandru Ardelean 	{ 2000, ADIN1300_RGMII_2_00_NS },
121c83e6163SAlexandru Ardelean 	{ 2200, ADIN1300_RGMII_2_20_NS },
122c83e6163SAlexandru Ardelean 	{ 2400, ADIN1300_RGMII_2_40_NS },
123c83e6163SAlexandru Ardelean 	{ },
124c83e6163SAlexandru Ardelean };
125c83e6163SAlexandru Ardelean 
126f1012fb4SAlexandru Ardelean static const struct adin_cfg_reg_map adin_rmii_fifo_depths[] = {
127f1012fb4SAlexandru Ardelean 	{ 4,  ADIN1300_RMII_4_BITS },
128f1012fb4SAlexandru Ardelean 	{ 8,  ADIN1300_RMII_8_BITS },
129f1012fb4SAlexandru Ardelean 	{ 12, ADIN1300_RMII_12_BITS },
130f1012fb4SAlexandru Ardelean 	{ 16, ADIN1300_RMII_16_BITS },
131f1012fb4SAlexandru Ardelean 	{ 20, ADIN1300_RMII_20_BITS },
132f1012fb4SAlexandru Ardelean 	{ 24, ADIN1300_RMII_24_BITS },
133f1012fb4SAlexandru Ardelean 	{ },
134f1012fb4SAlexandru Ardelean };
135f1012fb4SAlexandru Ardelean 
136c6aa697cSAlexandru Ardelean /**
137c6aa697cSAlexandru Ardelean  * struct adin_clause45_mmd_map - map to convert Clause 45 regs to Clause 22
138c6aa697cSAlexandru Ardelean  * @devad		device address used in Clause 45 access
139c6aa697cSAlexandru Ardelean  * @cl45_regnum		register address defined by Clause 45
140c6aa697cSAlexandru Ardelean  * @adin_regnum		equivalent register address accessible via Clause 22
141c6aa697cSAlexandru Ardelean  */
142c6aa697cSAlexandru Ardelean struct adin_clause45_mmd_map {
143c6aa697cSAlexandru Ardelean 	int devad;
144c6aa697cSAlexandru Ardelean 	u16 cl45_regnum;
145c6aa697cSAlexandru Ardelean 	u16 adin_regnum;
146c6aa697cSAlexandru Ardelean };
147c6aa697cSAlexandru Ardelean 
148c6aa697cSAlexandru Ardelean static struct adin_clause45_mmd_map adin_clause45_mmd_map[] = {
149c6aa697cSAlexandru Ardelean 	{ MDIO_MMD_PCS,	MDIO_PCS_EEE_ABLE,	ADIN1300_EEE_CAP_REG },
150c6aa697cSAlexandru Ardelean 	{ MDIO_MMD_AN,	MDIO_AN_EEE_LPABLE,	ADIN1300_EEE_LPABLE_REG },
151c6aa697cSAlexandru Ardelean 	{ MDIO_MMD_AN,	MDIO_AN_EEE_ADV,	ADIN1300_EEE_ADV_REG },
152c6aa697cSAlexandru Ardelean 	{ MDIO_MMD_PCS,	MDIO_CTRL1,		ADIN1300_CLOCK_STOP_REG },
153c6aa697cSAlexandru Ardelean 	{ MDIO_MMD_PCS, MDIO_PCS_EEE_WK_ERR,	ADIN1300_LPI_WAKE_ERR_CNT_REG },
154c6aa697cSAlexandru Ardelean };
155c6aa697cSAlexandru Ardelean 
1569fe0b8d6SAlexandru Ardelean struct adin_hw_stat {
1579fe0b8d6SAlexandru Ardelean 	const char *string;
1589fe0b8d6SAlexandru Ardelean 	u16 reg1;
1599fe0b8d6SAlexandru Ardelean 	u16 reg2;
1609fe0b8d6SAlexandru Ardelean };
1619fe0b8d6SAlexandru Ardelean 
1629fe0b8d6SAlexandru Ardelean static struct adin_hw_stat adin_hw_stats[] = {
1639fe0b8d6SAlexandru Ardelean 	{ "total_frames_checked_count",		0x940A, 0x940B }, /* hi + lo */
1649fe0b8d6SAlexandru Ardelean 	{ "length_error_frames_count",		0x940C },
1659fe0b8d6SAlexandru Ardelean 	{ "alignment_error_frames_count",	0x940D },
1669fe0b8d6SAlexandru Ardelean 	{ "symbol_error_count",			0x940E },
1679fe0b8d6SAlexandru Ardelean 	{ "oversized_frames_count",		0x940F },
1689fe0b8d6SAlexandru Ardelean 	{ "undersized_frames_count",		0x9410 },
1699fe0b8d6SAlexandru Ardelean 	{ "odd_nibble_frames_count",		0x9411 },
1709fe0b8d6SAlexandru Ardelean 	{ "odd_preamble_packet_count",		0x9412 },
1719fe0b8d6SAlexandru Ardelean 	{ "dribble_bits_frames_count",		0x9413 },
1729fe0b8d6SAlexandru Ardelean 	{ "false_carrier_events_count",		0x9414 },
1739fe0b8d6SAlexandru Ardelean };
1749fe0b8d6SAlexandru Ardelean 
1759fe0b8d6SAlexandru Ardelean /**
1769fe0b8d6SAlexandru Ardelean  * struct adin_priv - ADIN PHY driver private data
1779fe0b8d6SAlexandru Ardelean  * stats		statistic counters for the PHY
1789fe0b8d6SAlexandru Ardelean  */
1799fe0b8d6SAlexandru Ardelean struct adin_priv {
1809fe0b8d6SAlexandru Ardelean 	u64			stats[ARRAY_SIZE(adin_hw_stats)];
1819fe0b8d6SAlexandru Ardelean };
1829fe0b8d6SAlexandru Ardelean 
183c83e6163SAlexandru Ardelean static int adin_lookup_reg_value(const struct adin_cfg_reg_map *tbl, int cfg)
184c83e6163SAlexandru Ardelean {
185c83e6163SAlexandru Ardelean 	size_t i;
186c83e6163SAlexandru Ardelean 
187c83e6163SAlexandru Ardelean 	for (i = 0; tbl[i].cfg; i++) {
188c83e6163SAlexandru Ardelean 		if (tbl[i].cfg == cfg)
189c83e6163SAlexandru Ardelean 			return tbl[i].reg;
190c83e6163SAlexandru Ardelean 	}
191c83e6163SAlexandru Ardelean 
192c83e6163SAlexandru Ardelean 	return -EINVAL;
193c83e6163SAlexandru Ardelean }
194c83e6163SAlexandru Ardelean 
195c83e6163SAlexandru Ardelean static u32 adin_get_reg_value(struct phy_device *phydev,
196c83e6163SAlexandru Ardelean 			      const char *prop_name,
197c83e6163SAlexandru Ardelean 			      const struct adin_cfg_reg_map *tbl,
198c83e6163SAlexandru Ardelean 			      u32 dflt)
199c83e6163SAlexandru Ardelean {
200c83e6163SAlexandru Ardelean 	struct device *dev = &phydev->mdio.dev;
201c83e6163SAlexandru Ardelean 	u32 val;
202c83e6163SAlexandru Ardelean 	int rc;
203c83e6163SAlexandru Ardelean 
204c83e6163SAlexandru Ardelean 	if (device_property_read_u32(dev, prop_name, &val))
205c83e6163SAlexandru Ardelean 		return dflt;
206c83e6163SAlexandru Ardelean 
207c83e6163SAlexandru Ardelean 	rc = adin_lookup_reg_value(tbl, val);
208c83e6163SAlexandru Ardelean 	if (rc < 0) {
209c83e6163SAlexandru Ardelean 		phydev_warn(phydev,
210c83e6163SAlexandru Ardelean 			    "Unsupported value %u for %s using default (%u)\n",
211c83e6163SAlexandru Ardelean 			    val, prop_name, dflt);
212c83e6163SAlexandru Ardelean 		return dflt;
213c83e6163SAlexandru Ardelean 	}
214c83e6163SAlexandru Ardelean 
215c83e6163SAlexandru Ardelean 	return rc;
216c83e6163SAlexandru Ardelean }
217c83e6163SAlexandru Ardelean 
218d6200c8fSAlexandru Ardelean static int adin_config_rgmii_mode(struct phy_device *phydev)
219d6200c8fSAlexandru Ardelean {
220c83e6163SAlexandru Ardelean 	u32 val;
221d6200c8fSAlexandru Ardelean 	int reg;
222d6200c8fSAlexandru Ardelean 
223d6200c8fSAlexandru Ardelean 	if (!phy_interface_is_rgmii(phydev))
224d6200c8fSAlexandru Ardelean 		return phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1,
225d6200c8fSAlexandru Ardelean 					  ADIN1300_GE_RGMII_CFG_REG,
226d6200c8fSAlexandru Ardelean 					  ADIN1300_GE_RGMII_EN);
227d6200c8fSAlexandru Ardelean 
228d6200c8fSAlexandru Ardelean 	reg = phy_read_mmd(phydev, MDIO_MMD_VEND1, ADIN1300_GE_RGMII_CFG_REG);
229d6200c8fSAlexandru Ardelean 	if (reg < 0)
230d6200c8fSAlexandru Ardelean 		return reg;
231d6200c8fSAlexandru Ardelean 
232d6200c8fSAlexandru Ardelean 	reg |= ADIN1300_GE_RGMII_EN;
233d6200c8fSAlexandru Ardelean 
234d6200c8fSAlexandru Ardelean 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
235d6200c8fSAlexandru Ardelean 	    phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
236d6200c8fSAlexandru Ardelean 		reg |= ADIN1300_GE_RGMII_RXID_EN;
237c83e6163SAlexandru Ardelean 
238c83e6163SAlexandru Ardelean 		val = adin_get_reg_value(phydev, "adi,rx-internal-delay-ps",
239c83e6163SAlexandru Ardelean 					 adin_rgmii_delays,
240c83e6163SAlexandru Ardelean 					 ADIN1300_RGMII_2_00_NS);
241c83e6163SAlexandru Ardelean 		reg &= ~ADIN1300_GE_RGMII_RX_MSK;
242c83e6163SAlexandru Ardelean 		reg |= ADIN1300_GE_RGMII_RX_SEL(val);
243d6200c8fSAlexandru Ardelean 	} else {
244d6200c8fSAlexandru Ardelean 		reg &= ~ADIN1300_GE_RGMII_RXID_EN;
245d6200c8fSAlexandru Ardelean 	}
246d6200c8fSAlexandru Ardelean 
247d6200c8fSAlexandru Ardelean 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
248d6200c8fSAlexandru Ardelean 	    phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
249d6200c8fSAlexandru Ardelean 		reg |= ADIN1300_GE_RGMII_TXID_EN;
250c83e6163SAlexandru Ardelean 
251c83e6163SAlexandru Ardelean 		val = adin_get_reg_value(phydev, "adi,tx-internal-delay-ps",
252c83e6163SAlexandru Ardelean 					 adin_rgmii_delays,
253c83e6163SAlexandru Ardelean 					 ADIN1300_RGMII_2_00_NS);
254c83e6163SAlexandru Ardelean 		reg &= ~ADIN1300_GE_RGMII_GTX_MSK;
255c83e6163SAlexandru Ardelean 		reg |= ADIN1300_GE_RGMII_GTX_SEL(val);
256d6200c8fSAlexandru Ardelean 	} else {
257d6200c8fSAlexandru Ardelean 		reg &= ~ADIN1300_GE_RGMII_TXID_EN;
258d6200c8fSAlexandru Ardelean 	}
259d6200c8fSAlexandru Ardelean 
260d6200c8fSAlexandru Ardelean 	return phy_write_mmd(phydev, MDIO_MMD_VEND1,
261d6200c8fSAlexandru Ardelean 			     ADIN1300_GE_RGMII_CFG_REG, reg);
262d6200c8fSAlexandru Ardelean }
263d6200c8fSAlexandru Ardelean 
264d6200c8fSAlexandru Ardelean static int adin_config_rmii_mode(struct phy_device *phydev)
265d6200c8fSAlexandru Ardelean {
266f1012fb4SAlexandru Ardelean 	u32 val;
267d6200c8fSAlexandru Ardelean 	int reg;
268d6200c8fSAlexandru Ardelean 
269d6200c8fSAlexandru Ardelean 	if (phydev->interface != PHY_INTERFACE_MODE_RMII)
270d6200c8fSAlexandru Ardelean 		return phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1,
271d6200c8fSAlexandru Ardelean 					  ADIN1300_GE_RMII_CFG_REG,
272d6200c8fSAlexandru Ardelean 					  ADIN1300_GE_RMII_EN);
273d6200c8fSAlexandru Ardelean 
274d6200c8fSAlexandru Ardelean 	reg = phy_read_mmd(phydev, MDIO_MMD_VEND1, ADIN1300_GE_RMII_CFG_REG);
275d6200c8fSAlexandru Ardelean 	if (reg < 0)
276d6200c8fSAlexandru Ardelean 		return reg;
277d6200c8fSAlexandru Ardelean 
278d6200c8fSAlexandru Ardelean 	reg |= ADIN1300_GE_RMII_EN;
279d6200c8fSAlexandru Ardelean 
280f1012fb4SAlexandru Ardelean 	val = adin_get_reg_value(phydev, "adi,fifo-depth-bits",
281f1012fb4SAlexandru Ardelean 				 adin_rmii_fifo_depths,
282f1012fb4SAlexandru Ardelean 				 ADIN1300_RMII_8_BITS);
283f1012fb4SAlexandru Ardelean 
284f1012fb4SAlexandru Ardelean 	reg &= ~ADIN1300_GE_RMII_FIFO_DEPTH_MSK;
285f1012fb4SAlexandru Ardelean 	reg |= ADIN1300_GE_RMII_FIFO_DEPTH_SEL(val);
286f1012fb4SAlexandru Ardelean 
287d6200c8fSAlexandru Ardelean 	return phy_write_mmd(phydev, MDIO_MMD_VEND1,
288d6200c8fSAlexandru Ardelean 			     ADIN1300_GE_RMII_CFG_REG, reg);
289d6200c8fSAlexandru Ardelean }
290d6200c8fSAlexandru Ardelean 
2912d99b584SAlexandru Ardelean static int adin_get_downshift(struct phy_device *phydev, u8 *data)
2922d99b584SAlexandru Ardelean {
2932d99b584SAlexandru Ardelean 	int val, cnt, enable;
2942d99b584SAlexandru Ardelean 
2952d99b584SAlexandru Ardelean 	val = phy_read(phydev, ADIN1300_PHY_CTRL2);
2962d99b584SAlexandru Ardelean 	if (val < 0)
2972d99b584SAlexandru Ardelean 		return val;
2982d99b584SAlexandru Ardelean 
2992d99b584SAlexandru Ardelean 	cnt = phy_read(phydev, ADIN1300_PHY_CTRL3);
3002d99b584SAlexandru Ardelean 	if (cnt < 0)
3012d99b584SAlexandru Ardelean 		return cnt;
3022d99b584SAlexandru Ardelean 
3032d99b584SAlexandru Ardelean 	enable = FIELD_GET(ADIN1300_DOWNSPEEDS_EN, val);
3042d99b584SAlexandru Ardelean 	cnt = FIELD_GET(ADIN1300_DOWNSPEED_RETRIES_MSK, cnt);
3052d99b584SAlexandru Ardelean 
3062d99b584SAlexandru Ardelean 	*data = (enable && cnt) ? cnt : DOWNSHIFT_DEV_DISABLE;
3072d99b584SAlexandru Ardelean 
3082d99b584SAlexandru Ardelean 	return 0;
3092d99b584SAlexandru Ardelean }
3102d99b584SAlexandru Ardelean 
3112d99b584SAlexandru Ardelean static int adin_set_downshift(struct phy_device *phydev, u8 cnt)
3122d99b584SAlexandru Ardelean {
3132d99b584SAlexandru Ardelean 	u16 val;
3142d99b584SAlexandru Ardelean 	int rc;
3152d99b584SAlexandru Ardelean 
3162d99b584SAlexandru Ardelean 	if (cnt == DOWNSHIFT_DEV_DISABLE)
3172d99b584SAlexandru Ardelean 		return phy_clear_bits(phydev, ADIN1300_PHY_CTRL2,
3182d99b584SAlexandru Ardelean 				      ADIN1300_DOWNSPEEDS_EN);
3192d99b584SAlexandru Ardelean 
3202d99b584SAlexandru Ardelean 	if (cnt > 7)
3212d99b584SAlexandru Ardelean 		return -E2BIG;
3222d99b584SAlexandru Ardelean 
3232d99b584SAlexandru Ardelean 	val = FIELD_PREP(ADIN1300_DOWNSPEED_RETRIES_MSK, cnt);
3242d99b584SAlexandru Ardelean 	val |= ADIN1300_LINKING_EN;
3252d99b584SAlexandru Ardelean 
3262d99b584SAlexandru Ardelean 	rc = phy_modify(phydev, ADIN1300_PHY_CTRL3,
3272d99b584SAlexandru Ardelean 			ADIN1300_LINKING_EN | ADIN1300_DOWNSPEED_RETRIES_MSK,
3282d99b584SAlexandru Ardelean 			val);
3292d99b584SAlexandru Ardelean 	if (rc < 0)
3302d99b584SAlexandru Ardelean 		return rc;
3312d99b584SAlexandru Ardelean 
3322d99b584SAlexandru Ardelean 	return phy_set_bits(phydev, ADIN1300_PHY_CTRL2,
3332d99b584SAlexandru Ardelean 			    ADIN1300_DOWNSPEEDS_EN);
3342d99b584SAlexandru Ardelean }
3352d99b584SAlexandru Ardelean 
33665d7be09SAlexandru Ardelean static int adin_get_edpd(struct phy_device *phydev, u16 *tx_interval)
33765d7be09SAlexandru Ardelean {
33865d7be09SAlexandru Ardelean 	int val;
33965d7be09SAlexandru Ardelean 
34065d7be09SAlexandru Ardelean 	val = phy_read(phydev, ADIN1300_PHY_CTRL_STATUS2);
34165d7be09SAlexandru Ardelean 	if (val < 0)
34265d7be09SAlexandru Ardelean 		return val;
34365d7be09SAlexandru Ardelean 
34465d7be09SAlexandru Ardelean 	if (ADIN1300_NRG_PD_EN & val) {
34565d7be09SAlexandru Ardelean 		if (val & ADIN1300_NRG_PD_TX_EN)
34665d7be09SAlexandru Ardelean 			/* default is 1 second */
34765d7be09SAlexandru Ardelean 			*tx_interval = ETHTOOL_PHY_EDPD_DFLT_TX_MSECS;
34865d7be09SAlexandru Ardelean 		else
34965d7be09SAlexandru Ardelean 			*tx_interval = ETHTOOL_PHY_EDPD_NO_TX;
35065d7be09SAlexandru Ardelean 	} else {
35165d7be09SAlexandru Ardelean 		*tx_interval = ETHTOOL_PHY_EDPD_DISABLE;
35265d7be09SAlexandru Ardelean 	}
35365d7be09SAlexandru Ardelean 
35465d7be09SAlexandru Ardelean 	return 0;
35565d7be09SAlexandru Ardelean }
35665d7be09SAlexandru Ardelean 
35765d7be09SAlexandru Ardelean static int adin_set_edpd(struct phy_device *phydev, u16 tx_interval)
35865d7be09SAlexandru Ardelean {
35965d7be09SAlexandru Ardelean 	u16 val;
36065d7be09SAlexandru Ardelean 
36165d7be09SAlexandru Ardelean 	if (tx_interval == ETHTOOL_PHY_EDPD_DISABLE)
36265d7be09SAlexandru Ardelean 		return phy_clear_bits(phydev, ADIN1300_PHY_CTRL_STATUS2,
36365d7be09SAlexandru Ardelean 				(ADIN1300_NRG_PD_EN | ADIN1300_NRG_PD_TX_EN));
36465d7be09SAlexandru Ardelean 
36565d7be09SAlexandru Ardelean 	val = ADIN1300_NRG_PD_EN;
36665d7be09SAlexandru Ardelean 
36765d7be09SAlexandru Ardelean 	switch (tx_interval) {
36865d7be09SAlexandru Ardelean 	case 1000: /* 1 second */
36965d7be09SAlexandru Ardelean 		/* fallthrough */
37065d7be09SAlexandru Ardelean 	case ETHTOOL_PHY_EDPD_DFLT_TX_MSECS:
37165d7be09SAlexandru Ardelean 		val |= ADIN1300_NRG_PD_TX_EN;
37265d7be09SAlexandru Ardelean 		/* fallthrough */
37365d7be09SAlexandru Ardelean 	case ETHTOOL_PHY_EDPD_NO_TX:
37465d7be09SAlexandru Ardelean 		break;
37565d7be09SAlexandru Ardelean 	default:
37665d7be09SAlexandru Ardelean 		return -EINVAL;
37765d7be09SAlexandru Ardelean 	}
37865d7be09SAlexandru Ardelean 
37965d7be09SAlexandru Ardelean 	return phy_modify(phydev, ADIN1300_PHY_CTRL_STATUS2,
38065d7be09SAlexandru Ardelean 			  (ADIN1300_NRG_PD_EN | ADIN1300_NRG_PD_TX_EN),
38165d7be09SAlexandru Ardelean 			  val);
38265d7be09SAlexandru Ardelean }
38365d7be09SAlexandru Ardelean 
3842d99b584SAlexandru Ardelean static int adin_get_tunable(struct phy_device *phydev,
3852d99b584SAlexandru Ardelean 			    struct ethtool_tunable *tuna, void *data)
3862d99b584SAlexandru Ardelean {
3872d99b584SAlexandru Ardelean 	switch (tuna->id) {
3882d99b584SAlexandru Ardelean 	case ETHTOOL_PHY_DOWNSHIFT:
3892d99b584SAlexandru Ardelean 		return adin_get_downshift(phydev, data);
39065d7be09SAlexandru Ardelean 	case ETHTOOL_PHY_EDPD:
39165d7be09SAlexandru Ardelean 		return adin_get_edpd(phydev, data);
3922d99b584SAlexandru Ardelean 	default:
3932d99b584SAlexandru Ardelean 		return -EOPNOTSUPP;
3942d99b584SAlexandru Ardelean 	}
3952d99b584SAlexandru Ardelean }
3962d99b584SAlexandru Ardelean 
3972d99b584SAlexandru Ardelean static int adin_set_tunable(struct phy_device *phydev,
3982d99b584SAlexandru Ardelean 			    struct ethtool_tunable *tuna, const void *data)
3992d99b584SAlexandru Ardelean {
4002d99b584SAlexandru Ardelean 	switch (tuna->id) {
4012d99b584SAlexandru Ardelean 	case ETHTOOL_PHY_DOWNSHIFT:
4022d99b584SAlexandru Ardelean 		return adin_set_downshift(phydev, *(const u8 *)data);
40365d7be09SAlexandru Ardelean 	case ETHTOOL_PHY_EDPD:
40465d7be09SAlexandru Ardelean 		return adin_set_edpd(phydev, *(const u16 *)data);
4052d99b584SAlexandru Ardelean 	default:
4062d99b584SAlexandru Ardelean 		return -EOPNOTSUPP;
4072d99b584SAlexandru Ardelean 	}
4082d99b584SAlexandru Ardelean }
4092d99b584SAlexandru Ardelean 
4109c102981SAlexandru Ardelean static int adin_config_init(struct phy_device *phydev)
4119c102981SAlexandru Ardelean {
412d6200c8fSAlexandru Ardelean 	int rc;
413d6200c8fSAlexandru Ardelean 
414b422d1b6SAlexandru Ardelean 	phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
415b422d1b6SAlexandru Ardelean 
416d6200c8fSAlexandru Ardelean 	rc = adin_config_rgmii_mode(phydev);
417d6200c8fSAlexandru Ardelean 	if (rc < 0)
418d6200c8fSAlexandru Ardelean 		return rc;
419d6200c8fSAlexandru Ardelean 
420d6200c8fSAlexandru Ardelean 	rc = adin_config_rmii_mode(phydev);
421d6200c8fSAlexandru Ardelean 	if (rc < 0)
422d6200c8fSAlexandru Ardelean 		return rc;
423d6200c8fSAlexandru Ardelean 
4242d99b584SAlexandru Ardelean 	rc = adin_set_downshift(phydev, 4);
4252d99b584SAlexandru Ardelean 	if (rc < 0)
4262d99b584SAlexandru Ardelean 		return rc;
4272d99b584SAlexandru Ardelean 
42865d7be09SAlexandru Ardelean 	rc = adin_set_edpd(phydev, ETHTOOL_PHY_EDPD_DFLT_TX_MSECS);
42965d7be09SAlexandru Ardelean 	if (rc < 0)
43065d7be09SAlexandru Ardelean 		return rc;
43165d7be09SAlexandru Ardelean 
432d6200c8fSAlexandru Ardelean 	phydev_dbg(phydev, "PHY is using mode '%s'\n",
433d6200c8fSAlexandru Ardelean 		   phy_modes(phydev->interface));
434d6200c8fSAlexandru Ardelean 
435d6200c8fSAlexandru Ardelean 	return 0;
4369c102981SAlexandru Ardelean }
4379c102981SAlexandru Ardelean 
438fb44b8d6SAlexandru Ardelean static int adin_phy_ack_intr(struct phy_device *phydev)
439fb44b8d6SAlexandru Ardelean {
440fb44b8d6SAlexandru Ardelean 	/* Clear pending interrupts */
441fb44b8d6SAlexandru Ardelean 	int rc = phy_read(phydev, ADIN1300_INT_STATUS_REG);
442fb44b8d6SAlexandru Ardelean 
443fb44b8d6SAlexandru Ardelean 	return rc < 0 ? rc : 0;
444fb44b8d6SAlexandru Ardelean }
445fb44b8d6SAlexandru Ardelean 
446fb44b8d6SAlexandru Ardelean static int adin_phy_config_intr(struct phy_device *phydev)
447fb44b8d6SAlexandru Ardelean {
448fb44b8d6SAlexandru Ardelean 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
449fb44b8d6SAlexandru Ardelean 		return phy_set_bits(phydev, ADIN1300_INT_MASK_REG,
450fb44b8d6SAlexandru Ardelean 				    ADIN1300_INT_MASK_EN);
451fb44b8d6SAlexandru Ardelean 
452fb44b8d6SAlexandru Ardelean 	return phy_clear_bits(phydev, ADIN1300_INT_MASK_REG,
453fb44b8d6SAlexandru Ardelean 			      ADIN1300_INT_MASK_EN);
454fb44b8d6SAlexandru Ardelean }
455fb44b8d6SAlexandru Ardelean 
456c6aa697cSAlexandru Ardelean static int adin_cl45_to_adin_reg(struct phy_device *phydev, int devad,
457c6aa697cSAlexandru Ardelean 				 u16 cl45_regnum)
458c6aa697cSAlexandru Ardelean {
459c6aa697cSAlexandru Ardelean 	struct adin_clause45_mmd_map *m;
460c6aa697cSAlexandru Ardelean 	int i;
461c6aa697cSAlexandru Ardelean 
462c6aa697cSAlexandru Ardelean 	if (devad == MDIO_MMD_VEND1)
463c6aa697cSAlexandru Ardelean 		return cl45_regnum;
464c6aa697cSAlexandru Ardelean 
465c6aa697cSAlexandru Ardelean 	for (i = 0; i < ARRAY_SIZE(adin_clause45_mmd_map); i++) {
466c6aa697cSAlexandru Ardelean 		m = &adin_clause45_mmd_map[i];
467c6aa697cSAlexandru Ardelean 		if (m->devad == devad && m->cl45_regnum == cl45_regnum)
468c6aa697cSAlexandru Ardelean 			return m->adin_regnum;
469c6aa697cSAlexandru Ardelean 	}
470c6aa697cSAlexandru Ardelean 
471c6aa697cSAlexandru Ardelean 	phydev_err(phydev,
472c6aa697cSAlexandru Ardelean 		   "No translation available for devad: %d reg: %04x\n",
473c6aa697cSAlexandru Ardelean 		   devad, cl45_regnum);
474c6aa697cSAlexandru Ardelean 
475c6aa697cSAlexandru Ardelean 	return -EINVAL;
476c6aa697cSAlexandru Ardelean }
477c6aa697cSAlexandru Ardelean 
4783e32d020SAlexandru Ardelean static int adin_read_mmd(struct phy_device *phydev, int devad, u16 regnum)
4793e32d020SAlexandru Ardelean {
4803e32d020SAlexandru Ardelean 	struct mii_bus *bus = phydev->mdio.bus;
4813e32d020SAlexandru Ardelean 	int phy_addr = phydev->mdio.addr;
482c6aa697cSAlexandru Ardelean 	int adin_regnum;
4833e32d020SAlexandru Ardelean 	int err;
4843e32d020SAlexandru Ardelean 
485c6aa697cSAlexandru Ardelean 	adin_regnum = adin_cl45_to_adin_reg(phydev, devad, regnum);
486c6aa697cSAlexandru Ardelean 	if (adin_regnum < 0)
487c6aa697cSAlexandru Ardelean 		return adin_regnum;
488c6aa697cSAlexandru Ardelean 
489c6aa697cSAlexandru Ardelean 	err = __mdiobus_write(bus, phy_addr, ADIN1300_MII_EXT_REG_PTR,
490c6aa697cSAlexandru Ardelean 			      adin_regnum);
4913e32d020SAlexandru Ardelean 	if (err)
4923e32d020SAlexandru Ardelean 		return err;
4933e32d020SAlexandru Ardelean 
4943e32d020SAlexandru Ardelean 	return __mdiobus_read(bus, phy_addr, ADIN1300_MII_EXT_REG_DATA);
4953e32d020SAlexandru Ardelean }
4963e32d020SAlexandru Ardelean 
4973e32d020SAlexandru Ardelean static int adin_write_mmd(struct phy_device *phydev, int devad, u16 regnum,
4983e32d020SAlexandru Ardelean 			  u16 val)
4993e32d020SAlexandru Ardelean {
5003e32d020SAlexandru Ardelean 	struct mii_bus *bus = phydev->mdio.bus;
5013e32d020SAlexandru Ardelean 	int phy_addr = phydev->mdio.addr;
502c6aa697cSAlexandru Ardelean 	int adin_regnum;
5033e32d020SAlexandru Ardelean 	int err;
5043e32d020SAlexandru Ardelean 
505c6aa697cSAlexandru Ardelean 	adin_regnum = adin_cl45_to_adin_reg(phydev, devad, regnum);
506c6aa697cSAlexandru Ardelean 	if (adin_regnum < 0)
507c6aa697cSAlexandru Ardelean 		return adin_regnum;
508c6aa697cSAlexandru Ardelean 
509c6aa697cSAlexandru Ardelean 	err = __mdiobus_write(bus, phy_addr, ADIN1300_MII_EXT_REG_PTR,
510c6aa697cSAlexandru Ardelean 			      adin_regnum);
5113e32d020SAlexandru Ardelean 	if (err)
5123e32d020SAlexandru Ardelean 		return err;
5133e32d020SAlexandru Ardelean 
5143e32d020SAlexandru Ardelean 	return __mdiobus_write(bus, phy_addr, ADIN1300_MII_EXT_REG_DATA, val);
5153e32d020SAlexandru Ardelean }
5163e32d020SAlexandru Ardelean 
517b422d1b6SAlexandru Ardelean static int adin_config_mdix(struct phy_device *phydev)
518b422d1b6SAlexandru Ardelean {
519b422d1b6SAlexandru Ardelean 	bool auto_en, mdix_en;
520b422d1b6SAlexandru Ardelean 	int reg;
521b422d1b6SAlexandru Ardelean 
522b422d1b6SAlexandru Ardelean 	mdix_en = false;
523b422d1b6SAlexandru Ardelean 	auto_en = false;
524b422d1b6SAlexandru Ardelean 	switch (phydev->mdix_ctrl) {
525b422d1b6SAlexandru Ardelean 	case ETH_TP_MDI:
526b422d1b6SAlexandru Ardelean 		break;
527b422d1b6SAlexandru Ardelean 	case ETH_TP_MDI_X:
528b422d1b6SAlexandru Ardelean 		mdix_en = true;
529b422d1b6SAlexandru Ardelean 		break;
530b422d1b6SAlexandru Ardelean 	case ETH_TP_MDI_AUTO:
531b422d1b6SAlexandru Ardelean 		auto_en = true;
532b422d1b6SAlexandru Ardelean 		break;
533b422d1b6SAlexandru Ardelean 	default:
534b422d1b6SAlexandru Ardelean 		return -EINVAL;
535b422d1b6SAlexandru Ardelean 	}
536b422d1b6SAlexandru Ardelean 
537b422d1b6SAlexandru Ardelean 	reg = phy_read(phydev, ADIN1300_PHY_CTRL1);
538b422d1b6SAlexandru Ardelean 	if (reg < 0)
539b422d1b6SAlexandru Ardelean 		return reg;
540b422d1b6SAlexandru Ardelean 
541b422d1b6SAlexandru Ardelean 	if (mdix_en)
542b422d1b6SAlexandru Ardelean 		reg |= ADIN1300_MAN_MDIX_EN;
543b422d1b6SAlexandru Ardelean 	else
544b422d1b6SAlexandru Ardelean 		reg &= ~ADIN1300_MAN_MDIX_EN;
545b422d1b6SAlexandru Ardelean 
546b422d1b6SAlexandru Ardelean 	if (auto_en)
547b422d1b6SAlexandru Ardelean 		reg |= ADIN1300_AUTO_MDI_EN;
548b422d1b6SAlexandru Ardelean 	else
549b422d1b6SAlexandru Ardelean 		reg &= ~ADIN1300_AUTO_MDI_EN;
550b422d1b6SAlexandru Ardelean 
551b422d1b6SAlexandru Ardelean 	return phy_write(phydev, ADIN1300_PHY_CTRL1, reg);
552b422d1b6SAlexandru Ardelean }
553b422d1b6SAlexandru Ardelean 
554b422d1b6SAlexandru Ardelean static int adin_config_aneg(struct phy_device *phydev)
555b422d1b6SAlexandru Ardelean {
556b422d1b6SAlexandru Ardelean 	int ret;
557b422d1b6SAlexandru Ardelean 
558b422d1b6SAlexandru Ardelean 	ret = adin_config_mdix(phydev);
559b422d1b6SAlexandru Ardelean 	if (ret)
560b422d1b6SAlexandru Ardelean 		return ret;
561b422d1b6SAlexandru Ardelean 
562b422d1b6SAlexandru Ardelean 	return genphy_config_aneg(phydev);
563b422d1b6SAlexandru Ardelean }
564b422d1b6SAlexandru Ardelean 
565b422d1b6SAlexandru Ardelean static int adin_mdix_update(struct phy_device *phydev)
566b422d1b6SAlexandru Ardelean {
567b422d1b6SAlexandru Ardelean 	bool auto_en, mdix_en;
568b422d1b6SAlexandru Ardelean 	bool swapped;
569b422d1b6SAlexandru Ardelean 	int reg;
570b422d1b6SAlexandru Ardelean 
571b422d1b6SAlexandru Ardelean 	reg = phy_read(phydev, ADIN1300_PHY_CTRL1);
572b422d1b6SAlexandru Ardelean 	if (reg < 0)
573b422d1b6SAlexandru Ardelean 		return reg;
574b422d1b6SAlexandru Ardelean 
575b422d1b6SAlexandru Ardelean 	auto_en = !!(reg & ADIN1300_AUTO_MDI_EN);
576b422d1b6SAlexandru Ardelean 	mdix_en = !!(reg & ADIN1300_MAN_MDIX_EN);
577b422d1b6SAlexandru Ardelean 
578b422d1b6SAlexandru Ardelean 	/* If MDI/MDIX is forced, just read it from the control reg */
579b422d1b6SAlexandru Ardelean 	if (!auto_en) {
580b422d1b6SAlexandru Ardelean 		if (mdix_en)
581b422d1b6SAlexandru Ardelean 			phydev->mdix = ETH_TP_MDI_X;
582b422d1b6SAlexandru Ardelean 		else
583b422d1b6SAlexandru Ardelean 			phydev->mdix = ETH_TP_MDI;
584b422d1b6SAlexandru Ardelean 		return 0;
585b422d1b6SAlexandru Ardelean 	}
586b422d1b6SAlexandru Ardelean 
587b422d1b6SAlexandru Ardelean 	/**
588b422d1b6SAlexandru Ardelean 	 * Otherwise, we need to deduce it from the PHY status2 reg.
589b422d1b6SAlexandru Ardelean 	 * When Auto-MDI is enabled, the ADIN1300_MAN_MDIX_EN bit implies
590b422d1b6SAlexandru Ardelean 	 * a preference for MDIX when it is set.
591b422d1b6SAlexandru Ardelean 	 */
592b422d1b6SAlexandru Ardelean 	reg = phy_read(phydev, ADIN1300_PHY_STATUS1);
593b422d1b6SAlexandru Ardelean 	if (reg < 0)
594b422d1b6SAlexandru Ardelean 		return reg;
595b422d1b6SAlexandru Ardelean 
596b422d1b6SAlexandru Ardelean 	swapped = !!(reg & ADIN1300_PAIR_01_SWAP);
597b422d1b6SAlexandru Ardelean 
598b422d1b6SAlexandru Ardelean 	if (mdix_en != swapped)
599b422d1b6SAlexandru Ardelean 		phydev->mdix = ETH_TP_MDI_X;
600b422d1b6SAlexandru Ardelean 	else
601b422d1b6SAlexandru Ardelean 		phydev->mdix = ETH_TP_MDI;
602b422d1b6SAlexandru Ardelean 
603b422d1b6SAlexandru Ardelean 	return 0;
604b422d1b6SAlexandru Ardelean }
605b422d1b6SAlexandru Ardelean 
606b422d1b6SAlexandru Ardelean static int adin_read_status(struct phy_device *phydev)
607b422d1b6SAlexandru Ardelean {
608b422d1b6SAlexandru Ardelean 	int ret;
609b422d1b6SAlexandru Ardelean 
610b422d1b6SAlexandru Ardelean 	ret = adin_mdix_update(phydev);
611b422d1b6SAlexandru Ardelean 	if (ret < 0)
612b422d1b6SAlexandru Ardelean 		return ret;
613b422d1b6SAlexandru Ardelean 
614b422d1b6SAlexandru Ardelean 	return genphy_read_status(phydev);
615b422d1b6SAlexandru Ardelean }
616b422d1b6SAlexandru Ardelean 
617fa5bd9c5SAlexandru Ardelean static int adin_soft_reset(struct phy_device *phydev)
618fa5bd9c5SAlexandru Ardelean {
619fa5bd9c5SAlexandru Ardelean 	int rc;
620fa5bd9c5SAlexandru Ardelean 
621fa5bd9c5SAlexandru Ardelean 	/* The reset bit is self-clearing, set it and wait */
622fa5bd9c5SAlexandru Ardelean 	rc = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1,
623fa5bd9c5SAlexandru Ardelean 			      ADIN1300_GE_SOFT_RESET_REG,
624fa5bd9c5SAlexandru Ardelean 			      ADIN1300_GE_SOFT_RESET);
625fa5bd9c5SAlexandru Ardelean 	if (rc < 0)
626fa5bd9c5SAlexandru Ardelean 		return rc;
627fa5bd9c5SAlexandru Ardelean 
628fa5bd9c5SAlexandru Ardelean 	msleep(10);
629fa5bd9c5SAlexandru Ardelean 
630fa5bd9c5SAlexandru Ardelean 	/* If we get a read error something may be wrong */
631fa5bd9c5SAlexandru Ardelean 	rc = phy_read_mmd(phydev, MDIO_MMD_VEND1,
632fa5bd9c5SAlexandru Ardelean 			  ADIN1300_GE_SOFT_RESET_REG);
633fa5bd9c5SAlexandru Ardelean 
634fa5bd9c5SAlexandru Ardelean 	return rc < 0 ? rc : 0;
635fa5bd9c5SAlexandru Ardelean }
636fa5bd9c5SAlexandru Ardelean 
6379fe0b8d6SAlexandru Ardelean static int adin_get_sset_count(struct phy_device *phydev)
6389fe0b8d6SAlexandru Ardelean {
6399fe0b8d6SAlexandru Ardelean 	return ARRAY_SIZE(adin_hw_stats);
6409fe0b8d6SAlexandru Ardelean }
6419fe0b8d6SAlexandru Ardelean 
6429fe0b8d6SAlexandru Ardelean static void adin_get_strings(struct phy_device *phydev, u8 *data)
6439fe0b8d6SAlexandru Ardelean {
6449fe0b8d6SAlexandru Ardelean 	int i;
6459fe0b8d6SAlexandru Ardelean 
6469fe0b8d6SAlexandru Ardelean 	for (i = 0; i < ARRAY_SIZE(adin_hw_stats); i++) {
6479fe0b8d6SAlexandru Ardelean 		strlcpy(&data[i * ETH_GSTRING_LEN],
6489fe0b8d6SAlexandru Ardelean 			adin_hw_stats[i].string, ETH_GSTRING_LEN);
6499fe0b8d6SAlexandru Ardelean 	}
6509fe0b8d6SAlexandru Ardelean }
6519fe0b8d6SAlexandru Ardelean 
6529fe0b8d6SAlexandru Ardelean static int adin_read_mmd_stat_regs(struct phy_device *phydev,
6539fe0b8d6SAlexandru Ardelean 				   struct adin_hw_stat *stat,
6549fe0b8d6SAlexandru Ardelean 				   u32 *val)
6559fe0b8d6SAlexandru Ardelean {
6569fe0b8d6SAlexandru Ardelean 	int ret;
6579fe0b8d6SAlexandru Ardelean 
6589fe0b8d6SAlexandru Ardelean 	ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, stat->reg1);
6599fe0b8d6SAlexandru Ardelean 	if (ret < 0)
6609fe0b8d6SAlexandru Ardelean 		return ret;
6619fe0b8d6SAlexandru Ardelean 
6629fe0b8d6SAlexandru Ardelean 	*val = (ret & 0xffff);
6639fe0b8d6SAlexandru Ardelean 
6649fe0b8d6SAlexandru Ardelean 	if (stat->reg2 == 0)
6659fe0b8d6SAlexandru Ardelean 		return 0;
6669fe0b8d6SAlexandru Ardelean 
6679fe0b8d6SAlexandru Ardelean 	ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, stat->reg2);
6689fe0b8d6SAlexandru Ardelean 	if (ret < 0)
6699fe0b8d6SAlexandru Ardelean 		return ret;
6709fe0b8d6SAlexandru Ardelean 
6719fe0b8d6SAlexandru Ardelean 	*val <<= 16;
6729fe0b8d6SAlexandru Ardelean 	*val |= (ret & 0xffff);
6739fe0b8d6SAlexandru Ardelean 
6749fe0b8d6SAlexandru Ardelean 	return 0;
6759fe0b8d6SAlexandru Ardelean }
6769fe0b8d6SAlexandru Ardelean 
6779fe0b8d6SAlexandru Ardelean static u64 adin_get_stat(struct phy_device *phydev, int i)
6789fe0b8d6SAlexandru Ardelean {
6799fe0b8d6SAlexandru Ardelean 	struct adin_hw_stat *stat = &adin_hw_stats[i];
6809fe0b8d6SAlexandru Ardelean 	struct adin_priv *priv = phydev->priv;
6819fe0b8d6SAlexandru Ardelean 	u32 val;
6829fe0b8d6SAlexandru Ardelean 	int ret;
6839fe0b8d6SAlexandru Ardelean 
6849fe0b8d6SAlexandru Ardelean 	if (stat->reg1 > 0x1f) {
6859fe0b8d6SAlexandru Ardelean 		ret = adin_read_mmd_stat_regs(phydev, stat, &val);
6869fe0b8d6SAlexandru Ardelean 		if (ret < 0)
6879fe0b8d6SAlexandru Ardelean 			return (u64)(~0);
6889fe0b8d6SAlexandru Ardelean 	} else {
6899fe0b8d6SAlexandru Ardelean 		ret = phy_read(phydev, stat->reg1);
6909fe0b8d6SAlexandru Ardelean 		if (ret < 0)
6919fe0b8d6SAlexandru Ardelean 			return (u64)(~0);
6929fe0b8d6SAlexandru Ardelean 		val = (ret & 0xffff);
6939fe0b8d6SAlexandru Ardelean 	}
6949fe0b8d6SAlexandru Ardelean 
6959fe0b8d6SAlexandru Ardelean 	priv->stats[i] += val;
6969fe0b8d6SAlexandru Ardelean 
6979fe0b8d6SAlexandru Ardelean 	return priv->stats[i];
6989fe0b8d6SAlexandru Ardelean }
6999fe0b8d6SAlexandru Ardelean 
7009fe0b8d6SAlexandru Ardelean static void adin_get_stats(struct phy_device *phydev,
7019fe0b8d6SAlexandru Ardelean 			   struct ethtool_stats *stats, u64 *data)
7029fe0b8d6SAlexandru Ardelean {
7039fe0b8d6SAlexandru Ardelean 	int i, rc;
7049fe0b8d6SAlexandru Ardelean 
7059fe0b8d6SAlexandru Ardelean 	/* latch copies of all the frame-checker counters */
7069fe0b8d6SAlexandru Ardelean 	rc = phy_read(phydev, ADIN1300_RX_ERR_CNT);
7079fe0b8d6SAlexandru Ardelean 	if (rc < 0)
7089fe0b8d6SAlexandru Ardelean 		return;
7099fe0b8d6SAlexandru Ardelean 
7109fe0b8d6SAlexandru Ardelean 	for (i = 0; i < ARRAY_SIZE(adin_hw_stats); i++)
7119fe0b8d6SAlexandru Ardelean 		data[i] = adin_get_stat(phydev, i);
7129fe0b8d6SAlexandru Ardelean }
7139fe0b8d6SAlexandru Ardelean 
7149fe0b8d6SAlexandru Ardelean static int adin_probe(struct phy_device *phydev)
7159fe0b8d6SAlexandru Ardelean {
7169fe0b8d6SAlexandru Ardelean 	struct device *dev = &phydev->mdio.dev;
7179fe0b8d6SAlexandru Ardelean 	struct adin_priv *priv;
7189fe0b8d6SAlexandru Ardelean 
7199fe0b8d6SAlexandru Ardelean 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
7209fe0b8d6SAlexandru Ardelean 	if (!priv)
7219fe0b8d6SAlexandru Ardelean 		return -ENOMEM;
7229fe0b8d6SAlexandru Ardelean 
7239fe0b8d6SAlexandru Ardelean 	phydev->priv = priv;
7249fe0b8d6SAlexandru Ardelean 
7259fe0b8d6SAlexandru Ardelean 	return 0;
7269fe0b8d6SAlexandru Ardelean }
7279fe0b8d6SAlexandru Ardelean 
7289c102981SAlexandru Ardelean static struct phy_driver adin_driver[] = {
7299c102981SAlexandru Ardelean 	{
7309c102981SAlexandru Ardelean 		PHY_ID_MATCH_MODEL(PHY_ID_ADIN1200),
7319c102981SAlexandru Ardelean 		.name		= "ADIN1200",
7329fe0b8d6SAlexandru Ardelean 		.probe		= adin_probe,
7339c102981SAlexandru Ardelean 		.config_init	= adin_config_init,
734fa5bd9c5SAlexandru Ardelean 		.soft_reset	= adin_soft_reset,
735b422d1b6SAlexandru Ardelean 		.config_aneg	= adin_config_aneg,
736b422d1b6SAlexandru Ardelean 		.read_status	= adin_read_status,
7372d99b584SAlexandru Ardelean 		.get_tunable	= adin_get_tunable,
7382d99b584SAlexandru Ardelean 		.set_tunable	= adin_set_tunable,
739fb44b8d6SAlexandru Ardelean 		.ack_interrupt	= adin_phy_ack_intr,
740fb44b8d6SAlexandru Ardelean 		.config_intr	= adin_phy_config_intr,
7419fe0b8d6SAlexandru Ardelean 		.get_sset_count	= adin_get_sset_count,
7429fe0b8d6SAlexandru Ardelean 		.get_strings	= adin_get_strings,
7439fe0b8d6SAlexandru Ardelean 		.get_stats	= adin_get_stats,
74449cc4c7dSAlexandru Ardelean 		.resume		= genphy_resume,
74549cc4c7dSAlexandru Ardelean 		.suspend	= genphy_suspend,
7463e32d020SAlexandru Ardelean 		.read_mmd	= adin_read_mmd,
7473e32d020SAlexandru Ardelean 		.write_mmd	= adin_write_mmd,
7489c102981SAlexandru Ardelean 	},
7499c102981SAlexandru Ardelean 	{
7509c102981SAlexandru Ardelean 		PHY_ID_MATCH_MODEL(PHY_ID_ADIN1300),
7519c102981SAlexandru Ardelean 		.name		= "ADIN1300",
7529fe0b8d6SAlexandru Ardelean 		.probe		= adin_probe,
7539c102981SAlexandru Ardelean 		.config_init	= adin_config_init,
754fa5bd9c5SAlexandru Ardelean 		.soft_reset	= adin_soft_reset,
755b422d1b6SAlexandru Ardelean 		.config_aneg	= adin_config_aneg,
756b422d1b6SAlexandru Ardelean 		.read_status	= adin_read_status,
7572d99b584SAlexandru Ardelean 		.get_tunable	= adin_get_tunable,
7582d99b584SAlexandru Ardelean 		.set_tunable	= adin_set_tunable,
759fb44b8d6SAlexandru Ardelean 		.ack_interrupt	= adin_phy_ack_intr,
760fb44b8d6SAlexandru Ardelean 		.config_intr	= adin_phy_config_intr,
7619fe0b8d6SAlexandru Ardelean 		.get_sset_count	= adin_get_sset_count,
7629fe0b8d6SAlexandru Ardelean 		.get_strings	= adin_get_strings,
7639fe0b8d6SAlexandru Ardelean 		.get_stats	= adin_get_stats,
76449cc4c7dSAlexandru Ardelean 		.resume		= genphy_resume,
76549cc4c7dSAlexandru Ardelean 		.suspend	= genphy_suspend,
7663e32d020SAlexandru Ardelean 		.read_mmd	= adin_read_mmd,
7673e32d020SAlexandru Ardelean 		.write_mmd	= adin_write_mmd,
7689c102981SAlexandru Ardelean 	},
7699c102981SAlexandru Ardelean };
7709c102981SAlexandru Ardelean 
7719c102981SAlexandru Ardelean module_phy_driver(adin_driver);
7729c102981SAlexandru Ardelean 
7739c102981SAlexandru Ardelean static struct mdio_device_id __maybe_unused adin_tbl[] = {
7749c102981SAlexandru Ardelean 	{ PHY_ID_MATCH_MODEL(PHY_ID_ADIN1200) },
7759c102981SAlexandru Ardelean 	{ PHY_ID_MATCH_MODEL(PHY_ID_ADIN1300) },
7769c102981SAlexandru Ardelean 	{ }
7779c102981SAlexandru Ardelean };
7789c102981SAlexandru Ardelean 
7799c102981SAlexandru Ardelean MODULE_DEVICE_TABLE(mdio, adin_tbl);
7809c102981SAlexandru Ardelean MODULE_DESCRIPTION("Analog Devices Industrial Ethernet PHY driver");
7819c102981SAlexandru Ardelean MODULE_LICENSE("GPL");
782