uboot/drivers/misc/i2c_eeprom.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (c) 2014 Google, Inc
   4 */
   5
   6#define LOG_CATEGORY UCLASS_I2C_EEPROM
   7
   8#include <common.h>
   9#include <eeprom.h>
  10#include <linux/delay.h>
  11#include <linux/err.h>
  12#include <linux/kernel.h>
  13#include <dm.h>
  14#include <dm/device-internal.h>
  15#include <i2c.h>
  16#include <i2c_eeprom.h>
  17
  18struct i2c_eeprom_drv_data {
  19        u32 size; /* size in bytes */
  20        u32 pagesize; /* page size in bytes */
  21        u32 addr_offset_mask; /* bits in addr used for offset overflow */
  22        u32 offset_len; /* size in bytes of offset */
  23        u32 start_offset; /* valid start offset inside memory, by default 0 */
  24};
  25
  26int i2c_eeprom_read(struct udevice *dev, int offset, uint8_t *buf, int size)
  27{
  28        const struct i2c_eeprom_ops *ops = device_get_ops(dev);
  29
  30        if (!ops->read)
  31                return -ENOSYS;
  32
  33        return ops->read(dev, offset, buf, size);
  34}
  35
  36int i2c_eeprom_write(struct udevice *dev, int offset, uint8_t *buf, int size)
  37{
  38        const struct i2c_eeprom_ops *ops = device_get_ops(dev);
  39
  40        if (!ops->write)
  41                return -ENOSYS;
  42
  43        return ops->write(dev, offset, buf, size);
  44}
  45
  46int i2c_eeprom_size(struct udevice *dev)
  47{
  48        const struct i2c_eeprom_ops *ops = device_get_ops(dev);
  49
  50        if (!ops->size)
  51                return -ENOSYS;
  52
  53        return ops->size(dev);
  54}
  55
  56static int i2c_eeprom_std_read(struct udevice *dev, int offset, uint8_t *buf,
  57                               int size)
  58{
  59        return dm_i2c_read(dev, offset, buf, size);
  60}
  61
  62static int i2c_eeprom_std_write(struct udevice *dev, int offset,
  63                                const uint8_t *buf, int size)
  64{
  65        struct i2c_eeprom *priv = dev_get_priv(dev);
  66        int ret;
  67
  68        while (size > 0) {
  69                int write_size = min_t(int, size, priv->pagesize);
  70
  71                ret = dm_i2c_write(dev, offset, buf, write_size);
  72                if (ret)
  73                        return ret;
  74
  75                offset += write_size;
  76                buf += write_size;
  77                size -= write_size;
  78
  79                udelay(10000);
  80        }
  81
  82        return 0;
  83}
  84
  85static int i2c_eeprom_std_size(struct udevice *dev)
  86{
  87        struct i2c_eeprom *priv = dev_get_priv(dev);
  88
  89        return priv->size;
  90}
  91
  92static const struct i2c_eeprom_ops i2c_eeprom_std_ops = {
  93        .read   = i2c_eeprom_std_read,
  94        .write  = i2c_eeprom_std_write,
  95        .size   = i2c_eeprom_std_size,
  96};
  97
  98static int i2c_eeprom_std_of_to_plat(struct udevice *dev)
  99{
 100        struct i2c_eeprom *priv = dev_get_priv(dev);
 101        struct i2c_eeprom_drv_data *data =
 102                (struct i2c_eeprom_drv_data *)dev_get_driver_data(dev);
 103        u32 pagesize;
 104        u32 size;
 105
 106        if (dev_read_u32(dev, "pagesize", &pagesize) == 0)
 107                priv->pagesize = pagesize;
 108        else
 109                /* 6 bit -> page size of up to 2^63 (should be sufficient) */
 110                priv->pagesize = data->pagesize;
 111
 112        if (dev_read_u32(dev, "size", &size) == 0)
 113                priv->size = size;
 114        else
 115                priv->size = data->size;
 116
 117        return 0;
 118}
 119
 120static int i2c_eeprom_std_bind(struct udevice *dev)
 121{
 122        ofnode partitions = ofnode_find_subnode(dev_ofnode(dev), "partitions");
 123        ofnode partition;
 124        const char *name;
 125
 126        if (!ofnode_valid(partitions))
 127                return 0;
 128        if (!ofnode_device_is_compatible(partitions, "fixed-partitions"))
 129                return -ENOTSUPP;
 130
 131        ofnode_for_each_subnode(partition, partitions) {
 132                name = ofnode_get_name(partition);
 133                if (!name)
 134                        continue;
 135
 136                device_bind(dev, DM_DRIVER_GET(i2c_eeprom_partition), name,
 137                            NULL, partition, NULL);
 138        }
 139
 140        return 0;
 141}
 142
 143static int i2c_eeprom_std_probe(struct udevice *dev)
 144{
 145        u8 test_byte;
 146        int ret;
 147        struct i2c_eeprom_drv_data *data =
 148                (struct i2c_eeprom_drv_data *)dev_get_driver_data(dev);
 149
 150        i2c_set_chip_offset_len(dev, data->offset_len);
 151        i2c_set_chip_addr_offset_mask(dev, data->addr_offset_mask);
 152
 153        /* Verify that the chip is functional */
 154        /*
 155         * Not all eeproms start from offset 0. Valid offset is available
 156         * in the platform data struct.
 157         */
 158        ret = i2c_eeprom_read(dev, data->start_offset, &test_byte, 1);
 159        if (ret)
 160                return -ENODEV;
 161
 162        return 0;
 163}
 164
 165static const struct i2c_eeprom_drv_data eeprom_data = {
 166        .size = 0,
 167        .pagesize = 1,
 168        .addr_offset_mask = 0,
 169        .offset_len = 1,
 170};
 171
 172static const struct i2c_eeprom_drv_data mc24aa02e48_data = {
 173        .size = 256,
 174        .pagesize = 8,
 175        .addr_offset_mask = 0,
 176        .offset_len = 1,
 177};
 178
 179static const struct i2c_eeprom_drv_data atmel24c01a_data = {
 180        .size = 128,
 181        .pagesize = 8,
 182        .addr_offset_mask = 0,
 183        .offset_len = 1,
 184};
 185
 186static const struct i2c_eeprom_drv_data atmel24c02_data = {
 187        .size = 256,
 188        .pagesize = 8,
 189        .addr_offset_mask = 0,
 190        .offset_len = 1,
 191};
 192
 193static const struct i2c_eeprom_drv_data atmel24c04_data = {
 194        .size = 512,
 195        .pagesize = 16,
 196        .addr_offset_mask = 0x1,
 197        .offset_len = 1,
 198};
 199
 200static const struct i2c_eeprom_drv_data atmel24c08_data = {
 201        .size = 1024,
 202        .pagesize = 16,
 203        .addr_offset_mask = 0x3,
 204        .offset_len = 1,
 205};
 206
 207static const struct i2c_eeprom_drv_data atmel24c08a_data = {
 208        .size = 1024,
 209        .pagesize = 16,
 210        .addr_offset_mask = 0x3,
 211        .offset_len = 1,
 212};
 213
 214static const struct i2c_eeprom_drv_data atmel24c16a_data = {
 215        .size = 2048,
 216        .pagesize = 16,
 217        .addr_offset_mask = 0x7,
 218        .offset_len = 1,
 219};
 220
 221static const struct i2c_eeprom_drv_data atmel24mac402_data = {
 222        .size = 256,
 223        .pagesize = 16,
 224        .addr_offset_mask = 0,
 225        .offset_len = 1,
 226        .start_offset = 0x80,
 227};
 228
 229static const struct i2c_eeprom_drv_data atmel24c32_data = {
 230        .size = 4096,
 231        .pagesize = 32,
 232        .addr_offset_mask = 0,
 233        .offset_len = 2,
 234};
 235
 236static const struct i2c_eeprom_drv_data atmel24c64_data = {
 237        .size = 8192,
 238        .pagesize = 32,
 239        .addr_offset_mask = 0,
 240        .offset_len = 2,
 241};
 242
 243static const struct i2c_eeprom_drv_data atmel24c128_data = {
 244        .size = 16384,
 245        .pagesize = 64,
 246        .addr_offset_mask = 0,
 247        .offset_len = 2,
 248};
 249
 250static const struct i2c_eeprom_drv_data atmel24c256_data = {
 251        .size = 32768,
 252        .pagesize = 64,
 253        .addr_offset_mask = 0,
 254        .offset_len = 2,
 255};
 256
 257static const struct i2c_eeprom_drv_data atmel24c512_data = {
 258        .size = 65536,
 259        .pagesize = 64,
 260        .addr_offset_mask = 0,
 261        .offset_len = 2,
 262};
 263
 264static const struct udevice_id i2c_eeprom_std_ids[] = {
 265        { .compatible = "i2c-eeprom", (ulong)&eeprom_data },
 266        { .compatible = "microchip,24aa02e48", (ulong)&mc24aa02e48_data },
 267        { .compatible = "atmel,24c01", (ulong)&atmel24c01a_data },
 268        { .compatible = "atmel,24c01a", (ulong)&atmel24c01a_data },
 269        { .compatible = "atmel,24c02", (ulong)&atmel24c02_data },
 270        { .compatible = "atmel,24c04", (ulong)&atmel24c04_data },
 271        { .compatible = "atmel,24c08", (ulong)&atmel24c08_data },
 272        { .compatible = "atmel,24c08a", (ulong)&atmel24c08a_data },
 273        { .compatible = "atmel,24c16a", (ulong)&atmel24c16a_data },
 274        { .compatible = "atmel,24mac402", (ulong)&atmel24mac402_data },
 275        { .compatible = "atmel,24c32", (ulong)&atmel24c32_data },
 276        { .compatible = "atmel,24c64", (ulong)&atmel24c64_data },
 277        { .compatible = "atmel,24c128", (ulong)&atmel24c128_data },
 278        { .compatible = "atmel,24c256", (ulong)&atmel24c256_data },
 279        { .compatible = "atmel,24c512", (ulong)&atmel24c512_data },
 280        { }
 281};
 282
 283U_BOOT_DRIVER(i2c_eeprom_std) = {
 284        .name                   = "i2c_eeprom",
 285        .id                     = UCLASS_I2C_EEPROM,
 286        .of_match               = i2c_eeprom_std_ids,
 287        .bind                   = i2c_eeprom_std_bind,
 288        .probe                  = i2c_eeprom_std_probe,
 289        .of_to_plat     = i2c_eeprom_std_of_to_plat,
 290        .priv_auto      = sizeof(struct i2c_eeprom),
 291        .ops                    = &i2c_eeprom_std_ops,
 292};
 293
 294struct i2c_eeprom_partition {
 295        u32 offset;
 296        u32 size;
 297};
 298
 299static int i2c_eeprom_partition_probe(struct udevice *dev)
 300{
 301        return 0;
 302}
 303
 304static int i2c_eeprom_partition_of_to_plat(struct udevice *dev)
 305{
 306        struct i2c_eeprom_partition *priv = dev_get_priv(dev);
 307        u32 reg[2];
 308        int ret;
 309
 310        ret = dev_read_u32_array(dev, "reg", reg, 2);
 311        if (ret)
 312                return ret;
 313
 314        if (!reg[1])
 315                return -EINVAL;
 316
 317        priv->offset = reg[0];
 318        priv->size = reg[1];
 319
 320        debug("%s: base %x, size %x\n", __func__, priv->offset, priv->size);
 321
 322        return 0;
 323}
 324
 325static int i2c_eeprom_partition_read(struct udevice *dev, int offset,
 326                                     u8 *buf, int size)
 327{
 328        struct i2c_eeprom_partition *priv = dev_get_priv(dev);
 329        struct udevice *parent = dev_get_parent(dev);
 330
 331        if (!parent)
 332                return -ENODEV;
 333        if (offset + size > priv->size)
 334                return -EINVAL;
 335
 336        return i2c_eeprom_read(parent, offset + priv->offset, buf, size);
 337}
 338
 339static int i2c_eeprom_partition_write(struct udevice *dev, int offset,
 340                                      const u8 *buf, int size)
 341{
 342        struct i2c_eeprom_partition *priv = dev_get_priv(dev);
 343        struct udevice *parent = dev_get_parent(dev);
 344
 345        if (!parent)
 346                return -ENODEV;
 347        if (offset + size > priv->size)
 348                return -EINVAL;
 349
 350        return i2c_eeprom_write(parent, offset + priv->offset, (uint8_t *)buf,
 351                                size);
 352}
 353
 354static int i2c_eeprom_partition_size(struct udevice *dev)
 355{
 356        struct i2c_eeprom_partition *priv = dev_get_priv(dev);
 357
 358        return priv->size;
 359}
 360
 361static const struct i2c_eeprom_ops i2c_eeprom_partition_ops = {
 362        .read   = i2c_eeprom_partition_read,
 363        .write  = i2c_eeprom_partition_write,
 364        .size   = i2c_eeprom_partition_size,
 365};
 366
 367U_BOOT_DRIVER(i2c_eeprom_partition) = {
 368        .name                   = "i2c_eeprom_partition",
 369        .id                     = UCLASS_I2C_EEPROM,
 370        .probe                  = i2c_eeprom_partition_probe,
 371        .of_to_plat     = i2c_eeprom_partition_of_to_plat,
 372        .priv_auto      = sizeof(struct i2c_eeprom_partition),
 373        .ops                    = &i2c_eeprom_partition_ops,
 374};
 375
 376UCLASS_DRIVER(i2c_eeprom) = {
 377        .id             = UCLASS_I2C_EEPROM,
 378        .name           = "i2c_eeprom",
 379};
 380