qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 01/18] hw: add Faraday a369 SoC platform support
@ 2013-01-18  6:28 Dante
  2013-01-18  6:28 ` [Qemu-devel] [PATCH 02/18] hw: add QEMU model for Faraday APB DMA Dante
                   ` (4 more replies)
  0 siblings, 5 replies; 8+ messages in thread
From: Dante @ 2013-01-18  6:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: peter.maydell, paul, dantesu

Faraday A369, is an ARM SoC prototype chip, highly integrated with common SoC
features, including a 533MHz ARM v5TE master CPU, a 133MHz ARM v5TE slave CPU,
a 32-bit DDR-2 533MHz memory controller, 2 USB 2.0 host/OTG ports, SATA
host/device port, 10/100/1000 Ethernet port, MPEG4 codec, H.264 decoder,
image signal processor, and various low-speed peripheral interfaces.

This patch adds QEMU model for A369 without MPEG4/H.264 and USB support.
The USB host is still under migration from qemu-1.3.0 into the current
development release from GIT.

These patches include:

1. hw/a369.c:        QEMU model for Faraday a369 SoC platform
2. hw/a369_scu.c:    A369 System Control Uint (Clock & Power Management)
3. hw/ftapbbrg020.c: APB DMA Engine
4. hw/ftapbbrg020.h
5. hw/ftdmac020.c:   AHB DMA Engine
6. hw/ftdmac020.h
7. hw/ftgmac100.c:   1Gbps Ethernet MAC
8. hw/ftgmac100.h
9. hw/fti2c010.c:    I2C Master
10.hw/fti2c010.h
11.hw/ftintc020.c:   Interrupt Controller
12.hw/ftkbc010.c:    A369 Keypad Controller
13.hw/ftlcdc200.c:   LCD Controller
14.hw/ftlcdc200.h
15.hw/ftlcdc200_template.h
16.hw/ftmac110.c:    100Mbps Ethernet MAC
17.hw/ftmac110.h
18.hw/ftnandc021.c:  NAND Flash Controller
19.hw/ftnandc021.h
20.hw/ftpwmtmr010.c: Timer
21.hw/ftrtc011.c:    RTC Timer
22.hw/ftsdc010.c:    MMC/SD Host Controller
23.hw/ftsdc010.h
24.hw/ftspi020.c:    SPI NOR Flash Controller
25.hw/ftspi020.h
26.hw/ftssp010.c:    Multi-function device with SPI and I2S support
27.hw/ftssp010.h
28.hw/fttmr010.c:    Timer
29/hw/fttsc010.c:    Touchscreen Controller
30.hw/fttsc010.h
31.hw/ftwdt010.c:    Watchdog Timer
32.hw/rom.c:         Embedded ROM device model for SoC Platform

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/a369.c            |  499 ++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/a369_scu.c        |  192 +++++++++++++++++++
 hw/arm/Makefile.objs |    6 +
 3 files changed, 697 insertions(+)
 create mode 100644 hw/a369.c
 create mode 100644 hw/a369_scu.c

diff --git a/hw/a369.c b/hw/a369.c
new file mode 100644
index 0000000..cc78351
--- /dev/null
+++ b/hw/a369.c
@@ -0,0 +1,499 @@
+/*
+ * Faraday A369 Evalution Board
+ *
+ * Copyright (c) 2012 Faraday Technology
+ *
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This code is licensed under the GPL.
+ */
+
+#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"
+
+#define A369_NOR_FLASH_ADDR            0x20000000
+#define A369_NOR_FLASH_SIZE            (16 * 1024 * 1024)
+#define A369_NOR_FLASH_SECT_SIZE    (128 * 1024)
+
+#ifndef min
+#define min(a, b)                    ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef max
+#define max(a, b)                    ((a) > (b) ? (a) : (b))
+#endif
+
+struct a369_ahbc_state {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    /* HW register cache */
+    uint32_t cr;
+};
+
+struct a369_ddrc_state {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    
+    /* HW register caches */
+    uint32_t mcr;
+    uint32_t msr;
+};
+
+struct a369_state {
+    ARMCPU *cpu;
+    DeviceState *rom;
+    DeviceState *hdma;    /* AHB DMA */
+    DeviceState *pdma;    /* APB DMA */
+
+    MemoryRegion *as;
+    MemoryRegion *ram;
+    MemoryRegion *ram_alias;
+    MemoryRegion *sram;
+
+    i2c_bus *i2c[2];
+
+    struct a369_ahbc_state ahbc;
+    struct a369_ddrc_state ddrc;
+};
+
+/* ftintc020.c */
+qemu_irq *ftintc020_init(hwaddr base, ARMCPU *cpu);
+
+/* ftgmac100.c */
+void ftgmac100_init(NICInfo *nd, uint32_t base, qemu_irq irq);
+
+/* AHB controller block */
+
+static uint64_t a369_ahbc_read(void *opaque, hwaddr addr, unsigned size)
+{
+    struct a369_state *mach = opaque;
+    struct a369_ahbc_state *s = &mach->ahbc;
+    
+    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->cr;
+    case 0x8c: return 0x00010301;
+    default:
+        break;
+    }
+    return 0;
+}
+
+static void a369_ahbc_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    struct a369_state *mach = opaque;
+    struct a369_ahbc_state *s = &mach->ahbc;
+
+    if (addr == 0x88) {
+        if (!(s->cr & 0x01) && (val & 0x01)) {
+            /* AHB remap */
+            printf("[qemu] a369: AHB remap...\n");
+            sysbus_mmio_map(sysbus_from_qdev(mach->rom), 0, 0x40000000);
+            memory_region_del_subregion(mach->as, mach->ram_alias);
+            memory_region_add_subregion(mach->as, 0x00000000, mach->ram);            
+        }
+        s->cr = (uint32_t)val;
+    }
+}
+
+static const MemoryRegionOps a369_ahbc_ops = {
+    .read  = a369_ahbc_read,
+    .write = a369_ahbc_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int a369_ahbc_init(struct a369_state *mach, hwaddr base)
+{
+    struct a369_ahbc_state *s = &mach->ahbc;
+    memory_region_init_io(&s->iomem, &a369_ahbc_ops, mach, "a369_ahbc", 0x1000);
+    memory_region_add_subregion(mach->as, base, &s->iomem);
+    s->cr = 0;
+    return 0;
+}
+
+/* DDRII controller block */
+
+static uint64_t a369_ddrc_read(void *opaque, hwaddr addr, unsigned size)
+{
+    struct a369_state *mach = opaque;
+    struct a369_ddrc_state *s = &mach->ddrc;
+    
+    switch(addr) {
+    case 0x00:
+        return s->mcr;
+    case 0x04:
+        return s->msr;
+    default:
+        break;
+    }
+    return 0;
+}
+
+static void a369_ddrc_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    struct a369_state *mach = opaque;
+    struct a369_ddrc_state *s = &mach->ddrc;
+    
+    switch(addr) {
+    case 0x00:
+        s->mcr = (uint32_t)val & 0xffff;
+        break;
+    case 0x04:
+        val = (val & 0x3f) | (s->msr & 0x100);
+        if (!(s->msr & 0x100) && (val & 0x01)) {
+            val &= 0xfffffffe;
+            val |= 0x100;
+            printf("[qemu] a369: ddr init...\n");
+            memory_region_add_subregion(mach->as, 0x10000000, mach->ram_alias);            
+        }
+        s->msr = (uint32_t)val;
+        break;
+    default:
+        break;
+    }
+}
+
+static const MemoryRegionOps a369_ddrc_ops = {
+    .read  = a369_ddrc_read,
+    .write = a369_ddrc_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int a369_ddrc_init(struct a369_state *mach, hwaddr base)
+{
+    struct a369_ddrc_state *s = &mach->ddrc;
+    
+    memory_region_init_io(&s->iomem, &a369_ddrc_ops, mach, "a369_ddrc", 0x1000);
+    memory_region_add_subregion(mach->as, base, &s->iomem);
+    s->mcr = 0;
+    s->msr = 0;
+    return 0;
+}
+
+/* Board init.  */
+
+static void a369_device_init(struct a369_state *s)
+{
+    qemu_irq *pic;
+    DeviceState *ds;
+    int done_nic = 0;
+    int i;
+    
+    /* 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(sysbus_from_qdev(ds), 0, 0x92300000);
+    sysbus_connect_irq(sysbus_from_qdev(ds), 0, pic[8]);
+    sysbus_connect_irq(sysbus_from_qdev(ds), 1, pic[9]);
+    sysbus_connect_irq(sysbus_from_qdev(ds), 2, pic[10]);
+    sysbus_connect_irq(sysbus_from_qdev(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_NATIVE_ENDIAN);
+    }
+    if (serial_hds[1]) {
+        serial_mm_init(s->as,
+                       0x92c00000,
+                       2,
+                       pic[54],
+                       18432000 / 16,
+                       serial_hds[1],
+                       DEVICE_NATIVE_ENDIAN);
+    }
+
+    /* ftscu010 */
+    sysbus_create_simple("a369_scu", 0x92000000, pic[41]);
+    
+    /* ftdmac020 */
+    s->hdma = sysbus_create_simple("ftdmac020", 0x90300000, pic[15]);
+    sysbus_create_simple("ftdmac020", 0x96100000, pic[17]);
+
+    /* ftapbbrg020 */
+    s->pdma = sysbus_create_simple("ftapbbrg020", 0x90f00000, pic[14]);
+
+    /* ftnandc021 */
+    do {
+        qemu_irq ack, req;
+        ds = sysbus_create_simple("ftnandc021", 0x90200000, pic[30]);
+        ack = qdev_get_gpio_in(ds, 0);
+        req = qdev_get_gpio_in(s->hdma, 15);
+        qdev_connect_gpio_out(s->hdma, 15, ack);
+        qdev_connect_gpio_out(ds, 0, req);
+    } while(0);
+
+    /* ftsdc010 */
+#if 0
+    do {
+        qemu_irq ack, req;
+        ds = sysbus_create_simple("ftsdc010", 0x90500000, pic[38]);
+        ack = qdev_get_gpio_in(ds, 0);
+        req = qdev_get_gpio_in(s->hdma, 14);
+        qdev_connect_gpio_out(s->hdma, 14, ack);
+        qdev_connect_gpio_out(ds, 0, req);
+    } while(0);
+#endif
+    do {
+        qemu_irq ack, req;
+        ds = sysbus_create_simple("ftsdc010", 0x90600000, pic[39]);
+        ack = qdev_get_gpio_in(ds, 0);
+        req = qdev_get_gpio_in(s->hdma, 13);
+        qdev_connect_gpio_out(s->hdma, 13, ack);
+        qdev_connect_gpio_out(ds, 0, req);
+    } while(0);
+
+#if 0
+    /* fusbh200 */
+    sysbus_create_simple("faraday-ehci-usb", 0x90800000, pic[36]);
+    
+    /* fotg210 */
+    sysbus_create_simple("faraday-ehci-usb", 0x90900000, pic[37]);
+#endif
+
+    /* 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],  /* Global (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 */
+    do {
+        SSIBus *spi;
+        DeviceState *fl;
+        int nr_flash = 1;
+        qemu_irq cs_line;
+        qemu_irq ack, req;
+        
+        ds = qdev_create(NULL, "ftssp010");
+        
+        /* i2s */
+        qdev_prop_set_ptr(ds, "codec_i2c", s->i2c[0]);
+        qdev_init_nofail(ds);
+        sysbus_mmio_map(sysbus_from_qdev(ds), 0, 0x92700000);
+        sysbus_connect_irq(sysbus_from_qdev(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(sysbus_from_qdev(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);
+    } while(0);
+
+    /* ftddrII030 */
+    a369_ddrc_init(s, 0x93100000);
+
+    /* ftahbc020 */
+    a369_ahbc_init(s, 0x94000000);
+
+    /* Parallel NOR Flash */
+    do {
+        DriveInfo *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)) {
+            fprintf(stderr, "qemu: Error registering flash memory.\n");
+        }
+    } while(0);
+}
+
+static void a369_board_reset(void *opaque)
+{
+    struct 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(sysbus_from_qdev(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 a369_state *s = g_new(struct 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) {
+        struct arm_boot_info *bi = g_new(struct arm_boot_info, 1);
+        
+        /* RAM Address Binding */
+        memory_region_add_subregion(s->as, 0x00000000, s->ram);
+        
+        /* Boot Info */
+        memset(bi, 0, sizeof(*bi));
+        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(sysbus_from_qdev(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/a369_scu.c b/hw/a369_scu.c
new file mode 100644
index 0000000..ee548d4
--- /dev/null
+++ b/hw/a369_scu.c
@@ -0,0 +1,192 @@
+/*
+ * QEMU model of the A369 SCU
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Copyright (C) 2012 Dante Su <dantesu@faraday-tech.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "sysemu/sysemu.h"
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    qemu_irq     irq;
+    
+    /* HW register caches */
+    uint32_t general_cfg;
+    uint32_t sclk_cfg0;
+    uint32_t sclk_cfg1;
+    uint32_t mfpsr0;
+    uint32_t mfpsr1;
+
+} a369_scu_state;
+
+static uint64_t
+a369_scu_mem_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    a369_scu_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->general_cfg;
+    case 0x204:
+        return 0x00001cc8;
+    case 0x228:
+        return s->sclk_cfg0;
+    case 0x22c:
+        return s->sclk_cfg1;
+    case 0x230:
+        return 0x00003fff;
+    case 0x238:
+        return s->mfpsr0;
+    case 0x23c:
+        return s->mfpsr1;
+    case 0x240:
+        return 0x11111111;
+    case 0x244:
+        return 0x11111111;
+    case 0x254:
+        return 0x00000303;
+    case 0x258:
+        return 0x8000007f;
+    }
+
+    return 0;
+}
+
+static void
+a369_scu_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
+{
+    a369_scu_state *s = opaque;
+    
+    switch(addr) {
+    case 0x200:
+        s->general_cfg = (uint32_t)val;
+        break;
+    case 0x228:
+        s->sclk_cfg0 = (uint32_t)val;
+        break;
+    case 0x22c:
+        s->sclk_cfg1 = (uint32_t)val;
+        break;
+    case 0x238:
+        s->mfpsr0 = (uint32_t)val;
+        break;
+    case 0x23c:
+        s->mfpsr1 = (uint32_t)val;
+        break;
+    default:
+        break;
+    }
+}
+
+static const MemoryRegionOps bus_ops = {
+    .read  = a369_scu_mem_read,
+    .write = a369_scu_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void a369_scu_reset(DeviceState *d)
+{
+    a369_scu_state *s = DO_UPCAST(a369_scu_state, busdev.qdev, d);
+    
+    s->general_cfg = 0x00001078;
+    s->sclk_cfg0   = 0x26877330;
+    s->sclk_cfg1   = 0x000a0a0a;
+    s->mfpsr0      = 0x00000241;
+    s->mfpsr1      = 0x00000000;
+}
+
+static int a369_scu_init(SysBusDevice *dev)
+{
+    a369_scu_state *s = FROM_SYSBUS(typeof(*s), dev);
+    
+    memory_region_init_io(&s->iomem, &bus_ops, s, "a369_scu", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+    sysbus_init_irq(dev, &s->irq);
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_a369_scu = {
+    .name = "a369_scu",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(general_cfg, a369_scu_state),
+        VMSTATE_UINT32(sclk_cfg0, a369_scu_state),
+        VMSTATE_UINT32(sclk_cfg1, a369_scu_state),
+        VMSTATE_UINT32(mfpsr0, a369_scu_state),
+        VMSTATE_UINT32(mfpsr1, a369_scu_state),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void a369_scu_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+    DeviceClass *k = DEVICE_CLASS(klass);
+
+    sdc->init  = a369_scu_init;
+    k->vmsd    = &vmstate_a369_scu;
+    k->reset   = a369_scu_reset;
+    k->no_user = 1;
+}
+
+static TypeInfo a369_scu_info = {
+    .name           = "a369_scu",
+    .parent         = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(a369_scu_state),
+    .class_init     = a369_scu_class_init,
+};
+
+static void a369_scu_register_types(void)
+{
+    type_register_static(&a369_scu_info);
+}
+
+type_init(a369_scu_register_types)
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 6d049e7..4cc85e5 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 += a369.o a369_scu.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
-- 
1.7.9.5


********************* Confidentiality Notice ************************
This electronic message and any attachments may contain
confidential and legally privileged information or
information which is otherwise protected from disclosure.
If you are not the intended recipient,please do not disclose
the contents, either in whole or in part, to anyone,and
immediately delete the message and any attachments from
your computer system and destroy all hard copies.
Thank you for your cooperation.
***********************************************************************

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [Qemu-devel] [PATCH 02/18] hw: add QEMU model for Faraday APB DMA
  2013-01-18  6:28 [Qemu-devel] [PATCH 01/18] hw: add Faraday a369 SoC platform support Dante
@ 2013-01-18  6:28 ` Dante
  2013-01-19  8:51   ` Blue Swirl
  2013-01-18  6:28 ` [Qemu-devel] [PATCH 03/18] hw: add QEMU model for Faraday AHB DMA Dante
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 8+ messages in thread
From: Dante @ 2013-01-18  6:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: peter.maydell, paul, dantesu

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/ftapbbrg020.c |  485 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/ftapbbrg020.h |   43 +++++
 2 files changed, 528 insertions(+)
 create mode 100644 hw/ftapbbrg020.c
 create mode 100644 hw/ftapbbrg020.h

diff --git a/hw/ftapbbrg020.c b/hw/ftapbbrg020.c
new file mode 100644
index 0000000..3378312
--- /dev/null
+++ b/hw/ftapbbrg020.c
@@ -0,0 +1,485 @@
+/*
+ * QEMU model of the FTAPBBRG020 DMA Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Copyright (C) 2012 Dante Su <dantesu@faraday-tech.com>
+ *
+ * Note: The FTAPBBRG020 DMA decreasing address mode is not implemented.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/blockdev.h"
+
+#include "ftapbbrg020.h"
+
+#ifndef min
+#define min(a, b)    ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef max
+#define max(a, b)    ((a) > (b) ? (a) : (b))
+#endif
+
+typedef struct _ftapbbrg020_state ftapbbrg020_state;
+
+typedef struct _ftapbbrg020_chan {
+    ftapbbrg020_state *chip;
+
+    int id;
+    int burst;
+    int src_bw;
+    int src_stride;
+    int dst_bw;
+    int dst_stride;
+    
+    /* HW register caches */
+    uint32_t src;
+    uint32_t dst;
+    uint32_t len;
+    uint32_t cmd;
+} ftapbbrg020_chan;
+
+typedef struct _ftapbbrg020_state {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    qemu_irq irq;
+    
+    ftapbbrg020_chan chan[4];
+    qemu_irq         ack[16];
+    uint32_t         req;
+
+    int busy;    /* Busy Channel ID */
+    QEMUTimer *qtimer;
+
+} ftapbbrg020_state;
+
+static inline uint32_t
+ftapbbrg020_get_isr(ftapbbrg020_state *s)
+{
+    int i;
+    uint32_t isr = 0;
+    ftapbbrg020_chan *chan;
+
+    for (i = 0; i < 4; ++i) {
+        chan = s->chan + i;
+        isr |= (chan->cmd & 0x12);
+    }
+
+    return isr;
+}
+
+static inline void 
+ftapbbrg020_update_irq(ftapbbrg020_state *s)
+{
+    uint32_t isr = ftapbbrg020_get_isr(s);
+    
+    if (isr)
+        qemu_set_irq(s->irq, 1);
+    else
+        qemu_set_irq(s->irq, 0);
+}
+
+static void 
+ftapbbrg020_chan_cmd_decode(ftapbbrg020_chan *c)
+{
+    uint32_t tmp;
+    
+    /* 1. decode burst size */
+    c->burst = (c->cmd & 0x08) ? 4 : 1;
+        
+    /* 2. decode source/destination width */
+    tmp = (c->cmd >> 20) & 0x03;
+    if (tmp > 2)
+        tmp = 2;
+    c->src_bw = c->dst_bw = 8 << (2 - tmp);
+
+    /* 3. decode source address stride */
+    switch((c->cmd >> 8) & 0x03) {
+    case 0:
+        c->src_stride = 0;
+        break;
+    case 1:
+        c->src_stride = c->src_bw >> 3;
+        break;
+    case 2:
+        c->src_stride = 2 * (c->src_bw >> 3);
+        break;
+    case 3:
+        c->src_stride = 4 * (c->src_bw >> 3);
+        break;
+    }
+
+    /* 4. decode destination address stride */
+    switch((c->cmd >> 12) & 0x03) {
+    case 0:
+        c->dst_stride = 0;
+        break;
+    case 1:
+        c->dst_stride = c->dst_bw >> 3;
+        break;
+    case 2:
+        c->dst_stride = 2 * (c->dst_bw >> 3);
+        break;
+    case 3:
+        c->dst_stride = 4 * (c->dst_bw >> 3);
+        break;
+    }
+}
+
+static void 
+ftapbbrg020_chan_start(ftapbbrg020_chan *c)
+{
+    ftapbbrg020_state *s = c->chip;
+    hwaddr src, dst, src_len, dst_len;
+    uint8_t *src_map = NULL, *dst_map = NULL;
+    uint8_t *src_ptr = NULL, *dst_ptr = NULL;
+    uint8_t buf[4096];
+    int src_hs = 0, dst_hs = 0, len = 0;
+
+    if (!(c->cmd & 0x01))
+        return;
+
+    s->busy = c->id;
+    
+    src = c->src;
+    dst = c->dst;
+    src_hs = (c->cmd >> 24) & 0xf;
+    dst_hs = (c->cmd >> 16) & 0xf;
+    src_len = c->src_stride ? (c->len * (c->src_bw >> 3)) : (c->src_bw >> 3);
+    dst_len = c->dst_stride ? (c->len * (c->dst_bw >> 3)) : (c->dst_bw >> 3);
+    if (!cpu_physical_memory_is_io(c->src))
+        src_map = src_ptr = cpu_physical_memory_map(c->src, &src_len, 0);
+    if (!cpu_physical_memory_is_io(c->dst))
+        dst_map = dst_ptr = cpu_physical_memory_map(c->dst, &dst_len, 1);
+
+    while (c->len > 0) {
+
+        if (src_hs && !(s->req & (1 << src_hs)))
+            break;
+
+        if (dst_hs && !(s->req & (1 << dst_hs)))
+            break;
+
+        len = min(sizeof(buf), c->burst * (c->src_bw >> 3));
+        
+        /* load data from source into local buffer */
+        if (src_ptr) {
+            if (c->src_stride) {
+                memcpy(buf, src_ptr, len);
+                src += len;
+                src_ptr += len;
+            } else {
+                int i;
+                switch(c->src_bw) {
+                case 8:
+                    for (i = 0; i < len; i += 1)
+                        *(uint8_t *)(buf + i) = *(uint8_t *)src_ptr;
+                    break;
+                case 16:
+                    for (i = 0; i < len; i += 2)
+                        *(uint16_t *)(buf + i) = *(uint16_t *)src_ptr;
+                    break;
+                default:
+                    for (i = 0; i < len; i += 4)
+                        *(uint32_t *)(buf + i) = *(uint32_t *)src_ptr;
+                    break;
+                }
+            }
+        } else {
+            uint32_t rl, stride = c->src_bw >> 3;
+            for (rl = 0; rl < len; rl += stride, src += c->src_stride)
+                cpu_physical_memory_read(src, (uint64_t *)(buf + rl), stride);
+        }
+
+        /* DMA Hardware Handshake */
+        if (src_hs) {
+            qemu_set_irq(s->ack[src_hs], 1);
+        }
+
+        /* store data into destination from local buffer */
+        if (dst_ptr) {
+            if (c->dst_stride) {
+                memcpy(dst_ptr, buf, len);
+                dst += len;
+                dst_ptr += len;
+            } else {
+                int i;
+                switch(c->dst_bw) {
+                case 8:
+                    for (i = 0; i < len; i += 1)
+                        *(uint8_t *)dst_ptr = *(uint8_t *)(buf + i);
+                    break;
+                case 16:
+                    for (i = 0; i < len; i += 2)
+                        *(uint16_t *)dst_ptr = *(uint16_t *)(buf + i);
+                    break;
+                default:
+                    for (i = 0; i < len; i += 4)
+                        *(uint32_t *)dst_ptr = *(uint32_t *)(buf + i);
+                    break;
+                }
+            }
+        } else {
+            uint32_t wl, stride = c->dst_bw >> 3;
+            for (wl = 0; wl < len; wl += stride, dst += c->dst_stride)
+                cpu_physical_memory_write(dst, (uint64_t *)(buf + wl), stride);
+        }
+
+        /* DMA Hardware Handshake */
+        if (dst_hs) {
+            qemu_set_irq(s->ack[dst_hs], 1);
+        }
+
+        /* update the channel transfer size */
+        c->len -= len / (c->src_bw >> 3);
+
+        if (c->len == 0) {
+            /* release the memory mappings */
+            if (src_map) {
+                cpu_physical_memory_unmap(src_map, src_len, 0, (hwaddr)(src_ptr - src_map));
+                src_map = src_ptr = NULL;
+            }
+            if (dst_map) {
+                cpu_physical_memory_unmap(dst_map, dst_len, 1, (hwaddr)(dst_ptr - dst_map));
+                dst_map = dst_ptr = NULL;
+            }
+            /* update the channel transfer status */
+            if (c->cmd & 0x04) {
+                c->cmd |= 0x02;
+                ftapbbrg020_update_irq(s);
+            }
+            /* clear start bit */
+            c->cmd &= 0xfffffffe;
+        }
+    }
+    
+    /* release the memory mappings */
+    if (src_map)
+        cpu_physical_memory_unmap(src_map, src_len, 0, (hwaddr)(src_ptr - src_map));
+    if (dst_map)
+        cpu_physical_memory_unmap(dst_map, dst_len, 1, (hwaddr)(dst_ptr - dst_map));
+
+    /* update src/dst address */
+    c->src = src;
+    c->dst = dst;
+
+    s->busy = -1;
+}
+
+static void 
+ftapbbrg020_chan_reset(ftapbbrg020_chan *c)
+{
+    c->cmd = 0;
+    c->src = 0;
+    c->dst = 0;
+    c->len = 0;
+}
+
+static void 
+ftapbbrg020_timer_tick(void *opaque)
+{
+    ftapbbrg020_state *s = (ftapbbrg020_state *)opaque;
+    ftapbbrg020_chan *c = NULL;
+    int i, jobs, done;
+    
+    jobs = 0;
+    done = 0;
+    for (i = 0; i < 4; ++i) {
+        c = s->chan + i;
+        if (c->cmd & 0x01) {
+            ++jobs;
+            ftapbbrg020_chan_start(c);
+            if (!(c->cmd & 0x01))
+                ++done;
+        }
+    }
+
+    /* ToDo: Use mutex to skip this periodic checker */
+    if (jobs - done > 0) {
+        qemu_mod_timer(s->qtimer, qemu_get_clock_ns(vm_clock) + 1);
+    } else {
+        qemu_mod_timer(s->qtimer, qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() >> 2));
+    }
+}
+
+static void 
+ftapbbrg020_handle_req(void *opaque, int line, int level)
+{
+    ftapbbrg020_state *s = (ftapbbrg020_state *)opaque;
+
+    if (level) {
+        s->req |= (1 << line);
+    } else {
+        s->req &= ~(1 << line);
+        qemu_set_irq(s->ack[line], 0);
+    }
+}
+
+static void ftapbbrg020_chip_reset(ftapbbrg020_state *s)
+{
+    int i;
+    
+    for (i = 0; i < 4; ++i) {
+        ftapbbrg020_chan_reset(s->chan + i);
+    }
+
+    /* We can assume our GPIO have been wired up now */
+    for (i = 0; i < 16; ++i) {
+        qemu_set_irq(s->ack[i], 0);
+    }
+    s->req = 0;
+}
+
+static uint64_t
+ftapbbrg020_mem_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    ftapbbrg020_state *s = opaque;
+    ftapbbrg020_chan  *c = NULL;
+    uint32_t ret = 0;
+
+    if (addr >= 0x80 && addr < 0xC0) {
+        c = s->chan + REG_CHAN_ID(addr);
+        switch(addr & 0x0f) {
+        case REG_CHAN_CMD:
+            return c->cmd;
+        case REG_CHAN_SRC:
+            return c->src;
+        case REG_CHAN_DST:
+            return c->dst;
+        case REG_CHAN_CYC:
+            return c->len;
+        }
+    } else {
+        switch(addr) {
+        case 0xC8:    /* revision register */
+            return 0x00010800;
+        default:
+            break;
+        }
+    }
+
+    return ret;
+}
+
+static void
+ftapbbrg020_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
+{
+    ftapbbrg020_state *s = opaque;
+    ftapbbrg020_chan  *c = NULL;
+    
+    if (addr >= 0x80 && addr < 0xC0) {
+        c = s->chan + REG_CHAN_ID(addr);
+        switch(addr & 0x0f) {
+        case REG_CHAN_CMD:
+            c->cmd = (uint32_t)val;
+            ftapbbrg020_update_irq(s);
+            if (c->cmd & 0x01) {
+                ftapbbrg020_chan_cmd_decode(c);
+                /* kick-off DMA engine */
+                qemu_mod_timer(s->qtimer, qemu_get_clock_ns(vm_clock) + 1);
+            }
+            break;
+        case REG_CHAN_SRC:
+            c->src = (uint32_t)val;
+            break;
+        case REG_CHAN_DST:
+            c->dst = (uint32_t)val;
+            break;
+        case REG_CHAN_CYC:
+            c->len = (uint32_t)val & 0x00ffffff;
+            break;
+        }
+    }
+}
+
+static const MemoryRegionOps ftapbbrg020_mem_ops = {
+    .read  = ftapbbrg020_mem_read,
+    .write = ftapbbrg020_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void ftapbbrg020_reset(DeviceState *d)
+{
+    ftapbbrg020_state *s = DO_UPCAST(ftapbbrg020_state, busdev.qdev, d);
+    ftapbbrg020_chip_reset(s);
+}
+
+static int ftapbbrg020_init(SysBusDevice *dev)
+{
+    ftapbbrg020_state *s = FROM_SYSBUS(typeof(*s), dev);
+    int i;
+
+    memory_region_init_io(&s->iomem, &ftapbbrg020_mem_ops, s, "ftapbbrg020", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+    sysbus_init_irq(dev, &s->irq);
+    qdev_init_gpio_in (&s->busdev.qdev, ftapbbrg020_handle_req, 16);
+    qdev_init_gpio_out(&s->busdev.qdev, s->ack, 16);
+    
+    s->busy = -1;
+    s->qtimer = qemu_new_timer_ns(vm_clock, ftapbbrg020_timer_tick, s);
+    for (i = 0; i < 4; ++i) {
+        ftapbbrg020_chan *c = s->chan + i;
+        c->id   = i;
+        c->chip = s;
+    }
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_ftapbbrg020 = {
+    .name = "ftapbbrg020",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void ftapbbrg020_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+    DeviceClass *k = DEVICE_CLASS(klass);
+
+    sdc->init  = ftapbbrg020_init;
+    k->vmsd    = &vmstate_ftapbbrg020;
+    k->reset   = ftapbbrg020_reset;
+    k->no_user = 1;
+}
+
+static TypeInfo ftapbbrg020_info = {
+    .name           = "ftapbbrg020",
+    .parent         = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(ftapbbrg020_state),
+    .class_init     = ftapbbrg020_class_init,
+};
+
+static void ftapbbrg020_register_types(void)
+{
+    type_register_static(&ftapbbrg020_info);
+}
+
+type_init(ftapbbrg020_register_types)
diff --git a/hw/ftapbbrg020.h b/hw/ftapbbrg020.h
new file mode 100644
index 0000000..0279f10
--- /dev/null
+++ b/hw/ftapbbrg020.h
@@ -0,0 +1,43 @@
+/*
+ *  arch/arm/mach-faraday/drivers/ftapbbrg020.h
+ *
+ *  Faraday FTAPBB020 APB Bridge with DMA function
+ *
+ *  Copyright (C) 2010 Faraday Technology
+ *  Copyright (C) 2012 Dante Su <dantesu@faraday-tech.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __FTAPBBRG020_H
+#define __FTAPBBRG020_H
+
+/*
+ * Channel base address
+ * @ch: channle id (0 <= id <= 3)
+ *      i.e. 0: Channel A
+ *           1: Channel B
+ *           2: Channel C
+ *           3: Channel D
+ */
+#define REG_CHAN_ID(off)    (((off) - 0x80) >> 4)
+#define REG_CHAN_BASE(ch)    (0x80 + ((ch) << 4))
+
+#define REG_CHAN_SRC        0x00
+#define REG_CHAN_DST        0x04
+#define REG_CHAN_CYC        0x08
+#define REG_CHAN_CMD        0x0C
+
+#endif    /* __FTAPBB020_H */
-- 
1.7.9.5


********************* Confidentiality Notice ************************
This electronic message and any attachments may contain
confidential and legally privileged information or
information which is otherwise protected from disclosure.
If you are not the intended recipient,please do not disclose
the contents, either in whole or in part, to anyone,and
immediately delete the message and any attachments from
your computer system and destroy all hard copies.
Thank you for your cooperation.
***********************************************************************

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [Qemu-devel] [PATCH 03/18] hw: add QEMU model for Faraday AHB DMA
  2013-01-18  6:28 [Qemu-devel] [PATCH 01/18] hw: add Faraday a369 SoC platform support Dante
  2013-01-18  6:28 ` [Qemu-devel] [PATCH 02/18] hw: add QEMU model for Faraday APB DMA Dante
@ 2013-01-18  6:28 ` Dante
  2013-01-18  6:28 ` [Qemu-devel] [PATCH 04/18] hw: add QEMU model for Faraday 1Gbps Ethernet MAC Dante
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 8+ messages in thread
From: Dante @ 2013-01-18  6:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: peter.maydell, paul, dantesu

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/ftdmac020.c |  625 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/ftdmac020.h |  117 +++++++++++
 2 files changed, 742 insertions(+)
 create mode 100644 hw/ftdmac020.c
 create mode 100644 hw/ftdmac020.h

diff --git a/hw/ftdmac020.c b/hw/ftdmac020.c
new file mode 100644
index 0000000..2841455
--- /dev/null
+++ b/hw/ftdmac020.c
@@ -0,0 +1,625 @@
+/*
+ * QEMU model of the FTDMAC020 DMA Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Copyright (C) 2012 Dante Su <dantesu@faraday-tech.com>
+ *
+ * Note: The FTDMAC020 decreasing address mode is not implemented.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/blockdev.h"
+
+#include "ftdmac020.h"
+
+#ifndef min
+#define min(a, b)    ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef max
+#define max(a, b)    ((a) > (b) ? (a) : (b))
+#endif
+
+typedef struct _ftdmac020_state ftdmac020_state;
+
+/**
+ * struct ftdmac020_lld - hardware link list descriptor.
+ * @src: source physical address
+ * @dst: destination physical addr
+ * @next: phsical address to the next link list descriptor
+ * @ctrl: control field
+ * @size: transfer size
+ *
+ * should be word aligned
+ */
+typedef struct _ftdmac020_lld {
+    uint32_t src;
+    uint32_t dst;
+    uint32_t next;
+    uint32_t ctrl;
+    uint32_t size;
+} ftdmac020_lld;
+
+typedef struct _ftdmac020_chan {
+    ftdmac020_state *chip;
+    
+    int id;
+    int burst;
+    int llp_cnt;
+    int src_bw;
+    int src_stride;
+    int dst_bw;
+    int dst_stride;
+    
+    /* HW register cache */
+    uint32_t ccr;
+    uint32_t cfg;
+    uint32_t src;
+    uint32_t dst;
+    uint32_t llp;
+    uint32_t len;
+} ftdmac020_chan;
+
+typedef struct _ftdmac020_state {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    qemu_irq irq;
+    
+    ftdmac020_chan chan[8];
+    qemu_irq       ack[16];
+    uint32_t       req;
+
+    int busy;    /* Busy Channel ID */
+    QEMUTimer *qtimer;
+    
+    /* HW register cache */
+    uint32_t tcisr;
+    uint32_t eaisr;
+    uint32_t tcsr;
+    uint32_t easr;
+    uint32_t cesr;
+    uint32_t cbsr;
+    uint32_t csr;
+    uint32_t sync;
+} ftdmac020_state;
+
+static inline uint32_t
+ftdmac020_get_isr(ftdmac020_state *s)
+{
+    uint32_t isr = 0;
+
+    /* 1. Checking TC interrupts */
+    isr |= s->tcisr & 0xff;
+    /* 2. Checking Error interrupts */
+    isr |= s->eaisr & 0xff;
+    /* 3. Checking Abort interrupts */
+    isr |= (s->eaisr >> 16) & 0xff;
+    
+    return isr;
+}
+
+static inline void 
+ftdmac020_update_irq(ftdmac020_state *s)
+{
+    uint32_t isr = ftdmac020_get_isr(s);
+    
+    if (isr)
+        qemu_set_irq(s->irq, 1);
+    else
+        qemu_set_irq(s->irq, 0);
+}
+
+static void 
+ftdmac020_chan_ccr_decode(ftdmac020_chan *c)
+{
+    uint32_t tmp;
+    
+    /* 1. decode burst size */
+    tmp = (c->ccr >> 16) & 0x07;
+    c->burst  = 1 << (tmp ? tmp + 1 : 0);
+        
+    /* 2. decode source width */
+    tmp = (c->ccr >> 11) & 0x03;
+    c->src_bw = 8 << tmp;
+    
+    /* 3. decode destination width */
+    tmp = (c->ccr >> 8) & 0x03;
+    c->dst_bw = 8 << tmp;
+    
+    /* 4. decode source address stride */
+    tmp = (c->ccr >> 5) & 0x03;
+    if(tmp == 2)
+        c->src_stride = 0;
+    else
+        c->src_stride = c->src_bw >> 3;
+
+    /* 5. decode destination address stride */
+    tmp = (c->ccr >> 3) & 0x03;
+    if (tmp == 2)
+        c->dst_stride = 0;
+    else
+        c->dst_stride = c->dst_bw >> 3;
+}
+
+static void 
+ftdmac020_chan_start(ftdmac020_chan *c)
+{
+    ftdmac020_state *s = c->chip;
+    hwaddr src, dst, src_len, dst_len;
+    int src_hs, dst_hs;
+    uint8_t *src_map = NULL, *dst_map = NULL;
+    uint8_t *src_ptr = NULL, *dst_ptr = NULL;
+    uint8_t buf[4096];
+    ftdmac020_lld desc;
+    int len = 0;
+
+    if (!(c->ccr & 0x01))
+        return;
+
+    s->busy = c->id;
+    
+    src = c->src;
+    dst = c->dst;
+    src_hs = (c->cfg & 0x0080) ? ((c->cfg >> 3) & 0x0f) : -1;
+    dst_hs = (c->cfg & 0x2000) ? ((c->cfg >> 9) & 0x0f) : -1;
+    src_len = c->src_stride ? (c->len * (c->src_bw >> 3)) : (c->src_bw >> 3);
+    dst_len = c->dst_stride ? (c->len * (c->dst_bw >> 3)) : (c->dst_bw >> 3);
+    if (!cpu_physical_memory_is_io(c->src))
+        src_map = src_ptr = cpu_physical_memory_map(c->src, &src_len, 0);
+    if (!cpu_physical_memory_is_io(c->dst))
+        dst_map = dst_ptr = cpu_physical_memory_map(c->dst, &dst_len, 1);
+
+    while (c->len > 0) {
+        
+        if ((src_hs >= 0) && !(s->req & (1 << src_hs)))
+            break;
+
+        if ((dst_hs >= 0) && !(s->req & (1 << dst_hs)))
+            break;
+
+        len = min(sizeof(buf), c->burst * (c->src_bw >> 3));
+
+        /* load data from source into local buffer */
+        if (src_ptr) {
+            if (c->src_stride) {
+                memcpy(buf, src_ptr, len);
+                src += len;
+                src_ptr += len;
+            } else {
+                int i;
+                switch(c->src_bw) {
+                case 8:
+                    for (i = 0; i < len; i += 1)
+                        *(uint8_t *)(buf + i) = *(uint8_t *)src_ptr;
+                    break;
+                case 16:
+                    for (i = 0; i < len; i += 2)
+                        *(uint16_t *)(buf + i) = *(uint16_t *)src_ptr;
+                    break;
+                case 32:
+                    for (i = 0; i < len; i += 4)
+                        *(uint32_t *)(buf + i) = *(uint32_t *)src_ptr;
+                    break;
+                default:
+                    for (i = 0; i < len; i += 8)
+                        *(uint64_t *)(buf + i) = *(uint64_t *)src_ptr;
+                    break;
+                }
+            }
+        } else {
+            uint32_t rl, stride = c->src_bw >> 3;
+            for (rl = 0; rl < len; rl += stride, src += c->src_stride)
+                cpu_physical_memory_read(src, (uint64_t *)(buf + rl), stride);
+        }
+
+        /* DMA Hardware Handshake */
+        if (src_hs >= 0) {
+            qemu_set_irq(s->ack[src_hs], 1);
+        }
+
+        /* store data into destination from local buffer */
+        if (dst_ptr) {
+            if (c->dst_stride) {
+                memcpy(dst_ptr, buf, len);
+                dst += len;
+                dst_ptr += len;
+            } else {
+                int i;
+                switch(c->dst_bw) {
+                case 8:
+                    for (i = 0; i < len; i += 1)
+                        *(uint8_t *)dst_ptr = *(uint8_t *)(buf + i);
+                    break;
+                case 16:
+                    for (i = 0; i < len; i += 2)
+                        *(uint16_t *)dst_ptr = *(uint16_t *)(buf + i);
+                    break;
+                case 32:
+                    for (i = 0; i < len; i += 4)
+                        *(uint32_t *)dst_ptr = *(uint32_t *)(buf + i);
+                    break;
+                default:
+                    for (i = 0; i < len; i += 8)
+                        *(uint64_t *)dst_ptr = *(uint64_t *)(buf + i);
+                    break;
+                }
+            }
+        } else {
+            uint32_t wl, stride = c->dst_bw >> 3;
+            for (wl = 0; wl < len; wl += stride, dst += c->dst_stride)
+                cpu_physical_memory_write(dst, (uint64_t *)(buf + wl), stride);
+        }
+
+        /* DMA Hardware Handshake */
+        if (dst_hs >= 0) {
+            qemu_set_irq(s->ack[dst_hs], 1);
+        }
+
+        /* update the channel transfer size */
+        c->len -= len / (c->src_bw >> 3);
+
+        if (c->len == 0) {
+            /* release the memory mappings */
+            if (src_map) {
+                cpu_physical_memory_unmap(src_map, src_len, 0, (hwaddr)(src_ptr - src_map));
+                src_map = src_ptr = NULL;
+            }
+            if (dst_map) {
+                cpu_physical_memory_unmap(dst_map, dst_len, 1, (hwaddr)(dst_ptr - dst_map));
+                dst_map = dst_ptr = NULL;
+            }
+            /* update the channel transfer status */
+            if (!(c->ccr & (1 << 31))) {
+                s->tcsr |= (1 << c->id);
+                if (!(c->cfg & 0x01))
+                    s->tcisr |= (1 << c->id);
+                ftdmac020_update_irq(s);
+            }
+            /* try to load next lld */
+            if (c->llp) {
+                c->llp_cnt += 1;
+                cpu_physical_memory_read(c->llp, &desc, sizeof(desc));
+                c->src = desc.src;
+                c->dst = desc.dst;
+                c->llp = desc.next & 0xfffffffc;
+                c->len = desc.size & 0x003fffff;
+                c->ccr = (c->ccr & 0x78f8c081) 
+                       | (((desc.ctrl >> 29) & 0x07) << 24)
+                       | ((desc.ctrl & (1 << 28)) ? (1 << 31) : 0)
+                       | (((desc.ctrl >> 25) & 0x07) << 11)
+                       | (((desc.ctrl >> 22) & 0x07) << 8)
+                       | (((desc.ctrl >> 20) & 0x03) << 5)
+                       | (((desc.ctrl >> 18) & 0x03) << 3)
+                       | (((desc.ctrl >> 16) & 0x03) << 1);
+                ftdmac020_chan_ccr_decode(c);
+                
+                src = c->src;
+                dst = c->dst;
+                src_len = c->src_stride ? (c->len * (c->src_bw >> 3)) : (c->src_bw >> 3);
+                dst_len = c->dst_stride ? (c->len * (c->dst_bw >> 3)) : (c->dst_bw >> 3);
+                if (!cpu_physical_memory_is_io(c->src))
+                    src_map = src_ptr = cpu_physical_memory_map(c->src, &src_len, 0);
+                if (!cpu_physical_memory_is_io(c->dst))
+                    dst_map = dst_ptr = cpu_physical_memory_map(c->dst, &dst_len, 1);
+            } else {
+                /* clear dma start bit */
+                c->ccr &= 0xfffffffe;
+            }
+        }
+    }
+    
+    /* release the memory mappings */
+    if (src_map)
+        cpu_physical_memory_unmap(src_map, src_len, 0, (hwaddr)(src_ptr - src_map));
+    if (dst_map)
+        cpu_physical_memory_unmap(dst_map, dst_len, 1, (hwaddr)(dst_ptr - dst_map));
+
+    /* update dma src/dst address */
+    c->src = src;
+    c->dst = dst;
+    
+    s->busy = -1;
+}
+
+static void 
+ftdmac020_chan_reset(ftdmac020_chan *c)
+{
+    c->ccr = 0;
+    c->cfg = 0;
+    c->src = 0;
+    c->dst = 0;
+    c->llp = 0;
+    c->len = 0;
+}
+
+static void 
+ftdmac020_timer_tick(void *opaque)
+{
+    ftdmac020_state *s = (ftdmac020_state *)opaque;
+    ftdmac020_chan *c = NULL;
+    int i, jobs, done;
+    
+    jobs = 0;
+    done = 0;
+    for (i = 0; i < 8; ++i) {
+        c = s->chan + i;
+        if (c->ccr & 0x01) {
+            ++jobs;
+            ftdmac020_chan_start(c);
+            if (!(c->ccr & 0x01))
+                ++done;
+        }
+    }
+
+    /* ToDo: Use mutex to skip this periodic checker */
+    if (jobs - done > 0) {
+        qemu_mod_timer(s->qtimer, qemu_get_clock_ns(vm_clock) + 1);
+    } else {
+        qemu_mod_timer(s->qtimer, qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() >> 2));
+    }
+}
+
+static void 
+ftdmac020_handle_req(void *opaque, int line, int level)
+{
+    ftdmac020_state *s = (ftdmac020_state *)opaque;
+
+    if (level) {
+        s->req |= (1 << line);
+    } else {
+        s->req &= ~(1 << line);
+        qemu_set_irq(s->ack[line], 0);
+    }
+}
+
+static void ftdmac020_chip_reset(ftdmac020_state *s)
+{
+    int i;
+
+    s->tcisr = 0;
+    s->eaisr = 0;
+    s->tcsr = 0;
+    s->easr = 0;
+    s->cesr = 0;
+    s->cbsr = 0;
+    s->csr  = 0;
+    s->sync = 0;
+    
+    for (i = 0; i < 8; ++i) {
+        ftdmac020_chan_reset(s->chan + i);
+    }
+
+    /* We can assume our GPIO have been wired up now */
+    for (i = 0; i < 16; ++i) {
+        qemu_set_irq(s->ack[i], 0);
+    }
+    s->req = 0;
+}
+
+static uint64_t
+ftdmac020_mem_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    ftdmac020_state *s = opaque;
+    ftdmac020_chan  *c = NULL;
+    uint32_t i, ret = 0;
+    
+    if (addr < 0x100) {
+        switch(addr) {
+        case REG_ISR:
+            return ftdmac020_get_isr(s);
+        case REG_TCISR:
+            return s->tcisr;
+        case REG_EAISR:
+            return s->eaisr;
+        case REG_TCSR:
+            return s->tcsr;
+        case REG_EASR:
+            return s->easr;
+        case REG_CESR:
+            ret = 0;
+            for (i = 0; i < 8; ++i) {
+                c = s->chan + i;
+                ret |= (c->ccr & 0x01) ? (1 << i) : 0;
+            }
+            break;
+        case REG_CBSR:
+            return (s->busy > 0) ? (1 << s->busy) : 0;
+        case REG_CSR:
+            return s->csr;
+        case REG_SYNC:
+            return s->sync;
+        case REG_REVISION:
+            return 0x00011300;
+        case REG_FEATURE:
+            return 0x00008105;            
+        default:
+            break;
+        }
+    } else if (addr < 0x1f8) {
+        c = s->chan + REG_CHAN_ID(addr);
+        switch(addr & 0x1f) {
+        case REG_CHAN_CCR:
+            return c->ccr;
+        case REG_CHAN_CFG:
+            ret = c->cfg;
+            ret |= (s->busy == c->id) ? (1 << 8) : 0;
+            ret |= (c->llp_cnt & 0x0f) << 16;
+            break;
+        case REG_CHAN_SRC:
+            return c->src;
+        case REG_CHAN_DST:
+            return c->dst;
+        case REG_CHAN_LLP:
+            return c->llp;
+        case REG_CHAN_LEN:
+            return c->len;
+        }
+    }
+
+    return ret;
+}
+
+static void
+ftdmac020_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
+{
+    ftdmac020_state *s = opaque;
+    ftdmac020_chan  *c = NULL;
+    
+    if (addr < 0x100) {
+        switch(addr) {
+        case REG_TCCLR:
+            s->tcisr &= ~((uint32_t)val);
+            s->tcsr &= ~((uint32_t)val);
+            ftdmac020_update_irq(s);
+            break;
+        case REG_EACLR:
+            s->eaisr &= ~((uint32_t)val);
+            s->easr &= ~((uint32_t)val);
+            ftdmac020_update_irq(s);
+            break;
+        case REG_CSR:
+            s->csr = (uint32_t)val;
+            break;
+        case REG_SYNC:
+            /* In QEMU, devices are all in the same clock domain
+             * so there is nothing needs to be done.
+             */
+            s->sync = (uint32_t)val;
+            break;
+        default:
+            break;
+        }
+    } else if (addr < 0x1f8) {
+        c = s->chan + REG_CHAN_ID(addr);
+        switch(addr & 0x1f) {
+        case REG_CHAN_CCR:
+            if (!(c->ccr & 0x01) && (val & 0x01)) {
+                c->llp_cnt = 0;
+            }
+            c->ccr = (uint32_t)val & 0x87FFBFFF;
+            if (c->ccr & 0x01) {
+                ftdmac020_chan_ccr_decode(c);
+                /* kick-off DMA engine */
+                qemu_mod_timer(s->qtimer, qemu_get_clock_ns(vm_clock) + 1);
+            }
+            break;
+        case REG_CHAN_CFG:
+            c->cfg = (uint32_t)val & 0x3eff;
+            break;
+        case REG_CHAN_SRC:
+            c->src = (uint32_t)val;
+            break;
+        case REG_CHAN_DST:
+            c->dst = (uint32_t)val;
+            break;
+        case REG_CHAN_LLP:
+            c->llp = (uint32_t)val & 0xfffffffc;
+            break;
+        case REG_CHAN_LEN:
+            c->len = (uint32_t)val & 0x003fffff;
+            break;
+        default:
+            break;
+        }
+    }
+}
+
+static const MemoryRegionOps ftdmac020_mem_ops = {
+    .read  = ftdmac020_mem_read,
+    .write = ftdmac020_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void ftdmac020_reset(DeviceState *d)
+{
+    ftdmac020_state *s = DO_UPCAST(ftdmac020_state, busdev.qdev, d);
+    ftdmac020_chip_reset(s);
+}
+
+static int ftdmac020_init(SysBusDevice *dev)
+{
+    ftdmac020_state *s = FROM_SYSBUS(typeof(*s), dev);
+    int i;
+
+    memory_region_init_io(&s->iomem, &ftdmac020_mem_ops, s, "ftdmac020", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+    sysbus_init_irq(dev, &s->irq);
+    qdev_init_gpio_in (&s->busdev.qdev, ftdmac020_handle_req, 16);
+    qdev_init_gpio_out(&s->busdev.qdev, s->ack, 16);
+    
+    s->busy = -1;
+    s->qtimer = qemu_new_timer_ns(vm_clock, ftdmac020_timer_tick, s);
+    for (i = 0; i < 8; ++i) {
+        ftdmac020_chan *c = s->chan + i;
+        c->id   = i;
+        c->chip = s;
+    }
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_ftdmac020 = {
+    .name = "ftdmac020",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(tcisr, ftdmac020_state),
+        VMSTATE_UINT32(eaisr, ftdmac020_state),
+        VMSTATE_UINT32(tcsr, ftdmac020_state),
+        VMSTATE_UINT32(easr, ftdmac020_state),
+        VMSTATE_UINT32(cesr, ftdmac020_state),
+        VMSTATE_UINT32(cbsr, ftdmac020_state),
+        VMSTATE_UINT32(csr, ftdmac020_state),
+        VMSTATE_UINT32(sync, ftdmac020_state),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void ftdmac020_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+    DeviceClass *k = DEVICE_CLASS(klass);
+
+    sdc->init  = ftdmac020_init;
+    k->vmsd    = &vmstate_ftdmac020;
+    k->reset   = ftdmac020_reset;
+    k->no_user = 1;
+}
+
+static TypeInfo ftdmac020_info = {
+    .name           = "ftdmac020",
+    .parent         = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(ftdmac020_state),
+    .class_init     = ftdmac020_class_init,
+};
+
+static void ftdmac020_register_types(void)
+{
+    type_register_static(&ftdmac020_info);
+}
+
+type_init(ftdmac020_register_types)
diff --git a/hw/ftdmac020.h b/hw/ftdmac020.h
new file mode 100644
index 0000000..75df3bc
--- /dev/null
+++ b/hw/ftdmac020.h
@@ -0,0 +1,117 @@
+/*
+ *  arch/arm/mach-faraday/drivers/ftdmac020.h
+ *
+ *  Faraday FTDMAC020 DMA controller
+ *
+ *  Copyright (C) 2010 Faraday Technology
+ *  Copyright (C) 2011 Dante Su <dantesu@faraday-tech.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __FTDMAC020_H
+#define __FTDMAC020_H
+
+#define REG_ISR            0x00    /* Interrupt Status Register */
+#define REG_TCISR        0x04    /* Terminal Count Interrupt Status Register */
+#define REG_TCCLR        0x08    /* Terminal Count Status Clear Register */
+#define REG_EAISR        0x0c    /* Error/Abort Interrupt Status Register */
+#define REG_EACLR        0x10    /* Error/Abort Status Clear Register */
+#define REG_TCSR        0x14    /* Terminal Count Status Register */
+#define REG_EASR        0x18    /* Error/Abort Status Register */
+#define REG_CESR        0x1c    /* Channel Enable Status Register */
+#define REG_CBSR        0x20    /* Channel Busy Status Register */
+#define REG_CSR            0x24    /* Configuration Status Register */
+#define REG_SYNC        0x28    /* Synchronization Register */
+#define REG_REVISION    0x30
+#define REG_FEATURE        0x34
+
+#define REG_CHAN_ID(addr)    (((addr) - 0x100) >> 5)
+#define REG_CHAN_BASE(ch)    (0x100 + ((ch) << 5))
+
+#define REG_CHAN_CCR    0x00
+#define REG_CHAN_CFG    0x04
+#define REG_CHAN_SRC    0x08
+#define REG_CHAN_DST    0x0C
+#define REG_CHAN_LLP    0x10
+#define REG_CHAN_LEN    0x14
+
+/*
+ * Feature register
+ */
+#define FEATURE_NCHAN(f)        (((f) >> 12) & 0xF)
+#define FEATURE_BRIDGE(f)        ((f) & (1 << 10))
+#define FEATURE_DUALBUS(f)        ((f) & (1 << 9))
+#define FEATURE_LLP(f)            ((f) & (1 << 8))
+#define FEATURE_FIFOAW(f)        ((f) & 0xF)
+
+/*
+ * Channel control register
+ */
+#define CCR_ENABLE                (1 << 0)
+#define CCR_DST_M1                (1 << 1)
+#define CCR_SRC_M1                (1 << 2)
+#define CCR_DST_INC                (0 << 3)
+#define CCR_DST_DEC                (1 << 3)
+#define CCR_DST_FIXED            (2 << 3)
+#define CCR_SRC_INC                (0 << 5)
+#define CCR_SRC_DEC                (1 << 5)
+#define CCR_SRC_FIXED            (2 << 5)
+#define CCR_HANDSHAKE            (1 << 7)
+#define CCR_DST_WIDTH_8            (0 << 8)
+#define CCR_DST_WIDTH_16        (1 << 8)
+#define CCR_DST_WIDTH_32        (2 << 8)
+#define CCR_DST_WIDTH_64        (3 << 8)
+#define CCR_SRC_WIDTH_8            (0 << 11)
+#define CCR_SRC_WIDTH_16        (1 << 11)
+#define CCR_SRC_WIDTH_32        (2 << 11)
+#define CCR_SRC_WIDTH_64        (3 << 11)
+#define CCR_ABORT                (1 << 15)
+#define CCR_BURST_1                (0 << 16)
+#define CCR_BURST_4                (1 << 16)
+#define CCR_BURST_8                (2 << 16)
+#define CCR_BURST_16            (3 << 16)
+#define CCR_BURST_32            (4 << 16)
+#define CCR_BURST_64            (5 << 16)
+#define CCR_BURST_128            (6 << 16)
+#define CCR_BURST_256            (7 << 16)
+#define CCR_PRIVILEGED            (1 << 19)
+#define CCR_BUFFERABLE            (1 << 20)
+#define CCR_CACHEABLE            (1 << 21)
+#define CCR_PRIO_0                (0 << 22)
+#define CCR_PRIO_1                (1 << 22)
+#define CCR_PRIO_2                (2 << 22)
+#define CCR_PRIO_3                (3 << 22)
+#define CCR_FIFOTH_1            (0 << 24)
+#define CCR_FIFOTH_2            (1 << 24)
+#define CCR_FIFOTH_4            (2 << 24)
+#define CCR_FIFOTH_8            (3 << 24)
+#define CCR_FIFOTH_16            (4 << 24)
+#define CCR_MASK_TC                (1 << 31)
+
+/*
+ * Channel configuration register
+ */
+#define CFG_MASK_TCI            (1 << 0)    /* mask tc interrupt */
+#define CFG_MASK_EI                (1 << 1)    /* mask error interrupt */
+#define CFG_MASK_AI                (1 << 2)    /* mask abort interrupt */
+#define CFG_SRC_HANDSHAKE(x)    (((x) & 0xf) << 3)
+#define CFG_SRC_HANDSHAKE_EN    (1 << 7)
+#define CFG_BUSY                (1 << 8)
+#define CFG_DST_HANDSHAKE(x)    (((x) & 0xf) << 9)
+#define CFG_DST_HANDSHAKE_EN    (1 << 13)
+#define CFG_LLP_CNT(cfg)        (((cfg) >> 16) & 0xf)
+
+#endif    /* __FTDMAC020_H */
-- 
1.7.9.5


********************* Confidentiality Notice ************************
This electronic message and any attachments may contain
confidential and legally privileged information or
information which is otherwise protected from disclosure.
If you are not the intended recipient,please do not disclose
the contents, either in whole or in part, to anyone,and
immediately delete the message and any attachments from
your computer system and destroy all hard copies.
Thank you for your cooperation.
***********************************************************************

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [Qemu-devel] [PATCH 04/18] hw: add QEMU model for Faraday 1Gbps Ethernet MAC
  2013-01-18  6:28 [Qemu-devel] [PATCH 01/18] hw: add Faraday a369 SoC platform support Dante
  2013-01-18  6:28 ` [Qemu-devel] [PATCH 02/18] hw: add QEMU model for Faraday APB DMA Dante
  2013-01-18  6:28 ` [Qemu-devel] [PATCH 03/18] hw: add QEMU model for Faraday AHB DMA Dante
@ 2013-01-18  6:28 ` Dante
  2013-01-19  8:41   ` Blue Swirl
  2013-01-18  6:28 ` [Qemu-devel] [PATCH 05/18] hw: add QEMU model for Faraday 10/100Mbps " Dante
  2013-01-19  8:15 ` [Qemu-devel] [PATCH 01/18] hw: add Faraday a369 SoC platform support Blue Swirl
  4 siblings, 1 reply; 8+ messages in thread
From: Dante @ 2013-01-18  6:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: peter.maydell, paul, dantesu

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/ftgmac100.c |  621 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/ftgmac100.h |  191 +++++++++++++++++
 2 files changed, 812 insertions(+)
 create mode 100644 hw/ftgmac100.c
 create mode 100644 hw/ftgmac100.h

diff --git a/hw/ftgmac100.c b/hw/ftgmac100.c
new file mode 100644
index 0000000..d66c719
--- /dev/null
+++ b/hw/ftgmac100.c
@@ -0,0 +1,621 @@
+/*
+ * QEMU model of the FTGMAC100 Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Copyright (C) 2012 Dante Su <dantesu@faraday-tech.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "sysemu/sysemu.h"
+#include "net/net.h"
+
+#include "ftgmac100.h"
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion mmio;
+
+    QEMUTimer *qtimer;
+    qemu_irq irq;
+    NICState *nic;
+    NICConf conf;
+
+    uint32_t isr;
+    uint32_t ier;
+    uint32_t mhash[2];
+    uint32_t tx_bar;
+    uint32_t rx_bar;
+    uint32_t hptx_bar;
+    uint32_t tx_idx;
+    uint32_t rx_idx;
+    uint32_t hptx_idx;
+    uint32_t maccr;
+    uint32_t macsr;
+    uint32_t phycr;
+    uint32_t phycr_rd;
+    
+    struct {
+        uint8_t  buf[9220];
+        uint32_t len;
+    } txbuff;
+    
+    uint32_t rx_pkt;
+    uint32_t rx_bcst;
+    uint32_t rx_mcst;
+    uint16_t rx_runt;
+    uint16_t rx_drop;
+    uint16_t rx_crc;
+    uint16_t rx_ftl;
+    uint32_t tx_pkt;
+
+} ftgmac100_state;
+
+static inline uint8_t bitrev8(uint8_t v)
+{
+    uint8_t r = 0;
+    int i;    
+    for(i = 0; i < 8; ++i) {
+        if (v & (1 << i))
+            r |= (1 << (7 - i));
+    }
+    return r;
+}
+
+static int ftgmac100_mcast_hash (ftgmac100_state *s, const uint8_t *data)
+{
+#define CRCPOLY_BE    0x04c11db7
+    int i, len;
+    uint32_t crc = 0xFFFFFFFF;
+
+    /* 
+     * 2011.12.28 
+     * It's a FTGMAC100 hardware bug reported by MTD Jeffrey & Jason
+     */
+    if (s->maccr & MACCR_GMODE)
+        len = 5;
+    else
+        len = 6;
+ 
+    while (len--) {
+        uint32_t c = *(data++);
+        for (i = 0; i < 8; ++i) {
+            crc = (crc << 1)^ ((((crc>>31)^c)&0x01) ? CRCPOLY_BE : 0);
+            c >>= 1;
+        }
+    }
+    crc = ~crc;
+ 
+    /* Reverse CRC32 and return MSB 6 bits only */
+    return bitrev8(crc >> 24) >> 2;
+}
+
+static inline void ftgmac100_update_irq(ftgmac100_state *s)
+{
+    if (s->isr & s->ier)
+        qemu_set_irq(s->irq, 1);
+    else
+        qemu_set_irq(s->irq, 0);
+}
+
+static int ftgmac100_can_receive(NetClientState *nc)
+{
+    ftgmac100_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+    ftgmac100_rxdesc_t rxd;
+    hwaddr off = s->rx_bar + s->rx_idx * sizeof(rxd);
+
+    if ((s->maccr & (MACCR_RCV_EN | MACCR_RDMA_EN)) != (MACCR_RCV_EN | MACCR_RDMA_EN))
+        return 0;
+
+    cpu_physical_memory_read(off, (uint8_t *)&rxd, sizeof(rxd));
+
+    return !rxd.owner;
+}
+
+static ssize_t ftgmac100_receive(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+    const uint8_t *ptr = buf;
+    hwaddr off;
+    size_t len;
+    ftgmac100_rxdesc_t rxd;
+    ftgmac100_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+    int bcst, mcst;
+    
+    if ((s->maccr & (MACCR_RCV_EN | MACCR_RDMA_EN)) != (MACCR_RCV_EN | MACCR_RDMA_EN))
+        return -1;
+
+    /* if it's a broadcast */
+    if ((buf[0] == 0xff) && (buf[1] == 0xff) && (buf[2] == 0xff)
+            && (buf[3] == 0xff) && (buf[4] == 0xff) && (buf[5] == 0xff)) {
+        bcst = 1;
+        if (!(s->maccr & MACCR_RCV_ALL) && !(s->maccr & MACCR_RX_BROADPKT)) {
+            printf("[qemu] ftgmac100_receive: bcst filtered\n");
+            return -1;
+        }
+    } else {
+        bcst = 0;
+    }
+
+    /* if it's a multicast */
+    if ((buf[0] == 0x01) && (buf[1] == 0x00) && (buf[2] == 0x5e) 
+        && (buf[3] <= 0x7f)) {
+        mcst = 1;
+        if (!(s->maccr & MACCR_RCV_ALL) && !(s->maccr & MACCR_RX_MULTIPKT)) {
+            int hash;
+            if (!(s->maccr & MACCR_HT_MULTI_EN)) {
+                printf("[qemu] ftgmac100_receive: mcst filtered\n");
+                return -1;
+            }
+            hash = ftgmac100_mcast_hash(s, buf);
+            if (!(s->mhash[hash / 32] & (1 << (hash % 32)))) {
+                printf("[qemu] ftgmac100_receive: mcst filtered\n");
+                return -1;
+            }
+        }
+    } else {
+        mcst = 0;
+    }
+
+    /* check if the destination matches NIC mac address */
+    if (!(s->maccr & MACCR_RCV_ALL) && !bcst && !mcst) {
+        if (memcmp(s->conf.macaddr.a, buf, 6)) {
+#if 0
+            printf("[qemu] ftgmac100_receive: pkt filtered\n");
+            printf("[qemu] packet dump: (IF MAC=%02X:%02X:%02X:%02X:%02X:%02X)\n",
+                s->conf.macaddr.a[0], s->conf.macaddr.a[1],
+                s->conf.macaddr.a[2], s->conf.macaddr.a[3],
+                s->conf.macaddr.a[4], s->conf.macaddr.a[5]);
+            for (off = 0; off < 14; ++off) {
+                printf("%02X ", buf[off]);
+            }
+            printf("\n");
+#endif
+            return -1;
+        }
+    }
+
+    while (size > 0) {
+        off = s->rx_bar + s->rx_idx * sizeof(rxd);
+        cpu_physical_memory_read(off, (uint8_t *)&rxd, sizeof(rxd));
+        if (rxd.owner) {
+            s->isr |= ISR_NORXBUF;
+            printf("[qemu] ftgmac100: out of rxd!? (ptr=%p, buf=%p)\n", ptr, buf);
+            return -1;
+        }
+
+        if (ptr == buf)
+            rxd.frs = 1;
+        else
+            rxd.frs = 0;
+        
+        len = size > rxd.len ? rxd.len : size;
+        cpu_physical_memory_write(rxd.buf, (uint8_t *)ptr, len);
+        ptr  += len;
+        size -= len;
+
+        if (size <= 0)
+            rxd.lrs = 1;
+        else
+            rxd.lrs = 0;
+
+        rxd.len = len;
+        rxd.bcast = bcst;
+        rxd.mcast = mcst;
+        rxd.owner = 1;
+        
+        /* write-back the rx descriptor */
+        cpu_physical_memory_write(off, (uint8_t *)&rxd, sizeof(rxd));
+
+        if (rxd.end)
+            s->rx_idx = 0;
+        else
+            s->rx_idx += 1;
+    }
+    
+    /* 3. update interrupt signal */
+    s->isr |= ISR_RPKT_OK | ISR_RPKT_FINISH;
+    ftgmac100_update_irq(s);
+    
+    return (ssize_t)((uint32_t)ptr - (uint32_t)buf);
+}
+
+static void ftgmac100_transmit(ftgmac100_state *s, uint32_t *bar, uint32_t *idx)
+{
+    hwaddr off;
+    uint8_t *buf;
+    ftgmac100_txdesc_t txd;
+    
+    if ((s->maccr & (MACCR_XMT_EN | MACCR_XDMA_EN)) != (MACCR_XMT_EN | MACCR_XDMA_EN))
+        return;
+    
+    do {
+        off = (*bar) + (*idx) * sizeof(txd);
+        cpu_physical_memory_read(off, (uint8_t *)&txd, sizeof(txd));
+        if (!txd.owner) {
+            s->isr |= ISR_NOTXBUF;
+            break;
+        }
+        if (txd.fts)
+            s->txbuff.len = 0;
+        buf = s->txbuff.buf + s->txbuff.len;
+        cpu_physical_memory_read(txd.buf, (uint8_t *)buf, txd.len);
+        s->txbuff.len += txd.len;
+        if (txd.lts) {
+            if (s->maccr & MACCR_LOOP_EN) {
+                ftgmac100_receive(&s->nic->nc, s->txbuff.buf, s->txbuff.len);
+            } else {
+                qemu_send_packet(&s->nic->nc, s->txbuff.buf, s->txbuff.len);
+            }
+        }
+        if (txd.end)
+            *idx = 0;
+        else
+            *idx += 1;
+        if (txd.tx2fic)
+            s->isr |= ISR_XPKT_OK;
+        if (txd.txic)
+            s->isr |= ISR_XPKT_FINISH;
+
+        txd.owner = 0;
+        cpu_physical_memory_write(off, (uint8_t *)&txd, sizeof(txd));
+    } while(1);
+}
+
+static void ftgmac100_timer_tick(void *opaque)
+{
+    ftgmac100_state *s = opaque;
+    
+    /* 1. process high priority tx ring */
+    if (s->hptx_bar && (s->maccr & MACCR_HPTXR_EN)) {
+        ftgmac100_transmit(s, &s->hptx_bar, &s->hptx_idx);
+    }
+
+    /* 2. process normal priority tx ring */
+    if (s->tx_bar) {
+        ftgmac100_transmit(s, &s->tx_bar, &s->tx_idx);
+    }
+
+    /* 3. update interrupt signal */
+    ftgmac100_update_irq(s);
+}
+
+static uint64_t ftgmac100_mem_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    ftgmac100_state *s = opaque;
+    uint32_t rc = 0;
+
+    switch (addr) {
+    case REG_ISR:
+        return s->isr;
+    case REG_IMR:
+        return s->ier;
+    case REG_HMAC:
+        return s->conf.macaddr.a[1] 
+               | (s->conf.macaddr.a[0] << 8);
+    case REG_LMAC:
+        return s->conf.macaddr.a[5] 
+               | (s->conf.macaddr.a[4] << 8) 
+               | (s->conf.macaddr.a[3] << 16) 
+               | (s->conf.macaddr.a[2] << 24);
+    case REG_MHASH0:
+        return s->mhash[0];
+    case REG_MHASH1:
+        return s->mhash[1];
+    case REG_TXBAR:
+        return s->tx_bar;
+    case REG_RXBAR:
+        return s->rx_bar;
+    case REG_HPTXBAR:
+        return s->hptx_bar;
+    case REG_ITC:
+        return 0x00000000;
+    case REG_APTC:
+        return 0x00000001;
+    case REG_DBLAC:
+        return 0x00022f72;
+    case 0x3c:
+        return 0x0c000000;
+    case 0x40:
+        return 0x00000600;
+    case 0x44:
+        return 0x0000001b;
+    case 0x48:
+        return 0x000000f1;
+    case 0x4c:
+        return 0x00000640;
+    case REG_MACCR:
+        return s->maccr;
+    case REG_MACSR:
+        return s->macsr;
+    case REG_PHYCTRL:
+        return s->phycr;
+    case REG_PHYDATA:
+        do {
+            uint8_t dev = (s->phycr >> 16) & 0x1f;
+            uint8_t reg = (s->phycr >> 21) & 0x1f;
+            if (dev != 0)
+                break;
+            if (s->phycr_rd) {
+                switch(reg) {
+                case 0:    /* PHY control register */
+                    return 0x1140 << 16;
+                case 1:    /* PHY status register */
+                    return 0x796d << 16;
+                case 2:    /* PHY ID 1 register */
+                    return 0x0141 << 16;
+                case 3:    /* PHY ID 2 register */
+                    return 0x0cc2 << 16;
+                case 4:    /* Autonegotiation advertisement register */
+                    return 0x0de1 << 16;
+                case 5:    /* Autonegotiation partner abilities register */
+                    return 0x45e1 << 16;
+                case 17:/* Marvell 88E1111: PHY Specific Status Register - Copper */
+                    return ((2 << 14) | (1 << 13) | (1 << 11) | (1 << 10)) << 16;
+                }
+            }
+        } while(0);
+        break;
+    case REG_FCR:
+        return (2 << 9);
+    case REG_BPR:
+        return (4 << 8);
+    case REG_TXPTR:
+        return s->tx_idx;
+    case REG_HPTXPTR:
+        return s->hptx_idx;
+    case REG_RXPTR:
+        return s->rx_idx;
+    case REG_TXPKT:
+        return s->tx_pkt;
+    case REG_RXPKT:
+        return s->rx_pkt;
+    case REG_RXBCST:
+        return s->rx_bcst;
+    case REG_RXMCST:
+        return s->rx_mcst;
+    case REG_RXRUNT:
+        return s->rx_runt;
+    case REG_RXERR0:
+        return (s->rx_crc << 16) | (s->rx_ftl);
+    case REG_RXERR1:
+        return (s->rx_drop << 16);
+    default:
+        break;
+    }
+
+    return rc;
+}
+
+static void ftgmac100_chip_reset(ftgmac100_state *s)
+{
+    s->isr = 0;
+    s->ier = 0;
+    s->mhash[0] = 0;
+    s->mhash[1] = 0;
+    s->tx_bar = 0;
+    s->rx_bar = 0;
+    s->hptx_bar = 0;
+    s->tx_idx = 0;
+    s->rx_idx = 0;
+    s->hptx_idx = 0;
+    s->maccr = 0;
+    s->macsr = 0;
+    s->phycr = 0;
+    s->txbuff.len = 0;
+    s->rx_pkt = 0;
+    s->rx_bcst = 0;
+    s->rx_mcst = 0;
+    s->rx_runt = 0;
+    s->rx_drop = 0;
+    s->rx_crc = 0;
+    s->rx_ftl = 0;
+    s->tx_pkt = 0;
+    
+    if (s->qtimer)
+        qemu_del_timer(s->qtimer);
+
+    ftgmac100_update_irq(s);
+}
+
+static void ftgmac100_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
+{
+    ftgmac100_state *s = opaque;
+    
+    switch (addr) {
+    case REG_ISR:
+        s->isr &= ~((uint32_t)val);
+        ftgmac100_update_irq(s);
+        break;
+    case REG_IMR:
+        s->ier = (uint32_t)val;
+        ftgmac100_update_irq(s);
+        break;
+    case REG_HMAC:
+        s->conf.macaddr.a[1] = (val >> 0) & 0xff;
+        s->conf.macaddr.a[0] = (val >> 8) & 0xff;
+        break;
+    case REG_LMAC:
+        s->conf.macaddr.a[5] = (val >> 0) & 0xff;
+        s->conf.macaddr.a[4] = (val >> 8) & 0xff;
+        s->conf.macaddr.a[3] = (val >> 16) & 0xff;
+        s->conf.macaddr.a[2] = (val >> 24) & 0xff;
+        break;
+    case REG_MHASH0:
+        s->mhash[0] = (uint32_t)val;
+        break;
+    case REG_MHASH1:
+        s->mhash[1] = (uint32_t)val;
+        break;
+    case REG_TXBAR:
+        s->tx_bar = (uint32_t)val;
+        break;
+    case REG_RXBAR:
+        s->rx_bar = (uint32_t)val;
+        break;
+    case REG_HPTXBAR:
+        s->hptx_bar = (uint32_t)val;
+        break;
+    case REG_MACCR:
+        s->maccr = (uint32_t)val;
+        if (s->maccr & MACCR_SW_RST) {
+            ftgmac100_chip_reset(s);
+            s->maccr &= ~MACCR_SW_RST;
+        }
+        break;
+    case REG_MACSR:
+        s->macsr &= ~((uint32_t)val);
+        break;
+    case REG_PHYCTRL:
+        s->phycr = (uint32_t)val;
+        if (s->phycr & PHYCR_MDIORD)
+            s->phycr_rd = 1;
+        else
+            s->phycr_rd = 0;
+        s->phycr &= ~(PHYCR_MDIOWR | PHYCR_MDIORD);
+        break;
+    case REG_TXPD:
+    case REG_HPTXPD:
+        qemu_mod_timer(s->qtimer, qemu_get_clock_ns(vm_clock) + 1);
+        break;
+    default:
+        break;
+    }
+}
+
+static const MemoryRegionOps bus_ops = {
+    .read  = ftgmac100_mem_read,
+    .write = ftgmac100_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void ftgmac100_cleanup(NetClientState *nc)
+{
+    ftgmac100_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+    s->nic = NULL;
+}
+
+static NetClientInfo net_ftgmac100_info = {
+    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .size = sizeof(NICState),
+    .can_receive = ftgmac100_can_receive,
+    .receive = ftgmac100_receive,
+    .cleanup = ftgmac100_cleanup,
+};
+
+static void ftgmac100_reset(DeviceState *dev)
+{
+    ftgmac100_state *s = FROM_SYSBUS(ftgmac100_state, sysbus_from_qdev(dev));
+    
+    ftgmac100_chip_reset(s);
+}
+
+static int ftgmac100_init1(SysBusDevice *dev)
+{
+    ftgmac100_state *s = FROM_SYSBUS(typeof(*s), dev);
+
+    memory_region_init_io(&s->mmio, &bus_ops, s, "ftgmac100", 0x1000);
+    sysbus_init_mmio(dev, &s->mmio);
+    sysbus_init_irq(dev, &s->irq);
+
+    qemu_macaddr_default_if_unset(&s->conf.macaddr);
+    s->nic = qemu_new_nic(&net_ftgmac100_info, &s->conf,
+                          object_get_typename(OBJECT(dev)), dev->qdev.id, s);
+    qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+    
+    s->qtimer = qemu_new_timer_ns(vm_clock, ftgmac100_timer_tick, s);
+    
+    ftgmac100_chip_reset(s);
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_ftgmac100 = {
+    .name = "ftgmac100",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(ier, ftgmac100_state),
+        VMSTATE_UINT32(tx_bar, ftgmac100_state),
+        VMSTATE_UINT32(rx_bar, ftgmac100_state),
+        VMSTATE_UINT32(hptx_bar, ftgmac100_state),
+        VMSTATE_UINT32(tx_idx, ftgmac100_state),
+        VMSTATE_UINT32(rx_idx, ftgmac100_state),
+        VMSTATE_UINT32(hptx_idx, ftgmac100_state),
+        VMSTATE_UINT32(maccr, ftgmac100_state),
+        VMSTATE_UINT32(macsr, ftgmac100_state),
+        VMSTATE_UINT32(phycr, ftgmac100_state),
+        VMSTATE_UINT32_ARRAY(mhash, ftgmac100_state, 2),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property ftgmac100_properties[] = {
+    DEFINE_NIC_PROPERTIES(ftgmac100_state, conf),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void ftgmac100_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = ftgmac100_init1;
+    dc->reset = ftgmac100_reset;
+    dc->vmsd = &vmstate_ftgmac100;
+    dc->props = ftgmac100_properties;
+}
+
+static TypeInfo ftgmac100_info = {
+    .name           = "ftgmac100",
+    .parent         = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(ftgmac100_state),
+    .class_init     = ftgmac100_class_init,
+};
+
+static void ftgmac100_register_types(void)
+{
+    type_register_static(&ftgmac100_info);
+}
+
+/* Legacy helper function.  Should go away when machine config files are
+   implemented.  */
+void ftgmac100_init(NICInfo *nd, uint32_t base, qemu_irq irq);
+
+void ftgmac100_init(NICInfo *nd, uint32_t base, qemu_irq irq)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+
+    qemu_check_nic_model(nd, "ftgmac100");
+    dev = qdev_create(NULL, "ftgmac100");
+    qdev_set_nic_properties(dev, nd);
+    qdev_init_nofail(dev);
+    s = sysbus_from_qdev(dev);
+    sysbus_mmio_map(s, 0, base);
+    sysbus_connect_irq(s, 0, irq);
+}
+
+type_init(ftgmac100_register_types)
diff --git a/hw/ftgmac100.h b/hw/ftgmac100.h
new file mode 100644
index 0000000..c8c00f9
--- /dev/null
+++ b/hw/ftgmac100.h
@@ -0,0 +1,191 @@
+/*
+ *
+ * Copyright (C) 2011 Dante Su <dantesu@faraday-tech.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _FTGMAC100_H
+#define _FTGMAC100_H
+
+// --------------------------------------------------------------------
+//        FTGMAC100 MAC Registers
+// --------------------------------------------------------------------
+
+#define REG_ISR             0x00            // interrups status register
+#define REG_IMR             0x04            // interrupt maks register
+#define REG_HMAC            0x08            // MAC address (Most significant)
+#define REG_LMAC            0x0c            // MAC address (Least significant)
+#define REG_MHASH0          0x10            // Multicast Address Hash Table 0 register
+#define REG_MHASH1          0x14            // Multicast Address Hash Table 1 register
+#define REG_TXPD            0x18            // Transmit Poll Demand register
+#define REG_RXPD            0x1c            // Receive Poll Demand register
+#define REG_TXBAR           0x20            // Transmit Ring Base Address register
+#define REG_RXBAR           0x24            // Receive Ring Base Address register
+#define REG_HPTXPD          0x28            // High Priority Transmit Ring Base Address register
+#define REG_HPTXBAR         0x2C            // High Priority Receive Ring Base Address register
+#define REG_ITC             0x30            // interrupt timer control register
+#define REG_APTC            0x34            // Automatic Polling Timer control register
+#define REG_DBLAC           0x38            // DMA Burst Length and Arbitration control register
+
+#define REG_MACCR           0x50            // MAC control register
+#define REG_MACSR           0x54            // MAC status register
+#define REG_TSTMODE         0x58            // test mode register
+#define REG_PHYCTRL         0x60            // PHY control register
+#define REG_PHYDATA         0x64            // PHY Write Data register
+#define REG_FCR             0x68            // Flow Control register
+#define REG_BPR             0x6c            // back pressure register
+#define REG_WOLCR           0x70            // Wake-On-Lan control register
+#define REG_WOLSR           0x74            // Wake-On-Lan status register
+#define REG_WFCRC           0x78            // Wake-up Frame CRC register
+#define REG_WFBM1           0x80            // wake-up frame byte mask 1st double word register
+#define REG_WFBM2           0x84            // wake-up frame byte mask 2nd double word register
+#define REG_WFBM3           0x88            // wake-up frame byte mask 3rd double word register
+#define REG_WFBM4           0x8c            // wake-up frame byte mask 4th double word register
+#define REG_TXPTR           0x90
+#define REG_HPTXPTR         0x94
+#define REG_RXPTR           0x98
+#define REG_TXPKT           0xa0
+#define REG_TXERR0          0xa4
+#define REG_TXERR1          0xa8
+#define REG_TXERR2          0xac
+#define REG_RXPKT           0xb0
+#define REG_RXBCST          0xb4
+#define REG_RXMCST          0xb8
+#define REG_RXRUNT          0xc0
+#define REG_RXERR0          0xc4
+#define REG_RXERR1          0xc8
+
+//Interrupt status register(ISR), Interrupt mask register(IMR) bit setting
+#define ISR_NOHTXB            (1UL<<10)
+#define ISR_PHYSTS_CHG        (1UL<<9)
+#define ISR_AHB_ERR            (1UL<<8)
+#define ISR_XPKT_LOST        (1UL<<7)
+#define ISR_NOTXBUF            (1UL<<6)
+#define ISR_XPKT_OK            (1UL<<5)        // FIFO
+#define ISR_XPKT_FINISH        (1UL<<4)        // ETHER
+#define ISR_RPKT_LOST        (1UL<<3)
+#define ISR_NORXBUF            (1UL<<2)
+#define ISR_RPKT_OK            (1UL<<1)        // FIFO
+#define ISR_RPKT_FINISH        (1UL<<0)        // ETHER
+
+/* MACC control bits */
+#define MACCR_SW_RST            (1UL<<31)
+#define MACCR_100M                (1UL<<19)
+#define MACCR_CRC_DIS            (1UL<<18)
+#define MACCR_RX_BROADPKT        (1UL<<17)
+#define MACCR_RX_MULTIPKT        (1UL<<16)
+#define MACCR_HT_MULTI_EN        (1UL<<15)
+#define MACCR_RCV_ALL            (1UL<<14)
+#define MACCR_JUMBO_LF            (1UL<<13)
+#define MACCR_RX_RUNT            (1UL<<12)
+#define MACCR_CRC_APD            (1UL<<10)
+#define MACCR_GMODE                (1UL<<9)
+#define MACCR_FULLDUP            (1UL<<8)
+#define MACCR_ENRX_IN_HALFTX    (1UL<<7)
+#define MACCR_LOOP_EN            (1UL<<6)
+#define MACCR_HPTXR_EN            (1UL<<5)
+#define MACCR_VLAN_RM            (1UL<<4)
+#define MACCR_RCV_EN            (1UL<<3)
+#define MACCR_XMT_EN            (1UL<<2)
+#define MACCR_RDMA_EN            (1UL<<1)
+#define MACCR_XDMA_EN            (1UL<<0)
+
+// --------------------------------------------------------------------
+//        MII PHY Registers
+// --------------------------------------------------------------------
+
+//
+// Bits related to the MII interface
+//
+#define PHYCR_MDIOWR            (1 << 27)
+#define PHYCR_MDIORD            (1 << 26)
+
+// --------------------------------------------------------------------
+//        Receive Ring descriptor structure
+// --------------------------------------------------------------------
+typedef struct {
+    // RXDES0
+    uint32_t len: 14; // 0~13
+    uint32_t rsvd1: 1; // 14
+    uint32_t end: 1; //15
+    uint32_t mcast: 1; //16
+    uint32_t bcast: 1; //17
+    uint32_t rxerr: 1; //18
+    uint32_t crcerr: 1; //19
+    uint32_t ftl: 1; //20
+    uint32_t runt: 1; //21
+    uint32_t oddnb: 1; //22
+    uint32_t fifofull: 1; //23
+    uint32_t pauseopc: 1; //24
+    uint32_t pausefrm: 1; //25
+    uint32_t rsvd2: 2; //26 ~ 27
+    uint32_t lrs: 1; //28
+    uint32_t frs: 1; //29
+    uint32_t rsvd3: 1; //30
+    uint32_t owner: 1; //31 - 1:Software, 0: Hardware
+
+    // RXDES1
+    uint32_t vlantag: 16; // 0 ~ 15
+    uint32_t rsvd4: 4; // 16 ~ 19
+    uint32_t proto: 2; // 20 ~ 21
+    uint32_t llc: 1; // 22
+    uint32_t df: 1; // 23
+    uint32_t vlan: 1; // 24
+    uint32_t tcpcs: 1; // 25
+    uint32_t udpcs: 1; // 26
+    uint32_t ipcs: 1; // 27
+    uint32_t rsvd5: 4; // 28 ~ 31
+
+    // RXDES2
+    void    *skb;
+
+    // RXDES3
+    uint32_t buf;
+} ftgmac100_rxdesc_t;
+
+typedef struct {
+    // TXDES0
+    uint32_t len: 14;
+    uint32_t rsvd1: 1;
+    uint32_t end: 1;
+    uint32_t rsvd2: 3;
+    uint32_t crcerr: 1;
+    uint32_t rsvd3: 8;
+    uint32_t lts: 1;
+    uint32_t fts: 1;
+    uint32_t rsvd4: 1;
+    uint32_t owner: 1; // 31 - 1:Hardware, 0: Software
+
+    // TXDES1
+    uint32_t vlantag: 16;
+    uint32_t vlan:   1;
+    uint32_t tcpcs:  1;
+    uint32_t udpcs:  1;
+    uint32_t ipcs:   1;
+    uint32_t rsvd5:  2;
+    uint32_t llc:    1;
+    uint32_t rsvd6:  7;
+    uint32_t tx2fic: 1;
+    uint32_t txic:   1;
+
+    // TXDES2
+    void    *skb;
+
+    // TXDES3
+    uint32_t buf;
+} ftgmac100_txdesc_t;
+
+#endif    /* #ifndef _FTGMAC100_H */
-- 
1.7.9.5


********************* Confidentiality Notice ************************
This electronic message and any attachments may contain
confidential and legally privileged information or
information which is otherwise protected from disclosure.
If you are not the intended recipient,please do not disclose
the contents, either in whole or in part, to anyone,and
immediately delete the message and any attachments from
your computer system and destroy all hard copies.
Thank you for your cooperation.
***********************************************************************

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [Qemu-devel] [PATCH 05/18] hw: add QEMU model for Faraday 10/100Mbps Ethernet MAC
  2013-01-18  6:28 [Qemu-devel] [PATCH 01/18] hw: add Faraday a369 SoC platform support Dante
                   ` (2 preceding siblings ...)
  2013-01-18  6:28 ` [Qemu-devel] [PATCH 04/18] hw: add QEMU model for Faraday 1Gbps Ethernet MAC Dante
@ 2013-01-18  6:28 ` Dante
  2013-01-19  8:15 ` [Qemu-devel] [PATCH 01/18] hw: add Faraday a369 SoC platform support Blue Swirl
  4 siblings, 0 replies; 8+ messages in thread
From: Dante @ 2013-01-18  6:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: peter.maydell, paul, dantesu

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/ftmac110.c |  567 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/ftmac110.h |  130 +++++++++++++
 2 files changed, 697 insertions(+)
 create mode 100644 hw/ftmac110.c
 create mode 100644 hw/ftmac110.h

diff --git a/hw/ftmac110.c b/hw/ftmac110.c
new file mode 100644
index 0000000..ab264dd
--- /dev/null
+++ b/hw/ftmac110.c
@@ -0,0 +1,567 @@
+/*
+ * QEMU model of the FTMAC110 Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Copyright (C) 2012 Dante Su <dantesu@faraday-tech.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "sysemu/sysemu.h"
+#include "net/net.h"
+
+#include "ftmac110.h"
+
+/* For crc32 */
+#include <zlib.h>
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion mmio;
+
+    QEMUTimer *qtimer;
+    qemu_irq irq;
+    NICState *nic;
+    NICConf conf;
+
+    uint32_t isr;
+    uint32_t ier;
+    uint32_t mhash[2];
+    uint32_t tx_bar;
+    uint32_t rx_bar;
+    uint32_t tx_idx;
+    uint32_t rx_idx;
+    uint32_t maccr;
+    uint32_t macsr;
+    uint32_t phycr;
+    uint32_t phycr_rd;
+    
+    struct {
+        uint8_t  buf[2048];
+        uint32_t len;
+    } txbuff;
+    
+    uint32_t rx_pkt;
+    uint32_t rx_bcst;
+    uint32_t rx_mcst;
+    uint16_t rx_runt;
+    uint16_t rx_drop;
+    uint16_t rx_crc;
+    uint16_t rx_ftl;
+    uint32_t tx_pkt;
+
+} ftmac110_state;
+
+static inline uint8_t bitrev8(uint8_t v)
+{
+    uint8_t r = 0;
+    int i;    
+    for(i = 0; i < 8; ++i) {
+        if (v & (1 << i))
+            r |= (1 << (7 - i));
+    }
+    return r;
+}
+
+static int ftmac110_mcast_hash (int len, const uint8_t *p)
+{
+#define CRCPOLY_LE 0xedb88320
+    int i;
+    uint32_t crc = 0xFFFFFFFF;
+
+    while (len--) {
+        crc ^= *p++;
+        for (i = 0; i < 8; i++)
+            crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
+    }
+
+    /* Reverse CRC32 and return MSB 6 bits only */
+    return bitrev8(crc >> 24) >> 2;
+}
+
+static inline void ftmac110_update_irq(ftmac110_state *s)
+{
+    if (s->isr & s->ier)
+        qemu_set_irq(s->irq, 1);
+    else
+        qemu_set_irq(s->irq, 0);
+}
+
+static int ftmac110_can_receive(NetClientState *nc)
+{
+    ftmac110_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+    ftmac110_rxdesc_t rxd;
+    hwaddr off = s->rx_bar + s->rx_idx * sizeof(rxd);
+
+    if ((s->maccr & (MACCR_RCV_EN | MACCR_RDMA_EN)) != (MACCR_RCV_EN | MACCR_RDMA_EN))
+        return 0;
+
+    cpu_physical_memory_read(off, (uint8_t *)&rxd, sizeof(rxd));
+
+    return rxd.owner;
+}
+
+static ssize_t ftmac110_receive(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+    const uint8_t *ptr = buf;
+    hwaddr off;
+    size_t len;
+    ftmac110_rxdesc_t rxd;
+    ftmac110_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+    int bcst, mcst;
+    
+    if ((s->maccr & (MACCR_RCV_EN | MACCR_RDMA_EN)) != (MACCR_RCV_EN | MACCR_RDMA_EN))
+        return -1;
+
+    /* if it's a broadcast */
+    if ((buf[0] == 0xff) && (buf[1] == 0xff) && (buf[2] == 0xff)
+            && (buf[3] == 0xff) && (buf[4] == 0xff) && (buf[5] == 0xff)) {
+        bcst = 1;
+        if (!(s->maccr & MACCR_RCV_ALL) && !(s->maccr & MACCR_RX_BROADPKT)) {
+            printf("[qemu] ftmac110_receive: bcst filtered\n");
+            return -1;
+        }
+    } else {
+        bcst = 0;
+    }
+
+    /* if it's a multicast */
+    if ((buf[0] == 0x01) && (buf[1] == 0x00) && (buf[2] == 0x5e) 
+        && (buf[3] <= 0x7f)) {
+        mcst = 1;
+        if (!(s->maccr & MACCR_RCV_ALL) && !(s->maccr & MACCR_RX_MULTIPKT)) {
+            int hash;
+            if (!(s->maccr & MACCR_HT_MULTI_EN)) {
+                printf("[qemu] ftmac110_receive: mcst filtered\n");
+                return -1;
+            }
+            hash = ftmac110_mcast_hash(6, buf);
+            if (!(s->mhash[hash / 32] & (1 << (hash % 32)))) {
+                printf("[qemu] ftmac110_receive: mcst filtered\n");
+                return -1;
+            }
+        }
+    } else {
+        mcst = 0;
+    }
+
+    /* check if the destination matches NIC mac address */
+    if (!(s->maccr & MACCR_RCV_ALL) && !bcst && !mcst) {
+        if (memcmp(s->conf.macaddr.a, buf, 6)) {
+#if 0
+            printf("[qemu] ftmac110_receive: pkt filtered\n");
+            printf("[qemu] packet dump: (IF MAC=%02X:%02X:%02X:%02X:%02X:%02X)\n",
+                s->conf.macaddr.a[0], s->conf.macaddr.a[1],
+                s->conf.macaddr.a[2], s->conf.macaddr.a[3],
+                s->conf.macaddr.a[4], s->conf.macaddr.a[5]);
+            for (off = 0; off < 14; ++off) {
+                printf("%02X ", buf[off]);
+            }
+            printf("\n");
+#endif
+            return -1;
+        }
+    }
+
+    while (size > 0) {
+        off = s->rx_bar + s->rx_idx * sizeof(rxd);
+        cpu_physical_memory_read(off, (uint8_t *)&rxd, sizeof(rxd));
+        if (!rxd.owner) {
+            s->isr |= ISR_NORXBUF;
+            printf("[qemu] ftmac110: out of rxd!? (ptr=%p, buf=%p)\n", ptr, buf);
+            return -1;
+        }
+
+        if (ptr == buf)
+            rxd.frs = 1;
+        else
+            rxd.frs = 0;
+
+        len = size > rxd.bufsz ? rxd.bufsz : size;
+        cpu_physical_memory_write(rxd.buf, (uint8_t *)ptr, len);
+        ptr  += len;
+        size -= len;
+
+        if (size <= 0)
+            rxd.lrs = 1;
+        else
+            rxd.lrs = 0;
+
+        rxd.len = len;
+        rxd.bcast = bcst;
+        rxd.mcast = mcst;
+        rxd.owner = 0;
+        
+        /* write-back the rx descriptor */
+        cpu_physical_memory_write(off, (uint8_t *)&rxd, sizeof(rxd));
+
+        if (rxd.end)
+            s->rx_idx = 0;
+        else
+            s->rx_idx += 1;
+    }
+    
+    /* 3. update interrupt signal */
+    s->isr |= ISR_RPKT_OK | ISR_RPKT_FINISH;
+    ftmac110_update_irq(s);
+    
+    return (ssize_t)((uint32_t)ptr - (uint32_t)buf);
+}
+
+static void ftmac110_transmit(ftmac110_state *s, uint32_t *bar, uint32_t *idx)
+{
+    hwaddr off;
+    uint8_t *buf;
+    ftmac110_txdesc_t txd;
+    
+    if ((s->maccr & (MACCR_XMT_EN | MACCR_XDMA_EN)) != (MACCR_XMT_EN | MACCR_XDMA_EN))
+        return;
+    
+    do {
+        off = (*bar) + (*idx) * sizeof(txd);
+        cpu_physical_memory_read(off, (uint8_t *)&txd, sizeof(txd));
+        if (!txd.owner) {
+            s->isr |= ISR_NOTXBUF;
+            break;
+        }
+        if (txd.fts)
+            s->txbuff.len = 0;
+        buf = s->txbuff.buf + s->txbuff.len;
+        cpu_physical_memory_read(txd.buf, (uint8_t *)buf, txd.len);
+        s->txbuff.len += txd.len;
+        if (txd.lts) {
+            if (s->maccr & MACCR_LOOP_EN) {
+                ftmac110_receive(&s->nic->nc, s->txbuff.buf, s->txbuff.len);
+            } else {
+                qemu_send_packet(&s->nic->nc, s->txbuff.buf, s->txbuff.len);
+            }
+        }
+        if (txd.end)
+            *idx = 0;
+        else
+            *idx += 1;
+        if (txd.tx2fic)
+            s->isr |= ISR_XPKT_OK;
+        if (txd.txic)
+            s->isr |= ISR_XPKT_FINISH;
+
+        txd.owner = 0;
+        cpu_physical_memory_write(off, (uint8_t *)&txd, sizeof(txd));
+    } while(1);
+}
+
+static void ftmac110_timer_tick(void *opaque)
+{
+    ftmac110_state *s = opaque;
+
+    if (s->tx_bar) {
+        ftmac110_transmit(s, &s->tx_bar, &s->tx_idx);
+    }
+
+    ftmac110_update_irq(s);
+}
+
+static uint64_t ftmac110_mem_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    ftmac110_state *s = opaque;
+    uint32_t rc = 0;
+
+    switch (addr) {
+    case REG_ISR:
+        rc = s->isr;
+        s->isr = 0;
+        ftmac110_update_irq(s);
+        break;
+    case REG_IMR:
+        return s->ier;
+    case REG_HMAC:
+        return s->conf.macaddr.a[1] 
+               | (s->conf.macaddr.a[0] << 8);
+    case REG_LMAC:
+        return s->conf.macaddr.a[5] 
+               | (s->conf.macaddr.a[4] << 8) 
+               | (s->conf.macaddr.a[3] << 16) 
+               | (s->conf.macaddr.a[2] << 24);
+    case REG_MHASH0:
+        return s->mhash[0];
+    case REG_MHASH1:
+        return s->mhash[1];
+    case REG_TXBAR:
+        return s->tx_bar;
+    case REG_RXBAR:
+        return s->rx_bar;
+    case REG_MACCR:
+        return s->maccr;
+    case REG_MACSR:
+        rc = s->macsr;
+        s->macsr = 0;
+        break;
+    case REG_PHYCTRL:
+        do {
+            uint8_t dev = (s->phycr >> 16) & 0x1f;
+            uint8_t reg = (s->phycr >> 21) & 0x1f;
+            if (dev != 0)
+                break;
+            if (s->phycr_rd) {
+                switch(reg) {
+                case 0:    /* PHY control register */
+                    return s->phycr | 0x1140;
+                case 1:    /* PHY status register */
+                    return s->phycr | 0x796d;
+                case 2:    /* PHY ID 1 register */
+                    return s->phycr | 0x0141;
+                case 3:    /* PHY ID 2 register */
+                    return s->phycr | 0x0cc2;
+                case 4:    /* Autonegotiation advertisement register */
+                    return s->phycr | 0x0de1;
+                case 5:    /* Autonegotiation partner abilities register */
+                    return s->phycr | 0x45e1;
+                }
+            }
+        } while(0);
+        break;
+    case REG_FCR:
+        return 0x0000a400;
+    case REG_BPR:
+        return 0x00000400;
+    case REG_TXPKT:
+        return s->tx_pkt;
+    case REG_RXPKT:
+        return s->rx_pkt;
+    case REG_RXBCST:
+        return s->rx_bcst;
+    case REG_RXMCST:
+        return s->rx_mcst;
+    case REG_RXRUNT:
+        return (s->rx_runt << 16);
+    case REG_RXCRCFTL:
+        return (s->rx_crc << 16) | (s->rx_ftl);
+    case REG_REV:
+        return 0x00000700;
+    case REG_FEA:
+        return 0x00000007;
+    default:
+        break;
+    }
+
+    return rc;
+}
+
+static void ftmac110_chip_reset(ftmac110_state *s)
+{
+    s->isr = 0;
+    s->ier = 0;
+    s->mhash[0] = 0;
+    s->mhash[1] = 0;
+    s->tx_bar = 0;
+    s->rx_bar = 0;
+    s->tx_idx = 0;
+    s->rx_idx = 0;
+    s->maccr = 0;
+    s->macsr = 0;
+    s->phycr = 0;
+    s->txbuff.len = 0;
+    s->rx_pkt = 0;
+    s->rx_bcst = 0;
+    s->rx_mcst = 0;
+    s->rx_runt = 0;
+    s->rx_drop = 0;
+    s->rx_crc = 0;
+    s->rx_ftl = 0;
+    s->tx_pkt = 0;
+    
+    if (s->qtimer)
+        qemu_del_timer(s->qtimer);
+
+    ftmac110_update_irq(s);
+}
+
+static void ftmac110_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
+{
+    ftmac110_state *s = opaque;
+    
+    switch (addr) {
+    case REG_IMR:
+        s->ier = (uint32_t)val;
+        ftmac110_update_irq(s);
+        break;
+    case REG_HMAC:
+        s->conf.macaddr.a[1] = (val >> 0) & 0xff;
+        s->conf.macaddr.a[0] = (val >> 8) & 0xff;
+        break;
+    case REG_LMAC:
+        s->conf.macaddr.a[5] = (val >> 0) & 0xff;
+        s->conf.macaddr.a[4] = (val >> 8) & 0xff;
+        s->conf.macaddr.a[3] = (val >> 16) & 0xff;
+        s->conf.macaddr.a[2] = (val >> 24) & 0xff;
+        break;
+    case REG_MHASH0:
+        s->mhash[0] = (uint32_t)val;
+        break;
+    case REG_MHASH1:
+        s->mhash[1] = (uint32_t)val;
+        break;
+    case REG_TXBAR:
+        s->tx_bar = (uint32_t)val;
+        break;
+    case REG_RXBAR:
+        s->rx_bar = (uint32_t)val;
+        break;
+    case REG_MACCR:
+        s->maccr = (uint32_t)val;
+        if (s->maccr & MACCR_SW_RST) {
+            ftmac110_chip_reset(s);
+            s->maccr &= ~MACCR_SW_RST;
+        }
+        break;
+    case REG_PHYCTRL:
+        s->phycr = (uint32_t)val;
+        if (s->phycr & PHYCR_MDIORD)
+            s->phycr_rd = 1;
+        else
+            s->phycr_rd = 0;
+        s->phycr &= ~(PHYCR_MDIOWR | PHYCR_MDIORD);
+        break;
+    case REG_TXPD:
+        qemu_mod_timer(s->qtimer, qemu_get_clock_ns(vm_clock) + 1);
+        break;
+    default:
+        break;
+    }
+}
+
+static const MemoryRegionOps bus_ops = {
+    .read  = ftmac110_mem_read,
+    .write = ftmac110_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void ftmac110_cleanup(NetClientState *nc)
+{
+    ftmac110_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+    s->nic = NULL;
+}
+
+static NetClientInfo net_ftmac110_info = {
+    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .size = sizeof(NICState),
+    .can_receive = ftmac110_can_receive,
+    .receive = ftmac110_receive,
+    .cleanup = ftmac110_cleanup,
+};
+
+static void ftmac110_reset(DeviceState *dev)
+{
+    ftmac110_state *s = FROM_SYSBUS(ftmac110_state, sysbus_from_qdev(dev));
+    
+    ftmac110_chip_reset(s);
+}
+
+static int ftmac110_init1(SysBusDevice *dev)
+{
+    ftmac110_state *s = FROM_SYSBUS(typeof(*s), dev);
+
+    memory_region_init_io(&s->mmio, &bus_ops, s, "ftmac110", 0x1000);
+    sysbus_init_mmio(dev, &s->mmio);
+    sysbus_init_irq(dev, &s->irq);
+
+    qemu_macaddr_default_if_unset(&s->conf.macaddr);
+    s->nic = qemu_new_nic(&net_ftmac110_info, &s->conf,
+                          object_get_typename(OBJECT(dev)), dev->qdev.id, s);
+    qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+    
+    s->qtimer = qemu_new_timer_ns(vm_clock, ftmac110_timer_tick, s);
+    
+    ftmac110_chip_reset(s);
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_ftmac110 = {
+    .name = "ftmac110",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(ier, ftmac110_state),
+        VMSTATE_UINT32(tx_bar, ftmac110_state),
+        VMSTATE_UINT32(rx_bar, ftmac110_state),
+        VMSTATE_UINT32(tx_idx, ftmac110_state),
+        VMSTATE_UINT32(rx_idx, ftmac110_state),
+        VMSTATE_UINT32(maccr, ftmac110_state),
+        VMSTATE_UINT32(macsr, ftmac110_state),
+        VMSTATE_UINT32(phycr, ftmac110_state),
+        VMSTATE_UINT32_ARRAY(mhash, ftmac110_state, 2),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property ftmac110_properties[] = {
+    DEFINE_NIC_PROPERTIES(ftmac110_state, conf),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void ftmac110_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = ftmac110_init1;
+    dc->reset = ftmac110_reset;
+    dc->vmsd = &vmstate_ftmac110;
+    dc->props = ftmac110_properties;
+}
+
+static TypeInfo ftmac110_info = {
+    .name           = "ftmac110",
+    .parent         = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(ftmac110_state),
+    .class_init     = ftmac110_class_init,
+};
+
+static void ftmac110_register_types(void)
+{
+    type_register_static(&ftmac110_info);
+}
+
+/* Legacy helper function.  Should go away when machine config files are
+   implemented.  */
+void ftmac110_init(NICInfo *nd, uint32_t base, qemu_irq irq);
+
+void ftmac110_init(NICInfo *nd, uint32_t base, qemu_irq irq)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+
+    qemu_check_nic_model(nd, "ftmac110");
+    dev = qdev_create(NULL, "ftmac110");
+    qdev_set_nic_properties(dev, nd);
+    qdev_init_nofail(dev);
+    s = sysbus_from_qdev(dev);
+    sysbus_mmio_map(s, 0, base);
+    sysbus_connect_irq(s, 0, irq);
+}
+
+type_init(ftmac110_register_types)
diff --git a/hw/ftmac110.h b/hw/ftmac110.h
new file mode 100644
index 0000000..4187150
--- /dev/null
+++ b/hw/ftmac110.h
@@ -0,0 +1,130 @@
+#ifndef FTMAC110_H
+#define FTMAC110_H
+
+// --------------------------------------------------------------------
+//        FTMAC110 MAC Registers
+// --------------------------------------------------------------------
+
+#define REG_ISR             0x00            // interrups status register
+#define REG_IMR             0x04            // interrupt maks register
+#define REG_HMAC            0x08            // MAC address (Most significant)
+#define REG_LMAC            0x0c            // MAC address (Least significant)
+#define REG_MHASH0          0x10            // Multicast Address Hash Table 0 register
+#define REG_MHASH1          0x14            // Multicast Address Hash Table 1 register
+#define REG_TXPD            0x18            // Transmit Poll Demand register
+#define REG_RXPD            0x1c            // Receive Poll Demand register
+#define REG_TXBAR           0x20            // Transmit Ring Base Address register
+#define REG_RXBAR           0x24            // Receive Ring Base Address register
+#define REG_ITC             0x28            // interrupt timer control register
+#define REG_APTC            0x2C            // Automatic Polling Timer control register
+#define REG_DBLAC           0x30            // DMA Burst Length and Arbitration control register
+#define REG_REV             0x34            // Revision Register
+#define REG_FEA             0x38            // Feature Register
+
+#define REG_MACCR           0x88            // MAC control register
+#define REG_MACSR           0x8C            // MAC status register
+#define REG_PHYCTRL         0x90            // PHY control register
+#define REG_PHYDATA         0x94            // PHY Write Data register
+#define REG_FCR             0x98            // Flow Control register
+#define REG_BPR             0x9c            // back pressure register
+
+#define REG_TXPKT           0xf8
+#define REG_RXPKT           0xf4
+#define REG_RXBCST          0xec
+#define REG_RXMCST          0xf0
+#define REG_RXRUNT          0xe0
+#define REG_RXCRCFTL        0xe4
+
+//Interrupt status register(ISR), Interrupt mask register(IMR) bit setting
+#define ISR_PHYSTS_CHG        (1UL<<9)
+#define ISR_AHB_ERR            (1UL<<8)
+#define ISR_RPKT_LOST        (1UL<<7)
+#define ISR_RPKT_OK            (1UL<<6)
+#define ISR_XPKT_LOST        (1UL<<5)
+#define ISR_XPKT_OK            (1UL<<4)
+#define ISR_NOTXBUF            (1UL<<3)
+#define ISR_XPKT_FINISH        (1UL<<2)
+#define ISR_NORXBUF            (1UL<<1)
+#define ISR_RPKT_FINISH        (1UL<<0)
+
+/* MACC control bits */
+#define MACCR_100M                (1UL<<18)
+#define MACCR_RX_BROADPKT        (1UL<<17)           // Receiving broadcast packet
+#define MACCR_RX_MULTIPKT        (1UL<<16)           // receiving multicast packet
+#define MACCR_FULLDUP            (1UL<<15)            // full duplex
+#define MACCR_CRC_APD            (1UL<<14)           // append crc to transmit packet
+#define MACCR_RCV_ALL            (1UL<<12)           // not check incoming packet's destination address
+#define MACCR_RX_FTL            (1UL<<11)           // Store incoming packet even its length is les than 64 byte
+#define MACCR_RX_RUNT            (1UL<<10)           // Store incoming packet even its length is les than 64 byte
+#define MACCR_HT_MULTI_EN        (1UL<<9)
+#define MACCR_RCV_EN            (1UL<<8)           // receiver enable
+#define MACCR_ENRX_IN_HALFTX    (1UL<<6)            // rx in half tx
+#define MACCR_XMT_EN            (1UL<<5)           // transmitter enable
+#define MACCR_CRC_DIS            (1UL<<4)
+#define MACCR_LOOP_EN            (1UL<<3)           // Internal loop-back
+#define MACCR_SW_RST            (1UL<<2)
+#define MACCR_RDMA_EN            (1UL<<1)           // enable DMA receiving channel
+#define MACCR_XDMA_EN            (1UL<<0)           // enable DMA transmitting channel
+
+// --------------------------------------------------------------------
+//        MII PHY Registers
+// --------------------------------------------------------------------
+
+//
+// Bits related to the MII interface
+//
+#define PHYCR_MDIOWR            (1 << 27)
+#define PHYCR_MDIORD            (1 << 26)
+
+// --------------------------------------------------------------------
+//        Receive Ring descriptor structure
+// --------------------------------------------------------------------
+typedef struct {
+    // RXDES0
+    uint32_t len: 11;    // BIT:  0 - 10
+    uint32_t rsvd1: 5;    // BIT: 11 - 15
+    uint32_t mcast: 1;    // BIT: 16
+    uint32_t bcast: 1;    // BIT: 17
+    uint32_t error: 5;
+    uint32_t rsvd2: 5;    // BIT: 23 - 27
+    uint32_t lrs: 1;        // BIT: 28
+    uint32_t frs: 1;        // BIT: 29
+    uint32_t rsvd3: 1;    // BIT: 30
+    uint32_t owner: 1;    // BIT: 31 - 1:Hardware, 0: Software
+
+    // RXDES1
+    uint32_t bufsz: 11;    // BIT:  0 - 10
+    uint32_t rsvd4: 20;    // BIT: 11 - 30
+    uint32_t end: 1;        // BIT: 31
+        
+    // RXDES2
+    uint32_t buf;
+
+    // RXDES3
+    void     *skb;
+} ftmac110_rxdesc_t;
+
+typedef struct {
+    // TXDES0
+    uint32_t error : 2;
+    uint32_t rsvd1 : 29;
+    uint32_t owner : 1;    // BIT: 31 - 1:Hardware, 0: Software
+    
+    // TXDES1
+    uint32_t len : 11;    // BIT:  0 - 10
+    uint32_t rsvd2 : 16;    
+    uint32_t lts: 1;        // BIT: 27
+    uint32_t fts: 1;        // BIT: 28
+    uint32_t tx2fic: 1;    // BIT: 29
+    uint32_t txic: 1;    // BIT: 30
+    uint32_t end: 1;        // BIT: 31
+
+    // TXDES2
+    uint32_t buf;
+
+    // TXDES3
+    void     *skb;
+    
+} ftmac110_txdesc_t;
+ 
+#endif  /* FTMAC_H */
-- 
1.7.9.5


********************* Confidentiality Notice ************************
This electronic message and any attachments may contain
confidential and legally privileged information or
information which is otherwise protected from disclosure.
If you are not the intended recipient,please do not disclose
the contents, either in whole or in part, to anyone,and
immediately delete the message and any attachments from
your computer system and destroy all hard copies.
Thank you for your cooperation.
***********************************************************************

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [Qemu-devel] [PATCH 01/18] hw: add Faraday a369 SoC platform support
  2013-01-18  6:28 [Qemu-devel] [PATCH 01/18] hw: add Faraday a369 SoC platform support Dante
                   ` (3 preceding siblings ...)
  2013-01-18  6:28 ` [Qemu-devel] [PATCH 05/18] hw: add QEMU model for Faraday 10/100Mbps " Dante
@ 2013-01-19  8:15 ` Blue Swirl
  4 siblings, 0 replies; 8+ messages in thread
From: Blue Swirl @ 2013-01-19  8:15 UTC (permalink / raw)
  To: Dante; +Cc: peter.maydell, qemu-devel, paul

On Fri, Jan 18, 2013 at 6:28 AM, Dante <dantesu@faraday-tech.com> wrote:
> Faraday A369, is an ARM SoC prototype chip, highly integrated with common SoC
> features, including a 533MHz ARM v5TE master CPU, a 133MHz ARM v5TE slave CPU,
> a 32-bit DDR-2 533MHz memory controller, 2 USB 2.0 host/OTG ports, SATA
> host/device port, 10/100/1000 Ethernet port, MPEG4 codec, H.264 decoder,
> image signal processor, and various low-speed peripheral interfaces.
>
> This patch adds QEMU model for A369 without MPEG4/H.264 and USB support.
> The USB host is still under migration from qemu-1.3.0 into the current
> development release from GIT.
>
> These patches include:

The cover letter in git send-email will generate this for you, please
use that instead.

>
> 1. hw/a369.c:        QEMU model for Faraday a369 SoC platform
> 2. hw/a369_scu.c:    A369 System Control Uint (Clock & Power Management)
> 3. hw/ftapbbrg020.c: APB DMA Engine
> 4. hw/ftapbbrg020.h
> 5. hw/ftdmac020.c:   AHB DMA Engine
> 6. hw/ftdmac020.h
> 7. hw/ftgmac100.c:   1Gbps Ethernet MAC
> 8. hw/ftgmac100.h
> 9. hw/fti2c010.c:    I2C Master
> 10.hw/fti2c010.h
> 11.hw/ftintc020.c:   Interrupt Controller
> 12.hw/ftkbc010.c:    A369 Keypad Controller
> 13.hw/ftlcdc200.c:   LCD Controller
> 14.hw/ftlcdc200.h
> 15.hw/ftlcdc200_template.h
> 16.hw/ftmac110.c:    100Mbps Ethernet MAC
> 17.hw/ftmac110.h
> 18.hw/ftnandc021.c:  NAND Flash Controller
> 19.hw/ftnandc021.h
> 20.hw/ftpwmtmr010.c: Timer
> 21.hw/ftrtc011.c:    RTC Timer
> 22.hw/ftsdc010.c:    MMC/SD Host Controller
> 23.hw/ftsdc010.h
> 24.hw/ftspi020.c:    SPI NOR Flash Controller
> 25.hw/ftspi020.h
> 26.hw/ftssp010.c:    Multi-function device with SPI and I2S support
> 27.hw/ftssp010.h
> 28.hw/fttmr010.c:    Timer
> 29/hw/fttsc010.c:    Touchscreen Controller
> 30.hw/fttsc010.h
> 31.hw/ftwdt010.c:    Watchdog Timer
> 32.hw/rom.c:         Embedded ROM device model for SoC Platform
>
> Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
> ---
>  hw/a369.c            |  499 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/a369_scu.c        |  192 +++++++++++++++++++
>  hw/arm/Makefile.objs |    6 +
>  3 files changed, 697 insertions(+)
>  create mode 100644 hw/a369.c
>  create mode 100644 hw/a369_scu.c
>
> diff --git a/hw/a369.c b/hw/a369.c
> new file mode 100644
> index 0000000..cc78351
> --- /dev/null
> +++ b/hw/a369.c
> @@ -0,0 +1,499 @@
> +/*
> + * Faraday A369 Evalution Board
> + *
> + * Copyright (c) 2012 Faraday Technology
> + *
> + * Written by Dante Su <dantesu@faraday-tech.com>
> + *
> + * This code is licensed under the GPL.

Which version? We prefer "GPL v2 or later, at your option".

> + */
> +
> +#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"
> +
> +#define A369_NOR_FLASH_ADDR            0x20000000
> +#define A369_NOR_FLASH_SIZE            (16 * 1024 * 1024)
> +#define A369_NOR_FLASH_SECT_SIZE    (128 * 1024)
> +
> +#ifndef min
> +#define min(a, b)                    ((a) < (b) ? (a) : (b))

Macro names should be uppercase.

> +#endif
> +
> +#ifndef max
> +#define max(a, b)                    ((a) > (b) ? (a) : (b))
> +#endif
> +
> +struct a369_ahbc_state {

Struct names should use CamelCase and don't forget the typedef as
instructed in HACKING.

> +    SysBusDevice busdev;
> +    MemoryRegion iomem;
> +    /* HW register cache */
> +    uint32_t cr;
> +};
> +
> +struct a369_ddrc_state {
> +    SysBusDevice busdev;
> +    MemoryRegion iomem;
> +
> +    /* HW register caches */
> +    uint32_t mcr;
> +    uint32_t msr;
> +};
> +
> +struct a369_state {
> +    ARMCPU *cpu;
> +    DeviceState *rom;
> +    DeviceState *hdma;    /* AHB DMA */
> +    DeviceState *pdma;    /* APB DMA */
> +
> +    MemoryRegion *as;
> +    MemoryRegion *ram;
> +    MemoryRegion *ram_alias;
> +    MemoryRegion *sram;
> +
> +    i2c_bus *i2c[2];
> +
> +    struct a369_ahbc_state ahbc;
> +    struct a369_ddrc_state ddrc;
> +};
> +
> +/* ftintc020.c */
> +qemu_irq *ftintc020_init(hwaddr base, ARMCPU *cpu);

Function prototypes belong to a header file.

> +
> +/* ftgmac100.c */
> +void ftgmac100_init(NICInfo *nd, uint32_t base, qemu_irq irq);
> +
> +/* AHB controller block */
> +
> +static uint64_t a369_ahbc_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    struct a369_state *mach = opaque;
> +    struct a369_ahbc_state *s = &mach->ahbc;
> +
> +    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->cr;
> +    case 0x8c: return 0x00010301;
> +    default:
> +        break;
> +    }
> +    return 0;
> +}
> +
> +static void a369_ahbc_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
> +{
> +    struct a369_state *mach = opaque;
> +    struct a369_ahbc_state *s = &mach->ahbc;
> +
> +    if (addr == 0x88) {

Please use switch().

> +        if (!(s->cr & 0x01) && (val & 0x01)) {
> +            /* AHB remap */
> +            printf("[qemu] a369: AHB remap...\n");

Remove printfs or convert them to tracepoints or debug printfs.

> +            sysbus_mmio_map(sysbus_from_qdev(mach->rom), 0, 0x40000000);

Doesn't the previous mapping still stay?

> +            memory_region_del_subregion(mach->as, mach->ram_alias);
> +            memory_region_add_subregion(mach->as, 0x00000000, mach->ram);
> +        }
> +        s->cr = (uint32_t)val;
> +    }
> +}
> +
> +static const MemoryRegionOps a369_ahbc_ops = {
> +    .read  = a369_ahbc_read,
> +    .write = a369_ahbc_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +static int a369_ahbc_init(struct a369_state *mach, hwaddr base)
> +{
> +    struct a369_ahbc_state *s = &mach->ahbc;
> +    memory_region_init_io(&s->iomem, &a369_ahbc_ops, mach, "a369_ahbc", 0x1000);
> +    memory_region_add_subregion(mach->as, base, &s->iomem);
> +    s->cr = 0;
> +    return 0;
> +}
> +
> +/* DDRII controller block */
> +
> +static uint64_t a369_ddrc_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    struct a369_state *mach = opaque;
> +    struct a369_ddrc_state *s = &mach->ddrc;
> +
> +    switch(addr) {
> +    case 0x00:
> +        return s->mcr;
> +    case 0x04:
> +        return s->msr;
> +    default:
> +        break;
> +    }
> +    return 0;
> +}
> +
> +static void a369_ddrc_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
> +{
> +    struct a369_state *mach = opaque;
> +    struct a369_ddrc_state *s = &mach->ddrc;
> +
> +    switch(addr) {
> +    case 0x00:
> +        s->mcr = (uint32_t)val & 0xffff;
> +        break;
> +    case 0x04:
> +        val = (val & 0x3f) | (s->msr & 0x100);
> +        if (!(s->msr & 0x100) && (val & 0x01)) {
> +            val &= 0xfffffffe;
> +            val |= 0x100;
> +            printf("[qemu] a369: ddr init...\n");
> +            memory_region_add_subregion(mach->as, 0x10000000, mach->ram_alias);
> +        }
> +        s->msr = (uint32_t)val;
> +        break;
> +    default:
> +        break;
> +    }
> +}
> +
> +static const MemoryRegionOps a369_ddrc_ops = {
> +    .read  = a369_ddrc_read,
> +    .write = a369_ddrc_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +static int a369_ddrc_init(struct a369_state *mach, hwaddr base)
> +{
> +    struct a369_ddrc_state *s = &mach->ddrc;
> +
> +    memory_region_init_io(&s->iomem, &a369_ddrc_ops, mach, "a369_ddrc", 0x1000);
> +    memory_region_add_subregion(mach->as, base, &s->iomem);
> +    s->mcr = 0;
> +    s->msr = 0;
> +    return 0;
> +}
> +
> +/* Board init.  */
> +
> +static void a369_device_init(struct a369_state *s)
> +{
> +    qemu_irq *pic;
> +    DeviceState *ds;
> +    int done_nic = 0;
> +    int i;
> +
> +    /* 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(sysbus_from_qdev(ds), 0, 0x92300000);
> +    sysbus_connect_irq(sysbus_from_qdev(ds), 0, pic[8]);
> +    sysbus_connect_irq(sysbus_from_qdev(ds), 1, pic[9]);
> +    sysbus_connect_irq(sysbus_from_qdev(ds), 2, pic[10]);
> +    sysbus_connect_irq(sysbus_from_qdev(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_NATIVE_ENDIAN);

DEVICE_NATIVE_ENDIAN would indicate that the device is very close to
CPU. Probably DEVICE_LITTLE_ENDIAN would make more sense.

> +    }
> +    if (serial_hds[1]) {
> +        serial_mm_init(s->as,
> +                       0x92c00000,
> +                       2,
> +                       pic[54],
> +                       18432000 / 16,
> +                       serial_hds[1],
> +                       DEVICE_NATIVE_ENDIAN);
> +    }
> +
> +    /* ftscu010 */
> +    sysbus_create_simple("a369_scu", 0x92000000, pic[41]);
> +
> +    /* ftdmac020 */
> +    s->hdma = sysbus_create_simple("ftdmac020", 0x90300000, pic[15]);
> +    sysbus_create_simple("ftdmac020", 0x96100000, pic[17]);
> +
> +    /* ftapbbrg020 */
> +    s->pdma = sysbus_create_simple("ftapbbrg020", 0x90f00000, pic[14]);
> +
> +    /* ftnandc021 */
> +    do {

Just move the variable definitions to the start of the function, or
extract to a separate function.

> +        qemu_irq ack, req;
> +        ds = sysbus_create_simple("ftnandc021", 0x90200000, pic[30]);
> +        ack = qdev_get_gpio_in(ds, 0);
> +        req = qdev_get_gpio_in(s->hdma, 15);
> +        qdev_connect_gpio_out(s->hdma, 15, ack);
> +        qdev_connect_gpio_out(ds, 0, req);
> +    } while(0);
> +
> +    /* ftsdc010 */
> +#if 0

No disabled or commented out code, please. They will just bit rot.

> +    do {
> +        qemu_irq ack, req;
> +        ds = sysbus_create_simple("ftsdc010", 0x90500000, pic[38]);
> +        ack = qdev_get_gpio_in(ds, 0);
> +        req = qdev_get_gpio_in(s->hdma, 14);
> +        qdev_connect_gpio_out(s->hdma, 14, ack);
> +        qdev_connect_gpio_out(ds, 0, req);
> +    } while(0);
> +#endif
> +    do {
> +        qemu_irq ack, req;
> +        ds = sysbus_create_simple("ftsdc010", 0x90600000, pic[39]);
> +        ack = qdev_get_gpio_in(ds, 0);
> +        req = qdev_get_gpio_in(s->hdma, 13);
> +        qdev_connect_gpio_out(s->hdma, 13, ack);
> +        qdev_connect_gpio_out(ds, 0, req);
> +    } while(0);
> +
> +#if 0
> +    /* fusbh200 */
> +    sysbus_create_simple("faraday-ehci-usb", 0x90800000, pic[36]);
> +
> +    /* fotg210 */
> +    sysbus_create_simple("faraday-ehci-usb", 0x90900000, pic[37]);
> +#endif
> +
> +    /* ftgmac100 */
> +    for(i = 0; i < nb_nics; i++) {

Space between 'for' and '('.

> +        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],  /* Global (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 */
> +    do {
> +        SSIBus *spi;
> +        DeviceState *fl;
> +        int nr_flash = 1;
> +        qemu_irq cs_line;
> +        qemu_irq ack, req;
> +
> +        ds = qdev_create(NULL, "ftssp010");
> +
> +        /* i2s */
> +        qdev_prop_set_ptr(ds, "codec_i2c", s->i2c[0]);
> +        qdev_init_nofail(ds);
> +        sysbus_mmio_map(sysbus_from_qdev(ds), 0, 0x92700000);
> +        sysbus_connect_irq(sysbus_from_qdev(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(sysbus_from_qdev(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);
> +    } while(0);
> +
> +    /* ftddrII030 */
> +    a369_ddrc_init(s, 0x93100000);
> +
> +    /* ftahbc020 */
> +    a369_ahbc_init(s, 0x94000000);
> +
> +    /* Parallel NOR Flash */
> +    do {
> +        DriveInfo *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)) {
> +            fprintf(stderr, "qemu: Error registering flash memory.\n");

exit(), hw_error()?

> +        }
> +    } while(0);
> +}
> +
> +static void a369_board_reset(void *opaque)
> +{
> +    struct 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(sysbus_from_qdev(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 a369_state *s = g_new(struct 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)

Please use checkpatch.pl to catch brace problems.

> +        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) {
> +        struct arm_boot_info *bi = g_new(struct arm_boot_info, 1);
> +
> +        /* RAM Address Binding */
> +        memory_region_add_subregion(s->as, 0x00000000, s->ram);
> +
> +        /* Boot Info */
> +        memset(bi, 0, sizeof(*bi));
> +        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(sysbus_from_qdev(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/a369_scu.c b/hw/a369_scu.c
> new file mode 100644
> index 0000000..ee548d4
> --- /dev/null
> +++ b/hw/a369_scu.c
> @@ -0,0 +1,192 @@
> +/*
> + * QEMU model of the A369 SCU
> + *
> + * Copyright (C) 2012 Faraday Technology
> + * Copyright (C) 2012 Dante Su <dantesu@faraday-tech.com>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +
> +#include "sysbus.h"
> +#include "sysemu/sysemu.h"
> +
> +typedef struct {

The struct should also have a name.

> +    SysBusDevice busdev;
> +    MemoryRegion iomem;
> +    qemu_irq     irq;
> +
> +    /* HW register caches */
> +    uint32_t general_cfg;
> +    uint32_t sclk_cfg0;
> +    uint32_t sclk_cfg1;
> +    uint32_t mfpsr0;
> +    uint32_t mfpsr1;
> +
> +} a369_scu_state;
> +
> +static uint64_t
> +a369_scu_mem_read(void *opaque, hwaddr addr, unsigned int size)
> +{
> +    a369_scu_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->general_cfg;
> +    case 0x204:
> +        return 0x00001cc8;
> +    case 0x228:
> +        return s->sclk_cfg0;
> +    case 0x22c:
> +        return s->sclk_cfg1;
> +    case 0x230:
> +        return 0x00003fff;
> +    case 0x238:
> +        return s->mfpsr0;
> +    case 0x23c:
> +        return s->mfpsr1;
> +    case 0x240:
> +        return 0x11111111;
> +    case 0x244:
> +        return 0x11111111;
> +    case 0x254:
> +        return 0x00000303;
> +    case 0x258:
> +        return 0x8000007f;
> +    }
> +
> +    return 0;
> +}
> +
> +static void
> +a369_scu_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
> +{
> +    a369_scu_state *s = opaque;
> +
> +    switch(addr) {
> +    case 0x200:
> +        s->general_cfg = (uint32_t)val;
> +        break;
> +    case 0x228:
> +        s->sclk_cfg0 = (uint32_t)val;
> +        break;
> +    case 0x22c:
> +        s->sclk_cfg1 = (uint32_t)val;
> +        break;
> +    case 0x238:
> +        s->mfpsr0 = (uint32_t)val;
> +        break;
> +    case 0x23c:
> +        s->mfpsr1 = (uint32_t)val;
> +        break;
> +    default:
> +        break;
> +    }
> +}
> +
> +static const MemoryRegionOps bus_ops = {
> +    .read  = a369_scu_mem_read,
> +    .write = a369_scu_mem_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4
> +    }
> +};
> +
> +static void a369_scu_reset(DeviceState *d)
> +{
> +    a369_scu_state *s = DO_UPCAST(a369_scu_state, busdev.qdev, d);
> +
> +    s->general_cfg = 0x00001078;
> +    s->sclk_cfg0   = 0x26877330;
> +    s->sclk_cfg1   = 0x000a0a0a;
> +    s->mfpsr0      = 0x00000241;
> +    s->mfpsr1      = 0x00000000;
> +}
> +
> +static int a369_scu_init(SysBusDevice *dev)
> +{
> +    a369_scu_state *s = FROM_SYSBUS(typeof(*s), dev);
> +
> +    memory_region_init_io(&s->iomem, &bus_ops, s, "a369_scu", 0x1000);
> +    sysbus_init_mmio(dev, &s->iomem);
> +    sysbus_init_irq(dev, &s->irq);
> +
> +    return 0;
> +}
> +
> +static const VMStateDescription vmstate_a369_scu = {
> +    .name = "a369_scu",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .minimum_version_id_old = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32(general_cfg, a369_scu_state),
> +        VMSTATE_UINT32(sclk_cfg0, a369_scu_state),
> +        VMSTATE_UINT32(sclk_cfg1, a369_scu_state),
> +        VMSTATE_UINT32(mfpsr0, a369_scu_state),
> +        VMSTATE_UINT32(mfpsr1, a369_scu_state),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static void a369_scu_class_init(ObjectClass *klass, void *data)
> +{
> +    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
> +    DeviceClass *k = DEVICE_CLASS(klass);
> +
> +    sdc->init  = a369_scu_init;
> +    k->vmsd    = &vmstate_a369_scu;
> +    k->reset   = a369_scu_reset;
> +    k->no_user = 1;
> +}
> +
> +static TypeInfo a369_scu_info = {
> +    .name           = "a369_scu",
> +    .parent         = TYPE_SYS_BUS_DEVICE,
> +    .instance_size  = sizeof(a369_scu_state),
> +    .class_init     = a369_scu_class_init,
> +};
> +
> +static void a369_scu_register_types(void)
> +{
> +    type_register_static(&a369_scu_info);
> +}
> +
> +type_init(a369_scu_register_types)
> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> index 6d049e7..4cc85e5 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 += a369.o a369_scu.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
> --
> 1.7.9.5
>
>
> ********************* Confidentiality Notice ************************
> This electronic message and any attachments may contain
> confidential and legally privileged information or
> information which is otherwise protected from disclosure.
> If you are not the intended recipient,please do not disclose
> the contents, either in whole or in part, to anyone,and
> immediately delete the message and any attachments from
> your computer system and destroy all hard copies.
> Thank you for your cooperation.
> ***********************************************************************
>
>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Qemu-devel] [PATCH 04/18] hw: add QEMU model for Faraday 1Gbps Ethernet MAC
  2013-01-18  6:28 ` [Qemu-devel] [PATCH 04/18] hw: add QEMU model for Faraday 1Gbps Ethernet MAC Dante
@ 2013-01-19  8:41   ` Blue Swirl
  0 siblings, 0 replies; 8+ messages in thread
From: Blue Swirl @ 2013-01-19  8:41 UTC (permalink / raw)
  To: Dante; +Cc: peter.maydell, qemu-devel, paul

On Fri, Jan 18, 2013 at 6:28 AM, Dante <dantesu@faraday-tech.com> wrote:
> Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
> ---
>  hw/ftgmac100.c |  621 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/ftgmac100.h |  191 +++++++++++++++++
>  2 files changed, 812 insertions(+)
>  create mode 100644 hw/ftgmac100.c
>  create mode 100644 hw/ftgmac100.h
>
> diff --git a/hw/ftgmac100.c b/hw/ftgmac100.c
> new file mode 100644
> index 0000000..d66c719
> --- /dev/null
> +++ b/hw/ftgmac100.c
> @@ -0,0 +1,621 @@
> +/*
> + * QEMU model of the FTGMAC100 Controller
> + *
> + * Copyright (C) 2012 Faraday Technology
> + * Copyright (C) 2012 Dante Su <dantesu@faraday-tech.com>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +
> +#include "sysbus.h"
> +#include "sysemu/sysemu.h"
> +#include "net/net.h"
> +
> +#include "ftgmac100.h"
> +

Pointers to online chips docs would be nice.

> +typedef struct {
> +    SysBusDevice busdev;
> +    MemoryRegion mmio;
> +
> +    QEMUTimer *qtimer;
> +    qemu_irq irq;
> +    NICState *nic;
> +    NICConf conf;
> +
> +    uint32_t isr;
> +    uint32_t ier;
> +    uint32_t mhash[2];
> +    uint32_t tx_bar;
> +    uint32_t rx_bar;
> +    uint32_t hptx_bar;
> +    uint32_t tx_idx;
> +    uint32_t rx_idx;
> +    uint32_t hptx_idx;
> +    uint32_t maccr;
> +    uint32_t macsr;
> +    uint32_t phycr;
> +    uint32_t phycr_rd;
> +
> +    struct {
> +        uint8_t  buf[9220];
> +        uint32_t len;
> +    } txbuff;
> +
> +    uint32_t rx_pkt;
> +    uint32_t rx_bcst;
> +    uint32_t rx_mcst;
> +    uint16_t rx_runt;
> +    uint16_t rx_drop;
> +    uint16_t rx_crc;
> +    uint16_t rx_ftl;
> +    uint32_t tx_pkt;
> +
> +} ftgmac100_state;
> +
> +static inline uint8_t bitrev8(uint8_t v)

The compiler will probably inline this anyway.

> +{
> +    uint8_t r = 0;
> +    int i;
> +    for(i = 0; i < 8; ++i) {
> +        if (v & (1 << i))
> +            r |= (1 << (7 - i));
> +    }
> +    return r;
> +}
> +
> +static int ftgmac100_mcast_hash (ftgmac100_state *s, const uint8_t *data)

Extra space between function name and parenthesis.

> +{
> +#define CRCPOLY_BE    0x04c11db7
> +    int i, len;
> +    uint32_t crc = 0xFFFFFFFF;
> +
> +    /*
> +     * 2011.12.28
> +     * It's a FTGMAC100 hardware bug reported by MTD Jeffrey & Jason
> +     */
> +    if (s->maccr & MACCR_GMODE)
> +        len = 5;
> +    else
> +        len = 6;
> +
> +    while (len--) {
> +        uint32_t c = *(data++);
> +        for (i = 0; i < 8; ++i) {
> +            crc = (crc << 1)^ ((((crc>>31)^c)&0x01) ? CRCPOLY_BE : 0);
> +            c >>= 1;
> +        }
> +    }
> +    crc = ~crc;
> +
> +    /* Reverse CRC32 and return MSB 6 bits only */
> +    return bitrev8(crc >> 24) >> 2;
> +}
> +
> +static inline void ftgmac100_update_irq(ftgmac100_state *s)
> +{
> +    if (s->isr & s->ier)
> +        qemu_set_irq(s->irq, 1);
> +    else
> +        qemu_set_irq(s->irq, 0);
> +}
> +
> +static int ftgmac100_can_receive(NetClientState *nc)
> +{
> +    ftgmac100_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
> +    ftgmac100_rxdesc_t rxd;
> +    hwaddr off = s->rx_bar + s->rx_idx * sizeof(rxd);
> +
> +    if ((s->maccr & (MACCR_RCV_EN | MACCR_RDMA_EN)) != (MACCR_RCV_EN | MACCR_RDMA_EN))
> +        return 0;
> +
> +    cpu_physical_memory_read(off, (uint8_t *)&rxd, sizeof(rxd));
> +
> +    return !rxd.owner;
> +}
> +
> +static ssize_t ftgmac100_receive(NetClientState *nc, const uint8_t *buf, size_t size)
> +{
> +    const uint8_t *ptr = buf;
> +    hwaddr off;
> +    size_t len;
> +    ftgmac100_rxdesc_t rxd;
> +    ftgmac100_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
> +    int bcst, mcst;
> +
> +    if ((s->maccr & (MACCR_RCV_EN | MACCR_RDMA_EN)) != (MACCR_RCV_EN | MACCR_RDMA_EN))
> +        return -1;
> +
> +    /* if it's a broadcast */
> +    if ((buf[0] == 0xff) && (buf[1] == 0xff) && (buf[2] == 0xff)
> +            && (buf[3] == 0xff) && (buf[4] == 0xff) && (buf[5] == 0xff)) {
> +        bcst = 1;
> +        if (!(s->maccr & MACCR_RCV_ALL) && !(s->maccr & MACCR_RX_BROADPKT)) {
> +            printf("[qemu] ftgmac100_receive: bcst filtered\n");
> +            return -1;
> +        }
> +    } else {
> +        bcst = 0;
> +    }
> +
> +    /* if it's a multicast */
> +    if ((buf[0] == 0x01) && (buf[1] == 0x00) && (buf[2] == 0x5e)
> +        && (buf[3] <= 0x7f)) {
> +        mcst = 1;
> +        if (!(s->maccr & MACCR_RCV_ALL) && !(s->maccr & MACCR_RX_MULTIPKT)) {
> +            int hash;
> +            if (!(s->maccr & MACCR_HT_MULTI_EN)) {
> +                printf("[qemu] ftgmac100_receive: mcst filtered\n");
> +                return -1;
> +            }
> +            hash = ftgmac100_mcast_hash(s, buf);
> +            if (!(s->mhash[hash / 32] & (1 << (hash % 32)))) {
> +                printf("[qemu] ftgmac100_receive: mcst filtered\n");
> +                return -1;
> +            }
> +        }
> +    } else {
> +        mcst = 0;
> +    }
> +
> +    /* check if the destination matches NIC mac address */
> +    if (!(s->maccr & MACCR_RCV_ALL) && !bcst && !mcst) {
> +        if (memcmp(s->conf.macaddr.a, buf, 6)) {
> +#if 0
> +            printf("[qemu] ftgmac100_receive: pkt filtered\n");
> +            printf("[qemu] packet dump: (IF MAC=%02X:%02X:%02X:%02X:%02X:%02X)\n",
> +                s->conf.macaddr.a[0], s->conf.macaddr.a[1],
> +                s->conf.macaddr.a[2], s->conf.macaddr.a[3],
> +                s->conf.macaddr.a[4], s->conf.macaddr.a[5]);
> +            for (off = 0; off < 14; ++off) {
> +                printf("%02X ", buf[off]);
> +            }
> +            printf("\n");
> +#endif
> +            return -1;
> +        }
> +    }
> +
> +    while (size > 0) {
> +        off = s->rx_bar + s->rx_idx * sizeof(rxd);
> +        cpu_physical_memory_read(off, (uint8_t *)&rxd, sizeof(rxd));
> +        if (rxd.owner) {
> +            s->isr |= ISR_NORXBUF;
> +            printf("[qemu] ftgmac100: out of rxd!? (ptr=%p, buf=%p)\n", ptr, buf);
> +            return -1;
> +        }
> +
> +        if (ptr == buf)
> +            rxd.frs = 1;
> +        else
> +            rxd.frs = 0;
> +
> +        len = size > rxd.len ? rxd.len : size;
> +        cpu_physical_memory_write(rxd.buf, (uint8_t *)ptr, len);
> +        ptr  += len;
> +        size -= len;
> +
> +        if (size <= 0)
> +            rxd.lrs = 1;
> +        else
> +            rxd.lrs = 0;
> +
> +        rxd.len = len;
> +        rxd.bcast = bcst;
> +        rxd.mcast = mcst;
> +        rxd.owner = 1;
> +
> +        /* write-back the rx descriptor */
> +        cpu_physical_memory_write(off, (uint8_t *)&rxd, sizeof(rxd));
> +
> +        if (rxd.end)
> +            s->rx_idx = 0;
> +        else
> +            s->rx_idx += 1;
> +    }
> +
> +    /* 3. update interrupt signal */

There's no 1. or 2.

> +    s->isr |= ISR_RPKT_OK | ISR_RPKT_FINISH;
> +    ftgmac100_update_irq(s);
> +
> +    return (ssize_t)((uint32_t)ptr - (uint32_t)buf);
> +}
> +
> +static void ftgmac100_transmit(ftgmac100_state *s, uint32_t *bar, uint32_t *idx)
> +{
> +    hwaddr off;
> +    uint8_t *buf;
> +    ftgmac100_txdesc_t txd;
> +
> +    if ((s->maccr & (MACCR_XMT_EN | MACCR_XDMA_EN)) != (MACCR_XMT_EN | MACCR_XDMA_EN))
> +        return;
> +
> +    do {
> +        off = (*bar) + (*idx) * sizeof(txd);
> +        cpu_physical_memory_read(off, (uint8_t *)&txd, sizeof(txd));
> +        if (!txd.owner) {
> +            s->isr |= ISR_NOTXBUF;
> +            break;
> +        }
> +        if (txd.fts)
> +            s->txbuff.len = 0;
> +        buf = s->txbuff.buf + s->txbuff.len;
> +        cpu_physical_memory_read(txd.buf, (uint8_t *)buf, txd.len);

This assumes incorrectly that txd.len would be in the memory in the
same endianness as host. You need to use conversion functions like
le32_to_cpu().

> +        s->txbuff.len += txd.len;
> +        if (txd.lts) {
> +            if (s->maccr & MACCR_LOOP_EN) {
> +                ftgmac100_receive(&s->nic->nc, s->txbuff.buf, s->txbuff.len);
> +            } else {
> +                qemu_send_packet(&s->nic->nc, s->txbuff.buf, s->txbuff.len);
> +            }
> +        }
> +        if (txd.end)
> +            *idx = 0;
> +        else
> +            *idx += 1;
> +        if (txd.tx2fic)
> +            s->isr |= ISR_XPKT_OK;
> +        if (txd.txic)
> +            s->isr |= ISR_XPKT_FINISH;
> +
> +        txd.owner = 0;
> +        cpu_physical_memory_write(off, (uint8_t *)&txd, sizeof(txd));
> +    } while(1);
> +}
> +
> +static void ftgmac100_timer_tick(void *opaque)
> +{
> +    ftgmac100_state *s = opaque;
> +
> +    /* 1. process high priority tx ring */
> +    if (s->hptx_bar && (s->maccr & MACCR_HPTXR_EN)) {
> +        ftgmac100_transmit(s, &s->hptx_bar, &s->hptx_idx);
> +    }
> +
> +    /* 2. process normal priority tx ring */
> +    if (s->tx_bar) {
> +        ftgmac100_transmit(s, &s->tx_bar, &s->tx_idx);
> +    }
> +
> +    /* 3. update interrupt signal */
> +    ftgmac100_update_irq(s);
> +}
> +
> +static uint64_t ftgmac100_mem_read(void *opaque, hwaddr addr, unsigned int size)
> +{
> +    ftgmac100_state *s = opaque;
> +    uint32_t rc = 0;
> +
> +    switch (addr) {
> +    case REG_ISR:
> +        return s->isr;
> +    case REG_IMR:
> +        return s->ier;
> +    case REG_HMAC:
> +        return s->conf.macaddr.a[1]
> +               | (s->conf.macaddr.a[0] << 8);
> +    case REG_LMAC:
> +        return s->conf.macaddr.a[5]
> +               | (s->conf.macaddr.a[4] << 8)
> +               | (s->conf.macaddr.a[3] << 16)
> +               | (s->conf.macaddr.a[2] << 24);
> +    case REG_MHASH0:
> +        return s->mhash[0];
> +    case REG_MHASH1:
> +        return s->mhash[1];
> +    case REG_TXBAR:
> +        return s->tx_bar;
> +    case REG_RXBAR:
> +        return s->rx_bar;
> +    case REG_HPTXBAR:
> +        return s->hptx_bar;
> +    case REG_ITC:
> +        return 0x00000000;
> +    case REG_APTC:
> +        return 0x00000001;
> +    case REG_DBLAC:
> +        return 0x00022f72;
> +    case 0x3c:
> +        return 0x0c000000;
> +    case 0x40:
> +        return 0x00000600;
> +    case 0x44:
> +        return 0x0000001b;
> +    case 0x48:
> +        return 0x000000f1;
> +    case 0x4c:
> +        return 0x00000640;
> +    case REG_MACCR:
> +        return s->maccr;
> +    case REG_MACSR:
> +        return s->macsr;
> +    case REG_PHYCTRL:
> +        return s->phycr;
> +    case REG_PHYDATA:
> +        do {
> +            uint8_t dev = (s->phycr >> 16) & 0x1f;
> +            uint8_t reg = (s->phycr >> 21) & 0x1f;
> +            if (dev != 0)
> +                break;
> +            if (s->phycr_rd) {
> +                switch(reg) {
> +                case 0:    /* PHY control register */
> +                    return 0x1140 << 16;
> +                case 1:    /* PHY status register */
> +                    return 0x796d << 16;
> +                case 2:    /* PHY ID 1 register */
> +                    return 0x0141 << 16;
> +                case 3:    /* PHY ID 2 register */
> +                    return 0x0cc2 << 16;
> +                case 4:    /* Autonegotiation advertisement register */
> +                    return 0x0de1 << 16;
> +                case 5:    /* Autonegotiation partner abilities register */
> +                    return 0x45e1 << 16;
> +                case 17:/* Marvell 88E1111: PHY Specific Status Register - Copper */
> +                    return ((2 << 14) | (1 << 13) | (1 << 11) | (1 << 10)) << 16;
> +                }
> +            }
> +        } while(0);
> +        break;
> +    case REG_FCR:
> +        return (2 << 9);
> +    case REG_BPR:
> +        return (4 << 8);
> +    case REG_TXPTR:
> +        return s->tx_idx;
> +    case REG_HPTXPTR:
> +        return s->hptx_idx;
> +    case REG_RXPTR:
> +        return s->rx_idx;
> +    case REG_TXPKT:
> +        return s->tx_pkt;
> +    case REG_RXPKT:
> +        return s->rx_pkt;
> +    case REG_RXBCST:
> +        return s->rx_bcst;
> +    case REG_RXMCST:
> +        return s->rx_mcst;
> +    case REG_RXRUNT:
> +        return s->rx_runt;
> +    case REG_RXERR0:
> +        return (s->rx_crc << 16) | (s->rx_ftl);
> +    case REG_RXERR1:
> +        return (s->rx_drop << 16);
> +    default:
> +        break;
> +    }
> +
> +    return rc;
> +}
> +
> +static void ftgmac100_chip_reset(ftgmac100_state *s)
> +{
> +    s->isr = 0;
> +    s->ier = 0;
> +    s->mhash[0] = 0;
> +    s->mhash[1] = 0;
> +    s->tx_bar = 0;
> +    s->rx_bar = 0;
> +    s->hptx_bar = 0;
> +    s->tx_idx = 0;
> +    s->rx_idx = 0;
> +    s->hptx_idx = 0;
> +    s->maccr = 0;
> +    s->macsr = 0;
> +    s->phycr = 0;
> +    s->txbuff.len = 0;
> +    s->rx_pkt = 0;
> +    s->rx_bcst = 0;
> +    s->rx_mcst = 0;
> +    s->rx_runt = 0;
> +    s->rx_drop = 0;
> +    s->rx_crc = 0;
> +    s->rx_ftl = 0;
> +    s->tx_pkt = 0;
> +
> +    if (s->qtimer)
> +        qemu_del_timer(s->qtimer);
> +
> +    ftgmac100_update_irq(s);
> +}
> +
> +static void ftgmac100_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
> +{
> +    ftgmac100_state *s = opaque;
> +
> +    switch (addr) {
> +    case REG_ISR:
> +        s->isr &= ~((uint32_t)val);
> +        ftgmac100_update_irq(s);
> +        break;
> +    case REG_IMR:
> +        s->ier = (uint32_t)val;
> +        ftgmac100_update_irq(s);
> +        break;
> +    case REG_HMAC:
> +        s->conf.macaddr.a[1] = (val >> 0) & 0xff;
> +        s->conf.macaddr.a[0] = (val >> 8) & 0xff;
> +        break;
> +    case REG_LMAC:
> +        s->conf.macaddr.a[5] = (val >> 0) & 0xff;
> +        s->conf.macaddr.a[4] = (val >> 8) & 0xff;
> +        s->conf.macaddr.a[3] = (val >> 16) & 0xff;
> +        s->conf.macaddr.a[2] = (val >> 24) & 0xff;
> +        break;
> +    case REG_MHASH0:
> +        s->mhash[0] = (uint32_t)val;
> +        break;
> +    case REG_MHASH1:
> +        s->mhash[1] = (uint32_t)val;
> +        break;
> +    case REG_TXBAR:
> +        s->tx_bar = (uint32_t)val;
> +        break;
> +    case REG_RXBAR:
> +        s->rx_bar = (uint32_t)val;
> +        break;
> +    case REG_HPTXBAR:
> +        s->hptx_bar = (uint32_t)val;
> +        break;
> +    case REG_MACCR:
> +        s->maccr = (uint32_t)val;
> +        if (s->maccr & MACCR_SW_RST) {
> +            ftgmac100_chip_reset(s);
> +            s->maccr &= ~MACCR_SW_RST;
> +        }
> +        break;
> +    case REG_MACSR:
> +        s->macsr &= ~((uint32_t)val);
> +        break;
> +    case REG_PHYCTRL:
> +        s->phycr = (uint32_t)val;
> +        if (s->phycr & PHYCR_MDIORD)
> +            s->phycr_rd = 1;
> +        else
> +            s->phycr_rd = 0;
> +        s->phycr &= ~(PHYCR_MDIOWR | PHYCR_MDIORD);
> +        break;
> +    case REG_TXPD:
> +    case REG_HPTXPD:
> +        qemu_mod_timer(s->qtimer, qemu_get_clock_ns(vm_clock) + 1);
> +        break;
> +    default:
> +        break;
> +    }
> +}
> +
> +static const MemoryRegionOps bus_ops = {
> +    .read  = ftgmac100_mem_read,
> +    .write = ftgmac100_mem_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4
> +    }
> +};
> +
> +static void ftgmac100_cleanup(NetClientState *nc)
> +{
> +    ftgmac100_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
> +
> +    s->nic = NULL;
> +}
> +
> +static NetClientInfo net_ftgmac100_info = {
> +    .type = NET_CLIENT_OPTIONS_KIND_NIC,
> +    .size = sizeof(NICState),
> +    .can_receive = ftgmac100_can_receive,
> +    .receive = ftgmac100_receive,
> +    .cleanup = ftgmac100_cleanup,
> +};
> +
> +static void ftgmac100_reset(DeviceState *dev)
> +{
> +    ftgmac100_state *s = FROM_SYSBUS(ftgmac100_state, sysbus_from_qdev(dev));
> +
> +    ftgmac100_chip_reset(s);
> +}
> +
> +static int ftgmac100_init1(SysBusDevice *dev)
> +{
> +    ftgmac100_state *s = FROM_SYSBUS(typeof(*s), dev);
> +
> +    memory_region_init_io(&s->mmio, &bus_ops, s, "ftgmac100", 0x1000);
> +    sysbus_init_mmio(dev, &s->mmio);
> +    sysbus_init_irq(dev, &s->irq);
> +
> +    qemu_macaddr_default_if_unset(&s->conf.macaddr);
> +    s->nic = qemu_new_nic(&net_ftgmac100_info, &s->conf,
> +                          object_get_typename(OBJECT(dev)), dev->qdev.id, s);
> +    qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
> +
> +    s->qtimer = qemu_new_timer_ns(vm_clock, ftgmac100_timer_tick, s);
> +
> +    ftgmac100_chip_reset(s);
> +
> +    return 0;
> +}
> +
> +static const VMStateDescription vmstate_ftgmac100 = {
> +    .name = "ftgmac100",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .minimum_version_id_old = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32(ier, ftgmac100_state),
> +        VMSTATE_UINT32(tx_bar, ftgmac100_state),
> +        VMSTATE_UINT32(rx_bar, ftgmac100_state),
> +        VMSTATE_UINT32(hptx_bar, ftgmac100_state),
> +        VMSTATE_UINT32(tx_idx, ftgmac100_state),
> +        VMSTATE_UINT32(rx_idx, ftgmac100_state),
> +        VMSTATE_UINT32(hptx_idx, ftgmac100_state),
> +        VMSTATE_UINT32(maccr, ftgmac100_state),
> +        VMSTATE_UINT32(macsr, ftgmac100_state),
> +        VMSTATE_UINT32(phycr, ftgmac100_state),
> +        VMSTATE_UINT32_ARRAY(mhash, ftgmac100_state, 2),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static Property ftgmac100_properties[] = {
> +    DEFINE_NIC_PROPERTIES(ftgmac100_state, conf),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void ftgmac100_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +
> +    k->init = ftgmac100_init1;
> +    dc->reset = ftgmac100_reset;
> +    dc->vmsd = &vmstate_ftgmac100;
> +    dc->props = ftgmac100_properties;
> +}
> +
> +static TypeInfo ftgmac100_info = {
> +    .name           = "ftgmac100",
> +    .parent         = TYPE_SYS_BUS_DEVICE,
> +    .instance_size  = sizeof(ftgmac100_state),
> +    .class_init     = ftgmac100_class_init,
> +};
> +
> +static void ftgmac100_register_types(void)
> +{
> +    type_register_static(&ftgmac100_info);
> +}
> +
> +/* Legacy helper function.  Should go away when machine config files are
> +   implemented.  */
> +void ftgmac100_init(NICInfo *nd, uint32_t base, qemu_irq irq);
> +
> +void ftgmac100_init(NICInfo *nd, uint32_t base, qemu_irq irq)
> +{
> +    DeviceState *dev;
> +    SysBusDevice *s;
> +
> +    qemu_check_nic_model(nd, "ftgmac100");
> +    dev = qdev_create(NULL, "ftgmac100");
> +    qdev_set_nic_properties(dev, nd);
> +    qdev_init_nofail(dev);
> +    s = sysbus_from_qdev(dev);
> +    sysbus_mmio_map(s, 0, base);
> +    sysbus_connect_irq(s, 0, irq);
> +}
> +
> +type_init(ftgmac100_register_types)
> diff --git a/hw/ftgmac100.h b/hw/ftgmac100.h
> new file mode 100644
> index 0000000..c8c00f9
> --- /dev/null
> +++ b/hw/ftgmac100.h
> @@ -0,0 +1,191 @@
> +/*
> + *
> + * Copyright (C) 2011 Dante Su <dantesu@faraday-tech.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

Current address of FSF is:

51 Franklin Street, Fifth Floor
Boston, MA 02110-1301
USA

However, please use the recommended web version like other files.

> + */
> +
> +#ifndef _FTGMAC100_H

This should match file location, like QEMU_HW_FTGMAC100_H.

> +#define _FTGMAC100_H
> +
> +// --------------------------------------------------------------------

Please use C89 comments. I posted a sed script for conversion:
http://lists.nongnu.org/archive/html/qemu-devel/2011-01/msg01537.html

> +//        FTGMAC100 MAC Registers
> +// --------------------------------------------------------------------
> +
> +#define REG_ISR             0x00            // interrups status register
> +#define REG_IMR             0x04            // interrupt maks register
> +#define REG_HMAC            0x08            // MAC address (Most significant)
> +#define REG_LMAC            0x0c            // MAC address (Least significant)
> +#define REG_MHASH0          0x10            // Multicast Address Hash Table 0 register
> +#define REG_MHASH1          0x14            // Multicast Address Hash Table 1 register
> +#define REG_TXPD            0x18            // Transmit Poll Demand register
> +#define REG_RXPD            0x1c            // Receive Poll Demand register
> +#define REG_TXBAR           0x20            // Transmit Ring Base Address register
> +#define REG_RXBAR           0x24            // Receive Ring Base Address register
> +#define REG_HPTXPD          0x28            // High Priority Transmit Ring Base Address register
> +#define REG_HPTXBAR         0x2C            // High Priority Receive Ring Base Address register
> +#define REG_ITC             0x30            // interrupt timer control register
> +#define REG_APTC            0x34            // Automatic Polling Timer control register
> +#define REG_DBLAC           0x38            // DMA Burst Length and Arbitration control register
> +
> +#define REG_MACCR           0x50            // MAC control register
> +#define REG_MACSR           0x54            // MAC status register
> +#define REG_TSTMODE         0x58            // test mode register
> +#define REG_PHYCTRL         0x60            // PHY control register
> +#define REG_PHYDATA         0x64            // PHY Write Data register
> +#define REG_FCR             0x68            // Flow Control register
> +#define REG_BPR             0x6c            // back pressure register
> +#define REG_WOLCR           0x70            // Wake-On-Lan control register
> +#define REG_WOLSR           0x74            // Wake-On-Lan status register
> +#define REG_WFCRC           0x78            // Wake-up Frame CRC register
> +#define REG_WFBM1           0x80            // wake-up frame byte mask 1st double word register
> +#define REG_WFBM2           0x84            // wake-up frame byte mask 2nd double word register
> +#define REG_WFBM3           0x88            // wake-up frame byte mask 3rd double word register
> +#define REG_WFBM4           0x8c            // wake-up frame byte mask 4th double word register
> +#define REG_TXPTR           0x90
> +#define REG_HPTXPTR         0x94
> +#define REG_RXPTR           0x98
> +#define REG_TXPKT           0xa0
> +#define REG_TXERR0          0xa4
> +#define REG_TXERR1          0xa8
> +#define REG_TXERR2          0xac
> +#define REG_RXPKT           0xb0
> +#define REG_RXBCST          0xb4
> +#define REG_RXMCST          0xb8
> +#define REG_RXRUNT          0xc0
> +#define REG_RXERR0          0xc4
> +#define REG_RXERR1          0xc8
> +
> +//Interrupt status register(ISR), Interrupt mask register(IMR) bit setting
> +#define ISR_NOHTXB            (1UL<<10)

Enums would be nicer for GDB.

> +#define ISR_PHYSTS_CHG        (1UL<<9)
> +#define ISR_AHB_ERR            (1UL<<8)
> +#define ISR_XPKT_LOST        (1UL<<7)
> +#define ISR_NOTXBUF            (1UL<<6)
> +#define ISR_XPKT_OK            (1UL<<5)        // FIFO
> +#define ISR_XPKT_FINISH        (1UL<<4)        // ETHER
> +#define ISR_RPKT_LOST        (1UL<<3)
> +#define ISR_NORXBUF            (1UL<<2)
> +#define ISR_RPKT_OK            (1UL<<1)        // FIFO
> +#define ISR_RPKT_FINISH        (1UL<<0)        // ETHER
> +
> +/* MACC control bits */
> +#define MACCR_SW_RST            (1UL<<31)
> +#define MACCR_100M                (1UL<<19)
> +#define MACCR_CRC_DIS            (1UL<<18)
> +#define MACCR_RX_BROADPKT        (1UL<<17)
> +#define MACCR_RX_MULTIPKT        (1UL<<16)
> +#define MACCR_HT_MULTI_EN        (1UL<<15)
> +#define MACCR_RCV_ALL            (1UL<<14)
> +#define MACCR_JUMBO_LF            (1UL<<13)
> +#define MACCR_RX_RUNT            (1UL<<12)
> +#define MACCR_CRC_APD            (1UL<<10)
> +#define MACCR_GMODE                (1UL<<9)
> +#define MACCR_FULLDUP            (1UL<<8)
> +#define MACCR_ENRX_IN_HALFTX    (1UL<<7)
> +#define MACCR_LOOP_EN            (1UL<<6)
> +#define MACCR_HPTXR_EN            (1UL<<5)
> +#define MACCR_VLAN_RM            (1UL<<4)
> +#define MACCR_RCV_EN            (1UL<<3)
> +#define MACCR_XMT_EN            (1UL<<2)
> +#define MACCR_RDMA_EN            (1UL<<1)
> +#define MACCR_XDMA_EN            (1UL<<0)
> +
> +// --------------------------------------------------------------------
> +//        MII PHY Registers
> +// --------------------------------------------------------------------
> +
> +//
> +// Bits related to the MII interface
> +//
> +#define PHYCR_MDIOWR            (1 << 27)
> +#define PHYCR_MDIORD            (1 << 26)
> +
> +// --------------------------------------------------------------------
> +//        Receive Ring descriptor structure
> +// --------------------------------------------------------------------
> +typedef struct {
> +    // RXDES0
> +    uint32_t len: 14; // 0~13

The standard actually only allows a type of 'unsigned' for the bit fields.

However, bit fields cause problems since they are implementation defined.

> +    uint32_t rsvd1: 1; // 14
> +    uint32_t end: 1; //15
> +    uint32_t mcast: 1; //16
> +    uint32_t bcast: 1; //17
> +    uint32_t rxerr: 1; //18
> +    uint32_t crcerr: 1; //19
> +    uint32_t ftl: 1; //20
> +    uint32_t runt: 1; //21
> +    uint32_t oddnb: 1; //22
> +    uint32_t fifofull: 1; //23
> +    uint32_t pauseopc: 1; //24
> +    uint32_t pausefrm: 1; //25
> +    uint32_t rsvd2: 2; //26 ~ 27
> +    uint32_t lrs: 1; //28
> +    uint32_t frs: 1; //29
> +    uint32_t rsvd3: 1; //30
> +    uint32_t owner: 1; //31 - 1:Software, 0: Hardware
> +
> +    // RXDES1
> +    uint32_t vlantag: 16; // 0 ~ 15
> +    uint32_t rsvd4: 4; // 16 ~ 19
> +    uint32_t proto: 2; // 20 ~ 21
> +    uint32_t llc: 1; // 22
> +    uint32_t df: 1; // 23
> +    uint32_t vlan: 1; // 24
> +    uint32_t tcpcs: 1; // 25
> +    uint32_t udpcs: 1; // 26
> +    uint32_t ipcs: 1; // 27
> +    uint32_t rsvd5: 4; // 28 ~ 31
> +
> +    // RXDES2
> +    void    *skb;

This is wrong, the size of the pointer would change when compiled a 64 bit host.

> +
> +    // RXDES3
> +    uint32_t buf;
> +} ftgmac100_rxdesc_t;

The structure should be packed with QEMU_PACKED attribute.

> +
> +typedef struct {
> +    // TXDES0
> +    uint32_t len: 14;
> +    uint32_t rsvd1: 1;
> +    uint32_t end: 1;
> +    uint32_t rsvd2: 3;
> +    uint32_t crcerr: 1;
> +    uint32_t rsvd3: 8;
> +    uint32_t lts: 1;
> +    uint32_t fts: 1;
> +    uint32_t rsvd4: 1;
> +    uint32_t owner: 1; // 31 - 1:Hardware, 0: Software
> +
> +    // TXDES1
> +    uint32_t vlantag: 16;
> +    uint32_t vlan:   1;
> +    uint32_t tcpcs:  1;
> +    uint32_t udpcs:  1;
> +    uint32_t ipcs:   1;
> +    uint32_t rsvd5:  2;
> +    uint32_t llc:    1;
> +    uint32_t rsvd6:  7;
> +    uint32_t tx2fic: 1;
> +    uint32_t txic:   1;
> +
> +    // TXDES2
> +    void    *skb;
> +
> +    // TXDES3
> +    uint32_t buf;
> +} ftgmac100_txdesc_t;
> +
> +#endif    /* #ifndef _FTGMAC100_H */
> --
> 1.7.9.5
>
>
> ********************* Confidentiality Notice ************************
> This electronic message and any attachments may contain
> confidential and legally privileged information or
> information which is otherwise protected from disclosure.
> If you are not the intended recipient,please do not disclose
> the contents, either in whole or in part, to anyone,and
> immediately delete the message and any attachments from
> your computer system and destroy all hard copies.
> Thank you for your cooperation.
> ***********************************************************************
>
>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Qemu-devel] [PATCH 02/18] hw: add QEMU model for Faraday APB DMA
  2013-01-18  6:28 ` [Qemu-devel] [PATCH 02/18] hw: add QEMU model for Faraday APB DMA Dante
@ 2013-01-19  8:51   ` Blue Swirl
  0 siblings, 0 replies; 8+ messages in thread
From: Blue Swirl @ 2013-01-19  8:51 UTC (permalink / raw)
  To: Dante; +Cc: peter.maydell, qemu-devel, paul

On Fri, Jan 18, 2013 at 6:28 AM, Dante <dantesu@faraday-tech.com> wrote:
> Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
> ---
>  hw/ftapbbrg020.c |  485 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/ftapbbrg020.h |   43 +++++
>  2 files changed, 528 insertions(+)
>  create mode 100644 hw/ftapbbrg020.c
>  create mode 100644 hw/ftapbbrg020.h
>
> diff --git a/hw/ftapbbrg020.c b/hw/ftapbbrg020.c
> new file mode 100644
> index 0000000..3378312
> --- /dev/null
> +++ b/hw/ftapbbrg020.c
> @@ -0,0 +1,485 @@
> +/*
> + * QEMU model of the FTAPBBRG020 DMA Controller
> + *
> + * Copyright (C) 2012 Faraday Technology
> + * Copyright (C) 2012 Dante Su <dantesu@faraday-tech.com>
> + *
> + * Note: The FTAPBBRG020 DMA decreasing address mode is not implemented.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +
> +#include "sysbus.h"
> +#include "sysemu/sysemu.h"
> +#include "sysemu/blockdev.h"
> +
> +#include "ftapbbrg020.h"
> +
> +#ifndef min
> +#define min(a, b)    ((a) < (b) ? (a) : (b))
> +#endif
> +
> +#ifndef max
> +#define max(a, b)    ((a) > (b) ? (a) : (b))
> +#endif
> +
> +typedef struct _ftapbbrg020_state ftapbbrg020_state;

Underscores are not allowed at the start of identifiers, see HACKING.

> +
> +typedef struct _ftapbbrg020_chan {
> +    ftapbbrg020_state *chip;
> +
> +    int id;
> +    int burst;
> +    int src_bw;
> +    int src_stride;
> +    int dst_bw;
> +    int dst_stride;
> +
> +    /* HW register caches */
> +    uint32_t src;
> +    uint32_t dst;
> +    uint32_t len;
> +    uint32_t cmd;
> +} ftapbbrg020_chan;
> +
> +typedef struct _ftapbbrg020_state {
> +    SysBusDevice busdev;
> +    MemoryRegion iomem;
> +    qemu_irq irq;
> +
> +    ftapbbrg020_chan chan[4];
> +    qemu_irq         ack[16];
> +    uint32_t         req;
> +
> +    int busy;    /* Busy Channel ID */
> +    QEMUTimer *qtimer;
> +
> +} ftapbbrg020_state;
> +
> +static inline uint32_t
> +ftapbbrg020_get_isr(ftapbbrg020_state *s)
> +{
> +    int i;
> +    uint32_t isr = 0;
> +    ftapbbrg020_chan *chan;
> +
> +    for (i = 0; i < 4; ++i) {
> +        chan = s->chan + i;
> +        isr |= (chan->cmd & 0x12);
> +    }
> +
> +    return isr;
> +}
> +
> +static inline void
> +ftapbbrg020_update_irq(ftapbbrg020_state *s)
> +{
> +    uint32_t isr = ftapbbrg020_get_isr(s);
> +
> +    if (isr)
> +        qemu_set_irq(s->irq, 1);
> +    else
> +        qemu_set_irq(s->irq, 0);
> +}
> +
> +static void
> +ftapbbrg020_chan_cmd_decode(ftapbbrg020_chan *c)
> +{
> +    uint32_t tmp;
> +
> +    /* 1. decode burst size */
> +    c->burst = (c->cmd & 0x08) ? 4 : 1;
> +
> +    /* 2. decode source/destination width */
> +    tmp = (c->cmd >> 20) & 0x03;
> +    if (tmp > 2)
> +        tmp = 2;
> +    c->src_bw = c->dst_bw = 8 << (2 - tmp);
> +
> +    /* 3. decode source address stride */
> +    switch((c->cmd >> 8) & 0x03) {
> +    case 0:
> +        c->src_stride = 0;
> +        break;
> +    case 1:
> +        c->src_stride = c->src_bw >> 3;
> +        break;
> +    case 2:
> +        c->src_stride = 2 * (c->src_bw >> 3);
> +        break;
> +    case 3:
> +        c->src_stride = 4 * (c->src_bw >> 3);
> +        break;
> +    }
> +
> +    /* 4. decode destination address stride */
> +    switch((c->cmd >> 12) & 0x03) {
> +    case 0:
> +        c->dst_stride = 0;
> +        break;
> +    case 1:
> +        c->dst_stride = c->dst_bw >> 3;
> +        break;
> +    case 2:
> +        c->dst_stride = 2 * (c->dst_bw >> 3);
> +        break;
> +    case 3:
> +        c->dst_stride = 4 * (c->dst_bw >> 3);
> +        break;
> +    }
> +}
> +
> +static void
> +ftapbbrg020_chan_start(ftapbbrg020_chan *c)
> +{
> +    ftapbbrg020_state *s = c->chip;
> +    hwaddr src, dst, src_len, dst_len;
> +    uint8_t *src_map = NULL, *dst_map = NULL;
> +    uint8_t *src_ptr = NULL, *dst_ptr = NULL;

src_* could probably be const.

> +    uint8_t buf[4096];
> +    int src_hs = 0, dst_hs = 0, len = 0;
> +
> +    if (!(c->cmd & 0x01))
> +        return;
> +
> +    s->busy = c->id;
> +
> +    src = c->src;
> +    dst = c->dst;
> +    src_hs = (c->cmd >> 24) & 0xf;
> +    dst_hs = (c->cmd >> 16) & 0xf;
> +    src_len = c->src_stride ? (c->len * (c->src_bw >> 3)) : (c->src_bw >> 3);
> +    dst_len = c->dst_stride ? (c->len * (c->dst_bw >> 3)) : (c->dst_bw >> 3);
> +    if (!cpu_physical_memory_is_io(c->src))
> +        src_map = src_ptr = cpu_physical_memory_map(c->src, &src_len, 0);
> +    if (!cpu_physical_memory_is_io(c->dst))
> +        dst_map = dst_ptr = cpu_physical_memory_map(c->dst, &dst_len, 1);
> +
> +    while (c->len > 0) {
> +
> +        if (src_hs && !(s->req & (1 << src_hs)))
> +            break;
> +
> +        if (dst_hs && !(s->req & (1 << dst_hs)))
> +            break;
> +
> +        len = min(sizeof(buf), c->burst * (c->src_bw >> 3));
> +
> +        /* load data from source into local buffer */
> +        if (src_ptr) {
> +            if (c->src_stride) {
> +                memcpy(buf, src_ptr, len);
> +                src += len;
> +                src_ptr += len;
> +            } else {
> +                int i;
> +                switch(c->src_bw) {
> +                case 8:
> +                    for (i = 0; i < len; i += 1)
> +                        *(uint8_t *)(buf + i) = *(uint8_t *)src_ptr;

How is this and the other cases below different from memcpy()?

> +                    break;
> +                case 16:
> +                    for (i = 0; i < len; i += 2)
> +                        *(uint16_t *)(buf + i) = *(uint16_t *)src_ptr;
> +                    break;
> +                default:
> +                    for (i = 0; i < len; i += 4)
> +                        *(uint32_t *)(buf + i) = *(uint32_t *)src_ptr;
> +                    break;
> +                }
> +            }
> +        } else {
> +            uint32_t rl, stride = c->src_bw >> 3;
> +            for (rl = 0; rl < len; rl += stride, src += c->src_stride)
> +                cpu_physical_memory_read(src, (uint64_t *)(buf + rl), stride);
> +        }
> +
> +        /* DMA Hardware Handshake */
> +        if (src_hs) {
> +            qemu_set_irq(s->ack[src_hs], 1);
> +        }
> +
> +        /* store data into destination from local buffer */
> +        if (dst_ptr) {
> +            if (c->dst_stride) {
> +                memcpy(dst_ptr, buf, len);
> +                dst += len;
> +                dst_ptr += len;
> +            } else {
> +                int i;
> +                switch(c->dst_bw) {
> +                case 8:
> +                    for (i = 0; i < len; i += 1)
> +                        *(uint8_t *)dst_ptr = *(uint8_t *)(buf + i);
> +                    break;
> +                case 16:
> +                    for (i = 0; i < len; i += 2)
> +                        *(uint16_t *)dst_ptr = *(uint16_t *)(buf + i);
> +                    break;
> +                default:
> +                    for (i = 0; i < len; i += 4)
> +                        *(uint32_t *)dst_ptr = *(uint32_t *)(buf + i);
> +                    break;
> +                }
> +            }
> +        } else {
> +            uint32_t wl, stride = c->dst_bw >> 3;
> +            for (wl = 0; wl < len; wl += stride, dst += c->dst_stride)
> +                cpu_physical_memory_write(dst, (uint64_t *)(buf + wl), stride);
> +        }
> +
> +        /* DMA Hardware Handshake */
> +        if (dst_hs) {
> +            qemu_set_irq(s->ack[dst_hs], 1);
> +        }
> +
> +        /* update the channel transfer size */
> +        c->len -= len / (c->src_bw >> 3);
> +
> +        if (c->len == 0) {
> +            /* release the memory mappings */
> +            if (src_map) {
> +                cpu_physical_memory_unmap(src_map, src_len, 0, (hwaddr)(src_ptr - src_map));
> +                src_map = src_ptr = NULL;
> +            }
> +            if (dst_map) {
> +                cpu_physical_memory_unmap(dst_map, dst_len, 1, (hwaddr)(dst_ptr - dst_map));
> +                dst_map = dst_ptr = NULL;
> +            }
> +            /* update the channel transfer status */
> +            if (c->cmd & 0x04) {
> +                c->cmd |= 0x02;
> +                ftapbbrg020_update_irq(s);
> +            }
> +            /* clear start bit */
> +            c->cmd &= 0xfffffffe;
> +        }
> +    }
> +
> +    /* release the memory mappings */
> +    if (src_map)
> +        cpu_physical_memory_unmap(src_map, src_len, 0, (hwaddr)(src_ptr - src_map));
> +    if (dst_map)
> +        cpu_physical_memory_unmap(dst_map, dst_len, 1, (hwaddr)(dst_ptr - dst_map));
> +
> +    /* update src/dst address */
> +    c->src = src;
> +    c->dst = dst;
> +
> +    s->busy = -1;
> +}
> +
> +static void
> +ftapbbrg020_chan_reset(ftapbbrg020_chan *c)
> +{
> +    c->cmd = 0;
> +    c->src = 0;
> +    c->dst = 0;
> +    c->len = 0;
> +}
> +
> +static void
> +ftapbbrg020_timer_tick(void *opaque)
> +{
> +    ftapbbrg020_state *s = (ftapbbrg020_state *)opaque;
> +    ftapbbrg020_chan *c = NULL;
> +    int i, jobs, done;
> +
> +    jobs = 0;
> +    done = 0;
> +    for (i = 0; i < 4; ++i) {
> +        c = s->chan + i;
> +        if (c->cmd & 0x01) {
> +            ++jobs;
> +            ftapbbrg020_chan_start(c);
> +            if (!(c->cmd & 0x01))
> +                ++done;
> +        }
> +    }
> +
> +    /* ToDo: Use mutex to skip this periodic checker */
> +    if (jobs - done > 0) {
> +        qemu_mod_timer(s->qtimer, qemu_get_clock_ns(vm_clock) + 1);
> +    } else {
> +        qemu_mod_timer(s->qtimer, qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() >> 2));
> +    }
> +}
> +
> +static void
> +ftapbbrg020_handle_req(void *opaque, int line, int level)
> +{
> +    ftapbbrg020_state *s = (ftapbbrg020_state *)opaque;

Useless cast in C.

> +
> +    if (level) {
> +        s->req |= (1 << line);
> +    } else {
> +        s->req &= ~(1 << line);
> +        qemu_set_irq(s->ack[line], 0);
> +    }
> +}
> +
> +static void ftapbbrg020_chip_reset(ftapbbrg020_state *s)
> +{
> +    int i;
> +
> +    for (i = 0; i < 4; ++i) {
> +        ftapbbrg020_chan_reset(s->chan + i);
> +    }
> +
> +    /* We can assume our GPIO have been wired up now */
> +    for (i = 0; i < 16; ++i) {
> +        qemu_set_irq(s->ack[i], 0);
> +    }
> +    s->req = 0;
> +}
> +
> +static uint64_t
> +ftapbbrg020_mem_read(void *opaque, hwaddr addr, unsigned int size)
> +{
> +    ftapbbrg020_state *s = opaque;
> +    ftapbbrg020_chan  *c = NULL;
> +    uint32_t ret = 0;
> +
> +    if (addr >= 0x80 && addr < 0xC0) {
> +        c = s->chan + REG_CHAN_ID(addr);
> +        switch(addr & 0x0f) {
> +        case REG_CHAN_CMD:
> +            return c->cmd;
> +        case REG_CHAN_SRC:
> +            return c->src;
> +        case REG_CHAN_DST:
> +            return c->dst;
> +        case REG_CHAN_CYC:
> +            return c->len;
> +        }
> +    } else {
> +        switch(addr) {
> +        case 0xC8:    /* revision register */

The comment could be replaced by self-documenting enum for 0xC8.

> +            return 0x00010800;
> +        default:
> +            break;
> +        }
> +    }
> +
> +    return ret;
> +}
> +
> +static void
> +ftapbbrg020_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
> +{
> +    ftapbbrg020_state *s = opaque;
> +    ftapbbrg020_chan  *c = NULL;
> +
> +    if (addr >= 0x80 && addr < 0xC0) {
> +        c = s->chan + REG_CHAN_ID(addr);
> +        switch(addr & 0x0f) {
> +        case REG_CHAN_CMD:
> +            c->cmd = (uint32_t)val;
> +            ftapbbrg020_update_irq(s);
> +            if (c->cmd & 0x01) {
> +                ftapbbrg020_chan_cmd_decode(c);
> +                /* kick-off DMA engine */
> +                qemu_mod_timer(s->qtimer, qemu_get_clock_ns(vm_clock) + 1);
> +            }
> +            break;
> +        case REG_CHAN_SRC:
> +            c->src = (uint32_t)val;
> +            break;
> +        case REG_CHAN_DST:
> +            c->dst = (uint32_t)val;
> +            break;
> +        case REG_CHAN_CYC:
> +            c->len = (uint32_t)val & 0x00ffffff;
> +            break;
> +        }
> +    }
> +}
> +
> +static const MemoryRegionOps ftapbbrg020_mem_ops = {
> +    .read  = ftapbbrg020_mem_read,
> +    .write = ftapbbrg020_mem_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4
> +    }
> +};
> +
> +static void ftapbbrg020_reset(DeviceState *d)
> +{
> +    ftapbbrg020_state *s = DO_UPCAST(ftapbbrg020_state, busdev.qdev, d);
> +    ftapbbrg020_chip_reset(s);
> +}
> +
> +static int ftapbbrg020_init(SysBusDevice *dev)
> +{
> +    ftapbbrg020_state *s = FROM_SYSBUS(typeof(*s), dev);
> +    int i;
> +
> +    memory_region_init_io(&s->iomem, &ftapbbrg020_mem_ops, s, "ftapbbrg020", 0x1000);
> +    sysbus_init_mmio(dev, &s->iomem);
> +    sysbus_init_irq(dev, &s->irq);
> +    qdev_init_gpio_in (&s->busdev.qdev, ftapbbrg020_handle_req, 16);
> +    qdev_init_gpio_out(&s->busdev.qdev, s->ack, 16);
> +
> +    s->busy = -1;
> +    s->qtimer = qemu_new_timer_ns(vm_clock, ftapbbrg020_timer_tick, s);
> +    for (i = 0; i < 4; ++i) {
> +        ftapbbrg020_chan *c = s->chan + i;
> +        c->id   = i;
> +        c->chip = s;
> +    }
> +
> +    return 0;
> +}
> +
> +static const VMStateDescription vmstate_ftapbbrg020 = {
> +    .name = "ftapbbrg020",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .minimum_version_id_old = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static void ftapbbrg020_class_init(ObjectClass *klass, void *data)
> +{
> +    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
> +    DeviceClass *k = DEVICE_CLASS(klass);
> +
> +    sdc->init  = ftapbbrg020_init;
> +    k->vmsd    = &vmstate_ftapbbrg020;
> +    k->reset   = ftapbbrg020_reset;
> +    k->no_user = 1;
> +}
> +
> +static TypeInfo ftapbbrg020_info = {

const

> +    .name           = "ftapbbrg020",
> +    .parent         = TYPE_SYS_BUS_DEVICE,
> +    .instance_size  = sizeof(ftapbbrg020_state),
> +    .class_init     = ftapbbrg020_class_init,
> +};
> +
> +static void ftapbbrg020_register_types(void)
> +{
> +    type_register_static(&ftapbbrg020_info);
> +}
> +
> +type_init(ftapbbrg020_register_types)
> diff --git a/hw/ftapbbrg020.h b/hw/ftapbbrg020.h
> new file mode 100644
> index 0000000..0279f10
> --- /dev/null
> +++ b/hw/ftapbbrg020.h
> @@ -0,0 +1,43 @@
> +/*
> + *  arch/arm/mach-faraday/drivers/ftapbbrg020.h
> + *
> + *  Faraday FTAPBB020 APB Bridge with DMA function
> + *
> + *  Copyright (C) 2010 Faraday Technology
> + *  Copyright (C) 2012 Dante Su <dantesu@faraday-tech.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */
> +
> +#ifndef __FTAPBBRG020_H
> +#define __FTAPBBRG020_H
> +
> +/*
> + * Channel base address
> + * @ch: channle id (0 <= id <= 3)
> + *      i.e. 0: Channel A
> + *           1: Channel B
> + *           2: Channel C
> + *           3: Channel D
> + */
> +#define REG_CHAN_ID(off)    (((off) - 0x80) >> 4)
> +#define REG_CHAN_BASE(ch)    (0x80 + ((ch) << 4))
> +
> +#define REG_CHAN_SRC        0x00
> +#define REG_CHAN_DST        0x04
> +#define REG_CHAN_CYC        0x08
> +#define REG_CHAN_CMD        0x0C
> +
> +#endif    /* __FTAPBB020_H */
> --
> 1.7.9.5
>
>
> ********************* Confidentiality Notice ************************
> This electronic message and any attachments may contain
> confidential and legally privileged information or
> information which is otherwise protected from disclosure.
> If you are not the intended recipient,please do not disclose
> the contents, either in whole or in part, to anyone,and
> immediately delete the message and any attachments from
> your computer system and destroy all hard copies.
> Thank you for your cooperation.
> ***********************************************************************
>
>

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2013-01-19  8:51 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-01-18  6:28 [Qemu-devel] [PATCH 01/18] hw: add Faraday a369 SoC platform support Dante
2013-01-18  6:28 ` [Qemu-devel] [PATCH 02/18] hw: add QEMU model for Faraday APB DMA Dante
2013-01-19  8:51   ` Blue Swirl
2013-01-18  6:28 ` [Qemu-devel] [PATCH 03/18] hw: add QEMU model for Faraday AHB DMA Dante
2013-01-18  6:28 ` [Qemu-devel] [PATCH 04/18] hw: add QEMU model for Faraday 1Gbps Ethernet MAC Dante
2013-01-19  8:41   ` Blue Swirl
2013-01-18  6:28 ` [Qemu-devel] [PATCH 05/18] hw: add QEMU model for Faraday 10/100Mbps " Dante
2013-01-19  8:15 ` [Qemu-devel] [PATCH 01/18] hw: add Faraday a369 SoC platform support Blue Swirl

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).