diff options
author | Ricardo Ribalda Delgado <ricardo.ribalda@gmail.com> | 2013-08-14 14:23:49 -0700 |
---|---|---|
committer | Bryan Wu <cooloney@gmail.com> | 2013-08-26 17:22:14 -0700 |
commit | a7d0e9884fd7594d4de5066add5135ac6bb55bd4 (patch) | |
tree | 8090e4f54ecbde5fa2b5aa6afeb07f2e25b4cbca | |
parent | a5cd98b796b6891edc324415034a7f89531c754f (diff) | |
download | lwn-a7d0e9884fd7594d4de5066add5135ac6bb55bd4.tar.gz lwn-a7d0e9884fd7594d4de5066add5135ac6bb55bd4.zip |
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>
-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 { BLINK_SET, }; -struct pca9633_led { - struct i2c_client *client; +struct pca9633_led; + +struct pca9633 { struct pca9633_chipdef *chipdef; + struct mutex mutex; + struct i2c_client *client; + struct pca9633_led *leds; +}; + +struct pca9633_led { + struct pca9633 *chip; struct work_struct work; enum led_brightness brightness; struct led_classdev led_cdev; @@ -108,52 +116,60 @@ struct pca9633_led { static void pca9633_brightness_work(struct pca9633_led *pca9633) { - u8 ledout_addr = pca9633->chipdef->ledout_base + (pca9633->led_num / 4); + u8 ledout_addr = pca9633->chip->chipdef->ledout_base + + (pca9633->led_num / 4); u8 ledout; int shift = 2 * (pca9633->led_num % 4); u8 mask = 0x3 << shift; - ledout = i2c_smbus_read_byte_data(pca9633->client, ledout_addr); + mutex_lock(&pca9633->chip->mutex); + ledout = i2c_smbus_read_byte_data(pca9633->chip->client, ledout_addr); switch (pca9633->brightness) { case LED_FULL: - i2c_smbus_write_byte_data(pca9633->client, ledout_addr, + i2c_smbus_write_byte_data(pca9633->chip->client, ledout_addr, (ledout & ~mask) | (PCA9633_LED_ON << shift)); break; case LED_OFF: - i2c_smbus_write_byte_data(pca9633->client, ledout_addr, + i2c_smbus_write_byte_data(pca9633->chip->client, ledout_addr, ledout & ~mask); break; default: - i2c_smbus_write_byte_data(pca9633->client, + i2c_smbus_write_byte_data(pca9633->chip->client, PCA9633_PWM_BASE + pca9633->led_num, pca9633->brightness); - i2c_smbus_write_byte_data(pca9633->client, ledout_addr, + i2c_smbus_write_byte_data(pca9633->chip->client, ledout_addr, (ledout & ~mask) | (PCA9633_LED_PWM << shift)); break; } + mutex_unlock(&pca9633->chip->mutex); } static void pca9633_blink_work(struct pca9633_led *pca9633) { - u8 ledout_addr = pca9633->chipdef->ledout_base + (pca9633->led_num / 4); - u8 ledout = i2c_smbus_read_byte_data(pca9633->client, ledout_addr); - u8 mode2 = i2c_smbus_read_byte_data(pca9633->client, PCA9633_MODE2); + u8 ledout_addr = pca9633->chip->chipdef->ledout_base + + (pca9633->led_num / 4); + u8 ledout; + u8 mode2 = i2c_smbus_read_byte_data(pca9633->chip->client, + PCA9633_MODE2); int shift = 2 * (pca9633->led_num % 4); u8 mask = 0x3 << shift; - i2c_smbus_write_byte_data(pca9633->client, pca9633->chipdef->grppwm, - pca9633->gdc); + i2c_smbus_write_byte_data(pca9633->chip->client, + pca9633->chip->chipdef->grppwm, pca9633->gdc); - i2c_smbus_write_byte_data(pca9633->client, pca9633->chipdef->grpfreq, - pca9633->gfrq); + i2c_smbus_write_byte_data(pca9633->chip->client, + pca9633->chip->chipdef->grpfreq, pca9633->gfrq); if (!(mode2 & PCA9633_MODE2_DMBLNK)) - i2c_smbus_write_byte_data(pca9633->client, PCA9633_MODE2, + i2c_smbus_write_byte_data(pca9633->chip->client, PCA9633_MODE2, mode2 | PCA9633_MODE2_DMBLNK); + mutex_lock(&pca9633->chip->mutex); + ledout = i2c_smbus_read_byte_data(pca9633->chip->client, ledout_addr); if ((ledout & mask) != (PCA9633_LED_GRP_PWM << shift)) - i2c_smbus_write_byte_data(pca9633->client, ledout_addr, + i2c_smbus_write_byte_data(pca9633->chip->client, ledout_addr, (ledout & ~mask) | (PCA9633_LED_GRP_PWM << shift)); + mutex_unlock(&pca9633->chip->mutex); } static void pca9633_work(struct work_struct *work) @@ -318,6 +334,7 @@ pca9633_dt_init(struct i2c_client *client, struct pca9633_chipdef *chip) static int pca9633_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct pca9633 *pca9633_chip; struct pca9633_led *pca9633; struct pca9633_platform_data *pdata; struct pca9633_chipdef *chip; @@ -341,17 +358,30 @@ static int pca9633_probe(struct i2c_client *client, return -EINVAL; } + pca9633_chip = devm_kzalloc(&client->dev, sizeof(*pca9633_chip), + GFP_KERNEL); + if (!pca9633_chip) + return -ENOMEM; pca9633 = devm_kzalloc(&client->dev, chip->n_leds * sizeof(*pca9633), GFP_KERNEL); if (!pca9633) return -ENOMEM; - i2c_set_clientdata(client, pca9633); + i2c_set_clientdata(client, pca9633_chip); + + mutex_init(&pca9633_chip->mutex); + pca9633_chip->chipdef = chip; + pca9633_chip->client = client; + pca9633_chip->leds = pca9633; + + /* Turn off LEDs by default*/ + i2c_smbus_write_byte_data(client, chip->ledout_base, 0x00); + if (chip->n_leds > 4) + i2c_smbus_write_byte_data(client, chip->ledout_base + 1, 0x00); for (i = 0; i < chip->n_leds; i++) { - pca9633[i].client = client; pca9633[i].led_num = i; - pca9633[i].chipdef = chip; + pca9633[i].chip = pca9633_chip; /* Platform data can specify LED names and default triggers */ if (pdata && i < pdata->leds.num_leds) { @@ -389,11 +419,6 @@ static int pca9633_probe(struct i2c_client *client, if (pdata && pdata->outdrv == PCA9633_OPEN_DRAIN) i2c_smbus_write_byte_data(client, PCA9633_MODE2, 0x01); - /* Turn off LEDs */ - i2c_smbus_write_byte_data(client, chip->ledout_base, 0x00); - if (chip->n_leds > 4) - i2c_smbus_write_byte_data(client, chip->ledout_base + 1, 0x00); - return 0; exit: @@ -407,14 +432,13 @@ exit: static int pca9633_remove(struct i2c_client *client) { - struct pca9633_led *pca9633 = i2c_get_clientdata(client); + struct pca9633 *pca9633 = i2c_get_clientdata(client); int i; - for (i = 0; i < pca9633->chipdef->n_leds; i++) - if (pca9633[i].client != NULL) { - led_classdev_unregister(&pca9633[i].led_cdev); - cancel_work_sync(&pca9633[i].work); - } + for (i = 0; i < pca9633->chipdef->n_leds; i++) { + led_classdev_unregister(&pca9633->leds[i].led_cdev); + cancel_work_sync(&pca9633->leds[i].work); + } return 0; } |