xref: /linux/drivers/net/phy/intel-xway.c (revision 8faabc041a001140564f718dabe37753e88b37fa)
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 	struct device *dev = &phydev->mdio.dev;
178 	unsigned int delay_size = ARRAY_SIZE(xway_internal_delay);
179 	s32 int_delay;
180 	int val = 0;
181 
182 	if (!phy_interface_is_rgmii(phydev))
183 		return 0;
184 
185 	/* Existing behavior was to use default pin strapping delay in rgmii
186 	 * mode, but rgmii should have meant no delay.  Warn existing users,
187 	 * but do not change anything at the moment.
188 	 */
189 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII) {
190 		u16 txskew, rxskew;
191 
192 		val = phy_read(phydev, XWAY_MDIO_MIICTRL);
193 		if (val < 0)
194 			return val;
195 
196 		txskew = FIELD_GET(XWAY_MDIO_MIICTRL_TXSKEW_MASK, val);
197 		rxskew = FIELD_GET(XWAY_MDIO_MIICTRL_RXSKEW_MASK, val);
198 
199 		if (txskew > 0 || rxskew > 0)
200 			phydev_warn(phydev,
201 				    "PHY has delays (e.g. via pin strapping), but phy-mode = 'rgmii'\n"
202 				    "Should be 'rgmii-id' to use internal delays txskew:%d ps rxskew:%d ps\n",
203 				    xway_internal_delay[txskew],
204 				    xway_internal_delay[rxskew]);
205 		return 0;
206 	}
207 
208 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
209 	    phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
210 		int_delay = phy_get_internal_delay(phydev, dev,
211 						   xway_internal_delay,
212 						   delay_size, true);
213 
214 		/* if rx-internal-delay-ps is missing, use default of 2.0 ns */
215 		if (int_delay < 0)
216 			int_delay = 4; /* 2000 ps */
217 
218 		val |= FIELD_PREP(XWAY_MDIO_MIICTRL_RXSKEW_MASK, int_delay);
219 	}
220 
221 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
222 	    phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
223 		int_delay = phy_get_internal_delay(phydev, dev,
224 						   xway_internal_delay,
225 						   delay_size, false);
226 
227 		/* if tx-internal-delay-ps is missing, use default of 2.0 ns */
228 		if (int_delay < 0)
229 			int_delay = 4; /* 2000 ps */
230 
231 		val |= FIELD_PREP(XWAY_MDIO_MIICTRL_TXSKEW_MASK, int_delay);
232 	}
233 
234 	return phy_modify(phydev, XWAY_MDIO_MIICTRL,
235 			  XWAY_MDIO_MIICTRL_RXSKEW_MASK |
236 			  XWAY_MDIO_MIICTRL_TXSKEW_MASK, val);
237 }
238 
xway_gphy_init_leds(struct phy_device * phydev)239 static int xway_gphy_init_leds(struct phy_device *phydev)
240 {
241 	int err;
242 	u32 ledxh;
243 	u32 ledxl;
244 
245 	/* Ensure that integrated led function is enabled for all leds */
246 	err = phy_write(phydev, XWAY_MDIO_LED,
247 			XWAY_MDIO_LED_LED0_EN |
248 			XWAY_MDIO_LED_LED1_EN |
249 			XWAY_MDIO_LED_LED2_EN |
250 			XWAY_MDIO_LED_LED3_EN);
251 	if (err)
252 		return err;
253 
254 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDCH,
255 		      XWAY_MMD_LEDCH_NACS_NONE |
256 		      XWAY_MMD_LEDCH_SBF_F02HZ |
257 		      XWAY_MMD_LEDCH_FBF_F16HZ);
258 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDCL,
259 		      XWAY_MMD_LEDCH_CBLINK_NONE |
260 		      XWAY_MMD_LEDCH_SCAN_NONE);
261 
262 	/**
263 	 * In most cases only one LED is connected to this phy, so
264 	 * configure them all to constant on and pulse mode. LED3 is
265 	 * only available in some packages, leave it in its reset
266 	 * configuration.
267 	 */
268 	ledxh = XWAY_MMD_LEDxH_BLINKF_NONE | XWAY_MMD_LEDxH_CON_LINK10XX;
269 	ledxl = XWAY_MMD_LEDxL_PULSE_TXACT | XWAY_MMD_LEDxL_PULSE_RXACT |
270 		XWAY_MMD_LEDxL_BLINKS_NONE;
271 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED0H, ledxh);
272 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED0L, ledxl);
273 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED1H, ledxh);
274 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED1L, ledxl);
275 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED2H, ledxh);
276 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED2L, ledxl);
277 
278 	return 0;
279 }
280 
xway_gphy_config_init(struct phy_device * phydev)281 static int xway_gphy_config_init(struct phy_device *phydev)
282 {
283 	struct device_node *np = phydev->mdio.dev.of_node;
284 	int err;
285 
286 	/* Mask all interrupts */
287 	err = phy_write(phydev, XWAY_MDIO_IMASK, 0);
288 	if (err)
289 		return err;
290 
291 	/* Use default LED configuration if 'leds' node isn't defined */
292 	if (!of_get_child_by_name(np, "leds"))
293 		xway_gphy_init_leds(phydev);
294 
295 	/* Clear all pending interrupts */
296 	phy_read(phydev, XWAY_MDIO_ISTAT);
297 
298 	err = xway_gphy_rgmii_init(phydev);
299 	if (err)
300 		return err;
301 
302 	return 0;
303 }
304 
xway_gphy14_config_aneg(struct phy_device * phydev)305 static int xway_gphy14_config_aneg(struct phy_device *phydev)
306 {
307 	int reg, err;
308 
309 	/* Advertise as multi-port device, see IEEE802.3-2002 40.5.1.1 */
310 	/* This is a workaround for an errata in rev < 1.5 devices */
311 	reg = phy_read(phydev, MII_CTRL1000);
312 	reg |= ADVERTISED_MPD;
313 	err = phy_write(phydev, MII_CTRL1000, reg);
314 	if (err)
315 		return err;
316 
317 	return genphy_config_aneg(phydev);
318 }
319 
xway_gphy_ack_interrupt(struct phy_device * phydev)320 static int xway_gphy_ack_interrupt(struct phy_device *phydev)
321 {
322 	int reg;
323 
324 	reg = phy_read(phydev, XWAY_MDIO_ISTAT);
325 	return (reg < 0) ? reg : 0;
326 }
327 
xway_gphy_config_intr(struct phy_device * phydev)328 static int xway_gphy_config_intr(struct phy_device *phydev)
329 {
330 	u16 mask = 0;
331 	int err;
332 
333 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
334 		err = xway_gphy_ack_interrupt(phydev);
335 		if (err)
336 			return err;
337 
338 		mask = XWAY_MDIO_INIT_MASK;
339 		err = phy_write(phydev, XWAY_MDIO_IMASK, mask);
340 	} else {
341 		err = phy_write(phydev, XWAY_MDIO_IMASK, mask);
342 		if (err)
343 			return err;
344 
345 		err = xway_gphy_ack_interrupt(phydev);
346 	}
347 
348 	return err;
349 }
350 
xway_gphy_handle_interrupt(struct phy_device * phydev)351 static irqreturn_t xway_gphy_handle_interrupt(struct phy_device *phydev)
352 {
353 	int irq_status;
354 
355 	irq_status = phy_read(phydev, XWAY_MDIO_ISTAT);
356 	if (irq_status < 0) {
357 		phy_error(phydev);
358 		return IRQ_NONE;
359 	}
360 
361 	if (!(irq_status & XWAY_MDIO_INIT_MASK))
362 		return IRQ_NONE;
363 
364 	phy_trigger_machine(phydev);
365 
366 	return IRQ_HANDLED;
367 }
368 
xway_gphy_led_brightness_set(struct phy_device * phydev,u8 index,enum led_brightness value)369 static int xway_gphy_led_brightness_set(struct phy_device *phydev,
370 					u8 index, enum led_brightness value)
371 {
372 	int ret;
373 
374 	if (index >= XWAY_GPHY_MAX_LEDS)
375 		return -EINVAL;
376 
377 	/* clear EN and set manual LED state */
378 	ret = phy_modify(phydev, XWAY_MDIO_LED,
379 			 ((value == LED_OFF) ? XWAY_GPHY_LED_EN(index) : 0) |
380 			 XWAY_GPHY_LED_DA(index),
381 			 (value == LED_OFF) ? 0 : XWAY_GPHY_LED_DA(index));
382 	if (ret)
383 		return ret;
384 
385 	/* clear HW LED setup */
386 	if (value == LED_OFF) {
387 		ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxH(index), 0);
388 		if (ret)
389 			return ret;
390 
391 		return phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxL(index), 0);
392 	} else {
393 		return 0;
394 	}
395 }
396 
397 static const unsigned long supported_triggers = (BIT(TRIGGER_NETDEV_LINK) |
398 						 BIT(TRIGGER_NETDEV_LINK_10) |
399 						 BIT(TRIGGER_NETDEV_LINK_100) |
400 						 BIT(TRIGGER_NETDEV_LINK_1000) |
401 						 BIT(TRIGGER_NETDEV_RX) |
402 						 BIT(TRIGGER_NETDEV_TX));
403 
xway_gphy_led_hw_is_supported(struct phy_device * phydev,u8 index,unsigned long rules)404 static int xway_gphy_led_hw_is_supported(struct phy_device *phydev, u8 index,
405 					 unsigned long rules)
406 {
407 	if (index >= XWAY_GPHY_MAX_LEDS)
408 		return -EINVAL;
409 
410 	/* activity triggers are not possible without combination with a link
411 	 * trigger.
412 	 */
413 	if (rules & (BIT(TRIGGER_NETDEV_RX) | BIT(TRIGGER_NETDEV_TX)) &&
414 	    !(rules & (BIT(TRIGGER_NETDEV_LINK) |
415 		       BIT(TRIGGER_NETDEV_LINK_10) |
416 		       BIT(TRIGGER_NETDEV_LINK_100) |
417 		       BIT(TRIGGER_NETDEV_LINK_1000))))
418 		return -EOPNOTSUPP;
419 
420 	/* All other combinations of the supported triggers are allowed */
421 	if (rules & ~supported_triggers)
422 		return -EOPNOTSUPP;
423 
424 	return 0;
425 }
426 
xway_gphy_led_hw_control_get(struct phy_device * phydev,u8 index,unsigned long * rules)427 static int xway_gphy_led_hw_control_get(struct phy_device *phydev, u8 index,
428 					unsigned long *rules)
429 {
430 	int lval, hval;
431 
432 	if (index >= XWAY_GPHY_MAX_LEDS)
433 		return -EINVAL;
434 
435 	hval = phy_read_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxH(index));
436 	if (hval < 0)
437 		return hval;
438 
439 	lval = phy_read_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxL(index));
440 	if (lval < 0)
441 		return lval;
442 
443 	if (hval & XWAY_MMD_LEDxH_CON_LINK10)
444 		*rules |= BIT(TRIGGER_NETDEV_LINK_10);
445 
446 	if (hval & XWAY_MMD_LEDxH_CON_LINK100)
447 		*rules |= BIT(TRIGGER_NETDEV_LINK_100);
448 
449 	if (hval & XWAY_MMD_LEDxH_CON_LINK1000)
450 		*rules |= BIT(TRIGGER_NETDEV_LINK_1000);
451 
452 	if ((hval & XWAY_MMD_LEDxH_CON_LINK10) &&
453 	    (hval & XWAY_MMD_LEDxH_CON_LINK100) &&
454 	    (hval & XWAY_MMD_LEDxH_CON_LINK1000))
455 		*rules |= BIT(TRIGGER_NETDEV_LINK);
456 
457 	if (lval & XWAY_MMD_LEDxL_PULSE_TXACT)
458 		*rules |= BIT(TRIGGER_NETDEV_TX);
459 
460 	if (lval & XWAY_MMD_LEDxL_PULSE_RXACT)
461 		*rules |= BIT(TRIGGER_NETDEV_RX);
462 
463 	return 0;
464 }
465 
xway_gphy_led_hw_control_set(struct phy_device * phydev,u8 index,unsigned long rules)466 static int xway_gphy_led_hw_control_set(struct phy_device *phydev, u8 index,
467 					unsigned long rules)
468 {
469 	u16 hval = 0, lval = 0;
470 	int ret;
471 
472 	if (index >= XWAY_GPHY_MAX_LEDS)
473 		return -EINVAL;
474 
475 	if (rules & BIT(TRIGGER_NETDEV_LINK) ||
476 	    rules & BIT(TRIGGER_NETDEV_LINK_10))
477 		hval |= XWAY_MMD_LEDxH_CON_LINK10;
478 
479 	if (rules & BIT(TRIGGER_NETDEV_LINK) ||
480 	    rules & BIT(TRIGGER_NETDEV_LINK_100))
481 		hval |= XWAY_MMD_LEDxH_CON_LINK100;
482 
483 	if (rules & BIT(TRIGGER_NETDEV_LINK) ||
484 	    rules & BIT(TRIGGER_NETDEV_LINK_1000))
485 		hval |= XWAY_MMD_LEDxH_CON_LINK1000;
486 
487 	if (rules & BIT(TRIGGER_NETDEV_TX))
488 		lval |= XWAY_MMD_LEDxL_PULSE_TXACT;
489 
490 	if (rules & BIT(TRIGGER_NETDEV_RX))
491 		lval |= XWAY_MMD_LEDxL_PULSE_RXACT;
492 
493 	ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxH(index), hval);
494 	if (ret)
495 		return ret;
496 
497 	ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxL(index), lval);
498 	if (ret)
499 		return ret;
500 
501 	return phy_set_bits(phydev, XWAY_MDIO_LED, XWAY_GPHY_LED_EN(index));
502 }
503 
xway_gphy_led_polarity_set(struct phy_device * phydev,int index,unsigned long modes)504 static int xway_gphy_led_polarity_set(struct phy_device *phydev, int index,
505 				      unsigned long modes)
506 {
507 	bool force_active_low = false, force_active_high = false;
508 	u32 mode;
509 
510 	if (index >= XWAY_GPHY_MAX_LEDS)
511 		return -EINVAL;
512 
513 	for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
514 		switch (mode) {
515 		case PHY_LED_ACTIVE_LOW:
516 			force_active_low = true;
517 			break;
518 		case PHY_LED_ACTIVE_HIGH:
519 			force_active_high = true;
520 			break;
521 		default:
522 			return -EINVAL;
523 		}
524 	}
525 
526 	if (force_active_low)
527 		return phy_set_bits(phydev, XWAY_MDIO_LED, XWAY_GPHY_LED_INV(index));
528 
529 	if (force_active_high)
530 		return phy_clear_bits(phydev, XWAY_MDIO_LED, XWAY_GPHY_LED_INV(index));
531 
532 	return -EINVAL;
533 }
534 
535 static struct phy_driver xway_gphy[] = {
536 	{
537 		.phy_id		= PHY_ID_PHY11G_1_3,
538 		.phy_id_mask	= 0xffffffff,
539 		.name		= "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.3",
540 		/* PHY_GBIT_FEATURES */
541 		.config_init	= xway_gphy_config_init,
542 		.config_aneg	= xway_gphy14_config_aneg,
543 		.handle_interrupt = xway_gphy_handle_interrupt,
544 		.config_intr	= xway_gphy_config_intr,
545 		.suspend	= genphy_suspend,
546 		.resume		= genphy_resume,
547 		.led_brightness_set = xway_gphy_led_brightness_set,
548 		.led_hw_is_supported = xway_gphy_led_hw_is_supported,
549 		.led_hw_control_get = xway_gphy_led_hw_control_get,
550 		.led_hw_control_set = xway_gphy_led_hw_control_set,
551 		.led_polarity_set = xway_gphy_led_polarity_set,
552 	}, {
553 		.phy_id		= PHY_ID_PHY22F_1_3,
554 		.phy_id_mask	= 0xffffffff,
555 		.name		= "Intel XWAY PHY22F (PEF 7061) v1.3",
556 		/* PHY_BASIC_FEATURES */
557 		.config_init	= xway_gphy_config_init,
558 		.config_aneg	= xway_gphy14_config_aneg,
559 		.handle_interrupt = xway_gphy_handle_interrupt,
560 		.config_intr	= xway_gphy_config_intr,
561 		.suspend	= genphy_suspend,
562 		.resume		= genphy_resume,
563 		.led_brightness_set = xway_gphy_led_brightness_set,
564 		.led_hw_is_supported = xway_gphy_led_hw_is_supported,
565 		.led_hw_control_get = xway_gphy_led_hw_control_get,
566 		.led_hw_control_set = xway_gphy_led_hw_control_set,
567 		.led_polarity_set = xway_gphy_led_polarity_set,
568 	}, {
569 		.phy_id		= PHY_ID_PHY11G_1_4,
570 		.phy_id_mask	= 0xffffffff,
571 		.name		= "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.4",
572 		/* PHY_GBIT_FEATURES */
573 		.config_init	= xway_gphy_config_init,
574 		.config_aneg	= xway_gphy14_config_aneg,
575 		.handle_interrupt = xway_gphy_handle_interrupt,
576 		.config_intr	= xway_gphy_config_intr,
577 		.suspend	= genphy_suspend,
578 		.resume		= genphy_resume,
579 		.led_brightness_set = xway_gphy_led_brightness_set,
580 		.led_hw_is_supported = xway_gphy_led_hw_is_supported,
581 		.led_hw_control_get = xway_gphy_led_hw_control_get,
582 		.led_hw_control_set = xway_gphy_led_hw_control_set,
583 		.led_polarity_set = xway_gphy_led_polarity_set,
584 	}, {
585 		.phy_id		= PHY_ID_PHY22F_1_4,
586 		.phy_id_mask	= 0xffffffff,
587 		.name		= "Intel XWAY PHY22F (PEF 7061) v1.4",
588 		/* PHY_BASIC_FEATURES */
589 		.config_init	= xway_gphy_config_init,
590 		.config_aneg	= xway_gphy14_config_aneg,
591 		.handle_interrupt = xway_gphy_handle_interrupt,
592 		.config_intr	= xway_gphy_config_intr,
593 		.suspend	= genphy_suspend,
594 		.resume		= genphy_resume,
595 		.led_brightness_set = xway_gphy_led_brightness_set,
596 		.led_hw_is_supported = xway_gphy_led_hw_is_supported,
597 		.led_hw_control_get = xway_gphy_led_hw_control_get,
598 		.led_hw_control_set = xway_gphy_led_hw_control_set,
599 		.led_polarity_set = xway_gphy_led_polarity_set,
600 	}, {
601 		.phy_id		= PHY_ID_PHY11G_1_5,
602 		.phy_id_mask	= 0xffffffff,
603 		.name		= "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.5 / v1.6",
604 		/* PHY_GBIT_FEATURES */
605 		.config_init	= xway_gphy_config_init,
606 		.handle_interrupt = xway_gphy_handle_interrupt,
607 		.config_intr	= xway_gphy_config_intr,
608 		.suspend	= genphy_suspend,
609 		.resume		= genphy_resume,
610 		.led_brightness_set = xway_gphy_led_brightness_set,
611 		.led_hw_is_supported = xway_gphy_led_hw_is_supported,
612 		.led_hw_control_get = xway_gphy_led_hw_control_get,
613 		.led_hw_control_set = xway_gphy_led_hw_control_set,
614 		.led_polarity_set = xway_gphy_led_polarity_set,
615 	}, {
616 		.phy_id		= PHY_ID_PHY22F_1_5,
617 		.phy_id_mask	= 0xffffffff,
618 		.name		= "Intel XWAY PHY22F (PEF 7061) v1.5 / v1.6",
619 		/* PHY_BASIC_FEATURES */
620 		.config_init	= xway_gphy_config_init,
621 		.handle_interrupt = xway_gphy_handle_interrupt,
622 		.config_intr	= xway_gphy_config_intr,
623 		.suspend	= genphy_suspend,
624 		.resume		= genphy_resume,
625 		.led_brightness_set = xway_gphy_led_brightness_set,
626 		.led_hw_is_supported = xway_gphy_led_hw_is_supported,
627 		.led_hw_control_get = xway_gphy_led_hw_control_get,
628 		.led_hw_control_set = xway_gphy_led_hw_control_set,
629 		.led_polarity_set = xway_gphy_led_polarity_set,
630 	}, {
631 		.phy_id		= PHY_ID_PHY11G_VR9_1_1,
632 		.phy_id_mask	= 0xffffffff,
633 		.name		= "Intel XWAY PHY11G (xRX v1.1 integrated)",
634 		/* PHY_GBIT_FEATURES */
635 		.config_init	= xway_gphy_config_init,
636 		.handle_interrupt = xway_gphy_handle_interrupt,
637 		.config_intr	= xway_gphy_config_intr,
638 		.suspend	= genphy_suspend,
639 		.resume		= genphy_resume,
640 		.led_brightness_set = xway_gphy_led_brightness_set,
641 		.led_hw_is_supported = xway_gphy_led_hw_is_supported,
642 		.led_hw_control_get = xway_gphy_led_hw_control_get,
643 		.led_hw_control_set = xway_gphy_led_hw_control_set,
644 		.led_polarity_set = xway_gphy_led_polarity_set,
645 	}, {
646 		.phy_id		= PHY_ID_PHY22F_VR9_1_1,
647 		.phy_id_mask	= 0xffffffff,
648 		.name		= "Intel XWAY PHY22F (xRX v1.1 integrated)",
649 		/* PHY_BASIC_FEATURES */
650 		.config_init	= xway_gphy_config_init,
651 		.handle_interrupt = xway_gphy_handle_interrupt,
652 		.config_intr	= xway_gphy_config_intr,
653 		.suspend	= genphy_suspend,
654 		.resume		= genphy_resume,
655 		.led_brightness_set = xway_gphy_led_brightness_set,
656 		.led_hw_is_supported = xway_gphy_led_hw_is_supported,
657 		.led_hw_control_get = xway_gphy_led_hw_control_get,
658 		.led_hw_control_set = xway_gphy_led_hw_control_set,
659 		.led_polarity_set = xway_gphy_led_polarity_set,
660 	}, {
661 		.phy_id		= PHY_ID_PHY11G_VR9_1_2,
662 		.phy_id_mask	= 0xffffffff,
663 		.name		= "Intel XWAY PHY11G (xRX v1.2 integrated)",
664 		/* PHY_GBIT_FEATURES */
665 		.config_init	= xway_gphy_config_init,
666 		.handle_interrupt = xway_gphy_handle_interrupt,
667 		.config_intr	= xway_gphy_config_intr,
668 		.suspend	= genphy_suspend,
669 		.resume		= genphy_resume,
670 		.led_brightness_set = xway_gphy_led_brightness_set,
671 		.led_hw_is_supported = xway_gphy_led_hw_is_supported,
672 		.led_hw_control_get = xway_gphy_led_hw_control_get,
673 		.led_hw_control_set = xway_gphy_led_hw_control_set,
674 		.led_polarity_set = xway_gphy_led_polarity_set,
675 	}, {
676 		.phy_id		= PHY_ID_PHY22F_VR9_1_2,
677 		.phy_id_mask	= 0xffffffff,
678 		.name		= "Intel XWAY PHY22F (xRX v1.2 integrated)",
679 		/* PHY_BASIC_FEATURES */
680 		.config_init	= xway_gphy_config_init,
681 		.handle_interrupt = xway_gphy_handle_interrupt,
682 		.config_intr	= xway_gphy_config_intr,
683 		.suspend	= genphy_suspend,
684 		.resume		= genphy_resume,
685 		.led_brightness_set = xway_gphy_led_brightness_set,
686 		.led_hw_is_supported = xway_gphy_led_hw_is_supported,
687 		.led_hw_control_get = xway_gphy_led_hw_control_get,
688 		.led_hw_control_set = xway_gphy_led_hw_control_set,
689 		.led_polarity_set = xway_gphy_led_polarity_set,
690 	},
691 };
692 module_phy_driver(xway_gphy);
693 
694 static struct mdio_device_id __maybe_unused xway_gphy_tbl[] = {
695 	{ PHY_ID_PHY11G_1_3, 0xffffffff },
696 	{ PHY_ID_PHY22F_1_3, 0xffffffff },
697 	{ PHY_ID_PHY11G_1_4, 0xffffffff },
698 	{ PHY_ID_PHY22F_1_4, 0xffffffff },
699 	{ PHY_ID_PHY11G_1_5, 0xffffffff },
700 	{ PHY_ID_PHY22F_1_5, 0xffffffff },
701 	{ PHY_ID_PHY11G_VR9_1_1, 0xffffffff },
702 	{ PHY_ID_PHY22F_VR9_1_1, 0xffffffff },
703 	{ PHY_ID_PHY11G_VR9_1_2, 0xffffffff },
704 	{ PHY_ID_PHY22F_VR9_1_2, 0xffffffff },
705 	{ }
706 };
707 MODULE_DEVICE_TABLE(mdio, xway_gphy_tbl);
708 
709 MODULE_DESCRIPTION("Intel XWAY PHY driver");
710 MODULE_LICENSE("GPL");
711