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        gpio_trig_irq(0, led);
 103
 104        return n;
 105}
 106static DEVICE_ATTR(inverted, 0644, gpio_trig_inverted_show,
 107                gpio_trig_inverted_store);
 108
 109static ssize_t gpio_trig_gpio_show(struct device *dev,
 110                struct device_attribute *attr, char *buf)
 111{
 112        struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev);
 113
 114        return sprintf(buf, "%u\n", gpio_data->gpio);
 115}
 116
 117static ssize_t gpio_trig_gpio_store(struct device *dev,
 118                struct device_attribute *attr, const char *buf, size_t n)
 119{
 120        struct led_classdev *led = led_trigger_get_led(dev);
 121        struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev);
 122        unsigned gpio;
 123        int ret;
 124
 125        ret = sscanf(buf, "%u", &gpio);
 126        if (ret < 1) {
 127                dev_err(dev, "couldn't read gpio number\n");
 128                return -EINVAL;
 129        }
 130
 131        if (gpio_data->gpio == gpio)
 132                return n;
 133
 134        if (!gpio_is_valid(gpio)) {
 135                if (gpio_is_valid(gpio_data->gpio))
 136                        free_irq(gpio_to_irq(gpio_data->gpio), led);
 137                gpio_data->gpio = gpio;
 138                return n;
 139        }
 140
 141        ret = request_threaded_irq(gpio_to_irq(gpio), NULL, gpio_trig_irq,
 142                        IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_RISING
 143                        | IRQF_TRIGGER_FALLING, "ledtrig-gpio", led);
 144        if (ret) {
 145                dev_err(dev, "request_irq failed with error %d\n", ret);
 146        } else {
 147                if (gpio_is_valid(gpio_data->gpio))
 148                        free_irq(gpio_to_irq(gpio_data->gpio), led);
 149                gpio_data->gpio = gpio;
 150                /* After changing the GPIO, we need to update the LED. */
 151                gpio_trig_irq(0, led);
 152        }
 153
 154        return ret ? ret : n;
 155}
 156static DEVICE_ATTR(gpio, 0644, gpio_trig_gpio_show, gpio_trig_gpio_store);
 157
 158static struct attribute *gpio_trig_attrs[] = {
 159        &dev_attr_desired_brightness.attr,
 160        &dev_attr_inverted.attr,
 161        &dev_attr_gpio.attr,
 162        NULL
 163};
 164ATTRIBUTE_GROUPS(gpio_trig);
 165
 166static int gpio_trig_activate(struct led_classdev *led)
 167{
 168        struct gpio_trig_data *gpio_data;
 169
 170        gpio_data = kzalloc(sizeof(*gpio_data), GFP_KERNEL);
 171        if (!gpio_data)
 172                return -ENOMEM;
 173
 174        gpio_data->led = led;
 175        gpio_data->gpio = -ENOENT;
 176
 177        led_set_trigger_data(led, gpio_data);
 178
 179        return 0;
 180}
 181
 182static void gpio_trig_deactivate(struct led_classdev *led)
 183{
 184        struct gpio_trig_data *gpio_data = led_get_trigger_data(led);
 185
 186        if (gpio_is_valid(gpio_data->gpio))
 187                free_irq(gpio_to_irq(gpio_data->gpio), led);
 188        kfree(gpio_data);
 189}
 190
 191static struct led_trigger gpio_led_trigger = {
 192        .name           = "gpio",
 193        .activate       = gpio_trig_activate,
 194        .deactivate     = gpio_trig_deactivate,
 195        .groups         = gpio_trig_groups,
 196};
 197module_led_trigger(gpio_led_trigger);
 198
 199MODULE_AUTHOR("Felipe Balbi <me@felipebalbi.com>");
 200MODULE_DESCRIPTION("GPIO LED trigger");
 201MODULE_LICENSE("GPL v2");
 202