From patchwork Fri Jan 25 08:19:37 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kuo-Jung Su X-Patchwork-Id: 215604 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id AB7142C007B for ; Fri, 25 Jan 2013 20:39:58 +1100 (EST) Received: from localhost ([::1]:48598 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TyeXM-0005ot-Hu for incoming@patchwork.ozlabs.org; Fri, 25 Jan 2013 03:21:24 -0500 Received: from eggs.gnu.org ([208.118.235.92]:44438) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TyeWJ-0003dA-RM for qemu-devel@nongnu.org; Fri, 25 Jan 2013 03:20:30 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1TyeWE-0007sg-W8 for qemu-devel@nongnu.org; Fri, 25 Jan 2013 03:20:19 -0500 Received: from mail-pa0-f52.google.com ([209.85.220.52]:57574) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TyeWE-0007qA-IO for qemu-devel@nongnu.org; Fri, 25 Jan 2013 03:20:14 -0500 Received: by mail-pa0-f52.google.com with SMTP id fb1so123824pad.25 for ; Fri, 25 Jan 2013 00:20:11 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=x-received:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references:in-reply-to:references; bh=e9pUrFPt1VpzXwHs3ez6eNsE+OIQA7QyaHobO2R1wCM=; b=ukm95lqLkAz9TV20fflJjUjROsLoUN6yJtIw5ieOb/1Mb1beqH5qYgfdJYzfYpjEip Q1/kF5EHZIVs7/duGYDKAkz/vu/cG45c7kqJpzdCfIFFqMtjoo3VcDabXcMtJSq00ADx 4KLNESHw8JobflO69PiWtCQs+ZgoOxqc4BdBJhgtv7P6Wx5+NWkXzEKc3ADV/VkElL0/ iSDyI9vVEwbB5n2svwTfitalARCBRwdiSoOtNZ5v4pU1YWH4UACxoHLzu77Q7SeeRy/Y SBZDEy20EOlWdKf3fEDCFmxpJblLAlyocVSYqojfncXTMeNCC5659l7YK62huivQoIpz 1a+Q== X-Received: by 10.68.129.40 with SMTP id nt8mr12221432pbb.113.1359102011306; Fri, 25 Jan 2013 00:20:11 -0800 (PST) Received: from localhost.localdomain ([220.132.37.35]) by mx.google.com with ESMTPS id uh9sm246180pbc.65.2013.01.25.00.20.07 (version=TLSv1 cipher=DES-CBC3-SHA bits=168/168); Fri, 25 Jan 2013 00:20:10 -0800 (PST) From: Kuo-Jung Su To: qemu-devel@nongnu.org Date: Fri, 25 Jan 2013 16:19:37 +0800 Message-Id: <1359101996-11667-2-git-send-email-dantesu@gmail.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1359101996-11667-1-git-send-email-dantesu@gmail.com> References: <1359101996-11667-1-git-send-email-dantesu@gmail.com> In-Reply-To: <1358490506-18281-1-git-send-email-dantesu@faraday-tech.com> References: <1358490506-18281-1-git-send-email-dantesu@faraday-tech.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x [fuzzy] X-Received-From: 209.85.220.52 Cc: peter.maydell@linaro.org, blauwirbel@gmail.com, paul@codesourcery.com, Kuo-Jung Su , afaerber@suse.de, fred.konrad@greensocs.com Subject: [Qemu-devel] [PATCH v2 01/20] arm: add Faraday a36x SoC platform support X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Kuo-Jung Su The Faraday A360/A369 EVB is a Faraday platform main board used for the Faraday IP functional verification based on the well-known ARM AMBA 2.0 architecture. This main board provides a fully verified microprocessor platform, ATA-II, OTG 2.0 and USB 2.0 host connectivity. Faraday A360 EVB provides the on-board DDR2-SODIMM (256 MB), NAND Flash (128 MB), USB2.0 interface, two PCI Express interfaces, two I2C interfaces, two SSP ports, Secure Digital interface, and a 12-bit A/D converter in addition to many other features. Faraday A369 EVB provides the on-board DDR2-SODIMM (512 MB), NAND Flash (256 MB), SPI Flash ROM (16 MB), I2C EPROM, Giga-bit Ethernet, IDE, SATA host, OTG 2.0 and USB 2.0 host connectors, video output interface, TV encoder for video output, LCD controller interface, SD/MMC card reader interface, I2S codec, UART(X2), Multi-ICE, 80 ports, and an AHB extension bus. Signed-off-by: Kuo-Jung Su --- hw/a360.c | 271 +++++++++++++++++++++++ hw/a369.c | 581 ++++++++++++++++++++++++++++++++++++++++++++++++++ hw/arm/Makefile.objs | 6 + hw/faraday.h | 21 ++ 4 files changed, 879 insertions(+) create mode 100644 hw/a360.c create mode 100644 hw/a369.c create mode 100644 hw/faraday.h diff --git a/hw/a360.c b/hw/a360.c new file mode 100644 index 0000000..cb0a588 --- /dev/null +++ b/hw/a360.c @@ -0,0 +1,271 @@ +/* + * Faraday A360 Evalution Board + * + * Copyright (c) 2012 Faraday Technology + * Written by Dante Su + * + * This code is licensed under GNU GPL v2. + */ + +#include "sysbus.h" +#include "arm-misc.h" +#include "devices.h" +#include "net/net.h" +#include "sysemu/sysemu.h" +#include "sysemu/blockdev.h" +#include "exec/address-spaces.h" +#include "i2c.h" +#include "boards.h" +#include "flash.h" +#include "serial.h" +#include "ssi.h" +#include "faraday.h" + +typedef struct A360State { + ARMCPU *cpu; + DeviceState *hdma; /* AHB DMA */ + DeviceState *pdma; /* APB DMA */ + + MemoryRegion *as; + MemoryRegion *ram; + + i2c_bus *i2c[2]; + + struct { + MemoryRegion iomem; + } pmu; +} a360_state; + +/* PMU block */ +static uint64_t +a360_pmu_read(void *opaque, hwaddr addr, unsigned size) +{ + uint64_t ret = 0; + + switch (addr) { + case 0x30: + ret = 27 << 3; + break; + } + + return ret; +} + +static void +a360_pmu_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ +} + +static const MemoryRegionOps a360_pmu_ops = { + .read = a360_pmu_read, + .write = a360_pmu_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static int +a360_pmu_init(a360_state *s, hwaddr base) +{ + MemoryRegion *iomem = &s->pmu.iomem; + memory_region_init_io(iomem, &a360_pmu_ops, s, "a360_pmu", 0x1000); + memory_region_add_subregion(s->as, base, iomem); + return 0; +} + +/* Board init. */ +static void +a360_device_init(a360_state *s) +{ + qemu_irq *pic; + qemu_irq ack, req; + qemu_irq cs_line; + DeviceState *ds; + int i, done_nic = 0, nr_flash = 1; + SSIBus *spi; + DeviceState *fl; + + /* Interrupt Controller */ + pic = ftintc020_init(0x98800000, s->cpu); + + /* Timer */ + ds = qdev_create(NULL, "fttmr010"); + qdev_prop_set_uint32(ds, "freq", 66 * 1000000); + qdev_init_nofail(ds); + sysbus_mmio_map(SYS_BUS_DEVICE(ds), 0, 0x98400000); + sysbus_connect_irq(SYS_BUS_DEVICE(ds), 0, pic[42]); + sysbus_connect_irq(SYS_BUS_DEVICE(ds), 1, pic[19]); + sysbus_connect_irq(SYS_BUS_DEVICE(ds), 2, pic[14]); + sysbus_connect_irq(SYS_BUS_DEVICE(ds), 3, pic[15]); + + /* Serial (FTUART010 which is 16550A compatible) */ + if (serial_hds[0]) { + serial_mm_init(s->as, + 0x98200000, + 2, + pic[10], + 18432000 / 16, + serial_hds[0], + DEVICE_LITTLE_ENDIAN); + } + if (serial_hds[1]) { + serial_mm_init(s->as, + 0x98300000, + 2, + pic[11], + 18432000 / 16, + serial_hds[1], + DEVICE_LITTLE_ENDIAN); + } + + /* pmu */ + a360_pmu_init(s, 0x98100000); + + /* ftdmac020 */ + s->hdma = sysbus_create_varargs("ftdmac020", + 0x90400000, + pic[21], /* ALL */ + pic[40], /* TC */ + pic[41], /* ERR */ + NULL); + + /* ftapbbrg020 */ + s->pdma = sysbus_create_simple("ftapbbrg020", 0x90500000, pic[24]); + + /* ftsdc010 */ + ds = sysbus_create_simple("ftsdc010", 0x90700000, pic[5]); + ack = qdev_get_gpio_in(ds, 0); + req = qdev_get_gpio_in(s->hdma, 0); + qdev_connect_gpio_out(s->hdma, 0, ack); + qdev_connect_gpio_out(ds, 0, req); + + /* fusbh200 */ + sysbus_create_simple("faraday-ehci-usb", 0x90A00000, pic[23]); + + /* fotg210 */ + sysbus_create_simple("faraday-ehci-usb", 0x90B00000, pic[26]); + + /* ftmac110 */ + for (i = 0; i < nb_nics; i++) { + NICInfo *nd = &nd_table[i]; + if (!done_nic && (!nd->model || strcmp(nd->model, "ftmac110") == 0)) { + ftmac110_init(nd, 0x90900000, pic[25]); + done_nic = 1; + } + } + + /* ftwdt010 */ + sysbus_create_simple("ftwdt010", 0x98500000, pic[16]); + + /* ftlcdc200 */ + sysbus_create_varargs("ftlcdc200", + 0x90600000, + pic[27], /* Global */ + NULL); + + /* fti2c010 */ + ds = sysbus_create_simple("fti2c010", 0x98A00000, pic[3]); + s->i2c[0] = (i2c_bus *)qdev_get_child_bus(ds, "i2c"); + ds = sysbus_create_simple("fti2c010", 0x98C00000, pic[22]); + s->i2c[1] = (i2c_bus *)qdev_get_child_bus(ds, "i2c"); + + /* ftssp010 */ + ds = qdev_create(NULL, "ftssp010"); + + /* i2s */ + qdev_prop_set_ptr(ds, "codec_i2c", s->i2c[0]); + qdev_init_nofail(ds); + sysbus_mmio_map(SYS_BUS_DEVICE(ds), 0, 0x98B00000); + sysbus_connect_irq(SYS_BUS_DEVICE(ds), 0, pic[2]); + + /* spi */ + spi = (SSIBus *)qdev_get_child_bus(ds, "spi"); + for (i = 0; i < nr_flash; i++) { + fl = ssi_create_slave_no_init(spi, "m25p80"); + qdev_prop_set_string(fl, "partname", "w25q64"); + qdev_init_nofail(fl); + cs_line = qdev_get_gpio_in(fl, 0); + sysbus_connect_irq(SYS_BUS_DEVICE(ds), i+1, cs_line); + } + + /* DMA (Tx) */ + ack = qdev_get_gpio_in(ds, 0); + req = qdev_get_gpio_in(s->pdma, 1); + qdev_connect_gpio_out(s->pdma, 1, ack); + qdev_connect_gpio_out(ds, 0, req); + + /* DMA (Rx) */ + ack = qdev_get_gpio_in(ds, 1); + req = qdev_get_gpio_in(s->pdma, 2); + qdev_connect_gpio_out(s->pdma, 2, ack); + qdev_connect_gpio_out(ds, 1, req); +} + +static void +a360_board_reset(void *opaque) +{ + a360_state *s = opaque; + + cpu_reset(CPU(s->cpu)); +} + +static void +a360_board_init(QEMUMachineInitArgs *args) +{ + struct arm_boot_info *bi = NULL; + a360_state *s = g_new(a360_state, 1); + + s->as = get_system_memory(); + s->ram = g_new(MemoryRegion, 1); + + /* CPU */ + if (!args->cpu_model) { + args->cpu_model = "fa626te"; + } + + s->cpu = cpu_arm_init(args->cpu_model); + if (!s->cpu) { + fprintf(stderr, "Unable to find CPU definition\n"); + exit(1); + } + + /* A360 supports upto 1GB ram space */ + if (args->ram_size > 0x40000000) { + args->ram_size = 0x40000000; + } + + printf("qemu: faraday a360 with %dMB ram.\n", args->ram_size >> 20); + + /* RAM Init */ + memory_region_init_ram(s->ram, "a360.ram", args->ram_size); + vmstate_register_ram_global(s->ram); + + a360_device_init(s); + qemu_register_reset(a360_board_reset, s); + + /* Prepare for direct boot from linux kernel or u-boot elf */ + bi = g_new0(struct arm_boot_info, 1); + + /* RAM Address Binding */ + memory_region_add_subregion(s->as, 0x00000000, s->ram); + + /* Boot Info */ + bi->ram_size = args->ram_size; + bi->kernel_filename = args->kernel_filename; + bi->kernel_cmdline = args->kernel_cmdline; + bi->initrd_filename = args->initrd_filename; + bi->board_id = 0xa360; + arm_load_kernel(s->cpu, bi); +} + +static QEMUMachine a360_machine = { + .name = "a360", + .desc = "Faraday A360 (fa626te)", + .init = a360_board_init, +}; + +static void +a360_machine_init(void) +{ + qemu_register_machine(&a360_machine); +} + +machine_init(a360_machine_init); diff --git a/hw/a369.c b/hw/a369.c new file mode 100644 index 0000000..646888f --- /dev/null +++ b/hw/a369.c @@ -0,0 +1,581 @@ +/* + * Faraday A369 Evalution Board + * + * Copyright (c) 2012 Faraday Technology + * Written by Dante Su + * + * This code is licensed under GNU GPL v2. + */ + +#include "sysbus.h" +#include "arm-misc.h" +#include "devices.h" +#include "net/net.h" +#include "sysemu/sysemu.h" +#include "sysemu/blockdev.h" +#include "exec/address-spaces.h" +#include "i2c.h" +#include "boards.h" +#include "flash.h" +#include "serial.h" +#include "ssi.h" +#include "faraday.h" + +#define A369_NOR_FLASH_ADDR 0x20000000 +#define A369_NOR_FLASH_SIZE (16 * 1024 * 1024) +#define A369_NOR_FLASH_SECT_SIZE (128 * 1024) + +typedef struct A369State { + ARMCPU *cpu; + DeviceState *rom; + DeviceState *hdma[2]; /* AHB DMA */ + DeviceState *pdma; /* APB DMA */ + + MemoryRegion *as; + MemoryRegion *ram; + MemoryRegion *ram_alias; + MemoryRegion *sram; + + i2c_bus *i2c[2]; + + struct { + MemoryRegion iomem; + /* HW register cache */ + uint32_t general_cfg; + uint32_t sclk_cfg0; + uint32_t sclk_cfg1; + uint32_t mfpsr0; + uint32_t mfpsr1; + } scu; + + struct { + MemoryRegion iomem; + /* HW register cache */ + uint32_t cr; + } ahbc; + + struct { + MemoryRegion iomem; + /* HW register cache */ + uint32_t mcr; + uint32_t msr; + } ddrc; + +} a369_state; + +/* SCU block */ + +static uint64_t +a369_scu_read(void *opaque, hwaddr addr, unsigned size) +{ + a369_state *s = opaque; + + switch (addr) { + case 0x000: return 0x00003369; + case 0x004: return 0x00010000; + case 0x008: return 0x00000c10; + case 0x00c: return 0x00000230; + case 0x010: return 0x00000083; + case 0x014: return 0x00000100; + case 0x01C: return 0x00000003; + case 0x020: return 0x20010003; + case 0x024: return 0x00000003; + case 0x060: return 0x00280028; + case 0x200: return s->scu.general_cfg; + case 0x204: return 0x00001cc8; + case 0x228: return s->scu.sclk_cfg0; + case 0x22c: return s->scu.sclk_cfg1; + case 0x230: return 0x00003fff; + case 0x238: return s->scu.mfpsr0; + case 0x23c: return s->scu.mfpsr1; + case 0x240: return 0x11111111; + case 0x244: return 0x11111111; + case 0x254: return 0x00000303; + case 0x258: return 0x8000007f; + } + + return 0; +} + +static void +a369_scu_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ + a369_state *s = opaque; + + switch (addr) { + case 0x200: + s->scu.general_cfg = (uint32_t)val; + break; + case 0x228: + s->scu.sclk_cfg0 = (uint32_t)val; + break; + case 0x22c: + s->scu.sclk_cfg1 = (uint32_t)val; + break; + case 0x238: + s->scu.mfpsr0 = (uint32_t)val; + break; + case 0x23c: + s->scu.mfpsr1 = (uint32_t)val; + break; + } +} + +static const MemoryRegionOps a369_scu_ops = { + .read = a369_scu_read, + .write = a369_scu_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4 + } +}; + +static int +a369_scu_init(a369_state *s, hwaddr base) +{ + MemoryRegion *iomem = &s->scu.iomem; + + memory_region_init_io(iomem, &a369_scu_ops, s, "a369_scu", 0x1000); + memory_region_add_subregion(s->as, base, iomem); + + s->scu.general_cfg = 0x00001078; + s->scu.sclk_cfg0 = 0x26877330; + s->scu.sclk_cfg1 = 0x000a0a0a; + s->scu.mfpsr0 = 0x00000241; + s->scu.mfpsr1 = 0x00000000; + + return 0; +} + +/* AHB controller block */ + +static uint64_t +a369_ahbc_read(void *opaque, hwaddr addr, unsigned size) +{ + a369_state *s = opaque; + + switch (addr) { + case 0x00: return 0x94050000; + case 0x04: return 0x96040000; + case 0x08: return 0x90f00000; + case 0x0c: return 0x92050000; + case 0x10: return 0x20080000; + case 0x14: return 0xc0080000; + case 0x18: return 0x00090000; + case 0x1c: return 0x90000000; + case 0x20: return 0x90100000; + case 0x24: return 0x90200000; + case 0x28: return 0x90300000; + case 0x2c: return 0x90400000; + case 0x30: return 0x90500000; + case 0x34: return 0x90600000; + case 0x38: return 0x90700000; + case 0x3c: return 0x90800000; + case 0x40: return 0x90900000; + case 0x44: return 0x90a00000; + case 0x48: return 0x90b00000; + case 0x4c: return 0x90c00000; + case 0x50: return 0x90d00000; + case 0x54: return 0x90e00000; + case 0x58: return 0x40080000; + case 0x5c: return 0x60080000; + case 0x60: return 0xa0000000; + case 0x84: return 0x00000001; + case 0x88: return s->ahbc.cr; + case 0x8c: return 0x00010301; + } + + return 0; +} + +static void +a369_ahbc_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ + a369_state *s = opaque; + + switch (addr) { + case 0x88: + if (!(s->ahbc.cr & 0x01) && (val & 0x01)) { + /* AHB remap */ + sysbus_mmio_map(SYS_BUS_DEVICE(s->rom), 0, 0x40000000); + memory_region_del_subregion(s->as, s->ram_alias); + memory_region_add_subregion(s->as, 0x00000000, s->ram); + } + s->ahbc.cr = (uint32_t)val; + break; + } +} + +static const MemoryRegionOps a369_ahbc_ops = { + .read = a369_ahbc_read, + .write = a369_ahbc_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static int +a369_ahbc_init(a369_state *s, hwaddr base) +{ + MemoryRegion *iomem = &s->ahbc.iomem; + + memory_region_init_io(iomem, &a369_ahbc_ops, s, "a369_ahbc", 0x1000); + memory_region_add_subregion(s->as, base, iomem); + + s->ahbc.cr = 0; + + return 0; +} + +/* DDRII controller block */ + +static uint64_t +a369_ddrc_read(void *opaque, hwaddr addr, unsigned size) +{ + a369_state *s = opaque; + + switch (addr) { + case 0x00: return s->ddrc.mcr; + case 0x04: return s->ddrc.msr; + } + + return 0; +} + +static void +a369_ddrc_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ + a369_state *s = opaque; + + switch (addr) { + case 0x00: + s->ddrc.mcr = (uint32_t)val & 0xffff; + break; + case 0x04: + val = (val & 0x3f) | (s->ddrc.msr & 0x100); + if (!(s->ddrc.msr & 0x100) && (val & 0x01)) { + val &= 0xfffffffe; + val |= 0x100; + memory_region_add_subregion(s->as, 0x10000000, s->ram_alias); + } + s->ddrc.msr = (uint32_t)val; + break; + } +} + +static const MemoryRegionOps a369_ddrc_ops = { + .read = a369_ddrc_read, + .write = a369_ddrc_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static int +a369_ddrc_init(a369_state *s, hwaddr base) +{ + MemoryRegion *iomem = &s->ddrc.iomem; + + memory_region_init_io(iomem, &a369_ddrc_ops, s, "a369_ddrc", 0x1000); + memory_region_add_subregion(s->as, base, iomem); + + s->ddrc.mcr = 0; + s->ddrc.msr = 0; + + return 0; +} + +/* Board init. */ + +static void +a369_device_init(a369_state *s) +{ + qemu_irq *pic; + qemu_irq ack, req; + qemu_irq cs_line; + DeviceState *ds; + int i, done_nic = 0, nr_flash = 1; + SSIBus *spi; + DeviceState *fl; + DriveInfo *dinfo; + + /* Interrupt Controller */ + pic = ftintc020_init(0x90100000, s->cpu); + + /* Timer */ + ds = qdev_create(NULL, "ftpwmtmr010"); + qdev_prop_set_uint32(ds, "freq", 66 * 1000000); + qdev_init_nofail(ds); + sysbus_mmio_map(SYS_BUS_DEVICE(ds), 0, 0x92300000); + sysbus_connect_irq(SYS_BUS_DEVICE(ds), 0, pic[8]); + sysbus_connect_irq(SYS_BUS_DEVICE(ds), 1, pic[9]); + sysbus_connect_irq(SYS_BUS_DEVICE(ds), 2, pic[10]); + sysbus_connect_irq(SYS_BUS_DEVICE(ds), 3, pic[11]); + + /* Serial (FTUART010 which is 16550A compatible) */ + if (serial_hds[0]) { + serial_mm_init(s->as, + 0x92b00000, + 2, + pic[53], + 18432000 / 16, + serial_hds[0], + DEVICE_LITTLE_ENDIAN); + } + if (serial_hds[1]) { + serial_mm_init(s->as, + 0x92c00000, + 2, + pic[54], + 18432000 / 16, + serial_hds[1], + DEVICE_LITTLE_ENDIAN); + } + + /* ftscu010 */ + a369_scu_init(s, 0x92000000); + + /* ftddrII030 */ + a369_ddrc_init(s, 0x93100000); + + /* ftahbc020 */ + a369_ahbc_init(s, 0x94000000); + + /* ftdmac020 */ + s->hdma[0] = sysbus_create_varargs("ftdmac020", + 0x90300000, + pic[0], /* ALL (NC in A369) */ + pic[15], /* TC */ + pic[16], /* ERR */ + NULL); + s->hdma[1] = sysbus_create_varargs("ftdmac020", + 0x96100000, + pic[0], /* ALL (NC in A369) */ + pic[17], /* TC */ + pic[18], /* ERR */ + NULL); + + /* ftapbbrg020 */ + s->pdma = sysbus_create_simple("ftapbbrg020", 0x90f00000, pic[14]); + + /* ftnandc021 */ + ds = sysbus_create_simple("ftnandc021", 0x90200000, pic[30]); + ack = qdev_get_gpio_in(ds, 0); + req = qdev_get_gpio_in(s->hdma[0], 15); + qdev_connect_gpio_out(s->hdma[0], 15, ack); + qdev_connect_gpio_out(ds, 0, req); + + /* ftsdc010 */ +#if 0 + ds = sysbus_create_simple("ftsdc010", 0x90500000, pic[38]); + ack = qdev_get_gpio_in(ds, 0); + req = qdev_get_gpio_in(s->hdma[0], 14); + qdev_connect_gpio_out(s->hdma[0], 14, ack); + qdev_connect_gpio_out(ds, 0, req); +#endif + + ds = sysbus_create_simple("ftsdc010", 0x90600000, pic[39]); + ack = qdev_get_gpio_in(ds, 0); + req = qdev_get_gpio_in(s->hdma[0], 13); + qdev_connect_gpio_out(s->hdma[0], 13, ack); + qdev_connect_gpio_out(ds, 0, req); + + /* fusbh200 */ + sysbus_create_simple("faraday-ehci-usb", 0x90800000, pic[36]); + + /* fotg210 */ + sysbus_create_simple("faraday-ehci-usb", 0x90900000, pic[37]); + + /* ftgmac100 */ + for (i = 0; i < nb_nics; i++) { + NICInfo *nd = &nd_table[i]; + if (!done_nic && (!nd->model || strcmp(nd->model, "ftgmac100") == 0)) { + ftgmac100_init(nd, 0x90c00000, pic[32]); + done_nic = 1; + } + } + + /* ftrtc011 (only alarm interrupt is connected) */ + sysbus_create_varargs("ftrtc011", + 0x92100000, + pic[0], /* Alarm (Level): NC in A369 */ + pic[42], /* Alarm (Edge) */ + pic[43], /* Second (Edge) */ + pic[44], /* Minute (Edge) */ + pic[45], /* Hour (Edge) */ + NULL); + + /* ftwdt010 */ + sysbus_create_simple("ftwdt010", 0x92200000, pic[46]); + + /* fttsc010 */ + sysbus_create_simple("fttsc010", 0x92400000, pic[19]); + + /* ftkbc010 */ + sysbus_create_simple("ftkbc010", 0x92f00000, pic[21]); + + /* ftlcdc200 */ + sysbus_create_varargs("ftlcdc200", + 0x94a00000, + pic[0], /* ALL (NC in A369) */ + pic[25], /* VSTATUS */ + pic[24], /* Base Address Update */ + pic[23], /* FIFO Under-Run */ + pic[22], /* AHB Bus Error */ + NULL); + + /* fti2c010 */ + ds = sysbus_create_simple("fti2c010", 0x92900000, pic[51]); + s->i2c[0] = (i2c_bus *)qdev_get_child_bus(ds, "i2c"); + ds = sysbus_create_simple("fti2c010", 0x92A00000, pic[52]); + s->i2c[1] = (i2c_bus *)qdev_get_child_bus(ds, "i2c"); + + /* ftssp010 */ + ds = qdev_create(NULL, "ftssp010"); + + /* i2s */ + qdev_prop_set_ptr(ds, "codec_i2c", s->i2c[0]); + qdev_init_nofail(ds); + sysbus_mmio_map(SYS_BUS_DEVICE(ds), 0, 0x92700000); + sysbus_connect_irq(SYS_BUS_DEVICE(ds), 0, pic[49]); + + /* spi */ + spi = (SSIBus *)qdev_get_child_bus(ds, "spi"); + for (i = 0; i < nr_flash; i++) { + fl = ssi_create_slave_no_init(spi, "m25p80"); + qdev_prop_set_string(fl, "partname", "w25q64"); + qdev_init_nofail(fl); + cs_line = qdev_get_gpio_in(fl, 0); + sysbus_connect_irq(SYS_BUS_DEVICE(ds), i+1, cs_line); + } + + /* DMA (Tx) */ + ack = qdev_get_gpio_in(ds, 0); + req = qdev_get_gpio_in(s->pdma, 7); + qdev_connect_gpio_out(s->pdma, 7, ack); + qdev_connect_gpio_out(ds, 0, req); + + /* DMA (Rx) */ + ack = qdev_get_gpio_in(ds, 1); + req = qdev_get_gpio_in(s->pdma, 8); + qdev_connect_gpio_out(s->pdma, 8, ack); + qdev_connect_gpio_out(ds, 1, req); + + /* Parallel NOR Flash */ + dinfo = drive_get_next(IF_PFLASH); + if (!pflash_cfi01_register( + A369_NOR_FLASH_ADDR, + NULL, + "a369.pflash", + A369_NOR_FLASH_SIZE, + dinfo ? dinfo->bdrv : NULL, + A369_NOR_FLASH_SECT_SIZE, + A369_NOR_FLASH_SIZE / A369_NOR_FLASH_SECT_SIZE, + 2, 0x0001, 0x227E, 0x2101, 0x0, 0)) { + hw_error("qemu: Error registering flash memory.\n"); + exit(1); + } +} + +static void +a369_board_reset(void *opaque) +{ + a369_state *s = opaque; + + if (s->ddrc.msr) { + s->ddrc.mcr = 0; + s->ddrc.msr = 0; + if (s->ahbc.cr) { + /* AHB remapped */ + sysbus_mmio_map(SYS_BUS_DEVICE(s->rom), 0, 0x00000000); + memory_region_del_subregion(s->as, s->ram); + } else { + /* AHB is not yet remapped, but SDRAM is ready */ + memory_region_del_subregion(s->as, s->ram_alias); + } + s->ahbc.cr = 0; + } + + cpu_reset(CPU(s->cpu)); +} + +static void +a369_board_init(QEMUMachineInitArgs *args) +{ + struct arm_boot_info *bi = NULL; + a369_state *s = g_new(a369_state, 1); + + s->as = get_system_memory(); + s->ram = g_new(MemoryRegion, 1); + s->sram = g_new(MemoryRegion, 1); + + /* CPU */ + if (!args->cpu_model) { + args->cpu_model = "fa626te"; + } + + s->cpu = cpu_arm_init(args->cpu_model); + if (!s->cpu) { + fprintf(stderr, "Unable to find CPU definition\n"); + exit(1); + } + + /* A369 supports upto 1GB ram space */ + if (args->ram_size > 0x40000000) { + args->ram_size = 0x40000000; + } + + printf("qemu: faraday a369 with %dMB ram.\n", args->ram_size >> 20); + + /* Embedded ROM Init */ + s->rom = qdev_create(NULL, "rom"); + qdev_prop_set_uint32(s->rom, "size", 8192); + qdev_init_nofail(s->rom); + + /* Embedded RAM Init */ + memory_region_init_ram(s->sram, "a369.sram", 0x4000); + vmstate_register_ram_global(s->sram); + memory_region_add_subregion(s->as, 0xA0000000, s->sram); + + /* RAM Init */ + memory_region_init_ram(s->ram, "a369.ram", args->ram_size); + vmstate_register_ram_global(s->ram); + + a369_device_init(s); + qemu_register_reset(a369_board_reset, s); + + if (args->kernel_filename) { + bi = g_new0(struct arm_boot_info, 1); + + /* RAM Address Binding */ + memory_region_add_subregion(s->as, 0x00000000, s->ram); + + /* Boot Info */ + bi->ram_size = args->ram_size; + bi->kernel_filename = args->kernel_filename; + bi->kernel_cmdline = args->kernel_cmdline; + bi->initrd_filename = args->initrd_filename; + bi->board_id = 0xa369; + arm_load_kernel(s->cpu, bi); + } else { + /* ROM Address Binding */ + sysbus_mmio_map(SYS_BUS_DEVICE(s->rom), 0, 0x00000000); + /* Partial RAM (before ahb remapped) Address Binding */ + s->ram_alias = g_new(MemoryRegion, 1); + memory_region_init_alias(s->ram_alias, "a369.ram_alias", + s->ram, + 0, + MIN(0x10000000, args->ram_size)); + } +} + +static QEMUMachine a369_machine = { + .name = "a369", + .desc = "Faraday A369 (fa626te)", + .init = a369_board_init, +}; + +static void +a369_machine_init(void) +{ + qemu_register_machine(&a369_machine); +} + +machine_init(a369_machine_init); diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 6d049e7..c7bb10e 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -1,4 +1,10 @@ obj-y = integratorcp.o versatilepb.o arm_pic.o +obj-y += a360.o a369.o \ + rom.o ftdmac020.o ftapbbrg020.o \ + ftintc020.o fttmr010.o ftpwmtmr010.o \ + ftspi020.o ftssp010.o fti2c010.o \ + ftrtc011.o ftwdt010.o ftmac110.o ftgmac100.o ftlcdc200.o \ + fttsc010.o ftkbc010.o ftnandc021.o ftsdc010.o obj-y += arm_boot.o obj-y += xilinx_zynq.o zynq_slcr.o obj-y += xilinx_spips.o diff --git a/hw/faraday.h b/hw/faraday.h new file mode 100644 index 0000000..f4fe0cc --- /dev/null +++ b/hw/faraday.h @@ -0,0 +1,21 @@ +/* + * Faraday SoC platform support. + * + * Copyright (c) 2013 Faraday Technology + * Written by Kuo-Jung Su + * + * This code is licensed under the GNU GPL v2. + */ +#ifndef FARADAY_H +#define FARADAY_H + +/* ftintc020.c */ +qemu_irq *ftintc020_init(hwaddr base, ARMCPU *cpu); + +/* ftgmac100.c */ +void ftgmac100_init(NICInfo *, uint32_t, qemu_irq); + +/* ftmac110.c */ +void ftmac110_init(NICInfo *, uint32_t, qemu_irq); + +#endif