Skip to content

Commit 515db95

Browse files
author
Cruz Monrreal
authored
Merge pull request #8349 from offirko/offir-mbed-reduced-spif
A Reduced SPIF Block Device for Boot Loader
2 parents 8054060 + ef40d6d commit 515db95

File tree

3 files changed

+584
-0
lines changed

3 files changed

+584
-0
lines changed
Lines changed: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
1+
2+
/* mbed Microcontroller Library
3+
* Copyright (c) 2018 ARM Limited
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+
#include "SPIFReducedBlockDevice.h"
18+
#include "mbed_wait_api.h"
19+
20+
// Read/write/erase sizes
21+
#define SPIF_READ_SIZE 1
22+
#define SPIF_PROG_SIZE 1
23+
#define SPIF_SE_SIZE 4096
24+
#define SPIF_TIMEOUT 10000
25+
26+
// Debug available
27+
#define SPIF_DEBUG 0
28+
29+
// Legacy SFDP Instruction Table.
30+
enum ops {
31+
SPIF_NOP = 0x00, // No operation
32+
SPIF_READ = 0x03, // Read data
33+
SPIF_PROG = 0x02, // Program data
34+
SPIF_SE = 0x20, // 4KB Sector Erase
35+
SPIF_CE = 0xc7, // Chip Erase
36+
SPIF_SFDP = 0x5a, // Read SFDP
37+
SPIF_WREN = 0x06, // Write Enable
38+
SPIF_WRDI = 0x04, // Write Disable
39+
SPIF_RDSR = 0x05, // Read Status Register
40+
SPIF_RDID = 0x9f, // Read Manufacturer and JDEC Device ID
41+
};
42+
43+
// Status register from RDSR
44+
// [---------| wel | wip ]
45+
// [- 6 -| 1 | 1 ]
46+
#define SPIF_WEL 0x2
47+
#define SPIF_WIP 0x1
48+
49+
50+
SPIFReducedBlockDevice::SPIFReducedBlockDevice(
51+
PinName mosi, PinName miso, PinName sclk, PinName cs, int freq)
52+
: _spi(mosi, miso, sclk), _cs(cs), _size(0)
53+
{
54+
_cs = 1;
55+
_spi.frequency(freq);
56+
}
57+
58+
int SPIFReducedBlockDevice::init()
59+
{
60+
// Check for vendor specific hacks, these should move into more general
61+
// handling when possible. RDID is not used to verify a device is attached.
62+
uint8_t id[3];
63+
_cmdread(SPIF_RDID, 0, 3, 0x0, id);
64+
65+
switch (id[0]) {
66+
case 0xbf:
67+
// SST devices come preset with block protection
68+
// enabled for some regions, issue gbpu instruction to clear
69+
_wren();
70+
_cmdwrite(0x98, 0, 0, 0x0, NULL);
71+
break;
72+
}
73+
74+
// Check that device is doing ok
75+
int err = _sync();
76+
if (err) {
77+
return BD_ERROR_DEVICE_ERROR;
78+
}
79+
80+
// Check JEDEC serial flash discoverable parameters for device
81+
// specific info
82+
uint8_t header[16];
83+
_cmdread(SPIF_SFDP, 4, 16, 0x0, header);
84+
85+
// Verify SFDP signature for sanity
86+
// Also check that major/minor version is acceptable
87+
if (!(memcmp(&header[0], "SFDP", 4) == 0 && header[5] == 1)) {
88+
return BD_ERROR_DEVICE_ERROR;
89+
}
90+
91+
// The SFDP spec indicates the standard table is always at offset 0
92+
// in the parameter headers, we check just to be safe
93+
if (!(header[8] == 0 && header[10] == 1)) {
94+
return BD_ERROR_DEVICE_ERROR;
95+
}
96+
97+
// Parameter table pointer, spi commands are BE, SFDP is LE,
98+
// also sfdp command expects extra read wait byte
99+
// header 12-14 3 bytes building the parameter table address
100+
uint32_t table_addr = (
101+
(header[14] << 24) |
102+
(header[13] << 16) |
103+
(header[12] << 8 ));
104+
105+
uint8_t table[8];
106+
_cmdread(SPIF_SFDP, 4, 8, table_addr, table);
107+
108+
// Check erase size, currently only supports 4kbytes
109+
if ((table[0] & 0x3) != 0x1 || table[1] != SPIF_SE) {
110+
// First byte of table, bits 0 and 1 = 0x1 indicating 4 KB Erase is supported
111+
// Second Byte of table = Sector Erase Command (0x20)
112+
return BD_ERROR_DEVICE_ERROR;
113+
}
114+
115+
// Check address size, currently only supports 3byte addresses
116+
if ((table[2] & 0x4) != 0 || (table[7] & 0x80) != 0) {
117+
return BD_ERROR_DEVICE_ERROR;
118+
}
119+
120+
// Get device density, stored as size in bits - 1
121+
uint32_t density = (
122+
(table[7] << 24) |
123+
(table[6] << 16) |
124+
(table[5] << 8 ) |
125+
(table[4] << 0 ));
126+
// Table bytes 5-8 : Bits 0|30 indicate Flash Density (size) in bits (divide by 8 for Bytes)
127+
_size = (density / 8) + 1;
128+
129+
return 0;
130+
}
131+
132+
int SPIFReducedBlockDevice::deinit()
133+
{
134+
// Latch write disable just to keep noise
135+
// from changing the device
136+
_cmdwrite(SPIF_WRDI, 0, 0, 0x0, NULL);
137+
138+
return 0;
139+
}
140+
141+
void SPIFReducedBlockDevice::_cmdread(
142+
uint8_t op, uint32_t addrc, uint32_t retc,
143+
uint32_t addr, uint8_t *rets)
144+
{
145+
_cs = 0;
146+
_spi.write(op);
147+
148+
for (uint32_t i = 0; i < addrc; i++) {
149+
_spi.write(0xff & (addr >> 8 * (addrc - 1 - i)));
150+
}
151+
152+
for (uint32_t i = 0; i < retc; i++) {
153+
rets[i] = _spi.write(0);
154+
}
155+
_cs = 1;
156+
157+
if (SPIF_DEBUG) {
158+
printf("spif <- %02x", op);
159+
for (uint32_t i = 0; i < addrc; i++) {
160+
if (i < addrc) {
161+
printf("%02lx", 0xff & (addr >> 8 * (addrc - 1 - i)));
162+
} else {
163+
printf(" ");
164+
}
165+
}
166+
printf(" ");
167+
for (uint32_t i = 0; i < 16 && i < retc; i++) {
168+
printf("%02x", rets[i]);
169+
}
170+
if (retc > 16) {
171+
printf("...");
172+
}
173+
printf("\n");
174+
}
175+
}
176+
177+
void SPIFReducedBlockDevice::_cmdwrite(
178+
uint8_t op, uint32_t addrc, uint32_t argc,
179+
uint32_t addr, const uint8_t *args)
180+
{
181+
_cs = 0;
182+
_spi.write(op);
183+
184+
for (uint32_t i = 0; i < addrc; i++) {
185+
_spi.write(0xff & (addr >> 8 * (addrc - 1 - i)));
186+
}
187+
188+
for (uint32_t i = 0; i < argc; i++) {
189+
_spi.write(args[i]);
190+
}
191+
_cs = 1;
192+
193+
if (SPIF_DEBUG) {
194+
printf("spif -> %02x", op);
195+
for (uint32_t i = 0; i < addrc; i++) {
196+
if (i < addrc) {
197+
printf("%02lx", 0xff & (addr >> 8 * (addrc - 1 - i)));
198+
} else {
199+
printf(" ");
200+
}
201+
}
202+
printf(" ");
203+
for (uint32_t i = 0; i < 16 && i < argc; i++) {
204+
printf("%02x", args[i]);
205+
}
206+
if (argc > 16) {
207+
printf("...");
208+
}
209+
printf("\n");
210+
}
211+
}
212+
213+
int SPIFReducedBlockDevice::_sync()
214+
{
215+
for (int i = 0; i < SPIF_TIMEOUT; i++) {
216+
// Read status register until write not-in-progress
217+
uint8_t status;
218+
_cmdread(SPIF_RDSR, 0, 1, 0x0, &status);
219+
220+
// Check WIP bit
221+
if (!(status & SPIF_WIP)) {
222+
return 0;
223+
}
224+
225+
wait_ms(1);
226+
}
227+
228+
return BD_ERROR_DEVICE_ERROR;
229+
}
230+
231+
int SPIFReducedBlockDevice::_wren()
232+
{
233+
_cmdwrite(SPIF_WREN, 0, 0, 0x0, NULL);
234+
235+
for (int i = 0; i < SPIF_TIMEOUT; i++) {
236+
// Read status register until write latch is enabled
237+
uint8_t status;
238+
_cmdread(SPIF_RDSR, 0, 1, 0x0, &status);
239+
240+
// Check WEL bit
241+
if (status & SPIF_WEL) {
242+
return 0;
243+
}
244+
245+
wait_ms(1);
246+
}
247+
248+
return BD_ERROR_DEVICE_ERROR;
249+
}
250+
251+
int SPIFReducedBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size)
252+
{
253+
// Check the address and size fit onto the chip.
254+
MBED_ASSERT(is_valid_read(addr, size));
255+
256+
_cmdread(SPIF_READ, 3, size, addr, static_cast<uint8_t *>(buffer));
257+
return 0;
258+
}
259+
260+
int SPIFReducedBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size)
261+
{
262+
// Check the address and size fit onto the chip.
263+
MBED_ASSERT(is_valid_program(addr, size));
264+
265+
while (size > 0) {
266+
int err = _wren();
267+
if (err) {
268+
return err;
269+
}
270+
271+
// Write up to 256 bytes a page
272+
uint32_t off = addr % 256;
273+
uint32_t chunk = (off + size < 256) ? size : (256 - off);
274+
_cmdwrite(SPIF_PROG, 3, chunk, addr, static_cast<const uint8_t *>(buffer));
275+
buffer = static_cast<const uint8_t *>(buffer) + chunk;
276+
addr += chunk;
277+
size -= chunk;
278+
279+
wait_ms(1);
280+
281+
err = _sync();
282+
if (err) {
283+
return err;
284+
}
285+
}
286+
287+
return 0;
288+
}
289+
290+
int SPIFReducedBlockDevice::erase(bd_addr_t addr, bd_size_t size)
291+
{
292+
// Check the address and size fit onto the chip.
293+
MBED_ASSERT(is_valid_erase(addr, size));
294+
295+
while (size > 0) {
296+
int err = _wren();
297+
if (err) {
298+
return err;
299+
}
300+
301+
// Erase 4kbyte sectors
302+
uint32_t chunk = 4096;
303+
_cmdwrite(SPIF_SE, 3, 0, addr, NULL);
304+
addr += chunk;
305+
size -= chunk;
306+
307+
err = _sync();
308+
if (err) {
309+
return err;
310+
}
311+
}
312+
313+
return 0;
314+
}
315+
316+
bd_size_t SPIFReducedBlockDevice::get_read_size() const
317+
{
318+
return SPIF_READ_SIZE;
319+
}
320+
321+
bd_size_t SPIFReducedBlockDevice::get_program_size() const
322+
{
323+
return SPIF_PROG_SIZE;
324+
}
325+
326+
bd_size_t SPIFReducedBlockDevice::get_erase_size() const
327+
{
328+
return SPIF_SE_SIZE;
329+
}
330+
331+
bd_size_t SPIFReducedBlockDevice::get_erase_size(bd_addr_t addr) const
332+
{
333+
return SPIF_SE_SIZE;
334+
}
335+
336+
int SPIFReducedBlockDevice::get_erase_value() const
337+
{
338+
return 0xFF;
339+
}
340+
341+
bd_size_t SPIFReducedBlockDevice::size() const
342+
{
343+
return _size;
344+
}

0 commit comments

Comments
 (0)