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