From: Carlo Szelinsky <github@szelinsky.de>
To: Oleksij Rempel <o.rempel@pengutronix.de>,
Kory Maincent <kory.maincent@bootlin.com>
Cc: Andrew Lunn <andrew+netdev@lunn.ch>,
"David S . Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@google.com>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
Krzysztof Kozlowski <krzk@kernel.org>,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-leds@vger.kernel.org, Carlo Szelinsky <github@szelinsky.de>
Subject: [PATCH net-next v5 1/2] net: pse-pd: add devm_pse_poll_helper()
Date: Wed, 29 Apr 2026 23:32:23 +0200 [thread overview]
Message-ID: <20260429213224.1747410-2-github@szelinsky.de> (raw)
In-Reply-To: <20260429213224.1747410-1-github@szelinsky.de>
Extract the common event handling loop from pse_isr() into a shared
pse_handle_events() function, and add a generic poll-based alternative
to the IRQ path for PSE controllers that lack interrupt support or
have IRQ lines not wired on the board.
The new devm_pse_poll_helper() function sets up a delayed work that
periodically calls the driver's map_event callback to detect state
changes, feeding events into the existing ntf_fifo / pse_send_ntf_worker
notification pipeline. This reuses the same pse_irq_desc interface as
the IRQ path: the driver provides a map_event callback that populates
per-PI notification arrays.
The poll worker uses system_freezable_wq to avoid running during system
suspend when the underlying hardware (e.g. I2C bus) may be inaccessible.
Cancel the poll work explicitly in pse_controller_unregister() (next to
the existing disable_irq() for the IRQ path) so it cannot outlive the
controller it pushes events into. This avoids relying on devres LIFO
ordering between devm_pse_controller_register() and devm_pse_poll_helper()
when a driver follows the standard pattern of setting up the helper
before registering the controller.
The notifs_mask is allocated as a real bitmap via devm_bitmap_zalloc()
and passed to pse_handle_events() and the driver's map_event callback
as a pointer (mirroring the pattern from commit 5099807f335c
("net: pse-pd: fix out-of-bounds bitmap access in pse_isr() on 32-bit"))
so for_each_set_bit() does not read past a single unsigned long when
nr_lines > BITS_PER_LONG.
The poll interval defaults to 500ms, balancing responsiveness against
bus load (e.g. I2C).
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
---
drivers/net/pse-pd/pse_core.c | 157 +++++++++++++++++++++++++++-------
include/linux/pse-pd/pse.h | 14 +++
2 files changed, 140 insertions(+), 31 deletions(-)
diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c
index 87aa4f4e9724..b7ffec0c942c 100644
--- a/drivers/net/pse-pd/pse_core.c
+++ b/drivers/net/pse-pd/pse_core.c
@@ -14,10 +14,17 @@
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/rtnetlink.h>
+#include <linux/workqueue.h>
#include <net/net_trackers.h>
#define PSE_PW_D_LIMIT INT_MAX
+/* Default poll interval for controllers without IRQ support.
+ * 500ms provides a reasonable trade-off between responsiveness
+ * (event detection, PD detection) and I2C bus utilization.
+ */
+#define PSE_DEFAULT_POLL_INTERVAL_MS 500
+
static DEFINE_MUTEX(pse_list_mutex);
static LIST_HEAD(pse_controller_list);
static DEFINE_XARRAY_ALLOC(pse_pw_d_map);
@@ -1118,6 +1125,8 @@ void pse_controller_unregister(struct pse_controller_dev *pcdev)
pse_release_pis(pcdev);
if (pcdev->irq)
disable_irq(pcdev->irq);
+ if (pcdev->polling)
+ cancel_delayed_work_sync(&pcdev->poll_work);
cancel_work_sync(&pcdev->ntf_work);
kfifo_free(&pcdev->ntf_fifo);
mutex_lock(&pse_list_mutex);
@@ -1239,66 +1248,104 @@ static int pse_set_config_isr(struct pse_controller_dev *pcdev, int id,
}
/**
- * pse_isr - IRQ handler for PSE
- * @irq: irq number
- * @data: pointer to user interrupt structure
+ * pse_handle_events - Process PSE events for all PIs
+ * @pcdev: a pointer to the PSE controller device
+ * @notifs: per-PI notification array
+ * @notifs_mask: bitmap of PIs with events (sized for pcdev->nr_lines)
*
- * Return: irqreturn_t - status of IRQ
+ * Common event handling shared between IRQ and poll paths.
+ * Caller must hold pcdev->lock.
*/
-static irqreturn_t pse_isr(int irq, void *data)
+static void pse_handle_events(struct pse_controller_dev *pcdev,
+ unsigned long *notifs,
+ unsigned long *notifs_mask)
{
- struct pse_controller_dev *pcdev;
- struct pse_irq_desc *desc;
- struct pse_irq *h = data;
- int ret, i;
-
- desc = &h->desc;
- pcdev = h->pcdev;
-
- /* Clear notifs mask */
- memset(h->notifs, 0, pcdev->nr_lines * sizeof(*h->notifs));
- bitmap_zero(h->notifs_mask, pcdev->nr_lines);
- mutex_lock(&pcdev->lock);
- ret = desc->map_event(irq, pcdev, h->notifs, h->notifs_mask);
- if (ret || bitmap_empty(h->notifs_mask, pcdev->nr_lines)) {
- mutex_unlock(&pcdev->lock);
- return IRQ_NONE;
- }
+ int i;
- for_each_set_bit(i, h->notifs_mask, pcdev->nr_lines) {
- unsigned long notifs, rnotifs;
+ for_each_set_bit(i, notifs_mask, pcdev->nr_lines) {
+ unsigned long pi_notifs, rnotifs;
struct pse_ntf ntf = {};
+ int ret;
/* Do nothing PI not described */
if (!pcdev->pi[i].rdev)
continue;
- notifs = h->notifs[i];
+ pi_notifs = notifs[i];
if (pse_pw_d_is_sw_pw_control(pcdev, pcdev->pi[i].pw_d)) {
- ret = pse_set_config_isr(pcdev, i, notifs);
+ ret = pse_set_config_isr(pcdev, i, pi_notifs);
if (ret)
- notifs |= ETHTOOL_PSE_EVENT_SW_PW_CONTROL_ERROR;
+ pi_notifs |= ETHTOOL_PSE_EVENT_SW_PW_CONTROL_ERROR;
}
- dev_dbg(h->pcdev->dev,
- "Sending PSE notification EVT 0x%lx\n", notifs);
+ dev_dbg(pcdev->dev,
+ "Sending PSE notification EVT 0x%lx\n", pi_notifs);
- ntf.notifs = notifs;
+ ntf.notifs = pi_notifs;
ntf.id = i;
kfifo_in_spinlocked(&pcdev->ntf_fifo, &ntf, 1,
&pcdev->ntf_fifo_lock);
schedule_work(&pcdev->ntf_work);
- rnotifs = pse_to_regulator_notifs(notifs);
+ rnotifs = pse_to_regulator_notifs(pi_notifs);
regulator_notifier_call_chain(pcdev->pi[i].rdev, rnotifs,
NULL);
}
+}
+
+/**
+ * pse_isr - IRQ handler for PSE
+ * @irq: irq number
+ * @data: pointer to user interrupt structure
+ *
+ * Return: irqreturn_t - status of IRQ
+ */
+static irqreturn_t pse_isr(int irq, void *data)
+{
+ struct pse_controller_dev *pcdev;
+ struct pse_irq *h = data;
+ int ret;
+
+ pcdev = h->pcdev;
+ /* Clear notifs mask */
+ memset(h->notifs, 0, pcdev->nr_lines * sizeof(*h->notifs));
+ bitmap_zero(h->notifs_mask, pcdev->nr_lines);
+ mutex_lock(&pcdev->lock);
+ ret = h->desc.map_event(irq, pcdev, h->notifs, h->notifs_mask);
+ if (ret || bitmap_empty(h->notifs_mask, pcdev->nr_lines)) {
+ mutex_unlock(&pcdev->lock);
+ return IRQ_NONE;
+ }
+
+ pse_handle_events(pcdev, h->notifs, h->notifs_mask);
mutex_unlock(&pcdev->lock);
return IRQ_HANDLED;
}
+static void pse_poll_worker(struct work_struct *work)
+{
+ struct pse_controller_dev *pcdev =
+ container_of(work, struct pse_controller_dev,
+ poll_work.work);
+ int ret;
+
+ memset(pcdev->poll_notifs, 0,
+ pcdev->nr_lines * sizeof(*pcdev->poll_notifs));
+ bitmap_zero(pcdev->poll_notifs_mask, pcdev->nr_lines);
+ mutex_lock(&pcdev->lock);
+ ret = pcdev->poll_desc.map_event(0, pcdev, pcdev->poll_notifs,
+ pcdev->poll_notifs_mask);
+ if (!ret && !bitmap_empty(pcdev->poll_notifs_mask, pcdev->nr_lines))
+ pse_handle_events(pcdev, pcdev->poll_notifs,
+ pcdev->poll_notifs_mask);
+ mutex_unlock(&pcdev->lock);
+
+ queue_delayed_work(system_freezable_wq, &pcdev->poll_work,
+ msecs_to_jiffies(pcdev->poll_interval_ms));
+}
+
/**
* devm_pse_irq_helper - Register IRQ based PSE event notifier
* @pcdev: a pointer to the PSE
@@ -1356,6 +1403,54 @@ int devm_pse_irq_helper(struct pse_controller_dev *pcdev, int irq,
}
EXPORT_SYMBOL_GPL(devm_pse_irq_helper);
+/**
+ * devm_pse_poll_helper - Register poll-based PSE event notifier
+ * @pcdev: a pointer to the PSE controller device
+ * @d: PSE event description (uses same pse_irq_desc as IRQ path)
+ *
+ * For PSE controllers without IRQ support or with IRQ not wired. Sets
+ * up a delayed work that periodically calls the driver's map_event
+ * callback to detect state changes, feeding events into the standard
+ * notification pipeline.
+ *
+ * The poll worker uses system_freezable_wq to ensure it does not run
+ * during system suspend while the hardware may be inaccessible.
+ *
+ * Return: 0 on success and errno on failure
+ */
+int devm_pse_poll_helper(struct pse_controller_dev *pcdev,
+ const struct pse_irq_desc *d)
+{
+ struct device *dev = pcdev->dev;
+
+ if (!d || !d->map_event || !d->name)
+ return -EINVAL;
+
+ pcdev->poll_desc = *d;
+ pcdev->poll_notifs = devm_kcalloc(dev, pcdev->nr_lines,
+ sizeof(*pcdev->poll_notifs),
+ GFP_KERNEL);
+ if (!pcdev->poll_notifs)
+ return -ENOMEM;
+
+ pcdev->poll_notifs_mask = devm_bitmap_zalloc(dev, pcdev->nr_lines,
+ GFP_KERNEL);
+ if (!pcdev->poll_notifs_mask)
+ return -ENOMEM;
+
+ if (!pcdev->poll_interval_ms)
+ pcdev->poll_interval_ms = PSE_DEFAULT_POLL_INTERVAL_MS;
+
+ INIT_DELAYED_WORK(&pcdev->poll_work, pse_poll_worker);
+ pcdev->polling = true;
+
+ queue_delayed_work(system_freezable_wq, &pcdev->poll_work,
+ msecs_to_jiffies(pcdev->poll_interval_ms));
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(devm_pse_poll_helper);
+
/* PSE control section */
static void __pse_control_release(struct kref *kref)
diff --git a/include/linux/pse-pd/pse.h b/include/linux/pse-pd/pse.h
index 4e5696cfade7..4b5d5b11a084 100644
--- a/include/linux/pse-pd/pse.h
+++ b/include/linux/pse-pd/pse.h
@@ -292,6 +292,12 @@ struct pse_ntf {
* @pi: table of PSE PIs described in this controller device
* @no_of_pse_pi: flag set if the pse_pis devicetree node is not used
* @irq: PSE interrupt
+ * @polling: flag indicating poll-based event detection is active
+ * @poll_interval_ms: poll interval in milliseconds
+ * @poll_work: delayed work for poll-based event detection
+ * @poll_desc: copy of the driver's event descriptor for polling
+ * @poll_notifs: per-PI notification scratch space for poll worker
+ * @poll_notifs_mask: bitmap of PIs with events for poll worker
* @pis_prio_max: Maximum value allowed for the PSE PIs priority
* @supp_budget_eval_strategies: budget evaluation strategies supported
* by the PSE
@@ -312,6 +318,12 @@ struct pse_controller_dev {
struct pse_pi *pi;
bool no_of_pse_pi;
int irq;
+ bool polling;
+ unsigned int poll_interval_ms;
+ struct delayed_work poll_work;
+ struct pse_irq_desc poll_desc;
+ unsigned long *poll_notifs;
+ unsigned long *poll_notifs_mask;
unsigned int pis_prio_max;
u32 supp_budget_eval_strategies;
struct work_struct ntf_work;
@@ -345,6 +357,8 @@ int devm_pse_controller_register(struct device *dev,
struct pse_controller_dev *pcdev);
int devm_pse_irq_helper(struct pse_controller_dev *pcdev, int irq,
int irq_flags, const struct pse_irq_desc *d);
+int devm_pse_poll_helper(struct pse_controller_dev *pcdev,
+ const struct pse_irq_desc *d);
struct pse_control *of_pse_control_get(struct device_node *node,
struct phy_device *phydev);
--
2.43.0
next prev parent reply other threads:[~2026-04-29 21:33 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-10 12:44 [PATCH net-next v4 0/2] net: pse-pd: add poll path and LED trigger support Carlo Szelinsky
2026-04-10 12:44 ` [PATCH net-next v4 1/2] net: pse-pd: add devm_pse_poll_helper() Carlo Szelinsky
2026-04-13 22:50 ` Jakub Kicinski
2026-04-14 14:05 ` Kory Maincent
2026-04-14 14:11 ` Kory Maincent
2026-04-10 12:44 ` [PATCH net-next v4 2/2] net: pse-pd: add LED trigger support via notification path Carlo Szelinsky
2026-04-13 22:51 ` Jakub Kicinski
2026-04-13 22:53 ` Jakub Kicinski
2026-04-29 21:32 ` [PATCH net-next v5 0/2] net: pse-pd: add poll path and LED trigger support Carlo Szelinsky
2026-04-29 21:32 ` Carlo Szelinsky [this message]
2026-04-29 21:32 ` [PATCH net-next v5 2/2] net: pse-pd: add LED trigger support via notification path 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=20260429213224.1747410-2-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=krzk@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-leds@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