uboot/drivers/rtc/i2c_rtc_emul.c
<<
>>
Prefs
   1/*
   2 * Simulate an I2C real time clock
   3 *
   4 * Copyright (c) 2015 Google, Inc
   5 * Written by Simon Glass <sjg@chromium.org>
   6 *
   7 * SPDX-License-Identifier:     GPL-2.0+
   8 */
   9
  10/*
  11 * This is a test driver. It starts off with the current time of the machine,
  12 * but also supports setting the time, using an offset from the current
  13 * clock. This driver is only intended for testing, not accurate
  14 * time-keeping. It does not change the system time.
  15 */
  16
  17#include <common.h>
  18#include <dm.h>
  19#include <fdtdec.h>
  20#include <i2c.h>
  21#include <os.h>
  22#include <rtc.h>
  23#include <asm/rtc.h>
  24#include <asm/test.h>
  25
  26#ifdef DEBUG
  27#define debug_buffer print_buffer
  28#else
  29#define debug_buffer(x, ...)
  30#endif
  31
  32DECLARE_GLOBAL_DATA_PTR;
  33
  34/**
  35 * struct sandbox_i2c_rtc_plat_data - platform data for the RTC
  36 *
  37 * @base_time:          Base system time when RTC device was bound
  38 * @offset:             RTC offset from current system time
  39 * @use_system_time:    true to use system time, false to use @base_time
  40 * @reg:                Register values
  41 */
  42struct sandbox_i2c_rtc_plat_data {
  43        long base_time;
  44        long offset;
  45        bool use_system_time;
  46        u8 reg[REG_COUNT];
  47};
  48
  49struct sandbox_i2c_rtc {
  50        unsigned int offset_secs;
  51};
  52
  53long sandbox_i2c_rtc_set_offset(struct udevice *dev, bool use_system_time,
  54                                int offset)
  55{
  56        struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev);
  57        long old_offset;
  58
  59        old_offset = plat->offset;
  60        plat->use_system_time = use_system_time;
  61        if (offset != -1)
  62                plat->offset = offset;
  63
  64        return old_offset;
  65}
  66
  67long sandbox_i2c_rtc_get_set_base_time(struct udevice *dev, long base_time)
  68{
  69        struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev);
  70        long old_base_time;
  71
  72        old_base_time = plat->base_time;
  73        if (base_time != -1)
  74                plat->base_time = base_time;
  75
  76        return old_base_time;
  77}
  78
  79static void reset_time(struct udevice *dev)
  80{
  81        struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev);
  82        struct rtc_time now;
  83
  84        os_localtime(&now);
  85        plat->base_time = rtc_mktime(&now);
  86        plat->offset = 0;
  87        plat->use_system_time = true;
  88}
  89
  90static int sandbox_i2c_rtc_get(struct udevice *dev, struct rtc_time *time)
  91{
  92        struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev);
  93        struct rtc_time tm_now;
  94        long now;
  95
  96        if (plat->use_system_time) {
  97                os_localtime(&tm_now);
  98                now = rtc_mktime(&tm_now);
  99        } else {
 100                now = plat->base_time;
 101        }
 102
 103        return rtc_to_tm(now + plat->offset, time);
 104}
 105
 106static int sandbox_i2c_rtc_set(struct udevice *dev, const struct rtc_time *time)
 107{
 108        struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev);
 109        struct rtc_time tm_now;
 110        long now;
 111
 112        if (plat->use_system_time) {
 113                os_localtime(&tm_now);
 114                now = rtc_mktime(&tm_now);
 115        } else {
 116                now = plat->base_time;
 117        }
 118        plat->offset = rtc_mktime(time) - now;
 119
 120        return 0;
 121}
 122
 123/* Update the current time in the registers */
 124static int sandbox_i2c_rtc_prepare_read(struct udevice *emul)
 125{
 126        struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(emul);
 127        struct rtc_time time;
 128        int ret;
 129
 130        ret = sandbox_i2c_rtc_get(emul, &time);
 131        if (ret)
 132                return ret;
 133
 134        plat->reg[REG_SEC] = time.tm_sec;
 135        plat->reg[REG_MIN] = time.tm_min;
 136        plat->reg[REG_HOUR] = time.tm_hour;
 137        plat->reg[REG_MDAY] = time.tm_mday;
 138        plat->reg[REG_MON] = time.tm_mon;
 139        plat->reg[REG_YEAR] = time.tm_year - 1900;
 140        plat->reg[REG_WDAY] = time.tm_wday;
 141
 142        return 0;
 143}
 144
 145static int sandbox_i2c_rtc_complete_write(struct udevice *emul)
 146{
 147        struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(emul);
 148        struct rtc_time time;
 149        int ret;
 150
 151        time.tm_sec = plat->reg[REG_SEC];
 152        time.tm_min = plat->reg[REG_MIN];
 153        time.tm_hour = plat->reg[REG_HOUR];
 154        time.tm_mday = plat->reg[REG_MDAY];
 155        time.tm_mon = plat->reg[REG_MON];
 156        time.tm_year = plat->reg[REG_YEAR] + 1900;
 157        time.tm_wday = plat->reg[REG_WDAY];
 158
 159        ret = sandbox_i2c_rtc_set(emul, &time);
 160        if (ret)
 161                return ret;
 162
 163        return 0;
 164}
 165
 166static int sandbox_i2c_rtc_xfer(struct udevice *emul, struct i2c_msg *msg,
 167                                int nmsgs)
 168{
 169        struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(emul);
 170        uint offset = 0;
 171        int ret;
 172
 173        debug("\n%s\n", __func__);
 174        ret = sandbox_i2c_rtc_prepare_read(emul);
 175        if (ret)
 176                return ret;
 177        for (; nmsgs > 0; nmsgs--, msg++) {
 178                int len;
 179                u8 *ptr;
 180
 181                len = msg->len;
 182                debug("   %s: msg->len=%d",
 183                      msg->flags & I2C_M_RD ? "read" : "write",
 184                      msg->len);
 185                if (msg->flags & I2C_M_RD) {
 186                        debug(", offset %x, len %x: ", offset, len);
 187
 188                        /* Read the register */
 189                        memcpy(msg->buf, plat->reg + offset, len);
 190                        memset(msg->buf + len, '\xff', msg->len - len);
 191                        debug_buffer(0, msg->buf, 1, msg->len, 0);
 192                } else if (len >= 1) {
 193                        ptr = msg->buf;
 194                        offset = *ptr++ & (REG_COUNT - 1);
 195                        len--;
 196                        debug(", set offset %x: ", offset);
 197                        debug_buffer(0, msg->buf, 1, msg->len, 0);
 198
 199                        /* Write the register */
 200                        memcpy(plat->reg + offset, ptr, len);
 201                        if (offset == REG_RESET)
 202                                reset_time(emul);
 203                }
 204        }
 205        ret = sandbox_i2c_rtc_complete_write(emul);
 206        if (ret)
 207                return ret;
 208
 209        return 0;
 210}
 211
 212struct dm_i2c_ops sandbox_i2c_rtc_emul_ops = {
 213        .xfer = sandbox_i2c_rtc_xfer,
 214};
 215
 216static int sandbox_i2c_rtc_bind(struct udevice *dev)
 217{
 218        reset_time(dev);
 219
 220        return 0;
 221}
 222
 223static const struct udevice_id sandbox_i2c_rtc_ids[] = {
 224        { .compatible = "sandbox,i2c-rtc" },
 225        { }
 226};
 227
 228U_BOOT_DRIVER(sandbox_i2c_rtc_emul) = {
 229        .name           = "sandbox_i2c_rtc_emul",
 230        .id             = UCLASS_I2C_EMUL,
 231        .of_match       = sandbox_i2c_rtc_ids,
 232        .bind           = sandbox_i2c_rtc_bind,
 233        .priv_auto_alloc_size = sizeof(struct sandbox_i2c_rtc),
 234        .platdata_auto_alloc_size = sizeof(struct sandbox_i2c_rtc_plat_data),
 235        .ops            = &sandbox_i2c_rtc_emul_ops,
 236};
 237