Initialize
[sdk/emulator/qemu.git] / hw / mpcore.c
1 /*
2  * ARM MPCore internal peripheral emulation (common code).
3  *
4  * Copyright (c) 2006-2007 CodeSourcery.
5  * Written by Paul Brook
6  *
7  * This code is licenced under the GPL.
8  */
9
10 #include "sysbus.h"
11 #include "qemu-timer.h"
12
13 #define NCPU 4
14
15 static inline int
16 gic_get_current_cpu(void)
17 {
18   return cpu_single_env->cpu_index;
19 }
20
21 #include "arm_gic.c"
22
23 /* MPCore private memory region.  */
24
25 typedef struct {
26     uint32_t count;
27     uint32_t load;
28     uint32_t control;
29     uint32_t status;
30     uint32_t old_status;
31     int64_t tick;
32     QEMUTimer *timer;
33     struct mpcore_priv_state *mpcore;
34     int id; /* Encodes both timer/watchdog and CPU.  */
35 } mpcore_timer_state;
36
37 typedef struct mpcore_priv_state {
38     gic_state gic;
39     uint32_t scu_control;
40     int iomemtype;
41     mpcore_timer_state timer[8];
42     uint32_t num_cpu;
43 } mpcore_priv_state;
44
45 /* Per-CPU Timers.  */
46
47 static inline void mpcore_timer_update_irq(mpcore_timer_state *s)
48 {
49     if (s->status & ~s->old_status) {
50         gic_set_pending_private(&s->mpcore->gic, s->id >> 1, 29 + (s->id & 1));
51     }
52     s->old_status = s->status;
53 }
54
55 /* Return conversion factor from mpcore timer ticks to qemu timer ticks.  */
56 static inline uint32_t mpcore_timer_scale(mpcore_timer_state *s)
57 {
58     return (((s->control >> 8) & 0xff) + 1) * 10;
59 }
60
61 static void mpcore_timer_reload(mpcore_timer_state *s, int restart)
62 {
63     if (s->count == 0)
64         return;
65     if (restart)
66         s->tick = qemu_get_clock(vm_clock);
67     s->tick += (int64_t)s->count * mpcore_timer_scale(s);
68     qemu_mod_timer(s->timer, s->tick);
69 }
70
71 static void mpcore_timer_tick(void *opaque)
72 {
73     mpcore_timer_state *s = (mpcore_timer_state *)opaque;
74     s->status = 1;
75     if (s->control & 2) {
76         s->count = s->load;
77         mpcore_timer_reload(s, 0);
78     } else {
79         s->count = 0;
80     }
81     mpcore_timer_update_irq(s);
82 }
83
84 static uint32_t mpcore_timer_read(mpcore_timer_state *s, int offset)
85 {
86     int64_t val;
87     switch (offset) {
88     case 0: /* Load */
89         return s->load;
90         /* Fall through.  */
91     case 4: /* Counter.  */
92         if (((s->control & 1) == 0) || (s->count == 0))
93             return 0;
94         /* Slow and ugly, but hopefully won't happen too often.  */
95         val = s->tick - qemu_get_clock(vm_clock);
96         val /= mpcore_timer_scale(s);
97         if (val < 0)
98             val = 0;
99         return val;
100     case 8: /* Control.  */
101         return s->control;
102     case 12: /* Interrupt status.  */
103         return s->status;
104     default:
105         return 0;
106     }
107 }
108
109 static void mpcore_timer_write(mpcore_timer_state *s, int offset,
110                                uint32_t value)
111 {
112     int64_t old;
113     switch (offset) {
114     case 0: /* Load */
115         s->load = value;
116         /* Fall through.  */
117     case 4: /* Counter.  */
118         if ((s->control & 1) && s->count) {
119             /* Cancel the previous timer.  */
120             qemu_del_timer(s->timer);
121         }
122         s->count = value;
123         if (s->control & 1) {
124             mpcore_timer_reload(s, 1);
125         }
126         break;
127     case 8: /* Control.  */
128         old = s->control;
129         s->control = value;
130         if (((old & 1) == 0) && (value & 1)) {
131             if (s->count == 0 && (s->control & 2))
132                 s->count = s->load;
133             mpcore_timer_reload(s, 1);
134         }
135         break;
136     case 12: /* Interrupt status.  */
137         s->status &= ~value;
138         mpcore_timer_update_irq(s);
139         break;
140     }
141 }
142
143 static void mpcore_timer_init(mpcore_priv_state *mpcore,
144                               mpcore_timer_state *s, int id)
145 {
146     s->id = id;
147     s->mpcore = mpcore;
148     s->timer = qemu_new_timer(vm_clock, mpcore_timer_tick, s);
149 }
150
151
152 /* Per-CPU private memory mapped IO.  */
153
154 static uint32_t mpcore_priv_read(void *opaque, target_phys_addr_t offset)
155 {
156     mpcore_priv_state *s = (mpcore_priv_state *)opaque;
157     int id;
158     offset &= 0xfff;
159     if (offset < 0x100) {
160         /* SCU */
161         switch (offset) {
162         case 0x00: /* Control.  */
163             return s->scu_control;
164         case 0x04: /* Configuration.  */
165             id = ((1 << s->num_cpu) - 1) << 4;
166             return id | (s->num_cpu - 1);
167         case 0x08: /* CPU status.  */
168             return 0;
169         case 0x0c: /* Invalidate all.  */
170             return 0;
171         default:
172             goto bad_reg;
173         }
174     } else if (offset < 0x600) {
175         /* Interrupt controller.  */
176         if (offset < 0x200) {
177             id = gic_get_current_cpu();
178         } else {
179             id = (offset - 0x200) >> 8;
180             if (id >= s->num_cpu) {
181                 return 0;
182             }
183         }
184         return gic_cpu_read(&s->gic, id, offset & 0xff);
185     } else if (offset < 0xb00) {
186         /* Timers.  */
187         if (offset < 0x700) {
188             id = gic_get_current_cpu();
189         } else {
190             id = (offset - 0x700) >> 8;
191             if (id >= s->num_cpu) {
192                 return 0;
193             }
194         }
195         id <<= 1;
196         if (offset & 0x20)
197           id++;
198         return mpcore_timer_read(&s->timer[id], offset & 0xf);
199     }
200 bad_reg:
201     hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset);
202     return 0;
203 }
204
205 static void mpcore_priv_write(void *opaque, target_phys_addr_t offset,
206                           uint32_t value)
207 {
208     mpcore_priv_state *s = (mpcore_priv_state *)opaque;
209     int id;
210     offset &= 0xfff;
211     if (offset < 0x100) {
212         /* SCU */
213         switch (offset) {
214         case 0: /* Control register.  */
215             s->scu_control = value & 1;
216             break;
217         case 0x0c: /* Invalidate all.  */
218             /* This is a no-op as cache is not emulated.  */
219             break;
220         default:
221             goto bad_reg;
222         }
223     } else if (offset < 0x600) {
224         /* Interrupt controller.  */
225         if (offset < 0x200) {
226             id = gic_get_current_cpu();
227         } else {
228             id = (offset - 0x200) >> 8;
229         }
230         if (id < s->num_cpu) {
231             gic_cpu_write(&s->gic, id, offset & 0xff, value);
232         }
233     } else if (offset < 0xb00) {
234         /* Timers.  */
235         if (offset < 0x700) {
236             id = gic_get_current_cpu();
237         } else {
238             id = (offset - 0x700) >> 8;
239         }
240         if (id < s->num_cpu) {
241             id <<= 1;
242             if (offset & 0x20)
243               id++;
244             mpcore_timer_write(&s->timer[id], offset & 0xf, value);
245         }
246         return;
247     }
248     return;
249 bad_reg:
250     hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset);
251 }
252
253 static CPUReadMemoryFunc * const mpcore_priv_readfn[] = {
254    mpcore_priv_read,
255    mpcore_priv_read,
256    mpcore_priv_read
257 };
258
259 static CPUWriteMemoryFunc * const mpcore_priv_writefn[] = {
260    mpcore_priv_write,
261    mpcore_priv_write,
262    mpcore_priv_write
263 };
264
265 static void mpcore_priv_map(SysBusDevice *dev, target_phys_addr_t base)
266 {
267     mpcore_priv_state *s = FROM_SYSBUSGIC(mpcore_priv_state, dev);
268     cpu_register_physical_memory(base, 0x1000, s->iomemtype);
269     cpu_register_physical_memory(base + 0x1000, 0x1000, s->gic.iomemtype);
270 }
271
272 static int mpcore_priv_init(SysBusDevice *dev)
273 {
274     mpcore_priv_state *s = FROM_SYSBUSGIC(mpcore_priv_state, dev);
275     int i;
276
277     gic_init(&s->gic, s->num_cpu);
278     s->iomemtype = cpu_register_io_memory(mpcore_priv_readfn,
279                                           mpcore_priv_writefn, s,
280                                           DEVICE_NATIVE_ENDIAN);
281     sysbus_init_mmio_cb(dev, 0x2000, mpcore_priv_map);
282     for (i = 0; i < s->num_cpu * 2; i++) {
283         mpcore_timer_init(s, &s->timer[i], i);
284     }
285     return 0;
286 }