xref: /linux/drivers/net/phy/intel-xway.c (revision 4b99990cdf9560e8a071640baf19f312e6ae02f4)
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 #include <linux/bitfield.h>
12 
13 #define XWAY_MDIO_ERRCNT		0x15	/* error counter */
14 #define XWAY_MDIO_MIICTRL		0x17	/* mii control */
15 #define XWAY_MDIO_IMASK			0x19	/* interrupt mask */
16 #define XWAY_MDIO_ISTAT			0x1A	/* interrupt status */
17 #define XWAY_MDIO_LED			0x1B	/* led control */
18 
19 #define XWAY_MDIO_ERRCNT_SEL		GENMASK(11, 8)
20 #define XWAY_MDIO_ERRCNT_COUNT		GENMASK(7, 0)
21 #define XWAY_MDIO_ERRCNT_SEL_RXERR	0
22 
23 #define XWAY_MDIO_MIICTRL_RXSKEW_MASK	GENMASK(14, 12)
24 #define XWAY_MDIO_MIICTRL_TXSKEW_MASK	GENMASK(10, 8)
25 
26 /* bit 15:12 are reserved */
27 #define XWAY_MDIO_LED_LED3_EN		BIT(11)	/* Enable the integrated function of LED3 */
28 #define XWAY_MDIO_LED_LED2_EN		BIT(10)	/* Enable the integrated function of LED2 */
29 #define XWAY_MDIO_LED_LED1_EN		BIT(9)	/* Enable the integrated function of LED1 */
30 #define XWAY_MDIO_LED_LED0_EN		BIT(8)	/* Enable the integrated function of LED0 */
31 /* bit 7:4 are reserved */
32 #define XWAY_MDIO_LED_LED3_DA		BIT(3)	/* Direct Access to LED3 */
33 #define XWAY_MDIO_LED_LED2_DA		BIT(2)	/* Direct Access to LED2 */
34 #define XWAY_MDIO_LED_LED1_DA		BIT(1)	/* Direct Access to LED1 */
35 #define XWAY_MDIO_LED_LED0_DA		BIT(0)	/* Direct Access to LED0 */
36 
37 #define XWAY_MDIO_INIT_WOL		BIT(15)	/* Wake-On-LAN */
38 #define XWAY_MDIO_INIT_MSRE		BIT(14)
39 #define XWAY_MDIO_INIT_NPRX		BIT(13)
40 #define XWAY_MDIO_INIT_NPTX		BIT(12)
41 #define XWAY_MDIO_INIT_ANE		BIT(11)	/* Auto-Neg error */
42 #define XWAY_MDIO_INIT_ANC		BIT(10)	/* Auto-Neg complete */
43 #define XWAY_MDIO_INIT_ADSC		BIT(5)	/* Link auto-downspeed detect */
44 #define XWAY_MDIO_INIT_MPIPC		BIT(4)
45 #define XWAY_MDIO_INIT_MDIXC		BIT(3)
46 #define XWAY_MDIO_INIT_DXMC		BIT(2)	/* Duplex mode change */
47 #define XWAY_MDIO_INIT_LSPC		BIT(1)	/* Link speed change */
48 #define XWAY_MDIO_INIT_LSTC		BIT(0)	/* Link state change */
49 #define XWAY_MDIO_INIT_MASK		(XWAY_MDIO_INIT_LSTC | \
50 					 XWAY_MDIO_INIT_ADSC)
51 
52 #define ADVERTISED_MPD			BIT(10)	/* Multi-port device */
53 
54 /* LED Configuration */
55 #define XWAY_MMD_LEDCH			0x01E0
56 /* Inverse of SCAN Function */
57 #define  XWAY_MMD_LEDCH_NACS_NONE	0x0000
58 #define  XWAY_MMD_LEDCH_NACS_LINK	0x0001
59 #define  XWAY_MMD_LEDCH_NACS_PDOWN	0x0002
60 #define  XWAY_MMD_LEDCH_NACS_EEE	0x0003
61 #define  XWAY_MMD_LEDCH_NACS_ANEG	0x0004
62 #define  XWAY_MMD_LEDCH_NACS_ABIST	0x0005
63 #define  XWAY_MMD_LEDCH_NACS_CDIAG	0x0006
64 #define  XWAY_MMD_LEDCH_NACS_TEST	0x0007
65 /* Slow Blink Frequency */
66 #define  XWAY_MMD_LEDCH_SBF_F02HZ	0x0000
67 #define  XWAY_MMD_LEDCH_SBF_F04HZ	0x0010
68 #define  XWAY_MMD_LEDCH_SBF_F08HZ	0x0020
69 #define  XWAY_MMD_LEDCH_SBF_F16HZ	0x0030
70 /* Fast Blink Frequency */
71 #define  XWAY_MMD_LEDCH_FBF_F02HZ	0x0000
72 #define  XWAY_MMD_LEDCH_FBF_F04HZ	0x0040
73 #define  XWAY_MMD_LEDCH_FBF_F08HZ	0x0080
74 #define  XWAY_MMD_LEDCH_FBF_F16HZ	0x00C0
75 /* LED Configuration */
76 #define XWAY_MMD_LEDCL			0x01E1
77 /* Complex Blinking Configuration */
78 #define  XWAY_MMD_LEDCH_CBLINK_NONE	0x0000
79 #define  XWAY_MMD_LEDCH_CBLINK_LINK	0x0001
80 #define  XWAY_MMD_LEDCH_CBLINK_PDOWN	0x0002
81 #define  XWAY_MMD_LEDCH_CBLINK_EEE	0x0003
82 #define  XWAY_MMD_LEDCH_CBLINK_ANEG	0x0004
83 #define  XWAY_MMD_LEDCH_CBLINK_ABIST	0x0005
84 #define  XWAY_MMD_LEDCH_CBLINK_CDIAG	0x0006
85 #define  XWAY_MMD_LEDCH_CBLINK_TEST	0x0007
86 /* Complex SCAN Configuration */
87 #define  XWAY_MMD_LEDCH_SCAN_NONE	0x0000
88 #define  XWAY_MMD_LEDCH_SCAN_LINK	0x0010
89 #define  XWAY_MMD_LEDCH_SCAN_PDOWN	0x0020
90 #define  XWAY_MMD_LEDCH_SCAN_EEE	0x0030
91 #define  XWAY_MMD_LEDCH_SCAN_ANEG	0x0040
92 #define  XWAY_MMD_LEDCH_SCAN_ABIST	0x0050
93 #define  XWAY_MMD_LEDCH_SCAN_CDIAG	0x0060
94 #define  XWAY_MMD_LEDCH_SCAN_TEST	0x0070
95 /* Configuration for LED Pin x */
96 #define XWAY_MMD_LED0H			0x01E2
97 /* Fast Blinking Configuration */
98 #define  XWAY_MMD_LEDxH_BLINKF_MASK	0x000F
99 #define  XWAY_MMD_LEDxH_BLINKF_NONE	0x0000
100 #define  XWAY_MMD_LEDxH_BLINKF_LINK10	0x0001
101 #define  XWAY_MMD_LEDxH_BLINKF_LINK100	0x0002
102 #define  XWAY_MMD_LEDxH_BLINKF_LINK10X	0x0003
103 #define  XWAY_MMD_LEDxH_BLINKF_LINK1000	0x0004
104 #define  XWAY_MMD_LEDxH_BLINKF_LINK10_0	0x0005
105 #define  XWAY_MMD_LEDxH_BLINKF_LINK100X	0x0006
106 #define  XWAY_MMD_LEDxH_BLINKF_LINK10XX	0x0007
107 #define  XWAY_MMD_LEDxH_BLINKF_PDOWN	0x0008
108 #define  XWAY_MMD_LEDxH_BLINKF_EEE	0x0009
109 #define  XWAY_MMD_LEDxH_BLINKF_ANEG	0x000A
110 #define  XWAY_MMD_LEDxH_BLINKF_ABIST	0x000B
111 #define  XWAY_MMD_LEDxH_BLINKF_CDIAG	0x000C
112 /* Constant On Configuration */
113 #define  XWAY_MMD_LEDxH_CON_MASK	0x00F0
114 #define  XWAY_MMD_LEDxH_CON_NONE	0x0000
115 #define  XWAY_MMD_LEDxH_CON_LINK10	0x0010
116 #define  XWAY_MMD_LEDxH_CON_LINK100	0x0020
117 #define  XWAY_MMD_LEDxH_CON_LINK10X	0x0030
118 #define  XWAY_MMD_LEDxH_CON_LINK1000	0x0040
119 #define  XWAY_MMD_LEDxH_CON_LINK10_0	0x0050
120 #define  XWAY_MMD_LEDxH_CON_LINK100X	0x0060
121 #define  XWAY_MMD_LEDxH_CON_LINK10XX	0x0070
122 #define  XWAY_MMD_LEDxH_CON_PDOWN	0x0080
123 #define  XWAY_MMD_LEDxH_CON_EEE		0x0090
124 #define  XWAY_MMD_LEDxH_CON_ANEG	0x00A0
125 #define  XWAY_MMD_LEDxH_CON_ABIST	0x00B0
126 #define  XWAY_MMD_LEDxH_CON_CDIAG	0x00C0
127 #define  XWAY_MMD_LEDxH_CON_COPPER	0x00D0
128 #define  XWAY_MMD_LEDxH_CON_FIBER	0x00E0
129 /* Configuration for LED Pin x */
130 #define XWAY_MMD_LED0L			0x01E3
131 /* Pulsing Configuration */
132 #define  XWAY_MMD_LEDxL_PULSE_MASK	0x000F
133 #define  XWAY_MMD_LEDxL_PULSE_NONE	0x0000
134 #define  XWAY_MMD_LEDxL_PULSE_TXACT	0x0001
135 #define  XWAY_MMD_LEDxL_PULSE_RXACT	0x0002
136 #define  XWAY_MMD_LEDxL_PULSE_COL	0x0004
137 /* Slow Blinking Configuration */
138 #define  XWAY_MMD_LEDxL_BLINKS_MASK	0x00F0
139 #define  XWAY_MMD_LEDxL_BLINKS_NONE	0x0000
140 #define  XWAY_MMD_LEDxL_BLINKS_LINK10	0x0010
141 #define  XWAY_MMD_LEDxL_BLINKS_LINK100	0x0020
142 #define  XWAY_MMD_LEDxL_BLINKS_LINK10X	0x0030
143 #define  XWAY_MMD_LEDxL_BLINKS_LINK1000	0x0040
144 #define  XWAY_MMD_LEDxL_BLINKS_LINK10_0	0x0050
145 #define  XWAY_MMD_LEDxL_BLINKS_LINK100X	0x0060
146 #define  XWAY_MMD_LEDxL_BLINKS_LINK10XX	0x0070
147 #define  XWAY_MMD_LEDxL_BLINKS_PDOWN	0x0080
148 #define  XWAY_MMD_LEDxL_BLINKS_EEE	0x0090
149 #define  XWAY_MMD_LEDxL_BLINKS_ANEG	0x00A0
150 #define  XWAY_MMD_LEDxL_BLINKS_ABIST	0x00B0
151 #define  XWAY_MMD_LEDxL_BLINKS_CDIAG	0x00C0
152 #define XWAY_MMD_LED1H			0x01E4
153 #define XWAY_MMD_LED1L			0x01E5
154 #define XWAY_MMD_LED2H			0x01E6
155 #define XWAY_MMD_LED2L			0x01E7
156 #define XWAY_MMD_LED3H			0x01E8
157 #define XWAY_MMD_LED3L			0x01E9
158 
159 #define XWAY_GPHY_MAX_LEDS		3
160 #define XWAY_GPHY_LED_INV(idx)		BIT(12 + (idx))
161 #define XWAY_GPHY_LED_EN(idx)		BIT(8 + (idx))
162 #define XWAY_GPHY_LED_DA(idx)		BIT(idx)
163 #define XWAY_MMD_LEDxH(idx)		(XWAY_MMD_LED0H + 2 * (idx))
164 #define XWAY_MMD_LEDxL(idx)		(XWAY_MMD_LED0L + 2 * (idx))
165 
166 #define PHY_ID_PHY11G_1_3		0x030260D1
167 #define PHY_ID_PHY22F_1_3		0x030260E1
168 #define PHY_ID_PHY11G_1_4		0xD565A400
169 #define PHY_ID_PHY22F_1_4		0xD565A410
170 #define PHY_ID_PHY11G_1_5		0xD565A401
171 #define PHY_ID_PHY22F_1_5		0xD565A411
172 #define PHY_ID_PHY11G_VR9_1_1		0xD565A408
173 #define PHY_ID_PHY22F_VR9_1_1		0xD565A418
174 #define PHY_ID_PHY11G_VR9_1_2		0xD565A409
175 #define PHY_ID_PHY22F_VR9_1_2		0xD565A419
176 
177 struct xway_gphy_priv {
178 	u64 rx_errors;
179 };
180 
181 static const int xway_internal_delay[] = {0, 500, 1000, 1500, 2000, 2500,
182 					 3000, 3500};
183 
184 static int xway_gphy_rgmii_init(struct phy_device *phydev)
185 {
186 	unsigned int delay_size = ARRAY_SIZE(xway_internal_delay);
187 	s32 int_delay;
188 	int val = 0;
189 
190 	if (!phy_interface_is_rgmii(phydev))
191 		return 0;
192 
193 	/* Existing behavior was to use default pin strapping delay in rgmii
194 	 * mode, but rgmii should have meant no delay.  Warn existing users,
195 	 * but do not change anything at the moment.
196 	 */
197 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII) {
198 		u16 txskew, rxskew;
199 
200 		val = phy_read(phydev, XWAY_MDIO_MIICTRL);
201 		if (val < 0)
202 			return val;
203 
204 		txskew = FIELD_GET(XWAY_MDIO_MIICTRL_TXSKEW_MASK, val);
205 		rxskew = FIELD_GET(XWAY_MDIO_MIICTRL_RXSKEW_MASK, val);
206 
207 		if (txskew > 0 || rxskew > 0)
208 			phydev_warn(phydev,
209 				    "PHY has delays (e.g. via pin strapping), but phy-mode = 'rgmii'\n"
210 				    "Should be 'rgmii-id' to use internal delays txskew:%d ps rxskew:%d ps\n",
211 				    xway_internal_delay[txskew],
212 				    xway_internal_delay[rxskew]);
213 		return 0;
214 	}
215 
216 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
217 	    phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
218 		int_delay = phy_get_internal_delay(phydev, xway_internal_delay,
219 						   delay_size, true);
220 
221 		/* if rx-internal-delay-ps is missing, use default of 2.0 ns */
222 		if (int_delay < 0)
223 			int_delay = 4; /* 2000 ps */
224 
225 		val |= FIELD_PREP(XWAY_MDIO_MIICTRL_RXSKEW_MASK, int_delay);
226 	}
227 
228 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
229 	    phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
230 		int_delay = phy_get_internal_delay(phydev, xway_internal_delay,
231 						   delay_size, false);
232 
233 		/* if tx-internal-delay-ps is missing, use default of 2.0 ns */
234 		if (int_delay < 0)
235 			int_delay = 4; /* 2000 ps */
236 
237 		val |= FIELD_PREP(XWAY_MDIO_MIICTRL_TXSKEW_MASK, int_delay);
238 	}
239 
240 	return phy_modify(phydev, XWAY_MDIO_MIICTRL,
241 			  XWAY_MDIO_MIICTRL_RXSKEW_MASK |
242 			  XWAY_MDIO_MIICTRL_TXSKEW_MASK, val);
243 }
244 
245 static int xway_gphy_init_leds(struct phy_device *phydev)
246 {
247 	int err;
248 	u32 ledxh;
249 	u32 ledxl;
250 
251 	/* Ensure that integrated led function is enabled for all leds */
252 	err = phy_write(phydev, XWAY_MDIO_LED,
253 			XWAY_MDIO_LED_LED0_EN |
254 			XWAY_MDIO_LED_LED1_EN |
255 			XWAY_MDIO_LED_LED2_EN |
256 			XWAY_MDIO_LED_LED3_EN);
257 	if (err)
258 		return err;
259 
260 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDCH,
261 		      XWAY_MMD_LEDCH_NACS_NONE |
262 		      XWAY_MMD_LEDCH_SBF_F02HZ |
263 		      XWAY_MMD_LEDCH_FBF_F16HZ);
264 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDCL,
265 		      XWAY_MMD_LEDCH_CBLINK_NONE |
266 		      XWAY_MMD_LEDCH_SCAN_NONE);
267 
268 	/**
269 	 * In most cases only one LED is connected to this phy, so
270 	 * configure them all to constant on and pulse mode. LED3 is
271 	 * only available in some packages, leave it in its reset
272 	 * configuration.
273 	 */
274 	ledxh = XWAY_MMD_LEDxH_BLINKF_NONE | XWAY_MMD_LEDxH_CON_LINK10XX;
275 	ledxl = XWAY_MMD_LEDxL_PULSE_TXACT | XWAY_MMD_LEDxL_PULSE_RXACT |
276 		XWAY_MMD_LEDxL_BLINKS_NONE;
277 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED0H, ledxh);
278 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED0L, ledxl);
279 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED1H, ledxh);
280 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED1L, ledxl);
281 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED2H, ledxh);
282 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED2L, ledxl);
283 
284 	return 0;
285 }
286 
287 static int xway_gphy_config_init(struct phy_device *phydev)
288 {
289 	struct device_node *np;
290 	int err;
291 
292 	/* Mask all interrupts */
293 	err = phy_write(phydev, XWAY_MDIO_IMASK, 0);
294 	if (err)
295 		return err;
296 
297 	/* Use default LED configuration if 'leds' node isn't defined */
298 	np = of_get_child_by_name(phydev->mdio.dev.of_node, "leds");
299 	if (np)
300 		of_node_put(np);
301 	else
302 		xway_gphy_init_leds(phydev);
303 
304 	/* Clear all pending interrupts */
305 	phy_read(phydev, XWAY_MDIO_ISTAT);
306 
307 	err = xway_gphy_rgmii_init(phydev);
308 	if (err)
309 		return err;
310 
311 	/* Count MDI RX errors (SymbolErrorDuringCarrier) */
312 	return phy_write(phydev, XWAY_MDIO_ERRCNT,
313 			 FIELD_PREP(XWAY_MDIO_ERRCNT_SEL, XWAY_MDIO_ERRCNT_SEL_RXERR));
314 }
315 
316 static int xway_gphy_probe(struct phy_device *phydev)
317 {
318 	struct device *dev = &phydev->mdio.dev;
319 	struct xway_gphy_priv *priv;
320 
321 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
322 	if (!priv)
323 		return -ENOMEM;
324 	phydev->priv = priv;
325 
326 	return 0;
327 }
328 
329 static int xway_gphy14_config_aneg(struct phy_device *phydev)
330 {
331 	int reg, err;
332 
333 	/* Advertise as multi-port device, see IEEE802.3-2002 40.5.1.1 */
334 	/* This is a workaround for an errata in rev < 1.5 devices */
335 	reg = phy_read(phydev, MII_CTRL1000);
336 	reg |= ADVERTISED_MPD;
337 	err = phy_write(phydev, MII_CTRL1000, reg);
338 	if (err)
339 		return err;
340 
341 	return genphy_config_aneg(phydev);
342 }
343 
344 static int xway_gphy_ack_interrupt(struct phy_device *phydev)
345 {
346 	int reg;
347 
348 	reg = phy_read(phydev, XWAY_MDIO_ISTAT);
349 	return (reg < 0) ? reg : 0;
350 }
351 
352 static int xway_gphy_config_intr(struct phy_device *phydev)
353 {
354 	u16 mask = 0;
355 	int err;
356 
357 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
358 		err = xway_gphy_ack_interrupt(phydev);
359 		if (err)
360 			return err;
361 
362 		mask = XWAY_MDIO_INIT_MASK;
363 		err = phy_write(phydev, XWAY_MDIO_IMASK, mask);
364 	} else {
365 		err = phy_write(phydev, XWAY_MDIO_IMASK, mask);
366 		if (err)
367 			return err;
368 
369 		err = xway_gphy_ack_interrupt(phydev);
370 	}
371 
372 	return err;
373 }
374 
375 static irqreturn_t xway_gphy_handle_interrupt(struct phy_device *phydev)
376 {
377 	int irq_status;
378 
379 	irq_status = phy_read(phydev, XWAY_MDIO_ISTAT);
380 	if (irq_status < 0) {
381 		phy_error(phydev);
382 		return IRQ_NONE;
383 	}
384 
385 	if (!(irq_status & XWAY_MDIO_INIT_MASK))
386 		return IRQ_NONE;
387 
388 	phy_trigger_machine(phydev);
389 
390 	return IRQ_HANDLED;
391 }
392 
393 static int xway_gphy_led_brightness_set(struct phy_device *phydev,
394 					u8 index, enum led_brightness value)
395 {
396 	int ret;
397 
398 	if (index >= XWAY_GPHY_MAX_LEDS)
399 		return -EINVAL;
400 
401 	/* clear EN and set manual LED state */
402 	ret = phy_modify(phydev, XWAY_MDIO_LED,
403 			 ((value == LED_OFF) ? XWAY_GPHY_LED_EN(index) : 0) |
404 			 XWAY_GPHY_LED_DA(index),
405 			 (value == LED_OFF) ? 0 : XWAY_GPHY_LED_DA(index));
406 	if (ret)
407 		return ret;
408 
409 	/* clear HW LED setup */
410 	if (value == LED_OFF) {
411 		ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxH(index), 0);
412 		if (ret)
413 			return ret;
414 
415 		return phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxL(index), 0);
416 	} else {
417 		return 0;
418 	}
419 }
420 
421 static const unsigned long supported_triggers = (BIT(TRIGGER_NETDEV_LINK) |
422 						 BIT(TRIGGER_NETDEV_LINK_10) |
423 						 BIT(TRIGGER_NETDEV_LINK_100) |
424 						 BIT(TRIGGER_NETDEV_LINK_1000) |
425 						 BIT(TRIGGER_NETDEV_RX) |
426 						 BIT(TRIGGER_NETDEV_TX));
427 
428 static int xway_gphy_led_hw_is_supported(struct phy_device *phydev, u8 index,
429 					 unsigned long rules)
430 {
431 	if (index >= XWAY_GPHY_MAX_LEDS)
432 		return -EINVAL;
433 
434 	/* activity triggers are not possible without combination with a link
435 	 * trigger.
436 	 */
437 	if (rules & (BIT(TRIGGER_NETDEV_RX) | BIT(TRIGGER_NETDEV_TX)) &&
438 	    !(rules & (BIT(TRIGGER_NETDEV_LINK) |
439 		       BIT(TRIGGER_NETDEV_LINK_10) |
440 		       BIT(TRIGGER_NETDEV_LINK_100) |
441 		       BIT(TRIGGER_NETDEV_LINK_1000))))
442 		return -EOPNOTSUPP;
443 
444 	/* All other combinations of the supported triggers are allowed */
445 	if (rules & ~supported_triggers)
446 		return -EOPNOTSUPP;
447 
448 	return 0;
449 }
450 
451 static int xway_gphy_led_hw_control_get(struct phy_device *phydev, u8 index,
452 					unsigned long *rules)
453 {
454 	int lval, hval;
455 
456 	if (index >= XWAY_GPHY_MAX_LEDS)
457 		return -EINVAL;
458 
459 	hval = phy_read_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxH(index));
460 	if (hval < 0)
461 		return hval;
462 
463 	lval = phy_read_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxL(index));
464 	if (lval < 0)
465 		return lval;
466 
467 	if (hval & XWAY_MMD_LEDxH_CON_LINK10)
468 		*rules |= BIT(TRIGGER_NETDEV_LINK_10);
469 
470 	if (hval & XWAY_MMD_LEDxH_CON_LINK100)
471 		*rules |= BIT(TRIGGER_NETDEV_LINK_100);
472 
473 	if (hval & XWAY_MMD_LEDxH_CON_LINK1000)
474 		*rules |= BIT(TRIGGER_NETDEV_LINK_1000);
475 
476 	if ((hval & XWAY_MMD_LEDxH_CON_LINK10) &&
477 	    (hval & XWAY_MMD_LEDxH_CON_LINK100) &&
478 	    (hval & XWAY_MMD_LEDxH_CON_LINK1000))
479 		*rules |= BIT(TRIGGER_NETDEV_LINK);
480 
481 	if (lval & XWAY_MMD_LEDxL_PULSE_TXACT)
482 		*rules |= BIT(TRIGGER_NETDEV_TX);
483 
484 	if (lval & XWAY_MMD_LEDxL_PULSE_RXACT)
485 		*rules |= BIT(TRIGGER_NETDEV_RX);
486 
487 	return 0;
488 }
489 
490 static int xway_gphy_led_hw_control_set(struct phy_device *phydev, u8 index,
491 					unsigned long rules)
492 {
493 	u16 hval = 0, lval = 0;
494 	int ret;
495 
496 	if (index >= XWAY_GPHY_MAX_LEDS)
497 		return -EINVAL;
498 
499 	if (rules & BIT(TRIGGER_NETDEV_LINK) ||
500 	    rules & BIT(TRIGGER_NETDEV_LINK_10))
501 		hval |= XWAY_MMD_LEDxH_CON_LINK10;
502 
503 	if (rules & BIT(TRIGGER_NETDEV_LINK) ||
504 	    rules & BIT(TRIGGER_NETDEV_LINK_100))
505 		hval |= XWAY_MMD_LEDxH_CON_LINK100;
506 
507 	if (rules & BIT(TRIGGER_NETDEV_LINK) ||
508 	    rules & BIT(TRIGGER_NETDEV_LINK_1000))
509 		hval |= XWAY_MMD_LEDxH_CON_LINK1000;
510 
511 	if (rules & BIT(TRIGGER_NETDEV_TX))
512 		lval |= XWAY_MMD_LEDxL_PULSE_TXACT;
513 
514 	if (rules & BIT(TRIGGER_NETDEV_RX))
515 		lval |= XWAY_MMD_LEDxL_PULSE_RXACT;
516 
517 	ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxH(index), hval);
518 	if (ret)
519 		return ret;
520 
521 	ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxL(index), lval);
522 	if (ret)
523 		return ret;
524 
525 	return phy_set_bits(phydev, XWAY_MDIO_LED, XWAY_GPHY_LED_EN(index));
526 }
527 
528 static int xway_gphy_led_polarity_set(struct phy_device *phydev, int index,
529 				      unsigned long modes)
530 {
531 	bool force_active_low = false, force_active_high = false;
532 	u32 mode;
533 
534 	if (index >= XWAY_GPHY_MAX_LEDS)
535 		return -EINVAL;
536 
537 	for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
538 		switch (mode) {
539 		case PHY_LED_ACTIVE_LOW:
540 			force_active_low = true;
541 			break;
542 		case PHY_LED_ACTIVE_HIGH:
543 			force_active_high = true;
544 			break;
545 		default:
546 			return -EINVAL;
547 		}
548 	}
549 
550 	if (force_active_low)
551 		return phy_set_bits(phydev, XWAY_MDIO_LED, XWAY_GPHY_LED_INV(index));
552 
553 	if (force_active_high)
554 		return phy_clear_bits(phydev, XWAY_MDIO_LED, XWAY_GPHY_LED_INV(index));
555 
556 	return -EINVAL;
557 }
558 
559 static int xway_gphy_update_stats(struct phy_device *phydev)
560 {
561 	struct xway_gphy_priv *priv = phydev->priv;
562 	int ret;
563 
564 	/* XWAY_MDIO_ERRCNT: 8-bit read-clear counter, SEL set to RXERR */
565 	ret = phy_read(phydev, XWAY_MDIO_ERRCNT);
566 	if (ret < 0)
567 		return ret;
568 
569 	priv->rx_errors += FIELD_GET(XWAY_MDIO_ERRCNT_COUNT, ret);
570 
571 	return 0;
572 }
573 
574 static void xway_gphy_get_phy_stats(struct phy_device *phydev,
575 				    struct ethtool_eth_phy_stats *eth_stats,
576 				    struct ethtool_phy_stats *stats)
577 {
578 	struct xway_gphy_priv *priv = phydev->priv;
579 
580 	eth_stats->SymbolErrorDuringCarrier = priv->rx_errors;
581 	stats->rx_errors = priv->rx_errors;
582 }
583 
584 static struct phy_driver xway_gphy[] = {
585 	{
586 		.phy_id		= PHY_ID_PHY11G_1_3,
587 		.phy_id_mask	= 0xffffffff,
588 		.name		= "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.3",
589 		/* PHY_GBIT_FEATURES */
590 		.config_init	= xway_gphy_config_init,
591 		.probe		= xway_gphy_probe,
592 		.config_aneg	= xway_gphy14_config_aneg,
593 		.handle_interrupt = xway_gphy_handle_interrupt,
594 		.config_intr	= xway_gphy_config_intr,
595 		.suspend	= genphy_suspend,
596 		.resume		= genphy_resume,
597 		.led_brightness_set = xway_gphy_led_brightness_set,
598 		.led_hw_is_supported = xway_gphy_led_hw_is_supported,
599 		.led_hw_control_get = xway_gphy_led_hw_control_get,
600 		.led_hw_control_set = xway_gphy_led_hw_control_set,
601 		.led_polarity_set = xway_gphy_led_polarity_set,
602 		.update_stats	= xway_gphy_update_stats,
603 		.get_phy_stats	= xway_gphy_get_phy_stats,
604 	}, {
605 		.phy_id		= PHY_ID_PHY22F_1_3,
606 		.phy_id_mask	= 0xffffffff,
607 		.name		= "Intel XWAY PHY22F (PEF 7061) v1.3",
608 		/* PHY_BASIC_FEATURES */
609 		.config_init	= xway_gphy_config_init,
610 		.probe		= xway_gphy_probe,
611 		.config_aneg	= xway_gphy14_config_aneg,
612 		.handle_interrupt = xway_gphy_handle_interrupt,
613 		.config_intr	= xway_gphy_config_intr,
614 		.suspend	= genphy_suspend,
615 		.resume		= genphy_resume,
616 		.led_brightness_set = xway_gphy_led_brightness_set,
617 		.led_hw_is_supported = xway_gphy_led_hw_is_supported,
618 		.led_hw_control_get = xway_gphy_led_hw_control_get,
619 		.led_hw_control_set = xway_gphy_led_hw_control_set,
620 		.led_polarity_set = xway_gphy_led_polarity_set,
621 		.update_stats	= xway_gphy_update_stats,
622 		.get_phy_stats	= xway_gphy_get_phy_stats,
623 	}, {
624 		.phy_id		= PHY_ID_PHY11G_1_4,
625 		.phy_id_mask	= 0xffffffff,
626 		.name		= "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.4",
627 		/* PHY_GBIT_FEATURES */
628 		.config_init	= xway_gphy_config_init,
629 		.probe		= xway_gphy_probe,
630 		.config_aneg	= xway_gphy14_config_aneg,
631 		.handle_interrupt = xway_gphy_handle_interrupt,
632 		.config_intr	= xway_gphy_config_intr,
633 		.suspend	= genphy_suspend,
634 		.resume		= genphy_resume,
635 		.led_brightness_set = xway_gphy_led_brightness_set,
636 		.led_hw_is_supported = xway_gphy_led_hw_is_supported,
637 		.led_hw_control_get = xway_gphy_led_hw_control_get,
638 		.led_hw_control_set = xway_gphy_led_hw_control_set,
639 		.led_polarity_set = xway_gphy_led_polarity_set,
640 		.update_stats	= xway_gphy_update_stats,
641 		.get_phy_stats	= xway_gphy_get_phy_stats,
642 	}, {
643 		.phy_id		= PHY_ID_PHY22F_1_4,
644 		.phy_id_mask	= 0xffffffff,
645 		.name		= "Intel XWAY PHY22F (PEF 7061) v1.4",
646 		/* PHY_BASIC_FEATURES */
647 		.config_init	= xway_gphy_config_init,
648 		.probe		= xway_gphy_probe,
649 		.config_aneg	= xway_gphy14_config_aneg,
650 		.handle_interrupt = xway_gphy_handle_interrupt,
651 		.config_intr	= xway_gphy_config_intr,
652 		.suspend	= genphy_suspend,
653 		.resume		= genphy_resume,
654 		.led_brightness_set = xway_gphy_led_brightness_set,
655 		.led_hw_is_supported = xway_gphy_led_hw_is_supported,
656 		.led_hw_control_get = xway_gphy_led_hw_control_get,
657 		.led_hw_control_set = xway_gphy_led_hw_control_set,
658 		.led_polarity_set = xway_gphy_led_polarity_set,
659 		.update_stats	= xway_gphy_update_stats,
660 		.get_phy_stats	= xway_gphy_get_phy_stats,
661 	}, {
662 		.phy_id		= PHY_ID_PHY11G_1_5,
663 		.phy_id_mask	= 0xffffffff,
664 		.name		= "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.5 / v1.6",
665 		/* PHY_GBIT_FEATURES */
666 		.config_init	= xway_gphy_config_init,
667 		.probe		= xway_gphy_probe,
668 		.handle_interrupt = xway_gphy_handle_interrupt,
669 		.config_intr	= xway_gphy_config_intr,
670 		.suspend	= genphy_suspend,
671 		.resume		= genphy_resume,
672 		.led_brightness_set = xway_gphy_led_brightness_set,
673 		.led_hw_is_supported = xway_gphy_led_hw_is_supported,
674 		.led_hw_control_get = xway_gphy_led_hw_control_get,
675 		.led_hw_control_set = xway_gphy_led_hw_control_set,
676 		.led_polarity_set = xway_gphy_led_polarity_set,
677 		.update_stats	= xway_gphy_update_stats,
678 		.get_phy_stats	= xway_gphy_get_phy_stats,
679 	}, {
680 		.phy_id		= PHY_ID_PHY22F_1_5,
681 		.phy_id_mask	= 0xffffffff,
682 		.name		= "Intel XWAY PHY22F (PEF 7061) v1.5 / v1.6",
683 		/* PHY_BASIC_FEATURES */
684 		.config_init	= xway_gphy_config_init,
685 		.probe		= xway_gphy_probe,
686 		.handle_interrupt = xway_gphy_handle_interrupt,
687 		.config_intr	= xway_gphy_config_intr,
688 		.suspend	= genphy_suspend,
689 		.resume		= genphy_resume,
690 		.led_brightness_set = xway_gphy_led_brightness_set,
691 		.led_hw_is_supported = xway_gphy_led_hw_is_supported,
692 		.led_hw_control_get = xway_gphy_led_hw_control_get,
693 		.led_hw_control_set = xway_gphy_led_hw_control_set,
694 		.led_polarity_set = xway_gphy_led_polarity_set,
695 		.update_stats	= xway_gphy_update_stats,
696 		.get_phy_stats	= xway_gphy_get_phy_stats,
697 	}, {
698 		.phy_id		= PHY_ID_PHY11G_VR9_1_1,
699 		.phy_id_mask	= 0xffffffff,
700 		.name		= "Intel XWAY PHY11G (xRX v1.1 integrated)",
701 		/* PHY_GBIT_FEATURES */
702 		.config_init	= xway_gphy_config_init,
703 		.probe		= xway_gphy_probe,
704 		.handle_interrupt = xway_gphy_handle_interrupt,
705 		.config_intr	= xway_gphy_config_intr,
706 		.suspend	= genphy_suspend,
707 		.resume		= genphy_resume,
708 		.led_brightness_set = xway_gphy_led_brightness_set,
709 		.led_hw_is_supported = xway_gphy_led_hw_is_supported,
710 		.led_hw_control_get = xway_gphy_led_hw_control_get,
711 		.led_hw_control_set = xway_gphy_led_hw_control_set,
712 		.led_polarity_set = xway_gphy_led_polarity_set,
713 		.update_stats	= xway_gphy_update_stats,
714 		.get_phy_stats	= xway_gphy_get_phy_stats,
715 	}, {
716 		.phy_id		= PHY_ID_PHY22F_VR9_1_1,
717 		.phy_id_mask	= 0xffffffff,
718 		.name		= "Intel XWAY PHY22F (xRX v1.1 integrated)",
719 		/* PHY_BASIC_FEATURES */
720 		.config_init	= xway_gphy_config_init,
721 		.probe		= xway_gphy_probe,
722 		.handle_interrupt = xway_gphy_handle_interrupt,
723 		.config_intr	= xway_gphy_config_intr,
724 		.suspend	= genphy_suspend,
725 		.resume		= genphy_resume,
726 		.led_brightness_set = xway_gphy_led_brightness_set,
727 		.led_hw_is_supported = xway_gphy_led_hw_is_supported,
728 		.led_hw_control_get = xway_gphy_led_hw_control_get,
729 		.led_hw_control_set = xway_gphy_led_hw_control_set,
730 		.led_polarity_set = xway_gphy_led_polarity_set,
731 		.update_stats	= xway_gphy_update_stats,
732 		.get_phy_stats	= xway_gphy_get_phy_stats,
733 	}, {
734 		.phy_id		= PHY_ID_PHY11G_VR9_1_2,
735 		.phy_id_mask	= 0xffffffff,
736 		.name		= "Intel XWAY PHY11G (xRX v1.2 integrated)",
737 		/* PHY_GBIT_FEATURES */
738 		.config_init	= xway_gphy_config_init,
739 		.probe		= xway_gphy_probe,
740 		.handle_interrupt = xway_gphy_handle_interrupt,
741 		.config_intr	= xway_gphy_config_intr,
742 		.suspend	= genphy_suspend,
743 		.resume		= genphy_resume,
744 		.led_brightness_set = xway_gphy_led_brightness_set,
745 		.led_hw_is_supported = xway_gphy_led_hw_is_supported,
746 		.led_hw_control_get = xway_gphy_led_hw_control_get,
747 		.led_hw_control_set = xway_gphy_led_hw_control_set,
748 		.led_polarity_set = xway_gphy_led_polarity_set,
749 		.update_stats	= xway_gphy_update_stats,
750 		.get_phy_stats	= xway_gphy_get_phy_stats,
751 	}, {
752 		.phy_id		= PHY_ID_PHY22F_VR9_1_2,
753 		.phy_id_mask	= 0xffffffff,
754 		.name		= "Intel XWAY PHY22F (xRX v1.2 integrated)",
755 		/* PHY_BASIC_FEATURES */
756 		.config_init	= xway_gphy_config_init,
757 		.probe		= xway_gphy_probe,
758 		.handle_interrupt = xway_gphy_handle_interrupt,
759 		.config_intr	= xway_gphy_config_intr,
760 		.suspend	= genphy_suspend,
761 		.resume		= genphy_resume,
762 		.led_brightness_set = xway_gphy_led_brightness_set,
763 		.led_hw_is_supported = xway_gphy_led_hw_is_supported,
764 		.led_hw_control_get = xway_gphy_led_hw_control_get,
765 		.led_hw_control_set = xway_gphy_led_hw_control_set,
766 		.led_polarity_set = xway_gphy_led_polarity_set,
767 		.update_stats	= xway_gphy_update_stats,
768 		.get_phy_stats	= xway_gphy_get_phy_stats,
769 	},
770 };
771 module_phy_driver(xway_gphy);
772 
773 static const struct mdio_device_id __maybe_unused xway_gphy_tbl[] = {
774 	{ PHY_ID_PHY11G_1_3, 0xffffffff },
775 	{ PHY_ID_PHY22F_1_3, 0xffffffff },
776 	{ PHY_ID_PHY11G_1_4, 0xffffffff },
777 	{ PHY_ID_PHY22F_1_4, 0xffffffff },
778 	{ PHY_ID_PHY11G_1_5, 0xffffffff },
779 	{ PHY_ID_PHY22F_1_5, 0xffffffff },
780 	{ PHY_ID_PHY11G_VR9_1_1, 0xffffffff },
781 	{ PHY_ID_PHY22F_VR9_1_1, 0xffffffff },
782 	{ PHY_ID_PHY11G_VR9_1_2, 0xffffffff },
783 	{ PHY_ID_PHY22F_VR9_1_2, 0xffffffff },
784 	{ }
785 };
786 MODULE_DEVICE_TABLE(mdio, xway_gphy_tbl);
787 
788 MODULE_DESCRIPTION("Intel XWAY PHY driver");
789 MODULE_LICENSE("GPL");
790