2011-04-05 08:31:26

by Jan Weitzel

[permalink] [raw]
Subject: [PATCH] leds: leds-pca9532 add support pca9530, pca9531 and pca9533

Adding chipinfo to use driver with pca953x family
Rename driver to pca953x, but left files and platformflags named pca9532

Tested with pca9530 and pca9533

Tested-by: Juergen Kilb <[email protected]>
Signed-off-by: Jan Weitzel <[email protected]>
---
drivers/leds/leds-pca9532.c | 80 ++++++++++++++++++++++++++++++++-----------
1 files changed, 60 insertions(+), 20 deletions(-)

diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c
index afac338..5479b8f 100644
--- a/drivers/leds/leds-pca9532.c
+++ b/drivers/leds/leds-pca9532.c
@@ -1,13 +1,14 @@
/*
* pca9532.c - 16-bit Led dimmer
*
+ * Copyright (C) 2011 Jan Weitzel
* Copyright (C) 2008 Riku Voipio
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
- * Datasheet: http://www.nxp.com/acrobat/datasheets/PCA9532_3.pdf
+ * Datasheet: http://www.nxp.com/documents/data_sheet/PCA9532.pdf
*
*/

@@ -20,20 +21,26 @@
#include <linux/workqueue.h>
#include <linux/leds-pca9532.h>

-#define PCA9532_REG_PSC(i) (0x2+(i)*2)
-#define PCA9532_REG_PWM(i) (0x3+(i)*2)
-#define PCA9532_REG_LS0 0x6
-#define LED_REG(led) ((led>>2)+PCA9532_REG_LS0)
-#define LED_NUM(led) (led & 0x3)
+/* m = num_leds*/
+#define PCA9532_REG_OFFSET(m) ((m) >> 4)
+#define PCA9532_REG_PSC(m, i) (PCA9532_REG_OFFSET(m) + 0x1 + (i) * 2)
+#define PCA9532_REG_PWM(m, i) (PCA9532_REG_OFFSET(m) + 0x2 + (i) * 2)
+#define LED_REG(m, led) (PCA9532_REG_OFFSET(m) + 0x5 + (led >> 2))
+#define LED_NUM(led) (led & 0x3)

#define ldev_to_led(c) container_of(c, struct pca9532_led, ldev)

+struct pca9532_chip_info {
+ u8 num_leds;
+};
+
struct pca9532_data {
struct i2c_client *client;
struct pca9532_led leds[16];
struct mutex update_lock;
struct input_dev *idev;
struct work_struct work;
+ const struct pca9532_chip_info *chip_info;
u8 pwm[2];
u8 psc[2];
};
@@ -42,16 +49,41 @@ static int pca9532_probe(struct i2c_client *client,
const struct i2c_device_id *id);
static int pca9532_remove(struct i2c_client *client);

+enum {
+ pca9530,
+ pca9531,
+ pca9532,
+ pca9533,
+};
+
static const struct i2c_device_id pca9532_id[] = {
- { "pca9532", 0 },
+ { "pca9530", pca9530 },
+ { "pca9531", pca9531 },
+ { "pca9532", pca9532 },
+ { "pca9533", pca9533 },
{ }
};

MODULE_DEVICE_TABLE(i2c, pca9532_id);

+static const struct pca9532_chip_info pca9532_chip_info_tbl[] = {
+ [pca9530] = {
+ .num_leds = 2,
+ },
+ [pca9531] = {
+ .num_leds = 8,
+ },
+ [pca9532] = {
+ .num_leds = 16,
+ },
+ [pca9533] = {
+ .num_leds = 4,
+ },
+};
+
static struct i2c_driver pca9532_driver = {
.driver = {
- .name = "pca9532",
+ .name = "pca953x",
},
.probe = pca9532_probe,
.remove = pca9532_remove,
@@ -68,7 +100,7 @@ static int pca9532_calcpwm(struct i2c_client *client, int pwm, int blink,
{
int a = 0, b = 0, i = 0;
struct pca9532_data *data = i2c_get_clientdata(client);
- for (i = 0; i < 16; i++) {
+ for (i = 0; i < data->chip_info->num_leds; i++) {
if (data->leds[i].type == PCA9532_TYPE_LED &&
data->leds[i].state == PCA9532_PWM0+pwm) {
a++;
@@ -92,10 +124,12 @@ static int pca9532_calcpwm(struct i2c_client *client, int pwm, int blink,
static int pca9532_setpwm(struct i2c_client *client, int pwm)
{
struct pca9532_data *data = i2c_get_clientdata(client);
+ u8 maxleds = data->chip_info->num_leds;
+
mutex_lock(&data->update_lock);
- i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(pwm),
+ i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(maxleds, pwm),
data->pwm[pwm]);
- i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(pwm),
+ i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(maxleds, pwm),
data->psc[pwm]);
mutex_unlock(&data->update_lock);
return 0;
@@ -106,15 +140,16 @@ static void pca9532_setled(struct pca9532_led *led)
{
struct i2c_client *client = led->client;
struct pca9532_data *data = i2c_get_clientdata(client);
+ u8 maxleds = data->chip_info->num_leds;
char reg;

mutex_lock(&data->update_lock);
- reg = i2c_smbus_read_byte_data(client, LED_REG(led->id));
+ reg = i2c_smbus_read_byte_data(client, LED_REG(maxleds, led->id));
/* zero led bits */
reg = reg & ~(0x3<<LED_NUM(led->id)*2);
/* set the new value */
reg = reg | (led->state << LED_NUM(led->id)*2);
- i2c_smbus_write_byte_data(client, LED_REG(led->id), reg);
+ i2c_smbus_write_byte_data(client, LED_REG(maxleds, led->id), reg);
mutex_unlock(&data->update_lock);
}

@@ -183,10 +218,12 @@ static int pca9532_event(struct input_dev *dev, unsigned int type,

static void pca9532_input_work(struct work_struct *work)
{
- struct pca9532_data *data;
- data = container_of(work, struct pca9532_data, work);
+ struct pca9532_data *data =
+ container_of(work, struct pca9532_data, work);
+ u8 maxleds = data->chip_info->num_leds;
+
mutex_lock(&data->update_lock);
- i2c_smbus_write_byte_data(data->client, PCA9532_REG_PWM(1),
+ i2c_smbus_write_byte_data(data->client, PCA9532_REG_PWM(maxleds, 1),
data->pwm[1]);
mutex_unlock(&data->update_lock);
}
@@ -230,17 +267,18 @@ static int pca9532_configure(struct i2c_client *client,
struct pca9532_data *data, struct pca9532_platform_data *pdata)
{
int i, err = 0;
+ u8 maxleds = data->chip_info->num_leds;

for (i = 0; i < 2; i++) {
data->pwm[i] = pdata->pwm[i];
data->psc[i] = pdata->psc[i];
- i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(i),
+ i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(maxleds, i),
data->pwm[i]);
- i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(i),
+ i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(maxleds, i),
data->psc[i]);
}

- for (i = 0; i < 16; i++) {
+ for (i = 0; i < data->chip_info->num_leds; i++) {
struct pca9532_led *led = &data->leds[i];
struct pca9532_led *pled = &pdata->leds[i];
led->client = client;
@@ -322,6 +360,8 @@ static int pca9532_probe(struct i2c_client *client,
if (!data)
return -ENOMEM;

+ data->chip_info = &pca9532_chip_info_tbl[id->driver_data];
+
dev_info(&client->dev, "setting platform data\n");
i2c_set_clientdata(client, data);
data->client = client;
@@ -337,7 +377,7 @@ static int pca9532_probe(struct i2c_client *client,
static int pca9532_remove(struct i2c_client *client)
{
struct pca9532_data *data = i2c_get_clientdata(client);
- pca9532_destroy_devices(data, 16);
+ pca9532_destroy_devices(data, data->chip_info->num_leds);
kfree(data);
return 0;
}
--
1.7.0.4


2011-04-15 13:41:34

by Jan Weitzel

[permalink] [raw]
Subject: Re: [PATCH] leds: leds-pca9532 add support pca9530, pca9531 and pca9533

Am Dienstag, den 05.04.2011, 10:27 +0200 schrieb Jan Weitzel:
> Adding chipinfo to use driver with pca953x family
> Rename driver to pca953x, but left files and platformflags named pca9532
>
> Tested with pca9530 and pca9533
>
> Tested-by: Juergen Kilb <[email protected]>
> Signed-off-by: Jan Weitzel <[email protected]>
> ---
> drivers/leds/leds-pca9532.c | 80 ++++++++++++++++++++++++++++++++-----------

Is there any rework needed?

Regards, Jan

2011-04-19 23:15:41

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH] leds: leds-pca9532 add support pca9530, pca9531 and pca9533

On Tue, 5 Apr 2011 10:27:23 +0200
Jan Weitzel <[email protected]> wrote:

> Adding chipinfo to use driver with pca953x family
> Rename driver to pca953x, but left files and platformflags named pca9532
>
> Tested with pca9530 and pca9533

The patch generates rather a mess when merged on top of
drivers-leds-leds-pca9532c-add-gpio-capability.patch, below.

I suspect the changes in your patch also need to be made to the code
which drivers-leds-leds-pca9532c-add-gpio-capability.patch added. So
could you please review the below change and redo and retest your patch
on top of it?

Thanks.


From: Joachim Eastwood <[email protected]>

Allow unused leds on pca9532 to be used as gpio. The board I am working
on now has no less than 6 pca9532 chips. One chips is used for only leds,
one has 14 leds and 2 gpio and the rest of the chips are gpio only.

There is also one board in mainline which could use this capabilty;
arch/arm/mach-iop32x/n2100.c
232 { .type = PCA9532_TYPE_NONE }, /* power OFF gpio */
233 { .type = PCA9532_TYPE_NONE }, /* reset gpio */

This patch defines a new pin type, PCA9532_TYPE_GPIO, and registers a
gpiochip if any pin has this type set. The gpio will registers all chip
pins but will filter on gpio_request.

[[email protected]: fix build when GPIOLIB is not enabled]
Signed-off-by: Joachim Eastwood <[email protected]>
Reviewed-by: Wolfram Sang <[email protected]>
Reviewed-by: H Hartley Sweeten <[email protected]>
Cc: Richard Purdie <[email protected]>
Cc: Grant Likely <[email protected]>
Signed-off-by: Randy Dunlap <[email protected]>
Cc: Jan Weitzel <[email protected]>
Cc: Juergen Kilb <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
---

drivers/leds/Kconfig | 10 ++
drivers/leds/leds-pca9532.c | 113 ++++++++++++++++++++++++++++++++-
include/linux/leds-pca9532.h | 3
3 files changed, 122 insertions(+), 4 deletions(-)

diff -puN drivers/leds/Kconfig~drivers-leds-leds-pca9532c-add-gpio-capability drivers/leds/Kconfig
--- a/drivers/leds/Kconfig~drivers-leds-leds-pca9532c-add-gpio-capability
+++ a/drivers/leds/Kconfig
@@ -154,6 +154,16 @@ config LEDS_PCA9532
LED controller. It is generally only useful
as a platform driver

+config LEDS_PCA9532_GPIO
+ bool "Enable GPIO support for PCA9532"
+ depends on LEDS_PCA9532
+ depends on GPIOLIB
+ help
+ Allow unused pins on PCA9532 to be used as gpio.
+
+ To use a pin as gpio pca9532_type in pca9532_platform data needs to
+ set to PCA9532_TYPE_GPIO.
+
config LEDS_GPIO
tristate "LED Support for GPIO connected LEDs"
depends on LEDS_CLASS
diff -puN drivers/leds/leds-pca9532.c~drivers-leds-leds-pca9532c-add-gpio-capability drivers/leds/leds-pca9532.c
--- a/drivers/leds/leds-pca9532.c~drivers-leds-leds-pca9532c-add-gpio-capability
+++ a/drivers/leds/leds-pca9532.c
@@ -19,7 +19,9 @@
#include <linux/mutex.h>
#include <linux/workqueue.h>
#include <linux/leds-pca9532.h>
+#include <linux/gpio.h>

+#define PCA9532_REG_INPUT(i) ((i)/8)
#define PCA9532_REG_PSC(i) (0x2+(i)*2)
#define PCA9532_REG_PWM(i) (0x3+(i)*2)
#define PCA9532_REG_LS0 0x6
@@ -34,6 +36,9 @@ struct pca9532_data {
struct mutex update_lock;
struct input_dev *idev;
struct work_struct work;
+#ifdef CONFIG_LEDS_PCA9532_GPIO
+ struct gpio_chip gpio;
+#endif
u8 pwm[2];
u8 psc[2];
};
@@ -200,16 +205,68 @@ static void pca9532_led_work(struct work
pca9532_setled(led);
}

-static void pca9532_destroy_devices(struct pca9532_data *data, int n_devs)
+#ifdef CONFIG_LEDS_PCA9532_GPIO
+static int pca9532_gpio_request_pin(struct gpio_chip *gc, unsigned offset)
+{
+ struct pca9532_data *data = container_of(gc, struct pca9532_data, gpio);
+ struct pca9532_led *led = &data->leds[offset];
+
+ if (led->type == PCA9532_TYPE_GPIO)
+ return 0;
+
+ return -EBUSY;
+}
+
+static void pca9532_gpio_set_value(struct gpio_chip *gc, unsigned offset, int val)
+{
+ struct pca9532_data *data = container_of(gc, struct pca9532_data, gpio);
+ struct pca9532_led *led = &data->leds[offset];
+
+ if (val)
+ led->state = PCA9532_ON;
+ else
+ led->state = PCA9532_OFF;
+
+ pca9532_setled(led);
+}
+
+static int pca9532_gpio_get_value(struct gpio_chip *gc, unsigned offset)
+{
+ struct pca9532_data *data = container_of(gc, struct pca9532_data, gpio);
+ unsigned char reg;
+
+ reg = i2c_smbus_read_byte_data(data->client, PCA9532_REG_INPUT(offset));
+
+ return !!(reg & (1 << (offset % 8)));
+}
+
+static int pca9532_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
+{
+ /* To use as input ensure pin is not driven */
+ pca9532_gpio_set_value(gc, offset, 0);
+
+ return 0;
+}
+
+static int pca9532_gpio_direction_output(struct gpio_chip *gc, unsigned offset, int val)
+{
+ pca9532_gpio_set_value(gc, offset, val);
+
+ return 0;
+}
+#endif /* CONFIG_LEDS_PCA9532_GPIO */
+
+static int pca9532_destroy_devices(struct pca9532_data *data, int n_devs)
{
int i = n_devs;

if (!data)
- return;
+ return -EINVAL;

while (--i >= 0) {
switch (data->leds[i].type) {
case PCA9532_TYPE_NONE:
+ case PCA9532_TYPE_GPIO:
break;
case PCA9532_TYPE_LED:
led_classdev_unregister(&data->leds[i].ldev);
@@ -224,12 +281,26 @@ static void pca9532_destroy_devices(stru
break;
}
}
+
+#ifdef CONFIG_LEDS_PCA9532_GPIO
+ if (data->gpio.dev) {
+ int err = gpiochip_remove(&data->gpio);
+ if (err) {
+ dev_err(&data->client->dev, "%s failed, %d\n",
+ "gpiochip_remove()", err);
+ return err;
+ }
+ }
+#endif
+
+ return 0;
}

static int pca9532_configure(struct i2c_client *client,
struct pca9532_data *data, struct pca9532_platform_data *pdata)
{
int i, err = 0;
+ int gpios = 0;

for (i = 0; i < 2; i++) {
data->pwm[i] = pdata->pwm[i];
@@ -249,6 +320,9 @@ static int pca9532_configure(struct i2c_
switch (led->type) {
case PCA9532_TYPE_NONE:
break;
+ case PCA9532_TYPE_GPIO:
+ gpios++;
+ break;
case PCA9532_TYPE_LED:
led->state = pled->state;
led->name = pled->name;
@@ -297,6 +371,34 @@ static int pca9532_configure(struct i2c_
break;
}
}
+
+#ifdef CONFIG_LEDS_PCA9532_GPIO
+ if (gpios) {
+ data->gpio.label = "gpio-pca9532";
+ data->gpio.direction_input = pca9532_gpio_direction_input;
+ data->gpio.direction_output = pca9532_gpio_direction_output;
+ data->gpio.set = pca9532_gpio_set_value;
+ data->gpio.get = pca9532_gpio_get_value;
+ data->gpio.request = pca9532_gpio_request_pin;
+ data->gpio.can_sleep = 1;
+ data->gpio.base = pdata->gpio_base;
+ data->gpio.ngpio = 16;
+ data->gpio.dev = &client->dev;
+ data->gpio.owner = THIS_MODULE;
+
+ err = gpiochip_add(&data->gpio);
+ if (err) {
+ /* Use data->gpio.dev as a flag for freeing gpiochip */
+ data->gpio.dev = NULL;
+ dev_warn(&client->dev, "could not add gpiochip\n");
+ } else {
+ dev_info(&client->dev, "gpios %i...%i\n",
+ data->gpio.base, data->gpio.base +
+ data->gpio.ngpio - 1);
+ }
+ }
+#endif
+
return 0;

exit:
@@ -337,7 +439,12 @@ static int pca9532_probe(struct i2c_clie
static int pca9532_remove(struct i2c_client *client)
{
struct pca9532_data *data = i2c_get_clientdata(client);
- pca9532_destroy_devices(data, 16);
+ int err;
+
+ err = pca9532_destroy_devices(data, 16);
+ if (err)
+ return err;
+
kfree(data);
return 0;
}
diff -puN include/linux/leds-pca9532.h~drivers-leds-leds-pca9532c-add-gpio-capability include/linux/leds-pca9532.h
--- a/include/linux/leds-pca9532.h~drivers-leds-leds-pca9532c-add-gpio-capability
+++ a/include/linux/leds-pca9532.h
@@ -25,7 +25,7 @@ enum pca9532_state {
};

enum pca9532_type { PCA9532_TYPE_NONE, PCA9532_TYPE_LED,
- PCA9532_TYPE_N2100_BEEP };
+ PCA9532_TYPE_N2100_BEEP, PCA9532_TYPE_GPIO };

struct pca9532_led {
u8 id;
@@ -41,6 +41,7 @@ struct pca9532_platform_data {
struct pca9532_led leds[16];
u8 pwm[2];
u8 psc[2];
+ int gpio_base;
};

#endif /* __LINUX_PCA9532_H */
_

2011-04-27 11:27:10

by Jan Weitzel

[permalink] [raw]
Subject: [PATCH v2] leds: leds-pca9532 add support pca9530, pca9531 and pca9533

pca953x family are only different in number of leds and register layout
Adding chipinfo to use driver with whole pca953x family
Rename driver to pca953x, but left files and platformflags named pca9532

Tested with pca9530 and pca9533

Tested-by: Juergen Kilb <[email protected]>
Signed-off-by: Jan Weitzel <[email protected]>
---
v2: works on top off "pca9532c add gpio capability" patch

drivers/leds/leds-pca9532.c | 84 +++++++++++++++++++++++++++++++-----------
1 files changed, 62 insertions(+), 22 deletions(-)

diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c
index ebea856..d8d3a1e 100644
--- a/drivers/leds/leds-pca9532.c
+++ b/drivers/leds/leds-pca9532.c
@@ -1,13 +1,14 @@
/*
* pca9532.c - 16-bit Led dimmer
*
+ * Copyright (C) 2011 Jan Weitzel
* Copyright (C) 2008 Riku Voipio
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
- * Datasheet: http://www.nxp.com/acrobat/datasheets/PCA9532_3.pdf
+ * Datasheet: http://www.nxp.com/documents/data_sheet/PCA9532.pdf
*
*/

@@ -21,15 +22,20 @@
#include <linux/leds-pca9532.h>
#include <linux/gpio.h>

-#define PCA9532_REG_INPUT(i) ((i)/8)
-#define PCA9532_REG_PSC(i) (0x2+(i)*2)
-#define PCA9532_REG_PWM(i) (0x3+(i)*2)
-#define PCA9532_REG_LS0 0x6
-#define LED_REG(led) ((led>>2)+PCA9532_REG_LS0)
-#define LED_NUM(led) (led & 0x3)
+/* m = num_leds*/
+#define PCA9532_REG_INPUT(i) ((i) >> 3)
+#define PCA9532_REG_OFFSET(m) ((m) >> 4)
+#define PCA9532_REG_PSC(m, i) (PCA9532_REG_OFFSET(m) + 0x1 + (i) * 2)
+#define PCA9532_REG_PWM(m, i) (PCA9532_REG_OFFSET(m) + 0x2 + (i) * 2)
+#define LED_REG(m, led) (PCA9532_REG_OFFSET(m) + 0x5 + (led >> 2))
+#define LED_NUM(led) (led & 0x3)

#define ldev_to_led(c) container_of(c, struct pca9532_led, ldev)

+struct pca9532_chip_info {
+ u8 num_leds;
+};
+
struct pca9532_data {
struct i2c_client *client;
struct pca9532_led leds[16];
@@ -39,6 +45,7 @@ struct pca9532_data {
#ifdef CONFIG_LEDS_PCA9532_GPIO
struct gpio_chip gpio;
#endif
+ const struct pca9532_chip_info *chip_info;
u8 pwm[2];
u8 psc[2];
};
@@ -47,16 +54,41 @@ static int pca9532_probe(struct i2c_client *client,
const struct i2c_device_id *id);
static int pca9532_remove(struct i2c_client *client);

+enum {
+ pca9530,
+ pca9531,
+ pca9532,
+ pca9533,
+};
+
static const struct i2c_device_id pca9532_id[] = {
- { "pca9532", 0 },
+ { "pca9530", pca9530 },
+ { "pca9531", pca9531 },
+ { "pca9532", pca9532 },
+ { "pca9533", pca9533 },
{ }
};

MODULE_DEVICE_TABLE(i2c, pca9532_id);

+static const struct pca9532_chip_info pca9532_chip_info_tbl[] = {
+ [pca9530] = {
+ .num_leds = 2,
+ },
+ [pca9531] = {
+ .num_leds = 8,
+ },
+ [pca9532] = {
+ .num_leds = 16,
+ },
+ [pca9533] = {
+ .num_leds = 4,
+ },
+};
+
static struct i2c_driver pca9532_driver = {
.driver = {
- .name = "pca9532",
+ .name = "pca953x",
},
.probe = pca9532_probe,
.remove = pca9532_remove,
@@ -73,7 +105,7 @@ static int pca9532_calcpwm(struct i2c_client *client, int pwm, int blink,
{
int a = 0, b = 0, i = 0;
struct pca9532_data *data = i2c_get_clientdata(client);
- for (i = 0; i < 16; i++) {
+ for (i = 0; i < data->chip_info->num_leds; i++) {
if (data->leds[i].type == PCA9532_TYPE_LED &&
data->leds[i].state == PCA9532_PWM0+pwm) {
a++;
@@ -97,10 +129,12 @@ static int pca9532_calcpwm(struct i2c_client *client, int pwm, int blink,
static int pca9532_setpwm(struct i2c_client *client, int pwm)
{
struct pca9532_data *data = i2c_get_clientdata(client);
+ u8 maxleds = data->chip_info->num_leds;
+
mutex_lock(&data->update_lock);
- i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(pwm),
+ i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(maxleds, pwm),
data->pwm[pwm]);
- i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(pwm),
+ i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(maxleds, pwm),
data->psc[pwm]);
mutex_unlock(&data->update_lock);
return 0;
@@ -111,15 +145,16 @@ static void pca9532_setled(struct pca9532_led *led)
{
struct i2c_client *client = led->client;
struct pca9532_data *data = i2c_get_clientdata(client);
+ u8 maxleds = data->chip_info->num_leds;
char reg;

mutex_lock(&data->update_lock);
- reg = i2c_smbus_read_byte_data(client, LED_REG(led->id));
+ reg = i2c_smbus_read_byte_data(client, LED_REG(maxleds, led->id));
/* zero led bits */
reg = reg & ~(0x3<<LED_NUM(led->id)*2);
/* set the new value */
reg = reg | (led->state << LED_NUM(led->id)*2);
- i2c_smbus_write_byte_data(client, LED_REG(led->id), reg);
+ i2c_smbus_write_byte_data(client, LED_REG(maxleds, led->id), reg);
mutex_unlock(&data->update_lock);
}

@@ -188,10 +223,12 @@ static int pca9532_event(struct input_dev *dev, unsigned int type,

static void pca9532_input_work(struct work_struct *work)
{
- struct pca9532_data *data;
- data = container_of(work, struct pca9532_data, work);
+ struct pca9532_data *data =
+ container_of(work, struct pca9532_data, work);
+ u8 maxleds = data->chip_info->num_leds;
+
mutex_lock(&data->update_lock);
- i2c_smbus_write_byte_data(data->client, PCA9532_REG_PWM(1),
+ i2c_smbus_write_byte_data(data->client, PCA9532_REG_PWM(maxleds, 1),
data->pwm[1]);
mutex_unlock(&data->update_lock);
}
@@ -301,17 +338,18 @@ static int pca9532_configure(struct i2c_client *client,
{
int i, err = 0;
int gpios = 0;
+ u8 maxleds = data->chip_info->num_leds;

for (i = 0; i < 2; i++) {
data->pwm[i] = pdata->pwm[i];
data->psc[i] = pdata->psc[i];
- i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(i),
+ i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(maxleds, i),
data->pwm[i]);
- i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(i),
+ i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(maxleds, i),
data->psc[i]);
}

- for (i = 0; i < 16; i++) {
+ for (i = 0; i < data->chip_info->num_leds; i++) {
struct pca9532_led *led = &data->leds[i];
struct pca9532_led *pled = &pdata->leds[i];
led->client = client;
@@ -382,7 +420,7 @@ static int pca9532_configure(struct i2c_client *client,
data->gpio.request = pca9532_gpio_request_pin;
data->gpio.can_sleep = 1;
data->gpio.base = pdata->gpio_base;
- data->gpio.ngpio = 16;
+ data->gpio.ngpio = data->chip_info->num_leds;
data->gpio.dev = &client->dev;
data->gpio.owner = THIS_MODULE;

@@ -424,6 +462,8 @@ static int pca9532_probe(struct i2c_client *client,
if (!data)
return -ENOMEM;

+ data->chip_info = &pca9532_chip_info_tbl[id->driver_data];
+
dev_info(&client->dev, "setting platform data\n");
i2c_set_clientdata(client, data);
data->client = client;
@@ -441,7 +481,7 @@ static int pca9532_remove(struct i2c_client *client)
struct pca9532_data *data = i2c_get_clientdata(client);
int err;

- err = pca9532_destroy_devices(data, 16);
+ err = pca9532_destroy_devices(data, data->chip_info->num_leds);
if (err)
return err;

--
1.7.0.4

2011-04-27 17:14:30

by Joachim Eastwood

[permalink] [raw]
Subject: Re: [PATCH v2] leds: leds-pca9532 add support pca9530, pca9531 and pca9533

Hello Jan,

On 4/27/11, Jan Weitzel <[email protected]> wrote:
> pca953x family are only different in number of leds and register layout
> Adding chipinfo to use driver with whole pca953x family
> Rename driver to pca953x, but left files and platformflags named pca9532
>
> Tested with pca9530 and pca9533
>
> Tested-by: Juergen Kilb <[email protected]>
> Signed-off-by: Jan Weitzel <[email protected]>
> ---
> v2: works on top off "pca9532c add gpio capability" patch

I did a short test run with 39-rc5 plus all the pca9532 patches,
including this one, and it works with PCA9532 on my custom board.
Tested both LED and GPIO functionality.

Acked-and-tested-by: Joachim Eastwood <[email protected]>

regards
Joachim Eastwood

2011-05-16 08:49:54

by Jan Weitzel

[permalink] [raw]
Subject: [PATCH v3] leds: leds-pca9532 add support pca9530, pca9531 and pca9533

pca953x family are only different in number of leds and register layout
Adding chipinfo to use driver with whole pca953x family
Rename driver to pca953x, but left files and platformflags named pca9532

Tested with pca9530 and pca9533

Tested-by: Juergen Kilb <[email protected]>
Signed-off-by: Jan Weitzel <[email protected]>
Acked-and-tested-by: Joachim Eastwood <[email protected]>
---
v3: Acked-and-tested-by
v2: works on top off "pca9532c add gpio capability" patch

drivers/leds/leds-pca9532.c | 84 +++++++++++++++++++++++++++++++-----------
1 files changed, 62 insertions(+), 22 deletions(-)

diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c
index ebea856..d8d3a1e 100644
--- a/drivers/leds/leds-pca9532.c
+++ b/drivers/leds/leds-pca9532.c
@@ -1,13 +1,14 @@
/*
* pca9532.c - 16-bit Led dimmer
*
+ * Copyright (C) 2011 Jan Weitzel
* Copyright (C) 2008 Riku Voipio
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
- * Datasheet: http://www.nxp.com/acrobat/datasheets/PCA9532_3.pdf
+ * Datasheet: http://www.nxp.com/documents/data_sheet/PCA9532.pdf
*
*/

@@ -21,15 +22,20 @@
#include <linux/leds-pca9532.h>
#include <linux/gpio.h>

-#define PCA9532_REG_INPUT(i) ((i)/8)
-#define PCA9532_REG_PSC(i) (0x2+(i)*2)
-#define PCA9532_REG_PWM(i) (0x3+(i)*2)
-#define PCA9532_REG_LS0 0x6
-#define LED_REG(led) ((led>>2)+PCA9532_REG_LS0)
-#define LED_NUM(led) (led & 0x3)
+/* m = num_leds*/
+#define PCA9532_REG_INPUT(i) ((i) >> 3)
+#define PCA9532_REG_OFFSET(m) ((m) >> 4)
+#define PCA9532_REG_PSC(m, i) (PCA9532_REG_OFFSET(m) + 0x1 + (i) * 2)
+#define PCA9532_REG_PWM(m, i) (PCA9532_REG_OFFSET(m) + 0x2 + (i) * 2)
+#define LED_REG(m, led) (PCA9532_REG_OFFSET(m) + 0x5 + (led >> 2))
+#define LED_NUM(led) (led & 0x3)

#define ldev_to_led(c) container_of(c, struct pca9532_led, ldev)

+struct pca9532_chip_info {
+ u8 num_leds;
+};
+
struct pca9532_data {
struct i2c_client *client;
struct pca9532_led leds[16];
@@ -39,6 +45,7 @@ struct pca9532_data {
#ifdef CONFIG_LEDS_PCA9532_GPIO
struct gpio_chip gpio;
#endif
+ const struct pca9532_chip_info *chip_info;
u8 pwm[2];
u8 psc[2];
};
@@ -47,16 +54,41 @@ static int pca9532_probe(struct i2c_client *client,
const struct i2c_device_id *id);
static int pca9532_remove(struct i2c_client *client);

+enum {
+ pca9530,
+ pca9531,
+ pca9532,
+ pca9533,
+};
+
static const struct i2c_device_id pca9532_id[] = {
- { "pca9532", 0 },
+ { "pca9530", pca9530 },
+ { "pca9531", pca9531 },
+ { "pca9532", pca9532 },
+ { "pca9533", pca9533 },
{ }
};

MODULE_DEVICE_TABLE(i2c, pca9532_id);

+static const struct pca9532_chip_info pca9532_chip_info_tbl[] = {
+ [pca9530] = {
+ .num_leds = 2,
+ },
+ [pca9531] = {
+ .num_leds = 8,
+ },
+ [pca9532] = {
+ .num_leds = 16,
+ },
+ [pca9533] = {
+ .num_leds = 4,
+ },
+};
+
static struct i2c_driver pca9532_driver = {
.driver = {
- .name = "pca9532",
+ .name = "pca953x",
},
.probe = pca9532_probe,
.remove = pca9532_remove,
@@ -73,7 +105,7 @@ static int pca9532_calcpwm(struct i2c_client *client, int pwm, int blink,
{
int a = 0, b = 0, i = 0;
struct pca9532_data *data = i2c_get_clientdata(client);
- for (i = 0; i < 16; i++) {
+ for (i = 0; i < data->chip_info->num_leds; i++) {
if (data->leds[i].type == PCA9532_TYPE_LED &&
data->leds[i].state == PCA9532_PWM0+pwm) {
a++;
@@ -97,10 +129,12 @@ static int pca9532_calcpwm(struct i2c_client *client, int pwm, int blink,
static int pca9532_setpwm(struct i2c_client *client, int pwm)
{
struct pca9532_data *data = i2c_get_clientdata(client);
+ u8 maxleds = data->chip_info->num_leds;
+
mutex_lock(&data->update_lock);
- i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(pwm),
+ i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(maxleds, pwm),
data->pwm[pwm]);
- i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(pwm),
+ i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(maxleds, pwm),
data->psc[pwm]);
mutex_unlock(&data->update_lock);
return 0;
@@ -111,15 +145,16 @@ static void pca9532_setled(struct pca9532_led *led)
{
struct i2c_client *client = led->client;
struct pca9532_data *data = i2c_get_clientdata(client);
+ u8 maxleds = data->chip_info->num_leds;
char reg;

mutex_lock(&data->update_lock);
- reg = i2c_smbus_read_byte_data(client, LED_REG(led->id));
+ reg = i2c_smbus_read_byte_data(client, LED_REG(maxleds, led->id));
/* zero led bits */
reg = reg & ~(0x3<<LED_NUM(led->id)*2);
/* set the new value */
reg = reg | (led->state << LED_NUM(led->id)*2);
- i2c_smbus_write_byte_data(client, LED_REG(led->id), reg);
+ i2c_smbus_write_byte_data(client, LED_REG(maxleds, led->id), reg);
mutex_unlock(&data->update_lock);
}

@@ -188,10 +223,12 @@ static int pca9532_event(struct input_dev *dev, unsigned int type,

static void pca9532_input_work(struct work_struct *work)
{
- struct pca9532_data *data;
- data = container_of(work, struct pca9532_data, work);
+ struct pca9532_data *data =
+ container_of(work, struct pca9532_data, work);
+ u8 maxleds = data->chip_info->num_leds;
+
mutex_lock(&data->update_lock);
- i2c_smbus_write_byte_data(data->client, PCA9532_REG_PWM(1),
+ i2c_smbus_write_byte_data(data->client, PCA9532_REG_PWM(maxleds, 1),
data->pwm[1]);
mutex_unlock(&data->update_lock);
}
@@ -301,17 +338,18 @@ static int pca9532_configure(struct i2c_client *client,
{
int i, err = 0;
int gpios = 0;
+ u8 maxleds = data->chip_info->num_leds;

for (i = 0; i < 2; i++) {
data->pwm[i] = pdata->pwm[i];
data->psc[i] = pdata->psc[i];
- i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(i),
+ i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(maxleds, i),
data->pwm[i]);
- i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(i),
+ i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(maxleds, i),
data->psc[i]);
}

- for (i = 0; i < 16; i++) {
+ for (i = 0; i < data->chip_info->num_leds; i++) {
struct pca9532_led *led = &data->leds[i];
struct pca9532_led *pled = &pdata->leds[i];
led->client = client;
@@ -382,7 +420,7 @@ static int pca9532_configure(struct i2c_client *client,
data->gpio.request = pca9532_gpio_request_pin;
data->gpio.can_sleep = 1;
data->gpio.base = pdata->gpio_base;
- data->gpio.ngpio = 16;
+ data->gpio.ngpio = data->chip_info->num_leds;
data->gpio.dev = &client->dev;
data->gpio.owner = THIS_MODULE;

@@ -424,6 +462,8 @@ static int pca9532_probe(struct i2c_client *client,
if (!data)
return -ENOMEM;

+ data->chip_info = &pca9532_chip_info_tbl[id->driver_data];
+
dev_info(&client->dev, "setting platform data\n");
i2c_set_clientdata(client, data);
data->client = client;
@@ -441,7 +481,7 @@ static int pca9532_remove(struct i2c_client *client)
struct pca9532_data *data = i2c_get_clientdata(client);
int err;

- err = pca9532_destroy_devices(data, 16);
+ err = pca9532_destroy_devices(data, data->chip_info->num_leds);
if (err)
return err;

--
1.7.0.4