xref: /openbmc/linux/drivers/net/phy/adin.c (revision fb3ceec1)
19c102981SAlexandru Ardelean // SPDX-License-Identifier: GPL-2.0+
21f2d109eSYang Shen /*
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>
11f2531d45SAlexandru Ardelean #include <linux/ethtool_netlink.h>
129c102981SAlexandru Ardelean #include <linux/init.h>
139c102981SAlexandru Ardelean #include <linux/module.h>
149c102981SAlexandru Ardelean #include <linux/mii.h>
159c102981SAlexandru Ardelean #include <linux/phy.h>
16c83e6163SAlexandru Ardelean #include <linux/property.h>
179c102981SAlexandru Ardelean 
189c102981SAlexandru Ardelean #define PHY_ID_ADIN1200				0x0283bc20
199c102981SAlexandru Ardelean #define PHY_ID_ADIN1300				0x0283bc30
209c102981SAlexandru Ardelean 
213e32d020SAlexandru Ardelean #define ADIN1300_MII_EXT_REG_PTR		0x0010
223e32d020SAlexandru Ardelean #define ADIN1300_MII_EXT_REG_DATA		0x0011
233e32d020SAlexandru Ardelean 
24b422d1b6SAlexandru Ardelean #define ADIN1300_PHY_CTRL1			0x0012
25b422d1b6SAlexandru Ardelean #define   ADIN1300_AUTO_MDI_EN			BIT(10)
26b422d1b6SAlexandru Ardelean #define   ADIN1300_MAN_MDIX_EN			BIT(9)
2785ba75bbSAlexandru Ardelean #define   ADIN1300_DIAG_CLK_EN			BIT(2)
28b422d1b6SAlexandru Ardelean 
299fe0b8d6SAlexandru Ardelean #define ADIN1300_RX_ERR_CNT			0x0014
309fe0b8d6SAlexandru Ardelean 
3165d7be09SAlexandru Ardelean #define ADIN1300_PHY_CTRL_STATUS2		0x0015
3265d7be09SAlexandru Ardelean #define   ADIN1300_NRG_PD_EN			BIT(3)
3365d7be09SAlexandru Ardelean #define   ADIN1300_NRG_PD_TX_EN			BIT(2)
3465d7be09SAlexandru Ardelean #define   ADIN1300_NRG_PD_STATUS		BIT(1)
3565d7be09SAlexandru Ardelean 
362d99b584SAlexandru Ardelean #define ADIN1300_PHY_CTRL2			0x0016
372d99b584SAlexandru Ardelean #define   ADIN1300_DOWNSPEED_AN_100_EN		BIT(11)
382d99b584SAlexandru Ardelean #define   ADIN1300_DOWNSPEED_AN_10_EN		BIT(10)
392d99b584SAlexandru Ardelean #define   ADIN1300_GROUP_MDIO_EN		BIT(6)
402d99b584SAlexandru Ardelean #define   ADIN1300_DOWNSPEEDS_EN	\
412d99b584SAlexandru Ardelean 	(ADIN1300_DOWNSPEED_AN_100_EN | ADIN1300_DOWNSPEED_AN_10_EN)
422d99b584SAlexandru Ardelean 
432d99b584SAlexandru Ardelean #define ADIN1300_PHY_CTRL3			0x0017
442d99b584SAlexandru Ardelean #define   ADIN1300_LINKING_EN			BIT(13)
452d99b584SAlexandru Ardelean #define   ADIN1300_DOWNSPEED_RETRIES_MSK	GENMASK(12, 10)
462d99b584SAlexandru Ardelean 
47fb44b8d6SAlexandru Ardelean #define ADIN1300_INT_MASK_REG			0x0018
48fb44b8d6SAlexandru Ardelean #define   ADIN1300_INT_MDIO_SYNC_EN		BIT(9)
49fb44b8d6SAlexandru Ardelean #define   ADIN1300_INT_ANEG_STAT_CHNG_EN	BIT(8)
50fb44b8d6SAlexandru Ardelean #define   ADIN1300_INT_ANEG_PAGE_RX_EN		BIT(6)
51fb44b8d6SAlexandru Ardelean #define   ADIN1300_INT_IDLE_ERR_CNT_EN		BIT(5)
52fb44b8d6SAlexandru Ardelean #define   ADIN1300_INT_MAC_FIFO_OU_EN		BIT(4)
53fb44b8d6SAlexandru Ardelean #define   ADIN1300_INT_RX_STAT_CHNG_EN		BIT(3)
54fb44b8d6SAlexandru Ardelean #define   ADIN1300_INT_LINK_STAT_CHNG_EN	BIT(2)
55fb44b8d6SAlexandru Ardelean #define   ADIN1300_INT_SPEED_CHNG_EN		BIT(1)
56fb44b8d6SAlexandru Ardelean #define   ADIN1300_INT_HW_IRQ_EN		BIT(0)
57fb44b8d6SAlexandru Ardelean #define ADIN1300_INT_MASK_EN	\
58fb44b8d6SAlexandru Ardelean 	(ADIN1300_INT_LINK_STAT_CHNG_EN | ADIN1300_INT_HW_IRQ_EN)
59fb44b8d6SAlexandru Ardelean #define ADIN1300_INT_STATUS_REG			0x0019
60fb44b8d6SAlexandru Ardelean 
61b422d1b6SAlexandru Ardelean #define ADIN1300_PHY_STATUS1			0x001a
62b422d1b6SAlexandru Ardelean #define   ADIN1300_PAIR_01_SWAP			BIT(11)
63b422d1b6SAlexandru Ardelean 
64c6aa697cSAlexandru Ardelean /* EEE register addresses, accessible via Clause 22 access using
65c6aa697cSAlexandru Ardelean  * ADIN1300_MII_EXT_REG_PTR & ADIN1300_MII_EXT_REG_DATA.
66c6aa697cSAlexandru Ardelean  * The bit-fields are the same as specified by IEEE for EEE.
67c6aa697cSAlexandru Ardelean  */
68c6aa697cSAlexandru Ardelean #define ADIN1300_EEE_CAP_REG			0x8000
69c6aa697cSAlexandru Ardelean #define ADIN1300_EEE_ADV_REG			0x8001
70c6aa697cSAlexandru Ardelean #define ADIN1300_EEE_LPABLE_REG			0x8002
71c6aa697cSAlexandru Ardelean #define ADIN1300_CLOCK_STOP_REG			0x9400
72c6aa697cSAlexandru Ardelean #define ADIN1300_LPI_WAKE_ERR_CNT_REG		0xa000
73c6aa697cSAlexandru Ardelean 
74f2531d45SAlexandru Ardelean #define ADIN1300_CDIAG_RUN			0xba1b
75f2531d45SAlexandru Ardelean #define   ADIN1300_CDIAG_RUN_EN			BIT(0)
76f2531d45SAlexandru Ardelean 
77f2531d45SAlexandru Ardelean /*
78f2531d45SAlexandru Ardelean  * The XSIM3/2/1 and XSHRT3/2/1 are actually relative.
79f2531d45SAlexandru Ardelean  * For CDIAG_DTLD_RSLTS(0) it's ADIN1300_CDIAG_RSLT_XSIM3/2/1
80f2531d45SAlexandru Ardelean  * For CDIAG_DTLD_RSLTS(1) it's ADIN1300_CDIAG_RSLT_XSIM3/2/0
81f2531d45SAlexandru Ardelean  * For CDIAG_DTLD_RSLTS(2) it's ADIN1300_CDIAG_RSLT_XSIM3/1/0
82f2531d45SAlexandru Ardelean  * For CDIAG_DTLD_RSLTS(3) it's ADIN1300_CDIAG_RSLT_XSIM2/1/0
83f2531d45SAlexandru Ardelean  */
84f2531d45SAlexandru Ardelean #define ADIN1300_CDIAG_DTLD_RSLTS(x)		(0xba1d + (x))
85f2531d45SAlexandru Ardelean #define   ADIN1300_CDIAG_RSLT_BUSY		BIT(10)
86f2531d45SAlexandru Ardelean #define   ADIN1300_CDIAG_RSLT_XSIM3		BIT(9)
87f2531d45SAlexandru Ardelean #define   ADIN1300_CDIAG_RSLT_XSIM2		BIT(8)
88f2531d45SAlexandru Ardelean #define   ADIN1300_CDIAG_RSLT_XSIM1		BIT(7)
89f2531d45SAlexandru Ardelean #define   ADIN1300_CDIAG_RSLT_SIM		BIT(6)
90f2531d45SAlexandru Ardelean #define   ADIN1300_CDIAG_RSLT_XSHRT3		BIT(5)
91f2531d45SAlexandru Ardelean #define   ADIN1300_CDIAG_RSLT_XSHRT2		BIT(4)
92f2531d45SAlexandru Ardelean #define   ADIN1300_CDIAG_RSLT_XSHRT1		BIT(3)
93f2531d45SAlexandru Ardelean #define   ADIN1300_CDIAG_RSLT_SHRT		BIT(2)
94f2531d45SAlexandru Ardelean #define   ADIN1300_CDIAG_RSLT_OPEN		BIT(1)
95f2531d45SAlexandru Ardelean #define   ADIN1300_CDIAG_RSLT_GOOD		BIT(0)
96f2531d45SAlexandru Ardelean 
97f2531d45SAlexandru Ardelean #define ADIN1300_CDIAG_FLT_DIST(x)		(0xba21 + (x))
98f2531d45SAlexandru Ardelean 
99fa5bd9c5SAlexandru Ardelean #define ADIN1300_GE_SOFT_RESET_REG		0xff0c
100fa5bd9c5SAlexandru Ardelean #define   ADIN1300_GE_SOFT_RESET		BIT(0)
101fa5bd9c5SAlexandru Ardelean 
102ce334216SJosua Mayer #define ADIN1300_GE_CLK_CFG_REG			0xff1f
103ce334216SJosua Mayer #define   ADIN1300_GE_CLK_CFG_MASK		GENMASK(5, 0)
104ce334216SJosua Mayer #define   ADIN1300_GE_CLK_CFG_RCVR_125		BIT(5)
105ce334216SJosua Mayer #define   ADIN1300_GE_CLK_CFG_FREE_125		BIT(4)
106ce334216SJosua Mayer #define   ADIN1300_GE_CLK_CFG_REF_EN		BIT(3)
107ce334216SJosua Mayer #define   ADIN1300_GE_CLK_CFG_HRT_RCVR		BIT(2)
108ce334216SJosua Mayer #define   ADIN1300_GE_CLK_CFG_HRT_FREE		BIT(1)
109ce334216SJosua Mayer #define   ADIN1300_GE_CLK_CFG_25		BIT(0)
110ce334216SJosua Mayer 
111d6200c8fSAlexandru Ardelean #define ADIN1300_GE_RGMII_CFG_REG		0xff23
112c83e6163SAlexandru Ardelean #define   ADIN1300_GE_RGMII_RX_MSK		GENMASK(8, 6)
113c83e6163SAlexandru Ardelean #define   ADIN1300_GE_RGMII_RX_SEL(x)		\
114c83e6163SAlexandru Ardelean 		FIELD_PREP(ADIN1300_GE_RGMII_RX_MSK, x)
115c83e6163SAlexandru Ardelean #define   ADIN1300_GE_RGMII_GTX_MSK		GENMASK(5, 3)
116c83e6163SAlexandru Ardelean #define   ADIN1300_GE_RGMII_GTX_SEL(x)		\
117c83e6163SAlexandru Ardelean 		FIELD_PREP(ADIN1300_GE_RGMII_GTX_MSK, x)
118d6200c8fSAlexandru Ardelean #define   ADIN1300_GE_RGMII_RXID_EN		BIT(2)
119d6200c8fSAlexandru Ardelean #define   ADIN1300_GE_RGMII_TXID_EN		BIT(1)
120d6200c8fSAlexandru Ardelean #define   ADIN1300_GE_RGMII_EN			BIT(0)
121d6200c8fSAlexandru Ardelean 
122c83e6163SAlexandru Ardelean /* RGMII internal delay settings for rx and tx for ADIN1300 */
123c83e6163SAlexandru Ardelean #define ADIN1300_RGMII_1_60_NS			0x0001
124c83e6163SAlexandru Ardelean #define ADIN1300_RGMII_1_80_NS			0x0002
125c83e6163SAlexandru Ardelean #define	ADIN1300_RGMII_2_00_NS			0x0000
126c83e6163SAlexandru Ardelean #define	ADIN1300_RGMII_2_20_NS			0x0006
127c83e6163SAlexandru Ardelean #define	ADIN1300_RGMII_2_40_NS			0x0007
128c83e6163SAlexandru Ardelean 
129d6200c8fSAlexandru Ardelean #define ADIN1300_GE_RMII_CFG_REG		0xff24
130f1012fb4SAlexandru Ardelean #define   ADIN1300_GE_RMII_FIFO_DEPTH_MSK	GENMASK(6, 4)
131f1012fb4SAlexandru Ardelean #define   ADIN1300_GE_RMII_FIFO_DEPTH_SEL(x)	\
132f1012fb4SAlexandru Ardelean 		FIELD_PREP(ADIN1300_GE_RMII_FIFO_DEPTH_MSK, x)
133d6200c8fSAlexandru Ardelean #define   ADIN1300_GE_RMII_EN			BIT(0)
134d6200c8fSAlexandru Ardelean 
135f1012fb4SAlexandru Ardelean /* RMII fifo depth values */
136f1012fb4SAlexandru Ardelean #define ADIN1300_RMII_4_BITS			0x0000
137f1012fb4SAlexandru Ardelean #define ADIN1300_RMII_8_BITS			0x0001
138f1012fb4SAlexandru Ardelean #define ADIN1300_RMII_12_BITS			0x0002
139f1012fb4SAlexandru Ardelean #define ADIN1300_RMII_16_BITS			0x0003
140f1012fb4SAlexandru Ardelean #define ADIN1300_RMII_20_BITS			0x0004
141f1012fb4SAlexandru Ardelean #define ADIN1300_RMII_24_BITS			0x0005
142f1012fb4SAlexandru Ardelean 
143c83e6163SAlexandru Ardelean /**
144c83e6163SAlexandru Ardelean  * struct adin_cfg_reg_map - map a config value to aregister value
14519c5a5feSAndrew Lunn  * @cfg:	value in device configuration
14619c5a5feSAndrew Lunn  * @reg:	value in the register
147c83e6163SAlexandru Ardelean  */
148c83e6163SAlexandru Ardelean struct adin_cfg_reg_map {
149c83e6163SAlexandru Ardelean 	int cfg;
150c83e6163SAlexandru Ardelean 	int reg;
151c83e6163SAlexandru Ardelean };
152c83e6163SAlexandru Ardelean 
153c83e6163SAlexandru Ardelean static const struct adin_cfg_reg_map adin_rgmii_delays[] = {
154c83e6163SAlexandru Ardelean 	{ 1600, ADIN1300_RGMII_1_60_NS },
155c83e6163SAlexandru Ardelean 	{ 1800, ADIN1300_RGMII_1_80_NS },
156c83e6163SAlexandru Ardelean 	{ 2000, ADIN1300_RGMII_2_00_NS },
157c83e6163SAlexandru Ardelean 	{ 2200, ADIN1300_RGMII_2_20_NS },
158c83e6163SAlexandru Ardelean 	{ 2400, ADIN1300_RGMII_2_40_NS },
159c83e6163SAlexandru Ardelean 	{ },
160c83e6163SAlexandru Ardelean };
161c83e6163SAlexandru Ardelean 
162f1012fb4SAlexandru Ardelean static const struct adin_cfg_reg_map adin_rmii_fifo_depths[] = {
163f1012fb4SAlexandru Ardelean 	{ 4,  ADIN1300_RMII_4_BITS },
164f1012fb4SAlexandru Ardelean 	{ 8,  ADIN1300_RMII_8_BITS },
165f1012fb4SAlexandru Ardelean 	{ 12, ADIN1300_RMII_12_BITS },
166f1012fb4SAlexandru Ardelean 	{ 16, ADIN1300_RMII_16_BITS },
167f1012fb4SAlexandru Ardelean 	{ 20, ADIN1300_RMII_20_BITS },
168f1012fb4SAlexandru Ardelean 	{ 24, ADIN1300_RMII_24_BITS },
169f1012fb4SAlexandru Ardelean 	{ },
170f1012fb4SAlexandru Ardelean };
171f1012fb4SAlexandru Ardelean 
172c6aa697cSAlexandru Ardelean /**
173c6aa697cSAlexandru Ardelean  * struct adin_clause45_mmd_map - map to convert Clause 45 regs to Clause 22
17419c5a5feSAndrew Lunn  * @devad:		device address used in Clause 45 access
17519c5a5feSAndrew Lunn  * @cl45_regnum:	register address defined by Clause 45
17619c5a5feSAndrew Lunn  * @adin_regnum:	equivalent register address accessible via Clause 22
177c6aa697cSAlexandru Ardelean  */
178c6aa697cSAlexandru Ardelean struct adin_clause45_mmd_map {
179c6aa697cSAlexandru Ardelean 	int devad;
180c6aa697cSAlexandru Ardelean 	u16 cl45_regnum;
181c6aa697cSAlexandru Ardelean 	u16 adin_regnum;
182c6aa697cSAlexandru Ardelean };
183c6aa697cSAlexandru Ardelean 
184aa63b947SAlexandru Ardelean static const struct adin_clause45_mmd_map adin_clause45_mmd_map[] = {
185c6aa697cSAlexandru Ardelean 	{ MDIO_MMD_PCS,	MDIO_PCS_EEE_ABLE,	ADIN1300_EEE_CAP_REG },
186c6aa697cSAlexandru Ardelean 	{ MDIO_MMD_AN,	MDIO_AN_EEE_LPABLE,	ADIN1300_EEE_LPABLE_REG },
187c6aa697cSAlexandru Ardelean 	{ MDIO_MMD_AN,	MDIO_AN_EEE_ADV,	ADIN1300_EEE_ADV_REG },
188c6aa697cSAlexandru Ardelean 	{ MDIO_MMD_PCS,	MDIO_CTRL1,		ADIN1300_CLOCK_STOP_REG },
189c6aa697cSAlexandru Ardelean 	{ MDIO_MMD_PCS, MDIO_PCS_EEE_WK_ERR,	ADIN1300_LPI_WAKE_ERR_CNT_REG },
190c6aa697cSAlexandru Ardelean };
191c6aa697cSAlexandru Ardelean 
1929fe0b8d6SAlexandru Ardelean struct adin_hw_stat {
1939fe0b8d6SAlexandru Ardelean 	const char *string;
1949fe0b8d6SAlexandru Ardelean 	u16 reg1;
1959fe0b8d6SAlexandru Ardelean 	u16 reg2;
1969fe0b8d6SAlexandru Ardelean };
1979fe0b8d6SAlexandru Ardelean 
198aa63b947SAlexandru Ardelean static const struct adin_hw_stat adin_hw_stats[] = {
1999fe0b8d6SAlexandru Ardelean 	{ "total_frames_checked_count",		0x940A, 0x940B }, /* hi + lo */
2009fe0b8d6SAlexandru Ardelean 	{ "length_error_frames_count",		0x940C },
2019fe0b8d6SAlexandru Ardelean 	{ "alignment_error_frames_count",	0x940D },
2029fe0b8d6SAlexandru Ardelean 	{ "symbol_error_count",			0x940E },
2039fe0b8d6SAlexandru Ardelean 	{ "oversized_frames_count",		0x940F },
2049fe0b8d6SAlexandru Ardelean 	{ "undersized_frames_count",		0x9410 },
2059fe0b8d6SAlexandru Ardelean 	{ "odd_nibble_frames_count",		0x9411 },
2069fe0b8d6SAlexandru Ardelean 	{ "odd_preamble_packet_count",		0x9412 },
2079fe0b8d6SAlexandru Ardelean 	{ "dribble_bits_frames_count",		0x9413 },
2089fe0b8d6SAlexandru Ardelean 	{ "false_carrier_events_count",		0x9414 },
2099fe0b8d6SAlexandru Ardelean };
2109fe0b8d6SAlexandru Ardelean 
2119fe0b8d6SAlexandru Ardelean /**
2129fe0b8d6SAlexandru Ardelean  * struct adin_priv - ADIN PHY driver private data
21319c5a5feSAndrew Lunn  * @stats:		statistic counters for the PHY
2149fe0b8d6SAlexandru Ardelean  */
2159fe0b8d6SAlexandru Ardelean struct adin_priv {
2169fe0b8d6SAlexandru Ardelean 	u64			stats[ARRAY_SIZE(adin_hw_stats)];
2179fe0b8d6SAlexandru Ardelean };
2189fe0b8d6SAlexandru Ardelean 
adin_lookup_reg_value(const struct adin_cfg_reg_map * tbl,int cfg)219c83e6163SAlexandru Ardelean static int adin_lookup_reg_value(const struct adin_cfg_reg_map *tbl, int cfg)
220c83e6163SAlexandru Ardelean {
221c83e6163SAlexandru Ardelean 	size_t i;
222c83e6163SAlexandru Ardelean 
223c83e6163SAlexandru Ardelean 	for (i = 0; tbl[i].cfg; i++) {
224c83e6163SAlexandru Ardelean 		if (tbl[i].cfg == cfg)
225c83e6163SAlexandru Ardelean 			return tbl[i].reg;
226c83e6163SAlexandru Ardelean 	}
227c83e6163SAlexandru Ardelean 
228c83e6163SAlexandru Ardelean 	return -EINVAL;
229c83e6163SAlexandru Ardelean }
230c83e6163SAlexandru Ardelean 
adin_get_reg_value(struct phy_device * phydev,const char * prop_name,const struct adin_cfg_reg_map * tbl,u32 dflt)231c83e6163SAlexandru Ardelean static u32 adin_get_reg_value(struct phy_device *phydev,
232c83e6163SAlexandru Ardelean 			      const char *prop_name,
233c83e6163SAlexandru Ardelean 			      const struct adin_cfg_reg_map *tbl,
234c83e6163SAlexandru Ardelean 			      u32 dflt)
235c83e6163SAlexandru Ardelean {
236c83e6163SAlexandru Ardelean 	struct device *dev = &phydev->mdio.dev;
237c83e6163SAlexandru Ardelean 	u32 val;
238c83e6163SAlexandru Ardelean 	int rc;
239c83e6163SAlexandru Ardelean 
240c83e6163SAlexandru Ardelean 	if (device_property_read_u32(dev, prop_name, &val))
241c83e6163SAlexandru Ardelean 		return dflt;
242c83e6163SAlexandru Ardelean 
243c83e6163SAlexandru Ardelean 	rc = adin_lookup_reg_value(tbl, val);
244c83e6163SAlexandru Ardelean 	if (rc < 0) {
245c83e6163SAlexandru Ardelean 		phydev_warn(phydev,
246c83e6163SAlexandru Ardelean 			    "Unsupported value %u for %s using default (%u)\n",
247c83e6163SAlexandru Ardelean 			    val, prop_name, dflt);
248c83e6163SAlexandru Ardelean 		return dflt;
249c83e6163SAlexandru Ardelean 	}
250c83e6163SAlexandru Ardelean 
251c83e6163SAlexandru Ardelean 	return rc;
252c83e6163SAlexandru Ardelean }
253c83e6163SAlexandru Ardelean 
adin_config_rgmii_mode(struct phy_device * phydev)254d6200c8fSAlexandru Ardelean static int adin_config_rgmii_mode(struct phy_device *phydev)
255d6200c8fSAlexandru Ardelean {
256c83e6163SAlexandru Ardelean 	u32 val;
257d6200c8fSAlexandru Ardelean 	int reg;
258d6200c8fSAlexandru Ardelean 
259d6200c8fSAlexandru Ardelean 	if (!phy_interface_is_rgmii(phydev))
260d6200c8fSAlexandru Ardelean 		return phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1,
261d6200c8fSAlexandru Ardelean 					  ADIN1300_GE_RGMII_CFG_REG,
262d6200c8fSAlexandru Ardelean 					  ADIN1300_GE_RGMII_EN);
263d6200c8fSAlexandru Ardelean 
264d6200c8fSAlexandru Ardelean 	reg = phy_read_mmd(phydev, MDIO_MMD_VEND1, ADIN1300_GE_RGMII_CFG_REG);
265d6200c8fSAlexandru Ardelean 	if (reg < 0)
266d6200c8fSAlexandru Ardelean 		return reg;
267d6200c8fSAlexandru Ardelean 
268d6200c8fSAlexandru Ardelean 	reg |= ADIN1300_GE_RGMII_EN;
269d6200c8fSAlexandru Ardelean 
270d6200c8fSAlexandru Ardelean 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
271d6200c8fSAlexandru Ardelean 	    phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
272d6200c8fSAlexandru Ardelean 		reg |= ADIN1300_GE_RGMII_RXID_EN;
273c83e6163SAlexandru Ardelean 
274c83e6163SAlexandru Ardelean 		val = adin_get_reg_value(phydev, "adi,rx-internal-delay-ps",
275c83e6163SAlexandru Ardelean 					 adin_rgmii_delays,
276c83e6163SAlexandru Ardelean 					 ADIN1300_RGMII_2_00_NS);
277c83e6163SAlexandru Ardelean 		reg &= ~ADIN1300_GE_RGMII_RX_MSK;
278c83e6163SAlexandru Ardelean 		reg |= ADIN1300_GE_RGMII_RX_SEL(val);
279d6200c8fSAlexandru Ardelean 	} else {
280d6200c8fSAlexandru Ardelean 		reg &= ~ADIN1300_GE_RGMII_RXID_EN;
281d6200c8fSAlexandru Ardelean 	}
282d6200c8fSAlexandru Ardelean 
283d6200c8fSAlexandru Ardelean 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
284d6200c8fSAlexandru Ardelean 	    phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
285d6200c8fSAlexandru Ardelean 		reg |= ADIN1300_GE_RGMII_TXID_EN;
286c83e6163SAlexandru Ardelean 
287c83e6163SAlexandru Ardelean 		val = adin_get_reg_value(phydev, "adi,tx-internal-delay-ps",
288c83e6163SAlexandru Ardelean 					 adin_rgmii_delays,
289c83e6163SAlexandru Ardelean 					 ADIN1300_RGMII_2_00_NS);
290c83e6163SAlexandru Ardelean 		reg &= ~ADIN1300_GE_RGMII_GTX_MSK;
291c83e6163SAlexandru Ardelean 		reg |= ADIN1300_GE_RGMII_GTX_SEL(val);
292d6200c8fSAlexandru Ardelean 	} else {
293d6200c8fSAlexandru Ardelean 		reg &= ~ADIN1300_GE_RGMII_TXID_EN;
294d6200c8fSAlexandru Ardelean 	}
295d6200c8fSAlexandru Ardelean 
296d6200c8fSAlexandru Ardelean 	return phy_write_mmd(phydev, MDIO_MMD_VEND1,
297d6200c8fSAlexandru Ardelean 			     ADIN1300_GE_RGMII_CFG_REG, reg);
298d6200c8fSAlexandru Ardelean }
299d6200c8fSAlexandru Ardelean 
adin_config_rmii_mode(struct phy_device * phydev)300d6200c8fSAlexandru Ardelean static int adin_config_rmii_mode(struct phy_device *phydev)
301d6200c8fSAlexandru Ardelean {
302f1012fb4SAlexandru Ardelean 	u32 val;
303d6200c8fSAlexandru Ardelean 	int reg;
304d6200c8fSAlexandru Ardelean 
305d6200c8fSAlexandru Ardelean 	if (phydev->interface != PHY_INTERFACE_MODE_RMII)
306d6200c8fSAlexandru Ardelean 		return phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1,
307d6200c8fSAlexandru Ardelean 					  ADIN1300_GE_RMII_CFG_REG,
308d6200c8fSAlexandru Ardelean 					  ADIN1300_GE_RMII_EN);
309d6200c8fSAlexandru Ardelean 
310d6200c8fSAlexandru Ardelean 	reg = phy_read_mmd(phydev, MDIO_MMD_VEND1, ADIN1300_GE_RMII_CFG_REG);
311d6200c8fSAlexandru Ardelean 	if (reg < 0)
312d6200c8fSAlexandru Ardelean 		return reg;
313d6200c8fSAlexandru Ardelean 
314d6200c8fSAlexandru Ardelean 	reg |= ADIN1300_GE_RMII_EN;
315d6200c8fSAlexandru Ardelean 
316f1012fb4SAlexandru Ardelean 	val = adin_get_reg_value(phydev, "adi,fifo-depth-bits",
317f1012fb4SAlexandru Ardelean 				 adin_rmii_fifo_depths,
318f1012fb4SAlexandru Ardelean 				 ADIN1300_RMII_8_BITS);
319f1012fb4SAlexandru Ardelean 
320f1012fb4SAlexandru Ardelean 	reg &= ~ADIN1300_GE_RMII_FIFO_DEPTH_MSK;
321f1012fb4SAlexandru Ardelean 	reg |= ADIN1300_GE_RMII_FIFO_DEPTH_SEL(val);
322f1012fb4SAlexandru Ardelean 
323d6200c8fSAlexandru Ardelean 	return phy_write_mmd(phydev, MDIO_MMD_VEND1,
324d6200c8fSAlexandru Ardelean 			     ADIN1300_GE_RMII_CFG_REG, reg);
325d6200c8fSAlexandru Ardelean }
326d6200c8fSAlexandru Ardelean 
adin_get_downshift(struct phy_device * phydev,u8 * data)3272d99b584SAlexandru Ardelean static int adin_get_downshift(struct phy_device *phydev, u8 *data)
3282d99b584SAlexandru Ardelean {
3292d99b584SAlexandru Ardelean 	int val, cnt, enable;
3302d99b584SAlexandru Ardelean 
3312d99b584SAlexandru Ardelean 	val = phy_read(phydev, ADIN1300_PHY_CTRL2);
3322d99b584SAlexandru Ardelean 	if (val < 0)
3332d99b584SAlexandru Ardelean 		return val;
3342d99b584SAlexandru Ardelean 
3352d99b584SAlexandru Ardelean 	cnt = phy_read(phydev, ADIN1300_PHY_CTRL3);
3362d99b584SAlexandru Ardelean 	if (cnt < 0)
3372d99b584SAlexandru Ardelean 		return cnt;
3382d99b584SAlexandru Ardelean 
3392d99b584SAlexandru Ardelean 	enable = FIELD_GET(ADIN1300_DOWNSPEEDS_EN, val);
3402d99b584SAlexandru Ardelean 	cnt = FIELD_GET(ADIN1300_DOWNSPEED_RETRIES_MSK, cnt);
3412d99b584SAlexandru Ardelean 
3422d99b584SAlexandru Ardelean 	*data = (enable && cnt) ? cnt : DOWNSHIFT_DEV_DISABLE;
3432d99b584SAlexandru Ardelean 
3442d99b584SAlexandru Ardelean 	return 0;
3452d99b584SAlexandru Ardelean }
3462d99b584SAlexandru Ardelean 
adin_set_downshift(struct phy_device * phydev,u8 cnt)3472d99b584SAlexandru Ardelean static int adin_set_downshift(struct phy_device *phydev, u8 cnt)
3482d99b584SAlexandru Ardelean {
3492d99b584SAlexandru Ardelean 	u16 val;
3502d99b584SAlexandru Ardelean 	int rc;
3512d99b584SAlexandru Ardelean 
3522d99b584SAlexandru Ardelean 	if (cnt == DOWNSHIFT_DEV_DISABLE)
3532d99b584SAlexandru Ardelean 		return phy_clear_bits(phydev, ADIN1300_PHY_CTRL2,
3542d99b584SAlexandru Ardelean 				      ADIN1300_DOWNSPEEDS_EN);
3552d99b584SAlexandru Ardelean 
3562d99b584SAlexandru Ardelean 	if (cnt > 7)
3572d99b584SAlexandru Ardelean 		return -E2BIG;
3582d99b584SAlexandru Ardelean 
3592d99b584SAlexandru Ardelean 	val = FIELD_PREP(ADIN1300_DOWNSPEED_RETRIES_MSK, cnt);
3602d99b584SAlexandru Ardelean 
3612d99b584SAlexandru Ardelean 	rc = phy_modify(phydev, ADIN1300_PHY_CTRL3,
36285ba75bbSAlexandru Ardelean 			ADIN1300_DOWNSPEED_RETRIES_MSK,
3632d99b584SAlexandru Ardelean 			val);
3642d99b584SAlexandru Ardelean 	if (rc < 0)
3652d99b584SAlexandru Ardelean 		return rc;
3662d99b584SAlexandru Ardelean 
3672d99b584SAlexandru Ardelean 	return phy_set_bits(phydev, ADIN1300_PHY_CTRL2,
3682d99b584SAlexandru Ardelean 			    ADIN1300_DOWNSPEEDS_EN);
3692d99b584SAlexandru Ardelean }
3702d99b584SAlexandru Ardelean 
adin_get_edpd(struct phy_device * phydev,u16 * tx_interval)37165d7be09SAlexandru Ardelean static int adin_get_edpd(struct phy_device *phydev, u16 *tx_interval)
37265d7be09SAlexandru Ardelean {
37365d7be09SAlexandru Ardelean 	int val;
37465d7be09SAlexandru Ardelean 
37565d7be09SAlexandru Ardelean 	val = phy_read(phydev, ADIN1300_PHY_CTRL_STATUS2);
37665d7be09SAlexandru Ardelean 	if (val < 0)
37765d7be09SAlexandru Ardelean 		return val;
37865d7be09SAlexandru Ardelean 
37965d7be09SAlexandru Ardelean 	if (ADIN1300_NRG_PD_EN & val) {
38065d7be09SAlexandru Ardelean 		if (val & ADIN1300_NRG_PD_TX_EN)
38165d7be09SAlexandru Ardelean 			/* default is 1 second */
38265d7be09SAlexandru Ardelean 			*tx_interval = ETHTOOL_PHY_EDPD_DFLT_TX_MSECS;
38365d7be09SAlexandru Ardelean 		else
38465d7be09SAlexandru Ardelean 			*tx_interval = ETHTOOL_PHY_EDPD_NO_TX;
38565d7be09SAlexandru Ardelean 	} else {
38665d7be09SAlexandru Ardelean 		*tx_interval = ETHTOOL_PHY_EDPD_DISABLE;
38765d7be09SAlexandru Ardelean 	}
38865d7be09SAlexandru Ardelean 
38965d7be09SAlexandru Ardelean 	return 0;
39065d7be09SAlexandru Ardelean }
39165d7be09SAlexandru Ardelean 
adin_set_edpd(struct phy_device * phydev,u16 tx_interval)39265d7be09SAlexandru Ardelean static int adin_set_edpd(struct phy_device *phydev, u16 tx_interval)
39365d7be09SAlexandru Ardelean {
39465d7be09SAlexandru Ardelean 	u16 val;
39565d7be09SAlexandru Ardelean 
39665d7be09SAlexandru Ardelean 	if (tx_interval == ETHTOOL_PHY_EDPD_DISABLE)
39765d7be09SAlexandru Ardelean 		return phy_clear_bits(phydev, ADIN1300_PHY_CTRL_STATUS2,
39865d7be09SAlexandru Ardelean 				(ADIN1300_NRG_PD_EN | ADIN1300_NRG_PD_TX_EN));
39965d7be09SAlexandru Ardelean 
40065d7be09SAlexandru Ardelean 	val = ADIN1300_NRG_PD_EN;
40165d7be09SAlexandru Ardelean 
40265d7be09SAlexandru Ardelean 	switch (tx_interval) {
40365d7be09SAlexandru Ardelean 	case 1000: /* 1 second */
404df561f66SGustavo A. R. Silva 		fallthrough;
40565d7be09SAlexandru Ardelean 	case ETHTOOL_PHY_EDPD_DFLT_TX_MSECS:
40665d7be09SAlexandru Ardelean 		val |= ADIN1300_NRG_PD_TX_EN;
407df561f66SGustavo A. R. Silva 		fallthrough;
40865d7be09SAlexandru Ardelean 	case ETHTOOL_PHY_EDPD_NO_TX:
40965d7be09SAlexandru Ardelean 		break;
41065d7be09SAlexandru Ardelean 	default:
41165d7be09SAlexandru Ardelean 		return -EINVAL;
41265d7be09SAlexandru Ardelean 	}
41365d7be09SAlexandru Ardelean 
41465d7be09SAlexandru Ardelean 	return phy_modify(phydev, ADIN1300_PHY_CTRL_STATUS2,
41565d7be09SAlexandru Ardelean 			  (ADIN1300_NRG_PD_EN | ADIN1300_NRG_PD_TX_EN),
41665d7be09SAlexandru Ardelean 			  val);
41765d7be09SAlexandru Ardelean }
41865d7be09SAlexandru Ardelean 
adin_get_tunable(struct phy_device * phydev,struct ethtool_tunable * tuna,void * data)4192d99b584SAlexandru Ardelean static int adin_get_tunable(struct phy_device *phydev,
4202d99b584SAlexandru Ardelean 			    struct ethtool_tunable *tuna, void *data)
4212d99b584SAlexandru Ardelean {
4222d99b584SAlexandru Ardelean 	switch (tuna->id) {
4232d99b584SAlexandru Ardelean 	case ETHTOOL_PHY_DOWNSHIFT:
4242d99b584SAlexandru Ardelean 		return adin_get_downshift(phydev, data);
42565d7be09SAlexandru Ardelean 	case ETHTOOL_PHY_EDPD:
42665d7be09SAlexandru Ardelean 		return adin_get_edpd(phydev, data);
4272d99b584SAlexandru Ardelean 	default:
4282d99b584SAlexandru Ardelean 		return -EOPNOTSUPP;
4292d99b584SAlexandru Ardelean 	}
4302d99b584SAlexandru Ardelean }
4312d99b584SAlexandru Ardelean 
adin_set_tunable(struct phy_device * phydev,struct ethtool_tunable * tuna,const void * data)4322d99b584SAlexandru Ardelean static int adin_set_tunable(struct phy_device *phydev,
4332d99b584SAlexandru Ardelean 			    struct ethtool_tunable *tuna, const void *data)
4342d99b584SAlexandru Ardelean {
4352d99b584SAlexandru Ardelean 	switch (tuna->id) {
4362d99b584SAlexandru Ardelean 	case ETHTOOL_PHY_DOWNSHIFT:
4372d99b584SAlexandru Ardelean 		return adin_set_downshift(phydev, *(const u8 *)data);
43865d7be09SAlexandru Ardelean 	case ETHTOOL_PHY_EDPD:
43965d7be09SAlexandru Ardelean 		return adin_set_edpd(phydev, *(const u16 *)data);
4402d99b584SAlexandru Ardelean 	default:
4412d99b584SAlexandru Ardelean 		return -EOPNOTSUPP;
4422d99b584SAlexandru Ardelean 	}
4432d99b584SAlexandru Ardelean }
4442d99b584SAlexandru Ardelean 
adin_config_clk_out(struct phy_device * phydev)445ce334216SJosua Mayer static int adin_config_clk_out(struct phy_device *phydev)
446ce334216SJosua Mayer {
447ce334216SJosua Mayer 	struct device *dev = &phydev->mdio.dev;
448ce334216SJosua Mayer 	const char *val = NULL;
449ce334216SJosua Mayer 	u8 sel = 0;
450ce334216SJosua Mayer 
451ce334216SJosua Mayer 	device_property_read_string(dev, "adi,phy-output-clock", &val);
452ce334216SJosua Mayer 	if (!val) {
453ce334216SJosua Mayer 		/* property not present, do not enable GP_CLK pin */
454ce334216SJosua Mayer 	} else if (strcmp(val, "25mhz-reference") == 0) {
455ce334216SJosua Mayer 		sel |= ADIN1300_GE_CLK_CFG_25;
456ce334216SJosua Mayer 	} else if (strcmp(val, "125mhz-free-running") == 0) {
457ce334216SJosua Mayer 		sel |= ADIN1300_GE_CLK_CFG_FREE_125;
458ce334216SJosua Mayer 	} else if (strcmp(val, "adaptive-free-running") == 0) {
459ce334216SJosua Mayer 		sel |= ADIN1300_GE_CLK_CFG_HRT_FREE;
460ce334216SJosua Mayer 	} else {
461ce334216SJosua Mayer 		phydev_err(phydev, "invalid adi,phy-output-clock\n");
462ce334216SJosua Mayer 		return -EINVAL;
463ce334216SJosua Mayer 	}
464ce334216SJosua Mayer 
465ce334216SJosua Mayer 	if (device_property_read_bool(dev, "adi,phy-output-reference-clock"))
466ce334216SJosua Mayer 		sel |= ADIN1300_GE_CLK_CFG_REF_EN;
467ce334216SJosua Mayer 
468ce334216SJosua Mayer 	return phy_modify_mmd(phydev, MDIO_MMD_VEND1, ADIN1300_GE_CLK_CFG_REG,
469ce334216SJosua Mayer 			      ADIN1300_GE_CLK_CFG_MASK, sel);
470ce334216SJosua Mayer }
471ce334216SJosua Mayer 
adin_config_init(struct phy_device * phydev)4729c102981SAlexandru Ardelean static int adin_config_init(struct phy_device *phydev)
4739c102981SAlexandru Ardelean {
474d6200c8fSAlexandru Ardelean 	int rc;
475d6200c8fSAlexandru Ardelean 
476b422d1b6SAlexandru Ardelean 	phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
477b422d1b6SAlexandru Ardelean 
478d6200c8fSAlexandru Ardelean 	rc = adin_config_rgmii_mode(phydev);
479d6200c8fSAlexandru Ardelean 	if (rc < 0)
480d6200c8fSAlexandru Ardelean 		return rc;
481d6200c8fSAlexandru Ardelean 
482d6200c8fSAlexandru Ardelean 	rc = adin_config_rmii_mode(phydev);
483d6200c8fSAlexandru Ardelean 	if (rc < 0)
484d6200c8fSAlexandru Ardelean 		return rc;
485d6200c8fSAlexandru Ardelean 
4862d99b584SAlexandru Ardelean 	rc = adin_set_downshift(phydev, 4);
4872d99b584SAlexandru Ardelean 	if (rc < 0)
4882d99b584SAlexandru Ardelean 		return rc;
4892d99b584SAlexandru Ardelean 
49065d7be09SAlexandru Ardelean 	rc = adin_set_edpd(phydev, ETHTOOL_PHY_EDPD_DFLT_TX_MSECS);
49165d7be09SAlexandru Ardelean 	if (rc < 0)
49265d7be09SAlexandru Ardelean 		return rc;
49365d7be09SAlexandru Ardelean 
494ce334216SJosua Mayer 	rc = adin_config_clk_out(phydev);
495ce334216SJosua Mayer 	if (rc < 0)
496ce334216SJosua Mayer 		return rc;
497ce334216SJosua Mayer 
498d6200c8fSAlexandru Ardelean 	phydev_dbg(phydev, "PHY is using mode '%s'\n",
499d6200c8fSAlexandru Ardelean 		   phy_modes(phydev->interface));
500d6200c8fSAlexandru Ardelean 
501d6200c8fSAlexandru Ardelean 	return 0;
5029c102981SAlexandru Ardelean }
5039c102981SAlexandru Ardelean 
adin_phy_ack_intr(struct phy_device * phydev)504fb44b8d6SAlexandru Ardelean static int adin_phy_ack_intr(struct phy_device *phydev)
505fb44b8d6SAlexandru Ardelean {
506fb44b8d6SAlexandru Ardelean 	/* Clear pending interrupts */
507fb44b8d6SAlexandru Ardelean 	int rc = phy_read(phydev, ADIN1300_INT_STATUS_REG);
508fb44b8d6SAlexandru Ardelean 
509fb44b8d6SAlexandru Ardelean 	return rc < 0 ? rc : 0;
510fb44b8d6SAlexandru Ardelean }
511fb44b8d6SAlexandru Ardelean 
adin_phy_config_intr(struct phy_device * phydev)512fb44b8d6SAlexandru Ardelean static int adin_phy_config_intr(struct phy_device *phydev)
513fb44b8d6SAlexandru Ardelean {
5141d8300d3SIoana Ciornei 	int err;
515fb44b8d6SAlexandru Ardelean 
5161d8300d3SIoana Ciornei 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
5171d8300d3SIoana Ciornei 		err = adin_phy_ack_intr(phydev);
5181d8300d3SIoana Ciornei 		if (err)
5191d8300d3SIoana Ciornei 			return err;
5201d8300d3SIoana Ciornei 
5211d8300d3SIoana Ciornei 		err = phy_set_bits(phydev, ADIN1300_INT_MASK_REG,
522fb44b8d6SAlexandru Ardelean 				   ADIN1300_INT_MASK_EN);
5231d8300d3SIoana Ciornei 	} else {
5241d8300d3SIoana Ciornei 		err = phy_clear_bits(phydev, ADIN1300_INT_MASK_REG,
5251d8300d3SIoana Ciornei 				     ADIN1300_INT_MASK_EN);
5261d8300d3SIoana Ciornei 		if (err)
5271d8300d3SIoana Ciornei 			return err;
5281d8300d3SIoana Ciornei 
5291d8300d3SIoana Ciornei 		err = adin_phy_ack_intr(phydev);
5301d8300d3SIoana Ciornei 	}
5311d8300d3SIoana Ciornei 
5321d8300d3SIoana Ciornei 	return err;
533fb44b8d6SAlexandru Ardelean }
534fb44b8d6SAlexandru Ardelean 
adin_phy_handle_interrupt(struct phy_device * phydev)53566d7439eSIoana Ciornei static irqreturn_t adin_phy_handle_interrupt(struct phy_device *phydev)
53666d7439eSIoana Ciornei {
53766d7439eSIoana Ciornei 	int irq_status;
53866d7439eSIoana Ciornei 
53966d7439eSIoana Ciornei 	irq_status = phy_read(phydev, ADIN1300_INT_STATUS_REG);
54066d7439eSIoana Ciornei 	if (irq_status < 0) {
54166d7439eSIoana Ciornei 		phy_error(phydev);
54266d7439eSIoana Ciornei 		return IRQ_NONE;
54366d7439eSIoana Ciornei 	}
54466d7439eSIoana Ciornei 
54566d7439eSIoana Ciornei 	if (!(irq_status & ADIN1300_INT_LINK_STAT_CHNG_EN))
54666d7439eSIoana Ciornei 		return IRQ_NONE;
54766d7439eSIoana Ciornei 
54866d7439eSIoana Ciornei 	phy_trigger_machine(phydev);
54966d7439eSIoana Ciornei 
55066d7439eSIoana Ciornei 	return IRQ_HANDLED;
55166d7439eSIoana Ciornei }
55266d7439eSIoana Ciornei 
adin_cl45_to_adin_reg(struct phy_device * phydev,int devad,u16 cl45_regnum)553c6aa697cSAlexandru Ardelean static int adin_cl45_to_adin_reg(struct phy_device *phydev, int devad,
554c6aa697cSAlexandru Ardelean 				 u16 cl45_regnum)
555c6aa697cSAlexandru Ardelean {
556aa63b947SAlexandru Ardelean 	const struct adin_clause45_mmd_map *m;
557c6aa697cSAlexandru Ardelean 	int i;
558c6aa697cSAlexandru Ardelean 
559c6aa697cSAlexandru Ardelean 	if (devad == MDIO_MMD_VEND1)
560c6aa697cSAlexandru Ardelean 		return cl45_regnum;
561c6aa697cSAlexandru Ardelean 
562c6aa697cSAlexandru Ardelean 	for (i = 0; i < ARRAY_SIZE(adin_clause45_mmd_map); i++) {
563c6aa697cSAlexandru Ardelean 		m = &adin_clause45_mmd_map[i];
564c6aa697cSAlexandru Ardelean 		if (m->devad == devad && m->cl45_regnum == cl45_regnum)
565c6aa697cSAlexandru Ardelean 			return m->adin_regnum;
566c6aa697cSAlexandru Ardelean 	}
567c6aa697cSAlexandru Ardelean 
568c6aa697cSAlexandru Ardelean 	phydev_err(phydev,
569c6aa697cSAlexandru Ardelean 		   "No translation available for devad: %d reg: %04x\n",
570c6aa697cSAlexandru Ardelean 		   devad, cl45_regnum);
571c6aa697cSAlexandru Ardelean 
572c6aa697cSAlexandru Ardelean 	return -EINVAL;
573c6aa697cSAlexandru Ardelean }
574c6aa697cSAlexandru Ardelean 
adin_read_mmd(struct phy_device * phydev,int devad,u16 regnum)5753e32d020SAlexandru Ardelean static int adin_read_mmd(struct phy_device *phydev, int devad, u16 regnum)
5763e32d020SAlexandru Ardelean {
5773e32d020SAlexandru Ardelean 	struct mii_bus *bus = phydev->mdio.bus;
5783e32d020SAlexandru Ardelean 	int phy_addr = phydev->mdio.addr;
579c6aa697cSAlexandru Ardelean 	int adin_regnum;
5803e32d020SAlexandru Ardelean 	int err;
5813e32d020SAlexandru Ardelean 
582c6aa697cSAlexandru Ardelean 	adin_regnum = adin_cl45_to_adin_reg(phydev, devad, regnum);
583c6aa697cSAlexandru Ardelean 	if (adin_regnum < 0)
584c6aa697cSAlexandru Ardelean 		return adin_regnum;
585c6aa697cSAlexandru Ardelean 
586c6aa697cSAlexandru Ardelean 	err = __mdiobus_write(bus, phy_addr, ADIN1300_MII_EXT_REG_PTR,
587c6aa697cSAlexandru Ardelean 			      adin_regnum);
5883e32d020SAlexandru Ardelean 	if (err)
5893e32d020SAlexandru Ardelean 		return err;
5903e32d020SAlexandru Ardelean 
5913e32d020SAlexandru Ardelean 	return __mdiobus_read(bus, phy_addr, ADIN1300_MII_EXT_REG_DATA);
5923e32d020SAlexandru Ardelean }
5933e32d020SAlexandru Ardelean 
adin_write_mmd(struct phy_device * phydev,int devad,u16 regnum,u16 val)5943e32d020SAlexandru Ardelean static int adin_write_mmd(struct phy_device *phydev, int devad, u16 regnum,
5953e32d020SAlexandru Ardelean 			  u16 val)
5963e32d020SAlexandru Ardelean {
5973e32d020SAlexandru Ardelean 	struct mii_bus *bus = phydev->mdio.bus;
5983e32d020SAlexandru Ardelean 	int phy_addr = phydev->mdio.addr;
599c6aa697cSAlexandru Ardelean 	int adin_regnum;
6003e32d020SAlexandru Ardelean 	int err;
6013e32d020SAlexandru Ardelean 
602c6aa697cSAlexandru Ardelean 	adin_regnum = adin_cl45_to_adin_reg(phydev, devad, regnum);
603c6aa697cSAlexandru Ardelean 	if (adin_regnum < 0)
604c6aa697cSAlexandru Ardelean 		return adin_regnum;
605c6aa697cSAlexandru Ardelean 
606c6aa697cSAlexandru Ardelean 	err = __mdiobus_write(bus, phy_addr, ADIN1300_MII_EXT_REG_PTR,
607c6aa697cSAlexandru Ardelean 			      adin_regnum);
6083e32d020SAlexandru Ardelean 	if (err)
6093e32d020SAlexandru Ardelean 		return err;
6103e32d020SAlexandru Ardelean 
6113e32d020SAlexandru Ardelean 	return __mdiobus_write(bus, phy_addr, ADIN1300_MII_EXT_REG_DATA, val);
6123e32d020SAlexandru Ardelean }
6133e32d020SAlexandru Ardelean 
adin_config_mdix(struct phy_device * phydev)614b422d1b6SAlexandru Ardelean static int adin_config_mdix(struct phy_device *phydev)
615b422d1b6SAlexandru Ardelean {
616b422d1b6SAlexandru Ardelean 	bool auto_en, mdix_en;
617b422d1b6SAlexandru Ardelean 	int reg;
618b422d1b6SAlexandru Ardelean 
619b422d1b6SAlexandru Ardelean 	mdix_en = false;
620b422d1b6SAlexandru Ardelean 	auto_en = false;
621b422d1b6SAlexandru Ardelean 	switch (phydev->mdix_ctrl) {
622b422d1b6SAlexandru Ardelean 	case ETH_TP_MDI:
623b422d1b6SAlexandru Ardelean 		break;
624b422d1b6SAlexandru Ardelean 	case ETH_TP_MDI_X:
625b422d1b6SAlexandru Ardelean 		mdix_en = true;
626b422d1b6SAlexandru Ardelean 		break;
627b422d1b6SAlexandru Ardelean 	case ETH_TP_MDI_AUTO:
628b422d1b6SAlexandru Ardelean 		auto_en = true;
629b422d1b6SAlexandru Ardelean 		break;
630b422d1b6SAlexandru Ardelean 	default:
631b422d1b6SAlexandru Ardelean 		return -EINVAL;
632b422d1b6SAlexandru Ardelean 	}
633b422d1b6SAlexandru Ardelean 
634b422d1b6SAlexandru Ardelean 	reg = phy_read(phydev, ADIN1300_PHY_CTRL1);
635b422d1b6SAlexandru Ardelean 	if (reg < 0)
636b422d1b6SAlexandru Ardelean 		return reg;
637b422d1b6SAlexandru Ardelean 
638b422d1b6SAlexandru Ardelean 	if (mdix_en)
639b422d1b6SAlexandru Ardelean 		reg |= ADIN1300_MAN_MDIX_EN;
640b422d1b6SAlexandru Ardelean 	else
641b422d1b6SAlexandru Ardelean 		reg &= ~ADIN1300_MAN_MDIX_EN;
642b422d1b6SAlexandru Ardelean 
643b422d1b6SAlexandru Ardelean 	if (auto_en)
644b422d1b6SAlexandru Ardelean 		reg |= ADIN1300_AUTO_MDI_EN;
645b422d1b6SAlexandru Ardelean 	else
646b422d1b6SAlexandru Ardelean 		reg &= ~ADIN1300_AUTO_MDI_EN;
647b422d1b6SAlexandru Ardelean 
648b422d1b6SAlexandru Ardelean 	return phy_write(phydev, ADIN1300_PHY_CTRL1, reg);
649b422d1b6SAlexandru Ardelean }
650b422d1b6SAlexandru Ardelean 
adin_config_aneg(struct phy_device * phydev)651b422d1b6SAlexandru Ardelean static int adin_config_aneg(struct phy_device *phydev)
652b422d1b6SAlexandru Ardelean {
653b422d1b6SAlexandru Ardelean 	int ret;
654b422d1b6SAlexandru Ardelean 
65585ba75bbSAlexandru Ardelean 	ret = phy_clear_bits(phydev, ADIN1300_PHY_CTRL1, ADIN1300_DIAG_CLK_EN);
65685ba75bbSAlexandru Ardelean 	if (ret < 0)
65785ba75bbSAlexandru Ardelean 		return ret;
65885ba75bbSAlexandru Ardelean 
65985ba75bbSAlexandru Ardelean 	ret = phy_set_bits(phydev, ADIN1300_PHY_CTRL3, ADIN1300_LINKING_EN);
66085ba75bbSAlexandru Ardelean 	if (ret < 0)
66185ba75bbSAlexandru Ardelean 		return ret;
66285ba75bbSAlexandru Ardelean 
663b422d1b6SAlexandru Ardelean 	ret = adin_config_mdix(phydev);
664b422d1b6SAlexandru Ardelean 	if (ret)
665b422d1b6SAlexandru Ardelean 		return ret;
666b422d1b6SAlexandru Ardelean 
667b422d1b6SAlexandru Ardelean 	return genphy_config_aneg(phydev);
668b422d1b6SAlexandru Ardelean }
669b422d1b6SAlexandru Ardelean 
adin_mdix_update(struct phy_device * phydev)670b422d1b6SAlexandru Ardelean static int adin_mdix_update(struct phy_device *phydev)
671b422d1b6SAlexandru Ardelean {
672b422d1b6SAlexandru Ardelean 	bool auto_en, mdix_en;
673b422d1b6SAlexandru Ardelean 	bool swapped;
674b422d1b6SAlexandru Ardelean 	int reg;
675b422d1b6SAlexandru Ardelean 
676b422d1b6SAlexandru Ardelean 	reg = phy_read(phydev, ADIN1300_PHY_CTRL1);
677b422d1b6SAlexandru Ardelean 	if (reg < 0)
678b422d1b6SAlexandru Ardelean 		return reg;
679b422d1b6SAlexandru Ardelean 
680b422d1b6SAlexandru Ardelean 	auto_en = !!(reg & ADIN1300_AUTO_MDI_EN);
681b422d1b6SAlexandru Ardelean 	mdix_en = !!(reg & ADIN1300_MAN_MDIX_EN);
682b422d1b6SAlexandru Ardelean 
683b422d1b6SAlexandru Ardelean 	/* If MDI/MDIX is forced, just read it from the control reg */
684b422d1b6SAlexandru Ardelean 	if (!auto_en) {
685b422d1b6SAlexandru Ardelean 		if (mdix_en)
686b422d1b6SAlexandru Ardelean 			phydev->mdix = ETH_TP_MDI_X;
687b422d1b6SAlexandru Ardelean 		else
688b422d1b6SAlexandru Ardelean 			phydev->mdix = ETH_TP_MDI;
689b422d1b6SAlexandru Ardelean 		return 0;
690b422d1b6SAlexandru Ardelean 	}
691b422d1b6SAlexandru Ardelean 
692b422d1b6SAlexandru Ardelean 	/**
693b422d1b6SAlexandru Ardelean 	 * Otherwise, we need to deduce it from the PHY status2 reg.
694b422d1b6SAlexandru Ardelean 	 * When Auto-MDI is enabled, the ADIN1300_MAN_MDIX_EN bit implies
695b422d1b6SAlexandru Ardelean 	 * a preference for MDIX when it is set.
696b422d1b6SAlexandru Ardelean 	 */
697b422d1b6SAlexandru Ardelean 	reg = phy_read(phydev, ADIN1300_PHY_STATUS1);
698b422d1b6SAlexandru Ardelean 	if (reg < 0)
699b422d1b6SAlexandru Ardelean 		return reg;
700b422d1b6SAlexandru Ardelean 
701b422d1b6SAlexandru Ardelean 	swapped = !!(reg & ADIN1300_PAIR_01_SWAP);
702b422d1b6SAlexandru Ardelean 
703b422d1b6SAlexandru Ardelean 	if (mdix_en != swapped)
704b422d1b6SAlexandru Ardelean 		phydev->mdix = ETH_TP_MDI_X;
705b422d1b6SAlexandru Ardelean 	else
706b422d1b6SAlexandru Ardelean 		phydev->mdix = ETH_TP_MDI;
707b422d1b6SAlexandru Ardelean 
708b422d1b6SAlexandru Ardelean 	return 0;
709b422d1b6SAlexandru Ardelean }
710b422d1b6SAlexandru Ardelean 
adin_read_status(struct phy_device * phydev)711b422d1b6SAlexandru Ardelean static int adin_read_status(struct phy_device *phydev)
712b422d1b6SAlexandru Ardelean {
713b422d1b6SAlexandru Ardelean 	int ret;
714b422d1b6SAlexandru Ardelean 
715b422d1b6SAlexandru Ardelean 	ret = adin_mdix_update(phydev);
716b422d1b6SAlexandru Ardelean 	if (ret < 0)
717b422d1b6SAlexandru Ardelean 		return ret;
718b422d1b6SAlexandru Ardelean 
719b422d1b6SAlexandru Ardelean 	return genphy_read_status(phydev);
720b422d1b6SAlexandru Ardelean }
721b422d1b6SAlexandru Ardelean 
adin_soft_reset(struct phy_device * phydev)722fa5bd9c5SAlexandru Ardelean static int adin_soft_reset(struct phy_device *phydev)
723fa5bd9c5SAlexandru Ardelean {
724fa5bd9c5SAlexandru Ardelean 	int rc;
725fa5bd9c5SAlexandru Ardelean 
726fa5bd9c5SAlexandru Ardelean 	/* The reset bit is self-clearing, set it and wait */
727fa5bd9c5SAlexandru Ardelean 	rc = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1,
728fa5bd9c5SAlexandru Ardelean 			      ADIN1300_GE_SOFT_RESET_REG,
729fa5bd9c5SAlexandru Ardelean 			      ADIN1300_GE_SOFT_RESET);
730fa5bd9c5SAlexandru Ardelean 	if (rc < 0)
731fa5bd9c5SAlexandru Ardelean 		return rc;
732fa5bd9c5SAlexandru Ardelean 
7330c58ac1eSDejin Zheng 	msleep(20);
734fa5bd9c5SAlexandru Ardelean 
735fa5bd9c5SAlexandru Ardelean 	/* If we get a read error something may be wrong */
736fa5bd9c5SAlexandru Ardelean 	rc = phy_read_mmd(phydev, MDIO_MMD_VEND1,
737fa5bd9c5SAlexandru Ardelean 			  ADIN1300_GE_SOFT_RESET_REG);
738fa5bd9c5SAlexandru Ardelean 
739fa5bd9c5SAlexandru Ardelean 	return rc < 0 ? rc : 0;
740fa5bd9c5SAlexandru Ardelean }
741fa5bd9c5SAlexandru Ardelean 
adin_get_sset_count(struct phy_device * phydev)7429fe0b8d6SAlexandru Ardelean static int adin_get_sset_count(struct phy_device *phydev)
7439fe0b8d6SAlexandru Ardelean {
7449fe0b8d6SAlexandru Ardelean 	return ARRAY_SIZE(adin_hw_stats);
7459fe0b8d6SAlexandru Ardelean }
7469fe0b8d6SAlexandru Ardelean 
adin_get_strings(struct phy_device * phydev,u8 * data)7479fe0b8d6SAlexandru Ardelean static void adin_get_strings(struct phy_device *phydev, u8 *data)
7489fe0b8d6SAlexandru Ardelean {
7499fe0b8d6SAlexandru Ardelean 	int i;
7509fe0b8d6SAlexandru Ardelean 
7519fe0b8d6SAlexandru Ardelean 	for (i = 0; i < ARRAY_SIZE(adin_hw_stats); i++) {
752*fb3ceec1SWolfram Sang 		strscpy(&data[i * ETH_GSTRING_LEN],
7539fe0b8d6SAlexandru Ardelean 			adin_hw_stats[i].string, ETH_GSTRING_LEN);
7549fe0b8d6SAlexandru Ardelean 	}
7559fe0b8d6SAlexandru Ardelean }
7569fe0b8d6SAlexandru Ardelean 
adin_read_mmd_stat_regs(struct phy_device * phydev,const struct adin_hw_stat * stat,u32 * val)7579fe0b8d6SAlexandru Ardelean static int adin_read_mmd_stat_regs(struct phy_device *phydev,
758aa63b947SAlexandru Ardelean 				   const struct adin_hw_stat *stat,
7599fe0b8d6SAlexandru Ardelean 				   u32 *val)
7609fe0b8d6SAlexandru Ardelean {
7619fe0b8d6SAlexandru Ardelean 	int ret;
7629fe0b8d6SAlexandru Ardelean 
7639fe0b8d6SAlexandru Ardelean 	ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, stat->reg1);
7649fe0b8d6SAlexandru Ardelean 	if (ret < 0)
7659fe0b8d6SAlexandru Ardelean 		return ret;
7669fe0b8d6SAlexandru Ardelean 
7679fe0b8d6SAlexandru Ardelean 	*val = (ret & 0xffff);
7689fe0b8d6SAlexandru Ardelean 
7699fe0b8d6SAlexandru Ardelean 	if (stat->reg2 == 0)
7709fe0b8d6SAlexandru Ardelean 		return 0;
7719fe0b8d6SAlexandru Ardelean 
7729fe0b8d6SAlexandru Ardelean 	ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, stat->reg2);
7739fe0b8d6SAlexandru Ardelean 	if (ret < 0)
7749fe0b8d6SAlexandru Ardelean 		return ret;
7759fe0b8d6SAlexandru Ardelean 
7769fe0b8d6SAlexandru Ardelean 	*val <<= 16;
7779fe0b8d6SAlexandru Ardelean 	*val |= (ret & 0xffff);
7789fe0b8d6SAlexandru Ardelean 
7799fe0b8d6SAlexandru Ardelean 	return 0;
7809fe0b8d6SAlexandru Ardelean }
7819fe0b8d6SAlexandru Ardelean 
adin_get_stat(struct phy_device * phydev,int i)7829fe0b8d6SAlexandru Ardelean static u64 adin_get_stat(struct phy_device *phydev, int i)
7839fe0b8d6SAlexandru Ardelean {
784aa63b947SAlexandru Ardelean 	const struct adin_hw_stat *stat = &adin_hw_stats[i];
7859fe0b8d6SAlexandru Ardelean 	struct adin_priv *priv = phydev->priv;
7869fe0b8d6SAlexandru Ardelean 	u32 val;
7879fe0b8d6SAlexandru Ardelean 	int ret;
7889fe0b8d6SAlexandru Ardelean 
7899fe0b8d6SAlexandru Ardelean 	if (stat->reg1 > 0x1f) {
7909fe0b8d6SAlexandru Ardelean 		ret = adin_read_mmd_stat_regs(phydev, stat, &val);
7919fe0b8d6SAlexandru Ardelean 		if (ret < 0)
7929fe0b8d6SAlexandru Ardelean 			return (u64)(~0);
7939fe0b8d6SAlexandru Ardelean 	} else {
7949fe0b8d6SAlexandru Ardelean 		ret = phy_read(phydev, stat->reg1);
7959fe0b8d6SAlexandru Ardelean 		if (ret < 0)
7969fe0b8d6SAlexandru Ardelean 			return (u64)(~0);
7979fe0b8d6SAlexandru Ardelean 		val = (ret & 0xffff);
7989fe0b8d6SAlexandru Ardelean 	}
7999fe0b8d6SAlexandru Ardelean 
8009fe0b8d6SAlexandru Ardelean 	priv->stats[i] += val;
8019fe0b8d6SAlexandru Ardelean 
8029fe0b8d6SAlexandru Ardelean 	return priv->stats[i];
8039fe0b8d6SAlexandru Ardelean }
8049fe0b8d6SAlexandru Ardelean 
adin_get_stats(struct phy_device * phydev,struct ethtool_stats * stats,u64 * data)8059fe0b8d6SAlexandru Ardelean static void adin_get_stats(struct phy_device *phydev,
8069fe0b8d6SAlexandru Ardelean 			   struct ethtool_stats *stats, u64 *data)
8079fe0b8d6SAlexandru Ardelean {
8089fe0b8d6SAlexandru Ardelean 	int i, rc;
8099fe0b8d6SAlexandru Ardelean 
8109fe0b8d6SAlexandru Ardelean 	/* latch copies of all the frame-checker counters */
8119fe0b8d6SAlexandru Ardelean 	rc = phy_read(phydev, ADIN1300_RX_ERR_CNT);
8129fe0b8d6SAlexandru Ardelean 	if (rc < 0)
8139fe0b8d6SAlexandru Ardelean 		return;
8149fe0b8d6SAlexandru Ardelean 
8159fe0b8d6SAlexandru Ardelean 	for (i = 0; i < ARRAY_SIZE(adin_hw_stats); i++)
8169fe0b8d6SAlexandru Ardelean 		data[i] = adin_get_stat(phydev, i);
8179fe0b8d6SAlexandru Ardelean }
8189fe0b8d6SAlexandru Ardelean 
adin_probe(struct phy_device * phydev)8199fe0b8d6SAlexandru Ardelean static int adin_probe(struct phy_device *phydev)
8209fe0b8d6SAlexandru Ardelean {
8219fe0b8d6SAlexandru Ardelean 	struct device *dev = &phydev->mdio.dev;
8229fe0b8d6SAlexandru Ardelean 	struct adin_priv *priv;
8239fe0b8d6SAlexandru Ardelean 
8249fe0b8d6SAlexandru Ardelean 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
8259fe0b8d6SAlexandru Ardelean 	if (!priv)
8269fe0b8d6SAlexandru Ardelean 		return -ENOMEM;
8279fe0b8d6SAlexandru Ardelean 
8289fe0b8d6SAlexandru Ardelean 	phydev->priv = priv;
8299fe0b8d6SAlexandru Ardelean 
8309fe0b8d6SAlexandru Ardelean 	return 0;
8319fe0b8d6SAlexandru Ardelean }
8329fe0b8d6SAlexandru Ardelean 
adin_cable_test_start(struct phy_device * phydev)833f2531d45SAlexandru Ardelean static int adin_cable_test_start(struct phy_device *phydev)
834f2531d45SAlexandru Ardelean {
835f2531d45SAlexandru Ardelean 	int ret;
836f2531d45SAlexandru Ardelean 
837f2531d45SAlexandru Ardelean 	ret = phy_clear_bits(phydev, ADIN1300_PHY_CTRL3, ADIN1300_LINKING_EN);
838f2531d45SAlexandru Ardelean 	if (ret < 0)
839f2531d45SAlexandru Ardelean 		return ret;
840f2531d45SAlexandru Ardelean 
841f2531d45SAlexandru Ardelean 	ret = phy_clear_bits(phydev, ADIN1300_PHY_CTRL1, ADIN1300_DIAG_CLK_EN);
842f2531d45SAlexandru Ardelean 	if (ret < 0)
843f2531d45SAlexandru Ardelean 		return ret;
844f2531d45SAlexandru Ardelean 
845f2531d45SAlexandru Ardelean 	/* wait a bit for the clock to stabilize */
846f2531d45SAlexandru Ardelean 	msleep(50);
847f2531d45SAlexandru Ardelean 
848f2531d45SAlexandru Ardelean 	return phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, ADIN1300_CDIAG_RUN,
849f2531d45SAlexandru Ardelean 				ADIN1300_CDIAG_RUN_EN);
850f2531d45SAlexandru Ardelean }
851f2531d45SAlexandru Ardelean 
adin_cable_test_report_trans(int result)852f2531d45SAlexandru Ardelean static int adin_cable_test_report_trans(int result)
853f2531d45SAlexandru Ardelean {
854f2531d45SAlexandru Ardelean 	int mask;
855f2531d45SAlexandru Ardelean 
856f2531d45SAlexandru Ardelean 	if (result & ADIN1300_CDIAG_RSLT_GOOD)
857f2531d45SAlexandru Ardelean 		return ETHTOOL_A_CABLE_RESULT_CODE_OK;
858f2531d45SAlexandru Ardelean 	if (result & ADIN1300_CDIAG_RSLT_OPEN)
859f2531d45SAlexandru Ardelean 		return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
860f2531d45SAlexandru Ardelean 
861f2531d45SAlexandru Ardelean 	/* short with other pairs */
862f2531d45SAlexandru Ardelean 	mask = ADIN1300_CDIAG_RSLT_XSHRT3 |
863f2531d45SAlexandru Ardelean 	       ADIN1300_CDIAG_RSLT_XSHRT2 |
864f2531d45SAlexandru Ardelean 	       ADIN1300_CDIAG_RSLT_XSHRT1;
865f2531d45SAlexandru Ardelean 	if (result & mask)
866f2531d45SAlexandru Ardelean 		return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
867f2531d45SAlexandru Ardelean 
868f2531d45SAlexandru Ardelean 	if (result & ADIN1300_CDIAG_RSLT_SHRT)
869f2531d45SAlexandru Ardelean 		return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
870f2531d45SAlexandru Ardelean 
871f2531d45SAlexandru Ardelean 	return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
872f2531d45SAlexandru Ardelean }
873f2531d45SAlexandru Ardelean 
adin_cable_test_report_pair(struct phy_device * phydev,unsigned int pair)874f2531d45SAlexandru Ardelean static int adin_cable_test_report_pair(struct phy_device *phydev,
875f2531d45SAlexandru Ardelean 				       unsigned int pair)
876f2531d45SAlexandru Ardelean {
877f2531d45SAlexandru Ardelean 	int fault_rslt;
878f2531d45SAlexandru Ardelean 	int ret;
879f2531d45SAlexandru Ardelean 
880f2531d45SAlexandru Ardelean 	ret = phy_read_mmd(phydev, MDIO_MMD_VEND1,
881f2531d45SAlexandru Ardelean 			   ADIN1300_CDIAG_DTLD_RSLTS(pair));
882f2531d45SAlexandru Ardelean 	if (ret < 0)
883f2531d45SAlexandru Ardelean 		return ret;
884f2531d45SAlexandru Ardelean 
885f2531d45SAlexandru Ardelean 	fault_rslt = adin_cable_test_report_trans(ret);
886f2531d45SAlexandru Ardelean 
887f2531d45SAlexandru Ardelean 	ret = ethnl_cable_test_result(phydev, pair, fault_rslt);
888f2531d45SAlexandru Ardelean 	if (ret < 0)
889f2531d45SAlexandru Ardelean 		return ret;
890f2531d45SAlexandru Ardelean 
891f2531d45SAlexandru Ardelean 	ret = phy_read_mmd(phydev, MDIO_MMD_VEND1,
892f2531d45SAlexandru Ardelean 			   ADIN1300_CDIAG_FLT_DIST(pair));
893f2531d45SAlexandru Ardelean 	if (ret < 0)
894f2531d45SAlexandru Ardelean 		return ret;
895f2531d45SAlexandru Ardelean 
896f2531d45SAlexandru Ardelean 	switch (fault_rslt) {
897f2531d45SAlexandru Ardelean 	case ETHTOOL_A_CABLE_RESULT_CODE_OPEN:
898f2531d45SAlexandru Ardelean 	case ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT:
899f2531d45SAlexandru Ardelean 	case ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT:
900f2531d45SAlexandru Ardelean 		return ethnl_cable_test_fault_length(phydev, pair, ret * 100);
901f2531d45SAlexandru Ardelean 	default:
902f2531d45SAlexandru Ardelean 		return  0;
903f2531d45SAlexandru Ardelean 	}
904f2531d45SAlexandru Ardelean }
905f2531d45SAlexandru Ardelean 
adin_cable_test_report(struct phy_device * phydev)906f2531d45SAlexandru Ardelean static int adin_cable_test_report(struct phy_device *phydev)
907f2531d45SAlexandru Ardelean {
908f2531d45SAlexandru Ardelean 	unsigned int pair;
909f2531d45SAlexandru Ardelean 	int ret;
910f2531d45SAlexandru Ardelean 
911f2531d45SAlexandru Ardelean 	for (pair = ETHTOOL_A_CABLE_PAIR_A; pair <= ETHTOOL_A_CABLE_PAIR_D; pair++) {
912f2531d45SAlexandru Ardelean 		ret = adin_cable_test_report_pair(phydev, pair);
913f2531d45SAlexandru Ardelean 		if (ret < 0)
914f2531d45SAlexandru Ardelean 			return ret;
915f2531d45SAlexandru Ardelean 	}
916f2531d45SAlexandru Ardelean 
917f2531d45SAlexandru Ardelean 	return 0;
918f2531d45SAlexandru Ardelean }
919f2531d45SAlexandru Ardelean 
adin_cable_test_get_status(struct phy_device * phydev,bool * finished)920f2531d45SAlexandru Ardelean static int adin_cable_test_get_status(struct phy_device *phydev,
921f2531d45SAlexandru Ardelean 				      bool *finished)
922f2531d45SAlexandru Ardelean {
923f2531d45SAlexandru Ardelean 	int ret;
924f2531d45SAlexandru Ardelean 
925f2531d45SAlexandru Ardelean 	*finished = false;
926f2531d45SAlexandru Ardelean 
927f2531d45SAlexandru Ardelean 	ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, ADIN1300_CDIAG_RUN);
928f2531d45SAlexandru Ardelean 	if (ret < 0)
929f2531d45SAlexandru Ardelean 		return ret;
930f2531d45SAlexandru Ardelean 
931f2531d45SAlexandru Ardelean 	if (ret & ADIN1300_CDIAG_RUN_EN)
932f2531d45SAlexandru Ardelean 		return 0;
933f2531d45SAlexandru Ardelean 
934f2531d45SAlexandru Ardelean 	*finished = true;
935f2531d45SAlexandru Ardelean 
936f2531d45SAlexandru Ardelean 	return adin_cable_test_report(phydev);
937f2531d45SAlexandru Ardelean }
938f2531d45SAlexandru Ardelean 
9399c102981SAlexandru Ardelean static struct phy_driver adin_driver[] = {
9409c102981SAlexandru Ardelean 	{
9419c102981SAlexandru Ardelean 		PHY_ID_MATCH_MODEL(PHY_ID_ADIN1200),
9429c102981SAlexandru Ardelean 		.name		= "ADIN1200",
943f2531d45SAlexandru Ardelean 		.flags		= PHY_POLL_CABLE_TEST,
9449fe0b8d6SAlexandru Ardelean 		.probe		= adin_probe,
9459c102981SAlexandru Ardelean 		.config_init	= adin_config_init,
946fa5bd9c5SAlexandru Ardelean 		.soft_reset	= adin_soft_reset,
947b422d1b6SAlexandru Ardelean 		.config_aneg	= adin_config_aneg,
948b422d1b6SAlexandru Ardelean 		.read_status	= adin_read_status,
9492d99b584SAlexandru Ardelean 		.get_tunable	= adin_get_tunable,
9502d99b584SAlexandru Ardelean 		.set_tunable	= adin_set_tunable,
951fb44b8d6SAlexandru Ardelean 		.config_intr	= adin_phy_config_intr,
95266d7439eSIoana Ciornei 		.handle_interrupt = adin_phy_handle_interrupt,
9539fe0b8d6SAlexandru Ardelean 		.get_sset_count	= adin_get_sset_count,
9549fe0b8d6SAlexandru Ardelean 		.get_strings	= adin_get_strings,
9559fe0b8d6SAlexandru Ardelean 		.get_stats	= adin_get_stats,
95649cc4c7dSAlexandru Ardelean 		.resume		= genphy_resume,
95749cc4c7dSAlexandru Ardelean 		.suspend	= genphy_suspend,
9583e32d020SAlexandru Ardelean 		.read_mmd	= adin_read_mmd,
9593e32d020SAlexandru Ardelean 		.write_mmd	= adin_write_mmd,
960f2531d45SAlexandru Ardelean 		.cable_test_start	= adin_cable_test_start,
961f2531d45SAlexandru Ardelean 		.cable_test_get_status	= adin_cable_test_get_status,
9629c102981SAlexandru Ardelean 	},
9639c102981SAlexandru Ardelean 	{
9649c102981SAlexandru Ardelean 		PHY_ID_MATCH_MODEL(PHY_ID_ADIN1300),
9659c102981SAlexandru Ardelean 		.name		= "ADIN1300",
966f2531d45SAlexandru Ardelean 		.flags		= PHY_POLL_CABLE_TEST,
9679fe0b8d6SAlexandru Ardelean 		.probe		= adin_probe,
9689c102981SAlexandru Ardelean 		.config_init	= adin_config_init,
969fa5bd9c5SAlexandru Ardelean 		.soft_reset	= adin_soft_reset,
970b422d1b6SAlexandru Ardelean 		.config_aneg	= adin_config_aneg,
971b422d1b6SAlexandru Ardelean 		.read_status	= adin_read_status,
9722d99b584SAlexandru Ardelean 		.get_tunable	= adin_get_tunable,
9732d99b584SAlexandru Ardelean 		.set_tunable	= adin_set_tunable,
974fb44b8d6SAlexandru Ardelean 		.config_intr	= adin_phy_config_intr,
97566d7439eSIoana Ciornei 		.handle_interrupt = adin_phy_handle_interrupt,
9769fe0b8d6SAlexandru Ardelean 		.get_sset_count	= adin_get_sset_count,
9779fe0b8d6SAlexandru Ardelean 		.get_strings	= adin_get_strings,
9789fe0b8d6SAlexandru Ardelean 		.get_stats	= adin_get_stats,
97949cc4c7dSAlexandru Ardelean 		.resume		= genphy_resume,
98049cc4c7dSAlexandru Ardelean 		.suspend	= genphy_suspend,
9813e32d020SAlexandru Ardelean 		.read_mmd	= adin_read_mmd,
9823e32d020SAlexandru Ardelean 		.write_mmd	= adin_write_mmd,
983f2531d45SAlexandru Ardelean 		.cable_test_start	= adin_cable_test_start,
984f2531d45SAlexandru Ardelean 		.cable_test_get_status	= adin_cable_test_get_status,
9859c102981SAlexandru Ardelean 	},
9869c102981SAlexandru Ardelean };
9879c102981SAlexandru Ardelean 
9889c102981SAlexandru Ardelean module_phy_driver(adin_driver);
9899c102981SAlexandru Ardelean 
9909c102981SAlexandru Ardelean static struct mdio_device_id __maybe_unused adin_tbl[] = {
9919c102981SAlexandru Ardelean 	{ PHY_ID_MATCH_MODEL(PHY_ID_ADIN1200) },
9929c102981SAlexandru Ardelean 	{ PHY_ID_MATCH_MODEL(PHY_ID_ADIN1300) },
9939c102981SAlexandru Ardelean 	{ }
9949c102981SAlexandru Ardelean };
9959c102981SAlexandru Ardelean 
9969c102981SAlexandru Ardelean MODULE_DEVICE_TABLE(mdio, adin_tbl);
9979c102981SAlexandru Ardelean MODULE_DESCRIPTION("Analog Devices Industrial Ethernet PHY driver");
9989c102981SAlexandru Ardelean MODULE_LICENSE("GPL");
999