16861d27cSMihai Carabas // SPDX-License-Identifier: GPL-2.0+ 26861d27cSMihai Carabas /* 36861d27cSMihai Carabas * Pvpanic MMIO 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> 17*b3c0f877SMihai Carabas #include <linux/slab.h> 186861d27cSMihai Carabas 196861d27cSMihai Carabas #include <uapi/misc/pvpanic.h> 206861d27cSMihai Carabas 216861d27cSMihai Carabas #include "pvpanic.h" 226861d27cSMihai Carabas 236861d27cSMihai Carabas MODULE_AUTHOR("Hu Tao <hutao@cn.fujitsu.com>"); 246861d27cSMihai Carabas MODULE_DESCRIPTION("pvpanic-mmio device driver"); 256861d27cSMihai Carabas MODULE_LICENSE("GPL"); 266861d27cSMihai Carabas 276861d27cSMihai Carabas static ssize_t capability_show(struct device *dev, 286861d27cSMihai Carabas struct device_attribute *attr, char *buf) 296861d27cSMihai Carabas { 30*b3c0f877SMihai Carabas struct pvpanic_instance *pi = dev_get_drvdata(dev); 31*b3c0f877SMihai Carabas 32*b3c0f877SMihai Carabas return sysfs_emit(buf, "%x\n", pi->capability); 336861d27cSMihai Carabas } 346861d27cSMihai Carabas static DEVICE_ATTR_RO(capability); 356861d27cSMihai Carabas 366861d27cSMihai Carabas static ssize_t events_show(struct device *dev, struct device_attribute *attr, char *buf) 376861d27cSMihai Carabas { 38*b3c0f877SMihai Carabas struct pvpanic_instance *pi = dev_get_drvdata(dev); 39*b3c0f877SMihai Carabas 40*b3c0f877SMihai Carabas return sysfs_emit(buf, "%x\n", pi->events); 416861d27cSMihai Carabas } 426861d27cSMihai Carabas 436861d27cSMihai Carabas static ssize_t events_store(struct device *dev, struct device_attribute *attr, 446861d27cSMihai Carabas const char *buf, size_t count) 456861d27cSMihai Carabas { 46*b3c0f877SMihai Carabas struct pvpanic_instance *pi = dev_get_drvdata(dev); 476861d27cSMihai Carabas unsigned int tmp; 486861d27cSMihai Carabas int err; 496861d27cSMihai Carabas 506861d27cSMihai Carabas err = kstrtouint(buf, 16, &tmp); 516861d27cSMihai Carabas if (err) 526861d27cSMihai Carabas return err; 536861d27cSMihai Carabas 54*b3c0f877SMihai Carabas if ((tmp & pi->capability) != tmp) 556861d27cSMihai Carabas return -EINVAL; 566861d27cSMihai Carabas 57*b3c0f877SMihai Carabas pi->events = tmp; 586861d27cSMihai Carabas 596861d27cSMihai Carabas return count; 606861d27cSMihai Carabas } 616861d27cSMihai Carabas static DEVICE_ATTR_RW(events); 626861d27cSMihai Carabas 636861d27cSMihai Carabas static struct attribute *pvpanic_mmio_dev_attrs[] = { 646861d27cSMihai Carabas &dev_attr_capability.attr, 656861d27cSMihai Carabas &dev_attr_events.attr, 666861d27cSMihai Carabas NULL 676861d27cSMihai Carabas }; 686861d27cSMihai Carabas ATTRIBUTE_GROUPS(pvpanic_mmio_dev); 696861d27cSMihai Carabas 706861d27cSMihai Carabas static int pvpanic_mmio_probe(struct platform_device *pdev) 716861d27cSMihai Carabas { 726861d27cSMihai Carabas struct device *dev = &pdev->dev; 73*b3c0f877SMihai Carabas struct pvpanic_instance *pi; 746861d27cSMihai Carabas struct resource *res; 75*b3c0f877SMihai Carabas void __iomem *base; 766861d27cSMihai Carabas 776861d27cSMihai Carabas res = platform_get_mem_or_io(pdev, 0); 786861d27cSMihai Carabas if (!res) 796861d27cSMihai Carabas return -EINVAL; 806861d27cSMihai Carabas 816861d27cSMihai Carabas switch (resource_type(res)) { 826861d27cSMihai Carabas case IORESOURCE_IO: 836861d27cSMihai Carabas base = devm_ioport_map(dev, res->start, resource_size(res)); 846861d27cSMihai Carabas if (!base) 856861d27cSMihai Carabas return -ENOMEM; 866861d27cSMihai Carabas break; 876861d27cSMihai Carabas case IORESOURCE_MEM: 886861d27cSMihai Carabas base = devm_ioremap_resource(dev, res); 896861d27cSMihai Carabas if (IS_ERR(base)) 906861d27cSMihai Carabas return PTR_ERR(base); 916861d27cSMihai Carabas break; 926861d27cSMihai Carabas default: 936861d27cSMihai Carabas return -EINVAL; 946861d27cSMihai Carabas } 956861d27cSMihai Carabas 96*b3c0f877SMihai Carabas pi = kmalloc(sizeof(*pi), GFP_ATOMIC); 97*b3c0f877SMihai Carabas if (!pi) 98*b3c0f877SMihai Carabas return -ENOMEM; 99*b3c0f877SMihai Carabas 100*b3c0f877SMihai Carabas pi->base = base; 101*b3c0f877SMihai Carabas pi->capability = PVPANIC_PANICKED | PVPANIC_CRASH_LOADED; 102*b3c0f877SMihai Carabas 1036861d27cSMihai Carabas /* initlize capability by RDPT */ 104*b3c0f877SMihai Carabas pi->capability &= ioread8(base); 105*b3c0f877SMihai Carabas pi->events = pi->capability; 1066861d27cSMihai Carabas 107*b3c0f877SMihai Carabas dev_set_drvdata(dev, pi); 1086861d27cSMihai Carabas 109*b3c0f877SMihai Carabas return pvpanic_probe(pi); 1106861d27cSMihai Carabas } 1116861d27cSMihai Carabas 1126861d27cSMihai Carabas static int pvpanic_mmio_remove(struct platform_device *pdev) 1136861d27cSMihai Carabas { 114*b3c0f877SMihai Carabas struct pvpanic_instance *pi = dev_get_drvdata(&pdev->dev); 1156861d27cSMihai Carabas 116*b3c0f877SMihai Carabas pvpanic_remove(pi); 117*b3c0f877SMihai Carabas kfree(pi); 1186861d27cSMihai Carabas 1196861d27cSMihai Carabas return 0; 1206861d27cSMihai Carabas } 1216861d27cSMihai Carabas 1226861d27cSMihai Carabas static const struct of_device_id pvpanic_mmio_match[] = { 1236861d27cSMihai Carabas { .compatible = "qemu,pvpanic-mmio", }, 1246861d27cSMihai Carabas {} 1256861d27cSMihai Carabas }; 1266861d27cSMihai Carabas MODULE_DEVICE_TABLE(of, pvpanic_mmio_match); 1276861d27cSMihai Carabas 1286861d27cSMihai Carabas static const struct acpi_device_id pvpanic_device_ids[] = { 1296861d27cSMihai Carabas { "QEMU0001", 0 }, 1306861d27cSMihai Carabas { "", 0 } 1316861d27cSMihai Carabas }; 1326861d27cSMihai Carabas MODULE_DEVICE_TABLE(acpi, pvpanic_device_ids); 1336861d27cSMihai Carabas 1346861d27cSMihai Carabas static struct platform_driver pvpanic_mmio_driver = { 1356861d27cSMihai Carabas .driver = { 1366861d27cSMihai Carabas .name = "pvpanic-mmio", 1376861d27cSMihai Carabas .of_match_table = pvpanic_mmio_match, 1386861d27cSMihai Carabas .acpi_match_table = pvpanic_device_ids, 1396861d27cSMihai Carabas .dev_groups = pvpanic_mmio_dev_groups, 1406861d27cSMihai Carabas }, 1416861d27cSMihai Carabas .probe = pvpanic_mmio_probe, 1426861d27cSMihai Carabas .remove = pvpanic_mmio_remove, 1436861d27cSMihai Carabas }; 1446861d27cSMihai Carabas module_platform_driver(pvpanic_mmio_driver); 145