1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * QEMU Virt Machine System Controller Driver
4 *
5 * Copyright (C) 2026 Kuan-Wei Chiu <visitorckw@gmail.com>
6 */
7
8 #include <linux/io.h>
9 #include <linux/module.h>
10 #include <linux/platform_device.h>
11 #include <linux/reboot.h>
12
13 /* Registers */
14 #define VIRT_CTRL_REG_FEATURES 0x00
15 #define VIRT_CTRL_REG_CMD 0x04
16
17 /* Commands */
18 #define CMD_NOOP 0
19 #define CMD_RESET 1
20 #define CMD_HALT 2
21 #define CMD_PANIC 3
22
23 struct qemu_virt_ctrl {
24 void __iomem *base;
25 struct notifier_block reboot_nb;
26 };
27
virt_ctrl_write32(u32 val,void __iomem * addr)28 static inline void virt_ctrl_write32(u32 val, void __iomem *addr)
29 {
30 if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
31 iowrite32be(val, addr);
32 else
33 iowrite32(val, addr);
34 }
35
qemu_virt_ctrl_power_off(struct sys_off_data * data)36 static int qemu_virt_ctrl_power_off(struct sys_off_data *data)
37 {
38 struct qemu_virt_ctrl *ctrl = data->cb_data;
39
40 virt_ctrl_write32(CMD_HALT, ctrl->base + VIRT_CTRL_REG_CMD);
41
42 return NOTIFY_DONE;
43 }
44
qemu_virt_ctrl_restart(struct sys_off_data * data)45 static int qemu_virt_ctrl_restart(struct sys_off_data *data)
46 {
47 struct qemu_virt_ctrl *ctrl = data->cb_data;
48
49 virt_ctrl_write32(CMD_RESET, ctrl->base + VIRT_CTRL_REG_CMD);
50
51 return NOTIFY_DONE;
52 }
53
qemu_virt_ctrl_reboot_notify(struct notifier_block * nb,unsigned long action,void * data)54 static int qemu_virt_ctrl_reboot_notify(struct notifier_block *nb,
55 unsigned long action, void *data)
56 {
57 struct qemu_virt_ctrl *ctrl = container_of(nb, struct qemu_virt_ctrl, reboot_nb);
58
59 if (action == SYS_HALT)
60 virt_ctrl_write32(CMD_HALT, ctrl->base + VIRT_CTRL_REG_CMD);
61
62 return NOTIFY_DONE;
63 }
64
qemu_virt_ctrl_probe(struct platform_device * pdev)65 static int qemu_virt_ctrl_probe(struct platform_device *pdev)
66 {
67 struct qemu_virt_ctrl *ctrl;
68 int ret;
69
70 ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL);
71 if (!ctrl)
72 return -ENOMEM;
73
74 ctrl->base = devm_platform_ioremap_resource(pdev, 0);
75 if (IS_ERR(ctrl->base))
76 return PTR_ERR(ctrl->base);
77
78 ret = devm_register_sys_off_handler(&pdev->dev,
79 SYS_OFF_MODE_RESTART,
80 SYS_OFF_PRIO_DEFAULT,
81 qemu_virt_ctrl_restart,
82 ctrl);
83 if (ret)
84 return dev_err_probe(&pdev->dev, ret,
85 "cannot register restart handler\n");
86
87 ret = devm_register_sys_off_handler(&pdev->dev,
88 SYS_OFF_MODE_POWER_OFF,
89 SYS_OFF_PRIO_DEFAULT,
90 qemu_virt_ctrl_power_off,
91 ctrl);
92 if (ret)
93 return dev_err_probe(&pdev->dev, ret,
94 "cannot register power-off handler\n");
95
96 ctrl->reboot_nb.notifier_call = qemu_virt_ctrl_reboot_notify;
97 ret = devm_register_reboot_notifier(&pdev->dev, &ctrl->reboot_nb);
98 if (ret)
99 return dev_err_probe(&pdev->dev, ret, "cannot register reboot notifier\n");
100
101 return 0;
102 }
103
104 static const struct platform_device_id qemu_virt_ctrl_id[] = {
105 { .name = "qemu-virt-ctrl" },
106 { }
107 };
108 MODULE_DEVICE_TABLE(platform, qemu_virt_ctrl_id);
109
110 static struct platform_driver qemu_virt_ctrl_driver = {
111 .probe = qemu_virt_ctrl_probe,
112 .driver = {
113 .name = "qemu-virt-ctrl",
114 },
115 .id_table = qemu_virt_ctrl_id,
116 };
117 module_platform_driver(qemu_virt_ctrl_driver);
118
119 MODULE_AUTHOR("Kuan-Wei Chiu <visitorckw@gmail.com>");
120 MODULE_DESCRIPTION("QEMU Virt Machine System Controller Driver");
121 MODULE_LICENSE("GPL");
122