18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Kernel Panic LED Trigger
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2016 Ezequiel Garcia <ezequiel@vanguardiasur.com.ar>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/init.h>
108c2ecf20Sopenharmony_ci#include <linux/notifier.h>
118c2ecf20Sopenharmony_ci#include <linux/leds.h>
128c2ecf20Sopenharmony_ci#include "../leds.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cistatic struct led_trigger *trigger;
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/*
178c2ecf20Sopenharmony_ci * This is called in a special context by the atomic panic
188c2ecf20Sopenharmony_ci * notifier. This means the trigger can be changed without
198c2ecf20Sopenharmony_ci * worrying about locking.
208c2ecf20Sopenharmony_ci */
218c2ecf20Sopenharmony_cistatic void led_trigger_set_panic(struct led_classdev *led_cdev)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	struct led_trigger *trig;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	list_for_each_entry(trig, &trigger_list, next_trig) {
268c2ecf20Sopenharmony_ci		if (strcmp("panic", trig->name))
278c2ecf20Sopenharmony_ci			continue;
288c2ecf20Sopenharmony_ci		if (led_cdev->trigger)
298c2ecf20Sopenharmony_ci			list_del(&led_cdev->trig_list);
308c2ecf20Sopenharmony_ci		list_add_tail(&led_cdev->trig_list, &trig->led_cdevs);
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci		/* Avoid the delayed blink path */
338c2ecf20Sopenharmony_ci		led_cdev->blink_delay_on = 0;
348c2ecf20Sopenharmony_ci		led_cdev->blink_delay_off = 0;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci		led_cdev->trigger = trig;
378c2ecf20Sopenharmony_ci		if (trig->activate)
388c2ecf20Sopenharmony_ci			trig->activate(led_cdev);
398c2ecf20Sopenharmony_ci		break;
408c2ecf20Sopenharmony_ci	}
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic int led_trigger_panic_notifier(struct notifier_block *nb,
448c2ecf20Sopenharmony_ci				      unsigned long code, void *unused)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	struct led_classdev *led_cdev;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	list_for_each_entry(led_cdev, &leds_list, node)
498c2ecf20Sopenharmony_ci		if (led_cdev->flags & LED_PANIC_INDICATOR)
508c2ecf20Sopenharmony_ci			led_trigger_set_panic(led_cdev);
518c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic struct notifier_block led_trigger_panic_nb = {
558c2ecf20Sopenharmony_ci	.notifier_call = led_trigger_panic_notifier,
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic long led_panic_blink(int state)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	led_trigger_event(trigger, state ? LED_FULL : LED_OFF);
618c2ecf20Sopenharmony_ci	return 0;
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic int __init ledtrig_panic_init(void)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	led_trigger_register_simple("panic", &trigger);
678c2ecf20Sopenharmony_ci	if (!trigger)
688c2ecf20Sopenharmony_ci		return -ENOMEM;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	atomic_notifier_chain_register(&panic_notifier_list,
718c2ecf20Sopenharmony_ci				       &led_trigger_panic_nb);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	panic_blink = led_panic_blink;
748c2ecf20Sopenharmony_ci	return 0;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_cidevice_initcall(ledtrig_panic_init);
77