1/* 2 * pvpanic.c - pvpanic Device Support 3 * 4 * Copyright (C) 2013 Fujitsu. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 */ 20 21#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 22 23#include <linux/kernel.h> 24#include <linux/module.h> 25#include <linux/init.h> 26#include <linux/types.h> 27#include <linux/acpi.h> 28 29MODULE_AUTHOR("Hu Tao <hutao@cn.fujitsu.com>"); 30MODULE_DESCRIPTION("pvpanic device driver"); 31MODULE_LICENSE("GPL"); 32 33static int pvpanic_add(struct acpi_device *device); 34static int pvpanic_remove(struct acpi_device *device); 35 36static const struct acpi_device_id pvpanic_device_ids[] = { 37 { "QEMU0001", 0 }, 38 { "", 0 }, 39}; 40MODULE_DEVICE_TABLE(acpi, pvpanic_device_ids); 41 42#define PVPANIC_PANICKED (1 << 0) 43 44static u16 port; 45 46static struct acpi_driver pvpanic_driver = { 47 .name = "pvpanic", 48 .class = "QEMU", 49 .ids = pvpanic_device_ids, 50 .ops = { 51 .add = pvpanic_add, 52 .remove = pvpanic_remove, 53 }, 54 .owner = THIS_MODULE, 55}; 56 57static void 58pvpanic_send_event(unsigned int event) 59{ 60 outb(event, port); 61} 62 63static int 64pvpanic_panic_notify(struct notifier_block *nb, unsigned long code, 65 void *unused) 66{ 67 pvpanic_send_event(PVPANIC_PANICKED); 68 return NOTIFY_DONE; 69} 70 71static struct notifier_block pvpanic_panic_nb = { 72 .notifier_call = pvpanic_panic_notify, 73 .priority = 1, /* let this called before broken drm_fb_helper */ 74}; 75 76 77static acpi_status 78pvpanic_walk_resources(struct acpi_resource *res, void *context) 79{ 80 switch (res->type) { 81 case ACPI_RESOURCE_TYPE_END_TAG: 82 return AE_OK; 83 84 case ACPI_RESOURCE_TYPE_IO: 85 port = res->data.io.minimum; 86 return AE_OK; 87 88 default: 89 return AE_ERROR; 90 } 91} 92 93static int pvpanic_add(struct acpi_device *device) 94{ 95 acpi_status status; 96 u64 ret; 97 98 status = acpi_evaluate_integer(device->handle, "_STA", NULL, 99 &ret); 100 101 if (ACPI_FAILURE(status) || (ret & 0x0B) != 0x0B) 102 return -ENODEV; 103 104 acpi_walk_resources(device->handle, METHOD_NAME__CRS, 105 pvpanic_walk_resources, NULL); 106 107 if (!port) 108 return -ENODEV; 109 110 atomic_notifier_chain_register(&panic_notifier_list, 111 &pvpanic_panic_nb); 112 113 return 0; 114} 115 116static int pvpanic_remove(struct acpi_device *device) 117{ 118 119 atomic_notifier_chain_unregister(&panic_notifier_list, 120 &pvpanic_panic_nb); 121 return 0; 122} 123 124module_acpi_driver(pvpanic_driver); 125