summaryrefslogtreecommitdiffstats
path: root/drivers/leds
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/leds')
-rw-r--r--drivers/leds/Kconfig77
-rw-r--r--drivers/leds/Makefile16
-rw-r--r--drivers/leds/led-class.c167
-rw-r--r--drivers/leds/led-core.c25
-rw-r--r--drivers/leds/led-triggers.c239
-rw-r--r--drivers/leds/leds-corgi.c121
-rw-r--r--drivers/leds/leds-ixp4xx-gpio.c215
-rw-r--r--drivers/leds/leds-locomo.c95
-rw-r--r--drivers/leds/leds-spitz.c125
-rw-r--r--drivers/leds/leds-tosa.c131
-rw-r--r--drivers/leds/leds.h44
-rw-r--r--drivers/leds/ledtrig-ide-disk.c62
-rw-r--r--drivers/leds/ledtrig-timer.c170
13 files changed, 1487 insertions, 0 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
new file mode 100644
index 00000000000..2c4f20b7f02
--- /dev/null
+++ b/drivers/leds/Kconfig
@@ -0,0 +1,77 @@
+
+menu "LED devices"
+
+config NEW_LEDS
+ bool "LED Support"
+ help
+ Say Y to enable Linux LED support. This is not related to standard
+ keyboard LEDs which are controlled via the input system.
+
+config LEDS_CLASS
+ tristate "LED Class Support"
+ depends NEW_LEDS
+ help
+ This option enables the led sysfs class in /sys/class/leds. You'll
+ need this to do anything useful with LEDs. If unsure, say N.
+
+config LEDS_TRIGGERS
+ bool "LED Trigger support"
+ depends NEW_LEDS
+ help
+ This option enables trigger support for the leds class.
+ These triggers allow kernel events to drive the LEDs and can
+ be configured via sysfs. If unsure, say Y.
+
+config LEDS_CORGI
+ tristate "LED Support for the Sharp SL-C7x0 series"
+ depends 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 LEDS_CLASS && SHARP_LOCOMO
+ help
+ 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 LEDS_CLASS && PXA_SHARP_Cxx00
+ help
+ This option enables support for the LEDs on Sharp Zaurus
+ SL-Cxx00 series (C1000, C3000, C3100).
+
+config LEDS_IXP4XX
+ tristate "LED Support for GPIO connected LEDs on IXP4XX processors"
+ depends LEDS_CLASS && ARCH_IXP4XX
+ help
+ This option enables support for the LEDs connected to GPIO
+ outputs of the Intel IXP4XX processors. To be useful the
+ particular board must have LEDs and they must be connected
+ to the GPIO lines. If unsure, say Y.
+
+config LEDS_TOSA
+ tristate "LED Support for the Sharp SL-6000 series"
+ depends LEDS_CLASS && PXA_SHARPSL
+ help
+ This option enables support for the LEDs on Sharp Zaurus
+ SL-6000 series.
+
+config LEDS_TRIGGER_TIMER
+ tristate "LED Timer Trigger"
+ depends LEDS_TRIGGERS
+ help
+ This allows LEDs to be controlled by a programmable timer
+ via sysfs. If unsure, say Y.
+
+config LEDS_TRIGGER_IDE_DISK
+ bool "LED Timer Trigger"
+ depends LEDS_TRIGGERS && BLK_DEV_IDEDISK
+ help
+ This allows LEDs to be controlled by IDE disk activity.
+ If unsure, say Y.
+
+endmenu
+
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
new file mode 100644
index 00000000000..40699d3cabb
--- /dev/null
+++ b/drivers/leds/Makefile
@@ -0,0 +1,16 @@
+
+# LED Core
+obj-$(CONFIG_NEW_LEDS) += led-core.o
+obj-$(CONFIG_LEDS_CLASS) += led-class.o
+obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o
+
+# LED Platform Drivers
+obj-$(CONFIG_LEDS_CORGI) += leds-corgi.o
+obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o
+obj-$(CONFIG_LEDS_SPITZ) += leds-spitz.o
+obj-$(CONFIG_LEDS_IXP4XX) += leds-ixp4xx-gpio.o
+obj-$(CONFIG_LEDS_TOSA) += leds-tosa.o
+
+# LED Triggers
+obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o
+obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) += ledtrig-ide-disk.o
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
new file mode 100644
index 00000000000..b0b5d05fadd
--- /dev/null
+++ b/drivers/leds/led-class.c
@@ -0,0 +1,167 @@
+/*
+ * LED Class Core
+ *
+ * Copyright (C) 2005 John Lenz <lenz@cs.wisc.edu>
+ * Copyright (C) 2005-2006 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/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+#include <linux/sysdev.h>
+#include <linux/timer.h>
+#include <linux/err.h>
+#include <linux/leds.h>
+#include "leds.h"
+
+static struct class *leds_class;
+
+static ssize_t led_brightness_show(struct class_device *dev, char *buf)
+{
+ struct led_classdev *led_cdev = class_get_devdata(dev);
+ ssize_t ret = 0;
+
+ /* no lock needed for this */
+ sprintf(buf, "%u\n", led_cdev->brightness);
+ ret = strlen(buf) + 1;
+
+ return ret;
+}
+
+static ssize_t led_brightness_store(struct class_device *dev,
+ const char *buf, size_t size)
+{
+ struct led_classdev *led_cdev = class_get_devdata(dev);
+ ssize_t ret = -EINVAL;
+ char *after;
+ unsigned long state = simple_strtoul(buf, &after, 10);
+
+ if (after - buf > 0) {
+ ret = after - buf;
+ led_set_brightness(led_cdev, state);
+ }
+
+ return ret;
+}
+
+static CLASS_DEVICE_ATTR(brightness, 0644, led_brightness_show,
+ led_brightness_store);
+#ifdef CONFIG_LEDS_TRIGGERS
+static CLASS_DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store);
+#endif
+
+/**
+ * led_classdev_suspend - suspend an led_classdev.
+ * @led_cdev: the led_classdev to suspend.
+ */
+void led_classdev_suspend(struct led_classdev *led_cdev)
+{
+ led_cdev->flags |= LED_SUSPENDED;
+ led_cdev->brightness_set(led_cdev, 0);
+}
+EXPORT_SYMBOL_GPL(led_classdev_suspend);
+
+/**
+ * led_classdev_resume - resume an led_classdev.
+ * @led_cdev: the led_classdev to resume.
+ */
+void led_classdev_resume(struct led_classdev *led_cdev)
+{
+ led_cdev->brightness_set(led_cdev, led_cdev->brightness);
+ led_cdev->flags &= ~LED_SUSPENDED;
+}
+EXPORT_SYMBOL_GPL(led_classdev_resume);
+
+/**
+ * led_classdev_register - register a new object of led_classdev class.
+ * @dev: The device to register.
+ * @led_cdev: the led_classdev structure for this device.
+ */
+int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
+{
+ led_cdev->class_dev = class_device_create(leds_class, NULL, 0,
+ parent, "%s", led_cdev->name);
+ if (unlikely(IS_ERR(led_cdev->class_dev)))
+ return PTR_ERR(led_cdev->class_dev);
+
+ class_set_devdata(led_cdev->class_dev, led_cdev);
+
+ /* register the attributes */
+ class_device_create_file(led_cdev->class_dev,
+ &class_device_attr_brightness);
+
+ /* add to the list of leds */
+ write_lock(&leds_list_lock);
+ list_add_tail(&led_cdev->node, &leds_list);
+ write_unlock(&leds_list_lock);
+
+#ifdef CONFIG_LEDS_TRIGGERS
+ rwlock_init(&led_cdev->trigger_lock);
+
+ led_trigger_set_default(led_cdev);
+
+ class_device_create_file(led_cdev->class_dev,
+ &class_device_attr_trigger);
+#endif
+
+ printk(KERN_INFO "Registered led device: %s\n",
+ led_cdev->class_dev->class_id);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(led_classdev_register);
+
+/**
+ * led_classdev_unregister - unregisters a object of led_properties class.
+ * @led_cdev: the led device to unreigister
+ *
+ * Unregisters a previously registered via led_classdev_register object.
+ */
+void led_classdev_unregister(struct led_classdev *led_cdev)
+{
+ class_device_remove_file(led_cdev->class_dev,
+ &class_device_attr_brightness);
+#ifdef CONFIG_LEDS_TRIGGERS
+ class_device_remove_file(led_cdev->class_dev,
+ &class_device_attr_trigger);
+ write_lock(&led_cdev->trigger_lock);
+ if (led_cdev->trigger)
+ led_trigger_set(led_cdev, NULL);
+ write_unlock(&led_cdev->trigger_lock);
+#endif
+
+ class_device_unregister(led_cdev->class_dev);
+
+ write_lock(&leds_list_lock);
+ list_del(&led_cdev->node);
+ write_unlock(&leds_list_lock);
+}
+EXPORT_SYMBOL_GPL(led_classdev_unregister);
+
+static int __init leds_init(void)
+{
+ leds_class = class_create(THIS_MODULE, "leds");
+ if (IS_ERR(leds_class))
+ return PTR_ERR(leds_class);
+ return 0;
+}
+
+static void __exit leds_exit(void)
+{
+ class_destroy(leds_class);
+}
+
+subsys_initcall(leds_init);
+module_exit(leds_exit);
+
+MODULE_AUTHOR("John Lenz, Richard Purdie");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("LED Class Interface");
diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
new file mode 100644
index 00000000000..fe6541326c7
--- /dev/null
+++ b/drivers/leds/led-core.c
@@ -0,0 +1,25 @@
+/*
+ * LED Class 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/list.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/leds.h>
+#include "leds.h"
+
+rwlock_t leds_list_lock = RW_LOCK_UNLOCKED;
+LIST_HEAD(leds_list);
+
+EXPORT_SYMBOL_GPL(leds_list);
+EXPORT_SYMBOL_GPL(leds_list_lock);
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
new file mode 100644
index 00000000000..5e2cd8be119
--- /dev/null
+++ b/drivers/leds/led-triggers.c
@@ -0,0 +1,239 @@
+/*
+ * 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/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+#include <linux/sysdev.h>
+#include <linux/timer.h>
+#include <linux/leds.h>
+#include "leds.h"
+
+/*
+ * Nests outside led_cdev->trigger_lock
+ */
+static rwlock_t triggers_list_lock = RW_LOCK_UNLOCKED;
+static LIST_HEAD(trigger_list);
+
+ssize_t led_trigger_store(struct class_device *dev, const char *buf,
+ size_t count)
+{
+ struct led_classdev *led_cdev = class_get_devdata(dev);
+ char trigger_name[TRIG_NAME_MAX];
+ struct led_trigger *trig;
+ size_t len;
+
+ trigger_name[sizeof(trigger_name) - 1] = '\0';
+ strncpy(trigger_name, buf, sizeof(trigger_name) - 1);
+ len = strlen(trigger_name);
+
+ if (len && trigger_name[len - 1] == '\n')
+ trigger_name[len - 1] = '\0';
+
+ if (!strcmp(trigger_name, "none")) {
+ write_lock(&led_cdev->trigger_lock);
+ led_trigger_set(led_cdev, NULL);
+ write_unlock(&led_cdev->trigger_lock);
+ return count;
+ }
+
+ read_lock(&triggers_list_lock);
+ list_for_each_entry(trig, &trigger_list, next_trig) {
+ if (!strcmp(trigger_name, trig->name)) {
+ write_lock(&led_cdev->trigger_lock);
+ led_trigger_set(led_cdev, trig);
+ write_unlock(&led_cdev->trigger_lock);
+
+ read_unlock(&triggers_list_lock);
+ return count;
+ }
+ }
+ read_unlock(&triggers_list_lock);
+
+ return -EINVAL;
+}
+
+
+ssize_t led_trigger_show(struct class_device *dev, char *buf)
+{
+ struct led_classdev *led_cdev = class_get_devdata(dev);
+ struct led_trigger *trig;
+ int len = 0;
+
+ read_lock(&triggers_list_lock);
+ read_lock(&led_cdev->trigger_lock);
+
+ if (!led_cdev->trigger)
+ len += sprintf(buf+len, "[none] ");
+ else
+ len += sprintf(buf+len, "none ");
+
+ list_for_each_entry(trig, &trigger_list, next_trig) {
+ if (led_cdev->trigger && !strcmp(led_cdev->trigger->name,
+ trig->name))
+ len += sprintf(buf+len, "[%s] ", trig->name);
+ else
+ len += sprintf(buf+len, "%s ", trig->name);
+ }
+ read_unlock(&led_cdev->trigger_lock);
+ read_unlock(&triggers_list_lock);
+
+ len += sprintf(len+buf, "\n");
+ return len;
+}
+
+void led_trigger_event(struct led_trigger *trigger,
+ enum led_brightness brightness)
+{
+ struct list_head *entry;
+
+ if (!trigger)
+ return;
+
+ read_lock(&trigger->leddev_list_lock);
+ list_for_each(entry, &trigger->led_cdevs) {
+ struct led_classdev *led_cdev;
+
+ led_cdev = list_entry(entry, struct led_classdev, trig_list);
+ led_set_brightness(led_cdev, brightness);
+ }
+ read_unlock(&trigger->leddev_list_lock);
+}
+
+/* Caller must ensure led_cdev->trigger_lock held */
+void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger)
+{
+ unsigned long flags;
+
+ /* Remove any existing trigger */
+ if (led_cdev->trigger) {
+ write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags);
+ list_del(&led_cdev->trig_list);
+ write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock, flags);
+ if (led_cdev->trigger->deactivate)
+ led_cdev->trigger->deactivate(led_cdev);
+ }
+ 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);
+ if (trigger->activate)
+ trigger->activate(led_cdev);
+ }
+ led_cdev->trigger = trigger;
+}
+
+void led_trigger_set_default(struct led_classdev *led_cdev)
+{
+ struct led_trigger *trig;
+
+ if (!led_cdev->default_trigger)
+ return;
+
+ read_lock(&triggers_list_lock);
+ write_lock(&led_cdev->trigger_lock);
+ list_for_each_entry(trig, &trigger_list, next_trig) {
+ if (!strcmp(led_cdev->default_trigger, trig->name))
+ led_trigger_set(led_cdev, trig);
+ }
+ write_unlock(&led_cdev->trigger_lock);
+ read_unlock(&triggers_list_lock);
+}
+
+int led_trigger_register(struct led_trigger *trigger)
+{
+ struct led_classdev *led_cdev;
+
+ rwlock_init(&trigger->leddev_list_lock);
+ INIT_LIST_HEAD(&trigger->led_cdevs);
+
+ /* Add to the list of led triggers */
+ write_lock(&triggers_list_lock);
+ list_add_tail(&trigger->next_trig, &trigger_list);
+ write_unlock(&triggers_list_lock);
+
+ /* Register with any LEDs that have this as a default trigger */
+ read_lock(&leds_list_lock);
+ list_for_each_entry(led_cdev, &leds_list, node) {
+ write_lock(&led_cdev->trigger_lock);
+ if (!led_cdev->trigger && led_cdev->default_trigger &&
+ !strcmp(led_cdev->default_trigger, trigger->name))
+ led_trigger_set(led_cdev, trigger);
+ write_unlock(&led_cdev->trigger_lock);
+ }
+ read_unlock(&leds_list_lock);
+
+ return 0;
+}
+
+void led_trigger_register_simple(const char *name, struct led_trigger **tp)
+{
+ struct led_trigger *trigger;
+
+ trigger = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
+
+ if (trigger) {
+ trigger->name = name;
+ led_trigger_register(trigger);
+ }
+ *tp = trigger;
+}
+
+void led_trigger_unregister(struct led_trigger *trigger)
+{
+ struct led_classdev *led_cdev;
+
+ /* Remove from the list of led triggers */
+ write_lock(&triggers_list_lock);
+ list_del(&trigger->next_trig);
+ write_unlock(&triggers_list_lock);
+
+ /* Remove anyone actively using this trigger */
+ read_lock(&leds_list_lock);
+ list_for_each_entry(led_cdev, &leds_list, node) {
+ write_lock(&led_cdev->trigger_lock);
+ if (led_cdev->trigger == trigger)
+ led_trigger_set(led_cdev, NULL);
+ write_unlock(&led_cdev->trigger_lock);
+ }
+ read_unlock(&leds_list_lock);
+}
+
+void led_trigger_unregister_simple(struct led_trigger *trigger)
+{
+ led_trigger_unregister(trigger);
+ kfree(trigger);
+}
+
+/* Used by LED Class */
+EXPORT_SYMBOL_GPL(led_trigger_set);
+EXPORT_SYMBOL_GPL(led_trigger_set_default);
+EXPORT_SYMBOL_GPL(led_trigger_show);
+EXPORT_SYMBOL_GPL(led_trigger_store);
+
+/* LED Trigger Interface */
+EXPORT_SYMBOL_GPL(led_trigger_register);
+EXPORT_SYMBOL_GPL(led_trigger_unregister);
+
+/* Simple LED Tigger Interface */
+EXPORT_SYMBOL_GPL(led_trigger_register_simple);
+EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);
+EXPORT_SYMBOL_GPL(led_trigger_event);
+
+MODULE_AUTHOR("Richard Purdie");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("LED Triggers Core");
diff --git a/drivers/leds/leds-corgi.c b/drivers/leds/leds-corgi.c
new file mode 100644
index 00000000000..bb7d84df012
--- /dev/null
+++ b/drivers/leds/leds-corgi.c
@@ -0,0 +1,121 @@
+/*
+ * 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/config.h>
+#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",
+ .default_trigger = "sharpsl-charge",
+ .brightness_set = corgiled_amber_set,
+};
+
+static struct led_classdev corgi_green_led = {
+ .name = "corgi:green",
+ .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",
+ },
+};
+
+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");
diff --git a/drivers/leds/leds-ixp4xx-gpio.c b/drivers/leds/leds-ixp4xx-gpio.c
new file mode 100644
index 00000000000..30ced150e4c
--- /dev/null
+++ b/drivers/leds/leds-ixp4xx-gpio.c
@@ -0,0 +1,215 @@
+/*
+ * IXP4XX GPIO driver LED driver
+ *
+ * Author: John Bowler <jbowler@acm.org>
+ *
+ * Copyright (c) 2006 John Bowler
+ *
+ * Permission is hereby granted, free of charge, to any
+ * person obtaining a copy of this software and associated
+ * documentation files (the "Software"), to deal in the
+ * Software without restriction, including without
+ * limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the
+ * following conditions:
+ *
+ * The above copyright notice and this permission notice
+ * shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+ * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+ * SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/leds.h>
+#include <asm/arch/hardware.h>
+
+extern spinlock_t gpio_lock;
+
+/* Up to 16 gpio lines are possible. */
+#define GPIO_MAX 16
+static struct ixp4xxgpioled_device {
+ struct led_classdev ancestor;
+ int flags;
+} ixp4xxgpioled_devices[GPIO_MAX];
+
+void ixp4xxgpioled_brightness_set(struct led_classdev *pled,
+ enum led_brightness value)
+{
+ const struct ixp4xxgpioled_device *const ixp4xx_dev =
+ container_of(pled, struct ixp4xxgpioled_device, ancestor);
+ const u32 gpio_pin = ixp4xx_dev - ixp4xxgpioled_devices;
+
+ if (gpio_pin < GPIO_MAX && ixp4xx_dev->ancestor.name != 0) {
+ /* Set or clear the 'gpio_pin' bit according to the style
+ * and the required setting (value > 0 == on)
+ */
+ const int gpio_value =
+ (value > 0) == (ixp4xx_dev->flags != IXP4XX_GPIO_LOW) ?
+ IXP4XX_GPIO_HIGH : IXP4XX_GPIO_LOW;
+
+ {
+ unsigned long flags;
+ spin_lock_irqsave(&gpio_lock, flags);
+ gpio_line_set(gpio_pin, gpio_value);
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ }
+ }
+}
+
+/* LEDs are described in resources, the following iterates over the valid
+ * LED resources.
+ */
+#define for_all_leds(i, pdev) \
+ for (i=0; i<pdev->num_resources; ++i) \
+ if (pdev->resource[i].start < GPIO_MAX && \
+ pdev->resource[i].name != 0)
+
+/* The following applies 'operation' to each LED from the given platform,
+ * the function always returns 0 to allow tail call elimination.
+ */
+static int apply_to_all_leds(struct platform_device *pdev,
+ void (*operation)(struct led_classdev *pled))
+{
+ int i;
+
+ for_all_leds(i, pdev)
+ operation(&ixp4xxgpioled_devices[pdev->resource[i].start].ancestor);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int ixp4xxgpioled_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ return apply_to_all_leds(pdev, led_classdev_suspend);
+}
+
+static int ixp4xxgpioled_resume(struct platform_device *pdev)
+{
+ return apply_to_all_leds(pdev, led_classdev_resume);
+}
+#endif
+
+static void ixp4xxgpioled_remove_one_led(struct led_classdev *pled)
+{
+ led_classdev_unregister(pled);
+ pled->name = 0;
+}
+
+static int ixp4xxgpioled_remove(struct platform_device *pdev)
+{
+ return apply_to_all_leds(pdev, ixp4xxgpioled_remove_one_led);
+}
+
+static int ixp4xxgpioled_probe(struct platform_device *pdev)
+{
+ /* The board level has to tell the driver where the
+ * LEDs are connected - there is no way to find out
+ * electrically. It must also say whether the GPIO
+ * lines are active high or active low.
+ *
+ * To do this read the num_resources (the number of
+ * LEDs) and the struct resource (the data for each
+ * LED). The name comes from the resource, and it
+ * isn't copied.
+ */
+ int i;
+
+ for_all_leds(i, pdev) {
+ const u8 gpio_pin = pdev->resource[i].start;
+ int rc;
+
+ if (ixp4xxgpioled_devices[gpio_pin].ancestor.name == 0) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+ gpio_line_config(gpio_pin, IXP4XX_GPIO_OUT);
+ /* The config can, apparently, reset the state,
+ * I suspect the gpio line may be an input and
+ * the config may cause the line to be latched,
+ * so the setting depends on how the LED is
+ * connected to the line (which affects how it
+ * floats if not driven).
+ */
+ gpio_line_set(gpio_pin, IXP4XX_GPIO_HIGH);
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ ixp4xxgpioled_devices[gpio_pin].flags =
+ pdev->resource[i].flags & IORESOURCE_BITS;
+
+ ixp4xxgpioled_devices[gpio_pin].ancestor.name =
+ pdev->resource[i].name;
+
+ /* This is how a board manufacturer makes the LED
+ * come on on reset - the GPIO line will be high, so
+ * make the LED light when the line is low...
+ */
+ if (ixp4xxgpioled_devices[gpio_pin].flags != IXP4XX_GPIO_LOW)
+ ixp4xxgpioled_devices[gpio_pin].ancestor.brightness = 100;
+ else
+ ixp4xxgpioled_devices[gpio_pin].ancestor.brightness = 0;
+
+ ixp4xxgpioled_devices[gpio_pin].ancestor.flags = 0;
+
+ ixp4xxgpioled_devices[gpio_pin].ancestor.brightness_set =
+ ixp4xxgpioled_brightness_set;
+
+ ixp4xxgpioled_devices[gpio_pin].ancestor.default_trigger = 0;
+ }
+
+ rc = led_classdev_register(&pdev->dev,
+ &ixp4xxgpioled_devices[gpio_pin].ancestor);
+ if (rc < 0) {
+ ixp4xxgpioled_devices[gpio_pin].ancestor.name = 0;
+ ixp4xxgpioled_remove(pdev);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static struct platform_driver ixp4xxgpioled_driver = {
+ .probe = ixp4xxgpioled_probe,
+ .remove = ixp4xxgpioled_remove,
+#ifdef CONFIG_PM
+ .suspend = ixp4xxgpioled_suspend,
+ .resume = ixp4xxgpioled_resume,
+#endif
+ .driver = {
+ .name = "IXP4XX-GPIO-LED",
+ },
+};
+
+static int __init ixp4xxgpioled_init(void)
+{
+ return platform_driver_register(&ixp4xxgpioled_driver);
+}
+
+static void __exit ixp4xxgpioled_exit(void)
+{
+ platform_driver_unregister(&ixp4xxgpioled_driver);
+}
+
+module_init(ixp4xxgpioled_init);
+module_exit(ixp4xxgpioled_exit);
+
+MODULE_AUTHOR("John Bowler <jbowler@acm.org>");
+MODULE_DESCRIPTION("IXP4XX GPIO LED driver");
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/leds/leds-locomo.c b/drivers/leds/leds-locomo.c
new file mode 100644
index 00000000000..749a86c2adb
--- /dev/null
+++ b/drivers/leds/leds-locomo.c
@@ -0,0 +1,95 @@
+/*
+ * linux/drivers/leds/locomo.c
+ *
+ * Copyright (C) 2005 John Lenz <lenz@cs.wisc.edu>
+ *
+ * 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/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/leds.h>
+
+#include <asm/hardware.h>
+#include <asm/hardware/locomo.h>
+
+static void locomoled_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness value, int offset)
+{
+ struct locomo_dev *locomo_dev = LOCOMO_DEV(led_cdev->class_dev->dev);
+ unsigned long flags;
+
+ local_irq_save(flags);
+ if (value)
+ locomo_writel(LOCOMO_LPT_TOFH, locomo_dev->mapbase + offset);
+ else
+ locomo_writel(LOCOMO_LPT_TOFL, locomo_dev->mapbase + offset);
+ local_irq_restore(flags);
+}
+
+static void locomoled_brightness_set0(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ locomoled_brightness_set(led_cdev, value, LOCOMO_LPT0);
+}
+
+static void locomoled_brightness_set1(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ locomoled_brightness_set(led_cdev, value, LOCOMO_LPT1);
+}
+
+static struct led_classdev locomo_led0 = {
+ .name = "locomo:amber",
+ .brightness_set = locomoled_brightness_set0,
+};
+
+static struct led_classdev locomo_led1 = {
+ .name = "locomo:green",
+ .brightness_set = locomoled_brightness_set1,
+};
+
+static int locomoled_probe(struct locomo_dev *ldev)
+{
+ int ret;
+
+ ret = led_classdev_register(&ldev->dev, &locomo_led0);
+ if (ret < 0)
+ return ret;
+
+ ret = led_classdev_register(&ldev->dev, &locomo_led1);
+ if (ret < 0)
+ led_classdev_unregister(&locomo_led0);
+
+ return ret;
+}
+
+static int locomoled_remove(struct locomo_dev *dev)
+{
+ led_classdev_unregister(&locomo_led0);
+ led_classdev_unregister(&locomo_led1);
+ return 0;
+}
+
+static struct locomo_driver locomoled_driver = {
+ .drv = {
+ .name = "locomoled"
+ },
+ .devid = LOCOMO_DEVID_LED,
+ .probe = locomoled_probe,
+ .remove = locomoled_remove,
+};
+
+static int __init locomoled_init(void)
+{
+ return locomo_driver_register(&locomoled_driver);
+}
+module_init(locomoled_init);
+
+MODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>");
+MODULE_DESCRIPTION("Locomo LED driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-spitz.c b/drivers/leds/leds-spitz.c
new file mode 100644
index 00000000000..65bbef4a5e0
--- /dev/null
+++ b/drivers/leds/leds-spitz.c
@@ -0,0 +1,125 @@
+/*
+ * 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/config.h>
+#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",
+ .default_trigger = "sharpsl-charge",
+ .brightness_set = spitzled_amber_set,
+};
+
+static struct led_classdev spitz_green_led = {
+ .name = "spitz:green",
+ .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.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",
+ },
+};
+
+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");
diff --git a/drivers/leds/leds-tosa.c b/drivers/leds/leds-tosa.c
new file mode 100644
index 00000000000..c9e8cc1ec48
--- /dev/null
+++ b/drivers/leds/leds-tosa.c
@@ -0,0 +1,131 @@
+/*
+ * LED Triggers Core
+ *
+ * Copyright 2005 Dirk Opfer
+ *
+ * Author: Dirk Opfer <Dirk@Opfer-Online.de>
+ * based on spitz.c
+ *
+ * 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/config.h>
+#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/tosa.h>
+
+static void tosaled_amber_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ if (value)
+ set_scoop_gpio(&tosascoop_jc_device.dev,
+ TOSA_SCOOP_JC_CHRG_ERR_LED);
+ else
+ reset_scoop_gpio(&tosascoop_jc_device.dev,
+ TOSA_SCOOP_JC_CHRG_ERR_LED);
+}
+
+static void tosaled_green_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ if (value)
+ set_scoop_gpio(&tosascoop_jc_device.dev,
+ TOSA_SCOOP_JC_NOTE_LED);
+ else
+ reset_scoop_gpio(&tosascoop_jc_device.dev,
+ TOSA_SCOOP_JC_NOTE_LED);
+}
+
+static struct led_classdev tosa_amber_led = {
+ .name = "tosa:amber",
+ .default_trigger = "sharpsl-charge",
+ .brightness_set = tosaled_amber_set,
+};
+
+static struct led_classdev tosa_green_led = {
+ .name = "tosa:green",
+ .default_trigger = "nand-disk",
+ .brightness_set = tosaled_green_set,
+};
+
+#ifdef CONFIG_PM
+static int tosaled_suspend(struct platform_device *dev, pm_message_t state)
+{
+#ifdef CONFIG_LEDS_TRIGGERS
+ if (tosa_amber_led.trigger && strcmp(tosa_amber_led.trigger->name,
+ "sharpsl-charge"))
+#endif
+ led_classdev_suspend(&tosa_amber_led);
+ led_classdev_suspend(&tosa_green_led);
+ return 0;
+}
+
+static int tosaled_resume(struct platform_device *dev)
+{
+ led_classdev_resume(&tosa_amber_led);
+ led_classdev_resume(&tosa_green_led);
+ return 0;
+}
+#else
+#define tosaled_suspend NULL
+#define tosaled_resume NULL
+#endif
+
+static int tosaled_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ ret = led_classdev_register(&pdev->dev, &tosa_amber_led);
+ if (ret < 0)
+ return ret;
+
+ ret = led_classdev_register(&pdev->dev, &tosa_green_led);
+ if (ret < 0)
+ led_classdev_unregister(&tosa_amber_led);
+
+ return ret;
+}
+
+static int tosaled_remove(struct platform_device *pdev)
+{
+ led_classdev_unregister(&tosa_amber_led);
+ led_classdev_unregister(&tosa_green_led);
+
+ return 0;
+}
+
+static struct platform_driver tosaled_driver = {
+ .probe = tosaled_probe,
+ .remove = tosaled_remove,
+ .suspend = tosaled_suspend,
+ .resume = tosaled_resume,
+ .driver = {
+ .name = "tosa-led",
+ },
+};
+
+static int __init tosaled_init(void)
+{
+ return platform_driver_register(&tosaled_driver);
+}
+
+static void __exit tosaled_exit(void)
+{
+ platform_driver_unregister(&tosaled_driver);
+}
+
+module_init(tosaled_init);
+module_exit(tosaled_exit);
+
+MODULE_AUTHOR("Dirk Opfer <Dirk@Opfer-Online.de>");
+MODULE_DESCRIPTION("Tosa LED driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h
new file mode 100644
index 00000000000..a715c4ed93f
--- /dev/null
+++ b/drivers/leds/leds.h
@@ -0,0 +1,44 @@
+/*
+ * LED Core
+ *
+ * Copyright 2005 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.
+ *
+ */
+#ifndef __LEDS_H_INCLUDED
+#define __LEDS_H_INCLUDED
+
+#include <linux/leds.h>
+
+static inline void led_set_brightness(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ if (value > LED_FULL)
+ value = LED_FULL;
+ led_cdev->brightness = value;
+ if (!(led_cdev->flags & LED_SUSPENDED))
+ led_cdev->brightness_set(led_cdev, value);
+}
+
+extern rwlock_t leds_list_lock;
+extern struct list_head leds_list;
+
+#ifdef CONFIG_LEDS_TRIGGERS
+void led_trigger_set_default(struct led_classdev *led_cdev);
+void led_trigger_set(struct led_classdev *led_cdev,
+ struct led_trigger *trigger);
+#else
+#define led_trigger_set_default(x) do {} while(0)
+#define led_trigger_set(x, y) do {} while(0)
+#endif
+
+ssize_t led_trigger_store(struct class_device *dev, const char *buf,
+ size_t count);
+ssize_t led_trigger_show(struct class_device *dev, char *buf);
+
+#endif /* __LEDS_H_INCLUDED */
diff --git a/drivers/leds/ledtrig-ide-disk.c b/drivers/leds/ledtrig-ide-disk.c
new file mode 100644
index 00000000000..fa651886ab4
--- /dev/null
+++ b/drivers/leds/ledtrig-ide-disk.c
@@ -0,0 +1,62 @@
+/*
+ * LED IDE-Disk Activity Trigger
+ *
+ * Copyright 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/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/leds.h>
+
+static void ledtrig_ide_timerfunc(unsigned long data);
+
+DEFINE_LED_TRIGGER(ledtrig_ide);
+static DEFINE_TIMER(ledtrig_ide_timer, ledtrig_ide_timerfunc, 0, 0);
+static int ide_activity;
+static int ide_lastactivity;
+
+void ledtrig_ide_activity(void)
+{
+ ide_activity++;
+ if (!timer_pending(&ledtrig_ide_timer))
+ mod_timer(&ledtrig_ide_timer, jiffies + msecs_to_jiffies(10));
+}
+EXPORT_SYMBOL(ledtrig_ide_activity);
+
+static void ledtrig_ide_timerfunc(unsigned long data)
+{
+ if (ide_lastactivity != ide_activity) {
+ ide_lastactivity = ide_activity;
+ led_trigger_event(ledtrig_ide, LED_FULL);
+ mod_timer(&ledtrig_ide_timer, jiffies + msecs_to_jiffies(10));
+ } else {
+ led_trigger_event(ledtrig_ide, LED_OFF);
+ }
+}
+
+static int __init ledtrig_ide_init(void)
+{
+ led_trigger_register_simple("ide-disk", &ledtrig_ide);
+ return 0;
+}
+
+static void __exit ledtrig_ide_exit(void)
+{
+ led_trigger_unregister_simple(ledtrig_ide);
+}
+
+module_init(ledtrig_ide_init);
+module_exit(ledtrig_ide_exit);
+
+MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>");
+MODULE_DESCRIPTION("LED IDE Disk Activity Trigger");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/ledtrig-timer.c b/drivers/leds/ledtrig-timer.c
new file mode 100644
index 00000000000..f484b5d6dbf
--- /dev/null
+++ b/drivers/leds/ledtrig-timer.c
@@ -0,0 +1,170 @@
+/*
+ * LED Kernel Timer Trigger
+ *
+ * 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/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+#include <linux/sysdev.h>
+#include <linux/timer.h>
+#include <linux/leds.h>
+#include "leds.h"
+
+struct timer_trig_data {
+ unsigned long delay_on; /* milliseconds on */
+ unsigned long delay_off; /* milliseconds off */
+ struct timer_list timer;
+};
+
+static void led_timer_function(unsigned long data)
+{
+ struct led_classdev *led_cdev = (struct led_classdev *) data;
+ struct timer_trig_data *timer_data = led_cdev->trigger_data;
+ unsigned long brightness = LED_OFF;
+ unsigned long delay = timer_data->delay_off;
+
+ if (!timer_data->delay_on || !timer_data->delay_off) {
+ led_set_brightness(led_cdev, LED_OFF);
+ return;
+ }
+
+ if (!led_cdev->brightness) {
+ brightness = LED_FULL;
+ delay = timer_data->delay_on;
+ }
+
+ led_set_brightness(led_cdev, brightness);
+
+ mod_timer(&timer_data->timer, jiffies + msecs_to_jiffies(delay));
+}
+
+static ssize_t led_delay_on_show(struct class_device *dev, char *buf)
+{
+ struct led_classdev *led_cdev = class_get_devdata(dev);
+ struct timer_trig_data *timer_data = led_cdev->trigger_data;
+
+ sprintf(buf, "%lu\n", timer_data->delay_on);
+
+ return strlen(buf) + 1;
+}
+
+static ssize_t led_delay_on_store(struct class_device *dev, const char *buf,
+ size_t size)
+{
+ struct led_classdev *led_cdev = class_get_devdata(dev);
+ struct timer_trig_data *timer_data = led_cdev->trigger_data;
+ int ret = -EINVAL;
+ char *after;
+ unsigned long state = simple_strtoul(buf, &after, 10);
+
+ if (after - buf > 0) {
+ timer_data->delay_on = state;
+ mod_timer(&timer_data->timer, jiffies + 1);
+ ret = after - buf;
+ }
+
+ return ret;
+}
+
+static ssize_t led_delay_off_show(struct class_device *dev, char *buf)
+{
+ struct led_classdev *led_cdev = class_get_devdata(dev);
+ struct timer_trig_data *timer_data = led_cdev->trigger_data;
+
+ sprintf(buf, "%lu\n", timer_data->delay_off);
+
+ return strlen(buf) + 1;
+}
+
+static ssize_t led_delay_off_store(struct class_device *dev, const char *buf,
+ size_t size)
+{
+ struct led_classdev *led_cdev = class_get_devdata(dev);
+ struct timer_trig_data *timer_data = led_cdev->trigger_data;
+ int ret = -EINVAL;
+ char *after;
+ unsigned long state = simple_strtoul(buf, &after, 10);
+
+ if (after - buf > 0) {
+ timer_data->delay_off = state;
+ mod_timer(&timer_data->timer, jiffies + 1);
+ ret = after - buf;
+ }
+
+ return ret;
+}
+
+static CLASS_DEVICE_ATTR(delay_on, 0644, led_delay_on_show,
+ led_delay_on_store);
+static CLASS_DEVICE_ATTR(delay_off, 0644, led_delay_off_show,
+ led_delay_off_store);
+
+static void timer_trig_activate(struct led_classdev *led_cdev)
+{
+ struct timer_trig_data *timer_data;
+
+ timer_data = kzalloc(sizeof(struct timer_trig_data), GFP_KERNEL);
+ if (!timer_data)
+ return;
+
+ led_cdev->trigger_data = timer_data;
+
+ init_timer(&timer_data->timer);
+ timer_data->timer.function = led_timer_function;
+ timer_data->timer.data = (unsigned long) led_cdev;
+
+ class_device_create_file(led_cdev->class_dev,
+ &class_device_attr_delay_on);
+ class_device_create_file(led_cdev->class_dev,
+ &class_device_attr_delay_off);
+}
+
+static void timer_trig_deactivate(struct led_classdev *led_cdev)
+{
+ struct timer_trig_data *timer_data = led_cdev->trigger_data;
+
+ if (timer_data) {
+ class_device_remove_file(led_cdev->class_dev,
+ &class_device_attr_delay_on);
+ class_device_remove_file(led_cdev->class_dev,
+ &class_device_attr_delay_off);
+ del_timer_sync(&timer_data->timer);
+ kfree(timer_data);
+ }
+}
+
+static struct led_trigger timer_led_trigger = {
+ .name = "timer",
+ .activate = timer_trig_activate,
+ .deactivate = timer_trig_deactivate,
+};
+
+static int __init timer_trig_init(void)
+{
+ return led_trigger_register(&timer_led_trigger);
+}
+
+static void __exit timer_trig_exit(void)
+{
+ led_trigger_unregister(&timer_led_trigger);
+}
+
+module_init(timer_trig_init);
+module_exit(timer_trig_exit);
+
+MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>");
+MODULE_DESCRIPTION("Timer LED trigger");
+MODULE_LICENSE("GPL");