[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH 2/3] Implement basic part of SA-1110/SA-1100
From: |
Aurelien Jarno |
Subject: |
Re: [Qemu-devel] [PATCH 2/3] Implement basic part of SA-1110/SA-1100 |
Date: |
Sun, 10 Apr 2011 13:40:43 +0200 |
User-agent: |
Mutt/1.5.20 (2009-06-14) |
On Mon, Apr 04, 2011 at 05:38:45PM +0400, Dmitry Eremin-Solenikov wrote:
> Basic implementation of DEC/Intel SA-1100/SA-1110 chips emulation.
> Implemented:
> - IRQs
> - GPIO
> - PPC
> - RTC
> - UARTs (no IrDA/etc.)
> - OST reused from pxa25x
>
> Everything else is TODO (esp. PM/idle/sleep!) - see the todo in the
> hw/strongarm.c
>
> V3:
> * fix the name of UART VMSD
> * fix RTSR reg offset
> * add SSP support
>
> V2:
> * removed all strongarm variants except latest
> * dropped unused casts
> * fixed PIC vmstate
> * fixed new devices created with version_id = 1
>
> Signed-off-by: Dmitry Eremin-Solenikov <address@hidden>
> ---
> Makefile.target | 1 +
> hw/strongarm.c | 1539
> +++++++++++++++++++++++++++++++++++++++++++++++++++
> hw/strongarm.h | 64 +++
> target-arm/cpu.h | 3 +
> target-arm/helper.c | 9 +
> 5 files changed, 1616 insertions(+), 0 deletions(-)
> create mode 100644 hw/strongarm.c
> create mode 100644 hw/strongarm.h
>
> diff --git a/Makefile.target b/Makefile.target
> index 2f76714..cd03cec 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -352,6 +352,7 @@ obj-arm-y += syborg.o syborg_fb.o syborg_interrupt.o
> syborg_keyboard.o
> obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o
> obj-arm-y += syborg_virtio.o
> obj-arm-y += vexpress.o
> +obj-arm-y += strongarm.o
>
> obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
> obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o
> diff --git a/hw/strongarm.c b/hw/strongarm.c
> new file mode 100644
> index 0000000..90b9ad0
> --- /dev/null
> +++ b/hw/strongarm.c
> @@ -0,0 +1,1539 @@
> +/*
> + * StrongARM SA-1100/SA-1110 emulation
> + *
> + * Copyright (C) 2011 Dmitry Eremin-Solenikov
> + *
> + * Largely based on StrongARM emulation:
> + * Copyright (c) 2006 Openedhand Ltd.
> + * Written by Andrzej Zaborowski <address@hidden>
> + *
> + * UART code based on QEMU 16550A UART emulation
> + * Copyright (c) 2003-2004 Fabrice Bellard
> + * Copyright (c) 2008 Citrix Systems, Inc.
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2. See
> + * the COPYING file in the top-level directory.
> + */
> +#include "sysbus.h"
> +#include "strongarm.h"
> +#include "qemu-error.h"
> +#include "arm-misc.h"
> +#include "sysemu.h"
> +#include "ssi.h"
> +
> +/*
> + TODO
> + - Implement cp15, c14 ?
> + - Implement cp15, c15 !!! (idle used in L)
> + - Implement idle mode handling/DIM
> + - Implement sleep mode/Wake sources
> + - Implement reset control
> + - Implement memory control regs
> + - PCMCIA handling
> + - Maybe support MBGNT/MBREQ
> + - DMA channels
> + - GPCLK
> + - IrDA
> + - MCP
> + - Enhance UART with modem signals
> + */
> +
> +static struct {
> + target_phys_addr_t io_base;
> + int irq;
> +} sa_serial[] = {
> + { 0x80010000, SA_PIC_UART1 },
> + { 0x80030000, SA_PIC_UART2 },
> + { 0x80050000, SA_PIC_UART3 },
> + { 0, 0 }
> +};
> +
> +/* Interrupt Controller */
> +typedef struct {
> + SysBusDevice busdev;
> + qemu_irq irq;
> + qemu_irq fiq;
> +
> + uint32_t pending;
> + uint32_t enabled;
> + uint32_t is_fiq;
> + uint32_t int_idle;
> +} StrongARMPICState;
> +
> +#define ICIP 0x00
> +#define ICMR 0x04
> +#define ICLR 0x08
> +#define ICFP 0x10
> +#define ICPR 0x20
> +#define ICCR 0x0c
> +
> +#define SA_PIC_SRCS 32
> +
> +
> +static void strongarm_pic_update(void *opaque)
> +{
> + StrongARMPICState *s = opaque;
> +
> + /* FIXME: reflect DIM */
> + qemu_set_irq(s->fiq, s->pending & s->enabled & s->is_fiq);
> + qemu_set_irq(s->irq, s->pending & s->enabled & ~s->is_fiq);
> +}
> +
> +static void strongarm_pic_set_irq(void *opaque, int irq, int level)
> +{
> + StrongARMPICState *s = opaque;
> +
> + if (level) {
> + s->pending |= 1 << irq;
> + } else {
> + s->pending &= ~(1 << irq);
> + }
> +
> + strongarm_pic_update(s);
> +}
> +
> +static uint32_t strongarm_pic_mem_read(void *opaque, target_phys_addr_t
> offset)
> +{
> + StrongARMPICState *s = opaque;
> +
> + switch (offset) {
> + case ICIP:
> + return s->pending & ~s->is_fiq & s->enabled;
> + case ICMR:
> + return s->enabled;
> + case ICLR:
> + return s->is_fiq;
> + case ICCR:
> + return s->int_idle == 0;
> + case ICFP:
> + return s->pending & s->is_fiq & s->enabled;
> + case ICPR:
> + return s->pending;
> + default:
> + printf("%s: Bad register offset 0x" TARGET_FMT_plx "\n",
> + __func__, offset);
> + return 0;
> + }
> +}
> +
> +static void strongarm_pic_mem_write(void *opaque, target_phys_addr_t offset,
> + uint32_t value)
> +{
> + StrongARMPICState *s = opaque;
> +
> + switch (offset) {
> + case ICMR:
> + s->enabled = value;
> + break;
> + case ICLR:
> + s->is_fiq = value;
> + break;
> + case ICCR:
> + s->int_idle = (value & 1) ? 0 : ~0;
> + break;
> + default:
> + printf("%s: Bad register offset 0x" TARGET_FMT_plx "\n",
> + __func__, offset);
> + break;
> + }
> + strongarm_pic_update(s);
> +}
> +
> +static CPUReadMemoryFunc * const strongarm_pic_readfn[] = {
> + strongarm_pic_mem_read,
> + strongarm_pic_mem_read,
> + strongarm_pic_mem_read,
> +};
> +
> +static CPUWriteMemoryFunc * const strongarm_pic_writefn[] = {
> + strongarm_pic_mem_write,
> + strongarm_pic_mem_write,
> + strongarm_pic_mem_write,
> +};
> +
> +static int strongarm_pic_initfn(SysBusDevice *dev)
> +{
> + StrongARMPICState *s = FROM_SYSBUS(StrongARMPICState, dev);
> + int iomemtype;
> +
> + qdev_init_gpio_in(&dev->qdev, strongarm_pic_set_irq, SA_PIC_SRCS);
> + iomemtype = cpu_register_io_memory(strongarm_pic_readfn,
> + strongarm_pic_writefn, s, DEVICE_NATIVE_ENDIAN);
> + sysbus_init_mmio(dev, 0x1000, iomemtype);
> + sysbus_init_irq(dev, &s->irq);
> + sysbus_init_irq(dev, &s->fiq);
> +
> + return 0;
> +}
> +
> +static int strongarm_pic_post_load(void *opaque, int version_id)
> +{
> + strongarm_pic_update(opaque);
> + return 0;
> +}
> +
> +static VMStateDescription vmstate_strongarm_pic_regs = {
> + .name = "strongarm_pic",
> + .version_id = 0,
> + .minimum_version_id = 0,
> + .minimum_version_id_old = 0,
> + .post_load = strongarm_pic_post_load,
> + .fields = (VMStateField[]) {
> + VMSTATE_UINT32(pending, StrongARMPICState),
> + VMSTATE_UINT32(enabled, StrongARMPICState),
> + VMSTATE_UINT32(is_fiq, StrongARMPICState),
> + VMSTATE_UINT32(int_idle, StrongARMPICState),
> + VMSTATE_END_OF_LIST(),
> + },
> +};
> +
> +static SysBusDeviceInfo strongarm_pic_info = {
> + .init = strongarm_pic_initfn,
> + .qdev.name = "strongarm_pic",
> + .qdev.desc = "StrongARM PIC",
> + .qdev.size = sizeof(StrongARMPICState),
> + .qdev.vmsd = &vmstate_strongarm_pic_regs,
> +};
> +
> +/* Real-Time Clock */
> +#define RTAR 0x00 /* RTC Alarm register */
> +#define RCNR 0x04 /* RTC Counter register */
> +#define RTTR 0x08 /* RTC Timer Trim register */
> +#define RTSR 0x10 /* RTC Status register */
> +
> +typedef struct {
> + SysBusDevice busdev;
> + uint32_t rttr;
> + uint32_t rtsr;
> + uint32_t rtar;
> + uint32_t last_rcnr;
> + int64_t last_hz;
> + QEMUTimer *rtc_alarm;
> + QEMUTimer *rtc_hz;
> + qemu_irq rtc_irq;
> + qemu_irq rtc_hz_irq;
> +} StrongARMRTCState;
> +
> +static inline void strongarm_rtc_int_update(StrongARMRTCState *s)
> +{
> + qemu_set_irq(s->rtc_irq, s->rtsr & (1 << 0));
> + qemu_set_irq(s->rtc_hz_irq, s->rtsr & (1 << 1));
> +}
> +
> +static void strongarm_rtc_hzupdate(StrongARMRTCState *s)
> +{
> + int64_t rt = qemu_get_clock_ms(rt_clock);
> + s->last_rcnr += ((rt - s->last_hz) << 15) /
> + (1000 * ((s->rttr & 0xffff) + 1));
> + s->last_hz = rt;
> +}
> +
> +static inline void strongarm_rtc_timer_update(StrongARMRTCState *s,
> + uint32_t rtsr)
> +{
> + if ((rtsr & (1 << 4)) && !(rtsr & (1 << 1))) {
> + qemu_mod_timer(s->rtc_hz, s->last_hz +
> + (((s->rtar - s->last_rcnr) * 1000 *
> + ((s->rttr & 0xffff) + 1)) >> 15));
This part (and other similar parts in the code) is not very clear unless
you have the documentation when reading the code. It would be better to
use #define to explain what the role of the various rtsr bits. Maybe the
same could be done for rttr with a mask.
> + } else {
> + qemu_del_timer(s->rtc_hz);
> + }
> +
> + if ((rtsr & (1 << 2)) && !(rtsr & (1 << 0))) {
> + qemu_mod_timer(s->rtc_alarm, s->last_hz +
> + (((s->rtar - s->last_rcnr) * 1000 *
> + ((s->rttr & 0xffff) + 1)) >> 15));
> + } else {
> + qemu_del_timer(s->rtc_alarm);
> + }
> +}
> +
> +static inline void strongarm_rtc_alarm_tick(void *opaque)
> +{
> + StrongARMRTCState *s = (StrongARMRTCState *) opaque;
> + s->rtsr |= (1 << 0);
> + strongarm_rtc_timer_update(s, s->rtsr);
> + strongarm_rtc_int_update(s);
> +}
> +
> +static inline void strongarm_rtc_hz_tick(void *opaque)
> +{
> + StrongARMRTCState *s = (StrongARMRTCState *) opaque;
> + s->rtsr |= (1 << 1);
> + strongarm_rtc_timer_update(s, s->rtsr);
> + strongarm_rtc_int_update(s);
> +}
> +
> +static uint32_t strongarm_rtc_read(void *opaque, target_phys_addr_t addr)
> +{
> + StrongARMRTCState *s = (StrongARMRTCState *) opaque;
You don't need a cast there.
> +
> + switch (addr) {
> + case RTTR:
> + return s->rttr;
> + case RTSR:
> + return s->rtsr;
> + case RTAR:
> + return s->rtar;
> + case RCNR:
> + return s->last_rcnr +
> + ((qemu_get_clock_ms(rt_clock) - s->last_hz) << 15) /
> + (1000 * ((s->rttr & 0xffff) + 1));
> + default:
> + printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
> + return 0;
> + }
> +}
> +
> +static void strongarm_rtc_write(void *opaque, target_phys_addr_t addr,
> + uint32_t value)
> +{
> + StrongARMRTCState *s = (StrongARMRTCState *) opaque;
Ditto
> +
> + switch (addr) {
> + case RTTR:
> + strongarm_rtc_hzupdate(s);
> + s->rttr = value;
> + strongarm_rtc_timer_update(s, s->rtsr);
> + break;
> +
> + case RTSR:
> + if (((s->rtsr ^ value) & 0xc) | (value & ~0xc)) {
> + strongarm_rtc_timer_update(s, value);
> + }
> +
> + s->rtsr = (value & 0xc) | (s->rtsr & ~(value & ~0xc));
Same as previous, the code would be easier to read when knowing what is
0xc.
> + strongarm_rtc_int_update(s);
> + break;
> +
> + case RTAR:
> + s->rtar = value;
> + strongarm_rtc_timer_update(s, s->rtsr);
> + break;
> +
> + case RCNR:
> + strongarm_rtc_hzupdate(s);
> + s->last_rcnr = value;
> + strongarm_rtc_timer_update(s, s->rtsr);
> + break;
> +
> + default:
> + printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
> + }
> +}
> +
> +static CPUReadMemoryFunc * const strongarm_rtc_readfn[] = {
> + strongarm_rtc_read,
> + strongarm_rtc_read,
> + strongarm_rtc_read,
> +};
> +
> +static CPUWriteMemoryFunc * const strongarm_rtc_writefn[] = {
> + strongarm_rtc_write,
> + strongarm_rtc_write,
> + strongarm_rtc_write,
> +};
> +
> +static int strongarm_rtc_init(SysBusDevice *dev)
> +{
> + StrongARMRTCState *s = FROM_SYSBUS(StrongARMRTCState, dev);
> + struct tm tm;
> + int iomemtype;
> +
> + s->rttr = 0x0;
> + s->rtsr = 0;
> +
> + qemu_get_timedate(&tm, 0);
> +
> + s->last_rcnr = (uint32_t) mktimegm(&tm);
> + s->last_hz = qemu_get_clock_ms(rt_clock);
> +
> + s->rtc_alarm = qemu_new_timer_ms(rt_clock, strongarm_rtc_alarm_tick, s);
> + s->rtc_hz = qemu_new_timer_ms(rt_clock, strongarm_rtc_hz_tick, s);
> +
> + sysbus_init_irq(dev, &s->rtc_irq);
> + sysbus_init_irq(dev, &s->rtc_hz_irq);
> +
> + iomemtype = cpu_register_io_memory(strongarm_rtc_readfn,
> + strongarm_rtc_writefn, s, DEVICE_NATIVE_ENDIAN);
> + sysbus_init_mmio(dev, 0x10000, iomemtype);
> +
> + return 0;
> +}
> +
> +static void strongarm_rtc_pre_save(void *opaque)
> +{
> + StrongARMRTCState *s = (StrongARMRTCState *) opaque;
You don't need the cast here.
> +
> + strongarm_rtc_hzupdate(s);
> +}
> +
> +static int strongarm_rtc_post_load(void *opaque, int version_id)
> +{
> + StrongARMRTCState *s = (StrongARMRTCState *) opaque;
Ditto.
> + strongarm_rtc_timer_update(s, s->rtsr);
> +
> + return 0;
> +}
> +
> +static const VMStateDescription vmstate_strongarm_rtc_regs = {
> + .name = "strongarm-rtc",
> + .version_id = 0,
> + .minimum_version_id = 0,
> + .minimum_version_id_old = 0,
> + .pre_save = strongarm_rtc_pre_save,
> + .post_load = strongarm_rtc_post_load,
> + .fields = (VMStateField[]) {
> + VMSTATE_UINT32(rttr, StrongARMRTCState),
> + VMSTATE_UINT32(rtsr, StrongARMRTCState),
> + VMSTATE_UINT32(rtar, StrongARMRTCState),
> + VMSTATE_UINT32(last_rcnr, StrongARMRTCState),
> + VMSTATE_INT64(last_hz, StrongARMRTCState),
> + VMSTATE_END_OF_LIST(),
> + },
> +};
> +
> +static SysBusDeviceInfo strongarm_rtc_sysbus_info = {
> + .init = strongarm_rtc_init,
> + .qdev.name = "strongarm-rtc",
> + .qdev.desc = "StrongARM RTC Controller",
> + .qdev.size = sizeof(StrongARMRTCState),
> + .qdev.vmsd = &vmstate_strongarm_rtc_regs,
> +};
> +
> +/* GPIO */
> +#define GPLR 0x00
> +#define GPDR 0x04
> +#define GPSR 0x08
> +#define GPCR 0x0c
> +#define GRER 0x10
> +#define GFER 0x14
> +#define GEDR 0x18
> +#define GAFR 0x1c
> +
> +typedef struct StrongARMGPIOInfo StrongARMGPIOInfo;
> +struct StrongARMGPIOInfo {
> + SysBusDevice busdev;
> + qemu_irq handler[28];
> + qemu_irq irqs[11];
> + qemu_irq irqX;
> +
> + uint32_t ilevel;
> + uint32_t olevel;
> + uint32_t dir;
> + uint32_t rising;
> + uint32_t falling;
> + uint32_t status;
> + uint32_t gpsr;
> + uint32_t gafr;
> +
> + uint32_t prev_level;
> +};
> +
> +
> +static void strongarm_gpio_irq_update(StrongARMGPIOInfo *s)
> +{
> + int i;
> + for (i = 0; i < 11; i++) {
> + qemu_set_irq(s->irqs[i], s->status & (1 << i));
> + }
> +
> + qemu_set_irq(s->irqX, (s->status & ~0x7ff));
> +}
> +
> +static void strongarm_gpio_set(void *opaque, int line, int level)
> +{
> + StrongARMGPIOInfo *s = (StrongARMGPIOInfo *) opaque;
> + uint32_t mask;
> +
> + mask = 1 << line;
> +
> + if (level) {
> + s->status |= s->rising & mask &
> + ~s->ilevel & ~s->dir;
> + s->ilevel |= mask;
> + } else {
> + s->status |= s->falling & mask &
> + s->ilevel & ~s->dir;
> + s->ilevel &= ~mask;
> + }
> +
> + if (s->status & mask) {
> + strongarm_gpio_irq_update(s);
> + }
> +}
> +
> +static void strongarm_gpio_handler_update(StrongARMGPIOInfo *s)
> +{
> + uint32_t level, diff;
> + int bit;
> +
> + level = s->olevel & s->dir;
> +
> + for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) {
> + bit = ffs(diff) - 1;
> + qemu_set_irq(s->handler[bit], (level >> bit) & 1);
> + }
> +
> + s->prev_level = level;
> +}
> +
> +static uint32_t strongarm_gpio_read(void *opaque, target_phys_addr_t offset)
> +{
> + StrongARMGPIOInfo *s = (StrongARMGPIOInfo *) opaque;
> +
> + switch (offset) {
> + case GPDR: /* GPIO Pin-Direction registers */
> + return s->dir;
> +
> + case GPSR: /* GPIO Pin-Output Set registers */
> + printf("%s: Read from a write-only register 0x" TARGET_FMT_plx "\n",
> + __func__, offset);
> + return s->gpsr; /* Return last written value. */
> +
> + case GPCR: /* GPIO Pin-Output Clear registers */
> + printf("%s: Read from a write-only register 0x" TARGET_FMT_plx "\n",
> + __func__, offset);
> + return 31337; /* Specified as unpredictable in the docs. */
Do we really want all those printf() there? For debug maybe, but
probably not by default.
> +
> + case GRER: /* GPIO Rising-Edge Detect Enable registers */
> + return s->rising;
> +
> + case GFER: /* GPIO Falling-Edge Detect Enable registers */
> + return s->falling;
> +
> + case GAFR: /* GPIO Alternate Function registers */
> + return s->gafr;
> +
> + case GPLR: /* GPIO Pin-Level registers */
> + return (s->olevel & s->dir) |
> + (s->ilevel & ~s->dir);
> +
> + case GEDR: /* GPIO Edge Detect Status registers */
> + return s->status;
> +
> + default:
> + printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
> + }
> +
> + return 0;
> +}
> +
> +static void strongarm_gpio_write(void *opaque,
> + target_phys_addr_t offset, uint32_t value)
> +{
> + StrongARMGPIOInfo *s = (StrongARMGPIOInfo *) opaque;
> +
> + switch (offset) {
> + case GPDR: /* GPIO Pin-Direction registers */
> + s->dir = value;
> + strongarm_gpio_handler_update(s);
> + break;
> +
> + case GPSR: /* GPIO Pin-Output Set registers */
> + s->olevel |= value;
> + strongarm_gpio_handler_update(s);
> + s->gpsr = value;
> + break;
> +
> + case GPCR: /* GPIO Pin-Output Clear registers */
> + s->olevel &= ~value;
> + strongarm_gpio_handler_update(s);
> + break;
> +
> + case GRER: /* GPIO Rising-Edge Detect Enable registers */
> + s->rising = value;
> + break;
> +
> + case GFER: /* GPIO Falling-Edge Detect Enable registers */
> + s->falling = value;
> + break;
> +
> + case GAFR: /* GPIO Alternate Function registers */
> + s->gafr = value;
> + break;
> +
> + case GEDR: /* GPIO Edge Detect Status registers */
> + s->status &= ~value;
> + strongarm_gpio_irq_update(s);
> + break;
> +
> + default:
> + printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
> + }
> +}
> +
> +static CPUReadMemoryFunc * const strongarm_gpio_readfn[] = {
> + strongarm_gpio_read,
> + strongarm_gpio_read,
> + strongarm_gpio_read
> +};
> +
> +static CPUWriteMemoryFunc * const strongarm_gpio_writefn[] = {
> + strongarm_gpio_write,
> + strongarm_gpio_write,
> + strongarm_gpio_write
> +};
> +
> +static DeviceState *strongarm_gpio_init(target_phys_addr_t base,
> + DeviceState *pic)
> +{
> + DeviceState *dev;
> + int i;
> +
> + dev = qdev_create(NULL, "strongarm-gpio");
> + qdev_init_nofail(dev);
> +
> + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
> + for (i = 0; i < 12; i++)
> + sysbus_connect_irq(sysbus_from_qdev(dev), i,
> + qdev_get_gpio_in(pic, SA_PIC_GPIO0_EDGE + i));
> +
> + return dev;
> +}
> +
> +static int strongarm_gpio_initfn(SysBusDevice *dev)
> +{
> + int iomemtype;
> + StrongARMGPIOInfo *s;
> + int i;
> +
> + s = FROM_SYSBUS(StrongARMGPIOInfo, dev);
> +
> + qdev_init_gpio_in(&dev->qdev, strongarm_gpio_set, 28);
> + qdev_init_gpio_out(&dev->qdev, s->handler, 28);
> +
> + iomemtype = cpu_register_io_memory(strongarm_gpio_readfn,
> + strongarm_gpio_writefn, s, DEVICE_NATIVE_ENDIAN);
> +
> + sysbus_init_mmio(dev, 0x1000, iomemtype);
> + for (i = 0; i < 11; i++) {
> + sysbus_init_irq(dev, &s->irqs[i]);
> + }
> + sysbus_init_irq(dev, &s->irqX);
> +
> + return 0;
> +}
> +
> +static const VMStateDescription vmstate_strongarm_gpio_regs = {
> + .name = "strongarm-gpio",
> + .version_id = 0,
> + .minimum_version_id = 0,
> + .minimum_version_id_old = 0,
> + .fields = (VMStateField[]) {
> + VMSTATE_UINT32(ilevel, StrongARMGPIOInfo),
> + VMSTATE_UINT32(olevel, StrongARMGPIOInfo),
> + VMSTATE_UINT32(dir, StrongARMGPIOInfo),
> + VMSTATE_UINT32(rising, StrongARMGPIOInfo),
> + VMSTATE_UINT32(falling, StrongARMGPIOInfo),
> + VMSTATE_UINT32(status, StrongARMGPIOInfo),
> + VMSTATE_UINT32(gafr, StrongARMGPIOInfo),
> + VMSTATE_END_OF_LIST(),
> + },
> +};
> +
> +static SysBusDeviceInfo strongarm_gpio_info = {
> + .init = strongarm_gpio_initfn,
> + .qdev.name = "strongarm-gpio",
> + .qdev.desc = "StrongARM GPIO controller",
> + .qdev.size = sizeof(StrongARMGPIOInfo),
> +};
> +
> +/* Peripheral Pin Controller */
> +#define PPDR 0x00
> +#define PPSR 0x04
> +#define PPAR 0x08
> +#define PSDR 0x0c
> +#define PPFR 0x10
> +
> +typedef struct StrongARMPPCInfo StrongARMPPCInfo;
> +struct StrongARMPPCInfo {
> + SysBusDevice busdev;
> + qemu_irq handler[28];
> +
> + uint32_t ilevel;
> + uint32_t olevel;
> + uint32_t dir;
> + uint32_t ppar;
> + uint32_t psdr;
> + uint32_t ppfr;
> +
> + uint32_t prev_level;
> +};
> +
> +static void strongarm_ppc_set(void *opaque, int line, int level)
> +{
> + StrongARMPPCInfo *s = (StrongARMPPCInfo *) opaque;
> +
> + if (level) {
> + s->ilevel |= 1 << line;
> + } else {
> + s->ilevel &= ~(1 << line);
> + }
> +}
> +
> +static void strongarm_ppc_handler_update(StrongARMPPCInfo *s)
> +{
> + uint32_t level, diff;
> + int bit;
> +
> + level = s->olevel & s->dir;
> +
> + for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) {
> + bit = ffs(diff) - 1;
> + qemu_set_irq(s->handler[bit], (level >> bit) & 1);
> + }
> +
> + s->prev_level = level;
> +}
> +
> +static uint32_t strongarm_ppc_read(void *opaque, target_phys_addr_t offset)
> +{
> + StrongARMPPCInfo *s = (StrongARMPPCInfo *) opaque;
> +
> + switch (offset) {
> + case PPDR: /* PPC Pin Direction registers */
> + return s->dir | ~0x3fffff;
> +
> + case PPSR: /* PPC Pin State registers */
> + return (s->olevel & s->dir) |
> + (s->ilevel & ~s->dir) |
> + ~0x3fffff;
> +
> + case PPAR:
> + return s->ppar | ~0x41000;
> +
> + case PSDR:
> + return s->psdr;
> +
> + case PPFR:
> + return s->ppfr | ~0x7f001;
> +
> + default:
> + printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
> + }
> +
> + return 0;
> +}
> +
> +static void strongarm_ppc_write(void *opaque,
> + target_phys_addr_t offset, uint32_t value)
> +{
> + StrongARMPPCInfo *s = (StrongARMPPCInfo *) opaque;
> +
> + switch (offset) {
> + case PPDR: /* PPC Pin Direction registers */
> + s->dir = value & 0x3fffff;
> + strongarm_ppc_handler_update(s);
> + break;
> +
> + case PPSR: /* PPC Pin State registers */
> + s->olevel = value & s->dir & 0x3fffff;
> + strongarm_ppc_handler_update(s);
> + break;
> +
> + case PPAR:
> + s->ppar = value & 0x41000;
> + break;
> +
> + case PSDR:
> + s->psdr = value & 0x3fffff;
> + break;
> +
> + case PPFR:
> + s->ppfr = value & 0x7f001;
> + break;
> +
> + default:
> + printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
> + }
> +}
> +
> +static CPUReadMemoryFunc * const strongarm_ppc_readfn[] = {
> + strongarm_ppc_read,
> + strongarm_ppc_read,
> + strongarm_ppc_read
> +};
> +
> +static CPUWriteMemoryFunc * const strongarm_ppc_writefn[] = {
> + strongarm_ppc_write,
> + strongarm_ppc_write,
> + strongarm_ppc_write
> +};
> +
> +static int strongarm_ppc_init(SysBusDevice *dev)
> +{
> + int iomemtype;
> + StrongARMPPCInfo *s;
> +
> + s = FROM_SYSBUS(StrongARMPPCInfo, dev);
> +
> + qdev_init_gpio_in(&dev->qdev, strongarm_ppc_set, 22);
> + qdev_init_gpio_out(&dev->qdev, s->handler, 22);
> +
> + iomemtype = cpu_register_io_memory(strongarm_ppc_readfn,
> + strongarm_ppc_writefn, s, DEVICE_NATIVE_ENDIAN);
> +
> + sysbus_init_mmio(dev, 0x1000, iomemtype);
> +
> + return 0;
> +}
> +
> +static const VMStateDescription vmstate_strongarm_ppc_regs = {
> + .name = "strongarm-ppc",
> + .version_id = 0,
> + .minimum_version_id = 0,
> + .minimum_version_id_old = 0,
> + .fields = (VMStateField[]) {
> + VMSTATE_UINT32(ilevel, StrongARMPPCInfo),
> + VMSTATE_UINT32(olevel, StrongARMPPCInfo),
> + VMSTATE_UINT32(dir, StrongARMPPCInfo),
> + VMSTATE_UINT32(ppar, StrongARMPPCInfo),
> + VMSTATE_UINT32(psdr, StrongARMPPCInfo),
> + VMSTATE_UINT32(ppfr, StrongARMPPCInfo),
> + VMSTATE_END_OF_LIST(),
> + },
> +};
> +
> +static SysBusDeviceInfo strongarm_ppc_info = {
> + .init = strongarm_ppc_init,
> + .qdev.name = "strongarm-ppc",
> + .qdev.desc = "StrongARM PPC controller",
> + .qdev.size = sizeof(StrongARMPPCInfo),
> +};
> +
> +/* UART Ports */
> +#define UTCR0 0x00
> +#define UTCR1 0x04
> +#define UTCR2 0x08
> +#define UTCR3 0x0c
> +#define UTDR 0x14
> +#define UTSR0 0x1c
> +#define UTSR1 0x20
> +
> +#define RX_FIFO_PRE (1 << 8)
> +#define RX_FIFO_FRE (1 << 9)
> +#define RX_FIFO_ROR (1 << 10)
> +
> +typedef struct {
> + SysBusDevice busdev;
> + CharDriverState *chr;
> + qemu_irq irq;
> +
> + uint8_t utcr0;
> + uint16_t brd;
> + uint8_t utcr3;
> + uint8_t utsr0;
> + uint8_t utsr1;
> +
> + uint8_t tx_fifo[8];
> + uint8_t tx_start;
> + uint8_t tx_len;
> + uint16_t rx_fifo[12]; /* value + error flags in high bits */
> + uint8_t rx_start;
> + uint8_t rx_len;
> +
> + uint64_t char_transmit_time; /* time to transmit a char in ticks*/
> + bool wait_break_end;
> + QEMUTimer *rx_timeout_timer;
> + QEMUTimer *tx_timer;
> +} StrongARMUARTState;
> +
> +static void strongarm_uart_update_status(StrongARMUARTState *s)
> +{
> + uint16_t utsr1 = 0;
> +
> + if (s->tx_len != 8) {
> + utsr1 |= (1 << 2);
What bit is (1 << 2)? Please use #define to define the bits. Same for
most of the code below.
> + }
> +
> + if (s->rx_len != 0) {
> + uint16_t ent = s->rx_fifo[s->rx_start];
> +
> + utsr1 |= (1 << 1);
> + if (ent & RX_FIFO_PRE) {
> + s->utsr1 |= (1 << 3);
> + }
> + if (ent & RX_FIFO_FRE) {
> + s->utsr1 |= (1 << 4);
> + }
> + if (ent & RX_FIFO_ROR) {
> + s->utsr1 |= (1 << 5);
> + }
> + }
> +
> + s->utsr1 = utsr1;
> +}
> +
> +static void strongarm_uart_update_int_status(StrongARMUARTState *s)
> +{
> + uint16_t utsr0 = s->utsr0 & 0x1c;
> + int i;
> +
> + if ((s->utcr3 & (1 << 1)) &&
> + (s->utcr3 & (1 << 4)) &&
> + s->tx_len <= 4) {
> + utsr0 |= (1 << 0);
> + }
> +
> + if ((s->utcr3 & (1 << 0)) &&
> + (s->utcr3 & (1 << 3)) &&
> + s->rx_len > 4) {
> + utsr0 |= (1 << 1);
> + }
> +
> + for (i = 0; i < s->rx_len && i < 4; i++)
> + if (s->rx_fifo[(s->rx_start + i) % 12] & ~0xff) {
> + utsr0 |= (1 << 5);
> + break;
> + }
> +
> + s->utsr0 = utsr0;
> + qemu_set_irq(s->irq, utsr0);
> +}
> +
> +static void strongarm_uart_update_parameters(StrongARMUARTState *s)
> +{
> + int speed, parity, data_bits, stop_bits, frame_size;
> + QEMUSerialSetParams ssp;
> +
> + /* Start bit. */
> + frame_size = 1;
> + if (s->utcr0 & (1 << 0)) {
> + /* Parity bit. */
> + frame_size++;
> + if (s->utcr0 & (1 << 1)) {
> + parity = 'E';
> + } else {
> + parity = 'O';
> + }
> + } else {
> + parity = 'N';
> + }
> + if (s->utcr0 & (1 << 2)) {
> + stop_bits = 2;
> + } else {
> + stop_bits = 1;
> + }
> +
> + data_bits = (s->utcr0 & (1 << 3)) ? 8 : 7;
> + frame_size += data_bits + stop_bits;
> + speed = 3686400 / 16 / (s->brd + 1);
> + ssp.speed = speed;
> + ssp.parity = parity;
> + ssp.data_bits = data_bits;
> + ssp.stop_bits = stop_bits;
> + s->char_transmit_time = (get_ticks_per_sec() / speed) * frame_size;
> + if (s->chr) {
> + qemu_chr_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
> + }
> +
> +#if 0
> + fprintf(stderr, "%s speed=%d parity=%c data=%d stop=%d\n", s->chr->label,
> + speed, parity, data_bits, stop_bits);
> +#endif
> +}
> +
> +static void strongarm_uart_rx_to(void *opaque)
> +{
> + StrongARMUARTState *s = opaque;
> +
> + if (s->rx_len) {
> + s->utsr0 |= (1 << 2);
> + strongarm_uart_update_int_status(s);
> + }
> +}
> +
> +static void strongarm_uart_rx_push(StrongARMUARTState *s, uint16_t c)
> +{
> + if ((s->utcr3 & (1 << 0)) == 0) {
> + /* rx disabled */
> + return;
> + }
> +
> + if (s->wait_break_end) {
> + s->utsr0 |= (1 << 4); /* REB */
> + s->wait_break_end = false;
> + }
> +
> + if (s->rx_len < 12) {
> + s->rx_fifo[(s->rx_start + s->rx_len) % 12] = c;
> + s->rx_len++;
> + } else
> + s->rx_fifo[(s->rx_start + 11) % 12] |= RX_FIFO_ROR;
> +}
> +
> +static int strongarm_uart_can_receive(void *opaque)
> +{
> + StrongARMUARTState *s = opaque;
> +
> + if (s->rx_len == 12) {
> + return 0;
> + }
> + /* It's best not to get more than 2/3 of RX FIFO, so advertise that much
> */
> + if (s->rx_len < 8) {
> + return 8 - s->rx_len;
> + }
> + return 1;
> +}
> +
> +static void strongarm_uart_receive(void *opaque, const uint8_t *buf, int
> size)
> +{
> + StrongARMUARTState *s = opaque;
> + int i;
> +
> + for (i = 0; i < size; i++) {
> + strongarm_uart_rx_push(s, buf[i]);
> + }
> +
> + /* call the timeout receive callback in 3 char transmit time */
> + qemu_mod_timer(s->rx_timeout_timer,
> + qemu_get_clock_ns(vm_clock) + s->char_transmit_time * 3);
> +
> + strongarm_uart_update_status(s);
> + strongarm_uart_update_int_status(s);
> +}
> +
> +static void strongarm_uart_event(void *opaque, int event)
> +{
> + StrongARMUARTState *s = opaque;
> + if (event == CHR_EVENT_BREAK) {
> + s->utcr3 |= (1 << 3);
> + strongarm_uart_rx_push(s, RX_FIFO_FRE);
> + s->wait_break_end = true;
> + strongarm_uart_update_status(s);
> + strongarm_uart_update_int_status(s);
> + }
> +}
> +
> +static void strongarm_uart_tx(void *opaque)
> +{
> + StrongARMUARTState *s = opaque;
> + uint64_t new_xmit_ts = qemu_get_clock_ns(vm_clock);
> +
> + if (s->utcr3 & (1 << 5)) /* loopback */ {
> + strongarm_uart_receive(s, &s->tx_fifo[s->tx_start], 1);
> + } else if (s->chr) {
> + qemu_chr_write(s->chr, &s->tx_fifo[s->tx_start], 1);
> + }
> +
> + s->tx_start = (s->tx_start + 1) % 8;
> + s->tx_len--;
> + if (s->tx_len) {
> + qemu_mod_timer(s->tx_timer, new_xmit_ts + s->char_transmit_time);
> + }
> + strongarm_uart_update_status(s);
> + strongarm_uart_update_int_status(s);
> +}
> +
> +static uint32_t strongarm_uart_read(void *opaque, target_phys_addr_t addr)
> +{
> + StrongARMUARTState *s = opaque;
> + uint16_t ret;
> +
> + switch (addr) {
> + case UTCR0:
> + return s->utcr0;
> +
> + case UTCR1:
> + return s->brd >> 8;
> +
> + case UTCR2:
> + return s->brd & 0xff;
> +
> + case UTCR3:
> + return s->utcr3;
> +
> + case UTDR:
> + if (s->rx_len != 0) {
> + ret = s->rx_fifo[s->rx_start];
> + s->rx_start = (s->rx_start + 1) % 12;
> + s->rx_len--;
> + strongarm_uart_update_status(s);
> + strongarm_uart_update_int_status(s);
> + return ret;
> + }
> + return 0;
> +
> + case UTSR0:
> + return s->utsr0;
> +
> + case UTSR1:
> + return s->utsr1;
> +
> + default:
> + printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
> + return 0;
> + }
> +}
> +
> +static void strongarm_uart_write(void *opaque, target_phys_addr_t addr,
> + uint32_t value)
> +{
> + StrongARMUARTState *s = opaque;
> +
> + switch (addr) {
> + case UTCR0:
> + s->utcr0 = value & 0x7f;
> + strongarm_uart_update_parameters(s);
> + break;
> +
> + case UTCR1:
> + s->brd = (s->brd & 0xff) | ((value & 0xf) << 8);
> + strongarm_uart_update_parameters(s);
> + break;
> +
> + case UTCR2:
> + s->brd = (s->brd & 0xf00) | (value & 0xff);
> + strongarm_uart_update_parameters(s);
> + break;
> +
> + case UTCR3:
> + s->utcr3 = value & 0x3f;
> + if ((s->utcr3 & (1 << 0)) == 0) {
> + s->rx_len = 0;
> + }
> + if ((s->utcr3 & (1 << 1)) == 0) {
> + s->tx_len = 0;
> + }
> + strongarm_uart_update_status(s);
> + strongarm_uart_update_int_status(s);
> + break;
> +
> + case UTDR:
> + if ((s->utcr3 & (1 << 1)) && s->tx_len != 8) {
> + s->tx_fifo[(s->tx_start + s->tx_len) % 8] = value;
> + s->tx_len++;
> + strongarm_uart_update_status(s);
> + strongarm_uart_update_int_status(s);
> + if (s->tx_len == 1) {
> + strongarm_uart_tx(s);
> + }
> + }
> + break;
> +
> + case UTSR0:
> + s->utsr0 = s->utsr0 & ~(value & 0x1c);
> + strongarm_uart_update_int_status(s);
> + break;
> +
> + default:
> + printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
> + }
> +}
> +
> +static CPUReadMemoryFunc * const strongarm_uart_readfn[] = {
> + strongarm_uart_read,
> + strongarm_uart_read,
> + strongarm_uart_read,
> +};
> +
> +static CPUWriteMemoryFunc * const strongarm_uart_writefn[] = {
> + strongarm_uart_write,
> + strongarm_uart_write,
> + strongarm_uart_write,
> +};
> +
> +static int strongarm_uart_init(SysBusDevice *dev)
> +{
> + StrongARMUARTState *s = FROM_SYSBUS(StrongARMUARTState, dev);
> + int iomemtype;
> +
> + iomemtype = cpu_register_io_memory(strongarm_uart_readfn,
> + strongarm_uart_writefn, s, DEVICE_NATIVE_ENDIAN);
> + sysbus_init_mmio(dev, 0x10000, iomemtype);
> + sysbus_init_irq(dev, &s->irq);
> +
> + s->rx_timeout_timer = qemu_new_timer_ns(vm_clock, strongarm_uart_rx_to,
> s);
> + s->tx_timer = qemu_new_timer_ns(vm_clock, strongarm_uart_tx, s);
> +
> + if (s->chr) {
> + qemu_chr_add_handlers(s->chr,
> + strongarm_uart_can_receive,
> + strongarm_uart_receive,
> + strongarm_uart_event,
> + s);
> + }
> +
> + return 0;
> +}
> +
> +static void strongarm_uart_reset(DeviceState *dev)
> +{
> + StrongARMUARTState *s = DO_UPCAST(StrongARMUARTState, busdev.qdev, dev);
> +
> + s->utcr0 = 0x8; /* 8 data, no parity */
> + s->brd = 23; /* 9600 */
> + s->utcr3 = 0x3; /* enable send & recv -- this actually violates spec */
> +
> + s->rx_len = s->tx_len = 0;
> +
> + strongarm_uart_update_parameters(s);
> + strongarm_uart_update_status(s);
> + strongarm_uart_update_int_status(s);
> +}
> +
> +static int strongarm_uart_post_load(void *opaque, int version_id)
> +{
> + StrongARMUARTState *s = opaque;
> +
> + strongarm_uart_update_parameters(s);
> + strongarm_uart_update_status(s);
> + strongarm_uart_update_int_status(s);
> +
> + /* tx and restart timer */
> + if (s->tx_len) {
> + strongarm_uart_tx(s);
> + }
> +
> + /* restart rx timeout timer */
> + if (s->rx_len) {
> + qemu_mod_timer(s->rx_timeout_timer,
> + qemu_get_clock_ns(vm_clock) + s->char_transmit_time * 3);
> + }
> +
> + return 0;
> +}
> +
> +static const VMStateDescription vmstate_strongarm_uart_regs = {
> + .name = "strongarm-uart",
> + .version_id = 0,
> + .minimum_version_id = 0,
> + .minimum_version_id_old = 0,
> + .post_load = strongarm_uart_post_load,
> + .fields = (VMStateField[]) {
> + VMSTATE_UINT8(utcr0, StrongARMUARTState),
> + VMSTATE_UINT16(brd, StrongARMUARTState),
> + VMSTATE_UINT8(utcr3, StrongARMUARTState),
> + VMSTATE_UINT8(utsr0, StrongARMUARTState),
> + VMSTATE_UINT8_ARRAY(tx_fifo, StrongARMUARTState, 8),
> + VMSTATE_UINT8(tx_start, StrongARMUARTState),
> + VMSTATE_UINT8(tx_len, StrongARMUARTState),
> + VMSTATE_UINT16_ARRAY(rx_fifo, StrongARMUARTState, 12),
> + VMSTATE_UINT8(rx_start, StrongARMUARTState),
> + VMSTATE_UINT8(rx_len, StrongARMUARTState),
> + VMSTATE_BOOL(wait_break_end, StrongARMUARTState),
> + VMSTATE_END_OF_LIST(),
> + },
> +};
> +
> +static SysBusDeviceInfo strongarm_uart_info = {
> + .init = strongarm_uart_init,
> + .qdev.name = "strongarm-uart",
> + .qdev.desc = "StrongARM UART controller",
> + .qdev.size = sizeof(StrongARMUARTState),
> + .qdev.reset = strongarm_uart_reset,
> + .qdev.vmsd = &vmstate_strongarm_uart_regs,
> + .qdev.props = (Property[]) {
> + DEFINE_PROP_CHR("chardev", StrongARMUARTState, chr),
> + DEFINE_PROP_END_OF_LIST(),
> + }
> +};
> +
> +/* Synchronous Serial Ports */
> +typedef struct {
> + SysBusDevice busdev;
> + qemu_irq irq;
> + SSIBus *bus;
> +
> + uint16_t sscr[2];
> + uint16_t sssr;
> +
> + uint16_t rx_fifo[8];
> + uint8_t rx_level;
> + uint8_t rx_start;
> +} StrongARMSSPState;
> +
> +#define SSCR0 0x60 /* SSP Control register 0 */
> +#define SSCR1 0x64 /* SSP Control register 1 */
> +#define SSDR 0x6c /* SSP Data register */
> +#define SSSR 0x74 /* SSP Status register */
> +
> +/* Bitfields for above registers */
> +#define SSCR0_SPI(x) (((x) & 0x30) == 0x00)
> +#define SSCR0_SSP(x) (((x) & 0x30) == 0x10)
> +#define SSCR0_UWIRE(x) (((x) & 0x30) == 0x20)
> +#define SSCR0_PSP(x) (((x) & 0x30) == 0x30)
> +#define SSCR0_SSE (1 << 7)
> +#define SSCR0_DSS(x) (((x) & 0xf) + 1)
> +#define SSCR1_RIE (1 << 0)
> +#define SSCR1_TIE (1 << 1)
> +#define SSCR1_LBM (1 << 2)
> +#define SSSR_TNF (1 << 2)
> +#define SSSR_RNE (1 << 3)
> +#define SSSR_TFS (1 << 5)
> +#define SSSR_RFS (1 << 6)
> +#define SSSR_ROR (1 << 7)
> +#define SSSR_RW 0x0080
> +
> +static void strongarm_ssp_int_update(StrongARMSSPState *s)
> +{
> + int level = 0;
> +
> + level |= (s->sssr & SSSR_ROR);
> + level |= (s->sssr & SSSR_RFS) && (s->sscr[1] & SSCR1_RIE);
> + level |= (s->sssr & SSSR_TFS) && (s->sscr[1] & SSCR1_TIE);
> + qemu_set_irq(s->irq, level);
> +}
> +
> +static void strongarm_ssp_fifo_update(StrongARMSSPState *s)
> +{
> + s->sssr &= ~SSSR_TFS;
> + s->sssr &= ~SSSR_TNF;
> + if (s->sscr[0] & SSCR0_SSE) {
> + if (s->rx_level >= 4)
> + s->sssr |= SSSR_RFS;
> + else
> + s->sssr &= ~SSSR_RFS;
> + if (s->rx_level)
> + s->sssr |= SSSR_RNE;
> + else
> + s->sssr &= ~SSSR_RNE;
> + /* TX FIFO is never filled, so it is always in underrun
> + condition if SSP is enabled */
> + s->sssr |= SSSR_TFS;
> + s->sssr |= SSSR_TNF;
> + }
> +
> + strongarm_ssp_int_update(s);
> +}
> +
> +static uint32_t strongarm_ssp_read(void *opaque, target_phys_addr_t addr)
> +{
> + StrongARMSSPState *s = (StrongARMSSPState *) opaque;
> + uint32_t retval;
> +
> + switch (addr) {
> + case SSCR0:
> + return s->sscr[0];
> + case SSCR1:
> + return s->sscr[1];
> + case SSSR:
> + return s->sssr;
> + case SSDR:
> + if (~s->sscr[0] & SSCR0_SSE)
> + return 0xffffffff;
> + if (s->rx_level < 1) {
> + printf("%s: SSP Rx Underrun\n", __FUNCTION__);
> + return 0xffffffff;
> + }
> + s->rx_level --;
> + retval = s->rx_fifo[s->rx_start ++];
> + s->rx_start &= 0x7;
> + strongarm_ssp_fifo_update(s);
> + return retval;
> + default:
> + printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __FUNCTION__,
> addr);
> + break;
> + }
> + return 0;
> +}
> +
> +static void strongarm_ssp_write(void *opaque, target_phys_addr_t addr,
> + uint32_t value)
> +{
> + StrongARMSSPState *s = (StrongARMSSPState *) opaque;
> +
> + switch (addr) {
> + case SSCR0:
> + s->sscr[0] = value & 0xffbf;
> + if ((s->sscr[0] & SSCR0_SSE) && SSCR0_DSS(value) < 4)
> + printf("%s: Wrong data size: %i bits\n", __FUNCTION__,
> + SSCR0_DSS(value));
> + if (!(value & SSCR0_SSE)) {
> + s->sssr = 0;
> + s->rx_level = 0;
> + }
> + strongarm_ssp_fifo_update(s);
> + break;
> +
> + case SSCR1:
> + s->sscr[1] = value & 0x2f;
> + if (value & SSCR1_LBM)
> + printf("%s: Attempt to use SSP LBM mode\n", __FUNCTION__);
> + strongarm_ssp_fifo_update(s);
> + break;
> +
> + case SSSR:
> + s->sssr &= ~(value & SSSR_RW);
> + strongarm_ssp_int_update(s);
> + break;
> +
> + case SSDR:
> + if (SSCR0_UWIRE(s->sscr[0])) {
> + value &= 0xff;
> + } else
> + /* Note how 32bits overflow does no harm here */
> + value &= (1 << SSCR0_DSS(s->sscr[0])) - 1;
> +
> + /* Data goes from here to the Tx FIFO and is shifted out from
> + * there directly to the slave, no need to buffer it.
> + */
> + if (s->sscr[0] & SSCR0_SSE) {
> + uint32_t readval;
> + if (s->sscr[1] & SSCR1_LBM) {
> + readval = value;
> + } else {
> + readval = ssi_transfer(s->bus, value);
> + }
> +
> + if (s->rx_level < 0x08) {
> + s->rx_fifo[(s->rx_start + s->rx_level ++) & 0x7] = readval;
> + } else {
> + s->sssr |= SSSR_ROR;
> + }
> + }
> + strongarm_ssp_fifo_update(s);
> + break;
> +
> + default:
> + printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __FUNCTION__,
> addr);
> + break;
> + }
> +}
> +
> +static CPUReadMemoryFunc * const strongarm_ssp_readfn[] = {
> + strongarm_ssp_read,
> + strongarm_ssp_read,
> + strongarm_ssp_read,
> +};
> +
> +static CPUWriteMemoryFunc * const strongarm_ssp_writefn[] = {
> + strongarm_ssp_write,
> + strongarm_ssp_write,
> + strongarm_ssp_write,
> +};
> +
> +static int strongarm_ssp_post_load(void *opaque, int version_id)
> +{
> + StrongARMSSPState *s = (StrongARMSSPState *) opaque;
> +
> + strongarm_ssp_fifo_update(s);
> +
> + return 0;
> +}
> +
> +static int strongarm_ssp_init(SysBusDevice *dev)
> +{
> + int iomemtype;
> + StrongARMSSPState *s = FROM_SYSBUS(StrongARMSSPState, dev);
> +
> + sysbus_init_irq(dev, &s->irq);
> +
> + iomemtype = cpu_register_io_memory(strongarm_ssp_readfn,
> + strongarm_ssp_writefn, s,
> + DEVICE_NATIVE_ENDIAN);
> + sysbus_init_mmio(dev, 0x1000, iomemtype);
> +
> + s->bus = ssi_create_bus(&dev->qdev, "ssi");
> + return 0;
> +}
> +
> +static void strongarm_ssp_reset(DeviceState *dev)
> +{
> + StrongARMSSPState *s = DO_UPCAST(StrongARMSSPState, busdev.qdev, dev);
> + s->sssr = 0x03; /* 3 bit data, SPI, disabled */
> + s->rx_start = 0;
> + s->rx_level = 0;
> +}
> +
> +static const VMStateDescription vmstate_strongarm_ssp_regs = {
> + .name = "strongarm-ssp",
> + .version_id = 0,
> + .minimum_version_id = 0,
> + .minimum_version_id_old = 0,
> + .post_load = strongarm_ssp_post_load,
> + .fields = (VMStateField[]) {
> + VMSTATE_UINT16_ARRAY(sscr, StrongARMSSPState, 2),
> + VMSTATE_UINT16(sssr, StrongARMSSPState),
> + VMSTATE_UINT16_ARRAY(rx_fifo, StrongARMSSPState, 8),
> + VMSTATE_UINT8(rx_start, StrongARMSSPState),
> + VMSTATE_UINT8(rx_level, StrongARMSSPState),
> + VMSTATE_END_OF_LIST(),
> + },
> +};
> +
> +static SysBusDeviceInfo strongarm_ssp_info = {
> + .init = strongarm_ssp_init,
> + .qdev.name = "strongarm-ssp",
> + .qdev.desc = "StrongARM SSP controller",
> + .qdev.size = sizeof(StrongARMSSPState),
> + .qdev.reset = strongarm_ssp_reset,
> + .qdev.vmsd = &vmstate_strongarm_ssp_regs,
> +};
> +
> +/* Main CPU functions */
> +StrongARMState *sa1110_init(unsigned int sdram_size, const char *rev)
> +{
> + StrongARMState *s;
> + qemu_irq *pic;
> + int i;
> +
> + s = qemu_mallocz(sizeof(StrongARMState));
> +
> + if (!rev) {
> + rev = "sa1110-b5";
> + }
> +
> + if (strncmp(rev, "sa1110", 6)) {
> + error_report("Machine requires a SA1110 processor.\n");
> + exit(1);
> + }
> +
> + s->env = cpu_init(rev);
> +
> + if (!s->env) {
> + error_report("Unable to find CPU definition\n");
> + exit(1);
> + }
> +
> + cpu_register_physical_memory(SA_SDCS0,
> + sdram_size, qemu_ram_alloc(NULL, "strongarm.sdram",
> + sdram_size) | IO_MEM_RAM);
> +
> + pic = arm_pic_init_cpu(s->env);
> + s->pic = sysbus_create_varargs("strongarm_pic", 0x90050000,
> + pic[ARM_PIC_CPU_IRQ], pic[ARM_PIC_CPU_FIQ], NULL);
> +
> + sysbus_create_varargs("pxa25x-timer", 0x90000000,
> + qdev_get_gpio_in(s->pic, SA_PIC_OSTC0),
> + qdev_get_gpio_in(s->pic, SA_PIC_OSTC1),
> + qdev_get_gpio_in(s->pic, SA_PIC_OSTC2),
> + qdev_get_gpio_in(s->pic, SA_PIC_OSTC3),
> + NULL);
> +
> + sysbus_create_simple("strongarm-rtc", 0x90010000,
> + qdev_get_gpio_in(s->pic, SA_PIC_RTC_ALARM));
> +
> + s->gpio = strongarm_gpio_init(0x90040000, s->pic);
> +
> + s->ppc = sysbus_create_varargs("strongarm-ppc", 0x90060000, NULL);
> +
> + for (i = 0; sa_serial[i].io_base; i++) {
> + DeviceState *dev = qdev_create(NULL, "strongarm-uart");
> + qdev_prop_set_chr(dev, "chardev", serial_hds[i]);
> + qdev_init_nofail(dev);
> + sysbus_mmio_map(sysbus_from_qdev(dev), 0,
> + sa_serial[i].io_base);
> + sysbus_connect_irq(sysbus_from_qdev(dev), 0,
> + qdev_get_gpio_in(s->pic, sa_serial[i].irq));
> + }
> +
> + s->ssp = sysbus_create_varargs("strongarm-ssp", 0x80070000,
> + qdev_get_gpio_in(s->pic, SA_PIC_SSP), NULL);
> + s->ssp_bus = (SSIBus *)qdev_get_child_bus(s->ssp, "ssi");
> +
> + return s;
> +}
> +
> +static void strongarm_register_devices(void)
> +{
> + sysbus_register_withprop(&strongarm_pic_info);
> + sysbus_register_withprop(&strongarm_rtc_sysbus_info);
> + sysbus_register_withprop(&strongarm_gpio_info);
> + sysbus_register_withprop(&strongarm_ppc_info);
> + sysbus_register_withprop(&strongarm_uart_info);
> + sysbus_register_withprop(&strongarm_ssp_info);
> +}
> +device_init(strongarm_register_devices)
> diff --git a/hw/strongarm.h b/hw/strongarm.h
> new file mode 100644
> index 0000000..a81b110
> --- /dev/null
> +++ b/hw/strongarm.h
> @@ -0,0 +1,64 @@
> +#ifndef _STRONGARM_H
> +#define _STRONGARM_H
> +
> +#define SA_CS0 0x00000000
> +#define SA_CS1 0x08000000
> +#define SA_CS2 0x10000000
> +#define SA_CS3 0x18000000
> +#define SA_PCMCIA_CS0 0x20000000
> +#define SA_PCMCIA_CS1 0x30000000
> +#define SA_CS4 0x40000000
> +#define SA_CS5 0x48000000
> +/* system registers here */
> +#define SA_SDCS0 0xc0000000
> +#define SA_SDCS1 0xc8000000
> +#define SA_SDCS2 0xd0000000
> +#define SA_SDCS3 0xd8000000
> +
> +enum {
> + SA_PIC_GPIO0_EDGE = 0,
> + SA_PIC_GPIO1_EDGE,
> + SA_PIC_GPIO2_EDGE,
> + SA_PIC_GPIO3_EDGE,
> + SA_PIC_GPIO4_EDGE,
> + SA_PIC_GPIO5_EDGE,
> + SA_PIC_GPIO6_EDGE,
> + SA_PIC_GPIO7_EDGE,
> + SA_PIC_GPIO8_EDGE,
> + SA_PIC_GPIO9_EDGE,
> + SA_PIC_GPIO10_EDGE,
> + SA_PIC_GPIOX_EDGE,
> + SA_PIC_LCD,
> + SA_PIC_UDC,
> + SA_PIC_RSVD1,
> + SA_PIC_UART1,
> + SA_PIC_UART2,
> + SA_PIC_UART3,
> + SA_PIC_MCP,
> + SA_PIC_SSP,
> + SA_PIC_DMA_CH0,
> + SA_PIC_DMA_CH1,
> + SA_PIC_DMA_CH2,
> + SA_PIC_DMA_CH3,
> + SA_PIC_DMA_CH4,
> + SA_PIC_DMA_CH5,
> + SA_PIC_OSTC0,
> + SA_PIC_OSTC1,
> + SA_PIC_OSTC2,
> + SA_PIC_OSTC3,
> + SA_PIC_RTC_HZ,
> + SA_PIC_RTC_ALARM,
> +};
> +
> +typedef struct {
> + CPUState *env;
> + DeviceState *pic;
> + DeviceState *gpio;
> + DeviceState *ppc;
> + DeviceState *ssp;
> + SSIBus *ssp_bus;
> +} StrongARMState;
> +
> +StrongARMState *sa1110_init(unsigned int sdram_size, const char *rev);
> +
> +#endif
> diff --git a/target-arm/cpu.h b/target-arm/cpu.h
> index e247a7a..d5af644 100644
> --- a/target-arm/cpu.h
> +++ b/target-arm/cpu.h
> @@ -363,6 +363,7 @@ enum arm_features {
> ARM_FEATURE_V7MP, /* v7 Multiprocessing Extensions */
> ARM_FEATURE_V4T,
> ARM_FEATURE_V5,
> + ARM_FEATURE_STRONGARM,
> };
>
> static inline int arm_feature(CPUARMState *env, int feature)
> @@ -393,6 +394,8 @@ void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
> #define ARM_CPUID_ARM946 0x41059461
> #define ARM_CPUID_TI915T 0x54029152
> #define ARM_CPUID_TI925T 0x54029252
> +#define ARM_CPUID_SA1100 0x4401A11B
> +#define ARM_CPUID_SA1110 0x6901B119
> #define ARM_CPUID_PXA250 0x69052100
> #define ARM_CPUID_PXA255 0x69052d00
> #define ARM_CPUID_PXA260 0x69052903
> diff --git a/target-arm/helper.c b/target-arm/helper.c
> index ce9a9d8..5cb7b25 100644
> --- a/target-arm/helper.c
> +++ b/target-arm/helper.c
> @@ -214,6 +214,11 @@ static void cpu_reset_model_id(CPUARMState *env,
> uint32_t id)
> env->cp15.c0_cachetype = 0xd172172;
> env->cp15.c1_sys = 0x00000078;
> break;
> + case ARM_CPUID_SA1100:
> + case ARM_CPUID_SA1110:
> + set_feature(env, ARM_FEATURE_STRONGARM);
> + env->cp15.c1_sys = 0x00000070;
> + break;
> default:
> cpu_abort(env, "Bad CPU ID: %x\n", id);
> break;
> @@ -374,6 +379,8 @@ static const struct arm_cpu_t arm_cpu_names[] = {
> { ARM_CPUID_CORTEXA9, "cortex-a9"},
> { ARM_CPUID_TI925T, "ti925t" },
> { ARM_CPUID_PXA250, "pxa250" },
> + { ARM_CPUID_SA1100, "sa1100" },
> + { ARM_CPUID_SA1110, "sa1110" },
> { ARM_CPUID_PXA255, "pxa255" },
> { ARM_CPUID_PXA260, "pxa260" },
> { ARM_CPUID_PXA261, "pxa261" },
> @@ -1549,6 +1556,8 @@ void HELPER(set_cp15)(CPUState *env, uint32_t insn,
> uint32_t val)
> case 9:
> if (arm_feature(env, ARM_FEATURE_OMAPCP))
> break;
> + if (arm_feature(env, ARM_FEATURE_STRONGARM))
> + break; /* Ignore ReadBuffer access */
> switch (crm) {
> case 0: /* Cache lockdown. */
> switch (op1) {
> --
> 1.7.4.1
>
>
>
--
Aurelien Jarno GPG: 1024D/F1BCDB73
address@hidden http://www.aurel32.net