1 /*
2  * Copyright 2019-2020 NXP
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <stdbool.h>
8 
9 #include "mflash_drv.h"
10 
11 #include "fsl_flexspi.h"
12 #include "fsl_cache.h"
13 #include "pin_mux.h"
14 
15 #define NOR_CMD_LUT_SEQ_IDX_READ_NORMAL        2
16 #define NOR_CMD_LUT_SEQ_IDX_READ_FAST          1
17 #define NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD     0 // set it to index0 to align with xip settings
18 #define NOR_CMD_LUT_SEQ_IDX_READSTATUS         3
19 #define NOR_CMD_LUT_SEQ_IDX_WRITEENABLE        4
20 #define NOR_CMD_LUT_SEQ_IDX_ERASESECTOR        5
21 #define NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_SINGLE 6
22 #define NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD   7
23 #define NOR_CMD_LUT_SEQ_IDX_READID             8
24 #define NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG     9
25 #define NOR_CMD_LUT_SEQ_IDX_ENTERQPI           10
26 #define NOR_CMD_LUT_SEQ_IDX_EXITQPI            11
27 #define NOR_CMD_LUT_SEQ_IDX_READSTATUSREG      12
28 
29 #define CUSTOM_LUT_LENGTH        60
30 #define FLASH_BUSY_STATUS_POL    1
31 #define FLASH_BUSY_STATUS_OFFSET 0
32 
33 #define FLASH_SIZE 0x10000
34 
35 #ifndef XIP_EXTERNAL_FLASH
36 flexspi_device_config_t deviceconfig = {
37     .flexspiRootClk       = 100000000,
38     .flashSize            = FLASH_SIZE,
39     .CSIntervalUnit       = kFLEXSPI_CsIntervalUnit1SckCycle,
40     .CSInterval           = 2,
41     .CSHoldTime           = 3,
42     .CSSetupTime          = 3,
43     .dataValidTime        = 0,
44     .columnspace          = 0,
45     .enableWordAddress    = 0,
46     .AWRSeqIndex          = 0,
47     .AWRSeqNumber         = 0,
48     .ARDSeqIndex          = NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD,
49     .ARDSeqNumber         = 1,
50     .AHBWriteWaitUnit     = kFLEXSPI_AhbWriteWaitUnit2AhbCycle,
51     .AHBWriteWaitInterval = 0,
52 };
53 #endif
54 
55 static uint32_t customLUT[CUSTOM_LUT_LENGTH] = {
56     /* Normal read mode -SDR */
57     [4 * NOR_CMD_LUT_SEQ_IDX_READ_NORMAL] =
58         FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x03, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
59     [4 * NOR_CMD_LUT_SEQ_IDX_READ_NORMAL + 1] =
60         FLEXSPI_LUT_SEQ(kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
61 
62     /* Fast read mode - SDR */
63     [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST] =
64         FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x0B, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
65     /* In XIP, the speed of external flash is set to 133MHz, and the external flash require to match 8 corresponding dummy cycles.
66      * However, cm4 core cases or other non XIP boot targets are not suitable for XIP boot flow, uses flash default configuration */
67 #if defined(XIP_BOOT_HEADER_ENABLE) && XIP_BOOT_HEADER_ENABLE
68     [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST + 1] = FLEXSPI_LUT_SEQ(
69         kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_1PAD, 0x0B, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),
70 #else
71     [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST + 1] = FLEXSPI_LUT_SEQ(
72         kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_1PAD, 0x08, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),
73 #endif
74 
75     /* Fast read quad mode - SDR */
76     [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD] =
77         FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xEB, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_4PAD, 0x18),
78 #if defined(XIP_BOOT_HEADER_ENABLE) && XIP_BOOT_HEADER_ENABLE
79     [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD + 1] = FLEXSPI_LUT_SEQ(
80         kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_4PAD, 0x09, kFLEXSPI_Command_READ_SDR, kFLEXSPI_4PAD, 0x04),
81 #else
82     [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD + 1] = FLEXSPI_LUT_SEQ(
83         kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_4PAD, 0x06, kFLEXSPI_Command_READ_SDR, kFLEXSPI_4PAD, 0x04),
84 #endif
85 
86     /* Read extend parameters */
87     [4 * NOR_CMD_LUT_SEQ_IDX_READSTATUS] =
88         FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x81, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),
89 
90     /* Write Enable */
91     [4 * NOR_CMD_LUT_SEQ_IDX_WRITEENABLE] =
92         FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x06, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
93 
94     /* Erase Sector  */
95     [4 * NOR_CMD_LUT_SEQ_IDX_ERASESECTOR] =
96         FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x20, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
97 
98     /* Page Program - single mode */
99     [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_SINGLE] =
100         FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x02, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
101     [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_SINGLE + 1] =
102         FLEXSPI_LUT_SEQ(kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
103 
104     /* Page Program - quad mode */
105     [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD] =
106         FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x32, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
107     [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD + 1] =
108         FLEXSPI_LUT_SEQ(kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_4PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
109 
110     /* Read ID */
111     [4 * NOR_CMD_LUT_SEQ_IDX_READID] =
112         FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xAB, kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_1PAD, 0x18),
113     [4 * NOR_CMD_LUT_SEQ_IDX_READID + 1] =
114         FLEXSPI_LUT_SEQ(kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
115 
116     /* Enable Quad mode */
117     [4 * NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG] =
118         FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x01, kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x04),
119 
120     /* Enter QPI mode */
121     [4 * NOR_CMD_LUT_SEQ_IDX_ENTERQPI] =
122         FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x35, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
123 
124     /* Exit QPI mode */
125     [4 * NOR_CMD_LUT_SEQ_IDX_EXITQPI] =
126         FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_4PAD, 0xF5, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
127 
128     /* Read status register */
129     [4 * NOR_CMD_LUT_SEQ_IDX_READSTATUSREG] =
130         FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x05, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),
131 };
132 
flexspi_nor_wait_bus_busy(FLEXSPI_Type * base)133 static status_t flexspi_nor_wait_bus_busy(FLEXSPI_Type *base)
134 {
135     /* Wait status ready. */
136     bool isBusy;
137     uint32_t readValue;
138     status_t status;
139     flexspi_transfer_t flashXfer;
140 
141     flashXfer.deviceAddress = 0;
142     flashXfer.port          = kFLEXSPI_PortA1;
143     flashXfer.cmdType       = kFLEXSPI_Read;
144     flashXfer.SeqNumber     = 1;
145     flashXfer.seqIndex      = NOR_CMD_LUT_SEQ_IDX_READSTATUSREG;
146     flashXfer.data          = &readValue;
147     flashXfer.dataSize      = 1;
148 
149     do
150     {
151         status = FLEXSPI_TransferBlocking(base, &flashXfer);
152 
153         if (status != kStatus_Success)
154         {
155             return status;
156         }
157         if (FLASH_BUSY_STATUS_POL)
158         {
159             if (readValue & (1U << FLASH_BUSY_STATUS_OFFSET))
160             {
161                 isBusy = true;
162             }
163             else
164             {
165                 isBusy = false;
166             }
167         }
168         else
169         {
170             if (readValue & (1U << FLASH_BUSY_STATUS_OFFSET))
171             {
172                 isBusy = false;
173             }
174             else
175             {
176                 isBusy = true;
177             }
178         }
179 
180     } while (isBusy);
181 
182     return status;
183 }
184 
flexspi_nor_write_enable(FLEXSPI_Type * base,uint32_t baseAddr)185 static status_t flexspi_nor_write_enable(FLEXSPI_Type *base, uint32_t baseAddr)
186 {
187     flexspi_transfer_t flashXfer;
188     status_t status;
189 
190     /* Write neable */
191     flashXfer.deviceAddress = baseAddr;
192     flashXfer.port          = kFLEXSPI_PortA1;
193     flashXfer.cmdType       = kFLEXSPI_Command;
194     flashXfer.SeqNumber     = 1;
195     flashXfer.seqIndex      = NOR_CMD_LUT_SEQ_IDX_WRITEENABLE;
196 
197     status = FLEXSPI_TransferBlocking(base, &flashXfer);
198 
199     return status;
200 }
201 
flexspi_nor_enable_quad_mode(FLEXSPI_Type * base)202 status_t flexspi_nor_enable_quad_mode(FLEXSPI_Type *base)
203 {
204     flexspi_transfer_t flashXfer;
205     status_t status;
206     uint32_t writeValue = 0x40;
207 
208     /* Write neable */
209     status = flexspi_nor_write_enable(base, 0);
210 
211     if (status != kStatus_Success)
212     {
213         return status;
214     }
215 
216     /* Enable quad mode. */
217     flashXfer.deviceAddress = 0;
218     flashXfer.port          = kFLEXSPI_PortA1;
219     flashXfer.cmdType       = kFLEXSPI_Write;
220     flashXfer.SeqNumber     = 1;
221     flashXfer.seqIndex      = NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG;
222     flashXfer.data          = &writeValue;
223     flashXfer.dataSize      = 1;
224 
225     status = FLEXSPI_TransferBlocking(base, &flashXfer);
226     if (status != kStatus_Success)
227     {
228         return status;
229     }
230 
231     status = flexspi_nor_wait_bus_busy(base);
232 
233     return status;
234 }
235 
flexspi_nor_flash_sector_erase(FLEXSPI_Type * base,uint32_t address)236 static status_t flexspi_nor_flash_sector_erase(FLEXSPI_Type *base, uint32_t address)
237 {
238     status_t status;
239     flexspi_transfer_t flashXfer;
240 
241     /* Write enable */
242     status = flexspi_nor_write_enable(base, 0);
243 
244     if (status != kStatus_Success)
245     {
246         return status;
247     }
248 
249     flashXfer.deviceAddress = address;
250     flashXfer.port          = kFLEXSPI_PortA1;
251     flashXfer.cmdType       = kFLEXSPI_Command;
252     flashXfer.SeqNumber     = 1;
253     flashXfer.seqIndex      = NOR_CMD_LUT_SEQ_IDX_ERASESECTOR;
254     status                  = FLEXSPI_TransferBlocking(base, &flashXfer);
255 
256     if (status != kStatus_Success)
257     {
258         return status;
259     }
260 
261     status = flexspi_nor_wait_bus_busy(base);
262 
263     return status;
264 }
265 
flexspi_nor_flash_page_program(FLEXSPI_Type * base,uint32_t address,const uint32_t * src)266 static status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t address, const uint32_t *src)
267 {
268     status_t status;
269     flexspi_transfer_t flashXfer;
270 
271     /* Write enable */
272     status = flexspi_nor_write_enable(base, address);
273 
274     if (status != kStatus_Success)
275     {
276         return status;
277     }
278 
279     /* Prepare page program command */
280     flashXfer.deviceAddress = address;
281     flashXfer.port          = kFLEXSPI_PortA1;
282     flashXfer.cmdType       = kFLEXSPI_Write;
283     flashXfer.SeqNumber     = 1;
284     flashXfer.seqIndex      = NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD;
285     flashXfer.data          = (uint32_t *)src;
286     flashXfer.dataSize      = MFLASH_PAGE_SIZE;
287     status                  = FLEXSPI_TransferBlocking(base, &flashXfer);
288 
289     if (status != kStatus_Success)
290     {
291         return status;
292     }
293 
294     status = flexspi_nor_wait_bus_busy(base);
295 
296     return status;
297 }
298 
flexspi_nor_read_data(FLEXSPI_Type * base,uint32_t startAddress,uint32_t * buffer,uint32_t length)299 static status_t flexspi_nor_read_data(FLEXSPI_Type *base, uint32_t startAddress, uint32_t *buffer, uint32_t length)
300 {
301     status_t status;
302     flexspi_transfer_t flashXfer;
303     uint32_t readAddress = startAddress;
304 
305     /* Read page. */
306     flashXfer.deviceAddress = readAddress;
307     flashXfer.port          = kFLEXSPI_PortA1;
308     flashXfer.cmdType       = kFLEXSPI_Read;
309     flashXfer.SeqNumber     = 1;
310     flashXfer.seqIndex      = NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD;
311     flashXfer.data          = buffer;
312     flashXfer.dataSize      = length;
313 
314     status = FLEXSPI_TransferBlocking(base, &flashXfer);
315 
316     return status;
317 }
318 
319 /* Initialize flash peripheral,
320  * cannot be invoked directly, requires calling wrapper in non XIP memory */
mflash_drv_init_internal(void)321 static int32_t mflash_drv_init_internal(void)
322 {
323     status_t status = kStatus_Success;
324 
325     /* NOTE: Multithread access is not supported for SRAM target.
326      *       XIP target MUST be protected by disabling global interrupts
327      *       since all ISR (and API that is used inside) is placed at XIP.
328      *       It is necessary to place at least "mflash_drv.o", "fsl_flexspi.o" to SRAM */
329     /* disable interrupts when running from XIP */
330     uint32_t primask = __get_PRIMASK();
331 
332     __asm("cpsid i");
333 
334 #ifndef XIP_EXTERNAL_FLASH
335     flexspi_config_t config;
336 
337     /* Get FLEXSPI default settings and configure the flexspi. */
338     FLEXSPI_GetDefaultConfig(&config);
339 
340     /* Set AHB buffer size for reading data through AHB bus. */
341     config.ahbConfig.enableAHBPrefetch   = true;
342     config.ahbConfig.enableAHBBufferable = true;
343     config.ahbConfig.enableAHBCachable   = true;
344     config.rxSampleClock                 = kFLEXSPI_ReadSampleClkLoopbackFromDqsPad;
345     FLEXSPI_Init(MFLASH_FLEXSPI, &config);
346 
347     /* AHB Read Address option bit. This option bit is intend to remove AHB burst start address alignment limitation */
348     MFLASH_FLEXSPI->AHBCR |= FLEXSPI_AHBCR_READADDROPT_MASK;
349 
350     /* Configure flash settings according to serial flash feature. */
351     FLEXSPI_SetFlashConfig(MFLASH_FLEXSPI, &deviceconfig, kFLEXSPI_PortA1);
352 #endif
353 
354     /* Update LUT table. */
355     FLEXSPI_UpdateLUT(MFLASH_FLEXSPI, 0, customLUT, CUSTOM_LUT_LENGTH);
356 
357 #ifndef XIP_EXTERNAL_FLASH
358     /* Enter quad mode. */
359     status = flexspi_nor_enable_quad_mode(MFLASH_FLEXSPI);
360 #endif
361 
362     /* Do software reset. */
363     FLEXSPI_SoftwareReset(MFLASH_FLEXSPI);
364 
365     if (primask == 0)
366     {
367         __asm("cpsie i");
368     }
369 
370     return status;
371 }
372 
373 /* API - initialize 'mflash' */
mflash_drv_init(void)374 int32_t mflash_drv_init(void)
375 {
376     /* Necessary to have double wrapper call in non_xip memory */
377     return mflash_drv_init_internal();
378 }
379 
380 /* Internal - erase single sector */
mflash_drv_sector_erase_internal(uint32_t sector_addr)381 static int32_t mflash_drv_sector_erase_internal(uint32_t sector_addr)
382 {
383     status_t status;
384     uint32_t primask = __get_PRIMASK();
385 
386     __asm("cpsid i");
387 
388     status = flexspi_nor_flash_sector_erase(MFLASH_FLEXSPI, sector_addr);
389 
390     /* Do software reset. */
391     FLEXSPI_SoftwareReset(MFLASH_FLEXSPI);
392 
393     DCACHE_InvalidateByRange(MFLASH_BASE_ADDRESS + sector_addr, MFLASH_SECTOR_SIZE);
394 
395     if (primask == 0)
396     {
397         __asm("cpsie i");
398     }
399 
400     /* Flush pipeline to allow pending interrupts take place
401      * before starting next loop */
402     __ISB();
403 
404     return status;
405 }
406 
407 /* Calling wrapper for 'mflash_drv_sector_erase_internal'.
408  * Erase one sector starting at 'sector_addr' - must be sector aligned.
409  */
mflash_drv_sector_erase(uint32_t sector_addr)410 int32_t mflash_drv_sector_erase(uint32_t sector_addr)
411 {
412     if (0 == mflash_drv_is_sector_aligned(sector_addr))
413         return kStatus_InvalidArgument;
414 
415     return mflash_drv_sector_erase_internal(sector_addr);
416 }
417 
418 /* Internal - write single page */
mflash_drv_page_program_internal(uint32_t page_addr,uint32_t * data)419 static int32_t mflash_drv_page_program_internal(uint32_t page_addr, uint32_t *data)
420 {
421     uint32_t primask = __get_PRIMASK();
422 
423     __asm("cpsid i");
424 
425     status_t status;
426     status = flexspi_nor_flash_page_program(MFLASH_FLEXSPI, page_addr, data);
427 
428     /* Do software reset. */
429     FLEXSPI_SoftwareReset(MFLASH_FLEXSPI);
430 
431     DCACHE_InvalidateByRange(MFLASH_BASE_ADDRESS + page_addr, MFLASH_PAGE_SIZE);
432 
433     if (primask == 0)
434     {
435         __asm("cpsie i");
436     }
437 
438     /* Flush pipeline to allow pending interrupts take place
439      * before starting next loop */
440     __ISB();
441 
442     return status;
443 }
444 
445 /* Calling wrapper for 'mflash_drv_page_program_internal'.
446  * Write 'data' to 'page_addr' - must be page aligned.
447  * NOTE: Don't try to store constant data that are located in XIP !!
448  */
mflash_drv_page_program(uint32_t page_addr,uint32_t * data)449 int32_t mflash_drv_page_program(uint32_t page_addr, uint32_t *data)
450 {
451     if (0 == mflash_drv_is_page_aligned(page_addr))
452         return kStatus_InvalidArgument;
453 
454     return mflash_drv_page_program_internal(page_addr, data);
455 }
456 
457 /* Internal - read data */
mflash_drv_read_internal(uint32_t addr,uint32_t * buffer,uint32_t len)458 static int32_t mflash_drv_read_internal(uint32_t addr, uint32_t *buffer, uint32_t len)
459 {
460     uint32_t primask = __get_PRIMASK();
461 
462     __asm("cpsid i");
463 
464     status_t status;
465     status = flexspi_nor_read_data(MFLASH_FLEXSPI, addr, buffer, len);
466 
467     /* Do software reset. */
468     FLEXSPI_SoftwareReset(MFLASH_FLEXSPI);
469 
470     if (primask == 0)
471     {
472         __asm("cpsie i");
473     }
474 
475     /* Flush pipeline to allow pending interrupts take place
476      * before starting next loop */
477     __ISB();
478 
479     return status;
480 }
481 
482 /* API - Read data */
mflash_drv_read(uint32_t addr,uint32_t * buffer,uint32_t len)483 int32_t mflash_drv_read(uint32_t addr, uint32_t *buffer, uint32_t len)
484 {
485     /* Check alignment */
486     if (((uint32_t)buffer % 4) || (len % 4))
487         return kStatus_InvalidArgument;
488 
489     return mflash_drv_read_internal(addr, buffer, len);
490 }
491 
492 /* API - Get pointer to FLASH region */
mflash_drv_phys2log(uint32_t addr,uint32_t len)493 void *mflash_drv_phys2log(uint32_t addr, uint32_t len)
494 {
495     return (void *)(addr + MFLASH_BASE_ADDRESS);
496 }
497 
498 /* API - Get pointer to FLASH region */
mflash_drv_log2phys(void * ptr,uint32_t len)499 uint32_t mflash_drv_log2phys(void *ptr, uint32_t len)
500 {
501     if ((uint32_t)ptr < MFLASH_BASE_ADDRESS)
502         return kStatus_InvalidArgument;
503 
504     return ((uint32_t)ptr - MFLASH_BASE_ADDRESS);
505 }
506