linux/drivers/misc/pvpanic.c
<<
>>
Prefs
   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);
 197