hw: move target-independent files to subdirectories

This patch tackles all files that are compiled once, moving
them to subdirectories of hw/.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Paolo Bonzini committed 2013-04-08 18:13:12 +02:00
1 parent ce3b494cb5
commit 49ab747f66
227 files changed
+205 -208

No files matched your search

+10
View File
@@ -0,0 +1,10 @@
common-obj-$(CONFIG_ARM_TIMER) += arm_timer.o
common-obj-$(CONFIG_CADENCE) += cadence_ttc.o
common-obj-$(CONFIG_DS1338) += ds1338.o
common-obj-$(CONFIG_HPET) += hpet.o
common-obj-$(CONFIG_I8254) += i8254_common.o i8254.o
common-obj-$(CONFIG_M48T59) += m48t59.o
common-obj-$(CONFIG_PL031) += pl031.o
common-obj-$(CONFIG_PUV3) += puv3_ost.o
common-obj-$(CONFIG_TWL92230) += twl92230.o
common-obj-$(CONFIG_XILINX) += xilinx_timer.o
+399
View File
@@ -0,0 +1,399 @@
/*
* ARM PrimeCell Timer modules.
*
* Copyright (c) 2005-2006 CodeSourcery.
* Written by Paul Brook
*
* This code is licensed under the GPL.
*/
#include "hw/sysbus.h"
#include "qemu/timer.h"
#include "qemu-common.h"
#include "hw/qdev.h"
#include "hw/ptimer.h"
/* Common timer implementation. */
#define TIMER_CTRL_ONESHOT (1 << 0)
#define TIMER_CTRL_32BIT (1 << 1)
#define TIMER_CTRL_DIV1 (0 << 2)
#define TIMER_CTRL_DIV16 (1 << 2)
#define TIMER_CTRL_DIV256 (2 << 2)
#define TIMER_CTRL_IE (1 << 5)
#define TIMER_CTRL_PERIODIC (1 << 6)
#define TIMER_CTRL_ENABLE (1 << 7)
typedef struct {
ptimer_state *timer;
uint32_t control;
uint32_t limit;
int freq;
int int_level;
qemu_irq irq;
} arm_timer_state;
/* Check all active timers, and schedule the next timer interrupt. */
static void arm_timer_update(arm_timer_state *s)
{
/* Update interrupts. */
if (s->int_level && (s->control & TIMER_CTRL_IE)) {
qemu_irq_raise(s->irq);
} else {
qemu_irq_lower(s->irq);
}
}
static uint32_t arm_timer_read(void *opaque, hwaddr offset)
{
arm_timer_state *s = (arm_timer_state *)opaque;
switch (offset >> 2) {
case 0: /* TimerLoad */
case 6: /* TimerBGLoad */
return s->limit;
case 1: /* TimerValue */
return ptimer_get_count(s->timer);
case 2: /* TimerControl */
return s->control;
case 4: /* TimerRIS */
return s->int_level;
case 5: /* TimerMIS */
if ((s->control & TIMER_CTRL_IE) == 0)
return 0;
return s->int_level;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Bad offset %x\n", __func__, (int)offset);
return 0;
}
}
/* Reset the timer limit after settings have changed. */
static void arm_timer_recalibrate(arm_timer_state *s, int reload)
{
uint32_t limit;
if ((s->control & (TIMER_CTRL_PERIODIC | TIMER_CTRL_ONESHOT)) == 0) {
/* Free running. */
if (s->control & TIMER_CTRL_32BIT)
limit = 0xffffffff;
else
limit = 0xffff;
} else {
/* Periodic. */
limit = s->limit;
}
ptimer_set_limit(s->timer, limit, reload);
}
static void arm_timer_write(void *opaque, hwaddr offset,
uint32_t value)
{
arm_timer_state *s = (arm_timer_state *)opaque;
int freq;
switch (offset >> 2) {
case 0: /* TimerLoad */
s->limit = value;
arm_timer_recalibrate(s, 1);
break;
case 1: /* TimerValue */
/* ??? Linux seems to want to write to this readonly register.
Ignore it. */
break;
case 2: /* TimerControl */
if (s->control & TIMER_CTRL_ENABLE) {
/* Pause the timer if it is running. This may cause some
inaccuracy dure to rounding, but avoids a whole lot of other
messyness. */
ptimer_stop(s->timer);
}
s->control = value;
freq = s->freq;
/* ??? Need to recalculate expiry time after changing divisor. */
switch ((value >> 2) & 3) {
case 1: freq >>= 4; break;
case 2: freq >>= 8; break;
}
arm_timer_recalibrate(s, s->control & TIMER_CTRL_ENABLE);
ptimer_set_freq(s->timer, freq);
if (s->control & TIMER_CTRL_ENABLE) {
/* Restart the timer if still enabled. */
ptimer_run(s->timer, (s->control & TIMER_CTRL_ONESHOT) != 0);
}
break;
case 3: /* TimerIntClr */
s->int_level = 0;
break;
case 6: /* TimerBGLoad */
s->limit = value;
arm_timer_recalibrate(s, 0);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Bad offset %x\n", __func__, (int)offset);
}
arm_timer_update(s);
}
static void arm_timer_tick(void *opaque)
{
arm_timer_state *s = (arm_timer_state *)opaque;
s->int_level = 1;
arm_timer_update(s);
}
static const VMStateDescription vmstate_arm_timer = {
.name = "arm_timer",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(control, arm_timer_state),
VMSTATE_UINT32(limit, arm_timer_state),
VMSTATE_INT32(int_level, arm_timer_state),
VMSTATE_PTIMER(timer, arm_timer_state),
VMSTATE_END_OF_LIST()
}
};
static arm_timer_state *arm_timer_init(uint32_t freq)
{
arm_timer_state *s;
QEMUBH *bh;
s = (arm_timer_state *)g_malloc0(sizeof(arm_timer_state));
s->freq = freq;
s->control = TIMER_CTRL_IE;
bh = qemu_bh_new(arm_timer_tick, s);
s->timer = ptimer_init(bh);
vmstate_register(NULL, -1, &vmstate_arm_timer, s);
return s;
}
/* ARM PrimeCell SP804 dual timer module.
* Docs at
* http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0271d/index.html
*/
typedef struct {
SysBusDevice busdev;
MemoryRegion iomem;
arm_timer_state *timer[2];
uint32_t freq0, freq1;
int level[2];
qemu_irq irq;
} sp804_state;
static const uint8_t sp804_ids[] = {
/* Timer ID */
0x04, 0x18, 0x14, 0,
/* PrimeCell ID */
0xd, 0xf0, 0x05, 0xb1
};
/* Merge the IRQs from the two component devices. */
static void sp804_set_irq(void *opaque, int irq, int level)
{
sp804_state *s = (sp804_state *)opaque;
s->level[irq] = level;
qemu_set_irq(s->irq, s->level[0] || s->level[1]);
}
static uint64_t sp804_read(void *opaque, hwaddr offset,
unsigned size)
{
sp804_state *s = (sp804_state *)opaque;
if (offset < 0x20) {
return arm_timer_read(s->timer[0], offset);
}
if (offset < 0x40) {
return arm_timer_read(s->timer[1], offset - 0x20);
}
/* TimerPeriphID */
if (offset >= 0xfe0 && offset <= 0xffc) {
return sp804_ids[(offset - 0xfe0) >> 2];
}
switch (offset) {
/* Integration Test control registers, which we won't support */
case 0xf00: /* TimerITCR */
case 0xf04: /* TimerITOP (strictly write only but..) */
qemu_log_mask(LOG_UNIMP,
"%s: integration test registers unimplemented\n",
__func__);
return 0;
}
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Bad offset %x\n", __func__, (int)offset);
return 0;
}
static void sp804_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
sp804_state *s = (sp804_state *)opaque;
if (offset < 0x20) {
arm_timer_write(s->timer[0], offset, value);
return;
}
if (offset < 0x40) {
arm_timer_write(s->timer[1], offset - 0x20, value);
return;
}
/* Technically we could be writing to the Test Registers, but not likely */
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %x\n",
__func__, (int)offset);
}
static const MemoryRegionOps sp804_ops = {
.read = sp804_read,
.write = sp804_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static const VMStateDescription vmstate_sp804 = {
.name = "sp804",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_INT32_ARRAY(level, sp804_state, 2),
VMSTATE_END_OF_LIST()
}
};
static int sp804_init(SysBusDevice *dev)
{
sp804_state *s = FROM_SYSBUS(sp804_state, dev);
qemu_irq *qi;
qi = qemu_allocate_irqs(sp804_set_irq, s, 2);
sysbus_init_irq(dev, &s->irq);
s->timer[0] = arm_timer_init(s->freq0);
s->timer[1] = arm_timer_init(s->freq1);
s->timer[0]->irq = qi[0];
s->timer[1]->irq = qi[1];
memory_region_init_io(&s->iomem, &sp804_ops, s, "sp804", 0x1000);
sysbus_init_mmio(dev, &s->iomem);
vmstate_register(&dev->qdev, -1, &vmstate_sp804, s);
return 0;
}
/* Integrator/CP timer module. */
typedef struct {
SysBusDevice busdev;
MemoryRegion iomem;
arm_timer_state *timer[3];
} icp_pit_state;
static uint64_t icp_pit_read(void *opaque, hwaddr offset,
unsigned size)
{
icp_pit_state *s = (icp_pit_state *)opaque;
int n;
/* ??? Don't know the PrimeCell ID for this device. */
n = offset >> 8;
if (n > 2) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad timer %d\n", __func__, n);
}
return arm_timer_read(s->timer[n], offset & 0xff);
}
static void icp_pit_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
icp_pit_state *s = (icp_pit_state *)opaque;
int n;
n = offset >> 8;
if (n > 2) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad timer %d\n", __func__, n);
}
arm_timer_write(s->timer[n], offset & 0xff, value);
}
static const MemoryRegionOps icp_pit_ops = {
.read = icp_pit_read,
.write = icp_pit_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static int icp_pit_init(SysBusDevice *dev)
{
icp_pit_state *s = FROM_SYSBUS(icp_pit_state, dev);
/* Timer 0 runs at the system clock speed (40MHz). */
s->timer[0] = arm_timer_init(40000000);
/* The other two timers run at 1MHz. */
s->timer[1] = arm_timer_init(1000000);
s->timer[2] = arm_timer_init(1000000);
sysbus_init_irq(dev, &s->timer[0]->irq);
sysbus_init_irq(dev, &s->timer[1]->irq);
sysbus_init_irq(dev, &s->timer[2]->irq);
memory_region_init_io(&s->iomem, &icp_pit_ops, s, "icp_pit", 0x1000);
sysbus_init_mmio(dev, &s->iomem);
/* This device has no state to save/restore. The component timers will
save themselves. */
return 0;
}
static void icp_pit_class_init(ObjectClass *klass, void *data)
{
SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
sdc->init = icp_pit_init;
}
static const TypeInfo icp_pit_info = {
.name = "integrator_pit",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(icp_pit_state),
.class_init = icp_pit_class_init,
};
static Property sp804_properties[] = {
DEFINE_PROP_UINT32("freq0", sp804_state, freq0, 1000000),
DEFINE_PROP_UINT32("freq1", sp804_state, freq1, 1000000),
DEFINE_PROP_END_OF_LIST(),
};
static void sp804_class_init(ObjectClass *klass, void *data)
{
SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
DeviceClass *k = DEVICE_CLASS(klass);
sdc->init = sp804_init;
k->props = sp804_properties;
}
static const TypeInfo sp804_info = {
.name = "sp804",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(sp804_state),
.class_init = sp804_class_init,
};
static void arm_timer_register_types(void)
{
type_register_static(&icp_pit_info);
type_register_static(&sp804_info);
}
type_init(arm_timer_register_types)
+489
View File
@@ -0,0 +1,489 @@
/*
* Xilinx Zynq cadence TTC model
*
* Copyright (c) 2011 Xilinx Inc.
* Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com)
* Copyright (c) 2012 PetaLogix Pty Ltd.
* Written By Haibing Ma
* M. Habib
*
* 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.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "hw/sysbus.h"
#include "qemu/timer.h"
#ifdef CADENCE_TTC_ERR_DEBUG
#define DB_PRINT(...) do { \
fprintf(stderr, ": %s: ", __func__); \
fprintf(stderr, ## __VA_ARGS__); \
} while (0);
#else
#define DB_PRINT(...)
#endif
#define COUNTER_INTR_IV 0x00000001
#define COUNTER_INTR_M1 0x00000002
#define COUNTER_INTR_M2 0x00000004
#define COUNTER_INTR_M3 0x00000008
#define COUNTER_INTR_OV 0x00000010
#define COUNTER_INTR_EV 0x00000020
#define COUNTER_CTRL_DIS 0x00000001
#define COUNTER_CTRL_INT 0x00000002
#define COUNTER_CTRL_DEC 0x00000004
#define COUNTER_CTRL_MATCH 0x00000008
#define COUNTER_CTRL_RST 0x00000010
#define CLOCK_CTRL_PS_EN 0x00000001
#define CLOCK_CTRL_PS_V 0x0000001e
typedef struct {
QEMUTimer *timer;
int freq;
uint32_t reg_clock;
uint32_t reg_count;
uint32_t reg_value;
uint16_t reg_interval;
uint16_t reg_match[3];
uint32_t reg_intr;
uint32_t reg_intr_en;
uint32_t reg_event_ctrl;
uint32_t reg_event;
uint64_t cpu_time;
unsigned int cpu_time_valid;
qemu_irq irq;
} CadenceTimerState;
typedef struct {
SysBusDevice busdev;
MemoryRegion iomem;
CadenceTimerState timer[3];
} CadenceTTCState;
static void cadence_timer_update(CadenceTimerState *s)
{
qemu_set_irq(s->irq, !!(s->reg_intr & s->reg_intr_en));
}
static CadenceTimerState *cadence_timer_from_addr(void *opaque,
hwaddr offset)
{
unsigned int index;
CadenceTTCState *s = (CadenceTTCState *)opaque;
index = (offset >> 2) % 3;
return &s->timer[index];
}
static uint64_t cadence_timer_get_ns(CadenceTimerState *s, uint64_t timer_steps)
{
/* timer_steps has max value of 0x100000000. double check it
* (or overflow can happen below) */
assert(timer_steps <= 1ULL << 32);
uint64_t r = timer_steps * 1000000000ULL;
if (s->reg_clock & CLOCK_CTRL_PS_EN) {
r >>= 16 - (((s->reg_clock & CLOCK_CTRL_PS_V) >> 1) + 1);
} else {
r >>= 16;
}
r /= (uint64_t)s->freq;
return r;
}
static uint64_t cadence_timer_get_steps(CadenceTimerState *s, uint64_t ns)
{
uint64_t to_divide = 1000000000ULL;
uint64_t r = ns;
/* for very large intervals (> 8s) do some division first to stop
* overflow (costs some prescision) */
while (r >= 8ULL << 30 && to_divide > 1) {
r /= 1000;
to_divide /= 1000;
}
r <<= 16;
/* keep early-dividing as needed */
while (r >= 8ULL << 30 && to_divide > 1) {
r /= 1000;
to_divide /= 1000;
}
r *= (uint64_t)s->freq;
if (s->reg_clock & CLOCK_CTRL_PS_EN) {
r /= 1 << (((s->reg_clock & CLOCK_CTRL_PS_V) >> 1) + 1);
}
r /= to_divide;
return r;
}
/* determine if x is in between a and b, exclusive of a, inclusive of b */
static inline int64_t is_between(int64_t x, int64_t a, int64_t b)
{
if (a < b) {
return x > a && x <= b;
}
return x < a && x >= b;
}
static void cadence_timer_run(CadenceTimerState *s)
{
int i;
int64_t event_interval, next_value;
assert(s->cpu_time_valid); /* cadence_timer_sync must be called first */
if (s->reg_count & COUNTER_CTRL_DIS) {
s->cpu_time_valid = 0;
return;
}
{ /* figure out what's going to happen next (rollover or match) */
int64_t interval = (uint64_t)((s->reg_count & COUNTER_CTRL_INT) ?
(int64_t)s->reg_interval + 1 : 0x10000ULL) << 16;
next_value = (s->reg_count & COUNTER_CTRL_DEC) ? -1ULL : interval;
for (i = 0; i < 3; ++i) {
int64_t cand = (uint64_t)s->reg_match[i] << 16;
if (is_between(cand, (uint64_t)s->reg_value, next_value)) {
next_value = cand;
}
}
}
DB_PRINT("next timer event value: %09llx\n",
(unsigned long long)next_value);
event_interval = next_value - (int64_t)s->reg_value;
event_interval = (event_interval < 0) ? -event_interval : event_interval;
qemu_mod_timer(s->timer, s->cpu_time +
cadence_timer_get_ns(s, event_interval));
}
static void cadence_timer_sync(CadenceTimerState *s)
{
int i;
int64_t r, x;
int64_t interval = ((s->reg_count & COUNTER_CTRL_INT) ?
(int64_t)s->reg_interval + 1 : 0x10000ULL) << 16;
uint64_t old_time = s->cpu_time;
s->cpu_time = qemu_get_clock_ns(vm_clock);
DB_PRINT("cpu time: %lld ns\n", (long long)old_time);
if (!s->cpu_time_valid || old_time == s->cpu_time) {
s->cpu_time_valid = 1;
return;
}
r = (int64_t)cadence_timer_get_steps(s, s->cpu_time - old_time);
x = (int64_t)s->reg_value + ((s->reg_count & COUNTER_CTRL_DEC) ? -r : r);
for (i = 0; i < 3; ++i) {
int64_t m = (int64_t)s->reg_match[i] << 16;
if (m > interval) {
continue;
}
/* check to see if match event has occurred. check m +/- interval
* to account for match events in wrap around cases */
if (is_between(m, s->reg_value, x) ||
is_between(m + interval, s->reg_value, x) ||
is_between(m - interval, s->reg_value, x)) {
s->reg_intr |= (2 << i);
}
}
while (x < 0) {
x += interval;
}
s->reg_value = (uint32_t)(x % interval);
if (s->reg_value != x) {
s->reg_intr |= (s->reg_count & COUNTER_CTRL_INT) ?
COUNTER_INTR_IV : COUNTER_INTR_OV;
}
cadence_timer_update(s);
}
static void cadence_timer_tick(void *opaque)
{
CadenceTimerState *s = opaque;
DB_PRINT("\n");
cadence_timer_sync(s);
cadence_timer_run(s);
}
static uint32_t cadence_ttc_read_imp(void *opaque, hwaddr offset)
{
CadenceTimerState *s = cadence_timer_from_addr(opaque, offset);
uint32_t value;
cadence_timer_sync(s);
cadence_timer_run(s);
switch (offset) {
case 0x00: /* clock control */
case 0x04:
case 0x08:
return s->reg_clock;
case 0x0c: /* counter control */
case 0x10:
case 0x14:
return s->reg_count;
case 0x18: /* counter value */
case 0x1c:
case 0x20:
return (uint16_t)(s->reg_value >> 16);
case 0x24: /* reg_interval counter */
case 0x28:
case 0x2c:
return s->reg_interval;
case 0x30: /* match 1 counter */
case 0x34:
case 0x38:
return s->reg_match[0];
case 0x3c: /* match 2 counter */
case 0x40:
case 0x44:
return s->reg_match[1];
case 0x48: /* match 3 counter */
case 0x4c:
case 0x50:
return s->reg_match[2];
case 0x54: /* interrupt register */
case 0x58:
case 0x5c:
/* cleared after read */
value = s->reg_intr;
s->reg_intr = 0;
cadence_timer_update(s);
return value;
case 0x60: /* interrupt enable */
case 0x64:
case 0x68:
return s->reg_intr_en;
case 0x6c:
case 0x70:
case 0x74:
return s->reg_event_ctrl;
case 0x78:
case 0x7c:
case 0x80:
return s->reg_event;
default:
return 0;
}
}
static uint64_t cadence_ttc_read(void *opaque, hwaddr offset,
unsigned size)
{
uint32_t ret = cadence_ttc_read_imp(opaque, offset);
DB_PRINT("addr: %08x data: %08x\n", (unsigned)offset, (unsigned)ret);
return ret;
}
static void cadence_ttc_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
CadenceTimerState *s = cadence_timer_from_addr(opaque, offset);
DB_PRINT("addr: %08x data %08x\n", (unsigned)offset, (unsigned)value);
cadence_timer_sync(s);
switch (offset) {
case 0x00: /* clock control */
case 0x04:
case 0x08:
s->reg_clock = value & 0x3F;
break;
case 0x0c: /* counter control */
case 0x10:
case 0x14:
if (value & COUNTER_CTRL_RST) {
s->reg_value = 0;
}
s->reg_count = value & 0x3f & ~COUNTER_CTRL_RST;
break;
case 0x24: /* interval register */
case 0x28:
case 0x2c:
s->reg_interval = value & 0xffff;
break;
case 0x30: /* match register */
case 0x34:
case 0x38:
s->reg_match[0] = value & 0xffff;
case 0x3c: /* match register */
case 0x40:
case 0x44:
s->reg_match[1] = value & 0xffff;
case 0x48: /* match register */
case 0x4c:
case 0x50:
s->reg_match[2] = value & 0xffff;
break;
case 0x54: /* interrupt register */
case 0x58:
case 0x5c:
break;
case 0x60: /* interrupt enable */
case 0x64:
case 0x68:
s->reg_intr_en = value & 0x3f;
break;
case 0x6c: /* event control */
case 0x70:
case 0x74:
s->reg_event_ctrl = value & 0x07;
break;
default:
return;
}
cadence_timer_run(s);
cadence_timer_update(s);
}
static const MemoryRegionOps cadence_ttc_ops = {
.read = cadence_ttc_read,
.write = cadence_ttc_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void cadence_timer_reset(CadenceTimerState *s)
{
s->reg_count = 0x21;
}
static void cadence_timer_init(uint32_t freq, CadenceTimerState *s)
{
memset(s, 0, sizeof(CadenceTimerState));
s->freq = freq;
cadence_timer_reset(s);
s->timer = qemu_new_timer_ns(vm_clock, cadence_timer_tick, s);
}
static int cadence_ttc_init(SysBusDevice *dev)
{
CadenceTTCState *s = FROM_SYSBUS(CadenceTTCState, dev);
int i;
for (i = 0; i < 3; ++i) {
cadence_timer_init(133000000, &s->timer[i]);
sysbus_init_irq(dev, &s->timer[i].irq);
}
memory_region_init_io(&s->iomem, &cadence_ttc_ops, s, "timer", 0x1000);
sysbus_init_mmio(dev, &s->iomem);
return 0;
}
static void cadence_timer_pre_save(void *opaque)
{
cadence_timer_sync((CadenceTimerState *)opaque);
}
static int cadence_timer_post_load(void *opaque, int version_id)
{
CadenceTimerState *s = opaque;
s->cpu_time_valid = 0;
cadence_timer_sync(s);
cadence_timer_run(s);
cadence_timer_update(s);
return 0;
}
static const VMStateDescription vmstate_cadence_timer = {
.name = "cadence_timer",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.pre_save = cadence_timer_pre_save,
.post_load = cadence_timer_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT32(reg_clock, CadenceTimerState),
VMSTATE_UINT32(reg_count, CadenceTimerState),
VMSTATE_UINT32(reg_value, CadenceTimerState),
VMSTATE_UINT16(reg_interval, CadenceTimerState),
VMSTATE_UINT16_ARRAY(reg_match, CadenceTimerState, 3),
VMSTATE_UINT32(reg_intr, CadenceTimerState),
VMSTATE_UINT32(reg_intr_en, CadenceTimerState),
VMSTATE_UINT32(reg_event_ctrl, CadenceTimerState),
VMSTATE_UINT32(reg_event, CadenceTimerState),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription vmstate_cadence_ttc = {
.name = "cadence_TTC",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_STRUCT_ARRAY(timer, CadenceTTCState, 3, 0,
vmstate_cadence_timer,
CadenceTimerState),
VMSTATE_END_OF_LIST()
}
};
static void cadence_ttc_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
sdc->init = cadence_ttc_init;
dc->vmsd = &vmstate_cadence_ttc;
}
static const TypeInfo cadence_ttc_info = {
.name = "cadence_ttc",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(CadenceTTCState),
.class_init = cadence_ttc_class_init,
};
static void cadence_ttc_register_types(void)
{
type_register_static(&cadence_ttc_info);
}
type_init(cadence_ttc_register_types)
+236
View File
@@ -0,0 +1,236 @@
/*
* MAXIM DS1338 I2C RTC+NVRAM
*
* Copyright (c) 2009 CodeSourcery.
* Written by Paul Brook
*
* This code is licensed under the GNU GPL v2.
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#include "hw/i2c/i2c.h"
/* Size of NVRAM including both the user-accessible area and the
* secondary register area.
*/
#define NVRAM_SIZE 64
/* Flags definitions */
#define SECONDS_CH 0x80
#define HOURS_12 0x40
#define HOURS_PM 0x20
#define CTRL_OSF 0x20
typedef struct {
I2CSlave i2c;
int64_t offset;
uint8_t wday_offset;
uint8_t nvram[NVRAM_SIZE];
int32_t ptr;
bool addr_byte;
} DS1338State;
static const VMStateDescription vmstate_ds1338 = {
.name = "ds1338",
.version_id = 2,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_I2C_SLAVE(i2c, DS1338State),
VMSTATE_INT64(offset, DS1338State),
VMSTATE_UINT8_V(wday_offset, DS1338State, 2),
VMSTATE_UINT8_ARRAY(nvram, DS1338State, NVRAM_SIZE),
VMSTATE_INT32(ptr, DS1338State),
VMSTATE_BOOL(addr_byte, DS1338State),
VMSTATE_END_OF_LIST()
}
};
static void capture_current_time(DS1338State *s)
{
/* Capture the current time into the secondary registers
* which will be actually read by the data transfer operation.
*/
struct tm now;
qemu_get_timedate(&now, s->offset);
s->nvram[0] = to_bcd(now.tm_sec);
s->nvram[1] = to_bcd(now.tm_min);
if (s->nvram[2] & HOURS_12) {
int tmp = now.tm_hour;
if (tmp % 12 == 0) {
tmp += 12;
}
if (tmp <= 12) {
s->nvram[2] = HOURS_12 | to_bcd(tmp);
} else {
s->nvram[2] = HOURS_12 | HOURS_PM | to_bcd(tmp - 12);
}
} else {
s->nvram[2] = to_bcd(now.tm_hour);
}
s->nvram[3] = (now.tm_wday + s->wday_offset) % 7 + 1;
s->nvram[4] = to_bcd(now.tm_mday);
s->nvram[5] = to_bcd(now.tm_mon + 1);
s->nvram[6] = to_bcd(now.tm_year - 100);
}
static void inc_regptr(DS1338State *s)
{
/* The register pointer wraps around after 0x3F; wraparound
* causes the current time/date to be retransferred into
* the secondary registers.
*/
s->ptr = (s->ptr + 1) & (NVRAM_SIZE - 1);
if (!s->ptr) {
capture_current_time(s);
}
}
static void ds1338_event(I2CSlave *i2c, enum i2c_event event)
{
DS1338State *s = FROM_I2C_SLAVE(DS1338State, i2c);
switch (event) {
case I2C_START_RECV:
/* In h/w, capture happens on any START condition, not just a
* START_RECV, but there is no need to actually capture on
* START_SEND, because the guest can't get at that data
* without going through a START_RECV which would overwrite it.
*/
capture_current_time(s);
break;
case I2C_START_SEND:
s->addr_byte = true;
break;
default:
break;
}
}
static int ds1338_recv(I2CSlave *i2c)
{
DS1338State *s = FROM_I2C_SLAVE(DS1338State, i2c);
uint8_t res;
res = s->nvram[s->ptr];
inc_regptr(s);
return res;
}
static int ds1338_send(I2CSlave *i2c, uint8_t data)
{
DS1338State *s = FROM_I2C_SLAVE(DS1338State, i2c);
if (s->addr_byte) {
s->ptr = data & (NVRAM_SIZE - 1);
s->addr_byte = false;
return 0;
}
if (s->ptr < 7) {
/* Time register. */
struct tm now;
qemu_get_timedate(&now, s->offset);
switch(s->ptr) {
case 0:
/* TODO: Implement CH (stop) bit. */
now.tm_sec = from_bcd(data & 0x7f);
break;
case 1:
now.tm_min = from_bcd(data & 0x7f);
break;
case 2:
if (data & HOURS_12) {
int tmp = from_bcd(data & (HOURS_PM - 1));
if (data & HOURS_PM) {
tmp += 12;
}
if (tmp % 12 == 0) {
tmp -= 12;
}
now.tm_hour = tmp;
} else {
now.tm_hour = from_bcd(data & (HOURS_12 - 1));
}
break;
case 3:
{
/* The day field is supposed to contain a value in
the range 1-7. Otherwise behavior is undefined.
*/
int user_wday = (data & 7) - 1;
s->wday_offset = (user_wday - now.tm_wday + 7) % 7;
}
break;
case 4:
now.tm_mday = from_bcd(data & 0x3f);
break;
case 5:
now.tm_mon = from_bcd(data & 0x1f) - 1;
break;
case 6:
now.tm_year = from_bcd(data) + 100;
break;
}
s->offset = qemu_timedate_diff(&now);
} else if (s->ptr == 7) {
/* Control register. */
/* Ensure bits 2, 3 and 6 will read back as zero. */
data &= 0xB3;
/* Attempting to write the OSF flag to logic 1 leaves the
value unchanged. */
data = (data & ~CTRL_OSF) | (data & s->nvram[s->ptr] & CTRL_OSF);
s->nvram[s->ptr] = data;
} else {
s->nvram[s->ptr] = data;
}
inc_regptr(s);
return 0;
}
static int ds1338_init(I2CSlave *i2c)
{
return 0;
}
static void ds1338_reset(DeviceState *dev)
{
DS1338State *s = FROM_I2C_SLAVE(DS1338State, I2C_SLAVE(dev));
/* The clock is running and synchronized with the host */
s->offset = 0;
s->wday_offset = 0;
memset(s->nvram, 0, NVRAM_SIZE);
s->ptr = 0;
s->addr_byte = false;
}
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;
dc->reset = ds1338_reset;
dc->vmsd = &vmstate_ds1338;
}
static const TypeInfo ds1338_info = {
.name = "ds1338",
.parent = TYPE_I2C_SLAVE,
.instance_size = sizeof(DS1338State),
.class_init = ds1338_class_init,
};
static void ds1338_register_types(void)
{
type_register_static(&ds1338_info);
}
type_init(ds1338_register_types)
+760
View File
@@ -0,0 +1,760 @@
/*
* High Precisition Event Timer emulation
*
* Copyright (c) 2007 Alexander Graf
* Copyright (c) 2008 IBM Corporation
*
* Authors: Beth Kon <bkon@us.ibm.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 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/>.
*
* *****************************************************************
*
* This driver attempts to emulate an HPET device in software.
*/
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "ui/console.h"
#include "qemu/timer.h"
#include "hw/timer/hpet.h"
#include "hw/sysbus.h"
#include "hw/timer/mc146818rtc.h"
#include "hw/timer/i8254.h"
//#define HPET_DEBUG
#ifdef HPET_DEBUG
#define DPRINTF printf
#else
#define DPRINTF(...)
#endif
#define HPET_MSI_SUPPORT 0
struct HPETState;
typedef struct HPETTimer { /* timers */
uint8_t tn; /*timer number*/
QEMUTimer *qemu_timer;
struct HPETState *state;
/* Memory-mapped, software visible timer registers */
uint64_t config; /* configuration/cap */
uint64_t cmp; /* comparator */
uint64_t fsb; /* FSB route */
/* Hidden register state */
uint64_t period; /* Last value written to comparator */
uint8_t wrap_flag; /* timer pop will indicate wrap for one-shot 32-bit
* mode. Next pop will be actual timer expiration.
*/
} HPETTimer;
typedef struct HPETState {
SysBusDevice busdev;
MemoryRegion iomem;
uint64_t hpet_offset;
qemu_irq irqs[HPET_NUM_IRQ_ROUTES];
uint32_t flags;
uint8_t rtc_irq_level;
qemu_irq pit_enabled;
uint8_t num_timers;
HPETTimer timer[HPET_MAX_TIMERS];
/* Memory-mapped, software visible registers */
uint64_t capability; /* capabilities */
uint64_t config; /* configuration */
uint64_t isr; /* interrupt status reg */
uint64_t hpet_counter; /* main counter */
uint8_t hpet_id; /* instance id */
} HPETState;
static uint32_t hpet_in_legacy_mode(HPETState *s)
{
return s->config & HPET_CFG_LEGACY;
}
static uint32_t timer_int_route(struct HPETTimer *timer)
{
return (timer->config & HPET_TN_INT_ROUTE_MASK) >> HPET_TN_INT_ROUTE_SHIFT;
}
static uint32_t timer_fsb_route(HPETTimer *t)
{
return t->config & HPET_TN_FSB_ENABLE;
}
static uint32_t hpet_enabled(HPETState *s)
{
return s->config & HPET_CFG_ENABLE;
}
static uint32_t timer_is_periodic(HPETTimer *t)
{
return t->config & HPET_TN_PERIODIC;
}
static uint32_t timer_enabled(HPETTimer *t)
{
return t->config & HPET_TN_ENABLE;
}
static uint32_t hpet_time_after(uint64_t a, uint64_t b)
{
return ((int32_t)(b) - (int32_t)(a) < 0);
}
static uint32_t hpet_time_after64(uint64_t a, uint64_t b)
{
return ((int64_t)(b) - (int64_t)(a) < 0);
}
static uint64_t ticks_to_ns(uint64_t value)
{
return (muldiv64(value, HPET_CLK_PERIOD, FS_PER_NS));
}
static uint64_t ns_to_ticks(uint64_t value)
{
return (muldiv64(value, FS_PER_NS, HPET_CLK_PERIOD));
}
static uint64_t hpet_fixup_reg(uint64_t new, uint64_t old, uint64_t mask)
{
new &= mask;
new |= old & ~mask;
return new;
}
static int activating_bit(uint64_t old, uint64_t new, uint64_t mask)
{
return (!(old & mask) && (new & mask));
}
static int deactivating_bit(uint64_t old, uint64_t new, uint64_t mask)
{
return ((old & mask) && !(new & mask));
}
static uint64_t hpet_get_ticks(HPETState *s)
{
return ns_to_ticks(qemu_get_clock_ns(vm_clock) + s->hpet_offset);
}
/*
* calculate diff between comparator value and current ticks
*/
static inline uint64_t hpet_calculate_diff(HPETTimer *t, uint64_t current)
{
if (t->config & HPET_TN_32BIT) {
uint32_t diff, cmp;
cmp = (uint32_t)t->cmp;
diff = cmp - (uint32_t)current;
diff = (int32_t)diff > 0 ? diff : (uint32_t)1;
return (uint64_t)diff;
} else {
uint64_t diff, cmp;
cmp = t->cmp;
diff = cmp - current;
diff = (int64_t)diff > 0 ? diff : (uint64_t)1;
return diff;
}
}
static void update_irq(struct HPETTimer *timer, int set)
{
uint64_t mask;
HPETState *s;
int route;
if (timer->tn <= 1 && hpet_in_legacy_mode(timer->state)) {
/* if LegacyReplacementRoute bit is set, HPET specification requires
* timer0 be routed to IRQ0 in NON-APIC or IRQ2 in the I/O APIC,
* timer1 be routed to IRQ8 in NON-APIC or IRQ8 in the I/O APIC.
*/
route = (timer->tn == 0) ? 0 : RTC_ISA_IRQ;
} else {
route = timer_int_route(timer);
}
s = timer->state;
mask = 1 << timer->tn;
if (!set || !timer_enabled(timer) || !hpet_enabled(timer->state)) {
s->isr &= ~mask;
if (!timer_fsb_route(timer)) {
qemu_irq_lower(s->irqs[route]);
}
} else if (timer_fsb_route(timer)) {
stl_le_phys(timer->fsb >> 32, timer->fsb & 0xffffffff);
} else if (timer->config & HPET_TN_TYPE_LEVEL) {
s->isr |= mask;
qemu_irq_raise(s->irqs[route]);
} else {
s->isr &= ~mask;
qemu_irq_pulse(s->irqs[route]);
}
}
static void hpet_pre_save(void *opaque)
{
HPETState *s = opaque;
/* save current counter value */
s->hpet_counter = hpet_get_ticks(s);
}
static int hpet_pre_load(void *opaque)
{
HPETState *s = opaque;
/* version 1 only supports 3, later versions will load the actual value */
s->num_timers = HPET_MIN_TIMERS;
return 0;
}
static int hpet_post_load(void *opaque, int version_id)
{
HPETState *s = opaque;
/* Recalculate the offset between the main counter and guest time */
s->hpet_offset = ticks_to_ns(s->hpet_counter) - qemu_get_clock_ns(vm_clock);
/* Push number of timers into capability returned via HPET_ID */
s->capability &= ~HPET_ID_NUM_TIM_MASK;
s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT;
hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability;
/* Derive HPET_MSI_SUPPORT from the capability of the first timer. */
s->flags &= ~(1 << HPET_MSI_SUPPORT);
if (s->timer[0].config & HPET_TN_FSB_CAP) {
s->flags |= 1 << HPET_MSI_SUPPORT;
}
return 0;
}
static bool hpet_rtc_irq_level_needed(void *opaque)
{
HPETState *s = opaque;
return s->rtc_irq_level != 0;
}
static const VMStateDescription vmstate_hpet_rtc_irq_level = {
.name = "hpet/rtc_irq_level",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT8(rtc_irq_level, HPETState),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription vmstate_hpet_timer = {
.name = "hpet_timer",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField []) {
VMSTATE_UINT8(tn, HPETTimer),
VMSTATE_UINT64(config, HPETTimer),
VMSTATE_UINT64(cmp, HPETTimer),
VMSTATE_UINT64(fsb, HPETTimer),
VMSTATE_UINT64(period, HPETTimer),
VMSTATE_UINT8(wrap_flag, HPETTimer),
VMSTATE_TIMER(qemu_timer, HPETTimer),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription vmstate_hpet = {
.name = "hpet",
.version_id = 2,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.pre_save = hpet_pre_save,
.pre_load = hpet_pre_load,
.post_load = hpet_post_load,
.fields = (VMStateField []) {
VMSTATE_UINT64(config, HPETState),
VMSTATE_UINT64(isr, HPETState),
VMSTATE_UINT64(hpet_counter, HPETState),
VMSTATE_UINT8_V(num_timers, HPETState, 2),
VMSTATE_STRUCT_VARRAY_UINT8(timer, HPETState, num_timers, 0,
vmstate_hpet_timer, HPETTimer),
VMSTATE_END_OF_LIST()
},
.subsections = (VMStateSubsection[]) {
{
.vmsd = &vmstate_hpet_rtc_irq_level,
.needed = hpet_rtc_irq_level_needed,
}, {
/* empty */
}
}
};
/*
* timer expiration callback
*/
static void hpet_timer(void *opaque)
{
HPETTimer *t = opaque;
uint64_t diff;
uint64_t period = t->period;
uint64_t cur_tick = hpet_get_ticks(t->state);
if (timer_is_periodic(t) && period != 0) {
if (t->config & HPET_TN_32BIT) {
while (hpet_time_after(cur_tick, t->cmp)) {
t->cmp = (uint32_t)(t->cmp + t->period);
}
} else {
while (hpet_time_after64(cur_tick, t->cmp)) {
t->cmp += period;
}
}
diff = hpet_calculate_diff(t, cur_tick);
qemu_mod_timer(t->qemu_timer,
qemu_get_clock_ns(vm_clock) + (int64_t)ticks_to_ns(diff));
} else if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) {
if (t->wrap_flag) {
diff = hpet_calculate_diff(t, cur_tick);
qemu_mod_timer(t->qemu_timer, qemu_get_clock_ns(vm_clock) +
(int64_t)ticks_to_ns(diff));
t->wrap_flag = 0;
}
}
update_irq(t, 1);
}
static void hpet_set_timer(HPETTimer *t)
{
uint64_t diff;
uint32_t wrap_diff; /* how many ticks until we wrap? */
uint64_t cur_tick = hpet_get_ticks(t->state);
/* whenever new timer is being set up, make sure wrap_flag is 0 */
t->wrap_flag = 0;
diff = hpet_calculate_diff(t, cur_tick);
/* hpet spec says in one-shot 32-bit mode, generate an interrupt when
* counter wraps in addition to an interrupt with comparator match.
*/
if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) {
wrap_diff = 0xffffffff - (uint32_t)cur_tick;
if (wrap_diff < (uint32_t)diff) {
diff = wrap_diff;
t->wrap_flag = 1;
}
}
qemu_mod_timer(t->qemu_timer,
qemu_get_clock_ns(vm_clock) + (int64_t)ticks_to_ns(diff));
}
static void hpet_del_timer(HPETTimer *t)
{
qemu_del_timer(t->qemu_timer);
update_irq(t, 0);
}
#ifdef HPET_DEBUG
static uint32_t hpet_ram_readb(void *opaque, hwaddr addr)
{
printf("qemu: hpet_read b at %" PRIx64 "\n", addr);
return 0;
}
static uint32_t hpet_ram_readw(void *opaque, hwaddr addr)
{
printf("qemu: hpet_read w at %" PRIx64 "\n", addr);
return 0;
}
#endif
static uint64_t hpet_ram_read(void *opaque, hwaddr addr,
unsigned size)
{
HPETState *s = opaque;
uint64_t cur_tick, index;
DPRINTF("qemu: Enter hpet_ram_readl at %" PRIx64 "\n", addr);
index = addr;
/*address range of all TN regs*/
if (index >= 0x100 && index <= 0x3ff) {
uint8_t timer_id = (addr - 0x100) / 0x20;
HPETTimer *timer = &s->timer[timer_id];
if (timer_id > s->num_timers) {
DPRINTF("qemu: timer id out of range\n");
return 0;
}
switch ((addr - 0x100) % 0x20) {
case HPET_TN_CFG:
return timer->config;
case HPET_TN_CFG + 4: // Interrupt capabilities
return timer->config >> 32;
case HPET_TN_CMP: // comparator register
return timer->cmp;
case HPET_TN_CMP + 4:
return timer->cmp >> 32;
case HPET_TN_ROUTE:
return timer->fsb;
case HPET_TN_ROUTE + 4:
return timer->fsb >> 32;
default:
DPRINTF("qemu: invalid hpet_ram_readl\n");
break;
}
} else {
switch (index) {
case HPET_ID:
return s->capability;
case HPET_PERIOD:
return s->capability >> 32;
case HPET_CFG:
return s->config;
case HPET_CFG + 4:
DPRINTF("qemu: invalid HPET_CFG + 4 hpet_ram_readl\n");
return 0;
case HPET_COUNTER:
if (hpet_enabled(s)) {
cur_tick = hpet_get_ticks(s);
} else {
cur_tick = s->hpet_counter;
}
DPRINTF("qemu: reading counter = %" PRIx64 "\n", cur_tick);
return cur_tick;
case HPET_COUNTER + 4:
if (hpet_enabled(s)) {
cur_tick = hpet_get_ticks(s);
} else {
cur_tick = s->hpet_counter;
}
DPRINTF("qemu: reading counter + 4 = %" PRIx64 "\n", cur_tick);
return cur_tick >> 32;
case HPET_STATUS:
return s->isr;
default:
DPRINTF("qemu: invalid hpet_ram_readl\n");
break;
}
}
return 0;
}
static void hpet_ram_write(void *opaque, hwaddr addr,
uint64_t value, unsigned size)
{
int i;
HPETState *s = opaque;
uint64_t old_val, new_val, val, index;
DPRINTF("qemu: Enter hpet_ram_writel at %" PRIx64 " = %#x\n", addr, value);
index = addr;
old_val = hpet_ram_read(opaque, addr, 4);
new_val = value;
/*address range of all TN regs*/
if (index >= 0x100 && index <= 0x3ff) {
uint8_t timer_id = (addr - 0x100) / 0x20;
HPETTimer *timer = &s->timer[timer_id];
DPRINTF("qemu: hpet_ram_writel timer_id = %#x\n", timer_id);
if (timer_id > s->num_timers) {
DPRINTF("qemu: timer id out of range\n");
return;
}
switch ((addr - 0x100) % 0x20) {
case HPET_TN_CFG:
DPRINTF("qemu: hpet_ram_writel HPET_TN_CFG\n");
if (activating_bit(old_val, new_val, HPET_TN_FSB_ENABLE)) {
update_irq(timer, 0);
}
val = hpet_fixup_reg(new_val, old_val, HPET_TN_CFG_WRITE_MASK);
timer->config = (timer->config & 0xffffffff00000000ULL) | val;
if (new_val & HPET_TN_32BIT) {
timer->cmp = (uint32_t)timer->cmp;
timer->period = (uint32_t)timer->period;
}
if (activating_bit(old_val, new_val, HPET_TN_ENABLE)) {
hpet_set_timer(timer);
} else if (deactivating_bit(old_val, new_val, HPET_TN_ENABLE)) {
hpet_del_timer(timer);
}
break;
case HPET_TN_CFG + 4: // Interrupt capabilities
DPRINTF("qemu: invalid HPET_TN_CFG+4 write\n");
break;
case HPET_TN_CMP: // comparator register
DPRINTF("qemu: hpet_ram_writel HPET_TN_CMP\n");
if (timer->config & HPET_TN_32BIT) {
new_val = (uint32_t)new_val;
}
if (!timer_is_periodic(timer)
|| (timer->config & HPET_TN_SETVAL)) {
timer->cmp = (timer->cmp & 0xffffffff00000000ULL) | new_val;
}
if (timer_is_periodic(timer)) {
/*
* FIXME: Clamp period to reasonable min value?
* Clamp period to reasonable max value
*/
new_val &= (timer->config & HPET_TN_32BIT ? ~0u : ~0ull) >> 1;
timer->period =
(timer->period & 0xffffffff00000000ULL) | new_val;
}
timer->config &= ~HPET_TN_SETVAL;
if (hpet_enabled(s)) {
hpet_set_timer(timer);
}
break;
case HPET_TN_CMP + 4: // comparator register high order
DPRINTF("qemu: hpet_ram_writel HPET_TN_CMP + 4\n");
if (!timer_is_periodic(timer)
|| (timer->config & HPET_TN_SETVAL)) {
timer->cmp = (timer->cmp & 0xffffffffULL) | new_val << 32;
} else {
/*
* FIXME: Clamp period to reasonable min value?
* Clamp period to reasonable max value
*/
new_val &= (timer->config & HPET_TN_32BIT ? ~0u : ~0ull) >> 1;
timer->period =
(timer->period & 0xffffffffULL) | new_val << 32;
}
timer->config &= ~HPET_TN_SETVAL;
if (hpet_enabled(s)) {
hpet_set_timer(timer);
}
break;
case HPET_TN_ROUTE:
timer->fsb = (timer->fsb & 0xffffffff00000000ULL) | new_val;
break;
case HPET_TN_ROUTE + 4:
timer->fsb = (new_val << 32) | (timer->fsb & 0xffffffff);
break;
default:
DPRINTF("qemu: invalid hpet_ram_writel\n");
break;
}
return;
} else {
switch (index) {
case HPET_ID:
return;
case HPET_CFG:
val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK);
s->config = (s->config & 0xffffffff00000000ULL) | val;
if (activating_bit(old_val, new_val, HPET_CFG_ENABLE)) {
/* Enable main counter and interrupt generation. */
s->hpet_offset =
ticks_to_ns(s->hpet_counter) - qemu_get_clock_ns(vm_clock);
for (i = 0; i < s->num_timers; i++) {
if ((&s->timer[i])->cmp != ~0ULL) {
hpet_set_timer(&s->timer[i]);
}
}
} else if (deactivating_bit(old_val, new_val, HPET_CFG_ENABLE)) {
/* Halt main counter and disable interrupt generation. */
s->hpet_counter = hpet_get_ticks(s);
for (i = 0; i < s->num_timers; i++) {
hpet_del_timer(&s->timer[i]);
}
}
/* i8254 and RTC output pins are disabled
* when HPET is in legacy mode */
if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) {
qemu_set_irq(s->pit_enabled, 0);
qemu_irq_lower(s->irqs[0]);
qemu_irq_lower(s->irqs[RTC_ISA_IRQ]);
} else if (deactivating_bit(old_val, new_val, HPET_CFG_LEGACY)) {
qemu_irq_lower(s->irqs[0]);
qemu_set_irq(s->pit_enabled, 1);
qemu_set_irq(s->irqs[RTC_ISA_IRQ], s->rtc_irq_level);
}
break;
case HPET_CFG + 4:
DPRINTF("qemu: invalid HPET_CFG+4 write\n");
break;
case HPET_STATUS:
val = new_val & s->isr;
for (i = 0; i < s->num_timers; i++) {
if (val & (1 << i)) {
update_irq(&s->timer[i], 0);
}
}
break;
case HPET_COUNTER:
if (hpet_enabled(s)) {
DPRINTF("qemu: Writing counter while HPET enabled!\n");
}
s->hpet_counter =
(s->hpet_counter & 0xffffffff00000000ULL) | value;
DPRINTF("qemu: HPET counter written. ctr = %#x -> %" PRIx64 "\n",
value, s->hpet_counter);
break;
case HPET_COUNTER + 4:
if (hpet_enabled(s)) {
DPRINTF("qemu: Writing counter while HPET enabled!\n");
}
s->hpet_counter =
(s->hpet_counter & 0xffffffffULL) | (((uint64_t)value) << 32);
DPRINTF("qemu: HPET counter + 4 written. ctr = %#x -> %" PRIx64 "\n",
value, s->hpet_counter);
break;
default:
DPRINTF("qemu: invalid hpet_ram_writel\n");
break;
}
}
}
static const MemoryRegionOps hpet_ram_ops = {
.read = hpet_ram_read,
.write = hpet_ram_write,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
},
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void hpet_reset(DeviceState *d)
{
HPETState *s = FROM_SYSBUS(HPETState, SYS_BUS_DEVICE(d));
int i;
for (i = 0; i < s->num_timers; i++) {
HPETTimer *timer = &s->timer[i];
hpet_del_timer(timer);
timer->cmp = ~0ULL;
timer->config = HPET_TN_PERIODIC_CAP | HPET_TN_SIZE_CAP;
if (s->flags & (1 << HPET_MSI_SUPPORT)) {
timer->config |= HPET_TN_FSB_CAP;
}
/* advertise availability of ioapic inti2 */
timer->config |= 0x00000004ULL << 32;
timer->period = 0ULL;
timer->wrap_flag = 0;
}
qemu_set_irq(s->pit_enabled, 1);
s->hpet_counter = 0ULL;
s->hpet_offset = 0ULL;
s->config = 0ULL;
hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability;
hpet_cfg.hpet[s->hpet_id].address = SYS_BUS_DEVICE(d)->mmio[0].addr;
/* to document that the RTC lowers its output on reset as well */
s->rtc_irq_level = 0;
}
static void hpet_handle_legacy_irq(void *opaque, int n, int level)
{
HPETState *s = FROM_SYSBUS(HPETState, opaque);
if (n == HPET_LEGACY_PIT_INT) {
if (!hpet_in_legacy_mode(s)) {
qemu_set_irq(s->irqs[0], level);
}
} else {
s->rtc_irq_level = level;
if (!hpet_in_legacy_mode(s)) {
qemu_set_irq(s->irqs[RTC_ISA_IRQ], level);
}
}
}
static int hpet_init(SysBusDevice *dev)
{
HPETState *s = FROM_SYSBUS(HPETState, dev);
int i;
HPETTimer *timer;
if (hpet_cfg.count == UINT8_MAX) {
/* first instance */
hpet_cfg.count = 0;
}
if (hpet_cfg.count == 8) {
fprintf(stderr, "Only 8 instances of HPET is allowed\n");
return -1;
}
s->hpet_id = hpet_cfg.count++;
for (i = 0; i < HPET_NUM_IRQ_ROUTES; i++) {
sysbus_init_irq(dev, &s->irqs[i]);
}
if (s->num_timers < HPET_MIN_TIMERS) {
s->num_timers = HPET_MIN_TIMERS;
} else if (s->num_timers > HPET_MAX_TIMERS) {
s->num_timers = HPET_MAX_TIMERS;
}
for (i = 0; i < HPET_MAX_TIMERS; i++) {
timer = &s->timer[i];
timer->qemu_timer = qemu_new_timer_ns(vm_clock, hpet_timer, timer);
timer->tn = i;
timer->state = s;
}
/* 64-bit main counter; LegacyReplacementRoute. */
s->capability = 0x8086a001ULL;
s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT;
s->capability |= ((HPET_CLK_PERIOD) << 32);
qdev_init_gpio_in(&dev->qdev, hpet_handle_legacy_irq, 2);
qdev_init_gpio_out(&dev->qdev, &s->pit_enabled, 1);
/* HPET Area */
memory_region_init_io(&s->iomem, &hpet_ram_ops, s, "hpet", 0x400);
sysbus_init_mmio(dev, &s->iomem);
return 0;
}
static Property hpet_device_properties[] = {
DEFINE_PROP_UINT8("timers", HPETState, num_timers, HPET_MIN_TIMERS),
DEFINE_PROP_BIT("msi", HPETState, flags, HPET_MSI_SUPPORT, false),
DEFINE_PROP_END_OF_LIST(),
};
static void hpet_device_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = hpet_init;
dc->no_user = 1;
dc->reset = hpet_reset;
dc->vmsd = &vmstate_hpet;
dc->props = hpet_device_properties;
}
static const TypeInfo hpet_device_info = {
.name = "hpet",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(HPETState),
.class_init = hpet_device_class_init,
};
static void hpet_register_types(void)
{
type_register_static(&hpet_device_info);
}
type_init(hpet_register_types)
+362
View File
@@ -0,0 +1,362 @@
/*
* QEMU 8253/8254 interval timer emulation
*
* Copyright (c) 2003-2004 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/isa/isa.h"
#include "qemu/timer.h"
#include "hw/timer/i8254.h"
#include "hw/timer/i8254_internal.h"
//#define DEBUG_PIT
#define RW_STATE_LSB 1
#define RW_STATE_MSB 2
#define RW_STATE_WORD0 3
#define RW_STATE_WORD1 4
static void pit_irq_timer_update(PITChannelState *s, int64_t current_time);
static int pit_get_count(PITChannelState *s)
{
uint64_t d;
int counter;
d = muldiv64(qemu_get_clock_ns(vm_clock) - s->count_load_time, PIT_FREQ,
get_ticks_per_sec());
switch(s->mode) {
case 0:
case 1:
case 4:
case 5:
counter = (s->count - d) & 0xffff;
break;
case 3:
/* XXX: may be incorrect for odd counts */
counter = s->count - ((2 * d) % s->count);
break;
default:
counter = s->count - (d % s->count);
break;
}
return counter;
}
/* val must be 0 or 1 */
static void pit_set_channel_gate(PITCommonState *s, PITChannelState *sc,
int val)
{
switch (sc->mode) {
default:
case 0:
case 4:
/* XXX: just disable/enable counting */
break;
case 1:
case 5:
if (sc->gate < val) {
/* restart counting on rising edge */
sc->count_load_time = qemu_get_clock_ns(vm_clock);
pit_irq_timer_update(sc, sc->count_load_time);
}
break;
case 2:
case 3:
if (sc->gate < val) {
/* restart counting on rising edge */
sc->count_load_time = qemu_get_clock_ns(vm_clock);
pit_irq_timer_update(sc, sc->count_load_time);
}
/* XXX: disable/enable counting */
break;
}
sc->gate = val;
}
static inline void pit_load_count(PITChannelState *s, int val)
{
if (val == 0)
val = 0x10000;
s->count_load_time = qemu_get_clock_ns(vm_clock);
s->count = val;
pit_irq_timer_update(s, s->count_load_time);
}
/* if already latched, do not latch again */
static void pit_latch_count(PITChannelState *s)
{
if (!s->count_latched) {
s->latched_count = pit_get_count(s);
s->count_latched = s->rw_mode;
}
}
static void pit_ioport_write(void *opaque, hwaddr addr,
uint64_t val, unsigned size)
{
PITCommonState *pit = opaque;
int channel, access;
PITChannelState *s;
addr &= 3;
if (addr == 3) {
channel = val >> 6;
if (channel == 3) {
/* read back command */
for(channel = 0; channel < 3; channel++) {
s = &pit->channels[channel];
if (val & (2 << channel)) {
if (!(val & 0x20)) {
pit_latch_count(s);
}
if (!(val & 0x10) && !s->status_latched) {
/* status latch */
/* XXX: add BCD and null count */
s->status =
(pit_get_out(s,
qemu_get_clock_ns(vm_clock)) << 7) |
(s->rw_mode << 4) |
(s->mode << 1) |
s->bcd;
s->status_latched = 1;
}
}
}
} else {
s = &pit->channels[channel];
access = (val >> 4) & 3;
if (access == 0) {
pit_latch_count(s);
} else {
s->rw_mode = access;
s->read_state = access;
s->write_state = access;
s->mode = (val >> 1) & 7;
s->bcd = val & 1;
/* XXX: update irq timer ? */
}
}
} else {
s = &pit->channels[addr];
switch(s->write_state) {
default:
case RW_STATE_LSB:
pit_load_count(s, val);
break;
case RW_STATE_MSB:
pit_load_count(s, val << 8);
break;
case RW_STATE_WORD0:
s->write_latch = val;
s->write_state = RW_STATE_WORD1;
break;
case RW_STATE_WORD1:
pit_load_count(s, s->write_latch | (val << 8));
s->write_state = RW_STATE_WORD0;
break;
}
}
}
static uint64_t pit_ioport_read(void *opaque, hwaddr addr,
unsigned size)
{
PITCommonState *pit = opaque;
int ret, count;
PITChannelState *s;
addr &= 3;
s = &pit->channels[addr];
if (s->status_latched) {
s->status_latched = 0;
ret = s->status;
} else if (s->count_latched) {
switch(s->count_latched) {
default:
case RW_STATE_LSB:
ret = s->latched_count & 0xff;
s->count_latched = 0;
break;
case RW_STATE_MSB:
ret = s->latched_count >> 8;
s->count_latched = 0;
break;
case RW_STATE_WORD0:
ret = s->latched_count & 0xff;
s->count_latched = RW_STATE_MSB;
break;
}
} else {
switch(s->read_state) {
default:
case RW_STATE_LSB:
count = pit_get_count(s);
ret = count & 0xff;
break;
case RW_STATE_MSB:
count = pit_get_count(s);
ret = (count >> 8) & 0xff;
break;
case RW_STATE_WORD0:
count = pit_get_count(s);
ret = count & 0xff;
s->read_state = RW_STATE_WORD1;
break;
case RW_STATE_WORD1:
count = pit_get_count(s);
ret = (count >> 8) & 0xff;
s->read_state = RW_STATE_WORD0;
break;
}
}
return ret;
}
static void pit_irq_timer_update(PITChannelState *s, int64_t current_time)
{
int64_t expire_time;
int irq_level;
if (!s->irq_timer || s->irq_disabled) {
return;
}
expire_time = pit_get_next_transition_time(s, current_time);
irq_level = pit_get_out(s, current_time);
qemu_set_irq(s->irq, irq_level);
#ifdef DEBUG_PIT
printf("irq_level=%d next_delay=%f\n",
irq_level,
(double)(expire_time - current_time) / get_ticks_per_sec());
#endif
s->next_transition_time = expire_time;
if (expire_time != -1)
qemu_mod_timer(s->irq_timer, expire_time);
else
qemu_del_timer(s->irq_timer);
}
static void pit_irq_timer(void *opaque)
{
PITChannelState *s = opaque;
pit_irq_timer_update(s, s->next_transition_time);
}
static void pit_reset(DeviceState *dev)
{
PITCommonState *pit = DO_UPCAST(PITCommonState, dev.qdev, dev);
PITChannelState *s;
pit_reset_common(pit);
s = &pit->channels[0];
if (!s->irq_disabled) {
qemu_mod_timer(s->irq_timer, s->next_transition_time);
}
}
/* When HPET is operating in legacy mode, suppress the ignored timer IRQ,
* reenable it when legacy mode is left again. */
static void pit_irq_control(void *opaque, int n, int enable)
{
PITCommonState *pit = opaque;
PITChannelState *s = &pit->channels[0];
if (enable) {
s->irq_disabled = 0;
pit_irq_timer_update(s, qemu_get_clock_ns(vm_clock));
} else {
s->irq_disabled = 1;
qemu_del_timer(s->irq_timer);
}
}
static const MemoryRegionOps pit_ioport_ops = {
.read = pit_ioport_read,
.write = pit_ioport_write,
.impl = {
.min_access_size = 1,
.max_access_size = 1,
},
.endianness = DEVICE_LITTLE_ENDIAN,
};
static void pit_post_load(PITCommonState *s)
{
PITChannelState *sc = &s->channels[0];
if (sc->next_transition_time != -1) {
qemu_mod_timer(sc->irq_timer, sc->next_transition_time);
} else {
qemu_del_timer(sc->irq_timer);
}
}
static int pit_initfn(PITCommonState *pit)
{
PITChannelState *s;
s = &pit->channels[0];
/* the timer 0 is connected to an IRQ */
s->irq_timer = qemu_new_timer_ns(vm_clock, pit_irq_timer, s);
qdev_init_gpio_out(&pit->dev.qdev, &s->irq, 1);
memory_region_init_io(&pit->ioports, &pit_ioport_ops, pit, "pit", 4);
qdev_init_gpio_in(&pit->dev.qdev, pit_irq_control, 1);
return 0;
}
static Property pit_properties[] = {
DEFINE_PROP_HEX32("iobase", PITCommonState, iobase, -1),
DEFINE_PROP_END_OF_LIST(),
};
static void pit_class_initfn(ObjectClass *klass, void *data)
{
PITCommonClass *k = PIT_COMMON_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
k->init = pit_initfn;
k->set_channel_gate = pit_set_channel_gate;
k->get_channel_info = pit_get_channel_info_common;
k->post_load = pit_post_load;
dc->reset = pit_reset;
dc->props = pit_properties;
}
static const TypeInfo pit_info = {
.name = "isa-pit",
.parent = TYPE_PIT_COMMON,
.instance_size = sizeof(PITCommonState),
.class_init = pit_class_initfn,
};
static void pit_register_types(void)
{
type_register_static(&pit_info);
}
type_init(pit_register_types)
+311
View File
@@ -0,0 +1,311 @@
/*
* QEMU 8253/8254 - common bits of emulated and KVM kernel model
*
* Copyright (c) 2003-2004 Fabrice Bellard
* Copyright (c) 2012 Jan Kiszka, Siemens AG
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/isa/isa.h"
#include "qemu/timer.h"
#include "hw/timer/i8254.h"
#include "hw/timer/i8254_internal.h"
/* val must be 0 or 1 */
void pit_set_gate(ISADevice *dev, int channel, int val)
{
PITCommonState *pit = PIT_COMMON(dev);
PITChannelState *s = &pit->channels[channel];
PITCommonClass *c = PIT_COMMON_GET_CLASS(pit);
c->set_channel_gate(pit, s, val);
}
/* get pit output bit */
int pit_get_out(PITChannelState *s, int64_t current_time)
{
uint64_t d;
int out;
d = muldiv64(current_time - s->count_load_time, PIT_FREQ,
get_ticks_per_sec());
switch (s->mode) {
default:
case 0:
out = (d >= s->count);
break;
case 1:
out = (d < s->count);
break;
case 2:
if ((d % s->count) == 0 && d != 0) {
out = 1;
} else {
out = 0;
}
break;
case 3:
out = (d % s->count) < ((s->count + 1) >> 1);
break;
case 4:
case 5:
out = (d == s->count);
break;
}
return out;
}
/* return -1 if no transition will occur. */
int64_t pit_get_next_transition_time(PITChannelState *s, int64_t current_time)
{
uint64_t d, next_time, base;
int period2;
d = muldiv64(current_time - s->count_load_time, PIT_FREQ,
get_ticks_per_sec());
switch (s->mode) {
default:
case 0:
case 1:
if (d < s->count) {
next_time = s->count;
} else {
return -1;
}
break;
case 2:
base = (d / s->count) * s->count;
if ((d - base) == 0 && d != 0) {
next_time = base + s->count;
} else {
next_time = base + s->count + 1;
}
break;
case 3:
base = (d / s->count) * s->count;
period2 = ((s->count + 1) >> 1);
if ((d - base) < period2) {
next_time = base + period2;
} else {
next_time = base + s->count;
}
break;
case 4:
case 5:
if (d < s->count) {
next_time = s->count;
} else if (d == s->count) {
next_time = s->count + 1;
} else {
return -1;
}
break;
}
/* convert to timer units */
next_time = s->count_load_time + muldiv64(next_time, get_ticks_per_sec(),
PIT_FREQ);
/* fix potential rounding problems */
/* XXX: better solution: use a clock at PIT_FREQ Hz */
if (next_time <= current_time) {
next_time = current_time + 1;
}
return next_time;
}
void pit_get_channel_info_common(PITCommonState *s, PITChannelState *sc,
PITChannelInfo *info)
{
info->gate = sc->gate;
info->mode = sc->mode;
info->initial_count = sc->count;
info->out = pit_get_out(sc, qemu_get_clock_ns(vm_clock));
}
void pit_get_channel_info(ISADevice *dev, int channel, PITChannelInfo *info)
{
PITCommonState *pit = PIT_COMMON(dev);
PITChannelState *s = &pit->channels[channel];
PITCommonClass *c = PIT_COMMON_GET_CLASS(pit);
c->get_channel_info(pit, s, info);
}
void pit_reset_common(PITCommonState *pit)
{
PITChannelState *s;
int i;
for (i = 0; i < 3; i++) {
s = &pit->channels[i];
s->mode = 3;
s->gate = (i != 2);
s->count_load_time = qemu_get_clock_ns(vm_clock);
s->count = 0x10000;
if (i == 0 && !s->irq_disabled) {
s->next_transition_time =
pit_get_next_transition_time(s, s->count_load_time);
}
}
}
static int pit_init_common(ISADevice *dev)
{
PITCommonState *pit = PIT_COMMON(dev);
PITCommonClass *c = PIT_COMMON_GET_CLASS(pit);
int ret;
ret = c->init(pit);
if (ret < 0) {
return ret;
}
isa_register_ioport(dev, &pit->ioports, pit->iobase);
qdev_set_legacy_instance_id(&dev->qdev, pit->iobase, 2);
return 0;
}
static const VMStateDescription vmstate_pit_channel = {
.name = "pit channel",
.version_id = 2,
.minimum_version_id = 2,
.minimum_version_id_old = 2,
.fields = (VMStateField[]) {
VMSTATE_INT32(count, PITChannelState),
VMSTATE_UINT16(latched_count, PITChannelState),
VMSTATE_UINT8(count_latched, PITChannelState),
VMSTATE_UINT8(status_latched, PITChannelState),
VMSTATE_UINT8(status, PITChannelState),
VMSTATE_UINT8(read_state, PITChannelState),
VMSTATE_UINT8(write_state, PITChannelState),
VMSTATE_UINT8(write_latch, PITChannelState),
VMSTATE_UINT8(rw_mode, PITChannelState),
VMSTATE_UINT8(mode, PITChannelState),
VMSTATE_UINT8(bcd, PITChannelState),
VMSTATE_UINT8(gate, PITChannelState),
VMSTATE_INT64(count_load_time, PITChannelState),
VMSTATE_INT64(next_transition_time, PITChannelState),
VMSTATE_END_OF_LIST()
}
};
static int pit_load_old(QEMUFile *f, void *opaque, int version_id)
{
PITCommonState *pit = opaque;
PITCommonClass *c = PIT_COMMON_GET_CLASS(pit);
PITChannelState *s;
int i;
if (version_id != 1) {
return -EINVAL;
}
for (i = 0; i < 3; i++) {
s = &pit->channels[i];
s->count = qemu_get_be32(f);
qemu_get_be16s(f, &s->latched_count);
qemu_get_8s(f, &s->count_latched);
qemu_get_8s(f, &s->status_latched);
qemu_get_8s(f, &s->status);
qemu_get_8s(f, &s->read_state);
qemu_get_8s(f, &s->write_state);
qemu_get_8s(f, &s->write_latch);
qemu_get_8s(f, &s->rw_mode);
qemu_get_8s(f, &s->mode);
qemu_get_8s(f, &s->bcd);
qemu_get_8s(f, &s->gate);
s->count_load_time = qemu_get_be64(f);
s->irq_disabled = 0;
if (i == 0) {
s->next_transition_time = qemu_get_be64(f);
}
}
if (c->post_load) {
c->post_load(pit);
}
return 0;
}
static void pit_dispatch_pre_save(void *opaque)
{
PITCommonState *s = opaque;
PITCommonClass *c = PIT_COMMON_GET_CLASS(s);
if (c->pre_save) {
c->pre_save(s);
}
}
static int pit_dispatch_post_load(void *opaque, int version_id)
{
PITCommonState *s = opaque;
PITCommonClass *c = PIT_COMMON_GET_CLASS(s);
if (c->post_load) {
c->post_load(s);
}
return 0;
}
static const VMStateDescription vmstate_pit_common = {
.name = "i8254",
.version_id = 3,
.minimum_version_id = 2,
.minimum_version_id_old = 1,
.load_state_old = pit_load_old,
.pre_save = pit_dispatch_pre_save,
.post_load = pit_dispatch_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT32_V(channels[0].irq_disabled, PITCommonState, 3),
VMSTATE_STRUCT_ARRAY(channels, PITCommonState, 3, 2,
vmstate_pit_channel, PITChannelState),
VMSTATE_INT64(channels[0].next_transition_time,
PITCommonState), /* formerly irq_timer */
VMSTATE_END_OF_LIST()
}
};
static void pit_common_class_init(ObjectClass *klass, void *data)
{
ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
ic->init = pit_init_common;
dc->vmsd = &vmstate_pit_common;
dc->no_user = 1;
}
static const TypeInfo pit_common_type = {
.name = TYPE_PIT_COMMON,
.parent = TYPE_ISA_DEVICE,
.instance_size = sizeof(PITCommonState),
.class_size = sizeof(PITCommonClass),
.class_init = pit_common_class_init,
.abstract = true,
};
static void register_devices(void)
{
type_register_static(&pit_common_type);
}
type_init(register_devices);
+778
View File
@@ -0,0 +1,778 @@
/*
* QEMU M48T59 and M48T08 NVRAM emulation for PPC PREP and Sparc platforms
*
* Copyright (c) 2003-2005, 2007 Jocelyn Mayer
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "hw/hw.h"
#include "hw/timer/m48t59.h"
#include "qemu/timer.h"
#include "sysemu/sysemu.h"
#include "hw/sysbus.h"
#include "hw/isa/isa.h"
#include "exec/address-spaces.h"
//#define DEBUG_NVRAM
#if defined(DEBUG_NVRAM)
#define NVRAM_PRINTF(fmt, ...) do { printf(fmt , ## __VA_ARGS__); } while (0)
#else
#define NVRAM_PRINTF(fmt, ...) do { } while (0)
#endif
/*
* The M48T02, M48T08 and M48T59 chips are very similar. The newer '59 has
* alarm and a watchdog timer and related control registers. In the
* PPC platform there is also a nvram lock function.
*/
/*
* Chipset docs:
* http://www.st.com/stonline/products/literature/ds/2410/m48t02.pdf
* http://www.st.com/stonline/products/literature/ds/2411/m48t08.pdf
* http://www.st.com/stonline/products/literature/od/7001/m48t59y.pdf
*/
struct M48t59State {
/* Hardware parameters */
qemu_irq IRQ;
MemoryRegion iomem;
uint32_t io_base;
uint32_t size;
/* RTC management */
time_t time_offset;
time_t stop_time;
/* Alarm & watchdog */
struct tm alarm;
struct QEMUTimer *alrm_timer;
struct QEMUTimer *wd_timer;
/* NVRAM storage */
uint8_t *buffer;
/* Model parameters */
uint32_t model; /* 2 = m48t02, 8 = m48t08, 59 = m48t59 */
/* NVRAM storage */
uint16_t addr;
uint8_t lock;
};
typedef struct M48t59ISAState {
ISADevice busdev;
M48t59State state;
MemoryRegion io;
} M48t59ISAState;
typedef struct M48t59SysBusState {
SysBusDevice busdev;
M48t59State state;
MemoryRegion io;
} M48t59SysBusState;
/* Fake timer functions */
/* Alarm management */
static void alarm_cb (void *opaque)
{
struct tm tm;
uint64_t next_time;
M48t59State *NVRAM = opaque;
qemu_set_irq(NVRAM->IRQ, 1);
if ((NVRAM->buffer[0x1FF5] & 0x80) == 0 &&
(NVRAM->buffer[0x1FF4] & 0x80) == 0 &&
(NVRAM->buffer[0x1FF3] & 0x80) == 0 &&
(NVRAM->buffer[0x1FF2] & 0x80) == 0) {
/* Repeat once a month */
qemu_get_timedate(&tm, NVRAM->time_offset);
tm.tm_mon++;
if (tm.tm_mon == 13) {
tm.tm_mon = 1;
tm.tm_year++;
}
next_time = qemu_timedate_diff(&tm) - NVRAM->time_offset;
} else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 &&
(NVRAM->buffer[0x1FF4] & 0x80) == 0 &&
(NVRAM->buffer[0x1FF3] & 0x80) == 0 &&
(NVRAM->buffer[0x1FF2] & 0x80) == 0) {
/* Repeat once a day */
next_time = 24 * 60 * 60;
} else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 &&
(NVRAM->buffer[0x1FF4] & 0x80) != 0 &&
(NVRAM->buffer[0x1FF3] & 0x80) == 0 &&
(NVRAM->buffer[0x1FF2] & 0x80) == 0) {
/* Repeat once an hour */
next_time = 60 * 60;
} else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 &&
(NVRAM->buffer[0x1FF4] & 0x80) != 0 &&
(NVRAM->buffer[0x1FF3] & 0x80) != 0 &&
(NVRAM->buffer[0x1FF2] & 0x80) == 0) {
/* Repeat once a minute */
next_time = 60;
} else {
/* Repeat once a second */
next_time = 1;
}
qemu_mod_timer(NVRAM->alrm_timer, qemu_get_clock_ns(rtc_clock) +
next_time * 1000);
qemu_set_irq(NVRAM->IRQ, 0);
}
static void set_alarm(M48t59State *NVRAM)
{
int diff;
if (NVRAM->alrm_timer != NULL) {
qemu_del_timer(NVRAM->alrm_timer);
diff = qemu_timedate_diff(&NVRAM->alarm) - NVRAM->time_offset;
if (diff > 0)
qemu_mod_timer(NVRAM->alrm_timer, diff * 1000);
}
}
/* RTC management helpers */
static inline void get_time(M48t59State *NVRAM, struct tm *tm)
{
qemu_get_timedate(tm, NVRAM->time_offset);
}
static void set_time(M48t59State *NVRAM, struct tm *tm)
{
NVRAM->time_offset = qemu_timedate_diff(tm);
set_alarm(NVRAM);
}
/* Watchdog management */
static void watchdog_cb (void *opaque)
{
M48t59State *NVRAM = opaque;
NVRAM->buffer[0x1FF0] |= 0x80;
if (NVRAM->buffer[0x1FF7] & 0x80) {
NVRAM->buffer[0x1FF7] = 0x00;
NVRAM->buffer[0x1FFC] &= ~0x40;
/* May it be a hw CPU Reset instead ? */
qemu_system_reset_request();
} else {
qemu_set_irq(NVRAM->IRQ, 1);
qemu_set_irq(NVRAM->IRQ, 0);
}
}
static void set_up_watchdog(M48t59State *NVRAM, uint8_t value)
{
uint64_t interval; /* in 1/16 seconds */
NVRAM->buffer[0x1FF0] &= ~0x80;
if (NVRAM->wd_timer != NULL) {
qemu_del_timer(NVRAM->wd_timer);
if (value != 0) {
interval = (1 << (2 * (value & 0x03))) * ((value >> 2) & 0x1F);
qemu_mod_timer(NVRAM->wd_timer, ((uint64_t)time(NULL) * 1000) +
((interval * 1000) >> 4));
}
}
}
/* Direct access to NVRAM */
void m48t59_write (void *opaque, uint32_t addr, uint32_t val)
{
M48t59State *NVRAM = opaque;
struct tm tm;
int tmp;
if (addr > 0x1FF8 && addr < 0x2000)
NVRAM_PRINTF("%s: 0x%08x => 0x%08x\n", __func__, addr, val);
/* check for NVRAM access */
if ((NVRAM->model == 2 && addr < 0x7f8) ||
(NVRAM->model == 8 && addr < 0x1ff8) ||
(NVRAM->model == 59 && addr < 0x1ff0)) {
goto do_write;
}
/* TOD access */
switch (addr) {
case 0x1FF0:
/* flags register : read-only */
break;
case 0x1FF1:
/* unused */
break;
case 0x1FF2:
/* alarm seconds */
tmp = from_bcd(val & 0x7F);
if (tmp >= 0 && tmp <= 59) {
NVRAM->alarm.tm_sec = tmp;
NVRAM->buffer[0x1FF2] = val;
set_alarm(NVRAM);
}
break;
case 0x1FF3:
/* alarm minutes */
tmp = from_bcd(val & 0x7F);
if (tmp >= 0 && tmp <= 59) {
NVRAM->alarm.tm_min = tmp;
NVRAM->buffer[0x1FF3] = val;
set_alarm(NVRAM);
}
break;
case 0x1FF4:
/* alarm hours */
tmp = from_bcd(val & 0x3F);
if (tmp >= 0 && tmp <= 23) {
NVRAM->alarm.tm_hour = tmp;
NVRAM->buffer[0x1FF4] = val;
set_alarm(NVRAM);
}
break;
case 0x1FF5:
/* alarm date */
tmp = from_bcd(val & 0x3F);
if (tmp != 0) {
NVRAM->alarm.tm_mday = tmp;
NVRAM->buffer[0x1FF5] = val;
set_alarm(NVRAM);
}
break;
case 0x1FF6:
/* interrupts */
NVRAM->buffer[0x1FF6] = val;
break;
case 0x1FF7:
/* watchdog */
NVRAM->buffer[0x1FF7] = val;
set_up_watchdog(NVRAM, val);
break;
case 0x1FF8:
case 0x07F8:
/* control */
NVRAM->buffer[addr] = (val & ~0xA0) | 0x90;
break;
case 0x1FF9:
case 0x07F9:
/* seconds (BCD) */
tmp = from_bcd(val & 0x7F);
if (tmp >= 0 && tmp <= 59) {
get_time(NVRAM, &tm);
tm.tm_sec = tmp;
set_time(NVRAM, &tm);
}
if ((val & 0x80) ^ (NVRAM->buffer[addr] & 0x80)) {
if (val & 0x80) {
NVRAM->stop_time = time(NULL);
} else {
NVRAM->time_offset += NVRAM->stop_time - time(NULL);
NVRAM->stop_time = 0;
}
}
NVRAM->buffer[addr] = val & 0x80;
break;
case 0x1FFA:
case 0x07FA:
/* minutes (BCD) */
tmp = from_bcd(val & 0x7F);
if (tmp >= 0 && tmp <= 59) {
get_time(NVRAM, &tm);
tm.tm_min = tmp;
set_time(NVRAM, &tm);
}
break;
case 0x1FFB:
case 0x07FB:
/* hours (BCD) */
tmp = from_bcd(val & 0x3F);
if (tmp >= 0 && tmp <= 23) {
get_time(NVRAM, &tm);
tm.tm_hour = tmp;
set_time(NVRAM, &tm);
}
break;
case 0x1FFC:
case 0x07FC:
/* day of the week / century */
tmp = from_bcd(val & 0x07);
get_time(NVRAM, &tm);
tm.tm_wday = tmp;
set_time(NVRAM, &tm);
NVRAM->buffer[addr] = val & 0x40;
break;
case 0x1FFD:
case 0x07FD:
/* date (BCD) */
tmp = from_bcd(val & 0x3F);
if (tmp != 0) {
get_time(NVRAM, &tm);
tm.tm_mday = tmp;
set_time(NVRAM, &tm);
}
break;
case 0x1FFE:
case 0x07FE:
/* month */
tmp = from_bcd(val & 0x1F);
if (tmp >= 1 && tmp <= 12) {
get_time(NVRAM, &tm);
tm.tm_mon = tmp - 1;
set_time(NVRAM, &tm);
}
break;
case 0x1FFF:
case 0x07FF:
/* year */
tmp = from_bcd(val);
if (tmp >= 0 && tmp <= 99) {
get_time(NVRAM, &tm);
if (NVRAM->model == 8) {
tm.tm_year = from_bcd(val) + 68; // Base year is 1968
} else {
tm.tm_year = from_bcd(val);
}
set_time(NVRAM, &tm);
}
break;
default:
/* Check lock registers state */
if (addr >= 0x20 && addr <= 0x2F && (NVRAM->lock & 1))
break;
if (addr >= 0x30 && addr <= 0x3F && (NVRAM->lock & 2))
break;
do_write:
if (addr < NVRAM->size) {
NVRAM->buffer[addr] = val & 0xFF;
}
break;
}
}
uint32_t m48t59_read (void *opaque, uint32_t addr)
{
M48t59State *NVRAM = opaque;
struct tm tm;
uint32_t retval = 0xFF;
/* check for NVRAM access */
if ((NVRAM->model == 2 && addr < 0x078f) ||
(NVRAM->model == 8 && addr < 0x1ff8) ||
(NVRAM->model == 59 && addr < 0x1ff0)) {
goto do_read;
}
/* TOD access */
switch (addr) {
case 0x1FF0:
/* flags register */
goto do_read;
case 0x1FF1:
/* unused */
retval = 0;
break;
case 0x1FF2:
/* alarm seconds */
goto do_read;
case 0x1FF3:
/* alarm minutes */
goto do_read;
case 0x1FF4:
/* alarm hours */
goto do_read;
case 0x1FF5:
/* alarm date */
goto do_read;
case 0x1FF6:
/* interrupts */
goto do_read;
case 0x1FF7:
/* A read resets the watchdog */
set_up_watchdog(NVRAM, NVRAM->buffer[0x1FF7]);
goto do_read;
case 0x1FF8:
case 0x07F8:
/* control */
goto do_read;
case 0x1FF9:
case 0x07F9:
/* seconds (BCD) */
get_time(NVRAM, &tm);
retval = (NVRAM->buffer[addr] & 0x80) | to_bcd(tm.tm_sec);
break;
case 0x1FFA:
case 0x07FA:
/* minutes (BCD) */
get_time(NVRAM, &tm);
retval = to_bcd(tm.tm_min);
break;
case 0x1FFB:
case 0x07FB:
/* hours (BCD) */
get_time(NVRAM, &tm);
retval = to_bcd(tm.tm_hour);
break;
case 0x1FFC:
case 0x07FC:
/* day of the week / century */
get_time(NVRAM, &tm);
retval = NVRAM->buffer[addr] | tm.tm_wday;
break;
case 0x1FFD:
case 0x07FD:
/* date */
get_time(NVRAM, &tm);
retval = to_bcd(tm.tm_mday);
break;
case 0x1FFE:
case 0x07FE:
/* month */
get_time(NVRAM, &tm);
retval = to_bcd(tm.tm_mon + 1);
break;
case 0x1FFF:
case 0x07FF:
/* year */
get_time(NVRAM, &tm);
if (NVRAM->model == 8) {
retval = to_bcd(tm.tm_year - 68); // Base year is 1968
} else {
retval = to_bcd(tm.tm_year);
}
break;
default:
/* Check lock registers state */
if (addr >= 0x20 && addr <= 0x2F && (NVRAM->lock & 1))
break;
if (addr >= 0x30 && addr <= 0x3F && (NVRAM->lock & 2))
break;
do_read:
if (addr < NVRAM->size) {
retval = NVRAM->buffer[addr];
}
break;
}
if (addr > 0x1FF9 && addr < 0x2000)
NVRAM_PRINTF("%s: 0x%08x <= 0x%08x\n", __func__, addr, retval);
return retval;
}
void m48t59_toggle_lock (void *opaque, int lock)
{
M48t59State *NVRAM = opaque;
NVRAM->lock ^= 1 << lock;
}
/* IO access to NVRAM */
static void NVRAM_writeb(void *opaque, hwaddr addr, uint64_t val,
unsigned size)
{
M48t59State *NVRAM = opaque;
NVRAM_PRINTF("%s: 0x%08x => 0x%08x\n", __func__, addr, val);
switch (addr) {
case 0:
NVRAM->addr &= ~0x00FF;
NVRAM->addr |= val;
break;
case 1:
NVRAM->addr &= ~0xFF00;
NVRAM->addr |= val << 8;
break;
case 3:
m48t59_write(NVRAM, NVRAM->addr, val);
NVRAM->addr = 0x0000;
break;
default:
break;
}
}
static uint64_t NVRAM_readb(void *opaque, hwaddr addr, unsigned size)
{
M48t59State *NVRAM = opaque;
uint32_t retval;
switch (addr) {
case 3:
retval = m48t59_read(NVRAM, NVRAM->addr);
break;
default:
retval = -1;
break;
}
NVRAM_PRINTF("%s: 0x%08x <= 0x%08x\n", __func__, addr, retval);
return retval;
}
static void nvram_writeb (void *opaque, hwaddr addr, uint32_t value)
{
M48t59State *NVRAM = opaque;
m48t59_write(NVRAM, addr, value & 0xff);
}
static void nvram_writew (void *opaque, hwaddr addr, uint32_t value)
{
M48t59State *NVRAM = opaque;
m48t59_write(NVRAM, addr, (value >> 8) & 0xff);
m48t59_write(NVRAM, addr + 1, value & 0xff);
}
static void nvram_writel (void *opaque, hwaddr addr, uint32_t value)
{
M48t59State *NVRAM = opaque;
m48t59_write(NVRAM, addr, (value >> 24) & 0xff);
m48t59_write(NVRAM, addr + 1, (value >> 16) & 0xff);
m48t59_write(NVRAM, addr + 2, (value >> 8) & 0xff);
m48t59_write(NVRAM, addr + 3, value & 0xff);
}
static uint32_t nvram_readb (void *opaque, hwaddr addr)
{
M48t59State *NVRAM = opaque;
uint32_t retval;
retval = m48t59_read(NVRAM, addr);
return retval;
}
static uint32_t nvram_readw (void *opaque, hwaddr addr)
{
M48t59State *NVRAM = opaque;
uint32_t retval;
retval = m48t59_read(NVRAM, addr) << 8;
retval |= m48t59_read(NVRAM, addr + 1);
return retval;
}
static uint32_t nvram_readl (void *opaque, hwaddr addr)
{
M48t59State *NVRAM = opaque;
uint32_t retval;
retval = m48t59_read(NVRAM, addr) << 24;
retval |= m48t59_read(NVRAM, addr + 1) << 16;
retval |= m48t59_read(NVRAM, addr + 2) << 8;
retval |= m48t59_read(NVRAM, addr + 3);
return retval;
}
static const MemoryRegionOps nvram_ops = {
.old_mmio = {
.read = { nvram_readb, nvram_readw, nvram_readl, },
.write = { nvram_writeb, nvram_writew, nvram_writel, },
},
.endianness = DEVICE_NATIVE_ENDIAN,
};
static const VMStateDescription vmstate_m48t59 = {
.name = "m48t59",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT8(lock, M48t59State),
VMSTATE_UINT16(addr, M48t59State),
VMSTATE_VBUFFER_UINT32(buffer, M48t59State, 0, NULL, 0, size),
VMSTATE_END_OF_LIST()
}
};
static void m48t59_reset_common(M48t59State *NVRAM)
{
NVRAM->addr = 0;
NVRAM->lock = 0;
if (NVRAM->alrm_timer != NULL)
qemu_del_timer(NVRAM->alrm_timer);
if (NVRAM->wd_timer != NULL)
qemu_del_timer(NVRAM->wd_timer);
}
static void m48t59_reset_isa(DeviceState *d)
{
M48t59ISAState *isa = container_of(d, M48t59ISAState, busdev.qdev);
M48t59State *NVRAM = &isa->state;
m48t59_reset_common(NVRAM);
}
static void m48t59_reset_sysbus(DeviceState *d)
{
M48t59SysBusState *sys = container_of(d, M48t59SysBusState, busdev.qdev);
M48t59State *NVRAM = &sys->state;
m48t59_reset_common(NVRAM);
}
static const MemoryRegionOps m48t59_io_ops = {
.read = NVRAM_readb,
.write = NVRAM_writeb,
.impl = {
.min_access_size = 1,
.max_access_size = 1,
},
.endianness = DEVICE_LITTLE_ENDIAN,
};
/* Initialisation routine */
M48t59State *m48t59_init(qemu_irq IRQ, hwaddr mem_base,
uint32_t io_base, uint16_t size, int model)
{
DeviceState *dev;
SysBusDevice *s;
M48t59SysBusState *d;
M48t59State *state;
dev = qdev_create(NULL, "m48t59");
qdev_prop_set_uint32(dev, "model", model);
qdev_prop_set_uint32(dev, "size", size);
qdev_prop_set_uint32(dev, "io_base", io_base);
qdev_init_nofail(dev);
s = SYS_BUS_DEVICE(dev);
d = FROM_SYSBUS(M48t59SysBusState, s);
state = &d->state;
sysbus_connect_irq(s, 0, IRQ);
memory_region_init_io(&d->io, &m48t59_io_ops, state, "m48t59", 4);
if (io_base != 0) {
memory_region_add_subregion(get_system_io(), io_base, &d->io);
}
if (mem_base != 0) {
sysbus_mmio_map(s, 0, mem_base);
}
return state;
}
M48t59State *m48t59_init_isa(ISABus *bus, uint32_t io_base, uint16_t size,
int model)
{
M48t59ISAState *d;
ISADevice *dev;
M48t59State *s;
dev = isa_create(bus, "m48t59_isa");
qdev_prop_set_uint32(&dev->qdev, "model", model);
qdev_prop_set_uint32(&dev->qdev, "size", size);
qdev_prop_set_uint32(&dev->qdev, "io_base", io_base);
qdev_init_nofail(&dev->qdev);
d = DO_UPCAST(M48t59ISAState, busdev, dev);
s = &d->state;
memory_region_init_io(&d->io, &m48t59_io_ops, s, "m48t59", 4);
if (io_base != 0) {
isa_register_ioport(dev, &d->io, io_base);
}
return s;
}
static void m48t59_init_common(M48t59State *s)
{
s->buffer = g_malloc0(s->size);
if (s->model == 59) {
s->alrm_timer = qemu_new_timer_ns(rtc_clock, &alarm_cb, s);
s->wd_timer = qemu_new_timer_ns(vm_clock, &watchdog_cb, s);
}
qemu_get_timedate(&s->alarm, 0);
vmstate_register(NULL, -1, &vmstate_m48t59, s);
}
static int m48t59_init_isa1(ISADevice *dev)
{
M48t59ISAState *d = DO_UPCAST(M48t59ISAState, busdev, dev);
M48t59State *s = &d->state;
isa_init_irq(dev, &s->IRQ, 8);
m48t59_init_common(s);
return 0;
}
static int m48t59_init1(SysBusDevice *dev)
{
M48t59SysBusState *d = FROM_SYSBUS(M48t59SysBusState, dev);
M48t59State *s = &d->state;
sysbus_init_irq(dev, &s->IRQ);
memory_region_init_io(&s->iomem, &nvram_ops, s, "m48t59.nvram", s->size);
sysbus_init_mmio(dev, &s->iomem);
m48t59_init_common(s);
return 0;
}
static Property m48t59_isa_properties[] = {
DEFINE_PROP_UINT32("size", M48t59ISAState, state.size, -1),
DEFINE_PROP_UINT32("model", M48t59ISAState, state.model, -1),
DEFINE_PROP_HEX32( "io_base", M48t59ISAState, state.io_base, 0),
DEFINE_PROP_END_OF_LIST(),
};
static void m48t59_init_class_isa1(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
ic->init = m48t59_init_isa1;
dc->no_user = 1;
dc->reset = m48t59_reset_isa;
dc->props = m48t59_isa_properties;
}
static const TypeInfo m48t59_isa_info = {
.name = "m48t59_isa",
.parent = TYPE_ISA_DEVICE,
.instance_size = sizeof(M48t59ISAState),
.class_init = m48t59_init_class_isa1,
};
static Property m48t59_properties[] = {
DEFINE_PROP_UINT32("size", M48t59SysBusState, state.size, -1),
DEFINE_PROP_UINT32("model", M48t59SysBusState, state.model, -1),
DEFINE_PROP_HEX32( "io_base", M48t59SysBusState, state.io_base, 0),
DEFINE_PROP_END_OF_LIST(),
};
static void m48t59_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = m48t59_init1;
dc->reset = m48t59_reset_sysbus;
dc->props = m48t59_properties;
}
static const TypeInfo m48t59_info = {
.name = "m48t59",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(M48t59SysBusState),
.class_init = m48t59_class_init,
};
static void m48t59_register_types(void)
{
type_register_static(&m48t59_info);
type_register_static(&m48t59_isa_info);
}
type_init(m48t59_register_types)
+265
View File
@@ -0,0 +1,265 @@
/*
* ARM AMBA PrimeCell PL031 RTC
*
* Copyright (c) 2007 CodeSourcery
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#include "hw/sysbus.h"
#include "qemu/timer.h"
#include "sysemu/sysemu.h"
//#define DEBUG_PL031
#ifdef DEBUG_PL031
#define DPRINTF(fmt, ...) \
do { printf("pl031: " fmt , ## __VA_ARGS__); } while (0)
#else
#define DPRINTF(fmt, ...) do {} while(0)
#endif
#define RTC_DR 0x00 /* Data read register */
#define RTC_MR 0x04 /* Match register */
#define RTC_LR 0x08 /* Data load register */
#define RTC_CR 0x0c /* Control register */
#define RTC_IMSC 0x10 /* Interrupt mask and set register */
#define RTC_RIS 0x14 /* Raw interrupt status register */
#define RTC_MIS 0x18 /* Masked interrupt status register */
#define RTC_ICR 0x1c /* Interrupt clear register */
typedef struct {
SysBusDevice busdev;
MemoryRegion iomem;
QEMUTimer *timer;
qemu_irq irq;
/* Needed to preserve the tick_count across migration, even if the
* absolute value of the rtc_clock is different on the source and
* destination.
*/
uint32_t tick_offset_vmstate;
uint32_t tick_offset;
uint32_t mr;
uint32_t lr;
uint32_t cr;
uint32_t im;
uint32_t is;
} pl031_state;
static const unsigned char pl031_id[] = {
0x31, 0x10, 0x14, 0x00, /* Device ID */
0x0d, 0xf0, 0x05, 0xb1 /* Cell ID */
};
static void pl031_update(pl031_state *s)
{
qemu_set_irq(s->irq, s->is & s->im);
}
static void pl031_interrupt(void * opaque)
{
pl031_state *s = (pl031_state *)opaque;
s->is = 1;
DPRINTF("Alarm raised\n");
pl031_update(s);
}
static uint32_t pl031_get_count(pl031_state *s)
{
int64_t now = qemu_get_clock_ns(rtc_clock);
return s->tick_offset + now / get_ticks_per_sec();
}
static void pl031_set_alarm(pl031_state *s)
{
uint32_t ticks;
/* The timer wraps around. This subtraction also wraps in the same way,
and gives correct results when alarm < now_ticks. */
ticks = s->mr - pl031_get_count(s);
DPRINTF("Alarm set in %ud ticks\n", ticks);
if (ticks == 0) {
qemu_del_timer(s->timer);
pl031_interrupt(s);
} else {
int64_t now = qemu_get_clock_ns(rtc_clock);
qemu_mod_timer(s->timer, now + (int64_t)ticks * get_ticks_per_sec());
}
}
static uint64_t pl031_read(void *opaque, hwaddr offset,
unsigned size)
{
pl031_state *s = (pl031_state *)opaque;
if (offset >= 0xfe0 && offset < 0x1000)
return pl031_id[(offset - 0xfe0) >> 2];
switch (offset) {
case RTC_DR:
return pl031_get_count(s);
case RTC_MR:
return s->mr;
case RTC_IMSC:
return s->im;
case RTC_RIS:
return s->is;
case RTC_LR:
return s->lr;
case RTC_CR:
/* RTC is permanently enabled. */
return 1;
case RTC_MIS:
return s->is & s->im;
case RTC_ICR:
qemu_log_mask(LOG_GUEST_ERROR,
"pl031: read of write-only register at offset 0x%x\n",
(int)offset);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"pl031_read: Bad offset 0x%x\n", (int)offset);
break;
}
return 0;
}
static void pl031_write(void * opaque, hwaddr offset,
uint64_t value, unsigned size)
{
pl031_state *s = (pl031_state *)opaque;
switch (offset) {
case RTC_LR:
s->tick_offset += value - pl031_get_count(s);
pl031_set_alarm(s);
break;
case RTC_MR:
s->mr = value;
pl031_set_alarm(s);
break;
case RTC_IMSC:
s->im = value & 1;
DPRINTF("Interrupt mask %d\n", s->im);
pl031_update(s);
break;
case RTC_ICR:
/* The PL031 documentation (DDI0224B) states that the interrupt is
cleared when bit 0 of the written value is set. However the
arm926e documentation (DDI0287B) states that the interrupt is
cleared when any value is written. */
DPRINTF("Interrupt cleared");
s->is = 0;
pl031_update(s);
break;
case RTC_CR:
/* Written value is ignored. */
break;
case RTC_DR:
case RTC_MIS:
case RTC_RIS:
qemu_log_mask(LOG_GUEST_ERROR,
"pl031: write to read-only register at offset 0x%x\n",
(int)offset);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"pl031_write: Bad offset 0x%x\n", (int)offset);
break;
}
}
static const MemoryRegionOps pl031_ops = {
.read = pl031_read,
.write = pl031_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static int pl031_init(SysBusDevice *dev)
{
pl031_state *s = FROM_SYSBUS(pl031_state, dev);
struct tm tm;
memory_region_init_io(&s->iomem, &pl031_ops, s, "pl031", 0x1000);
sysbus_init_mmio(dev, &s->iomem);
sysbus_init_irq(dev, &s->irq);
qemu_get_timedate(&tm, 0);
s->tick_offset = mktimegm(&tm) - qemu_get_clock_ns(rtc_clock) / get_ticks_per_sec();
s->timer = qemu_new_timer_ns(rtc_clock, pl031_interrupt, s);
return 0;
}
static void pl031_pre_save(void *opaque)
{
pl031_state *s = opaque;
/* tick_offset is base_time - rtc_clock base time. Instead, we want to
* store the base time relative to the vm_clock for backwards-compatibility. */
int64_t delta = qemu_get_clock_ns(rtc_clock) - qemu_get_clock_ns(vm_clock);
s->tick_offset_vmstate = s->tick_offset + delta / get_ticks_per_sec();
}
static int pl031_post_load(void *opaque, int version_id)
{
pl031_state *s = opaque;
int64_t delta = qemu_get_clock_ns(rtc_clock) - qemu_get_clock_ns(vm_clock);
s->tick_offset = s->tick_offset_vmstate - delta / get_ticks_per_sec();
pl031_set_alarm(s);
return 0;
}
static const VMStateDescription vmstate_pl031 = {
.name = "pl031",
.version_id = 1,
.minimum_version_id = 1,
.pre_save = pl031_pre_save,
.post_load = pl031_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT32(tick_offset_vmstate, pl031_state),
VMSTATE_UINT32(mr, pl031_state),
VMSTATE_UINT32(lr, pl031_state),
VMSTATE_UINT32(cr, pl031_state),
VMSTATE_UINT32(im, pl031_state),
VMSTATE_UINT32(is, pl031_state),
VMSTATE_END_OF_LIST()
}
};
static void pl031_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = pl031_init;
dc->no_user = 1;
dc->vmsd = &vmstate_pl031;
}
static const TypeInfo pl031_info = {
.name = "pl031",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(pl031_state),
.class_init = pl031_class_init,
};
static void pl031_register_types(void)
{
type_register_static(&pl031_info);
}
type_init(pl031_register_types)
+151
View File
@@ -0,0 +1,151 @@
/*
* OSTimer device simulation in PKUnity SoC
*
* Copyright (C) 2010-2012 Guan Xuetao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation, or any later version.
* See the COPYING file in the top-level directory.
*/
#include "hw/sysbus.h"
#include "hw/ptimer.h"
#undef DEBUG_PUV3
#include "hw/unicore32/puv3.h"
/* puv3 ostimer implementation. */
typedef struct {
SysBusDevice busdev;
MemoryRegion iomem;
QEMUBH *bh;
qemu_irq irq;
ptimer_state *ptimer;
uint32_t reg_OSMR0;
uint32_t reg_OSCR;
uint32_t reg_OSSR;
uint32_t reg_OIER;
} PUV3OSTState;
static uint64_t puv3_ost_read(void *opaque, hwaddr offset,
unsigned size)
{
PUV3OSTState *s = opaque;
uint32_t ret = 0;
switch (offset) {
case 0x10: /* Counter Register */
ret = s->reg_OSMR0 - (uint32_t)ptimer_get_count(s->ptimer);
break;
case 0x14: /* Status Register */
ret = s->reg_OSSR;
break;
case 0x1c: /* Interrupt Enable Register */
ret = s->reg_OIER;
break;
default:
DPRINTF("Bad offset %x\n", (int)offset);
}
DPRINTF("offset 0x%x, value 0x%x\n", offset, ret);
return ret;
}
static void puv3_ost_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
PUV3OSTState *s = opaque;
DPRINTF("offset 0x%x, value 0x%x\n", offset, value);
switch (offset) {
case 0x00: /* Match Register 0 */
s->reg_OSMR0 = value;
if (s->reg_OSMR0 > s->reg_OSCR) {
ptimer_set_count(s->ptimer, s->reg_OSMR0 - s->reg_OSCR);
} else {
ptimer_set_count(s->ptimer, s->reg_OSMR0 +
(0xffffffff - s->reg_OSCR));
}
ptimer_run(s->ptimer, 2);
break;
case 0x14: /* Status Register */
assert(value == 0);
if (s->reg_OSSR) {
s->reg_OSSR = value;
qemu_irq_lower(s->irq);
}
break;
case 0x1c: /* Interrupt Enable Register */
s->reg_OIER = value;
break;
default:
DPRINTF("Bad offset %x\n", (int)offset);
}
}
static const MemoryRegionOps puv3_ost_ops = {
.read = puv3_ost_read,
.write = puv3_ost_write,
.impl = {
.min_access_size = 4,
.max_access_size = 4,
},
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void puv3_ost_tick(void *opaque)
{
PUV3OSTState *s = opaque;
DPRINTF("ost hit when ptimer counter from 0x%x to 0x%x!\n",
s->reg_OSCR, s->reg_OSMR0);
s->reg_OSCR = s->reg_OSMR0;
if (s->reg_OIER) {
s->reg_OSSR = 1;
qemu_irq_raise(s->irq);
}
}
static int puv3_ost_init(SysBusDevice *dev)
{
PUV3OSTState *s = FROM_SYSBUS(PUV3OSTState, dev);
s->reg_OIER = 0;
s->reg_OSSR = 0;
s->reg_OSMR0 = 0;
s->reg_OSCR = 0;
sysbus_init_irq(dev, &s->irq);
s->bh = qemu_bh_new(puv3_ost_tick, s);
s->ptimer = ptimer_init(s->bh);
ptimer_set_freq(s->ptimer, 50 * 1000 * 1000);
memory_region_init_io(&s->iomem, &puv3_ost_ops, s, "puv3_ost",
PUV3_REGS_OFFSET);
sysbus_init_mmio(dev, &s->iomem);
return 0;
}
static void puv3_ost_class_init(ObjectClass *klass, void *data)
{
SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
sdc->init = puv3_ost_init;
}
static const TypeInfo puv3_ost_info = {
.name = "puv3_ost",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(PUV3OSTState),
.class_init = puv3_ost_class_init,
};
static void puv3_ost_register_type(void)
{
type_register_static(&puv3_ost_info);
}
type_init(puv3_ost_register_type)
+882
View File
@@ -0,0 +1,882 @@
/*
* TI TWL92230C energy-management companion device for the OMAP24xx.
* Aka. Menelaus (N4200 MENELAUS1_V2.2)
*
* Copyright (C) 2008 Nokia Corporation
* Written by Andrzej Zaborowski <andrew@openedhand.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 or
* (at your option) version 3 of the License.
*
* 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/>.
*/
#include "hw/hw.h"
#include "qemu/timer.h"
#include "hw/i2c/i2c.h"
#include "sysemu/sysemu.h"
#include "ui/console.h"
#define VERBOSE 1
typedef struct {
I2CSlave i2c;
int firstbyte;
uint8_t reg;
uint8_t vcore[5];
uint8_t dcdc[3];
uint8_t ldo[8];
uint8_t sleep[2];
uint8_t osc;
uint8_t detect;
uint16_t mask;
uint16_t status;
uint8_t dir;
uint8_t inputs;
uint8_t outputs;
uint8_t bbsms;
uint8_t pull[4];
uint8_t mmc_ctrl[3];
uint8_t mmc_debounce;
struct {
uint8_t ctrl;
uint16_t comp;
QEMUTimer *hz_tm;
int64_t next;
struct tm tm;
struct tm new;
struct tm alm;
int sec_offset;
int alm_sec;
int next_comp;
} rtc;
uint16_t rtc_next_vmstate;
qemu_irq out[4];
uint8_t pwrbtn_state;
} MenelausState;
static inline void menelaus_update(MenelausState *s)
{
qemu_set_irq(s->out[3], s->status & ~s->mask);
}
static inline void menelaus_rtc_start(MenelausState *s)
{
s->rtc.next += qemu_get_clock_ms(rtc_clock);
qemu_mod_timer(s->rtc.hz_tm, s->rtc.next);
}
static inline void menelaus_rtc_stop(MenelausState *s)
{
qemu_del_timer(s->rtc.hz_tm);
s->rtc.next -= qemu_get_clock_ms(rtc_clock);
if (s->rtc.next < 1)
s->rtc.next = 1;
}
static void menelaus_rtc_update(MenelausState *s)
{
qemu_get_timedate(&s->rtc.tm, s->rtc.sec_offset);
}
static void menelaus_alm_update(MenelausState *s)
{
if ((s->rtc.ctrl & 3) == 3)
s->rtc.alm_sec = qemu_timedate_diff(&s->rtc.alm) - s->rtc.sec_offset;
}
static void menelaus_rtc_hz(void *opaque)
{
MenelausState *s = (MenelausState *) opaque;
s->rtc.next_comp --;
s->rtc.alm_sec --;
s->rtc.next += 1000;
qemu_mod_timer(s->rtc.hz_tm, s->rtc.next);
if ((s->rtc.ctrl >> 3) & 3) { /* EVERY */
menelaus_rtc_update(s);
if (((s->rtc.ctrl >> 3) & 3) == 1 && !s->rtc.tm.tm_sec)
s->status |= 1 << 8; /* RTCTMR */
else if (((s->rtc.ctrl >> 3) & 3) == 2 && !s->rtc.tm.tm_min)
s->status |= 1 << 8; /* RTCTMR */
else if (!s->rtc.tm.tm_hour)
s->status |= 1 << 8; /* RTCTMR */
} else
s->status |= 1 << 8; /* RTCTMR */
if ((s->rtc.ctrl >> 1) & 1) { /* RTC_AL_EN */
if (s->rtc.alm_sec == 0)
s->status |= 1 << 9; /* RTCALM */
/* TODO: wake-up */
}
if (s->rtc.next_comp <= 0) {
s->rtc.next -= muldiv64((int16_t) s->rtc.comp, 1000, 0x8000);
s->rtc.next_comp = 3600;
}
menelaus_update(s);
}
static void menelaus_reset(I2CSlave *i2c)
{
MenelausState *s = (MenelausState *) i2c;
s->reg = 0x00;
s->vcore[0] = 0x0c; /* XXX: X-loader needs 0x8c? check! */
s->vcore[1] = 0x05;
s->vcore[2] = 0x02;
s->vcore[3] = 0x0c;
s->vcore[4] = 0x03;
s->dcdc[0] = 0x33; /* Depends on wiring */
s->dcdc[1] = 0x03;
s->dcdc[2] = 0x00;
s->ldo[0] = 0x95;
s->ldo[1] = 0x7e;
s->ldo[2] = 0x00;
s->ldo[3] = 0x00; /* Depends on wiring */
s->ldo[4] = 0x03; /* Depends on wiring */
s->ldo[5] = 0x00;
s->ldo[6] = 0x00;
s->ldo[7] = 0x00;
s->sleep[0] = 0x00;
s->sleep[1] = 0x00;
s->osc = 0x01;
s->detect = 0x09;
s->mask = 0x0fff;
s->status = 0;
s->dir = 0x07;
s->outputs = 0x00;
s->bbsms = 0x00;
s->pull[0] = 0x00;
s->pull[1] = 0x00;
s->pull[2] = 0x00;
s->pull[3] = 0x00;
s->mmc_ctrl[0] = 0x03;
s->mmc_ctrl[1] = 0xc0;
s->mmc_ctrl[2] = 0x00;
s->mmc_debounce = 0x05;
if (s->rtc.ctrl & 1)
menelaus_rtc_stop(s);
s->rtc.ctrl = 0x00;
s->rtc.comp = 0x0000;
s->rtc.next = 1000;
s->rtc.sec_offset = 0;
s->rtc.next_comp = 1800;
s->rtc.alm_sec = 1800;
s->rtc.alm.tm_sec = 0x00;
s->rtc.alm.tm_min = 0x00;
s->rtc.alm.tm_hour = 0x00;
s->rtc.alm.tm_mday = 0x01;
s->rtc.alm.tm_mon = 0x00;
s->rtc.alm.tm_year = 2004;
menelaus_update(s);
}
static void menelaus_gpio_set(void *opaque, int line, int level)
{
MenelausState *s = (MenelausState *) opaque;
if (line < 3) {
/* No interrupt generated */
s->inputs &= ~(1 << line);
s->inputs |= level << line;
return;
}
if (!s->pwrbtn_state && level) {
s->status |= 1 << 11; /* PSHBTN */
menelaus_update(s);
}
s->pwrbtn_state = level;
}
#define MENELAUS_REV 0x01
#define MENELAUS_VCORE_CTRL1 0x02
#define MENELAUS_VCORE_CTRL2 0x03
#define MENELAUS_VCORE_CTRL3 0x04
#define MENELAUS_VCORE_CTRL4 0x05
#define MENELAUS_VCORE_CTRL5 0x06
#define MENELAUS_DCDC_CTRL1 0x07
#define MENELAUS_DCDC_CTRL2 0x08
#define MENELAUS_DCDC_CTRL3 0x09
#define MENELAUS_LDO_CTRL1 0x0a
#define MENELAUS_LDO_CTRL2 0x0b
#define MENELAUS_LDO_CTRL3 0x0c
#define MENELAUS_LDO_CTRL4 0x0d
#define MENELAUS_LDO_CTRL5 0x0e
#define MENELAUS_LDO_CTRL6 0x0f
#define MENELAUS_LDO_CTRL7 0x10
#define MENELAUS_LDO_CTRL8 0x11
#define MENELAUS_SLEEP_CTRL1 0x12
#define MENELAUS_SLEEP_CTRL2 0x13
#define MENELAUS_DEVICE_OFF 0x14
#define MENELAUS_OSC_CTRL 0x15
#define MENELAUS_DETECT_CTRL 0x16
#define MENELAUS_INT_MASK1 0x17
#define MENELAUS_INT_MASK2 0x18
#define MENELAUS_INT_STATUS1 0x19
#define MENELAUS_INT_STATUS2 0x1a
#define MENELAUS_INT_ACK1 0x1b
#define MENELAUS_INT_ACK2 0x1c
#define MENELAUS_GPIO_CTRL 0x1d
#define MENELAUS_GPIO_IN 0x1e
#define MENELAUS_GPIO_OUT 0x1f
#define MENELAUS_BBSMS 0x20
#define MENELAUS_RTC_CTRL 0x21
#define MENELAUS_RTC_UPDATE 0x22
#define MENELAUS_RTC_SEC 0x23
#define MENELAUS_RTC_MIN 0x24
#define MENELAUS_RTC_HR 0x25
#define MENELAUS_RTC_DAY 0x26
#define MENELAUS_RTC_MON 0x27
#define MENELAUS_RTC_YR 0x28
#define MENELAUS_RTC_WKDAY 0x29
#define MENELAUS_RTC_AL_SEC 0x2a
#define MENELAUS_RTC_AL_MIN 0x2b
#define MENELAUS_RTC_AL_HR 0x2c
#define MENELAUS_RTC_AL_DAY 0x2d
#define MENELAUS_RTC_AL_MON 0x2e
#define MENELAUS_RTC_AL_YR 0x2f
#define MENELAUS_RTC_COMP_MSB 0x30
#define MENELAUS_RTC_COMP_LSB 0x31
#define MENELAUS_S1_PULL_EN 0x32
#define MENELAUS_S1_PULL_DIR 0x33
#define MENELAUS_S2_PULL_EN 0x34
#define MENELAUS_S2_PULL_DIR 0x35
#define MENELAUS_MCT_CTRL1 0x36
#define MENELAUS_MCT_CTRL2 0x37
#define MENELAUS_MCT_CTRL3 0x38
#define MENELAUS_MCT_PIN_ST 0x39
#define MENELAUS_DEBOUNCE1 0x3a
static uint8_t menelaus_read(void *opaque, uint8_t addr)
{
MenelausState *s = (MenelausState *) opaque;
int reg = 0;
switch (addr) {
case MENELAUS_REV:
return 0x22;
case MENELAUS_VCORE_CTRL5: reg ++;
case MENELAUS_VCORE_CTRL4: reg ++;
case MENELAUS_VCORE_CTRL3: reg ++;
case MENELAUS_VCORE_CTRL2: reg ++;
case MENELAUS_VCORE_CTRL1:
return s->vcore[reg];
case MENELAUS_DCDC_CTRL3: reg ++;
case MENELAUS_DCDC_CTRL2: reg ++;
case MENELAUS_DCDC_CTRL1:
return s->dcdc[reg];
case MENELAUS_LDO_CTRL8: reg ++;
case MENELAUS_LDO_CTRL7: reg ++;
case MENELAUS_LDO_CTRL6: reg ++;
case MENELAUS_LDO_CTRL5: reg ++;
case MENELAUS_LDO_CTRL4: reg ++;
case MENELAUS_LDO_CTRL3: reg ++;
case MENELAUS_LDO_CTRL2: reg ++;
case MENELAUS_LDO_CTRL1:
return s->ldo[reg];
case MENELAUS_SLEEP_CTRL2: reg ++;
case MENELAUS_SLEEP_CTRL1:
return s->sleep[reg];
case MENELAUS_DEVICE_OFF:
return 0;
case MENELAUS_OSC_CTRL:
return s->osc | (1 << 7); /* CLK32K_GOOD */
case MENELAUS_DETECT_CTRL:
return s->detect;
case MENELAUS_INT_MASK1:
return (s->mask >> 0) & 0xff;
case MENELAUS_INT_MASK2:
return (s->mask >> 8) & 0xff;
case MENELAUS_INT_STATUS1:
return (s->status >> 0) & 0xff;
case MENELAUS_INT_STATUS2:
return (s->status >> 8) & 0xff;
case MENELAUS_INT_ACK1:
case MENELAUS_INT_ACK2:
return 0;
case MENELAUS_GPIO_CTRL:
return s->dir;
case MENELAUS_GPIO_IN:
return s->inputs | (~s->dir & s->outputs);
case MENELAUS_GPIO_OUT:
return s->outputs;
case MENELAUS_BBSMS:
return s->bbsms;
case MENELAUS_RTC_CTRL:
return s->rtc.ctrl;
case MENELAUS_RTC_UPDATE:
return 0x00;
case MENELAUS_RTC_SEC:
menelaus_rtc_update(s);
return to_bcd(s->rtc.tm.tm_sec);
case MENELAUS_RTC_MIN:
menelaus_rtc_update(s);
return to_bcd(s->rtc.tm.tm_min);
case MENELAUS_RTC_HR:
menelaus_rtc_update(s);
if ((s->rtc.ctrl >> 2) & 1) /* MODE12_n24 */
return to_bcd((s->rtc.tm.tm_hour % 12) + 1) |
(!!(s->rtc.tm.tm_hour >= 12) << 7); /* PM_nAM */
else
return to_bcd(s->rtc.tm.tm_hour);
case MENELAUS_RTC_DAY:
menelaus_rtc_update(s);
return to_bcd(s->rtc.tm.tm_mday);
case MENELAUS_RTC_MON:
menelaus_rtc_update(s);
return to_bcd(s->rtc.tm.tm_mon + 1);
case MENELAUS_RTC_YR:
menelaus_rtc_update(s);
return to_bcd(s->rtc.tm.tm_year - 2000);
case MENELAUS_RTC_WKDAY:
menelaus_rtc_update(s);
return to_bcd(s->rtc.tm.tm_wday);
case MENELAUS_RTC_AL_SEC:
return to_bcd(s->rtc.alm.tm_sec);
case MENELAUS_RTC_AL_MIN:
return to_bcd(s->rtc.alm.tm_min);
case MENELAUS_RTC_AL_HR:
if ((s->rtc.ctrl >> 2) & 1) /* MODE12_n24 */
return to_bcd((s->rtc.alm.tm_hour % 12) + 1) |
(!!(s->rtc.alm.tm_hour >= 12) << 7);/* AL_PM_nAM */
else
return to_bcd(s->rtc.alm.tm_hour);
case MENELAUS_RTC_AL_DAY:
return to_bcd(s->rtc.alm.tm_mday);
case MENELAUS_RTC_AL_MON:
return to_bcd(s->rtc.alm.tm_mon + 1);
case MENELAUS_RTC_AL_YR:
return to_bcd(s->rtc.alm.tm_year - 2000);
case MENELAUS_RTC_COMP_MSB:
return (s->rtc.comp >> 8) & 0xff;
case MENELAUS_RTC_COMP_LSB:
return (s->rtc.comp >> 0) & 0xff;
case MENELAUS_S1_PULL_EN:
return s->pull[0];
case MENELAUS_S1_PULL_DIR:
return s->pull[1];
case MENELAUS_S2_PULL_EN:
return s->pull[2];
case MENELAUS_S2_PULL_DIR:
return s->pull[3];
case MENELAUS_MCT_CTRL3: reg ++;
case MENELAUS_MCT_CTRL2: reg ++;
case MENELAUS_MCT_CTRL1:
return s->mmc_ctrl[reg];
case MENELAUS_MCT_PIN_ST:
/* TODO: return the real Card Detect */
return 0;
case MENELAUS_DEBOUNCE1:
return s->mmc_debounce;
default:
#ifdef VERBOSE
printf("%s: unknown register %02x\n", __FUNCTION__, addr);
#endif
break;
}
return 0;
}
static void menelaus_write(void *opaque, uint8_t addr, uint8_t value)
{
MenelausState *s = (MenelausState *) opaque;
int line;
int reg = 0;
struct tm tm;
switch (addr) {
case MENELAUS_VCORE_CTRL1:
s->vcore[0] = (value & 0xe) | MIN(value & 0x1f, 0x12);
break;
case MENELAUS_VCORE_CTRL2:
s->vcore[1] = value;
break;
case MENELAUS_VCORE_CTRL3:
s->vcore[2] = MIN(value & 0x1f, 0x12);
break;
case MENELAUS_VCORE_CTRL4:
s->vcore[3] = MIN(value & 0x1f, 0x12);
break;
case MENELAUS_VCORE_CTRL5:
s->vcore[4] = value & 3;
/* XXX
* auto set to 3 on M_Active, nRESWARM
* auto set to 0 on M_WaitOn, M_Backup
*/
break;
case MENELAUS_DCDC_CTRL1:
s->dcdc[0] = value & 0x3f;
break;
case MENELAUS_DCDC_CTRL2:
s->dcdc[1] = value & 0x07;
/* XXX
* auto set to 3 on M_Active, nRESWARM
* auto set to 0 on M_WaitOn, M_Backup
*/
break;
case MENELAUS_DCDC_CTRL3:
s->dcdc[2] = value & 0x07;
break;
case MENELAUS_LDO_CTRL1:
s->ldo[0] = value;
break;
case MENELAUS_LDO_CTRL2:
s->ldo[1] = value & 0x7f;
/* XXX
* auto set to 0x7e on M_WaitOn, M_Backup
*/
break;
case MENELAUS_LDO_CTRL3:
s->ldo[2] = value & 3;
/* XXX
* auto set to 3 on M_Active, nRESWARM
* auto set to 0 on M_WaitOn, M_Backup
*/
break;
case MENELAUS_LDO_CTRL4:
s->ldo[3] = value & 3;
/* XXX
* auto set to 3 on M_Active, nRESWARM
* auto set to 0 on M_WaitOn, M_Backup
*/
break;
case MENELAUS_LDO_CTRL5:
s->ldo[4] = value & 3;
/* XXX
* auto set to 3 on M_Active, nRESWARM
* auto set to 0 on M_WaitOn, M_Backup
*/
break;
case MENELAUS_LDO_CTRL6:
s->ldo[5] = value & 3;
break;
case MENELAUS_LDO_CTRL7:
s->ldo[6] = value & 3;
break;
case MENELAUS_LDO_CTRL8:
s->ldo[7] = value & 3;
break;
case MENELAUS_SLEEP_CTRL2: reg ++;
case MENELAUS_SLEEP_CTRL1:
s->sleep[reg] = value;
break;
case MENELAUS_DEVICE_OFF:
if (value & 1)
menelaus_reset(&s->i2c);
break;
case MENELAUS_OSC_CTRL:
s->osc = value & 7;
break;
case MENELAUS_DETECT_CTRL:
s->detect = value & 0x7f;
break;
case MENELAUS_INT_MASK1:
s->mask &= 0xf00;
s->mask |= value << 0;
menelaus_update(s);
break;
case MENELAUS_INT_MASK2:
s->mask &= 0x0ff;
s->mask |= value << 8;
menelaus_update(s);
break;
case MENELAUS_INT_ACK1:
s->status &= ~(((uint16_t) value) << 0);
menelaus_update(s);
break;
case MENELAUS_INT_ACK2:
s->status &= ~(((uint16_t) value) << 8);
menelaus_update(s);
break;
case MENELAUS_GPIO_CTRL:
for (line = 0; line < 3; line ++) {
if (((s->dir ^ value) >> line) & 1) {
qemu_set_irq(s->out[line],
((s->outputs & ~s->dir) >> line) & 1);
}
}
s->dir = value & 0x67;
break;
case MENELAUS_GPIO_OUT:
for (line = 0; line < 3; line ++) {
if ((((s->outputs ^ value) & ~s->dir) >> line) & 1) {
qemu_set_irq(s->out[line], (s->outputs >> line) & 1);
}
}
s->outputs = value & 0x07;
break;
case MENELAUS_BBSMS:
s->bbsms = 0x0d;
break;
case MENELAUS_RTC_CTRL:
if ((s->rtc.ctrl ^ value) & 1) { /* RTC_EN */
if (value & 1)
menelaus_rtc_start(s);
else
menelaus_rtc_stop(s);
}
s->rtc.ctrl = value & 0x1f;
menelaus_alm_update(s);
break;
case MENELAUS_RTC_UPDATE:
menelaus_rtc_update(s);
memcpy(&tm, &s->rtc.tm, sizeof(tm));
switch (value & 0xf) {
case 0:
break;
case 1:
tm.tm_sec = s->rtc.new.tm_sec;
break;
case 2:
tm.tm_min = s->rtc.new.tm_min;
break;
case 3:
if (s->rtc.new.tm_hour > 23)
goto rtc_badness;
tm.tm_hour = s->rtc.new.tm_hour;
break;
case 4:
if (s->rtc.new.tm_mday < 1)
goto rtc_badness;
/* TODO check range */
tm.tm_mday = s->rtc.new.tm_mday;
break;
case 5:
if (s->rtc.new.tm_mon < 0 || s->rtc.new.tm_mon > 11)
goto rtc_badness;
tm.tm_mon = s->rtc.new.tm_mon;
break;
case 6:
tm.tm_year = s->rtc.new.tm_year;
break;
case 7:
/* TODO set .tm_mday instead */
tm.tm_wday = s->rtc.new.tm_wday;
break;
case 8:
if (s->rtc.new.tm_hour > 23)
goto rtc_badness;
if (s->rtc.new.tm_mday < 1)
goto rtc_badness;
if (s->rtc.new.tm_mon < 0 || s->rtc.new.tm_mon > 11)
goto rtc_badness;
tm.tm_sec = s->rtc.new.tm_sec;
tm.tm_min = s->rtc.new.tm_min;
tm.tm_hour = s->rtc.new.tm_hour;
tm.tm_mday = s->rtc.new.tm_mday;
tm.tm_mon = s->rtc.new.tm_mon;
tm.tm_year = s->rtc.new.tm_year;
break;
rtc_badness:
default:
fprintf(stderr, "%s: bad RTC_UPDATE value %02x\n",
__FUNCTION__, value);
s->status |= 1 << 10; /* RTCERR */
menelaus_update(s);
}
s->rtc.sec_offset = qemu_timedate_diff(&tm);
break;
case MENELAUS_RTC_SEC:
s->rtc.tm.tm_sec = from_bcd(value & 0x7f);
break;
case MENELAUS_RTC_MIN:
s->rtc.tm.tm_min = from_bcd(value & 0x7f);
break;
case MENELAUS_RTC_HR:
s->rtc.tm.tm_hour = (s->rtc.ctrl & (1 << 2)) ? /* MODE12_n24 */
MIN(from_bcd(value & 0x3f), 12) + ((value >> 7) ? 11 : -1) :
from_bcd(value & 0x3f);
break;
case MENELAUS_RTC_DAY:
s->rtc.tm.tm_mday = from_bcd(value);
break;
case MENELAUS_RTC_MON:
s->rtc.tm.tm_mon = MAX(1, from_bcd(value)) - 1;
break;
case MENELAUS_RTC_YR:
s->rtc.tm.tm_year = 2000 + from_bcd(value);
break;
case MENELAUS_RTC_WKDAY:
s->rtc.tm.tm_mday = from_bcd(value);
break;
case MENELAUS_RTC_AL_SEC:
s->rtc.alm.tm_sec = from_bcd(value & 0x7f);
menelaus_alm_update(s);
break;
case MENELAUS_RTC_AL_MIN:
s->rtc.alm.tm_min = from_bcd(value & 0x7f);
menelaus_alm_update(s);
break;
case MENELAUS_RTC_AL_HR:
s->rtc.alm.tm_hour = (s->rtc.ctrl & (1 << 2)) ? /* MODE12_n24 */
MIN(from_bcd(value & 0x3f), 12) + ((value >> 7) ? 11 : -1) :
from_bcd(value & 0x3f);
menelaus_alm_update(s);
break;
case MENELAUS_RTC_AL_DAY:
s->rtc.alm.tm_mday = from_bcd(value);
menelaus_alm_update(s);
break;
case MENELAUS_RTC_AL_MON:
s->rtc.alm.tm_mon = MAX(1, from_bcd(value)) - 1;
menelaus_alm_update(s);
break;
case MENELAUS_RTC_AL_YR:
s->rtc.alm.tm_year = 2000 + from_bcd(value);
menelaus_alm_update(s);
break;
case MENELAUS_RTC_COMP_MSB:
s->rtc.comp &= 0xff;
s->rtc.comp |= value << 8;
break;
case MENELAUS_RTC_COMP_LSB:
s->rtc.comp &= 0xff << 8;
s->rtc.comp |= value;
break;
case MENELAUS_S1_PULL_EN:
s->pull[0] = value;
break;
case MENELAUS_S1_PULL_DIR:
s->pull[1] = value & 0x1f;
break;
case MENELAUS_S2_PULL_EN:
s->pull[2] = value;
break;
case MENELAUS_S2_PULL_DIR:
s->pull[3] = value & 0x1f;
break;
case MENELAUS_MCT_CTRL1:
s->mmc_ctrl[0] = value & 0x7f;
break;
case MENELAUS_MCT_CTRL2:
s->mmc_ctrl[1] = value;
/* TODO update Card Detect interrupts */
break;
case MENELAUS_MCT_CTRL3:
s->mmc_ctrl[2] = value & 0xf;
break;
case MENELAUS_DEBOUNCE1:
s->mmc_debounce = value & 0x3f;
break;
default:
#ifdef VERBOSE
printf("%s: unknown register %02x\n", __FUNCTION__, addr);
#endif
}
}
static void menelaus_event(I2CSlave *i2c, enum i2c_event event)
{
MenelausState *s = (MenelausState *) i2c;
if (event == I2C_START_SEND)
s->firstbyte = 1;
}
static int menelaus_tx(I2CSlave *i2c, uint8_t data)
{
MenelausState *s = (MenelausState *) i2c;
/* Interpret register address byte */
if (s->firstbyte) {
s->reg = data;
s->firstbyte = 0;
} else
menelaus_write(s, s->reg ++, data);
return 0;
}
static int menelaus_rx(I2CSlave *i2c)
{
MenelausState *s = (MenelausState *) i2c;
return menelaus_read(s, s->reg ++);
}
/* Save restore 32 bit int as uint16_t
This is a Big hack, but it is how the old state did it.
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)
{
int *v = pv;
*v = qemu_get_be16(f);
return 0;
}
static void put_int32_as_uint16(QEMUFile *f, void *pv, size_t size)
{
int *v = pv;
qemu_put_be16(f, *v);
}
static const VMStateInfo vmstate_hack_int32_as_uint16 = {
.name = "int32_as_uint16",
.get = get_int32_as_uint16,
.put = put_int32_as_uint16,
};
#define VMSTATE_UINT16_HACK(_f, _s) \
VMSTATE_SINGLE(_f, _s, 0, vmstate_hack_int32_as_uint16, int32_t)
static const VMStateDescription vmstate_menelaus_tm = {
.name = "menelaus_tm",
.version_id = 0,
.minimum_version_id = 0,
.minimum_version_id_old = 0,
.fields = (VMStateField []) {
VMSTATE_UINT16_HACK(tm_sec, struct tm),
VMSTATE_UINT16_HACK(tm_min, struct tm),
VMSTATE_UINT16_HACK(tm_hour, struct tm),
VMSTATE_UINT16_HACK(tm_mday, struct tm),
VMSTATE_UINT16_HACK(tm_min, struct tm),
VMSTATE_UINT16_HACK(tm_year, struct tm),
VMSTATE_END_OF_LIST()
}
};
static void menelaus_pre_save(void *opaque)
{
MenelausState *s = opaque;
/* Should be <= 1000 */
s->rtc_next_vmstate = s->rtc.next - qemu_get_clock_ms(rtc_clock);
}
static int menelaus_post_load(void *opaque, int version_id)
{
MenelausState *s = opaque;
if (s->rtc.ctrl & 1) /* RTC_EN */
menelaus_rtc_stop(s);
s->rtc.next = s->rtc_next_vmstate;
menelaus_alm_update(s);
menelaus_update(s);
if (s->rtc.ctrl & 1) /* RTC_EN */
menelaus_rtc_start(s);
return 0;
}
static const VMStateDescription vmstate_menelaus = {
.name = "menelaus",
.version_id = 0,
.minimum_version_id = 0,
.minimum_version_id_old = 0,
.pre_save = menelaus_pre_save,
.post_load = menelaus_post_load,
.fields = (VMStateField []) {
VMSTATE_INT32(firstbyte, MenelausState),
VMSTATE_UINT8(reg, MenelausState),
VMSTATE_UINT8_ARRAY(vcore, MenelausState, 5),
VMSTATE_UINT8_ARRAY(dcdc, MenelausState, 3),
VMSTATE_UINT8_ARRAY(ldo, MenelausState, 8),
VMSTATE_UINT8_ARRAY(sleep, MenelausState, 2),
VMSTATE_UINT8(osc, MenelausState),
VMSTATE_UINT8(detect, MenelausState),
VMSTATE_UINT16(mask, MenelausState),
VMSTATE_UINT16(status, MenelausState),
VMSTATE_UINT8(dir, MenelausState),
VMSTATE_UINT8(inputs, MenelausState),
VMSTATE_UINT8(outputs, MenelausState),
VMSTATE_UINT8(bbsms, MenelausState),
VMSTATE_UINT8_ARRAY(pull, MenelausState, 4),
VMSTATE_UINT8_ARRAY(mmc_ctrl, MenelausState, 3),
VMSTATE_UINT8(mmc_debounce, MenelausState),
VMSTATE_UINT8(rtc.ctrl, MenelausState),
VMSTATE_UINT16(rtc.comp, MenelausState),
VMSTATE_UINT16(rtc_next_vmstate, MenelausState),
VMSTATE_STRUCT(rtc.new, MenelausState, 0, vmstate_menelaus_tm,
struct tm),
VMSTATE_STRUCT(rtc.alm, MenelausState, 0, vmstate_menelaus_tm,
struct tm),
VMSTATE_UINT8(pwrbtn_state, MenelausState),
VMSTATE_I2C_SLAVE(i2c, MenelausState),
VMSTATE_END_OF_LIST()
}
};
static int twl92230_init(I2CSlave *i2c)
{
MenelausState *s = FROM_I2C_SLAVE(MenelausState, i2c);
s->rtc.hz_tm = qemu_new_timer_ms(rtc_clock, menelaus_rtc_hz, s);
/* Three output pins plus one interrupt pin. */
qdev_init_gpio_out(&i2c->qdev, s->out, 4);
/* Three input pins plus one power-button pin. */
qdev_init_gpio_in(&i2c->qdev, menelaus_gpio_set, 4);
menelaus_reset(&s->i2c);
return 0;
}
static void twl92230_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass);
sc->init = twl92230_init;
sc->event = menelaus_event;
sc->recv = menelaus_rx;
sc->send = menelaus_tx;
dc->vmsd = &vmstate_menelaus;
}
static const TypeInfo twl92230_info = {
.name = "twl92230",
.parent = TYPE_I2C_SLAVE,
.instance_size = sizeof(MenelausState),
.class_init = twl92230_class_init,
};
static void twl92230_register_types(void)
{
type_register_static(&twl92230_info);
}
type_init(twl92230_register_types)
+255
View File
@@ -0,0 +1,255 @@
/*
* QEMU model of the Xilinx timer block.
*
* Copyright (c) 2009 Edgar E. Iglesias.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "hw/sysbus.h"
#include "hw/ptimer.h"
#include "qemu/log.h"
#define D(x)
#define R_TCSR 0
#define R_TLR 1
#define R_TCR 2
#define R_MAX 4
#define TCSR_MDT (1<<0)
#define TCSR_UDT (1<<1)
#define TCSR_GENT (1<<2)
#define TCSR_CAPT (1<<3)
#define TCSR_ARHT (1<<4)
#define TCSR_LOAD (1<<5)
#define TCSR_ENIT (1<<6)
#define TCSR_ENT (1<<7)
#define TCSR_TINT (1<<8)
#define TCSR_PWMA (1<<9)
#define TCSR_ENALL (1<<10)
struct xlx_timer
{
QEMUBH *bh;
ptimer_state *ptimer;
void *parent;
int nr; /* for debug. */
unsigned long timer_div;
uint32_t regs[R_MAX];
};
struct timerblock
{
SysBusDevice busdev;
MemoryRegion mmio;
qemu_irq irq;
uint8_t one_timer_only;
uint32_t freq_hz;
struct xlx_timer *timers;
};
static inline unsigned int num_timers(struct timerblock *t)
{
return 2 - t->one_timer_only;
}
static inline unsigned int timer_from_addr(hwaddr addr)
{
/* Timers get a 4x32bit control reg area each. */
return addr >> 2;
}
static void timer_update_irq(struct timerblock *t)
{
unsigned int i, irq = 0;
uint32_t csr;
for (i = 0; i < num_timers(t); i++) {
csr = t->timers[i].regs[R_TCSR];
irq |= (csr & TCSR_TINT) && (csr & TCSR_ENIT);
}
/* All timers within the same slave share a single IRQ line. */
qemu_set_irq(t->irq, !!irq);
}
static uint64_t
timer_read(void *opaque, hwaddr addr, unsigned int size)
{
struct timerblock *t = opaque;
struct xlx_timer *xt;
uint32_t r = 0;
unsigned int timer;
addr >>= 2;
timer = timer_from_addr(addr);
xt = &t->timers[timer];
/* Further decoding to address a specific timers reg. */
addr &= 0x3;
switch (addr)
{
case R_TCR:
r = ptimer_get_count(xt->ptimer);
if (!(xt->regs[R_TCSR] & TCSR_UDT))
r = ~r;
D(qemu_log("xlx_timer t=%d read counter=%x udt=%d\n",
timer, r, xt->regs[R_TCSR] & TCSR_UDT));
break;
default:
if (addr < ARRAY_SIZE(xt->regs))
r = xt->regs[addr];
break;
}
D(fprintf(stderr, "%s timer=%d %x=%x\n", __func__, timer, addr * 4, r));
return r;
}
static void timer_enable(struct xlx_timer *xt)
{
uint64_t count;
D(fprintf(stderr, "%s timer=%d down=%d\n", __func__,
xt->nr, xt->regs[R_TCSR] & TCSR_UDT));
ptimer_stop(xt->ptimer);
if (xt->regs[R_TCSR] & TCSR_UDT)
count = xt->regs[R_TLR];
else
count = ~0 - xt->regs[R_TLR];
ptimer_set_limit(xt->ptimer, count, 1);
ptimer_run(xt->ptimer, 1);
}
static void
timer_write(void *opaque, hwaddr addr,
uint64_t val64, unsigned int size)
{
struct timerblock *t = opaque;
struct xlx_timer *xt;
unsigned int timer;
uint32_t value = val64;
addr >>= 2;
timer = timer_from_addr(addr);
xt = &t->timers[timer];
D(fprintf(stderr, "%s addr=%x val=%x (timer=%d off=%d)\n",
__func__, addr * 4, value, timer, addr & 3));
/* Further decoding to address a specific timers reg. */
addr &= 3;
switch (addr)
{
case R_TCSR:
if (value & TCSR_TINT)
value &= ~TCSR_TINT;
xt->regs[addr] = value;
if (value & TCSR_ENT)
timer_enable(xt);
break;
default:
if (addr < ARRAY_SIZE(xt->regs))
xt->regs[addr] = value;
break;
}
timer_update_irq(t);
}
static const MemoryRegionOps timer_ops = {
.read = timer_read,
.write = timer_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 4
}
};
static void timer_hit(void *opaque)
{
struct xlx_timer *xt = opaque;
struct timerblock *t = xt->parent;
D(fprintf(stderr, "%s %d\n", __func__, xt->nr));
xt->regs[R_TCSR] |= TCSR_TINT;
if (xt->regs[R_TCSR] & TCSR_ARHT)
timer_enable(xt);
timer_update_irq(t);
}
static int xilinx_timer_init(SysBusDevice *dev)
{
struct timerblock *t = FROM_SYSBUS(typeof (*t), dev);
unsigned int i;
/* All timers share a single irq line. */
sysbus_init_irq(dev, &t->irq);
/* Init all the ptimers. */
t->timers = g_malloc0(sizeof t->timers[0] * num_timers(t));
for (i = 0; i < num_timers(t); i++) {
struct xlx_timer *xt = &t->timers[i];
xt->parent = t;
xt->nr = i;
xt->bh = qemu_bh_new(timer_hit, xt);
xt->ptimer = ptimer_init(xt->bh);
ptimer_set_freq(xt->ptimer, t->freq_hz);
}
memory_region_init_io(&t->mmio, &timer_ops, t, "xlnx.xps-timer",
R_MAX * 4 * num_timers(t));
sysbus_init_mmio(dev, &t->mmio);
return 0;
}
static Property xilinx_timer_properties[] = {
DEFINE_PROP_UINT32("clock-frequency", struct timerblock, freq_hz,
62 * 1000000),
DEFINE_PROP_UINT8("one-timer-only", struct timerblock, one_timer_only, 0),
DEFINE_PROP_END_OF_LIST(),
};
static void xilinx_timer_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = xilinx_timer_init;
dc->props = xilinx_timer_properties;
}
static const TypeInfo xilinx_timer_info = {
.name = "xlnx.xps-timer",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(struct timerblock),
.class_init = xilinx_timer_class_init,
};
static void xilinx_timer_register_types(void)
{
type_register_static(&xilinx_timer_info);
}
type_init(xilinx_timer_register_types)