aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRiku Voipio <riku.voipio@iki.fi>2008-12-03 03:21:36 -0500
committerRichard Purdie <rpurdie@linux.intel.com>2009-01-08 07:38:58 -0500
commit934cd3f979a1daacbd403398f2c7a8f6720c33aa (patch)
tree1cfb5b9c4d4c380c9b88d045b6aa233ca5211755
parentf785d022add53ec4d9625495b335bed40bd6c079 (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>
-rw-r--r--drivers/leds/leds-pca9532.c51
-rw-r--r--include/linux/leds-pca9532.h2
2 files changed, 45 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
21static const unsigned short normal_i2c[] = { /*0x60,*/ I2C_CLIENT_END}; 22static 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 * */
66static int pca9532_setpwm(struct i2c_client *client, int pwm, int blink, 68static 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
94static 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
134static int pca9532_set_blink(struct led_classdev *led_cdev, 142static 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
154static int pca9532_event(struct input_dev *dev, unsigned int type, 167static 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
186static 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; 196static 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
177static int pca9532_configure(struct i2c_client *client, 205static 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;
diff --git a/include/linux/leds-pca9532.h b/include/linux/leds-pca9532.h
index 81b4207deb95..96eea90f01a8 100644
--- a/include/linux/leds-pca9532.h
+++ b/include/linux/leds-pca9532.h
@@ -15,6 +15,7 @@
15#define __LINUX_PCA9532_H 15#define __LINUX_PCA9532_H
16 16
17#include <linux/leds.h> 17#include <linux/leds.h>
18#include <linux/workqueue.h>
18 19
19enum pca9532_state { 20enum pca9532_state {
20 PCA9532_OFF = 0x0, 21 PCA9532_OFF = 0x0,
@@ -31,6 +32,7 @@ struct pca9532_led {
31 struct i2c_client *client; 32 struct i2c_client *client;
32 char *name; 33 char *name;
33 struct led_classdev ldev; 34 struct led_classdev ldev;
35 struct work_struct work;
34 enum pca9532_type type; 36 enum pca9532_type type;
35 enum pca9532_state state; 37 enum pca9532_state state;
36}; 38};