linux/drivers/leds/trigger/ledtrig-gpio.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * ledtrig-gio.c - LED Trigger Based on GPIO events
   4 *
   5 * Copyright 2009 Felipe Balbi <me@felipebalbi.com>
   6 */
   7
   8#include <linux/module.h>
   9#include <linux/kernel.h>
  10#include <linux/init.h>
  11#include <linux/gpio.h>
  12#include <linux/interrupt.h>
  13#include <linux/leds.h>
  14#include <linux/slab.h>
  15#include "../leds.h"
  16
  17struct gpio_trig_data {
  18        struct led_classdev *led;
  19
  20        unsigned desired_brightness;    /* desired brightness when led is on */
  21        unsigned inverted;              /* true when gpio is inverted */
  22        unsigned gpio;                  /* gpio that triggers the leds */
  23};
  24
  25static irqreturn_t gpio_trig_irq(int irq, void *_led)
  26{
  27        struct led_classdev *led = _led;
  28        struct gpio_trig_data *gpio_data = led_get_trigger_data(led);
  29        int tmp;
  30
  31        tmp = gpio_get_value_cansleep(gpio_data->gpio);
  32        if (gpio_data->inverted)
  33                tmp = !tmp;
  34
  35        if (tmp) {
  36                if (gpio_data->desired_brightness)
  37                        led_set_brightness_nosleep(gpio_data->led,
  38                                           gpio_data->desired_brightness);
  39                else
  40                        led_set_brightness_nosleep(gpio_data->led, LED_FULL);
  41        } else {
  42                led_set_brightness_nosleep(gpio_data->led, LED_OFF);
  43        }
  44
  45        return IRQ_HANDLED;
  46}
  47
  48static ssize_t gpio_trig_brightness_show(struct device *dev,
  49                struct device_attribute *attr, char *buf)
  50{
  51        struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev);
  52
  53        return sprintf(buf, "%u\n", gpio_data->desired_brightness);
  54}
  55
  56static ssize_t gpio_trig_brightness_store(struct device *dev,
  57                struct device_attribute *attr, const char *buf, size_t n)
  58{
  59        struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev);
  60        unsigned desired_brightness;
  61        int ret;
  62
  63        ret = sscanf(buf, "%u", &desired_brightness);
  64        if (ret < 1 || desired_brightness > 255) {
  65                dev_err(dev, "invalid value\n");
  66                return -EINVAL;
  67        }
  68
  69        gpio_data->desired_brightness = desired_brightness;
  70
  71        return n;
  72}
  73static DEVICE_ATTR(desired_brightness, 0644, gpio_trig_brightness_show,
  74                gpio_trig_brightness_store);
  75
  76static ssize_t gpio_trig_inverted_show(struct device *dev,
  77                struct device_attribute *attr, char *buf)
  78{
  79        struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev);
  80
  81        return sprintf(buf, "%u\n", gpio_data->inverted);
  82}
  83
  84static ssize_t gpio_trig_inverted_store(struct device *dev,
  85                struct device_attribute *attr, const char *buf, size_t n)
  86{
  87        struct led_classdev *led = led_trigger_get_led(dev);
  88        struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev);
  89        unsigned long inverted;
  90        int ret;
  91
  92        ret = kstrtoul(buf, 10, &inverted);
  93        if (ret < 0)
  94                return ret;
  95
  96        if (inverted > 1)
  97                return -EINVAL;
  98
  99        gpio_data->inverted = inverted;
 100
 101        /* After inverting, we need to update the LED. */
 102        if (gpio_is_valid(gpio_data->gpio))
 103                gpio_trig_irq(0, led);
 104
 105        return n;
 106}
 107static DEVICE_ATTR(inverted, 0644, gpio_trig_inverted_show,
 108                gpio_trig_inverted_store);
 109
 110static ssize_t gpio_trig_gpio_show(struct device *dev,
 111                struct device_attribute *attr, char *buf)
 112{
 113        struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev);
 114
 115        return sprintf(buf, "%u\n", gpio_data->gpio);
 116}
 117
 118static ssize_t gpio_trig_gpio_store(struct device *dev,
 119                struct device_attribute *attr, const char *buf, size_t n)
 120{
 121        struct led_classdev *led = led_trigger_get_led(dev);
 122        struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev);
 123        unsigned gpio;
 124        int ret;
 125
 126        ret = sscanf(buf, "%u", &gpio);
 127        if (ret < 1) {
 128                dev_err(dev, "couldn't read gpio number\n");
 129                return -EINVAL;
 130        }
 131
 132        if (gpio_data->gpio == gpio)
 133                return n;
 134
 135        if (!gpio_is_valid(gpio)) {
 136                if (gpio_is_valid(gpio_data->gpio))
 137                        free_irq(gpio_to_irq(gpio_data->gpio), led);
 138                gpio_data->gpio = gpio;
 139                return n;
 140        }
 141
 142        ret = request_threaded_irq(gpio_to_irq(gpio), NULL, gpio_trig_irq,
 143                        IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_RISING
 144                        | IRQF_TRIGGER_FALLING, "ledtrig-gpio", led);
 145        if (ret) {
 146                dev_err(dev, "request_irq failed with error %d\n", ret);
 147        } else {
 148                if (gpio_is_valid(gpio_data->gpio))
 149                        free_irq(gpio_to_irq(gpio_data->gpio), led);
 150                gpio_data->gpio = gpio;
 151                /* After changing the GPIO, we need to update the LED. */
 152                gpio_trig_irq(0, led);
 153        }
 154
 155        return ret ? ret : n;
 156}
 157static DEVICE_ATTR(gpio, 0644, gpio_trig_gpio_show, gpio_trig_gpio_store);
 158
 159static struct attribute *gpio_trig_attrs[] = {
 160        &dev_attr_desired_brightness.attr,
 161        &dev_attr_inverted.attr,
 162        &dev_attr_gpio.attr,
 163        NULL
 164};
 165ATTRIBUTE_GROUPS(gpio_trig);
 166
 167static int gpio_trig_activate(struct led_classdev *led)
 168{
 169        struct gpio_trig_data *gpio_data;
 170
 171        gpio_data = kzalloc(sizeof(*gpio_data), GFP_KERNEL);
 172        if (!gpio_data)
 173                return -ENOMEM;
 174
 175        gpio_data->led = led;
 176        gpio_data->gpio = -ENOENT;
 177
 178        led_set_trigger_data(led, gpio_data);
 179
 180        return 0;
 181}
 182
 183static void gpio_trig_deactivate(struct led_classdev *led)
 184{
 185        struct gpio_trig_data *gpio_data = led_get_trigger_data(led);
 186
 187        if (gpio_is_valid(gpio_data->gpio))
 188                free_irq(gpio_to_irq(gpio_data->gpio), led);
 189        kfree(gpio_data);
 190}
 191
 192static struct led_trigger gpio_led_trigger = {
 193        .name           = "gpio",
 194        .activate       = gpio_trig_activate,
 195        .deactivate     = gpio_trig_deactivate,
 196        .groups         = gpio_trig_groups,
 197};
 198module_led_trigger(gpio_led_trigger);
 199
 200MODULE_AUTHOR("Felipe Balbi <me@felipebalbi.com>");
 201MODULE_DESCRIPTION("GPIO LED trigger");
 202MODULE_LICENSE("GPL v2");
 203