Update serial API
[qemu/ar7.git] / hw / arm / a9m2410.c
blobb2b137148f284248dbe77ce9f31238b187f92a71
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 "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
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 Chardev *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 < serial_max_hds(); i++) {
292 #if 1
293 assert(serial_hd(i));
294 #else
295 /* TODO: This code no longer works. Remove or replace. */
296 if (!serial_hd(i)) {
297 char label[32];
298 snprintf(label, sizeof(label), "serial%d", i);
299 serial_hd(i) = qemu_chr_new(label, "vc:80Cx24C");
301 #endif
304 /* initialise SOC */
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 */
311 if (dinfo) {
312 /* load from specified flash device */
313 flash_bds = dinfo->bdrv;
314 } else {
315 /* Try and load default bootloader image */
316 char *filename= qemu_find_file(QEMU_FILE_TYPE_BIOS, BIOS_FILENAME);
317 if (filename) {
318 ret = load_image_targphys(filename,
319 A9M2410_NOR_RO_BASE, A9M2410_NOR_SIZE);
320 (void)ret;
321 g_free(filename);
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,
328 false);
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;
341 nd = &nd_table[0];
342 if (nd->used) {
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));
357 #if 0
358 nd = &nd_table[1];
359 if (nd->used) {
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));
369 #endif
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);
381 if (!dinfo) {
382 stcb->nand[2] = NULL;
383 } else {
384 stcb->nand[2] = nand_init(NULL, 0xEC, 0x79); /* 128MiB small-page */
387 chr = qemu_chr_new("uart0", "vc:80Cx24C");
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");
392 serial_mm_init(sysmem, SERIAL_BASE + 0x3f8, 0,
393 s3c24xx_get_eirq(stcb->soc->gpio, 14),
394 SERIAL_CLK, chr, DEVICE_NATIVE_ENDIAN);
395 #if 0
396 /* Super I/O */
397 isa_bus_new(NULL);
398 i8259 = i8259_init(s3c24xx_get_eirq(stcb->soc->gpio, 4));
399 isa_bus_irqs(i8259);
400 /*isa_dev =*/ isa_create_simple("i8042");
401 serial_isa_init(0, serial_hd(0));
402 serial_isa_init(1, serial_hd(1));
403 #endif
406 #if 0 // TODO: unsupported by QEMU API.
407 void stcb_exit()
409 // TODO: Add calls to memory_region_destroy.
411 #endif
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,
418 mc->max_cpus = 1,
421 DEFINE_MACHINE("a9m2410", a9m2410_machine_init)