Skip to content

Commit 2e0bc45

Browse files
Zach Browndavem330
authored andcommitted
net: phy: leds: add support for led triggers on phy link state change
Create an option CONFIG_LED_TRIGGER_PHY (default n), which will create a set of led triggers for each instantiated PHY device. There is one LED trigger per link-speed, per-phy. The triggers are registered during phy_attach and unregistered during phy_detach. This allows for a user to configure their system to allow a set of LEDs not controlled by the phy to represent link state changes on the phy. LEDS controlled by the phy are unaffected. For example, we have a board where some of the leds in the RJ45 socket are controlled by the phy, but others are not. Using the triggers provided by this patch the leds not controlled by the phy can be configured to show the current speed of the ethernet connection. The leds controlled by the phy are unaffected. Signed-off-by: Josh Cartwright <[email protected]> Signed-off-by: Nathan Sullivan <[email protected]> Signed-off-by: Zach Brown <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 1f9127c commit 2e0bc45

File tree

7 files changed

+214
-0
lines changed

7 files changed

+214
-0
lines changed

drivers/net/phy/Kconfig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,19 @@ if PHYLIB
1515
config SWPHY
1616
bool
1717

18+
config LED_TRIGGER_PHY
19+
bool "Support LED triggers for tracking link state"
20+
depends on LEDS_TRIGGERS
21+
---help---
22+
Adds support for a set of LED trigger events per-PHY. Link
23+
state change will trigger the events, for consumption by an
24+
LED class driver. There are triggers for each link speed currently
25+
supported by the phy, and are of the form:
26+
<mii bus id>:<phy>:<speed>
27+
28+
Where speed is in the form:
29+
<Speed in megabits>Mbps or <Speed in gigabits>Gbps
30+
1831
comment "MDIO bus device drivers"
1932

2033
config MDIO_BCM_IPROC

drivers/net/phy/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
libphy-y := phy.o phy_device.o mdio_bus.o mdio_device.o
44
libphy-$(CONFIG_SWPHY) += swphy.o
5+
libphy-$(CONFIG_LED_TRIGGER_PHY) += phy_led_triggers.o
56

67
obj-$(CONFIG_PHYLIB) += libphy.o
78

drivers/net/phy/phy.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,7 @@ EXPORT_SYMBOL(phy_start);
946946
static void phy_adjust_link(struct phy_device *phydev)
947947
{
948948
phydev->adjust_link(phydev->attached_dev);
949+
phy_led_trigger_change_speed(phydev);
949950
}
950951

951952
/**

drivers/net/phy/phy_device.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <linux/mii.h>
3131
#include <linux/ethtool.h>
3232
#include <linux/phy.h>
33+
#include <linux/phy_led_triggers.h>
3334
#include <linux/mdio.h>
3435
#include <linux/io.h>
3536
#include <linux/uaccess.h>
@@ -916,6 +917,8 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
916917
else
917918
phy_resume(phydev);
918919

920+
phy_led_triggers_register(phydev);
921+
919922
return err;
920923

921924
error:
@@ -989,6 +992,8 @@ void phy_detach(struct phy_device *phydev)
989992
}
990993
}
991994

995+
phy_led_triggers_unregister(phydev);
996+
992997
/*
993998
* The phydev might go away on the put_device() below, so avoid
994999
* a use-after-free bug by reading the underlying bus first.

drivers/net/phy/phy_led_triggers.c

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/* Copyright (C) 2016 National Instruments Corp.
2+
*
3+
* This program is free software; you can redistribute it and/or modify
4+
* it under the terms of the GNU General Public License as published by
5+
* the Free Software Foundation; either version 2 of the License, or
6+
* (at your option) any later version.
7+
*
8+
* This program is distributed in the hope that it will be useful,
9+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
* GNU General Public License for more details.
12+
*/
13+
#include <linux/leds.h>
14+
#include <linux/phy.h>
15+
#include <linux/netdevice.h>
16+
17+
static struct phy_led_trigger *phy_speed_to_led_trigger(struct phy_device *phy,
18+
unsigned int speed)
19+
{
20+
unsigned int i;
21+
22+
for (i = 0; i < phy->phy_num_led_triggers; i++) {
23+
if (phy->phy_led_triggers[i].speed == speed)
24+
return &phy->phy_led_triggers[i];
25+
}
26+
return NULL;
27+
}
28+
29+
void phy_led_trigger_change_speed(struct phy_device *phy)
30+
{
31+
struct phy_led_trigger *plt;
32+
33+
if (!phy->link)
34+
goto out_change_speed;
35+
36+
if (phy->speed == 0)
37+
return;
38+
39+
plt = phy_speed_to_led_trigger(phy, phy->speed);
40+
if (!plt) {
41+
netdev_alert(phy->attached_dev,
42+
"No phy led trigger registered for speed(%d)\n",
43+
phy->speed);
44+
goto out_change_speed;
45+
}
46+
47+
if (plt != phy->last_triggered) {
48+
led_trigger_event(&phy->last_triggered->trigger, LED_OFF);
49+
led_trigger_event(&plt->trigger, LED_FULL);
50+
phy->last_triggered = plt;
51+
}
52+
return;
53+
54+
out_change_speed:
55+
if (phy->last_triggered) {
56+
led_trigger_event(&phy->last_triggered->trigger,
57+
LED_OFF);
58+
phy->last_triggered = NULL;
59+
}
60+
}
61+
EXPORT_SYMBOL_GPL(phy_led_trigger_change_speed);
62+
63+
static int phy_led_trigger_register(struct phy_device *phy,
64+
struct phy_led_trigger *plt,
65+
unsigned int speed)
66+
{
67+
char name_suffix[PHY_LED_TRIGGER_SPEED_SUFFIX_SIZE];
68+
69+
plt->speed = speed;
70+
71+
if (speed < SPEED_1000)
72+
snprintf(name_suffix, sizeof(name_suffix), "%dMbps", speed);
73+
else if (speed == SPEED_2500)
74+
snprintf(name_suffix, sizeof(name_suffix), "2.5Gbps");
75+
else
76+
snprintf(name_suffix, sizeof(name_suffix), "%dGbps",
77+
DIV_ROUND_CLOSEST(speed, 1000));
78+
79+
snprintf(plt->name, sizeof(plt->name), PHY_ID_FMT ":%s",
80+
phy->mdio.bus->id, phy->mdio.addr, name_suffix);
81+
plt->trigger.name = plt->name;
82+
83+
return led_trigger_register(&plt->trigger);
84+
}
85+
86+
static void phy_led_trigger_unregister(struct phy_led_trigger *plt)
87+
{
88+
led_trigger_unregister(&plt->trigger);
89+
}
90+
91+
int phy_led_triggers_register(struct phy_device *phy)
92+
{
93+
int i, err;
94+
unsigned int speeds[50];
95+
96+
phy->phy_num_led_triggers = phy_supported_speeds(phy, speeds,
97+
ARRAY_SIZE(speeds));
98+
if (!phy->phy_num_led_triggers)
99+
return 0;
100+
101+
phy->phy_led_triggers = devm_kzalloc(&phy->mdio.dev,
102+
sizeof(struct phy_led_trigger) *
103+
phy->phy_num_led_triggers,
104+
GFP_KERNEL);
105+
if (!phy->phy_led_triggers)
106+
return -ENOMEM;
107+
108+
for (i = 0; i < phy->phy_num_led_triggers; i++) {
109+
err = phy_led_trigger_register(phy, &phy->phy_led_triggers[i],
110+
speeds[i]);
111+
if (err)
112+
goto out_unreg;
113+
}
114+
115+
phy->last_triggered = NULL;
116+
phy_led_trigger_change_speed(phy);
117+
118+
return 0;
119+
out_unreg:
120+
while (i--)
121+
phy_led_trigger_unregister(&phy->phy_led_triggers[i]);
122+
devm_kfree(&phy->mdio.dev, phy->phy_led_triggers);
123+
return err;
124+
}
125+
EXPORT_SYMBOL_GPL(phy_led_triggers_register);
126+
127+
void phy_led_triggers_unregister(struct phy_device *phy)
128+
{
129+
int i;
130+
131+
for (i = 0; i < phy->phy_num_led_triggers; i++)
132+
phy_led_trigger_unregister(&phy->phy_led_triggers[i]);
133+
134+
devm_kfree(&phy->mdio.dev, phy->phy_led_triggers);
135+
}
136+
EXPORT_SYMBOL_GPL(phy_led_triggers_unregister);

include/linux/phy.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <linux/timer.h>
2626
#include <linux/workqueue.h>
2727
#include <linux/mod_devicetable.h>
28+
#include <linux/phy_led_triggers.h>
2829

2930
#include <linux/atomic.h>
3031

@@ -420,6 +421,12 @@ struct phy_device {
420421

421422
int link_timeout;
422423

424+
#ifdef CONFIG_LED_TRIGGER_PHY
425+
struct phy_led_trigger *phy_led_triggers;
426+
unsigned int phy_num_led_triggers;
427+
struct phy_led_trigger *last_triggered;
428+
#endif
429+
423430
/*
424431
* Interrupt number for this PHY
425432
* -1 means no interrupt

include/linux/phy_led_triggers.h

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/* Copyright (C) 2016 National Instruments Corp.
2+
*
3+
* This program is free software; you can redistribute it and/or modify
4+
* it under the terms of the GNU General Public License as published by
5+
* the Free Software Foundation; either version 2 of the License, or
6+
* (at your option) any later version.
7+
*
8+
* This program is distributed in the hope that it will be useful,
9+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
* GNU General Public License for more details.
12+
*/
13+
#ifndef __PHY_LED_TRIGGERS
14+
#define __PHY_LED_TRIGGERS
15+
16+
struct phy_device;
17+
18+
#ifdef CONFIG_LED_TRIGGER_PHY
19+
20+
#include <linux/leds.h>
21+
22+
#define PHY_LED_TRIGGER_SPEED_SUFFIX_SIZE 10
23+
#define PHY_MII_BUS_ID_SIZE (20 - 3)
24+
25+
#define PHY_LINK_LED_TRIGGER_NAME_SIZE (PHY_MII_BUS_ID_SIZE + \
26+
FIELD_SIZEOF(struct mdio_device, addr)+\
27+
PHY_LED_TRIGGER_SPEED_SUFFIX_SIZE)
28+
29+
struct phy_led_trigger {
30+
struct led_trigger trigger;
31+
char name[PHY_LINK_LED_TRIGGER_NAME_SIZE];
32+
unsigned int speed;
33+
};
34+
35+
36+
extern int phy_led_triggers_register(struct phy_device *phy);
37+
extern void phy_led_triggers_unregister(struct phy_device *phy);
38+
extern void phy_led_trigger_change_speed(struct phy_device *phy);
39+
40+
#else
41+
42+
static inline int phy_led_triggers_register(struct phy_device *phy)
43+
{
44+
return 0;
45+
}
46+
static inline void phy_led_triggers_unregister(struct phy_device *phy) { }
47+
static inline void phy_led_trigger_change_speed(struct phy_device *phy) { }
48+
49+
#endif
50+
51+
#endif

0 commit comments

Comments
 (0)