16861d27cSMihai Carabas // SPDX-License-Identifier: GPL-2.0+ 26861d27cSMihai Carabas /* 36861d27cSMihai Carabas * Pvpanic Device Support 46861d27cSMihai Carabas * 56861d27cSMihai Carabas * Copyright (C) 2013 Fujitsu. 66861d27cSMihai Carabas * Copyright (C) 2018 ZTE. 76861d27cSMihai Carabas * Copyright (C) 2021 Oracle. 86861d27cSMihai Carabas */ 96861d27cSMihai Carabas 106861d27cSMihai Carabas #include <linux/io.h> 116861d27cSMihai Carabas #include <linux/kernel.h> 126861d27cSMihai Carabas #include <linux/kexec.h> 136861d27cSMihai Carabas #include <linux/mod_devicetable.h> 146861d27cSMihai Carabas #include <linux/module.h> 156861d27cSMihai Carabas #include <linux/platform_device.h> 16f39650deSAndy Shevchenko #include <linux/panic_notifier.h> 176861d27cSMihai Carabas #include <linux/types.h> 18b3c0f877SMihai Carabas #include <linux/cdev.h> 19b3c0f877SMihai Carabas #include <linux/list.h> 206861d27cSMihai Carabas 216861d27cSMihai Carabas #include <uapi/misc/pvpanic.h> 226861d27cSMihai Carabas 236861d27cSMihai Carabas #include "pvpanic.h" 246861d27cSMihai Carabas 256861d27cSMihai Carabas MODULE_AUTHOR("Mihai Carabas <mihai.carabas@oracle.com>"); 266861d27cSMihai Carabas MODULE_DESCRIPTION("pvpanic device driver"); 276861d27cSMihai Carabas MODULE_LICENSE("GPL"); 286861d27cSMihai Carabas 29391e2415SYueHaibing static struct list_head pvpanic_list; 30391e2415SYueHaibing static spinlock_t pvpanic_lock; 316861d27cSMihai Carabas 326861d27cSMihai Carabas static void 336861d27cSMihai Carabas pvpanic_send_event(unsigned int event) 346861d27cSMihai Carabas { 35b3c0f877SMihai Carabas struct pvpanic_instance *pi_cur; 36b3c0f877SMihai Carabas 37b3c0f877SMihai Carabas spin_lock(&pvpanic_lock); 38b3c0f877SMihai Carabas list_for_each_entry(pi_cur, &pvpanic_list, list) { 39b3c0f877SMihai Carabas if (event & pi_cur->capability & pi_cur->events) 40b3c0f877SMihai Carabas iowrite8(event, pi_cur->base); 41b3c0f877SMihai Carabas } 42b3c0f877SMihai Carabas spin_unlock(&pvpanic_lock); 436861d27cSMihai Carabas } 446861d27cSMihai Carabas 456861d27cSMihai Carabas static int 46*84b0f12aSAndy Shevchenko pvpanic_panic_notify(struct notifier_block *nb, unsigned long code, void *unused) 476861d27cSMihai Carabas { 486861d27cSMihai Carabas unsigned int event = PVPANIC_PANICKED; 496861d27cSMihai Carabas 506861d27cSMihai Carabas if (kexec_crash_loaded()) 516861d27cSMihai Carabas event = PVPANIC_CRASH_LOADED; 526861d27cSMihai Carabas 536861d27cSMihai Carabas pvpanic_send_event(event); 546861d27cSMihai Carabas 556861d27cSMihai Carabas return NOTIFY_DONE; 566861d27cSMihai Carabas } 576861d27cSMihai Carabas 586861d27cSMihai Carabas static struct notifier_block pvpanic_panic_nb = { 596861d27cSMihai Carabas .notifier_call = pvpanic_panic_notify, 60cc5b392dSAndy Shevchenko .priority = 1, /* let this called before broken drm_fb_helper() */ 616861d27cSMihai Carabas }; 626861d27cSMihai Carabas 63394febc9SChristophe JAILLET static void pvpanic_remove(void *param) 646861d27cSMihai Carabas { 65b3c0f877SMihai Carabas struct pvpanic_instance *pi_cur, *pi_next; 66394febc9SChristophe JAILLET struct pvpanic_instance *pi = param; 67b3c0f877SMihai Carabas 68b3c0f877SMihai Carabas spin_lock(&pvpanic_lock); 69b3c0f877SMihai Carabas list_for_each_entry_safe(pi_cur, pi_next, &pvpanic_list, list) { 70b3c0f877SMihai Carabas if (pi_cur == pi) { 71b3c0f877SMihai Carabas list_del(&pi_cur->list); 72b3c0f877SMihai Carabas break; 73b3c0f877SMihai Carabas } 74b3c0f877SMihai Carabas } 75b3c0f877SMihai Carabas spin_unlock(&pvpanic_lock); 766861d27cSMihai Carabas } 77394febc9SChristophe JAILLET 78394febc9SChristophe JAILLET int devm_pvpanic_probe(struct device *dev, struct pvpanic_instance *pi) 79394febc9SChristophe JAILLET { 80394febc9SChristophe JAILLET if (!pi || !pi->base) 81394febc9SChristophe JAILLET return -EINVAL; 82394febc9SChristophe JAILLET 83394febc9SChristophe JAILLET spin_lock(&pvpanic_lock); 84394febc9SChristophe JAILLET list_add(&pi->list, &pvpanic_list); 85394febc9SChristophe JAILLET spin_unlock(&pvpanic_lock); 86394febc9SChristophe JAILLET 87a99009bcSMihai Carabas dev_set_drvdata(dev, pi); 88a99009bcSMihai Carabas 89394febc9SChristophe JAILLET return devm_add_action_or_reset(dev, pvpanic_remove, pi); 90394febc9SChristophe JAILLET } 91394febc9SChristophe JAILLET EXPORT_SYMBOL_GPL(devm_pvpanic_probe); 926861d27cSMihai Carabas 93b3c0f877SMihai Carabas static int pvpanic_init(void) 946861d27cSMihai Carabas { 95b3c0f877SMihai Carabas INIT_LIST_HEAD(&pvpanic_list); 96b3c0f877SMihai Carabas spin_lock_init(&pvpanic_lock); 97b3c0f877SMihai Carabas 98*84b0f12aSAndy Shevchenko atomic_notifier_chain_register(&panic_notifier_list, &pvpanic_panic_nb); 99b3c0f877SMihai Carabas 100b3c0f877SMihai Carabas return 0; 1016861d27cSMihai Carabas } 10233a43041SAndy Shevchenko module_init(pvpanic_init); 103b3c0f877SMihai Carabas 104b3c0f877SMihai Carabas static void pvpanic_exit(void) 105b3c0f877SMihai Carabas { 106*84b0f12aSAndy Shevchenko atomic_notifier_chain_unregister(&panic_notifier_list, &pvpanic_panic_nb); 107b3c0f877SMihai Carabas 108b3c0f877SMihai Carabas } 109b3c0f877SMihai Carabas module_exit(pvpanic_exit); 110