Revision 5c1c390f hw/omap.c

b/hw/omap.c
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;

Also available in: Unified diff