source: rtems/c/src/lib/libbsp/arm/lpc32xx/misc/nand-mlc.c @ c499856

4.115
Last change on this file since c499856 was c499856, checked in by Chris Johns <chrisj@…>, on 03/20/14 at 21:10:47

Change all references of rtems.com to rtems.org.

  • Property mode set to 100644
File size: 8.6 KB
Line 
1/**
2 * @file
3 *
4 * @ingroup lpc32xx_nand_mlc
5 *
6 * @brief NAND MLC controller implementation.
7 */
8
9/*
10 * Copyright (c) 2010-2011 embedded brains GmbH.  All rights reserved.
11 *
12 *  embedded brains GmbH
13 *  Obere Lagerstr. 30
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#include <bsp/lpc32xx.h>
24#include <bsp/nand-mlc.h>
25
26static volatile lpc32xx_nand_mlc *const mlc = &lpc32xx.nand_mlc;
27
28static uint32_t mlc_flags;
29
30static uint32_t mlc_block_count;
31
32static uint32_t mlc_page_count;
33
34static bool mlc_small_pages(void)
35{
36  return (mlc_flags & MLC_SMALL_PAGES) != 0;
37}
38
39static bool mlc_many_address_cycles(void)
40{
41  return (mlc_flags & MLC_MANY_ADDRESS_CYCLES) != 0;
42}
43
44static bool mlc_normal_blocks(void)
45{
46  return (mlc_flags & MLC_NORMAL_BLOCKS) != 0;
47}
48
49uint32_t lpc32xx_mlc_page_size(void)
50{
51  if (mlc_small_pages()) {
52    return 512;
53  } else {
54    return 2048;
55  }
56}
57
58uint32_t lpc32xx_mlc_pages_per_block(void)
59{
60  if (mlc_small_pages()) {
61    return 32;
62  } else {
63    if (mlc_normal_blocks()) {
64      return 64;
65    } else {
66      return 128;
67    }
68  }
69}
70
71uint32_t lpc32xx_mlc_block_count(void)
72{
73  return mlc_block_count;
74}
75
76uint32_t lpc32xx_mlc_io_width(void)
77{
78  if ((mlc_flags & MLC_IO_WIDTH_16_BIT) == 0) {
79    return 8;
80  } else {
81    return 16;
82  }
83}
84
85static void mlc_unlock(void)
86{
87  mlc->lock_pr = MLC_UNLOCK_PROT;
88}
89
90static void mlc_wait(uint32_t flags)
91{
92  while ((mlc->isr & flags) != flags) {
93    /* Wait */
94  }
95}
96
97static void mlc_wait_until_ready(void)
98{
99  mlc_wait(MLC_ISR_CONTROLLER_READY | MLC_ISR_NAND_READY);
100}
101
102static void mlc_reset(void)
103{
104  mlc->cmd = 0xff;
105}
106
107static uint32_t mlc_status(void)
108{
109  mlc_wait_until_ready();
110  mlc->cmd = 0x70;
111
112  return mlc->data.w8;
113}
114
115static bool mlc_was_operation_successful(void)
116{
117  return (mlc_status() & (NAND_STATUS_READY | NAND_STATUS_ERROR))
118    == NAND_STATUS_READY;
119}
120
121static void mlc_set_block_address(uint32_t block_index)
122{
123  if (mlc_small_pages()) {
124    mlc->addr = (uint8_t) (block_index << 5);
125    mlc->addr = (uint8_t) (block_index >> 3);
126    if (mlc_many_address_cycles()) {
127      mlc->addr = (uint8_t) (block_index >> 11);
128    }
129  } else {
130    if (mlc_normal_blocks()) {
131      mlc->addr = (uint8_t) (block_index << 6);
132      mlc->addr = (uint8_t) (block_index >> 2);
133      if (mlc_many_address_cycles()) {
134        mlc->addr = (uint8_t) (block_index >> 10);
135      }
136    } else {
137      mlc->addr = (uint8_t) (block_index << 7);
138      mlc->addr = (uint8_t) (block_index >> 1);
139      if (mlc_many_address_cycles()) {
140        mlc->addr = (uint8_t) (block_index >> 9);
141      }
142    }
143  }
144}
145
146static void mlc_set_page_address(uint32_t page_index)
147{
148  mlc->addr = 0;
149  if (mlc_small_pages()) {
150    mlc->addr = (uint8_t) page_index;
151    mlc->addr = (uint8_t) (page_index >> 8);
152    if (mlc_many_address_cycles()) {
153      mlc->addr = (uint8_t) (page_index >> 16);
154    }
155  } else {
156    mlc->addr = 0;
157    mlc->addr = (uint8_t) page_index;
158    mlc->addr = (uint8_t) (page_index >> 8);
159    if (mlc_many_address_cycles()) {
160      mlc->addr = (uint8_t) (page_index >> 16);
161    }
162  }
163}
164
165void lpc32xx_mlc_init(const lpc32xx_mlc_config *cfg)
166{
167  uint32_t icr = 0;
168
169  mlc_flags = cfg->flags;
170  mlc_block_count = cfg->block_count;
171  mlc_page_count = cfg->block_count * lpc32xx_mlc_pages_per_block();
172
173  /* Clock */
174  LPC32XX_FLASHCLK_CTRL = FLASHCLK_IRQ_MLC | FLASHCLK_MLC_CLK_ENABLE;
175
176  /* Timing settings */
177  mlc_unlock();
178  mlc->time = cfg->time;
179
180  /* Configuration */
181  if (!mlc_small_pages()) {
182    icr |= MLC_ICR_LARGE_PAGES;
183  }
184  if (mlc_many_address_cycles()) {
185    icr |= MLC_ICR_ADDR_WORD_COUNT_4_5;
186  }
187  mlc_unlock();
188  mlc->icr = icr;
189
190  mlc_reset();
191}
192
193void lpc32xx_mlc_write_protection(
194  uint32_t page_index_low,
195  uint32_t page_index_high
196)
197{
198  mlc_unlock();
199  mlc->sw_wp_add_low = page_index_low;
200  mlc_unlock();
201  mlc->sw_wp_add_hig = page_index_high;
202  mlc_unlock();
203  mlc->icr |= MLC_ICR_SOFT_WRITE_PROT;
204}
205
206bool is_word_aligned(const void *data, const void *spare)
207{
208  return (((uintptr_t) data) | ((uintptr_t) spare)) % 4 == 0;
209}
210
211rtems_status_code lpc32xx_mlc_read_page(
212  uint32_t page_index,
213  void *data,
214  void *spare,
215  uint32_t *symbol_error_count_ptr
216)
217{
218  rtems_status_code sc = RTEMS_SUCCESSFUL;
219  size_t small_pages_count = mlc_small_pages() ? 1 : MLC_SMALL_PAGES_PER_LARGE_PAGE;
220  size_t sp = 0;
221  size_t i = 0;
222  uint32_t isr = 0;
223  uint32_t symbol_error_count = 0xffffffff;
224  bool aligned = is_word_aligned(data, spare);
225  uint8_t *current_data = data;
226  uint8_t *current_spare = spare;
227
228  if (page_index >= mlc_page_count) {
229    return RTEMS_INVALID_ID;
230  }
231
232  mlc_wait_until_ready();
233  mlc->cmd = 0x00;
234  if (!mlc_small_pages()) {
235    mlc->cmd = 0x30;
236  }
237  mlc_set_page_address(page_index);
238  mlc_wait(MLC_ISR_NAND_READY);
239
240  for (sp = 0; sc == RTEMS_SUCCESSFUL && sp < small_pages_count; ++sp) {
241    uint32_t *aligned_data = (uint32_t *) current_data;
242    uint32_t *aligned_spare = (uint32_t *) current_spare;
243
244    mlc->ecc_dec = 0;
245
246    if (aligned) {
247      for (i = 0; i < MLC_SMALL_DATA_WORD_COUNT; ++i) {
248        aligned_data [i] = mlc->data.w32;
249      }
250      for (i = 0; i < MLC_SMALL_SPARE_WORD_COUNT; ++i) {
251        aligned_spare [i] = mlc->data.w32;
252      }
253    } else {
254      for (i = 0; i < MLC_SMALL_DATA_SIZE; ++i) {
255        current_data [i] = mlc->data.w8;
256      }
257      for (i = 0; i < MLC_SMALL_SPARE_SIZE; ++i) {
258        current_spare [i] = mlc->data.w8;
259      }
260    }
261
262    mlc_wait(MLC_ISR_ECC_READY);
263
264    isr = mlc->isr;
265    if ((isr & MLC_ISR_ERRORS_DETECTED) == 0) {
266      symbol_error_count = 0;
267    } else {
268      if ((isr & MLC_ISR_DECODER_FAILURE) == 0) {
269        symbol_error_count = MLC_ISR_SYMBOL_ERRORS(isr);
270        if (aligned) {
271          mlc->rubp = 0;
272          for (i = 0; i < MLC_SMALL_DATA_WORD_COUNT; ++i) {
273            aligned_data [i] = mlc->buff.w32;
274          }
275          mlc->robp = 0;
276          for (i = 0; i < MLC_SMALL_SPARE_WORD_COUNT; ++i) {
277            aligned_spare [i] = mlc->buff.w32;
278          }
279        } else {
280          mlc->rubp = 0;
281          for (i = 0; i < MLC_SMALL_DATA_SIZE; ++i) {
282            current_data [i] = mlc->buff.w8;
283          }
284          mlc->robp = 0;
285          for (i = 0; i < MLC_SMALL_SPARE_SIZE; ++i) {
286            current_spare [i] = mlc->buff.w8;
287          }
288        }
289      } else {
290        sc = RTEMS_IO_ERROR;
291      }
292    }
293
294    current_data += MLC_SMALL_DATA_SIZE;
295    current_spare += MLC_SMALL_SPARE_SIZE;
296  }
297
298  if (symbol_error_count_ptr != NULL) {
299    *symbol_error_count_ptr = symbol_error_count;
300  }
301
302  return sc;
303}
304
305void lpc32xx_mlc_read_id(uint8_t *id, size_t n)
306{
307  size_t i = 0;
308
309  mlc_wait_until_ready();
310  mlc->cmd = 0x90;
311  mlc->addr = 0;
312  mlc_wait(MLC_ISR_NAND_READY);
313
314  for (i = 0; i < n; ++i) {
315    id [i] = mlc->data.w8;
316  }
317}
318
319rtems_status_code lpc32xx_mlc_erase_block(uint32_t block_index)
320{
321  rtems_status_code sc = RTEMS_UNSATISFIED;
322
323  if (block_index >= mlc_block_count) {
324    return RTEMS_INVALID_ID;
325  }
326
327  mlc_wait_until_ready();
328  mlc->cmd = 0x60;
329  mlc_set_block_address(block_index);
330  mlc->cmd = 0xd0;
331
332  if (mlc_was_operation_successful()) {
333    sc = RTEMS_SUCCESSFUL;
334  }
335
336  return sc;
337}
338
339rtems_status_code lpc32xx_mlc_write_page_with_ecc(
340  uint32_t page_index,
341  const void *data,
342  const void *spare
343)
344{
345  rtems_status_code sc = RTEMS_IO_ERROR;
346  size_t small_pages_count = mlc_small_pages() ?
347    1 : MLC_SMALL_PAGES_PER_LARGE_PAGE;
348  size_t sp = 0;
349  size_t i = 0;
350  bool aligned = is_word_aligned(data, spare);
351  const uint8_t *current_data = data;
352  const uint8_t *current_spare = spare;
353
354  if (page_index >= mlc_page_count) {
355    return RTEMS_INVALID_ID;
356  }
357
358  mlc_wait_until_ready();
359  mlc->cmd = 0x80;
360  mlc_set_page_address(page_index);
361
362  for (sp = 0; sp < small_pages_count; ++sp) {
363    mlc->ecc_enc = 0;
364
365    if (aligned) {
366      const uint32_t *aligned_data = (const uint32_t *) current_data;
367      const uint32_t *aligned_spare = (const uint32_t *) current_spare;
368
369      for (i = 0; i < MLC_SMALL_DATA_WORD_COUNT; ++i) {
370        mlc->data.w32 = aligned_data [i];
371      }
372      mlc->data.w32 = aligned_spare [0];
373      mlc->data.w16 = (uint16_t) aligned_spare [1];
374    } else {
375      for (i = 0; i < MLC_SMALL_DATA_SIZE; ++i) {
376        mlc->data.w8 = current_data [i];
377      }
378      for (i = 0; i < MLC_SMALL_USER_SPARE_SIZE; ++i) {
379        mlc->data.w8 = current_spare [i];
380      }
381    }
382    mlc->wpr = 0;
383
384    mlc_wait(MLC_ISR_CONTROLLER_READY);
385
386    current_data += MLC_SMALL_DATA_SIZE;
387    current_spare += MLC_SMALL_SPARE_SIZE;
388  }
389
390  mlc->cmd = 0x10;
391
392  if (mlc_was_operation_successful()) {
393    sc = RTEMS_SUCCESSFUL;
394  }
395
396  return sc;
397}
Note: See TracBrowser for help on using the repository browser.