1 /*
2  * Copyright (c) 2022-2023 HPMicro
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  * Change Logs:
7  * Date         Author      Notes
8  * 2022-03-09   hpmicro     First implementation
9  * 2022-08-01   hpmicro     Fixed random crashing during kvdb_init
10  * 2022-08-03   hpmicro     Improved erase speed
11  * 2023-01-31   hpmicro     Fix random crashing issue if the global interrupt is always enabled
12  *
13  */
14 #include <rtthread.h>
15 #include <rthw.h>
16 #ifdef RT_USING_FAL
17 #include "fal.h"
18 #include "hpm_romapi.h"
19 #include "board.h"
20 #include "hpm_l1c_drv.h"
21 
22 
23 #define FAL_ENTER_CRITICAL() do {\
24         disable_global_irq(CSR_MSTATUS_MIE_MASK);\
25     }while(0)
26 
27 #define FAL_EXIT_CRITICAL() do {\
28         enable_global_irq(CSR_MSTATUS_MIE_MASK);\
29     }while(0)
30 
31 #define FAL_RAMFUNC __attribute__((section(".isr_vector")))
32 
33 
34 /***************************************************************************************************
35  *      FAL Porting Guide
36  *
37  *      1. Most FLASH devices do not support RWW (Read-while-Write), the codes to access the FLASH
38  *         must be placed at RAM or ROM code
39  *      2. During FLASH erase/program, it is recommended to disable the interrupt, or place the
40  *         interrupt related codes to RAM
41  *
42  ***************************************************************************************************/
43 
44 static int init(void);
45 static int read(long offset, rt_uint8_t *buf, rt_size_t size);
46 static int write(long offset, const rt_uint8_t *buf, rt_size_t size);
47 static int erase(long offset, rt_size_t size);
48 
49 static xpi_nor_config_t s_flashcfg;
50 
51 /**
52  * @brief FAL Flash device context
53  */
54 struct fal_flash_dev nor_flash0 =
55     {
56             .name = NOR_FLASH_DEV_NAME,
57             /* If porting this code to the device with FLASH connected to XPI1, the address must be changed to 0x90000000 */
58             .addr = NOR_FLASH_MEM_BASE,
59             .len = 8 * 1024 * 1024,
60             .blk_size = 4096,
61             .ops = { .init = init, .read = read, .write = write, .erase = erase },
62             .write_gran = 1
63     };
64 
65 /**
66  * @brief FAL initialization
67  *        This function probes the FLASH using the ROM API
68  */
init(void)69 FAL_RAMFUNC static int init(void)
70 {
71     int ret = RT_EOK;
72     xpi_nor_config_option_t cfg_option;
73     cfg_option.header.U = BOARD_APP_XPI_NOR_CFG_OPT_HDR;
74     cfg_option.option0.U = BOARD_APP_XPI_NOR_CFG_OPT_OPT0;
75     cfg_option.option1.U = BOARD_APP_XPI_NOR_CFG_OPT_OPT1;
76 
77     FAL_ENTER_CRITICAL();
78     hpm_stat_t status = rom_xpi_nor_auto_config(BOARD_APP_XPI_NOR_XPI_BASE, &s_flashcfg, &cfg_option);
79     FAL_EXIT_CRITICAL();
80     if (status != status_success)
81     {
82         ret = -RT_ERROR;
83     }
84     else
85     {
86         s_flashcfg.device_info.clk_freq_for_non_read_cmd = 0U;
87         /* update the flash chip information */
88         rt_uint32_t sector_size;
89         rom_xpi_nor_get_property(BOARD_APP_XPI_NOR_XPI_BASE, &s_flashcfg, xpi_nor_property_sector_size, &sector_size);
90         rt_uint32_t flash_size;
91         rom_xpi_nor_get_property(BOARD_APP_XPI_NOR_XPI_BASE, &s_flashcfg, xpi_nor_property_total_size, &flash_size);
92         nor_flash0.blk_size = sector_size;
93         nor_flash0.len = flash_size;
94     }
95 
96     return ret;
97 }
98 
99 /**
100  * @brief FAL read function
101  *        Read data from FLASH
102  * @param offset FLASH offset
103  * @param buf Buffer to hold data read by this API
104  * @param size Size of data to be read
105  * @return actual read bytes
106  */
read(long offset,rt_uint8_t * buf,rt_size_t size)107 FAL_RAMFUNC static int read(long offset, rt_uint8_t *buf, rt_size_t size)
108 {
109     rt_uint32_t flash_addr = nor_flash0.addr + offset;
110     rt_uint32_t aligned_start = HPM_L1C_CACHELINE_ALIGN_DOWN(flash_addr);
111     rt_uint32_t aligned_end = HPM_L1C_CACHELINE_ALIGN_UP(flash_addr + size);
112     rt_uint32_t aligned_size = aligned_end - aligned_start;
113     rt_base_t level = rt_hw_interrupt_disable();
114     l1c_dc_invalidate(aligned_start, aligned_size);
115     rt_hw_interrupt_enable(level);
116 
117     (void) rt_memcpy(buf, (void*) flash_addr, size);
118 
119     return size;
120 }
121 
122 /**
123  * @brief Write unaligned data to the page
124  * @param offset FLASH offset
125  * @param buf Data buffer
126  * @param size Size of data to be written
127  * @return actual size of written data or error code
128  */
write_unaligned_page_data(long offset,const rt_uint32_t * buf,rt_size_t size)129 FAL_RAMFUNC static int write_unaligned_page_data(long offset, const rt_uint32_t *buf, rt_size_t size)
130 {
131     hpm_stat_t status;
132 
133     FAL_ENTER_CRITICAL();
134     status = rom_xpi_nor_program(BOARD_APP_XPI_NOR_XPI_BASE, xpi_xfer_channel_auto, &s_flashcfg, buf, offset, size);
135     FAL_EXIT_CRITICAL();
136 
137     if (status != status_success)
138     {
139         return -RT_ERROR;
140         rt_kprintf("write failed, status=%d\n", status);
141     }
142 
143     return size;
144 }
145 
146 /**
147  * @brief FAL write function
148  *        Write data to specified FLASH address
149  * @param offset FLASH offset
150  * @param buf Data buffer
151  * @param size Size of data to be written
152  * @return actual size of written data or error code
153  */
write(long offset,const rt_uint8_t * buf,rt_size_t size)154 FAL_RAMFUNC static int write(long offset, const rt_uint8_t *buf, rt_size_t size)
155 {
156     rt_uint32_t *src = NULL;
157     rt_uint32_t buf_32[64];
158     rt_uint32_t write_size;
159     rt_size_t remaining_size = size;
160     int ret = (int)size;
161 
162     rt_uint32_t page_size;
163     rom_xpi_nor_get_property(BOARD_APP_XPI_NOR_XPI_BASE, &s_flashcfg, xpi_nor_property_page_size, &page_size);
164     rt_uint32_t offset_in_page = offset % page_size;
165     if (offset_in_page != 0)
166     {
167         rt_uint32_t write_size_in_page = page_size - offset_in_page;
168         rt_uint32_t write_page_size = MIN(write_size_in_page, size);
169         (void) rt_memcpy(buf_32, buf, write_page_size);
170         write_size = write_unaligned_page_data(offset, buf_32, write_page_size);
171         if (write_size < 0)
172         {
173             ret = -RT_ERROR;
174             goto write_quit;
175         }
176 
177         remaining_size -= write_page_size;
178         offset += write_page_size;
179         buf += write_page_size;
180     }
181 
182     while (remaining_size > 0)
183     {
184         write_size = MIN(remaining_size, sizeof(buf_32));
185         rt_memcpy(buf_32, buf, write_size);
186         src = &buf_32[0];
187 
188         FAL_ENTER_CRITICAL();
189         hpm_stat_t status = rom_xpi_nor_program(BOARD_APP_XPI_NOR_XPI_BASE, xpi_xfer_channel_auto, &s_flashcfg, src,
190                 offset, write_size);
191         FAL_EXIT_CRITICAL();
192 
193         if (status != status_success)
194         {
195             ret = -RT_ERROR;
196             rt_kprintf("write failed, status=%d\n", status);
197             break;
198         }
199 
200         remaining_size -= write_size;
201         buf += write_size;
202         offset += write_size;
203     }
204 
205 write_quit:
206     return ret;
207 }
208 
209 /**
210  * @brief FAL erase function
211  *        Erase specified FLASH region
212  * @param offset the start FLASH address to be erased
213  * @param size size of the region to be erased
214  * @ret RT_EOK Erase operation is successful
215  * @retval -RT_ERROR Erase operation failed
216  */
erase(long offset,rt_size_t size)217 FAL_RAMFUNC static int erase(long offset, rt_size_t size)
218 {
219     rt_uint32_t aligned_size = (size + nor_flash0.blk_size - 1U) & ~(nor_flash0.blk_size - 1U);
220     hpm_stat_t status;
221     int ret = (int)size;
222 
223     rt_uint32_t block_size;
224     rt_uint32_t sector_size;
225     (void) rom_xpi_nor_get_property(BOARD_APP_XPI_NOR_XPI_BASE, &s_flashcfg, xpi_nor_property_sector_size, &sector_size);
226     (void) rom_xpi_nor_get_property(BOARD_APP_XPI_NOR_XPI_BASE, &s_flashcfg, xpi_nor_property_block_size, &block_size);
227     rt_uint32_t erase_unit;
228     while (aligned_size > 0)
229     {
230         FAL_ENTER_CRITICAL();
231         if ((offset % block_size == 0) && (aligned_size >= block_size))
232         {
233             erase_unit = block_size;
234             status = rom_xpi_nor_erase_block(BOARD_APP_XPI_NOR_XPI_BASE, xpi_xfer_channel_auto, &s_flashcfg, offset);
235         }
236         else
237         {
238             erase_unit = sector_size;
239             status = rom_xpi_nor_erase_sector(BOARD_APP_XPI_NOR_XPI_BASE, xpi_xfer_channel_auto, &s_flashcfg, offset);
240         }
241         FAL_EXIT_CRITICAL();
242 
243         if (status != status_success)
244         {
245             ret = -RT_ERROR;
246             break;
247         }
248         offset += erase_unit;
249         aligned_size -= erase_unit;
250     }
251 
252     return ret;
253 }
254 #endif /* RT_USING_FAL */
255