+++ /dev/null
-/*
- * QEMU PowerPC MPC8544 global util pseudo-device
- *
- * Copyright (C) 2011 Freescale Semiconductor, Inc. All rights reserved.
- *
- * Author: Alexander Graf, <alex@csgraf.de>
- *
- * This is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * *****************************************************************
- *
- * The documentation for this device is noted in the MPC8544 documentation,
- * file name "MPC8544ERM.pdf". You can easily find it on the web.
- *
- */
-
-#include "hw/hw.h"
-#include "sysemu/sysemu.h"
-#include "hw/sysbus.h"
-
-#define MPC8544_GUTS_MMIO_SIZE 0x1000
-#define MPC8544_GUTS_RSTCR_RESET 0x02
-
-#define MPC8544_GUTS_ADDR_PORPLLSR 0x00
-#define MPC8544_GUTS_ADDR_PORBMSR 0x04
-#define MPC8544_GUTS_ADDR_PORIMPSCR 0x08
-#define MPC8544_GUTS_ADDR_PORDEVSR 0x0C
-#define MPC8544_GUTS_ADDR_PORDBGMSR 0x10
-#define MPC8544_GUTS_ADDR_PORDEVSR2 0x14
-#define MPC8544_GUTS_ADDR_GPPORCR 0x20
-#define MPC8544_GUTS_ADDR_GPIOCR 0x30
-#define MPC8544_GUTS_ADDR_GPOUTDR 0x40
-#define MPC8544_GUTS_ADDR_GPINDR 0x50
-#define MPC8544_GUTS_ADDR_PMUXCR 0x60
-#define MPC8544_GUTS_ADDR_DEVDISR 0x70
-#define MPC8544_GUTS_ADDR_POWMGTCSR 0x80
-#define MPC8544_GUTS_ADDR_MCPSUMR 0x90
-#define MPC8544_GUTS_ADDR_RSTRSCR 0x94
-#define MPC8544_GUTS_ADDR_PVR 0xA0
-#define MPC8544_GUTS_ADDR_SVR 0xA4
-#define MPC8544_GUTS_ADDR_RSTCR 0xB0
-#define MPC8544_GUTS_ADDR_IOVSELSR 0xC0
-#define MPC8544_GUTS_ADDR_DDRCSR 0xB20
-#define MPC8544_GUTS_ADDR_DDRCDR 0xB24
-#define MPC8544_GUTS_ADDR_DDRCLKDR 0xB28
-#define MPC8544_GUTS_ADDR_CLKOCR 0xE00
-#define MPC8544_GUTS_ADDR_SRDS1CR1 0xF04
-#define MPC8544_GUTS_ADDR_SRDS2CR1 0xF10
-#define MPC8544_GUTS_ADDR_SRDS2CR3 0xF18
-
-struct GutsState {
- SysBusDevice busdev;
- MemoryRegion iomem;
-};
-
-typedef struct GutsState GutsState;
-
-static uint64_t mpc8544_guts_read(void *opaque, hwaddr addr,
- unsigned size)
-{
- uint32_t value = 0;
- CPUPPCState *env = cpu_single_env;
-
- addr &= MPC8544_GUTS_MMIO_SIZE - 1;
- switch (addr) {
- case MPC8544_GUTS_ADDR_PVR:
- value = env->spr[SPR_PVR];
- break;
- case MPC8544_GUTS_ADDR_SVR:
- value = env->spr[SPR_E500_SVR];
- break;
- default:
- fprintf(stderr, "guts: Unknown register read: %x\n", (int)addr);
- break;
- }
-
- return value;
-}
-
-static void mpc8544_guts_write(void *opaque, hwaddr addr,
- uint64_t value, unsigned size)
-{
- addr &= MPC8544_GUTS_MMIO_SIZE - 1;
-
- switch (addr) {
- case MPC8544_GUTS_ADDR_RSTCR:
- if (value & MPC8544_GUTS_RSTCR_RESET) {
- qemu_system_reset_request();
- }
- break;
- default:
- fprintf(stderr, "guts: Unknown register write: %x = %x\n",
- (int)addr, (unsigned)value);
- break;
- }
-}
-
-static const MemoryRegionOps mpc8544_guts_ops = {
- .read = mpc8544_guts_read,
- .write = mpc8544_guts_write,
- .endianness = DEVICE_BIG_ENDIAN,
- .valid = {
- .min_access_size = 4,
- .max_access_size = 4,
- },
-};
-
-static int mpc8544_guts_initfn(SysBusDevice *dev)
-{
- GutsState *s;
-
- s = FROM_SYSBUS(GutsState, SYS_BUS_DEVICE(dev));
-
- memory_region_init_io(&s->iomem, &mpc8544_guts_ops, s,
- "mpc6544.guts", MPC8544_GUTS_MMIO_SIZE);
- sysbus_init_mmio(dev, &s->iomem);
-
- return 0;
-}
-
-static void mpc8544_guts_class_init(ObjectClass *klass, void *data)
-{
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
- k->init = mpc8544_guts_initfn;
-}
-
-static const TypeInfo mpc8544_guts_info = {
- .name = "mpc8544-guts",
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(GutsState),
- .class_init = mpc8544_guts_class_init,
-};
-
-static void mpc8544_guts_register_types(void)
-{
- type_register_static(&mpc8544_guts_info);
-}
-
-type_init(mpc8544_guts_register_types)
# PREP target
obj-y += mc146818rtc.o
# IBM pSeries (sPAPR)
-obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_rtas.o spapr_vio.o
-obj-$(CONFIG_PSERIES) += xics.o spapr_vty.o spapr_llan.o spapr_vscsi.o
+obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_rtas.o
+obj-$(CONFIG_PSERIES) += spapr_vty.o spapr_llan.o spapr_vscsi.o
obj-$(CONFIG_PSERIES) += spapr_pci.o pci/pci-hotplug.o spapr_iommu.o
obj-$(CONFIG_PSERIES) += spapr_events.o spapr_nvram.o
# PowerPC 4xx boards
-obj-y += ppc4xx_devs.o ppc4xx_pci.o
-# PowerPC E500 boards
-obj-$(CONFIG_E500) += mpc8544_guts.o ppce500_spin.o
+obj-y += ppc4xx_pci.o
# PowerPC OpenPIC
obj-y += openpic.o
# shared objects
obj-y += ppc.o ppc_booke.o
# IBM pSeries (sPAPR)
-obj-$(CONFIG_PSERIES) += spapr.o
+obj-$(CONFIG_PSERIES) += spapr.o xics.o spapr_vio.o
# PowerPC 4xx boards
-obj-y += ppc405_boards.o ppc405_uc.o ppc440_bamboo.o
+obj-y += ppc405_boards.o ppc4xx_devs.o ppc405_uc.o ppc440_bamboo.o
# PReP
obj-y += prep.o
# OldWorld PowerMac
obj-y += mac_newworld.o
# e500
obj-$(CONFIG_E500) += e500.o mpc8544ds.o e500plat.o
+obj-$(CONFIG_E500) += mpc8544_guts.o ppce500_spin.o
# PowerPC 440 Xilinx ML507 reference board.
obj-y += virtex_ml507.o
--- /dev/null
+/*
+ * QEMU PowerPC MPC8544 global util pseudo-device
+ *
+ * Copyright (C) 2011 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * Author: Alexander Graf, <alex@csgraf.de>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * *****************************************************************
+ *
+ * The documentation for this device is noted in the MPC8544 documentation,
+ * file name "MPC8544ERM.pdf". You can easily find it on the web.
+ *
+ */
+
+#include "hw/hw.h"
+#include "sysemu/sysemu.h"
+#include "hw/sysbus.h"
+
+#define MPC8544_GUTS_MMIO_SIZE 0x1000
+#define MPC8544_GUTS_RSTCR_RESET 0x02
+
+#define MPC8544_GUTS_ADDR_PORPLLSR 0x00
+#define MPC8544_GUTS_ADDR_PORBMSR 0x04
+#define MPC8544_GUTS_ADDR_PORIMPSCR 0x08
+#define MPC8544_GUTS_ADDR_PORDEVSR 0x0C
+#define MPC8544_GUTS_ADDR_PORDBGMSR 0x10
+#define MPC8544_GUTS_ADDR_PORDEVSR2 0x14
+#define MPC8544_GUTS_ADDR_GPPORCR 0x20
+#define MPC8544_GUTS_ADDR_GPIOCR 0x30
+#define MPC8544_GUTS_ADDR_GPOUTDR 0x40
+#define MPC8544_GUTS_ADDR_GPINDR 0x50
+#define MPC8544_GUTS_ADDR_PMUXCR 0x60
+#define MPC8544_GUTS_ADDR_DEVDISR 0x70
+#define MPC8544_GUTS_ADDR_POWMGTCSR 0x80
+#define MPC8544_GUTS_ADDR_MCPSUMR 0x90
+#define MPC8544_GUTS_ADDR_RSTRSCR 0x94
+#define MPC8544_GUTS_ADDR_PVR 0xA0
+#define MPC8544_GUTS_ADDR_SVR 0xA4
+#define MPC8544_GUTS_ADDR_RSTCR 0xB0
+#define MPC8544_GUTS_ADDR_IOVSELSR 0xC0
+#define MPC8544_GUTS_ADDR_DDRCSR 0xB20
+#define MPC8544_GUTS_ADDR_DDRCDR 0xB24
+#define MPC8544_GUTS_ADDR_DDRCLKDR 0xB28
+#define MPC8544_GUTS_ADDR_CLKOCR 0xE00
+#define MPC8544_GUTS_ADDR_SRDS1CR1 0xF04
+#define MPC8544_GUTS_ADDR_SRDS2CR1 0xF10
+#define MPC8544_GUTS_ADDR_SRDS2CR3 0xF18
+
+struct GutsState {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+};
+
+typedef struct GutsState GutsState;
+
+static uint64_t mpc8544_guts_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ uint32_t value = 0;
+ CPUPPCState *env = cpu_single_env;
+
+ addr &= MPC8544_GUTS_MMIO_SIZE - 1;
+ switch (addr) {
+ case MPC8544_GUTS_ADDR_PVR:
+ value = env->spr[SPR_PVR];
+ break;
+ case MPC8544_GUTS_ADDR_SVR:
+ value = env->spr[SPR_E500_SVR];
+ break;
+ default:
+ fprintf(stderr, "guts: Unknown register read: %x\n", (int)addr);
+ break;
+ }
+
+ return value;
+}
+
+static void mpc8544_guts_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ addr &= MPC8544_GUTS_MMIO_SIZE - 1;
+
+ switch (addr) {
+ case MPC8544_GUTS_ADDR_RSTCR:
+ if (value & MPC8544_GUTS_RSTCR_RESET) {
+ qemu_system_reset_request();
+ }
+ break;
+ default:
+ fprintf(stderr, "guts: Unknown register write: %x = %x\n",
+ (int)addr, (unsigned)value);
+ break;
+ }
+}
+
+static const MemoryRegionOps mpc8544_guts_ops = {
+ .read = mpc8544_guts_read,
+ .write = mpc8544_guts_write,
+ .endianness = DEVICE_BIG_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static int mpc8544_guts_initfn(SysBusDevice *dev)
+{
+ GutsState *s;
+
+ s = FROM_SYSBUS(GutsState, SYS_BUS_DEVICE(dev));
+
+ memory_region_init_io(&s->iomem, &mpc8544_guts_ops, s,
+ "mpc6544.guts", MPC8544_GUTS_MMIO_SIZE);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ return 0;
+}
+
+static void mpc8544_guts_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = mpc8544_guts_initfn;
+}
+
+static const TypeInfo mpc8544_guts_info = {
+ .name = "mpc8544-guts",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(GutsState),
+ .class_init = mpc8544_guts_class_init,
+};
+
+static void mpc8544_guts_register_types(void)
+{
+ type_register_static(&mpc8544_guts_info);
+}
+
+type_init(mpc8544_guts_register_types)
--- /dev/null
+/*
+ * QEMU PowerPC 4xx embedded processors shared devices emulation
+ *
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/ppc.h"
+#include "hw/ppc4xx.h"
+#include "qemu/log.h"
+#include "exec/address-spaces.h"
+
+//#define DEBUG_MMIO
+//#define DEBUG_UNASSIGNED
+#define DEBUG_UIC
+
+
+#ifdef DEBUG_UIC
+# define LOG_UIC(...) qemu_log_mask(CPU_LOG_INT, ## __VA_ARGS__)
+#else
+# define LOG_UIC(...) do { } while (0)
+#endif
+
+static void ppc4xx_reset(void *opaque)
+{
+ PowerPCCPU *cpu = opaque;
+
+ cpu_reset(CPU(cpu));
+}
+
+/*****************************************************************************/
+/* Generic PowerPC 4xx processor instantiation */
+PowerPCCPU *ppc4xx_init(const char *cpu_model,
+ clk_setup_t *cpu_clk, clk_setup_t *tb_clk,
+ uint32_t sysclk)
+{
+ PowerPCCPU *cpu;
+ CPUPPCState *env;
+
+ /* init CPUs */
+ cpu = cpu_ppc_init(cpu_model);
+ if (cpu == NULL) {
+ fprintf(stderr, "Unable to find PowerPC %s CPU definition\n",
+ cpu_model);
+ exit(1);
+ }
+ env = &cpu->env;
+
+ cpu_clk->cb = NULL; /* We don't care about CPU clock frequency changes */
+ cpu_clk->opaque = env;
+ /* Set time-base frequency to sysclk */
+ tb_clk->cb = ppc_40x_timers_init(env, sysclk, PPC_INTERRUPT_PIT);
+ tb_clk->opaque = env;
+ ppc_dcr_init(env, NULL, NULL);
+ /* Register qemu callbacks */
+ qemu_register_reset(ppc4xx_reset, cpu);
+
+ return cpu;
+}
+
+/*****************************************************************************/
+/* "Universal" Interrupt controller */
+enum {
+ DCR_UICSR = 0x000,
+ DCR_UICSRS = 0x001,
+ DCR_UICER = 0x002,
+ DCR_UICCR = 0x003,
+ DCR_UICPR = 0x004,
+ DCR_UICTR = 0x005,
+ DCR_UICMSR = 0x006,
+ DCR_UICVR = 0x007,
+ DCR_UICVCR = 0x008,
+ DCR_UICMAX = 0x009,
+};
+
+#define UIC_MAX_IRQ 32
+typedef struct ppcuic_t ppcuic_t;
+struct ppcuic_t {
+ uint32_t dcr_base;
+ int use_vectors;
+ uint32_t level; /* Remembers the state of level-triggered interrupts. */
+ uint32_t uicsr; /* Status register */
+ uint32_t uicer; /* Enable register */
+ uint32_t uiccr; /* Critical register */
+ uint32_t uicpr; /* Polarity register */
+ uint32_t uictr; /* Triggering register */
+ uint32_t uicvcr; /* Vector configuration register */
+ uint32_t uicvr;
+ qemu_irq *irqs;
+};
+
+static void ppcuic_trigger_irq (ppcuic_t *uic)
+{
+ uint32_t ir, cr;
+ int start, end, inc, i;
+
+ /* Trigger interrupt if any is pending */
+ ir = uic->uicsr & uic->uicer & (~uic->uiccr);
+ cr = uic->uicsr & uic->uicer & uic->uiccr;
+ LOG_UIC("%s: uicsr %08" PRIx32 " uicer %08" PRIx32
+ " uiccr %08" PRIx32 "\n"
+ " %08" PRIx32 " ir %08" PRIx32 " cr %08" PRIx32 "\n",
+ __func__, uic->uicsr, uic->uicer, uic->uiccr,
+ uic->uicsr & uic->uicer, ir, cr);
+ if (ir != 0x0000000) {
+ LOG_UIC("Raise UIC interrupt\n");
+ qemu_irq_raise(uic->irqs[PPCUIC_OUTPUT_INT]);
+ } else {
+ LOG_UIC("Lower UIC interrupt\n");
+ qemu_irq_lower(uic->irqs[PPCUIC_OUTPUT_INT]);
+ }
+ /* Trigger critical interrupt if any is pending and update vector */
+ if (cr != 0x0000000) {
+ qemu_irq_raise(uic->irqs[PPCUIC_OUTPUT_CINT]);
+ if (uic->use_vectors) {
+ /* Compute critical IRQ vector */
+ if (uic->uicvcr & 1) {
+ start = 31;
+ end = 0;
+ inc = -1;
+ } else {
+ start = 0;
+ end = 31;
+ inc = 1;
+ }
+ uic->uicvr = uic->uicvcr & 0xFFFFFFFC;
+ for (i = start; i <= end; i += inc) {
+ if (cr & (1 << i)) {
+ uic->uicvr += (i - start) * 512 * inc;
+ break;
+ }
+ }
+ }
+ LOG_UIC("Raise UIC critical interrupt - "
+ "vector %08" PRIx32 "\n", uic->uicvr);
+ } else {
+ LOG_UIC("Lower UIC critical interrupt\n");
+ qemu_irq_lower(uic->irqs[PPCUIC_OUTPUT_CINT]);
+ uic->uicvr = 0x00000000;
+ }
+}
+
+static void ppcuic_set_irq (void *opaque, int irq_num, int level)
+{
+ ppcuic_t *uic;
+ uint32_t mask, sr;
+
+ uic = opaque;
+ mask = 1 << (31-irq_num);
+ LOG_UIC("%s: irq %d level %d uicsr %08" PRIx32
+ " mask %08" PRIx32 " => %08" PRIx32 " %08" PRIx32 "\n",
+ __func__, irq_num, level,
+ uic->uicsr, mask, uic->uicsr & mask, level << irq_num);
+ if (irq_num < 0 || irq_num > 31)
+ return;
+ sr = uic->uicsr;
+
+ /* Update status register */
+ if (uic->uictr & mask) {
+ /* Edge sensitive interrupt */
+ if (level == 1)
+ uic->uicsr |= mask;
+ } else {
+ /* Level sensitive interrupt */
+ if (level == 1) {
+ uic->uicsr |= mask;
+ uic->level |= mask;
+ } else {
+ uic->uicsr &= ~mask;
+ uic->level &= ~mask;
+ }
+ }
+ LOG_UIC("%s: irq %d level %d sr %" PRIx32 " => "
+ "%08" PRIx32 "\n", __func__, irq_num, level, uic->uicsr, sr);
+ if (sr != uic->uicsr)
+ ppcuic_trigger_irq(uic);
+}
+
+static uint32_t dcr_read_uic (void *opaque, int dcrn)
+{
+ ppcuic_t *uic;
+ uint32_t ret;
+
+ uic = opaque;
+ dcrn -= uic->dcr_base;
+ switch (dcrn) {
+ case DCR_UICSR:
+ case DCR_UICSRS:
+ ret = uic->uicsr;
+ break;
+ case DCR_UICER:
+ ret = uic->uicer;
+ break;
+ case DCR_UICCR:
+ ret = uic->uiccr;
+ break;
+ case DCR_UICPR:
+ ret = uic->uicpr;
+ break;
+ case DCR_UICTR:
+ ret = uic->uictr;
+ break;
+ case DCR_UICMSR:
+ ret = uic->uicsr & uic->uicer;
+ break;
+ case DCR_UICVR:
+ if (!uic->use_vectors)
+ goto no_read;
+ ret = uic->uicvr;
+ break;
+ case DCR_UICVCR:
+ if (!uic->use_vectors)
+ goto no_read;
+ ret = uic->uicvcr;
+ break;
+ default:
+ no_read:
+ ret = 0x00000000;
+ break;
+ }
+
+ return ret;
+}
+
+static void dcr_write_uic (void *opaque, int dcrn, uint32_t val)
+{
+ ppcuic_t *uic;
+
+ uic = opaque;
+ dcrn -= uic->dcr_base;
+ LOG_UIC("%s: dcr %d val 0x%x\n", __func__, dcrn, val);
+ switch (dcrn) {
+ case DCR_UICSR:
+ uic->uicsr &= ~val;
+ uic->uicsr |= uic->level;
+ ppcuic_trigger_irq(uic);
+ break;
+ case DCR_UICSRS:
+ uic->uicsr |= val;
+ ppcuic_trigger_irq(uic);
+ break;
+ case DCR_UICER:
+ uic->uicer = val;
+ ppcuic_trigger_irq(uic);
+ break;
+ case DCR_UICCR:
+ uic->uiccr = val;
+ ppcuic_trigger_irq(uic);
+ break;
+ case DCR_UICPR:
+ uic->uicpr = val;
+ break;
+ case DCR_UICTR:
+ uic->uictr = val;
+ ppcuic_trigger_irq(uic);
+ break;
+ case DCR_UICMSR:
+ break;
+ case DCR_UICVR:
+ break;
+ case DCR_UICVCR:
+ uic->uicvcr = val & 0xFFFFFFFD;
+ ppcuic_trigger_irq(uic);
+ break;
+ }
+}
+
+static void ppcuic_reset (void *opaque)
+{
+ ppcuic_t *uic;
+
+ uic = opaque;
+ uic->uiccr = 0x00000000;
+ uic->uicer = 0x00000000;
+ uic->uicpr = 0x00000000;
+ uic->uicsr = 0x00000000;
+ uic->uictr = 0x00000000;
+ if (uic->use_vectors) {
+ uic->uicvcr = 0x00000000;
+ uic->uicvr = 0x0000000;
+ }
+}
+
+qemu_irq *ppcuic_init (CPUPPCState *env, qemu_irq *irqs,
+ uint32_t dcr_base, int has_ssr, int has_vr)
+{
+ ppcuic_t *uic;
+ int i;
+
+ uic = g_malloc0(sizeof(ppcuic_t));
+ uic->dcr_base = dcr_base;
+ uic->irqs = irqs;
+ if (has_vr)
+ uic->use_vectors = 1;
+ for (i = 0; i < DCR_UICMAX; i++) {
+ ppc_dcr_register(env, dcr_base + i, uic,
+ &dcr_read_uic, &dcr_write_uic);
+ }
+ qemu_register_reset(ppcuic_reset, uic);
+
+ return qemu_allocate_irqs(&ppcuic_set_irq, uic, UIC_MAX_IRQ);
+}
+
+/*****************************************************************************/
+/* SDRAM controller */
+typedef struct ppc4xx_sdram_t ppc4xx_sdram_t;
+struct ppc4xx_sdram_t {
+ uint32_t addr;
+ int nbanks;
+ MemoryRegion containers[4]; /* used for clipping */
+ MemoryRegion *ram_memories;
+ hwaddr ram_bases[4];
+ hwaddr ram_sizes[4];
+ uint32_t besr0;
+ uint32_t besr1;
+ uint32_t bear;
+ uint32_t cfg;
+ uint32_t status;
+ uint32_t rtr;
+ uint32_t pmit;
+ uint32_t bcr[4];
+ uint32_t tr;
+ uint32_t ecccfg;
+ uint32_t eccesr;
+ qemu_irq irq;
+};
+
+enum {
+ SDRAM0_CFGADDR = 0x010,
+ SDRAM0_CFGDATA = 0x011,
+};
+
+/* XXX: TOFIX: some patches have made this code become inconsistent:
+ * there are type inconsistencies, mixing hwaddr, target_ulong
+ * and uint32_t
+ */
+static uint32_t sdram_bcr (hwaddr ram_base,
+ hwaddr ram_size)
+{
+ uint32_t bcr;
+
+ switch (ram_size) {
+ case (4 * 1024 * 1024):
+ bcr = 0x00000000;
+ break;
+ case (8 * 1024 * 1024):
+ bcr = 0x00020000;
+ break;
+ case (16 * 1024 * 1024):
+ bcr = 0x00040000;
+ break;
+ case (32 * 1024 * 1024):
+ bcr = 0x00060000;
+ break;
+ case (64 * 1024 * 1024):
+ bcr = 0x00080000;
+ break;
+ case (128 * 1024 * 1024):
+ bcr = 0x000A0000;
+ break;
+ case (256 * 1024 * 1024):
+ bcr = 0x000C0000;
+ break;
+ default:
+ printf("%s: invalid RAM size " TARGET_FMT_plx "\n", __func__,
+ ram_size);
+ return 0x00000000;
+ }
+ bcr |= ram_base & 0xFF800000;
+ bcr |= 1;
+
+ return bcr;
+}
+
+static inline hwaddr sdram_base(uint32_t bcr)
+{
+ return bcr & 0xFF800000;
+}
+
+static target_ulong sdram_size (uint32_t bcr)
+{
+ target_ulong size;
+ int sh;
+
+ sh = (bcr >> 17) & 0x7;
+ if (sh == 7)
+ size = -1;
+ else
+ size = (4 * 1024 * 1024) << sh;
+
+ return size;
+}
+
+static void sdram_set_bcr(ppc4xx_sdram_t *sdram,
+ uint32_t *bcrp, uint32_t bcr, int enabled)
+{
+ unsigned n = bcrp - sdram->bcr;
+
+ if (*bcrp & 0x00000001) {
+ /* Unmap RAM */
+#ifdef DEBUG_SDRAM
+ printf("%s: unmap RAM area " TARGET_FMT_plx " " TARGET_FMT_lx "\n",
+ __func__, sdram_base(*bcrp), sdram_size(*bcrp));
+#endif
+ memory_region_del_subregion(get_system_memory(),
+ &sdram->containers[n]);
+ memory_region_del_subregion(&sdram->containers[n],
+ &sdram->ram_memories[n]);
+ memory_region_destroy(&sdram->containers[n]);
+ }
+ *bcrp = bcr & 0xFFDEE001;
+ if (enabled && (bcr & 0x00000001)) {
+#ifdef DEBUG_SDRAM
+ printf("%s: Map RAM area " TARGET_FMT_plx " " TARGET_FMT_lx "\n",
+ __func__, sdram_base(bcr), sdram_size(bcr));
+#endif
+ memory_region_init(&sdram->containers[n], "sdram-containers",
+ sdram_size(bcr));
+ memory_region_add_subregion(&sdram->containers[n], 0,
+ &sdram->ram_memories[n]);
+ memory_region_add_subregion(get_system_memory(),
+ sdram_base(bcr),
+ &sdram->containers[n]);
+ }
+}
+
+static void sdram_map_bcr (ppc4xx_sdram_t *sdram)
+{
+ int i;
+
+ for (i = 0; i < sdram->nbanks; i++) {
+ if (sdram->ram_sizes[i] != 0) {
+ sdram_set_bcr(sdram,
+ &sdram->bcr[i],
+ sdram_bcr(sdram->ram_bases[i], sdram->ram_sizes[i]),
+ 1);
+ } else {
+ sdram_set_bcr(sdram, &sdram->bcr[i], 0x00000000, 0);
+ }
+ }
+}
+
+static void sdram_unmap_bcr (ppc4xx_sdram_t *sdram)
+{
+ int i;
+
+ for (i = 0; i < sdram->nbanks; i++) {
+#ifdef DEBUG_SDRAM
+ printf("%s: Unmap RAM area " TARGET_FMT_plx " " TARGET_FMT_lx "\n",
+ __func__, sdram_base(sdram->bcr[i]), sdram_size(sdram->bcr[i]));
+#endif
+ memory_region_del_subregion(get_system_memory(),
+ &sdram->ram_memories[i]);
+ }
+}
+
+static uint32_t dcr_read_sdram (void *opaque, int dcrn)
+{
+ ppc4xx_sdram_t *sdram;
+ uint32_t ret;
+
+ sdram = opaque;
+ switch (dcrn) {
+ case SDRAM0_CFGADDR:
+ ret = sdram->addr;
+ break;
+ case SDRAM0_CFGDATA:
+ switch (sdram->addr) {
+ case 0x00: /* SDRAM_BESR0 */
+ ret = sdram->besr0;
+ break;
+ case 0x08: /* SDRAM_BESR1 */
+ ret = sdram->besr1;
+ break;
+ case 0x10: /* SDRAM_BEAR */
+ ret = sdram->bear;
+ break;
+ case 0x20: /* SDRAM_CFG */
+ ret = sdram->cfg;
+ break;
+ case 0x24: /* SDRAM_STATUS */
+ ret = sdram->status;
+ break;
+ case 0x30: /* SDRAM_RTR */
+ ret = sdram->rtr;
+ break;
+ case 0x34: /* SDRAM_PMIT */
+ ret = sdram->pmit;
+ break;
+ case 0x40: /* SDRAM_B0CR */
+ ret = sdram->bcr[0];
+ break;
+ case 0x44: /* SDRAM_B1CR */
+ ret = sdram->bcr[1];
+ break;
+ case 0x48: /* SDRAM_B2CR */
+ ret = sdram->bcr[2];
+ break;
+ case 0x4C: /* SDRAM_B3CR */
+ ret = sdram->bcr[3];
+ break;
+ case 0x80: /* SDRAM_TR */
+ ret = -1; /* ? */
+ break;
+ case 0x94: /* SDRAM_ECCCFG */
+ ret = sdram->ecccfg;
+ break;
+ case 0x98: /* SDRAM_ECCESR */
+ ret = sdram->eccesr;
+ break;
+ default: /* Error */
+ ret = -1;
+ break;
+ }
+ break;
+ default:
+ /* Avoid gcc warning */
+ ret = 0x00000000;
+ break;
+ }
+
+ return ret;
+}
+
+static void dcr_write_sdram (void *opaque, int dcrn, uint32_t val)
+{
+ ppc4xx_sdram_t *sdram;
+
+ sdram = opaque;
+ switch (dcrn) {
+ case SDRAM0_CFGADDR:
+ sdram->addr = val;
+ break;
+ case SDRAM0_CFGDATA:
+ switch (sdram->addr) {
+ case 0x00: /* SDRAM_BESR0 */
+ sdram->besr0 &= ~val;
+ break;
+ case 0x08: /* SDRAM_BESR1 */
+ sdram->besr1 &= ~val;
+ break;
+ case 0x10: /* SDRAM_BEAR */
+ sdram->bear = val;
+ break;
+ case 0x20: /* SDRAM_CFG */
+ val &= 0xFFE00000;
+ if (!(sdram->cfg & 0x80000000) && (val & 0x80000000)) {
+#ifdef DEBUG_SDRAM
+ printf("%s: enable SDRAM controller\n", __func__);
+#endif
+ /* validate all RAM mappings */
+ sdram_map_bcr(sdram);
+ sdram->status &= ~0x80000000;
+ } else if ((sdram->cfg & 0x80000000) && !(val & 0x80000000)) {
+#ifdef DEBUG_SDRAM
+ printf("%s: disable SDRAM controller\n", __func__);
+#endif
+ /* invalidate all RAM mappings */
+ sdram_unmap_bcr(sdram);
+ sdram->status |= 0x80000000;
+ }
+ if (!(sdram->cfg & 0x40000000) && (val & 0x40000000))
+ sdram->status |= 0x40000000;
+ else if ((sdram->cfg & 0x40000000) && !(val & 0x40000000))
+ sdram->status &= ~0x40000000;
+ sdram->cfg = val;
+ break;
+ case 0x24: /* SDRAM_STATUS */
+ /* Read-only register */
+ break;
+ case 0x30: /* SDRAM_RTR */
+ sdram->rtr = val & 0x3FF80000;
+ break;
+ case 0x34: /* SDRAM_PMIT */
+ sdram->pmit = (val & 0xF8000000) | 0x07C00000;
+ break;
+ case 0x40: /* SDRAM_B0CR */
+ sdram_set_bcr(sdram, &sdram->bcr[0], val, sdram->cfg & 0x80000000);
+ break;
+ case 0x44: /* SDRAM_B1CR */
+ sdram_set_bcr(sdram, &sdram->bcr[1], val, sdram->cfg & 0x80000000);
+ break;
+ case 0x48: /* SDRAM_B2CR */
+ sdram_set_bcr(sdram, &sdram->bcr[2], val, sdram->cfg & 0x80000000);
+ break;
+ case 0x4C: /* SDRAM_B3CR */
+ sdram_set_bcr(sdram, &sdram->bcr[3], val, sdram->cfg & 0x80000000);
+ break;
+ case 0x80: /* SDRAM_TR */
+ sdram->tr = val & 0x018FC01F;
+ break;
+ case 0x94: /* SDRAM_ECCCFG */
+ sdram->ecccfg = val & 0x00F00000;
+ break;
+ case 0x98: /* SDRAM_ECCESR */
+ val &= 0xFFF0F000;
+ if (sdram->eccesr == 0 && val != 0)
+ qemu_irq_raise(sdram->irq);
+ else if (sdram->eccesr != 0 && val == 0)
+ qemu_irq_lower(sdram->irq);
+ sdram->eccesr = val;
+ break;
+ default: /* Error */
+ break;
+ }
+ break;
+ }
+}
+
+static void sdram_reset (void *opaque)
+{
+ ppc4xx_sdram_t *sdram;
+
+ sdram = opaque;
+ sdram->addr = 0x00000000;
+ sdram->bear = 0x00000000;
+ sdram->besr0 = 0x00000000; /* No error */
+ sdram->besr1 = 0x00000000; /* No error */
+ sdram->cfg = 0x00000000;
+ sdram->ecccfg = 0x00000000; /* No ECC */
+ sdram->eccesr = 0x00000000; /* No error */
+ sdram->pmit = 0x07C00000;
+ sdram->rtr = 0x05F00000;
+ sdram->tr = 0x00854009;
+ /* We pre-initialize RAM banks */
+ sdram->status = 0x00000000;
+ sdram->cfg = 0x00800000;
+}
+
+void ppc4xx_sdram_init (CPUPPCState *env, qemu_irq irq, int nbanks,
+ MemoryRegion *ram_memories,
+ hwaddr *ram_bases,
+ hwaddr *ram_sizes,
+ int do_init)
+{
+ ppc4xx_sdram_t *sdram;
+
+ sdram = g_malloc0(sizeof(ppc4xx_sdram_t));
+ sdram->irq = irq;
+ sdram->nbanks = nbanks;
+ sdram->ram_memories = ram_memories;
+ memset(sdram->ram_bases, 0, 4 * sizeof(hwaddr));
+ memcpy(sdram->ram_bases, ram_bases,
+ nbanks * sizeof(hwaddr));
+ memset(sdram->ram_sizes, 0, 4 * sizeof(hwaddr));
+ memcpy(sdram->ram_sizes, ram_sizes,
+ nbanks * sizeof(hwaddr));
+ qemu_register_reset(&sdram_reset, sdram);
+ ppc_dcr_register(env, SDRAM0_CFGADDR,
+ sdram, &dcr_read_sdram, &dcr_write_sdram);
+ ppc_dcr_register(env, SDRAM0_CFGDATA,
+ sdram, &dcr_read_sdram, &dcr_write_sdram);
+ if (do_init)
+ sdram_map_bcr(sdram);
+}
+
+/* Fill in consecutive SDRAM banks with 'ram_size' bytes of memory.
+ *
+ * sdram_bank_sizes[] must be 0-terminated.
+ *
+ * The 4xx SDRAM controller supports a small number of banks, and each bank
+ * must be one of a small set of sizes. The number of banks and the supported
+ * sizes varies by SoC. */
+ram_addr_t ppc4xx_sdram_adjust(ram_addr_t ram_size, int nr_banks,
+ MemoryRegion ram_memories[],
+ hwaddr ram_bases[],
+ hwaddr ram_sizes[],
+ const unsigned int sdram_bank_sizes[])
+{
+ ram_addr_t size_left = ram_size;
+ ram_addr_t base = 0;
+ int i;
+ int j;
+
+ for (i = 0; i < nr_banks; i++) {
+ for (j = 0; sdram_bank_sizes[j] != 0; j++) {
+ unsigned int bank_size = sdram_bank_sizes[j];
+
+ if (bank_size <= size_left) {
+ char name[32];
+ snprintf(name, sizeof(name), "ppc4xx.sdram%d", i);
+ memory_region_init_ram(&ram_memories[i], name, bank_size);
+ vmstate_register_ram_global(&ram_memories[i]);
+ ram_bases[i] = base;
+ ram_sizes[i] = bank_size;
+ base += bank_size;
+ size_left -= bank_size;
+ break;
+ }
+ }
+
+ if (!size_left) {
+ /* No need to use the remaining banks. */
+ break;
+ }
+ }
+
+ ram_size -= size_left;
+ if (size_left)
+ printf("Truncating memory to %d MiB to fit SDRAM controller limits.\n",
+ (int)(ram_size >> 20));
+
+ return ram_size;
+}
--- /dev/null
+/*
+ * QEMU PowerPC e500v2 ePAPR spinning code
+ *
+ * Copyright (C) 2011 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/>.
+ *
+ * This code is not really a device, but models an interface that usually
+ * firmware takes care of. It's used when QEMU plays the role of firmware.
+ *
+ * Specification:
+ *
+ * https://www.power.org/resources/downloads/Power_ePAPR_APPROVED_v1.1.pdf
+ *
+ */
+
+#include "hw/hw.h"
+#include "sysemu/sysemu.h"
+#include "hw/sysbus.h"
+#include "sysemu/kvm.h"
+
+#define MAX_CPUS 32
+
+typedef struct spin_info {
+ uint64_t addr;
+ uint64_t r3;
+ uint32_t resv;
+ uint32_t pir;
+ uint64_t reserved;
+} QEMU_PACKED SpinInfo;
+
+typedef struct spin_state {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ SpinInfo spin[MAX_CPUS];
+} SpinState;
+
+typedef struct spin_kick {
+ PowerPCCPU *cpu;
+ SpinInfo *spin;
+} SpinKick;
+
+static void spin_reset(void *opaque)
+{
+ SpinState *s = opaque;
+ int i;
+
+ for (i = 0; i < MAX_CPUS; i++) {
+ SpinInfo *info = &s->spin[i];
+
+ info->pir = i;
+ info->r3 = i;
+ info->addr = 1;
+ }
+}
+
+/* Create -kernel TLB entries for BookE, linearly spanning 256MB. */
+static inline hwaddr booke206_page_size_to_tlb(uint64_t size)
+{
+ return (ffs(size >> 10) - 1) >> 1;
+}
+
+static void mmubooke_create_initial_mapping(CPUPPCState *env,
+ target_ulong va,
+ hwaddr pa,
+ hwaddr len)
+{
+ ppcmas_tlb_t *tlb = booke206_get_tlbm(env, 1, 0, 1);
+ hwaddr size;
+
+ size = (booke206_page_size_to_tlb(len) << MAS1_TSIZE_SHIFT);
+ tlb->mas1 = MAS1_VALID | size;
+ tlb->mas2 = (va & TARGET_PAGE_MASK) | MAS2_M;
+ tlb->mas7_3 = pa & TARGET_PAGE_MASK;
+ tlb->mas7_3 |= MAS3_UR | MAS3_UW | MAS3_UX | MAS3_SR | MAS3_SW | MAS3_SX;
+ env->tlb_dirty = true;
+}
+
+static void spin_kick(void *data)
+{
+ SpinKick *kick = data;
+ CPUState *cpu = CPU(kick->cpu);
+ CPUPPCState *env = &kick->cpu->env;
+ SpinInfo *curspin = kick->spin;
+ hwaddr map_size = 64 * 1024 * 1024;
+ hwaddr map_start;
+
+ cpu_synchronize_state(env);
+ stl_p(&curspin->pir, env->spr[SPR_PIR]);
+ env->nip = ldq_p(&curspin->addr) & (map_size - 1);
+ env->gpr[3] = ldq_p(&curspin->r3);
+ env->gpr[4] = 0;
+ env->gpr[5] = 0;
+ env->gpr[6] = 0;
+ env->gpr[7] = map_size;
+ env->gpr[8] = 0;
+ env->gpr[9] = 0;
+
+ map_start = ldq_p(&curspin->addr) & ~(map_size - 1);
+ mmubooke_create_initial_mapping(env, 0, map_start, map_size);
+
+ env->halted = 0;
+ env->exception_index = -1;
+ cpu->stopped = false;
+ qemu_cpu_kick(cpu);
+}
+
+static void spin_write(void *opaque, hwaddr addr, uint64_t value,
+ unsigned len)
+{
+ SpinState *s = opaque;
+ int env_idx = addr / sizeof(SpinInfo);
+ CPUState *cpu;
+ SpinInfo *curspin = &s->spin[env_idx];
+ uint8_t *curspin_p = (uint8_t*)curspin;
+
+ cpu = qemu_get_cpu(env_idx);
+ if (cpu == NULL) {
+ /* Unknown CPU */
+ return;
+ }
+
+ if (cpu->cpu_index == 0) {
+ /* primary CPU doesn't spin */
+ return;
+ }
+
+ curspin_p = &curspin_p[addr % sizeof(SpinInfo)];
+ switch (len) {
+ case 1:
+ stb_p(curspin_p, value);
+ break;
+ case 2:
+ stw_p(curspin_p, value);
+ break;
+ case 4:
+ stl_p(curspin_p, value);
+ break;
+ }
+
+ if (!(ldq_p(&curspin->addr) & 1)) {
+ /* run CPU */
+ SpinKick kick = {
+ .cpu = POWERPC_CPU(cpu),
+ .spin = curspin,
+ };
+
+ run_on_cpu(cpu, spin_kick, &kick);
+ }
+}
+
+static uint64_t spin_read(void *opaque, hwaddr addr, unsigned len)
+{
+ SpinState *s = opaque;
+ uint8_t *spin_p = &((uint8_t*)s->spin)[addr];
+
+ switch (len) {
+ case 1:
+ return ldub_p(spin_p);
+ case 2:
+ return lduw_p(spin_p);
+ case 4:
+ return ldl_p(spin_p);
+ default:
+ hw_error("ppce500: unexpected %s with len = %u", __func__, len);
+ }
+}
+
+static const MemoryRegionOps spin_rw_ops = {
+ .read = spin_read,
+ .write = spin_write,
+ .endianness = DEVICE_BIG_ENDIAN,
+};
+
+static int ppce500_spin_initfn(SysBusDevice *dev)
+{
+ SpinState *s;
+
+ s = FROM_SYSBUS(SpinState, SYS_BUS_DEVICE(dev));
+
+ memory_region_init_io(&s->iomem, &spin_rw_ops, s, "e500 spin pv device",
+ sizeof(SpinInfo) * MAX_CPUS);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ qemu_register_reset(spin_reset, s);
+
+ return 0;
+}
+
+static void ppce500_spin_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = ppce500_spin_initfn;
+}
+
+static const TypeInfo ppce500_spin_info = {
+ .name = "e500-spin",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SpinState),
+ .class_init = ppce500_spin_class_init,
+};
+
+static void ppce500_spin_register_types(void)
+{
+ type_register_static(&ppce500_spin_info);
+}
+
+type_init(ppce500_spin_register_types)
--- /dev/null
+/*
+ * QEMU sPAPR VIO code
+ *
+ * Copyright (c) 2010 David Gibson, IBM Corporation <dwg@au1.ibm.com>
+ * Based on the s390 virtio bus code:
+ * Copyright (c) 2009 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/hw.h"
+#include "sysemu/sysemu.h"
+#include "hw/boards.h"
+#include "monitor/monitor.h"
+#include "hw/loader.h"
+#include "elf.h"
+#include "hw/sysbus.h"
+#include "sysemu/kvm.h"
+#include "sysemu/device_tree.h"
+#include "kvm_ppc.h"
+
+#include "hw/spapr.h"
+#include "hw/spapr_vio.h"
+#include "hw/xics.h"
+
+#ifdef CONFIG_FDT
+#include <libfdt.h>
+#endif /* CONFIG_FDT */
+
+/* #define DEBUG_SPAPR */
+
+#ifdef DEBUG_SPAPR
+#define dprintf(fmt, ...) \
+ do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define dprintf(fmt, ...) \
+ do { } while (0)
+#endif
+
+static Property spapr_vio_props[] = {
+ DEFINE_PROP_UINT32("irq", VIOsPAPRDevice, irq, 0), \
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static const TypeInfo spapr_vio_bus_info = {
+ .name = TYPE_SPAPR_VIO_BUS,
+ .parent = TYPE_BUS,
+ .instance_size = sizeof(VIOsPAPRBus),
+};
+
+VIOsPAPRDevice *spapr_vio_find_by_reg(VIOsPAPRBus *bus, uint32_t reg)
+{
+ BusChild *kid;
+ VIOsPAPRDevice *dev = NULL;
+
+ QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
+ dev = (VIOsPAPRDevice *)kid->child;
+ if (dev->reg == reg) {
+ return dev;
+ }
+ }
+
+ return NULL;
+}
+
+static char *vio_format_dev_name(VIOsPAPRDevice *dev)
+{
+ VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev);
+ char *name;
+
+ /* Device tree style name device@reg */
+ name = g_strdup_printf("%s@%x", pc->dt_name, dev->reg);
+
+ return name;
+}
+
+#ifdef CONFIG_FDT
+static int vio_make_devnode(VIOsPAPRDevice *dev,
+ void *fdt)
+{
+ VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev);
+ int vdevice_off, node_off, ret;
+ char *dt_name;
+
+ vdevice_off = fdt_path_offset(fdt, "/vdevice");
+ if (vdevice_off < 0) {
+ return vdevice_off;
+ }
+
+ dt_name = vio_format_dev_name(dev);
+ node_off = fdt_add_subnode(fdt, vdevice_off, dt_name);
+ g_free(dt_name);
+ if (node_off < 0) {
+ return node_off;
+ }
+
+ ret = fdt_setprop_cell(fdt, node_off, "reg", dev->reg);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (pc->dt_type) {
+ ret = fdt_setprop_string(fdt, node_off, "device_type",
+ pc->dt_type);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ if (pc->dt_compatible) {
+ ret = fdt_setprop_string(fdt, node_off, "compatible",
+ pc->dt_compatible);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ if (dev->irq) {
+ uint32_t ints_prop[] = {cpu_to_be32(dev->irq), 0};
+
+ ret = fdt_setprop(fdt, node_off, "interrupts", ints_prop,
+ sizeof(ints_prop));
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ ret = spapr_tcet_dma_dt(fdt, node_off, "ibm,my-dma-window", dev->dma);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (pc->devnode) {
+ ret = (pc->devnode)(dev, fdt, node_off);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ return node_off;
+}
+#endif /* CONFIG_FDT */
+
+/*
+ * CRQ handling
+ */
+static target_ulong h_reg_crq(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ target_ulong reg = args[0];
+ target_ulong queue_addr = args[1];
+ target_ulong queue_len = args[2];
+ VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
+
+ if (!dev) {
+ hcall_dprintf("Unit 0x" TARGET_FMT_lx " does not exist\n", reg);
+ return H_PARAMETER;
+ }
+
+ /* We can't grok a queue size bigger than 256M for now */
+ if (queue_len < 0x1000 || queue_len > 0x10000000) {
+ hcall_dprintf("Queue size too small or too big (0x" TARGET_FMT_lx
+ ")\n", queue_len);
+ return H_PARAMETER;
+ }
+
+ /* Check queue alignment */
+ if (queue_addr & 0xfff) {
+ hcall_dprintf("Queue not aligned (0x" TARGET_FMT_lx ")\n", queue_addr);
+ return H_PARAMETER;
+ }
+
+ /* Check if device supports CRQs */
+ if (!dev->crq.SendFunc) {
+ hcall_dprintf("Device does not support CRQ\n");
+ return H_NOT_FOUND;
+ }
+
+ /* Already a queue ? */
+ if (dev->crq.qsize) {
+ hcall_dprintf("CRQ already registered\n");
+ return H_RESOURCE;
+ }
+ dev->crq.qladdr = queue_addr;
+ dev->crq.qsize = queue_len;
+ dev->crq.qnext = 0;
+
+ dprintf("CRQ for dev 0x" TARGET_FMT_lx " registered at 0x"
+ TARGET_FMT_lx "/0x" TARGET_FMT_lx "\n",
+ reg, queue_addr, queue_len);
+ return H_SUCCESS;
+}
+
+static target_ulong free_crq(VIOsPAPRDevice *dev)
+{
+ dev->crq.qladdr = 0;
+ dev->crq.qsize = 0;
+ dev->crq.qnext = 0;
+
+ dprintf("CRQ for dev 0x%" PRIx32 " freed\n", dev->reg);
+
+ return H_SUCCESS;
+}
+
+static target_ulong h_free_crq(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ target_ulong reg = args[0];
+ VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
+
+ if (!dev) {
+ hcall_dprintf("Unit 0x" TARGET_FMT_lx " does not exist\n", reg);
+ return H_PARAMETER;
+ }
+
+ return free_crq(dev);
+}
+
+static target_ulong h_send_crq(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ target_ulong reg = args[0];
+ target_ulong msg_hi = args[1];
+ target_ulong msg_lo = args[2];
+ VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
+ uint64_t crq_mangle[2];
+
+ if (!dev) {
+ hcall_dprintf("Unit 0x" TARGET_FMT_lx " does not exist\n", reg);
+ return H_PARAMETER;
+ }
+ crq_mangle[0] = cpu_to_be64(msg_hi);
+ crq_mangle[1] = cpu_to_be64(msg_lo);
+
+ if (dev->crq.SendFunc) {
+ return dev->crq.SendFunc(dev, (uint8_t *)crq_mangle);
+ }
+
+ return H_HARDWARE;
+}
+
+static target_ulong h_enable_crq(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ target_ulong reg = args[0];
+ VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
+
+ if (!dev) {
+ hcall_dprintf("Unit 0x" TARGET_FMT_lx " does not exist\n", reg);
+ return H_PARAMETER;
+ }
+
+ return 0;
+}
+
+/* Returns negative error, 0 success, or positive: queue full */
+int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq)
+{
+ int rc;
+ uint8_t byte;
+
+ if (!dev->crq.qsize) {
+ fprintf(stderr, "spapr_vio_send_creq on uninitialized queue\n");
+ return -1;
+ }
+
+ /* Maybe do a fast path for KVM just writing to the pages */
+ rc = spapr_vio_dma_read(dev, dev->crq.qladdr + dev->crq.qnext, &byte, 1);
+ if (rc) {
+ return rc;
+ }
+ if (byte != 0) {
+ return 1;
+ }
+
+ rc = spapr_vio_dma_write(dev, dev->crq.qladdr + dev->crq.qnext + 8,
+ &crq[8], 8);
+ if (rc) {
+ return rc;
+ }
+
+ kvmppc_eieio();
+
+ rc = spapr_vio_dma_write(dev, dev->crq.qladdr + dev->crq.qnext, crq, 8);
+ if (rc) {
+ return rc;
+ }
+
+ dev->crq.qnext = (dev->crq.qnext + 16) % dev->crq.qsize;
+
+ if (dev->signal_state & 1) {
+ qemu_irq_pulse(spapr_vio_qirq(dev));
+ }
+
+ return 0;
+}
+
+/* "quiesce" handling */
+
+static void spapr_vio_quiesce_one(VIOsPAPRDevice *dev)
+{
+ if (dev->dma) {
+ spapr_tce_reset(dev->dma);
+ }
+ free_crq(dev);
+}
+
+static void rtas_set_tce_bypass(sPAPREnvironment *spapr, uint32_t token,
+ uint32_t nargs, target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ VIOsPAPRBus *bus = spapr->vio_bus;
+ VIOsPAPRDevice *dev;
+ uint32_t unit, enable;
+
+ if (nargs != 2) {
+ rtas_st(rets, 0, -3);
+ return;
+ }
+ unit = rtas_ld(args, 0);
+ enable = rtas_ld(args, 1);
+ dev = spapr_vio_find_by_reg(bus, unit);
+ if (!dev) {
+ rtas_st(rets, 0, -3);
+ return;
+ }
+
+ if (!dev->dma) {
+ rtas_st(rets, 0, -3);
+ return;
+ }
+
+ spapr_tce_set_bypass(dev->dma, !!enable);
+
+ rtas_st(rets, 0, 0);
+}
+
+static void rtas_quiesce(sPAPREnvironment *spapr, uint32_t token,
+ uint32_t nargs, target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ VIOsPAPRBus *bus = spapr->vio_bus;
+ BusChild *kid;
+ VIOsPAPRDevice *dev = NULL;
+
+ if (nargs != 0) {
+ rtas_st(rets, 0, -3);
+ return;
+ }
+
+ QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
+ dev = (VIOsPAPRDevice *)kid->child;
+ spapr_vio_quiesce_one(dev);
+ }
+
+ rtas_st(rets, 0, 0);
+}
+
+static VIOsPAPRDevice *reg_conflict(VIOsPAPRDevice *dev)
+{
+ VIOsPAPRBus *bus = DO_UPCAST(VIOsPAPRBus, bus, dev->qdev.parent_bus);
+ BusChild *kid;
+ VIOsPAPRDevice *other;
+
+ /*
+ * Check for a device other than the given one which is already
+ * using the requested address. We have to open code this because
+ * the given dev might already be in the list.
+ */
+ QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
+ other = DO_UPCAST(VIOsPAPRDevice, qdev, kid->child);
+
+ if (other != dev && other->reg == dev->reg) {
+ return other;
+ }
+ }
+
+ return 0;
+}
+
+static void spapr_vio_busdev_reset(DeviceState *qdev)
+{
+ VIOsPAPRDevice *dev = DO_UPCAST(VIOsPAPRDevice, qdev, qdev);
+ VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev);
+
+ /* Shut down the request queue and TCEs if necessary */
+ spapr_vio_quiesce_one(dev);
+
+ dev->signal_state = 0;
+
+ if (pc->reset) {
+ pc->reset(dev);
+ }
+}
+
+static int spapr_vio_busdev_init(DeviceState *qdev)
+{
+ VIOsPAPRDevice *dev = (VIOsPAPRDevice *)qdev;
+ VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev);
+ char *id;
+
+ if (dev->reg != -1) {
+ /*
+ * Explicitly assigned address, just verify that no-one else
+ * is using it. other mechanism). We have to open code this
+ * rather than using spapr_vio_find_by_reg() because sdev
+ * itself is already in the list.
+ */
+ VIOsPAPRDevice *other = reg_conflict(dev);
+
+ if (other) {
+ fprintf(stderr, "vio: %s and %s devices conflict at address %#x\n",
+ object_get_typename(OBJECT(qdev)),
+ object_get_typename(OBJECT(&other->qdev)),
+ dev->reg);
+ return -1;
+ }
+ } else {
+ /* Need to assign an address */
+ VIOsPAPRBus *bus = DO_UPCAST(VIOsPAPRBus, bus, dev->qdev.parent_bus);
+
+ do {
+ dev->reg = bus->next_reg++;
+ } while (reg_conflict(dev));
+ }
+
+ /* Don't overwrite ids assigned on the command line */
+ if (!dev->qdev.id) {
+ id = vio_format_dev_name(dev);
+ dev->qdev.id = id;
+ }
+
+ dev->irq = spapr_allocate_msi(dev->irq);
+ if (!dev->irq) {
+ return -1;
+ }
+
+ if (pc->rtce_window_size) {
+ uint32_t liobn = SPAPR_VIO_BASE_LIOBN | dev->reg;
+ dev->dma = spapr_tce_new_dma_context(liobn, pc->rtce_window_size);
+ }
+
+ return pc->init(dev);
+}
+
+static target_ulong h_vio_signal(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+ target_ulong opcode,
+ target_ulong *args)
+{
+ target_ulong reg = args[0];
+ target_ulong mode = args[1];
+ VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
+ VIOsPAPRDeviceClass *pc;
+
+ if (!dev) {
+ return H_PARAMETER;
+ }
+
+ pc = VIO_SPAPR_DEVICE_GET_CLASS(dev);
+
+ if (mode & ~pc->signal_mask) {
+ return H_PARAMETER;
+ }
+
+ dev->signal_state = mode;
+
+ return H_SUCCESS;
+}
+
+VIOsPAPRBus *spapr_vio_bus_init(void)
+{
+ VIOsPAPRBus *bus;
+ BusState *qbus;
+ DeviceState *dev;
+
+ /* Create bridge device */
+ dev = qdev_create(NULL, "spapr-vio-bridge");
+ qdev_init_nofail(dev);
+
+ /* Create bus on bridge device */
+
+ qbus = qbus_create(TYPE_SPAPR_VIO_BUS, dev, "spapr-vio");
+ bus = DO_UPCAST(VIOsPAPRBus, bus, qbus);
+ bus->next_reg = 0x71000000;
+
+ /* hcall-vio */
+ spapr_register_hypercall(H_VIO_SIGNAL, h_vio_signal);
+
+ /* hcall-crq */
+ spapr_register_hypercall(H_REG_CRQ, h_reg_crq);
+ spapr_register_hypercall(H_FREE_CRQ, h_free_crq);
+ spapr_register_hypercall(H_SEND_CRQ, h_send_crq);
+ spapr_register_hypercall(H_ENABLE_CRQ, h_enable_crq);
+
+ /* RTAS calls */
+ spapr_rtas_register("ibm,set-tce-bypass", rtas_set_tce_bypass);
+ spapr_rtas_register("quiesce", rtas_quiesce);
+
+ return bus;
+}
+
+/* Represents sPAPR hcall VIO devices */
+
+static int spapr_vio_bridge_init(SysBusDevice *dev)
+{
+ /* nothing */
+ return 0;
+}
+
+static void spapr_vio_bridge_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = spapr_vio_bridge_init;
+ dc->no_user = 1;
+}
+
+static const TypeInfo spapr_vio_bridge_info = {
+ .name = "spapr-vio-bridge",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SysBusDevice),
+ .class_init = spapr_vio_bridge_class_init,
+};
+
+static void vio_spapr_device_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *k = DEVICE_CLASS(klass);
+ k->init = spapr_vio_busdev_init;
+ k->reset = spapr_vio_busdev_reset;
+ k->bus_type = TYPE_SPAPR_VIO_BUS;
+ k->props = spapr_vio_props;
+}
+
+static const TypeInfo spapr_vio_type_info = {
+ .name = TYPE_VIO_SPAPR_DEVICE,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(VIOsPAPRDevice),
+ .abstract = true,
+ .class_size = sizeof(VIOsPAPRDeviceClass),
+ .class_init = vio_spapr_device_class_init,
+};
+
+static void spapr_vio_register_types(void)
+{
+ type_register_static(&spapr_vio_bus_info);
+ type_register_static(&spapr_vio_bridge_info);
+ type_register_static(&spapr_vio_type_info);
+}
+
+type_init(spapr_vio_register_types)
+
+#ifdef CONFIG_FDT
+static int compare_reg(const void *p1, const void *p2)
+{
+ VIOsPAPRDevice const *dev1, *dev2;
+
+ dev1 = (VIOsPAPRDevice *)*(DeviceState **)p1;
+ dev2 = (VIOsPAPRDevice *)*(DeviceState **)p2;
+
+ if (dev1->reg < dev2->reg) {
+ return -1;
+ }
+ if (dev1->reg == dev2->reg) {
+ return 0;
+ }
+
+ /* dev1->reg > dev2->reg */
+ return 1;
+}
+
+int spapr_populate_vdevice(VIOsPAPRBus *bus, void *fdt)
+{
+ DeviceState *qdev, **qdevs;
+ BusChild *kid;
+ int i, num, ret = 0;
+
+ /* Count qdevs on the bus list */
+ num = 0;
+ QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
+ num++;
+ }
+
+ /* Copy out into an array of pointers */
+ qdevs = g_malloc(sizeof(qdev) * num);
+ num = 0;
+ QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
+ qdevs[num++] = kid->child;
+ }
+
+ /* Sort the array */
+ qsort(qdevs, num, sizeof(qdev), compare_reg);
+
+ /* Hack alert. Give the devices to libfdt in reverse order, we happen
+ * to know that will mean they are in forward order in the tree. */
+ for (i = num - 1; i >= 0; i--) {
+ VIOsPAPRDevice *dev = (VIOsPAPRDevice *)(qdevs[i]);
+
+ ret = vio_make_devnode(dev, fdt);
+
+ if (ret < 0) {
+ goto out;
+ }
+ }
+
+ ret = 0;
+out:
+ free(qdevs);
+
+ return ret;
+}
+
+int spapr_populate_chosen_stdout(void *fdt, VIOsPAPRBus *bus)
+{
+ VIOsPAPRDevice *dev;
+ char *name, *path;
+ int ret, offset;
+
+ dev = spapr_vty_get_default(bus);
+ if (!dev)
+ return 0;
+
+ offset = fdt_path_offset(fdt, "/chosen");
+ if (offset < 0) {
+ return offset;
+ }
+
+ name = vio_format_dev_name(dev);
+ path = g_strdup_printf("/vdevice/%s", name);
+
+ ret = fdt_setprop_string(fdt, offset, "linux,stdout-path", path);
+
+ g_free(name);
+ g_free(path);
+
+ return ret;
+}
+#endif /* CONFIG_FDT */
--- /dev/null
+/*
+ * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
+ *
+ * PAPR Virtualized Interrupt System, aka ICS/ICP aka xics
+ *
+ * Copyright (c) 2010,2011 David Gibson, IBM Corporation.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#include "hw/hw.h"
+#include "trace.h"
+#include "hw/spapr.h"
+#include "hw/xics.h"
+
+/*
+ * ICP: Presentation layer
+ */
+
+struct icp_server_state {
+ uint32_t xirr;
+ uint8_t pending_priority;
+ uint8_t mfrr;
+ qemu_irq output;
+};
+
+#define XISR_MASK 0x00ffffff
+#define CPPR_MASK 0xff000000
+
+#define XISR(ss) (((ss)->xirr) & XISR_MASK)
+#define CPPR(ss) (((ss)->xirr) >> 24)
+
+struct ics_state;
+
+struct icp_state {
+ long nr_servers;
+ struct icp_server_state *ss;
+ struct ics_state *ics;
+};
+
+static void ics_reject(struct ics_state *ics, int nr);
+static void ics_resend(struct ics_state *ics);
+static void ics_eoi(struct ics_state *ics, int nr);
+
+static void icp_check_ipi(struct icp_state *icp, int server)
+{
+ struct icp_server_state *ss = icp->ss + server;
+
+ if (XISR(ss) && (ss->pending_priority <= ss->mfrr)) {
+ return;
+ }
+
+ trace_xics_icp_check_ipi(server, ss->mfrr);
+
+ if (XISR(ss)) {
+ ics_reject(icp->ics, XISR(ss));
+ }
+
+ ss->xirr = (ss->xirr & ~XISR_MASK) | XICS_IPI;
+ ss->pending_priority = ss->mfrr;
+ qemu_irq_raise(ss->output);
+}
+
+static void icp_resend(struct icp_state *icp, int server)
+{
+ struct icp_server_state *ss = icp->ss + server;
+
+ if (ss->mfrr < CPPR(ss)) {
+ icp_check_ipi(icp, server);
+ }
+ ics_resend(icp->ics);
+}
+
+static void icp_set_cppr(struct icp_state *icp, int server, uint8_t cppr)
+{
+ struct icp_server_state *ss = icp->ss + server;
+ uint8_t old_cppr;
+ uint32_t old_xisr;
+
+ old_cppr = CPPR(ss);
+ ss->xirr = (ss->xirr & ~CPPR_MASK) | (cppr << 24);
+
+ if (cppr < old_cppr) {
+ if (XISR(ss) && (cppr <= ss->pending_priority)) {
+ old_xisr = XISR(ss);
+ ss->xirr &= ~XISR_MASK; /* Clear XISR */
+ qemu_irq_lower(ss->output);
+ ics_reject(icp->ics, old_xisr);
+ }
+ } else {
+ if (!XISR(ss)) {
+ icp_resend(icp, server);
+ }
+ }
+}
+
+static void icp_set_mfrr(struct icp_state *icp, int server, uint8_t mfrr)
+{
+ struct icp_server_state *ss = icp->ss + server;
+
+ ss->mfrr = mfrr;
+ if (mfrr < CPPR(ss)) {
+ icp_check_ipi(icp, server);
+ }
+}
+
+static uint32_t icp_accept(struct icp_server_state *ss)
+{
+ uint32_t xirr = ss->xirr;
+
+ qemu_irq_lower(ss->output);
+ ss->xirr = ss->pending_priority << 24;
+
+ trace_xics_icp_accept(xirr, ss->xirr);
+
+ return xirr;
+}
+
+static void icp_eoi(struct icp_state *icp, int server, uint32_t xirr)
+{
+ struct icp_server_state *ss = icp->ss + server;
+
+ /* Send EOI -> ICS */
+ ss->xirr = (ss->xirr & ~CPPR_MASK) | (xirr & CPPR_MASK);
+ trace_xics_icp_eoi(server, xirr, ss->xirr);
+ ics_eoi(icp->ics, xirr & XISR_MASK);
+ if (!XISR(ss)) {
+ icp_resend(icp, server);
+ }
+}
+
+static void icp_irq(struct icp_state *icp, int server, int nr, uint8_t priority)
+{
+ struct icp_server_state *ss = icp->ss + server;
+
+ trace_xics_icp_irq(server, nr, priority);
+
+ if ((priority >= CPPR(ss))
+ || (XISR(ss) && (ss->pending_priority <= priority))) {
+ ics_reject(icp->ics, nr);
+ } else {
+ if (XISR(ss)) {
+ ics_reject(icp->ics, XISR(ss));
+ }
+ ss->xirr = (ss->xirr & ~XISR_MASK) | (nr & XISR_MASK);
+ ss->pending_priority = priority;
+ trace_xics_icp_raise(ss->xirr, ss->pending_priority);
+ qemu_irq_raise(ss->output);
+ }
+}
+
+/*
+ * ICS: Source layer
+ */
+
+struct ics_irq_state {
+ int server;
+ uint8_t priority;
+ uint8_t saved_priority;
+#define XICS_STATUS_ASSERTED 0x1
+#define XICS_STATUS_SENT 0x2
+#define XICS_STATUS_REJECTED 0x4
+#define XICS_STATUS_MASKED_PENDING 0x8
+ uint8_t status;
+};
+
+struct ics_state {
+ int nr_irqs;
+ int offset;
+ qemu_irq *qirqs;
+ bool *islsi;
+ struct ics_irq_state *irqs;
+ struct icp_state *icp;
+};
+
+static int ics_valid_irq(struct ics_state *ics, uint32_t nr)
+{
+ return (nr >= ics->offset)
+ && (nr < (ics->offset + ics->nr_irqs));
+}
+
+static void resend_msi(struct ics_state *ics, int srcno)
+{
+ struct ics_irq_state *irq = ics->irqs + srcno;
+
+ /* FIXME: filter by server#? */
+ if (irq->status & XICS_STATUS_REJECTED) {
+ irq->status &= ~XICS_STATUS_REJECTED;
+ if (irq->priority != 0xff) {
+ icp_irq(ics->icp, irq->server, srcno + ics->offset,
+ irq->priority);
+ }
+ }
+}
+
+static void resend_lsi(struct ics_state *ics, int srcno)
+{
+ struct ics_irq_state *irq = ics->irqs + srcno;
+
+ if ((irq->priority != 0xff)
+ && (irq->status & XICS_STATUS_ASSERTED)
+ && !(irq->status & XICS_STATUS_SENT)) {
+ irq->status |= XICS_STATUS_SENT;
+ icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority);
+ }
+}
+
+static void set_irq_msi(struct ics_state *ics, int srcno, int val)
+{
+ struct ics_irq_state *irq = ics->irqs + srcno;
+
+ trace_xics_set_irq_msi(srcno, srcno + ics->offset);
+
+ if (val) {
+ if (irq->priority == 0xff) {
+ irq->status |= XICS_STATUS_MASKED_PENDING;
+ trace_xics_masked_pending();
+ } else {
+ icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority);
+ }
+ }
+}
+
+static void set_irq_lsi(struct ics_state *ics, int srcno, int val)
+{
+ struct ics_irq_state *irq = ics->irqs + srcno;
+
+ trace_xics_set_irq_lsi(srcno, srcno + ics->offset);
+ if (val) {
+ irq->status |= XICS_STATUS_ASSERTED;
+ } else {
+ irq->status &= ~XICS_STATUS_ASSERTED;
+ }
+ resend_lsi(ics, srcno);
+}
+
+static void ics_set_irq(void *opaque, int srcno, int val)
+{
+ struct ics_state *ics = (struct ics_state *)opaque;
+
+ if (ics->islsi[srcno]) {
+ set_irq_lsi(ics, srcno, val);
+ } else {
+ set_irq_msi(ics, srcno, val);
+ }
+}
+
+static void write_xive_msi(struct ics_state *ics, int srcno)
+{
+ struct ics_irq_state *irq = ics->irqs + srcno;
+
+ if (!(irq->status & XICS_STATUS_MASKED_PENDING)
+ || (irq->priority == 0xff)) {
+ return;
+ }
+
+ irq->status &= ~XICS_STATUS_MASKED_PENDING;
+ icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority);
+}
+
+static void write_xive_lsi(struct ics_state *ics, int srcno)
+{
+ resend_lsi(ics, srcno);
+}
+
+static void ics_write_xive(struct ics_state *ics, int nr, int server,
+ uint8_t priority, uint8_t saved_priority)
+{
+ int srcno = nr - ics->offset;
+ struct ics_irq_state *irq = ics->irqs + srcno;
+
+ irq->server = server;
+ irq->priority = priority;
+ irq->saved_priority = saved_priority;
+
+ trace_xics_ics_write_xive(nr, srcno, server, priority);
+
+ if (ics->islsi[srcno]) {
+ write_xive_lsi(ics, srcno);
+ } else {
+ write_xive_msi(ics, srcno);
+ }
+}
+
+static void ics_reject(struct ics_state *ics, int nr)
+{
+ struct ics_irq_state *irq = ics->irqs + nr - ics->offset;
+
+ trace_xics_ics_reject(nr, nr - ics->offset);
+ irq->status |= XICS_STATUS_REJECTED; /* Irrelevant but harmless for LSI */
+ irq->status &= ~XICS_STATUS_SENT; /* Irrelevant but harmless for MSI */
+}
+
+static void ics_resend(struct ics_state *ics)
+{
+ int i;
+
+ for (i = 0; i < ics->nr_irqs; i++) {
+ /* FIXME: filter by server#? */
+ if (ics->islsi[i]) {
+ resend_lsi(ics, i);
+ } else {
+ resend_msi(ics, i);
+ }
+ }
+}
+
+static void ics_eoi(struct ics_state *ics, int nr)
+{
+ int srcno = nr - ics->offset;
+ struct ics_irq_state *irq = ics->irqs + srcno;
+
+ trace_xics_ics_eoi(nr);
+
+ if (ics->islsi[srcno]) {
+ irq->status &= ~XICS_STATUS_SENT;
+ }
+}
+
+/*
+ * Exported functions
+ */
+
+qemu_irq xics_get_qirq(struct icp_state *icp, int irq)
+{
+ if (!ics_valid_irq(icp->ics, irq)) {
+ return NULL;
+ }
+
+ return icp->ics->qirqs[irq - icp->ics->offset];
+}
+
+void xics_set_irq_type(struct icp_state *icp, int irq, bool lsi)
+{
+ assert(ics_valid_irq(icp->ics, irq));
+
+ icp->ics->islsi[irq - icp->ics->offset] = lsi;
+}
+
+static target_ulong h_cppr(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ CPUState *cs = CPU(cpu);
+ target_ulong cppr = args[0];
+
+ icp_set_cppr(spapr->icp, cs->cpu_index, cppr);
+ return H_SUCCESS;
+}
+
+static target_ulong h_ipi(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ target_ulong server = args[0];
+ target_ulong mfrr = args[1];
+
+ if (server >= spapr->icp->nr_servers) {
+ return H_PARAMETER;
+ }
+
+ icp_set_mfrr(spapr->icp, server, mfrr);
+ return H_SUCCESS;
+}
+
+static target_ulong h_xirr(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ CPUState *cs = CPU(cpu);
+ uint32_t xirr = icp_accept(spapr->icp->ss + cs->cpu_index);
+
+ args[0] = xirr;
+ return H_SUCCESS;
+}
+
+static target_ulong h_eoi(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ CPUState *cs = CPU(cpu);
+ target_ulong xirr = args[0];
+
+ icp_eoi(spapr->icp, cs->cpu_index, xirr);
+ return H_SUCCESS;
+}
+
+static void rtas_set_xive(sPAPREnvironment *spapr, uint32_t token,
+ uint32_t nargs, target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ struct ics_state *ics = spapr->icp->ics;
+ uint32_t nr, server, priority;
+
+ if ((nargs != 3) || (nret != 1)) {
+ rtas_st(rets, 0, -3);
+ return;
+ }
+
+ nr = rtas_ld(args, 0);
+ server = rtas_ld(args, 1);
+ priority = rtas_ld(args, 2);
+
+ if (!ics_valid_irq(ics, nr) || (server >= ics->icp->nr_servers)
+ || (priority > 0xff)) {
+ rtas_st(rets, 0, -3);
+ return;
+ }
+
+ ics_write_xive(ics, nr, server, priority, priority);
+
+ rtas_st(rets, 0, 0); /* Success */
+}
+
+static void rtas_get_xive(sPAPREnvironment *spapr, uint32_t token,
+ uint32_t nargs, target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ struct ics_state *ics = spapr->icp->ics;
+ uint32_t nr;
+
+ if ((nargs != 1) || (nret != 3)) {
+ rtas_st(rets, 0, -3);
+ return;
+ }
+
+ nr = rtas_ld(args, 0);
+
+ if (!ics_valid_irq(ics, nr)) {
+ rtas_st(rets, 0, -3);
+ return;
+ }
+
+ rtas_st(rets, 0, 0); /* Success */
+ rtas_st(rets, 1, ics->irqs[nr - ics->offset].server);
+ rtas_st(rets, 2, ics->irqs[nr - ics->offset].priority);
+}
+
+static void rtas_int_off(sPAPREnvironment *spapr, uint32_t token,
+ uint32_t nargs, target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ struct ics_state *ics = spapr->icp->ics;
+ uint32_t nr;
+
+ if ((nargs != 1) || (nret != 1)) {
+ rtas_st(rets, 0, -3);
+ return;
+ }
+
+ nr = rtas_ld(args, 0);
+
+ if (!ics_valid_irq(ics, nr)) {
+ rtas_st(rets, 0, -3);
+ return;
+ }
+
+ ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server, 0xff,
+ ics->irqs[nr - ics->offset].priority);
+
+ rtas_st(rets, 0, 0); /* Success */
+}
+
+static void rtas_int_on(sPAPREnvironment *spapr, uint32_t token,
+ uint32_t nargs, target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ struct ics_state *ics = spapr->icp->ics;
+ uint32_t nr;
+
+ if ((nargs != 1) || (nret != 1)) {
+ rtas_st(rets, 0, -3);
+ return;
+ }
+
+ nr = rtas_ld(args, 0);
+
+ if (!ics_valid_irq(ics, nr)) {
+ rtas_st(rets, 0, -3);
+ return;
+ }
+
+ ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server,
+ ics->irqs[nr - ics->offset].saved_priority,
+ ics->irqs[nr - ics->offset].saved_priority);
+
+ rtas_st(rets, 0, 0); /* Success */
+}
+
+static void xics_reset(void *opaque)
+{
+ struct icp_state *icp = (struct icp_state *)opaque;
+ struct ics_state *ics = icp->ics;
+ int i;
+
+ for (i = 0; i < icp->nr_servers; i++) {
+ icp->ss[i].xirr = 0;
+ icp->ss[i].pending_priority = 0xff;
+ icp->ss[i].mfrr = 0xff;
+ /* Make all outputs are deasserted */
+ qemu_set_irq(icp->ss[i].output, 0);
+ }
+
+ memset(ics->irqs, 0, sizeof(struct ics_irq_state) * ics->nr_irqs);
+ for (i = 0; i < ics->nr_irqs; i++) {
+ ics->irqs[i].priority = 0xff;
+ ics->irqs[i].saved_priority = 0xff;
+ }
+}
+
+struct icp_state *xics_system_init(int nr_irqs)
+{
+ CPUPPCState *env;
+ CPUState *cpu;
+ int max_server_num;
+ struct icp_state *icp;
+ struct ics_state *ics;
+
+ max_server_num = -1;
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ cpu = CPU(ppc_env_get_cpu(env));
+ if (cpu->cpu_index > max_server_num) {
+ max_server_num = cpu->cpu_index;
+ }
+ }
+
+ icp = g_malloc0(sizeof(*icp));
+ icp->nr_servers = max_server_num + 1;
+ icp->ss = g_malloc0(icp->nr_servers*sizeof(struct icp_server_state));
+
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ cpu = CPU(ppc_env_get_cpu(env));
+ struct icp_server_state *ss = &icp->ss[cpu->cpu_index];
+
+ switch (PPC_INPUT(env)) {
+ case PPC_FLAGS_INPUT_POWER7:
+ ss->output = env->irq_inputs[POWER7_INPUT_INT];
+ break;
+
+ case PPC_FLAGS_INPUT_970:
+ ss->output = env->irq_inputs[PPC970_INPUT_INT];
+ break;
+
+ default:
+ hw_error("XICS interrupt model does not support this CPU bus "
+ "model\n");
+ exit(1);
+ }
+ }
+
+ ics = g_malloc0(sizeof(*ics));
+ ics->nr_irqs = nr_irqs;
+ ics->offset = XICS_IRQ_BASE;
+ ics->irqs = g_malloc0(nr_irqs * sizeof(struct ics_irq_state));
+ ics->islsi = g_malloc0(nr_irqs * sizeof(bool));
+
+ icp->ics = ics;
+ ics->icp = icp;
+
+ ics->qirqs = qemu_allocate_irqs(ics_set_irq, ics, nr_irqs);
+
+ spapr_register_hypercall(H_CPPR, h_cppr);
+ spapr_register_hypercall(H_IPI, h_ipi);
+ spapr_register_hypercall(H_XIRR, h_xirr);
+ spapr_register_hypercall(H_EOI, h_eoi);
+
+ spapr_rtas_register("ibm,set-xive", rtas_set_xive);
+ spapr_rtas_register("ibm,get-xive", rtas_get_xive);
+ spapr_rtas_register("ibm,int-off", rtas_int_off);
+ spapr_rtas_register("ibm,int-on", rtas_int_on);
+
+ qemu_register_reset(xics_reset, icp);
+
+ return icp;
+}
+++ /dev/null
-/*
- * QEMU PowerPC 4xx embedded processors shared devices emulation
- *
- * Copyright (c) 2007 Jocelyn Mayer
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/hw.h"
-#include "hw/ppc.h"
-#include "hw/ppc4xx.h"
-#include "qemu/log.h"
-#include "exec/address-spaces.h"
-
-//#define DEBUG_MMIO
-//#define DEBUG_UNASSIGNED
-#define DEBUG_UIC
-
-
-#ifdef DEBUG_UIC
-# define LOG_UIC(...) qemu_log_mask(CPU_LOG_INT, ## __VA_ARGS__)
-#else
-# define LOG_UIC(...) do { } while (0)
-#endif
-
-static void ppc4xx_reset(void *opaque)
-{
- PowerPCCPU *cpu = opaque;
-
- cpu_reset(CPU(cpu));
-}
-
-/*****************************************************************************/
-/* Generic PowerPC 4xx processor instantiation */
-PowerPCCPU *ppc4xx_init(const char *cpu_model,
- clk_setup_t *cpu_clk, clk_setup_t *tb_clk,
- uint32_t sysclk)
-{
- PowerPCCPU *cpu;
- CPUPPCState *env;
-
- /* init CPUs */
- cpu = cpu_ppc_init(cpu_model);
- if (cpu == NULL) {
- fprintf(stderr, "Unable to find PowerPC %s CPU definition\n",
- cpu_model);
- exit(1);
- }
- env = &cpu->env;
-
- cpu_clk->cb = NULL; /* We don't care about CPU clock frequency changes */
- cpu_clk->opaque = env;
- /* Set time-base frequency to sysclk */
- tb_clk->cb = ppc_40x_timers_init(env, sysclk, PPC_INTERRUPT_PIT);
- tb_clk->opaque = env;
- ppc_dcr_init(env, NULL, NULL);
- /* Register qemu callbacks */
- qemu_register_reset(ppc4xx_reset, cpu);
-
- return cpu;
-}
-
-/*****************************************************************************/
-/* "Universal" Interrupt controller */
-enum {
- DCR_UICSR = 0x000,
- DCR_UICSRS = 0x001,
- DCR_UICER = 0x002,
- DCR_UICCR = 0x003,
- DCR_UICPR = 0x004,
- DCR_UICTR = 0x005,
- DCR_UICMSR = 0x006,
- DCR_UICVR = 0x007,
- DCR_UICVCR = 0x008,
- DCR_UICMAX = 0x009,
-};
-
-#define UIC_MAX_IRQ 32
-typedef struct ppcuic_t ppcuic_t;
-struct ppcuic_t {
- uint32_t dcr_base;
- int use_vectors;
- uint32_t level; /* Remembers the state of level-triggered interrupts. */
- uint32_t uicsr; /* Status register */
- uint32_t uicer; /* Enable register */
- uint32_t uiccr; /* Critical register */
- uint32_t uicpr; /* Polarity register */
- uint32_t uictr; /* Triggering register */
- uint32_t uicvcr; /* Vector configuration register */
- uint32_t uicvr;
- qemu_irq *irqs;
-};
-
-static void ppcuic_trigger_irq (ppcuic_t *uic)
-{
- uint32_t ir, cr;
- int start, end, inc, i;
-
- /* Trigger interrupt if any is pending */
- ir = uic->uicsr & uic->uicer & (~uic->uiccr);
- cr = uic->uicsr & uic->uicer & uic->uiccr;
- LOG_UIC("%s: uicsr %08" PRIx32 " uicer %08" PRIx32
- " uiccr %08" PRIx32 "\n"
- " %08" PRIx32 " ir %08" PRIx32 " cr %08" PRIx32 "\n",
- __func__, uic->uicsr, uic->uicer, uic->uiccr,
- uic->uicsr & uic->uicer, ir, cr);
- if (ir != 0x0000000) {
- LOG_UIC("Raise UIC interrupt\n");
- qemu_irq_raise(uic->irqs[PPCUIC_OUTPUT_INT]);
- } else {
- LOG_UIC("Lower UIC interrupt\n");
- qemu_irq_lower(uic->irqs[PPCUIC_OUTPUT_INT]);
- }
- /* Trigger critical interrupt if any is pending and update vector */
- if (cr != 0x0000000) {
- qemu_irq_raise(uic->irqs[PPCUIC_OUTPUT_CINT]);
- if (uic->use_vectors) {
- /* Compute critical IRQ vector */
- if (uic->uicvcr & 1) {
- start = 31;
- end = 0;
- inc = -1;
- } else {
- start = 0;
- end = 31;
- inc = 1;
- }
- uic->uicvr = uic->uicvcr & 0xFFFFFFFC;
- for (i = start; i <= end; i += inc) {
- if (cr & (1 << i)) {
- uic->uicvr += (i - start) * 512 * inc;
- break;
- }
- }
- }
- LOG_UIC("Raise UIC critical interrupt - "
- "vector %08" PRIx32 "\n", uic->uicvr);
- } else {
- LOG_UIC("Lower UIC critical interrupt\n");
- qemu_irq_lower(uic->irqs[PPCUIC_OUTPUT_CINT]);
- uic->uicvr = 0x00000000;
- }
-}
-
-static void ppcuic_set_irq (void *opaque, int irq_num, int level)
-{
- ppcuic_t *uic;
- uint32_t mask, sr;
-
- uic = opaque;
- mask = 1 << (31-irq_num);
- LOG_UIC("%s: irq %d level %d uicsr %08" PRIx32
- " mask %08" PRIx32 " => %08" PRIx32 " %08" PRIx32 "\n",
- __func__, irq_num, level,
- uic->uicsr, mask, uic->uicsr & mask, level << irq_num);
- if (irq_num < 0 || irq_num > 31)
- return;
- sr = uic->uicsr;
-
- /* Update status register */
- if (uic->uictr & mask) {
- /* Edge sensitive interrupt */
- if (level == 1)
- uic->uicsr |= mask;
- } else {
- /* Level sensitive interrupt */
- if (level == 1) {
- uic->uicsr |= mask;
- uic->level |= mask;
- } else {
- uic->uicsr &= ~mask;
- uic->level &= ~mask;
- }
- }
- LOG_UIC("%s: irq %d level %d sr %" PRIx32 " => "
- "%08" PRIx32 "\n", __func__, irq_num, level, uic->uicsr, sr);
- if (sr != uic->uicsr)
- ppcuic_trigger_irq(uic);
-}
-
-static uint32_t dcr_read_uic (void *opaque, int dcrn)
-{
- ppcuic_t *uic;
- uint32_t ret;
-
- uic = opaque;
- dcrn -= uic->dcr_base;
- switch (dcrn) {
- case DCR_UICSR:
- case DCR_UICSRS:
- ret = uic->uicsr;
- break;
- case DCR_UICER:
- ret = uic->uicer;
- break;
- case DCR_UICCR:
- ret = uic->uiccr;
- break;
- case DCR_UICPR:
- ret = uic->uicpr;
- break;
- case DCR_UICTR:
- ret = uic->uictr;
- break;
- case DCR_UICMSR:
- ret = uic->uicsr & uic->uicer;
- break;
- case DCR_UICVR:
- if (!uic->use_vectors)
- goto no_read;
- ret = uic->uicvr;
- break;
- case DCR_UICVCR:
- if (!uic->use_vectors)
- goto no_read;
- ret = uic->uicvcr;
- break;
- default:
- no_read:
- ret = 0x00000000;
- break;
- }
-
- return ret;
-}
-
-static void dcr_write_uic (void *opaque, int dcrn, uint32_t val)
-{
- ppcuic_t *uic;
-
- uic = opaque;
- dcrn -= uic->dcr_base;
- LOG_UIC("%s: dcr %d val 0x%x\n", __func__, dcrn, val);
- switch (dcrn) {
- case DCR_UICSR:
- uic->uicsr &= ~val;
- uic->uicsr |= uic->level;
- ppcuic_trigger_irq(uic);
- break;
- case DCR_UICSRS:
- uic->uicsr |= val;
- ppcuic_trigger_irq(uic);
- break;
- case DCR_UICER:
- uic->uicer = val;
- ppcuic_trigger_irq(uic);
- break;
- case DCR_UICCR:
- uic->uiccr = val;
- ppcuic_trigger_irq(uic);
- break;
- case DCR_UICPR:
- uic->uicpr = val;
- break;
- case DCR_UICTR:
- uic->uictr = val;
- ppcuic_trigger_irq(uic);
- break;
- case DCR_UICMSR:
- break;
- case DCR_UICVR:
- break;
- case DCR_UICVCR:
- uic->uicvcr = val & 0xFFFFFFFD;
- ppcuic_trigger_irq(uic);
- break;
- }
-}
-
-static void ppcuic_reset (void *opaque)
-{
- ppcuic_t *uic;
-
- uic = opaque;
- uic->uiccr = 0x00000000;
- uic->uicer = 0x00000000;
- uic->uicpr = 0x00000000;
- uic->uicsr = 0x00000000;
- uic->uictr = 0x00000000;
- if (uic->use_vectors) {
- uic->uicvcr = 0x00000000;
- uic->uicvr = 0x0000000;
- }
-}
-
-qemu_irq *ppcuic_init (CPUPPCState *env, qemu_irq *irqs,
- uint32_t dcr_base, int has_ssr, int has_vr)
-{
- ppcuic_t *uic;
- int i;
-
- uic = g_malloc0(sizeof(ppcuic_t));
- uic->dcr_base = dcr_base;
- uic->irqs = irqs;
- if (has_vr)
- uic->use_vectors = 1;
- for (i = 0; i < DCR_UICMAX; i++) {
- ppc_dcr_register(env, dcr_base + i, uic,
- &dcr_read_uic, &dcr_write_uic);
- }
- qemu_register_reset(ppcuic_reset, uic);
-
- return qemu_allocate_irqs(&ppcuic_set_irq, uic, UIC_MAX_IRQ);
-}
-
-/*****************************************************************************/
-/* SDRAM controller */
-typedef struct ppc4xx_sdram_t ppc4xx_sdram_t;
-struct ppc4xx_sdram_t {
- uint32_t addr;
- int nbanks;
- MemoryRegion containers[4]; /* used for clipping */
- MemoryRegion *ram_memories;
- hwaddr ram_bases[4];
- hwaddr ram_sizes[4];
- uint32_t besr0;
- uint32_t besr1;
- uint32_t bear;
- uint32_t cfg;
- uint32_t status;
- uint32_t rtr;
- uint32_t pmit;
- uint32_t bcr[4];
- uint32_t tr;
- uint32_t ecccfg;
- uint32_t eccesr;
- qemu_irq irq;
-};
-
-enum {
- SDRAM0_CFGADDR = 0x010,
- SDRAM0_CFGDATA = 0x011,
-};
-
-/* XXX: TOFIX: some patches have made this code become inconsistent:
- * there are type inconsistencies, mixing hwaddr, target_ulong
- * and uint32_t
- */
-static uint32_t sdram_bcr (hwaddr ram_base,
- hwaddr ram_size)
-{
- uint32_t bcr;
-
- switch (ram_size) {
- case (4 * 1024 * 1024):
- bcr = 0x00000000;
- break;
- case (8 * 1024 * 1024):
- bcr = 0x00020000;
- break;
- case (16 * 1024 * 1024):
- bcr = 0x00040000;
- break;
- case (32 * 1024 * 1024):
- bcr = 0x00060000;
- break;
- case (64 * 1024 * 1024):
- bcr = 0x00080000;
- break;
- case (128 * 1024 * 1024):
- bcr = 0x000A0000;
- break;
- case (256 * 1024 * 1024):
- bcr = 0x000C0000;
- break;
- default:
- printf("%s: invalid RAM size " TARGET_FMT_plx "\n", __func__,
- ram_size);
- return 0x00000000;
- }
- bcr |= ram_base & 0xFF800000;
- bcr |= 1;
-
- return bcr;
-}
-
-static inline hwaddr sdram_base(uint32_t bcr)
-{
- return bcr & 0xFF800000;
-}
-
-static target_ulong sdram_size (uint32_t bcr)
-{
- target_ulong size;
- int sh;
-
- sh = (bcr >> 17) & 0x7;
- if (sh == 7)
- size = -1;
- else
- size = (4 * 1024 * 1024) << sh;
-
- return size;
-}
-
-static void sdram_set_bcr(ppc4xx_sdram_t *sdram,
- uint32_t *bcrp, uint32_t bcr, int enabled)
-{
- unsigned n = bcrp - sdram->bcr;
-
- if (*bcrp & 0x00000001) {
- /* Unmap RAM */
-#ifdef DEBUG_SDRAM
- printf("%s: unmap RAM area " TARGET_FMT_plx " " TARGET_FMT_lx "\n",
- __func__, sdram_base(*bcrp), sdram_size(*bcrp));
-#endif
- memory_region_del_subregion(get_system_memory(),
- &sdram->containers[n]);
- memory_region_del_subregion(&sdram->containers[n],
- &sdram->ram_memories[n]);
- memory_region_destroy(&sdram->containers[n]);
- }
- *bcrp = bcr & 0xFFDEE001;
- if (enabled && (bcr & 0x00000001)) {
-#ifdef DEBUG_SDRAM
- printf("%s: Map RAM area " TARGET_FMT_plx " " TARGET_FMT_lx "\n",
- __func__, sdram_base(bcr), sdram_size(bcr));
-#endif
- memory_region_init(&sdram->containers[n], "sdram-containers",
- sdram_size(bcr));
- memory_region_add_subregion(&sdram->containers[n], 0,
- &sdram->ram_memories[n]);
- memory_region_add_subregion(get_system_memory(),
- sdram_base(bcr),
- &sdram->containers[n]);
- }
-}
-
-static void sdram_map_bcr (ppc4xx_sdram_t *sdram)
-{
- int i;
-
- for (i = 0; i < sdram->nbanks; i++) {
- if (sdram->ram_sizes[i] != 0) {
- sdram_set_bcr(sdram,
- &sdram->bcr[i],
- sdram_bcr(sdram->ram_bases[i], sdram->ram_sizes[i]),
- 1);
- } else {
- sdram_set_bcr(sdram, &sdram->bcr[i], 0x00000000, 0);
- }
- }
-}
-
-static void sdram_unmap_bcr (ppc4xx_sdram_t *sdram)
-{
- int i;
-
- for (i = 0; i < sdram->nbanks; i++) {
-#ifdef DEBUG_SDRAM
- printf("%s: Unmap RAM area " TARGET_FMT_plx " " TARGET_FMT_lx "\n",
- __func__, sdram_base(sdram->bcr[i]), sdram_size(sdram->bcr[i]));
-#endif
- memory_region_del_subregion(get_system_memory(),
- &sdram->ram_memories[i]);
- }
-}
-
-static uint32_t dcr_read_sdram (void *opaque, int dcrn)
-{
- ppc4xx_sdram_t *sdram;
- uint32_t ret;
-
- sdram = opaque;
- switch (dcrn) {
- case SDRAM0_CFGADDR:
- ret = sdram->addr;
- break;
- case SDRAM0_CFGDATA:
- switch (sdram->addr) {
- case 0x00: /* SDRAM_BESR0 */
- ret = sdram->besr0;
- break;
- case 0x08: /* SDRAM_BESR1 */
- ret = sdram->besr1;
- break;
- case 0x10: /* SDRAM_BEAR */
- ret = sdram->bear;
- break;
- case 0x20: /* SDRAM_CFG */
- ret = sdram->cfg;
- break;
- case 0x24: /* SDRAM_STATUS */
- ret = sdram->status;
- break;
- case 0x30: /* SDRAM_RTR */
- ret = sdram->rtr;
- break;
- case 0x34: /* SDRAM_PMIT */
- ret = sdram->pmit;
- break;
- case 0x40: /* SDRAM_B0CR */
- ret = sdram->bcr[0];
- break;
- case 0x44: /* SDRAM_B1CR */
- ret = sdram->bcr[1];
- break;
- case 0x48: /* SDRAM_B2CR */
- ret = sdram->bcr[2];
- break;
- case 0x4C: /* SDRAM_B3CR */
- ret = sdram->bcr[3];
- break;
- case 0x80: /* SDRAM_TR */
- ret = -1; /* ? */
- break;
- case 0x94: /* SDRAM_ECCCFG */
- ret = sdram->ecccfg;
- break;
- case 0x98: /* SDRAM_ECCESR */
- ret = sdram->eccesr;
- break;
- default: /* Error */
- ret = -1;
- break;
- }
- break;
- default:
- /* Avoid gcc warning */
- ret = 0x00000000;
- break;
- }
-
- return ret;
-}
-
-static void dcr_write_sdram (void *opaque, int dcrn, uint32_t val)
-{
- ppc4xx_sdram_t *sdram;
-
- sdram = opaque;
- switch (dcrn) {
- case SDRAM0_CFGADDR:
- sdram->addr = val;
- break;
- case SDRAM0_CFGDATA:
- switch (sdram->addr) {
- case 0x00: /* SDRAM_BESR0 */
- sdram->besr0 &= ~val;
- break;
- case 0x08: /* SDRAM_BESR1 */
- sdram->besr1 &= ~val;
- break;
- case 0x10: /* SDRAM_BEAR */
- sdram->bear = val;
- break;
- case 0x20: /* SDRAM_CFG */
- val &= 0xFFE00000;
- if (!(sdram->cfg & 0x80000000) && (val & 0x80000000)) {
-#ifdef DEBUG_SDRAM
- printf("%s: enable SDRAM controller\n", __func__);
-#endif
- /* validate all RAM mappings */
- sdram_map_bcr(sdram);
- sdram->status &= ~0x80000000;
- } else if ((sdram->cfg & 0x80000000) && !(val & 0x80000000)) {
-#ifdef DEBUG_SDRAM
- printf("%s: disable SDRAM controller\n", __func__);
-#endif
- /* invalidate all RAM mappings */
- sdram_unmap_bcr(sdram);
- sdram->status |= 0x80000000;
- }
- if (!(sdram->cfg & 0x40000000) && (val & 0x40000000))
- sdram->status |= 0x40000000;
- else if ((sdram->cfg & 0x40000000) && !(val & 0x40000000))
- sdram->status &= ~0x40000000;
- sdram->cfg = val;
- break;
- case 0x24: /* SDRAM_STATUS */
- /* Read-only register */
- break;
- case 0x30: /* SDRAM_RTR */
- sdram->rtr = val & 0x3FF80000;
- break;
- case 0x34: /* SDRAM_PMIT */
- sdram->pmit = (val & 0xF8000000) | 0x07C00000;
- break;
- case 0x40: /* SDRAM_B0CR */
- sdram_set_bcr(sdram, &sdram->bcr[0], val, sdram->cfg & 0x80000000);
- break;
- case 0x44: /* SDRAM_B1CR */
- sdram_set_bcr(sdram, &sdram->bcr[1], val, sdram->cfg & 0x80000000);
- break;
- case 0x48: /* SDRAM_B2CR */
- sdram_set_bcr(sdram, &sdram->bcr[2], val, sdram->cfg & 0x80000000);
- break;
- case 0x4C: /* SDRAM_B3CR */
- sdram_set_bcr(sdram, &sdram->bcr[3], val, sdram->cfg & 0x80000000);
- break;
- case 0x80: /* SDRAM_TR */
- sdram->tr = val & 0x018FC01F;
- break;
- case 0x94: /* SDRAM_ECCCFG */
- sdram->ecccfg = val & 0x00F00000;
- break;
- case 0x98: /* SDRAM_ECCESR */
- val &= 0xFFF0F000;
- if (sdram->eccesr == 0 && val != 0)
- qemu_irq_raise(sdram->irq);
- else if (sdram->eccesr != 0 && val == 0)
- qemu_irq_lower(sdram->irq);
- sdram->eccesr = val;
- break;
- default: /* Error */
- break;
- }
- break;
- }
-}
-
-static void sdram_reset (void *opaque)
-{
- ppc4xx_sdram_t *sdram;
-
- sdram = opaque;
- sdram->addr = 0x00000000;
- sdram->bear = 0x00000000;
- sdram->besr0 = 0x00000000; /* No error */
- sdram->besr1 = 0x00000000; /* No error */
- sdram->cfg = 0x00000000;
- sdram->ecccfg = 0x00000000; /* No ECC */
- sdram->eccesr = 0x00000000; /* No error */
- sdram->pmit = 0x07C00000;
- sdram->rtr = 0x05F00000;
- sdram->tr = 0x00854009;
- /* We pre-initialize RAM banks */
- sdram->status = 0x00000000;
- sdram->cfg = 0x00800000;
-}
-
-void ppc4xx_sdram_init (CPUPPCState *env, qemu_irq irq, int nbanks,
- MemoryRegion *ram_memories,
- hwaddr *ram_bases,
- hwaddr *ram_sizes,
- int do_init)
-{
- ppc4xx_sdram_t *sdram;
-
- sdram = g_malloc0(sizeof(ppc4xx_sdram_t));
- sdram->irq = irq;
- sdram->nbanks = nbanks;
- sdram->ram_memories = ram_memories;
- memset(sdram->ram_bases, 0, 4 * sizeof(hwaddr));
- memcpy(sdram->ram_bases, ram_bases,
- nbanks * sizeof(hwaddr));
- memset(sdram->ram_sizes, 0, 4 * sizeof(hwaddr));
- memcpy(sdram->ram_sizes, ram_sizes,
- nbanks * sizeof(hwaddr));
- qemu_register_reset(&sdram_reset, sdram);
- ppc_dcr_register(env, SDRAM0_CFGADDR,
- sdram, &dcr_read_sdram, &dcr_write_sdram);
- ppc_dcr_register(env, SDRAM0_CFGDATA,
- sdram, &dcr_read_sdram, &dcr_write_sdram);
- if (do_init)
- sdram_map_bcr(sdram);
-}
-
-/* Fill in consecutive SDRAM banks with 'ram_size' bytes of memory.
- *
- * sdram_bank_sizes[] must be 0-terminated.
- *
- * The 4xx SDRAM controller supports a small number of banks, and each bank
- * must be one of a small set of sizes. The number of banks and the supported
- * sizes varies by SoC. */
-ram_addr_t ppc4xx_sdram_adjust(ram_addr_t ram_size, int nr_banks,
- MemoryRegion ram_memories[],
- hwaddr ram_bases[],
- hwaddr ram_sizes[],
- const unsigned int sdram_bank_sizes[])
-{
- ram_addr_t size_left = ram_size;
- ram_addr_t base = 0;
- int i;
- int j;
-
- for (i = 0; i < nr_banks; i++) {
- for (j = 0; sdram_bank_sizes[j] != 0; j++) {
- unsigned int bank_size = sdram_bank_sizes[j];
-
- if (bank_size <= size_left) {
- char name[32];
- snprintf(name, sizeof(name), "ppc4xx.sdram%d", i);
- memory_region_init_ram(&ram_memories[i], name, bank_size);
- vmstate_register_ram_global(&ram_memories[i]);
- ram_bases[i] = base;
- ram_sizes[i] = bank_size;
- base += bank_size;
- size_left -= bank_size;
- break;
- }
- }
-
- if (!size_left) {
- /* No need to use the remaining banks. */
- break;
- }
- }
-
- ram_size -= size_left;
- if (size_left)
- printf("Truncating memory to %d MiB to fit SDRAM controller limits.\n",
- (int)(ram_size >> 20));
-
- return ram_size;
-}
+++ /dev/null
-/*
- * QEMU PowerPC e500v2 ePAPR spinning code
- *
- * Copyright (C) 2011 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/>.
- *
- * This code is not really a device, but models an interface that usually
- * firmware takes care of. It's used when QEMU plays the role of firmware.
- *
- * Specification:
- *
- * https://www.power.org/resources/downloads/Power_ePAPR_APPROVED_v1.1.pdf
- *
- */
-
-#include "hw/hw.h"
-#include "sysemu/sysemu.h"
-#include "hw/sysbus.h"
-#include "sysemu/kvm.h"
-
-#define MAX_CPUS 32
-
-typedef struct spin_info {
- uint64_t addr;
- uint64_t r3;
- uint32_t resv;
- uint32_t pir;
- uint64_t reserved;
-} QEMU_PACKED SpinInfo;
-
-typedef struct spin_state {
- SysBusDevice busdev;
- MemoryRegion iomem;
- SpinInfo spin[MAX_CPUS];
-} SpinState;
-
-typedef struct spin_kick {
- PowerPCCPU *cpu;
- SpinInfo *spin;
-} SpinKick;
-
-static void spin_reset(void *opaque)
-{
- SpinState *s = opaque;
- int i;
-
- for (i = 0; i < MAX_CPUS; i++) {
- SpinInfo *info = &s->spin[i];
-
- info->pir = i;
- info->r3 = i;
- info->addr = 1;
- }
-}
-
-/* Create -kernel TLB entries for BookE, linearly spanning 256MB. */
-static inline hwaddr booke206_page_size_to_tlb(uint64_t size)
-{
- return (ffs(size >> 10) - 1) >> 1;
-}
-
-static void mmubooke_create_initial_mapping(CPUPPCState *env,
- target_ulong va,
- hwaddr pa,
- hwaddr len)
-{
- ppcmas_tlb_t *tlb = booke206_get_tlbm(env, 1, 0, 1);
- hwaddr size;
-
- size = (booke206_page_size_to_tlb(len) << MAS1_TSIZE_SHIFT);
- tlb->mas1 = MAS1_VALID | size;
- tlb->mas2 = (va & TARGET_PAGE_MASK) | MAS2_M;
- tlb->mas7_3 = pa & TARGET_PAGE_MASK;
- tlb->mas7_3 |= MAS3_UR | MAS3_UW | MAS3_UX | MAS3_SR | MAS3_SW | MAS3_SX;
- env->tlb_dirty = true;
-}
-
-static void spin_kick(void *data)
-{
- SpinKick *kick = data;
- CPUState *cpu = CPU(kick->cpu);
- CPUPPCState *env = &kick->cpu->env;
- SpinInfo *curspin = kick->spin;
- hwaddr map_size = 64 * 1024 * 1024;
- hwaddr map_start;
-
- cpu_synchronize_state(env);
- stl_p(&curspin->pir, env->spr[SPR_PIR]);
- env->nip = ldq_p(&curspin->addr) & (map_size - 1);
- env->gpr[3] = ldq_p(&curspin->r3);
- env->gpr[4] = 0;
- env->gpr[5] = 0;
- env->gpr[6] = 0;
- env->gpr[7] = map_size;
- env->gpr[8] = 0;
- env->gpr[9] = 0;
-
- map_start = ldq_p(&curspin->addr) & ~(map_size - 1);
- mmubooke_create_initial_mapping(env, 0, map_start, map_size);
-
- env->halted = 0;
- env->exception_index = -1;
- cpu->stopped = false;
- qemu_cpu_kick(cpu);
-}
-
-static void spin_write(void *opaque, hwaddr addr, uint64_t value,
- unsigned len)
-{
- SpinState *s = opaque;
- int env_idx = addr / sizeof(SpinInfo);
- CPUState *cpu;
- SpinInfo *curspin = &s->spin[env_idx];
- uint8_t *curspin_p = (uint8_t*)curspin;
-
- cpu = qemu_get_cpu(env_idx);
- if (cpu == NULL) {
- /* Unknown CPU */
- return;
- }
-
- if (cpu->cpu_index == 0) {
- /* primary CPU doesn't spin */
- return;
- }
-
- curspin_p = &curspin_p[addr % sizeof(SpinInfo)];
- switch (len) {
- case 1:
- stb_p(curspin_p, value);
- break;
- case 2:
- stw_p(curspin_p, value);
- break;
- case 4:
- stl_p(curspin_p, value);
- break;
- }
-
- if (!(ldq_p(&curspin->addr) & 1)) {
- /* run CPU */
- SpinKick kick = {
- .cpu = POWERPC_CPU(cpu),
- .spin = curspin,
- };
-
- run_on_cpu(cpu, spin_kick, &kick);
- }
-}
-
-static uint64_t spin_read(void *opaque, hwaddr addr, unsigned len)
-{
- SpinState *s = opaque;
- uint8_t *spin_p = &((uint8_t*)s->spin)[addr];
-
- switch (len) {
- case 1:
- return ldub_p(spin_p);
- case 2:
- return lduw_p(spin_p);
- case 4:
- return ldl_p(spin_p);
- default:
- hw_error("ppce500: unexpected %s with len = %u", __func__, len);
- }
-}
-
-static const MemoryRegionOps spin_rw_ops = {
- .read = spin_read,
- .write = spin_write,
- .endianness = DEVICE_BIG_ENDIAN,
-};
-
-static int ppce500_spin_initfn(SysBusDevice *dev)
-{
- SpinState *s;
-
- s = FROM_SYSBUS(SpinState, SYS_BUS_DEVICE(dev));
-
- memory_region_init_io(&s->iomem, &spin_rw_ops, s, "e500 spin pv device",
- sizeof(SpinInfo) * MAX_CPUS);
- sysbus_init_mmio(dev, &s->iomem);
-
- qemu_register_reset(spin_reset, s);
-
- return 0;
-}
-
-static void ppce500_spin_class_init(ObjectClass *klass, void *data)
-{
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
- k->init = ppce500_spin_initfn;
-}
-
-static const TypeInfo ppce500_spin_info = {
- .name = "e500-spin",
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(SpinState),
- .class_init = ppce500_spin_class_init,
-};
-
-static void ppce500_spin_register_types(void)
-{
- type_register_static(&ppce500_spin_info);
-}
-
-type_init(ppce500_spin_register_types)
+++ /dev/null
-/*
- * QEMU sPAPR VIO code
- *
- * Copyright (c) 2010 David Gibson, IBM Corporation <dwg@au1.ibm.com>
- * Based on the s390 virtio bus code:
- * Copyright (c) 2009 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/hw.h"
-#include "sysemu/sysemu.h"
-#include "hw/boards.h"
-#include "monitor/monitor.h"
-#include "hw/loader.h"
-#include "elf.h"
-#include "hw/sysbus.h"
-#include "sysemu/kvm.h"
-#include "sysemu/device_tree.h"
-#include "kvm_ppc.h"
-
-#include "hw/spapr.h"
-#include "hw/spapr_vio.h"
-#include "hw/xics.h"
-
-#ifdef CONFIG_FDT
-#include <libfdt.h>
-#endif /* CONFIG_FDT */
-
-/* #define DEBUG_SPAPR */
-
-#ifdef DEBUG_SPAPR
-#define dprintf(fmt, ...) \
- do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
-#else
-#define dprintf(fmt, ...) \
- do { } while (0)
-#endif
-
-static Property spapr_vio_props[] = {
- DEFINE_PROP_UINT32("irq", VIOsPAPRDevice, irq, 0), \
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static const TypeInfo spapr_vio_bus_info = {
- .name = TYPE_SPAPR_VIO_BUS,
- .parent = TYPE_BUS,
- .instance_size = sizeof(VIOsPAPRBus),
-};
-
-VIOsPAPRDevice *spapr_vio_find_by_reg(VIOsPAPRBus *bus, uint32_t reg)
-{
- BusChild *kid;
- VIOsPAPRDevice *dev = NULL;
-
- QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
- dev = (VIOsPAPRDevice *)kid->child;
- if (dev->reg == reg) {
- return dev;
- }
- }
-
- return NULL;
-}
-
-static char *vio_format_dev_name(VIOsPAPRDevice *dev)
-{
- VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev);
- char *name;
-
- /* Device tree style name device@reg */
- name = g_strdup_printf("%s@%x", pc->dt_name, dev->reg);
-
- return name;
-}
-
-#ifdef CONFIG_FDT
-static int vio_make_devnode(VIOsPAPRDevice *dev,
- void *fdt)
-{
- VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev);
- int vdevice_off, node_off, ret;
- char *dt_name;
-
- vdevice_off = fdt_path_offset(fdt, "/vdevice");
- if (vdevice_off < 0) {
- return vdevice_off;
- }
-
- dt_name = vio_format_dev_name(dev);
- node_off = fdt_add_subnode(fdt, vdevice_off, dt_name);
- g_free(dt_name);
- if (node_off < 0) {
- return node_off;
- }
-
- ret = fdt_setprop_cell(fdt, node_off, "reg", dev->reg);
- if (ret < 0) {
- return ret;
- }
-
- if (pc->dt_type) {
- ret = fdt_setprop_string(fdt, node_off, "device_type",
- pc->dt_type);
- if (ret < 0) {
- return ret;
- }
- }
-
- if (pc->dt_compatible) {
- ret = fdt_setprop_string(fdt, node_off, "compatible",
- pc->dt_compatible);
- if (ret < 0) {
- return ret;
- }
- }
-
- if (dev->irq) {
- uint32_t ints_prop[] = {cpu_to_be32(dev->irq), 0};
-
- ret = fdt_setprop(fdt, node_off, "interrupts", ints_prop,
- sizeof(ints_prop));
- if (ret < 0) {
- return ret;
- }
- }
-
- ret = spapr_tcet_dma_dt(fdt, node_off, "ibm,my-dma-window", dev->dma);
- if (ret < 0) {
- return ret;
- }
-
- if (pc->devnode) {
- ret = (pc->devnode)(dev, fdt, node_off);
- if (ret < 0) {
- return ret;
- }
- }
-
- return node_off;
-}
-#endif /* CONFIG_FDT */
-
-/*
- * CRQ handling
- */
-static target_ulong h_reg_crq(PowerPCCPU *cpu, sPAPREnvironment *spapr,
- target_ulong opcode, target_ulong *args)
-{
- target_ulong reg = args[0];
- target_ulong queue_addr = args[1];
- target_ulong queue_len = args[2];
- VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
-
- if (!dev) {
- hcall_dprintf("Unit 0x" TARGET_FMT_lx " does not exist\n", reg);
- return H_PARAMETER;
- }
-
- /* We can't grok a queue size bigger than 256M for now */
- if (queue_len < 0x1000 || queue_len > 0x10000000) {
- hcall_dprintf("Queue size too small or too big (0x" TARGET_FMT_lx
- ")\n", queue_len);
- return H_PARAMETER;
- }
-
- /* Check queue alignment */
- if (queue_addr & 0xfff) {
- hcall_dprintf("Queue not aligned (0x" TARGET_FMT_lx ")\n", queue_addr);
- return H_PARAMETER;
- }
-
- /* Check if device supports CRQs */
- if (!dev->crq.SendFunc) {
- hcall_dprintf("Device does not support CRQ\n");
- return H_NOT_FOUND;
- }
-
- /* Already a queue ? */
- if (dev->crq.qsize) {
- hcall_dprintf("CRQ already registered\n");
- return H_RESOURCE;
- }
- dev->crq.qladdr = queue_addr;
- dev->crq.qsize = queue_len;
- dev->crq.qnext = 0;
-
- dprintf("CRQ for dev 0x" TARGET_FMT_lx " registered at 0x"
- TARGET_FMT_lx "/0x" TARGET_FMT_lx "\n",
- reg, queue_addr, queue_len);
- return H_SUCCESS;
-}
-
-static target_ulong free_crq(VIOsPAPRDevice *dev)
-{
- dev->crq.qladdr = 0;
- dev->crq.qsize = 0;
- dev->crq.qnext = 0;
-
- dprintf("CRQ for dev 0x%" PRIx32 " freed\n", dev->reg);
-
- return H_SUCCESS;
-}
-
-static target_ulong h_free_crq(PowerPCCPU *cpu, sPAPREnvironment *spapr,
- target_ulong opcode, target_ulong *args)
-{
- target_ulong reg = args[0];
- VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
-
- if (!dev) {
- hcall_dprintf("Unit 0x" TARGET_FMT_lx " does not exist\n", reg);
- return H_PARAMETER;
- }
-
- return free_crq(dev);
-}
-
-static target_ulong h_send_crq(PowerPCCPU *cpu, sPAPREnvironment *spapr,
- target_ulong opcode, target_ulong *args)
-{
- target_ulong reg = args[0];
- target_ulong msg_hi = args[1];
- target_ulong msg_lo = args[2];
- VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
- uint64_t crq_mangle[2];
-
- if (!dev) {
- hcall_dprintf("Unit 0x" TARGET_FMT_lx " does not exist\n", reg);
- return H_PARAMETER;
- }
- crq_mangle[0] = cpu_to_be64(msg_hi);
- crq_mangle[1] = cpu_to_be64(msg_lo);
-
- if (dev->crq.SendFunc) {
- return dev->crq.SendFunc(dev, (uint8_t *)crq_mangle);
- }
-
- return H_HARDWARE;
-}
-
-static target_ulong h_enable_crq(PowerPCCPU *cpu, sPAPREnvironment *spapr,
- target_ulong opcode, target_ulong *args)
-{
- target_ulong reg = args[0];
- VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
-
- if (!dev) {
- hcall_dprintf("Unit 0x" TARGET_FMT_lx " does not exist\n", reg);
- return H_PARAMETER;
- }
-
- return 0;
-}
-
-/* Returns negative error, 0 success, or positive: queue full */
-int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq)
-{
- int rc;
- uint8_t byte;
-
- if (!dev->crq.qsize) {
- fprintf(stderr, "spapr_vio_send_creq on uninitialized queue\n");
- return -1;
- }
-
- /* Maybe do a fast path for KVM just writing to the pages */
- rc = spapr_vio_dma_read(dev, dev->crq.qladdr + dev->crq.qnext, &byte, 1);
- if (rc) {
- return rc;
- }
- if (byte != 0) {
- return 1;
- }
-
- rc = spapr_vio_dma_write(dev, dev->crq.qladdr + dev->crq.qnext + 8,
- &crq[8], 8);
- if (rc) {
- return rc;
- }
-
- kvmppc_eieio();
-
- rc = spapr_vio_dma_write(dev, dev->crq.qladdr + dev->crq.qnext, crq, 8);
- if (rc) {
- return rc;
- }
-
- dev->crq.qnext = (dev->crq.qnext + 16) % dev->crq.qsize;
-
- if (dev->signal_state & 1) {
- qemu_irq_pulse(spapr_vio_qirq(dev));
- }
-
- return 0;
-}
-
-/* "quiesce" handling */
-
-static void spapr_vio_quiesce_one(VIOsPAPRDevice *dev)
-{
- if (dev->dma) {
- spapr_tce_reset(dev->dma);
- }
- free_crq(dev);
-}
-
-static void rtas_set_tce_bypass(sPAPREnvironment *spapr, uint32_t token,
- uint32_t nargs, target_ulong args,
- uint32_t nret, target_ulong rets)
-{
- VIOsPAPRBus *bus = spapr->vio_bus;
- VIOsPAPRDevice *dev;
- uint32_t unit, enable;
-
- if (nargs != 2) {
- rtas_st(rets, 0, -3);
- return;
- }
- unit = rtas_ld(args, 0);
- enable = rtas_ld(args, 1);
- dev = spapr_vio_find_by_reg(bus, unit);
- if (!dev) {
- rtas_st(rets, 0, -3);
- return;
- }
-
- if (!dev->dma) {
- rtas_st(rets, 0, -3);
- return;
- }
-
- spapr_tce_set_bypass(dev->dma, !!enable);
-
- rtas_st(rets, 0, 0);
-}
-
-static void rtas_quiesce(sPAPREnvironment *spapr, uint32_t token,
- uint32_t nargs, target_ulong args,
- uint32_t nret, target_ulong rets)
-{
- VIOsPAPRBus *bus = spapr->vio_bus;
- BusChild *kid;
- VIOsPAPRDevice *dev = NULL;
-
- if (nargs != 0) {
- rtas_st(rets, 0, -3);
- return;
- }
-
- QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
- dev = (VIOsPAPRDevice *)kid->child;
- spapr_vio_quiesce_one(dev);
- }
-
- rtas_st(rets, 0, 0);
-}
-
-static VIOsPAPRDevice *reg_conflict(VIOsPAPRDevice *dev)
-{
- VIOsPAPRBus *bus = DO_UPCAST(VIOsPAPRBus, bus, dev->qdev.parent_bus);
- BusChild *kid;
- VIOsPAPRDevice *other;
-
- /*
- * Check for a device other than the given one which is already
- * using the requested address. We have to open code this because
- * the given dev might already be in the list.
- */
- QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
- other = DO_UPCAST(VIOsPAPRDevice, qdev, kid->child);
-
- if (other != dev && other->reg == dev->reg) {
- return other;
- }
- }
-
- return 0;
-}
-
-static void spapr_vio_busdev_reset(DeviceState *qdev)
-{
- VIOsPAPRDevice *dev = DO_UPCAST(VIOsPAPRDevice, qdev, qdev);
- VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev);
-
- /* Shut down the request queue and TCEs if necessary */
- spapr_vio_quiesce_one(dev);
-
- dev->signal_state = 0;
-
- if (pc->reset) {
- pc->reset(dev);
- }
-}
-
-static int spapr_vio_busdev_init(DeviceState *qdev)
-{
- VIOsPAPRDevice *dev = (VIOsPAPRDevice *)qdev;
- VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev);
- char *id;
-
- if (dev->reg != -1) {
- /*
- * Explicitly assigned address, just verify that no-one else
- * is using it. other mechanism). We have to open code this
- * rather than using spapr_vio_find_by_reg() because sdev
- * itself is already in the list.
- */
- VIOsPAPRDevice *other = reg_conflict(dev);
-
- if (other) {
- fprintf(stderr, "vio: %s and %s devices conflict at address %#x\n",
- object_get_typename(OBJECT(qdev)),
- object_get_typename(OBJECT(&other->qdev)),
- dev->reg);
- return -1;
- }
- } else {
- /* Need to assign an address */
- VIOsPAPRBus *bus = DO_UPCAST(VIOsPAPRBus, bus, dev->qdev.parent_bus);
-
- do {
- dev->reg = bus->next_reg++;
- } while (reg_conflict(dev));
- }
-
- /* Don't overwrite ids assigned on the command line */
- if (!dev->qdev.id) {
- id = vio_format_dev_name(dev);
- dev->qdev.id = id;
- }
-
- dev->irq = spapr_allocate_msi(dev->irq);
- if (!dev->irq) {
- return -1;
- }
-
- if (pc->rtce_window_size) {
- uint32_t liobn = SPAPR_VIO_BASE_LIOBN | dev->reg;
- dev->dma = spapr_tce_new_dma_context(liobn, pc->rtce_window_size);
- }
-
- return pc->init(dev);
-}
-
-static target_ulong h_vio_signal(PowerPCCPU *cpu, sPAPREnvironment *spapr,
- target_ulong opcode,
- target_ulong *args)
-{
- target_ulong reg = args[0];
- target_ulong mode = args[1];
- VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
- VIOsPAPRDeviceClass *pc;
-
- if (!dev) {
- return H_PARAMETER;
- }
-
- pc = VIO_SPAPR_DEVICE_GET_CLASS(dev);
-
- if (mode & ~pc->signal_mask) {
- return H_PARAMETER;
- }
-
- dev->signal_state = mode;
-
- return H_SUCCESS;
-}
-
-VIOsPAPRBus *spapr_vio_bus_init(void)
-{
- VIOsPAPRBus *bus;
- BusState *qbus;
- DeviceState *dev;
-
- /* Create bridge device */
- dev = qdev_create(NULL, "spapr-vio-bridge");
- qdev_init_nofail(dev);
-
- /* Create bus on bridge device */
-
- qbus = qbus_create(TYPE_SPAPR_VIO_BUS, dev, "spapr-vio");
- bus = DO_UPCAST(VIOsPAPRBus, bus, qbus);
- bus->next_reg = 0x71000000;
-
- /* hcall-vio */
- spapr_register_hypercall(H_VIO_SIGNAL, h_vio_signal);
-
- /* hcall-crq */
- spapr_register_hypercall(H_REG_CRQ, h_reg_crq);
- spapr_register_hypercall(H_FREE_CRQ, h_free_crq);
- spapr_register_hypercall(H_SEND_CRQ, h_send_crq);
- spapr_register_hypercall(H_ENABLE_CRQ, h_enable_crq);
-
- /* RTAS calls */
- spapr_rtas_register("ibm,set-tce-bypass", rtas_set_tce_bypass);
- spapr_rtas_register("quiesce", rtas_quiesce);
-
- return bus;
-}
-
-/* Represents sPAPR hcall VIO devices */
-
-static int spapr_vio_bridge_init(SysBusDevice *dev)
-{
- /* nothing */
- return 0;
-}
-
-static void spapr_vio_bridge_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
- k->init = spapr_vio_bridge_init;
- dc->no_user = 1;
-}
-
-static const TypeInfo spapr_vio_bridge_info = {
- .name = "spapr-vio-bridge",
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(SysBusDevice),
- .class_init = spapr_vio_bridge_class_init,
-};
-
-static void vio_spapr_device_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *k = DEVICE_CLASS(klass);
- k->init = spapr_vio_busdev_init;
- k->reset = spapr_vio_busdev_reset;
- k->bus_type = TYPE_SPAPR_VIO_BUS;
- k->props = spapr_vio_props;
-}
-
-static const TypeInfo spapr_vio_type_info = {
- .name = TYPE_VIO_SPAPR_DEVICE,
- .parent = TYPE_DEVICE,
- .instance_size = sizeof(VIOsPAPRDevice),
- .abstract = true,
- .class_size = sizeof(VIOsPAPRDeviceClass),
- .class_init = vio_spapr_device_class_init,
-};
-
-static void spapr_vio_register_types(void)
-{
- type_register_static(&spapr_vio_bus_info);
- type_register_static(&spapr_vio_bridge_info);
- type_register_static(&spapr_vio_type_info);
-}
-
-type_init(spapr_vio_register_types)
-
-#ifdef CONFIG_FDT
-static int compare_reg(const void *p1, const void *p2)
-{
- VIOsPAPRDevice const *dev1, *dev2;
-
- dev1 = (VIOsPAPRDevice *)*(DeviceState **)p1;
- dev2 = (VIOsPAPRDevice *)*(DeviceState **)p2;
-
- if (dev1->reg < dev2->reg) {
- return -1;
- }
- if (dev1->reg == dev2->reg) {
- return 0;
- }
-
- /* dev1->reg > dev2->reg */
- return 1;
-}
-
-int spapr_populate_vdevice(VIOsPAPRBus *bus, void *fdt)
-{
- DeviceState *qdev, **qdevs;
- BusChild *kid;
- int i, num, ret = 0;
-
- /* Count qdevs on the bus list */
- num = 0;
- QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
- num++;
- }
-
- /* Copy out into an array of pointers */
- qdevs = g_malloc(sizeof(qdev) * num);
- num = 0;
- QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
- qdevs[num++] = kid->child;
- }
-
- /* Sort the array */
- qsort(qdevs, num, sizeof(qdev), compare_reg);
-
- /* Hack alert. Give the devices to libfdt in reverse order, we happen
- * to know that will mean they are in forward order in the tree. */
- for (i = num - 1; i >= 0; i--) {
- VIOsPAPRDevice *dev = (VIOsPAPRDevice *)(qdevs[i]);
-
- ret = vio_make_devnode(dev, fdt);
-
- if (ret < 0) {
- goto out;
- }
- }
-
- ret = 0;
-out:
- free(qdevs);
-
- return ret;
-}
-
-int spapr_populate_chosen_stdout(void *fdt, VIOsPAPRBus *bus)
-{
- VIOsPAPRDevice *dev;
- char *name, *path;
- int ret, offset;
-
- dev = spapr_vty_get_default(bus);
- if (!dev)
- return 0;
-
- offset = fdt_path_offset(fdt, "/chosen");
- if (offset < 0) {
- return offset;
- }
-
- name = vio_format_dev_name(dev);
- path = g_strdup_printf("/vdevice/%s", name);
-
- ret = fdt_setprop_string(fdt, offset, "linux,stdout-path", path);
-
- g_free(name);
- g_free(path);
-
- return ret;
-}
-#endif /* CONFIG_FDT */
+++ /dev/null
-/*
- * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
- *
- * PAPR Virtualized Interrupt System, aka ICS/ICP aka xics
- *
- * Copyright (c) 2010,2011 David Gibson, IBM Corporation.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- */
-
-#include "hw/hw.h"
-#include "trace.h"
-#include "hw/spapr.h"
-#include "hw/xics.h"
-
-/*
- * ICP: Presentation layer
- */
-
-struct icp_server_state {
- uint32_t xirr;
- uint8_t pending_priority;
- uint8_t mfrr;
- qemu_irq output;
-};
-
-#define XISR_MASK 0x00ffffff
-#define CPPR_MASK 0xff000000
-
-#define XISR(ss) (((ss)->xirr) & XISR_MASK)
-#define CPPR(ss) (((ss)->xirr) >> 24)
-
-struct ics_state;
-
-struct icp_state {
- long nr_servers;
- struct icp_server_state *ss;
- struct ics_state *ics;
-};
-
-static void ics_reject(struct ics_state *ics, int nr);
-static void ics_resend(struct ics_state *ics);
-static void ics_eoi(struct ics_state *ics, int nr);
-
-static void icp_check_ipi(struct icp_state *icp, int server)
-{
- struct icp_server_state *ss = icp->ss + server;
-
- if (XISR(ss) && (ss->pending_priority <= ss->mfrr)) {
- return;
- }
-
- trace_xics_icp_check_ipi(server, ss->mfrr);
-
- if (XISR(ss)) {
- ics_reject(icp->ics, XISR(ss));
- }
-
- ss->xirr = (ss->xirr & ~XISR_MASK) | XICS_IPI;
- ss->pending_priority = ss->mfrr;
- qemu_irq_raise(ss->output);
-}
-
-static void icp_resend(struct icp_state *icp, int server)
-{
- struct icp_server_state *ss = icp->ss + server;
-
- if (ss->mfrr < CPPR(ss)) {
- icp_check_ipi(icp, server);
- }
- ics_resend(icp->ics);
-}
-
-static void icp_set_cppr(struct icp_state *icp, int server, uint8_t cppr)
-{
- struct icp_server_state *ss = icp->ss + server;
- uint8_t old_cppr;
- uint32_t old_xisr;
-
- old_cppr = CPPR(ss);
- ss->xirr = (ss->xirr & ~CPPR_MASK) | (cppr << 24);
-
- if (cppr < old_cppr) {
- if (XISR(ss) && (cppr <= ss->pending_priority)) {
- old_xisr = XISR(ss);
- ss->xirr &= ~XISR_MASK; /* Clear XISR */
- qemu_irq_lower(ss->output);
- ics_reject(icp->ics, old_xisr);
- }
- } else {
- if (!XISR(ss)) {
- icp_resend(icp, server);
- }
- }
-}
-
-static void icp_set_mfrr(struct icp_state *icp, int server, uint8_t mfrr)
-{
- struct icp_server_state *ss = icp->ss + server;
-
- ss->mfrr = mfrr;
- if (mfrr < CPPR(ss)) {
- icp_check_ipi(icp, server);
- }
-}
-
-static uint32_t icp_accept(struct icp_server_state *ss)
-{
- uint32_t xirr = ss->xirr;
-
- qemu_irq_lower(ss->output);
- ss->xirr = ss->pending_priority << 24;
-
- trace_xics_icp_accept(xirr, ss->xirr);
-
- return xirr;
-}
-
-static void icp_eoi(struct icp_state *icp, int server, uint32_t xirr)
-{
- struct icp_server_state *ss = icp->ss + server;
-
- /* Send EOI -> ICS */
- ss->xirr = (ss->xirr & ~CPPR_MASK) | (xirr & CPPR_MASK);
- trace_xics_icp_eoi(server, xirr, ss->xirr);
- ics_eoi(icp->ics, xirr & XISR_MASK);
- if (!XISR(ss)) {
- icp_resend(icp, server);
- }
-}
-
-static void icp_irq(struct icp_state *icp, int server, int nr, uint8_t priority)
-{
- struct icp_server_state *ss = icp->ss + server;
-
- trace_xics_icp_irq(server, nr, priority);
-
- if ((priority >= CPPR(ss))
- || (XISR(ss) && (ss->pending_priority <= priority))) {
- ics_reject(icp->ics, nr);
- } else {
- if (XISR(ss)) {
- ics_reject(icp->ics, XISR(ss));
- }
- ss->xirr = (ss->xirr & ~XISR_MASK) | (nr & XISR_MASK);
- ss->pending_priority = priority;
- trace_xics_icp_raise(ss->xirr, ss->pending_priority);
- qemu_irq_raise(ss->output);
- }
-}
-
-/*
- * ICS: Source layer
- */
-
-struct ics_irq_state {
- int server;
- uint8_t priority;
- uint8_t saved_priority;
-#define XICS_STATUS_ASSERTED 0x1
-#define XICS_STATUS_SENT 0x2
-#define XICS_STATUS_REJECTED 0x4
-#define XICS_STATUS_MASKED_PENDING 0x8
- uint8_t status;
-};
-
-struct ics_state {
- int nr_irqs;
- int offset;
- qemu_irq *qirqs;
- bool *islsi;
- struct ics_irq_state *irqs;
- struct icp_state *icp;
-};
-
-static int ics_valid_irq(struct ics_state *ics, uint32_t nr)
-{
- return (nr >= ics->offset)
- && (nr < (ics->offset + ics->nr_irqs));
-}
-
-static void resend_msi(struct ics_state *ics, int srcno)
-{
- struct ics_irq_state *irq = ics->irqs + srcno;
-
- /* FIXME: filter by server#? */
- if (irq->status & XICS_STATUS_REJECTED) {
- irq->status &= ~XICS_STATUS_REJECTED;
- if (irq->priority != 0xff) {
- icp_irq(ics->icp, irq->server, srcno + ics->offset,
- irq->priority);
- }
- }
-}
-
-static void resend_lsi(struct ics_state *ics, int srcno)
-{
- struct ics_irq_state *irq = ics->irqs + srcno;
-
- if ((irq->priority != 0xff)
- && (irq->status & XICS_STATUS_ASSERTED)
- && !(irq->status & XICS_STATUS_SENT)) {
- irq->status |= XICS_STATUS_SENT;
- icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority);
- }
-}
-
-static void set_irq_msi(struct ics_state *ics, int srcno, int val)
-{
- struct ics_irq_state *irq = ics->irqs + srcno;
-
- trace_xics_set_irq_msi(srcno, srcno + ics->offset);
-
- if (val) {
- if (irq->priority == 0xff) {
- irq->status |= XICS_STATUS_MASKED_PENDING;
- trace_xics_masked_pending();
- } else {
- icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority);
- }
- }
-}
-
-static void set_irq_lsi(struct ics_state *ics, int srcno, int val)
-{
- struct ics_irq_state *irq = ics->irqs + srcno;
-
- trace_xics_set_irq_lsi(srcno, srcno + ics->offset);
- if (val) {
- irq->status |= XICS_STATUS_ASSERTED;
- } else {
- irq->status &= ~XICS_STATUS_ASSERTED;
- }
- resend_lsi(ics, srcno);
-}
-
-static void ics_set_irq(void *opaque, int srcno, int val)
-{
- struct ics_state *ics = (struct ics_state *)opaque;
-
- if (ics->islsi[srcno]) {
- set_irq_lsi(ics, srcno, val);
- } else {
- set_irq_msi(ics, srcno, val);
- }
-}
-
-static void write_xive_msi(struct ics_state *ics, int srcno)
-{
- struct ics_irq_state *irq = ics->irqs + srcno;
-
- if (!(irq->status & XICS_STATUS_MASKED_PENDING)
- || (irq->priority == 0xff)) {
- return;
- }
-
- irq->status &= ~XICS_STATUS_MASKED_PENDING;
- icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority);
-}
-
-static void write_xive_lsi(struct ics_state *ics, int srcno)
-{
- resend_lsi(ics, srcno);
-}
-
-static void ics_write_xive(struct ics_state *ics, int nr, int server,
- uint8_t priority, uint8_t saved_priority)
-{
- int srcno = nr - ics->offset;
- struct ics_irq_state *irq = ics->irqs + srcno;
-
- irq->server = server;
- irq->priority = priority;
- irq->saved_priority = saved_priority;
-
- trace_xics_ics_write_xive(nr, srcno, server, priority);
-
- if (ics->islsi[srcno]) {
- write_xive_lsi(ics, srcno);
- } else {
- write_xive_msi(ics, srcno);
- }
-}
-
-static void ics_reject(struct ics_state *ics, int nr)
-{
- struct ics_irq_state *irq = ics->irqs + nr - ics->offset;
-
- trace_xics_ics_reject(nr, nr - ics->offset);
- irq->status |= XICS_STATUS_REJECTED; /* Irrelevant but harmless for LSI */
- irq->status &= ~XICS_STATUS_SENT; /* Irrelevant but harmless for MSI */
-}
-
-static void ics_resend(struct ics_state *ics)
-{
- int i;
-
- for (i = 0; i < ics->nr_irqs; i++) {
- /* FIXME: filter by server#? */
- if (ics->islsi[i]) {
- resend_lsi(ics, i);
- } else {
- resend_msi(ics, i);
- }
- }
-}
-
-static void ics_eoi(struct ics_state *ics, int nr)
-{
- int srcno = nr - ics->offset;
- struct ics_irq_state *irq = ics->irqs + srcno;
-
- trace_xics_ics_eoi(nr);
-
- if (ics->islsi[srcno]) {
- irq->status &= ~XICS_STATUS_SENT;
- }
-}
-
-/*
- * Exported functions
- */
-
-qemu_irq xics_get_qirq(struct icp_state *icp, int irq)
-{
- if (!ics_valid_irq(icp->ics, irq)) {
- return NULL;
- }
-
- return icp->ics->qirqs[irq - icp->ics->offset];
-}
-
-void xics_set_irq_type(struct icp_state *icp, int irq, bool lsi)
-{
- assert(ics_valid_irq(icp->ics, irq));
-
- icp->ics->islsi[irq - icp->ics->offset] = lsi;
-}
-
-static target_ulong h_cppr(PowerPCCPU *cpu, sPAPREnvironment *spapr,
- target_ulong opcode, target_ulong *args)
-{
- CPUState *cs = CPU(cpu);
- target_ulong cppr = args[0];
-
- icp_set_cppr(spapr->icp, cs->cpu_index, cppr);
- return H_SUCCESS;
-}
-
-static target_ulong h_ipi(PowerPCCPU *cpu, sPAPREnvironment *spapr,
- target_ulong opcode, target_ulong *args)
-{
- target_ulong server = args[0];
- target_ulong mfrr = args[1];
-
- if (server >= spapr->icp->nr_servers) {
- return H_PARAMETER;
- }
-
- icp_set_mfrr(spapr->icp, server, mfrr);
- return H_SUCCESS;
-}
-
-static target_ulong h_xirr(PowerPCCPU *cpu, sPAPREnvironment *spapr,
- target_ulong opcode, target_ulong *args)
-{
- CPUState *cs = CPU(cpu);
- uint32_t xirr = icp_accept(spapr->icp->ss + cs->cpu_index);
-
- args[0] = xirr;
- return H_SUCCESS;
-}
-
-static target_ulong h_eoi(PowerPCCPU *cpu, sPAPREnvironment *spapr,
- target_ulong opcode, target_ulong *args)
-{
- CPUState *cs = CPU(cpu);
- target_ulong xirr = args[0];
-
- icp_eoi(spapr->icp, cs->cpu_index, xirr);
- return H_SUCCESS;
-}
-
-static void rtas_set_xive(sPAPREnvironment *spapr, uint32_t token,
- uint32_t nargs, target_ulong args,
- uint32_t nret, target_ulong rets)
-{
- struct ics_state *ics = spapr->icp->ics;
- uint32_t nr, server, priority;
-
- if ((nargs != 3) || (nret != 1)) {
- rtas_st(rets, 0, -3);
- return;
- }
-
- nr = rtas_ld(args, 0);
- server = rtas_ld(args, 1);
- priority = rtas_ld(args, 2);
-
- if (!ics_valid_irq(ics, nr) || (server >= ics->icp->nr_servers)
- || (priority > 0xff)) {
- rtas_st(rets, 0, -3);
- return;
- }
-
- ics_write_xive(ics, nr, server, priority, priority);
-
- rtas_st(rets, 0, 0); /* Success */
-}
-
-static void rtas_get_xive(sPAPREnvironment *spapr, uint32_t token,
- uint32_t nargs, target_ulong args,
- uint32_t nret, target_ulong rets)
-{
- struct ics_state *ics = spapr->icp->ics;
- uint32_t nr;
-
- if ((nargs != 1) || (nret != 3)) {
- rtas_st(rets, 0, -3);
- return;
- }
-
- nr = rtas_ld(args, 0);
-
- if (!ics_valid_irq(ics, nr)) {
- rtas_st(rets, 0, -3);
- return;
- }
-
- rtas_st(rets, 0, 0); /* Success */
- rtas_st(rets, 1, ics->irqs[nr - ics->offset].server);
- rtas_st(rets, 2, ics->irqs[nr - ics->offset].priority);
-}
-
-static void rtas_int_off(sPAPREnvironment *spapr, uint32_t token,
- uint32_t nargs, target_ulong args,
- uint32_t nret, target_ulong rets)
-{
- struct ics_state *ics = spapr->icp->ics;
- uint32_t nr;
-
- if ((nargs != 1) || (nret != 1)) {
- rtas_st(rets, 0, -3);
- return;
- }
-
- nr = rtas_ld(args, 0);
-
- if (!ics_valid_irq(ics, nr)) {
- rtas_st(rets, 0, -3);
- return;
- }
-
- ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server, 0xff,
- ics->irqs[nr - ics->offset].priority);
-
- rtas_st(rets, 0, 0); /* Success */
-}
-
-static void rtas_int_on(sPAPREnvironment *spapr, uint32_t token,
- uint32_t nargs, target_ulong args,
- uint32_t nret, target_ulong rets)
-{
- struct ics_state *ics = spapr->icp->ics;
- uint32_t nr;
-
- if ((nargs != 1) || (nret != 1)) {
- rtas_st(rets, 0, -3);
- return;
- }
-
- nr = rtas_ld(args, 0);
-
- if (!ics_valid_irq(ics, nr)) {
- rtas_st(rets, 0, -3);
- return;
- }
-
- ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server,
- ics->irqs[nr - ics->offset].saved_priority,
- ics->irqs[nr - ics->offset].saved_priority);
-
- rtas_st(rets, 0, 0); /* Success */
-}
-
-static void xics_reset(void *opaque)
-{
- struct icp_state *icp = (struct icp_state *)opaque;
- struct ics_state *ics = icp->ics;
- int i;
-
- for (i = 0; i < icp->nr_servers; i++) {
- icp->ss[i].xirr = 0;
- icp->ss[i].pending_priority = 0xff;
- icp->ss[i].mfrr = 0xff;
- /* Make all outputs are deasserted */
- qemu_set_irq(icp->ss[i].output, 0);
- }
-
- memset(ics->irqs, 0, sizeof(struct ics_irq_state) * ics->nr_irqs);
- for (i = 0; i < ics->nr_irqs; i++) {
- ics->irqs[i].priority = 0xff;
- ics->irqs[i].saved_priority = 0xff;
- }
-}
-
-struct icp_state *xics_system_init(int nr_irqs)
-{
- CPUPPCState *env;
- CPUState *cpu;
- int max_server_num;
- struct icp_state *icp;
- struct ics_state *ics;
-
- max_server_num = -1;
- for (env = first_cpu; env != NULL; env = env->next_cpu) {
- cpu = CPU(ppc_env_get_cpu(env));
- if (cpu->cpu_index > max_server_num) {
- max_server_num = cpu->cpu_index;
- }
- }
-
- icp = g_malloc0(sizeof(*icp));
- icp->nr_servers = max_server_num + 1;
- icp->ss = g_malloc0(icp->nr_servers*sizeof(struct icp_server_state));
-
- for (env = first_cpu; env != NULL; env = env->next_cpu) {
- cpu = CPU(ppc_env_get_cpu(env));
- struct icp_server_state *ss = &icp->ss[cpu->cpu_index];
-
- switch (PPC_INPUT(env)) {
- case PPC_FLAGS_INPUT_POWER7:
- ss->output = env->irq_inputs[POWER7_INPUT_INT];
- break;
-
- case PPC_FLAGS_INPUT_970:
- ss->output = env->irq_inputs[PPC970_INPUT_INT];
- break;
-
- default:
- hw_error("XICS interrupt model does not support this CPU bus "
- "model\n");
- exit(1);
- }
- }
-
- ics = g_malloc0(sizeof(*ics));
- ics->nr_irqs = nr_irqs;
- ics->offset = XICS_IRQ_BASE;
- ics->irqs = g_malloc0(nr_irqs * sizeof(struct ics_irq_state));
- ics->islsi = g_malloc0(nr_irqs * sizeof(bool));
-
- icp->ics = ics;
- ics->icp = icp;
-
- ics->qirqs = qemu_allocate_irqs(ics_set_irq, ics, nr_irqs);
-
- spapr_register_hypercall(H_CPPR, h_cppr);
- spapr_register_hypercall(H_IPI, h_ipi);
- spapr_register_hypercall(H_XIRR, h_xirr);
- spapr_register_hypercall(H_EOI, h_eoi);
-
- spapr_rtas_register("ibm,set-xive", rtas_set_xive);
- spapr_rtas_register("ibm,get-xive", rtas_get_xive);
- spapr_rtas_register("ibm,int-off", rtas_int_off);
- spapr_rtas_register("ibm,int-on", rtas_int_on);
-
- qemu_register_reset(xics_reset, icp);
-
- return icp;
-}