xref: /linux/drivers/power/reset/qemu-virt-ctrl.c (revision 26ba30221c03364d6ed9910be8da4c1fd871b07b)
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 
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 
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 
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 
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 
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