a64508304e
Migrate rtc_ref (which only needs to be 32-bit because it is summed to a 32-bit register), which requires bumping the migration version. The HPPA machine does not have versioned machine types so it is okay to block migration to old versions of QEMU. While at it, drop the write-only field rtc from LasiState. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
275 lines
6.4 KiB
C
275 lines
6.4 KiB
C
/*
|
|
* HP-PARISC Lasi chipset emulation.
|
|
*
|
|
* (C) 2019 by Helge Deller <deller@gmx.de>
|
|
*
|
|
* This work is licensed under the GNU GPL license version 2 or later.
|
|
*
|
|
* Documentation available at:
|
|
* https://parisc.wiki.kernel.org/images-parisc/7/79/Lasi_ers.pdf
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "qemu/units.h"
|
|
#include "qemu/log.h"
|
|
#include "qapi/error.h"
|
|
#include "trace.h"
|
|
#include "hw/irq.h"
|
|
#include "sysemu/sysemu.h"
|
|
#include "sysemu/runstate.h"
|
|
#include "migration/vmstate.h"
|
|
#include "qom/object.h"
|
|
#include "hw/misc/lasi.h"
|
|
|
|
|
|
static bool lasi_chip_mem_valid(void *opaque, hwaddr addr,
|
|
unsigned size, bool is_write,
|
|
MemTxAttrs attrs)
|
|
{
|
|
bool ret = false;
|
|
|
|
switch (addr) {
|
|
case LASI_IRR:
|
|
case LASI_IMR:
|
|
case LASI_IPR:
|
|
case LASI_ICR:
|
|
case LASI_IAR:
|
|
|
|
case LASI_LPT:
|
|
case LASI_UART:
|
|
case LASI_LAN:
|
|
case LASI_RTC:
|
|
|
|
case LASI_PCR ... LASI_AMR:
|
|
ret = true;
|
|
}
|
|
|
|
trace_lasi_chip_mem_valid(addr, ret);
|
|
return ret;
|
|
}
|
|
|
|
static MemTxResult lasi_chip_read_with_attrs(void *opaque, hwaddr addr,
|
|
uint64_t *data, unsigned size,
|
|
MemTxAttrs attrs)
|
|
{
|
|
LasiState *s = opaque;
|
|
MemTxResult ret = MEMTX_OK;
|
|
uint32_t val;
|
|
|
|
switch (addr) {
|
|
case LASI_IRR:
|
|
val = s->irr;
|
|
break;
|
|
case LASI_IMR:
|
|
val = s->imr;
|
|
break;
|
|
case LASI_IPR:
|
|
val = s->ipr;
|
|
/* Any read to IPR clears the register. */
|
|
s->ipr = 0;
|
|
break;
|
|
case LASI_ICR:
|
|
val = s->icr & ICR_BUS_ERROR_BIT; /* bus_error */
|
|
break;
|
|
case LASI_IAR:
|
|
val = s->iar;
|
|
break;
|
|
|
|
case LASI_LPT:
|
|
case LASI_UART:
|
|
case LASI_LAN:
|
|
val = 0;
|
|
break;
|
|
case LASI_RTC:
|
|
val = time(NULL);
|
|
val += s->rtc_ref;
|
|
break;
|
|
|
|
case LASI_PCR:
|
|
case LASI_VER: /* only version 0 existed. */
|
|
case LASI_IORESET:
|
|
val = 0;
|
|
break;
|
|
case LASI_ERRLOG:
|
|
val = s->errlog;
|
|
break;
|
|
case LASI_AMR:
|
|
val = s->amr;
|
|
break;
|
|
|
|
default:
|
|
/* Controlled by lasi_chip_mem_valid above. */
|
|
g_assert_not_reached();
|
|
}
|
|
|
|
trace_lasi_chip_read(addr, val);
|
|
|
|
*data = val;
|
|
return ret;
|
|
}
|
|
|
|
static MemTxResult lasi_chip_write_with_attrs(void *opaque, hwaddr addr,
|
|
uint64_t val, unsigned size,
|
|
MemTxAttrs attrs)
|
|
{
|
|
LasiState *s = opaque;
|
|
|
|
trace_lasi_chip_write(addr, val);
|
|
|
|
switch (addr) {
|
|
case LASI_IRR:
|
|
/* read-only. */
|
|
break;
|
|
case LASI_IMR:
|
|
s->imr = val;
|
|
if (((val & LASI_IRQ_BITS) != val) && (val != 0xffffffff)) {
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
"LASI: tried to set invalid %lx IMR value.\n",
|
|
(unsigned long) val);
|
|
}
|
|
break;
|
|
case LASI_IPR:
|
|
/* Any write to IPR clears the register. */
|
|
s->ipr = 0;
|
|
break;
|
|
case LASI_ICR:
|
|
s->icr = val;
|
|
/* if (val & ICR_TOC_BIT) issue_toc(); */
|
|
break;
|
|
case LASI_IAR:
|
|
s->iar = val;
|
|
break;
|
|
|
|
case LASI_LPT:
|
|
/* XXX: reset parallel port */
|
|
break;
|
|
case LASI_UART:
|
|
/* XXX: reset serial port */
|
|
break;
|
|
case LASI_LAN:
|
|
/* XXX: reset LAN card */
|
|
break;
|
|
case LASI_RTC:
|
|
s->rtc_ref = val - time(NULL);
|
|
break;
|
|
|
|
case LASI_PCR:
|
|
if (val == 0x02) { /* immediately power off */
|
|
qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
|
|
}
|
|
break;
|
|
case LASI_ERRLOG:
|
|
s->errlog = val;
|
|
break;
|
|
case LASI_VER:
|
|
/* read-only. */
|
|
break;
|
|
case LASI_IORESET:
|
|
break; /* XXX: TODO: Reset various devices. */
|
|
case LASI_AMR:
|
|
s->amr = val;
|
|
break;
|
|
|
|
default:
|
|
/* Controlled by lasi_chip_mem_valid above. */
|
|
g_assert_not_reached();
|
|
}
|
|
return MEMTX_OK;
|
|
}
|
|
|
|
static const MemoryRegionOps lasi_chip_ops = {
|
|
.read_with_attrs = lasi_chip_read_with_attrs,
|
|
.write_with_attrs = lasi_chip_write_with_attrs,
|
|
.endianness = DEVICE_BIG_ENDIAN,
|
|
.valid = {
|
|
.min_access_size = 1,
|
|
.max_access_size = 4,
|
|
.accepts = lasi_chip_mem_valid,
|
|
},
|
|
.impl = {
|
|
.min_access_size = 1,
|
|
.max_access_size = 4,
|
|
},
|
|
};
|
|
|
|
static const VMStateDescription vmstate_lasi = {
|
|
.name = "Lasi",
|
|
.version_id = 2,
|
|
.minimum_version_id = 1,
|
|
.fields = (VMStateField[]) {
|
|
VMSTATE_UINT32(irr, LasiState),
|
|
VMSTATE_UINT32(imr, LasiState),
|
|
VMSTATE_UINT32(ipr, LasiState),
|
|
VMSTATE_UINT32(icr, LasiState),
|
|
VMSTATE_UINT32(iar, LasiState),
|
|
VMSTATE_UINT32(errlog, LasiState),
|
|
VMSTATE_UINT32(amr, LasiState),
|
|
VMSTATE_UINT32_V(rtc_ref, LasiState, 2),
|
|
VMSTATE_END_OF_LIST()
|
|
}
|
|
};
|
|
|
|
|
|
static void lasi_set_irq(void *opaque, int irq, int level)
|
|
{
|
|
LasiState *s = opaque;
|
|
uint32_t bit = 1u << irq;
|
|
|
|
if (level) {
|
|
s->ipr |= bit;
|
|
if (bit & s->imr) {
|
|
uint32_t iar = s->iar;
|
|
s->irr |= bit;
|
|
if ((s->icr & ICR_BUS_ERROR_BIT) == 0) {
|
|
stl_be_phys(&address_space_memory, iar & -32, iar & 31);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void lasi_reset(DeviceState *dev)
|
|
{
|
|
LasiState *s = LASI_CHIP(dev);
|
|
|
|
s->iar = 0xFFFB0000 + 3; /* CPU_HPA + 3 */
|
|
|
|
/* Real time clock (RTC), it's only one 32-bit counter @9000 */
|
|
s->rtc_ref = 0;
|
|
}
|
|
|
|
static void lasi_init(Object *obj)
|
|
{
|
|
LasiState *s = LASI_CHIP(obj);
|
|
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
|
|
|
memory_region_init_io(&s->this_mem, OBJECT(s), &lasi_chip_ops,
|
|
s, "lasi", 0x100000);
|
|
|
|
sysbus_init_mmio(sbd, &s->this_mem);
|
|
|
|
qdev_init_gpio_in(DEVICE(obj), lasi_set_irq, LASI_IRQS);
|
|
}
|
|
|
|
static void lasi_class_init(ObjectClass *klass, void *data)
|
|
{
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
|
|
dc->reset = lasi_reset;
|
|
dc->vmsd = &vmstate_lasi;
|
|
}
|
|
|
|
static const TypeInfo lasi_pcihost_info = {
|
|
.name = TYPE_LASI_CHIP,
|
|
.parent = TYPE_SYS_BUS_DEVICE,
|
|
.instance_init = lasi_init,
|
|
.instance_size = sizeof(LasiState),
|
|
.class_init = lasi_class_init,
|
|
};
|
|
|
|
static void lasi_register_types(void)
|
|
{
|
|
type_register_static(&lasi_pcihost_info);
|
|
}
|
|
|
|
type_init(lasi_register_types)
|