xref: /linux/drivers/net/phy/adin1100.c (revision 7eaf9132996a34ba2643efe91a355385c6ff5114)
1*7eaf9132SAlexandru Ardelean // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
2*7eaf9132SAlexandru Ardelean /*
3*7eaf9132SAlexandru Ardelean  *  Driver for Analog Devices Industrial Ethernet T1L PHYs
4*7eaf9132SAlexandru Ardelean  *
5*7eaf9132SAlexandru Ardelean  * Copyright 2020 Analog Devices Inc.
6*7eaf9132SAlexandru Ardelean  */
7*7eaf9132SAlexandru Ardelean #include <linux/kernel.h>
8*7eaf9132SAlexandru Ardelean #include <linux/bitfield.h>
9*7eaf9132SAlexandru Ardelean #include <linux/delay.h>
10*7eaf9132SAlexandru Ardelean #include <linux/errno.h>
11*7eaf9132SAlexandru Ardelean #include <linux/init.h>
12*7eaf9132SAlexandru Ardelean #include <linux/module.h>
13*7eaf9132SAlexandru Ardelean #include <linux/mii.h>
14*7eaf9132SAlexandru Ardelean #include <linux/phy.h>
15*7eaf9132SAlexandru Ardelean #include <linux/property.h>
16*7eaf9132SAlexandru Ardelean 
17*7eaf9132SAlexandru Ardelean #define PHY_ID_ADIN1100				0x0283bc81
18*7eaf9132SAlexandru Ardelean 
19*7eaf9132SAlexandru Ardelean #define ADIN_FORCED_MODE			0x8000
20*7eaf9132SAlexandru Ardelean #define   ADIN_FORCED_MODE_EN			BIT(0)
21*7eaf9132SAlexandru Ardelean 
22*7eaf9132SAlexandru Ardelean #define ADIN_CRSM_SFT_RST			0x8810
23*7eaf9132SAlexandru Ardelean #define   ADIN_CRSM_SFT_RST_EN			BIT(0)
24*7eaf9132SAlexandru Ardelean 
25*7eaf9132SAlexandru Ardelean #define ADIN_CRSM_SFT_PD_CNTRL			0x8812
26*7eaf9132SAlexandru Ardelean #define   ADIN_CRSM_SFT_PD_CNTRL_EN		BIT(0)
27*7eaf9132SAlexandru Ardelean 
28*7eaf9132SAlexandru Ardelean #define ADIN_AN_PHY_INST_STATUS			0x8030
29*7eaf9132SAlexandru Ardelean #define   ADIN_IS_CFG_SLV			BIT(2)
30*7eaf9132SAlexandru Ardelean #define   ADIN_IS_CFG_MST			BIT(3)
31*7eaf9132SAlexandru Ardelean 
32*7eaf9132SAlexandru Ardelean #define ADIN_CRSM_STAT				0x8818
33*7eaf9132SAlexandru Ardelean #define   ADIN_CRSM_SFT_PD_RDY			BIT(1)
34*7eaf9132SAlexandru Ardelean #define   ADIN_CRSM_SYS_RDY			BIT(0)
35*7eaf9132SAlexandru Ardelean 
36*7eaf9132SAlexandru Ardelean /**
37*7eaf9132SAlexandru Ardelean  * struct adin_priv - ADIN PHY driver private data
38*7eaf9132SAlexandru Ardelean  * @tx_level_2v4_able:		set if the PHY supports 2.4V TX levels (10BASE-T1L)
39*7eaf9132SAlexandru Ardelean  * @tx_level_2v4:		set if the PHY requests 2.4V TX levels (10BASE-T1L)
40*7eaf9132SAlexandru Ardelean  * @tx_level_prop_present:	set if the TX level is specified in DT
41*7eaf9132SAlexandru Ardelean  */
42*7eaf9132SAlexandru Ardelean struct adin_priv {
43*7eaf9132SAlexandru Ardelean 	unsigned int		tx_level_2v4_able:1;
44*7eaf9132SAlexandru Ardelean 	unsigned int		tx_level_2v4:1;
45*7eaf9132SAlexandru Ardelean 	unsigned int		tx_level_prop_present:1;
46*7eaf9132SAlexandru Ardelean };
47*7eaf9132SAlexandru Ardelean 
48*7eaf9132SAlexandru Ardelean static int adin_read_status(struct phy_device *phydev)
49*7eaf9132SAlexandru Ardelean {
50*7eaf9132SAlexandru Ardelean 	int ret;
51*7eaf9132SAlexandru Ardelean 
52*7eaf9132SAlexandru Ardelean 	ret = genphy_c45_read_status(phydev);
53*7eaf9132SAlexandru Ardelean 	if (ret)
54*7eaf9132SAlexandru Ardelean 		return ret;
55*7eaf9132SAlexandru Ardelean 
56*7eaf9132SAlexandru Ardelean 	ret = phy_read_mmd(phydev, MDIO_MMD_AN, ADIN_AN_PHY_INST_STATUS);
57*7eaf9132SAlexandru Ardelean 	if (ret < 0)
58*7eaf9132SAlexandru Ardelean 		return ret;
59*7eaf9132SAlexandru Ardelean 
60*7eaf9132SAlexandru Ardelean 	if (ret & ADIN_IS_CFG_SLV)
61*7eaf9132SAlexandru Ardelean 		phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE;
62*7eaf9132SAlexandru Ardelean 
63*7eaf9132SAlexandru Ardelean 	if (ret & ADIN_IS_CFG_MST)
64*7eaf9132SAlexandru Ardelean 		phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER;
65*7eaf9132SAlexandru Ardelean 
66*7eaf9132SAlexandru Ardelean 	return 0;
67*7eaf9132SAlexandru Ardelean }
68*7eaf9132SAlexandru Ardelean 
69*7eaf9132SAlexandru Ardelean static int adin_config_aneg(struct phy_device *phydev)
70*7eaf9132SAlexandru Ardelean {
71*7eaf9132SAlexandru Ardelean 	struct adin_priv *priv = phydev->priv;
72*7eaf9132SAlexandru Ardelean 	int ret;
73*7eaf9132SAlexandru Ardelean 
74*7eaf9132SAlexandru Ardelean 	if (phydev->autoneg == AUTONEG_DISABLE) {
75*7eaf9132SAlexandru Ardelean 		ret = genphy_c45_pma_setup_forced(phydev);
76*7eaf9132SAlexandru Ardelean 		if (ret < 0)
77*7eaf9132SAlexandru Ardelean 			return ret;
78*7eaf9132SAlexandru Ardelean 
79*7eaf9132SAlexandru Ardelean 		if (priv->tx_level_prop_present && priv->tx_level_2v4)
80*7eaf9132SAlexandru Ardelean 			ret = phy_set_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_B10L_PMA_CTRL,
81*7eaf9132SAlexandru Ardelean 					       MDIO_PMA_10T1L_CTRL_2V4_EN);
82*7eaf9132SAlexandru Ardelean 		else
83*7eaf9132SAlexandru Ardelean 			ret = phy_clear_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_B10L_PMA_CTRL,
84*7eaf9132SAlexandru Ardelean 						 MDIO_PMA_10T1L_CTRL_2V4_EN);
85*7eaf9132SAlexandru Ardelean 		if (ret < 0)
86*7eaf9132SAlexandru Ardelean 			return ret;
87*7eaf9132SAlexandru Ardelean 
88*7eaf9132SAlexandru Ardelean 		/* Force PHY to use above configurations */
89*7eaf9132SAlexandru Ardelean 		return phy_set_bits_mmd(phydev, MDIO_MMD_AN, ADIN_FORCED_MODE, ADIN_FORCED_MODE_EN);
90*7eaf9132SAlexandru Ardelean 	}
91*7eaf9132SAlexandru Ardelean 
92*7eaf9132SAlexandru Ardelean 	ret = phy_clear_bits_mmd(phydev, MDIO_MMD_AN, ADIN_FORCED_MODE, ADIN_FORCED_MODE_EN);
93*7eaf9132SAlexandru Ardelean 	if (ret < 0)
94*7eaf9132SAlexandru Ardelean 		return ret;
95*7eaf9132SAlexandru Ardelean 
96*7eaf9132SAlexandru Ardelean 	/* Request increased transmit level from LP. */
97*7eaf9132SAlexandru Ardelean 	if (priv->tx_level_prop_present && priv->tx_level_2v4) {
98*7eaf9132SAlexandru Ardelean 		ret = phy_set_bits_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_ADV_H,
99*7eaf9132SAlexandru Ardelean 				       MDIO_AN_T1_ADV_H_10L_TX_HI |
100*7eaf9132SAlexandru Ardelean 				       MDIO_AN_T1_ADV_H_10L_TX_HI_REQ);
101*7eaf9132SAlexandru Ardelean 		if (ret < 0)
102*7eaf9132SAlexandru Ardelean 			return ret;
103*7eaf9132SAlexandru Ardelean 	}
104*7eaf9132SAlexandru Ardelean 
105*7eaf9132SAlexandru Ardelean 	/* Disable 2.4 Vpp transmit level. */
106*7eaf9132SAlexandru Ardelean 	if ((priv->tx_level_prop_present && !priv->tx_level_2v4) || !priv->tx_level_2v4_able) {
107*7eaf9132SAlexandru Ardelean 		ret = phy_clear_bits_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_ADV_H,
108*7eaf9132SAlexandru Ardelean 					 MDIO_AN_T1_ADV_H_10L_TX_HI |
109*7eaf9132SAlexandru Ardelean 					 MDIO_AN_T1_ADV_H_10L_TX_HI_REQ);
110*7eaf9132SAlexandru Ardelean 		if (ret < 0)
111*7eaf9132SAlexandru Ardelean 			return ret;
112*7eaf9132SAlexandru Ardelean 	}
113*7eaf9132SAlexandru Ardelean 
114*7eaf9132SAlexandru Ardelean 	return genphy_c45_config_aneg(phydev);
115*7eaf9132SAlexandru Ardelean }
116*7eaf9132SAlexandru Ardelean 
117*7eaf9132SAlexandru Ardelean static int adin_set_powerdown_mode(struct phy_device *phydev, bool en)
118*7eaf9132SAlexandru Ardelean {
119*7eaf9132SAlexandru Ardelean 	int ret;
120*7eaf9132SAlexandru Ardelean 	int val;
121*7eaf9132SAlexandru Ardelean 
122*7eaf9132SAlexandru Ardelean 	val = en ? ADIN_CRSM_SFT_PD_CNTRL_EN : 0;
123*7eaf9132SAlexandru Ardelean 	ret = phy_write_mmd(phydev, MDIO_MMD_VEND1,
124*7eaf9132SAlexandru Ardelean 			    ADIN_CRSM_SFT_PD_CNTRL, val);
125*7eaf9132SAlexandru Ardelean 	if (ret < 0)
126*7eaf9132SAlexandru Ardelean 		return ret;
127*7eaf9132SAlexandru Ardelean 
128*7eaf9132SAlexandru Ardelean 	return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, ADIN_CRSM_STAT, ret,
129*7eaf9132SAlexandru Ardelean 					 (ret & ADIN_CRSM_SFT_PD_RDY) == val,
130*7eaf9132SAlexandru Ardelean 					 1000, 30000, true);
131*7eaf9132SAlexandru Ardelean }
132*7eaf9132SAlexandru Ardelean 
133*7eaf9132SAlexandru Ardelean static int adin_suspend(struct phy_device *phydev)
134*7eaf9132SAlexandru Ardelean {
135*7eaf9132SAlexandru Ardelean 	return adin_set_powerdown_mode(phydev, true);
136*7eaf9132SAlexandru Ardelean }
137*7eaf9132SAlexandru Ardelean 
138*7eaf9132SAlexandru Ardelean static int adin_resume(struct phy_device *phydev)
139*7eaf9132SAlexandru Ardelean {
140*7eaf9132SAlexandru Ardelean 	return adin_set_powerdown_mode(phydev, false);
141*7eaf9132SAlexandru Ardelean }
142*7eaf9132SAlexandru Ardelean 
143*7eaf9132SAlexandru Ardelean static int adin_set_loopback(struct phy_device *phydev, bool enable)
144*7eaf9132SAlexandru Ardelean {
145*7eaf9132SAlexandru Ardelean 	if (enable)
146*7eaf9132SAlexandru Ardelean 		return phy_set_bits_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_10T1L_CTRL,
147*7eaf9132SAlexandru Ardelean 					BMCR_LOOPBACK);
148*7eaf9132SAlexandru Ardelean 
149*7eaf9132SAlexandru Ardelean 	/* PCS loopback (according to 10BASE-T1L spec) */
150*7eaf9132SAlexandru Ardelean 	return phy_clear_bits_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_10T1L_CTRL,
151*7eaf9132SAlexandru Ardelean 				 BMCR_LOOPBACK);
152*7eaf9132SAlexandru Ardelean }
153*7eaf9132SAlexandru Ardelean 
154*7eaf9132SAlexandru Ardelean static int adin_soft_reset(struct phy_device *phydev)
155*7eaf9132SAlexandru Ardelean {
156*7eaf9132SAlexandru Ardelean 	int ret;
157*7eaf9132SAlexandru Ardelean 
158*7eaf9132SAlexandru Ardelean 	ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, ADIN_CRSM_SFT_RST, ADIN_CRSM_SFT_RST_EN);
159*7eaf9132SAlexandru Ardelean 	if (ret < 0)
160*7eaf9132SAlexandru Ardelean 		return ret;
161*7eaf9132SAlexandru Ardelean 
162*7eaf9132SAlexandru Ardelean 	return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, ADIN_CRSM_STAT, ret,
163*7eaf9132SAlexandru Ardelean 					 (ret & ADIN_CRSM_SYS_RDY),
164*7eaf9132SAlexandru Ardelean 					 10000, 30000, true);
165*7eaf9132SAlexandru Ardelean }
166*7eaf9132SAlexandru Ardelean 
167*7eaf9132SAlexandru Ardelean static int adin_get_features(struct phy_device *phydev)
168*7eaf9132SAlexandru Ardelean {
169*7eaf9132SAlexandru Ardelean 	struct adin_priv *priv = phydev->priv;
170*7eaf9132SAlexandru Ardelean 	struct device *dev = &phydev->mdio.dev;
171*7eaf9132SAlexandru Ardelean 	int ret;
172*7eaf9132SAlexandru Ardelean 	u8 val;
173*7eaf9132SAlexandru Ardelean 
174*7eaf9132SAlexandru Ardelean 	ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10T1L_STAT);
175*7eaf9132SAlexandru Ardelean 	if (ret < 0)
176*7eaf9132SAlexandru Ardelean 		return ret;
177*7eaf9132SAlexandru Ardelean 
178*7eaf9132SAlexandru Ardelean 	/* This depends on the voltage level from the power source */
179*7eaf9132SAlexandru Ardelean 	priv->tx_level_2v4_able = !!(ret & MDIO_PMA_10T1L_STAT_2V4_ABLE);
180*7eaf9132SAlexandru Ardelean 
181*7eaf9132SAlexandru Ardelean 	phydev_dbg(phydev, "PHY supports 2.4V TX level: %s\n",
182*7eaf9132SAlexandru Ardelean 		   priv->tx_level_2v4_able ? "yes" : "no");
183*7eaf9132SAlexandru Ardelean 
184*7eaf9132SAlexandru Ardelean 	priv->tx_level_prop_present = device_property_present(dev, "phy-10base-t1l-2.4vpp");
185*7eaf9132SAlexandru Ardelean 	if (priv->tx_level_prop_present) {
186*7eaf9132SAlexandru Ardelean 		ret = device_property_read_u8(dev, "phy-10base-t1l-2.4vpp", &val);
187*7eaf9132SAlexandru Ardelean 		if (ret < 0)
188*7eaf9132SAlexandru Ardelean 			return ret;
189*7eaf9132SAlexandru Ardelean 
190*7eaf9132SAlexandru Ardelean 		priv->tx_level_2v4 = val;
191*7eaf9132SAlexandru Ardelean 		if (!priv->tx_level_2v4 && priv->tx_level_2v4_able)
192*7eaf9132SAlexandru Ardelean 			phydev_info(phydev,
193*7eaf9132SAlexandru Ardelean 				    "PHY supports 2.4V TX level, but disabled via config\n");
194*7eaf9132SAlexandru Ardelean 	}
195*7eaf9132SAlexandru Ardelean 
196*7eaf9132SAlexandru Ardelean 	linkmode_set_bit_array(phy_basic_ports_array, ARRAY_SIZE(phy_basic_ports_array),
197*7eaf9132SAlexandru Ardelean 			       phydev->supported);
198*7eaf9132SAlexandru Ardelean 
199*7eaf9132SAlexandru Ardelean 	return genphy_c45_pma_read_abilities(phydev);
200*7eaf9132SAlexandru Ardelean }
201*7eaf9132SAlexandru Ardelean 
202*7eaf9132SAlexandru Ardelean static int adin_probe(struct phy_device *phydev)
203*7eaf9132SAlexandru Ardelean {
204*7eaf9132SAlexandru Ardelean 	struct device *dev = &phydev->mdio.dev;
205*7eaf9132SAlexandru Ardelean 	struct adin_priv *priv;
206*7eaf9132SAlexandru Ardelean 
207*7eaf9132SAlexandru Ardelean 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
208*7eaf9132SAlexandru Ardelean 	if (!priv)
209*7eaf9132SAlexandru Ardelean 		return -ENOMEM;
210*7eaf9132SAlexandru Ardelean 
211*7eaf9132SAlexandru Ardelean 	phydev->priv = priv;
212*7eaf9132SAlexandru Ardelean 
213*7eaf9132SAlexandru Ardelean 	return 0;
214*7eaf9132SAlexandru Ardelean }
215*7eaf9132SAlexandru Ardelean 
216*7eaf9132SAlexandru Ardelean static struct phy_driver adin_driver[] = {
217*7eaf9132SAlexandru Ardelean 	{
218*7eaf9132SAlexandru Ardelean 		PHY_ID_MATCH_MODEL(PHY_ID_ADIN1100),
219*7eaf9132SAlexandru Ardelean 		.name			= "ADIN1100",
220*7eaf9132SAlexandru Ardelean 		.get_features		= adin_get_features,
221*7eaf9132SAlexandru Ardelean 		.soft_reset		= adin_soft_reset,
222*7eaf9132SAlexandru Ardelean 		.probe			= adin_probe,
223*7eaf9132SAlexandru Ardelean 		.config_aneg		= adin_config_aneg,
224*7eaf9132SAlexandru Ardelean 		.read_status		= adin_read_status,
225*7eaf9132SAlexandru Ardelean 		.set_loopback		= adin_set_loopback,
226*7eaf9132SAlexandru Ardelean 		.suspend		= adin_suspend,
227*7eaf9132SAlexandru Ardelean 		.resume			= adin_resume,
228*7eaf9132SAlexandru Ardelean 	},
229*7eaf9132SAlexandru Ardelean };
230*7eaf9132SAlexandru Ardelean 
231*7eaf9132SAlexandru Ardelean module_phy_driver(adin_driver);
232*7eaf9132SAlexandru Ardelean 
233*7eaf9132SAlexandru Ardelean static struct mdio_device_id __maybe_unused adin_tbl[] = {
234*7eaf9132SAlexandru Ardelean 	{ PHY_ID_MATCH_MODEL(PHY_ID_ADIN1100) },
235*7eaf9132SAlexandru Ardelean 	{ }
236*7eaf9132SAlexandru Ardelean };
237*7eaf9132SAlexandru Ardelean 
238*7eaf9132SAlexandru Ardelean MODULE_DEVICE_TABLE(mdio, adin_tbl);
239*7eaf9132SAlexandru Ardelean MODULE_DESCRIPTION("Analog Devices Industrial Ethernet T1L PHY driver");
240*7eaf9132SAlexandru Ardelean MODULE_LICENSE("Dual BSD/GPL");
241