mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-06 01:03:55 -06:00
ppc: move files referencing CPU to hw/ppc/
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
9743b581a8
commit
c68c4a56e9
6 changed files with 6 additions and 7 deletions
|
@ -1,14 +1,12 @@
|
|||
# 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
|
||||
|
||||
|
@ -20,9 +18,9 @@ obj-y := $(addprefix ../,$(obj-y))
|
|||
# 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
|
||||
|
@ -31,5 +29,6 @@ obj-y += mac_oldworld.o
|
|||
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
|
||||
|
|
143
hw/ppc/mpc8544_guts.c
Normal file
143
hw/ppc/mpc8544_guts.c
Normal file
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* 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)
|
721
hw/ppc/ppc4xx_devs.c
Normal file
721
hw/ppc/ppc4xx_devs.c
Normal file
|
@ -0,0 +1,721 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
222
hw/ppc/ppce500_spin.c
Normal file
222
hw/ppc/ppce500_spin.c
Normal file
|
@ -0,0 +1,222 @@
|
|||
/*
|
||||
* 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)
|
649
hw/ppc/spapr_vio.c
Normal file
649
hw/ppc/spapr_vio.c
Normal file
|
@ -0,0 +1,649 @@
|
|||
/*
|
||||
* 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 */
|
588
hw/ppc/xics.c
Normal file
588
hw/ppc/xics.c
Normal file
|
@ -0,0 +1,588 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue