aboutsummaryrefslogtreecommitdiff
path: root/lib/chibios-contrib/os/hal/src/hal_ee24xx.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chibios-contrib/os/hal/src/hal_ee24xx.c')
-rw-r--r--lib/chibios-contrib/os/hal/src/hal_ee24xx.c356
1 files changed, 356 insertions, 0 deletions
diff --git a/lib/chibios-contrib/os/hal/src/hal_ee24xx.c b/lib/chibios-contrib/os/hal/src/hal_ee24xx.c
new file mode 100644
index 000000000..a415f2de2
--- /dev/null
+++ b/lib/chibios-contrib/os/hal/src/hal_ee24xx.c
@@ -0,0 +1,356 @@
1/*
2 Copyright (c) 2013 Timon Wong
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
21*/
22
23/*
24 Copyright 2012 Uladzimir Pylinski aka barthess.
25 You may use this work without restrictions, as long as this notice is included.
26 The work is provided "as is" without warranty of any kind, neither express nor implied.
27*/
28
29/*****************************************************************************
30 * DATASHEET NOTES
31 *****************************************************************************
32Write cycle time (byte or page) - 5 ms
33
34Note:
35 Page write operations are limited to writing bytes within a single physical
36 page, regardless of the number of bytes actually being written. Physical page
37 boundaries start at addresses that are integer multiples of the page buffer
38 size (or page size and end at addresses that are integer multiples of
39 [page size]. If a Page Write command attempts to write across a physical
40 page boundary, the result is that the data wraps around to the beginning of
41 the current page (overwriting data previously stored there), instead of
42 being written to the next page as might be expected.
43*********************************************************************/
44
45#include "hal_ee24xx.h"
46#include <string.h>
47
48#if (defined(HAL_USE_EEPROM) && HAL_USE_EEPROM && EEPROM_USE_EE24XX) || defined(__DOXYGEN__)
49
50/*
51 ******************************************************************************
52 * DEFINES
53 ******************************************************************************
54 */
55/*
56#if defined(SAM7_PLATFORM)
57#define EEPROM_I2C_CLOCK (MCK / (((i2cp->config->cwgr & 0xFF) + ((i2cp->config->cwgr >> 8) & 0xFF)) * (1 << ((i2cp->config->cwgr >> 16) & 7)) + 6))
58#else
59#define EEPROM_I2C_CLOCK (i2cp->config->clock_speed)
60#endif
61*/
62#define EEPROM_I2C_CLOCK 400000
63
64/*
65 ******************************************************************************
66 * EXTERNS
67 ******************************************************************************
68 */
69
70/*
71 ******************************************************************************
72 * GLOBAL VARIABLES
73 ******************************************************************************
74 */
75
76/*
77 *******************************************************************************
78 * LOCAL FUNCTIONS
79 *******************************************************************************
80 */
81/**
82 * @brief Split one uint16_t address to two uint8_t.
83 *
84 * @param[in] txbuf pointer to driver transmit buffer
85 * @param[in] addr uint16_t address
86 */
87#define eeprom_split_addr(txbuf, addr){ \
88 (txbuf)[0] = ((uint8_t)((addr >> 8) & 0xFF)); \
89 (txbuf)[1] = ((uint8_t)(addr & 0xFF)); \
90 }
91
92/*
93 *******************************************************************************
94 * EXPORTED FUNCTIONS
95 *******************************************************************************
96 */
97
98/**
99 * @brief Calculates requred timeout.
100 */
101static systime_t calc_timeout(I2CDriver *i2cp, size_t txbytes, size_t rxbytes) {
102 (void)i2cp;
103 const uint32_t bitsinbyte = 10;
104 uint32_t tmo;
105 tmo = ((txbytes + rxbytes + 1) * bitsinbyte * 1000);
106 tmo /= EEPROM_I2C_CLOCK;
107 tmo += 10; /* some additional milliseconds to be safer */
108 return TIME_MS2I(tmo);
109}
110
111/**
112 * @brief EEPROM read routine.
113 *
114 * @param[in] eepcfg pointer to configuration structure of eeprom file
115 * @param[in] offset addres of 1-st byte to be read
116 * @param[in] data pointer to buffer with data to be written
117 * @param[in] len number of bytes to be red
118 */
119static msg_t eeprom_read(const I2CEepromFileConfig *eepcfg,
120 uint32_t offset, uint8_t *data, size_t len) {
121
122 msg_t status = MSG_RESET;
123 systime_t tmo = calc_timeout(eepcfg->i2cp, 2, len);
124
125 osalDbgAssert(((len <= eepcfg->size) && ((offset + len) <= eepcfg->size)),
126 "out of device bounds");
127
128 eeprom_split_addr(eepcfg->write_buf, (offset + eepcfg->barrier_low));
129
130#if I2C_USE_MUTUAL_EXCLUSION
131 i2cAcquireBus(eepcfg->i2cp);
132#endif
133
134 status = i2cMasterTransmitTimeout(eepcfg->i2cp, eepcfg->addr,
135 eepcfg->write_buf, 2, data, len, tmo);
136
137#if I2C_USE_MUTUAL_EXCLUSION
138 i2cReleaseBus(eepcfg->i2cp);
139#endif
140
141 return status;
142}
143
144/**
145 * @brief EEPROM write routine.
146 * @details Function writes data to EEPROM.
147 * @pre Data must be fit to single EEPROM page.
148 *
149 * @param[in] eepcfg pointer to configuration structure of eeprom file
150 * @param[in] offset addres of 1-st byte to be write
151 * @param[in] data pointer to buffer with data to be written
152 * @param[in] len number of bytes to be written
153 */
154static msg_t eeprom_write(const I2CEepromFileConfig *eepcfg, uint32_t offset,
155 const uint8_t *data, size_t len) {
156 msg_t status = MSG_RESET;
157 systime_t tmo = calc_timeout(eepcfg->i2cp, (len + 2), 0);
158
159 osalDbgAssert(((len <= eepcfg->size) && ((offset + len) <= eepcfg->size)),
160 "out of device bounds");
161 osalDbgAssert((((offset + eepcfg->barrier_low) / eepcfg->pagesize) ==
162 (((offset + eepcfg->barrier_low) + len - 1) / eepcfg->pagesize)),
163 "data can not be fitted in single page");
164
165 /* write address bytes */
166 eeprom_split_addr(eepcfg->write_buf, (offset + eepcfg->barrier_low));
167 /* write data bytes */
168 memcpy(&(eepcfg->write_buf[2]), data, len);
169
170#if I2C_USE_MUTUAL_EXCLUSION
171 i2cAcquireBus(eepcfg->i2cp);
172#endif
173
174 status = i2cMasterTransmitTimeout(eepcfg->i2cp, eepcfg->addr,
175 eepcfg->write_buf, (len + 2), NULL, 0, tmo);
176
177#if I2C_USE_MUTUAL_EXCLUSION
178 i2cReleaseBus(eepcfg->i2cp);
179#endif
180
181 /* wait until EEPROM process data */
182 chThdSleep(eepcfg->write_time);
183
184 return status;
185}
186
187/**
188 * @brief Determines and returns size of data that can be processed
189 */
190static size_t __clamp_size(void *ip, size_t n) {
191
192 if (((size_t)eepfs_getposition(ip) + n) > (size_t)eepfs_getsize(ip))
193 return eepfs_getsize(ip) - eepfs_getposition(ip);
194 else
195 return n;
196}
197
198/**
199 * @brief Write data that can be fitted in one page boundary
200 */
201static msg_t __fitted_write(void *ip, const uint8_t *data, size_t len, uint32_t *written) {
202
203 msg_t status = MSG_RESET;
204
205 osalDbgAssert(len > 0, "len must be greater than 0");
206
207 status = eeprom_write(((I2CEepromFileStream *)ip)->cfg,
208 eepfs_getposition(ip), data, len);
209 if (status == MSG_OK) {
210 *written += len;
211 eepfs_lseek(ip, eepfs_getposition(ip) + len);
212 }
213
214 return status;
215}
216
217/**
218 * @brief Write data to EEPROM.
219 * @details Only one EEPROM page can be written at once. So function
220 * splits large data chunks in small EEPROM transactions if needed.
221 * @note To achieve the maximum efficiency use write operations
222 * aligned to EEPROM page boundaries.
223 */
224static size_t write(void *ip, const uint8_t *bp, size_t n) {
225
226 size_t len = 0; /* bytes to be written per transaction */
227 uint32_t written = 0; /* total bytes successfully written */
228 uint16_t pagesize;
229 uint32_t firstpage;
230 uint32_t lastpage;
231
232 osalDbgCheck((ip != NULL) && (((EepromFileStream *)ip)->vmt != NULL));
233
234 if (n == 0)
235 return 0;
236
237 n = __clamp_size(ip, n);
238 if (n == 0)
239 return 0;
240
241 pagesize = ((EepromFileStream *)ip)->cfg->pagesize;
242 firstpage = (((EepromFileStream *)ip)->cfg->barrier_low +
243 eepfs_getposition(ip)) / pagesize;
244 lastpage = (((EepromFileStream *)ip)->cfg->barrier_low +
245 eepfs_getposition(ip) + n - 1) / pagesize;
246
247 /* data fits in single page */
248 if (firstpage == lastpage) {
249 len = n;
250 __fitted_write(ip, bp, len, &written);
251 return written;
252 }
253
254 else {
255 /* write first piece of data to first page boundary */
256 len = ((firstpage + 1) * pagesize) - eepfs_getposition(ip);
257 len -= ((EepromFileStream *)ip)->cfg->barrier_low;
258 if (__fitted_write(ip, bp, len, &written) != MSG_OK)
259 return written;
260 bp += len;
261
262 /* now write page sized blocks (zero or more) */
263 while ((n - written) > pagesize) {
264 len = pagesize;
265 if (__fitted_write(ip, bp, len, &written) != MSG_OK)
266 return written;
267 bp += len;
268 }
269
270 /* write tail */
271 len = n - written;
272 if (len == 0)
273 return written;
274 else {
275 __fitted_write(ip, bp, len, &written);
276 }
277 }
278
279 return written;
280}
281
282/**
283 * Read some bytes from current position in file. After successful
284 * read operation the position pointer will be increased by the number
285 * of read bytes.
286 */
287static size_t read(void *ip, uint8_t *bp, size_t n) {
288 msg_t status = MSG_OK;
289
290 osalDbgCheck((ip != NULL) && (((EepromFileStream *)ip)->vmt != NULL));
291
292 if (n == 0)
293 return 0;
294
295 n = __clamp_size(ip, n);
296 if (n == 0)
297 return 0;
298
299 /* Stupid I2C cell in STM32F1x does not allow to read single byte.
300 So we must read 2 bytes and return needed one. */
301#if defined(STM32F1XX_I2C)
302 if (n == 1) {
303 uint8_t __buf[2];
304 /* if NOT last byte of file requested */
305 if ((eepfs_getposition(ip) + 1) < eepfs_getsize(ip)) {
306 if (read(ip, __buf, 2) == 2) {
307 eepfs_lseek(ip, (eepfs_getposition(ip) + 1));
308 bp[0] = __buf[0];
309 return 1;
310 }
311 else
312 return 0;
313 }
314 else {
315 eepfs_lseek(ip, (eepfs_getposition(ip) - 1));
316 if (read(ip, __buf, 2) == 2) {
317 eepfs_lseek(ip, (eepfs_getposition(ip) + 2));
318 bp[0] = __buf[1];
319 return 1;
320 }
321 else
322 return 0;
323 }
324 }
325#endif /* defined(STM32F1XX_I2C) */
326
327 /* call low level function */
328 status = eeprom_read(((I2CEepromFileStream *)ip)->cfg,
329 eepfs_getposition(ip), bp, n);
330 if (status != MSG_OK)
331 return 0;
332 else {
333 eepfs_lseek(ip, (eepfs_getposition(ip) + n));
334 return n;
335 }
336}
337
338static const struct EepromFileStreamVMT vmt = {
339 (size_t)0,
340 write,
341 read,
342 eepfs_put,
343 eepfs_get,
344 eepfs_close,
345 eepfs_geterror,
346 eepfs_getsize,
347 eepfs_getposition,
348 eepfs_lseek,
349};
350
351EepromDevice eepdev_24xx = {
352 EEPROM_DEV_24XX,
353 &vmt
354};
355
356#endif /* EEPROM_USE_EE24XX */