[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH v3 12/17] hw/arm/allwinner: add RTC device support
From: |
Philippe Mathieu-Daudé |
Subject: |
Re: [PATCH v3 12/17] hw/arm/allwinner: add RTC device support |
Date: |
Sat, 18 Jan 2020 16:05:29 +0100 |
User-agent: |
Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Thunderbird/68.2.2 |
Hi Niek,
On 1/14/20 11:57 PM, Niek Linnenbank wrote:
On Tue, Jan 14, 2020 at 11:52 PM Niek Linnenbank
<address@hidden <mailto:address@hidden>> wrote:
Hi Philippe,
On Mon, Jan 13, 2020 at 11:57 PM Philippe Mathieu-Daudé
<address@hidden <mailto:address@hidden>> wrote:
On 1/8/20 9:00 PM, Niek Linnenbank wrote:
> Allwinner System-on-Chips usually contain a Real Time Clock (RTC)
> for non-volatile system date and time keeping. This commit
adds a generic
> Allwinner RTC device that supports the RTC devices found in
Allwinner SoC
> family sun4i (A10), sun7i (A20) and sun6i and newer (A31,
H2+, H3, etc).
[...]
> +static uint64_t allwinner_rtc_read(void *opaque, hwaddr offset,
> + unsigned size)
> +{
> + AwRtcState *s = AW_RTC(opaque);
> + const AwRtcClass *c = AW_RTC_GET_CLASS(s);
> + uint64_t val = 0;
> +
> + if (offset >= AW_RTC_REGS_MAXADDR) {
> + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds
offset 0x%04x\n",
> + __func__, (uint32_t)offset);
> + return 0;
> + }
> +
> + if (!c->regmap[offset]) {
> + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid
register 0x%04x\n",
> + __func__, (uint32_t)offset);
> + return 0;
> + }
> +
> + switch (c->regmap[offset]) {
> + case REG_LOSC: /* Low Oscillator Control */
> + val = s->regs[REG_LOSC];
> + s->regs[REG_LOSC] &= ~(REG_LOSC_YMD | REG_LOSC_HMS);
> + break;
> + case REG_YYMMDD: /* RTC Year-Month-Day */
> + case REG_HHMMSS: /* RTC Hour-Minute-Second */
> + case REG_GP0: /* General Purpose Register 0 */
> + case REG_GP1: /* General Purpose Register 1 */
> + case REG_GP2: /* General Purpose Register 2 */
> + case REG_GP3: /* General Purpose Register 3 */
> + val = s->regs[c->regmap[offset]];
> + break;
> + default:
> + if (!c->read(s, offset)) {
> + qemu_log_mask(LOG_UNIMP, "%s: unimplemented
register 0x%04x\n",
> + __func__, (uint32_t)offset);
> + }
> + val = s->regs[c->regmap[offset]];
> + break;
> + }
> +
> + trace_allwinner_rtc_read(offset, val);
> + return val;
> +}
> +
> +static void allwinner_rtc_write(void *opaque, hwaddr offset,
> + uint64_t val, unsigned size)
> +{
> + AwRtcState *s = AW_RTC(opaque);
> + const AwRtcClass *c = AW_RTC_GET_CLASS(s);
> +
> + if (offset >= AW_RTC_REGS_MAXADDR) {
> + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds
offset 0x%04x\n",
> + __func__, (uint32_t)offset);
> + return;
> + }
> +
> + if (!c->regmap[offset]) {
> + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid
register 0x%04x\n",
> + __func__, (uint32_t)offset);
> + return;
> + }
> +
> + trace_allwinner_rtc_write(offset, val);
> +
> + switch (c->regmap[offset]) {
> + case REG_YYMMDD: /* RTC Year-Month-Day */
> + s->regs[REG_YYMMDD] = val;
> + s->regs[REG_LOSC] |= REG_LOSC_YMD;
> + break;
> + case REG_HHMMSS: /* RTC Hour-Minute-Second */
> + s->regs[REG_HHMMSS] = val;
> + s->regs[REG_LOSC] |= REG_LOSC_HMS;
> + break;
> + case REG_GP0: /* General Purpose Register 0 */
> + case REG_GP1: /* General Purpose Register 1 */
> + case REG_GP2: /* General Purpose Register 2 */
> + case REG_GP3: /* General Purpose Register 3 */
> + s->regs[c->regmap[offset]] = val;
> + break;
> + default:
> + if (!c->write(s, offset, val)) {
> + qemu_log_mask(LOG_UNIMP, "%s: unimplemented
register 0x%04x\n",
> + __func__, (uint32_t)offset);
> + }
> + break;
> + }
> +}
> +
> +static const MemoryRegionOps allwinner_rtc_ops = {
> + .read = allwinner_rtc_read,
> + .write = allwinner_rtc_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> + .valid = {
> + .min_access_size = 4,
> + .max_access_size = 4,
> + },
> + .impl.min_access_size = 4,
> +};
> +
> +static void allwinner_rtc_reset(DeviceState *dev)
> +{
> + AwRtcState *s = AW_RTC(dev);
> + const AwRtcClass *c = AW_RTC_GET_CLASS(dev);
> + struct tm now;
> +
> + /* Clear registers */
> + memset(s->regs, 0, sizeof(s->regs));
> +
> + /* Get current datetime */
> + qemu_get_timedate(&now, 0);
> +
> + /* Set RTC with current datetime */
> + s->regs[REG_YYMMDD] = ((now.tm_year - c->year_offset)
<< 16) |
> + ((now.tm_mon + 1) << 8) |
> + now.tm_mday;
> + s->regs[REG_HHMMSS] = (((now.tm_wday + 6) % 7) << 29) |
> + (now.tm_hour << 16) |
> + (now.tm_min << 8) |
> + now.tm_sec;
This doesn't look correct.
From H3 Datasheet (Rev1.2):
4.8.3.4. RTC YY-MM-DD Register (Default Value: 0x00000000)
4.8.3.5. RTC HH-MM-SS Register (Default Value: 0x00000000)
I don't yet fully understand what you mean. Could you please explain
a bit more what should be changed?
I was thinking about:
- Start a VM on Monday at 12:34
- Pause the VM on Tuesday at 12:34
rtc_time=Tuesday,12:34
- [Eventually savevm/migrate/loadvm]
rtc_time=Tuesday,12:34
- Resume the VM on Wednesday 12:34
(time should be Tuesday at 12:34)
so rtc_time=Tuesday,12:34
- Check time on Thursday at 12:33
(time should be Wednesday at 12:33)
rtc_time=Wednesday,12:34
- Reset the VM on Thursday at 12:34
(time was Wednesday at 12:34)
- Check time on Thursday at 12:35
(time should be Wednesday at 12:35)
However due to reset() calling qemu_get_timedate(), the rtc_time will be
Thursday at 12:35 instead of Wednesday.
I don't know how the hardware keep these 2*32-bit safe when powered off.
Laurent Vivier posted a patch where he uses a block backend for his
machine NVRAM (used by RTC). This seems to me the clever way to do this:
https://www.mail-archive.com/address@hidden/msg666837.html
I'd use a block of 8 bytes for your RTC.
If we start a machine without rtc block backend, the 2*32-bit are
initialized as zero as the datasheet. If we provide a block, the machine
will power-on from that stored time.
You might want to look at the global -rtc command line option:
-rtc
[base=utc|localtime|datetime][,clock=host|rt|vm][,driftfix=none|slew]
Specify base as "utc" or "localtime" to let the RTC start
at the current UTC or local time, respectively. "localtime"
is required for correct date in MS-DOS or Windows. To start
at a specific point in time, provide datetime in the format
"2006-06-17T16:01:21" or "2006-06-17". The default base is
UTC.
But it might be specific to the MC146818 RTC.
For filling the YYMMDD and HHMMSS register fields, I simply looked
at the 4.8.3.4 and 4.8.3.5 sections
and filled it with the time retrieved from qemu_get_timedate. The
shifts are done so the values are set in the proper bits.
If I read it with the hwclock -r command under Linux, this reads out OK.
On NetBSD, this works as well, except for the base year mismatch
which I explained above.
I'm not sure what is the proper to model this, maybe set this
value in
init()? If we suspend a machine, migrate it, and resume it, what
RTC are
we expecting?
I forgot to reply on this one.
I have not used migration very often, but I did manage to test it a
couple of times
using the 'migrate' command on the Qemu monitor console before I
submitted each
new version of the patch series. My expectation would be that the RTC
time is suspended
along with all the other state of the machine such as memory, I/O
devices etc. So that would mean
the time is 'frozen' until resumed. I think that is what we currently
have here.
Do you think that is correct or should it work differently?
Yes, this is the behavior I'd expect. Maybe other would disagree.
- Re: [PATCH v3 17/17] docs: add Orange Pi PC document, (continued)
[PATCH v3 09/17] hw/arm/allwinner-h3: add EMAC ethernet device, Niek Linnenbank, 2020/01/08