Merge remote-tracking branch 'qemu/master'
[qemu/ar7.git] / hw / arm / a9m2410.c
blobc2165a6be3ca21ecbf3eb982a5a2e430d836bb2c
1 /* hw/a9m2410.c
3 * System emulation for the Digi a9m2410.
5 * Copyright 2010, 2013 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.
13 * TODO:
14 * * Replace stcb / STCB by a better name.
17 #include "qemu/osdep.h"
18 #include "hw/hw.h"
19 #include "hw/sysbus.h" /* SYS_BUS_DEVICE, ... */
20 #include "hw/arm/arm.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/smbus.h"
25 #include "hw/devices.h"
26 #include "hw/boards.h"
27 #include "hw/char/serial.h" /* serial_isa_init */
28 #include "net/net.h"
29 #include "sysemu/blockdev.h" /* drive_get */
30 #include "sysemu/dma.h" /* QEMUSGList (in ide/internal.h) */
31 #include "sysemu/sysemu.h"
32 #include "sysemu/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
39 typedef struct {
40 MemoryRegion cpld1;
41 MemoryRegion cpld5;
42 S3CState *soc;
43 DeviceState *nand[4];
44 uint8_t cpld_ctrl2;
45 } STCBState;
47 /* Useful defines */
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,
58 unsigned size)
60 STCBState *stcb = opaque;
61 int reg = (address >> 23) & 0xf;
62 if (reg == 0xc) {
63 return stcb->cpld_ctrl2;
65 return 0;
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;
73 if (reg == 0xc) {
74 stcb->cpld_ctrl2 = value;
75 s3c24xx_nand_attach(stcb->soc->nand, stcb->nand[stcb->cpld_ctrl2 & 3]);
79 static const MemoryRegionOps cpld_ops = {
80 .read = cpld_read,
81 .write = cpld_write,
82 .endianness = DEVICE_NATIVE_ENDIAN,
83 .valid = {
84 .min_access_size = 1,
85 .max_access_size = 4
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);
97 s->cpld_ctrl2 = 0;
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
122 typedef struct {
123 IDEBus bus;
124 MemoryRegion slow;
125 MemoryRegion fast;
126 MemoryRegion slowb;
127 MemoryRegion fastb;
128 int shift;
129 } MMIOState;
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);
138 if (alt) {
139 ide_cmd_write(&s->bus, 0, val);
141 if (reg == 0) {
142 /* Data register */
143 ide_data_writew(&s->bus, 0, val);
144 } else {
145 /* Everything else */
146 ide_ioport_write(&s->bus, reg, val);
150 static uint64_t stcb_ide_read(void *opaque, hwaddr addr,
151 unsigned size)
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);
157 if (alt) {
158 return ide_status_read(&s->bus, 0);
160 if (reg == 0) {
161 return ide_data_readw(&s->bus, 0);
162 } else {
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,
171 .valid = {
172 .min_access_size = 1,
173 .max_access_size = 4
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));
185 // TODO
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);
192 return s;
195 static void stcb_register_ide(STCBState *stcb)
197 DriveInfo *dinfo0;
198 DriveInfo *dinfo1;
199 MMIOState *s;
200 MemoryRegion *sysmem = get_system_memory();
202 if (drive_get_max_bus(IF_IDE) >= 2) {
203 fprintf(stderr, "qemu: too many IDE busses\n");
204 exit(1);
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);
243 DeviceState *eeprom;
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();
262 STCBState *stcb;
263 CharDriverState *chr;
264 DeviceState *dev;
265 DriveInfo *dinfo;
266 NICInfo *nd;
267 SysBusDevice *s;
268 int i;
269 int ret;
270 BlockDriverState *flash_bds = NULL;
271 //~ qemu_irq *i8259;
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 < MAX_SERIAL_PORTS; i++) {
292 if (!serial_hds[i]) {
293 char label[32];
294 snprintf(label, sizeof(label), "serial%d", i);
295 serial_hds[i] = qemu_chr_new(label, "vc:80Cx24C", NULL);
299 /* initialise SOC */
300 stcb->soc = s3c2410x_init(ram_size);
302 stcb_register_ide(stcb);
304 dinfo = drive_get(IF_PFLASH, 0, 0);
305 /* Acquire flash contents and register pflash device */
306 if (dinfo) {
307 /* load from specified flash device */
308 flash_bds = dinfo->bdrv;
309 } else {
310 /* Try and load default bootloader image */
311 char *filename= qemu_find_file(QEMU_FILE_TYPE_BIOS, BIOS_FILENAME);
312 if (filename) {
313 ret = load_image_targphys(filename,
314 A9M2410_NOR_RO_BASE, A9M2410_NOR_SIZE);
315 (void)ret;
316 g_free(filename);
319 // TODO: map flash ro and rw.
320 pflash_cfi02_register(A9M2410_NOR_RW_BASE, NULL, "a9m2410.flash",
321 A9M2410_NOR_SIZE, flash_bds, 65536, 32, 1, 2,
322 0x00BF, 0x234B, 0x0000, 0x0000, 0x5555, 0x2AAA,
323 false);
324 // TODO: map flash readonly to address A9M2410_NOR_RO_BASE.
326 /* if kernel is given, boot that directly */
327 if (machine->kernel_filename != NULL) {
328 a9m2410_binfo.loader_start = CPU_S3C2410X_DRAM;
329 //~ a9m2410_binfo.loader_start = 0xc0108000 - 0x00010000;
330 arm_load_kernel(stcb->soc->cpu, &a9m2410_binfo);
333 /* Setup initial (reset) program counter */
334 stcb->soc->cpu->env.regs[15] = a9m2410_binfo.loader_start;
336 nd = &nd_table[0];
337 if (nd->used) {
339 * SMSC 91C111 network controller on the baseboard
340 * connected to CS line 1 and interrupt line
341 * GPIO3, data width is 32 bit
343 qemu_check_nic_model(nd, "smc91c111");
344 dev = qdev_create(NULL, "smc91c111");
345 qdev_set_nic_properties(dev, nd);
346 qdev_init_nofail(dev);
347 s = SYS_BUS_DEVICE(dev);
348 sysbus_mmio_map(s, 0, CPU_S3C2410X_CS1 + 0x300);
349 sysbus_connect_irq(s, 0, s3c24xx_get_eirq(stcb->soc->gpio, 3));
352 #if 0
353 nd = &nd_table[1];
354 if (nd->used) {
355 qemu_check_nic_model(nd, "ax88796");
356 dev = qdev_create(NULL, "ax88796");
357 qdev_set_nic_properties(dev, nd);
358 qdev_init_nofail(dev);
359 s = SYS_BUS_DEVICE(dev);
360 sysbus_mmio_map(s, 0, ASIXNET_BASE);
361 logout("ASIXNET_BASE = 0x%08x\n", ASIXNET_BASE);
362 //~ sysbus_connect_irq(s, 0, s3c24xx_get_eirq(stcb->soc->gpio, AX88796_IRQ));
364 #endif
366 /* Initialise the A9M2410 CPLD */
367 stcb_cpld_register(stcb);
369 /* attach i2c devices */
370 stcb_i2c_setup(stcb);
372 /* Attach some NAND devices */
373 stcb->nand[0] = NULL;
374 stcb->nand[1] = NULL;
375 dinfo = drive_get(IF_MTD, 0, 0);
376 if (!dinfo) {
377 stcb->nand[2] = NULL;
378 } else {
379 stcb->nand[2] = nand_init(NULL, 0xEC, 0x79); /* 128MiB small-page */
382 chr = qemu_chr_new("uart0", "vc:80Cx24C", NULL);
383 serial_mm_init(sysmem, SERIAL_BASE + 0x2f8, 0,
384 s3c24xx_get_eirq(stcb->soc->gpio, 15),
385 SERIAL_CLK, chr, DEVICE_NATIVE_ENDIAN);
386 chr = qemu_chr_new("uart1", "vc:80Cx24C", NULL);
387 serial_mm_init(sysmem, SERIAL_BASE + 0x3f8, 0,
388 s3c24xx_get_eirq(stcb->soc->gpio, 14),
389 SERIAL_CLK, chr, DEVICE_NATIVE_ENDIAN);
390 #if 0
391 /* Super I/O */
392 isa_bus_new(NULL);
393 i8259 = i8259_init(s3c24xx_get_eirq(stcb->soc->gpio, 4));
394 isa_bus_irqs(i8259);
395 /*isa_dev =*/ isa_create_simple("i8042");
396 serial_isa_init(0, serial_hds[0]);
397 serial_isa_init(1, serial_hds[1]);
398 #endif
401 #if 0 // TODO: unsupported by QEMU API.
402 void stcb_exit()
404 // TODO: Add calls to memory_region_destroy.
406 #endif
408 static void a9m2410_machine_init(MachineClass *mc)
410 mc->desc = "Digi A9M2410 (S3C2410A, ARM920T)";
411 mc->init = stcb_init;
412 //~ mc->exit = stcb_exit,
413 mc->max_cpus = 1,
416 DEFINE_MACHINE("a9m2410", a9m2410_machine_init)