]>
Commit | Line | Data |
---|---|---|
5fafdf24 | 1 | /* |
20dcee94 PB |
2 | * Motorola ColdFire MCF5208 SoC emulation. |
3 | * | |
4 | * Copyright (c) 2007 CodeSourcery. | |
5 | * | |
8e31bf38 | 6 | * This code is licensed under the GPL |
20dcee94 | 7 | */ |
64552b6b | 8 | |
d8416665 | 9 | #include "qemu/osdep.h" |
4dab9c73 | 10 | #include "qemu/units.h" |
45876e91 | 11 | #include "qemu/error-report.h" |
db725815 | 12 | #include "qemu/main-loop.h" |
da34e65c | 13 | #include "qapi/error.h" |
4771d756 PB |
14 | #include "qemu-common.h" |
15 | #include "cpu.h" | |
83c9f4ca | 16 | #include "hw/hw.h" |
64552b6b | 17 | #include "hw/irq.h" |
0d09e41a | 18 | #include "hw/m68k/mcf.h" |
6ac38ed4 | 19 | #include "hw/m68k/mcf_fec.h" |
1de7afc9 | 20 | #include "qemu/timer.h" |
83c9f4ca | 21 | #include "hw/ptimer.h" |
9c17d615 | 22 | #include "sysemu/sysemu.h" |
5c12762c | 23 | #include "sysemu/qtest.h" |
1422e32d | 24 | #include "net/net.h" |
83c9f4ca PB |
25 | #include "hw/boards.h" |
26 | #include "hw/loader.h" | |
6ac38ed4 | 27 | #include "hw/sysbus.h" |
ca20cf32 | 28 | #include "elf.h" |
022c62cb | 29 | #include "exec/address-spaces.h" |
20dcee94 | 30 | |
cbf061bd | 31 | #define SYS_FREQ 166666666 |
20dcee94 | 32 | |
9f04e1d9 TH |
33 | #define ROM_SIZE 0x200000 |
34 | ||
20dcee94 PB |
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 { | |
c378b364 | 46 | MemoryRegion iomem; |
20dcee94 PB |
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 | ||
a8170e5e | 62 | static void m5208_timer_write(void *opaque, hwaddr offset, |
c378b364 | 63 | uint64_t value, unsigned size) |
20dcee94 | 64 | { |
8da3ff18 | 65 | m5208_timer_state *s = (m5208_timer_state *)opaque; |
20dcee94 PB |
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) | |
20dcee94 | 90 | limit = s->pmr; |
6d9db39c PB |
91 | else |
92 | limit = 0xffff; | |
20dcee94 PB |
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; | |
6d9db39c PB |
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 | } | |
20dcee94 PB |
107 | break; |
108 | case 4: | |
109 | break; | |
110 | default: | |
2ac71179 | 111 | hw_error("m5208_timer_write: Bad offset 0x%x\n", (int)offset); |
8da3ff18 | 112 | break; |
20dcee94 PB |
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 | ||
a8170e5e | 124 | static uint64_t m5208_timer_read(void *opaque, hwaddr addr, |
c378b364 | 125 | unsigned size) |
8da3ff18 PB |
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: | |
2ac71179 | 136 | hw_error("m5208_timer_read: Bad offset 0x%x\n", (int)addr); |
8da3ff18 PB |
137 | return 0; |
138 | } | |
139 | } | |
140 | ||
c378b364 AK |
141 | static const MemoryRegionOps m5208_timer_ops = { |
142 | .read = m5208_timer_read, | |
143 | .write = m5208_timer_write, | |
144 | .endianness = DEVICE_NATIVE_ENDIAN, | |
8da3ff18 PB |
145 | }; |
146 | ||
a8170e5e | 147 | static uint64_t m5208_sys_read(void *opaque, hwaddr addr, |
c378b364 | 148 | unsigned size) |
20dcee94 | 149 | { |
20dcee94 | 150 | switch (addr) { |
8da3ff18 | 151 | case 0x110: /* SDCS0 */ |
20dcee94 PB |
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 | } | |
8da3ff18 | 160 | case 0x114: /* SDCS1 */ |
20dcee94 PB |
161 | return 0; |
162 | ||
163 | default: | |
2ac71179 | 164 | hw_error("m5208_sys_read: Bad offset 0x%x\n", (int)addr); |
20dcee94 PB |
165 | return 0; |
166 | } | |
167 | } | |
168 | ||
a8170e5e | 169 | static void m5208_sys_write(void *opaque, hwaddr addr, |
c378b364 | 170 | uint64_t value, unsigned size) |
20dcee94 | 171 | { |
2ac71179 | 172 | hw_error("m5208_sys_write: Bad offset 0x%x\n", (int)addr); |
20dcee94 PB |
173 | } |
174 | ||
c378b364 AK |
175 | static const MemoryRegionOps m5208_sys_ops = { |
176 | .read = m5208_sys_read, | |
177 | .write = m5208_sys_write, | |
178 | .endianness = DEVICE_NATIVE_ENDIAN, | |
20dcee94 PB |
179 | }; |
180 | ||
c378b364 | 181 | static void mcf5208_sys_init(MemoryRegion *address_space, qemu_irq *pic) |
20dcee94 | 182 | { |
c378b364 | 183 | MemoryRegion *iomem = g_new(MemoryRegion, 1); |
8da3ff18 | 184 | m5208_timer_state *s; |
20dcee94 PB |
185 | QEMUBH *bh; |
186 | int i; | |
187 | ||
20dcee94 | 188 | /* SDRAMC. */ |
2c9b15ca | 189 | memory_region_init_io(iomem, NULL, &m5208_sys_ops, NULL, "m5208-sys", 0x00004000); |
c378b364 | 190 | memory_region_add_subregion(address_space, 0xfc0a8000, iomem); |
20dcee94 PB |
191 | /* Timers. */ |
192 | for (i = 0; i < 2; i++) { | |
d3c92188 | 193 | s = g_new0(m5208_timer_state, 1); |
8da3ff18 | 194 | bh = qemu_bh_new(m5208_timer_trigger, s); |
e7ea81c3 | 195 | s->timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT); |
2c9b15ca | 196 | memory_region_init_io(&s->iomem, NULL, &m5208_timer_ops, s, |
c378b364 AK |
197 | "m5208-timer", 0x00004000); |
198 | memory_region_add_subregion(address_space, 0xfc080000 + 0x4000 * i, | |
199 | &s->iomem); | |
8da3ff18 | 200 | s->irq = pic[4 + i]; |
20dcee94 PB |
201 | } |
202 | } | |
203 | ||
6ac38ed4 TH |
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 | ||
3ef96221 | 224 | static void mcf5208evb_init(MachineState *machine) |
20dcee94 | 225 | { |
3ef96221 | 226 | ram_addr_t ram_size = machine->ram_size; |
3ef96221 | 227 | const char *kernel_filename = machine->kernel_filename; |
9a6ee9fd | 228 | M68kCPU *cpu; |
7927df3a | 229 | CPUM68KState *env; |
20dcee94 PB |
230 | int kernel_size; |
231 | uint64_t elf_entry; | |
a8170e5e | 232 | hwaddr entry; |
20dcee94 | 233 | qemu_irq *pic; |
c378b364 | 234 | MemoryRegion *address_space_mem = get_system_memory(); |
9f04e1d9 | 235 | MemoryRegion *rom = g_new(MemoryRegion, 1); |
c378b364 AK |
236 | MemoryRegion *ram = g_new(MemoryRegion, 1); |
237 | MemoryRegion *sram = g_new(MemoryRegion, 1); | |
20dcee94 | 238 | |
ddbcc16f | 239 | cpu = M68K_CPU(cpu_create(machine->cpu_type)); |
9a6ee9fd | 240 | env = &cpu->env; |
20dcee94 PB |
241 | |
242 | /* Initialize CPU registers. */ | |
243 | env->vbr = 0; | |
244 | /* TODO: Configure BARs. */ | |
245 | ||
9f04e1d9 TH |
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 | ||
dcac9679 | 250 | /* DRAM at 0x40000000 */ |
1d5bfde1 | 251 | memory_region_allocate_system_memory(ram, NULL, "mcf5208.ram", ram_size); |
c378b364 | 252 | memory_region_add_subregion(address_space_mem, 0x40000000, ram); |
20dcee94 PB |
253 | |
254 | /* Internal SRAM. */ | |
4dab9c73 | 255 | memory_region_init_ram(sram, NULL, "mcf5208.sram", 16 * KiB, &error_fatal); |
c378b364 | 256 | memory_region_add_subregion(address_space_mem, 0x80000000, sram); |
20dcee94 PB |
257 | |
258 | /* Internal peripherals. */ | |
9a6ee9fd | 259 | pic = mcf_intc_init(address_space_mem, 0xfc048000, cpu); |
20dcee94 | 260 | |
9bca0edb PM |
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)); | |
20dcee94 | 264 | |
c378b364 | 265 | mcf5208_sys_init(address_space_mem, pic); |
20dcee94 | 266 | |
7e049b8a | 267 | if (nb_nics > 1) { |
45876e91 | 268 | error_report("Too many NICs"); |
7e049b8a PB |
269 | exit(1); |
270 | } | |
6ac38ed4 | 271 | if (nd_table[0].used) { |
c65fc1df BC |
272 | mcf_fec_init(address_space_mem, &nd_table[0], |
273 | 0xfc030000, pic + 36); | |
6ac38ed4 | 274 | } |
7e049b8a | 275 | |
20dcee94 PB |
276 | /* 0xfc000000 SCM. */ |
277 | /* 0xfc004000 XBS. */ | |
278 | /* 0xfc008000 FlexBus CS. */ | |
7e049b8a | 279 | /* 0xfc030000 FEC. */ |
20dcee94 PB |
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 | ||
9f04e1d9 TH |
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 | ||
20dcee94 PB |
319 | /* Load kernel. */ |
320 | if (!kernel_filename) { | |
9f04e1d9 | 321 | if (qtest_enabled() || bios_name) { |
5c12762c AF |
322 | return; |
323 | } | |
45876e91 | 324 | error_report("Kernel image must be specified"); |
20dcee94 PB |
325 | exit(1); |
326 | } | |
327 | ||
4366e1db | 328 | kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &elf_entry, |
7ef295ea | 329 | NULL, NULL, 1, EM_68K, 0, 0); |
20dcee94 PB |
330 | entry = elf_entry; |
331 | if (kernel_size < 0) { | |
25bda50a MF |
332 | kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL, |
333 | NULL, NULL); | |
20dcee94 PB |
334 | } |
335 | if (kernel_size < 0) { | |
dcac9679 PB |
336 | kernel_size = load_image_targphys(kernel_filename, 0x40000000, |
337 | ram_size); | |
338 | entry = 0x40000000; | |
20dcee94 PB |
339 | } |
340 | if (kernel_size < 0) { | |
45876e91 | 341 | error_report("Could not load kernel '%s'", kernel_filename); |
20dcee94 PB |
342 | exit(1); |
343 | } | |
344 | ||
345 | env->pc = entry; | |
346 | } | |
347 | ||
e264d29d | 348 | static void mcf5208evb_machine_init(MachineClass *mc) |
f80f9ec9 | 349 | { |
83dc62f6 | 350 | mc->desc = "MCF5208EVB"; |
e264d29d EH |
351 | mc->init = mcf5208evb_init; |
352 | mc->is_default = 1; | |
ddbcc16f | 353 | mc->default_cpu_type = M68K_CPU_TYPE_NAME("m5208"); |
f80f9ec9 AL |
354 | } |
355 | ||
e264d29d | 356 | DEFINE_MACHINE("mcf5208evb", mcf5208evb_machine_init) |