Branch data Line data Source code
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 : : 17 : : struct 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 : : 25 : 0 : static 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 : 0 : tmp = gpio_get_value_cansleep(gpio_data->gpio); 32 : 0 : if (gpio_data->inverted) 33 : 0 : tmp = !tmp; 34 : : 35 : 0 : if (tmp) { 36 : 0 : if (gpio_data->desired_brightness) 37 : 0 : led_set_brightness_nosleep(gpio_data->led, 38 : : gpio_data->desired_brightness); 39 : : else 40 : 0 : led_set_brightness_nosleep(gpio_data->led, LED_FULL); 41 : : } else { 42 : 0 : led_set_brightness_nosleep(gpio_data->led, LED_OFF); 43 : : } 44 : : 45 : 0 : return IRQ_HANDLED; 46 : : } 47 : : 48 : 0 : static 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 : 0 : return sprintf(buf, "%u\n", gpio_data->desired_brightness); 54 : : } 55 : : 56 : 0 : static 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 : 0 : ret = sscanf(buf, "%u", &desired_brightness); 64 : 0 : if (ret < 1 || desired_brightness > 255) { 65 : 0 : dev_err(dev, "invalid value\n"); 66 : 0 : return -EINVAL; 67 : : } 68 : : 69 : 0 : gpio_data->desired_brightness = desired_brightness; 70 : : 71 : 0 : return n; 72 : : } 73 : : static DEVICE_ATTR(desired_brightness, 0644, gpio_trig_brightness_show, 74 : : gpio_trig_brightness_store); 75 : : 76 : 0 : static 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 : 0 : return sprintf(buf, "%u\n", gpio_data->inverted); 82 : : } 83 : : 84 : 0 : static 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 : 0 : if (ret < 0) 94 : : return ret; 95 : : 96 : 0 : if (inverted > 1) 97 : : return -EINVAL; 98 : : 99 : 0 : gpio_data->inverted = inverted; 100 : : 101 : : /* After inverting, we need to update the LED. */ 102 : 0 : gpio_trig_irq(0, led); 103 : : 104 : 0 : return n; 105 : : } 106 : : static DEVICE_ATTR(inverted, 0644, gpio_trig_inverted_show, 107 : : gpio_trig_inverted_store); 108 : : 109 : 0 : static 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 : 0 : return sprintf(buf, "%u\n", gpio_data->gpio); 115 : : } 116 : : 117 : 0 : static 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 : 0 : ret = sscanf(buf, "%u", &gpio); 126 : 0 : if (ret < 1) { 127 : 0 : dev_err(dev, "couldn't read gpio number\n"); 128 : 0 : return -EINVAL; 129 : : } 130 : : 131 : 0 : if (gpio_data->gpio == gpio) 132 : 0 : return n; 133 : : 134 : 0 : if (!gpio_is_valid(gpio)) { 135 : 0 : if (gpio_is_valid(gpio_data->gpio)) 136 : 0 : free_irq(gpio_to_irq(gpio_data->gpio), led); 137 : 0 : gpio_data->gpio = gpio; 138 : 0 : return n; 139 : : } 140 : : 141 : 0 : 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 : 0 : if (ret) { 145 : 0 : dev_err(dev, "request_irq failed with error %d\n", ret); 146 : : } else { 147 : 0 : if (gpio_is_valid(gpio_data->gpio)) 148 : 0 : free_irq(gpio_to_irq(gpio_data->gpio), led); 149 : 0 : gpio_data->gpio = gpio; 150 : : /* After changing the GPIO, we need to update the LED. */ 151 : 0 : gpio_trig_irq(0, led); 152 : : } 153 : : 154 : 0 : return ret ? ret : n; 155 : : } 156 : : static DEVICE_ATTR(gpio, 0644, gpio_trig_gpio_show, gpio_trig_gpio_store); 157 : : 158 : : static struct attribute *gpio_trig_attrs[] = { 159 : : &dev_attr_desired_brightness.attr, 160 : : &dev_attr_inverted.attr, 161 : : &dev_attr_gpio.attr, 162 : : NULL 163 : : }; 164 : : ATTRIBUTE_GROUPS(gpio_trig); 165 : : 166 : 0 : static int gpio_trig_activate(struct led_classdev *led) 167 : : { 168 : : struct gpio_trig_data *gpio_data; 169 : : 170 : 0 : gpio_data = kzalloc(sizeof(*gpio_data), GFP_KERNEL); 171 : 0 : if (!gpio_data) 172 : : return -ENOMEM; 173 : : 174 : 0 : gpio_data->led = led; 175 : 0 : gpio_data->gpio = -ENOENT; 176 : : 177 : : led_set_trigger_data(led, gpio_data); 178 : : 179 : 0 : return 0; 180 : : } 181 : : 182 : 0 : static void gpio_trig_deactivate(struct led_classdev *led) 183 : : { 184 : : struct gpio_trig_data *gpio_data = led_get_trigger_data(led); 185 : : 186 : 0 : if (gpio_is_valid(gpio_data->gpio)) 187 : 0 : free_irq(gpio_to_irq(gpio_data->gpio), led); 188 : 0 : kfree(gpio_data); 189 : 0 : } 190 : : 191 : : static 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 : : }; 197 : 3 : module_led_trigger(gpio_led_trigger); 198 : : 199 : : MODULE_AUTHOR("Felipe Balbi <me@felipebalbi.com>"); 200 : : MODULE_DESCRIPTION("GPIO LED trigger"); 201 : : MODULE_LICENSE("GPL v2");