1
0
mirror of https://bitbucket.org/librepilot/librepilot.git synced 2024-11-30 08:24:11 +01:00
LibrePilot/flight/PiOS/Common/pios_flash_w25x.c
James Cotton 1e8811362e On revolution attach the flash chip to the accel bus now. Also extended the
flash chip driver to take in the slave number during initialization so this is
no longer a hardcoded option.
2012-01-20 07:37:47 -06:00

375 lines
9.5 KiB
C

/**
******************************************************************************
*
* @addtogroup PIOS PIOS Core hardware abstraction layer
* @{
* @addtogroup PIOS_FLASH Flash device handler
* @{
*
* @file pios_flash_w25x.c
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
* @brief Driver for talking to W25X flash chip (and most JEDEC chips)
* @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"
#include "pios_flash_w25x.h"
#include "pios_adxl345.h"
#define W25X_WRITE_ENABLE 0x06
#define W25X_WRITE_DISABLE 0x04
#define W25X_READ_STATUS 0x05
#define W25X_WRITE_STATUS 0x01
#define W25X_READ_DATA 0x03
#define W25X_FAST_READ 0x0b
#define W25X_DEVICE_ID 0x90
#define W25X_SECTOR_ERASE 0xD8
#define W25X_PAGE_WRITE 0x02
#define W25X_CHIP_ERASE 0xC7
#define W25X_STATUS_BUSY 0x01
#define W25X_STATUS_WRITEPROTECT 0x02
#define W25X_STATUS_BP0 0x04
#define W25X_STATUS_BP1 0x08
#define W25X_STATUS_BP2 0x10
#define W25X_STATUS_TP 0x20
#define W25X_STATUS_SEC 0x40
#define W25X_STATUS_SRP0 0x80
static uint8_t device_type;
enum pios_w25x_dev_magic {
PIOS_W25X_DEV_MAGIC = 0xcb55aa55,
};
//! Device handle structure
struct w25x_flash_dev {
uint32_t spi_id;
uint32_t slave_num;
enum pios_w25x_dev_magic magic;
};
//! Global structure for this flash device
struct w25x_flash_dev * flash_dev;
//! Private functions
static int32_t PIOS_Flash_W25X_Validate(struct w25x_flash_dev * dev);
static struct w25x_flash_dev * PIOS_Flash_W25X_alloc(void);
static int32_t PIOS_Flash_W25X_ClaimBus();
static int32_t PIOS_Flash_W25X_ReleaseBus();
static int32_t PIOS_Flash_W25X_WriteEnable();
static int32_t PIOS_Flash_W25X_Busy() ;
/**
* @brief Allocate a new device
*/
static struct w25x_flash_dev * PIOS_Flash_W25X_alloc(void)
{
struct w25x_flash_dev * w25x_dev;
w25x_dev = (struct w25x_flash_dev *)pvPortMalloc(sizeof(*w25x_dev));
if (!w25x_dev) return (NULL);
w25x_dev->magic = PIOS_W25X_DEV_MAGIC;
return(w25x_dev);
}
/**
* @brief Validate the handle to the spi device
*/
static int32_t PIOS_Flash_W25X_Validate(struct w25x_flash_dev * dev) {
if (dev == NULL)
return -1;
if (dev->magic != PIOS_W25X_DEV_MAGIC)
return -2;
if (dev->spi_id == 0)
return -3;
return 0;
}
/**
* @brief Claim the SPI bus for flash use and assert CS pin
* @return 0 for sucess, -1 for failure to get semaphore
*/
static int32_t PIOS_Flash_W25X_ClaimBus()
{
if(PIOS_Flash_W25X_Validate(flash_dev) != 0)
return -1;
int8_t ret = PIOS_SPI_ClaimBus(flash_dev->spi_id);
PIOS_SPI_RC_PinSet(flash_dev->spi_id, flash_dev->slave_num, 0);
return (ret == 0) ? 0 : -1;
}
/**
* @brief Release the SPI bus sempahore and ensure flash chip not using bus
*/
static int32_t PIOS_Flash_W25X_ReleaseBus()
{
if(PIOS_Flash_W25X_Validate(flash_dev) != 0)
return -1;
PIOS_SPI_RC_PinSet(flash_dev->spi_id, flash_dev->slave_num, 1);
PIOS_SPI_ReleaseBus(flash_dev->spi_id);
}
/**
* @brief Returns if the flash chip is busy
* @returns -1 for failure, 0 for not busy, 1 for busy
*/
static int32_t PIOS_Flash_W25X_Busy()
{
int32_t status = PIOS_Flash_W25X_ReadStatus();
if (status < 0)
return -1;
return status & W25X_STATUS_BUSY;
}
/**
* @brief Execute the write enable instruction and returns the status
* @returns 0 if successful, -1 if unable to claim bus
*/
static int32_t PIOS_Flash_W25X_WriteEnable()
{
if(PIOS_Flash_W25X_Validate(flash_dev) != 0)
return -1;
uint8_t out[] = {W25X_WRITE_ENABLE};
if(PIOS_Flash_W25X_ClaimBus() != 0)
return -1;
PIOS_SPI_TransferBlock(flash_dev->spi_id,out,NULL,sizeof(out),NULL);
PIOS_Flash_W25X_ReleaseBus();
return 0;
}
/**
* @brief Initialize the flash device and enable write access
*/
int32_t PIOS_Flash_W25X_Init(uint32_t spi_id, uint32_t slave_num)
{
flash_dev = PIOS_Flash_W25X_alloc();
if(flash_dev == NULL)
return -1;
flash_dev->spi_id = spi_id;
flash_dev->slave_num = slave_num;
device_type = PIOS_Flash_W25X_ReadID();
return 0;
}
/**
* @brief Read the status register from flash chip and return it
*/
int32_t PIOS_Flash_W25X_ReadStatus()
{
if(PIOS_Flash_W25X_Validate(flash_dev) != 0)
return -1;
uint8_t out[2] = {W25X_READ_STATUS, 0};
uint8_t in[2] = {0,0};
if(PIOS_Flash_W25X_ClaimBus() < 0)
return -1;
if(PIOS_SPI_TransferBlock(flash_dev->spi_id,out,in,sizeof(out),NULL) < 0) {
PIOS_Flash_W25X_ReleaseBus();
return -2;
}
PIOS_Flash_W25X_ReleaseBus();
return in[1];
}
/**
* @brief Read the status register from flash chip and return it
*/
int32_t PIOS_Flash_W25X_ReadID()
{
if(PIOS_Flash_W25X_Validate(flash_dev) != 0)
return -1;
uint8_t out[] = {W25X_DEVICE_ID, 0, 0, 0, 0, 0};
uint8_t in[6];
if (PIOS_Flash_W25X_ClaimBus() < 0)
return -1;
if(PIOS_SPI_TransferBlock(flash_dev->spi_id,out,in,sizeof(out),NULL) < 0) {
PIOS_Flash_W25X_ReleaseBus();
return -2;
}
PIOS_Flash_W25X_ReleaseBus();
return in[5];
}
/**
* @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
*/
int32_t PIOS_Flash_W25X_EraseSector(uint32_t addr)
{
if(PIOS_Flash_W25X_Validate(flash_dev) != 0)
return -1;
uint8_t ret;
uint8_t out[] = {W25X_SECTOR_ERASE, (addr >> 16) & 0xff, (addr >> 8) & 0xff , addr & 0xff};
if((ret = PIOS_Flash_W25X_WriteEnable()) != 0)
return ret;
if(PIOS_Flash_W25X_ClaimBus() != 0)
return -1;
if(PIOS_SPI_TransferBlock(flash_dev->spi_id,out,NULL,sizeof(out),NULL) < 0) {
PIOS_Flash_W25X_ReleaseBus();
return -2;
}
PIOS_Flash_W25X_ReleaseBus();
// Keep polling when bus is busy too
while(PIOS_Flash_W25X_Busy() != 0) {
}
return 0;
}
/**
* @brief Execute the whole chip
* @returns 0 if successful, -1 if unable to claim bus
*/
int32_t PIOS_Flash_W25X_EraseChip()
{
if(PIOS_Flash_W25X_Validate(flash_dev) != 0)
return -1;
uint8_t ret;
uint8_t out[] = {W25X_CHIP_ERASE};
if((ret = PIOS_Flash_W25X_WriteEnable()) != 0)
return ret;
if(PIOS_Flash_W25X_ClaimBus() != 0)
return -1;
if(PIOS_SPI_TransferBlock(flash_dev->spi_id,out,NULL,sizeof(out),NULL) < 0) {
PIOS_Flash_W25X_ReleaseBus();
return -2;
}
PIOS_Flash_W25X_ReleaseBus();
// Keep polling when bus is busy too
while(PIOS_Flash_W25X_Busy() != 0) {
}
return 0;
}
/**
* @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 claim SPI bus
* @retval -2 Size exceeds 256 bytes
* @retval -3 Length to write would wrap around page boundary
*/
int32_t PIOS_Flash_W25X_WriteData(uint32_t addr, uint8_t * data, uint16_t len)
{
if(PIOS_Flash_W25X_Validate(flash_dev) != 0)
return -1;
uint8_t ret;
uint8_t out[4] = {W25X_PAGE_WRITE, (addr >> 16) & 0xff, (addr >> 8) & 0xff , addr & 0xff};
/* Can only write one page at a time */
if(len > 0x100)
return -2;
/* Ensure number of bytes fits after starting address before end of page */
if(((addr & 0xff) + len) > 0x100)
return -3;
if((ret = PIOS_Flash_W25X_WriteEnable()) != 0)
return ret;
/* Execute write page command and clock in address. Keep CS asserted */
if(PIOS_Flash_W25X_ClaimBus() != 0)
return -1;
if(PIOS_SPI_TransferBlock(flash_dev->spi_id,out,NULL,sizeof(out),NULL) < 0) {
PIOS_Flash_W25X_ReleaseBus();
return -1;
}
/* Clock out data to flash */
if(PIOS_SPI_TransferBlock(flash_dev->spi_id,data,NULL,len,NULL) < 0) {
PIOS_Flash_W25X_ReleaseBus();
return -1;
}
PIOS_Flash_W25X_ReleaseBus();
// Keep polling while bus is busy too
while(PIOS_Flash_W25X_Busy() != 0) {
}
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 claim SPI bus
*/
int32_t PIOS_Flash_W25X_ReadData(uint32_t addr, uint8_t * data, uint16_t len)
{
if(PIOS_Flash_W25X_Validate(flash_dev) != 0)
return -1;
if(PIOS_Flash_W25X_ClaimBus() == -1)
return -1;
/* Execute read command and clock in address. Keep CS asserted */
uint8_t out[] = {W25X_READ_DATA, (addr >> 16) & 0xff, (addr >> 8) & 0xff , addr & 0xff};
if(PIOS_SPI_TransferBlock(flash_dev->spi_id,out,NULL,sizeof(out),NULL) < 0) {
PIOS_Flash_W25X_ReleaseBus();
return -2;
}
/* Copy the transfer data to the buffer */
if(PIOS_SPI_TransferBlock(flash_dev->spi_id,NULL,data,len,NULL) < 0) {
PIOS_Flash_W25X_ReleaseBus();
return -3;
}
PIOS_Flash_W25X_ReleaseBus();
return 0;
}