3 * System emulation for the Digi a9m2410.
5 * Copyright 2010-2019 Stefan Weil
7 * Based on code from bast.c
8 * Copyright 2006, 2008 Daniel Silverstone and Vincent Sanders
10 * This file is under the terms of the GNU General Public
11 * License Version 2 or later.
14 * * Replace stcb / STCB by a better name.
17 #include "qemu/osdep.h"
19 #include "hw/sysbus.h" /* SYS_BUS_DEVICE, ... */
20 #include "hw/arm/boot.h"
21 #include "hw/ide/internal.h" /* ide_cmd_write, ... */
22 #include "hw/loader.h" /* load_image_targphys */
23 #include "hw/s3c2410x.h"
24 #include "hw/i2c/i2c.h" /* i2c_create_slave */
25 #include "hw/devices.h"
26 #include "hw/boards.h"
27 #include "hw/char/serial.h" /* serial_isa_init */
29 #include "sysemu/blockdev.h" /* drive_get */
30 #include "sysemu/dma.h" /* QEMUSGList (in ide/internal.h) */
31 #include "sysemu/sysemu.h"
32 #include "chardev/char.h" /* qemu_chr_new */
33 #include "exec/address-spaces.h" /* get_system_memory */
35 #define BIOS_FILENAME "able.bin"
37 #define S3C24XX_DBF(format, ...) (void)0
48 #define A9M2410_NOR_RO_BASE CPU_S3C2410X_CS0
49 #define A9M2410_NOR_RW_BASE (CPU_S3C2410X_CS1 + 0x4000000)
50 #define A9M2410_NOR_SIZE (2 * MiB)
51 #define A9M2410_BOARD_ID 331
53 #define A9M2410_CS1_CPLD_BASE (CPU_S3C2410X_CS1 | (0xc << 23))
54 #define A9M2410_CS5_CPLD_BASE (CPU_S3C2410X_CS5 | (0xc << 23))
55 #define A9M2410_CPLD_SIZE (4<<23)
57 static uint64_t cpld_read(void *opaque
, hwaddr address
,
60 STCBState
*stcb
= opaque
;
61 int reg
= (address
>> 23) & 0xf;
63 return stcb
->cpld_ctrl2
;
68 static void cpld_write(void *opaque
, hwaddr address
,
69 uint64_t value
, unsigned size
)
71 STCBState
*stcb
= opaque
;
72 int reg
= (address
>> 23) & 0xf;
74 stcb
->cpld_ctrl2
= value
;
75 s3c24xx_nand_attach(stcb
->soc
->nand
, stcb
->nand
[stcb
->cpld_ctrl2
& 3]);
79 static const MemoryRegionOps cpld_ops
= {
82 .endianness
= DEVICE_NATIVE_ENDIAN
,
89 static void stcb_cpld_register(STCBState
*s
)
91 MemoryRegion
*sysmem
= get_system_memory();
92 memory_region_init_io(&s
->cpld1
, OBJECT(s
),
93 &cpld_ops
, s
, "cpld1", A9M2410_CPLD_SIZE
);
94 memory_region_init_alias(&s
->cpld5
, NULL
, "cpld5", &s
->cpld1
, 0, A9M2410_CPLD_SIZE
);
95 memory_region_add_subregion(sysmem
, A9M2410_CS1_CPLD_BASE
, &s
->cpld1
);
96 memory_region_add_subregion(sysmem
, A9M2410_CS5_CPLD_BASE
, &s
->cpld5
);
100 #define A9M2410_IDE_PRI_SLOW (CPU_S3C2410X_CS3 | 0x02000000)
101 #define A9M2410_IDE_SEC_SLOW (CPU_S3C2410X_CS3 | 0x03000000)
102 #define A9M2410_IDE_PRI_FAST (CPU_S3C2410X_CS5 | 0x02000000)
103 #define A9M2410_IDE_SEC_FAST (CPU_S3C2410X_CS5 | 0x03000000)
105 #define A9M2410_IDE_PRI_SLOW_BYTE (CPU_S3C2410X_CS2 | 0x02000000)
106 #define A9M2410_IDE_SEC_SLOW_BYTE (CPU_S3C2410X_CS2 | 0x03000000)
107 #define A9M2410_IDE_PRI_FAST_BYTE (CPU_S3C2410X_CS4 | 0x02000000)
108 #define A9M2410_IDE_SEC_FAST_BYTE (CPU_S3C2410X_CS4 | 0x03000000)
110 /* MMIO interface to IDE on A9M2410.
112 * Copyright Daniel Silverstone and Vincent Sanders
114 * This section of this file is under the terms of
115 * the GNU General Public License Version 2
118 /* Each A9M2410 IDE region is 0x01000000 bytes long,
119 * the second half is the "alternate" register set
131 static void stcb_ide_write(void *opaque
, hwaddr addr
,
132 uint64_t val
, unsigned size
)
134 MMIOState
*s
= opaque
;
135 int reg
= (addr
& 0x3ff) >> 5; /* 0x200 long, 0x20 stride */
136 int alt
= (addr
& 0x800000) != 0;
137 S3C24XX_DBF("IDE write to addr %08x (reg %d) of value %04x\n", (unsigned int)addr
, reg
, val
);
139 ide_cmd_write(&s
->bus
, 0, val
);
143 ide_data_writew(&s
->bus
, 0, val
);
145 /* Everything else */
146 ide_ioport_write(&s
->bus
, reg
, val
);
150 static uint64_t stcb_ide_read(void *opaque
, hwaddr addr
,
153 MMIOState
*s
= opaque
;
154 int reg
= (addr
& 0x3ff) >> 5; /* 0x200 long, 0x20 stride */
155 int alt
= (addr
& 0x800000) != 0;
156 S3C24XX_DBF("IDE read of addr %08x (reg %d)\n", (unsigned int)addr
, reg
);
158 return ide_status_read(&s
->bus
, 0);
161 return ide_data_readw(&s
->bus
, 0);
163 return ide_ioport_read(&s
->bus
, reg
);
167 static const MemoryRegionOps stcb_ide_ops
= {
168 .read
= stcb_ide_read
,
169 .write
= stcb_ide_write
,
170 .endianness
= DEVICE_NATIVE_ENDIAN
,
172 .min_access_size
= 1,
177 /* hd_table must contain 2 block drivers */
178 /* A9M2410 uses memory mapped registers, not I/O. Return the memory
179 * I/O tag to access the ide.
180 * The A9M2410 description will register it into the map in the right place.
182 static MMIOState
*stcb_ide_init(DriveInfo
*dinfo0
, DriveInfo
*dinfo1
, qemu_irq irq
)
184 MMIOState
*s
= g_malloc0(sizeof(MMIOState
));
186 //~ ide_init2_with_non_qdev_drives(&s->bus, dinfo0, dinfo1, irq);
187 memory_region_init_io(&s
->slow
, OBJECT(s
),
188 &stcb_ide_ops
, s
, "stcb-ide", 0x1000000);
189 memory_region_init_alias(&s
->fast
, NULL
, "stcb-ide", &s
->slow
, 0, 0x1000000);
190 memory_region_init_alias(&s
->slowb
, NULL
, "stcb-ide", &s
->slow
, 0, 0x1000000);
191 memory_region_init_alias(&s
->fastb
, NULL
, "stcb-ide", &s
->slow
, 0, 0x1000000);
195 static void stcb_register_ide(STCBState
*stcb
)
200 MemoryRegion
*sysmem
= get_system_memory();
202 if (drive_get_max_bus(IF_IDE
) >= 2) {
203 fprintf(stderr
, "qemu: too many IDE busses\n");
207 dinfo0
= drive_get(IF_IDE
, 0, 0);
208 dinfo1
= drive_get(IF_IDE
, 0, 1);
209 s
= stcb_ide_init(dinfo0
, dinfo1
, s3c24xx_get_eirq(stcb
->soc
->gpio
, 16));
210 memory_region_add_subregion(sysmem
, A9M2410_IDE_PRI_SLOW
, &s
->slow
);
211 memory_region_add_subregion(sysmem
, A9M2410_IDE_PRI_FAST
, &s
->fast
);
212 memory_region_add_subregion(sysmem
, A9M2410_IDE_PRI_SLOW_BYTE
, &s
->slowb
);
213 memory_region_add_subregion(sysmem
, A9M2410_IDE_PRI_FAST_BYTE
, &s
->fastb
);
215 dinfo0
= drive_get(IF_IDE
, 1, 0);
216 dinfo1
= drive_get(IF_IDE
, 1, 1);
217 s
= stcb_ide_init(dinfo0
, dinfo1
, s3c24xx_get_eirq(stcb
->soc
->gpio
, 17));
218 memory_region_add_subregion(sysmem
, A9M2410_IDE_SEC_SLOW
, &s
->slow
);
219 memory_region_add_subregion(sysmem
, A9M2410_IDE_SEC_FAST
, &s
->fast
);
220 memory_region_add_subregion(sysmem
, A9M2410_IDE_SEC_SLOW_BYTE
, &s
->slowb
);
221 memory_region_add_subregion(sysmem
, A9M2410_IDE_SEC_FAST_BYTE
, &s
->fastb
);
224 #define A9M2410_PA_ASIXNET 0x01000000
225 #define A9M2410_PA_SUPERIO 0x01800000
227 #define SERIAL_BASE (CPU_S3C2410X_CS2 + A9M2410_PA_SUPERIO)
228 #define SERIAL_CLK 1843200
230 #define ASIXNET_BASE (CPU_S3C2410X_CS5 + A9M2410_PA_ASIXNET)
231 #define ASIXNET_SIZE (0x400)
233 //~ #define DM9000_BASE (CPU_S3C2410X_CS5 + 0x05000000) // 0x2d000000)
234 //~ #define DM9000_IRQ 10
236 #define logout(fmt, ...) \
237 fprintf(stderr, "a9m2410\t%-24s" fmt, __func__, ##__VA_ARGS__)
239 static void stcb_i2c_setup(STCBState
*stcb
)
241 I2CBus
*bus
= s3c24xx_i2c_bus(stcb
->soc
->iic
);
242 uint8_t *eeprom_buf
= g_malloc0(256);
244 eeprom
= qdev_create((BusState
*)bus
, "smbus-eeprom");
245 qdev_prop_set_uint8(eeprom
, "address", 0x50);
246 qdev_prop_set_ptr(eeprom
, "data", eeprom_buf
);
247 qdev_init_nofail(eeprom
);
249 i2c_create_slave(bus
, "ch7xxx", 0x75);
250 i2c_create_slave(bus
, "stcpmu", 0x6B);
254 static struct arm_boot_info a9m2410_binfo
= {
255 .board_id
= A9M2410_BOARD_ID
,
256 .ram_size
= 0x10000000, /* 256MB */
259 static void stcb_init(MachineState
*machine
)
261 MemoryRegion
*sysmem
= get_system_memory();
270 BlockDriverState
*flash_bds
= NULL
;
273 /* ensure memory is limited to 256MB */
274 if (machine
->ram_size
> (256 * MiB
)) {
275 machine
->ram_size
= 256 * MiB
;
277 ram_size
= machine
->ram_size
;
279 /* initialise board informations */
280 a9m2410_binfo
.ram_size
= ram_size
;
281 a9m2410_binfo
.kernel_filename
= machine
->kernel_filename
;
282 a9m2410_binfo
.kernel_cmdline
= machine
->kernel_cmdline
;
283 a9m2410_binfo
.initrd_filename
= machine
->initrd_filename
;
284 a9m2410_binfo
.nb_cpus
= 1;
285 a9m2410_binfo
.loader_start
= A9M2410_NOR_RO_BASE
;
287 /* allocate storage for board state */
288 stcb
= g_malloc0(sizeof(STCBState
));
290 /* Make sure all serial ports are associated with a device. */
291 for (i
= 0; i
< serial_max_hds(); i
++) {
293 assert(serial_hd(i
));
295 /* TODO: This code no longer works. Remove or replace. */
298 snprintf(label
, sizeof(label
), "serial%d", i
);
299 serial_hd(i
) = qemu_chr_new(label
, "vc:80Cx24C", NULL
);
305 stcb
->soc
= s3c2410x_init(ram_size
);
307 stcb_register_ide(stcb
);
309 dinfo
= drive_get(IF_PFLASH
, 0, 0);
310 /* Acquire flash contents and register pflash device */
312 /* load from specified flash device */
313 flash_bds
= dinfo
->bdrv
;
315 /* Try and load default bootloader image */
316 char *filename
= qemu_find_file(QEMU_FILE_TYPE_BIOS
, BIOS_FILENAME
);
318 ret
= load_image_targphys(filename
,
319 A9M2410_NOR_RO_BASE
, A9M2410_NOR_SIZE
);
324 // TODO: map flash ro and rw.
325 pflash_cfi02_register(A9M2410_NOR_RW_BASE
, NULL
, "a9m2410.flash",
326 A9M2410_NOR_SIZE
, flash_bds
, 65536, 32, 1, 2,
327 0x00BF, 0x234B, 0x0000, 0x0000, 0x5555, 0x2AAA,
329 // TODO: map flash readonly to address A9M2410_NOR_RO_BASE.
331 /* if kernel is given, boot that directly */
332 if (machine
->kernel_filename
!= NULL
) {
333 a9m2410_binfo
.loader_start
= CPU_S3C2410X_DRAM
;
334 //~ a9m2410_binfo.loader_start = 0xc0108000 - 0x00010000;
335 arm_load_kernel(stcb
->soc
->cpu
, &a9m2410_binfo
);
338 /* Setup initial (reset) program counter */
339 stcb
->soc
->cpu
->env
.regs
[15] = a9m2410_binfo
.loader_start
;
344 * SMSC 91C111 network controller on the baseboard
345 * connected to CS line 1 and interrupt line
346 * GPIO3, data width is 32 bit
348 qemu_check_nic_model(nd
, "smc91c111");
349 dev
= qdev_create(NULL
, "smc91c111");
350 qdev_set_nic_properties(dev
, nd
);
351 qdev_init_nofail(dev
);
352 s
= SYS_BUS_DEVICE(dev
);
353 sysbus_mmio_map(s
, 0, CPU_S3C2410X_CS1
+ 0x300);
354 sysbus_connect_irq(s
, 0, s3c24xx_get_eirq(stcb
->soc
->gpio
, 3));
360 qemu_check_nic_model(nd
, "ax88796");
361 dev
= qdev_create(NULL
, "ax88796");
362 qdev_set_nic_properties(dev
, nd
);
363 qdev_init_nofail(dev
);
364 s
= SYS_BUS_DEVICE(dev
);
365 sysbus_mmio_map(s
, 0, ASIXNET_BASE
);
366 logout("ASIXNET_BASE = 0x%08x\n", ASIXNET_BASE
);
367 //~ sysbus_connect_irq(s, 0, s3c24xx_get_eirq(stcb->soc->gpio, AX88796_IRQ));
371 /* Initialise the A9M2410 CPLD */
372 stcb_cpld_register(stcb
);
374 /* attach i2c devices */
375 stcb_i2c_setup(stcb
);
377 /* Attach some NAND devices */
378 stcb
->nand
[0] = NULL
;
379 stcb
->nand
[1] = NULL
;
380 dinfo
= drive_get(IF_MTD
, 0, 0);
382 stcb
->nand
[2] = NULL
;
384 stcb
->nand
[2] = nand_init(NULL
, 0xEC, 0x79); /* 128MiB small-page */
387 chr
= qemu_chr_new("uart0", "vc:80Cx24C", NULL
);
388 serial_mm_init(sysmem
, SERIAL_BASE
+ 0x2f8, 0,
389 s3c24xx_get_eirq(stcb
->soc
->gpio
, 15),
390 SERIAL_CLK
, chr
, DEVICE_NATIVE_ENDIAN
);
391 chr
= qemu_chr_new("uart1", "vc:80Cx24C", NULL
);
392 serial_mm_init(sysmem
, SERIAL_BASE
+ 0x3f8, 0,
393 s3c24xx_get_eirq(stcb
->soc
->gpio
, 14),
394 SERIAL_CLK
, chr
, DEVICE_NATIVE_ENDIAN
);
398 i8259
= i8259_init(s3c24xx_get_eirq(stcb
->soc
->gpio
, 4));
400 /*isa_dev =*/ isa_create_simple("i8042");
401 serial_isa_init(0, serial_hd(0));
402 serial_isa_init(1, serial_hd(1));
406 #if 0 // TODO: unsupported by QEMU API.
409 // TODO: Add calls to memory_region_destroy.
413 static void a9m2410_machine_init(MachineClass
*mc
)
415 mc
->desc
= "Digi A9M2410 (S3C2410A, ARM920T)";
416 mc
->init
= stcb_init
;
417 //~ mc->exit = stcb_exit,
421 DEFINE_MACHINE("a9m2410", a9m2410_machine_init
)