xref: /linux/drivers/net/phy/mxl-86110.c (revision 1b98f357dadd6ea613a435fbaef1a5dd7b35fd21)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * PHY driver for Maxlinear MXL86110
4  *
5  * Copyright 2023 MaxLinear Inc.
6  *
7  */
8 
9 #include <linux/bitfield.h>
10 #include <linux/etherdevice.h>
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/of.h>
14 #include <linux/phy.h>
15 
16 /* PHY ID */
17 #define PHY_ID_MXL86110		0xc1335580
18 
19 /* required to access extended registers */
20 #define MXL86110_EXTD_REG_ADDR_OFFSET			0x1E
21 #define MXL86110_EXTD_REG_ADDR_DATA			0x1F
22 #define PHY_IRQ_ENABLE_REG				0x12
23 #define PHY_IRQ_ENABLE_REG_WOL				BIT(6)
24 
25 /* SyncE Configuration Register - COM_EXT SYNCE_CFG */
26 #define MXL86110_EXT_SYNCE_CFG_REG			0xA012
27 #define MXL86110_EXT_SYNCE_CFG_CLK_FRE_SEL		BIT(4)
28 #define MXL86110_EXT_SYNCE_CFG_EN_SYNC_E_DURING_LNKDN	BIT(5)
29 #define MXL86110_EXT_SYNCE_CFG_EN_SYNC_E		BIT(6)
30 #define MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_MASK		GENMASK(3, 1)
31 #define MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_125M_PLL	0
32 #define MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_25M		4
33 
34 /* MAC Address registers */
35 #define MXL86110_EXT_MAC_ADDR_CFG1			0xA007
36 #define MXL86110_EXT_MAC_ADDR_CFG2			0xA008
37 #define MXL86110_EXT_MAC_ADDR_CFG3			0xA009
38 
39 #define MXL86110_EXT_WOL_CFG_REG			0xA00A
40 #define MXL86110_WOL_CFG_WOL_MASK			BIT(3)
41 
42 /* RGMII register */
43 #define MXL86110_EXT_RGMII_CFG1_REG			0xA003
44 /* delay can be adjusted in steps of about 150ps */
45 #define MXL86110_EXT_RGMII_CFG1_RX_NO_DELAY		(0x0 << 10)
46 /* Closest value to 2000 ps */
47 #define MXL86110_EXT_RGMII_CFG1_RX_DELAY_1950PS		(0xD << 10)
48 #define MXL86110_EXT_RGMII_CFG1_RX_DELAY_MASK		GENMASK(13, 10)
49 
50 #define MXL86110_EXT_RGMII_CFG1_TX_1G_DELAY_1950PS	(0xD << 0)
51 #define MXL86110_EXT_RGMII_CFG1_TX_1G_DELAY_MASK	GENMASK(3, 0)
52 
53 #define MXL86110_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_1950PS	(0xD << 4)
54 #define MXL86110_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_MASK	GENMASK(7, 4)
55 
56 #define MXL86110_EXT_RGMII_CFG1_FULL_MASK \
57 			((MXL86110_EXT_RGMII_CFG1_RX_DELAY_MASK) | \
58 			(MXL86110_EXT_RGMII_CFG1_TX_1G_DELAY_MASK) | \
59 			(MXL86110_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_MASK))
60 
61 /* EXT Sleep Control register */
62 #define MXL86110_UTP_EXT_SLEEP_CTRL_REG			0x27
63 #define MXL86110_UTP_EXT_SLEEP_CTRL_EN_SLEEP_SW_OFF	0
64 #define MXL86110_UTP_EXT_SLEEP_CTRL_EN_SLEEP_SW_MASK	BIT(15)
65 
66 /* RGMII In-Band Status and MDIO Configuration Register */
67 #define MXL86110_EXT_RGMII_MDIO_CFG			0xA005
68 #define MXL86110_RGMII_MDIO_CFG_EPA0_MASK		GENMASK(6, 6)
69 #define MXL86110_EXT_RGMII_MDIO_CFG_EBA_MASK		GENMASK(5, 5)
70 #define MXL86110_EXT_RGMII_MDIO_CFG_BA_MASK		GENMASK(4, 0)
71 
72 #define MXL86110_MAX_LEDS	3
73 /* LED registers and defines */
74 #define MXL86110_LED0_CFG_REG 0xA00C
75 #define MXL86110_LED1_CFG_REG 0xA00D
76 #define MXL86110_LED2_CFG_REG 0xA00E
77 
78 #define MXL86110_LEDX_CFG_BLINK				BIT(13)
79 #define MXL86110_LEDX_CFG_LINK_UP_FULL_DUPLEX_ON	BIT(12)
80 #define MXL86110_LEDX_CFG_LINK_UP_HALF_DUPLEX_ON	BIT(11)
81 #define MXL86110_LEDX_CFG_LINK_UP_TX_ACT_ON		BIT(10)
82 #define MXL86110_LEDX_CFG_LINK_UP_RX_ACT_ON		BIT(9)
83 #define MXL86110_LEDX_CFG_LINK_UP_TX_ON			BIT(8)
84 #define MXL86110_LEDX_CFG_LINK_UP_RX_ON			BIT(7)
85 #define MXL86110_LEDX_CFG_LINK_UP_1GB_ON		BIT(6)
86 #define MXL86110_LEDX_CFG_LINK_UP_100MB_ON		BIT(5)
87 #define MXL86110_LEDX_CFG_LINK_UP_10MB_ON		BIT(4)
88 #define MXL86110_LEDX_CFG_LINK_UP_COLLISION		BIT(3)
89 #define MXL86110_LEDX_CFG_LINK_UP_1GB_BLINK		BIT(2)
90 #define MXL86110_LEDX_CFG_LINK_UP_100MB_BLINK		BIT(1)
91 #define MXL86110_LEDX_CFG_LINK_UP_10MB_BLINK		BIT(0)
92 
93 #define MXL86110_LED_BLINK_CFG_REG			0xA00F
94 #define MXL86110_LED_BLINK_CFG_FREQ_MODE1_2HZ		0
95 #define MXL86110_LED_BLINK_CFG_FREQ_MODE1_4HZ		BIT(0)
96 #define MXL86110_LED_BLINK_CFG_FREQ_MODE1_8HZ		BIT(1)
97 #define MXL86110_LED_BLINK_CFG_FREQ_MODE1_16HZ		(BIT(1) | BIT(0))
98 #define MXL86110_LED_BLINK_CFG_FREQ_MODE2_2HZ		0
99 #define MXL86110_LED_BLINK_CFG_FREQ_MODE2_4HZ		BIT(2)
100 #define MXL86110_LED_BLINK_CFG_FREQ_MODE2_8HZ		BIT(3)
101 #define MXL86110_LED_BLINK_CFG_FREQ_MODE2_16HZ		(BIT(3) | BIT(2))
102 #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_50_ON		0
103 #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_67_ON		(BIT(4))
104 #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_75_ON		(BIT(5))
105 #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_83_ON		(BIT(5) | BIT(4))
106 #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_50_OFF	(BIT(6))
107 #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_33_ON		(BIT(6) | BIT(4))
108 #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_25_ON		(BIT(6) | BIT(5))
109 #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_17_ON	(BIT(6) | BIT(5) | BIT(4))
110 
111 /* Chip Configuration Register - COM_EXT_CHIP_CFG */
112 #define MXL86110_EXT_CHIP_CFG_REG			0xA001
113 #define MXL86110_EXT_CHIP_CFG_RXDLY_ENABLE		BIT(8)
114 #define MXL86110_EXT_CHIP_CFG_SW_RST_N_MODE		BIT(15)
115 
116 /**
117  * __mxl86110_write_extended_reg() - write to a PHY's extended register
118  * @phydev: pointer to the PHY device structure
119  * @regnum: register number to write
120  * @val: value to write to @regnum
121  *
122  * Unlocked version of mxl86110_write_extended_reg
123  *
124  * Note: This function assumes the caller already holds the MDIO bus lock
125  * or otherwise has exclusive access to the PHY.
126  *
127  * Return: 0 or negative error code
128  */
129 static int __mxl86110_write_extended_reg(struct phy_device *phydev,
130 					 u16 regnum, u16 val)
131 {
132 	int ret;
133 
134 	ret = __phy_write(phydev, MXL86110_EXTD_REG_ADDR_OFFSET, regnum);
135 	if (ret < 0)
136 		return ret;
137 
138 	return __phy_write(phydev, MXL86110_EXTD_REG_ADDR_DATA, val);
139 }
140 
141 /**
142  * __mxl86110_read_extended_reg - Read a PHY's extended register
143  * @phydev: pointer to the PHY device structure
144  * @regnum: extended register number to read (address written to reg 30)
145  *
146  * Unlocked version of mxl86110_read_extended_reg
147  *
148  * Reads the content of a PHY extended register using the MaxLinear
149  * 2-step access mechanism: write the register address to reg 30 (0x1E),
150  * then read the value from reg 31 (0x1F).
151  *
152  * Note: This function assumes the caller already holds the MDIO bus lock
153  * or otherwise has exclusive access to the PHY.
154  *
155  * Return: 16-bit register value on success, or negative errno code on failure.
156  */
157 static int __mxl86110_read_extended_reg(struct phy_device *phydev, u16 regnum)
158 {
159 	int ret;
160 
161 	ret = __phy_write(phydev, MXL86110_EXTD_REG_ADDR_OFFSET, regnum);
162 	if (ret < 0)
163 		return ret;
164 	return __phy_read(phydev, MXL86110_EXTD_REG_ADDR_DATA);
165 }
166 
167 /**
168  * __mxl86110_modify_extended_reg() - modify bits of a PHY's extended register
169  * @phydev: pointer to the PHY device structure
170  * @regnum: register number to write
171  * @mask: bit mask of bits to clear
172  * @set: bit mask of bits to set
173  *
174  * Note: register value = (old register value & ~mask) | set.
175  * This function assumes the caller already holds the MDIO bus lock
176  * or otherwise has exclusive access to the PHY.
177  *
178  * Return: 0 or negative error code
179  */
180 static int __mxl86110_modify_extended_reg(struct phy_device *phydev,
181 					  u16 regnum, u16 mask, u16 set)
182 {
183 	int ret;
184 
185 	ret = __phy_write(phydev, MXL86110_EXTD_REG_ADDR_OFFSET, regnum);
186 	if (ret < 0)
187 		return ret;
188 
189 	return __phy_modify(phydev, MXL86110_EXTD_REG_ADDR_DATA, mask, set);
190 }
191 
192 /**
193  * mxl86110_write_extended_reg() - Write to a PHY's extended register
194  * @phydev: pointer to the PHY device structure
195  * @regnum: register number to write
196  * @val: value to write to @regnum
197  *
198  * This function writes to an extended register of the PHY using the
199  * MaxLinear two-step access method (reg 0x1E/0x1F). It handles acquiring
200  * and releasing the MDIO bus lock internally.
201  *
202  * Return: 0 or negative error code
203  */
204 static int mxl86110_write_extended_reg(struct phy_device *phydev,
205 				       u16 regnum, u16 val)
206 {
207 	int ret;
208 
209 	phy_lock_mdio_bus(phydev);
210 	ret = __mxl86110_write_extended_reg(phydev, regnum, val);
211 	phy_unlock_mdio_bus(phydev);
212 
213 	return ret;
214 }
215 
216 /**
217  * mxl86110_read_extended_reg() - Read a PHY's extended register
218  * @phydev: pointer to the PHY device structure
219  * @regnum: extended register number to read
220  *
221  * This function reads from an extended register of the PHY using the
222  * MaxLinear two-step access method (reg 0x1E/0x1F). It handles acquiring
223  * and releasing the MDIO bus lock internally.
224  *
225  * Return: 16-bit register value on success, or negative errno code on failure
226  */
227 static int mxl86110_read_extended_reg(struct phy_device *phydev, u16 regnum)
228 {
229 	int ret;
230 
231 	phy_lock_mdio_bus(phydev);
232 	ret = __mxl86110_read_extended_reg(phydev, regnum);
233 	phy_unlock_mdio_bus(phydev);
234 
235 	return ret;
236 }
237 
238 /**
239  * mxl86110_get_wol() - report if wake-on-lan is enabled
240  * @phydev: pointer to the phy_device
241  * @wol: a pointer to a &struct ethtool_wolinfo
242  */
243 static void mxl86110_get_wol(struct phy_device *phydev,
244 			     struct ethtool_wolinfo *wol)
245 {
246 	int val;
247 
248 	wol->supported = WAKE_MAGIC;
249 	wol->wolopts = 0;
250 	val = mxl86110_read_extended_reg(phydev, MXL86110_EXT_WOL_CFG_REG);
251 	if (val >= 0 && (val & MXL86110_WOL_CFG_WOL_MASK))
252 		wol->wolopts |= WAKE_MAGIC;
253 }
254 
255 /**
256  * mxl86110_set_wol() - enable/disable wake-on-lan
257  * @phydev: pointer to the phy_device
258  * @wol: a pointer to a &struct ethtool_wolinfo
259  *
260  * Configures the WOL Magic Packet MAC
261  *
262  * Return: 0 or negative errno code
263  */
264 static int mxl86110_set_wol(struct phy_device *phydev,
265 			    struct ethtool_wolinfo *wol)
266 {
267 	struct net_device *netdev;
268 	const unsigned char *mac;
269 	int ret = 0;
270 
271 	phy_lock_mdio_bus(phydev);
272 
273 	if (wol->wolopts & WAKE_MAGIC) {
274 		netdev = phydev->attached_dev;
275 		if (!netdev) {
276 			ret = -ENODEV;
277 			goto out;
278 		}
279 
280 		/* Configure the MAC address of the WOL magic packet */
281 		mac = netdev->dev_addr;
282 		ret = __mxl86110_write_extended_reg(phydev,
283 						    MXL86110_EXT_MAC_ADDR_CFG1,
284 						    ((mac[0] << 8) | mac[1]));
285 		if (ret < 0)
286 			goto out;
287 
288 		ret = __mxl86110_write_extended_reg(phydev,
289 						    MXL86110_EXT_MAC_ADDR_CFG2,
290 						    ((mac[2] << 8) | mac[3]));
291 		if (ret < 0)
292 			goto out;
293 
294 		ret = __mxl86110_write_extended_reg(phydev,
295 						    MXL86110_EXT_MAC_ADDR_CFG3,
296 						    ((mac[4] << 8) | mac[5]));
297 		if (ret < 0)
298 			goto out;
299 
300 		ret = __mxl86110_modify_extended_reg(phydev,
301 						     MXL86110_EXT_WOL_CFG_REG,
302 						     MXL86110_WOL_CFG_WOL_MASK,
303 						     MXL86110_WOL_CFG_WOL_MASK);
304 		if (ret < 0)
305 			goto out;
306 
307 		/* Enables Wake-on-LAN interrupt in the PHY. */
308 		ret = __phy_modify(phydev, PHY_IRQ_ENABLE_REG, 0,
309 				   PHY_IRQ_ENABLE_REG_WOL);
310 		if (ret < 0)
311 			goto out;
312 
313 		phydev_dbg(phydev,
314 			   "%s, MAC Addr: %02X:%02X:%02X:%02X:%02X:%02X\n",
315 			   __func__,
316 			   mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
317 	} else {
318 		ret = __mxl86110_modify_extended_reg(phydev,
319 						     MXL86110_EXT_WOL_CFG_REG,
320 						     MXL86110_WOL_CFG_WOL_MASK,
321 						     0);
322 		if (ret < 0)
323 			goto out;
324 
325 		/* Disables Wake-on-LAN interrupt in the PHY. */
326 		ret = __phy_modify(phydev, PHY_IRQ_ENABLE_REG,
327 				   PHY_IRQ_ENABLE_REG_WOL, 0);
328 	}
329 
330 out:
331 	phy_unlock_mdio_bus(phydev);
332 	return ret;
333 }
334 
335 static const unsigned long supported_trgs = (BIT(TRIGGER_NETDEV_LINK_10) |
336 					     BIT(TRIGGER_NETDEV_LINK_100) |
337 					     BIT(TRIGGER_NETDEV_LINK_1000) |
338 					     BIT(TRIGGER_NETDEV_HALF_DUPLEX) |
339 					     BIT(TRIGGER_NETDEV_FULL_DUPLEX) |
340 					     BIT(TRIGGER_NETDEV_TX) |
341 					     BIT(TRIGGER_NETDEV_RX));
342 
343 static int mxl86110_led_hw_is_supported(struct phy_device *phydev, u8 index,
344 					unsigned long rules)
345 {
346 	if (index >= MXL86110_MAX_LEDS)
347 		return -EINVAL;
348 
349 	/* All combinations of the supported triggers are allowed */
350 	if (rules & ~supported_trgs)
351 		return -EOPNOTSUPP;
352 
353 	return 0;
354 }
355 
356 static int mxl86110_led_hw_control_get(struct phy_device *phydev, u8 index,
357 				       unsigned long *rules)
358 {
359 	int val;
360 
361 	if (index >= MXL86110_MAX_LEDS)
362 		return -EINVAL;
363 
364 	val = mxl86110_read_extended_reg(phydev,
365 					 MXL86110_LED0_CFG_REG + index);
366 	if (val < 0)
367 		return val;
368 
369 	if (val & MXL86110_LEDX_CFG_LINK_UP_TX_ACT_ON)
370 		*rules |= BIT(TRIGGER_NETDEV_TX);
371 
372 	if (val & MXL86110_LEDX_CFG_LINK_UP_RX_ACT_ON)
373 		*rules |= BIT(TRIGGER_NETDEV_RX);
374 
375 	if (val & MXL86110_LEDX_CFG_LINK_UP_HALF_DUPLEX_ON)
376 		*rules |= BIT(TRIGGER_NETDEV_HALF_DUPLEX);
377 
378 	if (val & MXL86110_LEDX_CFG_LINK_UP_FULL_DUPLEX_ON)
379 		*rules |= BIT(TRIGGER_NETDEV_FULL_DUPLEX);
380 
381 	if (val & MXL86110_LEDX_CFG_LINK_UP_10MB_ON)
382 		*rules |= BIT(TRIGGER_NETDEV_LINK_10);
383 
384 	if (val & MXL86110_LEDX_CFG_LINK_UP_100MB_ON)
385 		*rules |= BIT(TRIGGER_NETDEV_LINK_100);
386 
387 	if (val & MXL86110_LEDX_CFG_LINK_UP_1GB_ON)
388 		*rules |= BIT(TRIGGER_NETDEV_LINK_1000);
389 
390 	return 0;
391 }
392 
393 static int mxl86110_led_hw_control_set(struct phy_device *phydev, u8 index,
394 				       unsigned long rules)
395 {
396 	u16 val = 0;
397 
398 	if (index >= MXL86110_MAX_LEDS)
399 		return -EINVAL;
400 
401 	if (rules & BIT(TRIGGER_NETDEV_LINK_10))
402 		val |= MXL86110_LEDX_CFG_LINK_UP_10MB_ON;
403 
404 	if (rules & BIT(TRIGGER_NETDEV_LINK_100))
405 		val |= MXL86110_LEDX_CFG_LINK_UP_100MB_ON;
406 
407 	if (rules & BIT(TRIGGER_NETDEV_LINK_1000))
408 		val |= MXL86110_LEDX_CFG_LINK_UP_1GB_ON;
409 
410 	if (rules & BIT(TRIGGER_NETDEV_TX))
411 		val |= MXL86110_LEDX_CFG_LINK_UP_TX_ACT_ON;
412 
413 	if (rules & BIT(TRIGGER_NETDEV_RX))
414 		val |= MXL86110_LEDX_CFG_LINK_UP_RX_ACT_ON;
415 
416 	if (rules & BIT(TRIGGER_NETDEV_HALF_DUPLEX))
417 		val |= MXL86110_LEDX_CFG_LINK_UP_HALF_DUPLEX_ON;
418 
419 	if (rules & BIT(TRIGGER_NETDEV_FULL_DUPLEX))
420 		val |= MXL86110_LEDX_CFG_LINK_UP_FULL_DUPLEX_ON;
421 
422 	if (rules & BIT(TRIGGER_NETDEV_TX) ||
423 	    rules & BIT(TRIGGER_NETDEV_RX))
424 		val |= MXL86110_LEDX_CFG_BLINK;
425 
426 	return mxl86110_write_extended_reg(phydev,
427 					  MXL86110_LED0_CFG_REG + index, val);
428 }
429 
430 /**
431  * mxl86110_synce_clk_cfg() - applies syncE/clk output configuration
432  * @phydev: pointer to the phy_device
433  *
434  * Note: This function assumes the caller already holds the MDIO bus lock
435  * or otherwise has exclusive access to the PHY.
436  *
437  * Return: 0 or negative errno code
438  */
439 static int mxl86110_synce_clk_cfg(struct phy_device *phydev)
440 {
441 	u16 mask = 0, val = 0;
442 
443 	/*
444 	 * Configures the clock output to its default
445 	 * setting as per the datasheet.
446 	 * This results in a 25MHz clock output being selected in the
447 	 * COM_EXT_SYNCE_CFG register for SyncE configuration.
448 	 */
449 	val = MXL86110_EXT_SYNCE_CFG_EN_SYNC_E |
450 			FIELD_PREP(MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_MASK,
451 				   MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_25M);
452 	mask = MXL86110_EXT_SYNCE_CFG_EN_SYNC_E |
453 	       MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_MASK |
454 	       MXL86110_EXT_SYNCE_CFG_CLK_FRE_SEL;
455 
456 	/* Write clock output configuration */
457 	return __mxl86110_modify_extended_reg(phydev,
458 					      MXL86110_EXT_SYNCE_CFG_REG,
459 					      mask, val);
460 }
461 
462 /**
463  * mxl86110_broadcast_cfg - Configure MDIO broadcast setting for PHY
464  * @phydev: Pointer to the PHY device structure
465  *
466  * This function configures the MDIO broadcast behavior of the MxL86110 PHY.
467  * Currently, broadcast mode is explicitly disabled by clearing the EPA0 bit
468  * in the RGMII_MDIO_CFG extended register.
469  *
470  * Note: This function assumes the caller already holds the MDIO bus lock
471  * or otherwise has exclusive access to the PHY.
472  *
473  * Return: 0 on success or a negative errno code on failure.
474  */
475 static int mxl86110_broadcast_cfg(struct phy_device *phydev)
476 {
477 	return __mxl86110_modify_extended_reg(phydev,
478 					      MXL86110_EXT_RGMII_MDIO_CFG,
479 					      MXL86110_RGMII_MDIO_CFG_EPA0_MASK,
480 					      0);
481 }
482 
483 /**
484  * mxl86110_enable_led_activity_blink - Enable LEDs activity blink on PHY
485  * @phydev: Pointer to the PHY device structure
486  *
487  * Configure all PHY LEDs to blink on traffic activity regardless of whether
488  * they are ON or OFF. This behavior allows each LED to serve as a pure activity
489  * indicator, independently of its use as a link status indicator.
490  *
491  * By default, each LED blinks only when it is also in the ON state.
492  * This function modifies the appropriate registers (LABx fields)
493  * to enable blinking even when the LEDs are OFF, to allow the LED to be used
494  * as a traffic indicator without requiring it to also serve
495  * as a link status LED.
496  *
497  * Note: Any further LED customization can be performed via the
498  * /sys/class/leds interface; the functions led_hw_is_supported,
499  * led_hw_control_get, and led_hw_control_set are used
500  * to support this mechanism.
501  *
502  * This function assumes the caller already holds the MDIO bus lock
503  * or otherwise has exclusive access to the PHY.
504  *
505  * Return: 0 on success or a negative errno code on failure.
506  */
507 static int mxl86110_enable_led_activity_blink(struct phy_device *phydev)
508 {
509 	int i, ret = 0;
510 
511 	for (i = 0; i < MXL86110_MAX_LEDS; i++) {
512 		ret = __mxl86110_modify_extended_reg(phydev,
513 						     MXL86110_LED0_CFG_REG + i,
514 						     0,
515 						     MXL86110_LEDX_CFG_BLINK);
516 		if (ret < 0)
517 			break;
518 	}
519 
520 	return ret;
521 }
522 
523 /**
524  * mxl86110_config_init() - initialize the PHY
525  * @phydev: pointer to the phy_device
526  *
527  * Return: 0 or negative errno code
528  */
529 static int mxl86110_config_init(struct phy_device *phydev)
530 {
531 	u16 val = 0;
532 	int ret;
533 
534 	phy_lock_mdio_bus(phydev);
535 
536 	/* configure syncE / clk output */
537 	ret = mxl86110_synce_clk_cfg(phydev);
538 	if (ret < 0)
539 		goto out;
540 
541 	switch (phydev->interface) {
542 	case PHY_INTERFACE_MODE_RGMII:
543 		val = 0;
544 		break;
545 	case PHY_INTERFACE_MODE_RGMII_RXID:
546 		val = MXL86110_EXT_RGMII_CFG1_RX_DELAY_1950PS;
547 		break;
548 	case PHY_INTERFACE_MODE_RGMII_TXID:
549 		val = MXL86110_EXT_RGMII_CFG1_TX_1G_DELAY_1950PS |
550 			MXL86110_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_1950PS;
551 		break;
552 	case PHY_INTERFACE_MODE_RGMII_ID:
553 		val = MXL86110_EXT_RGMII_CFG1_TX_1G_DELAY_1950PS |
554 			MXL86110_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_1950PS |
555 			MXL86110_EXT_RGMII_CFG1_RX_DELAY_1950PS;
556 		break;
557 	default:
558 		ret = -EINVAL;
559 		goto out;
560 	}
561 
562 	ret = __mxl86110_modify_extended_reg(phydev,
563 					     MXL86110_EXT_RGMII_CFG1_REG,
564 					     MXL86110_EXT_RGMII_CFG1_FULL_MASK,
565 					     val);
566 	if (ret < 0)
567 		goto out;
568 
569 	/* Configure RXDLY (RGMII Rx Clock Delay) to disable
570 	 * the default additional delay value on RX_CLK
571 	 * (2 ns for 125 MHz, 8 ns for 25 MHz/2.5 MHz)
572 	 * and use just the digital one selected before
573 	 */
574 	ret = __mxl86110_modify_extended_reg(phydev,
575 					     MXL86110_EXT_CHIP_CFG_REG,
576 					     MXL86110_EXT_CHIP_CFG_RXDLY_ENABLE,
577 					     0);
578 	if (ret < 0)
579 		goto out;
580 
581 	ret = mxl86110_enable_led_activity_blink(phydev);
582 	if (ret < 0)
583 		goto out;
584 
585 	ret = mxl86110_broadcast_cfg(phydev);
586 
587 out:
588 	phy_unlock_mdio_bus(phydev);
589 	return ret;
590 }
591 
592 static struct phy_driver mxl_phy_drvs[] = {
593 	{
594 		PHY_ID_MATCH_EXACT(PHY_ID_MXL86110),
595 		.name			= "MXL86110 Gigabit Ethernet",
596 		.config_init		= mxl86110_config_init,
597 		.get_wol		= mxl86110_get_wol,
598 		.set_wol		= mxl86110_set_wol,
599 		.led_hw_is_supported	= mxl86110_led_hw_is_supported,
600 		.led_hw_control_get     = mxl86110_led_hw_control_get,
601 		.led_hw_control_set     = mxl86110_led_hw_control_set,
602 	},
603 };
604 
605 module_phy_driver(mxl_phy_drvs);
606 
607 static const struct mdio_device_id __maybe_unused mxl_tbl[] = {
608 	{ PHY_ID_MATCH_EXACT(PHY_ID_MXL86110) },
609 	{  }
610 };
611 
612 MODULE_DEVICE_TABLE(mdio, mxl_tbl);
613 
614 MODULE_DESCRIPTION("MaxLinear MXL86110 PHY driver");
615 MODULE_AUTHOR("Stefano Radaelli");
616 MODULE_LICENSE("GPL");
617