Skip to content

Commit 6827b42

Browse files
authored
Merge pull request #14397 from macronix/macronix_spi_NAND
add SPI NAND Block device driver
2 parents e2ec3cd + 4f29d6a commit 6827b42

File tree

5 files changed

+1232
-2
lines changed

5 files changed

+1232
-2
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright (c) 2021 ARM Limited. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
target_include_directories(mbed-storage-spinand
5+
INTERFACE
6+
include
7+
include/SPINAND
8+
)
9+
10+
target_sources(mbed-storage-spinand
11+
INTERFACE
12+
source/SPINANDBlockDevice.cpp
13+
)
Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
/* mbed Microcontroller Library
2+
* Copyright (c) 2021 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+
#ifndef MBED_SPINAND_BLOCK_DEVICE_H
18+
#define MBED_SPINAND_BLOCK_DEVICE_H
19+
20+
#include "drivers/QSPI.h"
21+
#include "blockdevice/BlockDevice.h"
22+
#include "platform/Callback.h"
23+
24+
#ifndef MBED_CONF_SPINAND_QSPI_IO0
25+
#define MBED_CONF_SPINAND_QSPI_IO0 NC
26+
#endif
27+
#ifndef MBED_CONF_SPINAND_QSPI_IO1
28+
#define MBED_CONF_SPINAND_QSPI_IO1 NC
29+
#endif
30+
#ifndef MBED_CONF_SPINAND_QSPI_IO2
31+
#define MBED_CONF_SPINAND_QSPI_IO2 NC
32+
#endif
33+
#ifndef MBED_CONF_SPINAND_QSPI_IO3
34+
#define MBED_CONF_SPINAND_QSPI_IO3 NC
35+
#endif
36+
#ifndef MBED_CONF_SPINAND_QSPI_SCK
37+
#define MBED_CONF_SPINAND_QSPI_SCK NC
38+
#endif
39+
#ifndef MBED_CONF_SPINAND_QSPI_CSN
40+
#define MBED_CONF_SPINAND_QSPI_CSN NC
41+
#endif
42+
#ifndef MBED_CONF_SPINAND_QSPI_POLARITY_MODE
43+
#define MBED_CONF_SPINAND_QSPI_POLARITY_MODE 0
44+
#endif
45+
#ifndef MBED_CONF_SPINAND_QSPI_FREQ
46+
#define MBED_CONF_SPINAND_QSPI_FREQ 40000000
47+
#endif
48+
49+
/** Enum spinand standard error codes
50+
*
51+
* @enum spinand_bd_error
52+
*/
53+
enum spinand_bd_error {
54+
SPINAND_BD_ERROR_OK = 0, /*!< no error */
55+
SPINAND_BD_ERROR_DEVICE_ERROR = BD_ERROR_DEVICE_ERROR, /*!< device specific error -4001 */
56+
SPINAND_BD_ERROR_PARSING_FAILED = -4002, /* SFDP Parsing failed */
57+
SPINAND_BD_ERROR_READY_FAILED = -4003, /* Wait for Mem Ready failed */
58+
SPINAND_BD_ERROR_WREN_FAILED = -4004, /* Write Enable Failed */
59+
SPINAND_BD_ERROR_INVALID_ERASE_PARAMS = -4005, /* Erase command not on sector aligned addresses or exceeds device size */
60+
SPINAND_BD_ERROR_DEVICE_NOT_UNIQUE = -4006, /* Only one instance per csel is allowed */
61+
SPINAND_BD_ERROR_DEVICE_MAX_EXCEED = -4007 /* Max active SPINAND devices exceeded */
62+
};
63+
64+
/** Enum spinand polarity mode
65+
*
66+
* @enum spinand_polarity_mode
67+
*/
68+
enum spinand_polarity_mode {
69+
SPINAND_POLARITY_MODE_0 = 0, /* CPOL=0, CPHA=0 */
70+
SPINAND_POLARITY_MODE_1 /* CPOL=1, CPHA=1 */
71+
};
72+
73+
#define SPINAND_MAX_ACTIVE_FLASH_DEVICES 10
74+
75+
/** BlockDevice for SPI NAND flash devices over QSPI bus
76+
*
77+
* @code
78+
* // Here's an example using SPI NAND flash device on DISCO_L4R9I target
79+
* #include "mbed.h"
80+
* #include "SPINANDBlockDevice.h"
81+
*
82+
* SPINANDBlockDevice block_device(SPINAND_FLASH1_IO0, SPINAND_FLASH1_IO1, SPINAND_FLASH1_IO2, SPINAND_FLASH1_IO3,
83+
* SPINAND_FLASH1_SCK, SPINAND_FLASH1_CSN, SPINAND_POLARITY_MODE_0, MBED_CONF_SPINAND_QSPI_FREQ);
84+
*
85+
* int main()
86+
* {
87+
* printf("SPI NAND Flash Block Device example\n");
88+
*
89+
* // Initialize the SPI NAND flash device and print the memory layout
90+
* block_device.init();
91+
* bd_size_t sector_size_at_address_0 = block_device.get_erase_size(0);
92+
*
93+
* printf("SPINAND BD size: %llu\n", block_device.size());
94+
* printf("SPINAND BD read size: %llu\n", block_device.get_read_size());
95+
* printf("SPINAND BD program size: %llu\n", block_device.get_program_size());
96+
* printf("SPINAND BD erase size (at address 0): %llu\n", sector_size_at_address_0);
97+
*
98+
* // Write "Hello World!" to the first block
99+
* char *buffer = (char *) malloc(sector_size_at_address_0);
100+
* sprintf(buffer, "Hello World!\n");
101+
* block_device.erase(0, sector_size_at_address_0);
102+
* block_device.program(buffer, 0, sector_size_at_address_0);
103+
*
104+
* // Read back what was stored
105+
* block_device.read(buffer, 0, sector_size_at_address_0);
106+
* printf("%s", buffer);
107+
*
108+
* // Deinitialize the device
109+
* block_device.deinit();
110+
* }
111+
* @endcode
112+
*/
113+
class SPINANDBlockDevice : public mbed::BlockDevice {
114+
public:
115+
/** Create SPINANDBlockDevice - An SPI NAND Flash Block Device over QSPI bus
116+
*
117+
* @param io0 1st IO pin used for sending/receiving data during data phase of a transaction
118+
* @param io1 2nd IO pin used for sending/receiving data during data phase of a transaction
119+
* @param io2 3rd IO pin used for sending/receiving data during data phase of a transaction
120+
* @param io3 4th IO pin used for sending/receiving data during data phase of a transaction
121+
* @param sclk QSPI Clock pin
122+
* @param csel QSPI chip select pin
123+
* @param clock_mode specifies the QSPI Clock Polarity mode (SPINAND_POLARITY_MODE_0/SPINAND_POLARITY_MODE_1)
124+
* default value = 0
125+
* @param freq Clock frequency of the QSPI bus (defaults to 40MHz)
126+
*/
127+
SPINANDBlockDevice(PinName io0 = MBED_CONF_SPINAND_QSPI_IO0,
128+
PinName io1 = MBED_CONF_SPINAND_QSPI_IO1,
129+
PinName io2 = MBED_CONF_SPINAND_QSPI_IO2,
130+
PinName io3 = MBED_CONF_SPINAND_QSPI_IO3,
131+
PinName sclk = MBED_CONF_SPINAND_QSPI_SCK,
132+
PinName csel = MBED_CONF_SPINAND_QSPI_CSN,
133+
int clock_mode = MBED_CONF_SPINAND_QSPI_POLARITY_MODE,
134+
int freq = MBED_CONF_SPINAND_QSPI_FREQ);
135+
136+
/** Initialize a block device
137+
*
138+
* @return SPINAND_BD_ERROR_OK(0) - success
139+
* SPINAND_BD_ERROR_DEVICE_ERROR - device driver transaction failed
140+
* SPINAND_BD_ERROR_READY_FAILED - Waiting for Memory ready failed or timedout
141+
*/
142+
virtual int init();
143+
144+
/** Deinitialize a block device
145+
*
146+
* @return SPINAND_BD_ERROR_OK(0) - success
147+
* SPINAND_BD_ERROR_DEVICE_ERROR - device driver transaction failed
148+
*/
149+
virtual int deinit();
150+
151+
/** Destruct SPINANDBlockDevie
152+
*/
153+
~SPINANDBlockDevice()
154+
{
155+
deinit();
156+
}
157+
158+
/** Read blocks from a block device
159+
*
160+
* @param buffer Buffer to write blocks to
161+
* @param addr Address of block to begin reading from
162+
* @param size Size to read in bytes, must be a multiple of read block size
163+
* @return SPINAND_BD_ERROR_OK(0) - success
164+
* SPINAND_BD_ERROR_DEVICE_ERROR - device driver transaction failed
165+
*/
166+
virtual int read(void *buffer, mbed::bd_addr_t addr, mbed::bd_size_t size);
167+
168+
/** Program blocks to a block device
169+
*
170+
* The blocks must have been erased prior to being programmed
171+
*
172+
* @param buffer Buffer of data to write to blocks
173+
* @param addr Address of block to begin writing to
174+
* @param size Size to write in bytes, must be a multiple of program block size
175+
* @return SPINAND_BD_ERROR_OK(0) - success
176+
* SPINAND_BD_ERROR_DEVICE_ERROR - device driver transaction failed
177+
* SPINAND_BD_ERROR_READY_FAILED - Waiting for Memory ready failed or timed out
178+
* SPINAND_BD_ERROR_WREN_FAILED - Write Enable failed
179+
*/
180+
virtual int program(const void *buffer, mbed::bd_addr_t addr, mbed::bd_size_t size);
181+
182+
/** Erase blocks on a block device
183+
*
184+
* The state of an erased block is undefined until it has been programmed
185+
*
186+
* @param addr Address of block to begin erasing
187+
* @param size Size to erase in bytes, must be a multiple of erase block size
188+
* @return SPINAND_BD_ERROR_OK(0) - success
189+
* SPINAND_BD_ERROR_DEVICE_ERROR - device driver transaction failed
190+
* SPINAND_BD_ERROR_READY_FAILED - Waiting for Memory ready failed or timed out
191+
* SPINAND_BD_ERROR_WREN_FAILED - Write Enable failed
192+
* SPINAND_BD_ERROR_INVALID_ERASE_PARAMS - Trying to erase unaligned address or size
193+
*/
194+
virtual int erase(mbed::bd_addr_t addr, mbed::bd_size_t size);
195+
196+
/** Get the size of a readable block
197+
*
198+
* @return Size of a readable block in bytes
199+
*/
200+
virtual mbed::bd_size_t get_read_size() const;
201+
202+
/** Get the size of a programable block
203+
*
204+
* @return Size of a program block size in bytes
205+
* @note Must be a multiple of the read size
206+
*/
207+
virtual mbed::bd_size_t get_program_size() const;
208+
209+
/** Get the size of a eraseable block
210+
*
211+
* @return Size of a minimal erase block, common to all regions, in bytes
212+
* @note Must be a multiple of the program size
213+
*/
214+
virtual mbed::bd_size_t get_erase_size() const;
215+
virtual mbed::bd_size_t get_erase_size(bd_addr_t addr) const;
216+
217+
/** Get the value of storage byte after it was erased
218+
*
219+
* If get_erase_value returns a non-negative byte value, the underlying
220+
* storage is set to that value when erased, and storage containing
221+
* that value can be programmed without another erase.
222+
*
223+
* @return The value of storage when erased, or -1 if you can't
224+
* rely on the value of erased storage
225+
*/
226+
virtual int get_erase_value() const;
227+
228+
/** Get the total size of the underlying device
229+
*
230+
* @return Size of the underlying device in bytes
231+
*/
232+
virtual mbed::bd_size_t size() const;
233+
234+
/** Get the BlockDevice class type.
235+
*
236+
* @return A string represent the BlockDevice class type.
237+
*/
238+
virtual const char *get_type() const;
239+
240+
private:
241+
/********************************/
242+
/* Different Device Csel Mgmt */
243+
/********************************/
244+
// Add a new SPI NAND device CS to existing devices list.
245+
// Only one SPINANDBlockDevice instance per CS is allowed
246+
int add_new_csel_instance(PinName csel);
247+
248+
// Remove device CS from existing device list upon destroying object (last deinit is called)
249+
int remove_csel_instance(PinName csel);
250+
251+
/********************************/
252+
/* Calls to QSPI Driver APIs */
253+
/********************************/
254+
// Send Program/Write command to Driver
255+
qspi_status_t _qspi_send_program_command(mbed::qspi_inst_t prog_instruction, const void *buffer,
256+
mbed::bd_addr_t addr, mbed::bd_size_t *size);
257+
258+
// Send Read command to Driver
259+
qspi_status_t _qspi_send_read_command(mbed::qspi_inst_t read_instruction, void *buffer, mbed::bd_addr_t addr, mbed::bd_size_t size);
260+
261+
// Send Erase Instruction using command_transfer command to Driver
262+
qspi_status_t _qspi_send_erase_command(mbed::qspi_inst_t erase_instruction, mbed::bd_addr_t addr, mbed::bd_size_t size);
263+
264+
// Send Generic command_transfer command to Driver
265+
qspi_status_t _qspi_send_general_command(mbed::qspi_inst_t instruction_int, mbed::bd_addr_t addr, const char *tx_buffer,
266+
mbed::bd_size_t tx_length, const char *rx_buffer, mbed::bd_size_t rx_length);
267+
268+
// Send set_frequency command to Driver
269+
qspi_status_t _qspi_set_frequency(int freq);
270+
271+
/*********************************/
272+
/* Flash Configuration Functions */
273+
/*********************************/
274+
275+
// Quad Enable in Security Register
276+
int _set_quad_enable();
277+
278+
// Clear the device's block protection
279+
int _clear_block_protection();
280+
281+
// Configure Write Enable in Status Register
282+
int _set_write_enable();
283+
284+
// Wait on status register until write not-in-progress
285+
bool _is_mem_ready();
286+
287+
private:
288+
289+
// QSPI Driver Object
290+
mbed::QSPI _qspi;
291+
292+
// Static List of different QSPI based Flash devices csel that already exist
293+
// Each QSPI Flash device csel can have only 1 SPINANDBlockDevice instance
294+
// _devices_mutex is used to lock csel list - only one SPINANDBlockDevice instance per csel is allowed
295+
static SingletonPtr<PlatformMutex> _devices_mutex;
296+
static int _number_of_active_spinand_flash_csel;
297+
static PinName *_active_spinand_flash_csel_arr;
298+
299+
int _unique_device_status;
300+
PinName _csel;
301+
302+
// Mutex is used to protect Flash device for some QSPI Driver commands that must be done sequentially with no other commands in between
303+
// e.g. (1)Set Write Enable, (2)Program, (3)Wait Memory Ready
304+
PlatformMutex _mutex;
305+
306+
// Command Instructions
307+
mbed::qspi_inst_t _read_instruction;
308+
mbed::qspi_inst_t _program_instruction;
309+
310+
int _freq;
311+
312+
// Bus speed configuration
313+
qspi_bus_width_t _inst_width; //Bus width for Instruction phase
314+
qspi_bus_width_t _address_width; //Bus width for Address phase
315+
qspi_address_size_t _address_size; //Number of bits for address
316+
qspi_alt_size_t _alt_size; //Number of bits for alt
317+
bool _alt_enabled; //Whether alt is enabled
318+
uint8_t _dummy_cycles; //Number of Dummy cycles required by Current Bus Mode
319+
qspi_bus_width_t _data_width; //Bus width for Data phase
320+
321+
uint32_t _init_ref_count;
322+
bool _is_initialized;
323+
};
324+
325+
#endif
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"name": "spinand",
3+
"config": {
4+
"enable-and-reset": {
5+
"help": "(Legacy SFDP 1.0 ONLY) Reset sequence is enable reset (0x66) then reset (0x99)",
6+
"value": false
7+
},
8+
"direct-reset": {
9+
"help": "(Legacy SFDP 1.0 ONLY) Reset involves a single command (0xF0)",
10+
"value": false
11+
},
12+
"SPINAND_IO0": "MBED_CONF_DRIVERS_QSPI_IO0",
13+
"SPINAND_IO1": "MBED_CONF_DRIVERS_QSPI_IO1",
14+
"SPINAND_IO2": "MBED_CONF_DRIVERS_QSPI_IO2",
15+
"SPINAND_IO3": "MBED_CONF_DRIVERS_QSPI_IO3",
16+
"SPINAND_SCK": "MBED_CONF_DRIVERS_QSPI_SCK",
17+
"SPINAND_CSN": "MBED_CONF_DRIVERS_QSPI_CSN",
18+
"SPINAND_POLARITY_MODE": 0,
19+
"SPINAND_FREQ": "40000000",
20+
"SPINAND_MIN_READ_SIZE": "1",
21+
"SPINAND_MIN_PROG_SIZE": "1",
22+
"SPINAND_FLASH_SIZE": "2048*64*512",
23+
"SPINAND_BLOCK_SIZE": "2048*64",
24+
"SPINAND_PAGE_SIZE": "2048"
25+
},
26+
"target_overrides": {
27+
"MX31LF4GE4BC": {
28+
"SPINAND_FREQ": "2000000"
29+
}
30+
}
31+
}

0 commit comments

Comments
 (0)