diff options
Diffstat (limited to 'drivers/leds')
27 files changed, 612 insertions, 72 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 8a0e1ec95e4a..505eb64c329c 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -15,77 +15,86 @@ config LEDS_CLASS 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. +if LEDS_CLASS + comment "LED drivers" +config LEDS_88PM860X + tristate "LED Support for Marvell 88PM860x PMIC" + depends on LEDS_CLASS && MFD_88PM860X + help + This option enables support for on-chip LED drivers found on Marvell + Semiconductor 88PM8606 PMIC. + config LEDS_ATMEL_PWM tristate "LED Support using Atmel PWM outputs" - depends on LEDS_CLASS && ATMEL_PWM + depends on 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 + depends on 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 + depends on 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 + depends on 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 + depends on 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 + depends on 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 + depends on 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 + depends on 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 + depends on X86 && !GPIO_CS5535 && !CS5535_GPIO 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 + depends on 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 + depends on MIPS_COBALT help This option enables support for the front LED on Cobalt Qube series @@ -98,7 +107,7 @@ config LEDS_COBALT_RAQ config LEDS_SUNFIRE tristate "LED support for SunFire servers." - depends on LEDS_CLASS && SPARC64 + depends on SPARC64 select LEDS_TRIGGERS help This option enables support for the Left, Middle, and Right @@ -106,14 +115,14 @@ config LEDS_SUNFIRE config LEDS_HP6XX tristate "LED Support for the HP Jornada 6xx" - depends on LEDS_CLASS && SH_HP6XX + depends on 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 + depends on I2C && INPUT && EXPERIMENTAL help This option enables support for NXP pca9532 LED controller. It is generally only useful @@ -121,7 +130,7 @@ config LEDS_PCA9532 config LEDS_GPIO tristate "LED Support for GPIO connected LEDs" - depends on LEDS_CLASS && GENERIC_GPIO + depends on GENERIC_GPIO help This option enables support for the LEDs connected to GPIO outputs. To be useful the particular board must have LEDs @@ -148,7 +157,7 @@ config LEDS_GPIO_OF config LEDS_LP3944 tristate "LED Support for N.S. LP3944 (Fun Light) I2C chip" - depends on LEDS_CLASS && I2C + depends on I2C help This option enables support for LEDs connected to the National Semiconductor LP3944 Lighting Management Unit (LMU) also known as @@ -159,7 +168,7 @@ config LEDS_LP3944 config LEDS_CLEVO_MAIL tristate "Mail LED on Clevo notebook" - depends on LEDS_CLASS && X86 && SERIO_I8042 && DMI + depends on X86 && SERIO_I8042 && DMI help This driver makes the mail LED accessible from userspace programs through the leds subsystem. This LED have three @@ -189,7 +198,7 @@ config LEDS_CLEVO_MAIL config LEDS_PCA955X tristate "LED Support for PCA955x I2C chips" - depends on LEDS_CLASS && I2C + depends on I2C help This option enables support for LEDs connected to PCA955x LED driver chips accessed via the I2C bus. Supported @@ -197,54 +206,54 @@ config LEDS_PCA955X config LEDS_WM831X_STATUS tristate "LED support for status LEDs on WM831x PMICs" - depends on LEDS_CLASS && MFD_WM831X + depends on 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 + depends on 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 + depends on 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 + depends on 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 + depends on HAVE_PWM help This option enables support for pwm driven LEDs config LEDS_REGULATOR tristate "REGULATOR driven LED support" - depends on LEDS_CLASS && REGULATOR + depends on 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 + depends on 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 + depends on PCI && DMI help This option enables support for the Intel SS4200 series of Network Attached Storage servers. You may control the hard @@ -253,7 +262,7 @@ config LEDS_INTEL_SS4200 config LEDS_LT3593 tristate "LED driver for LT3593 controllers" - depends on LEDS_CLASS && GENERIC_GPIO + depends on GENERIC_GPIO help This option enables support for LEDs driven by a Linear Technology LT3593 controller. This controller uses a special one-wire pulse @@ -261,7 +270,7 @@ config LEDS_LT3593 config LEDS_ADP5520 tristate "LED Support for ADP5520/ADP5501 PMIC" - depends on LEDS_CLASS && PMIC_ADP5520 + depends on PMIC_ADP5520 help This option enables support for on-chip LED drivers found on Analog Devices ADP5520/ADP5501 PMICs. @@ -269,7 +278,12 @@ config LEDS_ADP5520 To compile this driver as a module, choose M here: the module will be called leds-adp5520. -comment "LED Triggers" +config LEDS_DELL_NETBOOKS + tristate "External LED on Dell Business Netbooks" + depends on X86 && ACPI_WMI + help + This adds support for the Latitude 2100 and similar + notebooks that have an external LED. config LEDS_TRIGGERS bool "LED Trigger support" @@ -278,9 +292,12 @@ config LEDS_TRIGGERS These triggers allow kernel events to drive the LEDs and can be configured via sysfs. If unsure, say Y. +if LEDS_TRIGGERS + +comment "LED Triggers" + 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 @@ -291,14 +308,13 @@ config LEDS_TRIGGER_TIMER config LEDS_TRIGGER_IDE_DISK bool "LED IDE Disk Trigger" - depends on LEDS_TRIGGERS && IDE_GD_ATA + depends on 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 @@ -307,7 +323,6 @@ config LEDS_TRIGGER_HEARTBEAT 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. @@ -316,7 +331,6 @@ config LEDS_TRIGGER_BACKLIGHT 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 @@ -329,7 +343,6 @@ config LEDS_TRIGGER_GPIO 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. @@ -337,4 +350,8 @@ config LEDS_TRIGGER_DEFAULT_ON comment "iptables trigger is under Netfilter config (LED target)" depends on LEDS_TRIGGERS +endif # LEDS_TRIGGERS + +endif # LEDS_CLASS + endif # NEW_LEDS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 9e63869d7c0d..0cd8b9957380 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_LEDS_CLASS) += led-class.o obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o # LED Platform Drivers +obj-$(CONFIG_LEDS_88PM860X) += leds-88pm860x.o obj-$(CONFIG_LEDS_ATMEL_PWM) += leds-atmel-pwm.o obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o @@ -33,6 +34,7 @@ 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 +obj-$(CONFIG_LEDS_DELL_NETBOOKS) += dell-led.o # LED SPI Drivers obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o diff --git a/drivers/leds/dell-led.c b/drivers/leds/dell-led.c new file mode 100644 index 000000000000..52590296af33 --- /dev/null +++ b/drivers/leds/dell-led.c @@ -0,0 +1,201 @@ +/* + * dell_led.c - Dell LED Driver + * + * Copyright (C) 2010 Dell Inc. + * Louis Davis <louis_davis@dell.com> + * Jim Dailey <jim_dailey@dell.com> + * + * 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. + * + */ + +#include <linux/acpi.h> +#include <linux/leds.h> +#include <linux/slab.h> + +MODULE_AUTHOR("Louis Davis/Jim Dailey"); +MODULE_DESCRIPTION("Dell LED Control Driver"); +MODULE_LICENSE("GPL"); + +#define DELL_LED_BIOS_GUID "F6E4FE6E-909D-47cb-8BAB-C9F6F2F8D396" +MODULE_ALIAS("wmi:" DELL_LED_BIOS_GUID); + +/* Error Result Codes: */ +#define INVALID_DEVICE_ID 250 +#define INVALID_PARAMETER 251 +#define INVALID_BUFFER 252 +#define INTERFACE_ERROR 253 +#define UNSUPPORTED_COMMAND 254 +#define UNSPECIFIED_ERROR 255 + +/* Device ID */ +#define DEVICE_ID_PANEL_BACK 1 + +/* LED Commands */ +#define CMD_LED_ON 16 +#define CMD_LED_OFF 17 +#define CMD_LED_BLINK 18 + +struct bios_args { + unsigned char length; + unsigned char result_code; + unsigned char device_id; + unsigned char command; + unsigned char on_time; + unsigned char off_time; +}; + +static int dell_led_perform_fn(u8 length, + u8 result_code, + u8 device_id, + u8 command, + u8 on_time, + u8 off_time) +{ + struct bios_args *bios_return; + u8 return_code; + union acpi_object *obj; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_buffer input; + acpi_status status; + + struct bios_args args; + args.length = length; + args.result_code = result_code; + args.device_id = device_id; + args.command = command; + args.on_time = on_time; + args.off_time = off_time; + + input.length = sizeof(struct bios_args); + input.pointer = &args; + + status = wmi_evaluate_method(DELL_LED_BIOS_GUID, + 1, + 1, + &input, + &output); + + if (ACPI_FAILURE(status)) + return status; + + obj = output.pointer; + + if (!obj) + return -EINVAL; + else if (obj->type != ACPI_TYPE_BUFFER) { + kfree(obj); + return -EINVAL; + } + + bios_return = ((struct bios_args *)obj->buffer.pointer); + return_code = bios_return->result_code; + + kfree(obj); + + return return_code; +} + +static int led_on(void) +{ + return dell_led_perform_fn(3, /* Length of command */ + INTERFACE_ERROR, /* Init to INTERFACE_ERROR */ + DEVICE_ID_PANEL_BACK, /* Device ID */ + CMD_LED_ON, /* Command */ + 0, /* not used */ + 0); /* not used */ +} + +static int led_off(void) +{ + return dell_led_perform_fn(3, /* Length of command */ + INTERFACE_ERROR, /* Init to INTERFACE_ERROR */ + DEVICE_ID_PANEL_BACK, /* Device ID */ + CMD_LED_OFF, /* Command */ + 0, /* not used */ + 0); /* not used */ +} + +static int led_blink(unsigned char on_eighths, + unsigned char off_eighths) +{ + return dell_led_perform_fn(5, /* Length of command */ + INTERFACE_ERROR, /* Init to INTERFACE_ERROR */ + DEVICE_ID_PANEL_BACK, /* Device ID */ + CMD_LED_BLINK, /* Command */ + on_eighths, /* blink on in eigths of a second */ + off_eighths); /* blink off in eights of a second */ +} + +static void dell_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + if (value == LED_OFF) + led_off(); + else + led_on(); +} + +static int dell_led_blink(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + unsigned long on_eighths; + unsigned long off_eighths; + + /* The Dell LED delay is based on 125ms intervals. + Need to round up to next interval. */ + + on_eighths = (*delay_on + 124) / 125; + if (0 == on_eighths) + on_eighths = 1; + if (on_eighths > 255) + on_eighths = 255; + *delay_on = on_eighths * 125; + + off_eighths = (*delay_off + 124) / 125; + if (0 == off_eighths) + off_eighths = 1; + if (off_eighths > 255) + off_eighths = 255; + *delay_off = off_eighths * 125; + + led_blink(on_eighths, off_eighths); + + return 0; +} + +static struct led_classdev dell_led = { + .name = "dell::lid", + .brightness = LED_OFF, + .max_brightness = 1, + .brightness_set = dell_led_set, + .blink_set = dell_led_blink, + .flags = LED_CORE_SUSPENDRESUME, +}; + +static int __init dell_led_init(void) +{ + int error = 0; + + if (!wmi_has_guid(DELL_LED_BIOS_GUID)) + return -ENODEV; + + error = led_off(); + if (error != 0) + return -ENODEV; + + return led_classdev_register(NULL, &dell_led); +} + +static void __exit dell_led_exit(void) +{ + led_classdev_unregister(&dell_led); + + led_off(); +} + +module_init(dell_led_init); +module_exit(dell_led_exit); diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 782f95822eab..69e7d86a5143 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -72,11 +72,14 @@ static ssize_t led_max_brightness_show(struct device *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); +static struct device_attribute led_class_attrs[] = { + __ATTR(brightness, 0644, led_brightness_show, led_brightness_store), + __ATTR(max_brightness, 0644, led_max_brightness_show, NULL), #ifdef CONFIG_LEDS_TRIGGERS -static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store); + __ATTR(trigger, 0644, led_trigger_show, led_trigger_store), #endif + __ATTR_NULL, +}; /** * led_classdev_suspend - suspend an led_classdev. @@ -127,18 +130,11 @@ static int led_resume(struct device *dev) */ 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 @@ -150,36 +146,18 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) 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", + printk(KERN_DEBUG "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); /** @@ -190,10 +168,7 @@ EXPORT_SYMBOL_GPL(led_classdev_register); */ 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); @@ -215,6 +190,7 @@ static int __init leds_init(void) return PTR_ERR(leds_class); leds_class->suspend = led_suspend; leds_class->resume = led_resume; + leds_class->dev_attrs = led_class_attrs; return 0; } diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index d8ddd9ef8994..f1c00db88b5e 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -21,6 +21,7 @@ #include <linux/timer.h> #include <linux/rwsem.h> #include <linux/leds.h> +#include <linux/slab.h> #include "leds.h" /* diff --git a/drivers/leds/leds-88pm860x.c b/drivers/leds/leds-88pm860x.c new file mode 100644 index 000000000000..16a60c06c96c --- /dev/null +++ b/drivers/leds/leds-88pm860x.c @@ -0,0 +1,326 @@ +/* + * LED driver for Marvell 88PM860x + * + * Copyright (C) 2009 Marvell International Ltd. + * Haojian Zhuang <haojian.zhuang@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/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/i2c.h> +#include <linux/leds.h> +#include <linux/slab.h> +#include <linux/workqueue.h> +#include <linux/mfd/88pm860x.h> + +#define LED_PWM_SHIFT (3) +#define LED_PWM_MASK (0x1F) +#define LED_CURRENT_MASK (0x07 << 5) + +#define LED_BLINK_ON_MASK (0x07) +#define LED_BLINK_PERIOD_MASK (0x0F << 3) +#define LED_BLINK_MASK (0x7F) + +#define LED_BLINK_ON(x) ((x & 0x7) * 66 + 66) +#define LED_BLINK_PERIOD(x) ((x & 0xF) * 530 + 930) +#define LED_BLINK_ON_MIN LED_BLINK_ON(0) +#define LED_BLINK_ON_MAX LED_BLINK_ON(0x7) +#define LED_BLINK_PERIOD_MIN LED_BLINK_PERIOD(0) +#define LED_BLINK_PERIOD_MAX LED_BLINK_PERIOD(0xE) +#define LED_TO_ON(x) ((x - 66) / 66) +#define LED_TO_PERIOD(x) ((x - 930) / 530) + +#define LED1_BLINK_EN (1 << 1) +#define LED2_BLINK_EN (1 << 2) + +enum { + SET_BRIGHTNESS, + SET_BLINK, +}; + +struct pm860x_led { + struct led_classdev cdev; + struct i2c_client *i2c; + struct work_struct work; + struct pm860x_chip *chip; + struct mutex lock; + char name[MFD_NAME_SIZE]; + + int port; + int iset; + int command; + int offset; + unsigned char brightness; + unsigned char current_brightness; + + int blink_data; + int blink_time; + int blink_on; + int blink_off; +}; + +/* return offset of color register */ +static inline int __led_off(int port) +{ + int ret = -EINVAL; + + switch (port) { + case PM8606_LED1_RED: + case PM8606_LED1_GREEN: + case PM8606_LED1_BLUE: + ret = port - PM8606_LED1_RED + PM8606_RGB1B; + break; + case PM8606_LED2_RED: + case PM8606_LED2_GREEN: + case PM8606_LED2_BLUE: + ret = port - PM8606_LED2_RED + PM8606_RGB2B; + break; + } + return ret; +} + +/* return offset of blink register */ +static inline int __blink_off(int port) +{ + int ret = -EINVAL; + + switch (port) { + case PM8606_LED1_RED: + case PM8606_LED1_GREEN: + case PM8606_LED1_BLUE: + ret = PM8606_RGB1A; + case PM8606_LED2_RED: + case PM8606_LED2_GREEN: + case PM8606_LED2_BLUE: + ret = PM8606_RGB2A; + } + return ret; +} + +static inline int __blink_ctl_mask(int port) +{ + int ret = -EINVAL; + + switch (port) { + case PM8606_LED1_RED: + case PM8606_LED1_GREEN: + case PM8606_LED1_BLUE: + ret = LED1_BLINK_EN; + break; + case PM8606_LED2_RED: + case PM8606_LED2_GREEN: + case PM8606_LED2_BLUE: + ret = LED2_BLINK_EN; + break; + } + return ret; +} + +static int __led_set(struct pm860x_led *led, int command) +{ + struct pm860x_chip *chip = led->chip; + int mask, ret; + + mutex_lock(&led->lock); + switch (command) { + case SET_BRIGHTNESS: + if ((led->current_brightness == 0) && led->brightness) { + if (led->iset) { + ret = pm860x_set_bits(led->i2c, led->offset, + LED_CURRENT_MASK, led->iset); + if (ret < 0) + goto out; + } + } else if (led->brightness == 0) { + ret = pm860x_set_bits(led->i2c, led->offset, + LED_CURRENT_MASK, 0); + if (ret < 0) + goto out; + } + ret = pm860x_set_bits(led->i2c, led->offset, LED_PWM_MASK, + led->brightness); + if (ret < 0) + goto out; + led->current_brightness = led->brightness; + dev_dbg(chip->dev, "Update LED. (reg:%d, brightness:%d)\n", + led->offset, led->brightness); + break; + case SET_BLINK: + ret = pm860x_set_bits(led->i2c, led->offset, + LED_BLINK_MASK, led->blink_data); + if (ret < 0) + goto out; + + mask = __blink_ctl_mask(led->port); + ret = pm860x_set_bits(led->i2c, PM8606_WLED3B, mask, mask); + if (ret < 0) + goto out; + dev_dbg(chip->dev, "LED blink delay on:%dms, delay off:%dms\n", + led->blink_on, led->blink_off); + break; + } +out: + mutex_unlock(&led->lock); + return 0; +} + +static void pm860x_led_work(struct work_struct *work) +{ + struct pm860x_led *led; + + led = container_of(work, struct pm860x_led, work); + __led_set(led, led->command); +} + +static void pm860x_led_set(struct led_classdev *cdev, + enum led_brightness value) +{ + struct pm860x_led *data = container_of(cdev, struct pm860x_led, cdev); + + data->offset = __led_off(data->port); + data->brightness = value >> 3; + data->command = SET_BRIGHTNESS; + schedule_work(&data->work); +} + +static int pm860x_led_blink(struct led_classdev *cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct pm860x_led *data = container_of(cdev, struct pm860x_led, cdev); + int period, on; + + on = *delay_on; + if ((on < LED_BLINK_ON_MIN) || (on > LED_BLINK_ON_MAX)) + return -EINVAL; + + on = LED_TO_ON(on); + on = LED_BLINK_ON(on); + + period = on + *delay_off; + if ((period < LED_BLINK_PERIOD_MIN) || (period > LED_BLINK_PERIOD_MAX)) + return -EINVAL; + period = LED_TO_PERIOD(period); + period = LED_BLINK_PERIOD(period); + + data->offset = __blink_off(data->port); + data->blink_on = on; + data->blink_off = period - data->blink_on; + data->blink_data = (period << 3) | data->blink_on; + data->command = SET_BLINK; + schedule_work(&data->work); + + return 0; +} + +static int __check_device(struct pm860x_led_pdata *pdata, char *name) +{ + struct pm860x_led_pdata *p = pdata; + int ret = -EINVAL; + + while (p && p->id) { + if ((p->id != PM8606_ID_LED) || (p->flags < 0)) + break; + + if (!strncmp(name, pm860x_led_name[p->flags], + MFD_NAME_SIZE)) { + ret = (int)p->flags; + break; + } + p++; + } + return ret; +} + +static int pm860x_led_probe(struct platform_device *pdev) +{ + struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct pm860x_platform_data *pm860x_pdata; + struct pm860x_led_pdata *pdata; + struct pm860x_led *data; + struct resource *res; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (res == NULL) { + dev_err(&pdev->dev, "No I/O resource!\n"); + return -EINVAL; + } + + if (pdev->dev.parent->platform_data) { + pm860x_pdata = pdev->dev.parent->platform_data; + pdata = pm860x_pdata->led; + } else + pdata = NULL; + + data = kzalloc(sizeof(struct pm860x_led), GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + strncpy(data->name, res->name, MFD_NAME_SIZE); + dev_set_drvdata(&pdev->dev, data); + data->chip = chip; + data->i2c = (chip->id == CHIP_PM8606) ? chip->client : chip->companion; + data->iset = pdata->iset; + data->port = __check_device(pdata, data->name); + if (data->port < 0) + return -EINVAL; + + data->current_brightness = 0; + data->cdev.name = data->name; + data->cdev.brightness_set = pm860x_led_set; + data->cdev.blink_set = pm860x_led_blink; + mutex_init(&data->lock); + INIT_WORK(&data->work, pm860x_led_work); + + ret = led_classdev_register(chip->dev, &data->cdev); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register LED: %d\n", ret); + goto out; + } + return 0; +out: + kfree(data); + return ret; +} + +static int pm860x_led_remove(struct platform_device *pdev) +{ + struct pm860x_led *data = platform_get_drvdata(pdev); + + led_classdev_unregister(&data->cdev); + kfree(data); + + return 0; +} + +static struct platform_driver pm860x_led_driver = { + .driver = { + .name = "88pm860x-led", + .owner = THIS_MODULE, + }, + .probe = pm860x_led_probe, + .remove = pm860x_led_remove, +}; + +static int __devinit pm860x_led_init(void) +{ + return platform_driver_register(&pm860x_led_driver); +} +module_init(pm860x_led_init); + +static void __devexit pm860x_led_exit(void) +{ + platform_driver_unregister(&pm860x_led_driver); +} +module_exit(pm860x_led_exit); + +MODULE_DESCRIPTION("LED driver for Marvell PM860x"); +MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:88pm860x-led"); diff --git a/drivers/leds/leds-adp5520.c b/drivers/leds/leds-adp5520.c index a8f315902131..7ba4c7b5b97e 100644 --- a/drivers/leds/leds-adp5520.c +++ b/drivers/leds/leds-adp5520.c @@ -20,6 +20,7 @@ #include <linux/leds.h> #include <linux/workqueue.h> #include <linux/mfd/adp5520.h> +#include <linux/slab.h> struct adp5520_led { struct led_classdev cdev; diff --git a/drivers/leds/leds-atmel-pwm.c b/drivers/leds/leds-atmel-pwm.c index 52297c3ab246..c941d906bba6 100644 --- a/drivers/leds/leds-atmel-pwm.c +++ b/drivers/leds/leds-atmel-pwm.c @@ -3,6 +3,7 @@ #include <linux/leds.h> #include <linux/io.h> #include <linux/atmel_pwm.h> +#include <linux/slab.h> struct pwmled { diff --git a/drivers/leds/leds-bd2802.c b/drivers/leds/leds-bd2802.c index 779d7f262c04..286b501a3573 100644 --- a/drivers/leds/leds-bd2802.c +++ b/drivers/leds/leds-bd2802.c @@ -18,6 +18,7 @@ #include <linux/delay.h> #include <linux/leds.h> #include <linux/leds-bd2802.h> +#include <linux/slab.h> #define LED_CTL(rgb2en, rgb1en) ((rgb2en) << 4 | ((rgb1en) << 0)) diff --git a/drivers/leds/leds-da903x.c b/drivers/leds/leds-da903x.c index 1f3cc512eff8..f28931cf6781 100644 --- a/drivers/leds/leds-da903x.c +++ b/drivers/leds/leds-da903x.c @@ -19,6 +19,7 @@ #include <linux/leds.h> #include <linux/workqueue.h> #include <linux/mfd/da903x.h> +#include <linux/slab.h> #define DA9030_LED1_CONTROL 0x20 #define DA9030_LED2_CONTROL 0x21 diff --git a/drivers/leds/leds-dac124s085.c b/drivers/leds/leds-dac124s085.c index 2913d76ad3d2..31cf0d60a9a5 100644 --- a/drivers/leds/leds-dac124s085.c +++ b/drivers/leds/leds-dac124s085.c @@ -9,7 +9,6 @@ * LED driver for the DAC124S085 SPI DAC */ -#include <linux/gfp.h> #include <linux/leds.h> #include <linux/module.h> #include <linux/mutex.h> diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index e5225d28f392..c6e4b772b757 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -14,6 +14,7 @@ #include <linux/init.h> #include <linux/platform_device.h> #include <linux/leds.h> +#include <linux/slab.h> #include <linux/workqueue.h> #include <asm/gpio.h> @@ -211,7 +212,6 @@ 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 led; struct gpio_led_of_platform_data *pdata; int count = 0, ret; @@ -226,8 +226,8 @@ static int __devinit of_gpio_leds_probe(struct of_device *ofdev, if (!pdata) return -ENOMEM; - memset(&led, 0, sizeof(led)); for_each_child_of_node(np, child) { + struct gpio_led led = {}; enum of_gpio_flags flags; const char *state; diff --git a/drivers/leds/leds-lp3944.c b/drivers/leds/leds-lp3944.c index 5946208ba26e..8d5ecceba181 100644 --- a/drivers/leds/leds-lp3944.c +++ b/drivers/leds/leds-lp3944.c @@ -28,6 +28,7 @@ #include <linux/module.h> #include <linux/i2c.h> +#include <linux/slab.h> #include <linux/leds.h> #include <linux/mutex.h> #include <linux/workqueue.h> diff --git a/drivers/leds/leds-lt3593.c b/drivers/leds/leds-lt3593.c index fee40a841959..2579678f97a6 100644 --- a/drivers/leds/leds-lt3593.c +++ b/drivers/leds/leds-lt3593.c @@ -23,6 +23,7 @@ #include <linux/workqueue.h> #include <linux/delay.h> #include <linux/gpio.h> +#include <linux/slab.h> struct lt3593_led_data { struct led_classdev cdev; diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c index adc561eb59d2..6682175fa9f7 100644 --- a/drivers/leds/leds-pca9532.c +++ b/drivers/leds/leds-pca9532.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/i2c.h> +#include <linux/slab.h> #include <linux/leds.h> #include <linux/input.h> #include <linux/mutex.h> diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c index 4e2d1a42b48f..8ff50f234190 100644 --- a/drivers/leds/leds-pca955x.c +++ b/drivers/leds/leds-pca955x.c @@ -48,6 +48,7 @@ #include <linux/err.h> #include <linux/i2c.h> #include <linux/workqueue.h> +#include <linux/slab.h> /* LED select registers determine the source that drives LED outputs */ #define PCA955X_LS_LED_ON 0x0 /* Output LOW */ diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index 88b1dd091cfb..da3fa8dcdf5b 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c @@ -21,6 +21,7 @@ #include <linux/err.h> #include <linux/pwm.h> #include <linux/leds_pwm.h> +#include <linux/slab.h> struct led_pwm_data { struct led_classdev cdev; diff --git a/drivers/leds/leds-regulator.c b/drivers/leds/leds-regulator.c index 7f00de3ef922..3790816643be 100644 --- a/drivers/leds/leds-regulator.c +++ b/drivers/leds/leds-regulator.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/err.h> +#include <linux/slab.h> #include <linux/workqueue.h> #include <linux/leds.h> #include <linux/leds-regulator.h> diff --git a/drivers/leds/leds-s3c24xx.c b/drivers/leds/leds-s3c24xx.c index aa7acf3b9224..a77771dc2e95 100644 --- a/drivers/leds/leds-s3c24xx.c +++ b/drivers/leds/leds-s3c24xx.c @@ -16,6 +16,7 @@ #include <linux/platform_device.h> #include <linux/leds.h> #include <linux/gpio.h> +#include <linux/slab.h> #include <mach/hardware.h> #include <mach/regs-gpio.h> diff --git a/drivers/leds/leds-ss4200.c b/drivers/leds/leds-ss4200.c index 97f04984c1ca..51477ec71391 100644 --- a/drivers/leds/leds-ss4200.c +++ b/drivers/leds/leds-ss4200.c @@ -63,7 +63,7 @@ MODULE_LICENSE("GPL"); /* * PCI ID of the Intel ICH7 LPC Device within which the GPIO block lives. */ -static struct pci_device_id ich7_lpc_pci_id[] = +static const 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) }, diff --git a/drivers/leds/leds-sunfire.c b/drivers/leds/leds-sunfire.c index 6b008f0c3f62..ab6d18f5c39f 100644 --- a/drivers/leds/leds-sunfire.c +++ b/drivers/leds/leds-sunfire.c @@ -9,6 +9,7 @@ #include <linux/leds.h> #include <linux/io.h> #include <linux/platform_device.h> +#include <linux/slab.h> #include <asm/fhc.h> #include <asm/upa.h> diff --git a/drivers/leds/leds-wm831x-status.c b/drivers/leds/leds-wm831x-status.c index c586d05e336a..ef5c24140a44 100644 --- a/drivers/leds/leds-wm831x-status.c +++ b/drivers/leds/leds-wm831x-status.c @@ -12,6 +12,7 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/platform_device.h> +#include <linux/slab.h> #include <linux/leds.h> #include <linux/err.h> #include <linux/mfd/wm831x/core.h> diff --git a/drivers/leds/leds-wm8350.c b/drivers/leds/leds-wm8350.c index 38c6bcb07e6c..5aab32ce4f4d 100644 --- a/drivers/leds/leds-wm8350.c +++ b/drivers/leds/leds-wm8350.c @@ -16,6 +16,7 @@ #include <linux/err.h> #include <linux/mfd/wm8350/pmic.h> #include <linux/regulator/consumer.h> +#include <linux/slab.h> /* Microamps */ static const int isink_cur[] = { diff --git a/drivers/leds/ledtrig-backlight.c b/drivers/leds/ledtrig-backlight.c index d3dfcfb417b8..f948e57bd9b8 100644 --- a/drivers/leds/ledtrig-backlight.c +++ b/drivers/leds/ledtrig-backlight.c @@ -12,6 +12,7 @@ #include <linux/module.h> #include <linux/kernel.h> +#include <linux/slab.h> #include <linux/init.h> #include <linux/fb.h> #include <linux/leds.h> diff --git a/drivers/leds/ledtrig-gpio.c b/drivers/leds/ledtrig-gpio.c index f5913372d691..991d93be0f44 100644 --- a/drivers/leds/ledtrig-gpio.c +++ b/drivers/leds/ledtrig-gpio.c @@ -16,6 +16,7 @@ #include <linux/interrupt.h> #include <linux/workqueue.h> #include <linux/leds.h> +#include <linux/slab.h> #include "leds.h" struct gpio_trig_data { diff --git a/drivers/leds/ledtrig-heartbeat.c b/drivers/leds/ledtrig-heartbeat.c index c1c1ea6f817b..759c0bba4a8f 100644 --- a/drivers/leds/ledtrig-heartbeat.c +++ b/drivers/leds/ledtrig-heartbeat.c @@ -14,6 +14,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> +#include <linux/slab.h> #include <linux/timer.h> #include <linux/sched.h> #include <linux/leds.h> diff --git a/drivers/leds/ledtrig-timer.c b/drivers/leds/ledtrig-timer.c index 38b3378be442..82b77bd482ff 100644 --- a/drivers/leds/ledtrig-timer.c +++ b/drivers/leds/ledtrig-timer.c @@ -22,6 +22,7 @@ #include <linux/timer.h> #include <linux/ctype.h> #include <linux/leds.h> +#include <linux/slab.h> #include "leds.h" struct timer_trig_data { |