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 */
__mxl86110_write_extended_reg(struct phy_device * phydev,u16 regnum,u16 val)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 */
__mxl86110_read_extended_reg(struct phy_device * phydev,u16 regnum)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 */
__mxl86110_modify_extended_reg(struct phy_device * phydev,u16 regnum,u16 mask,u16 set)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 */
mxl86110_write_extended_reg(struct phy_device * phydev,u16 regnum,u16 val)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 */
mxl86110_read_extended_reg(struct phy_device * phydev,u16 regnum)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 */
mxl86110_get_wol(struct phy_device * phydev,struct ethtool_wolinfo * wol)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 */
mxl86110_set_wol(struct phy_device * phydev,struct ethtool_wolinfo * wol)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
mxl86110_led_hw_is_supported(struct phy_device * phydev,u8 index,unsigned long rules)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
mxl86110_led_hw_control_get(struct phy_device * phydev,u8 index,unsigned long * rules)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
mxl86110_led_hw_control_set(struct phy_device * phydev,u8 index,unsigned long rules)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 */
mxl86110_synce_clk_cfg(struct phy_device * phydev)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 */
mxl86110_broadcast_cfg(struct phy_device * phydev)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 */
mxl86110_enable_led_activity_blink(struct phy_device * phydev)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 */
mxl86110_config_init(struct phy_device * phydev)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