From e62a02115b2079360403af622c743d5a56835127 Mon Sep 17 00:00:00 2001
From: Su Baocheng <baocheng.su@siemens.com>
Date: Tue, 22 Dec 2020 15:05:56 +0800
Subject: [PATCH] WIP: feat:extend led panic-indicator on and off

WIP because upstream strategy is still under discussion.

Signed-off-by: Gao Nian <nian.gao@siemens.com>
[Jan: ported and fixed non-panic cases]
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
---
 drivers/leds/led-triggers.c          | 10 ++++++++--
 drivers/leds/leds-gpio.c             | 12 ++++++++++--
 drivers/leds/trigger/ledtrig-panic.c |  5 ++++-
 include/linux/leds.h                 |  8 +++++++-
 4 files changed, 29 insertions(+), 6 deletions(-)

diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
index 4e7b78a84149..30527da8db97 100644
--- a/drivers/leds/led-triggers.c
+++ b/drivers/leds/led-triggers.c
@@ -384,8 +384,14 @@ void led_trigger_event(struct led_trigger *trig,
 		return;
 
 	read_lock_irqsave(&trig->leddev_list_lock, flags);
-	list_for_each_entry(led_cdev, &trig->led_cdevs, trig_list)
-		led_set_brightness(led_cdev, brightness);
+	list_for_each_entry(led_cdev, &trig->led_cdevs, trig_list) {
+		if (led_cdev->flags & LED_PANIC_INDICATOR_OFF)
+			led_set_brightness(led_cdev, LED_OFF);
+		else if (led_cdev->flags & LED_PANIC_INDICATOR_ON)
+			led_set_brightness(led_cdev, LED_FULL);
+		else
+			led_set_brightness(led_cdev, brightness);
+	}
 	read_unlock_irqrestore(&trig->leddev_list_lock, flags);
 }
 EXPORT_SYMBOL_GPL(led_trigger_event);
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c
index 93f5b1b60fde..8ad1b5dddc71 100644
--- a/drivers/leds/leds-gpio.c
+++ b/drivers/leds/leds-gpio.c
@@ -99,8 +99,12 @@ 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)
+	if (template->panic_indicator == LEDS_PANICINDICATOR_BLINK)
 		led_dat->cdev.flags |= LED_PANIC_INDICATOR;
+	else if (template->panic_indicator == LEDS_PANICINDICATOR_OFF)
+		led_dat->cdev.flags |= LED_PANIC_INDICATOR_OFF;
+	else if (template->panic_indicator == LEDS_PANICINDICATOR_ON)
+		led_dat->cdev.flags |= LED_PANIC_INDICATOR_ON;
 	if (template->retain_state_shutdown)
 		led_dat->cdev.flags |= LED_RETAIN_AT_SHUTDOWN;
 
@@ -175,7 +179,11 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev)
 		if (fwnode_property_present(child, "retain-state-shutdown"))
 			led.retain_state_shutdown = 1;
 		if (fwnode_property_present(child, "panic-indicator"))
-			led.panic_indicator = 1;
+			led.panic_indicator = LEDS_PANICINDICATOR_BLINK;
+		else if (fwnode_property_present(child, "panic-indicator-off"))
+			led.panic_indicator = LEDS_PANICINDICATOR_OFF;
+		else if (fwnode_property_present(child, "panic-indicator-on"))
+			led.panic_indicator = LEDS_PANICINDICATOR_ON;
 
 		ret = create_gpio_led(&led, led_dat, dev, child, NULL);
 		if (ret < 0) {
diff --git a/drivers/leds/trigger/ledtrig-panic.c b/drivers/leds/trigger/ledtrig-panic.c
index 5751cd032f9d..12b332ecd86e 100644
--- a/drivers/leds/trigger/ledtrig-panic.c
+++ b/drivers/leds/trigger/ledtrig-panic.c
@@ -32,6 +32,7 @@ static void led_trigger_set_panic(struct led_classdev *led_cdev)
 		/* Avoid the delayed blink path */
 		led_cdev->blink_delay_on = 0;
 		led_cdev->blink_delay_off = 0;
+		led_cdev->work_flags = 0;
 
 		led_cdev->trigger = trig;
 		if (trig->activate)
@@ -46,7 +47,9 @@ static int led_trigger_panic_notifier(struct notifier_block *nb,
 	struct led_classdev *led_cdev;
 
 	list_for_each_entry(led_cdev, &leds_list, node)
-		if (led_cdev->flags & LED_PANIC_INDICATOR)
+		if (led_cdev->flags & (LED_PANIC_INDICATOR |
+				       LED_PANIC_INDICATOR_OFF |
+				       LED_PANIC_INDICATOR_ON))
 			led_trigger_set_panic(led_cdev);
 	return NOTIFY_DONE;
 }
diff --git a/include/linux/leds.h b/include/linux/leds.h
index 6a8d6409c993..d84423d42f89 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -79,6 +79,8 @@ struct led_classdev {
 #define LED_BRIGHT_HW_CHANGED	BIT(21)
 #define LED_RETAIN_AT_SHUTDOWN	BIT(22)
 #define LED_INIT_DEFAULT_TRIGGER BIT(23)
+#define LED_PANIC_INDICATOR_OFF BIT(24)
+#define LED_PANIC_INDICATOR_ON  BIT(25)
 
 	/* set_brightness_work / blink_timer flags, atomic, private. */
 	unsigned long		work_flags;
@@ -516,7 +518,7 @@ struct gpio_led {
 	unsigned 	gpio;
 	unsigned	active_low : 1;
 	unsigned	retain_state_suspended : 1;
-	unsigned	panic_indicator : 1;
+	unsigned	panic_indicator : 2;
 	unsigned	default_state : 2;
 	unsigned	retain_state_shutdown : 1;
 	/* default_state should be one of LEDS_GPIO_DEFSTATE_(ON|OFF|KEEP) */
@@ -526,6 +528,10 @@ struct gpio_led {
 #define LEDS_GPIO_DEFSTATE_ON		1
 #define LEDS_GPIO_DEFSTATE_KEEP		2
 
+#define LEDS_PANICINDICATOR_BLINK   1
+#define LEDS_PANICINDICATOR_OFF     2
+#define LEDS_PANICINDICATOR_ON      3
+
 struct gpio_led_platform_data {
 	int 		num_leds;
 	const struct gpio_led *leds;
