at v5.2 196 lines 4.0 kB view raw
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Pvpanic Device Support 4 * 5 * Copyright (C) 2013 Fujitsu. 6 * Copyright (C) 2018 ZTE. 7 */ 8 9#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 10 11#include <linux/acpi.h> 12#include <linux/kernel.h> 13#include <linux/module.h> 14#include <linux/of.h> 15#include <linux/of_address.h> 16#include <linux/platform_device.h> 17#include <linux/types.h> 18 19static void __iomem *base; 20 21#define PVPANIC_PANICKED (1 << 0) 22 23MODULE_AUTHOR("Hu Tao <hutao@cn.fujitsu.com>"); 24MODULE_DESCRIPTION("pvpanic device driver"); 25MODULE_LICENSE("GPL"); 26 27static void 28pvpanic_send_event(unsigned int event) 29{ 30 iowrite8(event, base); 31} 32 33static int 34pvpanic_panic_notify(struct notifier_block *nb, unsigned long code, 35 void *unused) 36{ 37 pvpanic_send_event(PVPANIC_PANICKED); 38 return NOTIFY_DONE; 39} 40 41static struct notifier_block pvpanic_panic_nb = { 42 .notifier_call = pvpanic_panic_notify, 43 .priority = 1, /* let this called before broken drm_fb_helper */ 44}; 45 46#ifdef CONFIG_ACPI 47static int pvpanic_add(struct acpi_device *device); 48static int pvpanic_remove(struct acpi_device *device); 49 50static const struct acpi_device_id pvpanic_device_ids[] = { 51 { "QEMU0001", 0 }, 52 { "", 0 } 53}; 54MODULE_DEVICE_TABLE(acpi, pvpanic_device_ids); 55 56static struct acpi_driver pvpanic_driver = { 57 .name = "pvpanic", 58 .class = "QEMU", 59 .ids = pvpanic_device_ids, 60 .ops = { 61 .add = pvpanic_add, 62 .remove = pvpanic_remove, 63 }, 64 .owner = THIS_MODULE, 65}; 66 67static acpi_status 68pvpanic_walk_resources(struct acpi_resource *res, void *context) 69{ 70 struct resource r; 71 72 if (acpi_dev_resource_io(res, &r)) { 73#ifdef CONFIG_HAS_IOPORT_MAP 74 base = ioport_map(r.start, resource_size(&r)); 75 return AE_OK; 76#else 77 return AE_ERROR; 78#endif 79 } else if (acpi_dev_resource_memory(res, &r)) { 80 base = ioremap(r.start, resource_size(&r)); 81 return AE_OK; 82 } 83 84 return AE_ERROR; 85} 86 87static int pvpanic_add(struct acpi_device *device) 88{ 89 int ret; 90 91 ret = acpi_bus_get_status(device); 92 if (ret < 0) 93 return ret; 94 95 if (!device->status.enabled || !device->status.functional) 96 return -ENODEV; 97 98 acpi_walk_resources(device->handle, METHOD_NAME__CRS, 99 pvpanic_walk_resources, NULL); 100 101 if (!base) 102 return -ENODEV; 103 104 atomic_notifier_chain_register(&panic_notifier_list, 105 &pvpanic_panic_nb); 106 107 return 0; 108} 109 110static int pvpanic_remove(struct acpi_device *device) 111{ 112 113 atomic_notifier_chain_unregister(&panic_notifier_list, 114 &pvpanic_panic_nb); 115 iounmap(base); 116 117 return 0; 118} 119 120static int pvpanic_register_acpi_driver(void) 121{ 122 return acpi_bus_register_driver(&pvpanic_driver); 123} 124 125static void pvpanic_unregister_acpi_driver(void) 126{ 127 acpi_bus_unregister_driver(&pvpanic_driver); 128} 129#else 130static int pvpanic_register_acpi_driver(void) 131{ 132 return -ENODEV; 133} 134 135static void pvpanic_unregister_acpi_driver(void) {} 136#endif 137 138static int pvpanic_mmio_probe(struct platform_device *pdev) 139{ 140 struct resource *mem; 141 142 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 143 if (!mem) 144 return -EINVAL; 145 146 base = devm_ioremap_resource(&pdev->dev, mem); 147 if (IS_ERR(base)) 148 return PTR_ERR(base); 149 150 atomic_notifier_chain_register(&panic_notifier_list, 151 &pvpanic_panic_nb); 152 153 return 0; 154} 155 156static int pvpanic_mmio_remove(struct platform_device *pdev) 157{ 158 159 atomic_notifier_chain_unregister(&panic_notifier_list, 160 &pvpanic_panic_nb); 161 162 return 0; 163} 164 165static const struct of_device_id pvpanic_mmio_match[] = { 166 { .compatible = "qemu,pvpanic-mmio", }, 167 {} 168}; 169 170static struct platform_driver pvpanic_mmio_driver = { 171 .driver = { 172 .name = "pvpanic-mmio", 173 .of_match_table = pvpanic_mmio_match, 174 }, 175 .probe = pvpanic_mmio_probe, 176 .remove = pvpanic_mmio_remove, 177}; 178 179static int __init pvpanic_mmio_init(void) 180{ 181 if (acpi_disabled) 182 return platform_driver_register(&pvpanic_mmio_driver); 183 else 184 return pvpanic_register_acpi_driver(); 185} 186 187static void __exit pvpanic_mmio_exit(void) 188{ 189 if (acpi_disabled) 190 platform_driver_unregister(&pvpanic_mmio_driver); 191 else 192 pvpanic_unregister_acpi_driver(); 193} 194 195module_init(pvpanic_mmio_init); 196module_exit(pvpanic_mmio_exit);