qemu/hw/ds1338.c
<<
>>
Prefs
   1/*
   2 * MAXIM DS1338 I2C RTC+NVRAM
   3 *
   4 * Copyright (c) 2009 CodeSourcery.
   5 * Written by Paul Brook
   6 *
   7 * This code is licenced under the GNU GPL v2.
   8 */
   9
  10#include "i2c.h"
  11
  12typedef struct {
  13    i2c_slave i2c;
  14    time_t offset;
  15    struct tm now;
  16    uint8_t nvram[56];
  17    int ptr;
  18    int addr_byte;
  19} DS1338State;
  20
  21static void ds1338_event(i2c_slave *i2c, enum i2c_event event)
  22{
  23    DS1338State *s = FROM_I2C_SLAVE(DS1338State, i2c);
  24
  25    switch (event) {
  26    case I2C_START_RECV:
  27        qemu_get_timedate(&s->now, s->offset);
  28        s->nvram[0] = to_bcd(s->now.tm_sec);
  29        s->nvram[1] = to_bcd(s->now.tm_min);
  30        if (s->nvram[2] & 0x40) {
  31            s->nvram[2] = (to_bcd((s->now.tm_hour % 12)) + 1) | 0x40;
  32            if (s->now.tm_hour >= 12) {
  33                s->nvram[2] |= 0x20;
  34            }
  35        } else {
  36            s->nvram[2] = to_bcd(s->now.tm_hour);
  37        }
  38        s->nvram[3] = to_bcd(s->now.tm_wday) + 1;
  39        s->nvram[4] = to_bcd(s->now.tm_mday);
  40        s->nvram[5] = to_bcd(s->now.tm_mon) + 1;
  41        s->nvram[6] = to_bcd(s->now.tm_year - 100);
  42        break;
  43    case I2C_START_SEND:
  44        s->addr_byte = 1;
  45        break;
  46    default:
  47        break;
  48    }
  49}
  50
  51static int ds1338_recv(i2c_slave *i2c)
  52{
  53    DS1338State *s = FROM_I2C_SLAVE(DS1338State, i2c);
  54    uint8_t res;
  55
  56    res  = s->nvram[s->ptr];
  57    s->ptr = (s->ptr + 1) & 0xff;
  58    return res;
  59}
  60
  61static int ds1338_send(i2c_slave *i2c, uint8_t data)
  62{
  63    DS1338State *s = FROM_I2C_SLAVE(DS1338State, i2c);
  64    if (s->addr_byte) {
  65        s->ptr = data;
  66        s->addr_byte = 0;
  67        return 0;
  68    }
  69    s->nvram[s->ptr - 8] = data;
  70    if (data < 8) {
  71        qemu_get_timedate(&s->now, s->offset);
  72        switch(data) {
  73        case 0:
  74            /* TODO: Implement CH (stop) bit.  */
  75            s->now.tm_sec = from_bcd(data & 0x7f);
  76            break;
  77        case 1:
  78            s->now.tm_min = from_bcd(data & 0x7f);
  79            break;
  80        case 2:
  81            if (data & 0x40) {
  82                if (data & 0x20) {
  83                    data = from_bcd(data & 0x4f) + 11;
  84                } else {
  85                    data = from_bcd(data & 0x1f) - 1;
  86                }
  87            } else {
  88                data = from_bcd(data);
  89            }
  90            s->now.tm_hour = data;
  91            break;
  92        case 3:
  93            s->now.tm_wday = from_bcd(data & 7) - 1;
  94            break;
  95        case 4:
  96            s->now.tm_mday = from_bcd(data & 0x3f);
  97            break;
  98        case 5:
  99            s->now.tm_mon = from_bcd(data & 0x1f) - 1;
 100        case 6:
 101            s->now.tm_year = from_bcd(data) + 100;
 102            break;
 103        case 7:
 104            /* Control register. Currently ignored.  */
 105            break;
 106        }
 107        s->offset = qemu_timedate_diff(&s->now);
 108    }
 109    s->ptr = (s->ptr + 1) & 0xff;
 110    return 0;
 111}
 112
 113static int ds1338_init(i2c_slave *i2c)
 114{
 115    return 0;
 116}
 117
 118static I2CSlaveInfo ds1338_info = {
 119    .qdev.name = "ds1338",
 120    .qdev.size = sizeof(DS1338State),
 121    .init = ds1338_init,
 122    .event = ds1338_event,
 123    .recv = ds1338_recv,
 124    .send = ds1338_send,
 125};
 126
 127static void ds1338_register_devices(void)
 128{
 129    i2c_register_slave(&ds1338_info);
 130}
 131
 132device_init(ds1338_register_devices)
 133