82fb9590f6126524ce8d63a76290df2ab1f706b9
[bertos.git] / bertos / drv / eeprom.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2003, 2004, 2005, 2010 Develer S.r.l. (http://www.develer.com/)
30  *
31  * -->
32  *
33  * \brief Driver for the 24xx16 and 24xx256 I2C EEPROMS (implementation)
34  *
35  * \author Stefano Fedrigo <aleph@develer.com>
36  * \author Bernie Innocenti <bernie@codewiz.org>
37  */
38
39 #include "eeprom.h"
40
41 #include "cfg/cfg_i2c.h"
42 #include "cfg/cfg_eeprom.h"
43
44 /* Define logging setting (for cfg/log.h module). */
45 #define LOG_LEVEL         EEPROM_LOG_LEVEL
46 #define LOG_FORMAT        EEPROM_LOG_FORMAT
47 #include <cfg/log.h>
48 #include <cfg/debug.h>
49 #include <cfg/macros.h>  // MIN()
50
51 #include <cpu/attr.h>
52
53 #include <drv/i2c.h>
54
55 #include <string.h>  // memset()
56
57 /**
58  * EEPROM ID code
59  */
60 #define EEPROM_ID  0xA0
61
62 /**
63  * This macros form the correct slave address for EEPROMs
64  */
65 #define EEPROM_ADDR(x) (EEPROM_ID | (((uint8_t)((x) & 0x07)) << 1))
66
67
68 /**
69  * Array used to describe EEPROM memory devices currently supported.
70  */
71 static const EepromInfo mem_info[] =
72 {
73         {
74                 /* 24XX08 */
75                 .has_dev_addr = false,
76                 .blk_size = 0x10,
77                 .e2_size = 0x400,
78         },
79         {
80                 /* 24XX16 */
81                 .has_dev_addr = false,
82                 .blk_size = 0x10,
83                 .e2_size = 0x800,
84         },
85         {
86                 /* 24XX32 */
87                 .has_dev_addr = true,
88                 .blk_size = 0x20,
89                 .e2_size = 0x1000,
90         },
91         {
92                 /* 24XX64 */
93                 .has_dev_addr = true,
94                 .blk_size = 0x20,
95                 .e2_size = 0x2000,
96         },
97         {
98                 /* 24XX128 */
99                 .has_dev_addr = true,
100                 .blk_size = 0x40,
101                 .e2_size = 0x4000,
102         },
103         {
104                 /* 24XX256 */
105                 .has_dev_addr = true,
106                 .blk_size = 0x40,
107                 .e2_size = 0x8000,
108         },
109         {
110                 /* 24XX512 */
111                 .has_dev_addr = true,
112                 .blk_size = 0x80,
113                 .e2_size = 0x10000,
114         },
115         {
116                 /* 24XX1024 */
117                 .has_dev_addr = true,
118                 .blk_size = 0x100,
119                 .e2_size = 0x20000,
120         },
121
122         /* Add other memories here */
123 };
124
125 STATIC_ASSERT(countof(mem_info) == EEPROM_CNT);
126
127 #define CHUNCK_SIZE     16
128
129 /**
130  * Erase EEPROM.
131  * \param eep is the Kblock context.
132  * \param addr eeprom address where start to erase
133  * \param size number of byte to erase
134  */
135 bool eeprom_erase(Eeprom *eep, e2addr_t addr, e2_size_t size)
136 {
137         uint8_t tmp[CHUNCK_SIZE] = { [0 ... (CHUNCK_SIZE - 1)] = 0xFF };
138
139         while (size)
140         {
141                 block_idx_t idx = addr / eep->blk.blk_size;
142                 size_t offset = addr % eep->blk.blk_size;
143                 size_t count = MIN(size, (e2_size_t)CHUNCK_SIZE);
144                 size_t ret_len = eep->blk.priv.vt->writeDirect((KBlock *)eep, idx, tmp, offset, count);
145                 size -= ret_len;
146                 addr += ret_len;
147
148                 if (ret_len != count)
149                         return false;
150         }
151         return true;
152 }
153
154 /**
155  * Verify EEPROM.
156  * \param eep is the Kblock context.
157  * \param addr eeprom address where start to verify.
158  * \param buf buffer of data to compare with eeprom data read.
159  * \param size number of byte to verify.
160  */
161 bool eeprom_verify(Eeprom *eep, e2addr_t addr, const void *buf, size_t size)
162 {
163     uint8_t verify_buf[CHUNCK_SIZE];
164         while (size)
165         {
166                 block_idx_t idx = addr / eep->blk.blk_size;
167                 size_t offset = addr % eep->blk.blk_size;
168                 size_t count = MIN(size, (size_t)CHUNCK_SIZE);
169
170                 size_t ret_len = eep->blk.priv.vt->readDirect((KBlock *)eep, idx, verify_buf, offset, count);
171
172                 if (ret_len != count)
173                 {
174                         LOG_ERR("Verify read fail.\n");
175                         return false;
176                 }
177
178                 if (memcmp(buf, verify_buf, ret_len) != 0)
179                 {
180                         LOG_ERR("Data mismatch!\n");
181                         return false;
182                 }
183
184                 size -= ret_len;
185                 addr += ret_len;
186                 buf = ((const char *)buf) + ret_len;
187         }
188         return true;
189 }
190
191
192 static size_t eeprom_write(KBlock *blk, block_idx_t idx, const void *buf, size_t offset, size_t size)
193 {
194         Eeprom *eep = EEPROM_CAST_KBLOCK(blk);
195         e2dev_addr_t dev_addr;
196         uint8_t addr_buf[2];
197         uint8_t addr_len;
198         uint32_t abs_addr = blk->blk_size * idx + offset;
199
200         STATIC_ASSERT(countof(addr_buf) <= sizeof(e2addr_t));
201
202         /* clamp size to memory limit (otherwise may roll back) */
203         ASSERT(idx < blk->priv.blk_start + blk->blk_cnt);
204         size = MIN(size, blk->blk_size - offset);
205
206         if (mem_info[eep->type].has_dev_addr)
207         {
208                 dev_addr = eep->addr;
209                 addr_len = 2;
210         }
211         else
212         {
213                 dev_addr = (e2dev_addr_t)((abs_addr >> 8) & 0x07);
214                 addr_len = 1;
215         }
216
217         if (mem_info[eep->type].has_dev_addr)
218         {
219                 addr_buf[0] = (abs_addr >> 8) & 0xFF;
220                 addr_buf[1] = (abs_addr & 0xFF);
221         }
222         else
223         {
224                 dev_addr = (e2dev_addr_t)((abs_addr >> 8) & 0x07);
225                 addr_buf[0] = (abs_addr & 0xFF);
226         }
227
228         i2c_start_w(eep->i2c, EEPROM_ADDR(dev_addr),  addr_len + size, I2C_STOP);
229         i2c_write(eep->i2c, addr_buf, addr_len);
230         i2c_write(eep->i2c, buf, size);
231
232         if (i2c_error(eep->i2c))
233                 return 0;
234
235         return size;
236 }
237
238 static size_t eeprom_readDirect(struct KBlock *_blk, block_idx_t idx, void *_buf, size_t offset, size_t size)
239 {
240         Eeprom *blk = EEPROM_CAST_KBLOCK(_blk);
241         uint8_t addr_buf[2];
242         uint8_t addr_len;
243         size_t rd_len = 0;
244         uint8_t *buf = (uint8_t *)_buf;
245         uint32_t abs_addr = mem_info[blk->type].blk_size * idx + offset;
246
247         STATIC_ASSERT(countof(addr_buf) <= sizeof(e2addr_t));
248
249         /* clamp size to memory limit (otherwise may roll back) */
250         ASSERT(idx < blk->blk.priv.blk_start + blk->blk.blk_cnt);
251         size = MIN(size, blk->blk.blk_size - offset);
252
253         e2dev_addr_t dev_addr;
254         if (mem_info[blk->type].has_dev_addr)
255         {
256                 dev_addr = blk->addr;
257                 addr_len = 2;
258                 addr_buf[0] = (abs_addr >> 8) & 0xFF;
259                 addr_buf[1] = (abs_addr & 0xFF);
260         }
261         else
262         {
263                 dev_addr = (e2dev_addr_t)((abs_addr >> 8) & 0x07);
264                 addr_len = 1;
265                 addr_buf[0] = (abs_addr & 0xFF);
266         }
267
268
269         i2c_start_w(blk->i2c, EEPROM_ADDR(dev_addr),  addr_len, I2C_NOSTOP);
270         i2c_write(blk->i2c, addr_buf, addr_len);
271
272         i2c_start_r(blk->i2c, EEPROM_ADDR(dev_addr), size, I2C_STOP);
273         i2c_read(blk->i2c, buf, size);
274
275         if (i2c_error(blk->i2c))
276                    return rd_len;
277
278         rd_len += size;
279
280         return rd_len;
281 }
282
283 static size_t eeprom_writeDirect(KBlock *blk, block_idx_t idx, const void *buf, size_t offset, size_t size)
284 {
285         Eeprom *eep = EEPROM_CAST_KBLOCK(blk);
286         if (!eep->verify)
287                 return eeprom_write(blk, idx, buf, offset, size);
288         else
289         {
290                 int retries = 5;
291                 while (retries--)
292                 {
293                         uint8_t verify_buf[CHUNCK_SIZE];
294                         size_t wr_len = 0;
295                         size_t len = 0;
296                         while (size)
297                         {
298                                 /* Split read in smaller pieces */
299                                 size_t count = MIN(size, (size_t)CHUNCK_SIZE);
300                                 if ((wr_len = eeprom_write(blk, idx, buf, offset, count)) != 0)
301                                 {
302                                         if (eeprom_readDirect(blk, idx, verify_buf, offset, count) != wr_len)
303                                         {
304                                                 LOG_ERR("Verify read fail.\n");
305                                                 return 0;
306                                         }
307                                         else if (memcmp(buf, verify_buf, wr_len) != 0)
308                                         {
309                                                 LOG_ERR("Data mismatch!\n");
310                                                 continue;
311                                         }
312                                 }
313                                 else
314                                 {
315                                         LOG_ERR("Write fail.\n");
316                                         return 0;
317                                 }
318                                 size -= wr_len;
319                                 len += wr_len;
320                                 buf = ((const char *)buf) + wr_len;
321                         }
322                         return len;
323                 }
324         }
325
326         return 0;
327 }
328
329 static int kblockEeprom_dummy(UNUSED_ARG(struct KBlock *,b))
330 {
331         return 0;
332 }
333
334
335 static const KBlockVTable eeprom_unbuffered_vt =
336 {
337         .readDirect = eeprom_readDirect,
338         .writeDirect = eeprom_writeDirect,
339
340         .error = kblockEeprom_dummy,
341         .clearerr = (kblock_clearerr_t)kblockEeprom_dummy,
342 };
343
344 /**
345  * Initialize EEPROM module.
346  * \param eep is the Kblock context.
347  * \param type is the eeprom device we want to initialize (\see EepromType)
348  * \param i2c context for i2c channel
349  * \param addr is the i2c devide address (usually pins A0, A1, A2).
350  * \param verify enable the write check.
351  */
352 void eeprom_init_5(Eeprom *eep, I2c *i2c, EepromType type, e2dev_addr_t addr, bool verify)
353 {
354         ASSERT(type < EEPROM_CNT);
355
356         memset(eep, 0, sizeof(*eep));
357         DB(eep->blk.priv.type = KBT_EEPROM);
358
359         eep->type = type;
360         eep->addr = addr;
361         eep->i2c = i2c;
362         eep->verify = verify;
363
364         eep->blk.blk_size = mem_info[type].blk_size;
365         eep->blk.blk_cnt = mem_info[type].e2_size / mem_info[type].blk_size;
366         eep->blk.priv.flags |= KB_PARTIAL_WRITE;
367         eep->blk.priv.vt = &eeprom_unbuffered_vt;
368 }
369
370