diff options
-rw-r--r-- | include/plat/arm/board/common/drivers/norflash.h | 8 | ||||
-rw-r--r-- | plat/arm/board/common/drivers/norflash/norflash.c | 170 |
2 files changed, 138 insertions, 40 deletions
diff --git a/include/plat/arm/board/common/drivers/norflash.h b/include/plat/arm/board/common/drivers/norflash.h index e74635ec..5763b36d 100644 --- a/include/plat/arm/board/common/drivers/norflash.h +++ b/include/plat/arm/board/common/drivers/norflash.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -19,6 +19,7 @@ #define NOR_CMD_WORD_PROGRAM 0x40 #define NOR_CMD_BLOCK_ERASE 0x20 #define NOR_CMD_LOCK_UNLOCK 0x60 +#define NOR_CMD_BLOCK_ERASE_ACK 0xD0 /* Second bus cycle */ #define NOR_LOCK_BLOCK 0x01 @@ -37,8 +38,9 @@ /* Public API */ void nor_send_cmd(uintptr_t base_addr, unsigned long cmd); int nor_word_program(uintptr_t base_addr, unsigned long data); -void nor_lock(uintptr_t base_addr); -void nor_unlock(uintptr_t base_addr); +int nor_lock(uintptr_t base_addr); +int nor_unlock(uintptr_t base_addr); +int nor_erase(uintptr_t base_addr); #endif /* __NORFLASH_H_ */ diff --git a/plat/arm/board/common/drivers/norflash/norflash.c b/plat/arm/board/common/drivers/norflash/norflash.c index cc63d758..722cf33e 100644 --- a/plat/arm/board/common/drivers/norflash/norflash.c +++ b/plat/arm/board/common/drivers/norflash/norflash.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,8 +8,6 @@ #include <mmio.h> #include <norflash.h> -/* Helper macros to access two flash banks in parallel */ -#define NOR_2X16(d) ((d << 16) | (d & 0xffff)) /* * DWS ready poll retries. The number of retries in this driver have been @@ -17,32 +15,72 @@ * model */ #define DWS_WORD_PROGRAM_RETRIES 1000 +#define DWS_WORD_ERASE_RETRIES 3000000 +#define DWS_WORD_LOCK_RETRIES 1000 + +/* Helper macro to detect end of command */ +#define NOR_CMD_END (NOR_DWS | NOR_DWS << 16l) /* - * Poll Write State Machine. Return values: + * This file supplies a low level interface to the vexpress NOR flash + * memory of juno and fvp. This memory is organized as an interleaved + * memory of two chips with a 16 bit word. It means that every 32 bit + * access is going to access to two different chips. This is very + * important when we send commands or read status of the chips + */ + +/* Helper macros to access two flash banks in parallel */ +#define NOR_2X16(d) ((d << 16) | (d & 0xffff)) + +static unsigned int nor_status(uintptr_t base_addr) +{ + unsigned long status; + + nor_send_cmd(base_addr, NOR_CMD_READ_STATUS_REG); + status = mmio_read_32(base_addr); + status |= status >> 16; /* merge status from both flash banks */ + + return status & 0xFFFF; +} + +/* + * Poll Write State Machine. + * Return values: * 0 = WSM ready * -EBUSY = WSM busy after the number of retries */ -static int nor_poll_dws(uintptr_t base_addr, unsigned int retries) +static int nor_poll_dws(uintptr_t base_addr, unsigned long int retries) { - uint32_t status; - int ret; + unsigned long status; - for (;;) { + do { nor_send_cmd(base_addr, NOR_CMD_READ_STATUS_REG); status = mmio_read_32(base_addr); - if ((status & NOR_DWS) && - (status & (NOR_DWS << 16))) { - ret = 0; - break; - } - if (retries-- == 0) { - ret = -EBUSY; - break; - } - } + if ((status & NOR_CMD_END) == NOR_CMD_END) + return 0; + } while (retries-- > 0); - return ret; + return -EBUSY; +} + +/* + * Return values: + * 0 = success + * -EPERM = Device protected or Block locked + * -EIO = General I/O error + */ +static int nor_full_status_check(uintptr_t base_addr) +{ + unsigned long status; + + /* Full status check */ + status = nor_status(base_addr); + + if (status & (NOR_PS | NOR_BLS | NOR_ESS | NOR_PSS)) + return -EPERM; + if (status & (NOR_VPPS | NOR_ES)) + return -EIO; + return 0; } void nor_send_cmd(uintptr_t base_addr, unsigned long cmd) @@ -51,50 +89,108 @@ void nor_send_cmd(uintptr_t base_addr, unsigned long cmd) } /* + * This function programs a word in the flash. Be aware that it only + * can reset bits that were previously set. It cannot set bits that + * were previously reset. The resulting bits = old_bits & new bits. * Return values: - * 0 = success - * -EBUSY = WSM not ready - * -EPERM = Device protected or Block locked + * 0 = success + * otherwise it returns a negative value */ int nor_word_program(uintptr_t base_addr, unsigned long data) { uint32_t status; int ret; + nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG); + /* Set the device in write word mode */ nor_send_cmd(base_addr, NOR_CMD_WORD_PROGRAM); mmio_write_32(base_addr, data); ret = nor_poll_dws(base_addr, DWS_WORD_PROGRAM_RETRIES); - if (ret != 0) { - goto word_program_end; + if (ret == 0) { + /* Full status check */ + nor_send_cmd(base_addr, NOR_CMD_READ_STATUS_REG); + status = mmio_read_32(base_addr); + + if (status & (NOR_PS | NOR_BLS)) { + nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG); + ret = -EPERM; + } } - /* Full status check */ - nor_send_cmd(base_addr, NOR_CMD_READ_STATUS_REG); - status = mmio_read_32(base_addr); + if (ret == 0) + ret = nor_full_status_check(base_addr); + nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY); - if (status & (NOR_PS | NOR_BLS)) { - nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG); - ret = -EPERM; - } + return ret; +} -word_program_end: +/* + * Erase a full 256K block + * Return values: + * 0 = success + * otherwise it returns a negative value + */ +int nor_erase(uintptr_t base_addr) +{ + int ret; + + nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG); + + nor_send_cmd(base_addr, NOR_CMD_BLOCK_ERASE); + nor_send_cmd(base_addr, NOR_CMD_BLOCK_ERASE_ACK); + + ret = nor_poll_dws(base_addr, DWS_WORD_ERASE_RETRIES); + if (ret == 0) + ret = nor_full_status_check(base_addr); nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY); + return ret; } -void nor_lock(uintptr_t base_addr) +/* + * Lock a full 256 block + * Return values: + * 0 = success + * otherwise it returns a negative value + */ +int nor_lock(uintptr_t base_addr) { + int ret; + + nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG); + nor_send_cmd(base_addr, NOR_CMD_LOCK_UNLOCK); - mmio_write_32(base_addr, NOR_2X16(NOR_LOCK_BLOCK)); + nor_send_cmd(base_addr, NOR_LOCK_BLOCK); + + ret = nor_poll_dws(base_addr, DWS_WORD_LOCK_RETRIES); + if (ret == 0) + ret = nor_full_status_check(base_addr); nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY); + + return ret; } -void nor_unlock(uintptr_t base_addr) +/* + * unlock a full 256 block + * Return values: + * 0 = success + * otherwise it returns a negative value + */ +int nor_unlock(uintptr_t base_addr) { + int ret; + + nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG); + nor_send_cmd(base_addr, NOR_CMD_LOCK_UNLOCK); - mmio_write_32(base_addr, NOR_2X16(NOR_UNLOCK_BLOCK)); + nor_send_cmd(base_addr, NOR_UNLOCK_BLOCK); + + ret = nor_poll_dws(base_addr, DWS_WORD_LOCK_RETRIES); + if (ret == 0) + ret = nor_full_status_check(base_addr); nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY); -} + return ret; +} |