diff options
Diffstat (limited to 'target/linux/s3c24xx/patches-2.6.24/1291-gta03-pca9632.patch.patch')
-rw-r--r-- | target/linux/s3c24xx/patches-2.6.24/1291-gta03-pca9632.patch.patch | 650 |
1 files changed, 650 insertions, 0 deletions
diff --git a/target/linux/s3c24xx/patches-2.6.24/1291-gta03-pca9632.patch.patch b/target/linux/s3c24xx/patches-2.6.24/1291-gta03-pca9632.patch.patch new file mode 100644 index 0000000000..cf4df2a557 --- /dev/null +++ b/target/linux/s3c24xx/patches-2.6.24/1291-gta03-pca9632.patch.patch @@ -0,0 +1,650 @@ +From f6b1f49300a24c329f5d1049031f711c6268be95 Mon Sep 17 00:00:00 2001 +From: Matt Hsu <matt_hsu@openmoko.org> +Date: Thu, 18 Sep 2008 11:14:50 +0100 +Subject: [PATCH] gta03-pca9632.patch + + - pca9632 is a LED driver which will be adopted in gta03. + +Signed-off-by: Matt Hsu <matt_hsu@openmoko.org> +--- + drivers/i2c/chips/Kconfig | 9 + + drivers/i2c/chips/Makefile | 1 + + drivers/i2c/chips/pca9632.c | 551 +++++++++++++++++++++++++++++++++++++++++++ + drivers/i2c/chips/pca9632.h | 24 ++ + include/linux/i2c-id.h | 1 + + 5 files changed, 586 insertions(+), 0 deletions(-) + create mode 100644 drivers/i2c/chips/pca9632.c + create mode 100644 drivers/i2c/chips/pca9632.h + +diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig +index 1d6d36f..9af3c62 100644 +--- a/drivers/i2c/chips/Kconfig ++++ b/drivers/i2c/chips/Kconfig +@@ -193,4 +193,13 @@ config SENSORS_TSL256X + This driver can also be built as a module. If so, the module + will be called tsl256x. + ++config PCA9632 ++ tristate "Philips/NXP PCA9632 low power LED driver" ++ depends on I2C ++ help ++ If you say yes here you get support for the Philips/NXP PCA9632 ++ LED driver. ++ ++ This driver can also be built as a module. If so, the module ++ will be called pca9632. + endmenu +diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile +index 4b6ba04..67cd1e5 100644 +--- a/drivers/i2c/chips/Makefile ++++ b/drivers/i2c/chips/Makefile +@@ -18,6 +18,7 @@ obj-$(CONFIG_TPS65010) += tps65010.o + obj-$(CONFIG_MENELAUS) += menelaus.o + obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o + obj-$(CONFIG_SENSORS_TSL256X) += tsl256x.o ++obj-$(CONFIG_PCA9632) += pca9632.o + + ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) + EXTRA_CFLAGS += -DDEBUG +diff --git a/drivers/i2c/chips/pca9632.c b/drivers/i2c/chips/pca9632.c +new file mode 100644 +index 0000000..deda6a1 +--- /dev/null ++++ b/drivers/i2c/chips/pca9632.c +@@ -0,0 +1,551 @@ ++/* ++ * Philips/NXP PCA9632 low power LED driver. ++ * Copyright (C) 2008 Matt Hsu <matt_hsu@openmoko.org> ++ * ++ * low_level implementation are based on pcf50606 driver ++ * ++ * 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. ++ * ++ * TODO: ++ * - attach ledclass?? ++ * - add platform data ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/i2c.h> ++#include <linux/platform_device.h> ++ ++#include "pca9632.h" ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = { 0x62, I2C_CLIENT_END }; ++ ++/* Insmod parameters */ ++I2C_CLIENT_INSMOD_1(pca9632); ++ ++enum pca9632_pwr_state { ++ PCA9632_NORMAL, ++ PCA9632_SLEEP, ++}; ++ ++enum pca9632_led_output { ++ PCA9632_OFF, ++ PCA9632_ON, ++ PCA9632_CTRL_BY_PWM, ++ PCA9632_CTRL_BY_PWM_GRPPWM, ++}; ++ ++static const char *led_output_name[] = { ++ [PCA9632_OFF] = "off", ++ [PCA9632_ON] = "fully-on", ++ [PCA9632_CTRL_BY_PWM] = "ctrl-by-pwm", ++ [PCA9632_CTRL_BY_PWM_GRPPWM] = "ctrl-by-pwm-grppwm", ++}; ++ ++struct pca9632_data { ++ struct i2c_client client; ++ struct mutex lock; ++}; ++ ++static struct i2c_driver pca9632_driver; ++static struct platform_device *pca9632_pdev; ++ ++static int pca9632_attach_adapter(struct i2c_adapter *adapter); ++static int pca9632_detach_client(struct i2c_client *client); ++ ++static int __reg_write(struct pca9632_data *pca, u_int8_t reg, u_int8_t val) ++{ ++ return i2c_smbus_write_byte_data(&pca->client, reg, val); ++} ++ ++static int reg_write(struct pca9632_data *pca, u_int8_t reg, u_int8_t val) ++{ ++ int ret; ++ ++ mutex_lock(&pca->lock); ++ ret = __reg_write(pca, reg, val); ++ mutex_unlock(&pca->lock); ++ ++ return ret; ++} ++ ++static int32_t __reg_read(struct pca9632_data *pca, u_int8_t reg) ++{ ++ int32_t ret; ++ ++ ret = i2c_smbus_read_byte_data(&pca->client, reg); ++ ++ return ret; ++} ++ ++static u_int8_t reg_read(struct pca9632_data *pca, u_int8_t reg) ++{ ++ int32_t ret; ++ ++ mutex_lock(&pca->lock); ++ ret = __reg_read(pca, reg); ++ mutex_unlock(&pca->lock); ++ ++ return ret & 0xff; ++} ++ ++static int reg_set_bit_mask(struct pca9632_data *pca, ++ u_int8_t reg, u_int8_t mask, u_int8_t val) ++{ ++ int ret; ++ u_int8_t tmp; ++ ++ val &= mask; ++ ++ mutex_lock(&pca->lock); ++ ++ tmp = __reg_read(pca, reg); ++ tmp &= ~mask; ++ tmp |= val; ++ ret = __reg_write(pca, reg, tmp); ++ ++ mutex_unlock(&pca->lock); ++ ++ return ret; ++} ++ ++static inline int calc_dc(uint8_t idc) ++{ ++ return (idc * 100) / 256; ++} ++ ++/* ++ * Software reset ++ */ ++static int software_rst(struct i2c_adapter *adapter) ++{ ++ u8 buf[] = { 0xa5, 0x5a }; ++ ++ struct i2c_msg msg[] = { ++ { ++ .addr = 0x3, ++ .flags = 0, ++ .buf = &buf, ++ .len = sizeof(buf) ++ } ++ }; ++ ++ return i2c_transfer(adapter, msg, 1); ++} ++ ++/* ++ * Group dmblnk control ++ */ ++static void config_group_dmblnk(struct pca9632_data *pca, int group_dmblnk_mode) ++{ ++ reg_set_bit_mask(pca, PCA9632_REG_MODE2, 0x20, ++ group_dmblnk_mode << PCA9632_DMBLNK_SHIFT); ++} ++ ++static int get_group_dmblnk(struct pca9632_data *pca) ++{ ++ return reg_read(pca, PCA9632_REG_MODE2) >> PCA9632_DMBLNK_SHIFT; ++} ++ ++static ssize_t show_group_dmblnk(struct device *dev, struct device_attribute ++ *attr, char *buf) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct pca9632_data *pca = i2c_get_clientdata(client); ++ ++ if (get_group_dmblnk(pca)) ++ return sprintf(buf, "blinking\n"); ++ else ++ return sprintf(buf, "dimming\n"); ++} ++ ++static ssize_t set_group_dmblnk(struct device *dev, struct device_attribute ++ *attr, const char *buf, size_t count) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct pca9632_data *pca = i2c_get_clientdata(client); ++ unsigned int mode = simple_strtoul(buf, NULL, 10); ++ ++ if (mode) ++ dev_info(&pca->client.dev, "blinking\n"); ++ else ++ dev_info(&pca->client.dev, "dimming\n"); ++ ++ config_group_dmblnk(pca, mode); ++ ++ return count; ++} ++ ++static DEVICE_ATTR(group_dmblnk, S_IRUGO | S_IWUSR, show_group_dmblnk, ++ set_group_dmblnk); ++ ++static int reg_id_by_name(const char *name) ++{ ++ int reg_id = -1; ++ ++ if (!strncmp(name, "led0", 4)) ++ reg_id = PCA9632_REG_PWM0; ++ else if (!strncmp(name, "led1", 4)) ++ reg_id = PCA9632_REG_PWM1; ++ else if (!strncmp(name, "led2", 4)) ++ reg_id = PCA9632_REG_PWM2; ++ else if (!strncmp(name, "led3", 4)) ++ reg_id = PCA9632_REG_PWM3; ++ ++ return reg_id; ++} ++ ++static int get_led_output(struct pca9632_data *pca, int ldrx) ++{ ++ u_int8_t led_state; ++ ++ ldrx = ldrx - 2; ++ led_state = reg_read(pca, PCA9632_REG_LEDOUT); ++ led_state = (led_state >> (2 * ldrx)) & 0x03; ++ ++ return led_state; ++} ++ ++static void config_led_output(struct pca9632_data *pca, int ldrx, ++ enum pca9632_led_output led_output) ++{ ++ u_int8_t mask; ++ int tmp; ++ ++ ldrx = ldrx - 2; ++ mask = 0x03 << (2 * ldrx); ++ tmp = reg_set_bit_mask(pca, PCA9632_REG_LEDOUT, ++ mask, led_output << (2 * ldrx)); ++} ++ ++/* ++ * Individual brightness control ++ */ ++static ssize_t show_brightness(struct device *dev, struct device_attribute ++ *attr, char *buf) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct pca9632_data *pca = i2c_get_clientdata(client); ++ int ldrx; ++ ++ ldrx = reg_id_by_name(attr->attr.name); ++ ++ switch (get_led_output(pca, ldrx)) { ++ ++ case PCA9632_OFF: ++ case PCA9632_ON: ++ return sprintf(buf, "%s", ++ led_output_name[get_led_output(pca, ldrx)]); ++ ++ case PCA9632_CTRL_BY_PWM: ++ return sprintf(buf, "%d%% \n", calc_dc(reg_read(pca, ldrx))); ++ ++ case PCA9632_CTRL_BY_PWM_GRPPWM: ++ /* check group dmblnk */ ++ if (get_group_dmblnk(pca)) ++ return sprintf(buf, "%d%% \n", ++ calc_dc(reg_read(pca, ldrx))); ++ return sprintf(buf, "%d%% \n", ++ calc_dc((reg_read(pca, ldrx) & 0xfc))); ++ default: ++ break; ++ } ++ ++ return sprintf(buf, "invalid argument\n"); ++} ++ ++static ssize_t set_brightness(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct pca9632_data *pca = i2c_get_clientdata(client); ++ unsigned int pwm = simple_strtoul(buf, NULL, 10); ++ int ldrx; ++ ++ ldrx = reg_id_by_name(attr->attr.name); ++ reg_set_bit_mask(pca, ldrx, 0xff, pwm); ++ ++ return count; ++} ++ ++static ++DEVICE_ATTR(led0_pwm, S_IRUGO | S_IWUSR, show_brightness, set_brightness); ++static ++DEVICE_ATTR(led1_pwm, S_IRUGO | S_IWUSR, show_brightness, set_brightness); ++static ++DEVICE_ATTR(led2_pwm, S_IRUGO | S_IWUSR, show_brightness, set_brightness); ++static ++DEVICE_ATTR(led3_pwm, S_IRUGO | S_IWUSR, show_brightness, set_brightness); ++ ++/* ++ * Group frequency control ++ */ ++static ssize_t show_group_freq(struct device *dev, struct device_attribute ++ *attr, char *buf) ++{ ++ uint32_t period; ++ struct i2c_client *client = to_i2c_client(dev); ++ struct pca9632_data *pca = i2c_get_clientdata(client); ++ ++ period = ((reg_read(pca, PCA9632_REG_GRPFREQ) + 1) * 1000) / 24; ++ ++ return sprintf(buf, "%d ms\n", period); ++} ++ ++static ssize_t set_group_freq(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct pca9632_data *pca = i2c_get_clientdata(client); ++ ++ unsigned int freq = simple_strtoul(buf, NULL, 10); ++ reg_write(pca, PCA9632_REG_GRPFREQ, freq); ++ return count; ++} ++ ++static ++DEVICE_ATTR(group_freq, S_IRUGO | S_IWUSR, show_group_freq, set_group_freq); ++ ++/* ++ * Group duty cycle tonrol* ++ */ ++static ssize_t show_group_dc(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct pca9632_data *pca = i2c_get_clientdata(client); ++ ++ if (get_group_dmblnk(pca)) { ++ ++ if (reg_read(pca, PCA9632_REG_GRPFREQ) <= 0x03) ++ return sprintf(buf, "%d%% \n", ++ calc_dc(reg_read(pca, PCA9632_REG_GRPPWM) & 0xfc)); ++ ++ return sprintf(buf, "%d%% \n", calc_dc(reg_read(pca, ++ PCA9632_REG_GRPPWM))); ++ } ++ ++ return sprintf(buf, "%d%% \n", calc_dc(reg_read(pca, ++ PCA9632_REG_GRPPWM) & 0xf0)); ++} ++ ++static ssize_t set_group_dc(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct pca9632_data *pca = i2c_get_clientdata(client); ++ ++ unsigned int dc = simple_strtoul(buf, NULL, 10); ++ ++ reg_set_bit_mask(pca, PCA9632_REG_GRPPWM, 0xff, dc); ++ ++ return count; ++} ++ ++static DEVICE_ATTR(group_dc, S_IRUGO | S_IWUSR, show_group_dc, set_group_dc); ++ ++/* ++ * LED driver output ++ */ ++static ssize_t show_led_output(struct device *dev, struct device_attribute ++ *attr, char *buf) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct pca9632_data *pca = i2c_get_clientdata(client); ++ int ldrx; ++ ++ ldrx = reg_id_by_name(attr->attr.name); ++ ++ return sprintf(buf, "%s \n", ++ led_output_name[get_led_output(pca, ldrx)]); ++ ++} ++static ssize_t set_led_output(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct pca9632_data *pca = i2c_get_clientdata(client); ++ enum pca9632_led_output led_output; ++ int ldrx; ++ ++ led_output = simple_strtoul(buf, NULL, 10); ++ ldrx = reg_id_by_name(attr->attr.name); ++ config_led_output(pca, ldrx, led_output); ++ ++ return count; ++} ++ ++static ++DEVICE_ATTR(led0_output, S_IRUGO | S_IWUSR, show_led_output, set_led_output); ++static ++DEVICE_ATTR(led1_output, S_IRUGO | S_IWUSR, show_led_output, set_led_output); ++static ++DEVICE_ATTR(led2_output, S_IRUGO | S_IWUSR, show_led_output, set_led_output); ++static ++DEVICE_ATTR(led3_output, S_IRUGO | S_IWUSR, show_led_output, set_led_output); ++ ++static struct attribute *pca_sysfs_entries[] = { ++ &dev_attr_group_dmblnk.attr, ++ &dev_attr_led0_pwm.attr, ++ &dev_attr_led1_pwm.attr, ++ &dev_attr_led2_pwm.attr, ++ &dev_attr_led3_pwm.attr, ++ &dev_attr_group_dc.attr, ++ &dev_attr_group_freq.attr, ++ &dev_attr_led0_output.attr, ++ &dev_attr_led1_output.attr, ++ &dev_attr_led2_output.attr, ++ &dev_attr_led3_output.attr, ++ NULL ++}; ++ ++static struct attribute_group pca_attr_group = { ++ .name = NULL, /* put in device directory */ ++ .attrs = pca_sysfs_entries, ++}; ++ ++#ifdef CONFIG_PM ++static int pca9632_suspend(struct device *dev, pm_message_t state) ++{ ++ /* FIXME: Not implemented */ ++ return 0; ++} ++ ++static int pca9632_resume(struct device *dev) ++{ ++ /* FIXME: Not implemented */ ++ return 0; ++} ++#else ++#define pca9632_suspend NULL ++#define pca9632_resume NULL ++#endif ++ ++static struct i2c_driver pca9632_driver = { ++ .driver = { ++ .name = "pca9632", ++ .suspend = pca9632_suspend, ++ .resume = pca9632_resume, ++ }, ++ .id = I2C_DRIVERID_PCA9632, ++ .attach_adapter = pca9632_attach_adapter, ++ .detach_client = pca9632_detach_client, ++}; ++ ++static int pca9632_detect(struct i2c_adapter *adapter, int address, int kind) ++{ ++ struct i2c_client *new_client; ++ struct pca9632_data *pca; ++ int err; ++ ++ pca = kzalloc(sizeof(struct pca9632_data), GFP_KERNEL); ++ if (!pca) ++ return -ENOMEM; ++ ++ mutex_init(&pca->lock); ++ ++ new_client = &pca->client; ++ i2c_set_clientdata(new_client, pca); ++ new_client->addr = address; ++ new_client->adapter = adapter; ++ new_client->driver = &pca9632_driver; ++ new_client->flags = 0; ++ ++ strlcpy(new_client->name, "pca9632", I2C_NAME_SIZE); ++ ++ /* register with i2c core */ ++ err = i2c_attach_client(new_client); ++ if (err) ++ goto exit_kfree; ++ ++ err = sysfs_create_group(&new_client->dev.kobj, &pca_attr_group); ++ if (err) ++ goto exit_detach; ++ ++ /* software reset */ ++ if (!software_rst(adapter)) ++ dev_info(&pca->client.dev, "pca9632 sw-rst done\n"); ++ ++ /* enter normal mode */ ++ reg_set_bit_mask(pca, PCA9632_REG_MODE1, 0x10, PCA9632_NORMAL); ++ ++ return 0; ++ ++exit_detach: ++ i2c_detach_client(new_client); ++exit_kfree: ++ kfree(pca); ++ ++ return err; ++} ++ ++static int pca9632_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_probe(adapter, &addr_data, pca9632_detect); ++} ++ ++static int pca9632_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ sysfs_remove_group(&client->dev.kobj, &pca_attr_group); ++ err = i2c_detach_client(client); ++ ++ if (err) ++ return err; ++ ++ kfree(i2c_get_clientdata(client)); ++ ++ return 0; ++} ++ ++static int __init pca9632_plat_probe(struct platform_device *pdev) ++{ ++ /* FIXME: platform data should be attached here */ ++ pca9632_pdev = pdev; ++ ++ return 0; ++} ++ ++static int pca9632_plat_remove(struct platform_device *pdev) ++{ ++ return 0; ++} ++ ++static struct platform_driver pca9632_plat_driver = { ++ .probe = pca9632_plat_probe, ++ .remove = pca9632_plat_remove, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "pca9632", ++ }, ++}; ++ ++static int __init pca9632_init(void) ++{ ++ int rc; ++ ++ rc = platform_driver_register(&pca9632_plat_driver); ++ if (!rc) ++ i2c_add_driver(&pca9632_driver); ++ ++ return rc; ++} ++ ++static void __exit pca9632_exit(void) ++{ ++ i2c_del_driver(&pca9632_driver); ++ ++ platform_driver_unregister(&pca9632_plat_driver); ++} ++ ++MODULE_AUTHOR("Matt Hsu <matt_hsu@openmoko.org>"); ++MODULE_DESCRIPTION("NXP PCA9632 driver"); ++MODULE_LICENSE("GPL"); ++ ++module_init(pca9632_init); ++module_exit(pca9632_exit); +diff --git a/drivers/i2c/chips/pca9632.h b/drivers/i2c/chips/pca9632.h +new file mode 100644 +index 0000000..be2892e +--- /dev/null ++++ b/drivers/i2c/chips/pca9632.h +@@ -0,0 +1,24 @@ ++#ifndef _PCA9632_H ++#define _PCA9632_H ++ ++ ++enum pca9632_regs{ ++ ++ PCA9632_REG_MODE1 = 0x00, ++ PCA9632_REG_MODE2 = 0x01, ++ PCA9632_REG_PWM0 = 0x02, ++ PCA9632_REG_PWM1 = 0x03, ++ PCA9632_REG_PWM2 = 0x04, ++ PCA9632_REG_PWM3 = 0x05, ++ PCA9632_REG_GRPPWM = 0x06, ++ PCA9632_REG_GRPFREQ = 0x07, ++ PCA9632_REG_LEDOUT = 0x08, ++ PCA9632_REG_SUBADDR1 = 0x09, ++ PCA9632_REG_SUBADDR2 = 0x0a, ++ PCA9632_REG_SUBADDR3 = 0x0b, ++ PCA9632_REG_ALLCALLADR1 = 0x0c, ++}; ++ ++#define PCA9632_DMBLNK_SHIFT 5 ++ ++#endif /* _PCA9632_H */ +diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h +index ee60156..d5a63fe 100644 +--- a/include/linux/i2c-id.h ++++ b/include/linux/i2c-id.h +@@ -170,6 +170,7 @@ + #define I2C_DRIVERID_PCF50606 1049 + #define I2C_DRIVERID_TSL256X 1050 + #define I2C_DRIVERID_PCF50633 1051 ++#define I2C_DRIVERID_PCA9632 1052 + + /* + * ---- Adapter types ---------------------------------------------------- +-- +1.5.6.5 + |