diff options
Diffstat (limited to 'target/linux/pistachio/patches-4.9/414-mtd-spi-nand-Support-Gigadevice-GD5F.patch')
-rw-r--r-- | target/linux/pistachio/patches-4.9/414-mtd-spi-nand-Support-Gigadevice-GD5F.patch | 524 |
1 files changed, 0 insertions, 524 deletions
diff --git a/target/linux/pistachio/patches-4.9/414-mtd-spi-nand-Support-Gigadevice-GD5F.patch b/target/linux/pistachio/patches-4.9/414-mtd-spi-nand-Support-Gigadevice-GD5F.patch deleted file mode 100644 index 1f1461061c..0000000000 --- a/target/linux/pistachio/patches-4.9/414-mtd-spi-nand-Support-Gigadevice-GD5F.patch +++ /dev/null @@ -1,524 +0,0 @@ -From 7723e59d483a883578115a73eb87eb7fff0ff724 Mon Sep 17 00:00:00 2001 -From: Ezequiel Garcia <ezequiel.garcia@imgtec.com> -Date: Tue, 28 Feb 2017 10:37:24 +0000 -Subject: mtd: spi-nand: Support Gigadevice GD5F - -This commit uses the recently introduced SPI NAND framework to support -the Gigadevice GD5F serial NAND device. - -The current support includes: - - * Page read and page program operations (using on-die ECC) - * Page out-of-band read - * Erase - * Reset - * Device status retrieval - * Device ID retrieval - -(based on http://lists.infradead.org/pipermail/linux-mtd/2014-December/056769.html) - -Signed-off-by: Ezequiel Garcia <ezequiel.garcia@imgtec.com> -Signed-off-by: Ian Pozella <Ian.Pozella@imgtec.com> ---- - drivers/mtd/spi-nand/Kconfig | 10 + - drivers/mtd/spi-nand/Makefile | 1 + - drivers/mtd/spi-nand/spi-nand-device.c | 472 +++++++++++++++++++++++++++++++++ - 3 files changed, 483 insertions(+) - create mode 100644 drivers/mtd/spi-nand/spi-nand-device.c - ---- a/drivers/mtd/spi-nand/Kconfig -+++ b/drivers/mtd/spi-nand/Kconfig -@@ -5,3 +5,13 @@ menuconfig MTD_SPI_NAND - help - This is the framework for the SPI NAND. - -+if MTD_SPI_NAND -+ -+config MTD_SPI_NAND_DEVICES -+ tristate "Support for SPI NAND devices" -+ default y -+ depends on MTD_SPI_NAND -+ help -+ Select this option if you require support for SPI NAND devices. -+ -+endif # MTD_SPI_NAND ---- a/drivers/mtd/spi-nand/Makefile -+++ b/drivers/mtd/spi-nand/Makefile -@@ -1 +1,2 @@ - obj-$(CONFIG_MTD_SPI_NAND) += spi-nand-base.o -+obj-$(CONFIG_MTD_SPI_NAND_DEVICES) += spi-nand-device.o ---- /dev/null -+++ b/drivers/mtd/spi-nand/spi-nand-device.c -@@ -0,0 +1,472 @@ -+/* -+ * 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. We avoid using a stack-allocated buffer for SPI messages. Using -+ * a kmalloced buffer is probably better, given we shouldn't assume -+ * any particular usage by SPI core. -+ */ -+ -+#include <linux/device.h> -+#include <linux/err.h> -+#include <linux/errno.h> -+#include <linux/module.h> -+#include <linux/mtd/mtd.h> -+#include <linux/mtd/partitions.h> -+#include <linux/mtd/spi-nand.h> -+#include <linux/sizes.h> -+#include <linux/spi/spi.h> -+ -+/* SPI NAND commands */ -+#define SPI_NAND_WRITE_ENABLE 0x06 -+#define SPI_NAND_WRITE_DISABLE 0x04 -+#define SPI_NAND_GET_FEATURE 0x0f -+#define SPI_NAND_SET_FEATURE 0x1f -+#define SPI_NAND_PAGE_READ 0x13 -+#define SPI_NAND_READ_CACHE 0x03 -+#define SPI_NAND_FAST_READ_CACHE 0x0b -+#define SPI_NAND_READ_CACHE_X2 0x3b -+#define SPI_NAND_READ_CACHE_X4 0x6b -+#define SPI_NAND_READ_CACHE_DUAL_IO 0xbb -+#define SPI_NAND_READ_CACHE_QUAD_IO 0xeb -+#define SPI_NAND_READ_ID 0x9f -+#define SPI_NAND_PROGRAM_LOAD 0x02 -+#define SPI_NAND_PROGRAM_LOAD4 0x32 -+#define SPI_NAND_PROGRAM_EXEC 0x10 -+#define SPI_NAND_PROGRAM_LOAD_RANDOM 0x84 -+#define SPI_NAND_PROGRAM_LOAD_RANDOM4 0xc4 -+#define SPI_NAND_BLOCK_ERASE 0xd8 -+#define SPI_NAND_RESET 0xff -+ -+#define SPI_NAND_GD5F_READID_LEN 2 -+ -+#define SPI_NAND_GD5F_ECC_MASK (BIT(0) | BIT(1) | BIT(2)) -+#define SPI_NAND_GD5F_ECC_UNCORR (BIT(0) | BIT(1) | BIT(2)) -+#define SPI_NAND_GD5F_ECC_SHIFT 4 -+ -+static int spi_nand_gd5f_ooblayout_256_ecc(struct mtd_info *mtd, int section, -+ struct mtd_oob_region *oobregion) -+{ -+ if (section) -+ return -ERANGE; -+ -+ oobregion->offset = 128; -+ oobregion->length = 128; -+ -+ return 0; -+} -+ -+static int spi_nand_gd5f_ooblayout_256_free(struct mtd_info *mtd, int section, -+ struct mtd_oob_region *oobregion) -+{ -+ if (section) -+ return -ERANGE; -+ -+ oobregion->offset = 1; -+ oobregion->length = 127; -+ -+ return 0; -+} -+ -+static const struct mtd_ooblayout_ops spi_nand_gd5f_oob_256_ops = { -+ .ecc = spi_nand_gd5f_ooblayout_256_ecc, -+ .free = spi_nand_gd5f_ooblayout_256_free, -+}; -+ -+static struct nand_flash_dev spi_nand_flash_ids[] = { -+ { -+ .name = "SPI NAND 512MiB 3,3V", -+ .id = { NAND_MFR_GIGADEVICE, 0xb4 }, -+ .chipsize = 512, -+ .pagesize = SZ_4K, -+ .erasesize = SZ_256K, -+ .id_len = 2, -+ .oobsize = 256, -+ .ecc.strength_ds = 8, -+ .ecc.step_ds = 512, -+ }, -+ { -+ .name = "SPI NAND 512MiB 1,8V", -+ .id = { NAND_MFR_GIGADEVICE, 0xa4 }, -+ .chipsize = 512, -+ .pagesize = SZ_4K, -+ .erasesize = SZ_256K, -+ .id_len = 2, -+ .oobsize = 256, -+ .ecc.strength_ds = 8, -+ .ecc.step_ds = 512, -+ }, -+}; -+ -+enum spi_nand_device_variant { -+ SPI_NAND_GENERIC, -+ SPI_NAND_GD5F, -+}; -+ -+struct spi_nand_device_cmd { -+ -+ /* -+ * Command and address. I/O errors have been observed if a -+ * separate spi_transfer is used for command and address, -+ * so keep them together. -+ */ -+ u32 n_cmd; -+ u8 cmd[5]; -+ -+ /* Tx data */ -+ u32 n_tx; -+ u8 *tx_buf; -+ -+ /* Rx data */ -+ u32 n_rx; -+ u8 *rx_buf; -+ u8 rx_nbits; -+ u8 tx_nbits; -+}; -+ -+struct spi_nand_device { -+ struct spi_nand spi_nand; -+ struct spi_device *spi; -+ -+ struct spi_nand_device_cmd cmd; -+}; -+ -+static int spi_nand_send_command(struct spi_device *spi, -+ struct spi_nand_device_cmd *cmd) -+{ -+ struct spi_message message; -+ struct spi_transfer x[2]; -+ -+ if (!cmd->n_cmd) { -+ dev_err(&spi->dev, "cannot send an empty command\n"); -+ return -EINVAL; -+ } -+ -+ if (cmd->n_tx && cmd->n_rx) { -+ dev_err(&spi->dev, "cannot send and receive data at the same time\n"); -+ return -EINVAL; -+ } -+ -+ spi_message_init(&message); -+ memset(x, 0, sizeof(x)); -+ -+ /* Command and address */ -+ x[0].len = cmd->n_cmd; -+ x[0].tx_buf = cmd->cmd; -+ x[0].tx_nbits = cmd->tx_nbits; -+ spi_message_add_tail(&x[0], &message); -+ -+ /* Data to be transmitted */ -+ if (cmd->n_tx) { -+ x[1].len = cmd->n_tx; -+ x[1].tx_buf = cmd->tx_buf; -+ x[1].tx_nbits = cmd->tx_nbits; -+ spi_message_add_tail(&x[1], &message); -+ } -+ -+ /* Data to be received */ -+ if (cmd->n_rx) { -+ x[1].len = cmd->n_rx; -+ x[1].rx_buf = cmd->rx_buf; -+ x[1].rx_nbits = cmd->rx_nbits; -+ spi_message_add_tail(&x[1], &message); -+ } -+ -+ return spi_sync(spi, &message); -+} -+ -+static int spi_nand_device_reset(struct spi_nand *snand) -+{ -+ struct spi_nand_device *snand_dev = snand->priv; -+ struct spi_nand_device_cmd *cmd = &snand_dev->cmd; -+ -+ memset(cmd, 0, sizeof(struct spi_nand_device_cmd)); -+ cmd->n_cmd = 1; -+ cmd->cmd[0] = SPI_NAND_RESET; -+ -+ dev_dbg(snand->dev, "%s\n", __func__); -+ -+ return spi_nand_send_command(snand_dev->spi, cmd); -+} -+ -+static int spi_nand_device_read_reg(struct spi_nand *snand, u8 opcode, u8 *buf) -+{ -+ struct spi_nand_device *snand_dev = snand->priv; -+ struct spi_nand_device_cmd *cmd = &snand_dev->cmd; -+ -+ memset(cmd, 0, sizeof(struct spi_nand_device_cmd)); -+ cmd->n_cmd = 2; -+ cmd->cmd[0] = SPI_NAND_GET_FEATURE; -+ cmd->cmd[1] = opcode; -+ cmd->n_rx = 1; -+ cmd->rx_buf = buf; -+ -+ dev_dbg(snand->dev, "%s: reg 0%x\n", __func__, opcode); -+ -+ return spi_nand_send_command(snand_dev->spi, cmd); -+} -+ -+static int spi_nand_device_write_reg(struct spi_nand *snand, u8 opcode, u8 *buf) -+{ -+ struct spi_nand_device *snand_dev = snand->priv; -+ struct spi_nand_device_cmd *cmd = &snand_dev->cmd; -+ -+ memset(cmd, 0, sizeof(struct spi_nand_device_cmd)); -+ cmd->n_cmd = 2; -+ cmd->cmd[0] = SPI_NAND_SET_FEATURE; -+ cmd->cmd[1] = opcode; -+ cmd->n_tx = 1; -+ cmd->tx_buf = buf; -+ -+ dev_dbg(snand->dev, "%s: reg 0%x\n", __func__, opcode); -+ -+ return spi_nand_send_command(snand_dev->spi, cmd); -+} -+ -+static int spi_nand_device_write_enable(struct spi_nand *snand) -+{ -+ struct spi_nand_device *snand_dev = snand->priv; -+ struct spi_nand_device_cmd *cmd = &snand_dev->cmd; -+ -+ memset(cmd, 0, sizeof(struct spi_nand_device_cmd)); -+ cmd->n_cmd = 1; -+ cmd->cmd[0] = SPI_NAND_WRITE_ENABLE; -+ -+ dev_dbg(snand->dev, "%s\n", __func__); -+ -+ return spi_nand_send_command(snand_dev->spi, cmd); -+} -+ -+static int spi_nand_device_write_disable(struct spi_nand *snand) -+{ -+ struct spi_nand_device *snand_dev = snand->priv; -+ struct spi_nand_device_cmd *cmd = &snand_dev->cmd; -+ -+ memset(cmd, 0, sizeof(struct spi_nand_device_cmd)); -+ cmd->n_cmd = 1; -+ cmd->cmd[0] = SPI_NAND_WRITE_DISABLE; -+ -+ dev_dbg(snand->dev, "%s\n", __func__); -+ -+ return spi_nand_send_command(snand_dev->spi, cmd); -+} -+ -+static int spi_nand_device_write_page(struct spi_nand *snand, -+ unsigned int page_addr) -+{ -+ struct spi_nand_device *snand_dev = snand->priv; -+ struct spi_nand_device_cmd *cmd = &snand_dev->cmd; -+ -+ memset(cmd, 0, sizeof(struct spi_nand_device_cmd)); -+ cmd->n_cmd = 4; -+ cmd->cmd[0] = SPI_NAND_PROGRAM_EXEC; -+ cmd->cmd[1] = (u8)((page_addr & 0xff0000) >> 16); -+ cmd->cmd[2] = (u8)((page_addr & 0xff00) >> 8); -+ cmd->cmd[3] = (u8)(page_addr & 0xff); -+ -+ dev_dbg(snand->dev, "%s: page 0x%x\n", __func__, page_addr); -+ -+ return spi_nand_send_command(snand_dev->spi, cmd); -+} -+ -+static int spi_nand_device_store_cache(struct spi_nand *snand, -+ unsigned int page_offset, size_t length, -+ u8 *write_buf) -+{ -+ struct spi_nand_device *snand_dev = snand->priv; -+ struct spi_nand_device_cmd *cmd = &snand_dev->cmd; -+ struct spi_device *spi = snand_dev->spi; -+ -+ memset(cmd, 0, sizeof(struct spi_nand_device_cmd)); -+ cmd->n_cmd = 3; -+ cmd->cmd[0] = spi->mode & SPI_TX_QUAD ? SPI_NAND_PROGRAM_LOAD4 : -+ SPI_NAND_PROGRAM_LOAD; -+ cmd->cmd[1] = (u8)((page_offset & 0xff00) >> 8); -+ cmd->cmd[2] = (u8)(page_offset & 0xff); -+ cmd->n_tx = length; -+ cmd->tx_buf = write_buf; -+ cmd->tx_nbits = spi->mode & SPI_TX_QUAD ? 4 : 1; -+ -+ dev_dbg(snand->dev, "%s: offset 0x%x\n", __func__, page_offset); -+ -+ return spi_nand_send_command(snand_dev->spi, cmd); -+} -+ -+static int spi_nand_device_load_page(struct spi_nand *snand, -+ unsigned int page_addr) -+{ -+ struct spi_nand_device *snand_dev = snand->priv; -+ struct spi_nand_device_cmd *cmd = &snand_dev->cmd; -+ -+ memset(cmd, 0, sizeof(struct spi_nand_device_cmd)); -+ cmd->n_cmd = 4; -+ cmd->cmd[0] = SPI_NAND_PAGE_READ; -+ cmd->cmd[1] = (u8)((page_addr & 0xff0000) >> 16); -+ cmd->cmd[2] = (u8)((page_addr & 0xff00) >> 8); -+ cmd->cmd[3] = (u8)(page_addr & 0xff); -+ -+ dev_dbg(snand->dev, "%s: page 0x%x\n", __func__, page_addr); -+ -+ return spi_nand_send_command(snand_dev->spi, cmd); -+} -+ -+static int spi_nand_device_read_cache(struct spi_nand *snand, -+ unsigned int page_offset, size_t length, -+ u8 *read_buf) -+{ -+ struct spi_nand_device *snand_dev = snand->priv; -+ struct spi_nand_device_cmd *cmd = &snand_dev->cmd; -+ struct spi_device *spi = snand_dev->spi; -+ -+ memset(cmd, 0, sizeof(struct spi_nand_device_cmd)); -+ if ((spi->mode & SPI_RX_DUAL) || (spi->mode & SPI_RX_QUAD)) -+ cmd->n_cmd = 5; -+ else -+ cmd->n_cmd = 4; -+ cmd->cmd[0] = (spi->mode & SPI_RX_QUAD) ? SPI_NAND_READ_CACHE_X4 : -+ ((spi->mode & SPI_RX_DUAL) ? SPI_NAND_READ_CACHE_X2 : -+ SPI_NAND_READ_CACHE); -+ cmd->cmd[1] = 0; /* dummy byte */ -+ cmd->cmd[2] = (u8)((page_offset & 0xff00) >> 8); -+ cmd->cmd[3] = (u8)(page_offset & 0xff); -+ cmd->cmd[4] = 0; /* dummy byte */ -+ cmd->n_rx = length; -+ cmd->rx_buf = read_buf; -+ cmd->rx_nbits = (spi->mode & SPI_RX_QUAD) ? 4 : -+ ((spi->mode & SPI_RX_DUAL) ? 2 : 1); -+ -+ dev_dbg(snand->dev, "%s: offset 0x%x\n", __func__, page_offset); -+ -+ return spi_nand_send_command(snand_dev->spi, cmd); -+} -+ -+static int spi_nand_device_block_erase(struct spi_nand *snand, -+ unsigned int page_addr) -+{ -+ struct spi_nand_device *snand_dev = snand->priv; -+ struct spi_nand_device_cmd *cmd = &snand_dev->cmd; -+ -+ memset(cmd, 0, sizeof(struct spi_nand_device_cmd)); -+ cmd->n_cmd = 4; -+ cmd->cmd[0] = SPI_NAND_BLOCK_ERASE; -+ cmd->cmd[1] = (u8)((page_addr & 0xff0000) >> 16); -+ cmd->cmd[2] = (u8)((page_addr & 0xff00) >> 8); -+ cmd->cmd[3] = (u8)(page_addr & 0xff); -+ -+ dev_dbg(snand->dev, "%s: block 0x%x\n", __func__, page_addr); -+ -+ return spi_nand_send_command(snand_dev->spi, cmd); -+} -+ -+static int spi_nand_gd5f_read_id(struct spi_nand *snand, u8 *buf) -+{ -+ struct spi_nand_device *snand_dev = snand->priv; -+ struct spi_nand_device_cmd *cmd = &snand_dev->cmd; -+ -+ memset(cmd, 0, sizeof(struct spi_nand_device_cmd)); -+ cmd->n_cmd = 1; -+ cmd->cmd[0] = SPI_NAND_READ_ID; -+ cmd->n_rx = SPI_NAND_GD5F_READID_LEN; -+ cmd->rx_buf = buf; -+ -+ dev_dbg(snand->dev, "%s\n", __func__); -+ -+ return spi_nand_send_command(snand_dev->spi, cmd); -+} -+ -+static void spi_nand_gd5f_ecc_status(unsigned int status, -+ unsigned int *corrected, -+ unsigned int *ecc_error) -+{ -+ unsigned int ecc_status = (status >> SPI_NAND_GD5F_ECC_SHIFT) & -+ SPI_NAND_GD5F_ECC_MASK; -+ -+ *ecc_error = (ecc_status == SPI_NAND_GD5F_ECC_UNCORR) ? 1 : 0; -+ if (*ecc_error == 0) -+ *corrected = (ecc_status > 1) ? (2 + ecc_status) : 0; -+} -+ -+static int spi_nand_device_probe(struct spi_device *spi) -+{ -+ enum spi_nand_device_variant variant; -+ struct spi_nand_device *priv; -+ struct spi_nand *snand; -+ int ret; -+ -+ priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ snand = &priv->spi_nand; -+ -+ snand->read_cache = spi_nand_device_read_cache; -+ snand->load_page = spi_nand_device_load_page; -+ snand->store_cache = spi_nand_device_store_cache; -+ snand->write_page = spi_nand_device_write_page; -+ snand->write_reg = spi_nand_device_write_reg; -+ snand->read_reg = spi_nand_device_read_reg; -+ snand->block_erase = spi_nand_device_block_erase; -+ snand->reset = spi_nand_device_reset; -+ snand->write_enable = spi_nand_device_write_enable; -+ snand->write_disable = spi_nand_device_write_disable; -+ snand->dev = &spi->dev; -+ snand->priv = priv; -+ -+ /* This'll mean we won't need to specify any specific compatible string -+ * for a given device, and instead just support spi-nand. -+ */ -+ variant = spi_get_device_id(spi)->driver_data; -+ switch (variant) { -+ case SPI_NAND_GD5F: -+ snand->read_id = spi_nand_gd5f_read_id; -+ snand->get_ecc_status = spi_nand_gd5f_ecc_status; -+ snand->ooblayout = &spi_nand_gd5f_oob_256_ops; -+ break; -+ default: -+ dev_err(snand->dev, "unknown device\n"); -+ return -ENODEV; -+ } -+ -+ spi_set_drvdata(spi, snand); -+ priv->spi = spi; -+ -+ ret = spi_nand_register(snand, spi_nand_flash_ids); -+ if (ret) -+ return ret; -+ return 0; -+} -+ -+static int spi_nand_device_remove(struct spi_device *spi) -+{ -+ struct spi_nand *snand = spi_get_drvdata(spi); -+ -+ spi_nand_unregister(snand); -+ -+ return 0; -+} -+ -+const struct spi_device_id spi_nand_id_table[] = { -+ { "spi-nand", SPI_NAND_GENERIC }, -+ { "gd5f", SPI_NAND_GD5F }, -+ { }, -+}; -+MODULE_DEVICE_TABLE(spi, spi_nand_id_table); -+ -+static struct spi_driver spi_nand_device_driver = { -+ .driver = { -+ .name = "spi_nand_device", -+ .owner = THIS_MODULE, -+ }, -+ .id_table = spi_nand_id_table, -+ .probe = spi_nand_device_probe, -+ .remove = spi_nand_device_remove, -+}; -+module_spi_driver(spi_nand_device_driver); -+ -+MODULE_AUTHOR("Ezequiel Garcia <ezequiel.garcia@imgtec.com>"); -+MODULE_DESCRIPTION("SPI NAND device support"); -+MODULE_LICENSE("GPL v2"); |