aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/leds
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@arm.linux.org.uk>2014-04-06 18:20:03 -0400
committerBryan Wu <cooloney@gmail.com>2014-04-07 17:41:50 -0400
commit392369019eb96e914234ea21eda806cb51a1073e (patch)
tree7306c836d06f08d962251e1166875f855e3d2952 /drivers/leds
parent0016db26c093798632ea741402215a31af447704 (diff)
leds: leds-pwm: properly clean up after probe failure
When probing with DT, we add each LED one at a time. If we find a LED without a PWM device (because it is not available yet) we fail the initialisation, unregister previous LEDs, and then by way of managed resources, we free the structure. The problem with this is we may have a scheduled and active work_struct in this structure, and this results in a nasty kernel oops. We need to cancel this work_struct properly upon cleanup - and the cleanup we require is the same cleanup as we do when the LED platform device is removed. Rather than writing this same code three times, move it into a separate function and use it in all three places. Fixes: c971ff185f64 ("leds: leds-pwm: Defer led_pwm_set() if PWM can sleep") Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> Signed-off-by: Bryan Wu <cooloney@gmail.com>
Diffstat (limited to 'drivers/leds')
-rw-r--r--drivers/leds/leds-pwm.c23
1 files changed, 13 insertions, 10 deletions
diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c
index dd1787368fb1..7d0aaed1e23a 100644
--- a/drivers/leds/leds-pwm.c
+++ b/drivers/leds/leds-pwm.c
@@ -83,6 +83,15 @@ static inline size_t sizeof_pwm_leds_priv(int num_leds)
83 (sizeof(struct led_pwm_data) * num_leds); 83 (sizeof(struct led_pwm_data) * num_leds);
84} 84}
85 85
86static void led_pwm_cleanup(struct led_pwm_priv *priv)
87{
88 while (priv->num_leds--) {
89 led_classdev_unregister(&priv->leds[priv->num_leds].cdev);
90 if (priv->leds[priv->num_leds].can_sleep)
91 cancel_work_sync(&priv->leds[priv->num_leds].work);
92 }
93}
94
86static int led_pwm_create_of(struct platform_device *pdev, 95static int led_pwm_create_of(struct platform_device *pdev,
87 struct led_pwm_priv *priv) 96 struct led_pwm_priv *priv)
88{ 97{
@@ -130,8 +139,7 @@ static int led_pwm_create_of(struct platform_device *pdev,
130 139
131 return 0; 140 return 0;
132err: 141err:
133 while (priv->num_leds--) 142 led_pwm_cleanup(priv);
134 led_classdev_unregister(&priv->leds[priv->num_leds].cdev);
135 143
136 return ret; 144 return ret;
137} 145}
@@ -199,8 +207,8 @@ static int led_pwm_probe(struct platform_device *pdev)
199 return 0; 207 return 0;
200 208
201err: 209err:
202 while (i--) 210 priv->num_leds = i;
203 led_classdev_unregister(&priv->leds[i].cdev); 211 led_pwm_cleanup(priv);
204 212
205 return ret; 213 return ret;
206} 214}
@@ -208,13 +216,8 @@ err:
208static int led_pwm_remove(struct platform_device *pdev) 216static int led_pwm_remove(struct platform_device *pdev)
209{ 217{
210 struct led_pwm_priv *priv = platform_get_drvdata(pdev); 218 struct led_pwm_priv *priv = platform_get_drvdata(pdev);
211 int i;
212 219
213 for (i = 0; i < priv->num_leds; i++) { 220 led_pwm_cleanup(priv);
214 led_classdev_unregister(&priv->leds[i].cdev);
215 if (priv->leds[i].can_sleep)
216 cancel_work_sync(&priv->leds[i].work);
217 }
218 221
219 return 0; 222 return 0;
220} 223}