diff options
-rw-r--r-- | drivers/leds/leds-gpio.c | 80 | ||||
-rw-r--r-- | include/linux/leds.h | 1 |
2 files changed, 46 insertions, 35 deletions
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index b4518c8751c8..1ff95ce9487a 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c | |||
@@ -12,6 +12,7 @@ | |||
12 | */ | 12 | */ |
13 | #include <linux/err.h> | 13 | #include <linux/err.h> |
14 | #include <linux/gpio.h> | 14 | #include <linux/gpio.h> |
15 | #include <linux/gpio/consumer.h> | ||
15 | #include <linux/kernel.h> | 16 | #include <linux/kernel.h> |
16 | #include <linux/leds.h> | 17 | #include <linux/leds.h> |
17 | #include <linux/module.h> | 18 | #include <linux/module.h> |
@@ -24,11 +25,10 @@ | |||
24 | 25 | ||
25 | struct gpio_led_data { | 26 | struct gpio_led_data { |
26 | struct led_classdev cdev; | 27 | struct led_classdev cdev; |
27 | unsigned gpio; | 28 | struct gpio_desc *gpiod; |
28 | struct work_struct work; | 29 | struct work_struct work; |
29 | u8 new_level; | 30 | u8 new_level; |
30 | u8 can_sleep; | 31 | u8 can_sleep; |
31 | u8 active_low; | ||
32 | u8 blinking; | 32 | u8 blinking; |
33 | int (*platform_gpio_blink_set)(unsigned gpio, int state, | 33 | int (*platform_gpio_blink_set)(unsigned gpio, int state, |
34 | unsigned long *delay_on, unsigned long *delay_off); | 34 | unsigned long *delay_on, unsigned long *delay_off); |
@@ -40,12 +40,16 @@ static void gpio_led_work(struct work_struct *work) | |||
40 | container_of(work, struct gpio_led_data, work); | 40 | container_of(work, struct gpio_led_data, work); |
41 | 41 | ||
42 | if (led_dat->blinking) { | 42 | if (led_dat->blinking) { |
43 | led_dat->platform_gpio_blink_set(led_dat->gpio, | 43 | int gpio = desc_to_gpio(led_dat->gpiod); |
44 | led_dat->new_level, | 44 | int level = led_dat->new_level; |
45 | NULL, NULL); | 45 | |
46 | if (gpiod_is_active_low(led_dat->gpiod)) | ||
47 | level = !level; | ||
48 | |||
49 | led_dat->platform_gpio_blink_set(gpio, level, NULL, NULL); | ||
46 | led_dat->blinking = 0; | 50 | led_dat->blinking = 0; |
47 | } else | 51 | } else |
48 | gpio_set_value_cansleep(led_dat->gpio, led_dat->new_level); | 52 | gpiod_set_value_cansleep(led_dat->gpiod, led_dat->new_level); |
49 | } | 53 | } |
50 | 54 | ||
51 | static void gpio_led_set(struct led_classdev *led_cdev, | 55 | static void gpio_led_set(struct led_classdev *led_cdev, |
@@ -60,9 +64,6 @@ static void gpio_led_set(struct led_classdev *led_cdev, | |||
60 | else | 64 | else |
61 | level = 1; | 65 | level = 1; |
62 | 66 | ||
63 | if (led_dat->active_low) | ||
64 | level = !level; | ||
65 | |||
66 | /* Setting GPIOs with I2C/etc requires a task context, and we don't | 67 | /* Setting GPIOs with I2C/etc requires a task context, and we don't |
67 | * seem to have a reliable way to know if we're already in one; so | 68 | * seem to have a reliable way to know if we're already in one; so |
68 | * let's just assume the worst. | 69 | * let's just assume the worst. |
@@ -72,11 +73,16 @@ static void gpio_led_set(struct led_classdev *led_cdev, | |||
72 | schedule_work(&led_dat->work); | 73 | schedule_work(&led_dat->work); |
73 | } else { | 74 | } else { |
74 | if (led_dat->blinking) { | 75 | if (led_dat->blinking) { |
75 | led_dat->platform_gpio_blink_set(led_dat->gpio, level, | 76 | int gpio = desc_to_gpio(led_dat->gpiod); |
76 | NULL, NULL); | 77 | |
78 | if (gpiod_is_active_low(led_dat->gpiod)) | ||
79 | level = !level; | ||
80 | |||
81 | led_dat->platform_gpio_blink_set(gpio, level, NULL, | ||
82 | NULL); | ||
77 | led_dat->blinking = 0; | 83 | led_dat->blinking = 0; |
78 | } else | 84 | } else |
79 | gpio_set_value(led_dat->gpio, level); | 85 | gpiod_set_value(led_dat->gpiod, level); |
80 | } | 86 | } |
81 | } | 87 | } |
82 | 88 | ||
@@ -85,9 +91,10 @@ static int gpio_blink_set(struct led_classdev *led_cdev, | |||
85 | { | 91 | { |
86 | struct gpio_led_data *led_dat = | 92 | struct gpio_led_data *led_dat = |
87 | container_of(led_cdev, struct gpio_led_data, cdev); | 93 | container_of(led_cdev, struct gpio_led_data, cdev); |
94 | int gpio = desc_to_gpio(led_dat->gpiod); | ||
88 | 95 | ||
89 | led_dat->blinking = 1; | 96 | led_dat->blinking = 1; |
90 | return led_dat->platform_gpio_blink_set(led_dat->gpio, GPIO_LED_BLINK, | 97 | return led_dat->platform_gpio_blink_set(gpio, GPIO_LED_BLINK, |
91 | delay_on, delay_off); | 98 | delay_on, delay_off); |
92 | } | 99 | } |
93 | 100 | ||
@@ -97,24 +104,33 @@ static int create_gpio_led(const struct gpio_led *template, | |||
97 | { | 104 | { |
98 | int ret, state; | 105 | int ret, state; |
99 | 106 | ||
100 | led_dat->gpio = -1; | 107 | if (!template->gpiod) { |
108 | unsigned long flags = 0; | ||
101 | 109 | ||
102 | /* skip leds that aren't available */ | 110 | /* skip leds that aren't available */ |
103 | if (!gpio_is_valid(template->gpio)) { | 111 | if (!gpio_is_valid(template->gpio)) { |
104 | dev_info(parent, "Skipping unavailable LED gpio %d (%s)\n", | 112 | dev_info(parent, "Skipping unavailable LED gpio %d (%s)\n", |
105 | template->gpio, template->name); | 113 | template->gpio, template->name); |
106 | return 0; | 114 | return 0; |
107 | } | 115 | } |
108 | 116 | ||
109 | ret = devm_gpio_request(parent, template->gpio, template->name); | 117 | if (template->active_low) |
110 | if (ret < 0) | 118 | flags |= GPIOF_ACTIVE_LOW; |
111 | return ret; | 119 | |
120 | ret = devm_gpio_request_one(parent, template->gpio, flags, | ||
121 | template->name); | ||
122 | if (ret < 0) | ||
123 | return ret; | ||
124 | |||
125 | led_dat->gpiod = gpio_to_desc(template->gpio); | ||
126 | if (IS_ERR(led_dat->gpiod)) | ||
127 | return PTR_ERR(led_dat->gpiod); | ||
128 | } | ||
112 | 129 | ||
113 | led_dat->cdev.name = template->name; | 130 | led_dat->cdev.name = template->name; |
114 | led_dat->cdev.default_trigger = template->default_trigger; | 131 | led_dat->cdev.default_trigger = template->default_trigger; |
115 | led_dat->gpio = template->gpio; | 132 | led_dat->gpiod = template->gpiod; |
116 | led_dat->can_sleep = gpio_cansleep(template->gpio); | 133 | led_dat->can_sleep = gpiod_cansleep(template->gpiod); |
117 | led_dat->active_low = template->active_low; | ||
118 | led_dat->blinking = 0; | 134 | led_dat->blinking = 0; |
119 | if (blink_set) { | 135 | if (blink_set) { |
120 | led_dat->platform_gpio_blink_set = blink_set; | 136 | led_dat->platform_gpio_blink_set = blink_set; |
@@ -122,30 +138,24 @@ static int create_gpio_led(const struct gpio_led *template, | |||
122 | } | 138 | } |
123 | led_dat->cdev.brightness_set = gpio_led_set; | 139 | led_dat->cdev.brightness_set = gpio_led_set; |
124 | if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP) | 140 | if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP) |
125 | state = !!gpio_get_value_cansleep(led_dat->gpio) ^ led_dat->active_low; | 141 | state = !!gpiod_get_value_cansleep(led_dat->gpiod); |
126 | else | 142 | else |
127 | state = (template->default_state == LEDS_GPIO_DEFSTATE_ON); | 143 | state = (template->default_state == LEDS_GPIO_DEFSTATE_ON); |
128 | led_dat->cdev.brightness = state ? LED_FULL : LED_OFF; | 144 | led_dat->cdev.brightness = state ? LED_FULL : LED_OFF; |
129 | if (!template->retain_state_suspended) | 145 | if (!template->retain_state_suspended) |
130 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; | 146 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; |
131 | 147 | ||
132 | ret = gpio_direction_output(led_dat->gpio, led_dat->active_low ^ state); | 148 | ret = gpiod_direction_output(led_dat->gpiod, state); |
133 | if (ret < 0) | 149 | if (ret < 0) |
134 | return ret; | 150 | return ret; |
135 | 151 | ||
136 | INIT_WORK(&led_dat->work, gpio_led_work); | 152 | INIT_WORK(&led_dat->work, gpio_led_work); |
137 | 153 | ||
138 | ret = led_classdev_register(parent, &led_dat->cdev); | 154 | return led_classdev_register(parent, &led_dat->cdev); |
139 | if (ret < 0) | ||
140 | return ret; | ||
141 | |||
142 | return 0; | ||
143 | } | 155 | } |
144 | 156 | ||
145 | static void delete_gpio_led(struct gpio_led_data *led) | 157 | static void delete_gpio_led(struct gpio_led_data *led) |
146 | { | 158 | { |
147 | if (!gpio_is_valid(led->gpio)) | ||
148 | return; | ||
149 | led_classdev_unregister(&led->cdev); | 159 | led_classdev_unregister(&led->cdev); |
150 | cancel_work_sync(&led->work); | 160 | cancel_work_sync(&led->work); |
151 | } | 161 | } |
diff --git a/include/linux/leds.h b/include/linux/leds.h index a57611d0c94e..f3af5c4d9084 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h | |||
@@ -261,6 +261,7 @@ struct gpio_led { | |||
261 | unsigned retain_state_suspended : 1; | 261 | unsigned retain_state_suspended : 1; |
262 | unsigned default_state : 2; | 262 | unsigned default_state : 2; |
263 | /* default_state should be one of LEDS_GPIO_DEFSTATE_(ON|OFF|KEEP) */ | 263 | /* default_state should be one of LEDS_GPIO_DEFSTATE_(ON|OFF|KEEP) */ |
264 | struct gpio_desc *gpiod; | ||
264 | }; | 265 | }; |
265 | #define LEDS_GPIO_DEFSTATE_OFF 0 | 266 | #define LEDS_GPIO_DEFSTATE_OFF 0 |
266 | #define LEDS_GPIO_DEFSTATE_ON 1 | 267 | #define LEDS_GPIO_DEFSTATE_ON 1 |