Pārlūkot izejas kodu

Merge branch 'for-linus' of git://git.o-hand.com/linux-rpurdie-leds

* 'for-linus' of git://git.o-hand.com/linux-rpurdie-leds:
  leds: Add default-on trigger
  leds: Document the context brightness_set needs
  leds: Add new driver for the LEDs on the Freecom FSG-3
  leds: Add support to leds with readable status
  leds: enable support for blink_set() platform hook in leds-gpio
  leds: Cleanup various whitespace and code style issues
  leds: disable triggers on brightness set
  leds: Add mail LED support for "Clevo D400P"
Linus Torvalds 17 gadi atpakaļ
vecāks
revīzija
eb855fd8e7

+ 9 - 3
Documentation/leds-class.txt

@@ -19,6 +19,12 @@ optimises away.
 
 Complex triggers whilst available to all LEDs have LED specific
 parameters and work on a per LED basis. The timer trigger is an example.
+The timer trigger will periodically change the LED brightness between
+LED_OFF and the current brightness setting. The "on" and "off" time can
+be specified via /sys/class/leds/<device>/delay_{on,off} in milliseconds.
+You can change the brightness value of a LED independently of the timer
+trigger. However, if you set the brightness value to LED_OFF it will
+also disable the timer trigger.
 
 You can change triggers in a similar manner to the way an IO scheduler
 is chosen (via /sys/class/leds/<device>/trigger). Trigger specific
@@ -63,9 +69,9 @@ value if it is called with *delay_on==0 && *delay_off==0 parameters. In
 this case the driver should give back the chosen value through delay_on
 and delay_off parameters to the leds subsystem.
 
-Any call to the brightness_set() callback function should cancel the
-previously programmed hardware blinking function so setting the brightness
-to 0 can also cancel the blinking of the LED.
+Setting the brightness to zero with brightness_set() callback function
+should completely turn off the LED and cancel the previously programmed
+hardware blinking function, if any.
 
 
 Known Issues

+ 17 - 0
drivers/leds/Kconfig

@@ -65,6 +65,12 @@ config LEDS_NET48XX
 	  This option enables support for the Soekris net4801 and net4826 error
 	  LED.
 
+config LEDS_FSG
+	tristate "LED Support for the Freecom FSG-3"
+	depends on LEDS_CLASS && MACH_FSG
+	help
+	  This option enables support for the LEDs on the Freecom FSG-3.
+
 config LEDS_WRAP
 	tristate "LED Support for the WRAP series LEDs"
 	depends on LEDS_CLASS && SCx200_GPIO
@@ -127,6 +133,7 @@ config LEDS_CLEVO_MAIL
 
 	  This module can drive the mail LED for the following notebooks:
 
+	  	Clevo D400P
 	  	Clevo D410J
 	  	Clevo D410V
 	  	Clevo D400V/D470V (not tested, but might work)
@@ -134,6 +141,9 @@ config LEDS_CLEVO_MAIL
 	  	Clevo M5x0N (not tested, but might work)
 	  	Positivo Mobile (Clevo M5x0V)
 
+	  If your model is not listed here you can try the "nodetect"
+	  module paramter.
+
 	  To compile this driver as a module, choose M here: the
 	  module will be called leds-clevo-mail.
 
@@ -173,4 +183,11 @@ config LEDS_TRIGGER_HEARTBEAT
 	  load average.
 	  If unsure, say Y.
 
+config LEDS_TRIGGER_DEFAULT_ON
+	tristate "LED Default ON Trigger"
+	depends on LEDS_TRIGGERS
+	help
+	  This allows LEDs to be initialised in the ON state.
+	  If unsure, say Y.
+
 endif # NEW_LEDS

+ 2 - 0
drivers/leds/Makefile

@@ -20,8 +20,10 @@ 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
 
 # LED Triggers
 obj-$(CONFIG_LEDS_TRIGGER_TIMER)	+= ledtrig-timer.o
 obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK)	+= ledtrig-ide-disk.o
 obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT)	+= ledtrig-heartbeat.o
+obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON)	+= ledtrig-default-on.o

+ 12 - 0
drivers/leds/led-class.c

@@ -24,6 +24,12 @@
 
 static struct class *leds_class;
 
+static void led_update_brightness(struct led_classdev *led_cdev)
+{
+	if (led_cdev->brightness_get)
+		led_cdev->brightness = led_cdev->brightness_get(led_cdev);
+}
+
 static ssize_t led_brightness_show(struct device *dev, 
 		struct device_attribute *attr, char *buf)
 {
@@ -31,6 +37,7 @@ static ssize_t led_brightness_show(struct device *dev,
 	ssize_t ret = 0;
 
 	/* no lock needed for this */
+	led_update_brightness(led_cdev);
 	sprintf(buf, "%u\n", led_cdev->brightness);
 	ret = strlen(buf) + 1;
 
@@ -51,6 +58,9 @@ static ssize_t led_brightness_store(struct device *dev,
 
 	if (count == size) {
 		ret = count;
+
+		if (state == LED_OFF)
+			led_trigger_remove(led_cdev);
 		led_set_brightness(led_cdev, state);
 	}
 
@@ -110,6 +120,8 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
 	list_add_tail(&led_cdev->node, &leds_list);
 	up_write(&leds_list_lock);
 
+	led_update_brightness(led_cdev);
+
 #ifdef CONFIG_LEDS_TRIGGERS
 	init_rwsem(&led_cdev->trigger_lock);
 

+ 2 - 2
drivers/leds/led-core.c

@@ -19,7 +19,7 @@
 #include "leds.h"
 
 DECLARE_RWSEM(leds_list_lock);
-LIST_HEAD(leds_list);
+EXPORT_SYMBOL_GPL(leds_list_lock);
 
+LIST_HEAD(leds_list);
 EXPORT_SYMBOL_GPL(leds_list);
-EXPORT_SYMBOL_GPL(leds_list_lock);

+ 63 - 57
drivers/leds/led-triggers.c

@@ -29,6 +29,8 @@
 static DECLARE_RWSEM(triggers_list_lock);
 static LIST_HEAD(trigger_list);
 
+ /* Used by LED Class */
+
 ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
 		const char *buf, size_t count)
 {
@@ -45,9 +47,7 @@ ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
 		trigger_name[len - 1] = '\0';
 
 	if (!strcmp(trigger_name, "none")) {
-		down_write(&led_cdev->trigger_lock);
-		led_trigger_set(led_cdev, NULL);
-		up_write(&led_cdev->trigger_lock);
+		led_trigger_remove(led_cdev);
 		return count;
 	}
 
@@ -66,7 +66,7 @@ ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
 
 	return -EINVAL;
 }
-
+EXPORT_SYMBOL_GPL(led_trigger_store);
 
 ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
 		char *buf)
@@ -96,24 +96,7 @@ ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
 	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);
-}
+EXPORT_SYMBOL_GPL(led_trigger_show);
 
 /* Caller must ensure led_cdev->trigger_lock held */
 void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger)
@@ -124,7 +107,8 @@ void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *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);
+		write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock,
+			flags);
 		if (led_cdev->trigger->deactivate)
 			led_cdev->trigger->deactivate(led_cdev);
 		led_set_brightness(led_cdev, LED_OFF);
@@ -138,6 +122,15 @@ void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger)
 	}
 	led_cdev->trigger = trigger;
 }
+EXPORT_SYMBOL_GPL(led_trigger_set);
+
+void led_trigger_remove(struct led_classdev *led_cdev)
+{
+	down_write(&led_cdev->trigger_lock);
+	led_trigger_set(led_cdev, NULL);
+	up_write(&led_cdev->trigger_lock);
+}
+EXPORT_SYMBOL_GPL(led_trigger_remove);
 
 void led_trigger_set_default(struct led_classdev *led_cdev)
 {
@@ -155,6 +148,9 @@ void led_trigger_set_default(struct led_classdev *led_cdev)
 	up_write(&led_cdev->trigger_lock);
 	up_read(&triggers_list_lock);
 }
+EXPORT_SYMBOL_GPL(led_trigger_set_default);
+
+/* LED Trigger Interface */
 
 int led_trigger_register(struct led_trigger *trigger)
 {
@@ -181,26 +177,7 @@ int led_trigger_register(struct led_trigger *trigger)
 
 	return 0;
 }
-
-void led_trigger_register_simple(const char *name, struct led_trigger **tp)
-{
-	struct led_trigger *trigger;
-	int err;
-
-	trigger = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
-
-	if (trigger) {
-		trigger->name = name;
-		err = led_trigger_register(trigger);
-		if (err < 0)
-			printk(KERN_WARNING "LED trigger %s failed to register"
-				" (%d)\n", name, err);
-	} else
-		printk(KERN_WARNING "LED trigger %s failed to register"
-			" (no memory)\n", name);
-
-	*tp = trigger;
-}
+EXPORT_SYMBOL_GPL(led_trigger_register);
 
 void led_trigger_unregister(struct led_trigger *trigger)
 {
@@ -221,6 +198,49 @@ void led_trigger_unregister(struct led_trigger *trigger)
 	}
 	up_read(&leds_list_lock);
 }
+EXPORT_SYMBOL_GPL(led_trigger_unregister);
+
+/* Simple LED Tigger Interface */
+
+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);
+}
+EXPORT_SYMBOL_GPL(led_trigger_event);
+
+void led_trigger_register_simple(const char *name, struct led_trigger **tp)
+{
+	struct led_trigger *trigger;
+	int err;
+
+	trigger = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
+
+	if (trigger) {
+		trigger->name = name;
+		err = led_trigger_register(trigger);
+		if (err < 0)
+			printk(KERN_WARNING "LED trigger %s failed to register"
+				" (%d)\n", name, err);
+	} else
+		printk(KERN_WARNING "LED trigger %s failed to register"
+			" (no memory)\n", name);
+
+	*tp = trigger;
+}
+EXPORT_SYMBOL_GPL(led_trigger_register_simple);
 
 void led_trigger_unregister_simple(struct led_trigger *trigger)
 {
@@ -228,21 +248,7 @@ 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");

+ 13 - 3
drivers/leds/leds-clevo-mail.c

@@ -14,7 +14,7 @@
 #define CLEVO_MAIL_LED_BLINK_1HZ	0x008A
 #define CLEVO_MAIL_LED_BLINK_0_5HZ	0x0083
 
-MODULE_AUTHOR("Márton Németh <nm127@freemail.hu>");
+MODULE_AUTHOR("Márton Németh <nm127@freemail.hu>");
 MODULE_DESCRIPTION("Clevo mail LED driver");
 MODULE_LICENSE("GPL");
 
@@ -67,6 +67,16 @@ static struct dmi_system_id __initdata mail_led_whitelist[] = {
 			DMI_MATCH(DMI_PRODUCT_VERSION, "VT6198")
 		}
 	},
+	{
+		.callback = clevo_mail_led_dmi_callback,
+		.ident = "Clevo D400P",
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "Clevo"),
+			DMI_MATCH(DMI_BOARD_NAME, "D400P"),
+			DMI_MATCH(DMI_BOARD_VERSION, "Rev.A"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "0106")
+		}
+	},
 	{
 		.callback = clevo_mail_led_dmi_callback,
 		.ident = "Clevo D410V",
@@ -93,8 +103,8 @@ static void clevo_mail_led_set(struct led_classdev *led_cdev,
 }
 
 static int clevo_mail_led_blink(struct led_classdev *led_cdev,
-				unsigned long* delay_on,
-				unsigned long* delay_off)
+				unsigned long *delay_on,
+				unsigned long *delay_off)
 {
 	int status = -EINVAL;
 

+ 1 - 1
drivers/leds/leds-cobalt-qube.c

@@ -18,7 +18,7 @@ static void __iomem *led_port;
 static u8 led_value;
 
 static void qube_front_led_set(struct led_classdev *led_cdev,
-                               enum led_brightness brightness)
+			       enum led_brightness brightness)
 {
 	if (brightness)
 		led_value = LED_FRONT_LEFT | LED_FRONT_RIGHT;

+ 3 - 3
drivers/leds/leds-cobalt-raq.c

@@ -15,7 +15,7 @@
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 #include <linux/init.h>
 #include <linux/io.h>
@@ -33,7 +33,7 @@ static u8 led_value;
 static DEFINE_SPINLOCK(led_value_lock);
 
 static void raq_web_led_set(struct led_classdev *led_cdev,
-                            enum led_brightness brightness)
+			    enum led_brightness brightness)
 {
 	unsigned long flags;
 
@@ -54,7 +54,7 @@ static struct led_classdev raq_web_led = {
 };
 
 static void raq_power_off_led_set(struct led_classdev *led_cdev,
-                                  enum led_brightness brightness)
+				  enum led_brightness brightness)
 {
 	unsigned long flags;
 

+ 7 - 4
drivers/leds/leds-corgi.c

@@ -21,7 +21,8 @@
 #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)
+static void corgiled_amber_set(struct led_classdev *led_cdev,
+			       enum led_brightness value)
 {
 	if (value)
 		GPSR0 = GPIO_bit(CORGI_GPIO_LED_ORANGE);
@@ -29,7 +30,8 @@ static void corgiled_amber_set(struct led_classdev *led_cdev, enum led_brightnes
 		GPCR0 = GPIO_bit(CORGI_GPIO_LED_ORANGE);
 }
 
-static void corgiled_green_set(struct led_classdev *led_cdev, enum led_brightness value)
+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);
@@ -53,7 +55,8 @@ static struct led_classdev corgi_green_led = {
 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"))
+	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);
@@ -110,7 +113,7 @@ static int __init corgiled_init(void)
 
 static void __exit corgiled_exit(void)
 {
- 	platform_driver_unregister(&corgiled_driver);
+	platform_driver_unregister(&corgiled_driver);
 }
 
 module_init(corgiled_init);

+ 261 - 0
drivers/leds/leds-fsg.c

@@ -0,0 +1,261 @@
+/*
+ * LED Driver for the Freecom FSG-3
+ *
+ * Copyright (c) 2008 Rod Whitby <rod@whitby.id.au>
+ *
+ * Author: Rod Whitby <rod@whitby.id.au>
+ *
+ * Based on leds-spitz.c
+ * 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/arch/hardware.h>
+#include <asm/io.h>
+
+static short __iomem *latch_address;
+static unsigned short latch_value;
+
+
+static void fsg_led_wlan_set(struct led_classdev *led_cdev,
+			     enum led_brightness value)
+{
+	if (value) {
+		latch_value &= ~(1 << FSG_LED_WLAN_BIT);
+		*latch_address = latch_value;
+	} else {
+		latch_value |=  (1 << FSG_LED_WLAN_BIT);
+		*latch_address = latch_value;
+	}
+}
+
+static void fsg_led_wan_set(struct led_classdev *led_cdev,
+			    enum led_brightness value)
+{
+	if (value) {
+		latch_value &= ~(1 << FSG_LED_WAN_BIT);
+		*latch_address = latch_value;
+	} else {
+		latch_value |=  (1 << FSG_LED_WAN_BIT);
+		*latch_address = latch_value;
+	}
+}
+
+static void fsg_led_sata_set(struct led_classdev *led_cdev,
+			     enum led_brightness value)
+{
+	if (value) {
+		latch_value &= ~(1 << FSG_LED_SATA_BIT);
+		*latch_address = latch_value;
+	} else {
+		latch_value |=  (1 << FSG_LED_SATA_BIT);
+		*latch_address = latch_value;
+	}
+}
+
+static void fsg_led_usb_set(struct led_classdev *led_cdev,
+			    enum led_brightness value)
+{
+	if (value) {
+		latch_value &= ~(1 << FSG_LED_USB_BIT);
+		*latch_address = latch_value;
+	} else {
+		latch_value |=  (1 << FSG_LED_USB_BIT);
+		*latch_address = latch_value;
+	}
+}
+
+static void fsg_led_sync_set(struct led_classdev *led_cdev,
+			     enum led_brightness value)
+{
+	if (value) {
+		latch_value &= ~(1 << FSG_LED_SYNC_BIT);
+		*latch_address = latch_value;
+	} else {
+		latch_value |=  (1 << FSG_LED_SYNC_BIT);
+		*latch_address = latch_value;
+	}
+}
+
+static void fsg_led_ring_set(struct led_classdev *led_cdev,
+			     enum led_brightness value)
+{
+	if (value) {
+		latch_value &= ~(1 << FSG_LED_RING_BIT);
+		*latch_address = latch_value;
+	} else {
+		latch_value |=  (1 << FSG_LED_RING_BIT);
+		*latch_address = latch_value;
+	}
+}
+
+
+
+static struct led_classdev fsg_wlan_led = {
+	.name			= "fsg:blue:wlan",
+	.brightness_set		= fsg_led_wlan_set,
+};
+
+static struct led_classdev fsg_wan_led = {
+	.name			= "fsg:blue:wan",
+	.brightness_set		= fsg_led_wan_set,
+};
+
+static struct led_classdev fsg_sata_led = {
+	.name			= "fsg:blue:sata",
+	.brightness_set		= fsg_led_sata_set,
+};
+
+static struct led_classdev fsg_usb_led = {
+	.name			= "fsg:blue:usb",
+	.brightness_set		= fsg_led_usb_set,
+};
+
+static struct led_classdev fsg_sync_led = {
+	.name			= "fsg:blue:sync",
+	.brightness_set		= fsg_led_sync_set,
+};
+
+static struct led_classdev fsg_ring_led = {
+	.name			= "fsg:blue:ring",
+	.brightness_set		= fsg_led_ring_set,
+};
+
+
+
+#ifdef CONFIG_PM
+static int fsg_led_suspend(struct platform_device *dev, pm_message_t state)
+{
+	led_classdev_suspend(&fsg_wlan_led);
+	led_classdev_suspend(&fsg_wan_led);
+	led_classdev_suspend(&fsg_sata_led);
+	led_classdev_suspend(&fsg_usb_led);
+	led_classdev_suspend(&fsg_sync_led);
+	led_classdev_suspend(&fsg_ring_led);
+	return 0;
+}
+
+static int fsg_led_resume(struct platform_device *dev)
+{
+	led_classdev_resume(&fsg_wlan_led);
+	led_classdev_resume(&fsg_wan_led);
+	led_classdev_resume(&fsg_sata_led);
+	led_classdev_resume(&fsg_usb_led);
+	led_classdev_resume(&fsg_sync_led);
+	led_classdev_resume(&fsg_ring_led);
+	return 0;
+}
+#endif
+
+
+static int fsg_led_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	ret = led_classdev_register(&pdev->dev, &fsg_wlan_led);
+	if (ret < 0)
+		goto failwlan;
+
+	ret = led_classdev_register(&pdev->dev, &fsg_wan_led);
+	if (ret < 0)
+		goto failwan;
+
+	ret = led_classdev_register(&pdev->dev, &fsg_sata_led);
+	if (ret < 0)
+		goto failsata;
+
+	ret = led_classdev_register(&pdev->dev, &fsg_usb_led);
+	if (ret < 0)
+		goto failusb;
+
+	ret = led_classdev_register(&pdev->dev, &fsg_sync_led);
+	if (ret < 0)
+		goto failsync;
+
+	ret = led_classdev_register(&pdev->dev, &fsg_ring_led);
+	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:
+	led_classdev_unregister(&fsg_usb_led);
+ failusb:
+	led_classdev_unregister(&fsg_sata_led);
+ failsata:
+	led_classdev_unregister(&fsg_wan_led);
+ failwan:
+	led_classdev_unregister(&fsg_wlan_led);
+ failwlan:
+
+	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);
+	led_classdev_unregister(&fsg_usb_led);
+	led_classdev_unregister(&fsg_sync_led);
+	led_classdev_unregister(&fsg_ring_led);
+
+	return 0;
+}
+
+
+static struct platform_driver fsg_led_driver = {
+	.probe		= fsg_led_probe,
+	.remove		= fsg_led_remove,
+#ifdef CONFIG_PM
+	.suspend	= fsg_led_suspend,
+	.resume		= fsg_led_resume,
+#endif
+	.driver		= {
+		.name		= "fsg-led",
+	},
+};
+
+
+static int __init fsg_led_init(void)
+{
+	return platform_driver_register(&fsg_led_driver);
+}
+
+static void __exit fsg_led_exit(void)
+{
+	platform_driver_unregister(&fsg_led_driver);
+}
+
+
+module_init(fsg_led_init);
+module_exit(fsg_led_exit);
+
+MODULE_AUTHOR("Rod Whitby <rod@whitby.id.au>");
+MODULE_DESCRIPTION("Freecom FSG-3 LED driver");
+MODULE_LICENSE("GPL");

+ 15 - 0
drivers/leds/leds-gpio.c

@@ -24,6 +24,8 @@ struct gpio_led_data {
 	u8 new_level;
 	u8 can_sleep;
 	u8 active_low;
+	int (*platform_gpio_blink_set)(unsigned gpio,
+			unsigned long *delay_on, unsigned long *delay_off);
 };
 
 static void gpio_led_work(struct work_struct *work)
@@ -60,6 +62,15 @@ static void gpio_led_set(struct led_classdev *led_cdev,
 		gpio_set_value(led_dat->gpio, level);
 }
 
+static int gpio_blink_set(struct led_classdev *led_cdev,
+	unsigned long *delay_on, unsigned long *delay_off)
+{
+	struct gpio_led_data *led_dat =
+		container_of(led_cdev, struct gpio_led_data, cdev);
+
+	return led_dat->platform_gpio_blink_set(led_dat->gpio, delay_on, delay_off);
+}
+
 static int gpio_led_probe(struct platform_device *pdev)
 {
 	struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
@@ -88,6 +99,10 @@ static int gpio_led_probe(struct platform_device *pdev)
 		led_dat->gpio = cur_led->gpio;
 		led_dat->can_sleep = gpio_cansleep(cur_led->gpio);
 		led_dat->active_low = cur_led->active_low;
+		if (pdata->gpio_blink_set) {
+			led_dat->platform_gpio_blink_set = pdata->gpio_blink_set;
+			led_dat->cdev.blink_set = gpio_blink_set;
+		}
 		led_dat->cdev.brightness_set = gpio_led_set;
 		led_dat->cdev.brightness = LED_OFF;
 

+ 32 - 32
drivers/leds/leds-h1940.c

@@ -26,20 +26,20 @@
 void h1940_greenled_set(struct led_classdev *led_dev, enum led_brightness value)
 {
 	switch (value) {
-		case LED_HALF:
-			h1940_latch_control(0,H1940_LATCH_LED_FLASH);
-			s3c2410_gpio_setpin(S3C2410_GPA7,1);
-			break;
-		case LED_FULL:
-			h1940_latch_control(0,H1940_LATCH_LED_GREEN);
-			s3c2410_gpio_setpin(S3C2410_GPA7,1);
-			break;
-		default:
-		case LED_OFF:
-			h1940_latch_control(H1940_LATCH_LED_FLASH,0);
-			h1940_latch_control(H1940_LATCH_LED_GREEN,0);
-			s3c2410_gpio_setpin(S3C2410_GPA7,0);
-			break;
+	case LED_HALF:
+		h1940_latch_control(0, H1940_LATCH_LED_FLASH);
+		s3c2410_gpio_setpin(S3C2410_GPA7, 1);
+		break;
+	case LED_FULL:
+		h1940_latch_control(0, H1940_LATCH_LED_GREEN);
+		s3c2410_gpio_setpin(S3C2410_GPA7, 1);
+		break;
+	default:
+	case LED_OFF:
+		h1940_latch_control(H1940_LATCH_LED_FLASH, 0);
+		h1940_latch_control(H1940_LATCH_LED_GREEN, 0);
+		s3c2410_gpio_setpin(S3C2410_GPA7, 0);
+		break;
 	}
 }
 
@@ -55,20 +55,20 @@ static struct led_classdev h1940_greenled = {
 void h1940_redled_set(struct led_classdev *led_dev, enum led_brightness value)
 {
 	switch (value) {
-		case LED_HALF:
-			h1940_latch_control(0,H1940_LATCH_LED_FLASH);
-			s3c2410_gpio_setpin(S3C2410_GPA1,1);
-			break;
-		case LED_FULL:
-			h1940_latch_control(0,H1940_LATCH_LED_RED);
-			s3c2410_gpio_setpin(S3C2410_GPA1,1);
-			break;
-		default:
-		case LED_OFF:
-			h1940_latch_control(H1940_LATCH_LED_FLASH,0);
-			h1940_latch_control(H1940_LATCH_LED_RED,0);
-			s3c2410_gpio_setpin(S3C2410_GPA1,0);
-			break;
+	case LED_HALF:
+		h1940_latch_control(0, H1940_LATCH_LED_FLASH);
+		s3c2410_gpio_setpin(S3C2410_GPA1, 1);
+		break;
+	case LED_FULL:
+		h1940_latch_control(0, H1940_LATCH_LED_RED);
+		s3c2410_gpio_setpin(S3C2410_GPA1, 1);
+		break;
+	default:
+	case LED_OFF:
+		h1940_latch_control(H1940_LATCH_LED_FLASH, 0);
+		h1940_latch_control(H1940_LATCH_LED_RED, 0);
+		s3c2410_gpio_setpin(S3C2410_GPA1, 0);
+		break;
 	}
 }
 
@@ -86,11 +86,11 @@ void h1940_blueled_set(struct led_classdev *led_dev, enum led_brightness value)
 {
 	if (value) {
 		/* flashing Blue */
-		h1940_latch_control(0,H1940_LATCH_LED_FLASH);
-		s3c2410_gpio_setpin(S3C2410_GPA3,1);
+		h1940_latch_control(0, H1940_LATCH_LED_FLASH);
+		s3c2410_gpio_setpin(S3C2410_GPA3, 1);
 	} else {
-		h1940_latch_control(H1940_LATCH_LED_FLASH,0);
-		s3c2410_gpio_setpin(S3C2410_GPA3,0);
+		h1940_latch_control(H1940_LATCH_LED_FLASH, 0);
+		s3c2410_gpio_setpin(S3C2410_GPA3, 0);
 	}
 
 }

+ 4 - 2
drivers/leds/leds-hp6xx.c

@@ -17,7 +17,8 @@
 #include <asm/hd64461.h>
 #include <asm/hp6xx.h>
 
-static void hp6xxled_green_set(struct led_classdev *led_cdev, enum led_brightness value)
+static void hp6xxled_green_set(struct led_classdev *led_cdev,
+			       enum led_brightness value)
 {
 	u8 v8;
 
@@ -28,7 +29,8 @@ static void hp6xxled_green_set(struct led_classdev *led_cdev, enum led_brightnes
 		outb(v8 | PKDR_LED_GREEN, PKDR);
 }
 
-static void hp6xxled_red_set(struct led_classdev *led_cdev, enum led_brightness value)
+static void hp6xxled_red_set(struct led_classdev *led_cdev,
+			     enum led_brightness value)
 {
 	u16 v16;
 

+ 2 - 2
drivers/leds/leds-s3c24xx.c

@@ -51,7 +51,7 @@ static void s3c24xx_led_set(struct led_classdev *led_cdev,
 
 	if (pd->flags & S3C24XX_LEDF_TRISTATE)
 		s3c2410_gpio_cfgpin(pd->gpio,
-				    value ? S3C2410_GPIO_OUTPUT : S3C2410_GPIO_INPUT);
+			value ? S3C2410_GPIO_OUTPUT : S3C2410_GPIO_INPUT);
 
 }
 
@@ -151,7 +151,7 @@ static int __init s3c24xx_led_init(void)
 
 static void __exit s3c24xx_led_exit(void)
 {
- 	platform_driver_unregister(&s3c24xx_led_driver);
+	platform_driver_unregister(&s3c24xx_led_driver);
 }
 
 module_init(s3c24xx_led_init);

+ 7 - 4
drivers/leds/leds-spitz.c

@@ -21,7 +21,8 @@
 #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)
+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);
@@ -29,7 +30,8 @@ static void spitzled_amber_set(struct led_classdev *led_cdev, enum led_brightnes
 		reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_LED_ORANGE);
 }
 
-static void spitzled_green_set(struct led_classdev *led_cdev, enum led_brightness value)
+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);
@@ -53,7 +55,8 @@ static struct led_classdev spitz_green_led = {
 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"))
+	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);
@@ -116,7 +119,7 @@ static int __init spitzled_init(void)
 
 static void __exit spitzled_exit(void)
 {
- 	platform_driver_unregister(&spitzled_driver);
+	platform_driver_unregister(&spitzled_driver);
 }
 
 module_init(spitzled_init);

+ 9 - 2
drivers/leds/leds.h

@@ -27,6 +27,11 @@ static inline void led_set_brightness(struct led_classdev *led_cdev,
 		led_cdev->brightness_set(led_cdev, value);
 }
 
+static inline int led_get_brightness(struct led_classdev *led_cdev)
+{
+	return led_cdev->brightness;
+}
+
 extern struct rw_semaphore leds_list_lock;
 extern struct list_head leds_list;
 
@@ -34,9 +39,11 @@ extern struct list_head leds_list;
 void led_trigger_set_default(struct led_classdev *led_cdev);
 void led_trigger_set(struct led_classdev *led_cdev,
 			struct led_trigger *trigger);
+void led_trigger_remove(struct led_classdev *led_cdev);
 #else
-#define led_trigger_set_default(x) do {} while(0)
-#define led_trigger_set(x, y) do {} while(0)
+#define led_trigger_set_default(x) do {} while (0)
+#define led_trigger_set(x, y) do {} while (0)
+#define led_trigger_remove(x) do {} while (0)
 #endif
 
 ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,

+ 45 - 0
drivers/leds/ledtrig-default-on.c

@@ -0,0 +1,45 @@
+/*
+ * LED Kernel Default ON Trigger
+ *
+ * Copyright 2008 Nick Forbes <nick.forbes@incepta.com>
+ *
+ * Based on Richard Purdie's ledtrig-timer.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/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+#include "leds.h"
+
+static void defon_trig_activate(struct led_classdev *led_cdev)
+{
+	led_set_brightness(led_cdev, LED_FULL);
+}
+
+static struct led_trigger defon_led_trigger = {
+	.name     = "default-on",
+	.activate = defon_trig_activate,
+};
+
+static int __init defon_trig_init(void)
+{
+	return led_trigger_register(&defon_led_trigger);
+}
+
+static void __exit defon_trig_exit(void)
+{
+	led_trigger_unregister(&defon_led_trigger);
+}
+
+module_init(defon_trig_init);
+module_exit(defon_trig_exit);
+
+MODULE_AUTHOR("Nick Forbes <nick.forbes@incepta.com>");
+MODULE_DESCRIPTION("Default-ON LED trigger");
+MODULE_LICENSE("GPL");

+ 1 - 1
drivers/leds/ledtrig-ide-disk.c

@@ -38,7 +38,7 @@ 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));
+		mod_timer(&ledtrig_ide_timer, jiffies + msecs_to_jiffies(10));
 	} else {
 		led_trigger_event(ledtrig_ide, LED_OFF);
 	}

+ 25 - 10
drivers/leds/ledtrig-timer.c

@@ -25,6 +25,9 @@
 #include "leds.h"
 
 struct timer_trig_data {
+	int brightness_on;		/* LED brightness during "on" period.
+					 * (LED_OFF < brightness_on <= LED_FULL)
+					 */
 	unsigned long delay_on;		/* milliseconds on */
 	unsigned long delay_off;	/* milliseconds off */
 	struct timer_list timer;
@@ -34,17 +37,26 @@ 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;
+	unsigned long brightness;
+	unsigned long delay;
 
 	if (!timer_data->delay_on || !timer_data->delay_off) {
 		led_set_brightness(led_cdev, LED_OFF);
 		return;
 	}
 
-	if (!led_cdev->brightness) {
-		brightness = LED_FULL;
+	brightness = led_get_brightness(led_cdev);
+	if (!brightness) {
+		/* Time to switch the LED on. */
+		brightness = timer_data->brightness_on;
 		delay = timer_data->delay_on;
+	} else {
+		/* Store the current brightness value to be able
+		 * to restore it when the delay_off period is over.
+		 */
+		timer_data->brightness_on = brightness;
+		brightness = LED_OFF;
+		delay = timer_data->delay_off;
 	}
 
 	led_set_brightness(led_cdev, brightness);
@@ -52,7 +64,7 @@ static void led_timer_function(unsigned long data)
 	mod_timer(&timer_data->timer, jiffies + msecs_to_jiffies(delay));
 }
 
-static ssize_t led_delay_on_show(struct device *dev, 
+static ssize_t led_delay_on_show(struct device *dev,
 		struct device_attribute *attr, char *buf)
 {
 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
@@ -63,7 +75,7 @@ static ssize_t led_delay_on_show(struct device *dev,
 	return strlen(buf) + 1;
 }
 
-static ssize_t led_delay_on_store(struct device *dev, 
+static ssize_t led_delay_on_store(struct device *dev,
 		struct device_attribute *attr, const char *buf, size_t size)
 {
 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
@@ -87,7 +99,7 @@ static ssize_t led_delay_on_store(struct device *dev,
 			/* try to activate hardware acceleration, if any */
 			if (!led_cdev->blink_set ||
 			    led_cdev->blink_set(led_cdev,
-				&timer_data->delay_on, &timer_data->delay_off)) {
+			      &timer_data->delay_on, &timer_data->delay_off)) {
 				/* no hardware acceleration, blink via timer */
 				mod_timer(&timer_data->timer, jiffies + 1);
 			}
@@ -98,7 +110,7 @@ static ssize_t led_delay_on_store(struct device *dev,
 	return ret;
 }
 
-static ssize_t led_delay_off_show(struct device *dev, 
+static ssize_t led_delay_off_show(struct device *dev,
 		struct device_attribute *attr, char *buf)
 {
 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
@@ -109,7 +121,7 @@ static ssize_t led_delay_off_show(struct device *dev,
 	return strlen(buf) + 1;
 }
 
-static ssize_t led_delay_off_store(struct device *dev, 
+static ssize_t led_delay_off_store(struct device *dev,
 		struct device_attribute *attr, const char *buf, size_t size)
 {
 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
@@ -133,7 +145,7 @@ static ssize_t led_delay_off_store(struct device *dev,
 			/* try to activate hardware acceleration, if any */
 			if (!led_cdev->blink_set ||
 			    led_cdev->blink_set(led_cdev,
-				&timer_data->delay_on, &timer_data->delay_off)) {
+			      &timer_data->delay_on, &timer_data->delay_off)) {
 				/* no hardware acceleration, blink via timer */
 				mod_timer(&timer_data->timer, jiffies + 1);
 			}
@@ -156,6 +168,9 @@ static void timer_trig_activate(struct led_classdev *led_cdev)
 	if (!timer_data)
 		return;
 
+	timer_data->brightness_on = led_get_brightness(led_cdev);
+	if (timer_data->brightness_on == LED_OFF)
+		timer_data->brightness_on = LED_FULL;
 	led_cdev->trigger_data = timer_data;
 
 	init_timer(&timer_data->timer);

+ 6 - 0
include/linux/leds.h

@@ -35,8 +35,11 @@ struct led_classdev {
 #define LED_SUSPENDED		(1 << 0)
 
 	/* Set LED brightness level */
+	/* Must not sleep, use a workqueue if needed */
 	void		(*brightness_set)(struct led_classdev *led_cdev,
 					  enum led_brightness brightness);
+	/* Get LED brightness level */
+	enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);
 
 	/* Activate hardware accelerated blink */
 	int		(*blink_set)(struct led_classdev *led_cdev,
@@ -126,6 +129,9 @@ struct gpio_led {
 struct gpio_led_platform_data {
 	int 		num_leds;
 	struct gpio_led *leds;
+	int		(*gpio_blink_set)(unsigned gpio,
+					unsigned long *delay_on,
+					unsigned long *delay_off);
 };