mirror of
				https://git.proxmox.com/git/qemu
				synced 2025-10-27 17:45:41 +00:00 
			
		
		
		
	 a8170e5e97
			
		
	
	
		a8170e5e97
		
	
	
	
	
		
			
			target_phys_addr_t is unwieldly, violates the C standard (_t suffixes are
reserved) and its purpose doesn't match the name (most target_phys_addr_t
addresses are not target specific).  Replace it with a finger-friendly,
standards conformant hwaddr.
Outstanding patchsets can be fixed up with the command
  git rebase -i --exec 'find -name "*.[ch]"
                        | xargs s/target_phys_addr_t/hwaddr/g' origin
Signed-off-by: Avi Kivity <avi@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
		
	
			
		
			
				
	
	
		
			839 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			839 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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"
 | |
| 
 | |
| 
 | |
| #define TYPE_TYPHOON_PCI_HOST_BRIDGE "typhoon-pcihost"
 | |
| 
 | |
| 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;
 | |
| 
 | |
| #define TYPHOON_PCI_HOST_BRIDGE(obj) \
 | |
|     OBJECT_CHECK(TyphoonState, (obj), TYPE_TYPHOON_PCI_HOST_BRIDGE)
 | |
| 
 | |
| typedef struct TyphoonState {
 | |
|     PCIHostState parent_obj;
 | |
| 
 | |
|     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, hwaddr 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, hwaddr addr, unsigned size)
 | |
| {
 | |
|     /* Skip this.  It's all related to DRAM timing and setup.  */
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static uint64_t pchip_read(void *opaque, hwaddr 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, hwaddr 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, hwaddr addr,
 | |
|                         uint64_t val, unsigned size)
 | |
| {
 | |
|     /* Skip this.  It's all related to DRAM timing and setup.  */
 | |
| }
 | |
| 
 | |
| static void pchip_write(void *opaque, hwaddr 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;
 | |
|     TyphoonState *s;
 | |
|     PCIHostState *phb;
 | |
|     PCIBus *b;
 | |
|     int i;
 | |
| 
 | |
|     dev = qdev_create(NULL, TYPE_TYPHOON_PCI_HOST_BRIDGE);
 | |
|     qdev_init_nofail(dev);
 | |
| 
 | |
|     s = TYPHOON_PCI_HOST_BRIDGE(dev);
 | |
|     phb = PCI_HOST_BRIDGE(dev);
 | |
| 
 | |
|     /* 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(dev, "pci",
 | |
|                          typhoon_set_irq, sys_map_irq, s,
 | |
|                          &s->pchip.reg_mem, addr_space_io, 0, 64);
 | |
|     phb->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 const TypeInfo typhoon_pcihost_info = {
 | |
|     .name          = TYPE_TYPHOON_PCI_HOST_BRIDGE,
 | |
|     .parent        = TYPE_PCI_HOST_BRIDGE,
 | |
|     .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)
 |