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> 166861d27cSMihai Carabas #include <linux/types.h> 17b3c0f877SMihai Carabas #include <linux/cdev.h> 18b3c0f877SMihai Carabas #include <linux/list.h> 196861d27cSMihai Carabas 206861d27cSMihai Carabas #include <uapi/misc/pvpanic.h> 216861d27cSMihai Carabas 226861d27cSMihai Carabas #include "pvpanic.h" 236861d27cSMihai Carabas 246861d27cSMihai Carabas MODULE_AUTHOR("Mihai Carabas <mihai.carabas@oracle.com>"); 256861d27cSMihai Carabas MODULE_DESCRIPTION("pvpanic device driver "); 266861d27cSMihai Carabas MODULE_LICENSE("GPL"); 276861d27cSMihai Carabas 28*391e2415SYueHaibing static struct list_head pvpanic_list; 29*391e2415SYueHaibing static spinlock_t pvpanic_lock; 306861d27cSMihai Carabas 316861d27cSMihai Carabas static void 326861d27cSMihai Carabas pvpanic_send_event(unsigned int event) 336861d27cSMihai Carabas { 34b3c0f877SMihai Carabas struct pvpanic_instance *pi_cur; 35b3c0f877SMihai Carabas 36b3c0f877SMihai Carabas spin_lock(&pvpanic_lock); 37b3c0f877SMihai Carabas list_for_each_entry(pi_cur, &pvpanic_list, list) { 38b3c0f877SMihai Carabas if (event & pi_cur->capability & pi_cur->events) 39b3c0f877SMihai Carabas iowrite8(event, pi_cur->base); 40b3c0f877SMihai Carabas } 41b3c0f877SMihai Carabas spin_unlock(&pvpanic_lock); 426861d27cSMihai Carabas } 436861d27cSMihai Carabas 446861d27cSMihai Carabas static int 456861d27cSMihai Carabas pvpanic_panic_notify(struct notifier_block *nb, unsigned long code, 466861d27cSMihai Carabas 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, 606861d27cSMihai Carabas .priority = 1, /* let this called before broken drm_fb_helper */ 616861d27cSMihai Carabas }; 626861d27cSMihai Carabas 63b3c0f877SMihai Carabas int pvpanic_probe(struct pvpanic_instance *pi) 646861d27cSMihai Carabas { 65b3c0f877SMihai Carabas if (!pi || !pi->base) 66b3c0f877SMihai Carabas return -EINVAL; 676861d27cSMihai Carabas 68b3c0f877SMihai Carabas spin_lock(&pvpanic_lock); 69b3c0f877SMihai Carabas list_add(&pi->list, &pvpanic_list); 70b3c0f877SMihai Carabas spin_unlock(&pvpanic_lock); 71b3c0f877SMihai Carabas 72b3c0f877SMihai Carabas return 0; 736861d27cSMihai Carabas } 746861d27cSMihai Carabas EXPORT_SYMBOL_GPL(pvpanic_probe); 756861d27cSMihai Carabas 76b3c0f877SMihai Carabas void pvpanic_remove(struct pvpanic_instance *pi) 776861d27cSMihai Carabas { 78b3c0f877SMihai Carabas struct pvpanic_instance *pi_cur, *pi_next; 79b3c0f877SMihai Carabas 80b3c0f877SMihai Carabas if (!pi) 81b3c0f877SMihai Carabas return; 82b3c0f877SMihai Carabas 83b3c0f877SMihai Carabas spin_lock(&pvpanic_lock); 84b3c0f877SMihai Carabas list_for_each_entry_safe(pi_cur, pi_next, &pvpanic_list, list) { 85b3c0f877SMihai Carabas if (pi_cur == pi) { 86b3c0f877SMihai Carabas list_del(&pi_cur->list); 87b3c0f877SMihai Carabas break; 88b3c0f877SMihai Carabas } 89b3c0f877SMihai Carabas } 90b3c0f877SMihai Carabas spin_unlock(&pvpanic_lock); 916861d27cSMihai Carabas } 926861d27cSMihai Carabas EXPORT_SYMBOL_GPL(pvpanic_remove); 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 } 104b3c0f877SMihai Carabas 105b3c0f877SMihai Carabas static void pvpanic_exit(void) 106b3c0f877SMihai Carabas { 107b3c0f877SMihai Carabas atomic_notifier_chain_unregister(&panic_notifier_list, 108b3c0f877SMihai Carabas &pvpanic_panic_nb); 109b3c0f877SMihai Carabas 110b3c0f877SMihai Carabas } 111b3c0f877SMihai Carabas 112b3c0f877SMihai Carabas module_init(pvpanic_init); 113b3c0f877SMihai Carabas module_exit(pvpanic_exit); 114