2011-08-26 05:38:59 +08:00
|
|
|
/*
|
|
|
|
* DEC 21272 (TSUNAMI/TYPHOON) chipset emulation.
|
|
|
|
*
|
|
|
|
* Written by Richard Henderson.
|
|
|
|
*
|
|
|
|
* This work is licensed under the GNU GPL license version 2 or later.
|
|
|
|
*/
|
|
|
|
|
2016-01-27 02:17:04 +08:00
|
|
|
#include "qemu/osdep.h"
|
include/qemu/osdep.h: Don't include qapi/error.h
Commit 57cb38b included qapi/error.h into qemu/osdep.h to get the
Error typedef. Since then, we've moved to include qemu/osdep.h
everywhere. Its file comment explains: "To avoid getting into
possible circular include dependencies, this file should not include
any other QEMU headers, with the exceptions of config-host.h,
compiler.h, os-posix.h and os-win32.h, all of which are doing a
similar job to this file and are under similar constraints."
qapi/error.h doesn't do a similar job, and it doesn't adhere to
similar constraints: it includes qapi-types.h. That's in excess of
100KiB of crap most .c files don't actually need.
Add the typedef to qemu/typedefs.h, and include that instead of
qapi/error.h. Include qapi/error.h in .c files that need it and don't
get it now. Include qapi-types.h in qom/object.h for uint16List.
Update scripts/clean-includes accordingly. Update it further to match
reality: replace config.h by config-target.h, add sysemu/os-posix.h,
sysemu/os-win32.h. Update the list of includes in the qemu/osdep.h
comment quoted above similarly.
This reduces the number of objects depending on qapi/error.h from "all
of them" to less than a third. Unfortunately, the number depending on
qapi-types.h shrinks only a little. More work is needed for that one.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
[Fix compilation without the spice devel packages. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2016-03-14 16:01:28 +08:00
|
|
|
#include "qapi/error.h"
|
2011-08-26 05:38:59 +08:00
|
|
|
#include "cpu.h"
|
2013-02-04 22:40:22 +08:00
|
|
|
#include "hw/hw.h"
|
2013-04-09 22:26:55 +08:00
|
|
|
#include "hw/devices.h"
|
2012-12-18 01:20:04 +08:00
|
|
|
#include "sysemu/sysemu.h"
|
2013-03-19 00:36:02 +08:00
|
|
|
#include "alpha_sys.h"
|
2012-12-18 01:19:49 +08:00
|
|
|
#include "exec/address-spaces.h"
|
2011-08-26 05:38:59 +08:00
|
|
|
|
|
|
|
|
2012-08-21 01:07:57 +08:00
|
|
|
#define TYPE_TYPHOON_PCI_HOST_BRIDGE "typhoon-pcihost"
|
|
|
|
|
2011-08-26 05:38:59 +08:00
|
|
|
typedef struct TyphoonCchip {
|
|
|
|
MemoryRegion region;
|
|
|
|
uint64_t misc;
|
|
|
|
uint64_t drir;
|
|
|
|
uint64_t dim[4];
|
|
|
|
uint32_t iic[4];
|
2012-10-16 08:45:53 +08:00
|
|
|
AlphaCPU *cpu[4];
|
2011-08-26 05:38:59 +08:00
|
|
|
} TyphoonCchip;
|
|
|
|
|
|
|
|
typedef struct TyphoonWindow {
|
2013-08-05 09:27:13 +08:00
|
|
|
uint64_t wba;
|
|
|
|
uint64_t wsm;
|
|
|
|
uint64_t tba;
|
2011-08-26 05:38:59 +08:00
|
|
|
} TyphoonWindow;
|
|
|
|
|
|
|
|
typedef struct TyphoonPchip {
|
|
|
|
MemoryRegion region;
|
|
|
|
MemoryRegion reg_iack;
|
|
|
|
MemoryRegion reg_mem;
|
|
|
|
MemoryRegion reg_io;
|
|
|
|
MemoryRegion reg_conf;
|
2013-08-05 09:27:13 +08:00
|
|
|
|
|
|
|
AddressSpace iommu_as;
|
|
|
|
MemoryRegion iommu;
|
|
|
|
|
2011-08-26 05:38:59 +08:00
|
|
|
uint64_t ctl;
|
|
|
|
TyphoonWindow win[4];
|
|
|
|
} TyphoonPchip;
|
|
|
|
|
2012-08-21 01:07:57 +08:00
|
|
|
#define TYPHOON_PCI_HOST_BRIDGE(obj) \
|
|
|
|
OBJECT_CHECK(TyphoonState, (obj), TYPE_TYPHOON_PCI_HOST_BRIDGE)
|
|
|
|
|
2011-08-26 05:38:59 +08:00
|
|
|
typedef struct TyphoonState {
|
2012-08-21 01:08:09 +08:00
|
|
|
PCIHostState parent_obj;
|
2012-08-21 01:07:57 +08:00
|
|
|
|
2011-08-26 05:38:59 +08:00
|
|
|
TyphoonCchip cchip;
|
|
|
|
TyphoonPchip pchip;
|
|
|
|
MemoryRegion dchip_region;
|
|
|
|
MemoryRegion ram_region;
|
|
|
|
} TyphoonState;
|
|
|
|
|
|
|
|
/* Called when one of DRIR or DIM changes. */
|
2012-10-16 08:45:53 +08:00
|
|
|
static void cpu_irq_change(AlphaCPU *cpu, uint64_t req)
|
2011-08-26 05:38:59 +08:00
|
|
|
{
|
|
|
|
/* If there are any non-masked interrupts, tell the cpu. */
|
2012-10-16 08:45:53 +08:00
|
|
|
if (cpu != NULL) {
|
2013-01-18 05:30:20 +08:00
|
|
|
CPUState *cs = CPU(cpu);
|
2011-08-26 05:38:59 +08:00
|
|
|
if (req) {
|
2013-01-18 22:03:43 +08:00
|
|
|
cpu_interrupt(cs, CPU_INTERRUPT_HARD);
|
2011-08-26 05:38:59 +08:00
|
|
|
} else {
|
2013-01-18 05:30:20 +08:00
|
|
|
cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
|
2011-08-26 05:38:59 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-23 18:30:10 +08:00
|
|
|
static uint64_t cchip_read(void *opaque, hwaddr addr, unsigned size)
|
2011-08-26 05:38:59 +08:00
|
|
|
{
|
2013-05-27 11:17:50 +08:00
|
|
|
CPUState *cpu = current_cpu;
|
2011-08-26 05:38:59 +08:00
|
|
|
TyphoonState *s = opaque;
|
|
|
|
uint64_t ret = 0;
|
|
|
|
|
|
|
|
switch (addr) {
|
|
|
|
case 0x0000:
|
|
|
|
/* CSC: Cchip System Configuration Register. */
|
|
|
|
/* All sorts of data here; probably the only thing relevant is
|
|
|
|
PIP<14> Pchip 1 Present = 0. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0040:
|
|
|
|
/* MTR: Memory Timing Register. */
|
|
|
|
/* All sorts of stuff related to real DRAM. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0080:
|
|
|
|
/* MISC: Miscellaneous Register. */
|
2012-12-17 13:18:02 +08:00
|
|
|
ret = s->cchip.misc | (cpu->cpu_index & 3);
|
2011-08-26 05:38:59 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x00c0:
|
|
|
|
/* MPD: Memory Presence Detect Register. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0100: /* AAR0 */
|
|
|
|
case 0x0140: /* AAR1 */
|
|
|
|
case 0x0180: /* AAR2 */
|
|
|
|
case 0x01c0: /* AAR3 */
|
|
|
|
/* AAR: Array Address Register. */
|
|
|
|
/* All sorts of information about DRAM. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0200:
|
|
|
|
/* DIM0: Device Interrupt Mask Register, CPU0. */
|
|
|
|
ret = s->cchip.dim[0];
|
|
|
|
break;
|
|
|
|
case 0x0240:
|
|
|
|
/* DIM1: Device Interrupt Mask Register, CPU1. */
|
|
|
|
ret = s->cchip.dim[1];
|
|
|
|
break;
|
|
|
|
case 0x0280:
|
|
|
|
/* DIR0: Device Interrupt Request Register, CPU0. */
|
|
|
|
ret = s->cchip.dim[0] & s->cchip.drir;
|
|
|
|
break;
|
|
|
|
case 0x02c0:
|
|
|
|
/* DIR1: Device Interrupt Request Register, CPU1. */
|
|
|
|
ret = s->cchip.dim[1] & s->cchip.drir;
|
|
|
|
break;
|
|
|
|
case 0x0300:
|
|
|
|
/* DRIR: Device Raw Interrupt Request Register. */
|
|
|
|
ret = s->cchip.drir;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0340:
|
|
|
|
/* PRBEN: Probe Enable Register. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0380:
|
|
|
|
/* IIC0: Interval Ignore Count Register, CPU0. */
|
|
|
|
ret = s->cchip.iic[0];
|
|
|
|
break;
|
|
|
|
case 0x03c0:
|
|
|
|
/* IIC1: Interval Ignore Count Register, CPU1. */
|
|
|
|
ret = s->cchip.iic[1];
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0400: /* MPR0 */
|
|
|
|
case 0x0440: /* MPR1 */
|
|
|
|
case 0x0480: /* MPR2 */
|
|
|
|
case 0x04c0: /* MPR3 */
|
|
|
|
/* MPR: Memory Programming Register. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0580:
|
|
|
|
/* TTR: TIGbus Timing Register. */
|
|
|
|
/* All sorts of stuff related to interrupt delivery timings. */
|
|
|
|
break;
|
|
|
|
case 0x05c0:
|
|
|
|
/* TDR: TIGbug Device Timing Register. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0600:
|
|
|
|
/* DIM2: Device Interrupt Mask Register, CPU2. */
|
|
|
|
ret = s->cchip.dim[2];
|
|
|
|
break;
|
|
|
|
case 0x0640:
|
|
|
|
/* DIM3: Device Interrupt Mask Register, CPU3. */
|
|
|
|
ret = s->cchip.dim[3];
|
|
|
|
break;
|
|
|
|
case 0x0680:
|
|
|
|
/* DIR2: Device Interrupt Request Register, CPU2. */
|
|
|
|
ret = s->cchip.dim[2] & s->cchip.drir;
|
|
|
|
break;
|
|
|
|
case 0x06c0:
|
|
|
|
/* DIR3: Device Interrupt Request Register, CPU3. */
|
|
|
|
ret = s->cchip.dim[3] & s->cchip.drir;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0700:
|
|
|
|
/* IIC2: Interval Ignore Count Register, CPU2. */
|
|
|
|
ret = s->cchip.iic[2];
|
|
|
|
break;
|
|
|
|
case 0x0740:
|
|
|
|
/* IIC3: Interval Ignore Count Register, CPU3. */
|
|
|
|
ret = s->cchip.iic[3];
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0780:
|
|
|
|
/* PWR: Power Management Control. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0c00: /* CMONCTLA */
|
|
|
|
case 0x0c40: /* CMONCTLB */
|
|
|
|
case 0x0c80: /* CMONCNT01 */
|
|
|
|
case 0x0cc0: /* CMONCNT23 */
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2013-05-27 12:49:53 +08:00
|
|
|
cpu_unassigned_access(cpu, addr, false, false, 0, size);
|
2011-08-26 05:38:59 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-10-23 18:30:10 +08:00
|
|
|
static uint64_t dchip_read(void *opaque, hwaddr addr, unsigned size)
|
2011-08-26 05:38:59 +08:00
|
|
|
{
|
|
|
|
/* Skip this. It's all related to DRAM timing and setup. */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-10-23 18:30:10 +08:00
|
|
|
static uint64_t pchip_read(void *opaque, hwaddr addr, unsigned size)
|
2011-08-26 05:38:59 +08:00
|
|
|
{
|
|
|
|
TyphoonState *s = opaque;
|
|
|
|
uint64_t ret = 0;
|
|
|
|
|
|
|
|
switch (addr) {
|
|
|
|
case 0x0000:
|
|
|
|
/* WSBA0: Window Space Base Address Register. */
|
2013-08-05 09:27:13 +08:00
|
|
|
ret = s->pchip.win[0].wba;
|
2011-08-26 05:38:59 +08:00
|
|
|
break;
|
|
|
|
case 0x0040:
|
|
|
|
/* WSBA1 */
|
2013-08-05 09:27:13 +08:00
|
|
|
ret = s->pchip.win[1].wba;
|
2011-08-26 05:38:59 +08:00
|
|
|
break;
|
|
|
|
case 0x0080:
|
|
|
|
/* WSBA2 */
|
2013-08-05 09:27:13 +08:00
|
|
|
ret = s->pchip.win[2].wba;
|
2011-08-26 05:38:59 +08:00
|
|
|
break;
|
|
|
|
case 0x00c0:
|
|
|
|
/* WSBA3 */
|
2013-08-05 09:27:13 +08:00
|
|
|
ret = s->pchip.win[3].wba;
|
2011-08-26 05:38:59 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0100:
|
|
|
|
/* WSM0: Window Space Mask Register. */
|
2013-08-05 09:27:13 +08:00
|
|
|
ret = s->pchip.win[0].wsm;
|
2011-08-26 05:38:59 +08:00
|
|
|
break;
|
|
|
|
case 0x0140:
|
|
|
|
/* WSM1 */
|
2013-08-05 09:27:13 +08:00
|
|
|
ret = s->pchip.win[1].wsm;
|
2011-08-26 05:38:59 +08:00
|
|
|
break;
|
|
|
|
case 0x0180:
|
|
|
|
/* WSM2 */
|
2013-08-05 09:27:13 +08:00
|
|
|
ret = s->pchip.win[2].wsm;
|
2011-08-26 05:38:59 +08:00
|
|
|
break;
|
|
|
|
case 0x01c0:
|
|
|
|
/* WSM3 */
|
2013-08-05 09:27:13 +08:00
|
|
|
ret = s->pchip.win[3].wsm;
|
2011-08-26 05:38:59 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0200:
|
|
|
|
/* TBA0: Translated Base Address Register. */
|
2013-08-05 09:27:13 +08:00
|
|
|
ret = s->pchip.win[0].tba;
|
2011-08-26 05:38:59 +08:00
|
|
|
break;
|
|
|
|
case 0x0240:
|
|
|
|
/* TBA1 */
|
2013-08-05 09:27:13 +08:00
|
|
|
ret = s->pchip.win[1].tba;
|
2011-08-26 05:38:59 +08:00
|
|
|
break;
|
|
|
|
case 0x0280:
|
|
|
|
/* TBA2 */
|
2013-08-05 09:27:13 +08:00
|
|
|
ret = s->pchip.win[2].tba;
|
2011-08-26 05:38:59 +08:00
|
|
|
break;
|
|
|
|
case 0x02c0:
|
|
|
|
/* TBA3 */
|
2013-08-05 09:27:13 +08:00
|
|
|
ret = s->pchip.win[3].tba;
|
2011-08-26 05:38:59 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0300:
|
|
|
|
/* PCTL: Pchip Control Register. */
|
|
|
|
ret = s->pchip.ctl;
|
|
|
|
break;
|
|
|
|
case 0x0340:
|
|
|
|
/* PLAT: Pchip Master Latency Register. */
|
|
|
|
break;
|
|
|
|
case 0x03c0:
|
|
|
|
/* PERROR: Pchip Error Register. */
|
|
|
|
break;
|
|
|
|
case 0x0400:
|
|
|
|
/* PERRMASK: Pchip Error Mask Register. */
|
|
|
|
break;
|
|
|
|
case 0x0440:
|
|
|
|
/* PERRSET: Pchip Error Set Register. */
|
|
|
|
break;
|
|
|
|
case 0x0480:
|
|
|
|
/* TLBIV: Translation Buffer Invalidate Virtual Register (WO). */
|
|
|
|
break;
|
|
|
|
case 0x04c0:
|
|
|
|
/* TLBIA: Translation Buffer Invalidate All Register (WO). */
|
|
|
|
break;
|
|
|
|
case 0x0500: /* PMONCTL */
|
|
|
|
case 0x0540: /* PMONCNT */
|
|
|
|
case 0x0800: /* SPRST */
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2013-05-27 11:17:50 +08:00
|
|
|
cpu_unassigned_access(current_cpu, addr, false, false, 0, size);
|
2011-08-26 05:38:59 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-10-23 18:30:10 +08:00
|
|
|
static void cchip_write(void *opaque, hwaddr addr,
|
2013-07-09 05:57:39 +08:00
|
|
|
uint64_t val, unsigned size)
|
2011-08-26 05:38:59 +08:00
|
|
|
{
|
|
|
|
TyphoonState *s = opaque;
|
2013-07-09 05:57:39 +08:00
|
|
|
uint64_t oldval, newval;
|
2011-08-26 05:38:59 +08:00
|
|
|
|
|
|
|
switch (addr) {
|
|
|
|
case 0x0000:
|
|
|
|
/* CSC: Cchip System Configuration Register. */
|
|
|
|
/* All sorts of data here; nothing relevant RW. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0040:
|
|
|
|
/* MTR: Memory Timing Register. */
|
|
|
|
/* All sorts of stuff related to real DRAM. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0080:
|
|
|
|
/* MISC: Miscellaneous Register. */
|
|
|
|
newval = oldval = s->cchip.misc;
|
|
|
|
newval &= ~(val & 0x10000ff0); /* W1C fields */
|
|
|
|
if (val & 0x100000) {
|
|
|
|
newval &= ~0xff0000ull; /* ACL clears ABT and ABW */
|
|
|
|
} else {
|
|
|
|
newval |= val & 0x00f00000; /* ABT field is W1S */
|
|
|
|
if ((newval & 0xf0000) == 0) {
|
|
|
|
newval |= val & 0xf0000; /* ABW field is W1S iff zero */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
newval |= (val & 0xf000) >> 4; /* IPREQ field sets IPINTR. */
|
|
|
|
|
|
|
|
newval &= ~0xf0000000000ull; /* WO and RW fields */
|
|
|
|
newval |= val & 0xf0000000000ull;
|
|
|
|
s->cchip.misc = newval;
|
|
|
|
|
|
|
|
/* Pass on changes to IPI and ITI state. */
|
|
|
|
if ((newval ^ oldval) & 0xff0) {
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < 4; ++i) {
|
2012-10-16 08:45:53 +08:00
|
|
|
AlphaCPU *cpu = s->cchip.cpu[i];
|
|
|
|
if (cpu != NULL) {
|
2013-01-18 05:30:20 +08:00
|
|
|
CPUState *cs = CPU(cpu);
|
2011-08-26 05:38:59 +08:00
|
|
|
/* IPI can be either cleared or set by the write. */
|
|
|
|
if (newval & (1 << (i + 8))) {
|
2013-01-18 22:03:43 +08:00
|
|
|
cpu_interrupt(cs, CPU_INTERRUPT_SMP);
|
2011-08-26 05:38:59 +08:00
|
|
|
} else {
|
2013-01-18 05:30:20 +08:00
|
|
|
cpu_reset_interrupt(cs, CPU_INTERRUPT_SMP);
|
2011-08-26 05:38:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ITI can only be cleared by the write. */
|
|
|
|
if ((newval & (1 << (i + 4))) == 0) {
|
2013-01-18 05:30:20 +08:00
|
|
|
cpu_reset_interrupt(cs, CPU_INTERRUPT_TIMER);
|
2011-08-26 05:38:59 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x00c0:
|
|
|
|
/* MPD: Memory Presence Detect Register. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0100: /* AAR0 */
|
|
|
|
case 0x0140: /* AAR1 */
|
|
|
|
case 0x0180: /* AAR2 */
|
|
|
|
case 0x01c0: /* AAR3 */
|
|
|
|
/* AAR: Array Address Register. */
|
|
|
|
/* All sorts of information about DRAM. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0200: /* DIM0 */
|
|
|
|
/* DIM: Device Interrupt Mask Register, CPU0. */
|
|
|
|
s->cchip.dim[0] = val;
|
|
|
|
cpu_irq_change(s->cchip.cpu[0], val & s->cchip.drir);
|
|
|
|
break;
|
|
|
|
case 0x0240: /* DIM1 */
|
|
|
|
/* DIM: Device Interrupt Mask Register, CPU1. */
|
|
|
|
s->cchip.dim[0] = val;
|
|
|
|
cpu_irq_change(s->cchip.cpu[1], val & s->cchip.drir);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0280: /* DIR0 (RO) */
|
|
|
|
case 0x02c0: /* DIR1 (RO) */
|
|
|
|
case 0x0300: /* DRIR (RO) */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0340:
|
|
|
|
/* PRBEN: Probe Enable Register. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0380: /* IIC0 */
|
|
|
|
s->cchip.iic[0] = val & 0xffffff;
|
|
|
|
break;
|
|
|
|
case 0x03c0: /* IIC1 */
|
|
|
|
s->cchip.iic[1] = val & 0xffffff;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0400: /* MPR0 */
|
|
|
|
case 0x0440: /* MPR1 */
|
|
|
|
case 0x0480: /* MPR2 */
|
|
|
|
case 0x04c0: /* MPR3 */
|
|
|
|
/* MPR: Memory Programming Register. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0580:
|
|
|
|
/* TTR: TIGbus Timing Register. */
|
|
|
|
/* All sorts of stuff related to interrupt delivery timings. */
|
|
|
|
break;
|
|
|
|
case 0x05c0:
|
|
|
|
/* TDR: TIGbug Device Timing Register. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0600:
|
|
|
|
/* DIM2: Device Interrupt Mask Register, CPU2. */
|
|
|
|
s->cchip.dim[2] = val;
|
|
|
|
cpu_irq_change(s->cchip.cpu[2], val & s->cchip.drir);
|
|
|
|
break;
|
|
|
|
case 0x0640:
|
|
|
|
/* DIM3: Device Interrupt Mask Register, CPU3. */
|
|
|
|
s->cchip.dim[3] = val;
|
|
|
|
cpu_irq_change(s->cchip.cpu[3], val & s->cchip.drir);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0680: /* DIR2 (RO) */
|
|
|
|
case 0x06c0: /* DIR3 (RO) */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0700: /* IIC2 */
|
|
|
|
s->cchip.iic[2] = val & 0xffffff;
|
|
|
|
break;
|
|
|
|
case 0x0740: /* IIC3 */
|
|
|
|
s->cchip.iic[3] = val & 0xffffff;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0780:
|
|
|
|
/* PWR: Power Management Control. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0c00: /* CMONCTLA */
|
|
|
|
case 0x0c40: /* CMONCTLB */
|
|
|
|
case 0x0c80: /* CMONCNT01 */
|
|
|
|
case 0x0cc0: /* CMONCNT23 */
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2013-05-27 11:17:50 +08:00
|
|
|
cpu_unassigned_access(current_cpu, addr, true, false, 0, size);
|
2011-08-26 05:38:59 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-23 18:30:10 +08:00
|
|
|
static void dchip_write(void *opaque, hwaddr addr,
|
2011-08-26 05:38:59 +08:00
|
|
|
uint64_t val, unsigned size)
|
|
|
|
{
|
|
|
|
/* Skip this. It's all related to DRAM timing and setup. */
|
|
|
|
}
|
|
|
|
|
2012-10-23 18:30:10 +08:00
|
|
|
static void pchip_write(void *opaque, hwaddr addr,
|
2013-07-09 05:57:39 +08:00
|
|
|
uint64_t val, unsigned size)
|
2011-08-26 05:38:59 +08:00
|
|
|
{
|
|
|
|
TyphoonState *s = opaque;
|
2013-07-09 05:57:39 +08:00
|
|
|
uint64_t oldval;
|
2011-08-26 05:38:59 +08:00
|
|
|
|
|
|
|
switch (addr) {
|
|
|
|
case 0x0000:
|
|
|
|
/* WSBA0: Window Space Base Address Register. */
|
2013-08-05 09:27:13 +08:00
|
|
|
s->pchip.win[0].wba = val & 0xfff00003u;
|
2011-08-26 05:38:59 +08:00
|
|
|
break;
|
|
|
|
case 0x0040:
|
|
|
|
/* WSBA1 */
|
2013-08-05 09:27:13 +08:00
|
|
|
s->pchip.win[1].wba = val & 0xfff00003u;
|
2011-08-26 05:38:59 +08:00
|
|
|
break;
|
|
|
|
case 0x0080:
|
|
|
|
/* WSBA2 */
|
2013-08-05 09:27:13 +08:00
|
|
|
s->pchip.win[2].wba = val & 0xfff00003u;
|
2011-08-26 05:38:59 +08:00
|
|
|
break;
|
|
|
|
case 0x00c0:
|
|
|
|
/* WSBA3 */
|
2013-08-05 09:27:13 +08:00
|
|
|
s->pchip.win[3].wba = (val & 0x80fff00001ull) | 2;
|
2011-08-26 05:38:59 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0100:
|
|
|
|
/* WSM0: Window Space Mask Register. */
|
2013-08-05 09:27:13 +08:00
|
|
|
s->pchip.win[0].wsm = val & 0xfff00000u;
|
2011-08-26 05:38:59 +08:00
|
|
|
break;
|
|
|
|
case 0x0140:
|
|
|
|
/* WSM1 */
|
2013-08-05 09:27:13 +08:00
|
|
|
s->pchip.win[1].wsm = val & 0xfff00000u;
|
2011-08-26 05:38:59 +08:00
|
|
|
break;
|
|
|
|
case 0x0180:
|
|
|
|
/* WSM2 */
|
2013-08-05 09:27:13 +08:00
|
|
|
s->pchip.win[2].wsm = val & 0xfff00000u;
|
2011-08-26 05:38:59 +08:00
|
|
|
break;
|
|
|
|
case 0x01c0:
|
|
|
|
/* WSM3 */
|
2013-08-05 09:27:13 +08:00
|
|
|
s->pchip.win[3].wsm = val & 0xfff00000u;
|
2011-08-26 05:38:59 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0200:
|
|
|
|
/* TBA0: Translated Base Address Register. */
|
2013-08-05 09:27:13 +08:00
|
|
|
s->pchip.win[0].tba = val & 0x7fffffc00ull;
|
2011-08-26 05:38:59 +08:00
|
|
|
break;
|
|
|
|
case 0x0240:
|
|
|
|
/* TBA1 */
|
2013-08-05 09:27:13 +08:00
|
|
|
s->pchip.win[1].tba = val & 0x7fffffc00ull;
|
2011-08-26 05:38:59 +08:00
|
|
|
break;
|
|
|
|
case 0x0280:
|
|
|
|
/* TBA2 */
|
2013-08-05 09:27:13 +08:00
|
|
|
s->pchip.win[2].tba = val & 0x7fffffc00ull;
|
2011-08-26 05:38:59 +08:00
|
|
|
break;
|
|
|
|
case 0x02c0:
|
|
|
|
/* TBA3 */
|
2013-08-05 09:27:13 +08:00
|
|
|
s->pchip.win[3].tba = val & 0x7fffffc00ull;
|
2011-08-26 05:38:59 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0300:
|
|
|
|
/* PCTL: Pchip Control Register. */
|
|
|
|
oldval = s->pchip.ctl;
|
|
|
|
oldval &= ~0x00001cff0fc7ffull; /* RW fields */
|
|
|
|
oldval |= val & 0x00001cff0fc7ffull;
|
|
|
|
s->pchip.ctl = oldval;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0340:
|
|
|
|
/* PLAT: Pchip Master Latency Register. */
|
|
|
|
break;
|
|
|
|
case 0x03c0:
|
|
|
|
/* PERROR: Pchip Error Register. */
|
|
|
|
break;
|
|
|
|
case 0x0400:
|
|
|
|
/* PERRMASK: Pchip Error Mask Register. */
|
|
|
|
break;
|
|
|
|
case 0x0440:
|
|
|
|
/* PERRSET: Pchip Error Set Register. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0480:
|
|
|
|
/* TLBIV: Translation Buffer Invalidate Virtual Register. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x04c0:
|
|
|
|
/* TLBIA: Translation Buffer Invalidate All Register (WO). */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0500:
|
|
|
|
/* PMONCTL */
|
|
|
|
case 0x0540:
|
|
|
|
/* PMONCNT */
|
|
|
|
case 0x0800:
|
|
|
|
/* SPRST */
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2013-05-27 11:17:50 +08:00
|
|
|
cpu_unassigned_access(current_cpu, addr, true, false, 0, size);
|
2011-08-26 05:38:59 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const MemoryRegionOps cchip_ops = {
|
|
|
|
.read = cchip_read,
|
|
|
|
.write = cchip_write,
|
|
|
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
|
|
|
.valid = {
|
2013-07-09 05:57:39 +08:00
|
|
|
.min_access_size = 8,
|
2011-08-26 05:38:59 +08:00
|
|
|
.max_access_size = 8,
|
|
|
|
},
|
|
|
|
.impl = {
|
2013-07-09 05:57:39 +08:00
|
|
|
.min_access_size = 8,
|
|
|
|
.max_access_size = 8,
|
2011-08-26 05:38:59 +08:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static const MemoryRegionOps dchip_ops = {
|
|
|
|
.read = dchip_read,
|
|
|
|
.write = dchip_write,
|
|
|
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
|
|
|
.valid = {
|
2013-07-09 05:57:39 +08:00
|
|
|
.min_access_size = 8,
|
2011-08-26 05:38:59 +08:00
|
|
|
.max_access_size = 8,
|
|
|
|
},
|
|
|
|
.impl = {
|
2013-07-09 05:57:39 +08:00
|
|
|
.min_access_size = 8,
|
2011-08-26 05:38:59 +08:00
|
|
|
.max_access_size = 8,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static const MemoryRegionOps pchip_ops = {
|
|
|
|
.read = pchip_read,
|
|
|
|
.write = pchip_write,
|
|
|
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
|
|
|
.valid = {
|
2013-07-09 05:57:39 +08:00
|
|
|
.min_access_size = 8,
|
2011-08-26 05:38:59 +08:00
|
|
|
.max_access_size = 8,
|
|
|
|
},
|
|
|
|
.impl = {
|
2013-07-09 05:57:39 +08:00
|
|
|
.min_access_size = 8,
|
|
|
|
.max_access_size = 8,
|
2011-08-26 05:38:59 +08:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2013-08-05 09:27:13 +08:00
|
|
|
/* A subroutine of typhoon_translate_iommu that builds an IOMMUTLBEntry
|
|
|
|
using the given translated address and mask. */
|
|
|
|
static bool make_iommu_tlbe(hwaddr taddr, hwaddr mask, IOMMUTLBEntry *ret)
|
|
|
|
{
|
|
|
|
*ret = (IOMMUTLBEntry) {
|
|
|
|
.target_as = &address_space_memory,
|
|
|
|
.translated_addr = taddr,
|
|
|
|
.addr_mask = mask,
|
|
|
|
.perm = IOMMU_RW,
|
|
|
|
};
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* A subroutine of typhoon_translate_iommu that handles scatter-gather
|
|
|
|
translation, given the address of the PTE. */
|
|
|
|
static bool pte_translate(hwaddr pte_addr, IOMMUTLBEntry *ret)
|
|
|
|
{
|
Switch non-CPU callers from ld/st*_phys to address_space_ld/st*
Switch all the uses of ld/st*_phys to address_space_ld/st*,
except for those cases where the address space is the CPU's
(ie cs->as). This was done with the following script which
generates a Coccinelle patch.
A few over-80-columns lines in the result were rewrapped by
hand where Coccinelle failed to do the wrapping automatically,
as well as one location where it didn't put a line-continuation
'\' when wrapping lines on a change made to a match inside
a macro definition.
===begin===
#!/bin/sh -e
# Usage:
# ./ldst-phys.spatch.sh > ldst-phys.spatch
# spatch -sp_file ldst-phys.spatch -dir . | sed -e '/^+/s/\t/ /g' > out.patch
# patch -p1 < out.patch
for FN in ub uw_le uw_be l_le l_be q_le q_be uw l q; do
cat <<EOF
@ cpu_matches_ld_${FN} @
expression E1,E2;
identifier as;
@@
ld${FN}_phys(E1->as,E2)
@ other_matches_ld_${FN} depends on !cpu_matches_ld_${FN} @
expression E1,E2;
@@
-ld${FN}_phys(E1,E2)
+address_space_ld${FN}(E1,E2, MEMTXATTRS_UNSPECIFIED, NULL)
EOF
done
for FN in b w_le w_be l_le l_be q_le q_be w l q; do
cat <<EOF
@ cpu_matches_st_${FN} @
expression E1,E2,E3;
identifier as;
@@
st${FN}_phys(E1->as,E2,E3)
@ other_matches_st_${FN} depends on !cpu_matches_st_${FN} @
expression E1,E2,E3;
@@
-st${FN}_phys(E1,E2,E3)
+address_space_st${FN}(E1,E2,E3, MEMTXATTRS_UNSPECIFIED, NULL)
EOF
done
===endit===
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
2015-04-26 23:49:24 +08:00
|
|
|
uint64_t pte = address_space_ldq(&address_space_memory, pte_addr,
|
|
|
|
MEMTXATTRS_UNSPECIFIED, NULL);
|
2013-08-05 09:27:13 +08:00
|
|
|
|
|
|
|
/* Check valid bit. */
|
|
|
|
if ((pte & 1) == 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return make_iommu_tlbe((pte & 0x3ffffe) << 12, 0x1fff, ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* A subroutine of typhoon_translate_iommu that handles one of the
|
|
|
|
four single-address-cycle translation windows. */
|
|
|
|
static bool window_translate(TyphoonWindow *win, hwaddr addr,
|
|
|
|
IOMMUTLBEntry *ret)
|
|
|
|
{
|
|
|
|
uint32_t wba = win->wba;
|
|
|
|
uint64_t wsm = win->wsm;
|
|
|
|
uint64_t tba = win->tba;
|
|
|
|
uint64_t wsm_ext = wsm | 0xfffff;
|
|
|
|
|
|
|
|
/* Check for window disabled. */
|
|
|
|
if ((wba & 1) == 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check for window hit. */
|
|
|
|
if ((addr & ~wsm_ext) != (wba & 0xfff00000u)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wba & 2) {
|
|
|
|
/* Scatter-gather translation. */
|
|
|
|
hwaddr pte_addr;
|
|
|
|
|
|
|
|
/* See table 10-6, Generating PTE address for PCI DMA Address. */
|
|
|
|
pte_addr = tba & ~(wsm >> 10);
|
|
|
|
pte_addr |= (addr & (wsm | 0xfe000)) >> 10;
|
|
|
|
return pte_translate(pte_addr, ret);
|
|
|
|
} else {
|
|
|
|
/* Direct-mapped translation. */
|
|
|
|
return make_iommu_tlbe(tba & ~wsm_ext, wsm_ext, ret);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle PCI-to-system address translation. */
|
|
|
|
/* TODO: A translation failure here ought to set PCI error codes on the
|
|
|
|
Pchip and generate a machine check interrupt. */
|
2014-08-16 13:55:37 +08:00
|
|
|
static IOMMUTLBEntry typhoon_translate_iommu(MemoryRegion *iommu, hwaddr addr,
|
|
|
|
bool is_write)
|
2013-08-05 09:27:13 +08:00
|
|
|
{
|
|
|
|
TyphoonPchip *pchip = container_of(iommu, TyphoonPchip, iommu);
|
|
|
|
IOMMUTLBEntry ret;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (addr <= 0xffffffffu) {
|
|
|
|
/* Single-address cycle. */
|
|
|
|
|
|
|
|
/* Check for the Window Hole, inhibiting matching. */
|
|
|
|
if ((pchip->ctl & 0x20)
|
|
|
|
&& addr >= 0x80000
|
|
|
|
&& addr <= 0xfffff) {
|
|
|
|
goto failure;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check the first three windows. */
|
|
|
|
for (i = 0; i < 3; ++i) {
|
|
|
|
if (window_translate(&pchip->win[i], addr, &ret)) {
|
|
|
|
goto success;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check the fourth window for DAC disable. */
|
|
|
|
if ((pchip->win[3].wba & 0x80000000000ull) == 0
|
|
|
|
&& window_translate(&pchip->win[3], addr, &ret)) {
|
|
|
|
goto success;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Double-address cycle. */
|
|
|
|
|
|
|
|
if (addr >= 0x10000000000ull && addr < 0x20000000000ull) {
|
|
|
|
/* Check for the DMA monster window. */
|
|
|
|
if (pchip->ctl & 0x40) {
|
|
|
|
/* See 10.1.4.4; in particular <39:35> is ignored. */
|
|
|
|
make_iommu_tlbe(0, 0x007ffffffffull, &ret);
|
|
|
|
goto success;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-29 23:51:20 +08:00
|
|
|
if (addr >= 0x80000000000ull && addr <= 0xfffffffffffull) {
|
2013-08-05 09:27:13 +08:00
|
|
|
/* Check the fourth window for DAC enable and window enable. */
|
|
|
|
if ((pchip->win[3].wba & 0x80000000001ull) == 0x80000000001ull) {
|
|
|
|
uint64_t pte_addr;
|
|
|
|
|
|
|
|
pte_addr = pchip->win[3].tba & 0x7ffc00000ull;
|
|
|
|
pte_addr |= (addr & 0xffffe000u) >> 10;
|
|
|
|
if (pte_translate(pte_addr, &ret)) {
|
|
|
|
goto success;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
failure:
|
|
|
|
ret = (IOMMUTLBEntry) { .perm = IOMMU_NONE };
|
|
|
|
success:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const MemoryRegionIOMMUOps typhoon_iommu_ops = {
|
|
|
|
.translate = typhoon_translate_iommu,
|
|
|
|
};
|
|
|
|
|
|
|
|
static AddressSpace *typhoon_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn)
|
|
|
|
{
|
|
|
|
TyphoonState *s = opaque;
|
|
|
|
return &s->pchip.iommu_as;
|
|
|
|
}
|
|
|
|
|
2011-08-26 05:38:59 +08:00
|
|
|
static void typhoon_set_irq(void *opaque, int irq, int level)
|
|
|
|
{
|
|
|
|
TyphoonState *s = opaque;
|
|
|
|
uint64_t drir;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Set/Reset the bit in CCHIP.DRIR based on IRQ+LEVEL. */
|
|
|
|
drir = s->cchip.drir;
|
|
|
|
if (level) {
|
|
|
|
drir |= 1ull << irq;
|
|
|
|
} else {
|
|
|
|
drir &= ~(1ull << irq);
|
|
|
|
}
|
|
|
|
s->cchip.drir = drir;
|
|
|
|
|
|
|
|
for (i = 0; i < 4; ++i) {
|
|
|
|
cpu_irq_change(s->cchip.cpu[i], s->cchip.dim[i] & drir);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void typhoon_set_isa_irq(void *opaque, int irq, int level)
|
|
|
|
{
|
|
|
|
typhoon_set_irq(opaque, 55, level);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void typhoon_set_timer_irq(void *opaque, int irq, int level)
|
|
|
|
{
|
|
|
|
TyphoonState *s = opaque;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Thankfully, the mc146818rtc code doesn't track the IRQ state,
|
|
|
|
and so we don't have to worry about missing interrupts just
|
|
|
|
because we never actually ACK the interrupt. Just ignore any
|
|
|
|
case of the interrupt level going low. */
|
|
|
|
if (level == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Deliver the interrupt to each CPU, considering each CPU's IIC. */
|
|
|
|
for (i = 0; i < 4; ++i) {
|
2012-10-16 08:45:53 +08:00
|
|
|
AlphaCPU *cpu = s->cchip.cpu[i];
|
|
|
|
if (cpu != NULL) {
|
2011-08-26 05:38:59 +08:00
|
|
|
uint32_t iic = s->cchip.iic[i];
|
|
|
|
|
|
|
|
/* ??? The verbage in Section 10.2.2.10 isn't 100% clear.
|
|
|
|
Bit 24 is the OverFlow bit, RO, and set when the count
|
|
|
|
decrements past 0. When is OF cleared? My guess is that
|
|
|
|
OF is actually cleared when the IIC is written, and that
|
|
|
|
the ICNT field always decrements. At least, that's an
|
|
|
|
interpretation that makes sense, and "allows the CPU to
|
|
|
|
determine exactly how mant interval timer ticks were
|
|
|
|
skipped". At least within the next 4M ticks... */
|
|
|
|
|
|
|
|
iic = ((iic - 1) & 0x1ffffff) | (iic & 0x1000000);
|
|
|
|
s->cchip.iic[i] = iic;
|
|
|
|
|
|
|
|
if (iic & 0x1000000) {
|
|
|
|
/* Set the ITI bit for this cpu. */
|
|
|
|
s->cchip.misc |= 1 << (i + 4);
|
|
|
|
/* And signal the interrupt. */
|
2013-01-18 22:03:43 +08:00
|
|
|
cpu_interrupt(CPU(cpu), CPU_INTERRUPT_TIMER);
|
2011-08-26 05:38:59 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-29 01:40:08 +08:00
|
|
|
static void typhoon_alarm_timer(void *opaque)
|
|
|
|
{
|
|
|
|
TyphoonState *s = (TyphoonState *)((uintptr_t)opaque & ~3);
|
|
|
|
int cpu = (uintptr_t)opaque & 3;
|
|
|
|
|
|
|
|
/* Set the ITI bit for this cpu. */
|
|
|
|
s->cchip.misc |= 1 << (cpu + 4);
|
2013-01-18 22:03:43 +08:00
|
|
|
cpu_interrupt(CPU(s->cchip.cpu[cpu]), CPU_INTERRUPT_TIMER);
|
2011-04-29 01:40:08 +08:00
|
|
|
}
|
|
|
|
|
2011-12-16 05:09:55 +08:00
|
|
|
PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus,
|
|
|
|
qemu_irq *p_rtc_irq,
|
2012-10-16 08:45:53 +08:00
|
|
|
AlphaCPU *cpus[4], pci_map_irq_fn sys_map_irq)
|
2011-08-26 05:38:59 +08:00
|
|
|
{
|
|
|
|
const uint64_t MB = 1024 * 1024;
|
|
|
|
const uint64_t GB = 1024 * MB;
|
|
|
|
MemoryRegion *addr_space = get_system_memory();
|
|
|
|
DeviceState *dev;
|
|
|
|
TyphoonState *s;
|
2012-08-21 01:07:57 +08:00
|
|
|
PCIHostState *phb;
|
2011-08-26 05:38:59 +08:00
|
|
|
PCIBus *b;
|
2011-04-29 01:40:08 +08:00
|
|
|
int i;
|
2011-08-26 05:38:59 +08:00
|
|
|
|
2012-08-21 01:07:57 +08:00
|
|
|
dev = qdev_create(NULL, TYPE_TYPHOON_PCI_HOST_BRIDGE);
|
2011-08-26 05:38:59 +08:00
|
|
|
|
2012-08-21 01:07:57 +08:00
|
|
|
s = TYPHOON_PCI_HOST_BRIDGE(dev);
|
2012-08-21 01:08:08 +08:00
|
|
|
phb = PCI_HOST_BRIDGE(dev);
|
2011-08-26 05:38:59 +08:00
|
|
|
|
2013-08-05 09:27:13 +08:00
|
|
|
s->cchip.misc = 0x800000000ull; /* Revision: Typhoon. */
|
|
|
|
s->pchip.win[3].wba = 2; /* Window 3 SG always enabled. */
|
|
|
|
|
2011-08-26 05:38:59 +08:00
|
|
|
/* Remember the CPUs so that we can deliver interrupts to them. */
|
2011-04-29 01:40:08 +08:00
|
|
|
for (i = 0; i < 4; i++) {
|
2012-10-16 08:45:53 +08:00
|
|
|
AlphaCPU *cpu = cpus[i];
|
|
|
|
s->cchip.cpu[i] = cpu;
|
|
|
|
if (cpu != NULL) {
|
2013-08-21 23:03:08 +08:00
|
|
|
cpu->alarm_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
|
2011-04-29 01:40:08 +08:00
|
|
|
typhoon_alarm_timer,
|
|
|
|
(void *)((uintptr_t)s + i));
|
|
|
|
}
|
|
|
|
}
|
2011-08-26 05:38:59 +08:00
|
|
|
|
2015-05-29 13:27:09 +08:00
|
|
|
*p_rtc_irq = qemu_allocate_irq(typhoon_set_timer_irq, s, 0);
|
2011-08-26 05:38:59 +08:00
|
|
|
|
|
|
|
/* Main memory region, 0x00.0000.0000. Real hardware supports 32GB,
|
|
|
|
but the address space hole reserved at this point is 8TB. */
|
2015-04-04 20:14:14 +08:00
|
|
|
memory_region_allocate_system_memory(&s->ram_region, OBJECT(s), "ram",
|
|
|
|
ram_size);
|
2011-08-26 05:38:59 +08:00
|
|
|
memory_region_add_subregion(addr_space, 0, &s->ram_region);
|
|
|
|
|
|
|
|
/* TIGbus, 0x801.0000.0000, 1GB. */
|
|
|
|
/* ??? The TIGbus is used for delivering interrupts, and access to
|
|
|
|
the flash ROM. I'm not sure that we need to implement it at all. */
|
|
|
|
|
|
|
|
/* Pchip0 CSRs, 0x801.8000.0000, 256MB. */
|
2013-06-07 09:25:08 +08:00
|
|
|
memory_region_init_io(&s->pchip.region, OBJECT(s), &pchip_ops, s, "pchip0",
|
|
|
|
256*MB);
|
2011-10-09 14:50:50 +08:00
|
|
|
memory_region_add_subregion(addr_space, 0x80180000000ULL,
|
|
|
|
&s->pchip.region);
|
2011-08-26 05:38:59 +08:00
|
|
|
|
|
|
|
/* Cchip CSRs, 0x801.A000.0000, 256MB. */
|
2013-06-07 09:25:08 +08:00
|
|
|
memory_region_init_io(&s->cchip.region, OBJECT(s), &cchip_ops, s, "cchip0",
|
|
|
|
256*MB);
|
2011-10-09 14:50:50 +08:00
|
|
|
memory_region_add_subregion(addr_space, 0x801a0000000ULL,
|
|
|
|
&s->cchip.region);
|
2011-08-26 05:38:59 +08:00
|
|
|
|
|
|
|
/* Dchip CSRs, 0x801.B000.0000, 256MB. */
|
2013-06-07 09:25:08 +08:00
|
|
|
memory_region_init_io(&s->dchip_region, OBJECT(s), &dchip_ops, s, "dchip0",
|
|
|
|
256*MB);
|
2011-10-09 14:50:50 +08:00
|
|
|
memory_region_add_subregion(addr_space, 0x801b0000000ULL,
|
|
|
|
&s->dchip_region);
|
2011-08-26 05:38:59 +08:00
|
|
|
|
|
|
|
/* Pchip0 PCI memory, 0x800.0000.0000, 4GB. */
|
2013-06-07 09:25:08 +08:00
|
|
|
memory_region_init(&s->pchip.reg_mem, OBJECT(s), "pci0-mem", 4*GB);
|
2011-10-09 14:50:50 +08:00
|
|
|
memory_region_add_subregion(addr_space, 0x80000000000ULL,
|
|
|
|
&s->pchip.reg_mem);
|
2011-08-26 05:38:59 +08:00
|
|
|
|
|
|
|
/* Pchip0 PCI I/O, 0x801.FC00.0000, 32MB. */
|
2013-07-09 06:46:37 +08:00
|
|
|
memory_region_init_io(&s->pchip.reg_io, OBJECT(s), &alpha_pci_ignore_ops,
|
|
|
|
NULL, "pci0-io", 32*MB);
|
2011-10-09 14:50:50 +08:00
|
|
|
memory_region_add_subregion(addr_space, 0x801fc000000ULL,
|
|
|
|
&s->pchip.reg_io);
|
2011-08-26 05:38:59 +08:00
|
|
|
|
2012-08-21 01:07:57 +08:00
|
|
|
b = pci_register_bus(dev, "pci",
|
2011-08-26 05:38:59 +08:00
|
|
|
typhoon_set_irq, sys_map_irq, s,
|
2013-07-09 04:55:30 +08:00
|
|
|
&s->pchip.reg_mem, &s->pchip.reg_io,
|
|
|
|
0, 64, TYPE_PCI_BUS);
|
2012-08-21 01:07:57 +08:00
|
|
|
phb->bus = b;
|
2016-07-14 21:43:41 +08:00
|
|
|
qdev_init_nofail(dev);
|
2011-08-26 05:38:59 +08:00
|
|
|
|
2013-08-05 09:27:13 +08:00
|
|
|
/* Host memory as seen from the PCI side, via the IOMMU. */
|
|
|
|
memory_region_init_iommu(&s->pchip.iommu, OBJECT(s), &typhoon_iommu_ops,
|
|
|
|
"iommu-typhoon", UINT64_MAX);
|
|
|
|
address_space_init(&s->pchip.iommu_as, &s->pchip.iommu, "pchip0-pci");
|
|
|
|
pci_setup_iommu(b, typhoon_pci_dma_iommu, s);
|
|
|
|
|
2011-08-26 05:38:59 +08:00
|
|
|
/* Pchip0 PCI special/interrupt acknowledge, 0x801.F800.0000, 64MB. */
|
2013-07-09 04:55:30 +08:00
|
|
|
memory_region_init_io(&s->pchip.reg_iack, OBJECT(s), &alpha_pci_iack_ops,
|
|
|
|
b, "pci0-iack", 64*MB);
|
2011-10-09 14:50:50 +08:00
|
|
|
memory_region_add_subregion(addr_space, 0x801f8000000ULL,
|
|
|
|
&s->pchip.reg_iack);
|
2011-08-26 05:38:59 +08:00
|
|
|
|
|
|
|
/* Pchip0 PCI configuration, 0x801.FE00.0000, 16MB. */
|
2013-07-09 04:55:30 +08:00
|
|
|
memory_region_init_io(&s->pchip.reg_conf, OBJECT(s), &alpha_pci_conf1_ops,
|
|
|
|
b, "pci0-conf", 16*MB);
|
2011-10-09 14:50:50 +08:00
|
|
|
memory_region_add_subregion(addr_space, 0x801fe000000ULL,
|
|
|
|
&s->pchip.reg_conf);
|
2011-08-26 05:38:59 +08:00
|
|
|
|
|
|
|
/* For the record, these are the mappings for the second PCI bus.
|
|
|
|
We can get away with not implementing them because we indicate
|
|
|
|
via the Cchip.CSC<PIP> bit that Pchip1 is not present. */
|
|
|
|
/* Pchip1 PCI memory, 0x802.0000.0000, 4GB. */
|
|
|
|
/* Pchip1 CSRs, 0x802.8000.0000, 256MB. */
|
|
|
|
/* Pchip1 PCI special/interrupt acknowledge, 0x802.F800.0000, 64MB. */
|
|
|
|
/* Pchip1 PCI I/O, 0x802.FC00.0000, 32MB. */
|
|
|
|
/* Pchip1 PCI configuration, 0x802.FE00.0000, 16MB. */
|
|
|
|
|
|
|
|
/* Init the ISA bus. */
|
|
|
|
/* ??? Technically there should be a cy82c693ub pci-isa bridge. */
|
|
|
|
{
|
2015-05-29 13:27:09 +08:00
|
|
|
qemu_irq *isa_irqs;
|
2011-08-26 05:38:59 +08:00
|
|
|
|
isa: Clean up error handling around isa_bus_new()
We can have at most one ISA bus. If you try to create another one,
isa_bus_new() complains to stderr and returns null.
isa_bus_new() is called in two contexts, machine's init() and device's
realize() methods. Since complaining to stderr is not proper in the
latter context, convert isa_bus_new() to Error.
Machine's init():
* mips_jazz_init(), called from the init() methods of machines
"magnum" and "pica"
* mips_r4k_init(), the init() method of machine "mips"
* pc_init1() called from the init() methods of non-q35 PC machines
* typhoon_init(), called from clipper_init(), the init() method of
machine "clipper"
These callers always create the first ISA bus, hence isa_bus_new()
can't fail. Simply pass &error_abort.
Device's realize():
* i82378_realize(), of PCI device "i82378"
* ich9_lpc_realize(), of PCI device "ICH9-LPC"
* pci_ebus_realize(), of PCI device "ebus"
* piix3_realize(), of PCI device "pci-piix3", abstract parent of
"PIIX3" and "PIIX3-xen"
* piix4_realize(), of PCI device "PIIX4"
* vt82c686b_realize(), of PCI device "VT82C686B"
Propagate the error. Note that these devices are typically created
only by machine init() methods with qdev_init_nofail() or similar. If
we screwed up and created an ISA bus before that call, we now give up
right away. Before, we'd hobble on, and typically die in
isa_bus_irqs(). Similar if someone finds a way to hot-plug one of
these critters.
Cc: Richard Henderson <rth@twiddle.net>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Cc: "Hervé Poussineau" <hpoussin@reactos.org>
Cc: Aurelien Jarno <aurelien@aurel32.net>
Cc: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
Reviewed-by: Marcel Apfelbaum <marcel@redhat.com>
Reviewed-by: Hervé Poussineau <hpoussin@reactos.org>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Message-Id: <1450370121-5768-11-git-send-email-armbru@redhat.com>
2015-12-18 00:35:18 +08:00
|
|
|
*isa_bus = isa_bus_new(NULL, get_system_memory(), &s->pchip.reg_io,
|
|
|
|
&error_abort);
|
2015-05-29 13:27:09 +08:00
|
|
|
isa_irqs = i8259_init(*isa_bus,
|
|
|
|
qemu_allocate_irq(typhoon_set_isa_irq, s, 0));
|
2011-12-16 05:09:55 +08:00
|
|
|
isa_bus_irqs(*isa_bus, isa_irqs);
|
2011-08-26 05:38:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int typhoon_pcihost_init(SysBusDevice *dev)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-01-25 03:12:29 +08:00
|
|
|
static void typhoon_pcihost_class_init(ObjectClass *klass, void *data)
|
|
|
|
{
|
|
|
|
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
|
|
|
|
|
|
|
|
k->init = typhoon_pcihost_init;
|
|
|
|
}
|
|
|
|
|
2012-08-21 01:07:56 +08:00
|
|
|
static const TypeInfo typhoon_pcihost_info = {
|
2012-08-21 01:07:57 +08:00
|
|
|
.name = TYPE_TYPHOON_PCI_HOST_BRIDGE,
|
2012-08-21 01:08:08 +08:00
|
|
|
.parent = TYPE_PCI_HOST_BRIDGE,
|
2011-12-08 11:34:16 +08:00
|
|
|
.instance_size = sizeof(TyphoonState),
|
|
|
|
.class_init = typhoon_pcihost_class_init,
|
2011-08-26 05:38:59 +08:00
|
|
|
};
|
|
|
|
|
2012-02-09 22:20:55 +08:00
|
|
|
static void typhoon_register_types(void)
|
2011-08-26 05:38:59 +08:00
|
|
|
{
|
2011-12-08 11:34:16 +08:00
|
|
|
type_register_static(&typhoon_pcihost_info);
|
2011-08-26 05:38:59 +08:00
|
|
|
}
|
2012-02-09 22:20:55 +08:00
|
|
|
|
|
|
|
type_init(typhoon_register_types)
|