mirror of
https://bitbucket.org/librepilot/librepilot.git
synced 2025-03-15 07:29:15 +01:00
REVONANO - Add Eeprom support
This commit is contained in:
parent
764a15999f
commit
1e1195ec05
489
flight/pios/common/pios_flash_eeprom.c
Normal file
489
flight/pios/common/pios_flash_eeprom.c
Normal file
@ -0,0 +1,489 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
*
|
||||
* @file pios_flash_eeprom.c
|
||||
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2014.
|
||||
* @addtogroup PIOS PIOS Core hardware abstraction layer
|
||||
* @{
|
||||
* @addtogroup PIOS_FLASH EEPROM Driver API Definition
|
||||
* @{
|
||||
* @brief EEPROM Driver API Definition
|
||||
* @see The GNU Public License (GPL) Version 3
|
||||
*
|
||||
*****************************************************************************/
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
|
||||
#include <pios.h>
|
||||
|
||||
#ifdef PIOS_INCLUDE_FLASH_EEPROM
|
||||
|
||||
#include <pios_flash_eeprom.h>
|
||||
#include <pios_math.h>
|
||||
enum pios_eeprom_dev_magic {
|
||||
PIOS_EEPROM_DEV_MAGIC = 0xEE55aa55,
|
||||
};
|
||||
|
||||
// ! Device handle structure
|
||||
struct flash_eeprom_dev {
|
||||
enum pios_eeprom_dev_magic magic;
|
||||
uint32_t i2c_adapter;
|
||||
uint32_t i2c_address;
|
||||
const struct pios_flash_eeprom_cfg *cfg;
|
||||
#ifdef PIOS_INCLUDE_FREERTOS
|
||||
xSemaphoreHandle transaction_lock;
|
||||
#endif
|
||||
};
|
||||
|
||||
// ! Private functions
|
||||
static int32_t PIOS_Flash_EEPROM_Validate(struct flash_eeprom_dev *flash_dev);
|
||||
static struct flash_eeprom_dev *PIOS_Flash_EEPROM_alloc(void);
|
||||
|
||||
static int32_t PIOS_Flash_EEPROM_Busy(struct flash_eeprom_dev *flash_dev);
|
||||
|
||||
/**
|
||||
* @brief Allocate a new device
|
||||
*/
|
||||
static struct flash_eeprom_dev *PIOS_Flash_EEPROM_alloc(void)
|
||||
{
|
||||
struct flash_eeprom_dev *flash_dev;
|
||||
|
||||
flash_dev = (struct flash_eeprom_dev *)pios_malloc(sizeof(*flash_dev));
|
||||
if (!flash_dev) {
|
||||
return NULL;
|
||||
}
|
||||
flash_dev->magic = PIOS_EEPROM_DEV_MAGIC;
|
||||
#ifdef PIOS_INCLUDE_FREERTOS
|
||||
flash_dev->transaction_lock = xSemaphoreCreateMutex();
|
||||
#endif
|
||||
return flash_dev;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate the handler to the device
|
||||
*/
|
||||
static int32_t PIOS_Flash_EEPROM_Validate(struct flash_eeprom_dev *flash_dev)
|
||||
{
|
||||
if (flash_dev == NULL) {
|
||||
return -1;
|
||||
}
|
||||
if (flash_dev->magic != PIOS_EEPROM_DEV_MAGIC) {
|
||||
return -2;
|
||||
}
|
||||
if (flash_dev->i2c_adapter == 0 || flash_dev->i2c_address == 0) {
|
||||
return -3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the flash device and enable write access
|
||||
*/
|
||||
int32_t PIOS_Flash_EEPROM_Init(uintptr_t *flash_id, struct pios_flash_eeprom_cfg *cfg, int32_t i2c_adapter, uint32_t i2c_addr)
|
||||
{
|
||||
struct flash_eeprom_dev *flash_dev = PIOS_Flash_EEPROM_alloc();
|
||||
|
||||
if (!flash_dev) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
flash_dev->i2c_adapter = i2c_adapter;
|
||||
flash_dev->i2c_address = i2c_addr;
|
||||
flash_dev->cfg = cfg;
|
||||
|
||||
if (!flash_dev->cfg) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Give back a handle to this flash device */
|
||||
*flash_id = (uintptr_t)flash_dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads one or more bytes into a buffer
|
||||
* \param[in] the command indicating the address to read
|
||||
* \param[out] buffer destination buffer
|
||||
* \param[in] len number of bytes which should be read
|
||||
* \return 0 if operation was successful
|
||||
* \return -1 if error during I2C transfer
|
||||
*/
|
||||
int32_t PIOS_Flash_EEPROM_ReadSinglePage(struct flash_eeprom_dev *flash_dev, uint32_t address, uint8_t *buffer, uint8_t len)
|
||||
{
|
||||
uint8_t i2c_addr = flash_dev->i2c_address;
|
||||
uint32_t bus = flash_dev->i2c_adapter;
|
||||
|
||||
if (address > 0xFFFF) {
|
||||
return -2;
|
||||
}
|
||||
uint16_t address16 = address & 0xFFFF;
|
||||
|
||||
uint8_t mem_address[] = {
|
||||
(address16 & 0xff00) >> 8, // MSB
|
||||
(address16 & 0xFF) // LSB
|
||||
};
|
||||
|
||||
const struct pios_i2c_txn txn_list[] = {
|
||||
{
|
||||
.info = __func__,
|
||||
.addr = i2c_addr,
|
||||
.rw = PIOS_I2C_TXN_WRITE,
|
||||
.len = 2,
|
||||
.buf = mem_address,
|
||||
}
|
||||
,
|
||||
{
|
||||
.info = __func__,
|
||||
.addr = i2c_addr,
|
||||
.rw = PIOS_I2C_TXN_READ,
|
||||
.len = len,
|
||||
.buf = buffer,
|
||||
}
|
||||
};
|
||||
|
||||
return PIOS_I2C_Transfer(bus, txn_list, NELEMENTS(txn_list));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes one up to a single page (128 bytes) to the EEPROM
|
||||
* \param[in] address write start address
|
||||
* \param[in] buffer source buffer
|
||||
* \param[in] len Buffer length
|
||||
* \return 0 if operation was successful
|
||||
* \return -1 if error during I2C transfer
|
||||
*/
|
||||
int32_t PIOS_Flash_EEPROM_WriteSinglePage(struct flash_eeprom_dev *flash_dev, const uint32_t address, const uint8_t *buffer, const uint8_t len)
|
||||
{
|
||||
uint8_t i2c_addr = flash_dev->i2c_address;
|
||||
uint32_t bus = flash_dev->i2c_adapter;
|
||||
|
||||
|
||||
if (address > 0xFFFF) {
|
||||
return -2;
|
||||
}
|
||||
uint16_t address16 = address & 0xFFFF;
|
||||
|
||||
uint8_t mem_address[] = {
|
||||
(address16 & 0xff00) >> 8, // MSB
|
||||
(address16 & 0xFF) // LSB
|
||||
};
|
||||
|
||||
const struct pios_i2c_txn txn_list[] = {
|
||||
{
|
||||
.info = __func__,
|
||||
.addr = i2c_addr,
|
||||
.rw = PIOS_I2C_TXN_WRITE,
|
||||
.len = 2,
|
||||
.buf = mem_address,
|
||||
}
|
||||
,
|
||||
{
|
||||
.info = __func__,
|
||||
.addr = i2c_addr,
|
||||
.rw = PIOS_I2C_TXN_WRITE,
|
||||
.len = len,
|
||||
.buf = (uint8_t *)buffer,
|
||||
}
|
||||
};
|
||||
|
||||
return PIOS_I2C_Transfer(bus, txn_list, NELEMENTS(txn_list));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read blocks of data spliting them into several single page operations to the EEPROM
|
||||
* \param[in] address Read start address
|
||||
* \param[in] buffer Dest buffer
|
||||
* \param[in] len Buffer length
|
||||
* \return 0 if operation was successful
|
||||
* \return -1 if error during I2C transfer
|
||||
*/
|
||||
int32_t PIOS_Flash_EEPROM_Read(struct flash_eeprom_dev *flash_dev, const uint32_t address, uint8_t *buffer, const uint8_t len)
|
||||
{
|
||||
uint16_t address16 = address & 0xFFFF;
|
||||
|
||||
if (address16 < address) {
|
||||
return -2;
|
||||
}
|
||||
// split the operation into several page operations
|
||||
uint8_t bytes_read = 0;
|
||||
|
||||
const uint8_t page_len = flash_dev->cfg->page_len;
|
||||
|
||||
while (bytes_read < len) {
|
||||
// all is fine, wait until memory is not busy
|
||||
int32_t status;
|
||||
while ((status = PIOS_Flash_EEPROM_Busy(flash_dev)) == 1) {
|
||||
#ifdef PIOS_INCLUDE_FREERTOS
|
||||
vTaskDelay(1);
|
||||
#endif
|
||||
}
|
||||
// An error occurred while probing the status, return and report
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
uint8_t current_block_len = len - bytes_read;
|
||||
uint16_t index_within_page = (address16 + bytes_read) % page_len;
|
||||
// prevent overflowing the page boundary
|
||||
current_block_len = MIN(page_len - index_within_page, current_block_len);
|
||||
status = PIOS_Flash_EEPROM_ReadSinglePage(flash_dev, address + bytes_read, &buffer[bytes_read], current_block_len);
|
||||
bytes_read += current_block_len;
|
||||
if (!status) {
|
||||
// error occurred during the write operation
|
||||
return status;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes blocks of data spliting them into several single page writes to the EEPROM
|
||||
* \param[in] address write start address
|
||||
* \param[in] buffer source buffer
|
||||
* \param[in] len Buffer length
|
||||
* \return 0 if operation was successful
|
||||
* \return -1 if error during I2C transfer
|
||||
*/
|
||||
int32_t PIOS_Flash_EEPROM_Write(struct flash_eeprom_dev *flash_dev, const uint32_t address, const uint8_t *buffer, const uint8_t len)
|
||||
{
|
||||
uint16_t address16 = address & 0xFFFF;
|
||||
|
||||
if (address16 < address) {
|
||||
return -2;
|
||||
}
|
||||
// split the operation into several page writes
|
||||
uint8_t bytes_written = 0;
|
||||
|
||||
const uint8_t page_len = flash_dev->cfg->page_len;
|
||||
while (bytes_written < len) {
|
||||
// wait until memory is not busy
|
||||
int32_t status;
|
||||
while ((status = PIOS_Flash_EEPROM_Busy(flash_dev)) == 1) {
|
||||
#ifdef PIOS_INCLUDE_FREERTOS
|
||||
vTaskDelay(1);
|
||||
#endif
|
||||
}
|
||||
// An error occurred while probing the status, return and report
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
uint8_t current_block_len = len - bytes_written;
|
||||
uint16_t index_within_page = (address16 + bytes_written) % page_len;
|
||||
// prevent overflowing the page boundary
|
||||
current_block_len = MIN(page_len - index_within_page, current_block_len);
|
||||
status = PIOS_Flash_EEPROM_WriteSinglePage(flash_dev, address + bytes_written, &buffer[bytes_written], current_block_len);
|
||||
bytes_written += current_block_len;
|
||||
if (!status) {
|
||||
// error occurred during the write operation
|
||||
return status;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* @brief Returns if the flash chip is busy
|
||||
* @returns -1 for failure, 0 for not busy, 1 for busy
|
||||
*/
|
||||
static int32_t PIOS_Flash_EEPROM_Busy(struct flash_eeprom_dev *flash_dev)
|
||||
{
|
||||
uint8_t buf;
|
||||
int32_t status = PIOS_Flash_EEPROM_ReadSinglePage(flash_dev, 0x0, &buf, 1);
|
||||
|
||||
switch (status) {
|
||||
case -3: // NACK
|
||||
return 1;
|
||||
|
||||
case 0:
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************
|
||||
*
|
||||
* Provide a PIOS flash driver API
|
||||
*
|
||||
*********************************/
|
||||
#include "pios_flash.h"
|
||||
|
||||
#if FLASH_USE_FREERTOS_LOCKS
|
||||
|
||||
/**
|
||||
* @brief Grab the semaphore to perform a transaction
|
||||
* @return 0 for success, -1 for timeout
|
||||
*/
|
||||
static int32_t PIOS_Flash_EEPROM_StartTransaction(uintptr_t flash_id)
|
||||
{
|
||||
struct flash_eeprom_dev *flash_dev = (struct flash_eeprom_dev *)flash_id;
|
||||
|
||||
if (PIOS_Flash_EEPROM_Validate(flash_dev) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if defined(PIOS_INCLUDE_FREERTOS)
|
||||
if (xSemaphoreTake(flash_dev->transaction_lock, portMAX_DELAY) != pdTRUE) {
|
||||
return -2;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Release the semaphore to perform a transaction
|
||||
* @return 0 for success, -1 for timeout
|
||||
*/
|
||||
static int32_t PIOS_Flash_EEPROM_EndTransaction(uintptr_t flash_id)
|
||||
{
|
||||
struct flash_eeprom_dev *flash_dev = (struct flash_eeprom_dev *)flash_id;
|
||||
|
||||
if (PIOS_Flash_EEPROM_Validate(flash_dev) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if defined(PIOS_INCLUDE_FREERTOS)
|
||||
if (xSemaphoreGive(flash_dev->transaction_lock) != pdTRUE) {
|
||||
return -2;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else /* FLASH_USE_FREERTOS_LOCKS */
|
||||
|
||||
static int32_t PIOS_Flash_EEPROM_StartTransaction(__attribute__((unused)) uintptr_t flash_id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t PIOS_Flash_EEPROM_EndTransaction(__attribute__((unused)) uintptr_t flash_id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* FLASH_USE_FREERTOS_LOCKS */
|
||||
|
||||
/**
|
||||
* @brief Erase a sector on the flash chip
|
||||
* @param[in] add Address of flash to erase
|
||||
* @returns 0 if successful
|
||||
* @retval -1 if unable to claim bus
|
||||
* @retval
|
||||
*/
|
||||
static int32_t PIOS_Flash_EEPROM_EraseSector(uintptr_t flash_id, uint32_t addr)
|
||||
{
|
||||
struct flash_eeprom_dev *flash_dev = (struct flash_eeprom_dev *)flash_id;
|
||||
|
||||
if (PIOS_Flash_EEPROM_Validate(flash_dev) != 0) {
|
||||
return -1;
|
||||
}
|
||||
// Rewrite first block of bytes to invalidate the sector.
|
||||
const uint8_t buf[16] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
|
||||
return PIOS_Flash_EEPROM_Write(flash_dev, addr, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write one page of data (up to 256 bytes) aligned to a page start
|
||||
* @param[in] addr Address in flash to write to
|
||||
* @param[in] data Pointer to data to write to flash
|
||||
* @param[in] len Length of data to write (max 256 bytes)
|
||||
* @return Zero if success or error code
|
||||
* @retval -1 Unable to write to the device
|
||||
*/
|
||||
static int32_t PIOS_Flash_EEPROM_WriteData(uintptr_t flash_id, uint32_t addr, uint8_t *data, uint16_t len)
|
||||
{
|
||||
struct flash_eeprom_dev *flash_dev = (struct flash_eeprom_dev *)flash_id;
|
||||
|
||||
if (PIOS_Flash_EEPROM_Validate(flash_dev) != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (PIOS_Flash_EEPROM_Write(flash_dev, addr, data, len) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write multiple chunks of data in one transaction
|
||||
* @param[in] addr Address in flash to write to
|
||||
* @param[in] data Pointer to data to write to flash
|
||||
* @param[in] len Length of data to write
|
||||
* @return Zero if success or error code
|
||||
* @retval -1 Unable to write to the device
|
||||
*/
|
||||
static int32_t PIOS_Flash_EEPROM_WriteChunks(uintptr_t flash_id, uint32_t addr, struct pios_flash_chunk chunks[], uint32_t num)
|
||||
{
|
||||
struct flash_eeprom_dev *flash_dev = (struct flash_eeprom_dev *)flash_id;
|
||||
|
||||
if (PIOS_Flash_EEPROM_Validate(flash_dev) != 0) {
|
||||
return -1;
|
||||
}
|
||||
uint32_t memory_displacement = 0;
|
||||
for (uint32_t i = 0; i < num; i++) {
|
||||
struct pios_flash_chunk *chunk = &chunks[i];
|
||||
// no need to check for busy flag as it is done before each write operation by _Write function
|
||||
if (PIOS_Flash_EEPROM_Write(flash_dev, addr + memory_displacement, chunk->addr, chunk->len) < 0) {
|
||||
return -1;
|
||||
}
|
||||
memory_displacement += chunk->len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read data from a location in flash memory
|
||||
* @param[in] addr Address in flash to write to
|
||||
* @param[in] data Pointer to data to write from flash
|
||||
* @param[in] len Length of data to write (max 256 bytes)
|
||||
* @return Zero if success or error code
|
||||
* @retval -1 Unable to read from the device
|
||||
*/
|
||||
static int32_t PIOS_Flash_EEPROM_ReadData(uintptr_t flash_id, uint32_t addr, uint8_t *data, uint16_t len)
|
||||
{
|
||||
struct flash_eeprom_dev *flash_dev = (struct flash_eeprom_dev *)flash_id;
|
||||
|
||||
if (PIOS_Flash_EEPROM_Validate(flash_dev) != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (PIOS_Flash_EEPROM_Read(flash_dev, addr, data, len) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Provide a flash driver to external drivers */
|
||||
const struct pios_flash_driver pios_EEPROM_flash_driver = {
|
||||
.start_transaction = PIOS_Flash_EEPROM_StartTransaction,
|
||||
.end_transaction = PIOS_Flash_EEPROM_EndTransaction,
|
||||
.erase_chip = NULL,
|
||||
.erase_sector = PIOS_Flash_EEPROM_EraseSector,
|
||||
.write_chunks = PIOS_Flash_EEPROM_WriteChunks,
|
||||
.rewrite_chunks = PIOS_Flash_EEPROM_WriteChunks,
|
||||
.write_data = PIOS_Flash_EEPROM_WriteData,
|
||||
.rewrite_data = PIOS_Flash_EEPROM_WriteData,
|
||||
.read_data = PIOS_Flash_EEPROM_ReadData,
|
||||
};
|
||||
|
||||
#endif /* PIOS_INCLUDE_FLASH_EEPROM*/
|
@ -41,6 +41,8 @@ struct pios_flash_driver {
|
||||
int32_t (*erase_sector)(uintptr_t flash_id, uint32_t addr);
|
||||
int32_t (*write_data)(uintptr_t flash_id, uint32_t addr, uint8_t *data, uint16_t len);
|
||||
int32_t (*write_chunks)(uintptr_t flash_id, uint32_t addr, struct pios_flash_chunk chunks[], uint32_t num_chunks);
|
||||
int32_t (*rewrite_data)(uintptr_t flash_id, uint32_t addr, uint8_t *data, uint16_t len);
|
||||
int32_t (*rewrite_chunks)(uintptr_t flash_id, uint32_t addr, struct pios_flash_chunk chunks[], uint32_t num_chunks);
|
||||
int32_t (*read_data)(uintptr_t flash_id, uint32_t addr, uint8_t *data, uint16_t len);
|
||||
};
|
||||
|
||||
|
21
flight/pios/inc/pios_flash_eeprom.h
Normal file
21
flight/pios/inc/pios_flash_eeprom.h
Normal file
@ -0,0 +1,21 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
*
|
||||
* @file pios_flash_eeprom.h
|
||||
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2014.
|
||||
* @brief API to interface to EEPROM chips using the common flash api
|
||||
* --
|
||||
* @see The GNU Public License (GPL) Version 3
|
||||
*
|
||||
*****************************************************************************/
|
||||
#ifndef FLIGHT_PIOS_INC_PIOS_FLASH_EEPROM_H_
|
||||
#define FLIGHT_PIOS_INC_PIOS_FLASH_EEPROM_H_
|
||||
#include <pios_flash.h>
|
||||
struct pios_flash_eeprom_cfg {
|
||||
uint16_t page_len;
|
||||
uint32_t total_size;
|
||||
};
|
||||
extern const struct pios_flash_driver pios_EEPROM_flash_driver;
|
||||
int32_t PIOS_Flash_EEPROM_Init(uintptr_t *flash_id, struct pios_flash_eeprom_cfg *cfg, int32_t i2c_adapter, uint32_t i2c_addr);
|
||||
|
||||
#endif /* FLIGHT_PIOS_INC_PIOS_FLASH_EEPROM_H_ */
|
Loading…
x
Reference in New Issue
Block a user