Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:04

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  * @file
0005  *
0006  * @ingroup lpc32xx_nand_mlc
0007  *
0008  * @brief NAND MLC controller implementation.
0009  */
0010 
0011 /*
0012  * Copyright (C) 2010, 2011 embedded brains GmbH & Co. KG
0013  *
0014  * Redistribution and use in source and binary forms, with or without
0015  * modification, are permitted provided that the following conditions
0016  * are met:
0017  * 1. Redistributions of source code must retain the above copyright
0018  *    notice, this list of conditions and the following disclaimer.
0019  * 2. Redistributions in binary form must reproduce the above copyright
0020  *    notice, this list of conditions and the following disclaimer in the
0021  *    documentation and/or other materials provided with the distribution.
0022  *
0023  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0024  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0025  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0026  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0027  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0028  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0029  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0030  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0031  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0032  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0033  * POSSIBILITY OF SUCH DAMAGE.
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     /* Wait */
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   /* Clock */
0187   LPC32XX_FLASHCLK_CTRL = FLASHCLK_IRQ_MLC | FLASHCLK_MLC_CLK_ENABLE;
0188 
0189   /* Timing settings */
0190   mlc_unlock();
0191   mlc->time = cfg->time;
0192 
0193   /* Configuration */
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 }