From cec035de8265b18252742ef359b12e9694641112 Mon Sep 17 00:00:00 2001 From: Márton Németh Date: Wed, 31 Oct 2007 11:46:41 +0100 Subject: leds: Add clevo notebook LED driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver supports the mail LED commonly found on different Clevo notebooks. The driver access the LED through the i8042 hardware which is handled by the input subsystem. Signed-off-by: Márton Németh Signed-off-by: Richard Purdie --- drivers/leds/Kconfig | 24 ++++++ drivers/leds/Makefile | 1 + drivers/leds/leds-clevo-mail.c | 182 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 207 insertions(+) create mode 100644 drivers/leds/leds-clevo-mail.c (limited to 'drivers') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index ec568fa1c6c..cf3a6d4d947 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -114,6 +114,30 @@ config LEDS_CM_X270 help This option enables support for the CM-X270 LEDs. +config LEDS_CLEVO_MAIL + tristate "Mail LED on Clevo notebook (EXPERIMENTAL)" + depends on LEDS_CLASS && X86 && SERIO_I8042 && DMI && EXPERIMENTAL + 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. + + 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 D410J + Clevo D410V + Clevo D400V/D470V (not tested, but might work) + Clevo M540N + Clevo M5x0N (not tested, but might work) + Positivo Mobile (Clevo M5x0V) + + To compile this driver as a module, choose M here: the + module will be called leds-clevo-mail. + comment "LED Triggers" config LEDS_TRIGGERS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index a60de1b46c2..e433e2f4e52 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o obj-$(CONFIG_LEDS_CM_X270) += leds-cm-x270.o +obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o # LED Triggers obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o diff --git a/drivers/leds/leds-clevo-mail.c b/drivers/leds/leds-clevo-mail.c new file mode 100644 index 00000000000..50aaa0b3381 --- /dev/null +++ b/drivers/leds/leds-clevo-mail.c @@ -0,0 +1,182 @@ + +#include + +#include +#include +#include + +#include +#include + +#include + +#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 "); +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 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) +{ + 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); + +} + +static struct led_classdev clevo_mail_led = { + .name = "clevo", + .brightness_set = clevo_mail_led_set, +}; + +static int __init 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; +} + +#ifdef CONFIG_PM +static int clevo_mail_led_suspend(struct platform_device *dev, + pm_message_t state) +{ + led_classdev_suspend(&clevo_mail_led); + return 0; +} + +static int clevo_mail_led_resume(struct platform_device *dev) +{ + led_classdev_resume(&clevo_mail_led); + return 0; +} +#else +#define clevo_mail_led_suspend NULL +#define clevo_mail_led_resume NULL +#endif + +static struct platform_driver clevo_mail_led_driver = { + .probe = clevo_mail_led_probe, + .remove = clevo_mail_led_remove, + .suspend = clevo_mail_led_suspend, + .resume = clevo_mail_led_resume, + .driver = { + .name = KBUILD_MODNAME, + }, +}; + +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); -- cgit v1.2.3-70-g09d2 From 6c152beefbf90579d21afc4f7e075b1f801f9a75 Mon Sep 17 00:00:00 2001 From: Richard Purdie Date: Wed, 31 Oct 2007 15:00:07 +0100 Subject: leds: Standardise LED naming scheme As discussed on LKML some notion of 'function' is needed in LED naming. This patch adds this to the documentation and standardises existing LED drivers. Signed-off-by: Richard Purdie --- Documentation/leds-class.txt | 5 +++-- arch/arm/mach-ixp4xx/dsmg600-setup.c | 4 ++-- arch/arm/mach-ixp4xx/nas100d-setup.c | 6 +++--- arch/arm/mach-ixp4xx/nslu2-setup.c | 8 ++++---- drivers/hwmon/applesmc.c | 2 +- drivers/input/misc/wistron_btns.c | 4 ++-- drivers/leds/leds-ams-delta.c | 12 ++++++------ drivers/leds/leds-clevo-mail.c | 2 +- drivers/leds/leds-corgi.c | 4 ++-- drivers/leds/leds-locomo.c | 4 ++-- drivers/leds/leds-net48xx.c | 2 +- drivers/leds/leds-spitz.c | 8 +++++--- drivers/leds/leds-tosa.c | 4 ++-- drivers/leds/leds-wrap.c | 4 ++-- drivers/misc/asus-laptop.c | 2 +- drivers/net/wireless/b43/leds.c | 8 ++++---- 16 files changed, 41 insertions(+), 38 deletions(-) (limited to 'drivers') diff --git a/Documentation/leds-class.txt b/Documentation/leds-class.txt index 8c35c042611..877af6efc18 100644 --- a/Documentation/leds-class.txt +++ b/Documentation/leds-class.txt @@ -39,12 +39,13 @@ LED Device Naming Is currently of the form: -"devicename:colour" +"devicename:colour:function" There have been calls for LED properties such as colour to be exported as individual led class attributes. As a solution which doesn't incur as much overhead, I suggest these become part of the device name. The naming scheme -above leaves scope for further attributes should they be needed. +above leaves scope for further attributes should they be needed. If sections +of the name don't apply, just leave that section blank. Known Issues diff --git a/arch/arm/mach-ixp4xx/dsmg600-setup.c b/arch/arm/mach-ixp4xx/dsmg600-setup.c index 688659668bd..8cb07437a80 100644 --- a/arch/arm/mach-ixp4xx/dsmg600-setup.c +++ b/arch/arm/mach-ixp4xx/dsmg600-setup.c @@ -71,11 +71,11 @@ static struct i2c_board_info __initdata dsmg600_i2c_board_info [] = { static struct gpio_led dsmg600_led_pins[] = { { - .name = "power", + .name = "dsmg600:green:power", .gpio = DSMG600_LED_PWR_GPIO, }, { - .name = "wlan", + .name = "dsmg600:green:wlan", .gpio = DSMG600_LED_WLAN_GPIO, .active_low = true, }, diff --git a/arch/arm/mach-ixp4xx/nas100d-setup.c b/arch/arm/mach-ixp4xx/nas100d-setup.c index 4cecae84837..159e1c4f1ed 100644 --- a/arch/arm/mach-ixp4xx/nas100d-setup.c +++ b/arch/arm/mach-ixp4xx/nas100d-setup.c @@ -60,17 +60,17 @@ static struct i2c_board_info __initdata nas100d_i2c_board_info [] = { static struct gpio_led nas100d_led_pins[] = { { - .name = "wlan", /* green led */ + .name = "nas100d:green:wlan", .gpio = NAS100D_LED_WLAN_GPIO, .active_low = true, }, { - .name = "power", /* blue power led (off=flashing) */ + .name = "nas100d:blue:power", /* (off=flashing) */ .gpio = NAS100D_LED_PWR_GPIO, .active_low = true, }, { - .name = "disk", /* yellow led */ + .name = "nas100d:yellow:disk", .gpio = NAS100D_LED_DISK_GPIO, .active_low = true, }, diff --git a/arch/arm/mach-ixp4xx/nslu2-setup.c b/arch/arm/mach-ixp4xx/nslu2-setup.c index acaebcbce53..d9a182895a0 100644 --- a/arch/arm/mach-ixp4xx/nslu2-setup.c +++ b/arch/arm/mach-ixp4xx/nslu2-setup.c @@ -63,20 +63,20 @@ static struct i2c_board_info __initdata nslu2_i2c_board_info [] = { static struct gpio_led nslu2_led_pins[] = { { - .name = "ready", /* green led */ + .name = "nslu2:green:ready", .gpio = NSLU2_LED_GRN_GPIO, }, { - .name = "status", /* red led */ + .name = "nslu2:red:status", .gpio = NSLU2_LED_RED_GPIO, }, { - .name = "disk-1", + .name = "nslu2:green:disk-1", .gpio = NSLU2_LED_DISK1_GPIO, .active_low = true, }, { - .name = "disk-2", + .name = "nslu2:green:disk-2", .gpio = NSLU2_LED_DISK2_GPIO, .active_low = true, }, diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index 86c66c345f8..0c94770b7f8 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -905,7 +905,7 @@ static ssize_t applesmc_key_at_index_store(struct device *dev, } static struct led_classdev applesmc_backlight = { - .name = "smc:kbd_backlight", + .name = "smc::kbd_backlight", .default_trigger = "nand-disk", .brightness_set = applesmc_brightness_set, }; diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c index b438d998625..72176f3d49c 100644 --- a/drivers/input/misc/wistron_btns.c +++ b/drivers/input/misc/wistron_btns.c @@ -998,12 +998,12 @@ static void wistron_wifi_led_set(struct led_classdev *led_cdev, } static struct led_classdev wistron_mail_led = { - .name = "mail:green", + .name = "wistron:green:mail", .brightness_set = wistron_mail_led_set, }; static struct led_classdev wistron_wifi_led = { - .name = "wifi:red", + .name = "wistron:red:wifi", .brightness_set = wistron_wifi_led_set, }; diff --git a/drivers/leds/leds-ams-delta.c b/drivers/leds/leds-ams-delta.c index 599878c8e71..9e3077463d8 100644 --- a/drivers/leds/leds-ams-delta.c +++ b/drivers/leds/leds-ams-delta.c @@ -37,42 +37,42 @@ static void ams_delta_led_set(struct led_classdev *led_cdev, static struct ams_delta_led ams_delta_leds[] = { { .cdev = { - .name = "ams-delta:camera", + .name = "ams-delta::camera", .brightness_set = ams_delta_led_set, }, .bitmask = AMS_DELTA_LATCH1_LED_CAMERA, }, { .cdev = { - .name = "ams-delta:advert", + .name = "ams-delta::advert", .brightness_set = ams_delta_led_set, }, .bitmask = AMS_DELTA_LATCH1_LED_ADVERT, }, { .cdev = { - .name = "ams-delta:email", + .name = "ams-delta::email", .brightness_set = ams_delta_led_set, }, .bitmask = AMS_DELTA_LATCH1_LED_EMAIL, }, { .cdev = { - .name = "ams-delta:handsfree", + .name = "ams-delta::handsfree", .brightness_set = ams_delta_led_set, }, .bitmask = AMS_DELTA_LATCH1_LED_HANDSFREE, }, { .cdev = { - .name = "ams-delta:voicemail", + .name = "ams-delta::voicemail", .brightness_set = ams_delta_led_set, }, .bitmask = AMS_DELTA_LATCH1_LED_VOICEMAIL, }, { .cdev = { - .name = "ams-delta:voice", + .name = "ams-delta::voice", .brightness_set = ams_delta_led_set, }, .bitmask = AMS_DELTA_LATCH1_LED_VOICE, diff --git a/drivers/leds/leds-clevo-mail.c b/drivers/leds/leds-clevo-mail.c index 50aaa0b3381..49a7972459c 100644 --- a/drivers/leds/leds-clevo-mail.c +++ b/drivers/leds/leds-clevo-mail.c @@ -93,7 +93,7 @@ static void clevo_mail_led_set(struct led_classdev *led_cdev, } static struct led_classdev clevo_mail_led = { - .name = "clevo", + .name = "clevo::mail", .brightness_set = clevo_mail_led_set, }; diff --git a/drivers/leds/leds-corgi.c b/drivers/leds/leds-corgi.c index cf1dcd719a2..e45f6c4b59b 100644 --- a/drivers/leds/leds-corgi.c +++ b/drivers/leds/leds-corgi.c @@ -38,13 +38,13 @@ static void corgiled_green_set(struct led_classdev *led_cdev, enum led_brightnes } static struct led_classdev corgi_amber_led = { - .name = "corgi:amber", + .name = "corgi:amber:charge", .default_trigger = "sharpsl-charge", .brightness_set = corgiled_amber_set, }; static struct led_classdev corgi_green_led = { - .name = "corgi:green", + .name = "corgi:green:mail", .default_trigger = "nand-disk", .brightness_set = corgiled_green_set, }; diff --git a/drivers/leds/leds-locomo.c b/drivers/leds/leds-locomo.c index 2207335e921..7295f7f5218 100644 --- a/drivers/leds/leds-locomo.c +++ b/drivers/leds/leds-locomo.c @@ -43,13 +43,13 @@ static void locomoled_brightness_set1(struct led_classdev *led_cdev, } static struct led_classdev locomo_led0 = { - .name = "locomo:amber", + .name = "locomo:amber:charge", .default_trigger = "sharpsl-charge", .brightness_set = locomoled_brightness_set0, }; static struct led_classdev locomo_led1 = { - .name = "locomo:green", + .name = "locomo:green:mail", .default_trigger = "nand-disk", .brightness_set = locomoled_brightness_set1, }; diff --git a/drivers/leds/leds-net48xx.c b/drivers/leds/leds-net48xx.c index 45ba3d45bcb..054360473c9 100644 --- a/drivers/leds/leds-net48xx.c +++ b/drivers/leds/leds-net48xx.c @@ -31,7 +31,7 @@ static void net48xx_error_led_set(struct led_classdev *led_cdev, } static struct led_classdev net48xx_error_led = { - .name = "net48xx:error", + .name = "net48xx::error", .brightness_set = net48xx_error_led_set, }; diff --git a/drivers/leds/leds-spitz.c b/drivers/leds/leds-spitz.c index 126d09cc96e..93e1012b17e 100644 --- a/drivers/leds/leds-spitz.c +++ b/drivers/leds/leds-spitz.c @@ -38,13 +38,13 @@ static void spitzled_green_set(struct led_classdev *led_cdev, enum led_brightnes } static struct led_classdev spitz_amber_led = { - .name = "spitz:amber", + .name = "spitz:amber:charge", .default_trigger = "sharpsl-charge", .brightness_set = spitzled_amber_set, }; static struct led_classdev spitz_green_led = { - .name = "spitz:green", + .name = "spitz:green:hddactivity", .default_trigger = "ide-disk", .brightness_set = spitzled_green_set, }; @@ -72,8 +72,10 @@ static int spitzled_probe(struct platform_device *pdev) { int ret; - if (machine_is_akita()) + if (machine_is_akita()) { + spitz_green_led.name = "spitz:green:mail"; spitz_green_led.default_trigger = "nand-disk"; + } ret = led_classdev_register(&pdev->dev, &spitz_amber_led); if (ret < 0) diff --git a/drivers/leds/leds-tosa.c b/drivers/leds/leds-tosa.c index fb2416a3830..9e0a188fbb0 100644 --- a/drivers/leds/leds-tosa.c +++ b/drivers/leds/leds-tosa.c @@ -45,13 +45,13 @@ static void tosaled_green_set(struct led_classdev *led_cdev, } static struct led_classdev tosa_amber_led = { - .name = "tosa:amber", + .name = "tosa:amber:charge", .default_trigger = "sharpsl-charge", .brightness_set = tosaled_amber_set, }; static struct led_classdev tosa_green_led = { - .name = "tosa:green", + .name = "tosa:green:mail", .default_trigger = "nand-disk", .brightness_set = tosaled_green_set, }; diff --git a/drivers/leds/leds-wrap.c b/drivers/leds/leds-wrap.c index 27fb2d8e991..5770cc36ec2 100644 --- a/drivers/leds/leds-wrap.c +++ b/drivers/leds/leds-wrap.c @@ -43,12 +43,12 @@ static void wrap_extra_led_set(struct led_classdev *led_cdev, } static struct led_classdev wrap_error_led = { - .name = "wrap:error", + .name = "wrap::error", .brightness_set = wrap_error_led_set, }; static struct led_classdev wrap_extra_led = { - .name = "wrap:extra", + .name = "wrap::extra", .brightness_set = wrap_extra_led_set, }; diff --git a/drivers/misc/asus-laptop.c b/drivers/misc/asus-laptop.c index 0846c33296b..3a36a294e91 100644 --- a/drivers/misc/asus-laptop.c +++ b/drivers/misc/asus-laptop.c @@ -239,7 +239,7 @@ static struct workqueue_struct *led_workqueue; static int object##_led_wk; \ static DECLARE_WORK(object##_led_work, object##_led_update); \ static struct led_classdev object##_led = { \ - .name = "asus:" ledname, \ + .name = "asus::" ledname, \ .brightness_set = object##_led_set, \ } diff --git a/drivers/net/wireless/b43/leds.c b/drivers/net/wireless/b43/leds.c index 0908335892d..0aac1ff511d 100644 --- a/drivers/net/wireless/b43/leds.c +++ b/drivers/net/wireless/b43/leds.c @@ -147,12 +147,12 @@ static void b43_map_led(struct b43_wldev *dev, case B43_LED_TRANSFER: case B43_LED_APTRANSFER: snprintf(name, sizeof(name), - "b43-%s:tx", wiphy_name(hw->wiphy)); + "b43-%s::tx", wiphy_name(hw->wiphy)); b43_register_led(dev, &dev->led_tx, name, ieee80211_get_tx_led_name(hw), led_index, activelow); snprintf(name, sizeof(name), - "b43-%s:rx", wiphy_name(hw->wiphy)); + "b43-%s::rx", wiphy_name(hw->wiphy)); b43_register_led(dev, &dev->led_rx, name, ieee80211_get_rx_led_name(hw), led_index, activelow); @@ -162,7 +162,7 @@ static void b43_map_led(struct b43_wldev *dev, case B43_LED_RADIO_B: case B43_LED_MODE_BG: snprintf(name, sizeof(name), - "b43-%s:radio", wiphy_name(hw->wiphy)); + "b43-%s::radio", wiphy_name(hw->wiphy)); b43_register_led(dev, &dev->led_radio, name, b43_rfkill_led_name(dev), led_index, activelow); @@ -173,7 +173,7 @@ static void b43_map_led(struct b43_wldev *dev, case B43_LED_WEIRD: case B43_LED_ASSOC: snprintf(name, sizeof(name), - "b43-%s:assoc", wiphy_name(hw->wiphy)); + "b43-%s::assoc", wiphy_name(hw->wiphy)); b43_register_led(dev, &dev->led_assoc, name, ieee80211_get_assoc_led_name(hw), led_index, activelow); -- cgit v1.2.3-70-g09d2 From 4c79141d28bc290ae307e3f81f5bc909c26faf6e Mon Sep 17 00:00:00 2001 From: Márton Németh Date: Wed, 31 Oct 2007 15:07:12 +0100 Subject: leds: Add support for hardware accelerated LED flashing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extends the leds subsystem with a blink_set() callback function which can be optionally implemented by a LED driver. If implemented, the driver can use the hardware acceleration for blinking a LED. Signed-off-by: Márton Németh Signed-off-by: Richard Purdie --- Documentation/leds-class.txt | 24 ++++++++++++++++++++---- drivers/leds/Kconfig | 6 +++++- drivers/leds/ledtrig-timer.c | 41 +++++++++++++++++++++++++++++++++++++---- include/linux/leds.h | 5 +++++ 4 files changed, 67 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/Documentation/leds-class.txt b/Documentation/leds-class.txt index 877af6efc18..56757c751d6 100644 --- a/Documentation/leds-class.txt +++ b/Documentation/leds-class.txt @@ -48,6 +48,26 @@ above leaves scope for further attributes should they be needed. If sections of the name don't apply, just leave that section blank. +Hardware accelerated blink of LEDs +================================== + +Some LEDs can be programmed to blink without any CPU interaction. To +support this feature, a LED driver can optionally implement the +blink_set() function (see ). If implemeted, triggers can +attempt to use it before falling back to software timers. The blink_set() +function should return 0 if the blink setting is supported, or -EINVAL +otherwise, which means that LED blinking will be handled by software. + +The blink_set() function should choose a user friendly blinking +value if it is called with *delay_on==0 && *delay_off==0 parameters. In +this case the driver should give back the chosen value through delay_on +and delay_off parameters to the leds subsystem. + +Any call to the brightness_set() callback function should cancel the +previously programmed hardware blinking function so setting the brightness +to 0 can also cancel the blinking of the LED. + + Known Issues ============ @@ -56,10 +76,6 @@ would cause nightmare dependency issues. I see this as a minor issue compared to the benefits the simple trigger functionality brings. The rest of the LED subsystem can be modular. -Some leds can be programmed to flash in hardware. As this isn't a generic -LED device property, this should be exported as a device specific sysfs -attribute rather than part of the class if this functionality is required. - Future Development ================== diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index cf3a6d4d947..659448ead68 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -152,7 +152,11 @@ config LEDS_TRIGGER_TIMER depends on LEDS_TRIGGERS help This allows LEDs to be controlled by a programmable timer - via sysfs. If unsure, say Y. + 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" diff --git a/drivers/leds/ledtrig-timer.c b/drivers/leds/ledtrig-timer.c index ed9ff02c77e..82c55d6e490 100644 --- a/drivers/leds/ledtrig-timer.c +++ b/drivers/leds/ledtrig-timer.c @@ -77,8 +77,21 @@ static ssize_t led_delay_on_store(struct device *dev, count++; if (count == size) { - timer_data->delay_on = state; - mod_timer(&timer_data->timer, jiffies + 1); + 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; } @@ -110,8 +123,21 @@ static ssize_t led_delay_off_store(struct device *dev, count++; if (count == size) { - timer_data->delay_off = state; - mod_timer(&timer_data->timer, jiffies + 1); + 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; } @@ -143,6 +169,13 @@ static void timer_trig_activate(struct led_classdev *led_cdev) 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: diff --git a/include/linux/leds.h b/include/linux/leds.h index 00f89fd6c52..0201f6f51ce 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -38,6 +38,11 @@ struct led_classdev { void (*brightness_set)(struct led_classdev *led_cdev, enum led_brightness brightness); + /* Activate hardware accelerated blink */ + int (*blink_set)(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off); + struct device *dev; struct list_head node; /* LED Device list */ char *default_trigger; /* Trigger to use */ -- cgit v1.2.3-70-g09d2 From 92e015cb31c0a312bf2e0e5b96aef76a8c57e645 Mon Sep 17 00:00:00 2001 From: Márton Németh Date: Wed, 31 Oct 2007 15:09:05 +0100 Subject: leds: hw acceleration for Clevo mail LED driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for hardware accelerated LED blinking for the mail LED commonly found on Clevo notebooks. Signed-off-by: Márton Németh Signed-off-by: Richard Purdie --- drivers/leds/Kconfig | 8 +++++--- drivers/leds/leds-clevo-mail.c | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 659448ead68..922c3df548a 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -122,9 +122,11 @@ config LEDS_CLEVO_MAIL programs through the leds subsystem. This LED have three known mode: off, blink at 0.5Hz and blink at 1Hz. - 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. + 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: diff --git a/drivers/leds/leds-clevo-mail.c b/drivers/leds/leds-clevo-mail.c index 49a7972459c..6c3d33b8e38 100644 --- a/drivers/leds/leds-clevo-mail.c +++ b/drivers/leds/leds-clevo-mail.c @@ -92,9 +92,46 @@ static void clevo_mail_led_set(struct led_classdev *led_cdev, } +static int clevo_mail_led_blink(struct led_classdev *led_cdev, + unsigned long* delay_on, + unsigned long* delay_off) +{ + int status = -EINVAL; + + 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); + } + + return status; +} + static struct led_classdev clevo_mail_led = { .name = "clevo::mail", .brightness_set = clevo_mail_led_set, + .blink_set = clevo_mail_led_blink, }; static int __init clevo_mail_led_probe(struct platform_device *pdev) -- cgit v1.2.3-70-g09d2 From 2b7f1b8c8f4a8991dfeefc31844e15c642b6de2a Mon Sep 17 00:00:00 2001 From: Raphael Assenat Date: Sun, 13 Jan 2008 22:55:18 +0000 Subject: leds: Fix led-gpio active_low default brightness When gpio_direction_output() is called, led_dat->active_low is used as default value. This means that the led will always be off by default. cdev.brightness should really have been set to LED_OFF unconditionally to reflect this behavior. Signed-off-by: Raphael Assenat Signed-off-by: Richard Purdie --- drivers/leds/leds-gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 99bc50059d3..6c0a9c4761e 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -85,7 +85,7 @@ static int gpio_led_probe(struct platform_device *pdev) led_dat->can_sleep = gpio_cansleep(cur_led->gpio); led_dat->active_low = cur_led->active_low; led_dat->cdev.brightness_set = gpio_led_set; - led_dat->cdev.brightness = cur_led->active_low ? LED_FULL : LED_OFF; + led_dat->cdev.brightness = LED_OFF; ret = gpio_request(led_dat->gpio, led_dat->cdev.name); if (ret < 0) -- cgit v1.2.3-70-g09d2 From f5506a2f71ad75d680c81090117ff2f89602f9b9 Mon Sep 17 00:00:00 2001 From: Michael Loeffler Date: Sun, 13 Jan 2008 23:20:52 +0000 Subject: leds: Add power LED to the wrap driver The 3rd LED on this board is something like a power-led, it is on all the time. With this change to the leds-wrap driver it is possible to use this LED too. Signed-off-by: Michael Loeffler Signed-off-by: Richard Purdie --- drivers/leds/leds-wrap.c | 43 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-wrap.c b/drivers/leds/leds-wrap.c index 5770cc36ec2..7ac61a7b56a 100644 --- a/drivers/leds/leds-wrap.c +++ b/drivers/leds/leds-wrap.c @@ -19,11 +19,21 @@ #include #define DRVNAME "wrap-led" +#define WRAP_POWER_LED_GPIO 2 #define WRAP_ERROR_LED_GPIO 3 -#define WRAP_EXTRA_LED_GPIO 18 +#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) { @@ -42,6 +52,11 @@ static void wrap_extra_led_set(struct led_classdev *led_cdev, scx200_gpio_set_high(WRAP_EXTRA_LED_GPIO); } +static struct led_classdev wrap_power_led = { + .name = "wrap::power", + .brightness_set = wrap_power_led_set, +}; + static struct led_classdev wrap_error_led = { .name = "wrap::error", .brightness_set = wrap_error_led_set, @@ -56,6 +71,7 @@ static struct led_classdev wrap_extra_led = { static int wrap_led_suspend(struct platform_device *dev, pm_message_t state) { + led_classdev_suspend(&wrap_power_led); led_classdev_suspend(&wrap_error_led); led_classdev_suspend(&wrap_extra_led); return 0; @@ -63,6 +79,7 @@ static int wrap_led_suspend(struct platform_device *dev, static int wrap_led_resume(struct platform_device *dev) { + led_classdev_resume(&wrap_power_led); led_classdev_resume(&wrap_error_led); led_classdev_resume(&wrap_extra_led); return 0; @@ -76,17 +93,31 @@ 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) { - ret = led_classdev_register(&pdev->dev, &wrap_extra_led); - if (ret < 0) - led_classdev_unregister(&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; -- cgit v1.2.3-70-g09d2 From 8fe217e7b6d4f186e269fda8aba73a213fad0fa0 Mon Sep 17 00:00:00 2001 From: Rod Whitby Date: Thu, 7 Feb 2008 10:02:39 +0000 Subject: leds: Remove the now uneeded ixp4xx driver All boards using the IXP4XX-GPIO-LED driver have been updated to use the generic leds-gpio driver instead. Signed-off-by: Rod Whitby Signed-off-by: Richard Purdie --- drivers/leds/Kconfig | 9 -- drivers/leds/Makefile | 1 - drivers/leds/leds-ixp4xx-gpio.c | 214 ---------------------------------------- 3 files changed, 224 deletions(-) delete mode 100644 drivers/leds/leds-ixp4xx-gpio.c (limited to 'drivers') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 922c3df548a..1dfe5bbc200 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -39,15 +39,6 @@ config LEDS_SPITZ This option enables support for the LEDs on Sharp Zaurus SL-Cxx00 series (C1000, C3000, C3100). -config LEDS_IXP4XX - tristate "LED Support for GPIO connected LEDs on IXP4XX processors" - depends on LEDS_CLASS && ARCH_IXP4XX - help - This option enables support for the LEDs connected to GPIO - outputs of the Intel IXP4XX processors. To be useful the - particular board must have LEDs and they must be connected - to the GPIO lines. If unsure, say Y. - config LEDS_TOSA tristate "LED Support for the Sharp SL-6000 series" depends on LEDS_CLASS && PXA_SHARPSL diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index e433e2f4e52..1ec45552b03 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -8,7 +8,6 @@ obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o obj-$(CONFIG_LEDS_CORGI) += leds-corgi.o obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o obj-$(CONFIG_LEDS_SPITZ) += leds-spitz.o -obj-$(CONFIG_LEDS_IXP4XX) += leds-ixp4xx-gpio.o obj-$(CONFIG_LEDS_TOSA) += leds-tosa.o obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o diff --git a/drivers/leds/leds-ixp4xx-gpio.c b/drivers/leds/leds-ixp4xx-gpio.c deleted file mode 100644 index 7dcf0b92c46..00000000000 --- a/drivers/leds/leds-ixp4xx-gpio.c +++ /dev/null @@ -1,214 +0,0 @@ -/* - * IXP4XX GPIO driver LED driver - * - * Author: John Bowler - * - * Copyright (c) 2006 John Bowler - * - * Permission is hereby granted, free of charge, to any - * person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the - * Software without restriction, including without - * limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the - * following conditions: - * - * The above copyright notice and this permission notice - * shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF - * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT - * SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#include -#include -#include -#include -#include -#include - -extern spinlock_t gpio_lock; - -/* Up to 16 gpio lines are possible. */ -#define GPIO_MAX 16 -static struct ixp4xxgpioled_device { - struct led_classdev ancestor; - int flags; -} ixp4xxgpioled_devices[GPIO_MAX]; - -void ixp4xxgpioled_brightness_set(struct led_classdev *pled, - enum led_brightness value) -{ - const struct ixp4xxgpioled_device *const ixp4xx_dev = - container_of(pled, struct ixp4xxgpioled_device, ancestor); - const u32 gpio_pin = ixp4xx_dev - ixp4xxgpioled_devices; - - if (gpio_pin < GPIO_MAX && ixp4xx_dev->ancestor.name != 0) { - /* Set or clear the 'gpio_pin' bit according to the style - * and the required setting (value > 0 == on) - */ - const int gpio_value = - (value > 0) == (ixp4xx_dev->flags != IXP4XX_GPIO_LOW) ? - IXP4XX_GPIO_HIGH : IXP4XX_GPIO_LOW; - - { - unsigned long flags; - spin_lock_irqsave(&gpio_lock, flags); - gpio_line_set(gpio_pin, gpio_value); - spin_unlock_irqrestore(&gpio_lock, flags); - } - } -} - -/* LEDs are described in resources, the following iterates over the valid - * LED resources. - */ -#define for_all_leds(i, pdev) \ - for (i=0; inum_resources; ++i) \ - if (pdev->resource[i].start < GPIO_MAX && \ - pdev->resource[i].name != 0) - -/* The following applies 'operation' to each LED from the given platform, - * the function always returns 0 to allow tail call elimination. - */ -static int apply_to_all_leds(struct platform_device *pdev, - void (*operation)(struct led_classdev *pled)) -{ - int i; - - for_all_leds(i, pdev) - operation(&ixp4xxgpioled_devices[pdev->resource[i].start].ancestor); - return 0; -} - -#ifdef CONFIG_PM -static int ixp4xxgpioled_suspend(struct platform_device *pdev, - pm_message_t state) -{ - return apply_to_all_leds(pdev, led_classdev_suspend); -} - -static int ixp4xxgpioled_resume(struct platform_device *pdev) -{ - return apply_to_all_leds(pdev, led_classdev_resume); -} -#endif - -static void ixp4xxgpioled_remove_one_led(struct led_classdev *pled) -{ - led_classdev_unregister(pled); - pled->name = 0; -} - -static int ixp4xxgpioled_remove(struct platform_device *pdev) -{ - return apply_to_all_leds(pdev, ixp4xxgpioled_remove_one_led); -} - -static int ixp4xxgpioled_probe(struct platform_device *pdev) -{ - /* The board level has to tell the driver where the - * LEDs are connected - there is no way to find out - * electrically. It must also say whether the GPIO - * lines are active high or active low. - * - * To do this read the num_resources (the number of - * LEDs) and the struct resource (the data for each - * LED). The name comes from the resource, and it - * isn't copied. - */ - int i; - - for_all_leds(i, pdev) { - const u8 gpio_pin = pdev->resource[i].start; - int rc; - - if (ixp4xxgpioled_devices[gpio_pin].ancestor.name == 0) { - unsigned long flags; - - spin_lock_irqsave(&gpio_lock, flags); - gpio_line_config(gpio_pin, IXP4XX_GPIO_OUT); - /* The config can, apparently, reset the state, - * I suspect the gpio line may be an input and - * the config may cause the line to be latched, - * so the setting depends on how the LED is - * connected to the line (which affects how it - * floats if not driven). - */ - gpio_line_set(gpio_pin, IXP4XX_GPIO_HIGH); - spin_unlock_irqrestore(&gpio_lock, flags); - - ixp4xxgpioled_devices[gpio_pin].flags = - pdev->resource[i].flags & IORESOURCE_BITS; - - ixp4xxgpioled_devices[gpio_pin].ancestor.name = - pdev->resource[i].name; - - /* This is how a board manufacturer makes the LED - * come on on reset - the GPIO line will be high, so - * make the LED light when the line is low... - */ - if (ixp4xxgpioled_devices[gpio_pin].flags != IXP4XX_GPIO_LOW) - ixp4xxgpioled_devices[gpio_pin].ancestor.brightness = 100; - else - ixp4xxgpioled_devices[gpio_pin].ancestor.brightness = 0; - - ixp4xxgpioled_devices[gpio_pin].ancestor.flags = 0; - - ixp4xxgpioled_devices[gpio_pin].ancestor.brightness_set = - ixp4xxgpioled_brightness_set; - - ixp4xxgpioled_devices[gpio_pin].ancestor.default_trigger = 0; - } - - rc = led_classdev_register(&pdev->dev, - &ixp4xxgpioled_devices[gpio_pin].ancestor); - if (rc < 0) { - ixp4xxgpioled_devices[gpio_pin].ancestor.name = 0; - ixp4xxgpioled_remove(pdev); - return rc; - } - } - - return 0; -} - -static struct platform_driver ixp4xxgpioled_driver = { - .probe = ixp4xxgpioled_probe, - .remove = ixp4xxgpioled_remove, -#ifdef CONFIG_PM - .suspend = ixp4xxgpioled_suspend, - .resume = ixp4xxgpioled_resume, -#endif - .driver = { - .name = "IXP4XX-GPIO-LED", - }, -}; - -static int __init ixp4xxgpioled_init(void) -{ - return platform_driver_register(&ixp4xxgpioled_driver); -} - -static void __exit ixp4xxgpioled_exit(void) -{ - platform_driver_unregister(&ixp4xxgpioled_driver); -} - -module_init(ixp4xxgpioled_init); -module_exit(ixp4xxgpioled_exit); - -MODULE_AUTHOR("John Bowler "); -MODULE_DESCRIPTION("IXP4XX GPIO LED driver"); -MODULE_LICENSE("Dual MIT/GPL"); -- cgit v1.2.3-70-g09d2 From d39a7a63eb3971b1b3cc5c181ed526bf437b1c72 Mon Sep 17 00:00:00 2001 From: Kristoffer Ericson Date: Thu, 7 Feb 2008 10:10:28 +0000 Subject: leds: Add HP Jornada 6xx driver Add support for the LEDs on the HP Jornada 620/660/680/690 devices. Signed-off-by: Kristoffer Ericson Signed-off-by: Richard Purdie --- drivers/leds/Kconfig | 7 +++ drivers/leds/Makefile | 1 + drivers/leds/leds-hp6xx.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 drivers/leds/leds-hp6xx.c (limited to 'drivers') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 1dfe5bbc200..851a3b01781 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -91,6 +91,13 @@ config LEDS_COBALT_RAQ help This option enables support for the Cobalt Raq series LEDs. +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_GPIO tristate "LED Support for GPIO connected LEDs" depends on LEDS_CLASS && GENERIC_GPIO diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 1ec45552b03..bc6afc8dcb2 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o obj-$(CONFIG_LEDS_CM_X270) += leds-cm-x270.o obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o +obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o # LED Triggers obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o diff --git a/drivers/leds/leds-hp6xx.c b/drivers/leds/leds-hp6xx.c new file mode 100644 index 00000000000..82d4ec38479 --- /dev/null +++ b/drivers/leds/leds-hp6xx.c @@ -0,0 +1,120 @@ +/* + * LED Triggers Core + * For the HP Jornada 620/660/680/690 handhelds + * + * Copyright 2008 Kristoffer Ericson + * 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 +#include +#include +#include +#include +#include + +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, +}; + +static struct led_classdev hp6xx_green_led = { + .name = "hp6xx:green", + .default_trigger = "ide-disk", + .brightness_set = hp6xxled_green_set, +}; + +#ifdef CONFIG_PM +static int hp6xxled_suspend(struct platform_device *dev, pm_message_t state) +{ + led_classdev_suspend(&hp6xx_red_led); + led_classdev_suspend(&hp6xx_green_led); + return 0; +} + +static int hp6xxled_resume(struct platform_device *dev) +{ + led_classdev_resume(&hp6xx_red_led); + led_classdev_resume(&hp6xx_green_led); + return 0; +} +#endif + +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; +} + +static struct platform_driver hp6xxled_driver = { + .probe = hp6xxled_probe, + .remove = hp6xxled_remove, +#ifdef CONFIG_PM + .suspend = hp6xxled_suspend, + .resume = hp6xxled_resume, +#endif + .driver = { + .name = "hp6xx-led", + }, +}; + +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 "); +MODULE_DESCRIPTION("HP Jornada 6xx LED driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2