diff options
author | Riku Voipio <riku.voipio@iki.fi> | 2008-12-03 03:21:36 -0500 |
---|---|---|
committer | Richard Purdie <rpurdie@linux.intel.com> | 2009-01-08 07:38:58 -0500 |
commit | 934cd3f979a1daacbd403398f2c7a8f6720c33aa (patch) | |
tree | 1cfb5b9c4d4c380c9b88d045b6aa233ca5211755 /drivers/leds | |
parent | f785d022add53ec4d9625495b335bed40bd6c079 (diff) |
leds: leds-pcs9532 - Move i2c work to a workqueque
Apparently these might be called under atomic context,
and i2c operations may sleep. BUG found by
Ross Burton <ross@burtonini.com>
Signed-off-by: Riku Voipio <riku.voipio@iki.fi>
Signed-off-by: Richard Purdie <rpurdie@linux.intel.com>
Diffstat (limited to 'drivers/leds')
-rw-r--r-- | drivers/leds/leds-pca9532.c | 51 |
1 files changed, 43 insertions, 8 deletions
diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c index 62b60a038e2c..76ec7498e2d5 100644 --- a/drivers/leds/leds-pca9532.c +++ b/drivers/leds/leds-pca9532.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/leds.h> | 16 | #include <linux/leds.h> |
17 | #include <linux/input.h> | 17 | #include <linux/input.h> |
18 | #include <linux/mutex.h> | 18 | #include <linux/mutex.h> |
19 | #include <linux/workqueue.h> | ||
19 | #include <linux/leds-pca9532.h> | 20 | #include <linux/leds-pca9532.h> |
20 | 21 | ||
21 | static const unsigned short normal_i2c[] = { /*0x60,*/ I2C_CLIENT_END}; | 22 | static const unsigned short normal_i2c[] = { /*0x60,*/ I2C_CLIENT_END}; |
@@ -34,6 +35,7 @@ struct pca9532_data { | |||
34 | struct pca9532_led leds[16]; | 35 | struct pca9532_led leds[16]; |
35 | struct mutex update_lock; | 36 | struct mutex update_lock; |
36 | struct input_dev *idev; | 37 | struct input_dev *idev; |
38 | struct work_struct work; | ||
37 | u8 pwm[2]; | 39 | u8 pwm[2]; |
38 | u8 psc[2]; | 40 | u8 psc[2]; |
39 | }; | 41 | }; |
@@ -63,7 +65,7 @@ static struct i2c_driver pca9532_driver = { | |||
63 | * as a compromise we average one pwm to the values requested by all | 65 | * as a compromise we average one pwm to the values requested by all |
64 | * leds that are not ON/OFF. | 66 | * leds that are not ON/OFF. |
65 | * */ | 67 | * */ |
66 | static int pca9532_setpwm(struct i2c_client *client, int pwm, int blink, | 68 | static int pca9532_calcpwm(struct i2c_client *client, int pwm, int blink, |
67 | enum led_brightness value) | 69 | enum led_brightness value) |
68 | { | 70 | { |
69 | int a = 0, b = 0, i = 0; | 71 | int a = 0, b = 0, i = 0; |
@@ -84,11 +86,17 @@ static int pca9532_setpwm(struct i2c_client *client, int pwm, int blink, | |||
84 | b = b/a; | 86 | b = b/a; |
85 | if (b > 0xFF) | 87 | if (b > 0xFF) |
86 | return -EINVAL; | 88 | return -EINVAL; |
87 | mutex_lock(&data->update_lock); | ||
88 | data->pwm[pwm] = b; | 89 | data->pwm[pwm] = b; |
90 | data->psc[pwm] = blink; | ||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | static int pca9532_setpwm(struct i2c_client *client, int pwm) | ||
95 | { | ||
96 | struct pca9532_data *data = i2c_get_clientdata(client); | ||
97 | mutex_lock(&data->update_lock); | ||
89 | i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(pwm), | 98 | i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(pwm), |
90 | data->pwm[pwm]); | 99 | data->pwm[pwm]); |
91 | data->psc[pwm] = blink; | ||
92 | i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(pwm), | 100 | i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(pwm), |
93 | data->psc[pwm]); | 101 | data->psc[pwm]); |
94 | mutex_unlock(&data->update_lock); | 102 | mutex_unlock(&data->update_lock); |
@@ -124,11 +132,11 @@ static void pca9532_set_brightness(struct led_classdev *led_cdev, | |||
124 | led->state = PCA9532_ON; | 132 | led->state = PCA9532_ON; |
125 | else { | 133 | else { |
126 | led->state = PCA9532_PWM0; /* Thecus: hardcode one pwm */ | 134 | led->state = PCA9532_PWM0; /* Thecus: hardcode one pwm */ |
127 | err = pca9532_setpwm(led->client, 0, 0, value); | 135 | err = pca9532_calcpwm(led->client, 0, 0, value); |
128 | if (err) | 136 | if (err) |
129 | return; /* XXX: led api doesn't allow error code? */ | 137 | return; /* XXX: led api doesn't allow error code? */ |
130 | } | 138 | } |
131 | pca9532_setled(led); | 139 | schedule_work(&led->work); |
132 | } | 140 | } |
133 | 141 | ||
134 | static int pca9532_set_blink(struct led_classdev *led_cdev, | 142 | static int pca9532_set_blink(struct led_classdev *led_cdev, |
@@ -137,6 +145,7 @@ static int pca9532_set_blink(struct led_classdev *led_cdev, | |||
137 | struct pca9532_led *led = ldev_to_led(led_cdev); | 145 | struct pca9532_led *led = ldev_to_led(led_cdev); |
138 | struct i2c_client *client = led->client; | 146 | struct i2c_client *client = led->client; |
139 | int psc; | 147 | int psc; |
148 | int err = 0; | ||
140 | 149 | ||
141 | if (*delay_on == 0 && *delay_off == 0) { | 150 | if (*delay_on == 0 && *delay_off == 0) { |
142 | /* led subsystem ask us for a blink rate */ | 151 | /* led subsystem ask us for a blink rate */ |
@@ -148,7 +157,11 @@ static int pca9532_set_blink(struct led_classdev *led_cdev, | |||
148 | 157 | ||
149 | /* Thecus specific: only use PSC/PWM 0 */ | 158 | /* Thecus specific: only use PSC/PWM 0 */ |
150 | psc = (*delay_on * 152-1)/1000; | 159 | psc = (*delay_on * 152-1)/1000; |
151 | return pca9532_setpwm(client, 0, psc, led_cdev->brightness); | 160 | err = pca9532_calcpwm(client, 0, psc, led_cdev->brightness); |
161 | if (err) | ||
162 | return err; | ||
163 | schedule_work(&led->work); | ||
164 | return 0; | ||
152 | } | 165 | } |
153 | 166 | ||
154 | static int pca9532_event(struct input_dev *dev, unsigned int type, | 167 | static int pca9532_event(struct input_dev *dev, unsigned int type, |
@@ -165,13 +178,28 @@ static int pca9532_event(struct input_dev *dev, unsigned int type, | |||
165 | else | 178 | else |
166 | data->pwm[1] = 0; | 179 | data->pwm[1] = 0; |
167 | 180 | ||
168 | dev_info(&dev->dev, "setting beep to %d \n", data->pwm[1]); | 181 | schedule_work(&data->work); |
182 | |||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | static void pca9532_input_work(struct work_struct *work) | ||
187 | { | ||
188 | struct pca9532_data *data; | ||
189 | data = container_of(work, struct pca9532_data, work); | ||
169 | mutex_lock(&data->update_lock); | 190 | mutex_lock(&data->update_lock); |
170 | i2c_smbus_write_byte_data(data->client, PCA9532_REG_PWM(1), | 191 | i2c_smbus_write_byte_data(data->client, PCA9532_REG_PWM(1), |
171 | data->pwm[1]); | 192 | data->pwm[1]); |
172 | mutex_unlock(&data->update_lock); | 193 | mutex_unlock(&data->update_lock); |
194 | } | ||
173 | 195 | ||
174 | return 0; | 196 | static void pca9532_led_work(struct work_struct *work) |
197 | { | ||
198 | struct pca9532_led *led; | ||
199 | led = container_of(work, struct pca9532_led, work); | ||
200 | if (led->state == PCA9532_PWM0) | ||
201 | pca9532_setpwm(led->client, 0); | ||
202 | pca9532_setled(led); | ||
175 | } | 203 | } |
176 | 204 | ||
177 | static int pca9532_configure(struct i2c_client *client, | 205 | static int pca9532_configure(struct i2c_client *client, |
@@ -204,6 +232,7 @@ static int pca9532_configure(struct i2c_client *client, | |||
204 | led->ldev.brightness = LED_OFF; | 232 | led->ldev.brightness = LED_OFF; |
205 | led->ldev.brightness_set = pca9532_set_brightness; | 233 | led->ldev.brightness_set = pca9532_set_brightness; |
206 | led->ldev.blink_set = pca9532_set_blink; | 234 | led->ldev.blink_set = pca9532_set_blink; |
235 | INIT_WORK(&led->work, pca9532_led_work); | ||
207 | err = led_classdev_register(&client->dev, &led->ldev); | 236 | err = led_classdev_register(&client->dev, &led->ldev); |
208 | if (err < 0) { | 237 | if (err < 0) { |
209 | dev_err(&client->dev, | 238 | dev_err(&client->dev, |
@@ -233,9 +262,11 @@ static int pca9532_configure(struct i2c_client *client, | |||
233 | BIT_MASK(SND_TONE); | 262 | BIT_MASK(SND_TONE); |
234 | data->idev->event = pca9532_event; | 263 | data->idev->event = pca9532_event; |
235 | input_set_drvdata(data->idev, data); | 264 | input_set_drvdata(data->idev, data); |
265 | INIT_WORK(&data->work, pca9532_input_work); | ||
236 | err = input_register_device(data->idev); | 266 | err = input_register_device(data->idev); |
237 | if (err) { | 267 | if (err) { |
238 | input_free_device(data->idev); | 268 | input_free_device(data->idev); |
269 | cancel_work_sync(&data->work); | ||
239 | data->idev = NULL; | 270 | data->idev = NULL; |
240 | goto exit; | 271 | goto exit; |
241 | } | 272 | } |
@@ -252,11 +283,13 @@ exit: | |||
252 | break; | 283 | break; |
253 | case PCA9532_TYPE_LED: | 284 | case PCA9532_TYPE_LED: |
254 | led_classdev_unregister(&data->leds[i].ldev); | 285 | led_classdev_unregister(&data->leds[i].ldev); |
286 | cancel_work_sync(&data->leds[i].work); | ||
255 | break; | 287 | break; |
256 | case PCA9532_TYPE_N2100_BEEP: | 288 | case PCA9532_TYPE_N2100_BEEP: |
257 | if (data->idev != NULL) { | 289 | if (data->idev != NULL) { |
258 | input_unregister_device(data->idev); | 290 | input_unregister_device(data->idev); |
259 | input_free_device(data->idev); | 291 | input_free_device(data->idev); |
292 | cancel_work_sync(&data->work); | ||
260 | data->idev = NULL; | 293 | data->idev = NULL; |
261 | } | 294 | } |
262 | break; | 295 | break; |
@@ -307,11 +340,13 @@ static int pca9532_remove(struct i2c_client *client) | |||
307 | break; | 340 | break; |
308 | case PCA9532_TYPE_LED: | 341 | case PCA9532_TYPE_LED: |
309 | led_classdev_unregister(&data->leds[i].ldev); | 342 | led_classdev_unregister(&data->leds[i].ldev); |
343 | cancel_work_sync(&data->leds[i].work); | ||
310 | break; | 344 | break; |
311 | case PCA9532_TYPE_N2100_BEEP: | 345 | case PCA9532_TYPE_N2100_BEEP: |
312 | if (data->idev != NULL) { | 346 | if (data->idev != NULL) { |
313 | input_unregister_device(data->idev); | 347 | input_unregister_device(data->idev); |
314 | input_free_device(data->idev); | 348 | input_free_device(data->idev); |
349 | cancel_work_sync(&data->work); | ||
315 | data->idev = NULL; | 350 | data->idev = NULL; |
316 | } | 351 | } |
317 | break; | 352 | break; |