]> git.proxmox.com Git - mirror_qemu.git/blame - hw/mips_malta.c
MIPS Malta system and devices support, by Aurelien Jarno and Stefan Weil.
[mirror_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
27#define BIOS_FILENAME "mips_bios.bin"
28#ifdef MIPS_HAS_MIPS64
29#define INITRD_LOAD_ADDR (uint64_t)0x80800000
30#define ENVP_ADDR (uint64_t)0x80002000
31#else
32#define INITRD_LOAD_ADDR (uint32_t)0x80800000
33#define ENVP_ADDR (uint32_t)0x80002000
34#endif
35
36#define VIRT_TO_PHYS_ADDEND (-((uint64_t)(uint32_t)0x80000000))
37
38#define ENVP_NB_ENTRIES 16
39#define ENVP_ENTRY_SIZE 256
40
41
42extern FILE *logfile;
43
44typedef struct {
45 uint32_t leds;
46 uint32_t brk;
47 uint32_t gpout;
48 uint32_t i2coe;
49 uint32_t i2cout;
50 uint32_t i2csel;
51 CharDriverState *display;
52 char display_text[9];
53} MaltaFPGAState;
54
55static PITState *pit;
56
57static void pic_irq_request(void *opaque, int level)
58{
59 CPUState *env = first_cpu;
60 if (level) {
61 env->CP0_Cause |= 0x00000400;
62 cpu_interrupt(env, CPU_INTERRUPT_HARD);
63 } else {
64 env->CP0_Cause &= ~0x00000400;
65 cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
66 }
67}
68
69/* Malta FPGA */
70static void malta_fpga_update_display(void *opaque)
71{
72 char leds_text[9];
73 int i;
74 MaltaFPGAState *s = opaque;
75
76 for (i = 7 ; i >= 0 ; i--) {
77 if (s->leds & (1 << i))
78 leds_text[i] = '#';
79 else
80 leds_text[i] = ' ';
81 }
82 leds_text[8] = '\0';
83
84 qemu_chr_printf(s->display, "\e[H\n\n|\e[31m%-8.8s\e[00m|\r\n", leds_text);
85 qemu_chr_printf(s->display, "\n\n\n\n|\e[32m%-8.8s\e[00m|", s->display_text);
86}
87
88static uint32_t malta_fpga_readl(void *opaque, target_phys_addr_t addr)
89{
90 MaltaFPGAState *s = opaque;
91 uint32_t val = 0;
92 uint32_t saddr;
93
94 saddr = (addr & 0xfffff);
95
96 switch (saddr) {
97
98 /* SWITCH Register */
99 case 0x00200:
100 val = 0x00000000; /* All switches closed */
101 break;
102
103 /* STATUS Register */
104 case 0x00208:
105#ifdef TARGET_WORDS_BIGENDIAN
106 val = 0x00000012;
107#else
108 val = 0x00000010;
109#endif
110 break;
111
112 /* JMPRS Register */
113 case 0x00210:
114 val = 0x00;
115 break;
116
117 /* LEDBAR Register */
118 case 0x00408:
119 val = s->leds;
120 break;
121
122 /* BRKRES Register */
123 case 0x00508:
124 val = s->brk;
125 break;
126
127 /* GPOUT Register */
128 case 0x00a00:
129 val = s->gpout;
130 break;
131
132 /* XXX: implement a real I2C controller */
133
134 /* GPINP Register */
135 case 0x00a08:
136 /* IN = OUT until a real I2C control is implemented */
137 if (s->i2csel)
138 val = s->i2cout;
139 else
140 val = 0x00;
141 break;
142
143 /* I2CINP Register */
144 case 0x00b00:
145 val = 0x00000003;
146 break;
147
148 /* I2COE Register */
149 case 0x00b08:
150 val = s->i2coe;
151 break;
152
153 /* I2COUT Register */
154 case 0x00b10:
155 val = s->i2cout;
156 break;
157
158 /* I2CSEL Register */
159 case 0x00b18:
160 val = s->i2cout;
161 break;
162
163 default:
164#if 0
165 printf ("malta_fpga_read: Bad register offset 0x%x\n", (int)addr);
166#endif
167 break;
168 }
169 return val;
170}
171
172static void malta_fpga_writel(void *opaque, target_phys_addr_t addr,
173 uint32_t val)
174{
175 MaltaFPGAState *s = opaque;
176 uint32_t saddr;
177
178 saddr = (addr & 0xfffff);
179
180 switch (saddr) {
181
182 /* SWITCH Register */
183 case 0x00200:
184 break;
185
186 /* JMPRS Register */
187 case 0x00210:
188 break;
189
190 /* LEDBAR Register */
191 /* XXX: implement a 8-LED array */
192 case 0x00408:
193 s->leds = val & 0xff;
194 break;
195
196 /* ASCIIWORD Register */
197 case 0x00410:
198 snprintf(s->display_text, 9, "%08X", val);
199 malta_fpga_update_display(s);
200 break;
201
202 /* ASCIIPOS0 to ASCIIPOS7 Registers */
203 case 0x00418:
204 case 0x00420:
205 case 0x00428:
206 case 0x00430:
207 case 0x00438:
208 case 0x00440:
209 case 0x00448:
210 case 0x00450:
211 s->display_text[(saddr - 0x00418) >> 3] = (char) val;
212 malta_fpga_update_display(s);
213 break;
214
215 /* SOFTRES Register */
216 case 0x00500:
217 if (val == 0x42)
218 qemu_system_reset_request ();
219 break;
220
221 /* BRKRES Register */
222 case 0x00508:
223 s->brk = val & 0xff;
224 break;
225
226 /* GPOUT Register */
227 case 0x00a00:
228 s->gpout = val & 0xff;
229 break;
230
231 /* I2COE Register */
232 case 0x00b08:
233 s->i2coe = val & 0x03;
234 break;
235
236 /* I2COUT Register */
237 case 0x00b10:
238 s->i2cout = val & 0x03;
239 break;
240
241 /* I2CSEL Register */
242 case 0x00b18:
243 s->i2cout = val & 0x01;
244 break;
245
246 default:
247#if 0
248 printf ("malta_fpga_write: Bad register offset 0x%x\n", (int)addr);
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");
294 qemu_chr_printf(s->display, "\e[HMalta LEBDAR\r\n");
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);
392 stl_raw(p++, 0x0bf00010); /* j 0x1fc00040 */
393 stl_raw(p++, 0x00000000); /* nop */
394
395 /* Second part of the bootloader */
396 p = (uint32_t *) (phys_ram_base + bios_offset + 0x040);
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)); /* 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)); /* 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)); /* 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 */
409}
410
411static void prom_set(int index, const char *string, ...)
412{
413 va_list ap;
414 uint32_t *p;
415 uint32_t table_addr;
416 char *s;
417
418 if (index >= ENVP_NB_ENTRIES)
419 return;
420
421 p = (uint32_t *) (phys_ram_base + ENVP_ADDR + VIRT_TO_PHYS_ADDEND);
422 p += index;
423
424 if (string == NULL) {
425 stl_raw(p, 0);
426 return;
427 }
428
429 table_addr = ENVP_ADDR + sizeof(uint32_t) * ENVP_NB_ENTRIES + index * ENVP_ENTRY_SIZE;
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)
467 prom_set(index++, "rd_start=0x%08x rd_size=%li %s", INITRD_LOAD_ADDR, initrd_size, env->kernel_cmdline);
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
493void mips_malta_init (int ram_size, int vga_ram_size, int boot_device,
494 DisplayState *ds, const char **fd_filename, int snapshot,
495 const char *kernel_filename, const char *kernel_cmdline,
496 const char *initrd_filename)
497{
498 char buf[1024];
499 unsigned long bios_offset;
500 int64_t kernel_addr;
501 PCIBus *pci_bus;
502 CPUState *env;
503 RTCState *rtc_state;
504 fdctrl_t *floppy_controller;
505 MaltaFPGAState *malta_fpga;
506 int ret;
507
508 env = cpu_init();
509 register_savevm("cpu", 0, 3, cpu_save, cpu_load, env);
510 qemu_register_reset(main_cpu_reset, env);
511
512 /* allocate RAM */
513 cpu_register_physical_memory(0, ram_size, IO_MEM_RAM);
514
515 /* Map the bios at two physical locations, as on the real board */
516 bios_offset = ram_size + vga_ram_size;
517 cpu_register_physical_memory(0x1e000000LL,
518 BIOS_SIZE, bios_offset | IO_MEM_ROM);
519 cpu_register_physical_memory(0x1fc00000LL,
520 BIOS_SIZE, bios_offset | IO_MEM_ROM);
521
522 /* Load a BIOS image except if a kernel image has been specified. In
523 the later case, just write a small bootloader to the flash
524 location. */
525 if (kernel_filename) {
526 env->ram_size = ram_size;
527 env->kernel_filename = kernel_filename;
528 env->kernel_cmdline = kernel_cmdline;
529 env->initrd_filename = initrd_filename;
530 kernel_addr = load_kernel(env);
531 write_bootloader(env, bios_offset, kernel_addr);
532 } else {
533 snprintf(buf, sizeof(buf), "%s/%s", bios_dir, BIOS_FILENAME);
534 ret = load_image(buf, phys_ram_base + bios_offset);
535 if (ret != BIOS_SIZE) {
536 fprintf(stderr, "qemu: Warning, could not load MIPS bios '%s'\n",
537 buf);
538 exit(1);
539 }
540 }
541
542 /* Board ID = 0x420 (Malta Board with CoreLV)
543 XXX: theoretically 0x1e000010 should map to flash and 0x1fc00010 should
544 map to the board ID. */
545 stl_raw(phys_ram_base + bios_offset + 0x10, 0x00000420);
546
547 /* Init internal devices */
548 cpu_mips_clock_init(env);
549 cpu_mips_irqctrl_init();
550
551 /* FPGA */
552 malta_fpga = malta_fpga_init(0x1f000000LL);
553
554 /* Interrupt controller */
555 isa_pic = pic_init(pic_irq_request, env);
556
557 /* Northbridge */
558 pci_bus = pci_gt64120_init(isa_pic);
559
560 /* Southbridge */
561 piix4_init(pci_bus, 80);
562 pci_piix3_ide_init(pci_bus, bs_table, 81);
563 usb_uhci_init(pci_bus, 82);
564 piix4_pm_init(pci_bus, 83);
565 pit = pit_init(0x40, 0);
566 DMA_init(0);
567
568 /* Super I/O */
569 kbd_init();
570 rtc_state = rtc_init(0x70, 8);
571 serial_init(&pic_set_irq_new, isa_pic, 0x3f8, 4, serial_hds[0]);
572 parallel_init(0x378, 7, parallel_hds[0]);
573 /* XXX: The floppy controller does not work correctly, something is
574 probably wrong */
575 floppy_controller = fdctrl_init(6, 2, 0, 0x3f0, fd_table);
576
577 /* Sound card */
578#ifdef HAS_AUDIO
579 audio_init(pci_bus);
580#endif
581
582 /* Network card */
583 network_init(pci_bus);
584}
585
586QEMUMachine mips_malta_machine = {
587 "malta",
588 "MIPS Malta Core LV",
589 mips_malta_init,
590};