1 /***************************************************************************
2  * Copyright (c) 2024 Microsoft Corporation
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the MIT License which is available at
6  * https://opensource.org/licenses/MIT.
7  *
8  * SPDX-License-Identifier: MIT
9  **************************************************************************/
10 
11 
12 /**************************************************************************/
13 /**************************************************************************/
14 /**                                                                       */
15 /** LevelX Component                                                      */
16 /**                                                                       */
17 /**   NAND Flash                                                          */
18 /**                                                                       */
19 /**************************************************************************/
20 /**************************************************************************/
21 
22 #define LX_SOURCE_CODE
23 
24 
25 /* Disable ThreadX error checking.  */
26 
27 #ifndef LX_DISABLE_ERROR_CHECKING
28 #define LX_DISABLE_ERROR_CHECKING
29 #endif
30 
31 
32 /* Include necessary system files.  */
33 
34 #include "lx_api.h"
35 
36 
37 /**************************************************************************/
38 /*                                                                        */
39 /*  FUNCTION                                               RELEASE        */
40 /*                                                                        */
41 /*    _lx_nand_flash_sector_write                         PORTABLE C      */
42 /*                                                           6.2.1       */
43 /*  AUTHOR                                                                */
44 /*                                                                        */
45 /*    Xiuwen Cai, Microsoft Corporation                                   */
46 /*                                                                        */
47 /*  DESCRIPTION                                                           */
48 /*                                                                        */
49 /*    This function writes a logical sector to the NAND flash page.       */
50 /*                                                                        */
51 /*  INPUT                                                                 */
52 /*                                                                        */
53 /*    nand_flash                            NAND flash instance           */
54 /*    logical_sector                        Logical sector number         */
55 /*    buffer                                Pointer to buffer to write    */
56 /*                                            (the size is number of      */
57 /*                                             bytes in a page)           */
58 /*                                                                        */
59 /*  OUTPUT                                                                */
60 /*                                                                        */
61 /*    return status                                                       */
62 /*                                                                        */
63 /*  CALLS                                                                 */
64 /*                                                                        */
65 /*    _lx_nand_flash_block_find             Find the mapped block         */
66 /*    _lx_nand_flash_block_allocate         Allocate block                */
67 /*    _lx_nand_flash_mapped_block_list_remove                             */
68 /*                                          Remove mapped block           */
69 /*    _lx_nand_flash_data_page_copy         Copy data pages               */
70 /*    _lx_nand_flash_free_block_list_add    Add free block to list        */
71 /*    _lx_nand_flash_block_mapping_set      Set block mapping             */
72 /*    lx_nand_flash_driver_pages_write      Write pages                   */
73 /*    _lx_nand_flash_block_status_set       Set block status              */
74 /*    _lx_nand_flash_driver_block_erase     Erase block                   */
75 /*    _lx_nand_flash_erase_count_set        Set erase count               */
76 /*    _lx_nand_flash_block_data_move        Move block data               */
77 /*    _lx_nand_flash_mapped_block_list_add  Add mapped block to list      */
78 /*    _lx_nand_flash_system_error           Internal system error handler */
79 /*    tx_mutex_get                          Get thread protection         */
80 /*    tx_mutex_put                          Release thread protection     */
81 /*                                                                        */
82 /*  CALLED BY                                                             */
83 /*                                                                        */
84 /*    Application Code                                                    */
85 /*                                                                        */
86 /*  RELEASE HISTORY                                                       */
87 /*                                                                        */
88 /*    DATE              NAME                      DESCRIPTION             */
89 /*                                                                        */
90 /*  03-08-2023     Xiuwen Cai               Initial Version 6.2.1        */
91 /*                                                                        */
92 /**************************************************************************/
_lx_nand_flash_sector_write(LX_NAND_FLASH * nand_flash,ULONG logical_sector,VOID * buffer)93 UINT  _lx_nand_flash_sector_write(LX_NAND_FLASH *nand_flash, ULONG logical_sector, VOID *buffer)
94 {
95 
96 UINT                                status;
97 ULONG                               block;
98 ULONG                               new_block;
99 ULONG                               page;
100 USHORT                              block_status = 0;
101 USHORT                              new_block_status = LX_NAND_BLOCK_STATUS_ALLOCATED;
102 UCHAR                               *spare_buffer_ptr;
103 UINT                                update_mapping = LX_FALSE;
104 UINT                                copy_block = LX_FALSE;
105 
106 #ifdef LX_THREAD_SAFE_ENABLE
107 
108     /* Obtain the thread safe mutex.  */
109     tx_mutex_get(&nand_flash -> lx_nand_flash_mutex, TX_WAIT_FOREVER);
110 #endif
111 
112     /* Increment the number of write requests.  */
113     nand_flash -> lx_nand_flash_diagnostic_sector_write_requests++;
114 
115     /* See if we can find the logical sector in the current mapping.  */
116     status = _lx_nand_flash_block_find(nand_flash, logical_sector, &block, &block_status);
117 
118     /* Check return status.   */
119     if(status != LX_SUCCESS)
120     {
121 
122         /* Call system error handler.  */
123         _lx_nand_flash_system_error(nand_flash, status, block, 0);
124 
125         /* Determine if the error is fatal.  */
126         if (status != LX_NAND_ERROR_CORRECTED)
127         {
128 #ifdef LX_THREAD_SAFE_ENABLE
129 
130             /* Release the thread safe mutex.  */
131             tx_mutex_put(&nand_flash -> lx_nand_flash_mutex);
132 #endif
133 
134             /* Return an error.  */
135             return(LX_ERROR);
136         }
137     }
138 
139     /* Check if block is unmapped or block is full.  */
140     if (block == LX_NAND_BLOCK_UNMAPPED || block_status & LX_NAND_BLOCK_STATUS_FULL)
141     {
142 
143         /* Allocate a new block.  */
144         status = _lx_nand_flash_block_allocate(nand_flash, &new_block);
145 
146         /* Check if there is no blocks.  */
147         if (status == LX_NO_BLOCKS)
148         {
149 #ifdef LX_THREAD_SAFE_ENABLE
150 
151             /* Release the thread safe mutex.  */
152             tx_mutex_put(&nand_flash -> lx_nand_flash_mutex);
153 #endif
154 
155             /* Return error.  */
156             return(status);
157         }
158 
159         /* Check return status.  */
160         else if (status != LX_SUCCESS)
161         {
162 
163             /* Call system error handler.  */
164             _lx_nand_flash_system_error(nand_flash, status, new_block, 0);
165 
166             /* Determine if the error is fatal.  */
167             if (status != LX_NAND_ERROR_CORRECTED)
168             {
169 #ifdef LX_THREAD_SAFE_ENABLE
170 
171                 /* Release the thread safe mutex.  */
172                 tx_mutex_put(&nand_flash -> lx_nand_flash_mutex);
173 #endif
174 
175                 /* Return an error.  */
176                 return(LX_ERROR);
177             }
178         }
179 
180         /* Check if the block is full.  */
181         if (block_status & LX_NAND_BLOCK_STATUS_FULL)
182         {
183 
184             /* Set copy block flag.  */
185             copy_block = LX_TRUE;
186         }
187 
188         /* Set update mapping flag.  */
189         update_mapping = LX_TRUE;
190     }
191     else
192     {
193 
194         /* Set new block to the same as old block.  */
195         new_block = block;
196         new_block_status = block_status;
197     }
198 
199     /* Check if copy block flag is set.  */
200     if (copy_block)
201     {
202 
203         /* Remove the old block from mapped block list.  */
204         _lx_nand_flash_mapped_block_list_remove(nand_flash, logical_sector / nand_flash -> lx_nand_flash_pages_per_block);
205 
206         /* Copy valid sector to new block.  */
207         status =  _lx_nand_flash_data_page_copy(nand_flash, logical_sector - (logical_sector % nand_flash -> lx_nand_flash_pages_per_block), block, block_status, new_block, &new_block_status, (logical_sector % nand_flash -> lx_nand_flash_pages_per_block));
208 
209         /* Check for an error from flash driver.   */
210         if (status)
211         {
212 
213             /* Call system error handler.  */
214             _lx_nand_flash_system_error(nand_flash, status, block, 0);
215 #ifdef LX_THREAD_SAFE_ENABLE
216 
217             /* Release the thread safe mutex.  */
218             tx_mutex_put(&nand_flash -> lx_nand_flash_mutex);
219 #endif
220 
221             /* Return an error.  */
222             return(LX_ERROR);
223         }
224     }
225 
226     /* Check if update mapping flag is set.  */
227     if (update_mapping)
228     {
229 
230         /* Update block mapping.  */
231         _lx_nand_flash_block_mapping_set(nand_flash, logical_sector, new_block);
232     }
233 
234     /* Setup spare buffer pointer.  */
235     spare_buffer_ptr = nand_flash -> lx_nand_flash_page_buffer;
236 
237     /* Set spare buffer to all 0xFF bytes.  */
238     LX_MEMSET(spare_buffer_ptr, 0xFF, nand_flash -> lx_nand_flash_spare_total_length);
239 
240     /* Check if there is enough spare data for metadata block number.  */
241     if (nand_flash -> lx_nand_flash_spare_data2_length >= sizeof(USHORT))
242     {
243 
244         /* Save metadata block number in spare bytes.  */
245         LX_UTILITY_SHORT_SET(&spare_buffer_ptr[nand_flash -> lx_nand_flash_spare_data2_offset], nand_flash -> lx_nand_flash_metadata_block_number);
246     }
247 
248     /* Set page type and sector address.  */
249     LX_UTILITY_LONG_SET(&spare_buffer_ptr[nand_flash -> lx_nand_flash_spare_data1_offset], LX_NAND_PAGE_TYPE_USER_DATA | logical_sector);
250 
251     /* Get page to write.  */
252     page = new_block_status & LX_NAND_BLOCK_STATUS_PAGE_NUMBER_MASK;
253 
254     /* Write the page.  */
255 #ifdef LX_NAND_ENABLE_CONTROL_BLOCK_FOR_DRIVER_INTERFACE
256     status = (nand_flash -> lx_nand_flash_driver_pages_write)(nand_flash, new_block, page, (UCHAR*)buffer, spare_buffer_ptr, 1);
257 #else
258     status = (nand_flash -> lx_nand_flash_driver_pages_write)(new_block, page, (UCHAR*)buffer, spare_buffer_ptr, 1);
259 #endif
260 
261     /* Check for an error from flash driver.   */
262     if (status)
263     {
264 
265         /* Call system error handler.  */
266         _lx_nand_flash_system_error(nand_flash, status, new_block, 0);
267 #ifdef LX_THREAD_SAFE_ENABLE
268 
269         /* Release the thread safe mutex.  */
270         tx_mutex_put(&nand_flash -> lx_nand_flash_mutex);
271 #endif
272 
273         /* Return an error.  */
274         return(LX_ERROR);
275     }
276 
277     /* Determine if the sector number is sequential.  */
278     if ((new_block_status & LX_NAND_BLOCK_STATUS_PAGE_NUMBER_MASK) != (logical_sector % nand_flash -> lx_nand_flash_pages_per_block))
279     {
280 
281         /* Set non sequential status flag.  */
282         new_block_status |= LX_NAND_BLOCK_STATUS_NON_SEQUENTIAL;
283     }
284 
285     /* Increase page number.  */
286     page++;
287 
288     /* Check if page number reaches total pages per block.  */
289     if (page == nand_flash -> lx_nand_flash_pages_per_block)
290     {
291 
292         /* Set block full status flag.  */
293         new_block_status |= LX_NAND_BLOCK_STATUS_FULL;
294     }
295 
296     /* Build block status word.  */
297     new_block_status = (USHORT)(page | (new_block_status & ~LX_NAND_BLOCK_STATUS_PAGE_NUMBER_MASK));
298 
299     /* Determine if there are sectors after the addressed sector need to be copied.  */
300     if (copy_block && ((logical_sector % nand_flash -> lx_nand_flash_pages_per_block) < (nand_flash -> lx_nand_flash_pages_per_block - 1)))
301     {
302 
303         /* Copy valid sector to new block.  */
304         status = _lx_nand_flash_data_page_copy(nand_flash, logical_sector + 1, block, block_status, new_block, &new_block_status, (nand_flash -> lx_nand_flash_pages_per_block - 1) - (logical_sector % nand_flash -> lx_nand_flash_pages_per_block));
305 
306         /* Check for an error from flash driver.   */
307         if (status)
308         {
309 
310             /* Call system error handler.  */
311             _lx_nand_flash_system_error(nand_flash, status, block, 0);
312 #ifdef LX_THREAD_SAFE_ENABLE
313 
314             /* Release the thread safe mutex.  */
315             tx_mutex_put(&nand_flash -> lx_nand_flash_mutex);
316 #endif
317 
318             /* Return an error.  */
319             return(LX_ERROR);
320         }
321     }
322 
323     /* Set new block status.  */
324     status = _lx_nand_flash_block_status_set(nand_flash, new_block, new_block_status);
325 
326     /* Check for an error from flash driver.   */
327     if (status)
328     {
329 
330         /* Call system error handler.  */
331         _lx_nand_flash_system_error(nand_flash, status, new_block, 0);
332 #ifdef LX_THREAD_SAFE_ENABLE
333 
334         /* Release the thread safe mutex.  */
335         tx_mutex_put(&nand_flash -> lx_nand_flash_mutex);
336 #endif
337 
338         /* Return an error.  */
339         return(LX_ERROR);
340     }
341 
342     /* Check if copy block flag is set.  */
343     if (copy_block)
344     {
345 
346         /* Erase old block.  */
347         status = _lx_nand_flash_driver_block_erase(nand_flash, block, nand_flash -> lx_nand_flash_base_erase_count + nand_flash -> lx_nand_flash_erase_count_table[block] + 1);
348 
349         /* Check for an error from flash driver.   */
350         if (status)
351         {
352 
353             /* Call system error handler.  */
354             _lx_nand_flash_system_error(nand_flash, status, block, 0);
355 #ifdef LX_THREAD_SAFE_ENABLE
356 
357             /* Release the thread safe mutex.  */
358             tx_mutex_put(&nand_flash -> lx_nand_flash_mutex);
359 #endif
360 
361             /* Return an error.  */
362             return(LX_ERROR);
363         }
364 
365         /* Update erase count for the old block.  */
366         status = _lx_nand_flash_erase_count_set(nand_flash, block, (UCHAR)(nand_flash -> lx_nand_flash_erase_count_table[block] + 1));
367 
368         /* Check for an error from flash driver.   */
369         if (status)
370         {
371 
372             /* Call system error handler.  */
373             _lx_nand_flash_system_error(nand_flash, status, block, 0);
374 #ifdef LX_THREAD_SAFE_ENABLE
375 
376             /* Release the thread safe mutex.  */
377             tx_mutex_put(&nand_flash -> lx_nand_flash_mutex);
378 #endif
379 
380             /* Return an error.  */
381             return(LX_ERROR);
382         }
383 
384         /* Check if the block has too many erases.  */
385         if (nand_flash -> lx_nand_flash_erase_count_table[block] > LX_NAND_FLASH_MAX_ERASE_COUNT_DELTA)
386         {
387 
388             /* Move data from less worn block.  */
389             _lx_nand_flash_block_data_move(nand_flash, block);
390         }
391         else
392         {
393 
394             /* Set the block status to free.  */
395             status = _lx_nand_flash_block_status_set(nand_flash, block, LX_NAND_BLOCK_STATUS_FREE);
396 
397             /* Check for an error from flash driver.   */
398             if (status)
399             {
400 
401                 /* Call system error handler.  */
402                 _lx_nand_flash_system_error(nand_flash, status, block, 0);
403 #ifdef LX_THREAD_SAFE_ENABLE
404 
405                 /* Release the thread safe mutex.  */
406                 tx_mutex_put(&nand_flash -> lx_nand_flash_mutex);
407 #endif
408 
409                 /* Return an error.  */
410                 return(LX_ERROR);
411             }
412 
413             /* Add the block to free block list.  */
414             _lx_nand_flash_free_block_list_add(nand_flash, block);
415         }
416     }
417 
418     /* Check if update mapping flag is set.  */
419     if (update_mapping)
420     {
421 
422         /* Add the new block to mapped block list.  */
423         _lx_nand_flash_mapped_block_list_add(nand_flash, logical_sector / nand_flash -> lx_nand_flash_pages_per_block);
424     }
425 
426 #ifdef LX_THREAD_SAFE_ENABLE
427 
428     /* Release the thread safe mutex.  */
429     tx_mutex_put(&nand_flash -> lx_nand_flash_mutex);
430 #endif
431 
432     /* Return the completion status.  */
433     return(status);
434 }
435 
436 
437