diff options
author | Ingo Molnar <mingo@elte.hu> | 2008-10-15 13:46:29 +0200 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-10-15 13:46:29 +0200 |
commit | b2aaf8f74cdc84a9182f6cabf198b7763bcb9d40 (patch) | |
tree | 53ccb1c2c14751fe69cf93102e76e97021f6df07 /drivers/leds | |
parent | 4f962d4d65923d7b722192e729840cfb79af0a5a (diff) | |
parent | 278429cff8809958d25415ba0ed32b59866ab1a8 (diff) |
Merge branch 'linus' into stackprotector
Conflicts:
arch/x86/kernel/Makefile
include/asm-x86/pda.h
Diffstat (limited to 'drivers/leds')
-rw-r--r-- | drivers/leds/Kconfig | 38 | ||||
-rw-r--r-- | drivers/leds/Makefile | 5 | ||||
-rw-r--r-- | drivers/leds/led-triggers.c | 3 | ||||
-rw-r--r-- | drivers/leds/leds-ams-delta.c | 2 | ||||
-rw-r--r-- | drivers/leds/leds-atmel-pwm.c | 2 | ||||
-rw-r--r-- | drivers/leds/leds-cm-x270.c | 4 | ||||
-rw-r--r-- | drivers/leds/leds-corgi.c | 125 | ||||
-rw-r--r-- | drivers/leds/leds-fsg.c | 30 | ||||
-rw-r--r-- | drivers/leds/leds-h1940.c | 15 | ||||
-rw-r--r-- | drivers/leds/leds-locomo.c | 2 | ||||
-rw-r--r-- | drivers/leds/leds-pca9532.c | 337 | ||||
-rw-r--r-- | drivers/leds/leds-pca955x.c | 390 | ||||
-rw-r--r-- | drivers/leds/leds-s3c24xx.c | 6 | ||||
-rw-r--r-- | drivers/leds/leds-spitz.c | 131 | ||||
-rw-r--r-- | drivers/leds/leds-sunfire.c | 273 |
15 files changed, 1061 insertions, 302 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 86a369bc57d6..e3e40427e00e 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -24,13 +24,6 @@ config LEDS_ATMEL_PWM This option enables support for LEDs driven using outputs of the dedicated PWM controller found on newer Atmel SOCs. -config LEDS_CORGI - tristate "LED Support for the Sharp SL-C7x0 series" - depends on LEDS_CLASS && PXA_SHARP_C7xx - help - This option enables support for the LEDs on Sharp Zaurus - SL-C7x0 series (C700, C750, C760, C860). - config LEDS_LOCOMO tristate "LED Support for Locomo device" depends on LEDS_CLASS && SHARP_LOCOMO @@ -38,13 +31,6 @@ config LEDS_LOCOMO This option enables support for the LEDs on Sharp Locomo. Zaurus models SL-5500 and SL-5600. -config LEDS_SPITZ - tristate "LED Support for the Sharp SL-Cxx00 series" - depends on LEDS_CLASS && PXA_SHARP_Cxx00 - help - This option enables support for the LEDs on Sharp Zaurus - SL-Cxx00 series (C1000, C3000, C3100). - config LEDS_S3C24XX tristate "LED Support for Samsung S3C24XX GPIO LEDs" depends on LEDS_CLASS && ARCH_S3C2410 @@ -96,6 +82,14 @@ config LEDS_COBALT_RAQ help This option enables support for the Cobalt Raq series LEDs. +config LEDS_SUNFIRE + tristate "LED support for SunFire servers." + depends on LEDS_CLASS && SPARC64 + select LEDS_TRIGGERS + help + This option enables support for the Left, Middle, and Right + LEDs on the I/O and CPU boards of SunFire UltraSPARC servers. + config LEDS_HP6XX tristate "LED Support for the HP Jornada 6xx" depends on LEDS_CLASS && SH_HP6XX @@ -103,6 +97,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 + 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 @@ -147,6 +149,14 @@ config LEDS_CLEVO_MAIL To compile this driver as a module, choose M here: the module will be called leds-clevo-mail. +config LEDS_PCA955X + tristate "LED Support for PCA955x I2C chips" + depends on LEDS_CLASS && I2C + help + This option enables support for LEDs connected to PCA955x + LED driver chips accessed via the I2C bus. Supported + devices include PCA9550, PCA9551, PCA9552, and PCA9553. + comment "LED Triggers" config LEDS_TRIGGERS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 973d626f5f4a..eb186c351a1c 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -6,9 +6,7 @@ obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o # LED Platform Drivers obj-$(CONFIG_LEDS_ATMEL_PWM) += leds-atmel-pwm.o -obj-$(CONFIG_LEDS_CORGI) += leds-corgi.o obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o -obj-$(CONFIG_LEDS_SPITZ) += leds-spitz.o obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o @@ -16,11 +14,14 @@ 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_SUNFIRE) += leds-sunfire.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 obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o obj-$(CONFIG_LEDS_FSG) += leds-fsg.o +obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o # LED Triggers obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index 0f242b3f09b6..f910eaffe3a6 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -111,16 +111,17 @@ void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger) flags); if (led_cdev->trigger->deactivate) led_cdev->trigger->deactivate(led_cdev); + led_cdev->trigger = NULL; led_set_brightness(led_cdev, LED_OFF); } if (trigger) { write_lock_irqsave(&trigger->leddev_list_lock, flags); list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs); write_unlock_irqrestore(&trigger->leddev_list_lock, flags); + led_cdev->trigger = trigger; if (trigger->activate) trigger->activate(led_cdev); } - led_cdev->trigger = trigger; } EXPORT_SYMBOL_GPL(led_trigger_set); diff --git a/drivers/leds/leds-ams-delta.c b/drivers/leds/leds-ams-delta.c index c37bb0d5a0c5..32c98b2efa3f 100644 --- a/drivers/leds/leds-ams-delta.c +++ b/drivers/leds/leds-ams-delta.c @@ -12,7 +12,7 @@ #include <linux/init.h> #include <linux/platform_device.h> #include <linux/leds.h> -#include <asm/arch/board-ams-delta.h> +#include <mach/board-ams-delta.h> /* * Our context diff --git a/drivers/leds/leds-atmel-pwm.c b/drivers/leds/leds-atmel-pwm.c index 28db6c1444ed..52297c3ab246 100644 --- a/drivers/leds/leds-atmel-pwm.c +++ b/drivers/leds/leds-atmel-pwm.c @@ -37,7 +37,7 @@ static int __init pwmled_probe(struct platform_device *pdev) { const struct gpio_led_platform_data *pdata; struct pwmled *leds; - unsigned i; + int i; int status; pdata = pdev->dev.platform_data; diff --git a/drivers/leds/leds-cm-x270.c b/drivers/leds/leds-cm-x270.c index accc7eddb788..836a43d776e6 100644 --- a/drivers/leds/leds-cm-x270.c +++ b/drivers/leds/leds-cm-x270.c @@ -18,8 +18,8 @@ #include <linux/platform_device.h> #include <linux/leds.h> -#include <asm/arch/hardware.h> -#include <asm/arch/pxa-regs.h> +#include <mach/hardware.h> +#include <mach/pxa-regs.h> #define GPIO_RED_LED (93) #define GPIO_GREEN_LED (94) diff --git a/drivers/leds/leds-corgi.c b/drivers/leds/leds-corgi.c deleted file mode 100644 index a709704b9f93..000000000000 --- a/drivers/leds/leds-corgi.c +++ /dev/null @@ -1,125 +0,0 @@ -/* - * LED Triggers Core - * - * Copyright 2005-2006 Openedhand Ltd. - * - * Author: Richard Purdie <rpurdie@openedhand.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/platform_device.h> -#include <linux/leds.h> -#include <asm/mach-types.h> -#include <asm/arch/corgi.h> -#include <asm/arch/hardware.h> -#include <asm/arch/pxa-regs.h> -#include <asm/hardware/scoop.h> - -static void corgiled_amber_set(struct led_classdev *led_cdev, - enum led_brightness value) -{ - if (value) - GPSR0 = GPIO_bit(CORGI_GPIO_LED_ORANGE); - else - GPCR0 = GPIO_bit(CORGI_GPIO_LED_ORANGE); -} - -static void corgiled_green_set(struct led_classdev *led_cdev, - enum led_brightness value) -{ - if (value) - set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_LED_GREEN); - else - reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_LED_GREEN); -} - -static struct led_classdev corgi_amber_led = { - .name = "corgi:amber:charge", - .default_trigger = "sharpsl-charge", - .brightness_set = corgiled_amber_set, -}; - -static struct led_classdev corgi_green_led = { - .name = "corgi:green:mail", - .default_trigger = "nand-disk", - .brightness_set = corgiled_green_set, -}; - -#ifdef CONFIG_PM -static int corgiled_suspend(struct platform_device *dev, pm_message_t state) -{ -#ifdef CONFIG_LEDS_TRIGGERS - if (corgi_amber_led.trigger && - strcmp(corgi_amber_led.trigger->name, "sharpsl-charge")) -#endif - led_classdev_suspend(&corgi_amber_led); - led_classdev_suspend(&corgi_green_led); - return 0; -} - -static int corgiled_resume(struct platform_device *dev) -{ - led_classdev_resume(&corgi_amber_led); - led_classdev_resume(&corgi_green_led); - return 0; -} -#endif - -static int corgiled_probe(struct platform_device *pdev) -{ - int ret; - - ret = led_classdev_register(&pdev->dev, &corgi_amber_led); - if (ret < 0) - return ret; - - ret = led_classdev_register(&pdev->dev, &corgi_green_led); - if (ret < 0) - led_classdev_unregister(&corgi_amber_led); - - return ret; -} - -static int corgiled_remove(struct platform_device *pdev) -{ - led_classdev_unregister(&corgi_amber_led); - led_classdev_unregister(&corgi_green_led); - return 0; -} - -static struct platform_driver corgiled_driver = { - .probe = corgiled_probe, - .remove = corgiled_remove, -#ifdef CONFIG_PM - .suspend = corgiled_suspend, - .resume = corgiled_resume, -#endif - .driver = { - .name = "corgi-led", - .owner = THIS_MODULE, - }, -}; - -static int __init corgiled_init(void) -{ - return platform_driver_register(&corgiled_driver); -} - -static void __exit corgiled_exit(void) -{ - platform_driver_unregister(&corgiled_driver); -} - -module_init(corgiled_init); -module_exit(corgiled_exit); - -MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>"); -MODULE_DESCRIPTION("Corgi LED driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:corgi-led"); diff --git a/drivers/leds/leds-fsg.c b/drivers/leds/leds-fsg.c index a7421b8c47d8..34935155c1c0 100644 --- a/drivers/leds/leds-fsg.c +++ b/drivers/leds/leds-fsg.c @@ -19,7 +19,7 @@ #include <linux/init.h> #include <linux/platform_device.h> #include <linux/leds.h> -#include <asm/arch/hardware.h> +#include <mach/hardware.h> #include <asm/io.h> static short __iomem *latch_address; @@ -161,6 +161,16 @@ static int fsg_led_probe(struct platform_device *pdev) { int ret; + /* Map the LED chip select address space */ + latch_address = (unsigned short *) ioremap(IXP4XX_EXP_BUS_BASE(2), 512); + if (!latch_address) { + ret = -ENOMEM; + goto failremap; + } + + latch_value = 0xffff; + *latch_address = latch_value; + ret = led_classdev_register(&pdev->dev, &fsg_wlan_led); if (ret < 0) goto failwlan; @@ -185,20 +195,8 @@ static int fsg_led_probe(struct platform_device *pdev) if (ret < 0) goto failring; - /* Map the LED chip select address space */ - latch_address = (unsigned short *) ioremap(IXP4XX_EXP_BUS_BASE(2), 512); - if (!latch_address) { - ret = -ENOMEM; - goto failremap; - } - - latch_value = 0xffff; - *latch_address = latch_value; - return ret; - failremap: - led_classdev_unregister(&fsg_ring_led); failring: led_classdev_unregister(&fsg_sync_led); failsync: @@ -210,14 +208,14 @@ static int fsg_led_probe(struct platform_device *pdev) failwan: led_classdev_unregister(&fsg_wlan_led); failwlan: + iounmap(latch_address); + failremap: return ret; } static int fsg_led_remove(struct platform_device *pdev) { - iounmap(latch_address); - led_classdev_unregister(&fsg_wlan_led); led_classdev_unregister(&fsg_wan_led); led_classdev_unregister(&fsg_sata_led); @@ -225,6 +223,8 @@ static int fsg_led_remove(struct platform_device *pdev) led_classdev_unregister(&fsg_sync_led); led_classdev_unregister(&fsg_ring_led); + iounmap(latch_address); + return 0; } diff --git a/drivers/leds/leds-h1940.c b/drivers/leds/leds-h1940.c index bcec42230389..11b77a70bbcb 100644 --- a/drivers/leds/leds-h1940.c +++ b/drivers/leds/leds-h1940.c @@ -16,14 +16,15 @@ #include <linux/string.h> #include <linux/ctype.h> #include <linux/leds.h> -#include <asm/arch/regs-gpio.h> -#include <asm/hardware.h> -#include <asm/arch/h1940-latch.h> +#include <mach/regs-gpio.h> +#include <mach/hardware.h> +#include <mach/h1940-latch.h> /* * Green led. */ -void h1940_greenled_set(struct led_classdev *led_dev, enum led_brightness value) +static void h1940_greenled_set(struct led_classdev *led_dev, + enum led_brightness value) { switch (value) { case LED_HALF: @@ -52,7 +53,8 @@ static struct led_classdev h1940_greenled = { /* * Red led. */ -void h1940_redled_set(struct led_classdev *led_dev, enum led_brightness value) +static void h1940_redled_set(struct led_classdev *led_dev, + enum led_brightness value) { switch (value) { case LED_HALF: @@ -82,7 +84,8 @@ static struct led_classdev h1940_redled = { * Blue led. * (it can only be blue flashing led) */ -void h1940_blueled_set(struct led_classdev *led_dev, enum led_brightness value) +static void h1940_blueled_set(struct led_classdev *led_dev, + enum led_brightness value) { if (value) { /* flashing Blue */ diff --git a/drivers/leds/leds-locomo.c b/drivers/leds/leds-locomo.c index 7295f7f52185..5d91362e3066 100644 --- a/drivers/leds/leds-locomo.c +++ b/drivers/leds/leds-locomo.c @@ -13,7 +13,7 @@ #include <linux/device.h> #include <linux/leds.h> -#include <asm/hardware.h> +#include <mach/hardware.h> #include <asm/hardware/locomo.h> static void locomoled_brightness_set(struct led_classdev *led_cdev, diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c new file mode 100644 index 000000000000..4064d4f6b33b --- /dev/null +++ b/drivers/leds/leds-pca9532.c @@ -0,0 +1,337 @@ +/* + * pca9532.c - 16-bit Led dimmer + * + * Copyright (C) 2008 Riku Voipio <riku.voipio@movial.fi> + * + * 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 <linux/module.h> +#include <linux/i2c.h> +#include <linux/leds.h> +#include <linux/input.h> +#include <linux/mutex.h> +#include <linux/leds-pca9532.h> + +static const 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, + const struct i2c_device_id *id); +static int pca9532_remove(struct i2c_client *client); + +static const struct i2c_device_id pca9532_id[] = { + { "pca9532", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, pca9532_id); + +static struct i2c_driver pca9532_driver = { + .driver = { + .name = "pca9532", + }, + .probe = pca9532_probe, + .remove = pca9532_remove, + .id_table = pca9532_id, +}; + +/* 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<<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); + 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, + const struct i2c_device_id *id) +{ + 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 <riku.voipio@movial.fi>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("PCA 9532 LED dimmer"); + +module_init(pca9532_init); +module_exit(pca9532_exit); + diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c new file mode 100644 index 000000000000..f508729123b5 --- /dev/null +++ b/drivers/leds/leds-pca955x.c @@ -0,0 +1,390 @@ +/* + * Copyright 2007-2008 Extreme Engineering Solutions, Inc. + * + * Author: Nate Case <ncase@xes-inc.com> + * + * This file is subject to the terms and conditions of version 2 of + * the GNU General Public License. See the file COPYING in the main + * directory of this archive for more details. + * + * LED driver for various PCA955x I2C LED drivers + * + * Supported devices: + * + * Device Description 7-bit slave address + * ------ ----------- ------------------- + * PCA9550 2-bit driver 0x60 .. 0x61 + * PCA9551 8-bit driver 0x60 .. 0x67 + * PCA9552 16-bit driver 0x60 .. 0x67 + * PCA9553/01 4-bit driver 0x62 + * PCA9553/02 4-bit driver 0x63 + * + * Philips PCA955x LED driver chips follow a register map as shown below: + * + * Control Register Description + * ---------------- ----------- + * 0x0 Input register 0 + * .. + * NUM_INPUT_REGS - 1 Last Input register X + * + * NUM_INPUT_REGS Frequency prescaler 0 + * NUM_INPUT_REGS + 1 PWM register 0 + * NUM_INPUT_REGS + 2 Frequency prescaler 1 + * NUM_INPUT_REGS + 3 PWM register 1 + * + * NUM_INPUT_REGS + 4 LED selector 0 + * NUM_INPUT_REGS + 4 + * + NUM_LED_REGS - 1 Last LED selector + * + * where NUM_INPUT_REGS and NUM_LED_REGS vary depending on how many + * bits the chip supports. + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/ctype.h> +#include <linux/leds.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/workqueue.h> + +/* LED select registers determine the source that drives LED outputs */ +#define PCA955X_LS_LED_ON 0x0 /* Output LOW */ +#define PCA955X_LS_LED_OFF 0x1 /* Output HI-Z */ +#define PCA955X_LS_BLINK0 0x2 /* Blink at PWM0 rate */ +#define PCA955X_LS_BLINK1 0x3 /* Blink at PWM1 rate */ + +enum pca955x_type { + pca9550, + pca9551, + pca9552, + pca9553, +}; + +struct pca955x_chipdef { + int bits; + u8 slv_addr; /* 7-bit slave address mask */ + int slv_addr_shift; /* Number of bits to ignore */ +}; + +static struct pca955x_chipdef pca955x_chipdefs[] = { + [pca9550] = { + .bits = 2, + .slv_addr = /* 110000x */ 0x60, + .slv_addr_shift = 1, + }, + [pca9551] = { + .bits = 8, + .slv_addr = /* 1100xxx */ 0x60, + .slv_addr_shift = 3, + }, + [pca9552] = { + .bits = 16, + .slv_addr = /* 1100xxx */ 0x60, + .slv_addr_shift = 3, + }, + [pca9553] = { + .bits = 4, + .slv_addr = /* 110001x */ 0x62, + .slv_addr_shift = 1, + }, +}; + +static const struct i2c_device_id pca955x_id[] = { + { "pca9550", pca9550 }, + { "pca9551", pca9551 }, + { "pca9552", pca9552 }, + { "pca9553", pca9553 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pca955x_id); + +struct pca955x_led { + struct pca955x_chipdef *chipdef; + struct i2c_client *client; + struct work_struct work; + spinlock_t lock; + enum led_brightness brightness; + struct led_classdev led_cdev; + int led_num; /* 0 .. 15 potentially */ + char name[32]; +}; + +/* 8 bits per input register */ +static inline int pca95xx_num_input_regs(int bits) +{ + return (bits + 7) / 8; +} + +/* 4 bits per LED selector register */ +static inline int pca95xx_num_led_regs(int bits) +{ + return (bits + 3) / 4; +} + +/* + * Return an LED selector register value based on an existing one, with + * the appropriate 2-bit state value set for the given LED number (0-3). + */ +static inline u8 pca955x_ledsel(u8 oldval, int led_num, int state) +{ + return (oldval & (~(0x3 << (led_num << 1)))) | + ((state & 0x3) << (led_num << 1)); +} + +/* + * Write to frequency prescaler register, used to program the + * period of the PWM output. period = (PSCx + 1) / 38 + */ +static void pca955x_write_psc(struct i2c_client *client, int n, u8 val) +{ + struct pca955x_led *pca955x = i2c_get_clientdata(client); + + i2c_smbus_write_byte_data(client, + pca95xx_num_input_regs(pca955x->chipdef->bits) + 2*n, + val); +} + +/* + * Write to PWM register, which determines the duty cycle of the + * output. LED is OFF when the count is less than the value of this + * register, and ON when it is greater. If PWMx == 0, LED is always OFF. + * + * Duty cycle is (256 - PWMx) / 256 + */ +static void pca955x_write_pwm(struct i2c_client *client, int n, u8 val) +{ + struct pca955x_led *pca955x = i2c_get_clientdata(client); + + i2c_smbus_write_byte_data(client, + pca95xx_num_input_regs(pca955x->chipdef->bits) + 1 + 2*n, + val); +} + +/* + * Write to LED selector register, which determines the source that + * drives the LED output. + */ +static void pca955x_write_ls(struct i2c_client *client, int n, u8 val) +{ + struct pca955x_led *pca955x = i2c_get_clientdata(client); + + i2c_smbus_write_byte_data(client, + pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n, + val); +} + +/* + * Read the LED selector register, which determines the source that + * drives the LED output. + */ +static u8 pca955x_read_ls(struct i2c_client *client, int n) +{ + struct pca955x_led *pca955x = i2c_get_clientdata(client); + + return (u8) i2c_smbus_read_byte_data(client, + pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n); +} + +static void pca955x_led_work(struct work_struct *work) +{ + struct pca955x_led *pca955x; + u8 ls; + int chip_ls; /* which LSx to use (0-3 potentially) */ + int ls_led; /* which set of bits within LSx to use (0-3) */ + + pca955x = container_of(work, struct pca955x_led, work); + chip_ls = pca955x->led_num / 4; + ls_led = pca955x->led_num % 4; + + ls = pca955x_read_ls(pca955x->client, chip_ls); + + switch (pca955x->brightness) { + case LED_FULL: + ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_LED_ON); + break; + case LED_OFF: + ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_LED_OFF); + break; + case LED_HALF: + ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK0); + break; + default: + /* + * Use PWM1 for all other values. This has the unwanted + * side effect of making all LEDs on the chip share the + * same brightness level if set to a value other than + * OFF, HALF, or FULL. But, this is probably better than + * just turning off for all other values. + */ + pca955x_write_pwm(pca955x->client, 1, 255-pca955x->brightness); + ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK1); + break; + } + + pca955x_write_ls(pca955x->client, chip_ls, ls); +} + +void pca955x_led_set(struct led_classdev *led_cdev, enum led_brightness value) +{ + struct pca955x_led *pca955x; + + pca955x = container_of(led_cdev, struct pca955x_led, led_cdev); + + spin_lock(&pca955x->lock); + pca955x->brightness = value; + + /* + * Must use workqueue for the actual I/O since I2C operations + * can sleep. + */ + schedule_work(&pca955x->work); + + spin_unlock(&pca955x->lock); +} + +static int __devinit pca955x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pca955x_led *pca955x; + struct pca955x_chipdef *chip; + struct i2c_adapter *adapter; + struct led_platform_data *pdata; + int i, err; + + chip = &pca955x_chipdefs[id->driver_data]; + adapter = to_i2c_adapter(client->dev.parent); + pdata = client->dev.platform_data; + + /* Make sure the slave address / chip type combo given is possible */ + if ((client->addr & ~((1 << chip->slv_addr_shift) - 1)) != + chip->slv_addr) { + dev_err(&client->dev, "invalid slave address %02x\n", + client->addr); + return -ENODEV; + } + + printk(KERN_INFO "leds-pca955x: Using %s %d-bit LED driver at " + "slave address 0x%02x\n", + id->name, chip->bits, client->addr); + + if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) + return -EIO; + + if (pdata) { + if (pdata->num_leds != chip->bits) { + dev_err(&client->dev, "board info claims %d LEDs" + " on a %d-bit chip\n", + pdata->num_leds, chip->bits); + return -ENODEV; + } + } + + pca955x = kzalloc(sizeof(*pca955x) * chip->bits, GFP_KERNEL); + if (!pca955x) + return -ENOMEM; + + i2c_set_clientdata(client, pca955x); + + for (i = 0; i < chip->bits; i++) { + pca955x[i].chipdef = chip; + pca955x[i].client = client; + pca955x[i].led_num = i; + + /* Platform data can specify LED names and default triggers */ + if (pdata) { + if (pdata->leds[i].name) + snprintf(pca955x[i].name, + sizeof(pca955x[i].name), "pca955x:%s", + pdata->leds[i].name); + if (pdata->leds[i].default_trigger) + pca955x[i].led_cdev.default_trigger = + pdata->leds[i].default_trigger; + } else { + snprintf(pca955x[i].name, sizeof(pca955x[i].name), + "pca955x:%d", i); + } + + spin_lock_init(&pca955x[i].lock); + + pca955x[i].led_cdev.name = pca955x[i].name; + pca955x[i].led_cdev.brightness_set = pca955x_led_set; + + INIT_WORK(&pca955x[i].work, pca955x_led_work); + + err = led_classdev_register(&client->dev, &pca955x[i].led_cdev); + if (err < 0) + goto exit; + } + + /* Turn off LEDs */ + for (i = 0; i < pca95xx_num_led_regs(chip->bits); i++) + pca955x_write_ls(client, i, 0x55); + + /* PWM0 is used for half brightness or 50% duty cycle */ + pca955x_write_pwm(client, 0, 255-LED_HALF); + + /* PWM1 is used for variable brightness, default to OFF */ + pca955x_write_pwm(client, 1, 0); + + /* Set to fast frequency so we do not see flashing */ + pca955x_write_psc(client, 0, 0); + pca955x_write_psc(client, 1, 0); + + return 0; + +exit: + while (i--) { + led_classdev_unregister(&pca955x[i].led_cdev); + cancel_work_sync(&pca955x[i].work); + } + + kfree(pca955x); + i2c_set_clientdata(client, NULL); + + return err; +} + +static int __devexit pca955x_remove(struct i2c_client *client) +{ + struct pca955x_led *pca955x = i2c_get_clientdata(client); + int i; + + for (i = 0; i < pca955x->chipdef->bits; i++) { + led_classdev_unregister(&pca955x[i].led_cdev); + cancel_work_sync(&pca955x[i].work); + } + + kfree(pca955x); + i2c_set_clientdata(client, NULL); + + return 0; +} + +static struct i2c_driver pca955x_driver = { + .driver = { + .name = "leds-pca955x", + .owner = THIS_MODULE, + }, + .probe = pca955x_probe, + .remove = __devexit_p(pca955x_remove), + .id_table = pca955x_id, +}; + +static int __init pca955x_leds_init(void) +{ + return i2c_add_driver(&pca955x_driver); +} + +static void __exit pca955x_leds_exit(void) +{ + i2c_del_driver(&pca955x_driver); +} + +module_init(pca955x_leds_init); +module_exit(pca955x_leds_exit); + +MODULE_AUTHOR("Nate Case <ncase@xes-inc.com>"); +MODULE_DESCRIPTION("PCA955x LED driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/leds/leds-s3c24xx.c b/drivers/leds/leds-s3c24xx.c index d4f5021dccbf..25a07f2643ad 100644 --- a/drivers/leds/leds-s3c24xx.c +++ b/drivers/leds/leds-s3c24xx.c @@ -16,9 +16,9 @@ #include <linux/platform_device.h> #include <linux/leds.h> -#include <asm/hardware.h> -#include <asm/arch/regs-gpio.h> -#include <asm/arch/leds-gpio.h> +#include <mach/hardware.h> +#include <mach/regs-gpio.h> +#include <mach/leds-gpio.h> /* our context */ diff --git a/drivers/leds/leds-spitz.c b/drivers/leds/leds-spitz.c deleted file mode 100644 index e75e8543bc5a..000000000000 --- a/drivers/leds/leds-spitz.c +++ /dev/null @@ -1,131 +0,0 @@ -/* - * LED Triggers Core - * - * Copyright 2005-2006 Openedhand Ltd. - * - * Author: Richard Purdie <rpurdie@openedhand.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/platform_device.h> -#include <linux/leds.h> -#include <asm/hardware/scoop.h> -#include <asm/mach-types.h> -#include <asm/arch/hardware.h> -#include <asm/arch/pxa-regs.h> -#include <asm/arch/spitz.h> - -static void spitzled_amber_set(struct led_classdev *led_cdev, - enum led_brightness value) -{ - if (value) - set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_LED_ORANGE); - else - reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_LED_ORANGE); -} - -static void spitzled_green_set(struct led_classdev *led_cdev, - enum led_brightness value) -{ - if (value) - set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_LED_GREEN); - else - reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_LED_GREEN); -} - -static struct led_classdev spitz_amber_led = { - .name = "spitz:amber:charge", - .default_trigger = "sharpsl-charge", - .brightness_set = spitzled_amber_set, -}; - -static struct led_classdev spitz_green_led = { - .name = "spitz:green:hddactivity", - .default_trigger = "ide-disk", - .brightness_set = spitzled_green_set, -}; - -#ifdef CONFIG_PM -static int spitzled_suspend(struct platform_device *dev, pm_message_t state) -{ -#ifdef CONFIG_LEDS_TRIGGERS - if (spitz_amber_led.trigger && - strcmp(spitz_amber_led.trigger->name, "sharpsl-charge")) -#endif - led_classdev_suspend(&spitz_amber_led); - led_classdev_suspend(&spitz_green_led); - return 0; -} - -static int spitzled_resume(struct platform_device *dev) -{ - led_classdev_resume(&spitz_amber_led); - led_classdev_resume(&spitz_green_led); - return 0; -} -#endif - -static int spitzled_probe(struct platform_device *pdev) -{ - int ret; - - if (machine_is_akita()) { - spitz_green_led.name = "spitz:green:mail"; - spitz_green_led.default_trigger = "nand-disk"; - } - - ret = led_classdev_register(&pdev->dev, &spitz_amber_led); - if (ret < 0) - return ret; - - ret = led_classdev_register(&pdev->dev, &spitz_green_led); - if (ret < 0) - led_classdev_unregister(&spitz_amber_led); - - return ret; -} - -static int spitzled_remove(struct platform_device *pdev) -{ - led_classdev_unregister(&spitz_amber_led); - led_classdev_unregister(&spitz_green_led); - - return 0; -} - -static struct platform_driver spitzled_driver = { - .probe = spitzled_probe, - .remove = spitzled_remove, -#ifdef CONFIG_PM - .suspend = spitzled_suspend, - .resume = spitzled_resume, -#endif - .driver = { - .name = "spitz-led", - .owner = THIS_MODULE, - }, -}; - -static int __init spitzled_init(void) -{ - return platform_driver_register(&spitzled_driver); -} - -static void __exit spitzled_exit(void) -{ - platform_driver_unregister(&spitzled_driver); -} - -module_init(spitzled_init); -module_exit(spitzled_exit); - -MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>"); -MODULE_DESCRIPTION("Spitz LED driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:spitz-led"); diff --git a/drivers/leds/leds-sunfire.c b/drivers/leds/leds-sunfire.c new file mode 100644 index 000000000000..6b008f0c3f62 --- /dev/null +++ b/drivers/leds/leds-sunfire.c @@ -0,0 +1,273 @@ +/* leds-sunfire.c: SUNW,Ultra-Enterprise LED driver. + * + * Copyright (C) 2008 David S. Miller <davem@davemloft.net> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/leds.h> +#include <linux/io.h> +#include <linux/platform_device.h> + +#include <asm/fhc.h> +#include <asm/upa.h> + +#define DRIVER_NAME "leds-sunfire" +#define PFX DRIVER_NAME ": " + +MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); +MODULE_DESCRIPTION("Sun Fire LED driver"); +MODULE_LICENSE("GPL"); + +struct sunfire_led { + struct led_classdev led_cdev; + void __iomem *reg; +}; +#define to_sunfire_led(d) container_of(d, struct sunfire_led, led_cdev) + +static void __clockboard_set(struct led_classdev *led_cdev, + enum led_brightness led_val, u8 bit) +{ + struct sunfire_led *p = to_sunfire_led(led_cdev); + u8 reg = upa_readb(p->reg); + + switch (bit) { + case CLOCK_CTRL_LLED: + if (led_val) + reg &= ~bit; + else + reg |= bit; + break; + + default: + if (led_val) + reg |= bit; + else + reg &= ~bit; + break; + } + upa_writeb(reg, p->reg); +} + +static void clockboard_left_set(struct led_classdev *led_cdev, + enum led_brightness led_val) +{ + __clockboard_set(led_cdev, led_val, CLOCK_CTRL_LLED); +} + +static void clockboard_middle_set(struct led_classdev *led_cdev, + enum led_brightness led_val) +{ + __clockboard_set(led_cdev, led_val, CLOCK_CTRL_MLED); +} + +static void clockboard_right_set(struct led_classdev *led_cdev, + enum led_brightness led_val) +{ + __clockboard_set(led_cdev, led_val, CLOCK_CTRL_RLED); +} + +static void __fhc_set(struct led_classdev *led_cdev, + enum led_brightness led_val, u32 bit) +{ + struct sunfire_led *p = to_sunfire_led(led_cdev); + u32 reg = upa_readl(p->reg); + + switch (bit) { + case FHC_CONTROL_LLED: + if (led_val) + reg &= ~bit; + else + reg |= bit; + break; + + default: + if (led_val) + reg |= bit; + else + reg &= ~bit; + break; + } + upa_writel(reg, p->reg); +} + +static void fhc_left_set(struct led_classdev *led_cdev, + enum led_brightness led_val) +{ + __fhc_set(led_cdev, led_val, FHC_CONTROL_LLED); +} + +static void fhc_middle_set(struct led_classdev *led_cdev, + enum led_brightness led_val) +{ + __fhc_set(led_cdev, led_val, FHC_CONTROL_MLED); +} + +static void fhc_right_set(struct led_classdev *led_cdev, + enum led_brightness led_val) +{ + __fhc_set(led_cdev, led_val, FHC_CONTROL_RLED); +} + +typedef void (*set_handler)(struct led_classdev *, enum led_brightness); +struct led_type { + const char *name; + set_handler handler; + const char *default_trigger; +}; + +#define NUM_LEDS_PER_BOARD 3 +struct sunfire_drvdata { + struct sunfire_led leds[NUM_LEDS_PER_BOARD]; +}; + +static int __devinit sunfire_led_generic_probe(struct platform_device *pdev, + struct led_type *types) +{ + struct sunfire_drvdata *p; + int i, err = -EINVAL; + + if (pdev->num_resources != 1) { + printk(KERN_ERR PFX "Wrong number of resources %d, should be 1\n", + pdev->num_resources); + goto out; + } + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) { + printk(KERN_ERR PFX "Could not allocate struct sunfire_drvdata\n"); + goto out; + } + + for (i = 0; i < NUM_LEDS_PER_BOARD; i++) { + struct led_classdev *lp = &p->leds[i].led_cdev; + + p->leds[i].reg = (void __iomem *) pdev->resource[0].start; + lp->name = types[i].name; + lp->brightness = LED_FULL; + lp->brightness_set = types[i].handler; + lp->default_trigger = types[i].default_trigger; + + err = led_classdev_register(&pdev->dev, lp); + if (err) { + printk(KERN_ERR PFX "Could not register %s LED\n", + lp->name); + goto out_unregister_led_cdevs; + } + } + + dev_set_drvdata(&pdev->dev, p); + + err = 0; +out: + return err; + +out_unregister_led_cdevs: + for (i--; i >= 0; i--) + led_classdev_unregister(&p->leds[i].led_cdev); + goto out; +} + +static int __devexit sunfire_led_generic_remove(struct platform_device *pdev) +{ + struct sunfire_drvdata *p = dev_get_drvdata(&pdev->dev); + int i; + + for (i = 0; i < NUM_LEDS_PER_BOARD; i++) + led_classdev_unregister(&p->leds[i].led_cdev); + + kfree(p); + + return 0; +} + +static struct led_type clockboard_led_types[NUM_LEDS_PER_BOARD] = { + { + .name = "clockboard-left", + .handler = clockboard_left_set, + }, + { + .name = "clockboard-middle", + .handler = clockboard_middle_set, + }, + { + .name = "clockboard-right", + .handler = clockboard_right_set, + .default_trigger= "heartbeat", + }, +}; + +static int __devinit sunfire_clockboard_led_probe(struct platform_device *pdev) +{ + return sunfire_led_generic_probe(pdev, clockboard_led_types); +} + +static struct led_type fhc_led_types[NUM_LEDS_PER_BOARD] = { + { + .name = "fhc-left", + .handler = fhc_left_set, + }, + { + .name = "fhc-middle", + .handler = fhc_middle_set, + }, + { + .name = "fhc-right", + .handler = fhc_right_set, + .default_trigger= "heartbeat", + }, +}; + +static int __devinit sunfire_fhc_led_probe(struct platform_device *pdev) +{ + return sunfire_led_generic_probe(pdev, fhc_led_types); +} + +MODULE_ALIAS("platform:sunfire-clockboard-leds"); +MODULE_ALIAS("platform:sunfire-fhc-leds"); + +static struct platform_driver sunfire_clockboard_led_driver = { + .probe = sunfire_clockboard_led_probe, + .remove = __devexit_p(sunfire_led_generic_remove), + .driver = { + .name = "sunfire-clockboard-leds", + .owner = THIS_MODULE, + }, +}; + +static struct platform_driver sunfire_fhc_led_driver = { + .probe = sunfire_fhc_led_probe, + .remove = __devexit_p(sunfire_led_generic_remove), + .driver = { + .name = "sunfire-fhc-leds", + .owner = THIS_MODULE, + }, +}; + +static int __init sunfire_leds_init(void) +{ + int err = platform_driver_register(&sunfire_clockboard_led_driver); + + if (err) { + printk(KERN_ERR PFX "Could not register clock board LED driver\n"); + return err; + } + + err = platform_driver_register(&sunfire_fhc_led_driver); + if (err) { + printk(KERN_ERR PFX "Could not register FHC LED driver\n"); + platform_driver_unregister(&sunfire_clockboard_led_driver); + } + + return err; +} + +static void __exit sunfire_leds_exit(void) +{ + platform_driver_unregister(&sunfire_clockboard_led_driver); + platform_driver_unregister(&sunfire_fhc_led_driver); +} + +module_init(sunfire_leds_init); +module_exit(sunfire_leds_exit); |