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