summaryrefslogtreecommitdiffstats
path: root/drivers/leds
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/leds')
-rw-r--r--drivers/leds/Kconfig340
-rw-r--r--drivers/leds/Makefile46
-rw-r--r--drivers/leds/led-class.c231
-rw-r--r--drivers/leds/led-core.c25
-rw-r--r--drivers/leds/led-triggers.c264
-rw-r--r--drivers/leds/leds-adp5520.c230
-rw-r--r--drivers/leds/leds-alix2.c239
-rw-r--r--drivers/leds/leds-ams-delta.c136
-rw-r--r--drivers/leds/leds-atmel-pwm.c160
-rw-r--r--drivers/leds/leds-bd2802.c819
-rw-r--r--drivers/leds/leds-clevo-mail.c219
-rw-r--r--drivers/leds/leds-cobalt-qube.c105
-rw-r--r--drivers/leds/leds-cobalt-raq.c138
-rw-r--r--drivers/leds/leds-da903x.c176
-rw-r--r--drivers/leds/leds-dac124s085.c151
-rw-r--r--drivers/leds/leds-fsg.c243
-rw-r--r--drivers/leds/leds-gpio.c335
-rw-r--r--drivers/leds/leds-h1940.c170
-rw-r--r--drivers/leds/leds-hp6xx.c108
-rw-r--r--drivers/leds/leds-locomo.c96
-rw-r--r--drivers/leds/leds-lp3944.c466
-rw-r--r--drivers/leds/leds-lt3593.c217
-rw-r--r--drivers/leds/leds-net48xx.c96
-rw-r--r--drivers/leds/leds-pca9532.c373
-rw-r--r--drivers/leds/leds-pca955x.c390
-rw-r--r--drivers/leds/leds-pwm.c152
-rw-r--r--drivers/leds/leds-rb532.c77
-rw-r--r--drivers/leds/leds-regulator.c242
-rw-r--r--drivers/leds/leds-s3c24xx.c138
-rw-r--r--drivers/leds/leds-ss4200.c556
-rw-r--r--drivers/leds/leds-sunfire.c273
-rw-r--r--drivers/leds/leds-wm831x-status.c341
-rw-r--r--drivers/leds/leds-wm8350.c311
-rw-r--r--drivers/leds/leds-wrap.c153
-rw-r--r--drivers/leds/leds.h54
-rw-r--r--drivers/leds/ledtrig-backlight.c110
-rw-r--r--drivers/leds/ledtrig-default-on.c45
-rw-r--r--drivers/leds/ledtrig-gpio.c249
-rw-r--r--drivers/leds/ledtrig-heartbeat.c118
-rw-r--r--drivers/leds/ledtrig-ide-disk.c64
-rw-r--r--drivers/leds/ledtrig-timer.c237
41 files changed, 8893 insertions, 0 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
new file mode 100644
index 00000000000..8a0e1ec95e4
--- /dev/null
+++ b/drivers/leds/Kconfig
@@ -0,0 +1,340 @@
+menuconfig NEW_LEDS
+ bool "LED Support"
+ help
+ Say Y to enable Linux LED support. This allows control of supported
+ LEDs from both userspace and optionally, by kernel events (triggers).
+
+ This is not related to standard keyboard LEDs which are controlled
+ via the input system.
+
+if NEW_LEDS
+
+config LEDS_CLASS
+ tristate "LED Class Support"
+ 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.
+
+comment "LED drivers"
+
+config LEDS_ATMEL_PWM
+ tristate "LED Support using Atmel PWM outputs"
+ depends on LEDS_CLASS && ATMEL_PWM
+ help
+ This option enables support for LEDs driven using outputs
+ of the dedicated PWM controller found on newer Atmel SOCs.
+
+config LEDS_LOCOMO
+ tristate "LED Support for Locomo device"
+ depends on LEDS_CLASS && SHARP_LOCOMO
+ help
+ This option enables support for the LEDs on Sharp Locomo.
+ Zaurus models SL-5500 and SL-5600.
+
+config LEDS_MIKROTIK_RB532
+ tristate "LED Support for Mikrotik Routerboard 532"
+ depends on LEDS_CLASS && MIKROTIK_RB532
+ help
+ This option enables support for the so called "User LED" of
+ Mikrotik's Routerboard 532.
+
+config LEDS_S3C24XX
+ tristate "LED Support for Samsung S3C24XX GPIO LEDs"
+ depends on LEDS_CLASS && ARCH_S3C2410
+ help
+ This option enables support for LEDs connected to GPIO lines
+ on Samsung S3C24XX series CPUs, such as the S3C2410 and S3C2440.
+
+config LEDS_AMS_DELTA
+ tristate "LED Support for the Amstrad Delta (E3)"
+ depends on LEDS_CLASS && MACH_AMS_DELTA
+ help
+ This option enables support for the LEDs on Amstrad Delta (E3).
+
+config LEDS_NET48XX
+ tristate "LED Support for Soekris net48xx series Error LED"
+ depends on LEDS_CLASS && SCx200_GPIO
+ help
+ 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
+ help
+ This option enables support for the PCEngines WRAP programmable LEDs.
+
+config LEDS_ALIX2
+ tristate "LED Support for ALIX.2 and ALIX.3 series"
+ depends on LEDS_CLASS && X86 && EXPERIMENTAL
+ help
+ This option enables support for the PCEngines ALIX.2 and ALIX.3 LEDs.
+ You have to set leds-alix2.force=1 for boards with Award BIOS.
+
+config LEDS_H1940
+ tristate "LED Support for iPAQ H1940 device"
+ depends on LEDS_CLASS && ARCH_H1940
+ help
+ This option enables support for the LEDs on the h1940.
+
+config LEDS_COBALT_QUBE
+ tristate "LED Support for the Cobalt Qube series front LED"
+ depends on LEDS_CLASS && MIPS_COBALT
+ help
+ This option enables support for the front LED on Cobalt Qube series
+
+config LEDS_COBALT_RAQ
+ bool "LED Support for the Cobalt Raq series"
+ depends on LEDS_CLASS=y && MIPS_COBALT
+ select LEDS_TRIGGERS
+ 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
+ help
+ 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 useful
+ as a platform driver
+
+config LEDS_GPIO
+ tristate "LED Support for GPIO connected LEDs"
+ depends on LEDS_CLASS && GENERIC_GPIO
+ help
+ This option enables support for the LEDs connected to GPIO
+ outputs. To be useful the particular board must have LEDs
+ and they must be connected to the GPIO lines. The LEDs must be
+ defined as platform devices and/or OpenFirmware platform devices.
+ The code to use these bindings can be selected below.
+
+config LEDS_GPIO_PLATFORM
+ bool "Platform device bindings for GPIO LEDs"
+ depends on LEDS_GPIO
+ default y
+ help
+ Let the leds-gpio driver drive LEDs which have been defined as
+ platform devices. If you don't know what this means, say yes.
+
+config LEDS_GPIO_OF
+ bool "OpenFirmware platform device bindings for GPIO LEDs"
+ depends on LEDS_GPIO && OF_DEVICE
+ default y
+ help
+ Let the leds-gpio driver drive LEDs which have been defined as
+ of_platform devices. For instance, LEDs which are listed in a "dts"
+ file.
+
+config LEDS_LP3944
+ tristate "LED Support for N.S. LP3944 (Fun Light) I2C chip"
+ depends on LEDS_CLASS && I2C
+ help
+ This option enables support for LEDs connected to the National
+ Semiconductor LP3944 Lighting Management Unit (LMU) also known as
+ Fun Light Chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called leds-lp3944.
+
+config LEDS_CLEVO_MAIL
+ tristate "Mail LED on Clevo notebook"
+ depends on LEDS_CLASS && X86 && SERIO_I8042 && DMI
+ help
+ This driver makes the mail LED accessible from userspace
+ programs through the leds subsystem. This LED have three
+ known mode: off, blink at 0.5Hz and blink at 1Hz.
+
+ The driver supports two kinds of interface: using ledtrig-timer
+ or through /sys/class/leds/clevo::mail/brightness. As this LED
+ cannot change it's brightness it blinks instead. The brightness
+ value 0 means off, 1..127 means blink at 0.5Hz and 128..255 means
+ blink at 1Hz.
+
+ 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)
+ Clevo M540N
+ Clevo M5x0N (not tested, but might work)
+ Positivo Mobile (Clevo M5x0V)
+
+ If your model is not listed here you can try the "nodetect"
+ module parameter.
+
+ 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.
+
+config LEDS_WM831X_STATUS
+ tristate "LED support for status LEDs on WM831x PMICs"
+ depends on LEDS_CLASS && MFD_WM831X
+ help
+ This option enables support for the status LEDs of the WM831x
+ series of PMICs.
+
+config LEDS_WM8350
+ tristate "LED Support for WM8350 AudioPlus PMIC"
+ depends on LEDS_CLASS && MFD_WM8350
+ help
+ This option enables support for LEDs driven by the Wolfson
+ Microelectronics WM8350 AudioPlus PMIC.
+
+config LEDS_DA903X
+ tristate "LED Support for DA9030/DA9034 PMIC"
+ depends on LEDS_CLASS && PMIC_DA903X
+ help
+ This option enables support for on-chip LED drivers found
+ on Dialog Semiconductor DA9030/DA9034 PMICs.
+
+config LEDS_DAC124S085
+ tristate "LED Support for DAC124S085 SPI DAC"
+ depends on LEDS_CLASS && SPI
+ help
+ This option enables support for DAC124S085 SPI DAC from NatSemi,
+ which can be used to control up to four LEDs.
+
+config LEDS_PWM
+ tristate "PWM driven LED Support"
+ depends on LEDS_CLASS && HAVE_PWM
+ help
+ This option enables support for pwm driven LEDs
+
+config LEDS_REGULATOR
+ tristate "REGULATOR driven LED support"
+ depends on LEDS_CLASS && REGULATOR
+ help
+ This option enables support for regulator driven LEDs.
+
+config LEDS_BD2802
+ tristate "LED driver for BD2802 RGB LED"
+ depends on LEDS_CLASS && I2C
+ help
+ This option enables support for BD2802GU RGB LED driver chips
+ accessed via the I2C bus.
+
+config LEDS_INTEL_SS4200
+ tristate "LED driver for Intel NAS SS4200 series"
+ depends on LEDS_CLASS && PCI && DMI
+ help
+ This option enables support for the Intel SS4200 series of
+ Network Attached Storage servers. You may control the hard
+ drive or power LEDs on the front panel. Using this driver
+ can stop the front LED from blinking after startup.
+
+config LEDS_LT3593
+ tristate "LED driver for LT3593 controllers"
+ depends on LEDS_CLASS && GENERIC_GPIO
+ help
+ This option enables support for LEDs driven by a Linear Technology
+ LT3593 controller. This controller uses a special one-wire pulse
+ coding protocol to set the brightness.
+
+config LEDS_ADP5520
+ tristate "LED Support for ADP5520/ADP5501 PMIC"
+ depends on LEDS_CLASS && PMIC_ADP5520
+ help
+ This option enables support for on-chip LED drivers found
+ on Analog Devices ADP5520/ADP5501 PMICs.
+
+ To compile this driver as a module, choose M here: the module will
+ be called leds-adp5520.
+
+comment "LED Triggers"
+
+config LEDS_TRIGGERS
+ bool "LED Trigger support"
+ 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_TRIGGER_TIMER
+ tristate "LED Timer Trigger"
+ depends on LEDS_TRIGGERS
+ help
+ This allows LEDs to be controlled by a programmable timer
+ via sysfs. Some LED hardware can be programmed to start
+ blinking the LED without any further software interaction.
+ For more details read Documentation/leds-class.txt.
+
+ If unsure, say Y.
+
+config LEDS_TRIGGER_IDE_DISK
+ bool "LED IDE Disk Trigger"
+ depends on LEDS_TRIGGERS && IDE_GD_ATA
+ help
+ This allows LEDs to be controlled by IDE disk activity.
+ If unsure, say Y.
+
+config LEDS_TRIGGER_HEARTBEAT
+ tristate "LED Heartbeat Trigger"
+ depends on LEDS_TRIGGERS
+ help
+ This allows LEDs to be controlled by a CPU load average.
+ The flash frequency is a hyperbolic function of the 1-minute
+ load average.
+ If unsure, say Y.
+
+config LEDS_TRIGGER_BACKLIGHT
+ tristate "LED backlight Trigger"
+ depends on LEDS_TRIGGERS
+ help
+ This allows LEDs to be controlled as a backlight device: they
+ turn off and on when the display is blanked and unblanked.
+
+ If unsure, say N.
+
+config LEDS_TRIGGER_GPIO
+ tristate "LED GPIO Trigger"
+ depends on LEDS_TRIGGERS
+ depends on GPIOLIB
+ help
+ This allows LEDs to be controlled by gpio events. It's good
+ when using gpios as switches and triggering the needed LEDs
+ from there. One use case is n810's keypad LEDs that could
+ be triggered by this trigger when user slides up to show
+ keypad.
+
+ If unsure, say N.
+
+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.
+
+comment "iptables trigger is under Netfilter config (LED target)"
+ depends on LEDS_TRIGGERS
+
+endif # NEW_LEDS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
new file mode 100644
index 00000000000..9e63869d7c0
--- /dev/null
+++ b/drivers/leds/Makefile
@@ -0,0 +1,46 @@
+
+# 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_ATMEL_PWM) += leds-atmel-pwm.o
+obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o
+obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o
+obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o
+obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o
+obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o
+obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o
+obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o
+obj-$(CONFIG_LEDS_ALIX2) += leds-alix2.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_LP3944) += leds-lp3944.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
+obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o
+obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o
+obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o
+obj-$(CONFIG_LEDS_PWM) += leds-pwm.o
+obj-$(CONFIG_LEDS_REGULATOR) += leds-regulator.o
+obj-$(CONFIG_LEDS_INTEL_SS4200) += leds-ss4200.o
+obj-$(CONFIG_LEDS_LT3593) += leds-lt3593.o
+obj-$(CONFIG_LEDS_ADP5520) += leds-adp5520.o
+
+# LED SPI Drivers
+obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.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_BACKLIGHT) += ledtrig-backlight.o
+obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledtrig-gpio.o
+obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
new file mode 100644
index 00000000000..782f95822ea
--- /dev/null
+++ b/drivers/leds/led-class.c
@@ -0,0 +1,231 @@
+/*
+ * LED Class Core
+ *
+ * Copyright (C) 2005 John Lenz <lenz@cs.wisc.edu>
+ * Copyright (C) 2005-2007 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/list.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+#include <linux/sysdev.h>
+#include <linux/timer.h>
+#include <linux/err.h>
+#include <linux/ctype.h>
+#include <linux/leds.h>
+#include "leds.h"
+
+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)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+
+ /* no lock needed for this */
+ led_update_brightness(led_cdev);
+
+ return sprintf(buf, "%u\n", led_cdev->brightness);
+}
+
+static ssize_t led_brightness_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ ssize_t ret = -EINVAL;
+ char *after;
+ unsigned long state = simple_strtoul(buf, &after, 10);
+ size_t count = after - buf;
+
+ if (isspace(*after))
+ count++;
+
+ if (count == size) {
+ ret = count;
+
+ if (state == LED_OFF)
+ led_trigger_remove(led_cdev);
+ led_set_brightness(led_cdev, state);
+ }
+
+ return ret;
+}
+
+static ssize_t led_max_brightness_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", led_cdev->max_brightness);
+}
+
+static DEVICE_ATTR(brightness, 0644, led_brightness_show, led_brightness_store);
+static DEVICE_ATTR(max_brightness, 0444, led_max_brightness_show, NULL);
+#ifdef CONFIG_LEDS_TRIGGERS
+static 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);
+
+static int led_suspend(struct device *dev, pm_message_t state)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+
+ if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
+ led_classdev_suspend(led_cdev);
+
+ return 0;
+}
+
+static int led_resume(struct device *dev)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+
+ if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
+ led_classdev_resume(led_cdev);
+
+ return 0;
+}
+
+/**
+ * led_classdev_register - register a new object of led_classdev class.
+ * @parent: 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)
+{
+ int rc;
+
+ led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
+ "%s", led_cdev->name);
+ if (IS_ERR(led_cdev->dev))
+ return PTR_ERR(led_cdev->dev);
+
+ /* register the attributes */
+ rc = device_create_file(led_cdev->dev, &dev_attr_brightness);
+ if (rc)
+ goto err_out;
+
+#ifdef CONFIG_LEDS_TRIGGERS
+ init_rwsem(&led_cdev->trigger_lock);
+#endif
+ /* add to the list of leds */
+ down_write(&leds_list_lock);
+ list_add_tail(&led_cdev->node, &leds_list);
+ up_write(&leds_list_lock);
+
+ if (!led_cdev->max_brightness)
+ led_cdev->max_brightness = LED_FULL;
+
+ rc = device_create_file(led_cdev->dev, &dev_attr_max_brightness);
+ if (rc)
+ goto err_out_attr_max;
+
+ led_update_brightness(led_cdev);
+
+#ifdef CONFIG_LEDS_TRIGGERS
+ rc = device_create_file(led_cdev->dev, &dev_attr_trigger);
+ if (rc)
+ goto err_out_led_list;
+
+ led_trigger_set_default(led_cdev);
+#endif
+
+ printk(KERN_INFO "Registered led device: %s\n",
+ led_cdev->name);
+
+ return 0;
+
+#ifdef CONFIG_LEDS_TRIGGERS
+err_out_led_list:
+ device_remove_file(led_cdev->dev, &dev_attr_max_brightness);
+#endif
+err_out_attr_max:
+ device_remove_file(led_cdev->dev, &dev_attr_brightness);
+ list_del(&led_cdev->node);
+err_out:
+ device_unregister(led_cdev->dev);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(led_classdev_register);
+
+/**
+ * led_classdev_unregister - unregisters a object of led_properties class.
+ * @led_cdev: the led device to unregister
+ *
+ * Unregisters a previously registered via led_classdev_register object.
+ */
+void led_classdev_unregister(struct led_classdev *led_cdev)
+{
+ device_remove_file(led_cdev->dev, &dev_attr_max_brightness);
+ device_remove_file(led_cdev->dev, &dev_attr_brightness);
+#ifdef CONFIG_LEDS_TRIGGERS
+ device_remove_file(led_cdev->dev, &dev_attr_trigger);
+ down_write(&led_cdev->trigger_lock);
+ if (led_cdev->trigger)
+ led_trigger_set(led_cdev, NULL);
+ up_write(&led_cdev->trigger_lock);
+#endif
+
+ device_unregister(led_cdev->dev);
+
+ down_write(&leds_list_lock);
+ list_del(&led_cdev->node);
+ up_write(&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);
+ leds_class->suspend = led_suspend;
+ leds_class->resume = led_resume;
+ 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..016d19f5486
--- /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/rwsem.h>
+#include <linux/leds.h>
+#include "leds.h"
+
+DECLARE_RWSEM(leds_list_lock);
+EXPORT_SYMBOL_GPL(leds_list_lock);
+
+LIST_HEAD(leds_list);
+EXPORT_SYMBOL_GPL(leds_list);
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
new file mode 100644
index 00000000000..d8ddd9ef899
--- /dev/null
+++ b/drivers/leds/led-triggers.c
@@ -0,0 +1,264 @@
+/*
+ * LED Triggers Core
+ *
+ * Copyright 2005-2007 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/list.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+#include <linux/sysdev.h>
+#include <linux/timer.h>
+#include <linux/rwsem.h>
+#include <linux/leds.h>
+#include "leds.h"
+
+/*
+ * Nests outside led_cdev->trigger_lock
+ */
+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)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(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")) {
+ led_trigger_remove(led_cdev);
+ return count;
+ }
+
+ down_read(&triggers_list_lock);
+ list_for_each_entry(trig, &trigger_list, next_trig) {
+ if (!strcmp(trigger_name, trig->name)) {
+ down_write(&led_cdev->trigger_lock);
+ led_trigger_set(led_cdev, trig);
+ up_write(&led_cdev->trigger_lock);
+
+ up_read(&triggers_list_lock);
+ return count;
+ }
+ }
+ up_read(&triggers_list_lock);
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(led_trigger_store);
+
+ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct led_trigger *trig;
+ int len = 0;
+
+ down_read(&triggers_list_lock);
+ down_read(&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);
+ }
+ up_read(&led_cdev->trigger_lock);
+ up_read(&triggers_list_lock);
+
+ len += sprintf(len+buf, "\n");
+ return len;
+}
+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)
+{
+ 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);
+ 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);
+ }
+}
+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)
+{
+ struct led_trigger *trig;
+
+ if (!led_cdev->default_trigger)
+ return;
+
+ down_read(&triggers_list_lock);
+ down_write(&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);
+ }
+ 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)
+{
+ struct led_classdev *led_cdev;
+ struct led_trigger *trig;
+
+ rwlock_init(&trigger->leddev_list_lock);
+ INIT_LIST_HEAD(&trigger->led_cdevs);
+
+ down_write(&triggers_list_lock);
+ /* Make sure the trigger's name isn't already in use */
+ list_for_each_entry(trig, &trigger_list, next_trig) {
+ if (!strcmp(trig->name, trigger->name)) {
+ up_write(&triggers_list_lock);
+ return -EEXIST;
+ }
+ }
+ /* Add to the list of led triggers */
+ list_add_tail(&trigger->next_trig, &trigger_list);
+ up_write(&triggers_list_lock);
+
+ /* Register with any LEDs that have this as a default trigger */
+ down_read(&leds_list_lock);
+ list_for_each_entry(led_cdev, &leds_list, node) {
+ down_write(&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);
+ up_write(&led_cdev->trigger_lock);
+ }
+ up_read(&leds_list_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(led_trigger_register);
+
+void led_trigger_unregister(struct led_trigger *trigger)
+{
+ struct led_classdev *led_cdev;
+
+ /* Remove from the list of led triggers */
+ down_write(&triggers_list_lock);
+ list_del(&trigger->next_trig);
+ up_write(&triggers_list_lock);
+
+ /* Remove anyone actively using this trigger */
+ down_read(&leds_list_lock);
+ list_for_each_entry(led_cdev, &leds_list, node) {
+ down_write(&led_cdev->trigger_lock);
+ if (led_cdev->trigger == trigger)
+ led_trigger_set(led_cdev, NULL);
+ up_write(&led_cdev->trigger_lock);
+ }
+ 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)
+{
+ if (trigger)
+ led_trigger_unregister(trigger);
+ kfree(trigger);
+}
+EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);
+
+MODULE_AUTHOR("Richard Purdie");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("LED Triggers Core");
diff --git a/drivers/leds/leds-adp5520.c b/drivers/leds/leds-adp5520.c
new file mode 100644
index 00000000000..a8f31590213
--- /dev/null
+++ b/drivers/leds/leds-adp5520.c
@@ -0,0 +1,230 @@
+/*
+ * LEDs driver for Analog Devices ADP5520/ADP5501 MFD PMICs
+ *
+ * Copyright 2009 Analog Devices Inc.
+ *
+ * Loosely derived from leds-da903x:
+ * Copyright (C) 2008 Compulab, Ltd.
+ * Mike Rapoport <mike@compulab.co.il>
+ *
+ * Copyright (C) 2006-2008 Marvell International Ltd.
+ * Eric Miao <eric.miao@marvell.com>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/workqueue.h>
+#include <linux/mfd/adp5520.h>
+
+struct adp5520_led {
+ struct led_classdev cdev;
+ struct work_struct work;
+ struct device *master;
+ enum led_brightness new_brightness;
+ int id;
+ int flags;
+};
+
+static void adp5520_led_work(struct work_struct *work)
+{
+ struct adp5520_led *led = container_of(work, struct adp5520_led, work);
+ adp5520_write(led->master, ADP5520_LED1_CURRENT + led->id - 1,
+ led->new_brightness >> 2);
+}
+
+static void adp5520_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct adp5520_led *led;
+
+ led = container_of(led_cdev, struct adp5520_led, cdev);
+ led->new_brightness = value;
+ schedule_work(&led->work);
+}
+
+static int adp5520_led_setup(struct adp5520_led *led)
+{
+ struct device *dev = led->master;
+ int flags = led->flags;
+ int ret = 0;
+
+ switch (led->id) {
+ case FLAG_ID_ADP5520_LED1_ADP5501_LED0:
+ ret |= adp5520_set_bits(dev, ADP5520_LED_TIME,
+ (flags >> ADP5520_FLAG_OFFT_SHIFT) &
+ ADP5520_FLAG_OFFT_MASK);
+ ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL,
+ ADP5520_LED1_EN);
+ break;
+ case FLAG_ID_ADP5520_LED2_ADP5501_LED1:
+ ret |= adp5520_set_bits(dev, ADP5520_LED_TIME,
+ ((flags >> ADP5520_FLAG_OFFT_SHIFT) &
+ ADP5520_FLAG_OFFT_MASK) << 2);
+ ret |= adp5520_clr_bits(dev, ADP5520_LED_CONTROL,
+ ADP5520_R3_MODE);
+ ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL,
+ ADP5520_LED2_EN);
+ break;
+ case FLAG_ID_ADP5520_LED3_ADP5501_LED2:
+ ret |= adp5520_set_bits(dev, ADP5520_LED_TIME,
+ ((flags >> ADP5520_FLAG_OFFT_SHIFT) &
+ ADP5520_FLAG_OFFT_MASK) << 4);
+ ret |= adp5520_clr_bits(dev, ADP5520_LED_CONTROL,
+ ADP5520_C3_MODE);
+ ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL,
+ ADP5520_LED3_EN);
+ break;
+ }
+
+ return ret;
+}
+
+static int __devinit adp5520_led_prepare(struct platform_device *pdev)
+{
+ struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data;
+ struct device *dev = pdev->dev.parent;
+ int ret = 0;
+
+ ret |= adp5520_write(dev, ADP5520_LED1_CURRENT, 0);
+ ret |= adp5520_write(dev, ADP5520_LED2_CURRENT, 0);
+ ret |= adp5520_write(dev, ADP5520_LED3_CURRENT, 0);
+ ret |= adp5520_write(dev, ADP5520_LED_TIME, pdata->led_on_time << 6);
+ ret |= adp5520_write(dev, ADP5520_LED_FADE, FADE_VAL(pdata->fade_in,
+ pdata->fade_out));
+
+ return ret;
+}
+
+static int __devinit adp5520_led_probe(struct platform_device *pdev)
+{
+ struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data;
+ struct adp5520_led *led, *led_dat;
+ struct led_info *cur_led;
+ int ret, i;
+
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "missing platform data\n");
+ return -ENODEV;
+ }
+
+ if (pdata->num_leds > ADP5520_01_MAXLEDS) {
+ dev_err(&pdev->dev, "can't handle more than %d LEDS\n",
+ ADP5520_01_MAXLEDS);
+ return -EFAULT;
+ }
+
+ led = kzalloc(sizeof(*led) * pdata->num_leds, GFP_KERNEL);
+ if (led == NULL) {
+ dev_err(&pdev->dev, "failed to alloc memory\n");
+ return -ENOMEM;
+ }
+
+ ret = adp5520_led_prepare(pdev);
+
+ if (ret) {
+ dev_err(&pdev->dev, "failed to write\n");
+ goto err_free;
+ }
+
+ for (i = 0; i < pdata->num_leds; ++i) {
+ cur_led = &pdata->leds[i];
+ led_dat = &led[i];
+
+ led_dat->cdev.name = cur_led->name;
+ led_dat->cdev.default_trigger = cur_led->default_trigger;
+ led_dat->cdev.brightness_set = adp5520_led_set;
+ led_dat->cdev.brightness = LED_OFF;
+
+ if (cur_led->flags & ADP5520_FLAG_LED_MASK)
+ led_dat->flags = cur_led->flags;
+ else
+ led_dat->flags = i + 1;
+
+ led_dat->id = led_dat->flags & ADP5520_FLAG_LED_MASK;
+
+ led_dat->master = pdev->dev.parent;
+ led_dat->new_brightness = LED_OFF;
+
+ INIT_WORK(&led_dat->work, adp5520_led_work);
+
+ ret = led_classdev_register(led_dat->master, &led_dat->cdev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register LED %d\n",
+ led_dat->id);
+ goto err;
+ }
+
+ ret = adp5520_led_setup(led_dat);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to write\n");
+ i++;
+ goto err;
+ }
+ }
+
+ platform_set_drvdata(pdev, led);
+ return 0;
+
+err:
+ if (i > 0) {
+ for (i = i - 1; i >= 0; i--) {
+ led_classdev_unregister(&led[i].cdev);
+ cancel_work_sync(&led[i].work);
+ }
+ }
+
+err_free:
+ kfree(led);
+ return ret;
+}
+
+static int __devexit adp5520_led_remove(struct platform_device *pdev)
+{
+ struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data;
+ struct adp5520_led *led;
+ int i;
+
+ led = platform_get_drvdata(pdev);
+
+ adp5520_clr_bits(led->master, ADP5520_LED_CONTROL,
+ ADP5520_LED1_EN | ADP5520_LED2_EN | ADP5520_LED3_EN);
+
+ for (i = 0; i < pdata->num_leds; i++) {
+ led_classdev_unregister(&led[i].cdev);
+ cancel_work_sync(&led[i].work);
+ }
+
+ kfree(led);
+ return 0;
+}
+
+static struct platform_driver adp5520_led_driver = {
+ .driver = {
+ .name = "adp5520-led",
+ .owner = THIS_MODULE,
+ },
+ .probe = adp5520_led_probe,
+ .remove = __devexit_p(adp5520_led_remove),
+};
+
+static int __init adp5520_led_init(void)
+{
+ return platform_driver_register(&adp5520_led_driver);
+}
+module_init(adp5520_led_init);
+
+static void __exit adp5520_led_exit(void)
+{
+ platform_driver_unregister(&adp5520_led_driver);
+}
+module_exit(adp5520_led_exit);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("LEDS ADP5520(01) Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:adp5520-led");
diff --git a/drivers/leds/leds-alix2.c b/drivers/leds/leds-alix2.c
new file mode 100644
index 00000000000..f59ffadf512
--- /dev/null
+++ b/drivers/leds/leds-alix2.c
@@ -0,0 +1,239 @@
+/*
+ * LEDs driver for PCEngines ALIX.2 and ALIX.3
+ *
+ * Copyright (C) 2008 Constantin Baranov <const@mimas.ru>
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/pci.h>
+
+static int force = 0;
+module_param(force, bool, 0444);
+MODULE_PARM_DESC(force, "Assume system has ALIX.2/ALIX.3 style LEDs");
+
+#define MSR_LBAR_GPIO 0x5140000C
+#define CS5535_GPIO_SIZE 256
+
+static u32 gpio_base;
+
+static struct pci_device_id divil_pci[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) },
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) },
+ { } /* NULL entry */
+};
+MODULE_DEVICE_TABLE(pci, divil_pci);
+
+struct alix_led {
+ struct led_classdev cdev;
+ unsigned short port;
+ unsigned int on_value;
+ unsigned int off_value;
+};
+
+static void alix_led_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct alix_led *led_dev =
+ container_of(led_cdev, struct alix_led, cdev);
+
+ if (brightness)
+ outl(led_dev->on_value, gpio_base + led_dev->port);
+ else
+ outl(led_dev->off_value, gpio_base + led_dev->port);
+}
+
+static struct alix_led alix_leds[] = {
+ {
+ .cdev = {
+ .name = "alix:1",
+ .brightness_set = alix_led_set,
+ },
+ .port = 0x00,
+ .on_value = 1 << 22,
+ .off_value = 1 << 6,
+ },
+ {
+ .cdev = {
+ .name = "alix:2",
+ .brightness_set = alix_led_set,
+ },
+ .port = 0x80,
+ .on_value = 1 << 25,
+ .off_value = 1 << 9,
+ },
+ {
+ .cdev = {
+ .name = "alix:3",
+ .brightness_set = alix_led_set,
+ },
+ .port = 0x80,
+ .on_value = 1 << 27,
+ .off_value = 1 << 11,
+ },
+};
+
+static int __init alix_led_probe(struct platform_device *pdev)
+{
+ int i;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(alix_leds); i++) {
+ alix_leds[i].cdev.flags |= LED_CORE_SUSPENDRESUME;
+ ret = led_classdev_register(&pdev->dev, &alix_leds[i].cdev);
+ if (ret < 0)
+ goto fail;
+ }
+ return 0;
+
+fail:
+ while (--i >= 0)
+ led_classdev_unregister(&alix_leds[i].cdev);
+ return ret;
+}
+
+static int alix_led_remove(struct platform_device *pdev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(alix_leds); i++)
+ led_classdev_unregister(&alix_leds[i].cdev);
+ return 0;
+}
+
+static struct platform_driver alix_led_driver = {
+ .remove = alix_led_remove,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init alix_present(unsigned long bios_phys,
+ const char *alix_sig,
+ size_t alix_sig_len)
+{
+ const size_t bios_len = 0x00010000;
+ const char *bios_virt;
+ const char *scan_end;
+ const char *p;
+ char name[64];
+
+ if (force) {
+ printk(KERN_NOTICE "%s: forced to skip BIOS test, "
+ "assume system has ALIX.2 style LEDs\n",
+ KBUILD_MODNAME);
+ return 1;
+ }
+
+ bios_virt = phys_to_virt(bios_phys);
+ scan_end = bios_virt + bios_len - (alix_sig_len + 2);
+ for (p = bios_virt; p < scan_end; p++) {
+ const char *tail;
+ char *a;
+
+ if (memcmp(p, alix_sig, alix_sig_len) != 0)
+ continue;
+
+ memcpy(name, p, sizeof(name));
+
+ /* remove the first \0 character from string */
+ a = strchr(name, '\0');
+ if (a)
+ *a = ' ';
+
+ /* cut the string at a newline */
+ a = strchr(name, '\r');
+ if (a)
+ *a = '\0';
+
+ tail = p + alix_sig_len;
+ if ((tail[0] == '2' || tail[0] == '3')) {
+ printk(KERN_INFO
+ "%s: system is recognized as \"%s\"\n",
+ KBUILD_MODNAME, name);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static struct platform_device *pdev;
+
+static int __init alix_pci_led_init(void)
+{
+ u32 low, hi;
+
+ if (pci_dev_present(divil_pci) == 0) {
+ printk(KERN_WARNING KBUILD_MODNAME": DIVIL not found\n");
+ return -ENODEV;
+ }
+
+ /* Grab the GPIO I/O range */
+ rdmsr(MSR_LBAR_GPIO, low, hi);
+
+ /* Check the mask and whether GPIO is enabled (sanity check) */
+ if (hi != 0x0000f001) {
+ printk(KERN_WARNING KBUILD_MODNAME": GPIO not enabled\n");
+ return -ENODEV;
+ }
+
+ /* Mask off the IO base address */
+ gpio_base = low & 0x0000ff00;
+
+ if (!request_region(gpio_base, CS5535_GPIO_SIZE, KBUILD_MODNAME)) {
+ printk(KERN_ERR KBUILD_MODNAME": can't allocate I/O for GPIO\n");
+ return -ENODEV;
+ }
+
+ /* Set GPIO function to output */
+ outl(1 << 6, gpio_base + 0x04);
+ outl(1 << 9, gpio_base + 0x84);
+ outl(1 << 11, gpio_base + 0x84);
+
+ return 0;
+}
+
+static int __init alix_led_init(void)
+{
+ int ret = -ENODEV;
+ const char tinybios_sig[] = "PC Engines ALIX.";
+ const char coreboot_sig[] = "PC Engines\0ALIX.";
+
+ if (alix_present(0xf0000, tinybios_sig, sizeof(tinybios_sig) - 1) ||
+ alix_present(0x500, coreboot_sig, sizeof(coreboot_sig) - 1))
+ ret = alix_pci_led_init();
+
+ if (ret < 0)
+ return ret;
+
+ pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);
+ if (!IS_ERR(pdev)) {
+ ret = platform_driver_probe(&alix_led_driver, alix_led_probe);
+ if (ret)
+ platform_device_unregister(pdev);
+ } else
+ ret = PTR_ERR(pdev);
+
+ return ret;
+}
+
+static void __exit alix_led_exit(void)
+{
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&alix_led_driver);
+ release_region(gpio_base, CS5535_GPIO_SIZE);
+}
+
+module_init(alix_led_init);
+module_exit(alix_led_exit);
+
+MODULE_AUTHOR("Constantin Baranov <const@mimas.ru>");
+MODULE_DESCRIPTION("PCEngines ALIX.2 and ALIX.3 LED driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-ams-delta.c b/drivers/leds/leds-ams-delta.c
new file mode 100644
index 00000000000..b9826032450
--- /dev/null
+++ b/drivers/leds/leds-ams-delta.c
@@ -0,0 +1,136 @@
+/*
+ * LEDs driver for Amstrad Delta (E3)
+ *
+ * Copyright (C) 2006 Jonathan McDowell <noodles@earth.li>
+ *
+ * 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 <plat/board-ams-delta.h>
+
+/*
+ * Our context
+ */
+struct ams_delta_led {
+ struct led_classdev cdev;
+ u8 bitmask;
+};
+
+static void ams_delta_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct ams_delta_led *led_dev =
+ container_of(led_cdev, struct ams_delta_led, cdev);
+
+ if (value)
+ ams_delta_latch1_write(led_dev->bitmask, led_dev->bitmask);
+ else
+ ams_delta_latch1_write(led_dev->bitmask, 0);
+}
+
+static struct ams_delta_led ams_delta_leds[] = {
+ {
+ .cdev = {
+ .name = "ams-delta::camera",
+ .brightness_set = ams_delta_led_set,
+ },
+ .bitmask = AMS_DELTA_LATCH1_LED_CAMERA,
+ },
+ {
+ .cdev = {
+ .name = "ams-delta::advert",
+ .brightness_set = ams_delta_led_set,
+ },
+ .bitmask = AMS_DELTA_LATCH1_LED_ADVERT,
+ },
+ {
+ .cdev = {
+ .name = "ams-delta::email",
+ .brightness_set = ams_delta_led_set,
+ },
+ .bitmask = AMS_DELTA_LATCH1_LED_EMAIL,
+ },
+ {
+ .cdev = {
+ .name = "ams-delta::handsfree",
+ .brightness_set = ams_delta_led_set,
+ },
+ .bitmask = AMS_DELTA_LATCH1_LED_HANDSFREE,
+ },
+ {
+ .cdev = {
+ .name = "ams-delta::voicemail",
+ .brightness_set = ams_delta_led_set,
+ },
+ .bitmask = AMS_DELTA_LATCH1_LED_VOICEMAIL,
+ },
+ {
+ .cdev = {
+ .name = "ams-delta::voice",
+ .brightness_set = ams_delta_led_set,
+ },
+ .bitmask = AMS_DELTA_LATCH1_LED_VOICE,
+ },
+};
+
+static int ams_delta_led_probe(struct platform_device *pdev)
+{
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++) {
+ ams_delta_leds[i].cdev.flags |= LED_CORE_SUSPENDRESUME;
+ ret = led_classdev_register(&pdev->dev,
+ &ams_delta_leds[i].cdev);
+ if (ret < 0)
+ goto fail;
+ }
+
+ return 0;
+fail:
+ while (--i >= 0)
+ led_classdev_unregister(&ams_delta_leds[i].cdev);
+ return ret;
+}
+
+static int ams_delta_led_remove(struct platform_device *pdev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++)
+ led_classdev_unregister(&ams_delta_leds[i].cdev);
+
+ return 0;
+}
+
+static struct platform_driver ams_delta_led_driver = {
+ .probe = ams_delta_led_probe,
+ .remove = ams_delta_led_remove,
+ .driver = {
+ .name = "ams-delta-led",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ams_delta_led_init(void)
+{
+ return platform_driver_register(&ams_delta_led_driver);
+}
+
+static void __exit ams_delta_led_exit(void)
+{
+ platform_driver_unregister(&ams_delta_led_driver);
+}
+
+module_init(ams_delta_led_init);
+module_exit(ams_delta_led_exit);
+
+MODULE_AUTHOR("Jonathan McDowell <noodles@earth.li>");
+MODULE_DESCRIPTION("Amstrad Delta LED driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ams-delta-led");
diff --git a/drivers/leds/leds-atmel-pwm.c b/drivers/leds/leds-atmel-pwm.c
new file mode 100644
index 00000000000..52297c3ab24
--- /dev/null
+++ b/drivers/leds/leds-atmel-pwm.c
@@ -0,0 +1,160 @@
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/io.h>
+#include <linux/atmel_pwm.h>
+
+
+struct pwmled {
+ struct led_classdev cdev;
+ struct pwm_channel pwmc;
+ struct gpio_led *desc;
+ u32 mult;
+ u8 active_low;
+};
+
+
+/*
+ * For simplicity, we use "brightness" as if it were a linear function
+ * of PWM duty cycle. However, a logarithmic function of duty cycle is
+ * probably a better match for perceived brightness: two is half as bright
+ * as four, four is half as bright as eight, etc
+ */
+static void pwmled_brightness(struct led_classdev *cdev, enum led_brightness b)
+{
+ struct pwmled *led;
+
+ /* update the duty cycle for the *next* period */
+ led = container_of(cdev, struct pwmled, cdev);
+ pwm_channel_writel(&led->pwmc, PWM_CUPD, led->mult * (unsigned) b);
+}
+
+/*
+ * NOTE: we reuse the platform_data structure of GPIO leds,
+ * but repurpose its "gpio" number as a PWM channel number.
+ */
+static int __init pwmled_probe(struct platform_device *pdev)
+{
+ const struct gpio_led_platform_data *pdata;
+ struct pwmled *leds;
+ int i;
+ int status;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata || pdata->num_leds < 1)
+ return -ENODEV;
+
+ leds = kcalloc(pdata->num_leds, sizeof(*leds), GFP_KERNEL);
+ if (!leds)
+ return -ENOMEM;
+
+ for (i = 0; i < pdata->num_leds; i++) {
+ struct pwmled *led = leds + i;
+ const struct gpio_led *dat = pdata->leds + i;
+ u32 tmp;
+
+ led->cdev.name = dat->name;
+ led->cdev.brightness = LED_OFF;
+ led->cdev.brightness_set = pwmled_brightness;
+ led->cdev.default_trigger = dat->default_trigger;
+
+ led->active_low = dat->active_low;
+
+ status = pwm_channel_alloc(dat->gpio, &led->pwmc);
+ if (status < 0)
+ goto err;
+
+ /*
+ * Prescale clock by 2^x, so PWM counts in low MHz.
+ * Start each cycle with the LED active, so increasing
+ * the duty cycle gives us more time on (== brighter).
+ */
+ tmp = 5;
+ if (!led->active_low)
+ tmp |= PWM_CPR_CPOL;
+ pwm_channel_writel(&led->pwmc, PWM_CMR, tmp);
+
+ /*
+ * Pick a period so PWM cycles at 100+ Hz; and a multiplier
+ * for scaling duty cycle: brightness * mult.
+ */
+ tmp = (led->pwmc.mck / (1 << 5)) / 100;
+ tmp /= 255;
+ led->mult = tmp;
+ pwm_channel_writel(&led->pwmc, PWM_CDTY,
+ led->cdev.brightness * 255);
+ pwm_channel_writel(&led->pwmc, PWM_CPRD,
+ LED_FULL * tmp);
+
+ pwm_channel_enable(&led->pwmc);
+
+ /* Hand it over to the LED framework */
+ status = led_classdev_register(&pdev->dev, &led->cdev);
+ if (status < 0) {
+ pwm_channel_free(&led->pwmc);
+ goto err;
+ }
+ }
+
+ platform_set_drvdata(pdev, leds);
+ return 0;
+
+err:
+ if (i > 0) {
+ for (i = i - 1; i >= 0; i--) {
+ led_classdev_unregister(&leds[i].cdev);
+ pwm_channel_free(&leds[i].pwmc);
+ }
+ }
+ kfree(leds);
+
+ return status;
+}
+
+static int __exit pwmled_remove(struct platform_device *pdev)
+{
+ const struct gpio_led_platform_data *pdata;
+ struct pwmled *leds;
+ unsigned i;
+
+ pdata = pdev->dev.platform_data;
+ leds = platform_get_drvdata(pdev);
+
+ for (i = 0; i < pdata->num_leds; i++) {
+ struct pwmled *led = leds + i;
+
+ led_classdev_unregister(&led->cdev);
+ pwm_channel_free(&led->pwmc);
+ }
+
+ kfree(leds);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:leds-atmel-pwm");
+
+static struct platform_driver pwmled_driver = {
+ .driver = {
+ .name = "leds-atmel-pwm",
+ .owner = THIS_MODULE,
+ },
+ /* REVISIT add suspend() and resume() methods */
+ .remove = __exit_p(pwmled_remove),
+};
+
+static int __init modinit(void)
+{
+ return platform_driver_probe(&pwmled_driver, pwmled_probe);
+}
+module_init(modinit);
+
+static void __exit modexit(void)
+{
+ platform_driver_unregister(&pwmled_driver);
+}
+module_exit(modexit);
+
+MODULE_DESCRIPTION("Driver for LEDs with PWM-controlled brightness");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-bd2802.c b/drivers/leds/leds-bd2802.c
new file mode 100644
index 00000000000..779d7f262c0
--- /dev/null
+++ b/drivers/leds/leds-bd2802.c
@@ -0,0 +1,819 @@
+/*
+ * leds-bd2802.c - RGB LED Driver
+ *
+ * Copyright (C) 2009 Samsung Electronics
+ * Kim Kyuwon <q1.kim@samsung.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.
+ *
+ * Datasheet: http://www.rohm.com/products/databook/driver/pdf/bd2802gu-e.pdf
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/leds.h>
+#include <linux/leds-bd2802.h>
+
+
+#define LED_CTL(rgb2en, rgb1en) ((rgb2en) << 4 | ((rgb1en) << 0))
+
+#define BD2802_LED_OFFSET 0xa
+#define BD2802_COLOR_OFFSET 0x3
+
+#define BD2802_REG_CLKSETUP 0x00
+#define BD2802_REG_CONTROL 0x01
+#define BD2802_REG_HOURSETUP 0x02
+#define BD2802_REG_CURRENT1SETUP 0x03
+#define BD2802_REG_CURRENT2SETUP 0x04
+#define BD2802_REG_WAVEPATTERN 0x05
+
+#define BD2802_CURRENT_032 0x10 /* 3.2mA */
+#define BD2802_CURRENT_000 0x00 /* 0.0mA */
+
+#define BD2802_PATTERN_FULL 0x07
+#define BD2802_PATTERN_HALF 0x03
+
+enum led_ids {
+ LED1,
+ LED2,
+ LED_NUM,
+};
+
+enum led_colors {
+ RED,
+ GREEN,
+ BLUE,
+};
+
+enum led_bits {
+ BD2802_OFF,
+ BD2802_BLINK,
+ BD2802_ON,
+};
+
+/*
+ * State '0' : 'off'
+ * State '1' : 'blink'
+ * State '2' : 'on'.
+ */
+struct led_state {
+ unsigned r:2;
+ unsigned g:2;
+ unsigned b:2;
+};
+
+struct bd2802_led {
+ struct bd2802_led_platform_data *pdata;
+ struct i2c_client *client;
+ struct rw_semaphore rwsem;
+ struct work_struct work;
+
+ struct led_state led[2];
+
+ /*
+ * Making led_classdev as array is not recommended, because array
+ * members prevent using 'container_of' macro. So repetitive works
+ * are needed.
+ */
+ struct led_classdev cdev_led1r;
+ struct led_classdev cdev_led1g;
+ struct led_classdev cdev_led1b;
+ struct led_classdev cdev_led2r;
+ struct led_classdev cdev_led2g;
+ struct led_classdev cdev_led2b;
+
+ /*
+ * Advanced Configuration Function(ADF) mode:
+ * In ADF mode, user can set registers of BD2802GU directly,
+ * therefore BD2802GU doesn't enter reset state.
+ */
+ int adf_on;
+
+ enum led_ids led_id;
+ enum led_colors color;
+ enum led_bits state;
+
+ /* General attributes of RGB LEDs */
+ int wave_pattern;
+ int rgb_current;
+};
+
+
+/*--------------------------------------------------------------*/
+/* BD2802GU helper functions */
+/*--------------------------------------------------------------*/
+
+static inline int bd2802_is_rgb_off(struct bd2802_led *led, enum led_ids id,
+ enum led_colors color)
+{
+ switch (color) {
+ case RED:
+ return !led->led[id].r;
+ case GREEN:
+ return !led->led[id].g;
+ case BLUE:
+ return !led->led[id].b;
+ default:
+ dev_err(&led->client->dev, "%s: Invalid color\n", __func__);
+ return -EINVAL;
+ }
+}
+
+static inline int bd2802_is_led_off(struct bd2802_led *led, enum led_ids id)
+{
+ if (led->led[id].r || led->led[id].g || led->led[id].b)
+ return 0;
+
+ return 1;
+}
+
+static inline int bd2802_is_all_off(struct bd2802_led *led)
+{
+ int i;
+
+ for (i = 0; i < LED_NUM; i++)
+ if (!bd2802_is_led_off(led, i))
+ return 0;
+
+ return 1;
+}
+
+static inline u8 bd2802_get_base_offset(enum led_ids id, enum led_colors color)
+{
+ return id * BD2802_LED_OFFSET + color * BD2802_COLOR_OFFSET;
+}
+
+static inline u8 bd2802_get_reg_addr(enum led_ids id, enum led_colors color,
+ u8 reg_offset)
+{
+ return reg_offset + bd2802_get_base_offset(id, color);
+}
+
+
+/*--------------------------------------------------------------*/
+/* BD2802GU core functions */
+/*--------------------------------------------------------------*/
+
+static int bd2802_write_byte(struct i2c_client *client, u8 reg, u8 val)
+{
+ int ret = i2c_smbus_write_byte_data(client, reg, val);
+ if (ret >= 0)
+ return 0;
+
+ dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
+ __func__, reg, val, ret);
+
+ return ret;
+}
+
+static void bd2802_update_state(struct bd2802_led *led, enum led_ids id,
+ enum led_colors color, enum led_bits led_bit)
+{
+ int i;
+ u8 value;
+
+ for (i = 0; i < LED_NUM; i++) {
+ if (i == id) {
+ switch (color) {
+ case RED:
+ led->led[i].r = led_bit;
+ break;
+ case GREEN:
+ led->led[i].g = led_bit;
+ break;
+ case BLUE:
+ led->led[i].b = led_bit;
+ break;
+ default:
+ dev_err(&led->client->dev,
+ "%s: Invalid color\n", __func__);
+ return;
+ }
+ }
+ }
+
+ if (led_bit == BD2802_BLINK || led_bit == BD2802_ON)
+ return;
+
+ if (!bd2802_is_led_off(led, id))
+ return;
+
+ if (bd2802_is_all_off(led) && !led->adf_on) {
+ gpio_set_value(led->pdata->reset_gpio, 0);
+ return;
+ }
+
+ /*
+ * In this case, other led is turned on, and current led is turned
+ * off. So set RGB LED Control register to stop the current RGB LED
+ */
+ value = (id == LED1) ? LED_CTL(1, 0) : LED_CTL(0, 1);
+ bd2802_write_byte(led->client, BD2802_REG_CONTROL, value);
+}
+
+static void bd2802_configure(struct bd2802_led *led)
+{
+ struct bd2802_led_platform_data *pdata = led->pdata;
+ u8 reg;
+
+ reg = bd2802_get_reg_addr(LED1, RED, BD2802_REG_HOURSETUP);
+ bd2802_write_byte(led->client, reg, pdata->rgb_time);
+
+ reg = bd2802_get_reg_addr(LED2, RED, BD2802_REG_HOURSETUP);
+ bd2802_write_byte(led->client, reg, pdata->rgb_time);
+}
+
+static void bd2802_reset_cancel(struct bd2802_led *led)
+{
+ gpio_set_value(led->pdata->reset_gpio, 1);
+ udelay(100);
+ bd2802_configure(led);
+}
+
+static void bd2802_enable(struct bd2802_led *led, enum led_ids id)
+{
+ enum led_ids other_led = (id == LED1) ? LED2 : LED1;
+ u8 value, other_led_on;
+
+ other_led_on = !bd2802_is_led_off(led, other_led);
+ if (id == LED1)
+ value = LED_CTL(other_led_on, 1);
+ else
+ value = LED_CTL(1 , other_led_on);
+
+ bd2802_write_byte(led->client, BD2802_REG_CONTROL, value);
+}
+
+static void bd2802_set_on(struct bd2802_led *led, enum led_ids id,
+ enum led_colors color)
+{
+ u8 reg;
+
+ if (bd2802_is_all_off(led) && !led->adf_on)
+ bd2802_reset_cancel(led);
+
+ reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
+ bd2802_write_byte(led->client, reg, led->rgb_current);
+ reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
+ bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
+ reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN);
+ bd2802_write_byte(led->client, reg, BD2802_PATTERN_FULL);
+
+ bd2802_enable(led, id);
+ bd2802_update_state(led, id, color, BD2802_ON);
+}
+
+static void bd2802_set_blink(struct bd2802_led *led, enum led_ids id,
+ enum led_colors color)
+{
+ u8 reg;
+
+ if (bd2802_is_all_off(led) && !led->adf_on)
+ bd2802_reset_cancel(led);
+
+ reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
+ bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
+ reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
+ bd2802_write_byte(led->client, reg, led->rgb_current);
+ reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN);
+ bd2802_write_byte(led->client, reg, led->wave_pattern);
+
+ bd2802_enable(led, id);
+ bd2802_update_state(led, id, color, BD2802_BLINK);
+}
+
+static void bd2802_turn_on(struct bd2802_led *led, enum led_ids id,
+ enum led_colors color, enum led_bits led_bit)
+{
+ if (led_bit == BD2802_OFF) {
+ dev_err(&led->client->dev,
+ "Only 'blink' and 'on' are allowed\n");
+ return;
+ }
+
+ if (led_bit == BD2802_BLINK)
+ bd2802_set_blink(led, id, color);
+ else
+ bd2802_set_on(led, id, color);
+}
+
+static void bd2802_turn_off(struct bd2802_led *led, enum led_ids id,
+ enum led_colors color)
+{
+ u8 reg;
+
+ if (bd2802_is_rgb_off(led, id, color))
+ return;
+
+ reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
+ bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
+ reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
+ bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
+
+ bd2802_update_state(led, id, color, BD2802_OFF);
+}
+
+static void bd2802_restore_state(struct bd2802_led *led)
+{
+ int i;
+
+ for (i = 0; i < LED_NUM; i++) {
+ if (led->led[i].r)
+ bd2802_turn_on(led, i, RED, led->led[i].r);
+ if (led->led[i].g)
+ bd2802_turn_on(led, i, GREEN, led->led[i].g);
+ if (led->led[i].b)
+ bd2802_turn_on(led, i, BLUE, led->led[i].b);
+ }
+}
+
+#define BD2802_SET_REGISTER(reg_addr, reg_name) \
+static ssize_t bd2802_store_reg##reg_addr(struct device *dev, \
+ struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+ struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\
+ unsigned long val; \
+ int ret; \
+ if (!count) \
+ return -EINVAL; \
+ ret = strict_strtoul(buf, 16, &val); \
+ if (ret) \
+ return ret; \
+ down_write(&led->rwsem); \
+ bd2802_write_byte(led->client, reg_addr, (u8) val); \
+ up_write(&led->rwsem); \
+ return count; \
+} \
+static struct device_attribute bd2802_reg##reg_addr##_attr = { \
+ .attr = {.name = reg_name, .mode = 0644, .owner = THIS_MODULE}, \
+ .store = bd2802_store_reg##reg_addr, \
+};
+
+BD2802_SET_REGISTER(0x00, "0x00");
+BD2802_SET_REGISTER(0x01, "0x01");
+BD2802_SET_REGISTER(0x02, "0x02");
+BD2802_SET_REGISTER(0x03, "0x03");
+BD2802_SET_REGISTER(0x04, "0x04");
+BD2802_SET_REGISTER(0x05, "0x05");
+BD2802_SET_REGISTER(0x06, "0x06");
+BD2802_SET_REGISTER(0x07, "0x07");
+BD2802_SET_REGISTER(0x08, "0x08");
+BD2802_SET_REGISTER(0x09, "0x09");
+BD2802_SET_REGISTER(0x0a, "0x0a");
+BD2802_SET_REGISTER(0x0b, "0x0b");
+BD2802_SET_REGISTER(0x0c, "0x0c");
+BD2802_SET_REGISTER(0x0d, "0x0d");
+BD2802_SET_REGISTER(0x0e, "0x0e");
+BD2802_SET_REGISTER(0x0f, "0x0f");
+BD2802_SET_REGISTER(0x10, "0x10");
+BD2802_SET_REGISTER(0x11, "0x11");
+BD2802_SET_REGISTER(0x12, "0x12");
+BD2802_SET_REGISTER(0x13, "0x13");
+BD2802_SET_REGISTER(0x14, "0x14");
+BD2802_SET_REGISTER(0x15, "0x15");
+
+static struct device_attribute *bd2802_addr_attributes[] = {
+ &bd2802_reg0x00_attr,
+ &bd2802_reg0x01_attr,
+ &bd2802_reg0x02_attr,
+ &bd2802_reg0x03_attr,
+ &bd2802_reg0x04_attr,
+ &bd2802_reg0x05_attr,
+ &bd2802_reg0x06_attr,
+ &bd2802_reg0x07_attr,
+ &bd2802_reg0x08_attr,
+ &bd2802_reg0x09_attr,
+ &bd2802_reg0x0a_attr,
+ &bd2802_reg0x0b_attr,
+ &bd2802_reg0x0c_attr,
+ &bd2802_reg0x0d_attr,
+ &bd2802_reg0x0e_attr,
+ &bd2802_reg0x0f_attr,
+ &bd2802_reg0x10_attr,
+ &bd2802_reg0x11_attr,
+ &bd2802_reg0x12_attr,
+ &bd2802_reg0x13_attr,
+ &bd2802_reg0x14_attr,
+ &bd2802_reg0x15_attr,
+};
+
+static void bd2802_enable_adv_conf(struct bd2802_led *led)
+{
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(bd2802_addr_attributes); i++) {
+ ret = device_create_file(&led->client->dev,
+ bd2802_addr_attributes[i]);
+ if (ret) {
+ dev_err(&led->client->dev, "failed: sysfs file %s\n",
+ bd2802_addr_attributes[i]->attr.name);
+ goto failed_remove_files;
+ }
+ }
+
+ if (bd2802_is_all_off(led))
+ bd2802_reset_cancel(led);
+
+ led->adf_on = 1;
+
+ return;
+
+failed_remove_files:
+ for (i--; i >= 0; i--)
+ device_remove_file(&led->client->dev,
+ bd2802_addr_attributes[i]);
+}
+
+static void bd2802_disable_adv_conf(struct bd2802_led *led)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bd2802_addr_attributes); i++)
+ device_remove_file(&led->client->dev,
+ bd2802_addr_attributes[i]);
+
+ if (bd2802_is_all_off(led))
+ gpio_set_value(led->pdata->reset_gpio, 0);
+
+ led->adf_on = 0;
+}
+
+static ssize_t bd2802_show_adv_conf(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));
+ ssize_t ret;
+
+ down_read(&led->rwsem);
+ if (led->adf_on)
+ ret = sprintf(buf, "on\n");
+ else
+ ret = sprintf(buf, "off\n");
+ up_read(&led->rwsem);
+
+ return ret;
+}
+
+static ssize_t bd2802_store_adv_conf(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));
+
+ if (!count)
+ return -EINVAL;
+
+ down_write(&led->rwsem);
+ if (!led->adf_on && !strncmp(buf, "on", 2))
+ bd2802_enable_adv_conf(led);
+ else if (led->adf_on && !strncmp(buf, "off", 3))
+ bd2802_disable_adv_conf(led);
+ up_write(&led->rwsem);
+
+ return count;
+}
+
+static struct device_attribute bd2802_adv_conf_attr = {
+ .attr = {
+ .name = "advanced_configuration",
+ .mode = 0644,
+ .owner = THIS_MODULE
+ },
+ .show = bd2802_show_adv_conf,
+ .store = bd2802_store_adv_conf,
+};
+
+#define BD2802_CONTROL_ATTR(attr_name, name_str) \
+static ssize_t bd2802_show_##attr_name(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\
+ ssize_t ret; \
+ down_read(&led->rwsem); \
+ ret = sprintf(buf, "0x%02x\n", led->attr_name); \
+ up_read(&led->rwsem); \
+ return ret; \
+} \
+static ssize_t bd2802_store_##attr_name(struct device *dev, \
+ struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+ struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\
+ unsigned long val; \
+ int ret; \
+ if (!count) \
+ return -EINVAL; \
+ ret = strict_strtoul(buf, 16, &val); \
+ if (ret) \
+ return ret; \
+ down_write(&led->rwsem); \
+ led->attr_name = val; \
+ up_write(&led->rwsem); \
+ return count; \
+} \
+static struct device_attribute bd2802_##attr_name##_attr = { \
+ .attr = { \
+ .name = name_str, \
+ .mode = 0644, \
+ .owner = THIS_MODULE \
+ }, \
+ .show = bd2802_show_##attr_name, \
+ .store = bd2802_store_##attr_name, \
+};
+
+BD2802_CONTROL_ATTR(wave_pattern, "wave_pattern");
+BD2802_CONTROL_ATTR(rgb_current, "rgb_current");
+
+static struct device_attribute *bd2802_attributes[] = {
+ &bd2802_adv_conf_attr,
+ &bd2802_wave_pattern_attr,
+ &bd2802_rgb_current_attr,
+};
+
+static void bd2802_led_work(struct work_struct *work)
+{
+ struct bd2802_led *led = container_of(work, struct bd2802_led, work);
+
+ if (led->state)
+ bd2802_turn_on(led, led->led_id, led->color, led->state);
+ else
+ bd2802_turn_off(led, led->led_id, led->color);
+}
+
+#define BD2802_CONTROL_RGBS(name, id, clr) \
+static void bd2802_set_##name##_brightness(struct led_classdev *led_cdev,\
+ enum led_brightness value) \
+{ \
+ struct bd2802_led *led = \
+ container_of(led_cdev, struct bd2802_led, cdev_##name); \
+ led->led_id = id; \
+ led->color = clr; \
+ if (value == LED_OFF) \
+ led->state = BD2802_OFF; \
+ else \
+ led->state = BD2802_ON; \
+ schedule_work(&led->work); \
+} \
+static int bd2802_set_##name##_blink(struct led_classdev *led_cdev, \
+ unsigned long *delay_on, unsigned long *delay_off) \
+{ \
+ struct bd2802_led *led = \
+ container_of(led_cdev, struct bd2802_led, cdev_##name); \
+ if (*delay_on == 0 || *delay_off == 0) \
+ return -EINVAL; \
+ led->led_id = id; \
+ led->color = clr; \
+ led->state = BD2802_BLINK; \
+ schedule_work(&led->work); \
+ return 0; \
+}
+
+BD2802_CONTROL_RGBS(led1r, LED1, RED);
+BD2802_CONTROL_RGBS(led1g, LED1, GREEN);
+BD2802_CONTROL_RGBS(led1b, LED1, BLUE);
+BD2802_CONTROL_RGBS(led2r, LED2, RED);
+BD2802_CONTROL_RGBS(led2g, LED2, GREEN);
+BD2802_CONTROL_RGBS(led2b, LED2, BLUE);
+
+static int bd2802_register_led_classdev(struct bd2802_led *led)
+{
+ int ret;
+
+ INIT_WORK(&led->work, bd2802_led_work);
+
+ led->cdev_led1r.name = "led1_R";
+ led->cdev_led1r.brightness = LED_OFF;
+ led->cdev_led1r.brightness_set = bd2802_set_led1r_brightness;
+ led->cdev_led1r.blink_set = bd2802_set_led1r_blink;
+
+ ret = led_classdev_register(&led->client->dev, &led->cdev_led1r);
+ if (ret < 0) {
+ dev_err(&led->client->dev, "couldn't register LED %s\n",
+ led->cdev_led1r.name);
+ goto failed_unregister_led1_R;
+ }
+
+ led->cdev_led1g.name = "led1_G";
+ led->cdev_led1g.brightness = LED_OFF;
+ led->cdev_led1g.brightness_set = bd2802_set_led1g_brightness;
+ led->cdev_led1g.blink_set = bd2802_set_led1g_blink;
+
+ ret = led_classdev_register(&led->client->dev, &led->cdev_led1g);
+ if (ret < 0) {
+ dev_err(&led->client->dev, "couldn't register LED %s\n",
+ led->cdev_led1g.name);
+ goto failed_unregister_led1_G;
+ }
+
+ led->cdev_led1b.name = "led1_B";
+ led->cdev_led1b.brightness = LED_OFF;
+ led->cdev_led1b.brightness_set = bd2802_set_led1b_brightness;
+ led->cdev_led1b.blink_set = bd2802_set_led1b_blink;
+
+ ret = led_classdev_register(&led->client->dev, &led->cdev_led1b);
+ if (ret < 0) {
+ dev_err(&led->client->dev, "couldn't register LED %s\n",
+ led->cdev_led1b.name);
+ goto failed_unregister_led1_B;
+ }
+
+ led->cdev_led2r.name = "led2_R";
+ led->cdev_led2r.brightness = LED_OFF;
+ led->cdev_led2r.brightness_set = bd2802_set_led2r_brightness;
+ led->cdev_led2r.blink_set = bd2802_set_led2r_blink;
+
+ ret = led_classdev_register(&led->client->dev, &led->cdev_led2r);
+ if (ret < 0) {
+ dev_err(&led->client->dev, "couldn't register LED %s\n",
+ led->cdev_led2r.name);
+ goto failed_unregister_led2_R;
+ }
+
+ led->cdev_led2g.name = "led2_G";
+ led->cdev_led2g.brightness = LED_OFF;
+ led->cdev_led2g.brightness_set = bd2802_set_led2g_brightness;
+ led->cdev_led2g.blink_set = bd2802_set_led2g_blink;
+
+ ret = led_classdev_register(&led->client->dev, &led->cdev_led2g);
+ if (ret < 0) {
+ dev_err(&led->client->dev, "couldn't register LED %s\n",
+ led->cdev_led2g.name);
+ goto failed_unregister_led2_G;
+ }
+
+ led->cdev_led2b.name = "led2_B";
+ led->cdev_led2b.brightness = LED_OFF;
+ led->cdev_led2b.brightness_set = bd2802_set_led2b_brightness;
+ led->cdev_led2b.blink_set = bd2802_set_led2b_blink;
+ led->cdev_led2b.flags |= LED_CORE_SUSPENDRESUME;
+
+ ret = led_classdev_register(&led->client->dev, &led->cdev_led2b);
+ if (ret < 0) {
+ dev_err(&led->client->dev, "couldn't register LED %s\n",
+ led->cdev_led2b.name);
+ goto failed_unregister_led2_B;
+ }
+
+ return 0;
+
+failed_unregister_led2_B:
+ led_classdev_unregister(&led->cdev_led2g);
+failed_unregister_led2_G:
+ led_classdev_unregister(&led->cdev_led2r);
+failed_unregister_led2_R:
+ led_classdev_unregister(&led->cdev_led1b);
+failed_unregister_led1_B:
+ led_classdev_unregister(&led->cdev_led1g);
+failed_unregister_led1_G:
+ led_classdev_unregister(&led->cdev_led1r);
+failed_unregister_led1_R:
+
+ return ret;
+}
+
+static void bd2802_unregister_led_classdev(struct bd2802_led *led)
+{
+ cancel_work_sync(&led->work);
+ led_classdev_unregister(&led->cdev_led1r);
+}
+
+static int __devinit bd2802_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct bd2802_led *led;
+ struct bd2802_led_platform_data *pdata;
+ int ret, i;
+
+ led = kzalloc(sizeof(struct bd2802_led), GFP_KERNEL);
+ if (!led) {
+ dev_err(&client->dev, "failed to allocate driver data\n");
+ return -ENOMEM;
+ }
+
+ led->client = client;
+ pdata = led->pdata = client->dev.platform_data;
+ i2c_set_clientdata(client, led);
+
+ /* Configure RESET GPIO (L: RESET, H: RESET cancel) */
+ gpio_request(pdata->reset_gpio, "RGB_RESETB");
+ gpio_direction_output(pdata->reset_gpio, 1);
+
+ /* Tacss = min 0.1ms */
+ udelay(100);
+
+ /* Detect BD2802GU */
+ ret = bd2802_write_byte(client, BD2802_REG_CLKSETUP, 0x00);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to detect device\n");
+ goto failed_free;
+ } else
+ dev_info(&client->dev, "return 0x%02x\n", ret);
+
+ /* To save the power, reset BD2802 after detecting */
+ gpio_set_value(led->pdata->reset_gpio, 0);
+
+ /* Default attributes */
+ led->wave_pattern = BD2802_PATTERN_HALF;
+ led->rgb_current = BD2802_CURRENT_032;
+
+ init_rwsem(&led->rwsem);
+
+ for (i = 0; i < ARRAY_SIZE(bd2802_attributes); i++) {
+ ret = device_create_file(&led->client->dev,
+ bd2802_attributes[i]);
+ if (ret) {
+ dev_err(&led->client->dev, "failed: sysfs file %s\n",
+ bd2802_attributes[i]->attr.name);
+ goto failed_unregister_dev_file;
+ }
+ }
+
+ ret = bd2802_register_led_classdev(led);
+ if (ret < 0)
+ goto failed_unregister_dev_file;
+
+ return 0;
+
+failed_unregister_dev_file:
+ for (i--; i >= 0; i--)
+ device_remove_file(&led->client->dev, bd2802_attributes[i]);
+failed_free:
+ i2c_set_clientdata(client, NULL);
+ kfree(led);
+
+ return ret;
+}
+
+static int __exit bd2802_remove(struct i2c_client *client)
+{
+ struct bd2802_led *led = i2c_get_clientdata(client);
+ int i;
+
+ gpio_set_value(led->pdata->reset_gpio, 0);
+ bd2802_unregister_led_classdev(led);
+ if (led->adf_on)
+ bd2802_disable_adv_conf(led);
+ for (i = 0; i < ARRAY_SIZE(bd2802_attributes); i++)
+ device_remove_file(&led->client->dev, bd2802_attributes[i]);
+ i2c_set_clientdata(client, NULL);
+ kfree(led);
+
+ return 0;
+}
+
+static int bd2802_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ struct bd2802_led *led = i2c_get_clientdata(client);
+
+ gpio_set_value(led->pdata->reset_gpio, 0);
+
+ return 0;
+}
+
+static int bd2802_resume(struct i2c_client *client)
+{
+ struct bd2802_led *led = i2c_get_clientdata(client);
+
+ if (!bd2802_is_all_off(led) || led->adf_on) {
+ bd2802_reset_cancel(led);
+ bd2802_restore_state(led);
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id bd2802_id[] = {
+ { "BD2802", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, bd2802_id);
+
+static struct i2c_driver bd2802_i2c_driver = {
+ .driver = {
+ .name = "BD2802",
+ },
+ .probe = bd2802_probe,
+ .remove = __exit_p(bd2802_remove),
+ .suspend = bd2802_suspend,
+ .resume = bd2802_resume,
+ .id_table = bd2802_id,
+};
+
+static int __init bd2802_init(void)
+{
+ return i2c_add_driver(&bd2802_i2c_driver);
+}
+module_init(bd2802_init);
+
+static void __exit bd2802_exit(void)
+{
+ i2c_del_driver(&bd2802_i2c_driver);
+}
+module_exit(bd2802_exit);
+
+MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>");
+MODULE_DESCRIPTION("BD2802 LED driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/leds/leds-clevo-mail.c b/drivers/leds/leds-clevo-mail.c
new file mode 100644
index 00000000000..a498135a4e8
--- /dev/null
+++ b/drivers/leds/leds-clevo-mail.c
@@ -0,0 +1,219 @@
+
+#include <linux/module.h>
+
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/leds.h>
+
+#include <linux/io.h>
+#include <linux/dmi.h>
+
+#include <linux/i8042.h>
+
+#define CLEVO_MAIL_LED_OFF 0x0084
+#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_DESCRIPTION("Clevo mail LED driver");
+MODULE_LICENSE("GPL");
+
+static unsigned int __initdata nodetect;
+module_param_named(nodetect, nodetect, bool, 0);
+MODULE_PARM_DESC(nodetect, "Skip DMI hardware detection");
+
+static struct platform_device *pdev;
+
+static int __init clevo_mail_led_dmi_callback(const struct dmi_system_id *id)
+{
+ printk(KERN_INFO KBUILD_MODNAME ": '%s' found\n", id->ident);
+ return 1;
+}
+
+/*
+ * struct mail_led_whitelist - List of known good models
+ *
+ * Contains the known good models this driver is compatible with.
+ * When adding a new model try to be as strict as possible. This
+ * makes it possible to keep the false positives (the model is
+ * detected as working, but in reality it is not) as low as
+ * possible.
+ */
+static struct dmi_system_id __initdata mail_led_whitelist[] = {
+ {
+ .callback = clevo_mail_led_dmi_callback,
+ .ident = "Clevo D410J",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "VIA"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "K8N800"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "VT8204B")
+ }
+ },
+ {
+ .callback = clevo_mail_led_dmi_callback,
+ .ident = "Clevo M5x0N",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "CLEVO Co."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "M5x0N")
+ }
+ },
+ {
+ .callback = clevo_mail_led_dmi_callback,
+ .ident = "Positivo Mobile",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "CLEVO Co. "),
+ DMI_MATCH(DMI_BOARD_NAME, "M5X0V "),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Positivo Mobile"),
+ 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",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Clevo, Co."),
+ DMI_MATCH(DMI_BOARD_NAME, "D400V/D470V"),
+ DMI_MATCH(DMI_BOARD_VERSION, "SS78B"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Rev. A1")
+ }
+ },
+ { }
+};
+
+static void clevo_mail_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ i8042_lock_chip();
+
+ if (value == LED_OFF)
+ i8042_command(NULL, CLEVO_MAIL_LED_OFF);
+ else if (value <= LED_HALF)
+ i8042_command(NULL, CLEVO_MAIL_LED_BLINK_0_5HZ);
+ else
+ i8042_command(NULL, CLEVO_MAIL_LED_BLINK_1HZ);
+
+ i8042_unlock_chip();
+
+}
+
+static int clevo_mail_led_blink(struct led_classdev *led_cdev,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ int status = -EINVAL;
+
+ i8042_lock_chip();
+
+ if (*delay_on == 0 /* ms */ && *delay_off == 0 /* ms */) {
+ /* Special case: the leds subsystem requested us to
+ * chose one user friendly blinking of the LED, and
+ * start it. Let's blink the led slowly (0.5Hz).
+ */
+ *delay_on = 1000; /* ms */
+ *delay_off = 1000; /* ms */
+ i8042_command(NULL, CLEVO_MAIL_LED_BLINK_0_5HZ);
+ status = 0;
+
+ } else if (*delay_on == 500 /* ms */ && *delay_off == 500 /* ms */) {
+ /* blink the led with 1Hz */
+ i8042_command(NULL, CLEVO_MAIL_LED_BLINK_1HZ);
+ status = 0;
+
+ } else if (*delay_on == 1000 /* ms */ && *delay_off == 1000 /* ms */) {
+ /* blink the led with 0.5Hz */
+ i8042_command(NULL, CLEVO_MAIL_LED_BLINK_0_5HZ);
+ status = 0;
+
+ } else {
+ printk(KERN_DEBUG KBUILD_MODNAME
+ ": clevo_mail_led_blink(..., %lu, %lu),"
+ " returning -EINVAL (unsupported)\n",
+ *delay_on, *delay_off);
+ }
+
+ i8042_unlock_chip();
+
+ return status;
+}
+
+static struct led_classdev clevo_mail_led = {
+ .name = "clevo::mail",
+ .brightness_set = clevo_mail_led_set,
+ .blink_set = clevo_mail_led_blink,
+ .flags = LED_CORE_SUSPENDRESUME,
+};
+
+static int __devinit clevo_mail_led_probe(struct platform_device *pdev)
+{
+ return led_classdev_register(&pdev->dev, &clevo_mail_led);
+}
+
+static int clevo_mail_led_remove(struct platform_device *pdev)
+{
+ led_classdev_unregister(&clevo_mail_led);
+ return 0;
+}
+
+static struct platform_driver clevo_mail_led_driver = {
+ .probe = clevo_mail_led_probe,
+ .remove = clevo_mail_led_remove,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init clevo_mail_led_init(void)
+{
+ int error = 0;
+ int count = 0;
+
+ /* Check with the help of DMI if we are running on supported hardware */
+ if (!nodetect) {
+ count = dmi_check_system(mail_led_whitelist);
+ } else {
+ count = 1;
+ printk(KERN_ERR KBUILD_MODNAME ": Skipping DMI detection. "
+ "If the driver works on your hardware please "
+ "report model and the output of dmidecode in tracker "
+ "at http://sourceforge.net/projects/clevo-mailled/\n");
+ }
+
+ if (!count)
+ return -ENODEV;
+
+ pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);
+ if (!IS_ERR(pdev)) {
+ error = platform_driver_probe(&clevo_mail_led_driver,
+ clevo_mail_led_probe);
+ if (error) {
+ printk(KERN_ERR KBUILD_MODNAME
+ ": Can't probe platform driver\n");
+ platform_device_unregister(pdev);
+ }
+ } else
+ error = PTR_ERR(pdev);
+
+ return error;
+}
+
+static void __exit clevo_mail_led_exit(void)
+{
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&clevo_mail_led_driver);
+
+ clevo_mail_led_set(NULL, LED_OFF);
+}
+
+module_init(clevo_mail_led_init);
+module_exit(clevo_mail_led_exit);
diff --git a/drivers/leds/leds-cobalt-qube.c b/drivers/leds/leds-cobalt-qube.c
new file mode 100644
index 00000000000..da5fb016b1a
--- /dev/null
+++ b/drivers/leds/leds-cobalt-qube.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2006 - Florian Fainelli <florian@openwrt.org>
+ *
+ * Control the Cobalt Qube/RaQ front LED
+ */
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+
+#define LED_FRONT_LEFT 0x01
+#define LED_FRONT_RIGHT 0x02
+
+static void __iomem *led_port;
+static u8 led_value;
+
+static void qube_front_led_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ if (brightness)
+ led_value = LED_FRONT_LEFT | LED_FRONT_RIGHT;
+ else
+ led_value = ~(LED_FRONT_LEFT | LED_FRONT_RIGHT);
+ writeb(led_value, led_port);
+}
+
+static struct led_classdev qube_front_led = {
+ .name = "qube::front",
+ .brightness = LED_FULL,
+ .brightness_set = qube_front_led_set,
+ .default_trigger = "default-on",
+};
+
+static int __devinit cobalt_qube_led_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int retval;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EBUSY;
+
+ led_port = ioremap(res->start, resource_size(res));
+ if (!led_port)
+ return -ENOMEM;
+
+ led_value = LED_FRONT_LEFT | LED_FRONT_RIGHT;
+ writeb(led_value, led_port);
+
+ retval = led_classdev_register(&pdev->dev, &qube_front_led);
+ if (retval)
+ goto err_iounmap;
+
+ return 0;
+
+err_iounmap:
+ iounmap(led_port);
+ led_port = NULL;
+
+ return retval;
+}
+
+static int __devexit cobalt_qube_led_remove(struct platform_device *pdev)
+{
+ led_classdev_unregister(&qube_front_led);
+
+ if (led_port) {
+ iounmap(led_port);
+ led_port = NULL;
+ }
+
+ return 0;
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:cobalt-qube-leds");
+
+static struct platform_driver cobalt_qube_led_driver = {
+ .probe = cobalt_qube_led_probe,
+ .remove = __devexit_p(cobalt_qube_led_remove),
+ .driver = {
+ .name = "cobalt-qube-leds",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init cobalt_qube_led_init(void)
+{
+ return platform_driver_register(&cobalt_qube_led_driver);
+}
+
+static void __exit cobalt_qube_led_exit(void)
+{
+ platform_driver_unregister(&cobalt_qube_led_driver);
+}
+
+module_init(cobalt_qube_led_init);
+module_exit(cobalt_qube_led_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Front LED support for Cobalt Server");
+MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
diff --git a/drivers/leds/leds-cobalt-raq.c b/drivers/leds/leds-cobalt-raq.c
new file mode 100644
index 00000000000..438d4838463
--- /dev/null
+++ b/drivers/leds/leds-cobalt-raq.c
@@ -0,0 +1,138 @@
+/*
+ * LEDs driver for the Cobalt Raq series.
+ *
+ * Copyright (C) 2007 Yoichi Yuasa <yuasa@linux-mips.org>
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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
+ */
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/leds.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#define LED_WEB 0x04
+#define LED_POWER_OFF 0x08
+
+static void __iomem *led_port;
+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)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&led_value_lock, flags);
+
+ if (brightness)
+ led_value |= LED_WEB;
+ else
+ led_value &= ~LED_WEB;
+ writeb(led_value, led_port);
+
+ spin_unlock_irqrestore(&led_value_lock, flags);
+}
+
+static struct led_classdev raq_web_led = {
+ .name = "raq::web",
+ .brightness_set = raq_web_led_set,
+};
+
+static void raq_power_off_led_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&led_value_lock, flags);
+
+ if (brightness)
+ led_value |= LED_POWER_OFF;
+ else
+ led_value &= ~LED_POWER_OFF;
+ writeb(led_value, led_port);
+
+ spin_unlock_irqrestore(&led_value_lock, flags);
+}
+
+static struct led_classdev raq_power_off_led = {
+ .name = "raq::power-off",
+ .brightness_set = raq_power_off_led_set,
+ .default_trigger = "power-off",
+};
+
+static int __devinit cobalt_raq_led_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int retval;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EBUSY;
+
+ led_port = ioremap(res->start, resource_size(res));
+ if (!led_port)
+ return -ENOMEM;
+
+ retval = led_classdev_register(&pdev->dev, &raq_power_off_led);
+ if (retval)
+ goto err_iounmap;
+
+ retval = led_classdev_register(&pdev->dev, &raq_web_led);
+ if (retval)
+ goto err_unregister;
+
+ return 0;
+
+err_unregister:
+ led_classdev_unregister(&raq_power_off_led);
+
+err_iounmap:
+ iounmap(led_port);
+ led_port = NULL;
+
+ return retval;
+}
+
+static int __devexit cobalt_raq_led_remove(struct platform_device *pdev)
+{
+ led_classdev_unregister(&raq_power_off_led);
+ led_classdev_unregister(&raq_web_led);
+
+ if (led_port) {
+ iounmap(led_port);
+ led_port = NULL;
+ }
+
+ return 0;
+}
+
+static struct platform_driver cobalt_raq_led_driver = {
+ .probe = cobalt_raq_led_probe,
+ .remove = __devexit_p(cobalt_raq_led_remove),
+ .driver = {
+ .name = "cobalt-raq-leds",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init cobalt_raq_led_init(void)
+{
+ return platform_driver_register(&cobalt_raq_led_driver);
+}
+
+module_init(cobalt_raq_led_init);
diff --git a/drivers/leds/leds-da903x.c b/drivers/leds/leds-da903x.c
new file mode 100644
index 00000000000..1f3cc512eff
--- /dev/null
+++ b/drivers/leds/leds-da903x.c
@@ -0,0 +1,176 @@
+/*
+ * LEDs driver for Dialog Semiconductor DA9030/DA9034
+ *
+ * Copyright (C) 2008 Compulab, Ltd.
+ * Mike Rapoport <mike@compulab.co.il>
+ *
+ * Copyright (C) 2006-2008 Marvell International Ltd.
+ * Eric Miao <eric.miao@marvell.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/platform_device.h>
+#include <linux/leds.h>
+#include <linux/workqueue.h>
+#include <linux/mfd/da903x.h>
+
+#define DA9030_LED1_CONTROL 0x20
+#define DA9030_LED2_CONTROL 0x21
+#define DA9030_LED3_CONTROL 0x22
+#define DA9030_LED4_CONTROL 0x23
+#define DA9030_LEDPC_CONTROL 0x24
+#define DA9030_MISC_CONTROL_A 0x26 /* Vibrator Control */
+
+#define DA9034_LED1_CONTROL 0x35
+#define DA9034_LED2_CONTROL 0x36
+#define DA9034_VIBRA 0x40
+
+struct da903x_led {
+ struct led_classdev cdev;
+ struct work_struct work;
+ struct device *master;
+ enum led_brightness new_brightness;
+ int id;
+ int flags;
+};
+
+#define DA9030_LED_OFFSET(id) ((id) - DA9030_ID_LED_1)
+#define DA9034_LED_OFFSET(id) ((id) - DA9034_ID_LED_1)
+
+static void da903x_led_work(struct work_struct *work)
+{
+ struct da903x_led *led = container_of(work, struct da903x_led, work);
+ uint8_t val;
+ int offset;
+
+ switch (led->id) {
+ case DA9030_ID_LED_1:
+ case DA9030_ID_LED_2:
+ case DA9030_ID_LED_3:
+ case DA9030_ID_LED_4:
+ case DA9030_ID_LED_PC:
+ offset = DA9030_LED_OFFSET(led->id);
+ val = led->flags & ~0x87;
+ val |= (led->new_brightness) ? 0x80 : 0; /* EN bit */
+ val |= (0x7 - (led->new_brightness >> 5)) & 0x7; /* PWM<2:0> */
+ da903x_write(led->master, DA9030_LED1_CONTROL + offset, val);
+ break;
+ case DA9030_ID_VIBRA:
+ val = led->flags & ~0x80;
+ val |= (led->new_brightness) ? 0x80 : 0; /* EN bit */
+ da903x_write(led->master, DA9030_MISC_CONTROL_A, val);
+ break;
+ case DA9034_ID_LED_1:
+ case DA9034_ID_LED_2:
+ offset = DA9034_LED_OFFSET(led->id);
+ val = (led->new_brightness * 0x5f / LED_FULL) & 0x7f;
+ val |= (led->flags & DA9034_LED_RAMP) ? 0x80 : 0;
+ da903x_write(led->master, DA9034_LED1_CONTROL + offset, val);
+ break;
+ case DA9034_ID_VIBRA:
+ val = led->new_brightness & 0xfe;
+ da903x_write(led->master, DA9034_VIBRA, val);
+ break;
+ }
+}
+
+static void da903x_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct da903x_led *led;
+
+ led = container_of(led_cdev, struct da903x_led, cdev);
+ led->new_brightness = value;
+ schedule_work(&led->work);
+}
+
+static int __devinit da903x_led_probe(struct platform_device *pdev)
+{
+ struct led_info *pdata = pdev->dev.platform_data;
+ struct da903x_led *led;
+ int id, ret;
+
+ if (pdata == NULL)
+ return 0;
+
+ id = pdev->id;
+
+ if (!((id >= DA9030_ID_LED_1 && id <= DA9030_ID_VIBRA) ||
+ (id >= DA9034_ID_LED_1 && id <= DA9034_ID_VIBRA))) {
+ dev_err(&pdev->dev, "invalid LED ID (%d) specified\n", id);
+ return -EINVAL;
+ }
+
+ led = kzalloc(sizeof(struct da903x_led), GFP_KERNEL);
+ if (led == NULL) {
+ dev_err(&pdev->dev, "failed to alloc memory for LED%d\n", id);
+ return -ENOMEM;
+ }
+
+ led->cdev.name = pdata->name;
+ led->cdev.default_trigger = pdata->default_trigger;
+ led->cdev.brightness_set = da903x_led_set;
+ led->cdev.brightness = LED_OFF;
+
+ led->id = id;
+ led->flags = pdata->flags;
+ led->master = pdev->dev.parent;
+ led->new_brightness = LED_OFF;
+
+ INIT_WORK(&led->work, da903x_led_work);
+
+ ret = led_classdev_register(led->master, &led->cdev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register LED %d\n", id);
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, led);
+ return 0;
+
+err:
+ kfree(led);
+ return ret;
+}
+
+static int __devexit da903x_led_remove(struct platform_device *pdev)
+{
+ struct da903x_led *led = platform_get_drvdata(pdev);
+
+ led_classdev_unregister(&led->cdev);
+ kfree(led);
+ return 0;
+}
+
+static struct platform_driver da903x_led_driver = {
+ .driver = {
+ .name = "da903x-led",
+ .owner = THIS_MODULE,
+ },
+ .probe = da903x_led_probe,
+ .remove = __devexit_p(da903x_led_remove),
+};
+
+static int __init da903x_led_init(void)
+{
+ return platform_driver_register(&da903x_led_driver);
+}
+module_init(da903x_led_init);
+
+static void __exit da903x_led_exit(void)
+{
+ platform_driver_unregister(&da903x_led_driver);
+}
+module_exit(da903x_led_exit);
+
+MODULE_DESCRIPTION("LEDs driver for Dialog Semiconductor DA9030/DA9034");
+MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"
+ "Mike Rapoport <mike@compulab.co.il>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da903x-led");
diff --git a/drivers/leds/leds-dac124s085.c b/drivers/leds/leds-dac124s085.c
new file mode 100644
index 00000000000..2913d76ad3d
--- /dev/null
+++ b/drivers/leds/leds-dac124s085.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2008
+ * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de>
+ *
+ * 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 the DAC124S085 SPI DAC
+ */
+
+#include <linux/gfp.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/spi/spi.h>
+
+struct dac124s085_led {
+ struct led_classdev ldev;
+ struct spi_device *spi;
+ int id;
+ int brightness;
+ char name[sizeof("dac124s085-3")];
+
+ struct mutex mutex;
+ struct work_struct work;
+ spinlock_t lock;
+};
+
+struct dac124s085 {
+ struct dac124s085_led leds[4];
+};
+
+#define REG_WRITE (0 << 12)
+#define REG_WRITE_UPDATE (1 << 12)
+#define ALL_WRITE_UPDATE (2 << 12)
+#define POWER_DOWN_OUTPUT (3 << 12)
+
+static void dac124s085_led_work(struct work_struct *work)
+{
+ struct dac124s085_led *led = container_of(work, struct dac124s085_led,
+ work);
+ u16 word;
+
+ mutex_lock(&led->mutex);
+ word = cpu_to_le16(((led->id) << 14) | REG_WRITE_UPDATE |
+ (led->brightness & 0xfff));
+ spi_write(led->spi, (const u8 *)&word, sizeof(word));
+ mutex_unlock(&led->mutex);
+}
+
+static void dac124s085_set_brightness(struct led_classdev *ldev,
+ enum led_brightness brightness)
+{
+ struct dac124s085_led *led = container_of(ldev, struct dac124s085_led,
+ ldev);
+
+ spin_lock(&led->lock);
+ led->brightness = brightness;
+ schedule_work(&led->work);
+ spin_unlock(&led->lock);
+}
+
+static int dac124s085_probe(struct spi_device *spi)
+{
+ struct dac124s085 *dac;
+ struct dac124s085_led *led;
+ int i, ret;
+
+ dac = kzalloc(sizeof(*dac), GFP_KERNEL);
+ if (!dac)
+ return -ENOMEM;
+
+ spi->bits_per_word = 16;
+
+ for (i = 0; i < ARRAY_SIZE(dac->leds); i++) {
+ led = dac->leds + i;
+ led->id = i;
+ led->brightness = LED_OFF;
+ led->spi = spi;
+ snprintf(led->name, sizeof(led->name), "dac124s085-%d", i);
+ spin_lock_init(&led->lock);
+ INIT_WORK(&led->work, dac124s085_led_work);
+ mutex_init(&led->mutex);
+ led->ldev.name = led->name;
+ led->ldev.brightness = LED_OFF;
+ led->ldev.max_brightness = 0xfff;
+ led->ldev.brightness_set = dac124s085_set_brightness;
+ ret = led_classdev_register(&spi->dev, &led->ldev);
+ if (ret < 0)
+ goto eledcr;
+ }
+
+ spi_set_drvdata(spi, dac);
+
+ return 0;
+
+eledcr:
+ while (i--)
+ led_classdev_unregister(&dac->leds[i].ldev);
+
+ spi_set_drvdata(spi, NULL);
+ kfree(dac);
+ return ret;
+}
+
+static int dac124s085_remove(struct spi_device *spi)
+{
+ struct dac124s085 *dac = spi_get_drvdata(spi);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dac->leds); i++) {
+ led_classdev_unregister(&dac->leds[i].ldev);
+ cancel_work_sync(&dac->leds[i].work);
+ }
+
+ spi_set_drvdata(spi, NULL);
+ kfree(dac);
+
+ return 0;
+}
+
+static struct spi_driver dac124s085_driver = {
+ .probe = dac124s085_probe,
+ .remove = dac124s085_remove,
+ .driver = {
+ .name = "dac124s085",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init dac124s085_leds_init(void)
+{
+ return spi_register_driver(&dac124s085_driver);
+}
+
+static void __exit dac124s085_leds_exit(void)
+{
+ spi_unregister_driver(&dac124s085_driver);
+}
+
+module_init(dac124s085_leds_init);
+module_exit(dac124s085_leds_exit);
+
+MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>");
+MODULE_DESCRIPTION("DAC124S085 LED driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spi:dac124s085");
diff --git a/drivers/leds/leds-fsg.c b/drivers/leds/leds-fsg.c
new file mode 100644
index 00000000000..d11d05be0de
--- /dev/null
+++ b/drivers/leds/leds-fsg.c
@@ -0,0 +1,243 @@
+/*
+ * 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 <mach/hardware.h>
+#include <asm/io.h>
+
+#define FSG_LED_WLAN_BIT 0
+#define FSG_LED_WAN_BIT 1
+#define FSG_LED_SATA_BIT 2
+#define FSG_LED_USB_BIT 4
+#define FSG_LED_RING_BIT 5
+#define FSG_LED_SYNC_BIT 7
+
+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,
+ .flags = LED_CORE_SUSPENDRESUME,
+};
+
+static struct led_classdev fsg_wan_led = {
+ .name = "fsg:blue:wan",
+ .brightness_set = fsg_led_wan_set,
+ .flags = LED_CORE_SUSPENDRESUME,
+};
+
+static struct led_classdev fsg_sata_led = {
+ .name = "fsg:blue:sata",
+ .brightness_set = fsg_led_sata_set,
+ .flags = LED_CORE_SUSPENDRESUME,
+};
+
+static struct led_classdev fsg_usb_led = {
+ .name = "fsg:blue:usb",
+ .brightness_set = fsg_led_usb_set,
+ .flags = LED_CORE_SUSPENDRESUME,
+};
+
+static struct led_classdev fsg_sync_led = {
+ .name = "fsg:blue:sync",
+ .brightness_set = fsg_led_sync_set,
+ .flags = LED_CORE_SUSPENDRESUME,
+};
+
+static struct led_classdev fsg_ring_led = {
+ .name = "fsg:blue:ring",
+ .brightness_set = fsg_led_ring_set,
+ .flags = LED_CORE_SUSPENDRESUME,
+};
+
+
+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;
+
+ 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;
+
+ return ret;
+
+ 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:
+ iounmap(latch_address);
+ failremap:
+
+ return ret;
+}
+
+static int fsg_led_remove(struct platform_device *pdev)
+{
+ 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);
+
+ iounmap(latch_address);
+
+ return 0;
+}
+
+
+static struct platform_driver fsg_led_driver = {
+ .probe = fsg_led_probe,
+ .remove = fsg_led_remove,
+ .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");
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c
new file mode 100644
index 00000000000..0823e2622e8
--- /dev/null
+++ b/drivers/leds/leds-gpio.c
@@ -0,0 +1,335 @@
+/*
+ * LEDs driver for GPIOs
+ *
+ * Copyright (C) 2007 8D Technologies inc.
+ * Raphael Assenat <raph@8d.com>
+ * Copyright (C) 2008 Freescale Semiconductor, Inc.
+ *
+ * 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 <linux/workqueue.h>
+
+#include <asm/gpio.h>
+
+struct gpio_led_data {
+ struct led_classdev cdev;
+ unsigned gpio;
+ struct work_struct work;
+ 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)
+{
+ struct gpio_led_data *led_dat =
+ container_of(work, struct gpio_led_data, work);
+
+ gpio_set_value_cansleep(led_dat->gpio, led_dat->new_level);
+}
+
+static void gpio_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct gpio_led_data *led_dat =
+ container_of(led_cdev, struct gpio_led_data, cdev);
+ int level;
+
+ if (value == LED_OFF)
+ level = 0;
+ else
+ level = 1;
+
+ if (led_dat->active_low)
+ level = !level;
+
+ /* Setting GPIOs with I2C/etc requires a task context, and we don't
+ * seem to have a reliable way to know if we're already in one; so
+ * let's just assume the worst.
+ */
+ if (led_dat->can_sleep) {
+ led_dat->new_level = level;
+ schedule_work(&led_dat->work);
+ } else
+ 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 __devinit create_gpio_led(const struct gpio_led *template,
+ struct gpio_led_data *led_dat, struct device *parent,
+ int (*blink_set)(unsigned, unsigned long *, unsigned long *))
+{
+ int ret, state;
+
+ led_dat->gpio = -1;
+
+ /* skip leds that aren't available */
+ if (!gpio_is_valid(template->gpio)) {
+ printk(KERN_INFO "Skipping unavailable LED gpio %d (%s)\n",
+ template->gpio, template->name);
+ return 0;
+ }
+
+ ret = gpio_request(template->gpio, template->name);
+ if (ret < 0)
+ return ret;
+
+ led_dat->cdev.name = template->name;
+ led_dat->cdev.default_trigger = template->default_trigger;
+ led_dat->gpio = template->gpio;
+ led_dat->can_sleep = gpio_cansleep(template->gpio);
+ led_dat->active_low = template->active_low;
+ if (blink_set) {
+ led_dat->platform_gpio_blink_set = blink_set;
+ led_dat->cdev.blink_set = gpio_blink_set;
+ }
+ led_dat->cdev.brightness_set = gpio_led_set;
+ if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP)
+ state = !!gpio_get_value(led_dat->gpio) ^ led_dat->active_low;
+ else
+ state = (template->default_state == LEDS_GPIO_DEFSTATE_ON);
+ led_dat->cdev.brightness = state ? LED_FULL : LED_OFF;
+ if (!template->retain_state_suspended)
+ led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
+
+ ret = gpio_direction_output(led_dat->gpio, led_dat->active_low ^ state);
+ if (ret < 0)
+ goto err;
+
+ INIT_WORK(&led_dat->work, gpio_led_work);
+
+ ret = led_classdev_register(parent, &led_dat->cdev);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+err:
+ gpio_free(led_dat->gpio);
+ return ret;
+}
+
+static void delete_gpio_led(struct gpio_led_data *led)
+{
+ if (!gpio_is_valid(led->gpio))
+ return;
+ led_classdev_unregister(&led->cdev);
+ cancel_work_sync(&led->work);
+ gpio_free(led->gpio);
+}
+
+#ifdef CONFIG_LEDS_GPIO_PLATFORM
+static int __devinit gpio_led_probe(struct platform_device *pdev)
+{
+ struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
+ struct gpio_led_data *leds_data;
+ int i, ret = 0;
+
+ if (!pdata)
+ return -EBUSY;
+
+ leds_data = kzalloc(sizeof(struct gpio_led_data) * pdata->num_leds,
+ GFP_KERNEL);
+ if (!leds_data)
+ return -ENOMEM;
+
+ for (i = 0; i < pdata->num_leds; i++) {
+ ret = create_gpio_led(&pdata->leds[i], &leds_data[i],
+ &pdev->dev, pdata->gpio_blink_set);
+ if (ret < 0)
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, leds_data);
+
+ return 0;
+
+err:
+ for (i = i - 1; i >= 0; i--)
+ delete_gpio_led(&leds_data[i]);
+
+ kfree(leds_data);
+
+ return ret;
+}
+
+static int __devexit gpio_led_remove(struct platform_device *pdev)
+{
+ int i;
+ struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
+ struct gpio_led_data *leds_data;
+
+ leds_data = platform_get_drvdata(pdev);
+
+ for (i = 0; i < pdata->num_leds; i++)
+ delete_gpio_led(&leds_data[i]);
+
+ kfree(leds_data);
+
+ return 0;
+}
+
+static struct platform_driver gpio_led_driver = {
+ .probe = gpio_led_probe,
+ .remove = __devexit_p(gpio_led_remove),
+ .driver = {
+ .name = "leds-gpio",
+ .owner = THIS_MODULE,
+ },
+};
+
+MODULE_ALIAS("platform:leds-gpio");
+#endif /* CONFIG_LEDS_GPIO_PLATFORM */
+
+/* Code to create from OpenFirmware platform devices */
+#ifdef CONFIG_LEDS_GPIO_OF
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+
+struct gpio_led_of_platform_data {
+ int num_leds;
+ struct gpio_led_data led_data[];
+};
+
+static int __devinit of_gpio_leds_probe(struct of_device *ofdev,
+ const struct of_device_id *match)
+{
+ struct device_node *np = ofdev->node, *child;
+ struct gpio_led_of_platform_data *pdata;
+ int count = 0, ret;
+
+ /* count LEDs defined by this device, so we know how much to allocate */
+ for_each_child_of_node(np, child)
+ count++;
+ if (!count)
+ return 0; /* or ENODEV? */
+
+ pdata = kzalloc(sizeof(*pdata) + sizeof(struct gpio_led_data) * count,
+ GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ for_each_child_of_node(np, child) {
+ struct gpio_led led = {};
+ enum of_gpio_flags flags;
+ const char *state;
+
+ led.gpio = of_get_gpio_flags(child, 0, &flags);
+ led.active_low = flags & OF_GPIO_ACTIVE_LOW;
+ led.name = of_get_property(child, "label", NULL) ? : child->name;
+ led.default_trigger =
+ of_get_property(child, "linux,default-trigger", NULL);
+ state = of_get_property(child, "default-state", NULL);
+ if (state) {
+ if (!strcmp(state, "keep"))
+ led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
+ else if(!strcmp(state, "on"))
+ led.default_state = LEDS_GPIO_DEFSTATE_ON;
+ else
+ led.default_state = LEDS_GPIO_DEFSTATE_OFF;
+ }
+
+ ret = create_gpio_led(&led, &pdata->led_data[pdata->num_leds++],
+ &ofdev->dev, NULL);
+ if (ret < 0) {
+ of_node_put(child);
+ goto err;
+ }
+ }
+
+ dev_set_drvdata(&ofdev->dev, pdata);
+
+ return 0;
+
+err:
+ for (count = pdata->num_leds - 2; count >= 0; count--)
+ delete_gpio_led(&pdata->led_data[count]);
+
+ kfree(pdata);
+
+ return ret;
+}
+
+static int __devexit of_gpio_leds_remove(struct of_device *ofdev)
+{
+ struct gpio_led_of_platform_data *pdata = dev_get_drvdata(&ofdev->dev);
+ int i;
+
+ for (i = 0; i < pdata->num_leds; i++)
+ delete_gpio_led(&pdata->led_data[i]);
+
+ kfree(pdata);
+
+ dev_set_drvdata(&ofdev->dev, NULL);
+
+ return 0;
+}
+
+static const struct of_device_id of_gpio_leds_match[] = {
+ { .compatible = "gpio-leds", },
+ {},
+};
+
+static struct of_platform_driver of_gpio_leds_driver = {
+ .driver = {
+ .name = "of_gpio_leds",
+ .owner = THIS_MODULE,
+ },
+ .match_table = of_gpio_leds_match,
+ .probe = of_gpio_leds_probe,
+ .remove = __devexit_p(of_gpio_leds_remove),
+};
+#endif
+
+static int __init gpio_led_init(void)
+{
+ int ret;
+
+#ifdef CONFIG_LEDS_GPIO_PLATFORM
+ ret = platform_driver_register(&gpio_led_driver);
+ if (ret)
+ return ret;
+#endif
+#ifdef CONFIG_LEDS_GPIO_OF
+ ret = of_register_platform_driver(&of_gpio_leds_driver);
+#endif
+#ifdef CONFIG_LEDS_GPIO_PLATFORM
+ if (ret)
+ platform_driver_unregister(&gpio_led_driver);
+#endif
+
+ return ret;
+}
+
+static void __exit gpio_led_exit(void)
+{
+#ifdef CONFIG_LEDS_GPIO_PLATFORM
+ platform_driver_unregister(&gpio_led_driver);
+#endif
+#ifdef CONFIG_LEDS_GPIO_OF
+ of_unregister_platform_driver(&of_gpio_leds_driver);
+#endif
+}
+
+module_init(gpio_led_init);
+module_exit(gpio_led_exit);
+
+MODULE_AUTHOR("Raphael Assenat <raph@8d.com>, Trent Piepho <tpiepho@freescale.com>");
+MODULE_DESCRIPTION("GPIO LED driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-h1940.c b/drivers/leds/leds-h1940.c
new file mode 100644
index 00000000000..173d104d9ff
--- /dev/null
+++ b/drivers/leds/leds-h1940.c
@@ -0,0 +1,170 @@
+/*
+ * drivers/leds/leds-h1940.c
+ * Copyright (c) Arnaud Patard <arnaud.patard@rtp-net.org>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ * H1940 leds driver
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/leds.h>
+#include <linux/gpio.h>
+
+#include <mach/regs-gpio.h>
+#include <mach/hardware.h>
+#include <mach/h1940-latch.h>
+
+/*
+ * Green led.
+ */
+static 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;
+ }
+}
+
+static struct led_classdev h1940_greenled = {
+ .name = "h1940:green",
+ .brightness_set = h1940_greenled_set,
+ .default_trigger = "h1940-charger",
+};
+
+/*
+ * Red led.
+ */
+static 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;
+ }
+}
+
+static struct led_classdev h1940_redled = {
+ .name = "h1940:red",
+ .brightness_set = h1940_redled_set,
+ .default_trigger = "h1940-charger",
+};
+
+/*
+ * Blue led.
+ * (it can only be blue flashing led)
+ */
+static 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);
+ } else {
+ h1940_latch_control(H1940_LATCH_LED_FLASH, 0);
+ s3c2410_gpio_setpin(S3C2410_GPA3, 0);
+ }
+
+}
+
+static struct led_classdev h1940_blueled = {
+ .name = "h1940:blue",
+ .brightness_set = h1940_blueled_set,
+ .default_trigger = "h1940-bluetooth",
+};
+
+static int __devinit h1940leds_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ ret = led_classdev_register(&pdev->dev, &h1940_greenled);
+ if (ret)
+ goto err_green;
+
+ ret = led_classdev_register(&pdev->dev, &h1940_redled);
+ if (ret)
+ goto err_red;
+
+ ret = led_classdev_register(&pdev->dev, &h1940_blueled);
+ if (ret)
+ goto err_blue;
+
+ return 0;
+
+err_blue:
+ led_classdev_unregister(&h1940_redled);
+err_red:
+ led_classdev_unregister(&h1940_greenled);
+err_green:
+ return ret;
+}
+
+static int h1940leds_remove(struct platform_device *pdev)
+{
+ led_classdev_unregister(&h1940_greenled);
+ led_classdev_unregister(&h1940_redled);
+ led_classdev_unregister(&h1940_blueled);
+ return 0;
+}
+
+
+static struct platform_driver h1940leds_driver = {
+ .driver = {
+ .name = "h1940-leds",
+ .owner = THIS_MODULE,
+ },
+ .probe = h1940leds_probe,
+ .remove = h1940leds_remove,
+};
+
+
+static int __init h1940leds_init(void)
+{
+ return platform_driver_register(&h1940leds_driver);
+}
+
+static void __exit h1940leds_exit(void)
+{
+ platform_driver_unregister(&h1940leds_driver);
+}
+
+module_init(h1940leds_init);
+module_exit(h1940leds_exit);
+
+MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
+MODULE_DESCRIPTION("LED driver for the iPAQ H1940");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:h1940-leds");
diff --git a/drivers/leds/leds-hp6xx.c b/drivers/leds/leds-hp6xx.c
new file mode 100644
index 00000000000..e4ce1fd4633
--- /dev/null
+++ b/drivers/leds/leds-hp6xx.c
@@ -0,0 +1,108 @@
+/*
+ * LED Triggers Core
+ * For the HP Jornada 620/660/680/690 handhelds
+ *
+ * Copyright 2008 Kristoffer Ericson <kristoffer.ericson@gmail.com>
+ * this driver is based on leds-spitz.c by Richard Purdie.
+ *
+ * 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/hd64461.h>
+#include <mach/hp6xx.h>
+
+static void hp6xxled_green_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ u8 v8;
+
+ v8 = inb(PKDR);
+ if (value)
+ outb(v8 & (~PKDR_LED_GREEN), PKDR);
+ else
+ outb(v8 | PKDR_LED_GREEN, PKDR);
+}
+
+static void hp6xxled_red_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ u16 v16;
+
+ v16 = inw(HD64461_GPBDR);
+ if (value)
+ outw(v16 & (~HD64461_GPBDR_LED_RED), HD64461_GPBDR);
+ else
+ outw(v16 | HD64461_GPBDR_LED_RED, HD64461_GPBDR);
+}
+
+static struct led_classdev hp6xx_red_led = {
+ .name = "hp6xx:red",
+ .default_trigger = "hp6xx-charge",
+ .brightness_set = hp6xxled_red_set,
+ .flags = LED_CORE_SUSPENDRESUME,
+};
+
+static struct led_classdev hp6xx_green_led = {
+ .name = "hp6xx:green",
+ .default_trigger = "ide-disk",
+ .brightness_set = hp6xxled_green_set,
+ .flags = LED_CORE_SUSPENDRESUME,
+};
+
+static int hp6xxled_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ ret = led_classdev_register(&pdev->dev, &hp6xx_red_led);
+ if (ret < 0)
+ return ret;
+
+ ret = led_classdev_register(&pdev->dev, &hp6xx_green_led);
+ if (ret < 0)
+ led_classdev_unregister(&hp6xx_red_led);
+
+ return ret;
+}
+
+static int hp6xxled_remove(struct platform_device *pdev)
+{
+ led_classdev_unregister(&hp6xx_red_led);
+ led_classdev_unregister(&hp6xx_green_led);
+
+ return 0;
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:hp6xx-led");
+
+static struct platform_driver hp6xxled_driver = {
+ .probe = hp6xxled_probe,
+ .remove = hp6xxled_remove,
+ .driver = {
+ .name = "hp6xx-led",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init hp6xxled_init(void)
+{
+ return platform_driver_register(&hp6xxled_driver);
+}
+
+static void __exit hp6xxled_exit(void)
+{
+ platform_driver_unregister(&hp6xxled_driver);
+}
+
+module_init(hp6xxled_init);
+module_exit(hp6xxled_exit);
+
+MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson@gmail.com>");
+MODULE_DESCRIPTION("HP Jornada 6xx LED driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-locomo.c b/drivers/leds/leds-locomo.c
new file mode 100644
index 00000000000..1f7c10f6b7f
--- /dev/null
+++ b/drivers/leds/leds-locomo.c
@@ -0,0 +1,96 @@
+/*
+ * linux/drivers/leds/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/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/leds.h>
+
+#include <mach/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->dev->parent);
+ 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:charge",
+ .default_trigger = "main-battery-charging",
+ .brightness_set = locomoled_brightness_set0,
+};
+
+static struct led_classdev locomo_led1 = {
+ .name = "locomo:green:mail",
+ .default_trigger = "nand-disk",
+ .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-lp3944.c b/drivers/leds/leds-lp3944.c
new file mode 100644
index 00000000000..5946208ba26
--- /dev/null
+++ b/drivers/leds/leds-lp3944.c
@@ -0,0 +1,466 @@
+/*
+ * leds-lp3944.c - driver for National Semiconductor LP3944 Funlight Chip
+ *
+ * Copyright (C) 2009 Antonio Ospite <ospite@studenti.unina.it>
+ *
+ * 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.
+ *
+ */
+
+/*
+ * I2C driver for National Semiconductor LP3944 Funlight Chip
+ * http://www.national.com/pf/LP/LP3944.html
+ *
+ * This helper chip can drive up to 8 leds, with two programmable DIM modes;
+ * it could even be used as a gpio expander but this driver assumes it is used
+ * as a led controller.
+ *
+ * The DIM modes are used to set _blink_ patterns for leds, the pattern is
+ * specified supplying two parameters:
+ * - period: from 0s to 1.6s
+ * - duty cycle: percentage of the period the led is on, from 0 to 100
+ *
+ * LP3944 can be found on Motorola A910 smartphone, where it drives the rgb
+ * leds, the camera flash light and the displays backlights.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/leds.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/leds-lp3944.h>
+
+/* Read Only Registers */
+#define LP3944_REG_INPUT1 0x00 /* LEDs 0-7 InputRegister (Read Only) */
+#define LP3944_REG_REGISTER1 0x01 /* None (Read Only) */
+
+#define LP3944_REG_PSC0 0x02 /* Frequency Prescaler 0 (R/W) */
+#define LP3944_REG_PWM0 0x03 /* PWM Register 0 (R/W) */
+#define LP3944_REG_PSC1 0x04 /* Frequency Prescaler 1 (R/W) */
+#define LP3944_REG_PWM1 0x05 /* PWM Register 1 (R/W) */
+#define LP3944_REG_LS0 0x06 /* LEDs 0-3 Selector (R/W) */
+#define LP3944_REG_LS1 0x07 /* LEDs 4-7 Selector (R/W) */
+
+/* These registers are not used to control leds in LP3944, they can store
+ * arbitrary values which the chip will ignore.
+ */
+#define LP3944_REG_REGISTER8 0x08
+#define LP3944_REG_REGISTER9 0x09
+
+#define LP3944_DIM0 0
+#define LP3944_DIM1 1
+
+/* period in ms */
+#define LP3944_PERIOD_MIN 0
+#define LP3944_PERIOD_MAX 1600
+
+/* duty cycle is a percentage */
+#define LP3944_DUTY_CYCLE_MIN 0
+#define LP3944_DUTY_CYCLE_MAX 100
+
+#define ldev_to_led(c) container_of(c, struct lp3944_led_data, ldev)
+
+/* Saved data */
+struct lp3944_led_data {
+ u8 id;
+ enum lp3944_type type;
+ enum lp3944_status status;
+ struct led_classdev ldev;
+ struct i2c_client *client;
+ struct work_struct work;
+};
+
+struct lp3944_data {
+ struct mutex lock;
+ struct i2c_client *client;
+ struct lp3944_led_data leds[LP3944_LEDS_MAX];
+};
+
+static int lp3944_reg_read(struct i2c_client *client, u8 reg, u8 *value)
+{
+ int tmp;
+
+ tmp = i2c_smbus_read_byte_data(client, reg);
+ if (tmp < 0)
+ return -EINVAL;
+
+ *value = tmp;
+
+ return 0;
+}
+
+static int lp3944_reg_write(struct i2c_client *client, u8 reg, u8 value)
+{
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/**
+ * Set the period for DIM status
+ *
+ * @client: the i2c client
+ * @dim: either LP3944_DIM0 or LP3944_DIM1
+ * @period: period of a blink, that is a on/off cycle, expressed in ms.
+ */
+static int lp3944_dim_set_period(struct i2c_client *client, u8 dim, u16 period)
+{
+ u8 psc_reg;
+ u8 psc_value;
+ int err;
+
+ if (dim == LP3944_DIM0)
+ psc_reg = LP3944_REG_PSC0;
+ else if (dim == LP3944_DIM1)
+ psc_reg = LP3944_REG_PSC1;
+ else
+ return -EINVAL;
+
+ /* Convert period to Prescaler value */
+ if (period > LP3944_PERIOD_MAX)
+ return -EINVAL;
+
+ psc_value = (period * 255) / LP3944_PERIOD_MAX;
+
+ err = lp3944_reg_write(client, psc_reg, psc_value);
+
+ return err;
+}
+
+/**
+ * Set the duty cycle for DIM status
+ *
+ * @client: the i2c client
+ * @dim: either LP3944_DIM0 or LP3944_DIM1
+ * @duty_cycle: percentage of a period during which a led is ON
+ */
+static int lp3944_dim_set_dutycycle(struct i2c_client *client, u8 dim,
+ u8 duty_cycle)
+{
+ u8 pwm_reg;
+ u8 pwm_value;
+ int err;
+
+ if (dim == LP3944_DIM0)
+ pwm_reg = LP3944_REG_PWM0;
+ else if (dim == LP3944_DIM1)
+ pwm_reg = LP3944_REG_PWM1;
+ else
+ return -EINVAL;
+
+ /* Convert duty cycle to PWM value */
+ if (duty_cycle > LP3944_DUTY_CYCLE_MAX)
+ return -EINVAL;
+
+ pwm_value = (duty_cycle * 255) / LP3944_DUTY_CYCLE_MAX;
+
+ err = lp3944_reg_write(client, pwm_reg, pwm_value);
+
+ return err;
+}
+
+/**
+ * Set the led status
+ *
+ * @led: a lp3944_led_data structure
+ * @status: one of LP3944_LED_STATUS_OFF
+ * LP3944_LED_STATUS_ON
+ * LP3944_LED_STATUS_DIM0
+ * LP3944_LED_STATUS_DIM1
+ */
+static int lp3944_led_set(struct lp3944_led_data *led, u8 status)
+{
+ struct lp3944_data *data = i2c_get_clientdata(led->client);
+ u8 id = led->id;
+ u8 reg;
+ u8 val = 0;
+ int err;
+
+ dev_dbg(&led->client->dev, "%s: %s, status before normalization:%d\n",
+ __func__, led->ldev.name, status);
+
+ switch (id) {
+ case LP3944_LED0:
+ case LP3944_LED1:
+ case LP3944_LED2:
+ case LP3944_LED3:
+ reg = LP3944_REG_LS0;
+ break;
+ case LP3944_LED4:
+ case LP3944_LED5:
+ case LP3944_LED6:
+ case LP3944_LED7:
+ id -= LP3944_LED4;
+ reg = LP3944_REG_LS1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (status > LP3944_LED_STATUS_DIM1)
+ return -EINVAL;
+
+ /* invert only 0 and 1, leave unchanged the other values,
+ * remember we are abusing status to set blink patterns
+ */
+ if (led->type == LP3944_LED_TYPE_LED_INVERTED && status < 2)
+ status = 1 - status;
+
+ mutex_lock(&data->lock);
+ lp3944_reg_read(led->client, reg, &val);
+
+ val &= ~(LP3944_LED_STATUS_MASK << (id << 1));
+ val |= (status << (id << 1));
+
+ dev_dbg(&led->client->dev, "%s: %s, reg:%d id:%d status:%d val:%#x\n",
+ __func__, led->ldev.name, reg, id, status, val);
+
+ /* set led status */
+ err = lp3944_reg_write(led->client, reg, val);
+ mutex_unlock(&data->lock);
+
+ return err;
+}
+
+static int lp3944_led_set_blink(struct led_classdev *led_cdev,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ struct lp3944_led_data *led = ldev_to_led(led_cdev);
+ u16 period;
+ u8 duty_cycle;
+ int err;
+
+ /* units are in ms */
+ if (*delay_on + *delay_off > LP3944_PERIOD_MAX)
+ return -EINVAL;
+
+ if (*delay_on == 0 && *delay_off == 0) {
+ /* Special case: the leds subsystem requires a default user
+ * friendly blink pattern for the LED. Let's blink the led
+ * slowly (1Hz).
+ */
+ *delay_on = 500;
+ *delay_off = 500;
+ }
+
+ period = (*delay_on) + (*delay_off);
+
+ /* duty_cycle is the percentage of period during which the led is ON */
+ duty_cycle = 100 * (*delay_on) / period;
+
+ /* invert duty cycle for inverted leds, this has the same effect of
+ * swapping delay_on and delay_off
+ */
+ if (led->type == LP3944_LED_TYPE_LED_INVERTED)
+ duty_cycle = 100 - duty_cycle;
+
+ /* NOTE: using always the first DIM mode, this means that all leds
+ * will have the same blinking pattern.
+ *
+ * We could find a way later to have two leds blinking in hardware
+ * with different patterns at the same time, falling back to software
+ * control for the other ones.
+ */
+ err = lp3944_dim_set_period(led->client, LP3944_DIM0, period);
+ if (err)
+ return err;
+
+ err = lp3944_dim_set_dutycycle(led->client, LP3944_DIM0, duty_cycle);
+ if (err)
+ return err;
+
+ dev_dbg(&led->client->dev, "%s: OK hardware accelerated blink!\n",
+ __func__);
+
+ led->status = LP3944_LED_STATUS_DIM0;
+ schedule_work(&led->work);
+
+ return 0;
+}
+
+static void lp3944_led_set_brightness(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct lp3944_led_data *led = ldev_to_led(led_cdev);
+
+ dev_dbg(&led->client->dev, "%s: %s, %d\n",
+ __func__, led_cdev->name, brightness);
+
+ led->status = brightness;
+ schedule_work(&led->work);
+}
+
+static void lp3944_led_work(struct work_struct *work)
+{
+ struct lp3944_led_data *led;
+
+ led = container_of(work, struct lp3944_led_data, work);
+ lp3944_led_set(led, led->status);
+}
+
+static int lp3944_configure(struct i2c_client *client,
+ struct lp3944_data *data,
+ struct lp3944_platform_data *pdata)
+{
+ int i, err = 0;
+
+ for (i = 0; i < pdata->leds_size; i++) {
+ struct lp3944_led *pled = &pdata->leds[i];
+ struct lp3944_led_data *led = &data->leds[i];
+ led->client = client;
+ led->id = i;
+
+ switch (pled->type) {
+
+ case LP3944_LED_TYPE_LED:
+ case LP3944_LED_TYPE_LED_INVERTED:
+ led->type = pled->type;
+ led->status = pled->status;
+ led->ldev.name = pled->name;
+ led->ldev.max_brightness = 1;
+ led->ldev.brightness_set = lp3944_led_set_brightness;
+ led->ldev.blink_set = lp3944_led_set_blink;
+ led->ldev.flags = LED_CORE_SUSPENDRESUME;
+
+ INIT_WORK(&led->work, lp3944_led_work);
+ err = led_classdev_register(&client->dev, &led->ldev);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "couldn't register LED %s\n",
+ led->ldev.name);
+ goto exit;
+ }
+
+ /* to expose the default value to userspace */
+ led->ldev.brightness = led->status;
+
+ /* Set the default led status */
+ err = lp3944_led_set(led, led->status);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "%s couldn't set STATUS %d\n",
+ led->ldev.name, led->status);
+ goto exit;
+ }
+ break;
+
+ case LP3944_LED_TYPE_NONE:
+ default:
+ break;
+
+ }
+ }
+ return 0;
+
+exit:
+ if (i > 0)
+ for (i = i - 1; i >= 0; i--)
+ switch (pdata->leds[i].type) {
+
+ case LP3944_LED_TYPE_LED:
+ case LP3944_LED_TYPE_LED_INVERTED:
+ led_classdev_unregister(&data->leds[i].ldev);
+ cancel_work_sync(&data->leds[i].work);
+ break;
+
+ case LP3944_LED_TYPE_NONE:
+ default:
+ break;
+ }
+
+ return err;
+}
+
+static int __devinit lp3944_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct lp3944_platform_data *lp3944_pdata = client->dev.platform_data;
+ struct lp3944_data *data;
+
+ if (lp3944_pdata == NULL) {
+ dev_err(&client->dev, "no platform data\n");
+ return -EINVAL;
+ }
+
+ /* Let's see whether this adapter can support what we need. */
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&client->dev, "insufficient functionality!\n");
+ return -ENODEV;
+ }
+
+ data = kzalloc(sizeof(struct lp3944_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ i2c_set_clientdata(client, data);
+
+ mutex_init(&data->lock);
+
+ dev_info(&client->dev, "lp3944 enabled\n");
+
+ lp3944_configure(client, data, lp3944_pdata);
+ return 0;
+}
+
+static int __devexit lp3944_remove(struct i2c_client *client)
+{
+ struct lp3944_platform_data *pdata = client->dev.platform_data;
+ struct lp3944_data *data = i2c_get_clientdata(client);
+ int i;
+
+ for (i = 0; i < pdata->leds_size; i++)
+ switch (data->leds[i].type) {
+ case LP3944_LED_TYPE_LED:
+ case LP3944_LED_TYPE_LED_INVERTED:
+ led_classdev_unregister(&data->leds[i].ldev);
+ cancel_work_sync(&data->leds[i].work);
+ break;
+
+ case LP3944_LED_TYPE_NONE:
+ default:
+ break;
+ }
+
+ kfree(data);
+ i2c_set_clientdata(client, NULL);
+
+ return 0;
+}
+
+/* lp3944 i2c driver struct */
+static const struct i2c_device_id lp3944_id[] = {
+ {"lp3944", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, lp3944_id);
+
+static struct i2c_driver lp3944_driver = {
+ .driver = {
+ .name = "lp3944",
+ },
+ .probe = lp3944_probe,
+ .remove = __devexit_p(lp3944_remove),
+ .id_table = lp3944_id,
+};
+
+static int __init lp3944_module_init(void)
+{
+ return i2c_add_driver(&lp3944_driver);
+}
+
+static void __exit lp3944_module_exit(void)
+{
+ i2c_del_driver(&lp3944_driver);
+}
+
+module_init(lp3944_module_init);
+module_exit(lp3944_module_exit);
+
+MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>");
+MODULE_DESCRIPTION("LP3944 Fun Light Chip");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-lt3593.c b/drivers/leds/leds-lt3593.c
new file mode 100644
index 00000000000..fee40a84195
--- /dev/null
+++ b/drivers/leds/leds-lt3593.c
@@ -0,0 +1,217 @@
+/*
+ * LEDs driver for LT3593 controllers
+ *
+ * See the datasheet at http://cds.linear.com/docs/Datasheet/3593f.pdf
+ *
+ * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
+ *
+ * Based on leds-gpio.c,
+ *
+ * Copyright (C) 2007 8D Technologies inc.
+ * Raphael Assenat <raph@8d.com>
+ * Copyright (C) 2008 Freescale Semiconductor, Inc.
+ *
+ * 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 <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+
+struct lt3593_led_data {
+ struct led_classdev cdev;
+ unsigned gpio;
+ struct work_struct work;
+ u8 new_level;
+};
+
+static void lt3593_led_work(struct work_struct *work)
+{
+ int pulses;
+ struct lt3593_led_data *led_dat =
+ container_of(work, struct lt3593_led_data, work);
+
+ /*
+ * The LT3593 resets its internal current level register to the maximum
+ * level on the first falling edge on the control pin. Each following
+ * falling edge decreases the current level by 625uA. Up to 32 pulses
+ * can be sent, so the maximum power reduction is 20mA.
+ * After a timeout of 128us, the value is taken from the register and
+ * applied is to the output driver.
+ */
+
+ if (led_dat->new_level == 0) {
+ gpio_set_value_cansleep(led_dat->gpio, 0);
+ return;
+ }
+
+ pulses = 32 - (led_dat->new_level * 32) / 255;
+
+ if (pulses == 0) {
+ gpio_set_value_cansleep(led_dat->gpio, 0);
+ mdelay(1);
+ gpio_set_value_cansleep(led_dat->gpio, 1);
+ return;
+ }
+
+ gpio_set_value_cansleep(led_dat->gpio, 1);
+
+ while (pulses--) {
+ gpio_set_value_cansleep(led_dat->gpio, 0);
+ udelay(1);
+ gpio_set_value_cansleep(led_dat->gpio, 1);
+ udelay(1);
+ }
+}
+
+static void lt3593_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct lt3593_led_data *led_dat =
+ container_of(led_cdev, struct lt3593_led_data, cdev);
+
+ led_dat->new_level = value;
+ schedule_work(&led_dat->work);
+}
+
+static int __devinit create_lt3593_led(const struct gpio_led *template,
+ struct lt3593_led_data *led_dat, struct device *parent)
+{
+ int ret, state;
+
+ /* skip leds on GPIOs that aren't available */
+ if (!gpio_is_valid(template->gpio)) {
+ printk(KERN_INFO "%s: skipping unavailable LT3593 LED at gpio %d (%s)\n",
+ KBUILD_MODNAME, template->gpio, template->name);
+ return 0;
+ }
+
+ ret = gpio_request(template->gpio, template->name);
+ if (ret < 0)
+ return ret;
+
+ led_dat->cdev.name = template->name;
+ led_dat->cdev.default_trigger = template->default_trigger;
+ led_dat->gpio = template->gpio;
+
+ led_dat->cdev.brightness_set = lt3593_led_set;
+
+ state = (template->default_state == LEDS_GPIO_DEFSTATE_ON);
+ led_dat->cdev.brightness = state ? LED_FULL : LED_OFF;
+
+ if (!template->retain_state_suspended)
+ led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
+
+ ret = gpio_direction_output(led_dat->gpio, state);
+ if (ret < 0)
+ goto err;
+
+ INIT_WORK(&led_dat->work, lt3593_led_work);
+
+ ret = led_classdev_register(parent, &led_dat->cdev);
+ if (ret < 0)
+ goto err;
+
+ printk(KERN_INFO "%s: registered LT3593 LED '%s' at GPIO %d\n",
+ KBUILD_MODNAME, template->name, template->gpio);
+
+ return 0;
+
+err:
+ gpio_free(led_dat->gpio);
+ return ret;
+}
+
+static void delete_lt3593_led(struct lt3593_led_data *led)
+{
+ if (!gpio_is_valid(led->gpio))
+ return;
+
+ led_classdev_unregister(&led->cdev);
+ cancel_work_sync(&led->work);
+ gpio_free(led->gpio);
+}
+
+static int __devinit lt3593_led_probe(struct platform_device *pdev)
+{
+ struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
+ struct lt3593_led_data *leds_data;
+ int i, ret = 0;
+
+ if (!pdata)
+ return -EBUSY;
+
+ leds_data = kzalloc(sizeof(struct lt3593_led_data) * pdata->num_leds,
+ GFP_KERNEL);
+ if (!leds_data)
+ return -ENOMEM;
+
+ for (i = 0; i < pdata->num_leds; i++) {
+ ret = create_lt3593_led(&pdata->leds[i], &leds_data[i],
+ &pdev->dev);
+ if (ret < 0)
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, leds_data);
+
+ return 0;
+
+err:
+ for (i = i - 1; i >= 0; i--)
+ delete_lt3593_led(&leds_data[i]);
+
+ kfree(leds_data);
+
+ return ret;
+}
+
+static int __devexit lt3593_led_remove(struct platform_device *pdev)
+{
+ int i;
+ struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
+ struct lt3593_led_data *leds_data;
+
+ leds_data = platform_get_drvdata(pdev);
+
+ for (i = 0; i < pdata->num_leds; i++)
+ delete_lt3593_led(&leds_data[i]);
+
+ kfree(leds_data);
+
+ return 0;
+}
+
+static struct platform_driver lt3593_led_driver = {
+ .probe = lt3593_led_probe,
+ .remove = __devexit_p(lt3593_led_remove),
+ .driver = {
+ .name = "leds-lt3593",
+ .owner = THIS_MODULE,
+ },
+};
+
+MODULE_ALIAS("platform:leds-lt3593");
+
+static int __init lt3593_led_init(void)
+{
+ return platform_driver_register(&lt3593_led_driver);
+}
+
+static void __exit lt3593_led_exit(void)
+{
+ platform_driver_unregister(&lt3593_led_driver);
+}
+
+module_init(lt3593_led_init);
+module_exit(lt3593_led_exit);
+
+MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
+MODULE_DESCRIPTION("LED driver for LT3593 controllers");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-net48xx.c b/drivers/leds/leds-net48xx.c
new file mode 100644
index 00000000000..93987a12da4
--- /dev/null
+++ b/drivers/leds/leds-net48xx.c
@@ -0,0 +1,96 @@
+/*
+ * LEDs driver for Soekris net48xx
+ *
+ * Copyright (C) 2006 Chris Boot <bootc@bootc.net>
+ *
+ * Based on leds-ams-delta.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/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/err.h>
+#include <asm/io.h>
+#include <linux/nsc_gpio.h>
+#include <linux/scx200_gpio.h>
+
+#define DRVNAME "net48xx-led"
+#define NET48XX_ERROR_LED_GPIO 20
+
+static struct platform_device *pdev;
+
+static void net48xx_error_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ scx200_gpio_ops.gpio_set(NET48XX_ERROR_LED_GPIO, value ? 1 : 0);
+}
+
+static struct led_classdev net48xx_error_led = {
+ .name = "net48xx::error",
+ .brightness_set = net48xx_error_led_set,
+ .flags = LED_CORE_SUSPENDRESUME,
+};
+
+static int net48xx_led_probe(struct platform_device *pdev)
+{
+ return led_classdev_register(&pdev->dev, &net48xx_error_led);
+}
+
+static int net48xx_led_remove(struct platform_device *pdev)
+{
+ led_classdev_unregister(&net48xx_error_led);
+ return 0;
+}
+
+static struct platform_driver net48xx_led_driver = {
+ .probe = net48xx_led_probe,
+ .remove = net48xx_led_remove,
+ .driver = {
+ .name = DRVNAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init net48xx_led_init(void)
+{
+ int ret;
+
+ /* small hack, but scx200_gpio doesn't set .dev if the probe fails */
+ if (!scx200_gpio_ops.dev) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ ret = platform_driver_register(&net48xx_led_driver);
+ if (ret < 0)
+ goto out;
+
+ pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0);
+ if (IS_ERR(pdev)) {
+ ret = PTR_ERR(pdev);
+ platform_driver_unregister(&net48xx_led_driver);
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+static void __exit net48xx_led_exit(void)
+{
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&net48xx_led_driver);
+}
+
+module_init(net48xx_led_init);
+module_exit(net48xx_led_exit);
+
+MODULE_AUTHOR("Chris Boot <bootc@bootc.net>");
+MODULE_DESCRIPTION("Soekris net48xx LED driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c
new file mode 100644
index 00000000000..adc561eb59d
--- /dev/null
+++ b/drivers/leds/leds-pca9532.c
@@ -0,0 +1,373 @@
+/*
+ * pca9532.c - 16-bit Led dimmer
+ *
+ * Copyright (C) 2008 Riku Voipio
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * Datasheet: http://www.nxp.com/acrobat/datasheets/PCA9532_3.pdf
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/leds.h>
+#include <linux/input.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/leds-pca9532.h>
+
+#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;
+ struct work_struct work;
+ 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_calcpwm(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;
+ data->pwm[pwm] = b;
+ data->psc[pwm] = blink;
+ return 0;
+}
+
+static int pca9532_setpwm(struct i2c_client *client, int pwm)
+{
+ struct pca9532_data *data = i2c_get_clientdata(client);
+ mutex_lock(&data->update_lock);
+ i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(pwm),
+ data->pwm[pwm]);
+ 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_calcpwm(led->client, 0, 0, value);
+ if (err)
+ return; /* XXX: led api doesn't allow error code? */
+ }
+ schedule_work(&led->work);
+}
+
+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;
+ int err = 0;
+
+ 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;
+ err = pca9532_calcpwm(client, 0, psc, led_cdev->brightness);
+ if (err)
+ return err;
+ schedule_work(&led->work);
+ return 0;
+}
+
+static 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;
+
+ schedule_work(&data->work);
+
+ return 0;
+}
+
+static void pca9532_input_work(struct work_struct *work)
+{
+ struct pca9532_data *data;
+ data = container_of(work, struct pca9532_data, work);
+ mutex_lock(&data->update_lock);
+ i2c_smbus_write_byte_data(data->client, PCA9532_REG_PWM(1),
+ data->pwm[1]);
+ mutex_unlock(&data->update_lock);
+}
+
+static void pca9532_led_work(struct work_struct *work)
+{
+ struct pca9532_led *led;
+ led = container_of(work, struct pca9532_led, work);
+ if (led->state == PCA9532_PWM0)
+ pca9532_setpwm(led->client, 0);
+ pca9532_setled(led);
+}
+
+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;
+ INIT_WORK(&led->work, pca9532_led_work);
+ err = led_classdev_register(&client->dev, &led->ldev);
+ if (err < 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);
+ INIT_WORK(&data->work, pca9532_input_work);
+ err = input_register_device(data->idev);
+ if (err) {
+ input_free_device(data->idev);
+ cancel_work_sync(&data->work);
+ 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);
+ cancel_work_sync(&data->leds[i].work);
+ break;
+ case PCA9532_TYPE_N2100_BEEP:
+ if (data->idev != NULL) {
+ input_unregister_device(data->idev);
+ input_free_device(data->idev);
+ cancel_work_sync(&data->work);
+ 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;
+ int err;
+
+ if (!pca9532_pdata)
+ return -EIO;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ data = kzalloc(sizeof(*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);
+
+ err = pca9532_configure(client, data, pca9532_pdata);
+ if (err) {
+ kfree(data);
+ i2c_set_clientdata(client, NULL);
+ }
+
+ return err;
+}
+
+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);
+ cancel_work_sync(&data->leds[i].work);
+ break;
+ case PCA9532_TYPE_N2100_BEEP:
+ if (data->idev != NULL) {
+ input_unregister_device(data->idev);
+ input_free_device(data->idev);
+ cancel_work_sync(&data->work);
+ data->idev = NULL;
+ }
+ break;
+ }
+
+ kfree(data);
+ i2c_set_clientdata(client, NULL);
+ return 0;
+}
+
+static int __init pca9532_init(void)
+{
+ return i2c_add_driver(&pca9532_driver);
+}
+
+static void __exit pca9532_exit(void)
+{
+ i2c_del_driver(&pca9532_driver);
+}
+
+MODULE_AUTHOR("Riku Voipio");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("PCA 9532 LED dimmer");
+
+module_init(pca9532_init);
+module_exit(pca9532_exit);
+
diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c
new file mode 100644
index 00000000000..4e2d1a42b48
--- /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);
+}
+
+static 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-pwm.c b/drivers/leds/leds-pwm.c
new file mode 100644
index 00000000000..88b1dd091cf
--- /dev/null
+++ b/drivers/leds/leds-pwm.c
@@ -0,0 +1,152 @@
+/*
+ * linux/drivers/leds-pwm.c
+ *
+ * simple PWM based LED control
+ *
+ * Copyright 2009 Luotao Fu @ Pengutronix (l.fu@pengutronix.de)
+ *
+ * based on leds-gpio.c by Raphael Assenat <raph@8d.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/platform_device.h>
+#include <linux/fb.h>
+#include <linux/leds.h>
+#include <linux/err.h>
+#include <linux/pwm.h>
+#include <linux/leds_pwm.h>
+
+struct led_pwm_data {
+ struct led_classdev cdev;
+ struct pwm_device *pwm;
+ unsigned int active_low;
+ unsigned int period;
+};
+
+static void led_pwm_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct led_pwm_data *led_dat =
+ container_of(led_cdev, struct led_pwm_data, cdev);
+ unsigned int max = led_dat->cdev.max_brightness;
+ unsigned int period = led_dat->period;
+
+ if (brightness == 0) {
+ pwm_config(led_dat->pwm, 0, period);
+ pwm_disable(led_dat->pwm);
+ } else {
+ pwm_config(led_dat->pwm, brightness * period / max, period);
+ pwm_enable(led_dat->pwm);
+ }
+}
+
+static int led_pwm_probe(struct platform_device *pdev)
+{
+ struct led_pwm_platform_data *pdata = pdev->dev.platform_data;
+ struct led_pwm *cur_led;
+ struct led_pwm_data *leds_data, *led_dat;
+ int i, ret = 0;
+
+ if (!pdata)
+ return -EBUSY;
+
+ leds_data = kzalloc(sizeof(struct led_pwm_data) * pdata->num_leds,
+ GFP_KERNEL);
+ if (!leds_data)
+ return -ENOMEM;
+
+ for (i = 0; i < pdata->num_leds; i++) {
+ cur_led = &pdata->leds[i];
+ led_dat = &leds_data[i];
+
+ led_dat->pwm = pwm_request(cur_led->pwm_id,
+ cur_led->name);
+ if (IS_ERR(led_dat->pwm)) {
+ dev_err(&pdev->dev, "unable to request PWM %d\n",
+ cur_led->pwm_id);
+ goto err;
+ }
+
+ led_dat->cdev.name = cur_led->name;
+ led_dat->cdev.default_trigger = cur_led->default_trigger;
+ led_dat->active_low = cur_led->active_low;
+ led_dat->period = cur_led->pwm_period_ns;
+ led_dat->cdev.brightness_set = led_pwm_set;
+ led_dat->cdev.brightness = LED_OFF;
+ led_dat->cdev.max_brightness = cur_led->max_brightness;
+ led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
+
+ ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
+ if (ret < 0) {
+ pwm_free(led_dat->pwm);
+ goto err;
+ }
+ }
+
+ platform_set_drvdata(pdev, leds_data);
+
+ return 0;
+
+err:
+ if (i > 0) {
+ for (i = i - 1; i >= 0; i--) {
+ led_classdev_unregister(&leds_data[i].cdev);
+ pwm_free(leds_data[i].pwm);
+ }
+ }
+
+ kfree(leds_data);
+
+ return ret;
+}
+
+static int __devexit led_pwm_remove(struct platform_device *pdev)
+{
+ int i;
+ struct led_pwm_platform_data *pdata = pdev->dev.platform_data;
+ struct led_pwm_data *leds_data;
+
+ leds_data = platform_get_drvdata(pdev);
+
+ for (i = 0; i < pdata->num_leds; i++) {
+ led_classdev_unregister(&leds_data[i].cdev);
+ pwm_free(leds_data[i].pwm);
+ }
+
+ kfree(leds_data);
+
+ return 0;
+}
+
+static struct platform_driver led_pwm_driver = {
+ .probe = led_pwm_probe,
+ .remove = __devexit_p(led_pwm_remove),
+ .driver = {
+ .name = "leds_pwm",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init led_pwm_init(void)
+{
+ return platform_driver_register(&led_pwm_driver);
+}
+
+static void __exit led_pwm_exit(void)
+{
+ platform_driver_unregister(&led_pwm_driver);
+}
+
+module_init(led_pwm_init);
+module_exit(led_pwm_exit);
+
+MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>");
+MODULE_DESCRIPTION("PWM LED driver for PXA");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:leds-pwm");
diff --git a/drivers/leds/leds-rb532.c b/drivers/leds/leds-rb532.c
new file mode 100644
index 00000000000..c3525f37f73
--- /dev/null
+++ b/drivers/leds/leds-rb532.c
@@ -0,0 +1,77 @@
+/*
+ * LEDs driver for the "User LED" on Routerboard532
+ *
+ * Copyright (C) 2009 Phil Sutter <n0-1@freewrt.org>
+ *
+ * Based on leds-cobalt-qube.c by Florian Fainelly and
+ * rb-diag.c (my own standalone driver for both LED and
+ * button of Routerboard532).
+ */
+
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach-rc32434/gpio.h>
+#include <asm/mach-rc32434/rb.h>
+
+static void rb532_led_set(struct led_classdev *cdev,
+ enum led_brightness brightness)
+{
+ if (brightness)
+ set_latch_u5(LO_ULED, 0);
+
+ else
+ set_latch_u5(0, LO_ULED);
+}
+
+static enum led_brightness rb532_led_get(struct led_classdev *cdev)
+{
+ return (get_latch_u5() & LO_ULED) ? LED_FULL : LED_OFF;
+}
+
+static struct led_classdev rb532_uled = {
+ .name = "uled",
+ .brightness_set = rb532_led_set,
+ .brightness_get = rb532_led_get,
+ .default_trigger = "nand-disk",
+};
+
+static int __devinit rb532_led_probe(struct platform_device *pdev)
+{
+ return led_classdev_register(&pdev->dev, &rb532_uled);
+}
+
+static int __devexit rb532_led_remove(struct platform_device *pdev)
+{
+ led_classdev_unregister(&rb532_uled);
+ return 0;
+}
+
+static struct platform_driver rb532_led_driver = {
+ .probe = rb532_led_probe,
+ .remove = __devexit_p(rb532_led_remove),
+ .driver = {
+ .name = "rb532-led",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init rb532_led_init(void)
+{
+ return platform_driver_register(&rb532_led_driver);
+}
+
+static void __exit rb532_led_exit(void)
+{
+ platform_driver_unregister(&rb532_led_driver);
+}
+
+module_init(rb532_led_init);
+module_exit(rb532_led_exit);
+
+MODULE_ALIAS("platform:rb532-led");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("User LED support for Routerboard532");
+MODULE_AUTHOR("Phil Sutter <n0-1@freewrt.org>");
diff --git a/drivers/leds/leds-regulator.c b/drivers/leds/leds-regulator.c
new file mode 100644
index 00000000000..7f00de3ef92
--- /dev/null
+++ b/drivers/leds/leds-regulator.c
@@ -0,0 +1,242 @@
+/*
+ * leds-regulator.c - LED class driver for regulator driven LEDs.
+ *
+ * Copyright (C) 2009 Antonio Ospite <ospite@studenti.unina.it>
+ *
+ * Inspired by leds-wm8350 driver.
+ *
+ * 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/err.h>
+#include <linux/workqueue.h>
+#include <linux/leds.h>
+#include <linux/leds-regulator.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#define to_regulator_led(led_cdev) \
+ container_of(led_cdev, struct regulator_led, cdev)
+
+struct regulator_led {
+ struct led_classdev cdev;
+ enum led_brightness value;
+ int enabled;
+ struct mutex mutex;
+ struct work_struct work;
+
+ struct regulator *vcc;
+};
+
+static inline int led_regulator_get_max_brightness(struct regulator *supply)
+{
+ int ret;
+ int voltage = regulator_list_voltage(supply, 0);
+
+ if (voltage <= 0)
+ return 1;
+
+ /* even if regulator can't change voltages,
+ * we still assume it can change status
+ * and the LED can be turned on and off.
+ */
+ ret = regulator_set_voltage(supply, voltage, voltage);
+ if (ret < 0)
+ return 1;
+
+ return regulator_count_voltages(supply);
+}
+
+static int led_regulator_get_voltage(struct regulator *supply,
+ enum led_brightness brightness)
+{
+ if (brightness == 0)
+ return -EINVAL;
+
+ return regulator_list_voltage(supply, brightness - 1);
+}
+
+
+static void regulator_led_enable(struct regulator_led *led)
+{
+ int ret;
+
+ if (led->enabled)
+ return;
+
+ ret = regulator_enable(led->vcc);
+ if (ret != 0) {
+ dev_err(led->cdev.dev, "Failed to enable vcc: %d\n", ret);
+ return;
+ }
+
+ led->enabled = 1;
+}
+
+static void regulator_led_disable(struct regulator_led *led)
+{
+ int ret;
+
+ if (!led->enabled)
+ return;
+
+ ret = regulator_disable(led->vcc);
+ if (ret != 0) {
+ dev_err(led->cdev.dev, "Failed to disable vcc: %d\n", ret);
+ return;
+ }
+
+ led->enabled = 0;
+}
+
+static void regulator_led_set_value(struct regulator_led *led)
+{
+ int voltage;
+ int ret;
+
+ mutex_lock(&led->mutex);
+
+ if (led->value == LED_OFF) {
+ regulator_led_disable(led);
+ goto out;
+ }
+
+ if (led->cdev.max_brightness > 1) {
+ voltage = led_regulator_get_voltage(led->vcc, led->value);
+ dev_dbg(led->cdev.dev, "brightness: %d voltage: %d\n",
+ led->value, voltage);
+
+ ret = regulator_set_voltage(led->vcc, voltage, voltage);
+ if (ret != 0)
+ dev_err(led->cdev.dev, "Failed to set voltage %d: %d\n",
+ voltage, ret);
+ }
+
+ regulator_led_enable(led);
+
+out:
+ mutex_unlock(&led->mutex);
+}
+
+static void led_work(struct work_struct *work)
+{
+ struct regulator_led *led;
+
+ led = container_of(work, struct regulator_led, work);
+ regulator_led_set_value(led);
+}
+
+static void regulator_led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct regulator_led *led = to_regulator_led(led_cdev);
+
+ led->value = value;
+ schedule_work(&led->work);
+}
+
+static int __devinit regulator_led_probe(struct platform_device *pdev)
+{
+ struct led_regulator_platform_data *pdata = pdev->dev.platform_data;
+ struct regulator_led *led;
+ struct regulator *vcc;
+ int ret = 0;
+
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "no platform data\n");
+ return -ENODEV;
+ }
+
+ vcc = regulator_get_exclusive(&pdev->dev, "vled");
+ if (IS_ERR(vcc)) {
+ dev_err(&pdev->dev, "Cannot get vcc for %s\n", pdata->name);
+ return PTR_ERR(vcc);
+ }
+
+ led = kzalloc(sizeof(*led), GFP_KERNEL);
+ if (led == NULL) {
+ ret = -ENOMEM;
+ goto err_vcc;
+ }
+
+ led->cdev.max_brightness = led_regulator_get_max_brightness(vcc);
+ if (pdata->brightness > led->cdev.max_brightness) {
+ dev_err(&pdev->dev, "Invalid default brightness %d\n",
+ pdata->brightness);
+ ret = -EINVAL;
+ goto err_led;
+ }
+ led->value = pdata->brightness;
+
+ led->cdev.brightness_set = regulator_led_brightness_set;
+ led->cdev.name = pdata->name;
+ led->cdev.flags |= LED_CORE_SUSPENDRESUME;
+ led->vcc = vcc;
+
+ mutex_init(&led->mutex);
+ INIT_WORK(&led->work, led_work);
+
+ platform_set_drvdata(pdev, led);
+
+ ret = led_classdev_register(&pdev->dev, &led->cdev);
+ if (ret < 0) {
+ cancel_work_sync(&led->work);
+ goto err_led;
+ }
+
+ /* to expose the default value to userspace */
+ led->cdev.brightness = led->value;
+
+ /* Set the default led status */
+ regulator_led_set_value(led);
+
+ return 0;
+
+err_led:
+ kfree(led);
+err_vcc:
+ regulator_put(vcc);
+ return ret;
+}
+
+static int __devexit regulator_led_remove(struct platform_device *pdev)
+{
+ struct regulator_led *led = platform_get_drvdata(pdev);
+
+ led_classdev_unregister(&led->cdev);
+ cancel_work_sync(&led->work);
+ regulator_led_disable(led);
+ regulator_put(led->vcc);
+ kfree(led);
+ return 0;
+}
+
+static struct platform_driver regulator_led_driver = {
+ .driver = {
+ .name = "leds-regulator",
+ .owner = THIS_MODULE,
+ },
+ .probe = regulator_led_probe,
+ .remove = __devexit_p(regulator_led_remove),
+};
+
+static int __init regulator_led_init(void)
+{
+ return platform_driver_register(&regulator_led_driver);
+}
+module_init(regulator_led_init);
+
+static void __exit regulator_led_exit(void)
+{
+ platform_driver_unregister(&regulator_led_driver);
+}
+module_exit(regulator_led_exit);
+
+MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>");
+MODULE_DESCRIPTION("Regulator driven LED driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:leds-regulator");
diff --git a/drivers/leds/leds-s3c24xx.c b/drivers/leds/leds-s3c24xx.c
new file mode 100644
index 00000000000..aa7acf3b922
--- /dev/null
+++ b/drivers/leds/leds-s3c24xx.c
@@ -0,0 +1,138 @@
+/* drivers/leds/leds-s3c24xx.c
+ *
+ * (c) 2006 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C24XX - LEDs GPIO driver
+ *
+ * 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 <linux/gpio.h>
+
+#include <mach/hardware.h>
+#include <mach/regs-gpio.h>
+#include <mach/leds-gpio.h>
+
+/* our context */
+
+struct s3c24xx_gpio_led {
+ struct led_classdev cdev;
+ struct s3c24xx_led_platdata *pdata;
+};
+
+static inline struct s3c24xx_gpio_led *pdev_to_gpio(struct platform_device *dev)
+{
+ return platform_get_drvdata(dev);
+}
+
+static inline struct s3c24xx_gpio_led *to_gpio(struct led_classdev *led_cdev)
+{
+ return container_of(led_cdev, struct s3c24xx_gpio_led, cdev);
+}
+
+static void s3c24xx_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct s3c24xx_gpio_led *led = to_gpio(led_cdev);
+ struct s3c24xx_led_platdata *pd = led->pdata;
+
+ /* there will be a short delay between setting the output and
+ * going from output to input when using tristate. */
+
+ s3c2410_gpio_setpin(pd->gpio, (value ? 1 : 0) ^
+ (pd->flags & S3C24XX_LEDF_ACTLOW));
+
+ if (pd->flags & S3C24XX_LEDF_TRISTATE)
+ s3c2410_gpio_cfgpin(pd->gpio,
+ value ? S3C2410_GPIO_OUTPUT : S3C2410_GPIO_INPUT);
+
+}
+
+static int s3c24xx_led_remove(struct platform_device *dev)
+{
+ struct s3c24xx_gpio_led *led = pdev_to_gpio(dev);
+
+ led_classdev_unregister(&led->cdev);
+ kfree(led);
+
+ return 0;
+}
+
+static int s3c24xx_led_probe(struct platform_device *dev)
+{
+ struct s3c24xx_led_platdata *pdata = dev->dev.platform_data;
+ struct s3c24xx_gpio_led *led;
+ int ret;
+
+ led = kzalloc(sizeof(struct s3c24xx_gpio_led), GFP_KERNEL);
+ if (led == NULL) {
+ dev_err(&dev->dev, "No memory for device\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(dev, led);
+
+ led->cdev.brightness_set = s3c24xx_led_set;
+ led->cdev.default_trigger = pdata->def_trigger;
+ led->cdev.name = pdata->name;
+ led->cdev.flags |= LED_CORE_SUSPENDRESUME;
+
+ led->pdata = pdata;
+
+ /* no point in having a pull-up if we are always driving */
+
+ if (pdata->flags & S3C24XX_LEDF_TRISTATE) {
+ s3c2410_gpio_setpin(pdata->gpio, 0);
+ s3c2410_gpio_cfgpin(pdata->gpio, S3C2410_GPIO_INPUT);
+ } else {
+ s3c2410_gpio_pullup(pdata->gpio, 0);
+ s3c2410_gpio_setpin(pdata->gpio, 0);
+ s3c2410_gpio_cfgpin(pdata->gpio, S3C2410_GPIO_OUTPUT);
+ }
+
+ /* register our new led device */
+
+ ret = led_classdev_register(&dev->dev, &led->cdev);
+ if (ret < 0) {
+ dev_err(&dev->dev, "led_classdev_register failed\n");
+ kfree(led);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct platform_driver s3c24xx_led_driver = {
+ .probe = s3c24xx_led_probe,
+ .remove = s3c24xx_led_remove,
+ .driver = {
+ .name = "s3c24xx_led",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init s3c24xx_led_init(void)
+{
+ return platform_driver_register(&s3c24xx_led_driver);
+}
+
+static void __exit s3c24xx_led_exit(void)
+{
+ platform_driver_unregister(&s3c24xx_led_driver);
+}
+
+module_init(s3c24xx_led_init);
+module_exit(s3c24xx_led_exit);
+
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_DESCRIPTION("S3C24XX LED driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:s3c24xx_led");
diff --git a/drivers/leds/leds-ss4200.c b/drivers/leds/leds-ss4200.c
new file mode 100644
index 00000000000..97f04984c1c
--- /dev/null
+++ b/drivers/leds/leds-ss4200.c
@@ -0,0 +1,556 @@
+/*
+ * SS4200-E Hardware API
+ * Copyright (c) 2009, Intel Corporation.
+ * Copyright IBM Corporation, 2009
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * 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 St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Author: Dave Hansen <dave@sr71.net>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/dmi.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+
+MODULE_AUTHOR("Rodney Girod <rgirod@confocus.com>, Dave Hansen <dave@sr71.net>");
+MODULE_DESCRIPTION("Intel NAS/Home Server ICH7 GPIO Driver");
+MODULE_LICENSE("GPL");
+
+/*
+ * ICH7 LPC/GPIO PCI Config register offsets
+ */
+#define PMBASE 0x040
+#define GPIO_BASE 0x048
+#define GPIO_CTRL 0x04c
+#define GPIO_EN 0x010
+
+/*
+ * The ICH7 GPIO register block is 64 bytes in size.
+ */
+#define ICH7_GPIO_SIZE 64
+
+/*
+ * Define register offsets within the ICH7 register block.
+ */
+#define GPIO_USE_SEL 0x000
+#define GP_IO_SEL 0x004
+#define GP_LVL 0x00c
+#define GPO_BLINK 0x018
+#define GPI_INV 0x030
+#define GPIO_USE_SEL2 0x034
+#define GP_IO_SEL2 0x038
+#define GP_LVL2 0x03c
+
+/*
+ * PCI ID of the Intel ICH7 LPC Device within which the GPIO block lives.
+ */
+static struct pci_device_id ich7_lpc_pci_id[] =
+{
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_0) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_30) },
+ { } /* NULL entry */
+};
+
+MODULE_DEVICE_TABLE(pci, ich7_lpc_pci_id);
+
+static int __init ss4200_led_dmi_callback(const struct dmi_system_id *id)
+{
+ pr_info("detected '%s'\n", id->ident);
+ return 1;
+}
+
+static unsigned int __initdata nodetect;
+module_param_named(nodetect, nodetect, bool, 0);
+MODULE_PARM_DESC(nodetect, "Skip DMI-based hardware detection");
+
+/*
+ * struct nas_led_whitelist - List of known good models
+ *
+ * Contains the known good models this driver is compatible with.
+ * When adding a new model try to be as strict as possible. This
+ * makes it possible to keep the false positives (the model is
+ * detected as working, but in reality it is not) as low as
+ * possible.
+ */
+static struct dmi_system_id __initdata nas_led_whitelist[] = {
+ {
+ .callback = ss4200_led_dmi_callback,
+ .ident = "Intel SS4200-E",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "SS4200-E"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "1.00.00")
+ }
+ },
+};
+
+/*
+ * Base I/O address assigned to the Power Management register block
+ */
+static u32 g_pm_io_base;
+
+/*
+ * Base I/O address assigned to the ICH7 GPIO register block
+ */
+static u32 nas_gpio_io_base;
+
+/*
+ * When we successfully register a region, we are returned a resource.
+ * We use these to identify which regions we need to release on our way
+ * back out.
+ */
+static struct resource *gp_gpio_resource;
+
+struct nasgpio_led {
+ char *name;
+ u32 gpio_bit;
+ struct led_classdev led_cdev;
+};
+
+/*
+ * gpio_bit(s) are the ICH7 GPIO bit assignments
+ */
+static struct nasgpio_led nasgpio_leds[] = {
+ { .name = "hdd1:blue:sata", .gpio_bit = 0 },
+ { .name = "hdd1:amber:sata", .gpio_bit = 1 },
+ { .name = "hdd2:blue:sata", .gpio_bit = 2 },
+ { .name = "hdd2:amber:sata", .gpio_bit = 3 },
+ { .name = "hdd3:blue:sata", .gpio_bit = 4 },
+ { .name = "hdd3:amber:sata", .gpio_bit = 5 },
+ { .name = "hdd4:blue:sata", .gpio_bit = 6 },
+ { .name = "hdd4:amber:sata", .gpio_bit = 7 },
+ { .name = "power:blue:power", .gpio_bit = 27},
+ { .name = "power:amber:power", .gpio_bit = 28},
+};
+
+#define NAS_RECOVERY 0x00000400 /* GPIO10 */
+
+static struct nasgpio_led *
+led_classdev_to_nasgpio_led(struct led_classdev *led_cdev)
+{
+ return container_of(led_cdev, struct nasgpio_led, led_cdev);
+}
+
+static struct nasgpio_led *get_led_named(char *name)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(nasgpio_leds); i++) {
+ if (strcmp(nasgpio_leds[i].name, name))
+ continue;
+ return &nasgpio_leds[i];
+ }
+ return NULL;
+}
+
+/*
+ * This protects access to the gpio ports.
+ */
+static DEFINE_SPINLOCK(nasgpio_gpio_lock);
+
+/*
+ * There are two gpio ports, one for blinking and the other
+ * for power. @port tells us if we're doing blinking or
+ * power control.
+ *
+ * Caller must hold nasgpio_gpio_lock
+ */
+static void __nasgpio_led_set_attr(struct led_classdev *led_cdev,
+ u32 port, u32 value)
+{
+ struct nasgpio_led *led = led_classdev_to_nasgpio_led(led_cdev);
+ u32 gpio_out;
+
+ gpio_out = inl(nas_gpio_io_base + port);
+ if (value)
+ gpio_out |= (1<<led->gpio_bit);
+ else
+ gpio_out &= ~(1<<led->gpio_bit);
+
+ outl(gpio_out, nas_gpio_io_base + port);
+}
+
+static void nasgpio_led_set_attr(struct led_classdev *led_cdev,
+ u32 port, u32 value)
+{
+ spin_lock(&nasgpio_gpio_lock);
+ __nasgpio_led_set_attr(led_cdev, port, value);
+ spin_unlock(&nasgpio_gpio_lock);
+}
+
+u32 nasgpio_led_get_attr(struct led_classdev *led_cdev, u32 port)
+{
+ struct nasgpio_led *led = led_classdev_to_nasgpio_led(led_cdev);
+ u32 gpio_in;
+
+ spin_lock(&nasgpio_gpio_lock);
+ gpio_in = inl(nas_gpio_io_base + port);
+ spin_unlock(&nasgpio_gpio_lock);
+ if (gpio_in & (1<<led->gpio_bit))
+ return 1;
+ return 0;
+}
+
+/*
+ * There is actual brightness control in the hardware,
+ * but it is via smbus commands and not implemented
+ * in this driver.
+ */
+static void nasgpio_led_set_brightness(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ u32 setting = 0;
+ if (brightness >= LED_HALF)
+ setting = 1;
+ /*
+ * Hold the lock across both operations. This ensures
+ * consistency so that both the "turn off blinking"
+ * and "turn light off" operations complete as a set.
+ */
+ spin_lock(&nasgpio_gpio_lock);
+ /*
+ * LED class documentation asks that past blink state
+ * be disabled when brightness is turned to zero.
+ */
+ if (brightness == 0)
+ __nasgpio_led_set_attr(led_cdev, GPO_BLINK, 0);
+ __nasgpio_led_set_attr(led_cdev, GP_LVL, setting);
+ spin_unlock(&nasgpio_gpio_lock);
+}
+
+static int nasgpio_led_set_blink(struct led_classdev *led_cdev,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ u32 setting = 1;
+ if (!(*delay_on == 0 && *delay_off == 0) &&
+ !(*delay_on == 500 && *delay_off == 500))
+ return -EINVAL;
+ /*
+ * These are very approximate.
+ */
+ *delay_on = 500;
+ *delay_off = 500;
+
+ nasgpio_led_set_attr(led_cdev, GPO_BLINK, setting);
+
+ return 0;
+}
+
+
+/*
+ * Initialize the ICH7 GPIO registers for NAS usage. The BIOS should have
+ * already taken care of this, but we will do so in a non destructive manner
+ * so that we have what we need whether the BIOS did it or not.
+ */
+static int __devinit ich7_gpio_init(struct device *dev)
+{
+ int i;
+ u32 config_data = 0;
+ u32 all_nas_led = 0;
+
+ for (i = 0; i < ARRAY_SIZE(nasgpio_leds); i++)
+ all_nas_led |= (1<<nasgpio_leds[i].gpio_bit);
+
+ spin_lock(&nasgpio_gpio_lock);
+ /*
+ * We need to enable all of the GPIO lines used by the NAS box,
+ * so we will read the current Use Selection and add our usage
+ * to it. This should be benign with regard to the original
+ * BIOS configuration.
+ */
+ config_data = inl(nas_gpio_io_base + GPIO_USE_SEL);
+ dev_dbg(dev, ": Data read from GPIO_USE_SEL = 0x%08x\n", config_data);
+ config_data |= all_nas_led + NAS_RECOVERY;
+ outl(config_data, nas_gpio_io_base + GPIO_USE_SEL);
+ config_data = inl(nas_gpio_io_base + GPIO_USE_SEL);
+ dev_dbg(dev, ": GPIO_USE_SEL = 0x%08x\n\n", config_data);
+
+ /*
+ * The LED GPIO outputs need to be configured for output, so we
+ * will ensure that all LED lines are cleared for output and the
+ * RECOVERY line ready for input. This too should be benign with
+ * regard to BIOS configuration.
+ */
+ config_data = inl(nas_gpio_io_base + GP_IO_SEL);
+ dev_dbg(dev, ": Data read from GP_IO_SEL = 0x%08x\n",
+ config_data);
+ config_data &= ~all_nas_led;
+ config_data |= NAS_RECOVERY;
+ outl(config_data, nas_gpio_io_base + GP_IO_SEL);
+ config_data = inl(nas_gpio_io_base + GP_IO_SEL);
+ dev_dbg(dev, ": GP_IO_SEL = 0x%08x\n", config_data);
+
+ /*
+ * In our final system, the BIOS will initialize the state of all
+ * of the LEDs. For now, we turn them all off (or Low).
+ */
+ config_data = inl(nas_gpio_io_base + GP_LVL);
+ dev_dbg(dev, ": Data read from GP_LVL = 0x%08x\n", config_data);
+ /*
+ * In our final system, the BIOS will initialize the blink state of all
+ * of the LEDs. For now, we turn blink off for all of them.
+ */
+ config_data = inl(nas_gpio_io_base + GPO_BLINK);
+ dev_dbg(dev, ": Data read from GPO_BLINK = 0x%08x\n", config_data);
+
+ /*
+ * At this moment, I am unsure if anything needs to happen with GPI_INV
+ */
+ config_data = inl(nas_gpio_io_base + GPI_INV);
+ dev_dbg(dev, ": Data read from GPI_INV = 0x%08x\n", config_data);
+
+ spin_unlock(&nasgpio_gpio_lock);
+ return 0;
+}
+
+static void ich7_lpc_cleanup(struct device *dev)
+{
+ /*
+ * If we were given exclusive use of the GPIO
+ * I/O Address range, we must return it.
+ */
+ if (gp_gpio_resource) {
+ dev_dbg(dev, ": Releasing GPIO I/O addresses\n");
+ release_region(nas_gpio_io_base, ICH7_GPIO_SIZE);
+ gp_gpio_resource = NULL;
+ }
+}
+
+/*
+ * The OS has determined that the LPC of the Intel ICH7 Southbridge is present
+ * so we can retrive the required operational information and prepare the GPIO.
+ */
+static struct pci_dev *nas_gpio_pci_dev;
+static int __devinit ich7_lpc_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ int status;
+ u32 gc = 0;
+
+ status = pci_enable_device(dev);
+ if (status) {
+ dev_err(&dev->dev, "pci_enable_device failed\n");
+ return -EIO;
+ }
+
+ nas_gpio_pci_dev = dev;
+ status = pci_read_config_dword(dev, PMBASE, &g_pm_io_base);
+ if (status)
+ goto out;
+ g_pm_io_base &= 0x00000ff80;
+
+ status = pci_read_config_dword(dev, GPIO_CTRL, &gc);
+ if (!(GPIO_EN & gc)) {
+ status = -EEXIST;
+ dev_info(&dev->dev,
+ "ERROR: The LPC GPIO Block has not been enabled.\n");
+ goto out;
+ }
+
+ status = pci_read_config_dword(dev, GPIO_BASE, &nas_gpio_io_base);
+ if (0 > status) {
+ dev_info(&dev->dev, "Unable to read GPIOBASE.\n");
+ goto out;
+ }
+ dev_dbg(&dev->dev, ": GPIOBASE = 0x%08x\n", nas_gpio_io_base);
+ nas_gpio_io_base &= 0x00000ffc0;
+
+ /*
+ * Insure that we have exclusive access to the GPIO I/O address range.
+ */
+ gp_gpio_resource = request_region(nas_gpio_io_base, ICH7_GPIO_SIZE,
+ KBUILD_MODNAME);
+ if (NULL == gp_gpio_resource) {
+ dev_info(&dev->dev,
+ "ERROR Unable to register GPIO I/O addresses.\n");
+ status = -1;
+ goto out;
+ }
+
+ /*
+ * Initialize the GPIO for NAS/Home Server Use
+ */
+ ich7_gpio_init(&dev->dev);
+
+out:
+ if (status) {
+ ich7_lpc_cleanup(&dev->dev);
+ pci_disable_device(dev);
+ }
+ return status;
+}
+
+static void ich7_lpc_remove(struct pci_dev *dev)
+{
+ ich7_lpc_cleanup(&dev->dev);
+ pci_disable_device(dev);
+}
+
+/*
+ * pci_driver structure passed to the PCI modules
+ */
+static struct pci_driver nas_gpio_pci_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = ich7_lpc_pci_id,
+ .probe = ich7_lpc_probe,
+ .remove = ich7_lpc_remove,
+};
+
+static struct led_classdev *get_classdev_for_led_nr(int nr)
+{
+ struct nasgpio_led *nas_led = &nasgpio_leds[nr];
+ struct led_classdev *led = &nas_led->led_cdev;
+ return led;
+}
+
+
+static void set_power_light_amber_noblink(void)
+{
+ struct nasgpio_led *amber = get_led_named("power:amber:power");
+ struct nasgpio_led *blue = get_led_named("power:blue:power");
+
+ if (!amber || !blue)
+ return;
+ /*
+ * LED_OFF implies disabling future blinking
+ */
+ pr_debug("setting blue off and amber on\n");
+
+ nasgpio_led_set_brightness(&blue->led_cdev, LED_OFF);
+ nasgpio_led_set_brightness(&amber->led_cdev, LED_FULL);
+}
+
+static ssize_t nas_led_blink_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led = dev_get_drvdata(dev);
+ int blinking = 0;
+ if (nasgpio_led_get_attr(led, GPO_BLINK))
+ blinking = 1;
+ return sprintf(buf, "%u\n", blinking);
+}
+
+static ssize_t nas_led_blink_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ int ret;
+ struct led_classdev *led = dev_get_drvdata(dev);
+ unsigned long blink_state;
+
+ ret = strict_strtoul(buf, 10, &blink_state);
+ if (ret)
+ return ret;
+
+ nasgpio_led_set_attr(led, GPO_BLINK, blink_state);
+
+ return size;
+}
+
+static DEVICE_ATTR(blink, 0644, nas_led_blink_show, nas_led_blink_store);
+
+static int register_nasgpio_led(int led_nr)
+{
+ int ret;
+ struct nasgpio_led *nas_led = &nasgpio_leds[led_nr];
+ struct led_classdev *led = get_classdev_for_led_nr(led_nr);
+
+ led->name = nas_led->name;
+ led->brightness = LED_OFF;
+ if (nasgpio_led_get_attr(led, GP_LVL))
+ led->brightness = LED_FULL;
+ led->brightness_set = nasgpio_led_set_brightness;
+ led->blink_set = nasgpio_led_set_blink;
+ ret = led_classdev_register(&nas_gpio_pci_dev->dev, led);
+ if (ret)
+ return ret;
+ ret = device_create_file(led->dev, &dev_attr_blink);
+ if (ret)
+ led_classdev_unregister(led);
+ return ret;
+}
+
+static void unregister_nasgpio_led(int led_nr)
+{
+ struct led_classdev *led = get_classdev_for_led_nr(led_nr);
+ led_classdev_unregister(led);
+ device_remove_file(led->dev, &dev_attr_blink);
+}
+/*
+ * module load/initialization
+ */
+static int __init nas_gpio_init(void)
+{
+ int i;
+ int ret = 0;
+ int nr_devices = 0;
+
+ nr_devices = dmi_check_system(nas_led_whitelist);
+ if (nodetect) {
+ pr_info("skipping hardware autodetection\n");
+ pr_info("Please send 'dmidecode' output to dave@sr71.net\n");
+ nr_devices++;
+ }
+
+ if (nr_devices <= 0) {
+ pr_info("no LED devices found\n");
+ return -ENODEV;
+ }
+
+ pr_info("registering PCI driver\n");
+ ret = pci_register_driver(&nas_gpio_pci_driver);
+ if (ret)
+ return ret;
+ for (i = 0; i < ARRAY_SIZE(nasgpio_leds); i++) {
+ ret = register_nasgpio_led(i);
+ if (ret)
+ goto out_err;
+ }
+ /*
+ * When the system powers on, the BIOS leaves the power
+ * light blue and blinking. This will turn it solid
+ * amber once the driver is loaded.
+ */
+ set_power_light_amber_noblink();
+ return 0;
+out_err:
+ for (; i >= 0; i--)
+ unregister_nasgpio_led(i);
+ pci_unregister_driver(&nas_gpio_pci_driver);
+ return ret;
+}
+
+/*
+ * module unload
+ */
+static void __exit nas_gpio_exit(void)
+{
+ int i;
+ pr_info("Unregistering driver\n");
+ for (i = 0; i < ARRAY_SIZE(nasgpio_leds); i++)
+ unregister_nasgpio_led(i);
+ pci_unregister_driver(&nas_gpio_pci_driver);
+}
+
+module_init(nas_gpio_init);
+module_exit(nas_gpio_exit);
diff --git a/drivers/leds/leds-sunfire.c b/drivers/leds/leds-sunfire.c
new file mode 100644
index 00000000000..6b008f0c3f6
--- /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);
diff --git a/drivers/leds/leds-wm831x-status.c b/drivers/leds/leds-wm831x-status.c
new file mode 100644
index 00000000000..c586d05e336
--- /dev/null
+++ b/drivers/leds/leds-wm831x-status.c
@@ -0,0 +1,341 @@
+/*
+ * LED driver for WM831x status LEDs
+ *
+ * Copyright(C) 2009 Wolfson Microelectronics PLC.
+ *
+ * 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 <linux/err.h>
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/pdata.h>
+#include <linux/mfd/wm831x/status.h>
+
+
+struct wm831x_status {
+ struct led_classdev cdev;
+ struct wm831x *wm831x;
+ struct work_struct work;
+ struct mutex mutex;
+
+ spinlock_t value_lock;
+ int reg; /* Control register */
+ int reg_val; /* Control register value */
+
+ int blink;
+ int blink_time;
+ int blink_cyc;
+ int src;
+ enum led_brightness brightness;
+};
+
+#define to_wm831x_status(led_cdev) \
+ container_of(led_cdev, struct wm831x_status, cdev)
+
+static void wm831x_status_work(struct work_struct *work)
+{
+ struct wm831x_status *led = container_of(work, struct wm831x_status,
+ work);
+ unsigned long flags;
+
+ mutex_lock(&led->mutex);
+
+ led->reg_val &= ~(WM831X_LED_SRC_MASK | WM831X_LED_MODE_MASK |
+ WM831X_LED_DUTY_CYC_MASK | WM831X_LED_DUR_MASK);
+
+ spin_lock_irqsave(&led->value_lock, flags);
+
+ led->reg_val |= led->src << WM831X_LED_SRC_SHIFT;
+ if (led->blink) {
+ led->reg_val |= 2 << WM831X_LED_MODE_SHIFT;
+ led->reg_val |= led->blink_time << WM831X_LED_DUR_SHIFT;
+ led->reg_val |= led->blink_cyc;
+ } else {
+ if (led->brightness != LED_OFF)
+ led->reg_val |= 1 << WM831X_LED_MODE_SHIFT;
+ }
+
+ spin_unlock_irqrestore(&led->value_lock, flags);
+
+ wm831x_reg_write(led->wm831x, led->reg, led->reg_val);
+
+ mutex_unlock(&led->mutex);
+}
+
+static void wm831x_status_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct wm831x_status *led = to_wm831x_status(led_cdev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&led->value_lock, flags);
+ led->brightness = value;
+ if (value == LED_OFF)
+ led->blink = 0;
+ schedule_work(&led->work);
+ spin_unlock_irqrestore(&led->value_lock, flags);
+}
+
+static int wm831x_status_blink_set(struct led_classdev *led_cdev,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ struct wm831x_status *led = to_wm831x_status(led_cdev);
+ unsigned long flags;
+ int ret = 0;
+
+ /* Pick some defaults if we've not been given times */
+ if (*delay_on == 0 && *delay_off == 0) {
+ *delay_on = 250;
+ *delay_off = 250;
+ }
+
+ spin_lock_irqsave(&led->value_lock, flags);
+
+ /* We only have a limited selection of settings, see if we can
+ * support the configuration we're being given */
+ switch (*delay_on) {
+ case 1000:
+ led->blink_time = 0;
+ break;
+ case 250:
+ led->blink_time = 1;
+ break;
+ case 125:
+ led->blink_time = 2;
+ break;
+ case 62:
+ case 63:
+ /* Actually 62.5ms */
+ led->blink_time = 3;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret == 0) {
+ switch (*delay_off / *delay_on) {
+ case 1:
+ led->blink_cyc = 0;
+ break;
+ case 3:
+ led->blink_cyc = 1;
+ break;
+ case 4:
+ led->blink_cyc = 2;
+ break;
+ case 8:
+ led->blink_cyc = 3;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ }
+
+ if (ret == 0)
+ led->blink = 1;
+ else
+ led->blink = 0;
+
+ /* Always update; if we fail turn off blinking since we expect
+ * a software fallback. */
+ schedule_work(&led->work);
+
+ spin_unlock_irqrestore(&led->value_lock, flags);
+
+ return ret;
+}
+
+static const char *led_src_texts[] = {
+ "otp",
+ "power",
+ "charger",
+ "soft",
+};
+
+static ssize_t wm831x_status_src_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct wm831x_status *led = to_wm831x_status(led_cdev);
+ int i;
+ ssize_t ret = 0;
+
+ mutex_lock(&led->mutex);
+
+ for (i = 0; i < ARRAY_SIZE(led_src_texts); i++)
+ if (i == led->src)
+ ret += sprintf(&buf[ret], "[%s] ", led_src_texts[i]);
+ else
+ ret += sprintf(&buf[ret], "%s ", led_src_texts[i]);
+
+ mutex_unlock(&led->mutex);
+
+ ret += sprintf(&buf[ret], "\n");
+
+ return ret;
+}
+
+static ssize_t wm831x_status_src_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct wm831x_status *led = to_wm831x_status(led_cdev);
+ char name[20];
+ int i;
+ size_t len;
+
+ name[sizeof(name) - 1] = '\0';
+ strncpy(name, buf, sizeof(name) - 1);
+ len = strlen(name);
+
+ if (len && name[len - 1] == '\n')
+ name[len - 1] = '\0';
+
+ for (i = 0; i < ARRAY_SIZE(led_src_texts); i++) {
+ if (!strcmp(name, led_src_texts[i])) {
+ mutex_lock(&led->mutex);
+
+ led->src = i;
+ schedule_work(&led->work);
+
+ mutex_unlock(&led->mutex);
+ }
+ }
+
+ return size;
+}
+
+static DEVICE_ATTR(src, 0644, wm831x_status_src_show, wm831x_status_src_store);
+
+static int wm831x_status_probe(struct platform_device *pdev)
+{
+ struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+ struct wm831x_pdata *chip_pdata;
+ struct wm831x_status_pdata pdata;
+ struct wm831x_status *drvdata;
+ struct resource *res;
+ int id = pdev->id % ARRAY_SIZE(chip_pdata->status);
+ int ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "No I/O resource\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ drvdata = kzalloc(sizeof(struct wm831x_status), GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+ dev_set_drvdata(&pdev->dev, drvdata);
+
+ drvdata->wm831x = wm831x;
+ drvdata->reg = res->start;
+
+ if (wm831x->dev->platform_data)
+ chip_pdata = wm831x->dev->platform_data;
+ else
+ chip_pdata = NULL;
+
+ memset(&pdata, 0, sizeof(pdata));
+ if (chip_pdata && chip_pdata->status[id])
+ memcpy(&pdata, chip_pdata->status[id], sizeof(pdata));
+ else
+ pdata.name = dev_name(&pdev->dev);
+
+ mutex_init(&drvdata->mutex);
+ INIT_WORK(&drvdata->work, wm831x_status_work);
+ spin_lock_init(&drvdata->value_lock);
+
+ /* We cache the configuration register and read startup values
+ * from it. */
+ drvdata->reg_val = wm831x_reg_read(wm831x, drvdata->reg);
+
+ if (drvdata->reg_val & WM831X_LED_MODE_MASK)
+ drvdata->brightness = LED_FULL;
+ else
+ drvdata->brightness = LED_OFF;
+
+ /* Set a default source if configured, otherwise leave the
+ * current hardware setting.
+ */
+ if (pdata.default_src == WM831X_STATUS_PRESERVE) {
+ drvdata->src = drvdata->reg_val;
+ drvdata->src &= WM831X_LED_SRC_MASK;
+ drvdata->src >>= WM831X_LED_SRC_SHIFT;
+ } else {
+ drvdata->src = pdata.default_src - 1;
+ }
+
+ drvdata->cdev.name = pdata.name;
+ drvdata->cdev.default_trigger = pdata.default_trigger;
+ drvdata->cdev.brightness_set = wm831x_status_set;
+ drvdata->cdev.blink_set = wm831x_status_blink_set;
+
+ ret = led_classdev_register(wm831x->dev, &drvdata->cdev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register LED: %d\n", ret);
+ goto err_led;
+ }
+
+ ret = device_create_file(drvdata->cdev.dev, &dev_attr_src);
+ if (ret != 0)
+ dev_err(&pdev->dev,
+ "No source control for LED: %d\n", ret);
+
+ return 0;
+
+err_led:
+ led_classdev_unregister(&drvdata->cdev);
+ kfree(drvdata);
+err:
+ return ret;
+}
+
+static int wm831x_status_remove(struct platform_device *pdev)
+{
+ struct wm831x_status *drvdata = platform_get_drvdata(pdev);
+
+ device_remove_file(drvdata->cdev.dev, &dev_attr_src);
+ led_classdev_unregister(&drvdata->cdev);
+ kfree(drvdata);
+
+ return 0;
+}
+
+static struct platform_driver wm831x_status_driver = {
+ .driver = {
+ .name = "wm831x-status",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm831x_status_probe,
+ .remove = wm831x_status_remove,
+};
+
+static int __devinit wm831x_status_init(void)
+{
+ return platform_driver_register(&wm831x_status_driver);
+}
+module_init(wm831x_status_init);
+
+static void wm831x_status_exit(void)
+{
+ platform_driver_unregister(&wm831x_status_driver);
+}
+module_exit(wm831x_status_exit);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM831x status LED driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm831x-status");
diff --git a/drivers/leds/leds-wm8350.c b/drivers/leds/leds-wm8350.c
new file mode 100644
index 00000000000..38c6bcb07e6
--- /dev/null
+++ b/drivers/leds/leds-wm8350.c
@@ -0,0 +1,311 @@
+/*
+ * LED driver for WM8350 driven LEDS.
+ *
+ * Copyright(C) 2007, 2008 Wolfson Microelectronics PLC.
+ *
+ * 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 <linux/err.h>
+#include <linux/mfd/wm8350/pmic.h>
+#include <linux/regulator/consumer.h>
+
+/* Microamps */
+static const int isink_cur[] = {
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 10,
+ 11,
+ 14,
+ 16,
+ 19,
+ 23,
+ 27,
+ 32,
+ 39,
+ 46,
+ 54,
+ 65,
+ 77,
+ 92,
+ 109,
+ 130,
+ 154,
+ 183,
+ 218,
+ 259,
+ 308,
+ 367,
+ 436,
+ 518,
+ 616,
+ 733,
+ 872,
+ 1037,
+ 1233,
+ 1466,
+ 1744,
+ 2073,
+ 2466,
+ 2933,
+ 3487,
+ 4147,
+ 4932,
+ 5865,
+ 6975,
+ 8294,
+ 9864,
+ 11730,
+ 13949,
+ 16589,
+ 19728,
+ 23460,
+ 27899,
+ 33178,
+ 39455,
+ 46920,
+ 55798,
+ 66355,
+ 78910,
+ 93840,
+ 111596,
+ 132710,
+ 157820,
+ 187681,
+ 223191
+};
+
+#define to_wm8350_led(led_cdev) \
+ container_of(led_cdev, struct wm8350_led, cdev)
+
+static void wm8350_led_enable(struct wm8350_led *led)
+{
+ int ret;
+
+ if (led->enabled)
+ return;
+
+ ret = regulator_enable(led->isink);
+ if (ret != 0) {
+ dev_err(led->cdev.dev, "Failed to enable ISINK: %d\n", ret);
+ return;
+ }
+
+ ret = regulator_enable(led->dcdc);
+ if (ret != 0) {
+ dev_err(led->cdev.dev, "Failed to enable DCDC: %d\n", ret);
+ regulator_disable(led->isink);
+ return;
+ }
+
+ led->enabled = 1;
+}
+
+static void wm8350_led_disable(struct wm8350_led *led)
+{
+ int ret;
+
+ if (!led->enabled)
+ return;
+
+ ret = regulator_disable(led->dcdc);
+ if (ret != 0) {
+ dev_err(led->cdev.dev, "Failed to disable DCDC: %d\n", ret);
+ return;
+ }
+
+ ret = regulator_disable(led->isink);
+ if (ret != 0) {
+ dev_err(led->cdev.dev, "Failed to disable ISINK: %d\n", ret);
+ regulator_enable(led->dcdc);
+ return;
+ }
+
+ led->enabled = 0;
+}
+
+static void led_work(struct work_struct *work)
+{
+ struct wm8350_led *led = container_of(work, struct wm8350_led, work);
+ int ret;
+ int uA;
+ unsigned long flags;
+
+ mutex_lock(&led->mutex);
+
+ spin_lock_irqsave(&led->value_lock, flags);
+
+ if (led->value == LED_OFF) {
+ spin_unlock_irqrestore(&led->value_lock, flags);
+ wm8350_led_disable(led);
+ goto out;
+ }
+
+ /* This scales linearly into the index of valid current
+ * settings which results in a linear scaling of perceived
+ * brightness due to the non-linear current settings provided
+ * by the hardware.
+ */
+ uA = (led->max_uA_index * led->value) / LED_FULL;
+ spin_unlock_irqrestore(&led->value_lock, flags);
+ BUG_ON(uA >= ARRAY_SIZE(isink_cur));
+
+ ret = regulator_set_current_limit(led->isink, isink_cur[uA],
+ isink_cur[uA]);
+ if (ret != 0)
+ dev_err(led->cdev.dev, "Failed to set %duA: %d\n",
+ isink_cur[uA], ret);
+
+ wm8350_led_enable(led);
+
+out:
+ mutex_unlock(&led->mutex);
+}
+
+static void wm8350_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct wm8350_led *led = to_wm8350_led(led_cdev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&led->value_lock, flags);
+ led->value = value;
+ schedule_work(&led->work);
+ spin_unlock_irqrestore(&led->value_lock, flags);
+}
+
+static void wm8350_led_shutdown(struct platform_device *pdev)
+{
+ struct wm8350_led *led = platform_get_drvdata(pdev);
+
+ mutex_lock(&led->mutex);
+ led->value = LED_OFF;
+ wm8350_led_disable(led);
+ mutex_unlock(&led->mutex);
+}
+
+static int wm8350_led_probe(struct platform_device *pdev)
+{
+ struct regulator *isink, *dcdc;
+ struct wm8350_led *led;
+ struct wm8350_led_platform_data *pdata = pdev->dev.platform_data;
+ int ret, i;
+
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "no platform data\n");
+ return -ENODEV;
+ }
+
+ if (pdata->max_uA < isink_cur[0]) {
+ dev_err(&pdev->dev, "Invalid maximum current %duA\n",
+ pdata->max_uA);
+ return -EINVAL;
+ }
+
+ isink = regulator_get(&pdev->dev, "led_isink");
+ if (IS_ERR(isink)) {
+ printk(KERN_ERR "%s: cant get ISINK\n", __func__);
+ return PTR_ERR(isink);
+ }
+
+ dcdc = regulator_get(&pdev->dev, "led_vcc");
+ if (IS_ERR(dcdc)) {
+ printk(KERN_ERR "%s: cant get DCDC\n", __func__);
+ ret = PTR_ERR(dcdc);
+ goto err_isink;
+ }
+
+ led = kzalloc(sizeof(*led), GFP_KERNEL);
+ if (led == NULL) {
+ ret = -ENOMEM;
+ goto err_dcdc;
+ }
+
+ led->cdev.brightness_set = wm8350_led_set;
+ led->cdev.default_trigger = pdata->default_trigger;
+ led->cdev.name = pdata->name;
+ led->cdev.flags |= LED_CORE_SUSPENDRESUME;
+ led->enabled = regulator_is_enabled(isink);
+ led->isink = isink;
+ led->dcdc = dcdc;
+
+ for (i = 0; i < ARRAY_SIZE(isink_cur) - 1; i++)
+ if (isink_cur[i] >= pdata->max_uA)
+ break;
+ led->max_uA_index = i;
+ if (pdata->max_uA != isink_cur[i])
+ dev_warn(&pdev->dev,
+ "Maximum current %duA is not directly supported,"
+ " check platform data\n",
+ pdata->max_uA);
+
+ spin_lock_init(&led->value_lock);
+ mutex_init(&led->mutex);
+ INIT_WORK(&led->work, led_work);
+ led->value = LED_OFF;
+ platform_set_drvdata(pdev, led);
+
+ ret = led_classdev_register(&pdev->dev, &led->cdev);
+ if (ret < 0)
+ goto err_led;
+
+ return 0;
+
+ err_led:
+ kfree(led);
+ err_dcdc:
+ regulator_put(dcdc);
+ err_isink:
+ regulator_put(isink);
+ return ret;
+}
+
+static int wm8350_led_remove(struct platform_device *pdev)
+{
+ struct wm8350_led *led = platform_get_drvdata(pdev);
+
+ led_classdev_unregister(&led->cdev);
+ flush_scheduled_work();
+ wm8350_led_disable(led);
+ regulator_put(led->dcdc);
+ regulator_put(led->isink);
+ kfree(led);
+ return 0;
+}
+
+static struct platform_driver wm8350_led_driver = {
+ .driver = {
+ .name = "wm8350-led",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8350_led_probe,
+ .remove = wm8350_led_remove,
+ .shutdown = wm8350_led_shutdown,
+};
+
+static int __devinit wm8350_led_init(void)
+{
+ return platform_driver_register(&wm8350_led_driver);
+}
+module_init(wm8350_led_init);
+
+static void wm8350_led_exit(void)
+{
+ platform_driver_unregister(&wm8350_led_driver);
+}
+module_exit(wm8350_led_exit);
+
+MODULE_AUTHOR("Mark Brown");
+MODULE_DESCRIPTION("WM8350 LED driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm8350-led");
diff --git a/drivers/leds/leds-wrap.c b/drivers/leds/leds-wrap.c
new file mode 100644
index 00000000000..2982c86ac4c
--- /dev/null
+++ b/drivers/leds/leds-wrap.c
@@ -0,0 +1,153 @@
+/*
+ * LEDs driver for PCEngines WRAP
+ *
+ * Copyright (C) 2006 Kristian Kielhofner <kris@krisk.org>
+ *
+ * Based on leds-net48xx.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/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/err.h>
+#include <asm/io.h>
+#include <linux/scx200_gpio.h>
+
+#define DRVNAME "wrap-led"
+#define WRAP_POWER_LED_GPIO 2
+#define WRAP_ERROR_LED_GPIO 3
+#define WRAP_EXTRA_LED_GPIO 18
+
+static struct platform_device *pdev;
+
+static void wrap_power_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ if (value)
+ scx200_gpio_set_low(WRAP_POWER_LED_GPIO);
+ else
+ scx200_gpio_set_high(WRAP_POWER_LED_GPIO);
+}
+
+static void wrap_error_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ if (value)
+ scx200_gpio_set_low(WRAP_ERROR_LED_GPIO);
+ else
+ scx200_gpio_set_high(WRAP_ERROR_LED_GPIO);
+}
+
+static void wrap_extra_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ if (value)
+ scx200_gpio_set_low(WRAP_EXTRA_LED_GPIO);
+ else
+ scx200_gpio_set_high(WRAP_EXTRA_LED_GPIO);
+}
+
+static struct led_classdev wrap_power_led = {
+ .name = "wrap::power",
+ .brightness_set = wrap_power_led_set,
+ .default_trigger = "default-on",
+ .flags = LED_CORE_SUSPENDRESUME,
+};
+
+static struct led_classdev wrap_error_led = {
+ .name = "wrap::error",
+ .brightness_set = wrap_error_led_set,
+ .flags = LED_CORE_SUSPENDRESUME,
+};
+
+static struct led_classdev wrap_extra_led = {
+ .name = "wrap::extra",
+ .brightness_set = wrap_extra_led_set,
+ .flags = LED_CORE_SUSPENDRESUME,
+};
+
+static int wrap_led_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ ret = led_classdev_register(&pdev->dev, &wrap_power_led);
+ if (ret < 0)
+ return ret;
+
+ ret = led_classdev_register(&pdev->dev, &wrap_error_led);
+ if (ret < 0)
+ goto err1;
+
+ ret = led_classdev_register(&pdev->dev, &wrap_extra_led);
+ if (ret < 0)
+ goto err2;
+
+ return ret;
+
+err2:
+ led_classdev_unregister(&wrap_error_led);
+err1:
+ led_classdev_unregister(&wrap_power_led);
+
+ return ret;
+}
+
+static int wrap_led_remove(struct platform_device *pdev)
+{
+ led_classdev_unregister(&wrap_power_led);
+ led_classdev_unregister(&wrap_error_led);
+ led_classdev_unregister(&wrap_extra_led);
+ return 0;
+}
+
+static struct platform_driver wrap_led_driver = {
+ .probe = wrap_led_probe,
+ .remove = wrap_led_remove,
+ .driver = {
+ .name = DRVNAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init wrap_led_init(void)
+{
+ int ret;
+
+ if (!scx200_gpio_present()) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ ret = platform_driver_register(&wrap_led_driver);
+ if (ret < 0)
+ goto out;
+
+ pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0);
+ if (IS_ERR(pdev)) {
+ ret = PTR_ERR(pdev);
+ platform_driver_unregister(&wrap_led_driver);
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+static void __exit wrap_led_exit(void)
+{
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&wrap_led_driver);
+}
+
+module_init(wrap_led_init);
+module_exit(wrap_led_exit);
+
+MODULE_AUTHOR("Kristian Kielhofner <kris@krisk.org>");
+MODULE_DESCRIPTION("PCEngines WRAP LED driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h
new file mode 100644
index 00000000000..2dd8ecbfdc3
--- /dev/null
+++ b/drivers/leds/leds.h
@@ -0,0 +1,54 @@
+/*
+ * 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/device.h>
+#include <linux/rwsem.h>
+#include <linux/leds.h>
+
+static inline void led_set_brightness(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ if (value > led_cdev->max_brightness)
+ value = led_cdev->max_brightness;
+ led_cdev->brightness = value;
+ if (!(led_cdev->flags & LED_SUSPENDED))
+ 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;
+
+#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);
+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_remove(x) do {} while (0)
+#endif
+
+ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count);
+ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
+ char *buf);
+
+#endif /* __LEDS_H_INCLUDED */
diff --git a/drivers/leds/ledtrig-backlight.c b/drivers/leds/ledtrig-backlight.c
new file mode 100644
index 00000000000..d3dfcfb417b
--- /dev/null
+++ b/drivers/leds/ledtrig-backlight.c
@@ -0,0 +1,110 @@
+/*
+ * Backlight emulation LED trigger
+ *
+ * Copyright 2008 (C) Rodolfo Giometti <giometti@linux.it>
+ * Copyright 2008 (C) Eurotech S.p.A. <info@eurotech.it>
+ *
+ * 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/fb.h>
+#include <linux/leds.h>
+#include "leds.h"
+
+#define BLANK 1
+#define UNBLANK 0
+
+struct bl_trig_notifier {
+ struct led_classdev *led;
+ int brightness;
+ int old_status;
+ struct notifier_block notifier;
+};
+
+static int fb_notifier_callback(struct notifier_block *p,
+ unsigned long event, void *data)
+{
+ struct bl_trig_notifier *n = container_of(p,
+ struct bl_trig_notifier, notifier);
+ struct led_classdev *led = n->led;
+ struct fb_event *fb_event = data;
+ int *blank = fb_event->data;
+
+ switch (event) {
+ case FB_EVENT_BLANK :
+ if (*blank && n->old_status == UNBLANK) {
+ n->brightness = led->brightness;
+ led_set_brightness(led, LED_OFF);
+ n->old_status = BLANK;
+ } else if (!*blank && n->old_status == BLANK) {
+ led_set_brightness(led, n->brightness);
+ n->old_status = UNBLANK;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static void bl_trig_activate(struct led_classdev *led)
+{
+ int ret;
+
+ struct bl_trig_notifier *n;
+
+ n = kzalloc(sizeof(struct bl_trig_notifier), GFP_KERNEL);
+ led->trigger_data = n;
+ if (!n) {
+ dev_err(led->dev, "unable to allocate backlight trigger\n");
+ return;
+ }
+
+ n->led = led;
+ n->brightness = led->brightness;
+ n->old_status = UNBLANK;
+ n->notifier.notifier_call = fb_notifier_callback;
+
+ ret = fb_register_client(&n->notifier);
+ if (ret)
+ dev_err(led->dev, "unable to register backlight trigger\n");
+}
+
+static void bl_trig_deactivate(struct led_classdev *led)
+{
+ struct bl_trig_notifier *n =
+ (struct bl_trig_notifier *) led->trigger_data;
+
+ if (n) {
+ fb_unregister_client(&n->notifier);
+ kfree(n);
+ }
+}
+
+static struct led_trigger bl_led_trigger = {
+ .name = "backlight",
+ .activate = bl_trig_activate,
+ .deactivate = bl_trig_deactivate
+};
+
+static int __init bl_trig_init(void)
+{
+ return led_trigger_register(&bl_led_trigger);
+}
+
+static void __exit bl_trig_exit(void)
+{
+ led_trigger_unregister(&bl_led_trigger);
+}
+
+module_init(bl_trig_init);
+module_exit(bl_trig_exit);
+
+MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
+MODULE_DESCRIPTION("Backlight emulation LED trigger");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/leds/ledtrig-default-on.c b/drivers/leds/ledtrig-default-on.c
new file mode 100644
index 00000000000..a4ef54b9d50
--- /dev/null
+++ b/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_cdev->max_brightness);
+}
+
+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");
diff --git a/drivers/leds/ledtrig-gpio.c b/drivers/leds/ledtrig-gpio.c
new file mode 100644
index 00000000000..f5913372d69
--- /dev/null
+++ b/drivers/leds/ledtrig-gpio.c
@@ -0,0 +1,249 @@
+/*
+ * ledtrig-gio.c - LED Trigger Based on GPIO events
+ *
+ * Copyright 2009 Felipe Balbi <me@felipebalbi.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/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/leds.h>
+#include "leds.h"
+
+struct gpio_trig_data {
+ struct led_classdev *led;
+ struct work_struct work;
+
+ unsigned desired_brightness; /* desired brightness when led is on */
+ unsigned inverted; /* true when gpio is inverted */
+ unsigned gpio; /* gpio that triggers the leds */
+};
+
+static irqreturn_t gpio_trig_irq(int irq, void *_led)
+{
+ struct led_classdev *led = _led;
+ struct gpio_trig_data *gpio_data = led->trigger_data;
+
+ /* just schedule_work since gpio_get_value can sleep */
+ schedule_work(&gpio_data->work);
+
+ return IRQ_HANDLED;
+};
+
+static void gpio_trig_work(struct work_struct *work)
+{
+ struct gpio_trig_data *gpio_data = container_of(work,
+ struct gpio_trig_data, work);
+ int tmp;
+
+ if (!gpio_data->gpio)
+ return;
+
+ tmp = gpio_get_value(gpio_data->gpio);
+ if (gpio_data->inverted)
+ tmp = !tmp;
+
+ if (tmp) {
+ if (gpio_data->desired_brightness)
+ led_set_brightness(gpio_data->led,
+ gpio_data->desired_brightness);
+ else
+ led_set_brightness(gpio_data->led, LED_FULL);
+ } else {
+ led_set_brightness(gpio_data->led, LED_OFF);
+ }
+}
+
+static ssize_t gpio_trig_brightness_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led = dev_get_drvdata(dev);
+ struct gpio_trig_data *gpio_data = led->trigger_data;
+
+ return sprintf(buf, "%u\n", gpio_data->desired_brightness);
+}
+
+static ssize_t gpio_trig_brightness_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t n)
+{
+ struct led_classdev *led = dev_get_drvdata(dev);
+ struct gpio_trig_data *gpio_data = led->trigger_data;
+ unsigned desired_brightness;
+ int ret;
+
+ ret = sscanf(buf, "%u", &desired_brightness);
+ if (ret < 1 || desired_brightness > 255) {
+ dev_err(dev, "invalid value\n");
+ return -EINVAL;
+ }
+
+ gpio_data->desired_brightness = desired_brightness;
+
+ return n;
+}
+static DEVICE_ATTR(desired_brightness, 0644, gpio_trig_brightness_show,
+ gpio_trig_brightness_store);
+
+static ssize_t gpio_trig_inverted_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led = dev_get_drvdata(dev);
+ struct gpio_trig_data *gpio_data = led->trigger_data;
+
+ return sprintf(buf, "%s\n", gpio_data->inverted ? "yes" : "no");
+}
+
+static ssize_t gpio_trig_inverted_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t n)
+{
+ struct led_classdev *led = dev_get_drvdata(dev);
+ struct gpio_trig_data *gpio_data = led->trigger_data;
+ unsigned inverted;
+ int ret;
+
+ ret = sscanf(buf, "%u", &inverted);
+ if (ret < 1) {
+ dev_err(dev, "invalid value\n");
+ return -EINVAL;
+ }
+
+ gpio_data->inverted = !!inverted;
+
+ /* After inverting, we need to update the LED. */
+ schedule_work(&gpio_data->work);
+
+ return n;
+}
+static DEVICE_ATTR(inverted, 0644, gpio_trig_inverted_show,
+ gpio_trig_inverted_store);
+
+static ssize_t gpio_trig_gpio_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led = dev_get_drvdata(dev);
+ struct gpio_trig_data *gpio_data = led->trigger_data;
+
+ return sprintf(buf, "%u\n", gpio_data->gpio);
+}
+
+static ssize_t gpio_trig_gpio_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t n)
+{
+ struct led_classdev *led = dev_get_drvdata(dev);
+ struct gpio_trig_data *gpio_data = led->trigger_data;
+ unsigned gpio;
+ int ret;
+
+ ret = sscanf(buf, "%u", &gpio);
+ if (ret < 1) {
+ dev_err(dev, "couldn't read gpio number\n");
+ flush_work(&gpio_data->work);
+ return -EINVAL;
+ }
+
+ if (gpio_data->gpio == gpio)
+ return n;
+
+ if (!gpio) {
+ if (gpio_data->gpio != 0)
+ free_irq(gpio_to_irq(gpio_data->gpio), led);
+ gpio_data->gpio = 0;
+ return n;
+ }
+
+ ret = request_irq(gpio_to_irq(gpio), gpio_trig_irq,
+ IRQF_SHARED | IRQF_TRIGGER_RISING
+ | IRQF_TRIGGER_FALLING, "ledtrig-gpio", led);
+ if (ret) {
+ dev_err(dev, "request_irq failed with error %d\n", ret);
+ } else {
+ if (gpio_data->gpio != 0)
+ free_irq(gpio_to_irq(gpio_data->gpio), led);
+ gpio_data->gpio = gpio;
+ }
+
+ return ret ? ret : n;
+}
+static DEVICE_ATTR(gpio, 0644, gpio_trig_gpio_show, gpio_trig_gpio_store);
+
+static void gpio_trig_activate(struct led_classdev *led)
+{
+ struct gpio_trig_data *gpio_data;
+ int ret;
+
+ gpio_data = kzalloc(sizeof(*gpio_data), GFP_KERNEL);
+ if (!gpio_data)
+ return;
+
+ ret = device_create_file(led->dev, &dev_attr_gpio);
+ if (ret)
+ goto err_gpio;
+
+ ret = device_create_file(led->dev, &dev_attr_inverted);
+ if (ret)
+ goto err_inverted;
+
+ ret = device_create_file(led->dev, &dev_attr_desired_brightness);
+ if (ret)
+ goto err_brightness;
+
+ gpio_data->led = led;
+ led->trigger_data = gpio_data;
+ INIT_WORK(&gpio_data->work, gpio_trig_work);
+
+ return;
+
+err_brightness:
+ device_remove_file(led->dev, &dev_attr_inverted);
+
+err_inverted:
+ device_remove_file(led->dev, &dev_attr_gpio);
+
+err_gpio:
+ kfree(gpio_data);
+}
+
+static void gpio_trig_deactivate(struct led_classdev *led)
+{
+ struct gpio_trig_data *gpio_data = led->trigger_data;
+
+ if (gpio_data) {
+ device_remove_file(led->dev, &dev_attr_gpio);
+ device_remove_file(led->dev, &dev_attr_inverted);
+ device_remove_file(led->dev, &dev_attr_desired_brightness);
+ flush_work(&gpio_data->work);
+ if (gpio_data->gpio != 0)
+ free_irq(gpio_to_irq(gpio_data->gpio), led);
+ kfree(gpio_data);
+ }
+}
+
+static struct led_trigger gpio_led_trigger = {
+ .name = "gpio",
+ .activate = gpio_trig_activate,
+ .deactivate = gpio_trig_deactivate,
+};
+
+static int __init gpio_trig_init(void)
+{
+ return led_trigger_register(&gpio_led_trigger);
+}
+module_init(gpio_trig_init);
+
+static void __exit gpio_trig_exit(void)
+{
+ led_trigger_unregister(&gpio_led_trigger);
+}
+module_exit(gpio_trig_exit);
+
+MODULE_AUTHOR("Felipe Balbi <me@felipebalbi.com>");
+MODULE_DESCRIPTION("GPIO LED trigger");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/ledtrig-heartbeat.c b/drivers/leds/ledtrig-heartbeat.c
new file mode 100644
index 00000000000..c1c1ea6f817
--- /dev/null
+++ b/drivers/leds/ledtrig-heartbeat.c
@@ -0,0 +1,118 @@
+/*
+ * LED Heartbeat Trigger
+ *
+ * Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
+ *
+ * Based on Richard Purdie's ledtrig-timer.c and some arch's
+ * CONFIG_HEARTBEAT code.
+ *
+ * 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/sched.h>
+#include <linux/leds.h>
+#include "leds.h"
+
+struct heartbeat_trig_data {
+ unsigned int phase;
+ unsigned int period;
+ struct timer_list timer;
+};
+
+static void led_heartbeat_function(unsigned long data)
+{
+ struct led_classdev *led_cdev = (struct led_classdev *) data;
+ struct heartbeat_trig_data *heartbeat_data = led_cdev->trigger_data;
+ unsigned long brightness = LED_OFF;
+ unsigned long delay = 0;
+
+ /* acts like an actual heart beat -- ie thump-thump-pause... */
+ switch (heartbeat_data->phase) {
+ case 0:
+ /*
+ * The hyperbolic function below modifies the
+ * heartbeat period length in dependency of the
+ * current (1min) load. It goes through the points
+ * f(0)=1260, f(1)=860, f(5)=510, f(inf)->300.
+ */
+ heartbeat_data->period = 300 +
+ (6720 << FSHIFT) / (5 * avenrun[0] + (7 << FSHIFT));
+ heartbeat_data->period =
+ msecs_to_jiffies(heartbeat_data->period);
+ delay = msecs_to_jiffies(70);
+ heartbeat_data->phase++;
+ brightness = led_cdev->max_brightness;
+ break;
+ case 1:
+ delay = heartbeat_data->period / 4 - msecs_to_jiffies(70);
+ heartbeat_data->phase++;
+ break;
+ case 2:
+ delay = msecs_to_jiffies(70);
+ heartbeat_data->phase++;
+ brightness = led_cdev->max_brightness;
+ break;
+ default:
+ delay = heartbeat_data->period - heartbeat_data->period / 4 -
+ msecs_to_jiffies(70);
+ heartbeat_data->phase = 0;
+ break;
+ }
+
+ led_set_brightness(led_cdev, brightness);
+ mod_timer(&heartbeat_data->timer, jiffies + delay);
+}
+
+static void heartbeat_trig_activate(struct led_classdev *led_cdev)
+{
+ struct heartbeat_trig_data *heartbeat_data;
+
+ heartbeat_data = kzalloc(sizeof(*heartbeat_data), GFP_KERNEL);
+ if (!heartbeat_data)
+ return;
+
+ led_cdev->trigger_data = heartbeat_data;
+ setup_timer(&heartbeat_data->timer,
+ led_heartbeat_function, (unsigned long) led_cdev);
+ heartbeat_data->phase = 0;
+ led_heartbeat_function(heartbeat_data->timer.data);
+}
+
+static void heartbeat_trig_deactivate(struct led_classdev *led_cdev)
+{
+ struct heartbeat_trig_data *heartbeat_data = led_cdev->trigger_data;
+
+ if (heartbeat_data) {
+ del_timer_sync(&heartbeat_data->timer);
+ kfree(heartbeat_data);
+ }
+}
+
+static struct led_trigger heartbeat_led_trigger = {
+ .name = "heartbeat",
+ .activate = heartbeat_trig_activate,
+ .deactivate = heartbeat_trig_deactivate,
+};
+
+static int __init heartbeat_trig_init(void)
+{
+ return led_trigger_register(&heartbeat_led_trigger);
+}
+
+static void __exit heartbeat_trig_exit(void)
+{
+ led_trigger_unregister(&heartbeat_led_trigger);
+}
+
+module_init(heartbeat_trig_init);
+module_exit(heartbeat_trig_exit);
+
+MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
+MODULE_DESCRIPTION("Heartbeat LED trigger");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/ledtrig-ide-disk.c b/drivers/leds/ledtrig-ide-disk.c
new file mode 100644
index 00000000000..ec099fcbcb0
--- /dev/null
+++ b/drivers/leds/ledtrig-ide-disk.c
@@ -0,0 +1,64 @@
+/*
+ * 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/jiffies.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;
+ /* INT_MAX will set each LED to its maximum brightness */
+ led_trigger_event(ledtrig_ide, INT_MAX);
+ 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..38b3378be44
--- /dev/null
+++ b/drivers/leds/ledtrig-timer.c
@@ -0,0 +1,237 @@
+/*
+ * 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/module.h>
+#include <linux/jiffies.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/ctype.h>
+#include <linux/leds.h>
+#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;
+};
+
+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;
+ unsigned long delay;
+
+ if (!timer_data->delay_on || !timer_data->delay_off) {
+ led_set_brightness(led_cdev, LED_OFF);
+ return;
+ }
+
+ 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);
+
+ mod_timer(&timer_data->timer, jiffies + msecs_to_jiffies(delay));
+}
+
+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);
+ struct timer_trig_data *timer_data = led_cdev->trigger_data;
+
+ return sprintf(buf, "%lu\n", timer_data->delay_on);
+}
+
+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);
+ struct timer_trig_data *timer_data = led_cdev->trigger_data;
+ int ret = -EINVAL;
+ char *after;
+ unsigned long state = simple_strtoul(buf, &after, 10);
+ size_t count = after - buf;
+
+ if (isspace(*after))
+ count++;
+
+ if (count == size) {
+ if (timer_data->delay_on != state) {
+ /* the new value differs from the previous */
+ timer_data->delay_on = state;
+
+ /* deactivate previous settings */
+ del_timer_sync(&timer_data->timer);
+
+ /* 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)) {
+ /* no hardware acceleration, blink via timer */
+ mod_timer(&timer_data->timer, jiffies + 1);
+ }
+ }
+ ret = count;
+ }
+
+ return ret;
+}
+
+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);
+ struct timer_trig_data *timer_data = led_cdev->trigger_data;
+
+ return sprintf(buf, "%lu\n", timer_data->delay_off);
+}
+
+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);
+ struct timer_trig_data *timer_data = led_cdev->trigger_data;
+ int ret = -EINVAL;
+ char *after;
+ unsigned long state = simple_strtoul(buf, &after, 10);
+ size_t count = after - buf;
+
+ if (isspace(*after))
+ count++;
+
+ if (count == size) {
+ if (timer_data->delay_off != state) {
+ /* the new value differs from the previous */
+ timer_data->delay_off = state;
+
+ /* deactivate previous settings */
+ del_timer_sync(&timer_data->timer);
+
+ /* 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)) {
+ /* no hardware acceleration, blink via timer */
+ mod_timer(&timer_data->timer, jiffies + 1);
+ }
+ }
+ ret = count;
+ }
+
+ return ret;
+}
+
+static DEVICE_ATTR(delay_on, 0644, led_delay_on_show, led_delay_on_store);
+static 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;
+ int rc;
+
+ timer_data = kzalloc(sizeof(struct timer_trig_data), GFP_KERNEL);
+ 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_cdev->max_brightness;
+ 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;
+
+ rc = device_create_file(led_cdev->dev, &dev_attr_delay_on);
+ if (rc)
+ goto err_out;
+ rc = device_create_file(led_cdev->dev, &dev_attr_delay_off);
+ if (rc)
+ goto err_out_delayon;
+
+ /* If there is hardware support for blinking, start one
+ * user friendly blink rate chosen by the driver.
+ */
+ if (led_cdev->blink_set)
+ led_cdev->blink_set(led_cdev,
+ &timer_data->delay_on, &timer_data->delay_off);
+
+ return;
+
+err_out_delayon:
+ device_remove_file(led_cdev->dev, &dev_attr_delay_on);
+err_out:
+ led_cdev->trigger_data = NULL;
+ kfree(timer_data);
+}
+
+static void timer_trig_deactivate(struct led_classdev *led_cdev)
+{
+ struct timer_trig_data *timer_data = led_cdev->trigger_data;
+ unsigned long on = 0, off = 0;
+
+ if (timer_data) {
+ device_remove_file(led_cdev->dev, &dev_attr_delay_on);
+ device_remove_file(led_cdev->dev, &dev_attr_delay_off);
+ del_timer_sync(&timer_data->timer);
+ kfree(timer_data);
+ }
+
+ /* If there is hardware support for blinking, stop it */
+ if (led_cdev->blink_set)
+ led_cdev->blink_set(led_cdev, &on, &off);
+}
+
+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");