diff options
author | David Brownell <dbrownell@users.sourceforge.net> | 2007-10-31 05:37:37 -0400 |
---|---|---|
committer | Richard Purdie <rpurdie@rpsys.net> | 2007-11-05 16:54:41 -0500 |
commit | 199fb21d520ac8c09de1f1288e667988815aa79a (patch) | |
tree | 2bed812af4e398e9795023bea7e86f02bdcf93d6 | |
parent | c11eef21aa119a41fa210b9693f2346997885bcf (diff) |
leds: bugfixes for leds-gpio
Three bugfixes to the leds-gpio driver, plus minor whitespace tweaks:
- Do the INIT_WORK() before registering each LED, so if its trigger
becomes immediately active it can schedule work without oopsing..
- Use normal registration, not platform_driver_probe(), so that
devices appearing "late" (hotplug type) can still be bound.
- Mark the driver remove code as "__devexit", preventing oopses
when the underlying device is removed.
These issues came up when using this driver with some GPIO expanders
living on serial busses, which act unlike "normal" platform devices:
they can appear and vanish along with the serial bus driver.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Richard Purdie <rpurdie@rpsys.net>
-rw-r--r-- | drivers/leds/leds-gpio.c | 20 |
1 files changed, 11 insertions, 9 deletions
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 47d90db280ce..99bc50059d35 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c | |||
@@ -60,7 +60,7 @@ static void gpio_led_set(struct led_classdev *led_cdev, | |||
60 | gpio_set_value(led_dat->gpio, level); | 60 | gpio_set_value(led_dat->gpio, level); |
61 | } | 61 | } |
62 | 62 | ||
63 | static int __init gpio_led_probe(struct platform_device *pdev) | 63 | static int gpio_led_probe(struct platform_device *pdev) |
64 | { | 64 | { |
65 | struct gpio_led_platform_data *pdata = pdev->dev.platform_data; | 65 | struct gpio_led_platform_data *pdata = pdev->dev.platform_data; |
66 | struct gpio_led *cur_led; | 66 | struct gpio_led *cur_led; |
@@ -93,13 +93,13 @@ static int __init gpio_led_probe(struct platform_device *pdev) | |||
93 | 93 | ||
94 | gpio_direction_output(led_dat->gpio, led_dat->active_low); | 94 | gpio_direction_output(led_dat->gpio, led_dat->active_low); |
95 | 95 | ||
96 | INIT_WORK(&led_dat->work, gpio_led_work); | ||
97 | |||
96 | ret = led_classdev_register(&pdev->dev, &led_dat->cdev); | 98 | ret = led_classdev_register(&pdev->dev, &led_dat->cdev); |
97 | if (ret < 0) { | 99 | if (ret < 0) { |
98 | gpio_free(led_dat->gpio); | 100 | gpio_free(led_dat->gpio); |
99 | goto err; | 101 | goto err; |
100 | } | 102 | } |
101 | |||
102 | INIT_WORK(&led_dat->work, gpio_led_work); | ||
103 | } | 103 | } |
104 | 104 | ||
105 | platform_set_drvdata(pdev, leds_data); | 105 | platform_set_drvdata(pdev, leds_data); |
@@ -110,17 +110,17 @@ err: | |||
110 | if (i > 0) { | 110 | if (i > 0) { |
111 | for (i = i - 1; i >= 0; i--) { | 111 | for (i = i - 1; i >= 0; i--) { |
112 | led_classdev_unregister(&leds_data[i].cdev); | 112 | led_classdev_unregister(&leds_data[i].cdev); |
113 | cancel_work_sync(&leds_data[i].work); | ||
113 | gpio_free(leds_data[i].gpio); | 114 | gpio_free(leds_data[i].gpio); |
114 | } | 115 | } |
115 | } | 116 | } |
116 | 117 | ||
117 | flush_scheduled_work(); | ||
118 | kfree(leds_data); | 118 | kfree(leds_data); |
119 | 119 | ||
120 | return ret; | 120 | return ret; |
121 | } | 121 | } |
122 | 122 | ||
123 | static int __exit gpio_led_remove(struct platform_device *pdev) | 123 | static int __devexit gpio_led_remove(struct platform_device *pdev) |
124 | { | 124 | { |
125 | int i; | 125 | int i; |
126 | struct gpio_led_platform_data *pdata = pdev->dev.platform_data; | 126 | struct gpio_led_platform_data *pdata = pdev->dev.platform_data; |
@@ -130,9 +130,10 @@ static int __exit gpio_led_remove(struct platform_device *pdev) | |||
130 | 130 | ||
131 | for (i = 0; i < pdata->num_leds; i++) { | 131 | for (i = 0; i < pdata->num_leds; i++) { |
132 | led_classdev_unregister(&leds_data[i].cdev); | 132 | led_classdev_unregister(&leds_data[i].cdev); |
133 | cancel_work_sync(&leds_data[i].work); | ||
133 | gpio_free(leds_data[i].gpio); | 134 | gpio_free(leds_data[i].gpio); |
134 | } | 135 | } |
135 | 136 | ||
136 | kfree(leds_data); | 137 | kfree(leds_data); |
137 | 138 | ||
138 | return 0; | 139 | return 0; |
@@ -144,7 +145,7 @@ static int gpio_led_suspend(struct platform_device *pdev, pm_message_t state) | |||
144 | struct gpio_led_platform_data *pdata = pdev->dev.platform_data; | 145 | struct gpio_led_platform_data *pdata = pdev->dev.platform_data; |
145 | struct gpio_led_data *leds_data; | 146 | struct gpio_led_data *leds_data; |
146 | int i; | 147 | int i; |
147 | 148 | ||
148 | leds_data = platform_get_drvdata(pdev); | 149 | leds_data = platform_get_drvdata(pdev); |
149 | 150 | ||
150 | for (i = 0; i < pdata->num_leds; i++) | 151 | for (i = 0; i < pdata->num_leds; i++) |
@@ -172,7 +173,8 @@ static int gpio_led_resume(struct platform_device *pdev) | |||
172 | #endif | 173 | #endif |
173 | 174 | ||
174 | static struct platform_driver gpio_led_driver = { | 175 | static struct platform_driver gpio_led_driver = { |
175 | .remove = __exit_p(gpio_led_remove), | 176 | .probe = gpio_led_probe, |
177 | .remove = __devexit_p(gpio_led_remove), | ||
176 | .suspend = gpio_led_suspend, | 178 | .suspend = gpio_led_suspend, |
177 | .resume = gpio_led_resume, | 179 | .resume = gpio_led_resume, |
178 | .driver = { | 180 | .driver = { |
@@ -183,7 +185,7 @@ static struct platform_driver gpio_led_driver = { | |||
183 | 185 | ||
184 | static int __init gpio_led_init(void) | 186 | static int __init gpio_led_init(void) |
185 | { | 187 | { |
186 | return platform_driver_probe(&gpio_led_driver, gpio_led_probe); | 188 | return platform_driver_register(&gpio_led_driver); |
187 | } | 189 | } |
188 | 190 | ||
189 | static void __exit gpio_led_exit(void) | 191 | static void __exit gpio_led_exit(void) |