Resync new repository with old repo r5166

git-svn-id: http://svn.code.sf.net/p/nuttx/code/trunk@5153 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
patacongo
2012-09-17 18:18:44 +00:00
commit 57623d42eb
5691 changed files with 1361853 additions and 0 deletions
+68
View File
@@ -0,0 +1,68 @@
#
# For a description of the syntax of this configuration file,
# see misc/tools/kconfig-language.txt.
#
config MTD_AT24XX
bool "I2C-based AT24XX eeprom"
default n
select I2C
config AT24XX_SIZE
int "at24xx size(kByte)"
default 64
depends on MTD_AT24XX
config AT24XX_ADDR
hex "at24xx i2c address"
default 0x50
depends on MTD_AT24XX
config MTD_AT45DB
bool "SPI-based AT45DB flash"
default n
select SPI
config AT45DB_FREQUENCY
int "at45db frequency"
default 1000000
depends on MTD_AT45DB
config AT45DB_PREWAIT
bool "enables higher performance write logic"
default y
depends on MTD_AT45DB
config AT45DB_PWRSAVE
bool "enables power save"
default n
depends on MTD_AT45DB
config MTD_MP25P
bool "SPI-based M25P1 falsh"
default n
select SPI
config MP25P_SPIMODE
int "mp25p spi mode"
default 0
depends on MTD_MP25P
config MP25P_MANUFACTURER
hex "mp25p manufacturers ID"
default 0x20
depends on MTD_MP25P
---help---
Various manufacturers may have produced the parts. 0x20 is the manufacturer ID
for the STMicro MP25x serial FLASH. If, for example, you are using the a Macronix
International MX25 serial FLASH, the correct manufacturer ID would be 0xc2.
config MTD_RAMTRON
bool "SPI-based RAMTRON NVRAM Devices FM25V10"
default n
select SPI
---help---
SPI-based RAMTRON NVRAM Devices FM25V10
config MTD_RAM
bool "Memory bus ram"
default n
+54
View File
@@ -0,0 +1,54 @@
############################################################################
# drivers/mtd/Make.defs
# These driver supports various Memory Technology Devices (MTD) using the
# NuttX MTD interface.
#
# Copyright (C) 2009-2012 Gregory Nutt. All rights reserved.
# Author: Gregory Nutt <gnutt@nuttx.org>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# 3. Neither the name NuttX nor the names of its contributors may be
# used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
############################################################################
# Include MTD drivers
CSRCS += at45db.c flash_eraseall.c ftl.c m25px.c rammtd.c ramtron.c
ifeq ($(CONFIG_MTD_AT24XX),y)
CSRCS += at24xx.c
endif
ifeq ($(CONFIG_MTD_SST25),y)
CSRCS += sst25.c
endif
# Include MTD driver support
DEPPATH += --dep-path mtd
VPATH += :mtd
+429
View File
@@ -0,0 +1,429 @@
/************************************************************************************
* drivers/mtd/at24xx.c
* Driver for I2C-based at24cxx EEPROM(at24c32,at24c64,at24c128,at24c256)
*
* Copyright (C) 2011 Li Zhuoyi. All rights reserved.
* Author: Li Zhuoyi <lzyy.cn@gmail.com>
* History: 0.1 2011-08-20 initial version
*
* 2011-11-1 Added support for larger MTD block sizes: Hal Glenn <hglenn@2g-eng.com>
*
* Derived from drivers/mtd/m25px.c
*
* Copyright (C) 2009-2011 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
************************************************************************************/
/************************************************************************************
* Included Files
************************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/kmalloc.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/i2c.h>
#include <nuttx/mtd.h>
#ifdef CONFIG_MTD_AT24XX
/************************************************************************************
* Pre-processor Definitions
************************************************************************************/
/* As a minimum, the size of the AT24 part and its 7-bit I2C address are required. */
#ifndef CONFIG_AT24XX_SIZE
# warning "Assuming AT24 size 64"
# define CONFIG_AT24XX_SIZE 64
#endif
#ifndef CONFIG_AT24XX_ADDR
# warning "Assuming AT24 address of 0x50"
# define CONFIG_AT24XX_ADDR 0x50
#endif
/* Get the part configuration based on the size configuration */
#if CONFIG_AT24XX_SIZE == 32
# define AT24XX_NPAGES 128
# define AT24XX_PAGESIZE 32
#elif CONFIG_AT24XX_SIZE == 48
# define AT24XX_NPAGES 192
# define AT24XX_PAGESIZE 32
#elif CONFIG_AT24XX_SIZE == 64
# define AT24XX_NPAGES 256
# define AT24XX_PAGESIZE 32
#elif CONFIG_AT24XX_SIZE == 128
# define AT24XX_NPAGES 256
# define AT24XX_PAGESIZE 64
#elif CONFIG_AT24XX_SIZE == 256
# define AT24XX_NPAGES 512
# define AT24XX_PAGESIZE 64
#endif
/* For applications where a file system is used on the AT24, the tiny page sizes
* will result in very inefficient FLASH usage. In such cases, it is better if
* blocks are comprised of "clusters" of pages so that the file system block
* size is, say, 256 or 512 bytes. In any event, the block size *must* be an
* even multiple of the pages.
*/
#ifndef CONFIG_AT24XX_MTD_BLOCKSIZE
# warning "Assuming driver block size is the same as the FLASH page size"
# define CONFIG_AT24XX_MTD_BLOCKSIZE AT24XX_PAGESIZE
#endif
/************************************************************************************
* Private Types
************************************************************************************/
/* This type represents the state of the MTD device. The struct mtd_dev_s
* must appear at the beginning of the definition so that you can freely
* cast between pointers to struct mtd_dev_s and struct at24c_dev_s.
*/
struct at24c_dev_s
{
struct mtd_dev_s mtd; /* MTD interface */
FAR struct i2c_dev_s *dev; /* Saved I2C interface instance */
uint8_t addr; /* I2C address */
uint16_t pagesize; /* 32, 63 */
uint16_t npages; /* 128, 256, 512, 1024 */
};
/************************************************************************************
* Private Function Prototypes
************************************************************************************/
/* MTD driver methods */
static int at24c_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks);
static ssize_t at24c_bread(FAR struct mtd_dev_s *dev, off_t startblock,
size_t nblocks, FAR uint8_t *buf);
static ssize_t at24c_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
size_t nblocks, FAR const uint8_t *buf);
static ssize_t at24c_read(FAR struct mtd_dev_s *dev, off_t offset,
size_t nbytes,FAR uint8_t *buffer);
static int at24c_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg);
/************************************************************************************
* Private Data
************************************************************************************/
/* At present, only a signal AT24 part is supported. In this case, a statically
* allocated state structure may be used.
*/
static struct at24c_dev_s g_at24c;
/************************************************************************************
* Private Functions
************************************************************************************/
static int at24c_eraseall(FAR struct at24c_dev_s *priv)
{
int startblock = 0;
uint8_t buf[AT24XX_PAGESIZE + 2];
memset(&buf[2],0xff,priv->pagesize);
I2C_SETADDRESS(priv->dev,priv->addr,7);
I2C_SETFREQUENCY(priv->dev,100000);
for (startblock = 0; startblock < priv->npages; startblock++)
{
uint16_t offset = startblock * priv->pagesize;
buf[1] = offset & 0xff;
buf[0] = (offset >> 8) & 0xff;
while (I2C_WRITE(priv->dev, buf, 2) < 0)
{
usleep(1000);
}
I2C_WRITE(priv->dev, buf, priv->pagesize + 2);
}
return OK;
}
/************************************************************************************
* Name: at24c_erase
************************************************************************************/
static int at24c_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks)
{
/* EEprom need not erase */
return (int)nblocks;
}
/************************************************************************************
* Name: at24c_bread
************************************************************************************/
static ssize_t at24c_bread(FAR struct mtd_dev_s *dev, off_t startblock,
size_t nblocks, FAR uint8_t *buffer)
{
FAR struct at24c_dev_s *priv = (FAR struct at24c_dev_s *)dev;
size_t blocksleft;
#if CONFIG_AT24XX_MTD_BLOCKSIZE > AT24XX_PAGESIZE
startblock *= (CONFIG_AT24XX_MTD_BLOCKSIZE / AT24XX_PAGESIZE);
nblocks *= (CONFIG_AT24XX_MTD_BLOCKSIZE / AT24XX_PAGESIZE);
#endif
blocksleft = nblocks;
fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
if (startblock >= priv->npages)
{
return 0;
}
if (startblock + nblocks > priv->npages)
{
nblocks = priv->npages - startblock;
}
I2C_SETADDRESS(priv->dev,priv->addr,7);
I2C_SETFREQUENCY(priv->dev,100000);
while (blocksleft-- > 0)
{
uint16_t offset = startblock * priv->pagesize;
uint8_t buf[2];
buf[1] = offset & 0xff;
buf[0] = (offset >> 8) & 0xff;
while (I2C_WRITE(priv->dev, buf, 2) < 0)
{
fvdbg("wait\n");
usleep(1000);
}
I2C_READ(priv->dev, buffer, priv->pagesize);
startblock++;
buffer += priv->pagesize;
}
#if CONFIG_AT24XX_MTD_BLOCKSIZE > AT24XX_PAGESIZE
return nblocks / (CONFIG_AT24XX_MTD_BLOCKSIZE / AT24XX_PAGESIZE);
#else
return nblocks;
#endif
}
/************************************************************************************
* Name: at24c_bwrite
*
* Operates on MTD block's and translates to FLASH pages
*
************************************************************************************/
static ssize_t at24c_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks,
FAR const uint8_t *buffer)
{
FAR struct at24c_dev_s *priv = (FAR struct at24c_dev_s *)dev;
size_t blocksleft;
uint8_t buf[AT24XX_PAGESIZE+2];
#if CONFIG_AT24XX_MTD_BLOCKSIZE > AT24XX_PAGESIZE
startblock *= (CONFIG_AT24XX_MTD_BLOCKSIZE / AT24XX_PAGESIZE);
nblocks *= (CONFIG_AT24XX_MTD_BLOCKSIZE / AT24XX_PAGESIZE);
#endif
blocksleft = nblocks;
if (startblock >= priv->npages)
{
return 0;
}
if (startblock + nblocks > priv->npages)
{
nblocks = priv->npages - startblock;
}
fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
I2C_SETADDRESS(priv->dev, priv->addr, 7);
I2C_SETFREQUENCY(priv->dev, 100000);
while (blocksleft-- > 0)
{
uint16_t offset = startblock * priv->pagesize;
while (I2C_WRITE(priv->dev, (uint8_t *)&offset, 2) < 0)
{
fvdbg("wait\n");
usleep(1000);
}
buf[1] = offset & 0xff;
buf[0] = (offset >> 8) & 0xff;
memcpy(&buf[2], buffer, priv->pagesize);
I2C_WRITE(priv->dev, buf, priv->pagesize + 2);
startblock++;
buffer += priv->pagesize;
}
#if CONFIG_AT24XX_MTD_BLOCKSIZE > AT24XX_PAGESIZE
return nblocks / (CONFIG_AT24XX_MTD_BLOCKSIZE / AT24XX_PAGESIZE);
#else
return nblocks;
#endif
}
/************************************************************************************
* Name: at24c_ioctl
************************************************************************************/
static int at24c_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg)
{
FAR struct at24c_dev_s *priv = (FAR struct at24c_dev_s *)dev;
int ret = -EINVAL; /* Assume good command with bad parameters */
fvdbg("cmd: %d \n", cmd);
switch (cmd)
{
case MTDIOC_GEOMETRY:
{
FAR struct mtd_geometry_s *geo = (FAR struct mtd_geometry_s *)((uintptr_t)arg);
if (geo)
{
/* Populate the geometry structure with information need to know
* the capacity and how to access the device.
*
* NOTE: that the device is treated as though it where just an array
* of fixed size blocks. That is most likely not true, but the client
* will expect the device logic to do whatever is necessary to make it
* appear so.
*
* blocksize:
* May be user defined. The block size for the at24XX devices may be
* larger than the page size in order to better support file systems.
* The read and write functions translate BLOCKS to pages for the
* small flash devices
* erasesize:
* It has to be at least as big as the blocksize, bigger serves no
* purpose.
* neraseblocks
* Note that the device size is in kilobits and must be scaled by
* 1024 / 8
*/
#if CONFIG_AT24XX_MTD_BLOCKSIZE > AT24XX_PAGESIZE
geo->blocksize = CONFIG_AT24XX_MTD_BLOCKSIZE;
geo->erasesize = CONFIG_AT24XX_MTD_BLOCKSIZE;
geo->neraseblocks = (CONFIG_AT24XX_SIZE * 1024 / 8) / CONFIG_AT24XX_MTD_BLOCKSIZE;
#else
geo->blocksize = priv->pagesize;
geo->erasesize = priv->pagesize;
geo->neraseblocks = priv->npages;
#endif
ret = OK;
fvdbg("blocksize: %d erasesize: %d neraseblocks: %d\n",
geo->blocksize, geo->erasesize, geo->neraseblocks);
}
}
break;
case MTDIOC_BULKERASE:
ret=at24c_eraseall(priv);
break;
case MTDIOC_XIPBASE:
default:
ret = -ENOTTY; /* Bad command */
break;
}
return ret;
}
/************************************************************************************
* Public Functions
************************************************************************************/
/************************************************************************************
* Name: at24c_initialize
*
* Description:
* Create an initialize MTD device instance. MTD devices are not registered
* in the file system, but are created as instances that can be bound to
* other functions (such as a block or character driver front end).
*
************************************************************************************/
FAR struct mtd_dev_s *at24c_initialize(FAR struct i2c_dev_s *dev)
{
FAR struct at24c_dev_s *priv;
fvdbg("dev: %p\n", dev);
/* Allocate a state structure (we allocate the structure instead of using
* a fixed, static allocation so that we can handle multiple FLASH devices.
* The current implementation would handle only one FLASH part per I2C
* device (only because of the SPIDEV_FLASH definition) and so would have
* to be extended to handle multiple FLASH parts on the same I2C bus.
*/
priv = &g_at24c;
if (priv)
{
/* Initialize the allocated structure */
priv->addr = CONFIG_AT24XX_ADDR;
priv->pagesize = AT24XX_PAGESIZE;
priv->npages = AT24XX_NPAGES;
priv->mtd.erase = at24c_erase;
priv->mtd.bread = at24c_bread;
priv->mtd.bwrite = at24c_bwrite;
priv->mtd.ioctl = at24c_ioctl;
priv->dev = dev;
}
/* Return the implementation-specific state structure as the MTD device */
fvdbg("Return %p\n", priv);
return (FAR struct mtd_dev_s *)priv;
}
#endif
+899
View File
@@ -0,0 +1,899 @@
/************************************************************************************
* drivers/mtd/at45db.c
* Driver for SPI-based AT45DB161D (16Mbit)
*
* Copyright (C) 2010-2011 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
************************************************************************************/
/* Ordering Code Detail:
*
* AT 45DB 16 1 D – SS U
* | | | | | | `- Device grade
* | | | | | `- Package Option
* | | | | `- Device revision
* | | | `- Interface: 1=serial
* | | `- Capacity: 16=16Mbit
* | `- Product family
* `- Atmel designator
*/
/************************************************************************************
* Included Files
************************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/kmalloc.h>
#include <nuttx/arch.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/spi.h>
#include <nuttx/mtd.h>
/************************************************************************************
* Pre-processor Definitions
************************************************************************************/
/* Configuration ********************************************************************/
/* CONFIG_AT45DB_PREWAIT enables higher performance write logic: We leave the chip
* busy after write and erase operations. This improves write and erase performance
* because we do not have to wait as long between transactions (other processing can
* occur while the chip is busy) but means that the chip must stay powered:
*/
#if defined(CONFIG_AT45DB_PWRSAVE) && defined(CONFIG_AT45DB_PREWAIT)
# error "Both CONFIG_AT45DB_PWRSAVE and CONFIG_AT45DB_PREWAIT are defined"
#endif
/* If the user has provided no frequency, use 1MHz */
#ifndef CONFIG_AT45DB_FREQUENCY
# define CONFIG_AT45DB_FREQUENCY 1000000
#endif
/* SPI Commands *********************************************************************/
/* Read commands */
#define AT45DB_RDMN 0xd2 /* Main Memory Page Read */
#define AT45DB_RDARRY 0xe8 /* Continuous Array Read (Legacy Command) */
#define AT45DB_RDARRAYLF 0x03 /* Continuous Array Read (Low Frequency) */
#define AT45DB_RDARRAYHF 0x0b /* Continuous Array Read (High Frequency) */
#define AT45DB_RDBF1LF 0xd1 /* Buffer 1 Read (Low Frequency) */
#define AT45DB_RDBF2LF 0xd3 /* Buffer 2 Read (Low Frequency) */
#define AT45DB_RDBF1 0xd4 /* Buffer 1 Read */
#define AT45DB_RDBF2 0xd6 /* Buffer 2 Read */
/* Program and Erase Commands */
#define AT45DB_WRBF1 0x84 /* Buffer 1 Write */
#define AT45DB_WRBF2 0x87 /* Buffer 2 Write */
#define AT45DB_BF1TOMNE 0x83 /* Buffer 1 to Main Memory Page Program with Built-in Erase */
#define AT45DB_BF2TOMNE 0x86 /* Buffer 2 to Main Memory Page Program with Built-in Erase */
#define AT45DB_BF1TOMN 0x88 /* Buffer 1 to Main Memory Page Program without Built-in Erase */
#define AT45DB_BF2TOMN 0x89 /* Buffer 2 to Main Memory Page Program without Built-in Erase */
#define AT45DB_PGERASE 0x81 /* Page Erase */
#define AT45DB_BLKERASE 0x50 /* Block Erase */
#define AT45DB_SECTERASE 0x7c /* Sector Erase */
#define AT45DB_CHIPERASE1 0xc7 /* Chip Erase - byte 1 */
# define AT45DB_CHIPERASE2 0x94 /* Chip Erase - byte 2 */
# define AT45DB_CHIPERASE3 0x80 /* Chip Erase - byte 3 */
# define AT45DB_CHIPERASE4 0x9a /* Chip Erase - byte 4 */
#define AT45DB_MNTHRUBF1 0x82 /* Main Memory Page Program Through Buffer 1 */
#define AT45DB_MNTHRUBF2 0x85 /* Main Memory Page Program Through Buffer 2 */
/* Protection and Security Commands */
#define AT45DB_ENABPROT1 0x3d /* Enable Sector Protection - byte 1 */
# define AT45DB_ENABPROT2 0x2a /* Enable Sector Protection - byte 2 */
# define AT45DB_ENABPROT3 0x7f /* Enable Sector Protection - byte 3 */
# define AT45DB_ENABPROT4 0xa9 /* Enable Sector Protection - byte 4 */
#define AT45DB_DISABPROT1 0x3d /* Disable Sector Protection - byte 1 */
# define AT45DB_DISABPROT2 0x2a /* Disable Sector Protection - byte 2 */
# define AT45DB_DISABPROT3 0x7f /* Disable Sector Protection - byte 3 */
# define AT45DB_DISABPROT4 0x9a /* Disable Sector Protection - byte 4 */
#define AT45DB_ERASEPROT1 0x3d /* Erase Sector Protection Register - byte 1 */
# define AT45DB_ERASEPROT2 0x2a /* Erase Sector Protection Register - byte 2 */
# define AT45DB_ERASEPROT3 0x7f /* Erase Sector Protection Register - byte 3 */
# define AT45DB_ERASEPROT4 0xcf /* Erase Sector Protection Register - byte 4 */
#define AT45DB_PROGPROT1 0x3d /* Program Sector Protection Register - byte 1 */
# define AT45DB_PROGPROT2 0x2a /* Program Sector Protection Register - byte 2 */
# define AT45DB_PROGPROT3 0x7f /* Program Sector Protection Register - byte 3 */
# define AT45DB_PROGPROT4 0xfc /* Program Sector Protection Register - byte 4 */
#define AT45DB_RDPROT 0x32 /* Read Sector Protection Register */
#define AT45DB_LOCKDOWN1 0x3d /* Sector Lockdown - byte 1 */
# define AT45DB_LOCKDOWN2 0x2a /* Sector Lockdown - byte 2 */
# define AT45DB_LOCKDOWN3 0x7f /* Sector Lockdown - byte 3 */
# define AT45DB_LOCKDOWN4 0x30 /* Sector Lockdown - byte 4 */
#define AT45DB_RDLOCKDOWN 0x35 /* Read Sector Lockdown Register */
#define AT45DB_PROGSEC1 0x9b /* Program Security Register - byte 1 */
# define AT45DB_PROGSEC2 0x00 /* Program Security Register - byte 2 */
# define AT45DB_PROGSEC3 0x00 /* Program Security Register - byte 3 */
# define AT45DB_PROGSEC4 0x00 /* Program Security Register - byte 4 */
#define AT45DB_RDSEC 0x77 /* Read Security Register */
/* Additional commands */
#define AT45DB_MNTOBF1XFR 0x53 /* Main Memory Page to Buffer 1 Transfer */
#define AT45DB_MNTOBF2XFR 0x55 /* Main Memory Page to Buffer 2 Transfer */
#define AT45DB_MNBF1CMP 0x60 /* Main Memory Page to Buffer 1 Compare */
#define AT45DB_MNBF2CMP 0x61 /* Main Memory Page to Buffer 2 Compare */
#define AT45DB_AUTOWRBF1 0x58 /* Auto Page Rewrite through Buffer 1 */
#define AT45DB_AUTOWRBF2 0x59 /* Auto Page Rewrite through Buffer 2 */
#define AT45DB_PWRDOWN 0xb9 /* Deep Power-down */
#define AT45DB_RESUME 0xab /* Resume from Deep Power-down */
#define AT45DB_RDSR 0xd7 /* Status Register Read */
#define AT45DB_RDDEVID 0x9f /* Manufacturer and Device ID Read */
#define AT45DB_MANUFACTURER 0x1f /* Manufacturer ID: Atmel */
#define AT45DB_DEVID1_CAPMSK 0x1f /* Bits 0-4: Capacity */
#define AT45DB_DEVID1_1MBIT 0x02 /* xxx0 0010 = 1Mbit AT45DB011 */
#define AT45DB_DEVID1_2MBIT 0x03 /* xxx0 0012 = 2Mbit AT45DB021 */
#define AT45DB_DEVID1_4MBIT 0x04 /* xxx0 0100 = 4Mbit AT45DB041 */
#define AT45DB_DEVID1_8MBIT 0x05 /* xxx0 0101 = 8Mbit AT45DB081 */
#define AT45DB_DEVID1_16MBIT 0x06 /* xxx0 0110 = 16Mbit AT45DB161 */
#define AT45DB_DEVID1_32MBIT 0x07 /* xxx0 0111 = 32Mbit AT45DB321 */
#define AT45DB_DEVID1_64MBIT 0x08 /* xxx0 1000 = 32Mbit AT45DB641 */
#define AT45DB_DEVID1_FAMMSK 0xe0 /* Bits 5-7: Family */
#define AT45DB_DEVID1_DFLASH 0x20 /* 001x xxxx = Dataflash */
#define AT45DB_DEVID1_AT26DF 0x40 /* 010x xxxx = AT26DFxxx series (Not supported) */
#define AT45DB_DEVID2_VERMSK 0x1f /* Bits 0-4: MLC mask */
#define AT45DB_DEVID2_MLCMSK 0xe0 /* Bits 5-7: MLC mask */
/* Status register bit definitions */
#define AT45DB_SR_RDY (1 << 7) /* Bit 7: RDY/ Not BUSY */
#define AT45DB_SR_COMP (1 << 6) /* Bit 6: COMP */
#define AT45DB_SR_PROTECT (1 << 1) /* Bit 1: PROTECT */
#define AT45DB_SR_PGSIZE (1 << 0) /* Bit 0: PAGE_SIZE */
/* 1 Block = 16 pages; 1 sector = 256 pages */
#define PG_PER_BLOCK (16)
#define PG_PER_SECTOR (256)
/************************************************************************************
* Private Types
************************************************************************************/
/* This type represents the state of the MTD device. The struct mtd_dev_s
* must appear at the beginning of the definition so that you can freely
* cast between pointers to struct mtd_dev_s and struct at45db_dev_s.
*/
struct at45db_dev_s
{
struct mtd_dev_s mtd; /* MTD interface */
FAR struct spi_dev_s *spi; /* Saved SPI interface instance */
uint8_t pageshift; /* log2 of the page size (eg. 1 << 9 = 512) */
uint32_t npages; /* Number of pages in the device */
};
/************************************************************************************
* Private Function Prototypes
************************************************************************************/
/* Lock and per-transaction configuration */
static void at45db_lock(struct at45db_dev_s *priv);
static inline void at45db_unlock(struct at45db_dev_s *priv);
/* Power management */
#ifdef CONFIG_AT45DB_PWRSAVE
static void at45db_pwrdown(struct at45db_dev_s *priv);
static void at45db_resume(struct at45db_dev_s *priv);
#else
# define at45db_pwrdown(priv)
# define at45db_resume(priv)
#endif
/* Low-level AT45DB Helpers */
static inline int at45db_rdid(struct at45db_dev_s *priv);
static inline uint8_t at45db_rdsr(struct at45db_dev_s *priv);
static uint8_t at45db_waitbusy(struct at45db_dev_s *priv);
static inline void at45db_pgerase(struct at45db_dev_s *priv, off_t offset);
static inline int at32db_chiperase(struct at45db_dev_s *priv);
static inline void at45db_pgwrite(struct at45db_dev_s *priv, FAR const uint8_t *buffer,
off_t offset);
/* MTD driver methods */
static int at45db_erase(FAR struct mtd_dev_s *mtd, off_t startblock, size_t nblocks);
static ssize_t at45db_bread(FAR struct mtd_dev_s *mtd, off_t startblock,
size_t nblocks, FAR uint8_t *buf);
static ssize_t at45db_bwrite(FAR struct mtd_dev_s *mtd, off_t startblock,
size_t nblocks, FAR const uint8_t *buf);
static ssize_t at45db_read(FAR struct mtd_dev_s *mtd, off_t offset, size_t nbytes,
FAR uint8_t *buffer);
static int at45db_ioctl(FAR struct mtd_dev_s *mtd, int cmd, unsigned long arg);
/************************************************************************************
* Private Data
************************************************************************************/
/* Chip erase sequence */
#define CHIP_ERASE_SIZE 4
static const uint8_t g_chiperase[CHIP_ERASE_SIZE] = {0xc7, 0x94, 0x80, 0x9a};
/* Sequence to program the device to binary page sizes{256, 512, 1024} */
#define BINPGSIZE_SIZE 4
static const uint8_t g_binpgsize[BINPGSIZE_SIZE] = {0x3d, 0x2a, 0x80, 0xa6};
/************************************************************************************
* Private Functions
************************************************************************************/
/************************************************************************************
* Name: at45db_lock
************************************************************************************/
static void at45db_lock(struct at45db_dev_s *priv)
{
/* On SPI busses where there are multiple devices, it will be necessary to
* lock SPI to have exclusive access to the busses for a sequence of
* transfers. The bus should be locked before the chip is selected.
*
* This is a blocking call and will not return until we have exclusiv access to
* the SPI buss. We will retain that exclusive access until the bus is unlocked.
*/
(void)SPI_LOCK(priv->spi, true);
/* After locking the SPI bus, the we also need call the setfrequency, setbits, and
* setmode methods to make sure that the SPI is properly configured for the device.
* If the SPI buss is being shared, then it may have been left in an incompatible
* state.
*/
SPI_SETMODE(priv->spi, SPIDEV_MODE0);
SPI_SETBITS(priv->spi, 8);
(void)SPI_SETFREQUENCY(priv->spi, CONFIG_AT45DB_FREQUENCY);
}
/************************************************************************************
* Name: at45db_unlock
************************************************************************************/
static inline void at45db_unlock(struct at45db_dev_s *priv)
{
(void)SPI_LOCK(priv->spi, false);
}
/************************************************************************************
* Name: at45db_pwrdown
************************************************************************************/
#ifdef CONFIG_AT45DB_PWRSAVE
static void at45db_pwrdown(struct at45db_dev_s *priv)
{
SPI_SELECT(priv->spi, SPIDEV_FLASH, true);
SPI_SEND(priv->spi, AT45DB_PWRDOWN);
SPI_SELECT(priv->spi, SPIDEV_FLASH, false);
}
#endif
/************************************************************************************
* Name: at45db_resume
************************************************************************************/
#ifdef CONFIG_AT45DB_PWRSAVE
static void at45db_resume(struct at45db_dev_s *priv)
{
SPI_SELECT(priv->spi, SPIDEV_FLASH, true);
SPI_SEND(priv->spi, AT45DB_RESUME);
SPI_SELECT(priv->spi, SPIDEV_FLASH, false);
up_udelay(50);
}
#endif
/************************************************************************************
* Name: at45db_rdid
************************************************************************************/
static inline int at45db_rdid(struct at45db_dev_s *priv)
{
uint8_t capacity;
uint8_t devid[3];
fvdbg("priv: %p\n", priv);
/* Configure the bus, and select this FLASH part. (The caller should alread have
* loced the bus for exclusive access)
*/
SPI_SELECT(priv->spi, SPIDEV_FLASH, true);
/* Send the " Manufacturer and Device ID Read" command and read the next three
* ID bytes from the FLASH.
*/
(void)SPI_SEND(priv->spi, AT45DB_RDDEVID);
SPI_RECVBLOCK(priv->spi, devid, 3);
/* Deselect the FLASH */
SPI_SELECT(priv->spi, SPIDEV_FLASH, false);
fvdbg("manufacturer: %02x devid1: %02x devid2: %02x\n",
devid[0], devid[1], devid[2]);
/* Check for a valid manufacturer and memory family */
if (devid[0] == AT45DB_MANUFACTURER &&
(devid[1] & AT45DB_DEVID1_FAMMSK) == AT45DB_DEVID1_DFLASH)
{
/* Okay.. is it a FLASH capacity that we understand? */
capacity = devid[1] & AT45DB_DEVID1_CAPMSK;
switch (capacity)
{
case AT45DB_DEVID1_1MBIT:
/* Save the FLASH geometry for the 16Mbit AT45DB011 */
priv->pageshift = 8; /* Page size = 256 bytes */
priv->npages = 512; /* 512 pages */
return OK;
case AT45DB_DEVID1_2MBIT:
/* Save the FLASH geometry for the 16Mbit AT45DB021 */
priv->pageshift = 8; /* Page size = 256/264 bytes */
priv->npages = 1024; /* 1024 pages */
return OK;
case AT45DB_DEVID1_4MBIT:
/* Save the FLASH geometry for the 16Mbit AT45DB041 */
priv->pageshift = 8; /* Page size = 256/264 bytes */
priv->npages = 2048; /* 2048 pages */
return OK;
case AT45DB_DEVID1_8MBIT:
/* Save the FLASH geometry for the 16Mbit AT45DB081 */
priv->pageshift = 8; /* Page size = 256/264 bytes */
priv->npages = 4096; /* 4096 pages */
return OK;
case AT45DB_DEVID1_16MBIT:
/* Save the FLASH geometry for the 16Mbit AT45DB161 */
priv->pageshift = 9; /* Page size = 512/528 bytes */
priv->npages = 4096; /* 4096 pages */
return OK;
case AT45DB_DEVID1_32MBIT:
/* Save the FLASH geometry for the 16Mbit AT45DB321 */
priv->pageshift = 9; /* Page size = 512/528 bytes */
priv->npages = 8192; /* 8192 pages */
return OK;
case AT45DB_DEVID1_64MBIT:
/* Save the FLASH geometry for the 16Mbit AT45DB321 */
priv->pageshift = 10; /* Page size = 1024/1056 bytes */
priv->npages = 8192; /* 8192 pages */
return OK;
default:
return -ENODEV;
}
}
return -ENODEV;
}
/************************************************************************************
* Name: at45db_rdsr
************************************************************************************/
static inline uint8_t at45db_rdsr(struct at45db_dev_s *priv)
{
uint8_t retval;
SPI_SELECT(priv->spi, SPIDEV_FLASH, true);
SPI_SEND(priv->spi, AT45DB_RDSR);
retval = SPI_SEND(priv->spi, 0xff);
SPI_SELECT(priv->spi, SPIDEV_FLASH, false);
return retval;
}
/************************************************************************************
* Name: at45db_waitbusy
************************************************************************************/
static uint8_t at45db_waitbusy(struct at45db_dev_s *priv)
{
uint8_t sr;
/* Poll the device, waiting for it to report that it is ready */
do
{
up_udelay(10);
sr = (uint8_t)at45db_rdsr(priv);
}
while ((sr & AT45DB_SR_RDY) == 0);
return sr;
}
/************************************************************************************
* Name: at45db_pgerase
************************************************************************************/
static inline void at45db_pgerase(struct at45db_dev_s *priv, off_t sector)
{
uint8_t erasecmd[4];
off_t offset = sector << priv->pageshift;
fvdbg("sector: %08lx\n", (long)sector);
/* Higher performance write logic: We leave the chip busy after write and erase
* operations. This improves write and erase performance because we do not have
* to wait as long between transactions (other processing can occur while the chip
* is busy) but means that the chip must stay powered and that we must check if
* the chip is still busy on each entry point.
*/
#ifdef CONFIG_AT45DB_PREWAIT
at45db_waitbusy(priv);
#endif
/* "The Page Erase command can be used to individually erase any page in the main
* memory array allowing the Buffer to Main Memory Page Program to be utilized at a
* later time. ... To perform a page erase in the binary page size ..., the
* opcode 81H must be loaded into the device, followed by three address bytes
* ... When a low-to-high transition occurs on the CS pin, the part will erase the
* selected page (the erased state is a logical 1). ... the status register and the
* RDY/BUSY pin will indicate that the part is busy."
*/
erasecmd[0] = AT45DB_PGERASE; /* Page erase command */
erasecmd[1] = (offset >> 16) & 0xff; /* 24-bit offset MS bytes */
erasecmd[2] = (offset >> 8) & 0xff; /* 24-bit offset middle bytes */
erasecmd[3] = offset & 0xff; /* 24-bit offset LS bytes */
/* Erase the page */
SPI_SELECT(priv->spi, SPIDEV_FLASH, true);
SPI_SNDBLOCK(priv->spi, erasecmd, 4);
SPI_SELECT(priv->spi, SPIDEV_FLASH, false);
/* Wait for any erase to complete if we are not trying to improve write
* performance. (see comments above).
*/
#ifndef CONFIG_AT45DB_PREWAIT
at45db_waitbusy(priv);
#endif
fvdbg("Erased\n");
}
/************************************************************************************
* Name: at32db_chiperase
************************************************************************************/
static inline int at32db_chiperase(struct at45db_dev_s *priv)
{
fvdbg("priv: %p\n", priv);
/* Higher performance write logic: We leave the chip busy after write and erase
* operations. This improves write and erase performance because we do not have
* to wait as long between transactions (other processing can occur while the chip
* is busy) but means that the chip must stay powered and that we must check if
* the chip is still busy on each entry point.
*/
#ifdef CONFIG_AT45DB_PREWAIT
at45db_waitbusy(priv);
#endif
/* "The entire main memory can be erased at one time by using the Chip Erase
* command. To execute the Chip Erase command, a 4-byte command sequence C7H, 94H,
* 80H and 9AH must be clocked into the device. ... After the last bit of the opcode
* sequence has been clocked in, the CS pin can be deasserted to start the erase
* process. ... the Status Register will indicate that the device is busy. The Chip
* Erase command will not affect sectors that are protected or locked down...
*/
SPI_SELECT(priv->spi, SPIDEV_FLASH, true);
SPI_SNDBLOCK(priv->spi, g_chiperase, CHIP_ERASE_SIZE);
SPI_SELECT(priv->spi, SPIDEV_FLASH, false);
/* Wait for any erase to complete if we are not trying to improve write
* performance. (see comments above).
*/
#ifndef CONFIG_AT45DB_PREWAIT
at45db_waitbusy(priv);
#endif
return OK;
}
/************************************************************************************
* Name: at45db_pgwrite
************************************************************************************/
static inline void at45db_pgwrite(struct at45db_dev_s *priv, FAR const uint8_t *buffer,
off_t page)
{
uint8_t wrcmd [4];
off_t offset = page << priv->pageshift;
fvdbg("page: %08lx offset: %08lx\n", (long)page, (long)offset);
/* We assume that sectors are not write protected */
wrcmd[0] = AT45DB_MNTHRUBF1; /* To main memory through buffer 1 */
wrcmd[1] = (offset >> 16) & 0xff; /* 24-bit address MS byte */
wrcmd[2] = (offset >> 8) & 0xff; /* 24-bit address middle byte */
wrcmd[3] = offset & 0xff; /* 24-bit address LS byte */
/* Higher performance write logic: We leave the chip busy after write and erase
* operations. This improves write and erase performance because we do not have
* to wait as long between transactions (other processing can occur while the chip
* is busy) but means that the chip must stay powered and that we must check if
* the chip is still busy on each entry point.
*/
#ifdef CONFIG_AT45DB_PREWAIT
at45db_waitbusy(priv);
#endif
SPI_SELECT(priv->spi, SPIDEV_FLASH, true);
SPI_SNDBLOCK(priv->spi, wrcmd, 4);
SPI_SNDBLOCK(priv->spi, buffer, 1 << priv->pageshift);
SPI_SELECT(priv->spi, SPIDEV_FLASH, false);
/* Wait for any erase to complete if we are not trying to improve write
* performance. (see comments above).
*/
#ifndef CONFIG_AT45DB_PREWAIT
at45db_waitbusy(priv);
#endif
fvdbg("Written\n");
}
/************************************************************************************
* Name: at45db_erase
************************************************************************************/
static int at45db_erase(FAR struct mtd_dev_s *mtd, off_t startblock, size_t nblocks)
{
FAR struct at45db_dev_s *priv = (FAR struct at45db_dev_s *)mtd;
size_t pgsleft = nblocks;
fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
/* Take the lock so that we have exclusive access to the bus, then power up the
* FLASH device.
*/
at45db_lock(priv);
at45db_resume(priv);
/* Then erase each page */
while (pgsleft-- > 0)
{
/* Erase each sector */
at45db_pgerase(priv, startblock);
startblock++;
}
at45db_pwrdown(priv);
at45db_unlock(priv);
return (int)nblocks;
}
/************************************************************************************
* Name: at45db_bread
************************************************************************************/
static ssize_t at45db_bread(FAR struct mtd_dev_s *mtd, off_t startblock, size_t nblocks,
FAR uint8_t *buffer)
{
FAR struct at45db_dev_s *priv = (FAR struct at45db_dev_s *)mtd;
ssize_t nbytes;
/* On this device, we can handle the block read just like the byte-oriented read */
nbytes = at45db_read(mtd, startblock << priv->pageshift, nblocks << priv->pageshift, buffer);
if (nbytes > 0)
{
return nbytes >> priv->pageshift;
}
return nbytes;
}
/************************************************************************************
* Name: at45db_bwrite
************************************************************************************/
static ssize_t at45db_bwrite(FAR struct mtd_dev_s *mtd, off_t startblock, size_t nblocks,
FAR const uint8_t *buffer)
{
FAR struct at45db_dev_s *priv = (FAR struct at45db_dev_s *)mtd;
size_t pgsleft = nblocks;
fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
/* Take the lock so that we have exclusive access to the bus, then power up the
* FLASH device.
*/
at45db_lock(priv);
at45db_resume(priv);
/* Write each page to FLASH */
while (pgsleft-- > 0)
{
at45db_pgwrite(priv, buffer, startblock);
startblock++;
}
at45db_pwrdown(priv);
at45db_unlock(priv);
return nblocks;
}
/************************************************************************************
* Name: at45db_read
************************************************************************************/
static ssize_t at45db_read(FAR struct mtd_dev_s *mtd, off_t offset, size_t nbytes,
FAR uint8_t *buffer)
{
FAR struct at45db_dev_s *priv = (FAR struct at45db_dev_s *)mtd;
uint8_t rdcmd [5];
fvdbg("offset: %08lx nbytes: %d\n", (long)offset, (int)nbytes);
/* Set up for the read */
rdcmd[0] = AT45DB_RDARRAYHF; /* FAST_READ is safe at all supported SPI speeds. */
rdcmd[1] = (offset >> 16) & 0xff; /* 24-bit address upper byte */
rdcmd[2] = (offset >> 8) & 0xff; /* 24-bit address middle byte */
rdcmd[3] = offset & 0xff; /* 24-bit address least significant byte */
rdcmd[4] = 0; /* Dummy byte */
/* Take the lock so that we have exclusive access to the bus, then power up the
* FLASH device.
*/
at45db_lock(priv);
at45db_resume(priv);
/* Higher performance write logic: We leave the chip busy after write and erase
* operations. This improves write and erase performance because we do not have
* to wait as long between transactions (other processing can occur while the chip
* is busy) but means that the chip must stay powered and that we must check if
* the chip is still busy on each entry point.
*/
#ifdef CONFIG_AT45DB_PREWAIT
at45db_waitbusy(priv);
#endif
/* Perform the read */
SPI_SELECT(priv->spi, SPIDEV_FLASH, true);
SPI_SNDBLOCK(priv->spi, rdcmd, 5);
SPI_RECVBLOCK(priv->spi, buffer, nbytes);
SPI_SELECT(priv->spi, SPIDEV_FLASH, false);
at45db_pwrdown(priv);
at45db_unlock(priv);
fvdbg("return nbytes: %d\n", (int)nbytes);
return nbytes;
}
/************************************************************************************
* Name: at45db_ioctl
************************************************************************************/
static int at45db_ioctl(FAR struct mtd_dev_s *mtd, int cmd, unsigned long arg)
{
FAR struct at45db_dev_s *priv = (FAR struct at45db_dev_s *)mtd;
int ret = -EINVAL; /* Assume good command with bad parameters */
fvdbg("cmd: %d \n", cmd);
switch (cmd)
{
case MTDIOC_GEOMETRY:
{
FAR struct mtd_geometry_s *geo = (FAR struct mtd_geometry_s *)((uintptr_t)arg);
if (geo)
{
/* Populate the geometry structure with information need to know
* the capacity and how to access the device.
*
* NOTE: that the device is treated as though it where just an array
* of fixed size blocks. That is most likely not true, but the client
* will expect the device logic to do whatever is necessary to make it
* appear so.
*/
geo->blocksize = (1 << priv->pageshift);
geo->erasesize = geo->blocksize;
geo->neraseblocks = priv->npages;
ret = OK;
fvdbg("blocksize: %d erasesize: %d neraseblocks: %d\n",
geo->blocksize, geo->erasesize, geo->neraseblocks);
}
}
break;
case MTDIOC_BULKERASE:
{
/* Take the lock so that we have exclusive access to the bus, then
* power up the FLASH device.
*/
at45db_lock(priv);
at45db_resume(priv);
/* Erase the entire device */
ret = at32db_chiperase(priv);
at45db_pwrdown(priv);
at45db_unlock(priv);
}
break;
case MTDIOC_XIPBASE:
default:
ret = -ENOTTY; /* Bad command */
break;
}
fvdbg("return %d\n", ret);
return ret;
}
/************************************************************************************
* Public Functions
************************************************************************************/
/************************************************************************************
* Name: at45db_initialize
*
* Description:
* Create an initialize MTD device instance. MTD devices are not registered
* in the file system, but are created as instances that can be bound to
* other functions (such as a block or character driver front end).
*
************************************************************************************/
FAR struct mtd_dev_s *at45db_initialize(FAR struct spi_dev_s *spi)
{
FAR struct at45db_dev_s *priv;
uint8_t sr;
int ret;
fvdbg("spi: %p\n", spi);
/* Allocate a state structure (we allocate the structure instead of using
* a fixed, static allocation so that we can handle multiple FLASH devices.
* The current implementation would handle only one FLASH part per SPI
* device (only because of the SPIDEV_FLASH definition) and so would have
* to be extended to handle multiple FLASH parts on the same SPI bus.
*/
priv = (FAR struct at45db_dev_s *)kmalloc(sizeof(struct at45db_dev_s));
if (priv)
{
/* Initialize the allocated structure */
priv->mtd.erase = at45db_erase;
priv->mtd.bread = at45db_bread;
priv->mtd.bwrite = at45db_bwrite;
priv->mtd.read = at45db_read;
priv->mtd.ioctl = at45db_ioctl;
priv->spi = spi;
/* Deselect the FLASH */
SPI_SELECT(spi, SPIDEV_FLASH, false);
/* Lock and configure the SPI bus. */
at45db_lock(priv);
at45db_resume(priv);
/* Identify the FLASH chip and get its capacity */
ret = at45db_rdid(priv);
if (ret != OK)
{
/* Unrecognized! Discard all of that work we just did and return NULL */
fdbg("Unrecognized\n");
goto errout;
}
/* Get the value of the status register (as soon as the device is ready) */
sr = at45db_waitbusy(priv);
/* Check if the device is configured as 256, 512 or 1024 bytes-per-page device */
if ((sr & AT45DB_SR_PGSIZE) == 0)
{
/* No, re-program it for the binary page size. NOTE: A power cycle
* is required after the device has be re-programmed.
*/
fdbg("Reprogramming page size\n");
SPI_SELECT(priv->spi, SPIDEV_FLASH, true);
SPI_SNDBLOCK(priv->spi, g_binpgsize, BINPGSIZE_SIZE);
SPI_SELECT(priv->spi, SPIDEV_FLASH, false);
goto errout;
}
/* Release the lock and power down the device */
at45db_pwrdown(priv);
at45db_unlock(priv);
}
fvdbg("Return %p\n", priv);
return (FAR struct mtd_dev_s *)priv;
/* On any failure, we need free memory allocations and release the lock that
* we hold on the SPI bus. On failures, assume that we cannot talk to the
* device to do any more.
*/
errout:
at45db_unlock(priv);
kfree(priv);
return NULL;
}
+117
View File
@@ -0,0 +1,117 @@
/****************************************************************************
* drivers/mtd/flash_eraseall.c
*
* Copyright (C) 2011 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/fs/fs.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/mtd.h>
/****************************************************************************
* Private Definitions
****************************************************************************/
/****************************************************************************
* Private Types
****************************************************************************/
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/****************************************************************************
* Private Data
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: flash_eraseall
*
* Description:
* Call a block driver with the MDIOC_BULKERASE ioctl command. This will
* cause the MTD driver to erase all of the flash.
*
****************************************************************************/
int flash_eraseall(FAR const char *driver)
{
FAR struct inode *inode;
FAR const struct block_operations *ops;
int ret;
/* Open the block driver */
ret = open_blockdriver(driver ,0, &inode);
if (ret < 0)
{
fdbg("ERROR: Failed to open '%s': %d\n", driver, ret);
return ret;
}
/* Get the block operations */
ops = inode->u.i_bops;
/* Invoke the block driver ioctl method */
ret = -EPERM;
if (ops->ioctl)
{
ret = ops->ioctl(inode, MTDIOC_BULKERASE, 0);
if (ret < 0)
{
fdbg("ERROR: MTD ioctl(%04x) failed: %d\n", MTDIOC_BULKERASE, ret);
}
}
/* Close the block driver */
close_blockdriver(inode);
return ret;
}
+578
View File
@@ -0,0 +1,578 @@
/****************************************************************************
* drivers/mtd/ftl.c
*
* Copyright (C) 2009, 2011 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <debug.h>
#include <errno.h>
#include <nuttx/kmalloc.h>
#include <nuttx/fs/fs.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/mtd.h>
#include <nuttx/rwbuffer.h>
/****************************************************************************
* Private Definitions
****************************************************************************/
#if defined(CONFIG_FS_READAHEAD) || (defined(CONFIG_FS_WRITABLE) && defined(CONFIG_FS_WRITEBUFFER))
# defined CONFIG_FTL_RWBUFFER 1
#endif
/****************************************************************************
* Private Types
****************************************************************************/
struct ftl_struct_s
{
FAR struct mtd_dev_s *mtd; /* Contained MTD interface */
struct mtd_geometry_s geo; /* Device geometry */
#ifdef CONFIG_FTL_RWBUFFER
struct rwbuffer_s rwb; /* Read-ahead/write buffer support */
#endif
uint16_t blkper; /* R/W blocks per erase block */
#ifdef CONFIG_FS_WRITABLE
FAR uint8_t *eblock; /* One, in-memory erase block */
#endif
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static int ftl_open(FAR struct inode *inode);
static int ftl_close(FAR struct inode *inode);
static ssize_t ftl_reload(FAR void *priv, FAR uint8_t *buffer,
off_t startblock, size_t nblocks);
static ssize_t ftl_read(FAR struct inode *inode, unsigned char *buffer,
size_t start_sector, unsigned int nsectors);
#ifdef CONFIG_FS_WRITABLE
static ssize_t ftl_flush(FAR void *priv, FAR const uint8_t *buffer,
off_t startblock, size_t nblocks);
static ssize_t ftl_write(FAR struct inode *inode, const unsigned char *buffer,
size_t start_sector, unsigned int nsectors);
#endif
static int ftl_geometry(FAR struct inode *inode, struct geometry *geometry);
static int ftl_ioctl(FAR struct inode *inode, int cmd, unsigned long arg);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct block_operations g_bops =
{
ftl_open, /* open */
ftl_close, /* close */
ftl_read, /* read */
#ifdef CONFIG_FS_WRITABLE
ftl_write, /* write */
#else
NULL, /* write */
#endif
ftl_geometry, /* geometry */
ftl_ioctl /* ioctl */
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: ftl_open
*
* Description: Open the block device
*
****************************************************************************/
static int ftl_open(FAR struct inode *inode)
{
fvdbg("Entry\n");
return OK;
}
/****************************************************************************
* Name: ftl_close
*
* Description: close the block device
*
****************************************************************************/
static int ftl_close(FAR struct inode *inode)
{
fvdbg("Entry\n");
return OK;
}
/****************************************************************************
* Name: ftl_reload
*
* Description: Read the specified numer of sectors
*
****************************************************************************/
static ssize_t ftl_reload(FAR void *priv, FAR uint8_t *buffer,
off_t startblock, size_t nblocks)
{
struct ftl_struct_s *dev = (struct ftl_struct_s *)priv;
ssize_t nread;
/* Read the full erase block into the buffer */
nread = MTD_BREAD(dev->mtd, startblock, nblocks, buffer);
if (nread != nblocks)
{
fdbg("Read %d blocks starting at block %d failed: %d\n",
nblocks, startblock, nread);
}
return nread;
}
/****************************************************************************
* Name: ftl_read
*
* Description: Read the specified numer of sectors
*
****************************************************************************/
static ssize_t ftl_read(FAR struct inode *inode, unsigned char *buffer,
size_t start_sector, unsigned int nsectors)
{
struct ftl_struct_s *dev;
fvdbg("sector: %d nsectors: %d\n", start_sector, nsectors);
DEBUGASSERT(inode && inode->i_private);
dev = (struct ftl_struct_s *)inode->i_private;
#ifdef CONFIG_FS_READAHEAD
return rwb_read(&dev->rwb, start_sector, nsectors, buffer);
#else
return ftl_reload(dev, buffer, start_sector, nsectors);
#endif
}
/****************************************************************************
* Name: ftl_flush
*
* Description: Write the specified number of sectors
*
****************************************************************************/
#ifdef CONFIG_FS_WRITABLE
static ssize_t ftl_flush(FAR void *priv, FAR const uint8_t *buffer,
off_t startblock, size_t nblocks)
{
struct ftl_struct_s *dev = (struct ftl_struct_s *)priv;
off_t alignedblock;
off_t mask;
off_t rwblock;
off_t eraseblock;
off_t offset;
size_t remaining;
size_t nxfrd;
int nbytes;
int ret;
/* Get the aligned block. Here is is assumed: (1) The number of R/W blocks
* per erase block is a power of 2, and (2) the erase begins with that same
* alignment.
*/
mask = dev->blkper - 1;
alignedblock = (startblock + mask) & ~mask;
/* Handle partial erase blocks before the first unaligned block */
remaining = nblocks;
if (alignedblock > startblock)
{
/* Read the full erase block into the buffer */
rwblock = startblock & ~mask;
nxfrd = MTD_BREAD(dev->mtd, rwblock, dev->blkper, dev->eblock);
if (nxfrd != dev->blkper)
{
fdbg("Read erase block %d failed: %d\n", rwblock, nxfrd);
return -EIO;
}
/* Then erase the erase block */
eraseblock = rwblock / dev->blkper;
ret = MTD_ERASE(dev->mtd, eraseblock, 1);
if (ret < 0)
{
fdbg("Erase block=%d failed: %d\n", eraseblock, ret);
return ret;
}
/* Copy the user data at the end of the buffered erase block */
offset = (startblock & mask) * dev->geo.blocksize;
nbytes = dev->geo.erasesize - offset;
fvdbg("Copy %d bytes into erase block=%d at offset=%d\n",
nbytes, eraseblock, offset);
memcpy(dev->eblock + offset, buffer, nbytes);
/* And write the erase back to flash */
nxfrd = MTD_BWRITE(dev->mtd, rwblock, dev->blkper, dev->eblock);
if (nxfrd != dev->blkper)
{
fdbg("Write erase block %d failed: %d\n", rwblock, nxfrd);
return -EIO;
}
/* Then update for amount written */
remaining -= dev->blkper - (startblock & mask);
buffer += nbytes;
}
/* How handle full erase pages in the middle */
while (remaining >= dev->blkper)
{
/* Erase the erase block */
eraseblock = alignedblock / dev->blkper;
ret = MTD_ERASE(dev->mtd, eraseblock, 1);
if (ret < 0)
{
fdbg("Erase block=%d failed: %d\n", eraseblock, ret);
return ret;
}
/* Write a full erase back to flash */
fvdbg("Write %d bytes into erase block=%d at offset=0\n",
dev->geo.erasesize, alignedblock);
nxfrd = MTD_BWRITE(dev->mtd, alignedblock, dev->blkper, buffer);
if (nxfrd != dev->blkper)
{
fdbg("Write erase block %d failed: %d\n", alignedblock, nxfrd);
return -EIO;
}
/* Then update for amount written */
alignedblock += dev->blkper;
remaining -= dev->blkper;
buffer += dev->geo.erasesize;
}
/* Finally, handler any partial blocks after the last full erase block */
if (remaining > 0)
{
/* Read the full erase block into the buffer */
nxfrd = MTD_BREAD(dev->mtd, alignedblock, dev->blkper, dev->eblock);
if (nxfrd != dev->blkper)
{
fdbg("Read erase block %d failed: %d\n", alignedblock, nxfrd);
return -EIO;
}
/* Then erase the erase block */
eraseblock = alignedblock / dev->blkper;
ret = MTD_ERASE(dev->mtd, eraseblock, 1);
if (ret < 0)
{
fdbg("Erase block=%d failed: %d\n", eraseblock, ret);
return ret;
}
/* Copy the user data at the beginning the buffered erase block */
nbytes = remaining * dev->geo.blocksize;
fvdbg("Copy %d bytes into erase block=%d at offset=0\n",
nbytes, alignedblock);
memcpy(dev->eblock, buffer, nbytes);
/* And write the erase back to flash */
nxfrd = MTD_BWRITE(dev->mtd, alignedblock, dev->blkper, dev->eblock);
if (nxfrd != dev->blkper)
{
fdbg("Write erase block %d failed: %d\n", alignedblock, nxfrd);
return -EIO;
}
}
return nblocks;
}
#endif
/****************************************************************************
* Name: ftl_write
*
* Description: Write (or buffer) the specified number of sectors
*
****************************************************************************/
#ifdef CONFIG_FS_WRITABLE
static ssize_t ftl_write(FAR struct inode *inode, const unsigned char *buffer,
size_t start_sector, unsigned int nsectors)
{
struct ftl_struct_s *dev;
fvdbg("sector: %d nsectors: %d\n", start_sector, nsectors);
DEBUGASSERT(inode && inode->i_private);
dev = (struct ftl_struct_s *)inode->i_private;
#ifdef CONFIG_FS_WRITEBUFFER
return rwb_write(&dev->rwb, start_sector, nsectors, buffer);
#else
return ftl_flush(dev, buffer, start_sector, nsectors);
#endif
}
#endif
/****************************************************************************
* Name: ftl_geometry
*
* Description: Return device geometry
*
****************************************************************************/
static int ftl_geometry(FAR struct inode *inode, struct geometry *geometry)
{
struct ftl_struct_s *dev;
fvdbg("Entry\n");
DEBUGASSERT(inode);
if (geometry)
{
dev = (struct ftl_struct_s *)inode->i_private;
geometry->geo_available = true;
geometry->geo_mediachanged = false;
#ifdef CONFIG_FS_WRITABLE
geometry->geo_writeenabled = true;
#else
geometry->geo_writeenabled = false;
#endif
geometry->geo_nsectors = dev->geo.neraseblocks * dev->blkper;
geometry->geo_sectorsize = dev->geo.blocksize;
fvdbg("available: true mediachanged: false writeenabled: %s\n",
geometry->geo_writeenabled ? "true" : "false");
fvdbg("nsectors: %d sectorsize: %d\n",
geometry->geo_nsectors, geometry->geo_sectorsize);
return OK;
}
return -EINVAL;
}
/****************************************************************************
* Name: ftl_ioctl
*
* Description: Return device geometry
*
****************************************************************************/
static int ftl_ioctl(FAR struct inode *inode, int cmd, unsigned long arg)
{
struct ftl_struct_s *dev ;
int ret;
fvdbg("Entry\n");
DEBUGASSERT(inode && inode->i_private);
/* Only one block driver ioctl command is supported by this driver (and
* that command is just passed on to the MTD driver in a slightly
* different form).
*/
if (cmd == BIOC_XIPBASE)
{
/* The argument accompanying the BIOC_XIPBASE should be non-NULL. If
* DEBUG is enabled, we will catch it here instead of in the MTD
* driver.
*/
#ifdef CONFIG_DEBUG
if (arg == 0)
{
fdbg("ERROR: BIOC_XIPBASE argument is NULL\n");
return -EINVAL;
}
#endif
/* Just change the BIOC_XIPBASE command to the MTDIOC_XIPBASE command. */
cmd = MTDIOC_XIPBASE;
}
/* No other block driver ioctl commmands are not recognized by this
* driver. Other possible MTD driver ioctl commands are passed through
* to the MTD driver (unchanged).
*/
dev = (struct ftl_struct_s *)inode->i_private;
ret = MTD_IOCTL(dev->mtd, cmd, arg);
if (ret < 0)
{
fdbg("ERROR: MTD ioctl(%04x) failed: %d\n", cmd, ret);
}
return ret;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: ftl_initialize
*
* Description:
* Initialize to provide a block driver wrapper around an MTD interface
*
* Input Parameters:
* minor - The minor device number. The MTD block device will be
* registered as as /dev/mtdblockN where N is the minor number.
* mtd - The MTD device that supports the FLASH interface.
*
****************************************************************************/
int ftl_initialize(int minor, FAR struct mtd_dev_s *mtd)
{
struct ftl_struct_s *dev;
char devname[16];
int ret = -ENOMEM;
/* Sanity check */
#ifdef CONFIG_DEBUG
if (minor < 0 || minor > 255 || !mtd)
{
return -EINVAL;
}
#endif
/* Allocate a FTL device structure */
dev = (struct ftl_struct_s *)kmalloc(sizeof(struct ftl_struct_s));
if (dev)
{
/* Initialize the FTL device structure */
dev->mtd = mtd;
/* Get the device geometry. (casting to uintptr_t first eliminates
* complaints on some architectures where the sizeof long is different
* from the size of a pointer).
*/
ret = MTD_IOCTL(mtd, MTDIOC_GEOMETRY, (unsigned long)((uintptr_t)&dev->geo));
if (ret < 0)
{
fdbg("MTD ioctl(MTDIOC_GEOMETRY) failed: %d\n", ret);
kfree(dev);
return ret;
}
/* Allocate one, in-memory erase block buffer */
#ifdef CONFIG_FS_WRITABLE
dev->eblock = (FAR uint8_t *)kmalloc(dev->geo.erasesize);
if (!dev->eblock)
{
fdbg("Failed to allocate an erase block buffer\n");
kfree(dev);
return -ENOMEM;
}
#endif
/* Get the number of R/W blocks per erase block */
dev->blkper = dev->geo.erasesize / dev->geo.blocksize;
DEBUGASSERT(dev->blkper * dev->geo.blocksize == dev->geo.erasesize);
/* Configure read-ahead/write buffering */
#ifdef CONFIG_FTL_RWBUFFER
dev->rwb.blocksize = dev->geo.blocksize;
dev->rwb.nblocks = dev->geo.neraseblocks * dev->blkper;
dev->rwb.dev = (FAR void *)dev;
#if defined(CONFIG_FS_WRITABLE) && defined(CONFIG_FS_WRITEBUFFER)
dev->rwb.wrmaxblocks = dev->blkper;
dev->rwb.wrflush = ftl_flush;
#endif
#ifdef CONFIG_FS_READAHEAD
dev->rwb.rhmaxblocks = dev->blkper;
dev->rwb.rhreload = ftl_reload;
#endif
ret = rwb_initialize(&dev->rwb);
if (ret < 0)
{
fdbg("rwb_initialize failed: %d\n", ret);
kfree(dev);
return ret;
}
#endif
/* Create a MTD block device name */
snprintf(devname, 16, "/dev/mtdblock%d", minor);
/* Inode private data is a reference to the FTL device structure */
ret = register_blockdriver(devname, &g_bops, 0, dev);
if (ret < 0)
{
fdbg("register_blockdriver failed: %d\n", -ret);
kfree(dev);
}
}
return ret;
}
+798
View File
@@ -0,0 +1,798 @@
/************************************************************************************
* drivers/mtd/m25px.c
* Driver for SPI-based M25P1 (128Kbit), M25P64 (32Mbit), M25P64 (64Mbit), and
* M25P128 (128Mbit) FLASH (and compatible).
*
* Copyright (C) 2009-2011 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
************************************************************************************/
/************************************************************************************
* Included Files
************************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/kmalloc.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/spi.h>
#include <nuttx/mtd.h>
/************************************************************************************
* Pre-processor Definitions
************************************************************************************/
/* Configuration ********************************************************************/
/* Per the data sheet, MP25P10 parts can be driven with either SPI mode 0 (CPOL=0 and
* CPHA=0) or mode 3 (CPOL=1 and CPHA=1). But I have heard that other devices can
* operated in mode 0 or 1. So you may need to specify CONFIG_MP25P_SPIMODE to
* select the best mode for your device. If CONFIG_MP25P_SPIMODE is not defined,
* mode 0 will be used.
*/
#ifndef CONFIG_MP25P_SPIMODE
# define CONFIG_MP25P_SPIMODE SPIDEV_MODE0
#endif
/* Various manufacturers may have produced the parts. 0x20 is the manufacturer ID
* for the STMicro MP25x serial FLASH. If, for example, you are using the a Macronix
* International MX25 serial FLASH, the correct manufacturer ID would be 0xc2.
*/
#ifndef CONFIG_MP25P_MANUFACTURER
# define CONFIG_MP25P_MANUFACTURER 0x20
#endif
/* M25P Registers *******************************************************************/
/* Indentification register values */
#define M25P_MANUFACTURER CONFIG_MP25P_MANUFACTURER
#define M25P_MEMORY_TYPE 0x20
#define M25P_M25P1_CAPACITY 0x11 /* 1 M-bit */
#define M25P_M25P32_CAPACITY 0x16 /* 32 M-bit */
#define M25P_M25P64_CAPACITY 0x17 /* 64 M-bit */
#define M25P_M25P128_CAPACITY 0x18 /* 128 M-bit */
/* M25P1 capacity is 131,072 bytes:
* (4 sectors) * (32,768 bytes per sector)
* (512 pages) * (256 bytes per page)
*/
#define M25P_M25P1_SECTOR_SHIFT 15 /* Sector size 1 << 15 = 65,536 */
#define M25P_M25P1_NSECTORS 4
#define M25P_M25P1_PAGE_SHIFT 8 /* Page size 1 << 8 = 256 */
#define M25P_M25P1_NPAGES 512
/* M25P32 capacity is 4,194,304 bytes:
* (64 sectors) * (65,536 bytes per sector)
* (16384 pages) * (256 bytes per page)
*/
#define M25P_M25P32_SECTOR_SHIFT 16 /* Sector size 1 << 16 = 65,536 */
#define M25P_M25P32_NSECTORS 64
#define M25P_M25P32_PAGE_SHIFT 8 /* Page size 1 << 8 = 256 */
#define M25P_M25P32_NPAGES 16384
/* M25P64 capacity is 8,338,608 bytes:
* (128 sectors) * (65,536 bytes per sector)
* (32768 pages) * (256 bytes per page)
*/
#define M25P_M25P64_SECTOR_SHIFT 16 /* Sector size 1 << 16 = 65,536 */
#define M25P_M25P64_NSECTORS 128
#define M25P_M25P64_PAGE_SHIFT 8 /* Page size 1 << 8 = 256 */
#define M25P_M25P64_NPAGES 32768
/* M25P128 capacity is 16,777,216 bytes:
* (64 sectors) * (262,144 bytes per sector)
* (65536 pages) * (256 bytes per page)
*/
#define M25P_M25P128_SECTOR_SHIFT 18 /* Sector size 1 << 18 = 262,144 */
#define M25P_M25P128_NSECTORS 64
#define M25P_M25P128_PAGE_SHIFT 8 /* Page size 1 << 8 = 256 */
#define M25P_M25P128_NPAGES 65536
/* Instructions */
/* Command Value N Description Addr Dummy Data */
#define M25P_WREN 0x06 /* 1 Write Enable 0 0 0 */
#define M25P_WRDI 0x04 /* 1 Write Disable 0 0 0 */
#define M25P_RDID 0x9f /* 1 Read Identification 0 0 1-3 */
#define M25P_RDSR 0x05 /* 1 Read Status Register 0 0 >=1 */
#define M25P_WRSR 0x01 /* 1 Write Status Register 0 0 1 */
#define M25P_READ 0x03 /* 1 Read Data Bytes 3 0 >=1 */
#define M25P_FAST_READ 0x0b /* 1 Higher speed read 3 1 >=1 */
#define M25P_PP 0x02 /* 1 Page Program 3 0 1-256 */
#define M25P_SE 0xd8 /* 1 Sector Erase 3 0 0 */
#define M25P_BE 0xc7 /* 1 Bulk Erase 0 0 0 */
#define M25P_DP 0xb9 /* 2 Deep power down 0 0 0 */
#define M25P_RES 0xab /* 2 Read Electronic Signature 0 3 >=1 */
/* NOTE 1: All parts, NOTE 2: M25P632/M25P64 */
/* Status register bit definitions */
#define M25P_SR_WIP (1 << 0) /* Bit 0: Write in progress bit */
#define M25P_SR_WEL (1 << 1) /* Bit 1: Write enable latch bit */
#define M25P_SR_BP_SHIFT (2) /* Bits 2-4: Block protect bits */
#define M25P_SR_BP_MASK (7 << M25P_SR_BP_SHIFT)
# define M25P_SR_BP_NONE (0 << M25P_SR_BP_SHIFT) /* Unprotected */
# define M25P_SR_BP_UPPER64th (1 << M25P_SR_BP_SHIFT) /* Upper 64th */
# define M25P_SR_BP_UPPER32nd (2 << M25P_SR_BP_SHIFT) /* Upper 32nd */
# define M25P_SR_BP_UPPER16th (3 << M25P_SR_BP_SHIFT) /* Upper 16th */
# define M25P_SR_BP_UPPER8th (4 << M25P_SR_BP_SHIFT) /* Upper 8th */
# define M25P_SR_BP_UPPERQTR (5 << M25P_SR_BP_SHIFT) /* Upper quarter */
# define M25P_SR_BP_UPPERHALF (6 << M25P_SR_BP_SHIFT) /* Upper half */
# define M25P_SR_BP_ALL (7 << M25P_SR_BP_SHIFT) /* All sectors */
/* Bits 5-6: Unused, read zero */
#define M25P_SR_SRWD (1 << 7) /* Bit 7: Status register write protect */
#define M25P_DUMMY 0xa5
/************************************************************************************
* Private Types
************************************************************************************/
/* This type represents the state of the MTD device. The struct mtd_dev_s
* must appear at the beginning of the definition so that you can freely
* cast between pointers to struct mtd_dev_s and struct m25p_dev_s.
*/
struct m25p_dev_s
{
struct mtd_dev_s mtd; /* MTD interface */
FAR struct spi_dev_s *dev; /* Saved SPI interface instance */
uint8_t sectorshift; /* 16 or 18 */
uint8_t pageshift; /* 8 */
uint16_t nsectors; /* 128 or 64 */
uint32_t npages; /* 32,768 or 65,536 */
};
/************************************************************************************
* Private Function Prototypes
************************************************************************************/
/* Helpers */
static void m25p_lock(FAR struct spi_dev_s *dev);
static inline void m25p_unlock(FAR struct spi_dev_s *dev);
static inline int m25p_readid(struct m25p_dev_s *priv);
static void m25p_waitwritecomplete(struct m25p_dev_s *priv);
static void m25p_writeenable(struct m25p_dev_s *priv);
static inline void m25p_sectorerase(struct m25p_dev_s *priv, off_t offset);
static inline int m25p_bulkerase(struct m25p_dev_s *priv);
static inline void m25p_pagewrite(struct m25p_dev_s *priv, FAR const uint8_t *buffer,
off_t offset);
/* MTD driver methods */
static int m25p_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks);
static ssize_t m25p_bread(FAR struct mtd_dev_s *dev, off_t startblock,
size_t nblocks, FAR uint8_t *buf);
static ssize_t m25p_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
size_t nblocks, FAR const uint8_t *buf);
static ssize_t m25p_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes,
FAR uint8_t *buffer);
static int m25p_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg);
/************************************************************************************
* Private Data
************************************************************************************/
/************************************************************************************
* Private Functions
************************************************************************************/
/************************************************************************************
* Name: m25p_lock
************************************************************************************/
static void m25p_lock(FAR struct spi_dev_s *dev)
{
/* On SPI busses where there are multiple devices, it will be necessary to
* lock SPI to have exclusive access to the busses for a sequence of
* transfers. The bus should be locked before the chip is selected.
*
* This is a blocking call and will not return until we have exclusiv access to
* the SPI buss. We will retain that exclusive access until the bus is unlocked.
*/
(void)SPI_LOCK(dev, true);
/* After locking the SPI bus, the we also need call the setfrequency, setbits, and
* setmode methods to make sure that the SPI is properly configured for the device.
* If the SPI buss is being shared, then it may have been left in an incompatible
* state.
*/
SPI_SETMODE(dev, CONFIG_MP25P_SPIMODE);
SPI_SETBITS(dev, 8);
(void)SPI_SETFREQUENCY(dev, 20000000);
}
/************************************************************************************
* Name: m25p_unlock
************************************************************************************/
static inline void m25p_unlock(FAR struct spi_dev_s *dev)
{
(void)SPI_LOCK(dev, false);
}
/************************************************************************************
* Name: m25p_readid
************************************************************************************/
static inline int m25p_readid(struct m25p_dev_s *priv)
{
uint16_t manufacturer;
uint16_t memory;
uint16_t capacity;
fvdbg("priv: %p\n", priv);
/* Lock the SPI bus, configure the bus, and select this FLASH part. */
m25p_lock(priv->dev);
SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
/* Send the "Read ID (RDID)" command and read the first three ID bytes */
(void)SPI_SEND(priv->dev, M25P_RDID);
manufacturer = SPI_SEND(priv->dev, M25P_DUMMY);
memory = SPI_SEND(priv->dev, M25P_DUMMY);
capacity = SPI_SEND(priv->dev, M25P_DUMMY);
/* Deselect the FLASH and unlock the bus */
SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
m25p_unlock(priv->dev);
fvdbg("manufacturer: %02x memory: %02x capacity: %02x\n",
manufacturer, memory, capacity);
/* Check for a valid manufacturer and memory type */
if (manufacturer == M25P_MANUFACTURER && memory == M25P_MEMORY_TYPE)
{
/* Okay.. is it a FLASH capacity that we understand? */
if (capacity == M25P_M25P1_CAPACITY)
{
/* Save the FLASH geometry */
priv->sectorshift = M25P_M25P1_SECTOR_SHIFT;
priv->nsectors = M25P_M25P1_NSECTORS;
priv->pageshift = M25P_M25P1_PAGE_SHIFT;
priv->npages = M25P_M25P1_NPAGES;
return OK;
}
else if (capacity == M25P_M25P32_CAPACITY)
{
/* Save the FLASH geometry */
priv->sectorshift = M25P_M25P32_SECTOR_SHIFT;
priv->nsectors = M25P_M25P32_NSECTORS;
priv->pageshift = M25P_M25P32_PAGE_SHIFT;
priv->npages = M25P_M25P32_NPAGES;
return OK;
}
else if (capacity == M25P_M25P64_CAPACITY)
{
/* Save the FLASH geometry */
priv->sectorshift = M25P_M25P64_SECTOR_SHIFT;
priv->nsectors = M25P_M25P64_NSECTORS;
priv->pageshift = M25P_M25P64_PAGE_SHIFT;
priv->npages = M25P_M25P64_NPAGES;
return OK;
}
else if (capacity == M25P_M25P128_CAPACITY)
{
/* Save the FLASH geometry */
priv->sectorshift = M25P_M25P128_SECTOR_SHIFT;
priv->nsectors = M25P_M25P128_NSECTORS;
priv->pageshift = M25P_M25P128_PAGE_SHIFT;
priv->npages = M25P_M25P128_NPAGES;
return OK;
}
}
return -ENODEV;
}
/************************************************************************************
* Name: m25p_waitwritecomplete
************************************************************************************/
static void m25p_waitwritecomplete(struct m25p_dev_s *priv)
{
uint8_t status;
/* Are we the only device on the bus? */
#ifdef CONFIG_SPI_OWNBUS
/* Select this FLASH part */
SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
/* Send "Read Status Register (RDSR)" command */
(void)SPI_SEND(priv->dev, M25P_RDSR);
/* Loop as long as the memory is busy with a write cycle */
do
{
/* Send a dummy byte to generate the clock needed to shift out the status */
status = SPI_SEND(priv->dev, M25P_DUMMY);
}
while ((status & M25P_SR_WIP) != 0);
/* Deselect the FLASH */
SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
#else
/* Loop as long as the memory is busy with a write cycle */
do
{
/* Select this FLASH part */
SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
/* Send "Read Status Register (RDSR)" command */
(void)SPI_SEND(priv->dev, M25P_RDSR);
/* Send a dummy byte to generate the clock needed to shift out the status */
status = SPI_SEND(priv->dev, M25P_DUMMY);
/* Deselect the FLASH */
SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
/* Given that writing could take up to few tens of milliseconds, and erasing
* could take more. The following short delay in the "busy" case will allow
* other peripherals to access the SPI bus.
*/
if ((status & M25P_SR_WIP) != 0)
{
m25p_unlock(priv->dev);
usleep(1000);
m25p_lock(priv->dev);
}
}
while ((status & M25P_SR_WIP) != 0);
#endif
fvdbg("Complete\n");
}
/************************************************************************************
* Name: m25p_writeenable
************************************************************************************/
static void m25p_writeenable(struct m25p_dev_s *priv)
{
/* Select this FLASH part */
SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
/* Send "Write Enable (WREN)" command */
(void)SPI_SEND(priv->dev, M25P_WREN);
/* Deselect the FLASH */
SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
fvdbg("Enabled\n");
}
/************************************************************************************
* Name: m25p_sectorerase
************************************************************************************/
static inline void m25p_sectorerase(struct m25p_dev_s *priv, off_t sector)
{
off_t offset = sector << priv->sectorshift;
fvdbg("sector: %08lx\n", (long)sector);
/* Wait for any preceding write to complete. We could simplify things by
* perform this wait at the end of each write operation (rather than at
* the beginning of ALL operations), but have the wait first will slightly
* improve performance.
*/
m25p_waitwritecomplete(priv);
/* Send write enable instruction */
m25p_writeenable(priv);
/* Select this FLASH part */
SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
/* Send the "Sector Erase (SE)" instruction */
(void)SPI_SEND(priv->dev, M25P_SE);
/* Send the sector offset high byte first. For all of the supported
* parts, the sector number is completely contained in the first byte
* and the values used in the following two bytes don't really matter.
*/
(void)SPI_SEND(priv->dev, (offset >> 16) & 0xff);
(void)SPI_SEND(priv->dev, (offset >> 8) & 0xff);
(void)SPI_SEND(priv->dev, offset & 0xff);
/* Deselect the FLASH */
SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
fvdbg("Erased\n");
}
/************************************************************************************
* Name: m25p_bulkerase
************************************************************************************/
static inline int m25p_bulkerase(struct m25p_dev_s *priv)
{
fvdbg("priv: %p\n", priv);
/* Wait for any preceding write to complete. We could simplify things by
* perform this wait at the end of each write operation (rather than at
* the beginning of ALL operations), but have the wait first will slightly
* improve performance.
*/
m25p_waitwritecomplete(priv);
/* Send write enable instruction */
m25p_writeenable(priv);
/* Select this FLASH part */
SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
/* Send the "Bulk Erase (BE)" instruction */
(void)SPI_SEND(priv->dev, M25P_BE);
/* Deselect the FLASH */
SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
fvdbg("Return: OK\n");
return OK;
}
/************************************************************************************
* Name: m25p_pagewrite
************************************************************************************/
static inline void m25p_pagewrite(struct m25p_dev_s *priv, FAR const uint8_t *buffer,
off_t page)
{
off_t offset = page << priv->pageshift;
fvdbg("page: %08lx offset: %08lx\n", (long)page, (long)offset);
/* Wait for any preceding write to complete. We could simplify things by
* perform this wait at the end of each write operation (rather than at
* the beginning of ALL operations), but have the wait first will slightly
* improve performance.
*/
m25p_waitwritecomplete(priv);
/* Enable the write access to the FLASH */
m25p_writeenable(priv);
/* Select this FLASH part */
SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
/* Send "Page Program (PP)" command */
(void)SPI_SEND(priv->dev, M25P_PP);
/* Send the page offset high byte first. */
(void)SPI_SEND(priv->dev, (offset >> 16) & 0xff);
(void)SPI_SEND(priv->dev, (offset >> 8) & 0xff);
(void)SPI_SEND(priv->dev, offset & 0xff);
/* Then write the specified number of bytes */
SPI_SNDBLOCK(priv->dev, buffer, 1 << priv->pageshift);
/* Deselect the FLASH: Chip Select high */
SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
fvdbg("Written\n");
}
/************************************************************************************
* Name: m25p_erase
************************************************************************************/
static int m25p_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks)
{
FAR struct m25p_dev_s *priv = (FAR struct m25p_dev_s *)dev;
size_t blocksleft = nblocks;
fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
/* Lock access to the SPI bus until we complete the erase */
m25p_lock(priv->dev);
while (blocksleft-- > 0)
{
/* Erase each sector */
m25p_sectorerase(priv, startblock);
startblock++;
}
m25p_unlock(priv->dev);
return (int)nblocks;
}
/************************************************************************************
* Name: m25p_bread
************************************************************************************/
static ssize_t m25p_bread(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks,
FAR uint8_t *buffer)
{
FAR struct m25p_dev_s *priv = (FAR struct m25p_dev_s *)dev;
ssize_t nbytes;
fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
/* On this device, we can handle the block read just like the byte-oriented read */
nbytes = m25p_read(dev, startblock << priv->pageshift, nblocks << priv->pageshift, buffer);
if (nbytes > 0)
{
return nbytes >> priv->pageshift;
}
return (int)nbytes;
}
/************************************************************************************
* Name: m25p_bwrite
************************************************************************************/
static ssize_t m25p_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks,
FAR const uint8_t *buffer)
{
FAR struct m25p_dev_s *priv = (FAR struct m25p_dev_s *)dev;
size_t blocksleft = nblocks;
fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
/* Lock the SPI bus and write each page to FLASH */
m25p_lock(priv->dev);
while (blocksleft-- > 0)
{
m25p_pagewrite(priv, buffer, startblock);
startblock++;
}
m25p_unlock(priv->dev);
return nblocks;
}
/************************************************************************************
* Name: m25p_read
************************************************************************************/
static ssize_t m25p_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes,
FAR uint8_t *buffer)
{
FAR struct m25p_dev_s *priv = (FAR struct m25p_dev_s *)dev;
fvdbg("offset: %08lx nbytes: %d\n", (long)offset, (int)nbytes);
/* Wait for any preceding write to complete. We could simplify things by
* perform this wait at the end of each write operation (rather than at
* the beginning of ALL operations), but have the wait first will slightly
* improve performance.
*/
m25p_waitwritecomplete(priv);
/* Lock the SPI bus and select this FLASH part */
m25p_lock(priv->dev);
SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
/* Send "Read from Memory " instruction */
(void)SPI_SEND(priv->dev, M25P_READ);
/* Send the page offset high byte first. */
(void)SPI_SEND(priv->dev, (offset >> 16) & 0xff);
(void)SPI_SEND(priv->dev, (offset >> 8) & 0xff);
(void)SPI_SEND(priv->dev, offset & 0xff);
/* Then read all of the requested bytes */
SPI_RECVBLOCK(priv->dev, buffer, nbytes);
/* Deselect the FLASH and unlock the SPI bus */
SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
m25p_unlock(priv->dev);
fvdbg("return nbytes: %d\n", (int)nbytes);
return nbytes;
}
/************************************************************************************
* Name: m25p_ioctl
************************************************************************************/
static int m25p_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg)
{
FAR struct m25p_dev_s *priv = (FAR struct m25p_dev_s *)dev;
int ret = -EINVAL; /* Assume good command with bad parameters */
fvdbg("cmd: %d \n", cmd);
switch (cmd)
{
case MTDIOC_GEOMETRY:
{
FAR struct mtd_geometry_s *geo = (FAR struct mtd_geometry_s *)((uintptr_t)arg);
if (geo)
{
/* Populate the geometry structure with information need to know
* the capacity and how to access the device.
*
* NOTE: that the device is treated as though it where just an array
* of fixed size blocks. That is most likely not true, but the client
* will expect the device logic to do whatever is necessary to make it
* appear so.
*/
geo->blocksize = (1 << priv->pageshift);
geo->erasesize = (1 << priv->sectorshift);
geo->neraseblocks = priv->nsectors;
ret = OK;
fvdbg("blocksize: %d erasesize: %d neraseblocks: %d\n",
geo->blocksize, geo->erasesize, geo->neraseblocks);
}
}
break;
case MTDIOC_BULKERASE:
{
/* Erase the entire device */
m25p_lock(priv->dev);
ret = m25p_bulkerase(priv);
m25p_unlock(priv->dev);
}
break;
case MTDIOC_XIPBASE:
default:
ret = -ENOTTY; /* Bad command */
break;
}
fvdbg("return %d\n", ret);
return ret;
}
/************************************************************************************
* Public Functions
************************************************************************************/
/************************************************************************************
* Name: m25p_initialize
*
* Description:
* Create an initialize MTD device instance. MTD devices are not registered
* in the file system, but are created as instances that can be bound to
* other functions (such as a block or character driver front end).
*
************************************************************************************/
FAR struct mtd_dev_s *m25p_initialize(FAR struct spi_dev_s *dev)
{
FAR struct m25p_dev_s *priv;
int ret;
fvdbg("dev: %p\n", dev);
/* Allocate a state structure (we allocate the structure instead of using
* a fixed, static allocation so that we can handle multiple FLASH devices.
* The current implementation would handle only one FLASH part per SPI
* device (only because of the SPIDEV_FLASH definition) and so would have
* to be extended to handle multiple FLASH parts on the same SPI bus.
*/
priv = (FAR struct m25p_dev_s *)kmalloc(sizeof(struct m25p_dev_s));
if (priv)
{
/* Initialize the allocated structure */
priv->mtd.erase = m25p_erase;
priv->mtd.bread = m25p_bread;
priv->mtd.bwrite = m25p_bwrite;
priv->mtd.read = m25p_read;
priv->mtd.ioctl = m25p_ioctl;
priv->dev = dev;
/* Deselect the FLASH */
SPI_SELECT(dev, SPIDEV_FLASH, false);
/* Identify the FLASH chip and get its capacity */
ret = m25p_readid(priv);
if (ret != OK)
{
/* Unrecognized! Discard all of that work we just did and return NULL */
fdbg("Unrecognized\n");
kfree(priv);
priv = NULL;
}
}
/* Return the implementation-specific state structure as the MTD device */
fvdbg("Return %p\n", priv);
return (FAR struct mtd_dev_s *)priv;
}
+417
View File
@@ -0,0 +1,417 @@
/****************************************************************************
* drivers/mtd/rammtd.c
*
* Copyright (C) 2011-2012 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/kmalloc.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/mtd.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Configuration ************************************************************/
#ifndef CONFIG_RAMMTD_BLOCKSIZE
# define CONFIG_RAMMTD_BLOCKSIZE 512
#endif
#ifndef CONFIG_RAMMTD_ERASESIZE
# define CONFIG_RAMMTD_ERASESIZE 4096
#endif
#ifndef CONFIG_RAMMTD_ERASESTATE
# define CONFIG_RAMMTD_ERASESTATE 0xff
#endif
#if CONFIG_RAMMTD_ERASESTATE != 0xff && CONFIG_RAMMTD_ERASESTATE != 0x00
# error "Unsupported value for CONFIG_RAMMTD_ERASESTATE"
#endif
#if CONFIG_RAMMTD_BLOCKSIZE > CONFIG_RAMMTD_ERASESIZE
# error "Must have CONFIG_RAMMTD_BLOCKSIZE <= CONFIG_RAMMTD_ERASESIZE"
#endif
#undef CONFIG_RAMMTD_BLKPER
#define CONFIG_RAMMTD_BLKPER (CONFIG_RAMMTD_ERASESIZE/CONFIG_RAMMTD_BLOCKSIZE)
#if CONFIG_RAMMTD_BLKPER*CONFIG_RAMMTD_BLOCKSIZE != CONFIG_RAMMTD_ERASESIZE
# error "CONFIG_RAMMTD_ERASESIZE must be an even multiple of CONFIG_RAMMTD_BLOCKSIZE"
#endif
/****************************************************************************
* Private Types
****************************************************************************/
/* This type represents the state of the MTD device. The struct mtd_dev_s
* must appear at the beginning of the definition so that you can freely
* cast between pointers to struct mtd_dev_s and struct ram_dev_s.
*/
struct ram_dev_s
{
struct mtd_dev_s mtd; /* MTD device */
FAR uint8_t *start; /* Start of RAM */
size_t nblocks; /* Number of erase blocks */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* The RAM MTD driver may be useful just as it is, but another good use of
* the RAM MTD driver is as a FLASH simulation -- to support testing of FLASH
* based logic without having FLASH. CONFIG_RAMMTD_FLASHSIM will add some
* extra logic to improve the level of FLASH simulation.
*/
#define ram_read(dest, src, len) memcpy(dest, src, len)
#ifdef CONFIG_RAMMTD_FLASHSIM
static void *ram_write(FAR void *dest, FAR const void *src, size_t len);
#else
# define ram_write(dest, src, len) memcpy(dest, src, len)
#endif
/* MTD driver methods */
static int ram_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks);
static ssize_t ram_bread(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks,
FAR uint8_t *buf);
static ssize_t ram_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks,
FAR const uint8_t *buf);
static int ram_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg);
/****************************************************************************
* Private Data
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: ram_write
****************************************************************************/
#ifdef CONFIG_RAMMTD_FLASHSIM
static void *ram_write(FAR void *dest, FAR const void *src, size_t len)
{
FAR uint8_t *pout = (FAR uint8_t *)dest;
FAR const uint8_t *pin = (FAR const uint8_t *)src;
while (len-- > 0)
{
/* Get the source and destination values */
uint8_t oldvalue = *pout;
uint8_t srcvalue = *pin++;
uint8_t newvalue;
/* Get the new destination value, accounting for bits that cannot be
* changes because they are not in the erased state.
*/
#if CONFIG_RAMMTD_ERASESTATE == 0xff
newvalue = oldvalue & srcvalue; /* We can only clear bits */
#else /* CONFIG_RAMMTD_ERASESTATE == 0x00 */
newvalue = oldvalue | srcvalue; /* We can only set bits */
#endif
/* Report any attempt to change the value of bits that are not in the
* erased state.
*/
#ifdef CONFIG_DEBUG
if (newvalue != srcvalue)
{
dbg("ERROR: Bad write: source=%02x dest=%02x result=%02x\n",
srcvalue, oldvalue, newvalue);
}
#endif
/* Write the modified value to simulated FLASH */
*pout++ = newvalue;
}
return dest;
}
#endif
/****************************************************************************
* Name: ram_erase
****************************************************************************/
static int ram_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks)
{
FAR struct ram_dev_s *priv = (FAR struct ram_dev_s *)dev;
off_t offset;
size_t nbytes;
DEBUGASSERT(dev);
/* Don't let the erase exceed the size of the ram buffer */
if (startblock >= priv->nblocks)
{
return 0;
}
if (startblock + nblocks > priv->nblocks)
{
nblocks = priv->nblocks - startblock;
}
/* Convert the erase block to a logical block and the number of blocks
* in logical block numbers
*/
startblock *= CONFIG_RAMMTD_BLKPER;
nblocks *= CONFIG_RAMMTD_BLKPER;
/* Get the offset corresponding to the first block and the size
* corresponding to the number of blocks.
*/
offset = startblock * CONFIG_RAMMTD_BLOCKSIZE;
nbytes = nblocks * CONFIG_RAMMTD_BLOCKSIZE;
/* Then erase the data in RAM */
memset(&priv->start[offset], CONFIG_RAMMTD_ERASESTATE, nbytes);
return OK;
}
/****************************************************************************
* Name: ram_bread
****************************************************************************/
static ssize_t ram_bread(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks,
FAR uint8_t *buf)
{
FAR struct ram_dev_s *priv = (FAR struct ram_dev_s *)dev;
off_t offset;
off_t maxblock;
size_t nbytes;
DEBUGASSERT(dev && buf);
/* Don't let the read exceed the size of the ram buffer */
maxblock = priv->nblocks * CONFIG_RAMMTD_BLKPER;
if (startblock >= maxblock)
{
return 0;
}
if (startblock + nblocks > maxblock)
{
nblocks = maxblock - startblock;
}
/* Get the offset corresponding to the first block and the size
* corresponding to the number of blocks.
*/
offset = startblock * CONFIG_RAMMTD_BLOCKSIZE;
nbytes = nblocks * CONFIG_RAMMTD_BLOCKSIZE;
/* Then read the data frp, RAM */
ram_read(buf, &priv->start[offset], nbytes);
return nblocks;
}
/****************************************************************************
* Name: ram_bwrite
****************************************************************************/
static ssize_t ram_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
size_t nblocks, FAR const uint8_t *buf)
{
FAR struct ram_dev_s *priv = (FAR struct ram_dev_s *)dev;
off_t offset;
off_t maxblock;
size_t nbytes;
DEBUGASSERT(dev && buf);
/* Don't let the write exceed the size of the ram buffer */
maxblock = priv->nblocks * CONFIG_RAMMTD_BLKPER;
if (startblock >= maxblock)
{
return 0;
}
if (startblock + nblocks > maxblock)
{
nblocks = maxblock - startblock;
}
/* Get the offset corresponding to the first block and the size
* corresponding to the number of blocks.
*/
offset = startblock * CONFIG_RAMMTD_BLOCKSIZE;
nbytes = nblocks * CONFIG_RAMMTD_BLOCKSIZE;
/* Then write the data to RAM */
ram_write(&priv->start[offset], buf, nbytes);
return nblocks;
}
/****************************************************************************
* Name: ram_ioctl
****************************************************************************/
static int ram_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg)
{
FAR struct ram_dev_s *priv = (FAR struct ram_dev_s *)dev;
int ret = -EINVAL; /* Assume good command with bad parameters */
switch (cmd)
{
case MTDIOC_GEOMETRY:
{
FAR struct mtd_geometry_s *geo = (FAR struct mtd_geometry_s *)((uintptr_t)arg);
if (geo)
{
/* Populate the geometry structure with information need to know
* the capacity and how to access the device.
*/
geo->blocksize = CONFIG_RAMMTD_BLOCKSIZE;
geo->erasesize = CONFIG_RAMMTD_ERASESIZE;
geo->neraseblocks = priv->nblocks;
ret = OK;
}
}
break;
case MTDIOC_XIPBASE:
{
FAR void **ppv = (FAR void**)((uintptr_t)arg);
if (ppv)
{
/* Return (void*) base address of device memory */
*ppv = (FAR void*)priv->start;
ret = OK;
}
}
break;
case MTDIOC_BULKERASE:
{
size_t size = priv->nblocks * CONFIG_RAMMTD_ERASESIZE;
/* Erase the entire device */
memset(priv->start, CONFIG_RAMMTD_ERASESTATE, size);
ret = OK;
}
break;
default:
ret = -ENOTTY; /* Bad command */
break;
}
return ret;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: rammtd_initialize
*
* Description:
* Create and initialize a RAM MTD device instance.
*
* Input Parameters:
* start - Address of the beginning of the allocated RAM regions.
* size - The size in bytes of the allocated RAM region.
*
****************************************************************************/
FAR struct mtd_dev_s *rammtd_initialize(FAR uint8_t *start, size_t size)
{
FAR struct ram_dev_s *priv;
size_t nblocks;
/* Create an instance of the RAM MTD device state structure */
priv = (FAR struct ram_dev_s *)kmalloc(sizeof(struct ram_dev_s));
if (!priv)
{
fdbg("Failed to allocate the RAM MTD state structure\n");
return NULL;
}
/* Force the size to be an even number of the erase block size */
nblocks = size / CONFIG_RAMMTD_ERASESIZE;
if (nblocks <= 0)
{
fdbg("Need to provide at least one full erase block\n");
return NULL;
}
/* Perform initialization as necessary */
priv->mtd.erase = ram_erase;
priv->mtd.bread = ram_bread;
priv->mtd.bwrite = ram_bwrite;
priv->mtd.ioctl = ram_ioctl;
priv->mtd.erase = ram_erase;
priv->start = start;
priv->nblocks = nblocks;
return &priv->mtd;
}
+674
View File
@@ -0,0 +1,674 @@
/************************************************************************************
* drivers/mtd/ramtron.c
* Driver for SPI-based RAMTRON NVRAM Devices FM25V10 and others (not tested)
*
* Copyright (C) 2011 Uros Platise. All rights reserved.
* Copyright (C) 2009-2010, 2012 Gregory Nutt. All rights reserved.
* Author: Uros Platise <uros.platise@isotel.eu>
* Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
************************************************************************************/
/* OPTIONS:
* - additional non-jedec standard device: FM25H20
* must be enabled with the CONFIG_RAMTRON_FRAM_NON_JEDEC=y
*
* NOTE:
* - frequency is fixed to desired max by RAMTRON_INIT_CLK_MAX
* if new devices with different speed arrive, then SETFREQUENCY()
* needs to handle freq changes and INIT_CLK_MAX must be reduced
* to fit all devices. Note that STM32_SPI driver is prone to
* too high freq. parameters and limit it within physical constraints.
*
* TODO:
* - add support for sleep
* - add support for faster read FSTRD command
*/
/************************************************************************************
* Included Files
************************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <errno.h>
#include <debug.h>
#include <assert.h>
#include <nuttx/kmalloc.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/spi.h>
#include <nuttx/mtd.h>
/************************************************************************************
* Pre-processor Definitions
************************************************************************************/
/* RAMTRON devices are flat!
* For purpose of the VFAT file system we emulate the following configuration:
*/
#define RAMTRON_EMULATE_SECTOR_SHIFT 9
#define RAMTRON_EMULATE_PAGE_SHIFT 9
/* RAMTRON Indentification register values */
#define RAMTRON_MANUFACTURER 0x7F
#define RAMTRON_MEMORY_TYPE 0xC2
/* Instructions:
* Command Value N Description Addr Dummy Data */
#define RAMTRON_WREN 0x06 /* 1 Write Enable 0 0 0 */
#define RAMTRON_WRDI 0x04 /* 1 Write Disable 0 0 0 */
#define RAMTRON_RDSR 0x05 /* 1 Read Status Register 0 0 >=1 */
#define RAMTRON_WRSR 0x01 /* 1 Write Status Register 0 0 1 */
#define RAMTRON_READ 0x03 /* 1 Read Data Bytes A 0 >=1 */
#define RAMTRON_FSTRD 0x0b /* 1 Higher speed read A 1 >=1 */
#define RAMTRON_WRITE 0x02 /* 1 Write A 0 1-256 */
#define RAMTRON_SLEEP 0xb9 // TODO:
#define RAMTRON_RDID 0x9f /* 1 Read Identification 0 0 1-3 */
#define RAMTRON_SN 0xc3 // TODO:
/* Status register bit definitions */
#define RAMTRON_SR_WIP (1 << 0) /* Bit 0: Write in progress bit */
#define RAMTRON_SR_WEL (1 << 1) /* Bit 1: Write enable latch bit */
#define RAMTRON_SR_BP_SHIFT (2) /* Bits 2-4: Block protect bits */
#define RAMTRON_SR_BP_MASK (7 << RAMTRON_SR_BP_SHIFT)
# define RAMTRON_SR_BP_NONE (0 << RAMTRON_SR_BP_SHIFT) /* Unprotected */
# define RAMTRON_SR_BP_UPPER64th (1 << RAMTRON_SR_BP_SHIFT) /* Upper 64th */
# define RAMTRON_SR_BP_UPPER32nd (2 << RAMTRON_SR_BP_SHIFT) /* Upper 32nd */
# define RAMTRON_SR_BP_UPPER16th (3 << RAMTRON_SR_BP_SHIFT) /* Upper 16th */
# define RAMTRON_SR_BP_UPPER8th (4 << RAMTRON_SR_BP_SHIFT) /* Upper 8th */
# define RAMTRON_SR_BP_UPPERQTR (5 << RAMTRON_SR_BP_SHIFT) /* Upper quarter */
# define RAMTRON_SR_BP_UPPERHALF (6 << RAMTRON_SR_BP_SHIFT) /* Upper half */
# define RAMTRON_SR_BP_ALL (7 << RAMTRON_SR_BP_SHIFT) /* All sectors */
#define RAMTRON_SR_SRWD (1 << 7) /* Bit 7: Status register write protect */
#define RAMTRON_DUMMY 0xa5
/************************************************************************************
* Private Types
************************************************************************************/
struct ramtron_parts_s
{
const char *name;
uint8_t id1;
uint8_t id2;
uint32_t size;
uint8_t addr_len;
uint32_t speed;
};
/* This type represents the state of the MTD device. The struct mtd_dev_s
* must appear at the beginning of the definition so that you can freely
* cast between pointers to struct mtd_dev_s and struct ramtron_dev_s.
*/
struct ramtron_dev_s
{
struct mtd_dev_s mtd; /* MTD interface */
FAR struct spi_dev_s *dev; /* Saved SPI interface instance */
uint8_t sectorshift;
uint8_t pageshift;
uint16_t nsectors;
uint32_t npages;
const struct ramtron_parts_s *part; /* part instance */
};
/************************************************************************************
* Supported Part Lists
************************************************************************************/
// Defines the initial speed compatible with all devices. In case of RAMTRON
// the defined devices within the part list have all the same speed.
#define RAMTRON_INIT_CLK_MAX 40000000UL
static struct ramtron_parts_s ramtron_parts[] =
{
{
"FM25V02", /* name */
0x22, /* id1 */
0x00, /* id2 */
32L*1024L, /* size */
2, /* addr_len */
40000000 /* speed */
},
{
"FM25VN02", /* name */
0x22, /* id1 */
0x01, /* id2 */
32L*1024L, /* size */
2, /* addr_len */
40000000 /* speed */
},
{
"FM25V05", /* name */
0x23, /* id1 */
0x00, /* id2 */
64L*1024L, /* size */
2, /* addr_len */
40000000 /* speed */
},
{
"FM25VN05", /* name */
0x23, /* id1 */
0x01, /* id2 */
64L*1024L, /* size */
2, /* addr_len */
40000000 /* speed */
},
{
"FM25V10", /* name */
0x24, /* id1 */
0x00, /* id2 */
128L*1024L, /* size */
3, /* addr_len */
40000000 /* speed */
},
{
"FM25VN10", /* name */
0x24, /* id1 */
0x01, /* id2 */
128L*1024L, /* size */
3, /* addr_len */
40000000 /* speed */
},
#ifdef CONFIG_RAMTRON_FRAM_NON_JEDEC
{
"FM25H20", /* name */
0xff, /* id1 */
0xff, /* id2 */
256L*1024L, /* size */
3, /* addr_len */
40000000 /* speed */
},
{
NULL, /* name */
0, /* id1 */
0, /* id2 */
0, /* size */
0, /* addr_len */
0 /* speed */
}
#endif
};
/************************************************************************************
* Private Function Prototypes
************************************************************************************/
/* Helpers */
static void ramtron_lock(FAR struct spi_dev_s *dev);
static inline void ramtron_unlock(FAR struct spi_dev_s *dev);
static inline int ramtron_readid(struct ramtron_dev_s *priv);
static void ramtron_waitwritecomplete(struct ramtron_dev_s *priv);
static void ramtron_writeenable(struct ramtron_dev_s *priv);
static inline void ramtron_pagewrite(struct ramtron_dev_s *priv, FAR const uint8_t *buffer,
off_t offset);
/* MTD driver methods */
static int ramtron_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks);
static ssize_t ramtron_bread(FAR struct mtd_dev_s *dev, off_t startblock,
size_t nblocks, FAR uint8_t *buf);
static ssize_t ramtron_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
size_t nblocks, FAR const uint8_t *buf);
static ssize_t ramtron_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes,
FAR uint8_t *buffer);
static int ramtron_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg);
/************************************************************************************
* Private Data
************************************************************************************/
/************************************************************************************
* Private Functions
************************************************************************************/
/************************************************************************************
* Name: ramtron_lock
************************************************************************************/
static void ramtron_lock(FAR struct spi_dev_s *dev)
{
/* On SPI busses where there are multiple devices, it will be necessary to
* lock SPI to have exclusive access to the busses for a sequence of
* transfers. The bus should be locked before the chip is selected.
*
* This is a blocking call and will not return until we have exclusiv access to
* the SPI buss. We will retain that exclusive access until the bus is unlocked.
*/
(void)SPI_LOCK(dev, true);
/* After locking the SPI bus, the we also need call the setfrequency, setbits, and
* setmode methods to make sure that the SPI is properly configured for the device.
* If the SPI buss is being shared, then it may have been left in an incompatible
* state.
*/
SPI_SETMODE(dev, SPIDEV_MODE3);
SPI_SETBITS(dev, 8);
(void)SPI_SETFREQUENCY(dev, RAMTRON_INIT_CLK_MAX);
}
/************************************************************************************
* Name: ramtron_unlock
************************************************************************************/
static inline void ramtron_unlock(FAR struct spi_dev_s *dev)
{
(void)SPI_LOCK(dev, false);
}
/************************************************************************************
* Name: ramtron_readid
************************************************************************************/
static inline int ramtron_readid(struct ramtron_dev_s *priv)
{
uint16_t manufacturer, memory, capacity, part;
int i;
fvdbg("priv: %p\n", priv);
/* Lock the SPI bus, configure the bus, and select this FLASH part. */
ramtron_lock(priv->dev);
SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
/* Send the "Read ID (RDID)" command and read the first three ID bytes */
(void)SPI_SEND(priv->dev, RAMTRON_RDID);
for (i=0; i<6; i++) manufacturer = SPI_SEND(priv->dev, RAMTRON_DUMMY);
memory = SPI_SEND(priv->dev, RAMTRON_DUMMY);
capacity = SPI_SEND(priv->dev, RAMTRON_DUMMY); // fram.id1
part = SPI_SEND(priv->dev, RAMTRON_DUMMY); // fram.id2
/* Deselect the FLASH and unlock the bus */
SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
ramtron_unlock(priv->dev);
// Select part from the part list
for (priv->part = ramtron_parts;
priv->part->name != NULL && !(priv->part->id1 == capacity && priv->part->id2 == part);
priv->part++);
if (priv->part->name) {
fvdbg("RAMTRON %s of size %d bytes (mf:%02x mem:%02x cap:%02x part:%02x)\n",
priv->part->name, priv->part->size, manufacturer, memory, capacity, part);
priv->sectorshift = RAMTRON_EMULATE_SECTOR_SHIFT;
priv->nsectors = priv->part->size / (1 << RAMTRON_EMULATE_SECTOR_SHIFT);
priv->pageshift = RAMTRON_EMULATE_PAGE_SHIFT;
priv->npages = priv->part->size / (1 << RAMTRON_EMULATE_PAGE_SHIFT);
return OK;
}
fvdbg("RAMTRON device not found\n");
return -ENODEV;
}
/************************************************************************************
* Name: ramtron_waitwritecomplete
************************************************************************************/
static void ramtron_waitwritecomplete(struct ramtron_dev_s *priv)
{
uint8_t status;
/* Select this FLASH part */
SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
/* Send "Read Status Register (RDSR)" command */
(void)SPI_SEND(priv->dev, RAMTRON_RDSR);
/* Loop as long as the memory is busy with a write cycle */
do
{
/* Send a dummy byte to generate the clock needed to shift out the status */
status = SPI_SEND(priv->dev, RAMTRON_DUMMY);
}
while ((status & RAMTRON_SR_WIP) != 0);
/* Deselect the FLASH */
SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
fvdbg("Complete\n");
}
/************************************************************************************
* Name: ramtron_writeenable
************************************************************************************/
static void ramtron_writeenable(struct ramtron_dev_s *priv)
{
/* Select this FLASH part */
SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
/* Send "Write Enable (WREN)" command */
(void)SPI_SEND(priv->dev, RAMTRON_WREN);
/* Deselect the FLASH */
SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
fvdbg("Enabled\n");
}
/************************************************************************************
* Name: ramtron_sendaddr
************************************************************************************/
static inline void ramtron_sendaddr(const struct ramtron_dev_s *priv, uint32_t addr)
{
DEBUGASSERT(priv->part->addr_len == 3 || priv->part->addr_len == 2);
if (priv->part->addr_len == 3)
(void)SPI_SEND(priv->dev, (addr >> 16) & 0xff);
(void)SPI_SEND(priv->dev, (addr >> 8) & 0xff);
(void)SPI_SEND(priv->dev, addr & 0xff);
}
/************************************************************************************
* Name: ramtron_pagewrite
************************************************************************************/
static inline void ramtron_pagewrite(struct ramtron_dev_s *priv, FAR const uint8_t *buffer,
off_t page)
{
off_t offset = page << priv->pageshift;
fvdbg("page: %08lx offset: %08lx\n", (long)page, (long)offset);
/* Wait for any preceding write to complete. We could simplify things by
* perform this wait at the end of each write operation (rather than at
* the beginning of ALL operations), but have the wait first will slightly
* improve performance.
*/
ramtron_waitwritecomplete(priv);
/* Enable the write access to the FLASH */
ramtron_writeenable(priv);
/* Select this FLASH part */
SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
/* Send "Page Program (PP)" command */
(void)SPI_SEND(priv->dev, RAMTRON_WRITE);
/* Send the page offset high byte first. */
ramtron_sendaddr(priv, offset);
/* Then write the specified number of bytes */
SPI_SNDBLOCK(priv->dev, buffer, 1 << priv->pageshift);
/* Deselect the FLASH: Chip Select high */
SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
fvdbg("Written\n");
}
/************************************************************************************
* Name: ramtron_erase
************************************************************************************/
static int ramtron_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks)
{
fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
fvdbg("On RAMTRON devices erasing makes no sense, returning as OK\n");
return (int)nblocks;
}
/************************************************************************************
* Name: ramtron_bread
************************************************************************************/
static ssize_t ramtron_bread(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks,
FAR uint8_t *buffer)
{
FAR struct ramtron_dev_s *priv = (FAR struct ramtron_dev_s *)dev;
ssize_t nbytes;
fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
/* On this device, we can handle the block read just like the byte-oriented read */
nbytes = ramtron_read(dev, startblock << priv->pageshift, nblocks << priv->pageshift, buffer);
if (nbytes > 0)
{
return nbytes >> priv->pageshift;
}
return (int)nbytes;
}
/************************************************************************************
* Name: ramtron_bwrite
************************************************************************************/
static ssize_t ramtron_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks,
FAR const uint8_t *buffer)
{
FAR struct ramtron_dev_s *priv = (FAR struct ramtron_dev_s *)dev;
size_t blocksleft = nblocks;
fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
/* Lock the SPI bus and write each page to FLASH */
ramtron_lock(priv->dev);
while (blocksleft-- > 0)
{
ramtron_pagewrite(priv, buffer, startblock);
startblock++;
}
ramtron_unlock(priv->dev);
return nblocks;
}
/************************************************************************************
* Name: ramtron_read
************************************************************************************/
static ssize_t ramtron_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes,
FAR uint8_t *buffer)
{
FAR struct ramtron_dev_s *priv = (FAR struct ramtron_dev_s *)dev;
fvdbg("offset: %08lx nbytes: %d\n", (long)offset, (int)nbytes);
/* Wait for any preceding write to complete. We could simplify things by
* perform this wait at the end of each write operation (rather than at
* the beginning of ALL operations), but have the wait first will slightly
* improve performance.
*/
ramtron_waitwritecomplete(priv);
/* Lock the SPI bus and select this FLASH part */
ramtron_lock(priv->dev);
SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
/* Send "Read from Memory " instruction */
(void)SPI_SEND(priv->dev, RAMTRON_READ);
/* Send the page offset high byte first. */
ramtron_sendaddr(priv, offset);
/* Then read all of the requested bytes */
SPI_RECVBLOCK(priv->dev, buffer, nbytes);
/* Deselect the FLASH and unlock the SPI bus */
SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
ramtron_unlock(priv->dev);
fvdbg("return nbytes: %d\n", (int)nbytes);
return nbytes;
}
/************************************************************************************
* Name: ramtron_ioctl
************************************************************************************/
static int ramtron_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg)
{
FAR struct ramtron_dev_s *priv = (FAR struct ramtron_dev_s *)dev;
int ret = -EINVAL; /* Assume good command with bad parameters */
fvdbg("cmd: %d \n", cmd);
switch (cmd)
{
case MTDIOC_GEOMETRY:
{
FAR struct mtd_geometry_s *geo = (FAR struct mtd_geometry_s *)((uintptr_t)arg);
if (geo)
{
/* Populate the geometry structure with information need to know
* the capacity and how to access the device.
*
* NOTE: that the device is treated as though it where just an array
* of fixed size blocks. That is most likely not true, but the client
* will expect the device logic to do whatever is necessary to make it
* appear so.
*/
geo->blocksize = (1 << priv->pageshift);
geo->erasesize = (1 << priv->sectorshift);
geo->neraseblocks = priv->nsectors;
ret = OK;
fvdbg("blocksize: %d erasesize: %d neraseblocks: %d\n",
geo->blocksize, geo->erasesize, geo->neraseblocks);
}
}
break;
case MTDIOC_BULKERASE:
fvdbg("BULDERASE: Makes no sense in ramtron. Let's confirm operation as OK\n");
ret = OK;
break;
case MTDIOC_XIPBASE:
default:
ret = -ENOTTY; /* Bad command */
break;
}
fvdbg("return %d\n", ret);
return ret;
}
/************************************************************************************
* Public Functions
************************************************************************************/
/************************************************************************************
* Name: ramtron_initialize
*
* Description:
* Create an initialize MTD device instance. MTD devices are not registered
* in the file system, but are created as instances that can be bound to
* other functions (such as a block or character driver front end).
*
************************************************************************************/
FAR struct mtd_dev_s *ramtron_initialize(FAR struct spi_dev_s *dev)
{
FAR struct ramtron_dev_s *priv;
fvdbg("dev: %p\n", dev);
/* Allocate a state structure (we allocate the structure instead of using
* a fixed, static allocation so that we can handle multiple FLASH devices.
* The current implementation would handle only one FLASH part per SPI
* device (only because of the SPIDEV_FLASH definition) and so would have
* to be extended to handle multiple FLASH parts on the same SPI bus.
*/
priv = (FAR struct ramtron_dev_s *)kmalloc(sizeof(struct ramtron_dev_s));
if (priv)
{
/* Initialize the allocated structure */
priv->mtd.erase = ramtron_erase;
priv->mtd.bread = ramtron_bread;
priv->mtd.bwrite = ramtron_bwrite;
priv->mtd.read = ramtron_read;
priv->mtd.ioctl = ramtron_ioctl;
priv->dev = dev;
/* Deselect the FLASH */
SPI_SELECT(dev, SPIDEV_FLASH, false);
/* Identify the FLASH chip and get its capacity */
if (ramtron_readid(priv) != OK)
{
/* Unrecognized! Discard all of that work we just did and return NULL */
kfree(priv);
priv = NULL;
}
}
/* Return the implementation-specific state structure as the MTD device */
fvdbg("Return %p\n", priv);
return (FAR struct mtd_dev_s *)priv;
}
+275
View File
@@ -0,0 +1,275 @@
/****************************************************************************
* drivers/mtd/skeleton.c
*
* Copyright (C) 2011 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <stdint.h>
#include <errno.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/mtd.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Private Types
****************************************************************************/
/* This type represents the state of the MTD device. The struct mtd_dev_s
* must appear at the beginning of the definition so that you can freely
* cast between pointers to struct mtd_dev_s and struct skel_dev_s.
*/
struct skel_dev_s
{
struct mtd_dev_s mtd;
/* Other implementation specific data may follow here */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* MTD driver methods */
static int skel_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks);
static ssize_t skel_bread(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks,
FAR uint8_t *buf);
static ssize_t skel_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks,
FAR const uint8_t *buf);
static ssize_t skel_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes,
FAR uint8_t *buffer);
static int skel_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg);
/****************************************************************************
* Private Data
****************************************************************************/
static struct skel_dev_s g_skeldev =
{
{ skel_erase, skel_rbead, skel_bwrite, skel_read, skel_ioctl },
/* Initialization of any other implemenation specific data goes here */
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: skel_erase
****************************************************************************/
static int skel_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks)
{
FAR struct skel_dev_s *priv = (FAR struct skel_dev_s *)dev;
/* The interface definition assumes that all erase blocks ar the same size.
* If that is not true for this particular device, then transform the
* start block and nblocks as necessary.
*/
/* Erase the specified blocks and return status (OK or a negated errno) */
return OK;
}
/****************************************************************************
* Name: skel_bread
****************************************************************************/
static ssize_t skel_bread(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks,
FAR uint8_t *buf)
{
FAR struct skel_dev_s *priv = (FAR struct skel_dev_s *)dev;
/* The interface definition assumes that all read/write blocks ar the same size.
* If that is not true for this particular device, then transform the
* start block and nblocks as necessary.
*/
/* Read the specified blocks into the provided user buffer and return status
* (The positive, number of blocks actually read or a negated errno).
*/
return 0;
}
/****************************************************************************
* Name: skel_bwrite
****************************************************************************/
static ssize_t skel_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks,
FAR const uint8_t *buf)
{
FAR struct skel_dev_s *priv = (FAR struct skel_dev_s *)dev;
/* The interface definition assumes that all read/write blocks ar the same size.
* If that is not true for this particular device, then transform the
* start block and nblocks as necessary.
*/
/* Write the specified blocks from the provided user buffer and return status
* (The positive, number of blocks actually written or a negated errno)
*/
return 0;
}
/****************************************************************************
* Name: skel_read
****************************************************************************/
static ssize_t skel_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes,
FAR uint8_t *buffer)
{
FAR struct skel_dev_s *priv = (FAR struct skel_dev_s *)dev;
/* Some devices may support byte oriented read (optional). Byte-oriented
* writing is inherently block oriented on most MTD devices and is not supported.
* It is recommended that low-level drivers not support read() if it requires
* buffering -- let the higher level logic handle that. If the read method is
* not implemented, just set the method pointer to NULL in the struct mtd_dev_s
* instance.
*/
/* The interface definition assumes that all read/write blocks ar the same size.
* If that is not true for this particular device, then transform the
* start block and nblocks as necessary.
*/
/* Read the specified blocks into the provided user buffer and return status
* (The positive, number of blocks actually read or a negated errno)
*/
return 0;
}
/****************************************************************************
* Name: skel_ioctl
****************************************************************************/
static int skel_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg)
{
FAR struct skel_dev_s *priv = (FAR struct skel_dev_s *)dev;
int ret = -EINVAL; /* Assume good command with bad parameters */
switch (cmd)
{
case MTDIOC_GEOMETRY:
{
FAR struct mtd_geometry_s *geo = (FARstruct mtd_geometry_s *)arg;
if (geo)
{
/* Populate the geometry structure with information need to know
* the capacity and how to access the device.
*
* NOTE: that the device is treated as though it where just an array
* of fixed size blocks. That is most likely not true, but the client
* will expect the device logic to do whatever is necessary to make it
* appear so.
*/
geo->blocksize = 512; /* Size of one read/write block */
geo->erasesize = 4096; /* Size of one erase block */
geo->neraseblocks = 1024; /* Number of erase blocks */
ret = OK;
}
}
break;
case MTDIOC_XIPBASE:
{
FAR void **ppv = (FAR void**)arg;
if (ppv)
{
/* If media is directly acccesible, return (void*) base address
* of device memory. NULL otherwise. It is acceptable to omit
* this case altogether and simply return -ENOTTY.
*/
*ppv = NULL;
ret = OK;
}
}
break;
case MTDIOC_BULKERASE:
{
/* Erase the entire device */
ret = OK;
}
break;
default:
ret = -ENOTTY; /* Bad command */
break;
}
return ret;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: skel_initialize
*
* Description:
* Create and initialize an MTD device instance. MTD devices are not
* registered in the file system, but are created as instances that can
* be bound to other functions (such as a block or character driver front
* end).
*
****************************************************************************/
void skel_initialize(void)
{
/* Perform initialization as necessary */
/* Return the implementation-specific state structure as the MTD device */
return (FAR struct mtd_dev_s *)&g_skeldev;
}
File diff suppressed because it is too large Load Diff