mainstone.c 7.88 KB
/*
 * PXA270-based Intel Mainstone platforms.
 *
 * Copyright (c) 2007 by Armin Kuster <akuster@kama-aina.net> or
 *                                    <akuster@mvista.com>
 *
 * Code based on spitz platform by Andrzej Zaborowski <balrog@zabor.org>
 *
 * This code is licensed under the GNU GPL v2.
 */
#include "hw.h"
#include "pxa.h"
#include "arm-misc.h"
#include "net.h"
#include "devices.h"
#include "boards.h"

#define MST_ETH_PHYS	0x10000300
#define MST_FPGA_PHYS	0x08000000

/* Mainstone FPGA for extern irqs */
#define FPGA_GPIO_PIN	0
#define MST_NUM_IRQS	16
#define MST_BASE	MST_FPGA_PHYS
#define MST_LEDDAT1	0x10
#define MST_LEDDAT2	0x14
#define MST_LEDCTRL	0x40
#define MST_GPSWR	0x60
#define MST_MSCWR1	0x80
#define MST_MSCWR2	0x84
#define MST_MSCWR3	0x88
#define MST_MSCRD	0x90
#define MST_INTMSKENA	0xc0
#define MST_INTSETCLR	0xd0
#define MST_PCMCIA0	0xe0
#define MST_PCMCIA1	0xe4

/* IRQ definitions */
#define ETHERNET_IRQ	3

typedef struct mst_irq_state {
    target_phys_addr_t target_base;
    qemu_irq *parent;
    qemu_irq *pins;

    uint32_t prev_level;
    uint32_t leddat1;
    uint32_t leddat2;
    uint32_t ledctrl;
    uint32_t gpswr;
    uint32_t mscwr1;
    uint32_t mscwr2;
    uint32_t mscwr3;
    uint32_t mscrd;
    uint32_t intmskena;
    uint32_t intsetclr;
    uint32_t pcmcia0;
    uint32_t pcmcia1;
} mst_irq_state;

static void 
mst_fpga_update_gpio(mst_irq_state *s)
{
    uint32_t level, diff;
    int bit;
    level = s->prev_level ^ s->intsetclr;

    for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) {
        bit = ffs(diff) - 1;
        qemu_set_irq(s->pins[bit], (level >> bit) & 1 );
    }
    s->prev_level = level;
}

static void 
mst_fpga_set_irq(void *opaque, int irq, int level)
{
    mst_irq_state *s = (mst_irq_state *)opaque;

    if (level)
        s->prev_level |= 1u << irq;
    else
        s->prev_level &= ~(1u << irq);

    if(s->intmskena & (1u << irq)) {
        s->intsetclr = 1u << irq;
        qemu_set_irq(s->parent[0], level);
    }
}

static uint32_t 
mst_fpga_readb(void *opaque, target_phys_addr_t addr)
{
    mst_irq_state *s = (mst_irq_state *) opaque;
    addr -= s->target_base;

    switch (addr) {
    case MST_LEDDAT1:
        return s->leddat1;
    case MST_LEDDAT2:
        return s->leddat2;
    case MST_LEDCTRL:
        return s->ledctrl;
    case MST_GPSWR:
        return s->gpswr;
    case MST_MSCWR1:
        return s->mscwr1;
    case MST_MSCWR2:
        return s->mscwr2;
    case MST_MSCWR3:
        return s->mscwr3;
    case MST_MSCRD:
        return s->mscrd;
    case MST_INTMSKENA:
        return s->intmskena;
    case MST_INTSETCLR:
        return s->intsetclr;
    case MST_PCMCIA0:
        return s->pcmcia0;
    case MST_PCMCIA1:
        return s->pcmcia1;
    default:
        printf("Mainstone - mst_fpga_readb: Bad register offset " 
                REG_FMT " \n", addr);
    }
    return 0;
}

static void 
mst_fpga_writeb(void *opaque, target_phys_addr_t addr, uint32_t value)
{
    mst_irq_state *s = (mst_irq_state *) opaque;
    addr -= s->target_base;
    value &= 0xffffffff;

    switch (addr) {
    case MST_LEDDAT1:
        s->leddat1 = value;
        break;
    case MST_LEDDAT2:
        s->leddat2 = value;
        break;
    case MST_LEDCTRL:
        s->ledctrl = value;
        break;
    case MST_GPSWR:
        s->gpswr = value;
        break;
    case MST_MSCWR1:
        s->mscwr1 = value;
        break;
    case MST_MSCWR2:
        s->mscwr2 = value;
        break;
    case MST_MSCWR3:
        s->mscwr3 = value;
        break;
    case MST_MSCRD:
        s->mscrd =  value;
        break;
    case MST_INTMSKENA:	/* Mask interupt */
        s->intmskena = (value & 0xFEEFF);
        mst_fpga_update_gpio(s);
        break;
    case MST_INTSETCLR:	/* clear or set interrupt */
        s->intsetclr = (value & 0xFEEFF);
        break;
    case MST_PCMCIA0:
        s->pcmcia0 = value;
        break;
    case MST_PCMCIA1:
        s->pcmcia1 = value;
        break;
    default:
        printf("Mainstone - mst_fpga_writeb: Bad register offset "
                REG_FMT " \n", addr);
    }
}

CPUReadMemoryFunc *mst_fpga_readfn[] = {
    mst_fpga_readb,
    mst_fpga_readb,
    mst_fpga_readb,
};
CPUWriteMemoryFunc *mst_fpga_writefn[] = {
    mst_fpga_writeb,
    mst_fpga_writeb,
    mst_fpga_writeb,
};

static void 
mst_fpga_save(QEMUFile *f, void *opaque)
{
    struct mst_irq_state *s = (mst_irq_state *) opaque;

    qemu_put_be32s(f, &s->prev_level);
    qemu_put_be32s(f, &s->leddat1);
    qemu_put_be32s(f, &s->leddat2);
    qemu_put_be32s(f, &s->ledctrl);
    qemu_put_be32s(f, &s->gpswr);
    qemu_put_be32s(f, &s->mscwr1);
    qemu_put_be32s(f, &s->mscwr2);
    qemu_put_be32s(f, &s->mscwr3);
    qemu_put_be32s(f, &s->mscrd);
    qemu_put_be32s(f, &s->intmskena);
    qemu_put_be32s(f, &s->intsetclr);
    qemu_put_be32s(f, &s->pcmcia0);
    qemu_put_be32s(f, &s->pcmcia1);
}

static int 
mst_fpga_load(QEMUFile *f, void *opaque, int version_id)
{
    mst_irq_state *s = (mst_irq_state *) opaque;

    qemu_get_be32s(f, &s->prev_level);
    qemu_get_be32s(f, &s->leddat1);
    qemu_get_be32s(f, &s->leddat2);
    qemu_get_be32s(f, &s->ledctrl);
    qemu_get_be32s(f, &s->gpswr);
    qemu_get_be32s(f, &s->mscwr1);
    qemu_get_be32s(f, &s->mscwr2);
    qemu_get_be32s(f, &s->mscwr3);
    qemu_get_be32s(f, &s->mscrd);
    qemu_get_be32s(f, &s->intmskena);
    qemu_get_be32s(f, &s->intsetclr);
    qemu_get_be32s(f, &s->pcmcia0);
    qemu_get_be32s(f, &s->pcmcia1);
    return 0;
}

static qemu_irq 
*mst_irq_init(struct pxa2xx_state_s *cpu, uint32_t base, int irq)
{
    mst_irq_state *s;
    int iomemtype;
    qemu_irq *qi;

    s = (mst_irq_state *) qemu_mallocz(sizeof(mst_irq_state));

    if (!s)
        return NULL;
    s->target_base = base;
    s->parent = &cpu->pic[irq];

    /* alloc the external 16 irqs */
    qi = qemu_allocate_irqs(mst_fpga_set_irq, s, MST_NUM_IRQS);
    s->pins = qi;

    iomemtype = cpu_register_io_memory(0, mst_fpga_readfn,
                    mst_fpga_writefn, s);
    cpu_register_physical_memory(MST_BASE, 0x00100000, iomemtype);
    register_savevm("mainstone_fpga", 0, 0, mst_fpga_save, mst_fpga_load, s);
    return qi;
}

enum mainstone_model_e { mainstone };

static void mainstone_common_init(int ram_size, int vga_ram_size,
                DisplayState *ds, const char *kernel_filename,
                const char *kernel_cmdline, const char *initrd_filename,
                const char *cpu_model, enum mainstone_model_e model, int arm_id)
{
    uint32_t mainstone_ram = 0x04000000;
    uint32_t mainstone_rom = 0x00800000;
    struct pxa2xx_state_s *cpu;
    qemu_irq *mst_irq;

    if (!cpu_model)
        cpu_model = "pxa270-c5";

    /* Setup CPU & memory */
    if (ram_size < mainstone_ram + mainstone_rom + PXA2XX_INTERNAL_SIZE) {
        fprintf(stderr, "This platform requires %i bytes of memory\n",
                        mainstone_ram + mainstone_rom + PXA2XX_INTERNAL_SIZE);
        exit(1);
    }

    cpu = pxa270_init(mainstone_ram, ds, cpu_model);
    cpu_register_physical_memory(0, mainstone_rom,
                    qemu_ram_alloc(mainstone_rom) | IO_MEM_ROM);

    /* Setup initial (reset) machine state */
    cpu->env->regs[15] = PXA2XX_SDRAM_BASE;

    mst_irq = mst_irq_init(cpu, MST_BASE, PXA2XX_PIC_GPIO_0);
    smc91c111_init(&nd_table[0], MST_ETH_PHYS, mst_irq[ETHERNET_IRQ]);

    arm_load_kernel(cpu->env, mainstone_ram, kernel_filename, kernel_cmdline,
                    initrd_filename, arm_id, PXA2XX_SDRAM_BASE);
}

static void mainstone_init(int ram_size, int vga_ram_size,
                const char *boot_device, DisplayState *ds,
                const char *kernel_filename, const char *kernel_cmdline,
                const char *initrd_filename, const char *cpu_model)
{
    mainstone_common_init(ram_size, vga_ram_size, ds, kernel_filename,
                kernel_cmdline, initrd_filename, cpu_model, mainstone, 0x196);
}

QEMUMachine mainstone2_machine = {
    "mainstone",
    "Mainstone II (PXA27x)",
    mainstone_init,
};