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.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
  • Loading branch information
lunn authored and intel-lab-lkp committed Mar 17, 2023
1 parent 5bce1fa commit 7628377
Show file tree
Hide file tree
Showing 3 changed files with 92 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 @@ -16,6 +16,7 @@ config PHYLINK
menuconfig PHYLIB
tristate "PHY Device support and infrastructure"
depends on NETDEVICES
depends on LEDS_CLASS
select MDIO_DEVICE
select MDIO_DEVRES
help
Expand Down
75 changes: 75 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,73 @@ 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_u32(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);

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 @@ -3187,6 +3257,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:
/* Assert the reset signal */
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;
u32 index;
};

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

0 comments on commit 7628377

Please sign in to comment.