blob: f5358c40d03f84f5d068b81eca3e1432a68c9a74 [file] [log] [blame]
Felipe Balbi17354bf2009-02-17 13:18:11 +02001/*
2 * ledtrig-gio.c - LED Trigger Based on GPIO events
3 *
4 * Copyright 2009 Felipe Balbi <me@felipebalbi.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
Felipe Balbi17354bf2009-02-17 13:18:11 +02009 */
10
11#include <linux/module.h>
12#include <linux/kernel.h>
13#include <linux/init.h>
14#include <linux/gpio.h>
15#include <linux/interrupt.h>
Felipe Balbi17354bf2009-02-17 13:18:11 +020016#include <linux/leds.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090017#include <linux/slab.h>
Kim, Milof07fb522013-02-20 00:36:01 -080018#include "../leds.h"
Felipe Balbi17354bf2009-02-17 13:18:11 +020019
20struct gpio_trig_data {
21 struct led_classdev *led;
Felipe Balbi17354bf2009-02-17 13:18:11 +020022
23 unsigned desired_brightness; /* desired brightness when led is on */
24 unsigned inverted; /* true when gpio is inverted */
25 unsigned gpio; /* gpio that triggers the leds */
26};
27
28static irqreturn_t gpio_trig_irq(int irq, void *_led)
29{
30 struct led_classdev *led = _led;
31 struct gpio_trig_data *gpio_data = led->trigger_data;
Felipe Balbi17354bf2009-02-17 13:18:11 +020032 int tmp;
33
Lothar Waßmann914ae252014-09-09 00:40:32 -070034 tmp = gpio_get_value_cansleep(gpio_data->gpio);
Thadeu Lima de Souza Cascardo74cbe202009-08-06 16:04:51 -070035 if (gpio_data->inverted)
36 tmp = !tmp;
Felipe Balbi17354bf2009-02-17 13:18:11 +020037
Thadeu Lima de Souza Cascardo74cbe202009-08-06 16:04:51 -070038 if (tmp) {
39 if (gpio_data->desired_brightness)
Jacek Anaszewski81fe8e52015-10-07 11:10:41 +020040 led_set_brightness_nosleep(gpio_data->led,
Thadeu Lima de Souza Cascardo74cbe202009-08-06 16:04:51 -070041 gpio_data->desired_brightness);
42 else
Jacek Anaszewski81fe8e52015-10-07 11:10:41 +020043 led_set_brightness_nosleep(gpio_data->led, LED_FULL);
Thadeu Lima de Souza Cascardo74cbe202009-08-06 16:04:51 -070044 } else {
Jacek Anaszewski81fe8e52015-10-07 11:10:41 +020045 led_set_brightness_nosleep(gpio_data->led, LED_OFF);
Thadeu Lima de Souza Cascardo74cbe202009-08-06 16:04:51 -070046 }
Jan Kiszka71c17b02017-05-26 15:17:47 +020047
48 return IRQ_HANDLED;
Felipe Balbi17354bf2009-02-17 13:18:11 +020049}
50
51static ssize_t gpio_trig_brightness_show(struct device *dev,
52 struct device_attribute *attr, char *buf)
53{
54 struct led_classdev *led = dev_get_drvdata(dev);
55 struct gpio_trig_data *gpio_data = led->trigger_data;
56
57 return sprintf(buf, "%u\n", gpio_data->desired_brightness);
58}
59
60static ssize_t gpio_trig_brightness_store(struct device *dev,
61 struct device_attribute *attr, const char *buf, size_t n)
62{
63 struct led_classdev *led = dev_get_drvdata(dev);
64 struct gpio_trig_data *gpio_data = led->trigger_data;
65 unsigned desired_brightness;
66 int ret;
67
68 ret = sscanf(buf, "%u", &desired_brightness);
69 if (ret < 1 || desired_brightness > 255) {
70 dev_err(dev, "invalid value\n");
71 return -EINVAL;
72 }
73
74 gpio_data->desired_brightness = desired_brightness;
75
76 return n;
77}
78static DEVICE_ATTR(desired_brightness, 0644, gpio_trig_brightness_show,
79 gpio_trig_brightness_store);
80
81static ssize_t gpio_trig_inverted_show(struct device *dev,
82 struct device_attribute *attr, char *buf)
83{
84 struct led_classdev *led = dev_get_drvdata(dev);
85 struct gpio_trig_data *gpio_data = led->trigger_data;
86
Janusz Krzysztofikcc587ec2011-01-20 14:44:29 -080087 return sprintf(buf, "%u\n", gpio_data->inverted);
Felipe Balbi17354bf2009-02-17 13:18:11 +020088}
89
90static ssize_t gpio_trig_inverted_store(struct device *dev,
91 struct device_attribute *attr, const char *buf, size_t n)
92{
93 struct led_classdev *led = dev_get_drvdata(dev);
94 struct gpio_trig_data *gpio_data = led->trigger_data;
Janusz Krzysztofikcc587ec2011-01-20 14:44:29 -080095 unsigned long inverted;
Felipe Balbi17354bf2009-02-17 13:18:11 +020096 int ret;
97
Jingoo Hane8941922012-10-23 05:27:52 -070098 ret = kstrtoul(buf, 10, &inverted);
Janusz Krzysztofikcc587ec2011-01-20 14:44:29 -080099 if (ret < 0)
100 return ret;
Felipe Balbi17354bf2009-02-17 13:18:11 +0200101
Janusz Krzysztofikcc587ec2011-01-20 14:44:29 -0800102 if (inverted > 1)
103 return -EINVAL;
104
105 gpio_data->inverted = inverted;
Felipe Balbi17354bf2009-02-17 13:18:11 +0200106
Thadeu Lima de Souza Cascardocc674c82009-08-26 14:29:32 -0700107 /* After inverting, we need to update the LED. */
Jan Kiszka71c17b02017-05-26 15:17:47 +0200108 gpio_trig_irq(0, led);
Thadeu Lima de Souza Cascardocc674c82009-08-26 14:29:32 -0700109
Felipe Balbi17354bf2009-02-17 13:18:11 +0200110 return n;
111}
112static DEVICE_ATTR(inverted, 0644, gpio_trig_inverted_show,
113 gpio_trig_inverted_store);
114
115static ssize_t gpio_trig_gpio_show(struct device *dev,
116 struct device_attribute *attr, char *buf)
117{
118 struct led_classdev *led = dev_get_drvdata(dev);
119 struct gpio_trig_data *gpio_data = led->trigger_data;
120
121 return sprintf(buf, "%u\n", gpio_data->gpio);
122}
123
124static ssize_t gpio_trig_gpio_store(struct device *dev,
125 struct device_attribute *attr, const char *buf, size_t n)
126{
127 struct led_classdev *led = dev_get_drvdata(dev);
128 struct gpio_trig_data *gpio_data = led->trigger_data;
129 unsigned gpio;
130 int ret;
131
132 ret = sscanf(buf, "%u", &gpio);
133 if (ret < 1) {
134 dev_err(dev, "couldn't read gpio number\n");
Felipe Balbi17354bf2009-02-17 13:18:11 +0200135 return -EINVAL;
136 }
137
Thadeu Lima de Souza Cascardo48cccd22009-08-26 14:29:31 -0700138 if (gpio_data->gpio == gpio)
139 return n;
140
Felipe Balbi17354bf2009-02-17 13:18:11 +0200141 if (!gpio) {
Thadeu Lima de Souza Cascardo48cccd22009-08-26 14:29:31 -0700142 if (gpio_data->gpio != 0)
143 free_irq(gpio_to_irq(gpio_data->gpio), led);
144 gpio_data->gpio = 0;
Felipe Balbi17354bf2009-02-17 13:18:11 +0200145 return n;
146 }
147
Jan Kiszka71c17b02017-05-26 15:17:47 +0200148 ret = request_threaded_irq(gpio_to_irq(gpio), NULL, gpio_trig_irq,
149 IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_RISING
Felipe Balbi17354bf2009-02-17 13:18:11 +0200150 | IRQF_TRIGGER_FALLING, "ledtrig-gpio", led);
Thadeu Lima de Souza Cascardo48cccd22009-08-26 14:29:31 -0700151 if (ret) {
Felipe Balbi17354bf2009-02-17 13:18:11 +0200152 dev_err(dev, "request_irq failed with error %d\n", ret);
Thadeu Lima de Souza Cascardo48cccd22009-08-26 14:29:31 -0700153 } else {
154 if (gpio_data->gpio != 0)
155 free_irq(gpio_to_irq(gpio_data->gpio), led);
156 gpio_data->gpio = gpio;
Jan Kiszka71afe3c2017-05-26 15:17:46 +0200157 /* After changing the GPIO, we need to update the LED. */
Jan Kiszka71c17b02017-05-26 15:17:47 +0200158 gpio_trig_irq(0, led);
Thadeu Lima de Souza Cascardo48cccd22009-08-26 14:29:31 -0700159 }
Felipe Balbi17354bf2009-02-17 13:18:11 +0200160
161 return ret ? ret : n;
162}
163static DEVICE_ATTR(gpio, 0644, gpio_trig_gpio_show, gpio_trig_gpio_store);
164
Uwe Kleine-König2282e1252018-07-02 22:05:21 +0200165static int gpio_trig_activate(struct led_classdev *led)
Felipe Balbi17354bf2009-02-17 13:18:11 +0200166{
167 struct gpio_trig_data *gpio_data;
168 int ret;
169
170 gpio_data = kzalloc(sizeof(*gpio_data), GFP_KERNEL);
171 if (!gpio_data)
Uwe Kleine-König2282e1252018-07-02 22:05:21 +0200172 return 0;
Felipe Balbi17354bf2009-02-17 13:18:11 +0200173
174 ret = device_create_file(led->dev, &dev_attr_gpio);
175 if (ret)
176 goto err_gpio;
177
178 ret = device_create_file(led->dev, &dev_attr_inverted);
179 if (ret)
180 goto err_inverted;
181
182 ret = device_create_file(led->dev, &dev_attr_desired_brightness);
183 if (ret)
184 goto err_brightness;
185
186 gpio_data->led = led;
187 led->trigger_data = gpio_data;
Shuah Khan03c091e2012-05-29 15:07:28 -0700188 led->activated = true;
Felipe Balbi17354bf2009-02-17 13:18:11 +0200189
Uwe Kleine-König2282e1252018-07-02 22:05:21 +0200190 return 0;
Felipe Balbi17354bf2009-02-17 13:18:11 +0200191
192err_brightness:
193 device_remove_file(led->dev, &dev_attr_inverted);
194
195err_inverted:
196 device_remove_file(led->dev, &dev_attr_gpio);
197
198err_gpio:
199 kfree(gpio_data);
Uwe Kleine-König2282e1252018-07-02 22:05:21 +0200200
201 return 0;
Felipe Balbi17354bf2009-02-17 13:18:11 +0200202}
203
204static void gpio_trig_deactivate(struct led_classdev *led)
205{
206 struct gpio_trig_data *gpio_data = led->trigger_data;
207
Shuah Khan03c091e2012-05-29 15:07:28 -0700208 if (led->activated) {
Felipe Balbi17354bf2009-02-17 13:18:11 +0200209 device_remove_file(led->dev, &dev_attr_gpio);
210 device_remove_file(led->dev, &dev_attr_inverted);
211 device_remove_file(led->dev, &dev_attr_desired_brightness);
Thadeu Lima de Souza Cascardo48cccd22009-08-26 14:29:31 -0700212 if (gpio_data->gpio != 0)
213 free_irq(gpio_to_irq(gpio_data->gpio), led);
Felipe Balbi17354bf2009-02-17 13:18:11 +0200214 kfree(gpio_data);
Shuah Khan03c091e2012-05-29 15:07:28 -0700215 led->activated = false;
Felipe Balbi17354bf2009-02-17 13:18:11 +0200216 }
217}
218
219static struct led_trigger gpio_led_trigger = {
220 .name = "gpio",
221 .activate = gpio_trig_activate,
222 .deactivate = gpio_trig_deactivate,
223};
224
225static int __init gpio_trig_init(void)
226{
227 return led_trigger_register(&gpio_led_trigger);
228}
229module_init(gpio_trig_init);
230
231static void __exit gpio_trig_exit(void)
232{
233 led_trigger_unregister(&gpio_led_trigger);
234}
235module_exit(gpio_trig_exit);
236
237MODULE_AUTHOR("Felipe Balbi <me@felipebalbi.com>");
238MODULE_DESCRIPTION("GPIO LED trigger");
Uwe Kleine-König033692e2018-07-02 22:05:20 +0200239MODULE_LICENSE("GPL v2");