 8d5f07fa3b
			
		
	
	
		8d5f07fa3b
		
	
	
	
	
		
			
			git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1098 c046a42c-6fe2-441c-8c8c-71466251a162
		
			
				
	
	
		
			269 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			269 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * QEMU interrupt controller emulation
 | |
|  * 
 | |
|  * Copyright (c) 2003-2004 Fabrice Bellard
 | |
|  * 
 | |
|  * Permission is hereby granted, free of charge, to any person obtaining a copy
 | |
|  * of this software and associated documentation files (the "Software"), to deal
 | |
|  * in the Software without restriction, including without limitation the rights
 | |
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | |
|  * copies of the Software, and to permit persons to whom the Software is
 | |
|  * furnished to do so, subject to the following conditions:
 | |
|  *
 | |
|  * The above copyright notice and this permission notice shall be included in
 | |
|  * all copies or substantial portions of the Software.
 | |
|  *
 | |
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 | |
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | |
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | |
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | |
|  * THE SOFTWARE.
 | |
|  */
 | |
| #include "vl.h"
 | |
| //#define DEBUG_IRQ_COUNT
 | |
| 
 | |
| /* These registers are used for sending/receiving irqs from/to
 | |
|  * different cpu's.
 | |
|  */
 | |
| struct sun4m_intreg_percpu {
 | |
| 	unsigned int tbt;        /* Intrs pending for this cpu, by PIL. */
 | |
| 	/* These next two registers are WRITE-ONLY and are only
 | |
| 	 * "on bit" sensitive, "off bits" written have NO affect.
 | |
| 	 */
 | |
| 	unsigned int clear;  /* Clear this cpus irqs here. */
 | |
| 	unsigned int set;    /* Set this cpus irqs here. */
 | |
| };
 | |
| /*
 | |
|  * djhr
 | |
|  * Actually the clear and set fields in this struct are misleading..
 | |
|  * according to the SLAVIO manual (and the same applies for the SEC)
 | |
|  * the clear field clears bits in the mask which will ENABLE that IRQ
 | |
|  * the set field sets bits in the mask to DISABLE the IRQ.
 | |
|  *
 | |
|  * Also the undirected_xx address in the SLAVIO is defined as
 | |
|  * RESERVED and write only..
 | |
|  *
 | |
|  * DAVEM_NOTE: The SLAVIO only specifies behavior on uniprocessor
 | |
|  *             sun4m machines, for MP the layout makes more sense.
 | |
|  */
 | |
| struct sun4m_intreg_master {
 | |
| 	unsigned int tbt;        /* IRQ's that are pending, see sun4m masks. */
 | |
| 	unsigned int irqs;       /* Master IRQ bits. */
 | |
| 
 | |
| 	/* Again, like the above, two these registers are WRITE-ONLY. */
 | |
| 	unsigned int clear;      /* Clear master IRQ's by setting bits here. */
 | |
| 	unsigned int set;        /* Set master IRQ's by setting bits here. */
 | |
| 
 | |
| 	/* This register is both READ and WRITE. */
 | |
| 	unsigned int undirected_target;  /* Which cpu gets undirected irqs. */
 | |
| };
 | |
| 
 | |
| #define SUN4M_INT_ENABLE        0x80000000
 | |
| #define SUN4M_INT_E14           0x00000080
 | |
| #define SUN4M_INT_E10           0x00080000
 | |
| 
 | |
| #define SUN4M_HARD_INT(x)       (0x000000001 << (x))
 | |
| #define SUN4M_SOFT_INT(x)       (0x000010000 << (x))
 | |
| 
 | |
| #define SUN4M_INT_MASKALL       0x80000000        /* mask all interrupts */
 | |
| #define SUN4M_INT_MODULE_ERR    0x40000000        /* module error */
 | |
| #define SUN4M_INT_M2S_WRITE     0x20000000        /* write buffer error */
 | |
| #define SUN4M_INT_ECC           0x10000000        /* ecc memory error */
 | |
| #define SUN4M_INT_FLOPPY        0x00400000        /* floppy disk */
 | |
| #define SUN4M_INT_MODULE        0x00200000        /* module interrupt */
 | |
| #define SUN4M_INT_VIDEO         0x00100000        /* onboard video */
 | |
| #define SUN4M_INT_REALTIME      0x00080000        /* system timer */
 | |
| #define SUN4M_INT_SCSI          0x00040000        /* onboard scsi */
 | |
| #define SUN4M_INT_AUDIO         0x00020000        /* audio/isdn */
 | |
| #define SUN4M_INT_ETHERNET      0x00010000        /* onboard ethernet */
 | |
| #define SUN4M_INT_SERIAL        0x00008000        /* serial ports */
 | |
| #define SUN4M_INT_SBUSBITS      0x00003F80        /* sbus int bits */
 | |
| 
 | |
| #define SUN4M_INT_SBUS(x)       (1 << (x+7))
 | |
| #define SUN4M_INT_VME(x)        (1 << (x))
 | |
| 
 | |
| typedef struct SCHEDState {
 | |
|     uint32_t addr, addrg;
 | |
|     uint32_t intreg_pending;
 | |
|     uint32_t intreg_enabled;
 | |
|     uint32_t intregm_pending;
 | |
|     uint32_t intregm_enabled;
 | |
| } SCHEDState;
 | |
| 
 | |
| static SCHEDState *ps;
 | |
| 
 | |
| #ifdef DEBUG_IRQ_COUNT
 | |
| static uint64_t irq_count[32];
 | |
| #endif
 | |
| 
 | |
| static uint32_t intreg_mem_readl(void *opaque, target_phys_addr_t addr)
 | |
| {
 | |
|     SCHEDState *s = opaque;
 | |
|     uint32_t saddr;
 | |
| 
 | |
|     saddr = (addr - s->addr) >> 2;
 | |
|     switch (saddr) {
 | |
|     case 0:
 | |
| 	return s->intreg_pending;
 | |
| 	break;
 | |
|     default:
 | |
| 	break;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static void intreg_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
 | |
| {
 | |
|     SCHEDState *s = opaque;
 | |
|     uint32_t saddr;
 | |
| 
 | |
|     saddr = (addr - s->addr) >> 2;
 | |
|     switch (saddr) {
 | |
|     case 0:
 | |
| 	s->intreg_pending = val;
 | |
| 	break;
 | |
|     case 1: // clear
 | |
| 	s->intreg_enabled &= ~val;
 | |
| 	break;
 | |
|     case 2: // set
 | |
| 	s->intreg_enabled |= val;
 | |
| 	break;
 | |
|     default:
 | |
| 	break;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static CPUReadMemoryFunc *intreg_mem_read[3] = {
 | |
|     intreg_mem_readl,
 | |
|     intreg_mem_readl,
 | |
|     intreg_mem_readl,
 | |
| };
 | |
| 
 | |
| static CPUWriteMemoryFunc *intreg_mem_write[3] = {
 | |
|     intreg_mem_writel,
 | |
|     intreg_mem_writel,
 | |
|     intreg_mem_writel,
 | |
| };
 | |
| 
 | |
| static uint32_t intregm_mem_readl(void *opaque, target_phys_addr_t addr)
 | |
| {
 | |
|     SCHEDState *s = opaque;
 | |
|     uint32_t saddr;
 | |
| 
 | |
|     saddr = (addr - s->addrg) >> 2;
 | |
|     switch (saddr) {
 | |
|     case 0:
 | |
| 	return s->intregm_pending;
 | |
| 	break;
 | |
|     case 1:
 | |
| 	return s->intregm_enabled;
 | |
| 	break;
 | |
|     default:
 | |
| 	break;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static void intregm_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
 | |
| {
 | |
|     SCHEDState *s = opaque;
 | |
|     uint32_t saddr;
 | |
| 
 | |
|     saddr = (addr - s->addrg) >> 2;
 | |
|     switch (saddr) {
 | |
|     case 0:
 | |
| 	s->intregm_pending = val;
 | |
| 	break;
 | |
|     case 1:
 | |
| 	s->intregm_enabled = val;
 | |
| 	break;
 | |
|     case 2: // clear
 | |
| 	s->intregm_enabled &= ~val;
 | |
| 	break;
 | |
|     case 3: // set
 | |
| 	s->intregm_enabled |= val;
 | |
| 	break;
 | |
|     default:
 | |
| 	break;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static CPUReadMemoryFunc *intregm_mem_read[3] = {
 | |
|     intregm_mem_readl,
 | |
|     intregm_mem_readl,
 | |
|     intregm_mem_readl,
 | |
| };
 | |
| 
 | |
| static CPUWriteMemoryFunc *intregm_mem_write[3] = {
 | |
|     intregm_mem_writel,
 | |
|     intregm_mem_writel,
 | |
|     intregm_mem_writel,
 | |
| };
 | |
| 
 | |
| void pic_info(void)
 | |
| {
 | |
|     term_printf("per-cpu: pending 0x%08x, enabled 0x%08x\n", ps->intreg_pending, ps->intreg_enabled);
 | |
|     term_printf("master: pending 0x%08x, enabled 0x%08x\n", ps->intregm_pending, ps->intregm_enabled);
 | |
| }
 | |
| 
 | |
| void irq_info(void)
 | |
| {
 | |
| #ifndef DEBUG_IRQ_COUNT
 | |
|     term_printf("irq statistic code not compiled.\n");
 | |
| #else
 | |
|     int i;
 | |
|     int64_t count;
 | |
| 
 | |
|     term_printf("IRQ statistics:\n");
 | |
|     for (i = 0; i < 32; i++) {
 | |
|         count = irq_count[i];
 | |
|         if (count > 0)
 | |
|             term_printf("%2d: %lld\n", i, count);
 | |
|     }
 | |
| #endif
 | |
| }
 | |
| 
 | |
| static const unsigned int intr_to_mask[16] = {
 | |
| 	0,	0,	0,	0,	0,	0, SUN4M_INT_ETHERNET,	0,
 | |
| 	0,	0,	0,	0,	0,	0,	0,	0,
 | |
| };
 | |
| 
 | |
| void pic_set_irq(int irq, int level)
 | |
| {
 | |
|     if (irq < 16) {
 | |
| 	unsigned int mask = intr_to_mask[irq];
 | |
| 	ps->intreg_pending |= 1 << irq;
 | |
| 	if (ps->intregm_enabled & mask) {
 | |
| 	    cpu_single_env->interrupt_index = irq;
 | |
| 	    cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HARD);
 | |
| 	}
 | |
|     }
 | |
| #ifdef DEBUG_IRQ_COUNT
 | |
|     if (level == 1)
 | |
| 	irq_count[irq]++;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| void sched_init(uint32_t addr, uint32_t addrg)
 | |
| {
 | |
|     int intreg_io_memory, intregm_io_memory;
 | |
|     SCHEDState *s;
 | |
| 
 | |
|     s = qemu_mallocz(sizeof(SCHEDState));
 | |
|     if (!s)
 | |
|         return;
 | |
|     s->addr = addr;
 | |
|     s->addrg = addrg;
 | |
| 
 | |
|     intreg_io_memory = cpu_register_io_memory(0, intreg_mem_read, intreg_mem_write, s);
 | |
|     cpu_register_physical_memory(addr, 3, intreg_io_memory);
 | |
| 
 | |
|     intregm_io_memory = cpu_register_io_memory(0, intregm_mem_read, intregm_mem_write, s);
 | |
|     cpu_register_physical_memory(addrg, 5, intregm_io_memory);
 | |
| 
 | |
|     ps = s;
 | |
| }
 | |
| 
 |