diff options
Diffstat (limited to 'hw/timer')
| -rw-r--r-- | hw/timer/Makefile.objs | 3 | ||||
| -rw-r--r-- | hw/timer/altera_timer.c | 237 | ||||
| -rw-r--r-- | hw/timer/ds1338.c | 10 | ||||
| -rw-r--r-- | hw/timer/mc146818rtc.c | 12 | ||||
| -rw-r--r-- | hw/timer/sun4v-rtc.c | 102 | ||||
| -rw-r--r-- | hw/timer/twl92230.c | 12 |
6 files changed, 366 insertions, 10 deletions
diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index 7ba8c23c75..71994f2d88 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -18,6 +18,7 @@ common-obj-$(CONFIG_IMX) += imx_gpt.o common-obj-$(CONFIG_LM32) += lm32_timer.o common-obj-$(CONFIG_MILKYMIST) += milkymist-sysctl.o +obj-$(CONFIG_ALTERA_TIMER) += altera_timer.o obj-$(CONFIG_EXYNOS4) += exynos4210_mct.o obj-$(CONFIG_EXYNOS4) += exynos4210_pwm.o obj-$(CONFIG_EXYNOS4) += exynos4210_rtc.o @@ -34,3 +35,5 @@ obj-$(CONFIG_ALLWINNER_A10_PIT) += allwinner-a10-pit.o common-obj-$(CONFIG_STM32F2XX_TIMER) += stm32f2xx_timer.o common-obj-$(CONFIG_ASPEED_SOC) += aspeed_timer.o + +common-obj-$(CONFIG_SUN4V_RTC) += sun4v-rtc.o diff --git a/hw/timer/altera_timer.c b/hw/timer/altera_timer.c new file mode 100644 index 0000000000..6d4862661d --- /dev/null +++ b/hw/timer/altera_timer.c @@ -0,0 +1,237 @@ +/* + * QEMU model of the Altera timer. + * + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/lgpl-2.1.html> + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qapi/error.h" + +#include "hw/sysbus.h" +#include "sysemu/sysemu.h" +#include "hw/ptimer.h" + +#define R_STATUS 0 +#define R_CONTROL 1 +#define R_PERIODL 2 +#define R_PERIODH 3 +#define R_SNAPL 4 +#define R_SNAPH 5 +#define R_MAX 6 + +#define STATUS_TO 0x0001 +#define STATUS_RUN 0x0002 + +#define CONTROL_ITO 0x0001 +#define CONTROL_CONT 0x0002 +#define CONTROL_START 0x0004 +#define CONTROL_STOP 0x0008 + +#define TYPE_ALTERA_TIMER "ALTR.timer" +#define ALTERA_TIMER(obj) \ + OBJECT_CHECK(AlteraTimer, (obj), TYPE_ALTERA_TIMER) + +typedef struct AlteraTimer { + SysBusDevice busdev; + MemoryRegion mmio; + qemu_irq irq; + uint32_t freq_hz; + QEMUBH *bh; + ptimer_state *ptimer; + uint32_t regs[R_MAX]; +} AlteraTimer; + +static int timer_irq_state(AlteraTimer *t) +{ + bool irq = (t->regs[R_STATUS] & STATUS_TO) && + (t->regs[R_CONTROL] & CONTROL_ITO); + return irq; +} + +static uint64_t timer_read(void *opaque, hwaddr addr, + unsigned int size) +{ + AlteraTimer *t = opaque; + uint64_t r = 0; + + addr >>= 2; + + switch (addr) { + case R_CONTROL: + r = t->regs[R_CONTROL] & (CONTROL_ITO | CONTROL_CONT); + break; + + default: + if (addr < ARRAY_SIZE(t->regs)) { + r = t->regs[addr]; + } + break; + } + + return r; +} + +static void timer_write(void *opaque, hwaddr addr, + uint64_t value, unsigned int size) +{ + AlteraTimer *t = opaque; + uint64_t tvalue; + uint32_t count = 0; + int irqState = timer_irq_state(t); + + addr >>= 2; + + switch (addr) { + case R_STATUS: + /* The timeout bit is cleared by writing the status register. */ + t->regs[R_STATUS] &= ~STATUS_TO; + break; + + case R_CONTROL: + t->regs[R_CONTROL] = value & (CONTROL_ITO | CONTROL_CONT); + if ((value & CONTROL_START) && + !(t->regs[R_STATUS] & STATUS_RUN)) { + ptimer_run(t->ptimer, 1); + t->regs[R_STATUS] |= STATUS_RUN; + } + if ((value & CONTROL_STOP) && (t->regs[R_STATUS] & STATUS_RUN)) { + ptimer_stop(t->ptimer); + t->regs[R_STATUS] &= ~STATUS_RUN; + } + break; + + case R_PERIODL: + case R_PERIODH: + t->regs[addr] = value & 0xFFFF; + if (t->regs[R_STATUS] & STATUS_RUN) { + ptimer_stop(t->ptimer); + t->regs[R_STATUS] &= ~STATUS_RUN; + } + tvalue = (t->regs[R_PERIODH] << 16) | t->regs[R_PERIODL]; + ptimer_set_limit(t->ptimer, tvalue + 1, 1); + break; + + case R_SNAPL: + case R_SNAPH: + count = ptimer_get_count(t->ptimer); + t->regs[R_SNAPL] = count & 0xFFFF; + t->regs[R_SNAPH] = count >> 16; + break; + + default: + break; + } + + if (irqState != timer_irq_state(t)) { + qemu_set_irq(t->irq, timer_irq_state(t)); + } +} + +static const MemoryRegionOps timer_ops = { + .read = timer_read, + .write = timer_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4 + } +}; + +static void timer_hit(void *opaque) +{ + AlteraTimer *t = opaque; + const uint64_t tvalue = (t->regs[R_PERIODH] << 16) | t->regs[R_PERIODL]; + + t->regs[R_STATUS] |= STATUS_TO; + + ptimer_set_limit(t->ptimer, tvalue + 1, 1); + + if (!(t->regs[R_CONTROL] & CONTROL_CONT)) { + t->regs[R_STATUS] &= ~STATUS_RUN; + ptimer_set_count(t->ptimer, tvalue); + } else { + ptimer_run(t->ptimer, 1); + } + + qemu_set_irq(t->irq, timer_irq_state(t)); +} + +static void altera_timer_realize(DeviceState *dev, Error **errp) +{ + AlteraTimer *t = ALTERA_TIMER(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + if (t->freq_hz == 0) { + error_setg(errp, "\"clock-frequency\" property must be provided."); + return; + } + + t->bh = qemu_bh_new(timer_hit, t); + t->ptimer = ptimer_init(t->bh, PTIMER_POLICY_DEFAULT); + ptimer_set_freq(t->ptimer, t->freq_hz); + + memory_region_init_io(&t->mmio, OBJECT(t), &timer_ops, t, + TYPE_ALTERA_TIMER, R_MAX * sizeof(uint32_t)); + sysbus_init_mmio(sbd, &t->mmio); +} + +static void altera_timer_init(Object *obj) +{ + AlteraTimer *t = ALTERA_TIMER(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + sysbus_init_irq(sbd, &t->irq); +} + +static void altera_timer_reset(DeviceState *dev) +{ + AlteraTimer *t = ALTERA_TIMER(dev); + + ptimer_stop(t->ptimer); + ptimer_set_limit(t->ptimer, 0xffffffff, 1); + memset(t->regs, 0, ARRAY_SIZE(t->regs)); +} + +static Property altera_timer_properties[] = { + DEFINE_PROP_UINT32("clock-frequency", AlteraTimer, freq_hz, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void altera_timer_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = altera_timer_realize; + dc->props = altera_timer_properties; + dc->reset = altera_timer_reset; +} + +static const TypeInfo altera_timer_info = { + .name = TYPE_ALTERA_TIMER, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AlteraTimer), + .instance_init = altera_timer_init, + .class_init = altera_timer_class_init, +}; + +static void altera_timer_register(void) +{ + type_register_static(&altera_timer_info); +} + +type_init(altera_timer_register) diff --git a/hw/timer/ds1338.c b/hw/timer/ds1338.c index 0112949e23..3849b74a68 100644 --- a/hw/timer/ds1338.c +++ b/hw/timer/ds1338.c @@ -94,7 +94,7 @@ static void inc_regptr(DS1338State *s) } } -static void ds1338_event(I2CSlave *i2c, enum i2c_event event) +static int ds1338_event(I2CSlave *i2c, enum i2c_event event) { DS1338State *s = DS1338(i2c); @@ -113,6 +113,8 @@ static void ds1338_event(I2CSlave *i2c, enum i2c_event event) default: break; } + + return 0; } static int ds1338_recv(I2CSlave *i2c) @@ -198,11 +200,6 @@ static int ds1338_send(I2CSlave *i2c, uint8_t data) return 0; } -static int ds1338_init(I2CSlave *i2c) -{ - return 0; -} - static void ds1338_reset(DeviceState *dev) { DS1338State *s = DS1338(dev); @@ -220,7 +217,6 @@ static void ds1338_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); - k->init = ds1338_init; k->event = ds1338_event; k->recv = ds1338_recv; k->send = ds1338_send; diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c index da209d02f0..637f8722a7 100644 --- a/hw/timer/mc146818rtc.c +++ b/hw/timer/mc146818rtc.c @@ -946,11 +946,23 @@ static Property mc146818rtc_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +static void rtc_resetdev(DeviceState *d) +{ + RTCState *s = MC146818_RTC(d); + + /* Reason: VM do suspend self will set 0xfe + * Reset any values other than 0xfe(Guest suspend case) */ + if (s->cmos_data[0x0f] != 0xfe) { + s->cmos_data[0x0f] = 0x00; + } +} + static void rtc_class_initfn(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = rtc_realizefn; + dc->reset = rtc_resetdev; dc->vmsd = &vmstate_rtc; dc->props = mc146818rtc_properties; /* Reason: needs to be wired up by rtc_init() */ diff --git a/hw/timer/sun4v-rtc.c b/hw/timer/sun4v-rtc.c new file mode 100644 index 0000000000..310523225f --- /dev/null +++ b/hw/timer/sun4v-rtc.c @@ -0,0 +1,102 @@ +/* + * QEMU sun4v Real Time Clock device + * + * The sun4v_rtc device (sun4v tod clock) + * + * Copyright (c) 2016 Artyom Tarasenko + * + * This code is licensed under the GNU GPL v3 or (at your option) any later + * version. + */ + +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "qemu/timer.h" +#include "hw/timer/sun4v-rtc.h" + +//#define DEBUG_SUN4V_RTC + +#ifdef DEBUG_SUN4V_RTC +#define DPRINTF(fmt, ...) \ + do { printf("sun4v_rtc: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) do {} while (0) +#endif + +#define TYPE_SUN4V_RTC "sun4v_rtc" +#define SUN4V_RTC(obj) OBJECT_CHECK(Sun4vRtc, (obj), TYPE_SUN4V_RTC) + +typedef struct Sun4vRtc { + SysBusDevice parent_obj; + + MemoryRegion iomem; +} Sun4vRtc; + +static uint64_t sun4v_rtc_read(void *opaque, hwaddr addr, + unsigned size) +{ + uint64_t val = get_clock_realtime() / NANOSECONDS_PER_SECOND; + if (!(addr & 4ULL)) { + /* accessing the high 32 bits */ + val >>= 32; + } + DPRINTF("read from " TARGET_FMT_plx " val %lx\n", addr, val); + return val; +} + +static void sun4v_rtc_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + DPRINTF("write 0x%x to " TARGET_FMT_plx "\n", (unsigned)val, addr); +} + +static const MemoryRegionOps sun4v_rtc_ops = { + .read = sun4v_rtc_read, + .write = sun4v_rtc_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +void sun4v_rtc_init(hwaddr addr) +{ + DeviceState *dev; + SysBusDevice *s; + + dev = qdev_create(NULL, TYPE_SUN4V_RTC); + s = SYS_BUS_DEVICE(dev); + + qdev_init_nofail(dev); + + sysbus_mmio_map(s, 0, addr); +} + +static int sun4v_rtc_init1(SysBusDevice *dev) +{ + Sun4vRtc *s = SUN4V_RTC(dev); + + memory_region_init_io(&s->iomem, OBJECT(s), &sun4v_rtc_ops, s, + "sun4v-rtc", 0x08ULL); + sysbus_init_mmio(dev, &s->iomem); + return 0; +} + +static void sun4v_rtc_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = sun4v_rtc_init1; +} + +static const TypeInfo sun4v_rtc_info = { + .name = TYPE_SUN4V_RTC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Sun4vRtc), + .class_init = sun4v_rtc_class_init, +}; + +static void sun4v_rtc_register_types(void) +{ + type_register_static(&sun4v_rtc_info); +} + +type_init(sun4v_rtc_register_types) diff --git a/hw/timer/twl92230.c b/hw/timer/twl92230.c index 7ba4e9a7c9..c0aa8ae3de 100644 --- a/hw/timer/twl92230.c +++ b/hw/timer/twl92230.c @@ -713,12 +713,14 @@ static void menelaus_write(void *opaque, uint8_t addr, uint8_t value) } } -static void menelaus_event(I2CSlave *i2c, enum i2c_event event) +static int menelaus_event(I2CSlave *i2c, enum i2c_event event) { MenelausState *s = TWL92230(i2c); if (event == I2C_START_SEND) s->firstbyte = 1; + + return 0; } static int menelaus_tx(I2CSlave *i2c, uint8_t data) @@ -747,17 +749,21 @@ static int menelaus_rx(I2CSlave *i2c) Or we broke compatibility in the state, or we can't use struct tm */ -static int get_int32_as_uint16(QEMUFile *f, void *pv, size_t size) +static int get_int32_as_uint16(QEMUFile *f, void *pv, size_t size, + VMStateField *field) { int *v = pv; *v = qemu_get_be16(f); return 0; } -static void put_int32_as_uint16(QEMUFile *f, void *pv, size_t size) +static int put_int32_as_uint16(QEMUFile *f, void *pv, size_t size, + VMStateField *field, QJSON *vmdesc) { int *v = pv; qemu_put_be16(f, *v); + + return 0; } static const VMStateInfo vmstate_hack_int32_as_uint16 = { |