source: rtems/cpukit/dev/i2c/eeprom.c @ 08135c85

5
Last change on this file since 08135c85 was d6f0ca64, checked in by Sebastian Huber <sebastian.huber@…>, on 11/26/14 at 07:24:39

i2c: Avoid undefined right shift operation

  • Property mode set to 100644
File size: 5.3 KB
Line 
1/**
2 * @file
3 *
4 * @brief EEPROM Driver Implementation
5 *
6 * @ingroup I2CEEPROM
7 */
8
9/*
10 * Copyright (c) 2014 embedded brains GmbH.  All rights reserved.
11 *
12 *  embedded brains GmbH
13 *  Dornierstr. 4
14 *  82178 Puchheim
15 *  Germany
16 *  <rtems@embedded-brains.de>
17 *
18 * The license and distribution terms for this file may be
19 * found in the file LICENSE in this distribution or at
20 * http://www.rtems.org/license/LICENSE.
21 */
22
23#if HAVE_CONFIG_H
24  #include "config.h"
25#endif
26
27#include <dev/i2c/eeprom.h>
28
29#include <string.h>
30
31#define EEPROM_MAX_ADDRESS_BYTES 4
32
33#define EEPROM_MAX_PAGE_SIZE 128
34
35typedef struct {
36  i2c_dev base;
37  uint16_t address_bytes;
38  uint16_t page_size;
39  uint32_t size;
40  uint16_t i2c_address_mask;
41  uint16_t i2c_address_shift;
42  rtems_interval program_timeout;
43} eeprom;
44
45static uint16_t eeprom_i2c_addr(eeprom *dev, uint32_t off)
46{
47  return dev->base.address
48    | ((off >> dev->i2c_address_shift) & dev->i2c_address_mask);
49}
50
51static ssize_t eeprom_read(
52  i2c_dev *base,
53  void *buf,
54  size_t n,
55  off_t offset
56)
57{
58  eeprom *dev = (eeprom *) base;
59  off_t avail = dev->size - offset;
60  uint32_t off = (uint32_t) offset;
61  uint8_t *in = buf;
62  size_t todo;
63
64  if (avail <= 0) {
65    return 0;
66  }
67
68  if (n > avail) {
69    n = (size_t) avail;
70  }
71
72  todo = n;
73
74  while (todo > 0) {
75    uint16_t i2c_addr = eeprom_i2c_addr(dev, off);
76
77    /*
78     * Limit the transfer size so that it can be stored in 8-bits.  This may
79     * help some bus controllers.
80     */
81    uint16_t cur = (uint16_t) (todo < 255 ?  todo : 255);
82
83    uint8_t addr[EEPROM_MAX_ADDRESS_BYTES] = {
84      (uint8_t) off,
85      (uint8_t) (off >> 8),
86      (uint8_t) (off >> 16),
87      (uint8_t) (off >> 24)
88    };
89    i2c_msg msgs[2] = {
90      {
91        .addr = i2c_addr,
92        .flags = 0,
93        .len = dev->address_bytes,
94        .buf = &addr[0]
95      }, {
96        .addr = i2c_addr,
97        .flags = I2C_M_RD,
98        .buf = in,
99        .len = cur
100      }
101    };
102    int err;
103
104    err = i2c_bus_transfer(dev->base.bus, &msgs[0], RTEMS_ARRAY_SIZE(msgs));
105    if (err != 0) {
106      return err;
107    }
108
109    todo -= cur;
110    off += cur;
111    in += cur;
112  }
113
114  return (ssize_t) n;
115}
116
117static ssize_t eeprom_write(
118  i2c_dev *base,
119  const void *buf,
120  size_t n,
121  off_t offset
122)
123{
124  eeprom *dev = (eeprom *) base;
125  off_t avail = dev->size - offset;
126  uint32_t off = (uint32_t) offset;
127  const uint8_t *out = buf;
128  size_t todo;
129
130  if (avail <= 0) {
131    return 0;
132  }
133
134  if (n > avail) {
135    n = (size_t) avail;
136  }
137
138  todo = n;
139
140  while (todo > 0) {
141    uint16_t i2c_addr = eeprom_i2c_addr(dev, off);
142    uint16_t rem = dev->page_size - (off & (dev->page_size - 1));
143    uint16_t cur = (uint16_t) (todo < rem ? todo : rem);
144    uint8_t addr[EEPROM_MAX_ADDRESS_BYTES] = {
145      (uint8_t) off,
146      (uint8_t) (off >> 8),
147      (uint8_t) (off >> 16),
148      (uint8_t) (off >> 24)
149    };
150    i2c_msg msgs[2] = {
151      {
152        .addr = i2c_addr,
153        .flags = 0,
154        .len = dev->address_bytes,
155        .buf = &addr[0]
156      }, {
157        .addr = i2c_addr,
158        .flags = I2C_M_NOSTART,
159        .buf = RTEMS_DECONST(uint8_t *, out),
160        .len = cur
161      }
162    };
163    uint8_t in[EEPROM_MAX_PAGE_SIZE];
164    int err;
165    ssize_t m;
166    rtems_interval timeout;
167
168    err = i2c_bus_transfer(dev->base.bus, &msgs[0], RTEMS_ARRAY_SIZE(msgs));
169    if (err != 0) {
170      return err;
171    }
172
173    timeout = rtems_clock_tick_later(dev->program_timeout);
174
175    do {
176      m = eeprom_read(&dev->base, &in[0], cur, off);
177    } while (m != cur && rtems_clock_tick_before(timeout));
178
179    if (m != cur) {
180      return -ETIMEDOUT;
181    }
182
183    if (memcmp(&in[0], &out[0], cur) != 0) {
184      return -EIO;
185    }
186
187    todo -= cur;
188    off += cur;
189    out += cur;
190  }
191
192  return (ssize_t) n;
193}
194
195static off_t eeprom_get_size(i2c_dev *base)
196{
197  eeprom *dev = (eeprom *) base;
198
199  return dev->size;
200}
201
202static blksize_t eeprom_get_block_size(i2c_dev *base)
203{
204  eeprom *dev = (eeprom *) base;
205
206  return dev->page_size;
207}
208
209int i2c_dev_register_eeprom(
210  const char *bus_path,
211  const char *dev_path,
212  uint16_t i2c_address,
213  uint16_t address_bytes,
214  uint16_t page_size_in_bytes,
215  uint32_t size_in_bytes,
216  uint32_t program_timeout_in_ms
217)
218{
219  uint32_t extra_address;
220  eeprom *dev;
221
222  if (address_bytes > EEPROM_MAX_ADDRESS_BYTES) {
223    rtems_set_errno_and_return_minus_one(ERANGE);
224  } else if (address_bytes == EEPROM_MAX_ADDRESS_BYTES) {
225    extra_address = 0;
226  } else {
227    extra_address = size_in_bytes >> (8 * address_bytes);
228  }
229
230  if (extra_address != 0 && (extra_address & (extra_address - 1)) != 0) {
231    rtems_set_errno_and_return_minus_one(EINVAL);
232  }
233
234  if (page_size_in_bytes > EEPROM_MAX_PAGE_SIZE) {
235    page_size_in_bytes = EEPROM_MAX_PAGE_SIZE;
236  }
237
238  if (program_timeout_in_ms == 0) {
239    program_timeout_in_ms = 1000;
240  }
241
242  dev = (eeprom *)
243    i2c_dev_alloc_and_init(sizeof(*dev), bus_path, i2c_address);
244  if (dev == NULL) {
245    return -1;
246  }
247
248  dev->base.read = eeprom_read;
249  dev->base.write = eeprom_write;
250  dev->base.get_size = eeprom_get_size;
251  dev->base.get_block_size = eeprom_get_block_size;
252  dev->address_bytes = address_bytes;
253  dev->page_size = page_size_in_bytes;
254  dev->size = size_in_bytes;
255  dev->program_timeout = RTEMS_MILLISECONDS_TO_TICKS(program_timeout_in_ms);
256
257  if (extra_address != 0) {
258    dev->i2c_address_mask = extra_address - 1;
259    dev->i2c_address_shift = (uint16_t) (8 * address_bytes);
260  }
261
262  return i2c_dev_register(&dev->base, dev_path);
263}
Note: See TracBrowser for help on using the repository browser.