diff options
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.patch | 706 |
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 |