aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/pistachio/patches-4.9/413-mtd-Introduce-SPI-NAND-framework.patch
diff options
context:
space:
mode:
authorMatti Laakso <matti.laakso@outlook.com>2018-03-18 17:26:24 +0200
committerHauke Mehrtens <hauke@hauke-m.de>2018-03-31 16:13:19 +0200
commit76f267ef6c54cb4cd9c1bee6e21d09495ac2f733 (patch)
treecf66c913439ec27af5e97fd8bdcf9949fb261918 /target/linux/pistachio/patches-4.9/413-mtd-Introduce-SPI-NAND-framework.patch
parent0394edbba0d365a367cc207b62914f3adce2ee1d (diff)
downloadupstream-76f267ef6c54cb4cd9c1bee6e21d09495ac2f733.tar.gz
upstream-76f267ef6c54cb4cd9c1bee6e21d09495ac2f733.tar.bz2
upstream-76f267ef6c54cb4cd9c1bee6e21d09495ac2f733.zip
pistachio: remove kernel 4.9 support
Signed-off-by: Matti Laakso <matti.laakso@outlook.com>
Diffstat (limited to 'target/linux/pistachio/patches-4.9/413-mtd-Introduce-SPI-NAND-framework.patch')
-rw-r--r--target/linux/pistachio/patches-4.9/413-mtd-Introduce-SPI-NAND-framework.patch706
1 files changed, 0 insertions, 706 deletions
diff --git a/target/linux/pistachio/patches-4.9/413-mtd-Introduce-SPI-NAND-framework.patch b/target/linux/pistachio/patches-4.9/413-mtd-Introduce-SPI-NAND-framework.patch
deleted file mode 100644
index b13dde7115..0000000000
--- a/target/linux/pistachio/patches-4.9/413-mtd-Introduce-SPI-NAND-framework.patch
+++ /dev/null
@@ -1,706 +0,0 @@
-From 082a89a78e29b15008284df90441747cb742f149 Mon Sep 17 00:00:00 2001
-From: Ezequiel Garcia <ezequiel.garcia@imgtec.com>
-Date: Tue, 2 Dec 2014 09:58:52 -0300
-Subject: mtd: Introduce SPI NAND framework
-
-Add a new framework, to support SPI NAND devices. The framework registers
-a NAND chip and handles the generic SPI NAND protocol, calling device-specific
-hooks for each SPI NAND command.
-
-The following is the stack design, from userspace to hardware. This commit
-adds the "SPI NAND core" layer.
-
- Userspace
- ------------------
- MTD
- ------------------
- NAND core
- ------------------
- SPI NAND core
- ------------------
- SPI NAND device
- ------------------
- SPI core
- ------------------
- SPI master
- ------------------
- Hardware
-
-(based on http://lists.infradead.org/pipermail/linux-mtd/2014-December/056763.html)
-
-Signed-off-by: Ionela Voinescu <ionela.voinescu@imgtec.com>
-Signed-off-by: Ezequiel Garcia <ezequiel.garcia@imgtec.com>
-Signed-off-by: Ian Pozella <Ian.Pozella@imgtec.com>
----
- drivers/mtd/Kconfig | 2 +
- drivers/mtd/Makefile | 1 +
- drivers/mtd/spi-nand/Kconfig | 7 +
- drivers/mtd/spi-nand/Makefile | 1 +
- drivers/mtd/spi-nand/spi-nand-base.c | 566 +++++++++++++++++++++++++++++++++++
- include/linux/mtd/spi-nand.h | 54 ++++
- 6 files changed, 631 insertions(+)
- 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-base.c
- create mode 100644 include/linux/mtd/spi-nand.h
-
---- a/drivers/mtd/Kconfig
-+++ b/drivers/mtd/Kconfig
-@@ -373,6 +373,8 @@ source "drivers/mtd/onenand/Kconfig"
-
- source "drivers/mtd/lpddr/Kconfig"
-
-+source "drivers/mtd/spi-nand/Kconfig"
-+
- source "drivers/mtd/spi-nor/Kconfig"
-
- source "drivers/mtd/ubi/Kconfig"
---- a/drivers/mtd/Makefile
-+++ b/drivers/mtd/Makefile
-@@ -36,5 +36,6 @@ inftl-objs := inftlcore.o inftlmount.o
-
- obj-y += chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/
-
-+obj-$(CONFIG_MTD_SPI_NAND) += spi-nand/
- obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/
- obj-$(CONFIG_MTD_UBI) += ubi/
---- /dev/null
-+++ b/drivers/mtd/spi-nand/Kconfig
-@@ -0,0 +1,7 @@
-+menuconfig MTD_SPI_NAND
-+ tristate "SPI NAND device support"
-+ depends on MTD
-+ select MTD_NAND
-+ help
-+ This is the framework for the SPI NAND.
-+
---- /dev/null
-+++ b/drivers/mtd/spi-nand/Makefile
-@@ -0,0 +1 @@
-+obj-$(CONFIG_MTD_SPI_NAND) += spi-nand-base.o
---- /dev/null
-+++ b/drivers/mtd/spi-nand/spi-nand-base.c
-@@ -0,0 +1,566 @@
-+/*
-+ * Copyright (C) 2014 Imagination Technologies Ltd.
-+ *
-+ * 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; version 2 of the License.
-+ *
-+ * Notes:
-+ * 1. Erase and program operations need to call write_enable() first,
-+ * to clear the enable bit. This bit is cleared automatically after
-+ * the erase or program operation.
-+ *
-+ */
-+
-+#include <linux/device.h>
-+#include <linux/err.h>
-+#include <linux/errno.h>
-+#include <linux/kernel.h>
-+#include <linux/module.h>
-+#include <linux/mtd/nand.h>
-+#include <linux/mtd/mtd.h>
-+#include <linux/mtd/partitions.h>
-+#include <linux/mtd/spi-nand.h>
-+#include <linux/of.h>
-+#include <linux/slab.h>
-+
-+/* Registers common to all devices */
-+#define SPI_NAND_LOCK_REG 0xa0
-+#define SPI_NAND_PROT_UNLOCK_ALL 0x0
-+
-+#define SPI_NAND_FEATURE_REG 0xb0
-+#define SPI_NAND_ECC_EN BIT(4)
-+#define SPI_NAND_QUAD_EN BIT(0)
-+
-+#define SPI_NAND_STATUS_REG 0xc0
-+#define SPI_NAND_STATUS_REG_ECC_MASK 0x3
-+#define SPI_NAND_STATUS_REG_ECC_SHIFT 4
-+#define SPI_NAND_STATUS_REG_PROG_FAIL BIT(3)
-+#define SPI_NAND_STATUS_REG_ERASE_FAIL BIT(2)
-+#define SPI_NAND_STATUS_REG_WREN BIT(1)
-+#define SPI_NAND_STATUS_REG_BUSY BIT(0)
-+
-+#define SPI_NAND_CMD_BUF_LEN 8
-+
-+/* Rewind and fill the buffer with 0xff */
-+static void spi_nand_clear_buffer(struct spi_nand *snand)
-+{
-+ snand->buf_start = 0;
-+ memset(snand->data_buf, 0xff, snand->buf_size);
-+}
-+
-+static int spi_nand_enable_ecc(struct spi_nand *snand)
-+{
-+ int ret;
-+
-+ ret = snand->read_reg(snand, SPI_NAND_FEATURE_REG, snand->buf);
-+ if (ret)
-+ return ret;
-+
-+ snand->buf[0] |= SPI_NAND_ECC_EN;
-+ ret = snand->write_reg(snand, SPI_NAND_FEATURE_REG, snand->buf);
-+ if (ret)
-+ return ret;
-+ snand->ecc = true;
-+
-+ return 0;
-+}
-+
-+static int spi_nand_disable_ecc(struct spi_nand *snand)
-+{
-+ int ret;
-+
-+ ret = snand->read_reg(snand, SPI_NAND_FEATURE_REG, snand->buf);
-+ if (ret)
-+ return ret;
-+
-+ snand->buf[0] &= ~SPI_NAND_ECC_EN;
-+ ret = snand->write_reg(snand, SPI_NAND_FEATURE_REG, snand->buf);
-+ if (ret)
-+ return ret;
-+ snand->ecc = false;
-+
-+ return 0;
-+}
-+
-+static int spi_nand_enable_quad(struct spi_nand *snand)
-+{
-+ int ret;
-+
-+ ret = snand->read_reg(snand, SPI_NAND_FEATURE_REG, snand->buf);
-+ if (ret)
-+ return ret;
-+
-+ snand->buf[0] |= SPI_NAND_QUAD_EN;
-+ ret = snand->write_reg(snand, SPI_NAND_FEATURE_REG, snand->buf);
-+ if (ret)
-+ return ret;
-+
-+ return 0;
-+}
-+/*
-+ * Wait until the status register busy bit is cleared.
-+ * Returns a negatie errno on error or time out, and a non-negative status
-+ * value if the device is ready.
-+ */
-+static int spi_nand_wait_till_ready(struct spi_nand *snand)
-+{
-+ unsigned long deadline = jiffies + msecs_to_jiffies(100);
-+ bool timeout = false;
-+ int ret;
-+
-+ /*
-+ * Perhaps we should set a different timeout for each
-+ * operation (reset, read, write, erase).
-+ */
-+ while (!timeout) {
-+ if (time_after_eq(jiffies, deadline))
-+ timeout = true;
-+
-+ ret = snand->read_reg(snand, SPI_NAND_STATUS_REG, snand->buf);
-+ if (ret < 0) {
-+ dev_err(snand->dev, "error reading status register\n");
-+ return ret;
-+ } else if (!(snand->buf[0] & SPI_NAND_STATUS_REG_BUSY)) {
-+ return snand->buf[0];
-+ }
-+
-+ cond_resched();
-+ }
-+
-+ dev_err(snand->dev, "operation timed out\n");
-+
-+ return -ETIMEDOUT;
-+}
-+
-+static int spi_nand_reset(struct spi_nand *snand)
-+{
-+ int ret;
-+
-+ ret = snand->reset(snand);
-+ if (ret < 0) {
-+ dev_err(snand->dev, "reset command failed\n");
-+ return ret;
-+ }
-+
-+ /*
-+ * The NAND core won't wait after a device reset, so we need
-+ * to do that here.
-+ */
-+ ret = spi_nand_wait_till_ready(snand);
-+ if (ret < 0)
-+ return ret;
-+ return 0;
-+}
-+
-+static int spi_nand_status(struct spi_nand *snand)
-+{
-+ int ret, status;
-+
-+ ret = snand->read_reg(snand, SPI_NAND_STATUS_REG, snand->buf);
-+ if (ret < 0) {
-+ dev_err(snand->dev, "error reading status register\n");
-+ return ret;
-+ }
-+ status = snand->buf[0];
-+
-+ /* Convert this into standard NAND_STATUS values */
-+ if (status & SPI_NAND_STATUS_REG_BUSY)
-+ snand->buf[0] = 0;
-+ else
-+ snand->buf[0] = NAND_STATUS_READY;
-+
-+ if (status & SPI_NAND_STATUS_REG_PROG_FAIL ||
-+ status & SPI_NAND_STATUS_REG_ERASE_FAIL)
-+ snand->buf[0] |= NAND_STATUS_FAIL;
-+
-+ /*
-+ * Since we unlock the entire device at initialization, unconditionally
-+ * set the WP bit to indicate it's not protected.
-+ */
-+ snand->buf[0] |= NAND_STATUS_WP;
-+ return 0;
-+}
-+
-+static int spi_nand_erase(struct spi_nand *snand, int page_addr)
-+{
-+ int ret;
-+
-+ ret = snand->write_enable(snand);
-+ if (ret < 0) {
-+ dev_err(snand->dev, "write enable command failed\n");
-+ return ret;
-+ }
-+
-+ ret = snand->block_erase(snand, page_addr);
-+ if (ret < 0) {
-+ dev_err(snand->dev, "block erase command failed\n");
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+
-+static int spi_nand_write(struct spi_nand *snand)
-+{
-+ int ret;
-+
-+ /* Enable quad mode */
-+ ret = spi_nand_enable_quad(snand);
-+ if (ret) {
-+ dev_err(snand->dev, "error %d enabling quad mode\n", ret);
-+ return ret;
-+ }
-+ /* Store the page to cache */
-+ ret = snand->store_cache(snand, 0, snand->buf_size, snand->data_buf);
-+ if (ret < 0) {
-+ dev_err(snand->dev, "error %d storing page 0x%x to cache\n",
-+ ret, snand->page_addr);
-+ return ret;
-+ }
-+
-+ ret = snand->write_enable(snand);
-+ if (ret < 0) {
-+ dev_err(snand->dev, "write enable command failed\n");
-+ return ret;
-+ }
-+
-+ /* Get page from the device cache into our internal buffer */
-+ ret = snand->write_page(snand, snand->page_addr);
-+ if (ret < 0) {
-+ dev_err(snand->dev, "error %d reading page 0x%x from cache\n",
-+ ret, snand->page_addr);
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+
-+static int spi_nand_read_id(struct spi_nand *snand)
-+{
-+ int ret;
-+
-+ ret = snand->read_id(snand, snand->data_buf);
-+ if (ret < 0) {
-+ dev_err(snand->dev, "error %d reading ID\n", ret);
-+ return ret;
-+ }
-+ return 0;
-+}
-+
-+static int spi_nand_read_page(struct spi_nand *snand, unsigned int page_addr,
-+ unsigned int page_offset, size_t length)
-+{
-+ unsigned int corrected = 0, ecc_error = 0;
-+ int ret;
-+
-+ /* Load a page into the cache register */
-+ ret = snand->load_page(snand, page_addr);
-+ if (ret < 0) {
-+ dev_err(snand->dev, "error %d loading page 0x%x to cache\n",
-+ ret, page_addr);
-+ return ret;
-+ }
-+
-+ ret = spi_nand_wait_till_ready(snand);
-+ if (ret < 0)
-+ return ret;
-+
-+ if (snand->ecc) {
-+ snand->get_ecc_status(ret, &corrected, &ecc_error);
-+ snand->bitflips = corrected;
-+
-+ /*
-+ * If there's an ECC error, print a message and notify MTD
-+ * about it. Then complete the read, to load actual data on
-+ * the buffer (instead of the status result).
-+ */
-+ if (ecc_error) {
-+ dev_err(snand->dev,
-+ "internal ECC error reading page 0x%x\n",
-+ page_addr);
-+ snand->nand_chip.mtd.ecc_stats.failed++;
-+ } else {
-+ snand->nand_chip.mtd.ecc_stats.corrected += corrected;
-+ }
-+ }
-+
-+ /* Enable quad mode */
-+ ret = spi_nand_enable_quad(snand);
-+ if (ret) {
-+ dev_err(snand->dev, "error %d enabling quad mode\n", ret);
-+ return ret;
-+ }
-+ /* Get page from the device cache into our internal buffer */
-+ ret = snand->read_cache(snand, page_offset, length, snand->data_buf);
-+ if (ret < 0) {
-+ dev_err(snand->dev, "error %d reading page 0x%x from cache\n",
-+ ret, page_addr);
-+ return ret;
-+ }
-+ return 0;
-+}
-+
-+static u8 spi_nand_read_byte(struct mtd_info *mtd)
-+{
-+ struct nand_chip *chip = mtd_to_nand(mtd);
-+ struct spi_nand *snand = nand_get_controller_data(chip);
-+ char val = 0xff;
-+
-+ if (snand->buf_start < snand->buf_size)
-+ val = snand->data_buf[snand->buf_start++];
-+ return val;
-+}
-+
-+static void spi_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
-+{
-+ struct nand_chip *chip = mtd_to_nand(mtd);
-+ struct spi_nand *snand = nand_get_controller_data(chip);
-+ size_t n = min_t(size_t, len, snand->buf_size - snand->buf_start);
-+
-+ memcpy(snand->data_buf + snand->buf_start, buf, n);
-+ snand->buf_start += n;
-+}
-+
-+static void spi_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len)
-+{
-+ struct nand_chip *chip = mtd_to_nand(mtd);
-+ struct spi_nand *snand = nand_get_controller_data(chip);
-+ size_t n = min_t(size_t, len, snand->buf_size - snand->buf_start);
-+
-+ memcpy(buf, snand->data_buf + snand->buf_start, n);
-+ snand->buf_start += n;
-+}
-+
-+static int spi_nand_write_page_hwecc(struct mtd_info *mtd,
-+ struct nand_chip *chip, const uint8_t *buf, int oob_required,
-+ int page)
-+{
-+ chip->write_buf(mtd, buf, mtd->writesize);
-+ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
-+
-+ return 0;
-+}
-+
-+static int spi_nand_read_page_hwecc(struct mtd_info *mtd,
-+ struct nand_chip *chip, uint8_t *buf, int oob_required,
-+ int page)
-+{
-+ struct spi_nand *snand = nand_get_controller_data(chip);
-+
-+ chip->read_buf(mtd, buf, mtd->writesize);
-+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
-+
-+ return snand->bitflips;
-+}
-+
-+static int spi_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
-+{
-+ struct spi_nand *snand = nand_get_controller_data(chip);
-+ int ret;
-+
-+ ret = spi_nand_wait_till_ready(snand);
-+
-+ if (ret < 0) {
-+ return NAND_STATUS_FAIL;
-+ } else if (ret & SPI_NAND_STATUS_REG_PROG_FAIL) {
-+ dev_err(snand->dev, "page program failed\n");
-+ return NAND_STATUS_FAIL;
-+ } else if (ret & SPI_NAND_STATUS_REG_ERASE_FAIL) {
-+ dev_err(snand->dev, "block erase failed\n");
-+ return NAND_STATUS_FAIL;
-+ }
-+
-+ return NAND_STATUS_READY;
-+}
-+
-+static void spi_nand_cmdfunc(struct mtd_info *mtd, unsigned int command,
-+ int column, int page_addr)
-+{
-+ struct nand_chip *chip = mtd_to_nand(mtd);
-+ struct spi_nand *snand = nand_get_controller_data(chip);
-+
-+ /*
-+ * In case there's any unsupported command, let's make sure
-+ * we don't keep garbage around in the buffer.
-+ */
-+ if (command != NAND_CMD_PAGEPROG) {
-+ spi_nand_clear_buffer(snand);
-+ snand->page_addr = 0;
-+ }
-+
-+ switch (command) {
-+ case NAND_CMD_READ0:
-+ spi_nand_read_page(snand, page_addr, 0, mtd->writesize);
-+ break;
-+ case NAND_CMD_READOOB:
-+ spi_nand_disable_ecc(snand);
-+ spi_nand_read_page(snand, page_addr, mtd->writesize,
-+ mtd->oobsize);
-+ spi_nand_enable_ecc(snand);
-+ break;
-+ case NAND_CMD_READID:
-+ spi_nand_read_id(snand);
-+ break;
-+ case NAND_CMD_ERASE1:
-+ spi_nand_erase(snand, page_addr);
-+ break;
-+ case NAND_CMD_ERASE2:
-+ /* There's nothing to do here, as the erase is one-step */
-+ break;
-+ case NAND_CMD_SEQIN:
-+ snand->buf_start = column;
-+ snand->page_addr = page_addr;
-+ break;
-+ case NAND_CMD_PAGEPROG:
-+ spi_nand_write(snand);
-+ break;
-+ case NAND_CMD_STATUS:
-+ spi_nand_status(snand);
-+ break;
-+ case NAND_CMD_RESET:
-+ spi_nand_reset(snand);
-+ break;
-+ default:
-+ dev_err(&mtd->dev, "unknown command 0x%x\n", command);
-+ }
-+}
-+
-+static void spi_nand_select_chip(struct mtd_info *mtd, int chip)
-+{
-+ /* We need this to override the default */
-+}
-+
-+int spi_nand_check(struct spi_nand *snand)
-+{
-+ if (!snand->dev)
-+ return -ENODEV;
-+ if (!snand->read_cache)
-+ return -ENODEV;
-+ if (!snand->load_page)
-+ return -ENODEV;
-+ if (!snand->store_cache)
-+ return -ENODEV;
-+ if (!snand->write_page)
-+ return -ENODEV;
-+ if (!snand->write_reg)
-+ return -ENODEV;
-+ if (!snand->read_reg)
-+ return -ENODEV;
-+ if (!snand->block_erase)
-+ return -ENODEV;
-+ if (!snand->reset)
-+ return -ENODEV;
-+ if (!snand->write_enable)
-+ return -ENODEV;
-+ if (!snand->write_disable)
-+ return -ENODEV;
-+ if (!snand->get_ecc_status)
-+ return -ENODEV;
-+ return 0;
-+}
-+
-+int spi_nand_register(struct spi_nand *snand, struct nand_flash_dev *flash_ids)
-+{
-+ struct nand_chip *chip = &snand->nand_chip;
-+ struct mtd_info *mtd = nand_to_mtd(chip);
-+ struct device_node *np = snand->dev->of_node;
-+ const char __maybe_unused *of_mtd_name = NULL;
-+ int ret;
-+
-+ /* Let's check all the hooks are in-place so we don't panic later */
-+ ret = spi_nand_check(snand);
-+ if (ret)
-+ return ret;
-+
-+ nand_set_controller_data(chip, snand);
-+ nand_set_flash_node(chip, np);
-+ chip->read_buf = spi_nand_read_buf;
-+ chip->write_buf = spi_nand_write_buf;
-+ chip->read_byte = spi_nand_read_byte;
-+ chip->cmdfunc = spi_nand_cmdfunc;
-+ chip->waitfunc = spi_nand_waitfunc;
-+ chip->select_chip = spi_nand_select_chip;
-+ chip->options |= NAND_NO_SUBPAGE_WRITE;
-+ chip->bits_per_cell = 1;
-+
-+ mtd_set_ooblayout(mtd, snand->ooblayout);
-+ chip->ecc.read_page = spi_nand_read_page_hwecc;
-+ chip->ecc.write_page = spi_nand_write_page_hwecc;
-+ chip->ecc.mode = NAND_ECC_HW;
-+
-+ if (of_property_read_bool(np, "nand-on-flash-bbt"))
-+ chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
-+
-+#ifdef CONFIG_MTD_OF_PARTS
-+ of_property_read_string(np, "linux,mtd-name", &of_mtd_name);
-+#endif
-+ if (of_mtd_name)
-+ mtd->name = of_mtd_name;
-+ else
-+ mtd->name = snand->name;
-+ mtd->owner = THIS_MODULE;
-+
-+ /* Allocate buffer to be used to read/write the internal registers */
-+ snand->buf = kmalloc(SPI_NAND_CMD_BUF_LEN, GFP_KERNEL);
-+ if (!snand->buf)
-+ return -ENOMEM;
-+
-+ /* This is enabled at device power up but we'd better make sure */
-+ ret = spi_nand_enable_ecc(snand);
-+ if (ret)
-+ return ret;
-+
-+ /* Preallocate buffer for flash identification (NAND_CMD_READID) */
-+ snand->buf_size = SPI_NAND_CMD_BUF_LEN;
-+ snand->data_buf = kmalloc(snand->buf_size, GFP_KERNEL);
-+
-+ ret = nand_scan_ident(mtd, 1, flash_ids);
-+ if (ret)
-+ return ret;
-+
-+ /*
-+ * SPI NAND has on-die ECC, which means we can correct as much as
-+ * we are required to. This must be done after identification of
-+ * the device.
-+ */
-+ chip->ecc.strength = chip->ecc_strength_ds;
-+ chip->ecc.size = chip->ecc_step_ds;
-+
-+ /*
-+ * Unlock all the device before calling nand_scan_tail. This is needed
-+ * in case the in-flash bad block table needs to be created.
-+ * We could override __nand_unlock(), but since it's not currently used
-+ * by the NAND core we call this explicitly.
-+ */
-+ snand->buf[0] = SPI_NAND_PROT_UNLOCK_ALL;
-+ ret = snand->write_reg(snand, SPI_NAND_LOCK_REG, snand->buf);
-+ if (ret)
-+ return ret;
-+
-+ /* Free the buffer and allocate a good one, to fit a page plus OOB */
-+ kfree(snand->data_buf);
-+
-+ snand->buf_size = mtd->writesize + mtd->oobsize;
-+ snand->data_buf = kmalloc(snand->buf_size, GFP_KERNEL);
-+ if (!snand->data_buf)
-+ return -ENOMEM;
-+
-+ ret = nand_scan_tail(mtd);
-+ if (ret)
-+ return ret;
-+
-+ return mtd_device_register(mtd, NULL, 0);
-+}
-+EXPORT_SYMBOL_GPL(spi_nand_register);
-+
-+void spi_nand_unregister(struct spi_nand *snand)
-+{
-+ kfree(snand->buf);
-+ kfree(snand->data_buf);
-+}
-+EXPORT_SYMBOL_GPL(spi_nand_unregister);
-+
-+MODULE_AUTHOR("Ezequiel Garcia <ezequiel.garcia@imgtec.com>");
-+MODULE_DESCRIPTION("Framework for SPI NAND");
-+MODULE_LICENSE("GPL v2");
---- /dev/null
-+++ b/include/linux/mtd/spi-nand.h
-@@ -0,0 +1,54 @@
-+/*
-+ * Copyright (C) 2014 Imagination Technologies Ltd.
-+ *
-+ * 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; version 2 of the License.
-+ */
-+
-+#ifndef __LINUX_MTD_SPI_NAND_H
-+#define __LINUX_MTD_SPI_NAND_H
-+
-+#include <linux/mtd/mtd.h>
-+#include <linux/mtd/nand.h>
-+
-+struct spi_nand {
-+ struct nand_chip nand_chip;
-+ struct device *dev;
-+ const char *name;
-+
-+ u8 *buf, *data_buf;
-+ size_t buf_size;
-+ off_t buf_start;
-+ unsigned int page_addr;
-+ unsigned int bitflips;
-+ bool ecc;
-+ struct mtd_ooblayout_ops *ooblayout;
-+
-+ int (*reset)(struct spi_nand *snand);
-+ int (*read_id)(struct spi_nand *snand, u8 *buf);
-+
-+ int (*write_disable)(struct spi_nand *snand);
-+ int (*write_enable)(struct spi_nand *snand);
-+
-+ int (*read_reg)(struct spi_nand *snand, u8 opcode, u8 *buf);
-+ int (*write_reg)(struct spi_nand *snand, u8 opcode, u8 *buf);
-+ void (*get_ecc_status)(unsigned int status,
-+ unsigned int *corrected,
-+ unsigned int *ecc_errors);
-+
-+ int (*store_cache)(struct spi_nand *snand, unsigned int page_offset,
-+ size_t length, u8 *write_buf);
-+ int (*write_page)(struct spi_nand *snand, unsigned int page_addr);
-+ int (*load_page)(struct spi_nand *snand, unsigned int page_addr);
-+ int (*read_cache)(struct spi_nand *snand, unsigned int page_offset,
-+ size_t length, u8 *read_buf);
-+ int (*block_erase)(struct spi_nand *snand, unsigned int page_addr);
-+
-+ void *priv;
-+};
-+
-+int spi_nand_register(struct spi_nand *snand, struct nand_flash_dev *flash_ids);
-+void spi_nand_unregister(struct spi_nand *snand);
-+
-+#endif