1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Marvell 88Q2XXX automotive 100BASE-T1/1000BASE-T1 PHY driver
4  */
5 #include <linux/ethtool_netlink.h>
6 #include <linux/marvell_phy.h>
7 #include <linux/phy.h>
8 
9 #define MDIO_MMD_AN_MV_STAT			32769
10 #define MDIO_MMD_AN_MV_STAT_ANEG		0x0100
11 #define MDIO_MMD_AN_MV_STAT_LOCAL_RX		0x1000
12 #define MDIO_MMD_AN_MV_STAT_REMOTE_RX		0x2000
13 #define MDIO_MMD_AN_MV_STAT_LOCAL_MASTER	0x4000
14 #define MDIO_MMD_AN_MV_STAT_MS_CONF_FAULT	0x8000
15 
16 #define MDIO_MMD_PCS_MV_100BT1_STAT1			33032
17 #define MDIO_MMD_PCS_MV_100BT1_STAT1_IDLE_ERROR	0x00FF
18 #define MDIO_MMD_PCS_MV_100BT1_STAT1_JABBER		0x0100
19 #define MDIO_MMD_PCS_MV_100BT1_STAT1_LINK		0x0200
20 #define MDIO_MMD_PCS_MV_100BT1_STAT1_LOCAL_RX		0x1000
21 #define MDIO_MMD_PCS_MV_100BT1_STAT1_REMOTE_RX		0x2000
22 #define MDIO_MMD_PCS_MV_100BT1_STAT1_LOCAL_MASTER	0x4000
23 
24 #define MDIO_MMD_PCS_MV_100BT1_STAT2		33033
25 #define MDIO_MMD_PCS_MV_100BT1_STAT2_JABBER	0x0001
26 #define MDIO_MMD_PCS_MV_100BT1_STAT2_POL	0x0002
27 #define MDIO_MMD_PCS_MV_100BT1_STAT2_LINK	0x0004
28 #define MDIO_MMD_PCS_MV_100BT1_STAT2_ANGE	0x0008
29 
mv88q2xxx_soft_reset(struct phy_device * phydev)30 static int mv88q2xxx_soft_reset(struct phy_device *phydev)
31 {
32 	int ret;
33 	int val;
34 
35 	ret = phy_write_mmd(phydev, MDIO_MMD_PCS,
36 			    MDIO_PCS_1000BT1_CTRL, MDIO_PCS_1000BT1_CTRL_RESET);
37 	if (ret < 0)
38 		return ret;
39 
40 	return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_PCS,
41 					 MDIO_PCS_1000BT1_CTRL, val,
42 					 !(val & MDIO_PCS_1000BT1_CTRL_RESET),
43 					 50000, 600000, true);
44 }
45 
mv88q2xxx_read_link_gbit(struct phy_device * phydev)46 static int mv88q2xxx_read_link_gbit(struct phy_device *phydev)
47 {
48 	int ret;
49 	bool link = false;
50 
51 	/* Read vendor specific Auto-Negotiation status register to get local
52 	 * and remote receiver status according to software initialization
53 	 * guide.
54 	 */
55 	ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_MMD_AN_MV_STAT);
56 	if (ret < 0) {
57 		return ret;
58 	} else if ((ret & MDIO_MMD_AN_MV_STAT_LOCAL_RX) &&
59 		   (ret & MDIO_MMD_AN_MV_STAT_REMOTE_RX)) {
60 		/* The link state is latched low so that momentary link
61 		 * drops can be detected. Do not double-read the status
62 		 * in polling mode to detect such short link drops except
63 		 * the link was already down.
64 		 */
65 		if (!phy_polling_mode(phydev) || !phydev->link) {
66 			ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_1000BT1_STAT);
67 			if (ret < 0)
68 				return ret;
69 			else if (ret & MDIO_PCS_1000BT1_STAT_LINK)
70 				link = true;
71 		}
72 
73 		if (!link) {
74 			ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_1000BT1_STAT);
75 			if (ret < 0)
76 				return ret;
77 			else if (ret & MDIO_PCS_1000BT1_STAT_LINK)
78 				link = true;
79 		}
80 	}
81 
82 	phydev->link = link;
83 
84 	return 0;
85 }
86 
mv88q2xxx_read_link_100m(struct phy_device * phydev)87 static int mv88q2xxx_read_link_100m(struct phy_device *phydev)
88 {
89 	int ret;
90 
91 	/* The link state is latched low so that momentary link
92 	 * drops can be detected. Do not double-read the status
93 	 * in polling mode to detect such short link drops except
94 	 * the link was already down. In case we are not polling,
95 	 * we always read the realtime status.
96 	 */
97 	if (!phy_polling_mode(phydev) || !phydev->link) {
98 		ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_MMD_PCS_MV_100BT1_STAT1);
99 		if (ret < 0)
100 			return ret;
101 		else if (ret & MDIO_MMD_PCS_MV_100BT1_STAT1_LINK)
102 			goto out;
103 	}
104 
105 	ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_MMD_PCS_MV_100BT1_STAT1);
106 	if (ret < 0)
107 		return ret;
108 
109 out:
110 	/* Check if we have link and if the remote and local receiver are ok */
111 	if ((ret & MDIO_MMD_PCS_MV_100BT1_STAT1_LINK) &&
112 	    (ret & MDIO_MMD_PCS_MV_100BT1_STAT1_LOCAL_RX) &&
113 	    (ret & MDIO_MMD_PCS_MV_100BT1_STAT1_REMOTE_RX))
114 		phydev->link = true;
115 	else
116 		phydev->link = false;
117 
118 	return 0;
119 }
120 
mv88q2xxx_read_link(struct phy_device * phydev)121 static int mv88q2xxx_read_link(struct phy_device *phydev)
122 {
123 	int ret;
124 
125 	/* The 88Q2XXX PHYs do not have the PMA/PMD status register available,
126 	 * therefore we need to read the link status from the vendor specific
127 	 * registers depending on the speed.
128 	 */
129 	if (phydev->speed == SPEED_1000)
130 		ret = mv88q2xxx_read_link_gbit(phydev);
131 	else
132 		ret = mv88q2xxx_read_link_100m(phydev);
133 
134 	return ret;
135 }
136 
mv88q2xxx_read_status(struct phy_device * phydev)137 static int mv88q2xxx_read_status(struct phy_device *phydev)
138 {
139 	int ret;
140 
141 	ret = mv88q2xxx_read_link(phydev);
142 	if (ret < 0)
143 		return ret;
144 
145 	return genphy_c45_read_pma(phydev);
146 }
147 
mv88q2xxx_get_features(struct phy_device * phydev)148 static int mv88q2xxx_get_features(struct phy_device *phydev)
149 {
150 	int ret;
151 
152 	ret = genphy_c45_pma_read_abilities(phydev);
153 	if (ret)
154 		return ret;
155 
156 	/* We need to read the baset1 extended abilities manually because the
157 	 * PHY does not signalize it has the extended abilities register
158 	 * available.
159 	 */
160 	ret = genphy_c45_pma_baset1_read_abilities(phydev);
161 	if (ret)
162 		return ret;
163 
164 	/* The PHY signalizes it supports autonegotiation. Unfortunately, so
165 	 * far it was not possible to get a link even when following the init
166 	 * sequence provided by Marvell. Disable it for now until a proper
167 	 * workaround is found or a new PHY revision is released.
168 	 */
169 	linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
170 
171 	return 0;
172 }
173 
mv88q2xxx_config_aneg(struct phy_device * phydev)174 static int mv88q2xxx_config_aneg(struct phy_device *phydev)
175 {
176 	int ret;
177 
178 	ret = genphy_c45_config_aneg(phydev);
179 	if (ret)
180 		return ret;
181 
182 	return mv88q2xxx_soft_reset(phydev);
183 }
184 
mv88q2xxx_config_init(struct phy_device * phydev)185 static int mv88q2xxx_config_init(struct phy_device *phydev)
186 {
187 	int ret;
188 
189 	/* The 88Q2XXX PHYs do have the extended ability register available, but
190 	 * register MDIO_PMA_EXTABLE where they should signalize it does not
191 	 * work according to specification. Therefore, we force it here.
192 	 */
193 	phydev->pma_extable = MDIO_PMA_EXTABLE_BT1;
194 
195 	/* Read the current PHY configuration */
196 	ret = genphy_c45_read_pma(phydev);
197 	if (ret)
198 		return ret;
199 
200 	return mv88q2xxx_config_aneg(phydev);
201 }
202 
mv88q2xxxx_get_sqi(struct phy_device * phydev)203 static int mv88q2xxxx_get_sqi(struct phy_device *phydev)
204 {
205 	int ret;
206 
207 	if (phydev->speed == SPEED_100) {
208 		/* Read the SQI from the vendor specific receiver status
209 		 * register
210 		 */
211 		ret = phy_read_mmd(phydev, MDIO_MMD_PCS, 0x8230);
212 		if (ret < 0)
213 			return ret;
214 
215 		ret = ret >> 12;
216 	} else {
217 		/* Read from vendor specific registers, they are not documented
218 		 * but can be found in the Software Initialization Guide. Only
219 		 * revisions >= A0 are supported.
220 		 */
221 		ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, 0xFC5D, 0x00FF, 0x00AC);
222 		if (ret < 0)
223 			return ret;
224 
225 		ret = phy_read_mmd(phydev, MDIO_MMD_PCS, 0xfc88);
226 		if (ret < 0)
227 			return ret;
228 	}
229 
230 	return ret & 0x0F;
231 }
232 
mv88q2xxxx_get_sqi_max(struct phy_device * phydev)233 static int mv88q2xxxx_get_sqi_max(struct phy_device *phydev)
234 {
235 	return 15;
236 }
237 
238 static struct phy_driver mv88q2xxx_driver[] = {
239 	{
240 		.phy_id			= MARVELL_PHY_ID_88Q2110,
241 		.phy_id_mask		= MARVELL_PHY_ID_MASK,
242 		.name			= "mv88q2110",
243 		.get_features		= mv88q2xxx_get_features,
244 		.config_aneg		= mv88q2xxx_config_aneg,
245 		.config_init		= mv88q2xxx_config_init,
246 		.read_status		= mv88q2xxx_read_status,
247 		.soft_reset		= mv88q2xxx_soft_reset,
248 		.set_loopback		= genphy_c45_loopback,
249 		.get_sqi		= mv88q2xxxx_get_sqi,
250 		.get_sqi_max		= mv88q2xxxx_get_sqi_max,
251 	},
252 };
253 
254 module_phy_driver(mv88q2xxx_driver);
255 
256 static struct mdio_device_id __maybe_unused mv88q2xxx_tbl[] = {
257 	{ MARVELL_PHY_ID_88Q2110, MARVELL_PHY_ID_MASK },
258 	{ /*sentinel*/ }
259 };
260 MODULE_DEVICE_TABLE(mdio, mv88q2xxx_tbl);
261 
262 MODULE_DESCRIPTION("Marvell 88Q2XXX 100/1000BASE-T1 Automotive Ethernet PHY driver");
263 MODULE_LICENSE("GPL");
264