aboutsummaryrefslogtreecommitdiff
path: root/lib/chibios-contrib/os/hal/src/hal_ee25xx.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chibios-contrib/os/hal/src/hal_ee25xx.c')
-rw-r--r--lib/chibios-contrib/os/hal/src/hal_ee25xx.c403
1 files changed, 403 insertions, 0 deletions
diff --git a/lib/chibios-contrib/os/hal/src/hal_ee25xx.c b/lib/chibios-contrib/os/hal/src/hal_ee25xx.c
new file mode 100644
index 000000000..2849e6680
--- /dev/null
+++ b/lib/chibios-contrib/os/hal/src/hal_ee25xx.c
@@ -0,0 +1,403 @@
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_ee25xx.h"
46#include <string.h>
47
48#if (defined(HAL_USE_EEPROM) && HAL_USE_EEPROM && EEPROM_USE_EE25XX) || defined(__DOXYGEN__)
49
50/**
51 * @name Commands of 25XX chip.
52 * @{
53 */
54#define CMD_READ 0x03 /**< @brief Read data from memory array beginning at
55 selected address. */
56#define CMD_WRITE 0x02 /**< @brief Write data to memory array beginning at
57 selected address. */
58#define CMD_WRDI 0x04 /**< Reset the write enable latch (disable write
59 operations). */
60#define CMD_WREN 0x06 /**< Set the write enable latch (enable write
61 operations). */
62#define CMD_RDSR 0x05 /**< Read STATUS register. */
63#define CMD_WRSR 0x01 /**< Write STATUS register. */
64
65/** @} */
66
67/**
68 * @name Status of 25XX chip.
69 * @{}
70 */
71#define STAT_BP1 0x08 /**< @brief Block protection (high). */
72#define STAT_BP0 0x04 /**< @brief Block protection (low). */
73#define STAT_WEL 0x02 /**< @brief Write enable latch. */
74#define STAT_WIP 0x01 /**< @brief Write-In-Progress. */
75
76/** @} */
77
78/**
79 * @brief 25XX low level write then read rountine.
80 *
81 * @param[in] eepcfg pointer to configuration structure of eeprom file.
82 * @param[in] txbuf pointer to buffer to be transfered.
83 * @param[in] txlen number of bytes to be transfered.
84 * @param[out] rxbuf pointer to buffer to be received.
85 * @param[in] rxlen number of bytes to be received.
86 */
87static void ll_25xx_transmit_receive(const SPIEepromFileConfig *eepcfg,
88 const uint8_t *txbuf, size_t txlen,
89 uint8_t *rxbuf, size_t rxlen) {
90
91#if SPI_USE_MUTUAL_EXCLUSION
92 spiAcquireBus(eepcfg->spip);
93#endif
94 spiSelect(eepcfg->spip);
95 spiSend(eepcfg->spip, txlen, txbuf);
96 if (rxlen) /* Check if receive is needed. */
97 spiReceive(eepcfg->spip, rxlen, rxbuf);
98 spiUnselect(eepcfg->spip);
99
100#if SPI_USE_MUTUAL_EXCLUSION
101 spiReleaseBus(eepcfg->spip);
102#endif
103}
104
105/**
106 * @brief Check whether the device is busy (writing in progress).
107 *
108 * @param[in] eepcfg pointer to configuration structure of eeprom file.
109 * @return @p true on busy.
110 */
111static bool ll_eeprom_is_busy(const SPIEepromFileConfig *eepcfg) {
112
113 uint8_t cmd = CMD_RDSR;
114 uint8_t stat;
115 ll_25xx_transmit_receive(eepcfg, &cmd, 1, &stat, 1);
116 if (stat & STAT_WIP)
117 return TRUE;
118 return FALSE;
119}
120
121/**
122 * @brief Lock device.
123 *
124 * @param[in] eepcfg pointer to configuration structure of eeprom file.
125 */
126static void ll_eeprom_lock(const SPIEepromFileConfig *eepcfg) {
127
128 uint8_t cmd = CMD_WRDI;
129 ll_25xx_transmit_receive(eepcfg, &cmd, 1, NULL, 0);
130}
131
132/**
133 * @brief Unlock device.
134 *
135 * @param[in] eepcfg pointer to configuration structure of eeprom file.
136 */
137static void ll_eeprom_unlock(const SPIEepromFileConfig *eepcfg) {
138
139 uint8_t cmd = CMD_WREN;
140 ll_25xx_transmit_receive(eepcfg, &cmd, 1, NULL, 0);
141}
142
143/**
144 * @brief Prepare byte sequence for command and address
145 *
146 * @param[in] seq pointer to first 3byte sequence
147 * @param[in] size size of the eeprom device
148 * @param[in] cmd command
149 * @param[in] addr address
150 * @return number of bytes of this sequence
151 */
152static uint8_t ll_eeprom_prepare_seq(uint8_t *seq, uint32_t size, uint8_t cmd,
153 uint32_t addr) {
154
155 seq[0] = ((uint8_t)cmd & 0xff);
156
157 if (size > 0xffffUL) {
158 /* High density, 24bit address. */
159 seq[1] = (uint8_t)((addr >> 16) & 0xff);
160 seq[2] = (uint8_t)((addr >> 8) & 0xff);
161 seq[3] = (uint8_t)(addr & 0xff);
162 return 4;
163 }
164 else if (size > 0x00ffUL) {
165 /* Medium density, 16bit address. */
166 seq[1] = (uint8_t)((addr >> 8) & 0xff);
167 seq[2] = (uint8_t)(addr & 0xff);
168 return 3;
169 }
170
171 /* Low density, 8bit address. */
172 seq[1] = (uint8_t)(addr & 0xff);
173 return 2;
174}
175
176/**
177 * @brief EEPROM read routine.
178 *
179 * @param[in] eepcfg pointer to configuration structure of eeprom file.
180 * @param[in] offset addres of 1-st byte to be read.
181 * @param[out] data pointer to buffer with data to be written.
182 * @param[in] len number of bytes to be red.
183 */
184static msg_t ll_eeprom_read(const SPIEepromFileConfig *eepcfg, uint32_t offset,
185 uint8_t *data, size_t len) {
186
187 uint8_t txbuff[4];
188 uint8_t txlen;
189
190 osalDbgAssert(((len <= eepcfg->size) && ((offset + len) <= eepcfg->size)),
191 "out of device bounds");
192
193 if (eepcfg->spip->state != SPI_READY)
194 return MSG_RESET;
195
196 txlen = ll_eeprom_prepare_seq(txbuff, eepcfg->size, CMD_READ,
197 (offset + eepcfg->barrier_low));
198 ll_25xx_transmit_receive(eepcfg, txbuff, txlen, data, len);
199
200 return MSG_OK;
201}
202
203/**
204 * @brief EEPROM write routine.
205 * @details Function writes data to EEPROM.
206 * @pre Data must be fit to single EEPROM page.
207 *
208 * @param[in] eepcfg pointer to configuration structure of eeprom file.
209 * @param[in] offset addres of 1-st byte to be writen.
210 * @param[in] data pointer to buffer with data to be written.
211 * @param[in] len number of bytes to be written.
212 */
213static msg_t ll_eeprom_write(const SPIEepromFileConfig *eepcfg, uint32_t offset,
214 const uint8_t *data, size_t len) {
215
216 uint8_t txbuff[4];
217 uint8_t txlen;
218 systime_t now;
219
220 osalDbgAssert(((len <= eepcfg->size) && ((offset + len) <= eepcfg->size)),
221 "out of device bounds");
222 osalDbgAssert((((offset + eepcfg->barrier_low) / eepcfg->pagesize) ==
223 (((offset + eepcfg->barrier_low) + len - 1) / eepcfg->pagesize)),
224 "data can not be fitted in single page");
225
226 if (eepcfg->spip->state != SPI_READY)
227 return MSG_RESET;
228
229 /* Unlock array for writting. */
230 ll_eeprom_unlock(eepcfg);
231
232#if SPI_USE_MUTUAL_EXCLUSION
233 spiAcquireBus(eepcfg->spip);
234#endif
235
236 spiSelect(eepcfg->spip);
237 txlen = ll_eeprom_prepare_seq(txbuff, eepcfg->size, CMD_WRITE,
238 (offset + eepcfg->barrier_low));
239 spiSend(eepcfg->spip, txlen, txbuff);
240 spiSend(eepcfg->spip, len, data);
241 spiUnselect(eepcfg->spip);
242
243#if SPI_USE_MUTUAL_EXCLUSION
244 spiReleaseBus(eepcfg->spip);
245#endif
246
247 /* Wait until EEPROM process data. */
248 now = chVTGetSystemTimeX();
249 while (ll_eeprom_is_busy(eepcfg)) {
250 if ((chVTGetSystemTimeX() - now) > eepcfg->write_time) {
251 return MSG_TIMEOUT;
252 }
253
254 chThdYield();
255 }
256
257 /* Lock array preventing unexpected access */
258 ll_eeprom_lock(eepcfg);
259 return MSG_OK;
260}
261
262/**
263 * @brief Determines and returns size of data that can be processed
264 */
265static size_t __clamp_size(void *ip, size_t n) {
266
267 if (((size_t)eepfs_getposition(ip) + n) > (size_t)eepfs_getsize(ip))
268 return eepfs_getsize(ip) - eepfs_getposition(ip);
269 else
270 return n;
271}
272
273/**
274 * @brief Write data that can be fitted in one page boundary
275 */
276static msg_t __fitted_write(void *ip, const uint8_t *data, size_t len, uint32_t *written) {
277
278 msg_t status = MSG_RESET;
279
280 osalDbgAssert(len > 0, "len must be greater than 0");
281
282 status = ll_eeprom_write(((SPIEepromFileStream *)ip)->cfg,
283 eepfs_getposition(ip), data, len);
284 if (status == MSG_OK) {
285 *written += len;
286 eepfs_lseek(ip, eepfs_getposition(ip) + len);
287 }
288 return status;
289}
290
291/**
292 * @brief Write data to EEPROM.
293 * @details Only one EEPROM page can be written at once. So function
294 * splits large data chunks in small EEPROM transactions if needed.
295 * @note To achieve the maximum efficiency use write operations
296 * aligned to EEPROM page boundaries.
297 */
298static size_t write(void *ip, const uint8_t *bp, size_t n) {
299
300 size_t len = 0; /* bytes to be written per transaction */
301 uint32_t written = 0; /* total bytes successfully written */
302 uint16_t pagesize;
303 uint32_t firstpage;
304 uint32_t lastpage;
305
306 volatile const SPIEepromFileConfig *cfg = ((SPIEepromFileStream *)ip)->cfg;
307
308 osalDbgCheck((ip != NULL) && (((SPIEepromFileStream *)ip)->vmt != NULL));
309
310 if (n == 0)
311 return 0;
312
313 n = __clamp_size(ip, n);
314 if (n == 0)
315 return 0;
316
317 pagesize = cfg->pagesize;
318 firstpage = (cfg->barrier_low + eepfs_getposition(ip)) / pagesize;
319 lastpage = ((cfg->barrier_low + eepfs_getposition(ip) + n) - 1) / pagesize;
320
321 /* data fits in single page */
322 if (firstpage == lastpage) {
323 len = n;
324 __fitted_write(ip, bp, len, &written);
325 return written;
326 }
327
328 else {
329 /* write first piece of data to first page boundary */
330 len = ((firstpage + 1) * pagesize) - eepfs_getposition(ip);
331 len -= cfg->barrier_low;
332 if (__fitted_write(ip, bp, len, &written) != MSG_OK)
333 return written;
334 bp += len;
335
336 /* now write page sized blocks (zero or more) */
337 while ((n - written) > pagesize) {
338 len = pagesize;
339 if (__fitted_write(ip, bp, len, &written) != MSG_OK)
340 return written;
341 bp += len;
342 }
343
344 /* write tail */
345 len = n - written;
346 if (len == 0)
347 return written;
348 else {
349 __fitted_write(ip, bp, len, &written);
350 }
351 }
352
353 return written;
354}
355
356/**
357 * Read some bytes from current position in file. After successful
358 * read operation the position pointer will be increased by the number
359 * of read bytes.
360 */
361static size_t read(void *ip, uint8_t *bp, size_t n) {
362
363 msg_t status = MSG_OK;
364
365 osalDbgCheck((ip != NULL) && (((EepromFileStream *)ip)->vmt != NULL));
366
367 if (n == 0)
368 return 0;
369
370 n = __clamp_size(ip, n);
371 if (n == 0)
372 return 0;
373
374 /* call low level function */
375 status = ll_eeprom_read(((SPIEepromFileStream *)ip)->cfg,
376 eepfs_getposition(ip), bp, n);
377 if (status != MSG_OK)
378 return 0;
379 else {
380 eepfs_lseek(ip, (eepfs_getposition(ip) + n));
381 return n;
382 }
383}
384
385static const struct EepromFileStreamVMT vmt = {
386 (size_t)0,
387 write,
388 read,
389 eepfs_put,
390 eepfs_get,
391 eepfs_close,
392 eepfs_geterror,
393 eepfs_getsize,
394 eepfs_getposition,
395 eepfs_lseek,
396};
397
398EepromDevice eepdev_25xx = {
399 EEPROM_DEV_25XX,
400 &vmt
401};
402
403#endif /* EEPROM_USE_EE25XX */