Add a new driver for the 'virt-ctrl' device found on QEMU virt machines
(e.g. m68k). This device provides a simple interface for system reset
and power off [1].
This driver registers a restart handler for system reboot and sets the
global pm_power_off callback for system shutdown. It is designed to be
generic and can be reused by other architectures utilizing this QEMU
device.
Link: https://gitlab.com/qemu-project/qemu/-/blob/v10.2.0/hw/misc/virt_ctrl.c [1]
Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
---
MAINTAINERS | 6 ++
drivers/power/reset/Kconfig | 10 +++
drivers/power/reset/Makefile | 1 +
drivers/power/reset/qemu-virt-ctrl.c | 98 ++++++++++++++++++++++++++++
4 files changed, 115 insertions(+)
create mode 100644 drivers/power/reset/qemu-virt-ctrl.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 0d044a58cbfe..2586e4162304 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21179,6 +21179,12 @@ S: Maintained
F: drivers/firmware/qemu_fw_cfg.c
F: include/uapi/linux/qemu_fw_cfg.h
+QEMU VIRT MACHINE SYSTEM CONTROLLER DRIVER
+M: Kuan-Wei Chiu <visitorckw@gmail.com>
+L: linux-pm@vger.kernel.org
+S: Maintained
+F: drivers/power/reset/qemu-virt-ctrl.c
+
QLOGIC QL41xxx FCOE DRIVER
M: Saurav Kashyap <skashyap@marvell.com>
M: Javed Hasan <jhasan@marvell.com>
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
index f6c1bcbb57de..1ca1c2913cbc 100644
--- a/drivers/power/reset/Kconfig
+++ b/drivers/power/reset/Kconfig
@@ -354,4 +354,14 @@ config POWER_MLXBF
help
This driver supports reset or low power mode handling for Mellanox BlueField.
+config POWER_RESET_QEMU_VIRT_CTRL
+ bool "QEMU Virt Machine System Controller"
+ depends on HAS_IOMEM
+ help
+ This driver supports the system reset and power off functionality
+ provided by the QEMU 'virt-ctrl' device.
+
+ Say Y here if you are running Linux on a QEMU virtual machine that
+ provides this controller, such as the m68k virt machine.
+
endif
diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile
index 0e4ae6f6b5c5..d7ae97241a83 100644
--- a/drivers/power/reset/Makefile
+++ b/drivers/power/reset/Makefile
@@ -41,3 +41,4 @@ obj-$(CONFIG_SYSCON_REBOOT_MODE) += syscon-reboot-mode.o
obj-$(CONFIG_POWER_RESET_SC27XX) += sc27xx-poweroff.o
obj-$(CONFIG_NVMEM_REBOOT_MODE) += nvmem-reboot-mode.o
obj-$(CONFIG_POWER_MLXBF) += pwr-mlxbf.o
+obj-$(CONFIG_POWER_RESET_QEMU_VIRT_CTRL) += qemu-virt-ctrl.o
diff --git a/drivers/power/reset/qemu-virt-ctrl.c b/drivers/power/reset/qemu-virt-ctrl.c
new file mode 100644
index 000000000000..f1acd22172ce
--- /dev/null
+++ b/drivers/power/reset/qemu-virt-ctrl.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * QEMU Virt Machine System Controller Driver
+ *
+ * Copyright (C) 2026 Kuan-Wei Chiu <visitorckw@gmail.com>
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+
+/* Registers */
+#define VIRT_CTRL_REG_FEATURES 0x00
+#define VIRT_CTRL_REG_CMD 0x04
+
+/* Commands */
+#define CMD_NOOP 0
+#define CMD_RESET 1
+#define CMD_HALT 2
+#define CMD_PANIC 3
+
+struct qemu_virt_ctrl {
+ void __iomem *base;
+ struct notifier_block restart_nb;
+};
+
+static void __iomem *qemu_virt_ctrl_base;
+
+static void qemu_virt_ctrl_power_off(void)
+{
+ if (qemu_virt_ctrl_base)
+ iowrite32be(CMD_HALT, qemu_virt_ctrl_base + VIRT_CTRL_REG_CMD);
+}
+
+static int qemu_virt_ctrl_restart(struct notifier_block *nb, unsigned long action,
+ void *data)
+{
+ struct qemu_virt_ctrl *vc = container_of(nb, struct qemu_virt_ctrl, restart_nb);
+
+ iowrite32be(CMD_RESET, vc->base + VIRT_CTRL_REG_CMD);
+
+ return NOTIFY_DONE;
+}
+
+static int qemu_virt_ctrl_probe(struct platform_device *pdev)
+{
+ struct qemu_virt_ctrl *vc;
+ int ret;
+
+ vc = devm_kzalloc(&pdev->dev, sizeof(*vc), GFP_KERNEL);
+ if (!vc)
+ return -ENOMEM;
+
+ vc->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(vc->base))
+ return PTR_ERR(vc->base);
+
+ qemu_virt_ctrl_base = vc->base;
+
+ vc->restart_nb.notifier_call = qemu_virt_ctrl_restart;
+ vc->restart_nb.priority = 128;
+
+ ret = register_restart_handler(&vc->restart_nb);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "cannot register restart handler\n");
+
+ if (!pm_power_off)
+ pm_power_off = qemu_virt_ctrl_power_off;
+
+ platform_set_drvdata(pdev, vc);
+
+ return 0;
+}
+
+static void qemu_virt_ctrl_remove(struct platform_device *pdev)
+{
+ struct qemu_virt_ctrl *vc = platform_get_drvdata(pdev);
+
+ unregister_restart_handler(&vc->restart_nb);
+
+ if (pm_power_off == qemu_virt_ctrl_power_off)
+ pm_power_off = NULL;
+}
+
+static struct platform_driver qemu_virt_ctrl_driver = {
+ .probe = qemu_virt_ctrl_probe,
+ .remove = qemu_virt_ctrl_remove,
+ .driver = {
+ .name = "qemu-virt-ctrl",
+ },
+};
+module_platform_driver(qemu_virt_ctrl_driver);
+
+MODULE_AUTHOR("Kuan-Wei Chiu <visitorckw@gmail.com>");
+MODULE_DESCRIPTION("QEMU Virt Machine System Controller Driver");
+MODULE_LICENSE("GPL");
--
2.52.0.457.g6b5491de43-goog
Hi,
On Mon, Jan 12, 2026 at 06:22:56PM +0000, Kuan-Wei Chiu wrote:
> Add a new driver for the 'virt-ctrl' device found on QEMU virt machines
> (e.g. m68k). This device provides a simple interface for system reset
> and power off [1].
>
> This driver registers a restart handler for system reboot and sets the
> global pm_power_off callback for system shutdown. It is designed to be
> generic and can be reused by other architectures utilizing this QEMU
> device.
>
> Link: https://gitlab.com/qemu-project/qemu/-/blob/v10.2.0/hw/misc/virt_ctrl.c [1]
> Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
> ---
> MAINTAINERS | 6 ++
> drivers/power/reset/Kconfig | 10 +++
> drivers/power/reset/Makefile | 1 +
> drivers/power/reset/qemu-virt-ctrl.c | 98 ++++++++++++++++++++++++++++
> 4 files changed, 115 insertions(+)
> create mode 100644 drivers/power/reset/qemu-virt-ctrl.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 0d044a58cbfe..2586e4162304 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -21179,6 +21179,12 @@ S: Maintained
> F: drivers/firmware/qemu_fw_cfg.c
> F: include/uapi/linux/qemu_fw_cfg.h
>
> +QEMU VIRT MACHINE SYSTEM CONTROLLER DRIVER
> +M: Kuan-Wei Chiu <visitorckw@gmail.com>
> +L: linux-pm@vger.kernel.org
> +S: Maintained
> +F: drivers/power/reset/qemu-virt-ctrl.c
> +
> QLOGIC QL41xxx FCOE DRIVER
> M: Saurav Kashyap <skashyap@marvell.com>
> M: Javed Hasan <jhasan@marvell.com>
> diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
> index f6c1bcbb57de..1ca1c2913cbc 100644
> --- a/drivers/power/reset/Kconfig
> +++ b/drivers/power/reset/Kconfig
> @@ -354,4 +354,14 @@ config POWER_MLXBF
> help
> This driver supports reset or low power mode handling for Mellanox BlueField.
>
> +config POWER_RESET_QEMU_VIRT_CTRL
> + bool "QEMU Virt Machine System Controller"
> + depends on HAS_IOMEM
> + help
> + This driver supports the system reset and power off functionality
> + provided by the QEMU 'virt-ctrl' device.
> +
> + Say Y here if you are running Linux on a QEMU virtual machine that
> + provides this controller, such as the m68k virt machine.
> +
> endif
> diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile
> index 0e4ae6f6b5c5..d7ae97241a83 100644
> --- a/drivers/power/reset/Makefile
> +++ b/drivers/power/reset/Makefile
> @@ -41,3 +41,4 @@ obj-$(CONFIG_SYSCON_REBOOT_MODE) += syscon-reboot-mode.o
> obj-$(CONFIG_POWER_RESET_SC27XX) += sc27xx-poweroff.o
> obj-$(CONFIG_NVMEM_REBOOT_MODE) += nvmem-reboot-mode.o
> obj-$(CONFIG_POWER_MLXBF) += pwr-mlxbf.o
> +obj-$(CONFIG_POWER_RESET_QEMU_VIRT_CTRL) += qemu-virt-ctrl.o
> diff --git a/drivers/power/reset/qemu-virt-ctrl.c b/drivers/power/reset/qemu-virt-ctrl.c
> new file mode 100644
> index 000000000000..f1acd22172ce
> --- /dev/null
> +++ b/drivers/power/reset/qemu-virt-ctrl.c
> @@ -0,0 +1,98 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * QEMU Virt Machine System Controller Driver
> + *
> + * Copyright (C) 2026 Kuan-Wei Chiu <visitorckw@gmail.com>
> + */
> +
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/reboot.h>
> +
> +/* Registers */
> +#define VIRT_CTRL_REG_FEATURES 0x00
> +#define VIRT_CTRL_REG_CMD 0x04
> +
> +/* Commands */
> +#define CMD_NOOP 0
> +#define CMD_RESET 1
> +#define CMD_HALT 2
> +#define CMD_PANIC 3
> +
> +struct qemu_virt_ctrl {
> + void __iomem *base;
> + struct notifier_block restart_nb;
> +};
> +
> +static void __iomem *qemu_virt_ctrl_base;
> +
> +static void qemu_virt_ctrl_power_off(void)
> +{
> + if (qemu_virt_ctrl_base)
> + iowrite32be(CMD_HALT, qemu_virt_ctrl_base + VIRT_CTRL_REG_CMD);
> +}
> +
> +static int qemu_virt_ctrl_restart(struct notifier_block *nb, unsigned long action,
> + void *data)
> +{
> + struct qemu_virt_ctrl *vc = container_of(nb, struct qemu_virt_ctrl, restart_nb);
> +
> + iowrite32be(CMD_RESET, vc->base + VIRT_CTRL_REG_CMD);
> +
> + return NOTIFY_DONE;
> +}
> +
> +static int qemu_virt_ctrl_probe(struct platform_device *pdev)
> +{
> + struct qemu_virt_ctrl *vc;
> + int ret;
> +
> + vc = devm_kzalloc(&pdev->dev, sizeof(*vc), GFP_KERNEL);
> + if (!vc)
> + return -ENOMEM;
> +
> + vc->base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(vc->base))
> + return PTR_ERR(vc->base);
> +
> + qemu_virt_ctrl_base = vc->base;
> +
> + vc->restart_nb.notifier_call = qemu_virt_ctrl_restart;
> + vc->restart_nb.priority = 128;
> +
> + ret = register_restart_handler(&vc->restart_nb);
> + if (ret)
> + return dev_err_probe(&pdev->dev, ret,
> + "cannot register restart handler\n");
Please use devm_register_sys_off_handler().
> + if (!pm_power_off)
> + pm_power_off = qemu_virt_ctrl_power_off;
same.
> + platform_set_drvdata(pdev, vc);
> +
> + return 0;
> +}
> +
> +static void qemu_virt_ctrl_remove(struct platform_device *pdev)
> +{
> + struct qemu_virt_ctrl *vc = platform_get_drvdata(pdev);
> +
> + unregister_restart_handler(&vc->restart_nb);
> +
> + if (pm_power_off == qemu_virt_ctrl_power_off)
> + pm_power_off = NULL;
> +}
> +
> +static struct platform_driver qemu_virt_ctrl_driver = {
> + .probe = qemu_virt_ctrl_probe,
> + .remove = qemu_virt_ctrl_remove,
> + .driver = {
> + .name = "qemu-virt-ctrl",
> + },
> +};
> +module_platform_driver(qemu_virt_ctrl_driver);
> +
> +MODULE_AUTHOR("Kuan-Wei Chiu <visitorckw@gmail.com>");
> +MODULE_DESCRIPTION("QEMU Virt Machine System Controller Driver");
> +MODULE_LICENSE("GPL");
You mark this as module_platform_driver() and add MODULE_*, but the
Kconfig option is bool. To be usable as a module it is missing setup
of .id_table in the platform_driver struct for auto-loading.
Greetings,
-- Sebastian
Hi Kuan-Wei, On Tue, 13 Jan 2026 at 03:27, Kuan-Wei Chiu <visitorckw@gmail.com> wrote: > > Add a new driver for the 'virt-ctrl' device found on QEMU virt machines > (e.g. m68k). This device provides a simple interface for system reset > and power off [1]. > > This driver registers a restart handler for system reboot and sets the > global pm_power_off callback for system shutdown. It is designed to be > generic and can be reused by other architectures utilizing this QEMU > device. > > Link: https://gitlab.com/qemu-project/qemu/-/blob/v10.2.0/hw/misc/virt_ctrl.c [1] > Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com> FWIW: I have a driver for this in my "m68k with devicetree" tree. As far as I could tell the virt_ctrl thing in QEMU might get more features aside from power control. So I made it a misc device instead. Cheers, Daniel
Hi Daniel, On Wed, Jan 14, 2026 at 07:01:58PM +0900, Daniel Palmer wrote: > Hi Kuan-Wei, > > On Tue, 13 Jan 2026 at 03:27, Kuan-Wei Chiu <visitorckw@gmail.com> wrote: > > > > Add a new driver for the 'virt-ctrl' device found on QEMU virt machines > > (e.g. m68k). This device provides a simple interface for system reset > > and power off [1]. > > > > This driver registers a restart handler for system reboot and sets the > > global pm_power_off callback for system shutdown. It is designed to be > > generic and can be reused by other architectures utilizing this QEMU > > device. > > > > Link: https://gitlab.com/qemu-project/qemu/-/blob/v10.2.0/hw/misc/virt_ctrl.c [1] > > Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com> > > FWIW: I have a driver for this in my "m68k with devicetree" tree. As > far as I could tell the virt_ctrl thing in QEMU might get more > features aside from power control. > So I made it a misc device instead. Thanks for the note. Just out of curious, are there currently specific plans to add non-power features to virt_ctrl? If new features are added, shouldn't they be exposed via separate drivers in their respective subsystems, rather than consolidating everything into a misc driver? Regards, Kuan-Wei
Hi Kuan-Wei, On Thu, 15 Jan 2026 at 15:15, Kuan-Wei Chiu <visitorckw@gmail.com> wrote: > > FWIW: I have a driver for this in my "m68k with devicetree" tree. As > > far as I could tell the virt_ctrl thing in QEMU might get more > > features aside from power control. > > So I made it a misc device instead. > > Thanks for the note. > Just out of curious, are there currently specific plans to add > non-power features to virt_ctrl? The docs here suggest that power control is the only currently implemented feature but doesn't have to be the only feature: https://github.com/qemu/qemu/blob/master/docs/specs/virt-ctlr.rst > If new features are added, shouldn't they be exposed via separate > drivers in their respective subsystems, rather than consolidating > everything into a misc driver? I guess if it did get new features maybe it'd be a multifunction device? Since nothing except power control has ever actually been implemented, maybe the way you have it right now makes the most sense. Thanks, Daniel
Hi Daniel, On Thu, Jan 15, 2026 at 10:29:29PM +0900, Daniel Palmer wrote: > Hi Kuan-Wei, > > On Thu, 15 Jan 2026 at 15:15, Kuan-Wei Chiu <visitorckw@gmail.com> wrote: > > > > FWIW: I have a driver for this in my "m68k with devicetree" tree. As > > > far as I could tell the virt_ctrl thing in QEMU might get more > > > features aside from power control. > > > So I made it a misc device instead. > > > > Thanks for the note. > > Just out of curious, are there currently specific plans to add > > non-power features to virt_ctrl? > > The docs here suggest that power control is the only currently > implemented feature but doesn't have to be the only feature: > https://github.com/qemu/qemu/blob/master/docs/specs/virt-ctlr.rst > > > If new features are added, shouldn't they be exposed via separate > > drivers in their respective subsystems, rather than consolidating > > everything into a misc driver? > > I guess if it did get new features maybe it'd be a multifunction > device? Since nothing except power control has ever actually been > implemented, maybe the way you have it right now makes the most sense. > I agree that migrating to an MFD architecture would be the right move if/when qemu implements additional features. For now, I will stick to the current approach in drivers/power/reset as it fits the existing functionality best. Thanks again for your feedback. Regards, Kuan-Wei
© 2016 - 2026 Red Hat, Inc.