162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Kernel Panic LED Trigger
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2016 Ezequiel Garcia <ezequiel@vanguardiasur.com.ar>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/init.h>
1062306a36Sopenharmony_ci#include <linux/notifier.h>
1162306a36Sopenharmony_ci#include <linux/panic_notifier.h>
1262306a36Sopenharmony_ci#include <linux/leds.h>
1362306a36Sopenharmony_ci#include "../leds.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistatic struct led_trigger *trigger;
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/*
1862306a36Sopenharmony_ci * This is called in a special context by the atomic panic
1962306a36Sopenharmony_ci * notifier. This means the trigger can be changed without
2062306a36Sopenharmony_ci * worrying about locking.
2162306a36Sopenharmony_ci */
2262306a36Sopenharmony_cistatic void led_trigger_set_panic(struct led_classdev *led_cdev)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	struct led_trigger *trig;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	list_for_each_entry(trig, &trigger_list, next_trig) {
2762306a36Sopenharmony_ci		if (strcmp("panic", trig->name))
2862306a36Sopenharmony_ci			continue;
2962306a36Sopenharmony_ci		if (led_cdev->trigger)
3062306a36Sopenharmony_ci			list_del(&led_cdev->trig_list);
3162306a36Sopenharmony_ci		list_add_tail(&led_cdev->trig_list, &trig->led_cdevs);
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci		/* Avoid the delayed blink path */
3462306a36Sopenharmony_ci		led_cdev->blink_delay_on = 0;
3562306a36Sopenharmony_ci		led_cdev->blink_delay_off = 0;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci		led_cdev->trigger = trig;
3862306a36Sopenharmony_ci		if (trig->activate)
3962306a36Sopenharmony_ci			trig->activate(led_cdev);
4062306a36Sopenharmony_ci		break;
4162306a36Sopenharmony_ci	}
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic int led_trigger_panic_notifier(struct notifier_block *nb,
4562306a36Sopenharmony_ci				      unsigned long code, void *unused)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	struct led_classdev *led_cdev;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	list_for_each_entry(led_cdev, &leds_list, node)
5062306a36Sopenharmony_ci		if (led_cdev->flags & LED_PANIC_INDICATOR)
5162306a36Sopenharmony_ci			led_trigger_set_panic(led_cdev);
5262306a36Sopenharmony_ci	return NOTIFY_DONE;
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic struct notifier_block led_trigger_panic_nb = {
5662306a36Sopenharmony_ci	.notifier_call = led_trigger_panic_notifier,
5762306a36Sopenharmony_ci};
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic long led_panic_blink(int state)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	led_trigger_event(trigger, state ? LED_FULL : LED_OFF);
6262306a36Sopenharmony_ci	return 0;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic int __init ledtrig_panic_init(void)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	led_trigger_register_simple("panic", &trigger);
6862306a36Sopenharmony_ci	if (!trigger)
6962306a36Sopenharmony_ci		return -ENOMEM;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	atomic_notifier_chain_register(&panic_notifier_list,
7262306a36Sopenharmony_ci				       &led_trigger_panic_nb);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	panic_blink = led_panic_blink;
7562306a36Sopenharmony_ci	return 0;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_cidevice_initcall(ledtrig_panic_init);
78