[PATCH 0/2] staging: mtd: Support for GigaDevice SPI NAND flash
bpqw
bpqw at micron.com
Mon Nov 24 23:02:53 PST 2014
>Yeah, I've been thinking about this for some time. Right now, I think that
>SPI NAND is just that: a NAND over SPI, so I'm doing some experiments
>around this design:
>
> Userspace
> ------------------
> MTD
> ------------------
> NAND core
> ------------------
> SPI NAND core
> ------------------
> SPI NAND device
> ------------------
> SPI core
> ------------------
> SPI master
> ------------------
> Hardware
I have finished a initial version of SPI-NAND framework based on drivers/staging/
mt29f_spinand/mt29f_spinand.c . The structure is the same as yours, just treate SPI
NAND as a NAND over SPI, put SPI NAND core under NAND core. Do you think it is a
good structure to start with? Because it is a quick product, I even do not finish
the unregisger function. So just care about the structure. Any suggestion about the
patch is welcomed.
---
drivers/mtd/Kconfig | 2 +
drivers/mtd/Makefile | 1 +
drivers/mtd/nand/nand_ids.c | 1 +
drivers/mtd/spi-nand/Kconfig | 16 +
drivers/mtd/spi-nand/Makefile | 1 +
drivers/mtd/spi-nand/spi-nand.c | 911 ++++++++++++++++++++++++++
drivers/staging/mt29f_spinand/Kconfig | 9 +-
drivers/staging/mt29f_spinand/mt29f_spinand.c | 850 +-----------------------
include/linux/mtd/spi-nand.h | 116 ++++
9 files changed, 1068 insertions(+), 839 deletions(-)
create mode 100644 drivers/mtd/spi-nand/Kconfig
create mode 100644 drivers/mtd/spi-nand/Makefile
create mode 100644 drivers/mtd/spi-nand/spi-nand.c
create mode 100644 include/linux/mtd/spi-nand.h
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index 94b8210..7bab890 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -323,6 +323,8 @@ source "drivers/mtd/lpddr/Kconfig"
source "drivers/mtd/spi-nor/Kconfig"
+source "drivers/mtd/spi-nand/Kconfig"
+
source "drivers/mtd/ubi/Kconfig"
endif # MTD
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
index 99bb9a1..581688f 100644
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -33,4 +33,5 @@ inftl-objs := inftlcore.o inftlmount.o
obj-y += chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/
+obj-$(CONFIG_MTD_SPI_NAND) += spi-nand/
obj-$(CONFIG_MTD_UBI) += ubi/
diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c
index fbde8910..743a688 100644
--- a/drivers/mtd/nand/nand_ids.c
+++ b/drivers/mtd/nand/nand_ids.c
@@ -109,6 +109,7 @@ struct nand_flash_dev nand_flash_ids[] = {
/* 2 Gigabit */
EXTENDED_ID_NAND("NAND 256MiB 1,8V 8-bit", 0xAA, 256, LP_OPTIONS),
EXTENDED_ID_NAND("NAND 256MiB 3,3V 8-bit", 0xDA, 256, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 256MiB 3,3V 8-bit", 0x22, 256, LP_OPTIONS),
EXTENDED_ID_NAND("NAND 256MiB 1,8V 16-bit", 0xBA, 256, LP_OPTIONS16),
EXTENDED_ID_NAND("NAND 256MiB 3,3V 16-bit", 0xCA, 256, LP_OPTIONS16),
diff --git a/drivers/mtd/spi-nand/Kconfig b/drivers/mtd/spi-nand/Kconfig
new file mode 100644
index 0000000..ef0927b
--- /dev/null
+++ b/drivers/mtd/spi-nand/Kconfig
@@ -0,0 +1,16 @@
+menuconfig MTD_SPI_NAND
+ tristate "SPI-NAND device Support"
+ depends on MTD_NAND && SPI
+ help
+ This is the framework for the SPI NAND which can be used by the SPI
+ device drivers and the SPI-NAND device drivers.
+
+if MTD_SPI_NAND
+
+config MTD_SPI_NAND_ONDIEECC
+ bool "Use SPINAND internal ECC"
+ help
+ Internal ECC.
+ Enables Hardware ECC support for SPI NAND.
+
+endif # MTD_SPI_NAND
diff --git a/drivers/mtd/spi-nand/Makefile b/drivers/mtd/spi-nand/Makefile
new file mode 100644
index 0000000..0c018fc
--- /dev/null
+++ b/drivers/mtd/spi-nand/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_MTD_SPI_NAND) += spi-nand.o
diff --git a/drivers/mtd/spi-nand/spi-nand.c b/drivers/mtd/spi-nand/spi-nand.c
new file mode 100644
index 0000000..fccaabb
--- /dev/null
+++ b/drivers/mtd/spi-nand/spi-nand.c
@@ -0,0 +1,910 @@
+/*
+ * Copyright (c) 2009-2014 Micron Technology, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/nand.h>
+#include <linux/spi/spi.h>
+#include <linux/mtd/spi-nand.h>
+
+static inline struct spi_nand_chip *mtd_to_spinand(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = (struct nand_chip *)mtd->priv;
+ struct spi_nand_chip *spi_chip =
+ container_of(chip, struct spi_nand_chip, chip);
+
+ return spi_chip;
+}
+
+
+/*
+ * spinand_cmd - to process a command to send to the SPI Nand
+ * Description:
+ * Set up the command buffer to send to the SPI controller.
+ * The command buffer has to initialized to 0.
+ */
+
+static int spinand_cmd(struct spi_device *spi, struct spinand_cmd *cmd)
+{
+ struct spi_message message;
+ struct spi_transfer x[4];
+ u8 dummy = 0xff;
+
+ spi_message_init(&message);
+ memset(x, 0, sizeof(x));
+
+ x[0].len = 1;
+ x[0].tx_buf = &cmd->cmd;
+ spi_message_add_tail(&x[0], &message);
+
+ if (cmd->n_addr) {
+ x[1].len = cmd->n_addr;
+ x[1].tx_buf = cmd->addr;
+ spi_message_add_tail(&x[1], &message);
+ }
+
+ if (cmd->n_dummy) {
+ x[2].len = cmd->n_dummy;
+ x[2].tx_buf = &dummy;
+ spi_message_add_tail(&x[2], &message);
+ }
+
+ if (cmd->n_tx) {
+ x[3].len = cmd->n_tx;
+ x[3].tx_buf = cmd->tx_buf;
+ spi_message_add_tail(&x[3], &message);
+ }
+
+ if (cmd->n_rx) {
+ x[3].len = cmd->n_rx;
+ x[3].rx_buf = cmd->rx_buf;
+ spi_message_add_tail(&x[3], &message);
+ }
+
+ return spi_sync(spi, &message);
+}
+
+/*
+ * spinand_read_id- Read SPI Nand ID
+ * Description:
+ * Read ID: read two ID bytes from the SPI Nand device
+ */
+static int spinand_read_id(struct spi_device *spi, u8 *id)
+{
+ int retval;
+ u8 nand_id[3];
+ struct spinand_cmd cmd = {0};
+
+ cmd.cmd = SPINAND_CMD_READ_ID;
+ cmd.n_rx = 3;
+ cmd.rx_buf = &nand_id[0];
+
+ retval = spinand_cmd(spi, &cmd);
+ if (retval < 0) {
+ dev_err(&spi->dev, "error %d reading id\n", retval);
+ return retval;
+ }
+
+ /*Micron SPI NAND device*/
+ if (nand_id[1] == 0x2C) {
+ id[0] = nand_id[1];
+ id[1] = nand_id[2];
+ }
+ /*Giga device SPI NAND device*/
+ else if (nand_id[0] == 0xC8) {
+ id[0] = nand_id[0];
+ id[1] = nand_id[1];
+ id[2] = nand_id[2];
+ }
+
+ return retval;
+}
+
+/*
+ * spinand_read_status- send command 0xf to the SPI Nand status register
+ * Description:
+ * After read, write, or erase, the Nand device is expected to set the
+ * busy status.
+ * This function is to allow reading the status of the command: read,
+ * write, and erase.
+ * Once the status turns to be ready, the other status bits also are
+ * valid status bits.
+ */
+static int spinand_read_status(struct spi_device *spi, uint8_t *status)
+{
+ struct spinand_cmd cmd = {0};
+ int ret;
+
+ cmd.cmd = SPINAND_CMD_READ_REG;
+ cmd.n_addr = 1;
+ cmd.addr[0] = REG_STATUS;
+ cmd.n_rx = 1;
+ cmd.rx_buf = status;
+
+ ret = spinand_cmd(spi, &cmd);
+ if (ret < 0)
+ dev_err(&spi->dev, "err: %d read status register\n", ret);
+
+ return ret;
+}
+
+#define MAX_WAIT_JIFFIES (40 * HZ)
+static int wait_till_ready(struct spi_device *spi)
+{
+ unsigned long deadline;
+ int retval;
+ u8 stat = 0;
+
+ deadline = jiffies + MAX_WAIT_JIFFIES;
+ do {
+ retval = spinand_read_status(spi, &stat);
+ if (retval < 0)
+ return -1;
+ else if (!(stat & 0x1))
+ break;
+
+ cond_resched();
+ } while (!time_after_eq(jiffies, deadline));
+
+ if ((stat & 0x1) == 0)
+ return 0;
+
+ return -1;
+}
+/**
+ * spinand_get_otp- send command 0xf to read the SPI Nand OTP register
+ * Description:
+ * There is one bit( bit 0x10 ) to set or to clear the internal ECC.
+ * Enable chip internal ECC, set the bit to 1
+ * Disable chip internal ECC, clear the bit to 0
+ */
+static int spinand_get_otp(struct spi_device *spi, u8 *otp)
+{
+ struct spinand_cmd cmd = {0};
+ int retval;
+
+ cmd.cmd = SPINAND_CMD_READ_REG;
+ cmd.n_addr = 1;
+ cmd.addr[0] = REG_OTP;
+ cmd.n_rx = 1;
+ cmd.rx_buf = otp;
+
+ retval = spinand_cmd(spi, &cmd);
+ if (retval < 0)
+ dev_err(&spi->dev, "error %d get otp\n", retval);
+ return retval;
+}
+
+/**
+ * spinand_set_otp- send command 0x1f to write the SPI Nand OTP register
+ * Description:
+ * There is one bit( bit 0x10 ) to set or to clear the internal ECC.
+ * Enable chip internal ECC, set the bit to 1
+ * Disable chip internal ECC, clear the bit to 0
+ */
+static int spinand_set_otp(struct spi_device *spi, u8 *otp)
+{
+ int retval;
+ struct spinand_cmd cmd = {0};
+
+ cmd.cmd = SPINAND_CMD_WRITE_REG,
+ cmd.n_addr = 1,
+ cmd.addr[0] = REG_OTP,
+ cmd.n_tx = 1,
+ cmd.tx_buf = otp,
+
+ retval = spinand_cmd(spi, &cmd);
+ if (retval < 0)
+ dev_err(&spi->dev, "error %d set otp\n", retval);
+
+ return retval;
+}
+
+#ifdef CONFIG_MTD_SPINAND_ONDIEECC
+/**
+ * spinand_enable_ecc- send command 0x1f to write the SPI Nand OTP register
+ * Description:
+ * There is one bit( bit 0x10 ) to set or to clear the internal ECC.
+ * Enable chip internal ECC, set the bit to 1
+ * Disable chip internal ECC, clear the bit to 0
+ */
+static int spinand_enable_ecc(struct spi_device *spi)
+{
+ int retval;
+ u8 otp = 0;
+
+ retval = spinand_get_otp(spi, &otp);
+ if (retval < 0)
+ return retval;
+
+ if ((otp & OTP_ECC_MASK) == OTP_ECC_MASK)
+ return 0;
+
+ otp |= OTP_ECC_MASK;
+ retval = spinand_set_otp(spi, &otp);
+ if (retval < 0)
+ return retval;
+ return spinand_get_otp(spi, &otp);
+}
+#endif
+
+static int spinand_disable_ecc(struct spi_device *spi)
+{
+ int retval;
+ u8 otp = 0;
+
+ retval = spinand_get_otp(spi, &otp);
+ if (retval < 0)
+ return retval;
+
+ if ((otp & OTP_ECC_MASK) == OTP_ECC_MASK) {
+ otp &= ~OTP_ECC_MASK;
+ retval = spinand_set_otp(spi, &otp);
+ if (retval < 0)
+ return retval;
+ return spinand_get_otp(spi, &otp);
+ } else
+ return 0;
+}
+
+/**
+ * spinand_write_enable- send command 0x06 to enable write or erase the
+ * Nand cells
+ * Description:
+ * Before write and erase the Nand cells, the write enable has to be set.
+ * After the write or erase, the write enable bit is automatically
+ * cleared (status register bit 2)
+ * Set the bit 2 of the status register has the same effect
+ */
+static int spinand_write_enable(struct spi_device *spi)
+{
+ struct spinand_cmd cmd = {0};
+
+ cmd.cmd = SPINAND_CMD_WR_ENABLE;
+ return spinand_cmd(spi, &cmd);
+}
+
+static int spinand_read_page_to_cache(struct spi_device *spi, u32 page_id)
+{
+ struct spinand_cmd cmd = {0};
+
+ cmd.cmd = SPINAND_CMD_READ;
+ cmd.n_addr = 3;
+ cmd.addr[0] = (u8)((page_id & 0x00FF0000) >> 16);
+ cmd.addr[1] = (u8)((page_id & 0x0000FF00) >> 8);
+ cmd.addr[2] = (u8)(page_id & 0x000000FF);
+
+ return spinand_cmd(spi, &cmd);
+}
+
+/*
+ * spinand_read_from_cache- send command 0x03 to read out the data from the
+ * cache register(2112 bytes max)
+ * Description:
+ * The read can specify 1 to 2112 bytes of data read at the corresponding
+ * locations.
+ * No tRd delay.
+ */
+static int spinand_read_from_cache(struct spi_nand_chip *spi_chip, u32 page_id,
+ u16 byte_id, u16 len, u8 *rbuf)
+{
+ struct spinand_cmd cmd = {0};
+ u16 column;
+ struct spi_device *spi = spi_chip->spi;
+
+ column = byte_id;
+ cmd.cmd = SPINAND_CMD_READ_RDM;
+ cmd.n_addr = 3;
+ cmd.addr[0] = (u8)((column & 0xff00) >> 8);
+ if (spi_chip->plane_sel)
+ cmd.addr[0] |= (u8)(((page_id >> spi_chip->plane_bit) & 0x1)
+ << 4);
+ cmd.addr[1] = (u8)(column & 0x00ff);
+ cmd.addr[2] = (u8)(0xff);
+ cmd.n_dummy = 0;
+ cmd.n_rx = len;
+ cmd.rx_buf = rbuf;
+
+ return spinand_cmd(spi, &cmd);
+}
+
+/*
+ * spinand_read_page-to read a page with:
+ * @page_id: the physical page number
+ * @offset: the location from 0 to 2111
+ * @len: number of bytes to read
+ * @rbuf: read buffer to hold @len bytes
+ *
+ * Description:
+ * The read includes two commands to the Nand: 0x13 and 0x03 commands
+ * Poll to read status to wait for tRD time.
+ */
+static int spinand_read_page(struct spi_nand_chip *spi_chip, u32 page_id,
+ u16 offset, u16 len, u8 *rbuf)
+{
+ int ret;
+ u8 status = 0;
+ struct spi_device *spi = spi_chip->spi;
+
+#ifdef CONFIG_MTD_SPINAND_ONDIEECC
+ if (spi_chip->enable_read_hw_ecc) {
+ if (spinand_enable_ecc(spi) < 0)
+ dev_err(&spi->dev, "enable HW ECC failed!");
+ }
+#endif
+ ret = spinand_read_page_to_cache(spi, page_id);
+ if (ret < 0)
+ return ret;
+
+ if (wait_till_ready(spi))
+ dev_err(&spi->dev, "WAIT timedout!!!\n");
+
+ while (1) {
+ ret = spinand_read_status(spi, &status);
+ if (ret < 0) {
+ dev_err(&spi->dev,
+ "err %d read status register\n", ret);
+ return ret;
+ }
+
+ if ((status & STATUS_OIP_MASK) == STATUS_READY) {
+ if ((status & STATUS_ECC_MASK) == STATUS_ECC_ERROR) {
+ dev_err(&spi->dev, "ecc error, page=%d\n",
+ page_id);
+ return 0;
+ }
+ break;
+ }
+ }
+
+ ret = spinand_read_from_cache(spi_chip, page_id, offset, len, rbuf);
+ if (ret < 0) {
+ dev_err(&spi->dev, "read from cache failed!!\n");
+ return ret;
+ }
+
+#ifdef CONFIG_MTD_SPINAND_ONDIEECC
+ if (spi_chip->enable_read_hw_ecc) {
+ ret = spinand_disable_ecc(spi);
+ if (ret < 0) {
+ dev_err(&spi->dev, "disable ecc failed!!\n");
+ return ret;
+ }
+ spi_chip->enable_read_hw_ecc = 0;
+ }
+#endif
+ return ret;
+}
+
+/*
+ * spinand_program_data_to_cache--to write a page to cache with:
+ * @byte_id: the location to write to the cache
+ * @len: number of bytes to write
+ * @rbuf: read buffer to hold @len bytes
+ *
+ * Description:
+ * The write command used here is 0x84--indicating that the cache is
+ * not cleared first.
+ * Since it is writing the data to cache, there is no tPROG time.
+ */
+static int spinand_program_data_to_cache(struct spi_device *spi,
+ u32 page_id, u16 byte_id, u16 len, u8 *wbuf)
+{
+ struct spinand_cmd cmd = {0};
+ u16 column;
+
+ column = byte_id;
+ cmd.cmd = SPINAND_CMD_PROG_PAGE_CLRCACHE;
+ cmd.n_addr = 2;
+ cmd.addr[0] = (u8)((column & 0xff00) >> 8);
+ cmd.addr[0] |= (u8)(((page_id >> 6) & 0x1) << 4);
+ cmd.addr[1] = (u8)(column & 0x00ff);
+ cmd.n_tx = len;
+ cmd.tx_buf = wbuf;
+
+ return spinand_cmd(spi, &cmd);
+}
+
+/**
+ * spinand_program_execute--to write a page from cache to the Nand array with
+ * @page_id: the physical page location to write the page.
+ *
+ * Description:
+ * The write command used here is 0x10--indicating the cache is writing to
+ * the Nand array.
+ * Need to wait for tPROG time to finish the transaction.
+ */
+static int spinand_program_execute(struct spi_device *spi, u32 page_id)
+{
+ struct spinand_cmd cmd = {0};
+
+ cmd.cmd = SPINAND_CMD_PROG_PAGE_EXC;
+ cmd.n_addr = 3;
+ cmd.addr[0] = (u8)((page_id & 0x00FF0000) >> 16);
+ cmd.addr[1] = (u8)((page_id & 0x0000FF00) >> 8);
+ cmd.addr[2] = (u8)(page_id & 0x000000FF);
+
+
+ return spinand_cmd(spi, &cmd);
+}
+
+/**
+ * spinand_program_page--to write a page with:
+ * @page_id: the physical page location to write the page.
+ * @offset: the location from the cache starting from 0 to 2111
+ * @len: the number of bytes to write
+ * @wbuf: the buffer to hold the number of bytes
+ *
+ * Description:
+ * The commands used here are 0x06, 0x84, and 0x10--indicating that
+ * the write enable is first sent, the write cache command, and the
+ * write execute command.
+ * Poll to wait for the tPROG time to finish the transaction.
+ */
+static int spinand_program_page(struct spi_nand_chip *spi_chip,
+ u32 page_id, u16 offset, u16 len, u8 *buf)
+{
+ struct spi_device *spi = spi_chip->spi;
+ int retval;
+ u8 status = 0;
+ uint8_t *wbuf;
+#ifdef CONFIG_MTD_SPINAND_ONDIEECC
+ unsigned int i, j;
+
+ spi_chip->enable_read_hw_ecc = 0;
+ wbuf = spi_chip->ecc_buf;
+ spinand_read_page(spi_chip, page_id, 0, spi_chip->cache_size, wbuf);
+
+ for (i = offset, j = 0; i < len; i++, j++)
+ wbuf[i] &= buf[j];
+
+ if (spi_chip->enable_hw_ecc) {
+ retval = spinand_enable_ecc(spi);
+ if (retval < 0) {
+ dev_err(&spi->dev, "enable ecc failed!!\n");
+ return retval;
+ }
+ }
+#else
+ wbuf = buf;
+#endif
+ retval = spinand_write_enable(spi);
+ if (retval < 0) {
+ dev_err(&spi->dev, "write enable failed!!\n");
+ return retval;
+ }
+ if (wait_till_ready(spi))
+ dev_err(&spi->dev, "wait timedout!!!\n");
+
+ retval = spinand_program_data_to_cache(spi, page_id,
+ offset, len, wbuf);
+ if (retval < 0)
+ return retval;
+ retval = spinand_program_execute(spi, page_id);
+ if (retval < 0)
+ return retval;
+ while (1) {
+ retval = spinand_read_status(spi, &status);
+ if (retval < 0) {
+ dev_err(&spi->dev,
+ "error %d reading status register\n",
+ retval);
+ return retval;
+ }
+
+ if ((status & STATUS_OIP_MASK) == STATUS_READY) {
+ if ((status & STATUS_P_FAIL_MASK) == STATUS_P_FAIL)
+ dev_err(&spi->dev,
+ "program error, page %d\n", page_id);
+ return -1;
+ break;
+ }
+ }
+#ifdef CONFIG_MTD_SPINAND_ONDIEECC
+ if (spi_chip->enable_hw_ecc) {
+ retval = spinand_disable_ecc(spi);
+ if (retval < 0) {
+ dev_err(&spi->dev, "disable ecc failed!!\n");
+ return retval;
+ }
+ spi_chip->enable_hw_ecc = 0;
+ }
+#endif
+
+ return 0;
+}
+
+/**
+ * spinand_erase_block_erase--to erase a page with:
+ * @block_id: the physical block location to erase.
+ *
+ * Description:
+ * The command used here is 0xd8--indicating an erase command to erase
+ * one block--64 pages
+ * Need to wait for tERS.
+ */
+static int spinand_erase_block_erase(struct spi_device *spi, u16 block_id)
+{
+ struct spinand_cmd cmd = {0};
+ u16 row;
+
+ row = block_id;
+ cmd.cmd = SPINAND_CMD_ERASE_BLK;
+ cmd.n_addr = 3;
+ cmd.addr[1] = (u8)((row & 0xff00) >> 8);
+ cmd.addr[2] = (u8)(row & 0x00ff);
+
+ return spinand_cmd(spi, &cmd);
+}
+
+/**
+ * spinand_erase_block--to erase a page with:
+ * @block_id: the physical block location to erase.
+ *
+ * Description:
+ * The commands used here are 0x06 and 0xd8--indicating an erase
+ * command to erase one block--64 pages
+ * It will first to enable the write enable bit (0x06 command),
+ * and then send the 0xd8 erase command
+ * Poll to wait for the tERS time to complete the tranaction.
+ */
+static int spinand_erase_block(struct spi_device *spi, u16 block_id)
+{
+ int retval;
+ u8 status = 0;
+
+ retval = spinand_write_enable(spi);
+ if (wait_till_ready(spi))
+ dev_err(&spi->dev, "wait timedout!!!\n");
+
+ retval = spinand_erase_block_erase(spi, block_id);
+ while (1) {
+ retval = spinand_read_status(spi, &status);
+ if (retval < 0) {
+ dev_err(&spi->dev,
+ "error %d reading status register\n",
+ (int) retval);
+ return retval;
+ }
+
+ if ((status & STATUS_OIP_MASK) == STATUS_READY) {
+ if ((status & STATUS_E_FAIL_MASK) == STATUS_E_FAIL)
+ dev_err(&spi->dev,
+ "erase error, block %d\n", block_id);
+ return -1;
+ break;
+ }
+ }
+ return 0;
+}
+
+#ifdef CONFIG_MTD_SPINAND_ONDIEECC
+static int spinand_write_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf, int oob_required)
+{
+ const uint8_t *p = buf;
+ int eccsize = chip->ecc.size;
+ int eccsteps = chip->ecc.steps;
+ struct spi_nand_chip *spi_chip = mtd_to_spinand(mtd);
+
+ spi_chip->enable_hw_ecc = 1;
+ chip->write_buf(mtd, p, eccsize * eccsteps);
+ return 0;
+}
+
+static int spinand_read_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
+{
+ u8 retval, status;
+ uint8_t *p = buf;
+ int eccsize = chip->ecc.size;
+ int eccsteps = chip->ecc.steps;
+ struct spi_nand_chip *spi_chip = mtd_to_spinand(mtd);
+
+ spi_chip->enable_read_hw_ecc = 1;
+ chip->read_buf(mtd, p, eccsize * eccsteps);
+ if (oob_required)
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ while (1) {
+ retval = spinand_read_status(spi_chip->spi, &status);
+ if ((status & STATUS_OIP_MASK) == STATUS_READY) {
+ if ((status & STATUS_ECC_MASK) == STATUS_ECC_ERROR) {
+ pr_info("spinand: ECC error\n");
+ mtd->ecc_stats.failed++;
+ } else if ((status & STATUS_ECC_MASK) ==
+ STATUS_ECC_1BIT_CORRECTED)
+ mtd->ecc_stats.corrected++;
+ break;
+ }
+ }
+ return 0;
+
+}
+#endif
+
+static void spinand_select_chip(struct mtd_info *mtd, int dev)
+{
+}
+
+static uint8_t spinand_read_byte(struct mtd_info *mtd)
+{
+ struct spi_nand_chip *spi_chip = mtd_to_spinand(mtd);
+ u8 data;
+
+ data = spi_chip->buf[spi_chip->buf_ptr];
+ spi_chip->buf_ptr++;
+ return data;
+}
+
+
+static int spinand_wait(struct mtd_info *mtd, struct nand_chip *chip)
+{
+ struct spi_nand_chip *spi_chip = mtd_to_spinand(mtd);
+
+ unsigned long timeo = jiffies;
+ int retval, state = chip->state;
+ u8 status;
+
+ if (state == FL_ERASING)
+ timeo += (HZ * 400) / 1000;
+ else
+ timeo += (HZ * 20) / 1000;
+
+ while (time_before(jiffies, timeo)) {
+ retval = spinand_read_status(spi_chip->spi, &status);
+ if ((status & STATUS_OIP_MASK) == STATUS_READY)
+ return 0;
+
+ cond_resched();
+ }
+ return 0;
+}
+
+static void spinand_write_buf(struct mtd_info *mtd, const uint8_t *buf,
+ int len)
+{
+
+ struct spi_nand_chip *spi_chip = mtd_to_spinand(mtd);
+
+ memcpy(spi_chip->buf + spi_chip->buf_ptr, buf, len);
+ spi_chip->buf_ptr += len;
+}
+
+static void spinand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct spi_nand_chip *spi_chip = mtd_to_spinand(mtd);
+
+ memcpy(buf, spi_chip->buf + spi_chip->buf_ptr, len);
+ spi_chip->buf_ptr += len;
+}
+
+/*
+ * spinand_reset- send RESET command "0xff" to the Nand device.
+ */
+static void spinand_reset(struct spi_device *spi)
+{
+ struct spinand_cmd cmd = {0};
+
+ cmd.cmd = SPINAND_CMD_RESET;
+
+ if (spinand_cmd(spi, &cmd) < 0)
+ pr_info("spinand reset failed!\n");
+
+ /* elapse 1ms before issuing any other command */
+ udelay(1000);
+
+ if (wait_till_ready(spi))
+ dev_err(&spi->dev, "wait timedout!\n");
+}
+
+static void spinand_cmdfunc(struct mtd_info *mtd, unsigned int command,
+ int column, int page)
+{
+ struct spi_nand_chip *spi_chip = mtd_to_spinand(mtd);
+ struct spi_device *spi = spi_chip->spi;
+
+ switch (command) {
+ /*
+ * READ0 - read in first 0x800 bytes
+ */
+ case NAND_CMD_READ1:
+ case NAND_CMD_READ0:
+ spi_chip->buf_ptr = 0;
+ spinand_read_page(spi_chip, page, 0,
+ mtd->writesize + mtd->oobsize, spi_chip->buf);
+ break;
+ /* READOOB reads only the OOB because no ECC is performed. */
+ case NAND_CMD_READOOB:
+ spi_chip->buf_ptr = 0;
+ spinand_read_page(spi_chip, page, mtd->writesize,
+ mtd->oobsize, spi_chip->buf);
+ break;
+ case NAND_CMD_RNDOUT:
+ spi_chip->buf_ptr = column;
+ break;
+ case NAND_CMD_READID:
+ spi_chip->buf_ptr = 0;
+ spinand_read_id(spi, (u8 *)spi_chip->buf);
+ break;
+ case NAND_CMD_PARAM:
+ spi_chip->buf_ptr = 0;
+ break;
+ /* ERASE1 stores the block and page address */
+ case NAND_CMD_ERASE1:
+ spinand_erase_block(spi, page);
+ break;
+ /* ERASE2 uses the block and page address from ERASE1 */
+ case NAND_CMD_ERASE2:
+ break;
+ /* SEQIN sets up the addr buffer and all registers except the length */
+ case NAND_CMD_SEQIN:
+ spi_chip->col = column;
+ spi_chip->row = page;
+ spi_chip->buf_ptr = 0;
+ break;
+ /* PAGEPROG reuses all of the setup from SEQIN and adds the length */
+ case NAND_CMD_PAGEPROG:
+ spinand_program_page(spi_chip, spi_chip->row, spi_chip->col,
+ spi_chip->buf_ptr, spi_chip->buf);
+ break;
+ case NAND_CMD_STATUS:
+ spinand_get_otp(spi, spi_chip->buf);
+ if (!(spi_chip->buf[0] & 0x80))
+ spi_chip->buf[0] = 0x80;
+ spi_chip->buf_ptr = 0;
+ break;
+ /* RESET command */
+ case NAND_CMD_RESET:
+ if (wait_till_ready(spi))
+ dev_err(&spi->dev, "WAIT timedout!!!\n");
+ /* a minimum of 250us must elapse before issuing RESET cmd*/
+ udelay(250);
+ spinand_reset(spi);
+ break;
+ default:
+ dev_err(&mtd->dev, "Unknown CMD: 0x%x\n", command);
+ }
+}
+
+/**
+ * spinand_lock_block- send write register 0x1f command to the Nand device
+ *
+ * Description:
+ * After power up, all the Nand blocks are locked. This function allows
+ * one to unlock the blocks, and so it can be written or erased.
+ */
+static int spinand_lock_block(struct spi_device *spi, u8 lock)
+{
+ struct spinand_cmd cmd = {0};
+ int ret;
+ u8 otp = 0;
+
+ ret = spinand_get_otp(spi, &otp);
+
+ cmd.cmd = SPINAND_CMD_WRITE_REG;
+ cmd.n_addr = 1;
+ cmd.addr[0] = REG_BLOCK_LOCK;
+ cmd.n_tx = 1;
+ cmd.tx_buf = &lock;
+
+ ret = spinand_cmd(spi, &cmd);
+ if (ret < 0)
+ dev_err(&spi->dev, "error %d lock block\n", ret);
+
+ return ret;
+}
+
+int spinand_set_plane_info(struct spi_nand_chip *spi_chip, u16 plane_sel,
+ u16 plane_bit)
+{
+ spi_chip->plane_sel = plane_sel;
+ spi_chip->plane_bit = plane_bit;
+
+ return 0;
+}
+int spinand_set_buffer_size(struct spi_nand_chip *spi_chip, u32 buf_size,
+ u32 cache_size)
+{
+ spi_chip->buf_size = buf_size;
+ spi_chip->cache_size = cache_size;
+
+ return 0;
+}
+
+static int spinand_alloc_buffer(struct spi_nand_chip *spi_chip)
+{
+ struct spi_device *spi = spi_chip->spi;
+
+ spi_chip->buf_ptr = 0;
+ spi_chip->buf = devm_kzalloc(&spi->dev, spi_chip->buf_size,
+ GFP_KERNEL);
+ if (!spi_chip->buf)
+ return -ENOMEM;
+#ifdef CONFIG_MTD_SPINAND_ONDIEECC
+ spi_chip->ecc_buf = devm_kzalloc(&spi->dev, spi_chip->cache_size,
+ GFP_KERNEL);
+ if (!spi_chip->ecc_buf)
+ return -ENOMEM;
+#endif
+
+ return 0;
+}
+
+struct spi_nand_chip *spinand_alloc_chip(struct spi_device *spi)
+{
+ struct spi_nand_chip *spi_chip;
+ struct nand_chip *chip;
+
+ spi_chip = devm_kzalloc(&spi->dev, sizeof(struct spi_nand_chip),
+ GFP_KERNEL);
+ if (!spi_chip)
+ return ERR_PTR(-ENOMEM);
+
+ spi_chip->spi = spi;
+ chip = &spi_chip->chip;
+#ifdef CONFIG_MTD_SPINAND_ONDIEECC
+ chip->ecc.mode = NAND_ECC_HW;
+ chip->ecc.read_page = spinand_read_page_hwecc;
+ chip->ecc.write_page = spinand_write_page_hwecc;
+#else
+ chip->ecc.mode = NAND_ECC_SOFT;
+ if (spinand_disable_ecc(spi) < 0)
+ pr_info("%s: disable ecc failed!\n", __func__);
+#endif
+
+ chip->read_buf = spinand_read_buf;
+ chip->write_buf = spinand_write_buf;
+ chip->read_byte = spinand_read_byte;
+ chip->cmdfunc = spinand_cmdfunc;
+ chip->waitfunc = spinand_wait;
+ chip->options |= NAND_CACHEPRG;
+ chip->select_chip = spinand_select_chip;
+
+ return spi_chip;
+}
+EXPORT_SYMBOL(spinand_alloc_chip);
+
+
+int spinand_register_chip(struct spi_nand_chip *spi_chip)
+{
+ struct mtd_info *mtd;
+ struct mtd_part_parser_data ppdata;
+ struct spi_device *spi = spi_chip->spi;
+
+ mtd = devm_kzalloc(&spi->dev, sizeof(struct mtd_info), GFP_KERNEL);
+ if (!mtd)
+ return -ENOMEM;
+
+ dev_set_drvdata(&spi->dev, mtd);
+
+ mtd->priv = &spi_chip->chip;
+ mtd->name = dev_name(&spi->dev);
+ mtd->owner = THIS_MODULE;
+ spinand_alloc_buffer(spi_chip);
+
+ spinand_lock_block(spi, BL_ALL_UNLOCKED);
+ if (nand_scan(mtd, 1))
+ return -ENXIO;
+
+ ppdata.of_node = spi->dev.of_node;
+ return mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
+}
+EXPORT_SYMBOL(spinand_register_chip);
+
+
+MODULE_DESCRIPTION("SPI NAND framewowk");
+MODULE_AUTHOR("Peter Pan<bpqw at micron.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/mt29f_spinand/Kconfig b/drivers/staging/mt29f_spinand/Kconfig
index 4031748..7219c5a 100644
--- a/drivers/staging/mt29f_spinand/Kconfig
+++ b/drivers/staging/mt29f_spinand/Kconfig
@@ -1,16 +1,9 @@
config MTD_SPINAND_MT29F
tristate "SPINAND Device Support for Micron"
- depends on MTD_NAND && SPI
+ depends on MTD_SPI_NAND
help
This enables support for accessing Micron SPI NAND flash
devices.
If you have Micron SPI NAND chip say yes.
If unsure, say no here.
-
-config MTD_SPINAND_ONDIEECC
- bool "Use SPINAND internal ECC"
- depends on MTD_SPINAND_MT29F
- help
- Internel ECC.
- Enables Hardware ECC support for Micron SPI NAND.
diff --git a/drivers/staging/mt29f_spinand/mt29f_spinand.c b/drivers/staging/mt29f_spinand/mt29f_spinand.c
index 3628bcb..97cee77 100644
--- a/drivers/staging/mt29f_spinand/mt29f_spinand.c
+++ b/drivers/staging/mt29f_spinand/mt29f_spinand.c
@@ -20,23 +20,8 @@
#include <linux/mtd/partitions.h>
#include <linux/mtd/nand.h>
#include <linux/spi/spi.h>
+#include <linux/mtd/spi-nand.h>
-#include "mt29f_spinand.h"
-
-#define BUFSIZE (10 * 64 * 2048)
-#define CACHE_BUF 2112
-/*
- * OOB area specification layout: Total 32 available free bytes.
- */
-
-static inline struct spinand_state *mtd_to_state(struct mtd_info *mtd)
-{
- struct nand_chip *chip = (struct nand_chip *)mtd->priv;
- struct spinand_info *info = (struct spinand_info *)chip->priv;
- struct spinand_state *state = (struct spinand_state *)info->priv;
-
- return state;
-}
#ifdef CONFIG_MTD_SPINAND_ONDIEECC
static int enable_hw_ecc;
@@ -63,809 +48,39 @@ static struct nand_ecclayout spinand_oob_64 = {
};
#endif
-/*
- * spinand_cmd - to process a command to send to the SPI Nand
- * Description:
- * Set up the command buffer to send to the SPI controller.
- * The command buffer has to initialized to 0.
- */
-
-static int spinand_cmd(struct spi_device *spi, struct spinand_cmd *cmd)
-{
- struct spi_message message;
- struct spi_transfer x[4];
- u8 dummy = 0xff;
-
- spi_message_init(&message);
- memset(x, 0, sizeof(x));
-
- x[0].len = 1;
- x[0].tx_buf = &cmd->cmd;
- spi_message_add_tail(&x[0], &message);
-
- if (cmd->n_addr) {
- x[1].len = cmd->n_addr;
- x[1].tx_buf = cmd->addr;
- spi_message_add_tail(&x[1], &message);
- }
-
- if (cmd->n_dummy) {
- x[2].len = cmd->n_dummy;
- x[2].tx_buf = &dummy;
- spi_message_add_tail(&x[2], &message);
- }
-
- if (cmd->n_tx) {
- x[3].len = cmd->n_tx;
- x[3].tx_buf = cmd->tx_buf;
- spi_message_add_tail(&x[3], &message);
- }
-
- if (cmd->n_rx) {
- x[3].len = cmd->n_rx;
- x[3].rx_buf = cmd->rx_buf;
- spi_message_add_tail(&x[3], &message);
- }
-
- return spi_sync(spi, &message);
-}
-
-/*
- * spinand_read_id- Read SPI Nand ID
- * Description:
- * Read ID: read two ID bytes from the SPI Nand device
- */
-static int spinand_read_id(struct spi_device *spi_nand, u8 *id)
-{
- int retval;
- u8 nand_id[3];
- struct spinand_cmd cmd = {0};
-
- cmd.cmd = CMD_READ_ID;
- cmd.n_rx = 3;
- cmd.rx_buf = &nand_id[0];
-
- retval = spinand_cmd(spi_nand, &cmd);
- if (retval < 0) {
- dev_err(&spi_nand->dev, "error %d reading id\n", retval);
- return retval;
- }
- id[0] = nand_id[1];
- id[1] = nand_id[2];
- return retval;
-}
-
-/*
- * spinand_read_status- send command 0xf to the SPI Nand status register
- * Description:
- * After read, write, or erase, the Nand device is expected to set the
- * busy status.
- * This function is to allow reading the status of the command: read,
- * write, and erase.
- * Once the status turns to be ready, the other status bits also are
- * valid status bits.
- */
-static int spinand_read_status(struct spi_device *spi_nand, uint8_t *status)
-{
- struct spinand_cmd cmd = {0};
- int ret;
-
- cmd.cmd = CMD_READ_REG;
- cmd.n_addr = 1;
- cmd.addr[0] = REG_STATUS;
- cmd.n_rx = 1;
- cmd.rx_buf = status;
-
- ret = spinand_cmd(spi_nand, &cmd);
- if (ret < 0)
- dev_err(&spi_nand->dev, "err: %d read status register\n", ret);
-
- return ret;
-}
-
-#define MAX_WAIT_JIFFIES (40 * HZ)
-static int wait_till_ready(struct spi_device *spi_nand)
-{
- unsigned long deadline;
- int retval;
- u8 stat = 0;
-
- deadline = jiffies + MAX_WAIT_JIFFIES;
- do {
- retval = spinand_read_status(spi_nand, &stat);
- if (retval < 0)
- return -1;
- else if (!(stat & 0x1))
- break;
-
- cond_resched();
- } while (!time_after_eq(jiffies, deadline));
-
- if ((stat & 0x1) == 0)
- return 0;
-
- return -1;
-}
-/**
- * spinand_get_otp- send command 0xf to read the SPI Nand OTP register
- * Description:
- * There is one bit( bit 0x10 ) to set or to clear the internal ECC.
- * Enable chip internal ECC, set the bit to 1
- * Disable chip internal ECC, clear the bit to 0
- */
-static int spinand_get_otp(struct spi_device *spi_nand, u8 *otp)
-{
- struct spinand_cmd cmd = {0};
- int retval;
-
- cmd.cmd = CMD_READ_REG;
- cmd.n_addr = 1;
- cmd.addr[0] = REG_OTP;
- cmd.n_rx = 1;
- cmd.rx_buf = otp;
-
- retval = spinand_cmd(spi_nand, &cmd);
- if (retval < 0)
- dev_err(&spi_nand->dev, "error %d get otp\n", retval);
- return retval;
-}
-
-/**
- * spinand_set_otp- send command 0x1f to write the SPI Nand OTP register
- * Description:
- * There is one bit( bit 0x10 ) to set or to clear the internal ECC.
- * Enable chip internal ECC, set the bit to 1
- * Disable chip internal ECC, clear the bit to 0
- */
-static int spinand_set_otp(struct spi_device *spi_nand, u8 *otp)
-{
- int retval;
- struct spinand_cmd cmd = {0};
-
- cmd.cmd = CMD_WRITE_REG,
- cmd.n_addr = 1,
- cmd.addr[0] = REG_OTP,
- cmd.n_tx = 1,
- cmd.tx_buf = otp,
-
- retval = spinand_cmd(spi_nand, &cmd);
- if (retval < 0)
- dev_err(&spi_nand->dev, "error %d set otp\n", retval);
-
- return retval;
-}
-
-#ifdef CONFIG_MTD_SPINAND_ONDIEECC
-/**
- * spinand_enable_ecc- send command 0x1f to write the SPI Nand OTP register
- * Description:
- * There is one bit( bit 0x10 ) to set or to clear the internal ECC.
- * Enable chip internal ECC, set the bit to 1
- * Disable chip internal ECC, clear the bit to 0
- */
-static int spinand_enable_ecc(struct spi_device *spi_nand)
-{
- int retval;
- u8 otp = 0;
-
- retval = spinand_get_otp(spi_nand, &otp);
- if (retval < 0)
- return retval;
-
- if ((otp & OTP_ECC_MASK) == OTP_ECC_MASK)
- return 0;
- otp |= OTP_ECC_MASK;
- retval = spinand_set_otp(spi_nand, &otp);
- if (retval < 0)
- return retval;
- return spinand_get_otp(spi_nand, &otp);
-}
-#endif
-
-static int spinand_disable_ecc(struct spi_device *spi_nand)
-{
- int retval;
- u8 otp = 0;
-
- retval = spinand_get_otp(spi_nand, &otp);
- if (retval < 0)
- return retval;
-
- if ((otp & OTP_ECC_MASK) == OTP_ECC_MASK) {
- otp &= ~OTP_ECC_MASK;
- retval = spinand_set_otp(spi_nand, &otp);
- if (retval < 0)
- return retval;
- return spinand_get_otp(spi_nand, &otp);
- }
- return 0;
-}
-
-/**
- * spinand_write_enable- send command 0x06 to enable write or erase the
- * Nand cells
- * Description:
- * Before write and erase the Nand cells, the write enable has to be set.
- * After the write or erase, the write enable bit is automatically
- * cleared (status register bit 2)
- * Set the bit 2 of the status register has the same effect
- */
-static int spinand_write_enable(struct spi_device *spi_nand)
-{
- struct spinand_cmd cmd = {0};
-
- cmd.cmd = CMD_WR_ENABLE;
- return spinand_cmd(spi_nand, &cmd);
-}
-
-static int spinand_read_page_to_cache(struct spi_device *spi_nand, u16 page_id)
-{
- struct spinand_cmd cmd = {0};
- u16 row;
-
- row = page_id;
- cmd.cmd = CMD_READ;
- cmd.n_addr = 3;
- cmd.addr[1] = (u8)((row & 0xff00) >> 8);
- cmd.addr[2] = (u8)(row & 0x00ff);
-
- return spinand_cmd(spi_nand, &cmd);
-}
-
-/*
- * spinand_read_from_cache- send command 0x03 to read out the data from the
- * cache register(2112 bytes max)
- * Description:
- * The read can specify 1 to 2112 bytes of data read at the corresponding
- * locations.
- * No tRd delay.
- */
-static int spinand_read_from_cache(struct spi_device *spi_nand, u16 page_id,
- u16 byte_id, u16 len, u8 *rbuf)
-{
- struct spinand_cmd cmd = {0};
- u16 column;
-
- column = byte_id;
- cmd.cmd = CMD_READ_RDM;
- cmd.n_addr = 3;
- cmd.addr[0] = (u8)((column & 0xff00) >> 8);
- cmd.addr[0] |= (u8)(((page_id >> 6) & 0x1) << 4);
- cmd.addr[1] = (u8)(column & 0x00ff);
- cmd.addr[2] = (u8)(0xff);
- cmd.n_dummy = 0;
- cmd.n_rx = len;
- cmd.rx_buf = rbuf;
-
- return spinand_cmd(spi_nand, &cmd);
-}
-
-/*
- * spinand_read_page-to read a page with:
- * @page_id: the physical page number
- * @offset: the location from 0 to 2111
- * @len: number of bytes to read
- * @rbuf: read buffer to hold @len bytes
- *
- * Description:
- * The read includes two commands to the Nand: 0x13 and 0x03 commands
- * Poll to read status to wait for tRD time.
- */
-static int spinand_read_page(struct spi_device *spi_nand, u16 page_id,
- u16 offset, u16 len, u8 *rbuf)
-{
- int ret;
- u8 status = 0;
-
-#ifdef CONFIG_MTD_SPINAND_ONDIEECC
- if (enable_read_hw_ecc) {
- if (spinand_enable_ecc(spi_nand) < 0)
- dev_err(&spi_nand->dev, "enable HW ECC failed!");
- }
-#endif
- ret = spinand_read_page_to_cache(spi_nand, page_id);
- if (ret < 0)
- return ret;
-
- if (wait_till_ready(spi_nand))
- dev_err(&spi_nand->dev, "WAIT timedout!!!\n");
-
- while (1) {
- ret = spinand_read_status(spi_nand, &status);
- if (ret < 0) {
- dev_err(&spi_nand->dev,
- "err %d read status register\n", ret);
- return ret;
- }
-
- if ((status & STATUS_OIP_MASK) == STATUS_READY) {
- if ((status & STATUS_ECC_MASK) == STATUS_ECC_ERROR) {
- dev_err(&spi_nand->dev, "ecc error, page=%d\n",
- page_id);
- return 0;
- }
- break;
- }
- }
-
- ret = spinand_read_from_cache(spi_nand, page_id, offset, len, rbuf);
- if (ret < 0) {
- dev_err(&spi_nand->dev, "read from cache failed!!\n");
- return ret;
- }
-
-#ifdef CONFIG_MTD_SPINAND_ONDIEECC
- if (enable_read_hw_ecc) {
- ret = spinand_disable_ecc(spi_nand);
- if (ret < 0) {
- dev_err(&spi_nand->dev, "disable ecc failed!!\n");
- return ret;
- }
- enable_read_hw_ecc = 0;
- }
-#endif
- return ret;
-}
-
-/*
- * spinand_program_data_to_cache--to write a page to cache with:
- * @byte_id: the location to write to the cache
- * @len: number of bytes to write
- * @rbuf: read buffer to hold @len bytes
- *
- * Description:
- * The write command used here is 0x84--indicating that the cache is
- * not cleared first.
- * Since it is writing the data to cache, there is no tPROG time.
- */
-static int spinand_program_data_to_cache(struct spi_device *spi_nand,
- u16 page_id, u16 byte_id, u16 len, u8 *wbuf)
-{
- struct spinand_cmd cmd = {0};
- u16 column;
-
- column = byte_id;
- cmd.cmd = CMD_PROG_PAGE_CLRCACHE;
- cmd.n_addr = 2;
- cmd.addr[0] = (u8)((column & 0xff00) >> 8);
- cmd.addr[0] |= (u8)(((page_id >> 6) & 0x1) << 4);
- cmd.addr[1] = (u8)(column & 0x00ff);
- cmd.n_tx = len;
- cmd.tx_buf = wbuf;
-
- return spinand_cmd(spi_nand, &cmd);
-}
-
-/**
- * spinand_program_execute--to write a page from cache to the Nand array with
- * @page_id: the physical page location to write the page.
- *
- * Description:
- * The write command used here is 0x10--indicating the cache is writing to
- * the Nand array.
- * Need to wait for tPROG time to finish the transaction.
- */
-static int spinand_program_execute(struct spi_device *spi_nand, u16 page_id)
-{
- struct spinand_cmd cmd = {0};
- u16 row;
-
- row = page_id;
- cmd.cmd = CMD_PROG_PAGE_EXC;
- cmd.n_addr = 3;
- cmd.addr[1] = (u8)((row & 0xff00) >> 8);
- cmd.addr[2] = (u8)(row & 0x00ff);
-
- return spinand_cmd(spi_nand, &cmd);
-}
-
-/**
- * spinand_program_page--to write a page with:
- * @page_id: the physical page location to write the page.
- * @offset: the location from the cache starting from 0 to 2111
- * @len: the number of bytes to write
- * @wbuf: the buffer to hold the number of bytes
- *
- * Description:
- * The commands used here are 0x06, 0x84, and 0x10--indicating that
- * the write enable is first sent, the write cache command, and the
- * write execute command.
- * Poll to wait for the tPROG time to finish the transaction.
- */
-static int spinand_program_page(struct spi_device *spi_nand,
- u16 page_id, u16 offset, u16 len, u8 *buf)
-{
- int retval;
- u8 status = 0;
- uint8_t *wbuf;
-#ifdef CONFIG_MTD_SPINAND_ONDIEECC
- unsigned int i, j;
-
- enable_read_hw_ecc = 0;
- wbuf = devm_kzalloc(&spi_nand->dev, CACHE_BUF, GFP_KERNEL);
- spinand_read_page(spi_nand, page_id, 0, CACHE_BUF, wbuf);
-
- for (i = offset, j = 0; i < len; i++, j++)
- wbuf[i] &= buf[j];
-
- if (enable_hw_ecc) {
- retval = spinand_enable_ecc(spi_nand);
- if (retval < 0) {
- dev_err(&spi_nand->dev, "enable ecc failed!!\n");
- return retval;
- }
- }
-#else
- wbuf = buf;
-#endif
- retval = spinand_write_enable(spi_nand);
- if (retval < 0) {
- dev_err(&spi_nand->dev, "write enable failed!!\n");
- return retval;
- }
- if (wait_till_ready(spi_nand))
- dev_err(&spi_nand->dev, "wait timedout!!!\n");
-
- retval = spinand_program_data_to_cache(spi_nand, page_id,
- offset, len, wbuf);
- if (retval < 0)
- return retval;
- retval = spinand_program_execute(spi_nand, page_id);
- if (retval < 0)
- return retval;
- while (1) {
- retval = spinand_read_status(spi_nand, &status);
- if (retval < 0) {
- dev_err(&spi_nand->dev,
- "error %d reading status register\n",
- retval);
- return retval;
- }
-
- if ((status & STATUS_OIP_MASK) == STATUS_READY) {
- if ((status & STATUS_P_FAIL_MASK) == STATUS_P_FAIL) {
- dev_err(&spi_nand->dev,
- "program error, page %d\n", page_id);
- return -1;
- }
- break;
- }
- }
-#ifdef CONFIG_MTD_SPINAND_ONDIEECC
- if (enable_hw_ecc) {
- retval = spinand_disable_ecc(spi_nand);
- if (retval < 0) {
- dev_err(&spi_nand->dev, "disable ecc failed!!\n");
- return retval;
- }
- enable_hw_ecc = 0;
- }
-#endif
-
- return 0;
-}
-
-/**
- * spinand_erase_block_erase--to erase a page with:
- * @block_id: the physical block location to erase.
- *
- * Description:
- * The command used here is 0xd8--indicating an erase command to erase
- * one block--64 pages
- * Need to wait for tERS.
- */
-static int spinand_erase_block_erase(struct spi_device *spi_nand, u16 block_id)
-{
- struct spinand_cmd cmd = {0};
- u16 row;
-
- row = block_id;
- cmd.cmd = CMD_ERASE_BLK;
- cmd.n_addr = 3;
- cmd.addr[1] = (u8)((row & 0xff00) >> 8);
- cmd.addr[2] = (u8)(row & 0x00ff);
-
- return spinand_cmd(spi_nand, &cmd);
-}
-
-/**
- * spinand_erase_block--to erase a page with:
- * @block_id: the physical block location to erase.
- *
- * Description:
- * The commands used here are 0x06 and 0xd8--indicating an erase
- * command to erase one block--64 pages
- * It will first to enable the write enable bit (0x06 command),
- * and then send the 0xd8 erase command
- * Poll to wait for the tERS time to complete the tranaction.
- */
-static int spinand_erase_block(struct spi_device *spi_nand, u16 block_id)
-{
- int retval;
- u8 status = 0;
-
- retval = spinand_write_enable(spi_nand);
- if (wait_till_ready(spi_nand))
- dev_err(&spi_nand->dev, "wait timedout!!!\n");
-
- retval = spinand_erase_block_erase(spi_nand, block_id);
- while (1) {
- retval = spinand_read_status(spi_nand, &status);
- if (retval < 0) {
- dev_err(&spi_nand->dev,
- "error %d reading status register\n",
- (int) retval);
- return retval;
- }
-
- if ((status & STATUS_OIP_MASK) == STATUS_READY) {
- if ((status & STATUS_E_FAIL_MASK) == STATUS_E_FAIL) {
- dev_err(&spi_nand->dev,
- "erase error, block %d\n", block_id);
- return -1;
- }
- break;
- }
- }
- return 0;
-}
-
-#ifdef CONFIG_MTD_SPINAND_ONDIEECC
-static int spinand_write_page_hwecc(struct mtd_info *mtd,
- struct nand_chip *chip, const uint8_t *buf, int oob_required)
-{
- const uint8_t *p = buf;
- int eccsize = chip->ecc.size;
- int eccsteps = chip->ecc.steps;
-
- enable_hw_ecc = 1;
- chip->write_buf(mtd, p, eccsize * eccsteps);
- return 0;
-}
-
-static int spinand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
+static int spinand_init_size(struct mtd_info *mtd, struct nand_chip *chip,
+ u8 *id)
{
- u8 retval, status;
- uint8_t *p = buf;
- int eccsize = chip->ecc.size;
- int eccsteps = chip->ecc.steps;
- struct spinand_info *info = (struct spinand_info *)chip->priv;
+ if (id[0] == 0x2C && id[1] == 0x22) {
+ mtd->erasesize = (2048*64);
+ mtd->writesize = 2048;
+ mtd->oobsize = 64;
- enable_read_hw_ecc = 1;
-
- chip->read_buf(mtd, p, eccsize * eccsteps);
- if (oob_required)
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
-
- while (1) {
- retval = spinand_read_status(info->spi, &status);
- if ((status & STATUS_OIP_MASK) == STATUS_READY) {
- if ((status & STATUS_ECC_MASK) == STATUS_ECC_ERROR) {
- pr_info("spinand: ECC error\n");
- mtd->ecc_stats.failed++;
- } else if ((status & STATUS_ECC_MASK) ==
- STATUS_ECC_1BIT_CORRECTED)
- mtd->ecc_stats.corrected++;
- break;
- }
+ chip->bits_per_cell = 1;
}
- return 0;
-
-}
-#endif
-
-static void spinand_select_chip(struct mtd_info *mtd, int dev)
-{
-}
-
-static uint8_t spinand_read_byte(struct mtd_info *mtd)
-{
- struct spinand_state *state = mtd_to_state(mtd);
- u8 data;
-
- data = state->buf[state->buf_ptr];
- state->buf_ptr++;
- return data;
-}
-
-
-static int spinand_wait(struct mtd_info *mtd, struct nand_chip *chip)
-{
- struct spinand_info *info = (struct spinand_info *)chip->priv;
- unsigned long timeo = jiffies;
- int retval, state = chip->state;
- u8 status;
-
- if (state == FL_ERASING)
- timeo += (HZ * 400) / 1000;
- else
- timeo += (HZ * 20) / 1000;
-
- while (time_before(jiffies, timeo)) {
- retval = spinand_read_status(info->spi, &status);
- if ((status & STATUS_OIP_MASK) == STATUS_READY)
- return 0;
-
- cond_resched();
- }
return 0;
}
-static void spinand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
-{
-
- struct spinand_state *state = mtd_to_state(mtd);
-
- memcpy(state->buf + state->buf_ptr, buf, len);
- state->buf_ptr += len;
-}
-
-static void spinand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
-{
- struct spinand_state *state = mtd_to_state(mtd);
-
- memcpy(buf, state->buf + state->buf_ptr, len);
- state->buf_ptr += len;
-}
/*
- * spinand_reset- send RESET command "0xff" to the Nand device.
- */
-static void spinand_reset(struct spi_device *spi_nand)
-{
- struct spinand_cmd cmd = {0};
-
- cmd.cmd = CMD_RESET;
-
- if (spinand_cmd(spi_nand, &cmd) < 0)
- pr_info("spinand reset failed!\n");
-
- /* elapse 1ms before issuing any other command */
- udelay(1000);
-
- if (wait_till_ready(spi_nand))
- dev_err(&spi_nand->dev, "wait timedout!\n");
-}
-
-static void spinand_cmdfunc(struct mtd_info *mtd, unsigned int command,
- int column, int page)
-{
- struct nand_chip *chip = (struct nand_chip *)mtd->priv;
- struct spinand_info *info = (struct spinand_info *)chip->priv;
- struct spinand_state *state = (struct spinand_state *)info->priv;
-
- switch (command) {
- /*
- * READ0 - read in first 0x800 bytes
- */
- case NAND_CMD_READ1:
- case NAND_CMD_READ0:
- state->buf_ptr = 0;
- spinand_read_page(info->spi, page, 0x0, 0x840, state->buf);
- break;
- /* READOOB reads only the OOB because no ECC is performed. */
- case NAND_CMD_READOOB:
- state->buf_ptr = 0;
- spinand_read_page(info->spi, page, 0x800, 0x40, state->buf);
- break;
- case NAND_CMD_RNDOUT:
- state->buf_ptr = column;
- break;
- case NAND_CMD_READID:
- state->buf_ptr = 0;
- spinand_read_id(info->spi, (u8 *)state->buf);
- break;
- case NAND_CMD_PARAM:
- state->buf_ptr = 0;
- break;
- /* ERASE1 stores the block and page address */
- case NAND_CMD_ERASE1:
- spinand_erase_block(info->spi, page);
- break;
- /* ERASE2 uses the block and page address from ERASE1 */
- case NAND_CMD_ERASE2:
- break;
- /* SEQIN sets up the addr buffer and all registers except the length */
- case NAND_CMD_SEQIN:
- state->col = column;
- state->row = page;
- state->buf_ptr = 0;
- break;
- /* PAGEPROG reuses all of the setup from SEQIN and adds the length */
- case NAND_CMD_PAGEPROG:
- spinand_program_page(info->spi, state->row, state->col,
- state->buf_ptr, state->buf);
- break;
- case NAND_CMD_STATUS:
- spinand_get_otp(info->spi, state->buf);
- if (!(state->buf[0] & 0x80))
- state->buf[0] = 0x80;
- state->buf_ptr = 0;
- break;
- /* RESET command */
- case NAND_CMD_RESET:
- if (wait_till_ready(info->spi))
- dev_err(&info->spi->dev, "WAIT timedout!!!\n");
- /* a minimum of 250us must elapse before issuing RESET cmd*/
- udelay(250);
- spinand_reset(info->spi);
- break;
- default:
- dev_err(&mtd->dev, "Unknown CMD: 0x%x\n", command);
- }
-}
-
-/**
- * spinand_lock_block- send write register 0x1f command to the Nand device
- *
- * Description:
- * After power up, all the Nand blocks are locked. This function allows
- * one to unlock the blocks, and so it can be written or erased.
- */
-static int spinand_lock_block(struct spi_device *spi_nand, u8 lock)
-{
- struct spinand_cmd cmd = {0};
- int ret;
- u8 otp = 0;
-
- ret = spinand_get_otp(spi_nand, &otp);
-
- cmd.cmd = CMD_WRITE_REG;
- cmd.n_addr = 1;
- cmd.addr[0] = REG_BLOCK_LOCK;
- cmd.n_tx = 1;
- cmd.tx_buf = &lock;
-
- ret = spinand_cmd(spi_nand, &cmd);
- if (ret < 0)
- dev_err(&spi_nand->dev, "error %d lock block\n", ret);
-
- return ret;
-}
-/*
* spinand_probe - [spinand Interface]
* @spi_nand: registered device driver.
*
* Description:
* To set up the device driver parameters to make the device available.
*/
-static int spinand_probe(struct spi_device *spi_nand)
+static int mt29f_probe(struct spi_device *spi)
{
- struct mtd_info *mtd;
+ struct spi_nand_chip *spi_chip;
struct nand_chip *chip;
- struct spinand_info *info;
- struct spinand_state *state;
- struct mtd_part_parser_data ppdata;
-
- info = devm_kzalloc(&spi_nand->dev, sizeof(struct spinand_info),
- GFP_KERNEL);
- if (!info)
- return -ENOMEM;
-
- info->spi = spi_nand;
-
- spinand_lock_block(spi_nand, BL_ALL_UNLOCKED);
- state = devm_kzalloc(&spi_nand->dev, sizeof(struct spinand_state),
- GFP_KERNEL);
- if (!state)
- return -ENOMEM;
-
- info->priv = state;
- state->buf_ptr = 0;
- state->buf = devm_kzalloc(&spi_nand->dev, BUFSIZE, GFP_KERNEL);
- if (!state->buf)
- return -ENOMEM;
-
- chip = devm_kzalloc(&spi_nand->dev, sizeof(struct nand_chip),
- GFP_KERNEL);
- if (!chip)
- return -ENOMEM;
+ spi_chip = spinand_alloc_chip(spi);
+ if (IS_ERR(spi_chip))
+ return PTR_ERR(spi_chip);
+ chip = &spi_chip->chip;
#ifdef CONFIG_MTD_SPINAND_ONDIEECC
- chip->ecc.mode = NAND_ECC_HW;
chip->ecc.size = 0x200;
chip->ecc.bytes = 0x6;
chip->ecc.steps = 0x4;
@@ -873,39 +88,12 @@ static int spinand_probe(struct spi_device *spi_nand)
chip->ecc.strength = 1;
chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;
chip->ecc.layout = &spinand_oob_64;
- chip->ecc.read_page = spinand_read_page_hwecc;
- chip->ecc.write_page = spinand_write_page_hwecc;
-#else
- chip->ecc.mode = NAND_ECC_SOFT;
- if (spinand_disable_ecc(spi_nand) < 0)
- pr_info("%s: disable ecc failed!\n", __func__);
#endif
+ chip->init_size = spinand_init_size;
+ spinand_set_buffer_size(spi_chip, 10*2048*64, 2048+64);
+ spinand_set_plane_info(spi_chip, 1, 6);
- chip->priv = info;
- chip->read_buf = spinand_read_buf;
- chip->write_buf = spinand_write_buf;
- chip->read_byte = spinand_read_byte;
- chip->cmdfunc = spinand_cmdfunc;
- chip->waitfunc = spinand_wait;
- chip->options |= NAND_CACHEPRG;
- chip->select_chip = spinand_select_chip;
-
- mtd = devm_kzalloc(&spi_nand->dev, sizeof(struct mtd_info), GFP_KERNEL);
- if (!mtd)
- return -ENOMEM;
-
- dev_set_drvdata(&spi_nand->dev, mtd);
-
- mtd->priv = chip;
- mtd->name = dev_name(&spi_nand->dev);
- mtd->owner = THIS_MODULE;
- mtd->oobsize = 64;
-
- if (nand_scan(mtd, 1))
- return -ENXIO;
-
- ppdata.of_node = spi_nand->dev.of_node;
- return mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
+ return spinand_register_chip(spi_chip);
}
/*
@@ -937,7 +125,7 @@ static struct spi_driver spinand_driver = {
.owner = THIS_MODULE,
.of_match_table = spinand_dt,
},
- .probe = spinand_probe,
+ .probe = mt29f_probe,
.remove = spinand_remove,
};
diff --git a/include/linux/mtd/spi-nand.h b/include/linux/mtd/spi-nand.h
new file mode 100644
index 0000000..da1c37a
--- /dev/null
+++ b/include/linux/mtd/spi-nand.h
@@ -0,0 +1,116 @@
+/*-
+ *
+ * Copyright (c) 2009-2014 Micron Technology, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Peter Pan <bpqw at micron.com>
+ *
+ * based on mt29f_spinand.h
+ */
+#ifndef __LINUX_MTD_SPI_NAND_H
+#define __LINUX_MTD_SPI_NAND_H
+
+#include <linux/wait.h>
+#include <linux/spinlock.h>
+#include <linux/mtd/mtd.h>
+
+/* cmd */
+#define SPINAND_CMD_READ 0x13
+#define SPINAND_CMD_READ_RDM 0x03
+#define SPINAND_CMD_PROG_PAGE_CLRCACHE 0x02
+#define SPINAND_CMD_PROG_PAGE 0x84
+#define SPINAND_CMD_PROG_PAGE_EXC 0x10
+#define SPINAND_CMD_ERASE_BLK 0xd8
+#define SPINAND_CMD_WR_ENABLE 0x06
+#define SPINAND_CMD_WR_DISABLE 0x04
+#define SPINAND_CMD_READ_ID 0x9f
+#define SPINAND_CMD_RESET 0xff
+#define SPINAND_CMD_READ_REG 0x0f
+#define SPINAND_CMD_WRITE_REG 0x1f
+
+/* feature/ status reg */
+#define REG_BLOCK_LOCK 0xa0
+#define REG_OTP 0xb0
+#define REG_STATUS 0xc0/* timing */
+
+/* status */
+#define STATUS_OIP_MASK 0x01
+#define STATUS_READY (0 << 0)
+#define STATUS_BUSY (1 << 0)
+
+#define STATUS_E_FAIL_MASK 0x04
+#define STATUS_E_FAIL (1 << 2)
+
+#define STATUS_P_FAIL_MASK 0x08
+#define STATUS_P_FAIL (1 << 3)
+
+#define STATUS_ECC_MASK 0x30
+#define STATUS_ECC_1BIT_CORRECTED (1 << 4)
+#define STATUS_ECC_ERROR (2 << 4)
+#define STATUS_ECC_RESERVED (3 << 4)
+
+/*ECC enable defines*/
+#define OTP_ECC_MASK 0x10
+#define OTP_ECC_OFF 0
+#define OTP_ECC_ON 1
+
+#define ECC_DISABLED
+#define ECC_IN_NAND
+#define ECC_SOFT
+
+/* block lock */
+#define BL_ALL_LOCKED 0x38
+#define BL_1_2_LOCKED 0x30
+#define BL_1_4_LOCKED 0x28
+#define BL_1_8_LOCKED 0x20
+#define BL_1_16_LOCKED 0x18
+#define BL_1_32_LOCKED 0x10
+#define BL_1_64_LOCKED 0x08
+#define BL_ALL_UNLOCKED 0
+
+struct spi_nand_chip {
+ struct nand_chip chip;
+ struct spi_device *spi;
+ u8 *buf;
+ u32 buf_size;
+ int buf_ptr;
+ uint32_t col;
+ uint32_t row;
+ u32 cache_size;
+ u16 plane_sel;
+ u16 plane_bit;
+#ifdef CONFIG_MTD_SPINAND_ONDIEECC
+ u32 enable_hw_ecc;
+ u32 enable_read_hw_ecc;
+ u8 *ecc_buf;
+ struct nand_ecclayout *ecclayout;
+#endif
+};
+
+struct spinand_cmd {
+ u8 cmd;
+ u32 n_addr; /* Number of address */
+ u8 addr[3]; /* Reg Offset */
+ u32 n_dummy; /* Dummy use */
+ u32 n_tx; /* Number of tx bytes */
+ u8 *tx_buf; /* Tx buf */
+ u32 n_rx; /* Number of rx bytes */
+ u8 *rx_buf; /* Rx buf */
+};
+
+struct spi_nand_chip *spinand_alloc_chip(struct spi_device *spi);
+int spinand_register_chip(struct spi_nand_chip *spi_chip);
+int spinand_set_buffer_size(struct spi_nand_chip *spi_chip, u32 buf_size,
+ u32 cache_size);
+int spinand_set_plane_info(struct spi_nand_chip *spi_chip, u16 plane_sel,
+ u16 plane_bit);
+
+#endif /* __LINUX_MTD_SPI_NAND_H */
--
1.9.1
More information about the linux-mtd
mailing list