aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/leds
diff options
context:
space:
mode:
authorSamu Onkalo <samu.p.onkalo@nokia.com>2011-01-12 19:59:19 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2011-01-13 11:03:06 -0500
commitd4e7ad03e84b2301be4f9a39ef2778126699ff0c (patch)
treef9eddca8aed77b76aa3a7690ae25fa5b5fc2c467 /drivers/leds
parentfbac0812de0455c6af3e1f29fccffc207073b7eb (diff)
leds: lp5521: fix circular locking
Driver contained possibility for circular locking. One lock is held by sysfs-core and another one by the driver itself. This happened when the driver created or removed sysfs entries dynamically. There is no real need to do those operations. Now all the sysfs entries are created at probe and removed at removal. Engine load sysfs entries are now visible all the time. However, access to the entries fails if the engine is disabled or running. Signed-off-by: Samu Onkalo <samu.p.onkalo@nokia.com> Cc: Arun Murthy <arun.murthy@stericsson.com> Reviewed-by: Ilkka Koskinen <ilkka.koskinen@nokia.com> Cc: Richard Purdie <rpurdie@rpsys.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/leds')
-rw-r--r--drivers/leds/leds-lp5521.c52
1 files changed, 8 insertions, 44 deletions
diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c
index e881a75dc39d..4d91c08656c5 100644
--- a/drivers/leds/leds-lp5521.c
+++ b/drivers/leds/leds-lp5521.c
@@ -98,7 +98,6 @@
98#define LP5521_EXT_CLK_USED 0x08 98#define LP5521_EXT_CLK_USED 0x08
99 99
100struct lp5521_engine { 100struct lp5521_engine {
101 const struct attribute_group *attributes;
102 int id; 101 int id;
103 u8 mode; 102 u8 mode;
104 u8 prog_page; 103 u8 prog_page;
@@ -225,25 +224,22 @@ static int lp5521_set_led_current(struct lp5521_chip *chip, int led, u8 curr)
225 curr); 224 curr);
226} 225}
227 226
228static void lp5521_init_engine(struct lp5521_chip *chip, 227static void lp5521_init_engine(struct lp5521_chip *chip)
229 const struct attribute_group *attr_group)
230{ 228{
231 int i; 229 int i;
232 for (i = 0; i < ARRAY_SIZE(chip->engines); i++) { 230 for (i = 0; i < ARRAY_SIZE(chip->engines); i++) {
233 chip->engines[i].id = i + 1; 231 chip->engines[i].id = i + 1;
234 chip->engines[i].engine_mask = LP5521_ENG_MASK_BASE >> (i * 2); 232 chip->engines[i].engine_mask = LP5521_ENG_MASK_BASE >> (i * 2);
235 chip->engines[i].prog_page = i; 233 chip->engines[i].prog_page = i;
236 chip->engines[i].attributes = &attr_group[i];
237 } 234 }
238} 235}
239 236
240static int lp5521_configure(struct i2c_client *client, 237static int lp5521_configure(struct i2c_client *client)
241 const struct attribute_group *attr_group)
242{ 238{
243 struct lp5521_chip *chip = i2c_get_clientdata(client); 239 struct lp5521_chip *chip = i2c_get_clientdata(client);
244 int ret; 240 int ret;
245 241
246 lp5521_init_engine(chip, attr_group); 242 lp5521_init_engine(chip);
247 243
248 /* Set all PWMs to direct control mode */ 244 /* Set all PWMs to direct control mode */
249 ret = lp5521_write(client, LP5521_REG_OP_MODE, 0x3F); 245 ret = lp5521_write(client, LP5521_REG_OP_MODE, 0x3F);
@@ -329,9 +325,6 @@ static int lp5521_detect(struct i2c_client *client)
329/* Set engine mode and create appropriate sysfs attributes, if required. */ 325/* Set engine mode and create appropriate sysfs attributes, if required. */
330static int lp5521_set_mode(struct lp5521_engine *engine, u8 mode) 326static int lp5521_set_mode(struct lp5521_engine *engine, u8 mode)
331{ 327{
332 struct lp5521_chip *chip = engine_to_lp5521(engine);
333 struct i2c_client *client = chip->client;
334 struct device *dev = &client->dev;
335 int ret = 0; 328 int ret = 0;
336 329
337 /* if in that mode already do nothing, except for run */ 330 /* if in that mode already do nothing, except for run */
@@ -343,18 +336,10 @@ static int lp5521_set_mode(struct lp5521_engine *engine, u8 mode)
343 } else if (mode == LP5521_CMD_LOAD) { 336 } else if (mode == LP5521_CMD_LOAD) {
344 lp5521_set_engine_mode(engine, LP5521_CMD_DISABLED); 337 lp5521_set_engine_mode(engine, LP5521_CMD_DISABLED);
345 lp5521_set_engine_mode(engine, LP5521_CMD_LOAD); 338 lp5521_set_engine_mode(engine, LP5521_CMD_LOAD);
346
347 ret = sysfs_create_group(&dev->kobj, engine->attributes);
348 if (ret)
349 return ret;
350 } else if (mode == LP5521_CMD_DISABLED) { 339 } else if (mode == LP5521_CMD_DISABLED) {
351 lp5521_set_engine_mode(engine, LP5521_CMD_DISABLED); 340 lp5521_set_engine_mode(engine, LP5521_CMD_DISABLED);
352 } 341 }
353 342
354 /* remove load attribute from sysfs if not in load mode */
355 if (engine->mode == LP5521_CMD_LOAD && mode != LP5521_CMD_LOAD)
356 sysfs_remove_group(&dev->kobj, engine->attributes);
357
358 engine->mode = mode; 343 engine->mode = mode;
359 344
360 return ret; 345 return ret;
@@ -389,7 +374,10 @@ static int lp5521_do_store_load(struct lp5521_engine *engine,
389 goto fail; 374 goto fail;
390 375
391 mutex_lock(&chip->lock); 376 mutex_lock(&chip->lock);
392 ret = lp5521_load_program(engine, pattern); 377 if (engine->mode == LP5521_CMD_LOAD)
378 ret = lp5521_load_program(engine, pattern);
379 else
380 ret = -EINVAL;
393 mutex_unlock(&chip->lock); 381 mutex_unlock(&chip->lock);
394 382
395 if (ret) { 383 if (ret) {
@@ -576,20 +564,8 @@ static struct attribute *lp5521_attributes[] = {
576 &dev_attr_engine2_mode.attr, 564 &dev_attr_engine2_mode.attr,
577 &dev_attr_engine3_mode.attr, 565 &dev_attr_engine3_mode.attr,
578 &dev_attr_selftest.attr, 566 &dev_attr_selftest.attr,
579 NULL
580};
581
582static struct attribute *lp5521_engine1_attributes[] = {
583 &dev_attr_engine1_load.attr, 567 &dev_attr_engine1_load.attr,
584 NULL
585};
586
587static struct attribute *lp5521_engine2_attributes[] = {
588 &dev_attr_engine2_load.attr, 568 &dev_attr_engine2_load.attr,
589 NULL
590};
591
592static struct attribute *lp5521_engine3_attributes[] = {
593 &dev_attr_engine3_load.attr, 569 &dev_attr_engine3_load.attr,
594 NULL 570 NULL
595}; 571};
@@ -598,12 +574,6 @@ static const struct attribute_group lp5521_group = {
598 .attrs = lp5521_attributes, 574 .attrs = lp5521_attributes,
599}; 575};
600 576
601static const struct attribute_group lp5521_engine_group[] = {
602 {.attrs = lp5521_engine1_attributes },
603 {.attrs = lp5521_engine2_attributes },
604 {.attrs = lp5521_engine3_attributes },
605};
606
607static int lp5521_register_sysfs(struct i2c_client *client) 577static int lp5521_register_sysfs(struct i2c_client *client)
608{ 578{
609 struct device *dev = &client->dev; 579 struct device *dev = &client->dev;
@@ -618,12 +588,6 @@ static void lp5521_unregister_sysfs(struct i2c_client *client)
618 588
619 sysfs_remove_group(&dev->kobj, &lp5521_group); 589 sysfs_remove_group(&dev->kobj, &lp5521_group);
620 590
621 for (i = 0; i < ARRAY_SIZE(chip->engines); i++) {
622 if (chip->engines[i].mode == LP5521_CMD_LOAD)
623 sysfs_remove_group(&dev->kobj,
624 chip->engines[i].attributes);
625 }
626
627 for (i = 0; i < chip->num_leds; i++) 591 for (i = 0; i < chip->num_leds; i++)
628 sysfs_remove_group(&chip->leds[i].cdev.dev->kobj, 592 sysfs_remove_group(&chip->leds[i].cdev.dev->kobj,
629 &lp5521_led_attribute_group); 593 &lp5521_led_attribute_group);
@@ -725,7 +689,7 @@ static int lp5521_probe(struct i2c_client *client,
725 689
726 dev_info(&client->dev, "%s programmable led chip found\n", id->name); 690 dev_info(&client->dev, "%s programmable led chip found\n", id->name);
727 691
728 ret = lp5521_configure(client, lp5521_engine_group); 692 ret = lp5521_configure(client);
729 if (ret < 0) { 693 if (ret < 0) {
730 dev_err(&client->dev, "error configuring chip\n"); 694 dev_err(&client->dev, "error configuring chip\n");
731 goto fail2; 695 goto fail2;