add tercel speed-up but missing id for arty a7 at the moment
[ls2.git] / coldboot / tercel.c
1 // Copyright (C) 2020 - 2021 Raptor Engineering, LLC
2 //
3 // Released under the terms of the GPL v3
4 // See the LICENSE file for full details
5
6 #include <stdint.h>
7 #include <stdbool.h>
8
9 #include "console.h"
10 #include "microwatt_soc.h"
11 #include "io.h"
12
13 #include <stdlib.h>
14 #include <stdint.h>
15
16 #include "micron_n25q_flash.h"
17 #include "tercel_spi.h"
18
19 #define ALLOW_SPI_QUAD_MODE 1
20
21 static inline uint8_t read_tercel_reg8(uint8_t reg)
22 {
23 return readb((unsigned long)(SPI_FCTRL_BASE+reg));
24 }
25
26 static inline void write_tercel_reg8(uint8_t reg, uint8_t value)
27 {
28 writeb(value, (unsigned long)(SPI_FCTRL_BASE+reg));
29 }
30
31 static inline uint32_t read_tercel_register(uint8_t reg)
32 {
33 return readl((unsigned long)(SPI_FCTRL_BASE+reg));
34 }
35
36 static inline void write_tercel_register(uint8_t reg, uint32_t value)
37 {
38 writel(value, (unsigned long)(SPI_FCTRL_BASE+reg));
39 }
40
41 static uint32_t read_host_spi_flash_id(void)
42 {
43 uint32_t flash_id = 0;
44
45 // Set user mode
46 write_tercel_register(TERCEL_SPI_REG_SYS_CORE_CTL1,
47 read_tercel_register(TERCEL_SPI_REG_SYS_CORE_CTL1) |
48 (TERCEL_SPI_ENABLE_USER_MODE_MASK << TERCEL_SPI_ENABLE_USER_MODE_SHIFT));
49
50 // Send Flash ID command
51 write_tercel_reg8(0, 0x9e);
52
53 // Read response
54 flash_id = (flash_id << 8) | read_tercel_reg8(0);
55 flash_id = (flash_id << 8) | read_tercel_reg8(0);
56 flash_id = (flash_id << 8) | read_tercel_reg8(0);
57 flash_id = (flash_id << 8) | read_tercel_reg8(0);
58
59 // Clear user mode
60 write_tercel_register(TERCEL_SPI_REG_SYS_CORE_CTL1,
61 read_tercel_register(TERCEL_SPI_REG_SYS_CORE_CTL1) &
62 ~(TERCEL_SPI_ENABLE_USER_MODE_MASK <<
63 TERCEL_SPI_ENABLE_USER_MODE_SHIFT));
64
65 return flash_id;
66 }
67
68
69 /* this is a "level 1" speed-up, which gets an initial improvement of 10-50x
70 * over the default speed (which is a scant 100 bytes per second).
71 */
72 void crank_up_qspi_level1(void)
73 {
74 // WARNING: KESTREL SPECIFIC
75 // Set SPI clock cycle divider to 1
76 uint32_t dword;
77 dword = read_tercel_register(TERCEL_SPI_REG_SYS_PHY_CFG1);
78 dword &= ~(TERCEL_SPI_PHY_CLOCK_DIVISOR_MASK <<
79 TERCEL_SPI_PHY_CLOCK_DIVISOR_SHIFT);
80 dword |= ((1 & TERCEL_SPI_PHY_CLOCK_DIVISOR_MASK) <<
81 TERCEL_SPI_PHY_CLOCK_DIVISOR_SHIFT);
82 write_tercel_register(TERCEL_SPI_REG_SYS_PHY_CFG1, dword);
83 // Enable read merging
84 dword = read_tercel_register(TERCEL_SPI_REG_SYS_FLASH_CFG5);
85 dword |= (TERCEL_SPI_FLASH_EN_MULTCYC_READ_MASK <<
86 TERCEL_SPI_FLASH_EN_MULTCYC_READ_SHIFT);
87 write_tercel_register(TERCEL_SPI_REG_SYS_FLASH_CFG5, dword);
88 }
89
90 extern void uart_writeuint32(uint32_t val);
91
92 // level 2 speedup
93 int host_spi_flash_init(void)
94 {
95 int i;
96 uint32_t dword;
97 uint32_t flash_device_id;
98
99 if ((read_tercel_register(TERCEL_SPI_REG_DEVICE_ID_HIGH) !=
100 TERCEL_SPI_DEVICE_ID_HIGH) ||
101 (read_tercel_register(TERCEL_SPI_REG_DEVICE_ID_LOW) !=
102 TERCEL_SPI_DEVICE_ID_LOW))
103 {
104 return -1;
105 }
106
107 uint32_t tercel_version;
108 tercel_version = read_tercel_register(TERCEL_SPI_REG_DEVICE_VERSION);
109 puts("Raptor Tercel SPI master found, device version ");
110 uart_writeuint32((tercel_version >> TERCEL_SPI_VERSION_MAJOR_SHIFT) &
111 TERCEL_SPI_VERSION_MAJOR_MASK);
112 uart_writeuint32((tercel_version >> TERCEL_SPI_VERSION_MINOR_SHIFT) &
113 TERCEL_SPI_VERSION_MINOR_MASK);
114 uart_writeuint32((tercel_version >> TERCEL_SPI_VERSION_PATCH_SHIFT) &
115 TERCEL_SPI_VERSION_PATCH_MASK);
116 puts("\n");
117
118 flash_device_id = read_host_spi_flash_id();
119 puts("Flash ID ");
120 uart_writeuint32(flash_device_id);
121 puts("\n");
122 for (i = 0; i < (sizeof(micron_n25q_spi_device_ids) /
123 sizeof(micron_n25q_spi_device_ids[0])); i++)
124 {
125 if (flash_device_id == micron_n25q_spi_device_ids[i])
126 {
127 puts(micron_n25q_spi_device_names[i]);
128 puts(" Flash device detected, configuring\n");
129
130 // Set up Flash-specific commands
131 dword = 0;
132 dword |= (MICRON_N25Q_SPI_4BA_QSPI_READ_CMD
133 & TERCEL_SPI_4BA_QSPI_CMD_MASK) <<
134 TERCEL_SPI_4BA_QSPI_CMD_SHIFT;
135 dword |= (MICRON_N25Q_SPI_3BA_QSPI_READ_CMD
136 & TERCEL_SPI_3BA_QSPI_CMD_MASK) <<
137 TERCEL_SPI_3BA_QSPI_CMD_SHIFT;
138 dword |= (MICRON_N25Q_SPI_4BA_SPI_READ_CMD &
139 TERCEL_SPI_4BA_SPI_CMD_MASK) <<
140 TERCEL_SPI_4BA_SPI_CMD_SHIFT;
141 dword |= (MICRON_N25Q_SPI_3BA_SPI_READ_CMD &
142 TERCEL_SPI_3BA_SPI_CMD_MASK) <<
143 TERCEL_SPI_3BA_SPI_CMD_SHIFT;
144 write_tercel_register(TERCEL_SPI_REG_SYS_FLASH_CFG1, dword);
145
146 dword = 0;
147 dword |= (MICRON_N25Q_SPI_4BA_QSPI_FAST_READ_CMD
148 & TERCEL_SPI_4BA_QSPI_CMD_MASK) <<
149 TERCEL_SPI_4BA_QSPI_CMD_SHIFT;
150 dword |= (MICRON_N25Q_SPI_3BA_QSPI_FAST_READ_CMD
151 & TERCEL_SPI_3BA_QSPI_CMD_MASK) <<
152 TERCEL_SPI_3BA_QSPI_CMD_SHIFT;
153 dword |= (MICRON_N25Q_SPI_4BA_SPI_FAST_READ_CMD &
154 TERCEL_SPI_4BA_SPI_CMD_MASK) <<
155 TERCEL_SPI_4BA_SPI_CMD_SHIFT;
156 dword |= (MICRON_N25Q_SPI_3BA_SPI_FAST_READ_CMD &
157 TERCEL_SPI_3BA_SPI_CMD_MASK) <<
158 TERCEL_SPI_3BA_SPI_CMD_SHIFT;
159 write_tercel_register(TERCEL_SPI_REG_SYS_FLASH_CFG2, dword);
160
161 dword = 0;
162 dword |= (MICRON_N25Q_SPI_4BA_QSPI_PAGE_PROGRAM_CMD
163 & TERCEL_SPI_4BA_QSPI_CMD_MASK) <<
164 TERCEL_SPI_4BA_QSPI_CMD_SHIFT;
165 dword |= (MICRON_N25Q_SPI_3BA_QSPI_PAGE_PROGRAM_CMD
166 & TERCEL_SPI_3BA_QSPI_CMD_MASK) <<
167 TERCEL_SPI_3BA_QSPI_CMD_SHIFT;
168 dword |= (MICRON_N25Q_SPI_4BA_SPI_PAGE_PROGRAM_CMD &
169 TERCEL_SPI_4BA_SPI_CMD_MASK) <<
170 TERCEL_SPI_4BA_SPI_CMD_SHIFT;
171 dword |= (MICRON_N25Q_SPI_3BA_SPI_PAGE_PROGRAM_CMD &
172 TERCEL_SPI_3BA_SPI_CMD_MASK) <<
173 TERCEL_SPI_3BA_SPI_CMD_SHIFT;
174 write_tercel_register(TERCEL_SPI_REG_SYS_FLASH_CFG3, dword);
175
176 // Enable extended QSPI read/write operations
177 dword = read_tercel_register(TERCEL_SPI_REG_SYS_PHY_CFG1);
178 dword |= TERCEL_SPI_PHY_QSPI_EXT_READ_EN_MASK <<
179 TERCEL_SPI_PHY_QSPI_EXT_READ_EN_SHIFT;
180 dword |= TERCEL_SPI_PHY_QSPI_EXT_WRITE_EN_MASK <<
181 TERCEL_SPI_PHY_QSPI_EXT_WRITE_EN_SHIFT;
182 write_tercel_register(TERCEL_SPI_REG_SYS_PHY_CFG1, dword);
183
184 break;
185 }
186 }
187
188 // Set SPI core to automatic mode
189 write_tercel_register(TERCEL_SPI_REG_SYS_CORE_CTL1,
190 read_tercel_register(TERCEL_SPI_REG_SYS_CORE_CTL1) &
191 ~(TERCEL_SPI_ENABLE_USER_MODE_MASK <<
192 TERCEL_SPI_ENABLE_USER_MODE_SHIFT));
193
194 // Set extra CS delay cycle count to 0
195 dword = read_tercel_register(TERCEL_SPI_REG_SYS_PHY_CFG1);
196 dword &= ~(TERCEL_SPI_PHY_CS_EXTRA_IDLE_CYC_MASK <<
197 TERCEL_SPI_PHY_CS_EXTRA_IDLE_CYC_SHIFT);
198 dword |= ((0 & TERCEL_SPI_PHY_CS_EXTRA_IDLE_CYC_MASK) <<
199 TERCEL_SPI_PHY_CS_EXTRA_IDLE_CYC_SHIFT);
200 write_tercel_register(TERCEL_SPI_REG_SYS_PHY_CFG1, dword);
201
202 // Set maximum CS assert cycle count to 10000
203 dword = read_tercel_register(TERCEL_SPI_REG_SYS_FLASH_CFG4);
204 dword &= ~(TERCEL_SPI_FLASH_CS_EN_LIMIT_CYC_MASK <<
205 TERCEL_SPI_FLASH_CS_EN_LIMIT_CYC_SHIFT);
206 dword |= ((10000 & TERCEL_SPI_FLASH_CS_EN_LIMIT_CYC_MASK) <<
207 TERCEL_SPI_FLASH_CS_EN_LIMIT_CYC_SHIFT);
208 write_tercel_register(TERCEL_SPI_REG_SYS_FLASH_CFG4, dword);
209
210 // Set SPI fast read dummy cycles to
211 // MICRON_N25Q_SPI_FAST_READ_DUMMY_CLOCK_CYCLES
212 dword = read_tercel_register(TERCEL_SPI_REG_SYS_PHY_CFG1);
213 dword &= ~(TERCEL_SPI_PHY_DUMMY_CYCLES_MASK <<
214 TERCEL_SPI_PHY_DUMMY_CYCLES_SHIFT);
215 dword |= ((MICRON_N25Q_SPI_FAST_READ_DUMMY_CLOCK_CYCLES
216 & TERCEL_SPI_PHY_DUMMY_CYCLES_MASK) <<
217 TERCEL_SPI_PHY_DUMMY_CYCLES_SHIFT);
218 write_tercel_register(TERCEL_SPI_REG_SYS_PHY_CFG1, dword);
219
220 // Enable SPI fast read functionality
221 dword = read_tercel_register(TERCEL_SPI_REG_SYS_PHY_CFG1);
222 dword &= ~(TERCEL_SPI_PHY_FAST_READ_ENABLE_MASK <<
223 TERCEL_SPI_PHY_FAST_READ_ENABLE_SHIFT);
224 dword |= ((1 & TERCEL_SPI_PHY_FAST_READ_ENABLE_MASK) <<
225 TERCEL_SPI_PHY_FAST_READ_ENABLE_SHIFT);
226 write_tercel_register(TERCEL_SPI_REG_SYS_PHY_CFG1, dword);
227
228 // Set SPI controller to 4BA mode
229 dword = read_tercel_register(TERCEL_SPI_REG_SYS_PHY_CFG1);
230 dword &= ~(TERCEL_SPI_PHY_4BA_ENABLE_MASK <<
231 TERCEL_SPI_PHY_4BA_ENABLE_SHIFT);
232 dword |= ((TERCEL_SPI_PHY_4BA_MODE & TERCEL_SPI_PHY_4BA_ENABLE_MASK)
233 << TERCEL_SPI_PHY_4BA_ENABLE_SHIFT);
234 write_tercel_register(TERCEL_SPI_REG_SYS_PHY_CFG1, dword);
235
236 #if (ALLOW_SPI_QUAD_MODE)
237 // Set SPI controller to QSPI mode
238 dword = read_tercel_register(TERCEL_SPI_REG_SYS_PHY_CFG1);
239 dword &= ~(TERCEL_SPI_PHY_IO_TYPE_MASK << TERCEL_SPI_PHY_IO_TYPE_SHIFT);
240 dword |= ((TERCEL_SPI_PHY_IO_TYPE_QUAD & TERCEL_SPI_PHY_IO_TYPE_MASK)
241 << TERCEL_SPI_PHY_IO_TYPE_SHIFT);
242 write_tercel_register(TERCEL_SPI_REG_SYS_PHY_CFG1, dword);
243 #endif
244
245 // Set SPI clock cycle divider to 5
246 dword = read_tercel_register(TERCEL_SPI_REG_SYS_PHY_CFG1);
247 dword &= ~(TERCEL_SPI_PHY_CLOCK_DIVISOR_MASK <<
248 TERCEL_SPI_PHY_CLOCK_DIVISOR_SHIFT);
249 dword |= ((5 & TERCEL_SPI_PHY_CLOCK_DIVISOR_MASK) <<
250 TERCEL_SPI_PHY_CLOCK_DIVISOR_SHIFT);
251 write_tercel_register(TERCEL_SPI_REG_SYS_PHY_CFG1, dword);
252
253 // Calculate and dump configured SPI clock speed
254 uint8_t spi_divisor =
255 (read_tercel_register(TERCEL_SPI_REG_SYS_PHY_CFG1)
256 >> TERCEL_SPI_PHY_CLOCK_DIVISOR_SHIFT) &
257 TERCEL_SPI_PHY_CLOCK_DIVISOR_MASK;
258 spi_divisor = (spi_divisor + 1) * 2;
259 uint8_t spi_dummy_cycles =
260 (read_tercel_register(TERCEL_SPI_REG_SYS_PHY_CFG1)
261 >> TERCEL_SPI_PHY_DUMMY_CYCLES_SHIFT) &
262 TERCEL_SPI_PHY_DUMMY_CYCLES_MASK;
263 puts("Flash controller frequency ");
264 uart_writeuint32((read_tercel_register(TERCEL_SPI_REG_SYS_CLK_FREQ) /
265 spi_divisor) / 1000000);
266 puts("\nbus frequency ");
267 uart_writeuint32(read_tercel_register(TERCEL_SPI_REG_SYS_CLK_FREQ) /
268 1000000);
269 puts("\ndummy cycles");
270 uart_writeuint32(spi_dummy_cycles);
271 puts("\n");
272
273 // Enable read merging
274 write_tercel_register(TERCEL_SPI_REG_SYS_FLASH_CFG5,
275 read_tercel_register(TERCEL_SPI_REG_SYS_FLASH_CFG5) |
276 (TERCEL_SPI_FLASH_EN_MULTCYC_READ_MASK <<
277 TERCEL_SPI_FLASH_EN_MULTCYC_READ_SHIFT));
278
279 // Enable write merging
280 write_tercel_register(TERCEL_SPI_REG_SYS_FLASH_CFG5,
281 read_tercel_register(TERCEL_SPI_REG_SYS_FLASH_CFG5)
282 |
283 (TERCEL_SPI_FLASH_EN_MULTCYC_WRITE_MASK <<
284 TERCEL_SPI_FLASH_EN_MULTCYC_WRITE_SHIFT));
285
286 return 0;
287 }
288