xref: /linux/drivers/net/phy/intel-xway.c (revision ec8a42e7343234802b9054874fe01810880289ce)
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_config_intr(struct phy_device *phydev)
213 {
214 	u16 mask = 0;
215 	int err;
216 
217 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
218 		err = xway_gphy_ack_interrupt(phydev);
219 		if (err)
220 			return err;
221 
222 		mask = XWAY_MDIO_INIT_MASK;
223 		err = phy_write(phydev, XWAY_MDIO_IMASK, mask);
224 	} else {
225 		err = phy_write(phydev, XWAY_MDIO_IMASK, mask);
226 		if (err)
227 			return err;
228 
229 		err = xway_gphy_ack_interrupt(phydev);
230 	}
231 
232 	return err;
233 }
234 
235 static irqreturn_t xway_gphy_handle_interrupt(struct phy_device *phydev)
236 {
237 	int irq_status;
238 
239 	irq_status = phy_read(phydev, XWAY_MDIO_ISTAT);
240 	if (irq_status < 0) {
241 		phy_error(phydev);
242 		return IRQ_NONE;
243 	}
244 
245 	if (!(irq_status & XWAY_MDIO_INIT_MASK))
246 		return IRQ_NONE;
247 
248 	phy_trigger_machine(phydev);
249 
250 	return IRQ_HANDLED;
251 }
252 
253 static struct phy_driver xway_gphy[] = {
254 	{
255 		.phy_id		= PHY_ID_PHY11G_1_3,
256 		.phy_id_mask	= 0xffffffff,
257 		.name		= "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.3",
258 		/* PHY_GBIT_FEATURES */
259 		.config_init	= xway_gphy_config_init,
260 		.config_aneg	= xway_gphy14_config_aneg,
261 		.handle_interrupt = xway_gphy_handle_interrupt,
262 		.config_intr	= xway_gphy_config_intr,
263 		.suspend	= genphy_suspend,
264 		.resume		= genphy_resume,
265 	}, {
266 		.phy_id		= PHY_ID_PHY22F_1_3,
267 		.phy_id_mask	= 0xffffffff,
268 		.name		= "Intel XWAY PHY22F (PEF 7061) v1.3",
269 		/* PHY_BASIC_FEATURES */
270 		.config_init	= xway_gphy_config_init,
271 		.config_aneg	= xway_gphy14_config_aneg,
272 		.handle_interrupt = xway_gphy_handle_interrupt,
273 		.config_intr	= xway_gphy_config_intr,
274 		.suspend	= genphy_suspend,
275 		.resume		= genphy_resume,
276 	}, {
277 		.phy_id		= PHY_ID_PHY11G_1_4,
278 		.phy_id_mask	= 0xffffffff,
279 		.name		= "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.4",
280 		/* PHY_GBIT_FEATURES */
281 		.config_init	= xway_gphy_config_init,
282 		.config_aneg	= xway_gphy14_config_aneg,
283 		.handle_interrupt = xway_gphy_handle_interrupt,
284 		.config_intr	= xway_gphy_config_intr,
285 		.suspend	= genphy_suspend,
286 		.resume		= genphy_resume,
287 	}, {
288 		.phy_id		= PHY_ID_PHY22F_1_4,
289 		.phy_id_mask	= 0xffffffff,
290 		.name		= "Intel XWAY PHY22F (PEF 7061) v1.4",
291 		/* PHY_BASIC_FEATURES */
292 		.config_init	= xway_gphy_config_init,
293 		.config_aneg	= xway_gphy14_config_aneg,
294 		.handle_interrupt = xway_gphy_handle_interrupt,
295 		.config_intr	= xway_gphy_config_intr,
296 		.suspend	= genphy_suspend,
297 		.resume		= genphy_resume,
298 	}, {
299 		.phy_id		= PHY_ID_PHY11G_1_5,
300 		.phy_id_mask	= 0xffffffff,
301 		.name		= "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.5 / v1.6",
302 		/* PHY_GBIT_FEATURES */
303 		.config_init	= xway_gphy_config_init,
304 		.handle_interrupt = xway_gphy_handle_interrupt,
305 		.config_intr	= xway_gphy_config_intr,
306 		.suspend	= genphy_suspend,
307 		.resume		= genphy_resume,
308 	}, {
309 		.phy_id		= PHY_ID_PHY22F_1_5,
310 		.phy_id_mask	= 0xffffffff,
311 		.name		= "Intel XWAY PHY22F (PEF 7061) v1.5 / v1.6",
312 		/* PHY_BASIC_FEATURES */
313 		.config_init	= xway_gphy_config_init,
314 		.handle_interrupt = xway_gphy_handle_interrupt,
315 		.config_intr	= xway_gphy_config_intr,
316 		.suspend	= genphy_suspend,
317 		.resume		= genphy_resume,
318 	}, {
319 		.phy_id		= PHY_ID_PHY11G_VR9_1_1,
320 		.phy_id_mask	= 0xffffffff,
321 		.name		= "Intel XWAY PHY11G (xRX v1.1 integrated)",
322 		/* PHY_GBIT_FEATURES */
323 		.config_init	= xway_gphy_config_init,
324 		.handle_interrupt = xway_gphy_handle_interrupt,
325 		.config_intr	= xway_gphy_config_intr,
326 		.suspend	= genphy_suspend,
327 		.resume		= genphy_resume,
328 	}, {
329 		.phy_id		= PHY_ID_PHY22F_VR9_1_1,
330 		.phy_id_mask	= 0xffffffff,
331 		.name		= "Intel XWAY PHY22F (xRX v1.1 integrated)",
332 		/* PHY_BASIC_FEATURES */
333 		.config_init	= xway_gphy_config_init,
334 		.handle_interrupt = xway_gphy_handle_interrupt,
335 		.config_intr	= xway_gphy_config_intr,
336 		.suspend	= genphy_suspend,
337 		.resume		= genphy_resume,
338 	}, {
339 		.phy_id		= PHY_ID_PHY11G_VR9_1_2,
340 		.phy_id_mask	= 0xffffffff,
341 		.name		= "Intel XWAY PHY11G (xRX v1.2 integrated)",
342 		/* PHY_GBIT_FEATURES */
343 		.config_init	= xway_gphy_config_init,
344 		.handle_interrupt = xway_gphy_handle_interrupt,
345 		.config_intr	= xway_gphy_config_intr,
346 		.suspend	= genphy_suspend,
347 		.resume		= genphy_resume,
348 	}, {
349 		.phy_id		= PHY_ID_PHY22F_VR9_1_2,
350 		.phy_id_mask	= 0xffffffff,
351 		.name		= "Intel XWAY PHY22F (xRX v1.2 integrated)",
352 		/* PHY_BASIC_FEATURES */
353 		.config_init	= xway_gphy_config_init,
354 		.handle_interrupt = xway_gphy_handle_interrupt,
355 		.config_intr	= xway_gphy_config_intr,
356 		.suspend	= genphy_suspend,
357 		.resume		= genphy_resume,
358 	},
359 };
360 module_phy_driver(xway_gphy);
361 
362 static struct mdio_device_id __maybe_unused xway_gphy_tbl[] = {
363 	{ PHY_ID_PHY11G_1_3, 0xffffffff },
364 	{ PHY_ID_PHY22F_1_3, 0xffffffff },
365 	{ PHY_ID_PHY11G_1_4, 0xffffffff },
366 	{ PHY_ID_PHY22F_1_4, 0xffffffff },
367 	{ PHY_ID_PHY11G_1_5, 0xffffffff },
368 	{ PHY_ID_PHY22F_1_5, 0xffffffff },
369 	{ PHY_ID_PHY11G_VR9_1_1, 0xffffffff },
370 	{ PHY_ID_PHY22F_VR9_1_1, 0xffffffff },
371 	{ PHY_ID_PHY11G_VR9_1_2, 0xffffffff },
372 	{ PHY_ID_PHY22F_VR9_1_2, 0xffffffff },
373 	{ }
374 };
375 MODULE_DEVICE_TABLE(mdio, xway_gphy_tbl);
376 
377 MODULE_DESCRIPTION("Intel XWAY PHY driver");
378 MODULE_LICENSE("GPL");
379