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 466861d27cSMihai Carabas pvpanic_panic_notify(struct notifier_block *nb, unsigned long code, 476861d27cSMihai Carabas void *unused) 486861d27cSMihai Carabas { 496861d27cSMihai Carabas unsigned int event = PVPANIC_PANICKED; 506861d27cSMihai Carabas 516861d27cSMihai Carabas if (kexec_crash_loaded()) 526861d27cSMihai Carabas event = PVPANIC_CRASH_LOADED; 536861d27cSMihai Carabas 546861d27cSMihai Carabas pvpanic_send_event(event); 556861d27cSMihai Carabas 566861d27cSMihai Carabas return NOTIFY_DONE; 576861d27cSMihai Carabas } 586861d27cSMihai Carabas 596861d27cSMihai Carabas static struct notifier_block pvpanic_panic_nb = { 606861d27cSMihai Carabas .notifier_call = pvpanic_panic_notify, 616861d27cSMihai Carabas .priority = 1, /* let this called before broken drm_fb_helper */ 626861d27cSMihai Carabas }; 636861d27cSMihai Carabas 64394febc9SChristophe JAILLET static void pvpanic_remove(void *param) 656861d27cSMihai Carabas { 66b3c0f877SMihai Carabas struct pvpanic_instance *pi_cur, *pi_next; 67394febc9SChristophe JAILLET struct pvpanic_instance *pi = param; 68b3c0f877SMihai Carabas 69b3c0f877SMihai Carabas spin_lock(&pvpanic_lock); 70b3c0f877SMihai Carabas list_for_each_entry_safe(pi_cur, pi_next, &pvpanic_list, list) { 71b3c0f877SMihai Carabas if (pi_cur == pi) { 72b3c0f877SMihai Carabas list_del(&pi_cur->list); 73b3c0f877SMihai Carabas break; 74b3c0f877SMihai Carabas } 75b3c0f877SMihai Carabas } 76b3c0f877SMihai Carabas spin_unlock(&pvpanic_lock); 776861d27cSMihai Carabas } 78394febc9SChristophe JAILLET 79394febc9SChristophe JAILLET int devm_pvpanic_probe(struct device *dev, struct pvpanic_instance *pi) 80394febc9SChristophe JAILLET { 81394febc9SChristophe JAILLET if (!pi || !pi->base) 82394febc9SChristophe JAILLET return -EINVAL; 83394febc9SChristophe JAILLET 84394febc9SChristophe JAILLET spin_lock(&pvpanic_lock); 85394febc9SChristophe JAILLET list_add(&pi->list, &pvpanic_list); 86394febc9SChristophe JAILLET spin_unlock(&pvpanic_lock); 87394febc9SChristophe JAILLET 88a99009bcSMihai Carabas dev_set_drvdata(dev, pi); 89a99009bcSMihai Carabas 90394febc9SChristophe JAILLET return devm_add_action_or_reset(dev, pvpanic_remove, pi); 91394febc9SChristophe JAILLET } 92394febc9SChristophe JAILLET EXPORT_SYMBOL_GPL(devm_pvpanic_probe); 936861d27cSMihai Carabas 94b3c0f877SMihai Carabas static int pvpanic_init(void) 956861d27cSMihai Carabas { 96b3c0f877SMihai Carabas INIT_LIST_HEAD(&pvpanic_list); 97b3c0f877SMihai Carabas spin_lock_init(&pvpanic_lock); 98b3c0f877SMihai Carabas 99b3c0f877SMihai Carabas atomic_notifier_chain_register(&panic_notifier_list, 100b3c0f877SMihai Carabas &pvpanic_panic_nb); 101b3c0f877SMihai Carabas 102b3c0f877SMihai Carabas return 0; 1036861d27cSMihai Carabas } 104*33a43041SAndy Shevchenko module_init(pvpanic_init); 105b3c0f877SMihai Carabas 106b3c0f877SMihai Carabas static void pvpanic_exit(void) 107b3c0f877SMihai Carabas { 108b3c0f877SMihai Carabas atomic_notifier_chain_unregister(&panic_notifier_list, 109b3c0f877SMihai Carabas &pvpanic_panic_nb); 110b3c0f877SMihai Carabas 111b3c0f877SMihai Carabas } 112b3c0f877SMihai Carabas module_exit(pvpanic_exit); 113