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 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 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 48 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 return sprintf(buf, "%u\n", gpio_data->desired_brightness); 54 } 55 56 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 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 } 73 static DEVICE_ATTR(desired_brightness, 0644, gpio_trig_brightness_show, 74 gpio_trig_brightness_store); 75 76 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 return sprintf(buf, "%u\n", gpio_data->inverted); 82 } 83 84 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 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 } 106 static DEVICE_ATTR(inverted, 0644, gpio_trig_inverted_show, 107 gpio_trig_inverted_store); 108 109 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 return sprintf(buf, "%u\n", gpio_data->gpio); 115 } 116 117 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 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 } 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 static 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 182 static 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 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 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"); 202