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 28391e2415SYueHaibing static struct list_head pvpanic_list; 29391e2415SYueHaibing 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 63*394febc9SChristophe JAILLET static void pvpanic_remove(void *param) 646861d27cSMihai Carabas { 65b3c0f877SMihai Carabas struct pvpanic_instance *pi_cur, *pi_next; 66*394febc9SChristophe JAILLET struct pvpanic_instance *pi = param; 67b3c0f877SMihai Carabas 68b3c0f877SMihai Carabas if (!pi) 69b3c0f877SMihai Carabas return; 70b3c0f877SMihai Carabas 71b3c0f877SMihai Carabas spin_lock(&pvpanic_lock); 72b3c0f877SMihai Carabas list_for_each_entry_safe(pi_cur, pi_next, &pvpanic_list, list) { 73b3c0f877SMihai Carabas if (pi_cur == pi) { 74b3c0f877SMihai Carabas list_del(&pi_cur->list); 75b3c0f877SMihai Carabas break; 76b3c0f877SMihai Carabas } 77b3c0f877SMihai Carabas } 78b3c0f877SMihai Carabas spin_unlock(&pvpanic_lock); 796861d27cSMihai Carabas } 80*394febc9SChristophe JAILLET 81*394febc9SChristophe JAILLET int devm_pvpanic_probe(struct device *dev, struct pvpanic_instance *pi) 82*394febc9SChristophe JAILLET { 83*394febc9SChristophe JAILLET if (!pi || !pi->base) 84*394febc9SChristophe JAILLET return -EINVAL; 85*394febc9SChristophe JAILLET 86*394febc9SChristophe JAILLET spin_lock(&pvpanic_lock); 87*394febc9SChristophe JAILLET list_add(&pi->list, &pvpanic_list); 88*394febc9SChristophe JAILLET spin_unlock(&pvpanic_lock); 89*394febc9SChristophe JAILLET 90*394febc9SChristophe JAILLET return devm_add_action_or_reset(dev, pvpanic_remove, pi); 91*394febc9SChristophe JAILLET } 92*394febc9SChristophe 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 } 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