diff options
Diffstat (limited to 'drivers/leds/leds-lp5523.c')
-rw-r--r-- | drivers/leds/leds-lp5523.c | 103 |
1 files changed, 42 insertions, 61 deletions
diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 1e11fcc08b28..d0c4068ecddd 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -105,7 +105,6 @@ #define SHIFT_MASK(id) (((id) - 1) * 2) struct lp5523_engine { - const struct attribute_group *attributes; int id; u8 mode; u8 prog_page; @@ -134,15 +133,18 @@ struct lp5523_chip { u8 num_leds; }; -#define cdev_to_led(c) container_of(c, struct lp5523_led, cdev) +static inline struct lp5523_led *cdev_to_led(struct led_classdev *cdev) +{ + return container_of(cdev, struct lp5523_led, cdev); +} -static struct lp5523_chip *engine_to_lp5523(struct lp5523_engine *engine) +static inline struct lp5523_chip *engine_to_lp5523(struct lp5523_engine *engine) { return container_of(engine, struct lp5523_chip, engines[engine->id - 1]); } -static struct lp5523_chip *led_to_lp5523(struct lp5523_led *led) +static inline struct lp5523_chip *led_to_lp5523(struct lp5523_led *led) { return container_of(led, struct lp5523_chip, leds[led->id]); @@ -200,13 +202,9 @@ static int lp5523_configure(struct i2c_client *client) { 0x9c, 0x50, 0x9c, 0xd0, 0x9d, 0x80, 0xd8, 0x00, 0}, }; - lp5523_write(client, LP5523_REG_RESET, 0xff); - - usleep_range(10000, 100000); - ret |= lp5523_write(client, LP5523_REG_ENABLE, LP5523_ENABLE); - /* Chip startup time after reset is 500 us */ - usleep_range(1000, 10000); + /* Chip startup time is 500 us, 1 - 2 ms gives some margin */ + usleep_range(1000, 2000); ret |= lp5523_write(client, LP5523_REG_CONFIG, LP5523_AUTO_INC | LP5523_PWR_SAVE | @@ -243,8 +241,8 @@ static int lp5523_configure(struct i2c_client *client) return -1; } - /* Wait 3ms and check the engine status */ - usleep_range(3000, 20000); + /* Let the programs run for couple of ms and check the engine status */ + usleep_range(3000, 6000); lp5523_read(client, LP5523_REG_STATUS, &status); status &= LP5523_ENG_STATUS_MASK; @@ -404,14 +402,23 @@ static ssize_t store_engine_leds(struct device *dev, struct i2c_client *client = to_i2c_client(dev); struct lp5523_chip *chip = i2c_get_clientdata(client); u16 mux = 0; + ssize_t ret; if (lp5523_mux_parse(buf, &mux, len)) return -EINVAL; + mutex_lock(&chip->lock); + ret = -EINVAL; + if (chip->engines[nr - 1].mode != LP5523_CMD_LOAD) + goto leave; + if (lp5523_load_mux(&chip->engines[nr - 1], mux)) - return -EINVAL; + goto leave; - return len; + ret = len; +leave: + mutex_unlock(&chip->lock); + return ret; } #define store_leds(nr) \ @@ -449,10 +456,10 @@ static ssize_t lp5523_selftest(struct device *dev, /* Measure VDD (i.e. VBAT) first (channel 16 corresponds to VDD) */ lp5523_write(chip->client, LP5523_REG_LED_TEST_CTRL, LP5523_EN_LEDTEST | 16); - usleep_range(3000, 10000); + usleep_range(3000, 6000); /* ADC conversion time is typically 2.7 ms */ ret = lp5523_read(chip->client, LP5523_REG_STATUS, &status); if (!(status & LP5523_LEDTEST_DONE)) - usleep_range(3000, 10000); + usleep_range(3000, 6000); /* Was not ready. Wait little bit */ ret |= lp5523_read(chip->client, LP5523_REG_LED_TEST_ADC, &vdd); vdd--; /* There may be some fluctuation in measurement */ @@ -468,16 +475,16 @@ static ssize_t lp5523_selftest(struct device *dev, chip->pdata->led_config[i].led_current); lp5523_write(chip->client, LP5523_REG_LED_PWM_BASE + i, 0xff); - /* let current stabilize 2ms before measurements start */ - usleep_range(2000, 10000); + /* let current stabilize 2 - 4ms before measurements start */ + usleep_range(2000, 4000); lp5523_write(chip->client, LP5523_REG_LED_TEST_CTRL, LP5523_EN_LEDTEST | i); - /* ledtest takes 2.7ms */ - usleep_range(3000, 10000); + /* ADC conversion time is 2.7 ms typically */ + usleep_range(3000, 6000); ret = lp5523_read(chip->client, LP5523_REG_STATUS, &status); if (!(status & LP5523_LEDTEST_DONE)) - usleep_range(3000, 10000); + usleep_range(3000, 6000);/* Was not ready. Wait. */ ret |= lp5523_read(chip->client, LP5523_REG_LED_TEST_ADC, &adc); if (adc >= vdd || adc < LP5523_ADC_SHORTCIRC_LIM) @@ -557,7 +564,11 @@ static int lp5523_do_store_load(struct lp5523_engine *engine, mutex_lock(&chip->lock); - ret = lp5523_load_program(engine, pattern); + if (engine->mode == LP5523_CMD_LOAD) + ret = lp5523_load_program(engine, pattern); + else + ret = -EINVAL; + mutex_unlock(&chip->lock); if (ret) { @@ -738,37 +749,18 @@ static struct attribute *lp5523_attributes[] = { &dev_attr_engine2_mode.attr, &dev_attr_engine3_mode.attr, &dev_attr_selftest.attr, - NULL -}; - -static struct attribute *lp5523_engine1_attributes[] = { &dev_attr_engine1_load.attr, &dev_attr_engine1_leds.attr, - NULL -}; - -static struct attribute *lp5523_engine2_attributes[] = { &dev_attr_engine2_load.attr, &dev_attr_engine2_leds.attr, - NULL -}; - -static struct attribute *lp5523_engine3_attributes[] = { &dev_attr_engine3_load.attr, &dev_attr_engine3_leds.attr, - NULL }; static const struct attribute_group lp5523_group = { .attrs = lp5523_attributes, }; -static const struct attribute_group lp5523_engine_group[] = { - {.attrs = lp5523_engine1_attributes }, - {.attrs = lp5523_engine2_attributes }, - {.attrs = lp5523_engine3_attributes }, -}; - static int lp5523_register_sysfs(struct i2c_client *client) { struct device *dev = &client->dev; @@ -789,10 +781,6 @@ static void lp5523_unregister_sysfs(struct i2c_client *client) sysfs_remove_group(&dev->kobj, &lp5523_group); - for (i = 0; i < ARRAY_SIZE(chip->engines); i++) - if (chip->engines[i].mode == LP5523_CMD_LOAD) - sysfs_remove_group(&dev->kobj, &lp5523_engine_group[i]); - for (i = 0; i < chip->num_leds; i++) sysfs_remove_group(&chip->leds[i].cdev.dev->kobj, &lp5523_led_attribute_group); @@ -803,10 +791,6 @@ static void lp5523_unregister_sysfs(struct i2c_client *client) /*--------------------------------------------------------------*/ static int lp5523_set_mode(struct lp5523_engine *engine, u8 mode) { - /* engine to chip */ - struct lp5523_chip *chip = engine_to_lp5523(engine); - struct i2c_client *client = chip->client; - struct device *dev = &client->dev; int ret = 0; /* if in that mode already do nothing, except for run */ @@ -818,18 +802,10 @@ static int lp5523_set_mode(struct lp5523_engine *engine, u8 mode) } else if (mode == LP5523_CMD_LOAD) { lp5523_set_engine_mode(engine, LP5523_CMD_DISABLED); lp5523_set_engine_mode(engine, LP5523_CMD_LOAD); - - ret = sysfs_create_group(&dev->kobj, engine->attributes); - if (ret) - return ret; } else if (mode == LP5523_CMD_DISABLED) { lp5523_set_engine_mode(engine, LP5523_CMD_DISABLED); } - /* remove load attribute from sysfs if not in load mode */ - if (engine->mode == LP5523_CMD_LOAD && mode != LP5523_CMD_LOAD) - sysfs_remove_group(&dev->kobj, engine->attributes); - engine->mode = mode; return ret; @@ -846,7 +822,6 @@ static int __init lp5523_init_engine(struct lp5523_engine *engine, int id) engine->engine_mask = LP5523_ENG_MASK_BASE >> SHIFT_MASK(id); engine->prog_page = id - 1; engine->mux_page = id + 2; - engine->attributes = &lp5523_engine_group[id - 1]; return 0; } @@ -871,7 +846,8 @@ static int __init lp5523_init_led(struct lp5523_led *led, struct device *dev, return -EINVAL; } - snprintf(name, 32, "lp5523:channel%d", chan); + snprintf(name, sizeof(name), "%s:channel%d", + pdata->label ?: "lp5523", chan); led->cdev.name = name; led->cdev.brightness_set = lp5523_set_brightness; @@ -930,11 +906,16 @@ static int lp5523_probe(struct i2c_client *client, if (pdata->enable) { pdata->enable(0); - usleep_range(1000, 10000); + usleep_range(1000, 2000); /* Keep enable down at least 1ms */ pdata->enable(1); - usleep_range(1000, 10000); /* Spec says min 500us */ + usleep_range(1000, 2000); /* 500us abs min. */ } + lp5523_write(client, LP5523_REG_RESET, 0xff); + usleep_range(10000, 20000); /* + * Exact value is not available. 10 - 20ms + * appears to be enough for reset. + */ ret = lp5523_detect(client); if (ret) goto fail2; |