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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox