* [Qemu-devel] [patch V4 0/4] Support for i.MX31 and the Kyoto KZM11 board
@ 2012-03-09 3:27 peter.chubb
2012-03-09 3:27 ` [Qemu-devel] [patch V4 1/4] i.MX UART support peter.chubb
` (3 more replies)
0 siblings, 4 replies; 13+ messages in thread
From: peter.chubb @ 2012-03-09 3:27 UTC (permalink / raw)
To: peter.maydell, qemu-devel; +Cc: philipo
This is version 4 of the patchset. The major changes since the last
round are:
* Moved to new QEMU object model for devices
-- this requred rearranging the intialisation of the serial device,
to ensure that the lowest address uart got serial 0, so qemu
-nographic would work as expected.
* Hans Jiang and Alex Clench (original patch authors) have left
OK-Labs, so the Signed-Off-By: line is changed to their boss who
has given permission for release.
* EPIT now honours prescaler and clocksource fields.
* I *think* all previous comments have been addressed:
-- now all files under GPL 2.0 or later
-- better emulation of which fields in which registers are
read-only
-- AVIC implements priorities.
Still to do:
Clock module support --- assumes 50MHz ipg_clk
clock source and prescaler etc., for the general purpose timer
UART is very bare-bones -- ignores baud rate, and implements only a
one character FIFO
--
Dr Peter Chubb peter.chubb AT nicta.com.au
http://www.ssrg.nicta.com.au Software Systems Research Group/NICTA
^ permalink raw reply [flat|nested] 13+ messages in thread
* [Qemu-devel] [patch V4 1/4] i.MX UART support
2012-03-09 3:27 [Qemu-devel] [patch V4 0/4] Support for i.MX31 and the Kyoto KZM11 board peter.chubb
@ 2012-03-09 3:27 ` peter.chubb
2012-03-15 17:01 ` Peter Maydell
2012-03-09 3:27 ` [Qemu-devel] [patch V4 2/4] FreeSCALE i.MX31 support: Timers peter.chubb
` (2 subsequent siblings)
3 siblings, 1 reply; 13+ messages in thread
From: peter.chubb @ 2012-03-09 3:27 UTC (permalink / raw)
To: peter.maydell, qemu-devel; +Cc: Peter Chubb, philipo
[-- Attachment #1: imx-serial.patch --]
[-- Type: text/plain, Size: 14681 bytes --]
Implement the FreeScale i.MX UART. This uart is used in a variety of
SoCs, including some by Motorola, as well as in the FreeScale i.MX
series.
This patch gives only a `bare-bones' implementation, enough to run Linux
or OKL4, but that's about it.
Signed-off-by: Philip O'Sullivan <philipo@ok-labs.com>
Signed-off-by: Peter Chubb <peter.chubb@nicta.com.au>
---
Makefile.target | 1
hw/imx_serial.c | 466 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 467 insertions(+)
create mode 100644 hw/imx_serial.c
Index: qemu-working/hw/imx_serial.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ qemu-working/hw/imx_serial.c 2012-03-09 14:13:49.714061283 +1100
@@ -0,0 +1,466 @@
+/*
+ * IMX31 UARTS
+ *
+ * Copyright (c) 2008 OKL
+ * Originally Written by Hans Jiang
+ * Copyright (c) 2011 NICTA Pty Ltd.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * This is a `bare-bones' implementation of the IMX series serial ports.
+ * TODO:
+ * -- implement FIFOs. The real hardware has 32 word transmit
+ * and receive FIFOs; we currently use a 1-char buffer
+ * -- implement DMA
+ * -- implement BAUD-rate and modem lines, for when the backend
+ * is a real serial device.
+ */
+
+#include "hw.h"
+#include "sysbus.h"
+#include "sysemu.h"
+#include "qemu-char.h"
+#include "imx.h"
+
+//#define DEBUG_SERIAL 1
+#ifdef DEBUG_SERIAL
+#define DPRINTF(fmt, args...) \
+do { printf("imx_serial: " fmt , ##args); } while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while (0)
+#endif
+
+/*
+ * Define to 1 for messages about attempts to
+ * access unimplemented registers or similar.
+ */
+//#define DEBUG_IMPLEMENTATION 1
+#ifdef DEBUG_IMPLEMENTATION
+# define IPRINTF(fmt, args...) \
+ do { fprintf(stderr, "imx_serial: " fmt, ##args); } while (0)
+#else
+# define IPRINTF(fmt, args...) do {} while (0)
+#endif
+
+typedef struct {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ int32_t readbuff;
+
+ uint32_t usr1;
+ uint32_t usr2;
+ uint32_t ucr1;
+ uint32_t ucr2;
+ uint32_t uts1;
+
+ /*
+ * The registers below are implemented just so that the
+ * guest OS sees what it has written
+ */
+ uint32_t onems;
+ uint32_t ufcr;
+ uint32_t ubmr;
+ uint32_t ubrc;
+ uint32_t ucr3;
+
+ qemu_irq irq;
+ CharDriverState *chr;
+} imx_state;
+
+static const VMStateDescription vmstate_imx_serial = {
+ .name = "imx-serial",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32(readbuff, imx_state),
+ VMSTATE_UINT32(usr1, imx_state),
+ VMSTATE_UINT32(usr2, imx_state),
+ VMSTATE_UINT32(ucr1, imx_state),
+ VMSTATE_UINT32(uts1, imx_state),
+ VMSTATE_UINT32(onems, imx_state),
+ VMSTATE_UINT32(ufcr, imx_state),
+ VMSTATE_UINT32(ubmr, imx_state),
+ VMSTATE_UINT32(ubrc, imx_state),
+ VMSTATE_UINT32(ucr3, imx_state),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+
+#define URXD_CHARRDY (1<<15) /* character read is valid */
+#define URXD_ERR (1<<14) /* Character has error */
+#define URXD_BRK (1<<11) /* Break received */
+
+#define USR1_PARTYER (1<<15) /* Parity Error */
+#define USR1_RTSS (1<<14) /* RTS pin status */
+#define USR1_TRDY (1<<13) /* Tx ready */
+#define USR1_RTSD (1<<12) /* RTS delta: pin changed state */
+#define USR1_ESCF (1<<11) /* Escape sequence interrupt */
+#define USR1_FRAMERR (1<<10) /* Framing error */
+#define USR1_RRDY (1<<9) /* receiver ready */
+#define USR1_AGTIM (1<<8) /* Aging timer interrupt */
+#define USR1_DTRD (1<<7) /* DTR changed */
+#define USR1_RXDS (1<<6) /* Receiver is idle */
+#define USR1_AIRINT (1<<5) /* Aysnch IR interrupt */
+#define USR1_AWAKE (1<<4) /* Falling edge detected on RXd pin */
+
+#define USR2_ADET (1<<15) /* Autobaud complete */
+#define USR2_TXFE (1<<14) /* Transmit FIFO empty */
+#define USR2_DTRF (1<<13) /* DTR/DSR transition */
+#define USR2_IDLE (1<<12) /* UART has been idle for too long */
+#define USR2_ACST (1<<11) /* Autobaud counter stopped */
+#define USR2_RIDELT (1<<10) /* Ring Indicator delta */
+#define USR2_RIIN (1<<9) /* Ring Indicator Input */
+#define USR2_IRINT (1<<8) /* Serial Infrared Interrupt */
+#define USR2_WAKE (1<<7) /* Start bit detected */
+#define USR2_DCDDELT (1<<6) /* Data Carrier Detect delta */
+#define USR2_DCDIN (1<<5) /* Data Carrier Detect Input */
+#define USR2_RTSF (1<<4) /* RTS transition */
+#define USR2_TXDC (1<<3) /* Transmission complete */
+#define USR2_BRCD (1<<2) /* Break condition detected */
+#define USR2_ORE (1<<1) /* Overrun error */
+#define USR2_RDR (1<<0) /* Receive data ready */
+
+#define UCR1_TRDYEN (1<<13) /* Tx Ready Interrupt Enable */
+#define UCR1_RRDYEN (1<<9) /* Rx Ready Interrupt Enable */
+#define UCR1_TXMPTYEN (1<<6) /* Tx Empty Interrupt Enable */
+#define UCR1_UARTEN (1<<0) /* UART Enable */
+
+#define UCR2_TXEN (1<<2) /* Transmitter enable */
+#define UCR2_RXEN (1<<1) /* Receiver enable */
+#define UCR2_SRST (1<<0) /* Reset complete */
+
+#define UTS1_TXEMPTY (1<<6)
+#define UTS1_RXEMPTY (1<<5)
+#define UTS1_TXFULL (1<<4)
+#define UTS1_RXFULL (1<<3)
+
+static void imx_update(imx_state *s)
+{
+ uint32_t flags;
+
+ flags = (s->usr1 & s->ucr1) & (USR1_TRDY|USR1_RRDY);
+ if (!(s->ucr1 & UCR1_TXMPTYEN)) {
+ flags &= ~USR1_TRDY;
+ }
+
+ qemu_set_irq(s->irq, !!flags);
+}
+
+static void imx_serial_reset(imx_state *s)
+{
+
+ s->usr1 = USR1_TRDY | USR1_RXDS;
+ /*
+ * Fake attachment of a terminal: assert RTS.
+ */
+ s->usr1 |= USR1_RTSS;
+ s->usr2 = USR2_TXFE | USR2_TXDC | USR2_DCDIN;
+ s->uts1 = UTS1_RXEMPTY | UTS1_TXEMPTY;
+ s->ucr1 = 0;
+ s->ucr2 = UCR2_SRST;
+ s->ucr3 = 0x700;
+ s->ubmr = 0;
+ s->ubrc = 4;
+ s->readbuff = URXD_ERR;
+}
+
+static void imx_serial_reset_at_boot(DeviceState *dev)
+{
+ imx_state *s = container_of(dev, imx_state, busdev.qdev);
+
+ imx_serial_reset(s);
+
+ /*
+ * enable the uart on boot, so messages from the linux decompresser
+ * are visible. On real hardware this is done by the boot rom
+ * before anything else is loaded.
+ */
+ s->ucr1 = UCR1_UARTEN;
+ s->ucr2 = UCR2_TXEN;
+
+}
+
+static uint64_t imx_serial_read(void *opaque, target_phys_addr_t offset,
+ unsigned size)
+{
+ imx_state *s = (imx_state *)opaque;
+ uint32_t c;
+
+ DPRINTF("read(offset=%x)\n", offset >> 2);
+ switch (offset >> 2) {
+ case 0x0: /* URXD */
+ c = s->readbuff;
+ if (!(s->uts1 & UTS1_RXEMPTY)) {
+ /* Character is valid */
+ c |= URXD_CHARRDY;
+ s->usr1 &= ~USR1_RRDY;
+ s->usr2 &= ~USR2_RDR;
+ s->uts1 |= UTS1_RXEMPTY;
+ imx_update(s);
+ qemu_chr_accept_input(s->chr);
+ }
+ return c;
+
+ case 0x20: /* UCR1 */
+ return s->ucr1;
+
+ case 0x21: /* UCR2 */
+ return s->ucr2;
+
+ case 0x25: /* USR1 */
+ return s->usr1;
+
+ case 0x26: /* USR2 */
+ return s->usr2;
+
+ case 0x2A: /* BRM Modulator */
+ return s->ubmr;
+
+ case 0x2B: /* Baud Rate Count */
+ return s->ubrc;
+
+ case 0x2d: /* Test register */
+ return s->uts1;
+
+ case 0x24: /* UFCR */
+ return s->ufcr;
+
+ case 0x2c:
+ return s->onems;
+
+ case 0x22: /* UCR3 */
+ return s->ucr3;
+
+ case 0x23: /* UCR4 */
+ case 0x29: /* BRM Incremental */
+ return 0x0; /* TODO */
+
+ default:
+ IPRINTF("imx_serial_read: bad offset: 0x%x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void imx_serial_write(void *opaque, target_phys_addr_t offset,
+ uint64_t value, unsigned size)
+{
+ imx_state *s = (imx_state *)opaque;
+ unsigned char ch;
+
+ DPRINTF("write(offset=%x, value = %x) to %s\n",
+ offset >> 2,
+ (unsigned int)value, s->chr ? s->chr->label : "NODEV");
+
+ switch (offset >> 2) {
+ case 0x10: /* UTXD */
+ ch = value;
+ if (s->ucr2 & UCR2_TXEN) {
+ if (s->chr) {
+ qemu_chr_fe_write(s->chr, &ch, 1);
+ }
+ s->usr1 &= ~USR1_TRDY;
+ imx_update(s);
+ s->usr1 |= USR1_TRDY;
+ imx_update(s);
+ }
+ break;
+
+ case 0x20: /* UCR1 */
+ s->ucr1 = value & 0xffff;
+ DPRINTF("write(ucr1=%x)\n", (unsigned int)value);
+ imx_update(s);
+ break;
+
+ case 0x21: /* UCR2 */
+ /*
+ * Only a few bits in control register 2 are implemented as yet.
+ * If it's intended to use a real serial device as a back-end, this
+ * register will have to be implemented more fully.
+ */
+ if (!(value & UCR2_SRST)) {
+ imx_serial_reset(s);
+ imx_update(s);
+ value |= UCR2_SRST;
+ }
+ if (value & UCR2_RXEN) {
+ if (!(s->ucr2 & UCR2_RXEN)) {
+ qemu_chr_accept_input(s->chr);
+ }
+ }
+ s->ucr2 = value & 0xffff;
+ break;
+
+ case 0x25: /* USR1 */
+ value &= USR1_AWAKE | USR1_AIRINT | USR1_DTRD | USR1_AGTIM |
+ USR1_FRAMERR | USR1_ESCF | USR1_RTSD | USR1_PARTYER;
+ s->usr1 &= ~value;
+ break;
+
+ case 0x26: /* USR2 */
+ /*
+ * Writing 1 to some bits clears them; all other
+ * values are ignored
+ */
+ value &= USR2_ADET | USR2_DTRF | USR2_IDLE | USR2_ACST |
+ USR2_RIDELT | USR2_IRINT | USR2_WAKE |
+ USR2_DCDDELT | USR2_RTSF | USR2_BRCD | USR2_ORE;
+ s->usr2 &= ~value;
+ break;
+
+ /*
+ * Linux expects to see what it writes to these registers
+ * We don't currently alter the baud rate
+ */
+ case 0x29: /* UBIR */
+ s->ubrc = value & 0xffff;
+ break;
+
+ case 0x2a: /* UBMR */
+ s->ubmr = value & 0xffff;
+ break;
+
+ case 0x2c: /* One ms reg */
+ s->onems = value & 0xffff;
+ break;
+
+ case 0x24: /* FIFO control register */
+ s->ufcr = value & 0xffff;
+ break;
+
+ case 0x22: /* UCR3 */
+ s->ucr3 = value & 0xffff;
+ break;
+
+ case 0x2d: /* UTS1 */
+ case 0x23: /* UCR4 */
+ IPRINTF("Unimplemented Register %x written to\n", offset >> 2);
+ /* TODO */
+ break;
+
+ default:
+ IPRINTF("imx_serial_write: Bad offset 0x%x\n", (int)offset);
+ }
+}
+
+static int imx_can_receive(void *opaque)
+{
+ imx_state *s = (imx_state *)opaque;
+ return !(s->usr1 & USR1_RRDY);
+}
+
+static void imx_put_data(void *opaque, uint32_t value)
+{
+ imx_state *s = (imx_state *)opaque;
+ DPRINTF("received char\n");
+ s->usr1 |= USR1_RRDY;
+ s->usr2 |= USR2_RDR;
+ s->uts1 &= ~UTS1_RXEMPTY;
+ s->readbuff = value;
+ imx_update(s);
+}
+
+static void imx_receive(void *opaque, const uint8_t *buf, int size)
+{
+ imx_put_data(opaque, *buf);
+}
+
+static void imx_event(void *opaque, int event)
+{
+ if (event == CHR_EVENT_BREAK) {
+ imx_put_data(opaque, URXD_BRK);
+ }
+}
+
+
+static const struct MemoryRegionOps imx_serial_ops = {
+ .read = imx_serial_read,
+ .write = imx_serial_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int imx_serial_init(SysBusDevice *dev)
+{
+ imx_state *s = FROM_SYSBUS(imx_state, dev);
+
+
+ memory_region_init_io(&s->iomem, &imx_serial_ops, s, "imx-serial", 0x1000);
+ sysbus_init_mmio(dev, &s->iomem);
+ sysbus_init_irq(dev, &s->irq);
+
+ if (s->chr) {
+ qemu_chr_add_handlers(s->chr, imx_can_receive, imx_receive,
+ imx_event, s);
+ } else {
+ DPRINTF("No char dev for uart at 0x%lx\n",
+ (unsigned long)s->iomem.ram_addr);
+ }
+
+ return 0;
+}
+
+void imx_serial_create(int uart, const target_phys_addr_t addr, qemu_irq irq)
+{
+ DeviceState *dev;
+ SysBusDevice *bus;
+ CharDriverState *chr;
+ const char chr_name[] = "serial";
+ char label[ARRAY_SIZE(chr_name) + 1];
+
+ dev = qdev_create(NULL, "imx-serial");
+
+ if (uart >= MAX_SERIAL_PORTS) {
+ hw_error("Cannot assign uart %d: QEMU supports only %d ports\n",
+ uart, MAX_SERIAL_PORTS);
+ }
+ chr = serial_hds[uart];
+ if (!chr) {
+ snprintf(label, ARRAY_SIZE(label), "%s%d", chr_name, uart);
+ chr = qemu_chr_new(label, "null", NULL);
+ if (!(chr)) {
+ hw_error("Can't assign serial port to imx-uart%d.\n", uart);
+ }
+ }
+
+ qdev_prop_set_chr(dev, "chardev", chr);
+ bus = sysbus_from_qdev(dev);
+ qdev_init_nofail(dev);
+ if (addr != (target_phys_addr_t)-1) {
+ sysbus_mmio_map(bus, 0, addr);
+ }
+ sysbus_connect_irq(bus, 0, irq);
+
+}
+
+
+static Property imx32_serial_properties[] = {
+ DEFINE_PROP_CHR("chardev", imx_state, chr),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void imx_serial_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+ k->init = imx_serial_init;
+ dc->vmsd = &vmstate_imx_serial;
+ dc->reset = imx_serial_reset_at_boot;
+ dc->desc = "i.MX series UART";
+ dc->props = imx32_serial_properties;
+}
+
+static TypeInfo imx_serial_info = {
+ .name = "imx-serial",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(imx_state),
+ .class_init = imx_serial_class_init,
+};
+
+static void imx_serial_register_types(void)
+{
+ type_register_static(&imx_serial_info);
+}
+
+type_init(imx_serial_register_types)
Index: qemu-working/Makefile.target
===================================================================
--- qemu-working.orig/Makefile.target 2012-03-09 14:13:48.626046347 +1100
+++ qemu-working/Makefile.target 2012-03-09 14:13:49.714061283 +1100
@@ -379,6 +379,7 @@ obj-arm-y += vexpress.o
obj-arm-y += strongarm.o
obj-arm-y += collie.o
obj-arm-y += pl041.o lm4549.o
+obj-arm-y += imx_serial.o
obj-arm-$(CONFIG_FDT) += device_tree.o
obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
^ permalink raw reply [flat|nested] 13+ messages in thread
* [Qemu-devel] [patch V4 2/4] FreeSCALE i.MX31 support: Timers
2012-03-09 3:27 [Qemu-devel] [patch V4 0/4] Support for i.MX31 and the Kyoto KZM11 board peter.chubb
2012-03-09 3:27 ` [Qemu-devel] [patch V4 1/4] i.MX UART support peter.chubb
@ 2012-03-09 3:27 ` peter.chubb
2012-03-15 17:08 ` Peter Maydell
2012-03-09 3:27 ` [Qemu-devel] [patch V4 3/4] FreeSCALE i.MX31 support: AVIC peter.chubb
2012-03-09 3:27 ` [Qemu-devel] [patch V4 4/4] FreeSCALE i.MX31 support: KZM-ARM11-01 evaluation board peter.chubb
3 siblings, 1 reply; 13+ messages in thread
From: peter.chubb @ 2012-03-09 3:27 UTC (permalink / raw)
To: peter.maydell, qemu-devel; +Cc: Peter Chubb, philipo
[-- Attachment #1: imx-timer.patch --]
[-- Type: text/plain, Size: 17801 bytes --]
Implement the timers on the FreeScale i.MX31 SoC.
This is not a complete implementation, but gives enough for
Linux to boot and run. In particular external triggers, which are
not useful under QEMU, are not implemented.
Signed-off-by: Philip O'Sullivan <philipo@ok-labs.com>
Signed-off-by: Peter Chubb <peter.chubb@nicta.com.au>
---
Makefile.target | 2
hw/imx_timer.c | 575 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 576 insertions(+), 1 deletion(-)
create mode 100644 hw/imx_timer.c
Index: qemu-working/hw/imx_timer.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ qemu-working/hw/imx_timer.c 2012-03-09 14:13:51.406084465 +1100
@@ -0,0 +1,575 @@
+/*
+ * IMX31 Timer
+ *
+ * Copyright (c) 2008 OK Labs
+ * Copyright (c) 2011 NICTA Pty Ltd
+ * Originally Written by Hans Jiang
+ * Updated by Peter Chubb
+ *
+ * This code is licenced under GPL version 2 or later. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "hw.h"
+#include "qemu-timer.h"
+#include "ptimer.h"
+#include "sysbus.h"
+
+//#define DEBUG_TIMER 1
+#ifdef DEBUG_TIMER
+# define DPRINTF(fmt, args...) \
+ do { printf("imx_timer: " fmt , ##args); } while (0)
+#else
+# define DPRINTF(fmt, args...) do {} while (0)
+#endif
+
+/*
+ * Define to 1 for messages about attempts to
+ * access unimplemented registers or similar.
+ */
+#define DEBUG_IMPLEMENTATION 1
+#if DEBUG_IMPLEMENTATION
+# define IPRINTF(fmt, args...) \
+ do { fprintf(stderr, "imx_timer: " fmt, ##args); } while (0)
+#else
+# define IPRINTF(fmt, args...) do {} while (0)
+#endif
+
+/*
+ * GPT : General purpose timer
+ *
+ * This timer counts up continuously while it is enabled, resetting itself
+ * to 0 after it reaches TIMER_MAX (in freerun mode) or when it
+ * reaches the value of ocr1 (in periodic mode). Unfortunately, the
+ * Qemu ptimer abstraction doesn't have a mode like this, so the code
+ * uses Qemu timers directly.
+ *
+ * The code emulates a free-running timer by using Qemu's nanosecond
+ * clock, suitably scaled, using the remainder after dividing by the
+ * timer's period. In the real hardware, there are three comparison
+ * registers that can trigger interrupts, and compare channel 1 can be
+ * used to force-reset the timer. However, this is a `bare-bones'
+ * implementation: only what Linux 3.0.x uses has been implemented
+ * (free-running timer from 0 to OCR1 or TIMER_MAX) Likewise, only a
+ * single frequency is implemented, rather than all the complicated
+ * clock-source and prescaling logic that the real hardware implements
+ * (most of which doesn't make sense on Qemu)
+ */
+
+#define TIMER_MAX 0XFFFFFFFFULL
+#define GPT_FREQ 50000000 /* Hz == 50 MHz */
+
+/* Control register. Not all of these bits have any effect (yet) */
+#define GPT_CR_EN (1 << 0) /* GPT Enable */
+#define GPT_CR_ENMOD (1 << 1) /* GPT Enable Mode */
+#define GPT_CR_DBGEN (1 << 2) /* GPT Debug mode enable */
+#define GPT_CR_WAITEN (1 << 3) /* GPT Wait Mode Enable */
+#define GPT_CR_DOZEN (1 << 4) /* GPT Doze mode enable */
+#define GPT_CR_STOPEN (1 << 5) /* GPT Stop Mode Enable */
+#define GPT_CR_CLKSRC (7 << 6) /* Clock source select (3 bits) */
+#define GPT_CR_FRR (1 << 9) /* Freerun or Restart */
+#define GPT_CR_SWR (1 << 15) /* Software Reset */
+#define GPT_CR_IM1 (3 << 16) /* Input capture channel 1 mode (2 bits) */
+#define GPT_CR_IM2 (3 << 18) /* Input capture channel 2 mode (2 bits) */
+#define GPT_CR_OM1 (7 << 20) /* Output Compare Channel 1 Mode (3 bits) */
+#define GPT_CR_OM2 (7 << 23) /* Output Compare Channel 2 Mode (3 bits) */
+#define GPT_CR_OM3 (7 << 26) /* Output Compare Channel 3 Mode (3 bits) */
+#define GPT_CR_FO1 (1 << 29) /* Force Output Compare Channel 1 */
+#define GPT_CR_FO2 (1 << 30) /* Force Output Compare Channel 2 */
+#define GPT_CR_FO3 (1 << 31) /* Force Output Compare Channel 3 */
+
+#define GPT_SR_OF1 (1 << 0)
+#define GPT_SR_ROV (1 << 5)
+
+#define GPT_IR_OF1IE (1 << 0)
+#define GPT_IR_ROVIE (1 << 5)
+
+typedef struct {
+ SysBusDevice busdev;
+ QEMUTimer *timer;
+ MemoryRegion iomem;
+ uint32_t cr;
+ uint32_t pr;
+ uint32_t sr;
+ uint32_t ir;
+ uint32_t ocr1;
+ uint32_t cnt;
+
+ uint32_t waiting_rov;
+ qemu_irq irq;
+} imx_timerg_state;
+
+static const VMStateDescription vmstate_imx_timerg = {
+ .name = "imx-timerg",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(cr, imx_timerg_state),
+ VMSTATE_UINT32(pr, imx_timerg_state),
+ VMSTATE_UINT32(sr, imx_timerg_state),
+ VMSTATE_UINT32(ir, imx_timerg_state),
+ VMSTATE_UINT32(ocr1, imx_timerg_state),
+ VMSTATE_UINT32(cnt, imx_timerg_state),
+ VMSTATE_UINT32(waiting_rov, imx_timerg_state),
+ VMSTATE_TIMER(timer, imx_timerg_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+
+static void imx_timerg_update(imx_timerg_state *s)
+{
+ uint32_t flags = s->sr & s->ir & (GPT_SR_OF1 | GPT_SR_ROV);
+
+ DPRINTF("g-timer SR: %s %s IR=%s %s, %s\n",
+ s->sr & GPT_SR_OF1 ? "OF1" : "",
+ s->sr & GPT_SR_ROV ? "ROV" : "",
+ s->ir & GPT_SR_OF1 ? "OF1" : "",
+ s->ir & GPT_SR_ROV ? "ROV" : "",
+ s->cr & GPT_CR_EN ? "CR_EN" : "Not Enabled");
+
+
+ qemu_set_irq(s->irq, (s->cr & GPT_CR_EN) && flags);
+}
+
+static uint64_t imx_timerg_update_count(imx_timerg_state *s)
+{
+ uint64_t clk = qemu_get_clock_ns(vm_clock);
+ uint64_t period = (s->cr & GPT_CR_FRR) ? TIMER_MAX + 1 : s->ocr1;
+
+ s->cnt = (uint32_t)(muldiv64(clk, GPT_FREQ/1000000,
+ 1000) % period);
+ return clk;
+}
+
+static void imx_timerg_run(imx_timerg_state *s, uint32_t timeout)
+{
+ uint64_t clk = imx_timerg_update_count(s);
+ uint32_t diff_cnt;
+
+ /*
+ * For small timeouts, qemu sometimes runs too slow.
+ * Better deliver a late interrupt than none.
+ */
+ if (timeout) {
+ DPRINTF("g-run: s->cnt %u < timout %u\n", s->cnt, timeout);
+ if (timeout > s->cnt) {
+ diff_cnt = (timeout - s->cnt);
+ } else {
+ diff_cnt = timeout;
+ }
+ s->waiting_rov = 0;
+ } else {
+ DPRINTF("g-run, FRR\n");
+ diff_cnt = (TIMER_MAX + 1) - s->cnt;
+ s->waiting_rov = 1;
+ }
+ qemu_mod_timer(s->timer, clk + diff_cnt * 1000 / (GPT_FREQ/1000000));
+}
+
+static uint64_t imx_timerg_read(void *opaque, target_phys_addr_t offset,
+ unsigned size)
+{
+ imx_timerg_state *s = (imx_timerg_state *)opaque;
+
+ DPRINTF("g-read(offset=%x)\n", offset >> 2);
+ switch (offset >> 2) {
+ case 0: /* Control Register */
+ return s->cr;
+
+ case 1: /* prescaler */
+ return s->pr;
+
+ case 2: /* Status Register */
+ return s->sr;
+
+ case 3: /* Interrupt Register */
+ return s->ir;
+
+ case 4: /* Output Compare Register 1 */
+ return s->ocr1;
+
+ case 9: /* cnt */
+ imx_timerg_update_count(s);
+ return s->cnt;
+ }
+
+ IPRINTF("imx_timerg_read: Bad offset %x\n",
+ (int)offset >> 2);
+ return 0;
+}
+
+static void imx_timerg_reset(DeviceState *dev)
+{
+ imx_timerg_state *s = container_of(dev, imx_timerg_state, busdev.qdev);
+
+ /*
+ * Soft reset doesn't touch some bits; hard reset clears them
+ */
+ s->cr &= ~(GPT_CR_EN|GPT_CR_DOZEN|GPT_CR_WAITEN|GPT_CR_DBGEN);
+ s->sr = 0;
+ s->pr = 0;
+ s->ir = 0;
+ s->cnt = 0;
+ s->ocr1 = TIMER_MAX;
+ imx_timerg_update_count(s);
+}
+
+static void imx_timerg_write(void *opaque, target_phys_addr_t offset,
+ uint64_t value, unsigned size)
+{
+ imx_timerg_state *s = (imx_timerg_state *)opaque;
+ DPRINTF("g-write(offset=%x, value = %x)\n", (unsigned int)offset >> 2,
+ (unsigned int)value);
+
+ switch (offset >> 2) {
+ case 0: /* CR */
+ if (value & GPT_CR_SWR) { /* force reset */
+ value &= ~GPT_CR_SWR;
+ imx_timerg_reset(&s->busdev.qdev);
+ imx_timerg_update(s);
+ }
+ if (!(s->cr & GPT_CR_EN) && (value & GPT_CR_EN)) {
+ if (value & GPT_CR_ENMOD) {
+ s->cnt = 0;
+ }
+ imx_timerg_run(s, s->ocr1);
+ } else if ((s->cr & GPT_CR_EN) && !(value & GPT_CR_EN)) {
+ qemu_del_timer(s->timer);
+ };
+
+ s->cr = value & ~0x7c;
+ return;
+
+ case 1: /* Prescaler */
+ s->pr = value & 0xfff;
+ return;
+
+ case 2: /* SR */
+ /*
+ * No point in implementing the status register bits to do with
+ * external interrupt sources.
+ */
+ value &= GPT_SR_OF1 | GPT_SR_ROV;
+ s->sr &= ~value;
+ imx_timerg_update(s);
+ return;
+
+ case 3: /* IR -- interrupt register */
+ s->ir = value & 0x3f;
+ imx_timerg_update(s);
+ return;
+
+ case 4: /* OCR1 -- output compare register */
+ /* In non-freerun mode, reset count when this register is written &*/
+ s->ocr1 = value ;
+ if (!(s->cr & GPT_CR_FRR)) {
+ s->cnt = 0;
+ }
+ if (s->cr & GPT_CR_EN) {
+ imx_timerg_run(s, s->ocr1);
+ }
+ return;
+
+ default:
+ IPRINTF("imx_timerg_write: Bad offset %x\n",
+ (int)offset >> 2);
+ }
+}
+
+static void imx_timerg_timeout(void *opaque)
+{
+ imx_timerg_state *s = (imx_timerg_state *)opaque;
+
+ DPRINTF("imx_timerg_timeout, waiting rov=%d\n", s->waiting_rov);
+ if (s->waiting_rov) {
+ s->sr |= GPT_SR_ROV;
+ if (s->ocr1 == TIMER_MAX) {
+ s->sr |= GPT_SR_OF1;
+ }
+ imx_timerg_run(s, s->ocr1);
+ } else {
+ s->sr |= GPT_SR_OF1;
+ imx_timerg_run(s, 0);
+ }
+ imx_timerg_update(s);
+}
+
+static const MemoryRegionOps imx_timerg_ops = {
+ .read = imx_timerg_read,
+ .write = imx_timerg_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+
+static int imx_timerg_init(SysBusDevice *dev)
+{
+ imx_timerg_state *s = FROM_SYSBUS(imx_timerg_state, dev);
+
+ sysbus_init_irq(dev, &s->irq);
+ memory_region_init_io(&s->iomem, &imx_timerg_ops,
+ s, "imxg-timer",
+ 0x00001000);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ s->timer = qemu_new_timer_ns(vm_clock, imx_timerg_timeout, s);
+ /* Hard reset resets extra bits in CR */
+ s->cr = 0;
+ return 0;
+}
+
+
+
+/*
+ * EPIT: Enhanced periodic interrupt timer
+ */
+
+#define TIMER_TICK_LENGTH 5000
+
+#define CR_EN (1 << 0)
+#define CR_ENMOD (1 << 1)
+#define CR_OCIEN (1 << 2)
+#define CR_RLD (1 << 3)
+#define CR_PRESCALE_SHIFT (4)
+#define CR_PRESCALE_MASK (0xfff << CR_PRESCALE_SHIFT)
+#define CR_SWR (1 << 16)
+#define CR_IOVW (1 << 17)
+#define CR_DBGEN (1 << 18)
+#define CR_EPIT (1 << 19)
+#define CR_DOZEN (1 << 20)
+#define CR_STOPEN (1 << 21)
+#define CR_CLKSRC_SHIFT (24)
+#define CR_CLKSRC_MASK (0x3 << CR_CLKSRC_SHIFT)
+
+/*
+ * Exact clock frequencies vary from board to board.
+ * These are typical.
+ */
+static const uint32_t clocks[] = {
+ 0, /* disabled */
+ 53200000, /* ipg_clk, ~50MHz */
+ 53200000, /* ipg_clk_highfreq */
+ 32768, /* ipg_clk_32k -- ~32kHz */
+};
+
+
+typedef struct {
+ SysBusDevice busdev;
+ ptimer_state *timer;
+ MemoryRegion iomem;
+ uint32_t cr;
+ uint32_t lr;
+ uint32_t cmp;
+
+ uint32_t freq;
+ int int_level;
+ qemu_irq irq;
+} imx_timerp_state;
+
+/*
+ * Update interrupt status
+ */
+static void imx_timerp_update(imx_timerp_state *s)
+{
+ if (s->int_level && (s->cr & CR_OCIEN)) {
+ qemu_irq_raise(s->irq);
+ } else {
+ qemu_irq_lower(s->irq);
+ }
+}
+
+static void imx_timerp_reset(DeviceState *dev)
+{
+ imx_timerp_state *s = container_of(dev, imx_timerp_state, busdev.qdev);
+
+ s->cr = 0;
+ s->lr = 0xffffffff;
+ s->int_level = 0;
+ s->cmp = 0;
+ ptimer_stop(s->timer);
+ ptimer_set_count(s->timer, 0xffffffff);
+}
+
+static uint64_t imx_timerp_read(void *opaque, target_phys_addr_t offset,
+ unsigned size)
+{
+ imx_timerp_state *s = (imx_timerp_state *)opaque;
+
+ DPRINTF("p-read(offset=%x)\n", offset);
+ switch (offset >> 2) {
+ case 0: /* Control Register */
+ return s->cr;
+
+ case 1: /* Status Register */
+ return s->int_level;
+
+ case 2: /* LR - ticks*/
+ return s->lr;
+
+ case 3: /* CMP */
+ return s->cmp;
+
+ case 4: /* CNT */
+ return ptimer_get_count(s->timer);
+ }
+ IPRINTF("imx_timerp_read: Bad offset %x\n",
+ (int)offset >> 2);
+ return 0;
+}
+
+static void set_timerp_freq(imx_timerp_state *s)
+{
+ int clksrc;
+ unsigned prescaler;
+ uint32_t freq;
+
+ clksrc = (s->cr & CR_CLKSRC_MASK) >> CR_CLKSRC_SHIFT;
+ prescaler = 1 + ((s->cr & CR_PRESCALE_MASK) >> CR_PRESCALE_SHIFT);
+ DPRINTF("ptimer clksrc %d, prescaler %d", clksrc, prescaler);
+ freq = clocks[clksrc] / prescaler;
+
+ DPRINTF("Setting ptimer to frequency %d\n", freq);
+ s->freq = freq;
+
+ if (freq) {
+ ptimer_set_freq(s->timer, freq);
+ }
+}
+
+static void imx_timerp_write(void *opaque, target_phys_addr_t offset,
+ uint64_t value, unsigned size)
+{
+ imx_timerp_state *s = (imx_timerp_state *)opaque;
+ DPRINTF("p-write(offset=%x, value = %x)\n", (unsigned int)offset >> 2,
+ (unsigned int)value);
+
+ switch (offset >> 2) {
+ case 0: /* CR */
+ if (value & CR_SWR) {
+ imx_timerp_reset(&s->busdev.qdev);
+ value &= ~CR_SWR;
+ }
+ s->cr = value & 0x03ffffff;
+ set_timerp_freq(s);
+
+ if (s->freq && (s->cr & CR_EN)) {
+ ptimer_run(s->timer, 0);
+ } else {
+ ptimer_stop(s->timer);
+ }
+ break;
+
+ case 1: /* SR - ACK*/
+ s->int_level = 0;
+ imx_timerp_update(s);
+ break;
+
+ case 2: /* LR - set ticks */
+ s->lr = value;
+ ptimer_set_limit(s->timer, value, !!(s->cr & CR_IOVW));
+ break;
+
+ case 3: /* CMP */
+ s->cmp = value;
+ break;
+
+ default:
+ IPRINTF("imx_timerp_write: Bad offset %x\n",
+ (int)offset >> 2);
+ }
+}
+
+static void imx_timerp_tick(void *opaque)
+{
+ imx_timerp_state *s = (imx_timerp_state *)opaque;
+
+ s->int_level = 1;
+ if (!(s->cr & CR_RLD)) {
+ ptimer_set_count(s->timer, 0xffffffff);
+ }
+ imx_timerp_update(s);
+}
+
+static const MemoryRegionOps imx_timerp_ops = {
+ .read = imx_timerp_read,
+ .write = imx_timerp_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_imx_timerp = {
+ .name = "imx-timerp",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(cr, imx_timerp_state),
+ VMSTATE_UINT32(lr, imx_timerp_state),
+ VMSTATE_UINT32(cmp, imx_timerp_state),
+ VMSTATE_UINT32(freq, imx_timerp_state),
+ VMSTATE_INT32(int_level, imx_timerp_state),
+ VMSTATE_PTIMER(timer, imx_timerp_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int imx_timerp_init(SysBusDevice *dev)
+{
+ imx_timerp_state *s = FROM_SYSBUS(imx_timerp_state, dev);
+ QEMUBH *bh;
+
+ DPRINTF("imx_timerp_init\n");
+
+ sysbus_init_irq(dev, &s->irq);
+ memory_region_init_io(&s->iomem, &imx_timerp_ops,
+ s, "imxp-timer",
+ 0x00001000);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ bh = qemu_bh_new(imx_timerp_tick, s);
+ s->timer = ptimer_init(bh);
+
+ return 0;
+}
+
+static void imx_timerg_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+ k->init = imx_timerg_init;
+ dc->vmsd = &vmstate_imx_timerg;
+ dc->reset = imx_timerg_reset;
+ dc->desc = "i.MX general timer";
+}
+
+static void imx_timerp_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+ k->init = imx_timerp_init;
+ dc->vmsd = &vmstate_imx_timerp;
+ dc->reset = imx_timerp_reset;
+ dc->desc = "i.MX periodic timer";
+}
+
+static TypeInfo imx_timerp_info = {
+ .name = "imx_timerp",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(imx_timerp_state),
+ .class_init = imx_timerp_class_init,
+};
+
+static TypeInfo imx_timerg_info = {
+ .name = "imx_timerg",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(imx_timerg_state),
+ .class_init = imx_timerg_class_init,
+};
+
+static void imx_timer_register_types(void)
+{
+ type_register_static(&imx_timerp_info);
+ type_register_static(&imx_timerg_info);
+}
+
+type_init(imx_timer_register_types)
Index: qemu-working/Makefile.target
===================================================================
--- qemu-working.orig/Makefile.target 2012-03-09 14:13:50.038065728 +1100
+++ qemu-working/Makefile.target 2012-03-09 14:13:51.406084465 +1100
@@ -379,7 +379,7 @@ obj-arm-y += vexpress.o
obj-arm-y += strongarm.o
obj-arm-y += collie.o
obj-arm-y += pl041.o lm4549.o
-obj-arm-y += imx_serial.o
+obj-arm-y += imx_serial.o imx_timer.o
obj-arm-$(CONFIG_FDT) += device_tree.o
obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
^ permalink raw reply [flat|nested] 13+ messages in thread
* [Qemu-devel] [patch V4 3/4] FreeSCALE i.MX31 support: AVIC
2012-03-09 3:27 [Qemu-devel] [patch V4 0/4] Support for i.MX31 and the Kyoto KZM11 board peter.chubb
2012-03-09 3:27 ` [Qemu-devel] [patch V4 1/4] i.MX UART support peter.chubb
2012-03-09 3:27 ` [Qemu-devel] [patch V4 2/4] FreeSCALE i.MX31 support: Timers peter.chubb
@ 2012-03-09 3:27 ` peter.chubb
2012-03-15 17:14 ` Peter Maydell
2012-03-09 3:27 ` [Qemu-devel] [patch V4 4/4] FreeSCALE i.MX31 support: KZM-ARM11-01 evaluation board peter.chubb
3 siblings, 1 reply; 13+ messages in thread
From: peter.chubb @ 2012-03-09 3:27 UTC (permalink / raw)
To: peter.maydell, qemu-devel; +Cc: Peter Chubb, philipo
[-- Attachment #1: imx-avic.patch --]
[-- Type: text/plain, Size: 13648 bytes --]
Implement the FreeSCALE i.MX31 advanced vectored interrupt controller, at least
to the extent it is used by Linux 3.0.x
Vectors are not implemented.
Signed-off-by: Philip O'Sullivan <philipo@ok-labs.com>
Signed-off-by: Peter Chubb <peter.chubb@nicta.com.au>
---
Makefile.target | 2
hw/imx_avic.c | 409 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 410 insertions(+), 1 deletion(-)
create mode 100644 hw/imx_avic.c
Index: qemu-working/hw/imx_avic.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ qemu-working/hw/imx_avic.c 2012-03-09 14:13:52.878104591 +1100
@@ -0,0 +1,409 @@
+/*
+ * IMX31 Vectored Interrupt Controller
+ *
+ * Note this is NOT the PL192 provided by ARM, but
+ * a custom implementation by FreeScale.
+ *
+ * Copyright (c) 2008 OKL
+ * Copyright (c) 2011 NICTA Pty Ltd
+ * Originally Written by Hans Jiang
+ *
+ * This code is licenced under the GPL version 2 or later. See
+ * the COPYING file in the top-level directory.
+ *
+ * TODO: implement vectors.
+ */
+
+#include "hw.h"
+#include "sysbus.h"
+#include "host-utils.h"
+
+#define DEBUG_INT 1
+#undef DEBUG_INT /* comment out for debugging */
+
+#ifdef DEBUG_INT
+#define DPRINTF(fmt, args...) \
+do { printf("imx_avic: " fmt , ##args); } while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while (0)
+#endif
+
+/*
+ * Define to 1 for messages about attempts to
+ * access unimplemented registers or similar.
+ */
+#define DEBUG_IMPLEMENTATION 1
+#if DEBUG_IMPLEMENTATION
+# define IPRINTF(fmt, args...) \
+ do { fprintf(stderr, "imx_avic: " fmt, ##args); } while (0)
+#else
+# define IPRINTF(fmt, args...) do {} while (0)
+#endif
+
+#define IMX_AVIC_NUM_IRQS 64
+
+/* Interrupt Control Bits */
+#define ABFLAG (1<<25)
+#define ABFEN (1<<24)
+#define NIDIS (1<<22) /* Normal Interrupt disable */
+#define FIDIS (1<<21) /* Fast interrupt disable */
+#define NIAD (1<<20) /* Normal Interrupt Arbiter Rise ARM level */
+#define FIAD (1<<19) /* Fast Interrupt Arbiter Rise ARM level */
+#define NM (1<<18) /* Normal interrupt mode */
+
+
+#define PRIO_PER_WORD (sizeof(uint32_t) * 8 / 4)
+#define PRIO_WORDS (IMX_AVIC_NUM_IRQS/PRIO_PER_WORD)
+
+typedef struct {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ uint64_t pending;
+ uint64_t enabled;
+ uint64_t is_fiq;
+ uint32_t intcntl;
+ uint32_t intmask;
+ qemu_irq irq;
+ qemu_irq fiq;
+ uint32_t prio[PRIO_WORDS]; /* Priorities are 4-bits each */
+} imx_avic_state;
+
+static const VMStateDescription vmstate_imx_avic = {
+ .name = "imx-avic",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(pending, imx_avic_state),
+ VMSTATE_UINT64(enabled, imx_avic_state),
+ VMSTATE_UINT64(is_fiq, imx_avic_state),
+ VMSTATE_UINT32(intcntl, imx_avic_state),
+ VMSTATE_UINT32(intmask, imx_avic_state),
+ VMSTATE_UINT32_ARRAY(prio, imx_avic_state, PRIO_WORDS),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+
+
+static inline int imx_avic_prio(imx_avic_state *s, int irq)
+{
+ uint32_t word = irq / PRIO_PER_WORD;
+ uint32_t part = 4 * (irq % PRIO_PER_WORD);
+ return 0xf & (s->prio[word] >> part);
+}
+
+static inline void imx_avic_set_prio(imx_avic_state *s, int irq, int prio)
+{
+ uint32_t word = irq / PRIO_PER_WORD;
+ uint32_t part = 4 * (irq % PRIO_PER_WORD);
+ uint32_t mask = ~(0xf << part);
+ s->prio[word] &= mask;
+ s->prio[word] |= prio << part;
+}
+
+/* Update interrupts. */
+static void imx_avic_update(imx_avic_state *s)
+{
+ int i;
+ uint64_t new = s->pending & s->enabled;
+ uint64_t flags;
+
+ flags = new & s->is_fiq;
+ qemu_set_irq(s->fiq, !!flags);
+
+ flags = new & ~s->is_fiq;
+ if (!flags || (s->intmask == 0x1f)) {
+ qemu_set_irq(s->irq, !!flags);
+ return;
+ }
+
+ /*
+ * Take interrupt if there's a pending interrupt with
+ * priority higher than the value of intmask
+ */
+ for (i = 0; i < IMX_AVIC_NUM_IRQS; i++) {
+ if (flags & (1UL << i)) {
+ if (imx_avic_prio(s, i) > s->intmask) {
+ qemu_set_irq(s->irq, 1);
+ return;
+ }
+ }
+ }
+ qemu_set_irq(s->irq, 0);
+}
+
+static void imx_avic_set_irq(void *opaque, int irq, int level)
+{
+ imx_avic_state *s = (imx_avic_state *)opaque;
+
+ if (level) {
+ DPRINTF("Raising IRQ %d, prio %d\n",
+ irq, imx_avic_prio(s, irq));
+ s->pending |= (1ULL << irq);
+ } else {
+ DPRINTF("Clearing IRQ %d, prio %d\n",
+ irq, imx_avic_prio(s, irq));
+ s->pending &= ~(1ULL << irq);
+ }
+
+ imx_avic_update(s);
+}
+
+
+static uint64_t imx_avic_read(void *opaque,
+ target_phys_addr_t offset, unsigned size)
+{
+ imx_avic_state *s = (imx_avic_state *)opaque;
+
+
+ DPRINTF("read(offset = 0x%x)\n", offset >> 2);
+ switch (offset >> 2) {
+ case 0: /* INTCNTL */
+ return s->intcntl;
+
+ case 1: /* Normal Interrupt Mask Register, NIMASK */
+ return s->intmask;
+
+ case 2: /* Interrupt Enable Number Register, INTENNUM */
+ case 3: /* Interrupt Disable Number Register, INTDISNUM */
+ return 0;
+
+ case 4: /* Interrupt Enabled Number Register High */
+ return s->enabled >> 32;
+
+ case 5: /* Interrupt Enabled Number Register Low */
+ return s->enabled & 0xffffffffULL;
+
+ case 6: /* Interrupt Type Register High */
+ return s->is_fiq >> 32;
+
+ case 7: /* Interrupt Type Register Low */
+ return s->is_fiq & 0xffffffffULL;
+
+ case 8: /* Normal Interrupt Priority Register 7 */
+ case 9: /* Normal Interrupt Priority Register 6 */
+ case 10:/* Normal Interrupt Priority Register 5 */
+ case 11:/* Normal Interrupt Priority Register 4 */
+ case 12:/* Normal Interrupt Priority Register 3 */
+ case 13:/* Normal Interrupt Priority Register 2 */
+ case 14:/* Normal Interrupt Priority Register 1 */
+ case 15:/* Normal Interrupt Priority Register 0 */
+ return s->prio[15-(offset>>2)];
+
+ case 16: /* Normal interrupt vector and status register */
+ {
+ /*
+ * This returns the highest priority
+ * outstanding interrupt. Where there is more than
+ * one pending IRQ with the same priority,
+ * take the highest numbered one.
+ */
+ uint64_t flags = s->pending & s->enabled & ~s->is_fiq;
+ int i;
+ int prio = -1;
+ int irq = -1;
+ for (i = 63; i >= 0; --i) {
+ if (flags & (1ULL<<i)) {
+ int irq_prio = imx_avic_prio(s, i);
+ if (irq_prio > prio) {
+ irq = i;
+ prio = irq_prio;
+ }
+ }
+ }
+ if (irq >= 0) {
+ imx_avic_set_irq(s, irq, 0);
+ return irq << 16 | prio;
+ }
+ return 0xffffffffULL;
+ }
+ case 17:/* Fast Interrupt vector and status register */
+ {
+ uint64_t flags = s->pending & s->enabled & s->is_fiq;
+ int i = ctz64(flags);
+ if (i < 64) {
+ imx_avic_set_irq(opaque, i, 0);
+ return i;
+ }
+ return 0xffffffffULL;
+ }
+ case 18:/* Interrupt source register high */
+ return s->pending >> 32;
+
+ case 19:/* Interrupt source register low */
+ return s->pending & 0xffffffffULL;
+
+ case 20:/* Interrupt Force Register high */
+ case 21:/* Interrupt Force Register low */
+ return 0;
+
+ case 22:/* Normal Interrupt Pending Register High */
+ return (s->pending & s->enabled & ~s->is_fiq) >> 32;
+
+ case 23:/* Normal Interrupt Pending Register Low */
+ return (s->pending & s->enabled & ~s->is_fiq) & 0xffffffffULL;
+
+ case 24: /* Fast Interrupt Pending Register High */
+ return (s->pending & s->enabled & s->is_fiq) >> 32;
+
+ case 25: /* Fast Interrupt Pending Register Low */
+ return (s->pending & s->enabled & s->is_fiq) & 0xffffffffULL;
+
+ case 0x40: /* AVIC vector 0, use for WFI WAR */
+ return 0x4;
+
+ default:
+ IPRINTF("imx_avic_read: Bad offset 0x%x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void imx_avic_write(void *opaque, target_phys_addr_t offset,
+ uint64_t val, unsigned size)
+{
+ imx_avic_state *s = (imx_avic_state *)opaque;
+
+ /* Vector Registers not yet supported */
+ if (offset >= 0x100 && offset <= 0x2fc) {
+ IPRINTF("imx_avic_write to vector register %d ignored\n",
+ (offset - 0x100) >> 2);
+ return;
+ }
+
+ DPRINTF("imx_avic_write(0x%x) = %x\n",
+ (unsigned int)offset>>2, (unsigned int)val);
+ switch (offset >> 2) {
+ case 0: /* Interrupt Control Register, INTCNTL */
+ s->intcntl = val & (ABFEN | NIDIS | FIDIS | NIAD | FIAD | NM);
+ if (s->intcntl & ABFEN) {
+ s->intcntl &= ~(val & ABFLAG);
+ }
+ break;
+
+ case 1: /* Normal Interrupt Mask Register, NIMASK */
+ s->intmask = val & 0x1f;
+ break;
+
+ case 2: /* Interrupt Enable Number Register, INTENNUM */
+ DPRINTF("enable(%d)\n", (int)val);
+ val &= 0x3f;
+ s->enabled |= (1ULL << val);
+ break;
+
+ case 3: /* Interrupt Disable Number Register, INTDISNUM */
+ DPRINTF("disable(%d)\n", (int)val);
+ val &= 0x3f;
+ s->enabled &= ~(1ULL << val);
+ break;
+
+ case 4: /* Interrupt Enable Number Register High */
+ s->enabled = (s->enabled & 0xffffffffULL) | (val << 32);
+ break;
+
+ case 5: /* Interrupt Enable Number Register Low */
+ s->enabled = (s->enabled & 0xffffffff00000000ULL) | val;
+ break;
+
+ case 6: /* Interrupt Type Register High */
+ s->is_fiq = (s->is_fiq & 0xffffffffULL) | (val << 32);
+ break;
+
+ case 7: /* Interrupt Type Register Low */
+ s->is_fiq = (s->is_fiq & 0xffffffff00000000ULL) | val;
+ break;
+
+ case 8: /* Normal Interrupt Priority Register 7 */
+ case 9: /* Normal Interrupt Priority Register 6 */
+ case 10:/* Normal Interrupt Priority Register 5 */
+ case 11:/* Normal Interrupt Priority Register 4 */
+ case 12:/* Normal Interrupt Priority Register 3 */
+ case 13:/* Normal Interrupt Priority Register 2 */
+ case 14:/* Normal Interrupt Priority Register 1 */
+ case 15:/* Normal Interrupt Priority Register 0 */
+ s->prio[15-(offset>>2)] = val;
+ break;
+
+ /* Read-only registers, writes ignored */
+ case 16:/* Normal Interrupt Vector and Status register */
+ case 17:/* Fast Interrupt vector and status register */
+ case 18:/* Interrupt source register high */
+ case 19:/* Interrupt source register low */
+ return;
+
+ case 20:/* Interrupt Force Register high */
+ s->pending = (s->pending & 0xffffffffULL) | (val << 32);
+ break;
+
+ case 21:/* Interrupt Force Register low */
+ s->pending = (s->pending & 0xffffffff00000000ULL) | val;
+ break;
+
+ case 22:/* Normal Interrupt Pending Register High */
+ case 23:/* Normal Interrupt Pending Register Low */
+ case 24: /* Fast Interrupt Pending Register High */
+ case 25: /* Fast Interrupt Pending Register Low */
+ return;
+
+ default:
+ IPRINTF("imx_avic_write: Bad offset %x\n", (int)offset);
+ }
+ imx_avic_update(s);
+}
+
+static const MemoryRegionOps imx_avic_ops = {
+ .read = imx_avic_read,
+ .write = imx_avic_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void imx_avic_reset(DeviceState *dev)
+{
+ imx_avic_state *s = container_of(dev, imx_avic_state, busdev.qdev);
+ s->pending = 0;
+ s->enabled = 0;
+ s->is_fiq = 0;
+ s->intmask = 0x1f;
+ s->intcntl = 0;
+ memset(s->prio, 0, sizeof s->prio);
+}
+
+static int imx_avic_init(SysBusDevice *dev)
+{
+ imx_avic_state *s = FROM_SYSBUS(imx_avic_state, dev);;
+
+ memory_region_init_io(&s->iomem, &imx_avic_ops, s, "imx_avic", 0x1000);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ qdev_init_gpio_in(&dev->qdev, imx_avic_set_irq, IMX_AVIC_NUM_IRQS);
+ sysbus_init_irq(dev, &s->irq);
+ sysbus_init_irq(dev, &s->fiq);
+
+ return 0;
+}
+
+
+static void imx_avic_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+ k->init = imx_avic_init;
+ dc->vmsd = &vmstate_imx_avic;
+ dc->reset = imx_avic_reset;
+ dc->desc = "i.MX Advanced Vector Interrupt Controller";
+}
+
+static TypeInfo imx_avic_info = {
+ .name = "imx_avic",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(imx_avic_state),
+ .class_init = imx_avic_class_init,
+};
+
+static void imx_avic_register_types(void)
+{
+ type_register_static(&imx_avic_info);
+}
+
+type_init(imx_avic_register_types)
+
Index: qemu-working/Makefile.target
===================================================================
--- qemu-working.orig/Makefile.target 2012-03-09 14:13:51.754089227 +1100
+++ qemu-working/Makefile.target 2012-03-09 14:13:52.878104591 +1100
@@ -379,7 +379,7 @@ obj-arm-y += vexpress.o
obj-arm-y += strongarm.o
obj-arm-y += collie.o
obj-arm-y += pl041.o lm4549.o
-obj-arm-y += imx_serial.o imx_timer.o
+obj-arm-y += imx_serial.o imx_timer.o imx_avic.o
obj-arm-$(CONFIG_FDT) += device_tree.o
obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
^ permalink raw reply [flat|nested] 13+ messages in thread
* [Qemu-devel] [patch V4 4/4] FreeSCALE i.MX31 support: KZM-ARM11-01 evaluation board
2012-03-09 3:27 [Qemu-devel] [patch V4 0/4] Support for i.MX31 and the Kyoto KZM11 board peter.chubb
` (2 preceding siblings ...)
2012-03-09 3:27 ` [Qemu-devel] [patch V4 3/4] FreeSCALE i.MX31 support: AVIC peter.chubb
@ 2012-03-09 3:27 ` peter.chubb
2012-03-15 17:20 ` Peter Maydell
3 siblings, 1 reply; 13+ messages in thread
From: peter.chubb @ 2012-03-09 3:27 UTC (permalink / raw)
To: peter.maydell, qemu-devel; +Cc: Peter Chubb, philipo
[-- Attachment #1: kzm.patch --]
[-- Type: text/plain, Size: 7009 bytes --]
Board support for Kyoto Micro's KZM-ARM11-01, an evaluation board built
around the FreeScale i.MX31.
Signed-off-by: Philip O'Sullivan <philipo@ok-labs.com>
Signed-off-by: Peter Chubb <peter.chubb@nicta.com.au>
---
Makefile.target | 1
hw/kzm.c | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 160 insertions(+)
create mode 100644 hw/kzm.c
Index: qemu-working/hw/kzm.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ qemu-working/hw/kzm.c 2012-03-09 14:13:54.578127780 +1100
@@ -0,0 +1,159 @@
+/*
+ * KZM Board System emulation.
+ *
+ * Copyright (c) 2008 OKL and 2011 NICTA
+ * Written by Hans
+ * Updated by Peter Chubb.
+ *
+ * This code is licenced under the GPL, version 2 or later.
+ * See the file `COPYING' in the top level directory.
+ *
+ * It (partially) emulates a Kyoto Microcomputer
+ * KZM-ARM11-01 evaluation board, with a FreeScale
+ * I.MX31 SoC
+ */
+
+#include "sysbus.h"
+#include "exec-memory.h"
+#include "hw.h"
+#include "arm-misc.h"
+#include "primecell.h"
+#include "devices.h"
+#include "pci.h"
+#include "net.h"
+#include "sysemu.h"
+#include "boards.h"
+#include "pc.h" /* for the FPGA UART that emulates a 16550 */
+#include "imx.h"
+
+ /* Memory map for Kzm Emulation Baseboard:
+ * 0x00000000-0x00003fff 16k secure ROM IGNORED
+ * 0x00004000-0x00407fff Reserved IGNORED
+ * 0x00404000-0x00407fff ROM IGNORED
+ * 0x00408000-0x0fffffff Reserved IGNORED
+ * 0x10000000-0x1fffBfff RAM aliasing IGNORED
+ * 0x1fffc000-0x1fffffff RAM EMULATED
+ * 0x20000000-0x2fffffff Reserved IGNORED
+ * 0x30000000-0x7fffffff I.MX31 Internal Register Space
+ * 0x43f00000 IO_AREA0
+ * 0x43f90000 UART1 EMULATED
+ * 0x43f94000 UART2 EMULATED
+ * 0x68000000 PIC EMULATED
+ * 0x53f94000 PIT 1 EMULATED
+ * 0x53f98000 PIT 2 EMULATED
+ * 0x53f90000 GPT EMULATED
+ * 0x80000000-0x87ffffff RAM EMULATED
+ * 0x88000000-0x8fffffff RAM Aliasing EMULATED
+ * 0xa0000000-0xafffffff NAND Flash IGNORED
+ * 0xb0000000-0xb3ffffff Unavailable IGNORED
+ * 0xb4000000-0xb4000fff 8-bit free space IGNORED
+ * 0xb4001000-0xb400100f Board control IGNORED
+ * 0xb4001003 DIP switch
+ * 0xb4001010-0xb400101f 7-segment LED IGNORED
+ * 0xb4001020-0xb400102f LED IGNORED
+ * 0xb4001030-0xb400103f LED IGNORED
+ * 0xb4001040-0xb400104f FPGA, UART EMULATED
+ * 0xb4001050-0xb400105f FPGA, UART EMULATED
+ * 0xb4001060-0xb40fffff FPGA IGNORED
+ * 0xb6000000-0xb61fffff LAN controller EMULATED
+ * 0xb6200000-0xb62fffff FPGA NAND Controller IGNORED
+ * 0xb6300000-0xb7ffffff Free IGNORED
+ * 0xb8000000-0xb8004fff Memory control registers IGNORED
+ * 0xc0000000-0xc3ffffff PCMCIA/CF IGNORED
+ * 0xc4000000-0xffffffff Reserved IGNORED
+ */
+
+#define KZM_RAMADDRESS (0x80000000)
+#define KZM_FPGA (0xb4001040)
+
+static struct arm_boot_info kzm_binfo = {
+ .loader_start = KZM_RAMADDRESS,
+ .board_id = 1722,
+};
+
+static void kzm_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)
+{
+ CPUState *env;
+ MemoryRegion *address_space_mem = get_system_memory();
+ MemoryRegion *ram = g_new(MemoryRegion, 1);
+ MemoryRegion *sram = g_new(MemoryRegion, 1);
+ MemoryRegion *ram_alias = g_new(MemoryRegion, 1);
+ qemu_irq *cpu_pic;
+ DeviceState *dev;
+
+ if (!cpu_model) {
+ cpu_model = "arm1136";
+ }
+
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+
+ /* On a real system, the first 16k is a `secure boot rom' */
+
+ memory_region_init_ram(ram, "kzm.ram", ram_size);
+ vmstate_register_ram_global(ram);
+ memory_region_add_subregion(address_space_mem, KZM_RAMADDRESS, ram);
+
+ memory_region_init_alias(ram_alias, "ram.alias", ram, 0, ram_size);
+ memory_region_add_subregion(address_space_mem, 0x88000000, ram_alias);
+
+ memory_region_init_ram(sram, "kzm.sram", 0x4000);
+ memory_region_add_subregion(address_space_mem, 0x1FFFC000, sram);
+
+
+ cpu_pic = arm_pic_init_cpu(env);
+ dev = sysbus_create_varargs("imx_avic", 0x68000000,
+ cpu_pic[ARM_PIC_CPU_IRQ],
+ cpu_pic[ARM_PIC_CPU_FIQ], NULL);
+
+
+ imx_serial_create(0, 0x43f90000, qdev_get_gpio_in(dev, 45));
+ imx_serial_create(1, 0x43f94000, qdev_get_gpio_in(dev, 32));
+
+ sysbus_create_simple("imx_timerp", 0x53f94000, qdev_get_gpio_in(dev, 28));
+ sysbus_create_simple("imx_timerp", 0x53f98000, qdev_get_gpio_in(dev, 27));
+ sysbus_create_simple("imx_timerg", 0x53f90000, qdev_get_gpio_in(dev, 29));
+
+ if (nd_table[0].vlan) {
+ lan9118_init(&nd_table[0], 0xb6000000, qdev_get_gpio_in(dev, 52));
+ }
+
+ if (serial_hds[3]) {
+ serial_mm_init(address_space_mem, KZM_FPGA, 0,
+ qdev_get_gpio_in(dev, 52),
+ 14745600, serial_hds[3],
+ DEVICE_NATIVE_ENDIAN);
+ }
+ if (serial_hds[2]) { /* touchscreen */
+ serial_mm_init(address_space_mem, KZM_FPGA+0x10, 0,
+ qdev_get_gpio_in(dev, 52),
+ 14745600, serial_hds[2],
+ DEVICE_NATIVE_ENDIAN);
+ }
+
+ kzm_binfo.ram_size = ram_size;
+ kzm_binfo.kernel_filename = kernel_filename;
+ kzm_binfo.kernel_cmdline = kernel_cmdline;
+ kzm_binfo.initrd_filename = initrd_filename;
+ kzm_binfo.nb_cpus = 1;
+ arm_load_kernel(first_cpu, &kzm_binfo);
+}
+
+static QEMUMachine kzm_machine = {
+ .name = "kzm",
+ .desc = "ARM KZM Emulation Baseboard (ARM1136)",
+ .init = kzm_init,
+};
+
+static void kzm_machine_init(void)
+{
+ qemu_register_machine(&kzm_machine);
+}
+
+machine_init(kzm_machine_init);
Index: qemu-working/Makefile.target
===================================================================
--- qemu-working.orig/Makefile.target 2012-03-09 14:13:53.230109396 +1100
+++ qemu-working/Makefile.target 2012-03-09 14:13:54.578127780 +1100
@@ -380,6 +380,7 @@ obj-arm-y += strongarm.o
obj-arm-y += collie.o
obj-arm-y += pl041.o lm4549.o
obj-arm-y += imx_serial.o imx_timer.o imx_avic.o
+obj-arm-y += kzm.o
obj-arm-$(CONFIG_FDT) += device_tree.o
obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [patch V4 1/4] i.MX UART support
2012-03-09 3:27 ` [Qemu-devel] [patch V4 1/4] i.MX UART support peter.chubb
@ 2012-03-15 17:01 ` Peter Maydell
2012-03-15 21:10 ` Peter Chubb
0 siblings, 1 reply; 13+ messages in thread
From: Peter Maydell @ 2012-03-15 17:01 UTC (permalink / raw)
To: peter.chubb; +Cc: qemu-devel, philipo
On 9 March 2012 03:27, <peter.chubb@nicta.com.au> wrote:
> +typedef struct {
> + SysBusDevice busdev;
> + MemoryRegion iomem;
> + int32_t readbuff;
> +
> + uint32_t usr1;
> + uint32_t usr2;
> + uint32_t ucr1;
> + uint32_t ucr2;
> + uint32_t uts1;
> +
> + /*
> + * The registers below are implemented just so that the
> + * guest OS sees what it has written
> + */
> + uint32_t onems;
> + uint32_t ufcr;
> + uint32_t ubmr;
> + uint32_t ubrc;
> + uint32_t ucr3;
> +
> + qemu_irq irq;
> + CharDriverState *chr;
> +} imx_state;
CODING_STYLE says this should be CamelCase. Also shouldn't
the type name have the word 'serial' or 'uart' in it somewhere?
Otherwise looks good.
-- PMM
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [patch V4 2/4] FreeSCALE i.MX31 support: Timers
2012-03-09 3:27 ` [Qemu-devel] [patch V4 2/4] FreeSCALE i.MX31 support: Timers peter.chubb
@ 2012-03-15 17:08 ` Peter Maydell
2012-03-15 17:08 ` Peter Maydell
0 siblings, 1 reply; 13+ messages in thread
From: Peter Maydell @ 2012-03-15 17:08 UTC (permalink / raw)
To: peter.chubb; +Cc: qemu-devel, philipo
On 9 March 2012 03:27, <peter.chubb@nicta.com.au> wrote:
> Implement the timers on the FreeScale i.MX31 SoC.
> This is not a complete implementation, but gives enough for
> Linux to boot and run. In particular external triggers, which are
> not useful under QEMU, are not implemented.
CODING_STYLE wants CamelCase for typenames so 'IMXTimerGState' etc.
The rest looks OK to me but I'm not very good with qemu's timer
API. Paolo or Paul -- could you check this looks ok on that front?
thanks
-- PMM
> Signed-off-by: Philip O'Sullivan <philipo@ok-labs.com>
> Signed-off-by: Peter Chubb <peter.chubb@nicta.com.au>
> ---
> Makefile.target | 2
> hw/imx_timer.c | 575 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 576 insertions(+), 1 deletion(-)
> create mode 100644 hw/imx_timer.c
>
> Index: qemu-working/hw/imx_timer.c
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ qemu-working/hw/imx_timer.c 2012-03-09 14:13:51.406084465 +1100
> @@ -0,0 +1,575 @@
> +/*
> + * IMX31 Timer
> + *
> + * Copyright (c) 2008 OK Labs
> + * Copyright (c) 2011 NICTA Pty Ltd
> + * Originally Written by Hans Jiang
> + * Updated by Peter Chubb
> + *
> + * This code is licenced under GPL version 2 or later. See
> + * the COPYING file in the top-level directory.
> + */
> +
> +#include "hw.h"
> +#include "qemu-timer.h"
> +#include "ptimer.h"
> +#include "sysbus.h"
> +
> +//#define DEBUG_TIMER 1
> +#ifdef DEBUG_TIMER
> +# define DPRINTF(fmt, args...) \
> + do { printf("imx_timer: " fmt , ##args); } while (0)
> +#else
> +# define DPRINTF(fmt, args...) do {} while (0)
> +#endif
> +
> +/*
> + * Define to 1 for messages about attempts to
> + * access unimplemented registers or similar.
> + */
> +#define DEBUG_IMPLEMENTATION 1
> +#if DEBUG_IMPLEMENTATION
> +# define IPRINTF(fmt, args...) \
> + do { fprintf(stderr, "imx_timer: " fmt, ##args); } while (0)
> +#else
> +# define IPRINTF(fmt, args...) do {} while (0)
> +#endif
> +
> +/*
> + * GPT : General purpose timer
> + *
> + * This timer counts up continuously while it is enabled, resetting itself
> + * to 0 after it reaches TIMER_MAX (in freerun mode) or when it
> + * reaches the value of ocr1 (in periodic mode). Unfortunately, the
> + * Qemu ptimer abstraction doesn't have a mode like this, so the code
> + * uses Qemu timers directly.
> + *
> + * The code emulates a free-running timer by using Qemu's nanosecond
> + * clock, suitably scaled, using the remainder after dividing by the
> + * timer's period. In the real hardware, there are three comparison
> + * registers that can trigger interrupts, and compare channel 1 can be
> + * used to force-reset the timer. However, this is a `bare-bones'
> + * implementation: only what Linux 3.0.x uses has been implemented
> + * (free-running timer from 0 to OCR1 or TIMER_MAX) Likewise, only a
> + * single frequency is implemented, rather than all the complicated
> + * clock-source and prescaling logic that the real hardware implements
> + * (most of which doesn't make sense on Qemu)
> + */
> +
> +#define TIMER_MAX 0XFFFFFFFFULL
> +#define GPT_FREQ 50000000 /* Hz == 50 MHz */
> +
> +/* Control register. Not all of these bits have any effect (yet) */
> +#define GPT_CR_EN (1 << 0) /* GPT Enable */
> +#define GPT_CR_ENMOD (1 << 1) /* GPT Enable Mode */
> +#define GPT_CR_DBGEN (1 << 2) /* GPT Debug mode enable */
> +#define GPT_CR_WAITEN (1 << 3) /* GPT Wait Mode Enable */
> +#define GPT_CR_DOZEN (1 << 4) /* GPT Doze mode enable */
> +#define GPT_CR_STOPEN (1 << 5) /* GPT Stop Mode Enable */
> +#define GPT_CR_CLKSRC (7 << 6) /* Clock source select (3 bits) */
> +#define GPT_CR_FRR (1 << 9) /* Freerun or Restart */
> +#define GPT_CR_SWR (1 << 15) /* Software Reset */
> +#define GPT_CR_IM1 (3 << 16) /* Input capture channel 1 mode (2 bits) */
> +#define GPT_CR_IM2 (3 << 18) /* Input capture channel 2 mode (2 bits) */
> +#define GPT_CR_OM1 (7 << 20) /* Output Compare Channel 1 Mode (3 bits) */
> +#define GPT_CR_OM2 (7 << 23) /* Output Compare Channel 2 Mode (3 bits) */
> +#define GPT_CR_OM3 (7 << 26) /* Output Compare Channel 3 Mode (3 bits) */
> +#define GPT_CR_FO1 (1 << 29) /* Force Output Compare Channel 1 */
> +#define GPT_CR_FO2 (1 << 30) /* Force Output Compare Channel 2 */
> +#define GPT_CR_FO3 (1 << 31) /* Force Output Compare Channel 3 */
> +
> +#define GPT_SR_OF1 (1 << 0)
> +#define GPT_SR_ROV (1 << 5)
> +
> +#define GPT_IR_OF1IE (1 << 0)
> +#define GPT_IR_ROVIE (1 << 5)
> +
> +typedef struct {
> + SysBusDevice busdev;
> + QEMUTimer *timer;
> + MemoryRegion iomem;
> + uint32_t cr;
> + uint32_t pr;
> + uint32_t sr;
> + uint32_t ir;
> + uint32_t ocr1;
> + uint32_t cnt;
> +
> + uint32_t waiting_rov;
> + qemu_irq irq;
> +} imx_timerg_state;
> +
> +static const VMStateDescription vmstate_imx_timerg = {
> + .name = "imx-timerg",
> + .version_id = 1,
> + .minimum_version_id = 1,
> + .minimum_version_id_old = 1,
> + .fields = (VMStateField[]) {
> + VMSTATE_UINT32(cr, imx_timerg_state),
> + VMSTATE_UINT32(pr, imx_timerg_state),
> + VMSTATE_UINT32(sr, imx_timerg_state),
> + VMSTATE_UINT32(ir, imx_timerg_state),
> + VMSTATE_UINT32(ocr1, imx_timerg_state),
> + VMSTATE_UINT32(cnt, imx_timerg_state),
> + VMSTATE_UINT32(waiting_rov, imx_timerg_state),
> + VMSTATE_TIMER(timer, imx_timerg_state),
> + VMSTATE_END_OF_LIST()
> + }
> +};
> +
> +
> +static void imx_timerg_update(imx_timerg_state *s)
> +{
> + uint32_t flags = s->sr & s->ir & (GPT_SR_OF1 | GPT_SR_ROV);
> +
> + DPRINTF("g-timer SR: %s %s IR=%s %s, %s\n",
> + s->sr & GPT_SR_OF1 ? "OF1" : "",
> + s->sr & GPT_SR_ROV ? "ROV" : "",
> + s->ir & GPT_SR_OF1 ? "OF1" : "",
> + s->ir & GPT_SR_ROV ? "ROV" : "",
> + s->cr & GPT_CR_EN ? "CR_EN" : "Not Enabled");
> +
> +
> + qemu_set_irq(s->irq, (s->cr & GPT_CR_EN) && flags);
> +}
> +
> +static uint64_t imx_timerg_update_count(imx_timerg_state *s)
> +{
> + uint64_t clk = qemu_get_clock_ns(vm_clock);
> + uint64_t period = (s->cr & GPT_CR_FRR) ? TIMER_MAX + 1 : s->ocr1;
> +
> + s->cnt = (uint32_t)(muldiv64(clk, GPT_FREQ/1000000,
> + 1000) % period);
> + return clk;
> +}
> +
> +static void imx_timerg_run(imx_timerg_state *s, uint32_t timeout)
> +{
> + uint64_t clk = imx_timerg_update_count(s);
> + uint32_t diff_cnt;
> +
> + /*
> + * For small timeouts, qemu sometimes runs too slow.
> + * Better deliver a late interrupt than none.
> + */
> + if (timeout) {
> + DPRINTF("g-run: s->cnt %u < timout %u\n", s->cnt, timeout);
> + if (timeout > s->cnt) {
> + diff_cnt = (timeout - s->cnt);
> + } else {
> + diff_cnt = timeout;
> + }
> + s->waiting_rov = 0;
> + } else {
> + DPRINTF("g-run, FRR\n");
> + diff_cnt = (TIMER_MAX + 1) - s->cnt;
> + s->waiting_rov = 1;
> + }
> + qemu_mod_timer(s->timer, clk + diff_cnt * 1000 / (GPT_FREQ/1000000));
> +}
> +
> +static uint64_t imx_timerg_read(void *opaque, target_phys_addr_t offset,
> + unsigned size)
> +{
> + imx_timerg_state *s = (imx_timerg_state *)opaque;
> +
> + DPRINTF("g-read(offset=%x)\n", offset >> 2);
> + switch (offset >> 2) {
> + case 0: /* Control Register */
> + return s->cr;
> +
> + case 1: /* prescaler */
> + return s->pr;
> +
> + case 2: /* Status Register */
> + return s->sr;
> +
> + case 3: /* Interrupt Register */
> + return s->ir;
> +
> + case 4: /* Output Compare Register 1 */
> + return s->ocr1;
> +
> + case 9: /* cnt */
> + imx_timerg_update_count(s);
> + return s->cnt;
> + }
> +
> + IPRINTF("imx_timerg_read: Bad offset %x\n",
> + (int)offset >> 2);
> + return 0;
> +}
> +
> +static void imx_timerg_reset(DeviceState *dev)
> +{
> + imx_timerg_state *s = container_of(dev, imx_timerg_state, busdev.qdev);
> +
> + /*
> + * Soft reset doesn't touch some bits; hard reset clears them
> + */
> + s->cr &= ~(GPT_CR_EN|GPT_CR_DOZEN|GPT_CR_WAITEN|GPT_CR_DBGEN);
> + s->sr = 0;
> + s->pr = 0;
> + s->ir = 0;
> + s->cnt = 0;
> + s->ocr1 = TIMER_MAX;
> + imx_timerg_update_count(s);
> +}
> +
> +static void imx_timerg_write(void *opaque, target_phys_addr_t offset,
> + uint64_t value, unsigned size)
> +{
> + imx_timerg_state *s = (imx_timerg_state *)opaque;
> + DPRINTF("g-write(offset=%x, value = %x)\n", (unsigned int)offset >> 2,
> + (unsigned int)value);
> +
> + switch (offset >> 2) {
> + case 0: /* CR */
> + if (value & GPT_CR_SWR) { /* force reset */
> + value &= ~GPT_CR_SWR;
> + imx_timerg_reset(&s->busdev.qdev);
> + imx_timerg_update(s);
> + }
> + if (!(s->cr & GPT_CR_EN) && (value & GPT_CR_EN)) {
> + if (value & GPT_CR_ENMOD) {
> + s->cnt = 0;
> + }
> + imx_timerg_run(s, s->ocr1);
> + } else if ((s->cr & GPT_CR_EN) && !(value & GPT_CR_EN)) {
> + qemu_del_timer(s->timer);
> + };
> +
> + s->cr = value & ~0x7c;
> + return;
> +
> + case 1: /* Prescaler */
> + s->pr = value & 0xfff;
> + return;
> +
> + case 2: /* SR */
> + /*
> + * No point in implementing the status register bits to do with
> + * external interrupt sources.
> + */
> + value &= GPT_SR_OF1 | GPT_SR_ROV;
> + s->sr &= ~value;
> + imx_timerg_update(s);
> + return;
> +
> + case 3: /* IR -- interrupt register */
> + s->ir = value & 0x3f;
> + imx_timerg_update(s);
> + return;
> +
> + case 4: /* OCR1 -- output compare register */
> + /* In non-freerun mode, reset count when this register is written &*/
> + s->ocr1 = value ;
> + if (!(s->cr & GPT_CR_FRR)) {
> + s->cnt = 0;
> + }
> + if (s->cr & GPT_CR_EN) {
> + imx_timerg_run(s, s->ocr1);
> + }
> + return;
> +
> + default:
> + IPRINTF("imx_timerg_write: Bad offset %x\n",
> + (int)offset >> 2);
> + }
> +}
> +
> +static void imx_timerg_timeout(void *opaque)
> +{
> + imx_timerg_state *s = (imx_timerg_state *)opaque;
> +
> + DPRINTF("imx_timerg_timeout, waiting rov=%d\n", s->waiting_rov);
> + if (s->waiting_rov) {
> + s->sr |= GPT_SR_ROV;
> + if (s->ocr1 == TIMER_MAX) {
> + s->sr |= GPT_SR_OF1;
> + }
> + imx_timerg_run(s, s->ocr1);
> + } else {
> + s->sr |= GPT_SR_OF1;
> + imx_timerg_run(s, 0);
> + }
> + imx_timerg_update(s);
> +}
> +
> +static const MemoryRegionOps imx_timerg_ops = {
> + .read = imx_timerg_read,
> + .write = imx_timerg_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +
> +static int imx_timerg_init(SysBusDevice *dev)
> +{
> + imx_timerg_state *s = FROM_SYSBUS(imx_timerg_state, dev);
> +
> + sysbus_init_irq(dev, &s->irq);
> + memory_region_init_io(&s->iomem, &imx_timerg_ops,
> + s, "imxg-timer",
> + 0x00001000);
> + sysbus_init_mmio(dev, &s->iomem);
> +
> + s->timer = qemu_new_timer_ns(vm_clock, imx_timerg_timeout, s);
> + /* Hard reset resets extra bits in CR */
> + s->cr = 0;
> + return 0;
> +}
> +
> +
> +
> +/*
> + * EPIT: Enhanced periodic interrupt timer
> + */
> +
> +#define TIMER_TICK_LENGTH 5000
> +
> +#define CR_EN (1 << 0)
> +#define CR_ENMOD (1 << 1)
> +#define CR_OCIEN (1 << 2)
> +#define CR_RLD (1 << 3)
> +#define CR_PRESCALE_SHIFT (4)
> +#define CR_PRESCALE_MASK (0xfff << CR_PRESCALE_SHIFT)
> +#define CR_SWR (1 << 16)
> +#define CR_IOVW (1 << 17)
> +#define CR_DBGEN (1 << 18)
> +#define CR_EPIT (1 << 19)
> +#define CR_DOZEN (1 << 20)
> +#define CR_STOPEN (1 << 21)
> +#define CR_CLKSRC_SHIFT (24)
> +#define CR_CLKSRC_MASK (0x3 << CR_CLKSRC_SHIFT)
> +
> +/*
> + * Exact clock frequencies vary from board to board.
> + * These are typical.
> + */
> +static const uint32_t clocks[] = {
> + 0, /* disabled */
> + 53200000, /* ipg_clk, ~50MHz */
> + 53200000, /* ipg_clk_highfreq */
> + 32768, /* ipg_clk_32k -- ~32kHz */
> +};
> +
> +
> +typedef struct {
> + SysBusDevice busdev;
> + ptimer_state *timer;
> + MemoryRegion iomem;
> + uint32_t cr;
> + uint32_t lr;
> + uint32_t cmp;
> +
> + uint32_t freq;
> + int int_level;
> + qemu_irq irq;
> +} imx_timerp_state;
> +
> +/*
> + * Update interrupt status
> + */
> +static void imx_timerp_update(imx_timerp_state *s)
> +{
> + if (s->int_level && (s->cr & CR_OCIEN)) {
> + qemu_irq_raise(s->irq);
> + } else {
> + qemu_irq_lower(s->irq);
> + }
> +}
> +
> +static void imx_timerp_reset(DeviceState *dev)
> +{
> + imx_timerp_state *s = container_of(dev, imx_timerp_state, busdev.qdev);
> +
> + s->cr = 0;
> + s->lr = 0xffffffff;
> + s->int_level = 0;
> + s->cmp = 0;
> + ptimer_stop(s->timer);
> + ptimer_set_count(s->timer, 0xffffffff);
> +}
> +
> +static uint64_t imx_timerp_read(void *opaque, target_phys_addr_t offset,
> + unsigned size)
> +{
> + imx_timerp_state *s = (imx_timerp_state *)opaque;
> +
> + DPRINTF("p-read(offset=%x)\n", offset);
> + switch (offset >> 2) {
> + case 0: /* Control Register */
> + return s->cr;
> +
> + case 1: /* Status Register */
> + return s->int_level;
> +
> + case 2: /* LR - ticks*/
> + return s->lr;
> +
> + case 3: /* CMP */
> + return s->cmp;
> +
> + case 4: /* CNT */
> + return ptimer_get_count(s->timer);
> + }
> + IPRINTF("imx_timerp_read: Bad offset %x\n",
> + (int)offset >> 2);
> + return 0;
> +}
> +
> +static void set_timerp_freq(imx_timerp_state *s)
> +{
> + int clksrc;
> + unsigned prescaler;
> + uint32_t freq;
> +
> + clksrc = (s->cr & CR_CLKSRC_MASK) >> CR_CLKSRC_SHIFT;
> + prescaler = 1 + ((s->cr & CR_PRESCALE_MASK) >> CR_PRESCALE_SHIFT);
> + DPRINTF("ptimer clksrc %d, prescaler %d", clksrc, prescaler);
> + freq = clocks[clksrc] / prescaler;
> +
> + DPRINTF("Setting ptimer to frequency %d\n", freq);
> + s->freq = freq;
> +
> + if (freq) {
> + ptimer_set_freq(s->timer, freq);
> + }
> +}
> +
> +static void imx_timerp_write(void *opaque, target_phys_addr_t offset,
> + uint64_t value, unsigned size)
> +{
> + imx_timerp_state *s = (imx_timerp_state *)opaque;
> + DPRINTF("p-write(offset=%x, value = %x)\n", (unsigned int)offset >> 2,
> + (unsigned int)value);
> +
> + switch (offset >> 2) {
> + case 0: /* CR */
> + if (value & CR_SWR) {
> + imx_timerp_reset(&s->busdev.qdev);
> + value &= ~CR_SWR;
> + }
> + s->cr = value & 0x03ffffff;
> + set_timerp_freq(s);
> +
> + if (s->freq && (s->cr & CR_EN)) {
> + ptimer_run(s->timer, 0);
> + } else {
> + ptimer_stop(s->timer);
> + }
> + break;
> +
> + case 1: /* SR - ACK*/
> + s->int_level = 0;
> + imx_timerp_update(s);
> + break;
> +
> + case 2: /* LR - set ticks */
> + s->lr = value;
> + ptimer_set_limit(s->timer, value, !!(s->cr & CR_IOVW));
> + break;
> +
> + case 3: /* CMP */
> + s->cmp = value;
> + break;
> +
> + default:
> + IPRINTF("imx_timerp_write: Bad offset %x\n",
> + (int)offset >> 2);
> + }
> +}
> +
> +static void imx_timerp_tick(void *opaque)
> +{
> + imx_timerp_state *s = (imx_timerp_state *)opaque;
> +
> + s->int_level = 1;
> + if (!(s->cr & CR_RLD)) {
> + ptimer_set_count(s->timer, 0xffffffff);
> + }
> + imx_timerp_update(s);
> +}
> +
> +static const MemoryRegionOps imx_timerp_ops = {
> + .read = imx_timerp_read,
> + .write = imx_timerp_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +static const VMStateDescription vmstate_imx_timerp = {
> + .name = "imx-timerp",
> + .version_id = 1,
> + .minimum_version_id = 1,
> + .minimum_version_id_old = 1,
> + .fields = (VMStateField[]) {
> + VMSTATE_UINT32(cr, imx_timerp_state),
> + VMSTATE_UINT32(lr, imx_timerp_state),
> + VMSTATE_UINT32(cmp, imx_timerp_state),
> + VMSTATE_UINT32(freq, imx_timerp_state),
> + VMSTATE_INT32(int_level, imx_timerp_state),
> + VMSTATE_PTIMER(timer, imx_timerp_state),
> + VMSTATE_END_OF_LIST()
> + }
> +};
> +
> +static int imx_timerp_init(SysBusDevice *dev)
> +{
> + imx_timerp_state *s = FROM_SYSBUS(imx_timerp_state, dev);
> + QEMUBH *bh;
> +
> + DPRINTF("imx_timerp_init\n");
> +
> + sysbus_init_irq(dev, &s->irq);
> + memory_region_init_io(&s->iomem, &imx_timerp_ops,
> + s, "imxp-timer",
> + 0x00001000);
> + sysbus_init_mmio(dev, &s->iomem);
> +
> + bh = qemu_bh_new(imx_timerp_tick, s);
> + s->timer = ptimer_init(bh);
> +
> + return 0;
> +}
> +
> +static void imx_timerg_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> + k->init = imx_timerg_init;
> + dc->vmsd = &vmstate_imx_timerg;
> + dc->reset = imx_timerg_reset;
> + dc->desc = "i.MX general timer";
> +}
> +
> +static void imx_timerp_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> + k->init = imx_timerp_init;
> + dc->vmsd = &vmstate_imx_timerp;
> + dc->reset = imx_timerp_reset;
> + dc->desc = "i.MX periodic timer";
> +}
> +
> +static TypeInfo imx_timerp_info = {
> + .name = "imx_timerp",
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(imx_timerp_state),
> + .class_init = imx_timerp_class_init,
> +};
> +
> +static TypeInfo imx_timerg_info = {
> + .name = "imx_timerg",
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(imx_timerg_state),
> + .class_init = imx_timerg_class_init,
> +};
> +
> +static void imx_timer_register_types(void)
> +{
> + type_register_static(&imx_timerp_info);
> + type_register_static(&imx_timerg_info);
> +}
> +
> +type_init(imx_timer_register_types)
> Index: qemu-working/Makefile.target
> ===================================================================
> --- qemu-working.orig/Makefile.target 2012-03-09 14:13:50.038065728 +1100
> +++ qemu-working/Makefile.target 2012-03-09 14:13:51.406084465 +1100
> @@ -379,7 +379,7 @@ obj-arm-y += vexpress.o
> obj-arm-y += strongarm.o
> obj-arm-y += collie.o
> obj-arm-y += pl041.o lm4549.o
> -obj-arm-y += imx_serial.o
> +obj-arm-y += imx_serial.o imx_timer.o
> obj-arm-$(CONFIG_FDT) += device_tree.o
>
> obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
>
--
12345678901234567890123456789012345678901234567890123456789012345678901234567890
1 2 3 4 5 6 7 8
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [patch V4 2/4] FreeSCALE i.MX31 support: Timers
2012-03-15 17:08 ` Peter Maydell
@ 2012-03-15 17:08 ` Peter Maydell
2012-03-15 21:02 ` Peter Chubb
0 siblings, 1 reply; 13+ messages in thread
From: Peter Maydell @ 2012-03-15 17:08 UTC (permalink / raw)
To: peter.chubb; +Cc: Paolo Bonzini, qemu-devel, philipo, Paul Brook
...that would work better if I cc'd the people I meant to...
On 15 March 2012 17:08, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 9 March 2012 03:27, <peter.chubb@nicta.com.au> wrote:
>> Implement the timers on the FreeScale i.MX31 SoC.
>> This is not a complete implementation, but gives enough for
>> Linux to boot and run. In particular external triggers, which are
>> not useful under QEMU, are not implemented.
>
>
> CODING_STYLE wants CamelCase for typenames so 'IMXTimerGState' etc.
>
> The rest looks OK to me but I'm not very good with qemu's timer
> API. Paolo or Paul -- could you check this looks ok on that front?
>
> thanks
> -- PMM
>
>> Signed-off-by: Philip O'Sullivan <philipo@ok-labs.com>
>> Signed-off-by: Peter Chubb <peter.chubb@nicta.com.au>
>> ---
>> Makefile.target | 2
>> hw/imx_timer.c | 575 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>> 2 files changed, 576 insertions(+), 1 deletion(-)
>> create mode 100644 hw/imx_timer.c
>>
>> Index: qemu-working/hw/imx_timer.c
>> ===================================================================
>> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
>> +++ qemu-working/hw/imx_timer.c 2012-03-09 14:13:51.406084465 +1100
>> @@ -0,0 +1,575 @@
>> +/*
>> + * IMX31 Timer
>> + *
>> + * Copyright (c) 2008 OK Labs
>> + * Copyright (c) 2011 NICTA Pty Ltd
>> + * Originally Written by Hans Jiang
>> + * Updated by Peter Chubb
>> + *
>> + * This code is licenced under GPL version 2 or later. See
>> + * the COPYING file in the top-level directory.
>> + */
>> +
>> +#include "hw.h"
>> +#include "qemu-timer.h"
>> +#include "ptimer.h"
>> +#include "sysbus.h"
>> +
>> +//#define DEBUG_TIMER 1
>> +#ifdef DEBUG_TIMER
>> +# define DPRINTF(fmt, args...) \
>> + do { printf("imx_timer: " fmt , ##args); } while (0)
>> +#else
>> +# define DPRINTF(fmt, args...) do {} while (0)
>> +#endif
>> +
>> +/*
>> + * Define to 1 for messages about attempts to
>> + * access unimplemented registers or similar.
>> + */
>> +#define DEBUG_IMPLEMENTATION 1
>> +#if DEBUG_IMPLEMENTATION
>> +# define IPRINTF(fmt, args...) \
>> + do { fprintf(stderr, "imx_timer: " fmt, ##args); } while (0)
>> +#else
>> +# define IPRINTF(fmt, args...) do {} while (0)
>> +#endif
>> +
>> +/*
>> + * GPT : General purpose timer
>> + *
>> + * This timer counts up continuously while it is enabled, resetting itself
>> + * to 0 after it reaches TIMER_MAX (in freerun mode) or when it
>> + * reaches the value of ocr1 (in periodic mode). Unfortunately, the
>> + * Qemu ptimer abstraction doesn't have a mode like this, so the code
>> + * uses Qemu timers directly.
>> + *
>> + * The code emulates a free-running timer by using Qemu's nanosecond
>> + * clock, suitably scaled, using the remainder after dividing by the
>> + * timer's period. In the real hardware, there are three comparison
>> + * registers that can trigger interrupts, and compare channel 1 can be
>> + * used to force-reset the timer. However, this is a `bare-bones'
>> + * implementation: only what Linux 3.0.x uses has been implemented
>> + * (free-running timer from 0 to OCR1 or TIMER_MAX) Likewise, only a
>> + * single frequency is implemented, rather than all the complicated
>> + * clock-source and prescaling logic that the real hardware implements
>> + * (most of which doesn't make sense on Qemu)
>> + */
>> +
>> +#define TIMER_MAX 0XFFFFFFFFULL
>> +#define GPT_FREQ 50000000 /* Hz == 50 MHz */
>> +
>> +/* Control register. Not all of these bits have any effect (yet) */
>> +#define GPT_CR_EN (1 << 0) /* GPT Enable */
>> +#define GPT_CR_ENMOD (1 << 1) /* GPT Enable Mode */
>> +#define GPT_CR_DBGEN (1 << 2) /* GPT Debug mode enable */
>> +#define GPT_CR_WAITEN (1 << 3) /* GPT Wait Mode Enable */
>> +#define GPT_CR_DOZEN (1 << 4) /* GPT Doze mode enable */
>> +#define GPT_CR_STOPEN (1 << 5) /* GPT Stop Mode Enable */
>> +#define GPT_CR_CLKSRC (7 << 6) /* Clock source select (3 bits) */
>> +#define GPT_CR_FRR (1 << 9) /* Freerun or Restart */
>> +#define GPT_CR_SWR (1 << 15) /* Software Reset */
>> +#define GPT_CR_IM1 (3 << 16) /* Input capture channel 1 mode (2 bits) */
>> +#define GPT_CR_IM2 (3 << 18) /* Input capture channel 2 mode (2 bits) */
>> +#define GPT_CR_OM1 (7 << 20) /* Output Compare Channel 1 Mode (3 bits) */
>> +#define GPT_CR_OM2 (7 << 23) /* Output Compare Channel 2 Mode (3 bits) */
>> +#define GPT_CR_OM3 (7 << 26) /* Output Compare Channel 3 Mode (3 bits) */
>> +#define GPT_CR_FO1 (1 << 29) /* Force Output Compare Channel 1 */
>> +#define GPT_CR_FO2 (1 << 30) /* Force Output Compare Channel 2 */
>> +#define GPT_CR_FO3 (1 << 31) /* Force Output Compare Channel 3 */
>> +
>> +#define GPT_SR_OF1 (1 << 0)
>> +#define GPT_SR_ROV (1 << 5)
>> +
>> +#define GPT_IR_OF1IE (1 << 0)
>> +#define GPT_IR_ROVIE (1 << 5)
>> +
>> +typedef struct {
>> + SysBusDevice busdev;
>> + QEMUTimer *timer;
>> + MemoryRegion iomem;
>> + uint32_t cr;
>> + uint32_t pr;
>> + uint32_t sr;
>> + uint32_t ir;
>> + uint32_t ocr1;
>> + uint32_t cnt;
>> +
>> + uint32_t waiting_rov;
>> + qemu_irq irq;
>> +} imx_timerg_state;
>> +
>> +static const VMStateDescription vmstate_imx_timerg = {
>> + .name = "imx-timerg",
>> + .version_id = 1,
>> + .minimum_version_id = 1,
>> + .minimum_version_id_old = 1,
>> + .fields = (VMStateField[]) {
>> + VMSTATE_UINT32(cr, imx_timerg_state),
>> + VMSTATE_UINT32(pr, imx_timerg_state),
>> + VMSTATE_UINT32(sr, imx_timerg_state),
>> + VMSTATE_UINT32(ir, imx_timerg_state),
>> + VMSTATE_UINT32(ocr1, imx_timerg_state),
>> + VMSTATE_UINT32(cnt, imx_timerg_state),
>> + VMSTATE_UINT32(waiting_rov, imx_timerg_state),
>> + VMSTATE_TIMER(timer, imx_timerg_state),
>> + VMSTATE_END_OF_LIST()
>> + }
>> +};
>> +
>> +
>> +static void imx_timerg_update(imx_timerg_state *s)
>> +{
>> + uint32_t flags = s->sr & s->ir & (GPT_SR_OF1 | GPT_SR_ROV);
>> +
>> + DPRINTF("g-timer SR: %s %s IR=%s %s, %s\n",
>> + s->sr & GPT_SR_OF1 ? "OF1" : "",
>> + s->sr & GPT_SR_ROV ? "ROV" : "",
>> + s->ir & GPT_SR_OF1 ? "OF1" : "",
>> + s->ir & GPT_SR_ROV ? "ROV" : "",
>> + s->cr & GPT_CR_EN ? "CR_EN" : "Not Enabled");
>> +
>> +
>> + qemu_set_irq(s->irq, (s->cr & GPT_CR_EN) && flags);
>> +}
>> +
>> +static uint64_t imx_timerg_update_count(imx_timerg_state *s)
>> +{
>> + uint64_t clk = qemu_get_clock_ns(vm_clock);
>> + uint64_t period = (s->cr & GPT_CR_FRR) ? TIMER_MAX + 1 : s->ocr1;
>> +
>> + s->cnt = (uint32_t)(muldiv64(clk, GPT_FREQ/1000000,
>> + 1000) % period);
>> + return clk;
>> +}
>> +
>> +static void imx_timerg_run(imx_timerg_state *s, uint32_t timeout)
>> +{
>> + uint64_t clk = imx_timerg_update_count(s);
>> + uint32_t diff_cnt;
>> +
>> + /*
>> + * For small timeouts, qemu sometimes runs too slow.
>> + * Better deliver a late interrupt than none.
>> + */
>> + if (timeout) {
>> + DPRINTF("g-run: s->cnt %u < timout %u\n", s->cnt, timeout);
>> + if (timeout > s->cnt) {
>> + diff_cnt = (timeout - s->cnt);
>> + } else {
>> + diff_cnt = timeout;
>> + }
>> + s->waiting_rov = 0;
>> + } else {
>> + DPRINTF("g-run, FRR\n");
>> + diff_cnt = (TIMER_MAX + 1) - s->cnt;
>> + s->waiting_rov = 1;
>> + }
>> + qemu_mod_timer(s->timer, clk + diff_cnt * 1000 / (GPT_FREQ/1000000));
>> +}
>> +
>> +static uint64_t imx_timerg_read(void *opaque, target_phys_addr_t offset,
>> + unsigned size)
>> +{
>> + imx_timerg_state *s = (imx_timerg_state *)opaque;
>> +
>> + DPRINTF("g-read(offset=%x)\n", offset >> 2);
>> + switch (offset >> 2) {
>> + case 0: /* Control Register */
>> + return s->cr;
>> +
>> + case 1: /* prescaler */
>> + return s->pr;
>> +
>> + case 2: /* Status Register */
>> + return s->sr;
>> +
>> + case 3: /* Interrupt Register */
>> + return s->ir;
>> +
>> + case 4: /* Output Compare Register 1 */
>> + return s->ocr1;
>> +
>> + case 9: /* cnt */
>> + imx_timerg_update_count(s);
>> + return s->cnt;
>> + }
>> +
>> + IPRINTF("imx_timerg_read: Bad offset %x\n",
>> + (int)offset >> 2);
>> + return 0;
>> +}
>> +
>> +static void imx_timerg_reset(DeviceState *dev)
>> +{
>> + imx_timerg_state *s = container_of(dev, imx_timerg_state, busdev.qdev);
>> +
>> + /*
>> + * Soft reset doesn't touch some bits; hard reset clears them
>> + */
>> + s->cr &= ~(GPT_CR_EN|GPT_CR_DOZEN|GPT_CR_WAITEN|GPT_CR_DBGEN);
>> + s->sr = 0;
>> + s->pr = 0;
>> + s->ir = 0;
>> + s->cnt = 0;
>> + s->ocr1 = TIMER_MAX;
>> + imx_timerg_update_count(s);
>> +}
>> +
>> +static void imx_timerg_write(void *opaque, target_phys_addr_t offset,
>> + uint64_t value, unsigned size)
>> +{
>> + imx_timerg_state *s = (imx_timerg_state *)opaque;
>> + DPRINTF("g-write(offset=%x, value = %x)\n", (unsigned int)offset >> 2,
>> + (unsigned int)value);
>> +
>> + switch (offset >> 2) {
>> + case 0: /* CR */
>> + if (value & GPT_CR_SWR) { /* force reset */
>> + value &= ~GPT_CR_SWR;
>> + imx_timerg_reset(&s->busdev.qdev);
>> + imx_timerg_update(s);
>> + }
>> + if (!(s->cr & GPT_CR_EN) && (value & GPT_CR_EN)) {
>> + if (value & GPT_CR_ENMOD) {
>> + s->cnt = 0;
>> + }
>> + imx_timerg_run(s, s->ocr1);
>> + } else if ((s->cr & GPT_CR_EN) && !(value & GPT_CR_EN)) {
>> + qemu_del_timer(s->timer);
>> + };
>> +
>> + s->cr = value & ~0x7c;
>> + return;
>> +
>> + case 1: /* Prescaler */
>> + s->pr = value & 0xfff;
>> + return;
>> +
>> + case 2: /* SR */
>> + /*
>> + * No point in implementing the status register bits to do with
>> + * external interrupt sources.
>> + */
>> + value &= GPT_SR_OF1 | GPT_SR_ROV;
>> + s->sr &= ~value;
>> + imx_timerg_update(s);
>> + return;
>> +
>> + case 3: /* IR -- interrupt register */
>> + s->ir = value & 0x3f;
>> + imx_timerg_update(s);
>> + return;
>> +
>> + case 4: /* OCR1 -- output compare register */
>> + /* In non-freerun mode, reset count when this register is written &*/
>> + s->ocr1 = value ;
>> + if (!(s->cr & GPT_CR_FRR)) {
>> + s->cnt = 0;
>> + }
>> + if (s->cr & GPT_CR_EN) {
>> + imx_timerg_run(s, s->ocr1);
>> + }
>> + return;
>> +
>> + default:
>> + IPRINTF("imx_timerg_write: Bad offset %x\n",
>> + (int)offset >> 2);
>> + }
>> +}
>> +
>> +static void imx_timerg_timeout(void *opaque)
>> +{
>> + imx_timerg_state *s = (imx_timerg_state *)opaque;
>> +
>> + DPRINTF("imx_timerg_timeout, waiting rov=%d\n", s->waiting_rov);
>> + if (s->waiting_rov) {
>> + s->sr |= GPT_SR_ROV;
>> + if (s->ocr1 == TIMER_MAX) {
>> + s->sr |= GPT_SR_OF1;
>> + }
>> + imx_timerg_run(s, s->ocr1);
>> + } else {
>> + s->sr |= GPT_SR_OF1;
>> + imx_timerg_run(s, 0);
>> + }
>> + imx_timerg_update(s);
>> +}
>> +
>> +static const MemoryRegionOps imx_timerg_ops = {
>> + .read = imx_timerg_read,
>> + .write = imx_timerg_write,
>> + .endianness = DEVICE_NATIVE_ENDIAN,
>> +};
>> +
>> +
>> +static int imx_timerg_init(SysBusDevice *dev)
>> +{
>> + imx_timerg_state *s = FROM_SYSBUS(imx_timerg_state, dev);
>> +
>> + sysbus_init_irq(dev, &s->irq);
>> + memory_region_init_io(&s->iomem, &imx_timerg_ops,
>> + s, "imxg-timer",
>> + 0x00001000);
>> + sysbus_init_mmio(dev, &s->iomem);
>> +
>> + s->timer = qemu_new_timer_ns(vm_clock, imx_timerg_timeout, s);
>> + /* Hard reset resets extra bits in CR */
>> + s->cr = 0;
>> + return 0;
>> +}
>> +
>> +
>> +
>> +/*
>> + * EPIT: Enhanced periodic interrupt timer
>> + */
>> +
>> +#define TIMER_TICK_LENGTH 5000
>> +
>> +#define CR_EN (1 << 0)
>> +#define CR_ENMOD (1 << 1)
>> +#define CR_OCIEN (1 << 2)
>> +#define CR_RLD (1 << 3)
>> +#define CR_PRESCALE_SHIFT (4)
>> +#define CR_PRESCALE_MASK (0xfff << CR_PRESCALE_SHIFT)
>> +#define CR_SWR (1 << 16)
>> +#define CR_IOVW (1 << 17)
>> +#define CR_DBGEN (1 << 18)
>> +#define CR_EPIT (1 << 19)
>> +#define CR_DOZEN (1 << 20)
>> +#define CR_STOPEN (1 << 21)
>> +#define CR_CLKSRC_SHIFT (24)
>> +#define CR_CLKSRC_MASK (0x3 << CR_CLKSRC_SHIFT)
>> +
>> +/*
>> + * Exact clock frequencies vary from board to board.
>> + * These are typical.
>> + */
>> +static const uint32_t clocks[] = {
>> + 0, /* disabled */
>> + 53200000, /* ipg_clk, ~50MHz */
>> + 53200000, /* ipg_clk_highfreq */
>> + 32768, /* ipg_clk_32k -- ~32kHz */
>> +};
>> +
>> +
>> +typedef struct {
>> + SysBusDevice busdev;
>> + ptimer_state *timer;
>> + MemoryRegion iomem;
>> + uint32_t cr;
>> + uint32_t lr;
>> + uint32_t cmp;
>> +
>> + uint32_t freq;
>> + int int_level;
>> + qemu_irq irq;
>> +} imx_timerp_state;
>> +
>> +/*
>> + * Update interrupt status
>> + */
>> +static void imx_timerp_update(imx_timerp_state *s)
>> +{
>> + if (s->int_level && (s->cr & CR_OCIEN)) {
>> + qemu_irq_raise(s->irq);
>> + } else {
>> + qemu_irq_lower(s->irq);
>> + }
>> +}
>> +
>> +static void imx_timerp_reset(DeviceState *dev)
>> +{
>> + imx_timerp_state *s = container_of(dev, imx_timerp_state, busdev.qdev);
>> +
>> + s->cr = 0;
>> + s->lr = 0xffffffff;
>> + s->int_level = 0;
>> + s->cmp = 0;
>> + ptimer_stop(s->timer);
>> + ptimer_set_count(s->timer, 0xffffffff);
>> +}
>> +
>> +static uint64_t imx_timerp_read(void *opaque, target_phys_addr_t offset,
>> + unsigned size)
>> +{
>> + imx_timerp_state *s = (imx_timerp_state *)opaque;
>> +
>> + DPRINTF("p-read(offset=%x)\n", offset);
>> + switch (offset >> 2) {
>> + case 0: /* Control Register */
>> + return s->cr;
>> +
>> + case 1: /* Status Register */
>> + return s->int_level;
>> +
>> + case 2: /* LR - ticks*/
>> + return s->lr;
>> +
>> + case 3: /* CMP */
>> + return s->cmp;
>> +
>> + case 4: /* CNT */
>> + return ptimer_get_count(s->timer);
>> + }
>> + IPRINTF("imx_timerp_read: Bad offset %x\n",
>> + (int)offset >> 2);
>> + return 0;
>> +}
>> +
>> +static void set_timerp_freq(imx_timerp_state *s)
>> +{
>> + int clksrc;
>> + unsigned prescaler;
>> + uint32_t freq;
>> +
>> + clksrc = (s->cr & CR_CLKSRC_MASK) >> CR_CLKSRC_SHIFT;
>> + prescaler = 1 + ((s->cr & CR_PRESCALE_MASK) >> CR_PRESCALE_SHIFT);
>> + DPRINTF("ptimer clksrc %d, prescaler %d", clksrc, prescaler);
>> + freq = clocks[clksrc] / prescaler;
>> +
>> + DPRINTF("Setting ptimer to frequency %d\n", freq);
>> + s->freq = freq;
>> +
>> + if (freq) {
>> + ptimer_set_freq(s->timer, freq);
>> + }
>> +}
>> +
>> +static void imx_timerp_write(void *opaque, target_phys_addr_t offset,
>> + uint64_t value, unsigned size)
>> +{
>> + imx_timerp_state *s = (imx_timerp_state *)opaque;
>> + DPRINTF("p-write(offset=%x, value = %x)\n", (unsigned int)offset >> 2,
>> + (unsigned int)value);
>> +
>> + switch (offset >> 2) {
>> + case 0: /* CR */
>> + if (value & CR_SWR) {
>> + imx_timerp_reset(&s->busdev.qdev);
>> + value &= ~CR_SWR;
>> + }
>> + s->cr = value & 0x03ffffff;
>> + set_timerp_freq(s);
>> +
>> + if (s->freq && (s->cr & CR_EN)) {
>> + ptimer_run(s->timer, 0);
>> + } else {
>> + ptimer_stop(s->timer);
>> + }
>> + break;
>> +
>> + case 1: /* SR - ACK*/
>> + s->int_level = 0;
>> + imx_timerp_update(s);
>> + break;
>> +
>> + case 2: /* LR - set ticks */
>> + s->lr = value;
>> + ptimer_set_limit(s->timer, value, !!(s->cr & CR_IOVW));
>> + break;
>> +
>> + case 3: /* CMP */
>> + s->cmp = value;
>> + break;
>> +
>> + default:
>> + IPRINTF("imx_timerp_write: Bad offset %x\n",
>> + (int)offset >> 2);
>> + }
>> +}
>> +
>> +static void imx_timerp_tick(void *opaque)
>> +{
>> + imx_timerp_state *s = (imx_timerp_state *)opaque;
>> +
>> + s->int_level = 1;
>> + if (!(s->cr & CR_RLD)) {
>> + ptimer_set_count(s->timer, 0xffffffff);
>> + }
>> + imx_timerp_update(s);
>> +}
>> +
>> +static const MemoryRegionOps imx_timerp_ops = {
>> + .read = imx_timerp_read,
>> + .write = imx_timerp_write,
>> + .endianness = DEVICE_NATIVE_ENDIAN,
>> +};
>> +
>> +static const VMStateDescription vmstate_imx_timerp = {
>> + .name = "imx-timerp",
>> + .version_id = 1,
>> + .minimum_version_id = 1,
>> + .minimum_version_id_old = 1,
>> + .fields = (VMStateField[]) {
>> + VMSTATE_UINT32(cr, imx_timerp_state),
>> + VMSTATE_UINT32(lr, imx_timerp_state),
>> + VMSTATE_UINT32(cmp, imx_timerp_state),
>> + VMSTATE_UINT32(freq, imx_timerp_state),
>> + VMSTATE_INT32(int_level, imx_timerp_state),
>> + VMSTATE_PTIMER(timer, imx_timerp_state),
>> + VMSTATE_END_OF_LIST()
>> + }
>> +};
>> +
>> +static int imx_timerp_init(SysBusDevice *dev)
>> +{
>> + imx_timerp_state *s = FROM_SYSBUS(imx_timerp_state, dev);
>> + QEMUBH *bh;
>> +
>> + DPRINTF("imx_timerp_init\n");
>> +
>> + sysbus_init_irq(dev, &s->irq);
>> + memory_region_init_io(&s->iomem, &imx_timerp_ops,
>> + s, "imxp-timer",
>> + 0x00001000);
>> + sysbus_init_mmio(dev, &s->iomem);
>> +
>> + bh = qemu_bh_new(imx_timerp_tick, s);
>> + s->timer = ptimer_init(bh);
>> +
>> + return 0;
>> +}
>> +
>> +static void imx_timerg_class_init(ObjectClass *klass, void *data)
>> +{
>> + DeviceClass *dc = DEVICE_CLASS(klass);
>> + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
>> + k->init = imx_timerg_init;
>> + dc->vmsd = &vmstate_imx_timerg;
>> + dc->reset = imx_timerg_reset;
>> + dc->desc = "i.MX general timer";
>> +}
>> +
>> +static void imx_timerp_class_init(ObjectClass *klass, void *data)
>> +{
>> + DeviceClass *dc = DEVICE_CLASS(klass);
>> + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
>> + k->init = imx_timerp_init;
>> + dc->vmsd = &vmstate_imx_timerp;
>> + dc->reset = imx_timerp_reset;
>> + dc->desc = "i.MX periodic timer";
>> +}
>> +
>> +static TypeInfo imx_timerp_info = {
>> + .name = "imx_timerp",
>> + .parent = TYPE_SYS_BUS_DEVICE,
>> + .instance_size = sizeof(imx_timerp_state),
>> + .class_init = imx_timerp_class_init,
>> +};
>> +
>> +static TypeInfo imx_timerg_info = {
>> + .name = "imx_timerg",
>> + .parent = TYPE_SYS_BUS_DEVICE,
>> + .instance_size = sizeof(imx_timerg_state),
>> + .class_init = imx_timerg_class_init,
>> +};
>> +
>> +static void imx_timer_register_types(void)
>> +{
>> + type_register_static(&imx_timerp_info);
>> + type_register_static(&imx_timerg_info);
>> +}
>> +
>> +type_init(imx_timer_register_types)
>> Index: qemu-working/Makefile.target
>> ===================================================================
>> --- qemu-working.orig/Makefile.target 2012-03-09 14:13:50.038065728 +1100
>> +++ qemu-working/Makefile.target 2012-03-09 14:13:51.406084465 +1100
>> @@ -379,7 +379,7 @@ obj-arm-y += vexpress.o
>> obj-arm-y += strongarm.o
>> obj-arm-y += collie.o
>> obj-arm-y += pl041.o lm4549.o
>> -obj-arm-y += imx_serial.o
>> +obj-arm-y += imx_serial.o imx_timer.o
>> obj-arm-$(CONFIG_FDT) += device_tree.o
>>
>> obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
>>
>
>
>
> --
> 12345678901234567890123456789012345678901234567890123456789012345678901234567890
> 1 2 3 4 5 6 7 8
--
12345678901234567890123456789012345678901234567890123456789012345678901234567890
1 2 3 4 5 6 7 8
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [patch V4 3/4] FreeSCALE i.MX31 support: AVIC
2012-03-09 3:27 ` [Qemu-devel] [patch V4 3/4] FreeSCALE i.MX31 support: AVIC peter.chubb
@ 2012-03-15 17:14 ` Peter Maydell
0 siblings, 0 replies; 13+ messages in thread
From: Peter Maydell @ 2012-03-15 17:14 UTC (permalink / raw)
To: peter.chubb; +Cc: qemu-devel, philipo
On 9 March 2012 03:27, <peter.chubb@nicta.com.au> wrote:
> Implement the FreeSCALE i.MX31 advanced vectored interrupt controller, at least
> to the extent it is used by Linux 3.0.x
>
> Vectors are not implemented.
>
> Signed-off-by: Philip O'Sullivan <philipo@ok-labs.com>
> Signed-off-by: Peter Chubb <peter.chubb@nicta.com.au>
Looks good apart from the usual type name quibble. If the only
thing you do is s/imx_avic_state/IMXAVICState/g then you can add
my "Reviewed-by: Peter Maydell <peter.maydell@linaro.org>" when you
post the next round.
-- PMM
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [patch V4 4/4] FreeSCALE i.MX31 support: KZM-ARM11-01 evaluation board
2012-03-09 3:27 ` [Qemu-devel] [patch V4 4/4] FreeSCALE i.MX31 support: KZM-ARM11-01 evaluation board peter.chubb
@ 2012-03-15 17:20 ` Peter Maydell
0 siblings, 0 replies; 13+ messages in thread
From: Peter Maydell @ 2012-03-15 17:20 UTC (permalink / raw)
To: peter.chubb; +Cc: qemu-devel, philipo
On 9 March 2012 03:27, <peter.chubb@nicta.com.au> wrote:
> Board support for Kyoto Micro's KZM-ARM11-01, an evaluation board built
> around the FreeScale i.MX31.
>
>
> Signed-off-by: Philip O'Sullivan <philipo@ok-labs.com>
> Signed-off-by: Peter Chubb <peter.chubb@nicta.com.au>
> ---
> Makefile.target | 1
> hw/kzm.c | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 160 insertions(+)
> create mode 100644 hw/kzm.c
>
> Index: qemu-working/hw/kzm.c
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ qemu-working/hw/kzm.c 2012-03-09 14:13:54.578127780 +1100
> @@ -0,0 +1,159 @@
> +/*
> + * KZM Board System emulation.
> + *
> + * Copyright (c) 2008 OKL and 2011 NICTA
> + * Written by Hans
> + * Updated by Peter Chubb.
> + *
> + * This code is licenced under the GPL, version 2 or later.
> + * See the file `COPYING' in the top level directory.
> + *
> + * It (partially) emulates a Kyoto Microcomputer
> + * KZM-ARM11-01 evaluation board, with a FreeScale
> + * I.MX31 SoC
> + */
> +
> +#include "sysbus.h"
> +#include "exec-memory.h"
> +#include "hw.h"
> +#include "arm-misc.h"
> +#include "primecell.h"
You don't need this include.
> +#include "devices.h"
> +#include "pci.h"
Nor this one, I suspect.
> +#include "net.h"
> +#include "sysemu.h"
> +#include "boards.h"
> +#include "pc.h" /* for the FPGA UART that emulates a 16550 */
> +#include "imx.h"
> +
> + /* Memory map for Kzm Emulation Baseboard:
> + * 0x00000000-0x00003fff 16k secure ROM IGNORED
> + * 0x00004000-0x00407fff Reserved IGNORED
> + * 0x00404000-0x00407fff ROM IGNORED
> + * 0x00408000-0x0fffffff Reserved IGNORED
> + * 0x10000000-0x1fffBfff RAM aliasing IGNORED
that capital 'B' in the hex addr should be lowercased...
Otherwise looks OK. If the only changes to this patch in the next
round are removing unneeded #include lines and lowercasing that 'B'
you can add my 'Reviewed-by: Peter Maydell <peter.maydell@linaro.org>'
tag when you post the next version.
-- PMM
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [patch V4 2/4] FreeSCALE i.MX31 support: Timers
2012-03-15 17:08 ` Peter Maydell
@ 2012-03-15 21:02 ` Peter Chubb
0 siblings, 0 replies; 13+ messages in thread
From: Peter Chubb @ 2012-03-15 21:02 UTC (permalink / raw)
To: Peter Maydell; +Cc: Paolo Bonzini, peter.chubb, qemu-devel, philipo, Paul Brook
>>>>> "Peter" == Peter Maydell <peter.maydell@linaro.org> writes:
Peter> ...that would work better if I cc'd the people I meant to...
Peter> On 15 March 2012 17:08, Peter Maydell
Peter> <peter.maydell@linaro.org> wrote:
>> On 9 March 2012 03:27, <peter.chubb@nicta.com.au> wrote:
>>> Implement the timers on the FreeScale i.MX31 SoC. This is not a
>>> complete implementation, but gives enough for Linux to boot and
>>> run. In particular external triggers, which are not useful under
>>> QEMU, are not implemented.
>>
>>
>> The rest looks OK to me but I'm not very good with qemu's timer
>> API. Paolo or Paul -- could you check this looks ok on that front?
>>
Yes please! I think there's a bug in there somewhere when using the G
timer, as interrupts appear to arrive too far apart in some modes.
The scaling is probably wrong somewhere.
--
Dr Peter Chubb peter.chubb AT nicta.com.au
http://www.ssrg.nicta.com.au Software Systems Research Group/NICTA
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [patch V4 1/4] i.MX UART support
2012-03-15 17:01 ` Peter Maydell
@ 2012-03-15 21:10 ` Peter Chubb
2012-03-15 21:15 ` Peter Maydell
0 siblings, 1 reply; 13+ messages in thread
From: Peter Chubb @ 2012-03-15 21:10 UTC (permalink / raw)
To: Peter Maydell; +Cc: peter.chubb, qemu-devel, philipo
Thanks for your reviewing time and expertise Peter. It's much
appreciated.
May I add your Reviewed-By: line to the imx-serial patch too? The
only change is CamelCasing the typedef.
Peter C
--
Dr Peter Chubb peter.chubb AT nicta.com.au
http://www.ssrg.nicta.com.au Software Systems Research Group/NICTA
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [patch V4 1/4] i.MX UART support
2012-03-15 21:10 ` Peter Chubb
@ 2012-03-15 21:15 ` Peter Maydell
0 siblings, 0 replies; 13+ messages in thread
From: Peter Maydell @ 2012-03-15 21:15 UTC (permalink / raw)
To: Peter Chubb; +Cc: qemu-devel, philipo
On 15 March 2012 21:10, Peter Chubb <peter.chubb@nicta.com.au> wrote:
> Thanks for your reviewing time and expertise Peter. It's much
> appreciated.
>
> May I add your Reviewed-By: line to the imx-serial patch too? The
> only change is CamelCasing the typedef.
Yes, if you're just doing an s/imx_state/IMXSerialState/g then you
can add my r-b tag.
-- PMM
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2012-03-15 21:15 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-03-09 3:27 [Qemu-devel] [patch V4 0/4] Support for i.MX31 and the Kyoto KZM11 board peter.chubb
2012-03-09 3:27 ` [Qemu-devel] [patch V4 1/4] i.MX UART support peter.chubb
2012-03-15 17:01 ` Peter Maydell
2012-03-15 21:10 ` Peter Chubb
2012-03-15 21:15 ` Peter Maydell
2012-03-09 3:27 ` [Qemu-devel] [patch V4 2/4] FreeSCALE i.MX31 support: Timers peter.chubb
2012-03-15 17:08 ` Peter Maydell
2012-03-15 17:08 ` Peter Maydell
2012-03-15 21:02 ` Peter Chubb
2012-03-09 3:27 ` [Qemu-devel] [patch V4 3/4] FreeSCALE i.MX31 support: AVIC peter.chubb
2012-03-15 17:14 ` Peter Maydell
2012-03-09 3:27 ` [Qemu-devel] [patch V4 4/4] FreeSCALE i.MX31 support: KZM-ARM11-01 evaluation board peter.chubb
2012-03-15 17:20 ` Peter Maydell
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).