{"id":809228,"url":"http://patchwork.ozlabs.org/api/1.2/patches/809228/?format=json","web_url":"http://patchwork.ozlabs.org/project/lede/patch/20170903054359.4118-1-hackpascal@gmail.com/","project":{"id":54,"url":"http://patchwork.ozlabs.org/api/1.2/projects/54/?format=json","name":"LEDE development","link_name":"lede","list_id":"lede-dev.lists.infradead.org","list_email":"lede-dev@lists.infradead.org","web_url":"http://lede-project.org/","scm_url":"","webscm_url":"http://git.lede-project.org/","list_archive_url":"","list_archive_url_format":"","commit_url_format":""},"msgid":"<20170903054359.4118-1-hackpascal@gmail.com>","list_archive_url":null,"date":"2017-09-03T05:43:59","name":"[LEDE-DEV,PATCHv2,3/4] kernel/4.4: add generic spi-nand framework","commit_ref":null,"pull_url":null,"state":"superseded","archived":false,"hash":"2079f2e981197b996600a0e9216540b5786d6e31","submitter":{"id":65302,"url":"http://patchwork.ozlabs.org/api/1.2/people/65302/?format=json","name":"Weijie Gao","email":"hackpascal@gmail.com"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/lede/patch/20170903054359.4118-1-hackpascal@gmail.com/mbox/","series":[{"id":1199,"url":"http://patchwork.ozlabs.org/api/1.2/series/1199/?format=json","web_url":"http://patchwork.ozlabs.org/project/lede/list/?series=1199","date":"2017-09-03T05:43:18","name":"[LEDE-DEV,PATCHv2,1/4] mac80211: enable use of GPI9 of ath9k","version":1,"mbox":"http://patchwork.ozlabs.org/series/1199/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/809228/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/809228/checks/","tags":{},"related":[],"headers":{"Return-Path":"<lede-dev-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org>","X-Original-To":"incoming@patchwork.ozlabs.org","Delivered-To":"patchwork-incoming@bilbo.ozlabs.org","Authentication-Results":["ozlabs.org; spf=none (mailfrom)\n\tsmtp.mailfrom=lists.infradead.org (client-ip=65.50.211.133;\n\thelo=bombadil.infradead.org;\n\tenvelope-from=lede-dev-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org;\n\treceiver=<UNKNOWN>)","ozlabs.org; dkim=pass (2048-bit key;\n\tunprotected) header.d=lists.infradead.org\n\theader.i=@lists.infradead.org header.b=\"MFdpdsij\"; \n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=gmail.com header.i=@gmail.com\n\theader.b=\"JtBI1tM8\"; dkim-atps=neutral"],"Received":["from bombadil.infradead.org (bombadil.infradead.org\n\t[65.50.211.133])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256\n\tbits)) (No client certificate requested)\n\tby ozlabs.org (Postfix) with ESMTPS id 3xlMPt4Rcjz9s7C\n\tfor <incoming@patchwork.ozlabs.org>;\n\tSun,  3 Sep 2017 15:45:06 +1000 (AEST)","from localhost ([127.0.0.1] helo=bombadil.infradead.org)\n\tby bombadil.infradead.org with esmtp (Exim 4.87 #1 (Red Hat Linux))\n\tid 1doNiM-0007RZ-VH; Sun, 03 Sep 2017 05:44:58 +0000","from mail-pf0-x244.google.com ([2607:f8b0:400e:c00::244])\n\tby bombadil.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux))\n\tid 1doNhq-0006fk-Td\n\tfor lede-dev@lists.infradead.org; Sun, 03 Sep 2017 05:44:40 +0000","by mail-pf0-x244.google.com with SMTP id g13so2496515pfm.2\n\tfor <lede-dev@lists.infradead.org>;\n\tSat, 02 Sep 2017 22:44:04 -0700 (PDT)","from localhost ([182.148.31.147]) by smtp.gmail.com with ESMTPSA id\n\tp1sm5411305pfe.129.2017.09.02.22.44.01\n\t(version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256);\n\tSat, 02 Sep 2017 22:44:02 -0700 (PDT)"],"DKIM-Signature":["v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed;\n\td=lists.infradead.org; s=bombadil.20170209; h=Sender:\n\tContent-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe:\n\tList-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:Subject:Message-Id:\n\tDate:To:From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From:\n\tResent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:\n\tList-Owner; bh=Xwk5Q079cv7SZLUgPYDm1xdpaaNSFIuFiUT3xFL5KeQ=;\n\tb=MFdpdsijPBuF7X\n\tAMX+lwCgcAlW3QqrmoeXsX6WPLaTlKwx+fwDyF5rYETg0v6NzvTz0o1wih1Bkb73GoeQDakbfq8gq\n\tzPElh6r1XxxoMjD+4QtTFVz8LLVHGDYbUhrBVJOVX6oyQwxBZbgxMEWDkGeruEGnHjXYV1+iab6aJ\n\tIx4Z7Mq6ow36w9v52XgnviUqeA74fdbsClpz6ROVBqugWE6RZjn3HP6OPdHwPGP59syFPfO6caSOu\n\tK+rr/HUqfmkjyqj4kmMM4oiiaUWYWWyZV7J8jWv8Fr+G4cBAgA4a6dwD1xQvPjSPyj4wAnDkcKqFW\n\tbWgljxv6NC9aXRMTprEA==;","v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025;\n\th=from:to:cc:subject:date:message-id;\n\tbh=fw8X0p6gToKeeRGr5NkWUvJoI0HB9kFBCTVDgcjL9UA=;\n\tb=JtBI1tM8XMzctT07iOJCnKoTN7UTytKf5+V/2URFssMC6b2HEBS7YNlcw8j5YteUEj\n\tbnSB4EhPfULgiI+YyUV/nSHEu1UgpBFpizNgg1ex417iHJ1LoEsMhl7LCweXoJTZ1y4e\n\tS7xaT1Guc+SKmuCNH7sW0KSGNW+T3PI94LQOD/YxRcH63I3+F6UmTRh6nv285XmOufwz\n\tQxAGbLjhfIboqyTrNOC4DIEHqaLPDgUCnKB4sdziqAzel8p9F//dQ848V0TcUZvAJ2MG\n\tHxVEnZaLNIDSVZe76DE6H01AyEwmtLHoB+3Sr7cU8Z0M5MrJbDT2knhzq0cG9K/eDVF/\n\t7MZg=="],"X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:from:to:cc:subject:date:message-id;\n\tbh=fw8X0p6gToKeeRGr5NkWUvJoI0HB9kFBCTVDgcjL9UA=;\n\tb=rBLt8fpsJe9vutXYp3ungC7jYyRIukn/6MhzejebX77FLjwBsZ1DzTRJP9gtjLSQMH\n\tyXRo85wdytULK5qx12fGW3YtKxC/8QjkQEJ3yrEDzW4VDVgaTfRVygx2FOhZ4jHlc+6F\n\tnEnUx4IaCWSjV40mtUwhd2eB2sZaIqLrpP99GRBKNilNb/JP2ExeYyH3+2OAeAqDzNXK\n\tHQ+46HcNvlMwWMIioZuecD4BjJoOP2jYpp7S5IEyYlHwRBCdRJIvv5Y3v5T99aZrfM/g\n\tmRTY//glmvu1gwJiPHg9/19Fmqubbt8tNPy/2Ee6SxoXSQa+nIofOIAqAFkg+Rn6ty2U\n\tArgg==","X-Gm-Message-State":"AHPjjUj/2h1CNbFocPEaZWfxtSRhn3FjAklK1hu1mxuL8bMDL9zyzTSP\n\tDKY/MsDvqoWnJu8r","X-Google-Smtp-Source":"ADKCNb7oMi60woYfAZzhq20QAR65vshIB2QnauUj28RNxE3drg5ruUw5HsbYd/7AUm3Je3l3dhfBMw==","X-Received":"by 10.98.20.194 with SMTP id 185mr7757511pfu.296.1504417443282; \n\tSat, 02 Sep 2017 22:44:03 -0700 (PDT)","From":"hackpascal <hackpascal@gmail.com>","To":"lede-dev@lists.infradead.org","Date":"Sun,  3 Sep 2017 13:43:59 +0800","Message-Id":"<20170903054359.4118-1-hackpascal@gmail.com>","X-Mailer":"git-send-email 2.11.0","X-CRM114-Version":"20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 ","X-CRM114-CacheID":"sfid-20170902_224427_992972_7AB62DDD ","X-CRM114-Status":"GOOD (  21.90  )","X-Spam-Score":"-2.0 (--)","X-Spam-Report":"SpamAssassin version 3.4.1 on bombadil.infradead.org summary:\n\tContent analysis details:   (-2.0 points)\n\tpts rule name              description\n\t---- ----------------------\n\t--------------------------------------------------\n\t-0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/,\n\tno\n\ttrust [2607:f8b0:400e:c00:0:0:0:244 listed in] [list.dnswl.org]\n\t-0.0 SPF_PASS               SPF: sender matches SPF record\n\t0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail\n\tprovider (hackpascal[at]gmail.com)\n\t-1.9 BAYES_00               BODY: Bayes spam probability is 0 to 1%\n\t[score: 0.0000]\n\t-0.1 DKIM_VALID Message has at least one valid DKIM or DK signature\n\t0.1 DKIM_SIGNED            Message has a DKIM or DK signature,\n\tnot necessarily valid\n\t-0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from\n\tauthor's domain","Subject":"[LEDE-DEV] [PATCHv2 3/4] kernel/4.4: add generic spi-nand framework","X-BeenThere":"lede-dev@lists.infradead.org","X-Mailman-Version":"2.1.21","Precedence":"list","List-Id":"<lede-dev.lists.infradead.org>","List-Unsubscribe":"<http://lists.infradead.org/mailman/options/lede-dev>,\n\t<mailto:lede-dev-request@lists.infradead.org?subject=unsubscribe>","List-Archive":"<http://lists.infradead.org/pipermail/lede-dev/>","List-Post":"<mailto:lede-dev@lists.infradead.org>","List-Help":"<mailto:lede-dev-request@lists.infradead.org?subject=help>","List-Subscribe":"<http://lists.infradead.org/mailman/listinfo/lede-dev>,\n\t<mailto:lede-dev-request@lists.infradead.org?subject=subscribe>","Cc":"Weijie Gao <hackpascal@gmail.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Sender":"\"Lede-dev\" <lede-dev-bounces@lists.infradead.org>","Errors-To":"lede-dev-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org"},"content":"From: Weijie Gao <hackpascal@gmail.com>\n\nThis patch adds generic SPI-NAND framework for linux-4.4.\n\nFiles come from patches of target pistachio, but have lots of modifications\nto add full support for GD5F series.\n\nSigned-off-by: Weijie Gao <hackpascal@gmail.com>\n---\n target/linux/generic/config-4.4                    |   2 +\n .../generic/files/drivers/mtd/spi-nand/Kconfig     |  17 +\n .../generic/files/drivers/mtd/spi-nand/Makefile    |   2 +\n .../files/drivers/mtd/spi-nand/spi-nand-base.c     | 588 ++++++++++++++++\n .../files/drivers/mtd/spi-nand/spi-nand-device.c   | 761 +++++++++++++++++++++\n .../generic/files/include/linux/mtd/spi-nand.h     |  56 ++\n ...length-of-ID-before-reading-bits-per-cell.patch |  33 +\n ...-Add-JEDEC-manufacturer-ID-for-Gigadevice.patch |  35 +\n .../454-mtd-Introduce-SPI-NAND-framework.patch     |  20 +\n 9 files changed, 1514 insertions(+)\n create mode 100644 target/linux/generic/files/drivers/mtd/spi-nand/Kconfig\n create mode 100644 target/linux/generic/files/drivers/mtd/spi-nand/Makefile\n create mode 100644 target/linux/generic/files/drivers/mtd/spi-nand/spi-nand-base.c\n create mode 100644 target/linux/generic/files/drivers/mtd/spi-nand/spi-nand-device.c\n create mode 100644 target/linux/generic/files/include/linux/mtd/spi-nand.h\n create mode 100644 target/linux/generic/pending-4.4/452-mtd-nand-Check-length-of-ID-before-reading-bits-per-cell.patch\n create mode 100644 target/linux/generic/pending-4.4/453-mtd-nand-Add-JEDEC-manufacturer-ID-for-Gigadevice.patch\n create mode 100644 target/linux/generic/pending-4.4/454-mtd-Introduce-SPI-NAND-framework.patch","diff":"diff --git a/target/linux/generic/config-4.4 b/target/linux/generic/config-4.4\nindex 1c0af9597f..0fd7c1d49c 100644\n--- a/target/linux/generic/config-4.4\n+++ b/target/linux/generic/config-4.4\n@@ -2369,6 +2369,8 @@ CONFIG_MTD_ROOTFS_ROOT_DEV=y\n # CONFIG_MTD_SLRAM is not set\n # CONFIG_MTD_SM_COMMON is not set\n # CONFIG_MTD_SPINAND_MT29F is not set\n+# CONFIG_MTD_SPI_NAND is not set\n+# CONFIG_MTD_SPI_NAND_DEVICES is not set\n # CONFIG_MTD_SPI_NOR is not set\n # CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set\n CONFIG_MTD_SPLIT=y\ndiff --git a/target/linux/generic/files/drivers/mtd/spi-nand/Kconfig b/target/linux/generic/files/drivers/mtd/spi-nand/Kconfig\nnew file mode 100644\nindex 0000000000..ab6bb6c7fa\n--- /dev/null\n+++ b/target/linux/generic/files/drivers/mtd/spi-nand/Kconfig\n@@ -0,0 +1,17 @@\n+menuconfig MTD_SPI_NAND\n+\ttristate \"SPI NAND device support\"\n+\tdepends on MTD\n+\tselect MTD_NAND\n+\thelp\n+\t  This is the framework for the SPI NAND.\n+\n+if MTD_SPI_NAND\n+\n+config MTD_SPI_NAND_DEVICES\n+\ttristate \"Support for SPI NAND devices\"\n+\tdefault y\n+\tdepends on MTD_SPI_NAND\n+\thelp\n+\t  Select this option if you require support for SPI NAND devices.\n+\n+endif # MTD_SPI_NAND\ndiff --git a/target/linux/generic/files/drivers/mtd/spi-nand/Makefile b/target/linux/generic/files/drivers/mtd/spi-nand/Makefile\nnew file mode 100644\nindex 0000000000..6e460d1814\n--- /dev/null\n+++ b/target/linux/generic/files/drivers/mtd/spi-nand/Makefile\n@@ -0,0 +1,2 @@\n+obj-$(CONFIG_MTD_SPI_NAND)\t\t+= spi-nand-base.o\n+obj-$(CONFIG_MTD_SPI_NAND_DEVICES)     += spi-nand-device.o\ndiff --git a/target/linux/generic/files/drivers/mtd/spi-nand/spi-nand-base.c b/target/linux/generic/files/drivers/mtd/spi-nand/spi-nand-base.c\nnew file mode 100644\nindex 0000000000..07dad5397a\n--- /dev/null\n+++ b/target/linux/generic/files/drivers/mtd/spi-nand/spi-nand-base.c\n@@ -0,0 +1,588 @@\n+/*\n+ * Copyright (C) 2014 Imagination Technologies Ltd.\n+ *\n+ * This program is free software; you can redistribute it and/or modify\n+ * it under the terms of the GNU General Public License as published by\n+ * the Free Software Foundation; version 2 of the License.\n+ *\n+ * Notes:\n+ * 1. Erase and program operations need to call write_enable() first,\n+ *    to clear the enable bit. This bit is cleared automatically after\n+ *    the erase or program operation.\n+ *\n+ */\n+\n+#include <linux/device.h>\n+#include <linux/err.h>\n+#include <linux/errno.h>\n+#include <linux/kernel.h>\n+#include <linux/module.h>\n+#include <linux/mtd/nand.h>\n+#include <linux/mtd/mtd.h>\n+#include <linux/mtd/partitions.h>\n+#include <linux/mtd/spi-nand.h>\n+#include <linux/of.h>\n+#include <linux/slab.h>\n+\n+/* Registers common to all devices */\n+#define SPI_NAND_LOCK_REG\t\t0xa0\n+#define SPI_NAND_PROT_UNLOCK_ALL\t0x0\n+\n+#define SPI_NAND_FEATURE_REG\t\t0xb0\n+#define SPI_NAND_ECC_EN\t\t\tBIT(4)\n+#define SPI_NAND_QUAD_EN\t\tBIT(0)\n+\n+#define SPI_NAND_STATUS_REG\t\t0xc0\n+#define SPI_NAND_STATUS_REG_ECC_MASK\t0x3\n+#define SPI_NAND_STATUS_REG_ECC_SHIFT\t4\n+#define SPI_NAND_STATUS_REG_PROG_FAIL\tBIT(3)\n+#define SPI_NAND_STATUS_REG_ERASE_FAIL\tBIT(2)\n+#define SPI_NAND_STATUS_REG_WREN\tBIT(1)\n+#define SPI_NAND_STATUS_REG_BUSY\tBIT(0)\n+\n+#define SPI_NAND_CMD_BUF_LEN\t\t8\n+\n+/* Rewind and fill the buffer with 0xff */\n+static void spi_nand_clear_buffer(struct spi_nand *snand)\n+{\n+\tsnand->buf_start = 0;\n+\tmemset(snand->data_buf, 0xff, snand->buf_size);\n+}\n+\n+static int spi_nand_enable_ecc(struct spi_nand *snand)\n+{\n+\tint ret;\n+\n+\tret = snand->read_reg(snand, SPI_NAND_FEATURE_REG, snand->buf);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tsnand->buf[0] |= SPI_NAND_ECC_EN;\n+\tret = snand->write_reg(snand, SPI_NAND_FEATURE_REG, snand->buf);\n+\tif (ret)\n+\t\treturn ret;\n+\tsnand->ecc = true;\n+\n+\treturn 0;\n+}\n+\n+static int spi_nand_disable_ecc(struct spi_nand *snand)\n+{\n+\tint ret;\n+\n+\tret = snand->read_reg(snand, SPI_NAND_FEATURE_REG, snand->buf);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tsnand->buf[0] &= ~SPI_NAND_ECC_EN;\n+\tret = snand->write_reg(snand, SPI_NAND_FEATURE_REG, snand->buf);\n+\tif (ret)\n+\t\treturn ret;\n+\tsnand->ecc = false;\n+\n+\treturn 0;\n+}\n+\n+static int spi_nand_enable_quad(struct spi_nand *snand)\n+{\n+\tint ret;\n+\n+\tret = snand->read_reg(snand, SPI_NAND_FEATURE_REG, snand->buf);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tsnand->buf[0] |= SPI_NAND_QUAD_EN;\n+\tret = snand->write_reg(snand, SPI_NAND_FEATURE_REG, snand->buf);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treturn 0;\n+}\n+/*\n+ * Wait until the status register busy bit is cleared.\n+ * Returns a negatie errno on error or time out, and a non-negative status\n+ * value if the device is ready.\n+ */\n+static int spi_nand_wait_till_ready(struct spi_nand *snand)\n+{\n+\tunsigned long deadline = jiffies + msecs_to_jiffies(100);\n+\tbool timeout = false;\n+\tint ret;\n+\n+\t/*\n+\t * Perhaps we should set a different timeout for each\n+\t * operation (reset, read, write, erase).\n+\t */\n+\twhile (!timeout) {\n+\t\tif (time_after_eq(jiffies, deadline))\n+\t\t\ttimeout = true;\n+\n+\t\tret = snand->read_reg(snand, SPI_NAND_STATUS_REG, snand->buf);\n+\t\tif (ret < 0) {\n+\t\t\tdev_err(snand->dev, \"error reading status register\\n\");\n+\t\t\treturn ret;\n+\t\t} else if (!(snand->buf[0] & SPI_NAND_STATUS_REG_BUSY)) {\n+\t\t\treturn snand->buf[0];\n+\t\t}\n+\n+\t\tcond_resched();\n+\t}\n+\n+\tdev_err(snand->dev, \"operation timed out\\n\");\n+\n+\treturn -ETIMEDOUT;\n+}\n+\n+static int spi_nand_reset(struct spi_nand *snand)\n+{\n+\tint ret;\n+\n+\tret = snand->reset(snand);\n+\tif (ret < 0) {\n+\t\tdev_err(snand->dev, \"reset command failed\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\t/*\n+\t * The NAND core won't wait after a device reset, so we need\n+\t * to do that here.\n+\t */\n+\tret = spi_nand_wait_till_ready(snand);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\treturn 0;\n+}\n+\n+static int spi_nand_status(struct spi_nand *snand)\n+{\n+\tint ret, status;\n+\n+\tret = snand->read_reg(snand, SPI_NAND_STATUS_REG, snand->buf);\n+\tif (ret < 0) {\n+\t\tdev_err(snand->dev, \"error reading status register\\n\");\n+\t\treturn ret;\n+\t}\n+\tstatus = snand->buf[0];\n+\n+\t/* Convert this into standard NAND_STATUS values */\n+\tif (status & SPI_NAND_STATUS_REG_BUSY)\n+\t\tsnand->buf[0] = 0;\n+\telse\n+\t\tsnand->buf[0] = NAND_STATUS_READY;\n+\n+\tif (status & SPI_NAND_STATUS_REG_PROG_FAIL ||\n+\t    status & SPI_NAND_STATUS_REG_ERASE_FAIL)\n+\t\tsnand->buf[0] |= NAND_STATUS_FAIL;\n+\n+\t/*\n+\t * Since we unlock the entire device at initialization, unconditionally\n+\t * set the WP bit to indicate it's not protected.\n+\t */\n+\tsnand->buf[0] |= NAND_STATUS_WP;\n+\treturn 0;\n+}\n+\n+static int spi_nand_erase(struct spi_nand *snand, int page_addr)\n+{\n+\tint ret;\n+\n+\tret = snand->write_enable(snand);\n+\tif (ret < 0) {\n+\t\tdev_err(snand->dev, \"write enable command failed\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\tret = snand->block_erase(snand, page_addr);\n+\tif (ret < 0) {\n+\t\tdev_err(snand->dev, \"block erase command failed\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int spi_nand_write(struct spi_nand *snand)\n+{\n+\tint ret;\n+\n+\t/* Enable quad mode */\n+\tret = spi_nand_enable_quad(snand);\n+\tif (ret) {\n+\t\tdev_err(snand->dev, \"error %d enabling quad mode\\n\", ret);\n+\t\treturn ret;\n+\t}\n+\t/* Store the page to cache */\n+\tret = snand->store_cache(snand, 0, snand->buf_size, snand->data_buf);\n+\tif (ret < 0) {\n+\t\tdev_err(snand->dev, \"error %d storing page 0x%x to cache\\n\",\n+\t\t\tret, snand->page_addr);\n+\t\treturn ret;\n+\t}\n+\n+\tret = snand->write_enable(snand);\n+\tif (ret < 0) {\n+\t\tdev_err(snand->dev, \"write enable command failed\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\t/* Get page from the device cache into our internal buffer */\n+\tret = snand->write_page(snand, snand->page_addr);\n+\tif (ret < 0) {\n+\t\tdev_err(snand->dev, \"error %d reading page 0x%x from cache\\n\",\n+\t\t\tret, snand->page_addr);\n+\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int spi_nand_read_id(struct spi_nand *snand)\n+{\n+\tint ret;\n+\n+\tret = snand->read_id(snand, snand->data_buf);\n+\tif (ret < 0) {\n+\t\tdev_err(snand->dev, \"error %d reading ID\\n\", ret);\n+\t\treturn ret;\n+\t}\n+\treturn 0;\n+}\n+\n+static int spi_nand_read_page(struct spi_nand *snand, unsigned int page_addr,\n+\t\t\t      unsigned int page_offset, size_t length)\n+{\n+\tunsigned int corrected = 0, ecc_error = 0;\n+\tint ret;\n+\n+\t/* Load a page into the cache register */\n+\tret = snand->load_page(snand, page_addr);\n+\tif (ret < 0) {\n+\t\tdev_err(snand->dev, \"error %d loading page 0x%x to cache\\n\",\n+\t\t\tret, page_addr);\n+\t\treturn ret;\n+\t}\n+\n+\tret = spi_nand_wait_till_ready(snand);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tif (snand->ecc) {\n+\t\tsnand->get_ecc_status(ret, &corrected, &ecc_error);\n+\t\tsnand->bitflips = corrected;\n+\n+\t\t/*\n+\t\t * If there's an ECC error, print a message and notify MTD\n+\t\t * about it. Then complete the read, to load actual data on\n+\t\t * the buffer (instead of the status result).\n+\t\t */\n+\t\tif (ecc_error) {\n+\t\t\tdev_err(snand->dev,\n+\t\t\t\t\"internal ECC error reading page 0x%x\\n\",\n+\t\t\t\tpage_addr);\n+\t\t\tsnand->mtd.ecc_stats.failed++;\n+\t\t} else {\n+\t\t\tsnand->mtd.ecc_stats.corrected += corrected;\n+\t\t}\n+\t}\n+\n+\t/* Enable quad mode */\n+\tret = spi_nand_enable_quad(snand);\n+\tif (ret) {\n+\t\tdev_err(snand->dev, \"error %d enabling quad mode\\n\", ret);\n+\t\treturn ret;\n+\t}\n+\t/* Get page from the device cache into our internal buffer */\n+\tret = snand->read_cache(snand, page_offset, length, snand->data_buf);\n+\tif (ret < 0) {\n+\t\tdev_err(snand->dev, \"error %d reading page 0x%x from cache\\n\",\n+\t\t\tret, page_addr);\n+\t\treturn ret;\n+\t}\n+\treturn 0;\n+}\n+\n+static u8 spi_nand_read_byte(struct mtd_info *mtd)\n+{\n+\tstruct nand_chip *chip = mtd->priv;\n+\tstruct spi_nand *snand = chip->priv;\n+\tchar val = 0xff;\n+\n+\tif (snand->buf_start < snand->buf_size)\n+\t\tval = snand->data_buf[snand->buf_start++];\n+\treturn val;\n+}\n+\n+static void spi_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len)\n+{\n+\tstruct nand_chip *chip = mtd->priv;\n+\tstruct spi_nand *snand = chip->priv;\n+\tsize_t n = min_t(size_t, len, snand->buf_size - snand->buf_start);\n+\n+\tmemcpy(snand->data_buf + snand->buf_start, buf, n);\n+\tsnand->buf_start += n;\n+}\n+\n+static void spi_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len)\n+{\n+\tstruct nand_chip *chip = mtd->priv;\n+\tstruct spi_nand *snand = chip->priv;\n+\tsize_t n = min_t(size_t, len, snand->buf_size - snand->buf_start);\n+\n+\tmemcpy(buf, snand->data_buf + snand->buf_start, n);\n+\tsnand->buf_start += n;\n+}\n+\n+static int spi_nand_write_page_hwecc(struct mtd_info *mtd,\n+\t\tstruct nand_chip *chip, const uint8_t *buf, int oob_required,\n+\t\tint page)\n+{\n+\tchip->write_buf(mtd, buf, mtd->writesize);\n+\tchip->write_buf(mtd, chip->oob_poi, mtd->oobsize);\n+\n+\treturn 0;\n+}\n+\n+static int spi_nand_read_page_hwecc(struct mtd_info *mtd,\n+\t\tstruct nand_chip *chip, uint8_t *buf, int oob_required,\n+\t\tint page)\n+{\n+\tstruct spi_nand *snand = chip->priv;\n+\n+\tchip->read_buf(mtd, buf, mtd->writesize);\n+\tchip->read_buf(mtd, chip->oob_poi, mtd->oobsize);\n+\n+\treturn snand->bitflips;\n+}\n+\n+static int spi_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)\n+{\n+\tstruct spi_nand *snand = chip->priv;\n+\tint ret;\n+\n+\tret = spi_nand_wait_till_ready(snand);\n+\n+\tif (ret < 0) {\n+\t\treturn NAND_STATUS_FAIL;\n+\t} else if (ret & SPI_NAND_STATUS_REG_PROG_FAIL) {\n+\t\tdev_err(snand->dev, \"page program failed\\n\");\n+\t\treturn NAND_STATUS_FAIL;\n+\t} else if (ret & SPI_NAND_STATUS_REG_ERASE_FAIL) {\n+\t\tdev_err(snand->dev, \"block erase failed\\n\");\n+\t\treturn NAND_STATUS_FAIL;\n+\t}\n+\n+\treturn NAND_STATUS_READY;\n+}\n+\n+static void spi_nand_cmdfunc(struct mtd_info *mtd, unsigned int command,\n+\t\t\t     int column, int page_addr)\n+{\n+\tstruct nand_chip *chip = mtd->priv;\n+\tstruct spi_nand *snand = chip->priv;\n+\n+\t/*\n+\t * In case there's any unsupported command, let's make sure\n+\t * we don't keep garbage around in the buffer.\n+\t */\n+\tif (command != NAND_CMD_PAGEPROG) {\n+\t\tspi_nand_clear_buffer(snand);\n+\t\tsnand->page_addr = 0;\n+\t}\n+\n+\tswitch (command) {\n+\tcase NAND_CMD_READ0:\n+\t\tspi_nand_read_page(snand, page_addr, 0, mtd->writesize);\n+\t\tbreak;\n+\tcase NAND_CMD_READOOB:\n+\t\tspi_nand_disable_ecc(snand);\n+\t\tspi_nand_read_page(snand, page_addr, mtd->writesize,\n+\t\t\t\t   mtd->oobsize);\n+\t\tspi_nand_enable_ecc(snand);\n+\t\tbreak;\n+\tcase NAND_CMD_READID:\n+\t\tspi_nand_read_id(snand);\n+\t\tbreak;\n+\tcase NAND_CMD_ERASE1:\n+\t\tspi_nand_erase(snand, page_addr);\n+\t\tbreak;\n+\tcase NAND_CMD_ERASE2:\n+\t\t/* There's nothing to do here, as the erase is one-step */\n+\t\tbreak;\n+\tcase NAND_CMD_SEQIN:\n+\t\tsnand->buf_start = column;\n+\t\tsnand->page_addr = page_addr;\n+\t\tbreak;\n+\tcase NAND_CMD_PAGEPROG:\n+\t\tspi_nand_write(snand);\n+\t\tbreak;\n+\tcase NAND_CMD_STATUS:\n+\t\tspi_nand_status(snand);\n+\t\tbreak;\n+\tcase NAND_CMD_RESET:\n+\t\tspi_nand_reset(snand);\n+\t\tbreak;\n+\tdefault:\n+\t\tdev_err(&mtd->dev, \"unknown command 0x%x\\n\", command);\n+\t}\n+}\n+\n+static void spi_nand_select_chip(struct mtd_info *mtd, int chip)\n+{\n+\t/* We need this to override the default */\n+}\n+\n+static bool spi_nand_get_oob_layout(struct mtd_info *mtd, struct nand_ecclayout **ooblayout)\n+{\n+\tstruct nand_chip *chip = mtd->priv;\n+\tstruct spi_nand *snand = chip->priv;\n+\tu8 id[0x24];\t/* Maximum for GD5F */\n+\tstruct nand_ecclayout *new_ooblayout;\n+\n+\tspi_nand_clear_buffer(snand);\n+\tsnand->page_addr = 0;\n+\n+\t/* Send the command for reading device ID */\n+\tspi_nand_read_id(snand);\n+\n+\t/* Read ID bytes */\n+\tspi_nand_read_buf(mtd, id, sizeof (id));\n+\n+\t/* Get OOB layout */\n+\tnew_ooblayout = spi_nand_post_probe(id, sizeof (id));\n+\n+\tif (new_ooblayout && ooblayout)\n+\t\t*ooblayout = new_ooblayout;\n+\n+\treturn new_ooblayout != NULL;\n+}\n+\n+int spi_nand_check(struct spi_nand *snand)\n+{\n+\tif (!snand->dev)\n+\t\treturn -ENODEV;\n+\tif (!snand->read_cache)\n+\t\treturn -ENODEV;\n+\tif (!snand->load_page)\n+\t\treturn -ENODEV;\n+\tif (!snand->store_cache)\n+\t\treturn -ENODEV;\n+\tif (!snand->write_page)\n+\t\treturn -ENODEV;\n+\tif (!snand->write_reg)\n+\t\treturn -ENODEV;\n+\tif (!snand->read_reg)\n+\t\treturn -ENODEV;\n+\tif (!snand->block_erase)\n+\t\treturn -ENODEV;\n+\tif (!snand->reset)\n+\t\treturn -ENODEV;\n+\tif (!snand->write_enable)\n+\t\treturn -ENODEV;\n+\tif (!snand->write_disable)\n+\t\treturn -ENODEV;\n+\tif (!snand->get_ecc_status)\n+\t\treturn -ENODEV;\n+\treturn 0;\n+}\n+\n+int spi_nand_register(struct spi_nand *snand, struct nand_flash_dev *flash_ids)\n+{\n+\tstruct nand_chip *chip = &snand->nand_chip;\n+\tstruct mtd_info *mtd = &snand->mtd;\n+\tint ret;\n+\n+\t/* Let's check all the hooks are in-place so we don't panic later */\n+\tret = spi_nand_check(snand);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tchip->priv = snand;\n+\tchip->read_buf\t= spi_nand_read_buf;\n+\tchip->write_buf\t= spi_nand_write_buf;\n+\tchip->read_byte\t= spi_nand_read_byte;\n+\tchip->cmdfunc\t= spi_nand_cmdfunc;\n+\tchip->waitfunc\t= spi_nand_waitfunc;\n+\tchip->select_chip = spi_nand_select_chip;\n+\tchip->options |= NAND_NO_SUBPAGE_WRITE;\n+\tchip->bits_per_cell = 1;\n+\n+\tchip->ecc.read_page\t= spi_nand_read_page_hwecc;\n+\tchip->ecc.write_page\t= spi_nand_write_page_hwecc;\n+\tchip->ecc.mode\t\t= NAND_ECC_HW;\n+\n+\tif (!mtd->name)\n+\t\tmtd->name = dev_name(snand->dev);\n+\tmtd->owner = THIS_MODULE;\n+\tmtd->priv = chip;\n+\tmtd->type = MTD_NANDFLASH;\n+\tmtd->flags = MTD_CAP_NANDFLASH;\n+\n+\t/* Allocate buffer to be used to read/write the internal registers */\n+\tsnand->buf = kmalloc(SPI_NAND_CMD_BUF_LEN, GFP_KERNEL);\n+\tif (!snand->buf)\n+\t\treturn -ENOMEM;\n+\n+\t/* This is enabled at device power up but we'd better make sure */\n+\tret = spi_nand_enable_ecc(snand);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* Preallocate buffer for flash identification (NAND_CMD_READID) */\n+\tsnand->buf_size = SPI_NAND_CMD_BUF_LEN;\n+\tsnand->data_buf = kmalloc(snand->buf_size, GFP_KERNEL);\n+\n+\tret = nand_scan_ident(mtd, 1, flash_ids);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/*\n+\t * SPI NAND has on-die ECC, which means we can correct as much as\n+\t * we are required to. This must be done after identification of\n+\t * the device.\n+\t */\n+\tchip->ecc.strength = chip->ecc_strength_ds;\n+\tchip->ecc.size = chip->ecc_step_ds;\n+\n+\t/* Re-check manufacturer and device IDs to get proper OOB layout */\n+\tif (!spi_nand_get_oob_layout(mtd, &chip->ecc.layout)) {\n+\t\tdev_err(snand->dev, \"OOB layout not found\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t/*\n+\t * Unlock all the device before calling nand_scan_tail. This is needed\n+\t * in case the in-flash bad block table needs to be created.\n+\t * We could override __nand_unlock(), but since it's not currently used\n+\t * by the NAND core we call this explicitly.\n+\t */\n+\tsnand->buf[0] = SPI_NAND_PROT_UNLOCK_ALL;\n+\tret = snand->write_reg(snand, SPI_NAND_LOCK_REG, snand->buf);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* Free the buffer and allocate a good one, to fit a page plus OOB */\n+\tkfree(snand->data_buf);\n+\n+\tsnand->buf_size = mtd->writesize + mtd->oobsize;\n+\tsnand->data_buf = kmalloc(snand->buf_size, GFP_KERNEL);\n+\tif (!snand->data_buf)\n+\t\treturn -ENOMEM;\n+\n+\tret = nand_scan_tail(mtd);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treturn mtd_device_register(mtd, NULL, 0);\n+}\n+EXPORT_SYMBOL_GPL(spi_nand_register);\n+\n+void spi_nand_unregister(struct spi_nand *snand)\n+{\n+\tkfree(snand->buf);\n+\tkfree(snand->data_buf);\n+}\n+EXPORT_SYMBOL_GPL(spi_nand_unregister);\n+\n+MODULE_AUTHOR(\"Ezequiel Garcia <ezequiel.garcia@imgtec.com>\");\n+MODULE_DESCRIPTION(\"Framework for SPI NAND\");\n+MODULE_LICENSE(\"GPL v2\");\ndiff --git a/target/linux/generic/files/drivers/mtd/spi-nand/spi-nand-device.c b/target/linux/generic/files/drivers/mtd/spi-nand/spi-nand-device.c\nnew file mode 100644\nindex 0000000000..9fb793493b\n--- /dev/null\n+++ b/target/linux/generic/files/drivers/mtd/spi-nand/spi-nand-device.c\n@@ -0,0 +1,761 @@\n+/*\n+ * Copyright (C) 2014 Imagination Technologies Ltd.\n+ * Copyright (C) 2017 Weijie Gao <hackpascal@gmail.com>\n+ *\n+ * This program is free software; you can redistribute it and/or modify\n+ * it under the terms of the GNU General Public License as published by\n+ * the Free Software Foundation; version 2 of the License.\n+ *\n+ * Notes:\n+ * 1. We avoid using a stack-allocated buffer for SPI messages. Using\n+ *    a kmalloced buffer is probably better, given we shouldn't assume\n+ *    any particular usage by SPI core.\n+ */\n+\n+#include <linux/device.h>\n+#include <linux/err.h>\n+#include <linux/errno.h>\n+#include <linux/module.h>\n+#include <linux/mtd/mtd.h>\n+#include <linux/mtd/partitions.h>\n+#include <linux/mtd/spi-nand.h>\n+#include <linux/sizes.h>\n+#include <linux/spi/spi.h>\n+\n+/* SPI NAND commands */\n+#define\tSPI_NAND_WRITE_ENABLE\t\t0x06\n+#define\tSPI_NAND_WRITE_DISABLE\t\t0x04\n+#define\tSPI_NAND_GET_FEATURE\t\t0x0f\n+#define\tSPI_NAND_SET_FEATURE\t\t0x1f\n+#define\tSPI_NAND_PAGE_READ\t\t0x13\n+#define\tSPI_NAND_READ_CACHE\t\t0x03\n+#define\tSPI_NAND_FAST_READ_CACHE\t0x0b\n+#define\tSPI_NAND_READ_CACHE_X2\t\t0x3b\n+#define\tSPI_NAND_READ_CACHE_X4\t\t0x6b\n+#define\tSPI_NAND_READ_CACHE_DUAL_IO\t0xbb\n+#define\tSPI_NAND_READ_CACHE_QUAD_IO\t0xeb\n+#define\tSPI_NAND_READ_ID\t\t0x9f\n+#define\tSPI_NAND_PROGRAM_LOAD\t\t0x02\n+#define\tSPI_NAND_PROGRAM_LOAD4\t\t0x32\n+#define\tSPI_NAND_PROGRAM_EXEC\t\t0x10\n+#define\tSPI_NAND_PROGRAM_LOAD_RANDOM\t0x84\n+#define\tSPI_NAND_PROGRAM_LOAD_RANDOM4\t0xc4\n+#define\tSPI_NAND_BLOCK_ERASE\t\t0xd8\n+#define\tSPI_NAND_RESET\t\t\t0xff\n+\n+#define SPI_NAND_GD5F_READID_LEN\t0x24\n+\n+#define SPI_NAND_GD5F_ECC_MASK\t\t(BIT(0) | BIT(1) | BIT(2))\n+#define SPI_NAND_GD5F_ECC_UNCORR\t(BIT(0) | BIT(1) | BIT(2))\n+#define SPI_NAND_GD5F_ECC_SHIFT\t\t4\n+\n+/* Used for GD5FxGQ4UAYIG */\n+static struct nand_ecclayout gd25_oob_64_layout = {\n+\t.eccbytes = 16,\n+\t.eccpos = {\n+\t\t12, 13, 14, 15, 28, 29, 30, 31,\n+\t\t44, 45, 46, 47, 60, 61, 62, 63\n+\t},\n+\t/* Not including spare regions that are not ECC-ed */\n+\t.oobavail = 32,\n+\t.oobfree = {\n+\t\t{\n+\t\t\t.offset = 4,\n+\t\t\t.length = 8\n+\t\t}, {\n+\t\t\t.offset = 20,\n+\t\t\t.length = 8\n+\t\t}, {\n+\t\t\t.offset = 36,\n+\t\t\t.length = 8\n+\t\t}, {\n+\t\t\t.offset = 52,\n+\t\t\t.length = 8\n+\t\t}\n+\t}\n+};\n+\n+/* Used for GD5FxGQ4UAY with \"SNFI\" on ID addr. 0x20 */\n+static struct nand_ecclayout gd25_snfi_oob_64_layout = {\n+\t.eccbytes = 32,\n+\t.eccpos = {\n+\t\t8, 9, 10, 11, 12, 13, 14, 15,\n+\t\t24, 25, 26, 27, 28, 29, 30, 31,\n+\t\t40, 41, 42, 43, 44, 45, 46, 47,\n+\t\t56, 57, 58, 59, 60, 61, 62, 63\n+\t},\n+\t/* Not including spare regions that are not ECC-ed */\n+\t.oobavail = 32,\n+\t.oobfree = {\n+\t\t{\n+\t\t\t.offset = 4,\n+\t\t\t.length = 4\n+\t\t}, {\n+\t\t\t.offset = 20,\n+\t\t\t.length = 4\n+\t\t}, {\n+\t\t\t.offset = 36,\n+\t\t\t.length = 4\n+\t\t}, {\n+\t\t\t.offset = 52,\n+\t\t\t.length = 4\n+\t\t}\n+\t}\n+};\n+\n+static struct nand_ecclayout gd25_oob_128_layout = {\n+\t.eccbytes = 64,\n+\t.eccpos = {\n+\t\t64, 65, 66, 67, 68, 69, 70, 71,\n+\t\t72, 73, 74, 75, 76, 77, 78, 79,\n+\t\t80, 81, 82, 83, 84, 85, 86, 87,\n+\t\t88, 89, 90, 91, 92, 93, 94, 95,\n+\t\t96, 97, 98, 99, 100, 101, 102, 103,\n+\t\t104, 105, 106, 107, 108, 109, 110, 111,\n+\t\t112, 113, 114, 115, 116, 117, 118, 119,\n+\t\t120, 121, 122, 123, 124, 125, 126, 127\n+\t},\n+\t.oobavail = 63,\n+\t.oobfree = {\n+\t\t{\n+\t\t\t.offset = 1,\n+\t\t\t.length = 63,\n+\t\t}\n+\t},\n+};\n+\n+static struct nand_ecclayout gd25_oob_256_layout = {\n+\t.eccbytes = 128,\n+\t.eccpos = {\n+\t\t128, 129, 130, 131, 132, 133, 134, 135,\n+\t\t136, 137, 138, 139, 140, 141, 142, 143,\n+\t\t144, 145, 146, 147, 148, 149, 150, 151,\n+\t\t152, 153, 154, 155, 156, 157, 158, 159,\n+\t\t160, 161, 162, 163, 164, 165, 166, 167,\n+\t\t168, 169, 170, 171, 172, 173, 174, 175,\n+\t\t176, 177, 178, 179, 180, 181, 182, 183,\n+\t\t184, 185, 186, 187, 188, 189, 190, 191,\n+\t\t192, 193, 194, 195, 196, 197, 198, 199,\n+\t\t200, 201, 202, 203, 204, 205, 206, 207,\n+\t\t208, 209, 210, 211, 212, 213, 214, 215,\n+\t\t216, 217, 218, 219, 220, 221, 222, 223,\n+\t\t224, 225, 226, 227, 228, 229, 230, 231,\n+\t\t232, 233, 234, 235, 236, 237, 238, 239,\n+\t\t240, 241, 242, 243, 244, 245, 246, 247,\n+\t\t248, 249, 250, 251, 252, 253, 254, 255\n+\t},\n+\t.oobavail = 127,\n+\t.oobfree = {\n+\t\t{\n+\t\t\t.offset = 1,\n+\t\t\t.length = 127,\n+\t\t}\n+\t},\n+};\n+\n+static struct nand_flash_dev spi_nand_flash_ids[] = {\n+\t{\n+\t\t.name = \"GD5F1GQ4UA\",\n+\t\t.id = { NAND_MFR_GIGADEVICE, 0xf1 },\n+\t\t.chipsize = 128,\n+\t\t.pagesize = SZ_2K,\n+\t\t.erasesize = SZ_128K,\n+\t\t.id_len = 2,\n+\t\t.oobsize = 64,\n+\t\t.ecc.strength_ds = 8,\n+\t\t.ecc.step_ds = 512,\n+\t},\n+\t{\n+\t\t.name = \"GD5F1GQ4RA\",\n+\t\t.id = { NAND_MFR_GIGADEVICE, 0xe1 },\n+\t\t.chipsize = 128,\n+\t\t.pagesize = SZ_2K,\n+\t\t.erasesize = SZ_128K,\n+\t\t.id_len = 2,\n+\t\t.oobsize = 64,\n+\t\t.ecc.strength_ds = 8,\n+\t\t.ecc.step_ds = 512,\n+\t},\n+\t{\n+\t\t.name = \"GD5F1GQ4UB\",\n+\t\t.id = { NAND_MFR_GIGADEVICE, 0xd1 },\n+\t\t.chipsize = 128,\n+\t\t.pagesize = SZ_2K,\n+\t\t.erasesize = SZ_128K,\n+\t\t.id_len = 2,\n+\t\t.oobsize = 128,\n+\t\t.ecc.strength_ds = 8,\n+\t\t.ecc.step_ds = 512,\n+\t},\n+\t{\n+\t\t.name = \"GD5F1GQ4RB\",\n+\t\t.id = { NAND_MFR_GIGADEVICE, 0xc1 },\n+\t\t.chipsize = 128,\n+\t\t.pagesize = SZ_2K,\n+\t\t.erasesize = SZ_128K,\n+\t\t.id_len = 2,\n+\t\t.oobsize = 128,\n+\t\t.ecc.strength_ds = 8,\n+\t\t.ecc.step_ds = 512,\n+\t},\n+\t{\n+\t\t.name = \"GD5F1GQ4UC\",\n+\t\t.id = { NAND_MFR_GIGADEVICE, 0xb1 },\n+\t\t.chipsize = 128,\n+\t\t.pagesize = SZ_2K,\n+\t\t.erasesize = SZ_128K,\n+\t\t.id_len = 2,\n+\t\t.oobsize = 128,\n+\t\t.ecc.strength_ds = 8,\n+\t\t.ecc.step_ds = 512,\n+\t},\n+\t{\n+\t\t.name = \"GD5F1GQ4RC\",\n+\t\t.id = { NAND_MFR_GIGADEVICE, 0xa1 },\n+\t\t.chipsize = 128,\n+\t\t.pagesize = SZ_2K,\n+\t\t.erasesize = SZ_128K,\n+\t\t.id_len = 2,\n+\t\t.oobsize = 128,\n+\t\t.ecc.strength_ds = 8,\n+\t\t.ecc.step_ds = 512,\n+\t},\n+\t{\n+\t\t.name = \"GD5F2GQ4UA\",\n+\t\t.id = { NAND_MFR_GIGADEVICE, 0xf2 },\n+\t\t.chipsize = 256,\n+\t\t.pagesize = SZ_2K,\n+\t\t.erasesize = SZ_128K,\n+\t\t.id_len = 2,\n+\t\t.oobsize = 64,\n+\t\t.ecc.strength_ds = 8,\n+\t\t.ecc.step_ds = 512,\n+\t},\n+\t{\n+\t\t.name = \"GD5F2GQ4RA\",\n+\t\t.id = { NAND_MFR_GIGADEVICE, 0xe2 },\n+\t\t.chipsize = 256,\n+\t\t.pagesize = SZ_2K,\n+\t\t.erasesize = SZ_128K,\n+\t\t.id_len = 2,\n+\t\t.oobsize = 64,\n+\t\t.ecc.strength_ds = 8,\n+\t\t.ecc.step_ds = 512,\n+\t},\n+\t{\n+\t\t.name = \"GD5F2GQ4UB\",\n+\t\t.id = { NAND_MFR_GIGADEVICE, 0xd2 },\n+\t\t.chipsize = 256,\n+\t\t.pagesize = SZ_2K,\n+\t\t.erasesize = SZ_128K,\n+\t\t.id_len = 2,\n+\t\t.oobsize = 128,\n+\t\t.ecc.strength_ds = 8,\n+\t\t.ecc.step_ds = 512,\n+\t},\n+\t{\n+\t\t.name = \"GD5F2GQ4RB\",\n+\t\t.id = { NAND_MFR_GIGADEVICE, 0xc2 },\n+\t\t.chipsize = 256,\n+\t\t.pagesize = SZ_2K,\n+\t\t.erasesize = SZ_128K,\n+\t\t.id_len = 2,\n+\t\t.oobsize = 128,\n+\t\t.ecc.strength_ds = 8,\n+\t\t.ecc.step_ds = 512,\n+\t},\n+\t{\n+\t\t.name = \"GD5F2GQ4UC\",\n+\t\t.id = { NAND_MFR_GIGADEVICE, 0xb2 },\n+\t\t.chipsize = 256,\n+\t\t.pagesize = SZ_2K,\n+\t\t.erasesize = SZ_128K,\n+\t\t.id_len = 2,\n+\t\t.oobsize = 128,\n+\t\t.ecc.strength_ds = 8,\n+\t\t.ecc.step_ds = 512,\n+\t},\n+\t{\n+\t\t.name = \"GD5F2GQ4RC\",\n+\t\t.id = { NAND_MFR_GIGADEVICE, 0xa2 },\n+\t\t.chipsize = 256,\n+\t\t.pagesize = SZ_2K,\n+\t\t.erasesize = SZ_128K,\n+\t\t.id_len = 2,\n+\t\t.oobsize = 128,\n+\t\t.ecc.strength_ds = 8,\n+\t\t.ecc.step_ds = 512,\n+\t},\n+\t{\n+\t\t.name = \"GD5F4GQ4UA\",\n+\t\t.id = { NAND_MFR_GIGADEVICE, 0xf4 },\n+\t\t.chipsize = 512,\n+\t\t.pagesize = SZ_2K,\n+\t\t.erasesize = SZ_128K,\n+\t\t.id_len = 2,\n+\t\t.oobsize = 64,\n+\t\t.ecc.strength_ds = 8,\n+\t\t.ecc.step_ds = 512,\n+\t},\n+\t{\n+\t\t.name = \"GD5F4GQ4RA\",\n+\t\t.id = { NAND_MFR_GIGADEVICE, 0xe4 },\n+\t\t.chipsize = 512,\n+\t\t.pagesize = SZ_2K,\n+\t\t.erasesize = SZ_128K,\n+\t\t.id_len = 2,\n+\t\t.oobsize = 64,\n+\t\t.ecc.strength_ds = 8,\n+\t\t.ecc.step_ds = 512,\n+\t},\n+\t{\n+\t\t.name = \"GD5F4GQ4UB\",\n+\t\t.id = { NAND_MFR_GIGADEVICE, 0xd4 },\n+\t\t.chipsize = 512,\n+\t\t.pagesize = SZ_4K,\n+\t\t.erasesize = SZ_256K,\n+\t\t.id_len = 2,\n+\t\t.oobsize = 256,\n+\t\t.ecc.strength_ds = 8,\n+\t\t.ecc.step_ds = 512,\n+\t},\n+\t{\n+\t\t.name = \"GD5F4GQ4RB\",\n+\t\t.id = { NAND_MFR_GIGADEVICE, 0xc4 },\n+\t\t.chipsize = 512,\n+\t\t.pagesize = SZ_4K,\n+\t\t.erasesize = SZ_256K,\n+\t\t.id_len = 2,\n+\t\t.oobsize = 256,\n+\t\t.ecc.strength_ds = 8,\n+\t\t.ecc.step_ds = 512,\n+\t},\n+\t{\n+\t\t.name = \"GD5F4GQ4UC\",\n+\t\t.id = { NAND_MFR_GIGADEVICE, 0xb4 },\n+\t\t.chipsize = 512,\n+\t\t.pagesize = SZ_4K,\n+\t\t.erasesize = SZ_256K,\n+\t\t.id_len = 2,\n+\t\t.oobsize = 256,\n+\t\t.ecc.strength_ds = 8,\n+\t\t.ecc.step_ds = 512,\n+\t},\n+\t{\n+\t\t.name = \"GD5F4GQ4RC\",\n+\t\t.id = { NAND_MFR_GIGADEVICE, 0xa4 },\n+\t\t.chipsize = 512,\n+\t\t.pagesize = SZ_4K,\n+\t\t.erasesize = SZ_256K,\n+\t\t.id_len = 2,\n+\t\t.oobsize = 256,\n+\t\t.ecc.strength_ds = 8,\n+\t\t.ecc.step_ds = 512,\n+\t},\n+};\n+\n+enum spi_nand_device_variant {\n+\tSPI_NAND_GENERIC,\n+\tSPI_NAND_GD5F,\n+};\n+\n+struct spi_nand_device_cmd {\n+\n+\t/*\n+\t * Command and address. I/O errors have been observed if a\n+\t * separate spi_transfer is used for command and address,\n+\t * so keep them together.\n+\t */\n+\tu32 n_cmd;\n+\tu8 cmd[5];\n+\n+\t/* Tx data */\n+\tu32 n_tx;\n+\tu8 *tx_buf;\n+\n+\t/* Rx data */\n+\tu32 n_rx;\n+\tu8 *rx_buf;\n+\tu8 rx_nbits;\n+\tu8 tx_nbits;\n+};\n+\n+struct spi_nand_device {\n+\tstruct spi_nand\tspi_nand;\n+\tstruct spi_device *spi;\n+\n+\tstruct spi_nand_device_cmd cmd;\n+};\n+\n+static int spi_nand_send_command(struct spi_device *spi,\n+\t\t\t\t struct spi_nand_device_cmd *cmd)\n+{\n+\tstruct spi_message message;\n+\tstruct spi_transfer x[2];\n+\n+\tif (!cmd->n_cmd) {\n+\t\tdev_err(&spi->dev, \"cannot send an empty command\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (cmd->n_tx && cmd->n_rx) {\n+\t\tdev_err(&spi->dev, \"cannot send and receive data at the same time\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tspi_message_init(&message);\n+\tmemset(x, 0, sizeof(x));\n+\n+\t/* Command and address */\n+\tx[0].len = cmd->n_cmd;\n+\tx[0].tx_buf = cmd->cmd;\n+\tx[0].tx_nbits = cmd->tx_nbits;\n+\tspi_message_add_tail(&x[0], &message);\n+\n+\t/* Data to be transmitted */\n+\tif (cmd->n_tx) {\n+\t\tx[1].len = cmd->n_tx;\n+\t\tx[1].tx_buf = cmd->tx_buf;\n+\t\tx[1].tx_nbits = cmd->tx_nbits;\n+\t\tspi_message_add_tail(&x[1], &message);\n+\t}\n+\n+\t/* Data to be received */\n+\tif (cmd->n_rx) {\n+\t\tx[1].len = cmd->n_rx;\n+\t\tx[1].rx_buf = cmd->rx_buf;\n+\t\tx[1].rx_nbits = cmd->rx_nbits;\n+\t\tspi_message_add_tail(&x[1], &message);\n+\t}\n+\n+\treturn spi_sync(spi, &message);\n+}\n+\n+static int spi_nand_device_reset(struct spi_nand *snand)\n+{\n+\tstruct spi_nand_device *snand_dev = snand->priv;\n+\tstruct spi_nand_device_cmd *cmd = &snand_dev->cmd;\n+\n+\tmemset(cmd, 0, sizeof(struct spi_nand_device_cmd));\n+\tcmd->n_cmd = 1;\n+\tcmd->cmd[0] = SPI_NAND_RESET;\n+\n+\tdev_dbg(snand->dev, \"%s\\n\", __func__);\n+\n+\treturn spi_nand_send_command(snand_dev->spi, cmd);\n+}\n+\n+static int spi_nand_device_read_reg(struct spi_nand *snand, u8 opcode, u8 *buf)\n+{\n+\tstruct spi_nand_device *snand_dev = snand->priv;\n+\tstruct spi_nand_device_cmd *cmd = &snand_dev->cmd;\n+\n+\tmemset(cmd, 0, sizeof(struct spi_nand_device_cmd));\n+\tcmd->n_cmd = 2;\n+\tcmd->cmd[0] = SPI_NAND_GET_FEATURE;\n+\tcmd->cmd[1] = opcode;\n+\tcmd->n_rx = 1;\n+\tcmd->rx_buf = buf;\n+\n+\tdev_dbg(snand->dev, \"%s: reg 0%x\\n\", __func__, opcode);\n+\n+\treturn spi_nand_send_command(snand_dev->spi, cmd);\n+}\n+\n+static int spi_nand_device_write_reg(struct spi_nand *snand, u8 opcode, u8 *buf)\n+{\n+\tstruct spi_nand_device *snand_dev = snand->priv;\n+\tstruct spi_nand_device_cmd *cmd = &snand_dev->cmd;\n+\n+\tmemset(cmd, 0, sizeof(struct spi_nand_device_cmd));\n+\tcmd->n_cmd = 2;\n+\tcmd->cmd[0] = SPI_NAND_SET_FEATURE;\n+\tcmd->cmd[1] = opcode;\n+\tcmd->n_tx = 1;\n+\tcmd->tx_buf = buf;\n+\n+\tdev_dbg(snand->dev, \"%s: reg 0%x\\n\", __func__, opcode);\n+\n+\treturn spi_nand_send_command(snand_dev->spi, cmd);\n+}\n+\n+static int spi_nand_device_write_enable(struct spi_nand *snand)\n+{\n+\tstruct spi_nand_device *snand_dev = snand->priv;\n+\tstruct spi_nand_device_cmd *cmd = &snand_dev->cmd;\n+\n+\tmemset(cmd, 0, sizeof(struct spi_nand_device_cmd));\n+\tcmd->n_cmd = 1;\n+\tcmd->cmd[0] = SPI_NAND_WRITE_ENABLE;\n+\n+\tdev_dbg(snand->dev, \"%s\\n\", __func__);\n+\n+\treturn spi_nand_send_command(snand_dev->spi, cmd);\n+}\n+\n+static int spi_nand_device_write_disable(struct spi_nand *snand)\n+{\n+\tstruct spi_nand_device *snand_dev = snand->priv;\n+\tstruct spi_nand_device_cmd *cmd = &snand_dev->cmd;\n+\n+\tmemset(cmd, 0, sizeof(struct spi_nand_device_cmd));\n+\tcmd->n_cmd = 1;\n+\tcmd->cmd[0] = SPI_NAND_WRITE_DISABLE;\n+\n+\tdev_dbg(snand->dev, \"%s\\n\", __func__);\n+\n+\treturn spi_nand_send_command(snand_dev->spi, cmd);\n+}\n+\n+static int spi_nand_device_write_page(struct spi_nand *snand,\n+\t\t\t\t      unsigned int page_addr)\n+{\n+\tstruct spi_nand_device *snand_dev = snand->priv;\n+\tstruct spi_nand_device_cmd *cmd = &snand_dev->cmd;\n+\n+\tmemset(cmd, 0, sizeof(struct spi_nand_device_cmd));\n+\tcmd->n_cmd = 4;\n+\tcmd->cmd[0] = SPI_NAND_PROGRAM_EXEC;\n+\tcmd->cmd[1] = (u8)((page_addr & 0xff0000) >> 16);\n+\tcmd->cmd[2] = (u8)((page_addr & 0xff00) >> 8);\n+\tcmd->cmd[3] = (u8)(page_addr & 0xff);\n+\n+\tdev_dbg(snand->dev, \"%s: page 0x%x\\n\", __func__, page_addr);\n+\n+\treturn spi_nand_send_command(snand_dev->spi, cmd);\n+}\n+\n+static int spi_nand_device_store_cache(struct spi_nand *snand,\n+\t\t\t\t       unsigned int page_offset, size_t length,\n+\t\t\t\t       u8 *write_buf)\n+{\n+\tstruct spi_nand_device *snand_dev = snand->priv;\n+\tstruct spi_nand_device_cmd *cmd = &snand_dev->cmd;\n+\tstruct spi_device *spi = snand_dev->spi;\n+\n+\tmemset(cmd, 0, sizeof(struct spi_nand_device_cmd));\n+\tcmd->n_cmd = 3;\n+\tcmd->cmd[0] = spi->mode & SPI_TX_QUAD ? SPI_NAND_PROGRAM_LOAD4 :\n+\t\t\tSPI_NAND_PROGRAM_LOAD;\n+\tcmd->cmd[1] = (u8)((page_offset & 0xff00) >> 8);\n+\tcmd->cmd[2] = (u8)(page_offset & 0xff);\n+\tcmd->n_tx = length;\n+\tcmd->tx_buf = write_buf;\n+\tcmd->tx_nbits = spi->mode & SPI_TX_QUAD ? 4 : 1;\n+\n+\tdev_dbg(snand->dev, \"%s: offset 0x%x\\n\", __func__, page_offset);\n+\n+\treturn spi_nand_send_command(snand_dev->spi, cmd);\n+}\n+\n+static int spi_nand_device_load_page(struct spi_nand *snand,\n+\t\t\t\t     unsigned int page_addr)\n+{\n+\tstruct spi_nand_device *snand_dev = snand->priv;\n+\tstruct spi_nand_device_cmd *cmd = &snand_dev->cmd;\n+\n+\tmemset(cmd, 0, sizeof(struct spi_nand_device_cmd));\n+\tcmd->n_cmd = 4;\n+\tcmd->cmd[0] = SPI_NAND_PAGE_READ;\n+\tcmd->cmd[1] = (u8)((page_addr & 0xff0000) >> 16);\n+\tcmd->cmd[2] = (u8)((page_addr & 0xff00) >> 8);\n+\tcmd->cmd[3] = (u8)(page_addr & 0xff);\n+\n+\tdev_dbg(snand->dev, \"%s: page 0x%x\\n\", __func__, page_addr);\n+\n+\treturn spi_nand_send_command(snand_dev->spi, cmd);\n+}\n+\n+static int spi_nand_device_read_cache(struct spi_nand *snand,\n+\t\t\t\t      unsigned int page_offset, size_t length,\n+\t\t\t\t      u8 *read_buf)\n+{\n+\tstruct spi_nand_device *snand_dev = snand->priv;\n+\tstruct spi_nand_device_cmd *cmd = &snand_dev->cmd;\n+\tstruct spi_device *spi = snand_dev->spi;\n+\n+\tmemset(cmd, 0, sizeof(struct spi_nand_device_cmd));\n+\tcmd->n_cmd = 4;\n+\tcmd->cmd[0] = (spi->mode & SPI_RX_QUAD) ? SPI_NAND_READ_CACHE_X4 :\n+\t\t\t((spi->mode & SPI_RX_DUAL) ? SPI_NAND_READ_CACHE_X2 :\n+\t\t\tSPI_NAND_READ_CACHE);\n+\tcmd->cmd[1] = (u8)((page_offset & 0xff00) >> 8);\n+\tcmd->cmd[2] = (u8)(page_offset & 0xff);\n+\tcmd->cmd[3] = 0; /* dummy byte */\n+\tcmd->n_rx = length;\n+\tcmd->rx_buf = read_buf;\n+\tcmd->rx_nbits = (spi->mode & SPI_RX_QUAD) ? 4 :\n+\t\t\t((spi->mode & SPI_RX_DUAL) ? 2 : 1);\n+\n+\tdev_dbg(snand->dev, \"%s: offset 0x%x\\n\", __func__, page_offset);\n+\n+\treturn spi_nand_send_command(snand_dev->spi, cmd);\n+}\n+\n+static int spi_nand_device_block_erase(struct spi_nand *snand,\n+\t\t\t\t       unsigned int page_addr)\n+{\n+\tstruct spi_nand_device *snand_dev = snand->priv;\n+\tstruct spi_nand_device_cmd *cmd = &snand_dev->cmd;\n+\n+\tmemset(cmd, 0, sizeof(struct spi_nand_device_cmd));\n+\tcmd->n_cmd = 4;\n+\tcmd->cmd[0] = SPI_NAND_BLOCK_ERASE;\n+\tcmd->cmd[1] = (u8)((page_addr & 0xff0000) >> 16);\n+\tcmd->cmd[2] = (u8)((page_addr & 0xff00) >> 8);\n+\tcmd->cmd[3] = (u8)(page_addr & 0xff);\n+\n+\tdev_dbg(snand->dev, \"%s: block 0x%x\\n\", __func__, page_addr);\n+\n+\treturn spi_nand_send_command(snand_dev->spi, cmd);\n+}\n+\n+static int spi_nand_gd5f_read_id(struct spi_nand *snand, u8 *buf)\n+{\n+\tstruct spi_nand_device *snand_dev = snand->priv;\n+\tstruct spi_nand_device_cmd *cmd = &snand_dev->cmd;\n+\n+\tmemset(cmd, 0, sizeof(struct spi_nand_device_cmd));\n+\tcmd->n_cmd = 2;\n+\tcmd->cmd[0] = SPI_NAND_READ_ID;\n+\tcmd->cmd[1] = 0; /* dummy byte */\n+\tcmd->n_rx = SPI_NAND_GD5F_READID_LEN;\n+\tcmd->rx_buf = buf;\n+\n+\tdev_dbg(snand->dev, \"%s\\n\", __func__);\n+\n+\treturn spi_nand_send_command(snand_dev->spi, cmd);\n+}\n+\n+static void spi_nand_gd5f_ecc_status(unsigned int status,\n+\t\t\t\t     unsigned int *corrected,\n+\t\t\t\t     unsigned int *ecc_error)\n+{\n+\tunsigned int ecc_status = (status >> SPI_NAND_GD5F_ECC_SHIFT) &\n+\t\t\t\t\t     SPI_NAND_GD5F_ECC_MASK;\n+\n+\t*ecc_error = (ecc_status == SPI_NAND_GD5F_ECC_UNCORR) ? 1 : 0;\n+\tif (*ecc_error == 0)\n+\t\t*corrected = (ecc_status > 1) ? (2 + ecc_status) : 0;\n+}\n+\n+struct nand_ecclayout *spi_nand_post_probe(u8 *id, int len)\n+{\n+\tint i;\n+\tstruct nand_flash_dev *nfd = NULL;\n+\n+\tif (len < 2)\n+\t\treturn NULL;\n+\n+\tfor (i = 0; i < ARRAY_SIZE(spi_nand_flash_ids); i++) {\n+\t\tif (spi_nand_flash_ids[i].id[0] == id[0] &&\n+\t\t    spi_nand_flash_ids[i].id[1] == id[1]) {\n+\t\t\tnfd = &spi_nand_flash_ids[i];\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tif (!nfd)\n+\t\treturn NULL;\n+\n+\tswitch (id[0])\n+\t{\n+\tcase NAND_MFR_GIGADEVICE:\n+\t\tswitch (nfd->oobsize) {\n+\t\tcase 64:\n+\t\t\tif (id[0x20] == 'S' &&\n+\t\t\t    id[0x21] == 'N' &&\n+\t\t\t    id[0x22] == 'F' &&\n+\t\t\t    id[0x23] == 'I')\n+\t\t\t\treturn &gd25_snfi_oob_64_layout;\n+\t\t\telse\n+\t\t\t\treturn &gd25_oob_64_layout;\n+\t\tcase 128:\n+\t\t\treturn &gd25_oob_128_layout;\n+\t\tcase 256:\n+\t\t\treturn &gd25_oob_256_layout;\n+\t\t}\n+\t}\n+\n+\treturn NULL; \n+}\n+\n+static int spi_nand_device_probe(struct spi_device *spi)\n+{\n+\tenum spi_nand_device_variant variant;\n+\tstruct spi_nand_device *priv;\n+\tstruct spi_nand *snand;\n+\tint ret;\n+\n+\tpriv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);\n+\tif (!priv)\n+\t\treturn -ENOMEM;\n+\n+\tsnand = &priv->spi_nand;\n+\n+\tsnand->read_cache = spi_nand_device_read_cache;\n+\tsnand->load_page = spi_nand_device_load_page;\n+\tsnand->store_cache = spi_nand_device_store_cache;\n+\tsnand->write_page = spi_nand_device_write_page;\n+\tsnand->write_reg = spi_nand_device_write_reg;\n+\tsnand->read_reg = spi_nand_device_read_reg;\n+\tsnand->block_erase = spi_nand_device_block_erase;\n+\tsnand->reset = spi_nand_device_reset;\n+\tsnand->write_enable = spi_nand_device_write_enable;\n+\tsnand->write_disable = spi_nand_device_write_disable;\n+\tsnand->dev = &spi->dev;\n+\tsnand->priv = priv;\n+\n+\t/* This'll mean we won't need to specify any specific compatible string\n+\t * for a given device, and instead just support spi-nand.\n+\t */\n+\tvariant = spi_get_device_id(spi)->driver_data;\n+\tswitch (variant) {\n+\tcase SPI_NAND_GD5F:\n+\t\tsnand->read_id = spi_nand_gd5f_read_id;\n+\t\tsnand->get_ecc_status = spi_nand_gd5f_ecc_status;\n+\t\tbreak;\n+\tdefault:\n+\t\tdev_err(snand->dev, \"unknown device\\n\");\n+\t\treturn -ENODEV;\n+\t}\n+\n+\tspi_set_drvdata(spi, snand);\n+\tpriv->spi = spi;\n+\n+\tret = spi_nand_register(snand, spi_nand_flash_ids);\n+\tif (ret)\n+\t\treturn ret;\n+\treturn 0;\n+}\n+\n+static int spi_nand_device_remove(struct spi_device *spi)\n+{\n+\tstruct spi_nand *snand = spi_get_drvdata(spi);\n+\n+\tspi_nand_unregister(snand);\n+\n+\treturn 0;\n+}\n+\n+const struct spi_device_id spi_nand_id_table[] = {\n+\t{ \"spi-nand\", SPI_NAND_GENERIC },\n+\t{ \"gd5f\", SPI_NAND_GD5F },\n+\t{ },\n+};\n+MODULE_DEVICE_TABLE(spi, spi_nand_id_table);\n+\n+static struct spi_driver spi_nand_device_driver = {\n+\t.driver = {\n+\t\t.name\t= \"spi_nand_device\",\n+\t\t.owner\t= THIS_MODULE,\n+\t},\n+\t.id_table = spi_nand_id_table,\n+\t.probe\t= spi_nand_device_probe,\n+\t.remove\t= spi_nand_device_remove,\n+};\n+module_spi_driver(spi_nand_device_driver);\n+\n+MODULE_AUTHOR(\"Ezequiel Garcia <ezequiel.garcia@imgtec.com>\");\n+MODULE_DESCRIPTION(\"SPI NAND device support\");\n+MODULE_LICENSE(\"GPL v2\");\ndiff --git a/target/linux/generic/files/include/linux/mtd/spi-nand.h b/target/linux/generic/files/include/linux/mtd/spi-nand.h\nnew file mode 100644\nindex 0000000000..5fcc98e7bb\n--- /dev/null\n+++ b/target/linux/generic/files/include/linux/mtd/spi-nand.h\n@@ -0,0 +1,56 @@\n+/*\n+ * Copyright (C) 2014 Imagination Technologies Ltd.\n+ *\n+ * This program is free software; you can redistribute it and/or modify\n+ * it under the terms of the GNU General Public License as published by\n+ * the Free Software Foundation; version 2 of the License.\n+ */\n+\n+#ifndef __LINUX_MTD_SPI_NAND_H\n+#define __LINUX_MTD_SPI_NAND_H\n+\n+#include <linux/mtd/mtd.h>\n+#include <linux/mtd/nand.h>\n+\n+struct spi_nand {\n+\tstruct nand_chip\tnand_chip;\n+\tstruct mtd_info\t\tmtd;\n+\tstruct device\t\t*dev;\n+\tconst char\t\t*name;\n+\n+\tu8\t\t\t*buf, *data_buf;\n+\tsize_t\t\t\tbuf_size;\n+\toff_t\t\t\tbuf_start;\n+\tunsigned int\t\tpage_addr;\n+\tunsigned int\t\tbitflips;\n+\tbool\t\t\tecc;\n+\n+\tint (*reset)(struct spi_nand *snand);\n+\tint (*read_id)(struct spi_nand *snand, u8 *buf);\n+\n+\tint (*write_disable)(struct spi_nand *snand);\n+\tint (*write_enable)(struct spi_nand *snand);\n+\n+\tint (*read_reg)(struct spi_nand *snand, u8 opcode, u8 *buf);\n+\tint (*write_reg)(struct spi_nand *snand, u8 opcode, u8 *buf);\n+\tvoid (*get_ecc_status)(unsigned int status,\n+\t\t\t       unsigned int *corrected,\n+\t\t\t       unsigned int *ecc_errors);\n+\n+\tint (*store_cache)(struct spi_nand *snand, unsigned int page_offset,\n+\t\t\t   size_t length, u8 *write_buf);\n+\tint (*write_page)(struct spi_nand *snand, unsigned int page_addr);\n+\tint (*load_page)(struct spi_nand *snand, unsigned int page_addr);\n+\tint (*read_cache)(struct spi_nand *snand, unsigned int page_offset,\n+\t\t\t  size_t length, u8 *read_buf);\n+\tint (*block_erase)(struct spi_nand *snand, unsigned int page_addr);\n+\n+\tvoid *priv;\n+};\n+\n+struct nand_ecclayout *spi_nand_post_probe(u8 *id, int len);\n+\n+int spi_nand_register(struct spi_nand *snand, struct nand_flash_dev *flash_ids);\n+void spi_nand_unregister(struct spi_nand *snand);\n+\n+#endif\ndiff --git a/target/linux/generic/pending-4.4/452-mtd-nand-Check-length-of-ID-before-reading-bits-per-cell.patch b/target/linux/generic/pending-4.4/452-mtd-nand-Check-length-of-ID-before-reading-bits-per-cell.patch\nnew file mode 100644\nindex 0000000000..60da4d6459\n--- /dev/null\n+++ b/target/linux/generic/pending-4.4/452-mtd-nand-Check-length-of-ID-before-reading-bits-per-cell.patch\n@@ -0,0 +1,33 @@\n+From 42ebff638003be18fab503b37de4ad7853244e95 Mon Sep 17 00:00:00 2001\n+From: Ezequiel Garcia <ezequiel.garcia@imgtec.com>\n+Date: Sat, 25 Feb 2017 15:58:22 +0000\n+Subject: mtd: nand: Check length of ID before reading bits per cell\n+\n+The table-based NAND identification currently reads the number\n+of bits per cell from the 3rd byte of the extended ID. This is done\n+for the so-called 'full ID' devices; i.e. devices that have a known\n+length ID.\n+\n+However, if the ID length is shorter than three, there's no 3rd byte,\n+and so it's wrong to read the bits per cell from there. Fix this by\n+adding a check for the ID length.\n+\n+(picked from http://lists.infradead.org/pipermail/linux-mtd/2014-December/056764.html)\n+\n+Signed-off-by: Ezequiel Garcia <ezequiel.garcia@imgtec.com>\n+---\n+ drivers/mtd/nand/nand_base.c | 3 ++-\n+ 1 file changed, 2 insertions(+), 1 deletion(-)\n+\n+--- a/drivers/mtd/nand/nand_base.c\n++++ b/drivers/mtd/nand/nand_base.c\n+@@ -3758,7 +3758,8 @@ static bool find_full_id_nand(struct mtd\n+ \t\tmtd->erasesize = type->erasesize;\n+ \t\tmtd->oobsize = type->oobsize;\n+ \n+-\t\tchip->bits_per_cell = nand_get_bits_per_cell(id_data[2]);\n++\t\tif (type->id_len > 2)\n++\t\t\tchip->bits_per_cell = nand_get_bits_per_cell(id_data[2]);\n+ \t\tchip->chipsize = (uint64_t)type->chipsize << 20;\n+ \t\tchip->options |= type->options;\n+ \t\tchip->ecc_strength_ds = NAND_ECC_STRENGTH(type);\ndiff --git a/target/linux/generic/pending-4.4/453-mtd-nand-Add-JEDEC-manufacturer-ID-for-Gigadevice.patch b/target/linux/generic/pending-4.4/453-mtd-nand-Add-JEDEC-manufacturer-ID-for-Gigadevice.patch\nnew file mode 100644\nindex 0000000000..70b311be70\n--- /dev/null\n+++ b/target/linux/generic/pending-4.4/453-mtd-nand-Add-JEDEC-manufacturer-ID-for-Gigadevice.patch\n@@ -0,0 +1,35 @@\n+From a4bc33b205fd9b1db862f1e45173dba57b0fa57f Mon Sep 17 00:00:00 2001\n+From: Ezequiel Garcia <ezequiel.garcia@imgtec.com>\n+Date: Sat, 25 Feb 2017 15:43:09 +0000\n+Subject: mtd: nand: Add JEDEC manufacturer ID for Gigadevice\n+\n+This commit adds Gigadevice to the list of manufacturer ID and name strings.\n+\n+(picked from http://lists.infradead.org/pipermail/linux-mtd/2014-December/056765.html)\n+\n+Signed-off-by: Ezequiel Garcia <ezequiel.garcia@imgtec.com>\n+---\n+ drivers/mtd/nand/nand_ids.c | 1 +\n+ include/linux/mtd/nand.h    | 1 +\n+ 2 files changed, 2 insertions(+)\n+\n+--- a/drivers/mtd/nand/nand_ids.c\n++++ b/drivers/mtd/nand/nand_ids.c\n+@@ -181,6 +181,7 @@ struct nand_manufacturers nand_manuf_ids\n+ \t{NAND_MFR_SANDISK, \"SanDisk\"},\n+ \t{NAND_MFR_INTEL, \"Intel\"},\n+ \t{NAND_MFR_ATO, \"ATO\"},\n++\t{NAND_MFR_GIGADEVICE, \"Gigadevice\"},\n+ \t{0x0, \"Unknown\"}\n+ };\n+ \n+--- a/include/linux/mtd/nand.h\n++++ b/include/linux/mtd/nand.h\n+@@ -736,6 +736,7 @@ static inline void nand_set_controller_d\n+ #define NAND_MFR_SANDISK\t0x45\n+ #define NAND_MFR_INTEL\t\t0x89\n+ #define NAND_MFR_ATO\t\t0x9b\n++#define NAND_MFR_GIGADEVICE\t0xc8\n+ \n+ /* The maximum expected count of bytes in the NAND ID sequence */\n+ #define NAND_MAX_ID_LEN 8\ndiff --git a/target/linux/generic/pending-4.4/454-mtd-Introduce-SPI-NAND-framework.patch b/target/linux/generic/pending-4.4/454-mtd-Introduce-SPI-NAND-framework.patch\nnew file mode 100644\nindex 0000000000..18c703026b\n--- /dev/null\n+++ b/target/linux/generic/pending-4.4/454-mtd-Introduce-SPI-NAND-framework.patch\n@@ -0,0 +1,20 @@\n+--- a/drivers/mtd/Kconfig\n++++ b/drivers/mtd/Kconfig\n+@@ -369,6 +369,8 @@ source \"drivers/mtd/onenand/Kconfig\"\n+ \n+ source \"drivers/mtd/lpddr/Kconfig\"\n+ \n++source \"drivers/mtd/spi-nand/Kconfig\"\n++\n+ source \"drivers/mtd/spi-nor/Kconfig\"\n+ \n+ source \"drivers/mtd/ubi/Kconfig\"\n+--- a/drivers/mtd/Makefile\n++++ b/drivers/mtd/Makefile\n+@@ -35,5 +35,6 @@ inftl-objs\t\t:= inftlcore.o inftlmount.o\n+ \n+ obj-y\t\t+= chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/\n+ \n++obj-$(CONFIG_MTD_SPI_NAND)\t+= spi-nand/\n+ obj-$(CONFIG_MTD_SPI_NOR)\t+= spi-nor/\n+ obj-$(CONFIG_MTD_UBI)\t\t+= ubi/\n","prefixes":["LEDE-DEV","PATCHv2","3/4"]}