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); +}