qemu/hw/rtc/m41t80.c
<<
>>
Prefs
   1/*
   2 * M41T80 serial rtc emulation
   3 *
   4 * Copyright (c) 2018 BALATON Zoltan
   5 *
   6 * This work is licensed under the GNU GPL license version 2 or later.
   7 *
   8 */
   9
  10#include "qemu/osdep.h"
  11#include "qemu/log.h"
  12#include "qemu/module.h"
  13#include "qemu/timer.h"
  14#include "qemu/bcd.h"
  15#include "hw/i2c/i2c.h"
  16#include "qom/object.h"
  17#include "sysemu/rtc.h"
  18
  19#define TYPE_M41T80 "m41t80"
  20OBJECT_DECLARE_SIMPLE_TYPE(M41t80State, M41T80)
  21
  22struct M41t80State {
  23    I2CSlave parent_obj;
  24    int8_t addr;
  25};
  26
  27static void m41t80_realize(DeviceState *dev, Error **errp)
  28{
  29    M41t80State *s = M41T80(dev);
  30
  31    s->addr = -1;
  32}
  33
  34static int m41t80_send(I2CSlave *i2c, uint8_t data)
  35{
  36    M41t80State *s = M41T80(i2c);
  37
  38    if (s->addr < 0) {
  39        s->addr = data;
  40    } else {
  41        s->addr++;
  42    }
  43    return 0;
  44}
  45
  46static uint8_t m41t80_recv(I2CSlave *i2c)
  47{
  48    M41t80State *s = M41T80(i2c);
  49    struct tm now;
  50    int64_t rt;
  51
  52    if (s->addr < 0) {
  53        s->addr = 0;
  54    }
  55    if (s->addr >= 1 && s->addr <= 7) {
  56        qemu_get_timedate(&now, -1);
  57    }
  58    switch (s->addr++) {
  59    case 0:
  60        rt = g_get_real_time();
  61        return to_bcd((rt % G_USEC_PER_SEC) / 10000);
  62    case 1:
  63        return to_bcd(now.tm_sec);
  64    case 2:
  65        return to_bcd(now.tm_min);
  66    case 3:
  67        return to_bcd(now.tm_hour);
  68    case 4:
  69        return to_bcd(now.tm_wday);
  70    case 5:
  71        return to_bcd(now.tm_mday);
  72    case 6:
  73        return to_bcd(now.tm_mon + 1);
  74    case 7:
  75        return to_bcd(now.tm_year % 100);
  76    case 8 ... 19:
  77        qemu_log_mask(LOG_UNIMP, "%s: unimplemented register: %d\n",
  78                      __func__, s->addr - 1);
  79        return 0;
  80    default:
  81        qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid register: %d\n",
  82                      __func__, s->addr - 1);
  83        return 0;
  84    }
  85}
  86
  87static int m41t80_event(I2CSlave *i2c, enum i2c_event event)
  88{
  89    M41t80State *s = M41T80(i2c);
  90
  91    if (event == I2C_START_SEND) {
  92        s->addr = -1;
  93    }
  94    return 0;
  95}
  96
  97static void m41t80_class_init(ObjectClass *klass, void *data)
  98{
  99    DeviceClass *dc = DEVICE_CLASS(klass);
 100    I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass);
 101
 102    dc->realize = m41t80_realize;
 103    sc->send = m41t80_send;
 104    sc->recv = m41t80_recv;
 105    sc->event = m41t80_event;
 106}
 107
 108static const TypeInfo m41t80_info = {
 109    .name          = TYPE_M41T80,
 110    .parent        = TYPE_I2C_SLAVE,
 111    .instance_size = sizeof(M41t80State),
 112    .class_init    = m41t80_class_init,
 113};
 114
 115static void m41t80_register_types(void)
 116{
 117    type_register_static(&m41t80_info);
 118}
 119
 120type_init(m41t80_register_types)
 121