1 From f6b1f49300a24c329f5d1049031f711c6268be95 Mon Sep 17 00:00:00 2001
2 From: Matt Hsu <matt_hsu@openmoko.org>
3 Date: Thu, 18 Sep 2008 11:14:50 +0100
4 Subject: [PATCH] gta03-pca9632.patch
6 - pca9632 is a LED driver which will be adopted in gta03.
8 Signed-off-by: Matt Hsu <matt_hsu@openmoko.org>
10 drivers/i2c/chips/Kconfig | 9 +
11 drivers/i2c/chips/Makefile | 1 +
12 drivers/i2c/chips/pca9632.c | 551 +++++++++++++++++++++++++++++++++++++++++++
13 drivers/i2c/chips/pca9632.h | 24 ++
14 include/linux/i2c-id.h | 1 +
15 5 files changed, 586 insertions(+), 0 deletions(-)
16 create mode 100644 drivers/i2c/chips/pca9632.c
17 create mode 100644 drivers/i2c/chips/pca9632.h
19 diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
20 index 1d6d36f..9af3c62 100644
21 --- a/drivers/i2c/chips/Kconfig
22 +++ b/drivers/i2c/chips/Kconfig
23 @@ -193,4 +193,13 @@ config SENSORS_TSL256X
24 This driver can also be built as a module. If so, the module
25 will be called tsl256x.
28 + tristate "Philips/NXP PCA9632 low power LED driver"
31 + If you say yes here you get support for the Philips/NXP PCA9632
34 + This driver can also be built as a module. If so, the module
35 + will be called pca9632.
37 diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile
38 index 4b6ba04..67cd1e5 100644
39 --- a/drivers/i2c/chips/Makefile
40 +++ b/drivers/i2c/chips/Makefile
41 @@ -18,6 +18,7 @@ obj-$(CONFIG_TPS65010) += tps65010.o
42 obj-$(CONFIG_MENELAUS) += menelaus.o
43 obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
44 obj-$(CONFIG_SENSORS_TSL256X) += tsl256x.o
45 +obj-$(CONFIG_PCA9632) += pca9632.o
47 ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
48 EXTRA_CFLAGS += -DDEBUG
49 diff --git a/drivers/i2c/chips/pca9632.c b/drivers/i2c/chips/pca9632.c
51 index 0000000..deda6a1
53 +++ b/drivers/i2c/chips/pca9632.c
56 + * Philips/NXP PCA9632 low power LED driver.
57 + * Copyright (C) 2008 Matt Hsu <matt_hsu@openmoko.org>
59 + * low_level implementation are based on pcf50606 driver
61 + * This program is free software; you can redistribute it and/or modify
62 + * it under the terms of the GNU General Public License as published by
63 + * the Free Software Foundation; version 2 of the License.
66 + * - attach ledclass??
67 + * - add platform data
71 +#include <linux/module.h>
72 +#include <linux/init.h>
73 +#include <linux/i2c.h>
74 +#include <linux/platform_device.h>
78 +/* Addresses to scan */
79 +static unsigned short normal_i2c[] = { 0x62, I2C_CLIENT_END };
81 +/* Insmod parameters */
82 +I2C_CLIENT_INSMOD_1(pca9632);
84 +enum pca9632_pwr_state {
89 +enum pca9632_led_output {
92 + PCA9632_CTRL_BY_PWM,
93 + PCA9632_CTRL_BY_PWM_GRPPWM,
96 +static const char *led_output_name[] = {
97 + [PCA9632_OFF] = "off",
98 + [PCA9632_ON] = "fully-on",
99 + [PCA9632_CTRL_BY_PWM] = "ctrl-by-pwm",
100 + [PCA9632_CTRL_BY_PWM_GRPPWM] = "ctrl-by-pwm-grppwm",
103 +struct pca9632_data {
104 + struct i2c_client client;
108 +static struct i2c_driver pca9632_driver;
109 +static struct platform_device *pca9632_pdev;
111 +static int pca9632_attach_adapter(struct i2c_adapter *adapter);
112 +static int pca9632_detach_client(struct i2c_client *client);
114 +static int __reg_write(struct pca9632_data *pca, u_int8_t reg, u_int8_t val)
116 + return i2c_smbus_write_byte_data(&pca->client, reg, val);
119 +static int reg_write(struct pca9632_data *pca, u_int8_t reg, u_int8_t val)
123 + mutex_lock(&pca->lock);
124 + ret = __reg_write(pca, reg, val);
125 + mutex_unlock(&pca->lock);
130 +static int32_t __reg_read(struct pca9632_data *pca, u_int8_t reg)
134 + ret = i2c_smbus_read_byte_data(&pca->client, reg);
139 +static u_int8_t reg_read(struct pca9632_data *pca, u_int8_t reg)
143 + mutex_lock(&pca->lock);
144 + ret = __reg_read(pca, reg);
145 + mutex_unlock(&pca->lock);
150 +static int reg_set_bit_mask(struct pca9632_data *pca,
151 + u_int8_t reg, u_int8_t mask, u_int8_t val)
158 + mutex_lock(&pca->lock);
160 + tmp = __reg_read(pca, reg);
163 + ret = __reg_write(pca, reg, tmp);
165 + mutex_unlock(&pca->lock);
170 +static inline int calc_dc(uint8_t idc)
172 + return (idc * 100) / 256;
178 +static int software_rst(struct i2c_adapter *adapter)
180 + u8 buf[] = { 0xa5, 0x5a };
182 + struct i2c_msg msg[] = {
191 + return i2c_transfer(adapter, msg, 1);
195 + * Group dmblnk control
197 +static void config_group_dmblnk(struct pca9632_data *pca, int group_dmblnk_mode)
199 + reg_set_bit_mask(pca, PCA9632_REG_MODE2, 0x20,
200 + group_dmblnk_mode << PCA9632_DMBLNK_SHIFT);
203 +static int get_group_dmblnk(struct pca9632_data *pca)
205 + return reg_read(pca, PCA9632_REG_MODE2) >> PCA9632_DMBLNK_SHIFT;
208 +static ssize_t show_group_dmblnk(struct device *dev, struct device_attribute
211 + struct i2c_client *client = to_i2c_client(dev);
212 + struct pca9632_data *pca = i2c_get_clientdata(client);
214 + if (get_group_dmblnk(pca))
215 + return sprintf(buf, "blinking\n");
217 + return sprintf(buf, "dimming\n");
220 +static ssize_t set_group_dmblnk(struct device *dev, struct device_attribute
221 + *attr, const char *buf, size_t count)
223 + struct i2c_client *client = to_i2c_client(dev);
224 + struct pca9632_data *pca = i2c_get_clientdata(client);
225 + unsigned int mode = simple_strtoul(buf, NULL, 10);
228 + dev_info(&pca->client.dev, "blinking\n");
230 + dev_info(&pca->client.dev, "dimming\n");
232 + config_group_dmblnk(pca, mode);
237 +static DEVICE_ATTR(group_dmblnk, S_IRUGO | S_IWUSR, show_group_dmblnk,
240 +static int reg_id_by_name(const char *name)
244 + if (!strncmp(name, "led0", 4))
245 + reg_id = PCA9632_REG_PWM0;
246 + else if (!strncmp(name, "led1", 4))
247 + reg_id = PCA9632_REG_PWM1;
248 + else if (!strncmp(name, "led2", 4))
249 + reg_id = PCA9632_REG_PWM2;
250 + else if (!strncmp(name, "led3", 4))
251 + reg_id = PCA9632_REG_PWM3;
256 +static int get_led_output(struct pca9632_data *pca, int ldrx)
258 + u_int8_t led_state;
261 + led_state = reg_read(pca, PCA9632_REG_LEDOUT);
262 + led_state = (led_state >> (2 * ldrx)) & 0x03;
267 +static void config_led_output(struct pca9632_data *pca, int ldrx,
268 + enum pca9632_led_output led_output)
274 + mask = 0x03 << (2 * ldrx);
275 + tmp = reg_set_bit_mask(pca, PCA9632_REG_LEDOUT,
276 + mask, led_output << (2 * ldrx));
280 + * Individual brightness control
282 +static ssize_t show_brightness(struct device *dev, struct device_attribute
285 + struct i2c_client *client = to_i2c_client(dev);
286 + struct pca9632_data *pca = i2c_get_clientdata(client);
289 + ldrx = reg_id_by_name(attr->attr.name);
291 + switch (get_led_output(pca, ldrx)) {
295 + return sprintf(buf, "%s",
296 + led_output_name[get_led_output(pca, ldrx)]);
298 + case PCA9632_CTRL_BY_PWM:
299 + return sprintf(buf, "%d%% \n", calc_dc(reg_read(pca, ldrx)));
301 + case PCA9632_CTRL_BY_PWM_GRPPWM:
302 + /* check group dmblnk */
303 + if (get_group_dmblnk(pca))
304 + return sprintf(buf, "%d%% \n",
305 + calc_dc(reg_read(pca, ldrx)));
306 + return sprintf(buf, "%d%% \n",
307 + calc_dc((reg_read(pca, ldrx) & 0xfc)));
312 + return sprintf(buf, "invalid argument\n");
315 +static ssize_t set_brightness(struct device *dev, struct device_attribute *attr,
316 + const char *buf, size_t count)
318 + struct i2c_client *client = to_i2c_client(dev);
319 + struct pca9632_data *pca = i2c_get_clientdata(client);
320 + unsigned int pwm = simple_strtoul(buf, NULL, 10);
323 + ldrx = reg_id_by_name(attr->attr.name);
324 + reg_set_bit_mask(pca, ldrx, 0xff, pwm);
330 +DEVICE_ATTR(led0_pwm, S_IRUGO | S_IWUSR, show_brightness, set_brightness);
332 +DEVICE_ATTR(led1_pwm, S_IRUGO | S_IWUSR, show_brightness, set_brightness);
334 +DEVICE_ATTR(led2_pwm, S_IRUGO | S_IWUSR, show_brightness, set_brightness);
336 +DEVICE_ATTR(led3_pwm, S_IRUGO | S_IWUSR, show_brightness, set_brightness);
339 + * Group frequency control
341 +static ssize_t show_group_freq(struct device *dev, struct device_attribute
345 + struct i2c_client *client = to_i2c_client(dev);
346 + struct pca9632_data *pca = i2c_get_clientdata(client);
348 + period = ((reg_read(pca, PCA9632_REG_GRPFREQ) + 1) * 1000) / 24;
350 + return sprintf(buf, "%d ms\n", period);
353 +static ssize_t set_group_freq(struct device *dev, struct device_attribute *attr,
354 + const char *buf, size_t count)
356 + struct i2c_client *client = to_i2c_client(dev);
357 + struct pca9632_data *pca = i2c_get_clientdata(client);
359 + unsigned int freq = simple_strtoul(buf, NULL, 10);
360 + reg_write(pca, PCA9632_REG_GRPFREQ, freq);
365 +DEVICE_ATTR(group_freq, S_IRUGO | S_IWUSR, show_group_freq, set_group_freq);
368 + * Group duty cycle tonrol*
370 +static ssize_t show_group_dc(struct device *dev, struct device_attribute *attr,
373 + struct i2c_client *client = to_i2c_client(dev);
374 + struct pca9632_data *pca = i2c_get_clientdata(client);
376 + if (get_group_dmblnk(pca)) {
378 + if (reg_read(pca, PCA9632_REG_GRPFREQ) <= 0x03)
379 + return sprintf(buf, "%d%% \n",
380 + calc_dc(reg_read(pca, PCA9632_REG_GRPPWM) & 0xfc));
382 + return sprintf(buf, "%d%% \n", calc_dc(reg_read(pca,
383 + PCA9632_REG_GRPPWM)));
386 + return sprintf(buf, "%d%% \n", calc_dc(reg_read(pca,
387 + PCA9632_REG_GRPPWM) & 0xf0));
390 +static ssize_t set_group_dc(struct device *dev, struct device_attribute *attr,
391 + const char *buf, size_t count)
393 + struct i2c_client *client = to_i2c_client(dev);
394 + struct pca9632_data *pca = i2c_get_clientdata(client);
396 + unsigned int dc = simple_strtoul(buf, NULL, 10);
398 + reg_set_bit_mask(pca, PCA9632_REG_GRPPWM, 0xff, dc);
403 +static DEVICE_ATTR(group_dc, S_IRUGO | S_IWUSR, show_group_dc, set_group_dc);
406 + * LED driver output
408 +static ssize_t show_led_output(struct device *dev, struct device_attribute
411 + struct i2c_client *client = to_i2c_client(dev);
412 + struct pca9632_data *pca = i2c_get_clientdata(client);
415 + ldrx = reg_id_by_name(attr->attr.name);
417 + return sprintf(buf, "%s \n",
418 + led_output_name[get_led_output(pca, ldrx)]);
421 +static ssize_t set_led_output(struct device *dev, struct device_attribute *attr,
422 + const char *buf, size_t count)
424 + struct i2c_client *client = to_i2c_client(dev);
425 + struct pca9632_data *pca = i2c_get_clientdata(client);
426 + enum pca9632_led_output led_output;
429 + led_output = simple_strtoul(buf, NULL, 10);
430 + ldrx = reg_id_by_name(attr->attr.name);
431 + config_led_output(pca, ldrx, led_output);
437 +DEVICE_ATTR(led0_output, S_IRUGO | S_IWUSR, show_led_output, set_led_output);
439 +DEVICE_ATTR(led1_output, S_IRUGO | S_IWUSR, show_led_output, set_led_output);
441 +DEVICE_ATTR(led2_output, S_IRUGO | S_IWUSR, show_led_output, set_led_output);
443 +DEVICE_ATTR(led3_output, S_IRUGO | S_IWUSR, show_led_output, set_led_output);
445 +static struct attribute *pca_sysfs_entries[] = {
446 + &dev_attr_group_dmblnk.attr,
447 + &dev_attr_led0_pwm.attr,
448 + &dev_attr_led1_pwm.attr,
449 + &dev_attr_led2_pwm.attr,
450 + &dev_attr_led3_pwm.attr,
451 + &dev_attr_group_dc.attr,
452 + &dev_attr_group_freq.attr,
453 + &dev_attr_led0_output.attr,
454 + &dev_attr_led1_output.attr,
455 + &dev_attr_led2_output.attr,
456 + &dev_attr_led3_output.attr,
460 +static struct attribute_group pca_attr_group = {
461 + .name = NULL, /* put in device directory */
462 + .attrs = pca_sysfs_entries,
466 +static int pca9632_suspend(struct device *dev, pm_message_t state)
468 + /* FIXME: Not implemented */
472 +static int pca9632_resume(struct device *dev)
474 + /* FIXME: Not implemented */
478 +#define pca9632_suspend NULL
479 +#define pca9632_resume NULL
482 +static struct i2c_driver pca9632_driver = {
485 + .suspend = pca9632_suspend,
486 + .resume = pca9632_resume,
488 + .id = I2C_DRIVERID_PCA9632,
489 + .attach_adapter = pca9632_attach_adapter,
490 + .detach_client = pca9632_detach_client,
493 +static int pca9632_detect(struct i2c_adapter *adapter, int address, int kind)
495 + struct i2c_client *new_client;
496 + struct pca9632_data *pca;
499 + pca = kzalloc(sizeof(struct pca9632_data), GFP_KERNEL);
503 + mutex_init(&pca->lock);
505 + new_client = &pca->client;
506 + i2c_set_clientdata(new_client, pca);
507 + new_client->addr = address;
508 + new_client->adapter = adapter;
509 + new_client->driver = &pca9632_driver;
510 + new_client->flags = 0;
512 + strlcpy(new_client->name, "pca9632", I2C_NAME_SIZE);
514 + /* register with i2c core */
515 + err = i2c_attach_client(new_client);
519 + err = sysfs_create_group(&new_client->dev.kobj, &pca_attr_group);
523 + /* software reset */
524 + if (!software_rst(adapter))
525 + dev_info(&pca->client.dev, "pca9632 sw-rst done\n");
527 + /* enter normal mode */
528 + reg_set_bit_mask(pca, PCA9632_REG_MODE1, 0x10, PCA9632_NORMAL);
533 + i2c_detach_client(new_client);
540 +static int pca9632_attach_adapter(struct i2c_adapter *adapter)
542 + return i2c_probe(adapter, &addr_data, pca9632_detect);
545 +static int pca9632_detach_client(struct i2c_client *client)
549 + sysfs_remove_group(&client->dev.kobj, &pca_attr_group);
550 + err = i2c_detach_client(client);
555 + kfree(i2c_get_clientdata(client));
560 +static int __init pca9632_plat_probe(struct platform_device *pdev)
562 + /* FIXME: platform data should be attached here */
563 + pca9632_pdev = pdev;
568 +static int pca9632_plat_remove(struct platform_device *pdev)
573 +static struct platform_driver pca9632_plat_driver = {
574 + .probe = pca9632_plat_probe,
575 + .remove = pca9632_plat_remove,
577 + .owner = THIS_MODULE,
582 +static int __init pca9632_init(void)
586 + rc = platform_driver_register(&pca9632_plat_driver);
588 + i2c_add_driver(&pca9632_driver);
593 +static void __exit pca9632_exit(void)
595 + i2c_del_driver(&pca9632_driver);
597 + platform_driver_unregister(&pca9632_plat_driver);
600 +MODULE_AUTHOR("Matt Hsu <matt_hsu@openmoko.org>");
601 +MODULE_DESCRIPTION("NXP PCA9632 driver");
602 +MODULE_LICENSE("GPL");
604 +module_init(pca9632_init);
605 +module_exit(pca9632_exit);
606 diff --git a/drivers/i2c/chips/pca9632.h b/drivers/i2c/chips/pca9632.h
608 index 0000000..be2892e
610 +++ b/drivers/i2c/chips/pca9632.h
618 + PCA9632_REG_MODE1 = 0x00,
619 + PCA9632_REG_MODE2 = 0x01,
620 + PCA9632_REG_PWM0 = 0x02,
621 + PCA9632_REG_PWM1 = 0x03,
622 + PCA9632_REG_PWM2 = 0x04,
623 + PCA9632_REG_PWM3 = 0x05,
624 + PCA9632_REG_GRPPWM = 0x06,
625 + PCA9632_REG_GRPFREQ = 0x07,
626 + PCA9632_REG_LEDOUT = 0x08,
627 + PCA9632_REG_SUBADDR1 = 0x09,
628 + PCA9632_REG_SUBADDR2 = 0x0a,
629 + PCA9632_REG_SUBADDR3 = 0x0b,
630 + PCA9632_REG_ALLCALLADR1 = 0x0c,
633 +#define PCA9632_DMBLNK_SHIFT 5
635 +#endif /* _PCA9632_H */
636 diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h
637 index ee60156..d5a63fe 100644
638 --- a/include/linux/i2c-id.h
639 +++ b/include/linux/i2c-id.h
641 #define I2C_DRIVERID_PCF50606 1049
642 #define I2C_DRIVERID_TSL256X 1050
643 #define I2C_DRIVERID_PCF50633 1051
644 +#define I2C_DRIVERID_PCA9632 1052
647 * ---- Adapter types ----------------------------------------------------