Skip to content

Commit 56a22dd

Browse files
committed
stm32/octospi: Add preliminary support for OCTOSPI peripheral.
It currently operates in 1-line (SPI) mode only. Signed-off-by: Damien George <damien@micropython.org>
1 parent ec4232b commit 56a22dd

File tree

3 files changed

+377
-0
lines changed

3 files changed

+377
-0
lines changed

ports/stm32/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ SRC_C += \
320320
spi.c \
321321
pyb_spi.c \
322322
qspi.c \
323+
octospi.c \
323324
uart.c \
324325
ulpi.c \
325326
can.c \

ports/stm32/octospi.c

Lines changed: 343 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,343 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2023 Damien P. George
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
// This OCTOSPI driver is currently configured to run in 1-line (SPI) mode.
28+
// It uses the mp_qspi_proto_t QSPI protocol and translates quad-commands
29+
// into 1-line commands.
30+
31+
#include "py/mperrno.h"
32+
#include "py/mphal.h"
33+
#include "octospi.h"
34+
#include "pin_static_af.h"
35+
36+
#if defined(MICROPY_HW_OSPIFLASH_SIZE_BITS_LOG2)
37+
38+
#ifndef MICROPY_HW_OSPI_PRESCALER
39+
#define MICROPY_HW_OSPI_PRESCALER (3) // F_CLK = F_AHB/3
40+
#endif
41+
42+
#ifndef MICROPY_HW_OSPI_CS_HIGH_CYCLES
43+
#define MICROPY_HW_OSPI_CS_HIGH_CYCLES (2) // nCS stays high for 2 cycles
44+
#endif
45+
46+
void octospi_init(void) {
47+
// Configure OCTOSPI pins (allows 1, 2, 4 or 8 line configuration).
48+
mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_CS, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI1_NCS);
49+
mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_SCK, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI1_CLK);
50+
mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_IO0, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI1_IO0);
51+
#if defined(MICROPY_HW_OSPIFLASH_IO1)
52+
mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_IO1, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI1_IO1);
53+
#if defined(MICROPY_HW_OSPIFLASH_IO2)
54+
mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_IO2, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI1_IO2);
55+
mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_IO3, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI1_IO3);
56+
#if defined(MICROPY_HW_OSPIFLASH_IO4)
57+
mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_IO4, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI1_IO4);
58+
mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_IO5, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI1_IO5);
59+
mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_IO6, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI1_IO6);
60+
mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_IO7, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI1_IO7);
61+
#endif
62+
#endif
63+
#endif
64+
65+
// Reset and turn on the OCTOSPI peripheral.
66+
__HAL_RCC_OSPI1_CLK_ENABLE();
67+
__HAL_RCC_OSPI1_FORCE_RESET();
68+
__HAL_RCC_OSPI1_RELEASE_RESET();
69+
70+
// Configure the OCTOSPI peripheral.
71+
72+
OCTOSPI1->CR =
73+
3 << OCTOSPI_CR_FTHRES_Pos // 4 bytes must be available to read/write
74+
| 0 << OCTOSPI_CR_MSEL_Pos // FLASH 0 selected
75+
| 0 << OCTOSPI_CR_DMM_Pos // dual-memory mode disabled
76+
;
77+
78+
OCTOSPI1->DCR1 =
79+
(MICROPY_HW_OSPIFLASH_SIZE_BITS_LOG2 - 3 - 1) << OCTOSPI_DCR1_DEVSIZE_Pos
80+
| (MICROPY_HW_OSPI_CS_HIGH_CYCLES - 1) << OCTOSPI_DCR1_CSHT_Pos
81+
| 0 << OCTOSPI_DCR1_CKMODE_Pos // CLK idles at low state
82+
;
83+
84+
OCTOSPI1->DCR2 =
85+
(MICROPY_HW_OSPI_PRESCALER - 1) << OCTOSPI_DCR2_PRESCALER_Pos
86+
;
87+
88+
OCTOSPI1->DCR3 = 0;
89+
OCTOSPI1->DCR4 = 0;
90+
91+
// Enable the OCTOSPI peripheral.
92+
OCTOSPI1->CR |= OCTOSPI_CR_EN;
93+
}
94+
95+
STATIC int octospi_ioctl(void *self_in, uint32_t cmd) {
96+
(void)self_in;
97+
switch (cmd) {
98+
case MP_QSPI_IOCTL_INIT:
99+
octospi_init();
100+
break;
101+
case MP_QSPI_IOCTL_BUS_ACQUIRE:
102+
// Abort any ongoing transfer if peripheral is busy.
103+
if (OCTOSPI1->SR & OCTOSPI_SR_BUSY) {
104+
OCTOSPI1->CR |= OCTOSPI_CR_ABORT;
105+
while (OCTOSPI1->CR & OCTOSPI_CR_ABORT) {
106+
}
107+
}
108+
break;
109+
case MP_QSPI_IOCTL_BUS_RELEASE:
110+
break;
111+
}
112+
return 0; // success
113+
}
114+
115+
STATIC int octospi_write_cmd_data(void *self_in, uint8_t cmd, size_t len, uint32_t data) {
116+
(void)self_in;
117+
118+
OCTOSPI1->FCR = OCTOSPI_FCR_CTCF; // clear TC flag
119+
120+
OCTOSPI1->CR = (OCTOSPI1->CR & ~OCTOSPI_CR_FMODE_Msk) | 0 << OCTOSPI_CR_FMODE_Pos; // indirect write mode
121+
122+
if (len == 0) {
123+
OCTOSPI1->CCR =
124+
0 << OCTOSPI_CCR_DDTR_Pos // DD mode disabled
125+
| 0 << OCTOSPI_CCR_SIOO_Pos // send instruction every transaction
126+
| 0 << OCTOSPI_CCR_DMODE_Pos // no data
127+
| 0 << OCTOSPI_CCR_ABMODE_Pos // no alternate byte
128+
| 0 << OCTOSPI_CCR_ADMODE_Pos // no address
129+
| 1 << OCTOSPI_CCR_IMODE_Pos // instruction on 1 line
130+
;
131+
OCTOSPI1->TCR = 0 << OCTOSPI_TCR_DCYC_Pos; // 0 dummy cycles
132+
133+
// This triggers the start of the operation.
134+
OCTOSPI1->IR = cmd << OCTOSPI_IR_INSTRUCTION_Pos; // write opcode
135+
} else {
136+
OCTOSPI1->DLR = len - 1;
137+
OCTOSPI1->CCR =
138+
0 << OCTOSPI_CCR_DDTR_Pos // DD mode disabled
139+
| 0 << OCTOSPI_CCR_SIOO_Pos // send instruction every transaction
140+
| 1 << OCTOSPI_CCR_DMODE_Pos // data on 1 line
141+
| 0 << OCTOSPI_CCR_ABMODE_Pos // no alternate byte
142+
| 0 << OCTOSPI_CCR_ADMODE_Pos // no address
143+
| 1 << OCTOSPI_CCR_IMODE_Pos // instruction on 1 line
144+
;
145+
OCTOSPI1->TCR = 0 << OCTOSPI_TCR_DCYC_Pos; // 0 dummy cycles
146+
OCTOSPI1->IR = cmd << OCTOSPI_IR_INSTRUCTION_Pos; // write opcode
147+
148+
// Wait for at least 1 free byte location in the FIFO.
149+
while (!(OCTOSPI1->SR & OCTOSPI_SR_FTF)) {
150+
}
151+
152+
// This triggers the start of the operation.
153+
// This assumes len==2
154+
*(uint16_t *)&OCTOSPI1->DR = data;
155+
}
156+
157+
// Wait for write to finish
158+
while (!(OCTOSPI1->SR & OCTOSPI_SR_TCF)) {
159+
if (OCTOSPI1->SR & OCTOSPI_SR_TEF) {
160+
return -MP_EIO;
161+
}
162+
}
163+
164+
OCTOSPI1->FCR = OCTOSPI_FCR_CTCF; // clear TC flag
165+
166+
return 0;
167+
}
168+
169+
STATIC int octospi_write_cmd_addr_data(void *self_in, uint8_t cmd, uint32_t addr, size_t len, const uint8_t *src) {
170+
(void)self_in;
171+
172+
uint8_t adsize = MICROPY_HW_SPI_ADDR_IS_32BIT(addr) ? 3 : 2;
173+
174+
OCTOSPI1->FCR = OCTOSPI_FCR_CTCF; // clear TC flag
175+
176+
OCTOSPI1->CR = (OCTOSPI1->CR & ~OCTOSPI_CR_FMODE_Msk) | 0 << OCTOSPI_CR_FMODE_Pos; // indirect write mode
177+
178+
if (len == 0) {
179+
OCTOSPI1->CCR =
180+
0 << OCTOSPI_CCR_DDTR_Pos // DD mode disabled
181+
| 0 << OCTOSPI_CCR_SIOO_Pos // send instruction every transaction
182+
| 0 << OCTOSPI_CCR_DMODE_Pos // no data
183+
| 0 << OCTOSPI_CCR_ABMODE_Pos // no alternate byte
184+
| adsize << OCTOSPI_CCR_ADSIZE_Pos // 32/24-bit address size
185+
| 1 << OCTOSPI_CCR_ADMODE_Pos // address on 1 line
186+
| 1 << OCTOSPI_CCR_IMODE_Pos // instruction on 1 line
187+
;
188+
OCTOSPI1->TCR = 0 << OCTOSPI_TCR_DCYC_Pos; // 0 dummy cycles
189+
OCTOSPI1->IR = cmd << OCTOSPI_IR_INSTRUCTION_Pos; // write opcode
190+
191+
// This triggers the start of the operation.
192+
OCTOSPI1->AR = addr;
193+
} else {
194+
OCTOSPI1->DLR = len - 1;
195+
196+
OCTOSPI1->CCR =
197+
0 << OCTOSPI_CCR_DDTR_Pos // DD mode disabled
198+
| 0 << OCTOSPI_CCR_SIOO_Pos // send instruction every transaction
199+
| 1 << OCTOSPI_CCR_DMODE_Pos // data on 1 line
200+
| 0 << OCTOSPI_CCR_ABMODE_Pos // no alternate byte
201+
| adsize << OCTOSPI_CCR_ADSIZE_Pos // 32/24-bit address size
202+
| 1 << OCTOSPI_CCR_ADMODE_Pos // address on 1 line
203+
| 1 << OCTOSPI_CCR_IMODE_Pos // instruction on 1 line
204+
;
205+
OCTOSPI1->TCR = 0 << OCTOSPI_TCR_DCYC_Pos; // 0 dummy cycles
206+
OCTOSPI1->IR = cmd << OCTOSPI_IR_INSTRUCTION_Pos; // write opcode
207+
OCTOSPI1->AR = addr;
208+
209+
// Write out the data 1 byte at a time
210+
// This triggers the start of the operation.
211+
while (len) {
212+
while (!(OCTOSPI1->SR & OCTOSPI_SR_FTF)) {
213+
if (OCTOSPI1->SR & OCTOSPI_SR_TEF) {
214+
return -MP_EIO;
215+
}
216+
}
217+
*(volatile uint8_t *)&OCTOSPI1->DR = *src++;
218+
--len;
219+
}
220+
}
221+
222+
// Wait for write to finish
223+
while (!(OCTOSPI1->SR & OCTOSPI_SR_TCF)) {
224+
if (OCTOSPI1->SR & OCTOSPI_SR_TEF) {
225+
return -MP_EIO;
226+
}
227+
}
228+
229+
OCTOSPI1->FCR = OCTOSPI_FCR_CTCF; // clear TC flag
230+
231+
return 0;
232+
}
233+
234+
STATIC int octospi_read_cmd(void *self_in, uint8_t cmd, size_t len, uint32_t *dest) {
235+
(void)self_in;
236+
237+
OCTOSPI1->FCR = OCTOSPI_FCR_CTCF; // clear TC flag
238+
239+
OCTOSPI1->DLR = len - 1; // number of bytes to read
240+
241+
OCTOSPI1->CR = (OCTOSPI1->CR & ~OCTOSPI_CR_FMODE_Msk) | 1 << OCTOSPI_CR_FMODE_Pos; // indirect read mode
242+
243+
OCTOSPI1->CCR =
244+
0 << OCTOSPI_CCR_DDTR_Pos // DD mode disabled
245+
| 0 << OCTOSPI_CCR_SIOO_Pos // send instruction every transaction
246+
| 1 << OCTOSPI_CCR_DMODE_Pos // data on 1 line
247+
| 0 << OCTOSPI_CCR_ABMODE_Pos // no alternate byte
248+
| 0 << OCTOSPI_CCR_ADMODE_Pos // no address
249+
| 1 << OCTOSPI_CCR_IMODE_Pos // instruction on 1 line
250+
;
251+
252+
OCTOSPI1->TCR = 0 << OCTOSPI_TCR_DCYC_Pos; // 0 dummy cycles
253+
254+
// This triggers the start of the operation.
255+
OCTOSPI1->IR = cmd << OCTOSPI_IR_INSTRUCTION_Pos; // read opcode
256+
257+
// Wait for read to finish
258+
while (!(OCTOSPI1->SR & OCTOSPI_SR_TCF)) {
259+
if (OCTOSPI1->SR & OCTOSPI_SR_TEF) {
260+
return -MP_EIO;
261+
}
262+
}
263+
264+
OCTOSPI1->FCR = OCTOSPI_FCR_CTCF; // clear TC flag
265+
266+
// Read result
267+
*dest = OCTOSPI1->DR;
268+
269+
return 0;
270+
}
271+
272+
STATIC int octospi_read_cmd_qaddr_qdata(void *self_in, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest) {
273+
(void)self_in;
274+
275+
uint32_t adsize = MICROPY_HW_SPI_ADDR_IS_32BIT(addr) ? 3 : 2;
276+
uint32_t dmode = 1; // data on 1-line
277+
uint32_t admode = 1; // address on 1-line
278+
uint32_t dcyc = 0; // 0 dummy cycles
279+
280+
if (cmd == 0xeb) {
281+
// Convert to 1-line command.
282+
cmd = MICROPY_HW_SPI_ADDR_IS_32BIT(addr) ? 0x13 : 0x03;
283+
}
284+
285+
OCTOSPI1->FCR = OCTOSPI_FCR_CTCF; // clear TC flag
286+
287+
OCTOSPI1->DLR = len - 1; // number of bytes to read
288+
OCTOSPI1->CR = (OCTOSPI1->CR & ~OCTOSPI_CR_FMODE_Msk) | 1 << OCTOSPI_CR_FMODE_Pos; // indirect read mode
289+
OCTOSPI1->CCR =
290+
0 << OCTOSPI_CCR_DDTR_Pos // DD mode disabled
291+
| 0 << OCTOSPI_CCR_SIOO_Pos // send instruction every transaction
292+
| dmode << OCTOSPI_CCR_DMODE_Pos // data on n lines
293+
| 0 << OCTOSPI_CCR_ABSIZE_Pos // 8-bit alternate byte
294+
| 0 << OCTOSPI_CCR_ABMODE_Pos // no alternate byte
295+
| adsize << OCTOSPI_CCR_ADSIZE_Pos // 32 or 24-bit address size
296+
| admode << OCTOSPI_CCR_ADMODE_Pos // address on n lines
297+
| 1 << OCTOSPI_CCR_IMODE_Pos // instruction on 1 line
298+
;
299+
OCTOSPI1->TCR = dcyc << OCTOSPI_TCR_DCYC_Pos; // n dummy cycles
300+
OCTOSPI1->IR = cmd << OCTOSPI_IR_INSTRUCTION_Pos; // quad read opcode
301+
302+
// This triggers the start of the operation.
303+
OCTOSPI1->AR = addr; // address to read from
304+
305+
// Read in the data 4 bytes at a time if dest is aligned
306+
if (((uintptr_t)dest & 3) == 0) {
307+
while (len >= 4) {
308+
while (!(OCTOSPI1->SR & OCTOSPI_SR_FTF)) {
309+
if (OCTOSPI1->SR & OCTOSPI_SR_TEF) {
310+
return -MP_EIO;
311+
}
312+
}
313+
*(uint32_t *)dest = OCTOSPI1->DR;
314+
dest += 4;
315+
len -= 4;
316+
}
317+
}
318+
319+
// Read in remaining data 1 byte at a time
320+
while (len) {
321+
while (!((OCTOSPI1->SR >> OCTOSPI_SR_FLEVEL_Pos) & 0x3f)) {
322+
if (OCTOSPI1->SR & OCTOSPI_SR_TEF) {
323+
return -MP_EIO;
324+
}
325+
}
326+
*dest++ = *(volatile uint8_t *)&OCTOSPI1->DR;
327+
--len;
328+
}
329+
330+
OCTOSPI1->FCR = OCTOSPI_FCR_CTCF; // clear TC flag
331+
332+
return 0;
333+
}
334+
335+
const mp_qspi_proto_t octospi_proto = {
336+
.ioctl = octospi_ioctl,
337+
.write_cmd_data = octospi_write_cmd_data,
338+
.write_cmd_addr_data = octospi_write_cmd_addr_data,
339+
.read_cmd = octospi_read_cmd,
340+
.read_cmd_qaddr_qdata = octospi_read_cmd_qaddr_qdata,
341+
};
342+
343+
#endif // defined(MICROPY_HW_OSPIFLASH_SIZE_BITS_LOG2)

ports/stm32/octospi.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2023 Damien P. George
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
#ifndef MICROPY_INCLUDED_STM32_OCTOSPI_H
27+
#define MICROPY_INCLUDED_STM32_OCTOSPI_H
28+
29+
#include "drivers/bus/qspi.h"
30+
31+
extern const mp_qspi_proto_t octospi_proto;
32+
33+
#endif // MICROPY_INCLUDED_STM32_OCTOSPI_H

0 commit comments

Comments
 (0)