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