mirror of
https://github.com/PX4/PX4-Autopilot.git
synced 2026-07-24 15:27:41 +08:00
feat(mtd): add NOR FLASH support
This commit is contained in:
committed by
Alexander Lerach
parent
b5f69ac4e8
commit
6f1f7035a5
@@ -49,6 +49,7 @@ typedef struct {
|
||||
const px4_mtd_types_t type;
|
||||
const char *path;
|
||||
const uint32_t nblocks;
|
||||
const bool bypass_ftl;
|
||||
} px4_mtd_part_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -33,6 +33,10 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(__PX4_NUTTX)
|
||||
#include <nuttx/config.h>
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
MFT = 0,
|
||||
MTD = 1,
|
||||
@@ -49,9 +53,16 @@ typedef struct {
|
||||
I2C = 0,
|
||||
SPI = 1,
|
||||
ONCHIP = 2,
|
||||
FLEXSPI = 3
|
||||
FLEXSPI = 3,
|
||||
} bus_type;
|
||||
|
||||
enum px4_spi_driver {
|
||||
SPI_DRIVER_DEFAULT = 0, /* Ramtron (FRAM) */
|
||||
#if defined(CONFIG_MTD_MX25L)
|
||||
SPI_DRIVER_MX25L = 1, /* Macronix MX25L NOR flash */
|
||||
#endif
|
||||
} spi_driver;
|
||||
|
||||
uint32_t devid;
|
||||
} px4_mft_device_t;
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ typedef struct {
|
||||
int *partition_block_counts;
|
||||
int *partition_types;
|
||||
const char **partition_names;
|
||||
bool *partition_bypass_ftl;
|
||||
struct mtd_dev_s **part_dev;
|
||||
uint32_t devid;
|
||||
unsigned n_partitions_current;
|
||||
|
||||
@@ -60,8 +60,13 @@
|
||||
|
||||
extern "C" {
|
||||
struct mtd_dev_s *ramtron_initialize(FAR struct spi_dev_s *dev);
|
||||
#if defined(CONFIG_MTD_MX25L)
|
||||
struct mtd_dev_s *mx25l_initialize_spi(FAR struct spi_dev_s *dev);
|
||||
#endif
|
||||
struct mtd_dev_s *mtd_partition(FAR struct mtd_dev_s *mtd,
|
||||
off_t firstblock, off_t nblocks);
|
||||
int register_mtddriver(FAR const char *path, FAR struct mtd_dev_s *mtd,
|
||||
unsigned int mode, FAR void *priv);
|
||||
}
|
||||
static int num_instances = 0;
|
||||
static int total_blocks = 0;
|
||||
@@ -131,6 +136,34 @@ static int ramtron_attach(mtd_instance_s &instance)
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(CONFIG_SPI)
|
||||
template<struct mtd_dev_s *(*Initialize)(struct spi_dev_s *)>
|
||||
static int spi_nor_attach(mtd_instance_s &instance, uint32_t spi_speed_hz)
|
||||
{
|
||||
struct spi_dev_s *spi = px4_spibus_initialize(px4_find_spi_bus(instance.devid));
|
||||
|
||||
if (spi == nullptr) {
|
||||
PX4_ERR("failed to locate spi bus");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
SPI_LOCK(spi, true);
|
||||
SPI_SETFREQUENCY(spi, spi_speed_hz);
|
||||
SPI_SETBITS(spi, 8);
|
||||
SPI_SETMODE(spi, SPIDEV_MODE0);
|
||||
SPI_SELECT(spi, instance.devid, false);
|
||||
SPI_LOCK(spi, false);
|
||||
|
||||
instance.mtd_dev = Initialize(spi);
|
||||
|
||||
if (instance.mtd_dev == nullptr) {
|
||||
PX4_ERR("failed to initialize NOR flash driver");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int at24xxx_attach(mtd_instance_s &instance)
|
||||
{
|
||||
@@ -340,17 +373,47 @@ memoryout:
|
||||
goto memoryout;
|
||||
}
|
||||
|
||||
instances[i]->partition_bypass_ftl = new bool[nparts];
|
||||
|
||||
if (instances[i]->partition_bypass_ftl == nullptr) {
|
||||
goto memoryout;
|
||||
}
|
||||
|
||||
for (uint32_t p = 0; p < nparts; p++) {
|
||||
instances[i]->partition_block_counts[p] = mtd_list->entries[num_entry]->partd[p].nblocks;
|
||||
instances[i]->partition_names[p] = mtd_list->entries[num_entry]->partd[p].path;
|
||||
instances[i]->partition_types[p] = mtd_list->entries[num_entry]->partd[p].type;
|
||||
instances[i]->partition_bypass_ftl[p] = mtd_list->entries[num_entry]->partd[p].bypass_ftl;
|
||||
}
|
||||
|
||||
unsigned long blocksize;
|
||||
unsigned long erasesize;
|
||||
unsigned long neraseblocks;
|
||||
unsigned int blkpererase;
|
||||
unsigned int nblocks;
|
||||
unsigned int partsize;
|
||||
char blockname[32];
|
||||
unsigned long offset;
|
||||
unsigned part;
|
||||
|
||||
if (mtd_list->entries[num_entry]->device->bus_type == px4_mft_device_t::I2C) {
|
||||
rv = at24xxx_attach(*instances[i]);
|
||||
|
||||
} else if (mtd_list->entries[num_entry]->device->bus_type == px4_mft_device_t::SPI) {
|
||||
rv = ramtron_attach(*instances[i]);
|
||||
switch (mtd_list->entries[num_entry]->device->spi_driver) {
|
||||
#if defined(CONFIG_MTD_MX25L)
|
||||
|
||||
case px4_mft_device_t::SPI_DRIVER_MX25L:
|
||||
rv = spi_nor_attach<mx25l_initialize_spi>(*instances[i], CONFIG_MX25L_SPIFREQUENCY);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case px4_mft_device_t::SPI_DRIVER_DEFAULT:
|
||||
default:
|
||||
rv = ramtron_attach(*instances[i]);
|
||||
break;
|
||||
}
|
||||
|
||||
#if defined(HAS_FLEXSPI)
|
||||
|
||||
} else if (mtd_list->entries[num_entry]->device->bus_type == px4_mft_device_t::FLEXSPI) {
|
||||
@@ -370,13 +433,6 @@ memoryout:
|
||||
goto errout;
|
||||
}
|
||||
|
||||
unsigned long blocksize;
|
||||
unsigned long erasesize;
|
||||
unsigned long neraseblocks;
|
||||
unsigned int blkpererase;
|
||||
unsigned int nblocks;
|
||||
unsigned int partsize;
|
||||
|
||||
rv = px4_mtd_get_geometry(instances[i], &blocksize, &erasesize, &neraseblocks, &blkpererase, &nblocks, &partsize);
|
||||
|
||||
if (rv != 0) {
|
||||
@@ -385,11 +441,6 @@ memoryout:
|
||||
|
||||
/* Now create MTD FLASH partitions */
|
||||
|
||||
char blockname[32];
|
||||
|
||||
unsigned long offset;
|
||||
unsigned part;
|
||||
|
||||
for (offset = 0, part = 0; rv == 0 && part < nparts; offset += instances[i]->partition_block_counts[part], part++) {
|
||||
|
||||
/* Create the partition */
|
||||
@@ -403,26 +454,43 @@ memoryout:
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/* Initialize to provide an FTL block driver on the MTD FLASH interface */
|
||||
if (instances[i]->partition_bypass_ftl[part]) {
|
||||
/* When requested: register MTD device directly so the filesystem can drive
|
||||
* erase blocks natively, bypassing the FTL. Only works for filesystem that
|
||||
* can directly work with MTD devices */
|
||||
#if !defined(CONFIG_BCH)
|
||||
PX4_WARN("%s bypasses the FTL but CONFIG_BCH is not set: mtd commands will fail!",
|
||||
instances[i]->partition_names[part]);
|
||||
#endif
|
||||
rv = register_mtddriver(instances[i]->partition_names[part], instances[i]->part_dev[part], 0755, NULL);
|
||||
|
||||
snprintf(blockname, sizeof(blockname), "/dev/mtdblock%d", total_blocks);
|
||||
if (rv < 0) {
|
||||
PX4_ERR("register_mtddriver %s failed: %d", instances[i]->partition_names[part], rv);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
rv = ftl_initialize(total_blocks, instances[i]->part_dev[part]);
|
||||
} else {
|
||||
/* Initialize to provide an FTL block driver on the MTD FLASH interface */
|
||||
|
||||
if (rv < 0) {
|
||||
PX4_ERR("ftl_initialize %s failed: %d", blockname, rv);
|
||||
goto errout;
|
||||
}
|
||||
snprintf(blockname, sizeof(blockname), "/dev/mtdblock%d", total_blocks);
|
||||
|
||||
total_blocks++;
|
||||
rv = ftl_initialize(total_blocks, instances[i]->part_dev[part]);
|
||||
|
||||
/* Now create a character device on the block device */
|
||||
if (rv < 0) {
|
||||
PX4_ERR("ftl_initialize %s failed: %d", blockname, rv);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
rv = bchdev_register(blockname, instances[i]->partition_names[part], false);
|
||||
total_blocks++;
|
||||
|
||||
if (rv < 0) {
|
||||
PX4_ERR("bchdev_register %s failed: %d", instances[i]->partition_names[part], rv);
|
||||
goto errout;
|
||||
/* Now create a character device on the block device */
|
||||
|
||||
rv = bchdev_register(blockname, instances[i]->partition_names[part], false);
|
||||
|
||||
if (rv < 0) {
|
||||
PX4_ERR("bchdev_register %s failed: %d", instances[i]->partition_names[part], rv);
|
||||
goto errout;
|
||||
}
|
||||
}
|
||||
|
||||
instances[i]->n_partitions_current++;
|
||||
|
||||
@@ -228,6 +228,54 @@ int mtd_readtest(const mtd_instance_s &instance)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* rwtest for bypass_ftl: talk to the MTD partition directly without BCH to avoid overhead. */
|
||||
static ssize_t mtd_part_brwtest(FAR struct mtd_dev_s *part, ssize_t expected_size)
|
||||
{
|
||||
FAR struct mtd_geometry_s geo;
|
||||
|
||||
if (MTD_IOCTL(part, MTDIOC_GEOMETRY, (unsigned long)((uintptr_t)&geo)) < 0) {
|
||||
PX4_ERR("Failed to get partition geometry");
|
||||
return -1;
|
||||
}
|
||||
|
||||
FAR uint8_t *v = new uint8_t[geo.blocksize];
|
||||
FAR uint8_t *v2 = new uint8_t[geo.blocksize];
|
||||
off_t nblocks = expected_size / geo.blocksize;
|
||||
ssize_t count = 0;
|
||||
|
||||
for (off_t block = 0; block < nblocks; block++) {
|
||||
if (MTD_BREAD(part, block, 1, v) != 1) {
|
||||
PX4_ERR("bread failed at block %ld", (long)block);
|
||||
count = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (MTD_BWRITE(part, block, 1, v) != 1) {
|
||||
PX4_ERR("bwrite failed at block %ld", (long)block);
|
||||
count = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (MTD_BREAD(part, block, 1, v2) != 1) {
|
||||
PX4_ERR("bread failed at block %ld", (long)block);
|
||||
count = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (memcmp(v, v2, geo.blocksize) != 0) {
|
||||
PX4_ERR("memcmp failed at block %ld", (long)block);
|
||||
count = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
count += geo.blocksize;
|
||||
}
|
||||
|
||||
delete[] v;
|
||||
delete[] v2;
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
rwtest is useful during startup to validate the device is
|
||||
responding on the bus for both reads and writes. It reads data in
|
||||
@@ -239,9 +287,6 @@ int mtd_rwtest(const mtd_instance_s &instance)
|
||||
uint8_t v[32], v2[32];
|
||||
|
||||
for (uint8_t i = 0; i < instance.n_partitions_current; i++) {
|
||||
ssize_t count = 0;
|
||||
off_t offset = 0;
|
||||
|
||||
ssize_t expected_size = px4_mtd_get_partition_size(&instance, instance.partition_names[i]);
|
||||
|
||||
if (expected_size == 0) {
|
||||
@@ -251,88 +296,98 @@ int mtd_rwtest(const mtd_instance_s &instance)
|
||||
|
||||
printf("rwtest %s testing %zd bytes\n", instance.partition_names[i], expected_size);
|
||||
|
||||
bool run = true;
|
||||
|
||||
while (run) {
|
||||
|
||||
int fd = open(instance.partition_names[i], O_RDWR);
|
||||
|
||||
if (fd == -1) {
|
||||
PX4_ERR("Failed to open partition");
|
||||
if (instance.partition_bypass_ftl[i]) {
|
||||
if (mtd_part_brwtest(instance.part_dev[i], expected_size) != expected_size) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (read(fd, v, sizeof(v)) != sizeof(v)) {
|
||||
PX4_ERR("read failed");
|
||||
close(fd);
|
||||
} else {
|
||||
ssize_t count = 0;
|
||||
off_t offset = 0;
|
||||
|
||||
bool run = true;
|
||||
|
||||
while (run) {
|
||||
|
||||
int fd = open(instance.partition_names[i], O_RDWR);
|
||||
|
||||
if (fd == -1) {
|
||||
PX4_ERR("Failed to open partition");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (read(fd, v, sizeof(v)) != sizeof(v)) {
|
||||
PX4_ERR("read failed");
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
count += sizeof(v);
|
||||
|
||||
if (lseek(fd, offset, SEEK_SET) != offset) {
|
||||
PX4_ERR("seek failed");
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (write(fd, v, sizeof(v)) != sizeof(v)) {
|
||||
PX4_ERR("write failed");
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
//sync and close to discard data from the Block Device buffer
|
||||
if (OK != fsync(fd)) {
|
||||
PX4_ERR("Failed to fsync");
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (OK != close(fd)) {
|
||||
PX4_ERR("Failed to close partition");
|
||||
return 1;
|
||||
}
|
||||
|
||||
fd = open(instance.partition_names[i], O_RDONLY);
|
||||
|
||||
if (fd == -1) {
|
||||
PX4_ERR("Failed to open partition");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (lseek(fd, offset, SEEK_SET) != offset) {
|
||||
PX4_ERR("seek failed");
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (read(fd, v2, sizeof(v2)) != sizeof(v2)) {
|
||||
PX4_ERR("read failed");
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (OK != close(fd)) {
|
||||
PX4_ERR("Failed to close partition");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (memcmp(v, v2, sizeof(v2)) != 0) {
|
||||
PX4_ERR("memcmp failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
offset += sizeof(v);
|
||||
|
||||
if (count >= expected_size) {
|
||||
run = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (count != expected_size) {
|
||||
PX4_ERR("Failed to read partition - got %zd/%zd bytes", count, expected_size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
count += sizeof(v);
|
||||
|
||||
if (lseek(fd, offset, SEEK_SET) != offset) {
|
||||
PX4_ERR("seek failed");
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (write(fd, v, sizeof(v)) != sizeof(v)) {
|
||||
PX4_ERR("write failed");
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
//sync and close to discard data from the Block Device buffer
|
||||
if (OK != fsync(fd)) {
|
||||
PX4_ERR("Failed to fsync");
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (OK != close(fd)) {
|
||||
PX4_ERR("Failed to close partition");
|
||||
return 1;
|
||||
}
|
||||
|
||||
fd = open(instance.partition_names[i], O_RDONLY);
|
||||
|
||||
if (fd == -1) {
|
||||
PX4_ERR("Failed to open partition");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (lseek(fd, offset, SEEK_SET) != offset) {
|
||||
PX4_ERR("seek failed");
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (read(fd, v2, sizeof(v2)) != sizeof(v2)) {
|
||||
PX4_ERR("read failed");
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (OK != close(fd)) {
|
||||
PX4_ERR("Failed to close partition");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (memcmp(v, v2, sizeof(v2)) != 0) {
|
||||
PX4_ERR("memcmp failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
offset += sizeof(v);
|
||||
|
||||
if (count >= expected_size) {
|
||||
run = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (count != expected_size) {
|
||||
PX4_ERR("Failed to read partition - got %zd/%zd bytes", count, expected_size);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user