4021
/* Real-time Clock module */
4023
target_phys_addr_t base;
4035
struct tm *(*convert)(const time_t *timep, struct tm *result);
4039
struct tm current_tm;
4044
static void omap_rtc_interrupts_update(struct omap_rtc_s *s)
4046
qemu_set_irq(s->alarm, (s->status >> 6) & 1);
4049
static void omap_rtc_alarm_update(struct omap_rtc_s *s)
4051
s->alarm_ti = mktime(&s->alarm_tm);
4052
if (s->alarm_ti == -1)
4053
printf("%s: conversion failed\n", __FUNCTION__);
4056
static inline uint8_t omap_rtc_bcd(int num)
4058
return ((num / 10) << 4) | (num % 10);
4061
static inline int omap_rtc_bin(uint8_t num)
4063
return (num & 15) + 10 * (num >> 4);
4066
static uint32_t omap_rtc_read(void *opaque, target_phys_addr_t addr)
4068
struct omap_rtc_s *s = (struct omap_rtc_s *) opaque;
4069
int offset = addr - s->base;
4073
case 0x00: /* SECONDS_REG */
4074
return omap_rtc_bcd(s->current_tm.tm_sec);
4076
case 0x04: /* MINUTES_REG */
4077
return omap_rtc_bcd(s->current_tm.tm_min);
4079
case 0x08: /* HOURS_REG */
4081
return ((s->current_tm.tm_hour > 11) << 7) |
4082
omap_rtc_bcd(((s->current_tm.tm_hour - 1) % 12) + 1);
4084
return omap_rtc_bcd(s->current_tm.tm_hour);
4086
case 0x0c: /* DAYS_REG */
4087
return omap_rtc_bcd(s->current_tm.tm_mday);
4089
case 0x10: /* MONTHS_REG */
4090
return omap_rtc_bcd(s->current_tm.tm_mon + 1);
4092
case 0x14: /* YEARS_REG */
4093
return omap_rtc_bcd(s->current_tm.tm_year % 100);
4095
case 0x18: /* WEEK_REG */
4096
return s->current_tm.tm_wday;
4098
case 0x20: /* ALARM_SECONDS_REG */
4099
return omap_rtc_bcd(s->alarm_tm.tm_sec);
4101
case 0x24: /* ALARM_MINUTES_REG */
4102
return omap_rtc_bcd(s->alarm_tm.tm_min);
4104
case 0x28: /* ALARM_HOURS_REG */
4106
return ((s->alarm_tm.tm_hour > 11) << 7) |
4107
omap_rtc_bcd(((s->alarm_tm.tm_hour - 1) % 12) + 1);
4109
return omap_rtc_bcd(s->alarm_tm.tm_hour);
4111
case 0x2c: /* ALARM_DAYS_REG */
4112
return omap_rtc_bcd(s->alarm_tm.tm_mday);
4114
case 0x30: /* ALARM_MONTHS_REG */
4115
return omap_rtc_bcd(s->alarm_tm.tm_mon + 1);
4117
case 0x34: /* ALARM_YEARS_REG */
4118
return omap_rtc_bcd(s->alarm_tm.tm_year % 100);
4120
case 0x40: /* RTC_CTRL_REG */
4121
return (s->pm_am << 3) | (s->auto_comp << 2) |
4122
(s->round << 1) | s->running;
4124
case 0x44: /* RTC_STATUS_REG */
4129
case 0x48: /* RTC_INTERRUPTS_REG */
4130
return s->interrupts;
4132
case 0x4c: /* RTC_COMP_LSB_REG */
4133
return ((uint16_t) s->comp_reg) & 0xff;
4135
case 0x50: /* RTC_COMP_MSB_REG */
4136
return ((uint16_t) s->comp_reg) >> 8;
4143
static void omap_rtc_write(void *opaque, target_phys_addr_t addr,
4146
struct omap_rtc_s *s = (struct omap_rtc_s *) opaque;
4147
int offset = addr - s->base;
4152
case 0x00: /* SECONDS_REG */
4154
printf("RTC SEC_REG <-- %02x\n", value);
4156
s->ti -= s->current_tm.tm_sec;
4157
s->ti += omap_rtc_bin(value);
4160
case 0x04: /* MINUTES_REG */
4162
printf("RTC MIN_REG <-- %02x\n", value);
4164
s->ti -= s->current_tm.tm_min * 60;
4165
s->ti += omap_rtc_bin(value) * 60;
4168
case 0x08: /* HOURS_REG */
4170
printf("RTC HRS_REG <-- %02x\n", value);
4172
s->ti -= s->current_tm.tm_hour * 3600;
4174
s->ti += (omap_rtc_bin(value & 0x3f) & 12) * 3600;
4175
s->ti += ((value >> 7) & 1) * 43200;
4177
s->ti += omap_rtc_bin(value & 0x3f) * 3600;
4180
case 0x0c: /* DAYS_REG */
4182
printf("RTC DAY_REG <-- %02x\n", value);
4184
s->ti -= s->current_tm.tm_mday * 86400;
4185
s->ti += omap_rtc_bin(value) * 86400;
4188
case 0x10: /* MONTHS_REG */
4190
printf("RTC MTH_REG <-- %02x\n", value);
4192
memcpy(&new_tm, &s->current_tm, sizeof(new_tm));
4193
new_tm.tm_mon = omap_rtc_bin(value);
4194
ti[0] = mktime(&s->current_tm);
4195
ti[1] = mktime(&new_tm);
4197
if (ti[0] != -1 && ti[1] != -1) {
4201
/* A less accurate version */
4202
s->ti -= s->current_tm.tm_mon * 2592000;
4203
s->ti += omap_rtc_bin(value) * 2592000;
4207
case 0x14: /* YEARS_REG */
4209
printf("RTC YRS_REG <-- %02x\n", value);
4211
memcpy(&new_tm, &s->current_tm, sizeof(new_tm));
4212
new_tm.tm_year += omap_rtc_bin(value) - (new_tm.tm_year % 100);
4213
ti[0] = mktime(&s->current_tm);
4214
ti[1] = mktime(&new_tm);
4216
if (ti[0] != -1 && ti[1] != -1) {
4220
/* A less accurate version */
4221
s->ti -= (s->current_tm.tm_year % 100) * 31536000;
4222
s->ti += omap_rtc_bin(value) * 31536000;
4226
case 0x18: /* WEEK_REG */
4227
return; /* Ignored */
4229
case 0x20: /* ALARM_SECONDS_REG */
4231
printf("ALM SEC_REG <-- %02x\n", value);
4233
s->alarm_tm.tm_sec = omap_rtc_bin(value);
4234
omap_rtc_alarm_update(s);
4237
case 0x24: /* ALARM_MINUTES_REG */
4239
printf("ALM MIN_REG <-- %02x\n", value);
4241
s->alarm_tm.tm_min = omap_rtc_bin(value);
4242
omap_rtc_alarm_update(s);
4245
case 0x28: /* ALARM_HOURS_REG */
4247
printf("ALM HRS_REG <-- %02x\n", value);
4250
s->alarm_tm.tm_hour =
4251
((omap_rtc_bin(value & 0x3f)) % 12) +
4252
((value >> 7) & 1) * 12;
4254
s->alarm_tm.tm_hour = omap_rtc_bin(value);
4255
omap_rtc_alarm_update(s);
4258
case 0x2c: /* ALARM_DAYS_REG */
4260
printf("ALM DAY_REG <-- %02x\n", value);
4262
s->alarm_tm.tm_mday = omap_rtc_bin(value);
4263
omap_rtc_alarm_update(s);
4266
case 0x30: /* ALARM_MONTHS_REG */
4268
printf("ALM MON_REG <-- %02x\n", value);
4270
s->alarm_tm.tm_mon = omap_rtc_bin(value);
4271
omap_rtc_alarm_update(s);
4274
case 0x34: /* ALARM_YEARS_REG */
4276
printf("ALM YRS_REG <-- %02x\n", value);
4278
s->alarm_tm.tm_year = omap_rtc_bin(value);
4279
omap_rtc_alarm_update(s);
4282
case 0x40: /* RTC_CTRL_REG */
4284
printf("RTC CONTROL <-- %02x\n", value);
4286
s->pm_am = (value >> 3) & 1;
4287
s->auto_comp = (value >> 2) & 1;
4288
s->round = (value >> 1) & 1;
4289
s->running = value & 1;
4291
s->status |= s->running << 1;
4294
case 0x44: /* RTC_STATUS_REG */
4296
printf("RTC STATUSL <-- %02x\n", value);
4298
s->status &= ~((value & 0xc0) ^ 0x80);
4299
omap_rtc_interrupts_update(s);
4302
case 0x48: /* RTC_INTERRUPTS_REG */
4304
printf("RTC INTRS <-- %02x\n", value);
4306
s->interrupts = value;
4309
case 0x4c: /* RTC_COMP_LSB_REG */
4311
printf("RTC COMPLSB <-- %02x\n", value);
4313
s->comp_reg &= 0xff00;
4314
s->comp_reg |= 0x00ff & value;
4317
case 0x50: /* RTC_COMP_MSB_REG */
4319
printf("RTC COMPMSB <-- %02x\n", value);
4321
s->comp_reg &= 0x00ff;
4322
s->comp_reg |= 0xff00 & (value << 8);
4331
static CPUReadMemoryFunc *omap_rtc_readfn[] = {
4333
omap_badwidth_read8,
4334
omap_badwidth_read8,
4337
static CPUWriteMemoryFunc *omap_rtc_writefn[] = {
4339
omap_badwidth_write8,
4340
omap_badwidth_write8,
4343
static void omap_rtc_tick(void *opaque)
4345
struct omap_rtc_s *s = opaque;
4348
/* Round to nearest full minute. */
4349
if (s->current_tm.tm_sec < 30)
4350
s->ti -= s->current_tm.tm_sec;
4352
s->ti += 60 - s->current_tm.tm_sec;
4357
localtime_r(&s->ti, &s->current_tm);
4359
if ((s->interrupts & 0x08) && s->ti == s->alarm_ti) {
4361
omap_rtc_interrupts_update(s);
4364
if (s->interrupts & 0x04)
4365
switch (s->interrupts & 3) {
4368
qemu_irq_raise(s->irq);
4371
if (s->current_tm.tm_sec)
4374
qemu_irq_raise(s->irq);
4377
if (s->current_tm.tm_sec || s->current_tm.tm_min)
4380
qemu_irq_raise(s->irq);
4383
if (s->current_tm.tm_sec ||
4384
s->current_tm.tm_min || s->current_tm.tm_hour)
4387
qemu_irq_raise(s->irq);
4397
* Every full hour add a rough approximation of the compensation
4398
* register to the 32kHz Timer (which drives the RTC) value.
4400
if (s->auto_comp && !s->current_tm.tm_sec && !s->current_tm.tm_min)
4401
s->tick += s->comp_reg * 1000 / 32768;
4403
qemu_mod_timer(s->clk, s->tick);
4406
void omap_rtc_reset(struct omap_rtc_s *s)
4414
s->tick = qemu_get_clock(rt_clock);
4415
memset(&s->alarm_tm, 0, sizeof(s->alarm_tm));
4416
s->alarm_tm.tm_mday = 0x01;
4419
s->ti = mktime(s->convert(&s->ti, &s->current_tm));
4421
omap_rtc_alarm_update(s);
4425
struct omap_rtc_s *omap_rtc_init(target_phys_addr_t base,
4426
qemu_irq *irq, omap_clk clk)
4429
struct omap_rtc_s *s = (struct omap_rtc_s *)
4430
qemu_mallocz(sizeof(struct omap_rtc_s));
4435
s->clk = qemu_new_timer(rt_clock, omap_rtc_tick, s);
4436
s->convert = rtc_utc ? gmtime_r : localtime_r;
4440
iomemtype = cpu_register_io_memory(0, omap_rtc_readfn,
4441
omap_rtc_writefn, s);
4442
cpu_register_physical_memory(s->base, 0x800, iomemtype);
4021
4447
/* General chip reset */
4022
4448
static void omap_mpu_reset(void *opaque)