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 #define PHY_ID_MXL86111 0xc1335588
19
20 /* required to access extended registers */
21 #define MXL86110_EXTD_REG_ADDR_OFFSET 0x1E
22 #define MXL86110_EXTD_REG_ADDR_DATA 0x1F
23 #define PHY_IRQ_ENABLE_REG 0x12
24 #define PHY_IRQ_ENABLE_REG_WOL BIT(6)
25
26 /* different pages for EXTD access for MXL86111 */
27 /* SerDes/PHY Control Access Register - COM_EXT_SMI_SDS_PHY */
28 #define MXL86111_EXT_SMI_SDS_PHY_REG 0xA000
29 #define MXL86111_EXT_SMI_SDS_PHYSPACE_MASK BIT(1)
30 #define MXL86111_EXT_SMI_SDS_PHYFIBER_SPACE (0x1 << 1)
31 #define MXL86111_EXT_SMI_SDS_PHYUTP_SPACE (0x0 << 1)
32 #define MXL86111_EXT_SMI_SDS_PHY_AUTO 0xff
33
34 /* SyncE Configuration Register - COM_EXT_SYNCE_CFG */
35 #define MXL86110_EXT_SYNCE_CFG_REG 0xA012
36 #define MXL86110_EXT_SYNCE_CFG_CLK_FRE_SEL BIT(4)
37 #define MXL86110_EXT_SYNCE_CFG_EN_SYNC_E_DURING_LNKDN BIT(5)
38 #define MXL86110_EXT_SYNCE_CFG_EN_SYNC_E BIT(6)
39 #define MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_MASK GENMASK(3, 1)
40 #define MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_125M_PLL 0
41 #define MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_25M 4
42
43 /* MAC Address registers */
44 #define MXL86110_EXT_MAC_ADDR_CFG1 0xA007
45 #define MXL86110_EXT_MAC_ADDR_CFG2 0xA008
46 #define MXL86110_EXT_MAC_ADDR_CFG3 0xA009
47
48 #define MXL86110_EXT_WOL_CFG_REG 0xA00A
49 #define MXL86110_WOL_CFG_WOL_MASK BIT(3)
50
51 /* RGMII register */
52 #define MXL86110_EXT_RGMII_CFG1_REG 0xA003
53 /* delay can be adjusted in steps of about 150ps */
54 #define MXL86110_EXT_RGMII_CFG1_RX_NO_DELAY (0x0 << 10)
55 /* Closest value to 2000 ps */
56 #define MXL86110_EXT_RGMII_CFG1_RX_DELAY_1950PS (0xD << 10)
57 #define MXL86110_EXT_RGMII_CFG1_RX_DELAY_MASK GENMASK(13, 10)
58
59 #define MXL86110_EXT_RGMII_CFG1_TX_1G_DELAY_1950PS (0xD << 0)
60 #define MXL86110_EXT_RGMII_CFG1_TX_1G_DELAY_MASK GENMASK(3, 0)
61
62 #define MXL86110_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_1950PS (0xD << 4)
63 #define MXL86110_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_MASK GENMASK(7, 4)
64
65 #define MXL86110_EXT_RGMII_CFG1_FULL_MASK \
66 ((MXL86110_EXT_RGMII_CFG1_RX_DELAY_MASK) | \
67 (MXL86110_EXT_RGMII_CFG1_TX_1G_DELAY_MASK) | \
68 (MXL86110_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_MASK))
69
70 /* EXT Sleep Control register */
71 #define MXL86110_UTP_EXT_SLEEP_CTRL_REG 0x27
72 #define MXL86110_UTP_EXT_SLEEP_CTRL_EN_SLEEP_SW_OFF 0
73 #define MXL86110_UTP_EXT_SLEEP_CTRL_EN_SLEEP_SW_MASK BIT(15)
74
75 /* RGMII In-Band Status and MDIO Configuration Register */
76 #define MXL86110_EXT_RGMII_MDIO_CFG 0xA005
77 #define MXL86110_RGMII_MDIO_CFG_EPA0_MASK GENMASK(6, 6)
78 #define MXL86110_EXT_RGMII_MDIO_CFG_EBA_MASK GENMASK(5, 5)
79 #define MXL86110_EXT_RGMII_MDIO_CFG_BA_MASK GENMASK(4, 0)
80
81 #define MXL86110_MAX_LEDS 3
82 /* LED registers and defines */
83 #define MXL86110_COM_EXT_LED_GEN_CFG 0xA00B
84 # define MXL86110_COM_EXT_LED_GEN_CFG_LFM(x) ((BIT(0) | BIT(1)) << (3 * (x)))
85 # define MXL86110_COM_EXT_LED_GEN_CFG_LFME(x) (BIT(0) << (3 * (x)))
86 # define MXL86110_COM_EXT_LED_GEN_CFG_LFE(x) (BIT(2) << (3 * (x)))
87
88 #define MXL86110_LED0_CFG_REG 0xA00C
89 #define MXL86110_LED1_CFG_REG 0xA00D
90 #define MXL86110_LED2_CFG_REG 0xA00E
91
92 #define MXL86110_LEDX_CFG_BLINK BIT(13)
93 #define MXL86110_LEDX_CFG_LINK_UP_FULL_DUPLEX_ON BIT(12)
94 #define MXL86110_LEDX_CFG_LINK_UP_HALF_DUPLEX_ON BIT(11)
95 #define MXL86110_LEDX_CFG_LINK_UP_TX_ACT_ON BIT(10)
96 #define MXL86110_LEDX_CFG_LINK_UP_RX_ACT_ON BIT(9)
97 #define MXL86110_LEDX_CFG_LINK_UP_TX_ON BIT(8)
98 #define MXL86110_LEDX_CFG_LINK_UP_RX_ON BIT(7)
99 #define MXL86110_LEDX_CFG_LINK_UP_1GB_ON BIT(6)
100 #define MXL86110_LEDX_CFG_LINK_UP_100MB_ON BIT(5)
101 #define MXL86110_LEDX_CFG_LINK_UP_10MB_ON BIT(4)
102 #define MXL86110_LEDX_CFG_LINK_UP_COLLISION BIT(3)
103 #define MXL86110_LEDX_CFG_LINK_UP_1GB_BLINK BIT(2)
104 #define MXL86110_LEDX_CFG_LINK_UP_100MB_BLINK BIT(1)
105 #define MXL86110_LEDX_CFG_LINK_UP_10MB_BLINK BIT(0)
106
107 #define MXL86110_LED_BLINK_CFG_REG 0xA00F
108 #define MXL86110_LED_BLINK_CFG_FREQ_MODE1_2HZ 0
109 #define MXL86110_LED_BLINK_CFG_FREQ_MODE1_4HZ BIT(0)
110 #define MXL86110_LED_BLINK_CFG_FREQ_MODE1_8HZ BIT(1)
111 #define MXL86110_LED_BLINK_CFG_FREQ_MODE1_16HZ (BIT(1) | BIT(0))
112 #define MXL86110_LED_BLINK_CFG_FREQ_MODE2_2HZ 0
113 #define MXL86110_LED_BLINK_CFG_FREQ_MODE2_4HZ BIT(2)
114 #define MXL86110_LED_BLINK_CFG_FREQ_MODE2_8HZ BIT(3)
115 #define MXL86110_LED_BLINK_CFG_FREQ_MODE2_16HZ (BIT(3) | BIT(2))
116 #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_50_ON 0
117 #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_67_ON (BIT(4))
118 #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_75_ON (BIT(5))
119 #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_83_ON (BIT(5) | BIT(4))
120 #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_50_OFF (BIT(6))
121 #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_33_ON (BIT(6) | BIT(4))
122 #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_25_ON (BIT(6) | BIT(5))
123 #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_17_ON (BIT(6) | BIT(5) | BIT(4))
124
125 /* Chip Configuration Register - COM_EXT_CHIP_CFG */
126 #define MXL86110_EXT_CHIP_CFG_REG 0xA001
127 #define MXL86111_EXT_CHIP_CFG_MODE_SEL_MASK GENMASK(2, 0)
128 #define MXL86111_EXT_CHIP_CFG_MODE_UTP_TO_RGMII 0
129 #define MXL86111_EXT_CHIP_CFG_MODE_FIBER_TO_RGMII 1
130 #define MXL86111_EXT_CHIP_CFG_MODE_UTP_FIBER_TO_RGMII 2
131 #define MXL86111_EXT_CHIP_CFG_MODE_UTP_TO_SGMII 3
132 #define MXL86111_EXT_CHIP_CFG_MODE_SGPHY_TO_RGMAC 4
133 #define MXL86111_EXT_CHIP_CFG_MODE_SGMAC_TO_RGPHY 5
134 #define MXL86111_EXT_CHIP_CFG_MODE_UTP_TO_FIBER_AUTO 6
135 #define MXL86111_EXT_CHIP_CFG_MODE_UTP_TO_FIBER_FORCE 7
136
137 #define MXL86111_EXT_CHIP_CFG_CLDO_MASK GENMASK(5, 4)
138 #define MXL86111_EXT_CHIP_CFG_CLDO_3V3 0
139 #define MXL86111_EXT_CHIP_CFG_CLDO_2V5 1
140 #define MXL86111_EXT_CHIP_CFG_CLDO_1V8_2 2
141 #define MXL86111_EXT_CHIP_CFG_CLDO_1V8_3 3
142 #define MXL86111_EXT_CHIP_CFG_CLDO_SHIFT 4
143 #define MXL86111_EXT_CHIP_CFG_ELDO BIT(6)
144 #define MXL86110_EXT_CHIP_CFG_RXDLY_ENABLE BIT(8)
145 #define MXL86110_EXT_CHIP_CFG_SW_RST_N_MODE BIT(15)
146
147 /* Specific Status Register - PHY_STAT */
148 #define MXL86111_PHY_STAT_REG 0x11
149 #define MXL86111_PHY_STAT_SPEED_MASK GENMASK(15, 14)
150 #define MXL86111_PHY_STAT_SPEED_OFFSET 14
151 #define MXL86111_PHY_STAT_SPEED_10M 0x0
152 #define MXL86111_PHY_STAT_SPEED_100M 0x1
153 #define MXL86111_PHY_STAT_SPEED_1000M 0x2
154 #define MXL86111_PHY_STAT_DPX_OFFSET 13
155 #define MXL86111_PHY_STAT_DPX BIT(13)
156 #define MXL86111_PHY_STAT_LSRT BIT(10)
157
158 /* 3 phy reg page modes,auto mode combines utp and fiber mode*/
159 #define MXL86111_MODE_FIBER 0x1
160 #define MXL86111_MODE_UTP 0x2
161 #define MXL86111_MODE_AUTO 0x3
162
163 /* FIBER Auto-Negotiation link partner ability - SDS_AN_LPA */
164 #define MXL86111_SDS_AN_LPA_PAUSE (0x3 << 7)
165 #define MXL86111_SDS_AN_LPA_ASYM_PAUSE (0x2 << 7)
166
167 /* Miscellaneous Control Register - COM_EXT _MISC_CFG */
168 #define MXL86111_EXT_MISC_CONFIG_REG 0xa006
169 #define MXL86111_EXT_MISC_CONFIG_FIB_SPEED_SEL BIT(0)
170 #define MXL86111_EXT_MISC_CONFIG_FIB_SPEED_SEL_1000BX (0x1 << 0)
171 #define MXL86111_EXT_MISC_CONFIG_FIB_SPEED_SEL_100BX (0x0 << 0)
172
173 /* Phy fiber Link timer cfg2 Register - EXT_SDS_LINK_TIMER_CFG2 */
174 #define MXL86111_EXT_SDS_LINK_TIMER_CFG2_REG 0xA5
175 #define MXL86111_EXT_SDS_LINK_TIMER_CFG2_EN_AUTOSEN BIT(15)
176
177 /* default values of PHY register, required for Dual Media mode */
178 #define MII_BMSR_DEFAULT_VAL 0x7949
179 #define MII_ESTATUS_DEFAULT_VAL 0x2000
180
181 /* Timeout in ms for PHY SW reset check in STD_CTRL/SDS_CTRL */
182 #define BMCR_RESET_TIMEOUT 500
183
184 /* PL P1 requires optimized RGMII timing for 1.8V RGMII voltage
185 */
186 #define MXL86111_PL_P1 0x500
187
188 /**
189 * __mxl86110_write_extended_reg() - write to a PHY's extended register
190 * @phydev: pointer to the PHY device structure
191 * @regnum: register number to write
192 * @val: value to write to @regnum
193 *
194 * Unlocked version of mxl86110_write_extended_reg
195 *
196 * Note: This function assumes the caller already holds the MDIO bus lock
197 * or otherwise has exclusive access to the PHY.
198 *
199 * Return: 0 or negative error code
200 */
__mxl86110_write_extended_reg(struct phy_device * phydev,u16 regnum,u16 val)201 static int __mxl86110_write_extended_reg(struct phy_device *phydev,
202 u16 regnum, u16 val)
203 {
204 int ret;
205
206 ret = __phy_write(phydev, MXL86110_EXTD_REG_ADDR_OFFSET, regnum);
207 if (ret < 0)
208 return ret;
209
210 return __phy_write(phydev, MXL86110_EXTD_REG_ADDR_DATA, val);
211 }
212
213 /**
214 * __mxl86110_read_extended_reg - Read a PHY's extended register
215 * @phydev: pointer to the PHY device structure
216 * @regnum: extended register number to read (address written to reg 30)
217 *
218 * Unlocked version of mxl86110_read_extended_reg
219 *
220 * Reads the content of a PHY extended register using the MaxLinear
221 * 2-step access mechanism: write the register address to reg 30 (0x1E),
222 * then read the value from reg 31 (0x1F).
223 *
224 * Note: This function assumes the caller already holds the MDIO bus lock
225 * or otherwise has exclusive access to the PHY.
226 *
227 * Return: 16-bit register value on success, or negative errno code on failure.
228 */
__mxl86110_read_extended_reg(struct phy_device * phydev,u16 regnum)229 static int __mxl86110_read_extended_reg(struct phy_device *phydev, u16 regnum)
230 {
231 int ret;
232
233 ret = __phy_write(phydev, MXL86110_EXTD_REG_ADDR_OFFSET, regnum);
234 if (ret < 0)
235 return ret;
236 return __phy_read(phydev, MXL86110_EXTD_REG_ADDR_DATA);
237 }
238
239 /**
240 * __mxl86110_modify_extended_reg() - modify bits of a PHY's extended register
241 * @phydev: pointer to the PHY device structure
242 * @regnum: register number to write
243 * @mask: bit mask of bits to clear
244 * @set: bit mask of bits to set
245 *
246 * Note: register value = (old register value & ~mask) | set.
247 * This function assumes the caller already holds the MDIO bus lock
248 * or otherwise has exclusive access to the PHY.
249 *
250 * Return: 0 or negative error code
251 */
__mxl86110_modify_extended_reg(struct phy_device * phydev,u16 regnum,u16 mask,u16 set)252 static int __mxl86110_modify_extended_reg(struct phy_device *phydev,
253 u16 regnum, u16 mask, u16 set)
254 {
255 int ret;
256
257 ret = __phy_write(phydev, MXL86110_EXTD_REG_ADDR_OFFSET, regnum);
258 if (ret < 0)
259 return ret;
260
261 return __phy_modify(phydev, MXL86110_EXTD_REG_ADDR_DATA, mask, set);
262 }
263
264 /**
265 * mxl86110_write_extended_reg() - Write to a PHY's extended register
266 * @phydev: pointer to the PHY device structure
267 * @regnum: register number to write
268 * @val: value to write to @regnum
269 *
270 * This function writes to an extended register of the PHY using the
271 * MaxLinear two-step access method (reg 0x1E/0x1F). It handles acquiring
272 * and releasing the MDIO bus lock internally.
273 *
274 * Return: 0 or negative error code
275 */
mxl86110_write_extended_reg(struct phy_device * phydev,u16 regnum,u16 val)276 static int mxl86110_write_extended_reg(struct phy_device *phydev,
277 u16 regnum, u16 val)
278 {
279 int ret;
280
281 phy_lock_mdio_bus(phydev);
282 ret = __mxl86110_write_extended_reg(phydev, regnum, val);
283 phy_unlock_mdio_bus(phydev);
284
285 return ret;
286 }
287
288 /**
289 * mxl86110_read_extended_reg() - Read a PHY's extended register
290 * @phydev: pointer to the PHY device structure
291 * @regnum: extended register number to read
292 *
293 * This function reads from an extended register of the PHY using the
294 * MaxLinear two-step access method (reg 0x1E/0x1F). It handles acquiring
295 * and releasing the MDIO bus lock internally.
296 *
297 * Return: 16-bit register value on success, or negative errno code on failure
298 */
mxl86110_read_extended_reg(struct phy_device * phydev,u16 regnum)299 static int mxl86110_read_extended_reg(struct phy_device *phydev, u16 regnum)
300 {
301 int ret;
302
303 phy_lock_mdio_bus(phydev);
304 ret = __mxl86110_read_extended_reg(phydev, regnum);
305 phy_unlock_mdio_bus(phydev);
306
307 return ret;
308 }
309
310 /**
311 * mxl86110_modify_extended_reg() - modify bits of a PHY's extended register
312 * @phydev: pointer to the PHY device structure
313 * @regnum: register number to write
314 * @mask: bit mask of bits to clear
315 * @set: bit mask of bits to set
316 *
317 * Note: register value = (old register value & ~mask) | set.
318 *
319 * Return: 0 or negative error code
320 */
mxl86110_modify_extended_reg(struct phy_device * phydev,u16 regnum,u16 mask,u16 set)321 static int mxl86110_modify_extended_reg(struct phy_device *phydev,
322 u16 regnum, u16 mask, u16 set)
323 {
324 int ret;
325
326 phy_lock_mdio_bus(phydev);
327 ret = __mxl86110_modify_extended_reg(phydev, regnum, mask, set);
328 phy_unlock_mdio_bus(phydev);
329
330 return ret;
331 }
332
333 /**
334 * mxl86110_get_wol() - report if wake-on-lan is enabled
335 * @phydev: pointer to the phy_device
336 * @wol: a pointer to a &struct ethtool_wolinfo
337 */
mxl86110_get_wol(struct phy_device * phydev,struct ethtool_wolinfo * wol)338 static void mxl86110_get_wol(struct phy_device *phydev,
339 struct ethtool_wolinfo *wol)
340 {
341 int val;
342
343 wol->supported = WAKE_MAGIC;
344 wol->wolopts = 0;
345 val = mxl86110_read_extended_reg(phydev, MXL86110_EXT_WOL_CFG_REG);
346 if (val >= 0 && (val & MXL86110_WOL_CFG_WOL_MASK))
347 wol->wolopts |= WAKE_MAGIC;
348 }
349
350 /**
351 * mxl86110_set_wol() - enable/disable wake-on-lan
352 * @phydev: pointer to the phy_device
353 * @wol: a pointer to a &struct ethtool_wolinfo
354 *
355 * Configures the WOL Magic Packet MAC
356 *
357 * Return: 0 or negative errno code
358 */
mxl86110_set_wol(struct phy_device * phydev,struct ethtool_wolinfo * wol)359 static int mxl86110_set_wol(struct phy_device *phydev,
360 struct ethtool_wolinfo *wol)
361 {
362 struct net_device *netdev;
363 const unsigned char *mac;
364 int ret = 0;
365
366 phy_lock_mdio_bus(phydev);
367
368 if (wol->wolopts & WAKE_MAGIC) {
369 netdev = phydev->attached_dev;
370 if (!netdev) {
371 ret = -ENODEV;
372 goto out;
373 }
374
375 /* Configure the MAC address of the WOL magic packet */
376 mac = netdev->dev_addr;
377 ret = __mxl86110_write_extended_reg(phydev,
378 MXL86110_EXT_MAC_ADDR_CFG1,
379 ((mac[0] << 8) | mac[1]));
380 if (ret < 0)
381 goto out;
382
383 ret = __mxl86110_write_extended_reg(phydev,
384 MXL86110_EXT_MAC_ADDR_CFG2,
385 ((mac[2] << 8) | mac[3]));
386 if (ret < 0)
387 goto out;
388
389 ret = __mxl86110_write_extended_reg(phydev,
390 MXL86110_EXT_MAC_ADDR_CFG3,
391 ((mac[4] << 8) | mac[5]));
392 if (ret < 0)
393 goto out;
394
395 ret = __mxl86110_modify_extended_reg(phydev,
396 MXL86110_EXT_WOL_CFG_REG,
397 MXL86110_WOL_CFG_WOL_MASK,
398 MXL86110_WOL_CFG_WOL_MASK);
399 if (ret < 0)
400 goto out;
401
402 /* Enables Wake-on-LAN interrupt in the PHY. */
403 ret = __phy_modify(phydev, PHY_IRQ_ENABLE_REG, 0,
404 PHY_IRQ_ENABLE_REG_WOL);
405 if (ret < 0)
406 goto out;
407
408 phydev_dbg(phydev,
409 "%s, MAC Addr: %02X:%02X:%02X:%02X:%02X:%02X\n",
410 __func__,
411 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
412 } else {
413 ret = __mxl86110_modify_extended_reg(phydev,
414 MXL86110_EXT_WOL_CFG_REG,
415 MXL86110_WOL_CFG_WOL_MASK,
416 0);
417 if (ret < 0)
418 goto out;
419
420 /* Disables Wake-on-LAN interrupt in the PHY. */
421 ret = __phy_modify(phydev, PHY_IRQ_ENABLE_REG,
422 PHY_IRQ_ENABLE_REG_WOL, 0);
423 }
424
425 out:
426 phy_unlock_mdio_bus(phydev);
427 return ret;
428 }
429
430 static const unsigned long supported_trgs = (BIT(TRIGGER_NETDEV_LINK_10) |
431 BIT(TRIGGER_NETDEV_LINK_100) |
432 BIT(TRIGGER_NETDEV_LINK_1000) |
433 BIT(TRIGGER_NETDEV_HALF_DUPLEX) |
434 BIT(TRIGGER_NETDEV_FULL_DUPLEX) |
435 BIT(TRIGGER_NETDEV_TX) |
436 BIT(TRIGGER_NETDEV_RX));
437
mxl86110_led_hw_is_supported(struct phy_device * phydev,u8 index,unsigned long rules)438 static int mxl86110_led_hw_is_supported(struct phy_device *phydev, u8 index,
439 unsigned long rules)
440 {
441 if (index >= MXL86110_MAX_LEDS)
442 return -EINVAL;
443
444 /* All combinations of the supported triggers are allowed */
445 if (rules & ~supported_trgs)
446 return -EOPNOTSUPP;
447
448 return 0;
449 }
450
mxl86110_led_hw_control_get(struct phy_device * phydev,u8 index,unsigned long * rules)451 static int mxl86110_led_hw_control_get(struct phy_device *phydev, u8 index,
452 unsigned long *rules)
453 {
454 int val;
455
456 if (index >= MXL86110_MAX_LEDS)
457 return -EINVAL;
458
459 val = mxl86110_read_extended_reg(phydev,
460 MXL86110_LED0_CFG_REG + index);
461 if (val < 0)
462 return val;
463
464 if (val & MXL86110_LEDX_CFG_LINK_UP_TX_ACT_ON)
465 *rules |= BIT(TRIGGER_NETDEV_TX);
466
467 if (val & MXL86110_LEDX_CFG_LINK_UP_RX_ACT_ON)
468 *rules |= BIT(TRIGGER_NETDEV_RX);
469
470 if (val & MXL86110_LEDX_CFG_LINK_UP_HALF_DUPLEX_ON)
471 *rules |= BIT(TRIGGER_NETDEV_HALF_DUPLEX);
472
473 if (val & MXL86110_LEDX_CFG_LINK_UP_FULL_DUPLEX_ON)
474 *rules |= BIT(TRIGGER_NETDEV_FULL_DUPLEX);
475
476 if (val & MXL86110_LEDX_CFG_LINK_UP_10MB_ON)
477 *rules |= BIT(TRIGGER_NETDEV_LINK_10);
478
479 if (val & MXL86110_LEDX_CFG_LINK_UP_100MB_ON)
480 *rules |= BIT(TRIGGER_NETDEV_LINK_100);
481
482 if (val & MXL86110_LEDX_CFG_LINK_UP_1GB_ON)
483 *rules |= BIT(TRIGGER_NETDEV_LINK_1000);
484
485 return 0;
486 }
487
mxl86110_led_hw_control_set(struct phy_device * phydev,u8 index,unsigned long rules)488 static int mxl86110_led_hw_control_set(struct phy_device *phydev, u8 index,
489 unsigned long rules)
490 {
491 u16 val = 0;
492 int ret;
493
494 if (index >= MXL86110_MAX_LEDS)
495 return -EINVAL;
496
497 if (rules & BIT(TRIGGER_NETDEV_LINK_10))
498 val |= MXL86110_LEDX_CFG_LINK_UP_10MB_ON;
499
500 if (rules & BIT(TRIGGER_NETDEV_LINK_100))
501 val |= MXL86110_LEDX_CFG_LINK_UP_100MB_ON;
502
503 if (rules & BIT(TRIGGER_NETDEV_LINK_1000))
504 val |= MXL86110_LEDX_CFG_LINK_UP_1GB_ON;
505
506 if (rules & BIT(TRIGGER_NETDEV_TX))
507 val |= MXL86110_LEDX_CFG_LINK_UP_TX_ACT_ON;
508
509 if (rules & BIT(TRIGGER_NETDEV_RX))
510 val |= MXL86110_LEDX_CFG_LINK_UP_RX_ACT_ON;
511
512 if (rules & BIT(TRIGGER_NETDEV_HALF_DUPLEX))
513 val |= MXL86110_LEDX_CFG_LINK_UP_HALF_DUPLEX_ON;
514
515 if (rules & BIT(TRIGGER_NETDEV_FULL_DUPLEX))
516 val |= MXL86110_LEDX_CFG_LINK_UP_FULL_DUPLEX_ON;
517
518 if (rules & BIT(TRIGGER_NETDEV_TX) ||
519 rules & BIT(TRIGGER_NETDEV_RX))
520 val |= MXL86110_LEDX_CFG_BLINK;
521
522 ret = mxl86110_write_extended_reg(phydev,
523 MXL86110_LED0_CFG_REG + index, val);
524 if (ret)
525 return ret;
526
527 /* clear manual control bit */
528 ret = mxl86110_modify_extended_reg(phydev,
529 MXL86110_COM_EXT_LED_GEN_CFG,
530 MXL86110_COM_EXT_LED_GEN_CFG_LFE(index),
531 0);
532
533 return ret;
534 }
535
mxl86110_led_brightness_set(struct phy_device * phydev,u8 index,enum led_brightness value)536 static int mxl86110_led_brightness_set(struct phy_device *phydev,
537 u8 index, enum led_brightness value)
538 {
539 u16 mask, set;
540 int ret;
541
542 if (index >= MXL86110_MAX_LEDS)
543 return -EINVAL;
544
545 /* force manual control */
546 set = MXL86110_COM_EXT_LED_GEN_CFG_LFE(index);
547 /* clear previous force mode */
548 mask = MXL86110_COM_EXT_LED_GEN_CFG_LFM(index);
549
550 /* force LED to be permanently on */
551 if (value != LED_OFF)
552 set |= MXL86110_COM_EXT_LED_GEN_CFG_LFME(index);
553
554 ret = mxl86110_modify_extended_reg(phydev,
555 MXL86110_COM_EXT_LED_GEN_CFG,
556 mask, set);
557
558 return ret;
559 }
560
561 /**
562 * mxl86110_synce_clk_cfg() - applies syncE/clk output configuration
563 * @phydev: pointer to the phy_device
564 *
565 * Note: This function assumes the caller already holds the MDIO bus lock
566 * or otherwise has exclusive access to the PHY.
567 *
568 * Return: 0 or negative errno code
569 */
mxl86110_synce_clk_cfg(struct phy_device * phydev)570 static int mxl86110_synce_clk_cfg(struct phy_device *phydev)
571 {
572 u16 mask = 0, val = 0;
573
574 /*
575 * Configures the clock output to its default
576 * setting as per the datasheet.
577 * This results in a 25MHz clock output being selected in the
578 * COM_EXT_SYNCE_CFG register for SyncE configuration.
579 */
580 val = MXL86110_EXT_SYNCE_CFG_EN_SYNC_E |
581 FIELD_PREP(MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_MASK,
582 MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_25M);
583 mask = MXL86110_EXT_SYNCE_CFG_EN_SYNC_E |
584 MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_MASK |
585 MXL86110_EXT_SYNCE_CFG_CLK_FRE_SEL;
586
587 /* Write clock output configuration */
588 return __mxl86110_modify_extended_reg(phydev,
589 MXL86110_EXT_SYNCE_CFG_REG,
590 mask, val);
591 }
592
593 /**
594 * mxl86110_broadcast_cfg - Configure MDIO broadcast setting for PHY
595 * @phydev: Pointer to the PHY device structure
596 *
597 * This function configures the MDIO broadcast behavior of the MxL86110 PHY.
598 * Currently, broadcast mode is explicitly disabled by clearing the EPA0 bit
599 * in the RGMII_MDIO_CFG extended register.
600 *
601 * Note: This function assumes the caller already holds the MDIO bus lock
602 * or otherwise has exclusive access to the PHY.
603 *
604 * Return: 0 on success or a negative errno code on failure.
605 */
mxl86110_broadcast_cfg(struct phy_device * phydev)606 static int mxl86110_broadcast_cfg(struct phy_device *phydev)
607 {
608 return __mxl86110_modify_extended_reg(phydev,
609 MXL86110_EXT_RGMII_MDIO_CFG,
610 MXL86110_RGMII_MDIO_CFG_EPA0_MASK,
611 0);
612 }
613
614 /**
615 * mxl86110_enable_led_activity_blink - Enable LEDs activity blink on PHY
616 * @phydev: Pointer to the PHY device structure
617 *
618 * Configure all PHY LEDs to blink on traffic activity regardless of whether
619 * they are ON or OFF. This behavior allows each LED to serve as a pure activity
620 * indicator, independently of its use as a link status indicator.
621 *
622 * By default, each LED blinks only when it is also in the ON state.
623 * This function modifies the appropriate registers (LABx fields)
624 * to enable blinking even when the LEDs are OFF, to allow the LED to be used
625 * as a traffic indicator without requiring it to also serve
626 * as a link status LED.
627 *
628 * Note: Any further LED customization can be performed via the
629 * /sys/class/leds interface; the functions led_hw_is_supported,
630 * led_hw_control_get, and led_hw_control_set are used
631 * to support this mechanism.
632 *
633 * This function assumes the caller already holds the MDIO bus lock
634 * or otherwise has exclusive access to the PHY.
635 *
636 * Return: 0 on success or a negative errno code on failure.
637 */
mxl86110_enable_led_activity_blink(struct phy_device * phydev)638 static int mxl86110_enable_led_activity_blink(struct phy_device *phydev)
639 {
640 int i, ret = 0;
641
642 for (i = 0; i < MXL86110_MAX_LEDS; i++) {
643 ret = __mxl86110_modify_extended_reg(phydev,
644 MXL86110_LED0_CFG_REG + i,
645 0,
646 MXL86110_LEDX_CFG_BLINK);
647 if (ret < 0)
648 break;
649 }
650
651 return ret;
652 }
653
654 /**
655 * mxl86110_config_rgmii_delay() - configure RGMII delays
656 * @phydev: pointer to the phy_device
657 *
658 * Return: 0 or negative errno code
659 */
mxl86110_config_rgmii_delay(struct phy_device * phydev)660 static int mxl86110_config_rgmii_delay(struct phy_device *phydev)
661 {
662 int ret;
663 u16 val;
664
665 switch (phydev->interface) {
666 case PHY_INTERFACE_MODE_RGMII:
667 val = 0;
668 break;
669 case PHY_INTERFACE_MODE_RGMII_RXID:
670 val = MXL86110_EXT_RGMII_CFG1_RX_DELAY_1950PS;
671 break;
672 case PHY_INTERFACE_MODE_RGMII_TXID:
673 val = MXL86110_EXT_RGMII_CFG1_TX_1G_DELAY_1950PS |
674 MXL86110_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_1950PS;
675 break;
676 case PHY_INTERFACE_MODE_RGMII_ID:
677 val = MXL86110_EXT_RGMII_CFG1_TX_1G_DELAY_1950PS |
678 MXL86110_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_1950PS |
679 MXL86110_EXT_RGMII_CFG1_RX_DELAY_1950PS;
680 break;
681 default:
682 ret = -EINVAL;
683 goto out;
684 }
685
686 ret = __mxl86110_modify_extended_reg(phydev,
687 MXL86110_EXT_RGMII_CFG1_REG,
688 MXL86110_EXT_RGMII_CFG1_FULL_MASK,
689 val);
690 if (ret < 0)
691 goto out;
692
693 /* Configure RXDLY (RGMII Rx Clock Delay) to disable
694 * the default additional delay value on RX_CLK
695 * (2 ns for 125 MHz, 8 ns for 25 MHz/2.5 MHz)
696 * and use just the digital one selected before
697 */
698 ret = __mxl86110_modify_extended_reg(phydev,
699 MXL86110_EXT_CHIP_CFG_REG,
700 MXL86110_EXT_CHIP_CFG_RXDLY_ENABLE,
701 0);
702 if (ret < 0)
703 goto out;
704
705 out:
706 return ret;
707 }
708
709 /**
710 * mxl86110_config_init() - initialize the MXL86110 PHY
711 * @phydev: pointer to the phy_device
712 *
713 * Return: 0 or negative errno code
714 */
mxl86110_config_init(struct phy_device * phydev)715 static int mxl86110_config_init(struct phy_device *phydev)
716 {
717 int ret;
718
719 phy_lock_mdio_bus(phydev);
720
721 /* configure syncE / clk output */
722 ret = mxl86110_synce_clk_cfg(phydev);
723 if (ret < 0)
724 goto out;
725
726 ret = mxl86110_config_rgmii_delay(phydev);
727 if (ret < 0)
728 goto out;
729
730 ret = mxl86110_enable_led_activity_blink(phydev);
731 if (ret < 0)
732 goto out;
733
734 ret = mxl86110_broadcast_cfg(phydev);
735
736 out:
737 phy_unlock_mdio_bus(phydev);
738 return ret;
739 }
740
741 /**
742 * mxl86111_probe() - validate bootstrap chip config and set UTP page
743 * @phydev: pointer to the phy_device
744 *
745 * Return: 0 or negative errno code
746 */
mxl86111_probe(struct phy_device * phydev)747 static int mxl86111_probe(struct phy_device *phydev)
748 {
749 int chip_config;
750 u16 reg_page;
751 int ret;
752
753 chip_config = mxl86110_read_extended_reg(phydev, MXL86110_EXT_CHIP_CFG_REG);
754 if (chip_config < 0)
755 return chip_config;
756
757 switch (chip_config & MXL86111_EXT_CHIP_CFG_MODE_SEL_MASK) {
758 case MXL86111_EXT_CHIP_CFG_MODE_UTP_TO_SGMII:
759 case MXL86111_EXT_CHIP_CFG_MODE_UTP_TO_RGMII:
760 phydev->port = PORT_TP;
761 reg_page = MXL86111_EXT_SMI_SDS_PHYUTP_SPACE;
762 break;
763 default:
764 return -EOPNOTSUPP;
765 }
766
767 ret = mxl86110_write_extended_reg(phydev,
768 MXL86111_EXT_SMI_SDS_PHY_REG,
769 reg_page);
770 if (ret < 0)
771 return ret;
772
773 return 0;
774 }
775
776 /**
777 * mxl86111_config_init() - initialize the MXL86111 PHY
778 * @phydev: pointer to the phy_device
779 *
780 * Return: 0 or negative errno code
781 */
mxl86111_config_init(struct phy_device * phydev)782 static int mxl86111_config_init(struct phy_device *phydev)
783 {
784 int ret;
785
786 phy_lock_mdio_bus(phydev);
787
788 /* configure syncE / clk output */
789 ret = mxl86110_synce_clk_cfg(phydev);
790 if (ret < 0)
791 goto out;
792
793 switch (phydev->interface) {
794 case PHY_INTERFACE_MODE_100BASEX:
795 ret = __mxl86110_modify_extended_reg(phydev,
796 MXL86111_EXT_MISC_CONFIG_REG,
797 MXL86111_EXT_MISC_CONFIG_FIB_SPEED_SEL,
798 MXL86111_EXT_MISC_CONFIG_FIB_SPEED_SEL_100BX);
799 if (ret < 0)
800 goto out;
801 break;
802 case PHY_INTERFACE_MODE_1000BASEX:
803 case PHY_INTERFACE_MODE_SGMII:
804 ret = __mxl86110_modify_extended_reg(phydev,
805 MXL86111_EXT_MISC_CONFIG_REG,
806 MXL86111_EXT_MISC_CONFIG_FIB_SPEED_SEL,
807 MXL86111_EXT_MISC_CONFIG_FIB_SPEED_SEL_1000BX);
808 if (ret < 0)
809 goto out;
810 break;
811 default:
812 /* RGMII modes */
813 ret = mxl86110_config_rgmii_delay(phydev);
814 if (ret < 0)
815 goto out;
816 ret = __mxl86110_modify_extended_reg(phydev, MXL86110_EXT_RGMII_CFG1_REG,
817 MXL86110_EXT_RGMII_CFG1_FULL_MASK, ret);
818
819 /* PL P1 requires optimized RGMII timing for 1.8V RGMII voltage
820 */
821 ret = __mxl86110_read_extended_reg(phydev, 0xf);
822 if (ret < 0)
823 goto out;
824
825 if (ret == MXL86111_PL_P1) {
826 ret = __mxl86110_read_extended_reg(phydev, MXL86110_EXT_CHIP_CFG_REG);
827 if (ret < 0)
828 goto out;
829
830 /* check if LDO is in 1.8V mode */
831 switch (FIELD_GET(MXL86111_EXT_CHIP_CFG_CLDO_MASK, ret)) {
832 case MXL86111_EXT_CHIP_CFG_CLDO_1V8_3:
833 case MXL86111_EXT_CHIP_CFG_CLDO_1V8_2:
834 ret = __mxl86110_write_extended_reg(phydev, 0xa010, 0xabff);
835 if (ret < 0)
836 goto out;
837 break;
838 default:
839 break;
840 }
841 }
842 break;
843 }
844
845 ret = mxl86110_enable_led_activity_blink(phydev);
846 if (ret < 0)
847 goto out;
848
849 ret = mxl86110_broadcast_cfg(phydev);
850 out:
851 phy_unlock_mdio_bus(phydev);
852
853 return ret;
854 }
855
856 /**
857 * mxl86111_read_page() - read reg page
858 * @phydev: pointer to the phy_device
859 *
860 * Return: current reg space of mxl86111 or negative errno code
861 */
mxl86111_read_page(struct phy_device * phydev)862 static int mxl86111_read_page(struct phy_device *phydev)
863 {
864 int page;
865
866 page = __mxl86110_read_extended_reg(phydev, MXL86111_EXT_SMI_SDS_PHY_REG);
867 if (page < 0)
868 return page;
869
870 return page & MXL86111_EXT_SMI_SDS_PHYSPACE_MASK;
871 };
872
873 /**
874 * mxl86111_write_page() - Set reg page
875 * @phydev: pointer to the phy_device
876 * @page: The reg page to set
877 *
878 * Return: 0 or negative errno code
879 */
mxl86111_write_page(struct phy_device * phydev,int page)880 static int mxl86111_write_page(struct phy_device *phydev, int page)
881 {
882 return __mxl86110_modify_extended_reg(phydev, MXL86111_EXT_SMI_SDS_PHY_REG,
883 MXL86111_EXT_SMI_SDS_PHYSPACE_MASK, page);
884 };
885
mxl86111_config_inband(struct phy_device * phydev,unsigned int modes)886 static int mxl86111_config_inband(struct phy_device *phydev, unsigned int modes)
887 {
888 int ret;
889
890 ret = phy_modify_paged(phydev, MXL86111_EXT_SMI_SDS_PHYFIBER_SPACE,
891 MII_BMCR, BMCR_ANENABLE,
892 (modes == LINK_INBAND_DISABLE) ? 0 : BMCR_ANENABLE);
893 if (ret < 0)
894 goto out;
895
896 phy_lock_mdio_bus(phydev);
897
898 ret = __mxl86110_modify_extended_reg(phydev, MXL86111_EXT_SDS_LINK_TIMER_CFG2_REG,
899 MXL86111_EXT_SDS_LINK_TIMER_CFG2_EN_AUTOSEN,
900 (modes == LINK_INBAND_DISABLE) ? 0 :
901 MXL86111_EXT_SDS_LINK_TIMER_CFG2_EN_AUTOSEN);
902 if (ret < 0)
903 goto out;
904
905 ret = __mxl86110_modify_extended_reg(phydev, MXL86110_EXT_CHIP_CFG_REG,
906 MXL86110_EXT_CHIP_CFG_SW_RST_N_MODE, 0);
907 if (ret < 0)
908 goto out;
909
910 /* For fiber forced mode, power down/up to re-aneg */
911 if (modes != LINK_INBAND_DISABLE) {
912 __phy_modify(phydev, MII_BMCR, 0, BMCR_PDOWN);
913 usleep_range(1000, 1050);
914 __phy_modify(phydev, MII_BMCR, BMCR_PDOWN, 0);
915 }
916
917 out:
918 phy_unlock_mdio_bus(phydev);
919
920 return ret;
921 }
922
mxl86111_inband_caps(struct phy_device * phydev,phy_interface_t interface)923 static unsigned int mxl86111_inband_caps(struct phy_device *phydev,
924 phy_interface_t interface)
925 {
926 switch (interface) {
927 case PHY_INTERFACE_MODE_100BASEX:
928 case PHY_INTERFACE_MODE_1000BASEX:
929 case PHY_INTERFACE_MODE_SGMII:
930 return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
931 default:
932 return 0;
933 }
934 }
935
936 static struct phy_driver mxl_phy_drvs[] = {
937 {
938 PHY_ID_MATCH_EXACT(PHY_ID_MXL86110),
939 .name = "MXL86110 Gigabit Ethernet",
940 .config_init = mxl86110_config_init,
941 .get_wol = mxl86110_get_wol,
942 .set_wol = mxl86110_set_wol,
943 .led_brightness_set = mxl86110_led_brightness_set,
944 .led_hw_is_supported = mxl86110_led_hw_is_supported,
945 .led_hw_control_get = mxl86110_led_hw_control_get,
946 .led_hw_control_set = mxl86110_led_hw_control_set,
947 },
948 {
949 PHY_ID_MATCH_EXACT(PHY_ID_MXL86111),
950 .name = "MXL86111 Gigabit Ethernet",
951 .probe = mxl86111_probe,
952 .config_init = mxl86111_config_init,
953 .get_wol = mxl86110_get_wol,
954 .set_wol = mxl86110_set_wol,
955 .inband_caps = mxl86111_inband_caps,
956 .config_inband = mxl86111_config_inband,
957 .read_page = mxl86111_read_page,
958 .write_page = mxl86111_write_page,
959 .led_brightness_set = mxl86110_led_brightness_set,
960 .led_hw_is_supported = mxl86110_led_hw_is_supported,
961 .led_hw_control_get = mxl86110_led_hw_control_get,
962 .led_hw_control_set = mxl86110_led_hw_control_set,
963 },
964 };
965
966 module_phy_driver(mxl_phy_drvs);
967
968 static const struct mdio_device_id __maybe_unused mxl_tbl[] = {
969 { PHY_ID_MATCH_EXACT(PHY_ID_MXL86110) },
970 { PHY_ID_MATCH_EXACT(PHY_ID_MXL86111) },
971 { }
972 };
973
974 MODULE_DEVICE_TABLE(mdio, mxl_tbl);
975
976 MODULE_DESCRIPTION("MaxLinear MXL86110/MXL86111 PHY driver");
977 MODULE_AUTHOR("Stefano Radaelli");
978 MODULE_LICENSE("GPL");
979