Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
17354bfe FB |
2 | /* |
3 | * ledtrig-gio.c - LED Trigger Based on GPIO events | |
4 | * | |
5 | * Copyright 2009 Felipe Balbi <me@felipebalbi.com> | |
4a11dbf0 | 6 | * Copyright 2023 Linus Walleij <linus.walleij@linaro.org> |
17354bfe FB |
7 | */ |
8 | ||
9 | #include <linux/module.h> | |
10 | #include <linux/kernel.h> | |
11 | #include <linux/init.h> | |
4a11dbf0 | 12 | #include <linux/gpio/consumer.h> |
17354bfe | 13 | #include <linux/interrupt.h> |
17354bfe | 14 | #include <linux/leds.h> |
5a0e3ad6 | 15 | #include <linux/slab.h> |
f07fb521 | 16 | #include "../leds.h" |
17354bfe FB |
17 | |
18 | struct gpio_trig_data { | |
19 | struct led_classdev *led; | |
17354bfe | 20 | unsigned desired_brightness; /* desired brightness when led is on */ |
4a11dbf0 | 21 | struct gpio_desc *gpiod; /* gpio that triggers the led */ |
17354bfe FB |
22 | }; |
23 | ||
24 | static irqreturn_t gpio_trig_irq(int irq, void *_led) | |
25 | { | |
26 | struct led_classdev *led = _led; | |
9bfd7d9e | 27 | struct gpio_trig_data *gpio_data = led_get_trigger_data(led); |
17354bfe FB |
28 | int tmp; |
29 | ||
4a11dbf0 | 30 | tmp = gpiod_get_value_cansleep(gpio_data->gpiod); |
74cbe202 TLSC |
31 | if (tmp) { |
32 | if (gpio_data->desired_brightness) | |
81fe8e5b | 33 | led_set_brightness_nosleep(gpio_data->led, |
74cbe202 TLSC |
34 | gpio_data->desired_brightness); |
35 | else | |
81fe8e5b | 36 | led_set_brightness_nosleep(gpio_data->led, LED_FULL); |
74cbe202 | 37 | } else { |
81fe8e5b | 38 | led_set_brightness_nosleep(gpio_data->led, LED_OFF); |
74cbe202 | 39 | } |
71c17b06 JK |
40 | |
41 | return IRQ_HANDLED; | |
17354bfe FB |
42 | } |
43 | ||
804073f5 | 44 | static ssize_t desired_brightness_show(struct device *dev, |
17354bfe FB |
45 | struct device_attribute *attr, char *buf) |
46 | { | |
9bfd7d9e | 47 | struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev); |
17354bfe | 48 | |
7b9c5500 | 49 | return sysfs_emit(buf, "%u\n", gpio_data->desired_brightness); |
17354bfe FB |
50 | } |
51 | ||
804073f5 | 52 | static ssize_t desired_brightness_store(struct device *dev, |
17354bfe FB |
53 | struct device_attribute *attr, const char *buf, size_t n) |
54 | { | |
9bfd7d9e | 55 | struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev); |
7d6766f5 | 56 | u8 desired_brightness; |
17354bfe FB |
57 | int ret; |
58 | ||
7d6766f5 AS |
59 | ret = kstrtou8(buf, 10, &desired_brightness); |
60 | if (ret) | |
61 | return ret; | |
17354bfe FB |
62 | |
63 | gpio_data->desired_brightness = desired_brightness; | |
64 | ||
65 | return n; | |
66 | } | |
804073f5 | 67 | static DEVICE_ATTR_RW(desired_brightness); |
17354bfe | 68 | |
9bfd7d9e UKK |
69 | static struct attribute *gpio_trig_attrs[] = { |
70 | &dev_attr_desired_brightness.attr, | |
9bfd7d9e UKK |
71 | NULL |
72 | }; | |
73 | ATTRIBUTE_GROUPS(gpio_trig); | |
74 | ||
2282e125 | 75 | static int gpio_trig_activate(struct led_classdev *led) |
17354bfe FB |
76 | { |
77 | struct gpio_trig_data *gpio_data; | |
4a11dbf0 LW |
78 | struct device *dev = led->dev; |
79 | int ret; | |
17354bfe FB |
80 | |
81 | gpio_data = kzalloc(sizeof(*gpio_data), GFP_KERNEL); | |
82 | if (!gpio_data) | |
9bfd7d9e | 83 | return -ENOMEM; |
17354bfe | 84 | |
4a11dbf0 LW |
85 | /* |
86 | * The generic property "trigger-sources" is followed, | |
87 | * and we hope that this is a GPIO. | |
88 | */ | |
9bbd6b72 | 89 | gpio_data->gpiod = gpiod_get_optional(dev, "trigger-sources", GPIOD_IN); |
4a11dbf0 LW |
90 | if (IS_ERR(gpio_data->gpiod)) { |
91 | ret = PTR_ERR(gpio_data->gpiod); | |
92 | kfree(gpio_data); | |
93 | return ret; | |
94 | } | |
95 | if (!gpio_data->gpiod) { | |
96 | dev_err(dev, "no valid GPIO for the trigger\n"); | |
97 | kfree(gpio_data); | |
98 | return -EINVAL; | |
99 | } | |
9bbd6b72 AS |
100 | |
101 | gpiod_set_consumer_name(gpio_data->gpiod, "led-trigger"); | |
4e421040 | 102 | |
4a11dbf0 | 103 | gpio_data->led = led; |
9bfd7d9e | 104 | led_set_trigger_data(led, gpio_data); |
2282e125 | 105 | |
4a11dbf0 LW |
106 | ret = request_threaded_irq(gpiod_to_irq(gpio_data->gpiod), NULL, gpio_trig_irq, |
107 | IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_RISING | |
108 | | IRQF_TRIGGER_FALLING, "ledtrig-gpio", led); | |
109 | if (ret) { | |
110 | dev_err(dev, "request_irq failed with error %d\n", ret); | |
111 | gpiod_put(gpio_data->gpiod); | |
112 | kfree(gpio_data); | |
113 | return ret; | |
114 | } | |
115 | ||
116 | /* Finally update the LED to initial status */ | |
117 | gpio_trig_irq(0, led); | |
118 | ||
2282e125 | 119 | return 0; |
17354bfe FB |
120 | } |
121 | ||
122 | static void gpio_trig_deactivate(struct led_classdev *led) | |
123 | { | |
9bfd7d9e | 124 | struct gpio_trig_data *gpio_data = led_get_trigger_data(led); |
17354bfe | 125 | |
4a11dbf0 LW |
126 | free_irq(gpiod_to_irq(gpio_data->gpiod), led); |
127 | gpiod_put(gpio_data->gpiod); | |
9bfd7d9e | 128 | kfree(gpio_data); |
17354bfe FB |
129 | } |
130 | ||
131 | static struct led_trigger gpio_led_trigger = { | |
132 | .name = "gpio", | |
133 | .activate = gpio_trig_activate, | |
134 | .deactivate = gpio_trig_deactivate, | |
9bfd7d9e | 135 | .groups = gpio_trig_groups, |
17354bfe | 136 | }; |
9bfd7d9e | 137 | module_led_trigger(gpio_led_trigger); |
17354bfe FB |
138 | |
139 | MODULE_AUTHOR("Felipe Balbi <me@felipebalbi.com>"); | |
140 | MODULE_DESCRIPTION("GPIO LED trigger"); | |
033692eb | 141 | MODULE_LICENSE("GPL v2"); |