X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=hw%2Fstellaris.c;h=a28093043a0372f3735d5c26198382e614e4b64a;hb=8e31bf388e56e5babd9600b110a94381d1be07b1;hp=62f2c034450c43669bbd96138dd868de60e5d8dd;hpb=9ee6e8bb853bdea7ef6c645a1a07aa55fd206aba;p=qemu.git diff --git a/hw/stellaris.c b/hw/stellaris.c index 62f2c0344..a28093043 100644 --- a/hw/stellaris.c +++ b/hw/stellaris.c @@ -1,14 +1,32 @@ /* - * Luminary Micro Stellaris preipherals + * Luminary Micro Stellaris peripherals * * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ -#include "vl.h" -#include "arm_pic.h" +#include "sysbus.h" +#include "ssi.h" +#include "arm-misc.h" +#include "devices.h" +#include "qemu-timer.h" +#include "i2c.h" +#include "net.h" +#include "boards.h" + +#define GPIO_A 0 +#define GPIO_B 1 +#define GPIO_C 2 +#define GPIO_D 3 +#define GPIO_E 4 +#define GPIO_F 5 +#define GPIO_G 6 + +#define BP_OLED_I2C 0x01 +#define BP_OLED_SSI 0x02 +#define BP_GAMEPAD 0x04 typedef const struct { const char *name; @@ -19,16 +37,13 @@ typedef const struct { uint32_t dc2; uint32_t dc3; uint32_t dc4; - enum {OLED_I2C, OLED_SSI} oled; + uint32_t peripherals; } stellaris_board_info; /* General purpose timer module. */ -/* Multiplication factor to convert from GPTM timer ticks to qemu timer - ticks. */ -static int stellaris_clock_scale; - typedef struct gptm_state { + SysBusDevice busdev; uint32_t config; uint32_t mode[2]; uint32_t control; @@ -41,7 +56,6 @@ typedef struct gptm_state { uint32_t rtc; int64_t tick[2]; struct gptm_state *opaque[2]; - uint32_t base; QEMUTimer *timer[2]; /* The timers have an alternate output used to trigger the ADC. */ qemu_irq trigger; @@ -64,7 +78,7 @@ static void gptm_reload(gptm_state *s, int n, int reset) { int64_t tick; if (reset) - tick = qemu_get_clock(vm_clock); + tick = qemu_get_clock_ns(vm_clock); else tick = s->tick[n]; @@ -72,15 +86,14 @@ static void gptm_reload(gptm_state *s, int n, int reset) /* 32-bit CountDown. */ uint32_t count; count = s->load[0] | (s->load[1] << 16); - tick += (int64_t)count * stellaris_clock_scale; + tick += (int64_t)count * system_clock_scale; } else if (s->config == 1) { /* 32-bit RTC. 1Hz tick. */ - tick += ticks_per_sec; + tick += get_ticks_per_sec(); } else if (s->mode[n] == 0xa) { /* PWM mode. Not implemented. */ } else { - cpu_abort(cpu_single_env, "TODO: 16-bit timer mode 0x%x\n", - s->mode[n]); + hw_error("TODO: 16-bit timer mode 0x%x\n", s->mode[n]); } s->tick[n] = tick; qemu_mod_timer(s->timer[n], tick); @@ -98,8 +111,7 @@ static void gptm_tick(void *opaque) s->state |= 1; if ((s->control & 0x20)) { /* Output trigger. */ - qemu_irq_raise(s->trigger); - qemu_irq_lower(s->trigger); + qemu_irq_pulse(s->trigger); } if (s->mode[0] & 1) { /* One-shot. */ @@ -122,8 +134,7 @@ static void gptm_tick(void *opaque) } else if (s->mode[n] == 0xa) { /* PWM mode. Not implemented. */ } else { - cpu_abort(cpu_single_env, "TODO: 16-bit timer mode 0x%x\n", - s->mode[n]); + hw_error("TODO: 16-bit timer mode 0x%x\n", s->mode[n]); } gptm_update_irq(s); } @@ -132,7 +143,6 @@ static uint32_t gptm_read(void *opaque, target_phys_addr_t offset) { gptm_state *s = (gptm_state *)opaque; - offset -= s->base; switch (offset) { case 0x00: /* CFG */ return s->config; @@ -170,9 +180,9 @@ static uint32_t gptm_read(void *opaque, target_phys_addr_t offset) if (s->control == 1) return s->rtc; case 0x4c: /* TBR */ - cpu_abort(cpu_single_env, "TODO: Timer value read\n"); + hw_error("TODO: Timer value read\n"); default: - cpu_abort(cpu_single_env, "gptm_read: Bad offset 0x%x\n", (int)offset); + hw_error("gptm_read: Bad offset 0x%x\n", (int)offset); return 0; } } @@ -182,7 +192,6 @@ static void gptm_write(void *opaque, target_phys_addr_t offset, uint32_t value) gptm_state *s = (gptm_state *)opaque; uint32_t oldval; - offset -= s->base; /* The timers should be disabled before changing the configuration. We take advantage of this and defer everything until the timer is enabled. */ @@ -253,47 +262,70 @@ static void gptm_write(void *opaque, target_phys_addr_t offset, uint32_t value) s->match_prescale[0] = value; break; default: - cpu_abort(cpu_single_env, "gptm_write: Bad offset 0x%x\n", (int)offset); + hw_error("gptm_write: Bad offset 0x%x\n", (int)offset); } gptm_update_irq(s); } -static CPUReadMemoryFunc *gptm_readfn[] = { +static CPUReadMemoryFunc * const gptm_readfn[] = { gptm_read, gptm_read, gptm_read }; -static CPUWriteMemoryFunc *gptm_writefn[] = { +static CPUWriteMemoryFunc * const gptm_writefn[] = { gptm_write, gptm_write, gptm_write }; -static void stellaris_gptm_init(uint32_t base, qemu_irq irq, qemu_irq trigger) +static const VMStateDescription vmstate_stellaris_gptm = { + .name = "stellaris_gptm", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(config, gptm_state), + VMSTATE_UINT32_ARRAY(mode, gptm_state, 2), + VMSTATE_UINT32(control, gptm_state), + VMSTATE_UINT32(state, gptm_state), + VMSTATE_UINT32(mask, gptm_state), + VMSTATE_UNUSED(8), + VMSTATE_UINT32_ARRAY(load, gptm_state, 2), + VMSTATE_UINT32_ARRAY(match, gptm_state, 2), + VMSTATE_UINT32_ARRAY(prescale, gptm_state, 2), + VMSTATE_UINT32_ARRAY(match_prescale, gptm_state, 2), + VMSTATE_UINT32(rtc, gptm_state), + VMSTATE_INT64_ARRAY(tick, gptm_state, 2), + VMSTATE_TIMER_ARRAY(timer, gptm_state, 2), + VMSTATE_END_OF_LIST() + } +}; + +static int stellaris_gptm_init(SysBusDevice *dev) { int iomemtype; - gptm_state *s; + gptm_state *s = FROM_SYSBUS(gptm_state, dev); - s = (gptm_state *)qemu_mallocz(sizeof(gptm_state)); - s->base = base; - s->irq = irq; - s->trigger = trigger; - s->opaque[0] = s->opaque[1] = s; + sysbus_init_irq(dev, &s->irq); + qdev_init_gpio_out(&dev->qdev, &s->trigger, 1); - iomemtype = cpu_register_io_memory(0, gptm_readfn, - gptm_writefn, s); - cpu_register_physical_memory(base, 0x00001000, iomemtype); - s->timer[0] = qemu_new_timer(vm_clock, gptm_tick, &s->opaque[0]); - s->timer[1] = qemu_new_timer(vm_clock, gptm_tick, &s->opaque[1]); - /* ??? Save/restore. */ + iomemtype = cpu_register_io_memory(gptm_readfn, + gptm_writefn, s, + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, 0x1000, iomemtype); + + s->opaque[0] = s->opaque[1] = s; + s->timer[0] = qemu_new_timer_ns(vm_clock, gptm_tick, &s->opaque[0]); + s->timer[1] = qemu_new_timer_ns(vm_clock, gptm_tick, &s->opaque[1]); + vmstate_register(&dev->qdev, -1, &vmstate_stellaris_gptm, s); + return 0; } /* System controller. */ typedef struct { - uint32_t base; uint32_t pborctl; uint32_t ldopctl; uint32_t int_status; @@ -305,6 +337,8 @@ typedef struct { uint32_t dcgc[3]; uint32_t clkvclr; uint32_t ldoarst; + uint32_t user0; + uint32_t user1; qemu_irq irq; stellaris_board_info *board; } ssys_state; @@ -356,7 +390,6 @@ static uint32_t ssys_read(void *opaque, target_phys_addr_t offset) { ssys_state *s = (ssys_state *)opaque; - offset -= s->base; switch (offset) { case 0x000: /* DID0 */ return s->board->did0; @@ -424,17 +457,25 @@ static uint32_t ssys_read(void *opaque, target_phys_addr_t offset) return s->clkvclr; case 0x160: /* LDOARST */ return s->ldoarst; + case 0x1e0: /* USER0 */ + return s->user0; + case 0x1e4: /* USER1 */ + return s->user1; default: - cpu_abort(cpu_single_env, "gptm_read: Bad offset 0x%x\n", (int)offset); + hw_error("ssys_read: Bad offset 0x%x\n", (int)offset); return 0; } } +static void ssys_calculate_system_clock(ssys_state *s) +{ + system_clock_scale = 5 * (((s->rcc >> 23) & 0xf) + 1); +} + static void ssys_write(void *opaque, target_phys_addr_t offset, uint32_t value) { ssys_state *s = (ssys_state *)opaque; - offset -= s->base; switch (offset) { case 0x030: /* PBORCTL */ s->pborctl = value & 0xffff; @@ -462,7 +503,7 @@ static void ssys_write(void *opaque, target_phys_addr_t offset, uint32_t value) s->int_status |= (1 << 6); } s->rcc = value; - stellaris_clock_scale = 5 * (((s->rcc >> 23) & 0xf) + 1); + ssys_calculate_system_clock(s); break; case 0x100: /* RCGC0 */ s->rcgc[0] = value; @@ -498,24 +539,24 @@ static void ssys_write(void *opaque, target_phys_addr_t offset, uint32_t value) s->ldoarst = value; break; default: - cpu_abort(cpu_single_env, "gptm_write: Bad offset 0x%x\n", (int)offset); + hw_error("ssys_write: Bad offset 0x%x\n", (int)offset); } ssys_update(s); } -static CPUReadMemoryFunc *ssys_readfn[] = { +static CPUReadMemoryFunc * const ssys_readfn[] = { ssys_read, ssys_read, ssys_read }; -static CPUWriteMemoryFunc *ssys_writefn[] = { +static CPUWriteMemoryFunc * const ssys_writefn[] = { ssys_write, ssys_write, ssys_write }; -void ssys_reset(void *opaque) +static void ssys_reset(void *opaque) { ssys_state *s = (ssys_state *)opaque; @@ -526,31 +567,67 @@ void ssys_reset(void *opaque) s->dcgc[0] = 1; } -static void stellaris_sys_init(uint32_t base, qemu_irq irq, - stellaris_board_info * board) +static int stellaris_sys_post_load(void *opaque, int version_id) +{ + ssys_state *s = opaque; + + ssys_calculate_system_clock(s); + + return 0; +} + +static const VMStateDescription vmstate_stellaris_sys = { + .name = "stellaris_sys", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .post_load = stellaris_sys_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32(pborctl, ssys_state), + VMSTATE_UINT32(ldopctl, ssys_state), + VMSTATE_UINT32(int_mask, ssys_state), + VMSTATE_UINT32(int_status, ssys_state), + VMSTATE_UINT32(resc, ssys_state), + VMSTATE_UINT32(rcc, ssys_state), + VMSTATE_UINT32_ARRAY(rcgc, ssys_state, 3), + VMSTATE_UINT32_ARRAY(scgc, ssys_state, 3), + VMSTATE_UINT32_ARRAY(dcgc, ssys_state, 3), + VMSTATE_UINT32(clkvclr, ssys_state), + VMSTATE_UINT32(ldoarst, ssys_state), + VMSTATE_END_OF_LIST() + } +}; + +static int stellaris_sys_init(uint32_t base, qemu_irq irq, + stellaris_board_info * board, + uint8_t *macaddr) { int iomemtype; ssys_state *s; s = (ssys_state *)qemu_mallocz(sizeof(ssys_state)); - s->base = base; s->irq = irq; s->board = board; + /* Most devices come preprogrammed with a MAC address in the user data. */ + s->user0 = macaddr[0] | (macaddr[1] << 8) | (macaddr[2] << 16); + s->user1 = macaddr[3] | (macaddr[4] << 8) | (macaddr[5] << 16); - iomemtype = cpu_register_io_memory(0, ssys_readfn, - ssys_writefn, s); + iomemtype = cpu_register_io_memory(ssys_readfn, + ssys_writefn, s, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x00001000, iomemtype); ssys_reset(s); - /* ??? Save/restore. */ + vmstate_register(NULL, -1, &vmstate_stellaris_sys, s); + return 0; } /* I2C controller. */ typedef struct { + SysBusDevice busdev; i2c_bus *bus; qemu_irq irq; - uint32_t base; uint32_t msa; uint32_t mcs; uint32_t mdr; @@ -572,7 +649,6 @@ static uint32_t stellaris_i2c_read(void *opaque, target_phys_addr_t offset) { stellaris_i2c_state *s = (stellaris_i2c_state *)opaque; - offset -= s->base; switch (offset) { case 0x00: /* MSA */ return s->msa; @@ -592,8 +668,7 @@ static uint32_t stellaris_i2c_read(void *opaque, target_phys_addr_t offset) case 0x20: /* MCR */ return s->mcr; default: - cpu_abort(cpu_single_env, "strllaris_i2c_read: Bad offset 0x%x\n", - (int)offset); + hw_error("strllaris_i2c_read: Bad offset 0x%x\n", (int)offset); return 0; } } @@ -611,7 +686,6 @@ static void stellaris_i2c_write(void *opaque, target_phys_addr_t offset, { stellaris_i2c_state *s = (stellaris_i2c_state *)opaque; - offset -= s->base; switch (offset) { case 0x00: /* MSA */ s->msa = value & 0xff; @@ -670,15 +744,15 @@ static void stellaris_i2c_write(void *opaque, target_phys_addr_t offset, break; case 0x20: /* MCR */ if (value & 1) - cpu_abort(cpu_single_env, + hw_error( "stellaris_i2c_write: Loopback not implemented\n"); if (value & 0x20) - cpu_abort(cpu_single_env, + hw_error( "stellaris_i2c_write: Slave mode not implemented\n"); s->mcr = value & 0x31; break; default: - cpu_abort(cpu_single_env, "stellaris_i2c_write: Bad offset 0x%x\n", + hw_error("stellaris_i2c_write: Bad offset 0x%x\n", (int)offset); } stellaris_i2c_update(s); @@ -699,33 +773,53 @@ static void stellaris_i2c_reset(stellaris_i2c_state *s) stellaris_i2c_update(s); } -static CPUReadMemoryFunc *stellaris_i2c_readfn[] = { +static CPUReadMemoryFunc * const stellaris_i2c_readfn[] = { stellaris_i2c_read, stellaris_i2c_read, stellaris_i2c_read }; -static CPUWriteMemoryFunc *stellaris_i2c_writefn[] = { +static CPUWriteMemoryFunc * const stellaris_i2c_writefn[] = { stellaris_i2c_write, stellaris_i2c_write, stellaris_i2c_write }; -static void stellaris_i2c_init(uint32_t base, qemu_irq irq, i2c_bus *bus) +static const VMStateDescription vmstate_stellaris_i2c = { + .name = "stellaris_i2c", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(msa, stellaris_i2c_state), + VMSTATE_UINT32(mcs, stellaris_i2c_state), + VMSTATE_UINT32(mdr, stellaris_i2c_state), + VMSTATE_UINT32(mtpr, stellaris_i2c_state), + VMSTATE_UINT32(mimr, stellaris_i2c_state), + VMSTATE_UINT32(mris, stellaris_i2c_state), + VMSTATE_UINT32(mcr, stellaris_i2c_state), + VMSTATE_END_OF_LIST() + } +}; + +static int stellaris_i2c_init(SysBusDevice * dev) { - stellaris_i2c_state *s; + stellaris_i2c_state *s = FROM_SYSBUS(stellaris_i2c_state, dev); + i2c_bus *bus; int iomemtype; - s = (stellaris_i2c_state *)qemu_mallocz(sizeof(stellaris_i2c_state)); - s->base = base; - s->irq = irq; + sysbus_init_irq(dev, &s->irq); + bus = i2c_init_bus(&dev->qdev, "i2c"); s->bus = bus; - iomemtype = cpu_register_io_memory(0, stellaris_i2c_readfn, - stellaris_i2c_writefn, s); - cpu_register_physical_memory(base, 0x00001000, iomemtype); + iomemtype = cpu_register_io_memory(stellaris_i2c_readfn, + stellaris_i2c_writefn, s, + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, 0x1000, iomemtype); /* ??? For now we only implement the master interface. */ stellaris_i2c_reset(s); + vmstate_register(&dev->qdev, -1, &vmstate_stellaris_i2c, s); + return 0; } /* Analogue to Digital Converter. This is only partially implemented, @@ -744,7 +838,7 @@ static void stellaris_i2c_init(uint32_t base, qemu_irq irq, i2c_bus *bus) typedef struct { - uint32_t base; + SysBusDevice busdev; uint32_t actss; uint32_t ris; uint32_t im; @@ -759,7 +853,8 @@ typedef struct } fifo[4]; uint32_t ssmux[4]; uint32_t ssctl[4]; - qemu_irq irq; + uint32_t noise; + qemu_irq irq[4]; } stellaris_adc_state; static uint32_t stellaris_adc_fifo_read(stellaris_adc_state *s, int n) @@ -783,6 +878,8 @@ static void stellaris_adc_fifo_write(stellaris_adc_state *s, int n, { int head; + /* TODO: Real hardware has limited size FIFOs. We have a full 16 entry + FIFO fir each sequencer. */ head = (s->fifo[n].state >> 4) & 0xf; if (s->fifo[n].state & STELLARIS_ADC_FIFO_FULL) { s->ostat |= 1 << n; @@ -799,27 +896,36 @@ static void stellaris_adc_fifo_write(stellaris_adc_state *s, int n, static void stellaris_adc_update(stellaris_adc_state *s) { int level; + int n; - level = (s->ris & s->im) != 0; - qemu_set_irq(s->irq, level); + for (n = 0; n < 4; n++) { + level = (s->ris & s->im & (1 << n)) != 0; + qemu_set_irq(s->irq[n], level); + } } static void stellaris_adc_trigger(void *opaque, int irq, int level) { stellaris_adc_state *s = (stellaris_adc_state *)opaque; - /* Some applications use the ADC as a random number source, so introduce - some variation into the signal. */ - static uint32_t noise = 0; + int n; - if ((s->actss & 1) == 0) { - return; - } + for (n = 0; n < 4; n++) { + if ((s->actss & (1 << n)) == 0) { + continue; + } - noise = noise * 314159 + 1; - /* ??? actual inputs not implemented. Return an arbitrary value. */ - stellaris_adc_fifo_write(s, 0, 0x200 + ((noise >> 16) & 7)); - s->ris |= 1; - stellaris_adc_update(s); + if (((s->emux >> (n * 4)) & 0xff) != 5) { + continue; + } + + /* Some applications use the ADC as a random number source, so introduce + some variation into the signal. */ + s->noise = s->noise * 314159 + 1; + /* ??? actual inputs not implemented. Return an arbitrary value. */ + stellaris_adc_fifo_write(s, n, 0x200 + ((s->noise >> 16) & 7)); + s->ris |= (1 << n); + stellaris_adc_update(s); + } } static void stellaris_adc_reset(stellaris_adc_state *s) @@ -838,7 +944,6 @@ static uint32_t stellaris_adc_read(void *opaque, target_phys_addr_t offset) stellaris_adc_state *s = (stellaris_adc_state *)opaque; /* TODO: Implement this. */ - offset -= s->base; if (offset >= 0x40 && offset < 0xc0) { int n; n = (offset - 0x40) >> 5; @@ -875,7 +980,7 @@ static uint32_t stellaris_adc_read(void *opaque, target_phys_addr_t offset) case 0x30: /* SAC */ return s->sac; default: - cpu_abort(cpu_single_env, "strllaris_adc_read: Bad offset 0x%x\n", + hw_error("strllaris_adc_read: Bad offset 0x%x\n", (int)offset); return 0; } @@ -887,7 +992,6 @@ static void stellaris_adc_write(void *opaque, target_phys_addr_t offset, stellaris_adc_state *s = (stellaris_adc_state *)opaque; /* TODO: Implement this. */ - offset -= s->base; if (offset >= 0x40 && offset < 0xc0) { int n; n = (offset - 0x40) >> 5; @@ -897,7 +1001,7 @@ static void stellaris_adc_write(void *opaque, target_phys_addr_t offset, return; case 0x04: /* SSCTL */ if (value != 6) { - cpu_abort(cpu_single_env, "ADC: Unimplemented sequence %x\n", + hw_error("ADC: Unimplemented sequence %x\n", value); } s->ssctl[n] = value; @@ -909,10 +1013,6 @@ static void stellaris_adc_write(void *opaque, target_phys_addr_t offset, switch (offset) { case 0x00: /* ACTSS */ s->actss = value & 0xf; - if (value & 0xe) { - cpu_abort(cpu_single_env, - "Not implemented: ADC sequencers 1-3\n"); - } break; case 0x08: /* IM */ s->im = value; @@ -933,46 +1033,133 @@ static void stellaris_adc_write(void *opaque, target_phys_addr_t offset, s->sspri = value; break; case 0x28: /* PSSI */ - cpu_abort(cpu_single_env, "Not implemented: ADC sample initiate\n"); + hw_error("Not implemented: ADC sample initiate\n"); break; case 0x30: /* SAC */ s->sac = value; break; default: - cpu_abort(cpu_single_env, "stellaris_adc_write: Bad offset 0x%x\n", - (int)offset); + hw_error("stellaris_adc_write: Bad offset 0x%x\n", (int)offset); } stellaris_adc_update(s); } -static CPUReadMemoryFunc *stellaris_adc_readfn[] = { +static CPUReadMemoryFunc * const stellaris_adc_readfn[] = { stellaris_adc_read, stellaris_adc_read, stellaris_adc_read }; -static CPUWriteMemoryFunc *stellaris_adc_writefn[] = { +static CPUWriteMemoryFunc * const stellaris_adc_writefn[] = { stellaris_adc_write, stellaris_adc_write, stellaris_adc_write }; -static qemu_irq stellaris_adc_init(uint32_t base, qemu_irq irq) +static const VMStateDescription vmstate_stellaris_adc = { + .name = "stellaris_adc", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(actss, stellaris_adc_state), + VMSTATE_UINT32(ris, stellaris_adc_state), + VMSTATE_UINT32(im, stellaris_adc_state), + VMSTATE_UINT32(emux, stellaris_adc_state), + VMSTATE_UINT32(ostat, stellaris_adc_state), + VMSTATE_UINT32(ustat, stellaris_adc_state), + VMSTATE_UINT32(sspri, stellaris_adc_state), + VMSTATE_UINT32(sac, stellaris_adc_state), + VMSTATE_UINT32(fifo[0].state, stellaris_adc_state), + VMSTATE_UINT32_ARRAY(fifo[0].data, stellaris_adc_state, 16), + VMSTATE_UINT32(ssmux[0], stellaris_adc_state), + VMSTATE_UINT32(ssctl[0], stellaris_adc_state), + VMSTATE_UINT32(fifo[1].state, stellaris_adc_state), + VMSTATE_UINT32_ARRAY(fifo[1].data, stellaris_adc_state, 16), + VMSTATE_UINT32(ssmux[1], stellaris_adc_state), + VMSTATE_UINT32(ssctl[1], stellaris_adc_state), + VMSTATE_UINT32(fifo[2].state, stellaris_adc_state), + VMSTATE_UINT32_ARRAY(fifo[2].data, stellaris_adc_state, 16), + VMSTATE_UINT32(ssmux[2], stellaris_adc_state), + VMSTATE_UINT32(ssctl[2], stellaris_adc_state), + VMSTATE_UINT32(fifo[3].state, stellaris_adc_state), + VMSTATE_UINT32_ARRAY(fifo[3].data, stellaris_adc_state, 16), + VMSTATE_UINT32(ssmux[3], stellaris_adc_state), + VMSTATE_UINT32(ssctl[3], stellaris_adc_state), + VMSTATE_UINT32(noise, stellaris_adc_state), + VMSTATE_END_OF_LIST() + } +}; + +static int stellaris_adc_init(SysBusDevice *dev) { - stellaris_adc_state *s; + stellaris_adc_state *s = FROM_SYSBUS(stellaris_adc_state, dev); int iomemtype; - qemu_irq *qi; + int n; - s = (stellaris_adc_state *)qemu_mallocz(sizeof(stellaris_adc_state)); - s->base = base; - s->irq = irq; + for (n = 0; n < 4; n++) { + sysbus_init_irq(dev, &s->irq[n]); + } - iomemtype = cpu_register_io_memory(0, stellaris_adc_readfn, - stellaris_adc_writefn, s); - cpu_register_physical_memory(base, 0x00001000, iomemtype); + iomemtype = cpu_register_io_memory(stellaris_adc_readfn, + stellaris_adc_writefn, s, + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, 0x1000, iomemtype); stellaris_adc_reset(s); - qi = qemu_allocate_irqs(stellaris_adc_trigger, s, 1); - return qi[0]; + qdev_init_gpio_in(&dev->qdev, stellaris_adc_trigger, 1); + vmstate_register(&dev->qdev, -1, &vmstate_stellaris_adc, s); + return 0; +} + +/* Some boards have both an OLED controller and SD card connected to + the same SSI port, with the SD card chip select connected to a + GPIO pin. Technically the OLED chip select is connected to the SSI + Fss pin. We do not bother emulating that as both devices should + never be selected simultaneously, and our OLED controller ignores stray + 0xff commands that occur when deselecting the SD card. */ + +typedef struct { + SSISlave ssidev; + qemu_irq irq; + int current_dev; + SSIBus *bus[2]; +} stellaris_ssi_bus_state; + +static void stellaris_ssi_bus_select(void *opaque, int irq, int level) +{ + stellaris_ssi_bus_state *s = (stellaris_ssi_bus_state *)opaque; + + s->current_dev = level; +} + +static uint32_t stellaris_ssi_bus_transfer(SSISlave *dev, uint32_t val) +{ + stellaris_ssi_bus_state *s = FROM_SSI_SLAVE(stellaris_ssi_bus_state, dev); + + return ssi_transfer(s->bus[s->current_dev], val); +} + +static const VMStateDescription vmstate_stellaris_ssi_bus = { + .name = "stellaris_ssi_bus", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32(current_dev, stellaris_ssi_bus_state), + VMSTATE_END_OF_LIST() + } +}; + +static int stellaris_ssi_bus_init(SSISlave *dev) +{ + stellaris_ssi_bus_state *s = FROM_SSI_SLAVE(stellaris_ssi_bus_state, dev); + + s->bus[0] = ssi_create_bus(&dev->qdev, "ssi0"); + s->bus[1] = ssi_create_bus(&dev->qdev, "ssi1"); + qdev_init_gpio_in(&dev->qdev, stellaris_ssi_bus_select, 1); + + vmstate_register(&dev->qdev, -1, &vmstate_stellaris_ssi_bus, s); + return 0; } /* Board init. */ @@ -985,7 +1172,7 @@ static stellaris_board_info stellaris_boards[] = { 0x01071013, 0x3f0f01ff, 0x0000001f, - OLED_I2C + BP_OLED_I2C }, { "LM3S6965EVB", 0x10010002, @@ -995,12 +1182,12 @@ static stellaris_board_info stellaris_boards[] = { 0x030f5317, 0x0f0f87ff, 0x5000007f, - OLED_SSI + BP_OLED_SSI | BP_GAMEPAD } }; static void stellaris_init(const char *kernel_filename, const char *cpu_model, - DisplayState *ds, stellaris_board_info *board) + stellaris_board_info *board) { static const int uart_irq[] = {5, 6, 33, 34}; static const int timer_irq[] = {19, 21, 23, 35}; @@ -1010,92 +1197,174 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model, static const int gpio_irq[7] = {0, 1, 2, 3, 4, 30, 31}; qemu_irq *pic; - qemu_irq *gpio_in[5]; - qemu_irq *gpio_out[5]; + DeviceState *gpio_dev[7]; + qemu_irq gpio_in[7][8]; + qemu_irq gpio_out[7][8]; qemu_irq adc; int sram_size; int flash_size; i2c_bus *i2c; + DeviceState *dev; int i; + int j; flash_size = ((board->dc0 & 0xffff) + 1) << 1; sram_size = (board->dc0 >> 18) + 1; pic = armv7m_init(flash_size, sram_size, kernel_filename, cpu_model); if (board->dc1 & (1 << 16)) { - adc = stellaris_adc_init(0x40038000, pic[14]); + dev = sysbus_create_varargs("stellaris-adc", 0x40038000, + pic[14], pic[15], pic[16], pic[17], NULL); + adc = qdev_get_gpio_in(dev, 0); } else { adc = NULL; } for (i = 0; i < 4; i++) { if (board->dc2 & (0x10000 << i)) { - stellaris_gptm_init(0x40030000 + i * 0x1000, - pic[timer_irq[i]], adc); + dev = sysbus_create_simple("stellaris-gptm", + 0x40030000 + i * 0x1000, + pic[timer_irq[i]]); + /* TODO: This is incorrect, but we get away with it because + the ADC output is only ever pulsed. */ + qdev_connect_gpio_out(dev, 0, adc); } } - stellaris_sys_init(0x400fe000, pic[28], board); + stellaris_sys_init(0x400fe000, pic[28], board, nd_table[0].macaddr.a); for (i = 0; i < 7; i++) { if (board->dc4 & (1 << i)) { - gpio_in[i] = pl061_init(gpio_addr[i], pic[gpio_irq[i]], - &gpio_out[i]); + gpio_dev[i] = sysbus_create_simple("pl061_luminary", gpio_addr[i], + pic[gpio_irq[i]]); + for (j = 0; j < 8; j++) { + gpio_in[i][j] = qdev_get_gpio_in(gpio_dev[i], j); + gpio_out[i][j] = NULL; + } } } if (board->dc2 & (1 << 12)) { - i2c = i2c_init_bus(); - stellaris_i2c_init(0x40020000, pic[8], i2c); - if (board->oled == OLED_I2C) { - ssd0303_init(ds, i2c, 0x3d); + dev = sysbus_create_simple("stellaris-i2c", 0x40020000, pic[8]); + i2c = (i2c_bus *)qdev_get_child_bus(dev, "i2c"); + if (board->peripherals & BP_OLED_I2C) { + i2c_create_slave(i2c, "ssd0303", 0x3d); } } for (i = 0; i < 4; i++) { if (board->dc2 & (1 << i)) { - pl011_init(0x4000c000 + i * 0x1000, pic[uart_irq[i]], - serial_hds[i], PL011_LUMINARY); + sysbus_create_simple("pl011_luminary", 0x4000c000 + i * 0x1000, + pic[uart_irq[i]]); } } if (board->dc2 & (1 << 4)) { - if (board->oled == OLED_SSI) { - void * oled; - /* FIXME: Implement chip select for OLED/MMC. */ - oled = ssd0323_init(ds, &gpio_out[2][7]); - pl022_init(0x40008000, pic[7], ssd0323_xfer_ssi, oled); - } else { - pl022_init(0x40008000, pic[7], NULL, NULL); + dev = sysbus_create_simple("pl022", 0x40008000, pic[7]); + if (board->peripherals & BP_OLED_SSI) { + DeviceState *mux; + void *bus; + + bus = qdev_get_child_bus(dev, "ssi"); + mux = ssi_create_slave(bus, "evb6965-ssi"); + gpio_out[GPIO_D][0] = qdev_get_gpio_in(mux, 0); + + bus = qdev_get_child_bus(mux, "ssi0"); + ssi_create_slave(bus, "ssi-sd"); + + bus = qdev_get_child_bus(mux, "ssi1"); + dev = ssi_create_slave(bus, "ssd0323"); + gpio_out[GPIO_C][7] = qdev_get_gpio_in(dev, 0); + + /* Make sure the select pin is high. */ + qemu_irq_raise(gpio_out[GPIO_D][0]); + } + } + if (board->dc4 & (1 << 28)) { + DeviceState *enet; + + qemu_check_nic_model(&nd_table[0], "stellaris"); + + enet = qdev_create(NULL, "stellaris_enet"); + qdev_set_nic_properties(enet, &nd_table[0]); + qdev_init_nofail(enet); + sysbus_mmio_map(sysbus_from_qdev(enet), 0, 0x40048000); + sysbus_connect_irq(sysbus_from_qdev(enet), 0, pic[42]); + } + if (board->peripherals & BP_GAMEPAD) { + qemu_irq gpad_irq[5]; + static const int gpad_keycode[5] = { 0xc8, 0xd0, 0xcb, 0xcd, 0x1d }; + + gpad_irq[0] = qemu_irq_invert(gpio_in[GPIO_E][0]); /* up */ + gpad_irq[1] = qemu_irq_invert(gpio_in[GPIO_E][1]); /* down */ + gpad_irq[2] = qemu_irq_invert(gpio_in[GPIO_E][2]); /* left */ + gpad_irq[3] = qemu_irq_invert(gpio_in[GPIO_E][3]); /* right */ + gpad_irq[4] = qemu_irq_invert(gpio_in[GPIO_F][1]); /* select */ + + stellaris_gamepad_init(5, gpad_irq, gpad_keycode); + } + for (i = 0; i < 7; i++) { + if (board->dc4 & (1 << i)) { + for (j = 0; j < 8; j++) { + if (gpio_out[i][j]) { + qdev_connect_gpio_out(gpio_dev[i], j, gpio_out[i][j]); + } + } } } } /* FIXME: Figure out how to generate these from stellaris_boards. */ -static void lm3s811evb_init(int ram_size, int vga_ram_size, - const char *boot_device, DisplayState *ds, - const char **fd_filename, int snapshot, +static void lm3s811evb_init(ram_addr_t ram_size, + const char *boot_device, const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, const char *cpu_model) { - stellaris_init(kernel_filename, cpu_model, ds, &stellaris_boards[0]); + stellaris_init(kernel_filename, cpu_model, &stellaris_boards[0]); } -static void lm3s6965evb_init(int ram_size, int vga_ram_size, - const char *boot_device, DisplayState *ds, - const char **fd_filename, int snapshot, +static void lm3s6965evb_init(ram_addr_t ram_size, + const char *boot_device, const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, const char *cpu_model) { - stellaris_init(kernel_filename, cpu_model, ds, &stellaris_boards[1]); + stellaris_init(kernel_filename, cpu_model, &stellaris_boards[1]); } -QEMUMachine lm3s811evb_machine = { - "lm3s811evb", - "Stellaris LM3S811EVB", - lm3s811evb_init, +static QEMUMachine lm3s811evb_machine = { + .name = "lm3s811evb", + .desc = "Stellaris LM3S811EVB", + .init = lm3s811evb_init, +}; + +static QEMUMachine lm3s6965evb_machine = { + .name = "lm3s6965evb", + .desc = "Stellaris LM3S6965EVB", + .init = lm3s6965evb_init, }; -QEMUMachine lm3s6965evb_machine = { - "lm3s6965evb", - "Stellaris LM3S6965EVB", - lm3s6965evb_init, +static void stellaris_machine_init(void) +{ + qemu_register_machine(&lm3s811evb_machine); + qemu_register_machine(&lm3s6965evb_machine); +} + +machine_init(stellaris_machine_init); + +static SSISlaveInfo stellaris_ssi_bus_info = { + .qdev.name = "evb6965-ssi", + .qdev.size = sizeof(stellaris_ssi_bus_state), + .init = stellaris_ssi_bus_init, + .transfer = stellaris_ssi_bus_transfer }; + +static void stellaris_register_devices(void) +{ + sysbus_register_dev("stellaris-i2c", sizeof(stellaris_i2c_state), + stellaris_i2c_init); + sysbus_register_dev("stellaris-gptm", sizeof(gptm_state), + stellaris_gptm_init); + sysbus_register_dev("stellaris-adc", sizeof(stellaris_adc_state), + stellaris_adc_init); + ssi_register_slave(&stellaris_ssi_bus_info); +} + +device_init(stellaris_register_devices)