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