From: Carlo Szelinsky <github@szelinsky.de>
To: o.rempel@pengutronix.de, kory.maincent@bootlin.com
Cc: andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
kuba@kernel.org, pabeni@redhat.com, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org,
Carlo Szelinsky <github@szelinsky.de>
Subject: [PATCH] net: pse-pd: add LED trigger support
Date: Sun, 15 Mar 2026 00:59:16 +0100 [thread overview]
Message-ID: <20260314235916.2391678-1-github@szelinsky.de> (raw)
Add LED trigger registration and polling into the PSE core subsystem.
Per-PI "delivering" and "enabled" triggers are registered for each PSE
controller, with a configurable poll interval via the DT property
"led-poll-interval-ms".
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
---
drivers/net/pse-pd/pse_core.c | 123 ++++++++++++++++++++++++++++++++++
include/linux/pse-pd/pse.h | 23 +++++++
2 files changed, 146 insertions(+)
diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c
index 3beaaaeec9e1..589a9b403916 100644
--- a/drivers/net/pse-pd/pse_core.c
+++ b/drivers/net/pse-pd/pse_core.c
@@ -15,6 +15,7 @@
#include <linux/regulator/machine.h>
#include <linux/rtnetlink.h>
#include <net/net_trackers.h>
+#include <linux/leds.h>
#define PSE_PW_D_LIMIT INT_MAX
@@ -1030,6 +1031,122 @@ static void pse_send_ntf_worker(struct work_struct *work)
}
}
+#if IS_ENABLED(CONFIG_LEDS_TRIGGERS)
+static void pse_led_poll_work(struct work_struct *work)
+{
+ struct pse_controller_dev *pcdev =
+ container_of(work, struct pse_controller_dev,
+ led_poll_work.work);
+ int i;
+
+ mutex_lock(&pcdev->lock);
+ for (i = 0; i < pcdev->nr_lines; i++) {
+ struct pse_pi_led_triggers *trigs = &pcdev->pi_led_trigs[i];
+ struct pse_pw_status pw_status = {};
+ struct pse_admin_state admin_state = {};
+ bool delivering, enabled;
+
+ if (!pcdev->pi[i].rdev)
+ continue;
+
+ if (pcdev->ops->pi_get_pw_status(pcdev, i, &pw_status))
+ continue;
+ if (pcdev->ops->pi_get_admin_state(pcdev, i, &admin_state))
+ continue;
+
+ delivering = pw_status.c33_pw_status ==
+ ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING;
+ enabled = admin_state.c33_admin_state ==
+ ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED;
+
+ if (trigs->last_delivering != delivering) {
+ trigs->last_delivering = delivering;
+ led_trigger_event(&trigs->delivering,
+ delivering ? LED_FULL : LED_OFF);
+ }
+
+ if (trigs->last_enabled != enabled) {
+ trigs->last_enabled = enabled;
+ led_trigger_event(&trigs->enabled,
+ enabled ? LED_FULL : LED_OFF);
+ }
+ }
+ mutex_unlock(&pcdev->lock);
+
+ schedule_delayed_work(&pcdev->led_poll_work,
+ msecs_to_jiffies(pcdev->led_poll_interval_ms));
+}
+
+static int pse_led_triggers_register(struct pse_controller_dev *pcdev)
+{
+ struct device *dev = pcdev->dev;
+ const char *node_name;
+ int i, ret;
+
+ node_name = dev->of_node ? dev->of_node->name : dev_name(dev);
+
+ of_property_read_u32(dev->of_node, "led-poll-interval-ms",
+ &pcdev->led_poll_interval_ms);
+ if (!pcdev->led_poll_interval_ms)
+ pcdev->led_poll_interval_ms = 500;
+
+ pcdev->pi_led_trigs = devm_kcalloc(dev, pcdev->nr_lines,
+ sizeof(*pcdev->pi_led_trigs),
+ GFP_KERNEL);
+ if (!pcdev->pi_led_trigs)
+ return -ENOMEM;
+
+ INIT_DELAYED_WORK(&pcdev->led_poll_work, pse_led_poll_work);
+
+ for (i = 0; i < pcdev->nr_lines; i++) {
+ struct pse_pi_led_triggers *trigs = &pcdev->pi_led_trigs[i];
+
+ if (!pcdev->pi[i].rdev)
+ continue;
+
+ trigs->delivering.name = devm_kasprintf(dev, GFP_KERNEL,
+ "%s:port%d:delivering",
+ node_name, i);
+ if (!trigs->delivering.name)
+ return -ENOMEM;
+
+ ret = devm_led_trigger_register(dev, &trigs->delivering);
+ if (ret)
+ return ret;
+
+ trigs->enabled.name = devm_kasprintf(dev, GFP_KERNEL,
+ "%s:port%d:enabled",
+ node_name, i);
+ if (!trigs->enabled.name)
+ return -ENOMEM;
+
+ ret = devm_led_trigger_register(dev, &trigs->enabled);
+ if (ret)
+ return ret;
+ }
+
+ schedule_delayed_work(&pcdev->led_poll_work,
+ msecs_to_jiffies(pcdev->led_poll_interval_ms));
+
+ return 0;
+}
+
+static void pse_led_triggers_cancel(struct pse_controller_dev *pcdev)
+{
+ if (pcdev->pi_led_trigs)
+ cancel_delayed_work_sync(&pcdev->led_poll_work);
+}
+#else
+static int pse_led_triggers_register(struct pse_controller_dev *pcdev)
+{
+ return 0;
+}
+
+static void pse_led_triggers_cancel(struct pse_controller_dev *pcdev)
+{
+}
+#endif /* CONFIG_LEDS_TRIGGERS */
+
/**
* pse_controller_register - register a PSE controller device
* @pcdev: a pointer to the initialized PSE controller device
@@ -1104,6 +1221,11 @@ int pse_controller_register(struct pse_controller_dev *pcdev)
list_add(&pcdev->list, &pse_controller_list);
mutex_unlock(&pse_list_mutex);
+ ret = pse_led_triggers_register(pcdev);
+ if (ret)
+ dev_warn(pcdev->dev, "Failed to register LED triggers: %d\n",
+ ret);
+
return 0;
}
EXPORT_SYMBOL_GPL(pse_controller_register);
@@ -1114,6 +1236,7 @@ EXPORT_SYMBOL_GPL(pse_controller_register);
*/
void pse_controller_unregister(struct pse_controller_dev *pcdev)
{
+ pse_led_triggers_cancel(pcdev);
pse_flush_pw_ds(pcdev);
pse_release_pis(pcdev);
if (pcdev->irq)
diff --git a/include/linux/pse-pd/pse.h b/include/linux/pse-pd/pse.h
index 4e5696cfade7..9e5d0123223e 100644
--- a/include/linux/pse-pd/pse.h
+++ b/include/linux/pse-pd/pse.h
@@ -11,6 +11,7 @@
#include <uapi/linux/ethtool.h>
#include <uapi/linux/ethtool_netlink_generated.h>
#include <linux/regulator/driver.h>
+#include <linux/leds.h>
/* Maximum current in uA according to IEEE 802.3-2022 Table 145-1 */
#define MAX_PI_CURRENT 1920000
@@ -266,6 +267,23 @@ struct pse_pi {
int pw_allocated_mW;
};
+#if IS_ENABLED(CONFIG_LEDS_TRIGGERS)
+/**
+ * struct pse_pi_led_triggers - LED trigger state for a PSE PI
+ *
+ * @delivering: LED trigger for power delivering state
+ * @enabled: LED trigger for admin enabled state
+ * @last_delivering: cached delivering state for change detection
+ * @last_enabled: cached enabled state for change detection
+ */
+struct pse_pi_led_triggers {
+ struct led_trigger delivering;
+ struct led_trigger enabled;
+ bool last_delivering;
+ bool last_enabled;
+};
+#endif
+
/**
* struct pse_ntf - PSE notification element
*
@@ -317,6 +335,11 @@ struct pse_controller_dev {
struct work_struct ntf_work;
DECLARE_KFIFO_PTR(ntf_fifo, struct pse_ntf);
spinlock_t ntf_fifo_lock; /* Protect @ntf_fifo writer */
+#if IS_ENABLED(CONFIG_LEDS_TRIGGERS)
+ struct pse_pi_led_triggers *pi_led_trigs;
+ struct delayed_work led_poll_work;
+ unsigned int led_poll_interval_ms;
+#endif
};
/**
--
2.43.0
next reply other threads:[~2026-03-15 0:07 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-14 23:59 Carlo Szelinsky [this message]
2026-03-15 16:58 ` [PATCH] net: pse-pd: add LED trigger support Oleksij Rempel
2026-03-15 21:12 ` Carlo Szelinsky
2026-03-16 6:12 ` Oleksij Rempel
2026-03-16 14:44 ` Kory Maincent
2026-03-18 20:55 ` Andrew Lunn
2026-03-21 17:55 ` Carlo Szelinsky
2026-03-23 10:30 ` Kory Maincent
2026-03-23 20:27 ` Carlo Szelinsky
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260314235916.2391678-1-github@szelinsky.de \
--to=github@szelinsky.de \
--cc=andrew+netdev@lunn.ch \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=kory.maincent@bootlin.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=o.rempel@pengutronix.de \
--cc=pabeni@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.