File indexing completed on 2025-05-11 08:23:04
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036 #include <bsp/lpc32xx.h>
0037 #include <bsp/nand-mlc.h>
0038
0039 static volatile lpc32xx_nand_mlc *const mlc = &lpc32xx.nand_mlc;
0040
0041 static uint32_t mlc_flags;
0042
0043 static uint32_t mlc_block_count;
0044
0045 static uint32_t mlc_page_count;
0046
0047 static bool mlc_small_pages(void)
0048 {
0049 return (mlc_flags & MLC_SMALL_PAGES) != 0;
0050 }
0051
0052 static bool mlc_many_address_cycles(void)
0053 {
0054 return (mlc_flags & MLC_MANY_ADDRESS_CYCLES) != 0;
0055 }
0056
0057 static bool mlc_normal_blocks(void)
0058 {
0059 return (mlc_flags & MLC_NORMAL_BLOCKS) != 0;
0060 }
0061
0062 uint32_t lpc32xx_mlc_page_size(void)
0063 {
0064 if (mlc_small_pages()) {
0065 return 512;
0066 } else {
0067 return 2048;
0068 }
0069 }
0070
0071 uint32_t lpc32xx_mlc_pages_per_block(void)
0072 {
0073 if (mlc_small_pages()) {
0074 return 32;
0075 } else {
0076 if (mlc_normal_blocks()) {
0077 return 64;
0078 } else {
0079 return 128;
0080 }
0081 }
0082 }
0083
0084 uint32_t lpc32xx_mlc_block_count(void)
0085 {
0086 return mlc_block_count;
0087 }
0088
0089 uint32_t lpc32xx_mlc_io_width(void)
0090 {
0091 if ((mlc_flags & MLC_IO_WIDTH_16_BIT) == 0) {
0092 return 8;
0093 } else {
0094 return 16;
0095 }
0096 }
0097
0098 static void mlc_unlock(void)
0099 {
0100 mlc->lock_pr = MLC_UNLOCK_PROT;
0101 }
0102
0103 static void mlc_wait(uint32_t flags)
0104 {
0105 while ((mlc->isr & flags) != flags) {
0106
0107 }
0108 }
0109
0110 static void mlc_wait_until_ready(void)
0111 {
0112 mlc_wait(MLC_ISR_CONTROLLER_READY | MLC_ISR_NAND_READY);
0113 }
0114
0115 static void mlc_reset(void)
0116 {
0117 mlc->cmd = 0xff;
0118 }
0119
0120 static uint32_t mlc_status(void)
0121 {
0122 mlc_wait_until_ready();
0123 mlc->cmd = 0x70;
0124
0125 return mlc->data.w8;
0126 }
0127
0128 static bool mlc_was_operation_successful(void)
0129 {
0130 return (mlc_status() & (NAND_STATUS_READY | NAND_STATUS_ERROR))
0131 == NAND_STATUS_READY;
0132 }
0133
0134 static void mlc_set_block_address(uint32_t block_index)
0135 {
0136 if (mlc_small_pages()) {
0137 mlc->addr = (uint8_t) (block_index << 5);
0138 mlc->addr = (uint8_t) (block_index >> 3);
0139 if (mlc_many_address_cycles()) {
0140 mlc->addr = (uint8_t) (block_index >> 11);
0141 }
0142 } else {
0143 if (mlc_normal_blocks()) {
0144 mlc->addr = (uint8_t) (block_index << 6);
0145 mlc->addr = (uint8_t) (block_index >> 2);
0146 if (mlc_many_address_cycles()) {
0147 mlc->addr = (uint8_t) (block_index >> 10);
0148 }
0149 } else {
0150 mlc->addr = (uint8_t) (block_index << 7);
0151 mlc->addr = (uint8_t) (block_index >> 1);
0152 if (mlc_many_address_cycles()) {
0153 mlc->addr = (uint8_t) (block_index >> 9);
0154 }
0155 }
0156 }
0157 }
0158
0159 static void mlc_set_page_address(uint32_t page_index)
0160 {
0161 mlc->addr = 0;
0162 if (mlc_small_pages()) {
0163 mlc->addr = (uint8_t) page_index;
0164 mlc->addr = (uint8_t) (page_index >> 8);
0165 if (mlc_many_address_cycles()) {
0166 mlc->addr = (uint8_t) (page_index >> 16);
0167 }
0168 } else {
0169 mlc->addr = 0;
0170 mlc->addr = (uint8_t) page_index;
0171 mlc->addr = (uint8_t) (page_index >> 8);
0172 if (mlc_many_address_cycles()) {
0173 mlc->addr = (uint8_t) (page_index >> 16);
0174 }
0175 }
0176 }
0177
0178 void lpc32xx_mlc_init(const lpc32xx_mlc_config *cfg)
0179 {
0180 uint32_t icr = 0;
0181
0182 mlc_flags = cfg->flags;
0183 mlc_block_count = cfg->block_count;
0184 mlc_page_count = cfg->block_count * lpc32xx_mlc_pages_per_block();
0185
0186
0187 LPC32XX_FLASHCLK_CTRL = FLASHCLK_IRQ_MLC | FLASHCLK_MLC_CLK_ENABLE;
0188
0189
0190 mlc_unlock();
0191 mlc->time = cfg->time;
0192
0193
0194 if (!mlc_small_pages()) {
0195 icr |= MLC_ICR_LARGE_PAGES;
0196 }
0197 if (mlc_many_address_cycles()) {
0198 icr |= MLC_ICR_ADDR_WORD_COUNT_4_5;
0199 }
0200 mlc_unlock();
0201 mlc->icr = icr;
0202
0203 mlc_reset();
0204 }
0205
0206 void lpc32xx_mlc_write_protection(
0207 uint32_t page_index_low,
0208 uint32_t page_index_high
0209 )
0210 {
0211 mlc_unlock();
0212 mlc->sw_wp_add_low = page_index_low;
0213 mlc_unlock();
0214 mlc->sw_wp_add_hig = page_index_high;
0215 mlc_unlock();
0216 mlc->icr |= MLC_ICR_SOFT_WRITE_PROT;
0217 }
0218
0219 static bool is_word_aligned(const void *data, const void *spare)
0220 {
0221 return (((uintptr_t) data) | ((uintptr_t) spare)) % 4 == 0;
0222 }
0223
0224 rtems_status_code lpc32xx_mlc_read_page(
0225 uint32_t page_index,
0226 void *data,
0227 void *spare,
0228 uint32_t *symbol_error_count_ptr
0229 )
0230 {
0231 rtems_status_code sc = RTEMS_SUCCESSFUL;
0232 size_t small_pages_count = mlc_small_pages() ? 1 : MLC_SMALL_PAGES_PER_LARGE_PAGE;
0233 size_t sp = 0;
0234 size_t i = 0;
0235 uint32_t isr = 0;
0236 uint32_t symbol_error_count = 0xffffffff;
0237 bool aligned = is_word_aligned(data, spare);
0238 uint8_t *current_data = data;
0239 uint8_t *current_spare = spare;
0240
0241 if (page_index >= mlc_page_count) {
0242 return RTEMS_INVALID_ID;
0243 }
0244
0245 mlc_wait_until_ready();
0246 mlc->cmd = 0x00;
0247 if (!mlc_small_pages()) {
0248 mlc->cmd = 0x30;
0249 }
0250 mlc_set_page_address(page_index);
0251 mlc_wait(MLC_ISR_NAND_READY);
0252
0253 for (sp = 0; sc == RTEMS_SUCCESSFUL && sp < small_pages_count; ++sp) {
0254 uint32_t *aligned_data = (uint32_t *) current_data;
0255 uint32_t *aligned_spare = (uint32_t *) current_spare;
0256
0257 mlc->ecc_dec = 0;
0258
0259 if (aligned) {
0260 for (i = 0; i < MLC_SMALL_DATA_WORD_COUNT; ++i) {
0261 aligned_data [i] = mlc->data.w32;
0262 }
0263 for (i = 0; i < MLC_SMALL_SPARE_WORD_COUNT; ++i) {
0264 aligned_spare [i] = mlc->data.w32;
0265 }
0266 } else {
0267 for (i = 0; i < MLC_SMALL_DATA_SIZE; ++i) {
0268 current_data [i] = mlc->data.w8;
0269 }
0270 for (i = 0; i < MLC_SMALL_SPARE_SIZE; ++i) {
0271 current_spare [i] = mlc->data.w8;
0272 }
0273 }
0274
0275 mlc_wait(MLC_ISR_ECC_READY);
0276
0277 isr = mlc->isr;
0278 if ((isr & MLC_ISR_ERRORS_DETECTED) == 0) {
0279 symbol_error_count = 0;
0280 } else {
0281 if ((isr & MLC_ISR_DECODER_FAILURE) == 0) {
0282 symbol_error_count = MLC_ISR_SYMBOL_ERRORS(isr);
0283 if (aligned) {
0284 mlc->rubp = 0;
0285 for (i = 0; i < MLC_SMALL_DATA_WORD_COUNT; ++i) {
0286 aligned_data [i] = mlc->buff.w32;
0287 }
0288 mlc->robp = 0;
0289 for (i = 0; i < MLC_SMALL_SPARE_WORD_COUNT; ++i) {
0290 aligned_spare [i] = mlc->buff.w32;
0291 }
0292 } else {
0293 mlc->rubp = 0;
0294 for (i = 0; i < MLC_SMALL_DATA_SIZE; ++i) {
0295 current_data [i] = mlc->buff.w8;
0296 }
0297 mlc->robp = 0;
0298 for (i = 0; i < MLC_SMALL_SPARE_SIZE; ++i) {
0299 current_spare [i] = mlc->buff.w8;
0300 }
0301 }
0302 } else {
0303 sc = RTEMS_IO_ERROR;
0304 }
0305 }
0306
0307 current_data += MLC_SMALL_DATA_SIZE;
0308 current_spare += MLC_SMALL_SPARE_SIZE;
0309 }
0310
0311 if (symbol_error_count_ptr != NULL) {
0312 *symbol_error_count_ptr = symbol_error_count;
0313 }
0314
0315 return sc;
0316 }
0317
0318 void lpc32xx_mlc_read_id(uint8_t *id, size_t n)
0319 {
0320 size_t i = 0;
0321
0322 mlc_wait_until_ready();
0323 mlc->cmd = 0x90;
0324 mlc->addr = 0;
0325 mlc_wait(MLC_ISR_NAND_READY);
0326
0327 for (i = 0; i < n; ++i) {
0328 id [i] = mlc->data.w8;
0329 }
0330 }
0331
0332 rtems_status_code lpc32xx_mlc_erase_block(uint32_t block_index)
0333 {
0334 rtems_status_code sc = RTEMS_UNSATISFIED;
0335
0336 if (block_index >= mlc_block_count) {
0337 return RTEMS_INVALID_ID;
0338 }
0339
0340 mlc_wait_until_ready();
0341 mlc->cmd = 0x60;
0342 mlc_set_block_address(block_index);
0343 mlc->cmd = 0xd0;
0344
0345 if (mlc_was_operation_successful()) {
0346 sc = RTEMS_SUCCESSFUL;
0347 }
0348
0349 return sc;
0350 }
0351
0352 rtems_status_code lpc32xx_mlc_write_page_with_ecc(
0353 uint32_t page_index,
0354 const void *data,
0355 const void *spare
0356 )
0357 {
0358 rtems_status_code sc = RTEMS_IO_ERROR;
0359 size_t small_pages_count = mlc_small_pages() ?
0360 1 : MLC_SMALL_PAGES_PER_LARGE_PAGE;
0361 size_t sp = 0;
0362 size_t i = 0;
0363 bool aligned = is_word_aligned(data, spare);
0364 const uint8_t *current_data = data;
0365 const uint8_t *current_spare = spare;
0366
0367 if (page_index >= mlc_page_count) {
0368 return RTEMS_INVALID_ID;
0369 }
0370
0371 mlc_wait_until_ready();
0372 mlc->cmd = 0x80;
0373 mlc_set_page_address(page_index);
0374
0375 for (sp = 0; sp < small_pages_count; ++sp) {
0376 mlc->ecc_enc = 0;
0377
0378 if (aligned) {
0379 const uint32_t *aligned_data = (const uint32_t *) current_data;
0380 const uint32_t *aligned_spare = (const uint32_t *) current_spare;
0381
0382 for (i = 0; i < MLC_SMALL_DATA_WORD_COUNT; ++i) {
0383 mlc->data.w32 = aligned_data [i];
0384 }
0385 mlc->data.w32 = aligned_spare [0];
0386 mlc->data.w16 = (uint16_t) aligned_spare [1];
0387 } else {
0388 for (i = 0; i < MLC_SMALL_DATA_SIZE; ++i) {
0389 mlc->data.w8 = current_data [i];
0390 }
0391 for (i = 0; i < MLC_SMALL_USER_SPARE_SIZE; ++i) {
0392 mlc->data.w8 = current_spare [i];
0393 }
0394 }
0395 mlc->wpr = 0;
0396
0397 mlc_wait(MLC_ISR_CONTROLLER_READY);
0398
0399 current_data += MLC_SMALL_DATA_SIZE;
0400 current_spare += MLC_SMALL_SPARE_SIZE;
0401 }
0402
0403 mlc->cmd = 0x10;
0404
0405 if (mlc_was_operation_successful()) {
0406 sc = RTEMS_SUCCESSFUL;
0407 }
0408
0409 return sc;
0410 }