diff options
Diffstat (limited to 'drivers/leds')
-rw-r--r-- | drivers/leds/Kconfig | 10 | ||||
-rw-r--r-- | drivers/leds/Makefile | 1 | ||||
-rw-r--r-- | drivers/leds/leds-88pm860x.c | 205 | ||||
-rw-r--r-- | drivers/leds/leds-lm3533.c | 6 | ||||
-rw-r--r-- | drivers/leds/leds-lp8788.c | 2 | ||||
-rw-r--r-- | drivers/leds/leds-netxbig.c | 2 | ||||
-rw-r--r-- | drivers/leds/leds-ns2.c | 2 | ||||
-rw-r--r-- | drivers/leds/leds-s3c24xx.c | 2 | ||||
-rw-r--r-- | drivers/leds/leds-wm8350.c | 2 | ||||
-rw-r--r-- | drivers/leds/ledtrig-cpu.c | 163 |
10 files changed, 280 insertions, 115 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index c96bbaadeebd..16578d3b52bb 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -506,6 +506,16 @@ config LEDS_TRIGGER_BACKLIGHT If unsure, say N. +config LEDS_TRIGGER_CPU + bool "LED CPU Trigger" + depends on LEDS_TRIGGERS + help + This allows LEDs to be controlled by active CPUs. This shows + the active CPUs across an array of LEDs so you can see which + CPUs are active on the system at any given moment. + + If unsure, say N. + config LEDS_TRIGGER_GPIO tristate "LED GPIO Trigger" depends on LEDS_TRIGGERS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index a4429a9217bc..a9b627c4f8ba 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -61,5 +61,6 @@ 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_CPU) += ledtrig-cpu.o obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT) += ledtrig-transient.o diff --git a/drivers/leds/leds-88pm860x.c b/drivers/leds/leds-88pm860x.c index 61897cfeeda6..b7e8cc0957fc 100644 --- a/drivers/leds/leds-88pm860x.c +++ b/drivers/leds/leds-88pm860x.c @@ -12,6 +12,7 @@ #include <linux/kernel.h> #include <linux/init.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/i2c.h> #include <linux/leds.h> @@ -20,18 +21,12 @@ #include <linux/mfd/88pm860x.h> #include <linux/module.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_MASK (0x7F) -#define LED_BLINK_ON(x) ((x & 0x7) * 66 + 66) -#define LED_BLINK_ON_MIN LED_BLINK_ON(0) -#define LED_BLINK_ON_MAX LED_BLINK_ON(0x7) #define LED_ON_CONTINUOUS (0x0F << 3) -#define LED_TO_ON(x) ((x - 66) / 66) #define LED1_BLINK_EN (1 << 1) #define LED2_BLINK_EN (1 << 2) @@ -49,85 +44,25 @@ struct pm860x_led { unsigned char brightness; unsigned char current_brightness; - int blink_data; - int blink_time; - int blink_on; - int blink_off; + int reg_control; + int reg_blink; + int blink_mask; }; -/* 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; - break; - case PM8606_LED2_RED: - case PM8606_LED2_GREEN: - case PM8606_LED2_BLUE: - ret = PM8606_RGB2A; - break; - } - 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_power_set(struct pm860x_chip *chip, int port, int on) { int ret = -EINVAL; switch (port) { - case PM8606_LED1_RED: - case PM8606_LED1_GREEN: - case PM8606_LED1_BLUE: + case 0: + case 1: + case 2: ret = on ? pm8606_osc_enable(chip, RGB1_ENABLE) : pm8606_osc_disable(chip, RGB1_ENABLE); break; - case PM8606_LED2_RED: - case PM8606_LED2_GREEN: - case PM8606_LED2_BLUE: + case 3: + case 4: + case 5: ret = on ? pm8606_osc_enable(chip, RGB2_ENABLE) : pm8606_osc_disable(chip, RGB2_ENABLE); break; @@ -141,7 +76,7 @@ static void pm860x_led_work(struct work_struct *work) struct pm860x_led *led; struct pm860x_chip *chip; unsigned char buf[3]; - int mask, ret; + int ret; led = container_of(work, struct pm860x_led, work); chip = led->chip; @@ -149,34 +84,34 @@ static void pm860x_led_work(struct work_struct *work) if ((led->current_brightness == 0) && led->brightness) { led_power_set(chip, led->port, 1); if (led->iset) { - pm860x_set_bits(led->i2c, __led_off(led->port), + pm860x_set_bits(led->i2c, led->reg_control, LED_CURRENT_MASK, led->iset); } - pm860x_set_bits(led->i2c, __blink_off(led->port), + pm860x_set_bits(led->i2c, led->reg_blink, LED_BLINK_MASK, LED_ON_CONTINUOUS); - mask = __blink_ctl_mask(led->port); - pm860x_set_bits(led->i2c, PM8606_WLED3B, mask, mask); + pm860x_set_bits(led->i2c, PM8606_WLED3B, led->blink_mask, + led->blink_mask); } - pm860x_set_bits(led->i2c, __led_off(led->port), LED_PWM_MASK, + pm860x_set_bits(led->i2c, led->reg_control, LED_PWM_MASK, led->brightness); if (led->brightness == 0) { - pm860x_bulk_read(led->i2c, __led_off(led->port), 3, buf); + pm860x_bulk_read(led->i2c, led->reg_control, 3, buf); ret = buf[0] & LED_PWM_MASK; ret |= buf[1] & LED_PWM_MASK; ret |= buf[2] & LED_PWM_MASK; if (ret == 0) { /* unset current since no led is lighting */ - pm860x_set_bits(led->i2c, __led_off(led->port), + pm860x_set_bits(led->i2c, led->reg_control, LED_CURRENT_MASK, 0); - mask = __blink_ctl_mask(led->port); - pm860x_set_bits(led->i2c, PM8606_WLED3B, mask, 0); + pm860x_set_bits(led->i2c, PM8606_WLED3B, + led->blink_mask, 0); led_power_set(chip, led->port, 0); } } led->current_brightness = led->brightness; dev_dbg(chip->dev, "Update LED. (reg:%d, brightness:%d)\n", - __led_off(led->port), led->brightness); + led->reg_control, led->brightness); mutex_unlock(&led->lock); } @@ -189,39 +124,92 @@ static void pm860x_led_set(struct led_classdev *cdev, schedule_work(&data->work); } +#ifdef CONFIG_OF +static int pm860x_led_dt_init(struct platform_device *pdev, + struct pm860x_led *data) +{ + struct device_node *nproot = pdev->dev.parent->of_node, *np; + int iset = 0; + if (!nproot) + return -ENODEV; + nproot = of_find_node_by_name(nproot, "leds"); + if (!nproot) { + dev_err(&pdev->dev, "failed to find leds node\n"); + return -ENODEV; + } + for_each_child_of_node(nproot, np) { + if (!of_node_cmp(np->name, data->name)) { + of_property_read_u32(np, "marvell,88pm860x-iset", + &iset); + data->iset = PM8606_LED_CURRENT(iset); + break; + } + } + return 0; +} +#else +#define pm860x_led_dt_init(x, y) (-1) +#endif + static int pm860x_led_probe(struct platform_device *pdev) { struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); - struct pm860x_led_pdata *pdata; + struct pm860x_led_pdata *pdata = pdev->dev.platform_data; 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; - } - - pdata = pdev->dev.platform_data; - if (pdata == NULL) { - dev_err(&pdev->dev, "No platform data!\n"); - return -EINVAL; - } + int ret = 0; data = devm_kzalloc(&pdev->dev, sizeof(struct pm860x_led), GFP_KERNEL); if (data == NULL) return -ENOMEM; - strncpy(data->name, res->name, MFD_NAME_SIZE - 1); + res = platform_get_resource_byname(pdev, IORESOURCE_REG, "control"); + if (!res) { + dev_err(&pdev->dev, "No REG resource for control\n"); + ret = -ENXIO; + goto out; + } + data->reg_control = res->start; + res = platform_get_resource_byname(pdev, IORESOURCE_REG, "blink"); + if (!res) { + dev_err(&pdev->dev, "No REG resource for blink\n"); + ret = -ENXIO; + goto out; + } + data->reg_blink = res->start; + memset(data->name, 0, MFD_NAME_SIZE); + switch (pdev->id) { + case 0: + data->blink_mask = LED1_BLINK_EN; + sprintf(data->name, "led0-red"); + break; + case 1: + data->blink_mask = LED1_BLINK_EN; + sprintf(data->name, "led0-green"); + break; + case 2: + data->blink_mask = LED1_BLINK_EN; + sprintf(data->name, "led0-blue"); + break; + case 3: + data->blink_mask = LED2_BLINK_EN; + sprintf(data->name, "led1-red"); + break; + case 4: + data->blink_mask = LED2_BLINK_EN; + sprintf(data->name, "led1-green"); + break; + case 5: + data->blink_mask = LED2_BLINK_EN; + sprintf(data->name, "led1-blue"); + break; + } 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 = pdata->flags; - if (data->port < 0) { - dev_err(&pdev->dev, "check device failed\n"); - return -EINVAL; - } + data->port = pdev->id; + if (pm860x_led_dt_init(pdev, data)) + if (pdata) + data->iset = pdata->iset; data->current_brightness = 0; data->cdev.name = data->name; @@ -236,6 +224,9 @@ static int pm860x_led_probe(struct platform_device *pdev) } pm860x_led_set(&data->cdev, 0); return 0; +out: + devm_kfree(&pdev->dev, data); + return ret; } static int pm860x_led_remove(struct platform_device *pdev) diff --git a/drivers/leds/leds-lm3533.c b/drivers/leds/leds-lm3533.c index f56b6e7ffdac..f6837b99908c 100644 --- a/drivers/leds/leds-lm3533.c +++ b/drivers/leds/leds-lm3533.c @@ -737,7 +737,7 @@ err_sysfs_remove: sysfs_remove_group(&led->cdev.dev->kobj, &lm3533_led_attribute_group); err_unregister: led_classdev_unregister(&led->cdev); - flush_work_sync(&led->work); + flush_work(&led->work); return ret; } @@ -751,7 +751,7 @@ static int __devexit lm3533_led_remove(struct platform_device *pdev) lm3533_ctrlbank_disable(&led->cb); sysfs_remove_group(&led->cdev.dev->kobj, &lm3533_led_attribute_group); led_classdev_unregister(&led->cdev); - flush_work_sync(&led->work); + flush_work(&led->work); return 0; } @@ -765,7 +765,7 @@ static void lm3533_led_shutdown(struct platform_device *pdev) lm3533_ctrlbank_disable(&led->cb); lm3533_led_set(&led->cdev, LED_OFF); /* disable blink */ - flush_work_sync(&led->work); + flush_work(&led->work); } static struct platform_driver lm3533_led_driver = { diff --git a/drivers/leds/leds-lp8788.c b/drivers/leds/leds-lp8788.c index 0ade6ebfc914..64009a176651 100644 --- a/drivers/leds/leds-lp8788.c +++ b/drivers/leds/leds-lp8788.c @@ -172,7 +172,7 @@ static int __devexit lp8788_led_remove(struct platform_device *pdev) struct lp8788_led *led = platform_get_drvdata(pdev); led_classdev_unregister(&led->led_dev); - flush_work_sync(&led->work); + flush_work(&led->work); return 0; } diff --git a/drivers/leds/leds-netxbig.c b/drivers/leds/leds-netxbig.c index e37618e363cf..461bbf9b33fa 100644 --- a/drivers/leds/leds-netxbig.c +++ b/drivers/leds/leds-netxbig.c @@ -28,7 +28,7 @@ #include <linux/platform_device.h> #include <linux/gpio.h> #include <linux/leds.h> -#include <mach/leds-netxbig.h> +#include <linux/platform_data/leds-kirkwood-netxbig.h> /* * GPIO extension bus. diff --git a/drivers/leds/leds-ns2.c b/drivers/leds/leds-ns2.c index 10528dafb043..d176ec83f5d9 100644 --- a/drivers/leds/leds-ns2.c +++ b/drivers/leds/leds-ns2.c @@ -29,7 +29,7 @@ #include <linux/gpio.h> #include <linux/leds.h> #include <linux/module.h> -#include <mach/leds-ns2.h> +#include <linux/platform_data/leds-kirkwood-ns2.h> /* * The Network Space v2 dual-GPIO LED is wired to a CPLD and can blink in diff --git a/drivers/leds/leds-s3c24xx.c b/drivers/leds/leds-s3c24xx.c index 942f0ea18178..e1a0df63a37f 100644 --- a/drivers/leds/leds-s3c24xx.c +++ b/drivers/leds/leds-s3c24xx.c @@ -21,7 +21,7 @@ #include <mach/hardware.h> #include <mach/regs-gpio.h> -#include <mach/leds-gpio.h> +#include <linux/platform_data/leds-s3c24xx.h> /* our context */ diff --git a/drivers/leds/leds-wm8350.c b/drivers/leds/leds-wm8350.c index 918d4baff1c7..4c62113f7a77 100644 --- a/drivers/leds/leds-wm8350.c +++ b/drivers/leds/leds-wm8350.c @@ -275,7 +275,7 @@ static int wm8350_led_remove(struct platform_device *pdev) struct wm8350_led *led = platform_get_drvdata(pdev); led_classdev_unregister(&led->cdev); - flush_work_sync(&led->work); + flush_work(&led->work); wm8350_led_disable(led); regulator_put(led->dcdc); regulator_put(led->isink); diff --git a/drivers/leds/ledtrig-cpu.c b/drivers/leds/ledtrig-cpu.c new file mode 100644 index 000000000000..b312056da14d --- /dev/null +++ b/drivers/leds/ledtrig-cpu.c @@ -0,0 +1,163 @@ +/* + * ledtrig-cpu.c - LED trigger based on CPU activity + * + * This LED trigger will be registered for each possible CPU and named as + * cpu0, cpu1, cpu2, cpu3, etc. + * + * It can be bound to any LED just like other triggers using either a + * board file or via sysfs interface. + * + * An API named ledtrig_cpu is exported for any user, who want to add CPU + * activity indication in their code + * + * Copyright 2011 Linus Walleij <linus.walleij@linaro.org> + * Copyright 2011 - 2012 Bryan Wu <bryan.wu@canonical.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/slab.h> +#include <linux/percpu.h> +#include <linux/syscore_ops.h> +#include <linux/rwsem.h> +#include "leds.h" + +#define MAX_NAME_LEN 8 + +struct led_trigger_cpu { + char name[MAX_NAME_LEN]; + struct led_trigger *_trig; + struct mutex lock; + int lock_is_inited; +}; + +static DEFINE_PER_CPU(struct led_trigger_cpu, cpu_trig); + +/** + * ledtrig_cpu - emit a CPU event as a trigger + * @evt: CPU event to be emitted + * + * Emit a CPU event on a CPU core, which will trigger a + * binded LED to turn on or turn off. + */ +void ledtrig_cpu(enum cpu_led_event ledevt) +{ + struct led_trigger_cpu *trig = &__get_cpu_var(cpu_trig); + + /* mutex lock should be initialized before calling mutex_call() */ + if (!trig->lock_is_inited) + return; + + mutex_lock(&trig->lock); + + /* Locate the correct CPU LED */ + switch (ledevt) { + case CPU_LED_IDLE_END: + case CPU_LED_START: + /* Will turn the LED on, max brightness */ + led_trigger_event(trig->_trig, LED_FULL); + break; + + case CPU_LED_IDLE_START: + case CPU_LED_STOP: + case CPU_LED_HALTED: + /* Will turn the LED off */ + led_trigger_event(trig->_trig, LED_OFF); + break; + + default: + /* Will leave the LED as it is */ + break; + } + + mutex_unlock(&trig->lock); +} +EXPORT_SYMBOL(ledtrig_cpu); + +static int ledtrig_cpu_syscore_suspend(void) +{ + ledtrig_cpu(CPU_LED_STOP); + return 0; +} + +static void ledtrig_cpu_syscore_resume(void) +{ + ledtrig_cpu(CPU_LED_START); +} + +static void ledtrig_cpu_syscore_shutdown(void) +{ + ledtrig_cpu(CPU_LED_HALTED); +} + +static struct syscore_ops ledtrig_cpu_syscore_ops = { + .shutdown = ledtrig_cpu_syscore_shutdown, + .suspend = ledtrig_cpu_syscore_suspend, + .resume = ledtrig_cpu_syscore_resume, +}; + +static int __init ledtrig_cpu_init(void) +{ + int cpu; + + /* Supports up to 9999 cpu cores */ + BUILD_BUG_ON(CONFIG_NR_CPUS > 9999); + + /* + * Registering CPU led trigger for each CPU core here + * ignores CPU hotplug, but after this CPU hotplug works + * fine with this trigger. + */ + for_each_possible_cpu(cpu) { + struct led_trigger_cpu *trig = &per_cpu(cpu_trig, cpu); + + mutex_init(&trig->lock); + + snprintf(trig->name, MAX_NAME_LEN, "cpu%d", cpu); + + mutex_lock(&trig->lock); + led_trigger_register_simple(trig->name, &trig->_trig); + trig->lock_is_inited = 1; + mutex_unlock(&trig->lock); + } + + register_syscore_ops(&ledtrig_cpu_syscore_ops); + + pr_info("ledtrig-cpu: registered to indicate activity on CPUs\n"); + + return 0; +} +module_init(ledtrig_cpu_init); + +static void __exit ledtrig_cpu_exit(void) +{ + int cpu; + + for_each_possible_cpu(cpu) { + struct led_trigger_cpu *trig = &per_cpu(cpu_trig, cpu); + + mutex_lock(&trig->lock); + + led_trigger_unregister_simple(trig->_trig); + trig->_trig = NULL; + memset(trig->name, 0, MAX_NAME_LEN); + trig->lock_is_inited = 0; + + mutex_unlock(&trig->lock); + mutex_destroy(&trig->lock); + } + + unregister_syscore_ops(&ledtrig_cpu_syscore_ops); +} +module_exit(ledtrig_cpu_exit); + +MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); +MODULE_AUTHOR("Bryan Wu <bryan.wu@canonical.com>"); +MODULE_DESCRIPTION("CPU LED trigger"); +MODULE_LICENSE("GPL"); |