blob: e0047c006c180c1f13120a3b6a9f234f66463193 [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>
9#include <norflash.h>
10
Roberto Vargasab29dca2017-07-26 14:15:07 +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 */
18
Juan Castillofacdd1c2015-08-12 12:53:02 +010019/* Helper macros to access two flash banks in parallel */
20#define NOR_2X16(d) ((d << 16) | (d & 0xffff))
21
22/*
23 * DWS ready poll retries. The number of retries in this driver have been
24 * obtained empirically from Juno. FVP implements a zero wait state NOR flash
25 * model
26 */
27#define DWS_WORD_PROGRAM_RETRIES 1000
Roberto Vargasea21edc2017-07-28 10:38:24 +010028#define DWS_WORD_ERASE_RETRIES 3000000
Roberto Vargas86a610e2017-07-26 14:37:56 +010029#define DWS_WORD_LOCK_RETRIES 1000
Juan Castillofacdd1c2015-08-12 12:53:02 +010030
Roberto Vargasab29dca2017-07-26 14:15:07 +010031/* Helper macro to detect end of command */
32#define NOR_CMD_END (NOR_DWS | NOR_DWS << 16l)
33
Juan Castillofacdd1c2015-08-12 12:53:02 +010034/*
35 * Poll Write State Machine. Return values:
36 * 0 = WSM ready
37 * -EBUSY = WSM busy after the number of retries
38 */
Roberto Vargasea21edc2017-07-28 10:38:24 +010039static int nor_poll_dws(uintptr_t base_addr, unsigned long int retries)
Juan Castillofacdd1c2015-08-12 12:53:02 +010040{
Roberto Vargasab29dca2017-07-26 14:15:07 +010041 unsigned long status;
Juan Castillofacdd1c2015-08-12 12:53:02 +010042
Roberto Vargasab29dca2017-07-26 14:15:07 +010043 do {
Juan Castillofacdd1c2015-08-12 12:53:02 +010044 nor_send_cmd(base_addr, NOR_CMD_READ_STATUS_REG);
45 status = mmio_read_32(base_addr);
Roberto Vargasab29dca2017-07-26 14:15:07 +010046 if ((status & NOR_CMD_END) == NOR_CMD_END)
47 return 0;
48 } while (retries-- > 0);
Juan Castillofacdd1c2015-08-12 12:53:02 +010049
Roberto Vargasab29dca2017-07-26 14:15:07 +010050 return -EBUSY;
Juan Castillofacdd1c2015-08-12 12:53:02 +010051}
52
53void nor_send_cmd(uintptr_t base_addr, unsigned long cmd)
54{
55 mmio_write_32(base_addr, NOR_2X16(cmd));
56}
57
58/*
Roberto Vargasab29dca2017-07-26 14:15:07 +010059 * This function programs a word in the flash. Be aware that it only
60 * can reset bits that were previously set. It cannot set bits that
61 * were previously reset. The resulting bits = old_bits & new bits.
Juan Castillofacdd1c2015-08-12 12:53:02 +010062 * Return values:
63 * 0 = success
64 * -EBUSY = WSM not ready
65 * -EPERM = Device protected or Block locked
66 */
67int nor_word_program(uintptr_t base_addr, unsigned long data)
68{
69 uint32_t status;
70 int ret;
71
Roberto Vargas2e94e732017-07-26 15:17:24 +010072 nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG);
73
Juan Castillofacdd1c2015-08-12 12:53:02 +010074 /* Set the device in write word mode */
75 nor_send_cmd(base_addr, NOR_CMD_WORD_PROGRAM);
76 mmio_write_32(base_addr, data);
77
78 ret = nor_poll_dws(base_addr, DWS_WORD_PROGRAM_RETRIES);
Roberto Vargasab29dca2017-07-26 14:15:07 +010079 if (ret == 0) {
80 /* Full status check */
81 nor_send_cmd(base_addr, NOR_CMD_READ_STATUS_REG);
82 status = mmio_read_32(base_addr);
Juan Castillofacdd1c2015-08-12 12:53:02 +010083
Roberto Vargasab29dca2017-07-26 14:15:07 +010084 if (status & (NOR_PS | NOR_BLS)) {
85 nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG);
86 ret = -EPERM;
87 }
Juan Castillofacdd1c2015-08-12 12:53:02 +010088 }
89
Juan Castillofacdd1c2015-08-12 12:53:02 +010090 nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY);
91 return ret;
92}
93
Roberto Vargasab29dca2017-07-26 14:15:07 +010094/*
Roberto Vargasea21edc2017-07-28 10:38:24 +010095 * Erase a full 256K block
96 * Return values:
97 * 0 = success
98 * -EBUSY = WSM not ready
99 */
100int nor_erase(uintptr_t base_addr)
101{
102 int ret;
103
104 nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG);
105
106 nor_send_cmd(base_addr, NOR_CMD_BLOCK_ERASE);
107 nor_send_cmd(base_addr, NOR_CMD_BLOCK_ERASE_ACK);
108
109 ret = nor_poll_dws(base_addr, DWS_WORD_ERASE_RETRIES);
110 nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY);
111
112 return ret;
113}
114
115/*
Roberto Vargasab29dca2017-07-26 14:15:07 +0100116 * Lock a full 256 block
Roberto Vargas86a610e2017-07-26 14:37:56 +0100117 * Return values:
118 * 0 = success
119 * otherwise it returns a negative value
Roberto Vargasab29dca2017-07-26 14:15:07 +0100120 */
Roberto Vargas86a610e2017-07-26 14:37:56 +0100121int nor_lock(uintptr_t base_addr)
Juan Castillofacdd1c2015-08-12 12:53:02 +0100122{
Roberto Vargas86a610e2017-07-26 14:37:56 +0100123 int ret;
124
Roberto Vargas2e94e732017-07-26 15:17:24 +0100125 nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG);
126
Juan Castillofacdd1c2015-08-12 12:53:02 +0100127 nor_send_cmd(base_addr, NOR_CMD_LOCK_UNLOCK);
Roberto Vargas2e94e732017-07-26 15:17:24 +0100128 nor_send_cmd(base_addr, NOR_LOCK_BLOCK);
Roberto Vargas86a610e2017-07-26 14:37:56 +0100129
130 ret = nor_poll_dws(base_addr, DWS_WORD_LOCK_RETRIES);
Juan Castillofacdd1c2015-08-12 12:53:02 +0100131 nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY);
Roberto Vargas86a610e2017-07-26 14:37:56 +0100132
133 return ret;
Juan Castillofacdd1c2015-08-12 12:53:02 +0100134}
135
Roberto Vargasab29dca2017-07-26 14:15:07 +0100136/*
137 * unlock a full 256 block
Roberto Vargas86a610e2017-07-26 14:37:56 +0100138 * Return values:
139 * 0 = success
140 * otherwise it returns a negative value
Roberto Vargasab29dca2017-07-26 14:15:07 +0100141 */
Roberto Vargas86a610e2017-07-26 14:37:56 +0100142int nor_unlock(uintptr_t base_addr)
Juan Castillofacdd1c2015-08-12 12:53:02 +0100143{
Roberto Vargas86a610e2017-07-26 14:37:56 +0100144 int ret;
145
Roberto Vargas2e94e732017-07-26 15:17:24 +0100146 nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG);
147
Juan Castillofacdd1c2015-08-12 12:53:02 +0100148 nor_send_cmd(base_addr, NOR_CMD_LOCK_UNLOCK);
Roberto Vargas2e94e732017-07-26 15:17:24 +0100149 nor_send_cmd(base_addr, NOR_UNLOCK_BLOCK);
Roberto Vargas86a610e2017-07-26 14:37:56 +0100150
151 ret = nor_poll_dws(base_addr, DWS_WORD_LOCK_RETRIES);
Juan Castillofacdd1c2015-08-12 12:53:02 +0100152 nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY);
Roberto Vargas86a610e2017-07-26 14:37:56 +0100153
154 return ret;
Juan Castillofacdd1c2015-08-12 12:53:02 +0100155}