Skip to content

Commit 4bbf002

Browse files
committed
LPC MCUXpresso: Add QSPI support
Signed-off-by: Mahesh Mahadevan <[email protected]>
1 parent fffb375 commit 4bbf002

File tree

4 files changed

+358
-1
lines changed

4 files changed

+358
-1
lines changed

targets/TARGET_NXP/TARGET_MCUXpresso_MCUS/TARGET_LPC/PeripheralPins.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,11 @@ extern const PinMap PinMap_SPI_SSEL[];
5050
/************PWM***************/
5151
extern const PinMap PinMap_PWM[];
5252

53+
#if DEVICE_QSPI
54+
/************QSPI***************/
55+
extern const PinMap PinMap_QSPI_DATA[];
56+
extern const PinMap PinMap_QSPI_SCLK[];
57+
extern const PinMap PinMap_QSPI_SSEL[];
58+
#endif
59+
5360
#endif

targets/TARGET_NXP/TARGET_MCUXpresso_MCUS/TARGET_LPC/objects.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "PortNames.h"
2121
#include "PeripheralNames.h"
2222
#include "PinNames.h"
23+
#include <stddef.h>
2324

2425
#ifdef __cplusplus
2526
extern "C" {
@@ -70,6 +71,12 @@ struct trng_s {
7071
};
7172
#endif
7273

74+
#if DEVICE_QSPI
75+
struct qspi_s {
76+
uint32_t instance;
77+
};
78+
#endif
79+
7380
#include "gpio_object.h"
7481

7582
#ifdef __cplusplus
Lines changed: 342 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,342 @@
1+
/* mbed Microcontroller Library
2+
* Copyright (c) 2018, ARM Limited
3+
* SPDX-License-Identifier: Apache-2.0
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
#if DEVICE_QSPI
19+
20+
#include "qspi_api.h"
21+
#include "mbed_error.h"
22+
#include "cmsis.h"
23+
#include "pinmap.h"
24+
#include "PeripheralPins.h"
25+
#include "qspi_device.h"
26+
27+
/* Command table indices */
28+
#define COMMAND_READ_INDEX (0)
29+
#define COMMAND_WRITE_INDEX (1)
30+
31+
#define LUT1_SEQ_INDEX 0 // Pre-defined read sequence
32+
#define LUT2_SEQ_INDEX 4 // Pre-defined write sequence
33+
#define LUT3_SEQ_INDEX 8 // User-define sequence
34+
/* Minimum write size */
35+
#define MIN_SIZE 16 // At least four words of data must be written into the TX Buffer
36+
37+
/* Array of QSPI peripheral base address. */
38+
static SPIFI_Type *const qspi_addrs[] = SPIFI_BASE_PTRS;
39+
40+
extern uint32_t qspi_get_freq(void);
41+
42+
qspi_status_t qspi_init(qspi_t *obj, PinName io0, PinName io1, PinName io2, PinName io3, PinName sclk, PinName ssel, uint32_t hz, uint8_t mode)
43+
{
44+
spifi_config_t config = {0};
45+
46+
uint32_t qspiio0name = pinmap_peripheral(io0, PinMap_QSPI_DATA);
47+
uint32_t qspiio1name = pinmap_peripheral(io1, PinMap_QSPI_DATA);
48+
uint32_t qspiio2name = pinmap_peripheral(io2, PinMap_QSPI_DATA);
49+
uint32_t qspiio3name = pinmap_peripheral(io3, PinMap_QSPI_DATA);
50+
uint32_t qspiclkname = pinmap_peripheral(sclk, PinMap_QSPI_SCLK);
51+
uint32_t qspisselname = pinmap_peripheral(ssel, PinMap_QSPI_SSEL);
52+
53+
uint32_t qspi_data_first = pinmap_merge(qspiio0name, qspiio1name);
54+
uint32_t qspi_data_second = pinmap_merge(qspiio2name, qspiio3name);
55+
uint32_t qspi_data_third = pinmap_merge(qspiclkname, qspisselname);
56+
57+
if (qspi_data_first != qspi_data_second || qspi_data_second != qspi_data_third ||
58+
qspi_data_first != qspi_data_third) {
59+
return QSPI_STATUS_INVALID_PARAMETER;
60+
}
61+
62+
if (qspi_frequency(obj, hz) != QSPI_STATUS_OK) {
63+
return QSPI_STATUS_INVALID_PARAMETER;
64+
}
65+
66+
/* Initialize SPIFI */
67+
SPIFI_GetDefaultConfig(&config);
68+
SPIFI_Init(SPIFI0, &config);
69+
70+
// tested all combinations, take first
71+
obj->instance = qspi_data_first;
72+
73+
pinmap_pinout(io0, PinMap_QSPI_DATA);
74+
pinmap_pinout(io1, PinMap_QSPI_DATA);
75+
pinmap_pinout(io2, PinMap_QSPI_DATA);
76+
pinmap_pinout(io3, PinMap_QSPI_DATA);
77+
78+
pinmap_pinout(sclk, PinMap_QSPI_SCLK);
79+
pinmap_pinout(ssel, PinMap_QSPI_SSEL);
80+
81+
return QSPI_STATUS_OK;
82+
}
83+
84+
qspi_status_t qspi_free(qspi_t *obj)
85+
{
86+
SPIFI_Deinit(qspi_addrs[obj->instance]);
87+
88+
return QSPI_STATUS_OK;
89+
}
90+
91+
qspi_status_t qspi_frequency(qspi_t *obj, int hz)
92+
{
93+
qspi_status_t status = QSPI_STATUS_OK;
94+
int div = qspi_get_freq() / hz;
95+
96+
if ((qspi_get_freq() % hz) != 0) {
97+
/* Incase the exact requested baud rate can be derived then set right div,
98+
* else set baudrate to the closest lower value
99+
*/
100+
div++;
101+
}
102+
103+
if (div > 256 || div < 1) {
104+
status = QSPI_STATUS_INVALID_PARAMETER;
105+
return status;
106+
}
107+
108+
/* Set the clock divider */
109+
CLOCK_SetClkDiv(kCLOCK_DivSpifiClk, div, false);
110+
111+
return status;
112+
}
113+
114+
static void qspi_prepare_command(qspi_t *obj, const qspi_command_t *command, const void *tx_data, size_t tx_size, void *rx_data, size_t rx_size)
115+
{
116+
spifi_command_t spifi_command = {0};
117+
SPIFI_Type *base = qspi_addrs[obj->instance];
118+
bool use_memory_command = false;
119+
120+
/* Set the opcode & poll mode */
121+
spifi_command.opcode = command->instruction.value;
122+
spifi_command.isPollMode = false;
123+
124+
/* Check if this is a data transmit */
125+
if (tx_data != NULL && tx_size) {
126+
spifi_command.dataLen = tx_size;
127+
spifi_command.direction = kSPIFI_DataOutput;
128+
}
129+
130+
/* Check if this is a data receive */
131+
if (rx_data != NULL && rx_size) {
132+
spifi_command.dataLen = rx_size;
133+
spifi_command.direction = kSPIFI_DataInput;
134+
}
135+
136+
/* Check if we need to set dual bit */
137+
if ((command->instruction.bus_width == QSPI_CFG_BUS_DUAL) ||
138+
(command->address.bus_width == QSPI_CFG_BUS_DUAL) ||
139+
(command->data.bus_width == QSPI_CFG_BUS_DUAL)) {
140+
base->CTRL |= SPIFI_CTRL_DUAL_MASK;
141+
} else {
142+
base->CTRL &= ~SPIFI_CTRL_DUAL_MASK;
143+
}
144+
145+
if (command->address.disabled == true) {
146+
/* No flash address specified */
147+
spifi_command.type = kSPIFI_CommandOpcodeOnly;
148+
spifi_command.format = kSPIFI_CommandAllSerial;
149+
if ((command->instruction.bus_width == QSPI_CFG_BUS_DUAL) ||
150+
(command->instruction.bus_width == QSPI_CFG_BUS_QUAD)) {
151+
spifi_command.format = kSPIFI_CommandAllQuad;
152+
}
153+
} else {
154+
/* Set the address size */
155+
spifi_command.type = (spifi_command_type_t)(command->address.size + 2);
156+
157+
/* Default to 1-1-1 mode */
158+
spifi_command.format = kSPIFI_CommandAllSerial;
159+
160+
/* Check if it is 2-2-2 or 4-4-4 mode */
161+
if (((command->instruction.bus_width == QSPI_CFG_BUS_DUAL) &&
162+
(command->address.bus_width == QSPI_CFG_BUS_DUAL) &&
163+
(command->data.bus_width == QSPI_CFG_BUS_DUAL)) ||
164+
((command->instruction.bus_width == QSPI_CFG_BUS_QUAD) &&
165+
(command->address.bus_width == QSPI_CFG_BUS_QUAD) &&
166+
(command->data.bus_width == QSPI_CFG_BUS_QUAD))) {
167+
/* All quad/dual. All fields of the command are in quad/dual format. */
168+
spifi_command.format = kSPIFI_CommandAllQuad;
169+
}
170+
171+
/* Check if it is 1-2-2 or 1-4-4 mode */
172+
if (((command->instruction.bus_width == QSPI_CFG_BUS_SINGLE) &&
173+
(command->address.bus_width == QSPI_CFG_BUS_DUAL) &&
174+
(command->data.bus_width == QSPI_CFG_BUS_DUAL)) ||
175+
((command->instruction.bus_width == QSPI_CFG_BUS_SINGLE) &&
176+
(command->address.bus_width == QSPI_CFG_BUS_QUAD) &&
177+
(command->data.bus_width == QSPI_CFG_BUS_QUAD))) {
178+
/* Serial opcode. Opcode field is serial. Other fields are quad/dual. */
179+
spifi_command.format = kSPIFI_CommandOpcodeSerial;
180+
}
181+
182+
/* Check if it is 1-1-2 or 1-1-4 mode */
183+
if (((command->instruction.bus_width == QSPI_CFG_BUS_SINGLE) &&
184+
(command->address.bus_width == QSPI_CFG_BUS_SINGLE) &&
185+
(command->data.bus_width == QSPI_CFG_BUS_DUAL)) ||
186+
((command->instruction.bus_width == QSPI_CFG_BUS_SINGLE) &&
187+
(command->address.bus_width == QSPI_CFG_BUS_SINGLE) &&
188+
(command->data.bus_width == QSPI_CFG_BUS_QUAD))) {
189+
/* Quad/dual data. Data field is quad/dual, other fields are serial. */
190+
spifi_command.format = kSPIFI_CommandDataQuad;
191+
}
192+
193+
/* Check if this is a data receive */
194+
if (rx_data != NULL && rx_size) {
195+
uint32_t cycles_per_byte = 8;
196+
197+
if ((command->address.bus_width == QSPI_CFG_BUS_DUAL) &&
198+
(command->data.bus_width == QSPI_CFG_BUS_DUAL)) {
199+
cycles_per_byte = 4;
200+
}
201+
if ((command->address.bus_width == QSPI_CFG_BUS_QUAD) &&
202+
(command->data.bus_width == QSPI_CFG_BUS_QUAD)) {
203+
cycles_per_byte = 2;
204+
}
205+
206+
/* Set dummy bytes */
207+
spifi_command.intermediateBytes = command->dummy_count / cycles_per_byte;
208+
209+
use_memory_command = true;
210+
}
211+
212+
/* Set the flash address */
213+
SPIFI_SetCommandAddress(base, command->address.value);
214+
}
215+
216+
if (use_memory_command) {
217+
/* Setup command */
218+
SPIFI_SetMemoryCommand(base, &spifi_command);
219+
} else {
220+
/* Setup memory command */
221+
SPIFI_SetCommand(base, &spifi_command);
222+
}
223+
}
224+
225+
qspi_status_t qspi_write(qspi_t *obj, const qspi_command_t *command, const void *data, size_t *length)
226+
{
227+
uint32_t to_write = *length;
228+
uint32_t *data_send = (uint32_t *)data;
229+
SPIFI_Type *base = qspi_addrs[obj->instance];
230+
uint32_t i = 0;
231+
232+
/* Enforce word-sized access */
233+
if ((to_write & 0x3) != 0) {
234+
return QSPI_STATUS_INVALID_PARAMETER;
235+
}
236+
237+
/* Use a pre-defined command is no write instruction is provided */
238+
if (command->instruction.disabled) {
239+
/* Set the flash address */
240+
SPIFI_SetCommandAddress(base, command->address.value);
241+
242+
/* If no instruction provided then use the pre-defined read sequence */
243+
preset_spifi_command[COMMAND_WRITE_INDEX].dataLen = to_write;
244+
SPIFI_SetCommand(base, &preset_spifi_command[COMMAND_WRITE_INDEX]);
245+
} else {
246+
/* Prepare the write command */
247+
qspi_prepare_command(obj, command, data, to_write, NULL, 0);
248+
}
249+
250+
/* Write the data */
251+
for (i = 0; i < to_write; i += 4, data_send++) {
252+
SPIFI_WriteData(base, *data_send);
253+
}
254+
255+
/* Wait for deasseration of CS */
256+
while (SPIFI_GetStatusFlag(base) & kSPIFI_CommandWriteFinished) {
257+
}
258+
259+
return QSPI_STATUS_OK;
260+
}
261+
262+
qspi_status_t qspi_read(qspi_t *obj, const qspi_command_t *command, void *data, size_t *length)
263+
{
264+
uint32_t dest_addr = FSL_FEATURE_SPIFI_START_ADDR + command->address.value;
265+
uint32_t to_read = *length;
266+
SPIFI_Type *base = qspi_addrs[obj->instance];
267+
268+
/* Enforce word-sized access */
269+
if ((to_read & 0x3) != 0) {
270+
return QSPI_STATUS_INVALID_PARAMETER;
271+
}
272+
273+
/* Use a pre-defined command is no read instruction is provided */
274+
if (command->instruction.disabled) {
275+
/* If no instruction provided then use the pre-defined read sequence */
276+
preset_spifi_command[COMMAND_READ_INDEX].dataLen = to_read;
277+
SPIFI_SetMemoryCommand(base, &preset_spifi_command[COMMAND_READ_INDEX]);
278+
} else {
279+
/* Prepare for read command */
280+
qspi_prepare_command(obj, command, NULL, 0, data, to_read);
281+
}
282+
283+
for (uint32_t i = 0; i < to_read / 4; i++) {
284+
((uint32_t*)data)[i] = *((uint32_t *)(dest_addr) + i);
285+
}
286+
287+
return QSPI_STATUS_OK;
288+
}
289+
290+
qspi_status_t qspi_command_transfer(qspi_t *obj, const qspi_command_t *command, const void *tx_data, size_t tx_size, void *rx_data, size_t rx_size)
291+
{
292+
SPIFI_Type *base = qspi_addrs[obj->instance];
293+
294+
if (tx_size > MIN_SIZE || rx_size > MIN_SIZE) {
295+
return QSPI_STATUS_INVALID_PARAMETER;
296+
}
297+
298+
if ((tx_data == NULL || tx_size == 0) && (rx_data == NULL || rx_size == 0)) {
299+
/* Setup the command */
300+
qspi_prepare_command(obj, command, tx_data, tx_size, rx_data, rx_size);
301+
} else {
302+
if (tx_data != NULL && tx_size) {
303+
/* Transmit data to QSPI */
304+
uint8_t val[MIN_SIZE];
305+
306+
memset(val, 0, sizeof(val));
307+
memcpy(val, tx_data, tx_size);
308+
309+
/* Setup the command */
310+
qspi_prepare_command(obj, command, tx_data, tx_size, rx_data, rx_size);
311+
312+
for (uint32_t i = 0; i < tx_size; i++) {
313+
SPIFI_WriteDataByte(base, val[i]);
314+
}
315+
}
316+
317+
if (rx_data != NULL && rx_size) {
318+
/* Receive data from QSPI */
319+
uint8_t val[MIN_SIZE];
320+
321+
memset(val, 0, sizeof(val));
322+
323+
/* Setup the command */
324+
qspi_prepare_command(obj, command, tx_data, tx_size, rx_data, rx_size);
325+
326+
for (uint32_t i = 0; i < rx_size; i++) {
327+
val[i] = SPIFI_ReadDataByte(base);
328+
}
329+
330+
memcpy(rx_data, val, rx_size);
331+
}
332+
}
333+
334+
/* Wait for deasseration of CS */
335+
while (SPIFI_GetStatusFlag(base) & kSPIFI_CommandWriteFinished) {
336+
}
337+
338+
return QSPI_STATUS_OK;
339+
}
340+
341+
#endif
342+

targets/targets.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1839,7 +1839,8 @@
18391839
"SPISLAVE",
18401840
"STDIO_MESSAGES",
18411841
"FLASH",
1842-
"TRNG"
1842+
"TRNG",
1843+
"QSPI"
18431844
],
18441845
"device_name": "LPC54628J512ET180",
18451846
"post_binary_hook": { "function": "LPCTargetCode.lpc_patch" },

0 commit comments

Comments
 (0)