uboot/drivers/misc/i2c_eeprom_emul.c
<<
>>
Prefs
   1/*
   2 * Simulate an I2C eeprom
   3 *
   4 * Copyright (c) 2014 Google, Inc
   5 *
   6 * SPDX-License-Identifier:     GPL-2.0+
   7 */
   8
   9#include <common.h>
  10#include <dm.h>
  11#include <fdtdec.h>
  12#include <i2c.h>
  13#include <malloc.h>
  14#include <asm/test.h>
  15
  16#ifdef DEBUG
  17#define debug_buffer print_buffer
  18#else
  19#define debug_buffer(x, ...)
  20#endif
  21
  22DECLARE_GLOBAL_DATA_PTR;
  23
  24struct sandbox_i2c_flash_plat_data {
  25        enum sandbox_i2c_eeprom_test_mode test_mode;
  26        const char *filename;
  27        int offset_len;         /* Length of an offset in bytes */
  28        int size;               /* Size of data buffer */
  29};
  30
  31struct sandbox_i2c_flash {
  32        uint8_t *data;
  33};
  34
  35void sandbox_i2c_eeprom_set_test_mode(struct udevice *dev,
  36                                      enum sandbox_i2c_eeprom_test_mode mode)
  37{
  38        struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(dev);
  39
  40        plat->test_mode = mode;
  41}
  42
  43void sandbox_i2c_eeprom_set_offset_len(struct udevice *dev, int offset_len)
  44{
  45        struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(dev);
  46
  47        plat->offset_len = offset_len;
  48}
  49
  50static int sandbox_i2c_eeprom_xfer(struct udevice *emul, struct i2c_msg *msg,
  51                                  int nmsgs)
  52{
  53        struct sandbox_i2c_flash *priv = dev_get_priv(emul);
  54        uint offset = 0;
  55
  56        debug("\n%s\n", __func__);
  57        debug_buffer(0, priv->data, 1, 16, 0);
  58        for (; nmsgs > 0; nmsgs--, msg++) {
  59                struct sandbox_i2c_flash_plat_data *plat =
  60                                dev_get_platdata(emul);
  61                int len;
  62                u8 *ptr;
  63
  64                if (!plat->size)
  65                        return -ENODEV;
  66                if (msg->addr + msg->len > plat->size) {
  67                        debug("%s: Address %x, len %x is outside range 0..%x\n",
  68                              __func__, msg->addr, msg->len, plat->size);
  69                        return -EINVAL;
  70                }
  71                len = msg->len;
  72                debug("   %s: msg->len=%d",
  73                      msg->flags & I2C_M_RD ? "read" : "write",
  74                      msg->len);
  75                if (msg->flags & I2C_M_RD) {
  76                        if (plat->test_mode == SIE_TEST_MODE_SINGLE_BYTE)
  77                                len = 1;
  78                        debug(", offset %x, len %x: ", offset, len);
  79                        memcpy(msg->buf, priv->data + offset, len);
  80                        memset(msg->buf + len, '\xff', msg->len - len);
  81                        debug_buffer(0, msg->buf, 1, msg->len, 0);
  82                } else if (len >= plat->offset_len) {
  83                        int i;
  84
  85                        ptr = msg->buf;
  86                        for (i = 0; i < plat->offset_len; i++, len--)
  87                                offset = (offset << 8) | *ptr++;
  88                        debug(", set offset %x: ", offset);
  89                        debug_buffer(0, msg->buf, 1, msg->len, 0);
  90                        if (plat->test_mode == SIE_TEST_MODE_SINGLE_BYTE)
  91                                len = min(len, 1);
  92
  93                        /* For testing, map offsets into our limited buffer */
  94                        for (i = 24; i > 0; i -= 8) {
  95                                if (offset > (1 << i)) {
  96                                        offset = (offset >> i) |
  97                                                (offset & ((1 << i) - 1));
  98                                        offset += i;
  99                                }
 100                        }
 101                        memcpy(priv->data + offset, ptr, len);
 102                }
 103        }
 104        debug_buffer(0, priv->data, 1, 16, 0);
 105
 106        return 0;
 107}
 108
 109struct dm_i2c_ops sandbox_i2c_emul_ops = {
 110        .xfer = sandbox_i2c_eeprom_xfer,
 111};
 112
 113static int sandbox_i2c_eeprom_ofdata_to_platdata(struct udevice *dev)
 114{
 115        struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(dev);
 116
 117        plat->size = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
 118                                    "sandbox,size", 32);
 119        plat->filename = fdt_getprop(gd->fdt_blob, dev->of_offset,
 120                                     "sandbox,filename", NULL);
 121        if (!plat->filename) {
 122                debug("%s: No filename for device '%s'\n", __func__,
 123                      dev->name);
 124                return -EINVAL;
 125        }
 126        plat->test_mode = SIE_TEST_MODE_NONE;
 127        plat->offset_len = 1;
 128
 129        return 0;
 130}
 131
 132static int sandbox_i2c_eeprom_probe(struct udevice *dev)
 133{
 134        struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(dev);
 135        struct sandbox_i2c_flash *priv = dev_get_priv(dev);
 136
 137        priv->data = calloc(1, plat->size);
 138        if (!priv->data)
 139                return -ENOMEM;
 140
 141        return 0;
 142}
 143
 144static int sandbox_i2c_eeprom_remove(struct udevice *dev)
 145{
 146        struct sandbox_i2c_flash *priv = dev_get_priv(dev);
 147
 148        free(priv->data);
 149
 150        return 0;
 151}
 152
 153static const struct udevice_id sandbox_i2c_ids[] = {
 154        { .compatible = "sandbox,i2c-eeprom" },
 155        { }
 156};
 157
 158U_BOOT_DRIVER(sandbox_i2c_emul) = {
 159        .name           = "sandbox_i2c_eeprom_emul",
 160        .id             = UCLASS_I2C_EMUL,
 161        .of_match       = sandbox_i2c_ids,
 162        .ofdata_to_platdata = sandbox_i2c_eeprom_ofdata_to_platdata,
 163        .probe          = sandbox_i2c_eeprom_probe,
 164        .remove         = sandbox_i2c_eeprom_remove,
 165        .priv_auto_alloc_size = sizeof(struct sandbox_i2c_flash),
 166        .platdata_auto_alloc_size = sizeof(struct sandbox_i2c_flash_plat_data),
 167        .ops            = &sandbox_i2c_emul_ops,
 168};
 169