* [Qemu-devel] [PATCH 0/3] PPC: E500: Add power off GPIO
@ 2014-10-01 14:15 Alexander Graf
2014-10-01 14:15 ` [Qemu-devel] [PATCH 1/3] PPC: Add MPC8XXX gpio controller Alexander Graf
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Alexander Graf @ 2014-10-01 14:15 UTC (permalink / raw)
To: qemu-ppc; +Cc: qemu-devel
When running a virtual machine, you eventually want to shut it down and see it
properly end the QEMU process. The magic that makes this work is the "power off"
code path that a guest OS uses to indicate that it wants to power off the host
machine.
This logic was missing from our e500 virt machine. The easiest way to implement
this is to expose a GPIO to the guest that allows the virtual machine to
indicate to QEMU that it wants to power down now.
To expose this GPIO to the guest, we need to add a GPIO controller, the GPIO
hookup and some description in device tree. This is what this patch set does.
We only support this for the e500 virt machine though, as the mpc8544ds does
not support power off from an OS.
To make use of this logic, you will also need patches to Linux that enable it
to register the gpio-poweroff driver:
https://lists.ozlabs.org/pipermail/linuxppc-dev/2014-October/121532.html
And you need to make sure that the following options are enabled in your guest
kernel:
CONFIG_POWER_SUPPLY=y
CONFIG_POWER_RESET=y
CONFIG_POWER_RESET_GPIO=y
Alexander Graf (3):
PPC: Add MPC8XXX gpio controller
PPC: E500: Instantiate MPC8XXX gpio controller on virt machine
PPC: E500: Hook up power off GPIO to GPIO controller
hw/gpio/Makefile.objs | 1 +
hw/gpio/mpc8xxx.c | 217 ++++++++++++++++++++++++++++++++++++++++++++++++++
hw/ppc/e500.c | 56 +++++++++++++
hw/ppc/e500.h | 1 +
hw/ppc/e500plat.c | 1 +
5 files changed, 276 insertions(+)
create mode 100644 hw/gpio/mpc8xxx.c
--
1.8.1.4
^ permalink raw reply [flat|nested] 4+ messages in thread
* [Qemu-devel] [PATCH 1/3] PPC: Add MPC8XXX gpio controller
2014-10-01 14:15 [Qemu-devel] [PATCH 0/3] PPC: E500: Add power off GPIO Alexander Graf
@ 2014-10-01 14:15 ` Alexander Graf
2014-10-01 14:15 ` [Qemu-devel] [PATCH 2/3] PPC: E500: Instantiate MPC8XXX gpio controller on virt machine Alexander Graf
2014-10-01 14:15 ` [Qemu-devel] [PATCH 3/3] PPC: E500: Hook up power off GPIO to GPIO controller Alexander Graf
2 siblings, 0 replies; 4+ messages in thread
From: Alexander Graf @ 2014-10-01 14:15 UTC (permalink / raw)
To: qemu-ppc; +Cc: qemu-devel
On e500 systems most SoCs implement a common GPIO controller that Linux
calls the "mpc8xxx" gpio controller. This patch adds an emulation model
for this device.
Signed-off-by: Alexander Graf <agraf@suse.de>
---
hw/gpio/Makefile.objs | 1 +
hw/gpio/mpc8xxx.c | 217 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 218 insertions(+)
create mode 100644 hw/gpio/mpc8xxx.c
diff --git a/hw/gpio/Makefile.objs b/hw/gpio/Makefile.objs
index 2c8b51f..1abcf17 100644
--- a/hw/gpio/Makefile.objs
+++ b/hw/gpio/Makefile.objs
@@ -2,5 +2,6 @@ common-obj-$(CONFIG_MAX7310) += max7310.o
common-obj-$(CONFIG_PL061) += pl061.o
common-obj-$(CONFIG_PUV3) += puv3_gpio.o
common-obj-$(CONFIG_ZAURUS) += zaurus.o
+common-obj-$(CONFIG_E500) += mpc8xxx.o
obj-$(CONFIG_OMAP) += omap_gpio.o
diff --git a/hw/gpio/mpc8xxx.c b/hw/gpio/mpc8xxx.c
new file mode 100644
index 0000000..1aeaaaa
--- /dev/null
+++ b/hw/gpio/mpc8xxx.c
@@ -0,0 +1,217 @@
+/*
+ * GPIO Controller for a lot of Freescale SoCs
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * Author: Alexander Graf, <agraf@suse.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/sysbus.h"
+
+#define TYPE_MPC8XXX_GPIO "mpc8xxx_gpio"
+#define MPC8XXX_GPIO(obj) OBJECT_CHECK(MPC8XXXGPIOState, (obj), TYPE_MPC8XXX_GPIO)
+
+typedef struct MPC8XXXGPIOState {
+ SysBusDevice parent_obj;
+
+ MemoryRegion iomem;
+ qemu_irq irq;
+ qemu_irq out[32];
+
+ uint32_t dir;
+ uint32_t odr;
+ uint32_t dat;
+ uint32_t ier;
+ uint32_t imr;
+ uint32_t icr;
+} MPC8XXXGPIOState;
+
+static const VMStateDescription vmstate_mpc8xxx_gpio = {
+ .name = "mpc8xxx_gpio",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(dir, MPC8XXXGPIOState),
+ VMSTATE_UINT32(odr, MPC8XXXGPIOState),
+ VMSTATE_UINT32(dat, MPC8XXXGPIOState),
+ VMSTATE_UINT32(ier, MPC8XXXGPIOState),
+ VMSTATE_UINT32(imr, MPC8XXXGPIOState),
+ VMSTATE_UINT32(icr, MPC8XXXGPIOState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void mpc8xxx_gpio_update(MPC8XXXGPIOState *s)
+{
+ qemu_set_irq(s->irq, !!(s->ier & s->imr));
+}
+
+static uint64_t mpc8xxx_gpio_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque;
+
+ if (size != 4) {
+ /* All registers are 32bit */
+ return 0;
+ }
+
+ switch (offset) {
+ case 0x0: /* Direction */
+ return s->dir;
+ case 0x4: /* Open Drain */
+ return s->odr;
+ case 0x8: /* Data */
+ return s->dat;
+ case 0xC: /* Interrupt Event */
+ return s->ier;
+ case 0x10: /* Interrupt Mask */
+ return s->imr;
+ case 0x14: /* Interrupt Control */
+ return s->icr;
+ default:
+ return 0;
+ }
+}
+
+static void mpc8xxx_write_data(MPC8XXXGPIOState *s, uint32_t new_data)
+{
+ uint32_t old_data = s->dat;
+ uint32_t diff = old_data ^ new_data;
+ int i;
+
+ for (i = 0; i < 32; i++) {
+ uint32_t mask = 0x80000000 >> i;
+ if (!(diff & mask)) {
+ continue;
+ }
+
+ if (s->dir & mask) {
+ /* Output */
+ qemu_set_irq(s->out[i], (new_data & mask) != 0);
+ }
+ }
+
+ s->dat = new_data;
+}
+
+static void mpc8xxx_gpio_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque;
+
+ if (size != 4) {
+ /* All registers are 32bit */
+ return;
+ }
+
+ switch (offset) {
+ case 0x0: /* Direction */
+ s->dir = value;
+ break;
+ case 0x4: /* Open Drain */
+ s->odr = value;
+ break;
+ case 0x8: /* Data */
+ mpc8xxx_write_data(s, value);
+ break;
+ case 0xC: /* Interrupt Event */
+ s->ier &= ~value;
+ break;
+ case 0x10: /* Interrupt Mask */
+ s->imr = value;
+ break;
+ case 0x14: /* Interrupt Control */
+ s->icr = value;
+ break;
+ }
+
+ mpc8xxx_gpio_update(s);
+}
+
+static void mpc8xxx_gpio_reset(MPC8XXXGPIOState *s)
+{
+ s->dir = 0;
+ s->odr = 0;
+ s->dat = 0;
+ s->ier = 0;
+ s->imr = 0;
+ s->icr = 0;
+}
+
+static void mpc8xxx_gpio_set_irq(void * opaque, int irq, int level)
+{
+ MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque;
+ uint32_t mask;
+
+ mask = 0x80000000 >> irq;
+ if ((s->dir & mask) == 0) {
+ uint32_t old_value = s->dat & mask;
+
+ s->dat &= ~mask;
+ if (level)
+ s->dat |= mask;
+
+ if (!(s->icr & irq) || (old_value && !level)) {
+ s->ier |= mask;
+ }
+
+ mpc8xxx_gpio_update(s);
+ }
+}
+
+static const MemoryRegionOps mpc8xxx_gpio_ops = {
+ .read = mpc8xxx_gpio_read,
+ .write = mpc8xxx_gpio_write,
+ .endianness = DEVICE_BIG_ENDIAN,
+};
+
+static int mpc8xxx_gpio_initfn(SysBusDevice *sbd)
+{
+ DeviceState *dev = DEVICE(sbd);
+ MPC8XXXGPIOState *s = MPC8XXX_GPIO(dev);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &mpc8xxx_gpio_ops, s, "mpc8xxx_gpio", 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+ sysbus_init_irq(sbd, &s->irq);
+ qdev_init_gpio_in(dev, mpc8xxx_gpio_set_irq, 32);
+ qdev_init_gpio_out(dev, s->out, 32);
+ mpc8xxx_gpio_reset(s);
+ return 0;
+}
+
+static void mpc8xxx_gpio_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = mpc8xxx_gpio_initfn;
+ dc->vmsd = &vmstate_mpc8xxx_gpio;
+}
+
+static const TypeInfo mpc8xxx_gpio_info = {
+ .name = TYPE_MPC8XXX_GPIO,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(MPC8XXXGPIOState),
+ .class_init = mpc8xxx_gpio_class_init,
+};
+
+static void mpc8xxx_gpio_register_types(void)
+{
+ type_register_static(&mpc8xxx_gpio_info);
+}
+
+type_init(mpc8xxx_gpio_register_types)
--
1.8.1.4
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [Qemu-devel] [PATCH 2/3] PPC: E500: Instantiate MPC8XXX gpio controller on virt machine
2014-10-01 14:15 [Qemu-devel] [PATCH 0/3] PPC: E500: Add power off GPIO Alexander Graf
2014-10-01 14:15 ` [Qemu-devel] [PATCH 1/3] PPC: Add MPC8XXX gpio controller Alexander Graf
@ 2014-10-01 14:15 ` Alexander Graf
2014-10-01 14:15 ` [Qemu-devel] [PATCH 3/3] PPC: E500: Hook up power off GPIO to GPIO controller Alexander Graf
2 siblings, 0 replies; 4+ messages in thread
From: Alexander Graf @ 2014-10-01 14:15 UTC (permalink / raw)
To: qemu-ppc; +Cc: qemu-devel
With the e500 virt machine, we don't have to adhere to the exact hardware
layout of an mpc8544ds board. So there we can just add a qoriq compatible
GPIO controller into the system that we can add a power off hook to.
Signed-off-by: Alexander Graf <agraf@suse.de>
---
hw/ppc/e500.c | 32 ++++++++++++++++++++++++++++++++
hw/ppc/e500.h | 1 +
hw/ppc/e500plat.c | 1 +
3 files changed, 34 insertions(+)
diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c
index 22cfeac..db82f72 100644
--- a/hw/ppc/e500.c
+++ b/hw/ppc/e500.c
@@ -64,6 +64,8 @@
#define MPC8544_PCI_IO 0xE1000000ULL
#define MPC8544_UTIL_OFFSET 0xe0000ULL
#define MPC8544_SPIN_BASE 0xEF000000ULL
+#define MPC8XXX_GPIO_OFFSET 0x000FF000ULL
+#define MPC8XXX_GPIO_IRQ 43
struct boot_info
{
@@ -170,6 +172,23 @@ static int create_devtree_etsec(SysBusDevice *sbdev, PlatformDevtreeData *data)
return 0;
}
+static void create_dt_mpc8xxx_gpio(void *fdt, const char *soc, const char *mpic)
+{
+ hwaddr mmio0 = MPC8XXX_GPIO_OFFSET;
+ int irq0 = MPC8XXX_GPIO_IRQ;
+ gchar *node = g_strdup_printf("%s/gpio@%"PRIx64, soc, mmio0);
+
+ qemu_fdt_add_subnode(fdt, node);
+ qemu_fdt_setprop_string(fdt, node, "compatible", "fsl,qoriq-gpio");
+ qemu_fdt_setprop_cells(fdt, node, "reg", mmio0, 0x1000);
+ qemu_fdt_setprop_cells(fdt, node, "interrupts", irq0, 0x2);
+ qemu_fdt_setprop_phandle(fdt, node, "interrupt-parent", mpic);
+ qemu_fdt_setprop_cells(fdt, node, "#gpio-cells", 2);
+ qemu_fdt_setprop(fdt, node, "gpio-controller", NULL, 0);
+
+ g_free(node);
+}
+
static int sysbus_device_create_devtree(SysBusDevice *sbdev, void *opaque)
{
PlatformDevtreeData *data = opaque;
@@ -490,6 +509,10 @@ static int ppce500_load_device_tree(MachineState *machine,
qemu_fdt_setprop_cell(fdt, pci, "#address-cells", 3);
qemu_fdt_setprop_string(fdt, "/aliases", "pci0", pci);
+ if (params->has_mpc8xxx_gpio) {
+ create_dt_mpc8xxx_gpio(fdt, soc, mpic);
+ }
+
if (params->has_platform_bus) {
platform_bus_create_devtree(params, fdt, mpic);
}
@@ -912,6 +935,15 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
sysbus_mmio_get_region(s, 0));
}
+ if (params->has_mpc8xxx_gpio) {
+ dev = qdev_create(NULL, "mpc8xxx_gpio");
+ s = SYS_BUS_DEVICE(dev);
+ qdev_init_nofail(dev);
+ sysbus_connect_irq(s, 0, mpic[MPC8XXX_GPIO_IRQ]);
+ memory_region_add_subregion(ccsr_addr_space, MPC8XXX_GPIO_OFFSET,
+ sysbus_mmio_get_region(s, 0));
+ }
+
/* Load kernel. */
if (machine->kernel_filename) {
kernel_base = cur_base;
diff --git a/hw/ppc/e500.h b/hw/ppc/e500.h
index f1b2766..08a4c3d 100644
--- a/hw/ppc/e500.h
+++ b/hw/ppc/e500.h
@@ -16,6 +16,7 @@ typedef struct PPCE500Params {
hwaddr platform_bus_size;
int platform_bus_first_irq;
int platform_bus_num_irqs;
+ bool has_mpc8xxx_gpio;
} PPCE500Params;
void ppce500_init(MachineState *machine, PPCE500Params *params);
diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c
index befe1d1..2d14d58 100644
--- a/hw/ppc/e500plat.c
+++ b/hw/ppc/e500plat.c
@@ -40,6 +40,7 @@ static void e500plat_init(MachineState *machine)
.platform_bus_size = (128ULL * 1024 * 1024),
.platform_bus_first_irq = 5,
.platform_bus_num_irqs = 10,
+ .has_mpc8xxx_gpio = true,
};
/* Older KVM versions don't support EPR which breaks guests when we announce
--
1.8.1.4
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [Qemu-devel] [PATCH 3/3] PPC: E500: Hook up power off GPIO to GPIO controller
2014-10-01 14:15 [Qemu-devel] [PATCH 0/3] PPC: E500: Add power off GPIO Alexander Graf
2014-10-01 14:15 ` [Qemu-devel] [PATCH 1/3] PPC: Add MPC8XXX gpio controller Alexander Graf
2014-10-01 14:15 ` [Qemu-devel] [PATCH 2/3] PPC: E500: Instantiate MPC8XXX gpio controller on virt machine Alexander Graf
@ 2014-10-01 14:15 ` Alexander Graf
2 siblings, 0 replies; 4+ messages in thread
From: Alexander Graf @ 2014-10-01 14:15 UTC (permalink / raw)
To: qemu-ppc; +Cc: qemu-devel
Now that we have a working GPIO controller on the virt machine, we can use
one pin to notify QEMU that the guests wants to power off the system.
Signed-off-by: Alexander Graf <agraf@suse.de>
---
hw/ppc/e500.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c
index db82f72..cc12f90 100644
--- a/hw/ppc/e500.c
+++ b/hw/ppc/e500.c
@@ -177,6 +177,8 @@ static void create_dt_mpc8xxx_gpio(void *fdt, const char *soc, const char *mpic)
hwaddr mmio0 = MPC8XXX_GPIO_OFFSET;
int irq0 = MPC8XXX_GPIO_IRQ;
gchar *node = g_strdup_printf("%s/gpio@%"PRIx64, soc, mmio0);
+ gchar *poweroff = g_strdup_printf("%s/power-off", soc);
+ int gpio_ph;
qemu_fdt_add_subnode(fdt, node);
qemu_fdt_setprop_string(fdt, node, "compatible", "fsl,qoriq-gpio");
@@ -185,8 +187,17 @@ static void create_dt_mpc8xxx_gpio(void *fdt, const char *soc, const char *mpic)
qemu_fdt_setprop_phandle(fdt, node, "interrupt-parent", mpic);
qemu_fdt_setprop_cells(fdt, node, "#gpio-cells", 2);
qemu_fdt_setprop(fdt, node, "gpio-controller", NULL, 0);
+ gpio_ph = qemu_fdt_alloc_phandle(fdt);
+ qemu_fdt_setprop_cell(fdt, node, "phandle", gpio_ph);
+ qemu_fdt_setprop_cell(fdt, node, "linux,phandle", gpio_ph);
+
+ /* Power Off Pin */
+ qemu_fdt_add_subnode(fdt, poweroff);
+ qemu_fdt_setprop_string(fdt, poweroff, "compatible", "gpio-poweroff");
+ qemu_fdt_setprop_cells(fdt, poweroff, "gpios", gpio_ph, 0, 0);
g_free(node);
+ g_free(poweroff);
}
static int sysbus_device_create_devtree(SysBusDevice *sbdev, void *opaque)
@@ -765,6 +776,13 @@ static qemu_irq *ppce500_init_mpic(PPCE500Params *params, MemoryRegion *ccsr,
return mpic;
}
+static void ppce500_power_off(void *opaque, int line, int on)
+{
+ if (on) {
+ qemu_system_shutdown_request();
+ }
+}
+
void ppce500_init(MachineState *machine, PPCE500Params *params)
{
MemoryRegion *address_space_mem = get_system_memory();
@@ -936,12 +954,18 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
}
if (params->has_mpc8xxx_gpio) {
+ qemu_irq poweroff_irq;
+
dev = qdev_create(NULL, "mpc8xxx_gpio");
s = SYS_BUS_DEVICE(dev);
qdev_init_nofail(dev);
sysbus_connect_irq(s, 0, mpic[MPC8XXX_GPIO_IRQ]);
memory_region_add_subregion(ccsr_addr_space, MPC8XXX_GPIO_OFFSET,
sysbus_mmio_get_region(s, 0));
+
+ /* Power Off GPIO at Pin 0 */
+ poweroff_irq = qemu_allocate_irq(ppce500_power_off, NULL, 0);
+ qdev_connect_gpio_out(dev, 0, poweroff_irq);
}
/* Load kernel. */
--
1.8.1.4
^ permalink raw reply related [flat|nested] 4+ messages in thread
end of thread, other threads:[~2014-10-01 14:16 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-10-01 14:15 [Qemu-devel] [PATCH 0/3] PPC: E500: Add power off GPIO Alexander Graf
2014-10-01 14:15 ` [Qemu-devel] [PATCH 1/3] PPC: Add MPC8XXX gpio controller Alexander Graf
2014-10-01 14:15 ` [Qemu-devel] [PATCH 2/3] PPC: E500: Instantiate MPC8XXX gpio controller on virt machine Alexander Graf
2014-10-01 14:15 ` [Qemu-devel] [PATCH 3/3] PPC: E500: Hook up power off GPIO to GPIO controller Alexander Graf
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).