Driver for heartbeat LEDs

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

Driver for heartbeat LEDs

wilfried.meindl
Hi,

this is a simple driver for the heartbeat status LED found on many ARM
boards. I find it quite useful on headless systems to see if the kernel
has booted, is still running or has shut down.

I am not sure if this driver is worth being included in the kernel. Still
I am sharing it here with anybody who is interested.

This driver depends on the LEDs having a linux,default-trigger="heartbeat"
property in the device tree. So you might have to modify your device tree.

Suggestions for improvement are welcome.

Cheers,
Wilfried

PS: I should mention that this is a diff against the stable tree.


Index: sys/arch/arm64/conf/GENERIC
===================================================================
RCS file: /cvs/src/sys/arch/arm64/conf/GENERIC,v
retrieving revision 1.173
diff -u -p -u -p -r1.173 GENERIC
--- sys/arch/arm64/conf/GENERIC 31 Jul 2020 12:47:43 -0000 1.173
+++ sys/arch/arm64/conf/GENERIC 9 Jan 2021 18:10:51 -0000
@@ -79,6 +79,7 @@ bwfm* at sdmmc? # Broadcom FullMAC
 xhci* at fdt?
 ccp* at fdt? # AMD Cryptographic Co-processor
 ipmi* at fdt?
+leds* at fdt?
 
 # NS16550 compatible serial ports
 com* at fdt?
Index: sys/dev/fdt/files.fdt
===================================================================
RCS file: /cvs/src/sys/dev/fdt/files.fdt,v
retrieving revision 1.141
diff -u -p -u -p -r1.141 files.fdt
--- sys/dev/fdt/files.fdt 25 Jun 2020 12:09:11 -0000 1.141
+++ sys/dev/fdt/files.fdt 9 Jan 2021 18:10:53 -0000
@@ -542,3 +542,7 @@ file dev/fdt/es8316ac.c escodec
 device cwfg
 attach cwfg at i2c
 file dev/fdt/cwfg.c cwfg
+
+device leds
+attach leds at fdt
+file dev/fdt/leds.c leds
Index: sys/dev/fdt/leds.c
===================================================================
RCS file: sys/dev/fdt/leds.c
diff -N sys/dev/fdt/leds.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ sys/dev/fdt/leds.c 9 Jan 2021 18:10:53 -0000
@@ -0,0 +1,110 @@
+/* $OpenBSD$ */
+/*
+ * Copyright (c) 2020 Wilfried Meindl <[hidden email]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/malloc.h>
+#include <sys/timeout.h>
+
+#include <machine/fdt.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_gpio.h>
+
+struct leds_softc {
+ struct device sc_dev;
+};
+
+struct leds_led {
+ struct timeout to;
+ uint32_t *gpio;
+ int state;
+};
+
+int leds_match(struct device *, void *, void *);
+void leds_attach(struct device *, struct device *, void *);
+
+struct cfattach leds_ca = {
+ sizeof (struct leds_softc),
+ leds_match,
+ leds_attach
+};
+
+struct cfdriver leds_cd = {
+ NULL, "leds", DV_DULL
+};
+
+void leds_beat(void *);
+
+int
+leds_match(struct device *parent, void *match, void *aux)
+{
+ struct fdt_attach_args *faa = aux;
+
+ return OF_is_compatible(faa->fa_node, "gpio-leds");
+}
+
+void
+leds_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct fdt_attach_args *faa = aux;
+ struct leds_led *led;
+ char buf[32];
+ int count, len, node;
+
+ count = 0;
+ for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) {
+ len = OF_getproplen(node, "gpios");
+ if (len <= 0)
+ continue;
+
+ if (OF_getprop(node, "linux,default-trigger", buf,
+    sizeof(buf)) != -1) {
+ if (!strncmp(buf, "heartbeat", sizeof(buf))) {
+ led = malloc(sizeof(struct leds_led), M_DEVBUF,
+    M_WAITOK);
+
+ led->gpio = malloc(len, M_DEVBUF, M_WAITOK);
+ OF_getpropintarray(node, "gpios", led->gpio,
+    len);
+ gpio_controller_config_pin(led->gpio,
+    GPIO_CONFIG_OUTPUT);
+
+ timeout_set(&led->to, leds_beat, led);
+ led->state = 0;
+ leds_beat(led);
+
+ count++;
+ }
+ }
+ }
+ printf(": %d %s\n", count, count == 1 ? "LED" : "LEDs");
+}
+
+void
+leds_beat(void *arg)
+{
+ struct leds_led *led = arg;
+ int timeout;
+
+ timeout = led->state == 3 ? 625 : 125;
+ led->state = (led->state + 1) % 4;
+
+ gpio_controller_set_pin(led->gpio, led->state % 2);
+ timeout_add_msec(&led->to, timeout);
+}