diff options
author | Ricardo Ribalda Delgado <ricardo.ribalda@gmail.com> | 2013-08-14 17:23:49 -0400 |
---|---|---|
committer | Bryan Wu <cooloney@gmail.com> | 2013-08-26 20:22:14 -0400 |
commit | a7d0e9884fd7594d4de5066add5135ac6bb55bd4 (patch) | |
tree | 8090e4f54ecbde5fa2b5aa6afeb07f2e25b4cbca /drivers | |
parent | a5cd98b796b6891edc324415034a7f89531c754f (diff) |
leds-pca9633: Add mutex to the ledout register
To update an LED a register has to be read, updated and writen. If
another LED whas been updated at the same time, this could lead into
wrong updates.
This patch adds a common mutex to all the leds of the same chip to
protect the ledout register.
Signed-off-by: Ricardo Ribalda Delgado <ricardo.ribalda@gmail.com>
Signed-off-by: Bryan Wu <cooloney@gmail.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/leds/leds-pca9633.c | 86 |
1 files changed, 55 insertions, 31 deletions
diff --git a/drivers/leds/leds-pca9633.c b/drivers/leds/leds-pca9633.c index 7dd48f896d4e..aaa1c4a0dbc5 100644 --- a/drivers/leds/leds-pca9633.c +++ b/drivers/leds/leds-pca9633.c | |||
@@ -93,9 +93,17 @@ enum pca9633_cmd { | |||
93 | BLINK_SET, | 93 | BLINK_SET, |
94 | }; | 94 | }; |
95 | 95 | ||
96 | struct pca9633_led { | 96 | struct pca9633_led; |
97 | struct i2c_client *client; | 97 | |
98 | struct pca9633 { | ||
98 | struct pca9633_chipdef *chipdef; | 99 | struct pca9633_chipdef *chipdef; |
100 | struct mutex mutex; | ||
101 | struct i2c_client *client; | ||
102 | struct pca9633_led *leds; | ||
103 | }; | ||
104 | |||
105 | struct pca9633_led { | ||
106 | struct pca9633 *chip; | ||
99 | struct work_struct work; | 107 | struct work_struct work; |
100 | enum led_brightness brightness; | 108 | enum led_brightness brightness; |
101 | struct led_classdev led_cdev; | 109 | struct led_classdev led_cdev; |
@@ -108,52 +116,60 @@ struct pca9633_led { | |||
108 | 116 | ||
109 | static void pca9633_brightness_work(struct pca9633_led *pca9633) | 117 | static void pca9633_brightness_work(struct pca9633_led *pca9633) |
110 | { | 118 | { |
111 | u8 ledout_addr = pca9633->chipdef->ledout_base + (pca9633->led_num / 4); | 119 | u8 ledout_addr = pca9633->chip->chipdef->ledout_base |
120 | + (pca9633->led_num / 4); | ||
112 | u8 ledout; | 121 | u8 ledout; |
113 | int shift = 2 * (pca9633->led_num % 4); | 122 | int shift = 2 * (pca9633->led_num % 4); |
114 | u8 mask = 0x3 << shift; | 123 | u8 mask = 0x3 << shift; |
115 | 124 | ||
116 | ledout = i2c_smbus_read_byte_data(pca9633->client, ledout_addr); | 125 | mutex_lock(&pca9633->chip->mutex); |
126 | ledout = i2c_smbus_read_byte_data(pca9633->chip->client, ledout_addr); | ||
117 | switch (pca9633->brightness) { | 127 | switch (pca9633->brightness) { |
118 | case LED_FULL: | 128 | case LED_FULL: |
119 | i2c_smbus_write_byte_data(pca9633->client, ledout_addr, | 129 | i2c_smbus_write_byte_data(pca9633->chip->client, ledout_addr, |
120 | (ledout & ~mask) | (PCA9633_LED_ON << shift)); | 130 | (ledout & ~mask) | (PCA9633_LED_ON << shift)); |
121 | break; | 131 | break; |
122 | case LED_OFF: | 132 | case LED_OFF: |
123 | i2c_smbus_write_byte_data(pca9633->client, ledout_addr, | 133 | i2c_smbus_write_byte_data(pca9633->chip->client, ledout_addr, |
124 | ledout & ~mask); | 134 | ledout & ~mask); |
125 | break; | 135 | break; |
126 | default: | 136 | default: |
127 | i2c_smbus_write_byte_data(pca9633->client, | 137 | i2c_smbus_write_byte_data(pca9633->chip->client, |
128 | PCA9633_PWM_BASE + pca9633->led_num, | 138 | PCA9633_PWM_BASE + pca9633->led_num, |
129 | pca9633->brightness); | 139 | pca9633->brightness); |
130 | i2c_smbus_write_byte_data(pca9633->client, ledout_addr, | 140 | i2c_smbus_write_byte_data(pca9633->chip->client, ledout_addr, |
131 | (ledout & ~mask) | (PCA9633_LED_PWM << shift)); | 141 | (ledout & ~mask) | (PCA9633_LED_PWM << shift)); |
132 | break; | 142 | break; |
133 | } | 143 | } |
144 | mutex_unlock(&pca9633->chip->mutex); | ||
134 | } | 145 | } |
135 | 146 | ||
136 | static void pca9633_blink_work(struct pca9633_led *pca9633) | 147 | static void pca9633_blink_work(struct pca9633_led *pca9633) |
137 | { | 148 | { |
138 | u8 ledout_addr = pca9633->chipdef->ledout_base + (pca9633->led_num / 4); | 149 | u8 ledout_addr = pca9633->chip->chipdef->ledout_base + |
139 | u8 ledout = i2c_smbus_read_byte_data(pca9633->client, ledout_addr); | 150 | (pca9633->led_num / 4); |
140 | u8 mode2 = i2c_smbus_read_byte_data(pca9633->client, PCA9633_MODE2); | 151 | u8 ledout; |
152 | u8 mode2 = i2c_smbus_read_byte_data(pca9633->chip->client, | ||
153 | PCA9633_MODE2); | ||
141 | int shift = 2 * (pca9633->led_num % 4); | 154 | int shift = 2 * (pca9633->led_num % 4); |
142 | u8 mask = 0x3 << shift; | 155 | u8 mask = 0x3 << shift; |
143 | 156 | ||
144 | i2c_smbus_write_byte_data(pca9633->client, pca9633->chipdef->grppwm, | 157 | i2c_smbus_write_byte_data(pca9633->chip->client, |
145 | pca9633->gdc); | 158 | pca9633->chip->chipdef->grppwm, pca9633->gdc); |
146 | 159 | ||
147 | i2c_smbus_write_byte_data(pca9633->client, pca9633->chipdef->grpfreq, | 160 | i2c_smbus_write_byte_data(pca9633->chip->client, |
148 | pca9633->gfrq); | 161 | pca9633->chip->chipdef->grpfreq, pca9633->gfrq); |
149 | 162 | ||
150 | if (!(mode2 & PCA9633_MODE2_DMBLNK)) | 163 | if (!(mode2 & PCA9633_MODE2_DMBLNK)) |
151 | i2c_smbus_write_byte_data(pca9633->client, PCA9633_MODE2, | 164 | i2c_smbus_write_byte_data(pca9633->chip->client, PCA9633_MODE2, |
152 | mode2 | PCA9633_MODE2_DMBLNK); | 165 | mode2 | PCA9633_MODE2_DMBLNK); |
153 | 166 | ||
167 | mutex_lock(&pca9633->chip->mutex); | ||
168 | ledout = i2c_smbus_read_byte_data(pca9633->chip->client, ledout_addr); | ||
154 | if ((ledout & mask) != (PCA9633_LED_GRP_PWM << shift)) | 169 | if ((ledout & mask) != (PCA9633_LED_GRP_PWM << shift)) |
155 | i2c_smbus_write_byte_data(pca9633->client, ledout_addr, | 170 | i2c_smbus_write_byte_data(pca9633->chip->client, ledout_addr, |
156 | (ledout & ~mask) | (PCA9633_LED_GRP_PWM << shift)); | 171 | (ledout & ~mask) | (PCA9633_LED_GRP_PWM << shift)); |
172 | mutex_unlock(&pca9633->chip->mutex); | ||
157 | } | 173 | } |
158 | 174 | ||
159 | static void pca9633_work(struct work_struct *work) | 175 | static void pca9633_work(struct work_struct *work) |
@@ -318,6 +334,7 @@ pca9633_dt_init(struct i2c_client *client, struct pca9633_chipdef *chip) | |||
318 | static int pca9633_probe(struct i2c_client *client, | 334 | static int pca9633_probe(struct i2c_client *client, |
319 | const struct i2c_device_id *id) | 335 | const struct i2c_device_id *id) |
320 | { | 336 | { |
337 | struct pca9633 *pca9633_chip; | ||
321 | struct pca9633_led *pca9633; | 338 | struct pca9633_led *pca9633; |
322 | struct pca9633_platform_data *pdata; | 339 | struct pca9633_platform_data *pdata; |
323 | struct pca9633_chipdef *chip; | 340 | struct pca9633_chipdef *chip; |
@@ -341,17 +358,30 @@ static int pca9633_probe(struct i2c_client *client, | |||
341 | return -EINVAL; | 358 | return -EINVAL; |
342 | } | 359 | } |
343 | 360 | ||
361 | pca9633_chip = devm_kzalloc(&client->dev, sizeof(*pca9633_chip), | ||
362 | GFP_KERNEL); | ||
363 | if (!pca9633_chip) | ||
364 | return -ENOMEM; | ||
344 | pca9633 = devm_kzalloc(&client->dev, chip->n_leds * sizeof(*pca9633), | 365 | pca9633 = devm_kzalloc(&client->dev, chip->n_leds * sizeof(*pca9633), |
345 | GFP_KERNEL); | 366 | GFP_KERNEL); |
346 | if (!pca9633) | 367 | if (!pca9633) |
347 | return -ENOMEM; | 368 | return -ENOMEM; |
348 | 369 | ||
349 | i2c_set_clientdata(client, pca9633); | 370 | i2c_set_clientdata(client, pca9633_chip); |
371 | |||
372 | mutex_init(&pca9633_chip->mutex); | ||
373 | pca9633_chip->chipdef = chip; | ||
374 | pca9633_chip->client = client; | ||
375 | pca9633_chip->leds = pca9633; | ||
376 | |||
377 | /* Turn off LEDs by default*/ | ||
378 | i2c_smbus_write_byte_data(client, chip->ledout_base, 0x00); | ||
379 | if (chip->n_leds > 4) | ||
380 | i2c_smbus_write_byte_data(client, chip->ledout_base + 1, 0x00); | ||
350 | 381 | ||
351 | for (i = 0; i < chip->n_leds; i++) { | 382 | for (i = 0; i < chip->n_leds; i++) { |
352 | pca9633[i].client = client; | ||
353 | pca9633[i].led_num = i; | 383 | pca9633[i].led_num = i; |
354 | pca9633[i].chipdef = chip; | 384 | pca9633[i].chip = pca9633_chip; |
355 | 385 | ||
356 | /* Platform data can specify LED names and default triggers */ | 386 | /* Platform data can specify LED names and default triggers */ |
357 | if (pdata && i < pdata->leds.num_leds) { | 387 | if (pdata && i < pdata->leds.num_leds) { |
@@ -389,11 +419,6 @@ static int pca9633_probe(struct i2c_client *client, | |||
389 | if (pdata && pdata->outdrv == PCA9633_OPEN_DRAIN) | 419 | if (pdata && pdata->outdrv == PCA9633_OPEN_DRAIN) |
390 | i2c_smbus_write_byte_data(client, PCA9633_MODE2, 0x01); | 420 | i2c_smbus_write_byte_data(client, PCA9633_MODE2, 0x01); |
391 | 421 | ||
392 | /* Turn off LEDs */ | ||
393 | i2c_smbus_write_byte_data(client, chip->ledout_base, 0x00); | ||
394 | if (chip->n_leds > 4) | ||
395 | i2c_smbus_write_byte_data(client, chip->ledout_base + 1, 0x00); | ||
396 | |||
397 | return 0; | 422 | return 0; |
398 | 423 | ||
399 | exit: | 424 | exit: |
@@ -407,14 +432,13 @@ exit: | |||
407 | 432 | ||
408 | static int pca9633_remove(struct i2c_client *client) | 433 | static int pca9633_remove(struct i2c_client *client) |
409 | { | 434 | { |
410 | struct pca9633_led *pca9633 = i2c_get_clientdata(client); | 435 | struct pca9633 *pca9633 = i2c_get_clientdata(client); |
411 | int i; | 436 | int i; |
412 | 437 | ||
413 | for (i = 0; i < pca9633->chipdef->n_leds; i++) | 438 | for (i = 0; i < pca9633->chipdef->n_leds; i++) { |
414 | if (pca9633[i].client != NULL) { | 439 | led_classdev_unregister(&pca9633->leds[i].led_cdev); |
415 | led_classdev_unregister(&pca9633[i].led_cdev); | 440 | cancel_work_sync(&pca9633->leds[i].work); |
416 | cancel_work_sync(&pca9633[i].work); | 441 | } |
417 | } | ||
418 | 442 | ||
419 | return 0; | 443 | return 0; |
420 | } | 444 | } |