mirror of https://github.com/ARMmbed/mbed-os.git
Merge pull request #8349 from offirko/offir-mbed-reduced-spif
A Reduced SPIF Block Device for Boot Loaderpull/8464/head
commit
515db95061
|
@ -0,0 +1,344 @@
|
|||
|
||||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2018 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include "SPIFReducedBlockDevice.h"
|
||||
#include "mbed_wait_api.h"
|
||||
|
||||
// Read/write/erase sizes
|
||||
#define SPIF_READ_SIZE 1
|
||||
#define SPIF_PROG_SIZE 1
|
||||
#define SPIF_SE_SIZE 4096
|
||||
#define SPIF_TIMEOUT 10000
|
||||
|
||||
// Debug available
|
||||
#define SPIF_DEBUG 0
|
||||
|
||||
// Legacy SFDP Instruction Table.
|
||||
enum ops {
|
||||
SPIF_NOP = 0x00, // No operation
|
||||
SPIF_READ = 0x03, // Read data
|
||||
SPIF_PROG = 0x02, // Program data
|
||||
SPIF_SE = 0x20, // 4KB Sector Erase
|
||||
SPIF_CE = 0xc7, // Chip Erase
|
||||
SPIF_SFDP = 0x5a, // Read SFDP
|
||||
SPIF_WREN = 0x06, // Write Enable
|
||||
SPIF_WRDI = 0x04, // Write Disable
|
||||
SPIF_RDSR = 0x05, // Read Status Register
|
||||
SPIF_RDID = 0x9f, // Read Manufacturer and JDEC Device ID
|
||||
};
|
||||
|
||||
// Status register from RDSR
|
||||
// [---------| wel | wip ]
|
||||
// [- 6 -| 1 | 1 ]
|
||||
#define SPIF_WEL 0x2
|
||||
#define SPIF_WIP 0x1
|
||||
|
||||
|
||||
SPIFReducedBlockDevice::SPIFReducedBlockDevice(
|
||||
PinName mosi, PinName miso, PinName sclk, PinName cs, int freq)
|
||||
: _spi(mosi, miso, sclk), _cs(cs), _size(0)
|
||||
{
|
||||
_cs = 1;
|
||||
_spi.frequency(freq);
|
||||
}
|
||||
|
||||
int SPIFReducedBlockDevice::init()
|
||||
{
|
||||
// Check for vendor specific hacks, these should move into more general
|
||||
// handling when possible. RDID is not used to verify a device is attached.
|
||||
uint8_t id[3];
|
||||
_cmdread(SPIF_RDID, 0, 3, 0x0, id);
|
||||
|
||||
switch (id[0]) {
|
||||
case 0xbf:
|
||||
// SST devices come preset with block protection
|
||||
// enabled for some regions, issue gbpu instruction to clear
|
||||
_wren();
|
||||
_cmdwrite(0x98, 0, 0, 0x0, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
// Check that device is doing ok
|
||||
int err = _sync();
|
||||
if (err) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
// Check JEDEC serial flash discoverable parameters for device
|
||||
// specific info
|
||||
uint8_t header[16];
|
||||
_cmdread(SPIF_SFDP, 4, 16, 0x0, header);
|
||||
|
||||
// Verify SFDP signature for sanity
|
||||
// Also check that major/minor version is acceptable
|
||||
if (!(memcmp(&header[0], "SFDP", 4) == 0 && header[5] == 1)) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
// The SFDP spec indicates the standard table is always at offset 0
|
||||
// in the parameter headers, we check just to be safe
|
||||
if (!(header[8] == 0 && header[10] == 1)) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
// Parameter table pointer, spi commands are BE, SFDP is LE,
|
||||
// also sfdp command expects extra read wait byte
|
||||
// header 12-14 3 bytes building the parameter table address
|
||||
uint32_t table_addr = (
|
||||
(header[14] << 24) |
|
||||
(header[13] << 16) |
|
||||
(header[12] << 8 ));
|
||||
|
||||
uint8_t table[8];
|
||||
_cmdread(SPIF_SFDP, 4, 8, table_addr, table);
|
||||
|
||||
// Check erase size, currently only supports 4kbytes
|
||||
if ((table[0] & 0x3) != 0x1 || table[1] != SPIF_SE) {
|
||||
// First byte of table, bits 0 and 1 = 0x1 indicating 4 KB Erase is supported
|
||||
// Second Byte of table = Sector Erase Command (0x20)
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
// Check address size, currently only supports 3byte addresses
|
||||
if ((table[2] & 0x4) != 0 || (table[7] & 0x80) != 0) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
// Get device density, stored as size in bits - 1
|
||||
uint32_t density = (
|
||||
(table[7] << 24) |
|
||||
(table[6] << 16) |
|
||||
(table[5] << 8 ) |
|
||||
(table[4] << 0 ));
|
||||
// Table bytes 5-8 : Bits 0|30 indicate Flash Density (size) in bits (divide by 8 for Bytes)
|
||||
_size = (density / 8) + 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SPIFReducedBlockDevice::deinit()
|
||||
{
|
||||
// Latch write disable just to keep noise
|
||||
// from changing the device
|
||||
_cmdwrite(SPIF_WRDI, 0, 0, 0x0, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SPIFReducedBlockDevice::_cmdread(
|
||||
uint8_t op, uint32_t addrc, uint32_t retc,
|
||||
uint32_t addr, uint8_t *rets)
|
||||
{
|
||||
_cs = 0;
|
||||
_spi.write(op);
|
||||
|
||||
for (uint32_t i = 0; i < addrc; i++) {
|
||||
_spi.write(0xff & (addr >> 8 * (addrc - 1 - i)));
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < retc; i++) {
|
||||
rets[i] = _spi.write(0);
|
||||
}
|
||||
_cs = 1;
|
||||
|
||||
if (SPIF_DEBUG) {
|
||||
printf("spif <- %02x", op);
|
||||
for (uint32_t i = 0; i < addrc; i++) {
|
||||
if (i < addrc) {
|
||||
printf("%02lx", 0xff & (addr >> 8 * (addrc - 1 - i)));
|
||||
} else {
|
||||
printf(" ");
|
||||
}
|
||||
}
|
||||
printf(" ");
|
||||
for (uint32_t i = 0; i < 16 && i < retc; i++) {
|
||||
printf("%02x", rets[i]);
|
||||
}
|
||||
if (retc > 16) {
|
||||
printf("...");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void SPIFReducedBlockDevice::_cmdwrite(
|
||||
uint8_t op, uint32_t addrc, uint32_t argc,
|
||||
uint32_t addr, const uint8_t *args)
|
||||
{
|
||||
_cs = 0;
|
||||
_spi.write(op);
|
||||
|
||||
for (uint32_t i = 0; i < addrc; i++) {
|
||||
_spi.write(0xff & (addr >> 8 * (addrc - 1 - i)));
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < argc; i++) {
|
||||
_spi.write(args[i]);
|
||||
}
|
||||
_cs = 1;
|
||||
|
||||
if (SPIF_DEBUG) {
|
||||
printf("spif -> %02x", op);
|
||||
for (uint32_t i = 0; i < addrc; i++) {
|
||||
if (i < addrc) {
|
||||
printf("%02lx", 0xff & (addr >> 8 * (addrc - 1 - i)));
|
||||
} else {
|
||||
printf(" ");
|
||||
}
|
||||
}
|
||||
printf(" ");
|
||||
for (uint32_t i = 0; i < 16 && i < argc; i++) {
|
||||
printf("%02x", args[i]);
|
||||
}
|
||||
if (argc > 16) {
|
||||
printf("...");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
int SPIFReducedBlockDevice::_sync()
|
||||
{
|
||||
for (int i = 0; i < SPIF_TIMEOUT; i++) {
|
||||
// Read status register until write not-in-progress
|
||||
uint8_t status;
|
||||
_cmdread(SPIF_RDSR, 0, 1, 0x0, &status);
|
||||
|
||||
// Check WIP bit
|
||||
if (!(status & SPIF_WIP)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
wait_ms(1);
|
||||
}
|
||||
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
int SPIFReducedBlockDevice::_wren()
|
||||
{
|
||||
_cmdwrite(SPIF_WREN, 0, 0, 0x0, NULL);
|
||||
|
||||
for (int i = 0; i < SPIF_TIMEOUT; i++) {
|
||||
// Read status register until write latch is enabled
|
||||
uint8_t status;
|
||||
_cmdread(SPIF_RDSR, 0, 1, 0x0, &status);
|
||||
|
||||
// Check WEL bit
|
||||
if (status & SPIF_WEL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
wait_ms(1);
|
||||
}
|
||||
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
int SPIFReducedBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size)
|
||||
{
|
||||
// Check the address and size fit onto the chip.
|
||||
MBED_ASSERT(is_valid_read(addr, size));
|
||||
|
||||
_cmdread(SPIF_READ, 3, size, addr, static_cast<uint8_t *>(buffer));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SPIFReducedBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size)
|
||||
{
|
||||
// Check the address and size fit onto the chip.
|
||||
MBED_ASSERT(is_valid_program(addr, size));
|
||||
|
||||
while (size > 0) {
|
||||
int err = _wren();
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// Write up to 256 bytes a page
|
||||
uint32_t off = addr % 256;
|
||||
uint32_t chunk = (off + size < 256) ? size : (256 - off);
|
||||
_cmdwrite(SPIF_PROG, 3, chunk, addr, static_cast<const uint8_t *>(buffer));
|
||||
buffer = static_cast<const uint8_t *>(buffer) + chunk;
|
||||
addr += chunk;
|
||||
size -= chunk;
|
||||
|
||||
wait_ms(1);
|
||||
|
||||
err = _sync();
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SPIFReducedBlockDevice::erase(bd_addr_t addr, bd_size_t size)
|
||||
{
|
||||
// Check the address and size fit onto the chip.
|
||||
MBED_ASSERT(is_valid_erase(addr, size));
|
||||
|
||||
while (size > 0) {
|
||||
int err = _wren();
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// Erase 4kbyte sectors
|
||||
uint32_t chunk = 4096;
|
||||
_cmdwrite(SPIF_SE, 3, 0, addr, NULL);
|
||||
addr += chunk;
|
||||
size -= chunk;
|
||||
|
||||
err = _sync();
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bd_size_t SPIFReducedBlockDevice::get_read_size() const
|
||||
{
|
||||
return SPIF_READ_SIZE;
|
||||
}
|
||||
|
||||
bd_size_t SPIFReducedBlockDevice::get_program_size() const
|
||||
{
|
||||
return SPIF_PROG_SIZE;
|
||||
}
|
||||
|
||||
bd_size_t SPIFReducedBlockDevice::get_erase_size() const
|
||||
{
|
||||
return SPIF_SE_SIZE;
|
||||
}
|
||||
|
||||
bd_size_t SPIFReducedBlockDevice::get_erase_size(bd_addr_t addr) const
|
||||
{
|
||||
return SPIF_SE_SIZE;
|
||||
}
|
||||
|
||||
int SPIFReducedBlockDevice::get_erase_value() const
|
||||
{
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
bd_size_t SPIFReducedBlockDevice::size() const
|
||||
{
|
||||
return _size;
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2018 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#ifndef MBED_RSPIF_BLOCK_DEVICE_H
|
||||
#define MBED_RSPIF_BLOCK_DEVICE_H
|
||||
|
||||
#include "SPI.h"
|
||||
#include "DigitalOut.h"
|
||||
#include "BlockDevice.h"
|
||||
|
||||
/** Reduced BlockDevice for SPI based flash devices
|
||||
* *Should only be used by Boot Loader*
|
||||
*
|
||||
* @code
|
||||
* // Here's an example using the SPI flash device on K82F
|
||||
* #include "mbed.h"
|
||||
* #include "SPIFReducedBlockDevice.h"
|
||||
*
|
||||
* // Create flash device on SPI bus with PTE5 as chip select
|
||||
* SPIFReducedBlockDevice rspif(PTE2, PTE4, PTE1, PTE5);
|
||||
*
|
||||
* int main() {
|
||||
* printf("reduced spif test\n");
|
||||
*
|
||||
* // Initialize the Reduced SPI flash device and print the memory layout
|
||||
* rspif.init();
|
||||
* printf("rspif size: %llu\n", rspif.size());
|
||||
* printf("rspif read size: %llu\n", rspif.get_read_size());
|
||||
* printf("rspif program size: %llu\n", rspif.get_program_size());
|
||||
* printf("rspif erase size: %llu\n", rspif.get_erase_size());
|
||||
*
|
||||
* // Write "Hello World!" to the first block
|
||||
* char *buffer = (char*)malloc(rspif.get_erase_size());
|
||||
* sprintf(buffer, "Hello World!\n");
|
||||
* rspif.erase(0, rspif.get_erase_size());
|
||||
* rspif.program(buffer, 0, rspif.get_erase_size());
|
||||
*
|
||||
* // Read back what was stored
|
||||
* rspif.read(buffer, 0, rspif.get_erase_size());
|
||||
* printf("%s", buffer);
|
||||
*
|
||||
* // Deinitialize the device
|
||||
* rspif.deinit();
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
class SPIFReducedBlockDevice : public BlockDevice {
|
||||
public:
|
||||
/** Creates a SPIFReducedBlockDevice on a SPI bus specified by pins
|
||||
*
|
||||
* @param mosi SPI master out, slave in pin
|
||||
* @param miso SPI master in, slave out pin
|
||||
* @param sclk SPI clock pin
|
||||
* @param csel SPI chip select pin
|
||||
* @param freq Clock speed of the SPI bus (defaults to 40MHz)
|
||||
*/
|
||||
SPIFReducedBlockDevice(PinName mosi, PinName miso, PinName sclk, PinName csel, int freq = 40000000);
|
||||
|
||||
/** Initialize a block device
|
||||
*
|
||||
* @return 0 on success or a negative error code on failure
|
||||
*/
|
||||
virtual int init();
|
||||
|
||||
/** Deinitialize a block device
|
||||
*
|
||||
* @return 0 on success or a negative error code on failure
|
||||
*/
|
||||
virtual int deinit();
|
||||
|
||||
/** Read blocks from a block device
|
||||
*
|
||||
* @param buffer Buffer to write blocks to
|
||||
* @param addr Address of block to begin reading from
|
||||
* @param size Size to read in bytes, must be a multiple of read block size
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int read(void *buffer, bd_addr_t addr, bd_size_t size);
|
||||
|
||||
/** Program blocks to a block device
|
||||
*
|
||||
* The blocks must have been erased prior to being programmed
|
||||
*
|
||||
* @param buffer Buffer of data to write to blocks
|
||||
* @param addr Address of block to begin writing to
|
||||
* @param size Size to write in bytes, must be a multiple of program block size
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int program(const void *buffer, bd_addr_t addr, bd_size_t size);
|
||||
|
||||
/** Erase blocks on a block device
|
||||
*
|
||||
* The state of an erased block is undefined until it has been programmed
|
||||
*
|
||||
* @param addr Address of block to begin erasing
|
||||
* @param size Size to erase in bytes, must be a multiple of erase block size
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int erase(bd_addr_t addr, bd_size_t size);
|
||||
|
||||
/** Get the size of a readable block
|
||||
*
|
||||
* @return Size of a readable block in bytes
|
||||
*/
|
||||
virtual bd_size_t get_read_size() const;
|
||||
|
||||
/** Get the size of a programable block
|
||||
*
|
||||
* @return Size of a programable block in bytes
|
||||
* @note Must be a multiple of the read size
|
||||
*/
|
||||
virtual bd_size_t get_program_size() const;
|
||||
|
||||
/** Get the size of a eraseable block
|
||||
*
|
||||
* @return Size of a eraseable block in bytes
|
||||
* @note Must be a multiple of the program size
|
||||
*/
|
||||
virtual bd_size_t get_erase_size() const;
|
||||
|
||||
/** Get the size of a eraseable block
|
||||
*
|
||||
* @param addr Address of block to query erase size
|
||||
* @return Size of a eraseable block in bytes
|
||||
* @note Must be a multiple of the program size
|
||||
*/
|
||||
virtual bd_size_t get_erase_size(bd_addr_t addr) const;
|
||||
|
||||
/** Get the value of storage byte after it was erased
|
||||
*
|
||||
* If get_erase_value returns a non-negative byte value, the underlying
|
||||
* storage is set to that value when erased, and storage containing
|
||||
* that value can be programmed without another erase.
|
||||
*
|
||||
* @return The value of storage when erased, or -1 if you can't
|
||||
* rely on the value of erased storage
|
||||
*/
|
||||
virtual int get_erase_value() const;
|
||||
|
||||
/** Get the total size of the underlying device
|
||||
*
|
||||
* @return Size of the underlying device in bytes
|
||||
*/
|
||||
virtual bd_size_t size() const;
|
||||
|
||||
private:
|
||||
// Master side hardware
|
||||
mbed::SPI _spi;
|
||||
mbed::DigitalOut _cs;
|
||||
|
||||
// Device configuration discovered through sfdp
|
||||
bd_size_t _size;
|
||||
|
||||
// Internal functions
|
||||
int _wren();
|
||||
int _sync();
|
||||
void _cmdread(uint8_t op, uint32_t addrc, uint32_t retc,
|
||||
uint32_t addr, uint8_t *rets);
|
||||
void _cmdwrite(uint8_t op, uint32_t addrc, uint32_t argc,
|
||||
uint32_t addr, const uint8_t *args);
|
||||
};
|
||||
#endif
|
|
@ -0,0 +1,66 @@
|
|||
{
|
||||
"name": "rspif-driver",
|
||||
"config": {
|
||||
"SPI_MOSI": "NC",
|
||||
"SPI_MISO": "NC",
|
||||
"SPI_CLK": "NC",
|
||||
"SPI_CS": "NC",
|
||||
"SPI_FREQ": "40000000"
|
||||
},
|
||||
"target_overrides": {
|
||||
"K82F": {
|
||||
"SPI_MOSI": "PTE2",
|
||||
"SPI_MISO": "PTE4",
|
||||
"SPI_CLK": "PTE1",
|
||||
"SPI_CS": "PTE5"
|
||||
},
|
||||
"LPC54114": {
|
||||
"SPI_MOSI": "P0_20",
|
||||
"SPI_MISO": "P0_18",
|
||||
"SPI_CLK": "P0_19",
|
||||
"SPI_CS": "P1_2"
|
||||
},
|
||||
"NRF52840_DK": {
|
||||
"SPI_MOSI": "p20",
|
||||
"SPI_MISO": "p21",
|
||||
"SPI_CLK": "p19",
|
||||
"SPI_CS": "p17"
|
||||
},
|
||||
"HEXIWEAR": {
|
||||
"SPI_MOSI": "PTD6",
|
||||
"SPI_MISO": "PTD7",
|
||||
"SPI_CLK": "PTD5",
|
||||
"SPI_CS": "PTD4"
|
||||
},
|
||||
"MTB_UBLOX_ODIN_W2": {
|
||||
"SPI_MOSI": "PE_14",
|
||||
"SPI_MISO": "PE_13",
|
||||
"SPI_CLK": "PE_12",
|
||||
"SPI_CS": "PE_11"
|
||||
},
|
||||
"MTB_ADV_WISE_1530": {
|
||||
"SPI_MOSI": "PC_3",
|
||||
"SPI_MISO": "PC_2",
|
||||
"SPI_CLK": "PB_13",
|
||||
"SPI_CS": "PC_12"
|
||||
},
|
||||
"MTB_MXCHIP_EMW3166": {
|
||||
"SPI_MOSI": "PB_15",
|
||||
"SPI_MISO": "PB_14",
|
||||
"SPI_CLK": "PB_13",
|
||||
"SPI_CS": "PA_10"
|
||||
},
|
||||
"MTB_USI_WM_BN_BM_22": {
|
||||
"SPI_MOSI": "PC_3",
|
||||
"SPI_MISO": "PC_2",
|
||||
"SPI_CLK": "PB_13",
|
||||
"SPI_CS": "PA_6"
|
||||
},
|
||||
"MTB_ADV_WISE_1570": {
|
||||
"SPI_MOSI": "PA_7",
|
||||
"SPI_MISO": "PA_6",
|
||||
"SPI_CLK": "PA_5",
|
||||
"SPI_CS": "PB_12"
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue