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 <wilfried.mei...@gmail.com>
+ *
+ * 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);
+}

Reply via email to