ledtrig-gpio.c (5149B)
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");