blob: fef76b5d5f0c73b480fefe55e15c2146becce245 [file] [log] [blame]
Hu Tao8b10acd2013-05-08 11:15:32 +08001/*
2 * pvpanic.c - pvpanic Device Support
3 *
4 * Copyright (C) 2013 Fujitsu.
Peng Hao46f934c2018-11-06 22:57:16 +08005 * Copyright (C) 2018 ZTE.
Hu Tao8b10acd2013-05-08 11:15:32 +08006 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
23
24#include <linux/kernel.h>
25#include <linux/module.h>
Peng Hao46f934c2018-11-06 22:57:16 +080026#include <linux/of.h>
27#include <linux/of_address.h>
28#include <linux/platform_device.h>
Hu Tao8b10acd2013-05-08 11:15:32 +080029#include <linux/init.h>
30#include <linux/types.h>
Lv Zheng8b484632013-12-03 08:49:16 +080031#include <linux/acpi.h>
Hu Tao8b10acd2013-05-08 11:15:32 +080032
Peng Hao725eba22018-11-06 22:57:14 +080033static void __iomem *base;
34
Hu Tao8b10acd2013-05-08 11:15:32 +080035MODULE_AUTHOR("Hu Tao <hutao@cn.fujitsu.com>");
36MODULE_DESCRIPTION("pvpanic device driver");
37MODULE_LICENSE("GPL");
38
39static int pvpanic_add(struct acpi_device *device);
40static int pvpanic_remove(struct acpi_device *device);
41
42static const struct acpi_device_id pvpanic_device_ids[] = {
43 { "QEMU0001", 0 },
44 { "", 0 },
45};
46MODULE_DEVICE_TABLE(acpi, pvpanic_device_ids);
47
48#define PVPANIC_PANICKED (1 << 0)
49
Hu Tao8b10acd2013-05-08 11:15:32 +080050static struct acpi_driver pvpanic_driver = {
51 .name = "pvpanic",
52 .class = "QEMU",
53 .ids = pvpanic_device_ids,
54 .ops = {
55 .add = pvpanic_add,
56 .remove = pvpanic_remove,
57 },
58 .owner = THIS_MODULE,
59};
60
61static void
62pvpanic_send_event(unsigned int event)
63{
Peng Hao725eba22018-11-06 22:57:14 +080064 iowrite8(event, base);
Hu Tao8b10acd2013-05-08 11:15:32 +080065}
66
67static int
68pvpanic_panic_notify(struct notifier_block *nb, unsigned long code,
69 void *unused)
70{
71 pvpanic_send_event(PVPANIC_PANICKED);
72 return NOTIFY_DONE;
73}
74
75static struct notifier_block pvpanic_panic_nb = {
76 .notifier_call = pvpanic_panic_notify,
Takashi Iwai79398312014-05-06 17:52:26 +020077 .priority = 1, /* let this called before broken drm_fb_helper */
Hu Tao8b10acd2013-05-08 11:15:32 +080078};
79
80
81static acpi_status
82pvpanic_walk_resources(struct acpi_resource *res, void *context)
83{
Peng Haod2ae1712018-11-06 22:57:13 +080084 struct resource r;
Hu Tao8b10acd2013-05-08 11:15:32 +080085
Peng Haod2ae1712018-11-06 22:57:13 +080086 if (acpi_dev_resource_io(res, &r)) {
Peng Hao725eba22018-11-06 22:57:14 +080087 base = ioport_map(r.start, resource_size(&r));
88 return AE_OK;
89 } else if (acpi_dev_resource_memory(res, &r)) {
90 base = ioremap(r.start, resource_size(&r));
Hu Tao8b10acd2013-05-08 11:15:32 +080091 return AE_OK;
Hu Tao8b10acd2013-05-08 11:15:32 +080092 }
Peng Haod2ae1712018-11-06 22:57:13 +080093
94 return AE_ERROR;
Hu Tao8b10acd2013-05-08 11:15:32 +080095}
96
97static int pvpanic_add(struct acpi_device *device)
98{
Radim Krčmář55cd3f02015-05-29 22:18:52 +020099 int ret;
Hu Tao8b10acd2013-05-08 11:15:32 +0800100
Radim Krčmář55cd3f02015-05-29 22:18:52 +0200101 ret = acpi_bus_get_status(device);
102 if (ret < 0)
103 return ret;
Hu Tao8b10acd2013-05-08 11:15:32 +0800104
Radim Krčmář55cd3f02015-05-29 22:18:52 +0200105 if (!device->status.enabled || !device->status.functional)
Hu Tao8b10acd2013-05-08 11:15:32 +0800106 return -ENODEV;
107
108 acpi_walk_resources(device->handle, METHOD_NAME__CRS,
109 pvpanic_walk_resources, NULL);
110
Peng Hao725eba22018-11-06 22:57:14 +0800111 if (!base)
Hu Tao8b10acd2013-05-08 11:15:32 +0800112 return -ENODEV;
113
114 atomic_notifier_chain_register(&panic_notifier_list,
115 &pvpanic_panic_nb);
116
117 return 0;
118}
119
120static int pvpanic_remove(struct acpi_device *device)
121{
122
123 atomic_notifier_chain_unregister(&panic_notifier_list,
124 &pvpanic_panic_nb);
Peng Hao725eba22018-11-06 22:57:14 +0800125 iounmap(base);
126
Hu Tao8b10acd2013-05-08 11:15:32 +0800127 return 0;
128}
129
Peng Hao46f934c2018-11-06 22:57:16 +0800130static int pvpanic_mmio_probe(struct platform_device *pdev)
131{
132 struct resource *mem;
133
134 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
135 if (!mem)
136 return -EINVAL;
137
138 base = devm_ioremap_resource(&pdev->dev, mem);
139 if (base == NULL)
140 return -EFAULT;
141
142 atomic_notifier_chain_register(&panic_notifier_list,
143 &pvpanic_panic_nb);
144
145 return 0;
146}
147
148static int pvpanic_mmio_remove(struct platform_device *pdev)
149{
150
151 atomic_notifier_chain_unregister(&panic_notifier_list,
152 &pvpanic_panic_nb);
153
154 return 0;
155}
156
157static const struct of_device_id pvpanic_mmio_match[] = {
158 { .compatible = "qemu,pvpanic-mmio", },
159 {}
160};
161
162static struct platform_driver pvpanic_mmio_driver = {
163 .driver = {
164 .name = "pvpanic-mmio",
165 .of_match_table = pvpanic_mmio_match,
166 },
167 .probe = pvpanic_mmio_probe,
168 .remove = pvpanic_mmio_remove,
169};
170
171static int __init pvpanic_mmio_init(void)
172{
173 if (acpi_disabled)
174 return platform_driver_register(&pvpanic_mmio_driver);
175 else
176 return acpi_bus_register_driver(&pvpanic_driver);
177}
178
179static void __exit pvpanic_mmio_exit(void)
180{
181 if (acpi_disabled)
182 platform_driver_unregister(&pvpanic_mmio_driver);
183 else
184 acpi_bus_unregister_driver(&pvpanic_driver);
185}
186
187module_init(pvpanic_mmio_init);
188module_exit(pvpanic_mmio_exit);