]> git.proxmox.com Git - qemu.git/blame - hw/mips_malta.c
iDocument ARM RealView board (Andrzej Zaborowski).
[qemu.git] / hw / mips_malta.c
CommitLineData
5856de80
TS
1/*
2 * QEMU Malta board support
3 *
4 * Copyright (c) 2006 Aurelien Jarno
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24
25#include "vl.h"
26
44cbbf18
TS
27#ifdef TARGET_WORDS_BIGENDIAN
28#define BIOS_FILENAME "mips_bios.bin"
29#else
30#define BIOS_FILENAME "mipsel_bios.bin"
31#endif
32
5856de80 33#ifdef MIPS_HAS_MIPS64
44cbbf18 34#define INITRD_LOAD_ADDR (int64_t)0x80800000
5856de80 35#else
44cbbf18 36#define INITRD_LOAD_ADDR (int32_t)0x80800000
5856de80
TS
37#endif
38
3ddd0065 39#define ENVP_ADDR (int32_t)0x80002000
44cbbf18 40#define VIRT_TO_PHYS_ADDEND (-((int64_t)(int32_t)0x80000000))
5856de80
TS
41
42#define ENVP_NB_ENTRIES 16
43#define ENVP_ENTRY_SIZE 256
44
45
46extern FILE *logfile;
47
48typedef struct {
49 uint32_t leds;
50 uint32_t brk;
51 uint32_t gpout;
52 uint32_t i2coe;
53 uint32_t i2cout;
54 uint32_t i2csel;
55 CharDriverState *display;
56 char display_text[9];
57} MaltaFPGAState;
58
59static PITState *pit;
60
4de9b249 61/* The 8259 is attached to the MIPS CPU INT0 pin, ie interrupt 2 */
5856de80
TS
62static void pic_irq_request(void *opaque, int level)
63{
4de9b249 64 cpu_mips_irq_request(opaque, 2, level);
5856de80
TS
65}
66
67/* Malta FPGA */
68static void malta_fpga_update_display(void *opaque)
69{
70 char leds_text[9];
71 int i;
72 MaltaFPGAState *s = opaque;
73
74 for (i = 7 ; i >= 0 ; i--) {
75 if (s->leds & (1 << i))
76 leds_text[i] = '#';
77 else
78 leds_text[i] = ' ';
79 }
80 leds_text[8] = '\0';
81
472c5273
TS
82 qemu_chr_printf(s->display, "\e[H\n\n|\e[32m%-8.8s\e[00m|\r\n", leds_text);
83 qemu_chr_printf(s->display, "\n\n\n\n|\e[31m%-8.8s\e[00m|", s->display_text);
5856de80
TS
84}
85
86static uint32_t malta_fpga_readl(void *opaque, target_phys_addr_t addr)
87{
88 MaltaFPGAState *s = opaque;
89 uint32_t val = 0;
90 uint32_t saddr;
91
92 saddr = (addr & 0xfffff);
93
94 switch (saddr) {
95
96 /* SWITCH Register */
97 case 0x00200:
98 val = 0x00000000; /* All switches closed */
99 break;
100
101 /* STATUS Register */
102 case 0x00208:
103#ifdef TARGET_WORDS_BIGENDIAN
104 val = 0x00000012;
105#else
106 val = 0x00000010;
107#endif
108 break;
109
110 /* JMPRS Register */
111 case 0x00210:
112 val = 0x00;
113 break;
114
115 /* LEDBAR Register */
116 case 0x00408:
117 val = s->leds;
118 break;
119
120 /* BRKRES Register */
121 case 0x00508:
122 val = s->brk;
123 break;
124
125 /* GPOUT Register */
126 case 0x00a00:
127 val = s->gpout;
128 break;
129
130 /* XXX: implement a real I2C controller */
131
132 /* GPINP Register */
133 case 0x00a08:
134 /* IN = OUT until a real I2C control is implemented */
135 if (s->i2csel)
136 val = s->i2cout;
137 else
138 val = 0x00;
139 break;
140
141 /* I2CINP Register */
142 case 0x00b00:
143 val = 0x00000003;
144 break;
145
146 /* I2COE Register */
147 case 0x00b08:
148 val = s->i2coe;
149 break;
150
151 /* I2COUT Register */
152 case 0x00b10:
153 val = s->i2cout;
154 break;
155
156 /* I2CSEL Register */
157 case 0x00b18:
158 val = s->i2cout;
159 break;
160
161 default:
162#if 0
3594c774 163 printf ("malta_fpga_read: Bad register offset 0x" TARGET_FMT_lx "\n",
44cbbf18 164 addr);
5856de80
TS
165#endif
166 break;
167 }
168 return val;
169}
170
171static void malta_fpga_writel(void *opaque, target_phys_addr_t addr,
172 uint32_t val)
173{
174 MaltaFPGAState *s = opaque;
175 uint32_t saddr;
176
177 saddr = (addr & 0xfffff);
178
179 switch (saddr) {
180
181 /* SWITCH Register */
182 case 0x00200:
183 break;
184
185 /* JMPRS Register */
186 case 0x00210:
187 break;
188
189 /* LEDBAR Register */
190 /* XXX: implement a 8-LED array */
191 case 0x00408:
192 s->leds = val & 0xff;
193 break;
194
195 /* ASCIIWORD Register */
196 case 0x00410:
197 snprintf(s->display_text, 9, "%08X", val);
198 malta_fpga_update_display(s);
199 break;
200
201 /* ASCIIPOS0 to ASCIIPOS7 Registers */
202 case 0x00418:
203 case 0x00420:
204 case 0x00428:
205 case 0x00430:
206 case 0x00438:
207 case 0x00440:
208 case 0x00448:
209 case 0x00450:
210 s->display_text[(saddr - 0x00418) >> 3] = (char) val;
211 malta_fpga_update_display(s);
212 break;
213
214 /* SOFTRES Register */
215 case 0x00500:
216 if (val == 0x42)
217 qemu_system_reset_request ();
218 break;
219
220 /* BRKRES Register */
221 case 0x00508:
222 s->brk = val & 0xff;
223 break;
224
225 /* GPOUT Register */
226 case 0x00a00:
227 s->gpout = val & 0xff;
228 break;
229
230 /* I2COE Register */
231 case 0x00b08:
232 s->i2coe = val & 0x03;
233 break;
234
235 /* I2COUT Register */
236 case 0x00b10:
237 s->i2cout = val & 0x03;
238 break;
239
240 /* I2CSEL Register */
241 case 0x00b18:
242 s->i2cout = val & 0x01;
243 break;
244
245 default:
246#if 0
3594c774 247 printf ("malta_fpga_write: Bad register offset 0x" TARGET_FMT_lx "\n",
44cbbf18 248 addr);
5856de80
TS
249#endif
250 break;
251 }
252}
253
254static CPUReadMemoryFunc *malta_fpga_read[] = {
255 malta_fpga_readl,
256 malta_fpga_readl,
257 malta_fpga_readl
258};
259
260static CPUWriteMemoryFunc *malta_fpga_write[] = {
261 malta_fpga_writel,
262 malta_fpga_writel,
263 malta_fpga_writel
264};
265
266void malta_fpga_reset(void *opaque)
267{
268 MaltaFPGAState *s = opaque;
269
270 s->leds = 0x00;
271 s->brk = 0x0a;
272 s->gpout = 0x00;
273 s->i2coe = 0x0;
274 s->i2cout = 0x3;
275 s->i2csel = 0x1;
276
277 s->display_text[8] = '\0';
278 snprintf(s->display_text, 9, " ");
279 malta_fpga_update_display(s);
280}
281
282MaltaFPGAState *malta_fpga_init(target_phys_addr_t base)
283{
284 MaltaFPGAState *s;
285 int malta;
286
287 s = (MaltaFPGAState *)qemu_mallocz(sizeof(MaltaFPGAState));
288
289 malta = cpu_register_io_memory(0, malta_fpga_read,
290 malta_fpga_write, s);
291 cpu_register_physical_memory(base, 0x100000, malta);
292
293 s->display = qemu_chr_open("vc");
af23902b 294 qemu_chr_printf(s->display, "\e[HMalta LEDBAR\r\n");
5856de80
TS
295 qemu_chr_printf(s->display, "+--------+\r\n");
296 qemu_chr_printf(s->display, "+ +\r\n");
297 qemu_chr_printf(s->display, "+--------+\r\n");
298 qemu_chr_printf(s->display, "\n");
299 qemu_chr_printf(s->display, "Malta ASCII\r\n");
300 qemu_chr_printf(s->display, "+--------+\r\n");
301 qemu_chr_printf(s->display, "+ +\r\n");
302 qemu_chr_printf(s->display, "+--------+\r\n");
303
304 malta_fpga_reset(s);
305 qemu_register_reset(malta_fpga_reset, s);
306
307 return s;
308}
309
310/* Audio support */
311#ifdef HAS_AUDIO
312static void audio_init (PCIBus *pci_bus)
313{
314 struct soundhw *c;
315 int audio_enabled = 0;
316
317 for (c = soundhw; !audio_enabled && c->name; ++c) {
318 audio_enabled = c->enabled;
319 }
320
321 if (audio_enabled) {
322 AudioState *s;
323
324 s = AUD_init ();
325 if (s) {
326 for (c = soundhw; c->name; ++c) {
327 if (c->enabled) {
328 if (c->isa) {
329 fprintf(stderr, "qemu: Unsupported Sound Card: %s\n", c->name);
330 exit(1);
331 }
332 else {
333 if (pci_bus) {
334 c->init.init_pci (pci_bus, s);
335 }
336 }
337 }
338 }
339 }
340 }
341}
342#endif
343
344/* Network support */
345static void network_init (PCIBus *pci_bus)
346{
347 int i;
348 NICInfo *nd;
349
350 for(i = 0; i < nb_nics; i++) {
351 nd = &nd_table[i];
352 if (!nd->model) {
353 nd->model = "pcnet";
354 }
355 if (i == 0 && strcmp(nd->model, "pcnet") == 0) {
356 /* The malta board has a PCNet card using PCI SLOT 11 */
357 pci_nic_init(pci_bus, nd, 88);
358 } else {
359 pci_nic_init(pci_bus, nd, -1);
360 }
361 }
362}
363
364/* ROM and pseudo bootloader
365
366 The following code implements a very very simple bootloader. It first
367 loads the registers a0 to a3 to the values expected by the OS, and
368 then jump at the kernel address.
369
370 The bootloader should pass the locations of the kernel arguments and
371 environment variables tables. Those tables contain the 32-bit address
372 of NULL terminated strings. The environment variables table should be
373 terminated by a NULL address.
374
375 For a simpler implementation, the number of kernel arguments is fixed
376 to two (the name of the kernel and the command line), and the two
377 tables are actually the same one.
378
379 The registers a0 to a3 should contain the following values:
380 a0 - number of kernel arguments
381 a1 - 32-bit address of the kernel arguments table
382 a2 - 32-bit address of the environment variables table
383 a3 - RAM size in bytes
384*/
385
386static void write_bootloader (CPUState *env, unsigned long bios_offset, int64_t kernel_addr)
387{
388 uint32_t *p;
389
390 /* Small bootloader */
391 p = (uint32_t *) (phys_ram_base + bios_offset);
3ddd0065
TS
392 stl_raw(p++, 0x0bf00010); /* j 0x1fc00040 */
393 stl_raw(p++, 0x00000000); /* nop */
5856de80
TS
394
395 /* Second part of the bootloader */
396 p = (uint32_t *) (phys_ram_base + bios_offset + 0x040);
3ddd0065
TS
397 stl_raw(p++, 0x3c040000); /* lui a0, 0 */
398 stl_raw(p++, 0x34840002); /* ori a0, a0, 2 */
399 stl_raw(p++, 0x3c050000 | ((ENVP_ADDR >> 16) & 0xffff)); /* lui a1, high(ENVP_ADDR) */
400 stl_raw(p++, 0x34a50000 | (ENVP_ADDR & 0xffff)); /* ori a1, a0, low(ENVP_ADDR) */
401 stl_raw(p++, 0x3c060000 | (((ENVP_ADDR + 8) >> 16) & 0xffff)); /* lui a2, high(ENVP_ADDR + 8) */
402 stl_raw(p++, 0x34c60000 | ((ENVP_ADDR + 8) & 0xffff)); /* ori a2, a2, low(ENVP_ADDR + 8) */
403 stl_raw(p++, 0x3c070000 | (env->ram_size >> 16)); /* lui a3, high(env->ram_size) */
404 stl_raw(p++, 0x34e70000 | (env->ram_size & 0xffff)); /* ori a3, a3, low(env->ram_size) */
405 stl_raw(p++, 0x3c1f0000 | ((kernel_addr >> 16) & 0xffff)); /* lui ra, high(kernel_addr) */;
406 stl_raw(p++, 0x37ff0000 | (kernel_addr & 0xffff)); /* ori ra, ra, low(kernel_addr) */
407 stl_raw(p++, 0x03e00008); /* jr ra */
408 stl_raw(p++, 0x00000000); /* nop */
5856de80
TS
409}
410
411static void prom_set(int index, const char *string, ...)
412{
413 va_list ap;
3ddd0065
TS
414 int32_t *p;
415 int32_t table_addr;
5856de80
TS
416 char *s;
417
418 if (index >= ENVP_NB_ENTRIES)
419 return;
420
3ddd0065 421 p = (int32_t *) (phys_ram_base + ENVP_ADDR + VIRT_TO_PHYS_ADDEND);
5856de80
TS
422 p += index;
423
424 if (string == NULL) {
425 stl_raw(p, 0);
426 return;
427 }
428
3ddd0065 429 table_addr = ENVP_ADDR + sizeof(int32_t) * ENVP_NB_ENTRIES + index * ENVP_ENTRY_SIZE;
5856de80
TS
430 s = (char *) (phys_ram_base + VIRT_TO_PHYS_ADDEND + table_addr);
431
432 stl_raw(p, table_addr);
433
434 va_start(ap, string);
435 vsnprintf (s, ENVP_ENTRY_SIZE, string, ap);
436 va_end(ap);
437}
438
439/* Kernel */
440static int64_t load_kernel (CPUState *env)
441{
442 int64_t kernel_addr = 0;
443 int index = 0;
444 long initrd_size;
445
446 if (load_elf(env->kernel_filename, VIRT_TO_PHYS_ADDEND, &kernel_addr) < 0) {
447 fprintf(stderr, "qemu: could not load kernel '%s'\n",
448 env->kernel_filename);
449 exit(1);
450 }
451
452 /* load initrd */
453 initrd_size = 0;
454 if (env->initrd_filename) {
455 initrd_size = load_image(env->initrd_filename,
456 phys_ram_base + INITRD_LOAD_ADDR + VIRT_TO_PHYS_ADDEND);
457 if (initrd_size == (target_ulong) -1) {
458 fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
459 env->initrd_filename);
460 exit(1);
461 }
462 }
463
464 /* Store command line. */
465 prom_set(index++, env->kernel_filename);
466 if (initrd_size > 0)
3594c774 467 prom_set(index++, "rd_start=0x" TARGET_FMT_lx " rd_size=%li %s", INITRD_LOAD_ADDR, initrd_size, env->kernel_cmdline);
5856de80
TS
468 else
469 prom_set(index++, env->kernel_cmdline);
470
471 /* Setup minimum environment variables */
472 prom_set(index++, "memsize");
473 prom_set(index++, "%i", env->ram_size);
474 prom_set(index++, "modetty0");
475 prom_set(index++, "38400n8r");
476 prom_set(index++, NULL);
477
478 return kernel_addr;
479}
480
481static void main_cpu_reset(void *opaque)
482{
483 CPUState *env = opaque;
484 cpu_reset(env);
485
486 /* The bootload does not need to be rewritten as it is located in a
487 read only location. The kernel location and the arguments table
488 location does not change. */
489 if (env->kernel_filename)
490 load_kernel (env);
491}
492
70705261 493static
5856de80
TS
494void mips_malta_init (int ram_size, int vga_ram_size, int boot_device,
495 DisplayState *ds, const char **fd_filename, int snapshot,
496 const char *kernel_filename, const char *kernel_cmdline,
497 const char *initrd_filename)
498{
499 char buf[1024];
500 unsigned long bios_offset;
501 int64_t kernel_addr;
502 PCIBus *pci_bus;
503 CPUState *env;
504 RTCState *rtc_state;
f1770b3e 505 /* fdctrl_t *floppy_controller; */
5856de80
TS
506 MaltaFPGAState *malta_fpga;
507 int ret;
508
509 env = cpu_init();
510 register_savevm("cpu", 0, 3, cpu_save, cpu_load, env);
511 qemu_register_reset(main_cpu_reset, env);
512
513 /* allocate RAM */
514 cpu_register_physical_memory(0, ram_size, IO_MEM_RAM);
515
516 /* Map the bios at two physical locations, as on the real board */
517 bios_offset = ram_size + vga_ram_size;
518 cpu_register_physical_memory(0x1e000000LL,
519 BIOS_SIZE, bios_offset | IO_MEM_ROM);
520 cpu_register_physical_memory(0x1fc00000LL,
521 BIOS_SIZE, bios_offset | IO_MEM_ROM);
522
523 /* Load a BIOS image except if a kernel image has been specified. In
524 the later case, just write a small bootloader to the flash
525 location. */
526 if (kernel_filename) {
527 env->ram_size = ram_size;
528 env->kernel_filename = kernel_filename;
529 env->kernel_cmdline = kernel_cmdline;
530 env->initrd_filename = initrd_filename;
531 kernel_addr = load_kernel(env);
532 write_bootloader(env, bios_offset, kernel_addr);
533 } else {
534 snprintf(buf, sizeof(buf), "%s/%s", bios_dir, BIOS_FILENAME);
535 ret = load_image(buf, phys_ram_base + bios_offset);
331ad6f4 536 if (ret < 0 || ret > BIOS_SIZE) {
5856de80
TS
537 fprintf(stderr, "qemu: Warning, could not load MIPS bios '%s'\n",
538 buf);
539 exit(1);
540 }
541 }
542
543 /* Board ID = 0x420 (Malta Board with CoreLV)
544 XXX: theoretically 0x1e000010 should map to flash and 0x1fc00010 should
545 map to the board ID. */
546 stl_raw(phys_ram_base + bios_offset + 0x10, 0x00000420);
547
548 /* Init internal devices */
549 cpu_mips_clock_init(env);
550 cpu_mips_irqctrl_init();
551
552 /* FPGA */
553 malta_fpga = malta_fpga_init(0x1f000000LL);
554
555 /* Interrupt controller */
556 isa_pic = pic_init(pic_irq_request, env);
557
558 /* Northbridge */
559 pci_bus = pci_gt64120_init(isa_pic);
560
561 /* Southbridge */
562 piix4_init(pci_bus, 80);
563 pci_piix3_ide_init(pci_bus, bs_table, 81);
564 usb_uhci_init(pci_bus, 82);
565 piix4_pm_init(pci_bus, 83);
566 pit = pit_init(0x40, 0);
567 DMA_init(0);
568
569 /* Super I/O */
570 kbd_init();
571 rtc_state = rtc_init(0x70, 8);
7bcc17dc
TS
572 if (serial_hds[0])
573 serial_init(&pic_set_irq_new, isa_pic, 0x3f8, 4, serial_hds[0]);
574 if (serial_hds[1])
575 serial_init(&pic_set_irq_new, isa_pic, 0x2f8, 4, serial_hds[1]);
576 if (parallel_hds[0])
577 parallel_init(0x378, 7, parallel_hds[0]);
5856de80 578 /* XXX: The floppy controller does not work correctly, something is
f1770b3e
TS
579 probably wrong.
580 floppy_controller = fdctrl_init(6, 2, 0, 0x3f0, fd_table); */
5856de80
TS
581
582 /* Sound card */
583#ifdef HAS_AUDIO
584 audio_init(pci_bus);
585#endif
586
587 /* Network card */
588 network_init(pci_bus);
589}
590
591QEMUMachine mips_malta_machine = {
592 "malta",
593 "MIPS Malta Core LV",
594 mips_malta_init,
595};