mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-03 15:53:54 -06:00
Fix typos and docs, trivial changes and RTC devices split
-----BEGIN PGP SIGNATURE----- iQJGBAABCAAwFiEEzS913cjjpNwuT1Fz8ww4vT8vvjwFAl2ys7YSHGxhdXJlbnRA dml2aWVyLmV1AAoJEPMMOL0/L748A9QP/jrWoywp5vNVti1YJtVpzHx/GOQcqr2s 1bXFEzEvTAfI1TxQseiz4Z/jW9OmYNSt+lmW8FHBHLn4l4oo8e4/PurSBRa5guW3 Dsohqe2TAK0Ua1ELf4eoIfGWm2rKwz2TH0iChk6nvWKJaLQPTzjEgAlZSqOx7jfV q2+jSKQOzQJDwzMtzOTcan3VxPnru5j4YYlJspSJVNbiQ/bmQMV7JcsXBU5i4Tf+ Z1679CDe1BiRqcxKTsWEYMvAVmINYYfEsp2RuE+Co1mr9bQj9pBCFfybe7x5T3VG FaCEnfMEtOhgAryfW6/k6IfMvHgV6HjvIpfc27ZIn+kMwhhJl6V4Ca9LY7iUKJnf zWYl4FbC0NCM3udWdB/ogaZ6GVJ9FHBcZtnGNWesb3H09KLkRrz95mkOJl1/kPnW eDhzCO9g49T3zOECjGpodBF0RXGXVl1vTukwYk0I0d7bo8NySJ4dxVX91D0OIq4S +TilFrLi1ssXv9/7Y0Y3zC8/p1qVSIzKa1t4618xqf1MO5LfiVRWpPTPgV+0z3kw R48LzMqX50KnIT6fY7BY0YsskwZ1BbZmIJ/++6r8M1L+xaegUsIdI8LHI70TJ7JR hJ+uTgWky8N19A0OUJj08xjetNOv6Souf2GFaTrGJO0DQbyzwjNwELYNVhagqvOZ PHakcrjaMv8G =u0rQ -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/vivier2/tags/trivial-branch-pull-request' into staging Fix typos and docs, trivial changes and RTC devices split # gpg: Signature made Fri 25 Oct 2019 09:35:02 BST # gpg: using RSA key CD2F75DDC8E3A4DC2E4F5173F30C38BD3F2FBE3C # gpg: issuer "laurent@vivier.eu" # gpg: Good signature from "Laurent Vivier <lvivier@redhat.com>" [full] # gpg: aka "Laurent Vivier <laurent@vivier.eu>" [full] # gpg: aka "Laurent Vivier (Red Hat) <lvivier@redhat.com>" [full] # Primary key fingerprint: CD2F 75DD C8E3 A4DC 2E4F 5173 F30C 38BD 3F2F BE3C * remotes/vivier2/tags/trivial-branch-pull-request: hw/rtc/aspeed_rtc: Remove unused includes hw/rtc/xlnx-zynqmp-rtc: Remove unused "ptimer.h" include hw/rtc/mc146818: Include mc146818rtc_regs.h a bit less hw: Move Aspeed RTC from hw/timer/ to hw/rtc/ subdirectory hw: Move Exynos4210 RTC from hw/timer/ to hw/rtc/ subdirectory hw: Move Xilinx ZynqMP RTC from hw/timer/ to hw/rtc/ subdirectory hw: Move DS1338 device from hw/timer/ to hw/rtc/ subdirectory hw: Move TWL92230 device from hw/timer/ to hw/rtc/ subdirectory hw: Move sun4v hypervisor RTC from hw/timer/ to hw/rtc/ subdirectory hw: Move M41T80 device from hw/timer/ to hw/rtc/ subdirectory hw: Move M48T59 device from hw/timer/ to hw/rtc/ subdirectory hw: Move MC146818 device from hw/timer/ to hw/rtc/ subdirectory hw: Move PL031 device from hw/timer/ to hw/rtc/ subdirectory hw/timer: Compile devices not target-dependent as common object qemu-timer: reuse MIN macro in qemu_timeout_ns_to_ms event_notifier: avoid dandling file descriptor in event_notifier_cleanup util/async: avoid useless cast pci_bridge: fix a typo in comment qemu-options.hx: Update for reboot-timeout parameter Signed-off-by: Peter Maydell <peter.maydell@linaro.org> # Conflicts: # hw/timer/trace-events
This commit is contained in:
commit
bad76ac319
56 changed files with 213 additions and 165 deletions
|
@ -9,10 +9,6 @@ config ARM_MPTIMER
|
|||
config A9_GTIMER
|
||||
bool
|
||||
|
||||
config DS1338
|
||||
bool
|
||||
depends on I2C
|
||||
|
||||
config HPET
|
||||
bool
|
||||
default y if PC
|
||||
|
@ -20,27 +16,10 @@ config HPET
|
|||
config I8254
|
||||
bool
|
||||
|
||||
config M41T80
|
||||
bool
|
||||
depends on I2C
|
||||
|
||||
config M48T59
|
||||
bool
|
||||
|
||||
config PL031
|
||||
bool
|
||||
|
||||
config TWL92230
|
||||
bool
|
||||
depends on I2C
|
||||
|
||||
config ALTERA_TIMER
|
||||
bool
|
||||
select PTIMER
|
||||
|
||||
config MC146818RTC
|
||||
bool
|
||||
|
||||
config ALLWINNER_A10_PIT
|
||||
bool
|
||||
select PTIMER
|
||||
|
@ -48,9 +27,6 @@ config ALLWINNER_A10_PIT
|
|||
config STM32F2XX_TIMER
|
||||
bool
|
||||
|
||||
config SUN4V_RTC
|
||||
bool
|
||||
|
||||
config CMSDK_APB_TIMER
|
||||
bool
|
||||
select PTIMER
|
||||
|
|
|
@ -3,17 +3,9 @@ common-obj-$(CONFIG_ARM_MPTIMER) += arm_mptimer.o
|
|||
common-obj-$(CONFIG_ARM_V7M) += armv7m_systick.o
|
||||
common-obj-$(CONFIG_A9_GTIMER) += a9gtimer.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_M41T80) += m41t80.o
|
||||
common-obj-$(CONFIG_M48T59) += m48t59.o
|
||||
ifeq ($(CONFIG_ISA_BUS),y)
|
||||
common-obj-$(CONFIG_M48T59) += m48t59-isa.o
|
||||
endif
|
||||
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
|
||||
common-obj-$(CONFIG_SLAVIO) += slavio_timer.o
|
||||
common-obj-$(CONFIG_ETRAXFS) += etraxfs_timer.o
|
||||
|
@ -22,28 +14,23 @@ common-obj-$(CONFIG_IMX) += imx_epit.o
|
|||
common-obj-$(CONFIG_IMX) += imx_gpt.o
|
||||
common-obj-$(CONFIG_LM32) += lm32_timer.o
|
||||
common-obj-$(CONFIG_MILKYMIST) += milkymist-sysctl.o
|
||||
common-obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp-rtc.o
|
||||
common-obj-$(CONFIG_NRF51_SOC) += nrf51_timer.o
|
||||
|
||||
obj-$(CONFIG_ALTERA_TIMER) += altera_timer.o
|
||||
obj-$(CONFIG_EXYNOS4) += exynos4210_mct.o
|
||||
obj-$(CONFIG_EXYNOS4) += exynos4210_pwm.o
|
||||
obj-$(CONFIG_EXYNOS4) += exynos4210_rtc.o
|
||||
obj-$(CONFIG_OMAP) += omap_gptimer.o
|
||||
obj-$(CONFIG_OMAP) += omap_synctimer.o
|
||||
obj-$(CONFIG_PXA2XX) += pxa2xx_timer.o
|
||||
obj-$(CONFIG_SH4) += sh_timer.o
|
||||
obj-$(CONFIG_DIGIC) += digic-timer.o
|
||||
obj-$(CONFIG_MIPS_CPS) += mips_gictimer.o
|
||||
common-obj-$(CONFIG_ALTERA_TIMER) += altera_timer.o
|
||||
common-obj-$(CONFIG_EXYNOS4) += exynos4210_mct.o
|
||||
common-obj-$(CONFIG_EXYNOS4) += exynos4210_pwm.o
|
||||
common-obj-$(CONFIG_OMAP) += omap_gptimer.o
|
||||
common-obj-$(CONFIG_OMAP) += omap_synctimer.o
|
||||
common-obj-$(CONFIG_PXA2XX) += pxa2xx_timer.o
|
||||
common-obj-$(CONFIG_SH4) += sh_timer.o
|
||||
common-obj-$(CONFIG_DIGIC) += digic-timer.o
|
||||
common-obj-$(CONFIG_MIPS_CPS) += mips_gictimer.o
|
||||
|
||||
obj-$(CONFIG_MC146818RTC) += mc146818rtc.o
|
||||
|
||||
obj-$(CONFIG_ALLWINNER_A10_PIT) += allwinner-a10-pit.o
|
||||
common-obj-$(CONFIG_ALLWINNER_A10_PIT) += allwinner-a10-pit.o
|
||||
|
||||
common-obj-$(CONFIG_STM32F2XX_TIMER) += stm32f2xx_timer.o
|
||||
common-obj-$(CONFIG_ASPEED_SOC) += aspeed_timer.o aspeed_rtc.o
|
||||
common-obj-$(CONFIG_ASPEED_SOC) += aspeed_timer.o
|
||||
|
||||
common-obj-$(CONFIG_SUN4V_RTC) += sun4v-rtc.o
|
||||
common-obj-$(CONFIG_CMSDK_APB_TIMER) += cmsdk-apb-timer.o
|
||||
common-obj-$(CONFIG_CMSDK_APB_DUALTIMER) += cmsdk-apb-dualtimer.o
|
||||
common-obj-$(CONFIG_MSF2) += mss-timer.o
|
||||
|
|
|
@ -1,181 +0,0 @@
|
|||
/*
|
||||
* ASPEED Real Time Clock
|
||||
* Joel Stanley <joel@jms.id.au>
|
||||
*
|
||||
* Copyright 2019 IBM Corp
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "hw/timer/aspeed_rtc.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/timer.h"
|
||||
|
||||
#include "trace.h"
|
||||
|
||||
#define COUNTER1 (0x00 / 4)
|
||||
#define COUNTER2 (0x04 / 4)
|
||||
#define ALARM (0x08 / 4)
|
||||
#define CONTROL (0x10 / 4)
|
||||
#define ALARM_STATUS (0x14 / 4)
|
||||
|
||||
#define RTC_UNLOCKED BIT(1)
|
||||
#define RTC_ENABLED BIT(0)
|
||||
|
||||
static void aspeed_rtc_calc_offset(AspeedRtcState *rtc)
|
||||
{
|
||||
struct tm tm;
|
||||
uint32_t year, cent;
|
||||
uint32_t reg1 = rtc->reg[COUNTER1];
|
||||
uint32_t reg2 = rtc->reg[COUNTER2];
|
||||
|
||||
tm.tm_mday = (reg1 >> 24) & 0x1f;
|
||||
tm.tm_hour = (reg1 >> 16) & 0x1f;
|
||||
tm.tm_min = (reg1 >> 8) & 0x3f;
|
||||
tm.tm_sec = (reg1 >> 0) & 0x3f;
|
||||
|
||||
cent = (reg2 >> 16) & 0x1f;
|
||||
year = (reg2 >> 8) & 0x7f;
|
||||
tm.tm_mon = ((reg2 >> 0) & 0x0f) - 1;
|
||||
tm.tm_year = year + (cent * 100) - 1900;
|
||||
|
||||
rtc->offset = qemu_timedate_diff(&tm);
|
||||
}
|
||||
|
||||
static uint32_t aspeed_rtc_get_counter(AspeedRtcState *rtc, int r)
|
||||
{
|
||||
uint32_t year, cent;
|
||||
struct tm now;
|
||||
|
||||
qemu_get_timedate(&now, rtc->offset);
|
||||
|
||||
switch (r) {
|
||||
case COUNTER1:
|
||||
return (now.tm_mday << 24) | (now.tm_hour << 16) |
|
||||
(now.tm_min << 8) | now.tm_sec;
|
||||
case COUNTER2:
|
||||
cent = (now.tm_year + 1900) / 100;
|
||||
year = now.tm_year % 100;
|
||||
return ((cent & 0x1f) << 16) | ((year & 0x7f) << 8) |
|
||||
((now.tm_mon + 1) & 0xf);
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t aspeed_rtc_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
AspeedRtcState *rtc = opaque;
|
||||
uint64_t val;
|
||||
uint32_t r = addr >> 2;
|
||||
|
||||
switch (r) {
|
||||
case COUNTER1:
|
||||
case COUNTER2:
|
||||
if (rtc->reg[CONTROL] & RTC_ENABLED) {
|
||||
rtc->reg[r] = aspeed_rtc_get_counter(rtc, r);
|
||||
}
|
||||
/* fall through */
|
||||
case CONTROL:
|
||||
val = rtc->reg[r];
|
||||
break;
|
||||
case ALARM:
|
||||
case ALARM_STATUS:
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx "\n", __func__, addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
trace_aspeed_rtc_read(addr, val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void aspeed_rtc_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
AspeedRtcState *rtc = opaque;
|
||||
uint32_t r = addr >> 2;
|
||||
|
||||
switch (r) {
|
||||
case COUNTER1:
|
||||
case COUNTER2:
|
||||
if (!(rtc->reg[CONTROL] & RTC_UNLOCKED)) {
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
case CONTROL:
|
||||
rtc->reg[r] = val;
|
||||
aspeed_rtc_calc_offset(rtc);
|
||||
break;
|
||||
case ALARM:
|
||||
case ALARM_STATUS:
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx "\n", __func__, addr);
|
||||
break;
|
||||
}
|
||||
trace_aspeed_rtc_write(addr, val);
|
||||
}
|
||||
|
||||
static void aspeed_rtc_reset(DeviceState *d)
|
||||
{
|
||||
AspeedRtcState *rtc = ASPEED_RTC(d);
|
||||
|
||||
rtc->offset = 0;
|
||||
memset(rtc->reg, 0, sizeof(rtc->reg));
|
||||
}
|
||||
|
||||
static const MemoryRegionOps aspeed_rtc_ops = {
|
||||
.read = aspeed_rtc_read,
|
||||
.write = aspeed_rtc_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_aspeed_rtc = {
|
||||
.name = TYPE_ASPEED_RTC,
|
||||
.version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32_ARRAY(reg, AspeedRtcState, 0x18),
|
||||
VMSTATE_INT32(offset, AspeedRtcState),
|
||||
VMSTATE_INT32(offset, AspeedRtcState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void aspeed_rtc_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||
AspeedRtcState *s = ASPEED_RTC(dev);
|
||||
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_rtc_ops, s,
|
||||
"aspeed-rtc", 0x18ULL);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
}
|
||||
|
||||
static void aspeed_rtc_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = aspeed_rtc_realize;
|
||||
dc->vmsd = &vmstate_aspeed_rtc;
|
||||
dc->reset = aspeed_rtc_reset;
|
||||
}
|
||||
|
||||
static const TypeInfo aspeed_rtc_info = {
|
||||
.name = TYPE_ASPEED_RTC,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(AspeedRtcState),
|
||||
.class_init = aspeed_rtc_class_init,
|
||||
};
|
||||
|
||||
static void aspeed_rtc_register_types(void)
|
||||
{
|
||||
type_register_static(&aspeed_rtc_info);
|
||||
}
|
||||
|
||||
type_init(aspeed_rtc_register_types)
|
|
@ -1,241 +0,0 @@
|
|||
/*
|
||||
* 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 "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "hw/i2c/i2c.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "qemu/bcd.h"
|
||||
#include "qemu/module.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
|
||||
|
||||
#define TYPE_DS1338 "ds1338"
|
||||
#define DS1338(obj) OBJECT_CHECK(DS1338State, (obj), TYPE_DS1338)
|
||||
|
||||
typedef struct DS1338State {
|
||||
I2CSlave parent_obj;
|
||||
|
||||
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,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_I2C_SLAVE(parent_obj, 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 int ds1338_event(I2CSlave *i2c, enum i2c_event event)
|
||||
{
|
||||
DS1338State *s = DS1338(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;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t ds1338_recv(I2CSlave *i2c)
|
||||
{
|
||||
DS1338State *s = DS1338(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 = DS1338(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 void ds1338_reset(DeviceState *dev)
|
||||
{
|
||||
DS1338State *s = DS1338(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->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 = TYPE_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)
|
|
@ -1,608 +0,0 @@
|
|||
/*
|
||||
* Samsung exynos4210 Real Time Clock
|
||||
*
|
||||
* Copyright (c) 2012 Samsung Electronics Co., Ltd.
|
||||
* Ogurtsov Oleg <o.ogurtsov@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Description:
|
||||
* Register RTCCON:
|
||||
* CLKSEL Bit[1] not used
|
||||
* CLKOUTEN Bit[9] not used
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/module.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qemu/bcd.h"
|
||||
#include "hw/ptimer.h"
|
||||
|
||||
#include "hw/irq.h"
|
||||
|
||||
#include "hw/arm/exynos4210.h"
|
||||
|
||||
#define DEBUG_RTC 0
|
||||
|
||||
#if DEBUG_RTC
|
||||
#define DPRINTF(fmt, ...) \
|
||||
do { fprintf(stdout, "RTC: [%24s:%5d] " fmt, __func__, __LINE__, \
|
||||
## __VA_ARGS__); } while (0)
|
||||
#else
|
||||
#define DPRINTF(fmt, ...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#define EXYNOS4210_RTC_REG_MEM_SIZE 0x0100
|
||||
|
||||
#define INTP 0x0030
|
||||
#define RTCCON 0x0040
|
||||
#define TICCNT 0x0044
|
||||
#define RTCALM 0x0050
|
||||
#define ALMSEC 0x0054
|
||||
#define ALMMIN 0x0058
|
||||
#define ALMHOUR 0x005C
|
||||
#define ALMDAY 0x0060
|
||||
#define ALMMON 0x0064
|
||||
#define ALMYEAR 0x0068
|
||||
#define BCDSEC 0x0070
|
||||
#define BCDMIN 0x0074
|
||||
#define BCDHOUR 0x0078
|
||||
#define BCDDAY 0x007C
|
||||
#define BCDDAYWEEK 0x0080
|
||||
#define BCDMON 0x0084
|
||||
#define BCDYEAR 0x0088
|
||||
#define CURTICNT 0x0090
|
||||
|
||||
#define TICK_TIMER_ENABLE 0x0100
|
||||
#define TICNT_THRESHOLD 2
|
||||
|
||||
|
||||
#define RTC_ENABLE 0x0001
|
||||
|
||||
#define INTP_TICK_ENABLE 0x0001
|
||||
#define INTP_ALM_ENABLE 0x0002
|
||||
|
||||
#define ALARM_INT_ENABLE 0x0040
|
||||
|
||||
#define RTC_BASE_FREQ 32768
|
||||
|
||||
#define TYPE_EXYNOS4210_RTC "exynos4210.rtc"
|
||||
#define EXYNOS4210_RTC(obj) \
|
||||
OBJECT_CHECK(Exynos4210RTCState, (obj), TYPE_EXYNOS4210_RTC)
|
||||
|
||||
typedef struct Exynos4210RTCState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion iomem;
|
||||
|
||||
/* registers */
|
||||
uint32_t reg_intp;
|
||||
uint32_t reg_rtccon;
|
||||
uint32_t reg_ticcnt;
|
||||
uint32_t reg_rtcalm;
|
||||
uint32_t reg_almsec;
|
||||
uint32_t reg_almmin;
|
||||
uint32_t reg_almhour;
|
||||
uint32_t reg_almday;
|
||||
uint32_t reg_almmon;
|
||||
uint32_t reg_almyear;
|
||||
uint32_t reg_curticcnt;
|
||||
|
||||
ptimer_state *ptimer; /* tick timer */
|
||||
ptimer_state *ptimer_1Hz; /* clock timer */
|
||||
uint32_t freq;
|
||||
|
||||
qemu_irq tick_irq; /* Time Tick Generator irq */
|
||||
qemu_irq alm_irq; /* alarm irq */
|
||||
|
||||
struct tm current_tm; /* current time */
|
||||
} Exynos4210RTCState;
|
||||
|
||||
#define TICCKSEL(value) ((value & (0x0F << 4)) >> 4)
|
||||
|
||||
/*** VMState ***/
|
||||
static const VMStateDescription vmstate_exynos4210_rtc_state = {
|
||||
.name = "exynos4210.rtc",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(reg_intp, Exynos4210RTCState),
|
||||
VMSTATE_UINT32(reg_rtccon, Exynos4210RTCState),
|
||||
VMSTATE_UINT32(reg_ticcnt, Exynos4210RTCState),
|
||||
VMSTATE_UINT32(reg_rtcalm, Exynos4210RTCState),
|
||||
VMSTATE_UINT32(reg_almsec, Exynos4210RTCState),
|
||||
VMSTATE_UINT32(reg_almmin, Exynos4210RTCState),
|
||||
VMSTATE_UINT32(reg_almhour, Exynos4210RTCState),
|
||||
VMSTATE_UINT32(reg_almday, Exynos4210RTCState),
|
||||
VMSTATE_UINT32(reg_almmon, Exynos4210RTCState),
|
||||
VMSTATE_UINT32(reg_almyear, Exynos4210RTCState),
|
||||
VMSTATE_UINT32(reg_curticcnt, Exynos4210RTCState),
|
||||
VMSTATE_PTIMER(ptimer, Exynos4210RTCState),
|
||||
VMSTATE_PTIMER(ptimer_1Hz, Exynos4210RTCState),
|
||||
VMSTATE_UINT32(freq, Exynos4210RTCState),
|
||||
VMSTATE_INT32(current_tm.tm_sec, Exynos4210RTCState),
|
||||
VMSTATE_INT32(current_tm.tm_min, Exynos4210RTCState),
|
||||
VMSTATE_INT32(current_tm.tm_hour, Exynos4210RTCState),
|
||||
VMSTATE_INT32(current_tm.tm_wday, Exynos4210RTCState),
|
||||
VMSTATE_INT32(current_tm.tm_mday, Exynos4210RTCState),
|
||||
VMSTATE_INT32(current_tm.tm_mon, Exynos4210RTCState),
|
||||
VMSTATE_INT32(current_tm.tm_year, Exynos4210RTCState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
#define BCD3DIGITS(x) \
|
||||
((uint32_t)to_bcd((uint8_t)(x % 100)) + \
|
||||
((uint32_t)to_bcd((uint8_t)((x % 1000) / 100)) << 8))
|
||||
|
||||
static void check_alarm_raise(Exynos4210RTCState *s)
|
||||
{
|
||||
unsigned int alarm_raise = 0;
|
||||
struct tm stm = s->current_tm;
|
||||
|
||||
if ((s->reg_rtcalm & 0x01) &&
|
||||
(to_bcd((uint8_t)stm.tm_sec) == (uint8_t)s->reg_almsec)) {
|
||||
alarm_raise = 1;
|
||||
}
|
||||
if ((s->reg_rtcalm & 0x02) &&
|
||||
(to_bcd((uint8_t)stm.tm_min) == (uint8_t)s->reg_almmin)) {
|
||||
alarm_raise = 1;
|
||||
}
|
||||
if ((s->reg_rtcalm & 0x04) &&
|
||||
(to_bcd((uint8_t)stm.tm_hour) == (uint8_t)s->reg_almhour)) {
|
||||
alarm_raise = 1;
|
||||
}
|
||||
if ((s->reg_rtcalm & 0x08) &&
|
||||
(to_bcd((uint8_t)stm.tm_mday) == (uint8_t)s->reg_almday)) {
|
||||
alarm_raise = 1;
|
||||
}
|
||||
if ((s->reg_rtcalm & 0x10) &&
|
||||
(to_bcd((uint8_t)stm.tm_mon) == (uint8_t)s->reg_almmon)) {
|
||||
alarm_raise = 1;
|
||||
}
|
||||
if ((s->reg_rtcalm & 0x20) &&
|
||||
(BCD3DIGITS(stm.tm_year) == s->reg_almyear)) {
|
||||
alarm_raise = 1;
|
||||
}
|
||||
|
||||
if (alarm_raise) {
|
||||
DPRINTF("ALARM IRQ\n");
|
||||
/* set irq status */
|
||||
s->reg_intp |= INTP_ALM_ENABLE;
|
||||
qemu_irq_raise(s->alm_irq);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* RTC update frequency
|
||||
* Parameters:
|
||||
* reg_value - current RTCCON register or his new value
|
||||
* Must be called within a ptimer_transaction_begin/commit block for s->ptimer.
|
||||
*/
|
||||
static void exynos4210_rtc_update_freq(Exynos4210RTCState *s,
|
||||
uint32_t reg_value)
|
||||
{
|
||||
uint32_t freq;
|
||||
|
||||
freq = s->freq;
|
||||
/* set frequncy for time generator */
|
||||
s->freq = RTC_BASE_FREQ / (1 << TICCKSEL(reg_value));
|
||||
|
||||
if (freq != s->freq) {
|
||||
ptimer_set_freq(s->ptimer, s->freq);
|
||||
DPRINTF("freq=%dHz\n", s->freq);
|
||||
}
|
||||
}
|
||||
|
||||
/* month is between 0 and 11. */
|
||||
static int get_days_in_month(int month, int year)
|
||||
{
|
||||
static const int days_tab[12] = {
|
||||
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
||||
};
|
||||
int d;
|
||||
if ((unsigned)month >= 12) {
|
||||
return 31;
|
||||
}
|
||||
d = days_tab[month];
|
||||
if (month == 1) {
|
||||
if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)) {
|
||||
d++;
|
||||
}
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
/* update 'tm' to the next second */
|
||||
static void rtc_next_second(struct tm *tm)
|
||||
{
|
||||
int days_in_month;
|
||||
|
||||
tm->tm_sec++;
|
||||
if ((unsigned)tm->tm_sec >= 60) {
|
||||
tm->tm_sec = 0;
|
||||
tm->tm_min++;
|
||||
if ((unsigned)tm->tm_min >= 60) {
|
||||
tm->tm_min = 0;
|
||||
tm->tm_hour++;
|
||||
if ((unsigned)tm->tm_hour >= 24) {
|
||||
tm->tm_hour = 0;
|
||||
/* next day */
|
||||
tm->tm_wday++;
|
||||
if ((unsigned)tm->tm_wday >= 7) {
|
||||
tm->tm_wday = 0;
|
||||
}
|
||||
days_in_month = get_days_in_month(tm->tm_mon,
|
||||
tm->tm_year + 1900);
|
||||
tm->tm_mday++;
|
||||
if (tm->tm_mday < 1) {
|
||||
tm->tm_mday = 1;
|
||||
} else if (tm->tm_mday > days_in_month) {
|
||||
tm->tm_mday = 1;
|
||||
tm->tm_mon++;
|
||||
if (tm->tm_mon >= 12) {
|
||||
tm->tm_mon = 0;
|
||||
tm->tm_year++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* tick handler
|
||||
*/
|
||||
static void exynos4210_rtc_tick(void *opaque)
|
||||
{
|
||||
Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
|
||||
|
||||
DPRINTF("TICK IRQ\n");
|
||||
/* set irq status */
|
||||
s->reg_intp |= INTP_TICK_ENABLE;
|
||||
/* raise IRQ */
|
||||
qemu_irq_raise(s->tick_irq);
|
||||
|
||||
/* restart timer */
|
||||
ptimer_set_count(s->ptimer, s->reg_ticcnt);
|
||||
ptimer_run(s->ptimer, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* 1Hz clock handler
|
||||
*/
|
||||
static void exynos4210_rtc_1Hz_tick(void *opaque)
|
||||
{
|
||||
Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
|
||||
|
||||
rtc_next_second(&s->current_tm);
|
||||
/* DPRINTF("1Hz tick\n"); */
|
||||
|
||||
/* raise IRQ */
|
||||
if (s->reg_rtcalm & ALARM_INT_ENABLE) {
|
||||
check_alarm_raise(s);
|
||||
}
|
||||
|
||||
ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ);
|
||||
ptimer_run(s->ptimer_1Hz, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* RTC Read
|
||||
*/
|
||||
static uint64_t exynos4210_rtc_read(void *opaque, hwaddr offset,
|
||||
unsigned size)
|
||||
{
|
||||
uint32_t value = 0;
|
||||
Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
|
||||
|
||||
switch (offset) {
|
||||
case INTP:
|
||||
value = s->reg_intp;
|
||||
break;
|
||||
case RTCCON:
|
||||
value = s->reg_rtccon;
|
||||
break;
|
||||
case TICCNT:
|
||||
value = s->reg_ticcnt;
|
||||
break;
|
||||
case RTCALM:
|
||||
value = s->reg_rtcalm;
|
||||
break;
|
||||
case ALMSEC:
|
||||
value = s->reg_almsec;
|
||||
break;
|
||||
case ALMMIN:
|
||||
value = s->reg_almmin;
|
||||
break;
|
||||
case ALMHOUR:
|
||||
value = s->reg_almhour;
|
||||
break;
|
||||
case ALMDAY:
|
||||
value = s->reg_almday;
|
||||
break;
|
||||
case ALMMON:
|
||||
value = s->reg_almmon;
|
||||
break;
|
||||
case ALMYEAR:
|
||||
value = s->reg_almyear;
|
||||
break;
|
||||
|
||||
case BCDSEC:
|
||||
value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_sec);
|
||||
break;
|
||||
case BCDMIN:
|
||||
value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_min);
|
||||
break;
|
||||
case BCDHOUR:
|
||||
value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_hour);
|
||||
break;
|
||||
case BCDDAYWEEK:
|
||||
value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_wday);
|
||||
break;
|
||||
case BCDDAY:
|
||||
value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_mday);
|
||||
break;
|
||||
case BCDMON:
|
||||
value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_mon + 1);
|
||||
break;
|
||||
case BCDYEAR:
|
||||
value = BCD3DIGITS(s->current_tm.tm_year);
|
||||
break;
|
||||
|
||||
case CURTICNT:
|
||||
s->reg_curticcnt = ptimer_get_count(s->ptimer);
|
||||
value = s->reg_curticcnt;
|
||||
break;
|
||||
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"exynos4210.rtc: bad read offset " TARGET_FMT_plx,
|
||||
offset);
|
||||
break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/*
|
||||
* RTC Write
|
||||
*/
|
||||
static void exynos4210_rtc_write(void *opaque, hwaddr offset,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
|
||||
|
||||
switch (offset) {
|
||||
case INTP:
|
||||
if (value & INTP_ALM_ENABLE) {
|
||||
qemu_irq_lower(s->alm_irq);
|
||||
s->reg_intp &= (~INTP_ALM_ENABLE);
|
||||
}
|
||||
if (value & INTP_TICK_ENABLE) {
|
||||
qemu_irq_lower(s->tick_irq);
|
||||
s->reg_intp &= (~INTP_TICK_ENABLE);
|
||||
}
|
||||
break;
|
||||
case RTCCON:
|
||||
ptimer_transaction_begin(s->ptimer_1Hz);
|
||||
ptimer_transaction_begin(s->ptimer);
|
||||
if (value & RTC_ENABLE) {
|
||||
exynos4210_rtc_update_freq(s, value);
|
||||
}
|
||||
if ((value & RTC_ENABLE) > (s->reg_rtccon & RTC_ENABLE)) {
|
||||
/* clock timer */
|
||||
ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ);
|
||||
ptimer_run(s->ptimer_1Hz, 1);
|
||||
DPRINTF("run clock timer\n");
|
||||
}
|
||||
if ((value & RTC_ENABLE) < (s->reg_rtccon & RTC_ENABLE)) {
|
||||
/* tick timer */
|
||||
ptimer_stop(s->ptimer);
|
||||
/* clock timer */
|
||||
ptimer_stop(s->ptimer_1Hz);
|
||||
DPRINTF("stop all timers\n");
|
||||
}
|
||||
if (value & RTC_ENABLE) {
|
||||
if ((value & TICK_TIMER_ENABLE) >
|
||||
(s->reg_rtccon & TICK_TIMER_ENABLE) &&
|
||||
(s->reg_ticcnt)) {
|
||||
ptimer_set_count(s->ptimer, s->reg_ticcnt);
|
||||
ptimer_run(s->ptimer, 1);
|
||||
DPRINTF("run tick timer\n");
|
||||
}
|
||||
if ((value & TICK_TIMER_ENABLE) <
|
||||
(s->reg_rtccon & TICK_TIMER_ENABLE)) {
|
||||
ptimer_stop(s->ptimer);
|
||||
}
|
||||
}
|
||||
ptimer_transaction_commit(s->ptimer_1Hz);
|
||||
ptimer_transaction_commit(s->ptimer);
|
||||
s->reg_rtccon = value;
|
||||
break;
|
||||
case TICCNT:
|
||||
if (value > TICNT_THRESHOLD) {
|
||||
s->reg_ticcnt = value;
|
||||
} else {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"exynos4210.rtc: bad TICNT value %u",
|
||||
(uint32_t)value);
|
||||
}
|
||||
break;
|
||||
|
||||
case RTCALM:
|
||||
s->reg_rtcalm = value;
|
||||
break;
|
||||
case ALMSEC:
|
||||
s->reg_almsec = (value & 0x7f);
|
||||
break;
|
||||
case ALMMIN:
|
||||
s->reg_almmin = (value & 0x7f);
|
||||
break;
|
||||
case ALMHOUR:
|
||||
s->reg_almhour = (value & 0x3f);
|
||||
break;
|
||||
case ALMDAY:
|
||||
s->reg_almday = (value & 0x3f);
|
||||
break;
|
||||
case ALMMON:
|
||||
s->reg_almmon = (value & 0x1f);
|
||||
break;
|
||||
case ALMYEAR:
|
||||
s->reg_almyear = (value & 0x0fff);
|
||||
break;
|
||||
|
||||
case BCDSEC:
|
||||
if (s->reg_rtccon & RTC_ENABLE) {
|
||||
s->current_tm.tm_sec = (int)from_bcd((uint8_t)value);
|
||||
}
|
||||
break;
|
||||
case BCDMIN:
|
||||
if (s->reg_rtccon & RTC_ENABLE) {
|
||||
s->current_tm.tm_min = (int)from_bcd((uint8_t)value);
|
||||
}
|
||||
break;
|
||||
case BCDHOUR:
|
||||
if (s->reg_rtccon & RTC_ENABLE) {
|
||||
s->current_tm.tm_hour = (int)from_bcd((uint8_t)value);
|
||||
}
|
||||
break;
|
||||
case BCDDAYWEEK:
|
||||
if (s->reg_rtccon & RTC_ENABLE) {
|
||||
s->current_tm.tm_wday = (int)from_bcd((uint8_t)value);
|
||||
}
|
||||
break;
|
||||
case BCDDAY:
|
||||
if (s->reg_rtccon & RTC_ENABLE) {
|
||||
s->current_tm.tm_mday = (int)from_bcd((uint8_t)value);
|
||||
}
|
||||
break;
|
||||
case BCDMON:
|
||||
if (s->reg_rtccon & RTC_ENABLE) {
|
||||
s->current_tm.tm_mon = (int)from_bcd((uint8_t)value) - 1;
|
||||
}
|
||||
break;
|
||||
case BCDYEAR:
|
||||
if (s->reg_rtccon & RTC_ENABLE) {
|
||||
/* 3 digits */
|
||||
s->current_tm.tm_year = (int)from_bcd((uint8_t)value) +
|
||||
(int)from_bcd((uint8_t)((value >> 8) & 0x0f)) * 100;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"exynos4210.rtc: bad write offset " TARGET_FMT_plx,
|
||||
offset);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set default values to timer fields and registers
|
||||
*/
|
||||
static void exynos4210_rtc_reset(DeviceState *d)
|
||||
{
|
||||
Exynos4210RTCState *s = EXYNOS4210_RTC(d);
|
||||
|
||||
qemu_get_timedate(&s->current_tm, 0);
|
||||
|
||||
DPRINTF("Get time from host: %d-%d-%d %2d:%02d:%02d\n",
|
||||
s->current_tm.tm_year, s->current_tm.tm_mon, s->current_tm.tm_mday,
|
||||
s->current_tm.tm_hour, s->current_tm.tm_min, s->current_tm.tm_sec);
|
||||
|
||||
s->reg_intp = 0;
|
||||
s->reg_rtccon = 0;
|
||||
s->reg_ticcnt = 0;
|
||||
s->reg_rtcalm = 0;
|
||||
s->reg_almsec = 0;
|
||||
s->reg_almmin = 0;
|
||||
s->reg_almhour = 0;
|
||||
s->reg_almday = 0;
|
||||
s->reg_almmon = 0;
|
||||
s->reg_almyear = 0;
|
||||
|
||||
s->reg_curticcnt = 0;
|
||||
|
||||
ptimer_transaction_begin(s->ptimer);
|
||||
exynos4210_rtc_update_freq(s, s->reg_rtccon);
|
||||
ptimer_stop(s->ptimer);
|
||||
ptimer_transaction_commit(s->ptimer);
|
||||
ptimer_transaction_begin(s->ptimer_1Hz);
|
||||
ptimer_stop(s->ptimer_1Hz);
|
||||
ptimer_transaction_commit(s->ptimer_1Hz);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps exynos4210_rtc_ops = {
|
||||
.read = exynos4210_rtc_read,
|
||||
.write = exynos4210_rtc_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
/*
|
||||
* RTC timer initialization
|
||||
*/
|
||||
static void exynos4210_rtc_init(Object *obj)
|
||||
{
|
||||
Exynos4210RTCState *s = EXYNOS4210_RTC(obj);
|
||||
SysBusDevice *dev = SYS_BUS_DEVICE(obj);
|
||||
|
||||
s->ptimer = ptimer_init(exynos4210_rtc_tick, s, PTIMER_POLICY_DEFAULT);
|
||||
ptimer_transaction_begin(s->ptimer);
|
||||
ptimer_set_freq(s->ptimer, RTC_BASE_FREQ);
|
||||
exynos4210_rtc_update_freq(s, 0);
|
||||
ptimer_transaction_commit(s->ptimer);
|
||||
|
||||
s->ptimer_1Hz = ptimer_init(exynos4210_rtc_1Hz_tick,
|
||||
s, PTIMER_POLICY_DEFAULT);
|
||||
ptimer_transaction_begin(s->ptimer_1Hz);
|
||||
ptimer_set_freq(s->ptimer_1Hz, RTC_BASE_FREQ);
|
||||
ptimer_transaction_commit(s->ptimer_1Hz);
|
||||
|
||||
sysbus_init_irq(dev, &s->alm_irq);
|
||||
sysbus_init_irq(dev, &s->tick_irq);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &exynos4210_rtc_ops, s,
|
||||
"exynos4210-rtc", EXYNOS4210_RTC_REG_MEM_SIZE);
|
||||
sysbus_init_mmio(dev, &s->iomem);
|
||||
}
|
||||
|
||||
static void exynos4210_rtc_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->reset = exynos4210_rtc_reset;
|
||||
dc->vmsd = &vmstate_exynos4210_rtc_state;
|
||||
}
|
||||
|
||||
static const TypeInfo exynos4210_rtc_info = {
|
||||
.name = TYPE_EXYNOS4210_RTC,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(Exynos4210RTCState),
|
||||
.instance_init = exynos4210_rtc_init,
|
||||
.class_init = exynos4210_rtc_class_init,
|
||||
};
|
||||
|
||||
static void exynos4210_rtc_register_types(void)
|
||||
{
|
||||
type_register_static(&exynos4210_rtc_info);
|
||||
}
|
||||
|
||||
type_init(exynos4210_rtc_register_types)
|
|
@ -33,7 +33,8 @@
|
|||
#include "qemu/timer.h"
|
||||
#include "hw/timer/hpet.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/timer/mc146818rtc.h"
|
||||
#include "hw/rtc/mc146818rtc.h"
|
||||
#include "hw/rtc/mc146818rtc_regs.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "hw/timer/i8254.h"
|
||||
|
||||
|
|
|
@ -1,119 +0,0 @@
|
|||
/*
|
||||
* M41T80 serial rtc emulation
|
||||
*
|
||||
* Copyright (c) 2018 BALATON Zoltan
|
||||
*
|
||||
* This work is licensed under the GNU GPL license version 2 or later.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qemu/bcd.h"
|
||||
#include "hw/i2c/i2c.h"
|
||||
|
||||
#define TYPE_M41T80 "m41t80"
|
||||
#define M41T80(obj) OBJECT_CHECK(M41t80State, (obj), TYPE_M41T80)
|
||||
|
||||
typedef struct M41t80State {
|
||||
I2CSlave parent_obj;
|
||||
int8_t addr;
|
||||
} M41t80State;
|
||||
|
||||
static void m41t80_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
M41t80State *s = M41T80(dev);
|
||||
|
||||
s->addr = -1;
|
||||
}
|
||||
|
||||
static int m41t80_send(I2CSlave *i2c, uint8_t data)
|
||||
{
|
||||
M41t80State *s = M41T80(i2c);
|
||||
|
||||
if (s->addr < 0) {
|
||||
s->addr = data;
|
||||
} else {
|
||||
s->addr++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t m41t80_recv(I2CSlave *i2c)
|
||||
{
|
||||
M41t80State *s = M41T80(i2c);
|
||||
struct tm now;
|
||||
qemu_timeval tv;
|
||||
|
||||
if (s->addr < 0) {
|
||||
s->addr = 0;
|
||||
}
|
||||
if (s->addr >= 1 && s->addr <= 7) {
|
||||
qemu_get_timedate(&now, -1);
|
||||
}
|
||||
switch (s->addr++) {
|
||||
case 0:
|
||||
qemu_gettimeofday(&tv);
|
||||
return to_bcd(tv.tv_usec / 10000);
|
||||
case 1:
|
||||
return to_bcd(now.tm_sec);
|
||||
case 2:
|
||||
return to_bcd(now.tm_min);
|
||||
case 3:
|
||||
return to_bcd(now.tm_hour);
|
||||
case 4:
|
||||
return to_bcd(now.tm_wday);
|
||||
case 5:
|
||||
return to_bcd(now.tm_mday);
|
||||
case 6:
|
||||
return to_bcd(now.tm_mon + 1);
|
||||
case 7:
|
||||
return to_bcd(now.tm_year % 100);
|
||||
case 8 ... 19:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: unimplemented register: %d\n",
|
||||
__func__, s->addr - 1);
|
||||
return 0;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid register: %d\n",
|
||||
__func__, s->addr - 1);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int m41t80_event(I2CSlave *i2c, enum i2c_event event)
|
||||
{
|
||||
M41t80State *s = M41T80(i2c);
|
||||
|
||||
if (event == I2C_START_SEND) {
|
||||
s->addr = -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void m41t80_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass);
|
||||
|
||||
dc->realize = m41t80_realize;
|
||||
sc->send = m41t80_send;
|
||||
sc->recv = m41t80_recv;
|
||||
sc->event = m41t80_event;
|
||||
}
|
||||
|
||||
static const TypeInfo m41t80_info = {
|
||||
.name = TYPE_M41T80,
|
||||
.parent = TYPE_I2C_SLAVE,
|
||||
.instance_size = sizeof(M41t80State),
|
||||
.class_init = m41t80_class_init,
|
||||
};
|
||||
|
||||
static void m41t80_register_types(void)
|
||||
{
|
||||
type_register_static(&m41t80_info);
|
||||
}
|
||||
|
||||
type_init(m41t80_register_types)
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* QEMU M48T59 and M48T08 NVRAM emulation (common header)
|
||||
*
|
||||
* Copyright (c) 2003-2005, 2007 Jocelyn Mayer
|
||||
* Copyright (c) 2013 Hervé Poussineau
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef HW_M48T59_INTERNAL_H
|
||||
#define HW_M48T59_INTERNAL_H
|
||||
|
||||
#define M48T59_DEBUG 0
|
||||
|
||||
#define NVRAM_PRINTF(fmt, ...) do { \
|
||||
if (M48T59_DEBUG) { printf(fmt , ## __VA_ARGS__); } } while (0)
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
typedef struct M48txxInfo {
|
||||
const char *bus_name;
|
||||
uint32_t model; /* 2 = m48t02, 8 = m48t08, 59 = m48t59 */
|
||||
uint32_t size;
|
||||
} M48txxInfo;
|
||||
|
||||
typedef struct M48t59State {
|
||||
/* Hardware parameters */
|
||||
qemu_irq IRQ;
|
||||
MemoryRegion iomem;
|
||||
uint32_t size;
|
||||
int32_t base_year;
|
||||
/* RTC management */
|
||||
time_t time_offset;
|
||||
time_t stop_time;
|
||||
/* Alarm & watchdog */
|
||||
struct tm alarm;
|
||||
QEMUTimer *alrm_timer;
|
||||
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;
|
||||
} M48t59State;
|
||||
|
||||
uint32_t m48t59_read(M48t59State *NVRAM, uint32_t addr);
|
||||
void m48t59_write(M48t59State *NVRAM, uint32_t addr, uint32_t val);
|
||||
void m48t59_reset_common(M48t59State *NVRAM);
|
||||
void m48t59_realize_common(M48t59State *s, Error **errp);
|
||||
|
||||
static inline void m48t59_toggle_lock(M48t59State *NVRAM, int lock)
|
||||
{
|
||||
NVRAM->lock ^= 1 << lock;
|
||||
}
|
||||
|
||||
extern const MemoryRegionOps m48t59_io_ops;
|
||||
|
||||
#endif /* HW_M48T59_INTERNAL_H */
|
|
@ -1,184 +0,0 @@
|
|||
/*
|
||||
* QEMU M48T59 and M48T08 NVRAM emulation (ISA bus interface
|
||||
*
|
||||
* Copyright (c) 2003-2005, 2007 Jocelyn Mayer
|
||||
* Copyright (c) 2013 Hervé Poussineau
|
||||
*
|
||||
* 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 "qemu/osdep.h"
|
||||
#include "hw/isa/isa.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/timer/m48t59.h"
|
||||
#include "m48t59-internal.h"
|
||||
#include "qemu/module.h"
|
||||
|
||||
#define TYPE_M48TXX_ISA "isa-m48txx"
|
||||
#define M48TXX_ISA_GET_CLASS(obj) \
|
||||
OBJECT_GET_CLASS(M48txxISADeviceClass, (obj), TYPE_M48TXX_ISA)
|
||||
#define M48TXX_ISA_CLASS(klass) \
|
||||
OBJECT_CLASS_CHECK(M48txxISADeviceClass, (klass), TYPE_M48TXX_ISA)
|
||||
#define M48TXX_ISA(obj) \
|
||||
OBJECT_CHECK(M48txxISAState, (obj), TYPE_M48TXX_ISA)
|
||||
|
||||
typedef struct M48txxISAState {
|
||||
ISADevice parent_obj;
|
||||
M48t59State state;
|
||||
uint32_t io_base;
|
||||
MemoryRegion io;
|
||||
} M48txxISAState;
|
||||
|
||||
typedef struct M48txxISADeviceClass {
|
||||
ISADeviceClass parent_class;
|
||||
M48txxInfo info;
|
||||
} M48txxISADeviceClass;
|
||||
|
||||
static M48txxInfo m48txx_isa_info[] = {
|
||||
{
|
||||
.bus_name = "isa-m48t59",
|
||||
.model = 59,
|
||||
.size = 0x2000,
|
||||
}
|
||||
};
|
||||
|
||||
Nvram *m48t59_init_isa(ISABus *bus, uint32_t io_base, uint16_t size,
|
||||
int base_year, int model)
|
||||
{
|
||||
DeviceState *dev;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(m48txx_isa_info); i++) {
|
||||
if (m48txx_isa_info[i].size != size ||
|
||||
m48txx_isa_info[i].model != model) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dev = DEVICE(isa_create(bus, m48txx_isa_info[i].bus_name));
|
||||
qdev_prop_set_uint32(dev, "iobase", io_base);
|
||||
qdev_prop_set_int32(dev, "base-year", base_year);
|
||||
qdev_init_nofail(dev);
|
||||
return NVRAM(dev);
|
||||
}
|
||||
|
||||
assert(false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static uint32_t m48txx_isa_read(Nvram *obj, uint32_t addr)
|
||||
{
|
||||
M48txxISAState *d = M48TXX_ISA(obj);
|
||||
return m48t59_read(&d->state, addr);
|
||||
}
|
||||
|
||||
static void m48txx_isa_write(Nvram *obj, uint32_t addr, uint32_t val)
|
||||
{
|
||||
M48txxISAState *d = M48TXX_ISA(obj);
|
||||
m48t59_write(&d->state, addr, val);
|
||||
}
|
||||
|
||||
static void m48txx_isa_toggle_lock(Nvram *obj, int lock)
|
||||
{
|
||||
M48txxISAState *d = M48TXX_ISA(obj);
|
||||
m48t59_toggle_lock(&d->state, lock);
|
||||
}
|
||||
|
||||
static Property m48t59_isa_properties[] = {
|
||||
DEFINE_PROP_INT32("base-year", M48txxISAState, state.base_year, 0),
|
||||
DEFINE_PROP_UINT32("iobase", M48txxISAState, io_base, 0x74),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void m48t59_reset_isa(DeviceState *d)
|
||||
{
|
||||
M48txxISAState *isa = M48TXX_ISA(d);
|
||||
M48t59State *NVRAM = &isa->state;
|
||||
|
||||
m48t59_reset_common(NVRAM);
|
||||
}
|
||||
|
||||
static void m48t59_isa_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
M48txxISADeviceClass *u = M48TXX_ISA_GET_CLASS(dev);
|
||||
ISADevice *isadev = ISA_DEVICE(dev);
|
||||
M48txxISAState *d = M48TXX_ISA(dev);
|
||||
M48t59State *s = &d->state;
|
||||
|
||||
s->model = u->info.model;
|
||||
s->size = u->info.size;
|
||||
isa_init_irq(isadev, &s->IRQ, 8);
|
||||
m48t59_realize_common(s, errp);
|
||||
memory_region_init_io(&d->io, OBJECT(dev), &m48t59_io_ops, s, "m48t59", 4);
|
||||
if (d->io_base != 0) {
|
||||
isa_register_ioport(isadev, &d->io, d->io_base);
|
||||
}
|
||||
}
|
||||
|
||||
static void m48txx_isa_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
NvramClass *nc = NVRAM_CLASS(klass);
|
||||
|
||||
dc->realize = m48t59_isa_realize;
|
||||
dc->reset = m48t59_reset_isa;
|
||||
dc->props = m48t59_isa_properties;
|
||||
nc->read = m48txx_isa_read;
|
||||
nc->write = m48txx_isa_write;
|
||||
nc->toggle_lock = m48txx_isa_toggle_lock;
|
||||
}
|
||||
|
||||
static void m48txx_isa_concrete_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
M48txxISADeviceClass *u = M48TXX_ISA_CLASS(klass);
|
||||
M48txxInfo *info = data;
|
||||
|
||||
u->info = *info;
|
||||
}
|
||||
|
||||
static const TypeInfo m48txx_isa_type_info = {
|
||||
.name = TYPE_M48TXX_ISA,
|
||||
.parent = TYPE_ISA_DEVICE,
|
||||
.instance_size = sizeof(M48txxISAState),
|
||||
.abstract = true,
|
||||
.class_init = m48txx_isa_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_NVRAM },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
||||
static void m48t59_isa_register_types(void)
|
||||
{
|
||||
TypeInfo isa_type_info = {
|
||||
.parent = TYPE_M48TXX_ISA,
|
||||
.class_size = sizeof(M48txxISADeviceClass),
|
||||
.class_init = m48txx_isa_concrete_class_init,
|
||||
};
|
||||
int i;
|
||||
|
||||
type_register_static(&m48txx_isa_type_info);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(m48txx_isa_info); i++) {
|
||||
isa_type_info.name = m48txx_isa_info[i].bus_name;
|
||||
isa_type_info.class_data = &m48txx_isa_info[i];
|
||||
type_register(&isa_type_info);
|
||||
}
|
||||
}
|
||||
|
||||
type_init(m48t59_isa_register_types)
|
|
@ -1,723 +0,0 @@
|
|||
/*
|
||||
* QEMU M48T59 and M48T08 NVRAM emulation for PPC PREP and Sparc platforms
|
||||
*
|
||||
* Copyright (c) 2003-2005, 2007, 2017 Jocelyn Mayer
|
||||
* Copyright (c) 2013 Hervé Poussineau
|
||||
*
|
||||
* 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 "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/timer/m48t59.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "qemu/bcd.h"
|
||||
#include "qemu/module.h"
|
||||
|
||||
#include "m48t59-internal.h"
|
||||
#include "migration/vmstate.h"
|
||||
|
||||
#define TYPE_M48TXX_SYS_BUS "sysbus-m48txx"
|
||||
#define M48TXX_SYS_BUS_GET_CLASS(obj) \
|
||||
OBJECT_GET_CLASS(M48txxSysBusDeviceClass, (obj), TYPE_M48TXX_SYS_BUS)
|
||||
#define M48TXX_SYS_BUS_CLASS(klass) \
|
||||
OBJECT_CLASS_CHECK(M48txxSysBusDeviceClass, (klass), TYPE_M48TXX_SYS_BUS)
|
||||
#define M48TXX_SYS_BUS(obj) \
|
||||
OBJECT_CHECK(M48txxSysBusState, (obj), TYPE_M48TXX_SYS_BUS)
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
typedef struct M48txxSysBusState {
|
||||
SysBusDevice parent_obj;
|
||||
M48t59State state;
|
||||
MemoryRegion io;
|
||||
} M48txxSysBusState;
|
||||
|
||||
typedef struct M48txxSysBusDeviceClass {
|
||||
SysBusDeviceClass parent_class;
|
||||
M48txxInfo info;
|
||||
} M48txxSysBusDeviceClass;
|
||||
|
||||
static M48txxInfo m48txx_sysbus_info[] = {
|
||||
{
|
||||
.bus_name = "sysbus-m48t02",
|
||||
.model = 2,
|
||||
.size = 0x800,
|
||||
},{
|
||||
.bus_name = "sysbus-m48t08",
|
||||
.model = 8,
|
||||
.size = 0x2000,
|
||||
},{
|
||||
.bus_name = "sysbus-m48t59",
|
||||
.model = 59,
|
||||
.size = 0x2000,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* 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;
|
||||
}
|
||||
timer_mod(NVRAM->alrm_timer, qemu_clock_get_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) {
|
||||
timer_del(NVRAM->alrm_timer);
|
||||
diff = qemu_timedate_diff(&NVRAM->alarm) - NVRAM->time_offset;
|
||||
if (diff > 0)
|
||||
timer_mod(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(SHUTDOWN_CAUSE_GUEST_RESET);
|
||||
} 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) {
|
||||
timer_del(NVRAM->wd_timer);
|
||||
if (value != 0) {
|
||||
interval = (1 << (2 * (value & 0x03))) * ((value >> 2) & 0x1F);
|
||||
timer_mod(NVRAM->wd_timer, ((uint64_t)time(NULL) * 1000) +
|
||||
((interval * 1000) >> 4));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Direct access to NVRAM */
|
||||
void m48t59_write(M48t59State *NVRAM, uint32_t addr, uint32_t val)
|
||||
{
|
||||
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);
|
||||
tm.tm_year = from_bcd(val) + NVRAM->base_year - 1900;
|
||||
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(M48t59State *NVRAM, uint32_t addr)
|
||||
{
|
||||
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);
|
||||
retval = to_bcd((tm.tm_year + 1900 - NVRAM->base_year) % 100);
|
||||
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;
|
||||
}
|
||||
|
||||
/* IO access to NVRAM */
|
||||
static void NVRAM_writeb(void *opaque, hwaddr addr, uint64_t val,
|
||||
unsigned size)
|
||||
{
|
||||
M48t59State *NVRAM = opaque;
|
||||
|
||||
NVRAM_PRINTF("%s: 0x%"HWADDR_PRIx" => 0x%"PRIx64"\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%"HWADDR_PRIx" <= 0x%08x\n", __func__, addr, retval);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static uint64_t nvram_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
M48t59State *NVRAM = opaque;
|
||||
|
||||
return m48t59_read(NVRAM, addr);
|
||||
}
|
||||
|
||||
static void nvram_write(void *opaque, hwaddr addr, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
M48t59State *NVRAM = opaque;
|
||||
|
||||
return m48t59_write(NVRAM, addr, value);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps nvram_ops = {
|
||||
.read = nvram_read,
|
||||
.write = nvram_write,
|
||||
.impl.min_access_size = 1,
|
||||
.impl.max_access_size = 1,
|
||||
.valid.min_access_size = 1,
|
||||
.valid.max_access_size = 4,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_m48t59 = {
|
||||
.name = "m48t59",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT8(lock, M48t59State),
|
||||
VMSTATE_UINT16(addr, M48t59State),
|
||||
VMSTATE_VBUFFER_UINT32(buffer, M48t59State, 0, NULL, size),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
void m48t59_reset_common(M48t59State *NVRAM)
|
||||
{
|
||||
NVRAM->addr = 0;
|
||||
NVRAM->lock = 0;
|
||||
if (NVRAM->alrm_timer != NULL)
|
||||
timer_del(NVRAM->alrm_timer);
|
||||
|
||||
if (NVRAM->wd_timer != NULL)
|
||||
timer_del(NVRAM->wd_timer);
|
||||
}
|
||||
|
||||
static void m48t59_reset_sysbus(DeviceState *d)
|
||||
{
|
||||
M48txxSysBusState *sys = M48TXX_SYS_BUS(d);
|
||||
M48t59State *NVRAM = &sys->state;
|
||||
|
||||
m48t59_reset_common(NVRAM);
|
||||
}
|
||||
|
||||
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 */
|
||||
Nvram *m48t59_init(qemu_irq IRQ, hwaddr mem_base,
|
||||
uint32_t io_base, uint16_t size, int base_year,
|
||||
int model)
|
||||
{
|
||||
DeviceState *dev;
|
||||
SysBusDevice *s;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(m48txx_sysbus_info); i++) {
|
||||
if (m48txx_sysbus_info[i].size != size ||
|
||||
m48txx_sysbus_info[i].model != model) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dev = qdev_create(NULL, m48txx_sysbus_info[i].bus_name);
|
||||
qdev_prop_set_int32(dev, "base-year", base_year);
|
||||
qdev_init_nofail(dev);
|
||||
s = SYS_BUS_DEVICE(dev);
|
||||
sysbus_connect_irq(s, 0, IRQ);
|
||||
if (io_base != 0) {
|
||||
memory_region_add_subregion(get_system_io(), io_base,
|
||||
sysbus_mmio_get_region(s, 1));
|
||||
}
|
||||
if (mem_base != 0) {
|
||||
sysbus_mmio_map(s, 0, mem_base);
|
||||
}
|
||||
|
||||
return NVRAM(s);
|
||||
}
|
||||
|
||||
assert(false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void m48t59_realize_common(M48t59State *s, Error **errp)
|
||||
{
|
||||
s->buffer = g_malloc0(s->size);
|
||||
if (s->model == 59) {
|
||||
s->alrm_timer = timer_new_ns(rtc_clock, &alarm_cb, s);
|
||||
s->wd_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &watchdog_cb, s);
|
||||
}
|
||||
qemu_get_timedate(&s->alarm, 0);
|
||||
}
|
||||
|
||||
static void m48t59_init1(Object *obj)
|
||||
{
|
||||
M48txxSysBusDeviceClass *u = M48TXX_SYS_BUS_GET_CLASS(obj);
|
||||
M48txxSysBusState *d = M48TXX_SYS_BUS(obj);
|
||||
SysBusDevice *dev = SYS_BUS_DEVICE(obj);
|
||||
M48t59State *s = &d->state;
|
||||
|
||||
s->model = u->info.model;
|
||||
s->size = u->info.size;
|
||||
sysbus_init_irq(dev, &s->IRQ);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &nvram_ops, s, "m48t59.nvram",
|
||||
s->size);
|
||||
memory_region_init_io(&d->io, obj, &m48t59_io_ops, s, "m48t59", 4);
|
||||
}
|
||||
|
||||
static void m48t59_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
M48txxSysBusState *d = M48TXX_SYS_BUS(dev);
|
||||
M48t59State *s = &d->state;
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
sysbus_init_mmio(sbd, &d->io);
|
||||
m48t59_realize_common(s, errp);
|
||||
}
|
||||
|
||||
static uint32_t m48txx_sysbus_read(Nvram *obj, uint32_t addr)
|
||||
{
|
||||
M48txxSysBusState *d = M48TXX_SYS_BUS(obj);
|
||||
return m48t59_read(&d->state, addr);
|
||||
}
|
||||
|
||||
static void m48txx_sysbus_write(Nvram *obj, uint32_t addr, uint32_t val)
|
||||
{
|
||||
M48txxSysBusState *d = M48TXX_SYS_BUS(obj);
|
||||
m48t59_write(&d->state, addr, val);
|
||||
}
|
||||
|
||||
static void m48txx_sysbus_toggle_lock(Nvram *obj, int lock)
|
||||
{
|
||||
M48txxSysBusState *d = M48TXX_SYS_BUS(obj);
|
||||
m48t59_toggle_lock(&d->state, lock);
|
||||
}
|
||||
|
||||
static Property m48t59_sysbus_properties[] = {
|
||||
DEFINE_PROP_INT32("base-year", M48txxSysBusState, state.base_year, 0),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void m48txx_sysbus_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
NvramClass *nc = NVRAM_CLASS(klass);
|
||||
|
||||
dc->realize = m48t59_realize;
|
||||
dc->reset = m48t59_reset_sysbus;
|
||||
dc->props = m48t59_sysbus_properties;
|
||||
dc->vmsd = &vmstate_m48t59;
|
||||
nc->read = m48txx_sysbus_read;
|
||||
nc->write = m48txx_sysbus_write;
|
||||
nc->toggle_lock = m48txx_sysbus_toggle_lock;
|
||||
}
|
||||
|
||||
static void m48txx_sysbus_concrete_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
M48txxSysBusDeviceClass *u = M48TXX_SYS_BUS_CLASS(klass);
|
||||
M48txxInfo *info = data;
|
||||
|
||||
u->info = *info;
|
||||
}
|
||||
|
||||
static const TypeInfo nvram_info = {
|
||||
.name = TYPE_NVRAM,
|
||||
.parent = TYPE_INTERFACE,
|
||||
.class_size = sizeof(NvramClass),
|
||||
};
|
||||
|
||||
static const TypeInfo m48txx_sysbus_type_info = {
|
||||
.name = TYPE_M48TXX_SYS_BUS,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(M48txxSysBusState),
|
||||
.instance_init = m48t59_init1,
|
||||
.abstract = true,
|
||||
.class_init = m48txx_sysbus_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_NVRAM },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
||||
static void m48t59_register_types(void)
|
||||
{
|
||||
TypeInfo sysbus_type_info = {
|
||||
.parent = TYPE_M48TXX_SYS_BUS,
|
||||
.class_size = sizeof(M48txxSysBusDeviceClass),
|
||||
.class_init = m48txx_sysbus_concrete_class_init,
|
||||
};
|
||||
int i;
|
||||
|
||||
type_register_static(&nvram_info);
|
||||
type_register_static(&m48txx_sysbus_type_info);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(m48txx_sysbus_info); i++) {
|
||||
sysbus_type_info.name = m48txx_sysbus_info[i].bus_name;
|
||||
sysbus_type_info.class_data = &m48txx_sysbus_info[i];
|
||||
type_register(&sysbus_type_info);
|
||||
}
|
||||
}
|
||||
|
||||
type_init(m48t59_register_types)
|
File diff suppressed because it is too large
Load diff
344
hw/timer/pl031.c
344
hw/timer/pl031.c
|
@ -1,344 +0,0 @@
|
|||
/*
|
||||
* 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 "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "hw/timer/pl031.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/module.h"
|
||||
#include "trace.h"
|
||||
|
||||
#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 */
|
||||
|
||||
static const unsigned char pl031_id[] = {
|
||||
0x31, 0x10, 0x14, 0x00, /* Device ID */
|
||||
0x0d, 0xf0, 0x05, 0xb1 /* Cell ID */
|
||||
};
|
||||
|
||||
static void pl031_update(PL031State *s)
|
||||
{
|
||||
uint32_t flags = s->is & s->im;
|
||||
|
||||
trace_pl031_irq_state(flags);
|
||||
qemu_set_irq(s->irq, flags);
|
||||
}
|
||||
|
||||
static void pl031_interrupt(void * opaque)
|
||||
{
|
||||
PL031State *s = (PL031State *)opaque;
|
||||
|
||||
s->is = 1;
|
||||
trace_pl031_alarm_raised();
|
||||
pl031_update(s);
|
||||
}
|
||||
|
||||
static uint32_t pl031_get_count(PL031State *s)
|
||||
{
|
||||
int64_t now = qemu_clock_get_ns(rtc_clock);
|
||||
return s->tick_offset + now / NANOSECONDS_PER_SECOND;
|
||||
}
|
||||
|
||||
static void pl031_set_alarm(PL031State *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);
|
||||
trace_pl031_set_alarm(ticks);
|
||||
if (ticks == 0) {
|
||||
timer_del(s->timer);
|
||||
pl031_interrupt(s);
|
||||
} else {
|
||||
int64_t now = qemu_clock_get_ns(rtc_clock);
|
||||
timer_mod(s->timer, now + (int64_t)ticks * NANOSECONDS_PER_SECOND);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t pl031_read(void *opaque, hwaddr offset,
|
||||
unsigned size)
|
||||
{
|
||||
PL031State *s = (PL031State *)opaque;
|
||||
uint64_t r;
|
||||
|
||||
switch (offset) {
|
||||
case RTC_DR:
|
||||
r = pl031_get_count(s);
|
||||
break;
|
||||
case RTC_MR:
|
||||
r = s->mr;
|
||||
break;
|
||||
case RTC_IMSC:
|
||||
r = s->im;
|
||||
break;
|
||||
case RTC_RIS:
|
||||
r = s->is;
|
||||
break;
|
||||
case RTC_LR:
|
||||
r = s->lr;
|
||||
break;
|
||||
case RTC_CR:
|
||||
/* RTC is permanently enabled. */
|
||||
r = 1;
|
||||
break;
|
||||
case RTC_MIS:
|
||||
r = s->is & s->im;
|
||||
break;
|
||||
case 0xfe0 ... 0xfff:
|
||||
r = pl031_id[(offset - 0xfe0) >> 2];
|
||||
break;
|
||||
case RTC_ICR:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"pl031: read of write-only register at offset 0x%x\n",
|
||||
(int)offset);
|
||||
r = 0;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"pl031_read: Bad offset 0x%x\n", (int)offset);
|
||||
r = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
trace_pl031_read(offset, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void pl031_write(void * opaque, hwaddr offset,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
PL031State *s = (PL031State *)opaque;
|
||||
|
||||
trace_pl031_write(offset, value);
|
||||
|
||||
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;
|
||||
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. */
|
||||
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 void pl031_init(Object *obj)
|
||||
{
|
||||
PL031State *s = PL031(obj);
|
||||
SysBusDevice *dev = SYS_BUS_DEVICE(obj);
|
||||
struct tm tm;
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &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_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND;
|
||||
|
||||
s->timer = timer_new_ns(rtc_clock, pl031_interrupt, s);
|
||||
}
|
||||
|
||||
static int pl031_pre_save(void *opaque)
|
||||
{
|
||||
PL031State *s = opaque;
|
||||
|
||||
/*
|
||||
* The PL031 device model code uses the tick_offset field, which is
|
||||
* the offset between what the guest RTC should read and what the
|
||||
* QEMU rtc_clock reads:
|
||||
* guest_rtc = rtc_clock + tick_offset
|
||||
* and so
|
||||
* tick_offset = guest_rtc - rtc_clock
|
||||
*
|
||||
* We want to migrate this offset, which sounds straightforward.
|
||||
* Unfortunately older versions of QEMU migrated a conversion of this
|
||||
* offset into an offset from the vm_clock. (This was in turn an
|
||||
* attempt to be compatible with even older QEMU versions, but it
|
||||
* has incorrect behaviour if the rtc_clock is not the same as the
|
||||
* vm_clock.) So we put the actual tick_offset into a migration
|
||||
* subsection, and the backwards-compatible time-relative-to-vm_clock
|
||||
* in the main migration state.
|
||||
*
|
||||
* Calculate base time relative to QEMU_CLOCK_VIRTUAL:
|
||||
*/
|
||||
int64_t delta = qemu_clock_get_ns(rtc_clock) - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
s->tick_offset_vmstate = s->tick_offset + delta / NANOSECONDS_PER_SECOND;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pl031_pre_load(void *opaque)
|
||||
{
|
||||
PL031State *s = opaque;
|
||||
|
||||
s->tick_offset_migrated = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pl031_post_load(void *opaque, int version_id)
|
||||
{
|
||||
PL031State *s = opaque;
|
||||
|
||||
/*
|
||||
* If we got the tick_offset subsection, then we can just use
|
||||
* the value in that. Otherwise the source is an older QEMU and
|
||||
* has given us the offset from the vm_clock; convert it back to
|
||||
* an offset from the rtc_clock. This will cause time to incorrectly
|
||||
* go backwards compared to the host RTC, but this is unavoidable.
|
||||
*/
|
||||
|
||||
if (!s->tick_offset_migrated) {
|
||||
int64_t delta = qemu_clock_get_ns(rtc_clock) -
|
||||
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
s->tick_offset = s->tick_offset_vmstate -
|
||||
delta / NANOSECONDS_PER_SECOND;
|
||||
}
|
||||
pl031_set_alarm(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pl031_tick_offset_post_load(void *opaque, int version_id)
|
||||
{
|
||||
PL031State *s = opaque;
|
||||
|
||||
s->tick_offset_migrated = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool pl031_tick_offset_needed(void *opaque)
|
||||
{
|
||||
PL031State *s = opaque;
|
||||
|
||||
return s->migrate_tick_offset;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_pl031_tick_offset = {
|
||||
.name = "pl031/tick-offset",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = pl031_tick_offset_needed,
|
||||
.post_load = pl031_tick_offset_post_load,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(tick_offset, PL031State),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_pl031 = {
|
||||
.name = "pl031",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.pre_save = pl031_pre_save,
|
||||
.pre_load = pl031_pre_load,
|
||||
.post_load = pl031_post_load,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(tick_offset_vmstate, PL031State),
|
||||
VMSTATE_UINT32(mr, PL031State),
|
||||
VMSTATE_UINT32(lr, PL031State),
|
||||
VMSTATE_UINT32(cr, PL031State),
|
||||
VMSTATE_UINT32(im, PL031State),
|
||||
VMSTATE_UINT32(is, PL031State),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmstate_pl031_tick_offset,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
static Property pl031_properties[] = {
|
||||
/*
|
||||
* True to correctly migrate the tick offset of the RTC. False to
|
||||
* obtain backward migration compatibility with older QEMU versions,
|
||||
* at the expense of the guest RTC going backwards compared with the
|
||||
* host RTC when the VM is saved/restored if using -rtc host.
|
||||
* (Even if set to 'true' older QEMU can migrate forward to newer QEMU;
|
||||
* 'false' also permits newer QEMU to migrate to older QEMU.)
|
||||
*/
|
||||
DEFINE_PROP_BOOL("migrate-tick-offset",
|
||||
PL031State, migrate_tick_offset, true),
|
||||
DEFINE_PROP_END_OF_LIST()
|
||||
};
|
||||
|
||||
static void pl031_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->vmsd = &vmstate_pl031;
|
||||
dc->props = pl031_properties;
|
||||
}
|
||||
|
||||
static const TypeInfo pl031_info = {
|
||||
.name = TYPE_PL031,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(PL031State),
|
||||
.instance_init = pl031_init,
|
||||
.class_init = pl031_class_init,
|
||||
};
|
||||
|
||||
static void pl031_register_types(void)
|
||||
{
|
||||
type_register_static(&pl031_info);
|
||||
}
|
||||
|
||||
type_init(pl031_register_types)
|
|
@ -1,95 +0,0 @@
|
|||
/*
|
||||
* QEMU sun4v Real Time Clock device
|
||||
*
|
||||
* The sun4v_rtc device (sun4v tod clock)
|
||||
*
|
||||
* Copyright (c) 2016 Artyom Tarasenko
|
||||
*
|
||||
* This code is licensed under the GNU GPL v3 or (at your option) any later
|
||||
* version.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/timer/sun4v-rtc.h"
|
||||
#include "trace.h"
|
||||
|
||||
|
||||
#define TYPE_SUN4V_RTC "sun4v_rtc"
|
||||
#define SUN4V_RTC(obj) OBJECT_CHECK(Sun4vRtc, (obj), TYPE_SUN4V_RTC)
|
||||
|
||||
typedef struct Sun4vRtc {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion iomem;
|
||||
} Sun4vRtc;
|
||||
|
||||
static uint64_t sun4v_rtc_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
uint64_t val = get_clock_realtime() / NANOSECONDS_PER_SECOND;
|
||||
if (!(addr & 4ULL)) {
|
||||
/* accessing the high 32 bits */
|
||||
val >>= 32;
|
||||
}
|
||||
trace_sun4v_rtc_read(addr, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
static void sun4v_rtc_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
trace_sun4v_rtc_write(addr, val);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps sun4v_rtc_ops = {
|
||||
.read = sun4v_rtc_read,
|
||||
.write = sun4v_rtc_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
void sun4v_rtc_init(hwaddr addr)
|
||||
{
|
||||
DeviceState *dev;
|
||||
SysBusDevice *s;
|
||||
|
||||
dev = qdev_create(NULL, TYPE_SUN4V_RTC);
|
||||
s = SYS_BUS_DEVICE(dev);
|
||||
|
||||
qdev_init_nofail(dev);
|
||||
|
||||
sysbus_mmio_map(s, 0, addr);
|
||||
}
|
||||
|
||||
static void sun4v_rtc_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||
Sun4vRtc *s = SUN4V_RTC(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &sun4v_rtc_ops, s,
|
||||
"sun4v-rtc", 0x08ULL);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
}
|
||||
|
||||
static void sun4v_rtc_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = sun4v_rtc_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo sun4v_rtc_info = {
|
||||
.name = TYPE_SUN4V_RTC,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(Sun4vRtc),
|
||||
.class_init = sun4v_rtc_class_init,
|
||||
};
|
||||
|
||||
static void sun4v_rtc_register_types(void)
|
||||
{
|
||||
type_register_static(&sun4v_rtc_info);
|
||||
}
|
||||
|
||||
type_init(sun4v_rtc_register_types)
|
|
@ -66,28 +66,10 @@ cmsdk_apb_dualtimer_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK A
|
|||
cmsdk_apb_dualtimer_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB dualtimer write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
|
||||
cmsdk_apb_dualtimer_reset(void) "CMSDK APB dualtimer: reset"
|
||||
|
||||
# hw/timer/aspeed-rtc.c
|
||||
aspeed_rtc_read(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64
|
||||
aspeed_rtc_write(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64
|
||||
|
||||
# sun4v-rtc.c
|
||||
sun4v_rtc_read(uint64_t addr, uint64_t value) "read: addr 0x%" PRIx64 " value 0x%" PRIx64
|
||||
sun4v_rtc_write(uint64_t addr, uint64_t value) "write: addr 0x%" PRIx64 " value 0x%" PRIx64
|
||||
|
||||
# xlnx-zynqmp-rtc.c
|
||||
xlnx_zynqmp_rtc_gettime(int year, int month, int day, int hour, int min, int sec) "Get time from host: %d-%d-%d %2d:%02d:%02d"
|
||||
|
||||
# nrf51_timer.c
|
||||
nrf51_timer_read(uint64_t addr, uint32_t value, unsigned size) "read addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u"
|
||||
nrf51_timer_write(uint64_t addr, uint32_t value, unsigned size) "write addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u"
|
||||
|
||||
# pl031.c
|
||||
pl031_irq_state(int level) "irq state %d"
|
||||
pl031_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x"
|
||||
pl031_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x"
|
||||
pl031_alarm_raised(void) "alarm raised"
|
||||
pl031_set_alarm(uint32_t ticks) "alarm set for %u ticks"
|
||||
|
||||
# bcm2835_systmr.c
|
||||
bcm2835_systmr_irq(bool enable) "timer irq state %u"
|
||||
bcm2835_systmr_read(uint64_t offset, uint64_t data) "timer read: offset 0x%" PRIx64 " data 0x%" PRIx64
|
||||
|
|
|
@ -1,898 +0,0 @@
|
|||
/*
|
||||
* 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 "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/i2c/i2c.h"
|
||||
#include "hw/irq.h"
|
||||
#include "migration/qemu-file-types.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "ui/console.h"
|
||||
#include "qemu/bcd.h"
|
||||
#include "qemu/module.h"
|
||||
|
||||
#define VERBOSE 1
|
||||
|
||||
#define TYPE_TWL92230 "twl92230"
|
||||
#define TWL92230(obj) OBJECT_CHECK(MenelausState, (obj), TYPE_TWL92230)
|
||||
|
||||
typedef struct MenelausState {
|
||||
I2CSlave parent_obj;
|
||||
|
||||
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_clock_get_ms(rtc_clock);
|
||||
timer_mod(s->rtc.hz_tm, s->rtc.next);
|
||||
}
|
||||
|
||||
static inline void menelaus_rtc_stop(MenelausState *s)
|
||||
{
|
||||
timer_del(s->rtc.hz_tm);
|
||||
s->rtc.next -= qemu_clock_get_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;
|
||||
timer_mod(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 = TWL92230(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", __func__, 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(I2C_SLAVE(s));
|
||||
}
|
||||
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",
|
||||
__func__, 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", __func__, addr);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static int menelaus_event(I2CSlave *i2c, enum i2c_event event)
|
||||
{
|
||||
MenelausState *s = TWL92230(i2c);
|
||||
|
||||
if (event == I2C_START_SEND)
|
||||
s->firstbyte = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int menelaus_tx(I2CSlave *i2c, uint8_t data)
|
||||
{
|
||||
MenelausState *s = TWL92230(i2c);
|
||||
|
||||
/* Interpret register address byte */
|
||||
if (s->firstbyte) {
|
||||
s->reg = data;
|
||||
s->firstbyte = 0;
|
||||
} else
|
||||
menelaus_write(s, s->reg ++, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t menelaus_rx(I2CSlave *i2c)
|
||||
{
|
||||
MenelausState *s = TWL92230(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,
|
||||
const VMStateField *field)
|
||||
{
|
||||
int *v = pv;
|
||||
*v = qemu_get_be16(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int put_int32_as_uint16(QEMUFile *f, void *pv, size_t size,
|
||||
const VMStateField *field, QJSON *vmdesc)
|
||||
{
|
||||
int *v = pv;
|
||||
qemu_put_be16(f, *v);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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,
|
||||
.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 int menelaus_pre_save(void *opaque)
|
||||
{
|
||||
MenelausState *s = opaque;
|
||||
/* Should be <= 1000 */
|
||||
s->rtc_next_vmstate = s->rtc.next - qemu_clock_get_ms(rtc_clock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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,
|
||||
.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(parent_obj, MenelausState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void twl92230_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
MenelausState *s = TWL92230(dev);
|
||||
|
||||
s->rtc.hz_tm = timer_new_ms(rtc_clock, menelaus_rtc_hz, s);
|
||||
/* Three output pins plus one interrupt pin. */
|
||||
qdev_init_gpio_out(dev, s->out, 4);
|
||||
|
||||
/* Three input pins plus one power-button pin. */
|
||||
qdev_init_gpio_in(dev, menelaus_gpio_set, 4);
|
||||
|
||||
menelaus_reset(I2C_SLAVE(dev));
|
||||
}
|
||||
|
||||
static void twl92230_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass);
|
||||
|
||||
dc->realize = twl92230_realize;
|
||||
sc->event = menelaus_event;
|
||||
sc->recv = menelaus_rx;
|
||||
sc->send = menelaus_tx;
|
||||
dc->vmsd = &vmstate_menelaus;
|
||||
}
|
||||
|
||||
static const TypeInfo twl92230_info = {
|
||||
.name = TYPE_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)
|
|
@ -1,276 +0,0 @@
|
|||
/*
|
||||
* QEMU model of the Xilinx ZynqMP Real Time Clock (RTC).
|
||||
*
|
||||
* Copyright (c) 2017 Xilinx Inc.
|
||||
*
|
||||
* Written-by: Alistair Francis <alistair.francis@xilinx.com>
|
||||
*
|
||||
* 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 "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/register.h"
|
||||
#include "qemu/bitops.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/module.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/ptimer.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "trace.h"
|
||||
#include "hw/timer/xlnx-zynqmp-rtc.h"
|
||||
#include "migration/vmstate.h"
|
||||
|
||||
#ifndef XLNX_ZYNQMP_RTC_ERR_DEBUG
|
||||
#define XLNX_ZYNQMP_RTC_ERR_DEBUG 0
|
||||
#endif
|
||||
|
||||
static void rtc_int_update_irq(XlnxZynqMPRTC *s)
|
||||
{
|
||||
bool pending = s->regs[R_RTC_INT_STATUS] & ~s->regs[R_RTC_INT_MASK];
|
||||
qemu_set_irq(s->irq_rtc_int, pending);
|
||||
}
|
||||
|
||||
static void addr_error_int_update_irq(XlnxZynqMPRTC *s)
|
||||
{
|
||||
bool pending = s->regs[R_ADDR_ERROR] & ~s->regs[R_ADDR_ERROR_INT_MASK];
|
||||
qemu_set_irq(s->irq_addr_error_int, pending);
|
||||
}
|
||||
|
||||
static uint32_t rtc_get_count(XlnxZynqMPRTC *s)
|
||||
{
|
||||
int64_t now = qemu_clock_get_ns(rtc_clock);
|
||||
return s->tick_offset + now / NANOSECONDS_PER_SECOND;
|
||||
}
|
||||
|
||||
static uint64_t current_time_postr(RegisterInfo *reg, uint64_t val64)
|
||||
{
|
||||
XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
|
||||
|
||||
return rtc_get_count(s);
|
||||
}
|
||||
|
||||
static void rtc_int_status_postw(RegisterInfo *reg, uint64_t val64)
|
||||
{
|
||||
XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
|
||||
rtc_int_update_irq(s);
|
||||
}
|
||||
|
||||
static uint64_t rtc_int_en_prew(RegisterInfo *reg, uint64_t val64)
|
||||
{
|
||||
XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
|
||||
|
||||
s->regs[R_RTC_INT_MASK] &= (uint32_t) ~val64;
|
||||
rtc_int_update_irq(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint64_t rtc_int_dis_prew(RegisterInfo *reg, uint64_t val64)
|
||||
{
|
||||
XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
|
||||
|
||||
s->regs[R_RTC_INT_MASK] |= (uint32_t) val64;
|
||||
rtc_int_update_irq(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void addr_error_postw(RegisterInfo *reg, uint64_t val64)
|
||||
{
|
||||
XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
|
||||
addr_error_int_update_irq(s);
|
||||
}
|
||||
|
||||
static uint64_t addr_error_int_en_prew(RegisterInfo *reg, uint64_t val64)
|
||||
{
|
||||
XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
|
||||
|
||||
s->regs[R_ADDR_ERROR_INT_MASK] &= (uint32_t) ~val64;
|
||||
addr_error_int_update_irq(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint64_t addr_error_int_dis_prew(RegisterInfo *reg, uint64_t val64)
|
||||
{
|
||||
XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
|
||||
|
||||
s->regs[R_ADDR_ERROR_INT_MASK] |= (uint32_t) val64;
|
||||
addr_error_int_update_irq(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const RegisterAccessInfo rtc_regs_info[] = {
|
||||
{ .name = "SET_TIME_WRITE", .addr = A_SET_TIME_WRITE,
|
||||
.unimp = MAKE_64BIT_MASK(0, 32),
|
||||
},{ .name = "SET_TIME_READ", .addr = A_SET_TIME_READ,
|
||||
.ro = 0xffffffff,
|
||||
.post_read = current_time_postr,
|
||||
},{ .name = "CALIB_WRITE", .addr = A_CALIB_WRITE,
|
||||
.unimp = MAKE_64BIT_MASK(0, 32),
|
||||
},{ .name = "CALIB_READ", .addr = A_CALIB_READ,
|
||||
.ro = 0x1fffff,
|
||||
},{ .name = "CURRENT_TIME", .addr = A_CURRENT_TIME,
|
||||
.ro = 0xffffffff,
|
||||
.post_read = current_time_postr,
|
||||
},{ .name = "CURRENT_TICK", .addr = A_CURRENT_TICK,
|
||||
.ro = 0xffff,
|
||||
},{ .name = "ALARM", .addr = A_ALARM,
|
||||
},{ .name = "RTC_INT_STATUS", .addr = A_RTC_INT_STATUS,
|
||||
.w1c = 0x3,
|
||||
.post_write = rtc_int_status_postw,
|
||||
},{ .name = "RTC_INT_MASK", .addr = A_RTC_INT_MASK,
|
||||
.reset = 0x3,
|
||||
.ro = 0x3,
|
||||
},{ .name = "RTC_INT_EN", .addr = A_RTC_INT_EN,
|
||||
.pre_write = rtc_int_en_prew,
|
||||
},{ .name = "RTC_INT_DIS", .addr = A_RTC_INT_DIS,
|
||||
.pre_write = rtc_int_dis_prew,
|
||||
},{ .name = "ADDR_ERROR", .addr = A_ADDR_ERROR,
|
||||
.w1c = 0x1,
|
||||
.post_write = addr_error_postw,
|
||||
},{ .name = "ADDR_ERROR_INT_MASK", .addr = A_ADDR_ERROR_INT_MASK,
|
||||
.reset = 0x1,
|
||||
.ro = 0x1,
|
||||
},{ .name = "ADDR_ERROR_INT_EN", .addr = A_ADDR_ERROR_INT_EN,
|
||||
.pre_write = addr_error_int_en_prew,
|
||||
},{ .name = "ADDR_ERROR_INT_DIS", .addr = A_ADDR_ERROR_INT_DIS,
|
||||
.pre_write = addr_error_int_dis_prew,
|
||||
},{ .name = "CONTROL", .addr = A_CONTROL,
|
||||
.reset = 0x1000000,
|
||||
.rsvd = 0x70fffffe,
|
||||
},{ .name = "SAFETY_CHK", .addr = A_SAFETY_CHK,
|
||||
}
|
||||
};
|
||||
|
||||
static void rtc_reset(DeviceState *dev)
|
||||
{
|
||||
XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(dev);
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
|
||||
register_reset(&s->regs_info[i]);
|
||||
}
|
||||
|
||||
rtc_int_update_irq(s);
|
||||
addr_error_int_update_irq(s);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps rtc_ops = {
|
||||
.read = register_read_memory,
|
||||
.write = register_write_memory,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
};
|
||||
|
||||
static void rtc_init(Object *obj)
|
||||
{
|
||||
XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
RegisterInfoArray *reg_array;
|
||||
struct tm current_tm;
|
||||
|
||||
memory_region_init(&s->iomem, obj, TYPE_XLNX_ZYNQMP_RTC,
|
||||
XLNX_ZYNQMP_RTC_R_MAX * 4);
|
||||
reg_array =
|
||||
register_init_block32(DEVICE(obj), rtc_regs_info,
|
||||
ARRAY_SIZE(rtc_regs_info),
|
||||
s->regs_info, s->regs,
|
||||
&rtc_ops,
|
||||
XLNX_ZYNQMP_RTC_ERR_DEBUG,
|
||||
XLNX_ZYNQMP_RTC_R_MAX * 4);
|
||||
memory_region_add_subregion(&s->iomem,
|
||||
0x0,
|
||||
®_array->mem);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
sysbus_init_irq(sbd, &s->irq_rtc_int);
|
||||
sysbus_init_irq(sbd, &s->irq_addr_error_int);
|
||||
|
||||
qemu_get_timedate(¤t_tm, 0);
|
||||
s->tick_offset = mktimegm(¤t_tm) -
|
||||
qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND;
|
||||
|
||||
trace_xlnx_zynqmp_rtc_gettime(current_tm.tm_year, current_tm.tm_mon,
|
||||
current_tm.tm_mday, current_tm.tm_hour,
|
||||
current_tm.tm_min, current_tm.tm_sec);
|
||||
}
|
||||
|
||||
static int rtc_pre_save(void *opaque)
|
||||
{
|
||||
XlnxZynqMPRTC *s = opaque;
|
||||
int64_t now = qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND;
|
||||
|
||||
/* Add the time at migration */
|
||||
s->tick_offset = s->tick_offset + now;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtc_post_load(void *opaque, int version_id)
|
||||
{
|
||||
XlnxZynqMPRTC *s = opaque;
|
||||
int64_t now = qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND;
|
||||
|
||||
/* Subtract the time after migration. This combined with the pre_save
|
||||
* action results in us having subtracted the time that the guest was
|
||||
* stopped to the offset.
|
||||
*/
|
||||
s->tick_offset = s->tick_offset - now;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_rtc = {
|
||||
.name = TYPE_XLNX_ZYNQMP_RTC,
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.pre_save = rtc_pre_save,
|
||||
.post_load = rtc_post_load,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPRTC, XLNX_ZYNQMP_RTC_R_MAX),
|
||||
VMSTATE_UINT32(tick_offset, XlnxZynqMPRTC),
|
||||
VMSTATE_END_OF_LIST(),
|
||||
}
|
||||
};
|
||||
|
||||
static void rtc_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->reset = rtc_reset;
|
||||
dc->vmsd = &vmstate_rtc;
|
||||
}
|
||||
|
||||
static const TypeInfo rtc_info = {
|
||||
.name = TYPE_XLNX_ZYNQMP_RTC,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(XlnxZynqMPRTC),
|
||||
.class_init = rtc_class_init,
|
||||
.instance_init = rtc_init,
|
||||
};
|
||||
|
||||
static void rtc_register_types(void)
|
||||
{
|
||||
type_register_static(&rtc_info);
|
||||
}
|
||||
|
||||
type_init(rtc_register_types)
|
Loading…
Add table
Add a link
Reference in a new issue