diff options
Diffstat (limited to 'drivers/leds/leds-gpio.c')
-rw-r--r-- | drivers/leds/leds-gpio.c | 150 |
1 files changed, 77 insertions, 73 deletions
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index b4518c8751c8..868e6fc17cba 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c | |||
@@ -12,25 +12,23 @@ | |||
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> |
18 | #include <linux/of.h> | ||
19 | #include <linux/of_gpio.h> | ||
20 | #include <linux/of_platform.h> | ||
21 | #include <linux/platform_device.h> | 19 | #include <linux/platform_device.h> |
20 | #include <linux/property.h> | ||
22 | #include <linux/slab.h> | 21 | #include <linux/slab.h> |
23 | #include <linux/workqueue.h> | 22 | #include <linux/workqueue.h> |
24 | 23 | ||
25 | struct gpio_led_data { | 24 | struct gpio_led_data { |
26 | struct led_classdev cdev; | 25 | struct led_classdev cdev; |
27 | unsigned gpio; | 26 | struct gpio_desc *gpiod; |
28 | struct work_struct work; | 27 | struct work_struct work; |
29 | u8 new_level; | 28 | u8 new_level; |
30 | u8 can_sleep; | 29 | u8 can_sleep; |
31 | u8 active_low; | ||
32 | u8 blinking; | 30 | u8 blinking; |
33 | int (*platform_gpio_blink_set)(unsigned gpio, int state, | 31 | int (*platform_gpio_blink_set)(struct gpio_desc *desc, int state, |
34 | unsigned long *delay_on, unsigned long *delay_off); | 32 | unsigned long *delay_on, unsigned long *delay_off); |
35 | }; | 33 | }; |
36 | 34 | ||
@@ -40,12 +38,11 @@ static void gpio_led_work(struct work_struct *work) | |||
40 | container_of(work, struct gpio_led_data, work); | 38 | container_of(work, struct gpio_led_data, work); |
41 | 39 | ||
42 | if (led_dat->blinking) { | 40 | if (led_dat->blinking) { |
43 | led_dat->platform_gpio_blink_set(led_dat->gpio, | 41 | led_dat->platform_gpio_blink_set(led_dat->gpiod, |
44 | led_dat->new_level, | 42 | led_dat->new_level, NULL, NULL); |
45 | NULL, NULL); | ||
46 | led_dat->blinking = 0; | 43 | led_dat->blinking = 0; |
47 | } else | 44 | } else |
48 | gpio_set_value_cansleep(led_dat->gpio, led_dat->new_level); | 45 | gpiod_set_value_cansleep(led_dat->gpiod, led_dat->new_level); |
49 | } | 46 | } |
50 | 47 | ||
51 | static void gpio_led_set(struct led_classdev *led_cdev, | 48 | static void gpio_led_set(struct led_classdev *led_cdev, |
@@ -60,9 +57,6 @@ static void gpio_led_set(struct led_classdev *led_cdev, | |||
60 | else | 57 | else |
61 | level = 1; | 58 | level = 1; |
62 | 59 | ||
63 | if (led_dat->active_low) | ||
64 | level = !level; | ||
65 | |||
66 | /* Setting GPIOs with I2C/etc requires a task context, and we don't | 60 | /* 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 | 61 | * seem to have a reliable way to know if we're already in one; so |
68 | * let's just assume the worst. | 62 | * let's just assume the worst. |
@@ -72,11 +66,11 @@ static void gpio_led_set(struct led_classdev *led_cdev, | |||
72 | schedule_work(&led_dat->work); | 66 | schedule_work(&led_dat->work); |
73 | } else { | 67 | } else { |
74 | if (led_dat->blinking) { | 68 | if (led_dat->blinking) { |
75 | led_dat->platform_gpio_blink_set(led_dat->gpio, level, | 69 | led_dat->platform_gpio_blink_set(led_dat->gpiod, level, |
76 | NULL, NULL); | 70 | NULL, NULL); |
77 | led_dat->blinking = 0; | 71 | led_dat->blinking = 0; |
78 | } else | 72 | } else |
79 | gpio_set_value(led_dat->gpio, level); | 73 | gpiod_set_value(led_dat->gpiod, level); |
80 | } | 74 | } |
81 | } | 75 | } |
82 | 76 | ||
@@ -87,34 +81,49 @@ static int gpio_blink_set(struct led_classdev *led_cdev, | |||
87 | container_of(led_cdev, struct gpio_led_data, cdev); | 81 | container_of(led_cdev, struct gpio_led_data, cdev); |
88 | 82 | ||
89 | led_dat->blinking = 1; | 83 | led_dat->blinking = 1; |
90 | return led_dat->platform_gpio_blink_set(led_dat->gpio, GPIO_LED_BLINK, | 84 | return led_dat->platform_gpio_blink_set(led_dat->gpiod, GPIO_LED_BLINK, |
91 | delay_on, delay_off); | 85 | delay_on, delay_off); |
92 | } | 86 | } |
93 | 87 | ||
94 | static int create_gpio_led(const struct gpio_led *template, | 88 | static int create_gpio_led(const struct gpio_led *template, |
95 | struct gpio_led_data *led_dat, struct device *parent, | 89 | struct gpio_led_data *led_dat, struct device *parent, |
96 | int (*blink_set)(unsigned, int, unsigned long *, unsigned long *)) | 90 | int (*blink_set)(struct gpio_desc *, int, unsigned long *, |
91 | unsigned long *)) | ||
97 | { | 92 | { |
98 | int ret, state; | 93 | int ret, state; |
99 | 94 | ||
100 | led_dat->gpio = -1; | 95 | led_dat->gpiod = template->gpiod; |
96 | if (!led_dat->gpiod) { | ||
97 | /* | ||
98 | * This is the legacy code path for platform code that | ||
99 | * still uses GPIO numbers. Ultimately we would like to get | ||
100 | * rid of this block completely. | ||
101 | */ | ||
102 | unsigned long flags = 0; | ||
103 | |||
104 | /* skip leds that aren't available */ | ||
105 | if (!gpio_is_valid(template->gpio)) { | ||
106 | dev_info(parent, "Skipping unavailable LED gpio %d (%s)\n", | ||
107 | template->gpio, template->name); | ||
108 | return 0; | ||
109 | } | ||
101 | 110 | ||
102 | /* skip leds that aren't available */ | 111 | if (template->active_low) |
103 | if (!gpio_is_valid(template->gpio)) { | 112 | flags |= GPIOF_ACTIVE_LOW; |
104 | dev_info(parent, "Skipping unavailable LED gpio %d (%s)\n", | ||
105 | template->gpio, template->name); | ||
106 | return 0; | ||
107 | } | ||
108 | 113 | ||
109 | ret = devm_gpio_request(parent, template->gpio, template->name); | 114 | ret = devm_gpio_request_one(parent, template->gpio, flags, |
110 | if (ret < 0) | 115 | template->name); |
111 | return ret; | 116 | if (ret < 0) |
117 | return ret; | ||
118 | |||
119 | led_dat->gpiod = gpio_to_desc(template->gpio); | ||
120 | if (IS_ERR(led_dat->gpiod)) | ||
121 | return PTR_ERR(led_dat->gpiod); | ||
122 | } | ||
112 | 123 | ||
113 | led_dat->cdev.name = template->name; | 124 | led_dat->cdev.name = template->name; |
114 | led_dat->cdev.default_trigger = template->default_trigger; | 125 | led_dat->cdev.default_trigger = template->default_trigger; |
115 | led_dat->gpio = template->gpio; | 126 | led_dat->can_sleep = gpiod_cansleep(led_dat->gpiod); |
116 | led_dat->can_sleep = gpio_cansleep(template->gpio); | ||
117 | led_dat->active_low = template->active_low; | ||
118 | led_dat->blinking = 0; | 127 | led_dat->blinking = 0; |
119 | if (blink_set) { | 128 | if (blink_set) { |
120 | led_dat->platform_gpio_blink_set = blink_set; | 129 | led_dat->platform_gpio_blink_set = blink_set; |
@@ -122,30 +131,24 @@ static int create_gpio_led(const struct gpio_led *template, | |||
122 | } | 131 | } |
123 | led_dat->cdev.brightness_set = gpio_led_set; | 132 | led_dat->cdev.brightness_set = gpio_led_set; |
124 | if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP) | 133 | if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP) |
125 | state = !!gpio_get_value_cansleep(led_dat->gpio) ^ led_dat->active_low; | 134 | state = !!gpiod_get_value_cansleep(led_dat->gpiod); |
126 | else | 135 | else |
127 | state = (template->default_state == LEDS_GPIO_DEFSTATE_ON); | 136 | state = (template->default_state == LEDS_GPIO_DEFSTATE_ON); |
128 | led_dat->cdev.brightness = state ? LED_FULL : LED_OFF; | 137 | led_dat->cdev.brightness = state ? LED_FULL : LED_OFF; |
129 | if (!template->retain_state_suspended) | 138 | if (!template->retain_state_suspended) |
130 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; | 139 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; |
131 | 140 | ||
132 | ret = gpio_direction_output(led_dat->gpio, led_dat->active_low ^ state); | 141 | ret = gpiod_direction_output(led_dat->gpiod, state); |
133 | if (ret < 0) | 142 | if (ret < 0) |
134 | return ret; | 143 | return ret; |
135 | 144 | ||
136 | INIT_WORK(&led_dat->work, gpio_led_work); | 145 | INIT_WORK(&led_dat->work, gpio_led_work); |
137 | 146 | ||
138 | ret = led_classdev_register(parent, &led_dat->cdev); | 147 | return led_classdev_register(parent, &led_dat->cdev); |
139 | if (ret < 0) | ||
140 | return ret; | ||
141 | |||
142 | return 0; | ||
143 | } | 148 | } |
144 | 149 | ||
145 | static void delete_gpio_led(struct gpio_led_data *led) | 150 | static void delete_gpio_led(struct gpio_led_data *led) |
146 | { | 151 | { |
147 | if (!gpio_is_valid(led->gpio)) | ||
148 | return; | ||
149 | led_classdev_unregister(&led->cdev); | 152 | led_classdev_unregister(&led->cdev); |
150 | cancel_work_sync(&led->work); | 153 | cancel_work_sync(&led->work); |
151 | } | 154 | } |
@@ -161,40 +164,47 @@ static inline int sizeof_gpio_leds_priv(int num_leds) | |||
161 | (sizeof(struct gpio_led_data) * num_leds); | 164 | (sizeof(struct gpio_led_data) * num_leds); |
162 | } | 165 | } |
163 | 166 | ||
164 | /* Code to create from OpenFirmware platform devices */ | 167 | static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev) |
165 | #ifdef CONFIG_OF_GPIO | ||
166 | static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev) | ||
167 | { | 168 | { |
168 | struct device_node *np = pdev->dev.of_node, *child; | 169 | struct device *dev = &pdev->dev; |
170 | struct fwnode_handle *child; | ||
169 | struct gpio_leds_priv *priv; | 171 | struct gpio_leds_priv *priv; |
170 | int count, ret; | 172 | int count, ret; |
173 | struct device_node *np; | ||
171 | 174 | ||
172 | /* count LEDs in this device, so we know how much to allocate */ | 175 | count = device_get_child_node_count(dev); |
173 | count = of_get_available_child_count(np); | ||
174 | if (!count) | 176 | if (!count) |
175 | return ERR_PTR(-ENODEV); | 177 | return ERR_PTR(-ENODEV); |
176 | 178 | ||
177 | for_each_available_child_of_node(np, child) | 179 | priv = devm_kzalloc(dev, sizeof_gpio_leds_priv(count), GFP_KERNEL); |
178 | if (of_get_gpio(child, 0) == -EPROBE_DEFER) | ||
179 | return ERR_PTR(-EPROBE_DEFER); | ||
180 | |||
181 | priv = devm_kzalloc(&pdev->dev, sizeof_gpio_leds_priv(count), | ||
182 | GFP_KERNEL); | ||
183 | if (!priv) | 180 | if (!priv) |
184 | return ERR_PTR(-ENOMEM); | 181 | return ERR_PTR(-ENOMEM); |
185 | 182 | ||
186 | for_each_available_child_of_node(np, child) { | 183 | device_for_each_child_node(dev, child) { |
187 | struct gpio_led led = {}; | 184 | struct gpio_led led = {}; |
188 | enum of_gpio_flags flags; | 185 | const char *state = NULL; |
189 | const char *state; | 186 | |
190 | 187 | led.gpiod = devm_get_gpiod_from_child(dev, child); | |
191 | led.gpio = of_get_gpio_flags(child, 0, &flags); | 188 | if (IS_ERR(led.gpiod)) { |
192 | led.active_low = flags & OF_GPIO_ACTIVE_LOW; | 189 | fwnode_handle_put(child); |
193 | led.name = of_get_property(child, "label", NULL) ? : child->name; | 190 | goto err; |
194 | led.default_trigger = | 191 | } |
195 | of_get_property(child, "linux,default-trigger", NULL); | 192 | |
196 | state = of_get_property(child, "default-state", NULL); | 193 | np = of_node(child); |
197 | if (state) { | 194 | |
195 | if (fwnode_property_present(child, "label")) { | ||
196 | fwnode_property_read_string(child, "label", &led.name); | ||
197 | } else { | ||
198 | if (IS_ENABLED(CONFIG_OF) && !led.name && np) | ||
199 | led.name = np->name; | ||
200 | if (!led.name) | ||
201 | return ERR_PTR(-EINVAL); | ||
202 | } | ||
203 | fwnode_property_read_string(child, "linux,default-trigger", | ||
204 | &led.default_trigger); | ||
205 | |||
206 | if (!fwnode_property_read_string(child, "linux,default_state", | ||
207 | &state)) { | ||
198 | if (!strcmp(state, "keep")) | 208 | if (!strcmp(state, "keep")) |
199 | led.default_state = LEDS_GPIO_DEFSTATE_KEEP; | 209 | led.default_state = LEDS_GPIO_DEFSTATE_KEEP; |
200 | else if (!strcmp(state, "on")) | 210 | else if (!strcmp(state, "on")) |
@@ -203,13 +213,13 @@ static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev) | |||
203 | led.default_state = LEDS_GPIO_DEFSTATE_OFF; | 213 | led.default_state = LEDS_GPIO_DEFSTATE_OFF; |
204 | } | 214 | } |
205 | 215 | ||
206 | if (of_get_property(child, "retain-state-suspended", NULL)) | 216 | if (fwnode_property_present(child, "retain-state-suspended")) |
207 | led.retain_state_suspended = 1; | 217 | led.retain_state_suspended = 1; |
208 | 218 | ||
209 | ret = create_gpio_led(&led, &priv->leds[priv->num_leds++], | 219 | ret = create_gpio_led(&led, &priv->leds[priv->num_leds++], |
210 | &pdev->dev, NULL); | 220 | dev, NULL); |
211 | if (ret < 0) { | 221 | if (ret < 0) { |
212 | of_node_put(child); | 222 | fwnode_handle_put(child); |
213 | goto err; | 223 | goto err; |
214 | } | 224 | } |
215 | } | 225 | } |
@@ -228,12 +238,6 @@ static const struct of_device_id of_gpio_leds_match[] = { | |||
228 | }; | 238 | }; |
229 | 239 | ||
230 | MODULE_DEVICE_TABLE(of, of_gpio_leds_match); | 240 | MODULE_DEVICE_TABLE(of, of_gpio_leds_match); |
231 | #else /* CONFIG_OF_GPIO */ | ||
232 | static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev) | ||
233 | { | ||
234 | return ERR_PTR(-ENODEV); | ||
235 | } | ||
236 | #endif /* CONFIG_OF_GPIO */ | ||
237 | 241 | ||
238 | static int gpio_led_probe(struct platform_device *pdev) | 242 | static int gpio_led_probe(struct platform_device *pdev) |
239 | { | 243 | { |
@@ -261,7 +265,7 @@ static int gpio_led_probe(struct platform_device *pdev) | |||
261 | } | 265 | } |
262 | } | 266 | } |
263 | } else { | 267 | } else { |
264 | priv = gpio_leds_create_of(pdev); | 268 | priv = gpio_leds_create(pdev); |
265 | if (IS_ERR(priv)) | 269 | if (IS_ERR(priv)) |
266 | return PTR_ERR(priv); | 270 | return PTR_ERR(priv); |
267 | } | 271 | } |
@@ -288,7 +292,7 @@ static struct platform_driver gpio_led_driver = { | |||
288 | .driver = { | 292 | .driver = { |
289 | .name = "leds-gpio", | 293 | .name = "leds-gpio", |
290 | .owner = THIS_MODULE, | 294 | .owner = THIS_MODULE, |
291 | .of_match_table = of_match_ptr(of_gpio_leds_match), | 295 | .of_match_table = of_gpio_leds_match, |
292 | }, | 296 | }, |
293 | }; | 297 | }; |
294 | 298 | ||