diff --git a/Documentation/devicetree/bindings/leds/common.txt b/Documentation/devicetree/bindings/leds/common.txt index 68419843e32fc83c28ba7ecb5b0a25c2f4c2efb4..af10678ea2f651a8b0c47881ab389685af180ece 100644 --- a/Documentation/devicetree/bindings/leds/common.txt +++ b/Documentation/devicetree/bindings/leds/common.txt @@ -37,6 +37,9 @@ Optional properties for child nodes: property is mandatory for the LEDs in the non-flash modes (e.g. torch or indicator). +- panic-indicator : This property specifies that the LED should be used, + if at all possible, as a panic indicator. + Required properties for flash LED child nodes: - flash-max-microamp : Maximum flash LED supply current in microamperes. - flash-max-timeout-us : Maximum timeout in microseconds after which the flash diff --git a/Documentation/devicetree/bindings/leds/leds-gpio.txt b/Documentation/devicetree/bindings/leds/leds-gpio.txt index fea1ebfe24a9573f2023b9ada78cec925d35e799..cbbeb1850910ed602e883b64e61254c91ad7ceda 100644 --- a/Documentation/devicetree/bindings/leds/leds-gpio.txt +++ b/Documentation/devicetree/bindings/leds/leds-gpio.txt @@ -23,6 +23,8 @@ LED sub-node properties: property is not present. - retain-state-suspended: (optional) The suspend state can be retained.Such as charge-led gpio. +- panic-indicator : (optional) + see Documentation/devicetree/bindings/leds/common.txt Examples: diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 225147863e024fcb93ac6b011976255af1486f29..5ae28340a98b4f53d288194a9d1bed1a08518a35 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -413,10 +413,11 @@ config LEDS_INTEL_SS4200 tristate "LED driver for Intel NAS SS4200 series" depends on LEDS_CLASS depends on PCI && DMI + depends on X86 help This option enables support for the Intel SS4200 series of - Network Attached Storage servers. You may control the hard - drive or power LEDs on the front panel. Using this driver + Network Attached Storage servers. You may control the hard + drive or power LEDs on the front panel. Using this driver can stop the front LED from blinking after startup. config LEDS_LT3593 diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index 2181581795d306c125f0cd0a7a9e14e744d07326..55fa65e1ae030dc2840d7d53e74975f4c6eaf311 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -26,7 +26,7 @@ * Nests outside led_cdev->trigger_lock */ static DECLARE_RWSEM(triggers_list_lock); -static LIST_HEAD(trigger_list); +LIST_HEAD(trigger_list); /* Used by LED Class */ diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 61143f55597e42f9b45fe4efe33a1c51614b22b4..8229f063b483c77cf993891cb2bc2e7e9239ee6a 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -127,6 +127,8 @@ static int create_gpio_led(const struct gpio_led *template, led_dat->cdev.brightness = state ? LED_FULL : LED_OFF; if (!template->retain_state_suspended) led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; + if (template->panic_indicator) + led_dat->cdev.flags |= LED_PANIC_INDICATOR; ret = gpiod_direction_output(led_dat->gpiod, state); if (ret < 0) @@ -200,6 +202,8 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev) if (fwnode_property_present(child, "retain-state-suspended")) led.retain_state_suspended = 1; + if (fwnode_property_present(child, "panic-indicator")) + led.panic_indicator = 1; ret = create_gpio_led(&led, &priv->leds[priv->num_leds], dev, NULL); diff --git a/drivers/leds/leds-ss4200.c b/drivers/leds/leds-ss4200.c index 046cb70087452ef8cea540b073ae0fa71ceeef94..732eb86bc1a5ca2307971ed4e9eb0cf80227e6a2 100644 --- a/drivers/leds/leds-ss4200.c +++ b/drivers/leds/leds-ss4200.c @@ -101,6 +101,19 @@ static struct dmi_system_id nas_led_whitelist[] __initdata = { DMI_MATCH(DMI_PRODUCT_VERSION, "1.00.00") } }, + { + /* + * FUJITSU SIEMENS SCALEO Home Server/SS4200-E + * BIOS V090L 12/19/2007 + */ + .callback = ss4200_led_dmi_callback, + .ident = "Fujitsu Siemens SCALEO Home Server", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "SCALEO Home Server"), + DMI_MATCH(DMI_PRODUCT_VERSION, "1.00.00") + } + }, {} }; diff --git a/drivers/leds/leds-tca6507.c b/drivers/leds/leds-tca6507.c index c548ea10f0f036648d1d357f998641551ff3f2a6..45222a7f4f75636d628596d90d9bdb29acf0b453 100644 --- a/drivers/leds/leds-tca6507.c +++ b/drivers/leds/leds-tca6507.c @@ -327,6 +327,8 @@ static void set_times(struct tca6507_chip *tca, int bank) int result; result = choose_times(tca->bank[bank].ontime, &c1, &c2); + if (result < 0) + return; dev_dbg(&tca->client->dev, "Chose on times %d(%d) %d(%d) for %dms\n", c1, time_codes[c1], diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h index db3f20da7221738920bf49bd35fbc13b1768e65d..7d38e6b9a740b2eecbf7d080895204d354c1a71a 100644 --- a/drivers/leds/leds.h +++ b/drivers/leds/leds.h @@ -30,5 +30,6 @@ void led_set_brightness_nosleep(struct led_classdev *led_cdev, extern struct rw_semaphore leds_list_lock; extern struct list_head leds_list; +extern struct list_head trigger_list; #endif /* __LEDS_H_INCLUDED */ diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig index 5bda6a9b56bbd90b4a3749f87bc0c6fda8dd5034..9893d911390daa4dd77986065ebde50b517d6b72 100644 --- a/drivers/leds/trigger/Kconfig +++ b/drivers/leds/trigger/Kconfig @@ -41,6 +41,14 @@ config LEDS_TRIGGER_IDE_DISK This allows LEDs to be controlled by IDE disk activity. If unsure, say Y. +config LEDS_TRIGGER_MTD + bool "LED MTD (NAND/NOR) Trigger" + depends on MTD + depends on LEDS_TRIGGERS + help + This allows LEDs to be controlled by MTD activity. + If unsure, say N. + config LEDS_TRIGGER_HEARTBEAT tristate "LED Heartbeat Trigger" depends on LEDS_TRIGGERS @@ -108,4 +116,14 @@ config LEDS_TRIGGER_CAMERA This enables direct flash/torch on/off by the driver, kernel space. If unsure, say Y. +config LEDS_TRIGGER_PANIC + bool "LED Panic Trigger" + depends on LEDS_TRIGGERS + help + This allows LEDs to be configured to blink on a kernel panic. + Enabling this option will allow to mark certain LEDs as panic indicators, + allowing to blink them on a kernel panic, even if they are set to + a different trigger. + If unsure, say Y. + endif # LEDS_TRIGGERS diff --git a/drivers/leds/trigger/Makefile b/drivers/leds/trigger/Makefile index 1abf48dacf7ebfcfb8208f7ae7bdf29d7c11ba32..8cc64a4f4e255c1dd9fafde1ccd72d5a08ccbd5b 100644 --- a/drivers/leds/trigger/Makefile +++ b/drivers/leds/trigger/Makefile @@ -1,6 +1,7 @@ obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o obj-$(CONFIG_LEDS_TRIGGER_ONESHOT) += ledtrig-oneshot.o obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) += ledtrig-ide-disk.o +obj-$(CONFIG_LEDS_TRIGGER_MTD) += ledtrig-mtd.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 @@ -8,3 +9,4 @@ 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 obj-$(CONFIG_LEDS_TRIGGER_CAMERA) += ledtrig-camera.o +obj-$(CONFIG_LEDS_TRIGGER_PANIC) += ledtrig-panic.o diff --git a/drivers/leds/trigger/ledtrig-ide-disk.c b/drivers/leds/trigger/ledtrig-ide-disk.c index c02a3ac3cd2bf124777e44741142a35a90d2f19d..15123d3892403afd09fb890b00db6ee89288bae4 100644 --- a/drivers/leds/trigger/ledtrig-ide-disk.c +++ b/drivers/leds/trigger/ledtrig-ide-disk.c @@ -18,10 +18,11 @@ #define BLINK_DELAY 30 DEFINE_LED_TRIGGER(ledtrig_ide); -static unsigned long ide_blink_delay = BLINK_DELAY; void ledtrig_ide_activity(void) { + unsigned long ide_blink_delay = BLINK_DELAY; + led_trigger_blink_oneshot(ledtrig_ide, &ide_blink_delay, &ide_blink_delay, 0); } diff --git a/drivers/leds/trigger/ledtrig-mtd.c b/drivers/leds/trigger/ledtrig-mtd.c new file mode 100644 index 0000000000000000000000000000000000000000..99b5b0a4d8260cfa4fe78f4dae349e96caf5cf32 --- /dev/null +++ b/drivers/leds/trigger/ledtrig-mtd.c @@ -0,0 +1,45 @@ +/* + * LED MTD trigger + * + * Copyright 2016 Ezequiel Garcia + * + * Based on LED IDE-Disk Activity Trigger + * + * Copyright 2006 Openedhand Ltd. + * + * Author: Richard Purdie + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include + +#define BLINK_DELAY 30 + +DEFINE_LED_TRIGGER(ledtrig_mtd); +DEFINE_LED_TRIGGER(ledtrig_nand); + +void ledtrig_mtd_activity(void) +{ + unsigned long blink_delay = BLINK_DELAY; + + led_trigger_blink_oneshot(ledtrig_mtd, + &blink_delay, &blink_delay, 0); + led_trigger_blink_oneshot(ledtrig_nand, + &blink_delay, &blink_delay, 0); +} +EXPORT_SYMBOL(ledtrig_mtd_activity); + +static int __init ledtrig_mtd_init(void) +{ + led_trigger_register_simple("mtd", &ledtrig_mtd); + led_trigger_register_simple("nand-disk", &ledtrig_nand); + + return 0; +} +device_initcall(ledtrig_mtd_init); diff --git a/drivers/leds/trigger/ledtrig-panic.c b/drivers/leds/trigger/ledtrig-panic.c new file mode 100644 index 0000000000000000000000000000000000000000..d735526b9db4f79c3e80f220f019994f13dcf35c --- /dev/null +++ b/drivers/leds/trigger/ledtrig-panic.c @@ -0,0 +1,77 @@ +/* + * Kernel Panic LED Trigger + * + * Copyright 2016 Ezequiel Garcia + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include "../leds.h" + +static struct led_trigger *trigger; + +/* + * This is called in a special context by the atomic panic + * notifier. This means the trigger can be changed without + * worrying about locking. + */ +static void led_trigger_set_panic(struct led_classdev *led_cdev) +{ + struct led_trigger *trig; + + list_for_each_entry(trig, &trigger_list, next_trig) { + if (strcmp("panic", trig->name)) + continue; + if (led_cdev->trigger) + list_del(&led_cdev->trig_list); + list_add_tail(&led_cdev->trig_list, &trig->led_cdevs); + + /* Avoid the delayed blink path */ + led_cdev->blink_delay_on = 0; + led_cdev->blink_delay_off = 0; + + led_cdev->trigger = trig; + if (trig->activate) + trig->activate(led_cdev); + break; + } +} + +static int led_trigger_panic_notifier(struct notifier_block *nb, + unsigned long code, void *unused) +{ + struct led_classdev *led_cdev; + + list_for_each_entry(led_cdev, &leds_list, node) + if (led_cdev->flags & LED_PANIC_INDICATOR) + led_trigger_set_panic(led_cdev); + return NOTIFY_DONE; +} + +static struct notifier_block led_trigger_panic_nb = { + .notifier_call = led_trigger_panic_notifier, +}; + +static long led_panic_blink(int state) +{ + led_trigger_event(trigger, state ? LED_FULL : LED_OFF); + return 0; +} + +static int __init ledtrig_panic_init(void) +{ + atomic_notifier_chain_register(&panic_notifier_list, + &led_trigger_panic_nb); + + led_trigger_register_simple("panic", &trigger); + panic_blink = led_panic_blink; + return 0; +} +device_initcall(ledtrig_panic_init); diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 309625130b21b751e5fa42bb4693800a8327001b..bee180bd11e79fe236e0287647986fe81fea864c 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -862,6 +863,7 @@ int mtd_erase(struct mtd_info *mtd, struct erase_info *instr) mtd_erase_callback(instr); return 0; } + ledtrig_mtd_activity(); return mtd->_erase(mtd, instr); } EXPORT_SYMBOL_GPL(mtd_erase); @@ -925,6 +927,7 @@ int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, if (!len) return 0; + ledtrig_mtd_activity(); /* * In the absence of an error, drivers return a non-negative integer * representing the maximum number of bitflips that were corrected on @@ -949,6 +952,7 @@ int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, return -EROFS; if (!len) return 0; + ledtrig_mtd_activity(); return mtd->_write(mtd, to, len, retlen, buf); } EXPORT_SYMBOL_GPL(mtd_write); @@ -982,6 +986,8 @@ int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) ops->retlen = ops->oobretlen = 0; if (!mtd->_read_oob) return -EOPNOTSUPP; + + ledtrig_mtd_activity(); /* * In cases where ops->datbuf != NULL, mtd->_read_oob() has semantics * similar to mtd->_read(), returning a non-negative integer @@ -997,6 +1003,19 @@ int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) } EXPORT_SYMBOL_GPL(mtd_read_oob); +int mtd_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + ops->retlen = ops->oobretlen = 0; + if (!mtd->_write_oob) + return -EOPNOTSUPP; + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + ledtrig_mtd_activity(); + return mtd->_write_oob(mtd, to, ops); +} +EXPORT_SYMBOL_GPL(mtd_write_oob); + /* * Method to access the protection register area, present in some flash * devices. The user data is one time programmable but the factory data is read diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 557b8462f55ed6f46961d2201affedd2c2746401..ba4f603e05375b32de7bf01f3bde05967bc0268e 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -43,7 +43,6 @@ #include #include #include -#include #include #include #include @@ -97,12 +96,6 @@ static int nand_get_device(struct mtd_info *mtd, int new_state); static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops); -/* - * For devices which display every fart in the system on a separate LED. Is - * compiled away when LED support is disabled. - */ -DEFINE_LED_TRIGGER(nand_led_trigger); - static int check_offs_len(struct mtd_info *mtd, loff_t ofs, uint64_t len) { @@ -540,19 +533,16 @@ void nand_wait_ready(struct mtd_info *mtd) if (in_interrupt() || oops_in_progress) return panic_nand_wait_ready(mtd, timeo); - led_trigger_event(nand_led_trigger, LED_FULL); /* Wait until command is processed or timeout occurs */ timeo = jiffies + msecs_to_jiffies(timeo); do { if (chip->dev_ready(mtd)) - goto out; + return; cond_resched(); } while (time_before(jiffies, timeo)); if (!chip->dev_ready(mtd)) pr_warn_ratelimited("timeout while waiting for chip to become ready\n"); -out: - led_trigger_event(nand_led_trigger, LED_OFF); } EXPORT_SYMBOL_GPL(nand_wait_ready); @@ -885,8 +875,6 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) int status; unsigned long timeo = 400; - led_trigger_event(nand_led_trigger, LED_FULL); - /* * Apply this short delay always to ensure that we do wait tWB in any * case on any machine. @@ -910,7 +898,6 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) cond_resched(); } while (time_before(jiffies, timeo)); } - led_trigger_event(nand_led_trigger, LED_OFF); status = (int)chip->read_byte(mtd); /* This can happen if in case of timeout or buggy dev_ready */ @@ -4466,20 +4453,6 @@ void nand_release(struct mtd_info *mtd) } EXPORT_SYMBOL_GPL(nand_release); -static int __init nand_base_init(void) -{ - led_trigger_register_simple("nand-disk", &nand_led_trigger); - return 0; -} - -static void __exit nand_base_exit(void) -{ - led_trigger_unregister_simple(nand_led_trigger); -} - -module_init(nand_base_init); -module_exit(nand_base_exit); - MODULE_LICENSE("GPL"); MODULE_AUTHOR("Steven J. Hill "); MODULE_AUTHOR("Thomas Gleixner "); diff --git a/include/linux/leds.h b/include/linux/leds.h index f203a8f89d30d0f3e214b43d2c72efea1bdad9f9..d2b13066e78155ccacc072afcd75a2ae2bcb23fa 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -50,6 +50,7 @@ struct led_classdev { #define LED_SYSFS_DISABLE (1 << 22) #define LED_DEV_CAP_FLASH (1 << 23) #define LED_HW_PLUGGABLE (1 << 24) +#define LED_PANIC_INDICATOR (1 << 25) /* Set LED brightness level * Must not sleep. Use brightness_set_blocking for drivers @@ -329,6 +330,12 @@ extern void ledtrig_ide_activity(void); static inline void ledtrig_ide_activity(void) {} #endif +#ifdef CONFIG_LEDS_TRIGGER_MTD +extern void ledtrig_mtd_activity(void); +#else +static inline void ledtrig_mtd_activity(void) {} +#endif + #if defined(CONFIG_LEDS_TRIGGER_CAMERA) || defined(CONFIG_LEDS_TRIGGER_CAMERA_MODULE) extern void ledtrig_flash_ctrl(bool on); extern void ledtrig_torch_ctrl(bool on); @@ -358,6 +365,7 @@ struct gpio_led { unsigned gpio; unsigned active_low : 1; unsigned retain_state_suspended : 1; + unsigned panic_indicator : 1; unsigned default_state : 2; /* default_state should be one of LEDS_GPIO_DEFSTATE_(ON|OFF|KEEP) */ struct gpio_desc *gpiod; diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 771272187316b3c1bc5e5f3cbd9b07072c7bd55c..ef9fea4fc40080dd5832d70405bbd87e2a605339 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -283,17 +283,7 @@ int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops); - -static inline int mtd_write_oob(struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops) -{ - ops->retlen = ops->oobretlen = 0; - if (!mtd->_write_oob) - return -EOPNOTSUPP; - if (!(mtd->flags & MTD_WRITEABLE)) - return -EROFS; - return mtd->_write_oob(mtd, to, ops); -} +int mtd_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops); int mtd_get_fact_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen, struct otp_info *buf);