blob: 66901891922b677e807de9cef11da22ba248b3b0 [file] [log] [blame]
Juan Castillofacdd1c2015-08-12 12:53:02 +01001/*
Zelalem91d80612020-02-12 10:37:03 -06002 * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved.
Juan Castillofacdd1c2015-08-12 12:53:02 +01003 *
dp-armfa3cf0b2017-05-03 09:38:09 +01004 * SPDX-License-Identifier: BSD-3-Clause
Juan Castillofacdd1c2015-08-12 12:53:02 +01005 */
6
7#include <errno.h>
Antonio Nino Diaze0f90632018-12-14 00:18:21 +00008
9#include <drivers/cfi/v2m_flash.h>
10#include <lib/mmio.h>
Juan Castillofacdd1c2015-08-12 12:53:02 +010011
Antonio Nino Diazd7da2f82018-10-10 11:14:44 +010012/*
13 * This file supplies a low level interface to the vexpress NOR flash
14 * memory of juno and fvp. This memory is organized as an interleaved
15 * memory of two chips with a 16 bit word. It means that every 32 bit
16 * access is going to access to two different chips. This is very
17 * important when we send commands or read status of the chips.
18 */
Juan Castillofacdd1c2015-08-12 12:53:02 +010019
20/*
21 * DWS ready poll retries. The number of retries in this driver have been
22 * obtained empirically from Juno. FVP implements a zero wait state NOR flash
23 * model
24 */
25#define DWS_WORD_PROGRAM_RETRIES 1000
Roberto Vargasea21edc2017-07-28 10:38:24 +010026#define DWS_WORD_ERASE_RETRIES 3000000
Roberto Vargas86a610e2017-07-26 14:37:56 +010027#define DWS_WORD_LOCK_RETRIES 1000
Juan Castillofacdd1c2015-08-12 12:53:02 +010028
Roberto Vargasab29dca2017-07-26 14:15:07 +010029/* Helper macro to detect end of command */
Zelalem91d80612020-02-12 10:37:03 -060030#define NOR_CMD_END (NOR_DWS | (NOR_DWS << 16l))
Roberto Vargasab29dca2017-07-26 14:15:07 +010031
Roberto Vargas6ca19112017-07-28 10:43:28 +010032/* Helper macros to access two flash banks in parallel */
33#define NOR_2X16(d) ((d << 16) | (d & 0xffff))
34
35static unsigned int nor_status(uintptr_t base_addr)
36{
37 unsigned long status;
38
39 nor_send_cmd(base_addr, NOR_CMD_READ_STATUS_REG);
40 status = mmio_read_32(base_addr);
41 status |= status >> 16; /* merge status from both flash banks */
42
43 return status & 0xFFFF;
44}
45
46/*
47 * Poll Write State Machine.
48 * Return values:
Juan Castillofacdd1c2015-08-12 12:53:02 +010049 * 0 = WSM ready
50 * -EBUSY = WSM busy after the number of retries
51 */
Roberto Vargasea21edc2017-07-28 10:38:24 +010052static int nor_poll_dws(uintptr_t base_addr, unsigned long int retries)
Juan Castillofacdd1c2015-08-12 12:53:02 +010053{
Roberto Vargasab29dca2017-07-26 14:15:07 +010054 unsigned long status;
Juan Castillofacdd1c2015-08-12 12:53:02 +010055
Roberto Vargasab29dca2017-07-26 14:15:07 +010056 do {
Juan Castillofacdd1c2015-08-12 12:53:02 +010057 nor_send_cmd(base_addr, NOR_CMD_READ_STATUS_REG);
58 status = mmio_read_32(base_addr);
Roberto Vargasab29dca2017-07-26 14:15:07 +010059 if ((status & NOR_CMD_END) == NOR_CMD_END)
60 return 0;
61 } while (retries-- > 0);
Juan Castillofacdd1c2015-08-12 12:53:02 +010062
Roberto Vargasab29dca2017-07-26 14:15:07 +010063 return -EBUSY;
Juan Castillofacdd1c2015-08-12 12:53:02 +010064}
65
Roberto Vargas6ca19112017-07-28 10:43:28 +010066/*
67 * Return values:
68 * 0 = success
69 * -EPERM = Device protected or Block locked
70 * -EIO = General I/O error
71 */
72static int nor_full_status_check(uintptr_t base_addr)
73{
74 unsigned long status;
75
76 /* Full status check */
77 status = nor_status(base_addr);
78
79 if (status & (NOR_PS | NOR_BLS | NOR_ESS | NOR_PSS))
80 return -EPERM;
81 if (status & (NOR_VPPS | NOR_ES))
82 return -EIO;
83 return 0;
84}
85
Juan Castillofacdd1c2015-08-12 12:53:02 +010086void nor_send_cmd(uintptr_t base_addr, unsigned long cmd)
87{
88 mmio_write_32(base_addr, NOR_2X16(cmd));
89}
90
91/*
Roberto Vargasab29dca2017-07-26 14:15:07 +010092 * This function programs a word in the flash. Be aware that it only
93 * can reset bits that were previously set. It cannot set bits that
94 * were previously reset. The resulting bits = old_bits & new bits.
Juan Castillofacdd1c2015-08-12 12:53:02 +010095 * Return values:
Roberto Vargas6ca19112017-07-28 10:43:28 +010096 * 0 = success
97 * otherwise it returns a negative value
Juan Castillofacdd1c2015-08-12 12:53:02 +010098 */
99int nor_word_program(uintptr_t base_addr, unsigned long data)
100{
101 uint32_t status;
102 int ret;
103
Roberto Vargas2e94e732017-07-26 15:17:24 +0100104 nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG);
105
Juan Castillofacdd1c2015-08-12 12:53:02 +0100106 /* Set the device in write word mode */
107 nor_send_cmd(base_addr, NOR_CMD_WORD_PROGRAM);
108 mmio_write_32(base_addr, data);
109
110 ret = nor_poll_dws(base_addr, DWS_WORD_PROGRAM_RETRIES);
Roberto Vargasab29dca2017-07-26 14:15:07 +0100111 if (ret == 0) {
112 /* Full status check */
113 nor_send_cmd(base_addr, NOR_CMD_READ_STATUS_REG);
114 status = mmio_read_32(base_addr);
Juan Castillofacdd1c2015-08-12 12:53:02 +0100115
Roberto Vargasab29dca2017-07-26 14:15:07 +0100116 if (status & (NOR_PS | NOR_BLS)) {
117 nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG);
118 ret = -EPERM;
119 }
Juan Castillofacdd1c2015-08-12 12:53:02 +0100120 }
121
Roberto Vargas6ca19112017-07-28 10:43:28 +0100122 if (ret == 0)
123 ret = nor_full_status_check(base_addr);
Juan Castillofacdd1c2015-08-12 12:53:02 +0100124 nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY);
Roberto Vargas6ca19112017-07-28 10:43:28 +0100125
Juan Castillofacdd1c2015-08-12 12:53:02 +0100126 return ret;
127}
128
Roberto Vargasab29dca2017-07-26 14:15:07 +0100129/*
Roberto Vargasea21edc2017-07-28 10:38:24 +0100130 * Erase a full 256K block
131 * Return values:
132 * 0 = success
Roberto Vargas6ca19112017-07-28 10:43:28 +0100133 * otherwise it returns a negative value
Roberto Vargasea21edc2017-07-28 10:38:24 +0100134 */
135int nor_erase(uintptr_t base_addr)
136{
137 int ret;
138
139 nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG);
140
141 nor_send_cmd(base_addr, NOR_CMD_BLOCK_ERASE);
142 nor_send_cmd(base_addr, NOR_CMD_BLOCK_ERASE_ACK);
143
144 ret = nor_poll_dws(base_addr, DWS_WORD_ERASE_RETRIES);
Roberto Vargas6ca19112017-07-28 10:43:28 +0100145 if (ret == 0)
146 ret = nor_full_status_check(base_addr);
Roberto Vargasea21edc2017-07-28 10:38:24 +0100147 nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY);
148
149 return ret;
150}
151
152/*
Roberto Vargasab29dca2017-07-26 14:15:07 +0100153 * Lock a full 256 block
Roberto Vargas86a610e2017-07-26 14:37:56 +0100154 * Return values:
155 * 0 = success
156 * otherwise it returns a negative value
Roberto Vargasab29dca2017-07-26 14:15:07 +0100157 */
Roberto Vargas86a610e2017-07-26 14:37:56 +0100158int nor_lock(uintptr_t base_addr)
Juan Castillofacdd1c2015-08-12 12:53:02 +0100159{
Roberto Vargas86a610e2017-07-26 14:37:56 +0100160 int ret;
161
Roberto Vargas2e94e732017-07-26 15:17:24 +0100162 nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG);
163
Juan Castillofacdd1c2015-08-12 12:53:02 +0100164 nor_send_cmd(base_addr, NOR_CMD_LOCK_UNLOCK);
Roberto Vargas2e94e732017-07-26 15:17:24 +0100165 nor_send_cmd(base_addr, NOR_LOCK_BLOCK);
Roberto Vargas86a610e2017-07-26 14:37:56 +0100166
167 ret = nor_poll_dws(base_addr, DWS_WORD_LOCK_RETRIES);
Roberto Vargas6ca19112017-07-28 10:43:28 +0100168 if (ret == 0)
169 ret = nor_full_status_check(base_addr);
Juan Castillofacdd1c2015-08-12 12:53:02 +0100170 nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY);
Roberto Vargas86a610e2017-07-26 14:37:56 +0100171
172 return ret;
Juan Castillofacdd1c2015-08-12 12:53:02 +0100173}
174
Roberto Vargasab29dca2017-07-26 14:15:07 +0100175/*
176 * unlock a full 256 block
Roberto Vargas86a610e2017-07-26 14:37:56 +0100177 * Return values:
178 * 0 = success
179 * otherwise it returns a negative value
Roberto Vargasab29dca2017-07-26 14:15:07 +0100180 */
Roberto Vargas86a610e2017-07-26 14:37:56 +0100181int nor_unlock(uintptr_t base_addr)
Juan Castillofacdd1c2015-08-12 12:53:02 +0100182{
Roberto Vargas86a610e2017-07-26 14:37:56 +0100183 int ret;
184
Roberto Vargas2e94e732017-07-26 15:17:24 +0100185 nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG);
186
Juan Castillofacdd1c2015-08-12 12:53:02 +0100187 nor_send_cmd(base_addr, NOR_CMD_LOCK_UNLOCK);
Roberto Vargas2e94e732017-07-26 15:17:24 +0100188 nor_send_cmd(base_addr, NOR_UNLOCK_BLOCK);
Roberto Vargas86a610e2017-07-26 14:37:56 +0100189
190 ret = nor_poll_dws(base_addr, DWS_WORD_LOCK_RETRIES);
Roberto Vargas6ca19112017-07-28 10:43:28 +0100191 if (ret == 0)
192 ret = nor_full_status_check(base_addr);
Juan Castillofacdd1c2015-08-12 12:53:02 +0100193 nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY);
Roberto Vargas86a610e2017-07-26 14:37:56 +0100194
195 return ret;
Juan Castillofacdd1c2015-08-12 12:53:02 +0100196}