1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Pvpanic MMIO Device Support 4 * 5 * Copyright (C) 2013 Fujitsu. 6 * Copyright (C) 2018 ZTE. 7 * Copyright (C) 2021 Oracle. 8 */ 9 10 #include <linux/io.h> 11 #include <linux/kernel.h> 12 #include <linux/kexec.h> 13 #include <linux/mod_devicetable.h> 14 #include <linux/module.h> 15 #include <linux/platform_device.h> 16 #include <linux/types.h> 17 #include <linux/slab.h> 18 19 #include <uapi/misc/pvpanic.h> 20 21 #include "pvpanic.h" 22 23 MODULE_AUTHOR("Hu Tao <hutao@cn.fujitsu.com>"); 24 MODULE_DESCRIPTION("pvpanic-mmio device driver"); 25 MODULE_LICENSE("GPL"); 26 27 static ssize_t capability_show(struct device *dev, 28 struct device_attribute *attr, char *buf) 29 { 30 struct pvpanic_instance *pi = dev_get_drvdata(dev); 31 32 return sysfs_emit(buf, "%x\n", pi->capability); 33 } 34 static DEVICE_ATTR_RO(capability); 35 36 static ssize_t events_show(struct device *dev, struct device_attribute *attr, char *buf) 37 { 38 struct pvpanic_instance *pi = dev_get_drvdata(dev); 39 40 return sysfs_emit(buf, "%x\n", pi->events); 41 } 42 43 static ssize_t events_store(struct device *dev, struct device_attribute *attr, 44 const char *buf, size_t count) 45 { 46 struct pvpanic_instance *pi = dev_get_drvdata(dev); 47 unsigned int tmp; 48 int err; 49 50 err = kstrtouint(buf, 16, &tmp); 51 if (err) 52 return err; 53 54 if ((tmp & pi->capability) != tmp) 55 return -EINVAL; 56 57 pi->events = tmp; 58 59 return count; 60 } 61 static DEVICE_ATTR_RW(events); 62 63 static struct attribute *pvpanic_mmio_dev_attrs[] = { 64 &dev_attr_capability.attr, 65 &dev_attr_events.attr, 66 NULL 67 }; 68 ATTRIBUTE_GROUPS(pvpanic_mmio_dev); 69 70 static int pvpanic_mmio_probe(struct platform_device *pdev) 71 { 72 struct device *dev = &pdev->dev; 73 struct pvpanic_instance *pi; 74 struct resource *res; 75 void __iomem *base; 76 77 res = platform_get_mem_or_io(pdev, 0); 78 if (!res) 79 return -EINVAL; 80 81 switch (resource_type(res)) { 82 case IORESOURCE_IO: 83 base = devm_ioport_map(dev, res->start, resource_size(res)); 84 if (!base) 85 return -ENOMEM; 86 break; 87 case IORESOURCE_MEM: 88 base = devm_ioremap_resource(dev, res); 89 if (IS_ERR(base)) 90 return PTR_ERR(base); 91 break; 92 default: 93 return -EINVAL; 94 } 95 96 pi = kmalloc(sizeof(*pi), GFP_ATOMIC); 97 if (!pi) 98 return -ENOMEM; 99 100 pi->base = base; 101 pi->capability = PVPANIC_PANICKED | PVPANIC_CRASH_LOADED; 102 103 /* initlize capability by RDPT */ 104 pi->capability &= ioread8(base); 105 pi->events = pi->capability; 106 107 dev_set_drvdata(dev, pi); 108 109 return pvpanic_probe(pi); 110 } 111 112 static int pvpanic_mmio_remove(struct platform_device *pdev) 113 { 114 struct pvpanic_instance *pi = dev_get_drvdata(&pdev->dev); 115 116 pvpanic_remove(pi); 117 kfree(pi); 118 119 return 0; 120 } 121 122 static const struct of_device_id pvpanic_mmio_match[] = { 123 { .compatible = "qemu,pvpanic-mmio", }, 124 {} 125 }; 126 MODULE_DEVICE_TABLE(of, pvpanic_mmio_match); 127 128 static const struct acpi_device_id pvpanic_device_ids[] = { 129 { "QEMU0001", 0 }, 130 { "", 0 } 131 }; 132 MODULE_DEVICE_TABLE(acpi, pvpanic_device_ids); 133 134 static struct platform_driver pvpanic_mmio_driver = { 135 .driver = { 136 .name = "pvpanic-mmio", 137 .of_match_table = pvpanic_mmio_match, 138 .acpi_match_table = pvpanic_device_ids, 139 .dev_groups = pvpanic_mmio_dev_groups, 140 }, 141 .probe = pvpanic_mmio_probe, 142 .remove = pvpanic_mmio_remove, 143 }; 144 module_platform_driver(pvpanic_mmio_driver); 145