Skip to content

Commit

Permalink
net: phy: Add a binding for PHY LEDs
Browse files Browse the repository at this point in the history
Define common binding parsing for all PHY drivers with LEDs using
phylib. Parse the DT as part of the phy_probe and add LEDs to the
linux LED class infrastructure. For the moment, provide a dummy
brightness function, which will later be replaced with a call into the
PHY driver. This allows testing since the LED core might otherwise
reject an LED whose brightness cannot be set.

Add a dependency on LED_CLASS. It either needs to be built in, or not
enabled, since a modular build can result in linker errors.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
lunn authored and davem330 committed Apr 19, 2023
1 parent e5029ed commit 01e5b72
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/net/phy/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ menuconfig PHYLIB
depends on NETDEVICES
select MDIO_DEVICE
select MDIO_DEVRES
depends on LEDS_CLASS || LEDS_CLASS=n
help
Ethernet controllers are usually attached to PHY
devices. This option provides infrastructure for
Expand Down
76 changes: 76 additions & 0 deletions drivers/net/phy/phy_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/mdio.h>
#include <linux/mii.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/netdevice.h>
#include <linux/phy.h>
#include <linux/phy_led_triggers.h>
Expand Down Expand Up @@ -674,6 +676,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id,
device_initialize(&mdiodev->dev);

dev->state = PHY_DOWN;
INIT_LIST_HEAD(&dev->leds);

mutex_init(&dev->lock);
INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);
Expand Down Expand Up @@ -2988,6 +2991,74 @@ static bool phy_drv_supports_irq(struct phy_driver *phydrv)
return phydrv->config_intr && phydrv->handle_interrupt;
}

/* Dummy implementation until calls into PHY driver are added */
static int phy_led_set_brightness(struct led_classdev *led_cdev,
enum led_brightness value)
{
return 0;
}

static int of_phy_led(struct phy_device *phydev,
struct device_node *led)
{
struct device *dev = &phydev->mdio.dev;
struct led_init_data init_data = {};
struct led_classdev *cdev;
struct phy_led *phyled;
int err;

phyled = devm_kzalloc(dev, sizeof(*phyled), GFP_KERNEL);
if (!phyled)
return -ENOMEM;

cdev = &phyled->led_cdev;

err = of_property_read_u8(led, "reg", &phyled->index);
if (err)
return err;

cdev->brightness_set_blocking = phy_led_set_brightness;
cdev->max_brightness = 1;
init_data.devicename = dev_name(&phydev->mdio.dev);
init_data.fwnode = of_fwnode_handle(led);
init_data.devname_mandatory = true;

err = devm_led_classdev_register_ext(dev, cdev, &init_data);
if (err)
return err;

list_add(&phyled->list, &phydev->leds);

return 0;
}

static int of_phy_leds(struct phy_device *phydev)
{
struct device_node *node = phydev->mdio.dev.of_node;
struct device_node *leds, *led;
int err;

if (!IS_ENABLED(CONFIG_OF_MDIO))
return 0;

if (!node)
return 0;

leds = of_get_child_by_name(node, "leds");
if (!leds)
return 0;

for_each_available_child_of_node(leds, led) {
err = of_phy_led(phydev, led);
if (err) {
of_node_put(led);
return err;
}
}

return 0;
}

/**
* fwnode_mdio_find_device - Given a fwnode, find the mdio_device
* @fwnode: pointer to the mdio_device's fwnode
Expand Down Expand Up @@ -3183,6 +3254,11 @@ static int phy_probe(struct device *dev)
/* Set the state to READY by default */
phydev->state = PHY_READY;

/* Get the LEDs from the device tree, and instantiate standard
* LEDs for them.
*/
err = of_phy_leds(phydev);

out:
/* Re-assert the reset signal on error */
if (err)
Expand Down
16 changes: 16 additions & 0 deletions include/linux/phy.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <linux/compiler.h>
#include <linux/spinlock.h>
#include <linux/ethtool.h>
#include <linux/leds.h>
#include <linux/linkmode.h>
#include <linux/netlink.h>
#include <linux/mdio.h>
Expand Down Expand Up @@ -600,6 +601,7 @@ struct macsec_ops;
* @phy_num_led_triggers: Number of triggers in @phy_led_triggers
* @led_link_trigger: LED trigger for link up/down
* @last_triggered: last LED trigger for link speed
* @leds: list of PHY LED structures
* @master_slave_set: User requested master/slave configuration
* @master_slave_get: Current master/slave advertisement
* @master_slave_state: Current master/slave configuration
Expand Down Expand Up @@ -699,6 +701,7 @@ struct phy_device {

struct phy_led_trigger *led_link_trigger;
#endif
struct list_head leds;

/*
* Interrupt number for this PHY
Expand Down Expand Up @@ -834,6 +837,19 @@ struct phy_plca_status {
bool pst;
};

/**
* struct phy_led: An LED driven by the PHY
*
* @list: List of LEDs
* @led_cdev: Standard LED class structure
* @index: Number of the LED
*/
struct phy_led {
struct list_head list;
struct led_classdev led_cdev;
u8 index;
};

/**
* struct phy_driver - Driver structure for a particular PHY type
*
Expand Down

0 comments on commit 01e5b72

Please sign in to comment.