Skip to content

Commit

Permalink
leds: Add the "input" trigger, for pwr_led
Browse files Browse the repository at this point in the history
The "input" trigger makes the associated GPIO an input.  This is to support
the Raspberry Pi PWR LED, which is driven by external hardware in normal use.

N.B. pwr_led is not available on Model A or B boards.

leds-gpio: Implement the brightness_get method

The power LED uses some clever logic that means it is driven
by a voltage measuring circuit when configured as input, otherwise
it is driven by the GPIO output value. This patch wires up the
brightness_get method for leds-gpio so that user-space can monitor
the LED value via /sys/class/gpio/led1/brightness. Using the input
trigger this returns an indication of the system power health,
otherwise it is just whatever value the trigger has written most
recently.

See: #1064
  • Loading branch information
Phil Elwell authored and popcornmix committed Oct 14, 2024
1 parent 5390d80 commit 995b368
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 1 deletion.
17 changes: 16 additions & 1 deletion drivers/leds/leds-gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,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);
Expand All @@ -67,6 +74,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)
{
Expand Down Expand Up @@ -96,6 +110,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)
Expand Down
7 changes: 7 additions & 0 deletions drivers/leds/trigger/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions drivers/leds/trigger/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ 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
Expand Down
55 changes: 55 additions & 0 deletions drivers/leds/trigger/ledtrig-input.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Set LED GPIO to Input "Trigger"
*
* Copyright 2015 Phil Elwell <phil@raspberrypi.org>
*
* 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 <phil@raspberrypi.org>");
MODULE_DESCRIPTION("Set LED GPIO to Input \"trigger\"");
MODULE_LICENSE("GPL");
3 changes: 3 additions & 0 deletions include/linux/leds.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ struct led_classdev {
#define LED_INIT_DEFAULT_TRIGGER BIT(23)
#define LED_REJECT_NAME_CONFLICT BIT(24)
#define LED_MULTI_COLOR BIT(25)
/* Additions for Raspberry Pi PWR LED */
#define SET_GPIO_INPUT BIT(30)
#define SET_GPIO_OUTPUT BIT(31)

/* set_brightness_work / blink_timer flags, atomic, private. */
unsigned long work_flags;
Expand Down

0 comments on commit 995b368

Please sign in to comment.