-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy path0003-net-phy-marvell10g-Add-LED-support-for-88X3310.patch
More file actions
511 lines (502 loc) · 13.8 KB
/
0003-net-phy-marvell10g-Add-LED-support-for-88X3310.patch
File metadata and controls
511 lines (502 loc) · 13.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
From d0b9c6e92f1a037f380437cef703a6015c63826a Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Wed, 15 Nov 2023 20:58:42 +0100
Subject: [PATCH 03/47] net: phy: marvell10g: Add LED support for 88X3310
Pickup the LEDs from the state in which the hardware reset or
bootloader left them, but also support further configuration via
device tree. This is primarily needed because the electrical polarity
and drive behavior is software controlled and not possible to set via
hardware strapping.
Trigger support:
- "none"
- "timer": Map 60-100 ms periods to the fast rate (81ms) and 1000-1600
ms periods to the slow rate (1300ms). Defer everything else to
software blinking
- "netdev": Offload link or duplex information to the solid behavior;
tx and/or rx activity to blink behavior.
---
drivers/net/phy/marvell10g.c | 422 +++++++++++++++++++++++++++++++++++
1 file changed, 422 insertions(+)
diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c
index 40439db49601..7ae4744f147b 100644
--- a/drivers/net/phy/marvell10g.c
+++ b/drivers/net/phy/marvell10g.c
@@ -28,6 +28,7 @@
#include <linux/firmware.h>
#include <linux/hwmon.h>
#include <linux/marvell_phy.h>
+#include <linux/of.h>
#include <linux/phy.h>
#include <linux/sfp.h>
#include <linux/netdevice.h>
@@ -133,6 +134,16 @@ enum {
MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_NO_SGMII_AN = 0x5,
MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH = 0x6,
MV_V2_33X0_PORT_CTRL_MACTYPE_USXGMII = 0x7,
+ MV_V2_LED0_CONTROL = 0xf020,
+ MV_V2_LED_CONTROL_POLARITY_MASK = 0x0003,
+ MV_V2_LED_CONTROL_POLARITY_SHIFT = 0,
+ MV_V2_LED_CONTROL_ACTIVE_HIGH = BIT(0),
+ MV_V2_LED_CONTROL_TRISTATE = BIT(1),
+ MV_V2_LED_CONTROL_BLINK_RATE = BIT(2),
+ MV_V2_LED_CONTROL_SOLID_FUNC_MASK = 0x00f8,
+ MV_V2_LED_CONTROL_SOLID_FUNC_SHIFT = 3,
+ MV_V2_LED_CONTROL_BLINK_FUNC_MASK = 0x1f00,
+ MV_V2_LED_CONTROL_BLINK_FUNC_SHIFT = 8,
MV_V2_PORT_INTR_STS = 0xf040,
MV_V2_PORT_INTR_MASK = 0xf043,
MV_V2_PORT_INTR_STS_WOL_EN = BIT(8),
@@ -161,6 +172,7 @@ struct mv3310_mactype {
struct mv3310_chip {
bool (*has_downshift)(struct phy_device *phydev);
void (*init_supported_interfaces)(unsigned long *mask);
+ int (*leds_probe)(struct phy_device *phydev);
int (*get_mactype)(struct phy_device *phydev);
int (*set_mactype)(struct phy_device *phydev, int mactype);
int (*select_mactype)(unsigned long *interfaces);
@@ -175,6 +187,13 @@ struct mv3310_chip {
#endif
};
+#define MV3310_N_LEDS 4
+
+struct mv3310_led {
+ u8 index;
+ u16 ctrl;
+};
+
struct mv3310_priv {
DECLARE_BITMAP(supported_interfaces, PHY_INTERFACE_MODE_MAX);
const struct mv3310_mactype *mactype;
@@ -184,6 +203,8 @@ struct mv3310_priv {
struct device *hwmon_dev;
char *hwmon_name;
+
+ struct mv3310_led led[MV3310_N_LEDS];
};
static const struct mv3310_chip *to_mv3310_chip(struct phy_device *phydev)
@@ -191,6 +212,396 @@ static const struct mv3310_chip *to_mv3310_chip(struct phy_device *phydev)
return phydev->drv->driver_data;
}
+enum mv3310_led_func {
+ MV3310_LED_FUNC_OFF = 0x00,
+ MV3310_LED_FUNC_TX_RX = 0x01,
+ MV3310_LED_FUNC_TX = 0x02,
+ MV3310_LED_FUNC_RX = 0x03,
+ MV3310_LED_FUNC_COLLISION = 0x04,
+ MV3310_LED_FUNC_LINK_COPPER = 0x05,
+ MV3310_LED_FUNC_LINK_FIBER = 0x06,
+ MV3310_LED_FUNC_LINK = 0x07,
+ MV3310_LED_FUNC_10M_LINK = 0x08,
+ MV3310_LED_FUNC_100M_LINK = 0x09,
+ MV3310_LED_FUNC_1G_LINK = 0x0a,
+ MV3310_LED_FUNC_10G_LINK = 0x0b,
+ MV3310_LED_FUNC_10M_100M_LINK = 0x0c,
+ MV3310_LED_FUNC_10M_100M_1G_LINK = 0x0d,
+ MV3310_LED_FUNC_100M_10G_LINK = 0x0e,
+ MV3310_LED_FUNC_1G_10G_LINK = 0x0f,
+ MV3310_LED_FUNC_1G_10G_SLAVE = 0x10,
+ MV3310_LED_FUNC_1G_10G_MASTER = 0x11,
+ MV3310_LED_FUNC_HALF_DUPLEX = 0x12,
+ MV3310_LED_FUNC_FULL_DUPLEX = 0x13,
+ MV3310_LED_FUNC_LINK_EEE = 0x14,
+ MV3310_LED_FUNC_2G5_LINK = 0x15,
+ MV3310_LED_FUNC_5G_LINK = 0x16,
+ MV3310_LED_FUNC_ON = 0x17,
+ MV3310_LED_FUNC_2G5_5G_SLAVE = 0x18,
+ MV3310_LED_FUNC_2G5_5G_MASTER = 0x19,
+
+ MV3310_LED_FUNC_SPEED_BLINK = 0x1f
+};
+
+static int mv3310_led_flags_from_funcs(struct mv3310_led *led,
+ enum mv3310_led_func solid,
+ enum mv3310_led_func blink,
+ unsigned long *flagsp)
+{
+ unsigned long flags = 0;
+
+ switch (solid) {
+ case MV3310_LED_FUNC_OFF:
+ break;
+ case MV3310_LED_FUNC_LINK_COPPER:
+ case MV3310_LED_FUNC_LINK_FIBER:
+ case MV3310_LED_FUNC_LINK:
+ flags |= TRIGGER_NETDEV_LINK;
+ break;
+ case MV3310_LED_FUNC_HALF_DUPLEX:
+ flags |= TRIGGER_NETDEV_HALF_DUPLEX;
+ break;
+ case MV3310_LED_FUNC_FULL_DUPLEX:
+ flags |= TRIGGER_NETDEV_FULL_DUPLEX;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (blink) {
+ case MV3310_LED_FUNC_OFF:
+ break;
+ case MV3310_LED_FUNC_TX:
+ flags |= TRIGGER_NETDEV_TX;
+ break;
+ case MV3310_LED_FUNC_RX:
+ flags |= TRIGGER_NETDEV_RX;
+ break;
+ case MV3310_LED_FUNC_TX_RX:
+ flags |= TRIGGER_NETDEV_TX | TRIGGER_NETDEV_RX;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *flagsp = flags;
+ return 0;
+}
+
+static int mv3310_led_funcs_from_flags(struct mv3310_led *led,
+ unsigned long flags,
+ enum mv3310_led_func *solid,
+ enum mv3310_led_func *blink)
+{
+ unsigned long activity, duplex, link;
+
+ if (flags & ~(BIT(TRIGGER_NETDEV_LINK) |
+ BIT(TRIGGER_NETDEV_HALF_DUPLEX) |
+ BIT(TRIGGER_NETDEV_FULL_DUPLEX) |
+ BIT(TRIGGER_NETDEV_TX) |
+ BIT(TRIGGER_NETDEV_RX)))
+ return -EINVAL;
+
+ link = flags & BIT(TRIGGER_NETDEV_LINK);
+
+ duplex = flags & (BIT(TRIGGER_NETDEV_HALF_DUPLEX) |
+ BIT(TRIGGER_NETDEV_FULL_DUPLEX));
+
+ activity = flags & (BIT(TRIGGER_NETDEV_TX) |
+ BIT(TRIGGER_NETDEV_RX));
+
+ if (link && duplex)
+ return -EINVAL;
+
+ if (solid) {
+ if (link) {
+ *solid = MV3310_LED_FUNC_LINK;
+ } else if (duplex) {
+ switch (duplex) {
+ case BIT(TRIGGER_NETDEV_HALF_DUPLEX):
+ *solid = MV3310_LED_FUNC_HALF_DUPLEX;
+ break;
+ case BIT(TRIGGER_NETDEV_FULL_DUPLEX):
+ *solid = MV3310_LED_FUNC_FULL_DUPLEX;
+ break;
+ default:
+ break;
+ }
+ } else {
+ *solid = MV3310_LED_FUNC_OFF;
+ }
+ }
+
+ if (blink) {
+ switch (activity) {
+ case 0:
+ *blink = MV3310_LED_FUNC_OFF;
+ break;
+ case BIT(TRIGGER_NETDEV_TX):
+ *blink = MV3310_LED_FUNC_TX;
+ break;
+ case BIT(TRIGGER_NETDEV_RX):
+ *blink = MV3310_LED_FUNC_RX;
+ break;
+ default:
+ *blink = MV3310_LED_FUNC_TX_RX;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int mv3310_led_get(struct phy_device *phydev,
+ struct mv3310_led *led,
+ enum mv3310_led_func *solid,
+ enum mv3310_led_func *blink,
+ bool *slow_blink)
+{
+ int ctrl;
+
+ ctrl = phy_read_mmd(phydev, MDIO_MMD_VEND2,
+ MV_V2_LED0_CONTROL + led->index);
+ if (ctrl < 0)
+ return ctrl;
+
+ *solid = (ctrl & MV_V2_LED_CONTROL_SOLID_FUNC_MASK) >>
+ MV_V2_LED_CONTROL_SOLID_FUNC_SHIFT;
+ *blink = (ctrl & MV_V2_LED_CONTROL_BLINK_FUNC_MASK) >>
+ MV_V2_LED_CONTROL_BLINK_FUNC_SHIFT;
+
+ *slow_blink = !!(ctrl & MV_V2_LED_CONTROL_BLINK_RATE);
+ return 0;
+}
+
+static int mv3310_led_set(struct phy_device *phydev,
+ struct mv3310_led *led,
+ enum mv3310_led_func solid,
+ enum mv3310_led_func blink,
+ bool slow_blink)
+{
+ u16 ctrl = led->ctrl;
+
+ ctrl &= ~MV_V2_LED_CONTROL_SOLID_FUNC_MASK;
+ ctrl &= ~MV_V2_LED_CONTROL_BLINK_FUNC_MASK;
+ ctrl &= ~MV_V2_LED_CONTROL_BLINK_RATE;
+
+ ctrl |= solid << MV_V2_LED_CONTROL_SOLID_FUNC_SHIFT;
+ ctrl |= blink << MV_V2_LED_CONTROL_BLINK_FUNC_SHIFT;
+
+ if (slow_blink)
+ ctrl |= MV_V2_LED_CONTROL_BLINK_RATE;
+
+ return phy_write_mmd(phydev, MDIO_MMD_VEND2,
+ MV_V2_LED0_CONTROL + led->index, ctrl);
+}
+
+static int mv3310_led_polarity_set(struct phy_device *phydev,
+ int index, unsigned long modes)
+{
+ struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
+ struct mv3310_led *led;
+ u16 ctrl;
+ int err;
+
+ if (index < 0 || index >= MV3310_N_LEDS)
+ return -ENODEV;
+
+ led = &priv->led[index];
+ ctrl = led->ctrl;
+
+ if (test_bit(PHY_LED_ACTIVE_LOW, &modes))
+ ctrl &= ~MV_V2_LED_CONTROL_ACTIVE_HIGH;
+ else
+ ctrl |= MV_V2_LED_CONTROL_ACTIVE_HIGH;
+
+ if (test_bit(PHY_LED_INACTIVE_HIGH_IMPEDANCE, &modes))
+ ctrl |= MV_V2_LED_CONTROL_TRISTATE;
+ else
+ ctrl &= ~MV_V2_LED_CONTROL_TRISTATE;
+
+ err = phy_write_mmd(phydev, MDIO_MMD_VEND2,
+ MV_V2_LED0_CONTROL + index, ctrl);
+ if (err)
+ return err;
+
+ led->ctrl = ctrl;
+ return 0;
+}
+
+static int mv3310_led_brightness_set(struct phy_device *phydev,
+ u8 index, enum led_brightness value)
+{
+ struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
+ enum mv3310_led_func solid;
+ struct mv3310_led *led;
+
+ if (index >= MV3310_N_LEDS)
+ return -ENODEV;
+
+ led = &priv->led[index];
+
+ if (value == LED_OFF)
+ solid = MV3310_LED_FUNC_OFF;
+ else
+ solid = MV3310_LED_FUNC_ON;
+
+ return mv3310_led_set(phydev, led, solid, MV3310_LED_FUNC_OFF, false);
+}
+
+static int mv3310_led_blink_set(struct phy_device *phydev, u8 index,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
+ struct mv3310_led *led;
+ bool slow_blink;
+ int err;
+
+ if (index >= MV3310_N_LEDS)
+ return -ENODEV;
+
+ led = &priv->led[index];
+
+ if (*delay_on != *delay_off)
+ /* Defer anything other than 50% duty cycles to
+ * software.
+ */
+ return -EINVAL;
+
+ /* Accept values within ~20% of our supported rates (80ms or
+ * 1300ms periods).
+ */
+ if ((*delay_on >= 30) && (*delay_on <= 50))
+ slow_blink = false;
+ else if (((*delay_on >= 500) && (*delay_on <= 800)) || (*delay_on == 0))
+ slow_blink = true;
+ else
+ return -EINVAL;
+
+ err = mv3310_led_set(phydev, led, MV3310_LED_FUNC_OFF,
+ MV3310_LED_FUNC_ON, slow_blink);
+ if (!err)
+ *delay_on = *delay_off = slow_blink ? 650 : 40;
+
+ return err;
+}
+
+static int mv3310_led_hw_is_supported(struct phy_device *phydev, u8 index,
+ unsigned long flags)
+{
+ struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
+ struct mv3310_led *led;
+
+ if (index >= MV3310_N_LEDS)
+ return -ENODEV;
+
+ led = &priv->led[index];
+
+ return mv3310_led_funcs_from_flags(led, flags, NULL, NULL);
+}
+
+static int mv3310_led_hw_control_set(struct phy_device *phydev, u8 index,
+ unsigned long flags)
+{
+ struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
+ enum mv3310_led_func solid, blink;
+ struct mv3310_led *led;
+ int err;
+
+ if (index >= MV3310_N_LEDS)
+ return -ENODEV;
+
+ led = &priv->led[index];
+
+ err = mv3310_led_funcs_from_flags(led, flags, &solid, &blink);
+ if (err)
+ return err;
+
+ return mv3310_led_set(phydev, led, solid, blink, false);
+}
+
+static int mv3310_led_hw_control_get(struct phy_device *phydev, u8 index,
+ unsigned long *flags)
+{
+ struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
+ enum mv3310_led_func solid, blink;
+ struct mv3310_led *led;
+ bool slow_blink;
+ int err;
+
+ if (index >= MV3310_N_LEDS)
+ return -ENODEV;
+
+ led = &priv->led[index];
+
+ err = mv3310_led_get(phydev, led, &solid, &blink, &slow_blink);
+ if (err)
+ return err;
+
+ return mv3310_led_flags_from_funcs(led, solid, blink, flags);
+}
+
+static int mv3310_led_probe_of(struct phy_device *phydev,
+ struct device_node *np)
+{
+ u32 index;
+ int err;
+
+ err = of_property_read_u32(np, "reg", &index);
+ if (err)
+ return err;
+
+ if (index >= MV3310_N_LEDS)
+ return -EINVAL;
+
+ /* mv3310_led_polarity_set() will not be called unless the
+ * device tree specifies a mode that of_phy_led() considers to
+ * be a non-default configuration. I.e., something other than
+ * (active-high, drive low on inactive). But the 3310's HW
+ * default is (active-low, drive low on inactive). Therefore,
+ * configure all LEDs defined in the DT with the kernel
+ * defaults, and then let of_phy_led() set the final value if
+ * the config deviates from that.
+ */
+ return mv3310_led_polarity_set(phydev, index, 0);
+}
+
+static int mv3310_leds_probe(struct phy_device *phydev)
+{
+ struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
+ struct device_node *node = phydev->mdio.dev.of_node;
+ struct device_node *pnp, *np;
+ int err, val, index;
+
+ for (index = 0; index < MV3310_N_LEDS; index++) {
+ val = phy_read_mmd(phydev, MDIO_MMD_VEND2,
+ MV_V2_LED0_CONTROL + index);
+ if (val < 0)
+ return val;
+
+ priv->led[index] = (struct mv3310_led) {
+ .index = index,
+ .ctrl = val,
+ };
+ }
+
+ if (!node)
+ return 0;
+
+ pnp = of_get_child_by_name(node, "leds");
+ if (!pnp)
+ return 0;
+
+ for_each_available_child_of_node(pnp, np) {
+ err = mv3310_led_probe_of(phydev, np);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
#ifdef CONFIG_HWMON
static umode_t mv3310_hwmon_is_visible(const void *data,
enum hwmon_sensor_types type,
@@ -712,6 +1123,10 @@ static int mv3310_probe(struct phy_device *phydev)
if (ret)
return ret;
+ ret = chip->leds_probe ? chip->leds_probe(phydev) : 0;
+ if (ret)
+ return ret;
+
chip->init_supported_interfaces(priv->supported_interfaces);
return phy_sfp_probe(phydev, &mv3310_sfp_ops);
@@ -1364,6 +1779,7 @@ static void mv2111_init_supported_interfaces(unsigned long *mask)
static const struct mv3310_chip mv3310_type = {
.has_downshift = mv3310_has_downshift,
.init_supported_interfaces = mv3310_init_supported_interfaces,
+ .leds_probe = mv3310_leds_probe,
.get_mactype = mv3310_get_mactype,
.set_mactype = mv3310_set_mactype,
.select_mactype = mv3310_select_mactype,
@@ -1577,6 +1993,12 @@ static struct phy_driver mv3310_drivers[] = {
.set_loopback = genphy_c45_loopback,
.get_wol = mv3110_get_wol,
.set_wol = mv3110_set_wol,
+ .led_polarity_set = mv3310_led_polarity_set,
+ .led_brightness_set = mv3310_led_brightness_set,
+ .led_blink_set = mv3310_led_blink_set,
+ .led_hw_is_supported = mv3310_led_hw_is_supported,
+ .led_hw_control_set = mv3310_led_hw_control_set,
+ .led_hw_control_get = mv3310_led_hw_control_get,
},
{
.phy_id = MARVELL_PHY_ID_88X3310,