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