1 | /* |
2 | * Motorola ColdFire MCF5208 SoC emulation. |
3 | * |
4 | * Copyright (c) 2007 CodeSourcery. |
5 | * |
6 | * This code is licensed under the GPL |
7 | */ |
8 | |
9 | #include "qemu/osdep.h" |
10 | #include "qemu/units.h" |
11 | #include "qemu/error-report.h" |
12 | #include "qemu/main-loop.h" |
13 | #include "qapi/error.h" |
14 | #include "qemu-common.h" |
15 | #include "cpu.h" |
16 | #include "hw/hw.h" |
17 | #include "hw/irq.h" |
18 | #include "hw/m68k/mcf.h" |
19 | #include "hw/m68k/mcf_fec.h" |
20 | #include "qemu/timer.h" |
21 | #include "hw/ptimer.h" |
22 | #include "sysemu/sysemu.h" |
23 | #include "sysemu/qtest.h" |
24 | #include "net/net.h" |
25 | #include "hw/boards.h" |
26 | #include "hw/loader.h" |
27 | #include "hw/sysbus.h" |
28 | #include "elf.h" |
29 | #include "exec/address-spaces.h" |
30 | |
31 | #define SYS_FREQ 166666666 |
32 | |
33 | #define ROM_SIZE 0x200000 |
34 | |
35 | #define PCSR_EN 0x0001 |
36 | #define PCSR_RLD 0x0002 |
37 | #define PCSR_PIF 0x0004 |
38 | #define PCSR_PIE 0x0008 |
39 | #define PCSR_OVW 0x0010 |
40 | #define PCSR_DBG 0x0020 |
41 | #define PCSR_DOZE 0x0040 |
42 | #define PCSR_PRE_SHIFT 8 |
43 | #define PCSR_PRE_MASK 0x0f00 |
44 | |
45 | typedef struct { |
46 | MemoryRegion iomem; |
47 | qemu_irq irq; |
48 | ptimer_state *timer; |
49 | uint16_t pcsr; |
50 | uint16_t pmr; |
51 | uint16_t pcntr; |
52 | } m5208_timer_state; |
53 | |
54 | static void m5208_timer_update(m5208_timer_state *s) |
55 | { |
56 | if ((s->pcsr & (PCSR_PIE | PCSR_PIF)) == (PCSR_PIE | PCSR_PIF)) |
57 | qemu_irq_raise(s->irq); |
58 | else |
59 | qemu_irq_lower(s->irq); |
60 | } |
61 | |
62 | static void m5208_timer_write(void *opaque, hwaddr offset, |
63 | uint64_t value, unsigned size) |
64 | { |
65 | m5208_timer_state *s = (m5208_timer_state *)opaque; |
66 | int prescale; |
67 | int limit; |
68 | switch (offset) { |
69 | case 0: |
70 | /* The PIF bit is set-to-clear. */ |
71 | if (value & PCSR_PIF) { |
72 | s->pcsr &= ~PCSR_PIF; |
73 | value &= ~PCSR_PIF; |
74 | } |
75 | /* Avoid frobbing the timer if we're just twiddling IRQ bits. */ |
76 | if (((s->pcsr ^ value) & ~PCSR_PIE) == 0) { |
77 | s->pcsr = value; |
78 | m5208_timer_update(s); |
79 | return; |
80 | } |
81 | |
82 | if (s->pcsr & PCSR_EN) |
83 | ptimer_stop(s->timer); |
84 | |
85 | s->pcsr = value; |
86 | |
87 | prescale = 1 << ((s->pcsr & PCSR_PRE_MASK) >> PCSR_PRE_SHIFT); |
88 | ptimer_set_freq(s->timer, (SYS_FREQ / 2) / prescale); |
89 | if (s->pcsr & PCSR_RLD) |
90 | limit = s->pmr; |
91 | else |
92 | limit = 0xffff; |
93 | ptimer_set_limit(s->timer, limit, 0); |
94 | |
95 | if (s->pcsr & PCSR_EN) |
96 | ptimer_run(s->timer, 0); |
97 | break; |
98 | case 2: |
99 | s->pmr = value; |
100 | s->pcsr &= ~PCSR_PIF; |
101 | if ((s->pcsr & PCSR_RLD) == 0) { |
102 | if (s->pcsr & PCSR_OVW) |
103 | ptimer_set_count(s->timer, value); |
104 | } else { |
105 | ptimer_set_limit(s->timer, value, s->pcsr & PCSR_OVW); |
106 | } |
107 | break; |
108 | case 4: |
109 | break; |
110 | default: |
111 | hw_error("m5208_timer_write: Bad offset 0x%x\n" , (int)offset); |
112 | break; |
113 | } |
114 | m5208_timer_update(s); |
115 | } |
116 | |
117 | static void m5208_timer_trigger(void *opaque) |
118 | { |
119 | m5208_timer_state *s = (m5208_timer_state *)opaque; |
120 | s->pcsr |= PCSR_PIF; |
121 | m5208_timer_update(s); |
122 | } |
123 | |
124 | static uint64_t m5208_timer_read(void *opaque, hwaddr addr, |
125 | unsigned size) |
126 | { |
127 | m5208_timer_state *s = (m5208_timer_state *)opaque; |
128 | switch (addr) { |
129 | case 0: |
130 | return s->pcsr; |
131 | case 2: |
132 | return s->pmr; |
133 | case 4: |
134 | return ptimer_get_count(s->timer); |
135 | default: |
136 | hw_error("m5208_timer_read: Bad offset 0x%x\n" , (int)addr); |
137 | return 0; |
138 | } |
139 | } |
140 | |
141 | static const MemoryRegionOps m5208_timer_ops = { |
142 | .read = m5208_timer_read, |
143 | .write = m5208_timer_write, |
144 | .endianness = DEVICE_NATIVE_ENDIAN, |
145 | }; |
146 | |
147 | static uint64_t m5208_sys_read(void *opaque, hwaddr addr, |
148 | unsigned size) |
149 | { |
150 | switch (addr) { |
151 | case 0x110: /* SDCS0 */ |
152 | { |
153 | int n; |
154 | for (n = 0; n < 32; n++) { |
155 | if (ram_size < (2u << n)) |
156 | break; |
157 | } |
158 | return (n - 1) | 0x40000000; |
159 | } |
160 | case 0x114: /* SDCS1 */ |
161 | return 0; |
162 | |
163 | default: |
164 | hw_error("m5208_sys_read: Bad offset 0x%x\n" , (int)addr); |
165 | return 0; |
166 | } |
167 | } |
168 | |
169 | static void m5208_sys_write(void *opaque, hwaddr addr, |
170 | uint64_t value, unsigned size) |
171 | { |
172 | hw_error("m5208_sys_write: Bad offset 0x%x\n" , (int)addr); |
173 | } |
174 | |
175 | static const MemoryRegionOps m5208_sys_ops = { |
176 | .read = m5208_sys_read, |
177 | .write = m5208_sys_write, |
178 | .endianness = DEVICE_NATIVE_ENDIAN, |
179 | }; |
180 | |
181 | static void mcf5208_sys_init(MemoryRegion *address_space, qemu_irq *pic) |
182 | { |
183 | MemoryRegion *iomem = g_new(MemoryRegion, 1); |
184 | m5208_timer_state *s; |
185 | QEMUBH *bh; |
186 | int i; |
187 | |
188 | /* SDRAMC. */ |
189 | memory_region_init_io(iomem, NULL, &m5208_sys_ops, NULL, "m5208-sys" , 0x00004000); |
190 | memory_region_add_subregion(address_space, 0xfc0a8000, iomem); |
191 | /* Timers. */ |
192 | for (i = 0; i < 2; i++) { |
193 | s = g_new0(m5208_timer_state, 1); |
194 | bh = qemu_bh_new(m5208_timer_trigger, s); |
195 | s->timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT); |
196 | memory_region_init_io(&s->iomem, NULL, &m5208_timer_ops, s, |
197 | "m5208-timer" , 0x00004000); |
198 | memory_region_add_subregion(address_space, 0xfc080000 + 0x4000 * i, |
199 | &s->iomem); |
200 | s->irq = pic[4 + i]; |
201 | } |
202 | } |
203 | |
204 | static void mcf_fec_init(MemoryRegion *sysmem, NICInfo *nd, hwaddr base, |
205 | qemu_irq *irqs) |
206 | { |
207 | DeviceState *dev; |
208 | SysBusDevice *s; |
209 | int i; |
210 | |
211 | qemu_check_nic_model(nd, TYPE_MCF_FEC_NET); |
212 | dev = qdev_create(NULL, TYPE_MCF_FEC_NET); |
213 | qdev_set_nic_properties(dev, nd); |
214 | qdev_init_nofail(dev); |
215 | |
216 | s = SYS_BUS_DEVICE(dev); |
217 | for (i = 0; i < FEC_NUM_IRQ; i++) { |
218 | sysbus_connect_irq(s, i, irqs[i]); |
219 | } |
220 | |
221 | memory_region_add_subregion(sysmem, base, sysbus_mmio_get_region(s, 0)); |
222 | } |
223 | |
224 | static void mcf5208evb_init(MachineState *machine) |
225 | { |
226 | ram_addr_t ram_size = machine->ram_size; |
227 | const char *kernel_filename = machine->kernel_filename; |
228 | M68kCPU *cpu; |
229 | CPUM68KState *env; |
230 | int kernel_size; |
231 | uint64_t elf_entry; |
232 | hwaddr entry; |
233 | qemu_irq *pic; |
234 | MemoryRegion *address_space_mem = get_system_memory(); |
235 | MemoryRegion *rom = g_new(MemoryRegion, 1); |
236 | MemoryRegion *ram = g_new(MemoryRegion, 1); |
237 | MemoryRegion *sram = g_new(MemoryRegion, 1); |
238 | |
239 | cpu = M68K_CPU(cpu_create(machine->cpu_type)); |
240 | env = &cpu->env; |
241 | |
242 | /* Initialize CPU registers. */ |
243 | env->vbr = 0; |
244 | /* TODO: Configure BARs. */ |
245 | |
246 | /* ROM at 0x00000000 */ |
247 | memory_region_init_rom(rom, NULL, "mcf5208.rom" , ROM_SIZE, &error_fatal); |
248 | memory_region_add_subregion(address_space_mem, 0x00000000, rom); |
249 | |
250 | /* DRAM at 0x40000000 */ |
251 | memory_region_allocate_system_memory(ram, NULL, "mcf5208.ram" , ram_size); |
252 | memory_region_add_subregion(address_space_mem, 0x40000000, ram); |
253 | |
254 | /* Internal SRAM. */ |
255 | memory_region_init_ram(sram, NULL, "mcf5208.sram" , 16 * KiB, &error_fatal); |
256 | memory_region_add_subregion(address_space_mem, 0x80000000, sram); |
257 | |
258 | /* Internal peripherals. */ |
259 | pic = mcf_intc_init(address_space_mem, 0xfc048000, cpu); |
260 | |
261 | mcf_uart_mm_init(0xfc060000, pic[26], serial_hd(0)); |
262 | mcf_uart_mm_init(0xfc064000, pic[27], serial_hd(1)); |
263 | mcf_uart_mm_init(0xfc068000, pic[28], serial_hd(2)); |
264 | |
265 | mcf5208_sys_init(address_space_mem, pic); |
266 | |
267 | if (nb_nics > 1) { |
268 | error_report("Too many NICs" ); |
269 | exit(1); |
270 | } |
271 | if (nd_table[0].used) { |
272 | mcf_fec_init(address_space_mem, &nd_table[0], |
273 | 0xfc030000, pic + 36); |
274 | } |
275 | |
276 | /* 0xfc000000 SCM. */ |
277 | /* 0xfc004000 XBS. */ |
278 | /* 0xfc008000 FlexBus CS. */ |
279 | /* 0xfc030000 FEC. */ |
280 | /* 0xfc040000 SCM + Power management. */ |
281 | /* 0xfc044000 eDMA. */ |
282 | /* 0xfc048000 INTC. */ |
283 | /* 0xfc058000 I2C. */ |
284 | /* 0xfc05c000 QSPI. */ |
285 | /* 0xfc060000 UART0. */ |
286 | /* 0xfc064000 UART0. */ |
287 | /* 0xfc068000 UART0. */ |
288 | /* 0xfc070000 DMA timers. */ |
289 | /* 0xfc080000 PIT0. */ |
290 | /* 0xfc084000 PIT1. */ |
291 | /* 0xfc088000 EPORT. */ |
292 | /* 0xfc08c000 Watchdog. */ |
293 | /* 0xfc090000 clock module. */ |
294 | /* 0xfc0a0000 CCM + reset. */ |
295 | /* 0xfc0a4000 GPIO. */ |
296 | /* 0xfc0a8000 SDRAM controller. */ |
297 | |
298 | /* Load firmware */ |
299 | if (bios_name) { |
300 | char *fn; |
301 | uint8_t *ptr; |
302 | |
303 | fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); |
304 | if (!fn) { |
305 | error_report("Could not find ROM image '%s'" , bios_name); |
306 | exit(1); |
307 | } |
308 | if (load_image_targphys(fn, 0x0, ROM_SIZE) < 8) { |
309 | error_report("Could not load ROM image '%s'" , bios_name); |
310 | exit(1); |
311 | } |
312 | g_free(fn); |
313 | /* Initial PC is always at offset 4 in firmware binaries */ |
314 | ptr = rom_ptr(0x4, 4); |
315 | assert(ptr != NULL); |
316 | env->pc = ldl_p(ptr); |
317 | } |
318 | |
319 | /* Load kernel. */ |
320 | if (!kernel_filename) { |
321 | if (qtest_enabled() || bios_name) { |
322 | return; |
323 | } |
324 | error_report("Kernel image must be specified" ); |
325 | exit(1); |
326 | } |
327 | |
328 | kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &elf_entry, |
329 | NULL, NULL, 1, EM_68K, 0, 0); |
330 | entry = elf_entry; |
331 | if (kernel_size < 0) { |
332 | kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL, |
333 | NULL, NULL); |
334 | } |
335 | if (kernel_size < 0) { |
336 | kernel_size = load_image_targphys(kernel_filename, 0x40000000, |
337 | ram_size); |
338 | entry = 0x40000000; |
339 | } |
340 | if (kernel_size < 0) { |
341 | error_report("Could not load kernel '%s'" , kernel_filename); |
342 | exit(1); |
343 | } |
344 | |
345 | env->pc = entry; |
346 | } |
347 | |
348 | static void mcf5208evb_machine_init(MachineClass *mc) |
349 | { |
350 | mc->desc = "MCF5208EVB" ; |
351 | mc->init = mcf5208evb_init; |
352 | mc->is_default = 1; |
353 | mc->default_cpu_type = M68K_CPU_TYPE_NAME("m5208" ); |
354 | } |
355 | |
356 | DEFINE_MACHINE("mcf5208evb" , mcf5208evb_machine_init) |
357 | |