diff --git a/debian/changelog b/debian/changelog index bcd7e785a..1522b00e8 100644 --- a/debian/changelog +++ b/debian/changelog @@ -43,6 +43,7 @@ linux-2.6 (2.6.26~rc5-1~experimental.1) UNRELEASED; urgency=low * [arm/ixp4xx] Disable ATA and more SCSI and network drivers. * [arm/versatile] Enable CONFIG_RTC_DRV_PL031 (closes: #484432). * [arm/iop32x, arm/ixp4xx, arm/versatile] Enable ARM_THUMB (closes: #484524). + * [arm/iop32x] Add LED driver for Thecus N2100 (Riku Voipio). [ Ian Campbell ] * Readme.build updated on how to generate orig tarballs. diff --git a/debian/config/arm/config.iop32x b/debian/config/arm/config.iop32x index c0d2d90e4..393ff7baa 100644 --- a/debian/config/arm/config.iop32x +++ b/debian/config/arm/config.iop32x @@ -356,7 +356,8 @@ CONFIG_TABLET_USB_WACOM=m ## file: drivers/leds/Kconfig ## CONFIG_NEW_LEDS=y -CONFIG_LEDS_CLASS=m +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_PCA9532=y CONFIG_LEDS_TRIGGERS=y CONFIG_LEDS_TRIGGER_TIMER=m # CONFIG_LEDS_TRIGGER_IDE_DISK is not set diff --git a/debian/patches/features/arm/led-pca9532-generic.patch b/debian/patches/features/arm/led-pca9532-generic.patch new file mode 100644 index 000000000..2896303a8 --- /dev/null +++ b/debian/patches/features/arm/led-pca9532-generic.patch @@ -0,0 +1,435 @@ +From 59711a33d366d004fed4008707676a16396b6b7d Mon Sep 17 00:00:00 2001 +From: Riku Voipio +Date: Sat, 31 May 2008 13:43:41 +0000 +Subject: leds: Add pca9532 led driver + +NXP pca9532 is a LED dimmer/controller attached to i2c bus. It allows +attaching upto 16 leds which can either be on, off or dimmed and/or blinked +with the two PWM modulators available. + +This driver is a "new-style" i2c driver that adheres to the driver model and +implements the led framework api. Since the leds connected to the driver are +platform specific, it is only useful when platform data is passed to the +driver to define what leds are connected to which pins. + +Signed-off-by: Riku Voipio +Signed-off-by: Andrew Morton +Signed-off-by: Richard Purdie +--- +diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig +index 86a369b..1c35dfa 100644 +--- a/drivers/leds/Kconfig ++++ b/drivers/leds/Kconfig +@@ -103,6 +103,14 @@ config LEDS_HP6XX + This option enables led support for the handheld + HP Jornada 620/660/680/690. + ++config LEDS_PCA9532 ++ tristate "LED driver for PCA9532 dimmer" ++ depends on LEDS_CLASS && I2C && INPUT && EXPERIMENTAL && ARM ++ help ++ This option enables support for NXP pca9532 ++ led controller. It is generally only usefull ++ as a platform driver ++ + config LEDS_GPIO + tristate "LED Support for GPIO connected LEDs" + depends on LEDS_CLASS && GENERIC_GPIO +diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile +index 973d626..7156f99 100644 +--- a/drivers/leds/Makefile ++++ b/drivers/leds/Makefile +@@ -16,6 +16,7 @@ obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o + obj-$(CONFIG_LEDS_H1940) += leds-h1940.o + obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o + obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o ++obj-$(CONFIG_LEDS_PCA9532) += leds-pca9532.o + obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o + obj-$(CONFIG_LEDS_CM_X270) += leds-cm-x270.o + obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o +diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c +new file mode 100644 +index 0000000..36b4088 +--- a/dev/null ++++ b/drivers/leds/leds-pca9532.c +@@ -0,0 +1,327 @@ ++/* ++ * pca9532.c - 16-bit Led dimmer ++ * ++ * 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 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static unsigned short normal_i2c[] = { /*0x60,*/ I2C_CLIENT_END}; ++I2C_CLIENT_INSMOD_1(pca9532); ++ ++#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) ++ ++#define ldev_to_led(c) container_of(c, struct pca9532_led, ldev) ++ ++struct pca9532_data { ++ struct i2c_client *client; ++ struct pca9532_led leds[16]; ++ struct mutex update_lock; ++ struct input_dev *idev; ++ u8 pwm[2]; ++ u8 psc[2]; ++}; ++ ++static int pca9532_probe(struct i2c_client *client); ++static int pca9532_remove(struct i2c_client *client); ++ ++static struct i2c_driver pca9532_driver = { ++ .driver = { ++ .name = "pca9532", ++ }, ++ .probe = pca9532_probe, ++ .remove = pca9532_remove, ++}; ++ ++/* We have two pwm/blinkers, but 16 possible leds to drive. Additionaly, ++ * the clever Thecus people are using one pwm to drive the beeper. So, ++ * as a compromise we average one pwm to the values requested by all ++ * leds that are not ON/OFF. ++ * */ ++static int pca9532_setpwm(struct i2c_client *client, int pwm, int blink, ++ enum led_brightness value) ++{ ++ int a = 0, b = 0, i = 0; ++ struct pca9532_data *data = i2c_get_clientdata(client); ++ for (i = 0; i < 16; i++) { ++ if (data->leds[i].type == PCA9532_TYPE_LED && ++ data->leds[i].state == PCA9532_PWM0+pwm) { ++ a++; ++ b += data->leds[i].ldev.brightness; ++ } ++ } ++ if (a == 0) { ++ dev_err(&client->dev, ++ "fear of division by zero %d/%d, wanted %d\n", ++ b, a, value); ++ return -EINVAL; ++ } ++ b = b/a; ++ if (b > 0xFF) ++ return -EINVAL; ++ mutex_lock(&data->update_lock); ++ data->pwm[pwm] = b; ++ i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(pwm), ++ data->pwm[pwm]); ++ data->psc[pwm] = blink; ++ i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(pwm), ++ data->psc[pwm]); ++ mutex_unlock(&data->update_lock); ++ return 0; ++} ++ ++/* Set LED routing */ ++static void pca9532_setled(struct pca9532_led *led) ++{ ++ struct i2c_client *client = led->client; ++ struct pca9532_data *data = i2c_get_clientdata(client); ++ char reg; ++ ++ mutex_lock(&data->update_lock); ++ reg = i2c_smbus_read_byte_data(client, LED_REG(led->id)); ++ /* zero led bits */ ++ reg = reg & ~(0x3<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); ++ mutex_unlock(&data->update_lock); ++} ++ ++static void pca9532_set_brightness(struct led_classdev *led_cdev, ++ enum led_brightness value) ++{ ++ int err = 0; ++ struct pca9532_led *led = ldev_to_led(led_cdev); ++ ++ if (value == LED_OFF) ++ led->state = PCA9532_OFF; ++ else if (value == LED_FULL) ++ led->state = PCA9532_ON; ++ else { ++ led->state = PCA9532_PWM0; /* Thecus: hardcode one pwm */ ++ err = pca9532_setpwm(led->client, 0, 0, value); ++ if (err) ++ return; /* XXX: led api doesn't allow error code? */ ++ } ++ pca9532_setled(led); ++} ++ ++static int pca9532_set_blink(struct led_classdev *led_cdev, ++ unsigned long *delay_on, unsigned long *delay_off) ++{ ++ struct pca9532_led *led = ldev_to_led(led_cdev); ++ struct i2c_client *client = led->client; ++ int psc; ++ ++ if (*delay_on == 0 && *delay_off == 0) { ++ /* led subsystem ask us for a blink rate */ ++ *delay_on = 1000; ++ *delay_off = 1000; ++ } ++ if (*delay_on != *delay_off || *delay_on > 1690 || *delay_on < 6) ++ return -EINVAL; ++ ++ /* Thecus specific: only use PSC/PWM 0 */ ++ psc = (*delay_on * 152-1)/1000; ++ return pca9532_setpwm(client, 0, psc, led_cdev->brightness); ++} ++ ++int pca9532_event(struct input_dev *dev, unsigned int type, unsigned int code, ++ int value) ++{ ++ struct pca9532_data *data = input_get_drvdata(dev); ++ ++ if (type != EV_SND && (code != SND_BELL || code != SND_TONE)) ++ return -1; ++ ++ /* XXX: allow different kind of beeps with psc/pwm modifications */ ++ if (value > 1 && value < 32767) ++ data->pwm[1] = 127; ++ else ++ data->pwm[1] = 0; ++ ++ dev_info(&dev->dev, "setting beep to %d \n", data->pwm[1]); ++ mutex_lock(&data->update_lock); ++ i2c_smbus_write_byte_data(data->client, PCA9532_REG_PWM(1), ++ data->pwm[1]); ++ mutex_unlock(&data->update_lock); ++ ++ return 0; ++} ++ ++static int pca9532_configure(struct i2c_client *client, ++ struct pca9532_data *data, struct pca9532_platform_data *pdata) ++{ ++ int i, err = 0; ++ ++ 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), ++ data->pwm[i]); ++ i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(i), ++ data->psc[i]); ++ } ++ ++ for (i = 0; i < 16; i++) { ++ struct pca9532_led *led = &data->leds[i]; ++ struct pca9532_led *pled = &pdata->leds[i]; ++ led->client = client; ++ led->id = i; ++ led->type = pled->type; ++ switch (led->type) { ++ case PCA9532_TYPE_NONE: ++ break; ++ case PCA9532_TYPE_LED: ++ led->state = pled->state; ++ led->name = pled->name; ++ led->ldev.name = led->name; ++ led->ldev.brightness = LED_OFF; ++ led->ldev.brightness_set = pca9532_set_brightness; ++ led->ldev.blink_set = pca9532_set_blink; ++ if (led_classdev_register(&client->dev, ++ &led->ldev) < 0) { ++ dev_err(&client->dev, ++ "couldn't register LED %s\n", ++ led->name); ++ goto exit; ++ } ++ pca9532_setled(led); ++ break; ++ case PCA9532_TYPE_N2100_BEEP: ++ BUG_ON(data->idev); ++ led->state = PCA9532_PWM1; ++ pca9532_setled(led); ++ data->idev = input_allocate_device(); ++ if (data->idev == NULL) { ++ err = -ENOMEM; ++ goto exit; ++ } ++ data->idev->name = pled->name; ++ data->idev->phys = "i2c/pca9532"; ++ data->idev->id.bustype = BUS_HOST; ++ data->idev->id.vendor = 0x001f; ++ data->idev->id.product = 0x0001; ++ data->idev->id.version = 0x0100; ++ data->idev->evbit[0] = BIT_MASK(EV_SND); ++ data->idev->sndbit[0] = BIT_MASK(SND_BELL) | ++ BIT_MASK(SND_TONE); ++ data->idev->event = pca9532_event; ++ input_set_drvdata(data->idev, data); ++ err = input_register_device(data->idev); ++ if (err) { ++ input_free_device(data->idev); ++ data->idev = NULL; ++ goto exit; ++ } ++ break; ++ } ++ } ++ return 0; ++ ++exit: ++ if (i > 0) ++ for (i = i - 1; i >= 0; i--) ++ switch (data->leds[i].type) { ++ case PCA9532_TYPE_NONE: ++ break; ++ case PCA9532_TYPE_LED: ++ led_classdev_unregister(&data->leds[i].ldev); ++ break; ++ case PCA9532_TYPE_N2100_BEEP: ++ if (data->idev != NULL) { ++ input_unregister_device(data->idev); ++ input_free_device(data->idev); ++ data->idev = NULL; ++ } ++ break; ++ } ++ ++ return err; ++ ++} ++ ++static int pca9532_probe(struct i2c_client *client) ++{ ++ struct pca9532_data *data = i2c_get_clientdata(client); ++ struct pca9532_platform_data *pca9532_pdata = client->dev.platform_data; ++ ++ if (!i2c_check_functionality(client->adapter, ++ I2C_FUNC_SMBUS_BYTE_DATA)) ++ return -EIO; ++ ++ data = kzalloc(sizeof(struct pca9532_data), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ dev_info(&client->dev, "setting platform data\n"); ++ i2c_set_clientdata(client, data); ++ data->client = client; ++ mutex_init(&data->update_lock); ++ ++ if (pca9532_pdata == NULL) ++ return -EIO; ++ ++ pca9532_configure(client, data, pca9532_pdata); ++ return 0; ++ ++} ++ ++static int pca9532_remove(struct i2c_client *client) ++{ ++ struct pca9532_data *data = i2c_get_clientdata(client); ++ int i; ++ for (i = 0; i < 16; i++) ++ switch (data->leds[i].type) { ++ case PCA9532_TYPE_NONE: ++ break; ++ case PCA9532_TYPE_LED: ++ led_classdev_unregister(&data->leds[i].ldev); ++ break; ++ case PCA9532_TYPE_N2100_BEEP: ++ if (data->idev != NULL) { ++ input_unregister_device(data->idev); ++ input_free_device(data->idev); ++ data->idev = NULL; ++ } ++ break; ++ } ++ ++ kfree(data); ++ i2c_set_clientdata(client, NULL); ++ return 0; ++} ++ ++static int __init pca9532_init(void) ++{ ++ return i2c_add_driver(&pca9532_driver); ++} ++ ++static void __exit pca9532_exit(void) ++{ ++ i2c_del_driver(&pca9532_driver); ++} ++ ++MODULE_AUTHOR("Riku Voipio "); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("PCA 9532 LED dimmer"); ++ ++module_init(pca9532_init); ++module_exit(pca9532_exit); ++ +diff --git a/include/linux/leds-pca9532.h b/include/linux/leds-pca9532.h +new file mode 100644 +index 0000000..81b4207 +--- a/dev/null ++++ b/include/linux/leds-pca9532.h +@@ -0,0 +1,45 @@ ++/* ++ * pca9532.h - platform data structure for pca9532 led controller ++ * ++ * 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 ++ * ++ */ ++ ++#ifndef __LINUX_PCA9532_H ++#define __LINUX_PCA9532_H ++ ++#include ++ ++enum pca9532_state { ++ PCA9532_OFF = 0x0, ++ PCA9532_ON = 0x1, ++ PCA9532_PWM0 = 0x2, ++ PCA9532_PWM1 = 0x3 ++}; ++ ++enum pca9532_type { PCA9532_TYPE_NONE, PCA9532_TYPE_LED, ++ PCA9532_TYPE_N2100_BEEP }; ++ ++struct pca9532_led { ++ u8 id; ++ struct i2c_client *client; ++ char *name; ++ struct led_classdev ldev; ++ enum pca9532_type type; ++ enum pca9532_state state; ++}; ++ ++struct pca9532_platform_data { ++ struct pca9532_led leds[16]; ++ u8 pwm[2]; ++ u8 psc[2]; ++}; ++ ++#endif /* __LINUX_PCA9532_H */ ++ +-- +cgit v0.7.2-85-gd188 diff --git a/debian/patches/features/arm/led-pca9532-n2100.patch b/debian/patches/features/arm/led-pca9532-n2100.patch new file mode 100644 index 000000000..5ac3dc412 --- /dev/null +++ b/debian/patches/features/arm/led-pca9532-n2100.patch @@ -0,0 +1,95 @@ +From fb46eb08b24fcfee0e97eba2bcb937acf96961a4 Mon Sep 17 00:00:00 2001 +From: Riku Voipio +Date: Sat, 31 May 2008 13:45:16 +0000 +Subject: leds: Add pca9532 platform data for Thecus N2100 + +Thecus N2100 has leds and a buzzer attached to a pca9532 controller. Attach +the driver to the i2c bus and define the pca9532 pin coniguration for this +platform in n2100_leds. + +With this patch, support for N2100 should be complete in mainline Linux. + +Signed-off-by: Riku Voipio +Acked-by: Lennert Buytenhek +Signed-off-by: Andrew Morton +Signed-off-by: Richard Purdie +--- +diff --git a/arch/arm/mach-iop32x/n2100.c b/arch/arm/mach-iop32x/n2100.c +index 2741063..28f164e 100644 +--- a/arch/arm/mach-iop32x/n2100.c ++++ b/arch/arm/mach-iop32x/n2100.c +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -206,6 +207,53 @@ static struct f75375s_platform_data n2100_f75375s = { + .pwm_enable = { 0, 0 }, + }; + ++static struct pca9532_platform_data n2100_leds = { ++ .leds = { ++ { .name = "n2100:red:satafail0", ++ .state = PCA9532_OFF, ++ .type = PCA9532_TYPE_LED, ++ }, ++ { .name = "n2100:red:satafail1", ++ .state = PCA9532_OFF, ++ .type = PCA9532_TYPE_LED, ++ }, ++ { .name = "n2100:blue:usb", ++ .state = PCA9532_OFF, ++ .type = PCA9532_TYPE_LED, ++ }, ++ { .type = PCA9532_TYPE_NONE }, ++ ++ { .type = PCA9532_TYPE_NONE }, ++ { .type = PCA9532_TYPE_NONE }, ++ { .type = PCA9532_TYPE_NONE }, ++ { .name = "n2100:red:usb", ++ .state = PCA9532_OFF, ++ .type = PCA9532_TYPE_LED, ++ }, ++ ++ { .type = PCA9532_TYPE_NONE }, /* power OFF gpio */ ++ { .type = PCA9532_TYPE_NONE }, /* reset gpio */ ++ { .type = PCA9532_TYPE_NONE }, ++ { .type = PCA9532_TYPE_NONE }, ++ ++ { .type = PCA9532_TYPE_NONE }, ++ { .name = "n2100:orange:system", ++ .state = PCA9532_OFF, ++ .type = PCA9532_TYPE_LED, ++ }, ++ { .name = "n2100:red:system", ++ .state = PCA9532_OFF, ++ .type = PCA9532_TYPE_LED, ++ }, ++ { .name = "N2100 beeper" , ++ .state = PCA9532_OFF, ++ .type = PCA9532_TYPE_N2100_BEEP, ++ }, ++ }, ++ .psc = { 0, 0 }, ++ .pwm = { 0, 0 }, ++}; ++ + static struct i2c_board_info __initdata n2100_i2c_devices[] = { + { + I2C_BOARD_INFO("rs5c372b", 0x32), +@@ -214,6 +262,10 @@ static struct i2c_board_info __initdata n2100_i2c_devices[] = { + I2C_BOARD_INFO("f75375", 0x2e), + .platform_data = &n2100_f75375s, + }, ++ { ++ I2C_BOARD_INFO("pca9532", 0x60), ++ .platform_data = &n2100_leds, ++ }, + }; + + /* +-- +cgit v0.7.2-85-gd188 diff --git a/debian/patches/series/1~experimental.1 b/debian/patches/series/1~experimental.1 index 8ca2e02d4..58e37dab4 100644 --- a/debian/patches/series/1~experimental.1 +++ b/debian/patches/series/1~experimental.1 @@ -30,6 +30,8 @@ + bugfix/arm/disable-r6040.patch + features/arm/speed_flush_cache.patch + features/arm/5281d0.patch ++ features/arm/led-pca9532-generic.patch ++ features/arm/led-pca9532-n2100.patch + features/all/at76.patch + bugfix/fix-hifn_795X-divdi3.patch + bugfix/all/mtd-prevent-physmap-from-causing-request_module-runaway-loop-modprobe-net-pf-1.patch