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:
Peter Maydell 2019-10-25 14:17:08 +01:00
commit bad76ac319
56 changed files with 213 additions and 165 deletions

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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"

View file

@ -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)

View file

@ -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 */

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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,
&reg_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(&current_tm, 0);
s->tick_offset = mktimegm(&current_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)