blob: 9b80e2f2a18d579d8c32bf33dab76ca485bc8e0a [file] [log] [blame]
Juan Castillofacdd1c2015-08-12 12:53:02 +01001/*
Roberto Vargasab29dca2017-07-26 14:15:07 +01002 * Copyright (c) 2015-2017, 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>
8#include <mmio.h>
Antonio Nino Diazd7da2f82018-10-10 11:14:44 +01009#include <v2m_flash.h>
Juan Castillofacdd1c2015-08-12 12:53:02 +010010
Antonio Nino Diazd7da2f82018-10-10 11:14:44 +010011/*
12 * This file supplies a low level interface to the vexpress NOR flash
13 * memory of juno and fvp. This memory is organized as an interleaved
14 * memory of two chips with a 16 bit word. It means that every 32 bit
15 * access is going to access to two different chips. This is very
16 * important when we send commands or read status of the chips.
17 */
Juan Castillofacdd1c2015-08-12 12:53:02 +010018
19/*
20 * DWS ready poll retries. The number of retries in this driver have been
21 * obtained empirically from Juno. FVP implements a zero wait state NOR flash
22 * model
23 */
24#define DWS_WORD_PROGRAM_RETRIES 1000
Roberto Vargasea21edc2017-07-28 10:38:24 +010025#define DWS_WORD_ERASE_RETRIES 3000000
Roberto Vargas86a610e2017-07-26 14:37:56 +010026#define DWS_WORD_LOCK_RETRIES 1000
Juan Castillofacdd1c2015-08-12 12:53:02 +010027
Roberto Vargasab29dca2017-07-26 14:15:07 +010028/* Helper macro to detect end of command */
29#define NOR_CMD_END (NOR_DWS | NOR_DWS << 16l)
30
Roberto Vargas6ca19112017-07-28 10:43:28 +010031/* Helper macros to access two flash banks in parallel */
32#define NOR_2X16(d) ((d << 16) | (d & 0xffff))
33
34static unsigned int nor_status(uintptr_t base_addr)
35{
36 unsigned long status;
37
38 nor_send_cmd(base_addr, NOR_CMD_READ_STATUS_REG);
39 status = mmio_read_32(base_addr);
40 status |= status >> 16; /* merge status from both flash banks */
41
42 return status & 0xFFFF;
43}
44
45/*
46 * Poll Write State Machine.
47 * Return values:
Juan Castillofacdd1c2015-08-12 12:53:02 +010048 * 0 = WSM ready
49 * -EBUSY = WSM busy after the number of retries
50 */
Roberto Vargasea21edc2017-07-28 10:38:24 +010051static int nor_poll_dws(uintptr_t base_addr, unsigned long int retries)
Juan Castillofacdd1c2015-08-12 12:53:02 +010052{
Roberto Vargasab29dca2017-07-26 14:15:07 +010053 unsigned long status;
Juan Castillofacdd1c2015-08-12 12:53:02 +010054
Roberto Vargasab29dca2017-07-26 14:15:07 +010055 do {
Juan Castillofacdd1c2015-08-12 12:53:02 +010056 nor_send_cmd(base_addr, NOR_CMD_READ_STATUS_REG);
57 status = mmio_read_32(base_addr);
Roberto Vargasab29dca2017-07-26 14:15:07 +010058 if ((status & NOR_CMD_END) == NOR_CMD_END)
59 return 0;
60 } while (retries-- > 0);
Juan Castillofacdd1c2015-08-12 12:53:02 +010061
Roberto Vargasab29dca2017-07-26 14:15:07 +010062 return -EBUSY;
Juan Castillofacdd1c2015-08-12 12:53:02 +010063}
64
Roberto Vargas6ca19112017-07-28 10:43:28 +010065/*
66 * Return values:
67 * 0 = success
68 * -EPERM = Device protected or Block locked
69 * -EIO = General I/O error
70 */
71static int nor_full_status_check(uintptr_t base_addr)
72{
73 unsigned long status;
74
75 /* Full status check */
76 status = nor_status(base_addr);
77
78 if (status & (NOR_PS | NOR_BLS | NOR_ESS | NOR_PSS))
79 return -EPERM;
80 if (status & (NOR_VPPS | NOR_ES))
81 return -EIO;
82 return 0;
83}
84
Juan Castillofacdd1c2015-08-12 12:53:02 +010085void nor_send_cmd(uintptr_t base_addr, unsigned long cmd)
86{
87 mmio_write_32(base_addr, NOR_2X16(cmd));
88}
89
90/*
Roberto Vargasab29dca2017-07-26 14:15:07 +010091 * This function programs a word in the flash. Be aware that it only
92 * can reset bits that were previously set. It cannot set bits that
93 * were previously reset. The resulting bits = old_bits & new bits.
Juan Castillofacdd1c2015-08-12 12:53:02 +010094 * Return values:
Roberto Vargas6ca19112017-07-28 10:43:28 +010095 * 0 = success
96 * otherwise it returns a negative value
Juan Castillofacdd1c2015-08-12 12:53:02 +010097 */
98int nor_word_program(uintptr_t base_addr, unsigned long data)
99{
100 uint32_t status;
101 int ret;
102
Roberto Vargas2e94e732017-07-26 15:17:24 +0100103 nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG);
104
Juan Castillofacdd1c2015-08-12 12:53:02 +0100105 /* Set the device in write word mode */
106 nor_send_cmd(base_addr, NOR_CMD_WORD_PROGRAM);
107 mmio_write_32(base_addr, data);
108
109 ret = nor_poll_dws(base_addr, DWS_WORD_PROGRAM_RETRIES);
Roberto Vargasab29dca2017-07-26 14:15:07 +0100110 if (ret == 0) {
111 /* Full status check */
112 nor_send_cmd(base_addr, NOR_CMD_READ_STATUS_REG);
113 status = mmio_read_32(base_addr);
Juan Castillofacdd1c2015-08-12 12:53:02 +0100114
Roberto Vargasab29dca2017-07-26 14:15:07 +0100115 if (status & (NOR_PS | NOR_BLS)) {
116 nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG);
117 ret = -EPERM;
118 }
Juan Castillofacdd1c2015-08-12 12:53:02 +0100119 }
120
Roberto Vargas6ca19112017-07-28 10:43:28 +0100121 if (ret == 0)
122 ret = nor_full_status_check(base_addr);
Juan Castillofacdd1c2015-08-12 12:53:02 +0100123 nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY);
Roberto Vargas6ca19112017-07-28 10:43:28 +0100124
Juan Castillofacdd1c2015-08-12 12:53:02 +0100125 return ret;
126}
127
Roberto Vargasab29dca2017-07-26 14:15:07 +0100128/*
Roberto Vargasea21edc2017-07-28 10:38:24 +0100129 * Erase a full 256K block
130 * Return values:
131 * 0 = success
Roberto Vargas6ca19112017-07-28 10:43:28 +0100132 * otherwise it returns a negative value
Roberto Vargasea21edc2017-07-28 10:38:24 +0100133 */
134int nor_erase(uintptr_t base_addr)
135{
136 int ret;
137
138 nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG);
139
140 nor_send_cmd(base_addr, NOR_CMD_BLOCK_ERASE);
141 nor_send_cmd(base_addr, NOR_CMD_BLOCK_ERASE_ACK);
142
143 ret = nor_poll_dws(base_addr, DWS_WORD_ERASE_RETRIES);
Roberto Vargas6ca19112017-07-28 10:43:28 +0100144 if (ret == 0)
145 ret = nor_full_status_check(base_addr);
Roberto Vargasea21edc2017-07-28 10:38:24 +0100146 nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY);
147
148 return ret;
149}
150
151/*
Roberto Vargasab29dca2017-07-26 14:15:07 +0100152 * Lock a full 256 block
Roberto Vargas86a610e2017-07-26 14:37:56 +0100153 * Return values:
154 * 0 = success
155 * otherwise it returns a negative value
Roberto Vargasab29dca2017-07-26 14:15:07 +0100156 */
Roberto Vargas86a610e2017-07-26 14:37:56 +0100157int nor_lock(uintptr_t base_addr)
Juan Castillofacdd1c2015-08-12 12:53:02 +0100158{
Roberto Vargas86a610e2017-07-26 14:37:56 +0100159 int ret;
160
Roberto Vargas2e94e732017-07-26 15:17:24 +0100161 nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG);
162
Juan Castillofacdd1c2015-08-12 12:53:02 +0100163 nor_send_cmd(base_addr, NOR_CMD_LOCK_UNLOCK);
Roberto Vargas2e94e732017-07-26 15:17:24 +0100164 nor_send_cmd(base_addr, NOR_LOCK_BLOCK);
Roberto Vargas86a610e2017-07-26 14:37:56 +0100165
166 ret = nor_poll_dws(base_addr, DWS_WORD_LOCK_RETRIES);
Roberto Vargas6ca19112017-07-28 10:43:28 +0100167 if (ret == 0)
168 ret = nor_full_status_check(base_addr);
Juan Castillofacdd1c2015-08-12 12:53:02 +0100169 nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY);
Roberto Vargas86a610e2017-07-26 14:37:56 +0100170
171 return ret;
Juan Castillofacdd1c2015-08-12 12:53:02 +0100172}
173
Roberto Vargasab29dca2017-07-26 14:15:07 +0100174/*
175 * unlock a full 256 block
Roberto Vargas86a610e2017-07-26 14:37:56 +0100176 * Return values:
177 * 0 = success
178 * otherwise it returns a negative value
Roberto Vargasab29dca2017-07-26 14:15:07 +0100179 */
Roberto Vargas86a610e2017-07-26 14:37:56 +0100180int nor_unlock(uintptr_t base_addr)
Juan Castillofacdd1c2015-08-12 12:53:02 +0100181{
Roberto Vargas86a610e2017-07-26 14:37:56 +0100182 int ret;
183
Roberto Vargas2e94e732017-07-26 15:17:24 +0100184 nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG);
185
Juan Castillofacdd1c2015-08-12 12:53:02 +0100186 nor_send_cmd(base_addr, NOR_CMD_LOCK_UNLOCK);
Roberto Vargas2e94e732017-07-26 15:17:24 +0100187 nor_send_cmd(base_addr, NOR_UNLOCK_BLOCK);
Roberto Vargas86a610e2017-07-26 14:37:56 +0100188
189 ret = nor_poll_dws(base_addr, DWS_WORD_LOCK_RETRIES);
Roberto Vargas6ca19112017-07-28 10:43:28 +0100190 if (ret == 0)
191 ret = nor_full_status_check(base_addr);
Juan Castillofacdd1c2015-08-12 12:53:02 +0100192 nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY);
Roberto Vargas86a610e2017-07-26 14:37:56 +0100193
194 return ret;
Juan Castillofacdd1c2015-08-12 12:53:02 +0100195}