Mercurial > ecos-v3_0-branch
changeset 2768:49a028abd944
* cdl/m25pxx.cdl:
* include/m25pxx.h:
* src/m25pxx.c:
* test/m25pxx_test.c:
New package - SPI flash driver for M25Pxx devices and compatibles.
Driver contributed by Chris Holgate. Includes a soak test which is
not part of the default tests as it requires a specific configuration
to be used - see test/m25pxx_test.c for details.
| author | nickg |
|---|---|
| date | Thu, 05 Feb 2009 17:25:24 +0000 |
| parents | 1d8577f3a1b1 |
| children | ef659b3b6980 |
| files | packages/devs/flash/spi/m25pxx/current/ChangeLog packages/devs/flash/spi/m25pxx/current/cdl/flash_m25pxx.cdl packages/devs/flash/spi/m25pxx/current/include/m25pxx.h packages/devs/flash/spi/m25pxx/current/src/m25pxx.c packages/devs/flash/spi/m25pxx/current/test/m25pxx_test.c |
| diffstat | 5 files changed, 1057 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/packages/devs/flash/spi/m25pxx/current/ChangeLog @@ -0,0 +1,34 @@ +2009-02-05 Chris Holgate <chris@zynaptic.com> + + * cdl/m25pxx.cdl: + * include/m25pxx.h: + * src/m25pxx.c: + * test/m25pxx_test.c: + New package - SPI flash driver for M25Pxx devices and compatibles. + Driver contributed by Chris Holgate. Includes a soak test which is + not part of the default tests as it requires a specific configuration + to be used - see test/m25pxx_test.c for details. + +//=========================================================================== +// ####GPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008, 2009 Free Software Foundation, Inc. +// +// 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 2 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., 51 Franklin Street, +// Fifth Floor, Boston, MA 02110-1301, USA. +// ------------------------------------------- +// ####GPLCOPYRIGHTEND#### +//===========================================================================
new file mode 100644 --- /dev/null +++ b/packages/devs/flash/spi/m25pxx/current/cdl/flash_m25pxx.cdl @@ -0,0 +1,77 @@ +##============================================================================= +## +## flash_m25pxx.cdl +## +## M25Pxx SPI flash driver configuration options. +## +##============================================================================= +## ####ECOSGPLCOPYRIGHTBEGIN#### +## ------------------------------------------- +## This file is part of eCos, the Embedded Configurable Operating System. +## Copyright (C) 2008, 2009 Free Software Foundation, Inc. +## +## eCos 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 2 or (at your option) any later +## version. +## +## eCos 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 eCos; if not, write to the Free Software Foundation, Inc., +## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +## +## As a special exception, if other files instantiate templates or use +## macros or inline functions from this file, or you compile this file +## and link it with other works to produce a work based on this file, +## this file does not by itself cause the resulting work to be covered by +## the GNU General Public License. However the source code for this file +## must still be made available in accordance with section (3) of the GNU +## General Public License v2. +## +## This exception does not invalidate any other reasons why a work based +## on this file might be covered by the GNU General Public License. +## ------------------------------------------- +## ####ECOSGPLCOPYRIGHTEND#### +##============================================================================= +#######DESCRIPTIONBEGIN#### +## +## Author(s): Chris Holgate +## Date: 2008-12-22 +## Purpose: Configure M25Pxx SPI flash driver. +## +######DESCRIPTIONEND#### +## +##============================================================================= + +cdl_package CYGPKG_DEVS_FLASH_SPI_M25PXX { + display "M25PXX flash memory support" + parent CYGPKG_IO_FLASH + active_if CYGPKG_IO_FLASH CYGPKG_IO_SPI + implements CYGHWR_IO_FLASH_DEVICE + implements CYGHWR_IO_FLASH_INDIRECT_READS + include_dir cyg/io + compile m25pxx.c + + description " + Flash memory support for Numonyx M25Pxx SPI flash devices and + compatibles. This driver implements the V2 flash driver API" + +cdl_option CYGNUM_DEVS_FLASH_SPI_M25PXX_READ_BLOCK_SIZE { + display "Maximum read block size" + description " + In theory it is possible to read back the entire flash contents using + a single SPI transaction. However, some SPI bus drivers have a maximum + transaction size - for example transactions may be limited to the + length of a DMA bounce buffer. Setting this option to a non-zero value + specifies the maximum SPI bus transfer size which will be used when + reading back data. Read requests for areas larger than this block size + will automatically be split into a series of smaller SPI bus transactions. + " + flavor data + default_value 0 +} +}
new file mode 100644 --- /dev/null +++ b/packages/devs/flash/spi/m25pxx/current/include/m25pxx.h @@ -0,0 +1,78 @@ +#ifndef CYGONCE_DEVS_FLASH_SPI_M25PXX_H +#define CYGONCE_DEVS_FLASH_SPI_M25PXX_H + +//============================================================================= +// +// m25pxx.h +// +// SPI flash driver for Numonyx M25Pxx devices and compatibles. +// +//============================================================================= +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008, 2009 Free Software Foundation, Inc. +// +// eCos 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 2 or (at your option) any later +// version. +// +// eCos 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 eCos; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// As a special exception, if other files instantiate templates or use +// macros or inline functions from this file, or you compile this file +// and link it with other works to produce a work based on this file, +// this file does not by itself cause the resulting work to be covered by +// the GNU General Public License. However the source code for this file +// must still be made available in accordance with section (3) of the GNU +// General Public License v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public License. +// ------------------------------------------- +// ####ECOSGPLCOPYRIGHTEND#### +//============================================================================= +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Chris Holgate +// Date: 2008-12-22 +// Purpose: Numonyx M25Pxx SPI flash driver implementation +// +//####DESCRIPTIONEND#### +// +//============================================================================= + +// Required data structures. +#include <cyg/io/flash_dev.h> + +// Exported handle on the driver function table. +externC struct cyg_flash_dev_funs cyg_devs_flash_spi_m25pxx_funs; + +//----------------------------------------------------------------------------- +// Macro used to generate a flash device object with the default M25PXX +// settings. Even though the block info data structure is declared here, the +// details are not filled in until the device type is inferred during +// initialisation. This also applies to the 'end' field which is calculated +// using the _start_ address and the inferred size of the device. +// _name_ is the root name of the instantiated data structures. +// _start_ is the base address of the device - for SPI based devices this can +// have an arbitrary value, since the device is not memory mapped. +// _spidev_ is a pointer to a SPI device object of type cyg_spi_device. This +// is not typechecked during compilation so be careful! + +#define CYG_DEVS_FLASH_SPI_M25PXX_DRIVER(_name_, _start_, _spidev_) \ +struct cyg_flash_block_info _name_ ##_block_info; \ +CYG_FLASH_DRIVER(_name_, &cyg_devs_flash_spi_m25pxx_funs, 0, \ + _start_, _start_, 1, & _name_ ##_block_info, (void*) _spidev_) + +//============================================================================= + +#endif // CYGONCE_DEVS_FLASH_SPI_M25PXX_H
new file mode 100644 --- /dev/null +++ b/packages/devs/flash/spi/m25pxx/current/src/m25pxx.c @@ -0,0 +1,473 @@ +//============================================================================= +// +// m25pxx.c +// +// SPI flash driver for Numonyx M25Pxx devices and compatibles. +// +//============================================================================= +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008, 2009 Free Software Foundation, Inc. +// +// eCos 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 2 or (at your option) any later +// version. +// +// eCos 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 eCos; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// As a special exception, if other files instantiate templates or use +// macros or inline functions from this file, or you compile this file +// and link it with other works to produce a work based on this file, +// this file does not by itself cause the resulting work to be covered by +// the GNU General Public License. However the source code for this file +// must still be made available in accordance with section (3) of the GNU +// General Public License v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public License. +// ------------------------------------------- +// ####ECOSGPLCOPYRIGHTEND#### +//============================================================================= +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Chris Holgate +// Date: 2008-12-22 +// Purpose: Numonyx M25Pxx SPI flash driver implementation +// +//####DESCRIPTIONEND#### +// +//============================================================================= + +#include <cyg/io/spi.h> +#include <cyg/io/flash.h> +#include <cyg/io/flash_dev.h> + +#include <cyg/infra/cyg_type.h> +#include <cyg/infra/cyg_ass.h> + +#include <pkgconf/devs_flash_spi_m25pxx.h> + +#include <string.h> + +//----------------------------------------------------------------------------- +// Enable polled SPI operation for non-kernel builds. + +#ifdef CYGPKG_KERNEL +#define M25PXX_POLLED false + +#else +#define M25PXX_POLLED true +#endif + +//----------------------------------------------------------------------------- +// Implement delay functions for kernel and non-kernel builds. The kernel +// build assumes that the API calls are made in the thread context. + +#ifdef CYGPKG_KERNEL +#define M25PXX_DELAY_MS(_msdelay_) cyg_thread_delay (\ + 1 + ((1000 * _msdelay_ * CYGNUM_HAL_RTC_DENOMINATOR) / (CYGNUM_HAL_RTC_NUMERATOR / 1000))) + +#else +#define M25PXX_DELAY_MS(_msdelay_) CYGACC_CALL_IF_DELAY_US (_msdelay_ * 1000) +#endif + +//----------------------------------------------------------------------------- +// Maintenance and debug macros. + +#define TODO_M25P(_msg_) CYG_ASSERT(false, "TODO (M25P) : " _msg_) +#define FAIL_M25P(_msg_) CYG_ASSERT(false, "FAIL (M25P) : " _msg_) +#define ASSERT_M25P(_test_, _msg_) CYG_ASSERT(_test_, "FAIL (M25P) : " _msg_) +#define TRACE_M25P(_msg_, _args_...) if (dev->pf) dev->pf ("M25PXX : " _msg_, ##_args_) + +//============================================================================= +// Define M25Pxx SPI protocol. +//============================================================================= + +typedef enum m25pxx_cmd { + M25PXX_CMD_WREN = 0x06, // Write enable. + M25PXX_CMD_WDRI = 0x04, // Write disable. + M25PXX_CMD_RDID = 0x9F, // Read identification. + M25PXX_CMD_RDSR = 0x05, // Read status register. + M25PXX_CMD_WRSR = 0x01, // Write status register. + M25PXX_CMD_READ = 0x03, // Read data. + M25PXX_CMD_FREAD = 0x0B, // Read data (fast transaction). + M25PXX_CMD_PP = 0x02, // Page program. + M25PXX_CMD_SE = 0xD8, // Sector erase. + M25PXX_CMD_BE = 0xC7, // Bulk erase. + M25PXX_CMD_RES = 0xAB, // Read electronic signature. +} m25pxx_cmd; + +// Status register bitfields. +#define M25PXX_STATUS_WIP 0x01 /* Write in progress. */ +#define M25PXX_STATUS_WEL 0x02 /* Write enable latch. */ +#define M25PXX_STATUS_BP0 0x04 /* Block protect 0. */ +#define M25PXX_STATUS_BP1 0x08 /* Block protect 1. */ +#define M25PXX_STATUS_BP2 0x10 /* Block protect 2. */ +#define M25PXX_STATUS_SRWD 0x80 /* Status register write protect. */ + +// Page size of 256 bytes appears to be common for all devices. +#define M25PXX_PAGE_SIZE 256 + +//============================================================================= +// Array containing a list of supported devices. This allows the device +// parameters to be dynamically detected on initialisation. +//============================================================================= + +typedef struct m25pxx_params { + cyg_uint16 sector_size; // Number of pages in a sector. + cyg_uint16 sector_count; // Number of sectors on device. + cyg_uint32 jedec_id; // 3 byte JEDEC identifier for this device. +} m25pxx_params; + +static const m25pxx_params m25pxx_supported_devices [] = { + { // Support for Numonyx 128 MBit devices. + sector_size : 1024, + sector_count : 64, + jedec_id : 0x00202018 + }, + { // Support for Numonyx 64 MBit devices. + sector_size : 256, + sector_count : 128, + jedec_id : 0x00202017 + }, + { // Support for Numonyx 16 MBit devices. + sector_size : 256, + sector_count : 64, + jedec_id : 0x00202016 + }, + { // Support for Numonyx 16 MBit devices. + sector_size : 256, + sector_count : 32, + jedec_id : 0x00202015 + }, + { // Support for Numonyx 8 MBit devices. + sector_size : 256, + sector_count : 16, + jedec_id : 0x00202014 + }, + { // Support for Numonyx 4 MBit devices. + sector_size : 256, + sector_count : 8, + jedec_id : 0x00202013 + }, + { // Support for Numonyx 2 MBit devices. + sector_size : 256, + sector_count : 4, + jedec_id : 0x00202012 + }, + { // Support for Numonyx 1 MBit devices. + sector_size : 128, + sector_count : 4, + jedec_id : 0x00202011 + }, + { // Support for Numonyx 512 KBit devices. + sector_size : 128, + sector_count : 2, + jedec_id : 0x00202010 + }, + { // Null terminating entry. + sector_size : 0, + sector_count : 0, + jedec_id : 0 + } +}; + +//============================================================================= +// Utility functions for address calculations. +//============================================================================= + +//----------------------------------------------------------------------------- +// Strips out any device address offset to give address within device. + +static cyg_bool m25pxx_to_local_addr + (struct cyg_flash_dev* dev, cyg_flashaddr_t* addr) +{ + cyg_bool retval = false; + + // Range check address before modifying it. + if ((*addr >= dev->start) && (*addr <= dev->end)) { + *addr -= dev->start; + retval = true; + } + return retval; +} + +//============================================================================= +// Wrapper functions for various SPI transactions. +//============================================================================= + +//----------------------------------------------------------------------------- +// Read back the 3-byte JEDEC ID, returning it as a 32-bit integer. +// This function is called during flash initialisation, which can often be +// called from the startup/idle thread. This means that we should always use +// SPI polled mode in order to prevent the thread from attempting to sleep. + +static inline cyg_uint32 m25pxx_spi_rdid + (struct cyg_flash_dev *dev) +{ + cyg_spi_device* spi_device = (cyg_spi_device*) dev->priv; + const cyg_uint8 tx_buf [4] = { M25PXX_CMD_RDID, 0, 0, 0 }; + cyg_uint8 rx_buf [4]; + cyg_uint32 retval = 0; + + // Carry out SPI transfer. + cyg_spi_transfer (spi_device, true, 4, tx_buf, rx_buf); + + // Convert 3-byte ID to 32-bit integer. + retval |= ((cyg_uint32) rx_buf[1]) << 16; + retval |= ((cyg_uint32) rx_buf[2]) << 8; + retval |= ((cyg_uint32) rx_buf[3]); + + return retval; +} + +//----------------------------------------------------------------------------- +// Send write enable command. + +static inline void m25pxx_spi_wren + (struct cyg_flash_dev *dev) +{ + cyg_spi_device* spi_device = (cyg_spi_device*) dev->priv; + const cyg_uint8 tx_buf [1] = { M25PXX_CMD_WREN }; + cyg_spi_transfer (spi_device, M25PXX_POLLED, 1, tx_buf, NULL); +} + +//----------------------------------------------------------------------------- +// Send sector erase command. The address parameter is a device local address +// within the sector to be erased. + +static inline void m25pxx_spi_se + (struct cyg_flash_dev *dev, cyg_flashaddr_t addr) +{ + cyg_spi_device* spi_device = (cyg_spi_device*) dev->priv; + const cyg_uint8 tx_buf [4] = { M25PXX_CMD_SE, + (cyg_uint8) (addr >> 16), (cyg_uint8) (addr >> 8), (cyg_uint8) (addr) }; + cyg_spi_transfer (spi_device, M25PXX_POLLED, 4, tx_buf, NULL); +} + +//----------------------------------------------------------------------------- +// Read and return the 8-bit device status register. + +static inline cyg_uint8 m25pxx_spi_rdsr + (struct cyg_flash_dev *dev) +{ + cyg_spi_device* spi_device = (cyg_spi_device*) dev->priv; + const cyg_uint8 tx_buf [2] = { M25PXX_CMD_RDSR, 0 }; + cyg_uint8 rx_buf [2]; + + // Carry out SPI transfer and return the status byte. + cyg_spi_transfer (spi_device, M25PXX_POLLED, 2, tx_buf, rx_buf); + return rx_buf [1]; +} + +//----------------------------------------------------------------------------- +// Program a single page. + +static inline void m25pxx_spi_pp + (struct cyg_flash_dev *dev, cyg_flashaddr_t addr, cyg_uint8* wbuf, cyg_uint32 wbuf_len) +{ + cyg_spi_device* spi_device = (cyg_spi_device*) dev->priv; + const cyg_uint8 tx_buf [4] = { M25PXX_CMD_PP, + (cyg_uint8) (addr >> 16), (cyg_uint8) (addr >> 8), (cyg_uint8) (addr) }; + + // Implement the program operation as a multistage SPI transaction. + cyg_spi_transaction_begin (spi_device); + cyg_spi_transaction_transfer (spi_device, M25PXX_POLLED, 4, tx_buf, NULL, false); + cyg_spi_transaction_transfer (spi_device, M25PXX_POLLED, wbuf_len, wbuf, NULL, false); + cyg_spi_transaction_end (spi_device); +} + +//----------------------------------------------------------------------------- +// Implement fast reads to the specified buffer. + +static inline void m25pxx_spi_fread + (struct cyg_flash_dev *dev, cyg_flashaddr_t addr, cyg_uint8* rbuf, cyg_uint32 rbuf_len) +{ + cyg_spi_device* spi_device = (cyg_spi_device*) dev->priv; + const cyg_uint8 tx_buf [5] = { M25PXX_CMD_FREAD, + (cyg_uint8) (addr >> 16), (cyg_uint8) (addr >> 8), (cyg_uint8) (addr), 0 }; + + // Implement the read operation as a multistage SPI transaction. + cyg_spi_transaction_begin (spi_device); + cyg_spi_transaction_transfer (spi_device, M25PXX_POLLED, 5, tx_buf, NULL, false); + cyg_spi_transaction_transfer (spi_device, M25PXX_POLLED, rbuf_len, NULL, rbuf, false); + cyg_spi_transaction_end (spi_device); +} + +//============================================================================= +// Standard Flash device API. All the following functions assume that a valid +// SPI device handle is passed in the 'priv' reference of the flash device +// data structure. +//============================================================================= + +//----------------------------------------------------------------------------- +// Initialise the SPI flash, reading back the flash parameters. + +static int m25pxx_init + (struct cyg_flash_dev *dev) +{ + m25pxx_params* dev_params = (m25pxx_params*) m25pxx_supported_devices; + cyg_uint32 device_id; + int retval = FLASH_ERR_INVALID; + + // Find the device in the supported devices list. + device_id = m25pxx_spi_rdid (dev); + while ((dev_params->jedec_id != 0) && (dev_params->jedec_id != device_id)) { + dev_params ++; + } + + // Found supported device - update device parameters. M25PXX devices have a + // uniform sector distribution, so only 1 block info record is required. + if (dev_params->jedec_id != 0) { + ASSERT_M25P (dev->num_block_infos == 1, "Only 1 block info record required."); + ASSERT_M25P (dev->block_info != NULL, "Null pointer to block info record."); + if ((dev->num_block_infos == 1) && (dev->block_info != NULL)) { + TRACE_M25P ("Init device with JEDEC ID 0x%06X.\n", device_id); + dev->end = dev->start + (M25PXX_PAGE_SIZE * (cyg_flashaddr_t) dev_params->sector_size * + (cyg_flashaddr_t) dev_params->sector_count) - 1; + + // Strictly speaking the block info fields are 'read only'. However, we + // have a legitimate reason for updating the contents here and can cast + // away the const. + ((cyg_flash_block_info_t*) dev->block_info)->block_size = + M25PXX_PAGE_SIZE * (size_t) dev_params->sector_size; + ((cyg_flash_block_info_t*) dev->block_info)->blocks = + (cyg_uint32) dev_params->sector_count; + retval = FLASH_ERR_OK; + } + } + return retval; +} + +//----------------------------------------------------------------------------- +// Erase a single sector of the flash. + +static int m25pxx_erase_block + (struct cyg_flash_dev *dev, cyg_flashaddr_t block_base) +{ + cyg_flashaddr_t local_base = block_base; + int retval = FLASH_ERR_INVALID; + cyg_uint8 dev_status; + + // Fix up the block address and send the sector erase command. + if (m25pxx_to_local_addr (dev, &local_base)) { + m25pxx_spi_wren (dev); + m25pxx_spi_se (dev, local_base); + + // Spin waiting for the erase to complete. This can take between 1 and 3 + // seconds, so we use a polling interval of 1/2 sec. + do { + M25PXX_DELAY_MS (500); + dev_status = m25pxx_spi_rdsr (dev); + } while (dev_status & M25PXX_STATUS_WIP); + + retval = FLASH_ERR_OK; + } + return retval; +} + +//----------------------------------------------------------------------------- +// Program an arbitrary number of pages into flash and verify written data. + +static int m25pxx_program + (struct cyg_flash_dev *dev, cyg_flashaddr_t base, const void* data, size_t len) +{ + cyg_flashaddr_t local_base = base; + int retval = FLASH_ERR_OK; + cyg_uint8* tx_ptr = (cyg_uint8*) data; + cyg_uint32 tx_bytes_left = (cyg_uint32) len; + cyg_uint32 tx_bytes; + cyg_uint8 dev_status; + + // Fix up the block address. + if (!m25pxx_to_local_addr (dev, &local_base)) { + retval = FLASH_ERR_INVALID; + goto out; + } + + // The start of the transaction may not be page aligned, so we need to work + // out how many bytes to transmit before we hit the first page boundary. + tx_bytes = M25PXX_PAGE_SIZE - (((cyg_uint32) local_base) & (M25PXX_PAGE_SIZE - 1)); + if (tx_bytes > tx_bytes_left) tx_bytes = tx_bytes_left; + + // Perform page program operations. + while (tx_bytes_left) { + m25pxx_spi_wren (dev); + m25pxx_spi_pp (dev, local_base, tx_ptr, tx_bytes); + + // Spin waiting for write to complete. This can take up to 5ms, so + // we use a polling interval of 1ms - which may get rounded up to the + // RTC tick granularity. + do { + M25PXX_DELAY_MS (1); + dev_status = m25pxx_spi_rdsr (dev); + } while (dev_status & M25PXX_STATUS_WIP); + + // Update counters and data pointers for the next page. + tx_bytes_left -= tx_bytes; + tx_ptr += tx_bytes; + local_base += tx_bytes; + tx_bytes = (tx_bytes_left > M25PXX_PAGE_SIZE) ? M25PXX_PAGE_SIZE : tx_bytes_left; + } + +out: + return retval; +} + +//----------------------------------------------------------------------------- +// Read back an arbitrary amount of data from flash. + +static int m25pxx_read + (struct cyg_flash_dev *dev, const cyg_flashaddr_t base, void* data, size_t len) +{ + cyg_flashaddr_t local_base = base; + int retval = FLASH_ERR_INVALID; + cyg_uint8* rx_ptr = (cyg_uint8*) data; + cyg_uint32 rx_bytes_left = (cyg_uint32) len; + cyg_uint32 rx_bytes; + + // Determine the maximum transfer size to use. + cyg_uint32 rx_block_size = (CYGNUM_DEVS_FLASH_SPI_M25PXX_READ_BLOCK_SIZE == 0) ? + 0xFFFFFFFF : CYGNUM_DEVS_FLASH_SPI_M25PXX_READ_BLOCK_SIZE; + + // Fix up the block address and fill the read buffer. + if (m25pxx_to_local_addr (dev, &local_base)) { + while (rx_bytes_left) { + rx_bytes = (rx_bytes_left < rx_block_size) ? rx_bytes_left : rx_block_size; + m25pxx_spi_fread (dev, local_base, rx_ptr, rx_bytes); + + // Update counters and data pointers for next read block. + rx_bytes_left -= rx_bytes; + rx_ptr += rx_bytes; + local_base += rx_bytes; + } + retval = FLASH_ERR_OK; + } + return retval; +} + +//============================================================================= +// Fill in the driver data structures. +//============================================================================= + +CYG_FLASH_FUNS ( + cyg_devs_flash_spi_m25pxx_funs, // Exported name of function pointers. + m25pxx_init, // Flash initialisation. + cyg_flash_devfn_query_nop, // Query operations not supported. + m25pxx_erase_block, // Sector erase. + m25pxx_program, // Program multiple pages. + m25pxx_read, // Read arbitrary amount of data. + cyg_flash_devfn_lock_nop, // Locking not supported (no per-sector locks). + cyg_flash_devfn_unlock_nop +); + +//=============================================================================
new file mode 100644 --- /dev/null +++ b/packages/devs/flash/spi/m25pxx/current/test/m25pxx_test.c @@ -0,0 +1,395 @@ +//============================================================================= +// +// m25pxx_test.c +// +// SPI flash driver tests for Numonyx M25Pxx devices and compatibles. +// +//============================================================================= +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008, 2009 Free Software Foundation, Inc. +// +// eCos 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 2 or (at your option) any later +// version. +// +// eCos 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 eCos; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// As a special exception, if other files instantiate templates or use +// macros or inline functions from this file, or you compile this file +// and link it with other works to produce a work based on this file, +// this file does not by itself cause the resulting work to be covered by +// the GNU General Public License. However the source code for this file +// must still be made available in accordance with section (3) of the GNU +// General Public License v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public License. +// ------------------------------------------- +// ####ECOSGPLCOPYRIGHTEND#### +//============================================================================= +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Chris Holgate +// Date: 2008-12-22 +// Purpose: Numonyx M25Pxx SPI flash driver stress tests. +// +//####DESCRIPTIONEND#### +// +//============================================================================= + +#include <pkgconf/system.h> + +#include <cyg/infra/cyg_type.h> +#include <cyg/infra/testcase.h> // Test macros +#include <cyg/infra/cyg_ass.h> // Assertion macros +#include <cyg/infra/diag.h> // Diagnostic output + +#include <cyg/hal/hal_arch.h> // CYGNUM_HAL_STACK_SIZE_TYPICAL +#include <cyg/hal/hal_if.h> +#include <cyg/kernel/kapi.h> + +#include <cyg/io/spi.h> +#include <cyg/io/flash.h> +#include <cyg/io/m25pxx.h> + +#include <string.h> + +#include CYGHWR_MEMORY_LAYOUT_H + +//----------------------------------------------------------------------------- +// Set up the SPI intertface on the ST STM3210E eval board for the STM32. The +// M25PXX device is connected to SPI bus 1. To configure the board for this +// test, jumper JP3 must be open and SPI1 should be set up in configtool so +// that chip select 1 corresponds to GPIO number 18 (PB2) and 50MHz I/O is +// enabled. If the data area is in external RAM, SPI1 must be configured with +// bounce buffers of at least 256 bytes and the M25PXX read data block size +// should be set to the same size as the bounce buffers. + +#ifdef CYGPKG_HAL_CORTEXM_STM32_STM3210E_EVAL +#include <cyg/io/spi_stm32.h> + +CYG_DEVS_SPI_CORTEXM_STM32_DEVICE ( + m25pxx_spi_device, 1, 0, false, 0, 0, 25000000, 1, 1, 1 +); + +#endif + +//----------------------------------------------------------------------------- +// Instantiate the M25Pxx device driver. + +CYG_DEVS_FLASH_SPI_M25PXX_DRIVER ( + m25pxx_flash_device, 0, &m25pxx_spi_device +); + +//----------------------------------------------------------------------------- +// Thread data structures. + +static cyg_uint32 stack [CYGNUM_HAL_STACK_SIZE_TYPICAL / 4]; +static cyg_thread thread_data; +static cyg_handle_t thread_handle; + +//----------------------------------------------------------------------------- +// Data transfer buffers. + +#define BUF_SIZE 1024 + +static cyg_uint8 wbuf [BUF_SIZE]; +static cyg_uint8 rbuf [BUF_SIZE]; + +//----------------------------------------------------------------------------- +// Print out version information. + +void do_version (void) +{ + char *version = CYGACC_CALL_IF_MONITOR_VERSION(); + if (version != NULL) diag_printf("%s", version); +#ifdef HAL_PLATFORM_CPU + diag_printf("Platform: %s (%s) %s\n", HAL_PLATFORM_BOARD, HAL_PLATFORM_CPU, HAL_PLATFORM_EXTRA); +#endif + diag_printf("RAM: %p-%p, ", (void*)(CYGMEM_REGION_ram), (void*)(CYGMEM_REGION_ram + CYGMEM_REGION_ram_SIZE - 1)); + diag_printf("(HEAP: %p-%p)\n", (void*)(CYGMEM_SECTION_heap1), (void*)(CYGMEM_SECTION_heap1 + CYGMEM_SECTION_heap1_SIZE - 1)); +} + +//----------------------------------------------------------------------------- +// Run checkerboard and inverse checkerboard writes over each sector in turn. + +cyg_uint32 run_test_1 + (void) +{ + int status; + cyg_uint32 i, j; + cyg_uint32 errors = 0; + cyg_flashaddr_t base_addr, err_addr; + + diag_printf ("Test 1 - write and verify checkerboard and inverse checkerboard.\n"); + + // Iterate over all flash sectors. + for (i = 0; i < m25pxx_flash_device.block_info->blocks; i++) { + base_addr = m25pxx_flash_device.start + (i * m25pxx_flash_device.block_info->block_size); + + // Erase block. + status = cyg_flash_erase (base_addr, 1, &err_addr); + if (status != FLASH_ERR_OK) { + diag_printf ("Flash erase error : %s\n", cyg_flash_errmsg (status)); + errors ++; + } + + // Set up buffer with checkerboard. + for (j = 0; j < BUF_SIZE;) { + wbuf [j++] = 0x55; + wbuf [j++] = 0xAA; + } + + // Write the checkerboard to the entire sector. + for (j = 0; j < m25pxx_flash_device.block_info->block_size; j += BUF_SIZE) { + status = cyg_flash_program (base_addr + j, wbuf, BUF_SIZE, &err_addr); + if (status != FLASH_ERR_OK) { + diag_printf ("Flash write error : %s\n", cyg_flash_errmsg (status)); + errors ++; + } + } + + // Read back the checkerboard and verify. + for (j = 0; j < m25pxx_flash_device.block_info->block_size; j += BUF_SIZE) { + status = cyg_flash_read (base_addr + j, rbuf, BUF_SIZE, &err_addr); + if (status != FLASH_ERR_OK) { + diag_printf ("Flash read error : %s\n", cyg_flash_errmsg (status)); + errors ++; + } + else if (memcmp (rbuf, wbuf, BUF_SIZE) != 0) { + diag_printf ("Flash read data corruption (0x%08X - 0x%08X).\n", base_addr + j, base_addr + j + BUF_SIZE - 1); + errors ++; + } + } + + // Erase block. + status = cyg_flash_erase (base_addr, 1, &err_addr); + if (status != FLASH_ERR_OK) { + diag_printf ("Flash erase error : %s\n", cyg_flash_errmsg (status)); + errors ++; + } + + // Set up buffer with inverse checkerboard. + for (j = 0; j < BUF_SIZE;) { + wbuf [j++] = 0xAA; + wbuf [j++] = 0x55; + } + + // Write the checkerboard to the entire sector. + for (j = 0; j < m25pxx_flash_device.block_info->block_size; j += BUF_SIZE) { + status = cyg_flash_program (base_addr + j, wbuf, BUF_SIZE, &err_addr); + if (status != FLASH_ERR_OK) { + diag_printf ("Flash write error : %s\n", cyg_flash_errmsg (status)); + errors ++; + } + } + + // Read back the checkerboard and verify. + for (j = 0; j < m25pxx_flash_device.block_info->block_size; j += BUF_SIZE) { + status = cyg_flash_read (base_addr + j, rbuf, BUF_SIZE, &err_addr); + if (status != FLASH_ERR_OK) { + diag_printf ("Flash read error : %s\n", cyg_flash_errmsg (status)); + errors ++; + } + else if (memcmp (rbuf, wbuf, BUF_SIZE) != 0) { + diag_printf ("Flash read data corruption (0x%08X - 0x%08X).\n", base_addr + j, base_addr + j + BUF_SIZE - 1); + errors ++; + } + } + } + return errors; +} + +//----------------------------------------------------------------------------- +// Write and verify counting sequence over all device. + +cyg_uint32 run_test_2 + (void) +{ + int status; + cyg_uint32 i, j; + cyg_uint32 errors = 0; + cyg_flashaddr_t base_addr, err_addr; + + diag_printf ("Test 2 - write and verify counting sequence.\n"); + + // Erase all flash sectors. + for (i = 0; i < m25pxx_flash_device.block_info->blocks; i++) { + base_addr = m25pxx_flash_device.start + (i * m25pxx_flash_device.block_info->block_size); + + // Erase block. + status = cyg_flash_erase (base_addr, 1, &err_addr); + if (status != FLASH_ERR_OK) { + diag_printf ("Flash erase error : %s\n", cyg_flash_errmsg (status)); + errors ++; + } + } + + // Write counting sequence. + base_addr = m25pxx_flash_device.start; + for (i = 0; i < (m25pxx_flash_device.end - m25pxx_flash_device.start) / 4;) { + for (j = 0; j < BUF_SIZE;) { + wbuf [j++] = (cyg_uint8) ((i >> 0) & 0xFF); + wbuf [j++] = (cyg_uint8) ((i >> 8) & 0xFF); + wbuf [j++] = (cyg_uint8) ((i >> 16) & 0xFF); + wbuf [j++] = (cyg_uint8) ((i >> 24) & 0xFF); + i++; + } + status = cyg_flash_program (base_addr, wbuf, BUF_SIZE, &err_addr); + if (status != FLASH_ERR_OK) { + diag_printf ("Flash write error : %s\n", cyg_flash_errmsg (status)); + errors ++; + } + base_addr += BUF_SIZE; + } + + // Verify counting sequence. + base_addr = m25pxx_flash_device.start; + for (i = 0; i < (m25pxx_flash_device.end - m25pxx_flash_device.start) / 4;) { + for (j = 0; j < BUF_SIZE;) { + wbuf [j++] = (cyg_uint8) ((i >> 0) & 0xFF); + wbuf [j++] = (cyg_uint8) ((i >> 8) & 0xFF); + wbuf [j++] = (cyg_uint8) ((i >> 16) & 0xFF); + wbuf [j++] = (cyg_uint8) ((i >> 24) & 0xFF); + i++; + } + status = cyg_flash_read (base_addr, rbuf, BUF_SIZE, &err_addr); + if (status != FLASH_ERR_OK) { + diag_printf ("Flash read error : %s\n", cyg_flash_errmsg (status)); + errors ++; + } + else if (memcmp (rbuf, wbuf, BUF_SIZE) != 0) { + diag_printf ("Flash read data corruption (0x%08X - 0x%08X).\n", base_addr, base_addr + BUF_SIZE - 1); + errors ++; + } + base_addr += BUF_SIZE; + } + return errors; +} + +//----------------------------------------------------------------------------- +// Perform non-aligned buffer read/write tests, spanning sector boundaries. + +cyg_uint32 run_test_3 + (void) +{ + int status; + cyg_uint32 i, count; + cyg_uint32 errors = 0; + cyg_flashaddr_t base_addr, err_addr; + + diag_printf ("Test 3 - test non-aligned writes and reads.\n"); + + // Fill the write buffer with a basic counting sequence. + count = 0; + for (i = 0; i < BUF_SIZE;) { + wbuf [i++] = (cyg_uint8) ((count >> 0) & 0xFF); + wbuf [i++] = (cyg_uint8) ((count >> 8) & 0xFF); + count++; + } + + // Assuming 256 byte pages gives 256 possible alignments. + base_addr = m25pxx_flash_device.start + m25pxx_flash_device.block_info->block_size; + for (i = 1; i <= 256; i++) { + + // Erase sectors either side of sector boundary. + status = cyg_flash_erase (base_addr - 1, 1, &err_addr); + if (status != FLASH_ERR_OK) { + diag_printf ("Flash erase error : %s\n", cyg_flash_errmsg (status)); + errors ++; + } + status = cyg_flash_erase (base_addr, 1, &err_addr); + if (status != FLASH_ERR_OK) { + diag_printf ("Flash erase error : %s\n", cyg_flash_errmsg (status)); + errors ++; + } + + // Write data spanning sector boundary. + status = cyg_flash_program (base_addr - i, wbuf, BUF_SIZE, &err_addr); + if (status != FLASH_ERR_OK) { + diag_printf ("Flash write error : %s\n", cyg_flash_errmsg (status)); + errors ++; + } + + // Verify data spanning sector boundary. + status = cyg_flash_read (base_addr - i, rbuf, BUF_SIZE, &err_addr); + if (status != FLASH_ERR_OK) { + diag_printf ("Flash read error : %s\n", cyg_flash_errmsg (status)); + errors ++; + } + else if (memcmp (rbuf, wbuf, BUF_SIZE) != 0) { + diag_printf ("Flash read data corruption (0x%08X - 0x%08X).\n", base_addr - i, base_addr - i + BUF_SIZE - 1); + errors ++; + } + + // Use next sector boundary for next test. + base_addr += m25pxx_flash_device.block_info->block_size; + if (base_addr >= m25pxx_flash_device.end) + base_addr = m25pxx_flash_device.start + m25pxx_flash_device.block_info->block_size; + } + return errors; +} + +//----------------------------------------------------------------------------- +// Run all M25Pxx SPI interface loopback tests. + +void run_tests + (void) +{ + cyg_uint32 errors = 0; + + // Check that the device is intialised. + if (m25pxx_flash_device.start == m25pxx_flash_device.end) { + diag_printf ("M25Pxx device not initialised.\n"); + errors ++; + goto out; + } + + // Run the tests. + errors += run_test_1 (); + errors += run_test_2 (); + errors += run_test_3 (); + +out: + diag_printf ("----\nTests complete - detected %d errors in total.\n", errors); + if (errors == 0) + CYG_TEST_PASS_FINISH ("M25Pxx driver tests ran OK."); + else + CYG_TEST_FAIL_FINISH ("M25Pxx driver test FAILED."); +} + +//----------------------------------------------------------------------------- +// User startup - tests are run in their own thread. + +void cyg_user_start + (void) +{ + do_version(); + diag_printf ("----\nChecking for M25Pxx compatible devices.\n"); + cyg_flash_init (&diag_printf); + CYG_TEST_INIT(); + cyg_thread_create( + 10, // Arbitrary priority + (cyg_thread_entry_t*) run_tests, // Thread entry point + 0, // + "test_thread", // Thread name + (cyg_uint8*) stack, // Stack + CYGNUM_HAL_STACK_SIZE_TYPICAL, // Stack size + &thread_handle, // Thread handle + &thread_data // Thread data structure + ); + cyg_thread_resume(thread_handle); + cyg_scheduler_start(); +} + +//=============================================================================
