From fba14ae8e924881038406ecff031d2d17becc2cb Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Wed, 22 Jan 2014 08:32:02 -0800 Subject: ledtrig-cpu: Handle CPU hot(un)plugging When CPU is hot(un)plugged, no syscore notification is being generated, nor is cpuidle involved. This leaves the CPU LED turned on, because the dying thread is doing some work (LED on) and than it is... well, dying (LED still on :-) Added notifier block for hot(un)plugging operations, generating existing trigger events. Signed-off-by: Pawel Moll Signed-off-by: Bryan Wu diff --git a/drivers/leds/trigger/ledtrig-cpu.c b/drivers/leds/trigger/ledtrig-cpu.c index 118335e..1c3ee9f 100644 --- a/drivers/leds/trigger/ledtrig-cpu.c +++ b/drivers/leds/trigger/ledtrig-cpu.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "../leds.h" #define MAX_NAME_LEN 8 @@ -92,6 +93,26 @@ static struct syscore_ops ledtrig_cpu_syscore_ops = { .resume = ledtrig_cpu_syscore_resume, }; +static int ledtrig_cpu_notify(struct notifier_block *self, + unsigned long action, void *hcpu) +{ + switch (action & ~CPU_TASKS_FROZEN) { + case CPU_STARTING: + ledtrig_cpu(CPU_LED_START); + break; + case CPU_DYING: + ledtrig_cpu(CPU_LED_STOP); + break; + } + + return NOTIFY_OK; +} + + +static struct notifier_block ledtrig_cpu_nb = { + .notifier_call = ledtrig_cpu_notify, +}; + static int __init ledtrig_cpu_init(void) { int cpu; @@ -113,6 +134,7 @@ static int __init ledtrig_cpu_init(void) } register_syscore_ops(&ledtrig_cpu_syscore_ops); + register_cpu_notifier(&ledtrig_cpu_nb); pr_info("ledtrig-cpu: registered to indicate activity on CPUs\n"); @@ -124,6 +146,8 @@ static void __exit ledtrig_cpu_exit(void) { int cpu; + unregister_cpu_notifier(&ledtrig_cpu_nb); + for_each_possible_cpu(cpu) { struct led_trigger_cpu *trig = &per_cpu(cpu_trig, cpu); -- cgit v0.10.2 From 8d82fef8bbee588d071372eb02439d2053b4bfe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20S=C3=B8rensen?= Date: Tue, 4 Feb 2014 00:11:42 -0800 Subject: leds: Turn off led if blinking is disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When using the timer trigger and setting delay_on to 0, the led will stay in whatever state is was in, while intuitively one would expect it to turn off. This patch changes the behaviour to turn it off. Signed-off-by: Stefan Sørensen Signed-off-by: Bryan Wu diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index ce8921a..71b40d3 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -39,9 +39,11 @@ static void led_set_software_blink(struct led_classdev *led_cdev, led_cdev->blink_delay_on = delay_on; led_cdev->blink_delay_off = delay_off; - /* never on - don't blink */ - if (!delay_on) + /* never on - just set to off */ + if (!delay_on) { + __led_set_brightness(led_cdev, LED_OFF); return; + } /* never off - just set to brightness */ if (!delay_off) { -- cgit v0.10.2 From a59ce6584d566847980f9dcad5343cd9856145c8 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Fri, 31 Jan 2014 22:36:28 -0800 Subject: leds: leds-mc13783: Add MC34708 LED support This patch adds support for two LEDs on MC34708 PMIC. Signed-off-by: Alexander Shiyan Signed-off-by: Bryan Wu diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 72156c1..93466d2 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -416,7 +416,7 @@ config LEDS_MC13783 depends on MFD_MC13XXX help This option enable support for on-chip LED drivers found - on Freescale Semiconductor MC13783/MC13892 PMIC. + on Freescale Semiconductor MC13783/MC13892/MC34708 PMIC. config LEDS_NS2 tristate "LED support for Network Space v2 GPIO LEDs" diff --git a/drivers/leds/leds-mc13783.c b/drivers/leds/leds-mc13783.c index ca87a1b..68f2455 100644 --- a/drivers/leds/leds-mc13783.c +++ b/drivers/leds/leds-mc13783.c @@ -1,5 +1,5 @@ /* - * LEDs driver for Freescale MC13783/MC13892 + * LEDs driver for Freescale MC13783/MC13892/MC34708 * * Copyright (C) 2010 Philippe Rétornaz * @@ -23,23 +23,23 @@ #include #include -#define MC13XXX_REG_LED_CONTROL(x) (51 + (x)) - struct mc13xxx_led_devtype { int led_min; int led_max; int num_regs; + u32 ledctrl_base; }; struct mc13xxx_led { struct led_classdev cdev; struct work_struct work; - struct mc13xxx *master; enum led_brightness new_brightness; int id; + struct mc13xxx_leds *leds; }; struct mc13xxx_leds { + struct mc13xxx *master; struct mc13xxx_led_devtype *devtype; int num_leds; struct mc13xxx_led led[0]; @@ -48,24 +48,15 @@ struct mc13xxx_leds { static void mc13xxx_led_work(struct work_struct *work) { struct mc13xxx_led *led = container_of(work, struct mc13xxx_led, work); - int reg, mask, value, bank, off, shift; + struct mc13xxx_leds *leds = led->leds; + unsigned int reg, mask, value, bank, off, shift; switch (led->id) { case MC13783_LED_MD: - reg = MC13XXX_REG_LED_CONTROL(2); - shift = 9; - mask = 0x0f; - value = led->new_brightness >> 4; - break; case MC13783_LED_AD: - reg = MC13XXX_REG_LED_CONTROL(2); - shift = 13; - mask = 0x0f; - value = led->new_brightness >> 4; - break; case MC13783_LED_KP: - reg = MC13XXX_REG_LED_CONTROL(2); - shift = 17; + reg = 2; + shift = 9 + (led->id - MC13783_LED_MD) * 4; mask = 0x0f; value = led->new_brightness >> 4; break; @@ -80,26 +71,16 @@ static void mc13xxx_led_work(struct work_struct *work) case MC13783_LED_B3: off = led->id - MC13783_LED_R1; bank = off / 3; - reg = MC13XXX_REG_LED_CONTROL(3) + bank; + reg = 3 + bank; shift = (off - bank * 3) * 5 + 6; value = led->new_brightness >> 3; mask = 0x1f; break; case MC13892_LED_MD: - reg = MC13XXX_REG_LED_CONTROL(0); - shift = 3; - mask = 0x3f; - value = led->new_brightness >> 2; - break; case MC13892_LED_AD: - reg = MC13XXX_REG_LED_CONTROL(0); - shift = 15; - mask = 0x3f; - value = led->new_brightness >> 2; - break; case MC13892_LED_KP: - reg = MC13XXX_REG_LED_CONTROL(1); - shift = 3; + reg = (led->id - MC13892_LED_MD) / 2; + shift = 3 + (led->id - MC13892_LED_MD) * 12; mask = 0x3f; value = led->new_brightness >> 2; break; @@ -108,16 +89,24 @@ static void mc13xxx_led_work(struct work_struct *work) case MC13892_LED_B: off = led->id - MC13892_LED_R; bank = off / 2; - reg = MC13XXX_REG_LED_CONTROL(2) + bank; + reg = 2 + bank; shift = (off - bank * 2) * 12 + 3; value = led->new_brightness >> 2; mask = 0x3f; break; + case MC34708_LED_R: + case MC34708_LED_G: + reg = 0; + shift = 3 + (led->id - MC34708_LED_R) * 12; + value = led->new_brightness >> 2; + mask = 0x3f; + break; default: BUG(); } - mc13xxx_reg_rmw(led->master, reg, mask << shift, value << shift); + mc13xxx_reg_rmw(leds->master, leds->devtype->ledctrl_base + reg, + mask << shift, value << shift); } static void mc13xxx_led_set(struct led_classdev *led_cdev, @@ -132,16 +121,17 @@ static void mc13xxx_led_set(struct led_classdev *led_cdev, static int __init mc13xxx_led_probe(struct platform_device *pdev) { - struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); - struct mc13xxx *mcdev = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(dev); + struct mc13xxx *mcdev = dev_get_drvdata(dev->parent); struct mc13xxx_led_devtype *devtype = (struct mc13xxx_led_devtype *)pdev->id_entry->driver_data; struct mc13xxx_leds *leds; int i, id, num_leds, ret = -ENODATA; - u32 reg, init_led = 0; + u32 init_led = 0; if (!pdata) { - dev_err(&pdev->dev, "Missing platform data\n"); + dev_err(dev, "Missing platform data\n"); return -ENODEV; } @@ -149,23 +139,23 @@ static int __init mc13xxx_led_probe(struct platform_device *pdev) if ((num_leds < 1) || (num_leds > (devtype->led_max - devtype->led_min + 1))) { - dev_err(&pdev->dev, "Invalid LED count %d\n", num_leds); + dev_err(dev, "Invalid LED count %d\n", num_leds); return -EINVAL; } - leds = devm_kzalloc(&pdev->dev, num_leds * sizeof(struct mc13xxx_led) + + leds = devm_kzalloc(dev, num_leds * sizeof(struct mc13xxx_led) + sizeof(struct mc13xxx_leds), GFP_KERNEL); if (!leds) return -ENOMEM; leds->devtype = devtype; leds->num_leds = num_leds; + leds->master = mcdev; platform_set_drvdata(pdev, leds); for (i = 0; i < devtype->num_regs; i++) { - reg = pdata->led_control[i]; - WARN_ON(reg >= (1 << 24)); - ret = mc13xxx_reg_write(mcdev, MC13XXX_REG_LED_CONTROL(i), reg); + ret = mc13xxx_reg_write(mcdev, leds->devtype->ledctrl_base + i, + pdata->led_control[i]); if (ret) return ret; } @@ -180,19 +170,18 @@ static int __init mc13xxx_led_probe(struct platform_device *pdev) trig = pdata->led[i].default_trigger; if ((id > devtype->led_max) || (id < devtype->led_min)) { - dev_err(&pdev->dev, "Invalid ID %i\n", id); + dev_err(dev, "Invalid ID %i\n", id); break; } if (init_led & (1 << id)) { - dev_warn(&pdev->dev, - "LED %i already initialized\n", id); + dev_warn(dev, "LED %i already initialized\n", id); break; } init_led |= 1 << id; leds->led[i].id = id; - leds->led[i].master = mcdev; + leds->led[i].leds = leds; leds->led[i].cdev.name = name; leds->led[i].cdev.default_trigger = trig; leds->led[i].cdev.brightness_set = mc13xxx_led_set; @@ -200,10 +189,9 @@ static int __init mc13xxx_led_probe(struct platform_device *pdev) INIT_WORK(&leds->led[i].work, mc13xxx_led_work); - ret = led_classdev_register(pdev->dev.parent, - &leds->led[i].cdev); + ret = led_classdev_register(dev->parent, &leds->led[i].cdev); if (ret) { - dev_err(&pdev->dev, "Failed to register LED %i\n", id); + dev_err(dev, "Failed to register LED %i\n", id); break; } } @@ -219,8 +207,8 @@ static int __init mc13xxx_led_probe(struct platform_device *pdev) static int mc13xxx_led_remove(struct platform_device *pdev) { - struct mc13xxx *mcdev = dev_get_drvdata(pdev->dev.parent); struct mc13xxx_leds *leds = platform_get_drvdata(pdev); + struct mc13xxx *mcdev = leds->master; int i; for (i = 0; i < leds->num_leds; i++) { @@ -229,7 +217,7 @@ static int mc13xxx_led_remove(struct platform_device *pdev) } for (i = 0; i < leds->devtype->num_regs; i++) - mc13xxx_reg_write(mcdev, MC13XXX_REG_LED_CONTROL(i), 0); + mc13xxx_reg_write(mcdev, leds->devtype->ledctrl_base + i, 0); return 0; } @@ -238,17 +226,27 @@ static const struct mc13xxx_led_devtype mc13783_led_devtype = { .led_min = MC13783_LED_MD, .led_max = MC13783_LED_B3, .num_regs = 6, + .ledctrl_base = 51, }; static const struct mc13xxx_led_devtype mc13892_led_devtype = { .led_min = MC13892_LED_MD, .led_max = MC13892_LED_B, .num_regs = 4, + .ledctrl_base = 51, +}; + +static const struct mc13xxx_led_devtype mc34708_led_devtype = { + .led_min = MC34708_LED_R, + .led_max = MC34708_LED_G, + .num_regs = 1, + .ledctrl_base = 54, }; static const struct platform_device_id mc13xxx_led_id_table[] = { { "mc13783-led", (kernel_ulong_t)&mc13783_led_devtype, }, { "mc13892-led", (kernel_ulong_t)&mc13892_led_devtype, }, + { "mc34708-led", (kernel_ulong_t)&mc34708_led_devtype, }, { } }; MODULE_DEVICE_TABLE(platform, mc13xxx_led_id_table); diff --git a/include/linux/mfd/mc13xxx.h b/include/linux/mfd/mc13xxx.h index ac39d91..a326c85 100644 --- a/include/linux/mfd/mc13xxx.h +++ b/include/linux/mfd/mc13xxx.h @@ -104,6 +104,9 @@ enum { MC13892_LED_R, MC13892_LED_G, MC13892_LED_B, + /* MC34708 LED IDs */ + MC34708_LED_R, + MC34708_LED_G, }; struct mc13xxx_led_platform_data { @@ -163,6 +166,9 @@ struct mc13xxx_leds_platform_data { #define MC13892_LED_C2_CURRENT_G(x) (((x) & 0x7) << 21) /* MC13892 LED Control 3 */ #define MC13892_LED_C3_CURRENT_B(x) (((x) & 0x7) << 9) +/* MC34708 LED Control 0 */ +#define MC34708_LED_C0_CURRENT_R(x) (((x) & 0x3) << 9) +#define MC34708_LED_C0_CURRENT_G(x) (((x) & 0x3) << 21) u32 led_control[MAX_LED_CONTROL_REGS]; }; -- cgit v0.10.2 From 02e9e11e24c30828a26daad04f9f165906b62efd Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Fri, 31 Jan 2014 22:36:29 -0800 Subject: leds: leds-mc13783: Use LED core PM functions Signed-off-by: Bryan Wu diff --git a/drivers/leds/leds-mc13783.c b/drivers/leds/leds-mc13783.c index 68f2455..491309a 100644 --- a/drivers/leds/leds-mc13783.c +++ b/drivers/leds/leds-mc13783.c @@ -184,6 +184,7 @@ static int __init mc13xxx_led_probe(struct platform_device *pdev) leds->led[i].leds = leds; leds->led[i].cdev.name = name; leds->led[i].cdev.default_trigger = trig; + leds->led[i].cdev.flags = LED_CORE_SUSPENDRESUME; leds->led[i].cdev.brightness_set = mc13xxx_led_set; leds->led[i].cdev.brightness = LED_OFF; -- cgit v0.10.2 From 677d13f27e9735d3f2e8d7b8b54cbd820630638a Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Fri, 31 Jan 2014 22:36:30 -0800 Subject: leds: leds-mc13783: Use proper "max_brightness" value fo LEDs Instead of using maximum value of 255 and shift it to appropriate LED duty cycle, this patch introduce a helper to use proper maximum value for each LED. Signed-off-by: Alexander Shiyan Signed-off-by: Bryan Wu diff --git a/drivers/leds/leds-mc13783.c b/drivers/leds/leds-mc13783.c index 491309a..b1686b4 100644 --- a/drivers/leds/leds-mc13783.c +++ b/drivers/leds/leds-mc13783.c @@ -45,11 +45,21 @@ struct mc13xxx_leds { struct mc13xxx_led led[0]; }; +static unsigned int mc13xxx_max_brightness(int id) +{ + if (id >= MC13783_LED_MD && id <= MC13783_LED_KP) + return 0x0f; + else if (id >= MC13783_LED_R1 && id <= MC13783_LED_B3) + return 0x1f; + + return 0x3f; +} + static void mc13xxx_led_work(struct work_struct *work) { struct mc13xxx_led *led = container_of(work, struct mc13xxx_led, work); struct mc13xxx_leds *leds = led->leds; - unsigned int reg, mask, value, bank, off, shift; + unsigned int reg, bank, off, shift; switch (led->id) { case MC13783_LED_MD: @@ -57,8 +67,6 @@ static void mc13xxx_led_work(struct work_struct *work) case MC13783_LED_KP: reg = 2; shift = 9 + (led->id - MC13783_LED_MD) * 4; - mask = 0x0f; - value = led->new_brightness >> 4; break; case MC13783_LED_R1: case MC13783_LED_G1: @@ -73,16 +81,12 @@ static void mc13xxx_led_work(struct work_struct *work) bank = off / 3; reg = 3 + bank; shift = (off - bank * 3) * 5 + 6; - value = led->new_brightness >> 3; - mask = 0x1f; break; case MC13892_LED_MD: case MC13892_LED_AD: case MC13892_LED_KP: reg = (led->id - MC13892_LED_MD) / 2; shift = 3 + (led->id - MC13892_LED_MD) * 12; - mask = 0x3f; - value = led->new_brightness >> 2; break; case MC13892_LED_R: case MC13892_LED_G: @@ -91,22 +95,19 @@ static void mc13xxx_led_work(struct work_struct *work) bank = off / 2; reg = 2 + bank; shift = (off - bank * 2) * 12 + 3; - value = led->new_brightness >> 2; - mask = 0x3f; break; case MC34708_LED_R: case MC34708_LED_G: reg = 0; shift = 3 + (led->id - MC34708_LED_R) * 12; - value = led->new_brightness >> 2; - mask = 0x3f; break; default: BUG(); } mc13xxx_reg_rmw(leds->master, leds->devtype->ledctrl_base + reg, - mask << shift, value << shift); + mc13xxx_max_brightness(led->id) << shift, + led->new_brightness << shift); } static void mc13xxx_led_set(struct led_classdev *led_cdev, @@ -186,7 +187,7 @@ static int __init mc13xxx_led_probe(struct platform_device *pdev) leds->led[i].cdev.default_trigger = trig; leds->led[i].cdev.flags = LED_CORE_SUSPENDRESUME; leds->led[i].cdev.brightness_set = mc13xxx_led_set; - leds->led[i].cdev.brightness = LED_OFF; + leds->led[i].cdev.max_brightness = mc13xxx_max_brightness(id); INIT_WORK(&leds->led[i].work, mc13xxx_led_work); -- cgit v0.10.2 From 2f18f8d638cc66a5339d901dea2c9d8af72e69c2 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Fri, 31 Jan 2014 22:36:31 -0800 Subject: leds: leds-mc13783: Remove unnecessary cleaning of registers on exit LED core switches each LED to OFF-state on exit, so there is no need for resetting registers. Signed-off-by: Alexander Shiyan Signed-off-by: Bryan Wu diff --git a/drivers/leds/leds-mc13783.c b/drivers/leds/leds-mc13783.c index b1686b4..15fa5e8 100644 --- a/drivers/leds/leds-mc13783.c +++ b/drivers/leds/leds-mc13783.c @@ -210,7 +210,6 @@ static int __init mc13xxx_led_probe(struct platform_device *pdev) static int mc13xxx_led_remove(struct platform_device *pdev) { struct mc13xxx_leds *leds = platform_get_drvdata(pdev); - struct mc13xxx *mcdev = leds->master; int i; for (i = 0; i < leds->num_leds; i++) { @@ -218,9 +217,6 @@ static int mc13xxx_led_remove(struct platform_device *pdev) cancel_work_sync(&leds->led[i].work); } - for (i = 0; i < leds->devtype->num_regs; i++) - mc13xxx_reg_write(mcdev, leds->devtype->ledctrl_base + i, 0); - return 0; } -- cgit v0.10.2 From 25c6579f872d0542809067d56fad22984b8ff565 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Fri, 31 Jan 2014 22:37:23 -0800 Subject: leds: leds-mc13783: Add devicetree support This patch adds devicetree support for the MC13XXX LED driver. (cooloney@gmail.com: remove unneeded semicolon) Signed-off-by: Alexander Shiyan Signed-off-by: Bryan Wu diff --git a/Documentation/devicetree/bindings/mfd/mc13xxx.txt b/Documentation/devicetree/bindings/mfd/mc13xxx.txt index abd9e3c..1413f39 100644 --- a/Documentation/devicetree/bindings/mfd/mc13xxx.txt +++ b/Documentation/devicetree/bindings/mfd/mc13xxx.txt @@ -10,9 +10,44 @@ Optional properties: - fsl,mc13xxx-uses-touch : Indicate the touchscreen controller is being used Sub-nodes: +- leds : Contain the led nodes and initial register values in property + "led-control". Number of register depends of used IC, for MC13783 is 6, + for MC13892 is 4, for MC34708 is 1. See datasheet for bits definitions of + these registers. + - #address-cells: Must be 1. + - #size-cells: Must be 0. + Each led node should contain "reg", which used as LED ID (described below). + Optional properties "label" and "linux,default-trigger" is described in + Documentation/devicetree/bindings/leds/common.txt. - regulators : Contain the regulator nodes. The regulators are bound using their names as listed below with their registers and bits for enabling. +MC13783 LED IDs: + 0 : Main display + 1 : AUX display + 2 : Keypad + 3 : Red 1 + 4 : Green 1 + 5 : Blue 1 + 6 : Red 2 + 7 : Green 2 + 8 : Blue 2 + 9 : Red 3 + 10 : Green 3 + 11 : Blue 3 + +MC13892 LED IDs: + 0 : Main display + 1 : AUX display + 2 : Keypad + 3 : Red + 4 : Green + 5 : Blue + +MC34708 LED IDs: + 0 : Charger Red + 1 : Charger Green + MC13783 regulators: sw1a : regulator SW1A (register 24, bit 0) sw1b : regulator SW1B (register 25, bit 0) @@ -89,6 +124,18 @@ ecspi@70010000 { /* ECSPI1 */ interrupt-parent = <&gpio0>; interrupts = <8>; + leds { + #address-cells = <1>; + #size-cells = <0>; + led-control = <0x000 0x000 0x0e0 0x000>; + + sysled { + reg = <3>; + label = "system:red:live"; + linux,default-trigger = "heartbeat"; + }; + }; + regulators { sw1_reg: mc13892__sw1 { regulator-min-microvolt = <600000>; diff --git a/drivers/leds/leds-mc13783.c b/drivers/leds/leds-mc13783.c index 15fa5e8..021adc1 100644 --- a/drivers/leds/leds-mc13783.c +++ b/drivers/leds/leds-mc13783.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -42,7 +43,7 @@ struct mc13xxx_leds { struct mc13xxx *master; struct mc13xxx_led_devtype *devtype; int num_leds; - struct mc13xxx_led led[0]; + struct mc13xxx_led *led; }; static unsigned int mc13xxx_max_brightness(int id) @@ -120,6 +121,74 @@ static void mc13xxx_led_set(struct led_classdev *led_cdev, schedule_work(&led->work); } +#ifdef CONFIG_OF +static struct mc13xxx_leds_platform_data __init *mc13xxx_led_probe_dt( + struct platform_device *pdev) +{ + struct mc13xxx_leds *leds = platform_get_drvdata(pdev); + struct mc13xxx_leds_platform_data *pdata; + struct device_node *parent, *child; + struct device *dev = &pdev->dev; + int i = 0, ret = -ENODATA; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + of_node_get(dev->parent->of_node); + + parent = of_find_node_by_name(dev->parent->of_node, "leds"); + if (!parent) + goto out_node_put; + + ret = of_property_read_u32_array(parent, "led-control", + pdata->led_control, + leds->devtype->num_regs); + if (ret) + goto out_node_put; + + pdata->num_leds = of_get_child_count(parent); + + pdata->led = devm_kzalloc(dev, pdata->num_leds * sizeof(*pdata->led), + GFP_KERNEL); + if (!pdata->led) { + ret = -ENOMEM; + goto out_node_put; + } + + for_each_child_of_node(parent, child) { + const char *str; + u32 tmp; + + if (of_property_read_u32(child, "reg", &tmp)) + continue; + pdata->led[i].id = leds->devtype->led_min + tmp; + + if (!of_property_read_string(child, "label", &str)) + pdata->led[i].name = str; + if (!of_property_read_string(child, "linux,default-trigger", + &str)) + pdata->led[i].default_trigger = str; + + i++; + } + + pdata->num_leds = i; + ret = i > 0 ? 0 : -ENODATA; + +out_node_put: + of_node_put(parent); + + return ret ? ERR_PTR(ret) : pdata; +} +#else +static inline struct mc13xxx_leds_platform_data __init *mc13xxx_led_probe_dt( + struct platform_device *pdev) +{ + return ERR_PTR(-ENOSYS); +} +#endif + static int __init mc13xxx_led_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -128,32 +197,37 @@ static int __init mc13xxx_led_probe(struct platform_device *pdev) struct mc13xxx_led_devtype *devtype = (struct mc13xxx_led_devtype *)pdev->id_entry->driver_data; struct mc13xxx_leds *leds; - int i, id, num_leds, ret = -ENODATA; + int i, id, ret = -ENODATA; u32 init_led = 0; - if (!pdata) { - dev_err(dev, "Missing platform data\n"); - return -ENODEV; - } - - num_leds = pdata->num_leds; - - if ((num_leds < 1) || - (num_leds > (devtype->led_max - devtype->led_min + 1))) { - dev_err(dev, "Invalid LED count %d\n", num_leds); - return -EINVAL; - } - - leds = devm_kzalloc(dev, num_leds * sizeof(struct mc13xxx_led) + - sizeof(struct mc13xxx_leds), GFP_KERNEL); + leds = devm_kzalloc(dev, sizeof(*leds), GFP_KERNEL); if (!leds) return -ENOMEM; leds->devtype = devtype; - leds->num_leds = num_leds; leds->master = mcdev; platform_set_drvdata(pdev, leds); + if (dev->parent->of_node) { + pdata = mc13xxx_led_probe_dt(pdev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + } else if (!pdata) + return -ENODATA; + + leds->num_leds = pdata->num_leds; + + if ((leds->num_leds < 1) || + (leds->num_leds > (devtype->led_max - devtype->led_min + 1))) { + dev_err(dev, "Invalid LED count %d\n", leds->num_leds); + return -EINVAL; + } + + leds->led = devm_kzalloc(dev, leds->num_leds * sizeof(*leds->led), + GFP_KERNEL); + if (!leds->led) + return -ENOMEM; + for (i = 0; i < devtype->num_regs; i++) { ret = mc13xxx_reg_write(mcdev, leds->devtype->ledctrl_base + i, pdata->led_control[i]); @@ -161,7 +235,7 @@ static int __init mc13xxx_led_probe(struct platform_device *pdev) return ret; } - for (i = 0; i < num_leds; i++) { + for (i = 0; i < leds->num_leds; i++) { const char *name, *trig; ret = -EINVAL; -- cgit v0.10.2 From 4270a78d23eece0b25a13bff1e71d114ec547de4 Mon Sep 17 00:00:00 2001 From: Robin Gong Date: Mon, 20 Jan 2014 03:41:26 -0800 Subject: leds: leds-gpio: add retain-state-suspended property Some gpio-leds need retain the state even in suspend, such as charger led. But this property missed in devicetree, add it. (cooloney@gmail.com: fold DT binding updates into this patch) Signed-off-by: Robin Gong Signed-off-by: Bryan Wu diff --git a/Documentation/devicetree/bindings/leds/leds-gpio.txt b/Documentation/devicetree/bindings/leds/leds-gpio.txt index df1b308..f77148f 100644 --- a/Documentation/devicetree/bindings/leds/leds-gpio.txt +++ b/Documentation/devicetree/bindings/leds/leds-gpio.txt @@ -21,6 +21,8 @@ LED sub-node properties: on). The "keep" setting will keep the LED at whatever its current state is, without producing a glitch. The default is off if this property is not present. +- retain-state-suspended: (optional) The suspend state can be retained.Such + as charge-led gpio. Examples: @@ -50,3 +52,13 @@ run-control { default-state = "on"; }; }; + +leds { + compatible = "gpio-leds"; + + charger-led { + gpios = <&gpio1 2 0>; + linux,default-trigger = "max8903-charger-charging"; + retain-state-suspended; + }; +}; diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 78b0e27..1bb3f1a 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -204,6 +204,9 @@ static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev) led.default_state = LEDS_GPIO_DEFSTATE_OFF; } + if (of_get_property(child, "retain-state-suspended", NULL)) + led.retain_state_suspended = 1; + ret = create_gpio_led(&led, &priv->leds[priv->num_leds++], &pdev->dev, NULL); if (ret < 0) { -- cgit v0.10.2 From 7c7d2a26dbb336ddabe53818750f4c32e2b45ddd Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Tue, 21 Jan 2014 13:22:57 -0800 Subject: drivers/leds: delete non-required instances of include None of these files are actually using any __init type directives and hence don't need to include . Most are just a left over from __devinit and __cpuinit removal, or simply due to code getting copied from one driver to the next. Cc: Bryan Wu Cc: Richard Purdie Cc: linux-leds@vger.kernel.org Signed-off-by: Paul Gortmaker Signed-off-by: Bryan Wu diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index e387f41..df1a7c1 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -13,7 +13,6 @@ #include #include -#include #include #include #include diff --git a/drivers/leds/leds-88pm860x.c b/drivers/leds/leds-88pm860x.c index 5f588c0..d1e1bca 100644 --- a/drivers/leds/leds-88pm860x.c +++ b/drivers/leds/leds-88pm860x.c @@ -11,7 +11,6 @@ */ #include -#include #include #include #include diff --git a/drivers/leds/leds-adp5520.c b/drivers/leds/leds-adp5520.c index 7e311a1..86b5bdb 100644 --- a/drivers/leds/leds-adp5520.c +++ b/drivers/leds/leds-adp5520.c @@ -15,7 +15,6 @@ #include #include -#include #include #include #include diff --git a/drivers/leds/leds-asic3.c b/drivers/leds/leds-asic3.c index 6de216a..70c74a7 100644 --- a/drivers/leds/leds-asic3.c +++ b/drivers/leds/leds-asic3.c @@ -7,7 +7,6 @@ */ #include -#include #include #include #include diff --git a/drivers/leds/leds-blinkm.c b/drivers/leds/leds-blinkm.c index 66d0a57..0c50860 100644 --- a/drivers/leds/leds-blinkm.c +++ b/drivers/leds/leds-blinkm.c @@ -18,7 +18,6 @@ */ #include -#include #include #include #include diff --git a/drivers/leds/leds-cobalt-qube.c b/drivers/leds/leds-cobalt-qube.c index 8abcb66..910339d 100644 --- a/drivers/leds/leds-cobalt-qube.c +++ b/drivers/leds/leds-cobalt-qube.c @@ -3,7 +3,6 @@ * * Control the Cobalt Qube/RaQ front LED */ -#include #include #include #include diff --git a/drivers/leds/leds-da903x.c b/drivers/leds/leds-da903x.c index 2a4b87f..35dffb1 100644 --- a/drivers/leds/leds-da903x.c +++ b/drivers/leds/leds-da903x.c @@ -14,7 +14,6 @@ #include #include -#include #include #include #include diff --git a/drivers/leds/leds-da9052.c b/drivers/leds/leds-da9052.c index 865d4fa..01486ad 100644 --- a/drivers/leds/leds-da9052.c +++ b/drivers/leds/leds-da9052.c @@ -14,7 +14,6 @@ #include #include -#include #include #include #include diff --git a/drivers/leds/leds-fsg.c b/drivers/leds/leds-fsg.c index b4d5a44..2b4dc73 100644 --- a/drivers/leds/leds-fsg.c +++ b/drivers/leds/leds-fsg.c @@ -16,7 +16,6 @@ */ #include -#include #include #include #include diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 1bb3f1a..953fb37 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -11,7 +11,6 @@ * */ #include -#include #include #include #include diff --git a/drivers/leds/leds-hp6xx.c b/drivers/leds/leds-hp6xx.c index 366b605..d61a988 100644 --- a/drivers/leds/leds-hp6xx.c +++ b/drivers/leds/leds-hp6xx.c @@ -12,7 +12,6 @@ #include #include -#include #include #include #include diff --git a/drivers/leds/leds-lm3533.c b/drivers/leds/leds-lm3533.c index 027ede7..e2c642c11 100644 --- a/drivers/leds/leds-lm3533.c +++ b/drivers/leds/leds-lm3533.c @@ -12,7 +12,6 @@ */ #include -#include #include #include #include diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 2ec34cf..8ca197a 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 4ade66a..cb5ed82 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/leds/leds-lp5562.c b/drivers/leds/leds-lp5562.c index bf006f4..315d3ca 100644 --- a/drivers/leds/leds-lp5562.c +++ b/drivers/leds/leds-lp5562.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/leds/leds-lt3593.c b/drivers/leds/leds-lt3593.c index 3417e5b..059f5b1 100644 --- a/drivers/leds/leds-lt3593.c +++ b/drivers/leds/leds-lt3593.c @@ -17,7 +17,6 @@ */ #include -#include #include #include #include diff --git a/drivers/leds/leds-mc13783.c b/drivers/leds/leds-mc13783.c index 021adc1..f1db88e 100644 --- a/drivers/leds/leds-mc13783.c +++ b/drivers/leds/leds-mc13783.c @@ -17,7 +17,6 @@ #include #include -#include #include #include #include diff --git a/drivers/leds/leds-netxbig.c b/drivers/leds/leds-netxbig.c index 2f9f141..e97f443 100644 --- a/drivers/leds/leds-netxbig.c +++ b/drivers/leds/leds-netxbig.c @@ -21,7 +21,6 @@ */ #include -#include #include #include #include diff --git a/drivers/leds/leds-ns2.c b/drivers/leds/leds-ns2.c index c7a4230..efa6258 100644 --- a/drivers/leds/leds-ns2.c +++ b/drivers/leds/leds-ns2.c @@ -23,7 +23,6 @@ */ #include -#include #include #include #include diff --git a/drivers/leds/leds-ot200.c b/drivers/leds/leds-ot200.c index 98cae52..c9d9060 100644 --- a/drivers/leds/leds-ot200.c +++ b/drivers/leds/leds-ot200.c @@ -8,7 +8,6 @@ */ #include -#include #include #include #include diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index 6050474..dd178736 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c @@ -14,7 +14,6 @@ #include #include -#include #include #include #include diff --git a/drivers/leds/leds-s3c24xx.c b/drivers/leds/leds-s3c24xx.c index 98174e7..28988b7 100644 --- a/drivers/leds/leds-s3c24xx.c +++ b/drivers/leds/leds-s3c24xx.c @@ -12,7 +12,6 @@ */ #include -#include #include #include #include diff --git a/drivers/leds/leds-wm831x-status.c b/drivers/leds/leds-wm831x-status.c index 0a1a13f..e72c974 100644 --- a/drivers/leds/leds-wm831x-status.c +++ b/drivers/leds/leds-wm831x-status.c @@ -10,7 +10,6 @@ */ #include -#include #include #include #include diff --git a/drivers/leds/leds-wm8350.c b/drivers/leds/leds-wm8350.c index 3f75fd2..4133ffe 100644 --- a/drivers/leds/leds-wm8350.c +++ b/drivers/leds/leds-wm8350.c @@ -10,7 +10,6 @@ */ #include -#include #include #include #include -- cgit v0.10.2 From fc87eb0b588394d8304d942bcd5c71d6bcc595c7 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 13 Feb 2014 22:37:09 -0800 Subject: leds: leds-s3c24xx: Trivial cleanup in header file Commit 436d42c61c3e ("ARM: samsung: move platform_data definitions") moved the files to the current location but forgot to remove the pointer to its previous location. Clean it up. While at it also change the header file protection macros appropriately. Signed-off-by: Sachin Kamat Signed-off-by: Bryan Wu diff --git a/include/linux/platform_data/leds-s3c24xx.h b/include/linux/platform_data/leds-s3c24xx.h index d8a7672..441a6f2 100644 --- a/include/linux/platform_data/leds-s3c24xx.h +++ b/include/linux/platform_data/leds-s3c24xx.h @@ -1,5 +1,4 @@ -/* arch/arm/mach-s3c2410/include/mach/leds-gpio.h - * +/* * Copyright (c) 2006 Simtec Electronics * http://armlinux.simtec.co.uk/ * Ben Dooks @@ -11,8 +10,8 @@ * published by the Free Software Foundation. */ -#ifndef __ASM_ARCH_LEDSGPIO_H -#define __ASM_ARCH_LEDSGPIO_H "leds-gpio.h" +#ifndef __LEDS_S3C24XX_H +#define __LEDS_S3C24XX_H #define S3C24XX_LEDF_ACTLOW (1<<0) /* LED is on when GPIO low */ #define S3C24XX_LEDF_TRISTATE (1<<1) /* tristate to turn off */ @@ -25,4 +24,4 @@ struct s3c24xx_led_platdata { char *def_trigger; }; -#endif /* __ASM_ARCH_LEDSGPIO_H */ +#endif /* __LEDS_S3C24XX_H */ -- cgit v0.10.2 From a6a83218d78339c19c4d6105316654ecd6632f7f Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 25 Feb 2014 21:10:51 -0800 Subject: leds: leds-ss4200: remove DEFINE_PCI_DEVICE_TABLE macro Don't use DEFINE_PCI_DEVICE_TABLE macro, because this macro is deprecated. Signed-off-by: Jingoo Han Signed-off-by: Bryan Wu diff --git a/drivers/leds/leds-ss4200.c b/drivers/leds/leds-ss4200.c index 5b8f938..2bdf642 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 DEFINE_PCI_DEVICE_TABLE(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) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_30) }, -- cgit v0.10.2 From 432eb69dd845d239213e714ae2b9efec3dcdf230 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 25 Feb 2014 21:11:41 -0800 Subject: leds: lp5562: remove unnecessary parentheses Remove unnecessary parentheses in order to fix the following checkpatch error. ERROR: return is not a function, parentheses are not required Signed-off-by: Jingoo Han Signed-off-by: Bryan Wu diff --git a/drivers/leds/leds-lp5562.c b/drivers/leds/leds-lp5562.c index 315d3ca..ca85724 100644 --- a/drivers/leds/leds-lp5562.c +++ b/drivers/leds/leds-lp5562.c @@ -346,9 +346,9 @@ static void lp5562_write_program_memory(struct lp55xx_chip *chip, /* check the size of program count */ static inline bool _is_pc_overflow(struct lp55xx_predef_pattern *ptn) { - return (ptn->size_r >= LP5562_PROGRAM_LENGTH || - ptn->size_g >= LP5562_PROGRAM_LENGTH || - ptn->size_b >= LP5562_PROGRAM_LENGTH); + return ptn->size_r >= LP5562_PROGRAM_LENGTH || + ptn->size_g >= LP5562_PROGRAM_LENGTH || + ptn->size_b >= LP5562_PROGRAM_LENGTH; } static int lp5562_run_predef_led_pattern(struct lp55xx_chip *chip, int mode) -- cgit v0.10.2 From cbaa93d5228b346bbcb7d9159eb1ac43a65f6f16 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 25 Feb 2014 21:16:11 -0800 Subject: leds: blinkm: remove unnecessary spaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove unnecessary space in order to fix the following checkpatch issues. WARNING: space prohibited before semicolon Signed-off-by: Jingoo Han Acked-by: Jan-Simon Möller Signed-off-by: Bryan Wu diff --git a/drivers/leds/leds-blinkm.c b/drivers/leds/leds-blinkm.c index 0c50860..d0452b0 100644 --- a/drivers/leds/leds-blinkm.c +++ b/drivers/leds/leds-blinkm.c @@ -443,7 +443,7 @@ static void led_work(struct work_struct *work) { int ret; struct blinkm_led *led; - struct blinkm_data *data ; + struct blinkm_data *data; struct blinkm_work *blm_work = work_to_blmwork(work); led = blm_work->blinkm_led; -- cgit v0.10.2 From a007ec59e32cb39f1a67a464b01ac6edeff74e5b Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 27 Feb 2014 23:25:07 -0800 Subject: leds: leds-ss4200: remove __initdata marker Remove __initdata marker, because it is not right for a module parameter. It will make the kernel oops problem. (cooloney@gmail.com: update commit message since it's really a wrong notation) Signed-off-by: Jingoo Han Signed-off-by: Bryan Wu diff --git a/drivers/leds/leds-ss4200.c b/drivers/leds/leds-ss4200.c index 2bdf642..2eb3ef6 100644 --- a/drivers/leds/leds-ss4200.c +++ b/drivers/leds/leds-ss4200.c @@ -78,7 +78,7 @@ static int __init ss4200_led_dmi_callback(const struct dmi_system_id *id) return 1; } -static bool __initdata nodetect; +static bool nodetect; module_param_named(nodetect, nodetect, bool, 0); MODULE_PARM_DESC(nodetect, "Skip DMI-based hardware detection"); -- cgit v0.10.2 From aad0f292756cb267953f6cd04bbf4b51f2497034 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 27 Feb 2014 23:26:08 -0800 Subject: leds: clevo-mail: remove __initdata marker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove __initdata marker, because it is not right for a module parameter. It will make the kernel oops problem. (cooloney@gmail.com: update commit message since it's really a wrong notation) Signed-off-by: Jingoo Han Suggested-by: Uwe Kleine-König Signed-off-by: Bryan Wu diff --git a/drivers/leds/leds-clevo-mail.c b/drivers/leds/leds-clevo-mail.c index d93e245..19202f5 100644 --- a/drivers/leds/leds-clevo-mail.c +++ b/drivers/leds/leds-clevo-mail.c @@ -19,7 +19,7 @@ MODULE_AUTHOR("Márton Németh "); MODULE_DESCRIPTION("Clevo mail LED driver"); MODULE_LICENSE("GPL"); -static bool __initdata nodetect; +static bool nodetect; module_param_named(nodetect, nodetect, bool, 0); MODULE_PARM_DESC(nodetect, "Skip DMI hardware detection"); -- cgit v0.10.2 From 472b854bbc0b55de850faa802250fc1aa7692e45 Mon Sep 17 00:00:00 2001 From: Paolo Pisati Date: Thu, 6 Mar 2014 09:18:37 -0800 Subject: leds-gpio: of: introduce MODULE_DEVICE_TABLE for module autoloading Enable autoloading of leds-gpio module when a corresponing DT entry is present. Signed-off-by: Paolo Pisati Signed-off-by: Bryan Wu diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 953fb37..57ff20f 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -226,6 +226,8 @@ static const struct of_device_id of_gpio_leds_match[] = { { .compatible = "gpio-leds", }, {}, }; + +MODULE_DEVICE_TABLE(of, of_gpio_leds_match); #else /* CONFIG_OF_GPIO */ static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev) { -- cgit v0.10.2 From d9e8928f83d76263d09e1a1eef0dab82e4a81a17 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 11 Mar 2014 09:30:53 -0700 Subject: leds-ot200: Fix dependencies The Bachmann OT200 is a Geode-based device, so OT200-specific drivers are only useful on X86_32, except for build testing. Signed-off-by: Jean Delvare Cc: Bryan Wu Cc: Richard Purdie Signed-off-by: Bryan Wu diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 93466d2..2394659 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -474,7 +474,7 @@ config LEDS_LM355x config LEDS_OT200 tristate "LED support for the Bachmann OT200" - depends on LEDS_CLASS && HAS_IOMEM + depends on LEDS_CLASS && HAS_IOMEM && (X86_32 || COMPILE_TEST) help This option enables support for the LEDs on the Bachmann OT200. Say Y to enable LEDs on the Bachmann OT200. -- cgit v0.10.2 From 0016db26c093798632ea741402215a31af447704 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 13 Mar 2014 10:56:24 -0700 Subject: leds: clevo-mail: Make probe function __init One of the benefits of platform_driver_probe() is that you can make the probe function __init. Signed-off-by: Jean Delvare Cc: Bryan Wu Cc: Richard Purdie Signed-off-by: Bryan Wu diff --git a/drivers/leds/leds-clevo-mail.c b/drivers/leds/leds-clevo-mail.c index 19202f5..f58a354 100644 --- a/drivers/leds/leds-clevo-mail.c +++ b/drivers/leds/leds-clevo-mail.c @@ -153,7 +153,7 @@ static struct led_classdev clevo_mail_led = { .flags = LED_CORE_SUSPENDRESUME, }; -static int clevo_mail_led_probe(struct platform_device *pdev) +static int __init clevo_mail_led_probe(struct platform_device *pdev) { return led_classdev_register(&pdev->dev, &clevo_mail_led); } @@ -165,7 +165,6 @@ static int clevo_mail_led_remove(struct platform_device *pdev) } static struct platform_driver clevo_mail_led_driver = { - .probe = clevo_mail_led_probe, .remove = clevo_mail_led_remove, .driver = { .name = KBUILD_MODNAME, -- cgit v0.10.2 From 392369019eb96e914234ea21eda806cb51a1073e Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 6 Apr 2014 15:20:03 -0700 Subject: leds: leds-pwm: properly clean up after probe failure When probing with DT, we add each LED one at a time. If we find a LED without a PWM device (because it is not available yet) we fail the initialisation, unregister previous LEDs, and then by way of managed resources, we free the structure. The problem with this is we may have a scheduled and active work_struct in this structure, and this results in a nasty kernel oops. We need to cancel this work_struct properly upon cleanup - and the cleanup we require is the same cleanup as we do when the LED platform device is removed. Rather than writing this same code three times, move it into a separate function and use it in all three places. Fixes: c971ff185f64 ("leds: leds-pwm: Defer led_pwm_set() if PWM can sleep") Signed-off-by: Russell King Signed-off-by: Bryan Wu diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index dd178736..7d0aaed 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c @@ -83,6 +83,15 @@ static inline size_t sizeof_pwm_leds_priv(int num_leds) (sizeof(struct led_pwm_data) * num_leds); } +static void led_pwm_cleanup(struct led_pwm_priv *priv) +{ + while (priv->num_leds--) { + led_classdev_unregister(&priv->leds[priv->num_leds].cdev); + if (priv->leds[priv->num_leds].can_sleep) + cancel_work_sync(&priv->leds[priv->num_leds].work); + } +} + static int led_pwm_create_of(struct platform_device *pdev, struct led_pwm_priv *priv) { @@ -130,8 +139,7 @@ static int led_pwm_create_of(struct platform_device *pdev, return 0; err: - while (priv->num_leds--) - led_classdev_unregister(&priv->leds[priv->num_leds].cdev); + led_pwm_cleanup(priv); return ret; } @@ -199,8 +207,8 @@ static int led_pwm_probe(struct platform_device *pdev) return 0; err: - while (i--) - led_classdev_unregister(&priv->leds[i].cdev); + priv->num_leds = i; + led_pwm_cleanup(priv); return ret; } @@ -208,13 +216,8 @@ err: static int led_pwm_remove(struct platform_device *pdev) { struct led_pwm_priv *priv = platform_get_drvdata(pdev); - int i; - for (i = 0; i < priv->num_leds; i++) { - led_classdev_unregister(&priv->leds[i].cdev); - if (priv->leds[i].can_sleep) - cancel_work_sync(&priv->leds[i].work); - } + led_pwm_cleanup(priv); return 0; } -- cgit v0.10.2 From 14f5716bc23cebb627b40a2808e9f04eb77ab206 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Fri, 4 Apr 2014 10:01:13 -0700 Subject: leds: make sure we unregister a trigger only once Currently, we may attempt to unregister a trigger more than once, for example when we receive two consecutive reboot notifications, or when we do a regular unregistration plus reboot notification. This leads to the following error since we try to delete the list node twice: [ 2780.254922] WARNING: CPU: 0 PID: 13764 at lib/list_debug.c:53 __list_del_entry+0x3e/0xe0() [ 2780.265559] list_del corruption, ffffffffa5eb6470->next is LIST_POISON1 (dead000000100100) [ 2780.271710] Modules linked in: [ 2780.274156] CPU: 0 PID: 13764 Comm: kworker/0:2 Tainted: G W 3.14.0-next-20140403-sasha-00012-gef5fa7d-dirty #373 [ 2780.283063] Workqueue: events do_poweroff [ 2780.285644] 0000000000000009 ffff8800330dbb38 ffffffffa34bfa33 0000000000002fe0 [ 2780.291571] ffff8800330dbb88 ffff8800330dbb78 ffffffffa015a37c ffff8800330dbb68 [ 2780.296670] ffffffffa5eb6470 0000000000000000 ffffffffa5eb6400 ffffffffa5ad7430 [ 2780.299756] Call Trace: [ 2780.301530] dump_stack (lib/dump_stack.c:52) [ 2780.303802] warn_slowpath_common (kernel/panic.c:418) [ 2780.306151] warn_slowpath_fmt (kernel/panic.c:433) [ 2780.308156] __list_del_entry (lib/list_debug.c:51 (discriminator 1)) [ 2780.310800] list_del (lib/list_debug.c:78) [ 2780.313175] led_trigger_unregister (drivers/leds/led-triggers.c:225) [ 2780.315599] heartbeat_reboot_notifier (drivers/leds/trigger/ledtrig-heartbeat.c:119) [ 2780.317247] notifier_call_chain (kernel/notifier.c:95) [ 2780.320014] __blocking_notifier_call_chain (kernel/notifier.c:316) [ 2780.323263] blocking_notifier_call_chain (kernel/notifier.c:326) [ 2780.326096] kernel_power_off (include/linux/kmod.h:95 kernel/reboot.c:153 kernel/reboot.c:179) [ 2780.327883] do_poweroff (kernel/power/poweroff.c:23) [ 2780.330748] process_one_work (kernel/workqueue.c:2221 include/linux/jump_label.h:105 include/trace/events/workqueue.h:111 kernel/workqueue.c:2226) [ 2780.333027] ? process_one_work (include/linux/workqueue.h:186 kernel/workqueue.c:611 kernel/workqueue.c:638 kernel/workqueue.c:2214) [ 2780.335487] process_scheduled_works (include/linux/list.h:188 kernel/workqueue.c:2277) [ 2780.337101] worker_thread (kernel/workqueue.c:2352) [ 2780.338712] ? rescuer_thread (kernel/workqueue.c:2297) [ 2780.341326] kthread (kernel/kthread.c:219) [ 2780.343446] ? kthread_create_on_node (kernel/kthread.c:185) [ 2780.345733] ret_from_fork (arch/x86/kernel/entry_64.S:555) [ 2780.347168] ? kthread_create_on_node (kernel/kthread.c:185) Prevent it by making sure we don't attempt to unregister a trigger that is not in the triggers list. Signed-off-by: Sasha Levin Signed-off-by: Bryan Wu diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index df1a7c1..c3734f1 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -219,9 +219,12 @@ void led_trigger_unregister(struct led_trigger *trig) { struct led_classdev *led_cdev; + if (list_empty_careful(&trig->next_trig)) + return; + /* Remove from the list of led triggers */ down_write(&triggers_list_lock); - list_del(&trig->next_trig); + list_del_init(&trig->next_trig); up_write(&triggers_list_lock); /* Remove anyone actively using this trigger */ -- cgit v0.10.2