summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpatacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3>2013-02-12 00:52:52 +0000
committerpatacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3>2013-02-12 00:52:52 +0000
commit5e92f8bf9f902ac2866d70180b448dc0b4228318 (patch)
treefddb1cd4d6005becf930622cb36cb53ac3c6ec12
parente7a073d41bdcd9b7d0f15411d1671a5786a0aaed (diff)
downloadnuttx-5e92f8bf9f902ac2866d70180b448dc0b4228318.tar.gz
nuttx-5e92f8bf9f902ac2866d70180b448dc0b4228318.tar.bz2
nuttx-5e92f8bf9f902ac2866d70180b448dc0b4228318.zip
Missed SST39VF driver in commit 45640
git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@5642 42af7a65-404d-4744-a932-0658087f49c3
-rw-r--r--nuttx/drivers/mtd/sst39vf.c847
1 files changed, 847 insertions, 0 deletions
diff --git a/nuttx/drivers/mtd/sst39vf.c b/nuttx/drivers/mtd/sst39vf.c
new file mode 100644
index 000000000..ac2e88b3e
--- /dev/null
+++ b/nuttx/drivers/mtd/sst39vf.c
@@ -0,0 +1,847 @@
+/****************************************************************************
+ * drivers/mtd/sst39vf.c
+ *
+ * Copyright (C) 2013 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 <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/clock.h>
+#include <nuttx/arch.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/mtd.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+/* Configuration */
+
+#ifndef CONFIG_SST39VF_BASE_ADDRESS
+# error "The FLASH base address was not provided (CONFIG_SST39VF_BASE_ADDRESS)"
+#endif
+
+/* MAP SST39VF address to a 16-bit bus address */
+
+#define SST39VF_ADDR(addr) \
+ (volatile FAR uint16_t *)(CONFIG_SST39VF_BASE_ADDRESS | (addr << 1))
+
+/* Timing */
+
+#define SST39VF_TBP_USEC 10 /* Word-Program Time (max); 7uS typical */
+#define SST39VF_TIDA_NSEC 150 /* Software ID Access and Exit Time (max) */
+#define SST39VF_TSE_MSEC 25 /* Sector-Erase 25 ms (max); 18 ms typical */
+#define SST39VF_TBE_MSEC 25 /* Block-Erase 25 ms (max); 18 ms typical */
+#define SST39VF_TSCE_MSEC 50 /* Chip-Erase 50 ms (max); */
+
+#define WORDWRITE_TIMEOUT 0x080000000
+
+/* IDs */
+
+#define SST_MANUFACTURER_ID 0xbf
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+/* This describes one chip in the SST39VF family */
+
+struct sst39vf_chip_s
+{
+#if 0 /* Not used */
+ bool top; /* Top protect SST39VF1602/3202 */
+#endif
+ uint16_t chipid; /* ID of the chip */
+#if 0 /* Not used */
+ uint16_t nblocks; /* Number of erase blocks */
+#endif
+ uint16_t nsectors; /* Number of erase-ablesectors */
+#if 0 /* Not used */
+ uint32_t blocksize; /* Size of one erase block */
+#endif
+ uint32_t sectorsize; /* Size of one sector */
+};
+
+/* This type holds one FLASH address and one 16-bit FLASH data value */
+
+struct sst39vf_wrinfo_s
+{
+ uintptr_t address;
+ uint16_t data;
+};
+
+/* 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 sst39vf_dev_s.
+ */
+
+struct sst39vf_dev_s
+{
+ struct mtd_dev_s mtd;
+ FAR const struct sst39vf_chip_s *chip;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+/* Low Level Helpers */
+
+static inline void sst39vf_flashwrite(FAR const struct sst39vf_wrinfo_s *wrinfo);
+static inline uint16_t sst39vf_flashread(uintptr_t address);
+static void sst39vf_writeseq(FAR const struct sst39vf_wrinfo_s *wrinfo, int nseq);
+static int sst39vf_chiperase(FAR struct sst39vf_dev_s *priv);
+static int sst39vf_sectorerase(FAR struct sst39vf_dev_s *priv,
+ uintptr_t sectaddr);
+static int sst39vf_writeword(FAR const struct sst39vf_wrinfo_s *wrinfo);
+
+/* MTD driver methods */
+
+static int sst39vf_erase(FAR struct mtd_dev_s *dev, off_t startblock,
+ size_t nblocks);
+static ssize_t sst39vf_bread(FAR struct mtd_dev_s *dev,
+ off_t startblock, size_t nblocks,
+ FAR uint8_t *buf);
+static ssize_t sst39vf_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
+ size_t nblocks, FAR const uint8_t *buf);
+static ssize_t sst39vf_read(FAR struct mtd_dev_s *dev, off_t offset,
+ size_t nbytes, FAR uint8_t *buffer);
+static int sst39vf_ioctl(FAR struct mtd_dev_s *dev, int cmd,
+ unsigned long arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct sst39vf_chip_s g_sst39vf1601 =
+{
+ /* false, top - Bottom hardware block protection */
+ 0x234b, /* chipid */
+ /* 32, nblocks */
+ 512, /* nsectors */
+ /* 64*1024, blocksize */
+ 4*1024, /* sectorsize */
+};
+
+static const struct sst39vf_chip_s g_sst39vf1602 =
+{
+ /* true, top - Top hardware block protection */
+ 0x234a, /* chipid */
+ /* 32, nblocks */
+ 512, /* nsectors */
+ /* 64*1024, blocksize */
+ 4*1024, /* sectorsize */
+};
+
+static const struct sst39vf_chip_s g_sst39vf3201 =
+{
+ /* false, top - Bottom hardware block protection */
+ 0x235b, /* chipid */
+ /* 64, nblocks */
+ 1024, /* nsectors */
+ /* 64*1024, blocksize */
+ 4*1024, /* sectorsize */
+};
+
+static const struct sst39vf_chip_s g_sst39vf3202 =
+{
+ /* true, top - Top hardware block protection */
+ 0x235a, /* chipid */
+ /* 64, nblocks */
+ 1024, /* nsectors */
+ /* 64*1024, blocksize */
+ 4*1024, /* sectorsize */
+};
+
+/* This structure holds the state of the MTD driver */
+
+static struct sst39vf_dev_s g_sst39vf =
+{
+ {
+ sst39vf_erase, /* erase method */
+ sst39vf_bread, /* bread method */
+ sst39vf_bwrite, /* bwrte method */
+ sst39vf_read, /* read method */
+ sst39vf_ioctl /* write method */
+ },
+ NULL /* Chip */
+};
+
+/* Command sequences */
+
+static const struct sst39vf_wrinfo_s g_wordprogram[3] =
+{
+ {0x5555, 0x00aa}, {0x2aaa, 0x0055}, {0x5555, 0x00a0} /* , {address, data} */
+};
+
+static const struct sst39vf_wrinfo_s g_sectorerase[5] =
+{
+ {0x5555, 0x00aa}, {0x2aaa, 0x0055}, {0x5555, 0x0080},
+ {0x5555, 0x00aa}, {0x2aaa, 0x0055} /* , {sector, 0x0030} */
+};
+
+#if 0 /* Not used */
+static const struct sst39vf_wrinfo_s g_blockerase[5] =
+{
+ {0x5555, 0x00aa}, {0x2aaa, 0x0055}, {0x5555, 0x80},
+ {0x5555, 0x00aa}, {0x2aaa, 0x0055} /* , {block, 0x0050} */
+};
+#endif
+
+static const struct sst39vf_wrinfo_s g_chiperase[6] =
+{
+ {0x5555, 0x00aa}, {0x2aaa, 0x0055}, {0x5555, 0x0080},
+ {0x5555, 0x00aa}, {0x2aaa, 0x0055}, {0x5555, 0x0010}
+};
+
+#if 0 /* Not used */
+static const struct sst39vf_wrinfo_s g_erasesuspend[1] =
+{
+ {0x5555, 0x00aa}
+};
+
+static const struct sst39vf_wrinfo_s g_eraseresume[1] =
+{
+ {0x5555, 0x00aa}
+};
+
+static const struct sst39vf_wrinfo_s g_querysecid[3] =
+{
+ {0x5555, 0x00aa}, {0x2aaa, 0x0055}, {0x5555, 0x0088}
+};
+
+static const struct sst39vf_wrinfo_s g_securityid_wordprogram[3] =
+{
+ {0x5555, 0x00aa}, {0x2aaa, 0x0055}, {0x5555, 0x00a5}, /* {address, data} */
+};
+
+static const struct sst39vf_wrinfo_s g_securityid_lockout[3] =
+{
+ {0x5555, 0x00aa}, {0x2aaa, 0x0055}, {0x5555, 0x0085} /* {0xXX, 0x0000} */
+};
+#endif
+
+static const struct sst39vf_wrinfo_s g_swid_entry[3] =
+{
+ {0x5555, 0x00aa}, {0x2aaa, 0x0055}, {0x5555, 0x0090}
+};
+
+#if 0 /* Not used */
+static const struct sst39vf_wrinfo_s g_cfiquery[3] =
+{
+ {0x5555, 0x00aa}, {0x2aaa, 0x0055}, {0x5555, 0x0080},
+};
+#endif
+
+static const struct sst39vf_wrinfo_s g_swid_exit[3] =
+{
+ {0x5555, 0x00aa}, {0x2aaa, 0x0055}, {0x5555, 0x00f0}
+};
+
+#if 0 /* Not used */
+static const struct sst39vf_wrinfo_s g_swid_exit2[1] =
+{
+ {0x0000, 0x00f0},
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: sst39vf_flashwrite
+ *
+ * Description:
+ * Write one value to FLASH
+ *
+ ****************************************************************************/
+
+static inline void sst39vf_flashwrite(FAR const struct sst39vf_wrinfo_s *wrinfo)
+{
+ volatile uint16_t *addr = SST39VF_ADDR(wrinfo->address);
+ *addr = wrinfo->data;
+}
+
+/****************************************************************************
+ * Name: sst39vf_flashread
+ *
+ * Description:
+ * Read one value from FLASH
+ *
+ ****************************************************************************/
+
+static inline uint16_t sst39vf_flashread(uintptr_t address)
+{
+ return *SST39VF_ADDR(address);
+}
+
+/****************************************************************************
+ * Name: sst39vf_writeseq
+ *
+ * Description:
+ * Write a sequence of values to FLASH
+ *
+ ****************************************************************************/
+
+static void sst39vf_writeseq(FAR const struct sst39vf_wrinfo_s *wrinfo, int nseq)
+{
+ while (nseq--)
+ {
+ sst39vf_flashwrite(wrinfo);
+ wrinfo++;
+ }
+}
+
+/****************************************************************************
+ * Name: sst39vf_checktoggle
+ *
+ * Description:
+ * Check for bit toggle
+ *
+ * "Toggle Bits (DQ6 and DQ2). During the internal Program or Erase
+ * operation, any consecutive attempts to read DQ6 will produce
+ * alternating “1”s and “0”s, i.e., toggling between 1 and 0. When
+ * the internal Program or Erase operation is completed, the DQ6 bit
+ * will stop toggling. The device is then ready for the next operation.
+ * For Sector-, Block-, or Chip-Erase, the toggle bit (DQ6) is valid
+ * after the rising edge of sixth WE# (or CE#) pulse. DQ6 will be set to
+ * “1” if a Read operation is attempted on an Erase-Suspended
+ * Sector/Block. If Program operation is initiated in a sector/block not
+ * selected in Erase-Suspend mode, DQ6 will toggle.
+ *
+ * "An additional Toggle Bit is available on DQ2, which can be used in
+ * conjunction with DQ6 to check whether a particular sector is being
+ * actively erased or erase-suspended. ... The Toggle Bit (DQ2) is valid
+ * after the rising edge of the last WE# (or CE#) pulse of Write operation.
+ * ..."
+ *
+ ****************************************************************************/
+
+static bool sst39vf_checktoggle(FAR const struct sst39vf_wrinfo_s *wrinfo)
+{
+ uint16_t value1;
+ uint16_t value2;
+
+ value1 = sst39vf_flashread(wrinfo->address);
+ value2 = sst39vf_flashread(wrinfo->address);
+
+ return (value1 == value2);
+}
+
+/****************************************************************************
+ * Name: sst39vf_waittoggle
+ *
+ * Description:
+ * Wait until the data is no longer toggling.
+ *
+ ****************************************************************************/
+
+static int sst39vf_waittoggle(FAR const struct sst39vf_wrinfo_s *wrinfo,
+ uint32_t retries)
+{
+ while (retries-- > 0)
+ {
+ if (sst39vf_checktoggle(wrinfo))
+ {
+ return OK;
+ }
+ }
+
+ return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Name: sst39vf_chiperase
+ *
+ * Description:
+ * Erase the entire chip
+ *
+ * "The SST39VF160x/320x provide a Chip-Erase operation, which allows the
+ * user to erase the entire memory array to the “1” state. This is useful
+ * when the entire device must be quickly erased. The Chip-Erase operation
+ * is initiated by executing a six-byte command sequence with Chip-Erase
+ * command (10H) at address 5555H in the last byte sequence. The Erase
+ * operation begins with the rising edge of the sixth WE# or CE#,
+ * whichever occurs first. During the Erase operation, the only valid
+ * read is Toggle Bit or Data# Polling... Any commands issued during the
+ * Chip-Erase operation are ignored. When WP# is low, any attempt to
+ * Chip-Erase will be ignored. During the command sequence, WP# should
+ * be statically held high or low."
+ *
+ ****************************************************************************/
+
+static int sst39vf_chiperase(FAR struct sst39vf_dev_s *priv)
+{
+#if 0
+ struct sst39vf_wrinfo_s wrinfo;
+ uint32_t start;
+ uint32_t elapsed;
+#endif
+
+ /* Send the sequence to erase the chip */
+
+ sst39vf_writeseq(g_chiperase, 6);
+
+ /* Use the data toggle delay method. The typical delay is 40 MSec. The
+ * maximum is 50 MSec. So using the data toggle delay method should give
+ * better chip erase performance by about 10MS.
+ */
+
+#if 0
+ wrinfo.address = CONFIG_SST39VF_BASE_ADDRESS;
+ wrinfo.data = 0xffff;
+
+ start = clock_systimer();
+ while (delay < SST39VF_TSCE_MSEC * MSEC_PER_TICK)
+ {
+ /* Check if the erase is complete */
+
+ if (sst39vf_checktoggle(&wrinfo))
+ {
+ return OK;
+ }
+
+ /* No, check if the timeout has elapsed */
+
+ elapsed = clock_systimer() - start;
+ if (elapsed > SST39VF_TSCE_MSEC * MSEC_PER_TICK)
+ {
+ return -ETIMEDOUT;
+ }
+
+ /* No, wait one system clock tick */
+
+ usleep(MSEC_PER_TICK * USEC_PER_MSEC);
+ }
+#else
+ /* Delay the maximum amount of time for the chip erase to complete. */
+
+ usleep(SST39VF_TSCE_MSEC * USEC_PER_MSEC);
+#endif
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: sst39vf_sectorerase
+ *
+ * Description:
+ * Erase the entire chip
+ *
+ * "... The Sector-Erase operation is initiated by executing a six-byte
+ * command sequence with Sector-Erase command (30H) and sector address
+ * (SA) in the last bus cycle.
+ *
+ * The sector ... address is latched on the falling edge of the sixth
+ * WE# pulse, while the command (30H or 50H) is latched on the rising edge
+ * of the sixth WE# pulse. The internal Erase operation begins after the
+ * sixth WE# pulse. The End-of-Erase operation can be determined using
+ * either Data# Polling or Toggle Bit methods."
+ *
+ ****************************************************************************/
+
+static int sst39vf_sectorerase(FAR struct sst39vf_dev_s *priv,
+ uintptr_t sectaddr)
+{
+ struct sst39vf_wrinfo_s wrinfo;
+#if 0
+ uint32_t start;
+ uint32_t elapsed;
+#endif
+
+ /* Set up the sector address */
+
+ wrinfo.address = sectaddr;
+ wrinfo.data = 0x0030;
+
+ /* Send the sequence to erase the chip */
+
+ sst39vf_writeseq(g_sectorerase, 5);
+ sst39vf_flashwrite(&wrinfo);
+
+ /* Use the data toggle delay method. The typical delay is 18 MSec. The
+ * maximum is 25 MSec. With a 10 MS system timer resolution, this is
+ * the difference of of waiting 20MS vs. 20MS. So using the data toggle
+ * delay method should give better write performance by about 10MS per
+ * block.
+ */
+
+#if 0
+ start = clock_systimer();
+ while (delay < SST39VF_TSE_MSEC * MSEC_PER_TICK)
+ {
+ /* Check if the erase is complete */
+
+ if (sst39vf_checktoggle(&wrinfo))
+ {
+ return OK;
+ }
+
+ /* No, check if the timeout has elapsed */
+
+ elapsed = clock_systimer() - start;
+ if (elapsed > SST39VF_TSE_MSEC * MSEC_PER_TICK)
+ {
+ return -ETIMEDOUT;
+ }
+
+ /* No, wait one system clock tick */
+
+ usleep(MSEC_PER_TICK * USEC_PER_MSEC);
+ }
+#else
+ /* Delay the maximum amount of time for the sector erase to complete. */
+
+ usleep(SST39VF_TSE_MSEC * USEC_PER_MSEC);
+#endif
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: sst39vf_writeword
+ *
+ * Description:
+ * Write one 16-bit word to FLASH
+ *
+ * "The SST39VF160x/320x are programmed on a word-by-word basis. Before
+ * programming, the sector where the word exists must be fully erased. The
+ * rogram operation is accomplished in three steps. The first step is the
+ * three-byte load sequence for Software Data Protection. The second step
+ * is to load word address and word data. During the Word-Program operation,
+ * the addresses are latched on the falling edge of either CE# or WE#,
+ * whichever occurs last. The data is latched on the rising edge of either
+ * CE# or WE#, whichever occurs first. The third step is the internal
+ * Program operation which is initiated after the rising edge of the
+ * fourth WE# or CE#, whichever occurs first. The Program operation, once
+ * initiated, will be completed within 10 µs. .... During the Program
+ * operation, the only valid reads are Data# Polling and Toggle Bit.
+ * During the internal Program operation, the host is free to perform
+ * additional tasks. Any commands issued during the internal Program
+ * operation are ignored. During the command sequence, WP# should be
+ * statically held high or low."
+ *
+ ****************************************************************************/
+
+static int sst39vf_writeword(FAR const struct sst39vf_wrinfo_s *wrinfo)
+{
+ /* Send the sequence to write the word to the chip */
+
+ sst39vf_writeseq(g_wordprogram, 3);
+ sst39vf_flashwrite(wrinfo);
+
+ /* Use the data toggle delay method. The typical delay is 7 usec; the
+ * maximum is 10 usec.
+ */
+
+ return sst39vf_waittoggle(wrinfo, WORDWRITE_TIMEOUT);
+}
+
+/****************************************************************************
+ * Name: sst39vf_erase
+ *
+ * Description:
+ * Erase several blocks, each of the size previously reported (i.e., one
+ * SST39VF sector).
+ *
+ ****************************************************************************/
+
+static int sst39vf_erase(FAR struct mtd_dev_s *dev, off_t startblock,
+ size_t nblocks)
+{
+ FAR struct sst39vf_dev_s *priv = (FAR struct sst39vf_dev_s *)dev;
+ uintptr_t address;
+ int ret;
+
+ DEBUGASSERT(priv && priv->chip && startblock < priv->chip->nsectors);
+
+ for (address = startblock * priv->chip->sectorsize;
+ nblocks > 0;
+ nblocks--, address += priv->chip->sectorsize)
+ {
+ /* Clear the sector */
+
+ ret = sst39vf_sectorerase(priv, address);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: sst39vf_bread
+ *
+ * Description:
+ * Read the specified number of blocks into the user provided buffer.
+ *
+ ****************************************************************************/
+
+static ssize_t sst39vf_bread(FAR struct mtd_dev_s *dev, off_t startblock,
+ size_t nblocks, FAR uint8_t *buf)
+{
+ FAR struct sst39vf_dev_s *priv = (FAR struct sst39vf_dev_s *)dev;
+ FAR const uint8_t *source;
+ size_t nbytes;
+
+ DEBUGASSERT(priv && priv->chip && startblock < priv->chip->nsectors);
+
+ /* Get the source address and the size of the transfer */
+
+ source = (FAR const uint8_t *)
+ SST39VF_ADDR(startblock * priv->chip->sectorsize);
+ nbytes = nblocks * priv->chip->sectorsize;
+
+ /* Copy the data to the user buffer */
+
+ memcpy(buf, source, nbytes);
+ return nblocks;
+}
+
+/****************************************************************************
+ * Name: sst39vf_bwrite
+ *
+ * Description:
+ * Write the specified number of blocks from the user provided buffer.
+ *
+ ****************************************************************************/
+
+static ssize_t sst39vf_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
+ size_t nblocks, FAR const uint8_t *buf)
+{
+ FAR struct sst39vf_dev_s *priv = (FAR struct sst39vf_dev_s *)dev;
+ struct sst39vf_wrinfo_s wrinfo;
+ FAR const uint16_t *source = (FAR const uint16_t *)buf;
+ size_t nwords;
+ int ret;
+
+ DEBUGASSERT(priv && priv->chip && ((uintptr_t)buf & 1) == 0 &&
+ startblock < priv->chip->nsectors);
+
+ /* Get the destination address and the size of the transfer */
+
+ wrinfo.address =
+ (uintptr_t)SST39VF_ADDR((startblock * priv->chip->sectorsize));
+ nwords = nblocks * (priv->chip->sectorsize >> 1);
+
+ /* Copy the data to the user buffer */
+
+ while (nwords-- > 0)
+ {
+ wrinfo.data = *source++;
+ ret = sst39vf_writeword(&wrinfo);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ wrinfo.address += sizeof(uint16_t);
+ }
+
+ return nblocks;
+}
+
+/****************************************************************************
+ * Name: sst39vf_read
+ *
+ * Description:
+ * Read the specified number of bytes to the user provided buffer.
+ *
+ ****************************************************************************/
+
+static ssize_t sst39vf_read(FAR struct mtd_dev_s *dev, off_t offset,
+ size_t nbytes, FAR uint8_t *buffer)
+{
+#ifdef CONFIG_DEBUG
+ FAR struct sst39vf_dev_s *priv = (FAR struct sst39vf_dev_s *)dev;
+#endif
+ FAR const uint8_t *source;
+
+ DEBUGASSERT(priv && priv->chip &&
+ offset < priv->chip->nsectors * priv->chip->sectorssize);
+
+ /* Get the source address and the size of the transfer */
+
+ source = (FAR const uint8_t *)SST39VF_ADDR(offset);
+
+ /* Copy the data to the user buffer */
+
+ memcpy(buffer, source, nbytes);
+ return nbytes;
+}
+
+/****************************************************************************
+ * Name: sst39vf_ioctl
+ ****************************************************************************/
+
+static int sst39vf_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg)
+{
+ FAR struct sst39vf_dev_s *priv = (FAR struct sst39vf_dev_s *)dev;
+ int ret = -ENOTTY;
+
+ DEBUGASSERT(priv && priv->chip);
+
+ switch (cmd)
+ {
+ case MTDIOC_GEOMETRY:
+ {
+ FAR struct mtd_geometry_s *geo = (FAR struct mtd_geometry_s *)arg;
+ if (geo)
+ {
+ /* Populate the geometry structure with information need to know
+ * the capacity and how to access the device.
+ */
+
+ geo->blocksize = priv->chip->sectorsize;
+ geo->erasesize = priv->chip->sectorsize;
+ geo->neraseblocks = priv->chip->nsectors;
+ ret = OK;
+ }
+ }
+ break;
+
+ case MTDIOC_XIPBASE:
+ {
+ FAR void **ppv = (FAR void **)arg;
+ if (ppv)
+ {
+ /* Return the base address of FLASH memory */
+
+ *ppv = (FAR void *)CONFIG_SST39VF_BASE_ADDRESS;
+ ret = OK;
+ }
+ }
+ break;
+
+ case MTDIOC_BULKERASE:
+ {
+ /* Erase the entire chip */
+
+ return sst39vf_chiperase(priv);
+ }
+ break;
+
+ default:
+ ret = -ENOTTY; /* Bad command */
+ break;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: sst39vf_initialize
+ *
+ * Description:
+ * Create and initialize an MTD device instance assuming an SST39VF NOR
+ * FLASH device at the configured address in memory. 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 *sst39vf_initialize(void)
+{
+ uint16_t manufacturer;
+ uint16_t chipid;
+
+ DEBUGASSERT(g_sst39vf.chip == NULL);
+
+ /* Issue the software entry command sequence */
+
+ sst39vf_writeseq(g_swid_entry, 3);
+ up_udelay(10);
+
+ /* Read the manufacturer and chip ID */
+
+ manufacturer = sst39vf_flashread(0x0000);
+ chipid = sst39vf_flashread(0x0001);
+
+ /* Issue the software exit sequence */
+
+ sst39vf_writeseq(g_swid_exit, 3);
+ up_udelay(10);
+
+ /* Now see if we can suport the part */
+
+ fvdbg("Manufacturer: %02x\n", manufacturer);
+ fvdbg("Chip ID: %04x\n", chipid);
+
+ if (manufacturer != SST_MANUFACTURER_ID)
+ {
+ fdbg("Unrecognized manufacturer: %02x\n", manufacturer);
+ return NULL;
+ }
+ else if (chipid == g_sst39vf1601.chipid)
+ {
+ g_sst39vf.chip = &g_sst39vf1601;
+ }
+ else if (chipid == g_sst39vf1602.chipid)
+ {
+ g_sst39vf.chip = &g_sst39vf1602;
+ }
+ else if (chipid == g_sst39vf3201.chipid)
+ {
+ g_sst39vf.chip = &g_sst39vf3201;
+ }
+ else if (chipid == g_sst39vf3202.chipid)
+ {
+ g_sst39vf.chip = &g_sst39vf3202;
+ }
+ else
+ {
+ fdbg("Unrecognized chip ID: %04x\n", chipid);
+ return NULL;
+ }
+
+ /* Return the state structure as the MTD device */
+
+ return (FAR struct mtd_dev_s *)&g_sst39vf;
+}