blob: 3bfa0f5484d077b2a963065fa903c195ce1fc552 [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
Peng Hao8eeffed2018-11-06 22:57:20 +080024#include <linux/acpi.h>
Hu Tao8b10acd2013-05-08 11:15:32 +080025#include <linux/kernel.h>
26#include <linux/module.h>
Peng Hao46f934c2018-11-06 22:57:16 +080027#include <linux/of.h>
28#include <linux/of_address.h>
29#include <linux/platform_device.h>
Hu Tao8b10acd2013-05-08 11:15:32 +080030#include <linux/types.h>
Hu Tao8b10acd2013-05-08 11:15:32 +080031
Peng Hao725eba22018-11-06 22:57:14 +080032static void __iomem *base;
33
Peng Haoa8b71d22018-11-06 22:57:17 +080034#define PVPANIC_PANICKED (1 << 0)
35
Hu Tao8b10acd2013-05-08 11:15:32 +080036MODULE_AUTHOR("Hu Tao <hutao@cn.fujitsu.com>");
37MODULE_DESCRIPTION("pvpanic device driver");
38MODULE_LICENSE("GPL");
39
Hu Tao8b10acd2013-05-08 11:15:32 +080040static void
41pvpanic_send_event(unsigned int event)
42{
Peng Hao725eba22018-11-06 22:57:14 +080043 iowrite8(event, base);
Hu Tao8b10acd2013-05-08 11:15:32 +080044}
45
46static int
47pvpanic_panic_notify(struct notifier_block *nb, unsigned long code,
48 void *unused)
49{
50 pvpanic_send_event(PVPANIC_PANICKED);
51 return NOTIFY_DONE;
52}
53
54static struct notifier_block pvpanic_panic_nb = {
55 .notifier_call = pvpanic_panic_notify,
Takashi Iwai79398312014-05-06 17:52:26 +020056 .priority = 1, /* let this called before broken drm_fb_helper */
Hu Tao8b10acd2013-05-08 11:15:32 +080057};
58
Peng Hao77703e02018-11-06 22:57:18 +080059#ifdef CONFIG_ACPI
Peng Haoa8b71d22018-11-06 22:57:17 +080060static int pvpanic_add(struct acpi_device *device);
61static int pvpanic_remove(struct acpi_device *device);
62
63static const struct acpi_device_id pvpanic_device_ids[] = {
64 { "QEMU0001", 0 },
65 { "", 0 },
66};
67MODULE_DEVICE_TABLE(acpi, pvpanic_device_ids);
68
69static struct acpi_driver pvpanic_driver = {
70 .name = "pvpanic",
71 .class = "QEMU",
72 .ids = pvpanic_device_ids,
73 .ops = {
74 .add = pvpanic_add,
75 .remove = pvpanic_remove,
76 },
77 .owner = THIS_MODULE,
78};
Hu Tao8b10acd2013-05-08 11:15:32 +080079
80static acpi_status
81pvpanic_walk_resources(struct acpi_resource *res, void *context)
82{
Peng Haod2ae1712018-11-06 22:57:13 +080083 struct resource r;
Hu Tao8b10acd2013-05-08 11:15:32 +080084
Peng Haod2ae1712018-11-06 22:57:13 +080085 if (acpi_dev_resource_io(res, &r)) {
Peng Hao725eba22018-11-06 22:57:14 +080086 base = ioport_map(r.start, resource_size(&r));
87 return AE_OK;
88 } else if (acpi_dev_resource_memory(res, &r)) {
89 base = ioremap(r.start, resource_size(&r));
Hu Tao8b10acd2013-05-08 11:15:32 +080090 return AE_OK;
Hu Tao8b10acd2013-05-08 11:15:32 +080091 }
Peng Haod2ae1712018-11-06 22:57:13 +080092
93 return AE_ERROR;
Hu Tao8b10acd2013-05-08 11:15:32 +080094}
95
96static int pvpanic_add(struct acpi_device *device)
97{
Radim Krčmář55cd3f02015-05-29 22:18:52 +020098 int ret;
Hu Tao8b10acd2013-05-08 11:15:32 +080099
Radim Krčmář55cd3f02015-05-29 22:18:52 +0200100 ret = acpi_bus_get_status(device);
101 if (ret < 0)
102 return ret;
Hu Tao8b10acd2013-05-08 11:15:32 +0800103
Radim Krčmář55cd3f02015-05-29 22:18:52 +0200104 if (!device->status.enabled || !device->status.functional)
Hu Tao8b10acd2013-05-08 11:15:32 +0800105 return -ENODEV;
106
107 acpi_walk_resources(device->handle, METHOD_NAME__CRS,
108 pvpanic_walk_resources, NULL);
109
Peng Hao725eba22018-11-06 22:57:14 +0800110 if (!base)
Hu Tao8b10acd2013-05-08 11:15:32 +0800111 return -ENODEV;
112
113 atomic_notifier_chain_register(&panic_notifier_list,
114 &pvpanic_panic_nb);
115
116 return 0;
117}
118
119static int pvpanic_remove(struct acpi_device *device)
120{
121
122 atomic_notifier_chain_unregister(&panic_notifier_list,
123 &pvpanic_panic_nb);
Peng Hao725eba22018-11-06 22:57:14 +0800124 iounmap(base);
125
Hu Tao8b10acd2013-05-08 11:15:32 +0800126 return 0;
127}
128
Peng Hao77703e02018-11-06 22:57:18 +0800129static int pvpanic_register_acpi_driver(void)
130{
131 return acpi_bus_register_driver(&pvpanic_driver);
132}
133
134static void pvpanic_unregister_acpi_driver(void)
135{
136 acpi_bus_unregister_driver(&pvpanic_driver);
137}
138#else
139static int pvpanic_register_acpi_driver(void)
140{
141 return -ENODEV;
142}
143
144static void pvpanic_unregister_acpi_driver(void) {}
145#endif
146
Peng Hao46f934c2018-11-06 22:57:16 +0800147static int pvpanic_mmio_probe(struct platform_device *pdev)
148{
149 struct resource *mem;
150
151 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
152 if (!mem)
153 return -EINVAL;
154
155 base = devm_ioremap_resource(&pdev->dev, mem);
156 if (base == NULL)
157 return -EFAULT;
158
159 atomic_notifier_chain_register(&panic_notifier_list,
160 &pvpanic_panic_nb);
161
162 return 0;
163}
164
165static int pvpanic_mmio_remove(struct platform_device *pdev)
166{
167
168 atomic_notifier_chain_unregister(&panic_notifier_list,
169 &pvpanic_panic_nb);
170
171 return 0;
172}
173
174static const struct of_device_id pvpanic_mmio_match[] = {
175 { .compatible = "qemu,pvpanic-mmio", },
176 {}
177};
178
179static struct platform_driver pvpanic_mmio_driver = {
180 .driver = {
181 .name = "pvpanic-mmio",
182 .of_match_table = pvpanic_mmio_match,
183 },
184 .probe = pvpanic_mmio_probe,
185 .remove = pvpanic_mmio_remove,
186};
187
188static int __init pvpanic_mmio_init(void)
189{
190 if (acpi_disabled)
191 return platform_driver_register(&pvpanic_mmio_driver);
192 else
Peng Hao77703e02018-11-06 22:57:18 +0800193 return pvpanic_register_acpi_driver();
Peng Hao46f934c2018-11-06 22:57:16 +0800194}
195
196static void __exit pvpanic_mmio_exit(void)
197{
198 if (acpi_disabled)
199 platform_driver_unregister(&pvpanic_mmio_driver);
200 else
Peng Hao77703e02018-11-06 22:57:18 +0800201 pvpanic_unregister_acpi_driver();
Peng Hao46f934c2018-11-06 22:57:16 +0800202}
203
204module_init(pvpanic_mmio_init);
205module_exit(pvpanic_mmio_exit);