1 /* 2 * ColdFire Interrupt Controller emulation. 3 * 4 * Copyright (c) 2007 CodeSourcery. 5 * 6 * This code is licensed under the GPL 7 */ 8 #include "qemu/osdep.h" 9 #include "qemu-common.h" 10 #include "cpu.h" 11 #include "hw/hw.h" 12 #include "hw/m68k/mcf.h" 13 #include "exec/address-spaces.h" 14 15 typedef struct { 16 MemoryRegion iomem; 17 uint64_t ipr; 18 uint64_t imr; 19 uint64_t ifr; 20 uint64_t enabled; 21 uint8_t icr[64]; 22 M68kCPU *cpu; 23 int active_vector; 24 } mcf_intc_state; 25 26 static void mcf_intc_update(mcf_intc_state *s) 27 { 28 uint64_t active; 29 int i; 30 int best; 31 int best_level; 32 33 active = (s->ipr | s->ifr) & s->enabled & ~s->imr; 34 best_level = 0; 35 best = 64; 36 if (active) { 37 for (i = 0; i < 64; i++) { 38 if ((active & 1) != 0 && s->icr[i] >= best_level) { 39 best_level = s->icr[i]; 40 best = i; 41 } 42 active >>= 1; 43 } 44 } 45 s->active_vector = ((best == 64) ? 24 : (best + 64)); 46 m68k_set_irq_level(s->cpu, best_level, s->active_vector); 47 } 48 49 static uint64_t mcf_intc_read(void *opaque, hwaddr addr, 50 unsigned size) 51 { 52 int offset; 53 mcf_intc_state *s = (mcf_intc_state *)opaque; 54 offset = addr & 0xff; 55 if (offset >= 0x40 && offset < 0x80) { 56 return s->icr[offset - 0x40]; 57 } 58 switch (offset) { 59 case 0x00: 60 return (uint32_t)(s->ipr >> 32); 61 case 0x04: 62 return (uint32_t)s->ipr; 63 case 0x08: 64 return (uint32_t)(s->imr >> 32); 65 case 0x0c: 66 return (uint32_t)s->imr; 67 case 0x10: 68 return (uint32_t)(s->ifr >> 32); 69 case 0x14: 70 return (uint32_t)s->ifr; 71 case 0xe0: /* SWIACK. */ 72 return s->active_vector; 73 case 0xe1: case 0xe2: case 0xe3: case 0xe4: 74 case 0xe5: case 0xe6: case 0xe7: 75 /* LnIACK */ 76 hw_error("mcf_intc_read: LnIACK not implemented\n"); 77 default: 78 return 0; 79 } 80 } 81 82 static void mcf_intc_write(void *opaque, hwaddr addr, 83 uint64_t val, unsigned size) 84 { 85 int offset; 86 mcf_intc_state *s = (mcf_intc_state *)opaque; 87 offset = addr & 0xff; 88 if (offset >= 0x40 && offset < 0x80) { 89 int n = offset - 0x40; 90 s->icr[n] = val; 91 if (val == 0) 92 s->enabled &= ~(1ull << n); 93 else 94 s->enabled |= (1ull << n); 95 mcf_intc_update(s); 96 return; 97 } 98 switch (offset) { 99 case 0x00: case 0x04: 100 /* Ignore IPR writes. */ 101 return; 102 case 0x08: 103 s->imr = (s->imr & 0xffffffff) | ((uint64_t)val << 32); 104 break; 105 case 0x0c: 106 s->imr = (s->imr & 0xffffffff00000000ull) | (uint32_t)val; 107 break; 108 case 0x1c: 109 if (val & 0x40) { 110 s->imr = ~0ull; 111 } else { 112 s->imr |= (0x1ull << (val & 0x3f)); 113 } 114 break; 115 case 0x1d: 116 if (val & 0x40) { 117 s->imr = 0ull; 118 } else { 119 s->imr &= ~(0x1ull << (val & 0x3f)); 120 } 121 break; 122 default: 123 hw_error("mcf_intc_write: Bad write offset %d\n", offset); 124 break; 125 } 126 mcf_intc_update(s); 127 } 128 129 static void mcf_intc_set_irq(void *opaque, int irq, int level) 130 { 131 mcf_intc_state *s = (mcf_intc_state *)opaque; 132 if (irq >= 64) 133 return; 134 if (level) 135 s->ipr |= 1ull << irq; 136 else 137 s->ipr &= ~(1ull << irq); 138 mcf_intc_update(s); 139 } 140 141 static void mcf_intc_reset(mcf_intc_state *s) 142 { 143 s->imr = ~0ull; 144 s->ipr = 0; 145 s->ifr = 0; 146 s->enabled = 0; 147 memset(s->icr, 0, 64); 148 s->active_vector = 24; 149 } 150 151 static const MemoryRegionOps mcf_intc_ops = { 152 .read = mcf_intc_read, 153 .write = mcf_intc_write, 154 .endianness = DEVICE_NATIVE_ENDIAN, 155 }; 156 157 qemu_irq *mcf_intc_init(MemoryRegion *sysmem, 158 hwaddr base, 159 M68kCPU *cpu) 160 { 161 mcf_intc_state *s; 162 163 s = g_malloc0(sizeof(mcf_intc_state)); 164 s->cpu = cpu; 165 mcf_intc_reset(s); 166 167 memory_region_init_io(&s->iomem, NULL, &mcf_intc_ops, s, "mcf", 0x100); 168 memory_region_add_subregion(sysmem, base, &s->iomem); 169 170 return qemu_allocate_irqs(mcf_intc_set_irq, s, 64); 171 } 172