1 // Copyright (C) 2020 - 2021 Raptor Engineering, LLC
3 // Released under the terms of the GPL v3
4 // See the LICENSE file for full details
10 #include "microwatt_soc.h"
16 #include "micron_n25q_flash.h"
17 #include "tercel_spi.h"
19 #define ALLOW_SPI_QUAD_MODE 1
21 static inline uint8_t read_tercel_reg8(uint8_t reg
)
23 return readb((unsigned long)(SPI_FCTRL_BASE
+reg
));
26 static inline void write_tercel_reg8(uint8_t reg
, uint8_t value
)
28 writeb(value
, (unsigned long)(SPI_FCTRL_BASE
+reg
));
31 static inline uint32_t read_tercel_register(uint8_t reg
)
33 return readl((unsigned long)(SPI_FCTRL_BASE
+reg
));
36 static inline void write_tercel_register(uint8_t reg
, uint32_t value
)
38 writel(value
, (unsigned long)(SPI_FCTRL_BASE
+reg
));
41 static uint32_t read_host_spi_flash_id(void)
43 uint32_t flash_id
= 0;
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
));
50 // Send Flash ID command
51 write_tercel_reg8(0, 0x9e);
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);
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
));
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).
72 void crank_up_qspi_level1(void)
74 // WARNING: KESTREL SPECIFIC
75 // Set SPI clock cycle divider to 1
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
);
90 extern void uart_writeuint32(uint32_t val
);
93 int host_spi_flash_init(void)
97 uint32_t flash_device_id
;
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
))
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
);
118 flash_device_id
= read_host_spi_flash_id();
120 uart_writeuint32(flash_device_id
);
122 for (i
= 0; i
< (sizeof(micron_n25q_spi_device_ids
) /
123 sizeof(micron_n25q_spi_device_ids
[0])); i
++)
125 if (flash_device_id
== micron_n25q_spi_device_ids
[i
])
127 puts(micron_n25q_spi_device_names
[i
]);
128 puts(" Flash device detected, configuring\n");
130 // Set up Flash-specific commands
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
);
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
);
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
);
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
);
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
));
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
) /
269 puts("\ndummy cycles");
270 uart_writeuint32(spi_dummy_cycles
);
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
));
279 // Enable write merging
280 write_tercel_register(TERCEL_SPI_REG_SYS_FLASH_CFG5
,
281 read_tercel_register(TERCEL_SPI_REG_SYS_FLASH_CFG5
)
283 (TERCEL_SPI_FLASH_EN_MULTCYC_WRITE_MASK
<<
284 TERCEL_SPI_FLASH_EN_MULTCYC_WRITE_SHIFT
));