From cae808946ae5f698d8c9e2c58b9b75e69c343c38 Mon Sep 17 00:00:00 2001 From: Matti Laakso Date: Sun, 18 Mar 2018 17:23:08 +0200 Subject: pistachio: add kernel 4.14 support Add patches and config for 4.14, refreshed from 4.9. Signed-off-by: Matti Laakso --- .../413-mtd-Introduce-SPI-NAND-framework.patch | 706 +++++++++++++++++++++ 1 file changed, 706 insertions(+) create mode 100644 target/linux/pistachio/patches-4.14/413-mtd-Introduce-SPI-NAND-framework.patch (limited to 'target/linux/pistachio/patches-4.14/413-mtd-Introduce-SPI-NAND-framework.patch') diff --git a/target/linux/pistachio/patches-4.14/413-mtd-Introduce-SPI-NAND-framework.patch b/target/linux/pistachio/patches-4.14/413-mtd-Introduce-SPI-NAND-framework.patch new file mode 100644 index 0000000000..fe93b2114a --- /dev/null +++ b/target/linux/pistachio/patches-4.14/413-mtd-Introduce-SPI-NAND-framework.patch @@ -0,0 +1,706 @@ +From 082a89a78e29b15008284df90441747cb742f149 Mon Sep 17 00:00:00 2001 +From: Ezequiel Garcia +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 +Signed-off-by: Ezequiel Garcia +Signed-off-by: Ian Pozella +--- + 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 +@@ -37,5 +37,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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* 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 "); ++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 ++#include ++ ++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 -- cgit v1.2.3