From f4953e761c9287607960e34c65f5054bc14d60c0 Mon Sep 17 00:00:00 2001 From: Roberto Vargas Date: Wed, 26 Jul 2017 14:15:07 +0100 Subject: [PATCH 1/5] norflash: clean-up norflash.c - Add comments to all the functions - Simplify nor_poll_dws - Simplify nor_word_program Change-Id: I29c0199d2908a4fceb1ac3430fcfdd699be22bb3 Signed-off-by: Roberto Vargas --- .../board/common/drivers/norflash/norflash.c | 62 +++++++++++-------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/plat/arm/board/common/drivers/norflash/norflash.c b/plat/arm/board/common/drivers/norflash/norflash.c index cc63d758e..053696eed 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,6 +8,14 @@ #include #include +/* + * 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)) @@ -18,6 +26,9 @@ */ #define DWS_WORD_PROGRAM_RETRIES 1000 +/* Helper macro to detect end of command */ +#define NOR_CMD_END (NOR_DWS | NOR_DWS << 16l) + /* * Poll Write State Machine. Return values: * 0 = WSM ready @@ -25,24 +36,16 @@ */ static int nor_poll_dws(uintptr_t base_addr, unsigned 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; } void nor_send_cmd(uintptr_t base_addr, unsigned long cmd) @@ -51,6 +54,9 @@ 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 @@ -66,24 +72,24 @@ int nor_word_program(uintptr_t base_addr, unsigned long data) mmio_write_32(base_addr, data); ret = nor_poll_dws(base_addr, DWS_WORD_PROGRAM_RETRIES); - if (ret != 0) { - goto word_program_end; - } - - /* Full status check */ - nor_send_cmd(base_addr, NOR_CMD_READ_STATUS_REG); - status = mmio_read_32(base_addr); + 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; + if (status & (NOR_PS | NOR_BLS)) { + nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG); + ret = -EPERM; + } } -word_program_end: nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY); return ret; } +/* + * Lock a full 256 block + */ void nor_lock(uintptr_t base_addr) { nor_send_cmd(base_addr, NOR_CMD_LOCK_UNLOCK); @@ -91,10 +97,12 @@ void nor_lock(uintptr_t base_addr) nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY); } +/* + * unlock a full 256 block + */ void nor_unlock(uintptr_t base_addr) { 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_CMD_READ_ARRAY); } - From 9753cb5b51794dac43fc9c4eb474207ebf369b59 Mon Sep 17 00:00:00 2001 From: Roberto Vargas Date: Wed, 26 Jul 2017 14:37:56 +0100 Subject: [PATCH 2/5] norflash: Wait for WSM bit in lock/unlock lock/unlock operation must wait until WSM bit is set. Since we do not allow to loop forever then these functions must return an error if WSM bit isn't enabled after a number of tries. Change-Id: I21c9e292b514b28786ff4a225128bcd8c1bfa999 Signed-off-by: Roberto Vargas --- .../plat/arm/board/common/drivers/norflash.h | 6 ++--- .../board/common/drivers/norflash/norflash.c | 23 +++++++++++++++++-- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/include/plat/arm/board/common/drivers/norflash.h b/include/plat/arm/board/common/drivers/norflash.h index e74635ecc..4b66e4251 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 */ @@ -37,8 +37,8 @@ /* 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); #endif /* __NORFLASH_H_ */ diff --git a/plat/arm/board/common/drivers/norflash/norflash.c b/plat/arm/board/common/drivers/norflash/norflash.c index 053696eed..18dad7b65 100644 --- a/plat/arm/board/common/drivers/norflash/norflash.c +++ b/plat/arm/board/common/drivers/norflash/norflash.c @@ -25,6 +25,7 @@ * model */ #define DWS_WORD_PROGRAM_RETRIES 1000 +#define DWS_WORD_LOCK_RETRIES 1000 /* Helper macro to detect end of command */ #define NOR_CMD_END (NOR_DWS | NOR_DWS << 16l) @@ -89,20 +90,38 @@ int nor_word_program(uintptr_t base_addr, unsigned long data) /* * Lock a full 256 block + * Return values: + * 0 = success + * otherwise it returns a negative value */ -void nor_lock(uintptr_t base_addr) +int nor_lock(uintptr_t base_addr) { + int ret; + nor_send_cmd(base_addr, NOR_CMD_LOCK_UNLOCK); mmio_write_32(base_addr, NOR_2X16(NOR_LOCK_BLOCK)); + + ret = nor_poll_dws(base_addr, DWS_WORD_LOCK_RETRIES); nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY); + + return ret; } /* * unlock a full 256 block + * Return values: + * 0 = success + * otherwise it returns a negative value */ -void nor_unlock(uintptr_t base_addr) +int nor_unlock(uintptr_t base_addr) { + int ret; + nor_send_cmd(base_addr, NOR_CMD_LOCK_UNLOCK); mmio_write_32(base_addr, NOR_2X16(NOR_UNLOCK_BLOCK)); + + ret = nor_poll_dws(base_addr, DWS_WORD_LOCK_RETRIES); nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY); + + return ret; } From b4b09ca5676fed17ead90f3b0ff3b08b9176f85f Mon Sep 17 00:00:00 2001 From: Roberto Vargas Date: Wed, 26 Jul 2017 15:17:24 +0100 Subject: [PATCH 3/5] norflash: Clear status register before sending commands The status register bits remain until explicitly cleared, which means that a command can be incorrectly considered to have generated an error - for example, after reset the status register contents may be unknown or if a previous command had failed. This patch clears the status register before beginning any command to be sure that the status register only represents information about the current operation. Change-Id: I9e98110ee24179937215461c00b6543a3467b350 Signed-off-by: Roberto Vargas --- plat/arm/board/common/drivers/norflash/norflash.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/plat/arm/board/common/drivers/norflash/norflash.c b/plat/arm/board/common/drivers/norflash/norflash.c index 18dad7b65..ee9eca7e1 100644 --- a/plat/arm/board/common/drivers/norflash/norflash.c +++ b/plat/arm/board/common/drivers/norflash/norflash.c @@ -68,6 +68,8 @@ 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); @@ -98,8 +100,10 @@ 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); nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY); @@ -117,8 +121,10 @@ 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); nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY); From 784502aeaed11aad205eb80ed05b1f78fc643d93 Mon Sep 17 00:00:00 2001 From: Roberto Vargas Date: Fri, 28 Jul 2017 10:38:24 +0100 Subject: [PATCH 4/5] norflash: Add nor_erase() to NOR driver NOR memory only supports setting bits to 1. To clear a bit, set to zero, the NOR memory needs to be erased. Change-Id: Ia82eb15a5af9a6d4fc7e5ea2b58e6db87226b351 Signed-off-by: Roberto Vargas --- .../plat/arm/board/common/drivers/norflash.h | 2 ++ .../board/common/drivers/norflash/norflash.c | 24 ++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/include/plat/arm/board/common/drivers/norflash.h b/include/plat/arm/board/common/drivers/norflash.h index 4b66e4251..5763b36df 100644 --- a/include/plat/arm/board/common/drivers/norflash.h +++ b/include/plat/arm/board/common/drivers/norflash.h @@ -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 @@ -39,6 +40,7 @@ void nor_send_cmd(uintptr_t base_addr, unsigned long cmd); int nor_word_program(uintptr_t base_addr, unsigned long data); 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 ee9eca7e1..e0047c006 100644 --- a/plat/arm/board/common/drivers/norflash/norflash.c +++ b/plat/arm/board/common/drivers/norflash/norflash.c @@ -25,6 +25,7 @@ * 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 */ @@ -35,7 +36,7 @@ * 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) { unsigned long status; @@ -90,6 +91,27 @@ int nor_word_program(uintptr_t base_addr, unsigned long data) return ret; } +/* + * Erase a full 256K block + * Return values: + * 0 = success + * -EBUSY = WSM not ready + */ +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); + nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY); + + return ret; +} + /* * Lock a full 256 block * Return values: From 3bbe34e5efb927494e18b75d3c20e8e914d2a1ca Mon Sep 17 00:00:00 2001 From: Roberto Vargas Date: Fri, 28 Jul 2017 10:43:28 +0100 Subject: [PATCH 5/5] norflash: Add full status check The nor_XXXXX functions may fail due to different reasons, and it is convenient to do a full check to detect any failure. It is also a good idea to have a specific function to do a full status check, because new checks can be added to this function and they will be incorporated automatically to any function calling it. Change-Id: I54fed913e37ef574c1608e94139a519426348d12 Signed-off-by: Roberto Vargas --- .../board/common/drivers/norflash/norflash.c | 71 +++++++++++++++---- 1 file changed, 56 insertions(+), 15 deletions(-) diff --git a/plat/arm/board/common/drivers/norflash/norflash.c b/plat/arm/board/common/drivers/norflash/norflash.c index e0047c006..722cf33ee 100644 --- a/plat/arm/board/common/drivers/norflash/norflash.c +++ b/plat/arm/board/common/drivers/norflash/norflash.c @@ -8,16 +8,6 @@ #include #include -/* - * 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)) /* * DWS ready poll retries. The number of retries in this driver have been @@ -32,7 +22,30 @@ #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 */ @@ -50,6 +63,26 @@ static int nor_poll_dws(uintptr_t base_addr, unsigned long int retries) 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) { mmio_write_32(base_addr, NOR_2X16(cmd)); @@ -60,9 +93,8 @@ void nor_send_cmd(uintptr_t base_addr, unsigned long cmd) * 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) { @@ -87,7 +119,10 @@ int nor_word_program(uintptr_t base_addr, unsigned long data) } } + if (ret == 0) + ret = nor_full_status_check(base_addr); nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY); + return ret; } @@ -95,7 +130,7 @@ int nor_word_program(uintptr_t base_addr, unsigned long data) * Erase a full 256K block * Return values: * 0 = success - * -EBUSY = WSM not ready + * otherwise it returns a negative value */ int nor_erase(uintptr_t base_addr) { @@ -107,6 +142,8 @@ int nor_erase(uintptr_t base_addr) 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; @@ -128,6 +165,8 @@ int nor_lock(uintptr_t base_addr) 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; @@ -149,6 +188,8 @@ int nor_unlock(uintptr_t base_addr) 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;