|  | /* | 
|  | * DEC 21272 (TSUNAMI/TYPHOON) chipset emulation. | 
|  | * | 
|  | * Written by Richard Henderson. | 
|  | * | 
|  | * This work is licensed under the GNU GPL license version 2 or later. | 
|  | */ | 
|  |  | 
|  | #include "cpu.h" | 
|  | #include "exec-all.h" | 
|  | #include "hw.h" | 
|  | #include "devices.h" | 
|  | #include "sysemu.h" | 
|  | #include "alpha_sys.h" | 
|  | #include "exec-memory.h" | 
|  |  | 
|  |  | 
|  | typedef struct TyphoonCchip { | 
|  | MemoryRegion region; | 
|  | uint64_t misc; | 
|  | uint64_t drir; | 
|  | uint64_t dim[4]; | 
|  | uint32_t iic[4]; | 
|  | CPUAlphaState *cpu[4]; | 
|  | } TyphoonCchip; | 
|  |  | 
|  | typedef struct TyphoonWindow { | 
|  | uint32_t base_addr; | 
|  | uint32_t mask; | 
|  | uint32_t translated_base_pfn; | 
|  | } TyphoonWindow; | 
|  |  | 
|  | typedef struct TyphoonPchip { | 
|  | MemoryRegion region; | 
|  | MemoryRegion reg_iack; | 
|  | MemoryRegion reg_mem; | 
|  | MemoryRegion reg_io; | 
|  | MemoryRegion reg_conf; | 
|  | uint64_t ctl; | 
|  | TyphoonWindow win[4]; | 
|  | } TyphoonPchip; | 
|  |  | 
|  | typedef struct TyphoonState { | 
|  | PCIHostState host; | 
|  | TyphoonCchip cchip; | 
|  | TyphoonPchip pchip; | 
|  | MemoryRegion dchip_region; | 
|  | MemoryRegion ram_region; | 
|  |  | 
|  | /* QEMU emulation state.  */ | 
|  | uint32_t latch_tmp; | 
|  | } TyphoonState; | 
|  |  | 
|  | /* Called when one of DRIR or DIM changes.  */ | 
|  | static void cpu_irq_change(CPUAlphaState *env, uint64_t req) | 
|  | { | 
|  | /* If there are any non-masked interrupts, tell the cpu.  */ | 
|  | if (env) { | 
|  | if (req) { | 
|  | cpu_interrupt(env, CPU_INTERRUPT_HARD); | 
|  | } else { | 
|  | cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static uint64_t cchip_read(void *opaque, target_phys_addr_t addr, unsigned size) | 
|  | { | 
|  | CPUAlphaState *env = cpu_single_env; | 
|  | TyphoonState *s = opaque; | 
|  | uint64_t ret = 0; | 
|  |  | 
|  | if (addr & 4) { | 
|  | return s->latch_tmp; | 
|  | } | 
|  |  | 
|  | 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.  */ | 
|  | ret = s->cchip.misc | (env->cpu_index & 3); | 
|  | 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: | 
|  | cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, size); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | s->latch_tmp = ret >> 32; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static uint64_t dchip_read(void *opaque, target_phys_addr_t addr, unsigned size) | 
|  | { | 
|  | /* Skip this.  It's all related to DRAM timing and setup.  */ | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static uint64_t pchip_read(void *opaque, target_phys_addr_t addr, unsigned size) | 
|  | { | 
|  | TyphoonState *s = opaque; | 
|  | uint64_t ret = 0; | 
|  |  | 
|  | if (addr & 4) { | 
|  | return s->latch_tmp; | 
|  | } | 
|  |  | 
|  | switch (addr) { | 
|  | case 0x0000: | 
|  | /* WSBA0: Window Space Base Address Register.  */ | 
|  | ret = s->pchip.win[0].base_addr; | 
|  | break; | 
|  | case 0x0040: | 
|  | /* WSBA1 */ | 
|  | ret = s->pchip.win[1].base_addr; | 
|  | break; | 
|  | case 0x0080: | 
|  | /* WSBA2 */ | 
|  | ret = s->pchip.win[2].base_addr; | 
|  | break; | 
|  | case 0x00c0: | 
|  | /* WSBA3 */ | 
|  | ret = s->pchip.win[3].base_addr; | 
|  | break; | 
|  |  | 
|  | case 0x0100: | 
|  | /* WSM0: Window Space Mask Register.  */ | 
|  | ret = s->pchip.win[0].mask; | 
|  | break; | 
|  | case 0x0140: | 
|  | /* WSM1 */ | 
|  | ret = s->pchip.win[1].mask; | 
|  | break; | 
|  | case 0x0180: | 
|  | /* WSM2 */ | 
|  | ret = s->pchip.win[2].mask; | 
|  | break; | 
|  | case 0x01c0: | 
|  | /* WSM3 */ | 
|  | ret = s->pchip.win[3].mask; | 
|  | break; | 
|  |  | 
|  | case 0x0200: | 
|  | /* TBA0: Translated Base Address Register.  */ | 
|  | ret = (uint64_t)s->pchip.win[0].translated_base_pfn << 10; | 
|  | break; | 
|  | case 0x0240: | 
|  | /* TBA1 */ | 
|  | ret = (uint64_t)s->pchip.win[1].translated_base_pfn << 10; | 
|  | break; | 
|  | case 0x0280: | 
|  | /* TBA2 */ | 
|  | ret = (uint64_t)s->pchip.win[2].translated_base_pfn << 10; | 
|  | break; | 
|  | case 0x02c0: | 
|  | /* TBA3 */ | 
|  | ret = (uint64_t)s->pchip.win[3].translated_base_pfn << 10; | 
|  | 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: | 
|  | cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, size); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | s->latch_tmp = ret >> 32; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static void cchip_write(void *opaque, target_phys_addr_t addr, | 
|  | uint64_t v32, unsigned size) | 
|  | { | 
|  | TyphoonState *s = opaque; | 
|  | uint64_t val, oldval, newval; | 
|  |  | 
|  | if (addr & 4) { | 
|  | val = v32 << 32 | s->latch_tmp; | 
|  | addr ^= 4; | 
|  | } else { | 
|  | s->latch_tmp = v32; | 
|  | return; | 
|  | } | 
|  |  | 
|  | 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) { | 
|  | CPUAlphaState *env = s->cchip.cpu[i]; | 
|  | if (env) { | 
|  | /* IPI can be either cleared or set by the write.  */ | 
|  | if (newval & (1 << (i + 8))) { | 
|  | cpu_interrupt(env, CPU_INTERRUPT_SMP); | 
|  | } else { | 
|  | cpu_reset_interrupt(env, CPU_INTERRUPT_SMP); | 
|  | } | 
|  |  | 
|  | /* ITI can only be cleared by the write.  */ | 
|  | if ((newval & (1 << (i + 4))) == 0) { | 
|  | cpu_reset_interrupt(env, CPU_INTERRUPT_TIMER); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | 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: | 
|  | cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, size); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void dchip_write(void *opaque, target_phys_addr_t addr, | 
|  | uint64_t val, unsigned size) | 
|  | { | 
|  | /* Skip this.  It's all related to DRAM timing and setup.  */ | 
|  | } | 
|  |  | 
|  | static void pchip_write(void *opaque, target_phys_addr_t addr, | 
|  | uint64_t v32, unsigned size) | 
|  | { | 
|  | TyphoonState *s = opaque; | 
|  | uint64_t val, oldval; | 
|  |  | 
|  | if (addr & 4) { | 
|  | val = v32 << 32 | s->latch_tmp; | 
|  | addr ^= 4; | 
|  | } else { | 
|  | s->latch_tmp = v32; | 
|  | return; | 
|  | } | 
|  |  | 
|  | switch (addr) { | 
|  | case 0x0000: | 
|  | /* WSBA0: Window Space Base Address Register.  */ | 
|  | s->pchip.win[0].base_addr = val; | 
|  | break; | 
|  | case 0x0040: | 
|  | /* WSBA1 */ | 
|  | s->pchip.win[1].base_addr = val; | 
|  | break; | 
|  | case 0x0080: | 
|  | /* WSBA2 */ | 
|  | s->pchip.win[2].base_addr = val; | 
|  | break; | 
|  | case 0x00c0: | 
|  | /* WSBA3 */ | 
|  | s->pchip.win[3].base_addr = val; | 
|  | break; | 
|  |  | 
|  | case 0x0100: | 
|  | /* WSM0: Window Space Mask Register.  */ | 
|  | s->pchip.win[0].mask = val; | 
|  | break; | 
|  | case 0x0140: | 
|  | /* WSM1 */ | 
|  | s->pchip.win[1].mask = val; | 
|  | break; | 
|  | case 0x0180: | 
|  | /* WSM2 */ | 
|  | s->pchip.win[2].mask = val; | 
|  | break; | 
|  | case 0x01c0: | 
|  | /* WSM3 */ | 
|  | s->pchip.win[3].mask = val; | 
|  | break; | 
|  |  | 
|  | case 0x0200: | 
|  | /* TBA0: Translated Base Address Register.  */ | 
|  | s->pchip.win[0].translated_base_pfn = val >> 10; | 
|  | break; | 
|  | case 0x0240: | 
|  | /* TBA1 */ | 
|  | s->pchip.win[1].translated_base_pfn = val >> 10; | 
|  | break; | 
|  | case 0x0280: | 
|  | /* TBA2 */ | 
|  | s->pchip.win[2].translated_base_pfn = val >> 10; | 
|  | break; | 
|  | case 0x02c0: | 
|  | /* TBA3 */ | 
|  | s->pchip.win[3].translated_base_pfn = val >> 10; | 
|  | 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: | 
|  | cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, size); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | static const MemoryRegionOps cchip_ops = { | 
|  | .read = cchip_read, | 
|  | .write = cchip_write, | 
|  | .endianness = DEVICE_LITTLE_ENDIAN, | 
|  | .valid = { | 
|  | .min_access_size = 4,  /* ??? Should be 8.  */ | 
|  | .max_access_size = 8, | 
|  | }, | 
|  | .impl = { | 
|  | .min_access_size = 4, | 
|  | .max_access_size = 4, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static const MemoryRegionOps dchip_ops = { | 
|  | .read = dchip_read, | 
|  | .write = dchip_write, | 
|  | .endianness = DEVICE_LITTLE_ENDIAN, | 
|  | .valid = { | 
|  | .min_access_size = 4,  /* ??? Should be 8.  */ | 
|  | .max_access_size = 8, | 
|  | }, | 
|  | .impl = { | 
|  | .min_access_size = 4, | 
|  | .max_access_size = 8, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static const MemoryRegionOps pchip_ops = { | 
|  | .read = pchip_read, | 
|  | .write = pchip_write, | 
|  | .endianness = DEVICE_LITTLE_ENDIAN, | 
|  | .valid = { | 
|  | .min_access_size = 4,  /* ??? Should be 8.  */ | 
|  | .max_access_size = 8, | 
|  | }, | 
|  | .impl = { | 
|  | .min_access_size = 4, | 
|  | .max_access_size = 4, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | 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) { | 
|  | CPUAlphaState *env = s->cchip.cpu[i]; | 
|  | if (env) { | 
|  | 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.  */ | 
|  | cpu_interrupt(env, CPU_INTERRUPT_TIMER); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | 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); | 
|  | cpu_interrupt(s->cchip.cpu[cpu], CPU_INTERRUPT_TIMER); | 
|  | } | 
|  |  | 
|  | PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, | 
|  | qemu_irq *p_rtc_irq, | 
|  | CPUAlphaState *cpus[4], pci_map_irq_fn sys_map_irq) | 
|  | { | 
|  | const uint64_t MB = 1024 * 1024; | 
|  | const uint64_t GB = 1024 * MB; | 
|  | MemoryRegion *addr_space = get_system_memory(); | 
|  | MemoryRegion *addr_space_io = get_system_io(); | 
|  | DeviceState *dev; | 
|  | PCIHostState *p; | 
|  | TyphoonState *s; | 
|  | PCIBus *b; | 
|  | int i; | 
|  |  | 
|  | dev = qdev_create(NULL, "typhoon-pcihost"); | 
|  | qdev_init_nofail(dev); | 
|  |  | 
|  | p = FROM_SYSBUS(PCIHostState, sysbus_from_qdev(dev)); | 
|  | s = container_of(p, TyphoonState, host); | 
|  |  | 
|  | /* Remember the CPUs so that we can deliver interrupts to them.  */ | 
|  | for (i = 0; i < 4; i++) { | 
|  | CPUAlphaState *env = cpus[i]; | 
|  | s->cchip.cpu[i] = env; | 
|  | if (env) { | 
|  | env->alarm_timer = qemu_new_timer_ns(rtc_clock, | 
|  | typhoon_alarm_timer, | 
|  | (void *)((uintptr_t)s + i)); | 
|  | } | 
|  | } | 
|  |  | 
|  | *p_rtc_irq = *qemu_allocate_irqs(typhoon_set_timer_irq, s, 1); | 
|  |  | 
|  | /* Main memory region, 0x00.0000.0000.  Real hardware supports 32GB, | 
|  | but the address space hole reserved at this point is 8TB.  */ | 
|  | memory_region_init_ram(&s->ram_region, "ram", ram_size); | 
|  | vmstate_register_ram_global(&s->ram_region); | 
|  | 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.  */ | 
|  | memory_region_init_io(&s->pchip.region, &pchip_ops, s, "pchip0", 256*MB); | 
|  | memory_region_add_subregion(addr_space, 0x80180000000ULL, | 
|  | &s->pchip.region); | 
|  |  | 
|  | /* Cchip CSRs, 0x801.A000.0000, 256MB.  */ | 
|  | memory_region_init_io(&s->cchip.region, &cchip_ops, s, "cchip0", 256*MB); | 
|  | memory_region_add_subregion(addr_space, 0x801a0000000ULL, | 
|  | &s->cchip.region); | 
|  |  | 
|  | /* Dchip CSRs, 0x801.B000.0000, 256MB.  */ | 
|  | memory_region_init_io(&s->dchip_region, &dchip_ops, s, "dchip0", 256*MB); | 
|  | memory_region_add_subregion(addr_space, 0x801b0000000ULL, | 
|  | &s->dchip_region); | 
|  |  | 
|  | /* Pchip0 PCI memory, 0x800.0000.0000, 4GB.  */ | 
|  | memory_region_init(&s->pchip.reg_mem, "pci0-mem", 4*GB); | 
|  | memory_region_add_subregion(addr_space, 0x80000000000ULL, | 
|  | &s->pchip.reg_mem); | 
|  |  | 
|  | /* Pchip0 PCI I/O, 0x801.FC00.0000, 32MB.  */ | 
|  | /* ??? Ideally we drop the "system" i/o space on the floor and give the | 
|  | PCI subsystem the full address space reserved by the chipset. | 
|  | We can't do that until the MEM and IO paths in memory.c are unified.  */ | 
|  | memory_region_init_io(&s->pchip.reg_io, &alpha_pci_bw_io_ops, NULL, | 
|  | "pci0-io", 32*MB); | 
|  | memory_region_add_subregion(addr_space, 0x801fc000000ULL, | 
|  | &s->pchip.reg_io); | 
|  |  | 
|  | b = pci_register_bus(&s->host.busdev.qdev, "pci", | 
|  | typhoon_set_irq, sys_map_irq, s, | 
|  | &s->pchip.reg_mem, addr_space_io, 0, 64); | 
|  | s->host.bus = b; | 
|  |  | 
|  | /* Pchip0 PCI special/interrupt acknowledge, 0x801.F800.0000, 64MB.  */ | 
|  | memory_region_init_io(&s->pchip.reg_iack, &alpha_pci_iack_ops, b, | 
|  | "pci0-iack", 64*MB); | 
|  | memory_region_add_subregion(addr_space, 0x801f8000000ULL, | 
|  | &s->pchip.reg_iack); | 
|  |  | 
|  | /* Pchip0 PCI configuration, 0x801.FE00.0000, 16MB.  */ | 
|  | memory_region_init_io(&s->pchip.reg_conf, &alpha_pci_conf1_ops, b, | 
|  | "pci0-conf", 16*MB); | 
|  | memory_region_add_subregion(addr_space, 0x801fe000000ULL, | 
|  | &s->pchip.reg_conf); | 
|  |  | 
|  | /* 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.  */ | 
|  | { | 
|  | qemu_irq isa_pci_irq, *isa_irqs; | 
|  |  | 
|  | *isa_bus = isa_bus_new(NULL, addr_space_io); | 
|  | isa_pci_irq = *qemu_allocate_irqs(typhoon_set_isa_irq, s, 1); | 
|  | isa_irqs = i8259_init(*isa_bus, isa_pci_irq); | 
|  | isa_bus_irqs(*isa_bus, isa_irqs); | 
|  | } | 
|  |  | 
|  | return b; | 
|  | } | 
|  |  | 
|  | static int typhoon_pcihost_init(SysBusDevice *dev) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void typhoon_pcihost_class_init(ObjectClass *klass, void *data) | 
|  | { | 
|  | DeviceClass *dc = DEVICE_CLASS(klass); | 
|  | SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); | 
|  |  | 
|  | k->init = typhoon_pcihost_init; | 
|  | dc->no_user = 1; | 
|  | } | 
|  |  | 
|  | static TypeInfo typhoon_pcihost_info = { | 
|  | .name          = "typhoon-pcihost", | 
|  | .parent        = TYPE_SYS_BUS_DEVICE, | 
|  | .instance_size = sizeof(TyphoonState), | 
|  | .class_init    = typhoon_pcihost_class_init, | 
|  | }; | 
|  |  | 
|  | static void typhoon_register_types(void) | 
|  | { | 
|  | type_register_static(&typhoon_pcihost_info); | 
|  | } | 
|  |  | 
|  | type_init(typhoon_register_types) |