aboutsummaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorAbdelkader Boudih <seuros@FreeBSD.org>2026-08-29 18:46:23 +0000
committerAbdelkader Boudih <seuros@FreeBSD.org>2026-08-29 18:46:23 +0000
commitcd7541977e2ce5aebeeeae3e8a375256ee1ecb1d (patch)
tree8aec2d32bef7572fa33671b548f26d48a3092ed8 /sys/dev
parente5283a9da86a0080ab0267bcbff4febb049d323d (diff)
gpioled: defer gpio pin writes to a taskqueue
led(4) invoked the led_t callback with its mutex held, including from the blink callout, so the callback must not sleep. PR: 251032 MFC After: 1 week Reviewed by: adrian Differential Revision: https://reviews.freebsd.org/D59263
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/gpio/gpioled_fdt.c29
1 files changed, 26 insertions, 3 deletions
diff --git a/sys/dev/gpio/gpioled_fdt.c b/sys/dev/gpio/gpioled_fdt.c
index 4003d532dd9f..b6afeba703c0 100644
--- a/sys/dev/gpio/gpioled_fdt.c
+++ b/sys/dev/gpio/gpioled_fdt.c
@@ -36,6 +36,9 @@
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/mutex.h>
+#include <sys/taskqueue.h>
+
+#include <machine/atomic.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus.h>
@@ -50,6 +53,8 @@ struct gpioled
struct gpioleds_softc *parent_sc;
gpio_pin_t pin;
struct cdev *leddev;
+ struct task task;
+ int state;
};
struct gpioleds_softc
@@ -65,14 +70,28 @@ static int gpioled_probe(device_t);
static int gpioled_attach(device_t);
static int gpioled_detach(device_t);
+/* Writes the most recently requested state, not every state update. */
+static void
+gpioled_update(void *arg, int pending __unused)
+{
+ struct gpioled *led;
+
+ led = arg;
+ gpio_pin_set_active(led->pin, atomic_load_int(&led->state) != 0);
+}
+
static void
gpioled_control(void *priv, int onoff)
{
struct gpioled *led;
led = (struct gpioled *)priv;
- if (led->pin)
- gpio_pin_set_active(led->pin, onoff);
+ if (led->pin == NULL)
+ return;
+
+ /* Runs under the led(4) mutex, so the pin write is deferred. */
+ atomic_store_int(&led->state, onoff);
+ taskqueue_enqueue(taskqueue_thread, &led->task);
}
static void
@@ -121,6 +140,8 @@ gpioleds_attach_led(struct gpioleds_softc *sc, phandle_t node,
}
gpio_pin_setflags(led->pin, GPIO_PIN_OUTPUT);
+ TASK_INIT(&led->task, 0, gpioled_update, led);
+
led->leddev = led_create_state(gpioled_control, led, name,
state);
@@ -132,8 +153,10 @@ static void
gpioleds_detach_led(struct gpioled *led)
{
- if (led->leddev != NULL)
+ if (led->leddev != NULL) {
led_destroy(led->leddev);
+ taskqueue_drain(taskqueue_thread, &led->task);
+ }
if (led->pin)
gpio_pin_release(led->pin);