Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:11

0001 /**
0002  * @file
0003  *
0004  * @brief EEPROM Driver Implementation
0005  *
0006  * @ingroup I2CEEPROM
0007  */
0008 
0009 /*
0010  * Copyright (c) 2014 embedded brains GmbH & Co. KG
0011  *
0012  * The license and distribution terms for this file may be
0013  * found in the file LICENSE in this distribution or at
0014  * http://www.rtems.org/license/LICENSE.
0015  */
0016 
0017 #ifdef HAVE_CONFIG_H
0018 #include "config.h"
0019 #endif
0020 
0021 #include <dev/i2c/eeprom.h>
0022 
0023 #include <string.h>
0024 
0025 #define EEPROM_MAX_ADDRESS_BYTES 4
0026 
0027 #define EEPROM_MAX_PAGE_SIZE 128
0028 
0029 typedef struct {
0030   i2c_dev base;
0031   uint16_t address_bytes;
0032   uint16_t page_size;
0033   uint32_t size;
0034   uint16_t i2c_address_mask;
0035   uint16_t i2c_address_shift;
0036   rtems_interval program_timeout_in_ticks;
0037 } eeprom;
0038 
0039 static uint16_t eeprom_i2c_addr(eeprom *dev, uint32_t off)
0040 {
0041   return dev->base.address
0042     | ((off >> dev->i2c_address_shift) & dev->i2c_address_mask);
0043 }
0044 
0045 static void eeprom_set_addr(
0046   const eeprom *dev,
0047   uint32_t off,
0048   uint8_t addr[EEPROM_MAX_ADDRESS_BYTES]
0049 )
0050 {
0051   int shift = 24 - (4 - dev->address_bytes) * 8;
0052 
0053   addr[0] = (uint8_t) (off >> shift);
0054   shift -= 8;
0055   addr[1] = (uint8_t) (off >> shift);
0056   shift -= 8;
0057   addr[2] = (uint8_t) (off >> shift);
0058   shift -= 8;
0059   addr[3] = (uint8_t) (off >> shift);
0060 }
0061 
0062 static ssize_t eeprom_read(
0063   i2c_dev *base,
0064   void *buf,
0065   size_t n,
0066   off_t offset
0067 )
0068 {
0069   eeprom *dev = (eeprom *) base;
0070   off_t avail = dev->size - offset;
0071   uint32_t off = (uint32_t) offset;
0072   uint8_t *in = buf;
0073   size_t todo;
0074 
0075   if (avail <= 0) {
0076     return 0;
0077   }
0078 
0079   if (n > avail) {
0080     n = (size_t) avail;
0081   }
0082 
0083   todo = n;
0084 
0085   while (todo > 0) {
0086     uint16_t i2c_addr = eeprom_i2c_addr(dev, off);
0087 
0088     /*
0089      * Limit the transfer size so that it can be stored in 8-bits.  This may
0090      * help some bus controllers.
0091      */
0092     uint16_t cur = (uint16_t) (todo < 255 ?  todo : 255);
0093 
0094     uint8_t addr[EEPROM_MAX_ADDRESS_BYTES];
0095     i2c_msg msgs[2] = {
0096       {
0097         .addr = i2c_addr,
0098         .flags = 0,
0099         .len = dev->address_bytes,
0100         .buf = &addr[0]
0101       }, {
0102         .addr = i2c_addr,
0103         .flags = I2C_M_RD,
0104         .buf = in,
0105         .len = cur
0106       }
0107     };
0108     int err;
0109 
0110     eeprom_set_addr(dev, off, addr);
0111     err = i2c_bus_transfer(dev->base.bus, &msgs[0], RTEMS_ARRAY_SIZE(msgs));
0112     if (err != 0) {
0113       return err;
0114     }
0115 
0116     todo -= cur;
0117     off += cur;
0118     in += cur;
0119   }
0120 
0121   return (ssize_t) n;
0122 }
0123 
0124 static ssize_t eeprom_write(
0125   i2c_dev *base,
0126   const void *buf,
0127   size_t n,
0128   off_t offset
0129 )
0130 {
0131   eeprom *dev = (eeprom *) base;
0132   off_t avail = dev->size - offset;
0133   uint32_t off = (uint32_t) offset;
0134   const uint8_t *out = buf;
0135   size_t todo;
0136 
0137   if (avail <= 0) {
0138     return 0;
0139   }
0140 
0141   if (n > avail) {
0142     n = (size_t) avail;
0143   }
0144 
0145   todo = n;
0146 
0147   while (todo > 0) {
0148     uint16_t i2c_addr = eeprom_i2c_addr(dev, off);
0149     uint16_t rem = dev->page_size - (off & (dev->page_size - 1));
0150     uint16_t cur = (uint16_t) (todo < rem ? todo : rem);
0151     uint8_t addr[EEPROM_MAX_ADDRESS_BYTES];
0152     i2c_msg msgs[2] = {
0153       {
0154         .addr = i2c_addr,
0155         .flags = 0,
0156         .len = dev->address_bytes,
0157         .buf = &addr[0]
0158       }, {
0159         .addr = i2c_addr,
0160         .flags = I2C_M_NOSTART,
0161         .buf = RTEMS_DECONST(uint8_t *, out),
0162         .len = cur
0163       }
0164     };
0165     uint8_t in[EEPROM_MAX_PAGE_SIZE];
0166     int err;
0167     ssize_t m;
0168     rtems_interval timeout;
0169     bool before;
0170 
0171     eeprom_set_addr(dev, off, addr);
0172     err = i2c_bus_transfer(dev->base.bus, &msgs[0], RTEMS_ARRAY_SIZE(msgs));
0173     if (err != 0) {
0174       return err;
0175     }
0176 
0177     timeout = rtems_clock_tick_later(dev->program_timeout_in_ticks);
0178 
0179     do {
0180       before = rtems_clock_tick_before(timeout);
0181 
0182       m = eeprom_read(&dev->base, &in[0], cur, off);
0183       if (m == cur) {
0184         break;
0185       }
0186     } while (before);
0187 
0188     if (m != cur) {
0189       return -ETIMEDOUT;
0190     }
0191 
0192     if (memcmp(&in[0], &out[0], cur) != 0) {
0193       return -EIO;
0194     }
0195 
0196     todo -= cur;
0197     off += cur;
0198     out += cur;
0199   }
0200 
0201   return (ssize_t) n;
0202 }
0203 
0204 static off_t eeprom_get_size(i2c_dev *base)
0205 {
0206   eeprom *dev = (eeprom *) base;
0207 
0208   return dev->size;
0209 }
0210 
0211 static blksize_t eeprom_get_block_size(i2c_dev *base)
0212 {
0213   eeprom *dev = (eeprom *) base;
0214 
0215   return dev->page_size;
0216 }
0217 
0218 int i2c_dev_register_eeprom(
0219   const char *bus_path,
0220   const char *dev_path,
0221   uint16_t i2c_address,
0222   uint16_t address_bytes,
0223   uint16_t page_size_in_bytes,
0224   uint32_t size_in_bytes,
0225   uint32_t program_timeout_in_ms
0226 )
0227 {
0228   uint32_t extra_address;
0229   uint32_t ms_per_tick;
0230   eeprom *dev;
0231 
0232   if (address_bytes > EEPROM_MAX_ADDRESS_BYTES) {
0233     rtems_set_errno_and_return_minus_one(ERANGE);
0234   } else if (address_bytes == EEPROM_MAX_ADDRESS_BYTES) {
0235     extra_address = 0;
0236   } else {
0237     extra_address = size_in_bytes >> (8 * address_bytes);
0238   }
0239 
0240   if (extra_address != 0 && (extra_address & (extra_address - 1)) != 0) {
0241     rtems_set_errno_and_return_minus_one(EINVAL);
0242   }
0243 
0244   if (page_size_in_bytes > EEPROM_MAX_PAGE_SIZE) {
0245     page_size_in_bytes = EEPROM_MAX_PAGE_SIZE;
0246   }
0247 
0248   if (program_timeout_in_ms == 0) {
0249     program_timeout_in_ms = 1000;
0250   }
0251 
0252   dev = (eeprom *)
0253     i2c_dev_alloc_and_init(sizeof(*dev), bus_path, i2c_address);
0254   if (dev == NULL) {
0255     return -1;
0256   }
0257 
0258   dev->base.read = eeprom_read;
0259   dev->base.write = eeprom_write;
0260   dev->base.get_size = eeprom_get_size;
0261   dev->base.get_block_size = eeprom_get_block_size;
0262   dev->address_bytes = address_bytes;
0263   dev->page_size = page_size_in_bytes;
0264   dev->size = size_in_bytes;
0265   ms_per_tick = rtems_configuration_get_milliseconds_per_tick();
0266   dev->program_timeout_in_ticks = (program_timeout_in_ms + ms_per_tick - 1)
0267     / ms_per_tick + 1;
0268 
0269   if (extra_address != 0) {
0270     dev->i2c_address_mask = extra_address - 1;
0271     dev->i2c_address_shift = (uint16_t) (8 * address_bytes);
0272   }
0273 
0274   return i2c_dev_register(&dev->base, dev_path);
0275 }