aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/leds
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/leds')
-rw-r--r--drivers/leds/leds-gpio.c17
-rw-r--r--drivers/leds/trigger/Kconfig18
-rw-r--r--drivers/leds/trigger/Makefile2
-rw-r--r--drivers/leds/trigger/ledtrig-actpwr.c190
-rw-r--r--drivers/leds/trigger/ledtrig-input.c55
5 files changed, 281 insertions, 1 deletions
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c
index a3428b22de3a..c4e0ceda70a7 100644
--- a/drivers/leds/leds-gpio.c
+++ b/drivers/leds/leds-gpio.c
@@ -50,8 +50,15 @@ static void gpio_led_set(struct led_classdev *led_cdev,
led_dat->platform_gpio_blink_set(led_dat->gpiod, level,
NULL, NULL);
led_dat->blinking = 0;
+ } else if (led_dat->cdev.flags & SET_GPIO_INPUT) {
+ gpiod_direction_input(led_dat->gpiod);
+ led_dat->cdev.flags &= ~SET_GPIO_INPUT;
+ } else if (led_dat->cdev.flags & SET_GPIO_OUTPUT) {
+ gpiod_direction_output(led_dat->gpiod, level);
+ led_dat->cdev.flags &= ~SET_GPIO_OUTPUT;
} else {
- if (led_dat->can_sleep)
+ if (led_dat->can_sleep ||
+ (led_dat->cdev.flags & (SET_GPIO_INPUT | SET_GPIO_OUTPUT) ))
gpiod_set_value_cansleep(led_dat->gpiod, level);
else
gpiod_set_value(led_dat->gpiod, level);
@@ -65,6 +72,13 @@ static int gpio_led_set_blocking(struct led_classdev *led_cdev,
return 0;
}
+static enum led_brightness gpio_led_get(struct led_classdev *led_cdev)
+{
+ struct gpio_led_data *led_dat =
+ container_of(led_cdev, struct gpio_led_data, cdev);
+ return gpiod_get_value_cansleep(led_dat->gpiod) ? LED_FULL : LED_OFF;
+}
+
static int gpio_blink_set(struct led_classdev *led_cdev,
unsigned long *delay_on, unsigned long *delay_off)
{
@@ -94,6 +108,7 @@ static int create_gpio_led(const struct gpio_led *template,
led_dat->platform_gpio_blink_set = blink_set;
led_dat->cdev.blink_set = gpio_blink_set;
}
+ led_dat->cdev.brightness_get = gpio_led_get;
if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP) {
state = gpiod_get_value_cansleep(led_dat->gpiod);
if (state < 0)
diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig
index c11282a74b5a..c48c1c3e9e11 100644
--- a/drivers/leds/trigger/Kconfig
+++ b/drivers/leds/trigger/Kconfig
@@ -113,6 +113,13 @@ config LEDS_TRIGGER_CAMERA
This enables direct flash/torch on/off by the driver, kernel space.
If unsure, say Y.
+config LEDS_TRIGGER_INPUT
+ tristate "LED Input Trigger"
+ depends on LEDS_TRIGGERS
+ help
+ This allows the GPIOs assigned to be LEDs to be initialised to inputs.
+ If unsure, say Y.
+
config LEDS_TRIGGER_PANIC
bool "LED Panic Trigger"
help
@@ -161,4 +168,15 @@ config LEDS_TRIGGER_INPUT_EVENTS
When build as a module this driver will be called ledtrig-input-events.
+config LEDS_TRIGGER_ACTPWR
+ tristate "ACT/PWR Input Trigger"
+ depends on LEDS_TRIGGERS
+ help
+ This trigger is intended for platforms that have one software-
+ controllable LED and no dedicated activity or power LEDs, hence the
+ need to make the one LED perform both functions. It cycles between
+ default-on and an inverted mmc0 every 500ms, guaranteeing that it is
+ on for at least half of the time.
+ If unsure, say N.
+
endif # LEDS_TRIGGERS
diff --git a/drivers/leds/trigger/Makefile b/drivers/leds/trigger/Makefile
index 3b3628889f68..35a6b611a9a9 100644
--- a/drivers/leds/trigger/Makefile
+++ b/drivers/leds/trigger/Makefile
@@ -11,8 +11,10 @@ obj-$(CONFIG_LEDS_TRIGGER_ACTIVITY) += ledtrig-activity.o
obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o
obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT) += ledtrig-transient.o
obj-$(CONFIG_LEDS_TRIGGER_CAMERA) += ledtrig-camera.o
+obj-$(CONFIG_LEDS_TRIGGER_INPUT) += ledtrig-input.o
obj-$(CONFIG_LEDS_TRIGGER_PANIC) += ledtrig-panic.o
obj-$(CONFIG_LEDS_TRIGGER_NETDEV) += ledtrig-netdev.o
obj-$(CONFIG_LEDS_TRIGGER_PATTERN) += ledtrig-pattern.o
obj-$(CONFIG_LEDS_TRIGGER_TTY) += ledtrig-tty.o
obj-$(CONFIG_LEDS_TRIGGER_INPUT_EVENTS) += ledtrig-input-events.o
+obj-$(CONFIG_LEDS_TRIGGER_ACTPWR) += ledtrig-actpwr.o
diff --git a/drivers/leds/trigger/ledtrig-actpwr.c b/drivers/leds/trigger/ledtrig-actpwr.c
new file mode 100644
index 000000000000..d0428f669bfb
--- /dev/null
+++ b/drivers/leds/trigger/ledtrig-actpwr.c
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Activity/power trigger
+ *
+ * Copyright (C) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * Based on Atsushi Nemoto's ledtrig-heartbeat.c, although there may be
+ * nothing left of the original now.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/leds.h>
+#include "../leds.h"
+
+enum {
+ TRIG_ACT,
+ TRIG_PWR,
+
+ TRIG_COUNT
+};
+
+struct actpwr_trig_src {
+ const char *name;
+ int interval;
+ bool invert;
+};
+
+struct actpwr_vled {
+ struct led_classdev cdev;
+ struct actpwr_trig_data *parent;
+ enum led_brightness value;
+ unsigned int interval;
+ bool invert;
+};
+
+struct actpwr_trig_data {
+ struct led_trigger trig;
+ struct actpwr_vled virt_leds[TRIG_COUNT];
+ struct actpwr_vled *active;
+ struct timer_list timer;
+ int next_active;
+};
+
+static int actpwr_trig_activate(struct led_classdev *led_cdev);
+static void actpwr_trig_deactivate(struct led_classdev *led_cdev);
+
+static const struct actpwr_trig_src actpwr_trig_sources[TRIG_COUNT] = {
+ [TRIG_ACT] = { "mmc0", 500, true },
+ [TRIG_PWR] = { "default-on", 500, false },
+};
+
+static struct actpwr_trig_data actpwr_data = {
+ {
+ .name = "actpwr",
+ .activate = actpwr_trig_activate,
+ .deactivate = actpwr_trig_deactivate,
+ }
+};
+
+static void actpwr_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct actpwr_vled *vled = container_of(led_cdev, struct actpwr_vled,
+ cdev);
+ struct actpwr_trig_data *trig = vled->parent;
+
+ if (vled->invert)
+ value = !value;
+ vled->value = value;
+
+ if (vled == trig->active)
+ led_trigger_event(&trig->trig, value);
+}
+
+static int actpwr_brightness_set_blocking(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ actpwr_brightness_set(led_cdev, value);
+ return 0;
+}
+
+static enum led_brightness actpwr_brightness_get(struct led_classdev *led_cdev)
+{
+ struct actpwr_vled *vled = container_of(led_cdev, struct actpwr_vled,
+ cdev);
+
+ return vled->value;
+}
+
+static void actpwr_trig_cycle(struct timer_list *t)
+{
+ struct actpwr_trig_data *trig = &actpwr_data;
+ struct actpwr_vled *active;
+
+ active = &trig->virt_leds[trig->next_active];
+ trig->active = active;
+ trig->next_active = (trig->next_active + 1) % TRIG_COUNT;
+
+ led_trigger_event(&trig->trig, active->value);
+
+ mod_timer(&trig->timer, jiffies + msecs_to_jiffies(active->interval));
+}
+
+static int actpwr_trig_activate(struct led_classdev *led_cdev)
+{
+ struct actpwr_trig_data *trig = &actpwr_data;
+
+ /* Start the timer if this is the first LED */
+ if (!trig->active)
+ actpwr_trig_cycle(&trig->timer);
+ else
+ led_set_brightness_nosleep(led_cdev, trig->active->value);
+
+ return 0;
+}
+
+static void actpwr_trig_deactivate(struct led_classdev *led_cdev)
+{
+ struct actpwr_trig_data *trig = &actpwr_data;
+
+ if (list_empty(&trig->trig.led_cdevs)) {
+ timer_delete_sync(&trig->timer);
+ trig->active = NULL;
+ }
+}
+
+static int __init actpwr_trig_init(void)
+{
+ struct actpwr_trig_data *trig = &actpwr_data;
+ int ret = 0;
+ int i;
+
+ timer_setup(&trig->timer, actpwr_trig_cycle, 0);
+
+ /* Register one "LED" for each source trigger */
+ for (i = 0; i < TRIG_COUNT; i++)
+ {
+ struct actpwr_vled *vled = &trig->virt_leds[i];
+ struct led_classdev *cdev = &vled->cdev;
+ const struct actpwr_trig_src *src = &actpwr_trig_sources[i];
+
+ vled->parent = trig;
+ vled->interval = src->interval;
+ vled->invert = src->invert;
+ cdev->name = src->name;
+ cdev->brightness_set = actpwr_brightness_set;
+ cdev->brightness_set_blocking = actpwr_brightness_set_blocking;
+ cdev->brightness_get = actpwr_brightness_get;
+ cdev->default_trigger = src->name;
+ ret = led_classdev_register(NULL, cdev);
+ if (ret)
+ goto error_classdev;
+ }
+
+ ret = led_trigger_register(&trig->trig);
+ if (ret)
+ goto error_classdev;
+
+ return 0;
+
+error_classdev:
+ while (i > 0)
+ {
+ i--;
+ led_classdev_unregister(&trig->virt_leds[i].cdev);
+ }
+
+ return ret;
+}
+
+static void __exit actpwr_trig_exit(void)
+{
+ int i;
+
+ led_trigger_unregister(&actpwr_data.trig);
+ for (i = 0; i < TRIG_COUNT; i++)
+ {
+ led_classdev_unregister(&actpwr_data.virt_leds[i].cdev);
+ }
+}
+
+module_init(actpwr_trig_init);
+module_exit(actpwr_trig_exit);
+
+MODULE_AUTHOR("Phil Elwell <[email protected]>");
+MODULE_DESCRIPTION("ACT/PWR LED trigger");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/leds/trigger/ledtrig-input.c b/drivers/leds/trigger/ledtrig-input.c
new file mode 100644
index 000000000000..8a974a355656
--- /dev/null
+++ b/drivers/leds/trigger/ledtrig-input.c
@@ -0,0 +1,55 @@
+/*
+ * Set LED GPIO to Input "Trigger"
+ *
+ * Copyright 2015 Phil Elwell <[email protected]>
+ *
+ * Based on Nick Forbes's ledtrig-default-on.c.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+#include <linux/gpio.h>
+#include "../leds.h"
+
+static int input_trig_activate(struct led_classdev *led_cdev)
+{
+ led_cdev->flags |= SET_GPIO_INPUT;
+ led_set_brightness(led_cdev, 0);
+ return 0;
+}
+
+static void input_trig_deactivate(struct led_classdev *led_cdev)
+{
+ led_cdev->flags |= SET_GPIO_OUTPUT;
+ led_set_brightness(led_cdev, 0);
+}
+
+static struct led_trigger input_led_trigger = {
+ .name = "input",
+ .activate = input_trig_activate,
+ .deactivate = input_trig_deactivate,
+};
+
+static int __init input_trig_init(void)
+{
+ return led_trigger_register(&input_led_trigger);
+}
+
+static void __exit input_trig_exit(void)
+{
+ led_trigger_unregister(&input_led_trigger);
+}
+
+module_init(input_trig_init);
+module_exit(input_trig_exit);
+
+MODULE_AUTHOR("Phil Elwell <[email protected]>");
+MODULE_DESCRIPTION("Set LED GPIO to Input \"trigger\"");
+MODULE_LICENSE("GPL");