xref: /linux/drivers/net/phy/intel-xway.c (revision 37744feebc086908fd89760650f458ab19071750)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2012 Daniel Schwierzeck <daniel.schwierzeck@googlemail.com>
4  * Copyright (C) 2016 Hauke Mehrtens <hauke@hauke-m.de>
5  */
6 
7 #include <linux/mdio.h>
8 #include <linux/module.h>
9 #include <linux/phy.h>
10 #include <linux/of.h>
11 
12 #define XWAY_MDIO_IMASK			0x19	/* interrupt mask */
13 #define XWAY_MDIO_ISTAT			0x1A	/* interrupt status */
14 
15 #define XWAY_MDIO_INIT_WOL		BIT(15)	/* Wake-On-LAN */
16 #define XWAY_MDIO_INIT_MSRE		BIT(14)
17 #define XWAY_MDIO_INIT_NPRX		BIT(13)
18 #define XWAY_MDIO_INIT_NPTX		BIT(12)
19 #define XWAY_MDIO_INIT_ANE		BIT(11)	/* Auto-Neg error */
20 #define XWAY_MDIO_INIT_ANC		BIT(10)	/* Auto-Neg complete */
21 #define XWAY_MDIO_INIT_ADSC		BIT(5)	/* Link auto-downspeed detect */
22 #define XWAY_MDIO_INIT_MPIPC		BIT(4)
23 #define XWAY_MDIO_INIT_MDIXC		BIT(3)
24 #define XWAY_MDIO_INIT_DXMC		BIT(2)	/* Duplex mode change */
25 #define XWAY_MDIO_INIT_LSPC		BIT(1)	/* Link speed change */
26 #define XWAY_MDIO_INIT_LSTC		BIT(0)	/* Link state change */
27 #define XWAY_MDIO_INIT_MASK		(XWAY_MDIO_INIT_LSTC | \
28 					 XWAY_MDIO_INIT_ADSC)
29 
30 #define ADVERTISED_MPD			BIT(10)	/* Multi-port device */
31 
32 /* LED Configuration */
33 #define XWAY_MMD_LEDCH			0x01E0
34 /* Inverse of SCAN Function */
35 #define  XWAY_MMD_LEDCH_NACS_NONE	0x0000
36 #define  XWAY_MMD_LEDCH_NACS_LINK	0x0001
37 #define  XWAY_MMD_LEDCH_NACS_PDOWN	0x0002
38 #define  XWAY_MMD_LEDCH_NACS_EEE	0x0003
39 #define  XWAY_MMD_LEDCH_NACS_ANEG	0x0004
40 #define  XWAY_MMD_LEDCH_NACS_ABIST	0x0005
41 #define  XWAY_MMD_LEDCH_NACS_CDIAG	0x0006
42 #define  XWAY_MMD_LEDCH_NACS_TEST	0x0007
43 /* Slow Blink Frequency */
44 #define  XWAY_MMD_LEDCH_SBF_F02HZ	0x0000
45 #define  XWAY_MMD_LEDCH_SBF_F04HZ	0x0010
46 #define  XWAY_MMD_LEDCH_SBF_F08HZ	0x0020
47 #define  XWAY_MMD_LEDCH_SBF_F16HZ	0x0030
48 /* Fast Blink Frequency */
49 #define  XWAY_MMD_LEDCH_FBF_F02HZ	0x0000
50 #define  XWAY_MMD_LEDCH_FBF_F04HZ	0x0040
51 #define  XWAY_MMD_LEDCH_FBF_F08HZ	0x0080
52 #define  XWAY_MMD_LEDCH_FBF_F16HZ	0x00C0
53 /* LED Configuration */
54 #define XWAY_MMD_LEDCL			0x01E1
55 /* Complex Blinking Configuration */
56 #define  XWAY_MMD_LEDCH_CBLINK_NONE	0x0000
57 #define  XWAY_MMD_LEDCH_CBLINK_LINK	0x0001
58 #define  XWAY_MMD_LEDCH_CBLINK_PDOWN	0x0002
59 #define  XWAY_MMD_LEDCH_CBLINK_EEE	0x0003
60 #define  XWAY_MMD_LEDCH_CBLINK_ANEG	0x0004
61 #define  XWAY_MMD_LEDCH_CBLINK_ABIST	0x0005
62 #define  XWAY_MMD_LEDCH_CBLINK_CDIAG	0x0006
63 #define  XWAY_MMD_LEDCH_CBLINK_TEST	0x0007
64 /* Complex SCAN Configuration */
65 #define  XWAY_MMD_LEDCH_SCAN_NONE	0x0000
66 #define  XWAY_MMD_LEDCH_SCAN_LINK	0x0010
67 #define  XWAY_MMD_LEDCH_SCAN_PDOWN	0x0020
68 #define  XWAY_MMD_LEDCH_SCAN_EEE	0x0030
69 #define  XWAY_MMD_LEDCH_SCAN_ANEG	0x0040
70 #define  XWAY_MMD_LEDCH_SCAN_ABIST	0x0050
71 #define  XWAY_MMD_LEDCH_SCAN_CDIAG	0x0060
72 #define  XWAY_MMD_LEDCH_SCAN_TEST	0x0070
73 /* Configuration for LED Pin x */
74 #define XWAY_MMD_LED0H			0x01E2
75 /* Fast Blinking Configuration */
76 #define  XWAY_MMD_LEDxH_BLINKF_MASK	0x000F
77 #define  XWAY_MMD_LEDxH_BLINKF_NONE	0x0000
78 #define  XWAY_MMD_LEDxH_BLINKF_LINK10	0x0001
79 #define  XWAY_MMD_LEDxH_BLINKF_LINK100	0x0002
80 #define  XWAY_MMD_LEDxH_BLINKF_LINK10X	0x0003
81 #define  XWAY_MMD_LEDxH_BLINKF_LINK1000	0x0004
82 #define  XWAY_MMD_LEDxH_BLINKF_LINK10_0	0x0005
83 #define  XWAY_MMD_LEDxH_BLINKF_LINK100X	0x0006
84 #define  XWAY_MMD_LEDxH_BLINKF_LINK10XX	0x0007
85 #define  XWAY_MMD_LEDxH_BLINKF_PDOWN	0x0008
86 #define  XWAY_MMD_LEDxH_BLINKF_EEE	0x0009
87 #define  XWAY_MMD_LEDxH_BLINKF_ANEG	0x000A
88 #define  XWAY_MMD_LEDxH_BLINKF_ABIST	0x000B
89 #define  XWAY_MMD_LEDxH_BLINKF_CDIAG	0x000C
90 /* Constant On Configuration */
91 #define  XWAY_MMD_LEDxH_CON_MASK	0x00F0
92 #define  XWAY_MMD_LEDxH_CON_NONE	0x0000
93 #define  XWAY_MMD_LEDxH_CON_LINK10	0x0010
94 #define  XWAY_MMD_LEDxH_CON_LINK100	0x0020
95 #define  XWAY_MMD_LEDxH_CON_LINK10X	0x0030
96 #define  XWAY_MMD_LEDxH_CON_LINK1000	0x0040
97 #define  XWAY_MMD_LEDxH_CON_LINK10_0	0x0050
98 #define  XWAY_MMD_LEDxH_CON_LINK100X	0x0060
99 #define  XWAY_MMD_LEDxH_CON_LINK10XX	0x0070
100 #define  XWAY_MMD_LEDxH_CON_PDOWN	0x0080
101 #define  XWAY_MMD_LEDxH_CON_EEE		0x0090
102 #define  XWAY_MMD_LEDxH_CON_ANEG	0x00A0
103 #define  XWAY_MMD_LEDxH_CON_ABIST	0x00B0
104 #define  XWAY_MMD_LEDxH_CON_CDIAG	0x00C0
105 #define  XWAY_MMD_LEDxH_CON_COPPER	0x00D0
106 #define  XWAY_MMD_LEDxH_CON_FIBER	0x00E0
107 /* Configuration for LED Pin x */
108 #define XWAY_MMD_LED0L			0x01E3
109 /* Pulsing Configuration */
110 #define  XWAY_MMD_LEDxL_PULSE_MASK	0x000F
111 #define  XWAY_MMD_LEDxL_PULSE_NONE	0x0000
112 #define  XWAY_MMD_LEDxL_PULSE_TXACT	0x0001
113 #define  XWAY_MMD_LEDxL_PULSE_RXACT	0x0002
114 #define  XWAY_MMD_LEDxL_PULSE_COL	0x0004
115 /* Slow Blinking Configuration */
116 #define  XWAY_MMD_LEDxL_BLINKS_MASK	0x00F0
117 #define  XWAY_MMD_LEDxL_BLINKS_NONE	0x0000
118 #define  XWAY_MMD_LEDxL_BLINKS_LINK10	0x0010
119 #define  XWAY_MMD_LEDxL_BLINKS_LINK100	0x0020
120 #define  XWAY_MMD_LEDxL_BLINKS_LINK10X	0x0030
121 #define  XWAY_MMD_LEDxL_BLINKS_LINK1000	0x0040
122 #define  XWAY_MMD_LEDxL_BLINKS_LINK10_0	0x0050
123 #define  XWAY_MMD_LEDxL_BLINKS_LINK100X	0x0060
124 #define  XWAY_MMD_LEDxL_BLINKS_LINK10XX	0x0070
125 #define  XWAY_MMD_LEDxL_BLINKS_PDOWN	0x0080
126 #define  XWAY_MMD_LEDxL_BLINKS_EEE	0x0090
127 #define  XWAY_MMD_LEDxL_BLINKS_ANEG	0x00A0
128 #define  XWAY_MMD_LEDxL_BLINKS_ABIST	0x00B0
129 #define  XWAY_MMD_LEDxL_BLINKS_CDIAG	0x00C0
130 #define XWAY_MMD_LED1H			0x01E4
131 #define XWAY_MMD_LED1L			0x01E5
132 #define XWAY_MMD_LED2H			0x01E6
133 #define XWAY_MMD_LED2L			0x01E7
134 #define XWAY_MMD_LED3H			0x01E8
135 #define XWAY_MMD_LED3L			0x01E9
136 
137 #define PHY_ID_PHY11G_1_3		0x030260D1
138 #define PHY_ID_PHY22F_1_3		0x030260E1
139 #define PHY_ID_PHY11G_1_4		0xD565A400
140 #define PHY_ID_PHY22F_1_4		0xD565A410
141 #define PHY_ID_PHY11G_1_5		0xD565A401
142 #define PHY_ID_PHY22F_1_5		0xD565A411
143 #define PHY_ID_PHY11G_VR9_1_1		0xD565A408
144 #define PHY_ID_PHY22F_VR9_1_1		0xD565A418
145 #define PHY_ID_PHY11G_VR9_1_2		0xD565A409
146 #define PHY_ID_PHY22F_VR9_1_2		0xD565A419
147 
148 static int xway_gphy_config_init(struct phy_device *phydev)
149 {
150 	int err;
151 	u32 ledxh;
152 	u32 ledxl;
153 
154 	/* Mask all interrupts */
155 	err = phy_write(phydev, XWAY_MDIO_IMASK, 0);
156 	if (err)
157 		return err;
158 
159 	/* Clear all pending interrupts */
160 	phy_read(phydev, XWAY_MDIO_ISTAT);
161 
162 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDCH,
163 		      XWAY_MMD_LEDCH_NACS_NONE |
164 		      XWAY_MMD_LEDCH_SBF_F02HZ |
165 		      XWAY_MMD_LEDCH_FBF_F16HZ);
166 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDCL,
167 		      XWAY_MMD_LEDCH_CBLINK_NONE |
168 		      XWAY_MMD_LEDCH_SCAN_NONE);
169 
170 	/**
171 	 * In most cases only one LED is connected to this phy, so
172 	 * configure them all to constant on and pulse mode. LED3 is
173 	 * only available in some packages, leave it in its reset
174 	 * configuration.
175 	 */
176 	ledxh = XWAY_MMD_LEDxH_BLINKF_NONE | XWAY_MMD_LEDxH_CON_LINK10XX;
177 	ledxl = XWAY_MMD_LEDxL_PULSE_TXACT | XWAY_MMD_LEDxL_PULSE_RXACT |
178 		XWAY_MMD_LEDxL_BLINKS_NONE;
179 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED0H, ledxh);
180 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED0L, ledxl);
181 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED1H, ledxh);
182 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED1L, ledxl);
183 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED2H, ledxh);
184 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED2L, ledxl);
185 
186 	return 0;
187 }
188 
189 static int xway_gphy14_config_aneg(struct phy_device *phydev)
190 {
191 	int reg, err;
192 
193 	/* Advertise as multi-port device, see IEEE802.3-2002 40.5.1.1 */
194 	/* This is a workaround for an errata in rev < 1.5 devices */
195 	reg = phy_read(phydev, MII_CTRL1000);
196 	reg |= ADVERTISED_MPD;
197 	err = phy_write(phydev, MII_CTRL1000, reg);
198 	if (err)
199 		return err;
200 
201 	return genphy_config_aneg(phydev);
202 }
203 
204 static int xway_gphy_ack_interrupt(struct phy_device *phydev)
205 {
206 	int reg;
207 
208 	reg = phy_read(phydev, XWAY_MDIO_ISTAT);
209 	return (reg < 0) ? reg : 0;
210 }
211 
212 static int xway_gphy_did_interrupt(struct phy_device *phydev)
213 {
214 	int reg;
215 
216 	reg = phy_read(phydev, XWAY_MDIO_ISTAT);
217 	return reg & XWAY_MDIO_INIT_MASK;
218 }
219 
220 static int xway_gphy_config_intr(struct phy_device *phydev)
221 {
222 	u16 mask = 0;
223 
224 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
225 		mask = XWAY_MDIO_INIT_MASK;
226 
227 	return phy_write(phydev, XWAY_MDIO_IMASK, mask);
228 }
229 
230 static struct phy_driver xway_gphy[] = {
231 	{
232 		.phy_id		= PHY_ID_PHY11G_1_3,
233 		.phy_id_mask	= 0xffffffff,
234 		.name		= "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.3",
235 		/* PHY_GBIT_FEATURES */
236 		.config_init	= xway_gphy_config_init,
237 		.config_aneg	= xway_gphy14_config_aneg,
238 		.ack_interrupt	= xway_gphy_ack_interrupt,
239 		.did_interrupt	= xway_gphy_did_interrupt,
240 		.config_intr	= xway_gphy_config_intr,
241 		.suspend	= genphy_suspend,
242 		.resume		= genphy_resume,
243 	}, {
244 		.phy_id		= PHY_ID_PHY22F_1_3,
245 		.phy_id_mask	= 0xffffffff,
246 		.name		= "Intel XWAY PHY22F (PEF 7061) v1.3",
247 		/* PHY_BASIC_FEATURES */
248 		.config_init	= xway_gphy_config_init,
249 		.config_aneg	= xway_gphy14_config_aneg,
250 		.ack_interrupt	= xway_gphy_ack_interrupt,
251 		.did_interrupt	= xway_gphy_did_interrupt,
252 		.config_intr	= xway_gphy_config_intr,
253 		.suspend	= genphy_suspend,
254 		.resume		= genphy_resume,
255 	}, {
256 		.phy_id		= PHY_ID_PHY11G_1_4,
257 		.phy_id_mask	= 0xffffffff,
258 		.name		= "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.4",
259 		/* PHY_GBIT_FEATURES */
260 		.config_init	= xway_gphy_config_init,
261 		.config_aneg	= xway_gphy14_config_aneg,
262 		.ack_interrupt	= xway_gphy_ack_interrupt,
263 		.did_interrupt	= xway_gphy_did_interrupt,
264 		.config_intr	= xway_gphy_config_intr,
265 		.suspend	= genphy_suspend,
266 		.resume		= genphy_resume,
267 	}, {
268 		.phy_id		= PHY_ID_PHY22F_1_4,
269 		.phy_id_mask	= 0xffffffff,
270 		.name		= "Intel XWAY PHY22F (PEF 7061) v1.4",
271 		/* PHY_BASIC_FEATURES */
272 		.config_init	= xway_gphy_config_init,
273 		.config_aneg	= xway_gphy14_config_aneg,
274 		.ack_interrupt	= xway_gphy_ack_interrupt,
275 		.did_interrupt	= xway_gphy_did_interrupt,
276 		.config_intr	= xway_gphy_config_intr,
277 		.suspend	= genphy_suspend,
278 		.resume		= genphy_resume,
279 	}, {
280 		.phy_id		= PHY_ID_PHY11G_1_5,
281 		.phy_id_mask	= 0xffffffff,
282 		.name		= "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.5 / v1.6",
283 		/* PHY_GBIT_FEATURES */
284 		.config_init	= xway_gphy_config_init,
285 		.ack_interrupt	= xway_gphy_ack_interrupt,
286 		.did_interrupt	= xway_gphy_did_interrupt,
287 		.config_intr	= xway_gphy_config_intr,
288 		.suspend	= genphy_suspend,
289 		.resume		= genphy_resume,
290 	}, {
291 		.phy_id		= PHY_ID_PHY22F_1_5,
292 		.phy_id_mask	= 0xffffffff,
293 		.name		= "Intel XWAY PHY22F (PEF 7061) v1.5 / v1.6",
294 		/* PHY_BASIC_FEATURES */
295 		.config_init	= xway_gphy_config_init,
296 		.ack_interrupt	= xway_gphy_ack_interrupt,
297 		.did_interrupt	= xway_gphy_did_interrupt,
298 		.config_intr	= xway_gphy_config_intr,
299 		.suspend	= genphy_suspend,
300 		.resume		= genphy_resume,
301 	}, {
302 		.phy_id		= PHY_ID_PHY11G_VR9_1_1,
303 		.phy_id_mask	= 0xffffffff,
304 		.name		= "Intel XWAY PHY11G (xRX v1.1 integrated)",
305 		/* PHY_GBIT_FEATURES */
306 		.config_init	= xway_gphy_config_init,
307 		.ack_interrupt	= xway_gphy_ack_interrupt,
308 		.did_interrupt	= xway_gphy_did_interrupt,
309 		.config_intr	= xway_gphy_config_intr,
310 		.suspend	= genphy_suspend,
311 		.resume		= genphy_resume,
312 	}, {
313 		.phy_id		= PHY_ID_PHY22F_VR9_1_1,
314 		.phy_id_mask	= 0xffffffff,
315 		.name		= "Intel XWAY PHY22F (xRX v1.1 integrated)",
316 		/* PHY_BASIC_FEATURES */
317 		.config_init	= xway_gphy_config_init,
318 		.ack_interrupt	= xway_gphy_ack_interrupt,
319 		.did_interrupt	= xway_gphy_did_interrupt,
320 		.config_intr	= xway_gphy_config_intr,
321 		.suspend	= genphy_suspend,
322 		.resume		= genphy_resume,
323 	}, {
324 		.phy_id		= PHY_ID_PHY11G_VR9_1_2,
325 		.phy_id_mask	= 0xffffffff,
326 		.name		= "Intel XWAY PHY11G (xRX v1.2 integrated)",
327 		/* PHY_GBIT_FEATURES */
328 		.config_init	= xway_gphy_config_init,
329 		.ack_interrupt	= xway_gphy_ack_interrupt,
330 		.did_interrupt	= xway_gphy_did_interrupt,
331 		.config_intr	= xway_gphy_config_intr,
332 		.suspend	= genphy_suspend,
333 		.resume		= genphy_resume,
334 	}, {
335 		.phy_id		= PHY_ID_PHY22F_VR9_1_2,
336 		.phy_id_mask	= 0xffffffff,
337 		.name		= "Intel XWAY PHY22F (xRX v1.2 integrated)",
338 		/* PHY_BASIC_FEATURES */
339 		.config_init	= xway_gphy_config_init,
340 		.ack_interrupt	= xway_gphy_ack_interrupt,
341 		.did_interrupt	= xway_gphy_did_interrupt,
342 		.config_intr	= xway_gphy_config_intr,
343 		.suspend	= genphy_suspend,
344 		.resume		= genphy_resume,
345 	},
346 };
347 module_phy_driver(xway_gphy);
348 
349 static struct mdio_device_id __maybe_unused xway_gphy_tbl[] = {
350 	{ PHY_ID_PHY11G_1_3, 0xffffffff },
351 	{ PHY_ID_PHY22F_1_3, 0xffffffff },
352 	{ PHY_ID_PHY11G_1_4, 0xffffffff },
353 	{ PHY_ID_PHY22F_1_4, 0xffffffff },
354 	{ PHY_ID_PHY11G_1_5, 0xffffffff },
355 	{ PHY_ID_PHY22F_1_5, 0xffffffff },
356 	{ PHY_ID_PHY11G_VR9_1_1, 0xffffffff },
357 	{ PHY_ID_PHY22F_VR9_1_1, 0xffffffff },
358 	{ PHY_ID_PHY11G_VR9_1_2, 0xffffffff },
359 	{ PHY_ID_PHY22F_VR9_1_2, 0xffffffff },
360 	{ }
361 };
362 MODULE_DEVICE_TABLE(mdio, xway_gphy_tbl);
363 
364 MODULE_DESCRIPTION("Intel XWAY PHY driver");
365 MODULE_LICENSE("GPL");
366