* [Qemu-devel] [PATCH 01/10] ARM: hw/exynos4210_mct.c: Fix a bug which hangs Linux kernel.
2012-07-04 11:14 [Qemu-devel] [PULL 00/10] arm-devs queue Peter Maydell
@ 2012-07-04 11:14 ` Peter Maydell
2012-07-04 11:14 ` [Qemu-devel] [PATCH 02/10] hw/exynos4210_pwm.c: Fix STOP status in tick handler Peter Maydell
` (9 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Peter Maydell @ 2012-07-04 11:14 UTC (permalink / raw)
To: Anthony Liguori, Blue Swirl; +Cc: qemu-devel, Paul Brook
From: Stanislav Vorobiov <s.vorobiov@samsung.com>
After some long period of time Linux kernel hanged due to
ptimer_get_count may return 0 before timer interrupt occurs,
thus, causing FRC to jump back in time
Signed-off-by: Evgeny Voevodin <e.voevodin@samsung.com>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/exynos4210_mct.c | 4 ----
1 files changed, 0 insertions(+), 4 deletions(-)
diff --git a/hw/exynos4210_mct.c b/hw/exynos4210_mct.c
index 7474fcf..7a22b1f 100644
--- a/hw/exynos4210_mct.c
+++ b/hw/exynos4210_mct.c
@@ -376,10 +376,6 @@ static uint64_t exynos4210_gfrc_get_count(Exynos4210MCTGT *s)
{
uint64_t count = 0;
count = ptimer_get_count(s->ptimer_frc);
- if (!count) {
- /* Timer event was generated and s->reg.cnt holds adequate value */
- return s->reg.cnt;
- }
count = s->count - count;
return s->reg.cnt + count;
}
--
1.7.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Qemu-devel] [PATCH 02/10] hw/exynos4210_pwm.c: Fix STOP status in tick handler.
2012-07-04 11:14 [Qemu-devel] [PULL 00/10] arm-devs queue Peter Maydell
2012-07-04 11:14 ` [Qemu-devel] [PATCH 01/10] ARM: hw/exynos4210_mct.c: Fix a bug which hangs Linux kernel Peter Maydell
@ 2012-07-04 11:14 ` Peter Maydell
2012-07-04 11:14 ` [Qemu-devel] [PATCH 03/10] hw/exynos4210.c: Fix misleading initialization of IROM mirror Peter Maydell
` (8 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Peter Maydell @ 2012-07-04 11:14 UTC (permalink / raw)
To: Anthony Liguori, Blue Swirl; +Cc: qemu-devel, Paul Brook
From: Evgeny Voevodin <e.voevodin@samsung.com>
START/STOP bit was not cleaned correctly.
Signed-off-by: Evgeny Voevodin <e.voevodin@samsung.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/exynos4210_pwm.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/hw/exynos4210_pwm.c b/hw/exynos4210_pwm.c
index 6243e59..0c22828 100644
--- a/hw/exynos4210_pwm.c
+++ b/hw/exynos4210_pwm.c
@@ -200,7 +200,7 @@ static void exynos4210_pwm_tick(void *opaque)
ptimer_run(p->timer[id].ptimer, 1);
} else {
/* stop timer, set status to STOP, see Basic Timer Operation */
- p->reg_tcon = ~TCON_TIMER_START(id);
+ p->reg_tcon &= ~TCON_TIMER_START(id);
ptimer_stop(p->timer[id].ptimer);
}
}
--
1.7.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Qemu-devel] [PATCH 03/10] hw/exynos4210.c: Fix misleading initialization of IROM mirror
2012-07-04 11:14 [Qemu-devel] [PULL 00/10] arm-devs queue Peter Maydell
2012-07-04 11:14 ` [Qemu-devel] [PATCH 01/10] ARM: hw/exynos4210_mct.c: Fix a bug which hangs Linux kernel Peter Maydell
2012-07-04 11:14 ` [Qemu-devel] [PATCH 02/10] hw/exynos4210_pwm.c: Fix STOP status in tick handler Peter Maydell
@ 2012-07-04 11:14 ` Peter Maydell
2012-07-04 11:14 ` [Qemu-devel] [PATCH 04/10] Exynos4: add RTC device Peter Maydell
` (7 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Peter Maydell @ 2012-07-04 11:14 UTC (permalink / raw)
To: Anthony Liguori, Blue Swirl; +Cc: qemu-devel, Paul Brook
From: Evgeny Voevodin <e.voevodin@samsung.com>
We want to mirror whole IROM and should pass zero instead of
EXYNOS4210_IROM_BASE_ADDR (though it equals to zero too) since
memory_region_init_alias takes an offset within an original
region as an argument.
Signed-off-by: Evgeny Voevodin <e.voevodin@samsung.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/exynos4210.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/hw/exynos4210.c b/hw/exynos4210.c
index 9c20b3f..80a00b9 100644
--- a/hw/exynos4210.c
+++ b/hw/exynos4210.c
@@ -216,7 +216,7 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
/* mirror of iROM */
memory_region_init_alias(&s->irom_alias_mem, "exynos4210.irom_alias",
&s->irom_mem,
- EXYNOS4210_IROM_BASE_ADDR,
+ 0,
EXYNOS4210_IROM_SIZE);
memory_region_set_readonly(&s->irom_alias_mem, true);
memory_region_add_subregion(system_mem, EXYNOS4210_IROM_MIRROR_BASE_ADDR,
--
1.7.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Qemu-devel] [PATCH 04/10] Exynos4: add RTC device
2012-07-04 11:14 [Qemu-devel] [PULL 00/10] arm-devs queue Peter Maydell
` (2 preceding siblings ...)
2012-07-04 11:14 ` [Qemu-devel] [PATCH 03/10] hw/exynos4210.c: Fix misleading initialization of IROM mirror Peter Maydell
@ 2012-07-04 11:14 ` Peter Maydell
2012-07-04 11:14 ` [Qemu-devel] [PATCH 05/10] i.MX: UART support Peter Maydell
` (6 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Peter Maydell @ 2012-07-04 11:14 UTC (permalink / raw)
To: Anthony Liguori, Blue Swirl; +Cc: qemu-devel, Paul Brook
From: Oleg Ogurtsov <o.ogurtsov@samsung.com>
Signed-off-by: Oleg Ogurtsov <o.ogurtsov@samsung.com>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/arm/Makefile.objs | 1 +
hw/exynos4210.c | 8 +
hw/exynos4210_rtc.c | 595 ++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 604 insertions(+), 0 deletions(-)
create mode 100644 hw/exynos4210_rtc.c
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 88ff47d..0fdb832 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -11,6 +11,7 @@ obj-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
obj-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o
obj-y += exynos4_boards.o exynos4210_uart.o exynos4210_pwm.o
obj-y += exynos4210_pmu.o exynos4210_mct.o exynos4210_fimd.o
+obj-y += exynos4210_rtc.o
obj-y += arm_l2x0.o
obj-y += arm_mptimer.o a15mpcore.o
obj-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
diff --git a/hw/exynos4210.c b/hw/exynos4210.c
index 80a00b9..7c58c90 100644
--- a/hw/exynos4210.c
+++ b/hw/exynos4210.c
@@ -33,6 +33,9 @@
/* PWM */
#define EXYNOS4210_PWM_BASE_ADDR 0x139D0000
+/* RTC */
+#define EXYNOS4210_RTC_BASE_ADDR 0x10070000
+
/* MCT */
#define EXYNOS4210_MCT_BASE_ADDR 0x10050000
@@ -258,6 +261,11 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
s->irq_table[exynos4210_get_irq(22, 3)],
s->irq_table[exynos4210_get_irq(22, 4)],
NULL);
+ /* RTC */
+ sysbus_create_varargs("exynos4210.rtc", EXYNOS4210_RTC_BASE_ADDR,
+ s->irq_table[exynos4210_get_irq(23, 0)],
+ s->irq_table[exynos4210_get_irq(23, 1)],
+ NULL);
/* Multi Core Timer */
dev = qdev_create(NULL, "exynos4210.mct");
diff --git a/hw/exynos4210_rtc.c b/hw/exynos4210_rtc.c
new file mode 100644
index 0000000..f781020
--- /dev/null
+++ b/hw/exynos4210_rtc.c
@@ -0,0 +1,595 @@
+/*
+ * Samsung exynos4210 Real Time Clock
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * Ogurtsov Oleg <o.ogurtsov@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/* Description:
+ * Register RTCCON:
+ * CLKSEL Bit[1] not used
+ * CLKOUTEN Bit[9] not used
+ */
+
+#include "sysbus.h"
+#include "qemu-timer.h"
+#include "qemu-common.h"
+#include "ptimer.h"
+
+#include "hw.h"
+#include "qemu-timer.h"
+#include "sysemu.h"
+
+#include "exynos4210.h"
+
+#define DEBUG_RTC 0
+
+#if DEBUG_RTC
+#define DPRINTF(fmt, ...) \
+ do { fprintf(stdout, "RTC: [%24s:%5d] " fmt, __func__, __LINE__, \
+ ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+#define EXYNOS4210_RTC_REG_MEM_SIZE 0x0100
+
+#define INTP 0x0030
+#define RTCCON 0x0040
+#define TICCNT 0x0044
+#define RTCALM 0x0050
+#define ALMSEC 0x0054
+#define ALMMIN 0x0058
+#define ALMHOUR 0x005C
+#define ALMDAY 0x0060
+#define ALMMON 0x0064
+#define ALMYEAR 0x0068
+#define BCDSEC 0x0070
+#define BCDMIN 0x0074
+#define BCDHOUR 0x0078
+#define BCDDAY 0x007C
+#define BCDDAYWEEK 0x0080
+#define BCDMON 0x0084
+#define BCDYEAR 0x0088
+#define CURTICNT 0x0090
+
+#define TICK_TIMER_ENABLE 0x0100
+#define TICNT_THRESHHOLD 2
+
+
+#define RTC_ENABLE 0x0001
+
+#define INTP_TICK_ENABLE 0x0001
+#define INTP_ALM_ENABLE 0x0002
+
+#define ALARM_INT_ENABLE 0x0040
+
+#define RTC_BASE_FREQ 32768
+
+typedef struct Exynos4210RTCState {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+
+ /* registers */
+ uint32_t reg_intp;
+ uint32_t reg_rtccon;
+ uint32_t reg_ticcnt;
+ uint32_t reg_rtcalm;
+ uint32_t reg_almsec;
+ uint32_t reg_almmin;
+ uint32_t reg_almhour;
+ uint32_t reg_almday;
+ uint32_t reg_almmon;
+ uint32_t reg_almyear;
+ uint32_t reg_curticcnt;
+
+ ptimer_state *ptimer; /* tick timer */
+ ptimer_state *ptimer_1Hz; /* clock timer */
+ uint32_t freq;
+
+ qemu_irq tick_irq; /* Time Tick Generator irq */
+ qemu_irq alm_irq; /* alarm irq */
+
+ struct tm current_tm; /* current time */
+} Exynos4210RTCState;
+
+#define TICCKSEL(value) ((value & (0x0F << 4)) >> 4)
+
+/*** VMState ***/
+static const VMStateDescription vmstate_exynos4210_rtc_state = {
+ .name = "exynos4210.rtc",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(reg_intp, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_rtccon, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_ticcnt, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_rtcalm, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_almsec, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_almmin, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_almhour, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_almday, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_almmon, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_almyear, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_curticcnt, Exynos4210RTCState),
+ VMSTATE_PTIMER(ptimer, Exynos4210RTCState),
+ VMSTATE_PTIMER(ptimer_1Hz, Exynos4210RTCState),
+ VMSTATE_UINT32(freq, Exynos4210RTCState),
+ VMSTATE_INT32(current_tm.tm_sec, Exynos4210RTCState),
+ VMSTATE_INT32(current_tm.tm_min, Exynos4210RTCState),
+ VMSTATE_INT32(current_tm.tm_hour, Exynos4210RTCState),
+ VMSTATE_INT32(current_tm.tm_wday, Exynos4210RTCState),
+ VMSTATE_INT32(current_tm.tm_mday, Exynos4210RTCState),
+ VMSTATE_INT32(current_tm.tm_mon, Exynos4210RTCState),
+ VMSTATE_INT32(current_tm.tm_year, Exynos4210RTCState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+#define BCD3DIGITS(x) \
+ ((uint32_t)to_bcd((uint8_t)x) + \
+ ((uint32_t)to_bcd((uint8_t)((x % 1000) / 100)) << 8))
+
+static void check_alarm_raise(Exynos4210RTCState *s)
+{
+ unsigned int alarm_raise = 0;
+ struct tm stm = s->current_tm;
+
+ if ((s->reg_rtcalm & 0x01) &&
+ (to_bcd((uint8_t)stm.tm_sec) == (uint8_t)s->reg_almsec)) {
+ alarm_raise = 1;
+ }
+ if ((s->reg_rtcalm & 0x02) &&
+ (to_bcd((uint8_t)stm.tm_min) == (uint8_t)s->reg_almmin)) {
+ alarm_raise = 1;
+ }
+ if ((s->reg_rtcalm & 0x04) &&
+ (to_bcd((uint8_t)stm.tm_hour) == (uint8_t)s->reg_almhour)) {
+ alarm_raise = 1;
+ }
+ if ((s->reg_rtcalm & 0x08) &&
+ (to_bcd((uint8_t)stm.tm_mday) == (uint8_t)s->reg_almday)) {
+ alarm_raise = 1;
+ }
+ if ((s->reg_rtcalm & 0x10) &&
+ (to_bcd((uint8_t)stm.tm_mon) == (uint8_t)s->reg_almmon)) {
+ alarm_raise = 1;
+ }
+ if ((s->reg_rtcalm & 0x20) &&
+ (BCD3DIGITS(stm.tm_year) == s->reg_almyear)) {
+ alarm_raise = 1;
+ }
+
+ if (alarm_raise) {
+ DPRINTF("ALARM IRQ\n");
+ /* set irq status */
+ s->reg_intp |= INTP_ALM_ENABLE;
+ qemu_irq_raise(s->alm_irq);
+ }
+}
+
+/*
+ * RTC update frequency
+ * Parameters:
+ * reg_value - current RTCCON register or his new value
+ */
+static void exynos4210_rtc_update_freq(Exynos4210RTCState *s,
+ uint32_t reg_value)
+{
+ uint32_t freq;
+
+ freq = s->freq;
+ /* set frequncy for time generator */
+ s->freq = RTC_BASE_FREQ / (1 << TICCKSEL(reg_value));
+
+ if (freq != s->freq) {
+ ptimer_set_freq(s->ptimer, s->freq);
+ DPRINTF("freq=%dHz\n", s->freq);
+ }
+}
+
+/* month is between 0 and 11. */
+static int get_days_in_month(int month, int year)
+{
+ static const int days_tab[12] = {
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ };
+ int d;
+ if ((unsigned)month >= 12) {
+ return 31;
+ }
+ d = days_tab[month];
+ if (month == 1) {
+ if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)) {
+ d++;
+ }
+ }
+ return d;
+}
+
+/* update 'tm' to the next second */
+static void rtc_next_second(struct tm *tm)
+{
+ int days_in_month;
+
+ tm->tm_sec++;
+ if ((unsigned)tm->tm_sec >= 60) {
+ tm->tm_sec = 0;
+ tm->tm_min++;
+ if ((unsigned)tm->tm_min >= 60) {
+ tm->tm_min = 0;
+ tm->tm_hour++;
+ if ((unsigned)tm->tm_hour >= 24) {
+ tm->tm_hour = 0;
+ /* next day */
+ tm->tm_wday++;
+ if ((unsigned)tm->tm_wday >= 7) {
+ tm->tm_wday = 0;
+ }
+ days_in_month = get_days_in_month(tm->tm_mon,
+ tm->tm_year + 1900);
+ tm->tm_mday++;
+ if (tm->tm_mday < 1) {
+ tm->tm_mday = 1;
+ } else if (tm->tm_mday > days_in_month) {
+ tm->tm_mday = 1;
+ tm->tm_mon++;
+ if (tm->tm_mon >= 12) {
+ tm->tm_mon = 0;
+ tm->tm_year++;
+ }
+ }
+ }
+ }
+ }
+}
+
+/*
+ * tick handler
+ */
+static void exynos4210_rtc_tick(void *opaque)
+{
+ Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
+
+ DPRINTF("TICK IRQ\n");
+ /* set irq status */
+ s->reg_intp |= INTP_TICK_ENABLE;
+ /* raise IRQ */
+ qemu_irq_raise(s->tick_irq);
+
+ /* restart timer */
+ ptimer_set_count(s->ptimer, s->reg_ticcnt);
+ ptimer_run(s->ptimer, 1);
+}
+
+/*
+ * 1Hz clock handler
+ */
+static void exynos4210_rtc_1Hz_tick(void *opaque)
+{
+ Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
+
+ rtc_next_second(&s->current_tm);
+ /* DPRINTF("1Hz tick\n"); */
+
+ /* raise IRQ */
+ if (s->reg_rtcalm & ALARM_INT_ENABLE) {
+ check_alarm_raise(s);
+ }
+
+ ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ);
+ ptimer_run(s->ptimer_1Hz, 1);
+}
+
+/*
+ * RTC Read
+ */
+static uint64_t exynos4210_rtc_read(void *opaque, target_phys_addr_t offset,
+ unsigned size)
+{
+ uint32_t value = 0;
+ Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
+
+ switch (offset) {
+ case INTP:
+ value = s->reg_intp;
+ break;
+ case RTCCON:
+ value = s->reg_rtccon;
+ break;
+ case TICCNT:
+ value = s->reg_ticcnt;
+ break;
+ case RTCALM:
+ value = s->reg_rtcalm;
+ break;
+ case ALMSEC:
+ value = s->reg_almsec;
+ break;
+ case ALMMIN:
+ value = s->reg_almmin;
+ break;
+ case ALMHOUR:
+ value = s->reg_almhour;
+ break;
+ case ALMDAY:
+ value = s->reg_almday;
+ break;
+ case ALMMON:
+ value = s->reg_almmon;
+ break;
+ case ALMYEAR:
+ value = s->reg_almyear;
+ break;
+
+ case BCDSEC:
+ value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_sec);
+ break;
+ case BCDMIN:
+ value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_min);
+ break;
+ case BCDHOUR:
+ value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_hour);
+ break;
+ case BCDDAYWEEK:
+ value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_wday);
+ break;
+ case BCDDAY:
+ value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_mday);
+ break;
+ case BCDMON:
+ value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_mon + 1);
+ break;
+ case BCDYEAR:
+ value = BCD3DIGITS(s->current_tm.tm_year);
+ break;
+
+ case CURTICNT:
+ s->reg_curticcnt = ptimer_get_count(s->ptimer);
+ value = s->reg_curticcnt;
+ break;
+
+ default:
+ fprintf(stderr,
+ "[exynos4210.rtc: bad read offset " TARGET_FMT_plx "]\n",
+ offset);
+ break;
+ }
+ return value;
+}
+
+/*
+ * RTC Write
+ */
+static void exynos4210_rtc_write(void *opaque, target_phys_addr_t offset,
+ uint64_t value, unsigned size)
+{
+ Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
+
+ switch (offset) {
+ case INTP:
+ if (value & INTP_ALM_ENABLE) {
+ qemu_irq_lower(s->alm_irq);
+ s->reg_intp &= (~INTP_ALM_ENABLE);
+ }
+ if (value & INTP_TICK_ENABLE) {
+ qemu_irq_lower(s->tick_irq);
+ s->reg_intp &= (~INTP_TICK_ENABLE);
+ }
+ break;
+ case RTCCON:
+ if (value & RTC_ENABLE) {
+ exynos4210_rtc_update_freq(s, value);
+ }
+ if ((value & RTC_ENABLE) > (s->reg_rtccon & RTC_ENABLE)) {
+ /* clock timer */
+ ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ);
+ ptimer_run(s->ptimer_1Hz, 1);
+ DPRINTF("run clock timer\n");
+ }
+ if ((value & RTC_ENABLE) < (s->reg_rtccon & RTC_ENABLE)) {
+ /* tick timer */
+ ptimer_stop(s->ptimer);
+ /* clock timer */
+ ptimer_stop(s->ptimer_1Hz);
+ DPRINTF("stop all timers\n");
+ }
+ if (value & RTC_ENABLE) {
+ if ((value & TICK_TIMER_ENABLE) >
+ (s->reg_rtccon & TICK_TIMER_ENABLE) &&
+ (s->reg_ticcnt)) {
+ ptimer_set_count(s->ptimer, s->reg_ticcnt);
+ ptimer_run(s->ptimer, 1);
+ DPRINTF("run tick timer\n");
+ }
+ if ((value & TICK_TIMER_ENABLE) <
+ (s->reg_rtccon & TICK_TIMER_ENABLE)) {
+ ptimer_stop(s->ptimer);
+ }
+ }
+ s->reg_rtccon = value;
+ break;
+ case TICCNT:
+ if (value > TICNT_THRESHHOLD) {
+ s->reg_ticcnt = value;
+ } else {
+ fprintf(stderr,
+ "[exynos4210.rtc: bad TICNT value %u ]\n",
+ (uint32_t)value);
+ }
+ break;
+
+ case RTCALM:
+ s->reg_rtcalm = value;
+ break;
+ case ALMSEC:
+ s->reg_almsec = (value & 0x7f);
+ break;
+ case ALMMIN:
+ s->reg_almmin = (value & 0x7f);
+ break;
+ case ALMHOUR:
+ s->reg_almhour = (value & 0x3f);
+ break;
+ case ALMDAY:
+ s->reg_almday = (value & 0x3f);
+ break;
+ case ALMMON:
+ s->reg_almmon = (value & 0x1f);
+ break;
+ case ALMYEAR:
+ s->reg_almyear = (value & 0x0fff);
+ break;
+
+ case BCDSEC:
+ if (s->reg_rtccon & RTC_ENABLE) {
+ s->current_tm.tm_sec = (int)from_bcd((uint8_t)value);
+ }
+ break;
+ case BCDMIN:
+ if (s->reg_rtccon & RTC_ENABLE) {
+ s->current_tm.tm_min = (int)from_bcd((uint8_t)value);
+ }
+ break;
+ case BCDHOUR:
+ if (s->reg_rtccon & RTC_ENABLE) {
+ s->current_tm.tm_hour = (int)from_bcd((uint8_t)value);
+ }
+ break;
+ case BCDDAYWEEK:
+ if (s->reg_rtccon & RTC_ENABLE) {
+ s->current_tm.tm_wday = (int)from_bcd((uint8_t)value);
+ }
+ break;
+ case BCDDAY:
+ if (s->reg_rtccon & RTC_ENABLE) {
+ s->current_tm.tm_mday = (int)from_bcd((uint8_t)value);
+ }
+ break;
+ case BCDMON:
+ if (s->reg_rtccon & RTC_ENABLE) {
+ s->current_tm.tm_mon = (int)from_bcd((uint8_t)value) - 1;
+ }
+ break;
+ case BCDYEAR:
+ if (s->reg_rtccon & RTC_ENABLE) {
+ /* 3 digits */
+ s->current_tm.tm_year = (int)from_bcd((uint8_t)value) +
+ (int)from_bcd((uint8_t)((value >> 8) & 0x0f)) * 100;
+ }
+ break;
+
+ default:
+ fprintf(stderr,
+ "[exynos4210.rtc: bad write offset " TARGET_FMT_plx "]\n",
+ offset);
+ break;
+
+ }
+}
+
+/*
+ * Set default values to timer fields and registers
+ */
+static void exynos4210_rtc_reset(DeviceState *d)
+{
+ Exynos4210RTCState *s = (Exynos4210RTCState *)d;
+
+ struct tm tm;
+
+ qemu_get_timedate(&tm, 0);
+ s->current_tm = tm;
+
+ DPRINTF("Get time from host: %d-%d-%d %2d:%02d:%02d\n",
+ s->current_tm.tm_year, s->current_tm.tm_mon, s->current_tm.tm_mday,
+ s->current_tm.tm_hour, s->current_tm.tm_min, s->current_tm.tm_sec);
+
+ s->reg_intp = 0;
+ s->reg_rtccon = 0;
+ s->reg_ticcnt = 0;
+ s->reg_rtcalm = 0;
+ s->reg_almsec = 0;
+ s->reg_almmin = 0;
+ s->reg_almhour = 0;
+ s->reg_almday = 0;
+ s->reg_almmon = 0;
+ s->reg_almyear = 0;
+
+ s->reg_curticcnt = 0;
+
+ exynos4210_rtc_update_freq(s, s->reg_rtccon);
+ ptimer_stop(s->ptimer);
+ ptimer_stop(s->ptimer_1Hz);
+}
+
+static const MemoryRegionOps exynos4210_rtc_ops = {
+ .read = exynos4210_rtc_read,
+ .write = exynos4210_rtc_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+/*
+ * RTC timer initialization
+ */
+static int exynos4210_rtc_init(SysBusDevice *dev)
+{
+ Exynos4210RTCState *s = FROM_SYSBUS(Exynos4210RTCState, dev);
+ QEMUBH *bh;
+
+ bh = qemu_bh_new(exynos4210_rtc_tick, s);
+ s->ptimer = ptimer_init(bh);
+ ptimer_set_freq(s->ptimer, RTC_BASE_FREQ);
+ exynos4210_rtc_update_freq(s, 0);
+
+ bh = qemu_bh_new(exynos4210_rtc_1Hz_tick, s);
+ s->ptimer_1Hz = ptimer_init(bh);
+ ptimer_set_freq(s->ptimer_1Hz, RTC_BASE_FREQ);
+
+ sysbus_init_irq(dev, &s->alm_irq);
+ sysbus_init_irq(dev, &s->tick_irq);
+
+ memory_region_init_io(&s->iomem, &exynos4210_rtc_ops, s, "exynos4210-rtc",
+ EXYNOS4210_RTC_REG_MEM_SIZE);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ return 0;
+}
+
+static void exynos4210_rtc_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = exynos4210_rtc_init;
+ dc->reset = exynos4210_rtc_reset;
+ dc->vmsd = &vmstate_exynos4210_rtc_state;
+}
+
+static const TypeInfo exynos4210_rtc_info = {
+ .name = "exynos4210.rtc",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Exynos4210RTCState),
+ .class_init = exynos4210_rtc_class_init,
+};
+
+static void exynos4210_rtc_register_types(void)
+{
+ type_register_static(&exynos4210_rtc_info);
+}
+
+type_init(exynos4210_rtc_register_types)
--
1.7.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Qemu-devel] [PATCH 05/10] i.MX: UART support
2012-07-04 11:14 [Qemu-devel] [PULL 00/10] arm-devs queue Peter Maydell
` (3 preceding siblings ...)
2012-07-04 11:14 ` [Qemu-devel] [PATCH 04/10] Exynos4: add RTC device Peter Maydell
@ 2012-07-04 11:14 ` Peter Maydell
2012-07-04 11:14 ` [Qemu-devel] [PATCH 06/10] i.MX31: Clock Control Module Peter Maydell
` (5 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Peter Maydell @ 2012-07-04 11:14 UTC (permalink / raw)
To: Anthony Liguori, Blue Swirl; +Cc: qemu-devel, Paul Brook
From: Peter Chubb <peter.chubb@nicta.com.au>
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>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/arm/Makefile.objs | 1 +
hw/imx.h | 16 ++
hw/imx_serial.c | 467 ++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 484 insertions(+), 0 deletions(-)
create mode 100644 hw/imx.h
create mode 100644 hw/imx_serial.c
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 0fdb832..2f8ff9a 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -35,6 +35,7 @@ obj-y += framebuffer.o
obj-y += vexpress.o
obj-y += strongarm.o
obj-y += collie.o
+obj-y += imx_serial.o
obj-y += pl041.o lm4549.o
obj-$(CONFIG_FDT) += ../device_tree.o
diff --git a/hw/imx.h b/hw/imx.h
new file mode 100644
index 0000000..c1023cc
--- /dev/null
+++ b/hw/imx.h
@@ -0,0 +1,16 @@
+/*
+ * i.MX31 emulation
+ *
+ * Copyright (C) 2012 Peter Chubb
+ * NICTA
+ *
+ * This code is released under the GPL, version 2.0 or later
+ * See the file `../COPYING' for details.
+ */
+
+#ifndef IMX_H
+#define IMX_H
+
+void imx_serial_create(int uart, const target_phys_addr_t addr, qemu_irq irq);
+
+#endif /* IMX_H */
diff --git a/hw/imx_serial.c b/hw/imx_serial.c
new file mode 100644
index 0000000..d4eae43
--- /dev/null
+++ b/hw/imx_serial.c
@@ -0,0 +1,467 @@
+/*
+ * 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;
+} IMXSerialState;
+
+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, IMXSerialState),
+ VMSTATE_UINT32(usr1, IMXSerialState),
+ VMSTATE_UINT32(usr2, IMXSerialState),
+ VMSTATE_UINT32(ucr1, IMXSerialState),
+ VMSTATE_UINT32(uts1, IMXSerialState),
+ VMSTATE_UINT32(onems, IMXSerialState),
+ VMSTATE_UINT32(ufcr, IMXSerialState),
+ VMSTATE_UINT32(ubmr, IMXSerialState),
+ VMSTATE_UINT32(ubrc, IMXSerialState),
+ VMSTATE_UINT32(ucr3, IMXSerialState),
+ 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(IMXSerialState *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(IMXSerialState *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)
+{
+ IMXSerialState *s = container_of(dev, IMXSerialState, 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)
+{
+ IMXSerialState *s = (IMXSerialState *)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)
+{
+ IMXSerialState *s = (IMXSerialState *)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)
+{
+ IMXSerialState *s = (IMXSerialState *)opaque;
+ return !(s->usr1 & USR1_RRDY);
+}
+
+static void imx_put_data(void *opaque, uint32_t value)
+{
+ IMXSerialState *s = (IMXSerialState *)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)
+{
+ IMXSerialState *s = FROM_SYSBUS(IMXSerialState, 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", IMXSerialState, 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(IMXSerialState),
+ .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)
--
1.7.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Qemu-devel] [PATCH 06/10] i.MX31: Clock Control Module
2012-07-04 11:14 [Qemu-devel] [PULL 00/10] arm-devs queue Peter Maydell
` (4 preceding siblings ...)
2012-07-04 11:14 ` [Qemu-devel] [PATCH 05/10] i.MX: UART support Peter Maydell
@ 2012-07-04 11:14 ` Peter Maydell
2012-07-04 11:14 ` [Qemu-devel] [PATCH 07/10] i.MX31: Timers Peter Maydell
` (4 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Peter Maydell @ 2012-07-04 11:14 UTC (permalink / raw)
To: Anthony Liguori, Blue Swirl; +Cc: qemu-devel, Paul Brook
From: Peter Chubb <peter.chubb@nicta.com.au>
For Linux to be able to work out how fast its clocks are going, so
that timer ticks come approximately at the right time, it needs to
be able to query the clock control module (CCM).
This is the start of a CCM implementation. It currently knows only about
the MCU, HSP and IPG clocks --- i.e., the ones used to feed the periodic
and general purpose timers.
Signed-off-by: Peter Chubb <peter.chubb@nicta.com.au>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/arm/Makefile.objs | 2 +-
hw/imx.h | 10 ++
hw/imx_ccm.c | 321 ++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 332 insertions(+), 1 deletions(-)
create mode 100644 hw/imx_ccm.c
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 2f8ff9a..110f2f1 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -35,7 +35,7 @@ obj-y += framebuffer.o
obj-y += vexpress.o
obj-y += strongarm.o
obj-y += collie.o
-obj-y += imx_serial.o
+obj-y += imx_serial.o imx_ccm.o
obj-y += pl041.o lm4549.o
obj-$(CONFIG_FDT) += ../device_tree.o
diff --git a/hw/imx.h b/hw/imx.h
index c1023cc..707e943 100644
--- a/hw/imx.h
+++ b/hw/imx.h
@@ -13,4 +13,14 @@
void imx_serial_create(int uart, const target_phys_addr_t addr, qemu_irq irq);
+typedef enum {
+ NOCLK,
+ MCU,
+ HSP,
+ IPG,
+ CLK_32k
+} IMXClk;
+
+uint32_t imx_clock_frequency(DeviceState *s, IMXClk clock);
+
#endif /* IMX_H */
diff --git a/hw/imx_ccm.c b/hw/imx_ccm.c
new file mode 100644
index 0000000..10952c6
--- /dev/null
+++ b/hw/imx_ccm.c
@@ -0,0 +1,321 @@
+/*
+ * IMX31 Clock Control Module
+ *
+ * Copyright (C) 2012 NICTA
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * To get the timer frequencies right, we need to emulate at least part of
+ * the CCM.
+ */
+
+#include "hw.h"
+#include "sysbus.h"
+#include "sysemu.h"
+#include "imx.h"
+
+#define CKIH_FREQ 26000000 /* 26MHz crystal input */
+#define CKIL_FREQ 32768 /* nominal 32khz clock */
+
+
+//#define DEBUG_CCM 1
+#ifdef DEBUG_CCM
+#define DPRINTF(fmt, args...) \
+do { printf("imx_ccm: " fmt , ##args); } while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while (0)
+#endif
+
+static int imx_ccm_post_load(void *opaque, int version_id);
+
+typedef struct {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+
+ uint32_t ccmr;
+ uint32_t pdr0;
+ uint32_t pdr1;
+ uint32_t mpctl;
+ uint32_t spctl;
+ uint32_t cgr[3];
+ uint32_t pmcr0;
+ uint32_t pmcr1;
+
+ /* Frequencies precalculated on register changes */
+ uint32_t pll_refclk_freq;
+ uint32_t mcu_clk_freq;
+ uint32_t hsp_clk_freq;
+ uint32_t ipg_clk_freq;
+} IMXCCMState;
+
+static const VMStateDescription vmstate_imx_ccm = {
+ .name = "imx-ccm",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(ccmr, IMXCCMState),
+ VMSTATE_UINT32(pdr0, IMXCCMState),
+ VMSTATE_UINT32(pdr1, IMXCCMState),
+ VMSTATE_UINT32(mpctl, IMXCCMState),
+ VMSTATE_UINT32(spctl, IMXCCMState),
+ VMSTATE_UINT32_ARRAY(cgr, IMXCCMState, 3),
+ VMSTATE_UINT32(pmcr0, IMXCCMState),
+ VMSTATE_UINT32(pmcr1, IMXCCMState),
+ VMSTATE_UINT32(pll_refclk_freq, IMXCCMState),
+ },
+ .post_load = imx_ccm_post_load,
+};
+
+/* CCMR */
+#define CCMR_FPME (1<<0)
+#define CCMR_MPE (1<<3)
+#define CCMR_MDS (1<<7)
+#define CCMR_FPMF (1<<26)
+#define CCMR_PRCS (3<<1)
+
+/* PDR0 */
+#define PDR0_MCU_PODF_SHIFT (0)
+#define PDR0_MCU_PODF_MASK (0x7)
+#define PDR0_MAX_PODF_SHIFT (3)
+#define PDR0_MAX_PODF_MASK (0x7)
+#define PDR0_IPG_PODF_SHIFT (6)
+#define PDR0_IPG_PODF_MASK (0x3)
+#define PDR0_NFC_PODF_SHIFT (8)
+#define PDR0_NFC_PODF_MASK (0x7)
+#define PDR0_HSP_PODF_SHIFT (11)
+#define PDR0_HSP_PODF_MASK (0x7)
+#define PDR0_PER_PODF_SHIFT (16)
+#define PDR0_PER_PODF_MASK (0x1f)
+#define PDR0_CSI_PODF_SHIFT (23)
+#define PDR0_CSI_PODF_MASK (0x1ff)
+
+#define EXTRACT(value, name) (((value) >> PDR0_##name##_PODF_SHIFT) \
+ & PDR0_##name##_PODF_MASK)
+#define INSERT(value, name) (((value) & PDR0_##name##_PODF_MASK) << \
+ PDR0_##name##_PODF_SHIFT)
+/* PLL control registers */
+#define PD(v) (((v) >> 26) & 0xf)
+#define MFD(v) (((v) >> 16) & 0x3ff)
+#define MFI(v) (((v) >> 10) & 0xf);
+#define MFN(v) ((v) & 0x3ff)
+
+#define PLL_PD(x) (((x) & 0xf) << 26)
+#define PLL_MFD(x) (((x) & 0x3ff) << 16)
+#define PLL_MFI(x) (((x) & 0xf) << 10)
+#define PLL_MFN(x) (((x) & 0x3ff) << 0)
+
+uint32_t imx_clock_frequency(DeviceState *dev, IMXClk clock)
+{
+ IMXCCMState *s = container_of(dev, IMXCCMState, busdev.qdev);
+
+ switch (clock) {
+ case NOCLK:
+ return 0;
+ case MCU:
+ return s->mcu_clk_freq;
+ case HSP:
+ return s->hsp_clk_freq;
+ case IPG:
+ return s->ipg_clk_freq;
+ case CLK_32k:
+ return CKIL_FREQ;
+ }
+ return 0;
+}
+
+/*
+ * Calculate PLL output frequency
+ */
+static uint32_t calc_pll(uint32_t pllreg, uint32_t base_freq)
+{
+ int32_t mfn = MFN(pllreg); /* Numerator */
+ uint32_t mfi = MFI(pllreg); /* Integer part */
+ uint32_t mfd = 1 + MFD(pllreg); /* Denominator */
+ uint32_t pd = 1 + PD(pllreg); /* Pre-divider */
+
+ if (mfi < 5) {
+ mfi = 5;
+ }
+ /* mfn is 10-bit signed twos-complement */
+ mfn <<= 32 - 10;
+ mfn >>= 32 - 10;
+
+ return ((2 * (base_freq >> 10) * (mfi * mfd + mfn)) /
+ (mfd * pd)) << 10;
+}
+
+static void update_clocks(IMXCCMState *s)
+{
+ /*
+ * If we ever emulate more clocks, this should switch to a data-driven
+ * approach
+ */
+
+ if ((s->ccmr & CCMR_PRCS) == 1) {
+ s->pll_refclk_freq = CKIL_FREQ * 1024;
+ } else {
+ s->pll_refclk_freq = CKIH_FREQ;
+ }
+
+ /* ipg_clk_arm aka MCU clock */
+ if ((s->ccmr & CCMR_MDS) || !(s->ccmr & CCMR_MPE)) {
+ s->mcu_clk_freq = s->pll_refclk_freq;
+ } else {
+ s->mcu_clk_freq = calc_pll(s->mpctl, s->pll_refclk_freq);
+ }
+
+ /* High-speed clock */
+ s->hsp_clk_freq = s->mcu_clk_freq / (1 + EXTRACT(s->pdr0, HSP));
+ s->ipg_clk_freq = s->hsp_clk_freq / (1 + EXTRACT(s->pdr0, IPG));
+
+ DPRINTF("Clocks: mcu %uMHz, HSP %uMHz, IPG %uHz\n",
+ s->mcu_clk_freq / 1000000,
+ s->hsp_clk_freq / 1000000,
+ s->ipg_clk_freq);
+}
+
+static void imx_ccm_reset(DeviceState *dev)
+{
+ IMXCCMState *s = container_of(dev, IMXCCMState, busdev.qdev);
+
+ s->ccmr = 0x074b0b7b;
+ s->pdr0 = 0xff870b48;
+ s->pdr1 = 0x49fcfe7f;
+ s->mpctl = PLL_PD(1) | PLL_MFD(0) | PLL_MFI(6) | PLL_MFN(0);
+ s->cgr[0] = s->cgr[1] = s->cgr[2] = 0xffffffff;
+ s->spctl = PLL_PD(1) | PLL_MFD(4) | PLL_MFI(0xc) | PLL_MFN(1);
+ s->pmcr0 = 0x80209828;
+
+ update_clocks(s);
+}
+
+static uint64_t imx_ccm_read(void *opaque, target_phys_addr_t offset,
+ unsigned size)
+{
+ IMXCCMState *s = (IMXCCMState *)opaque;
+
+ DPRINTF("read(offset=%x)", offset >> 2);
+ switch (offset >> 2) {
+ case 0: /* CCMR */
+ DPRINTF(" ccmr = 0x%x\n", s->ccmr);
+ return s->ccmr;
+ case 1:
+ DPRINTF(" pdr0 = 0x%x\n", s->pdr0);
+ return s->pdr0;
+ case 2:
+ DPRINTF(" pdr1 = 0x%x\n", s->pdr1);
+ return s->pdr1;
+ case 4:
+ DPRINTF(" mpctl = 0x%x\n", s->mpctl);
+ return s->mpctl;
+ case 6:
+ DPRINTF(" spctl = 0x%x\n", s->spctl);
+ return s->spctl;
+ case 8:
+ DPRINTF(" cgr0 = 0x%x\n", s->cgr[0]);
+ return s->cgr[0];
+ case 9:
+ DPRINTF(" cgr1 = 0x%x\n", s->cgr[1]);
+ return s->cgr[1];
+ case 10:
+ DPRINTF(" cgr2 = 0x%x\n", s->cgr[2]);
+ return s->cgr[2];
+ case 18: /* LTR1 */
+ return 0x00004040;
+ case 23:
+ DPRINTF(" pcmr0 = 0x%x\n", s->pmcr0);
+ return s->pmcr0;
+ }
+ DPRINTF(" return 0\n");
+ return 0;
+}
+
+static void imx_ccm_write(void *opaque, target_phys_addr_t offset,
+ uint64_t value, unsigned size)
+{
+ IMXCCMState *s = (IMXCCMState *)opaque;
+
+ DPRINTF("write(offset=%x, value = %x)\n",
+ offset >> 2, (unsigned int)value);
+ switch (offset >> 2) {
+ case 0:
+ s->ccmr = CCMR_FPMF | (value & 0x3b6fdfff);
+ break;
+ case 1:
+ s->pdr0 = value & 0xff9f3fff;
+ break;
+ case 2:
+ s->pdr1 = value;
+ break;
+ case 4:
+ s->mpctl = value & 0xbfff3fff;
+ break;
+ case 6:
+ s->spctl = value & 0xbfff3fff;
+ break;
+ case 8:
+ s->cgr[0] = value;
+ return;
+ case 9:
+ s->cgr[1] = value;
+ return;
+ case 10:
+ s->cgr[2] = value;
+ return;
+
+ default:
+ return;
+ }
+ update_clocks(s);
+}
+
+static const struct MemoryRegionOps imx_ccm_ops = {
+ .read = imx_ccm_read,
+ .write = imx_ccm_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int imx_ccm_init(SysBusDevice *dev)
+{
+ IMXCCMState *s = FROM_SYSBUS(typeof(*s), dev);
+
+ memory_region_init_io(&s->iomem, &imx_ccm_ops, s, "imx_ccm", 0x1000);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ return 0;
+}
+
+static int imx_ccm_post_load(void *opaque, int version_id)
+{
+ IMXCCMState *s = (IMXCCMState *)opaque;
+
+ update_clocks(s);
+ return 0;
+}
+
+static void imx_ccm_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sbc->init = imx_ccm_init;
+ dc->reset = imx_ccm_reset;
+ dc->vmsd = &vmstate_imx_ccm;
+ dc->desc = "i.MX Clock Control Module";
+}
+
+static TypeInfo imx_ccm_info = {
+ .name = "imx_ccm",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(IMXCCMState),
+ .class_init = imx_ccm_class_init,
+};
+
+static void imx_ccm_register_types(void)
+{
+ type_register_static(&imx_ccm_info);
+}
+
+type_init(imx_ccm_register_types)
--
1.7.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Qemu-devel] [PATCH 07/10] i.MX31: Timers
2012-07-04 11:14 [Qemu-devel] [PULL 00/10] arm-devs queue Peter Maydell
` (5 preceding siblings ...)
2012-07-04 11:14 ` [Qemu-devel] [PATCH 06/10] i.MX31: Clock Control Module Peter Maydell
@ 2012-07-04 11:14 ` Peter Maydell
2012-07-04 11:14 ` [Qemu-devel] [PATCH 08/10] i.MX31: Interrupt Controller Peter Maydell
` (3 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Peter Maydell @ 2012-07-04 11:14 UTC (permalink / raw)
To: Anthony Liguori, Blue Swirl; +Cc: qemu-devel, Paul Brook
From: Peter Chubb <peter.chubb@nicta.com.au>
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>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/arm/Makefile.objs | 2 +-
hw/imx.h | 8 +
hw/imx_timer.c | 689 ++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 698 insertions(+), 1 deletions(-)
create mode 100644 hw/imx_timer.c
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 110f2f1..4109162 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -35,7 +35,7 @@ obj-y += framebuffer.o
obj-y += vexpress.o
obj-y += strongarm.o
obj-y += collie.o
-obj-y += imx_serial.o imx_ccm.o
+obj-y += imx_serial.o imx_ccm.o imx_timer.o
obj-y += pl041.o lm4549.o
obj-$(CONFIG_FDT) += ../device_tree.o
diff --git a/hw/imx.h b/hw/imx.h
index 707e943..ccf586f 100644
--- a/hw/imx.h
+++ b/hw/imx.h
@@ -23,4 +23,12 @@ typedef enum {
uint32_t imx_clock_frequency(DeviceState *s, IMXClk clock);
+void imx_timerp_create(const target_phys_addr_t addr,
+ qemu_irq irq,
+ DeviceState *ccm);
+void imx_timerg_create(const target_phys_addr_t addr,
+ qemu_irq irq,
+ DeviceState *ccm);
+
+
#endif /* IMX_H */
diff --git a/hw/imx_timer.c b/hw/imx_timer.c
new file mode 100644
index 0000000..16215cc
--- /dev/null
+++ b/hw/imx_timer.c
@@ -0,0 +1,689 @@
+/*
+ * 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"
+#include "imx.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 when it reaches TIMER_MAX (in freerun mode) or when it
+ * reaches the value of ocr1 (in periodic mode). WE simulate this using a
+ * QEMU ptimer counting down from ocr1 and reloading from ocr1 in
+ * periodic mode, or counting from ocr1 to zero, then TIMER_MAX - ocr1.
+ * waiting_rov is set when counting from TIMER_MAX.
+ *
+ * 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.x uses has been implemented
+ * (free-running timer from 0 to OCR1 or TIMER_MAX) .
+ */
+
+
+#define TIMER_MAX 0XFFFFFFFFUL
+
+/* 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_SHIFT (6)
+#define GPT_CR_CLKSRC_MASK (0x7)
+
+#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;
+ ptimer_state *timer;
+ MemoryRegion iomem;
+ DeviceState *ccm;
+
+ 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;
+} IMXTimerGState;
+
+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, IMXTimerGState),
+ VMSTATE_UINT32(pr, IMXTimerGState),
+ VMSTATE_UINT32(sr, IMXTimerGState),
+ VMSTATE_UINT32(ir, IMXTimerGState),
+ VMSTATE_UINT32(ocr1, IMXTimerGState),
+ VMSTATE_UINT32(cnt, IMXTimerGState),
+ VMSTATE_UINT32(waiting_rov, IMXTimerGState),
+ VMSTATE_PTIMER(timer, IMXTimerGState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const IMXClk imx_timerg_clocks[] = {
+ NOCLK, /* 000 No clock source */
+ IPG, /* 001 ipg_clk, 532MHz*/
+ IPG, /* 010 ipg_clk_highfreq */
+ NOCLK, /* 011 not defined */
+ CLK_32k, /* 100 ipg_clk_32k */
+ NOCLK, /* 101 not defined */
+ NOCLK, /* 110 not defined */
+ NOCLK, /* 111 not defined */
+};
+
+
+static void imx_timerg_set_freq(IMXTimerGState *s)
+{
+ int clksrc;
+ uint32_t freq;
+
+ clksrc = (s->cr >> GPT_CR_CLKSRC_SHIFT) & GPT_CR_CLKSRC_MASK;
+ freq = imx_clock_frequency(s->ccm, imx_timerg_clocks[clksrc]) / (1 + s->pr);
+
+ DPRINTF("Setting gtimer clksrc %d to frequency %d\n", clksrc, freq);
+ if (freq) {
+ ptimer_set_freq(s->timer, freq);
+ }
+}
+
+static void imx_timerg_update(IMXTimerGState *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 uint32_t imx_timerg_update_counts(IMXTimerGState *s)
+{
+ uint64_t target = s->waiting_rov ? TIMER_MAX : s->ocr1;
+ uint64_t cnt = ptimer_get_count(s->timer);
+ s->cnt = target - cnt;
+ return s->cnt;
+}
+
+static void imx_timerg_reload(IMXTimerGState *s, uint32_t timeout)
+{
+ uint64_t diff_cnt;
+
+ if (!(s->cr & GPT_CR_FRR)) {
+ IPRINTF("IMX_timerg_reload --- called in reset-mode\n");
+ return;
+ }
+
+ /*
+ * For small timeouts, qemu sometimes runs too slow.
+ * Better deliver a late interrupt than none.
+ *
+ * In Reset mode (FRR bit clear)
+ * the ptimer reloads itself from OCR1;
+ * in free-running mode we need to fake
+ * running from 0 to ocr1 to TIMER_MAX
+ */
+ if (timeout > s->cnt) {
+ diff_cnt = timeout - s->cnt;
+ } else {
+ diff_cnt = 0;
+ }
+ ptimer_set_count(s->timer, diff_cnt);
+}
+
+static uint64_t imx_timerg_read(void *opaque, target_phys_addr_t offset,
+ unsigned size)
+{
+ IMXTimerGState *s = (IMXTimerGState *)opaque;
+
+ DPRINTF("g-read(offset=%x)", offset >> 2);
+ switch (offset >> 2) {
+ case 0: /* Control Register */
+ DPRINTF(" cr = %x\n", s->cr);
+ return s->cr;
+
+ case 1: /* prescaler */
+ DPRINTF(" pr = %x\n", s->pr);
+ return s->pr;
+
+ case 2: /* Status Register */
+ DPRINTF(" sr = %x\n", s->sr);
+ return s->sr;
+
+ case 3: /* Interrupt Register */
+ DPRINTF(" ir = %x\n", s->ir);
+ return s->ir;
+
+ case 4: /* Output Compare Register 1 */
+ DPRINTF(" ocr1 = %x\n", s->ocr1);
+ return s->ocr1;
+
+
+ case 9: /* cnt */
+ imx_timerg_update_counts(s);
+ DPRINTF(" cnt = %x\n", s->cnt);
+ return s->cnt;
+ }
+
+ IPRINTF("imx_timerg_read: Bad offset %x\n",
+ (int)offset >> 2);
+ return 0;
+}
+
+static void imx_timerg_reset(DeviceState *dev)
+{
+ IMXTimerGState *s = container_of(dev, IMXTimerGState, 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;
+ ptimer_stop(s->timer);
+ ptimer_set_limit(s->timer, TIMER_MAX, 1);
+ imx_timerg_set_freq(s);
+}
+
+static void imx_timerg_write(void *opaque, target_phys_addr_t offset,
+ uint64_t value, unsigned size)
+{
+ IMXTimerGState *s = (IMXTimerGState *)opaque;
+ DPRINTF("g-write(offset=%x, value = 0x%x)\n", (unsigned int)offset >> 2,
+ (unsigned int)value);
+
+ switch (offset >> 2) {
+ case 0: {
+ uint32_t oldcr = s->cr;
+ /* CR */
+ if (value & GPT_CR_SWR) { /* force reset */
+ value &= ~GPT_CR_SWR;
+ imx_timerg_reset(&s->busdev.qdev);
+ imx_timerg_update(s);
+ }
+
+ s->cr = value & ~0x7c00;
+ imx_timerg_set_freq(s);
+ if ((oldcr ^ value) & GPT_CR_EN) {
+ if (value & GPT_CR_EN) {
+ if (value & GPT_CR_ENMOD) {
+ ptimer_set_count(s->timer, s->ocr1);
+ s->cnt = 0;
+ }
+ ptimer_run(s->timer,
+ (value & GPT_CR_FRR) && (s->ocr1 != TIMER_MAX));
+ } else {
+ ptimer_stop(s->timer);
+ };
+ }
+ return;
+ }
+
+ case 1: /* Prescaler */
+ s->pr = value & 0xfff;
+ imx_timerg_set_freq(s);
+ 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 */
+ if (!(s->cr & GPT_CR_FRR)) {
+ s->waiting_rov = 0;
+ ptimer_set_limit(s->timer, value, 1);
+ } else {
+ imx_timerg_update_counts(s);
+ if (value > s->cnt) {
+ s->waiting_rov = 0;
+ imx_timerg_reload(s, value);
+ } else {
+ s->waiting_rov = 1;
+ imx_timerg_reload(s, TIMER_MAX - s->cnt);
+ }
+ }
+ s->ocr1 = value;
+ return;
+
+ default:
+ IPRINTF("imx_timerg_write: Bad offset %x\n",
+ (int)offset >> 2);
+ }
+}
+
+static void imx_timerg_timeout(void *opaque)
+{
+ IMXTimerGState *s = (IMXTimerGState *)opaque;
+
+ DPRINTF("imx_timerg_timeout, waiting rov=%d\n", s->waiting_rov);
+ if (s->cr & GPT_CR_FRR) {
+ /*
+ * Free running timer from 0 -> TIMERMAX
+ * Generates interrupt at TIMER_MAX and at cnt==ocr1
+ * If ocr1 == TIMER_MAX, then no need to reload timer.
+ */
+ if (s->ocr1 == TIMER_MAX) {
+ DPRINTF("s->ocr1 == TIMER_MAX, FRR\n");
+ s->sr |= GPT_SR_OF1 | GPT_SR_ROV;
+ imx_timerg_update(s);
+ return;
+ }
+
+ if (s->waiting_rov) {
+ /*
+ * We were waiting for cnt==TIMER_MAX
+ */
+ s->sr |= GPT_SR_ROV;
+ s->waiting_rov = 0;
+ s->cnt = 0;
+ imx_timerg_reload(s, s->ocr1);
+ } else {
+ /* Must have got a cnt==ocr1 timeout. */
+ s->sr |= GPT_SR_OF1;
+ s->cnt = s->ocr1;
+ s->waiting_rov = 1;
+ imx_timerg_reload(s, TIMER_MAX);
+ }
+ imx_timerg_update(s);
+ return;
+ }
+
+ s->sr |= GPT_SR_OF1;
+ 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)
+{
+ IMXTimerGState *s = FROM_SYSBUS(IMXTimerGState, dev);
+ QEMUBH *bh;
+
+ 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);
+
+ bh = qemu_bh_new(imx_timerg_timeout, s);
+ s->timer = ptimer_init(bh);
+
+ /* Hard reset resets extra bits in CR */
+ s->cr = 0;
+ return 0;
+}
+
+
+
+/*
+ * EPIT: Enhanced periodic interrupt timer
+ */
+
+#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)
+#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 IMXClk imx_timerp_clocks[] = {
+ 0, /* disabled */
+ IPG, /* ipg_clk, ~532MHz */
+ IPG, /* ipg_clk_highfreq */
+ CLK_32k, /* ipg_clk_32k -- ~32kHz */
+};
+
+typedef struct {
+ SysBusDevice busdev;
+ ptimer_state *timer;
+ MemoryRegion iomem;
+ DeviceState *ccm;
+
+ uint32_t cr;
+ uint32_t lr;
+ uint32_t cmp;
+
+ uint32_t freq;
+ int int_level;
+ qemu_irq irq;
+} IMXTimerPState;
+
+/*
+ * Update interrupt status
+ */
+static void imx_timerp_update(IMXTimerPState *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)
+{
+ IMXTimerPState *s = container_of(dev, IMXTimerPState, busdev.qdev);
+
+ s->cr = 0;
+ s->lr = TIMER_MAX;
+ s->int_level = 0;
+ s->cmp = 0;
+ ptimer_stop(s->timer);
+ ptimer_set_count(s->timer, TIMER_MAX);
+}
+
+static uint64_t imx_timerp_read(void *opaque, target_phys_addr_t offset,
+ unsigned size)
+{
+ IMXTimerPState *s = (IMXTimerPState *)opaque;
+
+ DPRINTF("p-read(offset=%x)", offset >> 2);
+ switch (offset >> 2) {
+ case 0: /* Control Register */
+ DPRINTF("cr %x\n", s->cr);
+ return s->cr;
+
+ case 1: /* Status Register */
+ DPRINTF("int_level %x\n", s->int_level);
+ return s->int_level;
+
+ case 2: /* LR - ticks*/
+ DPRINTF("lr %x\n", s->lr);
+ return s->lr;
+
+ case 3: /* CMP */
+ DPRINTF("cmp %x\n", s->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(IMXTimerPState *s)
+{
+ int clksrc;
+ unsigned prescaler;
+ uint32_t freq;
+
+ clksrc = (s->cr & CR_CLKSRC_MASK) >> CR_CLKSRC_SHIFT;
+ prescaler = 1 + ((s->cr >> CR_PRESCALE_SHIFT) & CR_PRESCALE_MASK);
+ freq = imx_clock_frequency(s->ccm, imx_timerp_clocks[clksrc]) / prescaler;
+
+ s->freq = freq;
+ DPRINTF("Setting ptimer frequency to %u\n", 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)
+{
+ IMXTimerPState *s = (IMXTimerPState *)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)) {
+ if (!(s->cr & CR_ENMOD)) {
+ ptimer_set_count(s->timer, s->lr);
+ }
+ 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;
+ if (value) {
+ IPRINTF(
+ "Values for EPIT comparison other than zero not supported\n"
+ );
+ }
+ break;
+
+ default:
+ IPRINTF("imx_timerp_write: Bad offset %x\n",
+ (int)offset >> 2);
+ }
+}
+
+static void imx_timerp_tick(void *opaque)
+{
+ IMXTimerPState *s = (IMXTimerPState *)opaque;
+
+ DPRINTF("imxp tick\n");
+ if (!(s->cr & CR_RLD)) {
+ ptimer_set_count(s->timer, TIMER_MAX);
+ }
+ s->int_level = 1;
+ imx_timerp_update(s);
+}
+
+void imx_timerp_create(const target_phys_addr_t addr,
+ qemu_irq irq,
+ DeviceState *ccm)
+{
+ IMXTimerPState *pp;
+ DeviceState *dev;
+
+ dev = sysbus_create_simple("imx_timerp", addr, irq);
+ pp = container_of(dev, IMXTimerPState, busdev.qdev);
+ pp->ccm = ccm;
+}
+
+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, IMXTimerPState),
+ VMSTATE_UINT32(lr, IMXTimerPState),
+ VMSTATE_UINT32(cmp, IMXTimerPState),
+ VMSTATE_UINT32(freq, IMXTimerPState),
+ VMSTATE_INT32(int_level, IMXTimerPState),
+ VMSTATE_PTIMER(timer, IMXTimerPState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int imx_timerp_init(SysBusDevice *dev)
+{
+ IMXTimerPState *s = FROM_SYSBUS(IMXTimerPState, 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;
+}
+
+
+void imx_timerg_create(const target_phys_addr_t addr,
+ qemu_irq irq,
+ DeviceState *ccm)
+{
+ IMXTimerGState *pp;
+ DeviceState *dev;
+
+ dev = sysbus_create_simple("imx_timerg", addr, irq);
+ pp = container_of(dev, IMXTimerGState, busdev.qdev);
+ pp->ccm = ccm;
+}
+
+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 const TypeInfo imx_timerp_info = {
+ .name = "imx_timerp",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(IMXTimerPState),
+ .class_init = imx_timerp_class_init,
+};
+
+static const TypeInfo imx_timerg_info = {
+ .name = "imx_timerg",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(IMXTimerGState),
+ .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)
--
1.7.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Qemu-devel] [PATCH 08/10] i.MX31: Interrupt Controller
2012-07-04 11:14 [Qemu-devel] [PULL 00/10] arm-devs queue Peter Maydell
` (6 preceding siblings ...)
2012-07-04 11:14 ` [Qemu-devel] [PATCH 07/10] i.MX31: Timers Peter Maydell
@ 2012-07-04 11:14 ` Peter Maydell
2012-07-04 11:14 ` [Qemu-devel] [PATCH 09/10] i.MX31: KZM-ARM11-01 evaluation board Peter Maydell
` (2 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Peter Maydell @ 2012-07-04 11:14 UTC (permalink / raw)
To: Anthony Liguori, Blue Swirl; +Cc: qemu-devel, Paul Brook
From: Peter Chubb <peter.chubb@nicta.com.au>
Implement the Freescale i.MX31 advanced vectored interrupt controller, at least
to the extent it is used by Linux 3.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>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/arm/Makefile.objs | 2 +-
hw/imx_avic.c | 408 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 409 insertions(+), 1 deletions(-)
create mode 100644 hw/imx_avic.c
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 4109162..15b9357 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -35,7 +35,7 @@ obj-y += framebuffer.o
obj-y += vexpress.o
obj-y += strongarm.o
obj-y += collie.o
-obj-y += imx_serial.o imx_ccm.o imx_timer.o
+obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
obj-y += pl041.o lm4549.o
obj-$(CONFIG_FDT) += ../device_tree.o
diff --git a/hw/imx_avic.c b/hw/imx_avic.c
new file mode 100644
index 0000000..25f47f3
--- /dev/null
+++ b/hw/imx_avic.c
@@ -0,0 +1,408 @@
+/*
+ * i.MX31 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 */
+} IMXAVICState;
+
+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, IMXAVICState),
+ VMSTATE_UINT64(enabled, IMXAVICState),
+ VMSTATE_UINT64(is_fiq, IMXAVICState),
+ VMSTATE_UINT32(intcntl, IMXAVICState),
+ VMSTATE_UINT32(intmask, IMXAVICState),
+ VMSTATE_UINT32_ARRAY(prio, IMXAVICState, PRIO_WORDS),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+
+
+static inline int imx_avic_prio(IMXAVICState *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(IMXAVICState *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(IMXAVICState *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)
+{
+ IMXAVICState *s = (IMXAVICState *)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)
+{
+ IMXAVICState *s = (IMXAVICState *)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)
+{
+ IMXAVICState *s = (IMXAVICState *)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)
+{
+ IMXAVICState *s = container_of(dev, IMXAVICState, 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)
+{
+ IMXAVICState *s = FROM_SYSBUS(IMXAVICState, 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 const TypeInfo imx_avic_info = {
+ .name = "imx_avic",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(IMXAVICState),
+ .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)
--
1.7.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Qemu-devel] [PATCH 09/10] i.MX31: KZM-ARM11-01 evaluation board
2012-07-04 11:14 [Qemu-devel] [PULL 00/10] arm-devs queue Peter Maydell
` (7 preceding siblings ...)
2012-07-04 11:14 ` [Qemu-devel] [PATCH 08/10] i.MX31: Interrupt Controller Peter Maydell
@ 2012-07-04 11:14 ` Peter Maydell
2012-07-04 11:14 ` [Qemu-devel] [PATCH 10/10] hw/cadence_gem: Make rx_desc_addr and tx_desc_addr uint32_t Peter Maydell
2012-07-07 9:21 ` [Qemu-devel] [PULL 00/10] arm-devs queue Blue Swirl
10 siblings, 0 replies; 14+ messages in thread
From: Peter Maydell @ 2012-07-04 11:14 UTC (permalink / raw)
To: Anthony Liguori, Blue Swirl; +Cc: qemu-devel, Paul Brook
From: Peter Chubb <peter.chubb@nicta.com.au>
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>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
MAINTAINERS | 6 ++
hw/arm/Makefile.objs | 1 +
hw/kzm.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 161 insertions(+), 0 deletions(-)
create mode 100644 hw/kzm.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 2a514fd..30ed56d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -207,6 +207,12 @@ M: qemu-devel@nongnu.org
S: Orphan
F: hw/gumstix.c
+i.MX31
+M: Peter Chubb <peter.chubb@nicta.com.au>
+S: Odd fixes
+F: hw/imx*
+F: hw/kzm.c
+
Integrator CP
M: Paul Brook <paul@codesourcery.com>
M: Peter Maydell <peter.maydell@linaro.org>
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 15b9357..236786e 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -36,6 +36,7 @@ obj-y += vexpress.o
obj-y += strongarm.o
obj-y += collie.o
obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
+obj-y += kzm.o
obj-y += pl041.o lm4549.o
obj-$(CONFIG_FDT) += ../device_tree.o
diff --git a/hw/kzm.c b/hw/kzm.c
new file mode 100644
index 0000000..08aaf43
--- /dev/null
+++ b/hw/kzm.c
@@ -0,0 +1,154 @@
+/*
+ * KZM Board System emulation.
+ *
+ * Copyright (c) 2008 OKL and 2011 NICTA
+ * Written by Hans at OK-Labs
+ * 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 "devices.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 AVIC EMULATED
+ * 0x53f80000 CCM 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)
+{
+ ARMCPU *cpu;
+ 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;
+ DeviceState *ccm;
+
+ if (!cpu_model) {
+ cpu_model = "arm1136";
+ }
+
+ cpu = cpu_arm_init(cpu_model);
+ if (!cpu) {
+ 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(cpu);
+ 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));
+
+ ccm = sysbus_create_simple("imx_ccm", 0x53f80000, NULL);
+
+ imx_timerp_create(0x53f94000, qdev_get_gpio_in(dev, 28), ccm);
+ imx_timerp_create(0x53f98000, qdev_get_gpio_in(dev, 27), ccm);
+ imx_timerg_create(0x53f90000, qdev_get_gpio_in(dev, 29), ccm);
+
+ if (nd_table[0].vlan) {
+ lan9118_init(&nd_table[0], 0xb6000000, qdev_get_gpio_in(dev, 52));
+ }
+
+ 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(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)
--
1.7.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Qemu-devel] [PATCH 10/10] hw/cadence_gem: Make rx_desc_addr and tx_desc_addr uint32_t
2012-07-04 11:14 [Qemu-devel] [PULL 00/10] arm-devs queue Peter Maydell
` (8 preceding siblings ...)
2012-07-04 11:14 ` [Qemu-devel] [PATCH 09/10] i.MX31: KZM-ARM11-01 evaluation board Peter Maydell
@ 2012-07-04 11:14 ` Peter Maydell
2012-07-07 9:21 ` [Qemu-devel] [PULL 00/10] arm-devs queue Blue Swirl
10 siblings, 0 replies; 14+ messages in thread
From: Peter Maydell @ 2012-07-04 11:14 UTC (permalink / raw)
To: Anthony Liguori, Blue Swirl; +Cc: qemu-devel, Paul Brook
Make the state fields rx_desc_addr and tx_desc_addr uint32_t;
this matches the VMStateDescription, and also conforms to how
hardware works: the registers don't magically become larger
if the device is attached to a CPU with a larger physical
address size. It also fixes a compile failure if the
target_phys_addr_t type is changed to 64 bits.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Peter A. G. Crosthwaite <peter.crosthwaite@petalogix.com>
---
hw/cadence_gem.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/hw/cadence_gem.c b/hw/cadence_gem.c
index dbde392..87143ca 100644
--- a/hw/cadence_gem.c
+++ b/hw/cadence_gem.c
@@ -339,8 +339,8 @@ typedef struct {
uint8_t phy_loop; /* Are we in phy loopback? */
/* The current DMA descriptor pointers */
- target_phys_addr_t rx_desc_addr;
- target_phys_addr_t tx_desc_addr;
+ uint32_t rx_desc_addr;
+ uint32_t tx_desc_addr;
} GemState;
--
1.7.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [Qemu-devel] [PULL 00/10] arm-devs queue
2012-07-04 11:14 [Qemu-devel] [PULL 00/10] arm-devs queue Peter Maydell
` (9 preceding siblings ...)
2012-07-04 11:14 ` [Qemu-devel] [PATCH 10/10] hw/cadence_gem: Make rx_desc_addr and tx_desc_addr uint32_t Peter Maydell
@ 2012-07-07 9:21 ` Blue Swirl
10 siblings, 0 replies; 14+ messages in thread
From: Blue Swirl @ 2012-07-07 9:21 UTC (permalink / raw)
To: Peter Maydell; +Cc: qemu-devel, Anthony Liguori, Paul Brook
On Wed, Jul 4, 2012 at 11:14 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
> Usual arm-devs pullreq; this contains some minor exynos fixes
> and the iMX31 model. Please pull.
Thanks, pulled.
>
> thanks
> -- PMM
>
>
> The following changes since commit 71ea2e016131a9fcde6f1ffd3e0e34a64c21f593:
>
> bsd-user: fix build (2012-06-28 20:28:36 +0000)
>
> are available in the git repository at:
> git://git.linaro.org/people/pmaydell/qemu-arm.git arm-devs.for-upstream
>
> Evgeny Voevodin (2):
> hw/exynos4210_pwm.c: Fix STOP status in tick handler.
> hw/exynos4210.c: Fix misleading initialization of IROM mirror
>
> Oleg Ogurtsov (1):
> Exynos4: add RTC device
>
> Peter Chubb (5):
> i.MX: UART support
> i.MX31: Clock Control Module
> i.MX31: Timers
> i.MX31: Interrupt Controller
> i.MX31: KZM-ARM11-01 evaluation board
>
> Peter Maydell (1):
> hw/cadence_gem: Make rx_desc_addr and tx_desc_addr uint32_t
>
> Stanislav Vorobiov (1):
> ARM: hw/exynos4210_mct.c: Fix a bug which hangs Linux kernel.
>
> MAINTAINERS | 6 +
> hw/arm/Makefile.objs | 3 +
> hw/cadence_gem.c | 4 +-
> hw/exynos4210.c | 10 +-
> hw/exynos4210_mct.c | 4 -
> hw/exynos4210_pwm.c | 2 +-
> hw/exynos4210_rtc.c | 595 +++++++++++++++++++++++++++++++++++++++++++
> hw/imx.h | 34 +++
> hw/imx_avic.c | 408 +++++++++++++++++++++++++++++
> hw/imx_ccm.c | 321 +++++++++++++++++++++++
> hw/imx_serial.c | 467 ++++++++++++++++++++++++++++++++++
> hw/imx_timer.c | 689 ++++++++++++++++++++++++++++++++++++++++++++++++++
> hw/kzm.c | 154 +++++++++++
> 13 files changed, 2689 insertions(+), 8 deletions(-)
> create mode 100644 hw/exynos4210_rtc.c
> create mode 100644 hw/imx.h
> create mode 100644 hw/imx_avic.c
> create mode 100644 hw/imx_ccm.c
> create mode 100644 hw/imx_serial.c
> create mode 100644 hw/imx_timer.c
> create mode 100644 hw/kzm.c
^ permalink raw reply [flat|nested] 14+ messages in thread