source: rtems/bsps/arm/lpc32xx/nand/nand-mlc.c

Last change on this file was bcef89f2, checked in by Sebastian Huber <sebastian.huber@…>, on 05/19/23 at 06:18:25

Update company name

The embedded brains GmbH & Co. KG is the legal successor of embedded
brains GmbH.

  • Property mode set to 100644
File size: 9.6 KB
Line 
1/* SPDX-License-Identifier: BSD-2-Clause */
2
3/**
4 * @file
5 *
6 * @ingroup lpc32xx_nand_mlc
7 *
8 * @brief NAND MLC controller implementation.
9 */
10
11/*
12 * Copyright (C) 2010, 2011 embedded brains GmbH & Co. KG
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 * 1. Redistributions of source code must retain the above copyright
18 *    notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 *    notice, this list of conditions and the following disclaimer in the
21 *    documentation and/or other materials provided with the distribution.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 */
35
36#include <bsp/lpc32xx.h>
37#include <bsp/nand-mlc.h>
38
39static volatile lpc32xx_nand_mlc *const mlc = &lpc32xx.nand_mlc;
40
41static uint32_t mlc_flags;
42
43static uint32_t mlc_block_count;
44
45static uint32_t mlc_page_count;
46
47static bool mlc_small_pages(void)
48{
49  return (mlc_flags & MLC_SMALL_PAGES) != 0;
50}
51
52static bool mlc_many_address_cycles(void)
53{
54  return (mlc_flags & MLC_MANY_ADDRESS_CYCLES) != 0;
55}
56
57static bool mlc_normal_blocks(void)
58{
59  return (mlc_flags & MLC_NORMAL_BLOCKS) != 0;
60}
61
62uint32_t lpc32xx_mlc_page_size(void)
63{
64  if (mlc_small_pages()) {
65    return 512;
66  } else {
67    return 2048;
68  }
69}
70
71uint32_t lpc32xx_mlc_pages_per_block(void)
72{
73  if (mlc_small_pages()) {
74    return 32;
75  } else {
76    if (mlc_normal_blocks()) {
77      return 64;
78    } else {
79      return 128;
80    }
81  }
82}
83
84uint32_t lpc32xx_mlc_block_count(void)
85{
86  return mlc_block_count;
87}
88
89uint32_t lpc32xx_mlc_io_width(void)
90{
91  if ((mlc_flags & MLC_IO_WIDTH_16_BIT) == 0) {
92    return 8;
93  } else {
94    return 16;
95  }
96}
97
98static void mlc_unlock(void)
99{
100  mlc->lock_pr = MLC_UNLOCK_PROT;
101}
102
103static void mlc_wait(uint32_t flags)
104{
105  while ((mlc->isr & flags) != flags) {
106    /* Wait */
107  }
108}
109
110static void mlc_wait_until_ready(void)
111{
112  mlc_wait(MLC_ISR_CONTROLLER_READY | MLC_ISR_NAND_READY);
113}
114
115static void mlc_reset(void)
116{
117  mlc->cmd = 0xff;
118}
119
120static uint32_t mlc_status(void)
121{
122  mlc_wait_until_ready();
123  mlc->cmd = 0x70;
124
125  return mlc->data.w8;
126}
127
128static bool mlc_was_operation_successful(void)
129{
130  return (mlc_status() & (NAND_STATUS_READY | NAND_STATUS_ERROR))
131    == NAND_STATUS_READY;
132}
133
134static void mlc_set_block_address(uint32_t block_index)
135{
136  if (mlc_small_pages()) {
137    mlc->addr = (uint8_t) (block_index << 5);
138    mlc->addr = (uint8_t) (block_index >> 3);
139    if (mlc_many_address_cycles()) {
140      mlc->addr = (uint8_t) (block_index >> 11);
141    }
142  } else {
143    if (mlc_normal_blocks()) {
144      mlc->addr = (uint8_t) (block_index << 6);
145      mlc->addr = (uint8_t) (block_index >> 2);
146      if (mlc_many_address_cycles()) {
147        mlc->addr = (uint8_t) (block_index >> 10);
148      }
149    } else {
150      mlc->addr = (uint8_t) (block_index << 7);
151      mlc->addr = (uint8_t) (block_index >> 1);
152      if (mlc_many_address_cycles()) {
153        mlc->addr = (uint8_t) (block_index >> 9);
154      }
155    }
156  }
157}
158
159static void mlc_set_page_address(uint32_t page_index)
160{
161  mlc->addr = 0;
162  if (mlc_small_pages()) {
163    mlc->addr = (uint8_t) page_index;
164    mlc->addr = (uint8_t) (page_index >> 8);
165    if (mlc_many_address_cycles()) {
166      mlc->addr = (uint8_t) (page_index >> 16);
167    }
168  } else {
169    mlc->addr = 0;
170    mlc->addr = (uint8_t) page_index;
171    mlc->addr = (uint8_t) (page_index >> 8);
172    if (mlc_many_address_cycles()) {
173      mlc->addr = (uint8_t) (page_index >> 16);
174    }
175  }
176}
177
178void lpc32xx_mlc_init(const lpc32xx_mlc_config *cfg)
179{
180  uint32_t icr = 0;
181
182  mlc_flags = cfg->flags;
183  mlc_block_count = cfg->block_count;
184  mlc_page_count = cfg->block_count * lpc32xx_mlc_pages_per_block();
185
186  /* Clock */
187  LPC32XX_FLASHCLK_CTRL = FLASHCLK_IRQ_MLC | FLASHCLK_MLC_CLK_ENABLE;
188
189  /* Timing settings */
190  mlc_unlock();
191  mlc->time = cfg->time;
192
193  /* Configuration */
194  if (!mlc_small_pages()) {
195    icr |= MLC_ICR_LARGE_PAGES;
196  }
197  if (mlc_many_address_cycles()) {
198    icr |= MLC_ICR_ADDR_WORD_COUNT_4_5;
199  }
200  mlc_unlock();
201  mlc->icr = icr;
202
203  mlc_reset();
204}
205
206void lpc32xx_mlc_write_protection(
207  uint32_t page_index_low,
208  uint32_t page_index_high
209)
210{
211  mlc_unlock();
212  mlc->sw_wp_add_low = page_index_low;
213  mlc_unlock();
214  mlc->sw_wp_add_hig = page_index_high;
215  mlc_unlock();
216  mlc->icr |= MLC_ICR_SOFT_WRITE_PROT;
217}
218
219static bool is_word_aligned(const void *data, const void *spare)
220{
221  return (((uintptr_t) data) | ((uintptr_t) spare)) % 4 == 0;
222}
223
224rtems_status_code lpc32xx_mlc_read_page(
225  uint32_t page_index,
226  void *data,
227  void *spare,
228  uint32_t *symbol_error_count_ptr
229)
230{
231  rtems_status_code sc = RTEMS_SUCCESSFUL;
232  size_t small_pages_count = mlc_small_pages() ? 1 : MLC_SMALL_PAGES_PER_LARGE_PAGE;
233  size_t sp = 0;
234  size_t i = 0;
235  uint32_t isr = 0;
236  uint32_t symbol_error_count = 0xffffffff;
237  bool aligned = is_word_aligned(data, spare);
238  uint8_t *current_data = data;
239  uint8_t *current_spare = spare;
240
241  if (page_index >= mlc_page_count) {
242    return RTEMS_INVALID_ID;
243  }
244
245  mlc_wait_until_ready();
246  mlc->cmd = 0x00;
247  if (!mlc_small_pages()) {
248    mlc->cmd = 0x30;
249  }
250  mlc_set_page_address(page_index);
251  mlc_wait(MLC_ISR_NAND_READY);
252
253  for (sp = 0; sc == RTEMS_SUCCESSFUL && sp < small_pages_count; ++sp) {
254    uint32_t *aligned_data = (uint32_t *) current_data;
255    uint32_t *aligned_spare = (uint32_t *) current_spare;
256
257    mlc->ecc_dec = 0;
258
259    if (aligned) {
260      for (i = 0; i < MLC_SMALL_DATA_WORD_COUNT; ++i) {
261        aligned_data [i] = mlc->data.w32;
262      }
263      for (i = 0; i < MLC_SMALL_SPARE_WORD_COUNT; ++i) {
264        aligned_spare [i] = mlc->data.w32;
265      }
266    } else {
267      for (i = 0; i < MLC_SMALL_DATA_SIZE; ++i) {
268        current_data [i] = mlc->data.w8;
269      }
270      for (i = 0; i < MLC_SMALL_SPARE_SIZE; ++i) {
271        current_spare [i] = mlc->data.w8;
272      }
273    }
274
275    mlc_wait(MLC_ISR_ECC_READY);
276
277    isr = mlc->isr;
278    if ((isr & MLC_ISR_ERRORS_DETECTED) == 0) {
279      symbol_error_count = 0;
280    } else {
281      if ((isr & MLC_ISR_DECODER_FAILURE) == 0) {
282        symbol_error_count = MLC_ISR_SYMBOL_ERRORS(isr);
283        if (aligned) {
284          mlc->rubp = 0;
285          for (i = 0; i < MLC_SMALL_DATA_WORD_COUNT; ++i) {
286            aligned_data [i] = mlc->buff.w32;
287          }
288          mlc->robp = 0;
289          for (i = 0; i < MLC_SMALL_SPARE_WORD_COUNT; ++i) {
290            aligned_spare [i] = mlc->buff.w32;
291          }
292        } else {
293          mlc->rubp = 0;
294          for (i = 0; i < MLC_SMALL_DATA_SIZE; ++i) {
295            current_data [i] = mlc->buff.w8;
296          }
297          mlc->robp = 0;
298          for (i = 0; i < MLC_SMALL_SPARE_SIZE; ++i) {
299            current_spare [i] = mlc->buff.w8;
300          }
301        }
302      } else {
303        sc = RTEMS_IO_ERROR;
304      }
305    }
306
307    current_data += MLC_SMALL_DATA_SIZE;
308    current_spare += MLC_SMALL_SPARE_SIZE;
309  }
310
311  if (symbol_error_count_ptr != NULL) {
312    *symbol_error_count_ptr = symbol_error_count;
313  }
314
315  return sc;
316}
317
318void lpc32xx_mlc_read_id(uint8_t *id, size_t n)
319{
320  size_t i = 0;
321
322  mlc_wait_until_ready();
323  mlc->cmd = 0x90;
324  mlc->addr = 0;
325  mlc_wait(MLC_ISR_NAND_READY);
326
327  for (i = 0; i < n; ++i) {
328    id [i] = mlc->data.w8;
329  }
330}
331
332rtems_status_code lpc32xx_mlc_erase_block(uint32_t block_index)
333{
334  rtems_status_code sc = RTEMS_UNSATISFIED;
335
336  if (block_index >= mlc_block_count) {
337    return RTEMS_INVALID_ID;
338  }
339
340  mlc_wait_until_ready();
341  mlc->cmd = 0x60;
342  mlc_set_block_address(block_index);
343  mlc->cmd = 0xd0;
344
345  if (mlc_was_operation_successful()) {
346    sc = RTEMS_SUCCESSFUL;
347  }
348
349  return sc;
350}
351
352rtems_status_code lpc32xx_mlc_write_page_with_ecc(
353  uint32_t page_index,
354  const void *data,
355  const void *spare
356)
357{
358  rtems_status_code sc = RTEMS_IO_ERROR;
359  size_t small_pages_count = mlc_small_pages() ?
360    1 : MLC_SMALL_PAGES_PER_LARGE_PAGE;
361  size_t sp = 0;
362  size_t i = 0;
363  bool aligned = is_word_aligned(data, spare);
364  const uint8_t *current_data = data;
365  const uint8_t *current_spare = spare;
366
367  if (page_index >= mlc_page_count) {
368    return RTEMS_INVALID_ID;
369  }
370
371  mlc_wait_until_ready();
372  mlc->cmd = 0x80;
373  mlc_set_page_address(page_index);
374
375  for (sp = 0; sp < small_pages_count; ++sp) {
376    mlc->ecc_enc = 0;
377
378    if (aligned) {
379      const uint32_t *aligned_data = (const uint32_t *) current_data;
380      const uint32_t *aligned_spare = (const uint32_t *) current_spare;
381
382      for (i = 0; i < MLC_SMALL_DATA_WORD_COUNT; ++i) {
383        mlc->data.w32 = aligned_data [i];
384      }
385      mlc->data.w32 = aligned_spare [0];
386      mlc->data.w16 = (uint16_t) aligned_spare [1];
387    } else {
388      for (i = 0; i < MLC_SMALL_DATA_SIZE; ++i) {
389        mlc->data.w8 = current_data [i];
390      }
391      for (i = 0; i < MLC_SMALL_USER_SPARE_SIZE; ++i) {
392        mlc->data.w8 = current_spare [i];
393      }
394    }
395    mlc->wpr = 0;
396
397    mlc_wait(MLC_ISR_CONTROLLER_READY);
398
399    current_data += MLC_SMALL_DATA_SIZE;
400    current_spare += MLC_SMALL_SPARE_SIZE;
401  }
402
403  mlc->cmd = 0x10;
404
405  if (mlc_was_operation_successful()) {
406    sc = RTEMS_SUCCESSFUL;
407  }
408
409  return sc;
410}
Note: See TracBrowser for help on using the repository browser.