Skip to content

Commit adda38c

Browse files
committed
stm32/qspi: Add hardware QSPI driver, with memory-map capability.
It supports the abstract QSPI protocol defined in drivers/bus/qspi.h.
1 parent 8bd0a51 commit adda38c

File tree

3 files changed

+285
-0
lines changed

3 files changed

+285
-0
lines changed

ports/stm32/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ SRC_C = \
214214
dma.c \
215215
i2c.c \
216216
spi.c \
217+
qspi.c \
217218
uart.c \
218219
can.c \
219220
usb.c \

ports/stm32/qspi.c

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2018 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+
#include <string.h>
28+
29+
#include "py/mperrno.h"
30+
#include "py/mphal.h"
31+
#include "genhdr/pins.h"
32+
#include "qspi.h"
33+
34+
#if defined(MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2)
35+
36+
void qspi_init(void) {
37+
// Configure pins
38+
mp_hal_pin_config(&MICROPY_HW_QSPIFLASH_CS, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, 10);
39+
mp_hal_pin_config(&MICROPY_HW_QSPIFLASH_SCK, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, 9);
40+
mp_hal_pin_config(&MICROPY_HW_QSPIFLASH_IO0, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, 9);
41+
mp_hal_pin_config(&MICROPY_HW_QSPIFLASH_IO1, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, 9);
42+
mp_hal_pin_config(&MICROPY_HW_QSPIFLASH_IO2, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, 9);
43+
mp_hal_pin_config(&MICROPY_HW_QSPIFLASH_IO3, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, 9);
44+
45+
// Bring up the QSPI peripheral
46+
47+
__HAL_RCC_QSPI_CLK_ENABLE();
48+
49+
QUADSPI->CR =
50+
2 << QUADSPI_CR_PRESCALER_Pos // F_CLK = F_AHB/3 (72MHz when CPU is 216MHz)
51+
#if defined(QUADSPI_CR_FSEL_Pos)
52+
| 0 << QUADSPI_CR_FSEL_Pos // FLASH 1 selected
53+
#endif
54+
#if defined(QUADSPI_CR_DFM_Pos)
55+
| 0 << QUADSPI_CR_DFM_Pos // dual-flash mode disabled
56+
#endif
57+
| 0 << QUADSPI_CR_SSHIFT_Pos // no sample shift
58+
| 1 << QUADSPI_CR_TCEN_Pos // timeout counter enabled
59+
| 1 << QUADSPI_CR_EN_Pos // enable the peripheral
60+
;
61+
62+
QUADSPI->DCR =
63+
(MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2 - 3 - 1) << QUADSPI_DCR_FSIZE_Pos
64+
| 1 << QUADSPI_DCR_CSHT_Pos // nCS stays high for 2 cycles
65+
| 0 << QUADSPI_DCR_CKMODE_Pos // CLK idles at low state
66+
;
67+
}
68+
69+
void qspi_memory_map(void) {
70+
// Enable memory-mapped mode
71+
72+
QUADSPI->ABR = 0; // disable continuous read mode
73+
QUADSPI->LPTR = 100; // to tune
74+
QUADSPI->CCR =
75+
0 << QUADSPI_CCR_DDRM_Pos // DDR mode disabled
76+
| 0 << QUADSPI_CCR_SIOO_Pos // send instruction every transaction
77+
| 3 << QUADSPI_CCR_FMODE_Pos // memory-mapped mode
78+
| 3 << QUADSPI_CCR_DMODE_Pos // data on 4 lines
79+
| 4 << QUADSPI_CCR_DCYC_Pos // 4 dummy cycles
80+
| 0 << QUADSPI_CCR_ABSIZE_Pos // 8-bit alternate byte
81+
| 3 << QUADSPI_CCR_ABMODE_Pos // alternate byte on 4 lines
82+
| 2 << QUADSPI_CCR_ADSIZE_Pos // 24-bit address size
83+
| 3 << QUADSPI_CCR_ADMODE_Pos // address on 4 lines
84+
| 1 << QUADSPI_CCR_IMODE_Pos // instruction on 1 line
85+
| 0xeb << QUADSPI_CCR_INSTRUCTION_Pos // quad read opcode
86+
;
87+
}
88+
89+
STATIC int qspi_ioctl(void *self_in, uint32_t cmd) {
90+
(void)self_in;
91+
switch (cmd) {
92+
case MP_QSPI_IOCTL_INIT:
93+
qspi_init();
94+
break;
95+
case MP_QSPI_IOCTL_BUS_RELEASE:
96+
// Switch to memory-map mode when bus is idle
97+
qspi_memory_map();
98+
break;
99+
}
100+
return 0; // success
101+
}
102+
103+
STATIC void qspi_write_cmd_data(void *self_in, uint8_t cmd, size_t len, uint32_t data) {
104+
(void)self_in;
105+
106+
QUADSPI->FCR = QUADSPI_FCR_CTCF; // clear TC flag
107+
108+
if (len == 0) {
109+
QUADSPI->CCR =
110+
0 << QUADSPI_CCR_DDRM_Pos // DDR mode disabled
111+
| 0 << QUADSPI_CCR_SIOO_Pos // send instruction every transaction
112+
| 0 << QUADSPI_CCR_FMODE_Pos // indirect write mode
113+
| 0 << QUADSPI_CCR_DMODE_Pos // no data
114+
| 0 << QUADSPI_CCR_DCYC_Pos // 0 dummy cycles
115+
| 0 << QUADSPI_CCR_ABMODE_Pos // no alternate byte
116+
| 0 << QUADSPI_CCR_ADMODE_Pos // no address
117+
| 1 << QUADSPI_CCR_IMODE_Pos // instruction on 1 line
118+
| cmd << QUADSPI_CCR_INSTRUCTION_Pos // write opcode
119+
;
120+
} else {
121+
QUADSPI->DLR = len - 1;
122+
123+
QUADSPI->CCR =
124+
0 << QUADSPI_CCR_DDRM_Pos // DDR mode disabled
125+
| 0 << QUADSPI_CCR_SIOO_Pos // send instruction every transaction
126+
| 0 << QUADSPI_CCR_FMODE_Pos // indirect write mode
127+
| 1 << QUADSPI_CCR_DMODE_Pos // data on 1 line
128+
| 0 << QUADSPI_CCR_DCYC_Pos // 0 dummy cycles
129+
| 0 << QUADSPI_CCR_ABMODE_Pos // no alternate byte
130+
| 0 << QUADSPI_CCR_ADMODE_Pos // no address
131+
| 1 << QUADSPI_CCR_IMODE_Pos // instruction on 1 line
132+
| cmd << QUADSPI_CCR_INSTRUCTION_Pos // write opcode
133+
;
134+
135+
// This assumes len==2
136+
*(uint16_t*)&QUADSPI->DR = data;
137+
}
138+
139+
// Wait for write to finish
140+
while (!(QUADSPI->SR & QUADSPI_SR_TCF)) {
141+
}
142+
143+
QUADSPI->FCR = QUADSPI_FCR_CTCF; // clear TC flag
144+
}
145+
146+
STATIC void qspi_write_cmd_addr_data(void *self_in, uint8_t cmd, uint32_t addr, size_t len, const uint8_t *src) {
147+
(void)self_in;
148+
149+
QUADSPI->FCR = QUADSPI_FCR_CTCF; // clear TC flag
150+
151+
if (len == 0) {
152+
QUADSPI->CCR =
153+
0 << QUADSPI_CCR_DDRM_Pos // DDR mode disabled
154+
| 0 << QUADSPI_CCR_SIOO_Pos // send instruction every transaction
155+
| 0 << QUADSPI_CCR_FMODE_Pos // indirect write mode
156+
| 0 << QUADSPI_CCR_DMODE_Pos // no data
157+
| 0 << QUADSPI_CCR_DCYC_Pos // 0 dummy cycles
158+
| 0 << QUADSPI_CCR_ABMODE_Pos // no alternate byte
159+
| 2 << QUADSPI_CCR_ADSIZE_Pos // 24-bit address size
160+
| 1 << QUADSPI_CCR_ADMODE_Pos // address on 1 line
161+
| 1 << QUADSPI_CCR_IMODE_Pos // instruction on 1 line
162+
| cmd << QUADSPI_CCR_INSTRUCTION_Pos // write opcode
163+
;
164+
165+
QUADSPI->AR = addr;
166+
} else {
167+
QUADSPI->DLR = len - 1;
168+
169+
QUADSPI->CCR =
170+
0 << QUADSPI_CCR_DDRM_Pos // DDR mode disabled
171+
| 0 << QUADSPI_CCR_SIOO_Pos // send instruction every transaction
172+
| 0 << QUADSPI_CCR_FMODE_Pos // indirect write mode
173+
| 1 << QUADSPI_CCR_DMODE_Pos // data on 1 line
174+
| 0 << QUADSPI_CCR_DCYC_Pos // 0 dummy cycles
175+
| 0 << QUADSPI_CCR_ABMODE_Pos // no alternate byte
176+
| 2 << QUADSPI_CCR_ADSIZE_Pos // 24-bit address size
177+
| 1 << QUADSPI_CCR_ADMODE_Pos // address on 1 line
178+
| 1 << QUADSPI_CCR_IMODE_Pos // instruction on 1 line
179+
| cmd << QUADSPI_CCR_INSTRUCTION_Pos // write opcode
180+
;
181+
182+
QUADSPI->AR = addr;
183+
184+
// Write out the data
185+
while (len) {
186+
while (!(QUADSPI->SR & QUADSPI_SR_FTF)) {
187+
}
188+
// TODO it seems that writes need to be 32-bit wide to start the xfer...
189+
//*(volatile uint8_t*)QUADSPI->DR = *src++;
190+
//--len;
191+
QUADSPI->DR = *(uint32_t*)src;
192+
src += 4;
193+
len -= 4;
194+
}
195+
}
196+
197+
// Wait for write to finish
198+
while (!(QUADSPI->SR & QUADSPI_SR_TCF)) {
199+
}
200+
201+
QUADSPI->FCR = QUADSPI_FCR_CTCF; // clear TC flag
202+
}
203+
204+
STATIC uint32_t qspi_read_cmd(void *self_in, uint8_t cmd, size_t len) {
205+
(void)self_in;
206+
207+
QUADSPI->FCR = QUADSPI_FCR_CTCF; // clear TC flag
208+
209+
QUADSPI->DLR = len - 1; // number of bytes to read
210+
211+
QUADSPI->CCR =
212+
0 << QUADSPI_CCR_DDRM_Pos // DDR mode disabled
213+
| 0 << QUADSPI_CCR_SIOO_Pos // send instruction every transaction
214+
| 1 << QUADSPI_CCR_FMODE_Pos // indirect read mode
215+
| 1 << QUADSPI_CCR_DMODE_Pos // data on 1 line
216+
| 0 << QUADSPI_CCR_DCYC_Pos // 0 dummy cycles
217+
| 0 << QUADSPI_CCR_ABMODE_Pos // no alternate byte
218+
| 0 << QUADSPI_CCR_ADMODE_Pos // no address
219+
| 1 << QUADSPI_CCR_IMODE_Pos // instruction on 1 line
220+
| cmd << QUADSPI_CCR_INSTRUCTION_Pos // read opcode
221+
;
222+
223+
// Wait for read to finish
224+
while (!(QUADSPI->SR & QUADSPI_SR_TCF)) {
225+
}
226+
227+
QUADSPI->FCR = QUADSPI_FCR_CTCF; // clear TC flag
228+
229+
// Read result
230+
return QUADSPI->DR;
231+
}
232+
233+
STATIC void qspi_read_cmd_qaddr_qdata(void *self_in, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest) {
234+
(void)self_in;
235+
// This assumes that cmd=0xeb
236+
qspi_memory_map();
237+
memcpy(dest, (void*)(0x90000000 + addr), len);
238+
}
239+
240+
const mp_qspi_proto_t qspi_proto = {
241+
.ioctl = qspi_ioctl,
242+
.write_cmd_data = qspi_write_cmd_data,
243+
.write_cmd_addr_data = qspi_write_cmd_addr_data,
244+
.read_cmd = qspi_read_cmd,
245+
.read_cmd_qaddr_qdata = qspi_read_cmd_qaddr_qdata,
246+
};
247+
248+
#endif // defined(MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2)

ports/stm32/qspi.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2018 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_QSPI_H
27+
#define MICROPY_INCLUDED_STM32_QSPI_H
28+
29+
#include "drivers/bus/qspi.h"
30+
31+
extern const mp_qspi_proto_t qspi_proto;
32+
33+
void qspi_init(void);
34+
void qspi_memory_map(void);
35+
36+
#endif // MICROPY_INCLUDED_STM32_QSPI_H

0 commit comments

Comments
 (0)