4018 |
4018 |
return s->bus;
|
4019 |
4019 |
}
|
4020 |
4020 |
|
|
4021 |
/* Real-time Clock module */
|
|
4022 |
struct omap_rtc_s {
|
|
4023 |
target_phys_addr_t base;
|
|
4024 |
qemu_irq irq;
|
|
4025 |
qemu_irq alarm;
|
|
4026 |
QEMUTimer *clk;
|
|
4027 |
|
|
4028 |
uint8_t interrupts;
|
|
4029 |
uint8_t status;
|
|
4030 |
int16_t comp_reg;
|
|
4031 |
int running;
|
|
4032 |
int pm_am;
|
|
4033 |
int auto_comp;
|
|
4034 |
int round;
|
|
4035 |
struct tm *(*convert)(const time_t *timep, struct tm *result);
|
|
4036 |
struct tm alarm_tm;
|
|
4037 |
time_t alarm_ti;
|
|
4038 |
|
|
4039 |
struct tm current_tm;
|
|
4040 |
time_t ti;
|
|
4041 |
uint64_t tick;
|
|
4042 |
};
|
|
4043 |
|
|
4044 |
static void omap_rtc_interrupts_update(struct omap_rtc_s *s)
|
|
4045 |
{
|
|
4046 |
qemu_set_irq(s->alarm, (s->status >> 6) & 1);
|
|
4047 |
}
|
|
4048 |
|
|
4049 |
static void omap_rtc_alarm_update(struct omap_rtc_s *s)
|
|
4050 |
{
|
|
4051 |
s->alarm_ti = mktime(&s->alarm_tm);
|
|
4052 |
if (s->alarm_ti == -1)
|
|
4053 |
printf("%s: conversion failed\n", __FUNCTION__);
|
|
4054 |
}
|
|
4055 |
|
|
4056 |
static inline uint8_t omap_rtc_bcd(int num)
|
|
4057 |
{
|
|
4058 |
return ((num / 10) << 4) | (num % 10);
|
|
4059 |
}
|
|
4060 |
|
|
4061 |
static inline int omap_rtc_bin(uint8_t num)
|
|
4062 |
{
|
|
4063 |
return (num & 15) + 10 * (num >> 4);
|
|
4064 |
}
|
|
4065 |
|
|
4066 |
static uint32_t omap_rtc_read(void *opaque, target_phys_addr_t addr)
|
|
4067 |
{
|
|
4068 |
struct omap_rtc_s *s = (struct omap_rtc_s *) opaque;
|
|
4069 |
int offset = addr - s->base;
|
|
4070 |
uint8_t i;
|
|
4071 |
|
|
4072 |
switch (offset) {
|
|
4073 |
case 0x00: /* SECONDS_REG */
|
|
4074 |
return omap_rtc_bcd(s->current_tm.tm_sec);
|
|
4075 |
|
|
4076 |
case 0x04: /* MINUTES_REG */
|
|
4077 |
return omap_rtc_bcd(s->current_tm.tm_min);
|
|
4078 |
|
|
4079 |
case 0x08: /* HOURS_REG */
|
|
4080 |
if (s->pm_am)
|
|
4081 |
return ((s->current_tm.tm_hour > 11) << 7) |
|
|
4082 |
omap_rtc_bcd(((s->current_tm.tm_hour - 1) % 12) + 1);
|
|
4083 |
else
|
|
4084 |
return omap_rtc_bcd(s->current_tm.tm_hour);
|
|
4085 |
|
|
4086 |
case 0x0c: /* DAYS_REG */
|
|
4087 |
return omap_rtc_bcd(s->current_tm.tm_mday);
|
|
4088 |
|
|
4089 |
case 0x10: /* MONTHS_REG */
|
|
4090 |
return omap_rtc_bcd(s->current_tm.tm_mon + 1);
|
|
4091 |
|
|
4092 |
case 0x14: /* YEARS_REG */
|
|
4093 |
return omap_rtc_bcd(s->current_tm.tm_year % 100);
|
|
4094 |
|
|
4095 |
case 0x18: /* WEEK_REG */
|
|
4096 |
return s->current_tm.tm_wday;
|
|
4097 |
|
|
4098 |
case 0x20: /* ALARM_SECONDS_REG */
|
|
4099 |
return omap_rtc_bcd(s->alarm_tm.tm_sec);
|
|
4100 |
|
|
4101 |
case 0x24: /* ALARM_MINUTES_REG */
|
|
4102 |
return omap_rtc_bcd(s->alarm_tm.tm_min);
|
|
4103 |
|
|
4104 |
case 0x28: /* ALARM_HOURS_REG */
|
|
4105 |
if (s->pm_am)
|
|
4106 |
return ((s->alarm_tm.tm_hour > 11) << 7) |
|
|
4107 |
omap_rtc_bcd(((s->alarm_tm.tm_hour - 1) % 12) + 1);
|
|
4108 |
else
|
|
4109 |
return omap_rtc_bcd(s->alarm_tm.tm_hour);
|
|
4110 |
|
|
4111 |
case 0x2c: /* ALARM_DAYS_REG */
|
|
4112 |
return omap_rtc_bcd(s->alarm_tm.tm_mday);
|
|
4113 |
|
|
4114 |
case 0x30: /* ALARM_MONTHS_REG */
|
|
4115 |
return omap_rtc_bcd(s->alarm_tm.tm_mon + 1);
|
|
4116 |
|
|
4117 |
case 0x34: /* ALARM_YEARS_REG */
|
|
4118 |
return omap_rtc_bcd(s->alarm_tm.tm_year % 100);
|
|
4119 |
|
|
4120 |
case 0x40: /* RTC_CTRL_REG */
|
|
4121 |
return (s->pm_am << 3) | (s->auto_comp << 2) |
|
|
4122 |
(s->round << 1) | s->running;
|
|
4123 |
|
|
4124 |
case 0x44: /* RTC_STATUS_REG */
|
|
4125 |
i = s->status;
|
|
4126 |
s->status &= ~0x3d;
|
|
4127 |
return i;
|
|
4128 |
|
|
4129 |
case 0x48: /* RTC_INTERRUPTS_REG */
|
|
4130 |
return s->interrupts;
|
|
4131 |
|
|
4132 |
case 0x4c: /* RTC_COMP_LSB_REG */
|
|
4133 |
return ((uint16_t) s->comp_reg) & 0xff;
|
|
4134 |
|
|
4135 |
case 0x50: /* RTC_COMP_MSB_REG */
|
|
4136 |
return ((uint16_t) s->comp_reg) >> 8;
|
|
4137 |
}
|
|
4138 |
|
|
4139 |
OMAP_BAD_REG(addr);
|
|
4140 |
return 0;
|
|
4141 |
}
|
|
4142 |
|
|
4143 |
static void omap_rtc_write(void *opaque, target_phys_addr_t addr,
|
|
4144 |
uint32_t value)
|
|
4145 |
{
|
|
4146 |
struct omap_rtc_s *s = (struct omap_rtc_s *) opaque;
|
|
4147 |
int offset = addr - s->base;
|
|
4148 |
struct tm new_tm;
|
|
4149 |
time_t ti[2];
|
|
4150 |
|
|
4151 |
switch (offset) {
|
|
4152 |
case 0x00: /* SECONDS_REG */
|
|
4153 |
#if ALMDEBUG
|
|
4154 |
printf("RTC SEC_REG <-- %02x\n", value);
|
|
4155 |
#endif
|
|
4156 |
s->ti -= s->current_tm.tm_sec;
|
|
4157 |
s->ti += omap_rtc_bin(value);
|
|
4158 |
return;
|
|
4159 |
|
|
4160 |
case 0x04: /* MINUTES_REG */
|
|
4161 |
#if ALMDEBUG
|
|
4162 |
printf("RTC MIN_REG <-- %02x\n", value);
|
|
4163 |
#endif
|
|
4164 |
s->ti -= s->current_tm.tm_min * 60;
|
|
4165 |
s->ti += omap_rtc_bin(value) * 60;
|
|
4166 |
return;
|
|
4167 |
|
|
4168 |
case 0x08: /* HOURS_REG */
|
|
4169 |
#if ALMDEBUG
|
|
4170 |
printf("RTC HRS_REG <-- %02x\n", value);
|
|
4171 |
#endif
|
|
4172 |
s->ti -= s->current_tm.tm_hour * 3600;
|
|
4173 |
if (s->pm_am) {
|
|
4174 |
s->ti += (omap_rtc_bin(value & 0x3f) & 12) * 3600;
|
|
4175 |
s->ti += ((value >> 7) & 1) * 43200;
|
|
4176 |
} else
|
|
4177 |
s->ti += omap_rtc_bin(value & 0x3f) * 3600;
|
|
4178 |
return;
|
|
4179 |
|
|
4180 |
case 0x0c: /* DAYS_REG */
|
|
4181 |
#if ALMDEBUG
|
|
4182 |
printf("RTC DAY_REG <-- %02x\n", value);
|
|
4183 |
#endif
|
|
4184 |
s->ti -= s->current_tm.tm_mday * 86400;
|
|
4185 |
s->ti += omap_rtc_bin(value) * 86400;
|
|
4186 |
return;
|
|
4187 |
|
|
4188 |
case 0x10: /* MONTHS_REG */
|
|
4189 |
#if ALMDEBUG
|
|
4190 |
printf("RTC MTH_REG <-- %02x\n", value);
|
|
4191 |
#endif
|
|
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);
|
|
4196 |
|
|
4197 |
if (ti[0] != -1 && ti[1] != -1) {
|
|
4198 |
s->ti -= ti[0];
|
|
4199 |
s->ti += ti[1];
|
|
4200 |
} else {
|
|
4201 |
/* A less accurate version */
|
|
4202 |
s->ti -= s->current_tm.tm_mon * 2592000;
|
|
4203 |
s->ti += omap_rtc_bin(value) * 2592000;
|
|
4204 |
}
|
|
4205 |
return;
|
|
4206 |
|
|
4207 |
case 0x14: /* YEARS_REG */
|
|
4208 |
#if ALMDEBUG
|
|
4209 |
printf("RTC YRS_REG <-- %02x\n", value);
|
|
4210 |
#endif
|
|
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);
|
|
4215 |
|
|
4216 |
if (ti[0] != -1 && ti[1] != -1) {
|
|
4217 |
s->ti -= ti[0];
|
|
4218 |
s->ti += ti[1];
|
|
4219 |
} else {
|
|
4220 |
/* A less accurate version */
|
|
4221 |
s->ti -= (s->current_tm.tm_year % 100) * 31536000;
|
|
4222 |
s->ti += omap_rtc_bin(value) * 31536000;
|
|
4223 |
}
|
|
4224 |
return;
|
|
4225 |
|
|
4226 |
case 0x18: /* WEEK_REG */
|
|
4227 |
return; /* Ignored */
|
|
4228 |
|
|
4229 |
case 0x20: /* ALARM_SECONDS_REG */
|
|
4230 |
#if ALMDEBUG
|
|
4231 |
printf("ALM SEC_REG <-- %02x\n", value);
|
|
4232 |
#endif
|
|
4233 |
s->alarm_tm.tm_sec = omap_rtc_bin(value);
|
|
4234 |
omap_rtc_alarm_update(s);
|
|
4235 |
return;
|
|
4236 |
|
|
4237 |
case 0x24: /* ALARM_MINUTES_REG */
|
|
4238 |
#if ALMDEBUG
|
|
4239 |
printf("ALM MIN_REG <-- %02x\n", value);
|
|
4240 |
#endif
|
|
4241 |
s->alarm_tm.tm_min = omap_rtc_bin(value);
|
|
4242 |
omap_rtc_alarm_update(s);
|
|
4243 |
return;
|
|
4244 |
|
|
4245 |
case 0x28: /* ALARM_HOURS_REG */
|
|
4246 |
#if ALMDEBUG
|
|
4247 |
printf("ALM HRS_REG <-- %02x\n", value);
|
|
4248 |
#endif
|
|
4249 |
if (s->pm_am)
|
|
4250 |
s->alarm_tm.tm_hour =
|
|
4251 |
((omap_rtc_bin(value & 0x3f)) % 12) +
|
|
4252 |
((value >> 7) & 1) * 12;
|
|
4253 |
else
|
|
4254 |
s->alarm_tm.tm_hour = omap_rtc_bin(value);
|
|
4255 |
omap_rtc_alarm_update(s);
|
|
4256 |
return;
|
|
4257 |
|
|
4258 |
case 0x2c: /* ALARM_DAYS_REG */
|
|
4259 |
#if ALMDEBUG
|
|
4260 |
printf("ALM DAY_REG <-- %02x\n", value);
|
|
4261 |
#endif
|
|
4262 |
s->alarm_tm.tm_mday = omap_rtc_bin(value);
|
|
4263 |
omap_rtc_alarm_update(s);
|
|
4264 |
return;
|
|
4265 |
|
|
4266 |
case 0x30: /* ALARM_MONTHS_REG */
|
|
4267 |
#if ALMDEBUG
|
|
4268 |
printf("ALM MON_REG <-- %02x\n", value);
|
|
4269 |
#endif
|
|
4270 |
s->alarm_tm.tm_mon = omap_rtc_bin(value);
|
|
4271 |
omap_rtc_alarm_update(s);
|
|
4272 |
return;
|
|
4273 |
|
|
4274 |
case 0x34: /* ALARM_YEARS_REG */
|
|
4275 |
#if ALMDEBUG
|
|
4276 |
printf("ALM YRS_REG <-- %02x\n", value);
|
|
4277 |
#endif
|
|
4278 |
s->alarm_tm.tm_year = omap_rtc_bin(value);
|
|
4279 |
omap_rtc_alarm_update(s);
|
|
4280 |
return;
|
|
4281 |
|
|
4282 |
case 0x40: /* RTC_CTRL_REG */
|
|
4283 |
#if ALMDEBUG
|
|
4284 |
printf("RTC CONTROL <-- %02x\n", value);
|
|
4285 |
#endif
|
|
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;
|
|
4290 |
s->status &= 0xfd;
|
|
4291 |
s->status |= s->running << 1;
|
|
4292 |
return;
|
|
4293 |
|
|
4294 |
case 0x44: /* RTC_STATUS_REG */
|
|
4295 |
#if ALMDEBUG
|
|
4296 |
printf("RTC STATUSL <-- %02x\n", value);
|
|
4297 |
#endif
|
|
4298 |
s->status &= ~((value & 0xc0) ^ 0x80);
|
|
4299 |
omap_rtc_interrupts_update(s);
|
|
4300 |
return;
|
|
4301 |
|
|
4302 |
case 0x48: /* RTC_INTERRUPTS_REG */
|
|
4303 |
#if ALMDEBUG
|
|
4304 |
printf("RTC INTRS <-- %02x\n", value);
|
|
4305 |
#endif
|
|
4306 |
s->interrupts = value;
|
|
4307 |
return;
|
|
4308 |
|
|
4309 |
case 0x4c: /* RTC_COMP_LSB_REG */
|
|
4310 |
#if ALMDEBUG
|
|
4311 |
printf("RTC COMPLSB <-- %02x\n", value);
|
|
4312 |
#endif
|
|
4313 |
s->comp_reg &= 0xff00;
|
|
4314 |
s->comp_reg |= 0x00ff & value;
|
|
4315 |
return;
|
|
4316 |
|
|
4317 |
case 0x50: /* RTC_COMP_MSB_REG */
|
|
4318 |
#if ALMDEBUG
|
|
4319 |
printf("RTC COMPMSB <-- %02x\n", value);
|
|
4320 |
#endif
|
|
4321 |
s->comp_reg &= 0x00ff;
|
|
4322 |
s->comp_reg |= 0xff00 & (value << 8);
|
|
4323 |
return;
|
|
4324 |
|
|
4325 |
default:
|
|
4326 |
OMAP_BAD_REG(addr);
|
|
4327 |
return;
|
|
4328 |
}
|
|
4329 |
}
|
|
4330 |
|
|
4331 |
static CPUReadMemoryFunc *omap_rtc_readfn[] = {
|
|
4332 |
omap_rtc_read,
|
|
4333 |
omap_badwidth_read8,
|
|
4334 |
omap_badwidth_read8,
|
|
4335 |
};
|
|
4336 |
|
|
4337 |
static CPUWriteMemoryFunc *omap_rtc_writefn[] = {
|
|
4338 |
omap_rtc_write,
|
|
4339 |
omap_badwidth_write8,
|
|
4340 |
omap_badwidth_write8,
|
|
4341 |
};
|
|
4342 |
|
|
4343 |
static void omap_rtc_tick(void *opaque)
|
|
4344 |
{
|
|
4345 |
struct omap_rtc_s *s = opaque;
|
|
4346 |
|
|
4347 |
if (s->round) {
|
|
4348 |
/* Round to nearest full minute. */
|
|
4349 |
if (s->current_tm.tm_sec < 30)
|
|
4350 |
s->ti -= s->current_tm.tm_sec;
|
|
4351 |
else
|
|
4352 |
s->ti += 60 - s->current_tm.tm_sec;
|
|
4353 |
|
|
4354 |
s->round = 0;
|
|
4355 |
}
|
|
4356 |
|
|
4357 |
localtime_r(&s->ti, &s->current_tm);
|
|
4358 |
|
|
4359 |
if ((s->interrupts & 0x08) && s->ti == s->alarm_ti) {
|
|
4360 |
s->status |= 0x40;
|
|
4361 |
omap_rtc_interrupts_update(s);
|
|
4362 |
}
|
|
4363 |
|
|
4364 |
if (s->interrupts & 0x04)
|
|
4365 |
switch (s->interrupts & 3) {
|
|
4366 |
case 0:
|
|
4367 |
s->status |= 0x04;
|
|
4368 |
qemu_irq_raise(s->irq);
|
|
4369 |
break;
|
|
4370 |
case 1:
|
|
4371 |
if (s->current_tm.tm_sec)
|
|
4372 |
break;
|
|
4373 |
s->status |= 0x08;
|
|
4374 |
qemu_irq_raise(s->irq);
|
|
4375 |
break;
|
|
4376 |
case 2:
|
|
4377 |
if (s->current_tm.tm_sec || s->current_tm.tm_min)
|
|
4378 |
break;
|
|
4379 |
s->status |= 0x10;
|
|
4380 |
qemu_irq_raise(s->irq);
|
|
4381 |
break;
|
|
4382 |
case 3:
|
|
4383 |
if (s->current_tm.tm_sec ||
|
|
4384 |
s->current_tm.tm_min || s->current_tm.tm_hour)
|
|
4385 |
break;
|
|
4386 |
s->status |= 0x20;
|
|
4387 |
qemu_irq_raise(s->irq);
|
|
4388 |
break;
|
|
4389 |
}
|
|
4390 |
|
|
4391 |
/* Move on */
|
|
4392 |
if (s->running)
|
|
4393 |
s->ti ++;
|
|
4394 |
s->tick += 1000;
|
|
4395 |
|
|
4396 |
/*
|
|
4397 |
* Every full hour add a rough approximation of the compensation
|
|
4398 |
* register to the 32kHz Timer (which drives the RTC) value.
|
|
4399 |
*/
|
|
4400 |
if (s->auto_comp && !s->current_tm.tm_sec && !s->current_tm.tm_min)
|
|
4401 |
s->tick += s->comp_reg * 1000 / 32768;
|
|
4402 |
|
|
4403 |
qemu_mod_timer(s->clk, s->tick);
|
|
4404 |
}
|
|
4405 |
|
|
4406 |
void omap_rtc_reset(struct omap_rtc_s *s)
|
|
4407 |
{
|
|
4408 |
s->interrupts = 0;
|
|
4409 |
s->comp_reg = 0;
|
|
4410 |
s->running = 0;
|
|
4411 |
s->pm_am = 0;
|
|
4412 |
s->auto_comp = 0;
|
|
4413 |
s->round = 0;
|
|
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;
|
|
4417 |
s->status = 1 << 7;
|
|
4418 |
time(&s->ti);
|
|
4419 |
s->ti = mktime(s->convert(&s->ti, &s->current_tm));
|
|
4420 |
|
|
4421 |
omap_rtc_alarm_update(s);
|
|
4422 |
omap_rtc_tick(s);
|
|
4423 |
}
|
|
4424 |
|
|
4425 |
struct omap_rtc_s *omap_rtc_init(target_phys_addr_t base,
|
|
4426 |
qemu_irq *irq, omap_clk clk)
|
|
4427 |
{
|
|
4428 |
int iomemtype;
|
|
4429 |
struct omap_rtc_s *s = (struct omap_rtc_s *)
|
|
4430 |
qemu_mallocz(sizeof(struct omap_rtc_s));
|
|
4431 |
|
|
4432 |
s->base = base;
|
|
4433 |
s->irq = irq[0];
|
|
4434 |
s->alarm = irq[1];
|
|
4435 |
s->clk = qemu_new_timer(rt_clock, omap_rtc_tick, s);
|
|
4436 |
s->convert = rtc_utc ? gmtime_r : localtime_r;
|
|
4437 |
|
|
4438 |
omap_rtc_reset(s);
|
|
4439 |
|
|
4440 |
iomemtype = cpu_register_io_memory(0, omap_rtc_readfn,
|
|
4441 |
omap_rtc_writefn, s);
|
|
4442 |
cpu_register_physical_memory(s->base, 0x800, iomemtype);
|
|
4443 |
|
|
4444 |
return s;
|
|
4445 |
}
|
|
4446 |
|
4021 |
4447 |
/* General chip reset */
|
4022 |
4448 |
static void omap_mpu_reset(void *opaque)
|
4023 |
4449 |
{
|
... | ... | |
4051 |
4477 |
omap_pwl_reset(mpu);
|
4052 |
4478 |
omap_pwt_reset(mpu);
|
4053 |
4479 |
omap_i2c_reset(mpu->i2c);
|
|
4480 |
omap_rtc_reset(mpu->rtc);
|
4054 |
4481 |
cpu_reset(mpu->env);
|
4055 |
4482 |
}
|
4056 |
4483 |
|
... | ... | |
4178 |
4605 |
s->i2c = omap_i2c_init(0xfffb3800, s->irq[1][OMAP_INT_I2C],
|
4179 |
4606 |
&s->drq[OMAP_DMA_I2C_RX], omap_findclk(s, "mpuper_ck"));
|
4180 |
4607 |
|
|
4608 |
s->rtc = omap_rtc_init(0xfffb4800, &s->irq[1][OMAP_INT_RTC_TIMER],
|
|
4609 |
omap_findclk(s, "clk32-kHz"));
|
4181 |
4610 |
qemu_register_reset(omap_mpu_reset, s);
|
4182 |
4611 |
|
4183 |
4612 |
return s;
|